pax_global_header00006660000000000000000000000064123143761430014516gustar00rootroot0000000000000052 comment=bcea5b446bdd9eea68ccb06ce9311dadff440991 PyTables-v.3.1.1/000077500000000000000000000000001231437614300135075ustar00rootroot00000000000000PyTables-v.3.1.1/.gitignore000066400000000000000000000001621231437614300154760ustar00rootroot00000000000000# Dirs build/ doc/build/ .ropeproject/ # File types *.pyc tables/*.c tables/*.so # specific files src/version.h PyTables-v.3.1.1/.travis.yml000066400000000000000000000006371231437614300156260ustar00rootroot00000000000000language: python virtualenv: system_site_packages: true python: - "2.6" - "2.7" - "3.2" - "3.3" before_install: - sudo apt-get update -qq - sudo apt-get install -qq libhdf5-serial-dev liblzo2-dev libbz2-dev python3-numpy - if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install -U numpy>=1.4.1 --use-mirrors; fi - pip install -r requirements.txt --use-mirrors script: "make check" PyTables-v.3.1.1/ANNOUNCE.txt.in000066400000000000000000000045361231437614300160730ustar00rootroot00000000000000=========================== Announcing PyTables @VERSION@ =========================== We are happy to announce PyTables @VERSION@. This is a bug-fix release that addresses a critical bug that make PyTables unusable on some platforms. What's new ========== - Fixed a critical bug that caused an exception at import time. The error was triggered when a bug in long-double detection is detected in the HDF5 library (see :issue:`275`) and numpy_ does not expose `float96` or `float128`. Closes :issue:`344`. - The internal Blosc_ library has been updated to version 1.3.5. This fixes a false buffer overrun condition that made c-blosc to fail, even if the problem was not real. As always, a large amount of bugs have been addressed and squashed as well. In case you want to know more in detail what has changed in this version, please refer to: http://pytables.github.io/release_notes.html You can download a source package with generated PDF and HTML docs, as well as binaries for Windows, from: http://sourceforge.net/projects/pytables/files/pytables/@VERSION@ For an online version of the manual, visit: http://pytables.github.io/usersguide/index.html What it is? =========== PyTables is a library for managing hierarchical datasets and designed to efficiently cope with extremely large amounts of data with support for full 64-bit file addressing. PyTables runs on top of the HDF5 library and NumPy package for achieving maximum throughput and convenient use. PyTables includes OPSI, a new indexing technology, allowing to perform data lookups in tables exceeding 10 gigarows (10**10 rows) in less than a tenth of a second. Resources ========= About PyTables: http://www.pytables.org About the HDF5 library: http://hdfgroup.org/HDF5/ About NumPy: http://numpy.scipy.org/ Acknowledgments =============== Thanks to many users who provided feature improvements, patches, bug reports, support and suggestions. See the ``THANKS`` file in the distribution package for a (incomplete) list of contributors. Most specially, a lot of kudos go to the HDF5 and NumPy makers. Without them, PyTables simply would not exist. Share your experience ===================== Let us know of any bugs, suggestions, gripes, kudos, etc. you may have. ---- **Enjoy data!** -- The PyTables Developers .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 72 .. End: PyTables-v.3.1.1/LICENSE.txt000066400000000000000000000032621231437614300153350ustar00rootroot00000000000000Copyright Notice and Statement for PyTables Software Library and Utilities: Copyright (c) 2002-2004 by Francesc Alted Copyright (c) 2005-2007 by Carabos Coop. V. Copyright (c) 2008-2010 by Francesc Alted Copyright (c) 2011-2014 by PyTables maintainers All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: a. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c. Neither the name of Francesc Alted nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PyTables-v.3.1.1/LICENSES/000077500000000000000000000000001231437614300147145ustar00rootroot00000000000000PyTables-v.3.1.1/LICENSES/BLOSC.txt000066400000000000000000000022651231437614300163240ustar00rootroot00000000000000Blosc - A blocking, shuffling and lossless compression library Copyright (C) 2009-2012 Francesc Alted Copyright (C) 2013 Francesc Alted Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PyTables-v.3.1.1/LICENSES/CLOUD-SPTHEME.txt000066400000000000000000000041151231437614300174270ustar00rootroot00000000000000.. -*- restructuredtext -*- =================== Copyright & License =================== Cloud Sphinx Theme ================== cloud_sptheme is released under the BSD license, and is (c) `Assurance Technologies `_:: The "cloud_sptheme" python package and artwork is Copyright (c) 2010-2012 by Assurance Technologies, LLC. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Assurance Technologies, nor the names of the contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Other Content ============= Most of the icons in ``cloud_sptheme:themes/cloud/static`` are from the `Tango Icon Project `_, which has released them into the Public Domain. PyTables-v.3.1.1/LICENSES/FASTLZ.txt000066400000000000000000000023121231437614300164560ustar00rootroot00000000000000FastLZ - lightning-fast lossless compression library Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PyTables-v.3.1.1/LICENSES/H5PY.txt000066400000000000000000000030321231437614300162000ustar00rootroot00000000000000Copyright Notice and Statement for the h5py Project Copyright (c) 2008 Andrew Collette http://www.h5py.org All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: a. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c. Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PyTables-v.3.1.1/LICENSES/HDF5.txt000066400000000000000000000072661231437614300161560ustar00rootroot00000000000000HDF5 (Hierarchical Data Format 5) Software Library and Utilities Copyright 2006-2007 by The HDF Group (THG). NCSA HDF5 (Hierarchical Data Format 5) Software Library and Utilities Copyright 1998-2006 by the Board of Trustees of the University of Illinois. All rights reserved. Contributors: National Center for Supercomputing Applications (NCSA) at the University of Illinois, Fortner Software, Unidata Program Center (netCDF), The Independent JPEG Group (JPEG), Jean-loup Gailly and Mark Adler (gzip), and Digital Equipment Corporation (DEC). Redistribution and use in source and binary forms, with or without modification, are permitted for any purpose (including commercial purposes) provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation and/or materials provided with the distribution. 3. In addition, redistributions of modified forms of the source or binary code must carry prominent notices stating that the original code was changed and the date of the change. 4. All publications or advertising materials mentioning features or use of this software are asked, but not required, to acknowledge that it was developed by The HDF Group and by the National Center for Supercomputing Applications at the University of Illinois at Urbana-Champaign and credit the contributors. 5. Neither the name of The HDF Group, the name of the University, nor the name of any Contributor may be used to endorse or promote products derived from this software without specific prior written permission from THG, the University, or the Contributor, respectively. DISCLAIMER: THIS SOFTWARE IS PROVIDED BY THE HDF GROUP (THG) AND THE CONTRIBUTORS "AS IS" WITH NO WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED. In no event shall THG or the Contributors be liable for any damages suffered by the users arising out of the use of this software, even if advised of the possibility of such damage. Portions of HDF5 were developed with support from the University of California, Lawrence Livermore National Laboratory (UC LLNL). The following statement applies to those portions of the product and must be retained in any redistribution of source code, binaries, documentation, and/or accompanying materials: This work was partially produced at the University of California, Lawrence Livermore National Laboratory (UC LLNL) under contract no. W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy (DOE) and The Regents of the University of California (University) for the operation of UC LLNL. DISCLAIMER: This work was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the University of California nor any of their employees, makes any warranty, express or implied, or assumes any liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately- owned rights. Reference herein to any specific commercial products, process, or service by trade name, trademark, manufacturer, or otherwise, does not necessarily constitute or imply its endorsement, recommendation, or favoring by the United States Government or the University of California. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or the University of California, and shall not be used for advertising or product endorsement purposes. PyTables-v.3.1.1/LICENSES/LZ4.txt000066400000000000000000000030101231437614300160600ustar00rootroot00000000000000LZ4 - Fast LZ compression algorithm Copyright (C) 2011-2013, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html - LZ4 source repository : http://code.google.com/p/lz4/ PyTables-v.3.1.1/LICENSES/SNAPPY.txt000066400000000000000000000027031231437614300164710ustar00rootroot00000000000000Copyright 2011, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PyTables-v.3.1.1/LICENSES/STDINT.txt000066400000000000000000000026421231437614300164660ustar00rootroot00000000000000Copyright (c) 2006-2013 Alexander Chemeris Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the product nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.PyTables-v.3.1.1/LICENSES/WIN32PTHREADS.txt000066400000000000000000000020771231437614300174200ustar00rootroot00000000000000Copyright (C) 2009 Andrzej K. Haczewski Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PyTables-v.3.1.1/LICENSES/ZLIB.txt000066400000000000000000000017521231437614300162220ustar00rootroot00000000000000Copyright notice: (C) 1995-2013 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu PyTables-v.3.1.1/MANIFEST.in000066400000000000000000000017241231437614300152510ustar00rootroot00000000000000include MANIFEST.in include *.txt THANKS include setup.py setup.cfg VERSION Makefile recursive-include tables *.py *.pyx *.pxd *.c recursive-include tables/tests *.h5 *.mat recursive-include tables/nodes/tests *.h5 *.dat *.xbm recursive-include src *.c *.h Makefile include c-blosc/hdf5/blosc_filter.? recursive-include c-blosc/blosc *.c *.h recursive-include c-blosc/internal-complibs *.c *.cc *.h recursive-include LICENSES * recursive-include utils * include doc/Makefile doc/make.bat #include doc/*.pdf recursive-include doc *.rst *.conf *.py *.*_t recursive-include doc *.html *.js *.css *.png *.ico recursive-include doc/source *.pdf objecttree.svg #recursive-include doc/source *.pdf *.svg recursive-include doc/html *.txt *.svg *.gif *.inv recursive-include doc/scripts *.py recursive-include doc/sphinxext * recursive-exclude doc/build * recursive-include examples *.py *.sh recursive-include bench *.sh *.py *.txt *.h5 *.gnuplot recursive-include contrib README *.py PyTables-v.3.1.1/Makefile000066400000000000000000000033031231437614300151460ustar00rootroot00000000000000# This Makefile is only intended to prepare for distribution the PyTables # sources exported from a repository. For building and installing PyTables, # please use ``setup.py`` as described in the ``README.txt`` file. VERSION = $(shell cat VERSION) SRCDIRS = src doc GENERATED = ANNOUNCE.txt PYTHON = python PYPLATFORM = $(shell $(PYTHON) -c "from distutils.util import get_platform; print(get_platform())") PYVER = $(shell $(PYTHON) -V 2>&1 | cut -c 8-10) PYBUILDDIR = $(PWD)/build/lib.$(PYPLATFORM)-$(PYVER) OPT = PYTHONPATH=$(PYBUILDDIR) .PHONY: all dist build check heavycheck clean distclean html all: $(GENERATED) build for srcdir in $(SRCDIRS) ; do $(MAKE) -C $$srcdir $(OPT) $@ ; done dist: all $(PYTHON) setup.py sdist cd dist && md5sum tables-$(VERSION).tar.gz > pytables-$(VERSION).md5 && cd - cp RELEASE_NOTES.txt dist/RELEASE_NOTES-$(VERSION).txt for srcdir in $(SRCDIRS) ; do $(MAKE) -C $$srcdir $(OPT) $@ ; done clean: rm -rf MANIFEST build dist tmp tables/__pycache__ rm -rf bench/*.h5 bench/*.prof rm -rf examples/*.h5 examples/raw rm -f $(GENERATED) tables/*.so a.out find . '(' -name '*.py[co]' -o -name '*~' ')' -exec rm '{}' ';' for srcdir in $(SRCDIRS) ; do $(MAKE) -C $$srcdir $(OPT) $@ ; done distclean: clean for srcdir in $(SRCDIRS) ; do $(MAKE) -C $$srcdir $(OPT) $@ ; done rm -f tables/_comp_*.c tables/*extension.c #git clean -fdx html: build $(MAKE) -C doc $(OPT) html %: %.in VERSION cat "$<" | sed -e 's/@VERSION@/$(VERSION)/g' > "$@" build: $(PYTHON) setup.py build check: build cd build/lib.*-$(PYVER) && env PYTHONPATH=. $(PYTHON) tables/tests/test_all.py heavycheck: build cd build/lib.*-$(PYVER) && env PYTHONPATH=. $(PYTHON) tables/tests/test_all.py --heavy PyTables-v.3.1.1/Makefile_windows000066400000000000000000000012231231437614300167170ustar00rootroot00000000000000# This MingW Makefile is only intended to prepare for distribution the PyTables # sources exported from a repository in a Windows box. For building and # installing PyTables, please use ``setup.py`` as described in the # ``README.txt`` file. VERSION=$(shell type VERSION) SRCDIRS=src doc GENERATED=ANNOUNCE.txt .PHONY: dist clean dist: $(GENERATED) for %%f in ($(SRCDIRS)) do $(MAKE) -f Makefile_windows -C %%f $@ clean: del /q /s MANIFEST build dist del /q /s $(GENERATED) tables\*.pyd del /q /s *.pyc *.pyo *~ for %%f in ($(SRCDIRS)) do $(MAKE) -f Makefile_windows -C %%f $@ %: %.in VERSION type "$<" | sed -e "s/@VERSION@/$(VERSION)/g" > "$@" PyTables-v.3.1.1/README.txt000066400000000000000000000130011231437614300152000ustar00rootroot00000000000000=========================================== PyTables: hierarchical datasets in Python =========================================== :URL: http://www.pytables.org/ PyTables is a package for managing hierarchical datasets and designed to efficiently cope with extremely large amounts of data. It is built on top of the HDF5 library and the NumPy package. It features an object-oriented interface that, combined with C extensions for the performance-critical parts of the code (generated using Cython), makes it a fast, yet extremely easy to use tool for interactively save and retrieve very large amounts of data. One important feature of PyTables is that it optimizes memory and disk resources so that they take much less space (between a factor 3 to 5, and more if the data is compressible) than other solutions, like for example, relational or object oriented databases. Not a RDBMS replacement ----------------------- PyTables is not designed to work as a relational database replacement, but rather as a teammate. If you want to work with large datasets of multidimensional data (for example, for multidimensional analysis), or just provide a categorized structure for some portions of your cluttered RDBS, then give PyTables a try. It works well for storing data from data acquisition systems (DAS), simulation software, network data monitoring systems (for example, traffic measurements of IP packets on routers), or as a centralized repository for system logs, to name only a few possible uses. Tables ------ A table is defined as a collection of records whose values are stored in fixed-length fields. All records have the same structure and all values in each field have the same data type. The terms "fixed-length" and strict "data types" seems to be quite a strange requirement for an interpreted language like Python, but they serve a useful function if the goal is to save very large quantities of data (such as is generated by many scientific applications, for example) in an efficient manner that reduces demand on CPU time and I/O. Arrays ------ There are other useful objects like arrays, enlargeable arrays or variable length arrays that can cope with different missions on your project. Also, quite a bit of effort has been invested to make browsing the hierarchical data structure a pleasant experience. PyTables implements a few easy-to-use methods for browsing. See the documentation (located in the ``doc/`` directory) for more details. Easy to use ----------- One of the principal objectives of PyTables is to be user-friendly. To that end, special Python features like generators, slots and metaclasses in new-brand classes have been used. In addition, iterators has been implemented were context was appropriate so as to enable the interactive work to be as productive as possible. For these reasons, you will need to use Python 2.6 or higher to take advantage of PyTables. Platforms --------- We are using Linux on top of Intel32 and Intel64 boxes as the main development platforms, but PyTables should be easy to compile/install on other UNIX or Windows machines. Nonetheless, caveat emptor: more testing is needed to achieve complete portability, we'd appreciate input on how it compiles and installs on your platform. Compiling --------- To compile PyTables you will need, at least, a recent version of HDF5 (C flavor) library, the Zlib compression library and the NumPy and Numexpr packages. Besides, if you want to take advantage of the LZO and bzip2 compression libraries support you will also need recent versions of them. LZO and bzip2 compression libraries are, however, optional. We've tested this PyTables version with HDF5 1.8.11/1.8.12, NumPy 1.7.1/1.8.0 and Numexpr 2.2.2, and you *need* to use these versions, or higher, to make use of PyTables. Installation ------------ The Python Distutils are used to build and install PyTables, so it is fairly simple to get things ready to go. Following are very simple instructions on how to proceed. However, more detailed instructions, including a section on binary installation for Windows users, is available in Chapter 2 of the User's Manual (``doc/usersguide.pdf`` or http://www.pytables.org/moin/HowToUse). 1. First, make sure that you have HDF5, NumPy and Numexpr installed (you will need at least HDF5 1.8.4, HDF5 >= 1.8.7 is strongly recommended, NumPy 1.4.1 and Numexpr 2.0). If don't, get them from http://www.hdfgroup.org/HDF5/, http://www.numpy.org and http://code.google.com/p/numexpr. Compile/install them. Optionally, consider to install the excellent LZO compression library from http://www.oberhumer.com/opensource/. You can also install the high-performance bzip2 compression library, available at http://www.bzip.org/. 2. From the main PyTables distribution directory run this command, (plus any extra flags needed as discussed above):: $ python setup.py build_ext --inplace 3. To run the test suite, set the PYTHONPATH environment variable to include the ``.`` directory, enter the Python interpreter and issue the commands:: >>> import tables >>> tables.test() If there is some test that does not pass, please send the complete output for tests back to us. 4. To install the entire PyTables Python package, run this command as the root user (remember to add any extra flags needed):: $ python setup.py install That's it! Good luck, and let us know of any bugs, suggestions, gripes, kudos, etc. you may have. ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: text .. coding: utf-8 .. fill-column: 70 .. End: PyTables-v.3.1.1/RELEASE_NOTES.txt000066400000000000000000000231371231437614300163060ustar00rootroot00000000000000======================================= Release notes for PyTables 3.1 series ======================================= :Author: PyTables Developers :Contact: pytables@googlemail.com .. py:currentmodule:: tables Changes from 3.1.0 to 3.1.1 =========================== Bugs fixed ---------- - Fixed a critical bug that caused an exception at import time. The error was triggered when a bug in long-double detection is detected in the HDF5 library (see :issue:`275`) and numpy_ does not expose `float96` or `float128`. Closes :issue:`344`. - The internal Blosc_ library has been updated to version 1.3.5. This fixes a false buffer overrun condition that made c-blosc to fail, even if the problem was not real. Improvements ------------ - Do not create a temporary array when the *obj* parameter is not specified in :meth:`File.create_array` (thanks to Francesc). Closes :issue:`337` and :issue:`339`). - Added two new utility functions (:func:`tables.nodes.filenode.read_from_filenode` and :func:`tables.nodes.filenode.save_to_filenode`) for the direct copy from filesystem to filenode and vice versa (closes :issue:`342`). Thanks to Andreas Hilboll. - Removed the :file:`examples/nested-iter.py` considered no longer useful. Closes :issue:`343`. - Better detection of the `-msse2` compiler flag. Changes from 3.0 to 3.1.0 ========================= New features ------------ - Now PyTables is able to save/restore the default value of :class:`EnumAtom` types (closes :issue:`234`). - Implemented support for the H5FD_SPLIT driver (closes :issue:`288`, :issue:`289` and :issue:`295`). Many thanks to simleo. - New quantization filter: the filter truncates floating point data to a specified precision before writing to disk. This can significantly improve the performance of compressors (closes :issue:`261`). Thanks to Andreas Hilboll. - Added new :meth:`VLArray.get_row_size` method to :class:`VLArray` for querying the number of atoms of a :class:`VLArray` row. Closes :issue:`24` and :issue:`315`. - The internal Blosc_ library has been updated to version 1.3.2. All new features introduced in the Blosc_ 1.3.x series, and in particular the ability to leverage different compressors within Blosc_ (see the `Blosc Release Notes`_), are now available in PyTables via the blosc filter (closes: :issue:`324`). A big thank you to Francesc. Improvements ------------ - The node caching mechanism has been completely redesigned to be simpler and less dependent from specific behaviours of the ``__del__`` method. Now PyTables is compatible with the forthcoming Python 3.4. Closes :issue:`306`. - PyTables no longer uses shared/cached file handlers. This change somewhat improves support for concurrent reading allowing the user to safely open the same file in different threads for reading (requires HDF5 >= 1.8.7). More details about this change can be found in the `Backward incompatible changes`_ section. See also :issue:`130`, :issue:`129` :issue:`292` and :issue:`216`. - PyTables is now able to detect and use external installations of the Blosc_ library (closes :issue:`104`). If Blosc_ is not found in the system, and the user do not specify a custom installation directory, then it is used an internal copy of the Blosc_ source code. - Automatically disable extended float support if a buggy version of HDF5 is detected (see also `Issues with H5T_NATIVE_LDOUBLE`_). See also :issue:`275`, :issue:`290` and :issue:`300`. - Documented an unexpected behaviour with string literals in query conditions on Python 3 (closes :issue:`265`) - The deprecated :mod:`getopt` module has been dropped in favour of :mod:`argparse` in all command line utilities (close :issue:`251`) - Improved the installation section of the :doc:`../usersguide/index`. * instructions for installing PyTables via pip_ have been added. * added a reference to the Anaconda_, Canopy_ and `Christoph Gohlke suites`_ (closes :issue:`291`) - Enabled `Travis-CI`_ builds for Python_ 3.3 - :meth:`Tables.read_coordinates` now also works with boolean indices input. Closes :issue:`287` and :issue:`298`. - Improved compatibility with numpy_ >= 1.8 (see :issue:`259`) - The code of the benchmark programs (bench directory) has been updated. Closes :issue:`114`. - Fixed some warning related to non-unicode file names (the Windows bytes API has been deprecated in Python 3.4) Bugs fixed ---------- - Fixed detection of platforms supporting Blosc_ - Fixed a crash that occurred when one attempts to write a numpy_ array to an :class:`Atom` (closes :issue:`209` and :issue:`296`) - Prevent creation of a table with no columns (closes :issue:`18` and :issue:`299`) - Fixed a memory leak that occured when iterating over :class:`CArray`/:class:`EArray` objects (closes :issue:`308`, see also :issue:`309`). Many thanks to Alistair Muldal. - Make NaN types sort to the end. Closes :issue:`282` and :issue:`313` - Fixed selection on float columns when NaNs are present (closes :issue:`327` and :issue:`330`) - Fix computation of the buffer size for iterations on rows. The buffers size was overestimated resulting in a :exc:`MemoryError` in some cases. Closes :issue:`316`. Thamks to bbudescu. - Better check of file open mode. Closes :issue:`318`. - The Blosc filter now works correctly together with fletcher32. Closes :issue:`21`. - Close the file handle before trying to delete the corresponding file. Fixes a test failure on Windows. - Use integer division for computing indices (fixes some warning on Windows) Deprecations ------------ Following the plan for the complete transition to the new (PEP8_ compliant) API, all calls to the old API will raise a :exc:`DeprecationWarning`. The new API has been introduced in PyTables 3.0 and is backward incompatible. In order to guarantee a smoother transition the old API is still usable even if it is now deprecated. The plan for the complete transition to the new API is outlined in :issue:`224`. Backward incompatible changes ----------------------------- In PyTables <= 3.0 file handles (objects that are returned by the :func:`open_file` function) were stored in an internal registry and re-used when possible. Two subsequent attempts to open the same file (with compatible open mode) returned the same file handle in PyTables <= 3.0:: In [1]: import tables In [2]: print(tables.__version__) 3.0.0 In [3]: a = tables.open_file('test.h5', 'a') In [4]: b = tables.open_file('test.h5', 'a') In [5]: a is b Out[5]: True All this is an implementation detail, it happened under the hood and the user had no control over the process. This kind of behaviour was considered a feature since it can speed up opening of files in case of repeated opens and it also avoids any potential problem related to multiple opens, a practice that the HDF5 developers recommend to avoid (see also H5Fopen_ reference page). The trick, of course, is that files are not opened multiple times at HDF5 level, rather an open file is referenced several times. The big drawback of this approach is that there are really few chances to use PyTables safely in a multi thread program. Several bug reports have been filed regarding this topic. After long discussions about the possibility to actually achieve concurrent I/O and about patterns that should be used for the I/O in concurrent programs PyTables developers decided to remove the *black magic under the hood* and allow the users to implement the patterns they want. Starting from PyTables 3.1 file handles are no more re-used (*shared*) and each call to the :func:`open_file` function returns a new file handle:: In [1]: import tables In [2]: print tables.__version__ 3.1.0 In [3]: a = tables.open_file('test.h5', 'a') In [4]: b = tables.open_file('test.h5', 'a') In [5]: a is b Out[5]: False It is important to stress that the new implementation still has an internal registry (implementation detail) and it is still **not thread safe**. Just now a smart enough developer should be able to use PyTables in a muti-thread program without too much headaches. The new implementation behaves differently from the previous one, although the API has not been changed. Now users should pay more attention when they open a file multiple times (as recommended in the `HDF5 reference`__ ) and they should take care of using them in an appropriate way. __ H5Fopen_ Please note that the :attr:`File.open_count` property was originally intended to keep track of the number of references to the same file handle. In PyTables >= 3.1, despite of the name, it maintains the same semantics, just now its value should never be higher that 1. .. note:: HDF5 versions lower than 1.8.7 are not fully compatible with PyTables 3.1. A partial support to HDF5 < 1.8.7 is still provided but in that case multiple file opens are not allowed at all (even in read-only mode). .. _pip: http://www.pip-installer.org .. _Anaconda: https://store.continuum.io/cshop/anaconda .. _Canopy: https://www.enthought.com/products/canopy .. _`Christoph Gohlke suites`: http://www.lfd.uci.edu/~gohlke/pythonlibs .. _`Issues with H5T_NATIVE_LDOUBLE`: http://hdf-forum.184993.n3.nabble.com/Issues-with-H5T-NATIVE-LDOUBLE-tt4026450.html .. _Python: http://www.python.org .. _Blosc: http://www.blosc.org .. _numpy: http://www.numpy.org .. _`Travis-CI`: https://travis-ci.org .. _PEP8: http://www.python.org/dev/peps/pep-0008 .. _`Blosc Release Notes`: https://github.com/FrancescAlted/blosc/wiki/Release-notes .. _H5Fopen: http://www.hdfgroup.org/HDF5/doc/RM/RM_H5F.html#File-Open **Enjoy data!** -- The PyTables Developers .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 72 .. End: PyTables-v.3.1.1/THANKS000066400000000000000000000062551231437614300144320ustar00rootroot00000000000000March 2009 We would like to thank the people have contributed directly or indirectly to PyTables. Scott Prater for editing the user's manual in order to make it more readable in english, as well as conducting the tests of PyTables on Solaris. Alan McIntyre for porting PyTables to Windows. John Nielsen for suggesting improvements and delivering code for completely avoid the recursion algorithms and allowing pytables to bypass the ~1000 levels of deepness that Python recursion limit imposed. Tom Hedley for providing a nice patch for supporting complex datatypes for Arrays, Errays and VLArrays. This was the root for the support of complex types in Tables as well. Shack Toms for providing a Python version of the nextafter and nextafterf math functions that despite the fact they are standard in C99 standard, they are not at the official places in Microsoft VC++ 6.x nor VC++ 7.x. Jeff Whitaker for providing the NetCDF module and the utility for converting netCDF files to HDF5 (nctoh5). Norbert Nemec for providing several interesting patches. Andrew Straw for suggesting to bracket the most intensive parts of PyTables with BEGIN_ALLOW_THREADS and END_ALLOW_THREADS. That will allow much better performance of PyTables apps in mutiprocessors platforms. Antonio Valentino for providing several patches for supporting native multidimensional attributes and the CArray object. Ashley Walsh, for reporting several problems and fixes. It has helped testing OSX platform, specially UCL compressor issues. Russel Howe, for reporting and providing an initial patch for a nasty memory leak when reading VLArray types. The HDF5 team at NCSA for making such an excellent library for data persistence, and specially Pedro Vicente, Quincey Koziol and Elena Pourmal, for quickly including my suggested patches to the HDF5_HL and solving the reported bugs in HDF5 library. Todd Miller and Perry Greenfield for promptly helping me to understand many of the intricacies of the numarray package and Jin-chung Hsu for discussions on recarray module (now numarray.records module). They have been very receptive and promptly worked-out most of the improvements in numarray (specially in the records module) that were necessary for PyTables. Travis Oliphant for its impressive work and responsiveness with NumPy. Evan Prodromou for his lrucache package, a very sleek implementation of an LRU queue. He had a very helpful attitude with the licensing and technical issues. Gerard Vermeulen for Windows/MSVS-2005 testing. Enric Cervera for testing the binaries for MacOSX/Intel. Daniel Bungert, Steve Langasek and Alexandre Fayolle for their support in creating Debian packages for PyTables. Greg Ewing for writing the excelent Pyrex tool and allowing to beginners like me to quickly and safely start writing Python extensions. He was also very responsive about questions on Pyrex. Stefan Behnel, Robert Bradshaw, and Dag Sverre Seljebotn for their impressive work with Cython. Andrew Collette, for his excellent work on the h5py project, from which PyTables starts to stole ideas (and code too ;-). Guido, you know who. And last, but definitely not least!, To those companies that are supporting the PyTables project with contracts. PyTables-v.3.1.1/VERSION000066400000000000000000000000061231437614300145530ustar00rootroot000000000000003.1.1 PyTables-v.3.1.1/bench/000077500000000000000000000000001231437614300145665ustar00rootroot00000000000000PyTables-v.3.1.1/bench/LRU-experiments.py000066400000000000000000000054301231437614300201450ustar00rootroot00000000000000# Testbed to perform experiments in order to determine best values for # the node numbers in LRU cache. Tables version. from __future__ import print_function from time import time from tables import * import tables print("PyTables version-->", tables.__version__) filename = "/tmp/junk-tables-100.h5" NLEAVES = 2000 NROWS = 1000 class Particle(IsDescription): name = StringCol(16, pos=1) # 16-character String lati = Int32Col(pos=2) # integer longi = Int32Col(pos=3) # integer pressure = Float32Col(pos=4) # float (single-precision) temperature = Float64Col(pos=5) # double (double-precision) def create_junk(): # Open a file in "w"rite mode fileh = open_file(filename, mode="w") # Create a new group group = fileh.create_group(fileh.root, "newgroup") for i in range(NLEAVES): # Create a new table in newgroup group table = fileh.create_table(group, 'table' + str(i), Particle, "A table", Filters(1)) particle = table.row print("Creating table-->", table._v_name) # Fill the table with particles for i in range(NROWS): # This injects the row values. particle.append() table.flush() # Finally, close the file fileh.close() def modify_junk_LRU(): fileh = open_file(filename, 'a') group = fileh.root.newgroup for j in range(5): print("iter -->", j) for tt in fileh.walk_nodes(group): if isinstance(tt, Table): pass # for row in tt: # pass fileh.close() def modify_junk_LRU2(): fileh = open_file(filename, 'a') group = fileh.root.newgroup for j in range(20): t1 = time() for i in range(100): #print("table-->", tt._v_name) tt = getattr(group, "table" + str(i)) #for row in tt: # pass print("iter and time -->", j + 1, round(time() - t1, 3)) fileh.close() def modify_junk_LRU3(): fileh = open_file(filename, 'a') group = fileh.root.newgroup for j in range(3): t1 = time() for tt in fileh.walk_nodes(group, "Table"): tt.attrs.TITLE for row in tt: pass print("iter and time -->", j + 1, round(time() - t1, 3)) fileh.close() if 1: # create_junk() # modify_junk_LRU() # uses the iterator version (walk_nodes) # modify_junk_LRU2() # uses a regular loop (getattr) modify_junk_LRU3() # uses a regular loop (getattr) else: import profile import pstats profile.run('modify_junk_LRU2()', 'modify.prof') stats = pstats.Stats('modify.prof') stats.strip_dirs() stats.sort_stats('time', 'calls') stats.print_stats() PyTables-v.3.1.1/bench/LRU-experiments2.py000066400000000000000000000027671231437614300202410ustar00rootroot00000000000000# Testbed to perform experiments in order to determine best values for # the node numbers in LRU cache. Arrays version. from __future__ import print_function from time import time import tables print("PyTables version-->", tables.__version__) filename = "/tmp/junk-array.h5" NOBJS = 1000 def create_junk(): fileh = tables.open_file(filename, mode="w") for i in range(NOBJS): fileh.create_array(fileh.root, 'array' + str(i), [1]) fileh.close() def modify_junk_LRU(): fileh = tables.open_file(filename, 'a') group = fileh.root for j in range(5): print("iter -->", j) for tt in fileh.walk_nodes(group): if isinstance(tt, tables.Array): # d = tt.read() pass fileh.close() def modify_junk_LRU2(): fileh = tables.open_file(filename, 'a') group = fileh.root for j in range(5): t1 = time() for i in range(100): # The number #print("table-->", tt._v_name) tt = getattr(group, "array" + str(i)) #d = tt.read() print("iter and time -->", j + 1, round(time() - t1, 3)) fileh.close() if 1: # create_junk() # modify_junk_LRU() # uses the iterador version (walk_nodes) modify_junk_LRU2() # uses a regular loop (getattr) else: import profile import pstats profile.run('modify_junk_LRU2()', 'modify.prof') stats = pstats.Stats('modify.prof') stats.strip_dirs() stats.sort_stats('time', 'calls') stats.print_stats() PyTables-v.3.1.1/bench/LRUcache-node-bench.py000066400000000000000000000035371231437614300205760ustar00rootroot00000000000000from __future__ import print_function import sys import numpy import tables from time import time #import psyco filename = "/tmp/LRU-bench.h5" nodespergroup = 250 niter = 100 print('nodespergroup:', nodespergroup) print('niter:', niter) if len(sys.argv) > 1: NODE_CACHE_SLOTS = int(sys.argv[1]) print('NODE_CACHE_SLOTS:', NODE_CACHE_SLOTS) else: NODE_CACHE_SLOTS = tables.parameters.NODE_CACHE_SLOTS f = tables.open_file(filename, "w", node_cache_slots=NODE_CACHE_SLOTS) g = f.create_group("/", "NodeContainer") print("Creating nodes") for i in range(nodespergroup): f.create_array(g, "arr%d" % i, [i]) f.close() f = tables.open_file(filename) def iternodes(): # for a in f.root.NodeContainer: # pass indices = numpy.random.randn(nodespergroup * niter) * \ 30 + nodespergroup / 2. indices = indices.astype('i4').clip(0, nodespergroup - 1) g = f.get_node("/", "NodeContainer") for i in indices: a = f.get_node(g, "arr%d" % i) # print("a-->", a) print("reading nodes...") # First iteration (put in LRU cache) t1 = time() for a in f.root.NodeContainer: pass print("time (init cache)-->", round(time() - t1, 3)) def timeLRU(): # Next iterations t1 = time() # for i in range(niter): # iternodes() iternodes() print("time (from cache)-->", round((time() - t1) / niter, 3)) def profile(verbose=False): import pstats import cProfile as prof prof.run('timeLRU()', 'out.prof') stats = pstats.Stats('out.prof') stats.strip_dirs() stats.sort_stats('time', 'calls') if verbose: stats.print_stats() else: stats.print_stats(20) # profile() # psyco.bind(timeLRU) timeLRU() f.close() # for N in 0 4 8 16 32 64 128 256 512 1024 2048 4096; do # env PYTHONPATH=../build/lib.linux-x86_64-2.7 \ # python LRUcache-node-bench.py $N; # done PyTables-v.3.1.1/bench/bench-postgres-ranges.sh000077500000000000000000000004021231437614300213210ustar00rootroot00000000000000#!/bin/sh export PYTHONPATH=..${PYTHONPATH:+:$PYTHONPATH} pyopt="-O -u" #qlvl="-Q8 -x" #qlvl="-Q8" qlvl="-Q7" #size="500m" size="1g" #python $pyopt indexed_search.py -P -c -n $size -m -v python $pyopt indexed_search.py -P -i -n $size -m -v -sfloat $qlvl PyTables-v.3.1.1/bench/bench-pytables-ranges.sh000077500000000000000000000017311231437614300213040ustar00rootroot00000000000000#!/bin/sh #export LD_LIBRARY_PATH=$HOME/computacio/hdf5-1.8.2/hdf5/lib export PYTHONPATH=..${PYTHONPATH:+:$PYTHONPATH} bench="python2.7 -O -u indexed_search.py" flags="-T -m -v " #sizes="1g 500m 200m 100m 50m 20m 10m 5m 2m 1m" sizes="1g" #sizes="1m" working_dir="data.nobackup" #working_dir="/scratch2/faltet" #for comprlvl in '-z0' '-z1 -llzo' '-z1 -lzlib' ; do #for comprlvl in '-z6 -lblosc' '-z3 -lblosc' '-z1 -lblosc' ; do for comprlvl in '-z5 -lblosc' ; do #for comprlvl in '-z0' ; do for optlvl in '-tfull -O9' ; do #for optlvl in '-tultralight -O3' '-tlight -O6' '-tmedium -O6' '-tfull -O9'; do #for optlvl in '-tultralight -O3'; do #rm -f $working_dir/* # XXX esta ben posat?? for mode in '-Q8 -i -s float' ; do #for mode in -c '-Q7 -i -s float' ; do #for mode in '-c -s float' '-Q8 -I -s float' '-Q8 -S -s float'; do for size in $sizes ; do $bench $flags $mode -n $size $optlvl $comprlvl -d $working_dir done done done done PyTables-v.3.1.1/bench/bench-pytables.sh000077500000000000000000000015601231437614300200270ustar00rootroot00000000000000#!/bin/sh export LD_LIBRARY_PATH=$HOME/computacio/hdf5-1.8.1/hdf5/lib #export PYTHONPATH=..${PYTHONPATH:+:$PYTHONPATH} bench="python2.7 -O -u indexed_search.py" flags="-T -m -v -d data.nobackup" #sizes="1m 2m 5m 10m 20m 50m 100m 200m 500m 1g" sizes="2g 1g 500m 200m 100m 50m 20m 10m 5m 2m 1m 500k 200k 100k 50k 20k 10k 5k 2k 1k" #sizes="1m 100k" #for optimlvl in 0 1 2 3 4 5 6 7 8 9 ; do for idxtype in ultralight light medium full; do #for idxtype in medium full; do for optimlvl in 0 3 6 9; do for compropt in '' '-z1 -lzlib' '-z1 -llzo' ; do #for compropt in '-z1 -llzo' ; do rm -f data.nobackup/* # Atencio: esta correctament posat? #for mode in -c '-i -s float' ; do for mode in -c '-i' ; do for size in $sizes ; do $bench $flags $mode -n $size -O $optimlvl -t $idxtype $compropt done done done done done PyTables-v.3.1.1/bench/blosc.py000066400000000000000000000110021231437614300162340ustar00rootroot00000000000000from __future__ import print_function import os import sys from time import time import numpy as np import tables as tb niter = 3 dirname = "/scratch2/faltet/blosc-data/" #expression = "a**2 + b**3 + 2*a*b + 3" #expression = "a+b" #expression = "a**2 + 2*a/b + 3" #expression = "(a+b)**2 - (a**2 + b**2 + 2*a*b) + 1.1" expression = "3*a-2*b+1.1" shuffle = True def create_file(kind, prec, synth): prefix_orig = 'cellzome/cellzome-' iname = dirname + prefix_orig + 'none-' + prec + '.h5' f = tb.open_file(iname, "r") if prec == "single": type_ = tb.Float32Atom() else: type_ = tb.Float64Atom() if synth: prefix = 'synth/synth-' else: prefix = 'cellzome/cellzome-' for clevel in range(10): oname = '%s/%s-%s%d-%s.h5' % (dirname, prefix, kind, clevel, prec) # print "creating...", iname f2 = tb.open_file(oname, "w") if kind in ["none", "numpy"]: filters = None else: filters = tb.Filters( complib=kind, complevel=clevel, shuffle=shuffle) for name in ['maxarea', 'mascotscore']: col = f.get_node('/', name) r = f2.create_carray('/', name, type_, col.shape, filters=filters) if synth: r[:] = np.arange(col.nrows, dtype=type_.dtype) else: r[:] = col[:] f2.close() if clevel == 0: size = 1.5 * float(os.stat(oname)[6]) f.close() return size def create_synth(kind, prec): prefix_orig = 'cellzome/cellzome-' iname = dirname + prefix_orig + 'none-' + prec + '.h5' f = tb.open_file(iname, "r") if prec == "single": type_ = tb.Float32Atom() else: type_ = tb.Float64Atom() prefix = 'synth/synth-' for clevel in range(10): oname = '%s/%s-%s%d-%s.h5' % (dirname, prefix, kind, clevel, prec) # print "creating...", iname f2 = tb.open_file(oname, "w") if kind in ["none", "numpy"]: filters = None else: filters = tb.Filters( complib=kind, complevel=clevel, shuffle=shuffle) for name in ['maxarea', 'mascotscore']: col = f.get_node('/', name) r = f2.create_carray('/', name, type_, col.shape, filters=filters) if name == 'maxarea': r[:] = np.arange(col.nrows, dtype=type_.dtype) else: r[:] = np.arange(col.nrows, 0, dtype=type_.dtype) f2.close() if clevel == 0: size = 1.5 * float(os.stat(oname)[6]) f.close() return size def process_file(kind, prec, clevel, synth): if kind == "numpy": lib = "none" else: lib = kind if synth: prefix = 'synth/synth-' else: prefix = 'cellzome/cellzome-' iname = '%s/%s-%s%d-%s.h5' % (dirname, prefix, kind, clevel, prec) f = tb.open_file(iname, "r") a_ = f.root.maxarea b_ = f.root.mascotscore oname = '%s/%s-%s%d-%s-r.h5' % (dirname, prefix, kind, clevel, prec) f2 = tb.open_file(oname, "w") if lib == "none": filters = None else: filters = tb.Filters(complib=lib, complevel=clevel, shuffle=shuffle) if prec == "single": type_ = tb.Float32Atom() else: type_ = tb.Float64Atom() r = f2.create_carray('/', 'r', type_, a_.shape, filters=filters) if kind == "numpy": a2, b2 = a_[:], b_[:] t0 = time() r = eval(expression, {'a': a2, 'b': b2}) print("%5.2f" % round(time() - t0, 3)) else: expr = tb.Expr(expression, {'a': a_, 'b': b_}) expr.set_output(r) expr.eval() f.close() f2.close() size = float(os.stat(iname)[6]) + float(os.stat(oname)[6]) return size if __name__ == '__main__': if len(sys.argv) > 3: kind = sys.argv[1] prec = sys.argv[2] if sys.argv[3] == "synth": synth = True else: synth = False else: print("3 parameters required") sys.exit(1) # print "kind, precision, synth:", kind, prec, synth # print "Creating input files..." size_orig = create_file(kind, prec, synth) # print "Processing files for compression levels in range(10)..." for clevel in range(10): t0 = time() ts = [] for i in range(niter): size = process_file(kind, prec, clevel, synth) ts.append(time() - t0) t0 = time() ratio = size_orig / size print("%5.2f, %5.2f" % (round(min(ts), 3), ratio)) PyTables-v.3.1.1/bench/bsddb-table-bench.py000066400000000000000000000201131231437614300203550ustar00rootroot00000000000000#!/usr/bin/env python ###### WARNING ####### ### This script is obsoleted ### # If you get it working again, please drop me a line # F. Alted 2004-01-27 from __future__ import print_function import sys import struct import cPickle from tables import * import numpy as np try: # For Python 2.3 from bsddb import db except ImportError: # For earlier Pythons w/distutils pybsddb from bsddb3 import db import psyco # This class is accessible only for the examples class Small(IsDescription): """Record descriptor. A record has several columns. They are represented here as class attributes, whose names are the column names and their values will become their types. The IsColDescr class will take care the user will not add any new variables and that its type is correct. """ var1 = StringCol(itemsize=16) var2 = Int32Col() var3 = Float64Col() # Define a user record to characterize some kind of particles class Medium(IsDescription): name = StringCol(itemsize=16, pos=0) # 16-character String #float1 = Float64Col(shape=2, dflt=2.3) float1 = Float64Col(dflt=1.3, pos=1) float2 = Float64Col(dflt=2.3, pos=2) ADCcount = Int16Col(pos=3) # signed short integer grid_i = Int32Col(pos=4) # integer grid_j = Int32Col(pos=5) # integer pressure = Float32Col(pos=6) # float (single-precision) energy = Float64Col(pos=7) # double (double-precision) # Define a user record to characterize some kind of particles class Big(IsDescription): name = StringCol(itemsize=16) # 16-character String #float1 = Float64Col(shape=32, dflt=np.arange(32)) #float2 = Float64Col(shape=32, dflt=np.arange(32)) float1 = Float64Col(shape=32, dflt=range(32)) float2 = Float64Col(shape=32, dflt=[2.2] * 32) ADCcount = Int16Col() # signed short integer grid_i = Int32Col() # integer grid_j = Int32Col() # integer pressure = Float32Col() # float (single-precision) energy = Float64Col() # double (double-precision) def createFile(filename, totalrows, recsize, verbose): # Open a 'n'ew file dd = db.DB() if recsize == "big": isrec = Description(Big) elif recsize == "medium": isrec = Medium() else: isrec = Description(Small) # dd.set_re_len(struct.calcsize(isrec._v_fmt)) # fixed length records dd.open(filename, db.DB_RECNO, db.DB_CREATE | db.DB_TRUNCATE) rowswritten = 0 # Get the record object associated with the new table if recsize == "big": isrec = Big() arr = np.array(np.arange(32), type=np.Float64) arr2 = np.array(np.arange(32), type=np.Float64) elif recsize == "medium": isrec = Medium() arr = np.array(np.arange(2), type=np.Float64) else: isrec = Small() # print d # Fill the table if recsize == "big" or recsize == "medium": d = {"name": " ", "float1": 1.0, "float2": 2.0, "ADCcount": 12, "grid_i": 1, "grid_j": 1, "pressure": 1.9, "energy": 1.8, } for i in range(totalrows): #d['name'] = 'Particle: %6d' % (i) #d['TDCcount'] = i % 256 d['ADCcount'] = (i * 256) % (1 << 16) if recsize == "big": #d.float1 = np.array([i]*32, np.Float64) #d.float2 = np.array([i**2]*32, np.Float64) arr[0] = 1.1 d['float1'] = arr arr2[0] = 2.2 d['float2'] = arr2 pass else: d['float1'] = float(i) d['float2'] = float(i) d['grid_i'] = i d['grid_j'] = 10 - i d['pressure'] = float(i * i) d['energy'] = d['pressure'] dd.append(cPickle.dumps(d)) # dd.append(struct.pack(isrec._v_fmt, # d['name'], d['float1'], d['float2'], # d['ADCcount'], # d['grid_i'], d['grid_j'], # d['pressure'], d['energy'])) else: d = {"var1": " ", "var2": 1, "var3": 12.1e10} for i in range(totalrows): d['var1'] = str(i) d['var2'] = i d['var3'] = 12.1e10 dd.append(cPickle.dumps(d)) #dd.append( # struct.pack(isrec._v_fmt, d['var1'], d['var2'], d['var3'])) rowswritten += totalrows # Close the file dd.close() return (rowswritten, struct.calcsize(isrec._v_fmt)) def readFile(filename, recsize, verbose): # Open the HDF5 file in read-only mode #fileh = shelve.open(filename, "r") dd = db.DB() if recsize == "big": isrec = Big() elif recsize == "medium": isrec = Medium() else: isrec = Small() # dd.set_re_len(struct.calcsize(isrec._v_fmt)) # fixed length records # dd.set_re_pad('-') # sets the pad character... # dd.set_re_pad(45) # ...test both int and char dd.open(filename, db.DB_RECNO) if recsize == "big" or recsize == "medium": print(isrec._v_fmt) c = dd.cursor() rec = c.first() e = [] while rec: record = cPickle.loads(rec[1]) #record = struct.unpack(isrec._v_fmt, rec[1]) # if verbose: # print record if record['grid_i'] < 20: e.append(record['grid_j']) # if record[4] < 20: # e.append(record[5]) rec = next(c) else: print(isrec._v_fmt) #e = [ t[1] for t in fileh[table] if t[1] < 20 ] c = dd.cursor() rec = c.first() e = [] while rec: record = cPickle.loads(rec[1]) #record = struct.unpack(isrec._v_fmt, rec[1]) # if verbose: # print record if record['var2'] < 20: e.append(record['var1']) # if record[1] < 20: # e.append(record[2]) rec = next(c) print("resulting selection list ==>", e) print("last record read ==>", record) print("Total selected records ==> ", len(e)) # Close the file (eventually destroy the extended type) dd.close() # Add code to test here if __name__ == "__main__": import getopt import time usage = """usage: %s [-v] [-s recsize] [-i iterations] file -v verbose -s use [big] record, [medium] or [small] -i sets the number of rows in each table\n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 's:vi:') except: sys.stderr.write(usage) sys.exit(0) # if we pass too much parameters, abort if len(pargs) != 1: sys.stderr.write(usage) sys.exit(0) # default options recsize = "medium" iterations = 100 verbose = 0 # Get the options for option in opts: if option[0] == '-s': recsize = option[1] if recsize not in ["big", "medium", "small"]: sys.stderr.write(usage) sys.exit(0) elif option[0] == '-i': iterations = int(option[1]) elif option[0] == '-v': verbose = 1 # Catch the hdf5 file passed as the last argument file = pargs[0] t1 = time.clock() psyco.bind(createFile) (rowsw, rowsz) = createFile(file, iterations, recsize, verbose) t2 = time.clock() tapprows = round(t2 - t1, 3) t1 = time.clock() psyco.bind(readFile) readFile(file, recsize, verbose) t2 = time.clock() treadrows = round(t2 - t1, 3) print("Rows written:", rowsw, " Row size:", rowsz) print("Time appending rows:", tapprows) if tapprows > 0.: print("Write rows/sec: ", int(iterations / float(tapprows))) print("Write KB/s :", int(rowsw * rowsz / (tapprows * 1024))) print("Time reading rows:", treadrows) if treadrows > 0.: print("Read rows/sec: ", int(iterations / float(treadrows))) print("Read KB/s :", int(rowsw * rowsz / (treadrows * 1024))) PyTables-v.3.1.1/bench/cacheout.py000066400000000000000000000004661231437614300167410ustar00rootroot00000000000000# Program to clean out the filesystem cache import numpy a = numpy.arange(1000 * 100 * 125, dtype='f8') # 100 MB of RAM b = a * 3 # Another 100 MB # delete the reference to the booked memory del a del b # Do a loop to fully recharge the python interpreter j = 2 for i in range(1000 * 1000): j += i * 2 PyTables-v.3.1.1/bench/chunkshape-bench.py000066400000000000000000000040671231437614300203550ustar00rootroot00000000000000#!/usr/bin/env python # Benchmark the effect of chunkshapes in reading large datasets. # You need at least PyTables 2.1 to run this! # F. Alted from __future__ import print_function import numpy import tables from time import time dim1, dim2 = 360, 6109666 rows_to_read = range(0, 360, 36) print("=" * 32) # Create the EArray f = tables.open_file("/tmp/test.h5", "w") a = f.create_earray(f.root, "a", tables.Float64Atom(), shape=(dim1, 0), expectedrows=dim2) print("Chunkshape for original array:", a.chunkshape) # Fill the EArray t1 = time() zeros = numpy.zeros((dim1, 1), dtype="float64") for i in range(dim2): a.append(zeros) tcre = round(time() - t1, 3) thcre = round(dim1 * dim2 * 8 / (tcre * 1024 * 1024), 1) print("Time to append %d rows: %s sec (%s MB/s)" % (a.nrows, tcre, thcre)) # Read some row vectors from the original array t1 = time() for i in rows_to_read: r1 = a[i, :] tr1 = round(time() - t1, 3) thr1 = round(dim2 * len(rows_to_read) * 8 / (tr1 * 1024 * 1024), 1) print("Time to read ten rows in original array: %s sec (%s MB/s)" % (tr1, thr1)) print("=" * 32) # Copy the array to another with a row-wise chunkshape t1 = time() #newchunkshape = (1, a.chunkshape[0]*a.chunkshape[1]) newchunkshape = (1, a.chunkshape[0] * a.chunkshape[1] * 10) # ten times larger b = a.copy(f.root, "b", chunkshape=newchunkshape) tcpy = round(time() - t1, 3) thcpy = round(dim1 * dim2 * 8 / (tcpy * 1024 * 1024), 1) print("Chunkshape for row-wise chunkshape array:", b.chunkshape) print("Time to copy the original array: %s sec (%s MB/s)" % (tcpy, thcpy)) # Read the same ten rows from the new copied array t1 = time() for i in rows_to_read: r2 = b[i, :] tr2 = round(time() - t1, 3) thr2 = round(dim2 * len(rows_to_read) * 8 / (tr2 * 1024 * 1024), 1) print("Time to read with a row-wise chunkshape: %s sec (%s MB/s)" % (tr2, thr2)) print("=" * 32) print("Speed-up with a row-wise chunkshape:", round(tr1 / tr2, 1)) f.close() PyTables-v.3.1.1/bench/chunkshape-testing.py000066400000000000000000000064171231437614300207540ustar00rootroot00000000000000#!/usr/bin/env python """Simple benchmark for testing chunkshapes and nrowsinbuf.""" from __future__ import print_function import numpy import tables from time import time L = 20 N = 2000 M = 30 complevel = 1 recarray = numpy.empty(shape=2, dtype='(2,2,2)i4,(2,3,3)f8,i4,i8') f = tables.open_file("chunkshape.h5", mode="w") # t = f.create_table(f.root, 'table', recarray, "mdim recarray") # a0 = f.create_array(f.root, 'field0', recarray['f0'], "mdim int32 array") # a1 = f.create_array(f.root, 'field1', recarray['f1'], "mdim float64 array") # c0 = f.create_carray(f.root, 'cfield0', # tables.Int32Atom(), (2,2,2), # "mdim int32 carray") # c1 = f.create_carray(f.root, 'cfield1', # tables.Float64Atom(), (2,3,3), # "mdim float64 carray") f1 = tables.open_file("chunkshape1.h5", mode="w") c1 = f.create_carray(f1.root, 'cfield1', tables.Int32Atom(), (L, N, M), "scalar int32 carray", tables.Filters(complevel=0)) t1 = time() c1[:] = numpy.empty(shape=(L, 1, 1), dtype="int32") print("carray1 populate time:", time() - t1) f1.close() f2 = tables.open_file("chunkshape2.h5", mode="w") c2 = f.create_carray(f2.root, 'cfield2', tables.Int32Atom(), (L, M, N), "scalar int32 carray", tables.Filters(complevel)) t1 = time() c2[:] = numpy.empty(shape=(L, 1, 1), dtype="int32") print("carray2 populate time:", time() - t1) f2.close() f0 = tables.open_file("chunkshape0.h5", mode="w") e0 = f.create_earray(f0.root, 'efield0', tables.Int32Atom(), (0, L, M), "scalar int32 carray", tables.Filters(complevel), expectedrows=N) t1 = time() e0.append(numpy.empty(shape=(N, L, M), dtype="int32")) print("earray0 populate time:", time() - t1) f0.close() f1 = tables.open_file("chunkshape1.h5", mode="w") e1 = f.create_earray(f1.root, 'efield1', tables.Int32Atom(), (L, 0, M), "scalar int32 carray", tables.Filters(complevel), expectedrows=N) t1 = time() e1.append(numpy.empty(shape=(L, N, M), dtype="int32")) print("earray1 populate time:", time() - t1) f1.close() f2 = tables.open_file("chunkshape2.h5", mode="w") e2 = f.create_earray(f2.root, 'efield2', tables.Int32Atom(), (L, M, 0), "scalar int32 carray", tables.Filters(complevel), expectedrows=N) t1 = time() e2.append(numpy.empty(shape=(L, M, N), dtype="int32")) print("earray2 populate time:", time() - t1) f2.close() # t1=time() # c2[:] = numpy.empty(shape=(M, N), dtype="int32") # print "carray populate time:", time()-t1 # f3 = f.create_carray(f.root, 'cfield3', # tables.Float64Atom(), (3,), # "scalar float64 carray", chunkshape=(32,)) # e2 = f.create_earray(f.root, 'efield2', # tables.Int32Atom(), (0, M), # "scalar int32 carray", expectedrows=N) # t1=time() # e2.append(numpy.empty(shape=(N, M), dtype="int32")) # print "earray populate time:", time()-t1 # t1=time() # c2._f_copy(newname='cfield2bis') # print "carray copy time:", time()-t1 # t1=time() # e2._f_copy(newname='efield2bis') # print "earray copy time:", time()-t1 f.close() PyTables-v.3.1.1/bench/collations.py000066400000000000000000000074211231437614300173130ustar00rootroot00000000000000from __future__ import print_function import numpy as np import tables from time import time N = 1000 * 1000 NCOLL = 200 # 200 collections maximum # In order to have reproducible results np.random.seed(19) class Energies(tables.IsDescription): collection = tables.UInt8Col() energy = tables.Float64Col() def fill_bucket(lbucket): #c = np.random.normal(NCOLL/2, NCOLL/10, lbucket) c = np.random.normal(NCOLL / 2, NCOLL / 100, lbucket) e = np.arange(lbucket, dtype='f8') return c, e # Fill the table t1 = time() f = tables.open_file("data.nobackup/collations.h5", "w") table = f.create_table("/", "Energies", Energies, expectedrows=N) # Fill the table with values lbucket = 1000 # Fill in buckets of 1000 rows, for speed for i in range(0, N, lbucket): bucket = fill_bucket(lbucket) table.append(bucket) # Fill the remaining rows bucket = fill_bucket(N % lbucket) table.append(bucket) f.close() print("Time to create the table with %d entries: %.3f" % (N, time() - t1)) # Now, read the table and group it by collection f = tables.open_file("data.nobackup/collations.h5", "a") table = f.root.Energies ######################################################### # First solution: load the table completely in memory ######################################################### t1 = time() t = table[:] # convert to structured array coll1 = [] collections = np.unique(t['collection']) for c in collections: cond = t['collection'] == c energy_this_collection = t['energy'][cond] sener = energy_this_collection.sum() coll1.append(sener) print(c, ' : ', sener) del collections, energy_this_collection print("Time for first solution: %.3f" % (time() - t1)) ######################################################### # Second solution: load all the collections in memory ######################################################### t1 = time() collections = {} for row in table: c = row['collection'] e = row['energy'] if c in collections: collections[c].append(e) else: collections[c] = [e] # Convert the lists in numpy arrays coll2 = [] for c in sorted(collections): energy_this_collection = np.array(collections[c]) sener = energy_this_collection.sum() coll2.append(sener) print(c, ' : ', sener) del collections, energy_this_collection print("Time for second solution: %.3f" % (time() - t1)) t1 = time() table.cols.collection.create_csindex() # table.cols.collection.reindex() print("Time for indexing: %.3f" % (time() - t1)) ######################################################### # Third solution: load each collection separately ######################################################### t1 = time() coll3 = [] for c in np.unique(table.col('collection')): energy_this_collection = table.read_where( 'collection == c', field='energy') sener = energy_this_collection.sum() coll3.append(sener) print(c, ' : ', sener) del energy_this_collection print("Time for third solution: %.3f" % (time() - t1)) t1 = time() table2 = table.copy('/', 'EnergySortedByCollation', overwrite=True, sortby="collection", propindexes=True) print("Time for sorting: %.3f" % (time() - t1)) ##################################################################### # Fourth solution: load each collection separately. Sorted table. ##################################################################### t1 = time() coll4 = [] for c in np.unique(table2.col('collection')): energy_this_collection = table2.read_where( 'collection == c', field='energy') sener = energy_this_collection.sum() coll4.append(sener) print(c, ' : ', sener) del energy_this_collection print("Time for fourth solution: %.3f" % (time() - t1)) # Finally, check that all solutions do match assert coll1 == coll2 == coll3 == coll4 f.close() PyTables-v.3.1.1/bench/copy-bench.py000066400000000000000000000021071231437614300171670ustar00rootroot00000000000000from __future__ import print_function import tables import sys import time if len(sys.argv) != 3: print("usage: %s source_file dest_file", sys.argv[0]) filesrc = sys.argv[1] filedest = sys.argv[2] filehsrc = tables.open_file(filesrc) filehdest = tables.open_file(filedest, 'w') ntables = 0 tsize = 0 t1 = time.time() for group in filehsrc.walk_groups(): if isinstance(group._v_parent, tables.File): groupdest = filehdest.root else: pathname = group._v_parent._v_pathname groupdest = filehdest.create_group(pathname, group._v_name, title=group._v_title) for table in filehsrc.list_nodes(group, classname='Table'): print("copying table -->", table) table.copy(groupdest, table.name) ntables += 1 tsize += table.nrows * table.rowsize tsizeMB = tsize / (1024 * 1024) ttime = round(time.time() - t1, 3) speed = round(tsizeMB / ttime, 2) print("Copied %s tables for a total of %s MB in %s seconds (%s MB/s)" % (ntables, tsizeMB, ttime, speed)) filehsrc.close() filehdest.close() PyTables-v.3.1.1/bench/create-large-number-objects.py000066400000000000000000000023421231437614300224110ustar00rootroot00000000000000"This creates an HDF5 file with a potentially large number of objects" import sys import numpy import tables filename = sys.argv[1] # Open a new empty HDF5 file fileh = tables.open_file(filename, mode="w") # nlevels -- Number of levels in hierarchy # ngroups -- Number of groups on each level # ndatasets -- Number of arrays on each group # LR: Low ratio groups/datasets #nlevels, ngroups, ndatasets = (3, 1, 1000) # MR: Medium ratio groups/datasets nlevels, ngroups, ndatasets = (3, 10, 100) #nlevels, ngroups, ndatasets = (3, 5, 10) # HR: High ratio groups/datasets #nlevels, ngroups, ndatasets = (30, 10, 10) # Create an Array to save on disk a = numpy.array([-1, 2, 4], numpy.int16) group = fileh.root group2 = fileh.root for k in range(nlevels): for j in range(ngroups): for i in range(ndatasets): # Save the array on the HDF5 file fileh.create_array(group2, 'array' + str(i), a, "Signed short array") # Create a new group group2 = fileh.create_group(group, 'group' + str(j)) # Create a new group group3 = fileh.create_group(group, 'ngroup' + str(k)) # Iterate over this new group (group3) group = group3 group2 = group3 fileh.close() PyTables-v.3.1.1/bench/deep-tree-h5py.py000066400000000000000000000065751231437614300177120ustar00rootroot00000000000000from __future__ import print_function import os import subprocess from time import time import random import numpy import h5py random.seed(2) def show_stats(explain, tref): "Show the used memory (only works for Linux 2.6.x)." # Build the command to obtain memory info cmd = "cat /proc/%s/status" % os.getpid() sout = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout for line in sout: if line.startswith("VmSize:"): vmsize = int(line.split()[1]) elif line.startswith("VmRSS:"): vmrss = int(line.split()[1]) elif line.startswith("VmData:"): vmdata = int(line.split()[1]) elif line.startswith("VmStk:"): vmstk = int(line.split()[1]) elif line.startswith("VmExe:"): vmexe = int(line.split()[1]) elif line.startswith("VmLib:"): vmlib = int(line.split()[1]) sout.close() print("Memory usage: ******* %s *******" % explain) print("VmSize: %7s kB\tVmRSS: %7s kB" % (vmsize, vmrss)) print("VmData: %7s kB\tVmStk: %7s kB" % (vmdata, vmstk)) print("VmExe: %7s kB\tVmLib: %7s kB" % (vmexe, vmlib)) tnow = time() print("WallClock time:", round(tnow - tref, 3)) return tnow def populate(f, nlevels): g = f arr = numpy.zeros((10,), "f4") for i in range(nlevels): g["DS1"] = arr g["DS2"] = arr g.create_group('group2_') g = g.create_group('group') def getnode(f, nlevels, niter, range_): for i in range(niter): nlevel = random.randrange( (nlevels - range_) / 2, (nlevels + range_) / 2) groupname = "" for i in range(nlevel): groupname += "/group" groupname += "/DS1" f[groupname] if __name__ == '__main__': nlevels = 1024 niter = 1000 range_ = 256 profile = True doprofile = True verbose = False if doprofile: import pstats import cProfile as prof if profile: tref = time() if profile: show_stats("Abans de crear...", tref) f = h5py.File("/tmp/deep-tree.h5", 'w') if doprofile: prof.run('populate(f, nlevels)', 'populate.prof') stats = pstats.Stats('populate.prof') stats.strip_dirs() stats.sort_stats('time', 'calls') if verbose: stats.print_stats() else: stats.print_stats(20) else: populate(f, nlevels) f.close() if profile: show_stats("Despres de crear", tref) # if profile: tref = time() # if profile: show_stats("Abans d'obrir...", tref) # f = h5py.File("/tmp/deep-tree.h5", 'r') # if profile: show_stats("Abans d'accedir...", tref) # if doprofile: # prof.run('getnode(f, nlevels, niter, range_)', 'deep-tree.prof') # stats = pstats.Stats('deep-tree.prof') # stats.strip_dirs() # stats.sort_stats('time', 'calls') # if verbose: # stats.print_stats() # else: # stats.print_stats(20) # else: # getnode(f, nlevels, niter, range_) # if profile: show_stats("Despres d'accedir", tref) # f.close() # if profile: show_stats("Despres de tancar", tref) # f = h5py.File("/tmp/deep-tree.h5", 'r') # g = f # for i in range(nlevels): # dset = g["DS1"] # dset = g["DS2"] # group2 = g['group2_'] # g = g['group'] # f.close() PyTables-v.3.1.1/bench/deep-tree.py000066400000000000000000000076401231437614300170210ustar00rootroot00000000000000# Small benchmark for compare creation times with parameter # PYTABLES_SYS_ATTRS active or not. from __future__ import print_function import os import subprocess from time import time import random #import numpy import tables random.seed(2) def show_stats(explain, tref): "Show the used memory (only works for Linux 2.6.x)." # Build the command to obtain memory info cmd = "cat /proc/%s/status" % os.getpid() sout = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout for line in sout: if line.startswith("VmSize:"): vmsize = int(line.split()[1]) elif line.startswith("VmRSS:"): vmrss = int(line.split()[1]) elif line.startswith("VmData:"): vmdata = int(line.split()[1]) elif line.startswith("VmStk:"): vmstk = int(line.split()[1]) elif line.startswith("VmExe:"): vmexe = int(line.split()[1]) elif line.startswith("VmLib:"): vmlib = int(line.split()[1]) sout.close() print("Memory usage: ******* %s *******" % explain) print("VmSize: %7s kB\tVmRSS: %7s kB" % (vmsize, vmrss)) print("VmData: %7s kB\tVmStk: %7s kB" % (vmdata, vmstk)) print("VmExe: %7s kB\tVmLib: %7s kB" % (vmexe, vmlib)) tnow = time() print("WallClock time:", round(tnow - tref, 3)) return tnow def populate(f, nlevels): g = f.root #arr = numpy.zeros((10,), "f4") #descr = {'f0': tables.Int32Col(), 'f1': tables.Float32Col()} for i in range(nlevels): #dset = f.create_array(g, "DS1", arr) #dset = f.create_array(g, "DS2", arr) f.create_carray(g, "DS1", tables.IntAtom(), (10,)) f.create_carray(g, "DS2", tables.IntAtom(), (10,)) #dset = f.create_table(g, "DS1", descr) #dset = f.create_table(g, "DS2", descr) f.create_group(g, 'group2_') g = f.create_group(g, 'group') def getnode(f, nlevels, niter, range_): for i in range(niter): nlevel = random.randrange( (nlevels - range_) / 2, (nlevels + range_) / 2) groupname = "" for i in range(nlevel): groupname += "/group" groupname += "/DS1" f.get_node(groupname) if __name__ == '__main__': nlevels = 1024 niter = 256 range_ = 128 nodeCacheSlots = 64 pytables_sys_attrs = True profile = True doprofile = True verbose = False if doprofile: import pstats import cProfile as prof if profile: tref = time() if profile: show_stats("Abans de crear...", tref) f = tables.open_file("/tmp/PTdeep-tree.h5", 'w', node_cache_slots=nodeCacheSlots, pytables_sys_attrs=pytables_sys_attrs) if doprofile: prof.run('populate(f, nlevels)', 'populate.prof') stats = pstats.Stats('populate.prof') stats.strip_dirs() stats.sort_stats('time', 'calls') if verbose: stats.print_stats() else: stats.print_stats(20) else: populate(f, nlevels) f.close() if profile: show_stats("Despres de crear", tref) if profile: tref = time() if profile: show_stats("Abans d'obrir...", tref) f = tables.open_file("/tmp/PTdeep-tree.h5", 'r', node_cache_slots=nodeCacheSlots, pytables_sys_attrs=pytables_sys_attrs) if profile: show_stats("Abans d'accedir...", tref) if doprofile: prof.run('getnode(f, nlevels, niter, range_)', 'getnode.prof') stats = pstats.Stats('getnode.prof') stats.strip_dirs() stats.sort_stats('time', 'calls') if verbose: stats.print_stats() else: stats.print_stats(20) else: getnode(f, nlevels, niter, range_) if profile: show_stats("Despres d'accedir", tref) f.close() if profile: show_stats("Despres de tancar", tref) PyTables-v.3.1.1/bench/evaluate.py000066400000000000000000000125701231437614300167530ustar00rootroot00000000000000from __future__ import print_function import sys from time import time import numpy as np import tables as tb from numexpr.necompiler import ( getContext, getExprNames, getType, NumExpr) shape = (1000, 160000) #shape = (10,1600) filters = tb.Filters(complevel=1, complib="blosc", shuffle=0) ofilters = tb.Filters(complevel=1, complib="blosc", shuffle=0) #filters = tb.Filters(complevel=1, complib="lzo", shuffle=0) #ofilters = tb.Filters(complevel=1, complib="lzo", shuffle=0) # TODO: Makes it sense to add a 's'tring typecode here? typecode_to_dtype = {'b': 'bool', 'i': 'int32', 'l': 'int64', 'f': 'float32', 'd': 'float64', 'c': 'complex128'} def _compute(result, function, arguments, start=None, stop=None, step=None): """Compute the `function` over the `arguments` and put the outcome in `result`""" arg0 = arguments[0] if hasattr(arg0, 'maindim'): maindim = arg0.maindim (start, stop, step) = arg0._process_range_read(start, stop, step) nrowsinbuf = arg0.nrowsinbuf print("nrowsinbuf-->", nrowsinbuf) else: maindim = 0 (start, stop, step) = (0, len(arg0), 1) nrowsinbuf = len(arg0) shape = list(arg0.shape) shape[maindim] = len(range(start, stop, step)) # The slices parameter for arg0.__getitem__ slices = [slice(0, dim, 1) for dim in arg0.shape] # This is a hack to prevent doing unnecessary conversions # when copying buffers if hasattr(arg0, 'maindim'): for arg in arguments: arg._v_convert = False # Start the computation itself for start2 in range(start, stop, step * nrowsinbuf): # Save the records on disk stop2 = start2 + step * nrowsinbuf if stop2 > stop: stop2 = stop # Set the proper slice in the main dimension slices[maindim] = slice(start2, stop2, step) start3 = (start2 - start) / step stop3 = start3 + nrowsinbuf if stop3 > shape[maindim]: stop3 = shape[maindim] # Compute the slice to be filled in destination sl = [] for i in range(maindim): sl.append(slice(None, None, None)) sl.append(slice(start3, stop3, None)) # Get the values for computing the buffer values = [arg.__getitem__(tuple(slices)) for arg in arguments] result[tuple(sl)] = function(*values) # Activate the conversion again (default) if hasattr(arg0, 'maindim'): for arg in arguments: arg._v_convert = True return result def evaluate(ex, out=None, local_dict=None, global_dict=None, **kwargs): """Evaluate expression and return an array.""" # First, get the signature for the arrays in expression context = getContext(kwargs) names, _ = getExprNames(ex, context) # Get the arguments based on the names. call_frame = sys._getframe(1) if local_dict is None: local_dict = call_frame.f_locals if global_dict is None: global_dict = call_frame.f_globals arguments = [] types = [] for name in names: try: a = local_dict[name] except KeyError: a = global_dict[name] arguments.append(a) if hasattr(a, 'atom'): types.append(a.atom) else: types.append(a) # Create a signature signature = [(name, getType(type_)) for (name, type_) in zip(names, types)] print("signature-->", signature) # Compile the expression compiled_ex = NumExpr(ex, signature, [], **kwargs) print("fullsig-->", compiled_ex.fullsig) _compute(out, compiled_ex, arguments) return if __name__ == "__main__": iarrays = 0 oarrays = 0 doprofile = 1 dokprofile = 0 f = tb.open_file("/scratch2/faltet/evaluate.h5", "w") # Create some arrays if iarrays: a = np.ones(shape, dtype='float32') b = np.ones(shape, dtype='float32') * 2 c = np.ones(shape, dtype='float32') * 3 else: a = f.create_carray(f.root, 'a', tb.Float32Atom(dflt=1.), shape=shape, filters=filters) a[:] = 1. b = f.create_carray(f.root, 'b', tb.Float32Atom(dflt=2.), shape=shape, filters=filters) b[:] = 2. c = f.create_carray(f.root, 'c', tb.Float32Atom(dflt=3.), shape=shape, filters=filters) c[:] = 3. if oarrays: out = np.empty(shape, dtype='float32') else: out = f.create_carray(f.root, 'out', tb.Float32Atom(), shape=shape, filters=ofilters) t0 = time() if iarrays and oarrays: #out = ne.evaluate("a*b+c") out = a * b + c elif doprofile: import cProfile as prof import pstats prof.run('evaluate("a*b+c", out)', 'evaluate.prof') stats = pstats.Stats('evaluate.prof') stats.strip_dirs() stats.sort_stats('time', 'calls') stats.print_stats(20) elif dokprofile: from cProfile import Profile import lsprofcalltree prof = Profile() prof.run('evaluate("a*b+c", out)') kcg = lsprofcalltree.KCacheGrind(prof) ofile = open('evaluate.kcg', 'w') kcg.output(ofile) ofile.close() else: evaluate("a*b+c", out) print("Time for evaluate-->", round(time() - t0, 3)) # print "out-->", `out` # print `out[:]` f.close() PyTables-v.3.1.1/bench/expression.py000066400000000000000000000127661231437614300173530ustar00rootroot00000000000000from __future__ import print_function from time import time import os.path import numpy as np import tables as tb OUT_DIR = "/scratch2/faltet/" # the directory for data output shape = (1000, 1000 * 1000) # shape for input arrays expr = "a*b+1" # Expression to be computed nrows, ncols = shape def tables(docompute, dowrite, complib, verbose): # Filenames ifilename = os.path.join(OUT_DIR, "expression-inputs.h5") ofilename = os.path.join(OUT_DIR, "expression-outputs.h5") # Filters shuffle = True if complib == 'blosc': filters = tb.Filters(complevel=1, complib='blosc', shuffle=shuffle) elif complib == 'lzo': filters = tb.Filters(complevel=1, complib='lzo', shuffle=shuffle) elif complib == 'zlib': filters = tb.Filters(complevel=1, complib='zlib', shuffle=shuffle) else: filters = tb.Filters(complevel=0, shuffle=False) if verbose: print("Will use filters:", filters) if dowrite: f = tb.open_file(ifilename, 'w') # Build input arrays t0 = time() root = f.root a = f.create_carray(root, 'a', tb.Float32Atom(), shape, filters=filters) b = f.create_carray(root, 'b', tb.Float32Atom(), shape, filters=filters) if verbose: print("chunkshape:", a.chunkshape) print("chunksize:", np.prod(a.chunkshape) * a.dtype.itemsize) #row = np.linspace(0, 1, ncols) row = np.arange(0, ncols, dtype='float32') for i in range(nrows): a[i] = row * (i + 1) b[i] = row * (i + 1) * 2 f.close() print("[tables.Expr] Time for creating inputs:", round(time() - t0, 3)) if docompute: f = tb.open_file(ifilename, 'r') fr = tb.open_file(ofilename, 'w') a = f.root.a b = f.root.b r1 = f.create_carray(fr.root, 'r1', tb.Float32Atom(), shape, filters=filters) # The expression e = tb.Expr(expr) e.set_output(r1) t0 = time() e.eval() if verbose: print("First ten values:", r1[0, :10]) f.close() fr.close() print("[tables.Expr] Time for computing & save:", round(time() - t0, 3)) def memmap(docompute, dowrite, verbose): afilename = os.path.join(OUT_DIR, "memmap-a.bin") bfilename = os.path.join(OUT_DIR, "memmap-b.bin") rfilename = os.path.join(OUT_DIR, "memmap-output.bin") if dowrite: t0 = time() a = np.memmap(afilename, dtype='float32', mode='w+', shape=shape) b = np.memmap(bfilename, dtype='float32', mode='w+', shape=shape) # Fill arrays a and b #row = np.linspace(0, 1, ncols) row = np.arange(0, ncols, dtype='float32') for i in range(nrows): a[i] = row * (i + 1) b[i] = row * (i + 1) * 2 del a, b # flush data print("[numpy.memmap] Time for creating inputs:", round(time() - t0, 3)) if docompute: t0 = time() # Reopen inputs in read-only mode a = np.memmap(afilename, dtype='float32', mode='r', shape=shape) b = np.memmap(bfilename, dtype='float32', mode='r', shape=shape) # Create the array output r = np.memmap(rfilename, dtype='float32', mode='w+', shape=shape) # Do the computation row by row for i in range(nrows): r[i] = eval(expr, {'a': a[i], 'b': b[i]}) if verbose: print("First ten values:", r[0, :10]) del a, b del r # flush output data print("[numpy.memmap] Time for compute & save:", round(time() - t0, 3)) def do_bench(what, documpute, dowrite, complib, verbose): if what == "tables": tables(docompute, dowrite, complib, verbose) if what == "memmap": memmap(docompute, dowrite, verbose) if __name__ == "__main__": import sys import os import getopt usage = """usage: %s [-T] [-M] [-c] [-w] [-v] [-z complib] -T use tables.Expr -M use numpy.memmap -c do the computation only -w write inputs only -v verbose mode -z select compression library ('zlib' or 'lzo'). Default is None. """ % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'TMcwvz:') except: sys.stderr.write(usage) sys.exit(1) # default options usepytables = False usememmap = False docompute = False dowrite = False verbose = False complib = None # Get the options for option in opts: if option[0] == '-T': usepytables = True elif option[0] == '-M': usememmap = True elif option[0] == '-c': docompute = True elif option[0] == '-w': dowrite = True elif option[0] == '-v': verbose = True elif option[0] == '-z': complib = option[1] if complib not in ('blosc', 'lzo', 'zlib'): print(("complib must be 'lzo' or 'zlib' " "and you passed: '%s'" % complib)) sys.exit(1) # If not a backend selected, abort if not usepytables and not usememmap: print("Please select a backend:") print("PyTables.Expr: -T") print("NumPy.memmap: -M") sys.exit(1) # Select backend and do the benchmark if usepytables: what = "tables" if usememmap: what = "memmap" do_bench(what, docompute, dowrite, complib, verbose) PyTables-v.3.1.1/bench/get-figures-ranges.py000066400000000000000000000177201231437614300206450ustar00rootroot00000000000000from __future__ import print_function from pylab import * linewidth = 2 #markers= ['+', ',', 'o', '.', 's', 'v', 'x', '>', '<', '^'] #markers= [ 'x', '+', 'o', 's', 'v', '^', '>', '<', ] markers = ['s', 'o', 'v', '^', '+', 'x', '>', '<', ] markersize = 8 def get_values(filename): f = open(filename) sizes = [] values = [] isize = None for line in f: if line.startswith('range'): tmp = line.split(':')[1] tmp = tmp.strip() tmp = tmp[1:-1] lower, upper = int(tmp.split(',')[0]), int(tmp.split(',')[1]) isize = upper - lower # print "isize-->", isize if isize is None or isize == 0: continue if insert and line.startswith('Insert time'): tmp = line.split(':')[1] #itime = float(tmp[:tmp.index(',')]) itime = float(tmp) sizes.append(isize) values.append(itime) elif line.startswith('Index time'): tmp = line.split(':')[1] #xtime = float(tmp[:tmp.index(',')]) xtime = float(tmp) txtime += xtime if create_index and create_index in line: sizes.append(isize) values.append(xtime) elif create_total and txtime > xtime: sizes.append(isize) values.append(txtime) elif table_size and line.startswith('Table size'): tsize = float(line.split(':')[1]) sizes.append(isize) values.append(tsize) elif indexes_size and line.startswith('Indexes size'): xsize = float(line.split(':')[1]) sizes.append(isize) values.append(xsize) elif total_size and line.startswith('Full size'): fsize = float(line.split(':')[1]) sizes.append(isize) values.append(fsize) elif ((query or query_cold or query_warm) and line.startswith('[NOREP]')): tmp = line.split(':')[1] try: qtime = float(tmp[:tmp.index('+-')]) except ValueError: qtime = float(tmp) if colname in line: if query and '1st' in line: sizes.append(isize) values.append(qtime) elif query_cold and 'cold' in line: sizes.append(isize) values.append(qtime) elif query_warm and 'warm' in line: sizes.append(isize) values.append(qtime) f.close() return sizes, values def show_plot(plots, yaxis, legends, gtitle): xlabel('Number of hits') ylabel(yaxis) title(gtitle) #ylim(0, 100) grid(True) # legends = [f[f.find('-'):f.index('.out')] for f in filenames] # legends = [l.replace('-', ' ') for l in legends] #legend([p[0] for p in plots], legends, loc = "upper left") legend([p[0] for p in plots], legends, loc="best") #subplots_adjust(bottom=0.2, top=None, wspace=0.2, hspace=0.2) if outfile: savefig(outfile) else: show() if __name__ == '__main__': import sys import getopt usage = """usage: %s [-o file] [-t title] [--insert] [--create-index] [--create-total] [--table-size] [--indexes-size] [--total-size] [--query=colname] [--query-cold=colname] [--query-warm=colname] files -o filename for output (only .png and .jpg extensions supported) -t title of the plot --insert -- Insert time for table --create-index=colname -- Index time for column --create-total -- Total time for creation of table + indexes --table-size -- Size of table --indexes-size -- Size of all indexes --total-size -- Total size of table + indexes --query=colname -- Time for querying the specified column --query-cold=colname -- Time for querying the specified column (cold cache) --query-warm=colname -- Time for querying the specified column (warm cache) \n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'o:t:', ['insert', 'create-index=', 'create-total', 'table-size', 'indexes-size', 'total-size', 'query=', 'query-cold=', 'query-warm=', ]) except: sys.stderr.write(usage) sys.exit(0) progname = sys.argv[0] args = sys.argv[1:] # if we pass too few parameters, abort if len(pargs) < 1: sys.stderr.write(usage) sys.exit(0) # default options outfile = None insert = 0 create_index = None create_total = 0 table_size = 0 indexes_size = 0 total_size = 0 query = 0 query_cold = 0 query_warm = 0 colname = None yaxis = "No axis name" tit = None gtitle = "Please set a title!" # Get the options for option in opts: if option[0] == '-o': outfile = option[1] elif option[0] == '-t': tit = option[1] elif option[0] == '--insert': insert = 1 yaxis = "Time (s)" gtitle = "Insert time for table" elif option[0] == '--create-index': create_index = option[1] yaxis = "Time (s)" gtitle = "Create index time for column " + create_index elif option[0] == '--create-total': create_total = 1 yaxis = "Time (s)" gtitle = "Create time for table + indexes" elif option[0] == '--table-size': table_size = 1 yaxis = "Size (MB)" gtitle = "Table size" elif option[0] == '--indexes-size': indexes_size = 1 yaxis = "Size (MB)" gtitle = "Indexes size" elif option[0] == '--total-size': total_size = 1 yaxis = "Size (MB)" gtitle = "Total size (table + indexes)" elif option[0] == '--query': query = 1 colname = option[1] yaxis = "Time (s)" gtitle = "Query time for " + colname + " column (first query)" elif option[0] == '--query-cold': query_cold = 1 colname = option[1] yaxis = "Time (s)" gtitle = "Query time for " + colname + " column (cold cache)" elif option[0] == '--query-warm': query_warm = 1 colname = option[1] yaxis = "Time (s)" gtitle = "Query time for " + colname + " column (warm cache)" filenames = pargs if tit: gtitle = tit plots = [] legends = [] for filename in filenames: plegend = filename[filename.find('-'):filename.index('.out')] plegend = plegend.replace('-', ' ') xval, yval = get_values(filename) print("Values for %s --> %s, %s" % (filename, xval, yval)) if "PyTables" in filename or "pytables" in filename: plot = loglog(xval, yval, linewidth=2) #plot = semilogx(xval, yval, linewidth=2) plots.append(plot) setp(plot, marker=markers[0], markersize=markersize, linewidth=linewidth) else: plots.append(loglog(xval, yval, linewidth=3, color='m')) #plots.append(semilogx(xval, yval, linewidth=3, color='m')) #plots.append(semilogx(xval, yval, linewidth=5)) legends.append(plegend) if 0: # Per a introduir dades simulades si es vol... xval = [1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000] # yval = [0.003, 0.005, 0.02, 0.06, 1.2, # 40, 210] yval = [0.0009, 0.0011, 0.0022, 0.005, 0.02, 0.2, 5.6] plots.append(loglog(xval, yval, linewidth=5)) legends.append("PyTables Std") show_plot(plots, yaxis, legends, gtitle) PyTables-v.3.1.1/bench/get-figures.py000066400000000000000000000245761231437614300173770ustar00rootroot00000000000000from __future__ import print_function from pylab import * linewidth = 2 #markers= ['+', ',', 'o', '.', 's', 'v', 'x', '>', '<', '^'] #markers= [ 'x', '+', 'o', 's', 'v', '^', '>', '<', ] markers = ['s', 'o', 'v', '^', '+', 'x', '>', '<', ] markersize = 8 def get_values(filename): f = open(filename) sizes = [] values = [] for line in f: if line.startswith('Processing database:'): txtime = 0 line = line.split(':')[1] # Check if entry is compressed and if has to be processed line = line[:line.rfind('.')] params = line.split('-') for param in params: if param[-1] in ('k', 'm', 'g'): size = param isize = int(size[:-1]) * 1000 if size[-1] == "m": isize *= 1000 elif size[-1] == "g": isize *= 1000 * 1000 elif insert and line.startswith('Insert time'): tmp = line.split(':')[1] itime = float(tmp) sizes.append(isize) values.append(itime) elif (overlaps or entropy) and line.startswith('overlaps'): tmp = line.split(':')[1] e1, e2 = tmp.split() if isize in sizes: sizes.pop() values.pop() sizes.append(isize) if overlaps: values.append(int(e1) + 1) else: values.append(float(e2) + 1) elif (create_total or create_index) and line.startswith('Index time'): tmp = line.split(':')[1] xtime = float(tmp) txtime += xtime if create_index and create_index in line: sizes.append(isize) values.append(xtime) elif create_total and txtime > xtime: sizes.append(isize) values.append(txtime) elif table_size and line.startswith('Table size'): tsize = float(line.split(':')[1]) sizes.append(isize) values.append(tsize) elif indexes_size and line.startswith('Indexes size'): xsize = float(line.split(':')[1]) sizes.append(isize) values.append(xsize) elif total_size and line.startswith('Full size'): fsize = float(line.split(':')[1]) sizes.append(isize) values.append(fsize) elif query and line.startswith('Query time'): tmp = line.split(':')[1] qtime = float(tmp) if colname in line: sizes.append(isize) values.append(qtime) elif ((query or query_cold or query_warm) and line.startswith('[NOREP]')): tmp = line.split(':')[1] try: qtime = float(tmp[:tmp.index('+-')]) except ValueError: qtime = float(tmp) if colname in line: if query and '1st' in line: sizes.append(isize) values.append(qtime) elif query_cold and 'cold' in line: sizes.append(isize) values.append(qtime) elif query_warm and 'warm' in line: sizes.append(isize) values.append(qtime) elif query_repeated and line.startswith('[REP]'): if colname in line and 'warm' in line: tmp = line.split(':')[1] qtime = float(tmp[:tmp.index('+-')]) sizes.append(isize) values.append(qtime) f.close() return sizes, values def show_plot(plots, yaxis, legends, gtitle): xlabel('Number of rows') ylabel(yaxis) title(gtitle) #xlim(10**3, 10**9) xlim(10 ** 3, 10 ** 10) # ylim(1.0e-5) #ylim(-1e4, 1e5) #ylim(-1e3, 1e4) #ylim(-1e2, 1e3) grid(True) # legends = [f[f.find('-'):f.index('.out')] for f in filenames] # legends = [l.replace('-', ' ') for l in legends] legend([p[0] for p in plots], legends, loc="upper left") #legend([p[0] for p in plots], legends, loc = "center left") #subplots_adjust(bottom=0.2, top=None, wspace=0.2, hspace=0.2) if outfile: savefig(outfile) else: show() if __name__ == '__main__': import sys import getopt usage = """usage: %s [-o file] [-t title] [--insert] [--create-index] [--create-total] [--overlaps] [--entropy] [--table-size] [--indexes-size] [--total-size] [--query=colname] [--query-cold=colname] [--query-warm=colname] [--query-repeated=colname] files -o filename for output (only .png and .jpg extensions supported) -t title of the plot --insert -- Insert time for table --create-index=colname -- Index time for column --create-total -- Total time for creation of table + indexes --overlaps -- The overlapping for the created index --entropy -- The entropy for the created index --table-size -- Size of table --indexes-size -- Size of all indexes --total-size -- Total size of table + indexes --query=colname -- Time for querying the specified column --query-cold=colname -- Time for querying the specified column (cold cache) --query-warm=colname -- Time for querying the specified column (warm cache) --query-repeated=colname -- Time for querying the specified column (rep query) \n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'o:t:', ['insert', 'create-index=', 'create-total', 'overlaps', 'entropy', 'table-size', 'indexes-size', 'total-size', 'query=', 'query-cold=', 'query-warm=', 'query-repeated=', ]) except: sys.stderr.write(usage) sys.exit(0) progname = sys.argv[0] args = sys.argv[1:] # if we pass too few parameters, abort if len(pargs) < 1: sys.stderr.write(usage) sys.exit(0) # default options outfile = None insert = 0 create_index = None create_total = 0 overlaps = 0 entropy = 0 table_size = 0 indexes_size = 0 total_size = 0 query = 0 query_cold = 0 query_warm = 0 query_repeated = 0 colname = None yaxis = "No axis name" tit = None gtitle = "Please set a title!" # Get the options for option in opts: if option[0] == '-o': outfile = option[1] elif option[0] == '-t': tit = option[1] elif option[0] == '--insert': insert = 1 yaxis = "Time (s)" gtitle = "Insert time for table" elif option[0] == '--create-index': create_index = option[1] yaxis = "Time (s)" gtitle = "Create index time for " + create_index + " column" elif option[0] == '--create-total': create_total = 1 yaxis = "Time (s)" gtitle = "Create time for table + indexes" elif option[0] == '--overlaps': overlaps = 1 yaxis = "Overlapping index + 1" gtitle = "Overlapping for col4 column" elif option[0] == '--entropy': entropy = 1 yaxis = "Entropy + 1" gtitle = "Entropy for col4 column" elif option[0] == '--table-size': table_size = 1 yaxis = "Size (MB)" gtitle = "Table size" elif option[0] == '--indexes-size': indexes_size = 1 yaxis = "Size (MB)" #gtitle = "Indexes size" gtitle = "Index size for col4 column" elif option[0] == '--total-size': total_size = 1 yaxis = "Size (MB)" gtitle = "Total size (table + indexes)" elif option[0] == '--query': query = 1 colname = option[1] yaxis = "Time (s)" gtitle = "Query time for " + colname + " column (first query)" elif option[0] == '--query-cold': query_cold = 1 colname = option[1] yaxis = "Time (s)" gtitle = "Query time for " + colname + " column (cold cache)" elif option[0] == '--query-warm': query_warm = 1 colname = option[1] yaxis = "Time (s)" gtitle = "Query time for " + colname + " column (warm cache)" elif option[0] == '--query-repeated': query_repeated = 1 colname = option[1] yaxis = "Time (s)" gtitle = "Query time for " + colname + " column (repeated query)" gtitle = gtitle.replace('col2', 'Int32') gtitle = gtitle.replace('col4', 'Float64') filenames = pargs if tit: gtitle = tit plots = [] legends = [] for i, filename in enumerate(filenames): plegend = filename[:filename.index('.out')] plegend = plegend.replace('-', ' ') #plegend = plegend.replace('zlib1', '') if filename.find('PyTables') != -1: xval, yval = get_values(filename) print("Values for %s --> %s, %s" % (filename, xval, yval)) if xval != []: plot = loglog(xval, yval) #plot = semilogx(xval, yval) setp(plot, marker=markers[i], markersize=markersize, linewidth=linewidth) plots.append(plot) legends.append(plegend) else: xval, yval = get_values(filename) print("Values for %s --> %s, %s" % (filename, xval, yval)) plots.append(loglog(xval, yval, linewidth=3, color='m')) #plots.append(semilogx(xval, yval, linewidth=linewidth, color='m')) legends.append(plegend) if 0: # Per a introduir dades simulades si es vol... xval = [1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000] # yval = [0.003, 0.005, 0.02, 0.06, 1.2, # 40, 210] yval = [0.0009, 0.0011, 0.0022, 0.005, 0.02, 0.2, 5.6] plots.append(loglog(xval, yval, linewidth=linewidth)) legends.append("PyTables Std") show_plot(plots, yaxis, legends, gtitle) PyTables-v.3.1.1/bench/indexed_search.py000066400000000000000000000417721231437614300201200ustar00rootroot00000000000000from __future__ import print_function from time import time import subprocess import random import numpy # Constants STEP = 1000 * 100 # the size of the buffer to fill the table, in rows SCALE = 0.1 # standard deviation of the noise compared with actual # values NI_NTIMES = 1 # The number of queries for doing a mean (non-idx cols) # COLDCACHE = 10 # The number of reads where the cache is considered 'cold' # WARMCACHE = 50 # The number of reads until the cache is considered 'warmed' # READ_TIMES = WARMCACHE+50 # The number of complete calls to DB.query_db() # COLDCACHE = 50 # The number of reads where the cache is considered 'cold' # WARMCACHE = 50 # The number of reads until the cache is considered 'warmed' # READ_TIMES = WARMCACHE+50 # The number of complete calls to DB.query_db() MROW = 1000 * 1000. # Test values COLDCACHE = 5 # The number of reads where the cache is considered 'cold' WARMCACHE = 5 # The number of reads until the cache is considered 'warmed' READ_TIMES = 10 # The number of complete calls to DB.query_db() # global variables rdm_cod = ['lin', 'rnd'] prec = 6 # precision for printing floats purposes def get_nrows(nrows_str): if nrows_str.endswith("k"): return int(float(nrows_str[:-1]) * 1000) elif nrows_str.endswith("m"): return int(float(nrows_str[:-1]) * 1000 * 1000) elif nrows_str.endswith("g"): return int(float(nrows_str[:-1]) * 1000 * 1000 * 1000) else: raise ValueError( "value of nrows must end with either 'k', 'm' or 'g' suffixes.") class DB(object): def __init__(self, nrows, rng, userandom): global step, scale self.step = STEP self.scale = SCALE self.rng = rng self.userandom = userandom self.filename = '-'.join([rdm_cod[userandom], nrows]) self.nrows = get_nrows(nrows) def get_db_size(self): sout = subprocess.Popen("sync;du -s %s" % self.filename, shell=True, stdout=subprocess.PIPE).stdout line = [l for l in sout][0] return int(line.split()[0]) def print_mtime(self, t1, explain): mtime = time() - t1 print("%s:" % explain, round(mtime, 6)) print("Krows/s:", round((self.nrows / 1000.) / mtime, 6)) def print_qtime(self, colname, ltimes): qtime1 = ltimes[0] # First measured time qtime2 = ltimes[-1] # Last measured time print("Query time for %s:" % colname, round(qtime1, 6)) print("Mrows/s:", round((self.nrows / (MROW)) / qtime1, 6)) print("Query time for %s (cached):" % colname, round(qtime2, 6)) print("Mrows/s (cached):", round((self.nrows / (MROW)) / qtime2, 6)) def norm_times(self, ltimes): "Get the mean and stddev of ltimes, avoiding the extreme values." lmean = ltimes.mean() lstd = ltimes.std() ntimes = ltimes[ltimes < lmean + lstd] nmean = ntimes.mean() nstd = ntimes.std() return nmean, nstd def print_qtime_idx(self, colname, ltimes, repeated, verbose): if repeated: r = "[REP] " else: r = "[NOREP] " ltimes = numpy.array(ltimes) ntimes = len(ltimes) qtime1 = ltimes[0] # First measured time ctimes = ltimes[1:COLDCACHE] cmean, cstd = self.norm_times(ctimes) wtimes = ltimes[WARMCACHE:] wmean, wstd = self.norm_times(wtimes) if verbose: print("Times for cold cache:\n", ctimes) # print "Times for warm cache:\n", wtimes print("Histogram for warm cache: %s\n%s" % numpy.histogram(wtimes)) print("%s1st query time for %s:" % (r, colname), round(qtime1, prec)) print("%sQuery time for %s (cold cache):" % (r, colname), round(cmean, prec), "+-", round(cstd, prec)) print("%sQuery time for %s (warm cache):" % (r, colname), round(wmean, prec), "+-", round(wstd, prec)) def print_db_sizes(self, init, filled, indexed): table_size = (filled - init) / 1024. indexes_size = (indexed - filled) / 1024. print("Table size (MB):", round(table_size, 3)) print("Indexes size (MB):", round(indexes_size, 3)) print("Full size (MB):", round(table_size + indexes_size, 3)) def fill_arrays(self, start, stop): arr_f8 = numpy.arange(start, stop, dtype='float64') arr_i4 = numpy.arange(start, stop, dtype='int32') if self.userandom: arr_f8 += numpy.random.normal(0, stop * self.scale, size=stop - start) arr_i4 = numpy.array(arr_f8, dtype='int32') return arr_i4, arr_f8 def create_db(self, dtype, kind, optlevel, verbose): self.con = self.open_db(remove=1) self.create_table(self.con) init_size = self.get_db_size() t1 = time() self.fill_table(self.con) table_size = self.get_db_size() self.print_mtime(t1, 'Insert time') self.index_db(dtype, kind, optlevel, verbose) indexes_size = self.get_db_size() self.print_db_sizes(init_size, table_size, indexes_size) self.close_db(self.con) def index_db(self, dtype, kind, optlevel, verbose): if dtype == "int": idx_cols = ['col2'] elif dtype == "float": idx_cols = ['col4'] else: idx_cols = ['col2', 'col4'] for colname in idx_cols: t1 = time() self.index_col(self.con, colname, kind, optlevel, verbose) self.print_mtime(t1, 'Index time (%s)' % colname) def query_db(self, niter, dtype, onlyidxquery, onlynonidxquery, avoidfscache, verbose, inkernel): self.con = self.open_db() if dtype == "int": reg_cols = ['col1'] idx_cols = ['col2'] elif dtype == "float": reg_cols = ['col3'] idx_cols = ['col4'] else: reg_cols = ['col1', 'col3'] idx_cols = ['col2', 'col4'] if avoidfscache: rseed = int(numpy.random.randint(self.nrows)) else: rseed = 19 # Query for non-indexed columns numpy.random.seed(rseed) base = numpy.random.randint(self.nrows) if not onlyidxquery: for colname in reg_cols: ltimes = [] random.seed(rseed) for i in range(NI_NTIMES): t1 = time() results = self.do_query(self.con, colname, base, inkernel) ltimes.append(time() - t1) if verbose: print("Results len:", results) self.print_qtime(colname, ltimes) # Always reopen the file after *every* query loop. # Necessary to make the benchmark to run correctly. self.close_db(self.con) self.con = self.open_db() # Query for indexed columns if not onlynonidxquery: for colname in idx_cols: ltimes = [] numpy.random.seed(rseed) rndbase = numpy.random.randint(self.nrows, size=niter) # First, non-repeated queries for i in range(niter): base = rndbase[i] t1 = time() results = self.do_query(self.con, colname, base, inkernel) #results, tprof = self.do_query( # self.con, colname, base, inkernel) ltimes.append(time() - t1) if verbose: print("Results len:", results) self.print_qtime_idx(colname, ltimes, False, verbose) # Always reopen the file after *every* query loop. # Necessary to make the benchmark to run correctly. self.close_db(self.con) self.con = self.open_db() ltimes = [] # Second, repeated queries # for i in range(niter): # t1=time() # results = self.do_query( # self.con, colname, base, inkernel) # results, tprof = self.do_query(self.con, colname, base, inkernel) # ltimes.append(time()-t1) # if verbose: # print "Results len:", results # self.print_qtime_idx(colname, ltimes, True, verbose) # Print internal PyTables index tprof statistics #tprof = numpy.array(tprof) #tmean, tstd = self.norm_times(tprof) # print "tprof-->", round(tmean, prec), "+-", round(tstd, prec) # print "tprof hist-->", \ # numpy.histogram(tprof) # print "tprof raw-->", tprof # Always reopen the file after *every* query loop. # Necessary to make the benchmark to run correctly. self.close_db(self.con) self.con = self.open_db() # Finally, close the file. self.close_db(self.con) def close_db(self, con): con.close() if __name__ == "__main__": import sys import getopt try: import psyco psyco_imported = 1 except: psyco_imported = 0 usage = """usage: %s [-T] [-P] [-v] [-f] [-k] [-p] [-m] [-c] [-q] [-i] [-I] [-S] [-x] [-z complevel] [-l complib] [-R range] [-N niter] [-n nrows] [-d datadir] [-O level] [-t kind] [-s] col -Q [suplim] -T use Pytables -P use Postgres -v verbose -f do a profile of the run (only query functionality & Python 2.5) -k do a profile for kcachegrind use (out file is 'indexed_search.kcg') -p use "psyco" if available -m use random values to fill the table -q do a query (both indexed and non-indexed versions) -i do a query (just indexed one) -I do a query (just in-kernel one) -S do a query (just standard one) -x choose a different seed for random numbers (i.e. avoid FS cache) -c create the database -z compress with zlib (no compression by default) -l use complib for compression (zlib used by default) -R select a range in a field in the form "start,stop" (def "0,10") -N number of iterations for reading -n sets the number of rows (in krows) in each table -d directory to save data (default: data.nobackup) -O set the optimization level for PyTables indexes -t select the index type: "medium" (default) or "full", "light", "ultralight" -s select a type column for operations ('int' or 'float'. def all) -Q do a repeteated query up to 10**value \n""" % sys.argv[0] try: opts, pargs = getopt.getopt( sys.argv[1:], 'TPvfkpmcqiISxz:l:R:N:n:d:O:t:s:Q:') except: sys.stderr.write(usage) sys.exit(1) # default options usepytables = 0 usepostgres = 0 verbose = 0 doprofile = 0 dokprofile = 0 usepsyco = 0 userandom = 0 docreate = 0 optlevel = 0 kind = "medium" docompress = 0 complib = "zlib" doquery = False onlyidxquery = False onlynonidxquery = False inkernel = True avoidfscache = 0 #rng = [-10, 10] rng = [-1000, -1000] repeatquery = 0 repeatvalue = 0 krows = '1k' niter = READ_TIMES dtype = "all" datadir = "data.nobackup" # Get the options for option in opts: if option[0] == '-T': usepytables = 1 elif option[0] == '-P': usepostgres = 1 elif option[0] == '-v': verbose = 1 elif option[0] == '-f': doprofile = 1 elif option[0] == '-k': dokprofile = 1 elif option[0] == '-p': usepsyco = 1 elif option[0] == '-m': userandom = 1 elif option[0] == '-c': docreate = 1 elif option[0] == '-q': doquery = True elif option[0] == '-i': doquery = True onlyidxquery = True elif option[0] == '-I': doquery = True onlynonidxquery = True elif option[0] == '-S': doquery = True onlynonidxquery = True inkernel = False elif option[0] == '-x': avoidfscache = 1 elif option[0] == '-z': docompress = int(option[1]) elif option[0] == '-l': complib = option[1] elif option[0] == '-R': rng = [int(i) for i in option[1].split(",")] elif option[0] == '-N': niter = int(option[1]) elif option[0] == '-n': krows = option[1] elif option[0] == '-d': datadir = option[1] elif option[0] == '-O': optlevel = int(option[1]) elif option[0] == '-t': if option[1] in ('full', 'medium', 'light', 'ultralight'): kind = option[1] else: print("kind should be either 'full', 'medium', 'light' or " "'ultralight'") sys.exit(1) elif option[0] == '-s': if option[1] in ('int', 'float'): dtype = option[1] else: print("column should be either 'int' or 'float'") sys.exit(1) elif option[0] == '-Q': repeatquery = 1 repeatvalue = int(option[1]) # If not database backend selected, abort if not usepytables and not usepostgres: print("Please select a backend:") print("PyTables: -T") print("Postgres: -P") sys.exit(1) # Create the class for the database if usepytables: from pytables_backend import PyTables_DB db = PyTables_DB(krows, rng, userandom, datadir, docompress, complib, kind, optlevel) elif usepostgres: from postgres_backend import Postgres_DB db = Postgres_DB(krows, rng, userandom) if not avoidfscache: # in order to always generate the same random sequence numpy.random.seed(20) if verbose: if userandom: print("using random values") if onlyidxquery: print("doing indexed queries only") if psyco_imported and usepsyco: psyco.bind(db.create_db) psyco.bind(db.query_db) if docreate: if verbose: print("writing %s rows" % krows) db.create_db(dtype, kind, optlevel, verbose) if doquery: print("Calling query_db() %s times" % niter) if doprofile: import pstats import cProfile as prof prof.run( 'db.query_db(niter, dtype, onlyidxquery, onlynonidxquery, ' 'avoidfscache, verbose, inkernel)', 'indexed_search.prof') stats = pstats.Stats('indexed_search.prof') stats.strip_dirs() stats.sort_stats('time', 'calls') if verbose: stats.print_stats() else: stats.print_stats(20) elif dokprofile: from cProfile import Profile import lsprofcalltree prof = Profile() prof.run( 'db.query_db(niter, dtype, onlyidxquery, onlynonidxquery, ' 'avoidfscache, verbose, inkernel)') kcg = lsprofcalltree.KCacheGrind(prof) ofile = open('indexed_search.kcg', 'w') kcg.output(ofile) ofile.close() elif doprofile: import hotshot import hotshot.stats prof = hotshot.Profile("indexed_search.prof") benchtime, stones = prof.run( 'db.query_db(niter, dtype, onlyidxquery, onlynonidxquery, ' 'avoidfscache, verbose, inkernel)') prof.close() stats = hotshot.stats.load("indexed_search.prof") stats.strip_dirs() stats.sort_stats('time', 'calls') stats.print_stats(20) else: db.query_db(niter, dtype, onlyidxquery, onlynonidxquery, avoidfscache, verbose, inkernel) if repeatquery: # Start by a range which is almost None db.rng = [1, 1] if verbose: print("range:", db.rng) db.query_db(niter, dtype, onlyidxquery, onlynonidxquery, avoidfscache, verbose, inkernel) for i in range(repeatvalue): for j in (1, 2, 5): rng = j * 10 ** i db.rng = [-rng / 2, rng / 2] if verbose: print("range:", db.rng) # if usepostgres: # os.system( # "echo 1 > /proc/sys/vm/drop_caches;" # " /etc/init.d/postgresql restart") # else: # os.system("echo 1 > /proc/sys/vm/drop_caches") db.query_db(niter, dtype, onlyidxquery, onlynonidxquery, avoidfscache, verbose, inkernel) PyTables-v.3.1.1/bench/keysort.py000066400000000000000000000016161231437614300166440ustar00rootroot00000000000000from __future__ import print_function from tables.indexesextension import keysort import numpy from time import time N = 1000 * 1000 rnd = numpy.random.randint(N, size=N) for dtype1 in ('S6', 'b1', 'i1', 'i2', 'i4', 'i8', 'u1', 'u2', 'u4', 'u8', 'f4', 'f8'): for dtype2 in ('u4', 'i8'): print("dtype array1, array2-->", dtype1, dtype2) a = numpy.array(rnd, dtype1) b = numpy.arange(N, dtype=dtype2) c = a.copy() t1 = time() d = c.argsort() # c.sort() # e=c e = c[d] f = b[d] tref = time() - t1 print("normal sort time-->", tref) t1 = time() keysort(a, b) tks = time() - t1 print("keysort time-->", tks, " %.2fx" % (tref / tks,)) assert numpy.alltrue(a == e) #assert numpy.alltrue(b == d) assert numpy.alltrue(f == d) PyTables-v.3.1.1/bench/lookup_bench.py000066400000000000000000000173721231437614300176220ustar00rootroot00000000000000"""Benchmark to help choosing the best chunksize so as to optimize the access time in random lookups.""" from __future__ import print_function from time import time import os import subprocess import numpy import tables # Constants NOISE = 1e-15 # standard deviation of the noise compared with actual values rdm_cod = ['lin', 'rnd'] def get_nrows(nrows_str): if nrows_str.endswith("k"): return int(float(nrows_str[:-1]) * 1000) elif nrows_str.endswith("m"): return int(float(nrows_str[:-1]) * 1000 * 1000) elif nrows_str.endswith("g"): return int(float(nrows_str[:-1]) * 1000 * 1000 * 1000) else: raise ValueError( "value of nrows must end with either 'k', 'm' or 'g' suffixes.") class DB(object): def __init__(self, nrows, dtype, chunksize, userandom, datadir, docompress=0, complib='zlib'): self.dtype = dtype self.docompress = docompress self.complib = complib self.filename = '-'.join([rdm_cod[userandom], "n" + nrows, "s" + chunksize, dtype]) # Complete the filename self.filename = "lookup-" + self.filename if docompress: self.filename += '-' + complib + str(docompress) self.filename = datadir + '/' + self.filename + '.h5' print("Processing database:", self.filename) self.userandom = userandom self.nrows = get_nrows(nrows) self.chunksize = get_nrows(chunksize) self.step = self.chunksize self.scale = NOISE def get_db_size(self): sout = subprocess.Popen("sync;du -s %s" % self.filename, shell=True, stdout=subprocess.PIPE).stdout line = [l for l in sout][0] return int(line.split()[0]) def print_mtime(self, t1, explain): mtime = time() - t1 print("%s:" % explain, round(mtime, 6)) print("Krows/s:", round((self.nrows / 1000.) / mtime, 6)) def print_db_sizes(self, init, filled): array_size = (filled - init) / 1024. print("Array size (MB):", round(array_size, 3)) def open_db(self, remove=0): if remove and os.path.exists(self.filename): os.remove(self.filename) con = tables.open_file(self.filename, 'a') return con def create_db(self, verbose): self.con = self.open_db(remove=1) self.create_array() init_size = self.get_db_size() t1 = time() self.fill_array() array_size = self.get_db_size() self.print_mtime(t1, 'Insert time') self.print_db_sizes(init_size, array_size) self.close_db() def create_array(self): # The filters chosen filters = tables.Filters(complevel=self.docompress, complib=self.complib) atom = tables.Atom.from_kind(self.dtype) self.con.create_earray(self.con.root, 'earray', atom, (0,), filters=filters, expectedrows=self.nrows, chunkshape=(self.chunksize,)) def fill_array(self): "Fills the array" earray = self.con.root.earray j = 0 arr = self.get_array(0, self.step) for i in range(0, self.nrows, self.step): stop = (j + 1) * self.step if stop > self.nrows: stop = self.nrows ###arr = self.get_array(i, stop, dtype) earray.append(arr) j += 1 earray.flush() def get_array(self, start, stop): arr = numpy.arange(start, stop, dtype='float') if self.userandom: arr += numpy.random.normal(0, stop * self.scale, size=stop - start) arr = arr.astype(self.dtype) return arr def print_qtime(self, ltimes): ltimes = numpy.array(ltimes) print("Raw query times:\n", ltimes) print("Histogram times:\n", numpy.histogram(ltimes[1:])) ntimes = len(ltimes) qtime1 = ltimes[0] # First measured time if ntimes > 5: # Wait until the 5th iteration (in order to # ensure that the index is effectively cached) to take times qtime2 = sum(ltimes[5:]) / (ntimes - 5) else: qtime2 = ltimes[-1] # Last measured time print("1st query time:", round(qtime1, 3)) print("Mean (skipping the first 5 meas.):", round(qtime2, 3)) def query_db(self, niter, avoidfscache, verbose): self.con = self.open_db() earray = self.con.root.earray if avoidfscache: rseed = int(numpy.random.randint(self.nrows)) else: rseed = 19 numpy.random.seed(rseed) numpy.random.randint(self.nrows) ltimes = [] for i in range(niter): t1 = time() self.do_query(earray, numpy.random.randint(self.nrows)) ltimes.append(time() - t1) self.print_qtime(ltimes) self.close_db() def do_query(self, earray, idx): return earray[idx] def close_db(self): self.con.close() if __name__ == "__main__": import sys import getopt usage = """usage: %s [-v] [-m] [-c] [-q] [-x] [-z complevel] [-l complib] [-N niter] [-n nrows] [-d datadir] [-t] type [-s] chunksize -v verbose -m use random values to fill the array -q do a (random) lookup -x choose a different seed for random numbers (i.e. avoid FS cache) -c create the file -z compress with zlib (no compression by default) -l use complib for compression (zlib used by default) -N number of iterations for reading -n sets the number of rows in the array -d directory to save data (default: data.nobackup) -t select the type for array ('int' or 'float'. def 'float') -s select the chunksize for array \n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'vmcqxz:l:N:n:d:t:s:') except: sys.stderr.write(usage) sys.exit(0) # default options verbose = 0 userandom = 0 docreate = 0 optlevel = 0 docompress = 0 complib = "zlib" doquery = False avoidfscache = 0 krows = '1k' chunksize = '32k' niter = 50 datadir = "data.nobackup" dtype = "float" # Get the options for option in opts: if option[0] == '-v': verbose = 1 elif option[0] == '-m': userandom = 1 elif option[0] == '-c': docreate = 1 createindex = 1 elif option[0] == '-q': doquery = True elif option[0] == '-x': avoidfscache = 1 elif option[0] == '-z': docompress = int(option[1]) elif option[0] == '-l': complib = option[1] elif option[0] == '-N': niter = int(option[1]) elif option[0] == '-n': krows = option[1] elif option[0] == '-d': datadir = option[1] elif option[0] == '-t': if option[1] in ('int', 'float'): dtype = option[1] else: print("type should be either 'int' or 'float'") sys.exit(0) elif option[0] == '-s': chunksize = option[1] if not avoidfscache: # in order to always generate the same random sequence numpy.random.seed(20) if verbose: if userandom: print("using random values") db = DB(krows, dtype, chunksize, userandom, datadir, docompress, complib) if docreate: if verbose: print("writing %s rows" % krows) db.create_db(verbose) if doquery: print("Calling query_db() %s times" % niter) db.query_db(niter, avoidfscache, verbose) PyTables-v.3.1.1/bench/open_close-bench.py000066400000000000000000000162541231437614300203530ustar00rootroot00000000000000"""Testbed for open/close PyTables files. This uses the HotShot profiler. """ from __future__ import print_function import os import sys import getopt import pstats import cProfile as prof import time import subprocess # From Python 2.4 on import tables filename = None niter = 1 def show_stats(explain, tref): "Show the used memory" # Build the command to obtain memory info (only for Linux 2.6.x) cmd = "cat /proc/%s/status" % os.getpid() sout = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout for line in sout: if line.startswith("VmSize:"): vmsize = int(line.split()[1]) elif line.startswith("VmRSS:"): vmrss = int(line.split()[1]) elif line.startswith("VmData:"): vmdata = int(line.split()[1]) elif line.startswith("VmStk:"): vmstk = int(line.split()[1]) elif line.startswith("VmExe:"): vmexe = int(line.split()[1]) elif line.startswith("VmLib:"): vmlib = int(line.split()[1]) sout.close() print("WallClock time:", time.time() - tref) print("Memory usage: ******* %s *******" % explain) print("VmSize: %7s kB\tVmRSS: %7s kB" % (vmsize, vmrss)) print("VmData: %7s kB\tVmStk: %7s kB" % (vmdata, vmstk)) print("VmExe: %7s kB\tVmLib: %7s kB" % (vmexe, vmlib)) def check_open_close(): for i in range(niter): print( "------------------ open_close #%s -------------------------" % i) tref = time.time() fileh = tables.open_file(filename) fileh.close() show_stats("After closing file", tref) def check_only_open(): for i in range(niter): print("------------------ only_open #%s -------------------------" % i) tref = time.time() fileh = tables.open_file(filename) show_stats("Before closing file", tref) fileh.close() def check_full_browse(): for i in range(niter): print("------------------ full_browse #%s -----------------------" % i) tref = time.time() fileh = tables.open_file(filename) for node in fileh: pass fileh.close() show_stats("After full browse", tref) def check_partial_browse(): for i in range(niter): print("------------------ partial_browse #%s --------------------" % i) tref = time.time() fileh = tables.open_file(filename) for node in fileh.root.ngroup0.ngroup1: pass fileh.close() show_stats("After closing file", tref) def check_full_browse_attrs(): for i in range(niter): print("------------------ full_browse_attrs #%s -----------------" % i) tref = time.time() fileh = tables.open_file(filename) for node in fileh: # Access to an attribute klass = node._v_attrs.CLASS fileh.close() show_stats("After full browse", tref) def check_partial_browse_attrs(): for i in range(niter): print("------------------ partial_browse_attrs #%s --------------" % i) tref = time.time() fileh = tables.open_file(filename) for node in fileh.root.ngroup0.ngroup1: # Access to an attribute klass = node._v_attrs.CLASS fileh.close() show_stats("After closing file", tref) def check_open_group(): for i in range(niter): print("------------------ open_group #%s ------------------------" % i) tref = time.time() fileh = tables.open_file(filename) group = fileh.root.ngroup0.ngroup1 # Access to an attribute klass = group._v_attrs.CLASS fileh.close() show_stats("After closing file", tref) def check_open_leaf(): for i in range(niter): print("------------------ open_leaf #%s -----------------------" % i) tref = time.time() fileh = tables.open_file(filename) leaf = fileh.root.ngroup0.ngroup1.array9 # Access to an attribute klass = leaf._v_attrs.CLASS fileh.close() show_stats("After closing file", tref) if __name__ == '__main__': usage = """usage: %s [-v] [-p] [-n niter] [-O] [-o] [-B] [-b] [-g] [-l] [-A] [-a] [-E] [-S] datafile -v verbose (total dump of profiling) -p do profiling -n number of iterations for reading -O Check open_close -o Check only_open -B Check full browse -b Check partial browse -A Check full browse and reading one attr each node -a Check partial browse and reading one attr each node -g Check open nested group -l Check open nested leaf -E Check everything -S Check everything as subprocess \n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'vpn:OoBbAaglESs') except: sys.stderr.write(usage) sys.exit(0) progname = sys.argv[0] args = sys.argv[1:] # if we pass too much parameters, abort if len(pargs) != 1: sys.stderr.write(usage) sys.exit(0) # default options verbose = 0 silent = 0 # if silent, does not print the final statistics profile = 0 all_checks = 0 all_system_checks = 0 func = [] # Checking options options = ['-O', '-o', '-B', '-b', '-A', '-a', '-g', '-l'] # Dict to map options to checking functions option2func = { '-O': 'check_open_close', '-o': 'check_only_open', '-B': 'check_full_browse', '-b': 'check_partial_browse', '-A': 'check_full_browse_attrs', '-a': 'check_partial_browse_attrs', '-g': 'check_open_group', '-l': 'check_open_leaf', } # Get the options for option in opts: if option[0] == '-v': verbose = 1 elif option[0] == '-p': profile = 1 elif option[0] in option2func: func.append(option2func[option[0]]) elif option[0] == '-E': all_checks = 1 for opt in options: func.append(option2func[opt]) elif option[0] == '-S': all_system_checks = 1 elif option[0] == '-s': silent = 1 elif option[0] == '-n': niter = int(option[1]) filename = pargs[0] tref = time.time() if all_system_checks: args.remove('-S') # We don't want -S in the options list again for opt in options: opts = "%s \-s %s %s" % (progname, opt, " ".join(args)) # print "opts-->", opts os.system("python2.4 %s" % opts) else: if profile: for ifunc in func: prof.run(ifunc + '()', ifunc + '.prof') stats = pstats.Stats(ifunc + '.prof') stats.strip_dirs() stats.sort_stats('time', 'calls') if verbose: stats.print_stats() else: stats.print_stats(20) else: for ifunc in func: eval(ifunc + '()') if not silent: print("------------------ End of run -------------------------") show_stats("Final statistics (after closing everything)", tref) PyTables-v.3.1.1/bench/opteron-stress-test.txt000066400000000000000000000034771231437614300213260ustar00rootroot00000000000000Stress test on a 64 bits AMD Opteron platform ============================================= 2004-02-04. F. Alted Platform description: 4 processors AMD Opteron (64-bits) @ 1.6 GHz and 1 MB cache 8 GB RAM HD IBM DeskStar 120GXP 80 GB ATA/100 2 MB cache @ 7200 rpm SuSe Linux Enterprise Server (SLES) Linux kernel 2.4.21-178-smp ReiserFS filesystem Here's the command to do the stress test: time python /tmp/stress-test3.py -l zlib -c 6 -g400 -t 300 -i 20000 /tmp/test-big-zlib-6.h5 ls -lh /tmp/test-big-zlib-6.h5 The output: Compression level: 6 Compression library: zlib Rows written: 2400000000 Row size: 512 Time writing rows: 56173.557 s (real) 56154.84 s (cpu) 100% Write rows/sec: 42724 Write KB/s : 21362 Rows read: 2400000000 Row size: 512 Buf size: 39936 Time reading rows: 29339.936 s (real) 29087.88 s (cpu) 99% Read rows/sec: 81799 Read KB/s : 40899 real 1425m43.846s user 1308m34.340s sys 112m17.100s -rw-r--r-- 1 falted users 2.7G 2004-02-04 02:25 /tmp/test-big-zlib-6 .h5 The maximum amount of RAM taken by the test should be less than 300 MB (241 MB when the test was running for 5750 minutes, which is the last time I've check for it). Another test with the same machine: time python /tmp/stress-test3.py -l zlib -c 6 -g400 -t 300 -i 100000 /tmp/test-big-zlib-6-2.h5 ls -lh /tmp/test-big-zlib-6-2.h5 Compression level: 6 Compression library: zlib Rows written: 12000000000 Row size: 512 Time writing rows: 262930.901 s (real) 262619.72 s (cpu) 100% Write rows/sec: 45639 Write KB/s : 22819 Rows read: 12000000000 Row size: 512 Buf size: 49664 Time reading rows: 143171.761 s (real) 141560.42 s (cpu) 99% Read rows/sec: 83815 Read KB/s : 41907 real 6768m34.076s user 6183m38.690s sys 552m51.150s -rw-r--r-- 1 5350 users 11G 2004-02-09 00:57 /tmp/test-big-zlib-6 -2.h5 PyTables-v.3.1.1/bench/optimal-chunksize.py000066400000000000000000000073561231437614300206210ustar00rootroot00000000000000"""Small benchmark on the effect of chunksizes and compression on HDF5 files. Francesc Alted 2007-11-25 """ from __future__ import print_function import os import math import subprocess import tempfile from time import time import numpy import tables # Size of dataset # N, M = 512, 2**16 # 256 MB # N, M = 512, 2**18 # 1 GB # N, M = 512, 2**19 # 2 GB N, M = 2000, 1000000 # 15 GB # N, M = 4000, 1000000 # 30 GB datom = tables.Float64Atom() # elements are double precision def quantize(data, least_significant_digit): """Quantize data to improve compression. data is quantized using around(scale*data)/scale, where scale is 2**bits, and bits is determined from the least_significant_digit. For example, if least_significant_digit=1, bits will be 4. """ precision = 10. ** -least_significant_digit exp = math.log(precision, 10) if exp < 0: exp = int(math.floor(exp)) else: exp = int(math.ceil(exp)) bits = math.ceil(math.log(10. ** -exp, 2)) scale = 2. ** bits return numpy.around(scale * data) / scale def get_db_size(filename): sout = subprocess.Popen("ls -sh %s" % filename, shell=True, stdout=subprocess.PIPE).stdout line = [l for l in sout][0] return line.split()[0] def bench(chunkshape, filters): numpy.random.seed(1) # to have reproductible results filename = tempfile.mktemp(suffix='.h5') print("Doing test on the file system represented by:", filename) f = tables.open_file(filename, 'w') e = f.create_earray(f.root, 'earray', datom, shape=(0, M), filters = filters, chunkshape = chunkshape) # Fill the array t1 = time() for i in range(N): # e.append([numpy.random.rand(M)]) # use this for less compressibility e.append([quantize(numpy.random.rand(M), 6)]) # os.system("sync") print("Creation time:", round(time() - t1, 3), end=' ') filesize = get_db_size(filename) filesize_bytes = os.stat(filename)[6] print("\t\tFile size: %d -- (%s)" % (filesize_bytes, filesize)) # Read in sequential mode: e = f.root.earray t1 = time() # Flush everything to disk and flush caches #os.system("sync; echo 1 > /proc/sys/vm/drop_caches") for row in e: t = row print("Sequential read time:", round(time() - t1, 3), end=' ') # f.close() # return # Read in random mode: i_index = numpy.random.randint(0, N, 128) j_index = numpy.random.randint(0, M, 256) # Flush everything to disk and flush caches #os.system("sync; echo 1 > /proc/sys/vm/drop_caches") # Protection against too large chunksizes # 4 MB if 0 and filters.complevel and chunkshape[0] * chunkshape[1] * 8 > 2 ** 22: f.close() return t1 = time() for i in i_index: for j in j_index: t = e[i, j] print("\tRandom read time:", round(time() - t1, 3)) f.close() # Benchmark with different chunksizes and filters # for complevel in (0, 1, 3, 6, 9): for complib in (None, 'zlib', 'lzo', 'blosc'): # for complib in ('blosc',): if complib: filters = tables.Filters(complevel=5, complib=complib) else: filters = tables.Filters(complevel=0) print("8<--" * 20, "\nFilters:", filters, "\n" + "-" * 80) # for ecs in (11, 14, 17, 20, 21, 22): for ecs in range(10, 24): # for ecs in (19,): chunksize = 2 ** ecs chunk1 = 1 chunk2 = chunksize / datom.itemsize if chunk2 > M: chunk1 = chunk2 / M chunk2 = M chunkshape = (chunk1, chunk2) cs_str = str(chunksize / 1024) + " KB" print("***** Chunksize:", cs_str, "/ Chunkshape:", chunkshape, "*****") bench(chunkshape, filters) PyTables-v.3.1.1/bench/plot-bar.py000066400000000000000000000060261231437614300166640ustar00rootroot00000000000000#!/usr/bin/env python # a stacked bar plot with errorbars from __future__ import print_function from pylab import * checks = ['open_close', 'only_open', 'full_browse', 'partial_browse', 'full_browse_attrs', 'partial_browse_attrs', 'open_group', 'open_leaf', 'total'] width = 0.15 # the width of the bars: can also be len(x) sequence colors = ['r', 'm', 'g', 'y', 'b'] ind = arange(len(checks)) # the x locations for the groups def get_values(filename): values = [] f = open(filename) for line in f: if show_memory: if line.startswith('VmData:'): values.append(float(line.split()[1]) / 1024.) else: if line.startswith('WallClock time:'): values.append(float(line.split(':')[1])) f.close() return values def plot_bar(values, n): global ind if not gtotal: # Remove the grand totals values.pop() if n == 0: checks.pop() ind = arange(len(checks)) p = bar(ind + width * n, values, width, color=colors[n]) return p def show_plot(bars, filenames, tit): if show_memory: ylabel('Memory (MB)') else: ylabel('Time (s)') title(tit) n = len(filenames) xticks(ind + width * n / 2., checks, rotation=45, horizontalalignment='right', fontsize=8) if not gtotal: #loc = 'center right' loc = 'upper left' else: loc = 'center left' legends = [f[:f.index('_')] for f in filenames] legends = [l.replace('-', ' ') for l in legends] legend([p[0] for p in bars], legends, loc=loc) subplots_adjust(bottom=0.2, top=None, wspace=0.2, hspace=0.2) if outfile: savefig(outfile) else: show() if __name__ == '__main__': import sys import getopt usage = """usage: %s [-g] [-m] [-o file] [-t title] files -g grand total -m show memory instead of time -o filename for output (only .png and .jpg extensions supported) -t title of the plot \n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'gmo:t:') except: sys.stderr.write(usage) sys.exit(0) progname = sys.argv[0] args = sys.argv[1:] # if we pass too few parameters, abort if len(pargs) < 1: sys.stderr.write(usage) sys.exit(0) # default options tit = "Comparison of differents PyTables versions" gtotal = 0 show_memory = 0 outfile = None # Get the options for option in opts: if option[0] == '-g': gtotal = 1 elif option[0] == '-m': show_memory = 1 elif option[0] == '-o': outfile = option[1] elif option[0] == '-t': tit = option[1] filenames = pargs bars = [] n = 0 for filename in filenames: values = get_values(filename) print("Values-->", values) bars.append(plot_bar(values, n)) n += 1 show_plot(bars, filenames, tit) PyTables-v.3.1.1/bench/plot-comparison-lzo-zlib-ucl.gnuplot000066400000000000000000000017021231437614300236470ustar00rootroot00000000000000#set term post color set term post eps color set xlabel "Number of rows" set ylabel "Speed (Krow/s)" set linestyle 1 lw 7 set linestyle 2 lw 7 set linestyle 3 lw 7 set linestyle 4 lw 7 set logscale x # For small record size set output "read-small-lzo-zlib-ucl-comparison.eps" set tit "Selecting with small record size (16 bytes)" pl [1000:] [0:1000] "small-nc.out" u ($1):($10) t "No compression" w linesp ls 1, \ "small-zlib.out" u ($1):($10) t "ZLIB" w linesp ls 2, \ "small-lzo.out" u ($1):($10) t "LZO" w linesp ls 3, \ "small-ucl.out" u ($1):($10) t "UCL" w linesp ls 4 # For small record size set output "write-small-lzo-zlib-ucl-comparison.eps" set tit "Writing with small record size (16 bytes)" pl [1000:] [0:500] "small-nc.out" u ($1):($5) tit "No compression" w linesp ls 1, \ "small-zlib.out" u ($1):($5) tit "ZLIB" w linesp ls 2, \ "small-lzo.out" u ($1):($5) tit "LZO" w linesp ls 3, \ "small-ucl.out" u ($1):($5) tit "UCL" w linesp ls 4 PyTables-v.3.1.1/bench/plot-comparison-psyco-lzo.gnuplot000066400000000000000000000021661231437614300232700ustar00rootroot00000000000000#set term post color set term post eps color set xlabel "Number of rows" set ylabel "Speed (Krow/s)" set linestyle 1 lw 7 set linestyle 2 lw 7 set linestyle 3 lw 7 set linestyle 4 lw 7 # For small record size set output "read-small-psyco-lzo-comparison.eps" set tit "Selecting with small record size (16 bytes)" set logscale x pl [1000:] [0:1200] "small-psyco-lzo.out" u ($1):($10) t "Psyco & compression (LZO)" w linesp ls 2, \ "small-psyco-nc.out" u ($1):($10) tit "Psyco & no compresion" w linesp ls 3, \ "small-lzo.out" u ($1):($10) t "No Psyco & compression (LZO)" w linesp ls 1, \ "small-nc.out" u ($1):($10) tit "No Psyco & no compression" w linesp ls 4 # For small record size set output "write-small-psyco-lzo-comparison.eps" set tit "Writing with small record size (16 bytes)" set logscale x pl [1000:] [0:1000] "small-psyco-lzo.out" u ($1):($5) t "Psyco & compression (LZO)" w linesp ls 2, \ "small-psyco-nc.out" u ($1):($5) tit "Psyco & no compresion" w linesp ls 3, \ "small-lzo.out" u ($1):($5) t "No Psyco & compression (LZO)" w linesp ls 1, \ "small-nc.out" u ($1):($5) tit "No Psyco & no compression" w linesp ls 4 PyTables-v.3.1.1/bench/poly.py000066400000000000000000000147231231437614300161320ustar00rootroot00000000000000####################################################################### # This script compares the speed of the computation of a polynomial # for different (numpy.memmap and tables.Expr) out-of-memory paradigms. # # Author: Francesc Alted # Date: 2010-02-24 ####################################################################### from __future__ import print_function import os from time import time import numpy as np import tables as tb import numexpr as ne expr = ".25*x**3 + .75*x**2 - 1.5*x - 2" # the polynomial to compute N = 10 * 1000 * 1000 # the number of points to compute expression (80 MB) step = 100 * 1000 # perform calculation in slices of `step` elements dtype = np.dtype('f8') # the datatype #CHUNKSHAPE = (2**17,) CHUNKSHAPE = None # Global variable for the x values for pure numpy & numexpr x = None # *** The next variables do not need to be changed *** # Filenames for numpy.memmap fprefix = "numpy.memmap" # the I/O file prefix mpfnames = [fprefix + "-x.bin", fprefix + "-r.bin"] # Filename for tables.Expr h5fname = "tablesExpr.h5" # the I/O file MB = 1024 * 1024. # a MegaByte def print_filesize(filename, clib=None, clevel=0): """Print some statistics about file sizes.""" # os.system("sync") # make sure that all data has been flushed to disk if isinstance(filename, list): filesize_bytes = 0 for fname in filename: filesize_bytes += os.stat(fname)[6] else: filesize_bytes = os.stat(filename)[6] filesize_MB = round(filesize_bytes / MB, 1) print("\t\tTotal file sizes: %d -- (%s MB)" % ( filesize_bytes, filesize_MB), end=' ') if clevel > 0: print("(using %s lvl%s)" % (clib, clevel)) else: print() def populate_x_numpy(): """Populate the values in x axis for numpy.""" global x # Populate x in range [-1, 1] x = np.linspace(-1, 1, N) def populate_x_memmap(): """Populate the values in x axis for numpy.memmap.""" # Create container for input x = np.memmap(mpfnames[0], dtype=dtype, mode="w+", shape=(N,)) # Populate x in range [-1, 1] for i in range(0, N, step): chunk = np.linspace((2 * i - N) / float(N), (2 * (i + step) - N) / float(N), step) x[i:i + step] = chunk del x # close x memmap def populate_x_tables(clib, clevel): """Populate the values in x axis for pytables.""" f = tb.open_file(h5fname, "w") # Create container for input atom = tb.Atom.from_dtype(dtype) filters = tb.Filters(complib=clib, complevel=clevel) x = f.create_carray(f.root, "x", atom=atom, shape=(N,), filters=filters, chunkshape=CHUNKSHAPE, ) # Populate x in range [-1, 1] for i in range(0, N, step): chunk = np.linspace((2 * i - N) / float(N), (2 * (i + step) - N) / float(N), step) x[i:i + step] = chunk f.close() def compute_numpy(): """Compute the polynomial with pure numpy.""" y = eval(expr) def compute_numexpr(): """Compute the polynomial with pure numexpr.""" y = ne.evaluate(expr) def compute_memmap(): """Compute the polynomial with numpy.memmap.""" # Reopen inputs in read-only mode x = np.memmap(mpfnames[0], dtype=dtype, mode='r', shape=(N,)) # Create the array output r = np.memmap(mpfnames[1], dtype=dtype, mode="w+", shape=(N,)) # Do the computation by chunks and store in output r[:] = eval(expr) # where is stored the result? # r = eval(expr) # result is stored in-memory del x, r # close x and r memmap arrays print_filesize(mpfnames) def compute_tables(clib, clevel): """Compute the polynomial with tables.Expr.""" f = tb.open_file(h5fname, "a") x = f.root.x # get the x input # Create container for output atom = tb.Atom.from_dtype(dtype) filters = tb.Filters(complib=clib, complevel=clevel) r = f.create_carray(f.root, "r", atom=atom, shape=(N,), filters=filters, chunkshape=CHUNKSHAPE, ) # Do the actual computation and store in output ex = tb.Expr(expr) # parse the expression ex.set_output(r) # where is stored the result? # when commented out, the result goes in-memory ex.eval() # evaluate! f.close() print_filesize(h5fname, clib, clevel) if __name__ == '__main__': tb.print_versions() print("Total size for datasets:", round(2 * N * dtype.itemsize / MB, 1), "MB") # Get the compression libraries supported # supported_clibs = [clib for clib in ("zlib", "lzo", "bzip2", "blosc") # supported_clibs = [clib for clib in ("zlib", "lzo", "blosc") supported_clibs = [clib for clib in ("blosc",) if tb.which_lib_version(clib)] # Initialization code # for what in ["numpy", "numpy.memmap", "numexpr"]: for what in ["numpy", "numexpr"]: # break print("Populating x using %s with %d points..." % (what, N)) t0 = time() if what == "numpy": populate_x_numpy() compute = compute_numpy elif what == "numexpr": populate_x_numpy() compute = compute_numexpr elif what == "numpy.memmap": populate_x_memmap() compute = compute_memmap print("*** Time elapsed populating:", round(time() - t0, 3)) print("Computing: '%s' using %s" % (expr, what)) t0 = time() compute() print("**************** Time elapsed computing:", round(time() - t0, 3)) for what in ["tables.Expr"]: t0 = time() first = True # Sentinel for clib in supported_clibs: # for clevel in (0, 1, 3, 6, 9): for clevel in range(10): # for clevel in (1,): if not first and clevel == 0: continue print("Populating x using %s with %d points..." % (what, N)) populate_x_tables(clib, clevel) print("*** Time elapsed populating:", round(time() - t0, 3)) print("Computing: '%s' using %s" % (expr, what)) t0 = time() compute_tables(clib, clevel) print("**************** Time elapsed computing:", round(time() - t0, 3)) first = False PyTables-v.3.1.1/bench/postgres-search-bench.py000066400000000000000000000147561231437614300213430ustar00rootroot00000000000000from __future__ import print_function from time import time import numpy import random DSN = "dbname=test port = 5435" # in order to always generate the same random sequence random.seed(19) def flatten(l): """Flattens list of tuples l.""" return [x[0] for x in l] def fill_arrays(start, stop): col_i = numpy.arange(start, stop, type=numpy.Int32) if userandom: col_j = numpy.random.uniform(0, nrows, size=[stop - start]) else: col_j = numpy.array(col_i, type=numpy.Float64) return col_i, col_j # Generator for ensure pytables benchmark compatibility def int_generator(nrows): step = 1000 * 100 j = 0 for i in range(nrows): if i >= step * j: stop = (j + 1) * step if stop > nrows: # Seems unnecessary stop = nrows col_i, col_j = fill_arrays(i, stop) j += 1 k = 0 yield (col_i[k], col_j[k]) k += 1 def int_generator_slow(nrows): for i in range(nrows): if userandom: yield (i, float(random.randint(0, nrows))) else: yield (i, float(i)) class Stream32(object): "Object simulating a file for reading" def __init__(self): self.n = None self.read_it = self.read_iter() # No va! Hi ha que convertir a un de normal! def readline(self, n=None): for tup in int_generator(nrows): sout = "%s\t%s\n" % tup if n is not None and len(sout) > n: for i in range(0, len(sout), n): yield sout[i:i + n] else: yield sout def read_iter(self): sout = "" n = self.n for tup in int_generator(nrows): sout += "%s\t%s\n" % tup if n is not None and len(sout) > n: for i in range(n, len(sout), n): rout = sout[:n] sout = sout[n:] yield rout yield sout def read(self, n=None): self.n = n try: str = next(self.read_it) except StopIteration: str = "" return str def open_db(filename, remove=0): if not filename: con = sqlite.connect(DSN) else: con = sqlite.connect(filename) cur = con.cursor() return con, cur def create_db(filename, nrows): con, cur = open_db(filename, remove=1) try: cur.execute("create table ints(i integer, j double precision)") except: con.rollback() cur.execute("DROP TABLE ints") cur.execute("create table ints(i integer, j double precision)") con.commit() con.set_isolation_level(2) t1 = time() st = Stream32() cur.copy_from(st, "ints") # In case of postgres, the speeds of generator and loop are similar #cur.executemany("insert into ints values (%s,%s)", int_generator(nrows)) # for i in xrange(nrows): # cur.execute("insert into ints values (%s,%s)", (i, float(i))) con.commit() ctime = time() - t1 if verbose: print("insert time:", round(ctime, 5)) print("Krows/s:", round((nrows / 1000.) / ctime, 5)) close_db(con, cur) def index_db(filename): con, cur = open_db(filename) t1 = time() cur.execute("create index ij on ints(j)") con.commit() itime = time() - t1 if verbose: print("index time:", round(itime, 5)) print("Krows/s:", round(nrows / itime, 5)) # Close the DB close_db(con, cur) def query_db(filename, rng): con, cur = open_db(filename) t1 = time() ntimes = 10 for i in range(ntimes): # between clause does not seem to take advantage of indexes # cur.execute("select j from ints where j between %s and %s" % \ cur.execute("select i from ints where j >= %s and j <= %s" % # cur.execute("select i from ints where i >= %s and i <= # %s" % (rng[0] + i, rng[1] + i)) results = cur.fetchall() con.commit() qtime = (time() - t1) / ntimes if verbose: print("query time:", round(qtime, 5)) print("Mrows/s:", round((nrows / 1000.) / qtime, 5)) results = sorted(flatten(results)) print(results) close_db(con, cur) def close_db(con, cur): cur.close() con.close() if __name__ == "__main__": import sys import getopt try: import psyco psyco_imported = 1 except: psyco_imported = 0 usage = """usage: %s [-v] [-p] [-m] [-i] [-q] [-c] [-R range] [-n nrows] file -v verbose -p use "psyco" if available -m use random values to fill the table -q do query -c create the database -i index the table -2 use sqlite2 (default is use sqlite3) -R select a range in a field in the form "start,stop" (def "0,10") -n sets the number of rows (in krows) in each table \n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'vpmiqc2R:n:') except: sys.stderr.write(usage) sys.exit(0) # default options verbose = 0 usepsyco = 0 userandom = 0 docreate = 0 createindex = 0 doquery = 0 sqlite_version = "3" rng = [0, 10] nrows = 1 # Get the options for option in opts: if option[0] == '-v': verbose = 1 elif option[0] == '-p': usepsyco = 1 elif option[0] == '-m': userandom = 1 elif option[0] == '-i': createindex = 1 elif option[0] == '-q': doquery = 1 elif option[0] == '-c': docreate = 1 elif option[0] == "-2": sqlite_version = "2" elif option[0] == '-R': rng = [int(i) for i in option[1].split(",")] elif option[0] == '-n': nrows = int(option[1]) # Catch the hdf5 file passed as the last argument filename = pargs[0] # if sqlite_version == "2": # import sqlite # else: # from pysqlite2 import dbapi2 as sqlite import psycopg2 as sqlite if verbose: # print "pysqlite version:", sqlite.version if userandom: print("using random values") if docreate: if verbose: print("writing %s krows" % nrows) if psyco_imported and usepsyco: psyco.bind(create_db) nrows *= 1000 create_db(filename, nrows) if createindex: index_db(filename) if doquery: query_db(filename, rng) PyTables-v.3.1.1/bench/postgres_backend.py000066400000000000000000000121471231437614300204620ustar00rootroot00000000000000from __future__ import print_function import subprocess # Needs Python 2.4 from indexed_search import DB import psycopg2 as db2 CLUSTER_NAME = "base" DATA_DIR = "/scratch2/postgres/data/%s" % CLUSTER_NAME #DATA_DIR = "/var/lib/pgsql/data/%s" % CLUSTER_NAME DSN = "dbname=%s port=%s" CREATE_DB = "createdb %s" DROP_DB = "dropdb %s" TABLE_NAME = "intsfloats" PORT = 5432 class StreamChar(object): "Object simulating a file for reading" def __init__(self, db): self.db = db self.nrows = db.nrows self.step = db.step self.read_it = self.read_iter() def values_generator(self): j = 0 for i in range(self.nrows): if i >= j * self.step: stop = (j + 1) * self.step if stop > self.nrows: stop = self.nrows arr_i4, arr_f8 = self.db.fill_arrays(i, stop) j += 1 k = 0 yield (arr_i4[k], arr_i4[k], arr_f8[k], arr_f8[k]) k += 1 def read_iter(self): sout = "" n = self.nbytes for tup in self.values_generator(): sout += "%s\t%s\t%s\t%s\n" % tup if n is not None and len(sout) > n: for i in range(n, len(sout), n): rout = sout[:n] sout = sout[n:] yield rout yield sout def read(self, n=None): self.nbytes = n try: str = next(self.read_it) except StopIteration: str = "" return str # required by postgres2 driver, but not used def readline(self): pass class Postgres_DB(DB): def __init__(self, nrows, rng, userandom): DB.__init__(self, nrows, rng, userandom) self.port = PORT def flatten(self, l): """Flattens list of tuples l.""" return [x[0] for x in l] # return map(lambda x: x[col], l) # Overloads the method in DB class def get_db_size(self): sout = subprocess.Popen("sudo du -s %s" % DATA_DIR, shell=True, stdout=subprocess.PIPE).stdout line = [l for l in sout][0] return int(line.split()[0]) def open_db(self, remove=0): if remove: sout = subprocess.Popen(DROP_DB % self.filename, shell=True, stdout=subprocess.PIPE).stdout for line in sout: print(line) sout = subprocess.Popen(CREATE_DB % self.filename, shell=True, stdout=subprocess.PIPE).stdout for line in sout: print(line) print("Processing database:", self.filename) con = db2.connect(DSN % (self.filename, self.port)) self.cur = con.cursor() return con def create_table(self, con): self.cur.execute("""create table %s( col1 integer, col2 integer, col3 double precision, col4 double precision)""" % TABLE_NAME) con.commit() def fill_table(self, con): st = StreamChar(self) self.cur.copy_from(st, TABLE_NAME) con.commit() def index_col(self, con, colname, optlevel, idxtype, verbose): self.cur.execute("create index %s on %s(%s)" % (colname + '_idx', TABLE_NAME, colname)) con.commit() def do_query_simple(self, con, column, base): self.cur.execute( "select sum(%s) from %s where %s >= %s and %s <= %s" % (column, TABLE_NAME, column, base + self.rng[0], column, base + self.rng[1])) # "select * from %s where %s >= %s and %s <= %s" % \ # (TABLE_NAME, # column, base+self.rng[0], # column, base+self.rng[1])) #results = self.flatten(self.cur.fetchall()) results = self.cur.fetchall() return results def do_query(self, con, column, base, *unused): d = (self.rng[1] - self.rng[0]) / 2. inf1 = int(self.rng[0] + base) sup1 = int(self.rng[0] + d + base) inf2 = self.rng[0] + base * 2 sup2 = self.rng[0] + d + base * 2 # print "lims-->", inf1, inf2, sup1, sup2 condition = "((%s>=%s) and (%s<%s)) or ((col2>%s) and (col2<%s))" #condition = "((col3>=%s) and (col3<%s)) or ((col1>%s) and (col1<%s))" condition += " and ((col1+3.1*col2+col3*col4) > 3)" #condition += " and (sqrt(col1^2+col2^2+col3^2+col4^2) > .1)" condition = condition % (column, inf2, column, sup2, inf1, sup1) # print "condition-->", condition self.cur.execute( # "select sum(%s) from %s where %s" % "select %s from %s where %s" % (column, TABLE_NAME, condition)) #results = self.flatten(self.cur.fetchall()) results = self.cur.fetchall() #results = self.cur.fetchall() # print "results-->", results # return results return len(results) def close_db(self, con): self.cur.close() con.close() PyTables-v.3.1.1/bench/pytables-search-bench.py000066400000000000000000000146061231437614300213120ustar00rootroot00000000000000from __future__ import print_function import os from time import time import random import numpy as np import tables # in order to always generate the same random sequence random.seed(19) np.random.seed((19, 20)) def open_db(filename, remove=0): if remove and os.path.exists(filename): os.remove(filename) con = tables.open_file(filename, 'a') return con def create_db(filename, nrows): class Record(tables.IsDescription): col1 = tables.Int32Col() col2 = tables.Int32Col() col3 = tables.Float64Col() col4 = tables.Float64Col() con = open_db(filename, remove=1) table = con.create_table(con.root, 'table', Record, filters=filters, expectedrows=nrows) table.indexFilters = filters step = 1000 * 100 scale = 0.1 t1 = time() j = 0 for i in range(0, nrows, step): stop = (j + 1) * step if stop > nrows: stop = nrows arr_f8 = np.arange(i, stop, type=np.Float64) arr_i4 = np.arange(i, stop, type=np.Int32) if userandom: arr_f8 += np.random.normal(0, stop * scale, shape=[stop - i]) arr_i4 = np.array(arr_f8, type=np.Int32) recarr = np.rec.fromarrays([arr_i4, arr_i4, arr_f8, arr_f8]) table.append(recarr) j += 1 table.flush() ctime = time() - t1 if verbose: print("insert time:", round(ctime, 5)) print("Krows/s:", round((nrows / 1000.) / ctime, 5)) index_db(table) close_db(con) def index_db(table): t1 = time() table.cols.col2.create_index() itime = time() - t1 if verbose: print("index time (int):", round(itime, 5)) print("Krows/s:", round((nrows / 1000.) / itime, 5)) t1 = time() table.cols.col4.create_index() itime = time() - t1 if verbose: print("index time (float):", round(itime, 5)) print("Krows/s:", round((nrows / 1000.) / itime, 5)) def query_db(filename, rng): con = open_db(filename) table = con.root.table # Query for integer columns # Query for non-indexed column if not doqueryidx: t1 = time() ntimes = 10 for i in range(ntimes): results = [ r['col1'] for r in table.where( rng[0] + i <= table.cols.col1 <= rng[1] + i) ] qtime = (time() - t1) / ntimes if verbose: print("query time (int, not indexed):", round(qtime, 5)) print("Mrows/s:", round((nrows / 1000.) / qtime, 5)) print(results) # Query for indexed column t1 = time() ntimes = 10 for i in range(ntimes): results = [ r['col1'] for r in table.where( rng[0] + i <= table.cols.col2 <= rng[1] + i) ] qtime = (time() - t1) / ntimes if verbose: print("query time (int, indexed):", round(qtime, 5)) print("Mrows/s:", round((nrows / 1000.) / qtime, 5)) print(results) # Query for floating columns # Query for non-indexed column if not doqueryidx: t1 = time() ntimes = 10 for i in range(ntimes): results = [ r['col3'] for r in table.where( rng[0] + i <= table.cols.col3 <= rng[1] + i) ] qtime = (time() - t1) / ntimes if verbose: print("query time (float, not indexed):", round(qtime, 5)) print("Mrows/s:", round((nrows / 1000.) / qtime, 5)) print(results) # Query for indexed column t1 = time() ntimes = 10 for i in range(ntimes): results = [r['col3'] for r in table.where(rng[0] + i <= table.cols.col4 <= rng[1] + i)] qtime = (time() - t1) / ntimes if verbose: print("query time (float, indexed):", round(qtime, 5)) print("Mrows/s:", round((nrows / 1000.) / qtime, 5)) print(results) close_db(con) def close_db(con): con.close() if __name__ == "__main__": import sys import getopt try: import psyco psyco_imported = 1 except: psyco_imported = 0 usage = """usage: %s [-v] [-p] [-m] [-c] [-q] [-i] [-z complevel] [-l complib] [-R range] [-n nrows] file -v verbose -p use "psyco" if available -m use random values to fill the table -q do a query (both indexed and non-indexed version) -i do a query (exclude non-indexed version) -c create the database -z compress with zlib (no compression by default) -l use complib for compression (zlib used by default) -R select a range in a field in the form "start,stop" (def "0,10") -n sets the number of rows (in krows) in each table \n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'vpmcqiz:l:R:n:') except: sys.stderr.write(usage) sys.exit(0) # default options verbose = 0 usepsyco = 0 userandom = 0 docreate = 0 docompress = 0 complib = "zlib" doquery = 0 doqueryidx = 0 rng = [0, 10] nrows = 1 # Get the options for option in opts: if option[0] == '-v': verbose = 1 elif option[0] == '-p': usepsyco = 1 elif option[0] == '-m': userandom = 1 elif option[0] == '-c': docreate = 1 createindex = 1 elif option[0] == '-q': doquery = 1 elif option[0] == '-i': doqueryidx = 1 elif option[0] == '-z': docompress = int(option[1]) elif option[0] == '-l': complib = option[1] elif option[0] == '-R': rng = [int(i) for i in option[1].split(",")] elif option[0] == '-n': nrows = int(option[1]) # Catch the hdf5 file passed as the last argument filename = pargs[0] # The filters chosen filters = tables.Filters(complevel=docompress, complib=complib) if verbose: print("pytables version:", tables.__version__) if userandom: print("using random values") if doqueryidx: print("doing indexed queries only") if docreate: if verbose: print("writing %s krows" % nrows) if psyco_imported and usepsyco: psyco.bind(create_db) nrows *= 1000 create_db(filename, nrows) if doquery: query_db(filename, rng) PyTables-v.3.1.1/bench/pytables_backend.py000066400000000000000000000171231231437614300204360ustar00rootroot00000000000000from __future__ import print_function import os import tables from indexed_search import DB class PyTables_DB(DB): def __init__(self, nrows, rng, userandom, datadir, docompress=0, complib='zlib', kind="medium", optlevel=6): DB.__init__(self, nrows, rng, userandom) self.tprof = [] # Specific part for pytables self.docompress = docompress self.complib = complib # Complete the filename self.filename = "pro-" + self.filename self.filename += '-' + 'O%s' % optlevel self.filename += '-' + kind if docompress: self.filename += '-' + complib + str(docompress) self.filename = datadir + '/' + self.filename + '.h5' # The chosen filters self.filters = tables.Filters(complevel=self.docompress, complib=self.complib, shuffle=1) print("Processing database:", self.filename) def open_db(self, remove=0): if remove and os.path.exists(self.filename): os.remove(self.filename) con = tables.open_file(self.filename, 'a') return con def close_db(self, con): # Remove first the table_cache attribute if it exists if hasattr(self, "table_cache"): del self.table_cache con.close() def create_table(self, con): class Record(tables.IsDescription): col1 = tables.Int32Col() col2 = tables.Int32Col() col3 = tables.Float64Col() col4 = tables.Float64Col() con.create_table(con.root, 'table', Record, filters=self.filters, expectedrows=self.nrows) def fill_table(self, con): "Fills the table" table = con.root.table j = 0 for i in range(0, self.nrows, self.step): stop = (j + 1) * self.step if stop > self.nrows: stop = self.nrows arr_i4, arr_f8 = self.fill_arrays(i, stop) # recarr = records.fromarrays([arr_i4, arr_i4, arr_f8, arr_f8]) # table.append(recarr) table.append([arr_i4, arr_i4, arr_f8, arr_f8]) j += 1 table.flush() def index_col(self, con, column, kind, optlevel, verbose): col = getattr(con.root.table.cols, column) col.create_index(kind=kind, optlevel=optlevel, filters=self.filters, tmp_dir="/scratch2/faltet", _verbose=verbose, _blocksizes=None) # _blocksizes=(2**27, 2**22, 2**15, 2**7)) # _blocksizes=(2**27, 2**22, 2**14, 2**6)) # _blocksizes=(2**27, 2**20, 2**13, 2**5), # _testmode=True) def do_query(self, con, column, base, inkernel): if True: if not hasattr(self, "table_cache"): self.table_cache = table = con.root.table self.colobj = getattr(table.cols, column) #self.colobj = getattr(table.cols, 'col1') self.condvars = {"col": self.colobj, "col1": table.cols.col1, "col2": table.cols.col2, "col3": table.cols.col3, "col4": table.cols.col4, } table = self.table_cache colobj = self.colobj else: table = con.root.table colobj = getattr(table.cols, column) self.condvars = {"col": colobj, "col1": table.cols.col1, "col2": table.cols.col2, "col3": table.cols.col3, "col4": table.cols.col4, } self.condvars['inf'] = self.rng[0] + base self.condvars['sup'] = self.rng[1] + base # For queries that can use two indexes instead of just one d = (self.rng[1] - self.rng[0]) / 2. inf1 = int(self.rng[0] + base) sup1 = int(self.rng[0] + d + base) inf2 = self.rng[0] + base * 2 sup2 = self.rng[0] + d + base * 2 self.condvars['inf1'] = inf1 self.condvars['sup1'] = sup1 self.condvars['inf2'] = inf2 self.condvars['sup2'] = sup2 #condition = "(inf == col2)" #condition = "(inf==col2) & (col4==sup)" #condition = "(inf==col2) | (col4==sup)" #condition = "(inf==col2) | (col2==sup)" #condition = "(inf==col2) & (col3==sup)" #condition = "((inf==col2) & (sup==col4)) & (col3==sup)" #condition = "((inf==col1) & (sup==col4)) & (col3==sup)" #condition = "(inf<=col1) & (col3", inf1, inf2, sup1, sup2 condition = "((inf2<=col) & (col", c['inf'], c['sup'], c['inf2'], c['sup2'] ncoords = 0 if colobj.is_indexed: results = [r[column] for r in table.where(condition, self.condvars)] # coords = table.get_where_list(condition, self.condvars) # results = table.read_coordinates(coords, field=column) # results = table.read_where(condition, self.condvars, field=column) elif inkernel: print("Performing in-kernel query") results = [r[column] for r in table.where(condition, self.condvars)] #coords = [r.nrow for r in table.where(condition, self.condvars)] #results = table.read_coordinates(coords) # for r in table.where(condition, self.condvars): # var = r[column] # ncoords += 1 else: # coords = [r.nrow for r in table # if (self.rng[0]+base <= r[column] <= self.rng[1]+base)] # results = table.read_coordinates(coords) print("Performing regular query") results = [ r[column] for r in table if (( (inf2 <= r['col4']) and (r['col4'] < sup2)) or ((inf1 < r['col2']) and (r['col2'] < sup1)) and ((r['col1'] + 3.1 * r['col2'] + r['col3'] * r['col4']) > 3) )] ncoords = len(results) # return coords # print "results-->", results # return results return ncoords #self.tprof.append( self.colobj.index.tprof ) # return ncoords, self.tprof PyTables-v.3.1.1/bench/recarray2-test.py000066400000000000000000000060671231437614300200200ustar00rootroot00000000000000from __future__ import print_function import os import sys import time import numpy as np import chararray import recarray import recarray2 # This is my modified version usage = """usage: %s recordlength Set recordlength to 1000 at least to obtain decent figures! """ % sys.argv[0] try: reclen = int(sys.argv[1]) except: print(usage) sys.exit() delta = 0.000001 # Creation of recarrays objects for test x1 = np.array(np.arange(reclen)) x2 = chararray.array(None, itemsize=7, shape=reclen) x3 = np.array(np.arange(reclen, reclen * 3, 2), np.Float64) r1 = recarray.fromarrays([x1, x2, x3], names='a,b,c') r2 = recarray2.fromarrays([x1, x2, x3], names='a,b,c') print("recarray shape in test ==>", r2.shape) print("Assignment in recarray original") print("-------------------------------") t1 = time.clock() for row in range(reclen): #r1.field("b")[row] = "changed" r1.field("c")[row] = float(row ** 2) t2 = time.clock() origtime = round(t2 - t1, 3) print("Assign time:", origtime, " Rows/s:", int(reclen / (origtime + delta))) # print "Field b on row 2 after re-assign:", r1.field("c")[2] print() print("Assignment in recarray modified") print("-------------------------------") t1 = time.clock() for row in range(reclen): rec = r2._row(row) # select the row to be changed # rec.b = "changed" # change the "b" field rec.c = float(row ** 2) # Change the "c" field t2 = time.clock() ttime = round(t2 - t1, 3) print("Assign time:", ttime, " Rows/s:", int(reclen / (ttime + delta)), end=' ') print(" Speed-up:", round(origtime / ttime, 3)) # print "Field b on row 2 after re-assign:", r2.field("c")[2] print() print("Selection in recarray original") print("------------------------------") t1 = time.clock() for row in range(reclen): rec = r1[row] if rec.field("a") < 3: print("This record pass the cut ==>", rec.field("c"), "(row", row, ")") t2 = time.clock() origtime = round(t2 - t1, 3) print("Select time:", origtime, " Rows/s:", int(reclen / (origtime + delta))) print() print("Selection in recarray modified") print("------------------------------") t1 = time.clock() for row in range(reclen): rec = r2._row(row) if rec.a < 3: print("This record pass the cut ==>", rec.c, "(row", row, ")") t2 = time.clock() ttime = round(t2 - t1, 3) print("Select time:", ttime, " Rows/s:", int(reclen / (ttime + delta)), end=' ') print(" Speed-up:", round(origtime / ttime, 3)) print() print("Printing in recarray original") print("------------------------------") f = open("test.out", "w") t1 = time.clock() f.write(str(r1)) t2 = time.clock() origtime = round(t2 - t1, 3) f.close() os.unlink("test.out") print("Print time:", origtime, " Rows/s:", int(reclen / (origtime + delta))) print() print("Printing in recarray modified") print("------------------------------") f = open("test2.out", "w") t1 = time.clock() f.write(str(r2)) t2 = time.clock() ttime = round(t2 - t1, 3) f.close() os.unlink("test2.out") print("Print time:", ttime, " Rows/s:", int(reclen / (ttime + delta)), end=' ') print(" Speed-up:", round(origtime / ttime, 3)) print() PyTables-v.3.1.1/bench/search-bench-plot.py000066400000000000000000000110571231437614300204420ustar00rootroot00000000000000from __future__ import print_function import tables from pylab import * def get_values(filename, complib=''): f = tables.open_file(filename) nrows = f.root.small.create_best.cols.nrows[:] corrected_sizes = nrows / 10. ** 6 if mb_units: corrected_sizes = 16 * nrows / 10. ** 6 if insert: values = corrected_sizes / f.root.small.create_best.cols.tfill[:] if table_size: values = f.root.small.create_best.cols.fsize[:] / nrows if query: values = corrected_sizes / \ f.root.small.search_best.inkernel.int.cols.time1[:] if query_cache: values = corrected_sizes / \ f.root.small.search_best.inkernel.int.cols.time2[:] f.close() return nrows, values def show_plot(plots, yaxis, legends, gtitle): xlabel('Number of rows') ylabel(yaxis) xlim(10 ** 3, 10 ** 8) title(gtitle) grid(True) # legends = [f[f.find('-'):f.index('.out')] for f in filenames] # legends = [l.replace('-', ' ') for l in legends] if table_size: legend([p[0] for p in plots], legends, loc="upper right") else: legend([p[0] for p in plots], legends, loc="upper left") #subplots_adjust(bottom=0.2, top=None, wspace=0.2, hspace=0.2) if outfile: savefig(outfile) else: show() if __name__ == '__main__': import sys import getopt usage = """usage: %s [-o file] [-t title] [--insert] [--table-size] [--query] [--query-cache] [--MB-units] files -o filename for output (only .png and .jpg extensions supported) -t title of the plot --insert -- Insert time for table --table-size -- Size of table --query -- Time for querying the integer column --query-cache -- Time for querying the integer (cached) --MB-units -- Express speed in MB/s instead of MRows/s \n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'o:t:', ['insert', 'table-size', 'query', 'query-cache', 'MB-units', ]) except: sys.stderr.write(usage) sys.exit(0) progname = sys.argv[0] args = sys.argv[1:] # if we pass too few parameters, abort if len(pargs) < 1: sys.stderr.write(usage) sys.exit(0) # default options outfile = None insert = 0 table_size = 0 query = 0 query_cache = 0 mb_units = 0 yaxis = "No axis name" tit = None gtitle = "Please set a title!" # Get the options for option in opts: if option[0] == '-o': outfile = option[1] elif option[0] == '-t': tit = option[1] elif option[0] == '--insert': insert = 1 yaxis = "MRows/s" gtitle = "Writing with small (16 bytes) record size" elif option[0] == '--table-size': table_size = 1 yaxis = "Bytes/row" gtitle = ("Disk space taken by a record (original record size: " "16 bytes)") elif option[0] == '--query': query = 1 yaxis = "MRows/s" gtitle = ("Selecting with small (16 bytes) record size (file not " "in cache)") elif option[0] == '--query-cache': query_cache = 1 yaxis = "MRows/s" gtitle = ("Selecting with small (16 bytes) record size (file in " "cache)") elif option[0] == '--MB-units': mb_units = 1 filenames = pargs if mb_units and yaxis == "MRows/s": yaxis = "MB/s" if tit: gtitle = tit plots = [] legends = [] for filename in filenames: plegend = filename[filename.find('cl-') + 3:filename.index('.h5')] plegend = plegend.replace('-', ' ') xval, yval = get_values(filename, '') print("Values for %s --> %s, %s" % (filename, xval, yval)) #plots.append(loglog(xval, yval, linewidth=5)) plots.append(semilogx(xval, yval, linewidth=4)) legends.append(plegend) if 0: # Per a introduir dades simulades si es vol... xval = [1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000] # yval = [0.003, 0.005, 0.02, 0.06, 1.2, # 40, 210] yval = [0.0009, 0.0011, 0.0022, 0.005, 0.02, 0.2, 5.6] plots.append(loglog(xval, yval, linewidth=5)) legends.append("PyTables Std") show_plot(plots, yaxis, legends, gtitle) PyTables-v.3.1.1/bench/search-bench-rnd.sh000077500000000000000000000064151231437614300202360ustar00rootroot00000000000000#!/bin/sh # I don't know why, but the /usr/bin/python2.3 from Debian is a 30% slower # than my own compiled version! 2004-08-18 python="/usr/local/bin/python2.3 -O" writedata () { nrows=$1 bfile=$2 worst=$3 psyco=$4 if [ "$shuffle" = "1" ]; then shufflef="-S" else shufflef="" fi cmd="${python} search-bench.py -R ${worst} -b ${bfile} -h ${psyco} -l ${libcomp} -c ${complevel} ${shufflef} -w -n ${nrows} data.nobackup/bench-${libcomp}-${nrows}k.h5" echo ${cmd} ${cmd} } readdata () { nrows=$1 bfile=$2 worst=$3 psyco=$4 smode=$5 if [ "$smode" = "indexed" ]; then #repeats=100 repeats=20 else repeats=2 fi cmd="${python} search-bench.py -R ${worst} -h -b ${bfile} ${psyco} -m ${smode} -r -k ${repeats} data.nobackup/bench-${libcomp}-${nrows}k.h5" echo ${cmd} ${cmd} return } overwrite=0 if [ $# > 1 ]; then if [ "$1" = "-o" ]; then overwrite=1 fi fi if [ $# > 2 ]; then psyco=$2 fi # Configuration for testing #nrowslist="50000" #nrowslistworst="50000" # Normal test #nrowslist="1 2 5 10 20 50 100 200 500 1000 2000 5000 10000 20000" #nrowslistworst="1 2 5 10 20 50 100 200 500 1000 2000 5000 10000 20000" nrowslist="1 2 5 10 20 50 100 200 500 1000" nrowslistworst="1 2 5 10 20 50 100 200 500 1000" #nrowslist="1 2 5 10" #nrowslistworst="1 2 5 10" # The next can be regarded as parameters shuffle=1 for libcomp in none zlib lzo; do #for libcomp in none lzo; do if [ "$libcomp" = "none" ]; then complevel=0 else complevel=1 fi # The name of the data bench file bfile="worst-dbench-cl-${libcomp}-c${complevel}-S${shuffle}.h5" # Move out a possible previous benchmark file bn=`basename $bfile ".h5"` mv -f ${bn}-bck2.h5 ${bn}-bck3.h5 mv -f ${bn}-bck.h5 ${bn}-bck2.h5 if [ "$overwrite" = "1" ]; then echo "moving ${bn}.h5 to ${bn}-bck.h5" mv -f ${bn}.h5 ${bn}-bck.h5 else echo "copying ${bn}.h5 to ${bn}-bck.h5" cp -f ${bn}.h5 ${bn}-bck.h5 fi for worst in "" -t; do #for worst in ""; do # Write data files if [ "$worst" = "-t" ]; then echo echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" echo "Entering worst case..." echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" echo nrowslist=$nrowslistworst fi # Write data file for nrows in $nrowslist; do echo "*************************************************************" echo "Writing for nrows=$nrows Krows, psyco=$psyco, worst='${worst}'" echo "*************************************************************" writedata ${nrows} ${bfile} "${worst}" "${psyco}" done # Read data files for smode in indexed inkernel standard; do ${python} cacheout.py for nrows in $nrowslist; do echo "***********************************************************" echo "Searching for nrows=$nrows Krows, $smode, psyco=$psyco, worst='${worst}'" echo "***********************************************************" readdata ${nrows} ${bfile} "${worst}" "${psyco}" "${smode}" done done # Finally, after the final search, delete the source (if desired) # for nrows in $nrowslist; do # rm -f data.nobackup/bench-${libcomp}-${nrows}k.h5 # done done echo "New data available on: $bfile" done exit 0 PyTables-v.3.1.1/bench/search-bench.py000066400000000000000000000430601231437614300174650ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import sys import math import time import random import warnings import numpy from tables import * # Initialize the random generator always with the same integer # in order to have reproductible results random.seed(19) numpy.random.seed(19) randomvalues = 0 worst = 0 Small = { "var1": StringCol(itemsize=4, dflt="Hi!", pos=2), "var2": Int32Col(pos=1), "var3": Float64Col(pos=0), #"var4" : BoolCol(), } def createNewBenchFile(bfile, verbose): class Create(IsDescription): nrows = Int32Col(pos=0) irows = Int32Col(pos=1) tfill = Float64Col(pos=2) tidx = Float64Col(pos=3) tcfill = Float64Col(pos=4) tcidx = Float64Col(pos=5) rowsecf = Float64Col(pos=6) rowseci = Float64Col(pos=7) fsize = Float64Col(pos=8) isize = Float64Col(pos=9) psyco = BoolCol(pos=10) class Search(IsDescription): nrows = Int32Col(pos=0) rowsel = Int32Col(pos=1) time1 = Float64Col(pos=2) time2 = Float64Col(pos=3) tcpu1 = Float64Col(pos=4) tcpu2 = Float64Col(pos=5) rowsec1 = Float64Col(pos=6) rowsec2 = Float64Col(pos=7) psyco = BoolCol(pos=8) if verbose: print("Creating a new benchfile:", bfile) # Open the benchmarking file bf = open_file(bfile, "w") # Create groups for recsize in ["small"]: group = bf.create_group("/", recsize, recsize + " Group") # Attach the row size of table as attribute if recsize == "small": group._v_attrs.rowsize = 16 # Create a Table for writing bench bf.create_table(group, "create_best", Create, "best case") bf.create_table(group, "create_worst", Create, "worst case") for case in ["best", "worst"]: # create a group for searching bench (best case) groupS = bf.create_group(group, "search_" + case, "Search Group") # Create Tables for searching for mode in ["indexed", "inkernel", "standard"]: groupM = bf.create_group(groupS, mode, mode + " Group") # for searching bench # for atom in ["string", "int", "float", "bool"]: for atom in ["string", "int", "float"]: bf.create_table(groupM, atom, Search, atom + " bench") bf.close() def createFile(filename, nrows, filters, index, heavy, noise, verbose): # Open a file in "w"rite mode fileh = open_file(filename, mode="w", title="Searchsorted Benchmark", filters=filters) rowswritten = 0 # Create the test table table = fileh.create_table(fileh.root, 'table', Small, "test table", None, nrows) t1 = time.time() cpu1 = time.clock() nrowsbuf = table.nrowsinbuf minimum = 0 maximum = nrows for i in range(0, nrows, nrowsbuf): if i + nrowsbuf > nrows: j = nrows else: j = i + nrowsbuf if randomvalues: var3 = numpy.random.uniform(minimum, maximum, size=j - i) else: var3 = numpy.arange(i, j, dtype=numpy.float64) if noise > 0: var3 += numpy.random.uniform(-noise, noise, size=j - i) var2 = numpy.array(var3, dtype=numpy.int32) var1 = numpy.empty(shape=[j - i], dtype="S4") if not heavy: var1[:] = var2 table.append([var3, var2, var1]) table.flush() rowswritten += nrows time1 = time.time() - t1 tcpu1 = time.clock() - cpu1 print("Time for filling:", round(time1, 3), "Krows/s:", round(nrows / 1000. / time1, 3), end=' ') fileh.close() size1 = os.stat(filename)[6] print(", File size:", round(size1 / (1024. * 1024.), 3), "MB") fileh = open_file(filename, mode="a", title="Searchsorted Benchmark", filters=filters) table = fileh.root.table rowsize = table.rowsize if index: t1 = time.time() cpu1 = time.clock() # Index all entries if not heavy: indexrows = table.cols.var1.create_index(filters=filters) for colname in ['var2', 'var3']: table.colinstances[colname].create_index(filters=filters) time2 = time.time() - t1 tcpu2 = time.clock() - cpu1 print("Time for indexing:", round(time2, 3), "iKrows/s:", round(indexrows / 1000. / time2, 3), end=' ') else: indexrows = 0 time2 = 0.0000000001 # an ugly hack tcpu2 = 0. if verbose: if index: idx = table.cols.var1.index print("Index parameters:", repr(idx)) else: print("NOT indexing rows") # Close the file fileh.close() size2 = os.stat(filename)[6] - size1 if index: print(", Index size:", round(size2 / (1024. * 1024.), 3), "MB") return (rowswritten, indexrows, rowsize, time1, time2, tcpu1, tcpu2, size1, size2) def benchCreate(file, nrows, filters, index, bfile, heavy, psyco, noise, verbose): # Open the benchfile in append mode bf = open_file(bfile, "a") recsize = "small" if worst: table = bf.get_node("/" + recsize + "/create_worst") else: table = bf.get_node("/" + recsize + "/create_best") (rowsw, irows, rowsz, time1, time2, tcpu1, tcpu2, size1, size2) = \ createFile(file, nrows, filters, index, heavy, noise, verbose) # Collect data table.row["nrows"] = rowsw table.row["irows"] = irows table.row["tfill"] = time1 table.row["tidx"] = time2 table.row["tcfill"] = tcpu1 table.row["tcidx"] = tcpu2 table.row["fsize"] = size1 table.row["isize"] = size2 table.row["psyco"] = psyco tapprows = round(time1, 3) cpuapprows = round(tcpu1, 3) tpercent = int(round(cpuapprows / tapprows, 2) * 100) print("Rows written:", rowsw, " Row size:", rowsz) print("Time writing rows: %s s (real) %s s (cpu) %s%%" % (tapprows, cpuapprows, tpercent)) rowsecf = rowsw / tapprows table.row["rowsecf"] = rowsecf # print "Write rows/sec: ", rowsecf print("Total file size:", round((size1 + size2) / (1024. * 1024.), 3), "MB", end=' ') print(", Write KB/s (pure data):", int(rowsw * rowsz / (tapprows * 1024))) # print "Write KB/s :", int((size1+size2) / ((time1+time2) * 1024)) tidxrows = time2 cpuidxrows = round(tcpu2, 3) tpercent = int(round(cpuidxrows / tidxrows, 2) * 100) print("Rows indexed:", irows, " (IMRows):", irows / float(10 ** 6)) print("Time indexing rows: %s s (real) %s s (cpu) %s%%" % (round(tidxrows, 3), cpuidxrows, tpercent)) rowseci = irows / tidxrows table.row["rowseci"] = rowseci table.row.append() bf.close() def readFile(filename, atom, riter, indexmode, dselect, verbose): # Open the HDF5 file in read-only mode fileh = open_file(filename, mode="r") table = fileh.root.table var1 = table.cols.var1 var2 = table.cols.var2 var3 = table.cols.var3 if indexmode == "indexed": if var2.index.nelements > 0: where = table._whereIndexed else: warnings.warn( "Not indexed table or empty index. Defaulting to in-kernel " "selection") indexmode = "inkernel" where = table._whereInRange elif indexmode == "inkernel": where = table.where if verbose: print("Max rows in buf:", table.nrowsinbuf) print("Rows in", table._v_pathname, ":", table.nrows) print("Buffersize:", table.rowsize * table.nrowsinbuf) print("MaxTuples:", table.nrowsinbuf) if indexmode == "indexed": print("Chunk size:", var2.index.sorted.chunksize) print("Number of elements per slice:", var2.index.nelemslice) print("Slice number in", table._v_pathname, ":", var2.index.nrows) #table.nrowsinbuf = 10 # print "nrowsinbuf-->", table.nrowsinbuf rowselected = 0 time2 = 0. tcpu2 = 0. results = [] print("Select mode:", indexmode, ". Selecting for type:", atom) # Initialize the random generator always with the same integer # in order to have reproductible results on each read iteration random.seed(19) numpy.random.seed(19) for i in range(riter): # The interval for look values at. This is aproximately equivalent to # the number of elements to select rnd = numpy.random.randint(table.nrows) cpu1 = time.clock() t1 = time.time() if atom == "string": val = str(rnd)[-4:] if indexmode in ["indexed", "inkernel"]: results = [p.nrow for p in where('var1 == val')] else: results = [p.nrow for p in table if p["var1"] == val] elif atom == "int": val = rnd + dselect if indexmode in ["indexed", "inkernel"]: results = [p.nrow for p in where('(rnd <= var3) & (var3 < val)')] else: results = [p.nrow for p in table if rnd <= p["var2"] < val] elif atom == "float": val = rnd + dselect if indexmode in ["indexed", "inkernel"]: t1 = time.time() results = [p.nrow for p in where('(rnd <= var3) & (var3 < val)')] else: results = [p.nrow for p in table if float(rnd) <= p["var3"] < float(val)] else: raise ValueError("Value for atom '%s' not supported." % atom) rowselected += len(results) # print "selected values-->", results if i == 0: # First iteration time1 = time.time() - t1 tcpu1 = time.clock() - cpu1 else: if indexmode == "indexed": # if indexed, wait until the 5th iteration (in order to # insure that the index is effectively cached) to take times if i >= 5: time2 += time.time() - t1 tcpu2 += time.clock() - cpu1 else: time2 += time.time() - t1 tcpu2 += time.clock() - cpu1 if riter > 1: if indexmode == "indexed" and riter >= 5: correction = 5 else: correction = 1 time2 = time2 / (riter - correction) tcpu2 = tcpu2 / (riter - correction) if verbose and 1: print("Values that fullfill the conditions:") print(results) #rowsread = table.nrows * riter rowsread = table.nrows rowsize = table.rowsize # Close the file fileh.close() return (rowsread, rowselected, rowsize, time1, time2, tcpu1, tcpu2) def benchSearch(file, riter, indexmode, bfile, heavy, psyco, dselect, verbose): # Open the benchfile in append mode bf = open_file(bfile, "a") recsize = "small" if worst: tableparent = "/" + recsize + "/search_worst/" + indexmode + "/" else: tableparent = "/" + recsize + "/search_best/" + indexmode + "/" # Do the benchmarks if not heavy: #atomlist = ["string", "int", "float", "bool"] atomlist = ["string", "int", "float"] else: #atomlist = ["int", "float", "bool"] atomlist = ["int", "float"] for atom in atomlist: tablepath = tableparent + atom table = bf.get_node(tablepath) (rowsr, rowsel, rowssz, time1, time2, tcpu1, tcpu2) = \ readFile(file, atom, riter, indexmode, dselect, verbose) row = table.row row["nrows"] = rowsr row["rowsel"] = rowsel treadrows = round(time1, 6) row["time1"] = time1 treadrows2 = round(time2, 6) row["time2"] = time2 cpureadrows = round(tcpu1, 6) row["tcpu1"] = tcpu1 cpureadrows2 = round(tcpu2, 6) row["tcpu2"] = tcpu2 row["psyco"] = psyco tpercent = int(round(cpureadrows / treadrows, 2) * 100) if riter > 1: tpercent2 = int(round(cpureadrows2 / treadrows2, 2) * 100) else: tpercent2 = 0. tMrows = rowsr / (1000 * 1000.) sKrows = rowsel / 1000. if atom == "string": # just to print once print("Rows read:", rowsr, "Mread:", round(tMrows, 6), "Mrows") print("Rows selected:", rowsel, "Ksel:", round(sKrows, 6), "Krows") print("Time selecting (1st time): %s s (real) %s s (cpu) %s%%" % (treadrows, cpureadrows, tpercent)) if riter > 1: print("Time selecting (cached): %s s (real) %s s (cpu) %s%%" % (treadrows2, cpureadrows2, tpercent2)) #rowsec1 = round(rowsr / float(treadrows), 6)/10**6 rowsec1 = rowsr / treadrows row["rowsec1"] = rowsec1 print("Read Mrows/sec: ", end=' ') print(round(rowsec1 / 10. ** 6, 6), "(first time)", end=' ') if riter > 1: rowsec2 = rowsr / treadrows2 row["rowsec2"] = rowsec2 print(round(rowsec2 / 10. ** 6, 6), "(cache time)") else: print() # Append the info to the table row.append() table.flush() # Close the benchmark file bf.close() if __name__ == "__main__": import getopt try: import psyco psyco_imported = 1 except: psyco_imported = 0 usage = """usage: %s [-v] [-p] [-R] [-r] [-w] [-c level] [-l complib] [-S] [-F] [-n nrows] [-x] [-b file] [-t] [-h] [-k riter] [-m indexmode] [-N range] [-d range] datafile -v verbose -p use "psyco" if available -R use Random values for filling -r only read test -w only write test -c sets a compression level (do not set it or 0 for no compression) -l sets the compression library ("zlib", "lzo", "ucl", "bzip2" or "none") -S activate shuffling filter -F activate fletcher32 filter -n set the number of rows in tables (in krows) -x don't make indexes -b bench filename -t worsT searching case -h heavy benchmark (operations without strings) -m index mode for reading ("indexed" | "inkernel" | "standard") -N introduce (uniform) noise within range into the values -d the interval for look values (int, float) at. Default is 3. -k number of iterations for reading\n""" % sys.argv[0] try: opts, pargs = getopt.getopt( sys.argv[1:], 'vpSFRrowxthk:b:c:l:n:m:N:d:') except: sys.stderr.write(usage) sys.exit(0) # if we pass too much parameters, abort if len(pargs) != 1: sys.stderr.write(usage) sys.exit(0) # default options dselect = 3. noise = 0. verbose = 0 fieldName = None testread = 1 testwrite = 1 usepsyco = 0 complevel = 0 shuffle = 0 fletcher32 = 0 complib = "zlib" nrows = 1000 index = 1 heavy = 0 bfile = "bench.h5" supported_imodes = ["indexed", "inkernel", "standard"] indexmode = "inkernel" riter = 1 # Get the options for option in opts: if option[0] == '-v': verbose = 1 if option[0] == '-p': usepsyco = 1 if option[0] == '-R': randomvalues = 1 if option[0] == '-S': shuffle = 1 if option[0] == '-F': fletcher32 = 1 elif option[0] == '-r': testwrite = 0 elif option[0] == '-w': testread = 0 elif option[0] == '-x': index = 0 elif option[0] == '-h': heavy = 1 elif option[0] == '-t': worst = 1 elif option[0] == '-b': bfile = option[1] elif option[0] == '-c': complevel = int(option[1]) elif option[0] == '-l': complib = option[1] elif option[0] == '-m': indexmode = option[1] if indexmode not in supported_imodes: raise ValueError( "Indexmode should be any of '%s' and you passed '%s'" % (supported_imodes, indexmode)) elif option[0] == '-n': nrows = int(float(option[1]) * 1000) elif option[0] == '-N': noise = float(option[1]) elif option[0] == '-d': dselect = float(option[1]) elif option[0] == '-k': riter = int(option[1]) if worst: nrows -= 1 # the worst case if complib == "none": # This means no compression at all complib = "zlib" # just to make PyTables not complaining complevel = 0 # Catch the hdf5 file passed as the last argument file = pargs[0] # Build the Filters instance filters = Filters(complevel=complevel, complib=complib, shuffle=shuffle, fletcher32=fletcher32) # Create the benchfile (if needed) if not os.path.exists(bfile): createNewBenchFile(bfile, verbose) if testwrite: if verbose: print("Compression level:", complevel) if complevel > 0: print("Compression library:", complib) if shuffle: print("Suffling...") if psyco_imported and usepsyco: psyco.bind(createFile) benchCreate(file, nrows, filters, index, bfile, heavy, usepsyco, noise, verbose) if testread: if psyco_imported and usepsyco: psyco.bind(readFile) benchSearch(file, riter, indexmode, bfile, heavy, usepsyco, dselect, verbose) PyTables-v.3.1.1/bench/search-bench.sh000077500000000000000000000065771231437614300174660ustar00rootroot00000000000000#!/bin/sh python="python2.5 -O" writedata () { nrows=$1 bfile=$2 heavy=$3 psyco=$4 if [ "$shuffle" = "1" ]; then shufflef="-S" else shufflef="" fi cmd="${python} search-bench.py -b ${bfile} ${heavy} ${psyco} -l ${libcomp} -c ${complevel} ${shufflef} -w -n ${nrows} -x data.nobackup/bench-${libcomp}-${nrows}k.h5" echo ${cmd} ${cmd} } readdata () { nrows=$1 bfile=$2 heavy=$3 psyco=$4 smode=$5 if [ "$smode" = "indexed" ]; then repeats=100 else repeats=2 fi if [ "$heavy" = "-h" -a "$smode" = "standard" ]; then # For heavy mode don't do a standard search echo "Skipping the standard search for heavy mode" else cmd="${python} search-bench.py -b ${bfile} ${heavy} ${psyco} -m ${smode} -r -k ${repeats} data.nobackup/bench-${libcomp}-${nrows}k.h5" echo ${cmd} ${cmd} fi if [ "$smode" = "standard" -a "1" = "0" ]; then # Finally, after the final search, delete the source (if desired) rm -f data.nobackup/bench-${libcomp}-${nrows}k.h5 fi return } overwrite=0 if [ $# > 1 ]; then if [ "$1" = "-o" ]; then overwrite=1 fi fi if [ $# > 2 ]; then psyco=$2 fi # The next can be regarded as parameters libcomp="lzo" complevel=1 shuffle=1 # The name of the data bench file bfile="dbench-cl-${libcomp}-c${complevel}-S${shuffle}.h5" # Move out a possible previous benchmark file bn=`basename $bfile ".h5"` mv -f ${bn}-bck2.h5 ${bn}-bck3.h5 mv -f ${bn}-bck.h5 ${bn}-bck2.h5 if [ "$overwrite" = "1" ]; then echo "moving ${bn}.h5 to ${bn}-bck.h5" mv -f ${bn}.h5 ${bn}-bck.h5 else echo "copying ${bn}.h5 to ${bn}-bck.h5" cp -f ${bn}.h5 ${bn}-bck.h5 fi # Configuration for testing nrowslist="1 2" nrowslistheavy="5 10" # This config takes 10 minutes to complete (psyco, zlib) #nrowslist="1 2 5 10 20 50 100 200 500 1000" #nrowslistheavy="2000 5000 10000" #nrowslist="" #nrowslistheavy="1 2 5 10 20 50 100 200 500 1000 2000 5000 10000 20000 50000 100000" # Normal test #nrowslist="1 2 5 10 20 50 100 200 500 1000 2000 5000 10000" #nrowslistheavy="20000 50000 100000 200000 500000 1000000" # Big test #nrowslist="1 2 5 10 20 50 100 200 500 1000 2000 5000 10000" #nrowslistheavy="20000 50000 100000 200000 500000 1000000 2000000 5000000" for heavy in "" -h; do # Write data files (light mode) if [ "$heavy" = "-h" ]; then echo echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" echo "Entering heavy mode..." echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" echo nrowslist=$nrowslistheavy fi # Write data file for nrows in $nrowslist; do echo "*************************************************************" echo "Writing for nrows=$nrows Krows, psyco=$psyco, heavy='${heavy}'" echo "*************************************************************" writedata ${nrows} ${bfile} "${heavy}" "${psyco}" done # Read data files #for smode in indexed inkernel standard; do for smode in inkernel standard; do # for smode in indexed; do ${python} cacheout.py for nrows in $nrowslist; do echo "***********************************************************" echo "Searching for nrows=$nrows Krows, $smode, psyco=$psyco, heavy='${heavy}'" echo "***********************************************************" readdata ${nrows} ${bfile} "${heavy}" "${psyco}" "${smode}" done done done echo "New data available on: $bfile" exit 0 PyTables-v.3.1.1/bench/searchsorted-bench.py000066400000000000000000000272401231437614300207100ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import time from tables import * class Small(IsDescription): var1 = StringCol(itemsize=4) var2 = Int32Col() var3 = Float64Col() var4 = BoolCol() # Define a user record to characterize some kind of particles class Medium(IsDescription): var1 = StringCol(itemsize=16) # 16-character String #float1 = Float64Col(dflt=2.3) #float2 = Float64Col(dflt=2.3) # zADCcount = Int16Col() # signed short integer var2 = Int32Col() # signed short integer var3 = Float64Col() grid_i = Int32Col() # integer grid_j = Int32Col() # integer pressure = Float32Col() # float (single-precision) energy = Float64Col(shape=2) # double (double-precision) def createFile(filename, nrows, filters, atom, recsize, index, verbose): # Open a file in "w"rite mode fileh = open_file(filename, mode="w", title="Searchsorted Benchmark", filters=filters) title = "This is the IndexArray title" # Create an IndexArray instance rowswritten = 0 # Create an entry klass = {"small": Small, "medium": Medium} table = fileh.create_table(fileh.root, 'table', klass[recsize], title, None, nrows) for i in range(nrows): #table.row['var1'] = str(i) #table.row['var2'] = random.randrange(nrows) table.row['var2'] = i table.row['var3'] = i #table.row['var4'] = i % 2 #table.row['var4'] = i > 2 table.row.append() rowswritten += nrows table.flush() rowsize = table.rowsize indexrows = 0 # Index one entry: if index: if atom == "string": indexrows = table.cols.var1.create_index() elif atom == "bool": indexrows = table.cols.var4.create_index() elif atom == "int": indexrows = table.cols.var2.create_index() elif atom == "float": indexrows = table.cols.var3.create_index() else: raise ValueError("Index type not supported yet") if verbose: print("Number of indexed rows:", indexrows) # Close the file (eventually destroy the extended type) fileh.close() return (rowswritten, rowsize) def readFile(filename, atom, niter, verbose): # Open the HDF5 file in read-only mode fileh = open_file(filename, mode="r") table = fileh.root.table print("reading", table) if atom == "string": idxcol = table.cols.var1.index elif atom == "bool": idxcol = table.cols.var4.index elif atom == "int": idxcol = table.cols.var2.index else: idxcol = table.cols.var3.index if verbose: print("Max rows in buf:", table.nrowsinbuf) print("Rows in", table._v_pathname, ":", table.nrows) print("Buffersize:", table.rowsize * table.nrowsinbuf) print("MaxTuples:", table.nrowsinbuf) print("Chunk size:", idxcol.sorted.chunksize) print("Number of elements per slice:", idxcol.nelemslice) print("Slice number in", table._v_pathname, ":", idxcol.nrows) rowselected = 0 if atom == "string": for i in range(niter): #results = [table.row["var3"] for i in table.where(2+i<=table.cols.var2 < 10+i)] #results = [table.row.nrow() for i in table.where(2<=table.cols.var2 < 10)] results = [p["var1"] # p.nrow() for p in table.where(table.cols.var1 == "1111")] # for p in table.where("1000"<=table.cols.var1<="1010")] rowselected += len(results) elif atom == "bool": for i in range(niter): results = [p["var2"] # p.nrow() for p in table.where(table.cols.var4 == 0)] rowselected += len(results) elif atom == "int": for i in range(niter): #results = [table.row["var3"] for i in table.where(2+i<=table.cols.var2 < 10+i)] #results = [table.row.nrow() for i in table.where(2<=table.cols.var2 < 10)] results = [p["var2"] # p.nrow() # for p in table.where(110*i<=table.cols.var2<110*(i+1))] # for p in table.where(1000-30", positions) print("Total iterations in search:", niter) rowsread += table.nrows uncomprBytes += idxcol.sorted.chunksize * niter * idxcol.sorted.itemsize results = table.read(coords=positions) print("results length:", len(results)) if verbose: print("Values that fullfill the conditions:") print(results) # Close the file (eventually destroy the extended type) fileh.close() return (rowsread, uncomprBytes, niter) if __name__ == "__main__": import sys import getopt try: import psyco psyco_imported = 1 except: psyco_imported = 0 usage = """usage: %s [-v] [-p] [-R range] [-r] [-w] [-s recsize ] [-a atom] [-c level] [-l complib] [-S] [-F] [-i item] [-n nrows] [-x] [-k niter] file -v verbose -p use "psyco" if available -R select a range in a field in the form "start,stop,step" -r only read test -w only write test -s record size -a use [float], [int], [bool] or [string] atom -c sets a compression level (do not set it or 0 for no compression) -S activate shuffling filter -F activate fletcher32 filter -l sets the compression library to be used ("zlib", "lzo", "ucl", "bzip2") -i item to search -n set the number of rows in tables -x don't make indexes -k number of iterations for reading\n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'vpSFR:rwxk:s:a:c:l:i:n:') except: sys.stderr.write(usage) sys.exit(0) # if we pass too much parameters, abort if len(pargs) != 1: sys.stderr.write(usage) sys.exit(0) # default options verbose = 0 rng = None item = None atom = "int" fieldName = None testread = 1 testwrite = 1 usepsyco = 0 complevel = 0 shuffle = 0 fletcher32 = 0 complib = "zlib" nrows = 100 recsize = "small" index = 1 niter = 1 # Get the options for option in opts: if option[0] == '-v': verbose = 1 if option[0] == '-p': usepsyco = 1 if option[0] == '-S': shuffle = 1 if option[0] == '-F': fletcher32 = 1 elif option[0] == '-R': rng = [int(i) for i in option[1].split(",")] elif option[0] == '-r': testwrite = 0 elif option[0] == '-w': testread = 0 elif option[0] == '-x': index = 0 elif option[0] == '-s': recsize = option[1] elif option[0] == '-a': atom = option[1] if atom not in ["float", "int", "bool", "string"]: sys.stderr.write(usage) sys.exit(0) elif option[0] == '-c': complevel = int(option[1]) elif option[0] == '-l': complib = option[1] elif option[0] == '-i': item = eval(option[1]) elif option[0] == '-n': nrows = int(option[1]) elif option[0] == '-k': niter = int(option[1]) # Build the Filters instance filters = Filters(complevel=complevel, complib=complib, shuffle=shuffle, fletcher32=fletcher32) # Catch the hdf5 file passed as the last argument file = pargs[0] if testwrite: print("Compression level:", complevel) if complevel > 0: print("Compression library:", complib) if shuffle: print("Suffling...") t1 = time.time() cpu1 = time.clock() if psyco_imported and usepsyco: psyco.bind(createFile) (rowsw, rowsz) = createFile(file, nrows, filters, atom, recsize, index, verbose) t2 = time.time() cpu2 = time.clock() tapprows = round(t2 - t1, 3) cpuapprows = round(cpu2 - cpu1, 3) tpercent = int(round(cpuapprows / tapprows, 2) * 100) print("Rows written:", rowsw, " Row size:", rowsz) print("Time writing rows: %s s (real) %s s (cpu) %s%%" % (tapprows, cpuapprows, tpercent)) print("Write rows/sec: ", int(rowsw / float(tapprows))) print("Write KB/s :", int(rowsw * rowsz / (tapprows * 1024))) if testread: if psyco_imported and usepsyco: psyco.bind(readFile) psyco.bind(searchFile) t1 = time.time() cpu1 = time.clock() if rng or item: (rowsr, uncomprB, niter) = searchFile(file, atom, verbose, item) else: for i in range(1): (rowsr, rowsel, rowsz) = readFile(file, atom, niter, verbose) t2 = time.time() cpu2 = time.clock() treadrows = round(t2 - t1, 3) cpureadrows = round(cpu2 - cpu1, 3) tpercent = int(round(cpureadrows / treadrows, 2) * 100) tMrows = rowsr / (1000 * 1000.) sKrows = rowsel / 1000. print("Rows read:", rowsr, "Mread:", round(tMrows, 3), "Mrows") print("Rows selected:", rowsel, "Ksel:", round(sKrows, 3), "Krows") print("Time reading rows: %s s (real) %s s (cpu) %s%%" % (treadrows, cpureadrows, tpercent)) print("Read Mrows/sec: ", round(tMrows / float(treadrows), 3)) # print "Read KB/s :", int(rowsr * rowsz / (treadrows * 1024)) # print "Uncompr MB :", int(uncomprB / (1024 * 1024)) # print "Uncompr MB/s :", int(uncomprB / (treadrows * 1024 * 1024)) # print "Total chunks uncompr :", int(niter) PyTables-v.3.1.1/bench/searchsorted-bench2.py000066400000000000000000000272771231437614300210040ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import time from tables import * class Small(IsDescription): var1 = StringCol(itemsize=4) var2 = Int32Col() var3 = Float64Col() var4 = BoolCol() # Define a user record to characterize some kind of particles class Medium(IsDescription): var1 = StringCol(itemsize=16, dflt="") # 16-character String #float1 = Float64Col(dflt=2.3) #float2 = Float64Col(dflt=2.3) # zADCcount = Int16Col() # signed short integer var2 = Int32Col() # signed short integer var3 = Float64Col() grid_i = Int32Col() # integer grid_j = Int32Col() # integer pressure = Float32Col() # float (single-precision) energy = Float64Col(shape=2) # double (double-precision) def createFile(filename, nrows, filters, atom, recsize, index, verbose): # Open a file in "w"rite mode fileh = open_file(filename, mode="w", title="Searchsorted Benchmark", filters=filters) title = "This is the IndexArray title" # Create an IndexArray instance rowswritten = 0 # Create an entry klass = {"small": Small, "medium": Medium} table = fileh.create_table(fileh.root, 'table', klass[recsize], title, None, nrows) for i in range(nrows): #table.row['var1'] = str(i) #table.row['var2'] = random.randrange(nrows) table.row['var2'] = i table.row['var3'] = i #table.row['var4'] = i % 2 table.row['var4'] = i > 2 table.row.append() rowswritten += nrows table.flush() rowsize = table.rowsize indexrows = 0 # Index one entry: if index: if atom == "string": indexrows = table.cols.var1.create_index() elif atom == "bool": indexrows = table.cols.var4.create_index() elif atom == "int": indexrows = table.cols.var2.create_index() elif atom == "float": indexrows = table.cols.var3.create_index() else: raise ValueError("Index type not supported yet") if verbose: print("Number of indexed rows:", indexrows) # Close the file (eventually destroy the extended type) fileh.close() return (rowswritten, rowsize) def readFile(filename, atom, niter, verbose): # Open the HDF5 file in read-only mode fileh = open_file(filename, mode="r") table = fileh.root.table print("reading", table) if atom == "string": idxcol = table.cols.var1.index elif atom == "bool": idxcol = table.cols.var4.index elif atom == "int": idxcol = table.cols.var2.index else: idxcol = table.cols.var3.index if verbose: print("Max rows in buf:", table.nrowsinbuf) print("Rows in", table._v_pathname, ":", table.nrows) print("Buffersize:", table.rowsize * table.nrowsinbuf) print("MaxTuples:", table.nrowsinbuf) print("Chunk size:", idxcol.sorted.chunksize) print("Number of elements per slice:", idxcol.nelemslice) print("Slice number in", table._v_pathname, ":", idxcol.nrows) rowselected = 0 if atom == "string": for i in range(niter): #results = [table.row["var3"] for i in table(where=2+i<=table.cols.var2 < 10+i)] #results = [table.row.nrow() for i in table(where=2<=table.cols.var2 < 10)] results = [p["var1"] # p.nrow() for p in table(where=table.cols.var1 == "1111")] # for p in table(where="1000"<=table.cols.var1<="1010")] rowselected += len(results) elif atom == "bool": for i in range(niter): results = [p["var2"] # p.nrow() for p in table(where=table.cols.var4 == 0)] rowselected += len(results) elif atom == "int": for i in range(niter): #results = [table.row["var3"] for i in table(where=2+i<=table.cols.var2 < 10+i)] #results = [table.row.nrow() for i in table(where=2<=table.cols.var2 < 10)] results = [p["var2"] # p.nrow() # for p in table(where=110*i<=table.cols.var2<110*(i+1))] # for p in table(where=1000-30", positions) print("Total iterations in search:", niter) rowsread += table.nrows uncomprBytes += idxcol.sorted.chunksize * niter * idxcol.sorted.itemsize results = table.read(coords=positions) print("results length:", len(results)) if verbose: print("Values that fullfill the conditions:") print(results) # Close the file (eventually destroy the extended type) fileh.close() return (rowsread, uncomprBytes, niter) if __name__ == "__main__": import sys import getopt try: import psyco psyco_imported = 1 except: psyco_imported = 0 usage = """usage: %s [-v] [-p] [-R range] [-r] [-w] [-s recsize ] [-a atom] [-c level] [-l complib] [-S] [-F] [-i item] [-n nrows] [-x] [-k niter] file -v verbose -p use "psyco" if available -R select a range in a field in the form "start,stop,step" -r only read test -w only write test -s record size -a use [float], [int], [bool] or [string] atom -c sets a compression level (do not set it or 0 for no compression) -S activate shuffling filter -F activate fletcher32 filter -l sets the compression library to be used ("zlib", "lzo", "ucl", "bzip2") -i item to search -n set the number of rows in tables -x don't make indexes -k number of iterations for reading\n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'vpSFR:rwxk:s:a:c:l:i:n:') except: sys.stderr.write(usage) sys.exit(0) # if we pass too much parameters, abort if len(pargs) != 1: sys.stderr.write(usage) sys.exit(0) # default options verbose = 0 rng = None item = None atom = "int" fieldName = None testread = 1 testwrite = 1 usepsyco = 0 complevel = 0 shuffle = 0 fletcher32 = 0 complib = "zlib" nrows = 100 recsize = "small" index = 1 niter = 1 # Get the options for option in opts: if option[0] == '-v': verbose = 1 if option[0] == '-p': usepsyco = 1 if option[0] == '-S': shuffle = 1 if option[0] == '-F': fletcher32 = 1 elif option[0] == '-R': rng = [int(i) for i in option[1].split(",")] elif option[0] == '-r': testwrite = 0 elif option[0] == '-w': testread = 0 elif option[0] == '-x': index = 0 elif option[0] == '-s': recsize = option[1] elif option[0] == '-a': atom = option[1] if atom not in ["float", "int", "bool", "string"]: sys.stderr.write(usage) sys.exit(0) elif option[0] == '-c': complevel = int(option[1]) elif option[0] == '-l': complib = option[1] elif option[0] == '-i': item = eval(option[1]) elif option[0] == '-n': nrows = int(option[1]) elif option[0] == '-k': niter = int(option[1]) # Build the Filters instance filters = Filters(complevel=complevel, complib=complib, shuffle=shuffle, fletcher32=fletcher32) # Catch the hdf5 file passed as the last argument file = pargs[0] if testwrite: print("Compression level:", complevel) if complevel > 0: print("Compression library:", complib) if shuffle: print("Suffling...") t1 = time.time() cpu1 = time.clock() if psyco_imported and usepsyco: psyco.bind(createFile) (rowsw, rowsz) = createFile(file, nrows, filters, atom, recsize, index, verbose) t2 = time.time() cpu2 = time.clock() tapprows = round(t2 - t1, 3) cpuapprows = round(cpu2 - cpu1, 3) tpercent = int(round(cpuapprows / tapprows, 2) * 100) print("Rows written:", rowsw, " Row size:", rowsz) print("Time writing rows: %s s (real) %s s (cpu) %s%%" % (tapprows, cpuapprows, tpercent)) print("Write rows/sec: ", int(rowsw / float(tapprows))) print("Write KB/s :", int(rowsw * rowsz / (tapprows * 1024))) if testread: if psyco_imported and usepsyco: psyco.bind(readFile) psyco.bind(searchFile) t1 = time.time() cpu1 = time.clock() if rng or item: (rowsr, uncomprB, niter) = searchFile(file, atom, verbose, item) else: for i in range(1): (rowsr, rowsel, rowsz) = readFile(file, atom, niter, verbose) t2 = time.time() cpu2 = time.clock() treadrows = round(t2 - t1, 3) cpureadrows = round(cpu2 - cpu1, 3) tpercent = int(round(cpureadrows / treadrows, 2) * 100) tMrows = rowsr / (1000 * 1000.) sKrows = rowsel / 1000. print("Rows read:", rowsr, "Mread:", round(tMrows, 3), "Mrows") print("Rows selected:", rowsel, "Ksel:", round(sKrows, 3), "Krows") print("Time reading rows: %s s (real) %s s (cpu) %s%%" % (treadrows, cpureadrows, tpercent)) print("Read Mrows/sec: ", round(tMrows / float(treadrows), 3)) # print "Read KB/s :", int(rowsr * rowsz / (treadrows * 1024)) # print "Uncompr MB :", int(uncomprB / (1024 * 1024)) # print "Uncompr MB/s :", int(uncomprB / (treadrows * 1024 * 1024)) # print "Total chunks uncompr :", int(niter) PyTables-v.3.1.1/bench/shelve-bench.py000066400000000000000000000145471231437614300175160ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function from tables import * import numpy as NA import struct import sys import shelve import psyco # This class is accessible only for the examples class Small(IsDescription): """Record descriptor. A record has several columns. They are represented here as class attributes, whose names are the column names and their values will become their types. The IsDescription class will take care the user will not add any new variables and that its type is correct. """ var1 = StringCol(itemsize=4) var2 = Int32Col() var3 = Float64Col() # Define a user record to characterize some kind of particles class Medium(IsDescription): name = StringCol(itemsize=16) # 16-character String float1 = Float64Col(shape=2, dflt=2.3) #float1 = Float64Col(dflt=1.3) #float2 = Float64Col(dflt=2.3) ADCcount = Int16Col() # signed short integer grid_i = Int32Col() # integer grid_j = Int32Col() # integer pressure = Float32Col() # float (single-precision) energy = Float64Col() # double (double-precision) # Define a user record to characterize some kind of particles class Big(IsDescription): name = StringCol(itemsize=16) # 16-character String #float1 = Float64Col(shape=32, dflt=NA.arange(32)) #float2 = Float64Col(shape=32, dflt=NA.arange(32)) float1 = Float64Col(shape=32, dflt=range(32)) float2 = Float64Col(shape=32, dflt=[2.2] * 32) ADCcount = Int16Col() # signed short integer grid_i = Int32Col() # integer grid_j = Int32Col() # integer pressure = Float32Col() # float (single-precision) energy = Float64Col() # double (double-precision) def createFile(filename, totalrows, recsize): # Open a 'n'ew file fileh = shelve.open(filename, flag="n") rowswritten = 0 # Get the record object associated with the new table if recsize == "big": d = Big() arr = NA.array(NA.arange(32), type=NA.Float64) arr2 = NA.array(NA.arange(32), type=NA.Float64) elif recsize == "medium": d = Medium() else: d = Small() # print d # sys.exit(0) for j in range(3): # Create a table # table = fileh.create_table(group, 'tuple'+str(j), Record(), title, # compress = 6, expectedrows = totalrows) # Create a Table instance tablename = 'tuple' + str(j) table = [] # Fill the table if recsize == "big" or recsize == "medium": for i in range(totalrows): d.name = 'Particle: %6d' % (i) #d.TDCcount = i % 256 d.ADCcount = (i * 256) % (1 << 16) if recsize == "big": #d.float1 = NA.array([i]*32, NA.Float64) #d.float2 = NA.array([i**2]*32, NA.Float64) arr[0] = 1.1 d.float1 = arr arr2[0] = 2.2 d.float2 = arr2 pass else: d.float1 = NA.array([i ** 2] * 2, NA.Float64) #d.float1 = float(i) #d.float2 = float(i) d.grid_i = i d.grid_j = 10 - i d.pressure = float(i * i) d.energy = float(d.pressure ** 4) table.append((d.ADCcount, d.energy, d.float1, d.float2, d.grid_i, d.grid_j, d.name, d.pressure)) # Only on float case # table.append((d.ADCcount, d.energy, d.float1, # d.grid_i, d.grid_j, d.name, d.pressure)) else: for i in range(totalrows): d.var1 = str(i) d.var2 = i d.var3 = 12.1e10 table.append((d.var1, d.var2, d.var3)) # Save this table on disk fileh[tablename] = table rowswritten += totalrows # Close the file fileh.close() return (rowswritten, struct.calcsize(d._v_fmt)) def readFile(filename, recsize): # Open the HDF5 file in read-only mode fileh = shelve.open(filename, "r") for table in ['tuple0', 'tuple1', 'tuple2']: if recsize == "big" or recsize == "medium": e = [t[2] for t in fileh[table] if t[4] < 20] # if there is only one float (array) #e = [ t[1] for t in fileh[table] if t[3] < 20 ] else: e = [t[1] for t in fileh[table] if t[1] < 20] print("resulting selection list ==>", e) print("Total selected records ==> ", len(e)) # Close the file (eventually destroy the extended type) fileh.close() # Add code to test here if __name__ == "__main__": import getopt import time usage = """usage: %s [-f] [-s recsize] [-i iterations] file -s use [big] record, [medium] or [small] -i sets the number of rows in each table\n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 's:fi:') except: sys.stderr.write(usage) sys.exit(0) # if we pass too much parameters, abort if len(pargs) != 1: sys.stderr.write(usage) sys.exit(0) # default options recsize = "medium" iterations = 100 # Get the options for option in opts: if option[0] == '-s': recsize = option[1] if recsize not in ["big", "medium", "small"]: sys.stderr.write(usage) sys.exit(0) elif option[0] == '-i': iterations = int(option[1]) # Catch the hdf5 file passed as the last argument file = pargs[0] t1 = time.clock() psyco.bind(createFile) (rowsw, rowsz) = createFile(file, iterations, recsize) t2 = time.clock() tapprows = round(t2 - t1, 3) t1 = time.clock() psyco.bind(readFile) readFile(file, recsize) t2 = time.clock() treadrows = round(t2 - t1, 3) print("Rows written:", rowsw, " Row size:", rowsz) print("Time appending rows:", tapprows) print("Write rows/sec: ", int(iterations * 3 / float(tapprows))) print("Write KB/s :", int(rowsw * rowsz / (tapprows * 1024))) print("Time reading rows:", treadrows) print("Read rows/sec: ", int(iterations * 3 / float(treadrows))) print("Read KB/s :", int(rowsw * rowsz / (treadrows * 1024))) PyTables-v.3.1.1/bench/split-file.py000066400000000000000000000021721231437614300172120ustar00rootroot00000000000000""" Split out a monolithic file with many different runs of indexed_search.py. The resulting files are meant for use in get-figures.py. Usage: python split-file.py prefix filename """ import sys prefix = sys.argv[1] filename = sys.argv[2] f = open(filename) sf = None for line in f: if line.startswith('Processing database:'): if sf: sf.close() line2 = line.split(':')[1] # Check if entry is compressed and if has to be processed line2 = line2[:line2.rfind('.')] params = line2.split('-') optlevel = 0 complib = None for param in params: if param[0] == 'O' and param[1].isdigit(): optlevel = int(param[1]) elif param[:-1] in ('zlib', 'lzo'): complib = param if 'PyTables' in prefix: if complib: sfilename = "%s-O%s-%s.out" % (prefix, optlevel, complib) else: sfilename = "%s-O%s.out" % (prefix, optlevel,) else: sfilename = "%s.out" % (prefix,) sf = file(sfilename, 'a') if sf: sf.write(line) f.close() PyTables-v.3.1.1/bench/sqlite-search-bench-rnd.sh000077500000000000000000000056651231437614300215430ustar00rootroot00000000000000#!/bin/sh # I don't know why, but the /usr/bin/python2.3 from Debian is a 30% slower # than my own compiled version! 2004-08-18 python="/usr/local/bin/python2.3 -O" writedata () { nrows=$1 bfile=$2 smode=$3 psyco=$4 cmd="${python} sqlite-search-bench.py -R -h -b ${bfile} ${psyco} -m ${smode} -w -n ${nrows} data.nobackup/sqlite-${nrows}k.h5" echo ${cmd} ${cmd} } readdata () { nrows=$1 bfile=$2 smode=$3 psyco=$4 if [ "$smode" = "indexed" ]; then #repeats=100 repeats=20 else repeats=2 fi cmd="${python} sqlite-search-bench.py -R -h -b ${bfile} ${psyco} -n ${nrows} -m ${smode} -r -k ${repeats} data.nobackup/sqlite-${nrows}k.h5" echo ${cmd} ${cmd} # Finally, delete the source (if desired) if [ "$smode" = "indexed" ]; then echo "Deleting data file data.nobackup/sqlite-${nrows}k.h5" # rm -f data.nobackup/sqlite-${nrows}k.h5 fi return } overwrite=0 if [ $# > 1 ]; then if [ "$1" = "-o" ]; then overwrite=1 fi fi if [ $# > 2 ]; then psyco=$2 fi # The name of the data bench file bfile="sqlite-dbench.h5" # Move out a possible previous benchmark file bn=`basename $bfile ".h5"` mv -f ${bn}-bck2.h5 ${bn}-bck3.h5 mv -f ${bn}-bck.h5 ${bn}-bck2.h5 if [ "$overwrite" = "1" ]; then echo "moving ${bn}.h5 to ${bn}-bck.h5" mv -f ${bn}.h5 ${bn}-bck.h5 else echo "copying ${bn}.h5 to ${bn}-bck.h5" cp -f ${bn}.h5 ${bn}-bck.h5 fi # Configuration for testing nrowsliststd="1 2" nrowslistidx="1 2" #nrowsliststd="1 2 5 10 20 50 100 200 500 1000 2000 5000 10000 20000 50000" #nrowsliststd="1 2 5 10 20" #nrowslistidx="1 2 5 10 20" # nrowsliststd="1 2 5 10 20 50 100 200 500 1000 2000 5000 10000" # nrowslistidx="1 2 5 10 20 50 100 200 500 1000 2000 5000 10000" #nrowsliststd="1 2 5 10 20 50 100 200 500 1000 2000 5000 10000 20000 50000 100000" #nrowslistidx="1 2 5 10 20 50 100 200 500 1000 2000 5000 10000 20000 50000 100000" for smode in standard indexed; do #for smode in indexed; do echo echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" echo "Entering ${smode} mode..." echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" echo if [ "$smode" = "standard" ]; then nrowslist=$nrowsliststd else nrowslist=$nrowslistidx fi # Write data files for nrows in $nrowslist; do echo "*************************************************************" echo "Writing for nrows=$nrows Krows, $smode, psyco=$psyco" echo "*************************************************************" writedata ${nrows} ${bfile} "${smode}" "${psyco}" done # Read data files ${python} cacheout.py for nrows in $nrowslist; do echo "***********************************************************" echo "Searching for nrows=$nrows Krows, $smode, psyco=$psyco" echo "***********************************************************" readdata ${nrows} ${bfile} "${smode}" "${psyco}" done done echo "New data available on: $bfile" exit 0 PyTables-v.3.1.1/bench/sqlite-search-bench.py000066400000000000000000000347601231437614300207730ustar00rootroot00000000000000#!/usr/bin/python from __future__ import print_function import sqlite import random import time import sys import os import os.path from tables import * import numpy as np randomvalues = 0 standarddeviation = 10000 # Initialize the random generator always with the same integer # in order to have reproductible results random.seed(19) np.random.seed((19, 20)) # defaults psycon = 0 worst = 0 def createNewBenchFile(bfile, verbose): class Create(IsDescription): nrows = Int32Col(pos=0) irows = Int32Col(pos=1) tfill = Float64Col(pos=2) tidx = Float64Col(pos=3) tcfill = Float64Col(pos=4) tcidx = Float64Col(pos=5) rowsecf = Float64Col(pos=6) rowseci = Float64Col(pos=7) fsize = Float64Col(pos=8) isize = Float64Col(pos=9) psyco = BoolCol(pos=10) class Search(IsDescription): nrows = Int32Col(pos=0) rowsel = Int32Col(pos=1) time1 = Float64Col(pos=2) time2 = Float64Col(pos=3) tcpu1 = Float64Col(pos=4) tcpu2 = Float64Col(pos=5) rowsec1 = Float64Col(pos=6) rowsec2 = Float64Col(pos=7) psyco = BoolCol(pos=8) if verbose: print("Creating a new benchfile:", bfile) # Open the benchmarking file bf = open_file(bfile, "w") # Create groups for recsize in ["sqlite_small"]: group = bf.create_group("/", recsize, recsize + " Group") # Attach the row size of table as attribute if recsize == "small": group._v_attrs.rowsize = 16 # Create a Table for writing bench bf.create_table(group, "create_indexed", Create, "indexed values") bf.create_table(group, "create_standard", Create, "standard values") # create a group for searching bench groupS = bf.create_group(group, "search", "Search Group") # Create Tables for searching for mode in ["indexed", "standard"]: group = bf.create_group(groupS, mode, mode + " Group") # for searching bench # for atom in ["string", "int", "float", "bool"]: for atom in ["string", "int", "float"]: bf.create_table(group, atom, Search, atom + " bench") bf.close() def createFile(filename, nrows, filters, indexmode, heavy, noise, bfile, verbose): # Initialize some variables t1 = 0. t2 = 0. tcpu1 = 0. tcpu2 = 0. rowsecf = 0. rowseci = 0. size1 = 0. size2 = 0. if indexmode == "standard": print("Creating a new database:", dbfile) instd = os.popen("/usr/local/bin/sqlite " + dbfile, "w") CREATESTD = """ CREATE TABLE small ( -- Name Type -- Example --------------------------------------- recnum INTEGER PRIMARY KEY, -- 345 var1 char(4), -- Abronia villosa var2 INTEGER, -- 111 var3 FLOAT -- 12.32 ); """ CREATEIDX = """ CREATE TABLE small ( -- Name Type -- Example --------------------------------------- recnum INTEGER PRIMARY KEY, -- 345 var1 char(4), -- Abronia villosa var2 INTEGER, -- 111 var3 FLOAT -- 12.32 ); CREATE INDEX ivar1 ON small(var1); CREATE INDEX ivar2 ON small(var2); CREATE INDEX ivar3 ON small(var3); """ # Creating the table first and indexing afterwards is a bit faster instd.write(CREATESTD) instd.close() conn = sqlite.connect(dbfile) cursor = conn.cursor() if indexmode == "standard": place_holders = ",".join(['%s'] * 3) # Insert rows SQL = "insert into small values(NULL, %s)" % place_holders time1 = time.time() cpu1 = time.clock() # This way of filling is to copy the PyTables benchmark nrowsbuf = 1000 minimum = 0 maximum = nrows for i in range(0, nrows, nrowsbuf): if i + nrowsbuf > nrows: j = nrows else: j = i + nrowsbuf if randomvalues: var3 = np.random.uniform(minimum, maximum, shape=[j - i]) else: var3 = np.arange(i, j, type=np.Float64) if noise: var3 += np.random.uniform(-3, 3, shape=[j - i]) var2 = np.array(var3, type=np.Int32) var1 = np.array(None, shape=[j - i], dtype='s4') if not heavy: for n in range(j - i): var1[n] = str("%.4s" % var2[n]) for n in range(j - i): fields = (var1[n], var2[n], var3[n]) cursor.execute(SQL, fields) conn.commit() t1 = round(time.time() - time1, 5) tcpu1 = round(time.clock() - cpu1, 5) rowsecf = nrows / t1 size1 = os.stat(dbfile)[6] print("******** Results for writing nrows = %s" % (nrows), "*********") print(("Insert time:", t1, ", KRows/s:", round((nrows / 10. ** 3) / t1, 3),)) print(", File size:", round(size1 / (1024. * 1024.), 3), "MB") # Indexem if indexmode == "indexed": time1 = time.time() cpu1 = time.clock() if not heavy: cursor.execute("CREATE INDEX ivar1 ON small(var1)") conn.commit() cursor.execute("CREATE INDEX ivar2 ON small(var2)") conn.commit() cursor.execute("CREATE INDEX ivar3 ON small(var3)") conn.commit() t2 = round(time.time() - time1, 5) tcpu2 = round(time.clock() - cpu1, 5) rowseci = nrows / t2 print(("Index time:", t2, ", IKRows/s:", round((nrows / 10. ** 3) / t2, 3),)) size2 = os.stat(dbfile)[6] - size1 print((", Final size with index:", round(size2 / (1024. * 1024), 3), "MB")) conn.close() # Collect benchmark data bf = open_file(bfile, "a") recsize = "sqlite_small" if indexmode == "indexed": table = bf.get_node("/" + recsize + "/create_indexed") else: table = bf.get_node("/" + recsize + "/create_standard") table.row["nrows"] = nrows table.row["irows"] = nrows table.row["tfill"] = t1 table.row["tidx"] = t2 table.row["tcfill"] = tcpu1 table.row["tcidx"] = tcpu2 table.row["psyco"] = psycon table.row["rowsecf"] = rowsecf table.row["rowseci"] = rowseci table.row["fsize"] = size1 table.row["isize"] = size2 table.row.append() bf.close() return def readFile(dbfile, nrows, indexmode, heavy, dselect, bfile, riter): # Connect to the database. conn = sqlite.connect(db=dbfile, mode=755) # Obtain a cursor cursor = conn.cursor() # select count(*), avg(var2) SQL1 = """ select recnum from small where var1 = %s """ SQL2 = """ select recnum from small where var2 >= %s and var2 < %s """ SQL3 = """ select recnum from small where var3 >= %s and var3 < %s """ # Open the benchmark database bf = open_file(bfile, "a") # default values for the case that columns are not indexed t2 = 0. tcpu2 = 0. # Some previous computations for the case of random values if randomvalues: # algorithm to choose a value separated from mean # If want to select fewer values, select this # if nrows/2 > standarddeviation*3: # Choose five standard deviations away from mean value # dev = standarddeviation*5 # dev = standarddeviation*math.log10(nrows/1000.) # This algorithm give place to too asymmetric result values # if standarddeviation*10 < nrows/2: # Choose four standard deviations away from mean value # dev = standarddeviation*4 # else: # dev = 100 # Yet Another Algorithm if nrows / 2 > standarddeviation * 10: dev = standarddeviation * 4. elif nrows / 2 > standarddeviation: dev = standarddeviation * 2. elif nrows / 2 > standarddeviation / 10.: dev = standarddeviation / 10. else: dev = standarddeviation / 100. valmax = int(round((nrows / 2.) - dev)) # split the selection range in regular chunks if riter > valmax * 2: riter = valmax * 2 chunksize = (valmax * 2 / riter) * 10 # Get a list of integers for the intervals randlist = range(0, valmax, chunksize) randlist.extend(range(nrows - valmax, nrows, chunksize)) # expand the list ten times so as to use the cache randlist = randlist * 10 # shuffle the list random.shuffle(randlist) # reset the value of chunksize chunksize = chunksize / 10 # print "chunksize-->", chunksize # randlist.sort();print "randlist-->", randlist else: chunksize = 3 if heavy: searchmodelist = ["int", "float"] else: searchmodelist = ["string", "int", "float"] # Execute queries for atom in searchmodelist: time2 = 0 cpu2 = 0 rowsel = 0 for i in range(riter): rnd = random.randrange(nrows) time1 = time.time() cpu1 = time.clock() if atom == "string": #cursor.execute(SQL1, "1111") cursor.execute(SQL1, str(rnd)[-4:]) elif atom == "int": #cursor.execute(SQL2 % (rnd, rnd+3)) cursor.execute(SQL2 % (rnd, rnd + dselect)) elif atom == "float": #cursor.execute(SQL3 % (float(rnd), float(rnd+3))) cursor.execute(SQL3 % (float(rnd), float(rnd + dselect))) else: raise ValueError( "atom must take a value in ['string','int','float']") if i == 0: t1 = time.time() - time1 tcpu1 = time.clock() - cpu1 else: if indexmode == "indexed": # if indexed, wait until the 5th iteration to take # times (so as to insure that the index is # effectively cached) if i >= 5: time2 += time.time() - time1 cpu2 += time.clock() - cpu1 else: time2 += time.time() - time1 time2 += time.clock() - cpu1 if riter > 1: if indexmode == "indexed" and riter >= 5: correction = 5 else: correction = 1 t2 = time2 / (riter - correction) tcpu2 = cpu2 / (riter - correction) print(("*** Query results for atom = %s, nrows = %s, " "indexmode = %s ***" % (atom, nrows, indexmode))) print("Query time:", round(t1, 5), ", cached time:", round(t2, 5)) print("MRows/s:", round((nrows / 10. ** 6) / t1, 3), end=' ') if t2 > 0: print(", cached MRows/s:", round((nrows / 10. ** 6) / t2, 3)) else: print() # Collect benchmark data recsize = "sqlite_small" tablepath = "/" + recsize + "/search/" + indexmode + "/" + atom table = bf.get_node(tablepath) table.row["nrows"] = nrows table.row["rowsel"] = rowsel table.row["time1"] = t1 table.row["time2"] = t2 table.row["tcpu1"] = tcpu1 table.row["tcpu2"] = tcpu2 table.row["psyco"] = psycon table.row["rowsec1"] = nrows / t1 if t2 > 0: table.row["rowsec2"] = nrows / t2 table.row.append() table.flush() # Flush the data # Close the database conn.close() bf.close() # the bench database return if __name__ == "__main__": import getopt try: import psyco psyco_imported = 1 except: psyco_imported = 0 usage = """usage: %s [-v] [-p] [-R] [-h] [-t] [-r] [-w] [-n nrows] [-b file] [-k riter] [-m indexmode] [-N range] datafile -v verbose -p use "psyco" if available -R use Random values for filling -h heavy mode (exclude strings from timings) -t worsT searching case (to emulate PyTables worst cases) -r only read test -w only write test -n the number of rows (in krows) -b bench filename -N introduce (uniform) noise within range into the values -d the interval for look values (int, float) at. Default is 3. -k number of iterations for reading\n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'vpRhtrwn:b:k:m:N:d:') except: sys.stderr.write(usage) sys.exit(0) # if we pass too much parameters, abort if len(pargs) != 1: sys.stderr.write(usage) sys.exit(0) # default options dselect = 3. noise = 0. verbose = 0 heavy = 0 testread = 1 testwrite = 1 usepsyco = 0 nrows = 1000 bfile = "sqlite-bench.h5" supported_imodes = ["indexed", "standard"] indexmode = "indexed" riter = 2 # Get the options for option in opts: if option[0] == '-v': verbose = 1 if option[0] == '-p': usepsyco = 1 elif option[0] == '-R': randomvalues = 1 elif option[0] == '-h': heavy = 1 elif option[0] == '-t': worst = 1 elif option[0] == '-r': testwrite = 0 elif option[0] == '-w': testread = 0 elif option[0] == '-b': bfile = option[1] elif option[0] == '-N': noise = float(option[1]) elif option[0] == '-m': indexmode = option[1] if indexmode not in supported_imodes: raise ValueError( "Indexmode should be any of '%s' and you passed '%s'" % (supported_imodes, indexmode)) elif option[0] == '-n': nrows = int(float(option[1]) * 1000) elif option[0] == '-d': dselect = float(option[1]) elif option[0] == '-k': riter = int(option[1]) # remaining parameters dbfile = pargs[0] if worst: nrows -= 1 # the worst case # Create the benchfile (if needed) if not os.path.exists(bfile): createNewBenchFile(bfile, verbose) if testwrite: if psyco_imported and usepsyco: psyco.bind(createFile) psycon = 1 createFile(dbfile, nrows, None, indexmode, heavy, noise, bfile, verbose) if testread: if psyco_imported and usepsyco: psyco.bind(readFile) psycon = 1 readFile(dbfile, nrows, indexmode, heavy, dselect, bfile, riter) PyTables-v.3.1.1/bench/sqlite-search-bench.sh000077500000000000000000000051051231437614300207470ustar00rootroot00000000000000#!/bin/sh # I don't know why, but the /usr/bin/python2.3 from Debian is a 30% slower # than my own compiled version! 2004-08-18 python="/usr/local/bin/python2.3 -O" writedata () { nrows=$1 bfile=$2 smode=$3 psyco=$4 cmd="${python} sqlite-search-bench.py -b ${bfile} ${psyco} -m ${smode} -w -n ${nrows} data.nobackup/sqlite-${nrows}k-${smode}.h5" echo ${cmd} ${cmd} } readdata () { nrows=$1 bfile=$2 smode=$3 psyco=$4 if [ "$smode" = "indexed" ]; then repeats=100 else repeats=2 fi cmd="${python} sqlite-search-bench.py -b ${bfile} ${psyco} -n ${nrows} -m ${smode} -r -k ${repeats} data.nobackup/sqlite-${nrows}k-${smode}.h5" echo ${cmd} ${cmd} # Finally, delete the source (if desired) #rm -f data.nobackup/sqlite-${nrows}k-${smode}.h5 return } overwrite=0 if [ $# > 1 ]; then if [ "$1" = "-o" ]; then overwrite=1 fi fi if [ $# > 2 ]; then psyco=$2 fi # The name of the data bench file bfile="sqlite-dbench.h5" # Move out a possible previous benchmark file bn=`basename $bfile ".h5"` mv -f ${bn}-bck2.h5 ${bn}-bck3.h5 mv -f ${bn}-bck.h5 ${bn}-bck2.h5 if [ "$overwrite" = "1" ]; then echo "moving ${bn}.h5 to ${bn}-bck.h5" mv -f ${bn}.h5 ${bn}-bck.h5 else echo "copying ${bn}.h5 to ${bn}-bck.h5" cp -f ${bn}.h5 ${bn}-bck.h5 fi # Configuration for testing nrowsliststd="1 2 5 10 20 50" #nrowslistidx="1 2 5 10 20 50" #nrowsliststd="1 2 5 10 20 50 100 200 500 1000 2000 5000 10000 20000 50000" nrowslistidx="1 2 5 10 20 50 100 200 500 1000 2000 5000 10000" #for smode in standard indexed; do for smode in indexed; do echo echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" echo "Entering ${smode} mode..." echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" echo if [ "$smode" = "standard" ]; then nrowslist=$nrowsliststd else nrowslist=$nrowslistidx fi # Write data files # for nrows in $nrowslist; do # echo "*************************************************************" # echo "Writing for nrows=$nrows Krows, $smode, psyco=$psyco" # echo "*************************************************************" # writedata ${nrows} ${bfile} "${smode}" "${psyco}" # done # Read data files ${python} cacheout.py for nrows in $nrowslist; do echo "***********************************************************" echo "Searching for nrows=$nrows Krows, $smode, psyco=$psyco" echo "***********************************************************" readdata ${nrows} ${bfile} "${smode}" "${psyco}" done done echo "New data available on: $bfile" exit 0 PyTables-v.3.1.1/bench/sqlite3-search-bench.py000066400000000000000000000116571231437614300210560ustar00rootroot00000000000000from __future__ import print_function import os import os.path from time import time import numpy import random # in order to always generate the same random sequence random.seed(19) def fill_arrays(start, stop): col_i = numpy.arange(start, stop, dtype=numpy.int32) if userandom: col_j = numpy.random.uniform(0, nrows, stop - start) else: col_j = numpy.array(col_i, dtype=numpy.float64) return col_i, col_j # Generator for ensure pytables benchmark compatibility def int_generator(nrows): step = 1000 * 100 j = 0 for i in range(nrows): if i >= step * j: stop = (j + 1) * step if stop > nrows: # Seems unnecessary stop = nrows col_i, col_j = fill_arrays(i, stop) j += 1 k = 0 yield (col_i[k], col_j[k]) k += 1 def int_generator_slow(nrows): for i in range(nrows): if userandom: yield (i, float(random.randint(0, nrows))) else: yield (i, float(i)) def open_db(filename, remove=0): if remove and os.path.exists(filename): os.remove(filename) con = sqlite.connect(filename) cur = con.cursor() return con, cur def create_db(filename, nrows): con, cur = open_db(filename, remove=1) cur.execute("create table ints(i integer, j real)") t1 = time() # This is twice as fast as a plain loop cur.executemany("insert into ints(i,j) values (?,?)", int_generator(nrows)) con.commit() ctime = time() - t1 if verbose: print("insert time:", round(ctime, 5)) print("Krows/s:", round((nrows / 1000.) / ctime, 5)) close_db(con, cur) def index_db(filename): con, cur = open_db(filename) t1 = time() cur.execute("create index ij on ints(j)") con.commit() itime = time() - t1 if verbose: print("index time:", round(itime, 5)) print("Krows/s:", round(nrows / itime, 5)) # Close the DB close_db(con, cur) def query_db(filename, rng): con, cur = open_db(filename) t1 = time() ntimes = 10 for i in range(ntimes): # between clause does not seem to take advantage of indexes # cur.execute("select j from ints where j between %s and %s" % \ cur.execute("select i from ints where j >= %s and j <= %s" % # cur.execute("select i from ints where i >= %s and i <= # %s" % (rng[0] + i, rng[1] + i)) results = cur.fetchall() con.commit() qtime = (time() - t1) / ntimes if verbose: print("query time:", round(qtime, 5)) print("Mrows/s:", round((nrows / 1000.) / qtime, 5)) print(results) close_db(con, cur) def close_db(con, cur): cur.close() con.close() if __name__ == "__main__": import sys import getopt try: import psyco psyco_imported = 1 except: psyco_imported = 0 usage = """usage: %s [-v] [-p] [-m] [-i] [-q] [-c] [-R range] [-n nrows] file -v verbose -p use "psyco" if available -m use random values to fill the table -q do query -c create the database -i index the table -2 use sqlite2 (default is use sqlite3) -R select a range in a field in the form "start,stop" (def "0,10") -n sets the number of rows (in krows) in each table \n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'vpmiqc2R:n:') except: sys.stderr.write(usage) sys.exit(0) # default options verbose = 0 usepsyco = 0 userandom = 0 docreate = 0 createindex = 0 doquery = 0 sqlite_version = "3" rng = [0, 10] nrows = 1 # Get the options for option in opts: if option[0] == '-v': verbose = 1 elif option[0] == '-p': usepsyco = 1 elif option[0] == '-m': userandom = 1 elif option[0] == '-i': createindex = 1 elif option[0] == '-q': doquery = 1 elif option[0] == '-c': docreate = 1 elif option[0] == "-2": sqlite_version = "2" elif option[0] == '-R': rng = [int(i) for i in option[1].split(",")] elif option[0] == '-n': nrows = int(option[1]) # Catch the hdf5 file passed as the last argument filename = pargs[0] if sqlite_version == "2": import sqlite else: from pysqlite2 import dbapi2 as sqlite if verbose: print("pysqlite version:", sqlite.version) if userandom: print("using random values") if docreate: if verbose: print("writing %s krows" % nrows) if psyco_imported and usepsyco: psyco.bind(create_db) nrows *= 1000 create_db(filename, nrows) if createindex: index_db(filename) if doquery: query_db(filename, rng) PyTables-v.3.1.1/bench/stress-test.py000066400000000000000000000276301231437614300174500ustar00rootroot00000000000000from __future__ import print_function import gc import sys import time #import types import numpy from tables import Group # , MetaIsDescription from tables import * class Test(IsDescription): ngroup = Int32Col(pos=1) ntable = Int32Col(pos=2) nrow = Int32Col(pos=3) #string = StringCol(itemsize=500, pos=4) TestDict = { "ngroup": Int32Col(pos=1), "ntable": Int32Col(pos=2), "nrow": Int32Col(pos=3), } def createFileArr(filename, ngroups, ntables, nrows): # First, create the groups # Open a file in "w"rite mode fileh = open_file(filename, mode="w", title="PyTables Stress Test") for k in range(ngroups): # Create the group fileh.create_group("/", 'group%04d' % k, "Group %d" % k) fileh.close() # Now, create the arrays arr = numpy.arange(nrows) for k in range(ngroups): fileh = open_file(filename, mode="a", root_uep='group%04d' % k) for j in range(ntables): # Create the array fileh.create_array("/", 'array%04d' % j, arr, "Array %d" % j) fileh.close() return (ngroups * ntables * nrows, 4) def readFileArr(filename, ngroups, recsize, verbose): rowsread = 0 for ngroup in range(ngroups): fileh = open_file(filename, mode="r", root_uep='group%04d' % ngroup) # Get the group group = fileh.root narrai = 0 if verbose: print("Group ==>", group) for arrai in fileh.list_nodes(group, 'Array'): if verbose > 1: print("Array ==>", arrai) print("Rows in", arrai._v_pathname, ":", arrai.shape) arr = arrai.read() rowsread += len(arr) narrai += 1 # Close the file (eventually destroy the extended type) fileh.close() return (rowsread, 4, rowsread * 4) def createFile(filename, ngroups, ntables, nrows, complevel, complib, recsize): # First, create the groups # Open a file in "w"rite mode fileh = open_file(filename, mode="w", title="PyTables Stress Test") for k in range(ngroups): # Create the group group = fileh.create_group("/", 'group%04d' % k, "Group %d" % k) fileh.close() # Now, create the tables rowswritten = 0 if not ntables: rowsize = 0 for k in range(ngroups): print("Filling tables in group:", k) fileh = open_file(filename, mode="a", root_uep='group%04d' % k) # Get the group group = fileh.root for j in range(ntables): # Create a table # table = fileh.create_table(group, 'table%04d'% j, Test, table = fileh.create_table(group, 'table%04d' % j, TestDict, 'Table%04d' % j, complevel, complib, nrows) rowsize = table.rowsize # Get the row object associated with the new table row = table.row # Fill the table for i in range(nrows): row['ngroup'] = k row['ntable'] = j row['nrow'] = i row.append() rowswritten += nrows table.flush() # Close the file fileh.close() return (rowswritten, rowsize) def readFile(filename, ngroups, recsize, verbose): # Open the HDF5 file in read-only mode rowsize = 0 buffersize = 0 rowsread = 0 for ngroup in range(ngroups): fileh = open_file(filename, mode="r", root_uep='group%04d' % ngroup) # Get the group group = fileh.root ntable = 0 if verbose: print("Group ==>", group) for table in fileh.list_nodes(group, 'Table'): rowsize = table.rowsize buffersize = table.rowsize * table.nrowsinbuf if verbose > 1: print("Table ==>", table) print("Max rows in buf:", table.nrowsinbuf) print("Rows in", table._v_pathname, ":", table.nrows) print("Buffersize:", table.rowsize * table.nrowsinbuf) print("MaxTuples:", table.nrowsinbuf) nrow = 0 if table.nrows > 0: # only read if we have rows in tables for row in table: try: assert row["ngroup"] == ngroup assert row["ntable"] == ntable assert row["nrow"] == nrow except: print("Error in group: %d, table: %d, row: %d" % (ngroup, ntable, nrow)) print("Record ==>", row) nrow += 1 assert nrow == table.nrows rowsread += table.nrows ntable += 1 # Close the file (eventually destroy the extended type) fileh.close() return (rowsread, rowsize, buffersize) class TrackRefs: """Object to track reference counts across test runs.""" def __init__(self, verbose=0): self.type2count = {} self.type2all = {} self.verbose = verbose def update(self, verbose=0): obs = sys.getobjects(0) type2count = {} type2all = {} for o in obs: all = sys.getrefcount(o) t = type(o) if verbose: # if t == types.TupleType: if isinstance(o, Group): # if isinstance(o, MetaIsDescription): print("-->", o, "refs:", all) refrs = gc.get_referrers(o) trefrs = [] for refr in refrs: trefrs.append(type(refr)) print("Referrers -->", refrs) print("Referrers types -->", trefrs) # if t == types.StringType: print "-->",o if t in type2count: type2count[t] += 1 type2all[t] += all else: type2count[t] = 1 type2all[t] = all ct = sorted([(type2count[t] - self.type2count.get(t, 0), type2all[t] - self.type2all.get(t, 0), t) for t in type2count.keys()]) ct.reverse() for delta1, delta2, t in ct: if delta1 or delta2: print("%-55s %8d %8d" % (t, delta1, delta2)) self.type2count = type2count self.type2all = type2all def dump_refs(preheat=10, iter1=10, iter2=10, *testargs): rc1 = rc2 = None # testMethod() for i in range(preheat): testMethod(*testargs) gc.collect() rc1 = sys.gettotalrefcount() track = TrackRefs() for i in range(iter1): testMethod(*testargs) print("First output of TrackRefs:") gc.collect() rc2 = sys.gettotalrefcount() track.update() print("Inc refs in function testMethod --> %5d" % (rc2 - rc1), file=sys.stderr) for i in range(iter2): testMethod(*testargs) track.update(verbose=1) print("Second output of TrackRefs:") gc.collect() rc3 = sys.gettotalrefcount() print("Inc refs in function testMethod --> %5d" % (rc3 - rc2), file=sys.stderr) def dump_garbage(): """show us waht the garbage is about.""" # Force collection print("\nGARBAGE:") gc.collect() print("\nGARBAGE OBJECTS:") for x in gc.garbage: s = str(x) #if len(s) > 80: s = s[:77] + "..." print(type(x), "\n ", s) # print "\nTRACKED OBJECTS:" # reportLoggedInstances("*") def testMethod(file, usearray, testwrite, testread, complib, complevel, ngroups, ntables, nrows): if complevel > 0: print("Compression library:", complib) if testwrite: t1 = time.time() cpu1 = time.clock() if usearray: (rowsw, rowsz) = createFileArr(file, ngroups, ntables, nrows) else: (rowsw, rowsz) = createFile(file, ngroups, ntables, nrows, complevel, complib, recsize) t2 = time.time() cpu2 = time.clock() tapprows = round(t2 - t1, 3) cpuapprows = round(cpu2 - cpu1, 3) tpercent = int(round(cpuapprows / tapprows, 2) * 100) print("Rows written:", rowsw, " Row size:", rowsz) print("Time writing rows: %s s (real) %s s (cpu) %s%%" % (tapprows, cpuapprows, tpercent)) print("Write rows/sec: ", int(rowsw / float(tapprows))) print("Write KB/s :", int(rowsw * rowsz / (tapprows * 1024))) if testread: t1 = time.time() cpu1 = time.clock() if usearray: (rowsr, rowsz, bufsz) = readFileArr(file, ngroups, recsize, verbose) else: (rowsr, rowsz, bufsz) = readFile(file, ngroups, recsize, verbose) t2 = time.time() cpu2 = time.clock() treadrows = round(t2 - t1, 3) cpureadrows = round(cpu2 - cpu1, 3) tpercent = int(round(cpureadrows / treadrows, 2) * 100) print("Rows read:", rowsr, " Row size:", rowsz, "Buf size:", bufsz) print("Time reading rows: %s s (real) %s s (cpu) %s%%" % (treadrows, cpureadrows, tpercent)) print("Read rows/sec: ", int(rowsr / float(treadrows))) print("Read KB/s :", int(rowsr * rowsz / (treadrows * 1024))) if __name__ == "__main__": import getopt import profile try: import psyco psyco_imported = 1 except: psyco_imported = 0 usage = """usage: %s [-d debug] [-v level] [-p] [-r] [-w] [-l complib] [-c complevel] [-g ngroups] [-t ntables] [-i nrows] file -d debugging level -v verbosity level -p use "psyco" if available -a use Array objects instead of Table -r only read test -w only write test -l sets the compression library to be used ("zlib", "lzo", "ucl", "bzip2") -c sets a compression level (do not set it or 0 for no compression) -g number of groups hanging from "/" -t number of tables per group -i number of rows per table """ try: opts, pargs = getopt.getopt(sys.argv[1:], 'd:v:parwl:c:g:t:i:') except: sys.stderr.write(usage) sys.exit(0) # if we pass too much parameters, abort if len(pargs) != 1: sys.stderr.write(usage) sys.exit(0) # default options ngroups = 5 ntables = 5 nrows = 100 verbose = 0 debug = 0 recsize = "medium" testread = 1 testwrite = 1 usepsyco = 0 usearray = 0 complevel = 0 complib = "zlib" # Get the options for option in opts: if option[0] == '-d': debug = int(option[1]) if option[0] == '-v': verbose = int(option[1]) if option[0] == '-p': usepsyco = 1 if option[0] == '-a': usearray = 1 elif option[0] == '-r': testwrite = 0 elif option[0] == '-w': testread = 0 elif option[0] == '-l': complib = option[1] elif option[0] == '-c': complevel = int(option[1]) elif option[0] == '-g': ngroups = int(option[1]) elif option[0] == '-t': ntables = int(option[1]) elif option[0] == '-i': nrows = int(option[1]) if debug: gc.enable() if debug == 1: gc.set_debug(gc.DEBUG_LEAK) # Catch the hdf5 file passed as the last argument file = pargs[0] if psyco_imported and usepsyco: psyco.bind(createFile) psyco.bind(readFile) if debug == 2: dump_refs(10, 10, 15, file, usearray, testwrite, testread, complib, complevel, ngroups, ntables, nrows) else: # testMethod(file, usearray, testwrite, testread, complib, complevel, # ngroups, ntables, nrows) profile.run("testMethod(file, usearray, testwrite, testread, " + "complib, complevel, ngroups, ntables, nrows)") # Show the dirt if debug == 1: dump_garbage() PyTables-v.3.1.1/bench/stress-test2.py000066400000000000000000000164731231437614300175350ustar00rootroot00000000000000from __future__ import print_function import gc import sys import time import random from tables import * class Test(IsDescription): ngroup = Int32Col(pos=1) ntable = Int32Col(pos=2) nrow = Int32Col(pos=3) time = Float64Col(pos=5) random = Float32Col(pos=4) def createFile(filename, ngroups, ntables, nrows, complevel, complib, recsize): # First, create the groups # Open a file in "w"rite mode fileh = open_file(filename, mode="w", title="PyTables Stress Test") for k in range(ngroups): # Create the group group = fileh.create_group("/", 'group%04d' % k, "Group %d" % k) fileh.close() # Now, create the tables rowswritten = 0 for k in range(ngroups): fileh = open_file(filename, mode="a", root_uep='group%04d' % k) # Get the group group = fileh.root for j in range(ntables): # Create a table table = fileh.create_table(group, 'table%04d' % j, Test, 'Table%04d' % j, complevel, complib, nrows) # Get the row object associated with the new table row = table.row # Fill the table for i in range(nrows): row['time'] = time.time() row['random'] = random.random() * 40 + 100 row['ngroup'] = k row['ntable'] = j row['nrow'] = i row.append() rowswritten += nrows table.flush() # Close the file fileh.close() return (rowswritten, table.rowsize) def readFile(filename, ngroups, recsize, verbose): # Open the HDF5 file in read-only mode rowsread = 0 for ngroup in range(ngroups): fileh = open_file(filename, mode="r", root_uep='group%04d' % ngroup) # Get the group group = fileh.root ntable = 0 if verbose: print("Group ==>", group) for table in fileh.list_nodes(group, 'Table'): rowsize = table.rowsize buffersize = table.rowsize * table.nrowsinbuf if verbose > 1: print("Table ==>", table) print("Max rows in buf:", table.nrowsinbuf) print("Rows in", table._v_pathname, ":", table.nrows) print("Buffersize:", table.rowsize * table.nrowsinbuf) print("MaxTuples:", table.nrowsinbuf) nrow = 0 time_1 = 0.0 for row in table: try: # print "row['ngroup'], ngroup ==>", row["ngroup"], ngroup assert row["ngroup"] == ngroup assert row["ntable"] == ntable assert row["nrow"] == nrow # print "row['time'], time_1 ==>", row["time"], time_1 assert row["time"] >= (time_1 - 0.01) #assert 100 <= row["random"] <= 139.999 assert 100 <= row["random"] <= 140 except: print("Error in group: %d, table: %d, row: %d" % (ngroup, ntable, nrow)) print("Record ==>", row) time_1 = row["time"] nrow += 1 assert nrow == table.nrows rowsread += table.nrows ntable += 1 # Close the file (eventually destroy the extended type) fileh.close() return (rowsread, rowsize, buffersize) def dump_garbage(): """show us waht the garbage is about.""" # Force collection print("\nGARBAGE:") gc.collect() print("\nGARBAGE OBJECTS:") for x in gc.garbage: s = str(x) #if len(s) > 80: s = s[:77] + "..." print(type(x), "\n ", s) if __name__ == "__main__": import getopt try: import psyco psyco_imported = 1 except: psyco_imported = 0 usage = """usage: %s [-d debug] [-v level] [-p] [-r] [-w] [-l complib] [-c complevel] [-g ngroups] [-t ntables] [-i nrows] file -d debugging level -v verbosity level -p use "psyco" if available -r only read test -w only write test -l sets the compression library to be used ("zlib", "lzo", "ucl", "bzip2") -c sets a compression level (do not set it or 0 for no compression) -g number of groups hanging from "/" -t number of tables per group -i number of rows per table """ try: opts, pargs = getopt.getopt(sys.argv[1:], 'd:v:prwl:c:g:t:i:') except: sys.stderr.write(usage) sys.exit(0) # if we pass too much parameters, abort if len(pargs) != 1: sys.stderr.write(usage) sys.exit(0) # default options ngroups = 5 ntables = 5 nrows = 100 verbose = 0 debug = 0 recsize = "medium" testread = 1 testwrite = 1 usepsyco = 0 complevel = 0 complib = "zlib" # Get the options for option in opts: if option[0] == '-d': debug = int(option[1]) if option[0] == '-v': verbose = int(option[1]) if option[0] == '-p': usepsyco = 1 elif option[0] == '-r': testwrite = 0 elif option[0] == '-w': testread = 0 elif option[0] == '-l': complib = option[1] elif option[0] == '-c': complevel = int(option[1]) elif option[0] == '-g': ngroups = int(option[1]) elif option[0] == '-t': ntables = int(option[1]) elif option[0] == '-i': nrows = int(option[1]) if debug: gc.enable() gc.set_debug(gc.DEBUG_LEAK) # Catch the hdf5 file passed as the last argument file = pargs[0] print("Compression level:", complevel) if complevel > 0: print("Compression library:", complib) if testwrite: t1 = time.time() cpu1 = time.clock() if psyco_imported and usepsyco: psyco.bind(createFile) (rowsw, rowsz) = createFile(file, ngroups, ntables, nrows, complevel, complib, recsize) t2 = time.time() cpu2 = time.clock() tapprows = round(t2 - t1, 3) cpuapprows = round(cpu2 - cpu1, 3) tpercent = int(round(cpuapprows / tapprows, 2) * 100) print("Rows written:", rowsw, " Row size:", rowsz) print("Time writing rows: %s s (real) %s s (cpu) %s%%" % (tapprows, cpuapprows, tpercent)) print("Write rows/sec: ", int(rowsw / float(tapprows))) print("Write KB/s :", int(rowsw * rowsz / (tapprows * 1024))) if testread: t1 = time.time() cpu1 = time.clock() if psyco_imported and usepsyco: psyco.bind(readFile) (rowsr, rowsz, bufsz) = readFile(file, ngroups, recsize, verbose) t2 = time.time() cpu2 = time.clock() treadrows = round(t2 - t1, 3) cpureadrows = round(cpu2 - cpu1, 3) tpercent = int(round(cpureadrows / treadrows, 2) * 100) print("Rows read:", rowsr, " Row size:", rowsz, "Buf size:", bufsz) print("Time reading rows: %s s (real) %s s (cpu) %s%%" % (treadrows, cpureadrows, tpercent)) print("Read rows/sec: ", int(rowsr / float(treadrows))) print("Read KB/s :", int(rowsr * rowsz / (treadrows * 1024))) # Show the dirt if debug > 1: dump_garbage() PyTables-v.3.1.1/bench/stress-test3.py000066400000000000000000000207701231437614300175310ustar00rootroot00000000000000#!/usr/bin/env python """This script allows to create arbitrarily large files with the desired combination of groups, tables per group and rows per table. Issue "python stress-test3.py" without parameters for a help on usage. """ from __future__ import print_function import gc import sys import time from tables import * class Test(IsDescription): ngroup = Int32Col(pos=1) ntable = Int32Col(pos=2) nrow = Int32Col(pos=3) string = StringCol(500, pos=4) def createFileArr(filename, ngroups, ntables, nrows): # First, create the groups # Open a file in "w"rite mode fileh = open_file(filename, mode="w", title="PyTables Stress Test") for k in range(ngroups): # Create the group fileh.create_group("/", 'group%04d' % k, "Group %d" % k) fileh.close() return (0, 4) def readFileArr(filename, ngroups, recsize, verbose): rowsread = 0 for ngroup in range(ngroups): fileh = open_file(filename, mode="r", root_uep='group%04d' % ngroup) # Get the group group = fileh.root ntable = 0 if verbose: print("Group ==>", group) for table in fileh.list_nodes(group, 'Array'): if verbose > 1: print("Array ==>", table) print("Rows in", table._v_pathname, ":", table.shape) arr = table.read() rowsread += len(arr) ntable += 1 # Close the file (eventually destroy the extended type) fileh.close() return (rowsread, 4, 0) def createFile(filename, ngroups, ntables, nrows, complevel, complib, recsize): # First, create the groups # Open a file in "w"rite mode fileh = open_file(filename, mode="w", title="PyTables Stress Test") for k in range(ngroups): # Create the group group = fileh.create_group("/", 'group%04d' % k, "Group %d" % k) fileh.close() # Now, create the tables rowswritten = 0 for k in range(ngroups): fileh = open_file(filename, mode="a", root_uep='group%04d' % k) # Get the group group = fileh.root for j in range(ntables): # Create a table table = fileh.create_table(group, 'table%04d' % j, Test, 'Table%04d' % j, Filters(complevel, complib), nrows) rowsize = table.rowsize # Get the row object associated with the new table row = table.row # Fill the table for i in range(nrows): row['ngroup'] = k row['ntable'] = j row['nrow'] = i row.append() rowswritten += nrows table.flush() # Close the file fileh.close() return (rowswritten, rowsize) def readFile(filename, ngroups, recsize, verbose): # Open the HDF5 file in read-only mode rowsread = 0 for ngroup in range(ngroups): fileh = open_file(filename, mode="r", root_uep='group%04d' % ngroup) # Get the group group = fileh.root ntable = 0 if verbose: print("Group ==>", group) for table in fileh.list_nodes(group, 'Table'): rowsize = table.rowsize buffersize = table.rowsize * table.nrowsinbuf if verbose > 1: print("Table ==>", table) print("Max rows in buf:", table.nrowsinbuf) print("Rows in", table._v_pathname, ":", table.nrows) print("Buffersize:", table.rowsize * table.nrowsinbuf) print("MaxTuples:", table.nrowsinbuf) nrow = 0 for row in table: try: assert row["ngroup"] == ngroup assert row["ntable"] == ntable assert row["nrow"] == nrow except: print("Error in group: %d, table: %d, row: %d" % (ngroup, ntable, nrow)) print("Record ==>", row) nrow += 1 assert nrow == table.nrows rowsread += table.nrows ntable += 1 # Close the file (eventually destroy the extended type) fileh.close() return (rowsread, rowsize, buffersize) def dump_garbage(): """show us waht the garbage is about.""" # Force collection print("\nGARBAGE:") gc.collect() print("\nGARBAGE OBJECTS:") for x in gc.garbage: s = str(x) #if len(s) > 80: s = s[:77] + "..." print(type(x), "\n ", s) if __name__ == "__main__": import getopt try: import psyco psyco_imported = 1 except: psyco_imported = 0 usage = """usage: %s [-d debug] [-v level] [-p] [-r] [-w] [-l complib] [-c complevel] [-g ngroups] [-t ntables] [-i nrows] file -d debugging level -v verbosity level -p use "psyco" if available -a use Array objects instead of Table -r only read test -w only write test -l sets the compression library to be used ("zlib", "lzo", "ucl", "bzip2") -c sets a compression level (do not set it or 0 for no compression) -g number of groups hanging from "/" -t number of tables per group -i number of rows per table """ try: opts, pargs = getopt.getopt(sys.argv[1:], 'd:v:parwl:c:g:t:i:') except: sys.stderr.write(usage) sys.exit(0) # if we pass too much parameters, abort if len(pargs) != 1: sys.stderr.write(usage) sys.exit(0) # default options ngroups = 5 ntables = 5 nrows = 100 verbose = 0 debug = 0 recsize = "medium" testread = 1 testwrite = 1 usepsyco = 0 usearray = 0 complevel = 0 complib = "zlib" # Get the options for option in opts: if option[0] == '-d': debug = int(option[1]) if option[0] == '-v': verbose = int(option[1]) if option[0] == '-p': usepsyco = 1 if option[0] == '-a': usearray = 1 elif option[0] == '-r': testwrite = 0 elif option[0] == '-w': testread = 0 elif option[0] == '-l': complib = option[1] elif option[0] == '-c': complevel = int(option[1]) elif option[0] == '-g': ngroups = int(option[1]) elif option[0] == '-t': ntables = int(option[1]) elif option[0] == '-i': nrows = int(option[1]) if debug: gc.enable() gc.set_debug(gc.DEBUG_LEAK) # Catch the hdf5 file passed as the last argument file = pargs[0] print("Compression level:", complevel) if complevel > 0: print("Compression library:", complib) if testwrite: t1 = time.time() cpu1 = time.clock() if psyco_imported and usepsyco: psyco.bind(createFile) if usearray: (rowsw, rowsz) = createFileArr(file, ngroups, ntables, nrows) else: (rowsw, rowsz) = createFile(file, ngroups, ntables, nrows, complevel, complib, recsize) t2 = time.time() cpu2 = time.clock() tapprows = round(t2 - t1, 3) cpuapprows = round(cpu2 - cpu1, 3) tpercent = int(round(cpuapprows / tapprows, 2) * 100) print("Rows written:", rowsw, " Row size:", rowsz) print("Time writing rows: %s s (real) %s s (cpu) %s%%" % (tapprows, cpuapprows, tpercent)) print("Write rows/sec: ", int(rowsw / float(tapprows))) print("Write KB/s :", int(rowsw * rowsz / (tapprows * 1024))) if testread: t1 = time.time() cpu1 = time.clock() if psyco_imported and usepsyco: psyco.bind(readFile) if usearray: (rowsr, rowsz, bufsz) = readFileArr(file, ngroups, recsize, verbose) else: (rowsr, rowsz, bufsz) = readFile(file, ngroups, recsize, verbose) t2 = time.time() cpu2 = time.clock() treadrows = round(t2 - t1, 3) cpureadrows = round(cpu2 - cpu1, 3) tpercent = int(round(cpureadrows / treadrows, 2) * 100) print("Rows read:", rowsr, " Row size:", rowsz, "Buf size:", bufsz) print("Time reading rows: %s s (real) %s s (cpu) %s%%" % (treadrows, cpureadrows, tpercent)) print("Read rows/sec: ", int(rowsr / float(treadrows))) print("Read KB/s :", int(rowsr * rowsz / (treadrows * 1024))) # Show the dirt if debug > 1: dump_garbage() PyTables-v.3.1.1/bench/table-bench.py000066400000000000000000000374471231437614300173230ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import numpy as NP from tables import * # This class is accessible only for the examples class Small(IsDescription): var1 = StringCol(itemsize=4, pos=2) var2 = Int32Col(pos=1) var3 = Float64Col(pos=0) # Define a user record to characterize some kind of particles class Medium(IsDescription): name = StringCol(itemsize=16, pos=0) # 16-character String float1 = Float64Col(shape=2, dflt=NP.arange(2), pos=1) #float1 = Float64Col(dflt=2.3) #float2 = Float64Col(dflt=2.3) # zADCcount = Int16Col() # signed short integer ADCcount = Int32Col(pos=6) # signed short integer grid_i = Int32Col(pos=7) # integer grid_j = Int32Col(pos=8) # integer pressure = Float32Col(pos=9) # float (single-precision) energy = Float64Col(pos=2) # double (double-precision) # unalig = Int8Col() # just to unalign data # Define a user record to characterize some kind of particles class Big(IsDescription): name = StringCol(itemsize=16) # 16-character String float1 = Float64Col(shape=32, dflt=NP.arange(32)) float2 = Float64Col(shape=32, dflt=2.2) TDCcount = Int8Col() # signed short integer #ADCcount = Int32Col() # ADCcount = Int16Col() # signed short integer grid_i = Int32Col() # integer grid_j = Int32Col() # integer pressure = Float32Col() # float (single-precision) energy = Float64Col() # double (double-precision) def createFile(filename, totalrows, filters, recsize): # Open a file in "w"rite mode fileh = open_file(filename, mode="w", title="Table Benchmark", filters=filters) # Table title title = "This is the table title" # Create a Table instance group = fileh.root rowswritten = 0 for j in range(3): # Create a table if recsize == "big": table = fileh.create_table(group, 'tuple' + str(j), Big, title, None, totalrows) elif recsize == "medium": table = fileh.create_table(group, 'tuple' + str(j), Medium, title, None, totalrows) elif recsize == "small": table = fileh.create_table(group, 'tuple' + str(j), Small, title, None, totalrows) else: raise RuntimeError("This should never happen") table.attrs.test = 2 rowsize = table.rowsize # Get the row object associated with the new table d = table.row # Fill the table if recsize == "big": for i in range(totalrows): # d['name'] = 'Part: %6d' % (i) d['TDCcount'] = i % 256 #d['float1'] = NP.array([i]*32, NP.float64) #d['float2'] = NP.array([i**2]*32, NP.float64) #d['float1'][0] = float(i) #d['float2'][0] = float(i*2) # Common part with medium d['grid_i'] = i d['grid_j'] = 10 - i d['pressure'] = float(i * i) # d['energy'] = float(d['pressure'] ** 4) d['energy'] = d['pressure'] # d['idnumber'] = i * (2 ** 34) d.append() elif recsize == "medium": for i in range(totalrows): #d['name'] = 'Part: %6d' % (i) #d['float1'] = NP.array([i]*2, NP.float64) #d['float1'] = arr #d['float1'] = i #d['float2'] = float(i) # Common part with big: d['grid_i'] = i d['grid_j'] = 10 - i d['pressure'] = i * 2 # d['energy'] = float(d['pressure'] ** 4) d['energy'] = d['pressure'] d.append() else: # Small record for i in range(totalrows): #d['var1'] = str(random.randrange(1000000)) #d['var3'] = random.randrange(10000000) d['var1'] = str(i) #d['var2'] = random.randrange(totalrows) d['var2'] = i #d['var3'] = 12.1e10 d['var3'] = totalrows - i d.append() # This is a 10% faster than table.append() rowswritten += totalrows if recsize == "small": # Testing with indexing pass # table._createIndex("var3", Filters(1,"zlib",shuffle=1)) # table.flush() group._v_attrs.test2 = "just a test" # Create a new group group2 = fileh.create_group(group, 'group' + str(j)) # Iterate over this new group (group2) group = group2 table.flush() # Close the file (eventually destroy the extended type) fileh.close() return (rowswritten, rowsize) def readFile(filename, recsize, verbose): # Open the HDF5 file in read-only mode fileh = open_file(filename, mode="r") rowsread = 0 for groupobj in fileh.walk_groups(fileh.root): # print "Group pathname:", groupobj._v_pathname row = 0 for table in fileh.list_nodes(groupobj, 'Table'): rowsize = table.rowsize print("reading", table) if verbose: print("Max rows in buf:", table.nrowsinbuf) print("Rows in", table._v_pathname, ":", table.nrows) print("Buffersize:", table.rowsize * table.nrowsinbuf) print("MaxTuples:", table.nrowsinbuf) if recsize == "big" or recsize == "medium": # e = [ p.float1 for p in table.iterrows() # if p.grid_i < 2 ] #e = [ str(p) for p in table.iterrows() ] # if p.grid_i < 2 ] # e = [ p['grid_i'] for p in table.iterrows() # if p['grid_j'] == 20 and p['grid_i'] < 20 ] # e = [ p['grid_i'] for p in table # if p['grid_i'] <= 2 ] # e = [ p['grid_i'] for p in table.where("grid_i<=20")] # e = [ p['grid_i'] for p in # table.where('grid_i <= 20')] e = [p['grid_i'] for p in table.where('(grid_i <= 20) & (grid_j == 20)')] # e = [ p['grid_i'] for p in table.iterrows() # if p.nrow() == 20 ] # e = [ table.delrow(p.nrow()) for p in table.iterrows() # if p.nrow() == 20 ] # The version with a for loop is only 1% better than # comprenhension list #e = [] # for p in table.iterrows(): # if p.grid_i < 20: # e.append(p.grid_j) else: # small record case # e = [ p['var3'] for p in table.iterrows() # if p['var2'] < 20 and p['var3'] < 20 ] # e = [ p['var3'] for p in table.where("var3 <= 20") # if p['var2'] < 20 ] # e = [ p['var3'] for p in table.where("var3 <= 20")] # Cuts 1) and 2) issues the same results but 2) is about 10 times faster # Cut 1) # e = [ p.nrow() for p in # table.where(table.cols.var2 > 5) # if p["var2"] < 10] # Cut 2) # e = [ p.nrow() for p in # table.where(table.cols.var2 < 10) # if p["var2"] > 5] # e = [ (p._nrow,p["var3"]) for p in # e = [ p["var3"] for p in # table.where(table.cols.var3 < 10)] # table.where(table.cols.var3 < 10)] # table if p["var3"] <= 10] # e = [ p['var3'] for p in table.where("var3 <= 20")] # e = [ p['var3'] for p in # table.where(table.cols.var1 == "10")] # More # than ten times faster than the next one # e = [ p['var3'] for p in table # if p['var1'] == "10"] # e = [ p['var3'] for p in table.where('var2 <= 20')] e = [p['var3'] for p in table.where('(var2 <= 20) & (var2 >= 3)')] # e = [ p[0] for p in table.where('var2 <= 20')] #e = [ p['var3'] for p in table if p['var2'] <= 20 ] # e = [ p[:] for p in table if p[1] <= 20 ] # e = [ p['var3'] for p in table._whereInRange(table.cols.var2 <=20)] #e = [ p['var3'] for p in table.iterrows(0,21) ] # e = [ p['var3'] for p in table.iterrows() # if p.nrow() <= 20 ] #e = [ p['var3'] for p in table.iterrows(1,0,1000)] #e = [ p['var3'] for p in table.iterrows(1,100)] # e = [ p['var3'] for p in table.iterrows(step=2) # if p.nrow() < 20 ] # e = [ p['var2'] for p in table.iterrows() # if p['var2'] < 20 ] # for p in table.iterrows(): # pass if verbose: # print "Last record read:", p print("resulting selection list ==>", e) rowsread += table.nrows row += 1 if verbose: print("Total selected records ==> ", len(e)) # Close the file (eventually destroy the extended type) fileh.close() return (rowsread, rowsize) def readField(filename, field, rng, verbose): fileh = open_file(filename, mode="r") rowsread = 0 if rng is None: rng = [0, -1, 1] if field == "all": field = None for groupobj in fileh.walk_groups(fileh.root): for table in fileh.list_nodes(groupobj, 'Table'): rowsize = table.rowsize # table.nrowsinbuf = 3 # For testing purposes if verbose: print("Max rows in buf:", table.nrowsinbuf) print("Rows in", table._v_pathname, ":", table.nrows) print("Buffersize:", table.rowsize * table.nrowsinbuf) print("MaxTuples:", table.nrowsinbuf) print("(field, start, stop, step) ==>", (field, rng[0], rng[1], rng[2])) e = table.read(rng[0], rng[1], rng[2], field) rowsread += table.nrows if verbose: print("Selected rows ==> ", e) print("Total selected rows ==> ", len(e)) # Close the file (eventually destroy the extended type) fileh.close() return (rowsread, rowsize) if __name__ == "__main__": import sys import getopt try: import psyco psyco_imported = 1 except: psyco_imported = 0 import time usage = """usage: %s [-v] [-p] [-P] [-R range] [-r] [-w] [-s recsize] [-f field] [-c level] [-l complib] [-i iterations] [-S] [-F] file -v verbose -p use "psyco" if available -P do profile -R select a range in a field in the form "start,stop,step" -r only read test -w only write test -s use [big] record, [medium] or [small] -f only read stated field name in tables ("all" means all fields) -c sets a compression level (do not set it or 0 for no compression) -S activate shuffling filter -F activate fletcher32 filter -l sets the compression library to be used ("zlib", "lzo", "blosc", "bzip2") -i sets the number of rows in each table\n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'vpPSFR:rwf:s:c:l:i:') except: sys.stderr.write(usage) sys.exit(0) # if we pass too much parameters, abort if len(pargs) != 1: sys.stderr.write(usage) sys.exit(0) # default options verbose = 0 profile = 0 rng = None recsize = "medium" fieldName = None testread = 1 testwrite = 1 usepsyco = 0 complevel = 0 shuffle = 0 fletcher32 = 0 complib = "zlib" iterations = 100 # Get the options for option in opts: if option[0] == '-v': verbose = 1 if option[0] == '-p': usepsyco = 1 if option[0] == '-P': profile = 1 if option[0] == '-S': shuffle = 1 if option[0] == '-F': fletcher32 = 1 elif option[0] == '-R': rng = [int(i) for i in option[1].split(",")] elif option[0] == '-r': testwrite = 0 elif option[0] == '-w': testread = 0 elif option[0] == '-f': fieldName = option[1] elif option[0] == '-s': recsize = option[1] if recsize not in ["big", "medium", "small"]: sys.stderr.write(usage) sys.exit(0) elif option[0] == '-c': complevel = int(option[1]) elif option[0] == '-l': complib = option[1] elif option[0] == '-i': iterations = int(option[1]) # Build the Filters instance filters = Filters(complevel=complevel, complib=complib, shuffle=shuffle, fletcher32=fletcher32) # Catch the hdf5 file passed as the last argument file = pargs[0] if verbose: print("numpy version:", NP.__version__) if psyco_imported and usepsyco: print("Using psyco version:", psyco.version_info) if testwrite: print("Compression level:", complevel) if complevel > 0: print("Compression library:", complib) if shuffle: print("Suffling...") t1 = time.time() cpu1 = time.clock() if psyco_imported and usepsyco: psyco.bind(createFile) if profile: import profile as prof import pstats prof.run( '(rowsw, rowsz) = createFile(file, iterations, filters, ' 'recsize)', 'table-bench.prof') stats = pstats.Stats('table-bench.prof') stats.strip_dirs() stats.sort_stats('time', 'calls') stats.print_stats(20) else: (rowsw, rowsz) = createFile(file, iterations, filters, recsize) t2 = time.time() cpu2 = time.clock() tapprows = round(t2 - t1, 3) cpuapprows = round(cpu2 - cpu1, 3) tpercent = int(round(cpuapprows / tapprows, 2) * 100) print("Rows written:", rowsw, " Row size:", rowsz) print("Time writing rows: %s s (real) %s s (cpu) %s%%" % (tapprows, cpuapprows, tpercent)) print("Write rows/sec: ", int(rowsw / float(tapprows))) print("Write KB/s :", int(rowsw * rowsz / (tapprows * 1024))) if testread: t1 = time.time() cpu1 = time.clock() if psyco_imported and usepsyco: psyco.bind(readFile) # psyco.bind(readField) pass if rng or fieldName: (rowsr, rowsz) = readField(file, fieldName, rng, verbose) pass else: for i in range(1): (rowsr, rowsz) = readFile(file, recsize, verbose) t2 = time.time() cpu2 = time.clock() treadrows = round(t2 - t1, 3) cpureadrows = round(cpu2 - cpu1, 3) tpercent = int(round(cpureadrows / treadrows, 2) * 100) print("Rows read:", rowsr, " Row size:", rowsz) print("Time reading rows: %s s (real) %s s (cpu) %s%%" % (treadrows, cpureadrows, tpercent)) print("Read rows/sec: ", int(rowsr / float(treadrows))) print("Read KB/s :", int(rowsr * rowsz / (treadrows * 1024))) PyTables-v.3.1.1/bench/table-copy.py000066400000000000000000000070211231437614300171770ustar00rootroot00000000000000from __future__ import print_function import time import numpy as np import tables N = 144000 #N = 144 def timed(func, *args, **kwargs): start = time.time() res = func(*args, **kwargs) print("%fs elapsed." % (time.time() - start)) return res def create_table(output_path): print("creating array...", end=' ') dt = np.dtype([('field%d' % i, int) for i in range(320)]) a = np.zeros(N, dtype=dt) print("done.") output_file = tables.open_file(output_path, mode="w") table = output_file.create_table("/", "test", dt) # , filters=blosc4) print("appending data...", end=' ') table.append(a) print("flushing...", end=' ') table.flush() print("done.") output_file.close() def copy1(input_path, output_path): print("copying data from %s to %s..." % (input_path, output_path)) input_file = tables.open_file(input_path, mode="r") output_file = tables.open_file(output_path, mode="w") # copy nodes as a batch input_file.copy_node("/", output_file.root, recursive=True) output_file.close() input_file.close() def copy2(input_path, output_path): print("copying data from %s to %s..." % (input_path, output_path)) input_file = tables.open_file(input_path, mode="r") input_file.copy_file(output_path, overwrite=True) input_file.close() def copy3(input_path, output_path): print("copying data from %s to %s..." % (input_path, output_path)) input_file = tables.open_file(input_path, mode="r") output_file = tables.open_file(output_path, mode="w") table = input_file.root.test table.copy(output_file.root) output_file.close() input_file.close() def copy4(input_path, output_path, complib='zlib', complevel=0): print("copying data from %s to %s..." % (input_path, output_path)) input_file = tables.open_file(input_path, mode="r") output_file = tables.open_file(output_path, mode="w") input_table = input_file.root.test print("reading data...", end=' ') data = input_file.root.test.read() print("done.") filter = tables.Filters(complevel=complevel, complib=complib) output_table = output_file.create_table("/", "test", input_table.dtype, filters=filter) print("appending data...", end=' ') output_table.append(data) print("flushing...", end=' ') output_table.flush() print("done.") input_file.close() output_file.close() def copy5(input_path, output_path, complib='zlib', complevel=0): print("copying data from %s to %s..." % (input_path, output_path)) input_file = tables.open_file(input_path, mode="r") output_file = tables.open_file(output_path, mode="w") input_table = input_file.root.test filter = tables.Filters(complevel=complevel, complib=complib) output_table = output_file.create_table("/", "test", input_table.dtype, filters=filter) chunksize = 10000 rowsleft = len(input_table) start = 0 for chunk in range((len(input_table) / chunksize) + 1): stop = start + min(chunksize, rowsleft) data = input_table.read(start, stop) output_table.append(data) output_table.flush() rowsleft -= chunksize start = stop input_file.close() output_file.close() if __name__ == '__main__': timed(create_table, 'tmp.h5') # timed(copy1, 'tmp.h5', 'test1.h5') timed(copy2, 'tmp.h5', 'test2.h5') # timed(copy3, 'tmp.h5', 'test3.h5') timed(copy4, 'tmp.h5', 'test4.h5') timed(copy5, 'tmp.h5', 'test5.h5') PyTables-v.3.1.1/bench/undo_redo.py000066400000000000000000000157241231437614300171270ustar00rootroot00000000000000########################################################################### # Benchmark for undo/redo. Run this program without parameters # for mode of use. # # Francesc Alted # 2005-03-09 ########################################################################### from __future__ import print_function import numpy from time import time import tables verbose = 0 class BasicBenchmark(object): def __init__(self, filename, testname, vecsize, nobjects, niter): self.file = filename self.test = testname self.vecsize = vecsize self.nobjects = nobjects self.niter = niter # Initialize the arrays self.a1 = numpy.arange(0, 1 * self.vecsize) self.a2 = numpy.arange(1 * self.vecsize, 2 * self.vecsize) self.a3 = numpy.arange(2 * self.vecsize, 3 * self.vecsize) def setUp(self): # Create an HDF5 file self.fileh = tables.open_file(self.file, mode="w") # open the do/undo self.fileh.enable_undo() def tearDown(self): self.fileh.disable_undo() self.fileh.close() # Remove the temporary file # os.remove(self.file) def createNode(self): """Checking a undo/redo create_array.""" for i in range(self.nobjects): # Create a new array self.fileh.create_array('/', 'array' + str(i), self.a1) # Put a mark self.fileh.mark() # Unwind all marks sequentially for i in range(self.niter): t1 = time() for i in range(self.nobjects): self.fileh.undo() if verbose: print("u", end=' ') if verbose: print() undo = time() - t1 # Rewind all marks sequentially t1 = time() for i in range(self.nobjects): self.fileh.redo() if verbose: print("r", end=' ') if verbose: print() redo = time() - t1 print("Time for Undo, Redo (createNode):", undo, "s, ", redo, "s") def copy_children(self): """Checking a undo/redo copy_children.""" # Create a group self.fileh.create_group('/', 'agroup') # Create several objects there for i in range(10): # Create a new array self.fileh.create_array('/agroup', 'array' + str(i), self.a1) # Excercise copy_children for i in range(self.nobjects): # Create another group for destination self.fileh.create_group('/', 'anothergroup' + str(i)) # Copy children from /agroup to /anothergroup+i self.fileh.copy_children('/agroup', '/anothergroup' + str(i)) # Put a mark self.fileh.mark() # Unwind all marks sequentially for i in range(self.niter): t1 = time() for i in range(self.nobjects): self.fileh.undo() if verbose: print("u", end=' ') if verbose: print() undo = time() - t1 # Rewind all marks sequentially t1 = time() for i in range(self.nobjects): self.fileh.redo() if verbose: print("r", end=' ') if verbose: print() redo = time() - t1 print(("Time for Undo, Redo (copy_children):", undo, "s, ", redo, "s")) def set_attr(self): """Checking a undo/redo for setting attributes.""" # Create a new array self.fileh.create_array('/', 'array', self.a1) for i in range(self.nobjects): # Set an attribute setattr(self.fileh.root.array.attrs, "attr" + str(i), str(self.a1)) # Put a mark self.fileh.mark() # Unwind all marks sequentially for i in range(self.niter): t1 = time() for i in range(self.nobjects): self.fileh.undo() if verbose: print("u", end=' ') if verbose: print() undo = time() - t1 # Rewind all marks sequentially t1 = time() for i in range(self.nobjects): self.fileh.redo() if verbose: print("r", end=' ') if verbose: print() redo = time() - t1 print("Time for Undo, Redo (set_attr):", undo, "s, ", redo, "s") def runall(self): if testname == "all": tests = [self.createNode, self.copy_children, self.set_attr] elif testname == "createNode": tests = [self.createNode] elif testname == "copy_children": tests = [self.copy_children] elif testname == "set_attr": tests = [self.set_attr] for meth in tests: self.setUp() meth() self.tearDown() if __name__ == '__main__': import sys import getopt usage = """usage: %s [-v] [-p] [-t test] [-s vecsize] [-n niter] datafile -v verbose (total dump of profiling) -p do profiling -t {createNode|copy_children|set_attr|all} run the specified test -s the size of vectors that are undone/redone -n number of objects in operations -i number of iterations for reading\n""" % sys.argv[0] try: opts, pargs = getopt.getopt(sys.argv[1:], 'vpt:s:n:i:') except: sys.stderr.write(usage) sys.exit(0) # if we pass too much parameters, abort if len(pargs) != 1: sys.stderr.write(usage) sys.exit(0) # default options verbose = 0 profile = 0 testname = "all" vecsize = 10 nobjects = 1 niter = 1 # Get the options for option in opts: if option[0] == '-v': verbose = 1 elif option[0] == '-p': profile = 1 elif option[0] == '-t': testname = option[1] if testname not in ['createNode', 'copy_children', 'set_attr', 'all']: sys.stderr.write(usage) sys.exit(0) elif option[0] == '-s': vecsize = int(option[1]) elif option[0] == '-n': nobjects = int(option[1]) elif option[0] == '-i': niter = int(option[1]) filename = pargs[0] bench = BasicBenchmark(filename, testname, vecsize, nobjects, niter) if profile: import hotshot import hotshot.stats prof = hotshot.Profile("do_undo.prof") prof.runcall(bench.runall) prof.close() stats = hotshot.stats.load("do_undo.prof") stats.strip_dirs() stats.sort_stats('time', 'calls') if verbose: stats.print_stats() else: stats.print_stats(20) else: bench.runall() # Local Variables: # mode: python # End: PyTables-v.3.1.1/bench/undo_redo.txt000066400000000000000000000104311231437614300173040ustar00rootroot00000000000000Benchmarks on PyTables Undo/Redo ================================ This is a small report for the performance of the Undo/Redo feature in PyTables. A small script (see undo_redo.py) has been made in order to check different scenarios for Undo/Redo, like creating single nodes, copying children from one group to another, and creating attributes. Undo/Redo is independent of object size --------------------------------------- Firstly, one thing to be noted is that the Undo/Redo feature is independent of the object size that is being treated. For example, the times for 10 objects (flag -n) each one with 10 elements (flag -s) is: $ time python2.4 undo_redo.py -n 10 -i 2 -s 10 data.nobackup/undo_redo.h5 Time for Undo, Redo (createNode): 0.213686943054 s, 0.0727670192719 s Time for Undo, Redo (createNode): 0.271666049957 s, 0.0740389823914 s Time for Undo, Redo (copy_children): 0.296227931976 s, 0.161941051483 s Time for Undo, Redo (copy_children): 0.363519906998 s, 0.162662982941 s Time for Undo, Redo (set_attr): 0.208750009537 s, 0.0732419490814 s Time for Undo, Redo (set_attr): 0.27628993988 s, 0.0736088752747 s real 0m5.557s user 0m4.354s sys 0m0.729s Note how all tests take more or less the same amount of time. This is because a move operation is used as a central tool to implement the Undo/Redo feature. Such a move operation has a constant cost, independently of the size of the objects. For example, using objects with 1000 elements, we can see that this does not affect the Undo/Redo speed: $ time python2.4 undo_redo.py -n 10 -i 2 -s 1000 data.nobackup/undo_redo.h5 Time for Undo, Redo (createNode): 0.213760137558 s, 0.0717759132385 s Time for Undo, Redo (createNode): 0.276151895523 s, 0.0724079608917 s Time for Undo, Redo (copy_children): 0.308417797089 s, 0.168260812759 s Time for Undo, Redo (copy_children): 0.382102966309 s, 0.168042898178 s Time for Undo, Redo (set_attr): 0.209735155106 s, 0.0740969181061 s Time for Undo, Redo (set_attr): 0.279798984528 s, 0.0770981311798 s real 0m5.835s user 0m4.585s sys 0m0.736s Undo/Redo times grow linearly with the number of objects implied ---------------------------------------------------------------- Secondly, the time for doing/undoing is obviously proportional (linearly) to the number of objects that are implied in that process (set by -n): $ time python2.4 undo_redo.py -n 100 -i 2 -s 10 data.nobackup/undo_redo.h5 Time for Undo, Redo (createNode): 2.27267885208 s, 0.779091119766 s Time for Undo, Redo (createNode): 2.31264209747 s, 0.766252040863 s Time for Undo, Redo (copy_children): 3.01871585846 s, 1.63346219063 s Time for Undo, Redo (copy_children): 3.07704997063 s, 1.62615203857 s Time for Undo, Redo (set_attr): 2.18017196655 s, 0.809293985367 s Time for Undo, Redo (set_attr): 2.23039293289 s, 0.809432029724 s real 0m48.395s user 0m40.385s sys 0m6.914s A note on actual performance and place for improvement ------------------------------------------------------ Finally, note how the Undo/Redo capability of PyTables is pretty fast. The next benchmark makes 1000 undo and 1000 redos for create_array: $ time python2.4 undo_redo.py -n 1000 -i 2 -t createNode -s 1000 data.nobackup/undo_redo.h5 Time for Undo, Redo (createNode): 22.7840828896 s, 7.9872610569 s Time for Undo, Redo (createNode): 22.2799329758 s, 7.95833396912 s real 1m32.307s user 1m16.598s sys 0m15.105s i.e. an undo takes 23 milliseconds while a redo takes 8 milliseconds approximately. The fact that undo operations take 3 times more than redo is probably due to how the action log is implemented. The action log has been implemented as a Table object, and PyTables has been optimized to read rows of tables in *forward* direction (the one needed for redo operations). However, when looking in *backward* direction (needed for undo operations), the internal cache of PyTables is counterproductive and makes look-ups quite slow (compared with forward access). Nevertheless, the code for Undo/Redo has been optimized quite a bit to smooth this kind of access as much as possible, but with a relative success. A more definitive optimization should involve getting much better performance for reading tables in backward direction. That would be a major task, and can be eventually addressed in the future. Francesc Alted 2005-03-10 PyTables-v.3.1.1/bench/widetree.py000066400000000000000000000100741231437614300167520ustar00rootroot00000000000000from __future__ import print_function import hotshot import hotshot.stats import unittest import os import tempfile from tables import * verbose = 0 class WideTreeTestCase(unittest.TestCase): """Checks for maximum number of childs for a Group.""" def test00_Leafs(self): """Checking creation of large number of leafs (1024) per group. Variable 'maxchilds' controls this check. PyTables support up to 4096 childs per group, but this would take too much memory (up to 64 MB) for testing purposes (may be we can add a test for big platforms). A 1024 childs run takes up to 30 MB. A 512 childs test takes around 25 MB. """ import time maxchilds = 1000 if verbose: print('\n', '-=' * 30) print("Running %s.test00_wideTree..." % self.__class__.__name__) print("Maximum number of childs tested :", maxchilds) # Open a new empty HDF5 file #file = tempfile.mktemp(".h5") file = "test_widetree.h5" fileh = open_file(file, mode="w") if verbose: print("Children writing progress: ", end=' ') for child in range(maxchilds): if verbose: print("%3d," % (child), end=' ') a = [1, 1] fileh.create_group(fileh.root, 'group' + str(child), "child: %d" % child) fileh.create_array("/group" + str(child), 'array' + str(child), a, "child: %d" % child) if verbose: print() # Close the file fileh.close() t1 = time.time() # Open the previous HDF5 file in read-only mode fileh = open_file(file, mode="r") print(("\nTime spent opening a file with %d groups + %d arrays: " "%s s" % (maxchilds, maxchilds, time.time() - t1))) if verbose: print("\nChildren reading progress: ", end=' ') # Close the file fileh.close() # Then, delete the file # os.remove(file) def test01_wideTree(self): """Checking creation of large number of groups (1024) per group. Variable 'maxchilds' controls this check. PyTables support up to 4096 childs per group, but this would take too much memory (up to 64 MB) for testing purposes (may be we can add a test for big platforms). A 1024 childs run takes up to 30 MB. A 512 childs test takes around 25 MB. """ import time maxchilds = 1000 if verbose: print('\n', '-=' * 30) print("Running %s.test00_wideTree..." % self.__class__.__name__) print("Maximum number of childs tested :", maxchilds) # Open a new empty HDF5 file file = tempfile.mktemp(".h5") #file = "test_widetree.h5" fileh = open_file(file, mode="w") if verbose: print("Children writing progress: ", end=' ') for child in range(maxchilds): if verbose: print("%3d," % (child), end=' ') fileh.create_group(fileh.root, 'group' + str(child), "child: %d" % child) if verbose: print() # Close the file fileh.close() t1 = time.time() # Open the previous HDF5 file in read-only mode fileh = open_file(file, mode="r") print("\nTime spent opening a file with %d groups: %s s" % (maxchilds, time.time() - t1)) # Close the file fileh.close() # Then, delete the file os.remove(file) #---------------------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() theSuite.addTest(unittest.makeSuite(WideTreeTestCase)) return theSuite if __name__ == '__main__': prof = hotshot.Profile("widetree.prof") benchtime, stones = prof.runcall(unittest.main(defaultTest='suite')) prof.close() stats = hotshot.stats.load("widetree.prof") stats.strip_dirs() stats.sort_stats('time', 'calls') stats.print_stats(20) PyTables-v.3.1.1/bench/widetree2.py000066400000000000000000000071001231437614300170300ustar00rootroot00000000000000from __future__ import print_function import unittest from tables import * # Next imports are only necessary for this test suite #from tables import Group, Leaf, Table, Array verbose = 0 class Test(IsDescription): ngroup = Int32Col(pos=1) ntable = Int32Col(pos=2) nrow = Int32Col(pos=3) #string = StringCol(itemsize=500, pos=4) class WideTreeTestCase(unittest.TestCase): def test00_Leafs(self): # Open a new empty HDF5 file filename = "test_widetree.h5" ngroups = 10 ntables = 300 nrows = 10 complevel = 0 complib = "lzo" print("Writing...") # Open a file in "w"rite mode fileh = open_file(filename, mode="w", title="PyTables Stress Test") for k in range(ngroups): # Create the group group = fileh.create_group("/", 'group%04d' % k, "Group %d" % k) fileh.close() # Now, create the tables rowswritten = 0 for k in range(ngroups): print("Filling tables in group:", k) fileh = open_file(filename, mode="a", root_uep='group%04d' % k) # Get the group group = fileh.root for j in range(ntables): # Create a table table = fileh.create_table(group, 'table%04d' % j, Test, 'Table%04d' % j, Filters(complevel, complib), nrows) # Get the row object associated with the new table row = table.row # Fill the table for i in range(nrows): row['ngroup'] = k row['ntable'] = j row['nrow'] = i row.append() rowswritten += nrows table.flush() # Close the file fileh.close() # read the file print("Reading...") rowsread = 0 for ngroup in range(ngroups): fileh = open_file(filename, mode="r", root_uep='group%04d' % ngroup) # Get the group group = fileh.root ntable = 0 if verbose: print("Group ==>", group) for table in fileh.list_nodes(group, 'Table'): if verbose > 1: print("Table ==>", table) print("Max rows in buf:", table.nrowsinbuf) print("Rows in", table._v_pathname, ":", table.nrows) print("Buffersize:", table.rowsize * table.nrowsinbuf) print("MaxTuples:", table.nrowsinbuf) nrow = 0 for row in table: try: assert row["ngroup"] == ngroup assert row["ntable"] == ntable assert row["nrow"] == nrow except: print("Error in group: %d, table: %d, row: %d" % (ngroup, ntable, nrow)) print("Record ==>", row) nrow += 1 assert nrow == table.nrows rowsread += table.nrows ntable += 1 # Close the file (eventually destroy the extended type) fileh.close() #---------------------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() theSuite.addTest(unittest.makeSuite(WideTreeTestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/bench/woody-pentiumIV.txt000066400000000000000000000272251231437614300204160ustar00rootroot00000000000000This is for Debian woody! Below are some benchmarking figures obtained while reading and writing to a file with three tables, each table containing 10000 records. For reference, the same tests have been repeated using the shelve module that comes with Python. The tests were conducted on a platform with a 2 GHz AMD Athlon chip, an IDE disk at 4600 rpm, and 256 MB of RAM. Version 0.2 | medium size records | small size records | (47 Bytes) | (16 Bytes) +---------------------------+------------------------------ | rows/s filesize | rows/s filesize | write read | write read ------------+---------------------------+------------------------------ no compress| | record | 24400 39000 1184 KB | 32600 52600 506 KB tupla | 17100 81100 1184 KB | 66666 107142 506 KB ------------+---------------------------+------------------------------ compress | | record | 22200 37500 494 KB | 31900 51700 94 KB tupla | 16100 75000 494 KB | 63900 107142 94 KB ------------+---------------------------+------------------------------ Shelve | 25800 14400 2500 KB | 68200 17000 921 KB New version (15-Jan-2003) PyTables pre-0.3 Rec length | rows/s | KB/s | rows | filesz | memory | | write read | write read | | (MB) | (MB) | ------------+-----------------+-----------------+-------+--------+--------+ 16 B | 31000 166600 | 480 2600 | 3.e4 | 0.49| 6.5 | ------------+-----------------+-----------------+-------+--------+--------+ 56 B | 17300 136000 | 942 7460 | 3.e4 | 1.7 | 7.2 | ------------+-----------------+-----------------+-------+--------+--------+ 56 B* | 1560 136000 | 85 7560 | 3.e4 | 1.7 | 7.2 | ------------+-----------------+-----------------+-------+--------+--------+ 64 B* | 1540 130000 | 96 8152 | 3.e4 | 1.9 | 7.2 | ------------+-----------------+-----------------+-------+--------+--------+ 550 B* | 879 81100 | 472 43500 | 3.e4 | 19 | 7.2 | ------------+-----------------+-----------------+-------+--------+--------+ 550 B** | 12000 103000 | 6440 55400 | 3.e5 | 168 | 7.2 | ------------+-----------------+-----------------+-------+--------+--------+ 550 B** | 15500 81100 | 8350 43500 | 3.e4 | 19 | 7.2 | ------------+-----------------+-----------------+-------+--------+--------+ 550 B**c| 909 1100 | 490 1081 | 3.e4 | 0.76| 8.5 | ------------+-----------------+-----------------+-------+--------+--------+ 550 B***| 3600 81100 | 1950 43500 | 3.e4 | 19 | 7.2 | ------------+-----------------+-----------------+-------+--------+--------+ * These are figures obtained with a numarray as part of the record ** The numarray record fields are not set in each iteration *** Some numarray elements of a record field are changed on each iteration **c Like ** but with compression (level 1) New version (10-March-2003) PyTables pre-0.4 Rec | rows/s | KB/s | rows | filesz | memory |%CP|%CP length | write read | write read | | (MB) | (MB) |(w)|(r) --------+-----------------+-----------------+-------+--------+--------+---+---- 16 B |434000 469000 | 6800 7300 | 3.e4 | 0.49| 6.5 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 16 Bc |326000 435000 | 5100 6800 | 3.e4 | 0.12| 6.5 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 16 B |663000 728000 | 10400 11400 | 3.e5 | 4.7 | 7.0 | 99|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 16 B |679000 797000 | 10600 12500 | 3.e6 | 46.0 | 10.0 | 98| 98 --------+-----------------+-----------------+-------+--------+--------+---+---- 16 Bc |452000 663000 | 7100 10400 | 3.e6 | 9.3 | 10.0 | 98| 98 --------+-----------------+-----------------+-------+--------+--------+---+---- 16 B |576000 590000 | 9000 9200 | 3.e7 | 458.0 | 11.0 | 78| 76 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B | 3050 380000 | 163 20700 | 3.e4 | 1.7 | 7.2 | 98|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B* |194000 340000 | 10600 18600 | 3.e4 | 1.7 | 7.2 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B*c |142000 306000 | 7800 16600 | 3.e4 | 0.3 | 7.2 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B* |273600 589000 | 14800 32214 | 3.e5 | 16.0 | 9.0 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B*c |184000 425000 | 10070 23362 | 3.e5 | 2.7 | 9.7 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B* |203600 649000 | 11100 35500 | 3.e6 | 161.0 | 12.0 | 72| 99 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B* |184000 229000 | 10000 12500 | 1.e7 | 534.0 | 17.0 | 56| 40 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B*np|184000 229000 | 10000 12500 | 1.e7 | 534.0 | 17.0 | 56| 40 --------+-----------------+-----------------+-------+--------+--------+---+---- 550 B | 2230 143000 | 1195 76600 | 3.e4 | 19 | 9.4 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 550 B* | 76000 250000 | 40900 134000 | 3.e4 | 19 | 9.4 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 550 B*c | 13900 30000 | 7400 16100 | 3.e4 | 0.7 | 10.0 | 99| 99 --------+-----------------+-----------------+-------+--------+--------+---+---- 550 B* | 25400 325000 | 13600 174000 | 3.e5 | 167 | 11.0 | 71| 96 --------+-----------------+-----------------+-------+--------+--------+---+---- 550 B* | 18700 28000 | 10000 15100 | 6.e5 | 322 | 13.0 | 76| 9 --------+-----------------+-----------------+-------+--------+--------+---+---- 550 B*c | 7300 21000 | 3900 11300 | 6.e5 | 11 | 17.0 | 98| 99 --------+-----------------+-----------------+-------+--------+--------+---+---- * These are figures obtained with a numarray as part of the record ** The numarray record fields are not set in each iteration c With compression (level 1) np No psyco optimizations Shelve Rec length | rows/s | KB/s | rows | filesz | memory | | write read | write read | | (MB) | (MB) | ------------+-----------------+-----------------+-------+--------+--------+ 16 B | 68200 17000 | 1070 266 | 3.e4 | 0.94| 7.2 | ------------+-----------------+-----------------+-------+--------+--------+ 56 B | 25000 14400 | 1367 784 | 3.e4 | 2.5 | 10.6 | ------------+-----------------+-----------------+-------+--------+--------+ 56 B* | 2980 2710 | 162 148 | 3.e4 | 7.3 | 33 | ------------+-----------------+-----------------+-------+--------+--------+ 64 B* | 2900 2700 | 182 168 | 3.e4 | 7.5 | 33 | ------------+-----------------+-----------------+-------+--------+--------+ 550 B* | 1090 1310 | 590 710 | 3.e4 | 58 | 122 | ------------+-----------------+-----------------+-------+--------+--------+ 550 B** | 16000 14900 | 2400 1200 | 3.e4 | 2.4 | 10.6 | ------------+-----------------+-----------------+-------+--------+--------+ 550 B***| 28000 11900 | 2400 1100 | 3.e4 | 2.5 | 10.6 | ------------+-----------------+-----------------+-------+--------+--------+ * These are figures obtained with a numarray as part of the record ** The nuamrray records are not set on each iteration *** Some numarray elements of a record field are changed on each iteration Python cPickle & bsddb3 RECNO with variable length Rec | Krows/s | MB/s | Krows | filesz | memory |%CP|%CP length | write read | write read | | (MB) | (MB) |(w)|(r) --------+-----------------+-----------------+-------+--------+--------+---+---- 16 B | 23.0 4.3 | 0.65 0.12 | 30 | 2.3 | 6.0 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 16 B | 22.0 4.3 | 0.60 0.12 | 300 | 24 | 25.0 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B | 12.3 2.0 | 0.68 0.11 | 30 | 5.8 | 6.2 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B | 8.8 2.0 | 0.44 0.11 | 300 | 61 | 6.2 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- Python struct & bsddb3 RECNO with fixed length Rec | Krows/s | MB/s | Krows | filesz | memory |%CP|%CP length | write read | write read | | (MB) | (MB) |(w)|(r) --------+-----------------+-----------------+-------+--------+--------+---+---- 16 B | 61 71 | 1.6 1.9 | 30 | 1.0 | 5.0 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 16 B | 56 65 | 1.5 1.8 | 300 | 10 | 5.8 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 16 B | 51 61 | 1.4 1.6 | 3000 | 100 | 6.1 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B | 51 52 | 2.7 2.8 | 30 | 1.8 | 5.8 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B | 18 50 | 1.0 2.7 | 300 | 18 | 6.2 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B | 16 48 | 0.9 2.6 | 1000 | 61 | 6.5 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- PySqlLite Rec | rows/s | KB/s | rows | filesz | memory |%CP|%CP length | write read | write read | | (MB) | (MB) |(w)|(r) --------+-----------------+-----------------+-------+--------+--------+---+---- 16 B | 4290 1400000 | 200 48000 | 3.e4 | 1.4 | 5.0 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 16 B | 3660 1030000 | 182 51000 | 3.e5 | 15 | 5.0 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 16 B | 3580 230000 | 192 12380 | 6.e6 | 322 | 5.0 |100| 25 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B | 2990 882000 | 250 76000 | 3.e4 | 2.6 | 5.0 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B | 2900 857000 | 270 80000 | 3.e5 | 28 | 5.0 |100|100 --------+-----------------+-----------------+-------+--------+--------+---+---- 56 B | 2900 120000 | 302 13100 | 3.e6 | 314 | 5.0 |100| 11 --------+-----------------+-----------------+-------+--------+--------+---+---- PyTables-v.3.1.1/c-blosc/000077500000000000000000000000001231437614300150315ustar00rootroot00000000000000PyTables-v.3.1.1/c-blosc/.gitignore000066400000000000000000000000141231437614300170140ustar00rootroot00000000000000bench/bench PyTables-v.3.1.1/c-blosc/.mailmap000066400000000000000000000003411231437614300164500ustar00rootroot00000000000000Francesc Alted FrancescAlted Francesc Alted FrancescAlted Francesc Alted FrancescAlted PyTables-v.3.1.1/c-blosc/.travis.yml000066400000000000000000000005371231437614300171470ustar00rootroot00000000000000language: c compiler: - gcc - clang install: sudo apt-get install libhdf5-serial-dev #install: sudo apt-get install libsnappy-dev zlib1g-dev libhdf5-serial-dev #install: sudo apt-get install liblz4-dev libsnappy-dev zlib1g-dev libhdf5-dev before_script: - mkdir build - cd build - cmake -DBUILD_HDF5_FILTER=TRUE .. script: make && make test PyTables-v.3.1.1/c-blosc/ANNOUNCE.rst000066400000000000000000000033131231437614300167710ustar00rootroot00000000000000=============================================================== Announcing c-blosc 1.3.5 A blocking, shuffling and lossless compression library =============================================================== What is new? ============ This is just a maintenance release for removing a 'pointer from integer without a cast' compiler warning due to a bad macro definition. For more info, please see the release notes in: https://github.com/Blosc/c-blosc/wiki/Release-notes What is it? =========== Blosc (http://www.blosc.org) is a high performance compressor optimized for binary data. It has been designed to transmit data to the processor cache faster than the traditional, non-compressed, direct memory fetch approach via a memcpy() OS call. Blosc is the first compressor (that I'm aware of) that is meant not only to reduce the size of large datasets on-disk or in-memory, but also to accelerate object manipulations that are memory-bound. Blosc has a Python wrapper called python-blosc (https://github.com/Blosc/python-blosc) with a high-performance interface to NumPy too. There is also a handy command line for Blosc called Bloscpack (https://github.com/Blosc/bloscpack) that allows you to compress large binary datafiles on-disk. Download sources ================ Please go to main web site: http://www.blosc.org/ and proceed from there. The github repository is over here: https://github.com/Blosc/c-blosc Blosc is distributed using the MIT license, see LICENSES/BLOSC.txt for details. Mailing list ============ There is an official Blosc mailing list at: blosc@googlegroups.com http://groups.google.es/group/blosc Enjoy Data! .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 70 .. End: PyTables-v.3.1.1/c-blosc/CMakeLists.txt000066400000000000000000000163531231437614300176010ustar00rootroot00000000000000# CMake build system for Blosc # ============================ # # Available options: # # BUILD_STATIC: default ON # build the static version of the Blosc library # BUILD_HDF5_FILTER: default OFF # build the compression filter for the HDF5 library # BUILD_TESTS: default ON # build test programs and generates the "test" target # BUILD_BENCHMARKS: default ON # build the benchmark program # DEACTIVATE_LZ4: default OFF # do not include support for the LZ4 library # DEACTIVATE_SNAPPY: default OFF # do not include support for the Snappy library # DEACTIVATE_ZLIB: default OFF # do not include support for the Zlib library # PREFER_EXTERNAL_COMPLIBS: default ON # when found, use the installed compression libs instead of included sources # TEST_INCLUDE_BENCH_SINGLE_1: default ON # add a test that runs the benchmark program passing "single" with 1 thread # as first parameter # TEST_INCLUDE_BENCH_SINGLE_N: default ON # add a test that runs the benchmark program passing "single" with all threads # as first parameter # TEST_INCLUDE_BENCH_SUITE: default OFF # add a test that runs the benchmark program passing "suite" # as first parameter # TEST_INCLUDE_BENCH_SUITE_PARALLEL: default OFF # add a test that runs the benchmark program passing "parallel" # as first parameter # TEST_INCLUDE_BENCH_HARDSUITE: default OFF # add a test that runs the benchmark program passing "hardsuite" # as first parameter # TEST_INCLUDE_BENCH_EXTREMESUITE: default OFF # add a test that runs the benchmark program passing "extremesuite" # as first parameter # TEST_INCLUDE_BENCH_DEBUGSUITE: default OFF # add a test that runs the benchmark program passing "debugsuite" # as first parameter # # Components: # # LIB: includes blosc.so # DEV: static includes blosc.a and blosc.h # HDF5_FILTER: includes blosc_filter.so # HDF5_FILTER_DEV: includes blosc_filter.h cmake_minimum_required(VERSION 2.8) project(blosc) # parse the full version numbers from blosc.h file(READ ${CMAKE_CURRENT_SOURCE_DIR}/blosc/blosc.h _blosc_h_contents) string(REGEX REPLACE ".*#define[ \t]+BLOSC_VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" BLOSC_VERSION_MAJOR ${_blosc_h_contents}) string(REGEX REPLACE ".*#define[ \t]+BLOSC_VERSION_MINOR[ \t]+([0-9]+).*" "\\1" BLOSC_VERSION_MINOR ${_blosc_h_contents}) string(REGEX REPLACE ".*#define[ \t]+BLOSC_VERSION_RELEASE[ \t]+([0-9]+).*" "\\1" BLOSC_VERSION_PATCH ${_blosc_h_contents}) string(REGEX REPLACE ".*#define[ \t]+BLOSC_VERSION_STRING[ \t]+\"([-0-9A-Za-z.]+)\".*" "\\1" BLOSC_VERSION_STRING ${_blosc_h_contents}) message("Configuring for Blosc version: " ${BLOSC_VERSION_STRING}) # options option(BUILD_STATIC "Build a static version of the blosc library." ON) option(BUILD_HDF5_FILTER "Build a blosc based compression filter for the HDF5 library" OFF) option(BUILD_TESTS "Build test programs form the blosc compression library" ON) option(BUILD_BENCHMARKS "Build benchmark programs form the blosc compression library" ON) option(DEACTIVATE_LZ4 "Do not include support for the LZ4 library." OFF) option(DEACTIVATE_SNAPPY "Do not include support for the SNAPPY library." OFF) option(DEACTIVATE_ZLIB "Do not include support for the ZLIB library." OFF) option(PREFER_EXTERNAL_COMPLIBS "When found, use the installed compression libs instead of included sources." ON) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") if(NOT PREFER_EXTERNAL_COMPLIBS) message(STATUS "Finding external libraries disabled. Using internal sources.") endif(NOT PREFER_EXTERNAL_COMPLIBS) if(NOT DEACTIVATE_LZ4) if(PREFER_EXTERNAL_COMPLIBS) find_package(LZ4) endif(PREFER_EXTERNAL_COMPLIBS) # HAVE_LZ4 will be set to true because even if the library is # not found, we will use the included sources for it set(HAVE_LZ4 TRUE) endif(NOT DEACTIVATE_LZ4) if(NOT DEACTIVATE_SNAPPY) if(PREFER_EXTERNAL_COMPLIBS) find_package(Snappy) endif(PREFER_EXTERNAL_COMPLIBS) # HAVE_SNAPPY will be set to true because even if the library is not found, # we will use the included sources for it set(HAVE_SNAPPY TRUE) endif(NOT DEACTIVATE_SNAPPY) if(NOT DEACTIVATE_ZLIB) # import the ZLIB_ROOT environment variable to help finding the zlib library if(PREFER_EXTERNAL_COMPLIBS) set(ZLIB_ROOT $ENV{ZLIB_ROOT}) find_package( ZLIB ) if (NOT ZLIB_FOUND ) message(STATUS "No zlib found. Using internal sources.") endif (NOT ZLIB_FOUND ) endif(PREFER_EXTERNAL_COMPLIBS) # HAVE_ZLIB will be set to true because even if the library is not found, # we will use the included sources for it set(HAVE_ZLIB TRUE) endif(NOT DEACTIVATE_ZLIB) # create the config.h file configure_file ("blosc/config.h.in" "blosc/config.h" ) # now make sure that you set the build directory on your "Include" path when compiling include_directories("${PROJECT_BINARY_DIR}/blosc/") # force the default build type to Release. if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) endif(NOT CMAKE_BUILD_TYPE) # flags # @TODO: set -Wall # @NOTE: -O3 is enabled in Release mode (CMAKE_BUILD_TYPE="Release") # Set the "-msse2" build flag only if the CMAKE_C_FLAGS is not already set. # Probably "-msse2" should be appended to CMAKE_C_FLAGS_RELEASE. if(CMAKE_C_COMPILER_ID STREQUAL GNU OR CMAKE_C_COMPILER_ID STREQUAL Clang) if(NOT CMAKE_C_FLAGS) set(CMAKE_C_FLAGS -msse2 CACHE STRING "C flags." FORCE) endif(NOT CMAKE_C_FLAGS) endif(CMAKE_C_COMPILER_ID STREQUAL GNU OR CMAKE_C_COMPILER_ID STREQUAL Clang) if(MSVC) if(NOT CMAKE_C_FLAGS) set(CMAKE_C_FLAGS "/Ox" CACHE STRING "C flags." FORCE) endif(NOT CMAKE_C_FLAGS) endif(MSVC) if(WIN32) # For some supporting headers include_directories("${CMAKE_CURRENT_SOURCE_DIR}/blosc") endif(WIN32) # subdirectories add_subdirectory(blosc) if(BUILD_TESTS) enable_testing() add_subdirectory(tests) endif(BUILD_TESTS) if(BUILD_HDF5_FILTER) add_subdirectory(hdf5) endif(BUILD_HDF5_FILTER) if(BUILD_BENCHMARKS) add_subdirectory(bench) endif(BUILD_BENCHMARKS) # uninstall target configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/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) # packaging include(InstallRequiredSystemLibraries) set(CPACK_GENERATOR TGZ ZIP) set(CPACK_SOURCE_GENERATOR TGZ ZIP) set(CPACK_PACKAGE_VERSION_MAJOR ${BLOSC_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${BLOSC_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${BLOSC_VERSION_PATCH}) set(CPACK_PACKAGE_VERSION ${BLOSC_STRING_VERSION}) set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.rst") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A blocking, shuffling and lossless compression library") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSES/BLOSC.txt") set(CPACK_SOURCE_IGNORE_FILES "/build.*;.*~;\\\\.git.*;\\\\.DS_Store") set(CPACK_STRIP_FILES TRUE) set(CPACK_SOURCE_STRIP_FILES TRUE) include(CPack) PyTables-v.3.1.1/c-blosc/LICENSES/000077500000000000000000000000001231437614300162365ustar00rootroot00000000000000PyTables-v.3.1.1/c-blosc/LICENSES/BLOSC.txt000066400000000000000000000022651231437614300176460ustar00rootroot00000000000000Blosc - A blocking, shuffling and lossless compression library Copyright (C) 2009-2012 Francesc Alted Copyright (C) 2013 Francesc Alted Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PyTables-v.3.1.1/c-blosc/LICENSES/FASTLZ.txt000066400000000000000000000023121231437614300200000ustar00rootroot00000000000000FastLZ - lightning-fast lossless compression library Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PyTables-v.3.1.1/c-blosc/LICENSES/H5PY.txt000066400000000000000000000030371231437614300175270ustar00rootroot00000000000000Copyright Notice and Statement for the h5py Project Copyright (c) 2008 Andrew Collette http://h5py.alfven.org All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: a. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c. Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PyTables-v.3.1.1/c-blosc/LICENSES/LZ4.txt000066400000000000000000000030101231437614300174020ustar00rootroot00000000000000LZ4 - Fast LZ compression algorithm Copyright (C) 2011-2013, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html - LZ4 source repository : http://code.google.com/p/lz4/ PyTables-v.3.1.1/c-blosc/LICENSES/SNAPPY.txt000066400000000000000000000027031231437614300200130ustar00rootroot00000000000000Copyright 2011, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PyTables-v.3.1.1/c-blosc/LICENSES/STDINT.txt000066400000000000000000000025621231437614300200110ustar00rootroot00000000000000Copyright (c) 2006-2008 Alexander Chemeris Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.PyTables-v.3.1.1/c-blosc/LICENSES/ZLIB.txt000066400000000000000000000017521231437614300175440ustar00rootroot00000000000000Copyright notice: (C) 1995-2013 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu PyTables-v.3.1.1/c-blosc/README.rst000066400000000000000000000243561231437614300165320ustar00rootroot00000000000000=============================================================== Blosc: A blocking, shuffling and lossless compression library =============================================================== :Author: Francesc Alted :Contact: faltet@gmail.com :URL: http://www.blosc.org What is it? =========== Blosc [1]_ is a high performance compressor optimized for binary data. It has been designed to transmit data to the processor cache faster than the traditional, non-compressed, direct memory fetch approach via a memcpy() OS call. Blosc is the first compressor (that I'm aware of) that is meant not only to reduce the size of large datasets on-disk or in-memory, but also to accelerate memory-bound computations. It uses the blocking technique (as described in [2]_) to reduce activity on the memory bus as much as possible. In short, this technique works by dividing datasets in blocks that are small enough to fit in caches of modern processors and perform compression / decompression there. It also leverages, if available, SIMD instructions (SSE2) and multi-threading capabilities of CPUs, in order to accelerate the compression / decompression process to a maximum. Blosc is actually a metacompressor, that meaning that it can use a range of compression libraries for performing the actual compression/decompression. Right now, it comes with integrated support for BloscLZ (the original one), LZ4, LZ4HC, Snappy and Zlib. Blosc comes with full sources for all compressors, so in case it does not find the libraries installed in your system, it will compile from the included sources and they will be integrated into the Blosc library anyway. That means that you can trust in having all supported compressors integrated in Blosc in all supported platforms. You can see some benchmarks about Blosc performance in [3]_ Blosc is distributed using the MIT license, see LICENSES/BLOSC.txt for details. .. [1] http://www.blosc.org .. [2] http://blosc.org/docs/StarvingCPUs-CISE-2010.pdf .. [3] http://blosc.org/trac/wiki/SyntheticBenchmarks Meta-compression and other advantages over existing compressors =============================================================== Blosc is not like other compressors: it should rather be called a meta-compressor. This is so because it can use different compressors and pre-conditioners (programs that generally improve compression ratio). At any rate, it can also be called a compressor because it happens that it already integrates one compressor and one pre-conditioner, so it can actually work like so. Currently it uses BloscLZ, a compressor heavily based on FastLZ (http://fastlz.org/), and a highly optimized (it can use SSE2 instructions, if available) Shuffle pre-conditioner. However, different compressors or pre-conditioners may be added in the future. Blosc is in charge of coordinating the compressor and pre-conditioners so that they can leverage the blocking technique (described above) as well as multi-threaded execution (if several cores are available) automatically. That makes that every compressor and pre-conditioner will work at very high speeds, even if it was not initially designed for doing blocking or multi-threading. Other advantages of Blosc are: * Meant for binary data: can take advantage of the type size meta-information for improved compression ratio (using the integrated shuffle pre-conditioner). * Small overhead on non-compressible data: only a maximum of 16 additional bytes over the source buffer length are needed to compress *every* input. * Maximum destination length: contrarily to many other compressors, both compression and decompression routines have support for maximum size lengths for the destination buffer. * Replacement for memcpy(): it supports a 0 compression level that does not compress at all and only adds 16 bytes of overhead. In this mode Blosc can copy memory usually faster than a plain memcpy(). When taken together, all these features set Blosc apart from other similar solutions. Compiling your application with a minimalistic Blosc ==================================================== The minimal Blosc consists of the next files (in blosc/ directory):: blosc.h and blosc.c -- the main routines shuffle.h and shuffle.c -- the shuffle code blosclz.h and blosclz.c -- the blosclz compressor Just add these files to your project in order to use Blosc. For information on compression and decompression routines, see blosc.h. To compile using GCC (4.4 or higher recommended) on Unix: .. code-block:: console $ gcc -O3 -msse2 -o myprog myprog.c blosc/*.c -lpthread Using Windows and MINGW: .. code-block:: console $ gcc -O3 -msse2 -o myprog myprog.c blosc\*.c Using Windows and MSVC (2010 or higher recommended): .. code-block:: console $ cl /Ox /Femyprog.exe myprog.c blosc\*.c A simple usage example is the benchmark in the bench/bench.c file. Another example for using Blosc as a generic HDF5 filter is in the hdf5/ directory. I have not tried to compile this with compilers other than GCC, clang, MINGW, Intel ICC or MSVC yet. Please report your experiences with your own platforms. Adding support for other compressors (LZ4, LZ4HC, Snappy, Zlib) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you want to add support for the LZ4, LZ4HC, Snappy or Zlib compressors, just add the symbols HAVE_LZ4 (will include both LZ4 and LZ4HC), HAVE_SNAPPY and HAVE_ZLIB during compilation and add the libraries. For example, for compiling Blosc with Zlib support do: .. code-block:: console $ gcc -O3 -msse2 -o myprog myprog.c blosc/*.c -lpthread -DHAVE_ZLIB -lz In the bench/ directory there a couple of Makefile files (one for UNIX and the other for MinGW) with more complete building examples, like selecting between libraries or internal sources for the compressors. Compiling the Blosc library with CMake ====================================== Blosc can also be built, tested and installed using CMake_. Although this procedure is a bit more invloved than the one described above, it is the most general because it allows to integrate other compressors than BloscLZ either from libraries or from internal sources. Hence, serious library developers should use this way. The following procedure describes the "out of source" build. Create the build directory and move into it: .. code-block:: console $ mkdir build $ cd build Now run CMake configuration and optionally specify the installation directory (e.g. '/usr' or '/usr/local'): .. code-block:: console $ cmake -DCMAKE_INSTALL_PREFIX=your_install_prefix_directory .. CMake allows to configure Blosc in many different ways, like prefering internal or external sources for compressors or enabling/disabling them. Please note that configuration can also be performed using UI tools provided by CMake_ (ccmake or cmake-gui): .. code-block:: console $ ccmake .. # run a curses-based interface $ cmake-gui .. # run a graphical interface Build, test and install Blosc: .. code-block:: console $ make $ make test $ make install The static and dynamic version of the Blosc library, together with header files, will be installed into the specified CMAKE_INSTALL_PREFIX. .. _CMake: http://www.cmake.org Adding support for other compressors (LZ4, LZ4HC, Snappy, Zlib) with CMake ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The CMake files in Blosc are configured to automatically detect other compressors like LZ4, LZ4HC, Snappy or Zlib by default. So as long as the libraries and the header files for these libraries are accessible, these will be used by default. *Note on Zlib*: the library should be easily found on UNIX systems, although on Windows, you can help CMake to find it by setting the environment variable 'ZLIB_ROOT' to where zlib 'include' and 'lib' directories are. Also, make sure that Zlib DDL library is in your '\Windows' directory. However, the full sources for LZ4, LZ4HC, Snappy and Zlib have been included in Blosc too. So, in general, you should not worry about not having (or CMake not finding) the libraries in your system because in this case, their sources will be automaticall compiled for you. That effectively means that you can be confident in having a complete support for all the supported compression libraries in all supported platforms. If you want to force Blosc to use the included compression sources instead of trying to find the libraries in the system first, you can switch off the PREFER_EXTERNAL_COMPLIBS CMake option: .. code-block:: console $ cmake -DPREFER_EXTERNAL_COMPLIBS=OFF .. You can also disable support for some compression libraries: .. code-block:: console $ cmake -DDEACTIVATE_SNAPPY=ON .. Mac OSX troubleshooting ======================= If you run into compilation troubles when using Mac OSX, please make sure that you have installed the command line developer tools. You can always install them with: .. code-block:: console $ xcode-select --install Wrapper for Python ================== Blosc has an official wrapper for Python. See: https://github.com/Blosc/python-blosc Command line interface and serialization format for Blosc ========================================================= Blosc can be used from command line by using Bloscpack. See: https://github.com/Blosc/bloscpack Filter for HDF5 =============== For those that want to use Blosc as a filter in the HDF5 library, there is a sample implementation in the hdf5/ directory. Mailing list ============ There is an official mailing list for Blosc at: blosc@googlegroups.com http://groups.google.es/group/blosc Acknowledgments =============== I'd like to thank the PyTables community that have collaborated in the exhaustive testing of Blosc. With an aggregate amount of more than 300 TB of different datasets compressed *and* decompressed successfully, I can say that Blosc is pretty safe now and ready for production purposes. Other important contributions: * Valentin Haenel did a terrific work implementing the support for the Snappy compression, fixing typos and improving docs and the plotting script. * Thibault North, with ideas from Oscar Villellas, contributed a way to call Blosc from different threads in a safe way. * The CMake support was initially contributed by Thibault North, and Antonio Valentino and Mark Wiebe made great enhancements to it. ---- **Enjoy data!** PyTables-v.3.1.1/c-blosc/README_HEADER.rst000066400000000000000000000032401231437614300175270ustar00rootroot00000000000000Blosc Header Format =================== Blosc (as of Version 1.0.0) has the following 16 byte header that stores information about the compressed buffer:: |-0-|-1-|-2-|-3-|-4-|-5-|-6-|-7-|-8-|-9-|-A-|-B-|-C-|-D-|-E-|-F-| ^ ^ ^ ^ | nbytes | blocksize | ctbytes | | | | | | | | +--typesize | | +------flags | +----------versionlz +--------------version Datatypes of the Header Entries ------------------------------- All entries are little endian. :version: (``uint8``) Blosc format version. :versionlz: (``uint8``) Version of the internal compressor used. :flags and compressor enumeration: (``bitfield``) The flags of the buffer :bit 0 (``0x01``): Whether the shuffle filter has been applied or not. :bit 1 (``0x02``): Whether the internal buffer is a pure memcpy or not. :bit 2 (``0x04``): Reserved :bit 3 (``0x08``): Reserved :bit 4 (``0x16``): Reserved :bit 5 (``0x32``): Part of the enumeration for compressors. :bit 6 (``0x64``): Part of the enumeration for compressors. :bit 7 (``0x64``): Part of the enumeration for compressors. The last three bits form an enumeration that allows to use alternative compressors. :``0``: ``blosclz`` :``1``: ``lz4`` or ``lz4hc`` :``2``: ``snappy`` :``3``: ``zlib`` :typesize: (``uint8``) Number of bytes for the atomic type. :nbytes: (``uint32``) Uncompressed size of the buffer. :blocksize: (``uint32``) Size of internal blocks. :ctbytes: (``uint32``) Compressed size of the buffer. PyTables-v.3.1.1/c-blosc/README_THREADED.rst000066400000000000000000000030411231437614300177560ustar00rootroot00000000000000Blosc supports threading ======================== Threads are the most efficient way to program parallel code for multi-core processors, but also the more difficult to program well. Also, they has a non-negligible start-up time that does not fit well with a high-performance compressor as Blosc tries to be. In order to reduce the overhead of threads as much as possible, I've decided to implement a pool of threads (the workers) that are waiting for the main process (the master) to send them jobs (basically, compressing and decompressing small blocks of the initial buffer). Despite this and many other internal optimizations in the threaded code, it does not work faster than the serial version for buffer sizes around 64/128 KB or less. This is for Intel Quad Core2 (Q8400 @ 2.66 GHz) / Linux (openSUSE 11.2, 64 bit), but your mileage may vary (and will vary!) for other processors / operating systems. In contrast, for buffers larger than 64/128 KB, the threaded version starts to perform significantly better, being the sweet point at 1 MB (again, this is with my setup). For larger buffer sizes than 1 MB, the threaded code slows down again, but it is probably due to a cache size issue and besides, it is still considerably faster than serial code. This is why Blosc falls back to use the serial version for such a 'small' buffers. So, you don't have to worry too much about deciding whether you should set the number of threads to 1 (serial) or more (parallel). Just set it to the number of cores in your processor and your are done! Francesc Alted PyTables-v.3.1.1/c-blosc/RELEASE_NOTES.rst000066400000000000000000000257201231437614300176210ustar00rootroot00000000000000================================ Release notes for c-blosc 1.3.5 ================================ :Author: Francesc Alted :Contact: faltet@gmail.com :URL: http://www.blosc.org Changes from 1.3.4 to 1.3.5 =========================== * Removed a pointer from 'pointer from integer without a cast' compiler warning due to a bad macro definition. Changes from 1.3.3 to 1.3.4 =========================== * Fixed a false buffer overrun condition. This bug made c-blosc to fail, even if the failure was not real. * Fixed the type of a buffer string. Changes from 1.3.2 to 1.3.3 =========================== * Updated to LZ4 1.1.3 (improved speed for 32-bit platforms). * Added a new `blosc_cbuffer_complib()` for getting the compression library for a compressed buffer. Changes from 1.3.1 to 1.3.2 =========================== * Fix for compiling Snappy sources against MSVC 2008. Thanks to Mark Wiebe! * Version for internal LZ4 and Snappy are now supported. When compiled against the external libraries, this info is not available because they do not support the symbols (yet). Changes from 1.3.0 to 1.3.1 =========================== * Fixes for a series of issues with the filter for HDF5 and, in particular, a problem in the decompression buffer size that made it impossible to use the blosc_filter in combination with other ones (e.g. fletcher32). See https://github.com/PyTables/PyTables/issues/21. Thanks to Antonio Valentino for the fix! Changes from 1.2.4 to 1.3.0 =========================== A nice handful of compressors have been added to Blosc: * LZ4 (http://code.google.com/p/lz4/): A very fast compressor/decompressor. Could be thought as a replacement of the original BloscLZ, but it can behave better is some scenarios. * LZ4HC (http://code.google.com/p/lz4/): This is a variation of LZ4 that achieves much better compression ratio at the cost of being much slower for compressing. Decompression speed is unaffected (and sometimes better than when using LZ4 itself!), so this is very good for read-only datasets. * Snappy (http://code.google.com/p/snappy/): A very fast compressor/decompressor. Could be thought as a replacement of the original BloscLZ, but it can behave better is some scenarios. * Zlib (http://www.zlib.net/): This is a classic. It achieves very good compression ratios, at the cost of speed. However, decompression speed is still pretty good, so it is a good candidate for read-only datasets. With this, you can select the compression library with the new function:: int blosc_set_complib(char* complib); where you pass the library that you want to use (currently "blosclz", "lz4", "lz4hc", "snappy" and "zlib", but the list can grow in the future). You can get more info about compressors support in you Blosc build by using these functions:: char* blosc_list_compressors(void); int blosc_get_complib_info(char *compressor, char **complib, char **version); Changes from 1.2.2 to 1.2.3 =========================== - Added a `blosc_init()` and `blosc_destroy()` so that the global lock can be initialized safely. These new functions will also allow other kind of initializations/destructions in the future. Existing applications using Blosc do not need to start using the new functions right away, as long as they calling `blosc_set_nthreads()` previous to anything else. However, using them is highly recommended. Thanks to Oscar Villellas for the init/destroy suggestion, it is a nice idea! Changes from 1.2.1 to 1.2.2 =========================== - All important warnings removed for all tested platforms. This will allow less intrusiveness compilation experiences with applications including Blosc source code. - The `bench/bench.c` has been updated so that it can be compiled on Windows again. - The new web site has been set to: http://www.blosc.org Changes from 1.2 to 1.2.1 ========================= - Fixed a problem with global lock not being initialized. This affected mostly to Windows platforms. Thanks to Christoph Gohlke for finding the cure! Changes from 1.1.5 to 1.2 ========================= - Now it is possible to call Blosc simultaneously from a parent threaded application without problems. This has been solved by setting a global lock so that the different calling threads do not execute Blosc routines at the same time. Of course, real threading work is still available *inside* Blosc itself. Thanks to Thibault North. - Support for cmake is now included. Linux, Mac OSX and Windows platforms are supported. Thanks to Thibault North, Antonio Valentino and Mark Wiebe. - Fixed many compilers warnings (specially about unused variables). - As a consequence of the above, as minimal change in the API has been introduced. That is, the previous API:: void blosc_free_resources(void) has changed to:: int blosc_free_resources(void) Now, a return value of 0 means that the resources have been released successfully. If the return value is negative, then it is not guaranteed that all the resources have been freed. - Many typos were fixed and docs have been improved. The script for generating nice plots for the included benchmarks has been improved too. Thanks to Valetin Haenel. Changes from 1.1.4 to 1.1.5 =========================== - Fix compile error with msvc compilers (Christoph Gohlke) Changes from 1.1.3 to 1.1.4 =========================== - Redefinition of the BLOSC_MAX_BUFFERSIZE constant as (INT_MAX - BLOSC_MAX_OVERHEAD) instead of just INT_MAX. This prevents to produce outputs larger than INT_MAX, which is not supported. - `exit()` call has been replaced by a ``return -1`` in blosc_compress() when checking for buffer sizes. Now programs will not just exit when the buffer is too large, but return a negative code. - Improvements in explicit casts. Blosc compiles without warnings (with GCC) now. - Lots of improvements in docs, in particular a nice ascii-art diagram of the Blosc format (Valentin Haenel). - Improvements to the plot-speeds.py (Valentin Haenel). - [HDF5 filter] Adapted HDF5 filter to use HDF5 1.8 by default (Antonio Valentino). - [HDF5 filter] New version of H5Z_class_t definition (Antonio Valentino). Changes from 1.1.2 to 1.1.3 =========================== - Much improved compression ratio when using large blocks (> 64 KB) and high compression levels (> 6) under some circumstances (special data distribution). Closes #7. Changes from 1.1.1 to 1.1.2 =========================== - Fixes for small typesizes (#6 and #1 of python-blosc). Changes from 1.1 to 1.1.1 ========================= - Added code to avoid calling blosc_set_nthreads more than necessary. That will improve performance up to 3x or more, specially for small chunksizes (< 1 MB). Changes from 1.0 to 1.1 ======================= - Added code for emulating pthreads API on Windows. No need to link explicitly with pthreads lib on Windows anymore. However, performance is a somewhat worse because the new emulation layer does not support the `pthread_barrier_wait()` call natively. But the big improvement in installation easiness is worth this penalty (most specially on 64-bit Windows, where pthreads-win32 support is flaky). - New BLOSC_MAX_BUFFERSIZE, BLOSC_MAX_TYPESIZE and BLOSC_MAX_THREADS symbols are available in blosc.h. These can be useful for validating parameters in clients. Thanks to Robert Smallshire for suggesting that. - A new BLOSC_MIN_HEADER_LENGTH symbol in blosc.h tells how many bytes long is the minimum length of a Blosc header. `blosc_cbuffer_sizes()` only needs these bytes to be passed to work correctly. - Removed many warnings (related with potentially dangerous type-casting code) issued by MSVC 2008 in 64-bit mode. - Fixed a problem with the computation of the blocksize in the Blosc filter for HDF5. - Fixed a problem with large datatypes. See http://www.pytables.org/trac/ticket/288 for more info. - Now Blosc is able to work well even if you fork an existing process with a pool of threads. Bug discovered when PyTables runs in multiprocess environments. See http://pytables.org/trac/ticket/295 for details. - Added a new `blosc_getitem()` call to allow the retrieval of items in sizes smaller than the complete buffer. That is useful for the carray project, but certainly for others too. Changes from 0.9.5 to 1.0 ========================= - Added a filter for HDF5 so that people can use Blosc outside PyTables, if they want to. - Many small improvements, specially in README files. - Do not assume that size_t is uint_32 for every platform. - Added more protection for large buffers or in allocation memory routines. - The src/ directory has been renamed to blosc/. - The `maxbytes` parameter in `blosc_compress()` has been renamed to `destsize`. This is for consistency with the `blosc_decompress()` parameters. Changes from 0.9.4 to 0.9.5 =========================== - Now, compression level 0 is allowed, meaning not compression at all. The overhead of this mode will be always BLOSC_MAX_OVERHEAD (16) bytes. This mode actually represents using Blosc as a basic memory container. - Supported a new parameter `maxbytes` for ``blosc_compress()``. It represents a maximum of bytes for output. Tests unit added too. - Added 3 new functions for querying different metadata on compressed buffers. A test suite for testing the new API has been added too. Changes from 0.9.3 to 0.9.4 =========================== - Support for cross-platform big/little endian compatibility in Blosc headers has been added. - Fixed several failures exposed by the extremesuite. The problem was a bad check for limits in the buffer size while compressing. - Added a new suite in bench.c called ``debugsuite`` that is appropriate for debugging purposes. Now, the ``extremesuite`` can be used for running the complete (and extremely long) suite. Changes from 0.9.0 to 0.9.3 =========================== - Fixed several nasty bugs uncovered by the new suites in bench.c. Thanks to Tony Theodore and Gabriel Beckers for their (very) responsive beta testing and feedback. - Added several modes (suites), namely ``suite``, ``hardsuite`` and ``extremehardsuite`` in bench.c so as to allow different levels of testing. Changes from 0.8.0 to 0.9 ========================= - Internal format version bumped to 2 in order to allow an easy way to indicate that a buffer is being saved uncompressed. This is not supported yet, but it might be in the future. - Blosc can use threads now for leveraging the increasing number of multi-core processors out there. See README-threaded.txt for more info. - Added a protection for MacOSX so that it has to not link against posix_memalign() funtion, which seems not available in old versions of MacOSX (for example, Tiger). At nay rate, posix_memalign() is not necessary on Mac because 16 bytes alignment is ensured by default. Thanks to Ivan Vilata. Fixes #3. .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 72 .. End: PyTables-v.3.1.1/c-blosc/RELEASING.rst000066400000000000000000000041201231437614300170710ustar00rootroot00000000000000================ Releasing Blosc ================ :Author: Francesc Alted :Contact: faltet@gmail.com :Date: 2014-01-15 Preliminaries ------------- - Make sure that ``RELEASE_NOTES.rst`` and ``ANNOUNCE.rst`` are up to date with the latest news in the release. - Check that *VERSION* symbols in blosc/blosc.h contains the correct info. Testing ------- Create a new build/ directory, change into it and issue:: $ cmake .. $ make $ make test To actually test Blosc the hard way, look at the end of: http://blosc.org/trac/wiki/SyntheticBenchmarks where instructions on how to intensively test (and benchmark) Blosc are given. Packaging --------- - Unpack the archive of the repository in a temporary directory:: $ export VERSION="the version number" $ mkdir /tmp/blosc-$VERSION # IMPORTANT: make sure that you are at the root of the repo now! $ git archive master | tar -x -C /tmp/blosc-$VERSION - And package the repo:: $ cd /tmp $ tar cvfz blosc-$VERSION.tar.gz blosc-$VERSION Do a quick check that the tarball is sane. Uploading --------- - Go to the downloads section in blosc.org and upload the source tarball. Tagging ------- - Create a tag ``X.Y.Z`` from ``master``. Use the next message:: $ git tag -a vX.Y.Z -m "Tagging version X.Y.Z" - Push the tag to the github repo:: $ git push --tags Announcing ---------- - Update the release notes in the github wiki: https://github.com/Blosc/c-blosc/wiki/Release-notes - Send an announcement to the blosc, pytables, carray and comp.compression lists. Use the ``ANNOUNCE.rst`` file as skeleton (possibly as the definitive version). Post-release actions -------------------- - Edit *VERSION* symbols in blosc/blosc.h in master to increment the version to the next minor one (i.e. X.Y.Z --> X.Y.(Z+1).dev). - Create new headers for adding new features in ``RELEASE_NOTES.rst`` and empty the release-specific information in ``ANNOUNCE.rst`` and add this place-holder instead: #XXX version-specific blurb XXX# That's all folks! .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 70 .. End: PyTables-v.3.1.1/c-blosc/bench/000077500000000000000000000000001231437614300161105ustar00rootroot00000000000000PyTables-v.3.1.1/c-blosc/bench/CMakeLists.txt000066400000000000000000000050711231437614300206530ustar00rootroot00000000000000# sources set(SOURCES bench.c) # targets add_executable(bench ${SOURCES}) target_link_libraries(bench blosc_shared) # tests if(BUILD_TESTS) option(TEST_INCLUDE_BENCH_SINGLE_1 "Include bench single (1 thread) in the tests" ON) if(TEST_INCLUDE_BENCH_SINGLE_1) add_test(test_blosclz_1 bench blosclz single 1) if (HAVE_LZ4) add_test(test_lz4_1 bench lz4 single 1) add_test(test_lz4hc_1 bench lz4hc single 1) endif (HAVE_LZ4) if (HAVE_SNAPPY) add_test(test_snappy_1 bench snappy single 1) endif (HAVE_SNAPPY) if (HAVE_ZLIB) add_test(test_zlib_1 bench zlib single 1) endif (HAVE_ZLIB) endif(TEST_INCLUDE_BENCH_SINGLE_1) option(TEST_INCLUDE_BENCH_SINGLE_N "Include bench single (multithread) in the tests" ON) if(TEST_INCLUDE_BENCH_SINGLE_N) add_test(test_blosclz_n bench blosclz single) if (HAVE_LZ4) add_test(test_lz4_n bench lz4 single) add_test(test_lz4hc_n bench lz4hc single) endif (HAVE_LZ4) if (HAVE_SNAPPY) add_test(test_snappy_n bench snappy single) endif (HAVE_SNAPPY) if (HAVE_ZLIB) add_test(test_zlib_n bench zlib single) endif (HAVE_ZLIB) endif(TEST_INCLUDE_BENCH_SINGLE_N) option(TEST_INCLUDE_BENCH_SUITE "Include bench suite in the tests" OFF) if(TEST_INCLUDE_BENCH_SUITE) add_test(test_blosclz bench blosclz suite) if (HAVE_LZ4) add_test(test_lz4 bench lz4 suite) add_test(test_lz4hc bench lz4hc suite) endif (HAVE_LZ4) if (HAVE_SNAPPY) add_test(test_snappy bench snappy suite) endif (HAVE_SNAPPY) if (HAVE_ZLIB) add_test(test_zlib bench zlib suite) endif (HAVE_ZLIB) endif(TEST_INCLUDE_BENCH_SUITE) option(TEST_INCLUDE_BENCH_HARDSUITE "Include bench hardsuite in the tests" OFF) if(TEST_INCLUDE_BENCH_HARDSUITE) add_test(test_hardsuite blosc blosclz hardsuite) endif(TEST_INCLUDE_BENCH_HARDSUITE) option(TEST_INCLUDE_BENCH_EXTREMESUITE "Include bench extremesuite in the tests" OFF) if(TEST_INCLUDE_BENCH_EXTREMESUITE) add_test(test_extremesuite bench blosclz extremesuite) endif(TEST_INCLUDE_BENCH_EXTREMESUITE) option(TEST_INCLUDE_BENCH_DEBUGSUITE "Include bench debugsuite in the tests" OFF) if(TEST_INCLUDE_BENCH_DEBUGSUITE) add_test(test_debugsuite bench debugsuite) endif(TEST_INCLUDE_BENCH_DEBUGSUITE) endif(BUILD_TESTS) PyTables-v.3.1.1/c-blosc/bench/Makefile000066400000000000000000000020451231437614300175510ustar00rootroot00000000000000CC = gcc # clang++, g++ or just gcc if not compiling Snappy (C++ code) CFLAGS = -O3 -g -msse2 -Wall LDFLAGS = -lpthread # for UNIX or for Windows with pthread installed #LDFLAGS = -static # for mingw SOURCES = $(wildcard ../blosc/*.c) EXECUTABLE = bench # Support for internal LZ4 and LZ4HC LZ4_DIR = ../internal-complibs/lz4-r113 CFLAGS += -DHAVE_LZ4 -I$(LZ4_DIR) SOURCES += $(wildcard $(LZ4_DIR)/*.c) # Support for external LZ4 and LZ4HC #LDFLAGS += -DHAVE_LZ4 -llz4 # Support for internal Snappy #SNAPPY_DIR = ../internal-complibs/snappy-1.1.1 #CFLAGS += -DHAVE_SNAPPY -I$(SNAPPY_DIR) #SOURCES += $(wildcard $(SNAPPY_DIR)/*.cc) # Support for external Snappy LDFLAGS += -DHAVE_SNAPPY -lsnappy # Support for external Zlib LDFLAGS += -DHAVE_ZLIB -lz # Support for internal Zlib #ZLIB_DIR = ../internal-complibs/zlib-1.2.8 #CFLAGS += -DHAVE_ZLIB -I$(ZLIB_DIR) #SOURCES += $(wildcard $(ZLIB_DIR)/*.c) SOURCES += bench.c all: $(SOURCES) $(EXECUTABLE) $(EXECUTABLE): $(SOURCES) $(CC) $(CFLAGS) $(SOURCES) -o $@ $(LDFLAGS) clean: rm -rf $(EXECUTABLE) PyTables-v.3.1.1/c-blosc/bench/Makefile.mingw000066400000000000000000000023601231437614300206710ustar00rootroot00000000000000# Makefile for the MinGW suite for Windows CC = g++ # clang++, g++ or just gcc if not compiling Snappy (C++ code) CFLAGS = -O3 -g -msse2 -Wall #LDFLAGS = -lpthread # for UNIX or for Windows with pthread installed LDFLAGS = -static # for mingw SOURCES = $(wildcard ../blosc/*.c) EXECUTABLE = bench # Support for internal LZ4 LZ4_DIR = ../internal-complibs/lz4-r113 CFLAGS += -DHAVE_LZ4 -I$(LZ4_DIR) SOURCES += $(wildcard $(LZ4_DIR)/*.c) # Support for external LZ4 #LDFLAGS += -DHAVE_LZ4 -llz4 # Support for internal Snappy SNAPPY_DIR = ../internal-complibs/snappy-1.1.1 CFLAGS += -DHAVE_SNAPPY -I$(SNAPPY_DIR) SOURCES += $(wildcard $(SNAPPY_DIR)/*.cc) # Support for external Snappy #LDFLAGS += -DHAVE_SNAPPY -lsnappy # Support for the msvc zlib: ZLIB_ROOT=/libs/zlib128 LDFLAGS=-DHAVE_ZLIB -I$(ZLIB_ROOT)/include -lzdll -L$(ZLIB_ROOT)/lib # Support for the mingw zlib: #ZLIB_ROOT=/libs/libz-1.2.8 #LDFLAGS=-DHAVE_ZLIB -I$(ZLIB_ROOT)/include -lz -L$(ZLIB_ROOT)/lib # Support for internal Zlib #ZLIB_DIR = ../internal-complibs/zlib-1.2.8 #CFLAGS += -DHAVE_ZLIB -I$(ZLIB_DIR) #SOURCES += $(wildcard $(ZLIB_DIR)/*.c) all: $(SOURCES) $(EXECUTABLE) $(EXECUTABLE): $(SOURCES) $(CC) $(CFLAGS) bench.c $(SOURCES) -o $@ $(LDFLAGS) clean: rm -rf $(EXECUTABLE) PyTables-v.3.1.1/c-blosc/bench/bench.c000066400000000000000000000361731231437614300173450ustar00rootroot00000000000000/********************************************************************* Small benchmark for testing basic capabilities of Blosc. You can select different degrees of 'randomness' in input buffer, as well as external datafiles (uncomment the lines after "For data coming from a file" comment). For usage instructions of this benchmark, please see: http://blosc.pytables.org/trac/wiki/SyntheticBenchmarks I'm collecting speeds for different machines, so the output of your benchmarks and your processor specifications are welcome! Author: Francesc Alted See LICENSES/BLOSC.txt for details about copyright and rights to use. **********************************************************************/ #include #include #include #include #include #include #if defined(_WIN32) && !defined(__MINGW32__) #include #else #include #include #endif #include struct bench_wrap_args { char *compressor; int nthreads; int size; int elsize; int rshift; FILE * output_file; }; void *bench_wrap(void * args); #include "../blosc/blosc.h" #define KB 1024 #define MB (1024*KB) #define GB (1024*MB) #define NCHUNKS (32*1024) /* maximum number of chunks */ #define MAX_THREADS 16 int nchunks = NCHUNKS; int niter = 3; /* default number of iterations */ double totalsize = 0.; /* total compressed/decompressed size */ #if defined(_WIN32) && !defined(__MINGW32__) #include #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 #else #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL #endif struct timezone { int tz_minuteswest; /* minutes W of Greenwich */ int tz_dsttime; /* type of dst correction */ }; int gettimeofday(struct timeval *tv, struct timezone *tz) { FILETIME ft; unsigned __int64 tmpres = 0; static int tzflag; if (NULL != tv) { GetSystemTimeAsFileTime(&ft); tmpres |= ft.dwHighDateTime; tmpres <<= 32; tmpres |= ft.dwLowDateTime; /*converting file time to unix epoch*/ tmpres -= DELTA_EPOCH_IN_MICROSECS; tmpres /= 10; /*convert into microseconds*/ tv->tv_sec = (long)(tmpres / 1000000UL); tv->tv_usec = (long)(tmpres % 1000000UL); } if (NULL != tz) { if (!tzflag) { _tzset(); tzflag++; } tz->tz_minuteswest = _timezone / 60; tz->tz_dsttime = _daylight; } return 0; } #endif /* _WIN32 */ /* Given two timeval stamps, return the difference in seconds */ float getseconds(struct timeval last, struct timeval current) { int sec, usec; sec = current.tv_sec - last.tv_sec; usec = current.tv_usec - last.tv_usec; return (float)(((double)sec + usec*1e-6)); } /* Given two timeval stamps, return the time per chunk in usec */ float get_usec_chunk(struct timeval last, struct timeval current) { return (float)(getseconds(last, current)/(niter*nchunks)*1e6); } int get_value(int i, int rshift) { int v; v = (i<<26)^(i<<18)^(i<<11)^(i<<3)^i; if (rshift < 32) { v &= (1 << rshift) - 1; } return v; } void init_buffer(void *src, int size, int rshift) { unsigned int i; int *_src = (int *)src; /* To have reproducible results */ srand(1); /* Initialize the original buffer */ for (i = 0; i < size/sizeof(int); ++i) { /* Choose one below */ /* _src[i] = 0; * _src[i] = 0x01010101; * _src[i] = 0x01020304; * _src[i] = i * 1/.3; * _src[i] = i; * _src[i] = rand() >> (32-rshift); */ _src[i] = get_value(i, rshift); } } void do_bench(char *compressor, int nthreads, int size, int elsize, int rshift, FILE * ofile) { void *src, *srccpy; void *dest[NCHUNKS], *dest2; int nbytes = 0, cbytes = 0; int i, j; struct timeval last, current; float tmemcpy, tshuf, tunshuf; int clevel, doshuffle=1; unsigned char *orig, *round; blosc_set_nthreads(nthreads); if(blosc_set_compressor(compressor) < 0){ printf("Compiled w/o support for compressor: '%s', so sorry.\n", compressor); exit(1); } /* Initialize buffers */ src = malloc(size); srccpy = malloc(size); dest2 = malloc(size); /* zero src to initialize byte on it, and not only multiples of 4 */ memset(src, 0, size); init_buffer(src, size, rshift); memcpy(srccpy, src, size); for (j = 0; j < nchunks; j++) { dest[j] = malloc(size+BLOSC_MAX_OVERHEAD); } /* Warm destination memory (memcpy() will go a bit faster later on) */ for (j = 0; j < nchunks; j++) { memcpy(dest[j], src, size); } fprintf(ofile, "--> %d, %d, %d, %d, %s\n", nthreads, size, elsize, rshift, compressor); fprintf(ofile, "********************** Run info ******************************\n"); fprintf(ofile, "Blosc version: %s (%s)\n", BLOSC_VERSION_STRING, BLOSC_VERSION_DATE); fprintf(ofile, "Using synthetic data with %d significant bits (out of 32)\n", rshift); fprintf(ofile, "Dataset size: %d bytes\tType size: %d bytes\n", size, elsize); fprintf(ofile, "Working set: %.1f MB\t\t", (size*nchunks) / (float)MB); fprintf(ofile, "Number of threads: %d\n", nthreads); fprintf(ofile, "********************** Running benchmarks *********************\n"); gettimeofday(&last, NULL); for (i = 0; i < niter; i++) { for (j = 0; j < nchunks; j++) { memcpy(dest[j], src, size); } } gettimeofday(¤t, NULL); tmemcpy = get_usec_chunk(last, current); fprintf(ofile, "memcpy(write):\t\t %6.1f us, %.1f MB/s\n", tmemcpy, size/(tmemcpy*MB/1e6)); gettimeofday(&last, NULL); for (i = 0; i < niter; i++) { for (j = 0; j < nchunks; j++) { memcpy(dest2, dest[j], size); } } gettimeofday(¤t, NULL); tmemcpy = get_usec_chunk(last, current); fprintf(ofile, "memcpy(read):\t\t %6.1f us, %.1f MB/s\n", tmemcpy, size/(tmemcpy*MB/1e6)); for (clevel=0; clevel<10; clevel++) { fprintf(ofile, "Compression level: %d\n", clevel); gettimeofday(&last, NULL); for (i = 0; i < niter; i++) { for (j = 0; j < nchunks; j++) { cbytes = blosc_compress(clevel, doshuffle, elsize, size, src, dest[j], size+BLOSC_MAX_OVERHEAD); } } gettimeofday(¤t, NULL); tshuf = get_usec_chunk(last, current); fprintf(ofile, "comp(write):\t %6.1f us, %.1f MB/s\t ", tshuf, size/(tshuf*MB/1e6)); fprintf(ofile, "Final bytes: %d ", cbytes); if (cbytes > 0) { fprintf(ofile, "Ratio: %3.2f", size/(float)cbytes); } fprintf(ofile, "\n"); /* Compressor was unable to compress. Copy the buffer manually. */ if (cbytes == 0) { for (j = 0; j < nchunks; j++) { memcpy(dest[j], src, size); } } gettimeofday(&last, NULL); for (i = 0; i < niter; i++) { for (j = 0; j < nchunks; j++) { if (cbytes == 0) { memcpy(dest2, dest[j], size); nbytes = size; } else { nbytes = blosc_decompress(dest[j], dest2, size); } } } gettimeofday(¤t, NULL); tunshuf = get_usec_chunk(last, current); fprintf(ofile, "decomp(read):\t %6.1f us, %.1f MB/s\t ", tunshuf, nbytes/(tunshuf*MB/1e6)); if (nbytes < 0) { fprintf(ofile, "FAILED. Error code: %d\n", nbytes); } /* fprintf(ofile, "Orig bytes: %d\tFinal bytes: %d\n", cbytes, nbytes); */ /* Check if data has had a good roundtrip */ orig = (unsigned char *)srccpy; round = (unsigned char *)dest2; for(i = 0; i %x, round-trip--> %x\n", orig[i], round[i]); break; } } if (i == size) fprintf(ofile, "OK\n"); } /* End clevel loop */ /* To compute the totalsize, we should take into account the 10 compression levels */ totalsize += (size * nchunks * niter * 10.); free(src); free(srccpy); free(dest2); for (i = 0; i < nchunks; i++) { free(dest[i]); } } /* Compute a sensible value for nchunks */ int get_nchunks(int size_, int ws) { int nchunks; nchunks = ws / size_; if (nchunks > NCHUNKS) nchunks = NCHUNKS; if (nchunks < 1) nchunks = 1; return nchunks; } void *bench_wrap(void * args) { struct bench_wrap_args * arg = (struct bench_wrap_args *) args; do_bench(arg->compressor, arg->nthreads, arg->size, arg->elsize, arg->rshift, arg->output_file); return 0; } void print_compress_info(void) { char *name = NULL, *version = NULL; int ret; printf("Blosc version: %s (%s)\n", BLOSC_VERSION_STRING, BLOSC_VERSION_DATE); printf("List of supported compressors in this build: %s\n", blosc_list_compressors()); printf("Supported compression libraries:\n"); ret = blosc_get_complib_info("blosclz", &name, &version); if (ret >= 0) printf(" %s: %s\n", name, version); ret = blosc_get_complib_info("lz4", &name, &version); if (ret >= 0) printf(" %s: %s\n", name, version); ret = blosc_get_complib_info("snappy", &name, &version); if (ret >= 0) printf(" %s: %s\n", name, version); ret = blosc_get_complib_info("zlib", &name, &version); if (ret >= 0) printf(" %s: %s\n", name, version); } int main(int argc, char *argv[]) { char compressor[32]; char bsuite[32]; int single = 1; int suite = 0; int hard_suite = 0; int extreme_suite = 0; int debug_suite = 0; int nthreads = 4; /* The number of threads */ int size = 2*MB; /* Buffer size */ int elsize = 8; /* Datatype size */ int rshift = 19; /* Significant bits */ int workingset = 256*MB; /* The maximum allocated memory */ int nthreads_, size_, elsize_, rshift_, i; FILE * output_file = stdout; struct timeval last, current; float totaltime; char usage[256]; print_compress_info(); strncpy(usage, "Usage: bench [blosclz | lz4 | lz4hc | snappy | zlib] " "[[single | suite | hardsuite | extremesuite | debugsuite] " "[nthreads [bufsize(bytes) [typesize [sbits ]]]]]", 255); if (argc < 2) { printf("%s\n", usage); exit(1); } strcpy(compressor, argv[1]); if (strcmp(compressor, "blosclz") != 0 && strcmp(compressor, "lz4") != 0 && strcmp(compressor, "lz4hc") != 0 && strcmp(compressor, "snappy") != 0 && strcmp(compressor, "zlib") != 0) { printf("No such compressor: '%s'\n", compressor); exit(2); } if (argc < 3) strcpy(bsuite, "single"); else strcpy(bsuite, argv[2]); if (strcmp(bsuite, "single") == 0) { single = 1; } else if (strcmp(bsuite, "suite") == 0) { suite = 1; } else if (strcmp(bsuite, "hardsuite") == 0) { hard_suite = 1; workingset = 64*MB; /* Values here are ending points for loops */ nthreads = 2; size = 8*MB; elsize = 32; rshift = 32; } else if (strcmp(bsuite, "extremesuite") == 0) { extreme_suite = 1; workingset = 32*MB; niter = 1; /* Values here are ending points for loops */ nthreads = 4; size = 16*MB; elsize = 32; rshift = 32; } else if (strcmp(bsuite, "debugsuite") == 0) { debug_suite = 1; workingset = 32*MB; niter = 1; /* Warning: values here are starting points for loops. This is useful for debugging. */ nthreads = 1; size = 16*KB; elsize = 1; rshift = 0; } else { printf("%s\n", usage); exit(1); } printf("Using compressor: %s\n", compressor); printf("Running suite: %s\n", bsuite); if (argc >= 4) { nthreads = atoi(argv[3]); } if (argc >= 5) { size = atoi(argv[4]); } if (argc >= 6) { elsize = atoi(argv[5]); } if (argc >= 7) { rshift = atoi(argv[6]); } if ((argc >= 8) || !(single || suite || hard_suite || extreme_suite)) { printf("%s\n", usage); exit(1); } nchunks = get_nchunks(size, workingset); gettimeofday(&last, NULL); blosc_init(); if (suite) { for (nthreads_=1; nthreads_ <= nthreads; nthreads_++) { do_bench(compressor, nthreads_, size, elsize, rshift, output_file); } } else if (hard_suite) { /* Let's start the rshift loop by 4 so that 19 is visited. This is to allow a direct comparison with the plain suite, that runs precisely at 19 significant bits. */ for (rshift_ = 4; rshift_ <= rshift; rshift_ += 5) { for (elsize_ = 1; elsize_ <= elsize; elsize_ *= 2) { /* The next loop is for getting sizes that are not power of 2 */ for (i = -elsize_; i <= elsize_; i += elsize_) { for (size_ = 32*KB; size_ <= size; size_ *= 2) { nchunks = get_nchunks(size_+i, workingset); niter = 1; for (nthreads_ = 1; nthreads_ <= nthreads; nthreads_++) { do_bench(compressor, nthreads_, size_+i, elsize_, rshift_, output_file); gettimeofday(¤t, NULL); totaltime = getseconds(last, current); printf("Elapsed time:\t %6.1f s. Processed data: %.1f GB\n", totaltime, totalsize / GB); } } } } } } else if (extreme_suite) { for (rshift_ = 0; rshift_ <= rshift; rshift_++) { for (elsize_ = 1; elsize_ <= elsize; elsize_++) { /* The next loop is for getting sizes that are not power of 2 */ for (i = -elsize_*2; i <= elsize_*2; i += elsize_) { for (size_ = 32*KB; size_ <= size; size_ *= 2) { nchunks = get_nchunks(size_+i, workingset); for (nthreads_ = 1; nthreads_ <= nthreads; nthreads_++) { do_bench(compressor, nthreads_, size_+i, elsize_, rshift_, output_file); gettimeofday(¤t, NULL); totaltime = getseconds(last, current); printf("Elapsed time:\t %6.1f s. Processed data: %.1f GB\n", totaltime, totalsize / GB); } } } } } } else if (debug_suite) { for (rshift_ = rshift; rshift_ <= 32; rshift_++) { for (elsize_ = elsize; elsize_ <= 32; elsize_++) { /* The next loop is for getting sizes that are not power of 2 */ for (i = -elsize_*2; i <= elsize_*2; i += elsize_) { for (size_ = size; size_ <= 16*MB; size_ *= 2) { nchunks = get_nchunks(size_+i, workingset); for (nthreads_ = nthreads; nthreads_ <= 6; nthreads_++) { do_bench(compressor, nthreads_, size_+i, elsize_, rshift_, output_file); gettimeofday(¤t, NULL); totaltime = getseconds(last, current); printf("Elapsed time:\t %6.1f s. Processed data: %.1f GB\n", totaltime, totalsize / GB); } } } } } } /* Single mode */ else { do_bench(compressor, nthreads, size, elsize, rshift, output_file); } /* Print out some statistics */ gettimeofday(¤t, NULL); totaltime = getseconds(last, current); printf("\nRound-trip compr/decompr on %.1f GB\n", totalsize / GB); printf("Elapsed time:\t %6.1f s, %.1f MB/s\n", totaltime, totalsize*2*1.1/(MB*totaltime)); /* Free blosc resources */ blosc_free_resources(); blosc_destroy(); return 0; } PyTables-v.3.1.1/c-blosc/bench/plot-speeds.py000066400000000000000000000140201231437614300207160ustar00rootroot00000000000000"""Script for plotting the results of the 'suite' benchmark. Invoke without parameters for usage hints. :Author: Francesc Alted :Date: 2010-06-01 """ import matplotlib as mpl from pylab import * KB_ = 1024 MB_ = 1024*KB_ GB_ = 1024*MB_ NCHUNKS = 128 # keep in sync with bench.c linewidth=2 #markers= ['+', ',', 'o', '.', 's', 'v', 'x', '>', '<', '^'] #markers= [ 'x', '+', 'o', 's', 'v', '^', '>', '<', ] markers= [ 's', 'o', 'v', '^', '+', 'x', '>', '<', '.', ',' ] markersize = 8 def get_values(filename): f = open(filename) values = {"memcpyw": [], "memcpyr": []} for line in f: if line.startswith('-->'): tmp = line.split('-->')[1] nthreads, size, elsize, sbits = [int(i) for i in tmp.split(', ')] values["size"] = size * NCHUNKS / MB_; values["elsize"] = elsize; values["sbits"] = sbits; # New run for nthreads (ratios, speedsw, speedsr) = ([], [], []) # Add a new entry for (ratios, speedw, speedr) values[nthreads] = (ratios, speedsw, speedsr) #print "-->", nthreads, size, elsize, sbits elif line.startswith('memcpy(write):'): tmp = line.split(',')[1] memcpyw = float(tmp.split(' ')[1]) values["memcpyw"].append(memcpyw) elif line.startswith('memcpy(read):'): tmp = line.split(',')[1] memcpyr = float(tmp.split(' ')[1]) values["memcpyr"].append(memcpyr) elif line.startswith('comp(write):'): tmp = line.split(',')[1] speedw = float(tmp.split(' ')[1]) ratio = float(line.split(':')[-1]) speedsw.append(speedw) ratios.append(ratio) elif line.startswith('decomp(read):'): tmp = line.split(',')[1] speedr = float(tmp.split(' ')[1]) speedsr.append(speedr) if "OK" not in line: print "WARNING! OK not found in decomp line!" f.close() return nthreads, values def show_plot(plots, yaxis, legends, gtitle, xmax=None): xlabel('Compresssion ratio') ylabel('Speed (MB/s)') title(gtitle) xlim(0, xmax) #ylim(0, 10000) ylim(0, None) grid(True) # legends = [f[f.find('-'):f.index('.out')] for f in filenames] # legends = [l.replace('-', ' ') for l in legends] #legend([p[0] for p in plots], legends, loc = "upper left") legend([p[0] for p in plots if not isinstance(p, mpl.lines.Line2D)], legends, loc = "best") #subplots_adjust(bottom=0.2, top=None, wspace=0.2, hspace=0.2) if outfile: print "Saving plot to:", outfile savefig(outfile) else: show() if __name__ == '__main__': from optparse import OptionParser usage = "usage: %prog [-o outfile] [-t title ] [-d|-c] filename" compress_title = 'Compression speed' decompress_title = 'Decompression speed' yaxis = 'No axis name' parser = OptionParser(usage=usage) parser.add_option('-o', '--outfile', dest='outfile', help='filename for output ' + \ '(many extensions supported, e.g. .png, .jpg, .pdf)') parser.add_option('-t', '--title', dest='title', help='title of the plot',) parser.add_option('-l', '--limit', dest='limit', help='expression to limit number of threads shown',) parser.add_option('-x', '--xmax', dest='xmax', help='limit the x-axis', default=None) parser.add_option('-d', '--decompress', action='store_true', dest='dspeed', help='plot decompression data', default=False) parser.add_option('-c', '--compress', action='store_true', dest='cspeed', help='plot compression data', default=False) (options, args) = parser.parse_args() if len(args) == 0: parser.error("No input arguments") elif len(args) > 1: parser.error("Too many input arguments") else: pass if options.dspeed and options.cspeed: parser.error("Can only select one of [-d, -c]") elif options.cspeed: options.dspeed = False plot_title = compress_title else: # either neither or dspeed options.dspeed = True plot_title = decompress_title filename = args[0] outfile = options.outfile cspeed = options.cspeed dspeed = options.dspeed plots = [] legends = [] nthreads, values = get_values(filename) #print "Values:", values if options.limit: thread_range = eval(options.limit) else: thread_range = range(1, nthreads+1) if options.title: plot_title = options.title else: plot_title += " (%(size).1f MB, %(elsize)d bytes, %(sbits)d bits)" % values gtitle = plot_title for nt in thread_range: #print "Values for %s threads --> %s" % (nt, values[nt]) (ratios, speedw, speedr) = values[nt] if cspeed: speed = speedw else: speed = speedr #plot_ = semilogx(ratios, speed, linewidth=2) plot_ = plot(ratios, speed, linewidth=2) plots.append(plot_) nmarker = nt if nt >= len(markers): nmarker = nt%len(markers) setp(plot_, marker=markers[nmarker], markersize=markersize, linewidth=linewidth) legends.append("%d threads" % nt) # Add memcpy lines if cspeed: mean = sum(values["memcpyw"]) / nthreads message = "memcpy (write to memory)" else: mean = sum(values["memcpyr"]) / nthreads message = "memcpy (read from memory)" plot_ = axhline(mean, linewidth=3, linestyle='-.', color='black') text(4.0, mean+50, message) plots.append(plot_) show_plot(plots, yaxis, legends, gtitle, xmax=int(options.xmax) if options.xmax else None) PyTables-v.3.1.1/c-blosc/blosc/000077500000000000000000000000001231437614300161335ustar00rootroot00000000000000PyTables-v.3.1.1/c-blosc/blosc/CMakeLists.txt000066400000000000000000000061351231437614300207000ustar00rootroot00000000000000# a simple way to detect that we are using CMAKE add_definitions(-DUSING_CMAKE) set(INTERNAL_LIBS ${CMAKE_SOURCE_DIR}/internal-complibs) # includes if(NOT DEACTIVATE_LZ4) if (LZ4_FOUND) include_directories( ${LZ4_INCLUDE_DIR} ) else(LZ4_FOUND) set(LZ4_LOCAL_DIR ${INTERNAL_LIBS}/lz4-r113) include_directories( ${LZ4_LOCAL_DIR} ) endif(LZ4_FOUND) endif(NOT DEACTIVATE_LZ4) if(NOT DEACTIVATE_SNAPPY) if (SNAPPY_FOUND) include_directories( ${SNAPPY_INCLUDE_DIR} ) else(SNAPPY_FOUND) set(SNAPPY_LOCAL_DIR ${INTERNAL_LIBS}/snappy-1.1.1) include_directories( ${SNAPPY_LOCAL_DIR} ) endif(SNAPPY_FOUND) endif(NOT DEACTIVATE_SNAPPY) if(NOT DEACTIVATE_ZLIB) if (ZLIB_FOUND) include_directories( ${ZLIB_INCLUDE_DIR} ) else(ZLIB_FOUND) set(ZLIB_LOCAL_DIR ${INTERNAL_LIBS}/zlib-1.2.8) include_directories( ${ZLIB_LOCAL_DIR} ) endif(ZLIB_FOUND) endif(NOT DEACTIVATE_ZLIB) # library sources set(SOURCES blosc.c blosclz.c shuffle.c) # library install directory set(lib_dir lib${LIB_SUFFIX}) set(version_string ${BLOSC_VERSION_MAJOR}.${BLOSC_VERSION_MINOR}.${BLOSC_VERSION_PATCH}) set(CMAKE_THREAD_PREFER_PTHREAD TRUE) if(WIN32) # try to use the system library find_package(Threads) if(NOT Threads_FOUND) message(STATUS "using the internal pthread library for win32 systems.") set(SOURCES ${SOURCES} win32/pthread.c) else(NOT Threads_FOUND) set(LIBS ${LIBS} ${CMAKE_THREAD_LIBS_INIT}) endif(NOT Threads_FOUND) else(WIN32) find_package(Threads REQUIRED) set(LIBS ${LIBS} ${CMAKE_THREAD_LIBS_INIT}) endif(WIN32) if(NOT DEACTIVATE_LZ4) if(LZ4_FOUND) set(LIBS ${LIBS} ${LZ4_LIBRARY}) else(LZ4_FOUND) file(GLOB LZ4_FILES ${LZ4_LOCAL_DIR}/*.c) set(SOURCES ${SOURCES} ${LZ4_FILES}) endif(LZ4_FOUND) endif(NOT DEACTIVATE_LZ4) if(NOT DEACTIVATE_SNAPPY) if(SNAPPY_FOUND) set(LIBS ${LIBS} ${SNAPPY_LIBRARY}) else(SNAPPY_FOUND) file(GLOB SNAPPY_FILES ${SNAPPY_LOCAL_DIR}/*.cc) set(SOURCES ${SOURCES} ${SNAPPY_FILES}) endif(SNAPPY_FOUND) endif(NOT DEACTIVATE_SNAPPY) if(NOT DEACTIVATE_ZLIB) if(ZLIB_FOUND) set(LIBS ${LIBS} ${ZLIB_LIBRARY}) else(ZLIB_FOUND) file(GLOB ZLIB_FILES ${ZLIB_LOCAL_DIR}/*.c) set(SOURCES ${SOURCES} ${ZLIB_FILES}) endif(ZLIB_FOUND) endif(NOT DEACTIVATE_ZLIB) # targets add_library(blosc_shared SHARED ${SOURCES}) set_target_properties(blosc_shared PROPERTIES OUTPUT_NAME blosc) set_target_properties(blosc_shared PROPERTIES VERSION ${version_string} SOVERSION ${version_string} ) target_link_libraries(blosc_shared ${LIBS}) if(BUILD_STATIC) add_library(blosc_static STATIC ${SOURCES}) set_target_properties(blosc_static PROPERTIES OUTPUT_NAME blosc) target_link_libraries(blosc_static ${LIBS}) endif(BUILD_STATIC) # install install(FILES blosc.h DESTINATION include COMPONENT DEV) install(TARGETS blosc_shared DESTINATION ${lib_dir} COMPONENT LIB) if(BUILD_STATIC) install(TARGETS blosc_static DESTINATION ${lib_dir} COMPONENT DEV) endif(BUILD_STATIC) PyTables-v.3.1.1/c-blosc/blosc/blosc.c000066400000000000000000001543161231437614300174130ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Suffling and Compression Library Author: Francesc Alted Creation date: 2009-05-20 See LICENSES/BLOSC.txt for details about copyright and rights to use. **********************************************************************/ #include #include #include #include #include #include #if defined(USING_CMAKE) #include "config.h" #endif /* USING_CMAKE */ #include "blosc.h" #include "shuffle.h" #include "blosclz.h" #if defined(HAVE_LZ4) #include "lz4.h" #include "lz4hc.h" #endif /* HAVE_LZ4 */ #if defined(HAVE_SNAPPY) #include "snappy-c.h" #endif /* HAVE_SNAPPY */ #if defined(HAVE_ZLIB) #include "zlib.h" #endif /* HAVE_ZLIB */ #if defined(_WIN32) && !defined(__MINGW32__) #include #include "win32/stdint-windows.h" #include #define getpid _getpid #else #include #include #include #endif /* _WIN32 */ #if defined(_WIN32) #include "win32/pthread.h" #include "win32/pthread.c" #else #include #endif /* Some useful units */ #define KB 1024 #define MB (1024*KB) /* Minimum buffer size to be compressed */ #define MIN_BUFFERSIZE 128 /* Cannot be smaller than 66 */ /* The maximum number of splits in a block for compression */ #define MAX_SPLITS 16 /* Cannot be larger than 128 */ /* The size of L1 cache. 32 KB is quite common nowadays. */ #define L1 (32*KB) /* Wrapped function to adjust the number of threads used by blosc */ int blosc_set_nthreads_(int); /* Global variables for main logic */ static int32_t init_temps_done = 0; /* temp for compr/decompr initialized? */ static int32_t force_blocksize = 0; /* force the use of a blocksize? */ static int pid = 0; /* the PID for this process */ static int init_lib = 0; /* is library initalized? */ /* Global variables for threads */ static int32_t nthreads = 1; /* number of desired threads in pool */ static int32_t compressor = BLOSC_BLOSCLZ; /* the compressor to use by default */ static int32_t init_threads_done = 0; /* pool of threads initialized? */ static int32_t end_threads = 0; /* should exisiting threads end? */ static int32_t init_sentinels_done = 0; /* sentinels initialized? */ static int32_t giveup_code; /* error code when give up */ static int32_t nblock; /* block counter */ static pthread_t threads[BLOSC_MAX_THREADS]; /* opaque structure for threads */ static int32_t tids[BLOSC_MAX_THREADS]; /* ID per each thread */ #if !defined(_WIN32) static pthread_attr_t ct_attr; /* creation time attrs for threads */ #endif /* Have problems using posix barriers when symbol value is 200112L */ /* This requires more investigation, but will work for the moment */ #if defined(_POSIX_BARRIERS) && ( (_POSIX_BARRIERS - 20012L) >= 0 && _POSIX_BARRIERS != 200112L) #define _POSIX_BARRIERS_MINE #endif /* Synchronization variables */ static pthread_mutex_t count_mutex; static pthread_mutex_t global_comp_mutex; #ifdef _POSIX_BARRIERS_MINE static pthread_barrier_t barr_init; static pthread_barrier_t barr_finish; #else static int32_t count_threads; static pthread_mutex_t count_threads_mutex; static pthread_cond_t count_threads_cv; #endif /* Structure for parameters in (de-)compression threads */ static struct thread_data { int32_t typesize; int32_t blocksize; int32_t compress; int32_t clevel; int32_t flags; int32_t memcpyed; int32_t ntbytes; int32_t nbytes; int32_t maxbytes; int32_t nblocks; int32_t leftover; int32_t *bstarts; /* start pointers for each block */ uint8_t *src; uint8_t *dest; uint8_t *tmp[BLOSC_MAX_THREADS]; uint8_t *tmp2[BLOSC_MAX_THREADS]; } params; /* Structure for parameters meant for keeping track of current temporaries */ static struct temp_data { int32_t nthreads; int32_t typesize; int32_t blocksize; } current_temp; /* Macros for synchronization */ /* Wait until all threads are initialized */ #ifdef _POSIX_BARRIERS_MINE static int rc; #define WAIT_INIT(RET_VAL) \ rc = pthread_barrier_wait(&barr_init); \ if (rc != 0 && rc != PTHREAD_BARRIER_SERIAL_THREAD) { \ printf("Could not wait on barrier (init)\n"); \ return((RET_VAL)); \ } #else #define WAIT_INIT(RET_VAL) \ pthread_mutex_lock(&count_threads_mutex); \ if (count_threads < nthreads) { \ count_threads++; \ pthread_cond_wait(&count_threads_cv, &count_threads_mutex); \ } \ else { \ pthread_cond_broadcast(&count_threads_cv); \ } \ pthread_mutex_unlock(&count_threads_mutex); #endif /* Wait for all threads to finish */ #ifdef _POSIX_BARRIERS_MINE #define WAIT_FINISH(RET_VAL) \ rc = pthread_barrier_wait(&barr_finish); \ if (rc != 0 && rc != PTHREAD_BARRIER_SERIAL_THREAD) { \ printf("Could not wait on barrier (finish)\n"); \ return((RET_VAL)); \ } #else #define WAIT_FINISH(RET_VAL) \ pthread_mutex_lock(&count_threads_mutex); \ if (count_threads > 0) { \ count_threads--; \ pthread_cond_wait(&count_threads_cv, &count_threads_mutex); \ } \ else { \ pthread_cond_broadcast(&count_threads_cv); \ } \ pthread_mutex_unlock(&count_threads_mutex); #endif /* A function for aligned malloc that is portable */ static uint8_t *my_malloc(size_t size) { void *block = NULL; int res = 0; #if defined(_WIN32) /* A (void *) cast needed for avoiding a warning with MINGW :-/ */ block = (void *)_aligned_malloc(size, 16); #elif defined __APPLE__ /* Mac OS X guarantees 16-byte alignment in small allocs */ block = malloc(size); #elif _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 /* Platform does have an implementation of posix_memalign */ res = posix_memalign(&block, 16, size); #else block = malloc(size); #endif /* _WIN32 */ if (block == NULL || res != 0) { printf("Error allocating memory!"); return NULL; } return (uint8_t *)block; } /* Release memory booked by my_malloc */ static void my_free(void *block) { #if defined(_WIN32) _aligned_free(block); #else free(block); #endif /* _WIN32 */ } /* If `a` is little-endian, return it as-is. If not, return a copy, with the endianness changed */ static int32_t sw32(int32_t a) { int32_t tmp; char *pa = (char *)&a; char *ptmp = (char *)&tmp; int i = 1; /* for big/little endian detection */ char *p = (char *)&i; if (p[0] != 1) { /* big endian */ ptmp[0] = pa[3]; ptmp[1] = pa[2]; ptmp[2] = pa[1]; ptmp[3] = pa[0]; return tmp; } else { /* little endian */ return a; } } /* * Conversion routines between compressor and compression libraries */ /* Return the library code associated with the compressor name */ static int compname_to_clibcode(const char *compname) { if (strcmp(compname, BLOSC_BLOSCLZ_COMPNAME) == 0) return BLOSC_BLOSCLZ_LIB; if (strcmp(compname, BLOSC_LZ4_COMPNAME) == 0) return BLOSC_LZ4_LIB; if (strcmp(compname, BLOSC_LZ4HC_COMPNAME) == 0) return BLOSC_LZ4_LIB; if (strcmp(compname, BLOSC_SNAPPY_COMPNAME) == 0) return BLOSC_SNAPPY_LIB; if (strcmp(compname, BLOSC_ZLIB_COMPNAME) == 0) return BLOSC_ZLIB_LIB; return -1; } /* Return the library name associated with the compressor code */ static char *clibcode_to_clibname(int clibcode) { if (clibcode == BLOSC_BLOSCLZ_LIB) return BLOSC_BLOSCLZ_LIBNAME; if (clibcode == BLOSC_LZ4_LIB) return BLOSC_LZ4_LIBNAME; if (clibcode == BLOSC_SNAPPY_LIB) return BLOSC_SNAPPY_LIBNAME; if (clibcode == BLOSC_ZLIB_LIB) return BLOSC_ZLIB_LIBNAME; return NULL; /* should never happen */ } /* * Conversion routines between compressor names and compressor codes */ /* Get the compressor name associated with the compressor code */ int blosc_compcode_to_compname(int compcode, char **compname) { int code = -1; /* -1 means non-existent compressor code */ char *name = NULL; /* Map the compressor code */ if (compcode == BLOSC_BLOSCLZ) name = BLOSC_BLOSCLZ_COMPNAME; else if (compcode == BLOSC_LZ4) name = BLOSC_LZ4_COMPNAME; else if (compcode == BLOSC_LZ4HC) name = BLOSC_LZ4HC_COMPNAME; else if (compcode == BLOSC_SNAPPY) name = BLOSC_SNAPPY_COMPNAME; else if (compcode == BLOSC_ZLIB) name = BLOSC_ZLIB_COMPNAME; *compname = name; /* Guess if there is support for this code */ if (compcode == BLOSC_BLOSCLZ) code = BLOSC_BLOSCLZ; #if defined(HAVE_LZ4) else if (compcode == BLOSC_LZ4) code = BLOSC_LZ4; else if (compcode == BLOSC_LZ4HC) code = BLOSC_LZ4HC; #endif /* HAVE_LZ4 */ #if defined(HAVE_SNAPPY) else if (compcode == BLOSC_SNAPPY) code = BLOSC_SNAPPY; #endif /* HAVE_SNAPPY */ #if defined(HAVE_ZLIB) else if (compcode == BLOSC_ZLIB) code = BLOSC_ZLIB; #endif /* HAVE_ZLIB */ return code; } /* Get the compressor code for the compressor name. -1 if it is not available */ int blosc_compname_to_compcode(const char *compname) { int code = -1; /* -1 means non-existent compressor code */ if (strcmp(compname, BLOSC_BLOSCLZ_COMPNAME) == 0) { code = BLOSC_BLOSCLZ; } #if defined(HAVE_LZ4) else if (strcmp(compname, BLOSC_LZ4_COMPNAME) == 0) { code = BLOSC_LZ4; } else if (strcmp(compname, BLOSC_LZ4HC_COMPNAME) == 0) { code = BLOSC_LZ4HC; } #endif /* HAVE_LZ4 */ #if defined(HAVE_SNAPPY) else if (strcmp(compname, BLOSC_SNAPPY_COMPNAME) == 0) { code = BLOSC_SNAPPY; } #endif /* HAVE_SNAPPY */ #if defined(HAVE_ZLIB) else if (strcmp(compname, BLOSC_ZLIB_COMPNAME) == 0) { code = BLOSC_ZLIB; } #endif /* HAVE_ZLIB */ return code; } #if defined(HAVE_LZ4) static int lz4_wrap_compress(const char* input, size_t input_length, char* output, size_t maxout) { int cbytes; cbytes = LZ4_compress_limitedOutput(input, output, (int)input_length, (int)maxout); return cbytes; } static int lz4hc_wrap_compress(const char* input, size_t input_length, char* output, size_t maxout, int clevel) { int cbytes; if (input_length > (size_t)(2<<30)) return -1; /* input larger than 1 GB is not supported */ /* clevel for lz4hc goes up to 16, at least in LZ4 1.1.3 */ cbytes = LZ4_compressHC2_limitedOutput(input, output, (int)input_length, (int)maxout, clevel*2-1); return cbytes; } static int lz4_wrap_decompress(const char* input, size_t compressed_length, char* output, size_t maxout) { size_t cbytes; cbytes = LZ4_decompress_fast(input, output, (int)maxout); if (cbytes != compressed_length) { return 0; } return (int)maxout; } #endif /* HAVE_LZ4 */ #if defined(HAVE_SNAPPY) static int snappy_wrap_compress(const char* input, size_t input_length, char* output, size_t maxout) { snappy_status status; size_t cl = maxout; status = snappy_compress(input, input_length, output, &cl); if (status != SNAPPY_OK){ return 0; } return (int)cl; } static int snappy_wrap_decompress(const char* input, size_t compressed_length, char* output, size_t maxout) { snappy_status status; size_t ul = maxout; status = snappy_uncompress(input, compressed_length, output, &ul); if (status != SNAPPY_OK){ return 0; } return (int)ul; } #endif /* HAVE_SNAPPY */ #if defined(HAVE_ZLIB) /* zlib is not very respectful with sharing name space with others. Fortunately, its names do not collide with those already in blosc. */ static int zlib_wrap_compress(const char* input, size_t input_length, char* output, size_t maxout, int clevel) { int status; uLongf cl = maxout; status = compress2( (Bytef*)output, &cl, (Bytef*)input, (uLong)input_length, clevel); if (status != Z_OK){ return 0; } return (int)cl; } static int zlib_wrap_decompress(const char* input, size_t compressed_length, char* output, size_t maxout) { int status; uLongf ul = maxout; status = uncompress( (Bytef*)output, &ul, (Bytef*)input, (uLong)compressed_length); if (status != Z_OK){ return 0; } return (int)ul; } #endif /* HAVE_ZLIB */ /* Shuffle & compress a single block */ static int blosc_c(int32_t blocksize, int32_t leftoverblock, int32_t ntbytes, int32_t maxbytes, uint8_t *src, uint8_t *dest, uint8_t *tmp) { int32_t j, neblock, nsplits; int32_t cbytes; /* number of compressed bytes in split */ int32_t ctbytes = 0; /* number of compressed bytes in block */ int32_t maxout; int32_t typesize = params.typesize; uint8_t *_tmp; char *compname; if ((params.flags & BLOSC_DOSHUFFLE) && (typesize > 1)) { /* Shuffle this block (this makes sense only if typesize > 1) */ shuffle(typesize, blocksize, src, tmp); _tmp = tmp; } else { _tmp = src; } /* Compress for each shuffled slice split for this block. */ /* If typesize is too large, neblock is too small or we are in a leftover block, do not split at all. */ if ((typesize <= MAX_SPLITS) && (blocksize/typesize) >= MIN_BUFFERSIZE && (!leftoverblock)) { nsplits = typesize; } else { nsplits = 1; } neblock = blocksize / nsplits; for (j = 0; j < nsplits; j++) { dest += sizeof(int32_t); ntbytes += (int32_t)sizeof(int32_t); ctbytes += (int32_t)sizeof(int32_t); maxout = neblock; #if defined(HAVE_SNAPPY) if (compressor == BLOSC_SNAPPY) { /* TODO perhaps refactor this to keep the value stashed somewhere */ maxout = snappy_max_compressed_length(neblock); } #endif /* HAVE_SNAPPY */ if (ntbytes+maxout > maxbytes) { maxout = maxbytes - ntbytes; /* avoid buffer overrun */ if (maxout <= 0) { return 0; /* non-compressible block */ } } if (compressor == BLOSC_BLOSCLZ) { cbytes = blosclz_compress(params.clevel, _tmp+j*neblock, neblock, dest, maxout); } #if defined(HAVE_LZ4) else if (compressor == BLOSC_LZ4) { cbytes = lz4_wrap_compress((char *)_tmp+j*neblock, (size_t)neblock, (char *)dest, (size_t)maxout); } else if (compressor == BLOSC_LZ4HC) { cbytes = lz4hc_wrap_compress((char *)_tmp+j*neblock, (size_t)neblock, (char *)dest, (size_t)maxout, params.clevel); } #endif /* HAVE_LZ4 */ #if defined(HAVE_SNAPPY) else if (compressor == BLOSC_SNAPPY) { cbytes = snappy_wrap_compress((char *)_tmp+j*neblock, (size_t)neblock, (char *)dest, (size_t)maxout); } #endif /* HAVE_SNAPPY */ #if defined(HAVE_ZLIB) else if (compressor == BLOSC_ZLIB) { cbytes = zlib_wrap_compress((char *)_tmp+j*neblock, (size_t)neblock, (char *)dest, (size_t)maxout, params.clevel); } #endif /* HAVE_ZLIB */ else { blosc_compcode_to_compname(compressor, &compname); fprintf(stderr, "Blosc has not been compiled with '%s' ", compname); fprintf(stderr, "compression support. Please use one having it."); return -5; /* signals no compression support */ } if (cbytes > maxout) { /* Buffer overrun caused by compression (should never happen) */ return -1; } else if (cbytes < 0) { /* cbytes should never be negative */ return -2; } else if (cbytes == 0) { /* The compressor has been unable to compress data at all. */ /* Before doing the copy, check that we are not running into a buffer overflow. */ if ((ntbytes+neblock) > maxbytes) { return 0; /* Non-compressible data */ } memcpy(dest, _tmp+j*neblock, neblock); cbytes = neblock; } ((int32_t *)(dest))[-1] = sw32(cbytes); dest += cbytes; ntbytes += cbytes; ctbytes += cbytes; } /* Closes j < nsplits */ return ctbytes; } /* Decompress & unshuffle a single block */ static int blosc_d(int32_t blocksize, int32_t leftoverblock, uint8_t *src, uint8_t *dest, uint8_t *tmp, uint8_t *tmp2) { int32_t j, neblock, nsplits; int32_t nbytes; /* number of decompressed bytes in split */ int32_t cbytes; /* number of compressed bytes in split */ int32_t ctbytes = 0; /* number of compressed bytes in block */ int32_t ntbytes = 0; /* number of uncompressed bytes in block */ uint8_t *_tmp; int32_t typesize = params.typesize; int compressor_format; char *compname; if ((params.flags & BLOSC_DOSHUFFLE) && (typesize > 1)) { _tmp = tmp; } else { _tmp = dest; } compressor_format = (params.flags & 0xe0) >> 5; /* Compress for each shuffled slice split for this block. */ if ((typesize <= MAX_SPLITS) && (blocksize/typesize) >= MIN_BUFFERSIZE && (!leftoverblock)) { nsplits = typesize; } else { nsplits = 1; } neblock = blocksize / nsplits; for (j = 0; j < nsplits; j++) { cbytes = sw32(((int32_t *)(src))[0]); /* amount of compressed bytes */ src += sizeof(int32_t); ctbytes += (int32_t)sizeof(int32_t); /* Uncompress */ if (cbytes == neblock) { memcpy(_tmp, src, neblock); nbytes = neblock; } else { if (compressor_format == BLOSC_BLOSCLZ_FORMAT) { nbytes = blosclz_decompress(src, cbytes, _tmp, neblock); } #if defined(HAVE_LZ4) else if (compressor_format == BLOSC_LZ4_FORMAT) { nbytes = lz4_wrap_decompress((char *)src, (size_t)cbytes, (char*)_tmp, (size_t)neblock); } #endif /* HAVE_LZ4 */ #if defined(HAVE_SNAPPY) else if (compressor_format == BLOSC_SNAPPY_FORMAT) { nbytes = snappy_wrap_decompress((char *)src, (size_t)cbytes, (char*)_tmp, (size_t)neblock); } #endif /* HAVE_SNAPPY */ #if defined(HAVE_ZLIB) else if (compressor_format == BLOSC_ZLIB_FORMAT) { nbytes = zlib_wrap_decompress((char *)src, (size_t)cbytes, (char*)_tmp, (size_t)neblock); } #endif /* HAVE_ZLIB */ else { blosc_compcode_to_compname(compressor_format, &compname); fprintf(stderr, "Blosc has not been compiled with decompression " "support for '%s' format. ", compname); fprintf(stderr, "Please recompile for adding this support.\n"); return -5; /* signals no decompression support */ } /* Check that decompressed bytes number is correct */ if (nbytes != neblock) { return -2; } } src += cbytes; ctbytes += cbytes; _tmp += nbytes; ntbytes += nbytes; } /* Closes j < nsplits */ if ((params.flags & BLOSC_DOSHUFFLE) && (typesize > 1)) { if ((uintptr_t)dest % 16 == 0) { /* 16-bytes aligned dest. SSE2 unshuffle will work. */ unshuffle(typesize, blocksize, tmp, dest); } else { /* dest is not aligned. Use tmp2, which is aligned, and copy. */ unshuffle(typesize, blocksize, tmp, tmp2); if (tmp2 != dest) { /* Copy only when dest is not tmp2 (e.g. not blosc_getitem()) */ memcpy(dest, tmp2, blocksize); } } } /* Return the number of uncompressed bytes */ return ntbytes; } /* Serial version for compression/decompression */ static int serial_blosc(void) { int32_t j, bsize, leftoverblock; int32_t cbytes; int32_t compress = params.compress; int32_t blocksize = params.blocksize; int32_t ntbytes = params.ntbytes; int32_t flags = params.flags; int32_t maxbytes = params.maxbytes; int32_t nblocks = params.nblocks; int32_t leftover = params.nbytes % params.blocksize; int32_t *bstarts = params.bstarts; uint8_t *src = params.src; uint8_t *dest = params.dest; uint8_t *tmp = params.tmp[0]; /* tmp for thread 0 */ uint8_t *tmp2 = params.tmp2[0]; /* tmp2 for thread 0 */ for (j = 0; j < nblocks; j++) { if (compress && !(flags & BLOSC_MEMCPYED)) { bstarts[j] = sw32(ntbytes); } bsize = blocksize; leftoverblock = 0; if ((j == nblocks - 1) && (leftover > 0)) { bsize = leftover; leftoverblock = 1; } if (compress) { if (flags & BLOSC_MEMCPYED) { /* We want to memcpy only */ memcpy(dest+BLOSC_MAX_OVERHEAD+j*blocksize, src+j*blocksize, bsize); cbytes = bsize; } else { /* Regular compression */ cbytes = blosc_c(bsize, leftoverblock, ntbytes, maxbytes, src+j*blocksize, dest+ntbytes, tmp); if (cbytes == 0) { ntbytes = 0; /* uncompressible data */ break; } } } else { if (flags & BLOSC_MEMCPYED) { /* We want to memcpy only */ memcpy(dest+j*blocksize, src+BLOSC_MAX_OVERHEAD+j*blocksize, bsize); cbytes = bsize; } else { /* Regular decompression */ cbytes = blosc_d(bsize, leftoverblock, src+sw32(bstarts[j]), dest+j*blocksize, tmp, tmp2); } } if (cbytes < 0) { ntbytes = cbytes; /* error in blosc_c or blosc_d */ break; } ntbytes += cbytes; } return ntbytes; } /* Threaded version for compression/decompression */ static int parallel_blosc(void) { /* Check whether we need to restart threads */ if (!init_threads_done || pid != getpid()) { blosc_set_nthreads_(nthreads); } /* Synchronization point for all threads (wait for initialization) */ WAIT_INIT(-1); /* Synchronization point for all threads (wait for finalization) */ WAIT_FINISH(-1); if (giveup_code > 0) { /* Return the total bytes (de-)compressed in threads */ return params.ntbytes; } else { /* Compression/decompression gave up. Return error code. */ return giveup_code; } } /* Convenience functions for creating and releasing temporaries */ static int create_temporaries(void) { int32_t tid, ebsize; int32_t typesize = params.typesize; int32_t blocksize = params.blocksize; /* Extended blocksize for temporary destination. Extended blocksize is only useful for compression in parallel mode, but it doesn't hurt serial mode either. */ ebsize = blocksize + typesize * (int32_t)sizeof(int32_t); /* Create temporary area for each thread */ for (tid = 0; tid < nthreads; tid++) { uint8_t *tmp = my_malloc(blocksize); uint8_t *tmp2; if (tmp == NULL) { return -1; } params.tmp[tid] = tmp; tmp2 = my_malloc(ebsize); if (tmp2 == NULL) { return -1; } params.tmp2[tid] = tmp2; } init_temps_done = 1; /* Update params for current temporaries */ current_temp.nthreads = nthreads; current_temp.typesize = typesize; current_temp.blocksize = blocksize; return 0; } static void release_temporaries(void) { int32_t tid; /* Release buffers */ for (tid = 0; tid < nthreads; tid++) { my_free(params.tmp[tid]); my_free(params.tmp2[tid]); } init_temps_done = 0; } /* Do the compression or decompression of the buffer depending on the global params. */ static int do_job(void) { int32_t ntbytes; /* Initialize/reset temporaries if needed */ if (!init_temps_done) { int ret; ret = create_temporaries(); if (ret < 0) { return -1; } } else if (current_temp.nthreads != nthreads || current_temp.typesize != params.typesize || current_temp.blocksize != params.blocksize) { int ret; release_temporaries(); ret = create_temporaries(); if (ret < 0) { return -1; } } /* Run the serial version when nthreads is 1 or when the buffers are not much larger than blocksize */ if (nthreads == 1 || (params.nbytes / params.blocksize) <= 1) { ntbytes = serial_blosc(); } else { ntbytes = parallel_blosc(); } return ntbytes; } static int32_t compute_blocksize(int32_t clevel, int32_t typesize, int32_t nbytes) { int32_t blocksize; /* Protection against very small buffers */ if (nbytes < (int32_t)typesize) { return 1; } blocksize = nbytes; /* Start by a whole buffer as blocksize */ if (force_blocksize) { blocksize = force_blocksize; /* Check that forced blocksize is not too small nor too large */ if (blocksize < MIN_BUFFERSIZE) { blocksize = MIN_BUFFERSIZE; } } else if (nbytes >= L1*4) { blocksize = L1 * 4; /* For Zlib, increase the block sizes in a factor of 8 because it is meant for compression large blocks (it shows a big overhead in compressing small ones). */ if (compressor == BLOSC_ZLIB) { blocksize *= 8; } /* For LZ4HC, increase the block sizes in a factor of 8 because it is meant for compression large blocks (it shows a big overhead in compressing small ones). */ if (compressor == BLOSC_LZ4HC) { blocksize *= 8; } if (clevel == 0) { blocksize /= 16; } else if (clevel <= 3) { blocksize /= 8; } else if (clevel <= 5) { blocksize /= 4; } else if (clevel <= 6) { blocksize /= 2; } else if (clevel < 9) { blocksize *= 1; } else { blocksize *= 2; } } else if (nbytes > (16 * 16)) { /* align to typesize to make use of vectorized shuffles */ if (typesize == 2) { blocksize -= blocksize % (16 * 2); } else if (typesize == 4) { blocksize -= blocksize % (16 * 4); } else if (typesize == 8) { blocksize -= blocksize % (16 * 8); } else if (typesize == 16) { blocksize -= blocksize % (16 * 16); } } /* Check that blocksize is not too large */ if (blocksize > (int32_t)nbytes) { blocksize = nbytes; } /* blocksize must be a multiple of the typesize */ if (blocksize > typesize) { blocksize = blocksize / typesize * typesize; } /* blocksize must not exceed (64 KB * typesize) in order to allow BloscLZ to achieve better compression ratios (the ultimate reason for this is that hash_log in BloscLZ cannot be larger than 15) */ if ((compressor == BLOSC_BLOSCLZ) && (blocksize / typesize) > 64*KB) { blocksize = 64 * KB * typesize; } return blocksize; } #define BLOSC_UNLOCK_RETURN(val) \ return (pthread_mutex_unlock(&global_comp_mutex), val) /* The public routine for compression. See blosc.h for docstrings. */ int blosc_compress(int clevel, int doshuffle, size_t typesize, size_t nbytes, const void *src, void *dest, size_t destsize) { uint8_t *_dest=NULL; /* current pos for destination buffer */ uint8_t *flags; /* flags for header. Currently booked: - 0: shuffled? - 1: memcpy'ed? */ int32_t nbytes_; /* number of bytes in source buffer */ int32_t nblocks; /* number of total blocks in buffer */ int32_t leftover; /* extra bytes at end of buffer */ int32_t *bstarts; /* start pointers for each block */ int32_t blocksize; /* length of the block in bytes */ int32_t ntbytes = 0; /* the number of compressed bytes */ int32_t *ntbytes_; /* placeholder for bytes in output buffer */ int32_t maxbytes = (int32_t)destsize; /* maximum size for dest buffer */ int compressor_format = -1; /* the format for compressor */ /* Check buffer size limits */ if (nbytes > BLOSC_MAX_BUFFERSIZE) { /* If buffer is too large, give up. */ fprintf(stderr, "Input buffer size cannot exceed %d bytes\n", BLOSC_MAX_BUFFERSIZE); return -1; } /* We can safely do this assignation now */ nbytes_ = (int32_t)nbytes; /* Compression level */ if (clevel < 0 || clevel > 9) { /* If clevel not in 0..9, print an error */ fprintf(stderr, "`clevel` parameter must be between 0 and 9!\n"); return -10; } /* Shuffle */ if (doshuffle != 0 && doshuffle != 1) { fprintf(stderr, "`shuffle` parameter must be either 0 or 1!\n"); return -10; } /* Check typesize limits */ if (typesize > BLOSC_MAX_TYPESIZE) { /* If typesize is too large, treat buffer as an 1-byte stream. */ typesize = 1; } /* Get the blocksize */ blocksize = compute_blocksize(clevel, (int32_t)typesize, nbytes_); /* Compute number of blocks in buffer */ nblocks = nbytes_ / blocksize; leftover = nbytes_ % blocksize; nblocks = (leftover>0)? nblocks+1: nblocks; _dest = (uint8_t *)(dest); /* Write header for this block */ _dest[0] = BLOSC_VERSION_FORMAT; /* blosc format version */ if (compressor == BLOSC_BLOSCLZ) { compressor_format = BLOSC_BLOSCLZ_FORMAT; _dest[1] = BLOSC_BLOSCLZ_VERSION_FORMAT; /* blosclz format version */ } #if defined(HAVE_LZ4) else if (compressor == BLOSC_LZ4) { compressor_format = BLOSC_LZ4_FORMAT; _dest[1] = BLOSC_LZ4_VERSION_FORMAT; /* lz4 format version */ } else if (compressor == BLOSC_LZ4HC) { compressor_format = BLOSC_LZ4_FORMAT; _dest[1] = BLOSC_LZ4_VERSION_FORMAT; /* lz4hc is the same than lz4 */ } #endif /* HAVE_LZ4 */ #if defined(HAVE_SNAPPY) else if (compressor == BLOSC_SNAPPY) { compressor_format = BLOSC_SNAPPY_FORMAT; _dest[1] = BLOSC_SNAPPY_VERSION_FORMAT; /* snappy format version */ } #endif /* HAVE_SNAPPY */ #if defined(HAVE_ZLIB) else if (compressor == BLOSC_ZLIB) { compressor_format = BLOSC_ZLIB_FORMAT; _dest[1] = BLOSC_ZLIB_VERSION_FORMAT; /* zlib format version */ } #endif /* HAVE_ZLIB */ flags = _dest+2; /* flags */ _dest[2] = 0; /* zeroes flags */ _dest[3] = (uint8_t)typesize; /* type size */ _dest += 4; ((int32_t *)_dest)[0] = sw32(nbytes_); /* size of the buffer */ ((int32_t *)_dest)[1] = sw32(blocksize); /* block size */ ntbytes_ = (int32_t *)(_dest+8); /* compressed buffer size */ _dest += sizeof(int32_t)*3; bstarts = (int32_t *)_dest; /* starts for every block */ _dest += sizeof(int32_t)*nblocks; /* space for pointers to blocks */ ntbytes = (int32_t)(_dest - (uint8_t *)dest); if (clevel == 0) { /* Compression level 0 means buffer to be memcpy'ed */ *flags |= BLOSC_MEMCPYED; } if (nbytes_ < MIN_BUFFERSIZE) { /* Buffer is too small. Try memcpy'ing. */ *flags |= BLOSC_MEMCPYED; } if (doshuffle == 1) { /* Shuffle is active */ *flags |= BLOSC_DOSHUFFLE; /* bit 0 set to one in flags */ } *flags |= compressor_format << 5; /* compressor format start at bit 5 */ /* Take global lock for the time of compression */ pthread_mutex_lock(&global_comp_mutex); /* Populate parameters for compression routines */ params.compress = 1; params.clevel = clevel; params.flags = (int32_t)*flags; params.typesize = (int32_t)typesize; params.blocksize = blocksize; params.ntbytes = ntbytes; params.nbytes = nbytes_; params.maxbytes = maxbytes; params.nblocks = nblocks; params.leftover = leftover; params.bstarts = bstarts; params.src = (uint8_t *)src; params.dest = (uint8_t *)dest; if (!(*flags & BLOSC_MEMCPYED)) { /* Do the actual compression */ ntbytes = do_job(); if (ntbytes < 0) { BLOSC_UNLOCK_RETURN(-1); } if ((ntbytes == 0) && (nbytes_+BLOSC_MAX_OVERHEAD <= maxbytes)) { /* Last chance for fitting `src` buffer in `dest`. Update flags and do a memcpy later on. */ *flags |= BLOSC_MEMCPYED; params.flags |= BLOSC_MEMCPYED; } } if (*flags & BLOSC_MEMCPYED) { if (nbytes_+BLOSC_MAX_OVERHEAD > maxbytes) { /* We are exceeding maximum output size */ ntbytes = 0; } else if (((nbytes_ % L1) == 0) || (nthreads > 1)) { /* More effective with large buffers that are multiples of the cache size or multi-cores */ params.ntbytes = BLOSC_MAX_OVERHEAD; ntbytes = do_job(); if (ntbytes < 0) { BLOSC_UNLOCK_RETURN(-1); } } else { memcpy((uint8_t *)dest+BLOSC_MAX_OVERHEAD, src, nbytes_); ntbytes = nbytes_ + BLOSC_MAX_OVERHEAD; } } /* Set the number of compressed bytes in header */ *ntbytes_ = sw32(ntbytes); /* Release global lock */ pthread_mutex_unlock(&global_comp_mutex); assert((int32_t)ntbytes <= (int32_t)maxbytes); return ntbytes; } /* The public routine for decompression. See blosc.h for docstrings. */ int blosc_decompress(const void *src, void *dest, size_t destsize) { uint8_t *_src=NULL; /* current pos for source buffer */ uint8_t version, versionlz; /* versions for compressed header */ uint8_t flags; /* flags for header */ int32_t ntbytes; /* the number of uncompressed bytes */ int32_t nblocks; /* number of total blocks in buffer */ int32_t leftover; /* extra bytes at end of buffer */ int32_t *bstarts; /* start pointers for each block */ int32_t typesize, blocksize, nbytes, ctbytes; _src = (uint8_t *)(src); /* Read the header block */ version = _src[0]; /* blosc format version */ versionlz = _src[1]; /* blosclz format version */ flags = _src[2]; /* flags */ typesize = (int32_t)_src[3]; /* typesize */ _src += 4; nbytes = sw32(((int32_t *)_src)[0]); /* buffer size */ blocksize = sw32(((int32_t *)_src)[1]); /* block size */ ctbytes = sw32(((int32_t *)_src)[2]); /* compressed buffer size */ version += 0; /* shut up compiler warning */ versionlz += 0; /* shut up compiler warning */ ctbytes += 0; /* shut up compiler warning */ _src += sizeof(int32_t)*3; bstarts = (int32_t *)_src; /* Compute some params */ /* Total blocks */ nblocks = nbytes / blocksize; leftover = nbytes % blocksize; nblocks = (leftover>0)? nblocks+1: nblocks; _src += sizeof(int32_t)*nblocks; /* Check that we have enough space to decompress */ if (nbytes > (int32_t)destsize) { return -1; } /* Take global lock for the time of decompression */ pthread_mutex_lock(&global_comp_mutex); /* Populate parameters for decompression routines */ params.compress = 0; params.clevel = 0; /* specific for compression */ params.flags = (int32_t)flags; params.typesize = typesize; params.blocksize = blocksize; params.ntbytes = 0; params.nbytes = nbytes; params.nblocks = nblocks; params.leftover = leftover; params.bstarts = bstarts; params.src = (uint8_t *)src; params.dest = (uint8_t *)dest; /* Check whether this buffer is memcpy'ed */ if (flags & BLOSC_MEMCPYED) { if (((nbytes % L1) == 0) || (nthreads > 1)) { /* More effective with large buffers that are multiples of the cache size or multi-cores */ ntbytes = do_job(); if (ntbytes < 0) { BLOSC_UNLOCK_RETURN(-1); } } else { memcpy(dest, (uint8_t *)src+BLOSC_MAX_OVERHEAD, nbytes); ntbytes = nbytes; } } else { /* Do the actual decompression */ ntbytes = do_job(); if (ntbytes < 0) { BLOSC_UNLOCK_RETURN(-1); } } /* Release global lock */ pthread_mutex_unlock(&global_comp_mutex); assert(ntbytes <= (int32_t)destsize); return ntbytes; } /* Specific routine optimized for decompression a small number of items out of a compressed chunk. This does not use threads because it would affect negatively to performance. */ int blosc_getitem(const void *src, int start, int nitems, void *dest) { uint8_t *_src=NULL; /* current pos for source buffer */ uint8_t version, versionlz; /* versions for compressed header */ uint8_t flags; /* flags for header */ int32_t ntbytes = 0; /* the number of uncompressed bytes */ int32_t nblocks; /* number of total blocks in buffer */ int32_t leftover; /* extra bytes at end of buffer */ int32_t *bstarts; /* start pointers for each block */ uint8_t *tmp = params.tmp[0]; /* tmp for thread 0 */ uint8_t *tmp2 = params.tmp2[0]; /* tmp2 for thread 0 */ int tmp_init = 0; int32_t typesize, blocksize, nbytes, ctbytes; int32_t j, bsize, bsize2, leftoverblock; int32_t cbytes, startb, stopb; int stop = start + nitems; _src = (uint8_t *)(src); /* Take global lock */ pthread_mutex_lock(&global_comp_mutex); /* Read the header block */ version = _src[0]; /* blosc format version */ versionlz = _src[1]; /* blosclz format version */ flags = _src[2]; /* flags */ typesize = (int32_t)_src[3]; /* typesize */ _src += 4; nbytes = sw32(((int32_t *)_src)[0]); /* buffer size */ blocksize = sw32(((int32_t *)_src)[1]); /* block size */ ctbytes = sw32(((int32_t *)_src)[2]); /* compressed buffer size */ version += 0; /* shut up compiler warning */ versionlz += 0; /* shut up compiler warning */ ctbytes += 0; /* shut up compiler warning */ _src += sizeof(int32_t)*3; bstarts = (int32_t *)_src; /* Compute some params */ /* Total blocks */ nblocks = nbytes / blocksize; leftover = nbytes % blocksize; nblocks = (leftover>0)? nblocks+1: nblocks; _src += sizeof(int32_t)*nblocks; /* Check region boundaries */ if ((start < 0) || (start*typesize > nbytes)) { fprintf(stderr, "`start` out of bounds"); BLOSC_UNLOCK_RETURN(-1); } if ((stop < 0) || (stop*typesize > nbytes)) { fprintf(stderr, "`start`+`nitems` out of bounds"); BLOSC_UNLOCK_RETURN(-1); } /* Parameters needed by blosc_d */ params.typesize = typesize; params.flags = flags; /* Initialize temporaries if needed */ if (tmp == NULL || tmp2 == NULL || current_temp.blocksize < blocksize) { tmp = my_malloc(blocksize); if (tmp == NULL) { BLOSC_UNLOCK_RETURN(-1); } tmp2 = my_malloc(blocksize); if (tmp2 == NULL) { BLOSC_UNLOCK_RETURN(-1); } tmp_init = 1; } for (j = 0; j < nblocks; j++) { bsize = blocksize; leftoverblock = 0; if ((j == nblocks - 1) && (leftover > 0)) { bsize = leftover; leftoverblock = 1; } /* Compute start & stop for each block */ startb = start * typesize - j * blocksize; stopb = stop * typesize - j * blocksize; if ((startb >= (int)blocksize) || (stopb <= 0)) { continue; } if (startb < 0) { startb = 0; } if (stopb > (int)blocksize) { stopb = blocksize; } bsize2 = stopb - startb; /* Do the actual data copy */ if (flags & BLOSC_MEMCPYED) { /* We want to memcpy only */ memcpy((uint8_t *)dest + ntbytes, (uint8_t *)src + BLOSC_MAX_OVERHEAD + j*blocksize + startb, bsize2); cbytes = bsize2; } else { /* Regular decompression. Put results in tmp2. */ cbytes = blosc_d(bsize, leftoverblock, (uint8_t *)src+sw32(bstarts[j]), tmp2, tmp, tmp2); if (cbytes < 0) { ntbytes = cbytes; break; } /* Copy to destination */ memcpy((uint8_t *)dest + ntbytes, tmp2 + startb, bsize2); cbytes = bsize2; } ntbytes += cbytes; } /* Release global lock */ pthread_mutex_unlock(&global_comp_mutex); if (tmp_init) { my_free(tmp); my_free(tmp2); } return ntbytes; } /* Decompress & unshuffle several blocks in a single thread */ static void *t_blosc(void *tids) { int32_t tid = *(int32_t *)tids; int32_t cbytes, ntdest; int32_t tblocks; /* number of blocks per thread */ int32_t leftover2; int32_t tblock; /* limit block on a thread */ int32_t nblock_; /* private copy of nblock */ int32_t bsize, leftoverblock; /* Parameters for threads */ int32_t blocksize; int32_t ebsize; int32_t compress; int32_t maxbytes; int32_t ntbytes; int32_t flags; int32_t nblocks; int32_t leftover; int32_t *bstarts; uint8_t *src; uint8_t *dest; uint8_t *tmp; uint8_t *tmp2; while (1) { init_sentinels_done = 0; /* sentinels have to be initialised yet */ /* Synchronization point for all threads (wait for initialization) */ WAIT_INIT(NULL); /* Check if thread has been asked to return */ if (end_threads) { return(NULL); } pthread_mutex_lock(&count_mutex); if (!init_sentinels_done) { /* Set sentinels and other global variables */ giveup_code = 1; /* no error code initially */ nblock = -1; /* block counter */ init_sentinels_done = 1; /* sentinels have been initialised */ } pthread_mutex_unlock(&count_mutex); /* Get parameters for this thread before entering the main loop */ blocksize = params.blocksize; ebsize = blocksize + params.typesize * (int32_t)sizeof(int32_t); compress = params.compress; flags = params.flags; maxbytes = params.maxbytes; nblocks = params.nblocks; leftover = params.leftover; bstarts = params.bstarts; src = params.src; dest = params.dest; tmp = params.tmp[tid]; tmp2 = params.tmp2[tid]; ntbytes = 0; /* only useful for decompression */ if (compress && !(flags & BLOSC_MEMCPYED)) { /* Compression always has to follow the block order */ pthread_mutex_lock(&count_mutex); nblock++; nblock_ = nblock; pthread_mutex_unlock(&count_mutex); tblock = nblocks; } else { /* Decompression can happen using any order. We choose sequential block order on each thread */ /* Blocks per thread */ tblocks = nblocks / nthreads; leftover2 = nblocks % nthreads; tblocks = (leftover2>0)? tblocks+1: tblocks; nblock_ = tid*tblocks; tblock = nblock_ + tblocks; if (tblock > nblocks) { tblock = nblocks; } } /* Loop over blocks */ leftoverblock = 0; while ((nblock_ < tblock) && giveup_code > 0) { bsize = blocksize; if (nblock_ == (nblocks - 1) && (leftover > 0)) { bsize = leftover; leftoverblock = 1; } if (compress) { if (flags & BLOSC_MEMCPYED) { /* We want to memcpy only */ memcpy(dest+BLOSC_MAX_OVERHEAD+nblock_*blocksize, src+nblock_*blocksize, bsize); cbytes = bsize; } else { /* Regular compression */ cbytes = blosc_c(bsize, leftoverblock, 0, ebsize, src+nblock_*blocksize, tmp2, tmp); } } else { if (flags & BLOSC_MEMCPYED) { /* We want to memcpy only */ memcpy(dest+nblock_*blocksize, src+BLOSC_MAX_OVERHEAD+nblock_*blocksize, bsize); cbytes = bsize; } else { cbytes = blosc_d(bsize, leftoverblock, src+sw32(bstarts[nblock_]), dest+nblock_*blocksize, tmp, tmp2); } } /* Check whether current thread has to giveup */ if (giveup_code <= 0) { break; } /* Check results for the compressed/decompressed block */ if (cbytes < 0) { /* compr/decompr failure */ /* Set giveup_code error */ pthread_mutex_lock(&count_mutex); giveup_code = cbytes; pthread_mutex_unlock(&count_mutex); break; } if (compress && !(flags & BLOSC_MEMCPYED)) { /* Start critical section */ pthread_mutex_lock(&count_mutex); ntdest = params.ntbytes; bstarts[nblock_] = sw32(ntdest); /* update block start counter */ if ( (cbytes == 0) || (ntdest+cbytes > (int32_t)maxbytes) ) { giveup_code = 0; /* uncompressible buffer */ pthread_mutex_unlock(&count_mutex); break; } nblock++; nblock_ = nblock; params.ntbytes += cbytes; /* update return bytes counter */ pthread_mutex_unlock(&count_mutex); /* End of critical section */ /* Copy the compressed buffer to destination */ memcpy(dest+ntdest, tmp2, cbytes); } else { nblock_++; /* Update counter for this thread */ ntbytes += cbytes; } } /* closes while (nblock_) */ /* Sum up all the bytes decompressed */ if ((!compress || (flags & BLOSC_MEMCPYED)) && giveup_code > 0) { /* Update global counter for all threads (decompression only) */ pthread_mutex_lock(&count_mutex); params.ntbytes += ntbytes; pthread_mutex_unlock(&count_mutex); } /* Meeting point for all threads (wait for finalization) */ WAIT_FINISH(NULL); } /* closes while(1) */ /* This should never be reached, but anyway */ return(NULL); } static int init_threads(void) { int32_t tid; int rc2; /* Initialize mutex and condition variable objects */ pthread_mutex_init(&count_mutex, NULL); /* Barrier initialization */ #ifdef _POSIX_BARRIERS_MINE pthread_barrier_init(&barr_init, NULL, nthreads+1); pthread_barrier_init(&barr_finish, NULL, nthreads+1); #else pthread_mutex_init(&count_threads_mutex, NULL); pthread_cond_init(&count_threads_cv, NULL); count_threads = 0; /* Reset threads counter */ #endif #if !defined(_WIN32) /* Initialize and set thread detached attribute */ pthread_attr_init(&ct_attr); pthread_attr_setdetachstate(&ct_attr, PTHREAD_CREATE_JOINABLE); #endif /* Finally, create the threads in detached state */ for (tid = 0; tid < nthreads; tid++) { tids[tid] = tid; #if !defined(_WIN32) rc2 = pthread_create(&threads[tid], &ct_attr, t_blosc, (void *)&tids[tid]); #else rc2 = pthread_create(&threads[tid], NULL, t_blosc, (void *)&tids[tid]); #endif if (rc2) { fprintf(stderr, "ERROR; return code from pthread_create() is %d\n", rc2); fprintf(stderr, "\tError detail: %s\n", strerror(rc2)); return(-1); } } init_threads_done = 1; /* Initialization done! */ pid = (int)getpid(); /* save the PID for this process */ return(0); } void blosc_init(void) { /* Init global lock */ pthread_mutex_init(&global_comp_mutex, NULL); init_lib = 1; } int blosc_set_nthreads(int nthreads_new) { int ret; /* Check if should initialize (implementing previous 1.2.3 behaviour, where calling blosc_set_nthreads was enough) */ if (!init_lib) blosc_init(); /* Take global lock */ pthread_mutex_lock(&global_comp_mutex); ret = blosc_set_nthreads_(nthreads_new); /* Release global lock */ pthread_mutex_unlock(&global_comp_mutex); return ret; } int blosc_set_nthreads_(int nthreads_new) { int32_t nthreads_old = nthreads; int32_t t; int rc2; void *status; if (nthreads_new > BLOSC_MAX_THREADS) { fprintf(stderr, "Error. nthreads cannot be larger than BLOSC_MAX_THREADS (%d)", BLOSC_MAX_THREADS); return -1; } else if (nthreads_new <= 0) { fprintf(stderr, "Error. nthreads must be a positive integer"); return -1; } /* Only join threads if they are not initialized or if our PID is different from that in pid var (probably means that we are a subprocess, and thus threads are non-existent). */ if (nthreads > 1 && init_threads_done && pid == getpid()) { /* Tell all existing threads to finish */ end_threads = 1; /* Synchronization point for all threads (wait for initialization) */ WAIT_INIT(-1); /* Join exiting threads */ for (t=0; t 1 && (!init_threads_done || pid != getpid())) { init_threads(); } return nthreads_old; } int blosc_set_compressor(const char *compname) { int code; /* Check if should initialize */ if (!init_lib) blosc_init(); code = blosc_compname_to_compcode(compname); /* Take global lock */ pthread_mutex_lock(&global_comp_mutex); compressor = code; /* Release global lock */ pthread_mutex_unlock(&global_comp_mutex); return code; } char* blosc_list_compressors(void) { static int compressors_list_done = 0; static char ret[256]; if (compressors_list_done) return ret; ret[0] = '\0'; strcat(ret, BLOSC_BLOSCLZ_COMPNAME); #if defined(HAVE_LZ4) strcat(ret, ","); strcat(ret, BLOSC_LZ4_COMPNAME); strcat(ret, ","); strcat(ret, BLOSC_LZ4HC_COMPNAME); #endif /* HAVE_LZ4 */ #if defined(HAVE_SNAPPY) strcat(ret, ","); strcat(ret, BLOSC_SNAPPY_COMPNAME); #endif /* HAVE_SNAPPY */ #if defined(HAVE_ZLIB) strcat(ret, ","); strcat(ret, BLOSC_ZLIB_COMPNAME); #endif /* HAVE_ZLIB */ compressors_list_done = 1; return ret; } int blosc_get_complib_info(char *compname, char **complib, char **version) { int clibcode; char *clibname; char *clibversion = "unknown"; char sbuffer[256]; clibcode = compname_to_clibcode(compname); clibname = clibcode_to_clibname(clibcode); /* complib version */ if (clibcode == BLOSC_BLOSCLZ_LIB) { clibversion = BLOSCLZ_VERSION_STRING; } #if defined(HAVE_LZ4) else if (clibcode == BLOSC_LZ4_LIB) { #if defined(LZ4_VERSION_MAJOR) sprintf(sbuffer, "%d.%d.%d", LZ4_VERSION_MAJOR, LZ4_VERSION_MINOR, LZ4_VERSION_RELEASE); clibversion = sbuffer; #endif /* LZ4_VERSION_MAJOR */ } #endif /* HAVE_LZ4 */ #if defined(HAVE_SNAPPY) else if (clibcode == BLOSC_SNAPPY_LIB) { #if defined(SNAPPY_VERSION) sprintf(sbuffer, "%d.%d.%d", SNAPPY_MAJOR, SNAPPY_MINOR, SNAPPY_PATCHLEVEL); clibversion = sbuffer; #endif /* SNAPPY_VERSION */ } #endif /* HAVE_SNAPPY */ #if defined(HAVE_ZLIB) else if (clibcode == BLOSC_ZLIB_LIB) { clibversion = ZLIB_VERSION; } #endif /* HAVE_ZLIB */ *complib = strdup(clibname); *version = strdup(clibversion); return clibcode; } /* Free possible memory temporaries and thread resources */ int blosc_free_resources(void) { int32_t t; int rc2; void *status; /* Take global lock */ pthread_mutex_lock(&global_comp_mutex); /* Release temporaries */ if (init_temps_done) { release_temporaries(); } /* Finish the possible thread pool */ if (nthreads > 1 && init_threads_done) { /* Tell all existing threads to finish */ end_threads = 1; /* Synchronization point for all threads (wait for initialization) */ WAIT_INIT(-1); /* Join exiting threads */ for (t=0; t> 5; complib = clibcode_to_clibname(clibcode); return complib; } /* Force the use of a specific blocksize. If 0, an automatic blocksize will be used (the default). */ void blosc_set_blocksize(size_t size) { /* Take global lock */ pthread_mutex_lock(&global_comp_mutex); force_blocksize = (int32_t)size; /* Release global lock */ pthread_mutex_unlock(&global_comp_mutex); } PyTables-v.3.1.1/c-blosc/blosc/blosc.h000066400000000000000000000256061231437614300174170ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Suffling and Compression Library Author: Francesc Alted See LICENSES/BLOSC.txt for details about copyright and rights to use. **********************************************************************/ #include #ifndef BLOSC_H #define BLOSC_H /* Version numbers */ #define BLOSC_VERSION_MAJOR 1 /* for major interface/format changes */ #define BLOSC_VERSION_MINOR 3 /* for minor interface/format changes */ #define BLOSC_VERSION_RELEASE 5 /* for tweaks, bug-fixes, or development */ #define BLOSC_VERSION_STRING "1.3.5" /* string version. Sync with above! */ #define BLOSC_VERSION_REVISION "$Rev$" /* revision version */ #define BLOSC_VERSION_DATE "$Date:: 2014-03-22 #$" /* date version */ #define BLOSCLZ_VERSION_STRING "1.0.1" /* the internal compressor version */ /* The *_VERS_FORMAT should be just 1-byte long */ #define BLOSC_VERSION_FORMAT 2 /* Blosc format version, starting at 1 */ /* Minimum header length */ #define BLOSC_MIN_HEADER_LENGTH 16 /* The maximum overhead during compression in bytes. This equals to BLOSC_MIN_HEADER_LENGTH now, but can be higher in future implementations */ #define BLOSC_MAX_OVERHEAD BLOSC_MIN_HEADER_LENGTH /* Maximum buffer size to be compressed */ #define BLOSC_MAX_BUFFERSIZE (INT_MAX - BLOSC_MAX_OVERHEAD) /* Maximum typesize before considering buffer as a stream of bytes */ #define BLOSC_MAX_TYPESIZE 255 /* Cannot be larger than 255 */ /* The maximum number of threads (for some static arrays) */ #define BLOSC_MAX_THREADS 256 /* Codes for internal flags (see blosc_cbuffer_metainfo) */ #define BLOSC_DOSHUFFLE 0x1 #define BLOSC_MEMCPYED 0x2 /* Codes for the different compressors shipped with Blosc */ #define BLOSC_BLOSCLZ 0 #define BLOSC_LZ4 1 #define BLOSC_LZ4HC 2 #define BLOSC_SNAPPY 3 #define BLOSC_ZLIB 4 /* Names for the different compressors shipped with Blosc */ #define BLOSC_BLOSCLZ_COMPNAME "blosclz" #define BLOSC_LZ4_COMPNAME "lz4" #define BLOSC_LZ4HC_COMPNAME "lz4hc" #define BLOSC_SNAPPY_COMPNAME "snappy" #define BLOSC_ZLIB_COMPNAME "zlib" /* Codes for the different compression libraries shipped with Blosc */ #define BLOSC_BLOSCLZ_LIB 0 #define BLOSC_LZ4_LIB 1 #define BLOSC_SNAPPY_LIB 2 #define BLOSC_ZLIB_LIB 3 /* Names for the different compression libraries shipped with Blosc */ #define BLOSC_BLOSCLZ_LIBNAME "BloscLZ" #define BLOSC_LZ4_LIBNAME "LZ4" #define BLOSC_SNAPPY_LIBNAME "Snappy" #define BLOSC_ZLIB_LIBNAME "Zlib" /* The codes for compressor formats shipped with Blosc (code must be < 8) */ #define BLOSC_BLOSCLZ_FORMAT BLOSC_BLOSCLZ_LIB #define BLOSC_LZ4_FORMAT BLOSC_LZ4_LIB /* LZ4HC and LZ4 share the same format */ #define BLOSC_LZ4HC_FORMAT BLOSC_LZ4_LIB #define BLOSC_SNAPPY_FORMAT BLOSC_SNAPPY_LIB #define BLOSC_ZLIB_FORMAT BLOSC_ZLIB_LIB /* The version formats for compressors shipped with Blosc */ /* All versions here starts at 1 */ #define BLOSC_BLOSCLZ_VERSION_FORMAT 1 #define BLOSC_LZ4_VERSION_FORMAT 1 #define BLOSC_LZ4HC_VERSION_FORMAT 1 /* LZ4HC and LZ4 share the same format */ #define BLOSC_SNAPPY_VERSION_FORMAT 1 #define BLOSC_ZLIB_VERSION_FORMAT 1 /** Initialize the Blosc library. You must call this previous to any other Blosc call, and make sure that you call this in a non-threaded environment. Other Blosc calls can be called in a threaded environment, if desired. */ void blosc_init(void); /** Destroy the Blosc library environment. You must call this after to you are done with all the Blosc calls, and make sure that you call this in a non-threaded environment. */ void blosc_destroy(void); /** Compress a block of data in the `src` buffer and returns the size of compressed block. The size of `src` buffer is specified by `nbytes`. There is not a minimum for `src` buffer size (`nbytes`). `clevel` is the desired compression level and must be a number between 0 (no compression) and 9 (maximum compression). `doshuffle` specifies whether the shuffle compression preconditioner should be applied or not. 0 means not applying it and 1 means applying it. `typesize` is the number of bytes for the atomic type in binary `src` buffer. This is mainly useful for the shuffle preconditioner. Only a typesize > 1 will allow the shuffle to work. The `dest` buffer must have at least the size of `destsize`. Blosc guarantees that if you set `destsize` to, at least, (`nbytes`+BLOSC_MAX_OVERHEAD), the compression will always succeed. The `src` buffer and the `dest` buffer can not overlap. Compression is memory safe and guaranteed not to write the `dest` buffer more than what is specified in `destsize`. However, it is not re-entrant and not thread-safe (despite the fact that it uses threads internally). If `src` buffer cannot be compressed into `destsize`, the return value is zero and you should discard the contents of the `dest` buffer. A negative return value means that an internal error happened. This should never happen. If you see this, please report it back together with the buffer data causing this and compression settings. */ int blosc_compress(int clevel, int doshuffle, size_t typesize, size_t nbytes, const void *src, void *dest, size_t destsize); /** Decompress a block of compressed data in `src`, put the result in `dest` and returns the size of the decompressed block. The `src` buffer and the `dest` buffer can not overlap. Decompression is memory safe and guaranteed not to write the `dest` buffer more than what is specified in `destsize`. However, it is not re-entrant and not thread-safe (despite the fact that it uses threads internally). If an error occurs, e.g. the compressed data is corrupted or the output buffer is not large enough, then 0 (zero) or a negative value will be returned instead. */ int blosc_decompress(const void *src, void *dest, size_t destsize); /** Get `nitems` (of typesize size) in `src` buffer starting in `start`. The items are returned in `dest` buffer, which has to have enough space for storing all items. Returns the number of bytes copied to `dest` or a negative value if some error happens. */ int blosc_getitem(const void *src, int start, int nitems, void *dest); /** Initialize a pool of threads for compression/decompression. If `nthreads` is 1, then the serial version is chosen and a possible previous existing pool is ended. If this is not called, `nthreads` is set to 1 internally. Returns the previous number of threads. */ int blosc_set_nthreads(int nthreads); /** Select the compressor to be used. The supported ones are "blosclz", "lz4", "lz4hc", "snappy" and "zlib". If this function is not called, then "blosclz" will be used. In case the compressor is not recognized, or there is not support for it in this build, it returns a -1. Else it returns the code for the compressor (>=0). */ int blosc_set_compressor(const char* compname); /** Get the `compname` associated with the `compcode`. If the compressor code is not recognized, or there is not support for it in this build, -1 is returned. Else, the compressor code is returned. */ int blosc_compcode_to_compname(int compcode, char **compname); /** Return the compressor code associated with the compressor name. If the compressor name is not recognized, or there is not support for it in this build, -1 is returned instead. */ int blosc_compname_to_compcode(const char *compname); /** Get a list of compressors supported in the current build. The returned value is a string with a concatenation of "blosclz", "lz4", "lz4hc", "snappy" or "zlib" separated by commas, depending on which ones are present in the build. This function does not leak, so you should not free() the returned list. This function should always succeed. */ char* blosc_list_compressors(void); /** Get info from compression libraries included in the current build. In `compname` you pass the compressor name that you want info from. In `complib` and `version` you get the compression library name and version (if available) as output. In `complib` and `version` you get a pointer to the compressor library name and the version in string format respectively. After using the name and version, you should free() them so as to avoid leaks. If the compressor is supported, it returns the code for the library (>=0). If it is not supported, this function returns -1. */ int blosc_get_complib_info(char *compname, char **complib, char **version); /** Free possible memory temporaries and thread resources. Use this when you are not going to use Blosc for a long while. In case of problems releasing the resources, it returns a negative number, else it returns 0. */ int blosc_free_resources(void); /** Return information about a compressed buffer, namely the number of uncompressed bytes (`nbytes`) and compressed (`cbytes`). It also returns the `blocksize` (which is used internally for doing the compression by blocks). You only need to pass the first BLOSC_MIN_HEADER_LENGTH bytes of a compressed buffer for this call to work. This function should always succeed. */ void blosc_cbuffer_sizes(const void *cbuffer, size_t *nbytes, size_t *cbytes, size_t *blocksize); /** Return information about a compressed buffer, namely the type size (`typesize`), as well as some internal `flags`. The `flags` is a set of bits, where the currently used ones are: * bit 0: whether the shuffle filter has been applied or not * bit 1: whether the internal buffer is a pure memcpy or not You can use the `BLOSC_DOSHUFFLE` and `BLOSC_MEMCPYED` symbols for extracting the interesting bits (e.g. ``flags & BLOSC_DOSHUFFLE`` says whether the buffer is shuffled or not). This function should always succeed. */ void blosc_cbuffer_metainfo(const void *cbuffer, size_t *typesize, int *flags); /** Return information about a compressed buffer, namely the internal Blosc format version (`version`) and the format for the internal Lempel-Ziv compressor used (`versionlz`). This function should always succeed. */ void blosc_cbuffer_versions(const void *cbuffer, int *version, int *versionlz); /** Return the compressor library/format used in a compressed buffer. This function should always succeed. */ char *blosc_cbuffer_complib(const void *cbuffer); /********************************************************************* Low-level functions follows. Use them only if you are an expert! *********************************************************************/ /** Force the use of a specific blocksize. If 0, an automatic blocksize will be used (the default). */ void blosc_set_blocksize(size_t blocksize); #endif PyTables-v.3.1.1/c-blosc/blosc/blosclz.c000066400000000000000000000270071231437614300177550ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Suffling and Compression Library Author: Francesc Alted Creation date: 2009-05-20 See LICENSES/BLOSC.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* The code in this file is heavily based on FastLZ, a lightning-fast lossless compression library. See LICENSES/FASTLZ.txt for details. **********************************************************************/ #include #include #include #include "blosclz.h" #if defined(_WIN32) && !defined(__MINGW32__) #include #include "win32/stdint-windows.h" #else #include #endif /* _WIN32 */ /* * Prevent accessing more than 8-bit at once, except on x86 architectures. */ #if !defined(BLOSCLZ_STRICT_ALIGN) #define BLOSCLZ_STRICT_ALIGN #if defined(__i386__) || defined(__386) || defined (__amd64) /* GNU C, Sun Studio */ #undef BLOSCLZ_STRICT_ALIGN #elif defined(__i486__) || defined(__i586__) || defined(__i686__) /* GNU C */ #undef BLOSCLZ_STRICT_ALIGN #elif defined(_M_IX86) /* Intel, MSVC */ #undef BLOSCLZ_STRICT_ALIGN #elif defined(__386) #undef BLOSCLZ_STRICT_ALIGN #elif defined(_X86_) /* MinGW */ #undef BLOSCLZ_STRICT_ALIGN #elif defined(__I86__) /* Digital Mars */ #undef BLOSCLZ_STRICT_ALIGN #endif #endif /* * Always check for bound when decompressing. * Generally it is best to leave it defined. */ #define BLOSCLZ_SAFE /* * Give hints to the compiler for branch prediction optimization. */ #if defined(__GNUC__) && (__GNUC__ > 2) #define BLOSCLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1)) #define BLOSCLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0)) #else #define BLOSCLZ_EXPECT_CONDITIONAL(c) (c) #define BLOSCLZ_UNEXPECT_CONDITIONAL(c) (c) #endif /* * Use inlined functions for supported systems. */ #if defined(_MSC_VER) && !defined(__cplusplus) /* Visual Studio */ #define inline __inline /* Visual C is not C99, but supports some kind of inline */ #endif #define MAX_COPY 32 #define MAX_LEN 264 /* 256 + 8 */ #define MAX_DISTANCE 8191 #define MAX_FARDISTANCE (65535+MAX_DISTANCE-1) #ifdef BLOSCLZ_STRICT_ALIGN #define BLOSCLZ_READU16(p) ((p)[0] | (p)[1]<<8) #else #define BLOSCLZ_READU16(p) *((const uint16_t*)(p)) #endif static inline int32_t hash_function(uint8_t* p, uint8_t hash_log) { int32_t v; v = BLOSCLZ_READU16(p); v ^= BLOSCLZ_READU16(p+1)^(v>>(16-hash_log)); v &= (1 << hash_log) - 1; return v; } #define IP_BOUNDARY 2 int blosclz_compress(int opt_level, const void* input, int length, void* output, int maxout) { uint8_t* ip = (uint8_t*) input; uint8_t* ibase = (uint8_t*) input; uint8_t* ip_bound = ip + length - IP_BOUNDARY; uint8_t* ip_limit = ip + length - 12; uint8_t* op = (uint8_t*) output; /* Hash table depends on the opt level. Hash_log cannot be larger than 15. */ int8_t hash_log_[10] = {-1, 8, 9, 9, 11, 11, 12, 13, 14, 15}; uint8_t hash_log = hash_log_[opt_level]; uint16_t hash_size = 1 << hash_log; uint16_t *htab; uint8_t* op_limit; int32_t hval; uint8_t copy; double maxlength_[10] = {-1, .1, .15, .2, .5, .7, .85, .925, .975, 1.0}; int32_t maxlength = (int32_t) (length * maxlength_[opt_level]); if (maxlength > (int32_t) maxout) { maxlength = (int32_t) maxout; } op_limit = op + maxlength; /* output buffer cannot be less than 66 bytes or we can get into problems. As output is usually the same length than input, we take input length. */ if (length < 66) { return 0; /* Mark this as uncompressible */ } htab = (uint16_t *) calloc(hash_size, sizeof(uint16_t)); /* sanity check */ if(BLOSCLZ_UNEXPECT_CONDITIONAL(length < 4)) { if(length) { /* create literal copy only */ *op++ = length-1; ip_bound++; while(ip <= ip_bound) *op++ = *ip++; free(htab); return length+1; } else goto out; } /* we start with literal copy */ copy = 2; *op++ = MAX_COPY-1; *op++ = *ip++; *op++ = *ip++; /* main loop */ while(BLOSCLZ_EXPECT_CONDITIONAL(ip < ip_limit)) { const uint8_t* ref; int32_t distance; int32_t len = 3; /* minimum match length */ uint8_t* anchor = ip; /* comparison starting-point */ /* check for a run */ if(ip[0] == ip[-1] && BLOSCLZ_READU16(ip-1)==BLOSCLZ_READU16(ip+1)) { distance = 1; ip += 3; ref = anchor - 1 + 3; goto match; } /* find potential match */ hval = hash_function(ip, hash_log); ref = ibase + htab[hval]; /* update hash table */ htab[hval] = (uint16_t)(anchor - ibase); /* calculate distance to the match */ distance = (int32_t)(anchor - ref); /* is this a match? check the first 3 bytes */ if (distance==0 || (distance >= MAX_FARDISTANCE) || *ref++ != *ip++ || *ref++!=*ip++ || *ref++!=*ip++) goto literal; /* far, needs at least 5-byte match */ if (distance >= MAX_DISTANCE) { if (*ip++ != *ref++ || *ip++ != *ref++) goto literal; len += 2; } match: /* last matched byte */ ip = anchor + len; /* distance is biased */ distance--; if(!distance) { /* zero distance means a run */ uint8_t x = ip[-1]; int64_t value, value2; /* Broadcast the value for every byte in a 64-bit register */ memset(&value, x, 8); /* safe because the outer check against ip limit */ while (ip < (ip_bound - (sizeof(int64_t) - IP_BOUNDARY))) { value2 = ((int64_t *)ref)[0]; if (value != value2) { /* Find the byte that starts to differ */ while (ip < ip_bound) { if (*ref++ != x) break; else ip++; } break; } else { ip += 8; ref += 8; } } if (ip > ip_bound) { long l = (long)(ip - ip_bound); ip -= l; ref -= l; } /* End of optimization */ } else { for(;;) { /* safe because the outer check against ip limit */ while (ip < (ip_bound - (sizeof(int64_t) - IP_BOUNDARY))) { if (*ref++ != *ip++) break; if (((int64_t *)ref)[0] != ((int64_t *)ip)[0]) { /* Find the byte that starts to differ */ while (ip < ip_bound) { if (*ref++ != *ip++) break; } break; } else { ip += 8; ref += 8; } } /* Last correction before exiting loop */ if (ip > ip_bound) { int32_t l = (int32_t)(ip - ip_bound); ip -= l; ref -= l; } /* End of optimization */ break; } } /* if we have copied something, adjust the copy count */ if (copy) /* copy is biased, '0' means 1 byte copy */ *(op-copy-1) = copy-1; else /* back, to overwrite the copy count */ op--; /* reset literal counter */ copy = 0; /* length is biased, '1' means a match of 3 bytes */ ip -= 3; len = (int32_t)(ip - anchor); /* check that we have space enough to encode the match for all the cases */ if (BLOSCLZ_UNEXPECT_CONDITIONAL(op+(len/255)+6 > op_limit)) goto out; /* encode the match */ if(distance < MAX_DISTANCE) { if(len < 7) { *op++ = (len << 5) + (distance >> 8); *op++ = (distance & 255); } else { *op++ = (uint8_t)((7 << 5) + (distance >> 8)); for(len-=7; len >= 255; len-= 255) *op++ = 255; *op++ = len; *op++ = (distance & 255); } } else { /* far away, but not yet in the another galaxy... */ if(len < 7) { distance -= MAX_DISTANCE; *op++ = (uint8_t)((len << 5) + 31); *op++ = 255; *op++ = (uint8_t)(distance >> 8); *op++ = distance & 255; } else { distance -= MAX_DISTANCE; *op++ = (7 << 5) + 31; for(len-=7; len >= 255; len-= 255) *op++ = 255; *op++ = len; *op++ = 255; *op++ = (uint8_t)(distance >> 8); *op++ = distance & 255; } } /* update the hash at match boundary */ hval = hash_function(ip, hash_log); htab[hval] = (uint16_t)(ip++ - ibase); hval = hash_function(ip, hash_log); htab[hval] = (uint16_t)(ip++ - ibase); /* assuming literal copy */ *op++ = MAX_COPY-1; continue; literal: if (BLOSCLZ_UNEXPECT_CONDITIONAL(op+2 > op_limit)) goto out; *op++ = *anchor++; ip = anchor; copy++; if(BLOSCLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) { copy = 0; *op++ = MAX_COPY-1; } } /* left-over as literal copy */ ip_bound++; while(ip <= ip_bound) { if (BLOSCLZ_UNEXPECT_CONDITIONAL(op+2 > op_limit)) goto out; *op++ = *ip++; copy++; if(copy == MAX_COPY) { copy = 0; *op++ = MAX_COPY-1; } } /* if we have copied something, adjust the copy length */ if(copy) *(op-copy-1) = copy-1; else op--; /* marker for blosclz */ *(uint8_t*)output |= (1 << 5); free(htab); return (int)(op - (uint8_t*)output); out: free(htab); return 0; } int blosclz_decompress(const void* input, int length, void* output, int maxout) { const uint8_t* ip = (const uint8_t*) input; const uint8_t* ip_limit = ip + length; uint8_t* op = (uint8_t*) output; uint8_t* op_limit = op + maxout; int32_t ctrl = (*ip++) & 31; int32_t loop = 1; do { const uint8_t* ref = op; int32_t len = ctrl >> 5; int32_t ofs = (ctrl & 31) << 8; if(ctrl >= 32) { uint8_t code; len--; ref -= ofs; if (len == 7-1) do { code = *ip++; len += code; } while (code==255); code = *ip++; ref -= code; /* match from 16-bit distance */ if(BLOSCLZ_UNEXPECT_CONDITIONAL(code==255)) if(BLOSCLZ_EXPECT_CONDITIONAL(ofs==(31 << 8))) { ofs = (*ip++) << 8; ofs += *ip++; ref = op - ofs - MAX_DISTANCE; } #ifdef BLOSCLZ_SAFE if (BLOSCLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit)) { return 0; } if (BLOSCLZ_UNEXPECT_CONDITIONAL(ref-1 < (uint8_t *)output)) { return 0; } #endif if(BLOSCLZ_EXPECT_CONDITIONAL(ip < ip_limit)) ctrl = *ip++; else loop = 0; if(ref == op) { /* optimize copy for a run */ uint8_t b = ref[-1]; memset(op, b, len+3); op += len+3; } else { /* copy from reference */ ref--; len += 3; if (abs((int32_t)(ref-op)) <= (int32_t)len) { /* src and dst do overlap: do a loop */ for(; len; --len) *op++ = *ref++; /* The memmove below does not work well (don't know why) */ /* memmove(op, ref, len); op += len; ref += len; len = 0; */ } else { memcpy(op, ref, len); op += len; ref += len; } } } else { ctrl++; #ifdef BLOSCLZ_SAFE if (BLOSCLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit)) { return 0; } if (BLOSCLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit)) { return 0; } #endif memcpy(op, ip, ctrl); ip += ctrl; op += ctrl; loop = (int32_t)BLOSCLZ_EXPECT_CONDITIONAL(ip < ip_limit); if(loop) ctrl = *ip++; } } while(BLOSCLZ_EXPECT_CONDITIONAL(loop)); return (int)(op - (uint8_t*)output); } PyTables-v.3.1.1/c-blosc/blosc/blosclz.h000066400000000000000000000035601231437614300177600ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Suffling and Compression Library Author: Francesc Alted See LICENSES/BLOSC.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* The code in this file is heavily based on FastLZ, a lightning-fast lossless compression library. See LICENSES/FASTLZ.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSCLZ_H #define BLOSCLZ_H #if defined (__cplusplus) extern "C" { #endif /** Compress a block of data in the input buffer and returns the size of compressed block. The size of input buffer is specified by length. The minimum input buffer size is 16. The output buffer must be at least 5% larger than the input buffer and can not be smaller than 66 bytes. If the input is not compressible, or output does not fit in maxout bytes, the return value will be 0 and you will have to discard the output buffer. The input buffer and the output buffer can not overlap. */ int blosclz_compress(int opt_level, const void* input, int length, void* output, int maxout); /** Decompress a block of compressed data and returns the size of the decompressed block. If error occurs, e.g. the compressed data is corrupted or the output buffer is not large enough, then 0 (zero) will be returned instead. The input buffer and the output buffer can not overlap. Decompression is memory safe and guaranteed not to write the output buffer more than what is specified in maxout. */ int blosclz_decompress(const void* input, int length, void* output, int maxout); #if defined (__cplusplus) } #endif #endif /* BLOSCLZ_H */ PyTables-v.3.1.1/c-blosc/blosc/config.h.in000066400000000000000000000003031231437614300201520ustar00rootroot00000000000000#ifndef _CONFIGURATION_HEADER_GUARD_H_ #define _CONFIGURATION_HEADER_GUARD_H_ #cmakedefine HAVE_LZ4 @HAVE_LZ4@ #cmakedefine HAVE_SNAPPY @HAVE_SNAPPY@ #cmakedefine HAVE_ZLIB @HAVE_ZLIB@ #endif PyTables-v.3.1.1/c-blosc/blosc/shuffle.c000066400000000000000000000371541231437614300177450ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Suffling and Compression Library Author: Francesc Alted Creation date: 2009-05-20 See LICENSES/BLOSC.txt for details about copyright and rights to use. **********************************************************************/ #include #include #include "shuffle.h" #if defined(_WIN32) && !defined(__MINGW32__) #include #include "win32/stdint-windows.h" #define __SSE2__ /* Windows does not define this by default */ #else #include #include #endif /* _WIN32 */ /* The non-SSE2 versions of shuffle and unshuffle */ /* Shuffle a block. This can never fail. */ static void _shuffle(size_t bytesoftype, size_t blocksize, uint8_t* _src, uint8_t* _dest) { size_t i, j, neblock, leftover; /* Non-optimized shuffle */ neblock = blocksize / bytesoftype; /* Number of elements in a block */ for (j = 0; j < bytesoftype; j++) { for (i = 0; i < neblock; i++) { _dest[j*neblock+i] = _src[i*bytesoftype+j]; } } leftover = blocksize % bytesoftype; memcpy(_dest + neblock*bytesoftype, _src + neblock*bytesoftype, leftover); } /* Unshuffle a block. This can never fail. */ static void _unshuffle(size_t bytesoftype, size_t blocksize, uint8_t* _src, uint8_t* _dest) { size_t i, j, neblock, leftover; /* Non-optimized unshuffle */ neblock = blocksize / bytesoftype; /* Number of elements in a block */ for (i = 0; i < neblock; i++) { for (j = 0; j < bytesoftype; j++) { _dest[i*bytesoftype+j] = _src[j*neblock+i]; } } leftover = blocksize % bytesoftype; memcpy(_dest+neblock*bytesoftype, _src+neblock*bytesoftype, leftover); } #ifdef __SSE2__ /* The SSE2 versions of shuffle and unshuffle */ #include /* The next is useful for debugging purposes */ #if 0 static void printxmm(__m128i xmm0) { uint8_t buf[16]; ((__m128i *)buf)[0] = xmm0; printf("%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); } #endif /* Routine optimized for shuffling a buffer for a type size of 2 bytes. */ static void shuffle2(uint8_t* dest, uint8_t* src, size_t size) { size_t i, j, k; size_t numof16belem; __m128i xmm0[2], xmm1[2]; numof16belem = size / (16*2); for (i = 0, j = 0; i < numof16belem; i++, j += 16*2) { /* Fetch and transpose bytes, words and double words in groups of 32 bytes */ for (k = 0; k < 2; k++) { xmm0[k] = _mm_loadu_si128((__m128i*)(src+j+k*16)); xmm0[k] = _mm_shufflelo_epi16(xmm0[k], 0xd8); xmm0[k] = _mm_shufflehi_epi16(xmm0[k], 0xd8); xmm0[k] = _mm_shuffle_epi32(xmm0[k], 0xd8); xmm1[k] = _mm_shuffle_epi32(xmm0[k], 0x4e); xmm0[k] = _mm_unpacklo_epi8(xmm0[k], xmm1[k]); xmm0[k] = _mm_shuffle_epi32(xmm0[k], 0xd8); xmm1[k] = _mm_shuffle_epi32(xmm0[k], 0x4e); xmm0[k] = _mm_unpacklo_epi16(xmm0[k], xmm1[k]); xmm0[k] = _mm_shuffle_epi32(xmm0[k], 0xd8); } /* Transpose quad words */ for (k = 0; k < 1; k++) { xmm1[k*2] = _mm_unpacklo_epi64(xmm0[k], xmm0[k+1]); xmm1[k*2+1] = _mm_unpackhi_epi64(xmm0[k], xmm0[k+1]); } /* Store the result vectors */ for (k = 0; k < 2; k++) { ((__m128i *)dest)[k*numof16belem+i] = xmm1[k]; } } } /* Routine optimized for shuffling a buffer for a type size of 4 bytes. */ static void shuffle4(uint8_t* dest, uint8_t* src, size_t size) { size_t i, j, k; size_t numof16belem; __m128i xmm0[4], xmm1[4]; numof16belem = size / (16*4); for (i = 0, j = 0; i < numof16belem; i++, j += 16*4) { /* Fetch and transpose bytes and words in groups of 64 bytes */ for (k = 0; k < 4; k++) { xmm0[k] = _mm_loadu_si128((__m128i*)(src+j+k*16)); xmm1[k] = _mm_shuffle_epi32(xmm0[k], 0xd8); xmm0[k] = _mm_shuffle_epi32(xmm0[k], 0x8d); xmm0[k] = _mm_unpacklo_epi8(xmm1[k], xmm0[k]); xmm1[k] = _mm_shuffle_epi32(xmm0[k], 0x04e); xmm0[k] = _mm_unpacklo_epi16(xmm0[k], xmm1[k]); } /* Transpose double words */ for (k = 0; k < 2; k++) { xmm1[k*2] = _mm_unpacklo_epi32(xmm0[k*2], xmm0[k*2+1]); xmm1[k*2+1] = _mm_unpackhi_epi32(xmm0[k*2], xmm0[k*2+1]); } /* Transpose quad words */ for (k = 0; k < 2; k++) { xmm0[k*2] = _mm_unpacklo_epi64(xmm1[k], xmm1[k+2]); xmm0[k*2+1] = _mm_unpackhi_epi64(xmm1[k], xmm1[k+2]); } /* Store the result vectors */ for (k = 0; k < 4; k++) { ((__m128i *)dest)[k*numof16belem+i] = xmm0[k]; } } } /* Routine optimized for shuffling a buffer for a type size of 8 bytes. */ static void shuffle8(uint8_t* dest, uint8_t* src, size_t size) { size_t i, j, k, l; size_t numof16belem; __m128i xmm0[8], xmm1[8]; numof16belem = size / (16*8); for (i = 0, j = 0; i < numof16belem; i++, j += 16*8) { /* Fetch and transpose bytes in groups of 128 bytes */ for (k = 0; k < 8; k++) { xmm0[k] = _mm_loadu_si128((__m128i*)(src+j+k*16)); xmm1[k] = _mm_shuffle_epi32(xmm0[k], 0x4e); xmm1[k] = _mm_unpacklo_epi8(xmm0[k], xmm1[k]); } /* Transpose words */ for (k = 0, l = 0; k < 4; k++, l +=2) { xmm0[k*2] = _mm_unpacklo_epi16(xmm1[l], xmm1[l+1]); xmm0[k*2+1] = _mm_unpackhi_epi16(xmm1[l], xmm1[l+1]); } /* Transpose double words */ for (k = 0, l = 0; k < 4; k++, l++) { if (k == 2) l += 2; xmm1[k*2] = _mm_unpacklo_epi32(xmm0[l], xmm0[l+2]); xmm1[k*2+1] = _mm_unpackhi_epi32(xmm0[l], xmm0[l+2]); } /* Transpose quad words */ for (k = 0; k < 4; k++) { xmm0[k*2] = _mm_unpacklo_epi64(xmm1[k], xmm1[k+4]); xmm0[k*2+1] = _mm_unpackhi_epi64(xmm1[k], xmm1[k+4]); } /* Store the result vectors */ for (k = 0; k < 8; k++) { ((__m128i *)dest)[k*numof16belem+i] = xmm0[k]; } } } /* Routine optimized for shuffling a buffer for a type size of 16 bytes. */ static void shuffle16(uint8_t* dest, uint8_t* src, size_t size) { size_t i, j, k, l; size_t numof16belem; __m128i xmm0[16], xmm1[16]; numof16belem = size / (16*16); for (i = 0, j = 0; i < numof16belem; i++, j += 16*16) { /* Fetch elements in groups of 256 bytes */ for (k = 0; k < 16; k++) { xmm0[k] = _mm_loadu_si128((__m128i*)(src+j+k*16)); } /* Transpose bytes */ for (k = 0, l = 0; k < 8; k++, l +=2) { xmm1[k*2] = _mm_unpacklo_epi8(xmm0[l], xmm0[l+1]); xmm1[k*2+1] = _mm_unpackhi_epi8(xmm0[l], xmm0[l+1]); } /* Transpose words */ for (k = 0, l = -2; k < 8; k++, l++) { if ((k%2) == 0) l += 2; xmm0[k*2] = _mm_unpacklo_epi16(xmm1[l], xmm1[l+2]); xmm0[k*2+1] = _mm_unpackhi_epi16(xmm1[l], xmm1[l+2]); } /* Transpose double words */ for (k = 0, l = -4; k < 8; k++, l++) { if ((k%4) == 0) l += 4; xmm1[k*2] = _mm_unpacklo_epi32(xmm0[l], xmm0[l+4]); xmm1[k*2+1] = _mm_unpackhi_epi32(xmm0[l], xmm0[l+4]); } /* Transpose quad words */ for (k = 0; k < 8; k++) { xmm0[k*2] = _mm_unpacklo_epi64(xmm1[k], xmm1[k+8]); xmm0[k*2+1] = _mm_unpackhi_epi64(xmm1[k], xmm1[k+8]); } /* Store the result vectors */ for (k = 0; k < 16; k++) { ((__m128i *)dest)[k*numof16belem+i] = xmm0[k]; } } } /* Shuffle a block. This can never fail. */ void shuffle(size_t bytesoftype, size_t blocksize, uint8_t* _src, uint8_t* _dest) { int unaligned_dest = (int)((uintptr_t)_dest % 16); int multiple_of_block = (blocksize % (16 * bytesoftype)) == 0; int too_small = (blocksize < 256); if (unaligned_dest || !multiple_of_block || too_small) { /* _dest buffer is not aligned, not multiple of the vectorization size * or is too small. Call the non-sse2 version. */ _shuffle(bytesoftype, blocksize, _src, _dest); return; } /* Optimized shuffle */ /* The buffer must be aligned on a 16 bytes boundary, have a power */ /* of 2 size and be larger or equal than 256 bytes. */ if (bytesoftype == 4) { shuffle4(_dest, _src, blocksize); } else if (bytesoftype == 8) { shuffle8(_dest, _src, blocksize); } else if (bytesoftype == 16) { shuffle16(_dest, _src, blocksize); } else if (bytesoftype == 2) { shuffle2(_dest, _src, blocksize); } else { /* Non-optimized shuffle */ _shuffle(bytesoftype, blocksize, _src, _dest); } } /* Routine optimized for unshuffling a buffer for a type size of 2 bytes. */ static void unshuffle2(uint8_t* dest, uint8_t* orig, size_t size) { size_t i, k; size_t neblock, numof16belem; __m128i xmm1[2], xmm2[2]; neblock = size / 2; numof16belem = neblock / 16; for (i = 0, k = 0; i < numof16belem; i++, k += 2) { /* Load the first 32 bytes in 2 XMM registrers */ xmm1[0] = ((__m128i *)orig)[0*numof16belem+i]; xmm1[1] = ((__m128i *)orig)[1*numof16belem+i]; /* Shuffle bytes */ /* Compute the low 32 bytes */ xmm2[0] = _mm_unpacklo_epi8(xmm1[0], xmm1[1]); /* Compute the hi 32 bytes */ xmm2[1] = _mm_unpackhi_epi8(xmm1[0], xmm1[1]); /* Store the result vectors in proper order */ ((__m128i *)dest)[k+0] = xmm2[0]; ((__m128i *)dest)[k+1] = xmm2[1]; } } /* Routine optimized for unshuffling a buffer for a type size of 4 bytes. */ static void unshuffle4(uint8_t* dest, uint8_t* orig, size_t size) { size_t i, j, k; size_t neblock, numof16belem; __m128i xmm0[4], xmm1[4]; neblock = size / 4; numof16belem = neblock / 16; for (i = 0, k = 0; i < numof16belem; i++, k += 4) { /* Load the first 64 bytes in 4 XMM registrers */ for (j = 0; j < 4; j++) { xmm0[j] = ((__m128i *)orig)[j*numof16belem+i]; } /* Shuffle bytes */ for (j = 0; j < 2; j++) { /* Compute the low 32 bytes */ xmm1[j] = _mm_unpacklo_epi8(xmm0[j*2], xmm0[j*2+1]); /* Compute the hi 32 bytes */ xmm1[2+j] = _mm_unpackhi_epi8(xmm0[j*2], xmm0[j*2+1]); } /* Shuffle 2-byte words */ for (j = 0; j < 2; j++) { /* Compute the low 32 bytes */ xmm0[j] = _mm_unpacklo_epi16(xmm1[j*2], xmm1[j*2+1]); /* Compute the hi 32 bytes */ xmm0[2+j] = _mm_unpackhi_epi16(xmm1[j*2], xmm1[j*2+1]); } /* Store the result vectors in proper order */ ((__m128i *)dest)[k+0] = xmm0[0]; ((__m128i *)dest)[k+1] = xmm0[2]; ((__m128i *)dest)[k+2] = xmm0[1]; ((__m128i *)dest)[k+3] = xmm0[3]; } } /* Routine optimized for unshuffling a buffer for a type size of 8 bytes. */ static void unshuffle8(uint8_t* dest, uint8_t* orig, size_t size) { size_t i, j, k; size_t neblock, numof16belem; __m128i xmm0[8], xmm1[8]; neblock = size / 8; numof16belem = neblock / 16; for (i = 0, k = 0; i < numof16belem; i++, k += 8) { /* Load the first 64 bytes in 8 XMM registrers */ for (j = 0; j < 8; j++) { xmm0[j] = ((__m128i *)orig)[j*numof16belem+i]; } /* Shuffle bytes */ for (j = 0; j < 4; j++) { /* Compute the low 32 bytes */ xmm1[j] = _mm_unpacklo_epi8(xmm0[j*2], xmm0[j*2+1]); /* Compute the hi 32 bytes */ xmm1[4+j] = _mm_unpackhi_epi8(xmm0[j*2], xmm0[j*2+1]); } /* Shuffle 2-byte words */ for (j = 0; j < 4; j++) { /* Compute the low 32 bytes */ xmm0[j] = _mm_unpacklo_epi16(xmm1[j*2], xmm1[j*2+1]); /* Compute the hi 32 bytes */ xmm0[4+j] = _mm_unpackhi_epi16(xmm1[j*2], xmm1[j*2+1]); } /* Shuffle 4-byte dwords */ for (j = 0; j < 4; j++) { /* Compute the low 32 bytes */ xmm1[j] = _mm_unpacklo_epi32(xmm0[j*2], xmm0[j*2+1]); /* Compute the hi 32 bytes */ xmm1[4+j] = _mm_unpackhi_epi32(xmm0[j*2], xmm0[j*2+1]); } /* Store the result vectors in proper order */ ((__m128i *)dest)[k+0] = xmm1[0]; ((__m128i *)dest)[k+1] = xmm1[4]; ((__m128i *)dest)[k+2] = xmm1[2]; ((__m128i *)dest)[k+3] = xmm1[6]; ((__m128i *)dest)[k+4] = xmm1[1]; ((__m128i *)dest)[k+5] = xmm1[5]; ((__m128i *)dest)[k+6] = xmm1[3]; ((__m128i *)dest)[k+7] = xmm1[7]; } } /* Routine optimized for unshuffling a buffer for a type size of 16 bytes. */ static void unshuffle16(uint8_t* dest, uint8_t* orig, size_t size) { size_t i, j, k; size_t neblock, numof16belem; __m128i xmm1[16], xmm2[16]; neblock = size / 16; numof16belem = neblock / 16; for (i = 0, k = 0; i < numof16belem; i++, k += 16) { /* Load the first 128 bytes in 16 XMM registrers */ for (j = 0; j < 16; j++) { xmm1[j] = ((__m128i *)orig)[j*numof16belem+i]; } /* Shuffle bytes */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm2[j] = _mm_unpacklo_epi8(xmm1[j*2], xmm1[j*2+1]); /* Compute the hi 32 bytes */ xmm2[8+j] = _mm_unpackhi_epi8(xmm1[j*2], xmm1[j*2+1]); } /* Shuffle 2-byte words */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm1[j] = _mm_unpacklo_epi16(xmm2[j*2], xmm2[j*2+1]); /* Compute the hi 32 bytes */ xmm1[8+j] = _mm_unpackhi_epi16(xmm2[j*2], xmm2[j*2+1]); } /* Shuffle 4-byte dwords */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm2[j] = _mm_unpacklo_epi32(xmm1[j*2], xmm1[j*2+1]); /* Compute the hi 32 bytes */ xmm2[8+j] = _mm_unpackhi_epi32(xmm1[j*2], xmm1[j*2+1]); } /* Shuffle 8-byte qwords */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm1[j] = _mm_unpacklo_epi64(xmm2[j*2], xmm2[j*2+1]); /* Compute the hi 32 bytes */ xmm1[8+j] = _mm_unpackhi_epi64(xmm2[j*2], xmm2[j*2+1]); } /* Store the result vectors in proper order */ ((__m128i *)dest)[k+0] = xmm1[0]; ((__m128i *)dest)[k+1] = xmm1[8]; ((__m128i *)dest)[k+2] = xmm1[4]; ((__m128i *)dest)[k+3] = xmm1[12]; ((__m128i *)dest)[k+4] = xmm1[2]; ((__m128i *)dest)[k+5] = xmm1[10]; ((__m128i *)dest)[k+6] = xmm1[6]; ((__m128i *)dest)[k+7] = xmm1[14]; ((__m128i *)dest)[k+8] = xmm1[1]; ((__m128i *)dest)[k+9] = xmm1[9]; ((__m128i *)dest)[k+10] = xmm1[5]; ((__m128i *)dest)[k+11] = xmm1[13]; ((__m128i *)dest)[k+12] = xmm1[3]; ((__m128i *)dest)[k+13] = xmm1[11]; ((__m128i *)dest)[k+14] = xmm1[7]; ((__m128i *)dest)[k+15] = xmm1[15]; } } /* Unshuffle a block. This can never fail. */ void unshuffle(size_t bytesoftype, size_t blocksize, uint8_t* _src, uint8_t* _dest) { int unaligned_src = (int)((uintptr_t)_src % 16); int unaligned_dest = (int)((uintptr_t)_dest % 16); int multiple_of_block = (blocksize % (16 * bytesoftype)) == 0; int too_small = (blocksize < 256); if (unaligned_src || unaligned_dest || !multiple_of_block || too_small) { /* _src or _dest buffer is not aligned, not multiple of the vectorization * size or is not too small. Call the non-sse2 version. */ _unshuffle(bytesoftype, blocksize, _src, _dest); return; } /* Optimized unshuffle */ /* The buffers must be aligned on a 16 bytes boundary, have a power */ /* of 2 size and be larger or equal than 256 bytes. */ if (bytesoftype == 4) { unshuffle4(_dest, _src, blocksize); } else if (bytesoftype == 8) { unshuffle8(_dest, _src, blocksize); } else if (bytesoftype == 16) { unshuffle16(_dest, _src, blocksize); } else if (bytesoftype == 2) { unshuffle2(_dest, _src, blocksize); } else { /* Non-optimized unshuffle */ _unshuffle(bytesoftype, blocksize, _src, _dest); } } #else /* no __SSE2__ available */ void shuffle(size_t bytesoftype, size_t blocksize, uint8_t* _src, uint8_t* _dest) { _shuffle(bytesoftype, blocksize, _src, _dest); } void unshuffle(size_t bytesoftype, size_t blocksize, uint8_t* _src, uint8_t* _dest) { _unshuffle(bytesoftype, blocksize, _src, _dest); } #endif /* __SSE2__ */ PyTables-v.3.1.1/c-blosc/blosc/shuffle.h000066400000000000000000000010711231437614300177370ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Suffling and Compression Library Author: Francesc Alted See LICENSES/BLOSC.txt for details about copyright and rights to use. **********************************************************************/ /* Shuffle/unshuffle routines */ void shuffle(size_t bytesoftype, size_t blocksize, unsigned char* _src, unsigned char* _dest); void unshuffle(size_t bytesoftype, size_t blocksize, unsigned char* _src, unsigned char* _dest); PyTables-v.3.1.1/c-blosc/blosc/win32/000077500000000000000000000000001231437614300170755ustar00rootroot00000000000000PyTables-v.3.1.1/c-blosc/blosc/win32/pthread.c000066400000000000000000000145471231437614300207030ustar00rootroot00000000000000/* * Code for simulating pthreads API on Windows. This is Git-specific, * but it is enough for Numexpr needs too. * * Copyright (C) 2009 Andrzej K. Haczewski * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * DISCLAIMER: The implementation is Git-specific, it is subset of original * Pthreads API, without lots of other features that Git doesn't use. * Git also makes sure that the passed arguments are valid, so there's * no need for double-checking. */ #include "pthread.h" #include #include #include #include #include void die(const char *err, ...) { printf("%s", err); exit(-1); } static unsigned __stdcall win32_start_routine(void *arg) { pthread_t *thread = (pthread_t*)arg; thread->arg = thread->start_routine(thread->arg); return 0; } int pthread_create(pthread_t *thread, const void *unused, void *(*start_routine)(void*), void *arg) { thread->arg = arg; thread->start_routine = start_routine; thread->handle = (HANDLE) _beginthreadex(NULL, 0, win32_start_routine, thread, 0, NULL); if (!thread->handle) return errno; else return 0; } int win32_pthread_join(pthread_t *thread, void **value_ptr) { DWORD result = WaitForSingleObject(thread->handle, INFINITE); switch (result) { case WAIT_OBJECT_0: if (value_ptr) *value_ptr = thread->arg; return 0; case WAIT_ABANDONED: return EINVAL; default: return GetLastError(); } } int pthread_cond_init(pthread_cond_t *cond, const void *unused) { cond->waiters = 0; cond->was_broadcast = 0; InitializeCriticalSection(&cond->waiters_lock); cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL); if (!cond->sema) die("CreateSemaphore() failed"); cond->continue_broadcast = CreateEvent(NULL, /* security */ FALSE, /* auto-reset */ FALSE, /* not signaled */ NULL); /* name */ if (!cond->continue_broadcast) die("CreateEvent() failed"); return 0; } int pthread_cond_destroy(pthread_cond_t *cond) { CloseHandle(cond->sema); CloseHandle(cond->continue_broadcast); DeleteCriticalSection(&cond->waiters_lock); return 0; } int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex) { int last_waiter; EnterCriticalSection(&cond->waiters_lock); cond->waiters++; LeaveCriticalSection(&cond->waiters_lock); /* * Unlock external mutex and wait for signal. * NOTE: we've held mutex locked long enough to increment * waiters count above, so there's no problem with * leaving mutex unlocked before we wait on semaphore. */ LeaveCriticalSection(mutex); /* let's wait - ignore return value */ WaitForSingleObject(cond->sema, INFINITE); /* * Decrease waiters count. If we are the last waiter, then we must * notify the broadcasting thread that it can continue. * But if we continued due to cond_signal, we do not have to do that * because the signaling thread knows that only one waiter continued. */ EnterCriticalSection(&cond->waiters_lock); cond->waiters--; last_waiter = cond->was_broadcast && cond->waiters == 0; LeaveCriticalSection(&cond->waiters_lock); if (last_waiter) { /* * cond_broadcast was issued while mutex was held. This means * that all other waiters have continued, but are contending * for the mutex at the end of this function because the * broadcasting thread did not leave cond_broadcast, yet. * (This is so that it can be sure that each waiter has * consumed exactly one slice of the semaphor.) * The last waiter must tell the broadcasting thread that it * can go on. */ SetEvent(cond->continue_broadcast); /* * Now we go on to contend with all other waiters for * the mutex. Auf in den Kampf! */ } /* lock external mutex again */ EnterCriticalSection(mutex); return 0; } /* * IMPORTANT: This implementation requires that pthread_cond_signal * is called while the mutex is held that is used in the corresponding * pthread_cond_wait calls! */ int pthread_cond_signal(pthread_cond_t *cond) { int have_waiters; EnterCriticalSection(&cond->waiters_lock); have_waiters = cond->waiters > 0; LeaveCriticalSection(&cond->waiters_lock); /* * Signal only when there are waiters */ if (have_waiters) return ReleaseSemaphore(cond->sema, 1, NULL) ? 0 : GetLastError(); else return 0; } /* * DOUBLY IMPORTANT: This implementation requires that pthread_cond_broadcast * is called while the mutex is held that is used in the corresponding * pthread_cond_wait calls! */ int pthread_cond_broadcast(pthread_cond_t *cond) { EnterCriticalSection(&cond->waiters_lock); if ((cond->was_broadcast = cond->waiters > 0)) { /* wake up all waiters */ ReleaseSemaphore(cond->sema, cond->waiters, NULL); LeaveCriticalSection(&cond->waiters_lock); /* * At this point all waiters continue. Each one takes its * slice of the semaphor. Now it's our turn to wait: Since * the external mutex is held, no thread can leave cond_wait, * yet. For this reason, we can be sure that no thread gets * a chance to eat *more* than one slice. OTOH, it means * that the last waiter must send us a wake-up. */ WaitForSingleObject(cond->continue_broadcast, INFINITE); /* * Since the external mutex is held, no thread can enter * cond_wait, and, hence, it is safe to reset this flag * without cond->waiters_lock held. */ cond->was_broadcast = 0; } else { LeaveCriticalSection(&cond->waiters_lock); } return 0; } PyTables-v.3.1.1/c-blosc/blosc/win32/pthread.h000066400000000000000000000062701231437614300207020ustar00rootroot00000000000000/* * Code for simulating pthreads API on Windows. This is Git-specific, * but it is enough for Numexpr needs too. * * Copyright (C) 2009 Andrzej K. Haczewski * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * DISCLAIMER: The implementation is Git-specific, it is subset of original * Pthreads API, without lots of other features that Git doesn't use. * Git also makes sure that the passed arguments are valid, so there's * no need for double-checking. */ #ifndef PTHREAD_H #define PTHREAD_H #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include /* * Defines that adapt Windows API threads to pthreads API */ #define pthread_mutex_t CRITICAL_SECTION #define pthread_mutex_init(a,b) InitializeCriticalSection((a)) #define pthread_mutex_destroy(a) DeleteCriticalSection((a)) #define pthread_mutex_lock EnterCriticalSection #define pthread_mutex_unlock LeaveCriticalSection /* * Implement simple condition variable for Windows threads, based on ACE * implementation. * * See original implementation: http://bit.ly/1vkDjo * ACE homepage: http://www.cse.wustl.edu/~schmidt/ACE.html * See also: http://www.cse.wustl.edu/~schmidt/win32-cv-1.html */ typedef struct { LONG waiters; int was_broadcast; CRITICAL_SECTION waiters_lock; HANDLE sema; HANDLE continue_broadcast; } pthread_cond_t; extern int pthread_cond_init(pthread_cond_t *cond, const void *unused); extern int pthread_cond_destroy(pthread_cond_t *cond); extern int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex); extern int pthread_cond_signal(pthread_cond_t *cond); extern int pthread_cond_broadcast(pthread_cond_t *cond); /* * Simple thread creation implementation using pthread API */ typedef struct { HANDLE handle; void *(*start_routine)(void*); void *arg; } pthread_t; extern int pthread_create(pthread_t *thread, const void *unused, void *(*start_routine)(void*), void *arg); /* * To avoid the need of copying a struct, we use small macro wrapper to pass * pointer to win32_pthread_join instead. */ #define pthread_join(a, b) win32_pthread_join(&(a), (b)) extern int win32_pthread_join(pthread_t *thread, void **value_ptr); #endif /* PTHREAD_H */ PyTables-v.3.1.1/c-blosc/blosc/win32/stdint-windows.h000066400000000000000000000176451231437614300222600ustar00rootroot00000000000000// ISO C9x compliant stdint.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006-2013 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. Neither the name of the product nor the names of its contributors may // be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ #error "Use this header only with Microsoft Visual C++ compilers!" #endif // _MSC_VER ] #ifndef _MSC_STDINT_H_ // [ #define _MSC_STDINT_H_ #if _MSC_VER > 1000 #pragma once #endif #if _MSC_VER >= 1600 // [ #include #else // ] _MSC_VER >= 1600 [ #include // For Visual Studio 6 in C++ mode and for many Visual Studio versions when // compiling for ARM we should wrap include with 'extern "C++" {}' // or compiler give many errors like this: // error C2733: second C linkage of overloaded function 'wmemchr' not allowed #ifdef __cplusplus extern "C" { #endif # include #ifdef __cplusplus } #endif // Define _W64 macros to mark types changing their size, like intptr_t. #ifndef _W64 # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 # define _W64 __w64 # else # define _W64 # endif #endif // 7.18.1 Integer types // 7.18.1.1 Exact-width integer types // Visual Studio 6 and Embedded Visual C++ 4 doesn't // realize that, e.g. char has the same size as __int8 // so we give up on __intX for them. #if (_MSC_VER < 1300) typedef signed char int8_t; typedef signed short int16_t; typedef signed int int32_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else typedef signed __int8 int8_t; typedef signed __int16 int16_t; typedef signed __int32 int32_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; // 7.18.1.2 Minimum-width integer types typedef int8_t int_least8_t; typedef int16_t int_least16_t; typedef int32_t int_least32_t; typedef int64_t int_least64_t; typedef uint8_t uint_least8_t; typedef uint16_t uint_least16_t; typedef uint32_t uint_least32_t; typedef uint64_t uint_least64_t; // 7.18.1.3 Fastest minimum-width integer types typedef int8_t int_fast8_t; typedef int16_t int_fast16_t; typedef int32_t int_fast32_t; typedef int64_t int_fast64_t; typedef uint8_t uint_fast8_t; typedef uint16_t uint_fast16_t; typedef uint32_t uint_fast32_t; typedef uint64_t uint_fast64_t; // 7.18.1.4 Integer types capable of holding object pointers #ifdef _WIN64 // [ typedef signed __int64 intptr_t; typedef unsigned __int64 uintptr_t; #else // _WIN64 ][ typedef _W64 signed int intptr_t; typedef _W64 unsigned int uintptr_t; #endif // _WIN64 ] // 7.18.1.5 Greatest-width integer types typedef int64_t intmax_t; typedef uint64_t uintmax_t; // 7.18.2 Limits of specified-width integer types #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 // 7.18.2.1 Limits of exact-width integer types #define INT8_MIN ((int8_t)_I8_MIN) #define INT8_MAX _I8_MAX #define INT16_MIN ((int16_t)_I16_MIN) #define INT16_MAX _I16_MAX #define INT32_MIN ((int32_t)_I32_MIN) #define INT32_MAX _I32_MAX #define INT64_MIN ((int64_t)_I64_MIN) #define INT64_MAX _I64_MAX #define UINT8_MAX _UI8_MAX #define UINT16_MAX _UI16_MAX #define UINT32_MAX _UI32_MAX #define UINT64_MAX _UI64_MAX // 7.18.2.2 Limits of minimum-width integer types #define INT_LEAST8_MIN INT8_MIN #define INT_LEAST8_MAX INT8_MAX #define INT_LEAST16_MIN INT16_MIN #define INT_LEAST16_MAX INT16_MAX #define INT_LEAST32_MIN INT32_MIN #define INT_LEAST32_MAX INT32_MAX #define INT_LEAST64_MIN INT64_MIN #define INT_LEAST64_MAX INT64_MAX #define UINT_LEAST8_MAX UINT8_MAX #define UINT_LEAST16_MAX UINT16_MAX #define UINT_LEAST32_MAX UINT32_MAX #define UINT_LEAST64_MAX UINT64_MAX // 7.18.2.3 Limits of fastest minimum-width integer types #define INT_FAST8_MIN INT8_MIN #define INT_FAST8_MAX INT8_MAX #define INT_FAST16_MIN INT16_MIN #define INT_FAST16_MAX INT16_MAX #define INT_FAST32_MIN INT32_MIN #define INT_FAST32_MAX INT32_MAX #define INT_FAST64_MIN INT64_MIN #define INT_FAST64_MAX INT64_MAX #define UINT_FAST8_MAX UINT8_MAX #define UINT_FAST16_MAX UINT16_MAX #define UINT_FAST32_MAX UINT32_MAX #define UINT_FAST64_MAX UINT64_MAX // 7.18.2.4 Limits of integer types capable of holding object pointers #ifdef _WIN64 // [ # define INTPTR_MIN INT64_MIN # define INTPTR_MAX INT64_MAX # define UINTPTR_MAX UINT64_MAX #else // _WIN64 ][ # define INTPTR_MIN INT32_MIN # define INTPTR_MAX INT32_MAX # define UINTPTR_MAX UINT32_MAX #endif // _WIN64 ] // 7.18.2.5 Limits of greatest-width integer types #define INTMAX_MIN INT64_MIN #define INTMAX_MAX INT64_MAX #define UINTMAX_MAX UINT64_MAX // 7.18.3 Limits of other integer types #ifdef _WIN64 // [ # define PTRDIFF_MIN _I64_MIN # define PTRDIFF_MAX _I64_MAX #else // _WIN64 ][ # define PTRDIFF_MIN _I32_MIN # define PTRDIFF_MAX _I32_MAX #endif // _WIN64 ] #define SIG_ATOMIC_MIN INT_MIN #define SIG_ATOMIC_MAX INT_MAX #ifndef SIZE_MAX // [ # ifdef _WIN64 // [ # define SIZE_MAX _UI64_MAX # else // _WIN64 ][ # define SIZE_MAX _UI32_MAX # endif // _WIN64 ] #endif // SIZE_MAX ] // WCHAR_MIN and WCHAR_MAX are also defined in #ifndef WCHAR_MIN // [ # define WCHAR_MIN 0 #endif // WCHAR_MIN ] #ifndef WCHAR_MAX // [ # define WCHAR_MAX _UI16_MAX #endif // WCHAR_MAX ] #define WINT_MIN 0 #define WINT_MAX _UI16_MAX #endif // __STDC_LIMIT_MACROS ] // 7.18.4 Limits of other integer types #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 // 7.18.4.1 Macros for minimum-width integer constants #define INT8_C(val) val##i8 #define INT16_C(val) val##i16 #define INT32_C(val) val##i32 #define INT64_C(val) val##i64 #define UINT8_C(val) val##ui8 #define UINT16_C(val) val##ui16 #define UINT32_C(val) val##ui32 #define UINT64_C(val) val##ui64 // 7.18.4.2 Macros for greatest-width integer constants // These #ifndef's are needed to prevent collisions with . // Check out Issue 9 for the details. #ifndef INTMAX_C // [ # define INTMAX_C INT64_C #endif // INTMAX_C ] #ifndef UINTMAX_C // [ # define UINTMAX_C UINT64_C #endif // UINTMAX_C ] #endif // __STDC_CONSTANT_MACROS ] #endif // _MSC_VER >= 1600 ] #endif // _MSC_STDINT_H_ ] PyTables-v.3.1.1/c-blosc/cmake/000077500000000000000000000000001231437614300161115ustar00rootroot00000000000000PyTables-v.3.1.1/c-blosc/cmake/FindLZ4.cmake000066400000000000000000000004121231437614300203220ustar00rootroot00000000000000find_path(LZ4_INCLUDE_DIR lz4.h) find_library(LZ4_LIBRARY NAMES lz4) if (LZ4_INCLUDE_DIR AND LZ4_LIBRARY) set(LZ4_FOUND TRUE) message(STATUS "Found LZ4 library: ${LZ4_LIBRARY}") else () message(STATUS "No lz4 found. Using internal sources.") endif () PyTables-v.3.1.1/c-blosc/cmake/FindSnappy.cmake000066400000000000000000000004521231437614300211670ustar00rootroot00000000000000find_path(SNAPPY_INCLUDE_DIR snappy-c.h) find_library(SNAPPY_LIBRARY NAMES snappy) if (SNAPPY_INCLUDE_DIR AND SNAPPY_LIBRARY) set(SNAPPY_FOUND TRUE) message(STATUS "Found SNAPPY library: ${SNAPPY_LIBRARY}") else () message(STATUS "No snappy found. Using internal sources.") endif () PyTables-v.3.1.1/c-blosc/cmake_uninstall.cmake.in000066400000000000000000000017631231437614300216200ustar00rootroot00000000000000if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") list(REVERSE files) foreach (file ${files}) message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") if (EXISTS "$ENV{DESTDIR}${file}") execute_process( COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" OUTPUT_VARIABLE rm_out RESULT_VARIABLE rm_retval ) if(NOT ${rm_retval} EQUAL 0) message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") endif (NOT ${rm_retval} EQUAL 0) else (EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") endif (EXISTS "$ENV{DESTDIR}${file}") endforeach(file) PyTables-v.3.1.1/c-blosc/hdf5/000077500000000000000000000000001231437614300156575ustar00rootroot00000000000000PyTables-v.3.1.1/c-blosc/hdf5/CMakeLists.txt000066400000000000000000000021311231437614300204140ustar00rootroot00000000000000# sources set(SOURCES blosc_filter.c) include_directories("${PROJECT_SOURCE_DIR}/blosc") # dependencies find_package(HDF5 REQUIRED) include_directories(HDF5_INCLIDE_DIRS) # targets add_library(blosc_filter_shared SHARED ${SOURCES}) set_target_properties(blosc_filter_shared PROPERTIES OUTPUT_NAME blosc_filter) target_link_libraries(blosc_filter_shared blosc_shared ${HDF5_LIBRARIES}) if(BUILD_STATIC) add_library(blosc_filter_static ${SOURCES}) set_target_properties( blosc_filter_static PROPERTIES OUTPUT_NAME blosc_filter) target_link_libraries(blosc_filter_static blosc_static) endif(BUILD_STATIC) # install install(FILES blosc_filter.h DESTINATION include COMPONENT HDF5_FILTER_DEV) install(TARGETS blosc_filter_static DESTINATION lib COMPONENT HDF5_FILTER) if(BUILD_STATIC) install( TARGETS blosc_filter_shared DESTINATION lib COMPONENT HDF5_FILTER_DEV) endif(BUILD_STATIC) # test if(BUILD_TESTS) add_executable(example example.c) target_link_libraries(example blosc_filter_static ${HDF5_LIBRARIES}) add_test(test_hdf5_filter example) endif(BUILD_TESTS) PyTables-v.3.1.1/c-blosc/hdf5/README.rst000066400000000000000000000036671231437614300173620ustar00rootroot00000000000000Using the Blosc filter from HDF5 ================================ In order to register Blosc into your HDF5 application, you only need to call a function in blosc_filter.h, with the following signature: int register_blosc(char **version, char **date) Calling this will register the filter with the HDF5 library and will return info about the Blosc release in `**version` and `**date` char pointers. A non-negative return value indicates success. If the registration fails, an error is pushed onto the current error stack and a negative value is returned. An example C program ("example.c") is included which demonstrates the proper use of the filter. This filter has been tested against HDF5 versions 1.6.5 through 1.8.10. It is released under the MIT license (see LICENSE.txt for details). Compiling ========= The filter consists of a single '.c' source file and '.h' header, along with an embedded version of the BLOSC compression library. Also, as Blosc uses SSE2 and multithreading, you must remember to use some special flags and libraries to make sure that these features are used (only necessary when compiling Blosc from sources). To compile using GCC on UNIX: gcc -O3 -msse2 -lhdf5 ../blosc/*.c blosc_filter.c \ example.c -o example -lpthread or, if you have the Blosc library already installed (recommended): gcc -O3 -lhdf5 -lblosc blosc_filter.c example.c -o example -lpthread Using MINGW on Windows: gcc -O3 -lhdf5 -lblosc blosc_filter.c example.c -o example Using Windows and MSVC (2008 or higher recommended): cl /Ox /Feexample.exe example.c ..\blosc\*.c blosc_filter.c Intel ICC compilers should work too. For activating the support for other compressors than the integrated BloscLZ (like LZ4, LZ4HC, Snappy or Zlib) see the README file in the main Blosc directory. Acknowledgments =============== This HDF5 filter interface and its example is based in the LZF interface (http://h5py.alfven.org) by Andrew Collette. PyTables-v.3.1.1/c-blosc/hdf5/blosc_filter.c000066400000000000000000000200331231437614300204700ustar00rootroot00000000000000/* Copyright (C) 2010 Francesc Alted http://blosc.pytables.org License: MIT (see LICENSE.txt) Filter program that allows the use of the Blosc filter in HDF5. This is based on the LZF filter interface (http://h5py.alfven.org) by Andrew Collette. */ #include #include #include #include #include "hdf5.h" #include "blosc_filter.h" #if H5Epush_vers == 2 /* 1.8.x */ #define PUSH_ERR(func, minor, str) H5Epush(H5E_DEFAULT, __FILE__, func, __LINE__, H5E_ERR_CLS, H5E_PLINE, minor, str) #else /* 1.6.x */ #define PUSH_ERR(func, minor, str) H5Epush(__FILE__, func, __LINE__, H5E_PLINE, minor, str) #endif #if H5Pget_filter_by_id_vers == 2 /* 1.8.x */ #define GET_FILTER(a,b,c,d,e,f,g) H5Pget_filter_by_id(a,b,c,d,e,f,g,NULL) #else /* 1.6.x */ #define GET_FILTER H5Pget_filter_by_id #endif #if H5Z_class_t_vers == 2 /* 1.8.x where x >= 3 */ #define H5Z_16API 0 #else /* 1.6.x and 1.8.x with x < 3*/ #define H5Z_16API 1 #endif size_t blosc_filter(unsigned flags, size_t cd_nelmts, const unsigned cd_values[], size_t nbytes, size_t *buf_size, void **buf); herr_t blosc_set_local(hid_t dcpl, hid_t type, hid_t space); /* Register the filter, passing on the HDF5 return value */ int register_blosc(char **version, char **date){ int retval; #if H5Z_16API H5Z_class_t filter_class = { (H5Z_filter_t)(FILTER_BLOSC), "blosc", NULL, (H5Z_set_local_func_t)(blosc_set_local), (H5Z_func_t)(blosc_filter) }; #else H5Z_class_t filter_class = { H5Z_CLASS_T_VERS, (H5Z_filter_t)(FILTER_BLOSC), 1, 1, "blosc", NULL, (H5Z_set_local_func_t)(blosc_set_local), (H5Z_func_t)(blosc_filter) }; #endif retval = H5Zregister(&filter_class); if(retval<0){ PUSH_ERR("register_blosc", H5E_CANTREGISTER, "Can't register Blosc filter"); } *version = strdup(BLOSC_VERSION_STRING); *date = strdup(BLOSC_VERSION_DATE); return 1; /* lib is available */ } /* Filter setup. Records the following inside the DCPL: 1. If version information is not present, set slots 0 and 1 to the filter revision and Blosc version, respectively. 2. Compute the type size in bytes and store it in slot 2. 3. Compute the chunk size in bytes and store it in slot 3. */ herr_t blosc_set_local(hid_t dcpl, hid_t type, hid_t space){ int ndims; int i; herr_t r; unsigned int typesize, basetypesize; unsigned int bufsize; hsize_t chunkdims[32]; unsigned int flags; size_t nelements = 8; unsigned int values[] = {0,0,0,0,0,0,0,0}; hid_t super_type; H5T_class_t class; r = GET_FILTER(dcpl, FILTER_BLOSC, &flags, &nelements, values, 0, NULL); if(r<0) return -1; if(nelements < 4) nelements = 4; /* First 4 slots reserved. */ /* Set Blosc info in first two slots */ values[0] = FILTER_BLOSC_VERSION; values[1] = BLOSC_VERSION_FORMAT; ndims = H5Pget_chunk(dcpl, 32, chunkdims); if(ndims<0) return -1; if(ndims>32){ PUSH_ERR("blosc_set_local", H5E_CALLBACK, "Chunk rank exceeds limit"); return -1; } typesize = H5Tget_size(type); if (typesize==0) return -1; /* Get the size of the base type, even for ARRAY types */ class = H5Tget_class(type); if (class == H5T_ARRAY) { /* Get the array base component */ super_type = H5Tget_super(type); basetypesize = H5Tget_size(super_type); /* Release resources */ H5Tclose(super_type); } else { basetypesize = typesize; } /* Limit large typesizes (they are pretty inneficient to shuffle and, in addition, Blosc does not handle typesizes larger than blocksizes). */ if (basetypesize > BLOSC_MAX_TYPESIZE) basetypesize = 1; values[2] = basetypesize; /* Get the size of the chunk */ bufsize = typesize; for (i=0; i= 5) { clevel = cd_values[4]; /* The compression level */ } if (cd_nelmts >= 6) { doshuffle = cd_values[5]; /* Shuffle? */ } if (cd_nelmts >= 7) { compcode = cd_values[6]; /* The Blosc compressor used */ /* Check that we actually have support for the compressor code */ complist = blosc_list_compressors(); code = blosc_compcode_to_compname(compcode, &compname); if (code == -1) { sprintf(errmsg, "this Blosc library does not have support for " "the '%s' compressor, but only for: %s", compname, complist); PUSH_ERR("blosc_filter", H5E_CALLBACK, errmsg); goto failed; } } /* We're compressing */ if(!(flags & H5Z_FLAG_REVERSE)){ #ifdef BLOSC_DEBUG fprintf(stderr, "Blosc: Compress %zd chunk w/buffer %zd\n", nbytes, outbuf_size); #endif /* Allocate an output buffer exactly as long as the input data; if the result is larger, we simply return 0. The filter is flagged as optional, so HDF5 marks the chunk as uncompressed and proceeds. */ outbuf_size = (*buf_size); outbuf = malloc(outbuf_size); if(outbuf == NULL){ PUSH_ERR("blosc_filter", H5E_CALLBACK, "Can't allocate compression buffer"); goto failed; } /* Select the correct compressor to use */ if (compname != NULL) blosc_set_compressor(compname); status = blosc_compress(clevel, doshuffle, typesize, nbytes, *buf, outbuf, nbytes); if (status < 0) { PUSH_ERR("blosc_filter", H5E_CALLBACK, "Blosc compression error"); goto failed; } /* We're decompressing */ } else { /* declare dummy variables */ size_t cbytes, blocksize; #ifdef BLOSC_DEBUG fprintf(stderr, "Blosc: Decompress %zd chunk w/buffer %zd\n", nbytes, outbuf_size); #endif free(outbuf); /* Extract the exact outbuf_size from the buffer header. * * NOTE: the guess value got from "cd_values" corresponds to the * uncompressed chunk size but it should not be used in a general * cases since other filters in the pipeline can modify the buffere * size. */ blosc_cbuffer_sizes(*buf, &outbuf_size, &cbytes, &blocksize); outbuf = malloc(outbuf_size); if(outbuf == NULL){ PUSH_ERR("blosc_filter", H5E_CALLBACK, "Can't allocate decompression buffer"); goto failed; } status = blosc_decompress(*buf, outbuf, outbuf_size); if(status <= 0){ /* decompression failed */ PUSH_ERR("blosc_filter", H5E_CALLBACK, "Blosc decompression error"); goto failed; } /* if !status */ } /* compressing vs decompressing */ if(status != 0){ free(*buf); *buf = outbuf; *buf_size = outbuf_size; return status; /* Size of compressed/decompressed data */ } failed: free(outbuf); return 0; } /* End filter function */ PyTables-v.3.1.1/c-blosc/hdf5/blosc_filter.h000066400000000000000000000007221231437614300205000ustar00rootroot00000000000000#ifndef FILTER_BLOSC_H #define FILTER_BLOSC_H #ifdef __cplusplus extern "C" { #endif #include "blosc.h" /* Filter revision number, starting at 1 */ /* #define FILTER_BLOSC_VERSION 1 */ #define FILTER_BLOSC_VERSION 2 /* multiple compressors since Blosc 1.3 */ /* Filter ID registered with the HDF Group */ #define FILTER_BLOSC 32001 /* Register the filter with the library */ int register_blosc(char **version, char **date); #ifdef __cplusplus } #endif #endif PyTables-v.3.1.1/c-blosc/hdf5/example.c000066400000000000000000000066031231437614300174630ustar00rootroot00000000000000/* Copyright (C) 2010 Francesc Alted http://blosc.pytables.org License: MIT (see LICENSE.txt) Example program demonstrating use of the Blosc filter from C code. This is based on the LZF example (http://h5py.alfven.org) by Andrew Collette. To compile this program: h5cc [-DH5_USE_16_API] -lblosc blosc_filter.c example.c \ -o example -lpthread To run: $ ./example Blosc version info: 1.3.0 ($Date:: 2014-01-11 #$) Success! $ h5ls -v example.h5 Opened "example.h5" with sec2 driver. dset Dataset {100/100, 100/100, 100/100} Location: 1:800 Links: 1 Chunks: {1, 100, 100} 40000 bytes Storage: 4000000 logical bytes, 126002 allocated bytes, 3174.55% utilization Filter-0: blosc-32001 OPT {2, 2, 4, 40000, 4, 1, 2} Type: native float */ #include #include "hdf5.h" #include "blosc_filter.h" #define SIZE 100*100*100 #define SHAPE {100,100,100} #define CHUNKSHAPE {1,100,100} int main(){ static float data[SIZE]; static float data_out[SIZE]; const hsize_t shape[] = SHAPE; const hsize_t chunkshape[] = CHUNKSHAPE; char *version, *date; int r, i; unsigned int cd_values[7]; int return_code = 1; hid_t fid, sid, dset, plist = 0; for(i=0; i0) H5Dclose(dset); if(sid>0) H5Sclose(sid); if(plist>0) H5Pclose(plist); if(fid>0) H5Fclose(fid); return return_code; } PyTables-v.3.1.1/c-blosc/internal-complibs/000077500000000000000000000000001231437614300204535ustar00rootroot00000000000000PyTables-v.3.1.1/c-blosc/internal-complibs/lz4-r113/000077500000000000000000000000001231437614300216505ustar00rootroot00000000000000PyTables-v.3.1.1/c-blosc/internal-complibs/lz4-r113/lz4.c000066400000000000000000000760121231437614300225330ustar00rootroot00000000000000/* LZ4 - Fast LZ compression algorithm Copyright (C) 2011-2014, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - LZ4 source repository : http://code.google.com/p/lz4/ - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c */ /************************************** Tuning parameters **************************************/ /* * MEMORY_USAGE : * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) * Increasing memory usage improves compression ratio * Reduced memory usage can improve speed, due to cache effect * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ #define MEMORY_USAGE 14 /* * HEAPMODE : * Select how default compression functions will allocate memory for their hash table, * in memory stack (0:default, fastest), or in memory heap (1:requires memory allocation (malloc)). */ #define HEAPMODE 0 /************************************** CPU Feature Detection **************************************/ /* 32 or 64 bits ? */ #if (defined(__x86_64__) || defined(_M_X64) || defined(_WIN64) \ || defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) \ || defined(__64BIT__) || defined(_LP64) || defined(__LP64__) \ || defined(__ia64) || defined(__itanium__) || defined(_M_IA64) ) /* Detects 64 bits mode */ # define LZ4_ARCH64 1 #else # define LZ4_ARCH64 0 #endif /* * Little Endian or Big Endian ? * Overwrite the #define below if you know your architecture endianess */ #if defined (__GLIBC__) # include # if (__BYTE_ORDER == __BIG_ENDIAN) # define LZ4_BIG_ENDIAN 1 # endif #elif (defined(__BIG_ENDIAN__) || defined(__BIG_ENDIAN) || defined(_BIG_ENDIAN)) && !(defined(__LITTLE_ENDIAN__) || defined(__LITTLE_ENDIAN) || defined(_LITTLE_ENDIAN)) # define LZ4_BIG_ENDIAN 1 #elif defined(__sparc) || defined(__sparc__) \ || defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) \ || defined(__hpux) || defined(__hppa) \ || defined(_MIPSEB) || defined(__s390__) # define LZ4_BIG_ENDIAN 1 #else /* Little Endian assumed. PDP Endian and other very rare endian format are unsupported. */ #endif /* * Unaligned memory access is automatically enabled for "common" CPU, such as x86. * For others CPU, such as ARM, the compiler may be more cautious, inserting unnecessary extra code to ensure aligned access property * If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance */ #if defined(__ARM_FEATURE_UNALIGNED) # define LZ4_FORCE_UNALIGNED_ACCESS 1 #endif /* Define this parameter if your target system or compiler does not support hardware bit count */ #if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */ # define LZ4_FORCE_SW_BITCOUNT #endif /* * BIG_ENDIAN_NATIVE_BUT_INCOMPATIBLE : * This option may provide a small boost to performance for some big endian cpu, although probably modest. * You may set this option to 1 if data will remain within closed environment. * This option is useless on Little_Endian CPU (such as x86) */ /* #define BIG_ENDIAN_NATIVE_BUT_INCOMPATIBLE 1 */ /************************************** Compiler Options **************************************/ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ /* "restrict" is a known keyword */ #else # define restrict /* Disable restrict */ #endif #ifdef _MSC_VER /* Visual Studio */ # define FORCE_INLINE static __forceinline # include /* For Visual 2005 */ # if LZ4_ARCH64 /* 64-bits */ # pragma intrinsic(_BitScanForward64) /* For Visual 2005 */ # pragma intrinsic(_BitScanReverse64) /* For Visual 2005 */ # else /* 32-bits */ # pragma intrinsic(_BitScanForward) /* For Visual 2005 */ # pragma intrinsic(_BitScanReverse) /* For Visual 2005 */ # endif # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ #else # ifdef __GNUC__ # define FORCE_INLINE static inline __attribute__((always_inline)) # else # define FORCE_INLINE static inline # endif #endif #ifdef _MSC_VER /* Visual Studio */ # define lz4_bswap16(x) _byteswap_ushort(x) #else # define lz4_bswap16(x) ((unsigned short int) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))) #endif #define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) #if (GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__) # define expect(expr,value) (__builtin_expect ((expr),(value)) ) #else # define expect(expr,value) (expr) #endif #define likely(expr) expect((expr) != 0, 1) #define unlikely(expr) expect((expr) != 0, 0) /************************************** Memory routines **************************************/ #include /* malloc, calloc, free */ #define ALLOCATOR(n,s) calloc(n,s) #define FREEMEM free #include /* memset, memcpy */ #define MEM_INIT memset /************************************** Includes **************************************/ #include "lz4.h" /************************************** Basic Types **************************************/ #if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ # include typedef uint8_t BYTE; typedef uint16_t U16; typedef uint32_t U32; typedef int32_t S32; typedef uint64_t U64; #else typedef unsigned char BYTE; typedef unsigned short U16; typedef unsigned int U32; typedef signed int S32; typedef unsigned long long U64; #endif #if defined(__GNUC__) && !defined(LZ4_FORCE_UNALIGNED_ACCESS) # define _PACKED __attribute__ ((packed)) #else # define _PACKED #endif #if !defined(LZ4_FORCE_UNALIGNED_ACCESS) && !defined(__GNUC__) # if defined(__IBMC__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) # pragma pack(1) # else # pragma pack(push, 1) # endif #endif typedef struct { U16 v; } _PACKED U16_S; typedef struct { U32 v; } _PACKED U32_S; typedef struct { U64 v; } _PACKED U64_S; typedef struct {size_t v;} _PACKED size_t_S; #if !defined(LZ4_FORCE_UNALIGNED_ACCESS) && !defined(__GNUC__) # if defined(__SUNPRO_C) || defined(__SUNPRO_CC) # pragma pack(0) # else # pragma pack(pop) # endif #endif #define A16(x) (((U16_S *)(x))->v) #define A32(x) (((U32_S *)(x))->v) #define A64(x) (((U64_S *)(x))->v) #define AARCH(x) (((size_t_S *)(x))->v) /************************************** Constants **************************************/ #define LZ4_HASHLOG (MEMORY_USAGE-2) #define HASHTABLESIZE (1 << MEMORY_USAGE) #define HASHNBCELLS4 (1 << LZ4_HASHLOG) #define MINMATCH 4 #define COPYLENGTH 8 #define LASTLITERALS 5 #define MFLIMIT (COPYLENGTH+MINMATCH) static const int LZ4_minLength = (MFLIMIT+1); #define KB *(1U<<10) #define MB *(1U<<20) #define GB *(1U<<30) #define LZ4_64KLIMIT ((64 KB) + (MFLIMIT-1)) #define SKIPSTRENGTH 6 /* Increasing this value will make the compression run slower on incompressible data */ #define MAXD_LOG 16 #define MAX_DISTANCE ((1 << MAXD_LOG) - 1) #define ML_BITS 4 #define ML_MASK ((1U<=e; */ #else # define LZ4_WILDCOPY(d,s,e) { if (likely(e-d <= 8)) LZ4_COPY8(d,s) else do { LZ4_COPY8(d,s) } while (d>3); # elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_clzll(val) >> 3); # else int r; if (!(val>>32)) { r=4; } else { r=0; val>>=32; } if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } r += (!val); return r; # endif # else # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanForward64( &r, val ); return (int)(r>>3); # elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_ctzll(val) >> 3); # else static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; # endif # endif } #else FORCE_INLINE int LZ4_NbCommonBytes (register U32 val) { # if defined(LZ4_BIG_ENDIAN) # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanReverse( &r, val ); return (int)(r>>3); # elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_clz(val) >> 3); # else int r; if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } r += (!val); return r; # endif # else # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r; _BitScanForward( &r, val ); return (int)(r>>3); # elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_ctz(val) >> 3); # else static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; # endif # endif } #endif /**************************** Compression functions ****************************/ FORCE_INLINE int LZ4_hashSequence(U32 sequence, tableType_t tableType) { if (tableType == byU16) return (((sequence) * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); else return (((sequence) * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); } FORCE_INLINE int LZ4_hashPosition(const BYTE* p, tableType_t tableType) { return LZ4_hashSequence(A32(p), tableType); } FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase) { switch (tableType) { case byPtr: { const BYTE** hashTable = (const BYTE**) tableBase; hashTable[h] = p; break; } case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); break; } case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); break; } } } FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) { U32 h = LZ4_hashPosition(p, tableType); LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); } FORCE_INLINE const BYTE* LZ4_getPositionOnHash(U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase) { if (tableType == byPtr) { const BYTE** hashTable = (const BYTE**) tableBase; return hashTable[h]; } if (tableType == byU32) { U32* hashTable = (U32*) tableBase; return hashTable[h] + srcBase; } { U16* hashTable = (U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */ } FORCE_INLINE const BYTE* LZ4_getPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) { U32 h = LZ4_hashPosition(p, tableType); return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); } FORCE_INLINE int LZ4_compress_generic( void* ctx, const char* source, char* dest, int inputSize, int maxOutputSize, limitedOutput_directive limitedOutput, tableType_t tableType, prefix64k_directive prefix) { const BYTE* ip = (const BYTE*) source; const BYTE* const base = (prefix==withPrefix) ? ((LZ4_Data_Structure*)ctx)->base : (const BYTE*) source; const BYTE* const lowLimit = ((prefix==withPrefix) ? ((LZ4_Data_Structure*)ctx)->bufferStart : (const BYTE*)source); const BYTE* anchor = (const BYTE*) source; const BYTE* const iend = ip + inputSize; const BYTE* const mflimit = iend - MFLIMIT; const BYTE* const matchlimit = iend - LASTLITERALS; BYTE* op = (BYTE*) dest; BYTE* const oend = op + maxOutputSize; int length; const int skipStrength = SKIPSTRENGTH; U32 forwardH; /* Init conditions */ if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */ if ((prefix==withPrefix) && (ip != ((LZ4_Data_Structure*)ctx)->nextBlock)) return 0; /* must continue from end of previous block */ if (prefix==withPrefix) ((LZ4_Data_Structure*)ctx)->nextBlock=iend; /* do it now, due to potential early exit */ if ((tableType == byU16) && (inputSize>=(int)LZ4_64KLIMIT)) return 0; /* Size too large (not within 64K limit) */ if (inputSize> skipStrength; ip = forwardIp; forwardIp = ip + step; if (unlikely(forwardIp > mflimit)) { goto _last_literals; } forwardH = LZ4_hashPosition(forwardIp, tableType); ref = LZ4_getPositionOnHash(h, ctx, tableType, base); LZ4_putPositionOnHash(ip, h, ctx, tableType, base); } while ((ref + MAX_DISTANCE < ip) || (A32(ref) != A32(ip))); /* Catch up */ while ((ip>anchor) && (ref > lowLimit) && (unlikely(ip[-1]==ref[-1]))) { ip--; ref--; } /* Encode Literal length */ length = (int)(ip - anchor); token = op++; if ((limitedOutput) && (unlikely(op + length + (2 + 1 + LASTLITERALS) + (length/255) > oend))) return 0; /* Check output limit */ if (length>=(int)RUN_MASK) { int len = length-RUN_MASK; *token=(RUN_MASK<= 255 ; len-=255) *op++ = 255; *op++ = (BYTE)len; } else *token = (BYTE)(length<>8) > oend))) return 0; /* Check output limit */ if (length>=(int)ML_MASK) { *token += ML_MASK; length -= ML_MASK; for (; length > 509 ; length-=510) { *op++ = 255; *op++ = 255; } if (length >= 255) { length-=255; *op++ = 255; } *op++ = (BYTE)length; } else *token += (BYTE)(length); /* Test end of chunk */ if (ip > mflimit) { anchor = ip; break; } /* Fill table */ LZ4_putPosition(ip-2, ctx, tableType, base); /* Test next position */ ref = LZ4_getPosition(ip, ctx, tableType, base); LZ4_putPosition(ip, ctx, tableType, base); if ((ref + MAX_DISTANCE >= ip) && (A32(ref) == A32(ip))) { token = op++; *token=0; goto _next_match; } /* Prepare next loop */ anchor = ip++; forwardH = LZ4_hashPosition(ip, tableType); } _last_literals: /* Encode Last Literals */ { int lastRun = (int)(iend - anchor); if ((limitedOutput) && (((char*)op - dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize)) return 0; /* Check output limit */ if (lastRun>=(int)RUN_MASK) { *op++=(RUN_MASK<= 255 ; lastRun-=255) *op++ = 255; *op++ = (BYTE) lastRun; } else *op++ = (BYTE)(lastRun<hashTable, 0, sizeof(lz4ds->hashTable)); lz4ds->bufferStart = base; lz4ds->base = base; lz4ds->nextBlock = base; } int LZ4_resetStreamState(void* state, const char* inputBuffer) { if ((((size_t)state) & 3) != 0) return 1; /* Error : pointer is not aligned on 4-bytes boundary */ LZ4_init((LZ4_Data_Structure*)state, (const BYTE*)inputBuffer); return 0; } void* LZ4_create (const char* inputBuffer) { void* lz4ds = ALLOCATOR(1, sizeof(LZ4_Data_Structure)); LZ4_init ((LZ4_Data_Structure*)lz4ds, (const BYTE*)inputBuffer); return lz4ds; } int LZ4_free (void* LZ4_Data) { FREEMEM(LZ4_Data); return (0); } char* LZ4_slideInputBuffer (void* LZ4_Data) { LZ4_Data_Structure* lz4ds = (LZ4_Data_Structure*)LZ4_Data; size_t delta = lz4ds->nextBlock - (lz4ds->bufferStart + 64 KB); if ( (lz4ds->base - delta > lz4ds->base) /* underflow control */ || ((size_t)(lz4ds->nextBlock - lz4ds->base) > 0xE0000000) ) /* close to 32-bits limit */ { size_t deltaLimit = (lz4ds->nextBlock - 64 KB) - lz4ds->base; int nH; for (nH=0; nH < HASHNBCELLS4; nH++) { if ((size_t)(lz4ds->hashTable[nH]) < deltaLimit) lz4ds->hashTable[nH] = 0; else lz4ds->hashTable[nH] -= (U32)deltaLimit; } memcpy((void*)(lz4ds->bufferStart), (const void*)(lz4ds->nextBlock - 64 KB), 64 KB); lz4ds->base = lz4ds->bufferStart; lz4ds->nextBlock = lz4ds->base + 64 KB; } else { memcpy((void*)(lz4ds->bufferStart), (const void*)(lz4ds->nextBlock - 64 KB), 64 KB); lz4ds->nextBlock -= delta; lz4ds->base -= delta; } return (char*)(lz4ds->nextBlock); } int LZ4_compress_continue (void* LZ4_Data, const char* source, char* dest, int inputSize) { return LZ4_compress_generic(LZ4_Data, source, dest, inputSize, 0, notLimited, byU32, withPrefix); } int LZ4_compress_limitedOutput_continue (void* LZ4_Data, const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_generic(LZ4_Data, source, dest, inputSize, maxOutputSize, limited, byU32, withPrefix); } /**************************** Decompression functions ****************************/ /* * This generic decompression function cover all use cases. * It shall be instanciated several times, using different sets of directives * Note that it is essential this generic function is really inlined, * in order to remove useless branches during compilation optimisation. */ FORCE_INLINE int LZ4_decompress_generic( const char* source, char* dest, int inputSize, int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */ int endOnInput, /* endOnOutputSize, endOnInputSize */ int prefix64k, /* noPrefix, withPrefix */ int partialDecoding, /* full, partial */ int targetOutputSize /* only used if partialDecoding==partial */ ) { /* Local Variables */ const BYTE* restrict ip = (const BYTE*) source; const BYTE* ref; const BYTE* const iend = ip + inputSize; BYTE* op = (BYTE*) dest; BYTE* const oend = op + outputSize; BYTE* cpy; BYTE* oexit = op + targetOutputSize; /*const size_t dec32table[] = {0, 3, 2, 3, 0, 0, 0, 0}; / static reduces speed for LZ4_decompress_safe() on GCC64 */ const size_t dec32table[] = {4-0, 4-3, 4-2, 4-3, 4-0, 4-0, 4-0, 4-0}; /* static reduces speed for LZ4_decompress_safe() on GCC64 */ static const size_t dec64table[] = {0, 0, 0, (size_t)-1, 0, 1, 2, 3}; /* Special cases */ if ((partialDecoding) && (oexit> oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => decode everything */ if ((endOnInput) && (unlikely(outputSize==0))) return ((inputSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */ if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1); /* Main Loop */ while (1) { unsigned token; size_t length; /* get runlength */ token = *ip++; if ((length=(token>>ML_BITS)) == RUN_MASK) { unsigned s=255; while (((endOnInput)?ip(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) ) || ((!endOnInput) && (cpy>oend-COPYLENGTH))) { if (partialDecoding) { if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */ if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */ } else { if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */ if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */ } memcpy(op, ip, length); ip += length; op += length; break; /* Necessarily EOF, due to parsing restrictions */ } LZ4_WILDCOPY(op, ip, cpy); ip -= (op-cpy); op = cpy; /* get offset */ LZ4_READ_LITTLEENDIAN_16(ref,cpy,ip); ip+=2; if ((prefix64k==noPrefix) && (unlikely(ref < (BYTE* const)dest))) goto _output_error; /* Error : offset outside destination buffer */ /* get matchlength */ if ((length=(token&ML_MASK)) == ML_MASK) { while ((!endOnInput) || (ipoend-COPYLENGTH-(STEPSIZE-4))) { if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last 5 bytes must be literals */ LZ4_SECURECOPY(op, ref, (oend-COPYLENGTH)); while(op (unsigned int)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) static inline int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } /* LZ4_compressBound() : Provides the maximum size that LZ4 may output in a "worst case" scenario (input data not compressible) primarily useful for memory allocation of output buffer. inline function is recommended for the general case, macro is also provided when result needs to be evaluated at compilation (such as stack memory allocation). isize : is the input size. Max supported value is LZ4_MAX_INPUT_SIZE return : maximum output size in a "worst case" scenario or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE) */ int LZ4_compress_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize); /* LZ4_compress_limitedOutput() : Compress 'inputSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'. If it cannot achieve it, compression will stop, and result of the function will be zero. This function never writes outside of provided output buffer. inputSize : Max supported value is LZ4_MAX_INPUT_VALUE maxOutputSize : is the size of the destination buffer (which must be already allocated) return : the number of bytes written in buffer 'dest' or 0 if the compression fails */ int LZ4_decompress_fast (const char* source, char* dest, int outputSize); /* LZ4_decompress_fast() : outputSize : is the original (uncompressed) size return : the number of bytes read from the source buffer (in other words, the compressed size) If the source stream is malformed, the function will stop decoding and return a negative result. note : This function is a bit faster than LZ4_decompress_safe() This function never writes outside of output buffers, but may read beyond input buffer in case of malicious data packet. Use this function preferably into a trusted environment (data to decode comes from a trusted source). Destination buffer must be already allocated. Its size must be a minimum of 'outputSize' bytes. */ int LZ4_decompress_safe_partial (const char* source, char* dest, int inputSize, int targetOutputSize, int maxOutputSize); /* LZ4_decompress_safe_partial() : This function decompress a compressed block of size 'inputSize' at position 'source' into output buffer 'dest' of size 'maxOutputSize'. The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached, reducing decompression time. return : the number of bytes decoded in the destination buffer (necessarily <= maxOutputSize) Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller. Always control how many bytes were decoded. If the source stream is detected malformed, the function will stop decoding and return a negative result. This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets */ int LZ4_sizeofState(); int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); /* These functions are provided should you prefer to allocate memory for compression tables with your own allocation methods. To know how much memory must be allocated for the compression tables, use : int LZ4_sizeofState(); Note that tables must be aligned on 4-bytes boundaries, otherwise compression will fail (return code 0). The allocated memory can be provided to the compressions functions using 'void* state' parameter. LZ4_compress_withState() and LZ4_compress_limitedOutput_withState() are equivalent to previously described functions. They just use the externally allocated memory area instead of allocating their own (on stack, or on heap). */ /************************************** Streaming Functions **************************************/ void* LZ4_create (const char* inputBuffer); int LZ4_compress_continue (void* LZ4_Data, const char* source, char* dest, int inputSize); int LZ4_compress_limitedOutput_continue (void* LZ4_Data, const char* source, char* dest, int inputSize, int maxOutputSize); char* LZ4_slideInputBuffer (void* LZ4_Data); int LZ4_free (void* LZ4_Data); /* These functions allow the compression of dependent blocks, where each block benefits from prior 64 KB within preceding blocks. In order to achieve this, it is necessary to start creating the LZ4 Data Structure, thanks to the function : void* LZ4_create (const char* inputBuffer); The result of the function is the (void*) pointer on the LZ4 Data Structure. This pointer will be needed in all other functions. If the pointer returned is NULL, then the allocation has failed, and compression must be aborted. The only parameter 'const char* inputBuffer' must, obviously, point at the beginning of input buffer. The input buffer must be already allocated, and size at least 192KB. 'inputBuffer' will also be the 'const char* source' of the first block. All blocks are expected to lay next to each other within the input buffer, starting from 'inputBuffer'. To compress each block, use either LZ4_compress_continue() or LZ4_compress_limitedOutput_continue(). Their behavior are identical to LZ4_compress() or LZ4_compress_limitedOutput(), but require the LZ4 Data Structure as their first argument, and check that each block starts right after the previous one. If next block does not begin immediately after the previous one, the compression will fail (return 0). When it's no longer possible to lay the next block after the previous one (not enough space left into input buffer), a call to : char* LZ4_slideInputBuffer(void* LZ4_Data); must be performed. It will typically copy the latest 64KB of input at the beginning of input buffer. Note that, for this function to work properly, minimum size of an input buffer must be 192KB. ==> The memory position where the next input data block must start is provided as the result of the function. Compression can then resume, using LZ4_compress_continue() or LZ4_compress_limitedOutput_continue(), as usual. When compression is completed, a call to LZ4_free() will release the memory used by the LZ4 Data Structure. */ int LZ4_sizeofStreamState(); int LZ4_resetStreamState(void* state, const char* inputBuffer); /* These functions achieve the same result as : void* LZ4_create (const char* inputBuffer); They are provided here to allow the user program to allocate memory using its own routines. To know how much space must be allocated, use LZ4_sizeofStreamState(); Note also that space must be 4-bytes aligned. Once space is allocated, you must initialize it using : LZ4_resetStreamState(void* state, const char* inputBuffer); void* state is a pointer to the space allocated. It must be aligned on 4-bytes boundaries, and be large enough. The parameter 'const char* inputBuffer' must, obviously, point at the beginning of input buffer. The input buffer must be already allocated, and size at least 192KB. 'inputBuffer' will also be the 'const char* source' of the first block. The same space can be re-used multiple times, just by initializing it each time with LZ4_resetStreamState(). return value of LZ4_resetStreamState() must be 0 is OK. Any other value means there was an error (typically, pointer is not aligned on 4-bytes boundaries). */ int LZ4_decompress_safe_withPrefix64k (const char* source, char* dest, int inputSize, int maxOutputSize); int LZ4_decompress_fast_withPrefix64k (const char* source, char* dest, int outputSize); /* *_withPrefix64k() : These decoding functions work the same as their "normal name" versions, but can use up to 64KB of data in front of 'char* dest'. These functions are necessary to decode inter-dependant blocks. */ /************************************** Obsolete Functions **************************************/ /* These functions are deprecated and should no longer be used. They are provided here for compatibility with existing user programs. */ static inline int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); } static inline int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); } #if defined (__cplusplus) } #endif PyTables-v.3.1.1/c-blosc/internal-complibs/lz4-r113/lz4hc.c000066400000000000000000000717211231437614300230500ustar00rootroot00000000000000/* LZ4 HC - High Compression Mode of LZ4 Copyright (C) 2011-2014, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html - LZ4 source repository : http://code.google.com/p/lz4/ */ /************************************** Tuning Parameter **************************************/ #define LZ4HC_DEFAULT_COMPRESSIONLEVEL 8 /************************************** Memory routines **************************************/ #include /* calloc, free */ #define ALLOCATOR(s) calloc(1,s) #define FREEMEM free #include /* memset, memcpy */ #define MEM_INIT memset /************************************** CPU Feature Detection **************************************/ /* 32 or 64 bits ? */ #if (defined(__x86_64__) || defined(_M_X64) || defined(_WIN64) \ || defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) \ || defined(__64BIT__) || defined(_LP64) || defined(__LP64__) \ || defined(__ia64) || defined(__itanium__) || defined(_M_IA64) ) /* Detects 64 bits mode */ # define LZ4_ARCH64 1 #else # define LZ4_ARCH64 0 #endif /* * Little Endian or Big Endian ? * Overwrite the #define below if you know your architecture endianess */ #if defined (__GLIBC__) # include # if (__BYTE_ORDER == __BIG_ENDIAN) # define LZ4_BIG_ENDIAN 1 # endif #elif (defined(__BIG_ENDIAN__) || defined(__BIG_ENDIAN) || defined(_BIG_ENDIAN)) && !(defined(__LITTLE_ENDIAN__) || defined(__LITTLE_ENDIAN) || defined(_LITTLE_ENDIAN)) # define LZ4_BIG_ENDIAN 1 #elif defined(__sparc) || defined(__sparc__) \ || defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) \ || defined(__hpux) || defined(__hppa) \ || defined(_MIPSEB) || defined(__s390__) # define LZ4_BIG_ENDIAN 1 #else /* Little Endian assumed. PDP Endian and other very rare endian format are unsupported. */ #endif /* * Unaligned memory access is automatically enabled for "common" CPU, such as x86. * For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected * If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance */ #if defined(__ARM_FEATURE_UNALIGNED) # define LZ4_FORCE_UNALIGNED_ACCESS 1 #endif /* Define this parameter if your target system or compiler does not support hardware bit count */ #if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */ # define LZ4_FORCE_SW_BITCOUNT #endif /************************************** Compiler Options **************************************/ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ /* "restrict" is a known keyword */ #else # define restrict /* Disable restrict */ #endif #ifdef _MSC_VER /* Visual Studio */ # define FORCE_INLINE static __forceinline # include /* For Visual 2005 */ # if LZ4_ARCH64 /* 64-bits */ # pragma intrinsic(_BitScanForward64) /* For Visual 2005 */ # pragma intrinsic(_BitScanReverse64) /* For Visual 2005 */ # else /* 32-bits */ # pragma intrinsic(_BitScanForward) /* For Visual 2005 */ # pragma intrinsic(_BitScanReverse) /* For Visual 2005 */ # endif # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4701) /* disable: C4701: potentially uninitialized local variable used */ #else # ifdef __GNUC__ # define FORCE_INLINE static inline __attribute__((always_inline)) # else # define FORCE_INLINE static inline # endif #endif #ifdef _MSC_VER /* Visual Studio */ # define lz4_bswap16(x) _byteswap_ushort(x) #else # define lz4_bswap16(x) ((unsigned short int) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))) #endif /************************************** Includes **************************************/ #include "lz4hc.h" #include "lz4.h" /************************************** Basic Types **************************************/ #if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ # include typedef uint8_t BYTE; typedef uint16_t U16; typedef uint32_t U32; typedef int32_t S32; typedef uint64_t U64; #else typedef unsigned char BYTE; typedef unsigned short U16; typedef unsigned int U32; typedef signed int S32; typedef unsigned long long U64; #endif #if defined(__GNUC__) && !defined(LZ4_FORCE_UNALIGNED_ACCESS) # define _PACKED __attribute__ ((packed)) #else # define _PACKED #endif #if !defined(LZ4_FORCE_UNALIGNED_ACCESS) && !defined(__GNUC__) # ifdef __IBMC__ # pragma pack(1) # else # pragma pack(push, 1) # endif #endif typedef struct _U16_S { U16 v; } _PACKED U16_S; typedef struct _U32_S { U32 v; } _PACKED U32_S; typedef struct _U64_S { U64 v; } _PACKED U64_S; #if !defined(LZ4_FORCE_UNALIGNED_ACCESS) && !defined(__GNUC__) # pragma pack(pop) #endif #define A64(x) (((U64_S *)(x))->v) #define A32(x) (((U32_S *)(x))->v) #define A16(x) (((U16_S *)(x))->v) /************************************** Constants **************************************/ #define MINMATCH 4 #define DICTIONARY_LOGSIZE 16 #define MAXD (1<> ((MINMATCH*8)-HASH_LOG)) #define HASH_VALUE(p) HASH_FUNCTION(A32(p)) #define HASH_POINTER(p) (HashTable[HASH_VALUE(p)] + base) #define DELTANEXT(p) chainTable[(size_t)(p) & MAXD_MASK] #define GETNEXT(p) ((p) - (size_t)DELTANEXT(p)) /************************************** Private functions **************************************/ #if LZ4_ARCH64 FORCE_INLINE int LZ4_NbCommonBytes (register U64 val) { #if defined(LZ4_BIG_ENDIAN) # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanReverse64( &r, val ); return (int)(r>>3); # elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_clzll(val) >> 3); # else int r; if (!(val>>32)) { r=4; } else { r=0; val>>=32; } if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } r += (!val); return r; # endif #else # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanForward64( &r, val ); return (int)(r>>3); # elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_ctzll(val) >> 3); # else static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; return DeBruijnBytePos[((U64)((val & -val) * 0x0218A392CDABBD3F)) >> 58]; # endif #endif } #else FORCE_INLINE int LZ4_NbCommonBytes (register U32 val) { #if defined(LZ4_BIG_ENDIAN) # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r; _BitScanReverse( &r, val ); return (int)(r>>3); # elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_clz(val) >> 3); # else int r; if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } r += (!val); return r; # endif #else # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r; _BitScanForward( &r, val ); return (int)(r>>3); # elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_ctz(val) >> 3); # else static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; # endif #endif } #endif int LZ4_sizeofStreamStateHC() { return sizeof(LZ4HC_Data_Structure); } FORCE_INLINE void LZ4_initHC (LZ4HC_Data_Structure* hc4, const BYTE* base) { MEM_INIT((void*)hc4->hashTable, 0, sizeof(hc4->hashTable)); MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable)); hc4->nextToUpdate = base + 1; hc4->base = base; hc4->inputBuffer = base; hc4->end = base; } int LZ4_resetStreamStateHC(void* state, const char* inputBuffer) { if ((((size_t)state) & (sizeof(void*)-1)) != 0) return 1; /* Error : pointer is not aligned for pointer (32 or 64 bits) */ LZ4_initHC((LZ4HC_Data_Structure*)state, (const BYTE*)inputBuffer); return 0; } void* LZ4_createHC (const char* inputBuffer) { void* hc4 = ALLOCATOR(sizeof(LZ4HC_Data_Structure)); LZ4_initHC ((LZ4HC_Data_Structure*)hc4, (const BYTE*)inputBuffer); return hc4; } int LZ4_freeHC (void* LZ4HC_Data) { FREEMEM(LZ4HC_Data); return (0); } /* Update chains up to ip (excluded) */ FORCE_INLINE void LZ4HC_Insert (LZ4HC_Data_Structure* hc4, const BYTE* ip) { U16* chainTable = hc4->chainTable; HTYPE* HashTable = hc4->hashTable; INITBASE(base,hc4->base); while(hc4->nextToUpdate < ip) { const BYTE* const p = hc4->nextToUpdate; size_t delta = (p) - HASH_POINTER(p); if (delta>MAX_DISTANCE) delta = MAX_DISTANCE; DELTANEXT(p) = (U16)delta; HashTable[HASH_VALUE(p)] = (HTYPE)((p) - base); hc4->nextToUpdate++; } } char* LZ4_slideInputBufferHC(void* LZ4HC_Data) { LZ4HC_Data_Structure* hc4 = (LZ4HC_Data_Structure*)LZ4HC_Data; U32 distance = (U32)(hc4->end - hc4->inputBuffer) - 64 KB; distance = (distance >> 16) << 16; /* Must be a multiple of 64 KB */ LZ4HC_Insert(hc4, hc4->end - MINMATCH); memcpy((void*)(hc4->end - 64 KB - distance), (const void*)(hc4->end - 64 KB), 64 KB); hc4->nextToUpdate -= distance; hc4->base -= distance; if ((U32)(hc4->inputBuffer - hc4->base) > 1 GB + 64 KB) /* Avoid overflow */ { int i; hc4->base += 1 GB; for (i=0; ihashTable[i] -= 1 GB; } hc4->end -= distance; return (char*)(hc4->end); } FORCE_INLINE size_t LZ4HC_CommonLength (const BYTE* p1, const BYTE* p2, const BYTE* const matchlimit) { const BYTE* p1t = p1; while (p1tchainTable; HTYPE* const HashTable = hc4->hashTable; const BYTE* ref; INITBASE(base,hc4->base); int nbAttempts=maxNbAttempts; size_t repl=0, ml=0; U16 delta=0; /* useless assignment, to remove an uninitialization warning */ /* HC4 match finder */ LZ4HC_Insert(hc4, ip); ref = HASH_POINTER(ip); #define REPEAT_OPTIMIZATION #ifdef REPEAT_OPTIMIZATION /* Detect repetitive sequences of length <= 4 */ if ((U32)(ip-ref) <= 4) /* potential repetition */ { if (A32(ref) == A32(ip)) /* confirmed */ { delta = (U16)(ip-ref); repl = ml = LZ4HC_CommonLength(ip+MINMATCH, ref+MINMATCH, matchlimit) + MINMATCH; *matchpos = ref; } ref = GETNEXT(ref); } #endif while (((U32)(ip-ref) <= MAX_DISTANCE) && (nbAttempts)) { nbAttempts--; if (*(ref+ml) == *(ip+ml)) if (A32(ref) == A32(ip)) { size_t mlt = LZ4HC_CommonLength(ip+MINMATCH, ref+MINMATCH, matchlimit) + MINMATCH; if (mlt > ml) { ml = mlt; *matchpos = ref; } } ref = GETNEXT(ref); } #ifdef REPEAT_OPTIMIZATION /* Complete table */ if (repl) { const BYTE* ptr = ip; const BYTE* end; end = ip + repl - (MINMATCH-1); while(ptr < end-delta) { DELTANEXT(ptr) = delta; /* Pre-Load */ ptr++; } do { DELTANEXT(ptr) = delta; HashTable[HASH_VALUE(ptr)] = (HTYPE)((ptr) - base); /* Head of chain */ ptr++; } while(ptr < end); hc4->nextToUpdate = end; } #endif return (int)ml; } FORCE_INLINE int LZ4HC_InsertAndGetWiderMatch (LZ4HC_Data_Structure* hc4, const BYTE* ip, const BYTE* startLimit, const BYTE* matchlimit, int longest, const BYTE** matchpos, const BYTE** startpos, const int maxNbAttempts) { U16* const chainTable = hc4->chainTable; HTYPE* const HashTable = hc4->hashTable; INITBASE(base,hc4->base); const BYTE* ref; int nbAttempts = maxNbAttempts; int delta = (int)(ip-startLimit); /* First Match */ LZ4HC_Insert(hc4, ip); ref = HASH_POINTER(ip); while (((U32)(ip-ref) <= MAX_DISTANCE) && (nbAttempts)) { nbAttempts--; if (*(startLimit + longest) == *(ref - delta + longest)) if (A32(ref) == A32(ip)) { #if 1 const BYTE* reft = ref+MINMATCH; const BYTE* ipt = ip+MINMATCH; const BYTE* startt = ip; while (iptstartLimit) && (reft > hc4->inputBuffer) && (startt[-1] == reft[-1])) {startt--; reft--;} if ((ipt-startt) > longest) { longest = (int)(ipt-startt); *matchpos = reft; *startpos = startt; } } ref = GETNEXT(ref); } return longest; } typedef enum { noLimit = 0, limitedOutput = 1 } limitedOutput_directive; FORCE_INLINE int LZ4HC_encodeSequence ( const BYTE** ip, BYTE** op, const BYTE** anchor, int matchLength, const BYTE* ref, limitedOutput_directive limitedOutputBuffer, BYTE* oend) { int length; BYTE* token; /* Encode Literal length */ length = (int)(*ip - *anchor); token = (*op)++; if ((limitedOutputBuffer) && ((*op + length + (2 + 1 + LASTLITERALS) + (length>>8)) > oend)) return 1; /* Check output limit */ if (length>=(int)RUN_MASK) { int len; *token=(RUN_MASK< 254 ; len-=255) *(*op)++ = 255; *(*op)++ = (BYTE)len; } else *token = (BYTE)(length<>8) > oend)) return 1; /* Check output limit */ if (length>=(int)ML_MASK) { *token+=ML_MASK; length-=ML_MASK; for(; length > 509 ; length-=510) { *(*op)++ = 255; *(*op)++ = 255; } if (length > 254) { length-=255; *(*op)++ = 255; } *(*op)++ = (BYTE)length; } else *token += (BYTE)(length); /* Prepare next loop */ *ip += matchLength; *anchor = *ip; return 0; } #define MAX_COMPRESSION_LEVEL 16 static int LZ4HC_compress_generic ( void* ctxvoid, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel, limitedOutput_directive limit ) { LZ4HC_Data_Structure* ctx = (LZ4HC_Data_Structure*) ctxvoid; const BYTE* ip = (const BYTE*) source; const BYTE* anchor = ip; const BYTE* const iend = ip + inputSize; const BYTE* const mflimit = iend - MFLIMIT; const BYTE* const matchlimit = (iend - LASTLITERALS); BYTE* op = (BYTE*) dest; BYTE* const oend = op + maxOutputSize; const int maxNbAttempts = compressionLevel > MAX_COMPRESSION_LEVEL ? 1 << MAX_COMPRESSION_LEVEL : compressionLevel ? 1<<(compressionLevel-1) : 1<end) return 0; ctx->end += inputSize; ip++; /* Main Loop */ while (ip < mflimit) { ml = LZ4HC_InsertAndFindBestMatch (ctx, ip, matchlimit, (&ref), maxNbAttempts); if (!ml) { ip++; continue; } /* saved, in case we would skip too much */ start0 = ip; ref0 = ref; ml0 = ml; _Search2: if (ip+ml < mflimit) ml2 = LZ4HC_InsertAndGetWiderMatch(ctx, ip + ml - 2, ip + 1, matchlimit, ml, &ref2, &start2, maxNbAttempts); else ml2 = ml; if (ml2 == ml) /* No better match */ { if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0; continue; } if (start0 < ip) { if (start2 < ip + ml0) /* empirical */ { ip = start0; ref = ref0; ml = ml0; } } /* Here, start0==ip */ if ((start2 - ip) < 3) /* First Match too small : removed */ { ml = ml2; ip = start2; ref =ref2; goto _Search2; } _Search3: /* * Currently we have : * ml2 > ml1, and * ip1+3 <= ip2 (usually < ip1+ml1) */ if ((start2 - ip) < OPTIMAL_ML) { int correction; int new_ml = ml; if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML; if (ip+new_ml > start2 + ml2 - MINMATCH) new_ml = (int)(start2 - ip) + ml2 - MINMATCH; correction = new_ml - (int)(start2 - ip); if (correction > 0) { start2 += correction; ref2 += correction; ml2 -= correction; } } /* Now, we have start2 = ip+new_ml, with new_ml = min(ml, OPTIMAL_ML=18) */ if (start2 + ml2 < mflimit) ml3 = LZ4HC_InsertAndGetWiderMatch(ctx, start2 + ml2 - 3, start2, matchlimit, ml2, &ref3, &start3, maxNbAttempts); else ml3 = ml2; if (ml3 == ml2) /* No better match : 2 sequences to encode */ { /* ip & ref are known; Now for ml */ if (start2 < ip+ml) ml = (int)(start2 - ip); /* Now, encode 2 sequences */ if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0; ip = start2; if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml2, ref2, limit, oend)) return 0; continue; } if (start3 < ip+ml+3) /* Not enough space for match 2 : remove it */ { if (start3 >= (ip+ml)) /* can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 */ { if (start2 < ip+ml) { int correction = (int)(ip+ml - start2); start2 += correction; ref2 += correction; ml2 -= correction; if (ml2 < MINMATCH) { start2 = start3; ref2 = ref3; ml2 = ml3; } } if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0; ip = start3; ref = ref3; ml = ml3; start0 = start2; ref0 = ref2; ml0 = ml2; goto _Search2; } start2 = start3; ref2 = ref3; ml2 = ml3; goto _Search3; } /* * OK, now we have 3 ascending matches; let's write at least the first one * ip & ref are known; Now for ml */ if (start2 < ip+ml) { if ((start2 - ip) < (int)ML_MASK) { int correction; if (ml > OPTIMAL_ML) ml = OPTIMAL_ML; if (ip + ml > start2 + ml2 - MINMATCH) ml = (int)(start2 - ip) + ml2 - MINMATCH; correction = ml - (int)(start2 - ip); if (correction > 0) { start2 += correction; ref2 += correction; ml2 -= correction; } } else { ml = (int)(start2 - ip); } } if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0; ip = start2; ref = ref2; ml = ml2; start2 = start3; ref2 = ref3; ml2 = ml3; goto _Search3; } /* Encode Last Literals */ { int lastRun = (int)(iend - anchor); if ((limit) && (((char*)op - dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize)) return 0; /* Check output limit */ if (lastRun>=(int)RUN_MASK) { *op++=(RUN_MASK< 254 ; lastRun-=255) *op++ = 255; *op++ = (BYTE) lastRun; } else *op++ = (BYTE)(lastRun< The memory position where the next input data block must start is provided as the result of the function. Compression can then resume, using LZ4_compressHC_continue() or LZ4_compressHC_limitedOutput_continue(), as usual. When compression is completed, a call to LZ4_freeHC() will release the memory used by the LZ4HC Data Structure. */ int LZ4_sizeofStreamStateHC(); int LZ4_resetStreamStateHC(void* state, const char* inputBuffer); /* These functions achieve the same result as : void* LZ4_createHC (const char* inputBuffer); They are provided here to allow the user program to allocate memory using its own routines. To know how much space must be allocated, use LZ4_sizeofStreamStateHC(); Note also that space must be aligned for pointers (32 or 64 bits). Once space is allocated, you must initialize it using : LZ4_resetStreamStateHC(void* state, const char* inputBuffer); void* state is a pointer to the space allocated. It must be aligned for pointers (32 or 64 bits), and be large enough. The parameter 'const char* inputBuffer' must, obviously, point at the beginning of input buffer. The input buffer must be already allocated, and size at least 192KB. 'inputBuffer' will also be the 'const char* source' of the first block. The same space can be re-used multiple times, just by initializing it each time with LZ4_resetStreamState(). return value of LZ4_resetStreamStateHC() must be 0 is OK. Any other value means there was an error (typically, state is not aligned for pointers (32 or 64 bits)). */ #if defined (__cplusplus) } #endif PyTables-v.3.1.1/c-blosc/internal-complibs/snappy-1.1.1/000077500000000000000000000000001231437614300224215ustar00rootroot00000000000000PyTables-v.3.1.1/c-blosc/internal-complibs/snappy-1.1.1/add-version.patch000066400000000000000000000011461231437614300256570ustar00rootroot00000000000000diff --git a/internal-complibs/snappy-1.1.1/snappy-c.h b/internal-complibs/snappy-1.1.1/snappy-c.h index c6c2a86..eabe3ae 100644 --- a/internal-complibs/snappy-1.1.1/snappy-c.h +++ b/internal-complibs/snappy-1.1.1/snappy-c.h @@ -37,6 +37,14 @@ extern "C" { #endif +// The next is for getting the Snappy version even if used the C API +// Please note that this is only defined in the Blosc sources of Snappy. +#define SNAPPY_MAJOR 1 +#define SNAPPY_MINOR 1 +#define SNAPPY_PATCHLEVEL 1 +#define SNAPPY_VERSION \ + ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL) + #include /* PyTables-v.3.1.1/c-blosc/internal-complibs/snappy-1.1.1/msvc1.patch000066400000000000000000000007021231437614300244720ustar00rootroot00000000000000--- a/internal-complibs/snappy-1.1.1/snappy.h +++ b/internal-complibs/snappy-1.1.1/snappy.h @@ -44,6 +44,14 @@ #include "snappy-stubs-public.h" +// Windows does not define ssize_t by default. This is a workaround. +// Please note that this is only defined in the Blosc sources of Snappy. +#if defined(_WIN32) && !defined(__MINGW32__) +#include +typedef SSIZE_T ssize_t; +#endif + + namespace snappy { class Source; class Sink; PyTables-v.3.1.1/c-blosc/internal-complibs/snappy-1.1.1/msvc2.patch000066400000000000000000000016471231437614300245040ustar00rootroot00000000000000diff --git a/internal-complibs/snappy-1.1.1/snappy-stubs-public.h b/internal-complibs/snappy-1.1.1/snappy-stubs-public.h index ecda439..4cc8965 100644 --- a/internal-complibs/snappy-1.1.1/snappy-stubs-public.h +++ b/internal-complibs/snappy-1.1.1/snappy-stubs-public.h @@ -36,8 +36,21 @@ #ifndef UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ #define UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ -#if 1 +// MSVC 2008 does not include stdint.h. This is a workaround by Mark W. +// Please note that this is only defined in the Blosc sources of Snappy. +#if !defined(_MSC_VER) || _MSC_VER >= 1600 #include +#else +typedef signed char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef __int64 int64_t; +typedef ptrdiff_t intptr_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned __int64 uint64_t; +typedef size_t uintptr_t; #endif #if 1 PyTables-v.3.1.1/c-blosc/internal-complibs/snappy-1.1.1/snappy-c.cc000066400000000000000000000070651231437614300244720ustar00rootroot00000000000000// Copyright 2011 Martin Gieseking . // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "snappy.h" #include "snappy-c.h" extern "C" { snappy_status snappy_compress(const char* input, size_t input_length, char* compressed, size_t *compressed_length) { if (*compressed_length < snappy_max_compressed_length(input_length)) { return SNAPPY_BUFFER_TOO_SMALL; } snappy::RawCompress(input, input_length, compressed, compressed_length); return SNAPPY_OK; } snappy_status snappy_uncompress(const char* compressed, size_t compressed_length, char* uncompressed, size_t* uncompressed_length) { size_t real_uncompressed_length; if (!snappy::GetUncompressedLength(compressed, compressed_length, &real_uncompressed_length)) { return SNAPPY_INVALID_INPUT; } if (*uncompressed_length < real_uncompressed_length) { return SNAPPY_BUFFER_TOO_SMALL; } if (!snappy::RawUncompress(compressed, compressed_length, uncompressed)) { return SNAPPY_INVALID_INPUT; } *uncompressed_length = real_uncompressed_length; return SNAPPY_OK; } size_t snappy_max_compressed_length(size_t source_length) { return snappy::MaxCompressedLength(source_length); } snappy_status snappy_uncompressed_length(const char *compressed, size_t compressed_length, size_t *result) { if (snappy::GetUncompressedLength(compressed, compressed_length, result)) { return SNAPPY_OK; } else { return SNAPPY_INVALID_INPUT; } } snappy_status snappy_validate_compressed_buffer(const char *compressed, size_t compressed_length) { if (snappy::IsValidCompressedBuffer(compressed, compressed_length)) { return SNAPPY_OK; } else { return SNAPPY_INVALID_INPUT; } } } // extern "C" PyTables-v.3.1.1/c-blosc/internal-complibs/snappy-1.1.1/snappy-c.h000066400000000000000000000131111231437614300243210ustar00rootroot00000000000000/* * Copyright 2011 Martin Gieseking . * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Plain C interface (a wrapper around the C++ implementation). */ #ifndef UTIL_SNAPPY_OPENSOURCE_SNAPPY_C_H_ #define UTIL_SNAPPY_OPENSOURCE_SNAPPY_C_H_ #ifdef __cplusplus extern "C" { #endif // The next is for getting the Snappy version even if used the C API. // Please note that this is only defined in the Blosc sources of Snappy. #define SNAPPY_MAJOR 1 #define SNAPPY_MINOR 1 #define SNAPPY_PATCHLEVEL 1 #define SNAPPY_VERSION \ ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL) #include /* * Return values; see the documentation for each function to know * what each can return. */ typedef enum { SNAPPY_OK = 0, SNAPPY_INVALID_INPUT = 1, SNAPPY_BUFFER_TOO_SMALL = 2 } snappy_status; /* * Takes the data stored in "input[0..input_length-1]" and stores * it in the array pointed to by "compressed". * * signals the space available in "compressed". * If it is not at least equal to "snappy_max_compressed_length(input_length)", * SNAPPY_BUFFER_TOO_SMALL is returned. After successful compression, * contains the true length of the compressed output, * and SNAPPY_OK is returned. * * Example: * size_t output_length = snappy_max_compressed_length(input_length); * char* output = (char*)malloc(output_length); * if (snappy_compress(input, input_length, output, &output_length) * == SNAPPY_OK) { * ... Process(output, output_length) ... * } * free(output); */ snappy_status snappy_compress(const char* input, size_t input_length, char* compressed, size_t* compressed_length); /* * Given data in "compressed[0..compressed_length-1]" generated by * calling the snappy_compress routine, this routine stores * the uncompressed data to * uncompressed[0..uncompressed_length-1]. * Returns failure (a value not equal to SNAPPY_OK) if the message * is corrupted and could not be decrypted. * * signals the space available in "uncompressed". * If it is not at least equal to the value returned by * snappy_uncompressed_length for this stream, SNAPPY_BUFFER_TOO_SMALL * is returned. After successful decompression, * contains the true length of the decompressed output. * * Example: * size_t output_length; * if (snappy_uncompressed_length(input, input_length, &output_length) * != SNAPPY_OK) { * ... fail ... * } * char* output = (char*)malloc(output_length); * if (snappy_uncompress(input, input_length, output, &output_length) * == SNAPPY_OK) { * ... Process(output, output_length) ... * } * free(output); */ snappy_status snappy_uncompress(const char* compressed, size_t compressed_length, char* uncompressed, size_t* uncompressed_length); /* * Returns the maximal size of the compressed representation of * input data that is "source_length" bytes in length. */ size_t snappy_max_compressed_length(size_t source_length); /* * REQUIRES: "compressed[]" was produced by snappy_compress() * Returns SNAPPY_OK and stores the length of the uncompressed data in * *result normally. Returns SNAPPY_INVALID_INPUT on parsing error. * This operation takes O(1) time. */ snappy_status snappy_uncompressed_length(const char* compressed, size_t compressed_length, size_t* result); /* * Check if the contents of "compressed[]" can be uncompressed successfully. * Does not return the uncompressed data; if so, returns SNAPPY_OK, * or if not, returns SNAPPY_INVALID_INPUT. * Takes time proportional to compressed_length, but is usually at least a * factor of four faster than actual decompression. */ snappy_status snappy_validate_compressed_buffer(const char* compressed, size_t compressed_length); #ifdef __cplusplus } // extern "C" #endif #endif /* UTIL_SNAPPY_OPENSOURCE_SNAPPY_C_H_ */ PyTables-v.3.1.1/c-blosc/internal-complibs/snappy-1.1.1/snappy-internal.h000066400000000000000000000125241231437614300257220ustar00rootroot00000000000000// Copyright 2008 Google Inc. All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Internals shared between the Snappy implementation and its unittest. #ifndef UTIL_SNAPPY_SNAPPY_INTERNAL_H_ #define UTIL_SNAPPY_SNAPPY_INTERNAL_H_ #include "snappy-stubs-internal.h" namespace snappy { namespace internal { class WorkingMemory { public: WorkingMemory() : large_table_(NULL) { } ~WorkingMemory() { delete[] large_table_; } // Allocates and clears a hash table using memory in "*this", // stores the number of buckets in "*table_size" and returns a pointer to // the base of the hash table. uint16* GetHashTable(size_t input_size, int* table_size); private: uint16 small_table_[1<<10]; // 2KB uint16* large_table_; // Allocated only when needed DISALLOW_COPY_AND_ASSIGN(WorkingMemory); }; // Flat array compression that does not emit the "uncompressed length" // prefix. Compresses "input" string to the "*op" buffer. // // REQUIRES: "input_length <= kBlockSize" // REQUIRES: "op" points to an array of memory that is at least // "MaxCompressedLength(input_length)" in size. // REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. // REQUIRES: "table_size" is a power of two // // Returns an "end" pointer into "op" buffer. // "end - op" is the compressed size of "input". char* CompressFragment(const char* input, size_t input_length, char* op, uint16* table, const int table_size); // Return the largest n such that // // s1[0,n-1] == s2[0,n-1] // and n <= (s2_limit - s2). // // Does not read *s2_limit or beyond. // Does not read *(s1 + (s2_limit - s2)) or beyond. // Requires that s2_limit >= s2. // // Separate implementation for x86_64, for speed. Uses the fact that // x86_64 is little endian. #if defined(ARCH_K8) static inline int FindMatchLength(const char* s1, const char* s2, const char* s2_limit) { assert(s2_limit >= s2); int matched = 0; // Find out how long the match is. We loop over the data 64 bits at a // time until we find a 64-bit block that doesn't match; then we find // the first non-matching bit and use that to calculate the total // length of the match. while (PREDICT_TRUE(s2 <= s2_limit - 8)) { if (PREDICT_FALSE(UNALIGNED_LOAD64(s2) == UNALIGNED_LOAD64(s1 + matched))) { s2 += 8; matched += 8; } else { // On current (mid-2008) Opteron models there is a 3% more // efficient code sequence to find the first non-matching byte. // However, what follows is ~10% better on Intel Core 2 and newer, // and we expect AMD's bsf instruction to improve. uint64 x = UNALIGNED_LOAD64(s2) ^ UNALIGNED_LOAD64(s1 + matched); int matching_bits = Bits::FindLSBSetNonZero64(x); matched += matching_bits >> 3; return matched; } } while (PREDICT_TRUE(s2 < s2_limit)) { if (PREDICT_TRUE(s1[matched] == *s2)) { ++s2; ++matched; } else { return matched; } } return matched; } #else static inline int FindMatchLength(const char* s1, const char* s2, const char* s2_limit) { // Implementation based on the x86-64 version, above. assert(s2_limit >= s2); int matched = 0; while (s2 <= s2_limit - 4 && UNALIGNED_LOAD32(s2) == UNALIGNED_LOAD32(s1 + matched)) { s2 += 4; matched += 4; } if (LittleEndian::IsLittleEndian() && s2 <= s2_limit - 4) { uint32 x = UNALIGNED_LOAD32(s2) ^ UNALIGNED_LOAD32(s1 + matched); int matching_bits = Bits::FindLSBSetNonZero(x); matched += matching_bits >> 3; } else { while ((s2 < s2_limit) && (s1[matched] == *s2)) { ++s2; ++matched; } } return matched; } #endif } // end namespace internal } // end namespace snappy #endif // UTIL_SNAPPY_SNAPPY_INTERNAL_H_ PyTables-v.3.1.1/c-blosc/internal-complibs/snappy-1.1.1/snappy-sinksource.cc000066400000000000000000000044771231437614300264410ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include "snappy-sinksource.h" namespace snappy { Source::~Source() { } Sink::~Sink() { } char* Sink::GetAppendBuffer(size_t length, char* scratch) { return scratch; } ByteArraySource::~ByteArraySource() { } size_t ByteArraySource::Available() const { return left_; } const char* ByteArraySource::Peek(size_t* len) { *len = left_; return ptr_; } void ByteArraySource::Skip(size_t n) { left_ -= n; ptr_ += n; } UncheckedByteArraySink::~UncheckedByteArraySink() { } void UncheckedByteArraySink::Append(const char* data, size_t n) { // Do no copying if the caller filled in the result of GetAppendBuffer() if (data != dest_) { memcpy(dest_, data, n); } dest_ += n; } char* UncheckedByteArraySink::GetAppendBuffer(size_t len, char* scratch) { return dest_; } } PyTables-v.3.1.1/c-blosc/internal-complibs/snappy-1.1.1/snappy-sinksource.h000066400000000000000000000115031231437614300262670ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef UTIL_SNAPPY_SNAPPY_SINKSOURCE_H_ #define UTIL_SNAPPY_SNAPPY_SINKSOURCE_H_ #include namespace snappy { // A Sink is an interface that consumes a sequence of bytes. class Sink { public: Sink() { } virtual ~Sink(); // Append "bytes[0,n-1]" to this. virtual void Append(const char* bytes, size_t n) = 0; // Returns a writable buffer of the specified length for appending. // May return a pointer to the caller-owned scratch buffer which // must have at least the indicated length. The returned buffer is // only valid until the next operation on this Sink. // // After writing at most "length" bytes, call Append() with the // pointer returned from this function and the number of bytes // written. Many Append() implementations will avoid copying // bytes if this function returned an internal buffer. // // If a non-scratch buffer is returned, the caller may only pass a // prefix of it to Append(). That is, it is not correct to pass an // interior pointer of the returned array to Append(). // // The default implementation always returns the scratch buffer. virtual char* GetAppendBuffer(size_t length, char* scratch); private: // No copying Sink(const Sink&); void operator=(const Sink&); }; // A Source is an interface that yields a sequence of bytes class Source { public: Source() { } virtual ~Source(); // Return the number of bytes left to read from the source virtual size_t Available() const = 0; // Peek at the next flat region of the source. Does not reposition // the source. The returned region is empty iff Available()==0. // // Returns a pointer to the beginning of the region and store its // length in *len. // // The returned region is valid until the next call to Skip() or // until this object is destroyed, whichever occurs first. // // The returned region may be larger than Available() (for example // if this ByteSource is a view on a substring of a larger source). // The caller is responsible for ensuring that it only reads the // Available() bytes. virtual const char* Peek(size_t* len) = 0; // Skip the next n bytes. Invalidates any buffer returned by // a previous call to Peek(). // REQUIRES: Available() >= n virtual void Skip(size_t n) = 0; private: // No copying Source(const Source&); void operator=(const Source&); }; // A Source implementation that yields the contents of a flat array class ByteArraySource : public Source { public: ByteArraySource(const char* p, size_t n) : ptr_(p), left_(n) { } virtual ~ByteArraySource(); virtual size_t Available() const; virtual const char* Peek(size_t* len); virtual void Skip(size_t n); private: const char* ptr_; size_t left_; }; // A Sink implementation that writes to a flat array without any bound checks. class UncheckedByteArraySink : public Sink { public: explicit UncheckedByteArraySink(char* dest) : dest_(dest) { } virtual ~UncheckedByteArraySink(); virtual void Append(const char* data, size_t n); virtual char* GetAppendBuffer(size_t len, char* scratch); // Return the current output pointer so that a caller can see how // many bytes were produced. // Note: this is not a Sink method. char* CurrentDestination() const { return dest_; } private: char* dest_; }; } #endif // UTIL_SNAPPY_SNAPPY_SINKSOURCE_H_ PyTables-v.3.1.1/c-blosc/internal-complibs/snappy-1.1.1/snappy-stubs-internal.cc000066400000000000000000000034431231437614300272160ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include "snappy-stubs-internal.h" namespace snappy { void Varint::Append32(string* s, uint32 value) { char buf[Varint::kMax32]; const char* p = Varint::Encode32(buf, value); s->append(buf, p - buf); } } // namespace snappy PyTables-v.3.1.1/c-blosc/internal-complibs/snappy-1.1.1/snappy-stubs-internal.h000066400000000000000000000357231231437614300270660ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Various stubs for the open-source version of Snappy. #ifndef UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ #define UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef HAVE_SYS_MMAN_H #include #endif #include "snappy-stubs-public.h" #if defined(__x86_64__) // Enable 64-bit optimized versions of some routines. #define ARCH_K8 1 #endif // Needed by OS X, among others. #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif // Pull in std::min, std::ostream, and the likes. This is safe because this // header file is never used from any public header files. using namespace std; // The size of an array, if known at compile-time. // Will give unexpected results if used on a pointer. // We undefine it first, since some compilers already have a definition. #ifdef ARRAYSIZE #undef ARRAYSIZE #endif #define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a))) // Static prediction hints. #ifdef HAVE_BUILTIN_EXPECT #define PREDICT_FALSE(x) (__builtin_expect(x, 0)) #define PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) #else #define PREDICT_FALSE(x) x #define PREDICT_TRUE(x) x #endif // This is only used for recomputing the tag byte table used during // decompression; for simplicity we just remove it from the open-source // version (anyone who wants to regenerate it can just do the call // themselves within main()). #define DEFINE_bool(flag_name, default_value, description) \ bool FLAGS_ ## flag_name = default_value #define DECLARE_bool(flag_name) \ extern bool FLAGS_ ## flag_name namespace snappy { static const uint32 kuint32max = static_cast(0xFFFFFFFF); static const int64 kint64max = static_cast(0x7FFFFFFFFFFFFFFFLL); // Potentially unaligned loads and stores. // x86 and PowerPC can simply do these loads and stores native. #if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) #define UNALIGNED_LOAD16(_p) (*reinterpret_cast(_p)) #define UNALIGNED_LOAD32(_p) (*reinterpret_cast(_p)) #define UNALIGNED_LOAD64(_p) (*reinterpret_cast(_p)) #define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast(_p) = (_val)) #define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast(_p) = (_val)) #define UNALIGNED_STORE64(_p, _val) (*reinterpret_cast(_p) = (_val)) // ARMv7 and newer support native unaligned accesses, but only of 16-bit // and 32-bit values (not 64-bit); older versions either raise a fatal signal, // do an unaligned read and rotate the words around a bit, or do the reads very // slowly (trip through kernel mode). There's no simple #define that says just // “ARMv7 or higherâ€, so we have to filter away all ARMv5 and ARMv6 // sub-architectures. // // This is a mess, but there's not much we can do about it. #elif defined(__arm__) && \ !defined(__ARM_ARCH_4__) && \ !defined(__ARM_ARCH_4T__) && \ !defined(__ARM_ARCH_5__) && \ !defined(__ARM_ARCH_5T__) && \ !defined(__ARM_ARCH_5TE__) && \ !defined(__ARM_ARCH_5TEJ__) && \ !defined(__ARM_ARCH_6__) && \ !defined(__ARM_ARCH_6J__) && \ !defined(__ARM_ARCH_6K__) && \ !defined(__ARM_ARCH_6Z__) && \ !defined(__ARM_ARCH_6ZK__) && \ !defined(__ARM_ARCH_6T2__) #define UNALIGNED_LOAD16(_p) (*reinterpret_cast(_p)) #define UNALIGNED_LOAD32(_p) (*reinterpret_cast(_p)) #define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast(_p) = (_val)) #define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast(_p) = (_val)) // TODO(user): NEON supports unaligned 64-bit loads and stores. // See if that would be more efficient on platforms supporting it, // at least for copies. inline uint64 UNALIGNED_LOAD64(const void *p) { uint64 t; memcpy(&t, p, sizeof t); return t; } inline void UNALIGNED_STORE64(void *p, uint64 v) { memcpy(p, &v, sizeof v); } #else // These functions are provided for architectures that don't support // unaligned loads and stores. inline uint16 UNALIGNED_LOAD16(const void *p) { uint16 t; memcpy(&t, p, sizeof t); return t; } inline uint32 UNALIGNED_LOAD32(const void *p) { uint32 t; memcpy(&t, p, sizeof t); return t; } inline uint64 UNALIGNED_LOAD64(const void *p) { uint64 t; memcpy(&t, p, sizeof t); return t; } inline void UNALIGNED_STORE16(void *p, uint16 v) { memcpy(p, &v, sizeof v); } inline void UNALIGNED_STORE32(void *p, uint32 v) { memcpy(p, &v, sizeof v); } inline void UNALIGNED_STORE64(void *p, uint64 v) { memcpy(p, &v, sizeof v); } #endif // This can be more efficient than UNALIGNED_LOAD64 + UNALIGNED_STORE64 // on some platforms, in particular ARM. inline void UnalignedCopy64(const void *src, void *dst) { if (sizeof(void *) == 8) { UNALIGNED_STORE64(dst, UNALIGNED_LOAD64(src)); } else { const char *src_char = reinterpret_cast(src); char *dst_char = reinterpret_cast(dst); UNALIGNED_STORE32(dst_char, UNALIGNED_LOAD32(src_char)); UNALIGNED_STORE32(dst_char + 4, UNALIGNED_LOAD32(src_char + 4)); } } // The following guarantees declaration of the byte swap functions. #ifdef WORDS_BIGENDIAN #ifdef HAVE_SYS_BYTEORDER_H #include #endif #ifdef HAVE_SYS_ENDIAN_H #include #endif #ifdef _MSC_VER #include #define bswap_16(x) _byteswap_ushort(x) #define bswap_32(x) _byteswap_ulong(x) #define bswap_64(x) _byteswap_uint64(x) #elif defined(__APPLE__) // Mac OS X / Darwin features #include #define bswap_16(x) OSSwapInt16(x) #define bswap_32(x) OSSwapInt32(x) #define bswap_64(x) OSSwapInt64(x) #elif defined(HAVE_BYTESWAP_H) #include #elif defined(bswap32) // FreeBSD defines bswap{16,32,64} in (already #included). #define bswap_16(x) bswap16(x) #define bswap_32(x) bswap32(x) #define bswap_64(x) bswap64(x) #elif defined(BSWAP_64) // Solaris 10 defines BSWAP_{16,32,64} in (already #included). #define bswap_16(x) BSWAP_16(x) #define bswap_32(x) BSWAP_32(x) #define bswap_64(x) BSWAP_64(x) #else inline uint16 bswap_16(uint16 x) { return (x << 8) | (x >> 8); } inline uint32 bswap_32(uint32 x) { x = ((x & 0xff00ff00UL) >> 8) | ((x & 0x00ff00ffUL) << 8); return (x >> 16) | (x << 16); } inline uint64 bswap_64(uint64 x) { x = ((x & 0xff00ff00ff00ff00ULL) >> 8) | ((x & 0x00ff00ff00ff00ffULL) << 8); x = ((x & 0xffff0000ffff0000ULL) >> 16) | ((x & 0x0000ffff0000ffffULL) << 16); return (x >> 32) | (x << 32); } #endif #endif // WORDS_BIGENDIAN // Convert to little-endian storage, opposite of network format. // Convert x from host to little endian: x = LittleEndian.FromHost(x); // convert x from little endian to host: x = LittleEndian.ToHost(x); // // Store values into unaligned memory converting to little endian order: // LittleEndian.Store16(p, x); // // Load unaligned values stored in little endian converting to host order: // x = LittleEndian.Load16(p); class LittleEndian { public: // Conversion functions. #ifdef WORDS_BIGENDIAN static uint16 FromHost16(uint16 x) { return bswap_16(x); } static uint16 ToHost16(uint16 x) { return bswap_16(x); } static uint32 FromHost32(uint32 x) { return bswap_32(x); } static uint32 ToHost32(uint32 x) { return bswap_32(x); } static bool IsLittleEndian() { return false; } #else // !defined(WORDS_BIGENDIAN) static uint16 FromHost16(uint16 x) { return x; } static uint16 ToHost16(uint16 x) { return x; } static uint32 FromHost32(uint32 x) { return x; } static uint32 ToHost32(uint32 x) { return x; } static bool IsLittleEndian() { return true; } #endif // !defined(WORDS_BIGENDIAN) // Functions to do unaligned loads and stores in little-endian order. static uint16 Load16(const void *p) { return ToHost16(UNALIGNED_LOAD16(p)); } static void Store16(void *p, uint16 v) { UNALIGNED_STORE16(p, FromHost16(v)); } static uint32 Load32(const void *p) { return ToHost32(UNALIGNED_LOAD32(p)); } static void Store32(void *p, uint32 v) { UNALIGNED_STORE32(p, FromHost32(v)); } }; // Some bit-manipulation functions. class Bits { public: // Return floor(log2(n)) for positive integer n. Returns -1 iff n == 0. static int Log2Floor(uint32 n); // Return the first set least / most significant bit, 0-indexed. Returns an // undefined value if n == 0. FindLSBSetNonZero() is similar to ffs() except // that it's 0-indexed. static int FindLSBSetNonZero(uint32 n); static int FindLSBSetNonZero64(uint64 n); private: DISALLOW_COPY_AND_ASSIGN(Bits); }; #ifdef HAVE_BUILTIN_CTZ inline int Bits::Log2Floor(uint32 n) { return n == 0 ? -1 : 31 ^ __builtin_clz(n); } inline int Bits::FindLSBSetNonZero(uint32 n) { return __builtin_ctz(n); } inline int Bits::FindLSBSetNonZero64(uint64 n) { return __builtin_ctzll(n); } #else // Portable versions. inline int Bits::Log2Floor(uint32 n) { if (n == 0) return -1; int log = 0; uint32 value = n; for (int i = 4; i >= 0; --i) { int shift = (1 << i); uint32 x = value >> shift; if (x != 0) { value = x; log += shift; } } assert(value == 1); return log; } inline int Bits::FindLSBSetNonZero(uint32 n) { int rc = 31; for (int i = 4, shift = 1 << 4; i >= 0; --i) { const uint32 x = n << shift; if (x != 0) { n = x; rc -= shift; } shift >>= 1; } return rc; } // FindLSBSetNonZero64() is defined in terms of FindLSBSetNonZero(). inline int Bits::FindLSBSetNonZero64(uint64 n) { const uint32 bottombits = static_cast(n); if (bottombits == 0) { // Bottom bits are zero, so scan in top bits return 32 + FindLSBSetNonZero(static_cast(n >> 32)); } else { return FindLSBSetNonZero(bottombits); } } #endif // End portable versions. // Variable-length integer encoding. class Varint { public: // Maximum lengths of varint encoding of uint32. static const int kMax32 = 5; // Attempts to parse a varint32 from a prefix of the bytes in [ptr,limit-1]. // Never reads a character at or beyond limit. If a valid/terminated varint32 // was found in the range, stores it in *OUTPUT and returns a pointer just // past the last byte of the varint32. Else returns NULL. On success, // "result <= limit". static const char* Parse32WithLimit(const char* ptr, const char* limit, uint32* OUTPUT); // REQUIRES "ptr" points to a buffer of length sufficient to hold "v". // EFFECTS Encodes "v" into "ptr" and returns a pointer to the // byte just past the last encoded byte. static char* Encode32(char* ptr, uint32 v); // EFFECTS Appends the varint representation of "value" to "*s". static void Append32(string* s, uint32 value); }; inline const char* Varint::Parse32WithLimit(const char* p, const char* l, uint32* OUTPUT) { const unsigned char* ptr = reinterpret_cast(p); const unsigned char* limit = reinterpret_cast(l); uint32 b, result; if (ptr >= limit) return NULL; b = *(ptr++); result = b & 127; if (b < 128) goto done; if (ptr >= limit) return NULL; b = *(ptr++); result |= (b & 127) << 7; if (b < 128) goto done; if (ptr >= limit) return NULL; b = *(ptr++); result |= (b & 127) << 14; if (b < 128) goto done; if (ptr >= limit) return NULL; b = *(ptr++); result |= (b & 127) << 21; if (b < 128) goto done; if (ptr >= limit) return NULL; b = *(ptr++); result |= (b & 127) << 28; if (b < 16) goto done; return NULL; // Value is too long to be a varint32 done: *OUTPUT = result; return reinterpret_cast(ptr); } inline char* Varint::Encode32(char* sptr, uint32 v) { // Operate on characters as unsigneds unsigned char* ptr = reinterpret_cast(sptr); static const int B = 128; if (v < (1<<7)) { *(ptr++) = v; } else if (v < (1<<14)) { *(ptr++) = v | B; *(ptr++) = v>>7; } else if (v < (1<<21)) { *(ptr++) = v | B; *(ptr++) = (v>>7) | B; *(ptr++) = v>>14; } else if (v < (1<<28)) { *(ptr++) = v | B; *(ptr++) = (v>>7) | B; *(ptr++) = (v>>14) | B; *(ptr++) = v>>21; } else { *(ptr++) = v | B; *(ptr++) = (v>>7) | B; *(ptr++) = (v>>14) | B; *(ptr++) = (v>>21) | B; *(ptr++) = v>>28; } return reinterpret_cast(ptr); } // If you know the internal layout of the std::string in use, you can // replace this function with one that resizes the string without // filling the new space with zeros (if applicable) -- // it will be non-portable but faster. inline void STLStringResizeUninitialized(string* s, size_t new_size) { s->resize(new_size); } // Return a mutable char* pointing to a string's internal buffer, // which may not be null-terminated. Writing through this pointer will // modify the string. // // string_as_array(&str)[i] is valid for 0 <= i < str.size() until the // next call to a string method that invalidates iterators. // // As of 2006-04, there is no standard-blessed way of getting a // mutable reference to a string's internal buffer. However, issue 530 // (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-defects.html#530) // proposes this as the method. It will officially be part of the standard // for C++0x. This should already work on all current implementations. inline char* string_as_array(string* str) { return str->empty() ? NULL : &*str->begin(); } } // namespace snappy #endif // UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ PyTables-v.3.1.1/c-blosc/internal-complibs/snappy-1.1.1/snappy-stubs-public.h000066400000000000000000000070731231437614300265250ustar00rootroot00000000000000// Copyright 2011 Google Inc. All Rights Reserved. // Author: sesse@google.com (Steinar H. Gunderson) // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Various type stubs for the open-source version of Snappy. // // This file cannot include config.h, as it is included from snappy.h, // which is a public header. Instead, snappy-stubs-public.h is generated by // from snappy-stubs-public.h.in at configure time. #ifndef UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ #define UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ // MSVC 2008 does not include stdint.h. This is a workaround by Mark W. // Please note that this is only defined in the Blosc sources of Snappy. #if !defined(_MSC_VER) || _MSC_VER >= 1600 #include #else typedef signed char int8_t; typedef short int16_t; typedef int int32_t; typedef __int64 int64_t; typedef ptrdiff_t intptr_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef unsigned __int64 uint64_t; typedef size_t uintptr_t; #endif #if 1 #include #endif #if 0 #include #endif #define SNAPPY_MAJOR 1 #define SNAPPY_MINOR 1 #define SNAPPY_PATCHLEVEL 1 #define SNAPPY_VERSION \ ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL) #include namespace snappy { #if 1 typedef int8_t int8; typedef uint8_t uint8; typedef int16_t int16; typedef uint16_t uint16; typedef int32_t int32; typedef uint32_t uint32; typedef int64_t int64; typedef uint64_t uint64; #else typedef signed char int8; typedef unsigned char uint8; typedef short int16; typedef unsigned short uint16; typedef int int32; typedef unsigned int uint32; typedef long long int64; typedef unsigned long long uint64; #endif typedef std::string string; #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&); \ void operator=(const TypeName&) #if !0 // Windows does not have an iovec type, yet the concept is universally useful. // It is simple to define it ourselves, so we put it inside our own namespace. struct iovec { void* iov_base; size_t iov_len; }; #endif } // namespace snappy #endif // UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ PyTables-v.3.1.1/c-blosc/internal-complibs/snappy-1.1.1/snappy.cc000066400000000000000000001321131231437614300242430ustar00rootroot00000000000000// Copyright 2005 Google Inc. All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "snappy.h" #include "snappy-internal.h" #include "snappy-sinksource.h" #include #include #include #include namespace snappy { // Any hash function will produce a valid compressed bitstream, but a good // hash function reduces the number of collisions and thus yields better // compression for compressible input, and more speed for incompressible // input. Of course, it doesn't hurt if the hash function is reasonably fast // either, as it gets called a lot. static inline uint32 HashBytes(uint32 bytes, int shift) { uint32 kMul = 0x1e35a7bd; return (bytes * kMul) >> shift; } static inline uint32 Hash(const char* p, int shift) { return HashBytes(UNALIGNED_LOAD32(p), shift); } size_t MaxCompressedLength(size_t source_len) { // Compressed data can be defined as: // compressed := item* literal* // item := literal* copy // // The trailing literal sequence has a space blowup of at most 62/60 // since a literal of length 60 needs one tag byte + one extra byte // for length information. // // Item blowup is trickier to measure. Suppose the "copy" op copies // 4 bytes of data. Because of a special check in the encoding code, // we produce a 4-byte copy only if the offset is < 65536. Therefore // the copy op takes 3 bytes to encode, and this type of item leads // to at most the 62/60 blowup for representing literals. // // Suppose the "copy" op copies 5 bytes of data. If the offset is big // enough, it will take 5 bytes to encode the copy op. Therefore the // worst case here is a one-byte literal followed by a five-byte copy. // I.e., 6 bytes of input turn into 7 bytes of "compressed" data. // // This last factor dominates the blowup, so the final estimate is: return 32 + source_len + source_len/6; } enum { LITERAL = 0, COPY_1_BYTE_OFFSET = 1, // 3 bit length + 3 bits of offset in opcode COPY_2_BYTE_OFFSET = 2, COPY_4_BYTE_OFFSET = 3 }; static const int kMaximumTagLength = 5; // COPY_4_BYTE_OFFSET plus the actual offset. // Copy "len" bytes from "src" to "op", one byte at a time. Used for // handling COPY operations where the input and output regions may // overlap. For example, suppose: // src == "ab" // op == src + 2 // len == 20 // After IncrementalCopy(src, op, len), the result will have // eleven copies of "ab" // ababababababababababab // Note that this does not match the semantics of either memcpy() // or memmove(). static inline void IncrementalCopy(const char* src, char* op, ssize_t len) { assert(len > 0); do { *op++ = *src++; } while (--len > 0); } // Equivalent to IncrementalCopy except that it can write up to ten extra // bytes after the end of the copy, and that it is faster. // // The main part of this loop is a simple copy of eight bytes at a time until // we've copied (at least) the requested amount of bytes. However, if op and // src are less than eight bytes apart (indicating a repeating pattern of // length < 8), we first need to expand the pattern in order to get the correct // results. For instance, if the buffer looks like this, with the eight-byte // and patterns marked as intervals: // // abxxxxxxxxxxxx // [------] src // [------] op // // a single eight-byte copy from to will repeat the pattern once, // after which we can move two bytes without moving : // // ababxxxxxxxxxx // [------] src // [------] op // // and repeat the exercise until the two no longer overlap. // // This allows us to do very well in the special case of one single byte // repeated many times, without taking a big hit for more general cases. // // The worst case of extra writing past the end of the match occurs when // op - src == 1 and len == 1; the last copy will read from byte positions // [0..7] and write to [4..11], whereas it was only supposed to write to // position 1. Thus, ten excess bytes. namespace { const int kMaxIncrementCopyOverflow = 10; inline void IncrementalCopyFastPath(const char* src, char* op, ssize_t len) { while (op - src < 8) { UnalignedCopy64(src, op); len -= op - src; op += op - src; } while (len > 0) { UnalignedCopy64(src, op); src += 8; op += 8; len -= 8; } } } // namespace static inline char* EmitLiteral(char* op, const char* literal, int len, bool allow_fast_path) { int n = len - 1; // Zero-length literals are disallowed if (n < 60) { // Fits in tag byte *op++ = LITERAL | (n << 2); // The vast majority of copies are below 16 bytes, for which a // call to memcpy is overkill. This fast path can sometimes // copy up to 15 bytes too much, but that is okay in the // main loop, since we have a bit to go on for both sides: // // - The input will always have kInputMarginBytes = 15 extra // available bytes, as long as we're in the main loop, and // if not, allow_fast_path = false. // - The output will always have 32 spare bytes (see // MaxCompressedLength). if (allow_fast_path && len <= 16) { UnalignedCopy64(literal, op); UnalignedCopy64(literal + 8, op + 8); return op + len; } } else { // Encode in upcoming bytes char* base = op; int count = 0; op++; while (n > 0) { *op++ = n & 0xff; n >>= 8; count++; } assert(count >= 1); assert(count <= 4); *base = LITERAL | ((59+count) << 2); } memcpy(op, literal, len); return op + len; } static inline char* EmitCopyLessThan64(char* op, size_t offset, int len) { assert(len <= 64); assert(len >= 4); assert(offset < 65536); if ((len < 12) && (offset < 2048)) { size_t len_minus_4 = len - 4; assert(len_minus_4 < 8); // Must fit in 3 bits *op++ = COPY_1_BYTE_OFFSET + ((len_minus_4) << 2) + ((offset >> 8) << 5); *op++ = offset & 0xff; } else { *op++ = COPY_2_BYTE_OFFSET + ((len-1) << 2); LittleEndian::Store16(op, offset); op += 2; } return op; } static inline char* EmitCopy(char* op, size_t offset, int len) { // Emit 64 byte copies but make sure to keep at least four bytes reserved while (len >= 68) { op = EmitCopyLessThan64(op, offset, 64); len -= 64; } // Emit an extra 60 byte copy if have too much data to fit in one copy if (len > 64) { op = EmitCopyLessThan64(op, offset, 60); len -= 60; } // Emit remainder op = EmitCopyLessThan64(op, offset, len); return op; } bool GetUncompressedLength(const char* start, size_t n, size_t* result) { uint32 v = 0; const char* limit = start + n; if (Varint::Parse32WithLimit(start, limit, &v) != NULL) { *result = v; return true; } else { return false; } } namespace internal { uint16* WorkingMemory::GetHashTable(size_t input_size, int* table_size) { // Use smaller hash table when input.size() is smaller, since we // fill the table, incurring O(hash table size) overhead for // compression, and if the input is short, we won't need that // many hash table entries anyway. assert(kMaxHashTableSize >= 256); size_t htsize = 256; while (htsize < kMaxHashTableSize && htsize < input_size) { htsize <<= 1; } uint16* table; if (htsize <= ARRAYSIZE(small_table_)) { table = small_table_; } else { if (large_table_ == NULL) { large_table_ = new uint16[kMaxHashTableSize]; } table = large_table_; } *table_size = htsize; memset(table, 0, htsize * sizeof(*table)); return table; } } // end namespace internal // For 0 <= offset <= 4, GetUint32AtOffset(GetEightBytesAt(p), offset) will // equal UNALIGNED_LOAD32(p + offset). Motivation: On x86-64 hardware we have // empirically found that overlapping loads such as // UNALIGNED_LOAD32(p) ... UNALIGNED_LOAD32(p+1) ... UNALIGNED_LOAD32(p+2) // are slower than UNALIGNED_LOAD64(p) followed by shifts and casts to uint32. // // We have different versions for 64- and 32-bit; ideally we would avoid the // two functions and just inline the UNALIGNED_LOAD64 call into // GetUint32AtOffset, but GCC (at least not as of 4.6) is seemingly not clever // enough to avoid loading the value multiple times then. For 64-bit, the load // is done when GetEightBytesAt() is called, whereas for 32-bit, the load is // done at GetUint32AtOffset() time. #ifdef ARCH_K8 typedef uint64 EightBytesReference; static inline EightBytesReference GetEightBytesAt(const char* ptr) { return UNALIGNED_LOAD64(ptr); } static inline uint32 GetUint32AtOffset(uint64 v, int offset) { assert(offset >= 0); assert(offset <= 4); return v >> (LittleEndian::IsLittleEndian() ? 8 * offset : 32 - 8 * offset); } #else typedef const char* EightBytesReference; static inline EightBytesReference GetEightBytesAt(const char* ptr) { return ptr; } static inline uint32 GetUint32AtOffset(const char* v, int offset) { assert(offset >= 0); assert(offset <= 4); return UNALIGNED_LOAD32(v + offset); } #endif // Flat array compression that does not emit the "uncompressed length" // prefix. Compresses "input" string to the "*op" buffer. // // REQUIRES: "input" is at most "kBlockSize" bytes long. // REQUIRES: "op" points to an array of memory that is at least // "MaxCompressedLength(input.size())" in size. // REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. // REQUIRES: "table_size" is a power of two // // Returns an "end" pointer into "op" buffer. // "end - op" is the compressed size of "input". namespace internal { char* CompressFragment(const char* input, size_t input_size, char* op, uint16* table, const int table_size) { // "ip" is the input pointer, and "op" is the output pointer. const char* ip = input; assert(input_size <= kBlockSize); assert((table_size & (table_size - 1)) == 0); // table must be power of two const int shift = 32 - Bits::Log2Floor(table_size); assert(static_cast(kuint32max >> shift) == table_size - 1); const char* ip_end = input + input_size; const char* base_ip = ip; // Bytes in [next_emit, ip) will be emitted as literal bytes. Or // [next_emit, ip_end) after the main loop. const char* next_emit = ip; const size_t kInputMarginBytes = 15; if (PREDICT_TRUE(input_size >= kInputMarginBytes)) { const char* ip_limit = input + input_size - kInputMarginBytes; for (uint32 next_hash = Hash(++ip, shift); ; ) { assert(next_emit < ip); // The body of this loop calls EmitLiteral once and then EmitCopy one or // more times. (The exception is that when we're close to exhausting // the input we goto emit_remainder.) // // In the first iteration of this loop we're just starting, so // there's nothing to copy, so calling EmitLiteral once is // necessary. And we only start a new iteration when the // current iteration has determined that a call to EmitLiteral will // precede the next call to EmitCopy (if any). // // Step 1: Scan forward in the input looking for a 4-byte-long match. // If we get close to exhausting the input then goto emit_remainder. // // Heuristic match skipping: If 32 bytes are scanned with no matches // found, start looking only at every other byte. If 32 more bytes are // scanned, look at every third byte, etc.. When a match is found, // immediately go back to looking at every byte. This is a small loss // (~5% performance, ~0.1% density) for compressible data due to more // bookkeeping, but for non-compressible data (such as JPEG) it's a huge // win since the compressor quickly "realizes" the data is incompressible // and doesn't bother looking for matches everywhere. // // The "skip" variable keeps track of how many bytes there are since the // last match; dividing it by 32 (ie. right-shifting by five) gives the // number of bytes to move ahead for each iteration. uint32 skip = 32; const char* next_ip = ip; const char* candidate; do { ip = next_ip; uint32 hash = next_hash; assert(hash == Hash(ip, shift)); uint32 bytes_between_hash_lookups = skip++ >> 5; next_ip = ip + bytes_between_hash_lookups; if (PREDICT_FALSE(next_ip > ip_limit)) { goto emit_remainder; } next_hash = Hash(next_ip, shift); candidate = base_ip + table[hash]; assert(candidate >= base_ip); assert(candidate < ip); table[hash] = ip - base_ip; } while (PREDICT_TRUE(UNALIGNED_LOAD32(ip) != UNALIGNED_LOAD32(candidate))); // Step 2: A 4-byte match has been found. We'll later see if more // than 4 bytes match. But, prior to the match, input // bytes [next_emit, ip) are unmatched. Emit them as "literal bytes." assert(next_emit + 16 <= ip_end); op = EmitLiteral(op, next_emit, ip - next_emit, true); // Step 3: Call EmitCopy, and then see if another EmitCopy could // be our next move. Repeat until we find no match for the // input immediately after what was consumed by the last EmitCopy call. // // If we exit this loop normally then we need to call EmitLiteral next, // though we don't yet know how big the literal will be. We handle that // by proceeding to the next iteration of the main loop. We also can exit // this loop via goto if we get close to exhausting the input. EightBytesReference input_bytes; uint32 candidate_bytes = 0; do { // We have a 4-byte match at ip, and no need to emit any // "literal bytes" prior to ip. const char* base = ip; int matched = 4 + FindMatchLength(candidate + 4, ip + 4, ip_end); ip += matched; size_t offset = base - candidate; assert(0 == memcmp(base, candidate, matched)); op = EmitCopy(op, offset, matched); // We could immediately start working at ip now, but to improve // compression we first update table[Hash(ip - 1, ...)]. const char* insert_tail = ip - 1; next_emit = ip; if (PREDICT_FALSE(ip >= ip_limit)) { goto emit_remainder; } input_bytes = GetEightBytesAt(insert_tail); uint32 prev_hash = HashBytes(GetUint32AtOffset(input_bytes, 0), shift); table[prev_hash] = ip - base_ip - 1; uint32 cur_hash = HashBytes(GetUint32AtOffset(input_bytes, 1), shift); candidate = base_ip + table[cur_hash]; candidate_bytes = UNALIGNED_LOAD32(candidate); table[cur_hash] = ip - base_ip; } while (GetUint32AtOffset(input_bytes, 1) == candidate_bytes); next_hash = HashBytes(GetUint32AtOffset(input_bytes, 2), shift); ++ip; } } emit_remainder: // Emit the remaining bytes as a literal if (next_emit < ip_end) { op = EmitLiteral(op, next_emit, ip_end - next_emit, false); } return op; } } // end namespace internal // Signature of output types needed by decompression code. // The decompression code is templatized on a type that obeys this // signature so that we do not pay virtual function call overhead in // the middle of a tight decompression loop. // // class DecompressionWriter { // public: // // Called before decompression // void SetExpectedLength(size_t length); // // // Called after decompression // bool CheckLength() const; // // // Called repeatedly during decompression // bool Append(const char* ip, size_t length); // bool AppendFromSelf(uint32 offset, size_t length); // // // The rules for how TryFastAppend differs from Append are somewhat // // convoluted: // // // // - TryFastAppend is allowed to decline (return false) at any // // time, for any reason -- just "return false" would be // // a perfectly legal implementation of TryFastAppend. // // The intention is for TryFastAppend to allow a fast path // // in the common case of a small append. // // - TryFastAppend is allowed to read up to bytes // // from the input buffer, whereas Append is allowed to read // // . However, if it returns true, it must leave // // at least five (kMaximumTagLength) bytes in the input buffer // // afterwards, so that there is always enough space to read the // // next tag without checking for a refill. // // - TryFastAppend must always return decline (return false) // // if is 61 or more, as in this case the literal length is not // // decoded fully. In practice, this should not be a big problem, // // as it is unlikely that one would implement a fast path accepting // // this much data. // // // bool TryFastAppend(const char* ip, size_t available, size_t length); // }; // ----------------------------------------------------------------------- // Lookup table for decompression code. Generated by ComputeTable() below. // ----------------------------------------------------------------------- // Mapping from i in range [0,4] to a mask to extract the bottom 8*i bits static const uint32 wordmask[] = { 0u, 0xffu, 0xffffu, 0xffffffu, 0xffffffffu }; // Data stored per entry in lookup table: // Range Bits-used Description // ------------------------------------ // 1..64 0..7 Literal/copy length encoded in opcode byte // 0..7 8..10 Copy offset encoded in opcode byte / 256 // 0..4 11..13 Extra bytes after opcode // // We use eight bits for the length even though 7 would have sufficed // because of efficiency reasons: // (1) Extracting a byte is faster than a bit-field // (2) It properly aligns copy offset so we do not need a <<8 static const uint16 char_table[256] = { 0x0001, 0x0804, 0x1001, 0x2001, 0x0002, 0x0805, 0x1002, 0x2002, 0x0003, 0x0806, 0x1003, 0x2003, 0x0004, 0x0807, 0x1004, 0x2004, 0x0005, 0x0808, 0x1005, 0x2005, 0x0006, 0x0809, 0x1006, 0x2006, 0x0007, 0x080a, 0x1007, 0x2007, 0x0008, 0x080b, 0x1008, 0x2008, 0x0009, 0x0904, 0x1009, 0x2009, 0x000a, 0x0905, 0x100a, 0x200a, 0x000b, 0x0906, 0x100b, 0x200b, 0x000c, 0x0907, 0x100c, 0x200c, 0x000d, 0x0908, 0x100d, 0x200d, 0x000e, 0x0909, 0x100e, 0x200e, 0x000f, 0x090a, 0x100f, 0x200f, 0x0010, 0x090b, 0x1010, 0x2010, 0x0011, 0x0a04, 0x1011, 0x2011, 0x0012, 0x0a05, 0x1012, 0x2012, 0x0013, 0x0a06, 0x1013, 0x2013, 0x0014, 0x0a07, 0x1014, 0x2014, 0x0015, 0x0a08, 0x1015, 0x2015, 0x0016, 0x0a09, 0x1016, 0x2016, 0x0017, 0x0a0a, 0x1017, 0x2017, 0x0018, 0x0a0b, 0x1018, 0x2018, 0x0019, 0x0b04, 0x1019, 0x2019, 0x001a, 0x0b05, 0x101a, 0x201a, 0x001b, 0x0b06, 0x101b, 0x201b, 0x001c, 0x0b07, 0x101c, 0x201c, 0x001d, 0x0b08, 0x101d, 0x201d, 0x001e, 0x0b09, 0x101e, 0x201e, 0x001f, 0x0b0a, 0x101f, 0x201f, 0x0020, 0x0b0b, 0x1020, 0x2020, 0x0021, 0x0c04, 0x1021, 0x2021, 0x0022, 0x0c05, 0x1022, 0x2022, 0x0023, 0x0c06, 0x1023, 0x2023, 0x0024, 0x0c07, 0x1024, 0x2024, 0x0025, 0x0c08, 0x1025, 0x2025, 0x0026, 0x0c09, 0x1026, 0x2026, 0x0027, 0x0c0a, 0x1027, 0x2027, 0x0028, 0x0c0b, 0x1028, 0x2028, 0x0029, 0x0d04, 0x1029, 0x2029, 0x002a, 0x0d05, 0x102a, 0x202a, 0x002b, 0x0d06, 0x102b, 0x202b, 0x002c, 0x0d07, 0x102c, 0x202c, 0x002d, 0x0d08, 0x102d, 0x202d, 0x002e, 0x0d09, 0x102e, 0x202e, 0x002f, 0x0d0a, 0x102f, 0x202f, 0x0030, 0x0d0b, 0x1030, 0x2030, 0x0031, 0x0e04, 0x1031, 0x2031, 0x0032, 0x0e05, 0x1032, 0x2032, 0x0033, 0x0e06, 0x1033, 0x2033, 0x0034, 0x0e07, 0x1034, 0x2034, 0x0035, 0x0e08, 0x1035, 0x2035, 0x0036, 0x0e09, 0x1036, 0x2036, 0x0037, 0x0e0a, 0x1037, 0x2037, 0x0038, 0x0e0b, 0x1038, 0x2038, 0x0039, 0x0f04, 0x1039, 0x2039, 0x003a, 0x0f05, 0x103a, 0x203a, 0x003b, 0x0f06, 0x103b, 0x203b, 0x003c, 0x0f07, 0x103c, 0x203c, 0x0801, 0x0f08, 0x103d, 0x203d, 0x1001, 0x0f09, 0x103e, 0x203e, 0x1801, 0x0f0a, 0x103f, 0x203f, 0x2001, 0x0f0b, 0x1040, 0x2040 }; // In debug mode, allow optional computation of the table at startup. // Also, check that the decompression table is correct. #ifndef NDEBUG DEFINE_bool(snappy_dump_decompression_table, false, "If true, we print the decompression table at startup."); static uint16 MakeEntry(unsigned int extra, unsigned int len, unsigned int copy_offset) { // Check that all of the fields fit within the allocated space assert(extra == (extra & 0x7)); // At most 3 bits assert(copy_offset == (copy_offset & 0x7)); // At most 3 bits assert(len == (len & 0x7f)); // At most 7 bits return len | (copy_offset << 8) | (extra << 11); } static void ComputeTable() { uint16 dst[256]; // Place invalid entries in all places to detect missing initialization int assigned = 0; for (int i = 0; i < 256; i++) { dst[i] = 0xffff; } // Small LITERAL entries. We store (len-1) in the top 6 bits. for (unsigned int len = 1; len <= 60; len++) { dst[LITERAL | ((len-1) << 2)] = MakeEntry(0, len, 0); assigned++; } // Large LITERAL entries. We use 60..63 in the high 6 bits to // encode the number of bytes of length info that follow the opcode. for (unsigned int extra_bytes = 1; extra_bytes <= 4; extra_bytes++) { // We set the length field in the lookup table to 1 because extra // bytes encode len-1. dst[LITERAL | ((extra_bytes+59) << 2)] = MakeEntry(extra_bytes, 1, 0); assigned++; } // COPY_1_BYTE_OFFSET. // // The tag byte in the compressed data stores len-4 in 3 bits, and // offset/256 in 5 bits. offset%256 is stored in the next byte. // // This format is used for length in range [4..11] and offset in // range [0..2047] for (unsigned int len = 4; len < 12; len++) { for (unsigned int offset = 0; offset < 2048; offset += 256) { dst[COPY_1_BYTE_OFFSET | ((len-4)<<2) | ((offset>>8)<<5)] = MakeEntry(1, len, offset>>8); assigned++; } } // COPY_2_BYTE_OFFSET. // Tag contains len-1 in top 6 bits, and offset in next two bytes. for (unsigned int len = 1; len <= 64; len++) { dst[COPY_2_BYTE_OFFSET | ((len-1)<<2)] = MakeEntry(2, len, 0); assigned++; } // COPY_4_BYTE_OFFSET. // Tag contents len-1 in top 6 bits, and offset in next four bytes. for (unsigned int len = 1; len <= 64; len++) { dst[COPY_4_BYTE_OFFSET | ((len-1)<<2)] = MakeEntry(4, len, 0); assigned++; } // Check that each entry was initialized exactly once. if (assigned != 256) { fprintf(stderr, "ComputeTable: assigned only %d of 256\n", assigned); abort(); } for (int i = 0; i < 256; i++) { if (dst[i] == 0xffff) { fprintf(stderr, "ComputeTable: did not assign byte %d\n", i); abort(); } } if (FLAGS_snappy_dump_decompression_table) { printf("static const uint16 char_table[256] = {\n "); for (int i = 0; i < 256; i++) { printf("0x%04x%s", dst[i], ((i == 255) ? "\n" : (((i%8) == 7) ? ",\n " : ", "))); } printf("};\n"); } // Check that computed table matched recorded table for (int i = 0; i < 256; i++) { if (dst[i] != char_table[i]) { fprintf(stderr, "ComputeTable: byte %d: computed (%x), expect (%x)\n", i, static_cast(dst[i]), static_cast(char_table[i])); abort(); } } } #endif /* !NDEBUG */ // Helper class for decompression class SnappyDecompressor { private: Source* reader_; // Underlying source of bytes to decompress const char* ip_; // Points to next buffered byte const char* ip_limit_; // Points just past buffered bytes uint32 peeked_; // Bytes peeked from reader (need to skip) bool eof_; // Hit end of input without an error? char scratch_[kMaximumTagLength]; // See RefillTag(). // Ensure that all of the tag metadata for the next tag is available // in [ip_..ip_limit_-1]. Also ensures that [ip,ip+4] is readable even // if (ip_limit_ - ip_ < 5). // // Returns true on success, false on error or end of input. bool RefillTag(); public: explicit SnappyDecompressor(Source* reader) : reader_(reader), ip_(NULL), ip_limit_(NULL), peeked_(0), eof_(false) { } ~SnappyDecompressor() { // Advance past any bytes we peeked at from the reader reader_->Skip(peeked_); } // Returns true iff we have hit the end of the input without an error. bool eof() const { return eof_; } // Read the uncompressed length stored at the start of the compressed data. // On succcess, stores the length in *result and returns true. // On failure, returns false. bool ReadUncompressedLength(uint32* result) { assert(ip_ == NULL); // Must not have read anything yet // Length is encoded in 1..5 bytes *result = 0; uint32 shift = 0; while (true) { if (shift >= 32) return false; size_t n; const char* ip = reader_->Peek(&n); if (n == 0) return false; const unsigned char c = *(reinterpret_cast(ip)); reader_->Skip(1); *result |= static_cast(c & 0x7f) << shift; if (c < 128) { break; } shift += 7; } return true; } // Process the next item found in the input. // Returns true if successful, false on error or end of input. template void DecompressAllTags(Writer* writer) { const char* ip = ip_; // We could have put this refill fragment only at the beginning of the loop. // However, duplicating it at the end of each branch gives the compiler more // scope to optimize the expression based on the local // context, which overall increases speed. #define MAYBE_REFILL() \ if (ip_limit_ - ip < kMaximumTagLength) { \ ip_ = ip; \ if (!RefillTag()) return; \ ip = ip_; \ } MAYBE_REFILL(); for ( ;; ) { const unsigned char c = *(reinterpret_cast(ip++)); if ((c & 0x3) == LITERAL) { size_t literal_length = (c >> 2) + 1u; if (writer->TryFastAppend(ip, ip_limit_ - ip, literal_length)) { assert(literal_length < 61); ip += literal_length; // NOTE(user): There is no MAYBE_REFILL() here, as TryFastAppend() // will not return true unless there's already at least five spare // bytes in addition to the literal. continue; } if (PREDICT_FALSE(literal_length >= 61)) { // Long literal. const size_t literal_length_length = literal_length - 60; literal_length = (LittleEndian::Load32(ip) & wordmask[literal_length_length]) + 1; ip += literal_length_length; } size_t avail = ip_limit_ - ip; while (avail < literal_length) { if (!writer->Append(ip, avail)) return; literal_length -= avail; reader_->Skip(peeked_); size_t n; ip = reader_->Peek(&n); avail = n; peeked_ = avail; if (avail == 0) return; // Premature end of input ip_limit_ = ip + avail; } if (!writer->Append(ip, literal_length)) { return; } ip += literal_length; MAYBE_REFILL(); } else { const uint32 entry = char_table[c]; const uint32 trailer = LittleEndian::Load32(ip) & wordmask[entry >> 11]; const uint32 length = entry & 0xff; ip += entry >> 11; // copy_offset/256 is encoded in bits 8..10. By just fetching // those bits, we get copy_offset (since the bit-field starts at // bit 8). const uint32 copy_offset = entry & 0x700; if (!writer->AppendFromSelf(copy_offset + trailer, length)) { return; } MAYBE_REFILL(); } } #undef MAYBE_REFILL } }; bool SnappyDecompressor::RefillTag() { const char* ip = ip_; if (ip == ip_limit_) { // Fetch a new fragment from the reader reader_->Skip(peeked_); // All peeked bytes are used up size_t n; ip = reader_->Peek(&n); peeked_ = n; if (n == 0) { eof_ = true; return false; } ip_limit_ = ip + n; } // Read the tag character assert(ip < ip_limit_); const unsigned char c = *(reinterpret_cast(ip)); const uint32 entry = char_table[c]; const uint32 needed = (entry >> 11) + 1; // +1 byte for 'c' assert(needed <= sizeof(scratch_)); // Read more bytes from reader if needed uint32 nbuf = ip_limit_ - ip; if (nbuf < needed) { // Stitch together bytes from ip and reader to form the word // contents. We store the needed bytes in "scratch_". They // will be consumed immediately by the caller since we do not // read more than we need. memmove(scratch_, ip, nbuf); reader_->Skip(peeked_); // All peeked bytes are used up peeked_ = 0; while (nbuf < needed) { size_t length; const char* src = reader_->Peek(&length); if (length == 0) return false; uint32 to_add = min(needed - nbuf, length); memcpy(scratch_ + nbuf, src, to_add); nbuf += to_add; reader_->Skip(to_add); } assert(nbuf == needed); ip_ = scratch_; ip_limit_ = scratch_ + needed; } else if (nbuf < kMaximumTagLength) { // Have enough bytes, but move into scratch_ so that we do not // read past end of input memmove(scratch_, ip, nbuf); reader_->Skip(peeked_); // All peeked bytes are used up peeked_ = 0; ip_ = scratch_; ip_limit_ = scratch_ + nbuf; } else { // Pass pointer to buffer returned by reader_. ip_ = ip; } return true; } template static bool InternalUncompress(Source* r, Writer* writer) { // Read the uncompressed length from the front of the compressed input SnappyDecompressor decompressor(r); uint32 uncompressed_len = 0; if (!decompressor.ReadUncompressedLength(&uncompressed_len)) return false; return InternalUncompressAllTags(&decompressor, writer, uncompressed_len); } template static bool InternalUncompressAllTags(SnappyDecompressor* decompressor, Writer* writer, uint32 uncompressed_len) { writer->SetExpectedLength(uncompressed_len); // Process the entire input decompressor->DecompressAllTags(writer); return (decompressor->eof() && writer->CheckLength()); } bool GetUncompressedLength(Source* source, uint32* result) { SnappyDecompressor decompressor(source); return decompressor.ReadUncompressedLength(result); } size_t Compress(Source* reader, Sink* writer) { size_t written = 0; size_t N = reader->Available(); char ulength[Varint::kMax32]; char* p = Varint::Encode32(ulength, N); writer->Append(ulength, p-ulength); written += (p - ulength); internal::WorkingMemory wmem; char* scratch = NULL; char* scratch_output = NULL; while (N > 0) { // Get next block to compress (without copying if possible) size_t fragment_size; const char* fragment = reader->Peek(&fragment_size); assert(fragment_size != 0); // premature end of input const size_t num_to_read = min(N, kBlockSize); size_t bytes_read = fragment_size; size_t pending_advance = 0; if (bytes_read >= num_to_read) { // Buffer returned by reader is large enough pending_advance = num_to_read; fragment_size = num_to_read; } else { // Read into scratch buffer if (scratch == NULL) { // If this is the last iteration, we want to allocate N bytes // of space, otherwise the max possible kBlockSize space. // num_to_read contains exactly the correct value scratch = new char[num_to_read]; } memcpy(scratch, fragment, bytes_read); reader->Skip(bytes_read); while (bytes_read < num_to_read) { fragment = reader->Peek(&fragment_size); size_t n = min(fragment_size, num_to_read - bytes_read); memcpy(scratch + bytes_read, fragment, n); bytes_read += n; reader->Skip(n); } assert(bytes_read == num_to_read); fragment = scratch; fragment_size = num_to_read; } assert(fragment_size == num_to_read); // Get encoding table for compression int table_size; uint16* table = wmem.GetHashTable(num_to_read, &table_size); // Compress input_fragment and append to dest const int max_output = MaxCompressedLength(num_to_read); // Need a scratch buffer for the output, in case the byte sink doesn't // have room for us directly. if (scratch_output == NULL) { scratch_output = new char[max_output]; } else { // Since we encode kBlockSize regions followed by a region // which is <= kBlockSize in length, a previously allocated // scratch_output[] region is big enough for this iteration. } char* dest = writer->GetAppendBuffer(max_output, scratch_output); char* end = internal::CompressFragment(fragment, fragment_size, dest, table, table_size); writer->Append(dest, end - dest); written += (end - dest); N -= num_to_read; reader->Skip(pending_advance); } delete[] scratch; delete[] scratch_output; return written; } // ----------------------------------------------------------------------- // IOVec interfaces // ----------------------------------------------------------------------- // A type that writes to an iovec. // Note that this is not a "ByteSink", but a type that matches the // Writer template argument to SnappyDecompressor::DecompressAllTags(). class SnappyIOVecWriter { private: const struct iovec* output_iov_; const size_t output_iov_count_; // We are currently writing into output_iov_[curr_iov_index_]. int curr_iov_index_; // Bytes written to output_iov_[curr_iov_index_] so far. size_t curr_iov_written_; // Total bytes decompressed into output_iov_ so far. size_t total_written_; // Maximum number of bytes that will be decompressed into output_iov_. size_t output_limit_; inline char* GetIOVecPointer(int index, size_t offset) { return reinterpret_cast(output_iov_[index].iov_base) + offset; } public: // Does not take ownership of iov. iov must be valid during the // entire lifetime of the SnappyIOVecWriter. inline SnappyIOVecWriter(const struct iovec* iov, size_t iov_count) : output_iov_(iov), output_iov_count_(iov_count), curr_iov_index_(0), curr_iov_written_(0), total_written_(0), output_limit_(-1) { } inline void SetExpectedLength(size_t len) { output_limit_ = len; } inline bool CheckLength() const { return total_written_ == output_limit_; } inline bool Append(const char* ip, size_t len) { if (total_written_ + len > output_limit_) { return false; } while (len > 0) { assert(curr_iov_written_ <= output_iov_[curr_iov_index_].iov_len); if (curr_iov_written_ >= output_iov_[curr_iov_index_].iov_len) { // This iovec is full. Go to the next one. if (curr_iov_index_ + 1 >= output_iov_count_) { return false; } curr_iov_written_ = 0; ++curr_iov_index_; } const size_t to_write = std::min( len, output_iov_[curr_iov_index_].iov_len - curr_iov_written_); memcpy(GetIOVecPointer(curr_iov_index_, curr_iov_written_), ip, to_write); curr_iov_written_ += to_write; total_written_ += to_write; ip += to_write; len -= to_write; } return true; } inline bool TryFastAppend(const char* ip, size_t available, size_t len) { const size_t space_left = output_limit_ - total_written_; if (len <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16 && output_iov_[curr_iov_index_].iov_len - curr_iov_written_ >= 16) { // Fast path, used for the majority (about 95%) of invocations. char* ptr = GetIOVecPointer(curr_iov_index_, curr_iov_written_); UnalignedCopy64(ip, ptr); UnalignedCopy64(ip + 8, ptr + 8); curr_iov_written_ += len; total_written_ += len; return true; } return false; } inline bool AppendFromSelf(size_t offset, size_t len) { if (offset > total_written_ || offset == 0) { return false; } const size_t space_left = output_limit_ - total_written_; if (len > space_left) { return false; } // Locate the iovec from which we need to start the copy. int from_iov_index = curr_iov_index_; size_t from_iov_offset = curr_iov_written_; while (offset > 0) { if (from_iov_offset >= offset) { from_iov_offset -= offset; break; } offset -= from_iov_offset; --from_iov_index; assert(from_iov_index >= 0); from_iov_offset = output_iov_[from_iov_index].iov_len; } // Copy bytes starting from the iovec pointed to by from_iov_index to // the current iovec. while (len > 0) { assert(from_iov_index <= curr_iov_index_); if (from_iov_index != curr_iov_index_) { const size_t to_copy = std::min( output_iov_[from_iov_index].iov_len - from_iov_offset, len); Append(GetIOVecPointer(from_iov_index, from_iov_offset), to_copy); len -= to_copy; if (len > 0) { ++from_iov_index; from_iov_offset = 0; } } else { assert(curr_iov_written_ <= output_iov_[curr_iov_index_].iov_len); size_t to_copy = std::min(output_iov_[curr_iov_index_].iov_len - curr_iov_written_, len); if (to_copy == 0) { // This iovec is full. Go to the next one. if (curr_iov_index_ + 1 >= output_iov_count_) { return false; } ++curr_iov_index_; curr_iov_written_ = 0; continue; } if (to_copy > len) { to_copy = len; } IncrementalCopy(GetIOVecPointer(from_iov_index, from_iov_offset), GetIOVecPointer(curr_iov_index_, curr_iov_written_), to_copy); curr_iov_written_ += to_copy; from_iov_offset += to_copy; total_written_ += to_copy; len -= to_copy; } } return true; } }; bool RawUncompressToIOVec(const char* compressed, size_t compressed_length, const struct iovec* iov, size_t iov_cnt) { ByteArraySource reader(compressed, compressed_length); return RawUncompressToIOVec(&reader, iov, iov_cnt); } bool RawUncompressToIOVec(Source* compressed, const struct iovec* iov, size_t iov_cnt) { SnappyIOVecWriter output(iov, iov_cnt); return InternalUncompress(compressed, &output); } // ----------------------------------------------------------------------- // Flat array interfaces // ----------------------------------------------------------------------- // A type that writes to a flat array. // Note that this is not a "ByteSink", but a type that matches the // Writer template argument to SnappyDecompressor::DecompressAllTags(). class SnappyArrayWriter { private: char* base_; char* op_; char* op_limit_; public: inline explicit SnappyArrayWriter(char* dst) : base_(dst), op_(dst) { } inline void SetExpectedLength(size_t len) { op_limit_ = op_ + len; } inline bool CheckLength() const { return op_ == op_limit_; } inline bool Append(const char* ip, size_t len) { char* op = op_; const size_t space_left = op_limit_ - op; if (space_left < len) { return false; } memcpy(op, ip, len); op_ = op + len; return true; } inline bool TryFastAppend(const char* ip, size_t available, size_t len) { char* op = op_; const size_t space_left = op_limit_ - op; if (len <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16) { // Fast path, used for the majority (about 95%) of invocations. UnalignedCopy64(ip, op); UnalignedCopy64(ip + 8, op + 8); op_ = op + len; return true; } else { return false; } } inline bool AppendFromSelf(size_t offset, size_t len) { char* op = op_; const size_t space_left = op_limit_ - op; // Check if we try to append from before the start of the buffer. // Normally this would just be a check for "produced < offset", // but "produced <= offset - 1u" is equivalent for every case // except the one where offset==0, where the right side will wrap around // to a very big number. This is convenient, as offset==0 is another // invalid case that we also want to catch, so that we do not go // into an infinite loop. assert(op >= base_); size_t produced = op - base_; if (produced <= offset - 1u) { return false; } if (len <= 16 && offset >= 8 && space_left >= 16) { // Fast path, used for the majority (70-80%) of dynamic invocations. UnalignedCopy64(op - offset, op); UnalignedCopy64(op - offset + 8, op + 8); } else { if (space_left >= len + kMaxIncrementCopyOverflow) { IncrementalCopyFastPath(op - offset, op, len); } else { if (space_left < len) { return false; } IncrementalCopy(op - offset, op, len); } } op_ = op + len; return true; } }; bool RawUncompress(const char* compressed, size_t n, char* uncompressed) { ByteArraySource reader(compressed, n); return RawUncompress(&reader, uncompressed); } bool RawUncompress(Source* compressed, char* uncompressed) { SnappyArrayWriter output(uncompressed); return InternalUncompress(compressed, &output); } bool Uncompress(const char* compressed, size_t n, string* uncompressed) { size_t ulength; if (!GetUncompressedLength(compressed, n, &ulength)) { return false; } // On 32-bit builds: max_size() < kuint32max. Check for that instead // of crashing (e.g., consider externally specified compressed data). if (ulength > uncompressed->max_size()) { return false; } STLStringResizeUninitialized(uncompressed, ulength); return RawUncompress(compressed, n, string_as_array(uncompressed)); } // A Writer that drops everything on the floor and just does validation class SnappyDecompressionValidator { private: size_t expected_; size_t produced_; public: inline SnappyDecompressionValidator() : produced_(0) { } inline void SetExpectedLength(size_t len) { expected_ = len; } inline bool CheckLength() const { return expected_ == produced_; } inline bool Append(const char* ip, size_t len) { produced_ += len; return produced_ <= expected_; } inline bool TryFastAppend(const char* ip, size_t available, size_t length) { return false; } inline bool AppendFromSelf(size_t offset, size_t len) { // See SnappyArrayWriter::AppendFromSelf for an explanation of // the "offset - 1u" trick. if (produced_ <= offset - 1u) return false; produced_ += len; return produced_ <= expected_; } }; bool IsValidCompressedBuffer(const char* compressed, size_t n) { ByteArraySource reader(compressed, n); SnappyDecompressionValidator writer; return InternalUncompress(&reader, &writer); } void RawCompress(const char* input, size_t input_length, char* compressed, size_t* compressed_length) { ByteArraySource reader(input, input_length); UncheckedByteArraySink writer(compressed); Compress(&reader, &writer); // Compute how many bytes were added *compressed_length = (writer.CurrentDestination() - compressed); } size_t Compress(const char* input, size_t input_length, string* compressed) { // Pre-grow the buffer to the max length of the compressed output compressed->resize(MaxCompressedLength(input_length)); size_t compressed_length; RawCompress(input, input_length, string_as_array(compressed), &compressed_length); compressed->resize(compressed_length); return compressed_length; } } // end namespace snappy PyTables-v.3.1.1/c-blosc/internal-complibs/snappy-1.1.1/snappy.h000066400000000000000000000212641231437614300241110ustar00rootroot00000000000000// Copyright 2005 and onwards Google Inc. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // A light-weight compression algorithm. It is designed for speed of // compression and decompression, rather than for the utmost in space // savings. // // For getting better compression ratios when you are compressing data // with long repeated sequences or compressing data that is similar to // other data, while still compressing fast, you might look at first // using BMDiff and then compressing the output of BMDiff with // Snappy. #ifndef UTIL_SNAPPY_SNAPPY_H__ #define UTIL_SNAPPY_SNAPPY_H__ #include #include #include "snappy-stubs-public.h" // Windows does not define ssize_t by default. This is a workaround. // Please note that this is only defined in the Blosc sources of Snappy. #if defined(_WIN32) && !defined(__MINGW32__) #include typedef SSIZE_T ssize_t; #endif namespace snappy { class Source; class Sink; // ------------------------------------------------------------------------ // Generic compression/decompression routines. // ------------------------------------------------------------------------ // Compress the bytes read from "*source" and append to "*sink". Return the // number of bytes written. size_t Compress(Source* source, Sink* sink); // Find the uncompressed length of the given stream, as given by the header. // Note that the true length could deviate from this; the stream could e.g. // be truncated. // // Also note that this leaves "*source" in a state that is unsuitable for // further operations, such as RawUncompress(). You will need to rewind // or recreate the source yourself before attempting any further calls. bool GetUncompressedLength(Source* source, uint32* result); // ------------------------------------------------------------------------ // Higher-level string based routines (should be sufficient for most users) // ------------------------------------------------------------------------ // Sets "*output" to the compressed version of "input[0,input_length-1]". // Original contents of *output are lost. // // REQUIRES: "input[]" is not an alias of "*output". size_t Compress(const char* input, size_t input_length, string* output); // Decompresses "compressed[0,compressed_length-1]" to "*uncompressed". // Original contents of "*uncompressed" are lost. // // REQUIRES: "compressed[]" is not an alias of "*uncompressed". // // returns false if the message is corrupted and could not be decompressed bool Uncompress(const char* compressed, size_t compressed_length, string* uncompressed); // ------------------------------------------------------------------------ // Lower-level character array based routines. May be useful for // efficiency reasons in certain circumstances. // ------------------------------------------------------------------------ // REQUIRES: "compressed" must point to an area of memory that is at // least "MaxCompressedLength(input_length)" bytes in length. // // Takes the data stored in "input[0..input_length]" and stores // it in the array pointed to by "compressed". // // "*compressed_length" is set to the length of the compressed output. // // Example: // char* output = new char[snappy::MaxCompressedLength(input_length)]; // size_t output_length; // RawCompress(input, input_length, output, &output_length); // ... Process(output, output_length) ... // delete [] output; void RawCompress(const char* input, size_t input_length, char* compressed, size_t* compressed_length); // Given data in "compressed[0..compressed_length-1]" generated by // calling the Snappy::Compress routine, this routine // stores the uncompressed data to // uncompressed[0..GetUncompressedLength(compressed)-1] // returns false if the message is corrupted and could not be decrypted bool RawUncompress(const char* compressed, size_t compressed_length, char* uncompressed); // Given data from the byte source 'compressed' generated by calling // the Snappy::Compress routine, this routine stores the uncompressed // data to // uncompressed[0..GetUncompressedLength(compressed,compressed_length)-1] // returns false if the message is corrupted and could not be decrypted bool RawUncompress(Source* compressed, char* uncompressed); // Given data in "compressed[0..compressed_length-1]" generated by // calling the Snappy::Compress routine, this routine // stores the uncompressed data to the iovec "iov". The number of physical // buffers in "iov" is given by iov_cnt and their cumulative size // must be at least GetUncompressedLength(compressed). The individual buffers // in "iov" must not overlap with each other. // // returns false if the message is corrupted and could not be decrypted bool RawUncompressToIOVec(const char* compressed, size_t compressed_length, const struct iovec* iov, size_t iov_cnt); // Given data from the byte source 'compressed' generated by calling // the Snappy::Compress routine, this routine stores the uncompressed // data to the iovec "iov". The number of physical // buffers in "iov" is given by iov_cnt and their cumulative size // must be at least GetUncompressedLength(compressed). The individual buffers // in "iov" must not overlap with each other. // // returns false if the message is corrupted and could not be decrypted bool RawUncompressToIOVec(Source* compressed, const struct iovec* iov, size_t iov_cnt); // Returns the maximal size of the compressed representation of // input data that is "source_bytes" bytes in length; size_t MaxCompressedLength(size_t source_bytes); // REQUIRES: "compressed[]" was produced by RawCompress() or Compress() // Returns true and stores the length of the uncompressed data in // *result normally. Returns false on parsing error. // This operation takes O(1) time. bool GetUncompressedLength(const char* compressed, size_t compressed_length, size_t* result); // Returns true iff the contents of "compressed[]" can be uncompressed // successfully. Does not return the uncompressed data. Takes // time proportional to compressed_length, but is usually at least // a factor of four faster than actual decompression. bool IsValidCompressedBuffer(const char* compressed, size_t compressed_length); // The size of a compression block. Note that many parts of the compression // code assumes that kBlockSize <= 65536; in particular, the hash table // can only store 16-bit offsets, and EmitCopy() also assumes the offset // is 65535 bytes or less. Note also that if you change this, it will // affect the framing format (see framing_format.txt). // // Note that there might be older data around that is compressed with larger // block sizes, so the decompression code should not rely on the // non-existence of long backreferences. static const int kBlockLog = 16; static const size_t kBlockSize = 1 << kBlockLog; static const int kMaxHashTableBits = 14; static const size_t kMaxHashTableSize = 1 << kMaxHashTableBits; } // end namespace snappy #endif // UTIL_SNAPPY_SNAPPY_H__ PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/000077500000000000000000000000001231437614300220575ustar00rootroot00000000000000PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/adler32.c000066400000000000000000000115501231437614300234610ustar00rootroot00000000000000/* adler32.c -- compute the Adler-32 checksum of a data stream * Copyright (C) 1995-2011 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #include "zutil.h" #define local static local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); #define BASE 65521 /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ #define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); #define DO16(buf) DO8(buf,0); DO8(buf,8); /* use NO_DIVIDE if your processor does not do division in hardware -- try it both ways to see which is faster */ #ifdef NO_DIVIDE /* note that this assumes BASE is 65521, where 65536 % 65521 == 15 (thank you to John Reiser for pointing this out) */ # define CHOP(a) \ do { \ unsigned long tmp = a >> 16; \ a &= 0xffffUL; \ a += (tmp << 4) - tmp; \ } while (0) # define MOD28(a) \ do { \ CHOP(a); \ if (a >= BASE) a -= BASE; \ } while (0) # define MOD(a) \ do { \ CHOP(a); \ MOD28(a); \ } while (0) # define MOD63(a) \ do { /* this assumes a is not negative */ \ z_off64_t tmp = a >> 32; \ a &= 0xffffffffL; \ a += (tmp << 8) - (tmp << 5) + tmp; \ tmp = a >> 16; \ a &= 0xffffL; \ a += (tmp << 4) - tmp; \ tmp = a >> 16; \ a &= 0xffffL; \ a += (tmp << 4) - tmp; \ if (a >= BASE) a -= BASE; \ } while (0) #else # define MOD(a) a %= BASE # define MOD28(a) a %= BASE # define MOD63(a) a %= BASE #endif /* ========================================================================= */ uLong ZEXPORT adler32(adler, buf, len) uLong adler; const Bytef *buf; uInt len; { unsigned long sum2; unsigned n; /* split Adler-32 into component sums */ sum2 = (adler >> 16) & 0xffff; adler &= 0xffff; /* in case user likes doing a byte at a time, keep it fast */ if (len == 1) { adler += buf[0]; if (adler >= BASE) adler -= BASE; sum2 += adler; if (sum2 >= BASE) sum2 -= BASE; return adler | (sum2 << 16); } /* initial Adler-32 value (deferred check for len == 1 speed) */ if (buf == Z_NULL) return 1L; /* in case short lengths are provided, keep it somewhat fast */ if (len < 16) { while (len--) { adler += *buf++; sum2 += adler; } if (adler >= BASE) adler -= BASE; MOD28(sum2); /* only added so many BASE's */ return adler | (sum2 << 16); } /* do length NMAX blocks -- requires just one modulo operation */ while (len >= NMAX) { len -= NMAX; n = NMAX / 16; /* NMAX is divisible by 16 */ do { DO16(buf); /* 16 sums unrolled */ buf += 16; } while (--n); MOD(adler); MOD(sum2); } /* do remaining bytes (less than NMAX, still just one modulo) */ if (len) { /* avoid modulos if none remaining */ while (len >= 16) { len -= 16; DO16(buf); buf += 16; } while (len--) { adler += *buf++; sum2 += adler; } MOD(adler); MOD(sum2); } /* return recombined sums */ return adler | (sum2 << 16); } /* ========================================================================= */ local uLong adler32_combine_(adler1, adler2, len2) uLong adler1; uLong adler2; z_off64_t len2; { unsigned long sum1; unsigned long sum2; unsigned rem; /* for negative len, return invalid adler32 as a clue for debugging */ if (len2 < 0) return 0xffffffffUL; /* the derivation of this formula is left as an exercise for the reader */ MOD63(len2); /* assumes len2 >= 0 */ rem = (unsigned)len2; sum1 = adler1 & 0xffff; sum2 = rem * sum1; MOD(sum2); sum1 += (adler2 & 0xffff) + BASE - 1; sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; if (sum1 >= BASE) sum1 -= BASE; if (sum1 >= BASE) sum1 -= BASE; if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); if (sum2 >= BASE) sum2 -= BASE; return sum1 | (sum2 << 16); } /* ========================================================================= */ uLong ZEXPORT adler32_combine(adler1, adler2, len2) uLong adler1; uLong adler2; z_off_t len2; { return adler32_combine_(adler1, adler2, len2); } uLong ZEXPORT adler32_combine64(adler1, adler2, len2) uLong adler1; uLong adler2; z_off64_t len2; { return adler32_combine_(adler1, adler2, len2); } PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/compress.c000066400000000000000000000047411231437614300240640ustar00rootroot00000000000000/* compress.c -- compress a memory buffer * Copyright (C) 1995-2005 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #define ZLIB_INTERNAL #include "zlib.h" /* =========================================================================== Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least 0.1% larger than sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, Z_STREAM_ERROR if the level parameter is invalid. */ int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) Bytef *dest; uLongf *destLen; const Bytef *source; uLong sourceLen; int level; { z_stream stream; int err; stream.next_in = (z_const Bytef *)source; stream.avail_in = (uInt)sourceLen; #ifdef MAXSEG_64K /* Check for source > 64K on 16-bit machine: */ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; #endif stream.next_out = dest; stream.avail_out = (uInt)*destLen; if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; stream.opaque = (voidpf)0; err = deflateInit(&stream, level); if (err != Z_OK) return err; err = deflate(&stream, Z_FINISH); if (err != Z_STREAM_END) { deflateEnd(&stream); return err == Z_OK ? Z_BUF_ERROR : err; } *destLen = stream.total_out; err = deflateEnd(&stream); return err; } /* =========================================================================== */ int ZEXPORT compress (dest, destLen, source, sourceLen) Bytef *dest; uLongf *destLen; const Bytef *source; uLong sourceLen; { return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); } /* =========================================================================== If the default memLevel or windowBits for deflateInit() is changed, then this function needs to be updated. */ uLong ZEXPORT compressBound (sourceLen) uLong sourceLen; { return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13; } PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/crc32.c000066400000000000000000000315661231437614300231520ustar00rootroot00000000000000/* crc32.c -- compute the CRC-32 of a data stream * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h * * Thanks to Rodney Brown for his contribution of faster * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing * tables for updating the shift register in one step with three exclusive-ors * instead of four steps with four exclusive-ors. This results in about a * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. */ /* @(#) $Id$ */ /* Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore protection on the static variables used to control the first-use generation of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should first call get_crc_table() to initialize the tables before allowing more than one thread to use crc32(). DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h. */ #ifdef MAKECRCH # include # ifndef DYNAMIC_CRC_TABLE # define DYNAMIC_CRC_TABLE # endif /* !DYNAMIC_CRC_TABLE */ #endif /* MAKECRCH */ #include "zutil.h" /* for STDC and FAR definitions */ #define local static /* Definitions for doing the crc four data bytes at a time. */ #if !defined(NOBYFOUR) && defined(Z_U4) # define BYFOUR #endif #ifdef BYFOUR local unsigned long crc32_little OF((unsigned long, const unsigned char FAR *, unsigned)); local unsigned long crc32_big OF((unsigned long, const unsigned char FAR *, unsigned)); # define TBLS 8 #else # define TBLS 1 #endif /* BYFOUR */ /* Local functions for crc concatenation */ local unsigned long gf2_matrix_times OF((unsigned long *mat, unsigned long vec)); local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2)); #ifdef DYNAMIC_CRC_TABLE local volatile int crc_table_empty = 1; local z_crc_t FAR crc_table[TBLS][256]; local void make_crc_table OF((void)); #ifdef MAKECRCH local void write_table OF((FILE *, const z_crc_t FAR *)); #endif /* MAKECRCH */ /* Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. Polynomials over GF(2) are represented in binary, one bit per coefficient, with the lowest powers in the most significant bit. Then adding polynomials is just exclusive-or, and multiplying a polynomial by x is a right shift by one. If we call the above polynomial p, and represent a byte as the polynomial q, also with the lowest power in the most significant bit (so the byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, where a mod b means the remainder after dividing a by b. This calculation is done using the shift-register method of multiplying and taking the remainder. The register is initialized to zero, and for each incoming bit, x^32 is added mod p to the register if the bit is a one (where x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by x (which is shifting right by one and adding x^32 mod p if the bit shifted out is a one). We start with the highest power (least significant bit) of q and repeat for all eight bits of q. The first table is simply the CRC of all possible eight bit values. This is all the information needed to generate CRCs on data a byte at a time for all combinations of CRC register values and incoming bytes. The remaining tables allow for word-at-a-time CRC calculation for both big-endian and little- endian machines, where a word is four bytes. */ local void make_crc_table() { z_crc_t c; int n, k; z_crc_t poly; /* polynomial exclusive-or pattern */ /* terms of polynomial defining this crc (except x^32): */ static volatile int first = 1; /* flag to limit concurrent making */ static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; /* See if another task is already doing this (not thread-safe, but better than nothing -- significantly reduces duration of vulnerability in case the advice about DYNAMIC_CRC_TABLE is ignored) */ if (first) { first = 0; /* make exclusive-or pattern from polynomial (0xedb88320UL) */ poly = 0; for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++) poly |= (z_crc_t)1 << (31 - p[n]); /* generate a crc for every 8-bit value */ for (n = 0; n < 256; n++) { c = (z_crc_t)n; for (k = 0; k < 8; k++) c = c & 1 ? poly ^ (c >> 1) : c >> 1; crc_table[0][n] = c; } #ifdef BYFOUR /* generate crc for each value followed by one, two, and three zeros, and then the byte reversal of those as well as the first table */ for (n = 0; n < 256; n++) { c = crc_table[0][n]; crc_table[4][n] = ZSWAP32(c); for (k = 1; k < 4; k++) { c = crc_table[0][c & 0xff] ^ (c >> 8); crc_table[k][n] = c; crc_table[k + 4][n] = ZSWAP32(c); } } #endif /* BYFOUR */ crc_table_empty = 0; } else { /* not first */ /* wait for the other guy to finish (not efficient, but rare) */ while (crc_table_empty) ; } #ifdef MAKECRCH /* write out CRC tables to crc32.h */ { FILE *out; out = fopen("crc32.h", "w"); if (out == NULL) return; fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); fprintf(out, "local const z_crc_t FAR "); fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); write_table(out, crc_table[0]); # ifdef BYFOUR fprintf(out, "#ifdef BYFOUR\n"); for (k = 1; k < 8; k++) { fprintf(out, " },\n {\n"); write_table(out, crc_table[k]); } fprintf(out, "#endif\n"); # endif /* BYFOUR */ fprintf(out, " }\n};\n"); fclose(out); } #endif /* MAKECRCH */ } #ifdef MAKECRCH local void write_table(out, table) FILE *out; const z_crc_t FAR *table; { int n; for (n = 0; n < 256; n++) fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", (unsigned long)(table[n]), n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); } #endif /* MAKECRCH */ #else /* !DYNAMIC_CRC_TABLE */ /* ======================================================================== * Tables of CRC-32s of all single-byte values, made by make_crc_table(). */ #include "crc32.h" #endif /* DYNAMIC_CRC_TABLE */ /* ========================================================================= * This function can be used by asm versions of crc32() */ const z_crc_t FAR * ZEXPORT get_crc_table() { #ifdef DYNAMIC_CRC_TABLE if (crc_table_empty) make_crc_table(); #endif /* DYNAMIC_CRC_TABLE */ return (const z_crc_t FAR *)crc_table; } /* ========================================================================= */ #define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) #define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 /* ========================================================================= */ unsigned long ZEXPORT crc32(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; uInt len; { if (buf == Z_NULL) return 0UL; #ifdef DYNAMIC_CRC_TABLE if (crc_table_empty) make_crc_table(); #endif /* DYNAMIC_CRC_TABLE */ #ifdef BYFOUR if (sizeof(void *) == sizeof(ptrdiff_t)) { z_crc_t endian; endian = 1; if (*((unsigned char *)(&endian))) return crc32_little(crc, buf, len); else return crc32_big(crc, buf, len); } #endif /* BYFOUR */ crc = crc ^ 0xffffffffUL; while (len >= 8) { DO8; len -= 8; } if (len) do { DO1; } while (--len); return crc ^ 0xffffffffUL; } #ifdef BYFOUR /* ========================================================================= */ #define DOLIT4 c ^= *buf4++; \ c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] #define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 /* ========================================================================= */ local unsigned long crc32_little(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; unsigned len; { register z_crc_t c; register const z_crc_t FAR *buf4; c = (z_crc_t)crc; c = ~c; while (len && ((ptrdiff_t)buf & 3)) { c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); len--; } buf4 = (const z_crc_t FAR *)(const void FAR *)buf; while (len >= 32) { DOLIT32; len -= 32; } while (len >= 4) { DOLIT4; len -= 4; } buf = (const unsigned char FAR *)buf4; if (len) do { c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); } while (--len); c = ~c; return (unsigned long)c; } /* ========================================================================= */ #define DOBIG4 c ^= *++buf4; \ c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] #define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 /* ========================================================================= */ local unsigned long crc32_big(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; unsigned len; { register z_crc_t c; register const z_crc_t FAR *buf4; c = ZSWAP32((z_crc_t)crc); c = ~c; while (len && ((ptrdiff_t)buf & 3)) { c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); len--; } buf4 = (const z_crc_t FAR *)(const void FAR *)buf; buf4--; while (len >= 32) { DOBIG32; len -= 32; } while (len >= 4) { DOBIG4; len -= 4; } buf4++; buf = (const unsigned char FAR *)buf4; if (len) do { c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); } while (--len); c = ~c; return (unsigned long)(ZSWAP32(c)); } #endif /* BYFOUR */ #define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ /* ========================================================================= */ local unsigned long gf2_matrix_times(mat, vec) unsigned long *mat; unsigned long vec; { unsigned long sum; sum = 0; while (vec) { if (vec & 1) sum ^= *mat; vec >>= 1; mat++; } return sum; } /* ========================================================================= */ local void gf2_matrix_square(square, mat) unsigned long *square; unsigned long *mat; { int n; for (n = 0; n < GF2_DIM; n++) square[n] = gf2_matrix_times(mat, mat[n]); } /* ========================================================================= */ local uLong crc32_combine_(crc1, crc2, len2) uLong crc1; uLong crc2; z_off64_t len2; { int n; unsigned long row; unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ /* degenerate case (also disallow negative lengths) */ if (len2 <= 0) return crc1; /* put operator for one zero bit in odd */ odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ row = 1; for (n = 1; n < GF2_DIM; n++) { odd[n] = row; row <<= 1; } /* put operator for two zero bits in even */ gf2_matrix_square(even, odd); /* put operator for four zero bits in odd */ gf2_matrix_square(odd, even); /* apply len2 zeros to crc1 (first square will put the operator for one zero byte, eight zero bits, in even) */ do { /* apply zeros operator for this bit of len2 */ gf2_matrix_square(even, odd); if (len2 & 1) crc1 = gf2_matrix_times(even, crc1); len2 >>= 1; /* if no more bits set, then done */ if (len2 == 0) break; /* another iteration of the loop with odd and even swapped */ gf2_matrix_square(odd, even); if (len2 & 1) crc1 = gf2_matrix_times(odd, crc1); len2 >>= 1; /* if no more bits set, then done */ } while (len2 != 0); /* return combined crc */ crc1 ^= crc2; return crc1; } /* ========================================================================= */ uLong ZEXPORT crc32_combine(crc1, crc2, len2) uLong crc1; uLong crc2; z_off_t len2; { return crc32_combine_(crc1, crc2, len2); } uLong ZEXPORT crc32_combine64(crc1, crc2, len2) uLong crc1; uLong crc2; z_off64_t len2; { return crc32_combine_(crc1, crc2, len2); } PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/crc32.h000066400000000000000000000735421231437614300231570ustar00rootroot00000000000000/* crc32.h -- tables for rapid CRC calculation * Generated automatically by crc32.c */ local const z_crc_t FAR crc_table[TBLS][256] = { { 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, 0x2d02ef8dUL #ifdef BYFOUR }, { 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, 0x9324fd72UL }, { 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, 0xbe9834edUL }, { 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, 0xde0506f1UL }, { 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, 0x8def022dUL }, { 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, 0x72fd2493UL }, { 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, 0xed3498beUL }, { 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, 0xf10605deUL #endif } }; PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/deflate.c000066400000000000000000002134641231437614300236410ustar00rootroot00000000000000/* deflate.c -- compress data using the deflation algorithm * Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process depends on being able to identify portions * of the input text which are identical to earlier input (within a * sliding window trailing behind the input currently being processed). * * The most straightforward technique turns out to be the fastest for * most input files: try all possible matches and select the longest. * The key feature of this algorithm is that insertions into the string * dictionary are very simple and thus fast, and deletions are avoided * completely. Insertions are performed at each input character, whereas * string matches are performed only when the previous match ends. So it * is preferable to spend more time in matches to allow very fast string * insertions and avoid deletions. The matching algorithm for small * strings is inspired from that of Rabin & Karp. A brute force approach * is used to find longer strings when a small match has been found. * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze * (by Leonid Broukhis). * A previous version of this file used a more sophisticated algorithm * (by Fiala and Greene) which is guaranteed to run in linear amortized * time, but has a larger average cost, uses more memory and is patented. * However the F&G algorithm may be faster for some highly redundant * files if the parameter max_chain_length (described below) is too large. * * ACKNOWLEDGEMENTS * * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and * I found it in 'freeze' written by Leonid Broukhis. * Thanks to many people for bug reports and testing. * * REFERENCES * * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". * Available in http://tools.ietf.org/html/rfc1951 * * A description of the Rabin and Karp algorithm is given in the book * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. * * Fiala,E.R., and Greene,D.H. * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 * */ /* @(#) $Id$ */ #include "deflate.h" const char deflate_copyright[] = " deflate 1.2.8 Copyright 1995-2013 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ /* =========================================================================== * Function prototypes. */ typedef enum { need_more, /* block not completed, need more input or more output */ block_done, /* block flush performed */ finish_started, /* finish started, need only more output at next deflate */ finish_done /* finish done, accept no more input or output */ } block_state; typedef block_state (*compress_func) OF((deflate_state *s, int flush)); /* Compression function. Returns the block state after the call. */ local void fill_window OF((deflate_state *s)); local block_state deflate_stored OF((deflate_state *s, int flush)); local block_state deflate_fast OF((deflate_state *s, int flush)); #ifndef FASTEST local block_state deflate_slow OF((deflate_state *s, int flush)); #endif local block_state deflate_rle OF((deflate_state *s, int flush)); local block_state deflate_huff OF((deflate_state *s, int flush)); local void lm_init OF((deflate_state *s)); local void putShortMSB OF((deflate_state *s, uInt b)); local void flush_pending OF((z_streamp strm)); local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); #ifdef ASMV void match_init OF((void)); /* asm code initialization */ uInt longest_match OF((deflate_state *s, IPos cur_match)); #else local uInt longest_match OF((deflate_state *s, IPos cur_match)); #endif #ifdef DEBUG local void check_match OF((deflate_state *s, IPos start, IPos match, int length)); #endif /* =========================================================================== * Local data */ #define NIL 0 /* Tail of hash chains */ #ifndef TOO_FAR # define TOO_FAR 4096 #endif /* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ /* Values for max_lazy_match, good_match and max_chain_length, depending on * the desired pack level (0..9). The values given below have been tuned to * exclude worst case performance for pathological files. Better values may be * found for specific files. */ typedef struct config_s { ush good_length; /* reduce lazy search above this match length */ ush max_lazy; /* do not perform lazy search above this match length */ ush nice_length; /* quit search above this match length */ ush max_chain; compress_func func; } config; #ifdef FASTEST local const config configuration_table[2] = { /* good lazy nice chain */ /* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ /* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ #else local const config configuration_table[10] = { /* good lazy nice chain */ /* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ /* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ /* 2 */ {4, 5, 16, 8, deflate_fast}, /* 3 */ {4, 6, 32, 32, deflate_fast}, /* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ /* 5 */ {8, 16, 32, 32, deflate_slow}, /* 6 */ {8, 16, 128, 128, deflate_slow}, /* 7 */ {8, 32, 128, 256, deflate_slow}, /* 8 */ {32, 128, 258, 1024, deflate_slow}, /* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ #endif /* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 * For deflate_fast() (levels <= 3) good is ignored and lazy has a different * meaning. */ #define EQUAL 0 /* result of memcmp for equal strings */ #ifndef NO_DUMMY_DECL struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ #endif /* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ #define RANK(f) (((f) << 1) - ((f) > 4 ? 9 : 0)) /* =========================================================================== * Update a hash value with the given input byte * IN assertion: all calls to to UPDATE_HASH are made with consecutive * input characters, so that a running hash key can be computed from the * previous key instead of complete recalculation each time. */ #define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) /* =========================================================================== * Insert string str in the dictionary and set match_head to the previous head * of the hash chain (the most recent string with same hash key). Return * the previous length of the hash chain. * If this file is compiled with -DFASTEST, the compression level is forced * to 1, and no hash chains are maintained. * IN assertion: all calls to to INSERT_STRING are made with consecutive * input characters and the first MIN_MATCH bytes of str are valid * (except for the last MIN_MATCH-1 bytes of the input file). */ #ifdef FASTEST #define INSERT_STRING(s, str, match_head) \ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ match_head = s->head[s->ins_h], \ s->head[s->ins_h] = (Pos)(str)) #else #define INSERT_STRING(s, str, match_head) \ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ s->head[s->ins_h] = (Pos)(str)) #endif /* =========================================================================== * Initialize the hash table (avoiding 64K overflow for 16 bit systems). * prev[] will be initialized on the fly. */ #define CLEAR_HASH(s) \ s->head[s->hash_size-1] = NIL; \ zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); /* ========================================================================= */ int ZEXPORT deflateInit_(strm, level, version, stream_size) z_streamp strm; int level; const char *version; int stream_size; { return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, version, stream_size); /* To do: ignore strm->next_in if we use it as window */ } /* ========================================================================= */ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, version, stream_size) z_streamp strm; int level; int method; int windowBits; int memLevel; int strategy; const char *version; int stream_size; { deflate_state *s; int wrap = 1; static const char my_version[] = ZLIB_VERSION; ushf *overlay; /* We overlay pending_buf and d_buf+l_buf. This works since the average * output size for (length,distance) codes is <= 24 bits. */ if (version == Z_NULL || version[0] != my_version[0] || stream_size != sizeof(z_stream)) { return Z_VERSION_ERROR; } if (strm == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; if (strm->zalloc == (alloc_func)0) { #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zalloc = zcalloc; strm->opaque = (voidpf)0; #endif } if (strm->zfree == (free_func)0) #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zfree = zcfree; #endif #ifdef FASTEST if (level != 0) level = 1; #else if (level == Z_DEFAULT_COMPRESSION) level = 6; #endif if (windowBits < 0) { /* suppress zlib wrapper */ wrap = 0; windowBits = -windowBits; } #ifdef GZIP else if (windowBits > 15) { wrap = 2; /* write gzip wrapper instead */ windowBits -= 16; } #endif if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { return Z_STREAM_ERROR; } if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); if (s == Z_NULL) return Z_MEM_ERROR; strm->state = (struct internal_state FAR *)s; s->strm = strm; s->wrap = wrap; s->gzhead = Z_NULL; s->w_bits = windowBits; s->w_size = 1 << s->w_bits; s->w_mask = s->w_size - 1; s->hash_bits = memLevel + 7; s->hash_size = 1 << s->hash_bits; s->hash_mask = s->hash_size - 1; s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); s->high_water = 0; /* nothing written to s->window yet */ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); s->pending_buf = (uchf *) overlay; s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || s->pending_buf == Z_NULL) { s->status = FINISH_STATE; strm->msg = ERR_MSG(Z_MEM_ERROR); deflateEnd (strm); return Z_MEM_ERROR; } s->d_buf = overlay + s->lit_bufsize/sizeof(ush); s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; s->level = level; s->strategy = strategy; s->method = (Byte)method; return deflateReset(strm); } /* ========================================================================= */ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) z_streamp strm; const Bytef *dictionary; uInt dictLength; { deflate_state *s; uInt str, n; int wrap; unsigned avail; z_const unsigned char *next; if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL) return Z_STREAM_ERROR; s = strm->state; wrap = s->wrap; if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead) return Z_STREAM_ERROR; /* when using zlib wrappers, compute Adler-32 for provided dictionary */ if (wrap == 1) strm->adler = adler32(strm->adler, dictionary, dictLength); s->wrap = 0; /* avoid computing Adler-32 in read_buf */ /* if dictionary would fill window, just replace the history */ if (dictLength >= s->w_size) { if (wrap == 0) { /* already empty otherwise */ CLEAR_HASH(s); s->strstart = 0; s->block_start = 0L; s->insert = 0; } dictionary += dictLength - s->w_size; /* use the tail */ dictLength = s->w_size; } /* insert dictionary into window and hash */ avail = strm->avail_in; next = strm->next_in; strm->avail_in = dictLength; strm->next_in = (z_const Bytef *)dictionary; fill_window(s); while (s->lookahead >= MIN_MATCH) { str = s->strstart; n = s->lookahead - (MIN_MATCH-1); do { UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); #ifndef FASTEST s->prev[str & s->w_mask] = s->head[s->ins_h]; #endif s->head[s->ins_h] = (Pos)str; str++; } while (--n); s->strstart = str; s->lookahead = MIN_MATCH-1; fill_window(s); } s->strstart += s->lookahead; s->block_start = (long)s->strstart; s->insert = s->lookahead; s->lookahead = 0; s->match_length = s->prev_length = MIN_MATCH-1; s->match_available = 0; strm->next_in = next; strm->avail_in = avail; s->wrap = wrap; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateResetKeep (strm) z_streamp strm; { deflate_state *s; if (strm == Z_NULL || strm->state == Z_NULL || strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { return Z_STREAM_ERROR; } strm->total_in = strm->total_out = 0; strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ strm->data_type = Z_UNKNOWN; s = (deflate_state *)strm->state; s->pending = 0; s->pending_out = s->pending_buf; if (s->wrap < 0) { s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ } s->status = s->wrap ? INIT_STATE : BUSY_STATE; strm->adler = #ifdef GZIP s->wrap == 2 ? crc32(0L, Z_NULL, 0) : #endif adler32(0L, Z_NULL, 0); s->last_flush = Z_NO_FLUSH; _tr_init(s); return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateReset (strm) z_streamp strm; { int ret; ret = deflateResetKeep(strm); if (ret == Z_OK) lm_init(strm->state); return ret; } /* ========================================================================= */ int ZEXPORT deflateSetHeader (strm, head) z_streamp strm; gz_headerp head; { if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; if (strm->state->wrap != 2) return Z_STREAM_ERROR; strm->state->gzhead = head; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflatePending (strm, pending, bits) unsigned *pending; int *bits; z_streamp strm; { if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; if (pending != Z_NULL) *pending = strm->state->pending; if (bits != Z_NULL) *bits = strm->state->bi_valid; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflatePrime (strm, bits, value) z_streamp strm; int bits; int value; { deflate_state *s; int put; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; s = strm->state; if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3)) return Z_BUF_ERROR; do { put = Buf_size - s->bi_valid; if (put > bits) put = bits; s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid); s->bi_valid += put; _tr_flush_bits(s); value >>= put; bits -= put; } while (bits); return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateParams(strm, level, strategy) z_streamp strm; int level; int strategy; { deflate_state *s; compress_func func; int err = Z_OK; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; s = strm->state; #ifdef FASTEST if (level != 0) level = 1; #else if (level == Z_DEFAULT_COMPRESSION) level = 6; #endif if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { return Z_STREAM_ERROR; } func = configuration_table[s->level].func; if ((strategy != s->strategy || func != configuration_table[level].func) && strm->total_in != 0) { /* Flush the last buffer: */ err = deflate(strm, Z_BLOCK); if (err == Z_BUF_ERROR && s->pending == 0) err = Z_OK; } if (s->level != level) { s->level = level; s->max_lazy_match = configuration_table[level].max_lazy; s->good_match = configuration_table[level].good_length; s->nice_match = configuration_table[level].nice_length; s->max_chain_length = configuration_table[level].max_chain; } s->strategy = strategy; return err; } /* ========================================================================= */ int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) z_streamp strm; int good_length; int max_lazy; int nice_length; int max_chain; { deflate_state *s; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; s = strm->state; s->good_match = good_length; s->max_lazy_match = max_lazy; s->nice_match = nice_length; s->max_chain_length = max_chain; return Z_OK; } /* ========================================================================= * For the default windowBits of 15 and memLevel of 8, this function returns * a close to exact, as well as small, upper bound on the compressed size. * They are coded as constants here for a reason--if the #define's are * changed, then this function needs to be changed as well. The return * value for 15 and 8 only works for those exact settings. * * For any setting other than those defaults for windowBits and memLevel, * the value returned is a conservative worst case for the maximum expansion * resulting from using fixed blocks instead of stored blocks, which deflate * can emit on compressed data for some combinations of the parameters. * * This function could be more sophisticated to provide closer upper bounds for * every combination of windowBits and memLevel. But even the conservative * upper bound of about 14% expansion does not seem onerous for output buffer * allocation. */ uLong ZEXPORT deflateBound(strm, sourceLen) z_streamp strm; uLong sourceLen; { deflate_state *s; uLong complen, wraplen; Bytef *str; /* conservative upper bound for compressed data */ complen = sourceLen + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; /* if can't get parameters, return conservative bound plus zlib wrapper */ if (strm == Z_NULL || strm->state == Z_NULL) return complen + 6; /* compute wrapper length */ s = strm->state; switch (s->wrap) { case 0: /* raw deflate */ wraplen = 0; break; case 1: /* zlib wrapper */ wraplen = 6 + (s->strstart ? 4 : 0); break; case 2: /* gzip wrapper */ wraplen = 18; if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ if (s->gzhead->extra != Z_NULL) wraplen += 2 + s->gzhead->extra_len; str = s->gzhead->name; if (str != Z_NULL) do { wraplen++; } while (*str++); str = s->gzhead->comment; if (str != Z_NULL) do { wraplen++; } while (*str++); if (s->gzhead->hcrc) wraplen += 2; } break; default: /* for compiler happiness */ wraplen = 6; } /* if not default parameters, return conservative bound */ if (s->w_bits != 15 || s->hash_bits != 8 + 7) return complen + wraplen; /* default settings: return tight bound for that case */ return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13 - 6 + wraplen; } /* ========================================================================= * Put a short in the pending buffer. The 16-bit value is put in MSB order. * IN assertion: the stream state is correct and there is enough room in * pending_buf. */ local void putShortMSB (s, b) deflate_state *s; uInt b; { put_byte(s, (Byte)(b >> 8)); put_byte(s, (Byte)(b & 0xff)); } /* ========================================================================= * Flush as much pending output as possible. All deflate() output goes * through this function so some applications may wish to modify it * to avoid allocating a large strm->next_out buffer and copying into it. * (See also read_buf()). */ local void flush_pending(strm) z_streamp strm; { unsigned len; deflate_state *s = strm->state; _tr_flush_bits(s); len = s->pending; if (len > strm->avail_out) len = strm->avail_out; if (len == 0) return; zmemcpy(strm->next_out, s->pending_out, len); strm->next_out += len; s->pending_out += len; strm->total_out += len; strm->avail_out -= len; s->pending -= len; if (s->pending == 0) { s->pending_out = s->pending_buf; } } /* ========================================================================= */ int ZEXPORT deflate (strm, flush) z_streamp strm; int flush; { int old_flush; /* value of flush param for previous deflate call */ deflate_state *s; if (strm == Z_NULL || strm->state == Z_NULL || flush > Z_BLOCK || flush < 0) { return Z_STREAM_ERROR; } s = strm->state; if (strm->next_out == Z_NULL || (strm->next_in == Z_NULL && strm->avail_in != 0) || (s->status == FINISH_STATE && flush != Z_FINISH)) { ERR_RETURN(strm, Z_STREAM_ERROR); } if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); s->strm = strm; /* just in case */ old_flush = s->last_flush; s->last_flush = flush; /* Write the header */ if (s->status == INIT_STATE) { #ifdef GZIP if (s->wrap == 2) { strm->adler = crc32(0L, Z_NULL, 0); put_byte(s, 31); put_byte(s, 139); put_byte(s, 8); if (s->gzhead == Z_NULL) { put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, s->level == 9 ? 2 : (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0)); put_byte(s, OS_CODE); s->status = BUSY_STATE; } else { put_byte(s, (s->gzhead->text ? 1 : 0) + (s->gzhead->hcrc ? 2 : 0) + (s->gzhead->extra == Z_NULL ? 0 : 4) + (s->gzhead->name == Z_NULL ? 0 : 8) + (s->gzhead->comment == Z_NULL ? 0 : 16) ); put_byte(s, (Byte)(s->gzhead->time & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); put_byte(s, s->level == 9 ? 2 : (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0)); put_byte(s, s->gzhead->os & 0xff); if (s->gzhead->extra != Z_NULL) { put_byte(s, s->gzhead->extra_len & 0xff); put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); } if (s->gzhead->hcrc) strm->adler = crc32(strm->adler, s->pending_buf, s->pending); s->gzindex = 0; s->status = EXTRA_STATE; } } else #endif { uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; uInt level_flags; if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) level_flags = 0; else if (s->level < 6) level_flags = 1; else if (s->level == 6) level_flags = 2; else level_flags = 3; header |= (level_flags << 6); if (s->strstart != 0) header |= PRESET_DICT; header += 31 - (header % 31); s->status = BUSY_STATE; putShortMSB(s, header); /* Save the adler32 of the preset dictionary: */ if (s->strstart != 0) { putShortMSB(s, (uInt)(strm->adler >> 16)); putShortMSB(s, (uInt)(strm->adler & 0xffff)); } strm->adler = adler32(0L, Z_NULL, 0); } } #ifdef GZIP if (s->status == EXTRA_STATE) { if (s->gzhead->extra != Z_NULL) { uInt beg = s->pending; /* start of bytes to update crc */ while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { if (s->pending == s->pending_buf_size) { if (s->gzhead->hcrc && s->pending > beg) strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); flush_pending(strm); beg = s->pending; if (s->pending == s->pending_buf_size) break; } put_byte(s, s->gzhead->extra[s->gzindex]); s->gzindex++; } if (s->gzhead->hcrc && s->pending > beg) strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); if (s->gzindex == s->gzhead->extra_len) { s->gzindex = 0; s->status = NAME_STATE; } } else s->status = NAME_STATE; } if (s->status == NAME_STATE) { if (s->gzhead->name != Z_NULL) { uInt beg = s->pending; /* start of bytes to update crc */ int val; do { if (s->pending == s->pending_buf_size) { if (s->gzhead->hcrc && s->pending > beg) strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); flush_pending(strm); beg = s->pending; if (s->pending == s->pending_buf_size) { val = 1; break; } } val = s->gzhead->name[s->gzindex++]; put_byte(s, val); } while (val != 0); if (s->gzhead->hcrc && s->pending > beg) strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); if (val == 0) { s->gzindex = 0; s->status = COMMENT_STATE; } } else s->status = COMMENT_STATE; } if (s->status == COMMENT_STATE) { if (s->gzhead->comment != Z_NULL) { uInt beg = s->pending; /* start of bytes to update crc */ int val; do { if (s->pending == s->pending_buf_size) { if (s->gzhead->hcrc && s->pending > beg) strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); flush_pending(strm); beg = s->pending; if (s->pending == s->pending_buf_size) { val = 1; break; } } val = s->gzhead->comment[s->gzindex++]; put_byte(s, val); } while (val != 0); if (s->gzhead->hcrc && s->pending > beg) strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); if (val == 0) s->status = HCRC_STATE; } else s->status = HCRC_STATE; } if (s->status == HCRC_STATE) { if (s->gzhead->hcrc) { if (s->pending + 2 > s->pending_buf_size) flush_pending(strm); if (s->pending + 2 <= s->pending_buf_size) { put_byte(s, (Byte)(strm->adler & 0xff)); put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); strm->adler = crc32(0L, Z_NULL, 0); s->status = BUSY_STATE; } } else s->status = BUSY_STATE; } #endif /* Flush as much pending output as possible */ if (s->pending != 0) { flush_pending(strm); if (strm->avail_out == 0) { /* Since avail_out is 0, deflate will be called again with * more output space, but possibly with both pending and * avail_in equal to zero. There won't be anything to do, * but this is not an error situation so make sure we * return OK instead of BUF_ERROR at next call of deflate: */ s->last_flush = -1; return Z_OK; } /* Make sure there is something to do and avoid duplicate consecutive * flushes. For repeated and useless calls with Z_FINISH, we keep * returning Z_STREAM_END instead of Z_BUF_ERROR. */ } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && flush != Z_FINISH) { ERR_RETURN(strm, Z_BUF_ERROR); } /* User must not provide more input after the first FINISH: */ if (s->status == FINISH_STATE && strm->avail_in != 0) { ERR_RETURN(strm, Z_BUF_ERROR); } /* Start a new block or continue the current one. */ if (strm->avail_in != 0 || s->lookahead != 0 || (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { block_state bstate; bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : (s->strategy == Z_RLE ? deflate_rle(s, flush) : (*(configuration_table[s->level].func))(s, flush)); if (bstate == finish_started || bstate == finish_done) { s->status = FINISH_STATE; } if (bstate == need_more || bstate == finish_started) { if (strm->avail_out == 0) { s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ } return Z_OK; /* If flush != Z_NO_FLUSH && avail_out == 0, the next call * of deflate should use the same flush parameter to make sure * that the flush is complete. So we don't have to output an * empty block here, this will be done at next call. This also * ensures that for a very small output buffer, we emit at most * one empty block. */ } if (bstate == block_done) { if (flush == Z_PARTIAL_FLUSH) { _tr_align(s); } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ _tr_stored_block(s, (char*)0, 0L, 0); /* For a full flush, this empty block will be recognized * as a special marker by inflate_sync(). */ if (flush == Z_FULL_FLUSH) { CLEAR_HASH(s); /* forget history */ if (s->lookahead == 0) { s->strstart = 0; s->block_start = 0L; s->insert = 0; } } } flush_pending(strm); if (strm->avail_out == 0) { s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ return Z_OK; } } } Assert(strm->avail_out > 0, "bug2"); if (flush != Z_FINISH) return Z_OK; if (s->wrap <= 0) return Z_STREAM_END; /* Write the trailer */ #ifdef GZIP if (s->wrap == 2) { put_byte(s, (Byte)(strm->adler & 0xff)); put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); put_byte(s, (Byte)(strm->total_in & 0xff)); put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); } else #endif { putShortMSB(s, (uInt)(strm->adler >> 16)); putShortMSB(s, (uInt)(strm->adler & 0xffff)); } flush_pending(strm); /* If avail_out is zero, the application will call deflate again * to flush the rest. */ if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ return s->pending != 0 ? Z_OK : Z_STREAM_END; } /* ========================================================================= */ int ZEXPORT deflateEnd (strm) z_streamp strm; { int status; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; status = strm->state->status; if (status != INIT_STATE && status != EXTRA_STATE && status != NAME_STATE && status != COMMENT_STATE && status != HCRC_STATE && status != BUSY_STATE && status != FINISH_STATE) { return Z_STREAM_ERROR; } /* Deallocate in reverse order of allocations: */ TRY_FREE(strm, strm->state->pending_buf); TRY_FREE(strm, strm->state->head); TRY_FREE(strm, strm->state->prev); TRY_FREE(strm, strm->state->window); ZFREE(strm, strm->state); strm->state = Z_NULL; return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; } /* ========================================================================= * Copy the source state to the destination state. * To simplify the source, this is not supported for 16-bit MSDOS (which * doesn't have enough memory anyway to duplicate compression states). */ int ZEXPORT deflateCopy (dest, source) z_streamp dest; z_streamp source; { #ifdef MAXSEG_64K return Z_STREAM_ERROR; #else deflate_state *ds; deflate_state *ss; ushf *overlay; if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { return Z_STREAM_ERROR; } ss = source->state; zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); if (ds == Z_NULL) return Z_MEM_ERROR; dest->state = (struct internal_state FAR *) ds; zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state)); ds->strm = dest; ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); ds->pending_buf = (uchf *) overlay; if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || ds->pending_buf == Z_NULL) { deflateEnd (dest); return Z_MEM_ERROR; } /* following zmemcpy do not work for 16-bit MSDOS */ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; ds->l_desc.dyn_tree = ds->dyn_ltree; ds->d_desc.dyn_tree = ds->dyn_dtree; ds->bl_desc.dyn_tree = ds->bl_tree; return Z_OK; #endif /* MAXSEG_64K */ } /* =========================================================================== * Read a new buffer from the current input stream, update the adler32 * and total number of bytes read. All deflate() input goes through * this function so some applications may wish to modify it to avoid * allocating a large strm->next_in buffer and copying from it. * (See also flush_pending()). */ local int read_buf(strm, buf, size) z_streamp strm; Bytef *buf; unsigned size; { unsigned len = strm->avail_in; if (len > size) len = size; if (len == 0) return 0; strm->avail_in -= len; zmemcpy(buf, strm->next_in, len); if (strm->state->wrap == 1) { strm->adler = adler32(strm->adler, buf, len); } #ifdef GZIP else if (strm->state->wrap == 2) { strm->adler = crc32(strm->adler, buf, len); } #endif strm->next_in += len; strm->total_in += len; return (int)len; } /* =========================================================================== * Initialize the "longest match" routines for a new zlib stream */ local void lm_init (s) deflate_state *s; { s->window_size = (ulg)2L*s->w_size; CLEAR_HASH(s); /* Set the default configuration parameters: */ s->max_lazy_match = configuration_table[s->level].max_lazy; s->good_match = configuration_table[s->level].good_length; s->nice_match = configuration_table[s->level].nice_length; s->max_chain_length = configuration_table[s->level].max_chain; s->strstart = 0; s->block_start = 0L; s->lookahead = 0; s->insert = 0; s->match_length = s->prev_length = MIN_MATCH-1; s->match_available = 0; s->ins_h = 0; #ifndef FASTEST #ifdef ASMV match_init(); /* initialize the asm code */ #endif #endif } #ifndef FASTEST /* =========================================================================== * Set match_start to the longest match starting at the given string and * return its length. Matches shorter or equal to prev_length are discarded, * in which case the result is equal to prev_length and match_start is * garbage. * IN assertions: cur_match is the head of the hash chain for the current * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 * OUT assertion: the match length is not greater than s->lookahead. */ #ifndef ASMV /* For 80x86 and 680x0, an optimized version will be provided in match.asm or * match.S. The code will be functionally equivalent. */ local uInt longest_match(s, cur_match) deflate_state *s; IPos cur_match; /* current match */ { unsigned chain_length = s->max_chain_length;/* max hash chain length */ register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ int best_len = s->prev_length; /* best match length so far */ int nice_match = s->nice_match; /* stop if match long enough */ IPos limit = s->strstart > (IPos)MAX_DIST(s) ? s->strstart - (IPos)MAX_DIST(s) : NIL; /* Stop when cur_match becomes <= limit. To simplify the code, * we prevent matches with the string of window index 0. */ Posf *prev = s->prev; uInt wmask = s->w_mask; #ifdef UNALIGNED_OK /* Compare two bytes at a time. Note: this is not always beneficial. * Try with and without -DUNALIGNED_OK to check. */ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; register ush scan_start = *(ushf*)scan; register ush scan_end = *(ushf*)(scan+best_len-1); #else register Bytef *strend = s->window + s->strstart + MAX_MATCH; register Byte scan_end1 = scan[best_len-1]; register Byte scan_end = scan[best_len]; #endif /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); /* Do not waste too much time if we already have a good match: */ if (s->prev_length >= s->good_match) { chain_length >>= 2; } /* Do not look for matches beyond the end of the input. This is necessary * to make deflate deterministic. */ if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); do { Assert(cur_match < s->strstart, "no future"); match = s->window + cur_match; /* Skip to next match if the match length cannot increase * or if the match length is less than 2. Note that the checks below * for insufficient lookahead only occur occasionally for performance * reasons. Therefore uninitialized memory will be accessed, and * conditional jumps will be made that depend on those values. * However the length of the match is limited to the lookahead, so * the output of deflate is not affected by the uninitialized values. */ #if (defined(UNALIGNED_OK) && MAX_MATCH == 258) /* This code assumes sizeof(unsigned short) == 2. Do not use * UNALIGNED_OK if your compiler uses a different size. */ if (*(ushf*)(match+best_len-1) != scan_end || *(ushf*)match != scan_start) continue; /* It is not necessary to compare scan[2] and match[2] since they are * always equal when the other bytes match, given that the hash keys * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at * strstart+3, +5, ... up to strstart+257. We check for insufficient * lookahead only every 4th comparison; the 128th check will be made * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is * necessary to put more guard bytes at the end of the window, or * to check more often for insufficient lookahead. */ Assert(scan[2] == match[2], "scan[2]?"); scan++, match++; do { } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && scan < strend); /* The funny "do {}" generates better code on most compilers */ /* Here, scan <= window+strstart+257 */ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); if (*scan == *match) scan++; len = (MAX_MATCH - 1) - (int)(strend-scan); scan = strend - (MAX_MATCH-1); #else /* UNALIGNED_OK */ if (match[best_len] != scan_end || match[best_len-1] != scan_end1 || *match != *scan || *++match != scan[1]) continue; /* The check at best_len-1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2, match++; Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. */ do { } while (*++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && scan < strend); Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); len = MAX_MATCH - (int)(strend - scan); scan = strend - MAX_MATCH; #endif /* UNALIGNED_OK */ if (len > best_len) { s->match_start = cur_match; best_len = len; if (len >= nice_match) break; #ifdef UNALIGNED_OK scan_end = *(ushf*)(scan+best_len-1); #else scan_end1 = scan[best_len-1]; scan_end = scan[best_len]; #endif } } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length != 0); if ((uInt)best_len <= s->lookahead) return (uInt)best_len; return s->lookahead; } #endif /* ASMV */ #else /* FASTEST */ /* --------------------------------------------------------------------------- * Optimized version for FASTEST only */ local uInt longest_match(s, cur_match) deflate_state *s; IPos cur_match; /* current match */ { register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ register Bytef *strend = s->window + s->strstart + MAX_MATCH; /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); Assert(cur_match < s->strstart, "no future"); match = s->window + cur_match; /* Return failure if the match length is less than 2: */ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; /* The check at best_len-1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2, match += 2; Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. */ do { } while (*++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && scan < strend); Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); len = MAX_MATCH - (int)(strend - scan); if (len < MIN_MATCH) return MIN_MATCH - 1; s->match_start = cur_match; return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; } #endif /* FASTEST */ #ifdef DEBUG /* =========================================================================== * Check that the match at match_start is indeed a match. */ local void check_match(s, start, match, length) deflate_state *s; IPos start, match; int length; { /* check that the match is indeed a match */ if (zmemcmp(s->window + match, s->window + start, length) != EQUAL) { fprintf(stderr, " start %u, match %u, length %d\n", start, match, length); do { fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); } while (--length != 0); z_error("invalid match"); } if (z_verbose > 1) { fprintf(stderr,"\\[%d,%d]", start-match, length); do { putc(s->window[start++], stderr); } while (--length != 0); } } #else # define check_match(s, start, match, length) #endif /* DEBUG */ /* =========================================================================== * Fill the window when the lookahead becomes insufficient. * Updates strstart and lookahead. * * IN assertion: lookahead < MIN_LOOKAHEAD * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD * At least one byte has been read, or avail_in == 0; reads are * performed for at least two bytes (required for the zip translate_eol * option -- not supported here). */ local void fill_window(s) deflate_state *s; { register unsigned n, m; register Posf *p; unsigned more; /* Amount of free space at the end of the window. */ uInt wsize = s->w_size; Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); do { more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); /* Deal with !@#$% 64K limit: */ if (sizeof(int) <= 2) { if (more == 0 && s->strstart == 0 && s->lookahead == 0) { more = wsize; } else if (more == (unsigned)(-1)) { /* Very unlikely, but possible on 16 bit machine if * strstart == 0 && lookahead == 1 (input done a byte at time) */ more--; } } /* If the window is almost full and there is insufficient lookahead, * move the upper half to the lower one to make room in the upper half. */ if (s->strstart >= wsize+MAX_DIST(s)) { zmemcpy(s->window, s->window+wsize, (unsigned)wsize); s->match_start -= wsize; s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ s->block_start -= (long) wsize; /* Slide the hash table (could be avoided with 32 bit values at the expense of memory usage). We slide even when level == 0 to keep the hash table consistent if we switch back to level > 0 later. (Using level 0 permanently is not an optimal usage of zlib, so we don't care about this pathological case.) */ n = s->hash_size; p = &s->head[n]; do { m = *--p; *p = (Pos)(m >= wsize ? m-wsize : NIL); } while (--n); n = wsize; #ifndef FASTEST p = &s->prev[n]; do { m = *--p; *p = (Pos)(m >= wsize ? m-wsize : NIL); /* If n is not on any hash chain, prev[n] is garbage but * its value will never be used. */ } while (--n); #endif more += wsize; } if (s->strm->avail_in == 0) break; /* If there was no sliding: * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && * more == window_size - lookahead - strstart * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) * => more >= window_size - 2*WSIZE + 2 * In the BIG_MEM or MMAP case (not yet supported), * window_size == input_size + MIN_LOOKAHEAD && * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. * Otherwise, window_size == 2*WSIZE so more >= 2. * If there was sliding, more >= WSIZE. So in all cases, more >= 2. */ Assert(more >= 2, "more < 2"); n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); s->lookahead += n; /* Initialize the hash value now that we have some input: */ if (s->lookahead + s->insert >= MIN_MATCH) { uInt str = s->strstart - s->insert; s->ins_h = s->window[str]; UPDATE_HASH(s, s->ins_h, s->window[str + 1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif while (s->insert) { UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); #ifndef FASTEST s->prev[str & s->w_mask] = s->head[s->ins_h]; #endif s->head[s->ins_h] = (Pos)str; str++; s->insert--; if (s->lookahead + s->insert < MIN_MATCH) break; } } /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, * but this is not important since only literal bytes will be emitted. */ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); /* If the WIN_INIT bytes after the end of the current data have never been * written, then zero those bytes in order to avoid memory check reports of * the use of uninitialized (or uninitialised as Julian writes) bytes by * the longest match routines. Update the high water mark for the next * time through here. WIN_INIT is set to MAX_MATCH since the longest match * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. */ if (s->high_water < s->window_size) { ulg curr = s->strstart + (ulg)(s->lookahead); ulg init; if (s->high_water < curr) { /* Previous high water mark below current data -- zero WIN_INIT * bytes or up to end of window, whichever is less. */ init = s->window_size - curr; if (init > WIN_INIT) init = WIN_INIT; zmemzero(s->window + curr, (unsigned)init); s->high_water = curr + init; } else if (s->high_water < (ulg)curr + WIN_INIT) { /* High water mark at or above current data, but below current data * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up * to end of window, whichever is less. */ init = (ulg)curr + WIN_INIT - s->high_water; if (init > s->window_size - s->high_water) init = s->window_size - s->high_water; zmemzero(s->window + s->high_water, (unsigned)init); s->high_water += init; } } Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, "not enough room for search"); } /* =========================================================================== * Flush the current block, with given end-of-file flag. * IN assertion: strstart is set to the end of the current match. */ #define FLUSH_BLOCK_ONLY(s, last) { \ _tr_flush_block(s, (s->block_start >= 0L ? \ (charf *)&s->window[(unsigned)s->block_start] : \ (charf *)Z_NULL), \ (ulg)((long)s->strstart - s->block_start), \ (last)); \ s->block_start = s->strstart; \ flush_pending(s->strm); \ Tracev((stderr,"[FLUSH]")); \ } /* Same but force premature exit if necessary. */ #define FLUSH_BLOCK(s, last) { \ FLUSH_BLOCK_ONLY(s, last); \ if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ } /* =========================================================================== * Copy without compression as much as possible from the input stream, return * the current block state. * This function does not insert new strings in the dictionary since * uncompressible data is probably not useful. This function is used * only for the level=0 compression option. * NOTE: this function should be optimized to avoid extra copying from * window to pending_buf. */ local block_state deflate_stored(s, flush) deflate_state *s; int flush; { /* Stored blocks are limited to 0xffff bytes, pending_buf is limited * to pending_buf_size, and each stored block has a 5 byte header: */ ulg max_block_size = 0xffff; ulg max_start; if (max_block_size > s->pending_buf_size - 5) { max_block_size = s->pending_buf_size - 5; } /* Copy as much as possible from input to output: */ for (;;) { /* Fill the window as much as possible: */ if (s->lookahead <= 1) { Assert(s->strstart < s->w_size+MAX_DIST(s) || s->block_start >= (long)s->w_size, "slide too late"); fill_window(s); if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; if (s->lookahead == 0) break; /* flush the current block */ } Assert(s->block_start >= 0L, "block gone"); s->strstart += s->lookahead; s->lookahead = 0; /* Emit a stored block if pending_buf will be full: */ max_start = s->block_start + max_block_size; if (s->strstart == 0 || (ulg)s->strstart >= max_start) { /* strstart == 0 is possible when wraparound on 16-bit machine */ s->lookahead = (uInt)(s->strstart - max_start); s->strstart = (uInt)max_start; FLUSH_BLOCK(s, 0); } /* Flush if we may have to slide, otherwise block_start may become * negative and the data will be gone: */ if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { FLUSH_BLOCK(s, 0); } } s->insert = 0; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if ((long)s->strstart > s->block_start) FLUSH_BLOCK(s, 0); return block_done; } /* =========================================================================== * Compress as much as possible from the input stream, return the current * block state. * This function does not perform lazy evaluation of matches and inserts * new strings in the dictionary only for unmatched strings or for short * matches. It is used only for the fast compression options. */ local block_state deflate_fast(s, flush) deflate_state *s; int flush; { IPos hash_head; /* head of the hash chain */ int bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s->lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = NIL; if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } /* Find the longest match, discarding those <= prev_length. * At this point we have always match_length < MIN_MATCH */ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s->match_length = longest_match (s, hash_head); /* longest_match() sets match_start */ } if (s->match_length >= MIN_MATCH) { check_match(s, s->strstart, s->match_start, s->match_length); _tr_tally_dist(s, s->strstart - s->match_start, s->match_length - MIN_MATCH, bflush); s->lookahead -= s->match_length; /* Insert new strings in the hash table only if the match length * is not too large. This saves time but degrades compression. */ #ifndef FASTEST if (s->match_length <= s->max_insert_length && s->lookahead >= MIN_MATCH) { s->match_length--; /* string at strstart already in table */ do { s->strstart++; INSERT_STRING(s, s->strstart, hash_head); /* strstart never exceeds WSIZE-MAX_MATCH, so there are * always MIN_MATCH bytes ahead. */ } while (--s->match_length != 0); s->strstart++; } else #endif { s->strstart += s->match_length; s->match_length = 0; s->ins_h = s->window[s->strstart]; UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not * matter since it will be recomputed at next deflate call. */ } } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit (s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; } if (bflush) FLUSH_BLOCK(s, 0); } s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->last_lit) FLUSH_BLOCK(s, 0); return block_done; } #ifndef FASTEST /* =========================================================================== * Same as above, but achieves better compression. We use a lazy * evaluation for matches: a match is finally adopted only if there is * no better match at the next window position. */ local block_state deflate_slow(s, flush) deflate_state *s; int flush; { IPos hash_head; /* head of hash chain */ int bflush; /* set if current block must be flushed */ /* Process the input block. */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s->lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = NIL; if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } /* Find the longest match, discarding those <= prev_length. */ s->prev_length = s->match_length, s->prev_match = s->match_start; s->match_length = MIN_MATCH-1; if (hash_head != NIL && s->prev_length < s->max_lazy_match && s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s->match_length = longest_match (s, hash_head); /* longest_match() sets match_start */ if (s->match_length <= 5 && (s->strategy == Z_FILTERED #if TOO_FAR <= 32767 || (s->match_length == MIN_MATCH && s->strstart - s->match_start > TOO_FAR) #endif )) { /* If prev_match is also MIN_MATCH, match_start is garbage * but we will ignore the current match anyway. */ s->match_length = MIN_MATCH-1; } } /* If there was a match at the previous step and the current * match is not better, output the previous match: */ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ check_match(s, s->strstart-1, s->prev_match, s->prev_length); _tr_tally_dist(s, s->strstart -1 - s->prev_match, s->prev_length - MIN_MATCH, bflush); /* Insert in hash table all strings up to the end of the match. * strstart-1 and strstart are already inserted. If there is not * enough lookahead, the last two strings are not inserted in * the hash table. */ s->lookahead -= s->prev_length-1; s->prev_length -= 2; do { if (++s->strstart <= max_insert) { INSERT_STRING(s, s->strstart, hash_head); } } while (--s->prev_length != 0); s->match_available = 0; s->match_length = MIN_MATCH-1; s->strstart++; if (bflush) FLUSH_BLOCK(s, 0); } else if (s->match_available) { /* If there was no match at the previous position, output a * single literal. If there was a match but the current match * is longer, truncate the previous match to a single literal. */ Tracevv((stderr,"%c", s->window[s->strstart-1])); _tr_tally_lit(s, s->window[s->strstart-1], bflush); if (bflush) { FLUSH_BLOCK_ONLY(s, 0); } s->strstart++; s->lookahead--; if (s->strm->avail_out == 0) return need_more; } else { /* There is no previous match to compare with, wait for * the next step to decide. */ s->match_available = 1; s->strstart++; s->lookahead--; } } Assert (flush != Z_NO_FLUSH, "no flush?"); if (s->match_available) { Tracevv((stderr,"%c", s->window[s->strstart-1])); _tr_tally_lit(s, s->window[s->strstart-1], bflush); s->match_available = 0; } s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->last_lit) FLUSH_BLOCK(s, 0); return block_done; } #endif /* FASTEST */ /* =========================================================================== * For Z_RLE, simply look for runs of bytes, generate matches only of distance * one. Do not maintain a hash table. (It will be regenerated if this run of * deflate switches away from Z_RLE.) */ local block_state deflate_rle(s, flush) deflate_state *s; int flush; { int bflush; /* set if current block must be flushed */ uInt prev; /* byte at distance one to match */ Bytef *scan, *strend; /* scan goes up to strend for length of run */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the longest run, plus one for the unrolled loop. */ if (s->lookahead <= MAX_MATCH) { fill_window(s); if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* See how many times the previous byte repeats */ s->match_length = 0; if (s->lookahead >= MIN_MATCH && s->strstart > 0) { scan = s->window + s->strstart - 1; prev = *scan; if (prev == *++scan && prev == *++scan && prev == *++scan) { strend = s->window + s->strstart + MAX_MATCH; do { } while (prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && scan < strend); s->match_length = MAX_MATCH - (int)(strend - scan); if (s->match_length > s->lookahead) s->match_length = s->lookahead; } Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); } /* Emit match if have run of MIN_MATCH or longer, else emit literal */ if (s->match_length >= MIN_MATCH) { check_match(s, s->strstart, s->strstart - 1, s->match_length); _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); s->lookahead -= s->match_length; s->strstart += s->match_length; s->match_length = 0; } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit (s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; } if (bflush) FLUSH_BLOCK(s, 0); } s->insert = 0; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->last_lit) FLUSH_BLOCK(s, 0); return block_done; } /* =========================================================================== * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. * (It will be regenerated if this run of deflate switches away from Huffman.) */ local block_state deflate_huff(s, flush) deflate_state *s; int flush; { int bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we have a literal to write. */ if (s->lookahead == 0) { fill_window(s); if (s->lookahead == 0) { if (flush == Z_NO_FLUSH) return need_more; break; /* flush the current block */ } } /* Output a literal byte */ s->match_length = 0; Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit (s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; if (bflush) FLUSH_BLOCK(s, 0); } s->insert = 0; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->last_lit) FLUSH_BLOCK(s, 0); return block_done; } PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/deflate.h000066400000000000000000000307461231437614300236460ustar00rootroot00000000000000/* deflate.h -- internal compression state * Copyright (C) 1995-2012 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* @(#) $Id$ */ #ifndef DEFLATE_H #define DEFLATE_H #include "zutil.h" /* define NO_GZIP when compiling if you want to disable gzip header and trailer creation by deflate(). NO_GZIP would be used to avoid linking in the crc code when it is not needed. For shared libraries, gzip encoding should be left enabled. */ #ifndef NO_GZIP # define GZIP #endif /* =========================================================================== * Internal compression state. */ #define LENGTH_CODES 29 /* number of length codes, not counting the special END_BLOCK code */ #define LITERALS 256 /* number of literal bytes 0..255 */ #define L_CODES (LITERALS+1+LENGTH_CODES) /* number of Literal or Length codes, including the END_BLOCK code */ #define D_CODES 30 /* number of distance codes */ #define BL_CODES 19 /* number of codes used to transfer the bit lengths */ #define HEAP_SIZE (2*L_CODES+1) /* maximum heap size */ #define MAX_BITS 15 /* All codes must not exceed MAX_BITS bits */ #define Buf_size 16 /* size of bit buffer in bi_buf */ #define INIT_STATE 42 #define EXTRA_STATE 69 #define NAME_STATE 73 #define COMMENT_STATE 91 #define HCRC_STATE 103 #define BUSY_STATE 113 #define FINISH_STATE 666 /* Stream status */ /* Data structure describing a single value and its code string. */ typedef struct ct_data_s { union { ush freq; /* frequency count */ ush code; /* bit string */ } fc; union { ush dad; /* father node in Huffman tree */ ush len; /* length of bit string */ } dl; } FAR ct_data; #define Freq fc.freq #define Code fc.code #define Dad dl.dad #define Len dl.len typedef struct static_tree_desc_s static_tree_desc; typedef struct tree_desc_s { ct_data *dyn_tree; /* the dynamic tree */ int max_code; /* largest code with non zero frequency */ static_tree_desc *stat_desc; /* the corresponding static tree */ } FAR tree_desc; typedef ush Pos; typedef Pos FAR Posf; typedef unsigned IPos; /* A Pos is an index in the character window. We use short instead of int to * save space in the various tables. IPos is used only for parameter passing. */ typedef struct internal_state { z_streamp strm; /* pointer back to this zlib stream */ int status; /* as the name implies */ Bytef *pending_buf; /* output still pending */ ulg pending_buf_size; /* size of pending_buf */ Bytef *pending_out; /* next pending byte to output to the stream */ uInt pending; /* nb of bytes in the pending buffer */ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ gz_headerp gzhead; /* gzip header information to write */ uInt gzindex; /* where in extra, name, or comment */ Byte method; /* can only be DEFLATED */ int last_flush; /* value of flush param for previous deflate call */ /* used by deflate.c: */ uInt w_size; /* LZ77 window size (32K by default) */ uInt w_bits; /* log2(w_size) (8..16) */ uInt w_mask; /* w_size - 1 */ Bytef *window; /* Sliding window. Input bytes are read into the second half of the window, * and move to the first half later to keep a dictionary of at least wSize * bytes. With this organization, matches are limited to a distance of * wSize-MAX_MATCH bytes, but this ensures that IO is always * performed with a length multiple of the block size. Also, it limits * the window size to 64K, which is quite useful on MSDOS. * To do: use the user input buffer as sliding window. */ ulg window_size; /* Actual size of window: 2*wSize, except when the user input buffer * is directly used as sliding window. */ Posf *prev; /* Link to older string with same hash index. To limit the size of this * array to 64K, this link is maintained only for the last 32K strings. * An index in this array is thus a window index modulo 32K. */ Posf *head; /* Heads of the hash chains or NIL. */ uInt ins_h; /* hash index of string to be inserted */ uInt hash_size; /* number of elements in hash table */ uInt hash_bits; /* log2(hash_size) */ uInt hash_mask; /* hash_size-1 */ uInt hash_shift; /* Number of bits by which ins_h must be shifted at each input * step. It must be such that after MIN_MATCH steps, the oldest * byte no longer takes part in the hash key, that is: * hash_shift * MIN_MATCH >= hash_bits */ long block_start; /* Window position at the beginning of the current output block. Gets * negative when the window is moved backwards. */ uInt match_length; /* length of best match */ IPos prev_match; /* previous match */ int match_available; /* set if previous match exists */ uInt strstart; /* start of string to insert */ uInt match_start; /* start of matching string */ uInt lookahead; /* number of valid bytes ahead in window */ uInt prev_length; /* Length of the best match at previous step. Matches not greater than this * are discarded. This is used in the lazy match evaluation. */ uInt max_chain_length; /* To speed up deflation, hash chains are never searched beyond this * length. A higher limit improves compression ratio but degrades the * speed. */ uInt max_lazy_match; /* Attempt to find a better match only when the current match is strictly * smaller than this value. This mechanism is used only for compression * levels >= 4. */ # define max_insert_length max_lazy_match /* Insert new strings in the hash table only if the match length is not * greater than this length. This saves time but degrades compression. * max_insert_length is used only for compression levels <= 3. */ int level; /* compression level (1..9) */ int strategy; /* favor or force Huffman coding*/ uInt good_match; /* Use a faster search when the previous match is longer than this */ int nice_match; /* Stop searching when current match exceeds this */ /* used by trees.c: */ /* Didn't use ct_data typedef below to suppress compiler warning */ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ struct tree_desc_s l_desc; /* desc. for literal tree */ struct tree_desc_s d_desc; /* desc. for distance tree */ struct tree_desc_s bl_desc; /* desc. for bit length tree */ ush bl_count[MAX_BITS+1]; /* number of codes at each bit length for an optimal tree */ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ int heap_len; /* number of elements in the heap */ int heap_max; /* element of largest frequency */ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. * The same heap array is used to build all trees. */ uch depth[2*L_CODES+1]; /* Depth of each subtree used as tie breaker for trees of equal frequency */ uchf *l_buf; /* buffer for literals or lengths */ uInt lit_bufsize; /* Size of match buffer for literals/lengths. There are 4 reasons for * limiting lit_bufsize to 64K: * - frequencies can be kept in 16 bit counters * - if compression is not successful for the first block, all input * data is still in the window so we can still emit a stored block even * when input comes from standard input. (This can also be done for * all blocks if lit_bufsize is not greater than 32K.) * - if compression is not successful for a file smaller than 64K, we can * even emit a stored file instead of a stored block (saving 5 bytes). * This is applicable only for zip (not gzip or zlib). * - creating new Huffman trees less frequently may not provide fast * adaptation to changes in the input data statistics. (Take for * example a binary file with poorly compressible code followed by * a highly compressible string table.) Smaller buffer sizes give * fast adaptation but have of course the overhead of transmitting * trees more frequently. * - I can't count above 4 */ uInt last_lit; /* running index in l_buf */ ushf *d_buf; /* Buffer for distances. To simplify the code, d_buf and l_buf have * the same number of elements. To use different lengths, an extra flag * array would be necessary. */ ulg opt_len; /* bit length of current block with optimal trees */ ulg static_len; /* bit length of current block with static trees */ uInt matches; /* number of string matches in current block */ uInt insert; /* bytes at end of window left to insert */ #ifdef DEBUG ulg compressed_len; /* total bit length of compressed file mod 2^32 */ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ #endif ush bi_buf; /* Output buffer. bits are inserted starting at the bottom (least * significant bits). */ int bi_valid; /* Number of valid bits in bi_buf. All bits above the last valid bit * are always zero. */ ulg high_water; /* High water mark offset in window for initialized bytes -- bytes above * this are set to zero in order to avoid memory check warnings when * longest match routines access bytes past the input. This is then * updated to the new high water mark. */ } FAR deflate_state; /* Output a byte on the stream. * IN assertion: there is enough room in pending_buf. */ #define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} #define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) /* Minimum amount of lookahead, except at the end of the input file. * See deflate.c for comments about the MIN_MATCH+1. */ #define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) /* In order to simplify the code, particularly on 16 bit machines, match * distances are limited to MAX_DIST instead of WSIZE. */ #define WIN_INIT MAX_MATCH /* Number of bytes after end of data in window to initialize in order to avoid memory checker errors from longest match routines */ /* in trees.c */ void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, int last)); void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, int last)); #define d_code(dist) \ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) /* Mapping from a distance to a distance code. dist is the distance - 1 and * must not have side effects. _dist_code[256] and _dist_code[257] are never * used. */ #ifndef DEBUG /* Inline versions of _tr_tally for speed: */ #if defined(GEN_TREES_H) || !defined(STDC) extern uch ZLIB_INTERNAL _length_code[]; extern uch ZLIB_INTERNAL _dist_code[]; #else extern const uch ZLIB_INTERNAL _length_code[]; extern const uch ZLIB_INTERNAL _dist_code[]; #endif # define _tr_tally_lit(s, c, flush) \ { uch cc = (c); \ s->d_buf[s->last_lit] = 0; \ s->l_buf[s->last_lit++] = cc; \ s->dyn_ltree[cc].Freq++; \ flush = (s->last_lit == s->lit_bufsize-1); \ } # define _tr_tally_dist(s, distance, length, flush) \ { uch len = (length); \ ush dist = (distance); \ s->d_buf[s->last_lit] = dist; \ s->l_buf[s->last_lit++] = len; \ dist--; \ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ s->dyn_dtree[d_code(dist)].Freq++; \ flush = (s->last_lit == s->lit_bufsize-1); \ } #else # define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) # define _tr_tally_dist(s, distance, length, flush) \ flush = _tr_tally(s, distance, length) #endif #endif /* DEFLATE_H */ PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/gzclose.c000066400000000000000000000012461231437614300236740ustar00rootroot00000000000000/* gzclose.c -- zlib gzclose() function * Copyright (C) 2004, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" /* gzclose() is in a separate file so that it is linked in only if it is used. That way the other gzclose functions can be used instead to avoid linking in unneeded compression or decompression routines. */ int ZEXPORT gzclose(file) gzFile file; { #ifndef NO_GZCOMPRESS gz_statep state; if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); #else return gzclose_r(file); #endif } PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/gzguts.h000066400000000000000000000146301231437614300235570ustar00rootroot00000000000000/* gzguts.h -- zlib internal header definitions for gz* operations * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #ifdef _LARGEFILE64_SOURCE # ifndef _LARGEFILE_SOURCE # define _LARGEFILE_SOURCE 1 # endif # ifdef _FILE_OFFSET_BITS # undef _FILE_OFFSET_BITS # endif #endif #ifdef HAVE_HIDDEN # define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) #else # define ZLIB_INTERNAL #endif #include #include "zlib.h" #ifdef STDC # include # include # include #endif #include #ifdef _WIN32 # include #endif #if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) # include #endif #ifdef WINAPI_FAMILY # define open _open # define read _read # define write _write # define close _close #endif #ifdef NO_DEFLATE /* for compatibility with old definition */ # define NO_GZCOMPRESS #endif #if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) # ifndef HAVE_VSNPRINTF # define HAVE_VSNPRINTF # endif #endif #if defined(__CYGWIN__) # ifndef HAVE_VSNPRINTF # define HAVE_VSNPRINTF # endif #endif #if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410) # ifndef HAVE_VSNPRINTF # define HAVE_VSNPRINTF # endif #endif #ifndef HAVE_VSNPRINTF # ifdef MSDOS /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), but for now we just assume it doesn't. */ # define NO_vsnprintf # endif # ifdef __TURBOC__ # define NO_vsnprintf # endif # ifdef WIN32 /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ # if !defined(vsnprintf) && !defined(NO_vsnprintf) # if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) # define vsnprintf _vsnprintf # endif # endif # endif # ifdef __SASC # define NO_vsnprintf # endif # ifdef VMS # define NO_vsnprintf # endif # ifdef __OS400__ # define NO_vsnprintf # endif # ifdef __MVS__ # define NO_vsnprintf # endif #endif /* unlike snprintf (which is required in C99, yet still not supported by Microsoft more than a decade later!), _snprintf does not guarantee null termination of the result -- however this is only used in gzlib.c where the result is assured to fit in the space provided */ #ifdef _MSC_VER # define snprintf _snprintf #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ /* gz* functions always use library allocation functions */ #ifndef STDC extern voidp malloc OF((uInt size)); extern void free OF((voidpf ptr)); #endif /* get errno and strerror definition */ #if defined UNDER_CE # include # define zstrerror() gz_strwinerror((DWORD)GetLastError()) #else # ifndef NO_STRERROR # include # define zstrerror() strerror(errno) # else # define zstrerror() "stdio error (consult errno)" # endif #endif /* provide prototypes for these when building zlib without LFS */ #if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); #endif /* default memLevel */ #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif /* default i/o buffer size -- double this for output when reading (this and twice this must be able to fit in an unsigned type) */ #define GZBUFSIZE 8192 /* gzip modes, also provide a little integrity check on the passed structure */ #define GZ_NONE 0 #define GZ_READ 7247 #define GZ_WRITE 31153 #define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ /* values for gz_state how */ #define LOOK 0 /* look for a gzip header */ #define COPY 1 /* copy input directly */ #define GZIP 2 /* decompress a gzip stream */ /* internal gzip file state data structure */ typedef struct { /* exposed contents for gzgetc() macro */ struct gzFile_s x; /* "x" for exposed */ /* x.have: number of bytes available at x.next */ /* x.next: next output data to deliver or write */ /* x.pos: current position in uncompressed data */ /* used for both reading and writing */ int mode; /* see gzip modes above */ int fd; /* file descriptor */ char *path; /* path or fd for error messages */ unsigned size; /* buffer size, zero if not allocated yet */ unsigned want; /* requested buffer size, default is GZBUFSIZE */ unsigned char *in; /* input buffer */ unsigned char *out; /* output buffer (double-sized when reading) */ int direct; /* 0 if processing gzip, 1 if transparent */ /* just for reading */ int how; /* 0: get header, 1: copy, 2: decompress */ z_off64_t start; /* where the gzip data started, for rewinding */ int eof; /* true if end of input file reached */ int past; /* true if read requested past end */ /* just for writing */ int level; /* compression level */ int strategy; /* compression strategy */ /* seek request */ z_off64_t skip; /* amount to skip (already rewound if backwards) */ int seek; /* true if seek request pending */ /* error information */ int err; /* error code */ char *msg; /* error message */ /* zlib inflate or deflate stream */ z_stream strm; /* stream structure in-place (not a pointer) */ } gz_state; typedef gz_state FAR *gz_statep; /* shared functions */ void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); #if defined UNDER_CE char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); #endif /* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t value -- needed when comparing unsigned to z_off64_t, which is signed (possible z_off64_t types off_t, off64_t, and long are all signed) */ #ifdef INT_MAX # define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) #else unsigned ZLIB_INTERNAL gz_intmax OF((void)); # define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) #endif PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/gzlib.c000066400000000000000000000400371231437614300233360ustar00rootroot00000000000000/* gzlib.c -- zlib functions common to reading and writing gzip files * Copyright (C) 2004, 2010, 2011, 2012, 2013 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" #if defined(_WIN32) && !defined(__BORLANDC__) # define LSEEK _lseeki64 #else #if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 # define LSEEK lseek64 #else # define LSEEK lseek #endif #endif /* Local functions */ local void gz_reset OF((gz_statep)); local gzFile gz_open OF((const void *, int, const char *)); #if defined UNDER_CE /* Map the Windows error number in ERROR to a locale-dependent error message string and return a pointer to it. Typically, the values for ERROR come from GetLastError. The string pointed to shall not be modified by the application, but may be overwritten by a subsequent call to gz_strwinerror The gz_strwinerror function does not change the current setting of GetLastError. */ char ZLIB_INTERNAL *gz_strwinerror (error) DWORD error; { static char buf[1024]; wchar_t *msgbuf; DWORD lasterr = GetLastError(); DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, error, 0, /* Default language */ (LPVOID)&msgbuf, 0, NULL); if (chars != 0) { /* If there is an \r\n appended, zap it. */ if (chars >= 2 && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { chars -= 2; msgbuf[chars] = 0; } if (chars > sizeof (buf) - 1) { chars = sizeof (buf) - 1; msgbuf[chars] = 0; } wcstombs(buf, msgbuf, chars + 1); LocalFree(msgbuf); } else { sprintf(buf, "unknown win32 error (%ld)", error); } SetLastError(lasterr); return buf; } #endif /* UNDER_CE */ /* Reset gzip file state */ local void gz_reset(state) gz_statep state; { state->x.have = 0; /* no output data available */ if (state->mode == GZ_READ) { /* for reading ... */ state->eof = 0; /* not at end of file */ state->past = 0; /* have not read past end yet */ state->how = LOOK; /* look for gzip header */ } state->seek = 0; /* no seek request pending */ gz_error(state, Z_OK, NULL); /* clear error */ state->x.pos = 0; /* no uncompressed data yet */ state->strm.avail_in = 0; /* no input data yet */ } /* Open a gzip file either by name or file descriptor. */ local gzFile gz_open(path, fd, mode) const void *path; int fd; const char *mode; { gz_statep state; size_t len; int oflag; #ifdef O_CLOEXEC int cloexec = 0; #endif #ifdef O_EXCL int exclusive = 0; #endif /* check input */ if (path == NULL) return NULL; /* allocate gzFile structure to return */ state = (gz_statep)malloc(sizeof(gz_state)); if (state == NULL) return NULL; state->size = 0; /* no buffers allocated yet */ state->want = GZBUFSIZE; /* requested buffer size */ state->msg = NULL; /* no error message yet */ /* interpret mode */ state->mode = GZ_NONE; state->level = Z_DEFAULT_COMPRESSION; state->strategy = Z_DEFAULT_STRATEGY; state->direct = 0; while (*mode) { if (*mode >= '0' && *mode <= '9') state->level = *mode - '0'; else switch (*mode) { case 'r': state->mode = GZ_READ; break; #ifndef NO_GZCOMPRESS case 'w': state->mode = GZ_WRITE; break; case 'a': state->mode = GZ_APPEND; break; #endif case '+': /* can't read and write at the same time */ free(state); return NULL; case 'b': /* ignore -- will request binary anyway */ break; #ifdef O_CLOEXEC case 'e': cloexec = 1; break; #endif #ifdef O_EXCL case 'x': exclusive = 1; break; #endif case 'f': state->strategy = Z_FILTERED; break; case 'h': state->strategy = Z_HUFFMAN_ONLY; break; case 'R': state->strategy = Z_RLE; break; case 'F': state->strategy = Z_FIXED; break; case 'T': state->direct = 1; break; default: /* could consider as an error, but just ignore */ ; } mode++; } /* must provide an "r", "w", or "a" */ if (state->mode == GZ_NONE) { free(state); return NULL; } /* can't force transparent read */ if (state->mode == GZ_READ) { if (state->direct) { free(state); return NULL; } state->direct = 1; /* for empty file */ } /* save the path name for error messages */ #ifdef _WIN32 if (fd == -2) { len = wcstombs(NULL, path, 0); if (len == (size_t)-1) len = 0; } else #endif len = strlen((const char *)path); state->path = (char *)malloc(len + 1); if (state->path == NULL) { free(state); return NULL; } #ifdef _WIN32 if (fd == -2) if (len) wcstombs(state->path, path, len + 1); else *(state->path) = 0; else #endif #if !defined(NO_snprintf) && !defined(NO_vsnprintf) snprintf(state->path, len + 1, "%s", (const char *)path); #else strcpy(state->path, path); #endif /* compute the flags for open() */ oflag = #ifdef O_LARGEFILE O_LARGEFILE | #endif #ifdef O_BINARY O_BINARY | #endif #ifdef O_CLOEXEC (cloexec ? O_CLOEXEC : 0) | #endif (state->mode == GZ_READ ? O_RDONLY : (O_WRONLY | O_CREAT | #ifdef O_EXCL (exclusive ? O_EXCL : 0) | #endif (state->mode == GZ_WRITE ? O_TRUNC : O_APPEND))); /* open the file with the appropriate flags (or just use fd) */ state->fd = fd > -1 ? fd : ( #ifdef _WIN32 fd == -2 ? _wopen(path, oflag, 0666) : #endif open((const char *)path, oflag, 0666)); if (state->fd == -1) { free(state->path); free(state); return NULL; } if (state->mode == GZ_APPEND) state->mode = GZ_WRITE; /* simplify later checks */ /* save the current position for rewinding (only if reading) */ if (state->mode == GZ_READ) { state->start = LSEEK(state->fd, 0, SEEK_CUR); if (state->start == -1) state->start = 0; } /* initialize stream */ gz_reset(state); /* return stream */ return (gzFile)state; } /* -- see zlib.h -- */ gzFile ZEXPORT gzopen(path, mode) const char *path; const char *mode; { return gz_open(path, -1, mode); } /* -- see zlib.h -- */ gzFile ZEXPORT gzopen64(path, mode) const char *path; const char *mode; { return gz_open(path, -1, mode); } /* -- see zlib.h -- */ gzFile ZEXPORT gzdopen(fd, mode) int fd; const char *mode; { char *path; /* identifier for error messages */ gzFile gz; if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL) return NULL; #if !defined(NO_snprintf) && !defined(NO_vsnprintf) snprintf(path, 7 + 3 * sizeof(int), "", fd); /* for debugging */ #else sprintf(path, "", fd); /* for debugging */ #endif gz = gz_open(path, fd, mode); free(path); return gz; } /* -- see zlib.h -- */ #ifdef _WIN32 gzFile ZEXPORT gzopen_w(path, mode) const wchar_t *path; const char *mode; { return gz_open(path, -2, mode); } #endif /* -- see zlib.h -- */ int ZEXPORT gzbuffer(file, size) gzFile file; unsigned size; { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return -1; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return -1; /* make sure we haven't already allocated memory */ if (state->size != 0) return -1; /* check and set requested size */ if (size < 2) size = 2; /* need two bytes to check magic header */ state->want = size; return 0; } /* -- see zlib.h -- */ int ZEXPORT gzrewind(file) gzFile file; { gz_statep state; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; /* check that we're reading and that there's no error */ if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR)) return -1; /* back up and start over */ if (LSEEK(state->fd, state->start, SEEK_SET) == -1) return -1; gz_reset(state); return 0; } /* -- see zlib.h -- */ z_off64_t ZEXPORT gzseek64(file, offset, whence) gzFile file; z_off64_t offset; int whence; { unsigned n; z_off64_t ret; gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return -1; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return -1; /* check that there's no error */ if (state->err != Z_OK && state->err != Z_BUF_ERROR) return -1; /* can only seek from start or relative to current position */ if (whence != SEEK_SET && whence != SEEK_CUR) return -1; /* normalize offset to a SEEK_CUR specification */ if (whence == SEEK_SET) offset -= state->x.pos; else if (state->seek) offset += state->skip; state->seek = 0; /* if within raw area while reading, just go there */ if (state->mode == GZ_READ && state->how == COPY && state->x.pos + offset >= 0) { ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR); if (ret == -1) return -1; state->x.have = 0; state->eof = 0; state->past = 0; state->seek = 0; gz_error(state, Z_OK, NULL); state->strm.avail_in = 0; state->x.pos += offset; return state->x.pos; } /* calculate skip amount, rewinding if needed for back seek when reading */ if (offset < 0) { if (state->mode != GZ_READ) /* writing -- can't go backwards */ return -1; offset += state->x.pos; if (offset < 0) /* before start of file! */ return -1; if (gzrewind(file) == -1) /* rewind, then skip to offset */ return -1; } /* if reading, skip what's in output buffer (one less gzgetc() check) */ if (state->mode == GZ_READ) { n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ? (unsigned)offset : state->x.have; state->x.have -= n; state->x.next += n; state->x.pos += n; offset -= n; } /* request skip (if not zero) */ if (offset) { state->seek = 1; state->skip = offset; } return state->x.pos + offset; } /* -- see zlib.h -- */ z_off_t ZEXPORT gzseek(file, offset, whence) gzFile file; z_off_t offset; int whence; { z_off64_t ret; ret = gzseek64(file, (z_off64_t)offset, whence); return ret == (z_off_t)ret ? (z_off_t)ret : -1; } /* -- see zlib.h -- */ z_off64_t ZEXPORT gztell64(file) gzFile file; { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return -1; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return -1; /* return position */ return state->x.pos + (state->seek ? state->skip : 0); } /* -- see zlib.h -- */ z_off_t ZEXPORT gztell(file) gzFile file; { z_off64_t ret; ret = gztell64(file); return ret == (z_off_t)ret ? (z_off_t)ret : -1; } /* -- see zlib.h -- */ z_off64_t ZEXPORT gzoffset64(file) gzFile file; { z_off64_t offset; gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return -1; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return -1; /* compute and return effective offset in file */ offset = LSEEK(state->fd, 0, SEEK_CUR); if (offset == -1) return -1; if (state->mode == GZ_READ) /* reading */ offset -= state->strm.avail_in; /* don't count buffered input */ return offset; } /* -- see zlib.h -- */ z_off_t ZEXPORT gzoffset(file) gzFile file; { z_off64_t ret; ret = gzoffset64(file); return ret == (z_off_t)ret ? (z_off_t)ret : -1; } /* -- see zlib.h -- */ int ZEXPORT gzeof(file) gzFile file; { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return 0; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return 0; /* return end-of-file state */ return state->mode == GZ_READ ? state->past : 0; } /* -- see zlib.h -- */ const char * ZEXPORT gzerror(file, errnum) gzFile file; int *errnum; { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return NULL; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return NULL; /* return error information */ if (errnum != NULL) *errnum = state->err; return state->err == Z_MEM_ERROR ? "out of memory" : (state->msg == NULL ? "" : state->msg); } /* -- see zlib.h -- */ void ZEXPORT gzclearerr(file) gzFile file; { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return; /* clear error and end-of-file */ if (state->mode == GZ_READ) { state->eof = 0; state->past = 0; } gz_error(state, Z_OK, NULL); } /* Create an error message in allocated memory and set state->err and state->msg accordingly. Free any previous error message already there. Do not try to free or allocate space if the error is Z_MEM_ERROR (out of memory). Simply save the error message as a static string. If there is an allocation failure constructing the error message, then convert the error to out of memory. */ void ZLIB_INTERNAL gz_error(state, err, msg) gz_statep state; int err; const char *msg; { /* free previously allocated message and clear */ if (state->msg != NULL) { if (state->err != Z_MEM_ERROR) free(state->msg); state->msg = NULL; } /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */ if (err != Z_OK && err != Z_BUF_ERROR) state->x.have = 0; /* set error code, and if no message, then done */ state->err = err; if (msg == NULL) return; /* for an out of memory error, return literal string when requested */ if (err == Z_MEM_ERROR) return; /* construct error message with path */ if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) == NULL) { state->err = Z_MEM_ERROR; return; } #if !defined(NO_snprintf) && !defined(NO_vsnprintf) snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, "%s%s%s", state->path, ": ", msg); #else strcpy(state->msg, state->path); strcat(state->msg, ": "); strcat(state->msg, msg); #endif return; } #ifndef INT_MAX /* portably return maximum value for an int (when limits.h presumed not available) -- we need to do this to cover cases where 2's complement not used, since C standard permits 1's complement and sign-bit representations, otherwise we could just use ((unsigned)-1) >> 1 */ unsigned ZLIB_INTERNAL gz_intmax() { unsigned p, q; p = 1; do { q = p; p <<= 1; p++; } while (p > q); return q >> 1; } #endif PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/gzread.c000066400000000000000000000444061231437614300235070ustar00rootroot00000000000000/* gzread.c -- zlib functions for reading gzip files * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" /* Local functions */ local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *)); local int gz_avail OF((gz_statep)); local int gz_look OF((gz_statep)); local int gz_decomp OF((gz_statep)); local int gz_fetch OF((gz_statep)); local int gz_skip OF((gz_statep, z_off64_t)); /* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from state->fd, and update state->eof, state->err, and state->msg as appropriate. This function needs to loop on read(), since read() is not guaranteed to read the number of bytes requested, depending on the type of descriptor. */ local int gz_load(state, buf, len, have) gz_statep state; unsigned char *buf; unsigned len; unsigned *have; { int ret; *have = 0; do { ret = read(state->fd, buf + *have, len - *have); if (ret <= 0) break; *have += ret; } while (*have < len); if (ret < 0) { gz_error(state, Z_ERRNO, zstrerror()); return -1; } if (ret == 0) state->eof = 1; return 0; } /* Load up input buffer and set eof flag if last data loaded -- return -1 on error, 0 otherwise. Note that the eof flag is set when the end of the input file is reached, even though there may be unused data in the buffer. Once that data has been used, no more attempts will be made to read the file. If strm->avail_in != 0, then the current data is moved to the beginning of the input buffer, and then the remainder of the buffer is loaded with the available data from the input file. */ local int gz_avail(state) gz_statep state; { unsigned got; z_streamp strm = &(state->strm); if (state->err != Z_OK && state->err != Z_BUF_ERROR) return -1; if (state->eof == 0) { if (strm->avail_in) { /* copy what's there to the start */ unsigned char *p = state->in; unsigned const char *q = strm->next_in; unsigned n = strm->avail_in; do { *p++ = *q++; } while (--n); } if (gz_load(state, state->in + strm->avail_in, state->size - strm->avail_in, &got) == -1) return -1; strm->avail_in += got; strm->next_in = state->in; } return 0; } /* Look for gzip header, set up for inflate or copy. state->x.have must be 0. If this is the first time in, allocate required memory. state->how will be left unchanged if there is no more input data available, will be set to COPY if there is no gzip header and direct copying will be performed, or it will be set to GZIP for decompression. If direct copying, then leftover input data from the input buffer will be copied to the output buffer. In that case, all further file reads will be directly to either the output buffer or a user buffer. If decompressing, the inflate state will be initialized. gz_look() will return 0 on success or -1 on failure. */ local int gz_look(state) gz_statep state; { z_streamp strm = &(state->strm); /* allocate read buffers and inflate memory */ if (state->size == 0) { /* allocate buffers */ state->in = (unsigned char *)malloc(state->want); state->out = (unsigned char *)malloc(state->want << 1); if (state->in == NULL || state->out == NULL) { if (state->out != NULL) free(state->out); if (state->in != NULL) free(state->in); gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } state->size = state->want; /* allocate inflate memory */ state->strm.zalloc = Z_NULL; state->strm.zfree = Z_NULL; state->strm.opaque = Z_NULL; state->strm.avail_in = 0; state->strm.next_in = Z_NULL; if (inflateInit2(&(state->strm), 15 + 16) != Z_OK) { /* gunzip */ free(state->out); free(state->in); state->size = 0; gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } } /* get at least the magic bytes in the input buffer */ if (strm->avail_in < 2) { if (gz_avail(state) == -1) return -1; if (strm->avail_in == 0) return 0; } /* look for gzip magic bytes -- if there, do gzip decoding (note: there is a logical dilemma here when considering the case of a partially written gzip file, to wit, if a single 31 byte is written, then we cannot tell whether this is a single-byte file, or just a partially written gzip file -- for here we assume that if a gzip file is being written, then the header will be written in a single operation, so that reading a single byte is sufficient indication that it is not a gzip file) */ if (strm->avail_in > 1 && strm->next_in[0] == 31 && strm->next_in[1] == 139) { inflateReset(strm); state->how = GZIP; state->direct = 0; return 0; } /* no gzip header -- if we were decoding gzip before, then this is trailing garbage. Ignore the trailing garbage and finish. */ if (state->direct == 0) { strm->avail_in = 0; state->eof = 1; state->x.have = 0; return 0; } /* doing raw i/o, copy any leftover input to output -- this assumes that the output buffer is larger than the input buffer, which also assures space for gzungetc() */ state->x.next = state->out; if (strm->avail_in) { memcpy(state->x.next, strm->next_in, strm->avail_in); state->x.have = strm->avail_in; strm->avail_in = 0; } state->how = COPY; state->direct = 1; return 0; } /* Decompress from input to the provided next_out and avail_out in the state. On return, state->x.have and state->x.next point to the just decompressed data. If the gzip stream completes, state->how is reset to LOOK to look for the next gzip stream or raw data, once state->x.have is depleted. Returns 0 on success, -1 on failure. */ local int gz_decomp(state) gz_statep state; { int ret = Z_OK; unsigned had; z_streamp strm = &(state->strm); /* fill output buffer up to end of deflate stream */ had = strm->avail_out; do { /* get more input for inflate() */ if (strm->avail_in == 0 && gz_avail(state) == -1) return -1; if (strm->avail_in == 0) { gz_error(state, Z_BUF_ERROR, "unexpected end of file"); break; } /* decompress and handle errors */ ret = inflate(strm, Z_NO_FLUSH); if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { gz_error(state, Z_STREAM_ERROR, "internal error: inflate stream corrupt"); return -1; } if (ret == Z_MEM_ERROR) { gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ gz_error(state, Z_DATA_ERROR, strm->msg == NULL ? "compressed data error" : strm->msg); return -1; } } while (strm->avail_out && ret != Z_STREAM_END); /* update available output */ state->x.have = had - strm->avail_out; state->x.next = strm->next_out - state->x.have; /* if the gzip stream completed successfully, look for another */ if (ret == Z_STREAM_END) state->how = LOOK; /* good decompression */ return 0; } /* Fetch data and put it in the output buffer. Assumes state->x.have is 0. Data is either copied from the input file or decompressed from the input file depending on state->how. If state->how is LOOK, then a gzip header is looked for to determine whether to copy or decompress. Returns -1 on error, otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the end of the input file has been reached and all data has been processed. */ local int gz_fetch(state) gz_statep state; { z_streamp strm = &(state->strm); do { switch(state->how) { case LOOK: /* -> LOOK, COPY (only if never GZIP), or GZIP */ if (gz_look(state) == -1) return -1; if (state->how == LOOK) return 0; break; case COPY: /* -> COPY */ if (gz_load(state, state->out, state->size << 1, &(state->x.have)) == -1) return -1; state->x.next = state->out; return 0; case GZIP: /* -> GZIP or LOOK (if end of gzip stream) */ strm->avail_out = state->size << 1; strm->next_out = state->out; if (gz_decomp(state) == -1) return -1; } } while (state->x.have == 0 && (!state->eof || strm->avail_in)); return 0; } /* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ local int gz_skip(state, len) gz_statep state; z_off64_t len; { unsigned n; /* skip over len bytes or reach end-of-file, whichever comes first */ while (len) /* skip over whatever is in output buffer */ if (state->x.have) { n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > len ? (unsigned)len : state->x.have; state->x.have -= n; state->x.next += n; state->x.pos += n; len -= n; } /* output buffer empty -- return if we're at the end of the input */ else if (state->eof && state->strm.avail_in == 0) break; /* need more data to skip -- load up output buffer */ else { /* get more output, looking for header if required */ if (gz_fetch(state) == -1) return -1; } return 0; } /* -- see zlib.h -- */ int ZEXPORT gzread(file, buf, len) gzFile file; voidp buf; unsigned len; { unsigned got, n; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; strm = &(state->strm); /* check that we're reading and that there's no (serious) error */ if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR)) return -1; /* since an int is returned, make sure len fits in one, otherwise return with an error (this avoids the flaw in the interface) */ if ((int)len < 0) { gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); return -1; } /* if len is zero, avoid unnecessary operations */ if (len == 0) return 0; /* process a skip request */ if (state->seek) { state->seek = 0; if (gz_skip(state, state->skip) == -1) return -1; } /* get len bytes to buf, or less than len if at the end */ got = 0; do { /* first just try copying data from the output buffer */ if (state->x.have) { n = state->x.have > len ? len : state->x.have; memcpy(buf, state->x.next, n); state->x.next += n; state->x.have -= n; } /* output buffer empty -- return if we're at the end of the input */ else if (state->eof && strm->avail_in == 0) { state->past = 1; /* tried to read past end */ break; } /* need output data -- for small len or new stream load up our output buffer */ else if (state->how == LOOK || len < (state->size << 1)) { /* get more output, looking for header if required */ if (gz_fetch(state) == -1) return -1; continue; /* no progress yet -- go back to copy above */ /* the copy above assures that we will leave with space in the output buffer, allowing at least one gzungetc() to succeed */ } /* large len -- read directly into user buffer */ else if (state->how == COPY) { /* read directly */ if (gz_load(state, (unsigned char *)buf, len, &n) == -1) return -1; } /* large len -- decompress directly into user buffer */ else { /* state->how == GZIP */ strm->avail_out = len; strm->next_out = (unsigned char *)buf; if (gz_decomp(state) == -1) return -1; n = state->x.have; state->x.have = 0; } /* update progress */ len -= n; buf = (char *)buf + n; got += n; state->x.pos += n; } while (len); /* return number of bytes read into user buffer (will fit in int) */ return (int)got; } /* -- see zlib.h -- */ #ifdef Z_PREFIX_SET # undef z_gzgetc #else # undef gzgetc #endif int ZEXPORT gzgetc(file) gzFile file; { int ret; unsigned char buf[1]; gz_statep state; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; /* check that we're reading and that there's no (serious) error */ if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR)) return -1; /* try output buffer (no need to check for skip request) */ if (state->x.have) { state->x.have--; state->x.pos++; return *(state->x.next)++; } /* nothing there -- try gzread() */ ret = gzread(file, buf, 1); return ret < 1 ? -1 : buf[0]; } int ZEXPORT gzgetc_(file) gzFile file; { return gzgetc(file); } /* -- see zlib.h -- */ int ZEXPORT gzungetc(c, file) int c; gzFile file; { gz_statep state; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; /* check that we're reading and that there's no (serious) error */ if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR)) return -1; /* process a skip request */ if (state->seek) { state->seek = 0; if (gz_skip(state, state->skip) == -1) return -1; } /* can't push EOF */ if (c < 0) return -1; /* if output buffer empty, put byte at end (allows more pushing) */ if (state->x.have == 0) { state->x.have = 1; state->x.next = state->out + (state->size << 1) - 1; state->x.next[0] = c; state->x.pos--; state->past = 0; return c; } /* if no room, give up (must have already done a gzungetc()) */ if (state->x.have == (state->size << 1)) { gz_error(state, Z_DATA_ERROR, "out of room to push characters"); return -1; } /* slide output data if needed and insert byte before existing data */ if (state->x.next == state->out) { unsigned char *src = state->out + state->x.have; unsigned char *dest = state->out + (state->size << 1); while (src > state->out) *--dest = *--src; state->x.next = dest; } state->x.have++; state->x.next--; state->x.next[0] = c; state->x.pos--; state->past = 0; return c; } /* -- see zlib.h -- */ char * ZEXPORT gzgets(file, buf, len) gzFile file; char *buf; int len; { unsigned left, n; char *str; unsigned char *eol; gz_statep state; /* check parameters and get internal structure */ if (file == NULL || buf == NULL || len < 1) return NULL; state = (gz_statep)file; /* check that we're reading and that there's no (serious) error */ if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR)) return NULL; /* process a skip request */ if (state->seek) { state->seek = 0; if (gz_skip(state, state->skip) == -1) return NULL; } /* copy output bytes up to new line or len - 1, whichever comes first -- append a terminating zero to the string (we don't check for a zero in the contents, let the user worry about that) */ str = buf; left = (unsigned)len - 1; if (left) do { /* assure that something is in the output buffer */ if (state->x.have == 0 && gz_fetch(state) == -1) return NULL; /* error */ if (state->x.have == 0) { /* end of file */ state->past = 1; /* read past end */ break; /* return what we have */ } /* look for end-of-line in current output buffer */ n = state->x.have > left ? left : state->x.have; eol = (unsigned char *)memchr(state->x.next, '\n', n); if (eol != NULL) n = (unsigned)(eol - state->x.next) + 1; /* copy through end-of-line, or remainder if not found */ memcpy(buf, state->x.next, n); state->x.have -= n; state->x.next += n; state->x.pos += n; left -= n; buf += n; } while (left && eol == NULL); /* return terminated string, or if nothing, end of file */ if (buf == str) return NULL; buf[0] = 0; return str; } /* -- see zlib.h -- */ int ZEXPORT gzdirect(file) gzFile file; { gz_statep state; /* get internal structure */ if (file == NULL) return 0; state = (gz_statep)file; /* if the state is not known, but we can find out, then do so (this is mainly for right after a gzopen() or gzdopen()) */ if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) (void)gz_look(state); /* return 1 if transparent, 0 if processing a gzip stream */ return state->direct; } /* -- see zlib.h -- */ int ZEXPORT gzclose_r(file) gzFile file; { int ret, err; gz_statep state; /* get internal structure */ if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; /* check that we're reading */ if (state->mode != GZ_READ) return Z_STREAM_ERROR; /* free memory and close file */ if (state->size) { inflateEnd(&(state->strm)); free(state->out); free(state->in); } err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK; gz_error(state, Z_OK, NULL); free(state->path); ret = close(state->fd); free(state); return ret ? Z_ERRNO : err; } PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/gzwrite.c000066400000000000000000000375071231437614300237320ustar00rootroot00000000000000/* gzwrite.c -- zlib functions for writing gzip files * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" /* Local functions */ local int gz_init OF((gz_statep)); local int gz_comp OF((gz_statep, int)); local int gz_zero OF((gz_statep, z_off64_t)); /* Initialize state for writing a gzip file. Mark initialization by setting state->size to non-zero. Return -1 on failure or 0 on success. */ local int gz_init(state) gz_statep state; { int ret; z_streamp strm = &(state->strm); /* allocate input buffer */ state->in = (unsigned char *)malloc(state->want); if (state->in == NULL) { gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } /* only need output buffer and deflate state if compressing */ if (!state->direct) { /* allocate output buffer */ state->out = (unsigned char *)malloc(state->want); if (state->out == NULL) { free(state->in); gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } /* allocate deflate memory, set up for gzip compression */ strm->zalloc = Z_NULL; strm->zfree = Z_NULL; strm->opaque = Z_NULL; ret = deflateInit2(strm, state->level, Z_DEFLATED, MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy); if (ret != Z_OK) { free(state->out); free(state->in); gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } } /* mark state as initialized */ state->size = state->want; /* initialize write buffer if compressing */ if (!state->direct) { strm->avail_out = state->size; strm->next_out = state->out; state->x.next = strm->next_out; } return 0; } /* Compress whatever is at avail_in and next_in and write to the output file. Return -1 if there is an error writing to the output file, otherwise 0. flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, then the deflate() state is reset to start a new gzip stream. If gz->direct is true, then simply write to the output file without compressing, and ignore flush. */ local int gz_comp(state, flush) gz_statep state; int flush; { int ret, got; unsigned have; z_streamp strm = &(state->strm); /* allocate memory if this is the first time through */ if (state->size == 0 && gz_init(state) == -1) return -1; /* write directly if requested */ if (state->direct) { got = write(state->fd, strm->next_in, strm->avail_in); if (got < 0 || (unsigned)got != strm->avail_in) { gz_error(state, Z_ERRNO, zstrerror()); return -1; } strm->avail_in = 0; return 0; } /* run deflate() on provided input until it produces no more output */ ret = Z_OK; do { /* write out current buffer contents if full, or if flushing, but if doing Z_FINISH then don't write until we get to Z_STREAM_END */ if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && (flush != Z_FINISH || ret == Z_STREAM_END))) { have = (unsigned)(strm->next_out - state->x.next); if (have && ((got = write(state->fd, state->x.next, have)) < 0 || (unsigned)got != have)) { gz_error(state, Z_ERRNO, zstrerror()); return -1; } if (strm->avail_out == 0) { strm->avail_out = state->size; strm->next_out = state->out; } state->x.next = strm->next_out; } /* compress */ have = strm->avail_out; ret = deflate(strm, flush); if (ret == Z_STREAM_ERROR) { gz_error(state, Z_STREAM_ERROR, "internal error: deflate stream corrupt"); return -1; } have -= strm->avail_out; } while (have); /* if that completed a deflate stream, allow another to start */ if (flush == Z_FINISH) deflateReset(strm); /* all done, no errors */ return 0; } /* Compress len zeros to output. Return -1 on error, 0 on success. */ local int gz_zero(state, len) gz_statep state; z_off64_t len; { int first; unsigned n; z_streamp strm = &(state->strm); /* consume whatever's left in the input buffer */ if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) return -1; /* compress len zeros (len guaranteed > 0) */ first = 1; while (len) { n = GT_OFF(state->size) || (z_off64_t)state->size > len ? (unsigned)len : state->size; if (first) { memset(state->in, 0, n); first = 0; } strm->avail_in = n; strm->next_in = state->in; state->x.pos += n; if (gz_comp(state, Z_NO_FLUSH) == -1) return -1; len -= n; } return 0; } /* -- see zlib.h -- */ int ZEXPORT gzwrite(file, buf, len) gzFile file; voidpc buf; unsigned len; { unsigned put = len; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return 0; state = (gz_statep)file; strm = &(state->strm); /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return 0; /* since an int is returned, make sure len fits in one, otherwise return with an error (this avoids the flaw in the interface) */ if ((int)len < 0) { gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); return 0; } /* if len is zero, avoid unnecessary operations */ if (len == 0) return 0; /* allocate memory if this is the first time through */ if (state->size == 0 && gz_init(state) == -1) return 0; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return 0; } /* for small len, copy to input buffer, otherwise compress directly */ if (len < state->size) { /* copy to input buffer, compress when full */ do { unsigned have, copy; if (strm->avail_in == 0) strm->next_in = state->in; have = (unsigned)((strm->next_in + strm->avail_in) - state->in); copy = state->size - have; if (copy > len) copy = len; memcpy(state->in + have, buf, copy); strm->avail_in += copy; state->x.pos += copy; buf = (const char *)buf + copy; len -= copy; if (len && gz_comp(state, Z_NO_FLUSH) == -1) return 0; } while (len); } else { /* consume whatever's left in the input buffer */ if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) return 0; /* directly compress user buffer to file */ strm->avail_in = len; strm->next_in = (z_const Bytef *)buf; state->x.pos += len; if (gz_comp(state, Z_NO_FLUSH) == -1) return 0; } /* input was all buffered or compressed (put will fit in int) */ return (int)put; } /* -- see zlib.h -- */ int ZEXPORT gzputc(file, c) gzFile file; int c; { unsigned have; unsigned char buf[1]; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; strm = &(state->strm); /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return -1; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return -1; } /* try writing to input buffer for speed (state->size == 0 if buffer not initialized) */ if (state->size) { if (strm->avail_in == 0) strm->next_in = state->in; have = (unsigned)((strm->next_in + strm->avail_in) - state->in); if (have < state->size) { state->in[have] = c; strm->avail_in++; state->x.pos++; return c & 0xff; } } /* no room in buffer or not initialized, use gz_write() */ buf[0] = c; if (gzwrite(file, buf, 1) != 1) return -1; return c & 0xff; } /* -- see zlib.h -- */ int ZEXPORT gzputs(file, str) gzFile file; const char *str; { int ret; unsigned len; /* write string */ len = (unsigned)strlen(str); ret = gzwrite(file, str, len); return ret == 0 && len != 0 ? -1 : ret; } #if defined(STDC) || defined(Z_HAVE_STDARG_H) #include /* -- see zlib.h -- */ int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) { int size, len; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; strm = &(state->strm); /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return 0; /* make sure we have some buffer space */ if (state->size == 0 && gz_init(state) == -1) return 0; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return 0; } /* consume whatever's left in the input buffer */ if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) return 0; /* do the printf() into the input buffer, put length in len */ size = (int)(state->size); state->in[size - 1] = 0; #ifdef NO_vsnprintf # ifdef HAS_vsprintf_void (void)vsprintf((char *)(state->in), format, va); for (len = 0; len < size; len++) if (state->in[len] == 0) break; # else len = vsprintf((char *)(state->in), format, va); # endif #else # ifdef HAS_vsnprintf_void (void)vsnprintf((char *)(state->in), size, format, va); len = strlen((char *)(state->in)); # else len = vsnprintf((char *)(state->in), size, format, va); # endif #endif /* check that printf() results fit in buffer */ if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) return 0; /* update buffer and position, defer compression until needed */ strm->avail_in = (unsigned)len; strm->next_in = state->in; state->x.pos += len; return len; } int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) { va_list va; int ret; va_start(va, format); ret = gzvprintf(file, format, va); va_end(va); return ret; } #else /* !STDC && !Z_HAVE_STDARG_H */ /* -- see zlib.h -- */ int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) gzFile file; const char *format; int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; { int size, len; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; strm = &(state->strm); /* check that can really pass pointer in ints */ if (sizeof(int) != sizeof(void *)) return 0; /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return 0; /* make sure we have some buffer space */ if (state->size == 0 && gz_init(state) == -1) return 0; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return 0; } /* consume whatever's left in the input buffer */ if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) return 0; /* do the printf() into the input buffer, put length in len */ size = (int)(state->size); state->in[size - 1] = 0; #ifdef NO_snprintf # ifdef HAS_sprintf_void sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); for (len = 0; len < size; len++) if (state->in[len] == 0) break; # else len = sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); # endif #else # ifdef HAS_snprintf_void snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); len = strlen((char *)(state->in)); # else len = snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); # endif #endif /* check that printf() results fit in buffer */ if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) return 0; /* update buffer and position, defer compression until needed */ strm->avail_in = (unsigned)len; strm->next_in = state->in; state->x.pos += len; return len; } #endif /* -- see zlib.h -- */ int ZEXPORT gzflush(file, flush) gzFile file; int flush; { gz_statep state; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return Z_STREAM_ERROR; /* check flush parameter */ if (flush < 0 || flush > Z_FINISH) return Z_STREAM_ERROR; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return -1; } /* compress remaining data with requested flush */ gz_comp(state, flush); return state->err; } /* -- see zlib.h -- */ int ZEXPORT gzsetparams(file, level, strategy) gzFile file; int level; int strategy; { gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; strm = &(state->strm); /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return Z_STREAM_ERROR; /* if no change is requested, then do nothing */ if (level == state->level && strategy == state->strategy) return Z_OK; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return -1; } /* change compression parameters for subsequent input */ if (state->size) { /* flush previous input with previous parameters before changing */ if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1) return state->err; deflateParams(strm, level, strategy); } state->level = level; state->strategy = strategy; return Z_OK; } /* -- see zlib.h -- */ int ZEXPORT gzclose_w(file) gzFile file; { int ret = Z_OK; gz_statep state; /* get internal structure */ if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; /* check that we're writing */ if (state->mode != GZ_WRITE) return Z_STREAM_ERROR; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) ret = state->err; } /* flush, free memory, and close file */ if (gz_comp(state, Z_FINISH) == -1) ret = state->err; if (state->size) { if (!state->direct) { (void)deflateEnd(&(state->strm)); free(state->out); } free(state->in); } gz_error(state, Z_OK, NULL); free(state->path); if (close(state->fd) == -1) ret = Z_ERRNO; free(state); return ret; } PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/infback.c000066400000000000000000000542651231437614300236340ustar00rootroot00000000000000/* infback.c -- inflate using a call-back interface * Copyright (C) 1995-2011 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* This code is largely copied from inflate.c. Normally either infback.o or inflate.o would be linked into an application--not both. The interface with inffast.c is retained so that optimized assembler-coded versions of inflate_fast() can be used with either inflate.c or infback.c. */ #include "zutil.h" #include "inftrees.h" #include "inflate.h" #include "inffast.h" /* function prototypes */ local void fixedtables OF((struct inflate_state FAR *state)); /* strm provides memory allocation functions in zalloc and zfree, or Z_NULL to use the library memory allocation functions. windowBits is in the range 8..15, and window is a user-supplied window and output buffer that is 2**windowBits bytes. */ int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) z_streamp strm; int windowBits; unsigned char FAR *window; const char *version; int stream_size; { struct inflate_state FAR *state; if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != (int)(sizeof(z_stream))) return Z_VERSION_ERROR; if (strm == Z_NULL || window == Z_NULL || windowBits < 8 || windowBits > 15) return Z_STREAM_ERROR; strm->msg = Z_NULL; /* in case we return an error */ if (strm->zalloc == (alloc_func)0) { #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zalloc = zcalloc; strm->opaque = (voidpf)0; #endif } if (strm->zfree == (free_func)0) #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zfree = zcfree; #endif state = (struct inflate_state FAR *)ZALLOC(strm, 1, sizeof(struct inflate_state)); if (state == Z_NULL) return Z_MEM_ERROR; Tracev((stderr, "inflate: allocated\n")); strm->state = (struct internal_state FAR *)state; state->dmax = 32768U; state->wbits = windowBits; state->wsize = 1U << windowBits; state->window = window; state->wnext = 0; state->whave = 0; return Z_OK; } /* Return state with length and distance decoding tables and index sizes set to fixed code decoding. Normally this returns fixed tables from inffixed.h. If BUILDFIXED is defined, then instead this routine builds the tables the first time it's called, and returns those tables the first time and thereafter. This reduces the size of the code by about 2K bytes, in exchange for a little execution time. However, BUILDFIXED should not be used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ local void fixedtables(state) struct inflate_state FAR *state; { #ifdef BUILDFIXED static int virgin = 1; static code *lenfix, *distfix; static code fixed[544]; /* build fixed huffman tables if first call (may not be thread safe) */ if (virgin) { unsigned sym, bits; static code *next; /* literal/length table */ sym = 0; while (sym < 144) state->lens[sym++] = 8; while (sym < 256) state->lens[sym++] = 9; while (sym < 280) state->lens[sym++] = 7; while (sym < 288) state->lens[sym++] = 8; next = fixed; lenfix = next; bits = 9; inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); /* distance table */ sym = 0; while (sym < 32) state->lens[sym++] = 5; distfix = next; bits = 5; inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); /* do this just once */ virgin = 0; } #else /* !BUILDFIXED */ # include "inffixed.h" #endif /* BUILDFIXED */ state->lencode = lenfix; state->lenbits = 9; state->distcode = distfix; state->distbits = 5; } /* Macros for inflateBack(): */ /* Load returned state from inflate_fast() */ #define LOAD() \ do { \ put = strm->next_out; \ left = strm->avail_out; \ next = strm->next_in; \ have = strm->avail_in; \ hold = state->hold; \ bits = state->bits; \ } while (0) /* Set state from registers for inflate_fast() */ #define RESTORE() \ do { \ strm->next_out = put; \ strm->avail_out = left; \ strm->next_in = next; \ strm->avail_in = have; \ state->hold = hold; \ state->bits = bits; \ } while (0) /* Clear the input bit accumulator */ #define INITBITS() \ do { \ hold = 0; \ bits = 0; \ } while (0) /* Assure that some input is available. If input is requested, but denied, then return a Z_BUF_ERROR from inflateBack(). */ #define PULL() \ do { \ if (have == 0) { \ have = in(in_desc, &next); \ if (have == 0) { \ next = Z_NULL; \ ret = Z_BUF_ERROR; \ goto inf_leave; \ } \ } \ } while (0) /* Get a byte of input into the bit accumulator, or return from inflateBack() with an error if there is no input available. */ #define PULLBYTE() \ do { \ PULL(); \ have--; \ hold += (unsigned long)(*next++) << bits; \ bits += 8; \ } while (0) /* Assure that there are at least n bits in the bit accumulator. If there is not enough available input to do that, then return from inflateBack() with an error. */ #define NEEDBITS(n) \ do { \ while (bits < (unsigned)(n)) \ PULLBYTE(); \ } while (0) /* Return the low n bits of the bit accumulator (n < 16) */ #define BITS(n) \ ((unsigned)hold & ((1U << (n)) - 1)) /* Remove n bits from the bit accumulator */ #define DROPBITS(n) \ do { \ hold >>= (n); \ bits -= (unsigned)(n); \ } while (0) /* Remove zero to seven bits as needed to go to a byte boundary */ #define BYTEBITS() \ do { \ hold >>= bits & 7; \ bits -= bits & 7; \ } while (0) /* Assure that some output space is available, by writing out the window if it's full. If the write fails, return from inflateBack() with a Z_BUF_ERROR. */ #define ROOM() \ do { \ if (left == 0) { \ put = state->window; \ left = state->wsize; \ state->whave = left; \ if (out(out_desc, put, left)) { \ ret = Z_BUF_ERROR; \ goto inf_leave; \ } \ } \ } while (0) /* strm provides the memory allocation functions and window buffer on input, and provides information on the unused input on return. For Z_DATA_ERROR returns, strm will also provide an error message. in() and out() are the call-back input and output functions. When inflateBack() needs more input, it calls in(). When inflateBack() has filled the window with output, or when it completes with data in the window, it calls out() to write out the data. The application must not change the provided input until in() is called again or inflateBack() returns. The application must not change the window/output buffer until inflateBack() returns. in() and out() are called with a descriptor parameter provided in the inflateBack() call. This parameter can be a structure that provides the information required to do the read or write, as well as accumulated information on the input and output such as totals and check values. in() should return zero on failure. out() should return non-zero on failure. If either in() or out() fails, than inflateBack() returns a Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it was in() or out() that caused in the error. Otherwise, inflateBack() returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format error, or Z_MEM_ERROR if it could not allocate memory for the state. inflateBack() can also return Z_STREAM_ERROR if the input parameters are not correct, i.e. strm is Z_NULL or the state was not initialized. */ int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) z_streamp strm; in_func in; void FAR *in_desc; out_func out; void FAR *out_desc; { struct inflate_state FAR *state; z_const unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ unsigned have, left; /* available input and output */ unsigned long hold; /* bit buffer */ unsigned bits; /* bits in bit buffer */ unsigned copy; /* number of stored or match bytes to copy */ unsigned char FAR *from; /* where to copy match bytes from */ code here; /* current decoding table entry */ code last; /* parent table entry */ unsigned len; /* length to copy for repeats, bits to drop */ int ret; /* return code */ static const unsigned short order[19] = /* permutation of code lengths */ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; /* Check that the strm exists and that the state was initialized */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* Reset the state */ strm->msg = Z_NULL; state->mode = TYPE; state->last = 0; state->whave = 0; next = strm->next_in; have = next != Z_NULL ? strm->avail_in : 0; hold = 0; bits = 0; put = state->window; left = state->wsize; /* Inflate until end of block marked as last */ for (;;) switch (state->mode) { case TYPE: /* determine and dispatch block type */ if (state->last) { BYTEBITS(); state->mode = DONE; break; } NEEDBITS(3); state->last = BITS(1); DROPBITS(1); switch (BITS(2)) { case 0: /* stored block */ Tracev((stderr, "inflate: stored block%s\n", state->last ? " (last)" : "")); state->mode = STORED; break; case 1: /* fixed block */ fixedtables(state); Tracev((stderr, "inflate: fixed codes block%s\n", state->last ? " (last)" : "")); state->mode = LEN; /* decode codes */ break; case 2: /* dynamic block */ Tracev((stderr, "inflate: dynamic codes block%s\n", state->last ? " (last)" : "")); state->mode = TABLE; break; case 3: strm->msg = (char *)"invalid block type"; state->mode = BAD; } DROPBITS(2); break; case STORED: /* get and verify stored block length */ BYTEBITS(); /* go to byte boundary */ NEEDBITS(32); if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { strm->msg = (char *)"invalid stored block lengths"; state->mode = BAD; break; } state->length = (unsigned)hold & 0xffff; Tracev((stderr, "inflate: stored length %u\n", state->length)); INITBITS(); /* copy stored block from input to output */ while (state->length != 0) { copy = state->length; PULL(); ROOM(); if (copy > have) copy = have; if (copy > left) copy = left; zmemcpy(put, next, copy); have -= copy; next += copy; left -= copy; put += copy; state->length -= copy; } Tracev((stderr, "inflate: stored end\n")); state->mode = TYPE; break; case TABLE: /* get dynamic table entries descriptor */ NEEDBITS(14); state->nlen = BITS(5) + 257; DROPBITS(5); state->ndist = BITS(5) + 1; DROPBITS(5); state->ncode = BITS(4) + 4; DROPBITS(4); #ifndef PKZIP_BUG_WORKAROUND if (state->nlen > 286 || state->ndist > 30) { strm->msg = (char *)"too many length or distance symbols"; state->mode = BAD; break; } #endif Tracev((stderr, "inflate: table sizes ok\n")); /* get code length code lengths (not a typo) */ state->have = 0; while (state->have < state->ncode) { NEEDBITS(3); state->lens[order[state->have++]] = (unsigned short)BITS(3); DROPBITS(3); } while (state->have < 19) state->lens[order[state->have++]] = 0; state->next = state->codes; state->lencode = (code const FAR *)(state->next); state->lenbits = 7; ret = inflate_table(CODES, state->lens, 19, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid code lengths set"; state->mode = BAD; break; } Tracev((stderr, "inflate: code lengths ok\n")); /* get length and distance code code lengths */ state->have = 0; while (state->have < state->nlen + state->ndist) { for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.val < 16) { DROPBITS(here.bits); state->lens[state->have++] = here.val; } else { if (here.val == 16) { NEEDBITS(here.bits + 2); DROPBITS(here.bits); if (state->have == 0) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } len = (unsigned)(state->lens[state->have - 1]); copy = 3 + BITS(2); DROPBITS(2); } else if (here.val == 17) { NEEDBITS(here.bits + 3); DROPBITS(here.bits); len = 0; copy = 3 + BITS(3); DROPBITS(3); } else { NEEDBITS(here.bits + 7); DROPBITS(here.bits); len = 0; copy = 11 + BITS(7); DROPBITS(7); } if (state->have + copy > state->nlen + state->ndist) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } while (copy--) state->lens[state->have++] = (unsigned short)len; } } /* handle error breaks in while */ if (state->mode == BAD) break; /* check for end-of-block code (better have one) */ if (state->lens[256] == 0) { strm->msg = (char *)"invalid code -- missing end-of-block"; state->mode = BAD; break; } /* build code tables -- note: do not change the lenbits or distbits values here (9 and 6) without reading the comments in inftrees.h concerning the ENOUGH constants, which depend on those values */ state->next = state->codes; state->lencode = (code const FAR *)(state->next); state->lenbits = 9; ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid literal/lengths set"; state->mode = BAD; break; } state->distcode = (code const FAR *)(state->next); state->distbits = 6; ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, &(state->next), &(state->distbits), state->work); if (ret) { strm->msg = (char *)"invalid distances set"; state->mode = BAD; break; } Tracev((stderr, "inflate: codes ok\n")); state->mode = LEN; case LEN: /* use inflate_fast() if we have enough input and output */ if (have >= 6 && left >= 258) { RESTORE(); if (state->whave < state->wsize) state->whave = state->wsize - left; inflate_fast(strm, state->wsize); LOAD(); break; } /* get a literal, length, or end-of-block code */ for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.op && (here.op & 0xf0) == 0) { last = here; for (;;) { here = state->lencode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); } DROPBITS(here.bits); state->length = (unsigned)here.val; /* process literal */ if (here.op == 0) { Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here.val)); ROOM(); *put++ = (unsigned char)(state->length); left--; state->mode = LEN; break; } /* process end of block */ if (here.op & 32) { Tracevv((stderr, "inflate: end of block\n")); state->mode = TYPE; break; } /* invalid code */ if (here.op & 64) { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } /* length code -- get extra bits, if any */ state->extra = (unsigned)(here.op) & 15; if (state->extra != 0) { NEEDBITS(state->extra); state->length += BITS(state->extra); DROPBITS(state->extra); } Tracevv((stderr, "inflate: length %u\n", state->length)); /* get distance code */ for (;;) { here = state->distcode[BITS(state->distbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if ((here.op & 0xf0) == 0) { last = here; for (;;) { here = state->distcode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); } DROPBITS(here.bits); if (here.op & 64) { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } state->offset = (unsigned)here.val; /* get distance extra bits, if any */ state->extra = (unsigned)(here.op) & 15; if (state->extra != 0) { NEEDBITS(state->extra); state->offset += BITS(state->extra); DROPBITS(state->extra); } if (state->offset > state->wsize - (state->whave < state->wsize ? left : 0)) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } Tracevv((stderr, "inflate: distance %u\n", state->offset)); /* copy match from window to output */ do { ROOM(); copy = state->wsize - state->offset; if (copy < left) { from = put + copy; copy = left - copy; } else { from = put - state->offset; copy = left; } if (copy > state->length) copy = state->length; state->length -= copy; left -= copy; do { *put++ = *from++; } while (--copy); } while (state->length != 0); break; case DONE: /* inflate stream terminated properly -- write leftover output */ ret = Z_STREAM_END; if (left < state->wsize) { if (out(out_desc, state->window, state->wsize - left)) ret = Z_BUF_ERROR; } goto inf_leave; case BAD: ret = Z_DATA_ERROR; goto inf_leave; default: /* can't happen, but makes compilers happy */ ret = Z_STREAM_ERROR; goto inf_leave; } /* Return unused input */ inf_leave: strm->next_in = next; strm->avail_in = have; return ret; } int ZEXPORT inflateBackEnd(strm) z_streamp strm; { if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) return Z_STREAM_ERROR; ZFREE(strm, strm->state); strm->state = Z_NULL; Tracev((stderr, "inflate: end\n")); return Z_OK; } PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/inffast.c000066400000000000000000000322171231437614300236620ustar00rootroot00000000000000/* inffast.c -- fast decoding * Copyright (C) 1995-2008, 2010, 2013 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "zutil.h" #include "inftrees.h" #include "inflate.h" #include "inffast.h" #ifndef ASMINF /* Allow machine dependent optimization for post-increment or pre-increment. Based on testing to date, Pre-increment preferred for: - PowerPC G3 (Adler) - MIPS R5000 (Randers-Pehrson) Post-increment preferred for: - none No measurable difference: - Pentium III (Anderson) - M68060 (Nikl) */ #ifdef POSTINC # define OFF 0 # define PUP(a) *(a)++ #else # define OFF 1 # define PUP(a) *++(a) #endif /* Decode literal, length, and distance codes and write out the resulting literal and match bytes until either not enough input or output is available, an end-of-block is encountered, or a data error is encountered. When large enough input and output buffers are supplied to inflate(), for example, a 16K input buffer and a 64K output buffer, more than 95% of the inflate execution time is spent in this routine. Entry assumptions: state->mode == LEN strm->avail_in >= 6 strm->avail_out >= 258 start >= strm->avail_out state->bits < 8 On return, state->mode is one of: LEN -- ran out of enough output space or enough available input TYPE -- reached end of block code, inflate() to interpret next block BAD -- error in block data Notes: - The maximum input bits used by a length/distance pair is 15 bits for the length code, 5 bits for the length extra, 15 bits for the distance code, and 13 bits for the distance extra. This totals 48 bits, or six bytes. Therefore if strm->avail_in >= 6, then there is enough input to avoid checking for available input while decoding. - The maximum bytes that a single length/distance pair can output is 258 bytes, which is the maximum length that can be coded. inflate_fast() requires strm->avail_out >= 258 for each loop to avoid checking for output space. */ void ZLIB_INTERNAL inflate_fast(strm, start) z_streamp strm; unsigned start; /* inflate()'s starting value for strm->avail_out */ { struct inflate_state FAR *state; z_const unsigned char FAR *in; /* local strm->next_in */ z_const unsigned char FAR *last; /* have enough input while in < last */ unsigned char FAR *out; /* local strm->next_out */ unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ unsigned char FAR *end; /* while out < end, enough space available */ #ifdef INFLATE_STRICT unsigned dmax; /* maximum distance from zlib header */ #endif unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ unsigned wnext; /* window write index */ unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ unsigned long hold; /* local strm->hold */ unsigned bits; /* local strm->bits */ code const FAR *lcode; /* local strm->lencode */ code const FAR *dcode; /* local strm->distcode */ unsigned lmask; /* mask for first level of length codes */ unsigned dmask; /* mask for first level of distance codes */ code here; /* retrieved table entry */ unsigned op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ unsigned len; /* match length, unused bytes */ unsigned dist; /* match distance */ unsigned char FAR *from; /* where to copy match from */ /* copy state to local variables */ state = (struct inflate_state FAR *)strm->state; in = strm->next_in - OFF; last = in + (strm->avail_in - 5); out = strm->next_out - OFF; beg = out - (start - strm->avail_out); end = out + (strm->avail_out - 257); #ifdef INFLATE_STRICT dmax = state->dmax; #endif wsize = state->wsize; whave = state->whave; wnext = state->wnext; window = state->window; hold = state->hold; bits = state->bits; lcode = state->lencode; dcode = state->distcode; lmask = (1U << state->lenbits) - 1; dmask = (1U << state->distbits) - 1; /* decode literals and length/distances until end-of-block or not enough input data or output space */ do { if (bits < 15) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; hold += (unsigned long)(PUP(in)) << bits; bits += 8; } here = lcode[hold & lmask]; dolen: op = (unsigned)(here.bits); hold >>= op; bits -= op; op = (unsigned)(here.op); if (op == 0) { /* literal */ Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here.val)); PUP(out) = (unsigned char)(here.val); } else if (op & 16) { /* length base */ len = (unsigned)(here.val); op &= 15; /* number of extra bits */ if (op) { if (bits < op) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; } len += (unsigned)hold & ((1U << op) - 1); hold >>= op; bits -= op; } Tracevv((stderr, "inflate: length %u\n", len)); if (bits < 15) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; hold += (unsigned long)(PUP(in)) << bits; bits += 8; } here = dcode[hold & dmask]; dodist: op = (unsigned)(here.bits); hold >>= op; bits -= op; op = (unsigned)(here.op); if (op & 16) { /* distance base */ dist = (unsigned)(here.val); op &= 15; /* number of extra bits */ if (bits < op) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; if (bits < op) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; } } dist += (unsigned)hold & ((1U << op) - 1); #ifdef INFLATE_STRICT if (dist > dmax) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #endif hold >>= op; bits -= op; Tracevv((stderr, "inflate: distance %u\n", dist)); op = (unsigned)(out - beg); /* max distance in output */ if (dist > op) { /* see if copy from window */ op = dist - op; /* distance back in window */ if (op > whave) { if (state->sane) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR if (len <= op - whave) { do { PUP(out) = 0; } while (--len); continue; } len -= op - whave; do { PUP(out) = 0; } while (--op > whave); if (op == 0) { from = out - dist; do { PUP(out) = PUP(from); } while (--len); continue; } #endif } from = window - OFF; if (wnext == 0) { /* very common case */ from += wsize - op; if (op < len) { /* some from window */ len -= op; do { PUP(out) = PUP(from); } while (--op); from = out - dist; /* rest from output */ } } else if (wnext < op) { /* wrap around window */ from += wsize + wnext - op; op -= wnext; if (op < len) { /* some from end of window */ len -= op; do { PUP(out) = PUP(from); } while (--op); from = window - OFF; if (wnext < len) { /* some from start of window */ op = wnext; len -= op; do { PUP(out) = PUP(from); } while (--op); from = out - dist; /* rest from output */ } } } else { /* contiguous in window */ from += wnext - op; if (op < len) { /* some from window */ len -= op; do { PUP(out) = PUP(from); } while (--op); from = out - dist; /* rest from output */ } } while (len > 2) { PUP(out) = PUP(from); PUP(out) = PUP(from); PUP(out) = PUP(from); len -= 3; } if (len) { PUP(out) = PUP(from); if (len > 1) PUP(out) = PUP(from); } } else { from = out - dist; /* copy direct from output */ do { /* minimum length is three */ PUP(out) = PUP(from); PUP(out) = PUP(from); PUP(out) = PUP(from); len -= 3; } while (len > 2); if (len) { PUP(out) = PUP(from); if (len > 1) PUP(out) = PUP(from); } } } else if ((op & 64) == 0) { /* 2nd level distance code */ here = dcode[here.val + (hold & ((1U << op) - 1))]; goto dodist; } else { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } } else if ((op & 64) == 0) { /* 2nd level length code */ here = lcode[here.val + (hold & ((1U << op) - 1))]; goto dolen; } else if (op & 32) { /* end-of-block */ Tracevv((stderr, "inflate: end of block\n")); state->mode = TYPE; break; } else { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } } while (in < last && out < end); /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ len = bits >> 3; in -= len; bits -= len << 3; hold &= (1U << bits) - 1; /* update state and return */ strm->next_in = in + OFF; strm->next_out = out + OFF; strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); strm->avail_out = (unsigned)(out < end ? 257 + (end - out) : 257 - (out - end)); state->hold = hold; state->bits = bits; return; } /* inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): - Using bit fields for code structure - Different op definition to avoid & for extra bits (do & for table bits) - Three separate decoding do-loops for direct, window, and wnext == 0 - Special case for distance > 1 copies to do overlapped load and store copy - Explicit branch predictions (based on measured branch probabilities) - Deferring match copy and interspersed it with decoding subsequent codes - Swapping literal/length else - Swapping window/direct else - Larger unrolled copy loops (three is about right) - Moving len -= 3 statement into middle of loop */ #endif /* !ASMINF */ PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/inffast.h000066400000000000000000000006531231437614300236660ustar00rootroot00000000000000/* inffast.h -- header to use inffast.c * Copyright (C) 1995-2003, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/inffixed.h000066400000000000000000000142741231437614300240340ustar00rootroot00000000000000 /* inffixed.h -- table for decoding fixed codes * Generated automatically by makefixed(). */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of this library and is subject to change. Applications should only use zlib.h. */ static const code lenfix[512] = { {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, {0,9,255} }; static const code distfix[32] = { {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, {22,5,193},{64,5,0} }; PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/inflate.c000066400000000000000000001504101231437614300236460ustar00rootroot00000000000000/* inflate.c -- zlib decompression * Copyright (C) 1995-2012 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* * Change history: * * 1.2.beta0 24 Nov 2002 * - First version -- complete rewrite of inflate to simplify code, avoid * creation of window when not needed, minimize use of window when it is * needed, make inffast.c even faster, implement gzip decoding, and to * improve code readability and style over the previous zlib inflate code * * 1.2.beta1 25 Nov 2002 * - Use pointers for available input and output checking in inffast.c * - Remove input and output counters in inffast.c * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 * - Remove unnecessary second byte pull from length extra in inffast.c * - Unroll direct copy to three copies per loop in inffast.c * * 1.2.beta2 4 Dec 2002 * - Change external routine names to reduce potential conflicts * - Correct filename to inffixed.h for fixed tables in inflate.c * - Make hbuf[] unsigned char to match parameter type in inflate.c * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) * to avoid negation problem on Alphas (64 bit) in inflate.c * * 1.2.beta3 22 Dec 2002 * - Add comments on state->bits assertion in inffast.c * - Add comments on op field in inftrees.h * - Fix bug in reuse of allocated window after inflateReset() * - Remove bit fields--back to byte structure for speed * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths * - Change post-increments to pre-increments in inflate_fast(), PPC biased? * - Add compile time option, POSTINC, to use post-increments instead (Intel?) * - Make MATCH copy in inflate() much faster for when inflate_fast() not used * - Use local copies of stream next and avail values, as well as local bit * buffer and bit count in inflate()--for speed when inflate_fast() not used * * 1.2.beta4 1 Jan 2003 * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings * - Move a comment on output buffer sizes from inffast.c to inflate.c * - Add comments in inffast.c to introduce the inflate_fast() routine * - Rearrange window copies in inflate_fast() for speed and simplification * - Unroll last copy for window match in inflate_fast() * - Use local copies of window variables in inflate_fast() for speed * - Pull out common wnext == 0 case for speed in inflate_fast() * - Make op and len in inflate_fast() unsigned for consistency * - Add FAR to lcode and dcode declarations in inflate_fast() * - Simplified bad distance check in inflate_fast() * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new * source file infback.c to provide a call-back interface to inflate for * programs like gzip and unzip -- uses window as output buffer to avoid * window copying * * 1.2.beta5 1 Jan 2003 * - Improved inflateBack() interface to allow the caller to provide initial * input in strm. * - Fixed stored blocks bug in inflateBack() * * 1.2.beta6 4 Jan 2003 * - Added comments in inffast.c on effectiveness of POSTINC * - Typecasting all around to reduce compiler warnings * - Changed loops from while (1) or do {} while (1) to for (;;), again to * make compilers happy * - Changed type of window in inflateBackInit() to unsigned char * * * 1.2.beta7 27 Jan 2003 * - Changed many types to unsigned or unsigned short to avoid warnings * - Added inflateCopy() function * * 1.2.0 9 Mar 2003 * - Changed inflateBack() interface to provide separate opaque descriptors * for the in() and out() functions * - Changed inflateBack() argument and in_func typedef to swap the length * and buffer address return values for the input function * - Check next_in and next_out for Z_NULL on entry to inflate() * * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. */ #include "zutil.h" #include "inftrees.h" #include "inflate.h" #include "inffast.h" #ifdef MAKEFIXED # ifndef BUILDFIXED # define BUILDFIXED # endif #endif /* function prototypes */ local void fixedtables OF((struct inflate_state FAR *state)); local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, unsigned copy)); #ifdef BUILDFIXED void makefixed OF((void)); #endif local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, unsigned len)); int ZEXPORT inflateResetKeep(strm) z_streamp strm; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; strm->total_in = strm->total_out = state->total = 0; strm->msg = Z_NULL; if (state->wrap) /* to support ill-conceived Java test suite */ strm->adler = state->wrap & 1; state->mode = HEAD; state->last = 0; state->havedict = 0; state->dmax = 32768U; state->head = Z_NULL; state->hold = 0; state->bits = 0; state->lencode = state->distcode = state->next = state->codes; state->sane = 1; state->back = -1; Tracev((stderr, "inflate: reset\n")); return Z_OK; } int ZEXPORT inflateReset(strm) z_streamp strm; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; state->wsize = 0; state->whave = 0; state->wnext = 0; return inflateResetKeep(strm); } int ZEXPORT inflateReset2(strm, windowBits) z_streamp strm; int windowBits; { int wrap; struct inflate_state FAR *state; /* get the state */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* extract wrap request from windowBits parameter */ if (windowBits < 0) { wrap = 0; windowBits = -windowBits; } else { wrap = (windowBits >> 4) + 1; #ifdef GUNZIP if (windowBits < 48) windowBits &= 15; #endif } /* set number of window bits, free window if different */ if (windowBits && (windowBits < 8 || windowBits > 15)) return Z_STREAM_ERROR; if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { ZFREE(strm, state->window); state->window = Z_NULL; } /* update state and reset the rest of it */ state->wrap = wrap; state->wbits = (unsigned)windowBits; return inflateReset(strm); } int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) z_streamp strm; int windowBits; const char *version; int stream_size; { int ret; struct inflate_state FAR *state; if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != (int)(sizeof(z_stream))) return Z_VERSION_ERROR; if (strm == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; /* in case we return an error */ if (strm->zalloc == (alloc_func)0) { #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zalloc = zcalloc; strm->opaque = (voidpf)0; #endif } if (strm->zfree == (free_func)0) #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zfree = zcfree; #endif state = (struct inflate_state FAR *) ZALLOC(strm, 1, sizeof(struct inflate_state)); if (state == Z_NULL) return Z_MEM_ERROR; Tracev((stderr, "inflate: allocated\n")); strm->state = (struct internal_state FAR *)state; state->window = Z_NULL; ret = inflateReset2(strm, windowBits); if (ret != Z_OK) { ZFREE(strm, state); strm->state = Z_NULL; } return ret; } int ZEXPORT inflateInit_(strm, version, stream_size) z_streamp strm; const char *version; int stream_size; { return inflateInit2_(strm, DEF_WBITS, version, stream_size); } int ZEXPORT inflatePrime(strm, bits, value) z_streamp strm; int bits; int value; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (bits < 0) { state->hold = 0; state->bits = 0; return Z_OK; } if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; value &= (1L << bits) - 1; state->hold += value << state->bits; state->bits += bits; return Z_OK; } /* Return state with length and distance decoding tables and index sizes set to fixed code decoding. Normally this returns fixed tables from inffixed.h. If BUILDFIXED is defined, then instead this routine builds the tables the first time it's called, and returns those tables the first time and thereafter. This reduces the size of the code by about 2K bytes, in exchange for a little execution time. However, BUILDFIXED should not be used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ local void fixedtables(state) struct inflate_state FAR *state; { #ifdef BUILDFIXED static int virgin = 1; static code *lenfix, *distfix; static code fixed[544]; /* build fixed huffman tables if first call (may not be thread safe) */ if (virgin) { unsigned sym, bits; static code *next; /* literal/length table */ sym = 0; while (sym < 144) state->lens[sym++] = 8; while (sym < 256) state->lens[sym++] = 9; while (sym < 280) state->lens[sym++] = 7; while (sym < 288) state->lens[sym++] = 8; next = fixed; lenfix = next; bits = 9; inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); /* distance table */ sym = 0; while (sym < 32) state->lens[sym++] = 5; distfix = next; bits = 5; inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); /* do this just once */ virgin = 0; } #else /* !BUILDFIXED */ # include "inffixed.h" #endif /* BUILDFIXED */ state->lencode = lenfix; state->lenbits = 9; state->distcode = distfix; state->distbits = 5; } #ifdef MAKEFIXED #include /* Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also defines BUILDFIXED, so the tables are built on the fly. makefixed() writes those tables to stdout, which would be piped to inffixed.h. A small program can simply call makefixed to do this: void makefixed(void); int main(void) { makefixed(); return 0; } Then that can be linked with zlib built with MAKEFIXED defined and run: a.out > inffixed.h */ void makefixed() { unsigned low, size; struct inflate_state state; fixedtables(&state); puts(" /* inffixed.h -- table for decoding fixed codes"); puts(" * Generated automatically by makefixed()."); puts(" */"); puts(""); puts(" /* WARNING: this file should *not* be used by applications."); puts(" It is part of the implementation of this library and is"); puts(" subject to change. Applications should only use zlib.h."); puts(" */"); puts(""); size = 1U << 9; printf(" static const code lenfix[%u] = {", size); low = 0; for (;;) { if ((low % 7) == 0) printf("\n "); printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op, state.lencode[low].bits, state.lencode[low].val); if (++low == size) break; putchar(','); } puts("\n };"); size = 1U << 5; printf("\n static const code distfix[%u] = {", size); low = 0; for (;;) { if ((low % 6) == 0) printf("\n "); printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, state.distcode[low].val); if (++low == size) break; putchar(','); } puts("\n };"); } #endif /* MAKEFIXED */ /* Update the window with the last wsize (normally 32K) bytes written before returning. If window does not exist yet, create it. This is only called when a window is already in use, or when output has been written during this inflate call, but the end of the deflate stream has not been reached yet. It is also called to create a window for dictionary data when a dictionary is loaded. Providing output buffers larger than 32K to inflate() should provide a speed advantage, since only the last 32K of output is copied to the sliding window upon return from inflate(), and since all distances after the first 32K of output will fall in the output data, making match copies simpler and faster. The advantage may be dependent on the size of the processor's data caches. */ local int updatewindow(strm, end, copy) z_streamp strm; const Bytef *end; unsigned copy; { struct inflate_state FAR *state; unsigned dist; state = (struct inflate_state FAR *)strm->state; /* if it hasn't been done already, allocate space for the window */ if (state->window == Z_NULL) { state->window = (unsigned char FAR *) ZALLOC(strm, 1U << state->wbits, sizeof(unsigned char)); if (state->window == Z_NULL) return 1; } /* if window not in use yet, initialize */ if (state->wsize == 0) { state->wsize = 1U << state->wbits; state->wnext = 0; state->whave = 0; } /* copy state->wsize or less output bytes into the circular window */ if (copy >= state->wsize) { zmemcpy(state->window, end - state->wsize, state->wsize); state->wnext = 0; state->whave = state->wsize; } else { dist = state->wsize - state->wnext; if (dist > copy) dist = copy; zmemcpy(state->window + state->wnext, end - copy, dist); copy -= dist; if (copy) { zmemcpy(state->window, end - copy, copy); state->wnext = copy; state->whave = state->wsize; } else { state->wnext += dist; if (state->wnext == state->wsize) state->wnext = 0; if (state->whave < state->wsize) state->whave += dist; } } return 0; } /* Macros for inflate(): */ /* check function to use adler32() for zlib or crc32() for gzip */ #ifdef GUNZIP # define UPDATE(check, buf, len) \ (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) #else # define UPDATE(check, buf, len) adler32(check, buf, len) #endif /* check macros for header crc */ #ifdef GUNZIP # define CRC2(check, word) \ do { \ hbuf[0] = (unsigned char)(word); \ hbuf[1] = (unsigned char)((word) >> 8); \ check = crc32(check, hbuf, 2); \ } while (0) # define CRC4(check, word) \ do { \ hbuf[0] = (unsigned char)(word); \ hbuf[1] = (unsigned char)((word) >> 8); \ hbuf[2] = (unsigned char)((word) >> 16); \ hbuf[3] = (unsigned char)((word) >> 24); \ check = crc32(check, hbuf, 4); \ } while (0) #endif /* Load registers with state in inflate() for speed */ #define LOAD() \ do { \ put = strm->next_out; \ left = strm->avail_out; \ next = strm->next_in; \ have = strm->avail_in; \ hold = state->hold; \ bits = state->bits; \ } while (0) /* Restore state from registers in inflate() */ #define RESTORE() \ do { \ strm->next_out = put; \ strm->avail_out = left; \ strm->next_in = next; \ strm->avail_in = have; \ state->hold = hold; \ state->bits = bits; \ } while (0) /* Clear the input bit accumulator */ #define INITBITS() \ do { \ hold = 0; \ bits = 0; \ } while (0) /* Get a byte of input into the bit accumulator, or return from inflate() if there is no input available. */ #define PULLBYTE() \ do { \ if (have == 0) goto inf_leave; \ have--; \ hold += (unsigned long)(*next++) << bits; \ bits += 8; \ } while (0) /* Assure that there are at least n bits in the bit accumulator. If there is not enough available input to do that, then return from inflate(). */ #define NEEDBITS(n) \ do { \ while (bits < (unsigned)(n)) \ PULLBYTE(); \ } while (0) /* Return the low n bits of the bit accumulator (n < 16) */ #define BITS(n) \ ((unsigned)hold & ((1U << (n)) - 1)) /* Remove n bits from the bit accumulator */ #define DROPBITS(n) \ do { \ hold >>= (n); \ bits -= (unsigned)(n); \ } while (0) /* Remove zero to seven bits as needed to go to a byte boundary */ #define BYTEBITS() \ do { \ hold >>= bits & 7; \ bits -= bits & 7; \ } while (0) /* inflate() uses a state machine to process as much input data and generate as much output data as possible before returning. The state machine is structured roughly as follows: for (;;) switch (state) { ... case STATEn: if (not enough input data or output space to make progress) return; ... make progress ... state = STATEm; break; ... } so when inflate() is called again, the same case is attempted again, and if the appropriate resources are provided, the machine proceeds to the next state. The NEEDBITS() macro is usually the way the state evaluates whether it can proceed or should return. NEEDBITS() does the return if the requested bits are not available. The typical use of the BITS macros is: NEEDBITS(n); ... do something with BITS(n) ... DROPBITS(n); where NEEDBITS(n) either returns from inflate() if there isn't enough input left to load n bits into the accumulator, or it continues. BITS(n) gives the low n bits in the accumulator. When done, DROPBITS(n) drops the low n bits off the accumulator. INITBITS() clears the accumulator and sets the number of available bits to zero. BYTEBITS() discards just enough bits to put the accumulator on a byte boundary. After BYTEBITS() and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return if there is no input available. The decoding of variable length codes uses PULLBYTE() directly in order to pull just enough bytes to decode the next code, and no more. Some states loop until they get enough input, making sure that enough state information is maintained to continue the loop where it left off if NEEDBITS() returns in the loop. For example, want, need, and keep would all have to actually be part of the saved state in case NEEDBITS() returns: case STATEw: while (want < need) { NEEDBITS(n); keep[want++] = BITS(n); DROPBITS(n); } state = STATEx; case STATEx: As shown above, if the next state is also the next case, then the break is omitted. A state may also return if there is not enough output space available to complete that state. Those states are copying stored data, writing a literal byte, and copying a matching string. When returning, a "goto inf_leave" is used to update the total counters, update the check value, and determine whether any progress has been made during that inflate() call in order to return the proper return code. Progress is defined as a change in either strm->avail_in or strm->avail_out. When there is a window, goto inf_leave will update the window with the last output written. If a goto inf_leave occurs in the middle of decompression and there is no window currently, goto inf_leave will create one and copy output to the window for the next call of inflate(). In this implementation, the flush parameter of inflate() only affects the return code (per zlib.h). inflate() always writes as much as possible to strm->next_out, given the space available and the provided input--the effect documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers the allocation of and copying into a sliding window until necessary, which provides the effect documented in zlib.h for Z_FINISH when the entire input stream available. So the only thing the flush parameter actually does is: when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it will return Z_BUF_ERROR if it has not reached the end of the stream. */ int ZEXPORT inflate(strm, flush) z_streamp strm; int flush; { struct inflate_state FAR *state; z_const unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ unsigned have, left; /* available input and output */ unsigned long hold; /* bit buffer */ unsigned bits; /* bits in bit buffer */ unsigned in, out; /* save starting available input and output */ unsigned copy; /* number of stored or match bytes to copy */ unsigned char FAR *from; /* where to copy match bytes from */ code here; /* current decoding table entry */ code last; /* parent table entry */ unsigned len; /* length to copy for repeats, bits to drop */ int ret; /* return code */ #ifdef GUNZIP unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ #endif static const unsigned short order[19] = /* permutation of code lengths */ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || (strm->next_in == Z_NULL && strm->avail_in != 0)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ LOAD(); in = have; out = left; ret = Z_OK; for (;;) switch (state->mode) { case HEAD: if (state->wrap == 0) { state->mode = TYPEDO; break; } NEEDBITS(16); #ifdef GUNZIP if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ state->check = crc32(0L, Z_NULL, 0); CRC2(state->check, hold); INITBITS(); state->mode = FLAGS; break; } state->flags = 0; /* expect zlib header */ if (state->head != Z_NULL) state->head->done = -1; if (!(state->wrap & 1) || /* check if zlib header allowed */ #else if ( #endif ((BITS(8) << 8) + (hold >> 8)) % 31) { strm->msg = (char *)"incorrect header check"; state->mode = BAD; break; } if (BITS(4) != Z_DEFLATED) { strm->msg = (char *)"unknown compression method"; state->mode = BAD; break; } DROPBITS(4); len = BITS(4) + 8; if (state->wbits == 0) state->wbits = len; else if (len > state->wbits) { strm->msg = (char *)"invalid window size"; state->mode = BAD; break; } state->dmax = 1U << len; Tracev((stderr, "inflate: zlib header ok\n")); strm->adler = state->check = adler32(0L, Z_NULL, 0); state->mode = hold & 0x200 ? DICTID : TYPE; INITBITS(); break; #ifdef GUNZIP case FLAGS: NEEDBITS(16); state->flags = (int)(hold); if ((state->flags & 0xff) != Z_DEFLATED) { strm->msg = (char *)"unknown compression method"; state->mode = BAD; break; } if (state->flags & 0xe000) { strm->msg = (char *)"unknown header flags set"; state->mode = BAD; break; } if (state->head != Z_NULL) state->head->text = (int)((hold >> 8) & 1); if (state->flags & 0x0200) CRC2(state->check, hold); INITBITS(); state->mode = TIME; case TIME: NEEDBITS(32); if (state->head != Z_NULL) state->head->time = hold; if (state->flags & 0x0200) CRC4(state->check, hold); INITBITS(); state->mode = OS; case OS: NEEDBITS(16); if (state->head != Z_NULL) { state->head->xflags = (int)(hold & 0xff); state->head->os = (int)(hold >> 8); } if (state->flags & 0x0200) CRC2(state->check, hold); INITBITS(); state->mode = EXLEN; case EXLEN: if (state->flags & 0x0400) { NEEDBITS(16); state->length = (unsigned)(hold); if (state->head != Z_NULL) state->head->extra_len = (unsigned)hold; if (state->flags & 0x0200) CRC2(state->check, hold); INITBITS(); } else if (state->head != Z_NULL) state->head->extra = Z_NULL; state->mode = EXTRA; case EXTRA: if (state->flags & 0x0400) { copy = state->length; if (copy > have) copy = have; if (copy) { if (state->head != Z_NULL && state->head->extra != Z_NULL) { len = state->head->extra_len - state->length; zmemcpy(state->head->extra + len, next, len + copy > state->head->extra_max ? state->head->extra_max - len : copy); } if (state->flags & 0x0200) state->check = crc32(state->check, next, copy); have -= copy; next += copy; state->length -= copy; } if (state->length) goto inf_leave; } state->length = 0; state->mode = NAME; case NAME: if (state->flags & 0x0800) { if (have == 0) goto inf_leave; copy = 0; do { len = (unsigned)(next[copy++]); if (state->head != Z_NULL && state->head->name != Z_NULL && state->length < state->head->name_max) state->head->name[state->length++] = len; } while (len && copy < have); if (state->flags & 0x0200) state->check = crc32(state->check, next, copy); have -= copy; next += copy; if (len) goto inf_leave; } else if (state->head != Z_NULL) state->head->name = Z_NULL; state->length = 0; state->mode = COMMENT; case COMMENT: if (state->flags & 0x1000) { if (have == 0) goto inf_leave; copy = 0; do { len = (unsigned)(next[copy++]); if (state->head != Z_NULL && state->head->comment != Z_NULL && state->length < state->head->comm_max) state->head->comment[state->length++] = len; } while (len && copy < have); if (state->flags & 0x0200) state->check = crc32(state->check, next, copy); have -= copy; next += copy; if (len) goto inf_leave; } else if (state->head != Z_NULL) state->head->comment = Z_NULL; state->mode = HCRC; case HCRC: if (state->flags & 0x0200) { NEEDBITS(16); if (hold != (state->check & 0xffff)) { strm->msg = (char *)"header crc mismatch"; state->mode = BAD; break; } INITBITS(); } if (state->head != Z_NULL) { state->head->hcrc = (int)((state->flags >> 9) & 1); state->head->done = 1; } strm->adler = state->check = crc32(0L, Z_NULL, 0); state->mode = TYPE; break; #endif case DICTID: NEEDBITS(32); strm->adler = state->check = ZSWAP32(hold); INITBITS(); state->mode = DICT; case DICT: if (state->havedict == 0) { RESTORE(); return Z_NEED_DICT; } strm->adler = state->check = adler32(0L, Z_NULL, 0); state->mode = TYPE; case TYPE: if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; case TYPEDO: if (state->last) { BYTEBITS(); state->mode = CHECK; break; } NEEDBITS(3); state->last = BITS(1); DROPBITS(1); switch (BITS(2)) { case 0: /* stored block */ Tracev((stderr, "inflate: stored block%s\n", state->last ? " (last)" : "")); state->mode = STORED; break; case 1: /* fixed block */ fixedtables(state); Tracev((stderr, "inflate: fixed codes block%s\n", state->last ? " (last)" : "")); state->mode = LEN_; /* decode codes */ if (flush == Z_TREES) { DROPBITS(2); goto inf_leave; } break; case 2: /* dynamic block */ Tracev((stderr, "inflate: dynamic codes block%s\n", state->last ? " (last)" : "")); state->mode = TABLE; break; case 3: strm->msg = (char *)"invalid block type"; state->mode = BAD; } DROPBITS(2); break; case STORED: BYTEBITS(); /* go to byte boundary */ NEEDBITS(32); if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { strm->msg = (char *)"invalid stored block lengths"; state->mode = BAD; break; } state->length = (unsigned)hold & 0xffff; Tracev((stderr, "inflate: stored length %u\n", state->length)); INITBITS(); state->mode = COPY_; if (flush == Z_TREES) goto inf_leave; case COPY_: state->mode = COPY; case COPY: copy = state->length; if (copy) { if (copy > have) copy = have; if (copy > left) copy = left; if (copy == 0) goto inf_leave; zmemcpy(put, next, copy); have -= copy; next += copy; left -= copy; put += copy; state->length -= copy; break; } Tracev((stderr, "inflate: stored end\n")); state->mode = TYPE; break; case TABLE: NEEDBITS(14); state->nlen = BITS(5) + 257; DROPBITS(5); state->ndist = BITS(5) + 1; DROPBITS(5); state->ncode = BITS(4) + 4; DROPBITS(4); #ifndef PKZIP_BUG_WORKAROUND if (state->nlen > 286 || state->ndist > 30) { strm->msg = (char *)"too many length or distance symbols"; state->mode = BAD; break; } #endif Tracev((stderr, "inflate: table sizes ok\n")); state->have = 0; state->mode = LENLENS; case LENLENS: while (state->have < state->ncode) { NEEDBITS(3); state->lens[order[state->have++]] = (unsigned short)BITS(3); DROPBITS(3); } while (state->have < 19) state->lens[order[state->have++]] = 0; state->next = state->codes; state->lencode = (const code FAR *)(state->next); state->lenbits = 7; ret = inflate_table(CODES, state->lens, 19, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid code lengths set"; state->mode = BAD; break; } Tracev((stderr, "inflate: code lengths ok\n")); state->have = 0; state->mode = CODELENS; case CODELENS: while (state->have < state->nlen + state->ndist) { for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.val < 16) { DROPBITS(here.bits); state->lens[state->have++] = here.val; } else { if (here.val == 16) { NEEDBITS(here.bits + 2); DROPBITS(here.bits); if (state->have == 0) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } len = state->lens[state->have - 1]; copy = 3 + BITS(2); DROPBITS(2); } else if (here.val == 17) { NEEDBITS(here.bits + 3); DROPBITS(here.bits); len = 0; copy = 3 + BITS(3); DROPBITS(3); } else { NEEDBITS(here.bits + 7); DROPBITS(here.bits); len = 0; copy = 11 + BITS(7); DROPBITS(7); } if (state->have + copy > state->nlen + state->ndist) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } while (copy--) state->lens[state->have++] = (unsigned short)len; } } /* handle error breaks in while */ if (state->mode == BAD) break; /* check for end-of-block code (better have one) */ if (state->lens[256] == 0) { strm->msg = (char *)"invalid code -- missing end-of-block"; state->mode = BAD; break; } /* build code tables -- note: do not change the lenbits or distbits values here (9 and 6) without reading the comments in inftrees.h concerning the ENOUGH constants, which depend on those values */ state->next = state->codes; state->lencode = (const code FAR *)(state->next); state->lenbits = 9; ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid literal/lengths set"; state->mode = BAD; break; } state->distcode = (const code FAR *)(state->next); state->distbits = 6; ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, &(state->next), &(state->distbits), state->work); if (ret) { strm->msg = (char *)"invalid distances set"; state->mode = BAD; break; } Tracev((stderr, "inflate: codes ok\n")); state->mode = LEN_; if (flush == Z_TREES) goto inf_leave; case LEN_: state->mode = LEN; case LEN: if (have >= 6 && left >= 258) { RESTORE(); inflate_fast(strm, out); LOAD(); if (state->mode == TYPE) state->back = -1; break; } state->back = 0; for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.op && (here.op & 0xf0) == 0) { last = here; for (;;) { here = state->lencode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); state->back += last.bits; } DROPBITS(here.bits); state->back += here.bits; state->length = (unsigned)here.val; if ((int)(here.op) == 0) { Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here.val)); state->mode = LIT; break; } if (here.op & 32) { Tracevv((stderr, "inflate: end of block\n")); state->back = -1; state->mode = TYPE; break; } if (here.op & 64) { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } state->extra = (unsigned)(here.op) & 15; state->mode = LENEXT; case LENEXT: if (state->extra) { NEEDBITS(state->extra); state->length += BITS(state->extra); DROPBITS(state->extra); state->back += state->extra; } Tracevv((stderr, "inflate: length %u\n", state->length)); state->was = state->length; state->mode = DIST; case DIST: for (;;) { here = state->distcode[BITS(state->distbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if ((here.op & 0xf0) == 0) { last = here; for (;;) { here = state->distcode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); state->back += last.bits; } DROPBITS(here.bits); state->back += here.bits; if (here.op & 64) { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } state->offset = (unsigned)here.val; state->extra = (unsigned)(here.op) & 15; state->mode = DISTEXT; case DISTEXT: if (state->extra) { NEEDBITS(state->extra); state->offset += BITS(state->extra); DROPBITS(state->extra); state->back += state->extra; } #ifdef INFLATE_STRICT if (state->offset > state->dmax) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #endif Tracevv((stderr, "inflate: distance %u\n", state->offset)); state->mode = MATCH; case MATCH: if (left == 0) goto inf_leave; copy = out - left; if (state->offset > copy) { /* copy from window */ copy = state->offset - copy; if (copy > state->whave) { if (state->sane) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR Trace((stderr, "inflate.c too far\n")); copy -= state->whave; if (copy > state->length) copy = state->length; if (copy > left) copy = left; left -= copy; state->length -= copy; do { *put++ = 0; } while (--copy); if (state->length == 0) state->mode = LEN; break; #endif } if (copy > state->wnext) { copy -= state->wnext; from = state->window + (state->wsize - copy); } else from = state->window + (state->wnext - copy); if (copy > state->length) copy = state->length; } else { /* copy from output */ from = put - state->offset; copy = state->length; } if (copy > left) copy = left; left -= copy; state->length -= copy; do { *put++ = *from++; } while (--copy); if (state->length == 0) state->mode = LEN; break; case LIT: if (left == 0) goto inf_leave; *put++ = (unsigned char)(state->length); left--; state->mode = LEN; break; case CHECK: if (state->wrap) { NEEDBITS(32); out -= left; strm->total_out += out; state->total += out; if (out) strm->adler = state->check = UPDATE(state->check, put - out, out); out = left; if (( #ifdef GUNZIP state->flags ? hold : #endif ZSWAP32(hold)) != state->check) { strm->msg = (char *)"incorrect data check"; state->mode = BAD; break; } INITBITS(); Tracev((stderr, "inflate: check matches trailer\n")); } #ifdef GUNZIP state->mode = LENGTH; case LENGTH: if (state->wrap && state->flags) { NEEDBITS(32); if (hold != (state->total & 0xffffffffUL)) { strm->msg = (char *)"incorrect length check"; state->mode = BAD; break; } INITBITS(); Tracev((stderr, "inflate: length matches trailer\n")); } #endif state->mode = DONE; case DONE: ret = Z_STREAM_END; goto inf_leave; case BAD: ret = Z_DATA_ERROR; goto inf_leave; case MEM: return Z_MEM_ERROR; case SYNC: default: return Z_STREAM_ERROR; } /* Return from inflate(), updating the total counts and the check value. If there was no progress during the inflate() call, return a buffer error. Call updatewindow() to create and/or update the window state. Note: a memory error from inflate() is non-recoverable. */ inf_leave: RESTORE(); if (state->wsize || (out != strm->avail_out && state->mode < BAD && (state->mode < CHECK || flush != Z_FINISH))) if (updatewindow(strm, strm->next_out, out - strm->avail_out)) { state->mode = MEM; return Z_MEM_ERROR; } in -= strm->avail_in; out -= strm->avail_out; strm->total_in += in; strm->total_out += out; state->total += out; if (state->wrap && out) strm->adler = state->check = UPDATE(state->check, strm->next_out - out, out); strm->data_type = state->bits + (state->last ? 64 : 0) + (state->mode == TYPE ? 128 : 0) + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) ret = Z_BUF_ERROR; return ret; } int ZEXPORT inflateEnd(strm) z_streamp strm; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->window != Z_NULL) ZFREE(strm, state->window); ZFREE(strm, strm->state); strm->state = Z_NULL; Tracev((stderr, "inflate: end\n")); return Z_OK; } int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength) z_streamp strm; Bytef *dictionary; uInt *dictLength; { struct inflate_state FAR *state; /* check state */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* copy dictionary */ if (state->whave && dictionary != Z_NULL) { zmemcpy(dictionary, state->window + state->wnext, state->whave - state->wnext); zmemcpy(dictionary + state->whave - state->wnext, state->window, state->wnext); } if (dictLength != Z_NULL) *dictLength = state->whave; return Z_OK; } int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) z_streamp strm; const Bytef *dictionary; uInt dictLength; { struct inflate_state FAR *state; unsigned long dictid; int ret; /* check state */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->wrap != 0 && state->mode != DICT) return Z_STREAM_ERROR; /* check for correct dictionary identifier */ if (state->mode == DICT) { dictid = adler32(0L, Z_NULL, 0); dictid = adler32(dictid, dictionary, dictLength); if (dictid != state->check) return Z_DATA_ERROR; } /* copy dictionary to window using updatewindow(), which will amend the existing dictionary if appropriate */ ret = updatewindow(strm, dictionary + dictLength, dictLength); if (ret) { state->mode = MEM; return Z_MEM_ERROR; } state->havedict = 1; Tracev((stderr, "inflate: dictionary set\n")); return Z_OK; } int ZEXPORT inflateGetHeader(strm, head) z_streamp strm; gz_headerp head; { struct inflate_state FAR *state; /* check state */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; /* save header structure */ state->head = head; head->done = 0; return Z_OK; } /* Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found or when out of input. When called, *have is the number of pattern bytes found in order so far, in 0..3. On return *have is updated to the new state. If on return *have equals four, then the pattern was found and the return value is how many bytes were read including the last byte of the pattern. If *have is less than four, then the pattern has not been found yet and the return value is len. In the latter case, syncsearch() can be called again with more data and the *have state. *have is initialized to zero for the first call. */ local unsigned syncsearch(have, buf, len) unsigned FAR *have; const unsigned char FAR *buf; unsigned len; { unsigned got; unsigned next; got = *have; next = 0; while (next < len && got < 4) { if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) got++; else if (buf[next]) got = 0; else got = 4 - got; next++; } *have = got; return next; } int ZEXPORT inflateSync(strm) z_streamp strm; { unsigned len; /* number of bytes to look at or looked at */ unsigned long in, out; /* temporary to save total_in and total_out */ unsigned char buf[4]; /* to restore bit buffer to byte string */ struct inflate_state FAR *state; /* check parameters */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; /* if first time, start search in bit buffer */ if (state->mode != SYNC) { state->mode = SYNC; state->hold <<= state->bits & 7; state->bits -= state->bits & 7; len = 0; while (state->bits >= 8) { buf[len++] = (unsigned char)(state->hold); state->hold >>= 8; state->bits -= 8; } state->have = 0; syncsearch(&(state->have), buf, len); } /* search available input */ len = syncsearch(&(state->have), strm->next_in, strm->avail_in); strm->avail_in -= len; strm->next_in += len; strm->total_in += len; /* return no joy or set up to restart inflate() on a new block */ if (state->have != 4) return Z_DATA_ERROR; in = strm->total_in; out = strm->total_out; inflateReset(strm); strm->total_in = in; strm->total_out = out; state->mode = TYPE; return Z_OK; } /* Returns true if inflate is currently at the end of a block generated by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored block. When decompressing, PPP checks that at the end of input packet, inflate is waiting for these length bytes. */ int ZEXPORT inflateSyncPoint(strm) z_streamp strm; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; return state->mode == STORED && state->bits == 0; } int ZEXPORT inflateCopy(dest, source) z_streamp dest; z_streamp source; { struct inflate_state FAR *state; struct inflate_state FAR *copy; unsigned char FAR *window; unsigned wsize; /* check input */ if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)source->state; /* allocate space */ copy = (struct inflate_state FAR *) ZALLOC(source, 1, sizeof(struct inflate_state)); if (copy == Z_NULL) return Z_MEM_ERROR; window = Z_NULL; if (state->window != Z_NULL) { window = (unsigned char FAR *) ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); if (window == Z_NULL) { ZFREE(source, copy); return Z_MEM_ERROR; } } /* copy state */ zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); if (state->lencode >= state->codes && state->lencode <= state->codes + ENOUGH - 1) { copy->lencode = copy->codes + (state->lencode - state->codes); copy->distcode = copy->codes + (state->distcode - state->codes); } copy->next = copy->codes + (state->next - state->codes); if (window != Z_NULL) { wsize = 1U << state->wbits; zmemcpy(window, state->window, wsize); } copy->window = window; dest->state = (struct internal_state FAR *)copy; return Z_OK; } int ZEXPORT inflateUndermine(strm, subvert) z_streamp strm; int subvert; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; state->sane = !subvert; #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR return Z_OK; #else state->sane = 1; return Z_DATA_ERROR; #endif } long ZEXPORT inflateMark(strm) z_streamp strm; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; state = (struct inflate_state FAR *)strm->state; return ((long)(state->back) << 16) + (state->mode == COPY ? state->length : (state->mode == MATCH ? state->was - state->length : 0)); } PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/inflate.h000066400000000000000000000143771231437614300236660ustar00rootroot00000000000000/* inflate.h -- internal inflate state definition * Copyright (C) 1995-2009 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* define NO_GZIP when compiling if you want to disable gzip header and trailer decoding by inflate(). NO_GZIP would be used to avoid linking in the crc code when it is not needed. For shared libraries, gzip decoding should be left enabled. */ #ifndef NO_GZIP # define GUNZIP #endif /* Possible inflate modes between inflate() calls */ typedef enum { HEAD, /* i: waiting for magic header */ FLAGS, /* i: waiting for method and flags (gzip) */ TIME, /* i: waiting for modification time (gzip) */ OS, /* i: waiting for extra flags and operating system (gzip) */ EXLEN, /* i: waiting for extra length (gzip) */ EXTRA, /* i: waiting for extra bytes (gzip) */ NAME, /* i: waiting for end of file name (gzip) */ COMMENT, /* i: waiting for end of comment (gzip) */ HCRC, /* i: waiting for header crc (gzip) */ DICTID, /* i: waiting for dictionary check value */ DICT, /* waiting for inflateSetDictionary() call */ TYPE, /* i: waiting for type bits, including last-flag bit */ TYPEDO, /* i: same, but skip check to exit inflate on new block */ STORED, /* i: waiting for stored size (length and complement) */ COPY_, /* i/o: same as COPY below, but only first time in */ COPY, /* i/o: waiting for input or output to copy stored block */ TABLE, /* i: waiting for dynamic block table lengths */ LENLENS, /* i: waiting for code length code lengths */ CODELENS, /* i: waiting for length/lit and distance code lengths */ LEN_, /* i: same as LEN below, but only first time in */ LEN, /* i: waiting for length/lit/eob code */ LENEXT, /* i: waiting for length extra bits */ DIST, /* i: waiting for distance code */ DISTEXT, /* i: waiting for distance extra bits */ MATCH, /* o: waiting for output space to copy string */ LIT, /* o: waiting for output space to write literal */ CHECK, /* i: waiting for 32-bit check value */ LENGTH, /* i: waiting for 32-bit length (gzip) */ DONE, /* finished check, done -- remain here until reset */ BAD, /* got a data error -- remain here until reset */ MEM, /* got an inflate() memory error -- remain here until reset */ SYNC /* looking for synchronization bytes to restart inflate() */ } inflate_mode; /* State transitions between above modes - (most modes can go to BAD or MEM on error -- not shown for clarity) Process header: HEAD -> (gzip) or (zlib) or (raw) (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> HCRC -> TYPE (zlib) -> DICTID or TYPE DICTID -> DICT -> TYPE (raw) -> TYPEDO Read deflate blocks: TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK STORED -> COPY_ -> COPY -> TYPE TABLE -> LENLENS -> CODELENS -> LEN_ LEN_ -> LEN Read deflate codes in fixed or dynamic block: LEN -> LENEXT or LIT or TYPE LENEXT -> DIST -> DISTEXT -> MATCH -> LEN LIT -> LEN Process trailer: CHECK -> LENGTH -> DONE */ /* state maintained between inflate() calls. Approximately 10K bytes. */ struct inflate_state { inflate_mode mode; /* current inflate mode */ int last; /* true if processing last block */ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ int havedict; /* true if dictionary provided */ int flags; /* gzip header method and flags (0 if zlib) */ unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ unsigned long check; /* protected copy of check value */ unsigned long total; /* protected copy of output count */ gz_headerp head; /* where to save gzip header information */ /* sliding window */ unsigned wbits; /* log base 2 of requested window size */ unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ unsigned wnext; /* window write index */ unsigned char FAR *window; /* allocated sliding window, if needed */ /* bit accumulator */ unsigned long hold; /* input bit accumulator */ unsigned bits; /* number of bits in "in" */ /* for string and stored block copying */ unsigned length; /* literal or length of data to copy */ unsigned offset; /* distance back to copy string from */ /* for table and code decoding */ unsigned extra; /* extra bits needed */ /* fixed and dynamic code tables */ code const FAR *lencode; /* starting table for length/literal codes */ code const FAR *distcode; /* starting table for distance codes */ unsigned lenbits; /* index bits for lencode */ unsigned distbits; /* index bits for distcode */ /* dynamic table building */ unsigned ncode; /* number of code length code lengths */ unsigned nlen; /* number of length code lengths */ unsigned ndist; /* number of distance code lengths */ unsigned have; /* number of code lengths in lens[] */ code FAR *next; /* next available space in codes[] */ unsigned short lens[320]; /* temporary storage for code lengths */ unsigned short work[288]; /* work area for code table building */ code codes[ENOUGH]; /* space for code tables */ int sane; /* if false, allow invalid distance too far */ int back; /* bits back of last unprocessed length/lit */ unsigned was; /* initial length of match */ }; PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/inftrees.c000066400000000000000000000313441231437614300240470ustar00rootroot00000000000000/* inftrees.c -- generate Huffman trees for efficient decoding * Copyright (C) 1995-2013 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "zutil.h" #include "inftrees.h" #define MAXBITS 15 const char inflate_copyright[] = " inflate 1.2.8 Copyright 1995-2013 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ /* Build a set of tables to decode the provided canonical Huffman code. The code lengths are lens[0..codes-1]. The result starts at *table, whose indices are 0..2^bits-1. work is a writable array of at least lens shorts, which is used as a work area. type is the type of code to be generated, CODES, LENS, or DISTS. On return, zero is success, -1 is an invalid code, and +1 means that ENOUGH isn't enough. table on return points to the next available entry's address. bits is the requested root table index bits, and on return it is the actual root table index bits. It will differ if the request is greater than the longest code or if it is less than the shortest code. */ int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) codetype type; unsigned short FAR *lens; unsigned codes; code FAR * FAR *table; unsigned FAR *bits; unsigned short FAR *work; { unsigned len; /* a code's length in bits */ unsigned sym; /* index of code symbols */ unsigned min, max; /* minimum and maximum code lengths */ unsigned root; /* number of index bits for root table */ unsigned curr; /* number of index bits for current table */ unsigned drop; /* code bits to drop for sub-table */ int left; /* number of prefix codes available */ unsigned used; /* code entries in table used */ unsigned huff; /* Huffman code */ unsigned incr; /* for incrementing code, index */ unsigned fill; /* index for replicating entries */ unsigned low; /* low bits for current root entry */ unsigned mask; /* mask for low root bits */ code here; /* table entry for duplication */ code FAR *next; /* next available space in table */ const unsigned short FAR *base; /* base value table to use */ const unsigned short FAR *extra; /* extra bits table to use */ int end; /* use base and extra for symbol > end */ unsigned short count[MAXBITS+1]; /* number of codes of each length */ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ static const unsigned short lbase[31] = { /* Length codes 257..285 base */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0}; static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 64, 64}; /* Process a set of code lengths to create a canonical Huffman code. The code lengths are lens[0..codes-1]. Each length corresponds to the symbols 0..codes-1. The Huffman code is generated by first sorting the symbols by length from short to long, and retaining the symbol order for codes with equal lengths. Then the code starts with all zero bits for the first code of the shortest length, and the codes are integer increments for the same length, and zeros are appended as the length increases. For the deflate format, these bits are stored backwards from their more natural integer increment ordering, and so when the decoding tables are built in the large loop below, the integer codes are incremented backwards. This routine assumes, but does not check, that all of the entries in lens[] are in the range 0..MAXBITS. The caller must assure this. 1..MAXBITS is interpreted as that code length. zero means that that symbol does not occur in this code. The codes are sorted by computing a count of codes for each length, creating from that a table of starting indices for each length in the sorted table, and then entering the symbols in order in the sorted table. The sorted table is work[], with that space being provided by the caller. The length counts are used for other purposes as well, i.e. finding the minimum and maximum length codes, determining if there are any codes at all, checking for a valid set of lengths, and looking ahead at length counts to determine sub-table sizes when building the decoding tables. */ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ for (len = 0; len <= MAXBITS; len++) count[len] = 0; for (sym = 0; sym < codes; sym++) count[lens[sym]]++; /* bound code lengths, force root to be within code lengths */ root = *bits; for (max = MAXBITS; max >= 1; max--) if (count[max] != 0) break; if (root > max) root = max; if (max == 0) { /* no symbols to code at all */ here.op = (unsigned char)64; /* invalid code marker */ here.bits = (unsigned char)1; here.val = (unsigned short)0; *(*table)++ = here; /* make a table to force an error */ *(*table)++ = here; *bits = 1; return 0; /* no symbols, but wait for decoding to report error */ } for (min = 1; min < max; min++) if (count[min] != 0) break; if (root < min) root = min; /* check for an over-subscribed or incomplete set of lengths */ left = 1; for (len = 1; len <= MAXBITS; len++) { left <<= 1; left -= count[len]; if (left < 0) return -1; /* over-subscribed */ } if (left > 0 && (type == CODES || max != 1)) return -1; /* incomplete set */ /* generate offsets into symbol table for each length for sorting */ offs[1] = 0; for (len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + count[len]; /* sort symbols by length, by symbol order within each length */ for (sym = 0; sym < codes; sym++) if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; /* Create and fill in decoding tables. In this loop, the table being filled is at next and has curr index bits. The code being used is huff with length len. That code is converted to an index by dropping drop bits off of the bottom. For codes where len is less than drop + curr, those top drop + curr - len bits are incremented through all values to fill the table with replicated entries. root is the number of index bits for the root table. When len exceeds root, sub-tables are created pointed to by the root entry with an index of the low root bits of huff. This is saved in low to check for when a new sub-table should be started. drop is zero when the root table is being filled, and drop is root when sub-tables are being filled. When a new sub-table is needed, it is necessary to look ahead in the code lengths to determine what size sub-table is needed. The length counts are used for this, and so count[] is decremented as codes are entered in the tables. used keeps track of how many table entries have been allocated from the provided *table space. It is checked for LENS and DIST tables against the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in the initial root table size constants. See the comments in inftrees.h for more information. sym increments through all symbols, and the loop terminates when all codes of length max, i.e. all codes, have been processed. This routine permits incomplete codes, so another loop after this one fills in the rest of the decoding tables with invalid code markers. */ /* set up for code type */ switch (type) { case CODES: base = extra = work; /* dummy value--not used */ end = 19; break; case LENS: base = lbase; base -= 257; extra = lext; extra -= 257; end = 256; break; default: /* DISTS */ base = dbase; extra = dext; end = -1; } /* initialize state for loop */ huff = 0; /* starting code */ sym = 0; /* starting code symbol */ len = min; /* starting code length */ next = *table; /* current table to fill in */ curr = root; /* current table index bits */ drop = 0; /* current bits to drop from code for index */ low = (unsigned)(-1); /* trigger new sub-table when len > root */ used = 1U << root; /* use root table entries */ mask = used - 1; /* mask for comparing low */ /* check available table space */ if ((type == LENS && used > ENOUGH_LENS) || (type == DISTS && used > ENOUGH_DISTS)) return 1; /* process all codes and make table entries */ for (;;) { /* create table entry */ here.bits = (unsigned char)(len - drop); if ((int)(work[sym]) < end) { here.op = (unsigned char)0; here.val = work[sym]; } else if ((int)(work[sym]) > end) { here.op = (unsigned char)(extra[work[sym]]); here.val = base[work[sym]]; } else { here.op = (unsigned char)(32 + 64); /* end of block */ here.val = 0; } /* replicate for those indices with low len bits equal to huff */ incr = 1U << (len - drop); fill = 1U << curr; min = fill; /* save offset to next table */ do { fill -= incr; next[(huff >> drop) + fill] = here; } while (fill != 0); /* backwards increment the len-bit code huff */ incr = 1U << (len - 1); while (huff & incr) incr >>= 1; if (incr != 0) { huff &= incr - 1; huff += incr; } else huff = 0; /* go to next symbol, update count, len */ sym++; if (--(count[len]) == 0) { if (len == max) break; len = lens[work[sym]]; } /* create new sub-table if needed */ if (len > root && (huff & mask) != low) { /* if first time, transition to sub-tables */ if (drop == 0) drop = root; /* increment past last table */ next += min; /* here min is 1 << curr */ /* determine length of next table */ curr = len - drop; left = (int)(1 << curr); while (curr + drop < max) { left -= count[curr + drop]; if (left <= 0) break; curr++; left <<= 1; } /* check for enough space */ used += 1U << curr; if ((type == LENS && used > ENOUGH_LENS) || (type == DISTS && used > ENOUGH_DISTS)) return 1; /* point entry in root table to sub-table */ low = huff & mask; (*table)[low].op = (unsigned char)curr; (*table)[low].bits = (unsigned char)root; (*table)[low].val = (unsigned short)(next - *table); } } /* fill in remaining table entry if code is incomplete (guaranteed to have at most one remaining entry, since if the code is incomplete, the maximum code length that was allowed to get this far is one bit) */ if (huff != 0) { here.op = (unsigned char)64; /* invalid code marker */ here.bits = (unsigned char)(len - drop); here.val = (unsigned short)0; next[huff] = here; } /* set return parameters */ *table += used; *bits = root; return 0; } PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/inftrees.h000066400000000000000000000055601231437614300240550ustar00rootroot00000000000000/* inftrees.h -- header to use inftrees.c * Copyright (C) 1995-2005, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* Structure for decoding tables. Each entry provides either the information needed to do the operation requested by the code that indexed that table entry, or it provides a pointer to another table that indexes more bits of the code. op indicates whether the entry is a pointer to another table, a literal, a length or distance, an end-of-block, or an invalid code. For a table pointer, the low four bits of op is the number of index bits of that table. For a length or distance, the low four bits of op is the number of extra bits to get after the code. bits is the number of bits in this code or part of the code to drop off of the bit buffer. val is the actual byte to output in the case of a literal, the base length or distance, or the offset from the current table to the next table. Each entry is four bytes. */ typedef struct { unsigned char op; /* operation, extra bits, table bits */ unsigned char bits; /* bits in this part of the code */ unsigned short val; /* offset in table or code value */ } code; /* op values as set by inflate_table(): 00000000 - literal 0000tttt - table link, tttt != 0 is the number of table index bits 0001eeee - length or distance, eeee is the number of extra bits 01100000 - end of block 01000000 - invalid code */ /* Maximum size of the dynamic table. The maximum number of code structures is 1444, which is the sum of 852 for literal/length codes and 592 for distance codes. These values were found by exhaustive searches using the program examples/enough.c found in the zlib distribtution. The arguments to that program are the number of symbols, the initial root table size, and the maximum bit length of a code. "enough 286 9 15" for literal/length codes returns returns 852, and "enough 30 6 15" for distance codes returns 592. The initial root table size (9 or 6) is found in the fifth argument of the inflate_table() calls in inflate.c and infback.c. If the root table size is changed, then these maximum sizes would be need to be recalculated and updated. */ #define ENOUGH_LENS 852 #define ENOUGH_DISTS 592 #define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) /* Type of code to build for inflate_table() */ typedef enum { CODES, LENS, DISTS } codetype; int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, unsigned codes, code FAR * FAR *table, unsigned FAR *bits, unsigned short FAR *work)); PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/trees.c000066400000000000000000001263371231437614300233610ustar00rootroot00000000000000/* trees.c -- output deflated data using Huffman coding * Copyright (C) 1995-2012 Jean-loup Gailly * detect_data_type() function provided freely by Cosmin Truta, 2006 * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process uses several Huffman trees. The more * common source values are represented by shorter bit sequences. * * Each code tree is stored in a compressed form which is itself * a Huffman encoding of the lengths of all the code strings (in * ascending order by source values). The actual code strings are * reconstructed from the lengths in the inflate process, as described * in the deflate specification. * * REFERENCES * * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc * * Storer, James A. * Data Compression: Methods and Theory, pp. 49-50. * Computer Science Press, 1988. ISBN 0-7167-8156-5. * * Sedgewick, R. * Algorithms, p290. * Addison-Wesley, 1983. ISBN 0-201-06672-6. */ /* @(#) $Id$ */ /* #define GEN_TREES_H */ #include "deflate.h" #ifdef DEBUG # include #endif /* =========================================================================== * Constants */ #define MAX_BL_BITS 7 /* Bit length codes must not exceed MAX_BL_BITS bits */ #define END_BLOCK 256 /* end of block literal code */ #define REP_3_6 16 /* repeat previous bit length 3-6 times (2 bits of repeat count) */ #define REPZ_3_10 17 /* repeat a zero length 3-10 times (3 bits of repeat count) */ #define REPZ_11_138 18 /* repeat a zero length 11-138 times (7 bits of repeat count) */ local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; local const int extra_dbits[D_CODES] /* extra bits for each distance code */ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; local const uch bl_order[BL_CODES] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; /* The lengths of the bit length codes are sent in order of decreasing * probability, to avoid transmitting the lengths for unused bit length codes. */ /* =========================================================================== * Local data. These are initialized only once. */ #define DIST_CODE_LEN 512 /* see definition of array dist_code below */ #if defined(GEN_TREES_H) || !defined(STDC) /* non ANSI compilers may not accept trees.h */ local ct_data static_ltree[L_CODES+2]; /* The static literal tree. Since the bit lengths are imposed, there is no * need for the L_CODES extra codes used during heap construction. However * The codes 286 and 287 are needed to build a canonical tree (see _tr_init * below). */ local ct_data static_dtree[D_CODES]; /* The static distance tree. (Actually a trivial tree since all codes use * 5 bits.) */ uch _dist_code[DIST_CODE_LEN]; /* Distance codes. The first 256 values correspond to the distances * 3 .. 258, the last 256 values correspond to the top 8 bits of * the 15 bit distances. */ uch _length_code[MAX_MATCH-MIN_MATCH+1]; /* length code for each normalized match length (0 == MIN_MATCH) */ local int base_length[LENGTH_CODES]; /* First normalized length for each code (0 = MIN_MATCH) */ local int base_dist[D_CODES]; /* First normalized distance for each code (0 = distance of 1) */ #else # include "trees.h" #endif /* GEN_TREES_H */ struct static_tree_desc_s { const ct_data *static_tree; /* static tree or NULL */ const intf *extra_bits; /* extra bits for each code or NULL */ int extra_base; /* base index for extra_bits */ int elems; /* max number of elements in the tree */ int max_length; /* max bit length for the codes */ }; local static_tree_desc static_l_desc = {static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; local static_tree_desc static_d_desc = {static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; local static_tree_desc static_bl_desc = {(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; /* =========================================================================== * Local (static) routines in this file. */ local void tr_static_init OF((void)); local void init_block OF((deflate_state *s)); local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); local void build_tree OF((deflate_state *s, tree_desc *desc)); local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); local int build_bl_tree OF((deflate_state *s)); local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, int blcodes)); local void compress_block OF((deflate_state *s, const ct_data *ltree, const ct_data *dtree)); local int detect_data_type OF((deflate_state *s)); local unsigned bi_reverse OF((unsigned value, int length)); local void bi_windup OF((deflate_state *s)); local void bi_flush OF((deflate_state *s)); local void copy_block OF((deflate_state *s, charf *buf, unsigned len, int header)); #ifdef GEN_TREES_H local void gen_trees_header OF((void)); #endif #ifndef DEBUG # define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) /* Send a code of the given tree. c and tree must not have side effects */ #else /* DEBUG */ # define send_code(s, c, tree) \ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ send_bits(s, tree[c].Code, tree[c].Len); } #endif /* =========================================================================== * Output a short LSB first on the stream. * IN assertion: there is enough room in pendingBuf. */ #define put_short(s, w) { \ put_byte(s, (uch)((w) & 0xff)); \ put_byte(s, (uch)((ush)(w) >> 8)); \ } /* =========================================================================== * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ #ifdef DEBUG local void send_bits OF((deflate_state *s, int value, int length)); local void send_bits(s, value, length) deflate_state *s; int value; /* value to send */ int length; /* number of bits */ { Tracevv((stderr," l %2d v %4x ", length, value)); Assert(length > 0 && length <= 15, "invalid length"); s->bits_sent += (ulg)length; /* If not enough room in bi_buf, use (valid) bits from bi_buf and * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) * unused bits in value. */ if (s->bi_valid > (int)Buf_size - length) { s->bi_buf |= (ush)value << s->bi_valid; put_short(s, s->bi_buf); s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); s->bi_valid += length - Buf_size; } else { s->bi_buf |= (ush)value << s->bi_valid; s->bi_valid += length; } } #else /* !DEBUG */ #define send_bits(s, value, length) \ { int len = length;\ if (s->bi_valid > (int)Buf_size - len) {\ int val = value;\ s->bi_buf |= (ush)val << s->bi_valid;\ put_short(s, s->bi_buf);\ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ s->bi_valid += len - Buf_size;\ } else {\ s->bi_buf |= (ush)(value) << s->bi_valid;\ s->bi_valid += len;\ }\ } #endif /* DEBUG */ /* the arguments must not have side effects */ /* =========================================================================== * Initialize the various 'constant' tables. */ local void tr_static_init() { #if defined(GEN_TREES_H) || !defined(STDC) static int static_init_done = 0; int n; /* iterates over tree elements */ int bits; /* bit counter */ int length; /* length value */ int code; /* code value */ int dist; /* distance index */ ush bl_count[MAX_BITS+1]; /* number of codes at each bit length for an optimal tree */ if (static_init_done) return; /* For some embedded targets, global variables are not initialized: */ #ifdef NO_INIT_GLOBAL_POINTERS static_l_desc.static_tree = static_ltree; static_l_desc.extra_bits = extra_lbits; static_d_desc.static_tree = static_dtree; static_d_desc.extra_bits = extra_dbits; static_bl_desc.extra_bits = extra_blbits; #endif /* Initialize the mapping length (0..255) -> length code (0..28) */ length = 0; for (code = 0; code < LENGTH_CODES-1; code++) { base_length[code] = length; for (n = 0; n < (1< dist code (0..29) */ dist = 0; for (code = 0 ; code < 16; code++) { base_dist[code] = dist; for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ for ( ; code < D_CODES; code++) { base_dist[code] = dist << 7; for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { _dist_code[256 + dist++] = (uch)code; } } Assert (dist == 256, "tr_static_init: 256+dist != 512"); /* Construct the codes of the static literal tree */ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; n = 0; while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; /* Codes 286 and 287 do not exist, but we must include them in the * tree construction to get a canonical Huffman tree (longest code * all ones) */ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); /* The static distance tree is trivial: */ for (n = 0; n < D_CODES; n++) { static_dtree[n].Len = 5; static_dtree[n].Code = bi_reverse((unsigned)n, 5); } static_init_done = 1; # ifdef GEN_TREES_H gen_trees_header(); # endif #endif /* defined(GEN_TREES_H) || !defined(STDC) */ } /* =========================================================================== * Genererate the file trees.h describing the static trees. */ #ifdef GEN_TREES_H # ifndef DEBUG # include # endif # define SEPARATOR(i, last, width) \ ((i) == (last)? "\n};\n\n" : \ ((i) % (width) == (width)-1 ? ",\n" : ", ")) void gen_trees_header() { FILE *header = fopen("trees.h", "w"); int i; Assert (header != NULL, "Can't open trees.h"); fprintf(header, "/* header created automatically with -DGEN_TREES_H */\n\n"); fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); for (i = 0; i < L_CODES+2; i++) { fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); } fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); for (i = 0; i < D_CODES; i++) { fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); } fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); for (i = 0; i < DIST_CODE_LEN; i++) { fprintf(header, "%2u%s", _dist_code[i], SEPARATOR(i, DIST_CODE_LEN-1, 20)); } fprintf(header, "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { fprintf(header, "%2u%s", _length_code[i], SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); } fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); for (i = 0; i < LENGTH_CODES; i++) { fprintf(header, "%1u%s", base_length[i], SEPARATOR(i, LENGTH_CODES-1, 20)); } fprintf(header, "local const int base_dist[D_CODES] = {\n"); for (i = 0; i < D_CODES; i++) { fprintf(header, "%5u%s", base_dist[i], SEPARATOR(i, D_CODES-1, 10)); } fclose(header); } #endif /* GEN_TREES_H */ /* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ void ZLIB_INTERNAL _tr_init(s) deflate_state *s; { tr_static_init(); s->l_desc.dyn_tree = s->dyn_ltree; s->l_desc.stat_desc = &static_l_desc; s->d_desc.dyn_tree = s->dyn_dtree; s->d_desc.stat_desc = &static_d_desc; s->bl_desc.dyn_tree = s->bl_tree; s->bl_desc.stat_desc = &static_bl_desc; s->bi_buf = 0; s->bi_valid = 0; #ifdef DEBUG s->compressed_len = 0L; s->bits_sent = 0L; #endif /* Initialize the first block of the first file: */ init_block(s); } /* =========================================================================== * Initialize a new block. */ local void init_block(s) deflate_state *s; { int n; /* iterates over tree elements */ /* Initialize the trees. */ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; s->dyn_ltree[END_BLOCK].Freq = 1; s->opt_len = s->static_len = 0L; s->last_lit = s->matches = 0; } #define SMALLEST 1 /* Index within the heap array of least frequent node in the Huffman tree */ /* =========================================================================== * Remove the smallest element from the heap and recreate the heap with * one less element. Updates heap and heap_len. */ #define pqremove(s, tree, top) \ {\ top = s->heap[SMALLEST]; \ s->heap[SMALLEST] = s->heap[s->heap_len--]; \ pqdownheap(s, tree, SMALLEST); \ } /* =========================================================================== * Compares to subtrees, using the tree depth as tie breaker when * the subtrees have equal frequency. This minimizes the worst case length. */ #define smaller(tree, n, m, depth) \ (tree[n].Freq < tree[m].Freq || \ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) /* =========================================================================== * Restore the heap property by moving down the tree starting at node k, * exchanging a node with the smallest of its two sons if necessary, stopping * when the heap property is re-established (each father smaller than its * two sons). */ local void pqdownheap(s, tree, k) deflate_state *s; ct_data *tree; /* the tree to restore */ int k; /* node to move down */ { int v = s->heap[k]; int j = k << 1; /* left son of k */ while (j <= s->heap_len) { /* Set j to the smallest of the two sons: */ if (j < s->heap_len && smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { j++; } /* Exit if v is smaller than both sons */ if (smaller(tree, v, s->heap[j], s->depth)) break; /* Exchange v with the smallest son */ s->heap[k] = s->heap[j]; k = j; /* And continue down the tree, setting j to the left son of k */ j <<= 1; } s->heap[k] = v; } /* =========================================================================== * Compute the optimal bit lengths for a tree and update the total bit length * for the current block. * IN assertion: the fields freq and dad are set, heap[heap_max] and * above are the tree nodes sorted by increasing frequency. * OUT assertions: the field len is set to the optimal bit length, the * array bl_count contains the frequencies for each bit length. * The length opt_len is updated; static_len is also updated if stree is * not null. */ local void gen_bitlen(s, desc) deflate_state *s; tree_desc *desc; /* the tree descriptor */ { ct_data *tree = desc->dyn_tree; int max_code = desc->max_code; const ct_data *stree = desc->stat_desc->static_tree; const intf *extra = desc->stat_desc->extra_bits; int base = desc->stat_desc->extra_base; int max_length = desc->stat_desc->max_length; int h; /* heap index */ int n, m; /* iterate over the tree elements */ int bits; /* bit length */ int xbits; /* extra bits */ ush f; /* frequency */ int overflow = 0; /* number of elements with bit length too large */ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; /* In a first pass, compute the optimal bit lengths (which may * overflow in the case of the bit length tree). */ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ for (h = s->heap_max+1; h < HEAP_SIZE; h++) { n = s->heap[h]; bits = tree[tree[n].Dad].Len + 1; if (bits > max_length) bits = max_length, overflow++; tree[n].Len = (ush)bits; /* We overwrite tree[n].Dad which is no longer needed */ if (n > max_code) continue; /* not a leaf node */ s->bl_count[bits]++; xbits = 0; if (n >= base) xbits = extra[n-base]; f = tree[n].Freq; s->opt_len += (ulg)f * (bits + xbits); if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); } if (overflow == 0) return; Trace((stderr,"\nbit length overflow\n")); /* This happens for example on obj2 and pic of the Calgary corpus */ /* Find the first bit length which could increase: */ do { bits = max_length-1; while (s->bl_count[bits] == 0) bits--; s->bl_count[bits]--; /* move one leaf down the tree */ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ s->bl_count[max_length]--; /* The brother of the overflow item also moves one step up, * but this does not affect bl_count[max_length] */ overflow -= 2; } while (overflow > 0); /* Now recompute all bit lengths, scanning in increasing frequency. * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all * lengths instead of fixing only the wrong ones. This idea is taken * from 'ar' written by Haruhiko Okumura.) */ for (bits = max_length; bits != 0; bits--) { n = s->bl_count[bits]; while (n != 0) { m = s->heap[--h]; if (m > max_code) continue; if ((unsigned) tree[m].Len != (unsigned) bits) { Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); s->opt_len += ((long)bits - (long)tree[m].Len) *(long)tree[m].Freq; tree[m].Len = (ush)bits; } n--; } } } /* =========================================================================== * Generate the codes for a given tree and bit counts (which need not be * optimal). * IN assertion: the array bl_count contains the bit length statistics for * the given tree and the field len is set for all tree elements. * OUT assertion: the field code is set for all tree elements of non * zero code length. */ local void gen_codes (tree, max_code, bl_count) ct_data *tree; /* the tree to decorate */ int max_code; /* largest code with non zero frequency */ ushf *bl_count; /* number of codes at each bit length */ { ush next_code[MAX_BITS+1]; /* next code value for each bit length */ ush code = 0; /* running code value */ int bits; /* bit index */ int n; /* code index */ /* The distribution counts are first used to generate the code values * without bit reversal. */ for (bits = 1; bits <= MAX_BITS; bits++) { next_code[bits] = code = (code + bl_count[bits-1]) << 1; } /* Check that the bit counts in bl_count are consistent. The last code * must be all ones. */ Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; const ct_data *stree = desc->stat_desc->static_tree; int elems = desc->stat_desc->elems; int n, m; /* iterate over heap elements */ int max_code = -1; /* largest code with non zero frequency */ int node; /* new node being created */ /* Construct the initial heap, with least frequent element in * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. * heap[0] is not used. */ s->heap_len = 0, s->heap_max = HEAP_SIZE; for (n = 0; n < elems; n++) { if (tree[n].Freq != 0) { s->heap[++(s->heap_len)] = max_code = n; s->depth[n] = 0; } else { tree[n].Len = 0; } } /* The pkzip format requires that at least one distance code exists, * and that at least one bit should be sent even if there is only one * possible code. So to avoid special checks later on we force at least * two codes of non zero frequency. */ while (s->heap_len < 2) { node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); tree[node].Freq = 1; s->depth[node] = 0; s->opt_len--; if (stree) s->static_len -= stree[node].Len; /* node is 0 or 1 so it does not have extra bits */ } desc->max_code = max_code; /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, * establish sub-heaps of increasing lengths: */ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); /* Construct the Huffman tree by repeatedly combining the least two * frequent nodes. */ node = elems; /* next internal node of the tree */ do { pqremove(s, tree, n); /* n = node of least frequency */ m = s->heap[SMALLEST]; /* m = node of next least frequency */ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ s->heap[--(s->heap_max)] = m; /* Create a new node father of n and m */ tree[node].Freq = tree[n].Freq + tree[m].Freq; s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? s->depth[n] : s->depth[m]) + 1); tree[n].Dad = tree[m].Dad = (ush)node; #ifdef DUMP_BL_TREE if (tree == s->bl_tree) { fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); } #endif /* and insert the new node in the heap */ s->heap[SMALLEST] = node++; pqdownheap(s, tree, SMALLEST); } while (s->heap_len >= 2); s->heap[--(s->heap_max)] = s->heap[SMALLEST]; /* At this point, the fields freq and dad are set. We can now * generate the bit lengths. */ gen_bitlen(s, (tree_desc *)desc); /* The field len is now set, we can generate the bit codes */ gen_codes ((ct_data *)tree, max_code, s->bl_count); } /* =========================================================================== * Scan a literal or distance tree to determine the frequencies of the codes * in the bit length tree. */ local void scan_tree (s, tree, max_code) deflate_state *s; ct_data *tree; /* the tree to be scanned */ int max_code; /* and its largest code of non zero frequency */ { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ int nextlen = tree[0].Len; /* length of next code */ int count = 0; /* repeat count of the current code */ int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ if (nextlen == 0) max_count = 138, min_count = 3; tree[max_code+1].Len = (ush)0xffff; /* guard */ for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[n+1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { s->bl_tree[curlen].Freq += count; } else if (curlen != 0) { if (curlen != prevlen) s->bl_tree[curlen].Freq++; s->bl_tree[REP_3_6].Freq++; } else if (count <= 10) { s->bl_tree[REPZ_3_10].Freq++; } else { s->bl_tree[REPZ_11_138].Freq++; } count = 0; prevlen = curlen; if (nextlen == 0) { max_count = 138, min_count = 3; } else if (curlen == nextlen) { max_count = 6, min_count = 3; } else { max_count = 7, min_count = 4; } } } /* =========================================================================== * Send a literal or distance tree in compressed form, using the codes in * bl_tree. */ local void send_tree (s, tree, max_code) deflate_state *s; ct_data *tree; /* the tree to be scanned */ int max_code; /* and its largest code of non zero frequency */ { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ int nextlen = tree[0].Len; /* length of next code */ int count = 0; /* repeat count of the current code */ int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ /* tree[max_code+1].Len = -1; */ /* guard already set */ if (nextlen == 0) max_count = 138, min_count = 3; for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[n+1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { do { send_code(s, curlen, s->bl_tree); } while (--count != 0); } else if (curlen != 0) { if (curlen != prevlen) { send_code(s, curlen, s->bl_tree); count--; } Assert(count >= 3 && count <= 6, " 3_6?"); send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); } else if (count <= 10) { send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); } else { send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); } count = 0; prevlen = curlen; if (nextlen == 0) { max_count = 138, min_count = 3; } else if (curlen == nextlen) { max_count = 6, min_count = 3; } else { max_count = 7, min_count = 4; } } } /* =========================================================================== * Construct the Huffman tree for the bit lengths and return the index in * bl_order of the last bit length code to send. */ local int build_bl_tree(s) deflate_state *s; { int max_blindex; /* index of last bit length code of non zero freq */ /* Determine the bit length frequencies for literal and distance trees */ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); /* Build the bit length tree: */ build_tree(s, (tree_desc *)(&(s->bl_desc))); /* opt_len now includes the length of the tree representations, except * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. */ /* Determine the number of bit length codes to send. The pkzip format * requires that at least 4 bit length codes be sent. (appnote.txt says * 3 but the actual value used is 4.) */ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; } /* Update opt_len to include the bit length tree and counts */ s->opt_len += 3*(max_blindex+1) + 5+5+4; Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", s->opt_len, s->static_len)); return max_blindex; } /* =========================================================================== * Send the header for a block using dynamic Huffman trees: the counts, the * lengths of the bit length codes, the literal tree and the distance tree. * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. */ local void send_all_trees(s, lcodes, dcodes, blcodes) deflate_state *s; int lcodes, dcodes, blcodes; /* number of codes for each tree */ { int rank; /* index in bl_order */ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, "too many codes"); Tracev((stderr, "\nbl counts: ")); send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ send_bits(s, dcodes-1, 5); send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ for (rank = 0; rank < blcodes; rank++) { Tracev((stderr, "\nbl code %2d ", bl_order[rank])); send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); } Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); } /* =========================================================================== * Send a stored block */ void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) deflate_state *s; charf *buf; /* input block */ ulg stored_len; /* length of input block */ int last; /* one if this is the last block for a file */ { send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ #ifdef DEBUG s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; s->compressed_len += (stored_len + 4) << 3; #endif copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ } /* =========================================================================== * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) */ void ZLIB_INTERNAL _tr_flush_bits(s) deflate_state *s; { bi_flush(s); } /* =========================================================================== * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. */ void ZLIB_INTERNAL _tr_align(s) deflate_state *s; { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); #ifdef DEBUG s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ #endif bi_flush(s); } /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static * trees or store, and output the encoded block to the zip file. */ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) deflate_state *s; charf *buf; /* input block, or NULL if too old */ ulg stored_len; /* length of input block */ int last; /* one if this is the last block for a file */ { ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ int max_blindex = 0; /* index of last bit length code of non zero freq */ /* Build the Huffman trees unless a stored block is forced */ if (s->level > 0) { /* Check if the file is binary or text */ if (s->strm->data_type == Z_UNKNOWN) s->strm->data_type = detect_data_type(s); /* Construct the literal and distance trees */ build_tree(s, (tree_desc *)(&(s->l_desc))); Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, s->static_len)); build_tree(s, (tree_desc *)(&(s->d_desc))); Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, s->static_len)); /* At this point, opt_len and static_len are the total bit lengths of * the compressed block data, excluding the tree representations. */ /* Build the bit length tree for the above two trees, and get the index * in bl_order of the last bit length code to send. */ max_blindex = build_bl_tree(s); /* Determine the best encoding. Compute the block lengths in bytes. */ opt_lenb = (s->opt_len+3+7)>>3; static_lenb = (s->static_len+3+7)>>3; Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, s->last_lit)); if (static_lenb <= opt_lenb) opt_lenb = static_lenb; } else { Assert(buf != (char*)0, "lost buf"); opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ } #ifdef FORCE_STORED if (buf != (char*)0) { /* force stored block */ #else if (stored_len+4 <= opt_lenb && buf != (char*)0) { /* 4: two words for the lengths */ #endif /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. * Otherwise we can't have processed more than WSIZE input bytes since * the last block flush, because compression would have been * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to * transform a block into a stored block. */ _tr_stored_block(s, buf, stored_len, last); #ifdef FORCE_STATIC } else if (static_lenb >= 0) { /* force static trees */ #else } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { #endif send_bits(s, (STATIC_TREES<<1)+last, 3); compress_block(s, (const ct_data *)static_ltree, (const ct_data *)static_dtree); #ifdef DEBUG s->compressed_len += 3 + s->static_len; #endif } else { send_bits(s, (DYN_TREES<<1)+last, 3); send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, max_blindex+1); compress_block(s, (const ct_data *)s->dyn_ltree, (const ct_data *)s->dyn_dtree); #ifdef DEBUG s->compressed_len += 3 + s->opt_len; #endif } Assert (s->compressed_len == s->bits_sent, "bad compressed size"); /* The above check is made mod 2^32, for files larger than 512 MB * and uLong implemented on 32 bits. */ init_block(s); if (last) { bi_windup(s); #ifdef DEBUG s->compressed_len += 7; /* align on byte boundary */ #endif } Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, s->compressed_len-7*last)); } /* =========================================================================== * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ int ZLIB_INTERNAL _tr_tally (s, dist, lc) deflate_state *s; unsigned dist; /* distance of matched string */ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ { s->d_buf[s->last_lit] = (ush)dist; s->l_buf[s->last_lit++] = (uch)lc; if (dist == 0) { /* lc is the unmatched char */ s->dyn_ltree[lc].Freq++; } else { s->matches++; /* Here, lc is the match length - MIN_MATCH */ dist--; /* dist = match distance - 1 */ Assert((ush)dist < (ush)MAX_DIST(s) && (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; s->dyn_dtree[d_code(dist)].Freq++; } #ifdef TRUNCATE_BLOCK /* Try to guess if it is profitable to stop the current block here */ if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { /* Compute an upper bound for the compressed length */ ulg out_length = (ulg)s->last_lit*8L; ulg in_length = (ulg)((long)s->strstart - s->block_start); int dcode; for (dcode = 0; dcode < D_CODES; dcode++) { out_length += (ulg)s->dyn_dtree[dcode].Freq * (5L+extra_dbits[dcode]); } out_length >>= 3; Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", s->last_lit, in_length, out_length, 100L - out_length*100L/in_length)); if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; } #endif return (s->last_lit == s->lit_bufsize-1); /* We avoid equality with lit_bufsize because of wraparound at 64K * on 16 bit machines and because stored blocks are restricted to * 64K-1 bytes. */ } /* =========================================================================== * Send the block data compressed using the given Huffman trees */ local void compress_block(s, ltree, dtree) deflate_state *s; const ct_data *ltree; /* literal tree */ const ct_data *dtree; /* distance tree */ { unsigned dist; /* distance of matched string */ int lc; /* match length or unmatched char (if dist == 0) */ unsigned lx = 0; /* running index in l_buf */ unsigned code; /* the code to send */ int extra; /* number of extra bits to send */ if (s->last_lit != 0) do { dist = s->d_buf[lx]; lc = s->l_buf[lx++]; if (dist == 0) { send_code(s, lc, ltree); /* send a literal byte */ Tracecv(isgraph(lc), (stderr," '%c' ", lc)); } else { /* Here, lc is the match length - MIN_MATCH */ code = _length_code[lc]; send_code(s, code+LITERALS+1, ltree); /* send the length code */ extra = extra_lbits[code]; if (extra != 0) { lc -= base_length[code]; send_bits(s, lc, extra); /* send the extra length bits */ } dist--; /* dist is now the match distance - 1 */ code = d_code(dist); Assert (code < D_CODES, "bad d_code"); send_code(s, code, dtree); /* send the distance code */ extra = extra_dbits[code]; if (extra != 0) { dist -= base_dist[code]; send_bits(s, dist, extra); /* send the extra distance bits */ } } /* literal or match pair ? */ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, "pendingBuf overflow"); } while (lx < s->last_lit); send_code(s, END_BLOCK, ltree); } /* =========================================================================== * Check if the data type is TEXT or BINARY, using the following algorithm: * - TEXT if the two conditions below are satisfied: * a) There are no non-portable control characters belonging to the * "black list" (0..6, 14..25, 28..31). * b) There is at least one printable character belonging to the * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). * - BINARY otherwise. * - The following partially-portable control characters form a * "gray list" that is ignored in this detection algorithm: * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). * IN assertion: the fields Freq of dyn_ltree are set. */ local int detect_data_type(s) deflate_state *s; { /* black_mask is the bit mask of black-listed bytes * set bits 0..6, 14..25, and 28..31 * 0xf3ffc07f = binary 11110011111111111100000001111111 */ unsigned long black_mask = 0xf3ffc07fUL; int n; /* Check for non-textual ("black-listed") bytes. */ for (n = 0; n <= 31; n++, black_mask >>= 1) if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) return Z_BINARY; /* Check for textual ("white-listed") bytes. */ if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 || s->dyn_ltree[13].Freq != 0) return Z_TEXT; for (n = 32; n < LITERALS; n++) if (s->dyn_ltree[n].Freq != 0) return Z_TEXT; /* There are no "black-listed" or "white-listed" bytes: * this stream either is empty or has tolerated ("gray-listed") bytes only. */ return Z_BINARY; } /* =========================================================================== * Reverse the first len bits of a code, using straightforward code (a faster * method would use a table) * IN assertion: 1 <= len <= 15 */ local unsigned bi_reverse(code, len) unsigned code; /* the value to invert */ int len; /* its bit length */ { register unsigned res = 0; do { res |= code & 1; code >>= 1, res <<= 1; } while (--len > 0); return res >> 1; } /* =========================================================================== * Flush the bit buffer, keeping at most 7 bits in it. */ local void bi_flush(s) deflate_state *s; { if (s->bi_valid == 16) { put_short(s, s->bi_buf); s->bi_buf = 0; s->bi_valid = 0; } else if (s->bi_valid >= 8) { put_byte(s, (Byte)s->bi_buf); s->bi_buf >>= 8; s->bi_valid -= 8; } } /* =========================================================================== * Flush the bit buffer and align the output on a byte boundary */ local void bi_windup(s) deflate_state *s; { if (s->bi_valid > 8) { put_short(s, s->bi_buf); } else if (s->bi_valid > 0) { put_byte(s, (Byte)s->bi_buf); } s->bi_buf = 0; s->bi_valid = 0; #ifdef DEBUG s->bits_sent = (s->bits_sent+7) & ~7; #endif } /* =========================================================================== * Copy a stored block, storing first the length and its * one's complement if requested. */ local void copy_block(s, buf, len, header) deflate_state *s; charf *buf; /* the input data */ unsigned len; /* its length */ int header; /* true if block header must be written */ { bi_windup(s); /* align on byte boundary */ if (header) { put_short(s, (ush)len); put_short(s, (ush)~len); #ifdef DEBUG s->bits_sent += 2*16; #endif } #ifdef DEBUG s->bits_sent += (ulg)len<<3; #endif while (len--) { put_byte(s, *buf++); } } PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/trees.h000066400000000000000000000204301231437614300233510ustar00rootroot00000000000000/* header created automatically with -DGEN_TREES_H */ local const ct_data static_ltree[L_CODES+2] = { {{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, {{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, {{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, {{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, {{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, {{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, {{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, {{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, {{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, {{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, {{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, {{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, {{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, {{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, {{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, {{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, {{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, {{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, {{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, {{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, {{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, {{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, {{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, {{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, {{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, {{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, {{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, {{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, {{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, {{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, {{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, {{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, {{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, {{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, {{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, {{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, {{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, {{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, {{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, {{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, {{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, {{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, {{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, {{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, {{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, {{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, {{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, {{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, {{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, {{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, {{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, {{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, {{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, {{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, {{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, {{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, {{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, {{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} }; local const ct_data static_dtree[D_CODES] = { {{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, {{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, {{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, {{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, {{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, {{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} }; const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 }; const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 }; local const int base_length[LENGTH_CODES] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0 }; local const int base_dist[D_CODES] = { 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 }; PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/uncompr.c000066400000000000000000000037231231437614300237130ustar00rootroot00000000000000/* uncompr.c -- decompress a memory buffer * Copyright (C) 1995-2003, 2010 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #define ZLIB_INTERNAL #include "zlib.h" /* =========================================================================== Decompresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be large enough to hold the entire uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.) Upon exit, destLen is the actual size of the compressed buffer. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, or Z_DATA_ERROR if the input data was corrupted. */ int ZEXPORT uncompress (dest, destLen, source, sourceLen) Bytef *dest; uLongf *destLen; const Bytef *source; uLong sourceLen; { z_stream stream; int err; stream.next_in = (z_const Bytef *)source; stream.avail_in = (uInt)sourceLen; /* Check for source > 64K on 16-bit machine: */ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; stream.next_out = dest; stream.avail_out = (uInt)*destLen; if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; err = inflateInit(&stream); if (err != Z_OK) return err; err = inflate(&stream, Z_FINISH); if (err != Z_STREAM_END) { inflateEnd(&stream); if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) return Z_DATA_ERROR; return err; } *destLen = stream.total_out; err = inflateEnd(&stream); return err; } PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/zconf.h000066400000000000000000000362241231437614300233560ustar00rootroot00000000000000/* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-2013 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #ifndef ZCONF_H #define ZCONF_H /* * If you *really* need a unique prefix for all types and library functions, * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. * Even better than compiling with -DZ_PREFIX would be to use configure to set * this permanently in zconf.h using "./configure --zprefix". */ #ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ # define Z_PREFIX_SET /* all linked symbols */ # define _dist_code z__dist_code # define _length_code z__length_code # define _tr_align z__tr_align # define _tr_flush_bits z__tr_flush_bits # define _tr_flush_block z__tr_flush_block # define _tr_init z__tr_init # define _tr_stored_block z__tr_stored_block # define _tr_tally z__tr_tally # define adler32 z_adler32 # define adler32_combine z_adler32_combine # define adler32_combine64 z_adler32_combine64 # ifndef Z_SOLO # define compress z_compress # define compress2 z_compress2 # define compressBound z_compressBound # endif # define crc32 z_crc32 # define crc32_combine z_crc32_combine # define crc32_combine64 z_crc32_combine64 # define deflate z_deflate # define deflateBound z_deflateBound # define deflateCopy z_deflateCopy # define deflateEnd z_deflateEnd # define deflateInit2_ z_deflateInit2_ # define deflateInit_ z_deflateInit_ # define deflateParams z_deflateParams # define deflatePending z_deflatePending # define deflatePrime z_deflatePrime # define deflateReset z_deflateReset # define deflateResetKeep z_deflateResetKeep # define deflateSetDictionary z_deflateSetDictionary # define deflateSetHeader z_deflateSetHeader # define deflateTune z_deflateTune # define deflate_copyright z_deflate_copyright # define get_crc_table z_get_crc_table # ifndef Z_SOLO # define gz_error z_gz_error # define gz_intmax z_gz_intmax # define gz_strwinerror z_gz_strwinerror # define gzbuffer z_gzbuffer # define gzclearerr z_gzclearerr # define gzclose z_gzclose # define gzclose_r z_gzclose_r # define gzclose_w z_gzclose_w # define gzdirect z_gzdirect # define gzdopen z_gzdopen # define gzeof z_gzeof # define gzerror z_gzerror # define gzflush z_gzflush # define gzgetc z_gzgetc # define gzgetc_ z_gzgetc_ # define gzgets z_gzgets # define gzoffset z_gzoffset # define gzoffset64 z_gzoffset64 # define gzopen z_gzopen # define gzopen64 z_gzopen64 # ifdef _WIN32 # define gzopen_w z_gzopen_w # endif # define gzprintf z_gzprintf # define gzvprintf z_gzvprintf # define gzputc z_gzputc # define gzputs z_gzputs # define gzread z_gzread # define gzrewind z_gzrewind # define gzseek z_gzseek # define gzseek64 z_gzseek64 # define gzsetparams z_gzsetparams # define gztell z_gztell # define gztell64 z_gztell64 # define gzungetc z_gzungetc # define gzwrite z_gzwrite # endif # define inflate z_inflate # define inflateBack z_inflateBack # define inflateBackEnd z_inflateBackEnd # define inflateBackInit_ z_inflateBackInit_ # define inflateCopy z_inflateCopy # define inflateEnd z_inflateEnd # define inflateGetHeader z_inflateGetHeader # define inflateInit2_ z_inflateInit2_ # define inflateInit_ z_inflateInit_ # define inflateMark z_inflateMark # define inflatePrime z_inflatePrime # define inflateReset z_inflateReset # define inflateReset2 z_inflateReset2 # define inflateSetDictionary z_inflateSetDictionary # define inflateGetDictionary z_inflateGetDictionary # define inflateSync z_inflateSync # define inflateSyncPoint z_inflateSyncPoint # define inflateUndermine z_inflateUndermine # define inflateResetKeep z_inflateResetKeep # define inflate_copyright z_inflate_copyright # define inflate_fast z_inflate_fast # define inflate_table z_inflate_table # ifndef Z_SOLO # define uncompress z_uncompress # endif # define zError z_zError # ifndef Z_SOLO # define zcalloc z_zcalloc # define zcfree z_zcfree # endif # define zlibCompileFlags z_zlibCompileFlags # define zlibVersion z_zlibVersion /* all zlib typedefs in zlib.h and zconf.h */ # define Byte z_Byte # define Bytef z_Bytef # define alloc_func z_alloc_func # define charf z_charf # define free_func z_free_func # ifndef Z_SOLO # define gzFile z_gzFile # endif # define gz_header z_gz_header # define gz_headerp z_gz_headerp # define in_func z_in_func # define intf z_intf # define out_func z_out_func # define uInt z_uInt # define uIntf z_uIntf # define uLong z_uLong # define uLongf z_uLongf # define voidp z_voidp # define voidpc z_voidpc # define voidpf z_voidpf /* all zlib structs in zlib.h and zconf.h */ # define gz_header_s z_gz_header_s # define internal_state z_internal_state #endif #if defined(__MSDOS__) && !defined(MSDOS) # define MSDOS #endif #if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) # define OS2 #endif #if defined(_WINDOWS) && !defined(WINDOWS) # define WINDOWS #endif #if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) # ifndef WIN32 # define WIN32 # endif #endif #if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) # if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) # ifndef SYS16BIT # define SYS16BIT # endif # endif #endif /* * Compile with -DMAXSEG_64K if the alloc function cannot allocate more * than 64k bytes at a time (needed on systems with 16-bit int). */ #ifdef SYS16BIT # define MAXSEG_64K #endif #ifdef MSDOS # define UNALIGNED_OK #endif #ifdef __STDC_VERSION__ # ifndef STDC # define STDC # endif # if __STDC_VERSION__ >= 199901L # ifndef STDC99 # define STDC99 # endif # endif #endif #if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) # define STDC #endif #if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) # define STDC #endif #if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) # define STDC #endif #if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) # define STDC #endif #if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ # define STDC #endif #ifndef STDC # ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ # define const /* note: need a more gentle solution here */ # endif #endif #if defined(ZLIB_CONST) && !defined(z_const) # define z_const const #else # define z_const #endif /* Some Mac compilers merge all .h files incorrectly: */ #if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) # define NO_DUMMY_DECL #endif /* Maximum value for memLevel in deflateInit2 */ #ifndef MAX_MEM_LEVEL # ifdef MAXSEG_64K # define MAX_MEM_LEVEL 8 # else # define MAX_MEM_LEVEL 9 # endif #endif /* Maximum value for windowBits in deflateInit2 and inflateInit2. * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files * created by gzip. (Files created by minigzip can still be extracted by * gzip.) */ #ifndef MAX_WBITS # define MAX_WBITS 15 /* 32K LZ77 window */ #endif /* The memory requirements for deflate are (in bytes): (1 << (windowBits+2)) + (1 << (memLevel+9)) that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) plus a few kilobytes for small objects. For example, if you want to reduce the default memory requirements from 256K to 128K, compile with make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits that is, 32K for windowBits=15 (default value) plus a few kilobytes for small objects. */ /* Type declarations */ #ifndef OF /* function prototypes */ # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif #ifndef Z_ARG /* function prototypes for stdarg */ # if defined(STDC) || defined(Z_HAVE_STDARG_H) # define Z_ARG(args) args # else # define Z_ARG(args) () # endif #endif /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, * just define FAR to be empty. */ #ifdef SYS16BIT # if defined(M_I86SM) || defined(M_I86MM) /* MSC small or medium model */ # define SMALL_MEDIUM # ifdef _MSC_VER # define FAR _far # else # define FAR far # endif # endif # if (defined(__SMALL__) || defined(__MEDIUM__)) /* Turbo C small or medium model */ # define SMALL_MEDIUM # ifdef __BORLANDC__ # define FAR _far # else # define FAR far # endif # endif #endif #if defined(WINDOWS) || defined(WIN32) /* If building or using zlib as a DLL, define ZLIB_DLL. * This is not mandatory, but it offers a little performance increase. */ # ifdef ZLIB_DLL # if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) # ifdef ZLIB_INTERNAL # define ZEXTERN extern __declspec(dllexport) # else # define ZEXTERN extern __declspec(dllimport) # endif # endif # endif /* ZLIB_DLL */ /* If building or using zlib with the WINAPI/WINAPIV calling convention, * define ZLIB_WINAPI. * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. */ # ifdef ZLIB_WINAPI # ifdef FAR # undef FAR # endif # include /* No need for _export, use ZLIB.DEF instead. */ /* For complete Windows compatibility, use WINAPI, not __stdcall. */ # define ZEXPORT WINAPI # ifdef WIN32 # define ZEXPORTVA WINAPIV # else # define ZEXPORTVA FAR CDECL # endif # endif #endif #if defined (__BEOS__) # ifdef ZLIB_DLL # ifdef ZLIB_INTERNAL # define ZEXPORT __declspec(dllexport) # define ZEXPORTVA __declspec(dllexport) # else # define ZEXPORT __declspec(dllimport) # define ZEXPORTVA __declspec(dllimport) # endif # endif #endif #ifndef ZEXTERN # define ZEXTERN extern #endif #ifndef ZEXPORT # define ZEXPORT #endif #ifndef ZEXPORTVA # define ZEXPORTVA #endif #ifndef FAR # define FAR #endif #if !defined(__MACTYPES__) typedef unsigned char Byte; /* 8 bits */ #endif typedef unsigned int uInt; /* 16 bits or more */ typedef unsigned long uLong; /* 32 bits or more */ #ifdef SMALL_MEDIUM /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ # define Bytef Byte FAR #else typedef Byte FAR Bytef; #endif typedef char FAR charf; typedef int FAR intf; typedef uInt FAR uIntf; typedef uLong FAR uLongf; #ifdef STDC typedef void const *voidpc; typedef void FAR *voidpf; typedef void *voidp; #else typedef Byte const *voidpc; typedef Byte FAR *voidpf; typedef Byte *voidp; #endif #if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) # include # if (UINT_MAX == 0xffffffffUL) # define Z_U4 unsigned # elif (ULONG_MAX == 0xffffffffUL) # define Z_U4 unsigned long # elif (USHRT_MAX == 0xffffffffUL) # define Z_U4 unsigned short # endif #endif #ifdef Z_U4 typedef Z_U4 z_crc_t; #else typedef unsigned long z_crc_t; #endif #ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_UNISTD_H #endif #ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_STDARG_H #endif #ifdef STDC # ifndef Z_SOLO # include /* for off_t */ # endif #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO # include /* for va_list */ # endif #endif #ifdef _WIN32 # ifndef Z_SOLO # include /* for wchar_t */ # endif #endif /* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even * though the former does not conform to the LFS document), but considering * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as * equivalently requesting no 64-bit operations */ #if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 # undef _LARGEFILE64_SOURCE #endif #if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) # define Z_HAVE_UNISTD_H #endif #ifndef Z_SOLO # if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) # include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ # ifdef VMS # include /* for off_t */ # endif # ifndef z_off_t # define z_off_t off_t # endif # endif #endif #if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 # define Z_LFS64 #endif #if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) # define Z_LARGE64 #endif #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) # define Z_WANT64 #endif #if !defined(SEEK_SET) && !defined(Z_SOLO) # define SEEK_SET 0 /* Seek from beginning of file. */ # define SEEK_CUR 1 /* Seek from current position. */ # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ #endif #ifndef z_off_t # define z_off_t long #endif #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t #else # if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) # define z_off64_t __int64 # else # define z_off64_t z_off_t # endif #endif /* MVS linker does not support external names larger than 8 bytes */ #if defined(__MVS__) #pragma map(deflateInit_,"DEIN") #pragma map(deflateInit2_,"DEIN2") #pragma map(deflateEnd,"DEEND") #pragma map(deflateBound,"DEBND") #pragma map(inflateInit_,"ININ") #pragma map(inflateInit2_,"ININ2") #pragma map(inflateEnd,"INEND") #pragma map(inflateSync,"INSY") #pragma map(inflateSetDictionary,"INSEDI") #pragma map(compressBound,"CMBND") #pragma map(inflate_table,"INTABL") #pragma map(inflate_fast,"INFA") #pragma map(inflate_copyright,"INCOPY") #endif #endif /* ZCONF_H */ PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/zlib.h000066400000000000000000002535131231437614300232010ustar00rootroot00000000000000/* zlib.h -- interface of the 'zlib' general purpose compression library version 1.2.8, April 28th, 2013 Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). */ #ifndef ZLIB_H #define ZLIB_H #include "zconf.h" #ifdef __cplusplus extern "C" { #endif #define ZLIB_VERSION "1.2.8" #define ZLIB_VERNUM 0x1280 #define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MINOR 2 #define ZLIB_VER_REVISION 8 #define ZLIB_VER_SUBREVISION 0 /* The 'zlib' compression library provides in-memory compression and decompression functions, including integrity checks of the uncompressed data. This version of the library supports only one compression method (deflation) but other algorithms will be added later and will have the same stream interface. Compression can be done in a single step if the buffers are large enough, or can be done by repeated calls of the compression function. In the latter case, the application must provide more input and/or consume the output (providing more output space) before each call. The compressed data format used by default by the in-memory functions is the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped around a deflate stream, which is itself documented in RFC 1951. The library also supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio using the functions that start with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. This library can optionally read and write gzip streams in memory as well. The zlib format was designed to be compact and fast for use in memory and on communications channels. The gzip format was designed for single- file compression on file systems, has a larger header than zlib to maintain directory information, and uses a different, slower check method than zlib. The library does not install any signal handler. The decoder checks the consistency of the compressed data, so the library should never crash even in case of corrupted input. */ typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); typedef void (*free_func) OF((voidpf opaque, voidpf address)); struct internal_state; typedef struct z_stream_s { z_const Bytef *next_in; /* next input byte */ uInt avail_in; /* number of bytes available at next_in */ uLong total_in; /* total number of input bytes read so far */ Bytef *next_out; /* next output byte should be put there */ uInt avail_out; /* remaining free space at next_out */ uLong total_out; /* total number of bytes output so far */ z_const char *msg; /* last error message, NULL if no error */ struct internal_state FAR *state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ free_func zfree; /* used to free the internal state */ voidpf opaque; /* private data object passed to zalloc and zfree */ int data_type; /* best guess about the data type: binary or text */ uLong adler; /* adler32 value of the uncompressed data */ uLong reserved; /* reserved for future use */ } z_stream; typedef z_stream FAR *z_streamp; /* gzip header information passed to and from zlib routines. See RFC 1952 for more details on the meanings of these fields. */ typedef struct gz_header_s { int text; /* true if compressed data believed to be text */ uLong time; /* modification time */ int xflags; /* extra flags (not used when writing a gzip file) */ int os; /* operating system */ Bytef *extra; /* pointer to extra field or Z_NULL if none */ uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ uInt extra_max; /* space at extra (only when reading header) */ Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ uInt name_max; /* space at name (only when reading header) */ Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ uInt comm_max; /* space at comment (only when reading header) */ int hcrc; /* true if there was or will be a header crc */ int done; /* true when done reading gzip header (not used when writing a gzip file) */ } gz_header; typedef gz_header FAR *gz_headerp; /* The application must update next_in and avail_in when avail_in has dropped to zero. It must update next_out and avail_out when avail_out has dropped to zero. The application must initialize zalloc, zfree and opaque before calling the init function. All other fields are set by the compression library and must not be updated by the application. The opaque value provided by the application will be passed as the first parameter for calls of zalloc and zfree. This can be useful for custom memory management. The compression library attaches no meaning to the opaque value. zalloc must return Z_NULL if there is not enough memory for the object. If zlib is used in a multi-threaded application, zalloc and zfree must be thread safe. On 16-bit systems, the functions zalloc and zfree must be able to allocate exactly 65536 bytes, but will not be required to allocate more than this if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers returned by zalloc for objects of exactly 65536 bytes *must* have their offset normalized to zero. The default allocation function provided by this library ensures this (see zutil.c). To reduce memory requirements and avoid any allocation of 64K objects, at the expense of compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). The fields total_in and total_out can be used for statistics or progress reports. After compression, total_in holds the total size of the uncompressed data and may be saved for use in the decompressor (particularly if the decompressor wants to decompress everything in a single step). */ /* constants */ #define Z_NO_FLUSH 0 #define Z_PARTIAL_FLUSH 1 #define Z_SYNC_FLUSH 2 #define Z_FULL_FLUSH 3 #define Z_FINISH 4 #define Z_BLOCK 5 #define Z_TREES 6 /* Allowed flush values; see deflate() and inflate() below for details */ #define Z_OK 0 #define Z_STREAM_END 1 #define Z_NEED_DICT 2 #define Z_ERRNO (-1) #define Z_STREAM_ERROR (-2) #define Z_DATA_ERROR (-3) #define Z_MEM_ERROR (-4) #define Z_BUF_ERROR (-5) #define Z_VERSION_ERROR (-6) /* Return codes for the compression/decompression functions. Negative values * are errors, positive values are used for special but normal events. */ #define Z_NO_COMPRESSION 0 #define Z_BEST_SPEED 1 #define Z_BEST_COMPRESSION 9 #define Z_DEFAULT_COMPRESSION (-1) /* compression levels */ #define Z_FILTERED 1 #define Z_HUFFMAN_ONLY 2 #define Z_RLE 3 #define Z_FIXED 4 #define Z_DEFAULT_STRATEGY 0 /* compression strategy; see deflateInit2() below for details */ #define Z_BINARY 0 #define Z_TEXT 1 #define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ #define Z_UNKNOWN 2 /* Possible values of the data_type field (though see inflate()) */ #define Z_DEFLATED 8 /* The deflate compression method (the only one supported in this version) */ #define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ #define zlib_version zlibVersion() /* for compatibility with versions < 1.0.2 */ /* basic functions */ ZEXTERN const char * ZEXPORT zlibVersion OF((void)); /* The application can compare zlibVersion and ZLIB_VERSION for consistency. If the first character differs, the library code actually used is not compatible with the zlib.h header file used by the application. This check is automatically made by deflateInit and inflateInit. */ /* ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); Initializes the internal stream state for compression. The fields zalloc, zfree and opaque must be initialized before by the caller. If zalloc and zfree are set to Z_NULL, deflateInit updates them to use default allocation functions. The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION requests a default compromise between speed and compression (currently equivalent to level 6). deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if level is not a valid compression level, or Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); /* deflate compresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. deflate performs one or both of the following actions: - Compress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate(). - Provide more output starting at next_out and update next_out and avail_out accordingly. This action is forced if the parameter flush is non zero. Forcing flush frequently degrades the compression ratio, so this parameter should be set only when necessary (in interactive applications). Some output may be provided even if flush is not set. Before the call of deflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating avail_in or avail_out accordingly; avail_out should never be zero before the call. The application can consume the compressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to decide how much data to accumulate before producing output, in order to maximize compression. If the parameter flush is set to Z_SYNC_FLUSH, all pending output is flushed to the output buffer and the output is aligned on a byte boundary, so that the decompressor can get all input data available so far. (In particular avail_in is zero after the call if enough output space has been provided before the call.) Flushing may degrade compression for some compression algorithms and so it should be used only when necessary. This completes the current deflate block and follows it with an empty stored block that is three bits plus filler bits to the next byte, followed by four bytes (00 00 ff ff). If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the output buffer, but the output is not aligned to a byte boundary. All of the input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. This completes the current deflate block and follows it with an empty fixed codes block that is 10 bits long. This assures that enough bytes are output in order for the decompressor to finish the block before the empty fixed code block. If flush is set to Z_BLOCK, a deflate block is completed and emitted, as for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to seven bits of the current block are held to be written as the next byte after the next deflate block is completed. In this case, the decompressor may not be provided enough bits at this point in order to complete decompression of the data provided so far to the compressor. It may need to wait for the next block to be emitted. This is for advanced applications that need to control the emission of deflate blocks. If flush is set to Z_FULL_FLUSH, all output is flushed as with Z_SYNC_FLUSH, and the compression state is reset so that decompression can restart from this point if previous compressed data has been damaged or if random access is desired. Using Z_FULL_FLUSH too often can seriously degrade compression. If deflate returns with avail_out == 0, this function must be called again with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that avail_out is greater than six to avoid repeated flush markers due to avail_out == 0 on return. If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was enough output space; if deflate returns with Z_OK, this function must be called again with Z_FINISH and more output space (updated avail_out) but no more input data, until it returns with Z_STREAM_END or an error. After deflate has returned Z_STREAM_END, the only possible operations on the stream are deflateReset or deflateEnd. Z_FINISH can be used immediately after deflateInit if all the compression is to be done in a single step. In this case, avail_out must be at least the value returned by deflateBound (see below). Then deflate is guaranteed to return Z_STREAM_END. If not enough output space is provided, deflate will not return Z_STREAM_END, and it must be called again as described above. deflate() sets strm->adler to the adler32 checksum of all input read so far (that is, total_in bytes). deflate() may update strm->data_type if it can make a good guess about the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered binary. This field is only for information purposes and does not affect the compression algorithm in any manner. deflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and deflate() can be called again with more input and more output space to continue compressing. */ ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent, Z_DATA_ERROR if the stream was freed prematurely (some input or output was discarded). In the error case, msg may be set but then points to a static string (which must not be deallocated). */ /* ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. If next_in is not Z_NULL and avail_in is large enough (the exact value depends on the compression method), inflateInit determines the compression method from the zlib header and allocates all data structures accordingly; otherwise the allocation will be deferred to the first call of inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to use default allocation functions. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller, or Z_STREAM_ERROR if the parameters are invalid, such as a null pointer to the structure. msg is set to null if there is no error message. inflateInit does not perform any decompression apart from possibly reading the zlib header if present: actual decompression will be done by inflate(). (So next_in and avail_in may be modified, but next_out and avail_out are unused and unchanged.) The current implementation of inflateInit() does not process any header information -- that is deferred until inflate() is called. */ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); /* inflate decompresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. inflate performs one or both of the following actions: - Decompress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in is updated and processing will resume at this point for the next call of inflate(). - Provide more output starting at next_out and update next_out and avail_out accordingly. inflate() provides as much output as possible, until there is no more input data or no more space in the output buffer (see below about the flush parameter). Before the call of inflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating the next_* and avail_* values accordingly. The application can consume the uncompressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of inflate(). If inflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much output as possible to the output buffer. Z_BLOCK requests that inflate() stop if and when it gets to the next deflate block boundary. When decoding the zlib or gzip format, this will cause inflate() to return immediately after the header and before the first block. When doing a raw inflate, inflate() will go ahead and process the first block, and will return when it gets to the end of that block, or when it runs out of data. The Z_BLOCK option assists in appending to or combining deflate streams. Also to assist in this, on return inflate() will set strm->data_type to the number of unused bits in the last byte taken from strm->next_in, plus 64 if inflate() is currently decoding the last block in the deflate stream, plus 128 if inflate() returned immediately after decoding an end-of-block code or decoding the complete header up to just before the first byte of the deflate stream. The end-of-block will not be indicated until all of the uncompressed data from that block has been written to strm->next_out. The number of unused bits may in general be greater than seven, except when bit 7 of data_type is set, in which case the number of unused bits will be less than eight. data_type is set as noted here every time inflate() returns for all flush options, and so can be used to determine the amount of currently consumed input in bits. The Z_TREES option behaves as Z_BLOCK does, but it also returns when the end of each deflate block header is reached, before any actual data in that block is decoded. This allows the caller to determine the length of the deflate block header for later use in random access within a deflate block. 256 is added to the value of strm->data_type when inflate() returns immediately after reaching the end of the deflate block header. inflate() should normally be called until it returns Z_STREAM_END or an error. However if all decompression is to be performed in a single step (a single call of inflate), the parameter flush should be set to Z_FINISH. In this case all pending input is processed and all pending output is flushed; avail_out must be large enough to hold all of the uncompressed data for the operation to complete. (The size of the uncompressed data may have been saved by the compressor for this purpose.) The use of Z_FINISH is not required to perform an inflation in one step. However it may be used to inform inflate that a faster approach can be used for the single inflate() call. Z_FINISH also informs inflate to not maintain a sliding window if the stream completes, which reduces inflate's memory footprint. If the stream does not complete, either because not all of the stream is provided or not enough output space is provided, then a sliding window will be allocated and inflate() can be called again to continue the operation as if Z_NO_FLUSH had been used. In this implementation, inflate() always flushes as much output as possible to the output buffer, and always uses the faster approach on the first call. So the effects of the flush parameter in this implementation are on the return value of inflate() as noted below, when inflate() returns early when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of memory for a sliding window when Z_FINISH is used. If a preset dictionary is needed after this call (see inflateSetDictionary below), inflate sets strm->adler to the Adler-32 checksum of the dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise it sets strm->adler to the Adler-32 checksum of all output produced so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described below. At the end of the stream, inflate() checks that its computed adler32 checksum is equal to that saved by the compressor and returns Z_STREAM_END only if the checksum is correct. inflate() can decompress and check either zlib-wrapped or gzip-wrapped deflate data. The header type is detected automatically, if requested when initializing with inflateInit2(). Any information contained in the gzip header is not retained, so applications that need that information should instead use raw inflate, see inflateInit2() below, or inflateBack() and perform their own processing of the gzip header and trailer. When processing gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output producted so far. The CRC-32 is checked against the gzip trailer. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_NEED_DICT if a preset dictionary is needed at this point, Z_DATA_ERROR if the input data was corrupted (input stream not conforming to the zlib format or incorrect check value), Z_STREAM_ERROR if the stream structure was inconsistent (for example next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no progress is possible or if there was not enough room in the output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to continue decompressing. If Z_DATA_ERROR is returned, the application may then call inflateSync() to look for a good compression block if a partial recovery of the data is desired. */ ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent. In the error case, msg may be set but then points to a static string (which must not be deallocated). */ /* Advanced functions */ /* The following functions are needed only in some special applications. */ /* ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy)); This is another version of deflateInit with more compression options. The fields next_in, zalloc, zfree and opaque must be initialized before by the caller. The method parameter is the compression method. It must be Z_DEFLATED in this version of the library. The windowBits parameter is the base two logarithm of the window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. Larger values of this parameter result in better compression at the expense of memory usage. The default value is 15 if deflateInit is used instead. windowBits can also be -8..-15 for raw deflate. In this case, -windowBits determines the window size. deflate() will then generate raw deflate data with no zlib header or trailer, and will not compute an adler32 check value. windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper. The gzip header will have no file name, no extra data, no comment, no modification time (set to zero), no header crc, and the operating system will be set to 255 (unknown). If a gzip stream is being written, strm->adler is a crc32 instead of an adler32. The memLevel parameter specifies how much memory should be allocated for the internal compression state. memLevel=1 uses minimum memory but is slow and reduces compression ratio; memLevel=9 uses maximum memory for optimal speed. The default value is 8. See zconf.h for total memory usage as a function of windowBits and memLevel. The strategy parameter is used to tune the compression algorithm. Use the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no string match), or Z_RLE to limit match distances to one (run-length encoding). Filtered data consists mostly of small values with a somewhat random distribution. In this case, the compression algorithm is tuned to compress them better. The effect of Z_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately. Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler decoder for special applications. deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit2 does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, const Bytef *dictionary, uInt dictLength)); /* Initializes the compression dictionary from the given byte sequence without producing any compressed output. When using the zlib format, this function must be called immediately after deflateInit, deflateInit2 or deflateReset, and before any call of deflate. When doing raw deflate, this function must be called either before any call of deflate, or immediately after the completion of a deflate block, i.e. after all input has been consumed and all output has been delivered when using any of the flush options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The compressor and decompressor must use exactly the same dictionary (see inflateSetDictionary). The dictionary should consist of strings (byte sequences) that are likely to be encountered later in the data to be compressed, with the most commonly used strings preferably put towards the end of the dictionary. Using a dictionary is most useful when the data to be compressed is short and can be predicted with good accuracy; the data can then be compressed better than with the default empty dictionary. Depending on the size of the compression data structures selected by deflateInit or deflateInit2, a part of the dictionary may in effect be discarded, for example if the dictionary is larger than the window size provided in deflateInit or deflateInit2. Thus the strings most likely to be useful should be put at the end of the dictionary, not at the front. In addition, the current implementation of deflate will use at most the window size minus 262 bytes of the provided dictionary. Upon return of this function, strm->adler is set to the adler32 value of the dictionary; the decompressor may later use this value to determine which dictionary has been used by the compressor. (The adler32 value applies to the whole dictionary even if only a subset of the dictionary is actually used by the compressor.) If a raw deflate was requested, then the adler32 value is not computed and strm->adler is not set. deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent (for example if deflate has already been called for this stream or if not at a block boundary for raw deflate). deflateSetDictionary does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, z_streamp source)); /* Sets the destination stream as a complete copy of the source stream. This function can be useful when several compression strategies will be tried, for example when there are several ways of pre-processing the input data with a filter. The streams that will be discarded should then be freed by calling deflateEnd. Note that deflateCopy duplicates the internal compression state which can be quite large, so this strategy is slow and can consume lots of memory. deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); /* This function is equivalent to deflateEnd followed by deflateInit, but does not free and reallocate all the internal compression state. The stream will keep the same compression level and any other attributes that may have been set by deflateInit2. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, int level, int strategy)); /* Dynamically update the compression level and compression strategy. The interpretation of level and strategy is as in deflateInit2. This can be used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. If the compression level is changed, the input available so far is compressed with the old level (and may be flushed); the new level will take effect only at the next call of deflate(). Before the call of deflateParams, the stream state must be set as for a call of deflate(), since the currently available input may have to be compressed and flushed. In particular, strm->avail_out must be non-zero. deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if strm->avail_out was zero. */ ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, int good_length, int max_lazy, int nice_length, int max_chain)); /* Fine tune deflate's internal compression parameters. This should only be used by someone who understands the algorithm used by zlib's deflate for searching for the best matching string, and even then only by the most fanatic optimizer trying to squeeze out the last compressed bit for their specific input data. Read the deflate.c source code for the meaning of the max_lazy, good_length, nice_length, and max_chain parameters. deflateTune() can be called after deflateInit() or deflateInit2(), and returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. */ ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, uLong sourceLen)); /* deflateBound() returns an upper bound on the compressed size after deflation of sourceLen bytes. It must be called after deflateInit() or deflateInit2(), and after deflateSetHeader(), if used. This would be used to allocate an output buffer for deflation in a single pass, and so would be called before deflate(). If that first deflate() call is provided the sourceLen input bytes, an output buffer allocated to the size returned by deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed to return Z_STREAM_END. Note that it is possible for the compressed size to be larger than the value returned by deflateBound() if flush options other than Z_FINISH or Z_NO_FLUSH are used. */ ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, unsigned *pending, int *bits)); /* deflatePending() returns the number of bytes and bits of output that have been generated, but not yet provided in the available output. The bytes not provided would be due to the available output space having being consumed. The number of bits of output not provided are between 0 and 7, where they await more bits to join them in order to fill out a full byte. If pending or bits are Z_NULL, then those values are not set. deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, int bits, int value)); /* deflatePrime() inserts bits in the deflate output stream. The intent is that this function is used to start off the deflate output with the bits leftover from a previous deflate stream when appending to it. As such, this function can only be used for raw deflate, and must be used before the first deflate() call after a deflateInit2() or deflateReset(). bits must be less than or equal to 16, and that many of the least significant bits of value will be inserted in the output. deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, gz_headerp head)); /* deflateSetHeader() provides gzip header information for when a gzip stream is requested by deflateInit2(). deflateSetHeader() may be called after deflateInit2() or deflateReset() and before the first call of deflate(). The text, time, os, extra field, name, and comment information in the provided gz_header structure are written to the gzip header (xflag is ignored -- the extra flags are set according to the compression level). The caller must assure that, if not Z_NULL, name and comment are terminated with a zero byte, and that if extra is not Z_NULL, that extra_len bytes are available there. If hcrc is true, a gzip header crc is included. Note that the current versions of the command-line version of gzip (up through version 1.3.x) do not support header crc's, and will report that it is a "multi-part gzip file" and give up. If deflateSetHeader is not used, the default gzip header has text false, the time set to zero, and os set to 255, with no extra, name, or comment fields. The gzip header is returned to the default state by deflateReset(). deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, int windowBits)); This is another version of inflateInit with an extra parameter. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. The windowBits parameter is the base two logarithm of the maximum window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. The default value is 15 if inflateInit is used instead. windowBits must be greater than or equal to the windowBits value provided to deflateInit2() while compressing, or it must be equal to 15 if deflateInit2() was not used. If a compressed stream with a larger window size is given as input, inflate() will return with the error code Z_DATA_ERROR instead of trying to allocate a larger window. windowBits can also be zero to request that inflate use the window size in the zlib header of the compressed stream. windowBits can also be -8..-15 for raw inflate. In this case, -windowBits determines the window size. inflate() will then process raw deflate data, not looking for a zlib or gzip header, not generating a check value, and not looking for any check values for comparison at the end of the stream. This is for use with other formats that use the deflate compressed data format such as zip. Those formats provide their own check values. If a custom format is developed using the raw deflate format for compressed data, it is recommended that a check value such as an adler32 or a crc32 be applied to the uncompressed data as is done in the zlib, gzip, and zip formats. For most applications, the zlib format should be used as is. Note that comments above on the use in deflateInit2() applies to the magnitude of windowBits. windowBits can also be greater than 15 for optional gzip decoding. Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection, or add 16 to decode only the gzip format (the zlib format will return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a crc32 instead of an adler32. inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller, or Z_STREAM_ERROR if the parameters are invalid, such as a null pointer to the structure. msg is set to null if there is no error message. inflateInit2 does not perform any decompression apart from possibly reading the zlib header if present: actual decompression will be done by inflate(). (So next_in and avail_in may be modified, but next_out and avail_out are unused and unchanged.) The current implementation of inflateInit2() does not process any header information -- that is deferred until inflate() is called. */ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, const Bytef *dictionary, uInt dictLength)); /* Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called immediately after a call of inflate, if that call returned Z_NEED_DICT. The dictionary chosen by the compressor can be determined from the adler32 value returned by that call of inflate. The compressor and decompressor must use exactly the same dictionary (see deflateSetDictionary). For raw inflate, this function can be called at any time to set the dictionary. If the provided dictionary is smaller than the window and there is already data in the window, then the provided dictionary will amend what's there. The application must insure that the dictionary that was used for compression is provided. inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the expected one (incorrect adler32 value). inflateSetDictionary does not perform any decompression: this will be done by subsequent calls of inflate(). */ ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, Bytef *dictionary, uInt *dictLength)); /* Returns the sliding dictionary being maintained by inflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied to dictionary. dictionary must have enough space, where 32768 bytes is always enough. If inflateGetDictionary() is called with dictionary equal to Z_NULL, then only the dictionary length is returned, and nothing is copied. Similary, if dictLength is Z_NULL, then it is not set. inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the stream state is inconsistent. */ ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); /* Skips invalid compressed data until a possible full flush point (see above for the description of deflate with Z_FULL_FLUSH) can be found, or until all available input is skipped. No output is provided. inflateSync searches for a 00 00 FF FF pattern in the compressed data. All full flush points have this pattern, but not all occurrences of this pattern are full flush points. inflateSync returns Z_OK if a possible full flush point has been found, Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the success case, the application may save the current current value of total_in which indicates where valid compressed data was found. In the error case, the application may repeatedly call inflateSync, providing more input each time, until success or end of the input data. */ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, z_streamp source)); /* Sets the destination stream as a complete copy of the source stream. This function can be useful when randomly accessing a large stream. The first pass through the stream can periodically record the inflate state, allowing restarting inflate at those points when randomly accessing the stream. inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); /* This function is equivalent to inflateEnd followed by inflateInit, but does not free and reallocate all the internal decompression state. The stream will keep attributes that may have been set by inflateInit2. inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, int windowBits)); /* This function is the same as inflateReset, but it also permits changing the wrap and window size requests. The windowBits parameter is interpreted the same as it is for inflateInit2. inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL), or if the windowBits parameter is invalid. */ ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, int bits, int value)); /* This function inserts bits in the inflate input stream. The intent is that this function is used to start inflating at a bit position in the middle of a byte. The provided bits will be used before any bytes are used from next_in. This function should only be used with raw inflate, and should be used before the first inflate() call after inflateInit2() or inflateReset(). bits must be less than or equal to 16, and that many of the least significant bits of value will be inserted in the input. If bits is negative, then the input stream bit buffer is emptied. Then inflatePrime() can be called again to put bits in the buffer. This is used to clear out bits leftover after feeding inflate a block description prior to feeding inflate codes. inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); /* This function returns two values, one in the lower 16 bits of the return value, and the other in the remaining upper bits, obtained by shifting the return value down 16 bits. If the upper value is -1 and the lower value is zero, then inflate() is currently decoding information outside of a block. If the upper value is -1 and the lower value is non-zero, then inflate is in the middle of a stored block, with the lower value equaling the number of bytes from the input remaining to copy. If the upper value is not -1, then it is the number of bits back from the current bit position in the input of the code (literal or length/distance pair) currently being processed. In that case the lower value is the number of bytes already emitted for that code. A code is being processed if inflate is waiting for more input to complete decoding of the code, or if it has completed decoding but is waiting for more output space to write the literal or match data. inflateMark() is used to mark locations in the input data for random access, which may be at bit positions, and to note those cases where the output of a code may span boundaries of random access blocks. The current location in the input stream can be determined from avail_in and data_type as noted in the description for the Z_BLOCK flush parameter for inflate. inflateMark returns the value noted above or -1 << 16 if the provided source stream state was inconsistent. */ ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, gz_headerp head)); /* inflateGetHeader() requests that gzip header information be stored in the provided gz_header structure. inflateGetHeader() may be called after inflateInit2() or inflateReset(), and before the first call of inflate(). As inflate() processes the gzip stream, head->done is zero until the header is completed, at which time head->done is set to one. If a zlib stream is being decoded, then head->done is set to -1 to indicate that there will be no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be used to force inflate() to return immediately after header processing is complete and before any actual data is decompressed. The text, time, xflags, and os fields are filled in with the gzip header contents. hcrc is set to true if there is a header CRC. (The header CRC was valid if done is set to one.) If extra is not Z_NULL, then extra_max contains the maximum number of bytes to write to extra. Once done is true, extra_len contains the actual extra field length, and extra contains the extra field, or that field truncated if extra_max is less than extra_len. If name is not Z_NULL, then up to name_max characters are written there, terminated with a zero unless the length is greater than name_max. If comment is not Z_NULL, then up to comm_max characters are written there, terminated with a zero unless the length is greater than comm_max. When any of extra, name, or comment are not Z_NULL and the respective field is not present in the header, then that field is set to Z_NULL to signal its absence. This allows the use of deflateSetHeader() with the returned structure to duplicate the header. However if those fields are set to allocated memory, then the application will need to save those pointers elsewhere so that they can be eventually freed. If inflateGetHeader is not used, then the header information is simply discarded. The header is always checked for validity, including the header CRC if present. inflateReset() will reset the process to discard the header information. The application would need to call inflateGetHeader() again to retrieve the header from the next gzip stream. inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, unsigned char FAR *window)); Initialize the internal stream state for decompression using inflateBack() calls. The fields zalloc, zfree and opaque in strm must be initialized before the call. If zalloc and zfree are Z_NULL, then the default library- derived memory allocation routines are used. windowBits is the base two logarithm of the window size, in the range 8..15. window is a caller supplied buffer of that size. Except for special applications where it is assured that deflate was used with small window sizes, windowBits must be 15 and a 32K byte window must be supplied to be able to decompress general deflate streams. See inflateBack() for the usage of these routines. inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of the parameters are invalid, Z_MEM_ERROR if the internal state could not be allocated, or Z_VERSION_ERROR if the version of the library does not match the version of the header file. */ typedef unsigned (*in_func) OF((void FAR *, z_const unsigned char FAR * FAR *)); typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, in_func in, void FAR *in_desc, out_func out, void FAR *out_desc)); /* inflateBack() does a raw inflate with a single call using a call-back interface for input and output. This is potentially more efficient than inflate() for file i/o applications, in that it avoids copying between the output and the sliding window by simply making the window itself the output buffer. inflate() can be faster on modern CPUs when used with large buffers. inflateBack() trusts the application to not change the output buffer passed by the output function, at least until inflateBack() returns. inflateBackInit() must be called first to allocate the internal state and to initialize the state with the user-provided window buffer. inflateBack() may then be used multiple times to inflate a complete, raw deflate stream with each call. inflateBackEnd() is then called to free the allocated state. A raw deflate stream is one with no zlib or gzip header or trailer. This routine would normally be used in a utility that reads zip or gzip files and writes out uncompressed files. The utility would decode the header and process the trailer on its own, hence this routine expects only the raw deflate stream to decompress. This is different from the normal behavior of inflate(), which expects either a zlib or gzip header and trailer around the deflate stream. inflateBack() uses two subroutines supplied by the caller that are then called by inflateBack() for input and output. inflateBack() calls those routines until it reads a complete deflate stream and writes out all of the uncompressed data, or until it encounters an error. The function's parameters and return types are defined above in the in_func and out_func typedefs. inflateBack() will call in(in_desc, &buf) which should return the number of bytes of provided input, and a pointer to that input in buf. If there is no input available, in() must return zero--buf is ignored in that case--and inflateBack() will return a buffer error. inflateBack() will call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() should return zero on success, or non-zero on failure. If out() returns non-zero, inflateBack() will return with an error. Neither in() nor out() are permitted to change the contents of the window provided to inflateBackInit(), which is also the buffer that out() uses to write from. The length written by out() will be at most the window size. Any non-zero amount of input may be provided by in(). For convenience, inflateBack() can be provided input on the first call by setting strm->next_in and strm->avail_in. If that input is exhausted, then in() will be called. Therefore strm->next_in must be initialized before calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in must also be initialized, and then if strm->avail_in is not zero, input will initially be taken from strm->next_in[0 .. strm->avail_in - 1]. The in_desc and out_desc parameters of inflateBack() is passed as the first parameter of in() and out() respectively when they are called. These descriptors can be optionally used to pass any information that the caller- supplied in() and out() functions need to do their job. On return, inflateBack() will set strm->next_in and strm->avail_in to pass back any unused input that was provided by the last in() call. The return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR if in() or out() returned an error, Z_DATA_ERROR if there was a format error in the deflate stream (in which case strm->msg is set to indicate the nature of the error), or Z_STREAM_ERROR if the stream was not properly initialized. In the case of Z_BUF_ERROR, an input or output error can be distinguished using strm->next_in which will be Z_NULL only if in() returned an error. If strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning non-zero. (in() will always be called before out(), so strm->next_in is assured to be defined if out() returns non-zero.) Note that inflateBack() cannot return Z_OK. */ ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); /* All memory allocated by inflateBackInit() is freed. inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream state was inconsistent. */ ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); /* Return flags indicating compile-time options. Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: 1.0: size of uInt 3.2: size of uLong 5.4: size of voidpf (pointer) 7.6: size of z_off_t Compiler, assembler, and debug options: 8: DEBUG 9: ASMV or ASMINF -- use ASM code 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention 11: 0 (reserved) One-time table building (smaller code, but not thread-safe if true): 12: BUILDFIXED -- build static block decoding tables when needed 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed 14,15: 0 (reserved) Library content (indicates missing functionality): 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking deflate code when not needed) 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect and decode gzip streams (to avoid linking crc code) 18-19: 0 (reserved) Operation variations (changes in library functionality): 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate 21: FASTEST -- deflate algorithm with only one, lowest compression level 22,23: 0 (reserved) The sprintf variant used by gzprintf (zero is best): 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! 26: 0 = returns value, 1 = void -- 1 means inferred string length returned Remainder: 27-31: 0 (reserved) */ #ifndef Z_SOLO /* utility functions */ /* The following utility functions are implemented on top of the basic stream-oriented functions. To simplify the interface, some default options are assumed (compression level and memory usage, standard memory allocation functions). The source code of these utility functions can be modified if you need special options. */ ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)); /* Compresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the compressed buffer. compress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer. */ ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level)); /* Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the compressed buffer. compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, Z_STREAM_ERROR if the level parameter is invalid. */ ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); /* compressBound() returns an upper bound on the compressed size after compress() or compress2() on sourceLen bytes. It would be used before a compress() or compress2() call to allocate the destination buffer. */ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)); /* Decompresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be large enough to hold the entire uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.) Upon exit, destLen is the actual size of the uncompressed buffer. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In the case where there is not enough room, uncompress() will fill the output buffer with the uncompressed data up to that point. */ /* gzip file access functions */ /* This library supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio, using the functions that start with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. */ typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ /* ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); Opens a gzip (.gz) file for reading or writing. The mode parameter is as in fopen ("rb" or "wb") but can also include a compression level ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression as in "wb9F". (See the description of deflateInit2 for more information about the strategy parameter.) 'T' will request transparent writing or appending with no compression and not using the gzip format. "a" can be used instead of "w" to request that the gzip stream that will be written be appended to the file. "+" will result in an error, since reading and writing to the same gzip file is not supported. The addition of "x" when writing will create the file exclusively, which fails if the file already exists. On systems that support it, the addition of "e" when reading or writing will set the flag to close the file on an execve() call. These functions, as well as gzip, will read and decode a sequence of gzip streams in a file. The append function of gzopen() can be used to create such a file. (Also see gzflush() for another way to do this.) When appending, gzopen does not test whether the file begins with a gzip stream, nor does it look for the end of the gzip streams to begin appending. gzopen will simply append a gzip stream to the existing file. gzopen can be used to read a file which is not in gzip format; in this case gzread will directly read from the file without decompression. When reading, this will be detected automatically by looking for the magic two- byte gzip header. gzopen returns NULL if the file could not be opened, if there was insufficient memory to allocate the gzFile state, or if an invalid mode was specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). errno can be checked to determine if the reason gzopen failed was that the file could not be opened. */ ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); /* gzdopen associates a gzFile with the file descriptor fd. File descriptors are obtained from calls like open, dup, creat, pipe or fileno (if the file has been previously opened with fopen). The mode parameter is as in gzopen. The next call of gzclose on the returned gzFile will also close the file descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, mode);. The duplicated descriptor should be saved to avoid a leak, since gzdopen does not close fd if it fails. If you are using fileno() to get the file descriptor from a FILE *, then you will have to use dup() to avoid double-close()ing the file descriptor. Both gzclose() and fclose() will close the associated file descriptor, so they need to have different file descriptors. gzdopen returns NULL if there was insufficient memory to allocate the gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not provided, or '+' was provided), or if fd is -1. The file descriptor is not used until the next gz* read, write, seek, or close operation, so gzdopen will not detect if fd is invalid (unless fd is -1). */ ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); /* Set the internal buffer size used by this library's functions. The default buffer size is 8192 bytes. This function must be called after gzopen() or gzdopen(), and before any other calls that read or write the file. The buffer memory allocation is always deferred to the first read or write. Two buffers are allocated, either both of the specified size when writing, or one of the specified size and the other twice that size when reading. A larger buffer size of, for example, 64K or 128K bytes will noticeably increase the speed of decompression (reading). The new buffer size also affects the maximum length for gzprintf(). gzbuffer() returns 0 on success, or -1 on failure, such as being called too late. */ ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); /* Dynamically update the compression level or strategy. See the description of deflateInit2 for the meaning of these parameters. gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not opened for writing. */ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); /* Reads the given number of uncompressed bytes from the compressed file. If the input file is not in gzip format, gzread copies the given number of bytes into the buffer directly from the file. After reaching the end of a gzip stream in the input, gzread will continue to read, looking for another gzip stream. Any number of gzip streams may be concatenated in the input file, and will all be decompressed by gzread(). If something other than a gzip stream is encountered after a gzip stream, that remaining trailing garbage is ignored (and no error is returned). gzread can be used to read a gzip file that is being concurrently written. Upon reaching the end of the input, gzread will return with the available data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then gzclearerr can be used to clear the end of file indicator in order to permit gzread to be tried again. Z_OK indicates that a gzip stream was completed on the last gzread. Z_BUF_ERROR indicates that the input file ended in the middle of a gzip stream. Note that gzread does not return -1 in the event of an incomplete gzip stream. This error is deferred until gzclose(), which will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip stream. Alternatively, gzerror can be used before gzclose to detect this case. gzread returns the number of uncompressed bytes actually read, less than len for end of file, or -1 for error. */ ZEXTERN int ZEXPORT gzwrite OF((gzFile file, voidpc buf, unsigned len)); /* Writes the given number of uncompressed bytes into the compressed file. gzwrite returns the number of uncompressed bytes written or 0 in case of error. */ ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); /* Converts, formats, and writes the arguments to the compressed file under control of the format string, as in fprintf. gzprintf returns the number of uncompressed bytes actually written, or 0 in case of error. The number of uncompressed bytes written is limited to 8191, or one less than the buffer size given to gzbuffer(). The caller should assure that this limit is not exceeded. If it is exceeded, then gzprintf() will return an error (0) with nothing written. In this case, there may also be a buffer overflow with unpredictable consequences, which is possible only if zlib was compiled with the insecure functions sprintf() or vsprintf() because the secure snprintf() or vsnprintf() functions were not available. This can be determined using zlibCompileFlags(). */ ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); /* Writes the given null-terminated string to the compressed file, excluding the terminating null character. gzputs returns the number of characters written, or -1 in case of error. */ ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); /* Reads bytes from the compressed file until len-1 characters are read, or a newline character is read and transferred to buf, or an end-of-file condition is encountered. If any characters are read or if len == 1, the string is terminated with a null character. If no characters are read due to an end-of-file or len < 1, then the buffer is left untouched. gzgets returns buf which is a null-terminated string, or it returns NULL for end-of-file or in case of error. If there was an error, the contents at buf are indeterminate. */ ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); /* Writes c, converted to an unsigned char, into the compressed file. gzputc returns the value that was written, or -1 in case of error. */ ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); /* Reads one byte from the compressed file. gzgetc returns this byte or -1 in case of end of file or error. This is implemented as a macro for speed. As such, it does not do all of the checking the other functions do. I.e. it does not check to see if file is NULL, nor whether the structure file points to has been clobbered or not. */ ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); /* Push one character back onto the stream to be read as the first character on the next read. At least one character of push-back is allowed. gzungetc() returns the character pushed, or -1 on failure. gzungetc() will fail if c is -1, and may fail if a character has been pushed but not read yet. If gzungetc is used immediately after gzopen or gzdopen, at least the output buffer size of pushed characters is allowed. (See gzbuffer above.) The pushed character will be discarded if the stream is repositioned with gzseek() or gzrewind(). */ ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); /* Flushes all pending output into the compressed file. The parameter flush is as in the deflate() function. The return value is the zlib error number (see function gzerror below). gzflush is only permitted when writing. If the flush parameter is Z_FINISH, the remaining data is written and the gzip stream is completed in the output. If gzwrite() is called again, a new gzip stream will be started in the output. gzread() is able to read such concatented gzip streams. gzflush should be called only when strictly necessary because it will degrade compression if called too often. */ /* ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, z_off_t offset, int whence)); Sets the starting position for the next gzread or gzwrite on the given compressed file. The offset represents a number of bytes in the uncompressed data stream. The whence parameter is defined as in lseek(2); the value SEEK_END is not supported. If the file is opened for reading, this function is emulated but can be extremely slow. If the file is opened for writing, only forward seeks are supported; gzseek then compresses a sequence of zeroes up to the new starting position. gzseek returns the resulting offset location as measured in bytes from the beginning of the uncompressed stream, or -1 in case of error, in particular if the file is opened for writing and the new starting position would be before the current position. */ ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); /* Rewinds the given file. This function is supported only for reading. gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) */ /* ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); Returns the starting position for the next gzread or gzwrite on the given compressed file. This position represents a number of bytes in the uncompressed data stream, and is zero when starting, even if appending or reading a gzip stream from the middle of a file using gzdopen(). gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) */ /* ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); Returns the current offset in the file being read or written. This offset includes the count of bytes that precede the gzip stream, for example when appending or when using gzdopen() for reading. When reading, the offset does not include as yet unused buffered input. This information can be used for a progress indicator. On error, gzoffset() returns -1. */ ZEXTERN int ZEXPORT gzeof OF((gzFile file)); /* Returns true (1) if the end-of-file indicator has been set while reading, false (0) otherwise. Note that the end-of-file indicator is set only if the read tried to go past the end of the input, but came up short. Therefore, just like feof(), gzeof() may return false even if there is no more data to read, in the event that the last read request was for the exact number of bytes remaining in the input file. This will happen if the input file size is an exact multiple of the buffer size. If gzeof() returns true, then the read functions will return no more data, unless the end-of-file indicator is reset by gzclearerr() and the input file has grown since the previous end of file was detected. */ ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); /* Returns true (1) if file is being copied directly while reading, or false (0) if file is a gzip stream being decompressed. If the input file is empty, gzdirect() will return true, since the input does not contain a gzip stream. If gzdirect() is used immediately after gzopen() or gzdopen() it will cause buffers to be allocated to allow reading the file to determine if it is a gzip file. Therefore if gzbuffer() is used, it should be called before gzdirect(). When writing, gzdirect() returns true (1) if transparent writing was requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: gzdirect() is not needed when writing. Transparent writing must be explicitly requested, so the application already knows the answer. When linking statically, using gzdirect() will include all of the zlib code for gzip file reading and decompression, which may not be desired.) */ ZEXTERN int ZEXPORT gzclose OF((gzFile file)); /* Flushes all pending output if necessary, closes the compressed file and deallocates the (de)compression state. Note that once file is closed, you cannot call gzerror with file, since its structures have been deallocated. gzclose must not be called more than once on the same file, just as free must not be called more than once on the same allocation. gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the last read ended in the middle of a gzip stream, or Z_OK on success. */ ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); /* Same as gzclose(), but gzclose_r() is only for use when reading, and gzclose_w() is only for use when writing or appending. The advantage to using these instead of gzclose() is that they avoid linking in zlib compression or decompression code that is not used when only reading or only writing respectively. If gzclose() is used, then both compression and decompression code will be included the application when linking to a static zlib library. */ ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); /* Returns the error message for the last error which occurred on the given compressed file. errnum is set to zlib error number. If an error occurred in the file system and not in the compression library, errnum is set to Z_ERRNO and the application may consult errno to get the exact error code. The application must not modify the returned string. Future calls to this function may invalidate the previously returned string. If file is closed, then the string previously returned by gzerror will no longer be available. gzerror() should be used to distinguish errors from end-of-file for those functions above that do not distinguish those cases in their return values. */ ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); /* Clears the error and end-of-file flags for file. This is analogous to the clearerr() function in stdio. This is useful for continuing to read a gzip file that is being written concurrently. */ #endif /* !Z_SOLO */ /* checksum functions */ /* These functions are not related to compression but are exported anyway because they might be useful in applications using the compression library. */ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); /* Update a running Adler-32 checksum with the bytes buf[0..len-1] and return the updated checksum. If buf is Z_NULL, this function returns the required initial value for the checksum. An Adler-32 checksum is almost as reliable as a CRC32 but can be computed much faster. Usage example: uLong adler = adler32(0L, Z_NULL, 0); while (read_buffer(buffer, length) != EOF) { adler = adler32(adler, buffer, length); } if (adler != original_adler) error(); */ /* ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, z_off_t len2)); Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note that the z_off_t type (like off_t) is a signed integer. If len2 is negative, the result has no meaning or utility. */ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); /* Update a running CRC-32 with the bytes buf[0..len-1] and return the updated CRC-32. If buf is Z_NULL, this function returns the required initial value for the crc. Pre- and post-conditioning (one's complement) is performed within this function so it shouldn't be done by the application. Usage example: uLong crc = crc32(0L, Z_NULL, 0); while (read_buffer(buffer, length) != EOF) { crc = crc32(crc, buffer, length); } if (crc != original_crc) error(); */ /* ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); Combine two CRC-32 check values into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, CRC-32 check values were calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and len2. */ /* various hacks, don't look :) */ /* deflateInit and inflateInit are macros to allow checking the zlib version * and the compiler's view of z_stream: */ ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, const char *version, int stream_size)); ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, unsigned char FAR *window, const char *version, int stream_size)); #define deflateInit(strm, level) \ deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) #define inflateInit(strm) \ inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) #define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) #define inflateInit2(strm, windowBits) \ inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ (int)sizeof(z_stream)) #define inflateBackInit(strm, windowBits, window) \ inflateBackInit_((strm), (windowBits), (window), \ ZLIB_VERSION, (int)sizeof(z_stream)) #ifndef Z_SOLO /* gzgetc() macro and its supporting function and exposed data structure. Note * that the real internal state is much larger than the exposed structure. * This abbreviated structure exposes just enough for the gzgetc() macro. The * user should not mess with these exposed elements, since their names or * behavior could change in the future, perhaps even capriciously. They can * only be used by the gzgetc() macro. You have been warned. */ struct gzFile_s { unsigned have; unsigned char *next; z_off64_t pos; }; ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ #ifdef Z_PREFIX_SET # undef z_gzgetc # define z_gzgetc(g) \ ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) #else # define gzgetc(g) \ ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) #endif /* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if * both are true, the application gets the *64 functions, and the regular * functions are changed to 64 bits) -- in case these are set on systems * without large file support, _LFS64_LARGEFILE must also be true */ #ifdef Z_LARGE64 ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); #endif #if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) # ifdef Z_PREFIX_SET # define z_gzopen z_gzopen64 # define z_gzseek z_gzseek64 # define z_gztell z_gztell64 # define z_gzoffset z_gzoffset64 # define z_adler32_combine z_adler32_combine64 # define z_crc32_combine z_crc32_combine64 # else # define gzopen gzopen64 # define gzseek gzseek64 # define gztell gztell64 # define gzoffset gzoffset64 # define adler32_combine adler32_combine64 # define crc32_combine crc32_combine64 # endif # ifndef Z_LARGE64 ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); # endif #else ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); #endif #else /* Z_SOLO */ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); #endif /* !Z_SOLO */ /* hack for buggy compilers */ #if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) struct internal_state {int dummy;}; #endif /* undocumented functions */ ZEXTERN const char * ZEXPORT zError OF((int)); ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); #if defined(_WIN32) && !defined(Z_SOLO) ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, const char *mode)); #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, const char *format, va_list va)); # endif #endif #ifdef __cplusplus } #endif #endif /* ZLIB_H */ PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/zutil.c000066400000000000000000000163661231437614300234060ustar00rootroot00000000000000/* zutil.c -- target dependent utility functions for the compression library * Copyright (C) 1995-2005, 2010, 2011, 2012 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #include "zutil.h" #ifndef Z_SOLO # include "gzguts.h" #endif #ifndef NO_DUMMY_DECL struct internal_state {int dummy;}; /* for buggy compilers */ #endif z_const char * const z_errmsg[10] = { "need dictionary", /* Z_NEED_DICT 2 */ "stream end", /* Z_STREAM_END 1 */ "", /* Z_OK 0 */ "file error", /* Z_ERRNO (-1) */ "stream error", /* Z_STREAM_ERROR (-2) */ "data error", /* Z_DATA_ERROR (-3) */ "insufficient memory", /* Z_MEM_ERROR (-4) */ "buffer error", /* Z_BUF_ERROR (-5) */ "incompatible version",/* Z_VERSION_ERROR (-6) */ ""}; const char * ZEXPORT zlibVersion() { return ZLIB_VERSION; } uLong ZEXPORT zlibCompileFlags() { uLong flags; flags = 0; switch ((int)(sizeof(uInt))) { case 2: break; case 4: flags += 1; break; case 8: flags += 2; break; default: flags += 3; } switch ((int)(sizeof(uLong))) { case 2: break; case 4: flags += 1 << 2; break; case 8: flags += 2 << 2; break; default: flags += 3 << 2; } switch ((int)(sizeof(voidpf))) { case 2: break; case 4: flags += 1 << 4; break; case 8: flags += 2 << 4; break; default: flags += 3 << 4; } switch ((int)(sizeof(z_off_t))) { case 2: break; case 4: flags += 1 << 6; break; case 8: flags += 2 << 6; break; default: flags += 3 << 6; } #ifdef DEBUG flags += 1 << 8; #endif #if defined(ASMV) || defined(ASMINF) flags += 1 << 9; #endif #ifdef ZLIB_WINAPI flags += 1 << 10; #endif #ifdef BUILDFIXED flags += 1 << 12; #endif #ifdef DYNAMIC_CRC_TABLE flags += 1 << 13; #endif #ifdef NO_GZCOMPRESS flags += 1L << 16; #endif #ifdef NO_GZIP flags += 1L << 17; #endif #ifdef PKZIP_BUG_WORKAROUND flags += 1L << 20; #endif #ifdef FASTEST flags += 1L << 21; #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifdef NO_vsnprintf flags += 1L << 25; # ifdef HAS_vsprintf_void flags += 1L << 26; # endif # else # ifdef HAS_vsnprintf_void flags += 1L << 26; # endif # endif #else flags += 1L << 24; # ifdef NO_snprintf flags += 1L << 25; # ifdef HAS_sprintf_void flags += 1L << 26; # endif # else # ifdef HAS_snprintf_void flags += 1L << 26; # endif # endif #endif return flags; } #ifdef DEBUG # ifndef verbose # define verbose 0 # endif int ZLIB_INTERNAL z_verbose = verbose; void ZLIB_INTERNAL z_error (m) char *m; { fprintf(stderr, "%s\n", m); exit(1); } #endif /* exported to allow conversion of error code to string for compress() and * uncompress() */ const char * ZEXPORT zError(err) int err; { return ERR_MSG(err); } #if defined(_WIN32_WCE) /* The Microsoft C Run-Time Library for Windows CE doesn't have * errno. We define it as a global variable to simplify porting. * Its value is always 0 and should not be used. */ int errno = 0; #endif #ifndef HAVE_MEMCPY void ZLIB_INTERNAL zmemcpy(dest, source, len) Bytef* dest; const Bytef* source; uInt len; { if (len == 0) return; do { *dest++ = *source++; /* ??? to be unrolled */ } while (--len != 0); } int ZLIB_INTERNAL zmemcmp(s1, s2, len) const Bytef* s1; const Bytef* s2; uInt len; { uInt j; for (j = 0; j < len; j++) { if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; } return 0; } void ZLIB_INTERNAL zmemzero(dest, len) Bytef* dest; uInt len; { if (len == 0) return; do { *dest++ = 0; /* ??? to be unrolled */ } while (--len != 0); } #endif #ifndef Z_SOLO #ifdef SYS16BIT #ifdef __TURBOC__ /* Turbo C in 16-bit mode */ # define MY_ZCALLOC /* Turbo C malloc() does not allow dynamic allocation of 64K bytes * and farmalloc(64K) returns a pointer with an offset of 8, so we * must fix the pointer. Warning: the pointer must be put back to its * original form in order to free it, use zcfree(). */ #define MAX_PTR 10 /* 10*64K = 640K */ local int next_ptr = 0; typedef struct ptr_table_s { voidpf org_ptr; voidpf new_ptr; } ptr_table; local ptr_table table[MAX_PTR]; /* This table is used to remember the original form of pointers * to large buffers (64K). Such pointers are normalized with a zero offset. * Since MSDOS is not a preemptive multitasking OS, this table is not * protected from concurrent access. This hack doesn't work anyway on * a protected system like OS/2. Use Microsoft C instead. */ voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) { voidpf buf = opaque; /* just to make some compilers happy */ ulg bsize = (ulg)items*size; /* If we allocate less than 65520 bytes, we assume that farmalloc * will return a usable pointer which doesn't have to be normalized. */ if (bsize < 65520L) { buf = farmalloc(bsize); if (*(ush*)&buf != 0) return buf; } else { buf = farmalloc(bsize + 16L); } if (buf == NULL || next_ptr >= MAX_PTR) return NULL; table[next_ptr].org_ptr = buf; /* Normalize the pointer to seg:0 */ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; *(ush*)&buf = 0; table[next_ptr++].new_ptr = buf; return buf; } void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) { int n; if (*(ush*)&ptr != 0) { /* object < 64K */ farfree(ptr); return; } /* Find the original pointer */ for (n = 0; n < next_ptr; n++) { if (ptr != table[n].new_ptr) continue; farfree(table[n].org_ptr); while (++n < next_ptr) { table[n-1] = table[n]; } next_ptr--; return; } ptr = opaque; /* just to make some compilers happy */ Assert(0, "zcfree: ptr not found"); } #endif /* __TURBOC__ */ #ifdef M_I86 /* Microsoft C in 16-bit mode */ # define MY_ZCALLOC #if (!defined(_MSC_VER) || (_MSC_VER <= 600)) # define _halloc halloc # define _hfree hfree #endif voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) { if (opaque) opaque = 0; /* to make compiler happy */ return _halloc((long)items, size); } void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) { if (opaque) opaque = 0; /* to make compiler happy */ _hfree(ptr); } #endif /* M_I86 */ #endif /* SYS16BIT */ #ifndef MY_ZCALLOC /* Any system without a special alloc function */ #ifndef STDC extern voidp malloc OF((uInt size)); extern voidp calloc OF((uInt items, uInt size)); extern void free OF((voidpf ptr)); #endif voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) voidpf opaque; unsigned items; unsigned size; { if (opaque) items += size - size; /* make compiler happy */ return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : (voidpf)calloc(items, size); } void ZLIB_INTERNAL zcfree (opaque, ptr) voidpf opaque; voidpf ptr; { free(ptr); if (opaque) return; /* make compiler happy */ } #endif /* MY_ZCALLOC */ #endif /* !Z_SOLO */ PyTables-v.3.1.1/c-blosc/internal-complibs/zlib-1.2.8/zutil.h000066400000000000000000000151561231437614300234070ustar00rootroot00000000000000/* zutil.h -- internal interface and configuration of the compression library * Copyright (C) 1995-2013 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* @(#) $Id$ */ #ifndef ZUTIL_H #define ZUTIL_H #ifdef HAVE_HIDDEN # define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) #else # define ZLIB_INTERNAL #endif #include "zlib.h" #if defined(STDC) && !defined(Z_SOLO) # if !(defined(_WIN32_WCE) && defined(_MSC_VER)) # include # endif # include # include #endif #ifdef Z_SOLO typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ typedef unsigned char uch; typedef uch FAR uchf; typedef unsigned short ush; typedef ush FAR ushf; typedef unsigned long ulg; extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* (size given to avoid silly warnings with Visual C++) */ #define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] #define ERR_RETURN(strm,err) \ return (strm->msg = ERR_MSG(err), (err)) /* To be used only when the state is known to be valid */ /* common constants */ #ifndef DEF_WBITS # define DEF_WBITS MAX_WBITS #endif /* default windowBits for decompression. MAX_WBITS is for compression only */ #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif /* default memLevel */ #define STORED_BLOCK 0 #define STATIC_TREES 1 #define DYN_TREES 2 /* The three kinds of block type */ #define MIN_MATCH 3 #define MAX_MATCH 258 /* The minimum and maximum match lengths */ #define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ /* target dependencies */ #if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) # define OS_CODE 0x00 # ifndef Z_SOLO # if defined(__TURBOC__) || defined(__BORLANDC__) # if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) /* Allow compilation with ANSI keywords only enabled */ void _Cdecl farfree( void *block ); void *_Cdecl farmalloc( unsigned long nbytes ); # else # include # endif # else /* MSC or DJGPP */ # include # endif # endif #endif #ifdef AMIGA # define OS_CODE 0x01 #endif #if defined(VAXC) || defined(VMS) # define OS_CODE 0x02 # define F_OPEN(name, mode) \ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") #endif #if defined(ATARI) || defined(atarist) # define OS_CODE 0x05 #endif #ifdef OS2 # define OS_CODE 0x06 # if defined(M_I86) && !defined(Z_SOLO) # include # endif #endif #if defined(MACOS) || defined(TARGET_OS_MAC) # define OS_CODE 0x07 # ifndef Z_SOLO # if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os # include /* for fdopen */ # else # ifndef fdopen # define fdopen(fd,mode) NULL /* No fdopen() */ # endif # endif # endif #endif #ifdef TOPS20 # define OS_CODE 0x0a #endif #ifdef WIN32 # ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ # define OS_CODE 0x0b # endif #endif #ifdef __50SERIES /* Prime/PRIMOS */ # define OS_CODE 0x0f #endif #if defined(_BEOS_) || defined(RISCOS) # define fdopen(fd,mode) NULL /* No fdopen() */ #endif #if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX # if defined(_WIN32_WCE) # define fdopen(fd,mode) NULL /* No fdopen() */ # ifndef _PTRDIFF_T_DEFINED typedef int ptrdiff_t; # define _PTRDIFF_T_DEFINED # endif # else # define fdopen(fd,type) _fdopen(fd,type) # endif #endif #if defined(__BORLANDC__) && !defined(MSDOS) #pragma warn -8004 #pragma warn -8008 #pragma warn -8066 #endif /* provide prototypes for these when building zlib without LFS */ #if !defined(_WIN32) && \ (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); #endif /* common defaults */ #ifndef OS_CODE # define OS_CODE 0x03 /* assume Unix */ #endif #ifndef F_OPEN # define F_OPEN(name, mode) fopen((name), (mode)) #endif /* functions */ #if defined(pyr) || defined(Z_SOLO) # define NO_MEMCPY #endif #if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) /* Use our own functions for small and medium model with MSC <= 5.0. * You may have to use the same strategy for Borland C (untested). * The __SC__ check is for Symantec. */ # define NO_MEMCPY #endif #if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) # define HAVE_MEMCPY #endif #ifdef HAVE_MEMCPY # ifdef SMALL_MEDIUM /* MSDOS small or medium model */ # define zmemcpy _fmemcpy # define zmemcmp _fmemcmp # define zmemzero(dest, len) _fmemset(dest, 0, len) # else # define zmemcpy memcpy # define zmemcmp memcmp # define zmemzero(dest, len) memset(dest, 0, len) # endif #else void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); #endif /* Diagnostic functions */ #ifdef DEBUG # include extern int ZLIB_INTERNAL z_verbose; extern void ZLIB_INTERNAL z_error OF((char *m)); # define Assert(cond,msg) {if(!(cond)) z_error(msg);} # define Trace(x) {if (z_verbose>=0) fprintf x ;} # define Tracev(x) {if (z_verbose>0) fprintf x ;} # define Tracevv(x) {if (z_verbose>1) fprintf x ;} # define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} # define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} #else # define Assert(cond,msg) # define Trace(x) # define Tracev(x) # define Tracevv(x) # define Tracec(c,x) # define Tracecv(c,x) #endif #ifndef Z_SOLO voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, unsigned size)); void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); #endif #define ZALLOC(strm, items, size) \ (*((strm)->zalloc))((strm)->opaque, (items), (size)) #define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) #define TRY_FREE(s, p) {if (p) ZFREE(s, p);} /* Reverse the bytes in a 32-bit value */ #define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) #endif /* ZUTIL_H */ PyTables-v.3.1.1/c-blosc/tests/000077500000000000000000000000001231437614300161735ustar00rootroot00000000000000PyTables-v.3.1.1/c-blosc/tests/.gitignore000066400000000000000000000000061231437614300201570ustar00rootroot00000000000000*.exe PyTables-v.3.1.1/c-blosc/tests/CMakeLists.txt000066400000000000000000000005671231437614300207430ustar00rootroot00000000000000# sources #aux_source_directory(. SOURCES) file(GLOB SOURCES test_*.c) # flags link_directories(${PROJECT_BINARY_DIR}/blosc) # targets and tests foreach(source ${SOURCES}) get_filename_component(target ${source} NAME_WE) add_executable(${target} ${source}) target_link_libraries(${target} blosc_shared) add_test(test_${target} ${target}) endforeach(source) PyTables-v.3.1.1/c-blosc/tests/Makefile000066400000000000000000000020611231437614300176320ustar00rootroot00000000000000CC=gcc CFLAGS=-O3 -msse2 -Wall -pthread LDFLAGS=-pthread BLOSC_LIB= $(wildcard ../blosc/*.c) # The list of executables # Generated PNG (intermediate) files SOURCES := $(wildcard *.c) EXECUTABLES := $(patsubst %.c, %.exe, $(SOURCES)) # Support for internal LZ4 and LZ4HC LZ4_DIR = ../internal-complibs/lz4-r113 CFLAGS += -DHAVE_LZ4 -I$(LZ4_DIR) BLOSC_LIB += $(wildcard $(LZ4_DIR)/*.c) # Support for external LZ4 and LZ4HC #LDFLAGS += -DHAVE_LZ4 -llz4 # Support for internal Snappy #SNAPPY_DIR = ../internal-complibs/snappy-1.1.1 #CFLAGS += -DHAVE_SNAPPY -I$(SNAPPY_DIR) #BLOSC_LIB += $(wildcard $(SNAPPY_DIR)/*.cc) # Support for external Snappy LDFLAGS += -DHAVE_SNAPPY -lsnappy # Support for external Zlib LDFLAGS += -DHAVE_ZLIB -lz # Support for internal Zlib #ZLIB_DIR = ../internal-complibs/zlib-1.2.8 #CFLAGS += -DHAVE_ZLIB -I$(ZLIB_DIR) #BLOSC_LIB += $(wildcard $(ZLIB_DIR)/*.c) .PHONY: all all: $(EXECUTABLES) test: $(EXECUTABLES) sh test_all.sh %.exe: %.c $(BLOSC_LIB) $(CC) $(CFLAGS) $(LDFLAGS) "$<" $(BLOSC_LIB) -o "$@" clean: rm -rf $(EXECUTABLES) PyTables-v.3.1.1/c-blosc/tests/print_versions.c000066400000000000000000000020531231437614300214230ustar00rootroot00000000000000/********************************************************************* Print versions for Blosc and all its internal compressors. *********************************************************************/ #include #include #include #include "../blosc/blosc.h" int main(int argc, char *argv[]) { char *name = NULL, *version = NULL; int ret; printf("Blosc version: %s (%s)\n", BLOSC_VERSION_STRING, BLOSC_VERSION_DATE); printf("List of supported compressors in this build: %s\n", blosc_list_compressors()); printf("Supported compression libraries:\n"); ret = blosc_get_complib_info("blosclz", &name, &version); if (ret >= 0) printf(" %s: %s\n", name, version); ret = blosc_get_complib_info("lz4", &name, &version); if (ret >= 0) printf(" %s: %s\n", name, version); ret = blosc_get_complib_info("snappy", &name, &version); if (ret >= 0) printf(" %s: %s\n", name, version); ret = blosc_get_complib_info("zlib", &name, &version); if (ret >= 0) printf(" %s: %s\n", name, version); return(0); } PyTables-v.3.1.1/c-blosc/tests/test_all.sh000066400000000000000000000006631231437614300203430ustar00rootroot00000000000000#********************************************************************* # Blosc - Blocked Suffling and Compression Library # # Unit tests for basic features in Blosc. # # Creation date: 2010-06-07 # Author: Francesc Alted # # See LICENSES/BLOSC.txt for details about copyright and rights to use. #********************************************************************** for exe in $(ls *.exe); do ./$exe done PyTables-v.3.1.1/c-blosc/tests/test_api.c000066400000000000000000000052601231437614300201520ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Suffling and Compression Library Unit tests for Blosc API. Creation date: 2010-06-07 Author: Francesc Alted See LICENSES/BLOSC.txt for details about copyright and rights to use. **********************************************************************/ #include "test_common.h" int tests_run = 0; /* Global vars */ void *src, *srccpy, *dest, *dest2; size_t nbytes, cbytes; int clevel = 3; int doshuffle = 1; size_t typesize = 4; size_t size = 1*MB; static char *test_cbuffer_sizes() { size_t nbytes_, cbytes_, blocksize; blosc_cbuffer_sizes(dest, &nbytes_, &cbytes_, &blocksize); mu_assert("ERROR: nbytes incorrect(1)", nbytes == size); mu_assert("ERROR: nbytes incorrect(2)", nbytes_ == nbytes); mu_assert("ERROR: cbytes incorrect", cbytes == cbytes_); mu_assert("ERROR: blocksize incorrect", blocksize >= 128); return 0; } static char *test_cbuffer_metainfo() { size_t typesize_; int flags; blosc_cbuffer_metainfo(dest, &typesize_, &flags); mu_assert("ERROR: typesize incorrect", typesize_ == typesize); mu_assert("ERROR: shuffle incorrect", (flags & BLOSC_DOSHUFFLE) == doshuffle); return 0; } static char *test_cbuffer_versions() { int version_; int versionlz_; blosc_cbuffer_versions(dest, &version_, &versionlz_); mu_assert("ERROR: version incorrect", version_ == BLOSC_VERSION_FORMAT); mu_assert("ERROR: versionlz incorrect", versionlz_ == BLOSC_BLOSCLZ_VERSION_FORMAT); return 0; } static char *test_cbuffer_complib() { char *complib; complib = blosc_cbuffer_complib(dest); mu_assert("ERROR: complib incorrect", strcmp(complib, "BloscLZ") == 0); return 0; } static char *all_tests() { mu_run_test(test_cbuffer_sizes); mu_run_test(test_cbuffer_metainfo); mu_run_test(test_cbuffer_versions); mu_run_test(test_cbuffer_complib); return 0; } int main(int argc, char **argv) { char *result; printf("STARTING TESTS for %s", argv[0]); blosc_init(); blosc_set_nthreads(1); /* Initialize buffers */ src = malloc(size); srccpy = malloc(size); dest = malloc(size); dest2 = malloc(size); memset(src, 0, size); memcpy(srccpy, src, size); /* Get a compressed buffer */ cbytes = blosc_compress(clevel, doshuffle, typesize, size, src, dest, size); /* Get a decompressed buffer */ nbytes = blosc_decompress(dest, dest2, size); /* Run all the suite */ result = all_tests(); if (result != 0) { printf(" (%s)\n", result); } else { printf(" ALL TESTS PASSED"); } printf("\tTests run: %d\n", tests_run); free(src); free(srccpy); free(dest); free(dest2); blosc_destroy(); return result != 0; } PyTables-v.3.1.1/c-blosc/tests/test_basics.c000066400000000000000000000066131231437614300206500ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Suffling and Compression Library Unit tests for basic features in Blosc. Creation date: 2010-06-07 Author: Francesc Alted See LICENSES/BLOSC.txt for details about copyright and rights to use. **********************************************************************/ #include "test_common.h" int tests_run = 0; /* Global vars */ void *src, *srccpy, *dest, *dest2; size_t nbytes, cbytes; int clevel = 1; int doshuffle = 0; size_t typesize = 4; size_t size = 1000; /* must be divisible by 4 */ /* Check maxout with maxout < size */ static char *test_maxout_less() { /* Get a compressed buffer */ cbytes = blosc_compress(clevel, doshuffle, typesize, size, src, dest, size+15); mu_assert("ERROR: cbytes is not 0", cbytes == 0); return 0; } /* Check maxout with maxout == size */ static char *test_maxout_equal() { /* Get a compressed buffer */ cbytes = blosc_compress(clevel, doshuffle, typesize, size, src, dest, size+16); mu_assert("ERROR: cbytes is not correct", cbytes == size+16); /* Decompress the buffer */ nbytes = blosc_decompress(dest, dest2, size); mu_assert("ERROR: nbytes incorrect(1)", nbytes == size); return 0; } /* Check maxout with maxout > size */ static char *test_maxout_great() { /* Get a compressed buffer */ cbytes = blosc_compress(clevel, doshuffle, typesize, size, src, dest, size+17); mu_assert("ERROR: cbytes is not 0", cbytes == size+16); /* Decompress the buffer */ nbytes = blosc_decompress(dest, dest2, size); mu_assert("ERROR: nbytes incorrect(1)", nbytes == size); return 0; } static char * test_shuffle() { int sizes[] = {7, 64 * 3, 7*256, 500, 8000, 100000, 702713}; int types[] = {1, 2, 3, 4, 5, 6, 7, 8, 16}; int i, j, k; int ok; for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) { for (j = 0; j < sizeof(types) / sizeof(types[0]); j++) { int n = sizes[i]; int t = types[j]; char * d = malloc(t * n); char * d2 = malloc(t * n); char * o = malloc(t * n + BLOSC_MAX_OVERHEAD); for (k = 0; k < n; k++) { d[k] = rand(); } blosc_compress(5, 1, t, t * n, d, o, t * n + BLOSC_MAX_OVERHEAD); blosc_decompress(o, d2, t * n); ok = 1; for (k = 0; ok&& k < n; k++) { ok = (d[k] == d2[k]); } free(d); free(d2); free(o); mu_assert("ERROR: multi size test failed", ok); } } return 0; } static char *all_tests() { mu_run_test(test_maxout_less); mu_run_test(test_maxout_equal); mu_run_test(test_maxout_great); mu_run_test(test_shuffle); return 0; } int main(int argc, char **argv) { size_t i; int32_t *_src; char *result; printf("STARTING TESTS for %s", argv[0]); blosc_init(); blosc_set_nthreads(1); /* Initialize buffers */ src = malloc(size); srccpy = malloc(size); dest = malloc(size+16); dest2 = malloc(size); _src = (int32_t *)src; for (i=0; i < (size/4); i++) { _src[i] = i; } memcpy(srccpy, src, size); /* Run all the suite */ result = all_tests(); if (result != 0) { printf(" (%s)\n", result); } else { printf(" ALL TESTS PASSED"); } printf("\tTests run: %d\n", tests_run); free(src); free(srccpy); free(dest); free(dest2); blosc_destroy(); return result != 0; } PyTables-v.3.1.1/c-blosc/tests/test_common.h000066400000000000000000000022241231437614300206730ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Suffling and Compression Library Unit tests for basic features in Blosc. Creation date: 2010-06-07 Author: Francesc Alted See LICENSES/BLOSC.txt for details about copyright and rights to use. **********************************************************************/ #include #include #include #include #include #include #if defined(_WIN32) && !defined(__MINGW32__) #include #include "win32/stdint-windows.h" #else #include #include #endif #include #include "../blosc/blosc.h" /* This is MinUnit in action (http://www.jera.com/techinfo/jtns/jtn002.html) */ #define mu_assert(message, test) do { if (!(test)) return message; } while (0) #define mu_run_test(test) do \ { char *message = test(); tests_run++; \ if (message) { printf("%c", 'F'); return message;} \ else printf("%c", '.'); } while (0) extern int tests_run; #define KB 1024 #define MB (1024*KB) #define GB (1024*MB) PyTables-v.3.1.1/contrib/000077500000000000000000000000001231437614300151475ustar00rootroot00000000000000PyTables-v.3.1.1/contrib/README000066400000000000000000000006531231437614300160330ustar00rootroot00000000000000In these directories you can find some scripts contributed by PyTables users. If you have any suggestion on them, please contact the original authors. nctoh5.py: Converts netcdf files to hdf5. You can find an improved nctoh5 utility included in utils/ directory Author: Jeff Whitaker make_hdf.py: Converts general python data structures into hdf5 Author: John Nielsen PyTables-v.3.1.1/contrib/make_hdf.py000066400000000000000000000250021231437614300172560ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import generators import tables, cPickle, time ################################################################################# def is_scalar(item): try: iter(item) #could be a string try: item[:0]+'' #check for string return 'str' except: return 0 except: return 'notstr' def is_dict(item): try: item.iteritems() return 1 except: return 0 def make_col(row_type, row_name, row_item, str_len): '''for strings it will always make at least 80 char or twice mac char size''' set_len=80 if str_len: if 2*str_len>set_len: set_len=2*str_len row_type[row_name]=tables.Col("CharType", set_len) else: type_matrix={ int: tables.Col("Int32", 1), float: tables.Col("Float32", 4), #Col("Int16", 1) } row_type[row_name]=type_matrix[type(row_item)] def make_row(data): row_type={} scalar_type=is_scalar(data) if scalar_type: if scalar_type=='str': make_col(row_type, 'scalar', data, len(data)) else: make_col(row_type, 'scalar', data, 0) else: #it is a list-like the_type=is_scalar(data[0]) if the_type=='str': #get max length the_max=0 for i in data: if len(i)>the_max: the_max=len(i) make_col(row_type, 'col', data[0], the_max) elif the_type: make_col(row_type, 'col', data[0], 0) else: #list within the list, make many columns make_col(row_type, 'col_depth', 0, 0) count=0 for col in data: the_type=is_scalar(col[0]) if the_type=='str': #get max length the_max=0 for i in data: if len(i)>the_max: the_max=len(i) make_col(row_type, 'col_'+str(count), col[0], the_max) elif the_type: make_col(row_type, 'col_'+str(count), col[0], 0) else: raise ValueError('too many nested levels of lists') count+=1 return row_type def add_table(fileh, group_obj, data, table_name): #figure out if it is a list of lists or a single list #get types of columns row_type=make_row(data) table1=fileh.createTable(group_obj, table_name, row_type, 'H', compress=1) row=table1.row if is_scalar(data): row['scalar']=data row.append() else: if is_scalar(data[0]): for i in data: row['col']=i row.append() else: count=0 for col in data: row['col_depth']=len(col) for the_row in col: if is_scalar(the_row): row['col_'+str(count)]=the_row row.append() else: raise ValueError('too many levels of lists') count+=1 table1.flush() def add_cache(fileh, cache): group_name='pytables_cache_v0';table_name='cache0' root=fileh.root group_obj=fileh.createGroup(root, group_name) cache_str=cPickle.dumps(cache, 0) cache_str=cache_str.replace('\n', chr(1)) cache_pieces=[] while cache_str: cache_part=cache_str[:8000];cache_str=cache_str[8000:] if cache_part: cache_pieces.append(cache_part) row_type={} row_type['col_0']=tables.Col("CharType", 8000) # table_cache=fileh.createTable(group_obj, table_name, row_type, 'H', compress =1) for piece in cache_pieces: print len(piece) table_cache.row['col_0']=piece table_cache.row.append() table_cache.flush() def save2(hdf_file, data): fileh=tables.openFile(hdf_file, mode='w', title='logon history') root=fileh.root;cache_root=cache={} root_path=root._v_pathname;root=0 stack = [ (root_path, data, cache) ] table_num=0 count=0 while stack: (group_obj_path, data, cache)=stack.pop() #data='wilma':{'mother':[22,23,24]}} #grp_name wilma for grp_name in data: #print 'fileh=',fileh count+=1 cache[grp_name]={} new_group_obj=fileh.createGroup(group_obj_path, grp_name) #print 'path=',new_group_obj._v_pathname new_path=new_group_obj._v_pathname #if dict, you have a bunch of groups if is_dict(data[grp_name]):#{'mother':[22,23,24]} stack.append((new_path, data[grp_name], cache[grp_name])) #you have a table else: #data[grp_name]=[110,130,140],[1,2,3] add_table(fileh, new_path, data[grp_name], 'tbl_'+str(table_num)) table_num+=1 #fileh=tables.openFile(hdf_file,mode='a',title='logon history') add_cache(fileh, cache_root) fileh.close() ######################## class Hdf_dict(dict): def __init__(self,hdf_file,hdf_dict={},stack=[]): self.hdf_file=hdf_file self.stack=stack if stack: self.hdf_dict=hdf_dict else: self.hdf_dict=self.get_cache() self.cur_dict=self.hdf_dict def get_cache(self): fileh=tables.openFile(self.hdf_file, rootUEP='pytables_cache_v0') table=fileh.root.cache0 total=[] print 'reading' begin=time.time() for i in table.iterrows(): total.append(i['col_0']) total=''.join(total) total=total.replace(chr(1), '\n') print 'loaded cache len=', len(total), time.time()-begin begin=time.time() a=cPickle.loads(total) print 'cache', time.time()-begin return a def has_key(self, k): return k in self.cur_dict def keys(self): return self.cur_dict.keys() def get(self,key,default=None): try: return self.__getitem__(key) except: return default def items(self): return list(self.iteritems()) def values(self): return list(self.itervalues()) ########################################### def __len__(self): return len(self.cur_dict) def __getitem__(self, k): if k in self.cur_dict: #now check if k has any data if self.cur_dict[k]: new_stack=self.stack[:] new_stack.append(k) return Hdf_dict(self.hdf_file, hdf_dict=self.cur_dict[k], stack=new_stack) else: new_stack=self.stack[:] new_stack.append(k) fileh=tables.openFile(self.hdf_file, rootUEP='/'.join(new_stack)) #cur_data=getattr(self.cur_group,k) #/wilma (Group) '' =getattr(/ (Group) 'logon history',wilma) for table in fileh.root: #return [ i['col_1'] for i in table.iterrows() ] #[9110,91] #perhaps they stored a single item try: for item in table['scalar']: return item except: #otherwise they stored a list of data try: return [ item for item in table['col']] except: cur_column=[] total_columns=[] col_num=0 cur_row=0 num_rows=0 for row in table: if not num_rows: num_rows=row['col_depth'] if cur_row==num_rows: cur_row=num_rows=0 col_num+=1 total_columns.append(cur_column) cur_column=[] cur_column.append( row['col_'+str(col_num)]) cur_row+=1 total_columns.append(cur_column) return total_columns else: raise KeyError(k) def iterkeys(self): for key in self.iterkeys(): yield key def __iter__(self): return self.iterkeys() def itervalues(self): for k in self.iterkeys(): v=self.__getitem__(k) yield v def iteritems(self): # yield children for k in self.iterkeys(): v=self.__getitem__(k) yield (k, v) def __repr__(self): return '{Hdf dict}' def __str__(self): return self.__repr__() ##### def setdefault(self,key,default=None): try: return self.__getitem__(key) except: self.__setitem__(key) return default def update(self, d): for k, v in d.iteritems(): self.__setitem__(k, v) def popitem(self): try: k, v = self.iteritems().next() del self[k] return k, v except StopIteration: raise KeyError("Hdf Dict is empty") def __setitem__(self, key, value): raise NotImplementedError def __delitem__(self, key): raise NotImplementedError def __hash__(self): raise TypeError("Hdf dict bjects are unhashable") if __name__=='__main__': def write_small(file=''): data1={ 'fred':['a', 'b', 'c'], 'barney':[[9110, 9130, 9140], [91, 92, 93]], 'wilma':{'mother':{'pebbles':[22, 23, 24],'bambam':[67, 68, 69]}} } print 'saving' save2(file, data1) print 'saved' def read_small(file=''): #a=make_hdf.Hdf_dict(file) a=Hdf_dict(file) print a['wilma'] b=a['wilma'] for i in b: print i print a.keys() print 'has fred', bool('fred' in a) print 'length a', len(a) print 'get', a.get('fred'), a.get('not here') print 'wilma keys', a['wilma'].keys() print 'barney', a['barney'] print 'get items' print a.items() for i in a.iteritems(): print 'item', i for i in a.itervalues(): print i a=raw_input('enter y to write out test file to test.hdf') if a.strip()=='y': print 'writing' write_small('test.hdf') print 'reading' read_small('test.hdf') PyTables-v.3.1.1/contrib/nctoh5.py000077500000000000000000000035151231437614300167300ustar00rootroot00000000000000#!/usr/bin/env python """ convert netCDF file to HDF5 using Scientific.IO.NetCDF and PyTables. Jeff Whitaker This requires Scientific from http://starship.python.net/~hinsen/ScientificPython """ from Scientific.IO import NetCDF import tables, sys # open netCDF file ncfile = NetCDF.NetCDFFile(sys.argv[1], mode = "r") # open h5 file. h5file = tables.openFile(sys.argv[2], mode = "w") # loop over variables in netCDF file. for varname in ncfile.variables.keys(): var = ncfile.variables[varname] vardims = list(var.dimensions) vardimsizes = [ncfile.dimensions[vardim] for vardim in vardims] # use long_name for title. if hasattr(var, 'long_name'): title = var.long_name else: # or, just use some bogus title. title = varname + ' array' # if variable has unlimited dimension or has rank>1, # make it enlargeable (with zlib compression). if vardimsizes[0] == None or len(vardimsizes) > 1: vardimsizes[0] = 0 vardata = h5file.createEArray(h5file.root, varname, tables.Atom(shape=tuple(vardimsizes), dtype=var.typecode(),), title, filters=tables.Filters(complevel=6, complib='zlib')) # write data to enlargeable array on record at a time. # (so the whole array doesn't have to be kept in memory). for n in range(var.shape[0]): vardata.append(var[n:n+1]) # or else, create regular array write data to it all at once. else: vardata=h5file.createArray(h5file.root, varname, var[:], title) # set variable attributes. for key, val in var.__dict__.iteritems(): setattr(vardata.attrs, key, val) setattr(vardata.attrs, 'dimensions', tuple(vardims)) # set global (file) attributes. for key, val in ncfile.__dict__.iteritems(): setattr(h5file.root._v_attrs, key, val) # Close the file h5file.close() PyTables-v.3.1.1/doc/000077500000000000000000000000001231437614300142545ustar00rootroot00000000000000PyTables-v.3.1.1/doc/Makefile000066400000000000000000000116241231437614300157200ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest all dist distclean help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR) html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pytables.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pytables.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/pytables" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pytables" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." VERSION = $(shell cat ../VERSION) all: html rm -rf html mv $(BUILDDIR)/html . dist: all latexpdf rm -rf usersguide-$(VERSION).pdf mv $(BUILDDIR)/latex/usersguide-$(VERSION).pdf . mkdir -p ../dist cp usersguide-$(VERSION).pdf ../dist/pytablesmanual-$(VERSION).pdf tar cvzf ../dist/pytablesmanual-$(VERSION)-html.tar.gz html distclean: clean -rm -rf html usersguide-$(VERSION).pdf PyTables-v.3.1.1/doc/make.bat000066400000000000000000000106471231437614300156710ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pytables.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pytables.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end PyTables-v.3.1.1/doc/scripts/000077500000000000000000000000001231437614300157435ustar00rootroot00000000000000PyTables-v.3.1.1/doc/scripts/filenode.py000066400000000000000000000023571231437614300201110ustar00rootroot00000000000000# Copy this file into the clipboard and paste into 'script -c python'. from __future__ import print_function from tables.nodes import FileNode import tables h5file = tables.open_file('fnode.h5', 'w') fnode = FileNode.new_node(h5file, where='/', name='fnode_test') print(h5file.getAttrNode('/fnode_test', 'NODE_TYPE')) print("This is a test text line.", file=fnode) print("And this is another one.", file=fnode) print(file=fnode) fnode.write("Of course, file methods can also be used.") fnode.seek(0) # Go back to the beginning of file. for line in fnode: print(repr(line)) fnode.close() print(fnode.closed) node = h5file.root.fnode_test fnode = FileNode.open_node(node, 'a+') print(repr(fnode.readline())) print(fnode.tell()) print("This is a new line.", file=fnode) print(repr(fnode.readline())) fnode.seek(0) for line in fnode: print(repr(line)) fnode.attrs.content_type = 'text/plain; charset=us-ascii' fnode.attrs.author = "Ivan Vilata i Balaguer" fnode.attrs.creation_date = '2004-10-20T13:25:25+0200' fnode.attrs.keywords_en = ["FileNode", "test", "metadata"] fnode.attrs.keywords_ca = ["FileNode", "prova", "metadades"] fnode.attrs.owner = 'ivan' fnode.attrs.acl = {'ivan': 'rw', '@users': 'r'} fnode.close() h5file.close() PyTables-v.3.1.1/doc/scripts/pickletrouble.py000066400000000000000000000013641231437614300211650ustar00rootroot00000000000000from __future__ import print_function import tables class MyClass(object): foo = 'bar' # An object of my custom class. myObject = MyClass() h5f = tables.open_file('test.h5', 'w') h5f.root._v_attrs.obj = myObject # store the object print(h5f.root._v_attrs.obj.foo) # retrieve it h5f.close() # Delete class of stored object and reopen the file. del MyClass, myObject h5f = tables.open_file('test.h5', 'r') print(h5f.root._v_attrs.obj.foo) # Let us inspect the object to see what is happening. print(repr(h5f.root._v_attrs.obj)) # Maybe unpickling the string will yield more information: import pickle pickle.loads(h5f.root._v_attrs.obj) # So the problem was not in the stored object, # but in the *environment* where it was restored. h5f.close() PyTables-v.3.1.1/doc/scripts/tutorial1.py000066400000000000000000000175141231437614300202510ustar00rootroot00000000000000"""Small but quite comprehensive example showing the use of PyTables. The program creates an output file, 'tutorial1.h5'. You can view it with any HDF5 generic utility. """ from __future__ import print_function import os import traceback SECTION = "I HAVE NO TITLE" def tutsep(): print('----8<----', SECTION, '----8<----') def tutprint(obj): tutsep() print(obj) def tutrepr(obj): tutsep() print(repr(obj)) def tutexc(): tutsep() traceback.print_exc(file=sys.stdout) SECTION = "Importing tables objects" from numpy import * from tables import * SECTION = "Declaring a Column Descriptor" # Define a user record to characterize some kind of particles class Particle(IsDescription): name = StringCol(16) # 16-character String idnumber = Int64Col() # Signed 64-bit integer ADCcount = UInt16Col() # Unsigned short integer TDCcount = UInt8Col() # unsigned byte grid_i = Int32Col() # integer grid_j = IntCol() # integer (equivalent to Int32Col) pressure = Float32Col() # float (single-precision) energy = FloatCol() # double (double-precision) SECTION = "Creating a PyTables file from scratch" # Open a file in "w"rite mode h5file = open_file('tutorial1.h5', mode="w", title="Test file") SECTION = "Creating a new group" # Create a new group under "/" (root) group = h5file.create_group("/", 'detector', 'Detector information') SECTION = "Creating a new table" # Create one table on it table = h5file.create_table(group, 'readout', Particle, "Readout example") tutprint(h5file) tutrepr(h5file) # Get a shortcut to the record object in table particle = table.row # Fill the table with 10 particles for i in range(10): particle['name'] = 'Particle: %6d' % (i) particle['TDCcount'] = i % 256 particle['ADCcount'] = (i * 256) % (1 << 16) particle['grid_i'] = i particle['grid_j'] = 10 - i particle['pressure'] = float(i*i) particle['energy'] = float(particle['pressure'] ** 4) particle['idnumber'] = i * (2 ** 34) # Insert a new particle record particle.append() # Flush the buffers for table table.flush() SECTION = "Reading (and selecting) data in a table" # Read actual data from table. We are interested in collecting pressure values # on entries where TDCcount field is greater than 3 and pressure less than 50 table = h5file.root.detector.readout pressure = [ x['pressure'] for x in table if x['TDCcount'] > 3 and 20 <= x['pressure'] < 50 ] tutrepr(pressure) # Read also the names with the same cuts names = [ x['name'] for x in table if x['TDCcount'] > 3 and 20 <= x['pressure'] < 50 ] tutrepr(names) SECTION = "Creating new array objects" gcolumns = h5file.create_group(h5file.root, "columns", "Pressure and Name") tutrepr( h5file.create_array(gcolumns, 'pressure', array(pressure), "Pressure column selection") ) tutrepr( h5file.create_array('/columns', 'name', names, "Name column selection") ) tutprint(h5file) SECTION = "Closing the file and looking at its content" # Close the file h5file.close() tutsep() os.system('h5ls -rd tutorial1.h5') tutsep() os.system('ptdump tutorial1.h5') """This example shows how to browse the object tree and enlarge tables. Before to run this program you need to execute first tutorial1-1.py that create the tutorial1.h5 file needed here. """ SECTION = "Traversing the object tree" # Reopen the file in append mode h5file = open_file("tutorial1.h5", "a") # Print the object tree created from this filename # List all the nodes (Group and Leaf objects) on tree tutprint(h5file) # List all the nodes (using File iterator) on tree tutsep() for node in h5file: print(node) # Now, only list all the groups on tree tutsep() for group in h5file.walk_groups("/"): print(group) # List only the arrays hanging from / tutsep() for group in h5file.walk_groups("/"): for array in h5file.list_nodes(group, classname='Array'): print(array) # This gives the same result tutsep() for array in h5file.walk_nodes("/", "Array"): print(array) # And finally, list only leafs on /detector group (there should be one!) # Other way using iterators and natural naming tutsep() for leaf in h5file.root.detector('Leaf'): print(leaf) SECTION = "Setting and getting user attributes" # Get a pointer to '/detector/readout' table = h5file.root.detector.readout # Attach it a string (date) attribute table.attrs.gath_date = "Wed, 06/12/2003 18:33" # Attach a floating point attribute table.attrs.temperature = 18.4 table.attrs.temp_scale = "Celsius" # Get a pointer to '/detector' detector = h5file.root.detector # Attach a general object to the parent (/detector) group detector._v_attrs.stuff = [5, (2.3, 4.5), "Integer and tuple"] # Now, get the attributes tutrepr(table.attrs.gath_date) tutrepr(table.attrs.temperature) tutrepr(table.attrs.temp_scale) tutrepr(detector._v_attrs.stuff) # Delete permanently the attribute gath_date of /detector/readout del table.attrs.gath_date # Print a representation of all attributes in /detector/table tutrepr(table.attrs) # Get the (user) attributes of /detector/table tutprint(table.attrs._f_list("user")) # Get the (sys) attributes of /detector/table tutprint(table.attrs._f_list("sys")) # Rename an attribute table.attrs._f_rename("temp_scale", "tempScale") tutprint(table.attrs._f_list()) # Try to rename a system attribute: try: table.attrs._f_rename("VERSION", "version") except: tutexc() h5file.flush() tutsep() os.system('h5ls -vr tutorial1.h5/detector/readout') SECTION = "Getting object metadata" # Get metadata from table tutsep() print("Object:", table) tutsep() print("Table name:", table.name) tutsep() print("Table title:", table.title) tutsep() print("Number of rows in table:", table.nrows) tutsep() print("Table variable names with their type and shape:") tutsep() for name in table.colnames: print(name, ':= %s, %s' % (table.coltypes[name], table.colshapes[name])) tutprint(table.__doc__) # Get the object in "/columns pressure" pressureObject = h5file.get_node("/columns", "pressure") # Get some metadata on this object tutsep() print("Info on the object:", repr(pressureObject)) tutsep() print(" shape: ==>", pressureObject.shape) tutsep() print(" title: ==>", pressureObject.title) tutsep() print(" type: ==>", pressureObject.type) SECTION = "Reading data from Array objects" # Read the 'pressure' actual data pressureArray = pressureObject.read() tutrepr(pressureArray) tutsep() print("pressureArray is an object of type:", type(pressureArray)) # Read the 'name' Array actual data nameArray = h5file.root.columns.name.read() tutrepr(nameArray) print("nameArray is an object of type:", type(nameArray)) # Print the data for both arrays tutprint("Data on arrays nameArray and pressureArray:") tutsep() for i in range(pressureObject.shape[0]): print(nameArray[i], "-->", pressureArray[i]) tutrepr(pressureObject.name) SECTION = "Appending data to an existing table" # Create a shortcut to table object table = h5file.root.detector.readout # Get the object row from table particle = table.row # Append 5 new particles to table for i in range(10, 15): particle['name'] = 'Particle: %6d' % (i) particle['TDCcount'] = i % 256 particle['ADCcount'] = (i * 256) % (1 << 16) particle['grid_i'] = i particle['grid_j'] = 10 - i particle['pressure'] = float(i*i) particle['energy'] = float(particle['pressure'] ** 4) particle['idnumber'] = i * (2 ** 34) # This exceeds long integer range particle.append() # Flush this table table.flush() # Print the data using the table iterator: tutsep() for r in table: print("%-16s | %11.1f | %11.4g | %6d | %6d | %8d |" % \ (r['name'], r['pressure'], r['energy'], r['grid_i'], r['grid_j'], r['TDCcount'])) # Delete some rows on the Table (yes, rows can be removed!) tutrepr(table.remove_rows(5, 10)) # Close the file h5file.close() PyTables-v.3.1.1/doc/source/000077500000000000000000000000001231437614300155545ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/FAQ.rst000066400000000000000000000600131231437614300167150ustar00rootroot00000000000000:source: http://www.pytables.org/moin/FAQ :revision: 95 :date: 2011-06-13 08:40:20 :author: FrancescAlted .. py:currentmodule:: tables === FAQ === General questions ================= What is PyTables? ----------------- PyTables is a package for managing hierarchical datasets designed to efficiently cope with extremely large amounts of data. It is built on top of the HDF5_ library, the `Python language`_ and the NumPy_ package. It features an object-oriented interface that, combined with C extensions for the performance-critical parts of the code, makes it a fast yet extremely easy-to-use tool for interactively storing and retrieving very large amounts of data. What are PyTables' licensing terms? ----------------------------------- PyTables is free for both commercial and non-commercial use, under the terms of the BSD license. .. todo: link to the BSD license http://opensource.org/licenses/BSD-3-Clause or to a local copy I'm having problems. How can I get support? ------------------------------------------- The most common and efficient way is to subscribe (remember you *need* to subscribe prior to send messages) to the PyTables `users mailing list`_, and send there a brief description of your issue and, if possible, a short script that can reproduce it. Hopefully, someone on the list will be able to help you. It is also a good idea to check out the `archives of the user's list`_ (you may want to check the `Gmane archives`_ instead) so as to see if the answer to your question has already been dealed with. Why HDF5? --------- HDF5_ is the underlying C library and file format that enables PyTables to efficiently deal with the data. It has been chosen for the following reasons: * Designed to efficiently manage very large datasets. * Lets you organize datasets hierarchically. * Very flexible and well tested in scientific environments. * Good maintenance and improvement rate. * Technical excellence (`R&D 100 Award`_). * **It's Open Source software** Why Python? ----------- 1. Python is interactive. People familiar with data processing understand how powerful command line interfaces are for exploring mathematical relationships and scientific data sets. Python provides an interactive environment with the added benefit of a full featured programming language behind it. 2. Python is productive for beginners and experts alike. PyTables is targeted at engineers, scientists, system analysts, financial analysts, and others who consider programming a necessary evil. Any time spent learning a language or tracking down bugs is time spent not solving their real problem. Python has a short learning curve and most people can do real and useful work with it in a day of learning. Its clean syntax and interactive nature facilitate this. 3. Python is data-handling friendly. Python comes with nice idioms that make the access to data much easier: general slicing (i.e. ``data[start:stop:step]``), list comprehensions, iterators, generators ... are constructs that make the interaction with your data very easy. Why NumPy? ---------- NumPy_ is a Python package to efficiently deal with large datasets **in-memory**, providing containers for homogeneous data, heterogeneous data, and string arrays. PyTables uses these NumPy containers as *in-memory buffers* to push the I/O bandwith towards the platform limits. Where can PyTables be applied? ============================== In all the scenarios where one needs to deal with large datasets: * Industrial applications - Data acquisition in real time - Quality control - Fast data processing * Scientific applications - Meteorology, oceanography - Numerical simulations - Medicine (biological sensors, general data gathering & processing) * Information systems - System log monitoring & consolidation - Tracing of routing data - Alert systems in security Is PyTables safe? ----------------- Well, first of all, let me state that PyTables does not support transactional features yet (we don't even know if we will ever be motivated to implement this!), so there is always the risk that you can lose your data in case of an unexpected event while writing (like a power outage, system shutdowns ...). Having said that, if your typical scenarios are *write once, read many*, then the use of PyTables is perfectly safe, even for dealing extremely large amounts of data. Can PyTables be used in concurrent access scenarios? ---------------------------------------------------- It depends. Concurrent reads are no problem at all. However, whenever a process (or thread) is trying to write, then problems will start to appear. First, PyTables doesn't support locking at any level, so several process writing concurrently to the same PyTables file will probably end up corrupting it, so don't do this! Even having only one process writing and the others reading is a hairy thing, because the reading processes might be reading incomplete data from a concurrent data writing operation. The solution would be to lock the file while writing and unlock it after a flush over the file has been performed. Also, in order to avoid cache (HDF5_, PyTables) problems with read apps, you would need to re-open your files whenever you are going to issue a read operation. If a re-opening operation is unacceptable in terms of speed, you may want to do all your I/O operations in one single process (or thread) and communicate the results via sockets, :class:`Queue.Queue` objects (in case of using threads), or whatever, with the client process/thread. The examples directory contains two scripts demonstrating methods of accessing a PyTables file from multiple processes. The first, *multiprocess_access_queues.py*, uses a :class:`multiprocessing.Queue` object to transfer read and write requests from multiple *DataProcessor* processes to a single process responsible for all access to the PyTables file. The results of read requests are then transferred back to the originating processes using other :class:`Queue` objects. The second example script, *multiprocess_access_benchmarks.py*, demonstrates and benchmarks four methods of transferring PyTables array data between processes. The four methods are: * Using :class:`multiprocessing.Pipe` from the Python standard library. * Using a memory mapped file that is shared between two processes. The NumPy array associated with the file is passed as the *out* argument to the :meth:`tables.Array.read` method. * Using a Unix domain socket. Note that this example uses the 'abstract namespace' and will only work under Linux. * Using an IPv4 socket. What kind of containers does PyTables implement? ------------------------------------------------ PyTables does support a series of data containers that address specific needs of the user. Below is a brief description of them: ::class:`Table`: Lets you deal with heterogeneous datasets. Allows compression. Enlargeable. Supports nested types. Good performance for read/writing data. ::class:`Array`: Provides quick and dirty array handling. Not compression allowed. Not enlargeable. Can be used only with relatively small datasets (i.e. those that fit in memory). It provides the fastest I/O speed. ::class:`CArray`: Provides compressed array support. Not enlargeable. Good speed when reading/writing. ::class:`EArray`: Most general array support. Compressible and enlargeable. It is pretty fast at extending, and very good at reading. ::class:`VLArray`: Supports collections of homogeneous data with a variable number of entries. Compressible and enlargeable. I/O is not very fast. ::class:`Group`: The structural component. A hierarchically-addressable container for HDF5 nodes (each of these containers, including Group, are nodes), similar to a directory in a UNIX filesystem. Please refer to the :doc:`usersguide/libref` for more specific information. Cool! I'd like to see some examples of use. ------------------------------------------- Sure. Go to the HowToUse section to find simple examples that will help you getting started. Can you show me some screenshots? --------------------------------- Well, PyTables is not a graphical library by itself. However, you may want to check out ViTables_, a GUI tool to browse and edit PyTables & HDF5_ files. Is PyTables a replacement for a relational database? ---------------------------------------------------- No, by no means. PyTables lacks many features that are standard in most relational databases. In particular, it does not have support for relationships (beyond the hierarchical one, of course) between datasets and it does not have transactional features. PyTables is more focused on speed and dealing with really large datasets, than implementing the above features. In that sense, PyTables can be best viewed as a *teammate* of a relational database. For example, if you have very large tables in your existing relational database, they will take lots of space on disk, potentially reducing the performance of the relational engine. In such a case, you can move those huge tables out of your existing relational database to PyTables, and let your relational engine do what it does best (i.e. manage relatively small or medium datasets with potentially complex relationships), and use PyTables for what it has been designed for (i.e. manage large amounts of data which are loosely related). How can PyTables be fast if it is written in an interpreted language like Python? --------------------------------------------------------------------------------- Actually, all of the critical I/O code in PyTables is a thin layer of code on top of HDF5_, which is a very efficient C library. Cython_ is used as the *glue* language to generate "wrappers" around HDF5 calls so that they can be used in Python. Also, the use of an efficient numerical package such as NumPy_ makes the most costly operations effectively run at C speed. Finally, time-critical loops are usually implemented in Cython_ (which, if used properly, allows to generate code that runs at almost pure C speeds). If it is designed to deal with very large datasets, then PyTables should consume a lot of memory, shouldn't it? --------------------------------------------------------------------------------------------------------------- Well, you already know that PyTables sits on top of HDF5, Python and NumPy_, and if we add its own logic (~7500 lines of code in Python, ~3000 in Cython and ~4000 in C), then we should conclude that PyTables isn't effectively a paradigm of lightness. Having said that, PyTables (as HDF5_ itself) tries very hard to optimize the memory consumption by implementing a series of features like dynamic determination of buffer sizes, *Least Recently Used* cache for keeping unused nodes out of memory, and extensive use of compact NumPy_ data containers. Moreover, PyTables is in a relatively mature state and most memory leaks have been already addressed and fixed. Just to give you an idea of what you can expect, a PyTables program can deal with a table with around 30 columns and 1 million entries using as low as 13 MB of memory (on a 32-bit platform). All in all, it is not that much, is it?. Why was PyTables born? ---------------------- Because, back in August 2002, one of its authors (`Francesc Alted`_) had a need to save lots of hierarchical data in an efficient way for later post-processing it. After trying out several approaches, he found that they presented distinct inconveniences. For example, working with file sizes larger than, say, 100 MB, was rather painful with ZODB (it took lots of memory with the version available by that time). The netCDF3_ interface provided by `Scientific Python`_ was great, but it did not allow to structure the hierarchically; besides, netCDF3_ only supports homogeneous datasets, not heterogeneous ones (i.e. tables). (As an aside, netCDF4_ overcomes many of the limitations of netCDF3_, although curiously enough, it is based on top of HDF5_, the library chosen as the base for PyTables from the very beginning.) So, he decided to give HDF5_ a try, start doing his own wrappings to it and voilà, this is how the first public release of PyTables (0.1) saw the light in October 2002, three months after his itch started to eat him ;-). Does PyTables have a client-server interface? --------------------------------------------- Not by itself, but you may be interested in using PyTables through pydap_, a Python implementation of the OPeNDAP_ protocol. Have a look at the `PyTables plugin` of pydap_. How does PyTables compare with the h5py project? ------------------------------------------------ Well, they are similar in that both packages are Python interfaces to the HDF5_ library, but there are some important differences to be noted. h5py_ is an attempt to map the HDF5_ feature set to NumPy_ as closely as possible. In addition, it also provides access to nearly all of the HDF5_ C API. Instead, PyTables builds up an additional abstraction layer on top of HDF5_ and NumPy_ where it implements things like an enhanced type system, an :ref:`engine for enabling complex queries `, an `efficient computational kernel`_, `advanced indexing capabilities`_ or an undo/redo feature, to name just a few. This additional layer also allows PyTables to be relatively independent of its underlying libraries (and their possible limitations). For example, PyTables can support HDF5_ data types like `enumerated` or `time` that are available in the HDF5_ library but not in the NumPy_ package; or even perform powerful complex queries that are not implemented directly in neither HDF5_ nor NumPy_. Furthermore, PyTables also tries hard to be a high performance interface to HDF5/NumPy, implementing niceties like internal LRU caches for nodes and other data and metadata, :ref:`automatic computation of optimal chunk sizes ` for the datasets, a variety of compressors, ranging from slow but efficient (bzip2_) to extremely fast ones (Blosc_) in addition to the standard `zlib`_. Another difference is that PyTables makes use of numexpr_ so as to accelerate internal computations (for example, in evaluating complex queries) to a maximum. For contrasting with other opinions, you may want to check the PyTables/h5py comparison in a similar entry of the `FAQ of h5py`_. I've found a bug. What do I do? -------------------------------- The PyTables development team works hard to make this eventuality as rare as possible, but, as in any software made by human beings, bugs do occur. If you find any bug, please tell us by file a bug report in the `issue tracker`_ on GitHub_. Is it possible to get involved in PyTables development? ------------------------------------------------------- Indeed. We are keen for more people to help out contributing code, unit tests, documentation, and helping out maintaining this wiki. Drop us a mail on the `users mailing list` and tell us in which area do you want to work. How can I cite PyTables? ------------------------ The recommended way to cite PyTables in a paper or a presentation is as following: * Author: Francesc Alted, Ivan Vilata and others * Title: PyTables: Hierarchical Datasets in Python * Year: 2002 - * URL: http://www.pytables.org Here's an example of a BibTeX entry:: @Misc{, author = {Francesc Alted and Ivan Vilata and others}, title = {{PyTables}: Hierarchical Datasets in {Python}}, year = {2002--}, url = "http://www.pytables.org/" } PyTables 2.x issues =================== I'm having problems migrating my apps from PyTables 1.x into PyTables 2.x. Please, help! ---------------------------------------------------------------------------------------- Sure. However, you should first check out the :doc:`MIGRATING_TO_2.x` document. It should provide hints to the most frequently asked questions on this regard. For combined searches like `table.where('(x<5) & (x>3)')`, why was a `&` operator chosen instead of an `and`? ------------------------------------------------------------------------------------------------------------- Search expressions are in fact Python expressions written as strings, and they are evaluated as such. This has the advantage of not having to learn a new syntax, but it also implies some limitations with logical `and` and `or` operators, namely that they can not be overloaded in Python. Thus, it is impossible right now to get an element-wise operation out of an expression like `'array1 and array2'`. That's why one has to choose some other operator, being `&` and `|` the most similar to their C counterparts `&&` and `||`, which aren't available in Python either. You should be careful about expressions like `'x<5 & x>3'` and others like `'3 < x < 5'` which ''won't work as expected'', because of the different operator precedence and the absence of an overloaded logical `and` operator. More on this in the appendix about condition syntax in the `HDF5 manual`_. There are quite a few packages affected by those limitations including NumPy_ themselves and SQLObject_, and there have been quite longish discussions about adding the possibility of overloading logical operators to Python (see `PEP 335`_ and `this thread`__ for more details). __ https://mail.python.org/pipermail/python-dev/2004-September/048763.html I can not select rows using in-kernel queries with a condition that involves an UInt64Col. Why? ----------------------------------------------------------------------------------------------- This turns out to be a limitation of the numexpr_ package. Internally, numexpr_ uses a limited set of types for doing calculations, and unsigned integers are always upcasted to the immediate signed integer that can fit the information. The problem here is that there is not a (standard) signed integer that can be used to keep the information of a 64-bit unsigned integer. So, your best bet right now is to avoid `uint64` types if you can. If you absolutely need `uint64`, the only way for doing selections with this is through regular Python selections. For example, if your table has a `colM` column which is declared as an `UInt64Col`, then you can still filter its values with:: [row['colN'] for row in table if row['colM'] < X] However, this approach will generally lead to slow speed (specially on Win32 platforms, where the values will be converted to Python `long` values). I'm already using PyTables 2.x but I'm still getting numarray objects instead of NumPy ones! -------------------------------------------------------------------------------------------- This is most probably due to the fact that you are using a file created with PyTables 1.x series. By default, PyTables 1.x was setting an HDF5 attribute `FLAVOR` with the value `'numarray'` to all leaves. Now, PyTables 2.x sees this attribute and obediently converts the internal object (truly a NumPy object) into a `numarray` one. For PyTables 2.x files the `FLAVOR` attribute will only be saved when explicitly set via the `leaf.flavor` property (or when passing data to an :class:`Array` or :class:`Table` at creation time), so you will be able to distinguish default flavors from user-set ones by checking the existence of the `FLAVOR` attribute. Meanwhile, if you don't want to receive `numarray` objects when reading old files, you have several possibilities: * Remove the flavor for your datasets by hand:: for leaf in h5file.walkNodes(classname='Leaf'): del leaf.flavor * Use the :program:'ptrepack` utility with the flag :option:`--upgrade-flavors` so as to convert all flavors in old files to the default (effectively by removing the `FLAVOR` attribute). * Remove the `numarray` (and/or `Numeric`) package from your system. Then PyTables 2.x will return you pure NumPy objects (it can't be otherwise!). Installation issues =================== Windows ------- Error when importing tables ~~~~~~~~~~~~~~~~~~~~~~~~~~~ You have installed the binary installer for Windows and, when importing the *tables* package you are getting an error like:: The command in "0x6714a822" refers to memory in "0x012011a0". The procedure "written" could not be executed. Click to ok to terminate. Click to abort to debug the program. This problem can be due to a series of reasons, but the most probable one is that you have a version of a DLL library that is needed by PyTables and it is not at the correct version. Please, double-check the versions of the required libraries for PyTables and install newer versions, if needed. In most cases, this solves the issue. In case you continue getting problems, there are situations where other programs do install libraries in the PATH that are **optional** to PyTables (for example BZIP2 or LZO), but that they will be used if they are found in your system (i.e. anywhere in your :envvar:`PATH`). So, if you find any of these libraries in your PATH, upgrade it to the latest version available (you don't need to re-install PyTables). Can't find LZO binaries for Windows ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unfortunately, the LZO binaries for Windows seems to be unavailable from its usual place at http://gnuwin32.sourceforge.net/packages/lzo.htm. So, in order to allow people to be able to install this excellent compressor easily, we have packaged the LZO binaries in a zip file available at: http://www.pytables.org/download/lzo-win. This zip file follows the same structure that a typical GnuWin32_ package, so it is just a matter of unpacking it in your ``GNUWIN32`` directory and following the :ref:`instructions ` in the `PyTables Manual`_. Hopefully somebody else will take care again of maintaining LZO for Windows again. Testing issues ============== Tests fail when running from IPython ------------------------------------ You may be getting errors related with Doctest when running the test suite from IPython. This is a known limitation in IPython (see http://lists.ipython.scipy.org/pipermail/ipython-dev/2007-April/002859.html). Try running the test suite from the vanilla Python interpreter instead. Tests fail when running from Python 2.5 and Numeric is installed ---------------------------------------------------------------- `Numeric` doesn't get well with Python 2.5, even on 32-bit platforms. This is a consequence of `Numeric` not being maintained anymore and you should consider migrating to NumPy as soon as possible. To get rid of these errors, just uninstall `Numeric`. ----- .. target-notes:: .. _HDF5: http://www.hdfgroup.org/HDF5 .. _`Python language`: http://www.python.org .. _NumPy: http://www.numpy.org .. _`users mailing list`: https://lists.sourceforge.net/lists/listinfo/pytables-users .. _`archives of the user's list`: http://sourceforge.net/mailarchive/forum.php?forum_id=13760 .. _`Gmane archives`: http://www.mail-archive.com/pytables-users@lists.sourceforge.net/ .. _`R&D 100 Award`: http://www.hdfgroup.org/HDF5/RD100-2002/ .. _ViTables: http://vitables.org .. _Cython: http://www.cython.org .. _`Francesc Alted`: http://www.pytables.org/moin/FrancescAlted .. _netCDF3: http://www.unidata.ucar.edu/software/netcdf .. _`Scientific Python`: http://dirac.cnrs-orleans.fr/plone/software/scientificpython .. _netCDF4: http://www.unidata.ucar.edu/software/netcdf .. _pydap: http://www.pydap.org .. _OPeNDAP: http://opendap.org .. _`PyTables plugin`: http://pydap.org/plugins/hdf5.html .. _`PyTables Manual`: http://www.pytables.org/docs/manual .. _h5py: http://www.h5py.org .. _`efficient computational kernel`: http://www.pytables.org/moin/ComputingKernel .. _`advanced indexing capabilities`: http://www.pytables.org/moin/PyTablesPro .. _`automatic computation of optimal chunk sizes`: http://www.pytables.org/docs/manual/ch05.html#chunksizeFineTune .. _bzip2: http://www.bzip.org .. _Blosc: http://blosc.pytables.org .. _`zlib`: http://zlib.net .. _numexpr: http://code.google.com/p/numexpr .. _`FAQ of h5py`: http://code.google.com/p/h5py/wiki/FAQ .. _`issue tracker`: https://github.com/PyTables/PyTables/issues .. _GitHub: https://github.com .. _`HDF5 manual`: http://www.hdfgroup.org/HDF5/doc/RM/RM_H5T.html .. _SQLObject: http://sqlobject.org .. _`PEP 335`: http://www.python.org/dev/peps/pep-0335 .. _GnuWin32: http://gnuwin32.sourceforge.net .. todo:: fix links that point to wiki pages PyTables-v.3.1.1/doc/source/MIGRATING_TO_2.x.rst000066400000000000000000000243051231437614300207240ustar00rootroot00000000000000================================== Migrating from PyTables 1.x to 2.x ================================== :Author: Francesc Alted i Abad :Contact: faltet@pytables.com :Author: Ivan Vilata i Balaguer :Contact: ivan@selidor.net Next are described a series of issues that you must have in mind when migrating from PyTables 1.x to PyTables 2.x series. New type system =============== In PyTables 2.x all the data types for leaves are described through a couple of classes: - ``Atom``: Describes homogeneous types of the atomic components in ``*Array`` objects (``Array``, ``CArray``, ``EArray`` and ``VLArray``). - ``Description``: Describes (possibly nested) heterogeneous types in ``Table`` objects. So, in order to upgrade to the new type system, you must perform the next replacements: - ``*Array.stype`` --> ``*Array.atom.type`` (PyTables type) - ``*Array.type`` --> ``*Array.atom.dtype`` (NumPy type) - ``*Array.itemsize`` --> ``*Array.atom.itemsize`` (the size of the item) Furthermore, the PyTables types (previously called "string types") have changed to better adapt to NumPy conventions. The next changes have been applied: - PyTables types are now written in lower case, so 'Type' becomes 'type'. For example, 'Int64' becomes now 'int64'. - 'CharType' --> 'string' - 'Complex32', 'Complex64' --> 'complex64', 'complex128'. Note that the numeric part of a 'complex' type refers now to the *size in bits* of the type and not to the precision, as before. See Appendix I of the Users' Manual on supported data types for more information on the new PyTables types. Important changes in ``Atom`` specification =========================================== - The ``dtype`` argument of ``EnumAtom`` and ``EnumCol`` constructors has been replaced by the ``base`` argument, which can take a full-blown atom, although it accepts bare PyTables types as well. This is a *mandatory* argument now. - ``vlstring`` pseudo-atoms used in ``VLArray`` nodes do no longer imply UTF-8 (nor any other) encoding, they only store and load *raw strings of bytes*. All encoding and decoding is left to the user. Be warned that reading old files may yield raw UTF-8 encoded strings, which may be coverted back to Unicode in this way:: unistr = vlarray[index].decode('utf-8') If you need to work with variable-length Unicode strings, you may want to use the new ``vlunicode`` pseudo-atom, which fully supports Unicode strings with no encoding hassles. - Finally, ``Atom`` and ``Col`` are now abstract classes, so you can't use them to create atoms or column definitions of an arbitrary type. If you know the particular type you need, use the proper subclass; otherwise, use the ``Atom.from_*()`` or ``Col.from_*()`` factory methods. See the section on declarative classes in the reference. You are also advised to avoid using the inheritance of atoms to check for their kind or type; for that purpose, use their ``kind`` and ``type`` attributes. New query system ================ - In-kernel conditions, since they are based now in Numexpr, must be written *as strings*. For example, a condition that in 1.x was stated as:: result = [row['col2'] for row in table.where(table.cols.col1 == 1)] now should read:: result = [row['col2'] for row in table.where('col1 == 1')] That means that complex selections are possible now:: result = [ row['col2'] for row in table.where('(col1 == 1) & (col3**4 > 1)') ] - For the same reason, conditions for indexed columns must be written as strings as well. New indexing system =================== The indexing system has been totally rewritten from scratch for PyTables 2.0 Pro Edition (http://www.pytables.com/moin/PyTablesPro). The new indexing systemsame has been included into PyTables with release 2.3. Due to this, your existing indexes created with PyTables 1.x will be useless, and although you will be able to continue using the actual data in files, you won't be able to take advantage of any improvement in speed. You will be offered the possibility to automatically re-create the indexes in PyTables 1.x format to the new 2.0 format by using the ``ptrepack`` utility. New meanings for atom shape and ``*Array`` shape argument ========================================================= With PyTables 1.x, the atom shape was used for different goals depending on the context it was used. For example, in ``createEArray()``, the shape of the atom was used to specify the *dataset shape* of the object on disk, while in ``CArray`` the same atom shape was used to specify the *chunk shape* of the dataset on disk. Moreover, for ``VLArray`` objects, the very same atom shape specified the *type shape* of the data type. As you see, all of these was quite a mess. Starting with PyTables 2.x, an ``Atom`` only specifies properties of the data type (à la ``VLArray`` in 1.x). This lets the door open for specifying multidimensional data types (that can be part of another layer of multidimensional datasets) in a consistent way along all the ``*Array`` objects in PyTables. As a consequence of this, ``File.createCArray()`` and ``File.createVLArray()`` methods have received new parameters in order to make possible to specify the shapes of the datasets as well as chunk sizes (in fact, it is possible now to specify the latter for all the chunked leaves, see below). Please have this in mind during the migration process. Another consequence is that, now that the meaning of the atom shape is clearly defined, it has been chosen as the main object to describe homogeneous data types in PyTables. See the Users' Manual for more info on this. New argument ``chunkshape`` of chunked leaves ============================================= It is possible now to specify the chunk shape for all the chunked leaves in PyTables (all except ``Array``). With PyTables 1.x this value was automatically calculated so as to achieve decent results in most of the situations. However, the user may be interested in specifying its own chunk shape based on her own needs (although this should be done only by advanced users). Of course, if this parameter is not specified, a sensible default is calculated for the size of the leave (which is recommended). A new attribute called ``chunkshape`` has been added to all leaves. It is read-only (you can't change the size of chunks once you have created a leaf), but it can be useful for inspection by advanced users. New flavor specification ======================== As of 2.x, flavors can *only* be set through the ``flavor`` attribute of leaves, and they are *persistent*, so changing a flavor requires that the file be writable. Flavors can no longer be set through ``File.create*()`` methods, nor the ``flavor`` argument previously found in some ``Table`` methods, nor through ``Atom`` constructors or the ``_v_flavor`` attribute of descriptions. System attributes can be deleted now ==================================== The protection against removing system attributes (like ``FILTERS``, ``FLAVOR`` or ``CLASS``, to name only a few) has been completely removed. It is now the responsibility of the user to make a proper use of this freedom. With this, users can get rid of all proprietary PyTables attributes if they want to (for example, for making a file to look more like an HDF5 native one). Byteorder issues ================ Now, all the data coming from reads and internal buffers is always converted on-the-fly, if needed, to the *native* byteorder. This represents a big advantage in terms of speed when operating with objects coming from files that have been created in machines with a byte ordering different from native. Besides, all leaf constructors have received a new ``byteorder`` parameter that allows specifying the byteorder of data on disk. In particular, a ``_v_byteorder`` entry in a Table description is no longer honored and you should use the aforementioned ``byteorder`` parameter. Tunable internal buffer sizes ============================= You can change the size of the internal buffers for I/O purposes of PyTables by changing the value of the new public attribute ``nrowsinbuf`` that is present in all leaves. By default, this contains a sensible value so as to achieve a good balance between speed and memory consumption. Be careful when changing it, if you don't want to get unwanted results (very slow I/O, huge memory consumption...). Changes to module names ======================= If your application is directly accessing modules under the ``tables`` package, you need to know that *the names of all modules are now all in lowercase*. This allows one to tell apart the ``tables.Array`` *class* from the ``tables.array`` *module* (which was also called ``tables.Array`` before). This includes subpackages like ``tables.nodes.FileNode``. On top of that, more-or-less independent modules have also been renamed and some of them grouped into subpackages. The most important are: - The ``tables.netcdf3`` subpackage replaces the old ``tables.NetCDF`` module. - The ``tables.nra`` subpackage replaces the old ``nestedrecords.py`` with the implementation of the ``NestedRecArray`` class. Also, the ``tables.misc`` package includes utility modules which do not depend on PyTables. Other changes ============= - ``Filters.complib`` is ``None`` for filter properties created with ``complevel=0`` (i.e. disabled compression, which is the default). - 'non-relevant' --> 'irrelevant' (applied to byteorders) - ``Table.colstypes`` --> ``Table.coltypes`` - ``Table.coltypes`` --> ``Table.coldtypes`` - Added ``Table.coldescr``, dictionary of the ``Col`` descriptions. - ``Table.colshapes`` has disappeared. You can get it this way:: colshapes = dict( (name, col.shape) for (name, col) in table.coldescr.iteritems() ) - ``Table.colitemsizes`` has disappeared. You can get it this way:: colitemsizes = dict( (name, col.itemsize) for (name, col) in table.coldescr.iteritems() ) - ``Description._v_totalsize`` --> ``Description._v_itemsize`` - ``Description._v_itemsizes`` and ``Description._v_totalsizes`` have disappeared. - ``Leaf._v_chunksize`` --> ``Leaf.chunkshape`` ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 78 .. End: PyTables-v.3.1.1/doc/source/MIGRATING_TO_3.x.rst000066400000000000000000000613451231437614300207320ustar00rootroot00000000000000================================== Migrating from PyTables 2.x to 3.x ================================== :Author: Antonio Valentino :Author: Anthony Scopatz This document describes the major changes in PyTables in going from the 2.x to 3.x series and what you need to know when migrating downstream code bases. Python 3 at Last! ================= The PyTables 3.x series now ships with full compatibility for Python 3.1+. Additionally, we plan on maintaining compatibility with Python 2.7 for the foreseeable future. Python 2.6 is no longer under actively supported but may work in most cases. Note that the entire 3.x series now relies on numexpr v2.1+, which itself is the first version of numexpr support both Python 2 & 3. Numeric, Numarray, NetCDF3, & HDF5 1.6 No More! =============================================== PyTables no longer supports numeric and numarray. Please use numpy instead. Additionally, the ``tables.netcdf3`` module has been removed. Please refer to the `netcdf4-python`_ project for further support. Lastly, the older HDF5 1.6 API is no longer supported. Please upgrade to HDF5 1.8+. Major API Changes ================= The PyTables developers, `by popular demand`_, have taken this opportunity that a major version number upgrade affords to implement significant API changes. We have tried to do this in such a way that will not immediately break most existing code, though in some breakages may still occur. PEP 8 Compliance **************** The PyTables 3.x series now follows `PEP 8`_ coding standard. This makes using PyTables more idiomatic with surrounding Python code that also adheres to this standard. The primary way that the 2.x series was *not* PEP 8 compliant was with respect to variable naming conventions. Approximately :ref:`450 API variables ` were identified and updated for PyTables 3.x. To ease migration, PyTables ships with a new ``pt2to3`` command line tool. This tool will run over a file and replace any instances of the old variable names with the 3.x version of the name. This tool covers the overwhelming majority of cases was used to transition the PyTables code base itself! However, it may also accidentally also pick up variable names in 3rd party codes that have *exactly* the same name as a PyTables' variable. This is because ``pt2to3`` was implemented using regular expressions rather than a fancier AST-based method. By using regexes, ``pt2to3`` works on Python and Cython code. ``pt2to3`` **help:** .. code-block:: bash usage: pt2to3 [-h] [-r] [-p] [-o OUTPUT] [-i] filename PyTables 2.x -> 3.x API transition tool This tool displays to standard out, so it is common to pipe this to another file: $ pt2to3 oldfile.py > newfile.py positional arguments: filename path to input file. optional arguments: -h, --help show this help message and exit -r, --reverse reverts changes, going from 3.x -> 2.x. -p, --no-ignore-previous ignores previous_api() calls. -o OUTPUT output file to write to. -i, --inplace overwrites the file in-place. Note that ``pt2to3`` only works on a single file, not a a directory. However, a simple BASH script may be written to run ``pt2to3`` over an entire directory and all sub-directories: .. code-block:: bash #!/bin/bash for f in $(find .) do echo $f pt2to3 $f > temp.txt mv temp.txt $f done .. note:: :program:`pt2to3` uses the :mod:`argparse` module that is part of the Python standard library since Python 2.7. Users of Python 2.6 should install :mod:`argparse` separately (e.g. via :program:`pip`). The old APIs and variable names will continue to be supported for the short term, where possible. (The major backwards incompatible changes come from the renaming of some function and method arguments and keyword arguments.) Using the 2.x APIs in the 3.x series, however, will issue warnings. The following is the release plan for the warning types: * 3.0 - PendingDeprecationWarning * 3.1 - DeprecationWarning * >=3.2 - Remove warnings, previous_api(), and _past.py; keep pt2to3, The current plan is to maintain the old APIs for at least 2 years, though this is subject to change. Consistent ``create_xxx()`` Signatures *************************************** Also by popular demand, it is now possible to create all data sets (``Array``, ``CArray``, ``EArray``, ``VLArray``, and ``Table``) from existing Python objects. Constructors for these classes now accept either of the following keyword arguments: * an ``obj`` to initialize with data * or both ``atom`` and ``shape`` to initialize an empty structure, if possible. These keyword arguments are also now part of the function signature for the corresponding ``create_xxx()`` methods on the ``File`` class. These would be called as follows:: # All create methods will support the following crete_xxx(where, name, obj=obj) # All non-variable length arrays support the following: crete_xxx(where, name, atom=atom, shape=shape) Using ``obj`` or ``atom`` and ``shape`` are mutually exclusive. Previously only ``Array`` could be created with an existing Python object using the ``object`` keyword argument. .. _api-name-changes: API Name Changes **************** The following tables shows the old 2.x names that have been update to their new values in the new 3.x series. Please use the ``pt2to3`` tool to convert between these. ================================ ================================ **2.x Name** **3.x Name** ================================ ================================ AtomFromHDF5Type atom_from_hdf5_type AtomToHDF5Type atom_to_hdf5_type BoolTypeNextAfter bool_type_next_after HDF5ClassToString hdf5_class_to_string HDF5ToNPExtType hdf5_to_np_ext_type HDF5ToNPNestedType hdf5_to_np_nested_type IObuf iobuf IObufcpy iobufcpy IntTypeNextAfter int_type_next_after NPExtPrefixesToPTKinds npext_prefixes_to_ptkinds PTSpecialKinds pt_special_kinds PTTypeToHDF5 pttype_to_hdf5 StringNextAfter string_next_after __allowedInitKwArgs __allowed_init_kwargs __getRootGroup __get_root_group __next__inKernel __next__inkernel _actionLogName _action_log_name _actionLogParent _action_log_parent _actionLogPath _action_log_path _addRowsToIndex _add_rows_to_index _appendZeros _append_zeros _autoIndex _autoindex _byteShape _byte_shape _c_classId _c_classid _c_shadowNameRE _c_shadow_name_re _cacheDescriptionData _cache_description_data _checkAndSetPair _check_and_set_pair _checkAttributes _check_attributes _checkBase _checkbase _checkColumn _check_column _checkGroup _check_group _checkNotClosed _check_not_closed _checkOpen _check_open _checkShape _check_shape _checkShapeAppend _check_shape_append _checkUndoEnabled _check_undo_enabled _checkWritable _check_writable _check_sortby_CSI _check_sortby_csi _closeFile _close_file _codeToOp _code_to_op _column__createIndex _column__create_index _compileCondition _compile_condition _conditionCache _condition_cache _convertTime64 _convert_time64 _convertTime64_ _convert_time64_ _convertTypes _convert_types _createArray _create_array _createCArray _create_carray _createMark _create_mark _createPath _create_path _createTable _create_table _createTransaction _create_transaction _createTransactionGroup _create_transaction_group _disableIndexingInQueries _disable_indexing_in_queries _doReIndex _do_reindex _emptyArrayCache _empty_array_cache _enableIndexingInQueries _enable_indexing_in_queries _enabledIndexingInQueries _enabled_indexing_in_queries _exprvarsCache _exprvars_cache _f_copyChildren _f_copy_children _f_delAttr _f_delattr _f_getAttr _f_getattr _f_getChild _f_get_child _f_isVisible _f_isvisible _f_iterNodes _f_iter_nodes _f_listNodes _f_list_nodes _f_setAttr _f_setattr _f_walkGroups _f_walk_groups _f_walkNodes _f_walknodes _fancySelection _fancy_selection _fillCol _fill_col _flushBufferedRows _flush_buffered_rows _flushFile _flush_file _flushModRows _flush_mod_rows _g_addChildrenNames _g_add_children_names _g_checkGroup _g_check_group _g_checkHasChild _g_check_has_child _g_checkName _g_check_name _g_checkNotContains _g_check_not_contains _g_checkOpen _g_check_open _g_closeDescendents _g_close_descendents _g_closeGroup _g_close_group _g_copyAsChild _g_copy_as_child _g_copyChildren _g_copy_children _g_copyRows _g_copy_rows _g_copyRows_optim _g_copy_rows_optim _g_copyWithStats _g_copy_with_stats _g_createHardLink _g_create_hard_link _g_delAndLog _g_del_and_log _g_delLocation _g_del_location _g_flushGroup _g_flush_group _g_getAttr _g_getattr _g_getChildGroupClass _g_get_child_group_class _g_getChildLeafClass _g_get_child_leaf_class _g_getGChildAttr _g_get_gchild_attr _g_getLChildAttr _g_get_lchild_attr _g_getLinkClass _g_get_link_class _g_listAttr _g_list_attr _g_listGroup _g_list_group _g_loadChild _g_load_child _g_logAdd _g_log_add _g_logCreate _g_log_create _g_logMove _g_log_move _g_maybeRemove _g_maybe_remove _g_moveNode _g_move_node _g_postInitHook _g_post_init_hook _g_postReviveHook _g_post_revive_hook _g_preKillHook _g_pre_kill_hook _g_propIndexes _g_prop_indexes _g_readCoords _g_read_coords _g_readSelection _g_read_selection _g_readSlice _g_read_slice _g_readSortedSlice _g_read_sorted_slice _g_refNode _g_refnode _g_removeAndLog _g_remove_and_log _g_setAttr _g_setattr _g_setLocation _g_set_location _g_setNestedNamesDescr _g_set_nested_names_descr _g_setPathNames _g_set_path_names _g_unrefNode _g_unrefnode _g_updateDependent _g_update_dependent _g_updateLocation _g_update_location _g_updateNodeLocation _g_update_node_location _g_updateTableLocation _g_update_table_location _g_widthWarning _g_width_warning _g_writeCoords _g_write_coords _g_writeSelection _g_write_selection _g_writeSlice _g_write_slice _getColumnInstance _get_column_instance _getConditionKey _get_condition_key _getContainer _get_container _getEnumMap _get_enum_map _getFileId _get_file_id _getFinalAction _get_final_action _getInfo _get_info _getLinkClass _get_link_class _getMarkID _get_mark_id _getNode _get_node _getOrCreatePath _get_or_create_path _getTypeColNames _get_type_col_names _getUnsavedNrows _get_unsaved_nrows _getValueFromContainer _get_value_from_container _hiddenNameRE _hidden_name_re _hiddenPathRE _hidden_path_re _indexNameOf _index_name_of _indexNameOf_ _index_name_of_ _indexPathnameOf _index_pathname_of _indexPathnameOfColumn _index_pathname_of_column _indexPathnameOfColumn_ _index_pathname_of_column_ _indexPathnameOf_ _index_pathname_of_ _initLoop _init_loop _initSortedSlice _init_sorted_slice _isWritable _iswritable _is_CSI _is_csi _killNode _killnode _lineChunkSize _line_chunksize _lineSeparator _line_separator _markColumnsAsDirty _mark_columns_as_dirty _newBuffer _new_buffer _notReadableError _not_readable_error _npSizeType _npsizetype _nxTypeFromNPType _nxtype_from_nptype _opToCode _op_to_code _openArray _open_array _openUnImplemented _open_unimplemented _pointSelection _point_selection _processRange _process_range _processRangeRead _process_range_read _pythonIdRE _python_id_re _reIndex _reindex _readArray _read_array _readCoordinates _read_coordinates _readCoords _read_coords _readIndexSlice _read_index_slice _readSelection _read_selection _readSlice _read_slice _readSortedSlice _read_sorted_slice _refNode _refnode _requiredExprVars _required_expr_vars _reservedIdRE _reserved_id_re _reviveNode _revivenode _saveBufferedRows _save_buffered_rows _searchBin _search_bin _searchBinNA_b _search_bin_na_b _searchBinNA_d _search_bin_na_d _searchBinNA_e _search_bin_na_e _searchBinNA_f _search_bin_na_f _searchBinNA_g _search_bin_na_g _searchBinNA_i _search_bin_na_i _searchBinNA_ll _search_bin_na_ll _searchBinNA_s _search_bin_na_s _searchBinNA_ub _search_bin_na_ub _searchBinNA_ui _search_bin_na_ui _searchBinNA_ull _search_bin_na_ull _searchBinNA_us _search_bin_na_us _setAttributes _set_attributes _setColumnIndexing _set_column_indexing _shadowName _shadow_name _shadowParent _shadow_parent _shadowPath _shadow_path _sizeToShape _size_to_shape _tableColumnPathnameOfIndex _table_column_pathname_of_index _tableFile _table_file _tablePath _table_path _table__autoIndex _table__autoindex _table__getautoIndex _table__getautoindex _table__setautoIndex _table__setautoindex _table__whereIndexed _table__where_indexed _transGroupName _trans_group_name _transGroupParent _trans_group_parent _transGroupPath _trans_group_path _transName _trans_name _transParent _trans_parent _transPath _trans_path _transVersion _trans_version _unrefNode _unrefnode _updateNodeLocations _update_node_locations _useIndex _use_index _vShape _vshape _vType _vtype _v__nodeFile _v__nodefile _v__nodePath _v__nodepath _v_colObjects _v_colobjects _v_maxGroupWidth _v_max_group_width _v_maxTreeDepth _v_maxtreedepth _v_nestedDescr _v_nested_descr _v_nestedFormats _v_nested_formats _v_nestedNames _v_nested_names _v_objectID _v_objectid _whereCondition _where_condition _writeCoords _write_coords _writeSelection _write_selection _writeSlice _write_slice appendLastRow append_last_row attrFromShadow attr_from_shadow attrToShadow attr_to_shadow autoIndex autoindex bufcoordsData bufcoords_data calcChunksize calc_chunksize checkFileAccess check_file_access checkNameValidity check_name_validity childName childname chunkmapData chunkmap_data classIdDict class_id_dict className classname classNameDict class_name_dict containerRef containerref convertToNPAtom convert_to_np_atom convertToNPAtom2 convert_to_np_atom2 copyChildren copy_children copyClass copyclass copyFile copy_file copyLeaf copy_leaf copyNode copy_node copyNodeAttrs copy_node_attrs countLoggedInstances count_logged_instances createArray create_array createCArray create_carray createCSIndex create_csindex createEArray create_earray createExternalLink create_external_link createGroup create_group createHardLink create_hard_link createIndex create_index createIndexesDescr create_indexes_descr createIndexesTable create_indexes_table createNestedType create_nested_type createSoftLink create_soft_link createTable create_table createVLArray create_vlarray defaultAutoIndex default_auto_index defaultIndexFilters default_index_filters delAttr del_attr delAttrs _del_attrs delNodeAttr del_node_attr detectNumberOfCores detect_number_of_cores disableUndo disable_undo dumpGroup dump_group dumpLeaf dump_leaf dumpLoggedInstances dump_logged_instances enableUndo enable_undo enumFromHDF5 enum_from_hdf5 enumToHDF5 enum_to_hdf5 fetchLoggedInstances fetch_logged_instances flushRowsToIndex flush_rows_to_index getAttr get_attr getAttrs _get_attrs getClassByName get_class_by_name getColsInOrder get_cols_in_order getCurrentMark get_current_mark getEnum get_enum getFilters get_filters getHDF5Version get_hdf5_version getIndices get_indices getLRUbounds get_lru_bounds getLRUsorted get_lru_sorted getLookupRange get_lookup_range getNestedField get_nested_field getNestedFieldCache get_nested_field_cache getNestedType get_nested_type getNode get_node getNodeAttr get_node_attr getPyTablesVersion get_pytables_version getTypeEnum get_type_enum getWhereList get_where_list hdf5Extension hdf5extension hdf5Version hdf5_version indexChunk indexchunk indexValid indexvalid indexValidData index_valid_data indexValues indexvalues indexValuesData index_values_data indexesExtension indexesextension infType inftype infinityF infinityf infinityMap infinitymap initRead initread isHDF5File is_hdf5_file isPyTablesFile is_pytables_file isUndoEnabled is_undo_enabled isVisible isvisible isVisibleName isvisiblename isVisibleNode is_visible_node isVisiblePath isvisiblepath is_CSI is_csi iterNodes iter_nodes iterseqMaxElements iterseq_max_elements joinPath join_path joinPaths join_paths linkExtension linkextension listLoggedInstances list_logged_instances listNodes list_nodes loadEnum load_enum logInstanceCreation log_instance_creation lrucacheExtension lrucacheextension metaIsDescription MetaIsDescription modifyColumn modify_column modifyColumns modify_columns modifyCoordinates modify_coordinates modifyRows modify_rows moveFromShadow move_from_shadow moveNode move_node moveToShadow move_to_shadow newNode new_node newSet newset newdstGroup newdst_group objectID object_id oldPathname oldpathname openFile open_file openNode open_node parentNode parentnode parentPath parentpath reIndex reindex reIndexDirty reindex_dirty readCoordinates read_coordinates readIndices read_indices readSlice read_slice readSorted read_sorted readWhere read_where read_sliceLR read_slice_lr recreateIndexes recreate_indexes redoAddAttr redo_add_attr redoCreate redo_create redoDelAttr redo_del_attr redoMove redo_move redoRemove redo_remove removeIndex remove_index removeNode remove_node removeRows remove_rows renameNode rename_node rootUEP root_uep searchLastRow search_last_row setAttr set_attr setAttrs _set_attrs setBloscMaxThreads set_blosc_max_threads setInputsRange set_inputs_range setNodeAttr set_node_attr setOutput set_output setOutputRange set_output_range silenceHDF5Messages silence_hdf5_messages splitPath split_path tableExtension tableextension undoAddAttr undo_add_attr undoCreate undo_create undoDelAttr undo_del_attr undoMove undo_move undoRemove undo_remove utilsExtension utilsextension walkGroups walk_groups walkNodes walk_nodes whereAppend append_where whereCond wherecond whichClass which_class whichLibVersion which_lib_version willQueryUseIndexing will_query_use_indexing ================================ ================================ ---- **Enjoy data!** -- The PyTables Developers .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 78 .. End: .. _by popular demand: http://sourceforge.net/mailarchive/message.php?msg_id=29584752 .. _PEP 8: http://www.python.org/dev/peps/pep-0008/ .. _netcdf4-python: http://code.google.com/p/netcdf4-python/ PyTables-v.3.1.1/doc/source/_static/000077500000000000000000000000001231437614300172025ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/_static/logo-pytables-small.png000066400000000000000000000405101231437614300235770ustar00rootroot00000000000000‰PNG  IHDRÈSÚùHsBIT|dˆ pHYs¾¾ä ÛtEXtSoftwarewww.inkscape.org›î< IDATxœì}i˜\Guö{ªî½½÷¬­},y´X–¼Ë cÆ6Ë(bH"ã„å ° HÉÇHÀ &!ÆÆ6lcy_åM–­Å­³öÌôô~—ª:ßÛ-KV÷hf$o|~ŸG¤™êºU·ëTå=§ˆ™ñ»"" ø|øzŠyìEÖ+x™A¼Øx>@DÔÓÓ#×®]k—gþ6&ÄŸ]uÕU¢&8¯àL ¿sBDbýúõVWWWä6o>5æûßÁ¼žwírz{{çæü ž?üN-"¢5kÖH¥Tô¬¡¡ù' ]C@ˆùø+~þó739EèPˆÚzå$úÿÖ‹=€c…ºZ•Éd"Bˆ–÷Þ{ï7l­—Ü&]­®ð›ÞÞ^ @©¿ 6POOXô?ws»ûfz-Œ,€Ñwûòvuý;®í[×gøwÕ˜ûÿôRþ^6´/ý­û*exÿÿ^ÝÛ¨íºuëd[[[¤R©´|á׿þ«ÅÙì_7hf~»téiß=ãŒm}}}A£E]ŒÁÁAYþËëçóªì Ík›’é òÊGÕ/ð+‚ò»…—œŠUWe.¸à«÷£_‹¾û¶Ê»Þ}—ºçÛ±þº‘¡MD"‘HØA$.¿ÿþ×76öÑ&݋Ӈ‡ßFm4˜{ýÚ²eK¤ôñkÏåÕ£÷N)@| õóK=ñ/½½½¯8~Çð’Q±ê;÷úõëeõü÷µé…§þ),ç è:ÐHˆ÷=sêÛ¿ÐÛ»eœˆ 33‰ÞÞ^KJ?oëÖågïÚõÏÄ,›=']­¾»klìKkÖ¬ñë}ÔŸ¿fÍ+“ÉD"Ÿ½ÿ,³tü&Ǧ=~Û|оúç{z²=_'"ýÊIò»ý!"Z·nìííµwœñÞŠ—~ý_ôâ³wÂŽ~éá['8Ý}y©T²jkÖ¬‘ŽãDÓž×þŽM›¾ikÝ1Õó¤1s>ðÀoéîî¶PSßê'Gwww¤£££Å?úw3Žˆª˜wí}—¼â)ûÝÁ‹r‚Ôm‹žž±víZiýé7_CÑôGØrÞD@ÓÝ؉\‘<ñœo®ïê*nÚ´ ÝÝÝ‘X,–þëŸþtCÒuO›Îó;ŠÅËü´§§' "îííÑh4bÛvºü¾[ÞŠ„wî,§&D\}0šHÜJDÌÌf–ý¼‚—^p#ˆDOOXñºKÒyïê¥HüC,äšõQ¹ÂÿÁ‡‰D"ùW·Ýöž“÷íûÇtÁ÷ü9ßÝë6ïÞ½[-Y²Ä"¢”mÛóÊÿ£qkå”Mêà±1|üâ´å[¿(‡6mÚ¤^Qµ^ÞxÁTº*µvíZ{Ñ'þ÷÷J¯ÿÓ툥~8SᎷ]Fã‰D"ùŽ'žX³j`àó3Ž8îÔïú±y±%K–XA8D”2ÆÌƒ£Ît<‡ŒààU»Qá^ÁË/˜€lذæÍ›gµµµÅiߦt4}šyK¯¸üöÛ¿ ‚ųù|z2‹3ï¹ñœàø5wëK>ö{Bˆ6ê¬tAš£¶Ë¨ ’ɤ“Éd^qû¾ÌñBípäû¾ô}? ]ÑåV]\0­ ҳ鯚LÓ¿âÛó¢·~ãie"šcZ;Êåxg>ïd Ù)²C$iîþg0g`'¢ÕÒ!}œøØX°g+¯fÞ ½î[>·_ Ö<5soLZ>²¤_)eår9ÐyÅP™â¡¡!tvv‚™Éc !bÖS·ù¯þãY èy+OåXj* DB`,™ôÆ’ÉèÓ'¯¾ñ“I¶"Ú·LŒ`î@(,óöïÄœXñÄÝ\¼`Z,bçsx׬ç)ú3ˇ»Ç„ͯ¨V¿x¡„;:: 3\)e@Õ~æÞñà´7/æxÛ”±¦¶œüæö?Þ `‚™“R°#mnÏå+È·ÏE¾}.¶Ÿüšæ-¾S"÷{@Ûof>&¶n<ã6!„ ‚ÀD£ÑWNŽ—9^™™yóæÍº\.ûDTdæ€ °™´·Ý•?š¾ƒ¥g¯äˆèW+‡‚K÷½?¹+Q¹ì¢n“žgÏ®Gì¸ðŽ›ñ'­{VåZ÷Û~pÖº4Ë™Ûé΃?Ù%&‡¶Èר¸QIfް‰ˆxÌ\0 `?€]D´Ë²¬!cL€¿qãÆc¦^Õßgooï¬]Ç«W¯æ 6Ì:㱞¯³eË–CÆP;%§Ýo}.6lÀÁ}ÕÆ‡™ôõBà˜²yë“_³f|]w·õw×]÷'>B@¢áæ‚ÍåB}Ϲè–G÷ßQyÓßž6Ò~ú6eí~بó7ÛÞ¥÷ÌHˆ#×¼6oßu¢Òm £ÞëÖÇMzî´•õ̽£‘{®¾‹™÷Ñøw~ÿJžêùì²=æûÅb³=róX÷On_Qìë;6yêµ*-r}ë¯Û×Ä÷ m›Ës“§|u¤2“]Ëo‘ÿuâÏ?ê+ÏmÃÀŸÇ†Ó¯¼òÊ)u&÷É'Ÿ,ÿ¥ã› ¼ü¹mÓÇì«øë/•Tcâæ­§ÉöôôÈË.»Ìù”ee¾tÓϯÁ§› Œ€Äçýç}·^rʽ:vË×·ÅnúÒn{׃£0º`ß±ºhß±ª±uÜÅÖCK'en ½áKEk÷ÃÓ:‚Dn%rÿ5÷Q¿b§b÷4>FQ 2-²rn‡UZŸ±Šßù£yOÜù½o:Ç€ÉKDbíÚµv¹\Žm÷5‰ž,gœé¦×É8¶m'ÊiH e&ñÀØW]uUÓõTôL&-•JiÃÔ°­Ï–½~ýzIÔø÷/4f<ˆƒKà¬[·N^pÁÖÆ¾U‰Ü#ñónúƽÿç[ÿçškÞ6ñÀ.§¢.žö gqù½¿~Õª¹;rD´›™÷ T]ÆìŸœ›Kíî˜Ön2gw{iqka\ µÀ0)wüôòf®¾ëâûN8.Y’®\Tšì+í X]­Ï©%YI¶$+XÞUWÛïEÅ\oOþúÏ—Lütr.úö¦Û‡Æó‰ýDT`æ”1¦ : G”l·ÊŸyàÞKDÁ,tj …q!D‡g¬®#~b(’$"{ÅŠ.Õò‚›µÍd2ÂãH˜[6‘†ùþ†…´,+–J¥Š[¶lñÍŲ,iŒ‰2s+€EŠŸ .;1­u4™LVð z" u#ê¦ÿ{÷8ÎÏÞ/ŸIÄ™i÷Ö`†žÙCì‡tâ²7ÞñA[òW”âÜÕÛÀüøÐðóÝà?¯xd!.&ñ±ö·á—•â'…/c™s?þvÕ»[Åj½ÖºèÎÇNøä~õꇤ”­DÔÊ :KÂ!õŽ8iÓߎ®y²¶gôe;Ž#Œ1Ž”2©YLYŒbº0Ž1F …#άT*Qkk«BƹÕg«á©j@2‚ˆmÛ"›Í6ì7 „^À$€NÓDÅ X8ìZû¤nDU«Uç“ÿö¶;>óÇ¿îîh-ž=£tŸ]G? ÷gÂ\,‹‹Ò÷]²(©[Yprø£ àêKåµ¥ÃÖ oŒ ËíûÐ)÷@†X¥›ï=峿¸ûÔ;¶™YÑ”Çïæ^_"€»íëŒØ.§]–.ew,ÿÝݯûð¦M›S=Žß÷ÉqADV‰£ôŸ¹ó `e4Dœ6âí·64œ5þ=wá¤{鉻ûÛ¬ê39c)¥(•JGüf«Õ*utt!„¥µŽjH§Q;‘”Ò*•JMµçyÄÌ’™fŽqûW³°Œ1ÒuÝ—ÚžÕ7Ø–eÅ‹ÅDëg¿ÿ®;æwLì|÷îºléÂÑKMéî 1tôºùF}ÇfAø?ñ(ð?´Í›/Žáô“|q1\ ØÀ­íeËÜð„™ –Û÷&‹ñÉÞØóµ'wÍ{œÙ¶mKfóÔó}¤Ú]€MÕnye™úÌœŸ§µà‰D"ÒÓÓ` ¿Çaf6̬X•»+'ì!" @œ™£šy–øáÊñù9tˆ™·Ñ# ´Ö&™LNKX•R¤”$ƒš±®ÉsÄ­µ&)¥ "‹›ÍD¤u#áÅÁE°bÅ `3sÂqœv¥Ô¢lë¼æ-›ÏË–Þyáç­èZToo˜¸XŽ˜|ÑâÉ‚ÀDŽé´íeÙ:³5p Ïùžvu ¾vu!€?z³…w¼%†åï¬`G:ôàž7ƒÓã`ìéÌëg_›+$FˆXÛ¶m´ÖLD$„8Ò—4 ÌÝãw$Çu2Ñ!ßA™EƒîLNß÷M,ó‰( `­X^©¦Ë×ÿ4ƒˆkÌéáÚŸ|®eYº­­mF§3ãØä¸0ó’²/uXõ@ïûRJ)ØÆ˜ØÜdyîÂtî¤öXiyk¤²`bK2²{°Õûâÿû6Ú¶›É÷å‡Þ{”šŠûn ðŸ¿PøÏ_áü5ðöÇ{°è™$8SÂLÔÁG¶/ÉÿÇ Þçbœ™«Dä‘BL‹¢ÎÌÏÔþÙBDs<¶;Ñ@m°ÈÌíêê’˜™ÑɉDB)¥ªRÊq{)å3gÌ'¢…z·jÞ…­²5JÏ€˲JÌlß¾ýHã %K–Àu§çí–RR,£+VÐsÿÞÞ^ò¶g¾‚2–Gú„ùâÉÅÏX– ÂGDæ•íT9j?å.,ý¶rÊ3WD4õ0µ¸Î^fÎ)¥J–eUÇÇÇýùóçëÝ»wSoo¯Øõ©¶dÆ®ÌÝ.OÞµfCèH˜©·­FÆ’gDû“ok}dþÛ>¼ì"òQël&}5Ïs8\Ÿ<îîÈ ññã¶U:ö®ù§!w:ã®ÇõꜲ׷õ‹wÏݼdŸ×2´ê+ÙJ£>,¥TÛ»—ç¢î­=˜¦^OO|8ëÓÀ“{žýù€¿ðéLü`ü€ÚÚh]Ø=üõW]à«.Ú[€¿x·Ã—¾-fn{äU›w· @?3ï`æg„{<ÏË !ÊŽãXžç!›FGäA ¢qž1Æ"¢Ôp$!ËDFætXåû¥„´Þ±~ýûoÈårtZñ¦T,¥… €‹9µsb‰3öé›s'\#¥ŒX–5  2Õ«AÈ6+—Ë“ÑhÔ+—˦££ƒ7tþø”9ó+Ÿħx1:›\³¶1pמOµ~À´·ï„ðç}{á~i‘ΠùíùªÙ@†¾ADÿÓÓÓC ή_ÝX¿~½¸*ó“•þüçÄj>•ÀÝä©!m6P?›«W:⟶4Š9ÕC=K`}Ê|á“Öjs‰Ÿ ~b<«ÍÚÎÀC¹ÏÆ?ÝþÅê`]P„eYs~òôkÝÁbÛÃ3x4lú{ଇþüsO’™âNß›ÅçáœÓ€«¿ôß =|áë>­º8/§!"ÚÎÌOxZ)Õ_­VGªÕj¾\.{¥R©nƒ5»¥,¥¬0suªˆbΤì[ '€.:År¥R*šN§“óEöT4Ù¨ :²ÓÔèöB šjŒ€/¥ÔÑhTüËqÿûyNénIæ-^rг¢>U€?x\´ððø§#gåóùi)ƶHÏ9¨¯Ïu„ù‘¥üd&“¾ïÏX?ëïïkÖ¬±8_û_˜kçï—dÞY#9Öí_Iàåüö˜ô¯”^·nÝÊ Ñ³=ÿvÍè¼O/¾ë&Gè+ø<ë”>Q€ßÛjU*.òê:LQ'3/ù¯Íçm1L3 dÅ€{¿œÒ³?3Þà™fj€Ÿ¸a‚ÅL±x!ðÕ›¯J[Sîxï§€ã/œÅjÞûµkí°•ˆ¶i­wk­Gâñx¾¿¿ßëïïWÉdrÚ.¸Ð©µ1FÑT„HÞ\Y@Bˆ®Š‰4t„kˆ•ÌÜ*„è´%Ù¬£­Þ¢aÛ¶ëv‡Á ~f6–eq©Tår9òåãn|OZTþ™B¯Zó¹—¶9Þ­µ²Ž”òhvd“þ»o¯¸q­mÛb&îÛb±(º»»­•+Wƾ¾ü7ïMËê¿R—öAˆÚ¤ÿù?Oüù_Ö™¢N¶\°`Aâüäö>›ô…S˜ÞmCŸN.%"!¤”Q"jÝ™›Ý4Ô=:Ý ÔaE€ß~¸ä  »cÎpj¾Ð&˜ðe½·¹#!™>ö^àÞ¹G=ìÞ|üŸ€“Þ$N(ÙüóK—ŽÒIû J©}²®ë·oßîoÚ´I¥R©ëÊDÄÏz½ë¿.Û•ª±Ú™yɨJ7T‰¼\ÑÉÌ ›†ê¤®ÏŸ^´mÛ1ÆPÍU:%˜™‚ °:;;£‰D¢­Ó*}|Ús¯iÙÿ¥”¥µ>ª¨vÊ > ¥´-Ëš–€c(‹Yét:–ŠÙívù3y^DèO½-ótº··×J¥RN2™L¾¯ýÎ R¯šnm¶ûÑÞÞ^Kc|fVĵÛÎÉÚšñB‘pÓ'·ti@¡€,ðçïŸø€ŸøCóü-[·Bﺸù[ÀȽ€Šyà¯Î}Ðz0Sò‹·¿ýþ µÖù ŽQb“EZ¤¤ÛpW.™h©F|œ? ³’-ÒÇI)3æGE°¬Q›ª±Ç<ŠÅ|ß·§K'"aŒqÇI_1çÎwF(8,ÿb*$¥÷gkRƒÄt²R_:÷ÉîZ$}Ê~8ÌQ’ÑhÔÑZ§/ï¸ýR‡T÷LžGàyoêÜù—ŽãD“Édœ™;Nˆ ÿÅÌÆ¬ÿxýq[æfÎÑ3ÆÊñâ{OœUØMØÀO?üñs|a€ðÇÞàOŒ¾Úïê8ï4àš/»o¼~@W€ÿ¾¸ä `ι€œ#Ü2=Ì›—ë©§ŽIß÷õÑ$5ÕvUÉÌò’äã'ÆÈoÈ-*™háͺÉýA{ÃgEHÍÐBˆLR¸ µ)˜XV)5má!„EDqcLG‡,ߨbÉûƒŽ†A '^•Øuf­Ÿ)ö¤NèaÕÚÌÇ"[#g!äbMÙP;µBˆÎ6YîiÔNCð.NUqãr³6ôE‰D"á8N‹eYóÚ­Rëôª.- ª¶Ã´{'NŽ ¼É2Æä„#5ÿú¼_>sÆð«mKÆí™UdWHDïÜܼ]÷Bà#—¿X²ˆ-DÈ} ¥i½ôÒ»{ï¿ÿ ßÂQ°E¿¼à§Ÿ`“Þʘðç4k7´ï‘Ùë·7Ô$ l-Œ.ÞêÎsR²Ú02>©ãY!k­œ~ b[‘$¢N‹tÀâÍ¥SÝ‹gV>Ùy8Þ=LÈãÂ_R˰ÜÄy°ÄçF×]cñ†¹?M/°r‡me6é…5âå”§ƒ„eYIfžc“nÈb¾>¿¦ú«Òi•×Ä·©?i»ó°Õ#ÈtcZ-ËJuÙãËø°¹MÔ|oâBoUt@ýŸŽ_6*7»Äò}¿ „•Rcæ•|§ó–]§Ì}늇g•AøöŸ‰ðO×éð¡uÀ[/NX¤ºêÀ‹J4èì,ÿ€ïφUÇ«pÞ‘Úxl{×Ϻ‡ˆÊÌœ: ŒÆS_`/î™rJ¸ ÑœNqX™Rk­Ù²Ž¬ 3³#„H茉 aÌ$«Ò3ó¨JóñÎác\Ljð÷>fuZyl»Ë}~‡n" ˆ(r$5‹C^—#„°˜9~Ãq©6€PMÔV3ŸˆÚŒ1-óœâŠFmŠ&fˆHOêxÃSYÀt f.K)Ç1ƒŒÞÒòPÞ‹ÏžT%€¯¼ ¨lòyà‹ÿœõ }:@xÑY8¶­W~éK·÷Äb±¦pÇ›ªÝçu|Œ™KÌ\qÙ.t¼¡Ò)‹ ³ÇP¾Ó¨nÝ BLÛ-„p˜9!„h ¿á†'‰+ŠDï’?Ÿ™©T;+„™T9"nØCzN½!šk‰5f°Ca‘ó¶„ðšŒÛŒ¦¡óSÂÄçËñEÚÓTnx ÕÆ=Aà†}Xdˆ \¥T¡VépÀ¯­ñ›Ÿ9mv%AÀ v±¼d ß,\X¸"™LÚÏ×ÕÍUãø×ÖÜmŒÉQ@ @uL§ªY)Qíì”ù¦‘ñ]^f€Àc¦+ ³<ãÌœ’0 m$ :`L qN2cµ~šªF&`æ}F$¸Qº–mcÌ‘T,ª›ˆÒïk±Ig pÓušª]‘ŽPÐÖxܬŒHj•0í¢¿¿_Y–U‚`‚ˆ뤶;÷®ÎVÒ3¨¹ƒ0tµÀHíÿ€YÔ{¾¡µ¨æó±étÚéïï?æ‰9ûƒŽòWÇÞ|Ó¤N ŒfjjV5«R ]½Q´$„×°0ƒf¡¶ù ™90Æ­õ´¤å0s¬Ù±mSM@ˆžl !ˆÈ2ƈfûˆ$V¨i¦I?$ºY¦F=±ª6îÆpH’0MD1ʼn(1© ßW‹¬8Ô8±y.‰¼6fXÖ¦M›tww·ŸH$òÌœµ,kÀþ‰Ö®ôxsÖ(#Ì4ið» €¥n°!½îE@HÜ{ï Å›o>}ÛÄDüqž1ÚBˆd29«ä‘êñ@<¡f h T»;´+XVeÙ[KTÊ1³U?A†UKÃ/J~<.ü†‹¾h¢£ÌìY–¥„3‰ö‹šQÜôûÄAHIiìîæmq¤8ˆPaf—ÐØ–ÂcÌ”RdaŒ±0[Y@{\B㓯ÖÊbfÚëw––ƦÞgÆv.¾³¼róƒúµÕb‚Dô¸1æA«–€c.»ì2ßó¼‚bÔ²¬fžg˜;¯Ûvæø‡Îúõa;¾„½W‡Úu#Ô•‰¹~ àmͧò¼@ûëÐßè{ÃðÄD"KDCÌf’™Ë¨éó‰Dãã÷HøÎÄë'*•>B"@@à½Æ˜íD´›ˆF´Ö!DÌSBT‚¶\£þ¢"ˆÄ…ßpqL|˜™=cŒªÓG¦;ÎZnFÓYc!›æ\2„£Æ¬g…öß´ÙtƒÌLá‰ÅM7¯Ó¢{Vε&DI5-fQqU¥vøó «"ûs¡¯ˆ -?5ºû™ÇÝ%ãu/!Bµ¸ÄÌžUëÈ\pÁjáÂ…U­uNJyày2»¸mûÄü¶íC‡wO-âmÛçóÛâ5ßëÒ`€§œØ´õ±ƒA˜Xr'ð“WLäû Õ É]Bˆa­uÞ÷}/‚Yyëˆh˜™Ëµ“¡ WûÙ>"Ú­”Ú§”šˆD"^hW‹*U‡‚¶IÅ‚-:4E5F¾#¯¡*0¡’ƒB_J©ªÕªñ}ŽÓTû8µ@¨þïÉ×l0—‰bãÙ¤Ç"Bm÷ç¼)õèŠÎPS^B¸‡Nµ¶ 5•¯©tÌtbæ)jEd謩mÏŽŠB៯J)ë»á3oŸ3ôÄèq•¼×_ðÙwœ1Ì@nŸê,z|„ä¢ú)Ò‚‹ù‹c3ùðÀ·\ ¦Ä¸Ûð43?à cÌSD´K)5*„(‹E¶t"zÒ³ ÀCƘ‡™ù1"zÚ³›™Gòù|¡¿¿ßSZëà ê¹;¦R‡–6)‘–Õ†‹t§?w/3J)cÛ6O7’nŒñ‰¨`Œ™ì!¢½~GúþíüåÛæZù†Ô–ç aV÷  ]Î%y*þ4n#K0Ç*¬øD憳™y mBì‘Rj|÷õõ™µk×úBˆ¢R*+¥`æN"Jö=õ*ËÓ6Mºñ*ÂsÁ1L-{‚Îô g¨ùy_Á³WÜ,p?€Gœ~t“?€­ hÃgæmÙ÷CRÊÉr¹\¶mÛÏårÁÆgÍÅ2Æ<ÃÌ{µÖDT6Æx®ëzÕjÕC¸këM›6™5kÖXmmmš™"r¸Y®‡ùæ;eñ0Q,½þÜæ PJ×uɶ§½Ù»Ì\`jªC®SæçüEû-oZhǪݱˆI)ÁaŘæ6Ñ1 II¯æbgæ‰'½®‘[J§´^œ|¢!±éxgô/o¿íº:ï.cŒ[,½C„™yݺu*—˹étzÒ3(¥lafg¸”.D¨"Tž’ìò2sV8CíMGyð^™0!¥÷x4­Ç1-ì@(S¨¡£ºe»1f€jµ:®”*g³Ù`ãÆGYf_1êºîp4Èår.BUM8„¹nÝ:Ca1ae“jV¥¦lR‡­Ž¢‰Žð,ËR‰DÂc¦½ׄc@Q)Un±üòÇ;oü“6Ù8²ÜTW‰ŽnñÒ»âHý!ˆˆ¦UJhÊ~ «òƘlÍ®ÎôåÏI§EÅ>'þL#öµ8=¾÷˯kÝóš{JK«Ñh48ÌÖ××gz{{}E)å(3Ç(,W3LD\Û ÓC;ý¹‹4ç†Å–¸!÷ý2Ì<ÑP0öOÑ& `ð›ÇO¹™ˆÆµÖy¥T!½ý˜\K`Œ©"´¨*ÙlÖo&t}}}èíí5–e)˲\cŒ;´NN÷9EafOk]¿9wÚ <ùA”…,¥ÔŸšó‹OµÉòâiO²†é,êc…f±–çâæÒéîÞ #tj0 ³ ø’U.&‚ý-²ºsHµŽ3s¡­@€ôÕ“=‘VY™Bdð0ÍÇ"Óýîö>wOiéGûúú÷×N](ª---9­µRVˆ(^‹àRJÇSB¤·û  ÓrÑ,nõ\I tûîE((¯žÎ먵ÿ-Â`d#ó,Ð TüHqkvÞn!¨\­V½þþ~õðÃÓ» a:¨ :‰è#ä”°ëº&‹ÌìQuÐÞÐÕÛy6ÆxA(ß÷ïûH§§Ç#å07[cð7s¹.cR¾ÐKñ¢×Ø™úý9Õ'ÜÅ c’¢bЂÈ%6Y[˜½x[«Æ)QQácâBˆ43§Sô»¹‹­Ov^7wŽ•?Lâ¿üK ®½nwOÏobúúúLOOOFK–ef.3³ó•ˆƒ8 *ƙ֭î+×8³±b)ÂÀâož(MÉâ ¿Exr4B@Â쫃Tó±Rr€ˆÊDäAtww¿X²pGG‡B¨š¡^ÍéD¡jþÕ¥1•Bø¶m«|>of˜ù(mÛv˜9:Ç*¼¾Q ë g ­ yNü™—D=Üé@BO2sv±3û›Ì/N>èWËœ ßÊö¬}Ä]ZÒZ—‰¨ÎïJ0s´¤û—XŸì¼.Þsç-æØ¥ïœÙeŸÜð…03oܸQïÛ·Ïcæb¡Pw]wÔó¼1×usALr˜h5  °Ùíj®24*-GY×U­FKwÀ|‡ !<…Îð„¥Óžc·Žæ[w3s%¡]ð¢Ñ&Ëå² ‚@ÕNГՄ´ø\ «ÖýB_¡£ÑèŒæ`Œq˜9•þ‚”tW5js}áÌꯊ§º< êÊK ©a;m¨¦ÊvLWkíyžWª™ {µÖ;‰¨ÀàpžøAîu OsIæø-|ð¦¡üZ„]oذÁlܸñà%nÍŸ?_F"‘’1¦ „(\_8s×I‘ý í‰Æî• €çjÝ…`ÀFõTúÑÚÿŸnÐOÀ"„jÔâdû&Úw¨H)ýR©tÄϘ™Ï<óL>í´ÓÔA†º;¦RÕ.{¼ùí[5<íÎß ÐZ›D"Á¾ßŒ,ÛQcŒ|ujǹ¦á»«¼2€ˆÙÁ1ÉÙ<:Сøƒj”™÷X¤šWvamˆHbáí æGʯ*šx*g’“:ÑZ2QëIwQñ w±=©ã­­²rØÜÛm÷Sh®éÕ|å•W Ðôôô ¦ÓU^t™S“ÿ‘ëþdæú.»Q*(Â…¾!ã7`;ÂxÆs_ÑœZÛéÑ&îíÚJD®çyjttô˜æ³E*•b)¥‚À·,«îê-ã3òت mã@h OLLðܹs§=)e”™m²Ô0a¨j]áèÀ– NáyeË1Œ1,„8"ÑÒÃt„ ”T@^ÐáÄÏýÇÖZ;]ÑÂùVåÿÎE“Â}d_¾#÷¸»xîëOæúµ…>cÚ:'×°qãFS(”ã8•šŠ5ÆÌãû‚Žìõ…3kVÞìx„Æ5¸À“xV8¢m•óœŠ GÉ‹æ‡&ÛG™Ù«V«³ªXr,‘ÉdX)eê±"ªé†ëƒQ2±±šÝø¾?cÊ#¢–€eÃZ/¤kÁàQÙ„¦þBcºû˜$ˆˆ¢©PW´•"¢Ö¢‰7œ¿Cº`b›· !Ô‚Y4£Œ£Ñ¨ò}¿ÊÌ9cÌBe){kåÔá­Þ‚üfAh`×Aq§x-B5l{Úx)µ@ÅãÐ7n|Q¤¯¯ƒ 0–e©éÐVI‹£ ££RJ/åºî´¯,¨£–S‘P,¾E6Ì<,„hšr¬ÀµškZÏÌ‘x¯¯­™šÖʪp4mYÖ¼Á ­¡€H2>M"¤£"nœ‹;êä·jµêÕ õa!Ä^ûáìÕùžÑЉúENU óx„Fû„÷˜¢âìô0’OïPñ<ÏÇ,¹VÇ®ë„Å\!Du¯ßž;Ò ò:>ÄÌžã8ª£cz™>¶"Z0ñÆ"°" 8UéÔc*;´‹#•u=%iht1DdßTÅ ³GÍ<ãÏk,DáÝ+e7¬êⳕ›•[¯¯¯Ï ¤SµÖ£Æ˜}æ@ N¨döò¯9T}˜J@l„‚±@Óü±™aÏxçv­uÕ¼˜úAàR©d”RŠ™=f®zl—óMòÓëÓ©­µ_.—u¹\63¼ÓƒHcŒ5¡â Yʬ¹YóÃPó™1•¬8œÛtn|ÇE³˜ÒaxU|Ǧúý jffs$!©%nÕ«jT·4Lè´Š‹ßÕz_SÚLNdz¨e7*X4¤ZËó­ÉCÜê«°ðo:¯ÿ£f}duzϬ¤'Éd2€B[[Û03'™9 ]E4~õdOâ#¿l'pèê=J™('yÛð|½}xA°ux¡;QJÖ)ä%f®We%¢I¥TµP(¨n¸áE?=€Ð“ài@µ–ŸÞ.;Åq,0û¾¶ÜeOð{ZïNE(8lw—O¸&þ ˜ â²1ÆBLù˜µÖ¾Â!rƘ‰>©b»„û˜ÍB¡È !„wPhV-Ñ«F{Õ|k"qœ=n/²Ç­ G; £ºÇïÈLb'ƒnô‡ság f6ëÖ­ ¢ÑhY1nŒÙoŒII)“"?<×ýø¬¤aù½…7µÖíˆ"ÂÒ9…: ²vLÖ^bæJí3®Rʳ,ËB5µE;Ž£s¹œ™mžy$a¥”ÑZDTý÷Ü…2ób"J"ä%0óSÌü0i˲XMÁ•›ê]µ‰¢µ®õþ†·IUL¤twåħ…€/„Ðuã<“ɰã8ÌÌÆøo=JD]Ìœ¬©TÞ&¢ÇŒ1åÚÏ"Z‰(a@´+˜[í÷笮Ð=Õ•¥»+'X4™yŸ1æi!ÄN!à‘û`¥{çCÕ¥º¶ñ‰ÐÞ&Y4Qç1w‰ý˜»„jjTa™ÝöcFf5Ú|"ªþxò5# Ç)LYÞ-„x Àc@àú¾ï9Ž3 `"aŒ‘Dä—8Öú´·0ú´·PÖœ~mýŒ¼Ë³‹™´ÖãŽã”<Ïóljh1ÆÄ‰ˆ ÃÚSA»àà(QX‡¡¿6î¡c* @@\³f¿fÍšb£–eÅÄ8¬¨ç‚Dªfë¹æ˜°#R›Š%"*+¥ªµ˜†'¥ô™9(—Ë*‘H¨\.g²Ù¬Éd2\£³ÏÞÕ V(¨å‘LÔv?ÁÌ „2FDY­uѲ,wºñ "¢žžù©UÛ;—Çî]¡àÍ)á½Ê"Ý0»'ÈlcæªRÊŸ˜˜Ðõ+›ëÙ‰‘HÄ'¢2ÂtÚ¡Ú”Ìì#äÆe™yRk]`æŠã8ÚcS¸¢*3@Ø"5"ªMDvÍ[U0,„Cx’ËÌ63 Ì΋RX1…ñlýßH­ˆrÌ<ÈÌýRÊÝÌœÕZ—¤”1¥T @N1ÌaA»€Jmqæ8,¦^­Ýýh¤”V]8ªLµ‚]SŸÜš“fX±›™û Ñd¹\öŒ1”ÒB8µyÖÓ[z.…· !v#L°QJ©€ÔU­9s渙Lf’ˆ9 Ö(„Ÿ ÔOB]HJp™ˆªZëêÁQ*•jùÞuøáx Ó1²3XJ©Ô9fûk÷óQÔ£…y"Ú/„UJ•µÖÁöíÛ§öëׄ£««+rbìáwtZ¥¯i »Ç=FDeÇq66VŽF£ ¡»˜Œ1¨©[y!Ä Â%RûŽ\SsGµÖCBˆáR©4‰DªÇT@j/ܬ[·NU«Õ 3G"Ô<õ—ajùÜecLµ¶c¸žçùÌÄãq•Ífˆú QÿRõ¸pME«¦Óé1˲ÈS`æÂÝUQ•Ã;ÇLj¨hÛv=½!ê‘Éd¢ÌœšÔñt§5õ^»‹·ý¶´ú­uÙã?ÇUÍmmmÊ÷ýŠbŒ™É²¬¼Ö:Y£1¦ÊÌ9Y)å¤1ÆÓZW84ØKƘfŽãÐ3!„°´ÖõSFQX/§µ.X–¥Œ1“µ3Æ0Â…gdXÇ'jŒq,Ë‚R*¨nyfÎ ! ¥RÉM$ì8”Rã„”²„ÐNµžÎEf÷}"‚ÊÄÄ„·lÙ2LLLèH$X–UD¨â¦˜9*„°jÑv¯¦¢Œ1y)e¡T*U‹Åbpà 7˜žž ”N§µRÊ‹D"9¥T‹eYqªÕþ­¹ Ë-oŒ)F"‘J¹\>¼hñ@½ðƒëºÅÚQ\¶,kDJ)}ß7µ 9ßu]_k´¶¶ÙlV—J%ÓÕÕe²Ù¬yâ03_uÕUzß¾}n[[Û$3û5U¡~§c”RªjŒ©D£ÑêöíÛ0¥ïš2™ŒÝÒÒ’™Ô‰)Iû»ü9߸èWÌœ³,«¤µö¦ÊÔÇ8>>^-‹¨yÃÆîþBÁBˆÀ÷}Wk]±,«šËåT©T¢d2éÇb± € Çqœƒ/éÐZ“ÖZ2³¬]´iˆÈ—RºBOkmlÛ¶<ÏËÙ¶-kùšC²ŸPJYRJK)…šmæc<˲Ül6ëÐår###ÜÞÞζmÕ¬H¶mSäjß÷Ýd2©;::Øó<@•J¥ªã8“RÊB¦PJ±eYÊ÷}?/‹yƒƒƒ~2™TõâDĽ½½<88hæÏŸ0sɶí1¥”SSýADºÞG[[Û1÷õõz¾ÖQx|©T²:;;­X,fù¾OŽã°eYú`ÈårfõêÕ¼aÆL šyÆ 488(s¹œ•J¥,˲ÜñBè±±1•L&Õ‘œ\pµpáÿkïÚuÚ¢è¹û°¼6Ɇ‘"A)M:GéÓ)RŠü û(_@›ÂM$Ú&ÕF#aá„,ÈYl²¯™ûÀ&9Æ>•µë{|gfgæÌëêj.=ÃÝbÖA&ˆýý}Y­VùÜÜ\? ›8Dšm¼Œ4Ó#"WJùKJyªªêoÏó.D٩ͦŒ¿7\3鯹IEND®B`‚PyTables-v.3.1.1/doc/source/_templates/000077500000000000000000000000001231437614300177115ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/_templates/travis-ci.html000066400000000000000000000002301231437614300224730ustar00rootroot00000000000000
travis-ci status page
PyTables-v.3.1.1/doc/source/_theme/000077500000000000000000000000001231437614300170155ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/_theme/altered_nature/000077500000000000000000000000001231437614300220135ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/_theme/altered_nature/static/000077500000000000000000000000001231437614300233025ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/_theme/altered_nature/static/altered_nature.css_t000066400000000000000000000120141231437614300273330ustar00rootroot00000000000000/** * Sphinx stylesheet -- default theme * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @import url("basic.css"); /* -- page layout ----------------------------------------------------------- */ body { font-family: Times, serif; font-size: 100%; background-color: #111; color: #555; margin: 0; padding: 0; } div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 0 0 0 230px; } hr{ border: 1px solid #B1B4B6; } div.document { background-color: #eee; } div.body { background-color: #ffffff; color: #3E4349; padding: 0 30px 30px 30px; font-size: 1em; } div.footer { color: #555; width: 100%; padding: 13px 0; text-align: center; font-size: 75%; } div.footer a { color: #444; text-decoration: underline; } div.related { background-color: #6BA81E; line-height: 32px; color: #fff; text-shadow: 0px 1px 0 #444; font-size: 0.80em; } div.related a { color: #E2F3CC; } div.sphinxsidebar { font-size: 0.75em; line-height: 1.5em; } div.sphinxsidebarwrapper{ padding: 20px 0; } div.sphinxsidebar h3, div.sphinxsidebar h4 { font-family: Times, serif; color: #222; font-size: 1.2em; font-weight: normal; margin: 0; padding: 5px 10px; background-color: #ddd; text-shadow: 1px 1px 0 white } div.sphinxsidebar h4{ font-size: 1.1em; } div.sphinxsidebar h3 a { color: #444; } div.sphinxsidebar p { color: #888; padding: 5px 20px; } div.sphinxsidebar p.topless { } div.sphinxsidebar ul { margin: 10px 20px; padding: 0; color: #000; } div.sphinxsidebar a { color: #444; } div.sphinxsidebar input { border: 1px solid #ccc; font-family: serif; font-size: 1em; } div.sphinxsidebar input[type=text]{ margin-left: 20px; } /* -- body styles ----------------------------------------------------------- */ a { color: #005B81; text-decoration: none; } a:hover { color: #E32E00; text-decoration: underline; } div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: Times, serif; background-color: #BED4EB; font-weight: normal; color: #212224; margin: 30px 0px 10px 0px; padding: 5px 0 5px 10px; text-shadow: 0px 1px 0 white } div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; } div.body h2 { font-size: 150%; background-color: #C8D5E3; } div.body h3 { font-size: 120%; background-color: #D8DEE3; } div.body h4 { font-size: 110%; background-color: #D8DEE3; } div.body h5 { font-size: 100%; background-color: #D8DEE3; } div.body h6 { font-size: 100%; background-color: #D8DEE3; } /* make p.rubric look like h2*/ p.rubric { font-family: Times, serif; background-color: #BED4EB; font-weight: normal; color: #212224; margin: 30px 0px 10px 0px; padding: 5px 0 5px 10px; text-shadow: 0px 1px 0 white; font-size: 120%; background-color: #D8DEE3; } a.headerlink { color: #c60f0f; font-size: 0.8em; padding: 0 4px 0 4px; text-decoration: none; } a.headerlink:hover { background-color: #c60f0f; color: white; } div.body p, div.body dd, div.body li { line-height: 1.5em; } /* div.highlight{ background-color: white; } */ pre { padding: 10px; background-color: White; color: #222; line-height: 1.2em; border: 1px solid #C6C9CB; font-size: 1.2em; margin: 1.5em 0 1.5em 0; -webkit-box-shadow: 1px 1px 1px #d8d8d8; -moz-box-shadow: 1px 1px 1px #d8d8d8; } tt { background-color: #ecf0f3; color: #222; padding: 1px 2px; font-size: 1.2em; font-family: monospace; } div.admonition, div.warning { font-size: 0.9em; margin: 1em 0.5em 1em 0; border: 1px solid #2a441b; background-color: #e6f6dc; padding: 0; } div.admonition p, div.warning p { margin: 0.5em 1em 0.5em 1em; padding: 0; } div.admonition pre, div.warning pre { margin: 0.4em 1em 0.4em 1em; } div.admonition p.admonition-title, div.warning p.admonition-title { margin: 0; padding: 0.1em 0 0.1em 0.5em; border-bottom: 1px solid #2a441b; font-weight: bold; background-color: #fdfcae; } div.warning { border: 1px solid #940000; } div.warning p.admonition-title { color: black; background-color: #CF0000; border-bottom-color: #940000; } div.admonition ul, div.admonition ol, div.warning ul, div.warning ol { margin: 0.1em 0.5em 0.5em 3em; padding: 0; } div.versioninfo { margin: 1em 0 0 0; border: 1px solid #ccc; background-color: #DDEAF0; padding: 8px; line-height: 1.3em; font-size: 0.9em; } /** * Styling for field lists */ table.field-list th { border-left: 1px solid #aaa !important; padding-left: 5px; } table.field-list { border-collapse: separate; border-spacing: 10px; } th { background-color: #ede; } .field-list th { /* color: rgb(0,102,204); */ color: #993333; white-space: nowrap; } .first { margin-top: 0 !important; } td.field-body > blockquote { margin-top: 0.1em; margin-bottom: 0.5em; } PyTables-v.3.1.1/doc/source/_theme/altered_nature/static/old_pygments.css000066400000000000000000000052351231437614300265250ustar00rootroot00000000000000.c { color: #999988; font-style: italic } /* Comment */ .k { font-weight: bold } /* Keyword */ .o { font-weight: bold } /* Operator */ .cm { color: #999988; font-style: italic } /* Comment.Multiline */ .cp { color: #999999; font-weight: bold } /* Comment.preproc */ .c1 { color: #999988; font-style: italic } /* Comment.Single */ .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .ge { font-style: italic } /* Generic.Emph */ .gr { color: #aa0000 } /* Generic.Error */ .gh { color: #999999 } /* Generic.Heading */ .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .go { color: #111 } /* Generic.Output */ .gp { color: #555555 } /* Generic.Prompt */ .gs { font-weight: bold } /* Generic.Strong */ .gu { color: #aaaaaa } /* Generic.Subheading */ .gt { color: #aa0000 } /* Generic.Traceback */ .kc { font-weight: bold } /* Keyword.Constant */ .kd { font-weight: bold } /* Keyword.Declaration */ .kp { font-weight: bold } /* Keyword.Pseudo */ .kr { font-weight: bold } /* Keyword.Reserved */ .kt { color: #445588; font-weight: bold } /* Keyword.Type */ .m { color: #009999 } /* Literal.Number */ .s { color: #bb8844 } /* Literal.String */ .na { color: #008080 } /* Name.Attribute */ .nb { color: #999999 } /* Name.Builtin */ .nc { color: #445588; font-weight: bold } /* Name.Class */ .no { color: #ff99ff } /* Name.Constant */ .ni { color: #800080 } /* Name.Entity */ .ne { color: #990000; font-weight: bold } /* Name.Exception */ .nf { color: #990000; font-weight: bold } /* Name.Function */ .nn { color: #555555 } /* Name.Namespace */ .nt { color: #000080 } /* Name.Tag */ .nv { color: purple } /* Name.Variable */ .ow { font-weight: bold } /* Operator.Word */ .mf { color: #009999 } /* Literal.Number.Float */ .mh { color: #009999 } /* Literal.Number.Hex */ .mi { color: #009999 } /* Literal.Number.Integer */ .mo { color: #009999 } /* Literal.Number.Oct */ .sb { color: #bb8844 } /* Literal.String.Backtick */ .sc { color: #bb8844 } /* Literal.String.Char */ .sd { color: #bb8844 } /* Literal.String.Doc */ .s2 { color: #bb8844 } /* Literal.String.Double */ .se { color: #bb8844 } /* Literal.String.Escape */ .sh { color: #bb8844 } /* Literal.String.Heredoc */ .si { color: #bb8844 } /* Literal.String.Interpol */ .sx { color: #bb8844 } /* Literal.String.Other */ .sr { color: #808000 } /* Literal.String.Regex */ .s1 { color: #bb8844 } /* Literal.String.Single */ .ss { color: #bb8844 } /* Literal.String.Symbol */ .bp { color: #999999 } /* Name.Builtin.Pseudo */ .vc { color: #ff99ff } /* Name.Variable.Class */ .vg { color: #ff99ff } /* Name.Variable.Global */ .vi { color: #ff99ff } /* Name.Variable.Instance */ .il { color: #009999 } /* Literal.Number.Integer.Long */PyTables-v.3.1.1/doc/source/_theme/altered_nature/theme.conf000066400000000000000000000001171231437614300237630ustar00rootroot00000000000000[theme] inherit = basic stylesheet = altered_nature.css pygments_style = tango PyTables-v.3.1.1/doc/source/_theme/cloud/000077500000000000000000000000001231437614300201235ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/_theme/cloud/layout.html000066400000000000000000000074421231437614300223350ustar00rootroot00000000000000{# cloud/layout.html ~~~~~~~~~~~~~~~~~ Sphinx layout template for the cloud theme. :copyright: Copyright 2009-2011 Assurance Technologies LLC :license: BSD roottarget - optional target for root link (default to toc) logotarget - optional target for logo link (defaults to root target) googleanalytics_id - if set, enabled google analytics snippet & footer googleanalytics_path - optional subpatch for GA cookie collapsiblesidebar - enable collapsible sidebar support #} {% extends "basic/layout.html" %} {% set reldelim2 = reldim2 is not defined and '    ' or reldelim2 %} {# add script file to instrument collapsable sections and other features #} {% set script_files = script_files + ['_static/jquery.cookie.js', '_static/toggle_sections.js'] %} {% if theme_collapsiblesidebar|tobool %} {% set script_files = script_files + ['_static/toggle_sidebar.js'] %} {% endif %} {# add font stylesheets #} {% set css_files = css_files + [theme_fontcssurl] %} {# make root link redirectable #} {%- set theme_roottarget = (theme_roottarget == "" and master_doc or theme_roottarget) %} {% block rootrellink %}
  • {{shorttitle|e}}{{reldelim1}}
  • {% endblock %} {# make logo link redirectable #} {%- set theme_logotarget = (theme_logotarget == "" and theme_roottarget or (theme_logotarget == "" and master_doc or theme_logotarget)) %} {%- block sidebarlogo %} {%- if logo %} {%- endif %} {%- endblock %} {# wrap relbars in distinct classes to make themeing easier #} {%- block relbar1 %}
    {{ super() }}
    {% endblock %} {%- block relbar2 %}
    {{ super() }}
    {% endblock %} {% block extrahead -%} {{ super() }} {%- if theme_googleanalytics_id -%} {%- endif %} {%- endblock %} {% block footer -%} {{ super() }} {%- if theme_googleanalytics_id -%} {%- endif %} {%- endblock %} PyTables-v.3.1.1/doc/source/_theme/cloud/static/000077500000000000000000000000001231437614300214125ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/_theme/cloud/static/cloud.css_t000066400000000000000000000455761231437614300235760ustar00rootroot00000000000000/***************************************************** * cloud.css_t * ~~~~~~~~~~~ * * stylesheet for "Cloud" theme for Sphinx. * * :copyright: Copyright 2011 by Assurance Technologies * :license: BSD * *****************************************************/ @import url("basic.css"); /***************************************************** * page layout *****************************************************/ body { font-family: {{ theme_bodyfont }}; font-size: 100%; color: #000; margin: 1em 1em 0 1em; padding: 0; #background-color: {{ theme_footerbgcolor }}; #background:{{ theme_footerbgcolor }} url('sun.png') no-repeat right top fixed; background-color: {{ theme_footerbgcolor }}; /* background-image:url('sun.png'), url('cld.png'), url('grass.png'), url('mount.png'), url('pyne_icon_small.png'), url('cld1.png'), url('cld1.png'), url('cld.png'), url('cld1.png'); background-repeat: no-repeat, no-repeat, repeat-x, no-repeat, no-repeat, no-repeat, no-repeat, no-repeat, no-repeat; background-position: top right, 2% 40%, left bottom, 100.5% bottom, -1% bottom, 9% 12%, 4% 80%, 93% 450px, 99% 25%; background-attachment: fixed, fixed, scroll, scroll, scroll, fixed, fixed, scroll, fixed; */ } /***************************************************** * page layout - relbars (top & bottom) *****************************************************/ div.related { margin: 0 auto; max-width: {{ theme_max_width }}; background-color: {{ theme_relbarbgcolor }}; line-height: 30px; color: {{ theme_relbartextcolor }}; } div.relbar-top div.related { border-radius: .7em .7em 0 0; -moz-border-radius: .7em .7em 0 0; -webkit-border-radius: .7em .7em 0 0; } div.relbar-bottom div.related { border-radius: 0 0 .7em .7em; -moz-border-radius: 0 0 .7em .7em; -webkit-border-radius: 0 0 .7em .7em; } div.related a { color: {{ theme_relbarlinkcolor }}; } /***************************************************** * page layout - document *****************************************************/ div.document { /* note: relative used by div.sidebartoggle */ position: relative; margin: 0 auto; max-width: {{ theme_max_width }}; background-color: {{ theme_sidebarbgcolor }}; } div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 0 0 0 {{ theme_sidebarwidth }}; } {% if theme_collapsiblesidebar|tobool %} div.document.collapsed-sidebar div.bodywrapper { margin-left: 0; } {% endif %} div.body { min-height: {{ theme_min_height }}; /* note: this ^ is just a hack to prevent body from being shorter than sidebar */ background-color: {{ theme_bgcolor }}; border-left: 1px solid {{theme_bodytrimcolor}}; color: {{ theme_textcolor }}; padding: 0 20px 30px 20px; } /***************************************************** * page layout - sidebar *****************************************************/ div.sphinxsidebar { /* it's nasty sometimes, but looks prettier than letting too-long module names break into document body*/ overflow: hidden; width: {{ theme_sidebarwidth }}; } {% if theme_collapsiblesidebar|tobool %} div.document.collapsed-sidebar div.sphinxsidebar { display: none; } {% endif %} div.sphinxsidebar h3, div.sphinxsidebar h4 { padding: 0; margin: 24px 16px 0 0; border-bottom: 1px dashed {{ theme_sidebartrimcolor }}; font-family: {{ theme_headfont }}; font-weight: normal; color: {{ theme_sidebartextcolor }}; } div.sphinxsidebar h3 { font-size: 1.4em; } div.sphinxsidebar h4 { font-size: 1.3em; } div.sphinxsidebar h3 a { color: {{ theme_sidebartextcolor }}; } div.sphinxsidebar p { color: {{ theme_sidebartextcolor }}; } div.sphinxsidebar p.topless { margin: 5px 10px 10px 10px; } div.sphinxsidebar ul { margin: 10px; padding: 0; color: {{ theme_sidebartextcolor }}; } div.sphinxsidebar a { color: {{ theme_sidebarlinkcolor }}; } div.sphinxsidebar input { border: 1px solid {{ theme_sidebartrimcolor }}; font-family: sans-serif; font-size: 1em; } div#searchbox input[type="submit"] { border-radius: 0 0 .7em 0; -webkit-border-radius: 0 0 .7em 0; -moz-border-radius: 0 0 .7em 0; } div.sphinxsidebar input[type="submit"]:hover, div.sidebartoggle button:hover { background: {{ theme_sidebarhighcolor }}; box-shadow: 0 1px 2px rgba(0,0,0,.2); -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.2); -moz-box-shadow: 0 1px 2px rgba(0,0,0,.2); } div.sphinxsidebar input[type="submit"]:active, div.sidebartoggle button:active { box-shadow: none; -webkit-box-shadow: none; -moz-box-shadow: none; } div.sphinxsidebar p.logo { margin: 16px 0 0 0; text-align: center; } div.sphinxsidebar .searchtip { color: {{ theme_sidebartrimcolor }}; } /*-------------------------------------------------- * collapsible sidebar buttons *--------------------------------------------------*/ {% if theme_collapsiblesidebar|tobool %} div.sidebartoggle { position: absolute; width: {{ theme_sidebarwidth }}; top: 0; left: 0; } div.document.collapsed-sidebar div.sidebartoggle { width: auto; } div.sidebartoggle button { position: absolute; right: -1px; top: 0; margin: 0; padding: 0 6px; background: {{ theme_sidebarbgcolor }}; border: 1px solid {{ theme_sidebartrimcolor }}; border-top: 0; color: {{ theme_sidebartextcolor }}; border-radius: 0 0 0 .7em; -webkit-border-radius: 0 0 0 .7em; -moz-border-radius: 0 0 0 .7em; white-space: nowrap; } div.document.collapsed-sidebar div.sidebartoggle button { right: auto; left: 0; border-radius: 0 0 .7em 0; -webkit-border-radius: 0 0 .7em 0; -moz-border-radius: 0 0 .7em 0; } {% endif %} /***************************************************** * page layout - footer *****************************************************/ div.footer { color: {{ theme_footertextcolor }}; width: 100%; padding: 9px 0; text-align: center; font-size: 75%; } div.footer button.link { margin: 0 -1px; padding: 0; background: none; border: none; font-size: inherit; font-family: inherit; } div.footer a, div.footer button.link { color: {{ theme_footertextcolor }}; text-decoration: underline; } div.footer a:hover, div.footer button.link:hover { color: white; } div.footer + div.footer, div.footer + script + div.footer { margin-top: -12px; } /***************************************************** * adaptive page layout - if too small, hide some things *****************************************************/ @media only screen and (max-width: {{ theme_compact_width }}), only screen and (max-width: {{ theme_minimal_width }}), only screen and (max-device-width: {{ theme_minimal_width }}), handheld { body { margin: 0; } div.relbar-top div.related, div.relbar-bottom div.related { border-radius: 0; -moz-border-radius: 0; -webkit-border-radius: 0; } } @media only screen and (max-width: {{ theme_minimal_width }}), only screen and (max-device-width: {{ theme_minimal_width }}), handheld { /* hide the sidebar */ div.sphinxsidebar { display: none; } {% if theme_collapsiblesidebar|tobool %} /* FIXME: would like to make this work some how, hiding it til it works */ div.sidebartoggle { display: none; } {% endif %} div.bodywrapper { margin-left: 0; } /* hide all lefthand links but first one, to save space*/ div.related > ul > li:not(.right) + li { display: none; } /* remove spacing on headers and reduce body padding */ body div.body h2, body div.body p.rubric { border-radius: 0; -moz-border-radius: 0; -webkit-border-radius: 0; } div.body { padding: 0 10px 30px; } } /***************************************************** * adapt for print media *****************************************************/ @media print { div.body { border: 0; } } /***************************************************** * document - link styles *****************************************************/ a { color: {{ theme_linkcolor }}; text-decoration: none; } a:visited { color: {{ theme_visitedlinkcolor }}; text-decoration: none; } a:hover { text-decoration: underline; } a.biglink { font-size: 130%; } {% if theme_externalrefs|tobool %} a.external:not(.issue):before { content: {{ theme_externalicon }}; padding-right: .1em; } a.issue:before { content: {{ theme_issueicon }}; padding-right: .2em; } {% endif %} dt:target, .footnote:target, .highlighted { padding: 4px; margin: -4px; border-radius: 4px; -webkit-border-radius: 4px; -moz-border-radius: 4px; } /***************************************************** * document - header styles *****************************************************/ div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6, div.body p.rubric { margin: 1em 0 -.25em; padding: 0; color: {{ theme_headtextcolor }}; font-family: {{ theme_headfont }}; font-weight: normal; text-shadow: 1px 1px 0 rgba(0,0,0,.1); } div.body h1 { margin: 0; padding-top: .25em; text-align: center; font-size: 200%; } div.body h2, div.body p.rubric { margin: 1em -10px 0 -10px; padding: 7px; background: {{ theme_sectionbgcolor }}; border: 1px solid {{ theme_sectiontrimcolor }}; border-width: 0 1px 1px 0; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; font-size: 120%; color: {{ theme_sectiontextcolor }}; text-shadow: 1px 1px 0 rgba(0,0,0,.1); } div.body p.rubric, div.body div.section.dimmed h2 { background: {{ theme_rubricbgcolor }}; } div.body h3 { font-size: 140%; } div.body h4 { font-size: 120%; } div.body h5 { font-size: 110%; } div.body h6 { font-size: 100%; } a.headerlink { color: {{ theme_headlinkcolor }}; font-size: 0.8em; margin: 0 0 0 1px; padding: 0 5px 0 4px; text-decoration: none; border-radius: 5px; } a.headerlink:hover { background-color: {{ theme_headlinkcolor }}; color: white; box-shadow: 2px 2px 2px rgba(0,0,0,.2); -webkit-box-shadow: 2px 2px 2px rgba(0,0,0,.2); -moz-box-shadow: 2px 2px 2px rgba(0,0,0,.2); } /***************************************************** * document - toggleable h1/h2 sections *****************************************************/ .html-toggle-button { position: relative; } .html-toggle-button:hover { /* give a little bit of hint that this is clickable */ cursor: pointer; box-shadow: 2px 2px 2px {{ theme_headlinkcolor }}; -moz-box-shadow: 2px 2px 2px {{ theme_headlinkcolor }}; -webkit-box-shadow: 2px 2px 2px {{ theme_headlinkcolor }}; } .html-toggle.collapsed > .html-toggle-button { margin-bottom: 1em; } .html-toggle.collapsed > .html-toggle-button:after { content: "[+ show section]"; position: absolute; right: 1em; margin: 0; padding: .15em 0 0 0; text-shadow: none; color: {{ theme_linkcolor }}; font-size: 65%; font-style: italic; } .html-toggle.expanded > .html-toggle-button:after { content: "[- hide section]"; position: absolute; right: 1em; margin: 0; padding: .15em 0 0 0; text-shadow: none; color: {{ theme_linkcolor }}; font-size: 65%; font-style: italic; } /***************************************************** * document - admonitions *****************************************************/ div.admonition { background: #f2f2f2; border: 1px solid rgba(0,0,0,.05); border-width: 0 1px 1px 0; margin: 1em 0; padding: .5em; border-radius: 0 10px 0 10px; -webkit-border-radius: 0 10px 0 10px; -moz-border-radius: 0 10px 0 10px; } div.note, div.seealso, div.warning, div.admonition-todo { background-position: 8px center; background-repeat: no-repeat; padding-left: 38px; } div.note { background-color: #E7F0FE; background-image: url(icon-note.png); } div.seealso { background-color: #FFF7E0; background-image: url(icon-seealso.png); } div.topic { background-color: #eee; } div.warning { background-color: #ffe4e4; background-image: url(icon-warning.png); } div.admonition-todo { background-color: #FFF7E0; background-image: url(icon-todo.png); } div.admonition.caution { background-color: green; } /*div.admonition.caution { background-color: #FFF0E4; }*/ div.admonition p, div.admonition pre, div.admonition ul, div.admonition ol { margin-bottom: 5px; } /* next 3 rules merge the title into the first paragraph */ p.admonition-title { display: inline; margin-right: 0; } p.admonition-title + p { display: inline; } p.admonition-title:after { content: ":"; } p.admonition-title + ul { margin-top: 0; } /* don't indent admonitions inside definitions */ dl div.admonition { margin-left: 0; } /***************************************************** * document - misc body styles *****************************************************/ div.body p, div.body dd, div.body li { text-align: justify; line-height: 140%; } /*dl.function, dl.method, dl.attribute, dl.class, dl.exception, dl.data */ dl { margin-bottom: 1.5em; } a.footnote-reference { font-size: 70%; position: relative; top: -.75em; } table.docutils.footnote { margin: 1em 0 0 1em; } /* join separate ULs together */ ul + ul, ul + div > ul:only-child, div.toctree-wrapper + ul { margin-top: -1em; } table.html-plain-table { border: 0; } table.docutils.html-plain-table td { border: 0; } h2 + table.docutils { margin-top: 1em; } /* styling for OLs */ dd ol { margin-bottom: 10px; } div.frontpage-images.container { text-align: center; margin-top: 2.5em; margin-bottom: 2.5em; } /***************************************************** * document - quoted text *****************************************************/ div.highlight .c { font-size: 90%; } pre { padding: 5px; background-color: {{ theme_codebgcolor }}; border: 1px solid #80858a; border-width: 1px 1px 1px 4px; color: {{ theme_codetextcolor }}; line-height: 120%; } tt { color: {{ theme_codetextcolor }}; font-size: 0.95em; } tt.literal { background-color: {{ theme_quotebgcolor }}; border: 1px solid {{ theme_quotetrimcolor }}; padding: 1px 2px; margin: 0 1px; color: {{ theme_codetextcolor }}; border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; } tt.samp.literal > em { font-family: {{ theme_codevarfont }}; font-size: 1em; padding: 0 1px; } h1 tt.literal { background-color: inherit; border: 0; font-weight: bold; } h1 tt.samp.literal > em { font-weight: normal; } div.sphinxsidebar tt.literal, a.reference tt.literal, div.related a tt.literal, tt.literal.xref { background-color: transparent; border: none; padding: 0; margin: 0; } th { background-color: #ede; } .viewcode-back { font-family: {{ theme_bodyfont }}; } div.viewcode-block:target { background-color: #f4debf; border-top: 1px solid #ac9; border-bottom: 1px solid #ac9; } /***************************************************** * nested sections - * (requires css classes set by astdoc.ext.nested_sections extension) *****************************************************/ dl.nested-section > dt { color: {{ theme_headtextcolor }}; font-family: {{ theme_headfont }}; font-weight: normal; font-size: 100%; } dl.nested-section-1 > dt { font-size: 140%; } dl.nested-section-2 > dt { font-size: 120%; } dl.nested-section-3 > dt { font-size: 110%; } dl.nested-section > dd { margin-left: 2em; margin-top: 1em; } /***************************************************** * index page - category colorization * (requires css classes set by astdoc.ext.index_styles extension) *****************************************************/ table.indextable span.category { font-size: 80%; color: #84ADBE; } table.indextable span.category.function, table.indextable span.category.classmethod, table.indextable span.category.method { color: #9AB9CE; } table.indextable span.category.attribute { color: #8BC38B; } table.indextable span.category.class { color: #9996c2; } /*table.indextable span.category.module { color: #8BC38B; }*/ table.indextable span.subject { font-weight: bold; } table.indextable td > dl > dt { margin-bottom: .5em; } table.indextable td > dl > dd > dl { margin-top: -.5em; margin-bottom: .5em; } /*################################################################# unorganized bits #################################################################*/ /***************************************************** * provide styling for TODO *****************************************************/ div#todos p.admonition-title { font-weight: normal; color: #AAA; font-size: 70%; } div#todos div.admonition-todo + p { font-size: 70%; text-align: right; margin-top: -.5em; margin-bottom: 1.5em; color: #AAA; } div#todos div.admonition-todo + p a { font-size: 130%; } /***************************************************** * add more whitespace to parameter lists *****************************************************/ td.field-body > ul.first.simple > li, td.field-body > p.first { margin-bottom: 1em; } td.field-body > p.first:only-child { margin: 0; /* work around Chrome bug */ } td.field-body > ul.first.simple > li > em, td.field-body > em { padding: 2px 4px; /* border-bottom: 1px solid #DEE6ED; background: #ECF0F3; */ } /***************************************************** * misc tweaks to parameter lists *****************************************************/ th { background: inherit; } table.field-list { border-collapse: separate; border-spacing: 10px; } table.field-list th { !border-left: 1px solid {{ theme_sidebartrimcolor }} !important; border: 1px solid {{ theme_sidebartrimcolor }} !important; padding-left: 5px; } th.field-name { background: {{ theme_sidebarbgcolor }}; border-top: 0; color: {{ theme_linkcolor }}; white-space: nowrap; } td.field-body > p.first:empty { display: none; } td.field-body > p:last-child:empty { margin-bottom: 1em; } /***************************************************** * css colorization of object definitions, * adds colored line underneath definition title. * color scheme used: * functions, methods - blue * classes - purple * exceptions - red * objects, attributes, data - green *****************************************************/ /* dl.classmethod > dt, dl.function > dt, dl.method > dt { background: #E2ECF3; } dl.exception > dt { background: #F2E3E1; } dl.class > dt { background: #E7E6F6; } dl.attribute > dt, dl.object > dt, dl.data > dt { background: #E2F2E2; }*/ /***************************************************** * EOF *****************************************************/ PyTables-v.3.1.1/doc/source/_theme/cloud/static/icon-note.png000066400000000000000000000022641231437614300240170ustar00rootroot00000000000000‰PNG  IHDRÄ´l;bKGDùC» pHYs  šœtIMEÖ / ={®HAIDAT8Ë•]lUÇÿwî|ìNgÛn÷ƒRªU[PJ!i@DjÔjR#å} áAM !êƒ$úà“1’Õ`Ô„•BbÐ4F%> ˆ!C¡eÁq¥@»ýØï™;×ת»ÿÉÉÍÉÉýÎͽ— †¶ïØ%äEBå§9çËáÚaÒ,!$Ιõ-À?‰E#N­ý¤ôaÒçë7nZص¤[Ñ4Š,A/™HeòøóÊEsôôÉש¼‹FNÜxûŽ]íßoتÚÔƒøDš`œ¹Ûq\—ð¥í~±½I¦Ã‡–““7žˆE#Çë‚·ïØµ‚içë-—“®Îe°ªËïj‘qp‡s¸©BÅŒOd­€Ï£mZ¾PÛûþžœi”ºcÑHòf–pkaçà–mM)#~m³P“b6xÄ’æ•K>¯Tlª•ŸœNÊú…yãñA DØ9ß±p+Wêo†éØÕÚòM)1 !eA %JH‘RRD¡Ð ˆ)Ÿ*ÍMçŒ|罜Pqó|°xsÂ]g‰ÇãÁlþÁ[àCˆA 1 !VN¯¤5¯¤»NÁ°„¿1Ì™³´.‚˜ÌŠªl8.ÏÏäÊf«_µ%J ÆyN7ì¤å°¢Ï+ËãD ”ÊfE tªî(~"“žå š½–G¢¥UÁ–P“GÌè•KYÝ:× ˆ©Þ®`ÏCˬ7-F5:=3k8Uß1³"c'ÚúäÐóâÅëy‹ „…›ÕŽŽÖÀu9Ê–Í&îiÓ=÷„Û»÷C×uìçƒéÍɹÑS3÷/í Sð•}½ËŬ^™4*êX"³ÇqÜ‹¦} Q•Vë¦s\•<§GNYgFGïß·ç£ÛŒ€ë¼6úÛÈ•Ì̤—¹Ä1*lJóŠSͪlˆƒ‹mÙTÒ:rä»i—9¯ü×Íû8Øœ9Cß KAŸ·uü÷?…Ì\ ˆsm"ˆÇ/üL‰°öàƒ-.c[cшyGà*}t䉾Áq Üèæ¦5ZÚF½Š ]C6“CMM?)¿Ü<7Kõ'ìýÇN¥ÞÔ‚6LÓBÕXA|ð h µé™U :m‹¥&_SºO²hè`JB¹ðl»ù¿´±ÜÒê{zxu´­ðásSÓ÷nu« Î$ŽØÙ^ªªÈZø Bá84-Ø9%äVo"Dìp`×w#Ð÷HH·±³]ƒíHxð`P”V $J -IêüˆàÎBFÇ­h»Çvyªß.Oõ3.‡Ûù0&ª•Í?Œ-m:=’žù»à‰DÙL†ÝÓÝ æl®;uج"Ôà±±QEÉ o¼reóKWßÌQŽ ›É¡`ö¿pñòúb×w4òí'±šÛ•»Î÷ó=U@À‹¥yˆ¥yˆÏ?Š_pçäN“OZ ;ŽU¬mé I¢ ™Pý`ø¥{éÒ•Ò‚;V|»!@{ñ$w¬0›ä·*ÅIp“«%AŸN¤gÐ ”½¹ “Ð|=€ð{€¾Y±®ŠÜjØô÷H„į¾{àUa7t×)õÌt8&²ÿ\úE¬ç÷ËçO¥Rr =óÕÍ­kF+Ù3$Üžw„ðÀžãÖê‹Y¬žHŠŸ‹÷ÙédÆ…§CÏÝùÉʆ°ÜdÀà¸cÿ·ðî¤çXñHvƒ¼7KfBºÂë–ükí¡dÜþÑ)Þ^¶þd¬ó÷m`âƒ{áß2yk=ñ8z“#tÄ0œ{ŸÞó pdYrŽ•$M³…ió‹k ÇÂ'è(‚O#ºôÜíýø ê½i—”uµÁIEND®B`‚PyTables-v.3.1.1/doc/source/_theme/cloud/static/icon-todo.png000066400000000000000000000024541231437614300240200ustar00rootroot00000000000000‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEÕ ';ÙT¹IDAT8Ë••[ˆÕÇk߹͙™3™™äŒãd¼Mµ4D“Ö[bã…AE¡O¥"^|SÛ>”"ŠJ }˜Êh"hª¢"eÎ$ÞKÕ¢¡4^&ãÉœ39×ïûöeù0§!Ú̃ löfÿ÷¯ýÿ¯-¬µgÏ~Œv‰¶K"L‹‰ËÁÛT¥´J4tê^yç—×ð}£6_=Úoý7´W>Ò´û©ÚlI³þ²¦ýÚ\ú‡.Î/׿0ëí?ãÂþùü£g¯=ßD±äÌ!Ľƒö_GÓ}dí}$«¯2uá/7™¸X¯ÍQ=†|‹åq\9Z¾pvó`lúzBrAT-z4Ž¥<± ›ôø÷›7aâÜÞwöö¬Ë8Š Þ&³?øé_¨T/H È¡‚bÐÐCí"¥rÆö[Þ¥2uí‹OG?;#ðâ_ÍÕªrùö[@;¸ôS‚k€” ªø`ñμECÀ˜Õ.Q.bòœŘÂóµ9âÿ6QþoìüƒQ]ÅeÇPu·‚ª|Fð)Á'x×'Îç >AdÛ=Hylgÿðá²óÄ·€kOþflfwudr+¶˜à{x×Áe_álï:×Åû.6k‘+äñ® Iç Ï0²i+Æn¯Íí>q6QÞ°-²ÉgxׯÛU¼màm—~‰wMœ]ÅÛ“Û&„.Þ÷I»bÓ¯pÙ D:ŒMß8A?Öj"b‰NýO˜¨„1EÄä‰ ¨:TSÔõQM ¾…wŸàì2ê3’öÛŒŸ{›Yùâï»N£&¼]!ø"ƾ8¼k‚€(¨zP‹sM4¸ÁUOûÄËŒÏì‘mqmŽ!¢!¡ßùA n%0>s>«ã} u-‚?‰÷m¼KY^n²q2w XÕSÙ¸t+q|å/Èçs!ÎOEIç"!bè­ »ºð) d™ÃYA5 ‰Ðá½sÀ˜Š!ø^·ù6›ï%7U›5p¶³­Ó²³4¬I/ø0 U~‚H3—4[ `$_ÙQOZǽŒ gÝ…˜ÂÚ“…ÞºMJän­0ºñfâüÞê ¾Xªb :øÞþ½•ñÙ­ë‘/TªìÄÛ•µÇZ'Š¥ F'w+ž‹â˜˜¹ŸÖ×ÿâµE^H-^€MÀôû/]qhËå¿–æÒS 1EÄתª),h ÎW‰ Uƒ¢ Žò†ë‘h†C/n·?À^Uor$:@óØÑ†+Ï^=}ñýô[5Ò·ˆÄMO"¦€˜<"ù5}‹ Ù¸(òŸwbÿ?íãõ&Ÿ]¸¯ L½öçܾ™ nزyÛC˜È´ß!퀯¥‰ äJQ,_‚DÃtW>æøá?r䣗n½Oï@"§õŒ!`ò™ßñÛ™j~Ï9—>Rªl•âÈyDq åÁû”àRN.½A«¾_WŽíËö¾‘ýêÉy^Z@ ùNÓÏØÿþÁÒÝÕ ¹.‡éÈP2†|õSúݾÔÅÈJ½¡o=üdÿ9à$v­‰£²Î¯9 8ÈP< ’Á8̇Sî¾Cã„m´¨×¥IEND®B`‚PyTables-v.3.1.1/doc/source/_theme/cloud/static/icon-warning.png000066400000000000000000000015671231437614300245240ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î<ôIDAT8µ•ÁOSY‡¿óún>Ö÷@^i*•G¡;#]vAºqa&³tX¸s?‰qaŒl\èb2‹Y`Lˆ1aá@táªÔM… d”ÉdD+Ѐ±Ô;‹B£#HÅøKÎâžœóå—soî­5ßCÆw¡f=E÷EâøCÀ4àâ/Zÿ»_O]Ž+ð{ÇÙ³£‘ÑÑá üYOϾàI‘áfß?êè Òìûç&E†¿ <%0Ló–—ɰ4=ÍÒô4^&ƒaš·¦D—á’“N÷m¾~Í»çÏ«Q(à¤Ó}e¸t ð”È¡€e]?œNóòáCRŽCÊqxõè‡Ói–u}JäÐWƒßÃUwhÈ-ÎÎb–J¤<”ça–JggqÝ÷põ«À“"Ýmm—›"Vr9NF£¨'OPù<'£QVr9š"ÛÚ.OŠt× 6à¦38\Îf ƒtzÕÒNÏ# ²œÍâ  ¸YøžHÆ:vì?ÏO¾JZ)~ìîf}a´&Ÿ¿'’ù"øšˆ!JÝŸ8Ár6ËÑöv\×E+UsŒR´º.GÛÛYÎf ?Ž(uûšˆ±'؇±–ÞÞþ/ØZY¡¿·­Z)Ê##”GFjçþTŠ­ÕUÞ--a'ý>Œí ž±•eÝÅbóyzººh²í(0>N`|¼vn²mz|ŸâÓ§„b1”eݘ±?áJK2yd}~$“ÉêlwÂ0ªñQ.™J¡€õ…ìžž#A¸²Ã­5wDºšg.Ü××Pxü˜S§èŒÇ·+d·K¯åŸ=cff†Ö3g(ÎÍm–VWøUë¿ÜûÞÐÐ…Òâ"›…Âî }ÔÐÚJ(ãU.÷à"ül œ®ll`'؉ħ»m˜ÿåv¶Pym Ó@£ ÿÀ]#ŸÿMà‹?Ö~ªÀÛ¿a‚íA)ÀZ€Õ­²Ç`÷”¶€ ` x#Zk¤ú¸ ªŽå`€UÓ|ô1à½ozIEND®B`‚PyTables-v.3.1.1/doc/source/_theme/cloud/static/jquery.cookie.js000066400000000000000000000027771231437614300245540ustar00rootroot00000000000000/** * jQuery Cookie plugin * * Copyright (c) 2010 Klaus Hartl (stilbuero.de) * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * */ jQuery.cookie = function (key, value, options) { // key and at least value given, set cookie... if (arguments.length > 1 && String(value) !== "[object Object]") { options = jQuery.extend({}, options); if (value === null || value === undefined) { options.expires = -1; } if (typeof options.expires === 'number') { var days = options.expires, t = options.expires = new Date(); t.setDate(t.getDate() + days); } value = String(value); return (document.cookie = [ encodeURIComponent(key), '=', options.raw ? value : encodeURIComponent(value), options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE options.path ? '; path=' + options.path : '', options.domain ? '; domain=' + options.domain : '', options.secure ? '; secure' : '' ].join('')); } // key and possibly options given, get cookie... options = value || {}; var result, decode = options.raw ? function (s) { return s; } : decodeURIComponent; return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? decode(result[1]) : null; }; PyTables-v.3.1.1/doc/source/_theme/cloud/static/toggle_sections.js000066400000000000000000000036441231437614300251470ustar00rootroot00000000000000/* * toggle_sections.js * ~~~~~~~~~~~~~~ * * Sphinx JavaScript helper for collapsible sections. * looks for sections with css class "html-toggle", * with optional additional classes "expanded" or "collapsed" * (defaults to "collapsed") * * :copyright: Copyright 2011 by Assurance Technologies * :license: BSD * * NOTE: while this provides full javascript instrumentation, * css styling should be applied to .html-toggle > .html-toggle-button */ $(document).ready(function (){ function init(){ var jobj = $(this); var parent = jobj.parent() /* add class for styling purposes */ jobj.addClass("html-toggle-button"); /* initialize state */ _setState(jobj, parent.hasClass("expanded") || _containsHash(parent)); /* bind toggle callback */ jobj.click(function (){ _setState(jobj, !parent.hasClass("expanded")); }); /* check for hash changes - older browsers may not have this evt */ $(window).bind("hashchange", function () { var hash = document.location.hash; if(!hash) return; if(_containsHash(parent)) _setState(jobj, true); var pos = $(hash).position(); window.scrollTo(pos.left, pos.top); }); } function _containsHash(parent){ var hash = document.location.hash; if(!hash) return false; return parent[0].id == hash.substr(1) || parent.find(hash).length>0; } function _setState(jobj, expanded){ var parent = jobj.parent(); if(expanded){ parent.addClass("expanded").removeClass("collapsed"); parent.children().show(); }else{ parent.addClass("collapsed").removeClass("expanded"); parent.children().hide(); parent.children("span:first-child:empty").show(); /* for :ref: span tag */ jobj.show(); } } $(".html-toggle.section > h2, .html-toggle.section > h3, .html-toggle.section > h4, .html-toggle.section > h5, .html-toggle.section > h6").each(init); }); PyTables-v.3.1.1/doc/source/_theme/cloud/static/toggle_sidebar.js_t000066400000000000000000000025101231437614300252430ustar00rootroot00000000000000/* * toggle_sidebar.js * ~~~~~~~~~~~~~~ * * Sphinx JavaScript helper for collapsible sidebar. * adds button into sidebar, and toggles document.collapsed-sidebar * all the actual collapsing is done via css. * * :copyright: Copyright 2011 by Assurance Technologies * :license: BSD * */ $(document).ready(function (){ var holder = $('
    '); var doc = $('div.document'); var show_btn = $('#sidebar-show', holder); var hide_btn = $('#sidebar-hide', holder); var copts = { expires: 7, path: DOCUMENTATION_OPTIONS.url_root }; show_btn.click(function (){ doc.removeClass("collapsed-sidebar"); hide_btn.show(); show_btn.hide(); $.cookie("sidebar", "expanded", copts); }); hide_btn.click(function (){ doc.addClass("collapsed-sidebar"); show_btn.show(); hide_btn.hide(); $.cookie("sidebar", "collapsed", copts); }); var state = $.cookie("sidebar"); if(!state && {{ theme_defaultcollapsed | tobool | lower }}){ state = "collapsed"; } doc.append(holder); if (state == "collapsed"){ doc.addClass("collapsed-sidebar"); show_btn.show(); hide_btn.hide(); } }); PyTables-v.3.1.1/doc/source/_theme/cloud/theme.conf000066400000000000000000000037731231437614300221060ustar00rootroot00000000000000[theme] inherit = basic stylesheet = cloud.css pygments_style = sphinx [options] #configuration options collapsiblesidebar = false defaultcollapsed = false externalrefs = true externalicon = "\21D7" issueicon = "\25CE" #link targets #NOTE: roottarget can be name of document (eg: 'index', or "") # logotarget is same, or can be "" to reflect root value roottarget = logotarget = #document dimensions max_width = 11in ; max width document will expand to. compact_width = 960px ; max width of "compact" mode (hides extra padding). minimal_width = 700px ; max width of "minimal" mode (hides sidebar and other extras). min_height = 6in ; min height of document #styling for document body bodyfont = Georgia, "Bitstream Vera Serif", "New York", Palatino, serif bodytrimcolor = #D0D0D0 bgcolor = #ffffff textcolor = #000000 linkcolor = #003469 quotebgcolor = rgba(0,0,0,.075) quotetrimcolor = rgba(0,0,0,.05) codebgcolor = #eeffcc codetextcolor = #333333 codevarfont = Georgia, "Bitstream Vera Serif", "New York", Palatino, serif #styling for document headers headfont = Georgia, "Bitstream Vera Serif", "New York", Palatino, serif headtextcolor = #000000 headlinkcolor = #003469 #styling for section headers sectionbgcolor = #84A6C7 sectiontrimcolor = rgba(0,0,0,.125) sectiontextcolor = #ffffff rubricbgcolor = #B2CCE0 #styling for footer / html background footerbgcolor = #1A4162 footertextcolor = #B0B0B0 #styling for sidebar sidebarwidth = 230px sidebarbgcolor = #F2F2F2 sidebartextcolor= #777777 sidebarlinkcolor= #003469 sidebartrimcolor= #C0C0C0 sidebarhighcolor= #F9F9F9 #styling for top & bottom relbars relbarbgcolor = #5682AD relbartextcolor = #ffffff relbarlinkcolor = #ffffff relbartrimcolor = #777777 #font css url - for loading in fonts (eg google font dir) #fontcssurl = http://fonts.googleapis.com/css?family=Crimson+Text fontcssurl = # set google analytics tracker googleanalytics_id = googleanalytics_path = / PyTables-v.3.1.1/doc/source/conf.py000066400000000000000000000230501231437614300170530ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # PyTables documentation build configuration file, created by # sphinx-quickstart on Sun Feb 7 22:29:49 2010. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('../sphinxext')) #sys.path.insert(0, os.path.abspath('../..')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.pngmath', 'sphinx.ext.inheritance_diagram', 'sphinx.ext.extlinks', 'sphinx.ext.todo', 'sphinx.ext.viewcode', 'ipython_console_highlighting', 'numpydoc', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'PyTables' copyright = u'2011-2014, PyTables maintainers' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. VERSION = open('../../VERSION').read().strip() version = VERSION # The full version, including alpha/beta/rc tags. release = VERSION # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. #pygments_style = 'sphinx' #pygments_style = 'friendly' #pygments_style = 'bw' #pygments_style = 'fruity' #pygments_style = 'manni' pygments_style = 'tango' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. #html_theme = 'default' #html_theme = 'altered_nature' html_theme = 'cloud' #html_theme = 'sphinxdoc' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. linkcolor = '#9F1E1E' trimcolor = '#511755' trimcolor = '#000000' html_theme_options = {'sidebarbgcolor': 'rgba(213, 197, 229, 0.15)', 'sidebartextcolor': '#280941', 'sidebarlinkcolor': linkcolor, 'sidebartrimcolor': trimcolor, 'collapsiblesidebar': True, 'relbarbgcolor': '#006FFF', 'footerbgcolor': 'rgba(252, 255, 0, 0.125)', 'footertextcolor': '#504A4B', 'bodytrimcolor': trimcolor, 'linkcolor': linkcolor, 'textcolor': '#323039', 'sectionbgcolor': '#3CAD1C', #'sectiontextcolor': '#777777', #'sectiontrimcolor': trimcolor, 'codebgcolor': '#F1FFF0', 'codetextcolor': '#000000', 'quotebgcolor': '#f6fcfc', 'rubricbgcolor': '#D00000', #'min_height': 'bottom', } # Add any paths that contain custom themes here, relative to this directory. html_theme_path = ["_theme"] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = '_static/logo-pytables-small.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. html_favicon = 'images/favicon.ico' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. html_sidebars = { 'index': ['globaltoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html', 'travis-ci.html'] } # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'pytablesdoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('usersguide/usersguide', 'usersguide-%s.tex' % version, u'PyTables User Guide', u'PyTables maintainers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. latex_logo = 'usersguide/images/pytables-front-logo.pdf' # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. latex_use_parts = True # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # A dictionary that contains LaTeX snippets that override those Sphinx # usually puts into the generated .tex files. latex_elements = { 'preamble': '\usepackage{bookmark,hyperref}', } # Documents to append as an appendix to all manuals. #latex_appendices = [ # 'usersguide/datatypes', # 'usersguide/condition_syntax', # 'usersguide/parameter_files', # 'usersguide/nested_rec_arrays', # 'usersguide/utilities', # 'usersguide/file_format', # 'usersguide/bibliography', #] # If false, no module index is generated. latex_domain_indices = False # -- Options for autodocumentation --------------------------------------------- autodoc_member_order = "groupwise" autoclass_content = "class" autosummary_generate = [] # -- Options for Epub output --------------------------------------------------- # Bibliographic Dublin Core info. epub_title = u'PyTables' epub_author = u'PyTables maintainers' epub_publisher = u'PyTables maintainers' epub_copyright = u'2011-2014, PyTables maintainers' # -- External link oOptions ---------------------------------------------------- extlinks = { 'issue': ('https://github.com/PyTables/PyTables/issues/%s', 'gh-'), 'irc': ('http://pytables.github.io/irc/%s.html', ''), } PyTables-v.3.1.1/doc/source/cookbook/000077500000000000000000000000001231437614300173625ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/cookbook/custom_data_types.rst000066400000000000000000000063611231437614300236510ustar00rootroot00000000000000:source: http://www.pytables.org/moin/UserDocuments/CustomDataTypes :revision: 1 :date: 2009-07-14 21:51:07 :author: KennethArnold ================================ Using your own custom data types ================================ You can make your own data types by subclassing Table (or other PyTables types, such as :class:`tables.Leaf`). This can be useful for storing a specialized type of data or presenting a customized API. Here's one way to do it, taken from http://sourceforge.net/mailarchive/message.php?msg_id=200805250042.50653.pgmdevlist%40gmail.com :: from __future__ import print_function import numpy as np import numpy.ma as ma import tables from tables import File, Table from tables.file import _checkfilters from tables.parameters import EXPECTED_ROWS_TABLE class MaskedTable(Table): _c_classId = 'MaskedTable' def __init__(self, parentNode, name, description=None, title="", filters=None, expectedrows=EXPECTED_ROWS_TABLE, chunkshape=None, byteorder=None, _log=True): new = description is None if not new: maskedarray = description description = np.array(zip(maskedarray.filled().flat, ma.getmaskarray(maskedarray).flat), dtype=[('_data',maskedarray.dtype), ('_mask',bool)]) Table.__init__(self, parentNode, name, description=description, title=title, filters=filters, expectedrows=expectedrows, chunkshape=chunkshape, byteorder=byteorder, _log=_log) if not new: self.attrs.shape = maskedarray.shape def read(self, start=None, stop=None, step=None, field=None): data = Table.read(self, start=start, stop=stop, step=step, field=field) newshape = self.attrs.shape return ma.array(data['_data'], mask=data['_mask']).reshape(newshape) def createMaskedTable(self, where, name, maskedarray, title="", filters=None, expectedrows=10000, chunkshape=None, byteorder=None, createparents=False): parentNode = self._getOrCreatePath(where, createparents) _checkfilters(filters) return MaskedTable(parentNode, name, maskedarray, title=title, filters=filters, expectedrows=expectedrows, chunkshape=chunkshape, byteorder=byteorder) File.createMaskedTable = createMaskedTable if __name__ == '__main__': x = ma.array(np.random.rand(100),mask=(np.random.rand(100) > 0.7)) h5file = tables.openFile('tester.hdf5','w') mtab = h5file.createMaskedTable('/','random',x) h5file.flush() print(type(mtab)) print(mtab.read()) h5file.close() h5file = tables.openFile('tester.hdf5','r') mtab = h5file.root.random print(type(mtab)) print(mtab.read()) PyTables-v.3.1.1/doc/source/cookbook/hints_for_sql_users.rst000066400000000000000000000640711231437614300242170ustar00rootroot00000000000000:source: http://www.pytables.org/moin/HintsForSQLUsers :revision: 56 :date: 2012-06-18 10:15:15 :author: valhallasw =================== Hints for SQL users =================== This page is intended to be **a guide to new PyTables for users who are used to writing SQL code** to access their relational databases. It will cover the most usual SQL statements. If you are missing a particular statement or usage example, you can ask at the `PyTables users' list`_ for it. If you know some examples yourself, you can also write them here! This page is under development: you can come back frequently to check for new examples. Also, this is no replacement for the `User's Guide`_; if you don't read the manual, you'll be missing lots of features not available in relational databases! Examples in Python assume that you have imported the PyTables package like this:: import tables .. .. contents:: Table Of Contents Creating a new database ======================= RDBMs happen to have several syntaxes for creating a database. A usual syntax is:: CREATE DATABASE database_name In PyTables, each database goes to a different HDF5_ file (much like SQLite_ or MS Access). To create a new HDF5_ file, you use the :func:`tables.open_file` function with the `'w'` mode (which deletes the database if it already exists), like this:: h5f = tables.open_file('database_name.h5', 'w') In this way you get the `h5f` PyTables *file handleé (an instance of the :class:`tables.File` class), which is a concept similar to a *database connection*, and a new :file:`database_name.h5` file is created in the current directory (you can use full paths here). You can close the handle (like you close the connection) with:: h5f.close() This is important for PyTables to dump pending changes to the database. In case you forget to do it, PyTables closes all open database handles for you when you exit your program or interactive session, but it is always safer to close your files explicitly. If you want to use the database after closing it, you just call :func:`open_file` again, but using the `'r+'` or `'r'` modes, depending on whether you do or don't need to modify the database, respectively. You may use several PyTables databases simultaneously in a program, so you must be explicit on which database you want to act upon (by using its handle). A note on concurrency under PyTables ------------------------------------ Unlike most RDBMs, PyTables is not intended to serve concurrent accesses to a database. It has no protections whatsoever against corruption for different (or even the same) programs accessing the same database. Opening several handles to the same database in read-only mode is safe, though. Creating a table ================ PyTables supports some other *datasets* besides tables, and they're not arranged in a flat namespace, but rather into a *hierarchicalé one (see an introduction to the _ref:`object tree `); however, due to the nature of these recipes, we'll limit ourselves to tables in the *root group*. The basic syntax for table creation under SQL is:: CREATE TABLE table_name ( column_name1 column_type1, column_name2 column_type2, ... column_nameN column_typeN ) Table descriptions ------------------ In PyTables, one first *describes* the structure of a table. PyTables allows you to *reuse a description* for creating several tables with the same structure, just by using the description object (`description_name` below) or getting it from a created table. This is specially useful for creating temporary tables holding query results. You can create a table description using a dictionary:: description_name = { 'column_name1': colum_type1, 'column_name2': colum_type2, 'column_name3': colum_type3, ... 'column_nameN': colum_typeN } or a subclass of :class:`tables.IsDescription`:: class description_name(tables.IsDescription): column_name1 = colum_type1 column_name2 = colum_type2 column_name3 = colum_type3 ... column_nameN = colum_typeN Please note that dictionaries are the only way of describing structures with names which cannot be Python identifiers. Also, if an explicit order is desired for colums, it must be specified through the column type declarations (see below), since dictionariy keys and class attributes aren't ordered. Otherwise, columns are ordered in alphabetic increasing order. It is important to note that PyTables doesn't have a concept of primary or foreign keys, so relationships between tables are left to the user. Column type declarations ------------------------ PyTables supports lots of types (including nested and multidimensional columns). Non-nested columns are declared through instances of :class:`tables.Col` subclasses (which you can also reuse). These are some correspondences with SQL: ==================== ========================== SQL type declaration PyTables type declaration ==================== ========================== INTEGER(digits) tables.IntCol(itemsize) REAL tables.FloatCol() VARCHAR(length) tables.StringCol(itemsize) DATE tables.Time32Col() TIMESTAMP tables.Time64Col() ==================== ========================== See a complete description of :ref:`PyTables types `. Note that some types admit different *item sizes*, which are specified in bytes. For types with a limited set of supported item sizes, you may also use specific subclasses which are named after the type and its *precision*, e.g. `Int32Col` for 4-byte (32 bit) item size. Cells in a PyTables' table always have a value of the cell type, so there is no `NULL`. Instead, cells take a *default value* (zero or empty) which can be changed in the type declaration, like this: `col_name = StringCol(10, dflt='nothing')` (`col_name` takes the value `'nothing'` if unset). The declaration also allows you to set *column order* via the `pos` argument, like this:: class ParticleDescription(tables.IsDescription): name = tables.StringCol(10, pos=1) x = tables.FloatCol(pos=2) y = tables.FloatCol(pos=3) temperature = tables.FloatCol(pos=4) == Using a description == Once you have a table description `description_name` and a writeable file handle `h5f`, creating a table with that description is as easy as:: tbl = h5f.create_table('/', 'table_name', description_name) PyTables is very object-oriented, and database is usually done through methods of :class:`tables.File`. The first argument indicates the *path* where the table will be created, i.e. the root path (HDF5 uses Unix-like paths). The :meth:`tables.File.create_table` method has many options e.g. for setting a table title or compression properties. What you get back is an instance of :class:`tables.Table`, a handle for accessing the data in that table. As with files, table handles can also be closed with `tbl.close()`. If you want to access an already created table, you can use:: tbl = h5f.get_node('/', 'table_name') (PyTables uses the concept of *node* for datasets -tables and others- and groups in the object tree) or, using *natural naming*:: tbl = h5f.root.table_name Once you have created a table, you can access (and reuse) its description by accessing the `description` attribute of its handle. Creating an index ================= RDBMs use to allow named indexes on any set of columns (or all of them) in a table, using a syntax like:: CREATE INDEX index_name ON table_name (column_name1, column_name2, column_name3...) and DROP INDEX index_name Indexing is supported in the versions of PyTables >= 2.3 (and in PyTablesPro). However, indexes don't have names and they are bound to single columns. Following the object-oriented philosophy of PyTables, index creation is a method (:meth:`tables.Column.create_index`) of a :class:`tables.Column` object of a table, which you can access trough its `cols` accessor. :: tbl.cols.colum_name.create_index() For dropping an index on a column:: tbl.cols.colum_name.remove_index() Altering a table ================ The first case of table alteration is renaming:: ALTER TABLE old_name RENAME TO new_name This is accomplished in !PyTables with:: h5f.rename_node('/', name='old_name', newname='new_name') or through the table handle:: tbl.rename('new_name') A handle to a table is still usable after renaming. The second alteration, namely column addition, is currently not supported in PyTables. Dropping a table ================ In SQL you can remove a table using:: DROP TABLE table_name In PyTables, tables are removed as other nodes, using the :meth:`tables.File.remove_node` method:: h5f.remove_node('/', 'table_name') or through the table handle:: tbl.remove() When you remove a table, its associated indexes are automatically removed. Inserting data ============== In SQL you can insert data one row at a time (fetching from a selection will be covered later) using a syntax like:: INSERT INTO table_name (column_name1, column_name2...) VALUES (value1, value2...) In PyTables, rows in a table form a *sequence*, so data isn't *inserted* into a set, but rather *appended* to the end of the sequence. This also implies that identical rows may exist in a table (but they have a different *row number*). There are two ways of appending rows: one at a time or in a block. The first one is conceptually similar to the SQL case:: tbl.row['column_name1'] = value1 tbl.row['column_name2'] = value2 ... tbl.row.append() The `tbl.row` accessor represents a *new row* in the table. You just set the values you want to set (the others take the default value from their column declarations - see above) and the effectively append the new row. This code is usually enclosed in some kind of loop, like:: row = tbl.row while some_condition: row['column_name1'] = value1 ... row.append() For appending a block of rows in a single shot, :meth:`tables.Table.append` is more adequate. You just pass a NumPy_ record array or Python sequence with elements which match the expected columns. For example, given the `tbl` handle for a table with the `ParticleDescription` structure described above:: rows = [ ('foo', 0.0, 0.0, 150.0), ('bar', 0.5, 0.0, 100.0), ('foo', 1.0, 1.0, 25.0) ] tbl.append(rows) # Using a NumPy container. import numpy rows = numpy.rec.array(rows) tbl.append(rows) A note on transactions ---------------------- PyTables doesn't support transactions nor checkpointing or rolling back (there is undo support for operations performed on the object tree, but this is unrelated). Changes to the database are optimised for maximum performance and reasonable memory requirements, which means that you can't tell whether e.g. `tbl.append()` has actually committed all, some or no data to disk when it ends. However, you can *force* PyTables to commit changes to disk using the `flush()` method of table and file handles:: tbl.flush() # flush data in the table h5f.flush() # flush all pending data Closing a table or a database actually flushes it, but it is recommended that you explicitly flush frequently (specially with tables). Updating data ============= We're now looking for alternatives to the SQL `UPDATE` statement:: UPDATE table_name SET column_name1 = expression1, column_name2 = expression2... [WHERE condition] There are different ways of approaching this, depending on your needs. If you aren't using a condition, then the `SET` clause updates all rows, something you can do in PyTables by iterating over the table:: for row in tbl: row['column_name1'] = expression1 row['column_name2'] = expression2 ... row.update() Don't forget to call `update()` or no value will be changed! Also, since the used iterator allows you to read values from the current row, you can implement a simple *conditional update*, like this:: for row in tbl: if condition on row['column_name1'], row['column_name2']...: row['column_name1'] = expression1 row['column_name2'] = expression2 ... row.update() There are substantially more efficient ways of locating rows fulfilling a condition. Given the main PyTables usage scenarios, querying and modifying data are quite decoupled operations, so we will have a look at querying later and assume that you already know the set of rows you want to update. If the set happens to be a slice of the table, you may use the :`meth:`tables.Table.modify_rows` method or its equivalent :meth:`tables.Table.__setitem__` notation:: rows = [ ('foo', 0.0, 0.0, 150.0), ('bar', 0.5, 0.0, 100.0), ('foo', 1.0, 1.0, 25.0) ] tbl.modifyRows(start=6, stop=13, step=3, rows=rows) tbl[6:13:3] = rows # this is the same If you just want to update some columns in the slice, use the :meth:`tables.Table.modify_columns` or :meth:`tables.Table.modify_column` methods:: cols = [ [150.0, 100.0, 25.0] ] # These are all equivalent. tbl.modify_columns(start=6, stop=13, step=3, columns=cols, names=['temperature']) tbl.modify_column(start=6, stop=13, step=3, column=cols[0], colname='temperature') tbl.cols.temperature[6:13:3] = cols[0] The last line shows an example of using the `cols` accessor to get to the desired :class:`tables.Column` of the table using natural naming and apply `setitem` on it. If the set happens to be an array of sparse coordinates, you can also use PyTables' extended slice notation:: rows = [ ('foo', 0.0, 0.0, 150.0), ('bar', 0.5, 0.0, 100.0), ('foo', 1.0, 1.0, 25.0) ] rownos = [2, 735, 371913476] tbl[rownos] = rows instead of the traditional:: for row_id, datum in zip(rownos, rows): tbl[row_id] = datum Since you are modifying table data in all cases, you should also remember to `flush()` the table when you're done. Deleting data ============= Rows are deleted from a table with the following SQL syntax:: DELETE FROM table_name [WHERE condition] :meth:`tables.Table.remove_rows` is the method used for deleting rows in PyTables. However, it is very simple (only contiguous blocks of rows can be deleted) and quite inefficient, and one should consider whether *dumping filtered data from one table into another* isn't a much more convenient approach. This is a far more optimized operation under PyTables which will be covered later. Anyway, using `remove_row()` or `remove_rows()` is quite straightforward:: tbl.remove_row(12) # delete one single row (12) tbl.remove_rows(12, 20) # delete all rows from 12 to 19 (included) tbl.remove_rows(0, tbl.nrows) # delete all rows unconditionally tbl.remove_rows(-4, tbl.nrows) # delete the last 4 rows Reading data ============ The most basic syntax in SQL for reading rows in a table without using a condition is:: SELECT (column_name1, column_name2... | *) FROM table_name Which reads all rows (though maybe not all columns) from a table. In PyTables there are two ways of retrieving data: *iteratively* or *at once*. You'll notice some similarities with how we appended and updated data above, since this dichotomy is widespread here. For a clearer separation with conditional queries (covered further below), and since the concept of *row number* doesn't exist in relational databases, we'll be including here the cases where you want to read a **known** *slice* or *sequence* of rows, besides the case of reading *all* rows. Iterating over rows ------------------- This is similar to using the `fetchone()` method of a DB `cursor` in a `Python DBAPI`_-compliant package, i.e. you *iterate* over the list of wanted rows, getting one *row handle* at a time. In this case, the handle is an instance of the :class:`tables.Row` class, which allows access to individual columns as items acessed by key (so there is no special way of selecting columns: you just use the ones you want whenever you want). This way of reading rows is recommended when you want to perform operations on individual rows in a simple manner, and specially if you want to process a lot of rows in the table (i.e. when loading them all at once would take too much memory). Iterators are also handy for using with the `itertools` Python module for grouping, sorting and other operations. For iterating over *all* rows, use plain iteration or the :meth:`tables.Table.iterrows` method:: for row in tbl: # or tbl.iterrows() do something with row['column_name1'], row['column_name2']... For iterating over a *slice* of rows, use the :meth:`tables.Table.iterrows|Table.iterrows` method:: for row in tbl.iterrows(start=6, stop=13, step=3): do something with row['column_name1'], row['column_name2']... For iterating over a *sequence* of rows, use the :meth:`tables.Table.itersequence` method:: for row in tbl.itersequence([6, 7, 9, 11]): do something with row['column_name1'], row['column_name2']... Reading rows at once -------------------- In contrast with iteration, you can fetch all desired rows into a single *container* in memory (usually an efficient NumPy_ record-array) in a single operation, like the `fetchall()` or `fetchmany()` methods of a DBAPI `cursor`. This is specially useful when you want to transfer the read data to another component in your program, avoiding loops to construct your own containers. However, you should be careful about the amount of data you are fetching into memory, since it can be quite large (and even exceed its physical capacity). You can choose between the `Table.read*()` methods or the :meth:`tables.Table.__getitem__` syntax for this kind of reads. The `read*()` methods offer you the chance to choose a single column to read via their `field` argument (which isn't still as powerful as the SQL `SELECT` column spec). For reading *all* rows, use `[:]` or the :meth:`tables.Table.read` method:: rows = tbl.read() rows = tbl[:] # equivalent For reading a *slice* of rows, use `[slice]` or the :meth:`tables.Table.read|Table.read` method:: rows = tbl.read(start=6, stop=13, step=3) rows = tbl[6:13:3] # equivalent For reading a *sequence* of rows, use the :meth:`tables.Table.read_coordinates` method:: rows = tbl.read_coordinates([6, 7, 9, 11]) Please note that you can add a `field='column_name'` argument to `read*()` methods in order to get only the given column instead of them all. Selecting data ============== When you want to read a subset of rows which match a given condition from a table you use a syntax like this in SQL:: SELECT column_specification FROM table_name WHERE condition The `condition` is an expression yielding a boolean value based on a combination of column names and constants with functions and operators. If the condition holds true for a given row, the `column_specification` is applied on it and the resulting row is added to the result. In PyTables, you may filter rows using two approaches: the first one is achieved through standard Python comparisons (similar to what we used for conditional update), like this:: for row in tbl: if condition on row['column_name1'], row['column_name2']...: do something with row This is easy for newcomers, but not very efficient. That's why PyTables offers another approach: **in-kernel** searches, which are much more efficient than standard searches, and can take advantage of indexing (under PyTables >= 2.3). In-kernel searches are used through the *where methods* in `Table`, which are passed a *condition string* describing the condition in a Python-like syntax. For instance, with the `ParticleDescription` we defined above, we may specify a condition for selecting particles at most 1 unit apart from the origin with a temperature under 100 with a condition string like this one:: '(sqrt(x**2 + y**2) <= 1) & (temperature < 100)' Where `x`, `y` and `temperature` are the names of columns in the table. The operators and functions you may use in a condition string are described in the :ref:`appendix on condition syntax ` in the `User's Guide`_. Iterating over selected rows ---------------------------- You can iterate over the rows in a table which fulfill a condition (a la DBAPI `fetchone()`) by using the :meth:`tables.Table.where` method, which is very similar to the :meth:`tables.Table.iterrows` one discussed above, and which can be used in the same circumstances (i.e. performing operations on individual rows or having results exceeding available memory). Here is an example of using `where()` with the previous example condition:: for row in tbl.where('(sqrt(x**2 + y**2) <= 1) & (temperature < 100)'): do something with row['name'], row['x']... Reading selected rows at once ----------------------------- Like the aforementioned :meth:`tables.Table.read`, :meth:`tables.Table.read_where` gets all the rows fulfilling the given condition and packs them in a single container (a la DBAPI `fetchmany()`). The same warning applies: be careful on how many rows you expect to retrieve, or you may run out of memory! Here is an example of using `read_where()` with the previous example condition:: rows = tbl.read_where('(sqrt(x**2 + y**2) <= 1) & (temperature < 100)') Please note that both :meth:`tables.Table.where` and :meth:`tables.Table.read_where` can also take slicing arguments. Getting the coordinates of selected rows ---------------------------------------- There is yet another method for querying tables: :meth:`tables.Table.get_where_list`. It returns just a sequence of the numbers of the rows which fulfil the given condition. You may pass that sequence to :meth:tables.Table.read_coordinates`, e.g. to retrieve data from a different table where rows with the same number as the queried one refer to the same first-class object or entity. A note on table joins --------------------- You may have noticed that queries in PyTables only cover one table. In fact, there is no way of directly performing a join between two tables in PyTables (remember that it's not a relational database). You may however work around this limitation depending on your case: * If one table is an *extension* of another (i.e. it contains additional columns for the same entities), your best bet is to arrange rows of the same entity so that they are placed in the same positions in both tables. For instance, if `tbl1` and `tbl2` follow this rule, you may do something like this to emulate a natural join:: for row1 in tbl1.where('condition'): row2 = tbl2[row1.nrow] if condition on row2['column_name1'], row2['column_name2']...: do something with row1 and row2... (Note that `row1` is a `Row` instance and `row2` is a record of the current flavor.) * If rows in both tables are linked by a common value (e.g. acting as an identifier), you'll need to split your condition in one for the first table and one for the second table, and then nest your queries, placing the most restrictive one first. For instance:: SELECT clients.name, bills.item_id FROM clients, bills WHERE clients.id = bills.client_id and clients.age > 50 and bills.price > 200 could be written as:: for client in clients.where('age > 50'): # Note that the following query is different for each client. for bill in bills.where('(client_id == %r) & (price > 200)' % client['id']): do something with client['name'] and bill['item_id'] In this example, indexing the `client_id` column of `bills` could speed up the inner query quite a lot. Also, you could avoid parsing the inner condition each time by using *condition variables*:: for client in clients.where('age > 50'): for bill in bills.where('(client_id == cid) & (price > 200)', {'cid': client['id']}): do something with client['name'] and bill['item_id'] Summary of row selection methods ================================ +----------------------+-----------------+---------------------+-----------------------+-------------------------+ | | **All rows** | **Range of rows** | **Sequence of rows** | **Condition** | +----------------------+-----------------+---------------------+-----------------------+-------------------------+ | **Iterative access** | ``__iter__()``, | ``iterrows(range)`` | ``itersequence()`` | ``where(condition)`` | | | ``iterrows()`` | | | | +----------------------+-----------------+---------------------+-----------------------+-------------------------+ | **Block access** | ``[:]``, | ``[range]``, | ``readCoordinates()`` |``read_where(condition)``| | | ``read()`` | ``read(range)`` | | | +----------------------+-----------------+---------------------+-----------------------+-------------------------+ Sorting the results of a selection ================================== *Do you feel like writing this section? Your contribution is welcome!* Grouping the results of a selection =================================== By making use of the :func:`itertools.groupby` utility, you can group results by field:: group = {} # dictionary to put results grouped by 'pressure' def pressure_selector(row): return row['pressure'] for pressure, rows_grouped_by_pressure in itertools.groupby(mytable, pressure_selector): group[pressure] = sum((r['energy'] + r['ADCcount'] for r in rows_grouped_by_pressure)) However, :func:`itertools.groupby` assumes the incoming array is sorted by the grouping field. If not, there are multiple groups with the same grouper returned. In the example, mytable thus has to be sorted on pressure, or the last line should be changed to:: group[pressure] += sum((r['energy'] + r['ADCcount'] for r in rows_grouped_by_pressure)) ----- .. target-notes:: .. _`PyTables users' list`: https://lists.sourceforge.net/lists/listinfo/pytables-users .. _`User's Guide`: http://www.pytables.org/docs/manual .. _HDF5: http://www.hdfgroup.org/HDF5 .. _SQLite: http://www.sqlite.org .. _NumPy: http://www.numpy.org .. _`Python DBAPI`: http://www.python.org/dev/peps/pep-0249 PyTables-v.3.1.1/doc/source/cookbook/index.rst000066400000000000000000000011301231437614300212160ustar00rootroot00000000000000================= PyTables Cookbook ================= .. this document includes contents coming from the UserDocuments section of wiki (http://www.pytables.org/moin/UserDocuments) and from the HintsForSQLUsers page (http://www.pytables.org/moin/HintsForSQLUsers). -------- Contents -------- .. toctree:: :maxdepth: 1 hints_for_sql_users PyTables & py2exe Howto (by Tommy Edvardsen) How to install PyTables when you're not root (by Koen van de Sande) tailoring_atexit_hooks custom_data_types simple_table inmemory_hdf5_files PyTables-v.3.1.1/doc/source/cookbook/inmemory_hdf5_files.rst000066400000000000000000000114451231437614300240500ustar00rootroot00000000000000==================== In-memory HDF5 files ==================== The HDF5 library provides functions to allow an application to work with a file in memory for faster reads and writes. File contents are kept in memory until the file is closed. At closing, the memory version of the file can be written back to disk or abandoned. Open an existing file in memory =============================== Assuming the :file:`sample.h5` exists in the current folder, it is possible to open it in memory simply using the CORE driver at opening time. The HDF5 driver that one intend to use to open/create a file can be specified using the *driver* keyword argument of the :func:`tables.open_file` function:: >>> import tables >>> h5file = tables.open_file("sample.h", driver="H5FD_CORE") The content of the :file`sample.h5` is opened for reading. It is loaded into memory and all reading operations are performed without disk I/O overhead. .. note:: the initial loading of the entire file into memory can be time expensive depending on the size of the opened file and on the performances of the disk subsystem. .. seealso:: general information about HDF5 drivers can be found in the `Alternate File Storage Layouts and Low-level File Drivers`__ section of the `HDF5 User's Guide`_. __ `HDF5 drivers`_ Creating a new file in memory ============================= Creating a new file in memory is as simple as creating a regular file, just one needs to specify to use the CORE driver:: >>> import tables >>> h5file = tables.open_file("new_sample.h5", "w", driver="H5FD_CORE") >>> import numpy >>> a = h5file.create_array(h5file.root, "array", numpy.zeros((300, 300))) >>> h5file.close() Backing store ============= In the previous example contents of the in-memory `h5file` are automatically saved to disk when the file descriptor is closed, so a new :file:`new_sample.h5` file is created and all data are transferred to disk. Again this can be time a time expensive action depending on the amount of data in the HDF5 file and depending on how fast is the disk I/O. Saving data to disk is the default behavior for the CORE driver in PyTables. This feature can be controlled using the *driver_core_backing_store* parameter of the :func:`tables.open_file` function. Setting it to `False` disables the backing store feature and all changes in the working `h5file` are lost after closing:: >>> h5file = tables.open_file("new_sample.h5", "w", driver="H5FD_CORE", ... driver_core_bacling_store=0) Please note that the *driver_core_backing_store* disables saving of data, not loading. In the following example the :file:`sample.h5` file is opened in-memory in append mode. All data in the existing :file:`sample.h5` file are loaded into memory and contents can be actually modified by the user:: >>> import tables >>> h5file = tables.open_file("sample.h5", "a", driver="H5FD_CORE", driver_core_backing_store=0) >>> import numpy >>> h5file.create_array(h5file.root, "new_array", numpy.arange(20), title="New array") >>> array2 = h5file.root.array2 >>> print(array2) /array2 (Array(20,)) 'New array' >>> h5file.close() Modifications are lost when the `h5file` descriptor is closed. Memory images of HDF5 files =========================== It is possible to get a memory image of an HDF5 file (see `HDF5 File Image Operations`_). This feature is only available if PyTables is build against version 1.8.9 or newer of the HDF5 library. In particular getting a memory image of an HDF5 file is possible only if the file has been opened with one of the following drivers: SEC2 (the default one), STDIO or CORE. An example of how to get an image:: >>> import tables >>> h5file = tables.open_file("sample.h5") >>> image = h5file.get_file_image() >>> h5file.close() The memory ìmage of the :file:`sample.h5` file is copied into the `ìmage` string (of bytes). .. note:: the `ìmage` string contains all data stored in the HDF5 file so, of course, it can be quite large. The `ìmage` string can be passed around and can also be used to initialize a new HDF55 file descriptor:: >>> import tables >>> h5file = tables.open_file("in-memory-sample.h5", driver="H5DF_CORE", driver_core_backing_store=0) >>> print(h5file.root.array) /array (Array(300, 300)) 'Array' >>> h5file.setNodeAttr(h5file.root, "description", "In memory file example") ----- .. target-notes:: .. _`HDF5 drivers`: http://www.hdfgroup.org/HDF5/doc/UG/08_TheFile.html#Drivers .. _`HDF5 User's Guide`: http://www.hdfgroup.org/HDF5/doc/UG/index.html .. _`HDF5 File Image Operations`: http://www.hdfgroup.org/HDF5/doc/Advanced/FileImageOperations/HDF5FileImageOperations.pdf PyTables-v.3.1.1/doc/source/cookbook/no_root_install.rst000066400000000000000000000137461231437614300233340ustar00rootroot00000000000000:source: http://www.pytables.org/moin/UserDocuments/InstallingPyTablesWhenNotRoot :revision: 50 :date: 2008-04-21 11:12:44 :author: localhost .. todo:: update to use new SW versions Installing PyTables when you're not root ======================================== By `Koen van de Sande `_. .. warning:: contents of this recipe recipe may be outdated. This guide describes how to install PyTables and its dependencies on Linux or other \*nix systems when your user account is not root. Installing the HDF5_ shared libraries and Python extensions NumArray and NumPy requires some non-trivial steps to work. We describe all steps needed. They only assumption is that you have Python 2.3 or higher and a C/C++ compiler (gcc) installed. Installing HDF5 --------------- * First go to or make a temporary folder where we can download and compile software. We'll assume you're in this temporary folder in the rest of this section. * Download `hdf5-1.6.5.tar.gz` from ftp://ftp.hdfgroup.org/HDF5/current16/src/:: wget ftp://ftp.hdfgroup.org/HDF5/current16/src/hdf5-1.6.5.tar.gz * Extract the archive to the current folder:: tar xzvf hdf5-1.6.5.tar.gz * Go to the extracted HDF5 folder:: cd hdf5-1.6.5 * Run the configure script:: ./configure * Run make:: make install * We've now compiled HDF5_ into the `hdf5` folder inside the source tree. We'll need to move this to its final location. For this guide, we'll make a `software` folder inside your home directory to store installed libraries:: mkdir ~/software * Move the files to the right location:: mv hdf5 ~/software/ Installing NumArray ------------------- * From the `NumArray SourceForge page `_ download NumArray 1.5.2 to our temporary folder. * Extract the archive:: tar xzvf numarray-1.5.2.tar.gz * Go to the NumArray folder:: cd numarray-1.5.2 * Build and install the Python module into our software folder (it will actually end up in `~/software/lib/python`:: python setup.py install --home=~/software We will also need to copy the header files of NumArray so PyTables can use them later on for compilation. Skipping this step will lead to compilation errors for PyTables. * Go into the header file folder:: cd include * Copy the header files. We'll put them together with the HDF5_ header files:: cp -r numarray ~/software/hdf5/include/ Installing NumPy (optional) --------------------------- It is not required to install NumPy; PyTables will work with just NumArray installed. However, I do recommend that you install NumPy as well, because PyTables can optionally use it. * From the `NumPy SourceForge page `_ download NumPy 1.0 (at time of writing) to our temporary folder. * Extract the archive:: tar xzvf numpy-1.0.tar.gz * Go to the NumPy folder:: cd numpy-1.0 * Build and install the Python module into our software folder:: python setup.py install --home=~/software Python wrapper script --------------------- We've installed all dependencies of PyTables. We need to create a wrapper script for Python to let PyTables actually find all these dependencies. Had we installed them as root, they'd be trivial to find, but now we need to help a bit. * Create a script with the following contents (I've called this script `p` on my machine):: #!/bin/bash export PYTHONPATH=~/software/lib/python export HDF5_DIR=~/software/hdf5 export LD_LIBRARY_PATH=~/software/lib/python/tables:~/software/hdf5/lib python $* * Make the script executable:: chmod 755 p * Place the script somewhere on your path (for example, inside a folder called `bin` inside your home dir, which is normally added to the path automatically). If you do not add this script to your path, you'll have to replace `p` in scripts below by the full path (and name of) your script, e.g. `~/pytablespython.sh` if you called it `pytablespython.sh` and put it in your home dir. * Test your Python wrapper script:: p * It should now start Python. And you should be able to import `numarray` (and optionally `numpy`) without errors:: Python 2.3.4 (#1, Feb 2 2005, 12:11:53) [GCC 3.4.2 20041017 (Red Hat 3.4.2-6.fc3)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import numarray >>> import numpy >>> .. note:: you could do this differently by defining these environment settings somewhere in your startup scripts, but this wrapper script approach is cleaner. Installing PyTables ------------------- * From the `SourceForge page `_ download PyTables 1.3.3 (at time of writing) to our temporary folder. * Extract the archive:: tar xzvf pytables-1.3.3.tar.gz * Go to the PyTables folder:: cd pytables-1.3.3 * Install PyTables using our wrapper script:: p setup.py install --home=~/software * If you get the following error then you are not using the wrapper script properly! :: .. ERROR:: Can't find a local numarray Python installation. Please, read carefully the ``README`` file and remember that PyTables needs the numarray package to compile and run.}}} Running Python with PyTables support ------------------------------------ * Use your Python wrapper script to start Python:: p * You can now import `tables` without errors:: Python 2.3.4 (#1, Feb 2 2005, 12:11:53) [GCC 3.4.2 20041017 (Red Hat 3.4.2-6.fc3)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import tables >>> tables.__version__ '1.3.3' >>> Concluding remarks ------------------ * It is safe to remove the temporary folder we have used in this guide, there are no dependencies on it. * This guide was written for and tested with HDF5 1.6.5, PyTables 1.3.3 and NumArray 1.5.2. Enjoy working with PyTables! *Koen* ----- .. target-notes:: .. _HDF5: http://www.hdfgroup.org/HDF5 PyTables-v.3.1.1/doc/source/cookbook/py2exe_howto.rst000066400000000000000000000047211231437614300225540ustar00rootroot00000000000000:source: http://www.pytables.org/moin/UserDocuments/PyTables%20%26%20py2exe :revision: 8 :date: 2008-04-21 11:12:45 :author: localhost .. todo:: update the code example to numpy ============================================================= How to integrate PyTables in your application by using py2exe ============================================================= This document shortly describes how to build an executable when using PyTables. Py2exe_ is a third party product that converts python scripts into standalone windows application/programs. For more information about py2exe please visit http://www.py2exe.org. To be able to use py2exe you have to download and install it. Please follow the instructions at http://www.py2exe.org. Let’s assume that you have written a python script as in the attachment :download:`py2exe_howto/pytables_test.py` .. literalinclude:: py2exe_howto/pytables_test.py :linenos: To wrap this script into an executable you have to create a setup script and a configuration script in your program directory. The setup script will look like this:: from distutils.core import setup import py2exe setup(console=['pytables_test.py']) The configuration script (:file:`setup.cfg`) specifies which modules to be included and excluded:: [py2exe] excludes= Tkconstants,Tkinter,tcl includes= encodings.*, tables.*, numarray.* As you can see I have included everything from tables (tables.*) and numarray (numarray.*). Now you are ready to build the executable file (:file:`pytable_test.exe`). During the build process a subfolder called *dist* will be created. This folder contains everything needed for your program. All dependencies (dll's and such stuff) will be copied into this folder. When you distribute your application you have to distribute all files and folders inside the *dist* folder. Below you can see how to start the build process (`python setup.py py2exe`):: c:pytables_test> python setup.py py2exe ... BUILDING EXECUTABLE ... After the build process I enter the *dist* folder and start :file:`pytables_test.exe`. :: c:pytables_test> cd dist c:pytables_testdist> pytables_test.exe tutorial.h5 (File) 'Test file' Last modif.: 'Tue Apr 04 23:09:17 2006' Object Tree: / (RootGroup) 'Test file' /detector (Group) 'Detector information' /detector/readout (Table(0,)) 'Readout example' [25.0, 36.0, 49.0] DONE! ----- .. target-notes:: .. _py2exe: http://www.py2exe.org PyTables-v.3.1.1/doc/source/cookbook/py2exe_howto/000077500000000000000000000000001231437614300220165ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/cookbook/py2exe_howto/pytables_test.py000066400000000000000000000024171231437614300252560ustar00rootroot00000000000000from tables import * import numarray class Particle(IsDescription): name = StringCol(16) # 16-character String idnumber = Int64Col() # Signed 64-bit integer ADCcount = UInt16Col() # Unsigned short integer TDCcount = UInt8Col() # Unsigned byte grid_i = Int32Col() # Integer grid_j = IntCol() # Integer (equivalent to Int32Col) pressure = Float32Col() # Float (single-precision) energy = FloatCol() # Double (double-precision) h5file = openFile("tutorial.h5", mode="w", title="Test file") group = h5file.createGroup("/", "detector", "Detector information") table = h5file.createTable(group, "readout", Particle, "Readout example") print h5file particle = table.row for i in xrange(10): particle['name'] = 'Particle: %6d' % i particle['TDCcount'] = i % 256 particle['ADCcount'] = (i*256) % (1<<16) particle['grid_i'] = i particle['grid_j'] = 10 - i particle['pressure'] = float(i*i) particle['energy'] = float(particle['pressure']**4) particle['idnumber'] = i * (2**34) particle.append() table.flush() table = h5file.root.detector.readout pressure = [x['pressure'] for x in table.iterrows() if x['TDCcount']>3 and 20<=x['pressure']<50] print pressure h5file.close() PyTables-v.3.1.1/doc/source/cookbook/simple_table.rst000066400000000000000000000105321231437614300225550ustar00rootroot00000000000000:source: http://www.pytables.org/moin/UserDocuments/SimpleTable :revision: 3 :date: 2010-04-20 16:44:41 :author: FrancescAlted =================================================== SimpleTable: simple wrapper around the Table object =================================================== Here it is yet another example on how to inherit from the :class:`tables.Table` object so as to build an easy-to-use Table object. Thanks to Brent Pedersen for this one (taken from https://pypi.python.org/pypi/simpletable). :: """ SimpleTable: simple wrapper around pytables hdf5 ------------------------------------------------------------------------------ Example Usage:: >>> from simpletable import SimpleTable >>> import tables # define the table as a subclass of simple table. >>> class ATable(SimpleTable): ... x = tables.Float32Col() ... y = tables.Float32Col() ... name = tables.StringCol(16) # instantiate with: args: filename, tablename >>> tbl = ATable('test_docs.h5', 'atable1') # insert as with pytables: >>> row = tbl.row >>> for i in range(50): ... row['x'], row['y'] = i, i * 10 ... row['name'] = "name_%i" % i ... row.append() >>> tbl.flush() # there is also insert_many() method() with takes an iterable # of dicts with keys matching the colunns (x, y, name) in this # case. # query the data (query() alias of tables' readWhere() >>> tbl.query('(x > 4) & (y < 70)') #doctest: +NORMALIZE_WHITESPACE array([('name_5', 5.0, 50.0), ('name_6', 6.0, 60.0)], dtype=[('name', '|S16'), ('x', ' 0 if verbose and are_open_files: sys.stderr.write("Closing remaining open files:") # make a copy of the open_files.handlers container for the iteration handlers = list(open_files.handlers) for fileh in handlers: if verbose: sys.stderr.write("%s..." % fileh.filename) fileh.close() if verbose: sys.stderr.write("done") if verbose and are_open_files: sys.stderr.write("\n") import sys, atexit atexit.register(my_close_open_files, False) then, you won't get the closing messages anymore because the new registered function is executed before the existing one. If you want the messages back again, just set the verbose parameter to true. You can also use the `atexit` hooks to perform other cleanup functions as well. PyTables-v.3.1.1/doc/source/dev_team.rst000066400000000000000000000006201231437614300200700ustar00rootroot00000000000000======================== PyTables Governance Team ======================== The PyTables team includes: * Francesc Alted * Ivan Vilata * Scott Prater * Vicent Mas * Tom Hedley * `Antonio Valentino`_ * Jeffrey Whitaker * `Josh Moore`_ * `Anthony Scopatz`_ .. _Anthony Scopatz: http://www.scopatz.com/ .. _Antonio Valentino: https://github.com/avalentino .. _Josh Moore: https://github.com/joshmoore PyTables-v.3.1.1/doc/source/development.rst000066400000000000000000000032261231437614300206330ustar00rootroot00000000000000==================== PyTables Development ==================== If you want to follow the development of PyTables and take part in it, you may have a look at the PyTables project pages on `GitHub `_. The source code for PyTables may be found at the `GitHub project site`_. You can get a copy of the latest version of the source code (under development) from the master branch of the project repository using git:: git clone git@github.com:PyTables/PyTables.git Also, be sure to subscribe to the `Users' Mailing List`_ and/or the `Developers' Mailing List`_. .. _`GitHub project site`: https://github.com/PyTables .. _`Users' Mailing List`: https://lists.sourceforge.net/lists/listinfo/pytables-users .. _`Developers' Mailing List`: https://groups.google.com/group/pytables-dev Other resources for developers: * `GitHub project site`_ * :ref:`library_reference` * `Git Repository browser `_ * `Issue tracker `_ * `Developers wiki `_ * `Users' Mailing List`_ * `Developers' Mailing List`_ * Continuous Integration: - `master `_ branch at OME_ - `develop `_ branch at `Shining Panda`_ - `all branches `_ on `Travis-CI`_ * `Old trac site `_ .. _OME: http://www.openmicroscopy.org .. _`Shining Panda`: https://www.shiningpanda-ci.com .. _`Travis-CI`: https://travis-ci.org .. toctree:: :maxdepth: 1 irclogs .. todo:: improve this section PyTables-v.3.1.1/doc/source/downloads.rst000066400000000000000000000037351231437614300203100ustar00rootroot00000000000000========= Downloads ========= Stable Versions --------------- The stable versions of PyTables can be downloaded from the file `download area`_ on SourceForge.net. The full distribution contains a copy of this documentation in HTML. The documentation in both HTML and PDF formats can also be downloaded separately from the same URL. A *pure source* version of the package (mainly intended for developers and packagers) is available on the `tags page`_ on GitHub. It contains all files under SCM but not the (generated) files, HTML doc and *cythonized* C extensions, so it is smaller that the standard package (about 3.5MB). Windows binaries can be obtained from many different distributions, like `Python(x,y)`_, ActiveState_, or Enthought_. In addition, Christoph Gohlke normally does an excellent job by providing binaries for many interesting software on his `website `_. You may be interested to easy_install the latest released stable version:: $ easy_install tables Or, you may prefer to install the stable version in Git repository instead using :program:`pip`. For example, for the stable 2.3 series, you can do:: $ pip install --install-option='--prefix=' \ -e git+https://github.com/PyTables/PyTables.git@v.2.3#egg=tables .. _`download area`: http://sourceforge.net/projects/pytables/files/pytables .. _`tags page`: https://github.com/PyTables/PyTables/tags .. _`Python(x,y)`: http://code.google.com/p/pythonxy .. _ActiveState: http://www.activestate.com/activepython .. _Enthought: https://www.enthought.com/products/epd Bleeding Edge Versions ---------------------- The latest, coolest, and possibly buggiest ;-) sources can be obtained from the new github repository: https://github.com/PyTables/PyTables A `snapshot `_ of the code in development is also available on the `GitHub project page`_. .. _`GitHub project page`: https://github.com/PyTables/PyTables PyTables-v.3.1.1/doc/source/images/000077500000000000000000000000001231437614300170215ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/images/favicon.ico000066400000000000000000000011471231437614300211450ustar00rootroot00000000000000‰PNG  IHDR‘h6bKGDÿÿÿ ½§“ pHYs©}F€tIME× *>AüQôIDAT(Ï•’_HSqÇ¿¿»œ­VkÁòÂÒrxײ"¢ ìMX a>õRQ=š />J¤ †=DDÂèMldÓ)–>H©H&D²µ²[©wsW[ëÞ­ýû‚ºŽŠürÞ_ΗÏù2"Âf$ü{imE" €ü€6нZås©ù5žôù8 ußÊðtsjÿE_¦‘Ezò&Úÿµ ÀlôRS­]‰ý°‹S“=Ã{ë˜Ó¶-E*øª ™øžyÇ ÿhCd±½¤€Ü'³I?ÈË?TΟ3\à:ùºèõtoÕéÙ3ùCs%ŠDS¼óðV%taW,™Lz½^QN½“~ï.<8Àí¤eÜ¢]ǨŠïá}ÏÛ"‘A"âœòïn/ TÏôX¼6ü4<TV£ãzihÌ6Ò× ªê/*Üo¤ (ˆÙnÔ7­¸w ¡ô0xDÐu¿#dY~ûÀIAÐCèã ¢ ‹]5—˦ËÓ_ image/svg+xml PyTables-v.3.1.1/doc/source/images/pytables-logo.svg000066400000000000000000000474771231437614300223460ustar00rootroot00000000000000 image/svg+xml PyTables-v.3.1.1/doc/source/index.rst000066400000000000000000000033011231437614300174120ustar00rootroot00000000000000=================================== Welcome to PyTable's documentation! =================================== PyTables is a package for managing hierarchical datasets and designed to efficiently and easily cope with extremely large amounts of data. You can download PyTables and use it for free. You can access documentation, some examples of use and presentations here. PyTables is built on top of the HDF5 library, using the Python language and the NumPy package. It features an object-oriented interface that, combined with C extensions for the performance-critical parts of the code (generated using Cython), makes it a fast, yet extremely easy to use tool for interactively browse, process and search very large amounts of data. One important feature of PyTables is that it optimizes memory and disk resources so that data takes much less space (specially if on-flight compression is used) than other solutions such as relational or object oriented databases. You can also, find more information by reading the PyTables :doc:`FAQ`. PyTables development is a continuing effort and we are always looking for more developers, testers, and users. If you are interested in being involved with this project, please contact us via `github`_ or the mailing list. -------- Contents -------- .. toctree:: :maxdepth: 1 User’s Guide Cookbook FAQ other_material Migrating from 2.x to 3.x downloads Release Notes project_pointers Development Development Team ============= Helpful Links ============= * :ref:`genindex` * :ref:`search` .. _github: https://github.com/PyTables/PyTables PyTables-v.3.1.1/doc/source/irclogs.rst000066400000000000000000000001471231437614300177520ustar00rootroot00000000000000================ Dev IRC Meetings ================ 2011 ---- * :irc:`2011/pytables.2011-08-29-20.25` PyTables-v.3.1.1/doc/source/other_material.rst000066400000000000000000000111701231437614300213050ustar00rootroot00000000000000============== Other Material ============== Videos ====== These are the videos of a series dedicated to introduce the main features of PyTables in a visual and easy to grasp manner. More videos will be made available with the time: * `HDF5 is for Lovers, SciPy 2012 Tutorial `_: a beginer's introduction to PyTables and HDF5. * `PyTables, part I: Introduction `_: HDF5 file creation, the object tree, homogeneous array storage, natural naming, working with attributes. * `PyTables, part II: Working with tables `_: Creation of tables with multidimensional and nested columns, and how to efficiently query them. Presentations ============= Here are the slides of some presentations about PyTables that you may find useful: * HDF5 is for Lovers, SciPy 2012 Tutorial, July 2012, Austin, TX, USA, `slides (pdf) `_, `video `_, `exercises `_, `solutions `_, and `repository `_. * `An on-disk binary data container `_. Talk given at the `Austin Python Meetup `_, Austin, TX, USA (May 2012). * `Large Data Analysis with Python `_. Seminar given at the `German Neuroinformatics Node `_, Munich, Germany (November 2010). * `Highly Efficient Computations In Python: Well Beyond NumPy `_. Tutorial given at `EuroSciPy 2010 `_ conference in Paris, France (July 2010). * `Starving CPUs (and coping with that in PyTables) `_. Seminar given at `FOM Institute for Plasma Physics Rijnhuizen `_, The Netherlands (September 2009). * `On The Data Access Issue (or Why Modern CPUs Are Starving) `_. Keynote presented at `EuroSciPy 2009 `_ conference in Leipzig, Germany (July 2009). * `An Overview of Future Improvements to OPSI `_. Informal talk given at the `THG headquarters `_ in Urbana-Champaign, Illinois, USA (October 2007). * `Finding Needles in a Huge DataStack `_. Talk given at the **EuroPython 2006 Conference**, held at CERN, Genève, Switzerland (July 2006). * `Presentation given at the "HDF Workshop 2005" `_, held at San Francisco, USA (December 2005). * `I `_ and `II `_ **Workshop in Free Software and Scientific Computing** given at the Universitat Jaume I, Castelló, Spain (October 2004). In Catalan. * `Presentation given at the "SciPy Workshop 2004" `_, held at Caltech, Pasadena, USA (September 2004). * `Slides `_ of presentation given at **EuroPython Conference** in Charleroi, Belgium (June 2003). * `Presentation for the "iParty5" `_ held at Castelló, Spain (May 2003). In Spanish. * `Talk `_ on PyTables given at the **PyCon 2003 Convention** held at Washington, USA (March 203). Reports ======= * White Paper on `OPSI indexes `_, explaining the powerful new indexing engine in PyTables Pro. * `Performance study `_ on how the new object tree cache introduced in PyTables 1.2 can accelerate the opening of files with a large number of objects, while being quite less memory hungry. * `Paper version `_ of the presentation at PyCon2003. Other sources for examples ========================== The examples presented above show just a little amount of the full capabilities of PyTables. Please check out the documentation and the :file:`examples/` directory in the source package for more examples. PyTables-v.3.1.1/doc/source/project_pointers.rst000066400000000000000000000023651231437614300217050ustar00rootroot00000000000000================ Project pointers ================ * `Project Home Page `_ * `GitHub Project Page `_ * `Online HTML Documentation `_ * `Download area `_ * `Git Repository browser `_ * `Users Mailing List `_ * `Announce Mailing List `_ * `Developers Mailing List `_ * Continuous Integration: - `OME `_ (master branch) - `Shining Panda `_ (development branch) - `Travis-CI `_ (all branches) * `Project page on PyPi `_ * `Project Page on SourceForge.net `_ (needs update) * `Project page on Launchpad `_ (going to be closed) * Development version of the `HTML documentation `_ * `Old trac site `_ PyTables-v.3.1.1/doc/source/release-notes/000077500000000000000000000000001231437614300203225ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v0.7.1.rst000066400000000000000000000030411231437614300240130ustar00rootroot00000000000000PyTables 0.7.1 is out! ---------------------- This is a mainly a bug-fixing release, where the next problems has been addressed: - Fixed several memory leaks. After that, the memory consumption when using large object trees has dropped sensibly. However, there remains some small leaks, but hopefully they are not very important unless you use *huge* object trees. - Fixed a bug that make the __getitem__ special method in table to fail when the stop parameter in a extended slice was not specified. That is, table[10:] now correctly returns table[10:table.nrows+1], and not table[10:11]. - The removeRows() method in Table did not update the NROWS attribute in Table objects, giving place to errors after doing further updating operations (removing or adding more rows) in the same table. This has been fixed now. Apart of these fixes, a new lazy reading algorithm for attributes has been activated by default. With that, the opening of objects with large hierarchies has been improved by 60% (you can obtain another additional 10% if using python 2.3 instead of python 2.2). The documentation has been updated as well, specially a more detailed instructions on the compression (zlib) libraries installation. Also, a stress test has been conducted in order to see if PyTables can *really* work not only with large data tables, but also with large object trees. In it, it has been generated and checked a file with more than 1 TB of size and more than 100 thousand tables on it!. See http://www.pytables.org/moin/StressTestsBck for details. PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v0.7.2.rst000066400000000000000000000023621231437614300240210ustar00rootroot00000000000000What's new in PyTables 0.7.2 ---------------------------- This is a mainly a maintenance release, where the next issues has been addressed: - Fixed a nasty memory leak located on the C libraries (It was occurring during attribute writes). After that, the memory consumption when using large object trees has dropped quite a bit. However, there remains some small leaks that has been tracked down to the underlying numarray library. These leaks has been reported, and hopefully they should be fixed more sooner than later. - Table buffers are built dinamically now, so if Tables are not accessed for reading or writing this memory will not be booked. This will help to reduce the memory consumption. - The opening of files with lots of nodes has been optimized between a factor 2 and 3. For example, a file with 10 groups and 3000 tables that takes 9.3 seconds to open in 0.7.1, now takes only 2.8 seconds. - The Table.read() method has been refactored and optimized and some parts of its code has been moved to Pyrex. In particular, in the special case of step=1, up to a factor 5 of speedup (reaching 160 MB/s on a Pentium4 @ 2 GHz) when reading table contents can be achieved now. Enjoy!, -- Francesc Alted falted@openlc.org PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v0.8.rst000066400000000000000000000144741231437614300236710ustar00rootroot00000000000000What's new in PyTables 0.8 ---------------------------- On this release, many enhancements has been added and some bugs has been fixed. Here is the (non-exhaustive) list: - The new VLArray class enables you to store large lists of rows containing variable numbers of elements. The elements can be scalars or fully multimensional objects, in the PyTables tradition. This class supports two special objects as rows: Unicode strings (UTF-8 codification is used internally) and generic Python objects (through the use of cPickle). - The new EArray class allows you to enlarge already existing multidimensional homogeneous data objects. Consider it an extension of the already existing Array class, but with more functionality. Online compression or other filters can be applied to EArray instances, for example. Another nice feature of EA's is their support for fully multidimensional data selection with extended slices. You can write "earray[1,2:3,...,4:200]", for example, to get the desired dataset slice from the disk. This is implemented using the powerful selection capabilities of the HDF5 library, which results in very highly efficient I/O operations. The same functionality has been added to Array objects as well. - New UnImplemented class. If a dataset contains unsupported datatypes, it will be associated with an UnImplemented instance, then inserted into to the object tree as usual. This allows you to continue to work with supported objects while retaining access to attributes of unsupported datasets. This has changed from previous versions, where a RuntimeError occurred when an unsupported object was encountered. The combination of the new UnImplemented class with the support for new datatypes will enable PyTables to greatly increase the number of types of native HDF5 files that can be read and modified. - Boolean support has been added for all the Leaf objects. - The Table class has now an append() method that allows you to save large buffers of data in one go (i.e. bypassing the Row accessor). This can greatly improve data gathering speed. - The standard HDF5 shuffle filter (to further enhance the compression level) is supported. - The standard HDF5 fletcher32 checksum filter is supported. - As the supported number of filters is growing (and may be further increased in the future), a Filters() class has been introduced to handle filters more easily. In order to add support for this class, it was necessary to make a change in the createTable() method that is not backwards compatible: the "compress" and "complib" parameters are deprecated now and the "filters" parameter should be used in their place. You will be able to continue using the old parameters (only a Deprecation warning will be issued) for the next few releases, but you should migrate to the new version as soon as possible. In general, you can easily migrate old code by substituting code in its place:: table = fileh.createTable(group, 'table', Test, '', complevel, complib) should be replaced by:: table = fileh.createTable(group, 'table', Test, '', Filters(complevel, complib)) - A copy() method that supports slicing and modification of filtering capabilities has been added for all the Leaf objects. See the User's Manual for more information. - A couple of new methods, namely copyFile() and copyChilds(), have been added to File class, to permit easy replication of complete hierarchies or sub-hierarchies, even to other files. You can change filters during the copy process as well. - Two new utilities has been added: ptdump and ptrepack. The utility ptdump allows the user to examine the contents of PyTables files (both metadata and actual data). The powerful ptrepack utility lets you selectively copy (portions of) hierarchies to specific locations in other files. It can be also used as an importer for generic HDF5 files. - The meaning of the stop parameter in read() methods has changed. Now a value of 'None' means the last row, and a value of 0 (zero) means the first row. This is more consistent with the range() function in python and the __getitem__() special method in numarray. - The method Table.removeRows() is no longer limited by table size. You can now delete rows regardless of the size of the table. - The "numarray" value has been added to the flavor parameter in the Table.read() method for completeness. - The attributes (.attr instance variable) are Python properties now. Access to their values is no longer lazy, i.e. you will be able to see both system or user attributes from the command line using the tab-completion capability of your python console (if enabled). - Documentation has been greatly improved to explain all the new functionality. In particular, the internal format of PyTables is now fully described. You can now build "native" PyTables files using any generic HDF5 software by just duplicating their format. - Many new tests have been added, not only to check new functionality but also to more stringently check existing functionality. There are more than 800 different tests now (and the number is increasing :). - PyTables has a new record in the data size that fits in one single file: more than 5 TB (yeah, more than 5000 GB), that accounts for 11 GB compressed, has been created on an AMD Opteron machine running Linux-64 (the 64 bits version of the Linux kernel). See the gory details in: http://pytables.sf.net/html/HowFast.html. - New platforms supported: PyTables has been compiled and tested under Linux32 (Intel), Linux64 (AMD Opteron and Alpha), Win32 (Intel), MacOSX (PowerPC), FreeBSD (Intel), Solaris (6, 7, 8 and 9 with UltraSparc), IRIX64 (IRIX 6.5 with R12000) and it probably works in many more architectures. In particular, release 0.8 is the first one that provides a relatively clean porting to 64-bit platforms. - As always, some bugs have been solved (especially bugs that occur when deleting and/or overwriting attributes). - And last, but definitely not least, a new donations section has been added to the PyTables web site (http://sourceforge.net/projects/pytables, then follow the "Donations" tag). If you like PyTables and want this effort to continue, please, donate! Enjoy!, -- Francesc Alted falted@pytables.org PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v0.9.1.rst000066400000000000000000000047761231437614300240350ustar00rootroot00000000000000What's new in PyTables 0.9.1 ---------------------------- This release is mainly a maintenance version. In it, some bugs has been fixed and a few improvements has been made. One important thing is that chunk sizes in EArrays has been re-tuned to get much better performance. Besides, it has been tested against the latest Python 2.4 and all unit tests seems to pass fine. More in detail: Improvements: - The chunksize computation for EArrays has been re-tuned to allow the compression rations that were usual before 0.9 release. - New --unpackshort and --quantize flags has been added to nctoh5 script. --unpackshort unpack short integer variables to float variables using scale_factor and add_offset netCDF variable attributes. --quantize quantize data to improve compression using least_significant_digit netCDF variable attribute (not active by default). See http://www.esrl.noaa.gov/psd/data/gridded/conventions/cdc_netcdf_standard.shtml for further explanation of what this attribute means. Thanks to Jeff Whitaker for providing this. - Table.itersequence has received a new parameter called "sort". This allows to disable the sorting of the sequence in case the user wants so. Backward-incompatible changes: - Now, the AttributeSet class throw an AttributeError on __getattr__ for nonexistent attributes in it. Formerly, the routine returned None, which is pretty much against convention in Python and breaks the built-in hasattr() function. Thanks to Robert Nemec for noting this and offering a patch. - VLArray.read() has changed its behaviour. Now, it always returns a list, as stated in documentation, even when the number of elements to return is 0 or 1. This is much more consistent when representing the actual number of elements on a certain VLArray row. API additions: - A Row.getTable() has been added. It is an accessor for the associated Table object. - A File.copyAttrs() has been added. It allows copying attributes from one leaf to other. Properly speaking, this was already there, but not documented :-/ Bug fixes: - Now, the copy of hierarchies works even when there are scalar Arrays (i.e. Arrays which shape is ()) on it. Thanks to Robert Nemec for providing a patch. - Solved a memory leak regarding the Filters instance associated with the File object, that was not released after closing the file. Now, there are no known leaks on PyTables itself. - Improved security of nodes name checking. Closes #1074335 Enjoy data!, -- Francesc Altet falted@pytables.org PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v0.9.rst000066400000000000000000000115161231437614300236640ustar00rootroot00000000000000What's new in PyTables 0.9 ========================== On this release you will find a series of quite exciting new features, being the most important the indexing capabilities, in-kernel selections, support for complex datatypes and the possibility to modify values in both tables *and* arrays (yeah, finally :). New features: ------------- - Indexing of columns in tables. That allow to make data selections on tables up to 500 times faster than standard selections (for ex. doing a selection along an indexed column of 100 milion of rows takes less than 1 second on a modern CPU). Perhaps the most interesting thing about the indexing algorithm implemented by PyTables is that the time taken to index grows *lineraly* with the length of the data, so, making the indexation process to be *scalable* (quite differently to many relational databases). This means that it can index, in a relatively quick way, arbitrarily large table columns (for ex. indexing a column of 100 milion of rows takes just 100 seconds, i.e. at a rate of 1 Mrow/sec). See more detailed info about that in http://www.pytables.org/docs/SciPy04.pdf. - In-kernel selections. This feature allow to make data selections on tables up to 5 times faster than standard selections (i.e. pre-0.9 selections), without a need to create an index. As a hint of how fast these selections can be, they are up to 10 times faster than a traditional relational database. Again, see http://www.pytables.org/docs/SciPy04.pdf for some experiments on that matter. - Support of complex datatypes for all the data objects (i.e. Table, Array, EArray and VLArray). With that, the complete set of datatypes of Numeric and numarray packages are supported. Thanks to Tom Hedley for providing the patches for Array, EArray and VLArray objects, as well as updating the User's Manual and adding unit tests for the new functionality. - Modification of values. You can modifiy Table, Array, EArray and VLArray values. See Table.modifyRows, Table.modifyColumns() and the newly introduced __setitem__() method for Table, Array, EArray and VLArray entities in the Library Reference of User's Manual. - A new sub-package called "nodes" is there. On it, there will be included different modules to make more easy working with different entities (like images, files, ...). The first module that has been added to this sub-package is "FileNode", whose mission is to enable the creation of a database of nodes which can be used like regular opened files in Python. In other words, you can store a set of files in a PyTables database, and read and write it as you would do with any other file in Python. Thanks to Ivan Vilata i Balaguer for contributing this. Improvements: ------------- - New __len__(self) methods added in Arrays, Tables and Columns. This, in combination with __getitem__(self,key) allows to better emulate sequences. - Better capabilities to import generic HDF5 files. In particular, Table objects (in the HDF5_HL naming schema) with "holes" in their compound type definition are supported. That allows to read certain files produced by NASA (thanks to Stephen Walton for reporting this). - Much improved test units. More than 2000 different tests has been implemented which accounts for more than 13000 loc (this represents twice of the PyTables library code itself (!)). Backward-incompatible API changes: ---------------------------------- - The __call__ special method has been removed from objects File, Group, Table, Array, EArray and VLArray. Now, you should use walkNodes() in File and Group and iterrows in Table, Array, EArray and VLArray to get the same functionality. This would provide better compatibility with IPython as well. 'nctoh5', a new importing utility: - Jeff Whitaker has contributed a script to easily convert NetCDF files into HDF5 files using Scientific Python and PyTables. It has been included and documented as a new utility. Bug fixes: ---------- - A call to File.flush() now invoke a call to H5Fflush() so to effectively flushing all the file contents to disk. Thanks to Shack Toms for reporting this and providing a patch. - SF #1054683: Security hole in utils.checkNameValidity(). Reported in 2004-10-26 by ivilata - SF #1049297: Suggestion: new method File.delAttrNode(). Reported in 2004-10-18 by ivilata - SF #1049285: Leak in AttributeSet.__delattr__(). Reported in 2004-10-18 by ivilata - SF #1014298: Wrong method call in examples/tutorial1-2.py. Reported in 2004-08-23 by ivilata - SF #1013202: Cryptic error appending to EArray on RO file. Reported in 2004-08-21 by ivilata - SF #991715: Table.read(field="var1", flavor="List") fails. Reported in 2004-07-15 by falted - SF #988547: Wrong file type assumption in File.__new__. Reported in 2004-07-10 by ivilata Bon profit!, -- Francesc Altet falted@pytables.org PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v1.0.rst000066400000000000000000000174731231437614300236640ustar00rootroot00000000000000============================ What's new in PyTables 1.0 ============================ :Author: Francesc Altet :Contact: faltet@carabos.com :Author: Ivan Vilata i Balaguer :Contact: ivilata@carabos.com This document details the modifications to PyTables since version 0.9.1. Its main purpose is help you ensure that your programs will be runnable when you switch from PyTables 0.9.1 to PyTables 1.0. API additions ============= - The new ``Table.col()`` method can be used to get a column from a table as a ``NumArray`` or ``CharArray`` object. This is preferred over the syntax ``table['colname']``. - The new ``Table.readCoordinates()`` method reads a set of rows given their indexes into an in-memory object. - The new ``Table.readAppend()`` method Append rows fullfilling the condition to a destination table. Backward-incompatible changes ============================= - Trying to open a nonexistent file or a file of unknown type raises ``IOError`` instead of ``RuntimeError``. Using an invalid mode raises ``ValueError`` instead of ``RuntimeError``. - Getting a child node from a closed group raises ``ValueError`` instead of ``RuntimeError``. - Running an action on the wrong type of node now (i.e. using ``file.listNodes()`` on a leaf) raises a ``TypeError`` instead of a ``NodeError``. - Removing a non-existing child now raises a ``NoSuchNodeError``, instead of doing nothing. - Removing a non-empty child group using ``del group.child`` fails with a ``NodeError`` instead of recursively doing the removal. This is because of the potential damage it may cause when used inadvertently. If a recursive behavior is needed, use the ``_f_remove()`` method of the child node. - The `recursive` flag of ``Group._f_walkNodes()`` is ``True`` by default now. Before it was ``False``. - Now, deleting and getting a non-existing attribute raises an ``AttributeError`` instead of a ``RuntimeError``. - Swapped last two arguments of ``File.copyAttrs()`` to match the other methods. Please use ``File.copyNodeAttrs()`` anyway. - Failing to infer the size of a string column raises ``ValueError`` instead of ``RuntimeError``. - Excessive table column name length and number of columns now raise ``ValueError`` instead of ``IndexError`` and ``NameError``. - Excessive table row length now raises ``ValueError`` instead of ``RuntimeError``. - ``table[integer]`` returns a ``numarray.records.Record`` object instead of a tuple. This was the original behavior before PyTables 0.9 and proved to be more consistent than the last one (tables do not have an explicit ordering of columns). - Specifying a nonexistent column in ``Table.read()`` raises a ``ValueError`` instead of a ``LookupError``. - When ``start >= stop`` an empty iterator is returned by ``Table.iterrows()`` instead of an empty ``RecArray``. Thanks to Ashley Walsh for noting this. - The interface of ``isHDF5File()`` and ``isPyTablesFile()`` file has been unified so that they both return true or false values on success and raise ``HDF5ExtError`` or errors. The true value in ``isPyTablesFile()`` is the format version string of the file. - ``Table.whereIndexed()`` and ``Table.whereInRange()`` are now *private* methods, since the ``Table.where()`` method is able to choose the most adequate option. - The global variables ``ExtVersion`` and ``HDF5Version`` have been renamed to ``extVersion`` and ``hdf5Version``, respectively. - ``whichLibVersion()`` returns ``None`` on querying unavailable libraries, and raises ``ValueError`` on unknown ones. The following modifications, though being (strictly speaking) modifications of the API, will most probably not cause compatibility problems (but your mileage may vary): - The default values for ``name`` and ``classname`` arguments in ``File.getNode()`` are now ``None``, although the empty string is still allowed for backwards compatibility. File hierarchy manipulation and attribute handling operations using those arguments have changed to reflect this. - Copy operations (``Group._f_copyChildren()``, ``File.copyChildren()``, ``File.copyNode()``...) do no longer return a tuple with the new node and statistics. Instead, they only return the new node, and statistics are collected via an optional keyword argument. - The ``copyFile()`` function in ``File.py`` has changed its signature from:: copyFile(srcfilename=None, dstfilename=None, title=None, filters=None, copyuserattrs=True, overwrite=False, stats=None) to:: copyFile(srcfilename, dstfilename, overwrite=False, **kwargs) Thus, the function allows the same options as ``File.copyFile()``. - The ``File.copyFile()`` method has changed its signature from:: copyFile(self, dstfilename=None, title=None, filters=None, copyuserattrs=1, overwrite=0, stats=None): to:: copyFile(self, dstfilename, overwrite=False, **kwargs) This enables this method to pass on arbitrary flags and options supported by copying methods of inner nodes in the hierarchy. - The ``File.copyChildren()`` method has changed its signature from:: copyChildren(self, wheresrc, wheredst, recursive=False, filters=None, copyuserattrs=True, start=0, stop=None, step=1, overwrite=False, stats=None) to:: copyChildren(self, srcgroup, dstgroup, overwrite=False, recursive=False, **kwargs): Thus, the function allows the same options as ``Group._f_copyChildren()``. - The ``Group._f_copyChildren()`` method has changed its signature from:: _f_copyChildren(self, where, recursive=False, filters=None, copyuserattrs=True, start=0, stop=None, step=1, overwrite=False, stats=None) to:: _f_copyChildren(self, dstgroup, overwrite=False, recursive=False, **kwargs) This enables this method to pass on arbitrary flags and options supported by copying methods of inner nodes in the group. - Renamed ``srcFilename`` and ``dstFilename`` arguments in ``copyFile()`` and ``File.copyFile()`` to ``srcfilename`` and ``dstfilename``, respectively. Renamed ``whereSrc`` and ``whereDst`` arguments in ``File.copyChildren()`` to ``wheresrc`` and ``wheredst``, respectively. Renamed ``dstNode`` argument in ``File.copyAttrs()`` to ``dstnode``. Tose arguments should be easier to type in interactive sessions (although 99% of the time it is not necessary to specify them). - Renamed ``object`` argument in ``EArray.append()`` to ``sequence``. - The ``rows`` argument in ``Table.append()`` is now compulsory. - The ``start`` argument in ``Table.removeRows()`` is now compulsory. API refinements =============== - The ``isHDF5()`` function has been deprecated in favor of ``isHDF5File()``. - Node attribute-handling methods in ``File`` have been renamed for a better coherence and understanding of their purpose: * ``getAttrNode()`` is now called ``getNodeAttr()`` * ``setAttrNode()`` is now called ``setNodeAttr()`` * ``delAttrNode()`` is now called ``delNodeAttr()`` * ``copyAttrs()`` is now called ``copyNodeAttrs()`` They keep their respective signatures, and the old versions still exist for backwards compatibility, though they issue a ``DeprecationWarning``. - Using ``VLArray.append()`` with multiple arguments is now deprecated for its ambiguity. You should put the arguments in a single sequence object (list, tuple, array...) and pass it as the only argument. - Using ``table['colname']`` is deprecated. Using ``table.col('colname')`` (with the new ``col()`` method) is preferred. Bug fixes (affecting API) ========================= - ``Table.iterrows()`` returns an empty iterator when no rows are selected, instead of returning ``None``. ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: text .. coding: utf-8 .. fill-column: 78 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v1.1.1.rst000066400000000000000000000023421231437614300240110ustar00rootroot00000000000000============================== What's new in PyTables 1.1.1 ============================== :Author: Francesc Altet :Contact: faltet@carabos.com :Author: Ivan Vilata i Balaguer :Contact: ivilata@carabos.com This document details the modifications to PyTables since version 1.0. Its main purpose is help you ensure that your programs will be runnable when you switch from PyTables 1.0 to PyTables 1.1.1. API additions ============= - None Backward-incompatible changes ============================= - ``Table.read()`` raises a ``KeyError`` instead of a ``ValueError`` when a nonexistent field name is specified, for consistency with other methods. The same goes for the ``col()`` method. - ``File.__contains__()`` returns a true value when it is asked for an existent node, be it visible or not. This is more consistent with ``Group.__contains__()``. API refinements =============== - Using ``table.cols['colname']`` is deprecated. The usage of ``table.cols._f_col('colname')`` (with the new ``Cols._f_col()`` method) is preferred. Bug fixes (affecting API) ========================= - None ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: text .. coding: utf-8 .. fill-column: 72 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v1.1.rst000066400000000000000000000023521231437614300236530ustar00rootroot00000000000000============================ What's new in PyTables 1.1 ============================ :Author: Francesc Altet :Contact: faltet@carabos.com :Author: Ivan Vilata i Balaguer :Contact: ivilata@carabos.com This document details the modifications to PyTables since version 1.0. Its main purpose is help you ensure that your programs will be runnable when you switch from PyTables 1.0 to PyTables 1.1. API additions ============= - something... Backward-incompatible changes ============================= - ``Table.read()`` raises a ``KeyError`` instead of a ``ValueError`` when a nonexistent field name is specified, for consistency with other methods. The same goes for the ``col()`` method. - ``File.__contains__()`` returns a true value when it is asked for an existent node, be it visible or not. This is more consistent with ``Group.__contains__()``. API refinements =============== - Using ``table.cols['colname']`` is deprecated. The usage of ``table.cols._f_col('colname')`` (with the new ``Cols._f_col()`` method) is preferred. Bug fixes (affecting API) ========================= - something... ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: text .. coding: utf-8 .. fill-column: 78 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v1.2.1.rst000066400000000000000000000014361231437614300240150ustar00rootroot00000000000000============================== What's new in PyTables 1.2.1 ============================== :Author: Francesc Altet :Contact: faltet@carabos.com :Author: Ivan Vilata i Balaguer :Contact: ivilata@carabos.com This document details the modifications to PyTables since version 1.2. Its main purpose is help you ensure that your programs will be runnable when you switch from PyTables 1.2 to PyTables 1.2.1. API additions ============= - None Backward-incompatible changes ============================= - None Deprecated features =================== - None API refinements =============== - None Bug fixes (affecting API) ========================= - None ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: text .. coding: utf-8 .. fill-column: 78 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v1.2.2.rst000066400000000000000000000014361231437614300240160ustar00rootroot00000000000000============================== What's new in PyTables 1.2.2 ============================== :Author: Francesc Altet :Contact: faltet@carabos.com :Author: Ivan Vilata i Balaguer :Contact: ivilata@carabos.com This document details the modifications to PyTables since version 1.2. Its main purpose is help you ensure that your programs will be runnable when you switch from PyTables 1.2 to PyTables 1.2.2. API additions ============= - None Backward-incompatible changes ============================= - None Deprecated features =================== - None API refinements =============== - None Bug fixes (affecting API) ========================= - None ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: text .. coding: utf-8 .. fill-column: 78 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v1.2.3.rst000066400000000000000000000014361231437614300240170ustar00rootroot00000000000000============================== What's new in PyTables 1.2.3 ============================== :Author: Francesc Altet :Contact: faltet@carabos.com :Author: Ivan Vilata i Balaguer :Contact: ivilata@carabos.com This document details the modifications to PyTables since version 1.2. Its main purpose is help you ensure that your programs will be runnable when you switch from PyTables 1.2 to PyTables 1.2.3. API additions ============= - None Backward-incompatible changes ============================= - None Deprecated features =================== - None API refinements =============== - None Bug fixes (affecting API) ========================= - None ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: text .. coding: utf-8 .. fill-column: 78 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v1.2.rst000066400000000000000000000065751231437614300236670ustar00rootroot00000000000000============================ What's new in PyTables 1.2 ============================ :Author: Francesc Altet :Contact: faltet@carabos.com :Author: Ivan Vilata i Balaguer :Contact: ivilata@carabos.com This document details the modifications to PyTables since version 1.1. Its main purpose is help you ensure that your programs will be runnable when you switch from PyTables 1.1 to PyTables 1.2. API additions ============= - The user is now allowed to set arbitrary Python (non-persistent) attributes on any instance of ``Node``. If the name matches that of a child node, the later will no longer be accessible via natural naming, but it will still be available via ``File.getNode()``, ``Group._f_getChild()`` and the group children dictionaries. Of course, this allows the user to overwrite internal (``^_[cfgv]_``) PyTables variables, but this is the way most Python packages work. - The new ``Group._f_getChild()`` method allows to get a child node (be it visible or not) by its name. This should be more intuitive that using ``getattr()`` or using the group children dictionaries. - The new ``File.isVisibleNode()``, ``Node._f_isVisible()`` and ``Leaf.isVisible()`` methods tell whether a node is visible or not, i.e. if the node will appear in listing operations such as ``Group._f_listNodes()``. Backward-incompatible changes ============================= - ``File.objects``, ``File.groups`` and ``File.leaves`` can no longer be used to iterate over all the nodes in the file. However, they still may be used to access any node by its path. - ``File.__contains__()`` returns a true value when it is asked for an existent node, be it visible or not. This is more consistent with ``Group.__contains__()``. - Using ``Group.__delattr__()`` to remove a child is no longer supported. Please use ``Group._f_remove()`` instead. - The ``indexprops`` attribute is now present on all ``Table`` instances, be they indexed or not. In the last case, it is ``None``. - Table.getWhereList() now has flavor parameter equal to "NumArray" by default, which is more consistent with other methods. Before, flavor defaulted to "List". - The ``extVersion`` variable does no longer exist. It did not make much sense either, since the canonical version of the whole PyTables package is that of ``__version__``. - The ``Row.nrow()`` has been converted into a property, so you have to replace any call to ``Row.nrow()`` into ``Row.nrow``. Deprecated features =================== - The ``objects``, ``groups`` and ``leaves`` mappings in ``File`` are retained only for compatibility purposes. Using ``File.getNode()`` is recommended to access nodes, ``File.__contains__()`` to check for node existence, and ``File.walkNodes()`` for iteration purposes. Using ``isinstance()`` and ``*isVisible*()`` methods is the preferred way of checking node type and visibility. Please note that the aforementioned mappings use the named methods internally, so the former have no special performance gains over the later. API refinements =============== - The ``isHDF5File()`` and ``isPyTablesFile()`` functions know how to handle nonexistent or unreadable files. An ``IOError`` is raised in those cases. Bug fixes (affecting API) ========================= - None ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: text .. coding: utf-8 .. fill-column: 78 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v1.3.1.rst000066400000000000000000000024401231437614300240120ustar00rootroot00000000000000============================== What's new in PyTables 1.3.1 ============================== :Author: Francesc Altet :Contact: faltet@carabos.com :Author: Ivan Vilata i Balaguer :Contact: ivilata@carabos.com This document details the modifications to PyTables since version 1.2. Its main purpose is help you ensure that your programs will be runnable when you switch from PyTables 1.2 to PyTables 1.3.1. API additions ============= - The Table.Cols accessor has received a new __setitem__() method that allows doing things like: table.cols[4] = record table.cols.x[4:1000:2] = array # homogeneous column table.cols.Info[4:1000:2] = recarray # nested column Backward-incompatible changes ============================= - None Deprecated features =================== - None API refinements =============== - Table.itersequence has changed the default value for 'sort' parameter. It is now False by default, as it is not clear if this actually accelerates the iterator, so it is better to let to the user doing the proper checks (if he is interested at all). Bug fixes (affecting API) ========================= - None ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: text .. coding: utf-8 .. fill-column: 78 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v1.3.2.rst000066400000000000000000000024211231437614300240120ustar00rootroot00000000000000============================== What's new in PyTables 1.3.2 ============================== :Author: Francesc Altet :Contact: faltet@carabos.com :Author: Ivan Vilata i Balaguer :Contact: ivilata@carabos.com This document details the modifications to PyTables since version 1.2. Its main purpose is help you ensure that your programs will be runnable when you switch from PyTables 1.2 to PyTables 1.3.2. API additions ============= - The ``Table.Cols`` accessor has received a new ``__setitem__()`` method that allows doing things like:: table.cols[4] = record table.cols.x[4:1000:2] = array # homogeneous column table.cols.Info[4:1000:2] = recarray # nested column Backward-incompatible changes ============================= - None Deprecated features =================== - None API refinements =============== - ``Table.itersequence()`` has changed the default value for the ``sort`` parameter. It is now false by default, as it is not clear if this actually accelerates the iterator, so it is better to let the user do the proper checks (if interested). Bug fixes (affecting API) ========================= - None ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: text .. coding: utf-8 .. fill-column: 78 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v1.3.3.rst000066400000000000000000000014371231437614300240210ustar00rootroot00000000000000============================== What's new in PyTables 1.3.3 ============================== :Author: Francesc Altet :Contact: faltet@carabos.com :Author: Ivan Vilata i Balaguer :Contact: ivilata@carabos.com This document details the modifications to PyTables since version 1.2. Its main purpose is help you ensure that your programs will be runnable when you switch from PyTables 1.2 to PyTables 1.3.3. API additions ============= - None Backward-incompatible changes ============================= - None Deprecated features =================== - None API refinements =============== - None Bug fixes (affecting API) ========================= - None ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: text .. coding: utf-8 .. fill-column: 78 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v1.3.rst000066400000000000000000000024301231437614300236520ustar00rootroot00000000000000============================ What's new in PyTables 1.3 ============================ :Author: Francesc Altet :Contact: faltet@carabos.com :Author: Ivan Vilata i Balaguer :Contact: ivilata@carabos.com This document details the modifications to PyTables since version 1.2. Its main purpose is help you ensure that your programs will be runnable when you switch from PyTables 1.2 to PyTables 1.3. API additions ============= - The Table.Cols accessor has received a new __setitem__() method that allows doing things like: table.cols[4] = record table.cols.x[4:1000:2] = array # homogeneous column table.cols.Info[4:1000:2] = recarray # nested column Backward-incompatible changes ============================= - None Deprecated features =================== - None API refinements =============== - Table.itersequence has changed the default value for 'sort' parameter. It is now False by default, as it is not clear if this actually accelerates the iterator, so it is better to let to the user doing the proper checks (if he is interested at all). Bug fixes (affecting API) ========================= - None ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: text .. coding: utf-8 .. fill-column: 78 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v1.4.rst000066400000000000000000000031621231437614300236560ustar00rootroot00000000000000============================ What's new in PyTables 1.4 ============================ :Author: Francesc Altet :Contact: faltet@carabos.com :Author: Ivan Vilata i Balaguer :Contact: ivilata@carabos.com This document details the modifications to PyTables since version 1.3. Its main purpose is help you ensure that your programs will be runnable when you switch from PyTables 1.3 to PyTables 1.4. API additions ============= - The ``Table.getWhereList()`` method has got a new ``sort`` parameter. The default now is to get the list of parameters unsorted. Set ``sort`` to True to get the old behaviour. We've done this to avoid unnecessary ordering of potentially large sets of coordinates. - Node creation, copying and moving operations have received a new optional `createparents` argument. When true, the necessary groups in the target path that don't exist at the time of running the operation are automatically created, so that the target group of the operation always exists. Backward-incompatible changes ============================= - None Deprecated features =================== - None API refinements =============== - ``Description._v_walk()`` has been renamed to ``_f_walk()``, since it is a public method, not a value. - ``Table.removeIndex()`` now accepts a column name in addition to an ``Index`` instance (the later is deprecated). This avoids the user having to retrieve the needed ``Index`` object. Bug fixes (affecting API) ========================= - None ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: text .. coding: utf-8 .. fill-column: 78 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v2.0.x-pro.rst000066400000000000000000000504571231437614300247300ustar00rootroot00000000000000=========================================== Release notes for PyTables Pro 2.0 series =========================================== :Author: Francesc Alted i Abad :Contact: faltet@pytables.com :Author: Ivan Vilata i Balaguer :Contact: ivan@selidor.net Changes from 2.0.3 to 2.0.4 =========================== - Selections in tables works now in threaded environments. The problem was in the Numexpr package -- the solution has been reported to the upstream authors too. Fixes #164. - PyTables had problems importing native HDF5 files with gaps in nested compound types. This has been solved. Fixes #173. - In order to prevent a bug existing in HDF5 1.6 series, the ``EArray.truncate()`` method refused to accept a 0 as parameter (i.e. truncate an existing EArray to have zero rows did not work). As this has been fixed in the recent HDF5 1.8 series, this limitation has been removed (but only if the user has one of these installed). Fixes #171. - Small fixes for allowing the test suite to pass when using the new NumPy 1.1. However, it remains a small issue with the way the new NumPy represents complex numbers. I'm not fixing that in the PyTables suite, as there are chances that this can be fixed in NumPy itself (see ticket #841). Changes from 2.0.2.1 to 2.0.3 ============================= - Replaced the algorithm for computing chunksizes by another that is more general and useful for a larger range of expected dataset sizes. The outcome of the new calculation is the same than before for dataset sizes <= 100 GB. For datasets between 100 GB <= size < 10 TB, larger values are returned. For sizes >= 10 TB a maximum value of 1 MB is always returned. - Added support for the latest 1.8.0 version of the HDF5 library. Fixes ticket #127. - PyTables compiles now against latest versions of Pyrex (0.9.6.4). For the first time, the extensions do compile without warnings! Fixes #159. - Numexpr module has been put in sync with the version in SciPy sandbox. - Added a couple of warnings in User's Guide so as to tell the user that it is not safe to use methods that can change the number of rows of a table in the middle of a row iterator. Fixes #153. - Fixed a problem when updating multidimensional cells using the Row.update() method in the middle of table iterators . Fixes #149. - Fixed a problem when using 64-bit indexes in 32-bit platforms. Solves ticket #148. - Table.indexFilters is working now as documented. However, as per ticket #155, its use is now deprecated (will be removed in 2.1). Fixes #155. Changes from 2.0.2 to 2.0.2.1 ============================= - Optimization added for avoid to unnecessarily update index columns that have not been modified in table update operations. Fixes #139. Changes from 2.0.1 to 2.0.2 =========================== - Fixed a critical bug that returned wrong results when doing repetitive queries affecting the last row part of indices. Fixes #60 of the private Trac of Carabos. - Added ``__enter__()`` and ``__exit__()`` methods to ``File``; fixes #113. With this, and if using Python 2.5 you can do things like: with tables.openFile("test.h5") as h5file: ... - Carefully preserve type when converting NumPy scalar to numarray; fixes #125. - Fixed a nasty bug that appeared when moving or renaming groups due to a bad interaction between ``Group._g_updateChildrenLocation()`` and the LRU cache. Solves #126. - Return 0 when no rows are given to ``Table.modifyRows()``; fixes #128. - Added an informative message when the ``nctoh5`` utility is run without the NetCDF interface of ScientificPython bening installed. - Now, a default representation of closed nodes is provided; fixes #129. Changes from 2.0 to 2.0.1 ========================= - The ``coords`` argument of ``Table.readCoords()`` was not checked for contiguousness, raising fatal errors when it was discontiguous. This has been fixed. - There is an inconsistency in the way used to specify the atom shape in ``Atom`` constructors. When the shape is specified as ``shape=()`` it means a scalar atom and when it is specified as ``shape=N`` it means an atom with ``shape=(N,)``. But when the shape is specified as ``shape=1`` (i.e. in the default case) then a scalar atom is obtained instead of an atom with ``shape=(1,)``. This is inconsistent and not the behavior that NumPy exhibits. Changing this will require a migration path which includes deprecating the old behaviour if we want to make the change happen before a new major version. The proposed path is: 1. In PyTables 2.0.1, we are changing the default value of the ``shape`` argument to ``()``, and issue a ``DeprecationWarning`` when someone uses ``shape=1`` stating that, for the time being, it is equivalent to ``()``, but in near future versions it will become equivalent to ``(1,)``, and recommending the user to pass ``shape=()`` if a scalar is desired. 2. In PyTables 2.1, we will remove the previous warning and take ``shape=N`` to mean ``shape=(N,)`` for any value of N. See ticket #96 for more info. - The info about the ``chunkshape`` attribute of a leaf is now printed in the ``__repr__()`` of chunked leaves (all except ``Array``). - After some scrupulous benchmarking job, the size of the I/O buffer for ``Table`` objects has been reduced to the minimum that allows maximum performance. This represents more than 10x of reduction in size for that buffer, which will benefit those programs dealing with many tables simultaneously (#109). - In the ``ptrepack`` utility, when ``--complevel`` and ``--shuffle`` were specified at the same time, the 'shuffle' filter was always set to 'off'. This has been fixed (#104). - An ugly bug related with the integrated Numexpr not being aware of all the variations of data arrangements in recarray objects has been fixed (#103). We should stress that the bug only affected the Numexpr version integrated in PyTables, and *not* the original one. - When passing a record array to a table at creation time, its real length is now used instead of the default value for ``expectedrows``. This allows for better performance (#97). - Added some workarounds so that NumPy scalars can be successfully converted to numarray objects. Fixes #98. - PyTables is now able to access table rows beyond 2**31 in 32-bit Python. The problem was a limitation of ``xrange`` and we have replaced it by a new ``lrange`` class written in Pyrex. Moreover, ``lrange`` has been made publicly accessible as a safe 64-bit replacement for ``xrange`` for 32-bit platforms users. Fixes #99. - If a group and a table are created in a function, and the table is accessed through the group, the table can be flushed now. Fixes #94. - It is now possible to directly assign a field in a nested record of a table using the natural naming notation (#93). Changes from 2.0rc2 to 2.0 ========================== - Added support for recognizing native HDF5 files with datasets compressed with szip compressor. - Fixed a problem when asking for the string representation (str()) of closed files. Fixes ticket #79. - Do not take LZO as available when its initialisation fails. - Fixed a glitch in ptrepack utility. When the user wants a copy of a group, and a group is *to be created* in destination, the attributes of the original group *are* copied. If it is *not to be created*, the attributes will *not be* copied. I think this should be what the user would expect most of the times. - Fixed the check for creating intermediate groups in ptrepack utility. Solves ticket #83. - Before, when reading a dataset with an unknown CLASS id, a warning was issued and the dataset mapped to ``UnImplemented``. This closed the door to have the opportunity to try to recognize the dataset and map it to a supported CLASS. Now, when a CLASS attribute is not recognized, an attempt to recognize its associated dataset is made. If it is recognized, the matching class is associated with the dataset. If it is not recognized, then a warning is issued and the dataset becomes mapped to ``UnImplemented``. - Always pass verbose and heavy values in the common test module to test(). Fixes ticket #85. - Now, the ``verbose`` and ``--heavy`` flag passed to test_all.py are honored. - All the DLL's of dependencies are included now in Windows binaries. This should allow for better portability of the binaries. - Fixed the description of Node._v_objectID that was misleading. Changes from 2.0rc1 to 2.0rc2 ============================= - The "Optimization tips" chapter of the User's Guide has been completely updated to adapt to PyTables 2.0 series. In particular, new benchmarks on the much improved indexed queries have been included; you will see that PyTables indexing is competitive (and sometimes much faster) than that of traditional relational databases. With this, the manual should be fairly finished for 2.0 final release. - Large refactoring done on the ``Row`` class. The most important change is that ``Table.row`` is now a single object. This allows to reuse the same ``Row`` instance even after ``Table.flush()`` calls, which can be convenient in many situations. - I/O buffers unified in the ``Row`` class. That allows for bigger savings in memory space whenever the ``Row`` extension is used. - Improved speed (up to a 70%) with unaligned column operations (a quite common scenario when dealing with ``Table`` objects) through the integrated Numexpr. In-kernel searches take advantage of this optimization. - Added ``VLUnicodeAtom`` for storing variable-length Unicode strings in ``VLArray`` objects regardless of encoding. Closes ticket #51. - Added support for ``time`` datatypes to be portable between big-endian and low-endian architectures. This feature is not currently supported natively by the HDF5 library, so the support for such conversion has been added in PyTables itself. Fixes #72. - Added slice arguments to ``Table.readWhere()`` and ``Table.getWhereList()``. Although API changes are frozen, this may still be seen as an inconsistency with other query methods. The patch is backwards-compatible anyway. - Added missing overwrite argument to ``File.renameNode()`` and ``Node._f_rename()``. Fixes ticket #66. - Calling ``tables.test()`` no longer exits the interpreter session. Fixes ticket #67. - Fix comparing strings where one is a prefix of the other in integrated Numexpr. Fixes ticket #76. - Added a check for avoiding an ugly HDF5 message when copying a file over itself (for both ``copyFile()`` and ``File.copyFile()``). Fixes ticket #73. - Corrected the appendix E, were it was said that PyTables doesn't support compounds of compounds (it does since version 1.2!). Changes from 2.0b2 to 2.0rc1 ============================ - The ``lastrow`` argument of ``Table.flushRowsToIndex()`` is no longer public. It was not documented, anyway. Fixes ticket #43. - Added a ``memlevel`` argument to ``Cols.createIndex()`` which allows the user to control the amount of memory required for creating an index. - Added ``blocksizes`` and ``opts`` arguments to ``Cols.createIndex()``, which allow the user to control the sizes of index datasets, and to specify different optimization levels for each index dataset, respectively. These are very low-level options meant only for experienced users. Normal users should stick to the higher-level ``memlevel`` and ``optlevel``. - Query tests have been tuned to exhaustively check the new parametrization of indexes. - A new algorithm has been implemented that better reduces the entropy of indexes. - The API Reference section of the User's Manual (and the matching docstrings) has been completely reviewed, expanded and corrected. This process has unveiled some errors and inconsistencies which have also been fixed. - Fixed ``VLArray.__getitem__()`` to behave as expected in Python when using slices, instead of following the semantics of PyTables' ``read()`` methods (e.g. reading just one element when no stop is provided). Fixes ticket #50. - Removed implicit UTF-8 encoding from ``VLArray`` data using ``vlstring`` atoms. Now a variable-length string is stored as is, which lets users use any encoding of their choice, or none of them. A ``vlunicode`` atom will probably be added to the next release so as to fix ticket #51. - Allow non-sequence objects to be passed to ``VLArray.append()`` when using an ``object`` atom. This was already possible in 1.x but stopped working when the old append syntax was dropped in 2.0. Fixes ticket #63. - Changed ``Cols.__len__()`` to return the number of rows of the table or nested column (instead of the number of fields), like its counterparts in ``Table`` and ``Column``. - Python scalars cached in ``AttributeSet`` instances are now kept as NumPy objects instead of Python ones, because they do become NumPy objects when retrieved from disk. Fixes ticket #59. - Avoid HDF5 error when appending an empty array to a ``Table`` (ticket #57) or ``EArray`` (ticket #49) dataset. - Fix wrong implementation of the top-level ``table.description._v_dflts`` map, which was also including the pathnames of columns inside nested columns. Fixes ticket #45. - Optimized the access to unaligned arrays in Numexpr between a 30% and a 70%. - Fixed a die-hard bug that caused the loading of groups while closing a file. This only showed with certain usage patterns of the LRU cache (e.g. the one caused by ``ManyNodesTestCase`` in ``test_indexes.py`` under Pro). - Avoid copious warnings about unused functions and variables when compiling Numexpr. - Several fixes to Numexpr expressions with all constant values. Fixed tickets #53, #54, #55, #58. Reported bugs to mainstream developers. - Solved an issue when trying to open one of the included test files in append mode on a system-wide installation by a normal user with no write privileges on it. The file isn't being modified anyway, so the test is skipped then. - Added a new benchmark to compare the I/O speed of ``Array`` and ``EArray`` objects with that of ``cPickle``. - The old ``Row.__call__()`` is no longer available as a public method. It was not documented, anyway. Fixes ticket #46. - ``Cols._f_close()`` is no longer public. Fixes ticket #47. - ``Attributes._f_close()`` is no longer public. Fixes ticket #52. - The undocumented ``Description.classdict`` attribute has been completely removed. Fixes ticket #44. Changes from 2.0b1 to 2.0b2 =========================== - A very exhaustive overhauling of the User's Manual is in process. The chapters 1 (Introduction), 2 (Installation), 3 (Tutorials) have been completed (and hopefully, the lines of code are easier to copy&paste now), while chapter 4 (API Reference) has been done up to (and including) the Table class. During this tedious (but critical in a library) overhauling work, we have tried hard to synchronize the text in the User's Guide with that which appears on the docstrings. - Removed the ``recursive`` argument in ``Group._f_walkNodes()``. Using it with a false value was redundant with ``Group._f_iterNodes()``. Fixes ticket #42. - Removed the ``coords`` argument from ``Table.read()``. It was undocumented and redundant with ``Table.readCoordinates()``. Fixes ticket #41. - Fixed the signature of ``Group.__iter__()`` (by removing its parameters). - Added new ``Table.coldescrs`` and ``Table.description._v_itemsize`` attributes. - Added a couple of new attributes for leaves: * ``nrowsinbuf``: the number of rows that fit in the internal buffers. * ``chunkshape``: the chunk size for chunked datasets. - Fixed setuptools so that making an egg out of the PyTables 2 package is possible now. - Added a new ``tables.restrict_flavors()`` function allowing to restrict available flavors to a given set. This can be useful e.g. if you want to force PyTables to get NumPy data out of an old, ``numarray``-flavored PyTables file even if the ``numarray`` package is installed. - Fixed a bug which caused filters of unavailable compression libraries to be loaded as using the default Zlib library, after issuing a warning. Added a new ``FiltersWarning`` and a ``Filters.copy()``. Changes from 1.4.x to 2.0b1 =========================== API additions ------------- - ``Column.createIndex()`` has received a couple of new parameters: ``optlevel`` and ``filters``. The first one sets the desired quality level of the index, while the second one allows the user to specify the filters for the index. - ``Table.indexprops`` has been split into ``Table.indexFilters`` and ``Table.autoIndex``. The later groups the functionality of the old ``auto`` and ``reindex``. - The new ``Table.colpathnames`` is a sequence which contains the full pathnames of all bottom-level columns in a table. This can be used to walk all ``Column`` objects in a table when used with ``Table.colinstances``. - The new ``Table.colinstances`` dictionary maps column pathnames to their associated ``Column`` or ``Cols`` object for simple or nested columns, respectively. This is similar to ``Table.cols._f_col()``, but faster. - ``Row`` has received a new ``Row.fetch_all_fields()`` method in order to return all the fields in the current row. This returns a NumPy void scalar for each call. - New ``tables.test(verbose=False, heavy=False)`` high level function for interactively running the complete test suite from the Python console. - Added a ``tables.print_versions()`` for easily getting the versions for all the software on which PyTables relies on. Backward-incompatible changes ----------------------------- - You can no longer mark a column for indexing in a ``Col`` declaration. The only way of creating an index for a column is to invoke the ``createIndex()`` method of the proper column object *after the table has been created*. - Now the ``Table.colnames`` attribute is just a list of the names of top-level columns in a table. You can still get something similar to the old structure by using ``Table.description._v_nestedNames``. See also the new ``Table.colpathnames`` attribute. - The ``File.objects``, ``File.leaves`` and ``File.groups`` dictionaries have been removed. If you still need this functionality, please use the ``File.getNode()`` and ``File.walkNodes()`` instead. - ``Table.removeIndex()`` is no longer available; to remove an index on a column, one must use the ``removeIndex()`` method of the associated ``Column`` instance. - ``Column.dirty`` is no longer available. If you want to check column index dirtiness, use ``Column.index.dirty``. - ``complib`` and ``complevel`` parameters have been removed from ``File.createTable()``, ``File.createEArray()``, ``File.createCArray()`` and ``File.createVLArray()``. They were already deprecated in PyTables 1.x. - The ``shape`` and ``atom`` parameters have been swapped in ``File.createCArray()``. This has been done to be consistent with ``Atom()`` definitions (i.e. type comes before and shape after). Deprecated features ------------------- - ``Node._v_rootgroup`` has been removed. Please use ``node._v_file.root`` instead. - The ``Node._f_isOpen()`` and ``Leaf.isOpen()`` methods have been removed. Please use the ``Node._v_isopen`` attribute instead (it is much faster). - The ``File.getAttrNode()``, ``File.setAttrNode()`` and ``File.delAttrNode()`` methods have been removed. Please use ``File.getNodeAttr()``, ``File.setNodeAttr()`` and ``File.delNodeAttr()`` instead. - ``File.copyAttrs()`` has been removed. Please use ``File.copyNodeAttrs()`` instead. - The ``table[colname]`` idiom is no longer supported. You can use ``table.cols._f_col(column)`` for doing the same. API refinements --------------- - ``File.createEArray()`` received a new ``shape`` parameter. This allows to not have to use the shape of the atom so as to set the shape of the underlying dataset on disk. - All the leaf constructors have received a new ``chunkshape`` parameter that allows specifying the chunk sizes of datasets on disk. - All ``File.create*()`` factories for ``Leaf`` nodes have received a new ``byteorder`` parameter that allows the user to specify the byteorder in which data will be written to disk (data in memory is now always handled in *native* order). ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 78 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v2.0.x.rst000066400000000000000000000456231231437614300241310ustar00rootroot00000000000000======================================= Release notes for PyTables 2.0 series ======================================= :Author: Francesc Alted i Abad :Contact: faltet@pytables.com :Author: Ivan Vilata i Balaguer :Contact: ivan@selidor.net Changes from 2.0.3 to 2.0.4 =========================== - Selections in tables works now in threaded environments. The problem was in the Numexpr package -- the solution has been reported to the upstream authors too. Fixes #164. - PyTables had problems importing native HDF5 files with gaps in nested compound types. This has been solved. Fixes #173. - In order to prevent a bug existing in HDF5 1.6 series, the ``EArray.truncate()`` method refused to accept a 0 as parameter (i.e. truncate an existing EArray to have zero rows did not work). As this has been fixed in the recent HDF5 1.8 series, this limitation has been removed (but only if the user has one of these installed). Fixes #171. - Small fixes for allowing the test suite to pass when using the new NumPy 1.1. However, it remains a small issue with the way the new NumPy represents complex numbers. I'm not fixing that in the PyTables suite, as there are chances that this can be fixed in NumPy itself (see ticket #841). Changes from 2.0.2 to 2.0.3 =========================== - Replaced the algorithm for computing chunksizes by another that is more general and useful for a larger range of expected dataset sizes. The outcome of the new calculation is the same than before for dataset sizes <= 100 GB. For datasets between 100 GB <= size < 10 TB, larger values are returned. For sizes >= 10 TB a maximum value of 1 MB is always returned. - Fixed a problem when updating multidimensional cells using the Row.update() method in the middle of table iterators . Fixes #149. - Added support for the latest 1.8.0 version of the HDF5 library. Fixes ticket #127. - PyTables compiles now against latest versions of Pyrex (0.9.6.4). For the first time, the extensions do compile without warnings! Fixes #159. - Numexpr module has been put in sync with the version in SciPy sandbox. - Added a couple of warnings in User's Guide so as to tell the user that it is not safe to use methods that can change the number of rows of a table in the middle of a row iterator. Fixes #153. Changes from 2.0.1 to 2.0.2 =========================== - Added ``__enter__()`` and ``__exit__()`` methods to ``File``; fixes #113. With this, and if using Python 2.5 you can do things like: with tables.openFile("test.h5") as h5file: ... - Carefully preserve type when converting NumPy scalar to numarray; fixes #125. - Fixed a nasty bug that appeared when moving or renaming groups due to a bad interaction between ``Group._g_updateChildrenLocation()`` and the LRU cache. Solves #126. - Return 0 when no rows are given to ``Table.modifyRows()``; fixes #128. - Added an informative message when the ``nctoh5`` utility is run without the NetCDF interface of ScientificPython bening installed. - Now, a default representation of closed nodes is provided; fixes #129. Changes from 2.0 to 2.0.1 ========================= - The ``coords`` argument of ``Table.readCoords()`` was not checked for contiguousness, raising fatal errors when it was discontiguous. This has been fixed. - There is an inconsistency in the way used to specify the atom shape in ``Atom`` constructors. When the shape is specified as ``shape=()`` it means a scalar atom and when it is specified as ``shape=N`` it means an atom with ``shape=(N,)``. But when the shape is specified as ``shape=1`` (i.e. in the default case) then a scalar atom is obtained instead of an atom with ``shape=(1,)``. This is inconsistent and not the behavior that NumPy exhibits. Changing this will require a migration path which includes deprecating the old behaviour if we want to make the change happen before a new major version. The proposed path is: 1. In PyTables 2.0.1, we are changing the default value of the ``shape`` argument to ``()``, and issue a ``DeprecationWarning`` when someone uses ``shape=1`` stating that, for the time being, it is equivalent to ``()``, but in near future versions it will become equivalent to ``(1,)``, and recommending the user to pass ``shape=()`` if a scalar is desired. 2. In PyTables 2.1, we will remove the previous warning and take ``shape=N`` to mean ``shape=(N,)`` for any value of N. See ticket #96 for more info. - The info about the ``chunkshape`` attribute of a leaf is now printed in the ``__repr__()`` of chunked leaves (all except ``Array``). - After some scrupulous benchmarking job, the size of the I/O buffer for ``Table`` objects has been reduced to the minimum that allows maximum performance. This represents more than 10x of reduction in size for that buffer, which will benefit those programs dealing with many tables simultaneously (#109). - In the ``ptrepack`` utility, when ``--complevel`` and ``--shuffle`` were specified at the same time, the 'shuffle' filter was always set to 'off'. This has been fixed (#104). - An ugly bug related with the integrated Numexpr not being aware of all the variations of data arrangements in recarray objects has been fixed (#103). We should stress that the bug only affected the Numexpr version integrated in PyTables, and *not* the original one. - When passing a record array to a table at creation time, its real length is now used instead of the default value for ``expectedrows``. This allows for better performance (#97). - Added some workarounds so that NumPy scalars can be successfully converted to numarray objects. Fixes #98. - PyTables is now able to access table rows beyond 2**31 in 32-bit Python. The problem was a limitation of ``xrange`` and we have replaced it by a new ``lrange`` class written in Pyrex. Moreover, ``lrange`` has been made publicly accessible as a safe 64-bit replacement for ``xrange`` for 32-bit platforms users. Fixes #99. - If a group and a table are created in a function, and the table is accessed through the group, the table can be flushed now. Fixes #94. - It is now possible to directly assign a field in a nested record of a table using the natural naming notation (#93). Changes from 2.0rc2 to 2.0 ========================== - Added support for recognizing native HDF5 files with datasets compressed with szip compressor. - Fixed a problem when asking for the string representation (str()) of closed files. Fixes ticket #79. - Do not take LZO as available when its initialisation fails. - Fixed a glitch in ptrepack utility. When the user wants a copy of a group, and a group is *to be created* in destination, the attributes of the original group *are* copied. If it is *not to be created*, the attributes will *not be* copied. I think this should be what the user would expect most of the times. - Fixed the check for creating intermediate groups in ptrepack utility. Solves ticket #83. - Before, when reading a dataset with an unknown CLASS id, a warning was issued and the dataset mapped to ``UnImplemented``. This closed the door to have the opportunity to try to recognize the dataset and map it to a supported CLASS. Now, when a CLASS attribute is not recognized, an attempt to recognize its associated dataset is made. If it is recognized, the matching class is associated with the dataset. If it is not recognized, then a warning is issued and the dataset becomes mapped to ``UnImplemented``. - Always pass verbose and heavy values in the common test module to test(). Fixes ticket #85. - Now, the ``verbose`` and ``--heavy`` flag passed to test_all.py are honored. - All the DLL's of dependencies are included now in Windows binaries. This should allow for better portability of the binaries. - Fixed the description of Node._v_objectID that was misleading. Changes from 2.0rc1 to 2.0rc2 ============================= - The "Optimization tips" chapter of the User's Guide has been completely updated to adapt to PyTables 2.0 series. In particular, new benchmarks on the much improved indexed queries have been included; you will see that PyTables indexing is competitive (and sometimes much faster) than that of traditional relational databases. With this, the manual should be fairly finished for 2.0 final release. - Large refactoring done on the ``Row`` class. The most important change is that ``Table.row`` is now a single object. This allows to reuse the same ``Row`` instance even after ``Table.flush()`` calls, which can be convenient in many situations. - I/O buffers unified in the ``Row`` class. That allows for bigger savings in memory space whenever the ``Row`` extension is used. - Improved speed (up to a 70%) with unaligned column operations (a quite common scenario when dealing with ``Table`` objects) through the integrated Numexpr. In-kernel searches take advantage of this optimization. - Added ``VLUnicodeAtom`` for storing variable-length Unicode strings in ``VLArray`` objects regardless of encoding. Closes ticket #51. - Added support for ``time`` datatypes to be portable between big-endian and low-endian architectures. This feature is not currently supported natively by the HDF5 library, so the support for such conversion has been added in PyTables itself. Fixes #72. - Added slice arguments to ``Table.readWhere()`` and ``Table.getWhereList()``. Although API changes are frozen, this may still be seen as an inconsistency with other query methods. The patch is backwards-compatible anyway. - Added missing overwrite argument to ``File.renameNode()`` and ``Node._f_rename()``. Fixes ticket #66. - Calling ``tables.test()`` no longer exits the interpreter session. Fixes ticket #67. - Fix comparing strings where one is a prefix of the other in integrated Numexpr. Fixes ticket #76. - Added a check for avoiding an ugly HDF5 message when copying a file over itself (for both ``copyFile()`` and ``File.copyFile()``). Fixes ticket #73. - Corrected the appendix E, were it was said that PyTables doesn't support compounds of compounds (it does since version 1.2!). Changes from 2.0b2 to 2.0rc1 ============================ - The API Reference section of the User's Manual (and the matching docstrings) has been completely reviewed, expanded and corrected. This process has unveiled some errors and inconsistencies which have also been fixed. - Fixed ``VLArray.__getitem__()`` to behave as expected in Python when using slices, instead of following the semantics of PyTables' ``read()`` methods (e.g. reading just one element when no stop is provided). Fixes ticket #50. - Removed implicit UTF-8 encoding from ``VLArray`` data using ``vlstring`` atoms. Now a variable-length string is stored as is, which lets users use any encoding of their choice, or none of them. A ``vlunicode`` atom will probably be added to the next release so as to fix ticket #51. - Allow non-sequence objects to be passed to ``VLArray.append()`` when using an ``object`` atom. This was already possible in 1.x but stopped working when the old append syntax was dropped in 2.0. Fixes ticket #63. - Changed ``Cols.__len__()`` to return the number of rows of the table or nested column (instead of the number of fields), like its counterparts in ``Table`` and ``Column``. - Python scalars cached in ``AttributeSet`` instances are now kept as NumPy objects instead of Python ones, because they do become NumPy objects when retrieved from disk. Fixes ticket #59. - Avoid HDF5 error when appending an empty array to a ``Table`` (ticket #57) or ``EArray`` (ticket #49) dataset. - Fix wrong implementation of the top-level ``table.description._v_dflts`` map, which was also including the pathnames of columns inside nested columns. Fixes ticket #45. - Optimized the access to unaligned arrays in Numexpr between a 30% and a 70%. - Fixed a die-hard bug that caused the loading of groups while closing a file. This only showed with certain usage patterns of the LRU cache (e.g. the one caused by ``ManyNodesTestCase`` in ``test_indexes.py`` under Pro). - Avoid copious warnings about unused functions and variables when compiling Numexpr. - Several fixes to Numexpr expressions with all constant values. Fixed tickets #53, #54, #55, #58. Reported bugs to mainstream developers. - Solved an issue when trying to open one of the included test files in append mode on a system-wide installation by a normal user with no write privileges on it. The file isn't being modified anyway, so the test is skipped then. - Added a new benchmark to compare the I/O speed of ``Array`` and ``EArray`` objects with that of ``cPickle``. - The old ``Row.__call__()`` is no longer available as a public method. It was not documented, anyway. Fixes ticket #46. - ``Cols._f_close()`` is no longer public. Fixes ticket #47. - ``Attributes._f_close()`` is no longer public. Fixes ticket #52. - The undocumented ``Description.classdict`` attribute has been completely removed. Fixes ticket #44. Changes from 2.0b1 to 2.0b2 =========================== - A very exhaustive overhauling of the User's Manual is in process. The chapters 1 (Introduction), 2 (Installation), 3 (Tutorials) have been completed (and hopefully, the lines of code are easier to copy&paste now), while chapter 4 (API Reference) has been done up to (and including) the Table class. During this tedious (but critical in a library) overhauling work, we have tried hard to synchronize the text in the User's Guide with that which appears on the docstrings. - Removed the ``recursive`` argument in ``Group._f_walkNodes()``. Using it with a false value was redundant with ``Group._f_iterNodes()``. Fixes ticket #42. - Removed the ``coords`` argument from ``Table.read()``. It was undocumented and redundant with ``Table.readCoordinates()``. Fixes ticket #41. - Fixed the signature of ``Group.__iter__()`` (by removing its parameters). - Added new ``Table.coldescrs`` and ``Table.description._v_itemsize`` attributes. - Added a couple of new attributes for leaves: * ``nrowsinbuf``: the number of rows that fit in the internal buffers. * ``chunkshape``: the chunk size for chunked datasets. - Fixed setuptools so that making an egg out of the PyTables 2 package is possible now. - Added a new ``tables.restrict_flavors()`` function allowing to restrict available flavors to a given set. This can be useful e.g. if you want to force PyTables to get NumPy data out of an old, ``numarray``-flavored PyTables file even if the ``numarray`` package is installed. - Fixed a bug which caused filters of unavailable compression libraries to be loaded as using the default Zlib library, after issuing a warning. Added a new ``FiltersWarning`` and a ``Filters.copy()``. Important changes from 1.4.x to 2.0 =================================== API additions ------------- - ``Column.createIndex()`` has received a couple of new parameters: ``optlevel`` and ``filters``. The first one sets the desired quality level of the index, while the second one allows the user to specify the filters for the index. - ``Table.indexprops`` has been split into ``Table.indexFilters`` and ``Table.autoIndex``. The later groups the functionality of the old ``auto`` and ``reindex``. - The new ``Table.colpathnames`` is a sequence which contains the full pathnames of all bottom-level columns in a table. This can be used to walk all ``Column`` objects in a table when used with ``Table.colinstances``. - The new ``Table.colinstances`` dictionary maps column pathnames to their associated ``Column`` or ``Cols`` object for simple or nested columns, respectively. This is similar to ``Table.cols._f_col()``, but faster. - ``Row`` has received a new ``Row.fetch_all_fields()`` method in order to return all the fields in the current row. This returns a NumPy void scalar for each call. - New ``tables.test(verbose=False, heavy=False)`` high level function for interactively running the complete test suite from the Python console. - Added a ``tables.print_versions()`` for easily getting the versions for all the software on which PyTables relies on. Backward-incompatible changes ----------------------------- - You can no longer mark a column for indexing in a ``Col`` declaration. The only way of creating an index for a column is to invoke the ``createIndex()`` method of the proper column object *after the table has been created*. - Now the ``Table.colnames`` attribute is just a list of the names of top-level columns in a table. You can still get something similar to the old structure by using ``Table.description._v_nestedNames``. See also the new ``Table.colpathnames`` attribute. - The ``File.objects``, ``File.leaves`` and ``File.groups`` dictionaries have been removed. If you still need this functionality, please use the ``File.getNode()`` and ``File.walkNodes()`` instead. - ``Table.removeIndex()`` is no longer available; to remove an index on a column, one must use the ``removeIndex()`` method of the associated ``Column`` instance. - ``Column.dirty`` is no longer available. If you want to check column index dirtiness, use ``Column.index.dirty``. - ``complib`` and ``complevel`` parameters have been removed from ``File.createTable()``, ``File.createEArray()``, ``File.createCArray()`` and ``File.createVLArray()``. They were already deprecated in PyTables 1.x. - The ``shape`` and ``atom`` parameters have been swapped in ``File.createCArray()``. This has been done to be consistent with ``Atom()`` definitions (i.e. type comes before and shape after). Deprecated features ------------------- - ``Node._v_rootgroup`` has been removed. Please use ``node._v_file.root`` instead. - The ``Node._f_isOpen()`` and ``Leaf.isOpen()`` methods have been removed. Please use the ``Node._v_isopen`` attribute instead (it is much faster). - The ``File.getAttrNode()``, ``File.setAttrNode()`` and ``File.delAttrNode()`` methods have been removed. Please use ``File.getNodeAttr()``, ``File.setNodeAttr()`` and ``File.delNodeAttr()`` instead. - ``File.copyAttrs()`` has been removed. Please use ``File.copyNodeAttrs()`` instead. - The ``table[colname]`` idiom is no longer supported. You can use ``table.cols._f_col(column)`` for doing the same. API refinements --------------- - ``File.createEArray()`` received a new ``shape`` parameter. This allows to not have to use the shape of the atom so as to set the shape of the underlying dataset on disk. - All the leaf constructors have received a new ``chunkshape`` parameter that allows specifying the chunk sizes of datasets on disk. - All ``File.create*()`` factories for ``Leaf`` nodes have received a new ``byteorder`` parameter that allows the user to specify the byteorder in which data will be written to disk (data in memory is now always handled in *native* order). ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 78 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v2.1.x-pro.rst000066400000000000000000000067421231437614300247270ustar00rootroot00000000000000======================================= Release notes for PyTables 2.1 series ======================================= :Author: Francesc Alted i Abad :Contact: faltet@pytables.org Changes from 2.1.1 to 2.1.2 =========================== Bug fixes --------- - Solved problems with Table.modifyColumn() when the column(s) is multidimensional. Fixes #228. - The row attribute of a table seems stalled after a table move or rename. Fixes #224. - Fixed a problem with ``len(array)`` in 32-bit platforms when array is large enough (> 2**31). - Added missing `_c_classId` attribute to the `UnImplemented` class. ``ptrepack`` no longer chokes while copying `Unimplemented` classes. - The ``FIELD_*`` sys attrs are no longer copied when the ``PYTABLES_SYS_ATTRS`` parameter is set to false. - The ``FILTERS`` attribute is not added anymore when ``PYTABLES_SYS_ATTR`` parameter is set to false. - Disable the printing of Unicode characters that cannot be printed on win32 platform. Fixes #235. Other changes ------------- - When retrieving a row of a 1-dimensional array, a 0-dim array was returned instead of a numpy scalar. Now, an actuall numpy scalar is returned. Closes #222. - LZO and bzip2 filters adapted to an API fix introduced in HDF5 1.8.3. Closes #225. - Unsupported HDF5 types in attributes are no longer transferred during copies. A new `_v_unimplemented` list have been added in `AttributeSet` class so as to keep track of such attributes. Closes #240. - LZO binaries have disappeared from the GnuWin32 repository. Until they come eventually back, they have been put at http://www.pytables.org/download/lzo-win. This has been documented in the install chapter. Changes from 2.1 to 2.1.1 ========================= Bug fixes --------- - Fixed a memory leak when a lot of queries were made. Closes #203 and #207. - The chunkshape="auto" parameter value of `Leaf.copy()` is honored now, even when the (start, stop, step) parameters are specified. Closes #204. - Due to a flaw in its design, the `File` class was not able to be subclassed. This has been fixed. Closes #205. - Default values were not correctly retrieved when opening already created CArray/EArray objects. Fixed. Closes #212. - Fixed a problem with the installation of the ``nctoh5`` script that prevented it from being executed. Closes #215. - [Pro] The ``iterseq`` cache ignored non-indexed conditions, giving wrong results when those appeared in condition expressions. This has been fixed. Closes #206. Other changes ------------- - `openFile()`, `isHDF5File()` and `isPyTablesFile()` functions accept Unicode filenames now. Closes #202 and #214. - When creating large type sizes (exceeding 64 KB), HDF5 complained and refused to do so. The HDF5 team has logged the issue as a bug, but meanwhile it has been implemented a workaround in PyTables that allows to create such large datatypes for situations that does not require defaults other than zero. Addresses #211. - In order to be consistent with how are stored the other data types, Unicode attributes are retrieved now as NumPy scalars instead of Python Unicode strings or NumPy arrays. For the moment, I've fixed this through pickling the Unicode strings. In the future, when HDF5 1.8.x series would be a requirement, that should be done via a HDF5 native Unicode type. Closes #213. ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 72 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v2.1.x.rst000066400000000000000000000067421231437614300241310ustar00rootroot00000000000000======================================= Release notes for PyTables 2.1 series ======================================= :Author: Francesc Alted i Abad :Contact: faltet@pytables.org Changes from 2.1.1 to 2.1.2 =========================== Bug fixes --------- - Solved problems with Table.modifyColumn() when the column(s) is multidimensional. Fixes #228. - The row attribute of a table seems stalled after a table move or rename. Fixes #224. - Fixed a problem with ``len(array)`` in 32-bit platforms when array is large enough (> 2**31). - Added missing `_c_classId` attribute to the `UnImplemented` class. ``ptrepack`` no longer chokes while copying `Unimplemented` classes. - The ``FIELD_*`` sys attrs are no longer copied when the ``PYTABLES_SYS_ATTRS`` parameter is set to false. - The ``FILTERS`` attribute is not added anymore when ``PYTABLES_SYS_ATTR`` parameter is set to false. - Disable the printing of Unicode characters that cannot be printed on win32 platform. Fixes #235. Other changes ------------- - When retrieving a row of a 1-dimensional array, a 0-dim array was returned instead of a numpy scalar. Now, an actuall numpy scalar is returned. Closes #222. - LZO and bzip2 filters adapted to an API fix introduced in HDF5 1.8.3. Closes #225. - Unsupported HDF5 types in attributes are no longer transferred during copies. A new `_v_unimplemented` list have been added in `AttributeSet` class so as to keep track of such attributes. Closes #240. - LZO binaries have disappeared from the GnuWin32 repository. Until they come eventually back, they have been put at http://www.pytables.org/download/lzo-win. This has been documented in the install chapter. Changes from 2.1 to 2.1.1 ========================= Bug fixes --------- - Fixed a memory leak when a lot of queries were made. Closes #203 and #207. - The chunkshape="auto" parameter value of `Leaf.copy()` is honored now, even when the (start, stop, step) parameters are specified. Closes #204. - Due to a flaw in its design, the `File` class was not able to be subclassed. This has been fixed. Closes #205. - Default values were not correctly retrieved when opening already created CArray/EArray objects. Fixed. Closes #212. - Fixed a problem with the installation of the ``nctoh5`` script that prevented it from being executed. Closes #215. - [Pro] The ``iterseq`` cache ignored non-indexed conditions, giving wrong results when those appeared in condition expressions. This has been fixed. Closes #206. Other changes ------------- - `openFile()`, `isHDF5File()` and `isPyTablesFile()` functions accept Unicode filenames now. Closes #202 and #214. - When creating large type sizes (exceeding 64 KB), HDF5 complained and refused to do so. The HDF5 team has logged the issue as a bug, but meanwhile it has been implemented a workaround in PyTables that allows to create such large datatypes for situations that does not require defaults other than zero. Addresses #211. - In order to be consistent with how are stored the other data types, Unicode attributes are retrieved now as NumPy scalars instead of Python Unicode strings or NumPy arrays. For the moment, I've fixed this through pickling the Unicode strings. In the future, when HDF5 1.8.x series would be a requirement, that should be done via a HDF5 native Unicode type. Closes #213. ---- **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 72 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v2.2.x-pro.rst000066400000000000000000000362601231437614300247260ustar00rootroot00000000000000======================================= Release notes for PyTables 2.2 series ======================================= :Author: Francesc Alted i Abad :Contact: faltet@pytables.org Changes from 2.2.1rc1 to 2.2.1 ============================== - The `Row` accessor implements a new `__contains__` special method that allows doing things like:: for row in table: if item in row: print "Value found in row", row.nrow break Closes #309. - PyTables is more friendly with easy_install and pip now, as all the Python dependencies should be installed automatically. Closes #298. Changes from 2.2 to 2.2.1rc1 ============================ - When using `ObjectAtom` objects in `VLArrays` the ``HIGHEST_PROTOCOL`` is used for pickling objects. For NumPy arrays, this simple change leads to space savings up to 3x and time improvements up to 30x. Closes #301. - The `Row` accessor implements a new `__contains__` special method that allows doing things like:: for row in table: if item in row: print "Value found in row", row.nrow break Closes #309. - tables.Expr can perform operations on scalars now. Thanks to Gaëtan de Menten for providing a patch for this. Closes #287. - Fixed a problem with indexes larger than 32-bit on leaf objects on 32-bit machines. Fixes #283. - Merged in Blosc 1.1.2 for fixing a problem with large datatypes and subprocess issues. Closes #288 and #295. - Due to the adoption of Blosc 1.1.2, the pthreads-win32 library dependency is dropped on Windows platforms. - Fixed a problem with tables.Expr and operands with vary large rowsizes. Closes #300. - ``leaf[numpy.array[scalar]]`` idiom returns a NumPy array instead of an scalar. This has been done for compatibility with NumPy. Closes #303. - Optimization for `Table.copy()` so that ``FIELD_*`` attrs are not overwritten during the copy. This can lead to speed-ups up to 100x for short tables that have hundreds of columns. Closes #304. - For external links, its relative paths are resolved now with respect to the directory of the main HDF5 file, rather than with respect to the current directory. Closes #306. - ``Expr.setInputsRange()`` and ``Expr.setOutputRange()`` do support ``numpy.integer`` types now. Closes #285. - Column names in tables can start with '__' now. Closes #291. - Unicode empty strings are supported now as atributes. Addresses #307. - Cython 0.13 and higher is supported now. Fixes #293. - PyTables should be more 'easy_install'-able now. Addresses #298. Changes from 2.2rc2 to 2.2 (final) ================================== - Updated Blosc to 1.0 (final). - Filter ID of Blosc changed from wrong 32010 to reserved 32001. This will prevent PyTables 2.2 (final) to read files created with Blosc and PyTables 2.2 pre-final. `ptrepack` can be used to retrieve those files, if necessary. More info in ticket #281. - Recent benchmarks suggest a new parametrization is better in most scenarios: * The default chunksize has been doubled for every dataset size. This works better in most of scenarios, specially with the new Blosc compressor. * The HDF5 CHUNK_CACHE_SIZE parameter has been raised to 2 MB in order to better adapt to the chunksize increase. This provides better hit ratio (at the cost of consuming more memory). Some plots have been added to the User's Manual (chapter 5) showing how the new parametrization works. Changes from 2.2rc1 to 2.2rc2 ============================= - A new version of Blosc (0.9.5) is included. This version is now considered to be stable and apt for production. Thanks for all PyTables users that have contributed to find and report bugs. - Added a new `IO_BUFFER_SIZE` parameter to ``tables/parameters.py`` that allows to set the internal PyTables' buffer for doing I/O. This replaces `CHUNKTIMES` but it is more general because it affects to all `Leaf` objects and also the `tables.Expr` module (and not only tables as before). - `BUFFERTIMES` parameter in ``tables/parameters.py`` has been renamed to `BUFFER_TIMES` which is more consistent with other parameter names. - On Windows platforms, the path to the tables module is now appended to sys.path and the PATH environment variable. That way DLLs and PYDs in the tables directory are to be found now. Thanks to Christoph Gohlke for the hint. - A replacement for barriers for Mac OSX, or other systems not implementing them, has been carried out. This allows to compile PyTables on such platforms. Fixes #278 - Fixed a couple of warts that raise compatibility warnings with forthcoming Python 2.7. - HDF5 1.8.5 is used in Windows binaries. Changes from 2.2b3 to 2.2rc1 ============================ - Numexpr is not included anymore in PyTables and has become a requisite instead. This is because Numexpr already has decent enough installers and is available in the PyPI repository also, so it should be easy for users to fulfill this dependency. - When using a Numexpr package that is turbo-loaded with Intel's VML/MKL, the parameter `MAX_THREADS` will control the number of threads that VML can use during computations. For a finer control, the `numexpr.set_vml_num_threads()` can always be used. - Cython is used now instead of Pyrex for Pyrex extensions. - Updated to 0.9 version of Blosc compressor. This version can make use of threads so as to accelerate the compression/decompression process. In order to change the maximum number of threads that Blosc can use (2 by default), you can modify the `MAX_THREADS` variable in ``tables/parameters.py`` or make use of the new `setBloscMaxThreads()` global function. - Reopening already opened files is supported now, provided that there is not incompatibility among intended usages (for example, you cannot reopen in append mode an already opened file in read-only mode). - Option ``--print-versions`` for ``test_all.py`` script is now preferred over the deprecated ``--show-versions``. This is more consistent with the existing `print_versions()` function. - Fixed a bug that, under some circumstances, prevented the use of table iterators in `itertool.groupby()`. Now, you can safely do things like:: sel_rows = table.where('(row_id >= 3)') for group_id, grouped_rows in itertools.groupby(sel_rows, f_group): group_mean = average([row['row_id'] for row in grouped_rows]) Fixes #264. - Copies of `Array` objects with multidimensional atoms (coming from native HDF5 files) work correctly now (i.e. the copy holds the atom dimensionality). Fixes #275. - The `tables.openFile()` function does not try anymore to open/close the file in order to guess whether it is a HDF5 or PyTables one before opening it definitely. This allows the `fcntl.flock()` and `fcntl.lockf()` Python functions to work correctly now (that's useful for arbitrating access to the file by different processes). Thanks to Dag Sverre Seljebotn and Ivan Vilata for their suggestions on hunting this one! Fixes #185. - The estimation of the chunksize when using multidimensional atoms in EArray/Carray was wrong because it did not take in account the shape of the atom. Thanks to Ralf Juengling for reporting. Fixes #273. - Non-contiguous arrays can now safely be saved as attributes. Before, if arrays were not contiguous, incorrect data was saved in attr. Fixes #270. - EXTDIM attribute for CArray/EArray now saves the correct extendeable dimension, instead of rubbish. This does not affected functionality, because extendeable dimension was retrieved directly from shape information, but it was providing misleading information to the user. Fixes #268. API changes ----------- - Now, `Table.Cols.__len__()` returns the number of top level columns instead of the number of rows in table. This is more consistent in that `Table.Cols` is an accessor for *columns*. Fixes #276. Changes from 2.2b2 to 2.2b3 =========================== - Blosc compressor has been added as an additional filter, in addition to the existing Zlib, LZO and bzip2. This new compressor is meant for fast compression and extremely fast decompression. Fixes #265. - In `File.copyFile()` method, `copyuserattrs` was set to false as default. This was unconsistent with other methods where the default value for `copyuserattrs` is true. The default for this is true now. Closes #261. - `tables.copyFile` and `File.copyFile` recognize now the parameters present in ``tables/parameters.py``. Fixes #262. - Backported fix for issue #25 in Numexpr (OP_NEG_LL treats the argument as an int, not a long long). Thanks to David Cooke for this. - CHUNK_CACHE_NELMTS in `tables/paramters.py` set to a prime number as Neil Fortner suggested. - Workaround for a problem in Python 2.6.4 (and probably other versions too) for pickling strings like "0" or "0.". Fixes #253. Changes from 2.2b1 to 2.2b2 =========================== Enhancements ------------ - Support for HDF5 hard links, soft links and external links (when PyTables is compiled against HDF5 1.8.x series). A new tutorial about its usage has been added to the 'Tutorials' chapter of User's Manual. Closes #239 and #247. - Added support for setting HDF5 chunk cache parameters in file opening/creating time. 'CHUNK_CACHE_NELMTS', 'CHUNK_CACHE_PREEMPT' and 'CHUNK_CACHE_SIZE' are the new parameters. See "PyTables' parameter files" appendix in User's Manual for more info. Closes #221. - New `Unknown` class added so that objects that HDF5 identifies as ``H5G_UNKNOWN`` can be mapped to it and continue operations gracefully. - Optimization in the indexed queries when the resulting rows increase monotonically. From 3x (for medium-size query results) and 10x (for very large query results) speed-ups can be expected. - Added flag `--dont-create-sysattrs` to ``ptrepack`` so as to not create sys attrs (default is to do it). - Support for native compound types in attributes. This allows for better compatibility with HDF5 files. Closes #208. - Support for native NumPy dtype in the description parameter of `File.createTable()`. Closes #238. Bugs fixed ---------- - Added missing `_c_classId` attribute to the `UnImplemented` class. ``ptrepack`` no longer chokes while copying `Unimplemented` classes. - The ``FIELD_*`` sys attrs are no longer copied when the ``PYTABLES_SYS_ATTRS`` parameter is set to false. - `File.createTable()` no longer segfaults if description=None. Closes #248. - Workaround for avoiding a Python issue causing a segfault when saving and then retrieving a string attribute with values "0" or "0.". Closes #253. API changes ----------- - `Row.__contains__()` disabled because it has little sense to query for a key in Row, and the correct way should be to query for it in `Table.colnames` or `Table.colpathnames` better. Closes #241. - [Semantic change] To avoid a common pitfall when asking for the string representation of a `Row` class, `Row.__str__()` has been redefined. Now, it prints something like:: >>> for row in table: ... print row ... /newgroup/table.row (Row), pointing to row #0 /newgroup/table.row (Row), pointing to row #1 /newgroup/table.row (Row), pointing to row #2 instead of:: >>> for row in table: ... print row ... ('Particle: 0', 0, 10, 0.0, 0.0) ('Particle: 1', 1, 9, 1.0, 1.0) ('Particle: 2', 2, 8, 4.0, 4.0) Use `print row[:]` idiom if you want to reproduce the old behaviour. Closes #252. Other changes ------------- - After some improvements in both HDF5 and PyTables, the limit before emitting a `PerformanceWarning` on the number of children in a group has been raised from 4096 to 16384. Changes from 2.1.1 to 2.2b1 =========================== Enhancements ------------ - Added `Expr`, a class for evaluating expressions containing array-like objects. It can evaluate expressions (like '3*a+4*b') that operate on arbitrary large arrays while optimizing the resources (basically main memory and CPU cache memory) required to perform them. It is similar to the Numexpr package, but in addition to NumPy objects, it also accepts disk-based homogeneous arrays, like the `Array`, `CArray`, `EArray` and `Column` PyTables objects. - Added support for NumPy's extended slicing in all `Leaf` objects. With that, you can do the next sort of selections:: array1 = array[4] # simple selection array2 = array[4:1000:2] # slice selection array3 = array[1, ..., ::2, 1:4, 4:] # general slice selection array4 = array[1, [1,5,10], ..., -1] # fancy selection array5 = array[np.where(array[:] > 4)] # point selection array6 = array[array[:] > 4] # boolean selection Thanks to Andrew Collette for implementing this for h5py, from which it has been backported. Closes #198 and #209. - Numexpr updated to 1.3.1. This can lead to up a 25% improvement of the time for both in-kernel and indexed queries for unaligned tables. - HDF5 1.8.3 supported. Bugs fixed ---------- - Fixed problems when modifying multidimensional columns in Table objects. Closes #228. - Row attribute is no longer stalled after a table move or rename. Fixes #224. - Array.__getitem__(scalar) returns a NumPy scalar now, instead of a 0-dim NumPy array. This should not be noticed by normal users, unless they check for the type of returned value. Fixes #222. API changes ----------- - Added a `dtype` attribute for all leaves. This is the NumPy ``dtype`` that most closely matches the leaf type. This allows for a quick-and-dirty check of leaf types. Closes #230. - Added a `shape` attribute for `Column` objects. This is formed by concatenating the length of the column and the shape of its type. Also, the representation of columns has changed an now includes the length of the column as the leading dimension. Closes #231. - Added a new `maindim` attribute for `Column` which has the 0 value (the leading dimension). This allows for a better similarity with other \*Array objects. - In order to be consistent and allow the extended slicing to happen in `VLArray` objects too, `VLArray.__setitem__()` is not able to partially modify rows based on the second dimension passed as key. If this is tried, an `IndexError` is raised now. Closes #210. - The `forceCSI` flag has been replaced by `checkCSI` in the next `Table` methods: `copy()`, `readSorted()` and `itersorted()`. The change reflects the fact that a re-index operation cannot be triggered from these methods anymore. The rational for the change is that an indexing operation is a potentially very expensive operation that should be carried out explicitly instead of being triggered by methods that should not be in charge of this task. Closes #216. Backward incompatible changes ----------------------------- - After the introduction of the `shape` attribute for `Column` objects, the shape information for multidimensional columns has been removed from the `dtype` attribute (it is set to the base type of the column now). Closes #232. **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 72 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v2.2.x.rst000066400000000000000000000354011231437614300241240ustar00rootroot00000000000000======================================= Release notes for PyTables 2.2 series ======================================= :Author: Francesc Alted i Abad :Contact: faltet@pytables.org Changes from 2.2.1rc1 to 2.2.1 ============================== - The `Row` accessor implements a new `__contains__` special method that allows doing things like:: for row in table: if item in row: print "Value found in row", row.nrow break Closes #309. - PyTables is more friendly with easy_install and pip now, as all the Python dependencies should be installed automatically. Closes #298. Changes from 2.2 to 2.2.1rc1 ============================ - When using `ObjectAtom` objects in `VLArrays` the ``HIGHEST_PROTOCOL`` is used for pickling objects. For NumPy arrays, this simple change leads to space savings up to 3x and time improvements up to 30x. Closes #301. - tables.Expr can perform operations on scalars now. Thanks to Gaëtan de Menten for providing a patch for this. Closes #287. - Fixed a problem with indexes larger than 32-bit on leaf objects on 32-bit machines. Fixes #283. - Merged in Blosc 1.1.2 for fixing a problem with large datatypes and subprocess issues. Closes #288 and #295. - Due to the adoption of Blosc 1.1.2, the pthreads-win32 library dependency is dropped on Windows platforms. - Fixed a problem with tables.Expr and operands with vary large rowsizes. Closes #300. - ``leaf[numpy.array[scalar]]`` idiom returns a NumPy array instead of an scalar. This has been done for compatibility with NumPy. Closes #303. - Optimization for `Table.copy()` so that ``FIELD_*`` attrs are not overwritten during the copy. This can lead to speed-ups up to 100x for short tables that have hundreds of columns. Closes #304. - For external links, its relative paths are resolved now with respect to the directory of the main HDF5 file, rather than with respect to the current directory. Closes #306. - ``Expr.setInputsRange()`` and ``Expr.setOutputRange()`` do support ``numpy.integer`` types now. Closes #285. - Column names in tables can start with '__' now. Closes #291. - Unicode empty strings are supported now as atributes. Addresses #307. - Cython 0.13 and higher is supported now. Fixes #293. - PyTables should be more 'easy_install'-able now. Addresses #298. Changes from 2.2rc2 to 2.2 (final) ================================== - Updated Blosc to 1.0 (final). - Filter ID of Blosc changed from wrong 32010 to reserved 32001. This will prevent PyTables 2.2 (final) to read files created with Blosc and PyTables 2.2 pre-final. `ptrepack` can be used to retrieve those files, if necessary. More info in ticket #281. - Recent benchmarks suggest a new parametrization is better in most scenarios: * The default chunksize has been doubled for every dataset size. This works better in most of scenarios, specially with the new Blosc compressor. * The HDF5 CHUNK_CACHE_SIZE parameter has been raised to 2 MB in order to better adapt to the chunksize increase. This provides better hit ratio (at the cost of consuming more memory). Some plots have been added to the User's Manual (chapter 5) showing how the new parametrization works. Changes from 2.2rc1 to 2.2rc2 ============================= - A new version of Blosc (0.9.5) is included. This version is now considered to be stable and apt for production. Thanks for all PyTables users that have contributed to find and report bugs. - Added a new `IO_BUFFER_SIZE` parameter to ``tables/parameters.py`` that allows to set the internal PyTables' buffer for doing I/O. This replaces `CHUNKTIMES` but it is more general because it affects to all `Leaf` objects and also the `tables.Expr` module (and not only tables as before). - `BUFFERTIMES` parameter in ``tables/parameters.py`` has been renamed to `BUFFER_TIMES` which is more consistent with other parameter names. - On Windows platforms, the path to the tables module is now appended to sys.path and the PATH environment variable. That way DLLs and PYDs in the tables directory are to be found now. Thanks to Christoph Gohlke for the hint. - A replacement for barriers for Mac OSX, or other systems not implementing them, has been carried out. This allows to compile PyTables on such platforms. Fixes #278 - Fixed a couple of warts that raise compatibility warnings with forthcoming Python 2.7. - HDF5 1.8.5 is used in Windows binaries. Changes from 2.2b3 to 2.2rc1 ============================ - Numexpr is not included anymore in PyTables and has become a requisite instead. This is because Numexpr already has decent enough installers and is available in the PyPI repository also, so it should be easy for users to fulfill this dependency. - When using a Numexpr package that is turbo-loaded with Intel's VML/MKL, the parameter `MAX_THREADS` will control the number of threads that VML can use during computations. For a finer control, the `numexpr.set_vml_num_threads()` can always be used. - Cython is used now instead of Pyrex for Pyrex extensions. - Updated to 0.9 version of Blosc compressor. This version can make use of threads so as to accelerate the compression/decompression process. In order to change the maximum number of threads that Blosc can use (2 by default), you can modify the `MAX_THREADS` variable in ``tables/parameters.py`` or make use of the new `setBloscMaxThreads()` global function. - Reopening already opened files is supported now, provided that there is not incompatibility among intended usages (for example, you cannot reopen in append mode an already opened file in read-only mode). - Option ``--print-versions`` for ``test_all.py`` script is now preferred over the deprecated ``--show-versions``. This is more consistent with the existing `print_versions()` function. - Fixed a bug that, under some circumstances, prevented the use of table iterators in `itertool.groupby()`. Now, you can safely do things like:: sel_rows = table.where('(row_id >= 3)') for group_id, grouped_rows in itertools.groupby(sel_rows, f_group): group_mean = average([row['row_id'] for row in grouped_rows]) Fixes #264. - Copies of `Array` objects with multidimensional atoms (coming from native HDF5 files) work correctly now (i.e. the copy holds the atom dimensionality). Fixes #275. - The `tables.openFile()` function does not try anymore to open/close the file in order to guess whether it is a HDF5 or PyTables one before opening it definitely. This allows the `fcntl.flock()` and `fcntl.lockf()` Python functions to work correctly now (that's useful for arbitrating access to the file by different processes). Thanks to Dag Sverre Seljebotn and Ivan Vilata for their suggestions on hunting this one! Fixes #185. - The estimation of the chunksize when using multidimensional atoms in EArray/Carray was wrong because it did not take in account the shape of the atom. Thanks to Ralf Juengling for reporting. Fixes #273. - Non-contiguous arrays can now safely be saved as attributes. Before, if arrays were not contiguous, incorrect data was saved in attr. Fixes #270. - EXTDIM attribute for CArray/EArray now saves the correct extendeable dimension, instead of rubbish. This does not affected functionality, because extendeable dimension was retrieved directly from shape information, but it was providing misleading information to the user. Fixes #268. API changes ----------- - Now, `Table.Cols.__len__()` returns the number of top level columns instead of the number of rows in table. This is more consistent in that `Table.Cols` is an accessor for *columns*. Fixes #276. Changes from 2.2b2 to 2.2b3 =========================== - Blosc compressor has been added as an additional filter, in addition to the existing Zlib, LZO and bzip2. This new compressor is meant for fast compression and extremely fast decompression. Fixes #265. - In `File.copyFile()` method, `copyuserattrs` was set to false as default. This was unconsistent with other methods where the default value for `copyuserattrs` is true. The default for this is true now. Closes #261. - `tables.copyFile` and `File.copyFile` recognize now the parameters present in ``tables/parameters.py``. Fixes #262. - Backported fix for issue #25 in Numexpr (OP_NEG_LL treats the argument as an int, not a long long). Thanks to David Cooke for this. - CHUNK_CACHE_NELMTS in `tables/paramters.py` set to a prime number as Neil Fortner suggested. - Workaround for a problem in Python 2.6.4 (and probably other versions too) for pickling strings like "0" or "0.". Fixes #253. Changes from 2.2b1 to 2.2b2 =========================== Enhancements ------------ - Support for HDF5 hard links, soft links and external links (when PyTables is compiled against HDF5 1.8.x series). A new tutorial about its usage has been added to the 'Tutorials' chapter of User's Manual. Closes #239 and #247. - Added support for setting HDF5 chunk cache parameters in file opening/creating time. 'CHUNK_CACHE_NELMTS', 'CHUNK_CACHE_PREEMPT' and 'CHUNK_CACHE_SIZE' are the new parameters. See "PyTables' parameter files" appendix in User's Manual for more info. Closes #221. - New `Unknown` class added so that objects that HDF5 identifies as ``H5G_UNKNOWN`` can be mapped to it and continue operations gracefully. - Added flag `--dont-create-sysattrs` to ``ptrepack`` so as to not create sys attrs (default is to do it). - Support for native compound types in attributes. This allows for better compatibility with HDF5 files. Closes #208. - Support for native NumPy dtype in the description parameter of `File.createTable()`. Closes #238. Bugs fixed ---------- - Added missing `_c_classId` attribute to the `UnImplemented` class. ``ptrepack`` no longer chokes while copying `Unimplemented` classes. - The ``FIELD_*`` sys attrs are no longer copied when the ``PYTABLES_SYS_ATTRS`` parameter is set to false. - `File.createTable()` no longer segfaults if description=None. Closes #248. - Workaround for avoiding a Python issue causing a segfault when saving and then retrieving a string attribute with values "0" or "0.". Closes #253. API changes ----------- - `Row.__contains__()` disabled because it has little sense to query for a key in Row, and the correct way should be to query for it in `Table.colnames` or `Table.colpathnames` better. Closes #241. - [Semantic change] To avoid a common pitfall when asking for the string representation of a `Row` class, `Row.__str__()` has been redefined. Now, it prints something like:: >>> for row in table: ... print row ... /newgroup/table.row (Row), pointing to row #0 /newgroup/table.row (Row), pointing to row #1 /newgroup/table.row (Row), pointing to row #2 instead of:: >>> for row in table: ... print row ... ('Particle: 0', 0, 10, 0.0, 0.0) ('Particle: 1', 1, 9, 1.0, 1.0) ('Particle: 2', 2, 8, 4.0, 4.0) Use `print row[:]` idiom if you want to reproduce the old behaviour. Closes #252. Other changes ------------- - After some improvements in both HDF5 and PyTables, the limit before emitting a `PerformanceWarning` on the number of children in a group has been raised from 4096 to 16384. Changes from 2.1.1 to 2.2b1 =========================== Enhancements ------------ - Added `Expr`, a class for evaluating expressions containing array-like objects. It can evaluate expressions (like '3*a+4*b') that operate on arbitrary large arrays while optimizing the resources (basically main memory and CPU cache memory) required to perform them. It is similar to the Numexpr package, but in addition to NumPy objects, it also accepts disk-based homogeneous arrays, like the `Array`, `CArray`, `EArray` and `Column` PyTables objects. - Added support for NumPy's extended slicing in all `Leaf` objects. With that, you can do the next sort of selections:: array1 = array[4] # simple selection array2 = array[4:1000:2] # slice selection array3 = array[1, ..., ::2, 1:4, 4:] # general slice selection array4 = array[1, [1,5,10], ..., -1] # fancy selection array5 = array[np.where(array[:] > 4)] # point selection array6 = array[array[:] > 4] # boolean selection Thanks to Andrew Collette for implementing this for h5py, from which it has been backported. Closes #198 and #209. - Numexpr updated to 1.3.1. This can lead to up a 25% improvement of the time for both in-kernel and indexed queries for unaligned tables. - HDF5 1.8.3 supported. Bugs fixed ---------- - Fixed problems when modifying multidimensional columns in Table objects. Closes #228. - Row attribute is no longer stalled after a table move or rename. Fixes #224. - Array.__getitem__(scalar) returns a NumPy scalar now, instead of a 0-dim NumPy array. This should not be noticed by normal users, unless they check for the type of returned value. Fixes #222. API changes ----------- - Added a `dtype` attribute for all leaves. This is the NumPy ``dtype`` that most closely matches the leaf type. This allows for a quick-and-dirty check of leaf types. Closes #230. - Added a `shape` attribute for `Column` objects. This is formed by concatenating the length of the column and the shape of its type. Also, the representation of columns has changed an now includes the length of the column as the leading dimension. Closes #231. - Added a new `maindim` attribute for `Column` which has the 0 value (the leading dimension). This allows for a better similarity with other \*Array objects. - In order to be consistent and allow the extended slicing to happen in `VLArray` objects too, `VLArray.__setitem__()` is not able to partially modify rows based on the second dimension passed as key. If this is tried, an `IndexError` is raised now. Closes #210. - The `forceCSI` flag has been replaced by `checkCSI` in the next `Table` methods: `copy()`, `readSorted()` and `itersorted()`. The change reflects the fact that a re-index operation cannot be triggered from these methods anymore. The rational for the change is that an indexing operation is a potentially very expensive operation that should be carried out explicitly instead of being triggered by methods that should not be in charge of this task. Closes #216. Backward incompatible changes ----------------------------- - After the introduction of the `shape` attribute for `Column` objects, the shape information for multidimensional columns has been removed from the `dtype` attribute (it is set to the base type of the column now). Closes #232. **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 72 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v2.3.x.rst000066400000000000000000000063501231437614300241260ustar00rootroot00000000000000======================================= Release notes for PyTables 2.3 series ======================================= :Author: PyTables maintainers :Contact: pytables@googlemail.com Changes from 2.3 to 2.3.1 ========================= - Fixed a bug that prevented to read scalar datasets of UnImplemented types (closes :issue:`111`). Thanks to Kamil Kisiel. - Fixed a bug in `setup.py` that caused installation of PyTables 2.3 to fail on hosts with multiple python versions installed (closes :issue:`113`). Thanks to sbinet. Changes from 2.2.1 to 2.3 ========================= Features coming from (now liberated) PyTables Pro ------------------------------------------------- - OPSI is a powerful and innovative indexing engine allowing PyTables to perform fast queries on arbitrarily large tables. Moreover, it offers a wide range of optimization levels for its indexes so that the user can choose the best one that suits her needs (more or less size, more or less performance). Indexation code also takes advantage of the vectorization capabilities of the NumPy and Numexpr packages to ensure really short indexing and search times. - A fine-tuned LRU cache for both metadata (nodes) and regular data that lets you achieve maximum speed for intensive object tree browsing during data reads and queries. It complements the already efficient cache present in HDF5, although this is more geared towards high-level structures that are specific to PyTables and that are critical for achieving very high performance. Other changes ------------- - Indexes with no elements are now evaluated as non-CSI ones. Closes #312. - Numexpr presence is tested now in setup.py, provided that user is not using setuptools (i.e. ``easy_install`` or ``pip`` tools). When using setuptools, numexpr continues to be a requisite (and Cython too). Closes #298. - Cython is enforced now during compilation time. Also, it is not required when running tests. - Repeatedly closing a file that has been reopened several times is supported now. Closes #318. - The number of times a file has been currently reopened is available now in the new `File.open_count` read-only attribute. - The entire documentation set has been converted to sphinx (close :issue:`85` and :issue:`86`) that now also has an index (closes :issue`39`). - The entire test suite has been updated to use unittest specific assertions (closes :issue:`66`). - PyTables has been tested against the latest version of numpy (v. 1.6.1 and 2.0dev) and Cython (v, 0.15) packages. Closes :issue:`84`. - The setup.py script has been improved to better detect runtimes (closes :issue:`73`). Deprecations ------------ Support for some old packages and related features has been deprecated and will be removed in future versions: - Numeric (closes :issue:`76`) - numarray (closes :issue`76` and :issue:`75`) - HDF5 1.6.x (closes :issue`96`) At the API level the following are now deprecated: - the tables.is_pro constant is deprecated because PyTables Pro has been released under an open source license. - the netcdf3 sub-package (closes :issue:`67`) - the nra sub-package **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 72 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v2.4.x.rst000066400000000000000000000122671231437614300241330ustar00rootroot00000000000000======================================= Release notes for PyTables 2.4 series ======================================= :Author: PyTables maintainers :Contact: pytables@googlemail.com .. py:currentmodule:: tables Changes from 2.3.1 to 2.4 ========================= New features ------------ - Improved HDF5 error logging management: * added a new function, :func:`silenceHDF5Messages`, for suppressing (and re-enabling) HDF5 messages. By default HDF5 error logging is now suppressed. Closes :issue:`87`. * now all HDF5 error messages and trace-backs are trapped and attached to the :exc:`exceptions.HDF5ExtError` exception instances. Closes :issue:`120`. - Added support for the float16 data type. It is only available if numpy_ provides it as well (i.e. numpy_ >= 1.6). See :issue:`51`. - Leaf nodes now have attributes for retrieving the size of data in memory and on disk. Data on disk can be compressed, so the new attributes make it easy to compute the data compression ration. Thanks to Josh Ayers (close :issue:`141`). - The maximum number of threads for Blosc_ and Numexpr_ is now handled using the :data:`parameters.MAX_BLOSC_THREADS` and :data:`parameters.MAX_NUMEXPR_THREADS` parameters respectively. This allows a more fine grained configuration capability. Closes :issue:`142`. - `ndim` (read-only) attribute added to :class:`Leaf`, :class:`Atom` and :class:`Col` objects (closes :issue:`126`). - Added read support for variable length string attributes (non scalar attributes are converted into numpy_ arrays with 'O8' type). See :issue:`54`. Other improvements ------------------ - Dropped support for HDF5 1.6.x. Now PyTables uses the HDF5 1.8 API (closes :issue:`105`). - Blosc_ updated to v. 1.1.3. - The Blosc_ compression library is now automatically disabled on platforms that do not support unaligned memory access (see also https://github.com/FrancescAlted/blosc/issues/3 and http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=661286). - Improved bzip2 detection on Windows (:issue:`116`). Thanks to cgohlke. - For Windows, the setup.py script now has the ability to automatically find the HDF5_DIR in the system PATH. Thanks to Mark (mwiebe). - Improved multi-arch support in GNU/Linux platforms (closes :issue:`124`) Thanks to Julian Taylor and Picca Frederic-Emmanuel. - Use new style syntax for exception raising. Closes :issue:`93`. - Fixed most of the warnings related to py3k compatibility (see :issue:`92`). - Fixed pyflakes_ warnings (closes :issue:`102`). - Cython_ extensions updated to use new constructs (closes :issue:`100`). - Reduced the number of build warnings (closes :issue:`101`). - Removed the old lrucache module. It is no more needed after the merge with PyTables Pro (closes :issue:`118`). - Added explicit (import time) testing for hdf5dll.dll on Windows to improve diagnostics (closes :issue:`146`). Thanks to Mark (mwiebe). Documentation improvements -------------------------- - new coockbook section (contents have been coming from the PyTables wiki on http://www.pytables.org) - complete rework of the library reference. Now the entire chapter is generated from docstrings using the sphinx autodoc extension. A big thank you to Josh Ayers. Closes :issue:`148`. - new sphinx theme based on the cloud template Bugs fixed ---------- - Fixed a segfault on platforms that do not support unaligned memory access (closes: :issue:`134`). Thanks to Julian Taylor. - Fixed broken inheritance in :class:`IsDescription` classes (thanks to Andrea Bedini). Closes :issue:`65`. - Fixed table descriptions copy method (closes :issue:`131`). - Fixed open failures handling (closes :issue:`158`). Errors that happen when one tries to open an invalid HDF5 file (e.g. an empty file) are now detected earlier by PyTables and a proper exception (:exc:`exceptions.HDF5ExtError`) is raised. Also, in case of open failures, invalid file descriptors are no more cached. Before is fix it was not possible to completely close the bad file and reopen the same path, even if a valid file was created in the meanwhile. Thanks to Daniele for reporting and for the useful test code. - Fixed support to rich structured numpy.dtype in :func:`description.descr_from_dtype`. Closes :issue:`160`. - Fixed sorting of nested tables that caused AttributeError. Closes :issue:`156` and :issue:`157`. Thanks to Uwe Mayer. - Fixed flavor deregistration (closes :issue:`163`) Deprecations ------------ - The :data:`parameters.MAX_THREADS` configuration parameter is now deprecated. Please use :data:`parameters.MAX_BLOSC_THREADS` and :data:`parameters.MAX_NUMEXPR_THREADS` instead. See :issue:`142`. - Since the support for HDF5 1.6.x has been dropped, the *warn16incompat* argument of the :meth:`File.createExternalLink` method and the :exc:`exceptions.Incompat16Warning` exception class are now deprecated. .. _pyflakes: https://launchpad.net/pyflakes .. _numpy: http://www.numpy.org .. _Blosc: https://github.com/FrancescAlted/blosc .. _Numexpr: http://code.google.com/p/numexpr .. _Cython: http://www.cython.org **Enjoy data!** -- The PyTables Team .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 72 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v3.0.x.rst000066400000000000000000000265661231437614300241370ustar00rootroot00000000000000======================================= Release notes for PyTables 3.0 series ======================================= :Author: PyTables Developers :Contact: pytables@googlemail.com .. py:currentmodule:: tables Changes from 2.4 to 3.0 ======================= New features ------------ - Since this release PyTables provides full support to Python_ 3 (closes :issue:`188`). - The entire code base is now more compliant with coding style guidelines describe in the PEP8_ (closes :issue:`103` and :issue:`224`). See `API changes`_ for more details. - Basic support for HDF5 drivers. Now it is possible to open/create an HDF5 file using one of the SEC2, DIRECT, LOG, WINDOWS, STDIO or CORE drivers. Users can also set the main driver parameters (closes :issue:`166`). Thanks to Michal Slonina. - Basic support for in-memory image files. An HDF5 file can be set from or copied into a memory buffer (thanks to Michal Slonina). This feature is only available if PyTables is built against HDF5 1.8.9 or newer. Closes :issue:`165` and :issue:`173`. - New :meth:`File.get_filesize` method for retrieving the HDF5 file size. - Implemented methods to get/set the user block size in a HDF5 file (closes :issue:`123`) - Improved support for PyInstaller_. Now it is easier to pack frozen applications that use the PyTables package (closes: :issue:`177`). Thanks to Stuart Mentzer and Christoph Gohlke. - All read methods now have an optional *out* argument that allows to pass a pre-allocated array to store data (closes :issue:`192`) - Added support for the floating point data types with extended precision (Float96, Float128, Complex192 and Complex256). This feature is only available if numpy_ provides it as well. Closes :issue:`51` and :issue:`214`. Many thanks to Andrea Bedini. - Consistent ``create_xxx()`` signatures. Now it is possible to create all data sets :class:`Array`, :class:`CArray`, :class:`EArray`, :class:`VLArray`, and :class:`Table` from existing Python objects (closes :issue:`61` and :issue:`249`). See also the `API changes`_ section. - Complete rewrite of the :mod:`nodes.filenode` module. Now it is fully compliant with the interfaces defined in the standard :mod:`io` module. Only non-buffered binary I/O is supported currently. See also the `API changes`_ section. Closes :issue:`244`. - New :program:`pt2to3` tool is provided to help users to port their applications to the new API (see `API changes`_ section). Improvements ------------ - Improved runtime checks on dynamic loading of libraries: meaningful error messages are generated in case of failure. Also, now PyTables no more alters the system PATH. Closes :issue:`178` and :issue:`179` (thanks to Christoph Gohlke). - Improved list of search paths for libraries as suggested by Nicholaus Halecky (see :issue:`219`). - Removed deprecated Cython_ include (.pxi) files. Contents of :file:`convtypetables.pxi` have been moved in :file:`utilsextension.pyx`. Closes :issue:`217`. - The internal Blosc_ library has been upgraded to version 1.2.3. - Pre-load the bzip2_ library on windows (closes :issue:`205`) - The :meth:`File.get_node` method now accepts unicode paths (closes :issue:`203`) - Improved compatibility with Cython_ 0.19 (see :issue:`220` and :issue:`221`) - Improved compatibility with numexpr_ 2.1 (see also :issue:`199` and :issue:`241`) - Improved compatibility with development versions of numpy_ (see :issue:`193`) - Packaging: since this release the standard tar-ball package no more includes the PDF version of the "PyTables User Guide", so it is a little bit smaller now. The complete and pre-build version of the documentation both in HTML and PDF format is available on the file `download area`_ on SourceForge.net. Closes: :issue:`172`. - Now PyTables also uses `Travis-CI`_ as continuous integration service. All branches and all pull requests are automatically tested with different Python_ versions. Closes :issue:`212`. Other changes ------------- - PyTables now requires Python 2.6 or newer. - Minimum supported version of Numexpr_ is now 2.0. API changes ----------- The entire PyTables API as been made more PEP8_ compliant (see :issue:`224`). This means that many methods, attributes, module global variables and also keyword parameters have been renamed to be compliant with PEP8_ style guidelines (e.g. the ``tables.hdf5Version`` constant has been renamed into ``tables.hdf5_version``). We made the best effort to maintain compatibility to the old API for existing applications. In most cases, the old 2.x API is still available and usable even if it is now deprecated (see the Deprecations_ section). The only important backwards incompatible API changes are for names of function/methods arguments. All uses of keyword arguments should be checked and fixed to use the new naming convention. The new :program:`pt2to3` tool can be used to port PyTables based applications to the new API. Many deprecated features and support for obsolete modules has been dropped: - The deprecated :data:`is_pro` module constant has been removed - The nra module and support for the obsolete numarray module has been removed. The *numarray* flavor is no more supported as well (closes :issue:`107`). - Support for the obsolete Numeric module has been removed. The *numeric* flavor is no longer available (closes :issue:`108`). - The tables.netcdf3 module has been removed (closes :issue:`68`). - The deprecated :exc:`exceptions.Incompat16Warning` exception has been removed - The :meth:`File.create_external_link` method no longer has a keyword parameter named *warn16incompat*. It was deprecated in PyTables 2.4. Moreover: - The :meth:`File.create_array`, :meth:`File.create_carray`, :meth:`File.create_earray`, :meth:`File.create_vlarray`, and :meth:`File.create_table` methods of the :class:`File` objects gained a new (optional) keyword argument named ``obj``. It can be used to initialize the newly created dataset with an existing Python object, though normally these are numpy_ arrays. The *atom*/*descriptor* and *shape* parameters are now optional if the *obj* argument is provided. - The :mod:`nodes.filenode` has been completely rewritten to be fully compliant with the interfaces defined in the :mod:`io` module. The FileNode classes currently implemented are intended for binary I/O. Main changes: * the FileNode base class is no more available, * the new version of :class:`nodes.filenode.ROFileNode` and :class:`nodes.filenode.RAFileNode` objects no more expose the *offset* attribute (the *seek* and *tell* methods can be used instead), * the *lineSeparator* property is no more available end the ``\n`` character is always used as line separator. - The `__version__` module constants has been removed from almost all the modules (it was not used after the switch to Git). Of course the package level constant (:data:`tables.__version__`) still remains. Closes :issue:`112`. - The :func:`lrange` has been dropped in favor of xrange (:issue:`181`) - The :data:`parameters.MAX_THREADS` configuration parameter has been dropped in favor of :data:`parameters.MAX_BLOSC_THREADS` and :data:`parameters.MAX_NUMEXPR_THREADS` (closes :issue:`147`). - The :func:`conditions.compile_condition` function no more has a *copycols* argument, it was no more necessary since Numexpr_ 1.3.1. Closes :issue:`117`. - The *expectedsizeinMB* parameter of the :meth:`File.create_vlarray` and of the :meth:`VLArrsy.__init__` methods has been replaced by *expectedrows*. See also (:issue:`35`). - The :meth:`Table.whereAppend` method has been renamed into :meth:`Table.append_where` (closes :issue:`248`). Please refer to the :doc:`../MIGRATING_TO_3.x` document for more details about API changes and for some useful hint about the migration process from the 2.X API to the new one. Other possibly incompatible changes ----------------------------------- - All methods of the :class:`Table` class that take *start*, *stop* and *step* parameters (including :meth:`Table.read`, :meth:`Table.where`, :meth:`Table.iterrows`, etc) have been redesigned to have a consistent behaviour. The meaning of the *start*, *stop* and *step* and their default values now always work exactly like in the standard :class:`slice` objects. Closes :issue:`44` and :issue:`255`. - Unicode attributes are not stored in the HDF5 file as pickled string. They are now saved on the HDF5 file as UTF-8 encoded strings. Although this does not introduce any API breakage, files produced are different (for unicode attributes) from the ones produced by earlier versions of PyTables. - System attributes are now stored in the HDF5 file using the character set that reflects the native string behaviour: ASCII for Python 2 and UTF8 for Python 3. In any case, system attributes are represented as Python string. - The :meth:`iterrows` method of :class:`*Array` and :class:`Table` as well as the :meth:`Table.itersorted` now behave like functions in the standard :mod:`itertools` module. If the *start* parameter is provided and *stop* is None then the array/table is iterated from *start* to the last line. In PyTables < 3.0 only one element was returned. Deprecations ------------ - As described in `API changes`_, all functions, methods and attribute names that was not compliant with the PEP8_ guidelines have been changed. Old names are still available but they are deprecated. - The use of upper-case keyword arguments in the :func:`open_file` function and the :class:`File` class initializer is now deprecated. All parameters defined in the :file:`tables/parameters.py` module can still be passed as keyword argument to the :func:`open_file` function just using a lower-case version of the parameter name. Bugs fixed ---------- - Better check access on closed files (closes :issue:`62`) - Fix for :meth:`File.renameNode` where in certain cases :meth:`File._g_updateLocation` was wrongly called (closes :issue:`208`). Thanks to Michka Popoff. - Fixed ptdump failure on data with nested columns (closes :issue:`213`). Thanks to Alexander Ford. - Fixed an error in :func:`open_file` when *filename* is a :class:`numpy.str_` (closes :issue:`204`) - Fixed :issue:`119`, :issue:`230` and :issue:`232`, where an index on :class:`Time64Col` (only, :class:`Time32Col` was ok) hides the data on selection from a Tables. Thanks to Jeff Reback. - Fixed ``tables.tests.test_nestedtypes.ColsTestCase.test_00a_repr`` test method. Now the ``repr`` of of cols on big-endian platforms is correctly handled (closes :issue:`237`). - Fixes bug with completely sorted indexes where *nrowsinbuf* must be equal to or greater than the *chunksize* (thanks to Thadeus Burgess). Closes :issue:`206` and :issue:`238`. - Fixed an issue of the :meth:`Table.itersorted` with reverse iteration (closes :issue:`252` and :issue:`253`). .. _Python: http://www.python.org .. _PEP8: http://www.python.org/dev/peps/pep-0008 .. _PyInstaller: http://www.pyinstaller.org .. _Blosc: https://github.com/FrancescAlted/blosc .. _bzip2: http://www.bzip.org .. _Cython: http://www.cython.org .. _Numexpr: http://code.google.com/p/numexpr .. _numpy: http://www.numpy.org .. _`download area`: http://sourceforge.net/projects/pytables/files/pytables .. _`Travis-CI`: https://travis-ci.org **Enjoy data!** -- The PyTables Developers .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 72 .. End: PyTables-v.3.1.1/doc/source/release-notes/RELEASE_NOTES_v3.1.x.rst000066400000000000000000000000501231437614300241140ustar00rootroot00000000000000.. include:: ../../../RELEASE_NOTES.txt PyTables-v.3.1.1/doc/source/release_notes.rst000066400000000000000000000072111231437614300211370ustar00rootroot00000000000000====================== PyTables Release Notes ====================== Migration ---------- .. toctree:: :maxdepth: 1 Migrating from 2.x to 3.x Migrating from 1.x to 2.x PyTables -------- .. toctree:: :maxdepth: 1 release-notes/RELEASE_NOTES_v3.1.x release-notes/RELEASE_NOTES_v3.0.x release-notes/RELEASE_NOTES_v2.4.x release-notes/RELEASE_NOTES_v2.3.x release-notes/RELEASE_NOTES_v2.2.x release-notes/RELEASE_NOTES_v2.1.x release-notes/RELEASE_NOTES_v2.0.x release-notes/RELEASE_NOTES_v1.4 release-notes/RELEASE_NOTES_v1.3.3 release-notes/RELEASE_NOTES_v1.3.2 release-notes/RELEASE_NOTES_v1.3.1 release-notes/RELEASE_NOTES_v1.3 release-notes/RELEASE_NOTES_v1.2.3 release-notes/RELEASE_NOTES_v1.2.2 release-notes/RELEASE_NOTES_v1.2.1 release-notes/RELEASE_NOTES_v1.2 release-notes/RELEASE_NOTES_v1.1.1 release-notes/RELEASE_NOTES_v1.1 release-notes/RELEASE_NOTES_v1.0 release-notes/RELEASE_NOTES_v0.9.1 release-notes/RELEASE_NOTES_v0.9 release-notes/RELEASE_NOTES_v0.8 release-notes/RELEASE_NOTES_v0.7.2 release-notes/RELEASE_NOTES_v0.7.1 PyTables Pro ------------ .. toctree:: :maxdepth: 1 release-notes/RELEASE_NOTES_v2.2.x-pro release-notes/RELEASE_NOTES_v2.1.x-pro release-notes/RELEASE_NOTES_v2.0.x-pro Release timeline ---------------- =============== =========== ========== PyTables 3.1.1 2014-03-25 PyTables 3.1.0 2014-02-05 PyTables 3.1.0rc2 2014-01-22 PyTables 3.1.0rc1 2014-01-17 PyTables 3.0 2013-06-01 PyTables 3.0rc3 2013-05-29 PyTables 3.0rc2 2013-05-17 PyTables 3.0rc1 2013-05-10 PyTables 3.0b1 2013-04-27 PyTables 2.4 2012-07-20 PyTables 2.4rc1 2012-07-16 PyTables 2.4b1 2012-07-07 PyTables 2.3.1 2011-10-28 PyTables 2.3 2011-09-23 PyTables 2.3rc1 2011-09-11 PyTables 2.2.1 2010-11-05 PyTables 2.2.1rc1 2010-11-03 Pytables 2.2 2010-07-01 PyTables 2.2rc2 2010-06-17 PyTables 2.2rc1 2010-05-20 PyTables 2.1.2 2009-09-14 PyTables 2.1.1 2009-03-13 PyTables Pro 2.1.1 2009-03-13 PyTables 2.1 2008-12-19 PyTables 2.1rc2 2008-11-18 PyTables 2.1rc1 2008-10-31 PyTables 2.0.4 2008-07-05 PyTables Pro 2.0.4 2008-07-05 PyTables 2.0.3 2008-03-07 PyTables Pro 2.0.2.1 2007-12-24 PyTables Pro 2.0.1 2007-09-20 PyTables 2.0.1 2007-09-20 PyTables Pro 2.0 2007-07-12 PyTables 2.0 2007-07-12 PyTables 2.0rc2 2007-05-28 PyTables 2.0rc1 2007-04-26 PyTables 1.4 2006-12-21 PyTables 1.3.3 2006-08-24 PyTables 1.3.2 2006-06-20 PyTables 1.3.1 2006-05-02 PyTables 1.3 2006-04-01 PyTables 1.2.3 2006-02-23 PyTables 1.2.2 2006-02-16 PyTables 1.2.1 2005-12-21 PyTables 1.2 2005-11-22 PyTables 1.1.1 2005-09-13 PyTables 1.1 2005-07-14 PyTables 1.0 2005-05-12 PyTables 0.9.1 2004-12-02 PyTables- 0.9 2004-11-08 PyTables 0.8.1 2004-07-13 PyTables 0.8 2004-03-03 PyTables 0.7.2 2003-09-22 PyTables 0.7 2003-07-31 PyTables 0.5.1 2003-05-14 PyTables 0.5 2003-05-10 Pytables 0.4 2003-03-19 =============== =========== ========== PyTables-v.3.1.1/doc/source/usersguide/000077500000000000000000000000001231437614300177335ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/usersguide/bibliography.rst000066400000000000000000000125661231437614300231520ustar00rootroot00000000000000Bibliography ============ .. _HDFG1: :ref:`[HDFG1] ` The HDF Group. What is HDF5?. Concise description about HDF5 capabilities and its differences from earlier versions (HDF4). ``_. .. _HDFG2: :ref:`[HDFG2] ` The HDF Group. Introduction to HDF5. Introduction to the HDF5 data model and programming model. ``_. .. _HDFG3: :ref:`[HDFG3] ` The HDF Group. The HDF5 table programming model. Examples on using HDF5 tables with the C API. ``_. .. _MERTZ: :ref:`[MERTZ] ` David Mertz. Objectify. On the 'Pythonic' treatment of XML documents as objects(II). Article describing XML Objectify, a Python module that allows working with XML documents as Python objects. Some of the ideas presented here are used in PyTables. ``_. .. _CYTHON: :ref:`[CYTHON] ` Stefan Behnel, Robert Bradshaw, Dag Sverre Seljebotn, and Greg Ewing. Cython. A language that makes writing C extensions for the Python language as easy as Python itself. ``_. .. _NUMPY: :ref:`[NUMPY] ` Travis Oliphant and et al. NumPy. Scientific Computing with Numerical Python. The latest and most powerful re-implementation of Numeric to date. It implements all the features that can be found in Numeric and numarray, plus a bunch of new others. In general, it is more efficient as well. ``_. .. _NUMEXPR: :ref:`[NUMEXPR] ` David Cooke, Francesc Alted, and et al. Numexpr. Fast evaluation of array expressions by using a vector-based virtual machine. It is an enhaced computing kernel that is generally faster (between 1x and 10x, depending on the kind of operations) than NumPy at evaluating complex array expressions. ``_. .. _ZLIB: :ref:`[ZLIB] ` JeanLoup Gailly and Mark Adler. zlib. A Massively Spiffy Yet Delicately Unobtrusive Compression Library. A standard library for compression purposes. ``_. .. _LZO: :ref:`[LZO] ` Markus F Oberhumer. LZO. A data compression library which is suitable for data de-/compression in real-time. It offers pretty fast compression and decompression with reasonable compression ratio. ``_. .. _BZIP2: :ref:`[BZIP2] ` Julian Seward. bzip2. A high performance lossless compressor. It offers very high compression ratios within reasonable times. ``_. .. _BLOSC: :ref:`[BLOSC] ` Francesc Alted. Blosc. A blocking, shuffling and loss-less compression library. A compressor designed to transmit data from memory to CPU (and back) faster than a plain memcpy(). ``_. .. _GNUWIN32: :ref:`[GNUWIN32] ` Alexis Wilke, Jerry S., Kees Zeelenberg, and Mathias Michaelis. GnuWin32. GNU (and other) tools ported to Win32. GnuWin32 provides native Win32-versions of GNU tools, or tools with a similar open source licence. ``_. .. _PSYCO: :ref:`[PYSCO] ` Armin Rigo. Psyco. A Python specializing compiler. Run existing Python software faster, with no change in your source. ``_. .. _SCIPY1: :ref:`[SCIPY1] ` Konrad Hinsen. Scientific Python. Collection of Python modules useful for scientific computing. ``_. .. _SCIPY2: :ref:`[SCIPY2] ` Eric Jones, Travis Oliphant, Pearu Peterson, and et al. SciPy. Scientific tools for Python. SciPy supplements the popular Numeric module, gathering a variety of high level science and engineering modules together as a single package. ``_. .. _OPTIM: :ref:`[OPTIM] ` Francesc Alted and Ivan Vilata. Optimization of file openings in PyTables. This document explores the savings of the opening process in terms of both CPU time and memory, due to the adoption of a LRU cache for the nodes in the object tree. ``_. .. _OPSI: :ref:`[OPSI] ` Francesc Alted and Ivan Vilata. OPSI: The indexing system of PyTables 2 Professional Edition. Exhaustive description and benchmarks about the indexing engine that comes with PyTables Pro. ``_. .. _VITABLES: :ref:`[VITABLES] ` Vicent Mas. ViTables. A GUI for PyTables/HDF5 files. It is a graphical tool for browsing and editing files in both PyTables and HDF5 formats. ``_. .. _GIT: :ref:`[GIT] ` Git is a free and open source, distributed version control system designed to handle everything from small to very large projects with speed and efficiency ``_. .. _SPHINX: :ref:`[SPHINX] ` Sphinx is a tool that makes it easy to create intelligent and beautiful documentation, written by Georg Brandl and licensed under the BSD license ``_. .. |Kuepper| unicode:: K U+00FC pper .. Kuepper .. todo:: remove the above substitution. It is no more needed with sphinx 1.0.8 PyTables-v.3.1.1/doc/source/usersguide/condition_syntax.rst000066400000000000000000000132201231437614300240570ustar00rootroot00000000000000.. _condition_syntax: Condition Syntax ================ .. currentmodule:: tables Conditions in PyTables are used in methods related with in-kernel and indexed searches such as :meth:`Table.where` or :meth:`Table.read_where`. They are interpreted using Numexpr, a powerful package for achieving C-speed computation of array operations (see :ref:`[NUMEXPR] `). A condition on a table is just a *string* containing a Python expression involving *at least one column*, and maybe some constants and external variables, all combined with algebraic operators and functions. The result of a valid condition is always a *boolean array* of the same length as the table, where the *i*-th element is true if the value of the expression on the *i*-th row of the table evaluates to true That is the reason why multidimensional fields in a table are not supported in conditions, since the truth value of each resulting multidimensional boolean value is not obvious. Usually, a method using a condition will only consider the rows where the boolean result is true. For instance, the condition 'sqrt(x*x + y*y) < 1' applied on a table with x and y columns consisting of floating point numbers results in a boolean array where the *i*-th element is true if (unsurprisingly) the value of the square root of the sum of squares of x and y is less than 1. The sqrt() function works element-wise, the 1 constant is adequately broadcast to an array of ones of the length of the table for evaluation, and the *less than* operator makes the result a valid boolean array. A condition like 'mycolumn' alone will not usually be valid, unless mycolumn is itself a column of scalar, boolean values. In the previous conditions, mycolumn, x and y are examples of *variables* which are associated with columns. Methods supporting conditions do usually provide their own ways of binding variable names to columns and other values. You can read the documentation of :meth:`Table.where` for more information on that. Also, please note that the names None, True and False, besides the names of functions (see below) *can not be overridden*, but you can always define other new names for the objects you intend to use. Values in a condition may have the following types: - 8-bit boolean (bool). - 32-bit signed integer (int). - 64-bit signed integer (long). - 32-bit, single-precision floating point number (float or float32). - 64-bit, double-precision floating point number (double or float64). - 2x64-bit, double-precision complex number (complex). - Raw string of bytes (str). Nevertheless, if the type passed is not among the above ones, it will be silently upcasted, so you don't need to worry too much about passing supported types, except for the Unsigned 64 bits integer, that cannot be upcasted to any of the supported types. However, the types in PyTables conditions are somewhat stricter than those of Python. For instance, the *only* valid constants for booleans are True and False, and they are *never* automatically cast to integers. The type strengthening also affects the availability of operators and functions. Beyond that, the usual type inference rules apply. Conditions support the set of operators listed below: - Logical operators: &, \|, ~. - Comparison operators: <, <=, ==, !=, >=, >. - Unary arithmetic operators: -. - Binary arithmetic operators: +, -, \*, /, \**, %. Types do not support all operators. Boolean values only support logical and strict (in)equality comparison operators, while strings only support comparisons, numbers do not work with logical operators, and complex comparisons can only check for strict (in)equality. Unsupported operations (including invalid castings) raise NotImplementedError exceptions. You may have noticed the special meaning of the usually bitwise operators &, | and ~. Because of the way Python handles the short-circuiting of logical operators and the truth values of their operands, conditions must use the bitwise operator equivalents instead. This is not difficult to remember, but you must be careful because bitwise operators have a *higher precedence* than logical operators. For instance, 'a and b == c' (*a is true AND b is equal to c*) is *not* equivalent to 'a & b == c' (*a AND b is equal to c)*. The safest way to avoid confusions is to *use parentheses* around logical operators, like this: 'a & (b == c)'. Another effect of short-circuiting is that expressions like '0 < x < 1' will *not* work as expected; you should use '(0 < x) & (x < 1)'. All of this may be solved if Python supported overloadable boolean operators (see PEP 335) or some kind of non-shortcircuiting boolean operators (like C's &&, || and !). You can also use the following functions in conditions: - where(bool, number1, number2): number - number1 if the bool condition is true, number2 otherwise. - {sin,cos,tan}(float|complex): float|complex - trigonometric sine, cosine or tangent. - {arcsin,arccos,arctan}(float|complex): float|complex - trigonometric inverse sine, cosine or tangent. - arctan2(float1, float2): float - trigonometric inverse tangent of float1/float2. - {sinh,cosh,tanh}(float|complex): float|complex - hyperbolic sine, cosine or tangent. - {arcsinh,arccosh,arctanh}(float|complex): float|complex - hyperbolic inverse sine, cosine or tangent. - {log,log10,log1p}(float|complex): float|complex - natural, base-10 and log(1+x) logarithms. - {exp,expm1}(float|complex): float|complex - exponential and exponential minus one. - sqrt(float|complex): float|complex - square root. - {abs}(float|complex): float|complex - absolute value. - {real,imag}(complex): float - real or imaginary part of complex. - complex(float, float): complex - complex from real and imaginary parts. PyTables-v.3.1.1/doc/source/usersguide/datatypes.rst000066400000000000000000000140561231437614300224710ustar00rootroot00000000000000.. _datatypes: Supported data types in PyTables ================================ All PyTables datasets can handle the complete set of data types supported by the NumPy (see :ref:`[NUMPY] `) package in Python. The data types for table fields can be set via instances of the Col class and its descendants (see :ref:`ColClassDescr`), while the data type of array elements can be set through the use of the Atom class and its descendants (see :ref:`AtomClassDescr`). PyTables uses ordinary strings to represent its *types*, with most of them matching the names of NumPy scalar types. Usually, a PyTables type consists of two parts: a *kind* and a *precision* in bits. The precision may be omitted in types with just one supported precision (like bool) or with a non-fixed size (like string). There are eight kinds of types supported by PyTables: - bool: Boolean (true/false) types. Supported precisions: 8 (default) bits. - int: Signed integer types. Supported precisions: 8, 16, 32 (default) and 64 bits. - uint: Unsigned integer types. Supported precisions: 8, 16, 32 (default) and 64 bits. - float: Floating point types. Supported precisions: 16, 32, 64 (default) bits and extended precision floating point (see :ref:`note on floating point types`). - complex: Complex number types. Supported precisions: 64 (32+32), 128 (64+64, default) bits and extended precision complex (see :ref:`note on floating point types`). - string: Raw string types. Supported precisions: 8-bit positive multiples. - time: Data/time types. Supported precisions: 32 and 64 (default) bits. - enum: Enumerated types. Precision depends on base type. .. _floating-point-note: .. note:: Floating point types. The half precision floating point data type (float16) and extended precision ones (fload96, float128, complex192, complex256) are only available if numpy_ supports them on the host platform. Also, in order to use the half precision floating point type (float16) it is required numpy_ >= 1.6.0. .. _numpy: http://www.numpy.org The time and enum kinds area little bit special, since they represent HDF5 types which have no direct Python counterpart, though atoms of these kinds have a more-or-less equivalent NumPy data type. There are two types of time: 4-byte signed integer (time32) and 8-byte double precision floating point (time64). Both of them reflect the number of seconds since the Unix epoch, i.e. Jan 1 00:00:00 UTC 1970. They are stored in memory as NumPy's int32 and float64, respectively, and in the HDF5 file using the H5T_TIME class. Integer times are stored on disk as such, while floating point times are split into two signed integer values representing seconds and microseconds (beware: smaller decimals will be lost!). PyTables also supports HDF5 H5T_ENUM *enumerations* (restricted sets of unique name and unique value pairs). The NumPy representation of an enumerated value (an Enum, see :ref:`EnumClassDescr`) depends on the concrete *base type* used to store the enumeration in the HDF5 file. Currently, only scalar integer values (both signed and unsigned) are supported in enumerations. This restriction may be lifted when HDF5 supports other kinds on enumerated values. Here you have a quick reference to the complete set of supported data types: .. table:: **Data types supported for array elements and tables columns in PyTables.** ================== ========================== ====================== =============== ================== Type Code Description C Type Size (in bytes) Python Counterpart ================== ========================== ====================== =============== ================== bool boolean unsigned char 1 bool int8 8-bit integer signed char 1 int uint8 8-bit unsigned integer unsigned char 1 int int16 16-bit integer short 2 int uint16 16-bit unsigned integer unsigned short 2 int int32 integer int 4 int uint32 unsigned integer unsigned int 4 long int64 64-bit integer long long 8 long uint64 unsigned 64-bit integer unsigned long long 8 long float16 [1]_ half-precision float - 2 - float32 single-precision float float 4 float float64 double-precision float double 8 float float96 [1]_ [2]_ extended precision float - 12 - float128 [1]_ [2]_ extended precision float - 16 - complex64 single-precision complex struct {float r, i;} 8 complex complex128 double-precision complex struct {double r, i;} 16 complex complex192 [1]_ extended precision complex - 24 - complex256 [1]_ extended precision complex - 32 - string arbitrary length string char[] * str time32 integer time POSIX's time_t 4 int time64 floating point time POSIX's struct timeval 8 float enum enumerated value enum - - ================== ========================== ====================== =============== ================== .. rubric:: Footnotes .. [1] see the above :ref:`note on floating point types `. .. [2] currently in numpy_. "float96" and "float128" are equivalent of "longdouble" i.e. 80 bit extended precision floating point. PyTables-v.3.1.1/doc/source/usersguide/file_format.rst000066400000000000000000000347231231437614300227650ustar00rootroot00000000000000PyTables File Format ==================== PyTables has a powerful capability to deal with native HDF5 files created with another tools. However, there are situations were you may want to create truly native PyTables files with those tools while retaining fully compatibility with PyTables format. That is perfectly possible, and in this appendix is presented the format that you should endow to your own-generated files in order to get a fully PyTables compatible file. We are going to describe the *2.0 version of PyTables file format* (introduced in PyTables version 2.0). As time goes by, some changes might be introduced (and documented here) in order to cope with new necessities. However, the changes will be carefully pondered so as to ensure backward compatibility whenever is possible. A PyTables file is composed with arbitrarily large amounts of HDF5 groups (Groups in PyTables naming scheme) and datasets (Leaves in PyTables naming scheme). For groups, the only requirements are that they must have some *system attributes* available. By convention, system attributes in PyTables are written in upper case, and user attributes in lower case but this is not enforced by the software. In the case of datasets, besides the mandatory system attributes, some conditions are further needed in their storage layout, as well as in the datatypes used in there, as we will see shortly. As a final remark, you can use any filter as you want to create a PyTables file, provided that the filter is a standard one in HDF5, like *zlib*, *shuffle* or *szip* (although the last one can not be used from within PyTables to create a new file, datasets compressed with szip can be read, because it is the HDF5 library which do the decompression transparently). .. currentmodule:: tables Mandatory attributes for a File ------------------------------- The File object is, in fact, an special HDF5 *group* structure that is *root* for the rest of the objects on the object tree. The next attributes are mandatory for the HDF5 *root group* structure in PyTables files: * *CLASS*: This attribute should always be set to 'GROUP' for group structures. * *PYTABLES_FORMAT_VERSION*: It represents the internal format version, and currently should be set to the '2.0' string. * *TITLE*: A string where the user can put some description on what is this group used for. * *VERSION*: Should contains the string '1.0'. Mandatory attributes for a Group -------------------------------- The next attributes are mandatory for *group* structures: * *CLASS*: This attribute should always be set to 'GROUP' for group structures. * *TITLE*: A string where the user can put some description on what is this group used for. * *VERSION*: Should contains the string '1.0'. Optional attributes for a Group ------------------------------- The next attributes are optional for *group* structures: * *FILTERS*: When present, this attribute contains the filter properties (a Filters instance, see section :ref:`FiltersClassDescr`) that may be inherited by leaves or groups created immediately under this group. This is a packed 64-bit integer structure, where - *byte 0* (the least-significant byte) is the compression level (complevel). - *byte 1* is the compression library used (complib): 0 when irrelevant, 1 for Zlib, 2 for LZO and 3 for Bzip2. - *byte 2* indicates which parameterless filters are enabled (shuffle and fletcher32): bit 0 is for *Shuffle* while bit 1 is for*Fletcher32*. - other bytes are reserved for future use. Mandatory attributes, storage layout and supported data types for Leaves ------------------------------------------------------------------------ This depends on the kind of Leaf. The format for each type follows. .. _TableFormatDescr: Table format ~~~~~~~~~~~~ Mandatory attributes ^^^^^^^^^^^^^^^^^^^^ The next attributes are mandatory for *table* structures: * *CLASS*: Must be set to 'TABLE'. * *TITLE*: A string where the user can put some description on what is this dataset used for. * *VERSION*: Should contain the string '2.6'. * *FIELD_X_NAME*: It contains the names of the different fields. The X means the number of the field, zero-based (beware, order do matter). You should add as many attributes of this kind as fields you have in your records. * *FIELD_X_FILL*: It contains the default values of the different fields. All the datatypes are supported natively, except for complex types that are currently serialized using Pickle. The X means the number of the field, zero-based (beware, order do matter). You should add as many attributes of this kind as fields you have in your records. These fields are meant for saving the default values persistently and their existence is optional. * *NROWS*: This should contain the number of *compound* data type entries in the dataset. It must be an *int* data type. Storage Layout ^^^^^^^^^^^^^^ A Table has a *dataspace* with a *1-dimensional chunked* layout. Datatypes supported ^^^^^^^^^^^^^^^^^^^ The datatype of the elements (rows) of Table must be the H5T_COMPOUND *compound* data type, and each of these compound components must be built with only the next HDF5 data types *classes*: * *H5T_BITFIELD*: This class is used to represent the Bool type. Such a type must be build using a H5T_NATIVE_B8 datatype, followed by a HDF5 H5Tset_precision call to set its precision to be just 1 bit. * *H5T_INTEGER*: This includes the next data types: * *H5T_NATIVE_SCHAR*: This represents a *signed char* C type, but it is effectively used to represent an Int8 type. * *H5T_NATIVE_UCHAR*: This represents an *unsigned char* C type, but it is effectively used to represent an UInt8 type. * *H5T_NATIVE_SHORT*: This represents a *short* C type, and it is effectively used to represent an Int16 type. * *H5T_NATIVE_USHORT*: This represents an *unsigned short* C type, and it is effectively used to represent an UInt16 type. * *H5T_NATIVE_INT*: This represents an *int* C type, and it is effectively used to represent an Int32 type. * *H5T_NATIVE_UINT*: This represents an *unsigned int* C type, and it is effectively used to represent an UInt32 type. * *H5T_NATIVE_LONG*: This represents a *long* C type, and it is effectively used to represent an Int32 or an Int64, depending on whether you are running a 32-bit or 64-bit architecture. * *H5T_NATIVE_ULONG*: This represents an *unsigned long* C type, and it is effectively used to represent an UInt32 or an UInt64, depending on whether you are running a 32-bit or 64-bit architecture. * *H5T_NATIVE_LLONG*: This represents a *long long* C type (__int64, if you are using a Windows system) and it is effectively used to represent an Int64 type. * *H5T_NATIVE_ULLONG*: This represents an *unsigned long long* C type (beware: this type does not have a correspondence on Windows systems) and it is effectively used to represent an UInt64 type. * *H5T_FLOAT*: This includes the next datatypes: * *H5T_NATIVE_FLOAT*: This represents a *float* C type and it is effectively used to represent an Float32 type. * *H5T_NATIVE_DOUBLE*: This represents a *double* C type and it is effectively used to represent an Float64 type. * *H5T_TIME*: This includes the next datatypes: * *H5T_UNIX_D32*: This represents a POSIX *time_t* C type and it is effectively used to represent a 'Time32' aliasing type, which corresponds to an Int32 type. * *H5T_UNIX_D64*: This represents a POSIX *struct timeval* C type and it is effectively used to represent a 'Time64' aliasing type, which corresponds to a Float64 type. * *H5T_STRING*: The datatype used to describe strings in PyTables is H5T_C_S1 (i.e. a *string* C type) followed with a call to the HDF5 H5Tset_size() function to set their length. * *H5T_ARRAY*: This allows the construction of homogeneous, multidimensional arrays, so that you can include such objects in compound records. The types supported as elements of H5T_ARRAY data types are the ones described above. Currently, PyTables does not support nested H5T_ARRAY types. * *H5T_COMPOUND*: This allows the support for datatypes that are compounds of compounds (this is also known as *nested types* along this manual). This support can also be used for defining complex numbers. Its format is described below: The H5T_COMPOUND type class contains two members. Both members must have the H5T_FLOAT atomic datatype class. The name of the first member should be "r" and represents the real part. The name of the second member should be "i" and represents the imaginary part. The *precision* property of both of the H5T_FLOAT members must be either 32 significant bits (e.g. H5T_NATIVE_FLOAT) or 64 significant bits (e.g. H5T_NATIVE_DOUBLE). They represent Complex32 and Complex64 types respectively. Array format ~~~~~~~~~~~~ Mandatory attributes ^^^^^^^^^^^^^^^^^^^^ The next attributes are mandatory for *array* structures: * *CLASS*: Must be set to 'ARRAY'. * *TITLE*: A string where the user can put some description on what is this dataset used for. * *VERSION*: Should contain the string '2.3'. Storage Layout ^^^^^^^^^^^^^^ An Array has a *dataspace* with a *N-dimensional contiguous* layout (if you prefer a *chunked* layout see EArray below). Datatypes supported ^^^^^^^^^^^^^^^^^^^ The elements of Array must have either HDF5 *atomic* data types or a *compound* data type representing a complex number. The atomic data types can currently be one of the next HDF5 data type *classes*: H5T_BITFIELD, H5T_INTEGER, H5T_FLOAT and H5T_STRING. The H5T_TIME class is also supported for reading existing Array objects, but not for creating them. See the Table format description in :ref:`TableFormatDescr` for more info about these types. In addition to the HDF5 atomic data types, the Array format supports complex numbers with the H5T_COMPOUND data type class. See the Table format description in :ref:`TableFormatDescr` for more info about this special type. You should note that H5T_ARRAY class datatypes are not allowed in Array objects. CArray format ~~~~~~~~~~~~~ Mandatory attributes ^^^^^^^^^^^^^^^^^^^^ The next attributes are mandatory for *CArray* structures: * *CLASS*: Must be set to 'CARRAY'. * *TITLE*: A string where the user can put some description on what is this dataset used for. * *VERSION*: Should contain the string '1.0'. Storage Layout ^^^^^^^^^^^^^^ An CArray has a *dataspace* with a *N-dimensional chunked* layout. Datatypes supported ^^^^^^^^^^^^^^^^^^^ The elements of CArray must have either HDF5 *atomic* data types or a *compound* data type representing a complex number. The atomic data types can currently be one of the next HDF5 data type *classes*: H5T_BITFIELD, H5T_INTEGER, H5T_FLOAT and H5T_STRING. The H5T_TIME class is also supported for reading existing CArray objects, but not for creating them. See the Table format description in :ref:`TableFormatDescr` for more info about these types. In addition to the HDF5 atomic data types, the CArray format supports complex numbers with the H5T_COMPOUND data type class. See the Table format description in :ref:`TableFormatDescr` for more info about this special type. You should note that H5T_ARRAY class datatypes are not allowed yet in Array objects. EArray format ~~~~~~~~~~~~~ Mandatory attributes ^^^^^^^^^^^^^^^^^^^^ The next attributes are mandatory for *earray* structures: * *CLASS*: Must be set to 'EARRAY'. * *EXTDIM*: (*Integer*) Must be set to the extendable dimension. Only one extendable dimension is supported right now. * *TITLE*: A string where the user can put some description on what is this dataset used for. * *VERSION*: Should contain the string '1.3'. Storage Layout ^^^^^^^^^^^^^^ An EArray has a *dataspace* with a *N-dimensional chunked* layout. Datatypes supported ^^^^^^^^^^^^^^^^^^^ The elements of EArray are allowed to have the same data types as for the elements in the Array format. They can be one of the HDF5 *atomic* data type *classes*: H5T_BITFIELD, H5T_INTEGER, H5T_FLOAT, H5T_TIME or H5T_STRING, see the Table format description in :ref:`TableFormatDescr` for more info about these types. They can also be a H5T_COMPOUND datatype representing a complex number, see the Table format description in :ref:`TableFormatDescr`. You should note that H5T_ARRAY class data types are not allowed in EArray objects. .. _VLArrayFormatDescr: VLArray format ~~~~~~~~~~~~~~ Mandatory attributes ^^^^^^^^^^^^^^^^^^^^ The next attributes are mandatory for *vlarray* structures: * *CLASS*: Must be set to 'VLARRAY'. * *PSEUDOATOM*: This is used so as to specify the kind of pseudo-atom (see :ref:`VLArrayFormatDescr`) for the VLArray. It can take the values 'vlstring', 'vlunicode' or 'object'. If your atom is not a pseudo-atom then you should not specify it. * *TITLE*: A string where the user can put some description on what is this dataset used for. * *VERSION*: Should contain the string '1.3'. Storage Layout ^^^^^^^^^^^^^^ An VLArray has a *dataspace* with a *1-dimensional chunked* layout. Data types supported ^^^^^^^^^^^^^^^^^^^^ The data type of the elements (rows) of VLArray objects must be the H5T_VLEN *variable-length* (or VL for short) datatype, and the base datatype specified for the VL datatype can be of any *atomic* HDF5 datatype that is listed in the Table format description :ref:`TableFormatDescr`. That includes the classes: - H5T_BITFIELD - H5T_INTEGER - H5T_FLOAT - H5T_TIME - H5T_STRING - H5T_ARRAY They can also be a H5T_COMPOUND data type representing a complex number, see the Table format description in :ref:`TableFormatDescr` for a detailed description. You should note that this does not include another VL datatype, or a compound datatype that does not fit the description of a complex number. Note as well that, for object and vlstring pseudo-atoms, the base for the VL datatype is always a H5T_NATIVE_UCHAR (H5T_NATIVE_UINT for vlunicode). That means that the complete row entry in the dataset has to be used in order to fully serialize the object or the variable length string. Optional attributes for Leaves ------------------------------ The next attributes are optional for *leaves*: * *FLAVOR*: This is meant to provide the information about the kind of object kept in the Leaf, i.e. when the dataset is read, it will be converted to the indicated flavor. It can take one the next string values: * *"numpy"*: Read data (structures arrays, arrays, records, scalars) will be returned as NumPy objects. * *"python"*: Read data will be returned as Python lists, tuples, or scalars. PyTables-v.3.1.1/doc/source/usersguide/filenode.rst000066400000000000000000000301231231437614300222510ustar00rootroot00000000000000.. _filenode_usersguide: filenode - simulating a filesystem with PyTables ================================================ .. currentmodule:: tables.nodes.filenode What is filenode? ----------------- filenode is a module which enables you to create a PyTables database of nodes which can be used like regular opened files in Python. In other words, you can store a file in a PyTables database, and read and write it as you would do with any other file in Python. Used in conjunction with PyTables hierarchical database organization, you can have your database turned into an open, extensible, efficient, high capacity, portable and metadata-rich filesystem for data exchange with other systems (including backup purposes). Between the main features of filenode, one can list: - *Open:* Since it relies on PyTables, which in turn, sits over HDF5 (see :ref:`[HDGG1] `), a standard hierarchical data format from NCSA. - *Extensible:* You can define new types of nodes, and their instances will be safely preserved (as are normal groups, leafs and attributes) by PyTables applications having no knowledge of their types. Moreover, the set of possible attributes for a node is not fixed, so you can define your own node attributes. - *Efficient:* Thanks to PyTables' proven extreme efficiency on handling huge amounts of data. filenode can make use of PyTables' on-the-fly compression and decompression of data. - *High capacity:* Since PyTables and HDF5 are designed for massive data storage (they use 64-bit addressing even where the platform does not support it natively). - *Portable:* Since the HDF5 format has an architecture-neutral design, and the HDF5 libraries and PyTables are known to run under a variety of platforms. Besides that, a PyTables database fits into a single file, which poses no trouble for transportation. - *Metadata-rich:* Since PyTables can store arbitrary key-value pairs (even Python objects!) for every database node. Metadata may include authorship, keywords, MIME types and encodings, ownership information, access control lists (ACL), decoding functions and anything you can imagine! Finding a filenode node ----------------------- filenode nodes can be recognized because they have a NODE_TYPE system attribute with a 'file' value. It is recommended that you use the :meth:`File.get_node_attr` method of tables.File class to get the NODE_TYPE attribute independently of the nature (group or leaf) of the node, so you do not need to care about. filenode - simulating files inside PyTables ------------------------------------------- The filenode module is part of the nodes sub-package of PyTables. The recommended way to import the module is:: >>> from tables.nodes import filenode However, filenode exports very few symbols, so you can import * for interactive usage. In fact, you will most probably only use the NodeType constant and the new_node() and open_node() calls. The NodeType constant contains the value that the NODE_TYPE system attribute of a node file is expected to contain ('file', as we have seen). Although this is not expected to change, you should use filenode.NodeType instead of the literal 'file' when possible. new_node() and open_node() are the equivalent to the Python file() call (alias open()) for ordinary files. Their arguments differ from that of file(), but this is the only point where you will note the difference between working with a node file and working with an ordinary file. For this little tutorial, we will assume that we have a PyTables database opened for writing. Also, if you are somewhat lazy at typing sentences, the code that we are going to explain is included in the examples/filenodes1.py file. You can create a brand new file with these sentences:: >>> import tables >>> h5file = tables.open_file('fnode.h5', 'w') Creating a new file node ~~~~~~~~~~~~~~~~~~~~~~~~ Creation of a new file node is achieved with the new_node() call. You must tell it in which PyTables file you want to create it, where in the PyTables hierarchy you want to create the node and which will be its name. The PyTables file is the first argument to new_node(); it will be also called the 'host PyTables file'. The other two arguments must be given as keyword arguments where and name, respectively. As a result of the call, a brand new appendable and readable file node object is returned. So let us create a new node file in the previously opened h5file PyTables file, named 'fnode_test' and placed right under the root of the database hierarchy. This is that command:: >>> fnode = filenode.new_node(h5file, where='/', name='fnode_test') That is basically all you need to create a file node. Simple, isn't it? From that point on, you can use fnode as any opened Python file (i.e. you can write data, read data, lines of text and so on). new_node() accepts some more keyword arguments. You can give a title to your file with the title argument. You can use PyTables' compression features with the filters argument. If you know beforehand the size that your file will have, you can give its final file size in bytes to the expectedsize argument so that the PyTables library would be able to optimize the data access. new_node() creates a PyTables node where it is told to. To prove it, we will try to get the NODE_TYPE attribute from the newly created node:: >>> print(h5file.get_node_attr('/fnode_test', 'NODE_TYPE')) file Using a file node ~~~~~~~~~~~~~~~~~ As stated above, you can use the new node file as any other opened file. Let us try to write some text in and read it:: >>> print("This is a test text line.", file=fnode) >>> print("And this is another one.", file=fnode) >>> print(file=fnode) >>> fnode.write("Of course, file methods can also be used.") >>> >>> fnode.seek(0) # Go back to the beginning of file. >>> >>> for line in fnode: ... print(repr(line)) 'This is a test text line.\\n' 'And this is another one.\\n' '\\n' 'Of course, file methods can also be used.' This was run on a Unix system, so newlines are expressed as '\n'. In fact, you can override the line separator for a file by setting its line_separator property to any string you want. While using a file node, you should take care of closing it *before* you close the PyTables host file. Because of the way PyTables works, your data it will not be at a risk, but every operation you execute after closing the host file will fail with a ValueError. To close a file node, simply delete it or call its close() method:: >>> fnode.close() >>> print(fnode.closed) True Opening an existing file node ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you have a file node that you created using new_node(), you can open it later by calling open_node(). Its arguments are similar to that of file() or open(): the first argument is the PyTables node that you want to open (i.e. a node with a NODE_TYPE attribute having a 'file' value), and the second argument is a mode string indicating how to open the file. Contrary to file(), open_node() can not be used to create a new file node. File nodes can be opened in read-only mode ('r') or in read-and-append mode ('a+'). Reading from a file node is allowed in both modes, but appending is only allowed in the second one. Just like Python files do, writing data to an appendable file places it after the file pointer if it is on or beyond the end of the file, or otherwise after the existing data. Let us see an example:: >>> node = h5file.root.fnode_test >>> fnode = filenode.open_node(node, 'a+') >>> print(repr(fnode.readline())) 'This is a test text line.\\n' >>> print(fnode.tell()) 26 >>> print("This is a new line.", file=fnode) >>> print(repr(fnode.readline())) '' Of course, the data append process places the pointer at the end of the file, so the last readline() call hit EOF. Let us seek to the beginning of the file to see the whole contents of our file:: >>> fnode.seek(0) >>> for line in fnode: ... print(repr(line)) 'This is a test text line.\\n' 'And this is another one.\\n' '\\n' 'Of course, file methods can also be used.This is a new line.\\n' As you can check, the last string we wrote was correctly appended at the end of the file, instead of overwriting the second line, where the file pointer was positioned by the time of the appending. Adding metadata to a file node ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can associate arbitrary metadata to any open node file, regardless of its mode, as long as the host PyTables file is writable. Of course, you could use the set_node_attr() method of tables.File to do it directly on the proper node, but filenode offers a much more comfortable way to do it. filenode objects have an attrs property which gives you direct access to their corresponding AttributeSet object. For instance, let us see how to associate MIME type metadata to our file node:: >>> fnode.attrs.content_type = 'text/plain; charset=us-ascii' As simple as A-B-C. You can put nearly anything in an attribute, which opens the way to authorship, keywords, permissions and more. Moreover, there is not a fixed list of attributes. However, you should avoid names in all caps or starting with '_', since PyTables and filenode may use them internally. Some valid examples:: >>> fnode.attrs.author = "Ivan Vilata i Balaguer" >>> fnode.attrs.creation_date = '2004-10-20T13:25:25+0200' >>> fnode.attrs.keywords_en = ["FileNode", "test", "metadata"] >>> fnode.attrs.keywords_ca = ["FileNode", "prova", "metadades"] >>> fnode.attrs.owner = 'ivan' >>> fnode.attrs.acl = {'ivan': 'rw', '@users': 'r'} You can check that these attributes get stored by running the ptdump command on the host PyTables file. .. code-block:: bash $ ptdump -a fnode.h5:/fnode_test /fnode_test (EArray(113,)) '' /fnode_test.attrs (AttributeSet), 14 attributes: [CLASS := 'EARRAY', EXTDIM := 0, FLAVOR := 'numpy', NODE_TYPE := 'file', NODE_TYPE_VERSION := 2, TITLE := '', VERSION := '1.2', acl := {'ivan': 'rw', '@users': 'r'}, author := 'Ivan Vilata i Balaguer', content_type := 'text/plain; charset=us-ascii', creation_date := '2004-10-20T13:25:25+0200', keywords_ca := ['FileNode', 'prova', 'metadades'], keywords_en := ['FileNode', 'test', 'metadata'], owner := 'ivan'] Note that filenode makes no assumptions about the meaning of your metadata, so its handling is entirely left to your needs and imagination. Complementary notes ------------------- You can use file nodes and PyTables groups to mimic a filesystem with files and directories. Since you can store nearly anything you want as file metadata, this enables you to use a PyTables file as a portable compressed backup, even between radically different platforms. Take this with a grain of salt, since node files are restricted in their naming (only valid Python identifiers are valid); however, remember that you can use node titles and metadata to overcome this limitation. Also, you may need to devise some strategy to represent special files such as devices, sockets and such (not necessarily using filenode). We are eager to hear your opinion about filenode and its potential uses. Suggestions to improve filenode and create other node types are also welcome. Do not hesitate to contact us! Current limitations ------------------- filenode is still a young piece of software, so it lacks some functionality. This is a list of known current limitations: #. Node files can only be opened for read-only or read and append mode. This should be enhanced in the future. #. Near future? #. Only binary I/O is supported currently (read/write strings of bytes) #. There is no universal newline support yet. The only new-line character used at the moment is ``\n``. This is likely to be improved in a near future. #. Sparse files (files with lots of zeros) are not treated specially; if you want them to take less space, you should be better off using compression. These limitations still make filenode entirely adequate to work with most binary and text files. Of course, suggestions and patches are welcome. See :ref:`filenode_classes` for detailed documentation on the filenode interface. PyTables-v.3.1.1/doc/source/usersguide/images/000077500000000000000000000000001231437614300212005ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/usersguide/images/Q7-10m-noidx.png000066400000000000000000002377211231437614300237230ustar00rootroot00000000000000‰PNG  IHDRÐß}™SsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwXÇÿð÷Q¤‰ˆÒDª "¡Y±*ÊÙ±—¨`‰ýgKK5FQ£F£1š –ˆÆ{Á‚ X)@”.>¿?xv¿,wpwT1ózžfggf÷v—ÏíÎÌŠˆˆÀ0 Ã0 Ã0Œ\”j» Ã0 Ã0 S—°ša†a†aÀh†a†a†Q  †a†aF,€f†a†a°ša†a†aÀh†a†a†Q  †a†aF,€f†a†a°ša†a†aÀh†a†a†Q  †a†aF,€f†a†a°ša†a†aÀh†a†a†Q  †a†aF,€f†a†a°ša†a†aÀh†a†a†Q  †a†aF,€f†a†a°ša†a†aÀh†a†a†Q  †a†aF,€f†a†a°ša†a†aÀh†a†a†Q  †a†aF,€f†a†a°ú3“ššŠ°°0¤¦¦ÖvSjܵk×0wî\¼y󦶛R¦œœœ>} .ÄòåËk»9uʶmÛ°jÕªÚnS æÎ‹#GŽTkoÞ¼Add$ˆ¨Zë©k°dÉäææÖvS¤:wîæÎ‹ŒŒŒ*/ûàÁƒ˜;wn•—[¢¢¢0wî\Ü»wO ƪU«Ø1]KXý™øé§Ÿ`aa]]]´k׺ººhÖ¬vîÜYÛM«R.\À¢E‹’’"±,<<[¶lARRR-´L¶ÐÐPèééaÈ! BXXXm7©N9zô(~ûí·ÚnS ¶lÙ‚‹/ÊÌ———‡Õ«WcðàÁ077‡H$‚M¹ëܼyÍš5ƒ‰‰ lmmѨQ#lݺUî¶5 "‘"‘<šÇÏÏϳoß>¹Ëþ|óÍ7‡ššZµ×E‹!""Bîunܸ-[¶àãÇUÞžÿý[¶l©òr«CLL ¶lÙ‚'OžðiÆÆÆX±bj±eÿ],€®ãòòòзo_Ìž=&&&عs'BCCñË/¿ÀÐÐÓ¦Mƒ§§' j»©UâÊ•+X·nÒÒÒ$–µk×¾¾¾022ª…–ɶmÛ6dgg#..wîÜÁéÓ§k»I S§deeaÙ²e†••ÔÕÕËÍwww(++ÃßßèÚµ+æÌ™ƒŸþY¡ºÕÔÔÊ Ž÷íÛW#hU»rå ‚ƒƒkì.ìÓ§O±nÝ:<}úTîuÜÜÜàëë‹úõëWcËê¦-Z oß¾ìÉ\-at·}ûvœ={^^^Æ”)SЭ[7L:¡¡¡?~AYÎÎÎdjjJíÛ·§îÝ»ÓÒ¥KiÊ”)åÖÿúõk²°° %%%=z4­_¿ž¾ýö[êÖ­ùùùñùBBBHKK‹ ÉÇLJV®\I­Zµ"%%%Ú¹s§ LWWW244¤Î;“™™}ýõ×Ô¢E @+V¬ Í›7“ššyyyÑ AƒHII‰ (33“/ãúõë€Äb1éëë“ùùù‘‰D"úùçŸ%ê477¤=|øŒŒŒHYY™zõêEË–-ãÛ1uêT>_JJ YZZRÓ¦MéÝ»w|º··7‰D":sæL¹ûˆèþýû¤££Czzz´xñbZ³f µjÕŠú÷ïOÈ××—Ï{èÐ!@gÏž•(ÇÃà iwïÞ%}}}RVV&±XLË–-#+++©çwÉ}ïââBK–,¡‰'RVVéêêR=$êŒ'?~¼ÌílÛ¶-yxxÐ’%KhÍš5äææF***diiIÙÙÙ‚¼jjjÔ±cG255%Z°`5oޜФI“y_¿~MÆÆÆ¤©©IóæÍ£ 6³³3 8Pâó’—¡¡!µlÙRê²û÷ïêÝ»·Ä²M›6Áñ_–‘#GŠ'GGG\n;¹ÿñññ|Úœ9s 6Œ´µµiêÔ©Ô¿RRR¢ Ðû÷ïexyyêÓ§mÞ¼™&MšDÔ±cG*ÅÆÆ’¥¥%‰D"rqq!___²³³ã·óñãGúâ‹/¨qãÆǧÏ;—®Ý7nÜ F‘ŠŠ õïߟ–.]Jæææ€–/_.¨ÿĉ¤ªªJVVV´jÕ*Z¾|9™ššò×™¿þúKb 4ˆ444¨°°°Ü}ÉT-@×a[·n%´}ûörómܸ‘ÐŽ;ø4EècÇŽš?>ñé< äííͧqt½zõ(66VPöëׯIEE…¾úê+Azjj*ijjÒСCen3w1ŽŽ–XV^mggG©©©|ú’%K©ªªÒ… øô§OŸòfNnn.Y[[“žžžàÂ\PP@½{÷&---zóæÌ¶;::’£££ -??ŸlllHSS“^½zŧ§¤¤™™5nÜXÐn¬ ¹¤#FH$¢óçÏK,ËÉÉ!"¢ÂÂBrpp zõê ömzz:Y[[“ŽŽŽ`Û]]]ù/2DDTTTDvvv¤¬¬L¦¦¦‚@uýúõ€öìÙçq4 çÓ333©E‹Ô°aCJNNÔY:€vqq!UUUzöìŸVTTD“&M"‘HDaaa|ú­[·HUU•<<<¨¨¨ˆþøã@>>>ríÇÞ½{“’’=yòD°,,,*@‘³³3©««SLLŒ }ìØ±¤¤¤D>ìäåå%QöüùóI$ÑÓ§Oéßÿ= «W¯ÊÜÎ’Çgÿþý€~üñGAºšš M›6 Úݾ}{RQQ|~S¦L!ȧåååñALUЧOŸ&4oÞ<‰eçÏŸ'ôÿ÷2ë(@oÚ´‰TUU)))‰ˆˆ8@(""¢ÜZ$QDD„ ÜþùGêçȶ_ý5Ÿ¶`Á‰Dü †ÔÔTRVV¦.]ºªª*ÿÅôõë×€–.]*s»¾þúk q&úßufÒ¤I”——ǧOž<™ÐåË—ù´Ÿ~úIêy´|ùr@?ýôŸÆý8~ü¸ÌöqÊ  ;wîLééé|úöíÛ mذO»ví /¿üRP.×îÒ´§§' 7nÒ¹ýuîÜ9>íñãǤ©©I;w¦üü|:yò¤ÄgšŸŸOvvv¤¥¥E¯_¿æÓ ièС¤ªªÊŸ¯ùùùdeeEúúúôáÃ>o||·<66¶BõlÛ¶ ªªªøæ›oNкuk‰Gšàââ333AZÓ¦M1`À:tHðˆòÀÈÊÊÂÔ©S+Ô>yŒ1BÐ_²GSSSôìÙ“OoÞ¼9LLLÝ Î;‡çÏŸcÚ´ihܸ1Ÿ®¬¬ ooo|üøQj—y„……!** ®®®011áÓ6lˆ~ýúáÇR÷¯´.Ò¤¦¦âðáÃðôô”úˆ™øôðáC„‡‡£K—.°°°à—kkkcÀ€HKK“ÚÇnâĉPVVˆD"¸¹¹¡°°ƒ†¾¾>ŸÛß/^¼(£eË–°··çÿÖÒÒBÿþý‘šš*uÛ9>Dhh(ßå„#‰0iÒ$Ξ=˧·oßëׯÇùóç1cÆ LŸ>;wÆêիˬƒ“””„ÿýNNN‚îÚÚÚèÛ·¯ÌõËsçÎܼy“ïQr;&Nœˆ¢¢"œ?^b½Ñ£GK¤M›6 °k×.>ˆ°{÷nØÛÛ£sçÎ2ÛSò<ÉÌÌDBB\]]¡­­;wîHäWWWt‰DpwwGAA¢££ùô?ÿüzzz‚óMUUC† ‘Ù¦Šàº—YYYI,㎗Ò]ÐdùòË/OwßèСZµjUîzmÛ¶•ÈóÇ<—‡UUU~9Pü˜žˆ ¸‹Paa!Ö¬Yƒüü|„††?“IÉ}\–ˆˆ¨©© ®;¥M:ªªªüß½zõÁ À?ÿü0nÜ8ÁºÜ¾*¹UmìØ±ÐÖÖæÿîÓ§Dû¸®UÆ ¬;bĉ®@¯^½B@@ÜÝÝáìì,X6iÒ$\“lmm±}ûv\»v S¦L··7Zµj…íÛ·óy‚ƒƒñèÑ#L˜0M›6åÓ•””0~üxäççãÂ… Š»@¾xñ=zô@£Fø¼FFFèÚµk™û»&)2» Sy*²³0Ÿ*îd|÷î]¹ù¸å 6¬P=ÐÒÒÂôéÓ%–¥¦¦âÝ»wÈÈÈ\Èúõë'µ¬éÓ§ãøñã8xð ¦L™ ¸?[³fͪu waåØÚÚJMç–…‡‡ós¥xzz òfggʃ 0¸L%õêÕ ;vìÀóçÏéÚÚÚrBðìÙ3@‡*ÕŽÍ›7K´COOíÚµ¤qûµÿþRÓ¥õ£íÞ½»Dš»»;6mÚTî—>îŸddd¤ÄçrQQQ‚´¹sçâÒ¥Køå—_ШQ#üõ×_c¤áúÕK;FÝÝݞѡ$n;ÂÃÃ%¶ƒësYz;444àêê*QVóæÍáîîŽ}ûöaõêÕPSSC`` ¢££±mÛ6¹ÚóðáC|÷Ýw ”˜OžûÒ^R=ç>þ 7¥dbb">~üˆ~ýúI-ÕuÞs_v_½z%±,..NG^èÓ§öíÛOOO ‚¥²H»>þjjjèÖ­› ½~ýúèÔ©BCC‘žžŽ  k×®¨W¯.\¸€Ñ£G#((ÖÖÖpqqµµ5.\¸€>}ú ((êêêr]bccadd%%é÷ьѾ}{AZéÏ(¾v˜˜˜à‹/¾ämÑ¢ÌÌÌ$®U©ôuÆÒÒ 4´ïåË—PRR’8_ `oo/¸ÖsçbBB‚ÔkŠªªªÄµÞÛÛ—.]ÂÞ½{¡¡¡Ã‡CKK‹_ÎÝŒ¹sçŽD™\q®Lî:#íšØ½{w©_¤ÿŽIÆT  ë0¬à[^úް4TjBv"BJJ š4i‚&MšHäçÒJ¯Wò.fIîîî°¶¶Æ®]»0eÊ\¿~>ÄÚµkåTQ¥GpsuIÙ-‰ÛóáÀ‰‰‰ÔQÿÓ¦Mƒ]…ÚÅ 2)yg‚Ã¥¥§§ Òw…Êí[:À©ŠvhiiI|fÜߥëãÒK'€ô'(\åÍýÊwRg^™2e œœœiDÄéÑÒÒ’{d¿<ûG^¥÷·†††R·cêÔ©ŒA™S¸Í˜1ƒÆßÿÑ£Gc×®]ÐÔÔÄØ±ce¶->>={ö„²²2fÍš{{{ØØØ ^½zèÞ½»Ä €ôc«ôç]•ûO^\¹?–XÆ¥U¤nooo 2_ý5êÕ«'õI@iÒ®‡éééhÔ¨444$–qíÊÈÈ@ƒ  ©©‰Î;ów˜/^¼ÈßeîÙ³'óâÅ‹èÒ¥‹\SêYZZ"00………üS¤’äù\¹6:::J­£iÓ¦¸s爨Z®ïeµ±tû´´´¤^»›6m* e‹'N<âp×)uuu‰U\™M›6…žžžÄºS§NE›6mø¶Ò¯‰å=iæ¾$ZZZ–™‡©z,€®ÃÚµkMMM8p+W®”ú5;;þþþPWWÜmÕÔÔ”zçúÖ­[‚¿E"š7oŽüü|ìØ±£Òm‰D˜6m¾ùæܽ{;wªj™³sH[¿¦µlÙ@ñ#Jiw¬+ƒûÇ„#F–qÿ,¥=‚–÷¨ZÖ—¬’í?~|•·£ãžö”ž000¢ÁiÓ¦ 4h€Ý»w+4/qY”••1uêT„„„ÀÇÇùùùr/àú—~¢ò÷ßWªMõêÕCÛ¶m.ñ¥BÚþ« 5ÂÀ‹„„>ˆpëÖ-~¹¢TUU±~ýzL:_ýu…ÛÇu³(½ýáááHLLDÇŽ'wÇyÉ’%PRRâówïÞ"‘K—.ä“…ërQÙ.ÎÎÎ(((8Ö¹s¥dwîú-­+Puá®[¥¿¨Ý¸qC0(îvbllŒ?þøYYY2Ë~ðàæÏŸ777ìÙ³[¶lÁåË—ÁwÇŽ¡®®Ž]»vÉ|åv›6m ¦¦¦ðͧOŸBSS“Ý®i5>l‘©RäääDhäÈ‘tþüyŠ‹‹£sçÎñS$999QVV–`=nfŽ#FÐíÛ·éüùóäééIúúú#}“““ÉÐÐôõõé·ß~£7oÞPff&EDDÐÖ­[£Ååe=nÜ8~tÉY0dIKK#êÕ«>|˜‚ƒƒù)„Ê›…#22RPN|||™30H›fláÂ…€FMááᔕ•E¯_¿¦ÿý—¾üòKzþü¹Ì¶K›…ƒˆhÑ¢E€¦OŸN111OË–-#4jÔ(A^ggç2g(Ë™3gµiÓ†BBB(==^¿~Mþù'8q‚Ï·bÅ ~ä}tt4%&&Òwß}G"‘ˆ<==eJ›ƒèŸÁõë×éÙÙÙ3-p³pèêêÒСC)::šRRRhݺu¤¤¤D”Y'7cA¯^½èæÍ›”™™I tùòeš>}:l}øðLMMÉÔÔ”Ý^TTDb±˜êÕ«'˜­£,?ÿü3 1cÆPll,}øðV¯^M7–˜…#??Ÿš5kFººº´uëVzñâùùù‘……5nÜXâøâÊîÛ·/ݾ}›233)>>žBBBhÊ”)*sß—Oªªªüç.¯ .ruu¥[·nQff&ùûû“‘‘Õ¯_ŸœùÕÔÔhäÈ‘åH›…„;{ôèAOž<¡ôôtÚ¹s')4 lj'hïÞ½´wï^jРñ—žõ$44”TTT¨}ûötóæMzöì?­gééÃÊRrŽò”7 GÉ4Nzz:žžPZZݾ}›¤žCùùùü4›¥?Ó6mÚÒÑÑágÅ‘%""BbNY×™ÈÈH@k×®åÓbbbH]],--éÒ¥K”žžNAAAdff&1³ Qñ4{ŽŽŽ´ÿ~ºtéEEE•ÛÎòfá(9ÕGGG‡ Äÿ““C¦¦¦Ô¨Q#  ¬¬,º}û6ÙÚÚ’®®®Ä,ÿý7 .]ºPhh(¥§§Ó»wïèÚµk4gÎ  ¢âÙ‚Z¶lIúúú‚Y˜FŒAJJJħ­Y³†Ÿ¾ïîÝ»ôñãGzûö-]¼x‘¼½½ÿ³fÏžMhÑ¢EôîÝ;zûö-Íš5‹okéY8ŠŠŠÈÜÜœ\]]ËÝLÕcôg 77—æÏŸÏÿÃ,ù3gÎÁÜ»œììl~^IîÇÅÅ…Ž9"õ$}ñâ¹»»K”¯®®N‹-âóÉ@sS Y[[ ¦Æ“ÇO?ýDݺu#Áż:è‚‚Ú°aijjJì[[[Á\Øe)+€ÎËË£… ’H$”ëååE‚¼  ‰Š?===AùÊÊÊ´{÷n>O~~>”^¹P IDAT-]º””””ùF-1µYUÐ[¶l¡~ýúñÓ}qqéŽeÕyàÀþ‹_ÉsssºuëÏ“ª¢¢"1[bb"‘µµµ`:,iŠŠŠhñâÅ‚:ÌÌÌ(((H"€&*ž¸à©¨¨Ðï¿ÿ.õø"*À5j$±Íš5ÓòÐDÄÏÅ[zoYfÏž-8µ´´èܹsÔ²eËJÐDÅÓŒ)++óŸuãÆ),,L¡ÚÑÑQbq?Ò‚ˆ€€ÒÐÐàóˆD"š7ožÜ×ê  ‰ˆ=zDööö‚mhܸq™×ÏZ°` ›Æ¬ô—NYúõëG­Zµ’ØŠÐDD—/_æç5æ~LLL_ü8ǧ>}úðŸIÉiP¥©lMTüeÁÒÒRpYºt)??tiÿý7?ÏÉccc>0?~<‰D"‰c<55•,--ÉØØ˜Ÿî°¨¨ˆ¶oßÎ*ùÓ¼ysÁôs9994jÔ(AžvíÚñçTéÿÍÜÔ³§OŸ.w?2UOD$ã™Sgäåå!""‘‘‘8sæ 8€9sæàÇ”šŸˆpïÞ=DGGÃÞÞ-Z´@vv6¡¯¯/Ỉ‹‹Cdd$ÒÓÓall '''A¾¬¬,¼{÷|?PiBBBàææ†uëÖaáÂ…ÚÞŒŒ |øðºººÐÑÑáÿ666F½zõ¿­1%%M›6 ¼+,,Ä«W¯ ££]]]A¹‰‰‰ÈËË“Úÿ633‘‘‘xùò%tuuammfÍšÉÕÞ·oß({0È»wïðàÁäååÁÑÑQêôRñññ(**ªÐà§œœDDDàùóçhذ!Z·n-˜jŽóþý{Ü¿999ptt”ºPPP ÑFî3hÒ¤‰` !66ÚÚÚüÌ7nÜ@§N°cÇL›6 ÑÑѸÿ>Z´hV­ZIÌPV@ñÛÚ¢¢¢ðôéShiiÁÒÒ’dË}ÖjjjRÂ&''#==]æ1ËyõêîÞ½ 333888àÕ«W°´´„¯¯/V¬X!È›’’‚Û·o(~d_¿~ýr¯œœDEEáÙ³g¨_¿>š5kÆ÷¡”g?”Ô¿„††âíÛ· ¿ùùó燉‰ œœœP¯^=¼yóJJJ‚} MMM‰ãèãÇHJJ‚¡¡¡Ä ¹wïÞáöíÛhܸ1Ú¶m UUUÄÄÄŽò¼}û¶Ì·ª««Kü•““ƒ°°0¤§§ÃÙÙY0E˜,IIIøøñ#LMM¥¶ãdffâýû÷ÐÓÓã÷wNNi¥"** QQQ055…]™ÇaJJ ÒÒÒ$Êãê–v=+wž={b±˜O/ë:“ŸŸ7oÞð×Ü’òòòðøñc¼xñVVV°µµ-w0#·oêׯ/up'55©©©‚ýϳ\ßú’âââ ®®.Ñ-077wïÞŇбcGèééáýû÷ÈÌÌ”ÚETT444`ff;;;~bll,TUU¥^‹¹6—þœ²²²‰/^@GGÍš5+³þÓ§Oñøñc´jÕ 666eþo‹Åøðáaj  å”——‡íÛ·ãÊ•+ÐÕÕÅ Aƒ$:ùJŠŠŠ0räH=z~~~ðññ©í& ¸¹¹!22OŸ>•kpóù)@×U111eеåúõëèܹ3–-[Væø†Š!ª¨¨(ôZsæÓ‰/¾øêÏÏT›…CN™™™ˆÇÂ… ‘™™‰9sæ@SSSî5MII þù'ÜÜÜPPP 1Bº6¤¦¦âÇÄíÛ·‚={ö°à™aªÐ¹sçpþüy=zM›6ýä¾83Ÿž7âĉÈÍÍ•kú;æÓ‘””???<×@Ë©Q£FX¿~=ÿ·——:ôÉÐ@ñÈ÷™3gÖv3x™™™Ø·oLLL°yófL˜0¡¶›ÄÔ"555˜››×ú»ÊRQQ¹¹y…§3«J·nÝÂ?ÿüwww̘1Cj7,†)ÉÑѱÌyœ™O›‹‹ \\\j»ÿY¬ Gzôè‰'J¼¾”a†a†ù¼ý§æÎËË+sð çãÇxòä ÿ ]i–,Y###<3 Ã0 Ãü}öôƒ0lØ0XXX@MM ½{÷–š///ãÆƒŽŽlllиqc9rD"ߊ+ðèÑ#üþûïÕÝt†a†aæôÙÐ (**Âĉù· I3þ|œ:u —.]Bzz:fÍš…Q£F ^m½bŠܺu ÇŽL‰Æ0 Ã0 Ãüwü§ú@‹ÅbäääH¼r4##ÆÆÆ˜5kÖ®]˧£W¯^Ø¿?ž?ŽæÍ›ÃÒÒ’Ÿc¸[·nøõ×_kr†a†a˜ZÆfápóæMdffÂÎÎNîàà€‹/¬¬¬-X^Þ¤ú%íÞ½”H733«`‹†a†aªF\\œDÚ˜1c0yòäZhMÝðÙwá÷†8[[[Aº½½=QTT‘HuuuÁ¼Ý8<ˆ›7o"??_‡ÜÜÜJ§ÅÅÅñ?UQ^é4®lE׉‰Att´Ì|/^¼œ¼¥ó…††"&&¦Z¶ (~éDaaaµí?®|E×½~ýº`ÐkYù®\¹Â—/-ߥK—ªmÛàÊ•+UZ^é4®|E× ­t¾ääd„……UÛ¶%''#""¢ÊÊ+–À—¯ÈºOŸ>EXX˜Ì|/3_XX’““«mÿ=|øiiiUV^é´{÷îñå+²îµk× 3ß•+W””Tf¾K—.UÛ¶UæÜªês°tÚùóç+¯  €¯¿:¶-++ ׯ_¯²òJ§ÅÄÄ>EÖ•gÿ"00P"ßû÷Ÿ»wïJ½ñÇ”P;o¯äêê*‘¾iÓ&@>|¤oÛ¶Prrr¥êíÞ½;ÙÛÛWªŒò?~œŽ?þÉ•öìY:tèP¥Ë·µµ¥””…ë—×Ô©S);;û“+Μ9rm·¬ò­¬¬®[^^^Ÿdùò®W^¾Ó§O“‡‡G…ê—ǽ{÷hóæÍŸ\ùÑÑÑäëë[éò=<<èôéÓ ×/¯µk×Rddä'WþæÍ›éÞ½{•.Ÿ»Ï—’’B¶¶¶ª_ÙÙÙ4uêÔO²|yöŸ¬òííí©{÷îªÿ¿‚uá ¯¯ˆŽŽF£Føô/^@UUµJ^ §§Wé2ÊbccSmeW¦|kkkäääTºünݺA]]½BmGÿþý¡¢R}§BEË‹Årm·¬ò»víªpÝŠðôôü$Ë—w½òò5kÖ nnnª_FFFèØ±ã'W~Æ åÚnY廹¹¡Y³f ×/¯®]»Vëµµ¢åwìØFFF•.Ÿ»ϧ®®ŽnݺU¨~y¨¨¨ ÿþŸdùòì?YåWçyõ¹`4Š ÀãÇѶm[>ýáÇ022‚H$ª­¦ÉåS «³üªRÁÊ”/‹«µüªò9ÿ®nFFFrZ5]¾"tu¶_–ê0+Z¾¼_Zª»ý²°s·â>÷š‘õFñÅNGG>äÓˆááár1 Ã0 Ã0ÌÃg:++ ÿþû/àÝ»wÈËËC@@ÀÝÝõëׇ¦¦&¦L™‚Ÿþ...hݺ56lØ€””̘1£Òm033“è¨ÏÈoþüùüÔŒâ-ZTÛM¨³ÌÌÌ0f̘ÚnF5fÌö(¸ع[qõêÕÃüùók»u–¡¡!ÔÔÔj»Ÿ´Ïþô‡0mÚ4L›6 oß¾Åû÷ïù¿KŽ’^»v-&Nœˆ±cÇÂÔÔ'OžÄÉ“'áääT%톷·7¼3òÛ´i“ÌW°3eóóó«í&ÔYqqql$z%2 Ã0UÇÓÓ³Vž1 óùøì»pÔW®\a4Ã0L59wîîß¿_ÛÍ`æ3Áî@×yvíÚ•=.a†©Õ9Ÿwu‰ŠŠªõi>몢¢"<þ-Z´¨í¦ÔI¹¹¹l¡ ìt ILL¬í&0 Ã0uDXqlaå°˜E6@×33³ÚnÃ0 S‡°'’§®®Ž_~ù¥¶›Qg±˜E6@×èèhüõ×_xôèQm7…a†aF£Gð×_!::º¶›òÉct ©W¯6luuõÚn Ã0 Ã0Œuuu4lؽ¼L,€®!zzz‹Å°¶¶®í¦0 Ã0u@TTTm7¡Î***ÂÓ§Ok»u޵µ5Äb1{ƒ¨X]CX‡üÏÃ7àçç‡ääd¹òûùùáôéÓÕܪòååå!66ÙÙÙµR¿¢û¬´´´4øùù}Ó<žXXXT¨žò¤¥¥ˆ0}útÀóçÏqæÌ„††âôéÓø÷ßå.K__Ÿ)'$$÷ïßÇÈ‘#addħ7jÔ¨JÚÿ©HIIÁ’%KЮ];øøøÀÂÂ!!!8yò$<==qìØ1 2Df9AAAðññAÆ annŽ˜˜…ïd·oßsæÌùìö1Ã0 óßÅè:£>WÈÌÕ¢EL…kÈÏÞ¿Ÿ À¢Ü|††²ÛQººº‚G—ppp@`` Ξ=‹>}úÈU޹¹9~üñGAÚܹsqÿþ}Ì;;v¬ÒvJ´µµqùòetíÚ•O›2e BBBàææ†o¿ýV®ºwïÞxþü9š5k†›7o¢S§N ·¥gÏžèÙ³§Âë1 Ã0̧Šuá¨!Šv?`þG[[^^^€{÷î!!!£FÂþýû¥æ?xð F…øøx¹ÊOJJ¦M›Ð§OØÛÛ£]»vøòË/qóæÍr×Û»w/<==ѺukLš4 111roÓË—/1sæLtíÚmÚ´———Ô)cbbðóÏ?cÈ!°±±A§N0sæL¼|ù²Üòµ´´Á3ÇÕÕ¶¶¶xòä‰\ÝpŒaee‘H$÷¶•vòäIŒ5 oß¾åÓ:„Q£F!-- üñ Œ5 Ïž=“«Ü÷ïßcòäÉøê«¯ý«Ïž=‹±cÇ¢uëÖpqqÁ‚ ––&X÷Áƒ5j®_¿ŽÐÐPLœ8سgbcc1jÔ(œ={<À”)Sàää2»ã|üø¾¾¾‹Å°³³Ã!CpìØ± ì­ÿ5jvìØøøxÌŸ?:t€‹‹ 6lØ€¢¢"‰üYYYðõõE¯^½ààà€Ñ£G—ÙÿžˆðÛo¿aĈ°··Gûöí1qâDܸqC/55>>>èÑ£œœœ0nÜ8„„„H”·uëVŒ?………ذaz÷înݺaÍš5üµïÂ… ü~öôôÄãÇe$%%aÔ¨Q8zô(îÞ½‹)S¦ uëÖ8p Ô}¹qãFLš4 X·núöí‹V­Z!33Sî}ü©cƒ+Ž "¬³ÈÆèRSò‹Š€ÔÔŠýäåÕH+…ˆ`dd„§OŸbÙ²eaÙ²eˆŒŒD“&Mä*óâÅ‹X½z5ttt ‹abb‚“'O¢sçÎeL«W¯ÆÊ•+ѬY3tèÐGŽA»víððáC™õ8qŽŽŽØµk´´´`cc´k×Nð¼|ùNNNX¹r%ÔÔÔ0räHØÛÛãܹs¸uë–\ÛV!11¦¦¦PQ©™Pð÷÷Gzz:ŸöàÁøûûcáÂ…˜0a  ¥¥tèÐAæ€Ã/^ S§N8sæ f̘Áwùæ›oзo_œ:u ŽŽŽ "üðÃprrBRR¿~||<üýý±cÇxzz"%%;w†²²2RRRàïïýû÷ÃÕÕW¯^EË–-Œ#FàСC‚¶ÄÄÄÀÉÉ «V­BBBºté‚+W®`ذaXºti…÷›¿¿?Ž=ŠnݺáСC°±±ÁãDZpáB‰~èIIIhÛ¶-¾ÿþ{èèè Gxüø1ú÷ïU«V òæåå¡W¯^˜4i^¿~ www´k×÷îÝô¹}ùò%±eËÁÅÅ7oÞD=°eËA™×®]ƒ¿¿?¼¼¼°bÅ èéé!66K—.ÅÒ¥KqþüyôíÛOŸ>…‘‘Nœ8.]º>ç?Âßßûö탇‡rrrлwoÄÄÄ`ذaظq£ ÎË—/ãÈ‘#=z4öìÙ###|ñÅ ¢ ïóO DXqlaå°A„r ¦Úyyy‘••yyyÑñãÇËÌãååUf;z@rüÈ›OÚ/Ñ2óúRtttµì+CCC277¤¥§§“…… S§NÑ®]»sþý÷_@Û¶m“Zþœ9s]¿~O{÷îeee òegg“‘‘™˜˜Paa!Ÿ¾víZ@ÆÆÆôîÝ;>ýöíÛ€((€àsMOO'CCCjÞ¼¹`ý¤¤$²²²"KKKÊÍÍ%"¢ 6 ”YTTD?~”º}²lݺ•¯¯¯Âë^¿~М9sZÛg‘‘‘|š [[[ŠåÓ·oßNhݺu|Ztt´ Í7oÞ$}}}jÕªÅÄÄðùBBBøý““ç_½z•ÐW_}ŧ={–’’Ý¿_ÐÞ{÷î@§OŸæÓ_½zEÊÊÊÔºukAþÁƒ“H$¢k×®ñi¹¹¹4vìXRVV¦ðé#GŽ$555¹ö׆U«VQ^^eeeQË–-ISS“²³³ù¼Ó§O'ôÇÚн{wRVV¦Çóé«V­"´f͉:ß¿Ïÿ>bÄ@|ZFF9::’ººº`ß9’‡‡%''Q~~>uïÞ”””¨Q£FÆçß³g  6ðiÜç €<ȧgggSûöíICCƒâããùôAƒñurçLy\]]+tÜ3ÌÉñãÇ1 S6vº†tíÚûö탧§gm7å“—’’‚E‹aÑ¢E6l wwwôíÛ0fÌ4hпþú«`Ý]»vASScÇŽ•»>}}}hhhÒÔÕÕ1yòd¼~ýZÐõ€#‹¡¯¯ÏÿÝ®];ØÛÛãäÉ“åÞ==xð 1{ölÁúzzzøòË/»wï(î ?F/I$ASSSîíãܹs ,€–,Y¢ðúÕaܸq‚ÑÞP|×ZšþùÝ»wG«V­põêU˜››óË6mÚ‘H„%K–@MMOïܹ3Z·n-µ@ûöíáèè(µ.GGGþx´oß‘‘‘ü]Îèèh?~}ûöô¯W¯f̘ÂÂB…f=)MCCsçÎ…ªª*ÿ·»»;²²²ø.CEEEسg´µµ1räHAÆŽ‹ÂÂBìÙ³‡Oß¼y3lmmáãã#Q_ãÆééé8räLMMáîîÎ/¯_¿>FŒœœ8p@bý¯¾úŠ?nUTTЫW/¡sçÎhÓ¦ ŸÇ ­Û’––†Êÿ­®®ŽáÇ#;;[âî?P| ±—>0LÕðôôľ}û¤vd„Ø ÂÏŒ¾>PÑØèÈàÚµªmOEdddðÓ5hÐíÛ·Ç!C0mÚ4¾/®––Ƈ;wâíÛ·066Fbb"Nœ8±cÇBGGGîúˆ¿ÿþ;vìØ—/_âýû÷‚ÇÀqqq011¬Ó£G‰rzõê…‡âÅ‹eÎ8Áõi¼qã†DðÀêQQQèØ±#†Ž `àÀhß¾=<==1vìØ M/ôøñcôéÓúúú8uêÔ'p”þBibb===©°zõj :¿ÿþ» HŠƒ1©móó󑜜ŒäädÁgÓ¯_?¹ÛQºqãÒÓÓ¡££Ã÷ãÍÈÈÀ´iÓ$ò+++Ëݧ[š^½zA[[[ÖºukÅXmllðæÍäåå¡OŸ>| Íá‚_®/hBBRRR0tèP()•}ÿäÙ³g "ôêÕKj›–.]*Ñ_YYb±XÆ}9éß¿¿ ½I“&Ð××—ú9»ººJŸÜùöüùsAºH$BïÞ½Ë܆a˜êÂèRSòuu¹s+¶nTÔ§@›™™É5 oúô騾};öîÝ‹¥K—â·ß~C~~>¦L™¢P}kÖ¬Á·ß~‹Þ½{cæÌ™°µµ…¾¾>þùçlÚ´ yR:‡K›ÂÏÒÒ€äã’¸¹óòò$òijjbäÈ‘044Püå!66¿ýööìÙƒ¥K—bùòå0`~þùg¹ûx?{ö îîîPVVÆ… wmk[Æ’S&*++KíÇZTT„¢¢"äååIÔ˜mmm‰ƒ`gg;;;‰>óÍ›7W¸møö%$$ðˤÕ;|øð2ïp˃»›[^RRR@ê[N¡®®ÎçùðáC™å–ÄåçŽé’¸4.GEEZZZ‚4.H—ö…VIIIêç,­Nî|+}Î4hÐ@ð$çs›ÚnFTTT„çÏŸ£E‹µÝ”:)77Wâ&#ÄèÂ:äW½/¾øݺuÃîÝ»±hÑ"ìÞ½ OO·}ûv˜˜˜àŸþÜùÚ¹sg™ë\¹r]ºt¤q³” rÛÊ•+ѪU+™mÓÖÖÆœ9s0gÎ<þ¿þú+Ö¯_GGG¬X±BæúÑÑÑèÙ³' péÒ%´lÙRæ:Ÿª!C†à›o¾Áĉ1pà@?~\ÐõÆÚÚRóW.h6lþïÿþ¯Æê-‰;Þ‚ƒƒ%–ݸq999hÖ¬ÀÊÊ JJJˆŽŽ.·L.pp°Ä‹_¸zªë‹˜´Y>¸·P~J_þj‚ŸŸ{™JqƒÙËT*&11‘½LEÖº†Tö@,**#ó§°°²¯k~-Geßa­iÓ§OGLL .\ˆ—/_*|÷9?? hݺµ xÎÍÍ-÷ÜAAA‚¿‹ŠŠ„ÆK½ƒÆá‚à+µµ5Ö­[‡&MšàŸþ‘™?..Ý»wGff&agWö‹xˆ999ÈÏÏW¸]5iüøñ8tè‚‚‚ ‹‘‘‘Á/ëÒ¥ ^¾|‰Ë—/×X{¡¥¥…ýû÷£°°°Æê-IGGÍš5ý{÷ø;ÌœÀÀ@Å}½âþÄíڵÙ3gÊæÑÜÜúúú¸zõªÄ‹s¸2Köi®J=ÜÙþw¾uèСZêüT±à¹âØ›+‡ϲ±;ÐuÄ„ hÝzŸÌ|úúí+\ÇàÁnd¿.YUµ¡ÔÇÛµaèСÐ××ǦM›<ªªª°µµÅÙ³gqêÔ)xxxàãÇøúë¯Ë ˆnܸ={ö`ܸqÈËËòeËœœŒï¿ÿ^¢jI½zõ Aƒ°qãF4lØÓ§O‡ŽŽ>~üˆ°°0ìÝ»[·n…¶¶66mÚ]]] 0zzzHOOÇáÇ/µvIÉÉÉèÞ½;bccùéÕÎ;'ÈãêêÊßÁ…¥¥%<<<ùòòòøà…ë¿ËçiÕªUß>|84551lØ0¸»»ãܹsÐÕÕÅÊ•+qøðaŒ3?ÿü3z÷î 555¼{÷.\ÀãÇñý÷ßWi[tuu±zõjÌ›7£GÆúõëaaaüü|<{ö DïÞ½áââR¥õ–¶jÕ*Œ;“&M¯¿þŠÆ#88Û·o‡©©)¾úê+>ïÆÑ­[7 0{ö샃 qçμzõ LJ²²2–,Y‚yóæaÚ´iزe ´µµñÏ?ÿàÏ?ÿ„ ÆŒS-Û¢¡¡™3gb×®]ÐÕÕŹsç°ÿ~ØÙÙaРAÕR'Ã0Œ¢X]GL›¦X`Xnððp«özªR½zõ0iÒ$øùùaäÈ‘ äìØ±ƒ € ®®Žœœ¸¸¸ÀÏϳgÏ–ºÎÞ½{1mÚ4Ìœ9(,,ĤI“ðÍ7ßȬoß¾}øæ›o°dÉ,^¼ 4àçHæ±Åóðnß¾@ñ Éììl¨ªª¢wïÞØ¼ys¹uÄÅÅñƒ¼–/_.5Ott´Ì×±'''K*]ôÔ IDAT¼ù1 Šgt˜[ÑN÷•Я_?œ:u ƒ ‚››a``€ .`òäÉ8p ”••¡¡¡ÌÌL())aÒ¤IÕÒ–Ù³g£°°Ë—/Ç‘#G ¥¥…œœ¢Q£F‚™<ªË˜1c‡åË—ÃÀÀÈÎÎF‹-pøða¨««óy»víŠÃ‡cÆŒprr‚¦¦&òòòPPP€ `øðမ3gâíÛ·øá‡ðÇ@]]ÙÙÙhݺ5:Ä÷Å®jÜÛBõôôÛqìØ±J½Ð‡a¦*‰HÚ(¦Jy{{#77·Ü¾™ÞÞÞØ#»ÇC$ÁÖÖVîuV­Z___\¿~]fÿç7oÞ )) -Z´L—””„ÀÀ@dff¢}ûöppp@ZZâââ`mmúõë(øöí[´jÕ ¹¹¹¸víâââàìì,u°Øýû÷ѨQ#©Ã^¼xððpÄÅÅ¡I“&°³³“Øî/^àÎ;HLLD‹-àââ"×vÙÙÙxòäI¹ylmmùn+yyyˆˆˆ@ƒ øþ¯PPP uª1NÓ¦Meâ*¹Ï¸A)ñññHLL„Ä ]?~ ¾\ÛŒŒŒ`dd$Èûúõk¼ÿ†††ü ÊÂÂBÜ»wHOO‡™™œùÁ™@ñ¬/^¼€………ÄÓnßIÛ6îø±··— “’’pïÞ=<}úÚÚÚ°¶¶†³³³`ûbcc‘––‡r÷Pö±“œœ,q\râââpûöm$&&â‹/¾@‡$¦h䤧§ãÎ;ˆˆˆ€ŽŽZ·n-µ›Ï‹/†äädØÛÛ£C‡OYÊÚ®òöséÏ9&&–––ðõõŲeËðàÁܾ}–––èÚµ«ÄqÌÌLØÛÛ—³ÿÇÍÍ nnnrøT°A„ÇVÎèÑ£¡¦¦öŸIÊÃèàíí[·naáÂ…prr‚“““Ô<  •––+++888HôKf¦î(@WG[hoooö?¡‚rrr0wî\ÖZA÷ïßÇýû÷±~ýztèÐå`]8jˆ™™™Ô»0LÅDDD 00ÇŽCZZ¶nÝZÛMb†©R,x©86ˆ°b6l 6ˆPlŽbdd777™}Nùܼy+V¬€H$ÂÞ½{Ëa‚a˜OŸ’’tttýµ†©Ypss“è.ÇHbw ™:i„ ˜0aBm7ƒa˜*bffVîKˆ†a>%ìt ©©72 Ã0ŸnêHFqEEEü+ìű˜E6@×ö&B†aF~~~µÝ„:‹{!S1,f‘Ð5„uÈg†aÁVDX9,f‘Ð Ã0 Ã0 £@3 Ã0 Ã0ŒX]CX‡|†aFlaűA„•ÃbÙX]CX‡|†aFlaűA„•ÃbÙX]CX‡|†aFlaűA„•ÃbÙØ‹TjÈ•+Wàíí OOOxzzÖvs>Y—.]<:jР,--ѤI…ËJKKÃõë×åÊ«¡¡WWW™ù~ýõWlܸgÏž…¥¥¥Ìü666}úÈ•×ÜÜ1112ó}øðOž<‘»Ø“'O/WÞª”šš ggg¾ H$ŒƒÊõÊä   Ìž=QQQ(,,Ì™3?þø£ÜmQtŸ•–ŸŸ'Ožàýû÷ZÿSòæÍ999 "lÛ¶ ½zõ‚¹¹9BCC±qãFü?{çÖÔõÆñoØ {/e(¢ .pPATD°¢b­‚­ ·uWëh­PG­{·ŽŠEÅU·¢‚¢‚8²$² ;ç÷¿¤Æ$$`Ïçyò<äœ÷Œ{sCÞûÞwœ9s ,À¶mÛDΓ6mÚ`Ú´iPWW—È’>a¸ººÂÜÜ\‚#¡ÔÎù–ä© ‡ÔÔTôìÙ={öÄõë×qûöíFÜ!E\^½zEoZ$„Íf#11;vlî­´J***¾¸ßÅÆ†*ÐR¢1òW†¬Äÿ@ÑÀ"n[¥A%Ò­Ó±;m7¿MÄ…ã´Æ•kWðí/ß"·. [ÛÆÙ²qîã9¸ wÁ“ëO   Ð uêB^^žû£Ñ©S' 6 ïß¿Gdd$Ο?/ö±ªª*œyÚ8–z[[[¾¾üü|¼{÷òòòèСƒXÚ¼¼<¤§§ÃÆÆ¦Þ7/l6ÉÉÉ`±XèÔ©Sç4??¯_¿†––Ú·o9¹º¿ºúúúxýú5 ·mРA°±±……Nœ8!–=vìXŒ;%‘mll ccã:eòóó‘’’+++¨©©Õ{Ï)//Çëׯ¡¦¦ žó ˆÊÊJÄÅÅÁÔÔúúú<}UUUˆ‡H¥”Éd"##VVVÐÐÐhðqH‚8ç;77ïß¿GçÎ^·gÏžåþœœLèf"$$„úAK'ˆúAKFvv6õƒ "” ½“±ïÊ>Ù ì¯hW5pâÌ ‰×`±X˜½z6òü)D‡àMÇ7øaù¯!)~~~j-ÒL& ˜0a‚@ÙÉ“'C^^^l׉Ç£[·nÐÖÖF·nÝ`ggUUUÌ™3,Kà˜¼¼<ôïߺººppp€ºº:vîÜ)Öz„üþûïÐÐЀ••ºví UUU,_¾œç±;Pk1ïÕ«tuuÑ»wotêÔ ººº\ ½0ddd*&&&°³³CAAØóJHH OJ®%K–pÛtïÞÚÚÚb»‡DDD M›6pqqA^^€Zו±cÇBUUhß¾= xB¸|ù2 BCC1}úthhh {÷îØ»w/bccÁ`0ðÇà矆¦¦&all ñ¯]»sss¡gÏžÐÔÔ„··7rrr$:g`0B_BÇrη —$&“ '''èéé¡k×®ÐÔÔÄÁƒ%Ú#¥é¡Ê³äР†A•gÑP t+aè~¼·y_§L¾M>–o[޼¶y­ñ&æ RõS*ϪÚUáîûÍßîß¿022‚¡¡!|}}qòäIlÞ¼™Ç¥¤¨¨GÅðáÃÅ~„ '''¬]»mÛ¶ÅÛ·o-[¶@VVV`*¤1cÆ`ðàÁøã?PRR‚Ÿ~ú Ó§O‡šš¾ýöÛ:×›3g¶mÛoooøûûCGG{öìApp0***°~ýzÀÕ«W1oÞ< >.\€µµ5²²²píÚ5ÈÈHv“ƒ/^ÀÑÑQ¤[øøø ]»v8yò$rssñÇ`Þ¼ypqq©Óß}çΘ5kFމC‡AQQUUUèÛ·/2331yòdŒ3IIIرc|}}qëÖ-|õÕW<ó¬\¹ÆÆÆ8vìttt ¬¬ÌíÛ¾};Øl6V®\ ;;;ìܹçÎÚ5k°nÝ:®\DD†Ì;NNN8~ü8þúë/ :>¬÷ç5þ|øûûó´eggcêÔ©ÐÔÔëéˆ ¼½½1räHìÝ»yyyXºt)¡©© ‰æ¤P(”ÿ"Íÿ J‹‡Ov"„d·¹ot!H²Eh/Z,¯&¥¥¥R‰È/((À¹sçpúôiÈËËcÀ€€   œ}úˆ}lŸóý÷ߣªª ¿üò‹Äs4&]ºtAXX÷½¥¥%<<N:…Ñ£G×ë¼ØÙÙÁÎîß/|YY\]]!##ƒsçÎI¬@ÛØØðXø/\¸}}},[¶Œ*Ð …R¨ ‡”hh¡jU ©Ÿ¸+(-&Oä%þ‡ôôtÂÐÐúúúP« uîÜàæækkkìÙ³‡gìîÝ»aaaz¯›œœŒþù‡ÆЮ];TWW#55•OvàÀ<ï•••áâ₤¤$¤§§ ]ãâÅ‹¨©©Áœ9søúƇÊÊJ®ÇI«÷óÏ?ãåË—õ>žÏY½z5Ο?iÓ¦It~šŽk‡¾}ûB^^^à9¯¨¨À7ß|ƒ 6`Û¶mX¿~=›ÊÙ³g¡©©É½^8¨©©aÈ!xòä Øl6Oß Aƒ„ÊxzzòøcËÊÊÂÍÍ YYY¨¬¬ÄÅÅ!99“&M⻡ô÷÷‡ŒŒ ?~,Æ™!ß~û-¢££qäÈôêÕKâ¹ÜÝÝyÞkkk£{÷…Z ´¡äÐJ„ ƒV" µ@K‰†þ8y¸xàÂÝ `·g *ÛÆ_óÿ’h—q/1ê—QÈ3«Ã„ hÉjAV¶?Ò¦MîãkNèÁƒó¹dL›6 óæÍÃýû÷ѧO<|øÏž=ÃÚµkE} “Éĸqã PQQžžJJJÔ~vŸGÂs,áŸ2hÐ üóÏ?HNN†™™™ÀµÞ¾} ðòòâë㤙KJJP«@Oš4 ‡ÆÁƒaee…ï¾ûS¦L¶¶¶ØÇÔæ^µjÆŒƒíÛ·×klSò¹K…ŠŠ ´µµQ\\Ì'ûûï¿sƒf̘Áן€ÂÂBYZª««QSSƒ¬¬,nnduÞH|¾7 Ö/ÍfƒÅbAAA €Í›7cëÖ­|òl6[¬ôˆu±hÑ"œ>}7nĈ#4—°ëöÑ£GHNN†Aƒæ§4.4ˆPrhaàA„¢¡ ´”hè…øýøï±õÐV¼1#ÔGÙô‘)vØ CUÉrB:ÂNη nš‚e´b´ðcÐÍ/.â’âÇÄž={ЧOìÞ½òòò˜8qb½Ö›4i"##±k×.¸»»ÃÂÂ222øí·ß°dÉ®bû)¥¥¥|m…»®lœ¿ÿþªªªe8Vvƒ½{÷â×_Åpúôi,]º?ÿü3®]»&¶+ÇÎ;1þ|øúúâï¿ÿ–غ)¨Ïؘ1cpûömcÀ€èÚµ+O?ƒÁ€ 6lØ tŽÏo<45…\èbîs£¶xñbôíÛW LC”ÒÝ»wcÆ ˜>}:~ø¡áÁ»’^·”æ*Ï’CƒUžECèV‚²²2þ\ÿ'ÆÎ‹tçtàÓ Y€a”!æûÏop®Ýc»á«_!É> lãO¬Ýl@3FÃ-†ãëá_7hÆBSSþþþ8vìV­Z…ãÇcøðáõ**SSSƒ7n ÿþ˜:u*Oßç™>%22Ççi»vípý•acc V©rqqkúúúX´h-Z„¸¸8888`×®]b)Ðû÷ïÇŒ3àåå…ãÇ·ˆÀAI±´´Äš5k0pà@¸¹¹áÊ•+%Xœ }œ4hÞ½{‡¨¨(¡ãþùçž4p>|ÀÇÑ£G:³øúúBEEË–-ãKYÔæ/æøé ²¶oßÚÚÚ\׺8tè¦L™www„……Õia,..Æ®]»pñâE‘ó6'íÚµÃíÛ·add„âîÝ3ÂŒ7øí·ßŽt>J—.]`oo­[· ôÛ&„M…Xqqq=z4ºté‚ãÇ7Ú ÁåË—yüÀ“““¯¾úªÞnA …ò_¦õš£ZåollŒ+'®¨UÚtuuý‘¼ŠŠ n¯Í ›——UUÕûx·gÏžèÞ½;bbb`iiÉ$%  6 'Ož„ŸŸ|}}ññãG¬_¿ÎÎÎxðà@jUUU 4'NDii)7ÕÝÚµkë\ÏÜÜë֭ìY³Ð­[7Lš4 ffføðábccqìØ1$''CSS³fÍ»wï0tèP˜››ƒÉdâüùóÈÉÉÁâÅ‹ë\'11'N›Í†²²2_`lݺzzzjË?ÁÓÓC‡åÊroJ>~ü 6ã“Ɉ].½±022ÂÍ›71hÐ xzzâìÙ³pwwǤI“†eË–!&&C† ¶¶6ÒÒÒ ‹Åµ¶6rrrØ·oÜÜÜàèèˆÙ³g£K—.`±Xxûö-Ž;†õë×Ã××·^óNœ8EEEèׯŸÀ‚7óæÍ“¨J›ÍưaÃðÍ7ß   6l€¢¢"BBBxäN:…S§N>| 6µ'¨òðáÃRõ”R7´¡äÐJ„ ƒV" U ¥DSD¸^1­)¶UÊÊʪÞÊz@@bbb0yòd‘Áƒ°¶¶æIK·sçNÈÉÉáÌ™3‡­­-V®\ ]]],]º”GVGGÖÖÖ8|ø06mÚ„E‹!''öööøçŸàææÆ³žµµ5ŸEzÆŒ°··Ç²e˰bÅ q«/NŸ>›ÍÁÃÃÛ·oÇO?ý„òòrÂÝÝ¡¡¡Üê€Â¨®®†••àõë×e8™$€ÚêÖÖÖhÛ¶-LUU7+çx€S½}ªl ƒsÎ>ýg¬¯¯kkk.%VVV<ûàìMWW—Û¦§§‡7nÀßß ,Àààà€‹/bÛ¶mسg"""PYY‰6mÚÀÉÉ “'OæŽWUU…µµ5ÔÕÕùÖWRR‚µµµÀkŸsý|jîÙ³'âãã±páBìÙ³YYY`0055Å!CàèèÈ•511ážÃºÐÓÓƒµµ5®^½*°ÆŒPTTäžÛO•YAmœó†åË—ã‡~@^^ŠÞ½{óÌÏd2¹Ÿ±¢¢"¬­­¹Á­ÆPD(94ˆ°aÐ BÑ0ýOØäâîÝ»èׯ|}}Z£8å©é?Ëúãïïððp¤¦¦6(`‹Íf£¢¢‚/ç³(ÊÊÊê=æS ¡®®^§ò_TT$PÙ£§ººeeeR¼>CYY¹Åû›7ôºmm¸ººÂÕÕ«V­jî­P( ¢<­ŠÆŠ`ȉŸmJ\ÂÃÃÎÕY¨N"œ–ýþ ‚^ˆMCtt4Nœ8%K–48—ŒŒŒD EC• ‘2Ty®?rrrRWž4Ëš’ð_Rž)”/…V ²gƒ!Ï€Zw5¨÷T‡¬zãMsŒ|£E84ˆÒ*9sæ áìì ccc,[¶¬¹·D¡P(J“RSREÀf±Qx§é¤#çd*ÒháiCh)A«ú4.puuÅÞ½{+•²â …"Mh%BÉù+’‚¢GE¼l 4¾N~©i<\ª³ˆ†*ÐR‚–Ém\úôéƒM›6aâĉ<Áe …ò¥ðyvŠøp‚¿$J_–¢¦¸F`ŸzOu0dÏ'šê,¢¡ ´” Ñ¬ …B©4nFr¾ÄJ„Å‹¶3äPí.¸²­¤PE4T¦P( …BiÁTdT "C°[E»6m#ê«”¡ 4…B¡P(J ¦èa‘Ð>u'š¥©9  ´” ù …B©4ˆPr¾¤ Âê¢j°^²ö)™+AÁ°ñ+SE4T–Ô!ŸB¡P(õJΗDXü¸Xh† u禱>SE4T–Ô!ŸB¡P(õJΗDHª ŠcÊiÉAÅZ¥IÖ¥:‹h¨M¡P( …Ò)y^6‹-°O½—:ÐøÕ¼)bBh ¥¼ÿQQQ(//K>** IIIM¼«–M}ÏÙçTTT ** ¼3铘˜ˆG‰%ýÅøpR(ÉšºNUÇÆM]G©rͽÿ Ô!_<¦M›†‚‚î{uuuXXXÀÇÇ666õš+11Ë—/KVOO[·n)wèÐ!,]º èÔ©“HùÞ½{# @êb !ˆŠŠÂ©S§ðèÑ#dffÂØØ½zõÂÂ… add$Ö<•••xþü9bbbðôéS`èС˜0a‚Ø{©ï9ûœ¬¬,ôîÝ+W®ÄªU«ê=¾%±|ùr„‡‡‹u3áîîWWW„‡‡7Ù~Øl6^½z…˜˜ÄÄÄ€ÉdÂÁÁK–,i²5)âóêÕ+‰¾3”Úk;11;vlî­HLYJ*³+ö©9¨AF©él PTTl²ù¿¨-%ê?}Ü8ä>y¹º?²LEE\yô22õÿbíÚº[¶@WI©N¹7åå»}[l%¬>„‡‡#//«,§§§#//Ë—/ÇêÕ«ÅVˆ ´´±±±|@~~>ÌÌÌ ôÉqš˜˜4δ²³³Ñ§O())ÁÑÑÎÎθsçîÝ»‡½{÷"** ¶¶¶"ç ÅĉyÚ ë¥@ëèèÀÚÚšþ3n<þŽŽŽàúõëÈÍÍÅÔ©SŽùñÇqùòe¼|ù©©©Ø±cnݺ…_~ù¥ÎµØl6üýýñæÍ;v ÙÙÙHMMEXX²²²Ä•=rä=z„Ý»w#''QQQHLLDrr²Äî'·oßFbb"†*ÑøÆfñâÅØ»w/233‘žžŽ‰'"** GŽ:¦  žžž8{ö,NŸ>3fRSS1fÌXZZâÎ;x÷î233±jÕ*œ:uJ`èòåË1þ|"11‹-âö…„„ÀÃé©©ÈÈÈÀßÿ‚‚,[¶ŒgŽ5kÖàÀXµj233ñæÍ¹té¹téÙ¾};qss#ˆ¥¥%)..&„’––FdeeÉôéÓyÆÏ™3‡0 ’””$p~Ahap¬Š999Ü6Ž5uĈ|ò&&&DEE…°X,n>³@Ÿ{öŒ S§Nå¶}jNKK#¶¶¶D__Ÿ„öí H<ø9ÚÚÅÇãYFºþ¿mƒæoÞÜ(ó‹âÇ2d÷½¬¬,|}}±nÝ:®ßoÛ¶máåå…#GŽ`ýúõPQQAyy9:wwwXZZÖkÍwïÞ᯿þÂÛ·o‘šš0™LnŸ®®.¼››ßîîî8xð ’““…f¸xòä ÀÔÔ§OŸ!„°Ùlhjjâããakk‹Q£FañâÅðôôÄ„ 0räH±(?§¨¨C‡ŇCCC‰æilüüüxÞÛÙÙA]]] •4..½{÷†ªª*¢¢¢`aaÁÓÿøñchkk£¸¸˜{nÙl6!°²²ÂÍ›7Áb± ¢òoå.///¡{ûúë¯ù,=ýû÷Ǿ}ûPZZŠ6mÚàùó稨¨@·nÝpæÌîz„”••AVV¯_¿–äÔÔ “É„——ÔÕÕÁý^”••!11vvv000àÃñùòe£ï‡Ò4Ð BÉi­A„eoËPõ±J`ŸZ750ä¥S9…І*Э™ëÖaÛ×_cI~>.ÉËÃméR(Ö#½›8ÌÍËÃݺá@j*˜ `mmݨkÃÈÈ/^P›º]»vƒÝ‚‚‚pîÜ9?~'Nĉ'ŸŸ/ÔíB—.]‚··7TUUѹsgØØØ@OO/^¼ÀëׯQRRÂ7†X÷)NNN8xð 222„*Щ©©€U«V | §©©‰¼¼<€¹¹9þùçlܸÁÁÁX»v-ììì0cÆ Š®´´^^^xúô)Ž;ÖbÜ7ðÝè0 (++£ªŠÿäÚµk(,,ÄÊ•+ù”gHJJBee%_Ú=êêêøøñ#]—+Œ ›°6mÚwœ"9aaa8{ö,Ÿ¼ªªªÀë§!°X,x{{#//wïÞ…±±1·/==„ÇÕ¹sg¡7' ¥e ´pŠ j=›6u¥~Pºò©z»v8Ò€ÌÂÐÖÖF»Áƒ»gþÖ×—šõ¨MÁæàà RÎÓÓ–––سg&NœˆÝ»wÃÐÐ>>>õZï‡~€‚‚^¿~Ícµ›5k–Ð1ÏŸ?çSR8þØuYw9ÊN||¼X¹§ÝÝÝáîîŽÌÌL„‡‡ãàÁƒ˜6mdddÄJ WVV†áÇãþýûøûï¿1räH‘cZ*³gÏFff&V¯^ YYYüôÓO<ýÆÆÆPSSãZùÅAÒ”Ÿ® ;vì¨WnlIa³Ù7nž>}ŠsçΡk×®<ýœ4tŸÇµORŠŠŠø,Ó ¥eP•S…²¤2}*T §IU¶– "”]‰pæºuðkÓ®õ°DÖ—¹k×b…¡!Фh}® S§NETTŽ=Šû÷ïã»ï¾;•PkI|ýú5†Ê£XBpæÌ¡ã"##ùÚ®^½ 999´oß^è8;;;À•+WÄÞ#P›nlÆŒ¸sçtttpøða‘c***0bÄܸqþù'ÆŽ[¯5[222Ø·of̘+V`éÒ¥<ývvvxñâÒÓÓ¥¶§.]º"""¤²Þ‚ ŽÍ›7 |’ ¦¦SSSDGGóTôj¯O­º2Û D(9­1ˆ°èa‘о¦.œò9´z²h¨-%Z‰ðsú¹ºÂpèPLnë3mmmt;VªÖçú2qâD(**bÒ¤I`0˜4iR½ÆËËËÃÀÀׯ_Gaa!·}ÿþýu>ê¾rå /é™3gœœŒñãÇ×™ŸÙßß666X´hîß¿ÏÓWYY‰ãÇsóñþóÏ?x÷îLJJ yÜQYY‰‘#GâÊ•+ؼy3ÆŒƒòòržù$×wVVœ1oÞ<¾¹8ò••µ%ekjj¸m555ua0ضm,X€Ì™3‡{+V¬Œ?YYY<ã²³³ºX4333Lš4 'OžÄ–-[xÎ)P›»¹±” ;v`Ó¦M˜;w.7óˆ æÏŸššlܸ‘ÛVPP€={ö@^^žïÉJEEÊË˹?˜l6›ûù r¥¡HæÞB«…S‰°µÀ.c£ä™`w/#(™Õ]!¸±ilåK„>Má¿ÿèQÈÊÊ6ú¼ŸòÓºuM¾FCÐÓÓÃÈ‘# OOO¾±¢X¶lfÍšccc¸ºº‚Éd"++ .Äúõ뎙9s&úôéƒ>}ú ´´÷îÝCçÎùŠ—|Ž‚‚Ž;ôë×]»v…™™>|ø€„„   JJJ Å¡C‡Ð¥KXYYáýû÷xòä D®óòåK\¸p@­ëÃìÙ³ùdRRR`nn V‰zøð!7‘“Éä«N·mÛ6lÛ¶ ¸ ´áŽþüóÏ`±Xؽ{7°wï^Ìž=VVVppp€¶¶6ÒÒÒðòåKôíÛ·Þî=â°aÃ0™LÌ™3›6m‚­­-X,‘žžŽððptêÔ©ÁëlÙ²pýúu.NwîÜššf̘[·naÍš58uêÌÍÍ¢¢"lÞ¼VVV<ã xn¯^½ eee@@@ dkFè¹—œÖDXS R%¸rJsN¡A„¢¡ t+FŠ­´•çåË—×{ÍÞ½{#44T¬àAoooòüs˜9s&:vìˆS§N¡¤¤îîî9r$ ¡­­ÍLæêêŠàà`L™2žžžˆˆˆ@jj*‚ƒƒ1uêTn.^ÁÁÁ\· vvvxúô)þþûo<{ö iii°µµ…ŸŸ†έַf͸¸¸ ::ÙÙÙpqqÁªU«àââ"ÒmllŒààà:e´´´xþæ œSSS«sž~ýúÕ¹ðï9Ó××ç¶ 2ššš+=._¾œG–³·Ï×Z½z5:uê„ÔÔTÄÇÇÃÎÎpwwDZcÇðòåK¢_¿~˜?>† Ækmmàà`>bàßs'èØ8×GÁ œ?/^ÄÝ»wñæÍhkk£_¿~2d¯ü7ß|ƒ=zˆÐ BÉiéA„E‹¶3äPëÞüîTg U ) …B¡P¤Dyj9*³*ö©Ú«BF™ªf­ú)Q( …B¡H‰–šºŽR?¨-%¨C>…B¡Pê "”œ–DX]P Ö+–À>%K%(è+HyG‚¡:‹h¨-%¨C¾xTTT ¼¼œû"DpŠq „ðÌU×KÜW®\Á´iÓÄþ<§M›†ƒJ| …‚ÌÌL<|øïÞ½CMà q`³Ù(//Guuu#î°epòäIL›6 eeeͽ … "”œ–DXü¸„-øwMÃYCÊ»ÕYDCh)Ñù/^¼hô9?çåË— Rºê‹™™”••y^ÖÖÖXºt) ê5×Çùæö²¶¶kΧOŸb÷îÝÈÏÏK~÷îݸqãF½öݰX,ÁÔÔ¦¦¦pvv†……ŒŒŒ°mÛ6±çyùò%fΜ‰Þ½{CUUÊÊÊX°`Aî¼y¸wïvïÞM­.” "”œ–DÈ®d£ø‰ÔuÚrP²R’òŽ„CƒEC+¶RX,\‡ Áýë×ÅVþ$á›™31mÂL l²5>GKK +V¬$''ãôéÓ Á7pïÞ=ÈÊÊŠ5O»víÌÓ{÷îaúôéhÛ¶-·]C£åÜù7EEEؽ{7¼¼¼ðã?¢C‡¸qã>ŒY³fAFFÓ§O9Ï£G°cÇtìØÎÎÎÍr3@¡P(_%ÏJÀ.c ìSwRƒÁòŽ( *Э”5[¶ ð»ï0{Í\9|¸IÖ¸yçÒ¬­ñÇÑ£˜<~¼ØŠkCQWWÇܹs¹ï7oÞ [[[<|øçÏŸ‡¯¯¯XócÉ’%ÐÔÔ„……|}}Eºï(++ó(Ïzôè{{{¤¤¤ˆåˬ¥¥5µ†E…ïÙ³®®®ÈÍÍÅš5kзo_¨««#&&PUUÅm×ÓÓƒ½½=æÍ›‡ÒÒR¾¹²²²0nÜ8˜™™¡sçΘ2e ŠŠŠ0lØ0,_¾œ+—––WWW„†† ÝO^^^û~ñâ¦M›†.]º@[[íÚµƒ»»;nÞ¼É'ûûï¿ÃÕÕ, Ë—/‡³³3ÔÔÔ˜˜(tþÐÐP¸ºº"-- üñúõë===¸¸¸àÎ;Ç„……aРA022BçÎ1aÂddd”ŨQ£Ð©S'hiiÁÎÎsçÎåóóþóÏ?áææ}}}ØÙÙaêÔ©øøñ#̧çóÚµk:t(ŒŒŒ0xð`œ;w““ƒ3fÀÆÆfff˜={6ŸKLHH\]]Q^^޹sçÂÞÞ¦¦¦5jÒÒÒxdáêêŠS§NáÚµkðõõ…¡¡!/^,ôœ~iÐ BÉiiA„åIå¨Ê­Ø§ê¨ Å–¡Ž…‡‡#00·oßnî­´xZÆ'öÀÜÜÛzZk¶lAº§' {Ä|¿z5^±Xú:tíÞ˜˜JJ(ûê+l>~\ª¾ÐŸÃQv”””`hhˆªª*£ªŠ÷RMM Ö®]‹òòr‰5÷ƒpþüy¸¹¹aåÊ•ðóóCzz:¾þúkìÝ»Wà˜¹sçâôéÓ5j¦L™‚'Ož OŸ>ˆŠŠ¹Þàää„ÈÈH¸ºº" OŸ>Å€x¾øøxôíÛGŽ““‚ƒƒ1~üx|üø bÛçTWWãÝ»whß¾=ää¤ó*99·nÝÂ÷ßÐÐPôîÝÓ¦Mƒªª*ÊÊÊЧOüôÓO¨¬¬Ä¼yó`hhˆÍ›7£wïÞ< Xvv6zôè3gÎ`äÈ‘˜6mÒÓÓ1dÈܼyqqq\Y‹…[·nñ)eŸî§²RpV.\ÀÇáåå…_~ùˆ‹‹ƒ››"""xdß¼yƒ[·naìØ±ˆˆˆ@ÿþý1eÊ() ÷iLKKí[·0{öl,[¶ VVV0`¢¢¢0`À>å{Íš5ðóóCAA~øáx{{ãܹs°··ç»¡:pàzöì‰ÇcäÈ‘øõ×_1xð`œ>}………\¹™3gâûï¿›ÍÆ’%K0`À:tööö<çŽs><ˆáǃÍfcĈ¸ÿ>ÆŒƒèèhx{{ãÒ¥Kðòò‚’’¶nÝÊç/ÿêÕ+ܺu ¾¾¾ˆ‰‰A`` Ƈ˗/£gÏž>>ܶ„„€ ÝOVV·s]äççsÛJJJøÆ555âààÀÓ>uêT€ôïߟTUUÕu:¸p®%RXXÈm #È?üÀmKJJ" ÄÙÙ™TTTpÛ?~LrÛ233‰ºº:qppà™—Bª««¹×ó£GƒÁ Æ #555\™ˆˆ€Œ7ŽÛÆ9ŸÈãǹíOž|øÀ×Ïd2ajjÊÓöù1Šƒ——Ï{555XZZ"77—ÛÆ¹n Ä7~ðàÁxóæ ’““a``À•íÙ³gëräÜÝÝyÚeee1hÐ œ8q)))<¾ôŸïUFFÖÖÖxÿþ=ìííyúlll@`ÊÇσÁ``àÀ8zô(˜L& …®I¡´&ØålÇ T0P€’EËI]G©T–‘_V õùÿúùáòŽøçï¿´ÆÍ;woaÁc}æPÖ¯6¯[×ä9Œ¹ t]L:6lÀÞ½{Œ}ûöÍfcÊ”)õZoÉ’%øý÷ß1nÜ8¸¸¸ÀÆÆzzz8räÖ¬Y#ÐÒ¨¯¯Ï×f`` Vñ'—u‡Z*Ñáÿ7AÊÊÊxûö-°ÿ~ìÞ½[·n…³³3BCCaaa!Öñ=yòC† ±±1®]»]]]±Æ56Ÿ¦ À TÓÓÓx,;v„²²2€ZßvB÷ý§(**ÖKþüI'NÄ©S§0sæLtëÖ ;w†¦¦&6oÞŒ]»v ¼.>?FqøÔÒÍÁ`ð쳸¸ö˜s} GÙä]r®?UÕº£ú‹‹‹¡¨¨---¡srÖµWaí€àó-è8„}þËùh_½z…N:5÷6Z%l6‰‰‰ƒ©¥IÉÓJÁÿsZ’ïóçTTTð=1¤ðBh)ÑЪ>, GoÜ@õÒ¥‚ttðœÍÆëׯ”zɦMÈ›:Up§­Ðâо}{xxx௿þŠ+°ÿ~ôîÝvvvõšçСC°²²ÂÁƒyn êʪqóæM|õÕW%77„XYYñ´§§§ãñãÇ"Ç76ýúõ¾Ï½¤¤÷î݃‘‘Wæ\cÛ·o—hδ´4$$$ K—.MV`èó5™L&^¼x^½zñ¹èü—¡Ê³ä´„J„¥¯JQ] 8]¨jwU0äZnáª<‹†Z [ v:`­šP×ãg¨ Èg¡B°ÜÓ³î5ÈN˜€¼¼<¡~¾Òdذa055Ehh(455ë<òòòèÑ£®^½Š}ûöÁÇÇL& .„ššÊË˺ÄÅÅáçŸÆ´iÓPZZŠ%K– ¸¸kÖ¬©Ó½¥oß¾˜0aöìÙƒ   ˜™™áLjÅ_ý…ãÇC]]+W®„’’¼½½annŽÌÌL„††‚ÉdÂÛÛ»Îãb2™8p >~üˆï¾ûN ’9nÜ8nŽçŒŒ XXXÀÓÓ—/_æÊ”••áàÁƒ€””µù‘9?L}ûö­·ÅÿS¶nÝ xxxà·ß~Cß¾}¡  €wïÞáüùóPSSâE‹«W¯ÆÙ³gáëë‹åË—ÃÌÌ —/_ÆÝ»w¡¬¬Ìó9ÉÊÊÂÇÇaaa˜?>…;wB[[»Î@O ¶¤¼±±1Ž=ŠbРAxóæ fÏž }}}>ÑM‰——\\\päÈ888`Ô¨QÈÍÍÅ¢E‹PYY‰5kÖp]‚\\\0fÌìß¿ŠŠŠ˜:u*LLL’’‚'N`ñâÅÐÖÖF@@6n܈-[¶ cÇŽ2d2220óÿñ¿ýö[“Ï¥K—°k×.Œ=ÙÙÙ ¬\¹²ÉÖ¤P¤°Â)Ôz6,¿>¥Ð<É?þ[4f»/AiìD±zõj€Ìš5K¤¬ 4v±±±¤]»vÜ”YˆŸŸÙ³g@nܸÁ•å¤û矈¥¥%ϘeË–ñ¤#„?!µ)ëV®\IyÆËÈȾ}ûrÓ~-[¶ŒÈÉÉñÈ‘€€RTTTçq~šLØ+%%…+Ÿ’’BOOOžy²²²êœcÓ¦M"Ϲ ´qŸòøñcÒ½{w¾¹õôôÈþýûydãââHŸ>}ˆ¼¼‡ÉdBYYYà£ð²²2¼zõ iiiÐÕÕ…µµ5_€_ii)âââ:p3ˆ¢ººš'‹ƒ ôôô¸Öòššäääð•±Ùì:­­êêê"3ž””” ¤¤úúúu>ÉÈÈ@BBª««abb[[[¡ÖüŠŠ ÈËËs3utèпþú+~üñG¹ÊÊJ<{ö yyyprr‚¦¦¦Àýp® KvAAž~üSSS888ü RSSØÚÚò€á\jjj|ëåååÍfó]»œïϧߵÀÀ@Ù?âäÉ“°µµ…‹‹‹Äû¼uënÞ¼ &“ }}}ôíÛb­ªªÂÇ…ŒŒ TTTÀÔÔîîîprrª×>JJJðäɼ~ý555øî»ï  @ƒw(JÓP–\†ªUûÔÔ £Dí”_*T¦´(-Z$ðÑ‘™™víÚ…Áƒ7ɺ‡¢E‹pñâEtëÖ­IÖ¨/÷î݃ŸŸ˜L&ddd`aa¬¬,î “É„Wž‚M›6áÇDEEtuu¹¹¹PRRÂúõë1sæLž5ÒÓÓ„©S§J¤@B0nÜ8=z –––HII›Í†——Ξ= YÙºsŸÞ»wnnn ÔÔÔ ¤¤Ë—/Ç„ pàÀ0¢Ÿº»»ãÆ`³ÿäñ÷÷§ 4…Bi2„¦®cj½Ô¤»ŠT¡·FR‚:ä‹OÛ¶m‘ŸŸüü|<~ü ,@FFüýýñþýû&Y“Åb!;;•••M2})//ÇèÑ£ñáÃ=z?~Dbb"JJJ‹yóæA^^žg̬Y³0þ|ôêÕ ¯_¿FNNrrrðòåK888`Ö¬YX¸pa£îóÈ‘#8zô(ˆ´´4$&&"33>>>¸pávîÜ)rŽöíÛ#<<yyy(((@~~>lmmùÜ1bccñþý{Œ5 ÿcï¾Ã›*ÛŽ3ºiiKi)ÈV–ÊPÜ2^ñÕ²ôÁ‰¨€þDP­ Ž”¡ŠTDÄ*²¤‚jÙZiKºw’óû£mèHš6I›¤ÜŸëêÕ䜓'OÇIî<羟硇2mW©T\ýõ•Šc:ÄŠ+¸é¦›Ø¾}{¹àúºë®ã×_妛nbÉ’%Lœ8‘:TýG¨¦={öÅWJSDš4iÂŒ3øúë¯Ù³gO¥Q7oNóæÍM÷µZ-½zõâµ×^cÈ!ìÞ½›ñãÇ[íˆ L·?ÿüs[~!\ʼyó$ÚF¥E„µ™µ? óûÜmᔊ.]º$yÐVH]Gõ˜—KÆ.ó µMí­vZAÄàÁƒùöÛo9sæ Pœ7;sæLÞyçr—ì'OžÌ;#···iÛòåËY¾|9ÿýw¹6.\È´iÓxå•Wøàƒ¸ÿþûMû_zé%SüƉŒŒ$;»xª"Ö¯_Ï‹/¾H“&Møå—_L»öÚk6l·Þz+Ó¦M£¨¨ˆ=zƒ^¯çÍ7ßdΜ9èõzÓcxà>üðCÓ‡ŒŒâ¿±µô‡Ro¾ù&F£‘Y³fU™ðôôdæÌ™Œ1‚·ß~›?þ¸ZíZÓ©S' øAY¥÷K÷Û¢4U¥K—.6·!„;“àÙvµ]Dh,0’ý§ù©ëÌÈ‘#iÛ¶-111dddŤI“¸pá‚Ùþnß¾eË–±iÓ&Μ9c Ð_xá^ýuFͶmÛ8vìÏ=÷[·neâĉ¦ÇßxãøøøðÕW_1cÆ Ž?^åïçÈ‘#hµZ `ñ˜{uJÅ_ýUe[5qÿý÷ÌܹsÙ½{7ƒýû÷óúë¯À¨Q£jÜfjj*6l`Ö¬Y„‡‡ÛÔ†BÔ¦ìCÙ ̯œâ³µê6„{“Z¸´“'O2gζlÙ‚¯¯/ýû÷'11‘¥K—ÁÊ•+iß¾=­[·fÁ‚ôêÕ‹O>ùÄ”ûvôèQEáù矧W¯^øùùѼys|ðA† @XX˜©¯eË–DDDAãÆxë­·P…÷ߟ=zÀàÁƒyöÙgÉÉ1¿tkjj*+W®dРA´jÕŠnݺqòäI–/_ÎwÜÁ§Ÿ~Ê€¸îºëX°`<ò_~ù¥)¸ eõêÕ4kÖŒ¹sçÒ±cGZ´hÁsÏ=ÇùóçË=—Á`àÌ™34oÞ¼Êkoooš4iRi$ÞáááÄÇÇÓ´iSúö틟Ÿ7ß|3üý÷ß´oß¾Úm• 6nܘ‡zˆ6mÚO³fÍÖ_!„°›™¿gšÝ¥öVÓàz÷œºNÔŒÐuDò«ïܹs¨T*T*íÛ·çÕW_%00Õ«WÓºuk>ž÷ߟ£G²bÅ –,Y‚ ªÝVÇŽ™:u*ï¼ó±±±,]º” 6°dɇôUw#+Ú®6W"´4uJ­Â¿gý˜ºNb뤈°Ž8*!_ã§qÚÔ8j¯ºù¼Õ°aC¦OŸnqéH«¹¼t›¿ÿ•±'žx‚ñãdze˾þúk6mÚÄÿþ÷?-ZÄÔ©S«ì‹J¥"((ˆ'NTÚwùòet:ÙÇùúúVÚVZØøõ×_[ü(;›GEZ­–#FÄÝwßÍ?þÈÿýßÿо}{vîÜÉ©S§hÛ¶­ÙÇ;vŒ‚‚ºvíjñ9jjÆ øúú–›àßÿþ7/½ôQQQ¦>ÖÔ¸qã˜5kQQQ¦ B\M¤ˆÐvµUDX˜\HÞé<³û|#|ÑÖ°JŠ­«é«H@ïz»÷ô8ö*-þûñÇyâ‰'ÊíûñÇ*åÞjµZ† ưaÃX±b×^{-«V­2Ð>>>fS:tèÀž={8{ö,­Zµ2mÿé§ŸÊÍbMÇŽâ©÷î»ï¾j?®¢víÚ”›Å#22’;w2wî\>úè#³›;w®éXGIJJB£ÑTm/Ý–””dsÛ¾¾¾x{{STd~•/!„¨k™{Íç>Cqñ ¸zH ‡p;½{÷¦uëÖìØ±ƒ””ÓöŒŒ ¶mÛFXX˜)?úâÅ‹ †r÷ôôÄßߟÔÔTÓ¶ÒUø>\éùJƒô¥K—š¶ååå±jÕªõû¾ûî#<<œ™3g–{îRÉÉɦ¼æØØX¢¢¢*õ`þüùtïÞÝ´-22’ž={²zõj–/_^é1 .äóÏ?§wïÞ¦TG¸á†ÈÊÊ*73ÀæÍ›INNæúë¯/·}åʕ̞=»\ðWéç4¬\¹’ÄÄÄJ+$FGG3{öl‹3 !Dm(ü§ìCæ§®ó ÷Ä»¥{O]'jFF k **Š-[¶pþüy6nÜHpppµ+ ùŽãééÉ¢E‹9r$7ß|3cÇŽE«Õ²fÍ’’’X½z5 WAøá‡|ôÑG >œ¶mÛ’““ÃÏ?ÿÌÑ£GyõÕWMm¶iÓ†¶mÛòÒK/ñÍ7ßÎàÁƒyà3f _}õ‹-âǤ}ûöÄÄÄзo_ÚµkgvÞes6lȪU«5jDFFÒ¶m[.]ºÄ_ýÅÖ­[ùûï¿iÕª.\àÁ¤E‹ôîÝ›¶mÛrñâE~þùg 3M½Å©&QQQ<øàƒLž<™µkךŠ÷îÝKLL 7Üp7n4ÛßÍ›7[,X*;ÇuEsçÎå§Ÿ~b„ ¬^½š{_ý•_~ù­V[)zåÊ•üõ×_LŸ>Ý4j={ölvíÚÅ­·ÞÊ5×\ÃéÓ§ùå—_Ðét4mÚ”wß}·\ÑÑѬY³†šoâ ›7o®|ÈŒ3èܹs¹ãûôéC“&MÊdFFF¢Õj‰‰‰aóæÍøúúÒ®]; Ääɓ˭ Å « 0À´èL)SžyÅ¥¼Ë.¬#„»•mçè•sŽäP`~ Ì£±~]¬Ï‚äNd%BëTŠ¢Xø<%,iÒ¤ ‡ª²à«¬ÒœÓª^«sŒp-üñ={öä­·Þâå—_vvwÌ*((àÞ{ïeÇŽ|üñÇŒ7ÎÙ]Â)î¸ãî¸ãŽrWn„¨c¡‘Äå‰è3ÍO]öï0|ÚúÔq¯j—Ä$ÖI´Õ°zõj’““M÷9ÂØ±cñóósé ÔËË‹èèhºuëÆã?Î×_íì. !„[Éø-Ãbðìá[ï‚gQ=WM Gaa!gÏž5]7Çh4rìØ1.\¸À7ÞX鹸z½ýöÛŒ;–àà`ôz=™™™4nܘÏ>ûÌåÿOüýýÙ·o999•R8„BX¦×é-μ¡ÒªdvŸ¨ÿêý»éž={˜2e GŽ¡°°Ûo¿ÝlQ”N§ãÞ{ïåÀ„……qñâEÞ|óM^yå‡ôCŠÝÛž={ؽ{7çÏŸÇh4Ò¡CzöìY)×Uyxx¸M_…ŤˆÐvŽ*"¼¼í2ŠÞ|¦kÀ-x9g]†Ú&E„ÖÕû£ÑÈwÜÁš5kèÕ«—Åãž|òIþùçÎ;Ç… X»v-¯¾ú*ß}÷Cú!E„î-((ˆÁƒóä“OòÔSOѯ_? H…µJV"´#V"Ì=‘K^¼ùES´ µÞZß$f±®ÞÐ}ûöeÁ‚<ôÐC4lØÐì1ÉÉÉlÚ´‰±cÇÒ´iSÆŒCëÖ­ùàƒLÇ=ÿüó´jÕ •JE¯^½¸÷Þ{«Ý©fBQRÀe;{W"T iÛÒ,îê„ÊCesû®Nbëê}]¤¨¨È´R\©.]º°oß>Óý pöìY’’’8{ö,[·n­Ñs|úé§DGG—û2· †BˆÚaîuW¶É¶ŠÛ2÷eRt¹ˆâ~ -·| ý‹îòšäY|¬;n+I^~ùe>ýôSU“HLL S§Nå¶wéÒ…”””r«¦ !„¢þÒgéÉØ™avŸJ­¢A·uÜ#ኮªy H~~~¥"ÂÅ‹3mÚ4RRR 1m_¾|9“'O&--Í®|×ÈÈH øüóÏ«<ä’BÔwœZŠmgOaê¦T²ÿ2¿d·O jdo÷\ÞèÑ£ñòò’˜¤ õ~Žê(]%>>¾\}üøq¼¼¼R,V„üÂÂBÒÓÓí~.!„å¹ã•DY‰Ðv¶®D˜>ŸìÃæƒg¯† »®Žiëd%Bë$€®¹æŽ=Ê-·ÜbÚ~äÈÓ>{UçñóÏ?¯r”Z!„íî¹çgw¡F$x¶ME„ ¤}—®ËÞˆÚçêÈ|•àÙ:  ^½zΘ8q"ùùù9rÄt¿¶½ñÆL:µNžK!®F¥W…0'ûÏl ͯÙàÙÄÿîþuÜ#áÊê}‘‘ÁŠ+8sæ EEE¦¹5üq‚ƒƒÑjµ¼øâ‹<÷Üs´mÛ–o¼‘%K– R©xòÉ'뤟-Z´O|B!„ó¤m·…C!„œœc9ƒg`ôÊ IDATzÔq„»Z!„Wc‘‘´, B¥•ÊAažÐu¤ ÀüÔ8Â:)"´"ÙNŠí#E„ö‘s×vÕ)"Ì܉>Ýü;>í|ð½Ö·6ºæ$f±Nè:R•…y .¤°°ÐÙÝp[¥Ó6ŠšKHH`ݺuÎî†ÛZ·n|±ƒœ»¶+]‰Ð}ºžŒÝf÷©4*‚×V×Ü‚Ä,ÖIaˆŒŒäÌ™3<ùä“tîÜ™Î;;»KB!ÄU+%*…œc9f÷5¼¥!Aý¯Ž%»+Š%66–+Vкuk)"¬‚Œ@×OOOe*6!„‰òNçY ž5 44¼ýê›u£”··7xzz:»+.ïêšØÐ‰š5kÆÀÝ !„âêe„´mUÞ„Úëê[l×®íÚµcýúõÎîŠË»zÿKê˜$äÛNŠí#…H¶“"BûH¡}äܵ¥"ÂÌý™&›¯©ñºÆ ¿ëýj»knAbë$€®#’o;)"´"ÙNŠí#E„ö‘s×væŠ 9ÒI7ÿÿ+•J¦­‰YªCŠ뀬è#„B8WêæT²f›Ý×àÆ„ ©ã¹.‰[¬“h!„BÔk‰dÿi>xV{« ºçêœuCØNh!„BÔ[Š¢ ûN®·7¼½!?MÝvJ¸=  ëˆ$äÛNŠí#…H¶“"BûH¡}äܵ]Ù"œÃ9œ7ÿìÑØƒ†7_½ÓÖY"1‹u@ב_~ù…ÈÈH¢££Ý·#E„ö‘B$ÛI¡}¤ˆÐ>rîÚ®´ˆÐX`$í'ËÓÖ –H¨Œèèh"##ùå—_œÝ—'E„u@’ñ…Bˆº—öcšÅ%»}#| }(´Ž{ä$n±N>w !„¢Þ)º\Dæ¾L³ûTZAÜ·pÐh4Jj£“ÉJ„B!„¨wtÛt(óÙn À#È£Ž{ä8“^x€•ï¾ëäž\½dºŽHB¾í¤ˆÐ>Rˆd;)"´ÚGÎ]ÛeÏæØ¾cf÷ij ¼5°Ž{ä8F£‘ÕÛ¶±zÛ¶Z{o”˜Å:  ëˆ¬êc;)"´"ÙNŠí#E„ö‘s×6Š^!yk2íýÈìþ þA¨<ÜwÅÁI/¼@Á A d‰v4‰Y¬“"Â: ÉøB!DÝHÿ-ôíæ—ìönåM“È&uÜ#Ç1øvéBÁ²exMžLî‘#¨ÕŽ•¸Å:B!D½ ÏÔ“ù›…ÂAµŠà×q«tôµÔêZ…U“Z!„nOÑ+¤D¥`,4ŸìßÃÏ0Ï:î•c¤±(!Uß}^Ù1p`­æB Ë$€®#qqq¬^½šC‡9»+nGŠí#…H¶“"BûH¡}äÜ­>EQHN¥àBqñ›Q1ræòÓ~¯†À;Ý«pШ(lÓéyô(ÍöîåÙ—^B2¤xô¹”ZMÁàÁ…>tè«W¯–ÿ½jºŽÒªU+ÝëvRDh)D²ÚGŠí#çnõ¥ÿœNNlŽé~‘¡¨\aà]¨}Ü#ä9›ŸÏÌ3ghµoÿ:|˜/SR(4àÐ!0 ò pØ(t`` ­Zµ’÷Üj"Â: ÉøB!DíÈþ+›ÔM©÷{]ãEøøppá‰7òF6¥¦òqR;ÒÒ(˜ùk4\³z5q¡¡(æh@õý÷<^Xè°y¡%n±Î=>Ž !„BT.ŸË›/[ܯ Ô::ÔeƒçCÙÙL>q‚¦{öðð±cl/<÷mØÿFDp±W/Nÿþ;J¿~ÛQúõcõ÷ßKºc’•…Bávô:=)R,®6¨öRúp(?M÷¬jéz=ë.]âãþá`VV¹}M<=y¬IÆ5iB__–-[†1+ ïéÓ«lוÅ{ï½ÇäÉ“k­ïâ  ëˆ¬êc»øøxÚµkçðy.¯qqqDDD8»n)77—ÔÔTZ´há쮸¥„„BBBð- DÍȹk™!ÏÀ¥ÿ]Âk0»_Q)¤÷H§E¨kœ» ðKz:'%±1%…ü2#ÅZ•ŠA1®IîmÔ­ªüpùäÉ“ë<(.((ÀËË«NŸÓÝHDRGdUÛI¡}¤ÉvRDh)"´œ»æ)…” )].²xŒß=~¼¿ñýZëƒN§cô#X=îbAsΣÝï¿sסCüïÒ%Sð|­¯/o·iÃùÞ½‰îÜ™ûBB*ÏÎ"1‹uRDX$_!„pŒÔ¯SÉþ3Ûâþ€^¬ÝSî¼ÿ~vÆÄpz×.Z¶lYn_‘¢°95•Oþù‡ïu: eÂ,?†Q3.<œ¾ Öjí!q‹u’Â!„B·±+£ÊàÙ§ƒÁj7xÖétì>}ã+¯pßøñüõÓOËÉáãþá³þ!¥¨üèx¯€Æ‡‡ó`h(þ×Êɶq¹:==ü‘ØØXbccINN&""‚Î;Ó­[7n½õVgwQ!„u,÷X.iÛÓ,î÷lâIèµ?ãÆˆ (z䈈àXVoÇĭѰ/³ü⡞žü;,ŒqMšÐÑϯv;%êœËä@§¤¤ðòË/Ó²eKzè!>ýôS222çСCÌž=›Ûn»o¼‘/¾øÂí¦j‘"BÛÉJ„ö‘¥l'+ÚGV"´œ»W\, eS XH:Õøk{8 •gqôl4kå÷wY§c×éÓУú±c™>y²)xÖ”nìÔ‰ ½{³ m[· ž%f±Î%èŸ~ú‰V­Zñǰ~ýzòòò8sæ ?ýôQQQÄÄÄ––FRR#GŽdêÔ©Üpà ÎîvHB¾í¤ˆÐ>Rˆd;)"´ÚGÎÝbú =ÉŸ'£™žU*BG‡¢ ¸’Ç­wÞi÷sŸÊË#*9™Oâî¿þ¢ÉÈ‘èËFD€ÑHËŒ æ´n͹^½ØÒ¥ ÷7nŒ‡‹ÚBbë\¢ˆððáèT*ºtéR­ã‹ŠŠØ´i£Fªåž9Fdd$»ví¢oß¾ 6ŒaÆ9»KB!„Ë3ùç“(¼daEG5Æïºò£¼î¸ƒã—.³v-Ý»w¯ÖsÉÏç¬,deñGV³²HÓ믕¯¾ K–”`\]¿üÒ” í΢££‰ŽŽ6Å,RDh™KÐõT³ !„å•i–(F…äÏ“É;‘gñ˜ ~A4ìS~6‹S§NñÈ#èŸ~šK—rî÷ß+=îl~¾)P>•ÅìltE–§ÅÓªTx¿ù&Ù˜Ò7Êíé%NFEUš‘Ã]IÜbËdddàçç‡V«5Ý߸q#ÁÁÁ2z+„B8€µ¶6}ðÑGL™1ƒ‚äd‹Ç¤mK«2xnЭA¥àà¾ñãÑÍšqÑÏ-{÷RØ®²³Móå*‚eJEG__zøûÓÝߟþþ´(,¤eJŠÙàŠs¡ËÎÈ!ê?—È.ëüùó“””@vv6­[·fÒ¤I >œ×^{ÍÉ=´$äÛ®¾æçç×jû‘cÇÖjûΤÓéX»vm­µ_ÛE„±±±Lwâkš¾ìåéZPß‹cbbjµýÚýÓétÜ>p ‡ªµç¨Ê ‹Stï½¼öÿgvæï™dîÏ4»À»µ75 ÐhäL~>¿ed°xÿ~â  ];¸pÃøñ ùÏqô(o;Ç:]¹àY­RÑÉÏG›4aiûöìéÖ¬[oåpÏž|ÁSÍšqs@?þxñÌ–”ÌÈqîÜ9Û~!.Fbë\.€>xð ×]wÍ›7`Íš5xyy‘˜˜ÈºuëØ°aƒ“{h›Ú¬¦Þ¹s';wö­Y¼xq­¸wÞsgÏž­µö·nÝZkm[“MË뮫µöW~ôkþ÷?öíÛWkÏñð¿ÿ]km[3bŸ9³ÖÚŠŠbÀàÁµÖþЧŸfQT”Ó> ö1‚ _|Qkí÷<˜¨¨¨Zkð¨Qlß¾½ÖÚ¯Jff&½ï¾›cÇŽÕJû©©©Œ›8‘ÔJû#&LÀ8n÷OšT+íWåƒ>"§[7”1cx×L‘nÞ‰<Ò¾/ž®®HQ¸\TÄ™ü|þÊÎfWF›”tžêœBC Û³ï;i³o·ýù'Óžz ã¸qPTQQЬC|4JOOgçöíhvïF³d‰Å/ƒ^σãÇ×Éï°¶I¡u.—ýÙgŸ±jÕ*~ûí7nºé&z÷îÍ’%K(,,¤aÆÄÇÇ›lwIÔ–-䦦ÖJûMK.)%þñG­´_•ÜÜ\ü[´àñÇcÅ»ï:¼ýcÇŽÑå¾û¸¡m[|ÿ½ÃÛ7x……1gÆ ^œ6Íáí[sï˜1|w⯠ʛ¯¼âðöý;w&ûÙgiºr%÷ïwxûo¾ý63çÏgëÚµ 8ÐáíWE§ÓÑä®»ÐwïμÎkåïפ{w.çäpiÏ‚ƒ»8ñcÇèúì³o¼‘Ç YY çOURSS ¿ë.ü=<ÐÕB¦Óé½å‚}}I>x°VÚ»åùùñO-™U¹gÔ(¶‡„ÐöÈN–¼_9RŸ¡CÙÓ¡Íû.=wŠ/F;{61‹×éÌV;“õÎ;àë‹**ŠÑÜ=y2 HMÌ#dC9yz2õzÓ²×¥ò½áÛAåo¦áÄDXº*Î^rñ"a‹sbß>›1ÉÏÏ'>>¾ZÇúùùѶmÛ?‡«‘hë\.ú¦›nbòäÉ ˆ‰‰aÑ¢Eh4 ÙÙ–W!rUùÞÞlß¾»ï¾Û¡íîܹ“äÓíÛn»Í¡í[3æÉ'1>õÿŠâ½ùóQ«{Qcèãc|í5޼÷/^¤Y³fmêŒFâõ?¬ó:;;›ŸŽwßeáÔ© W–ŒòЦ —‚‚Ø·o½zõrèsÌ]³eÙ2þ=}:)u@›3èÒ…7¦Msøßoûöí¤†…a¸ë.FL˜ÀÏ_}åÐö‡Lš„á‰' ,ŒÕS¦ð~-œ?U>~<ú‰ÉØ¿Ÿ/6nd䈎m &pyÇŽZyí1aú H­¥ö«’™™ÉÎøx˜4‰³±±;vŒŽ;:¬ýÔÔTöŸ?Ó¦‘øçŸ8p Ú3IT‡éÜôãÇ3ôÉ'ùõçŸÉ7«õ•WÝã †JÛ²¾ù†ünÝÀ×eøpÖ=ý4ëú÷Ç'Wað·*²rÌ÷Û ŸïRᬥ«—×Tøzýµ×8gn¸Y3Rýý‰?tȦߣ··7]»v­ñãDýærôµ×^Kpp0]»v¥¨¨ˆ=zЧO xº;À-«\•öíóâ‹)yèÙg1¼ø¢év]ŽBçææ²õàAˆŒ¤07—§^xÁ¡£ÐÇŽãŒJ-[RôØcÜ7nœCG¡F#«¾þeÙ2ò xgÑ¢: ¢G=ñE£Gƒ‡ùwÝÅ«sæTD…BE¡Àh¤Àh´z{Ú¢E(óç?vìX=ý4_þô¾j5¾M¥ï5³ôÍ·ß&ïÖ[!<]óælÛ¶Íá£Ð Åù¥?W¡ÑH¢rùrñbS¦Û»7/¼ó“¦LÁS¥ÂK­ÆK­ÆS¥ÂS­¶ia²1/¾ˆáå—!$„Ýk×¢Ó鬎B…¼’#×`°xûT\g½½!<€Â!C¸cÒ$"gͳL¿Ëþ ^%ßK¾r·Köi«ù7LMMå÷‹¡KŒmÛòÄk¯94€Öétì-ùû[¶dô /8tºt)e¦LÁЪU­¼¶Våþ (zøa€âÛ'ž ö×_QŴ·•5?jº}иqè{¬¸ýqãááwßöÓuÜü0ó5xjh¨Õ Õ Ñ˜¾‡ee·†øšE>uêõzhßÞìÓÆçþI“ÌÎÈ!„-\.€øóÏ?Y±biiiüç?ÿ1m‰‰aüøñø–|ru+j5)aa¼ÍeF‰UeÞðʾõY¼]æø#{öp)$BC¸ÂÒ­[ézË-î¼y¯MšDáÈ‘ R¡ ÈÇO?Í—_ƨRaPôŠ‚AQ0À•Û¶WÚ¬;äIpátìȡի™ºw/Áááx” %_¦Û¶›ö©ÕåS©xgÖ, ­eäHfM›ÆÀ‰)R”+_F£]÷ ËÞ/s;/'‡mGŽÀĉ(÷ÝÇÜ)Sˆîßßb@l¨I¶Õ֭н{ñ(OB´h.8˜»Ö­ #eZ•Êl`méû²Õ«Q/ÀɈ_äÙk¯½èVz Ëü,…ÕØW`4¢·ô3¿ñ”)èQFbÁ”),¸é&³‡— @½ªq;}ÿ~’ÃÂÀß’“)zä"F¦ûüùUÈÕÍe~é%˜:õJÿûõã·§Ÿæ·¸8°cñµJe ¦+×eo}áŠJ.ÑâëKzǎܼx1×ôëgz½QqåµG¥RÕhûÓ¦p&'CƤ†…1põjš–¹bÏkß–)S®t…„FÿÕ« ½ùær¯)å^_¨üzSý•ŽÉÎÆø÷ßPš;ܲ%§Õj|?û 1¸“‘Qüšwýõ¦s÷Ÿ ¸éóÏ¡CûÛç¨X·0a,Z V»à¥Vã]ƒ¯Ó7ò[Ïž(ßÇ ã®±Ÿ²hBK4 ÌÿÿÞH`Ï@‹ý1ͼQJQàâE¸æšâû%3r8z4¿¾*((ÀËËËÙÝpi.@7lØéÓ§WÚþøã;¡7’–†qÆŒâŠûeËÓæôéÅ“º—0ŽÇ3Ó§ç€Õ¶‚øë/SˆFCÑàÁ š<žxÂþöÏ+™hÙ²øEýé§1FF²ä¹çà­·ìo_Q`Ëxï½âû^^ä÷îÍõ/¿ ¾”mÖÛoÃèÑWî{x`¼çŽ~ø!”ŒlÙeËX° øöºuÅÿ+ãÇÿî*.PB¯(dêõX®{/cýz¸í6(}mÔˆÜ-xó‹/ÀBë0YYpùrù餼¼ o_ظÑì߯Ðh¤À`¨Þs,Z³fÁ¥K°k<ü0)Ÿ}ƶs犃j{œ;¦ÑgÔj:V­²ëü1* ùŠR)o´œŒ HO‡ÎM›”Ñ£ÙÿòËìwDlV$%ÿ}Ö­ƒ¾}QÆãû7ÞpÌk_ÙöK(ãÆñãë¯;îµµ*‹•ûðŸ[K—^9çìm¿ôÃMé¹;aBqà[’ÎhNu>k³³‰º|cũؚ7GÀ[F#×víZ퀸¦>ÿåw*mïqØ“¦M{³ó·ß¸ÓL¢_g?ÞQyººRIIIü}èegŽ01œ;‡¦uë+›22˜øÌ3ܵ«Æ}¿Ú\ºt‰-Z8».Í%謬,ükø¦”™™I@@@-õ¨„†BHHñ§áƒ¡[7ûÚ;|¸ø ¸dôÙôááÅûj;_kéRxðÁò£eÊ(ŠÝ£hÊÒ¥(¥—Ÿ}¶ø{§Nŕթ©Å¿K{|ò üë_ -s Œ ÏkçË\Y3é×Ï)SˆíÙ½JUíQürÇ”¹mißî7ß$½âÔ†¾¾Ð¥ -öï' dùc[SçÌ!¿4À,ûaðškÅ»$x+[¿^ö:ƒÅÛ%Ç_^¼˜ÂŠÿ’×Öf±±øßtš’«M¦ï˜å¶Y8ÆÒ~­J…>;›ÅÉÉ{ö,ÿü-Z nЀiF#MJRlÁÏÑ阑•…¡ôõ»ôÜ GÂûÀ ݺٜ‚uçý÷c´0kŽqüx>xõUNïÙcµ[”μA…Ñçö' s,pÝuìùî»J´Ws/B†…”»QQxx8úôôÚèöUK‚gë\"€Þ´ióçÏgêÔ©<òÈ#U^68~ü8Ë–-ã‹/¾ %%¥{é ãÇüÖ[l™0°ýMäþgŸå²™QzƧѼy|¹c‡ãú\AA^ƒÐ?ÿ|ù ªûîãþM›xõ­·Lo>eߘʾ)™{ÃÒ¨T?vŒ®¾¾Ì]Œ¤Û‡²÷»ï(*¹¬Zš¡/“&QÕöBƒ11-_^¾mooT}úðØîÝ<øä“xZ€­Ý·ö6vï˜177ʬÕbì×Ö_}eWAá}|`v”Šß$ç¿öíÈ|óí·™yûí(ÏÓFP·iÃW¹¹µ6#‡N§ã© Ì/fàåEá-· [¿Þ®\ö&'ÂË/WÞÑ£ykײ±E ›gä8vì‰>>åGŸK©Õ »3gÖÚŒ©©©4ÍÌ,þ0ZÑèÑd͜ɹ’š [èt:Â.]‚Š&Àر(óæqîé§íj¿IÅ«¥ÆG?w.Çíhßš{FÂha.`ãøñDÏše׌}&MÂP’û\©ýqã˜3}ºÍ3r”Í7«ysÎ{zrèСZ™‘ãùwßÅøÊ+ Ó™¶…'kèý«'”\0ч‡óóΦ Z¤%ô¡PTZÛd„¨-.@3•JÅœ9s˜1c·ÝvíÚµ£mÛ¶qêÔ)âãã‰å?þààçŸvv·mBF“&äÆÄØ\5¾sçNÒCCË>— %=4õáõ6#ÇðgžA_’û\‘2p [ž~š¨Å‹mžQ`èãc(M ©¨S'ެYCJR’Í3rL™>}IîsEʨQD=û,ÿ­…)å dæØØ+©/Ÿÿ¾ûìš‘cåG‘Ó£øø˜? 4Ôî9æ®YƒbáR²12²Vgä(;{€9ʨQvÍÈQ:ó†¥+Eõ”ÃG¡ÓÓÓiÔ¥ ÆÉ“«>pófæÉóÏcçÇÓ·o_{º^Éý‘‘lJK3‰¶”Á€vÕ* Ο¯ñ‹p×^½8š–†ªÌ ñÔ)Ô­[Z•n;tˆ‹'OnîRx¦LŸÎ²øx¸÷^Ëååáµhù§OרmkŒF#ÁmÚPh%[1hl0P2mcMÚ߸qc¥UíV¬XÁ“O>Yn›¯¯/C† ©QûÞaaÝrË•€ÈŽ„.9xõÍØØXÞ[µ ¥¹ž}o¾™GjXŒù믿rç!hÊ×åå¡èt¨Ë¼“’¸¿W/¾øü󵟞ž^íeÁƒ‚‚¾XÔ¸‰ùìçŸÑ4jTåq†³gù)*ŠÛo¿½Fíþ÷¿‰úí74eÎIcb"ª  Te>ÐŽç×-[jüÚ4rôh¾Ú·µ•sÞËέ[þÚ7`äHNVcu6[ÐÇãT…öÏ>L« #ÒAžž|¿qcÎîH†)RÈOÈ·xL£Áðïa_±n~~>S§NeåÊ•vµsµ‰ŽŽ&::šï¿ÿžH]— ë YÑÇuLŸ5‹=GŽX=ÎøßŠ„……Õ~§„â*›Ãå­—1æZž%& wÁ»ê§¨9‰[¬s‰h!êʼ×_wv„⪃°ß® IDATbÈ6pùÛËäÏ­ò8ßk} î/Á³p@ !„¢VäÎáòw—1æU½Èg¸'!#B°iÙP!œÀ=“¨ÜPAÙ ÞEÄÇÇWÊëÕçà\ä«Innnµó–Ee äæV=ê(,sçs×eàÒºK¤|•b5xÖøkŠÚÓq!‰Ñh$>>Þaí]m$f±Nè:r©Å'¼… RXXèìn¸­yóæ9» n+!!uëÖ9»nkݺuòÄîzîfý™ÅÅ÷.’ŸgõX¯f^„=†6À±Ä YXƒ¥ÉEy³X'E„u@’ñ…BÔwú =—¿¹LÞIë³J«"ðÎ@z RKÞ†«‘¸Å:—ÎÉÉaöìÙtìØ///ö•¬¼ôßÿþ—9sæ8¹wB!„(+ë@‰ï'V+xöjîEÓÿ4¥aŸ†< ·å’ô¨Q£xï½÷¸ûî»iTfÎÒ:0þ|ŠŠ,L¾.„Bˆ:£O×séÓK\þæ2Æ‚ªsU*‚>.:꡵Ãåè°uëV~üñG–-[F`™ÔºuëFFF.\pbm# ù¶“"Bû¸s!’³I¡}¤ˆÐ>®|î*ŠBæþL.¾‘¼ÓÕunQ<êÐ; NfÚ"BûHÌbËÐqqqtèÐn¸ ÜjKEEEhµZ²³³Õ=›IB¾í¤ˆÐ>îZˆä ¤ˆÐ>RDhW=w‹tE\Zs ÝVJaÕeT*Áƒ ŽG£ºu–"BûHÌbËîÞ½›Ûo¿ÄÄDBCCéÚµ+«V­¢W¯^|öÙg<öØcdggãëëëì®V›$ã !„p{ dþžIÚö4”"롃w+oÝ×`I×p7·Xçr#Ðݺu£eË–L™2…'N R©Ðëõ|õÕWÌ;—áÇ»Uð,„B¸»¢Ô"’>IB·Mg5xV{ª ¾7˜°ÇÂ$xõ–Ë­DèããÃúõëyà¸öÚkÑjµ 8œœn¼ñF–/_îì. !„WŨ¹7“ôŸÓQôÕun]2ê$³¨ß\.€èÙ³'qqqlÚ´‰£G¢ÑhèÚµ+ÇG£Ñ8»{6‘„|ÛÅÇÇÓ®]»rùð¢úââ∈ˆpv7ÜRnn.©©©´hÑÂÙ]qK „„„ÈUC9ûÜ-J)"5:•‚‹Öß¿Ô^j‚úÑ {T*çOMg49yò$:tpvWÜRAA^^^Îî†KsÙˆÄÇLJ‡~˜9sæðÆoðÀ¸mð ’o)"´«"¹)"´ÚÇiç®2~Ë ñƒÄjÏ>m}h:©)þ=ü]"x)"´—Ä,Ö¹\aYÿý7qqqTìâ°aÜÔ#ÛH2¾BwPx©ÔèT “¬Z¨½Ô¦A·uÐ3Q—$n±Î%S8æÎ˪U«8{ö¬Ùý.ó[d0ÈÏÏG«Õ¢Õºä¯]!ÄUJ1(dü–AÆo(ëï±>í|¹/M€û^•éõzôz=ƒÁ­¯ú×—Káøý÷ß™1c#GŽä?þ --ôôôr_îhÿþýL:•mÛ¶9»+B!„IARI&‘þKºÕàYí­¦ÑÐF„=&Ás=´mÛ6¦NÊþýûÝ—çrC¡gÏž¥I“&Ì;·^}úéÖ­+W®tv7Ü’ÚÇÙ…HîLŠí#E„ö©ísW1(düšAú®t¨Æb¯>|‚Æßõß›¥ˆÐ6ƒfðàÁŒ=ÚÙ]qy.‘ôéÓ‡ÔÔTþùçgwÅ¡$!ßvRDh)"´ÚGŠíS›çnÁÅ’>H"}§õàYí£&dxa‡¹Eð RDh/‰Y¬sÉ" ð믿2eÊî¸ã<<Ü{>IIÆBát äžÌ%û`6yç¡­¿ýûFøÒhp#4 Ü#pŽ!q‹u.—Â0pà@/^LÿþýÍîwÁ˜_!„pIúL=Ùf“}0}†¾ZÑøjúW ºÈ B˜ãrtzz:={ö¤sçÎŒ;–¶mۺ̼’B!„;PŒ y'òÈ:EþÉüj6—òëèGð `4~2ê,„%.@ïØ±µZÍ?ü@PP³»ã0²¡í¤ˆÐ>RDh;)"´ÚÇ–sWŸ^2Úüg6úÌê6—Òøi¾7¿N~5zœ+’"BûÈJ„Ö¹\DÒ¼ys¼½½ñññqvWJòm'E„ö‘"BÛI¡}¤ˆÐ>Õ>ws<‡Kk/qaÉÒM¯qðì×ɦ“šÖ‹à¤ˆÐ^³XçrE„ƒAƒqçwòì³Ïº}!H2¾BÇÓ§éÉ:˜EöŸÙ² 6µáìAà=øu¬³p ‰[¬s¹ŽÒÑŠéÓ§3wî\úôéS)ˆŽŽŽvFׄB§R ¹q¹Å¹ÍgòÁ†!0•F…ïu¾4èÖïÖÞRg$„ \.€ðööfèСÎî†BáŠ.‘}0›ìCÙrlmnì7\ßµ¯Ëep áV\.€nݺu½a–"BÛI¡}¤ˆÐvRDh)"´Ïñ£ÇinlNölòÏæÛÔ†ÊC…_G?ü»ûãÕâê) “"BûH¡u‘ÔIÈ·ÚGŠm'E„ö‘"ÂêQC–üsùdÿ™MÚö4R¾Haæc3IݘjSðìÙÄ“à{ƒiþ\sB†‡\UÁ3H¡½$f±Î%Š÷ìÙCTTãÇ'88˜ùóçWyüâÅ‹ë¨gŽ!ÉøBquSŒ †Lzž"]Eº"Óm}š¥Èþ·bµ—¿Î~4èÖ¯fWWÀ,Kâë\"…ãÒ¥KìÛ·ûï¿oooöíÛçì. !„5¢ôéú+q™ïút=Š¡vÆ«¼šyáßÝ¿Î~¨<¥ PˆºàôðáÃñôô¤}ûö„‡‡K-„Âeé3ô¥Q”\Tn4Ùi¨ÑŠöP{«ñëZœÛìæY'Ï)\ƒ^¯çûï¿`À€hµ.Ê]u\æ·>fÌV¯^ͰaÜݕZ!E„¶“"BûH¡í¤ˆÐ>î\D¨( † Cq œRDarañíÔ"ŒÆ:ééÔS´ i[n›wKotk€_'?TZm¶¤>êt:z èÅ™Œ3›ÿª§©i؆½Ûöì°ç’"Bë$"©#’o;)"´ÚNŠíãE„Jñ‚$yñydìÊ uS*‰«9?÷<_àÒÿ.¡ûAGö¡l .ÔYð °b×  x‰í†·4¤ÙÓÍh2¶ ®o Á³õ­ˆP§ÓѬk3NÜpý=Æ;ï4¢£'þ†xšumFrr²ÃžObë\¢ˆ 00°ÞŽ@K2¾B8YI \˜\HaJ¡id¹(µÈ!|Ž¢òTáì6H‹G°^ͼð¹Ö•Fæ«YÛîm9Ýý44³p@"´ŽiÍ郧ò|·Xç2)ß|ó gÏžµzÜÔ©Sk¿3B!ÜŽbTÐëôåƒäÒ@Yï²ÆWƒ6Xk ’ËÞÖ4Ð8»{ÂŤ§§“—`9xh çóÏ“žžN```õíjæRô'Ÿ|R­ã$€Bˆ«˜ ù 9†òArJE—] PVÖ¿8(ÖW’ÕÞ’=)ªoÓ¦MZX_}ÒÐÜÀ¦M›;vlôJ¸T½víZ äìnÔ )"´ÚGŠm'E„ö1WD¨”C¾¡Üwc¾cc¾ÑâvÓ~WH¹P6ðJp\švQ(«=ì½’s×võ©ˆ0;7»zÑšäåå9ä9¥ˆÐ:—  ýüüêí¥‡ÄS‰dÎFå¡Bí¡Få¡B¥½r-W¶K®[9 .dñâÅx{{›Ý¯ŒF”Ââ7fc±ÒWév¥P©´ÍXhD¥V™þ&eÿ6åþ.öU<Æì>'üÌ›7¯Þå±)zcQI ¥€ÚóÊïß‘ˆŽŽfúôém·&½‚RTüUú3;r›‰ T*¨®ÜVP*mCuåøŠ©¸ýƒ­0°ë@Úµ1À.üÖ„ <‚<ðh\üåê‰Gc´!Ž ’«RÏݺRZD¸råJgwÅf©¹©|pà–§.G9£@ϪWŸU3bć<÷¥K—dàÀ )"¬‘‘‘dÊfÁ°Õ{€Š+viÐæa9 S{¨¯Ì§R!~P©Š7((åî—}®ªî[z|¥ûŠ‚ (Å·Q0ÝV¡ª´Òÿ:…ÊÛËì+ý÷T Š)ø­$»Å²ŠÊAy™€¼ô÷n.@1=¾ôogÃv•¶øƒ™J« ¨µjÐXÞni¿J«*LÉÿ¢( çJ ¦/´¡§Rg,2ZÜ^1Ð+·O_æÿ§â¯¹ôƒ§ÊT›‚kOU¹ûe·WܦöTƒGq`Ž0”üê0€Qo,^ÃPà®l/û½ô1-ÛVÙ•Þ*~Vá`*ÐiM²é+Ä£Öe!Ê:|é0K~_º#ëÈ×—,ãþ10 hdáA:hü}c’ã3‡Zç2#Ð 6ÄÃÃÃÙÝp (… †Bë9OÂM(ÅÁ¥¡¨øoj žümUÅAkm­°VSбø*õèw,J¥Vç"WQöñ©á„Ó#ßÄÃ’}KøùìÏåöuïÎÃ<ÌËÿy™Âá…TáÁiàñ•»¶ïª» ×  Ï;çì.Ôª?ÎÿÁóÑÏÓ?¢?ý#ú;»;B8†‚ËÏB”e ”C=ðl쉶±ÏÆž( —’YÉ'~²ýË8ve :JÃðë†óÌÍÏзE_ÿ8˜[ßBz@º©¨P“ !03=Û÷8$ß;::šèèhvíÚEß¾}ín¯>s™Žú,22ÝKG,uvWÜÒ™ËghܵJ.£ÚÂÜjf¢zòŠòHËM£iæÎîŠ[JÌH$È7ŸÚ{5xWÎQöhäþ²ÚÎÕ‹OêN²lÿ2þûçÉ*Ì2môdb·‰<}ÓÓ´hh>ùÔ©S¬[_¼ÀÓÃ=LÛ¶Ž}=z4^^^’ÂQ—®ïR³SÝ·õÑÞ˜9p&^Z©¶ÅŠ]+ªŸ/ÊIÌH䇸x²ï“ÎîŠ[úúÈ×ôè_õ8¨½Õ¨½ÔÅß+ÜVy©ÐxkL·+㣮·…×RDh;W-"Ü~f;‹÷-æÛøoMµD!L¹y ^ÿ(~~U¶Ñ¶m[^{åµZí§Z'#Ðu 22’Üã¹¼;òÝ+EBrÙ»V©*=1+}^ªóÙúøŠ³?8p*,Ó e`•— —Æô\1@V©ëþ…¨t& sÁuUÁx¹™ÊÌ7ÌAU”2W" £b>ð•Vˆ«ŠN§ãæþ7sFC{¦u›x|Öã¼úŸWymzqZž ûXòû¾<ö%z£Þôø [ðTϧ˜Ø}"AÞ§ÒîFè:"+ÚNV"´]…Hê’¹¯"*UñU Íû0­×4ÖXÇkÓ^C½½Š)|c}™þâôÚúë”Ä,ÖIad|!„âŠ3gÎË€ðôô´»½3&§êƒâ€ ùÍZµ–.¡]èÙ¬'=›öä¦f7Ñ)´S¥Y3n¸ók£ôQÊ?¦ök-ß~ø-ýï©Ð¸›’¸Å:IáB!D˜üÂd>Üø!ú =JCõt5ú~þâgºvíZ£¶òõùO=Îá¤Ãä©ó¬? )u gÓžôlV,ßÐä|´>V~èçCLŸ5%k— Ðcl`D£Óh d×÷»:ç´p}@ !„W™ßÿí;¶Ó»Won¿ýö:©1étk'ŽûGyT’…<Ñeêè~wÖ½½Ž‘#FVzœ‚Âé´Ó¹t„#ÉGLßO\>A1d¨F ¡k³®üõô_6ÿ ó^ŸÇ¼×瑞žÎ‰'èÙ³§ÍmUWLL »wï®Ö±}úô©“>  ëŒÚNŠí#…H¶«Í"Âääd6mÚDJj Æ£sçÎK Ù¼y3›¾ÝDh£PF Aß¾}Ö~ll,³ß™Í¯û~ÅßߟÁw fÞÿÍsX1á'k>aö¢Ùè²u¨QÓªi+¢VEÕÉÿyjj*}õáüåóiŠÐ´ûóã†騱£Ýí2dôöÝO^Q^^´oÚžmQÛ ±»ý·Þy‹×—¿Ž¡•CêÔhÎhxtð£|´ü#»Û·ø¼óß"N‡r‹™¬ÑÐÒóè³ÒûÎÞÄgÄ— ”&%§ÈJzFÅ"¿’"B]9D• â~8äç ¬³@5--Ož}–g¬·è¸m›CžSŠ­“è:ùÿìÝwxåöÀñï¶$@!”ˆD ) ri ˆ EP)¢‚DEÁ†¨÷go×¶ˆv‘¢ (]‹”«(5„j¨IHBÊîÌï!}wgwf³!x>Ï3OvggÏL6;›³ï¼ç}ãYöó2fÍœE—.]{Ë–-Ìý~.·¼5¨ÿ„AkŘ÷ã<š5mÆ-·ÜBDDDÀb9r„©Ó¦òþ‡ïÓ³gO†ß1œ=z,þÆyoò{¬üs%G_Ì›†ðÐC,¾žü‘ißLcSò&:´ìÀ½Ãïåºë® Xü/¿ü’Ïç|ÎÆµ¹¶ëµŒ¹w 7ß|sÀâp:ØíÁÿ.¾e˾›ûGO¥ßþôìÙ3`_²:İÑÃØ¸y#9grhÒ¬ o>û&}nìc:ö©S§hÛ£-ûÏîGi¬ †©ØöÚ¨r² ‹f, h"ëÎÌÙ3¹÷©{Ékœ‡£@XwY‰JbÍOkhÒ¤‰©øwÞ'sVÌÁÕÉû€ B×…2å­) ½c¨áØŠ¢Ð¬c3vÚv¢\«@8Z¢tìK쌼i$½÷‘©ã÷&q~"·?|;ù7åCt±2´>°ÏÜý ¯ÔCû‡lÿµÑ¦VÖ-[g8vNN5ck’scNÙ)³Àú•EŸ-2ý;x2söL†¿<åVJŸ¢'!äûŽl:BTT”¡ø=óï¯~õ7ÿVòÁ>ËÎ?üáw_×=ô`¹}9j 7ñU°Íµ1ÿÍùôécþ‹NiÙÙÙDÅE‘{W.¸k˜SÁ>Íζ¥Û } Q…ˆÆdÝ–ÕÜl!3BHý;•ÈÈH¿ãoܸ‘¶ƒÛâ깯ƒu•iMcØa·ÉÌË$=7ôœtÒsÓIËI+¼]úgñÇ6¿³u¸NºqX œûŽI‹º-Ê$Ë5Bk¸}z‹N-ض¥“RØE€4°'Ú™“0‡z?†óÔâÅ‹ù¾o_>U·ßoµ2pÁz÷îýI¡>I ƒ >>žiOÓZ2ÁñƒM‹6¾Ü˜œœL‹î-È”¥?GÓÀ1×ÁŽßvp饗š>vwÞžð6Oþ4J?7ÿ„“ öŸµ9¾Ãø·à»î¿‹¯¶…Ú]-ù! ÕV#mWšáVÏ^·öbYî2Ô¶nÞúö]'·Ÿ,·±k£b£8Ýý´–¸—¶âöıcíÃñk\ZƒŒ›2ÀÝÕÞ}Ðhc#öýµÏpüç^{Ž·f¼…³¯ŠÿKÕZÉf¿7›ýËçŸTff&u®¨CÎ-9P·Ôƒ¹`ûÞÆì7f3h ±Ë´¿þú+=èë—ÛA>-k-ÜZ÷V¾ö­¡ø­º´âïFC¬‡ ²¡ê쪤ïNx«~^^5šÔ gXNÙó¶ÀAh¼±1»þ·Ëïø§N⢫/"D¾çRÓ¡ÖOµ8‘|Âïø‡"¦{ Î!NÏåBÄ7¤ïJ÷;¾žá÷ ç«3_Á^6:Mÿ×”ík·ûôØÑ|²ïhíe£ƒÐvo[Öÿ¼¾p•ŠJ¾+Ÿ|%§âôx{è¡lŠÛT²å¼´¨òM½3Èm"œ‘›QÔçØ*0‹ÂÄØ£t¨ýsm¦ÍFËè–4Œhè÷®žxæ &ÏšŒ3‰¡b=f¥¦­&+æ®H›Šä©:Ð­Ï ´/$‚ 4@:DýÅÉ䓆âE_ͱ^Ç´–EwNA%u8–tÌP|oŽ;FÃŽ É»3Ïã¥8Ë: Ãc†3íÃi~ÇONN¦yßæ8‡yù'¹ºfwå×ù¿úãÆ´»«ÎA^âïƒÖû[óç/ú_ÏèÇFóé®OQÿåù´³.²2åá)Œ6ÂïøÃG çëc_£¶òß²Ô¤»&ñà¨ýŽŸ””DË>-qÞå,ûå Bg„rbû ÂÃÃýޝçÊNW²9n34ò°A.„}Fú®tCWAj\VƒŒÛ2ÀËw'ût;×ôÚÒîTœäºrÉså‘çÊ#שÝnÞ±9yC½inùÕÂ󃟧]·v¨¨¨ªŠŠö÷,¸]ð±íÏã뗭烟>@½ÎûG¾}¶„i (ª‚¢*¸WÑmÕåqýß ÿfÉÎ%ð/¯á±Î°rçÛw[A •b·Ý¬ß³hIÙI Óxm™e¡íض¨6µð¹ñÌÜOý0e°›FƒÒûŸb¡öõ±X,X°øüóÀÄ8ow‚·§U°|n¡ÚCÕ “cEuß"YÆÀÝèÏþ0¸Ç·îØ,6j„Õ FhŸ‘a‘,xnö¹áÍvx¤Þ#L|o¢÷í|päÈ’’’èܹs…t1+žZ¡Ýú ’@ûâÂxWUÅ?7j@Zhï/}ŸÚõ´f‚R½Ûé§Ò9a?á9yˆ‚“Ž“LýïT"kù©Ï›)oM!¯çä@m§2gÚúlíSøÏ§ø?"o·g¼9g»R²'µß©0a»ÖÌXÃÛkÞ.‘0øòó‡w~к…x[VmáÙåÏ®*øÞ/•=úúø´yÓP‡yO`”kþýî¿ÙY'P”ùrûۥߖ½Lz‚­Ñj•ÿ|ü,m,„ØB±…j Õ~ÚA1d` IDATCˬ+¾þ¦a7áìã!y…ÜN¹Œ|d$S>‚KuáR\û¹ýèvðÖ >r/Ï呉Ьs3œŠ—â©8µÛj±Û¥Öçåç‘éÈ,™<çÙ”hiw^éäêG¯¦z§êZrì&Q.HhK8IÙ+Fn¨1*¯~ù*zƒÑM>œ‘NÆLS¶…_Ïo@›RëÒÑ^OGÑ*%ZaÆÂe»°èÙ†nr ÖVYÿÇzÿãëQÐMžT‡Êñl-ù”MžK»X´ø™y™þÇ÷tΖÙÌB£ÈF%àaZ\z»Çª9Üõ?[ÞÊ@míáóOÇ:O­~ÊÿßÍèèhΜ9sÁ$Ͻ[´à?6G¥°ú°9*ŠO˜<ƒúâÂygïJ+1 c'Œ…ö~ÆÙ€çÖ·âñ)Ü=înt‘ð*ŠŠ2<±@®#—ÁßöùC»ÐÀ€Rë~zSâÝšgËãÉ%Oú?—ævÀ‡Þù¶|Æ­ø¹:U¼ÑÔ€'Oðúª×5EW?"àØÉc<¸ÀÿhŽut¶¹ æÌšÃœ7æøß›ãh_¤t¨—¨|úͧeÎ9])û»¥£M¾P¼®¯.ÞpØû¥|w¬hI˜å3GlãÃvyxm/`µX ›Å†3ÊI~Z>4(¶Ñf´b¸âIàihÔ¤ö0;‹¥0†…b·Ý¬?Öäûïwßõ©Ëq =îëÝa/|nA<3÷¿ûá;Neœoå.u…roÛ{ýþrÿ}ø÷dffj…‘JŸ» „¨!<Òñ6v«‡ÕáÓíW¼ÈöÔíx¬B8 u«Õeï£{½¿ÈÌ>Wë¾Vó´VXZœ ¬?Yytè£4hÐÀÝÓý–——GBBüq@âU¸éÓaÌ^ÊÏç%àÓs«_²ZyiÆŒ€ï.55µ\FºH,¥krðþAì‰ða¼xœx¿h”߯ÛTð?y­…ç,P½Ø:w »$ÂµŠªUyëõ.pAˆ£¨¹©tO§Ò-Œ¾>®º|è1•XµÙ± ŽÛ—«‹…,WVÙÖÏþ%ï’‡ñÍ—^!à®Ö´P´cדÇã´`Ánµc³Ú°[íÚm‹vÛÖÄÆ‘ÕGJ¾~µ)™<œ„ÍZТE ¯­õîÖß½ànòÉ÷zøÖ=V&>:‘×u(q‰(qÙ¿à÷ñõñÿµýw¾|'ÊåÞ³ø´޼r¤(9¶ÚJ$Ê·KKº5‰··ÀÕ¼ØDé×ÎU²«°ïÉ}^ÁC}ÑèúF¸®òò”áÎp–ŒXâw|=ÿJý£?ÒÛóëgÙdaÀõ˜Üg²ßñcwÄòÒÏ/¡v*öþ+}î&à íoàžïø¿éMi=°5Î;=_³ýlcÒk“üŽí«”¿R¸¦÷5l[¿ %VÑú(²âØë`ÜcãxlÌcÛWXXØ…‘<;£FAb" µ%ý',Œ#9Ú·áòh}$yö$ÐÄvÐÆÆe©RU›ýÈ×Ëôitº¥Ê5Þÿ Z÷YY1Õ#ª{ÝÎ_œþ€))SJŒ­Y†U”*üùП…­7Å[r¼Ýžl›Ìëk_G­ë%ˇ[Gž=âsÿÂG̃sD­ç%þYˆ²Gqòyc}Ô½‰[ÇΓ;½¿~û``—Ì}a®ßñ›ÿØœmǶy¿ü¾vÈGÿþÈcOë™þÙ®lï­èépQ­‹x¼ÇãØ,6lV[À~vý¡+y:Y´u¯•¹ÏÏ¥k×®eew‰_qá…“•—åõ‹‚}›ù æ*Òý¼éç¬Ø²< ’“áGÂyhpà‡SŒíË#=‰“'<¾ÿ,,ôëܨ*þÂÑ´iSâªÆ±}ïvððÒX¶òò˜—ýŽ Ð Az\Õƒ%[–x…ãGß}ô¡øzî»÷>Þ˜ôûvïƒÆn68Õ7UgÖžY†â¿ðÔ |0õާw_èwªü· óöÌ3¿E‹ÜÕë.¦ÎŸŠÒG)ùß_ëJ+m"Û.ÀõExx8›VobïÞ½Ì;—í»¶Ó÷á¾ÜtÓM•rh¹r÷ý÷0z4Ö«Ÿ}ÆK6/õí P.­ÏÂ7RDeŠ@ó¤ælY½ÅP¼Æm³ç_{<_Ê<—ýq»ÿØm(¾7™™™Ô¹²9CsJôk,βÂÂÓ]Ÿæ—Þð;¾¢(D\AÖY/#[Zùdô'Œ¼{¤ßñjÆÖ$í–´²Wα}ocá; Ëe(±;wÒìÆfZ‘¤»úüsEx[áíß¿Ÿ&]›—‡‘ò´"»Ó;Næÿ%Šá£†óuêמû1ÖÅV>ý9w¸ÛïøzúíÏüœù¨WyØD&Frz÷iCñ§Í˜Æ= ÷ ô÷ðu+t8Ýß—ýn(¾¢(D^É™gÊv9© µµö‡µ´iSº3q`ìܹ“×· ïÆ¼2}„-,ÔÙ]‡ÔíÆ;_gffrI«KHk–†Ú¦Øß(_{_´¯Óž5‹ÖŽТs ¶çmG¹Nц{SãÚ8ÐÝöÆM0ß›ììlÚöhKR^JkEëòs ,Û,Ô=^— K6а¡ÿ#G8tè­»·æÄÅ'P¯TµÏ¨,°$[¨öW5~ùöÓC ~üÙÇ<öÚcä×ÍGi `=fÅvÈÆ£Ãå­×Þ2[HZ<ò|õUѺ;î€?„sCLv¬£õ7 äÈÅI¡>™Ú-X ®šíj˪±vñZáÖ-YG•EUà€›@Õ¥Uùcù†ã{Îg¯†mŽÍmSËÿ,4<ÝÐPò `µZ™÷Å<³ZŸWЊ¯T´~rK­´¯ÕÞpò ðëw¿ömì)õ@6ؾµ1ð_ËmÞ&Mš0vèXl_Ù £ÔƒÇÁ>ÃΔ·¦Á¢Q£F¼üðËØ¿¶CAY0bØa™Âwg(y˜ñÉ ªÿ]Ýs[2Ä䯔Kò 83‘¨ÍQX¶ºùöqBæ†ðÛ¿Ž?bønks¶Ù6­ÿs>…?­Ë­4ÚÙˆßoµZ9‘|‚ö'ÛãøÚu…ËZ ö¹vj/©Í¶åÛÊ-yíýwdÓolLèœP¬K¬Ø²:=”Aµ™JžAû|8µëà '|z8ŽiB§‡9;’¯þï+ÓÉ3h¾S™BÌ/1T›Qˆ¯#hÔšÍ?m.×ä jÕªlýïV¼º€^§zqQâEtÚ߉é÷Mçè¶£¦’gÐZÙ%ãõž¯·&ŽêS«³<†‡?Lúžô€Ì|7ú¾ÑœÝ–-3¶ð~÷÷Y÷Á:r÷ç^pɳ¢($''WôaøoÉhÑ¢(y®U æÌÙ³ “gÐZžË³õYfOö*Ê݈#TKu‹Ò8D½úº«Õ³gÏšŽ™žž®Æµ‹SMªõ:«j½Îª:š:Ô¸vqjzzzŽÚ»+V¨Õcª«övÕr£Eµu´©ŽFuÐðA‰øðaõ’«.QCbCTKm‹j³«U/­ªN˜4! ñ³²²Ô¶ÝÚªÕ.¯¦:š;Ô°+ÂÔZ—×R,XøzÖ®]«F7‹VÚ†©övµJÓ*j£«©»ví Hü;v¨ [5T«ÄUQ-µj“ªê×\¡žÂÀ>Lv L“:H¤C¾qÉÉÉÄÆÆ½{Ê…"))‰¦M›VôaTJÙÙÙœ8qBÆD5(%%…ÚµkSµª3³ˆ2äÜ5NQvíÚE\\\EJIN'¼ö¼þºv ];m¢”Ë/wû” 6pèÐH ¯Nðlذ! ´ÌD¨O2’ IM ô¼¼ÿ äåù2ƒ†pgüøñ}•VJJ 3gάèè´fΜIJJJEF¥%ç®q3žW¶mƒà?ÿÑ’g‡^}Ö¬ñ˜<©Dê,î§Q7Br}Ò$Ò‚eÜ1›T’>lÆ5mÚ”§Ÿ~º¢£Ò’×Î9w;¯f"T­ŸóóÏkýžZ¶ÔZ[µªØcó@r}’@ !„B”‡={´6V¯Öî[­ðÿ¯¼2ûb¥&]8ü››ËóÏ?O¿~ýHHH@0B!„[ W^Y”<ÇÆÂªU0~¼$ÏI ýðÜsϱmÛ6žzê)æÎˇ~èós¥ˆÐ¸äädÅÃÔÊBWRRREB¥•-}xMHII!;;»¢£Ò’s׸ ‰ðÐ!èÝx²²´Q5zþþ®¹¦bŽÉO’³è“ÚË–-ãƒ>àšk®á•W^á—_~ñù¹Ò!ß8)"4G ‘Œ“"Bs¤ˆÐ9w dá‘#Gh×°!×Ö­«»Üß±£6÷’%Ú“6„¥KaÒ$¨D£ÑH΢Oú@û(==C‡Ñ AZ´hÁï¿ÿîóó¥C¾qçM!H%%…HÆI¡9òÚ™#ç®q,"¬W¯ê™3|›žN”—íÞj?^´â®»`âD¨QÃÔþO2õtC$gÑ÷jÎËËÓmÉÌÊÊbÇŽeº ?~œˆˆˆÂû‘‘‘œ>}gÁ8ŽB!„¸ =ñᇼcµnX< P·.üðL›f:y^³fÌ0B”“ >þûï¿4h111„††Ò³gO·Ûååå1|øpjÔ¨AÓ¦M©U«ß~ûmáã±±±deeöçÛ¾};­ZµÂn—F|!„âB6xèPÖDDà©180p lÙýû›Þçܹн;œ= ðVëÝØlž‹å=Óû¾»à³¿£G¢( ÷Üsß}÷Çíüq~úé'~ýõWZµjÅ[o½ÅàÁƒiÔ¨íÚµ cÇŽ,Y²„°xñb:vìèóqH‡|ãd&Bsd63ãd&Bsd&BsäÜ5®„Jkûöíê¸qã*ú0*­qãÆ©Û·o¯èè´äÜ5îìÙ³ê¨Q£ôøqµ}•*êIPÕbË« NxöÙ€ìÂåRÕG- ¡ªK—$´_®¿þzyÿé&=`ݺudffÒ¢E‹민òJ–/_^xÿÚk¯%55•9sæ°gÏ.×z³È‰'˜>}:‰‰‰%–'N”ØNÖ•]÷ñdzxñâóâX*㺩S§ž7ÇRÙÖžÇR×=ýôÓ4mÚô¼8–ʸ® ˆð|8–ʶ®x¡éx‹ÁðápñÅŒ={–»‚­²yժѨm[ÓÇœ“·Ýï¿H½z'Xµ zô(ßתàvbb"Ï<ó Ó§OGè»à»pøâðáÃ4kÖ¬Äú–-[²|ùrE)ì>àp8¸ôÒKƒ~ŒB!„’¬,øúkmÒ“½{ W^²Z9­(Ô,†½ø¢éÝ:·Üƒ{5jóçkó°ˆó“EUÿ9ÓéõîÝ›œœV¬XQbý{ï½Çã?ÎÉ“'‰Š*¤fòäÉ<üðÜ:uŠš5kÞo||< C !„çµmÛà£`útÈÈ(Z·ß>ÈìÝ»Y?|8¯) ×U«Æ†ÌLS»Ü½n¼vîÔîwïßÅþ :É[ôIŠ:Ýï-ö-`÷îÝ8"##MïCŠ“™Í‘ÙÌŒ“™Í‘™Í‘s×8¿f"Ìχo¾ë¯‡æÍµIO ’çÆáí·áàAmXºöí Gäx>­ÏëÖAÇŽEÉó]wÁ¢E›<ƒä,¾¨_¿>[·n-±~óæÍ\tÑEX,ÓûY}Œ“™Í‘ÙÌŒ“™Í‘™Í‘s×7©©©4ŠâÚ5Š–š5éÒ²e‰u""¸ó¦›Šžxà¼ð\r Üq\¶Ù´þ‹k™í¿ÿ µj•ØçØÉ“ù6,ŒGŸ|ÒðqÏ›§åìs¯<ÿ¼–£;†CŒä,ú¤4СCjÔ¨ÁæÍ› שªÊ¦M›èׯ_@ö!Ã`'3š#—àŒ“™Í‘×Î9w}M¤Ýάӧ)óŸ¶XãËX«•wÜ¡M­ýÑGðãàr#GÂý÷kIµƒ‡¥Y©ü1i<ú¨6LÝ®ÎÈ‘†Ãœä,ú.ø:;;›¥K—pìØ1òòòHLLà†n <<œªU«rÿý÷óá‡Ò¥KZ·nÍÛo¿ÍéÓ§yðÁ+òð…B¡ã•Y³x¡gO¦yèîwø=$„ ¯¼»v•|°Kxà¸õV¿š¯4P᧪ðä“ðÎ;Úýðpøö[èÝÛïP¢‚]ðE„ mÛ¶n[½z5±±±¸\.{ì1¦M›FFF111|ôÑGôÀ»:>>žÕ«WÓ©S'ú÷ïOÿÌP$„Bˆ"×Ö­ËìãÇièæ±Ç€öh£hP½º64݃jýžƒ 7WëãüÍ7Úýzõ`ÁhÝ:(»÷IÁpv9‹\ñì‚O ý¥( ),Onn.³fÍ XÌ™‰Ð™ÍÌ8™‰Ð™‰Ð9wý³|ùrfôìÉTEAvqh­Ï}€u  ÷ÀÚŒááA;¶Ó§¡_?XµJ»ß¬™V,x¾~´ 2„ÐÐPI ½Œ¤«ÕÐä¹€tÈ7NŠÍ‘B$㤈Ð)"4GÎ]deÁòåðòËtã vª*< áÜ&¯]s ¬^ ÿ £G5yÞ·®¹¦(yîÚþûßó7yÉY|!-ÐA ã) !„ø§JMM¥Yƒ´×¹Š˜ª(ÔhÕŠ_þ÷?Ï:¥%«ViË€ÓYøðrà+àË‚Í>¬MO7ûkòÇз/ä£C‡Â—_BHH…ŽÏ$oÑwÁ !„¢âDGGÓ±Y3^ؼ™ö^¶`³ñÒ矗\yà@Q²¼j•6щ»v?«Z´ {çμ8c32¸xÍjeìäÉüu|¶`6:^V–vÿé§á7 #ãŠó€$ÐB!„(W“ü‘ûbcYZ¬µ¸¸í@úÅÓ*, >û¬(aÞ·Ï}@‡Ú´ÑFÐèÜ:u‚sÝ/_0€{öäEaMõê$ V>¿”Ÿ|=¤’g³ÁäÉ0jTÐC”#I ƒDfõ1NŠÍ‘B$㤈Ð)"4çB:w5jDÈW°ÎC+ôó pÅîT­ªMÙ×¹³–4·o¯­s£{÷î¼X«w8ÁØI“÷Kø@Uá¹ç`Ü8í~µj0gŽÖ£2ÉÍÍ%44´¢ã¼&IlÞ¼™Ù³g³eË–Š>”JGŠÍ‘B$㤈Ð)"4'çnlT×ÙlÜb·{\úØíÔ Añ0–²!ÙÙ°};,ZÄäÁƒyÑMCHpZUiuútÑÊš5áæ›á­·`íZHKƒŸ†—^Ò¦ïÓùRöü´i¬ ap[Ÿóò´‘ñ ’çèhmrÃÊ”t( û•"ãd&BsäµóÝ3?ÎéÇK¬ F\b­Z5>øì3¿¯È½6q"GŒàÍâ3ï•ò¹ÅBëô/vf&ì߯uµØ·¯äí}ûŠæ©`=ЮXˆç€„:u [·¢.-Z˜ê,|ã7²cÿ~ÃÏ/.;;›‹.j‹ªFyÜFU!'\®FÀW4mª SCš°°0"##©W¯^EÊyOè iРA@&e•—Õj¥ëu×1pÁnñ²Ý» 9÷Ü´ãBèëíµß¹Sw;{­Züºu«ß îŸK–pã¶m´ò²Íàݨ(¬_|áWl€ÁÆñþÓ–žŽ»ZÀç¡¡¬™0¡äeãâ·Ožôë8&W­Êýgϲä\!`pºAZ<èﯤ+::: qòòòÈÎnŽËõΖ9@w:w†ÄDˆòœoŸ·bcc‰eöìÙ}(ç=I …¢I³gÓ3*Š[òóÝ>ž Ì ã¯¾Ü$%%ñÝwßù´íUW]ÅÍ7ßìWü'N””äÓ¶ÑÑÑ4iÒįøzf͚źß~ÓßÐbážð{šâ÷Þz‹Ußè% Úí<5a:tð+þ°ÛogÝwßq…Nbú›¢°dÍ¿ãë©]§w®Yƒ·¿úfàÅÚµ Õk|8>cš6e¬—+T÷[­¼öõ×~Ç.ðè¤I¼1bo¹é¢ñ¥ÅB÷ví°>ñDÉ$¹x· _„‡kÍ®i?‹/ѨNW^Ɇ͛i ›6ͯø5kÖdÊK/ñZ±ß/8Ô*¶Ý×€í¥— %ÐC®»Ž;t¶ûUUéõļñöÛ~Åד‘Á¾O?e´ÎgÇV+/¾þºßñë]|1Ž?ÿä?Åâj¢uEPAv;­Zykguïã)S葘È|_>v÷Ô¯ðä`âÌ™ôªY“Û¼Ôc¼j·3éÇ Åoܸ1θ86nÛFÁÌÎI@Á™{Øŧî®dªªÖR|ú´¶œ:åöçàÓ§yßj%MQJ´B;ÏU•ßW®„•+½hD„Ç䘘¨UËëÓA‘cTl,ï;œºøbCï=Š¢°k×.âââÛ›ˆˆ #y–"B}2‘JÄÇÇóóôéô+–L.T^™6áÇû+99™ø+®àC$æA«•©Û·üãKëÖäoÚDC¾iKU•ä#Güþ‚pK—.XV¯æªbñ¨*½,–ßö>öž9ãwuÿÈÈKLÄ[g'ð¢ÍÆž¼¼Àü¡(pô(8@æ®]ô1‚5¥ú"æ׆†ò¿Ã‡ ]ûS…Îqq¸N*±~wV«U+±.*6–…ë×û½fÕ«sOf¦ÛKÀ…ûVÇİjï^¿ãëi[£Ë22¼î¿¯ÝÎĤ$7nìwü:ðغut>w? H zòæ×V©Âÿ²³ýŽ Ð§I&îÚE¬‡Ç@§°0Öde•˨3kÔ`yFžÎšÕÀ‡;3S/‰ò CÍš,LK£àÝ;èOQ8øõöÛùxÎCñïîÓ‡[-b€‡Ç‡X­<úßÿ–K 0²nž7wÍ›€§›5cáÖ­¾s:áìY­ãlNœ=Ëî;Ó¯ Î}6ÄSÏm~¿ÅÂÀ¾}é]½zÙä8-M/ͳ?€â_Ï>ö£ÍØGž“㘭¸/nºòJoÙ”?ÿ,—:''‡±cǤ†&--Úµï÷© G:Ý9v쿦÷YѺuëÆ%—\"54^H tÜ ªL>÷—üáwò GõØXB’“iæa›m@õØØrùæýùœ9ü»ys¾ñÒ ½ p¶oïwò ðÙ·ß2´aC^)ÖÊô ”8>Ò·¯¡¡±&~ý5×GF2-?O_>î9Ò÷FUµi¦€ƒµŸ¥o>\8[V8wî÷(Þ = ›«µàÔ¯-[jË•Wj?¯¸ÂkÓ†Õj¥fÕª¼º{wa V¡b­fsŸ V¶¼øÑGl1‚{ù7Øfãƒ~0_Ϙ 7r$ozØÿFÀg(yøhÞ<†6lÈòs₩%Ï`þ Á„ x¹ys¾òpþLµXè~ß}å6dãÈ·ÞâÝä¯ß«³ Ç;y2¯ N¹øÅ_;H á7?¯ ÷Á7ßÐ#*ŠnZ¡÷‡/ºˆW_­Í\átB~~ÉÅ争íÛÓó§Ÿèç&Y}ÞbáƒK/…~ýJ$Å%~¿í&Fc´/QÖ%ÏG€ÍªÊ§F»:8Zâ[³&ƒ£¢˜°a§NjžÛß!!ü¾v-ÈÈR IDAT\v™–@Áädäw”Kò Z1Ü…R€^dèN}Òñññ0mZá‡ásV+M?ÿœáwßm(^rr2O5oÎþ °ÛysëÖr»tÕ£iSvì ¥‡Ç»;Ì+gõÿ1‚w…O,R~˜×'N ÀÑ_˜ÒÒÒ¨Uë~åŸÓ-Syë“è Ë~©V×o¼QKÀ\.­µÃéôùvœÓIvݺl=|˜æ¥âo²ëÔ!nëVرCKº »Ýó}?›üý÷üûª«˜ï&I[Ô¸úê¢ä¹à«ÿ€?š6¡-[¶7ˆëÜ™ðS§´êoÿà'>ù$×/XÀ §³L+ô@÷K.ÁÚ­[QrldœjÕàâ‹¡aC·?Ã6$nذ¾Г,<ôôê›7Ö-ÚÏ;ŠZ].m<ÕíÛ¡x±Vx¸6äSË–4jÙ.¹„?÷ìáj7‡5¸²_?ÿ“gEÑŽ#7—GÇã‡v[ˆô¬ÕÊ»Ï<K–µâéýôe›bÛŽ‰eÜÆ¼Yê»ÿFÀi·Óø–[<&Ç…‹—ô€¡ÀòRë ¯´.Ó¾ï— ÀËÀW¥ÖOºçæb­_ßT|=#wK­˜¹u«Ö‚jÂXà5 ¡Ø:õÜýß¶lÑÞÛ&|€öå³x½8Lù'Ï”L Ÿ>hЪW‡°0m©RÅÐíÆaa8|û÷Óšs­Ïµjñéñã›zð°aLxøaŽ¥§óEHkK¼! > O=åõ#CüCI tÄÇdzrÚ4º µ<öüï¼QR2ðPúBùàM´.å©Ú?ÄÒ­ÐÝ™V+Ñ «AÇ€Îh¿k,Ú¬?×KѺA˜1è *¶Î‰öx=:3 é$Ç4lX8¥¬7™™™ôŒŠâ—ü|:yêW›ŸÉÉ%“ê-[`ï^¯ï~àAŠZ° ‘TàZ«•_ú÷'ÌéÔ¾œKŠ —â÷‹ß.õe©#°J´ïFS6ñ,me¥öß-¹1Öy£¤‚÷_àWüÐíX Z¡@'´+Á˜Ýª#ð3¶B¯Aû‚¨)c: µÆfµ_ѺGaµj] BB´ÅÀí»W¬ ÿÁƒ…IìP‹…1ññtˆ‰Ñ¶µÛµŸÅ—®yÛmÜ4>ý¿€gýéûìƒÝ»w3¦iSÞu:I°Z¸`AÀ‡AýÕW¼8|8·=òÈÙúl¶ˆ0+ Þ~Þy²²ÒЮ\ø-Љ‰‰$&&²råJºté"-Ð^Hñññ¤L›Æ@/`m€âöBkIjqîþà `I€â{“ ü­E¸À*à=àûÄO¥¨p4Z«ÝÒs±§ ~6Ð ø [¡?vÛí¼Ù¸±éäØWñ7ÝÄ‘… éòÜs<çÏÐuYY°u«–PO®à&´¾ãWSTˆô-ð Z «Yî ‘nž¯ãØze±”MX<üœ‘žÎ–}ûxóÜS7O‡‡³ä†Ê&?–Ôìl†ŽÍd—‹DÀa±sÇ<7thÙnn'=Ê+¯½ÆWçšµ>·XØÛ¥ ¯ß~{Q·ÒÝ@<­ó÷¾ÕÊóçs0!—Îí¿·ÝÎÌ%KˆªQ£è9Ï3p{ö¼y¬3†ºŠB? >$„ߎ%,"B»’eRÁ—Ï5ùùìâë×ç·M<ä‹ììlnˆŒdM~>7Ùí¼o°hÕ›^Í›ãÚ¶¬Úµù½Ød$tõײtÕªrës_‘Œ* |ù%¼ð9R°6 ‹å~TõÂO  H¡>I ƒ  t=‹…fC†0üšk´D  k„ÁÛÉðäÍ7“x®¥¿ÍÆ[?ýD\ÆEÝ>\®¢¥ø}où¸m÷ß'áäÉÂVèîV+3ÇŒ!ºzuÓÿ€±Xøî»<¶o_a_Ôkl6–¾ÿ>áUª$þÈ×_çÆµk¹­°ch(ë ú>Iff&­bcÙuôh`?›7³åJxõUžKTàZ‹…_.»Œ°ÐP­14TkÑÓ»íá±cư(;›šh_¨ˆŽfùÌ™î_’bü|Ý‹÷…63ò†'}ñÛanä O F䈡|GÞð¤Ã¹96˜yÃcüs#r,ÃÜÈžÜݧ-bŽÍÆ#«W—ÛÈžŒìߟzóæñG€[Ÿ ìÞ½›ÖMšðÍÂ…2 W,] ÿ÷P|ÄÙý ^y%ƒ[ni‰Õz…N…˜˜|vìøµ\3¤´TQîFŒ¡µ}DDÀc÷Œ‹S7ƒºÔžqqïÍŽ;Ô›ívUu%¨Ú·hü£GªÝUu¨#úö hü¬¬,µ½Ã¡* ~ ê“£F4¾¯Îž=[.qû¶l©þï\ïïo@}Ûm?kÆ õ «UUAd³©7n h|=Ó§LQŸ´ZÕ?AíÙ¬YÀã¼ÿÞ±XÔמ>àñwìØ¡Þi·«ŸY,ê³<ðøz>ÿøcõE«U½ÑáPOž<ðø³¾þZkµªíBBÊå=~æÌµµÝ®v©_?à±}‘••¥Ör8Ô]»v•Û>¦OŸ^n±E‘Í›Uµwïâ;ªÚ°¡ªÎ˜¡ªŠRÑGW1FŒ¡Ž1¢¢ã¼& tŒ1Bmi±¨Ó§L xì;v¨·Øíê-v»ºcÇŽ€Ç×sÃå—«›@íæp¨G xüíÛ«+Aíèp¨gΜ xü{ûõSç€ú¯ÐPÕår<~EÚ·oŸÚÛnWP;–SÓ¾F õwP»ÅÄ<¶/þ¡^g³•[3 }{µihh¹ÄVUU½16VmUォÂÃÕ!;—[üê¨Ûo/·ø#‡ Qÿý÷r‹¯gûöí¶oaÞ‘#ªzß}ªj³%ÎÕ««ê믫j9µkT’@ë“Q8‚ä°ÝnxØ:oâââȹì²ÂÛÁ6yþ|\q—y#€>š7«ë×ç†= [çÍÄ™3‰­^áññ\?ÀFa»â îݼ™«ŒŒ¼áƒ±“&Ñ{øpV”Ó¸ÏzÆL˜À´÷ßxÿÓ ³g3sf JëÊš°`ßΜYaï½ç¦L¡{÷îåÿ…Ï>£gÏžåÿ³rüÛø"3|z¨YD/tN§“1cž@QŠF)QU•ŒŒ4jÔ(9ñKûöm¸ãŽá$$À›oBf¦¶Þfƒûîƒÿü LapÁ‘™õI$uËé<À§Ž „¸¸8šôèÁG&&Gð&::š¨¦My·œâW­Z•'ß}—1cÆ”KüŠ6ùǹü²ËH›>½\â6Œ]ûö•Ûdz†ß}7ns7¢w`äää”krÇs/¿\nñõÜVޝÀž={8zô(‘,¼ý'?~¼ôAõAff&Ÿ|òŠò|±µyhsÖ)¶.Ÿ™3Çó Ã)^sÚ§6âF3O³“ý¥¦¦Êd*:¤ˆ0¤3¾¨H»wï.·Z!„¨hþLµ­ ¶ª’qÕUÚ0u7ÜPÞGXùHÞ¢ïºf-„(C’g!„(R¿>|ñüù§$ÏÂ8éÂ$»víbêÔ©´jÕªÂ.w !„ÿdÕªisSU+=—½à¯¿þ⯿þb×®]ÄÆÆê?áLZ ƒ$44”˜˜é h@rr2ŠÌ£jXRRREB¥•MJJJEF¥•’’Bv€ÇÏþ'‘s× m„ú’ªV•äÙ›ÈÈHbbb¤€Ð’@‰ªªtíÚ•˜˜˜Š>”J'!!¼¼¼Š>ŒJküøñ}•VJJJ¹ŽÂq¡›9s¦|1AÎ]}À‡j3–”$TÀUn111tíÚ)Ó'E„A ñ…BˆÀØ·æÎ…o¿…õëAUÓ€ûÎTÛåMò}ÒZ!„å&//oN~¾þEï=Úñ •Y¿g|÷¶lØPG)„$B!D¹ÉÎÎfÅŠS(ÊÛ:[æ‘”ôxa½k—–0û­6bFiMšÀ Aл7të.Wà]O$’ÜÜÜŠ>„J+99™ØØØ n¦À`‘ÙÌŒËÎÎæÄ‰2¡€A)))Ô®]›ªU«Vô¡TJÒ¹k±Dz#PåàrÁohIó_•Ý¢iS-i¾í6¸òJm]Z¨ê> x½‚ ê[—Å‚ðÌD¨Oè IMM­èC¨´˜0aB¹LEýO ³™—’’Bbb"O?ýtEJ¥4sæLú÷ïÁ$ÁöOþøãàíô˜˜Hbb"+V¬ W¯^}8ç5)" éŒ/„›»îz”mÛôû:DG‡³`Á4¿ã‡‡_GVÖoºÛ…†ö"'g GŽ”m]ÎÉqÿ¼¸új-a.X.¾X{ÌŸ"Â:unàØ±Õ~üf¢8G%|Õ¨QgNœÐÿ—zšS§ÜÌ¢Ãbqú]]¬ÖòíOîtÂìÙ%×…„@ëÖ%æ† }ÁÌ™/ú´íe—½ïÇÑ Qq$)"4NŠÍ‘"BãYDø÷ß³re]Tõ-w‰•.~àgøúëX,Eç©¢äb±8J¬ƒ³üòËg´iÓÆ¯ø÷ÞûÌ™ó_,›Î–gøå—ÏhÛ¶­_ñõœz÷ÊÏ?Äb‰ðº¢ìdΜ×4h_ñ7lØÁ™3_R2áô/±Îj}ÑÐçáßï&+k z­´Vë‹?~ÜïøÍ}5Òx`j‰5yyШ‘ÿI±¯BBàСÀÇ 6)"4Gf"Ô' tÈÑ8ù4çBIž—-[Æ7>„Õëu;UÍ¢sç‹øå½‚¥’þøãÞ|sªzg™ÇV¬(~ïË–}d0®8ÙÙ*.×Dôx‹e<.—+@{ýgŽ^²{·6%µ»åäɲëNŸÖºN”5µÌUõ>ò€ÍQQP§Ô®­-óç{ÚGIýF•BXX˜üï0Ar}’@ !|Öºu_Ž×oöjÚ´?ÿüU@÷ŸŸª&?ÿ-“ÈÈxÖÐ>,–+PU½a&“€å†âû*'Gka¬V ªT)×]ýc¸\Z—‡œœ’?}]——çÛ~rs!Öûw xïO­(púô""¼_îö×Eµ#+K¿_Yýú¡ìØñ³ßñCCcP”ºÛU©²‡ŒŒm~Çß¶í,yy?èn—–v³ß±++EÑZSSáØ1ígÁRüþÞ½¾Og¼aƒ–ÖšX­š–¹[¼=V|›³g}ÿΜÑ&ÈÈÉÑ–Ü\÷·‹ß?zԷت % ÕŠ¿.žnïÝë[|EQ£´D³xœŸïÛóËSõêZ˰»åÝw}KÒ˜æÿ(vBˆ“:Hrr®#'gz±5?òçŸú@gddàt>Ó©×J–HFÆ:¿SÏ–-{ÉÌ\xïl³ÝN^^žß ®ûøÉ@,Å“^£ñÏžµàt~/ǯ”èÿii5ÉÍ]¢»Ý¡C׊o±\ŽÓùS©µe ‘Åh|éÖçOÁ§ªj-ozËîž œJ^Îܹ®ºJK×Z7Ë‹ËÚ ª ÷l¤´ 6Ptžª*,/ß|¿¦Žö…ͦ%æ¾|²Ûá‹/Ê&È5kjɯ''ºK ½úCQ2±X¾ÐÝÎåº0êu¤ˆÐ)"Ô' tМ dÒæå“T”’L@/éž”-DªHyy•å~ÉÌôüXr²ï-¸7j£8fg?K)Ý—7#6mòþL‡êÖ…èhmÉΆßôç±àòËaÌíõ(½¼Nî_»ÏLJ‚özT«Vt¿xwO·ð<‰Gq tî —]¦u sÿÓ×uaaÚß2<\{íõØlp×]úÛù&pçî—_>É‘#Gt· 8 û«hRDhŽê“:hähœ|š3ÕíÚŒ -áÊÍ-ùÓÛm_[Zsr´$Æ]bŒ~˜Š¨d²)î áªUƒîÝ‹’ãèè’Ért´ÖâXÜœ9°r¥o_êÖ…ôÿhóó='Ûÿ÷”¤Ãb#àšk´ä1,Lû2¢w»[7í‹KIe_;«¦L>}üûÝþõ/m&<_Žÿ©§ü_ÑTõ4Vké±’/J®S B‚=Œ\E“"Bs$yÖ' tZ¼X+r¹|_vîô=þ´iÚèKÑbµº¿íë}_/#+ Ü{¯÷K–îøÐ 혥èõÑ»}ê”oñ].mf-»]Û‡ÑÅf+yß×~˜99л·¿›Ëå{Ò˜•5jø¶­.¬Àl¼‹Ö ®Ýöuô³èh¸çíïçë²y3¼ù¦o nÓ¦0ož¹ß­<8Zâ^:yxë-ßhÐÞ{þæ[2L»9Ë—Ê)> ªT¹>G#„Ð# túë/m)/GŽhKEPUøþûò_Þý(}-Z*.,Ñï*}Þ²Z¡k×¢6o‹·mªV-ºŒ¿p!Ü|³o]2.¾Þxÿc^¸PK2}í&bÌÏ> v«µ\B~—KèB»ÝØ Ê¥ÇBœß$š•@I …>S”Ølwû°¥Íü~jÓ¦ éé,LÆìBqᲨjùÖœ ˆçÌ™3|ñEÉY ÂÃñÛå;Œžäädbcc %ÌB ‘ÌÈÎÎæÄ‰r9Ó ””j×®í÷Œ¡B#ç®qRDhÎ!C •nD^HF$§OŸ&22²Ä"ɳoÈ;ÿ¦X«4ÆûZu*ÊHIIaæÌ™}•ÖÌ™3IñuØ Q†œ»Æ cR}tÿLZ ƒ >>‚!„Bœÿ$oÑ'-ÐB!„BøAh!„B!ü täú2{†p+99Å—ù›…[III}•Vvv¶ôá5!%%…ìììŠ>ŒJKÎ]ãE!99¹¢£Ò’œEŸ$ÐA"ò“"Bs¤É8)"4GŠÍ‘s×8)"4Gr}RDÒ_!„•…ä-ú¤Z!„B?H-„B!„$éoœš#…HÆI¡9RDhŽœ»ÆI¡9’³è“:H¤C¾qRDhŽ"'E„æH¡9rî'E„æH΢OŠƒ >>—ËÅgŸ}†Ýn—)¼…BqÞq:8Nî»ï>l6›z!-ÐA²~ýzÆŽËâÅ‹+úP„B!ÊX¼x1cÇŽeýúõ}(ç=iF!„•…ä-ú¤:H¤C¾qRDhŽ"'E„æH¡9rî'E„æH΢Oè ‘ùÆI¡9Rˆdœš#E„æÈ¹kœš#9‹>éÂr)D!„•…ä-ú¤Z!„B?H-„B!„$éoœš#…HÆI¡9RDhŽœ»ÆI¡9’³è“:H¤C¾qRDhŽ"'E„æH¡9rî'E„æH΢OŠƒ@:ã !„¢²¼EŸ´@ !„BáI …B!„ðƒ$ÐA"ò“"Bs¤É8)"4GŠÍ‘s×8)"4Gr}’@‰tÈ7NŠÍ‘B$㤈Ð)"4GÎ]㤈ÐÉYôIaHg|!„BT’·è“h!„B!ü ´B!„~:H¤C¾qRDhŽ"'E„æH¡9rî'E„æH΢Oè ‘ùÆI¡9Rˆdœš#E„æÈ¹kœš#9‹>)" éŒ/„BˆÊBò}Ò-„B!„$B!„Â’@‰tÈ7NŠÍ‘B$㤈Ð)"4GÎ]㤈ÐÉYôI$Ò!ß8)"4G ‘Œ“"Bs¤ˆÐ9w“"Bs$gÑ'E„A ñ…BQYHÞ¢OZ ƒdõêÕÄÇÇ“˜˜Xч"„BQFbb"ñññ¬^½º¢å¼'-ÐA ßä„BQYHÞ¢OZ ƒD:ä'E„æH!’qRDhŽš#ç®qRDhŽä,ú$éoœš#…HÆI¡9RDhŽœ»ÆI¡9’³è“.A —B„BQYHÞ¢OZ …B!„ðƒ$ÐB!„BøAè ‘ùÆI¡9Rˆdœš#E„æÈ¹kœš#9‹>I ƒD:ä'E„æH!’qRDhŽš#ç®qRDhŽä,ú¤ˆ0¤3¾B!* É[ôI ´B!„~Z!„B?H$Ò!ß8)"4G ‘Œ“"Bs¤ˆÐ9w“"Bs$gÑ' tH‡|㤈Ð)D2NŠÍ‘"BsäÜ5NŠÍ‘œEŸtÆB!De!y‹>iB!„Â’@ !„BáI ƒD:ä'E„æH!’qRDhŽš#ç®qRDhŽä,ú$éoœš#…HÆI¡9RDhŽœ»ÆI¡9’³è“"Â ÎøB!„¨,$oÑ'-ÐB!„BøAh!„B!ü tH‡|㤈Ð)D2NŠÍ‘"BsäÜ5NŠÍ‘œEŸ$ÐA"ò“"Bs¤É8)"4GŠÍ‘s×8)"4Gr}RDÒ_!„•…ä-ú¤Z!„B?H-„B!„$éoœš#…HÆI¡9RDhŽœ»ÆI¡9’³è“:H¤C¾qRDhŽ"'E„æH¡9rî'E„æH΢OŠƒ@:ã !„¢UâIDAT²¼EŸ´@ !„BáI …BñÿíÝ{\Tuþ?ð †#ƒ(ˆ wAQÅT4 /Y˜mš+›ØE\[-ÓrS^Ú]ÝLW-+«Õ­H1/¡à 4!PAå¢2r4˼¿ø›ósd@„àý|<|<:ŸóžÏ¼Ïéæ=gÎçscZàºð ù-ǃ[‡"µ"lDØ:|î¶"l®Y èvÂ7ä·"lˆÔr<ˆ°uxaëð¹Ûr<ˆ°u¸fy<DØøf|ÆcŒu\·<_Ö¾}û0wî\óUÆcŒ±.Š h-ÔÔÔ`Ê”)¸zõ*ß×ÇcŒ1ÖEq­…Y³faêÔ©‰DZ¿–oÈo9DØ:<©åxaëð ÂÖás·åxaëpÍòx\@·¾!¿åxaëð@¤–ãA„­Ãƒ[‡ÏÝ–ãA„­Ã5ËãuÓum…ˆ —Ëqÿþ}¸»»7———‡ôôt¸¸¸ÀÅÅEh///Gtt4ÀÙÙ-Î…?@cŒ1Ö‘píÒ¼Nwº¼¼ÁÁÁ°°°€££#<<<4Æ)•JDDDÀÞÞpssChh(jjj„õ … …‚‚dŒ1Æc‚NW@+•J¸ººâŸÿü'æÏŸßdÜŽ;ðõ×_ãĉÈËËÃåË—qúôi¬\¹ ‹±hÑ",Z´ãÇo¯ô[äÊ•+mz¯\Kûÿý÷ß‘žžÞfý?-‡B}}½ÞõäÈá ][ôÿ´ìß¿_/ûÒ×µuþÍ)..FRR’Þõ¯P(pêÔ©6ëÿi9sæ JJJô®ÿ¤¤$·YÿO Ÿ»-W__C‡éeÿO²_Ú:ÿ® ÓÐb±;vìÀ_þòH¥Ò&ãvìØáÇ#88àéé‰iÓ¦á믿F]]Æ×¬Zµ R©÷ïßGHHž{î¹'Ϋ©>Ÿ†Î^@———·é ÂÎ^@———kýÞÚèÌÂ555P(-zÿ'ÑÙ h…BñDÇpKuöšÏÝ–Ç)•Ê6ݽ€nËš¥³èÔRY¿~=–-[†G7±¦¦=zôÀ›o¾‰­[· í›6mÂâÅ‹‘••Õì}ÓÚ3f 1tèPtïÞ]mµµ5Œ…å¼¼<­Û¾Oéiô÷h›j0Á€´z­R©ÁÁÁ¡Ù¸ºº:tïÞ Ð÷óÏ?càÀ°µµ}êÛfllŒÄÄDøùù¡°°°Möß… àçç###­^[XXˆ!C†³¾4———‡áÇÃÈÈHcܾ}û0tèÐ6Ù6cccœ9sO­¿GÛRRR„1Ú¼V.—#((¨UqÈÊÊ‚››[›lÛ½{÷P\\ ™LÖ&ûO$áÎ;ÉdZ½6;;•••2dH³q™™™¨¯¯‡———Ƹ¸¸8xxx@©T¶ÉþËÎÎÆ€`nnÞ&û¯´´ŽŽŽ077×êµgÏž…££#úöíÛlÜ™3gàææ+++qûöíÃôéÓõîÜzÚçà£mGÅèÑ£[g``€˜˜L™2¥M¶M"‘àâÅ‹>|x›ì?¹\Ž‚‚µñWOsÿ544àĉ R«#JJJ`nnŽÔÔTøùùáĉ`šuÉúúõëprrÂÖ­[ñæ›o íÇŽøqãpüøqŒ3æ©å±sçN#ùU#cŒ1Ƙ®h08kÖ,„‡‡ë ›Ž¡ÓÎÂÑœŠŠ @Ÿ>}ÔÚUW T럖ððp>cŒ1Æ:‰Nwô“PÊN²ž••¥¶ž1ÆcŒ±GuɺOŸ>‰DÈÈÈPk¿|ù2 ÿþºH‹1ÆcŒu]²€644Ä”)Sššªvtrr2|}}¹€fŒ1ÆcMê”ô¶mÛ°~ýzÄÇÇx0˜pýúõHKKbÞ}÷]\¿~óæÍÉ'ðÞ{ï!..ï¿ÿ~»å™——‡ÀÀ@ØÙÙ!44´M§Ëêlêêê0vìXXZZÂÛÛ[×ét8_|ñ<<< •J1uêT$&&ê:¥eáÂ…prr‚ƒƒ^~ùeäääè:¥)44ºN£CIJJ‚‰‰ ¤R)¤R©VÓ©²~øáxzzÂÖÖ–÷Ÿ***„ãN*•B"‘téÏßNY@ÇÆÆbÿþý(++ðaðÿ~ìß¿¹¹¹B̰aÃpäÈ\»v Ó§OG||<¾ÿþ{LŸ>½Ýò\¾|9&Mš„œœôíÛ7nl·÷î茌Œ°aÃáqëL;R©gΜ\.Ç+¯¼‚wÞyG×)u(K–,Áï¿ÿŽk×®Á××Wx{r{ö쥥¥®Óèüýý!—Ë!—Ëqüøq]§Ó¡$$$`åÊ•øüóÏQXX¨q†,¦Y¯^½„ãN.—cÊ”)xå•Wt–ÎtêiìôƒƒRSSaaa¤¤$¬X±ÇŽÓuZJRR"""Ô~]`Ú©©©™™***УG]§ÓáDGGcçÎ8|ø°®Sé0þøã„††bÏž=ðññá_ß´””„7ÞxK–,ÁèÑ£agg§ë”:”iÓ¦ÁÏÏ“'O† ‰k¡»wïÂÎÎYYY]vâ….9>(,,D]]ðó¥§§'RRR T*ahØ)`zê“O>Á‹/¾Èų–Ö¯_èèhTTTàèÑ£ºN§CY¸p!Ö¬YÃÇ\ ˜ššbèСÈÌÌÄÆ!“ɰ{÷n]§Õa\¹rr¹ (..ƨQ£ðïÿ[×iu8ßÿ=‚‚‚ºlñ pÝbr¹ÙÙÙ°°°€ŸŸŸÆ˜{÷îáôéÓ¸qãüüüàãã#¬322BCCƒ°¬*œ ÚƒR©Dÿþýñ·¿ý­ÓOPQQ . ;;}ûöEhh¨Æ¸7nààÁƒ(((€¦OŸÞè Êðå—_¶ë˜1½DL+±±±$‘H Q£FiŒËËË#www‹Å4dÈ222¢·ß~›”J¥ãèèH%%%DDtæÌ?~|{l‚N-X°€¡¡! uëÖiŒÛ¶mÑĉ)44”ºuëF6lh—˜˜Hƒ jë´õ†¯¯¯°ïPVV–ƸåË—“¡¡!ùøøD"!©TJÙÙÙj1QQQ4hÐ º}ûv{¤®sééédcc#ì;{{{q4zôhzæ™gÈ××—ŒiúôéTSS£1þöíÛdjjJ m˜½îmذAØw4þ|q111Ô£G ¢—_~™LMMiÁ‚Âúýë_äææFnnnäììL†††äææF÷ïßo¯Mщàà`µ¿}ÑÑÑãþú׿’‰‰ ½øâ‹L"‘ˆ¢¢¢šìwäÈ‘ÛVië…ÊÊJrttŽ?T]]Ý(®¶¶–fΜIÝ»w'___êÙ³'ùùù Ÿ³DDK—.¥Ï?ÿ\X3f 8p ]¶CW¢££ÉÀÀ@8þšª[Ο?O‰„ D¯¾ú*õîÝ›&NœØh_gddPß¾}©®®®²×_\@kéÂ… ôé§ŸR||<5ªÉqòäÉäááAåååDDtäÈ200 Ÿ~úIˆ™={6-[¶ŒÊÊÊhÖ¬Y´råÊvØÝ:yò$%$$Peee“tII ÓG}$´mܸ‘ éÆBÛùóçiçÎäââB‰‰‰”ŸŸß.Û Kk×®¥½{÷ÒçŸÞd}ìØ1@?þø#UUUÑСC)00Pˆùé§ŸÈÁÁÒÒÒ¨¨¨ˆŠŠŠ¨¾¾¾Ý¶CrsséÿøÅÅÅѬY³š, /^L–––týúu"zðaaffFŸ|ò‰súôiª¯¯§²²2úøãéÕW_mMЩäädŠ‹‹£;wî½½½Æº¾¾žìììhöìÙBÛ/¿üB(>>¾Q|QQ™››·iÞúâÀ”œœLiiiMÐçÎ#´gÏ¡í7Þ KKKá \||<¥¤¤\.§Ï>ûŒììì:ý—·ªª*Z½z5:tˆ>øàƒ& è-[¶±±1¥¤¤уãËÖÖ–ÂÃÃ…˜Ã‡ÓرcI¡PPFFõëׯÉ/ÇÅÕ«WéàÁƒTTTD!!!MÖ- Æ/^¤nݺÑÎ;Õâ/^LK–,ië´õЭÐÔ˜ŸŸO†††´f͵vµ«Ì7oÞ¤I“&‘³³3ýéO¢ÊÊʶNY¯4U@oÞ¼™PzzºÐ–——GhÕªUBÛüùó),,Lø×ÜUšÎ&&&¦Éúå—_&+++µ¶-[¶ÊÌÌ$"¢E‹ѰaÃÔþÉåòvÉ]ÌŸ?_c]]]MôÚk¯©µOš4‰¤R©°xð P»¢§T*©gÏž¿d( úóŸÿܦ¹ê›¬¬¬& è7ß|“ ÕŠÃG¿ÇÄÄÐäÉ“)((ˆV­ZEW®\i·ÜõÁ¦M›š, ½¼¼( @­íwÞ!SSSáóU©TÒ‡~HÏ>û,MŸ>Nž<Ùië¦ê–«W¯µ DDÏ>û,1B­íwÞ¡œœœ¶L³Cà{ ÛÀ¥K— T*!“ÉÔÚˆÓ§O ËýúõáC‡Ú;=½—••‘H777¡ÍÎνzõ·Û·o×Ezz/55TkS‹©©©ðððÀ¦M›t‘šÞ“ËåP(öŸ§§'> …B DEEé(Cý–™™ àÁþR100€‡‡®\¹Ò(ÞÜÜ_~ùe»å§ï²²²àää¡Mu,ªþöM˜0&LÐI~ú¬¾¾éééWk8p îÝ»‡ììl <XµjV­Z¥£Lõ“êü|øÜì¿#GލµmÞ¼¹ÝòÒg<ÝC(,, ù@,))Amm­.Òê0 áîîŽnÝÔ¿ßÉd2aß²¦j,UëXÓnݺMî?Õz¦Yaa!žyæ™Fƒãd2ï»'PXXØès£OŸ>èÓ§Ÿ»QTT¥R©ñsà¿}£Ú?š.ü•••¡ººZié5. Û@UU4zÂVïÞ½AD| >FCCD"Q£öîÝ»«Í\ÂS*•¨©©AïÞ½ÕÚÍÍÍa`` ›L3Õ¹ùèþSË|î6¯¡¡FFFfG‰DP*•:ʪãhhhÐ8ãH$â¿}ÑÜçîÃë™fªãëÑÏ^Õ2qÝúõë~²ÌÈÈ@=`nn®‹´: ddd4úÀÍÈÈ€Ž²ê ammŒŒ µö«W¯‚ˆ„c“iÖ§Oh´ÿTç²j=ÓÌÆÆ•••ÈÏÏWkÏÈÈ€µµµŽ²ê8TûVVV†‚‚þÛ÷Í}î>¼ži¦:¾=þ._¾ 333ôìÙSié5. Û€½½=Í¢jkš³³3ª««qíÚ5¡íÖ­[¸sçœu˜YÇ`ooË—/«µ©ŽE>þšgkk ÷ŸH$âú1Ts«î…VÉÌÌ„ƒƒƒ.RêPœ‘““ƒºº:¡-==]XÇšÖ«W/XXXhüÜøoß㨎/Mû=͸€n¾¾¾ppp@bb¢ÐVQQË—/#,,L‡™u ³fÍ‚‘‘bbb„6Õ“Þ^{í5]¥Õa„……!;;üñ‡Ð–˜˜+++ë03ýgmm1cÆàìÙ³ "~ºLIIÁŒ3Ý—ÏÔ½ð H$jOfLNN†B¡Àܹsu˜YÇ0{ölÔÕÕáäÉ“B[LL zöì‰iÓ¦é0³Ž!,, .\Àýû÷…¶_ýÆ ãú1¼¼¼0hÐ ÄÅÅ myyyÈÈÈÀìÙ³u˜™Óñ, Nii)EFFRdd$ÙÛÛ“­­­°|ëÖ-!n×®]d``@‹/¦ï¾ûŽÈÖÖ–nÞ¼©Ãìu/::šBBB($$„««+…„„¨ÍKôàA "‘ˆ,X@ .¤gžy†Þzë-e­?¶oßN‘‘‘4uêT@³gϦÈÈHaŠ+"¢;wî““ 2„¾ùæúàƒÈÐÐ>ýôSf®TçêÀÉÌÌLX¾té’O&&&4kÖ,Ú½{7M›6ÌÍÍÕbº¢sçΠ箉‰ ÙÙ٠˵µµBÜ®]»ÈÈȈ^{í5Zºt)Y[[Ó /¼Ðéç*~œíÛ·SHHòññ¡Z¾|¹ZÜŒ3¨wïÞôî»ïÒܹsÉÐÐ6oÞ¬£¬õÇÚµk)22’‚‚‚½ýöÛI§Nb²²²¨wïÞ4aÂÚ½{7…‡‡S·nÝèðáÃ:Ì\÷Š‹‹…sU"‘X,–~~Bll,õèу&MšDË—/'GGG|ãÇ×UÚzA$5yî>ü˜óqãÆ!!!ÿýï‘‘‘ððp¼õÖ[èÕ«W»æÛQý¿ß)cŒ1ÆcÕµ/ 0ÆcŒ1¦%. cŒ1ÆÓÐŒ1ÆcŒi hÆcŒ1Æ´À4cŒ1ÆcZàš1ÆcŒ1-pÍcí,-- ÙÙÙºN£YÅÅňGII‰ÆõÉÉÉËåÍöqçΜ:uJmŽ|Æë ¸€fŒu þþþð÷÷GVV–Zû7àï襤¤vËeÑ¢Eظqc»½Ÿ6ÊËË1|øpH¥RÌ™3.\Ð7{ölìÚµ«Ù¾’““Œ»wï }ïÚµKí1óŒ1Öñ“c]Âo¿ýX±böîÝ+´WWWã·ß~ƒB¡ÐUjzåðáÃÈÍÍÅíÛ·[ý2777¬[·=zôaîܹHLL„••ÕÓH—1Æt‚¯@3ƺŒçž{ûöíCJJJ³qåå娭­Uk»ÿ¾Ú#Ókkk…¢»¾¾—.]R{$8\¿~¿ÿþ{³ïURR‚ôôt(•Ê&c ‘––Ö¨P(¨«« 33³Ù÷#"äææââÅ‹¶±¢¢±±±ÉdP*•(//o¶/•Û·oãâÅ‹ò³³³CDDD"”J%*++wïÞ…B¡hô¥¥ªª ÉÉÉÈÉÉiv0Ƙ®qÍë2&OžŒ€€,[¶¬Ù8kkkìÙ³G­mÛ¶mpuu–÷ìÙ±XŒ@,ÃÇlj?þø#òóó!“ÉàââÌœ9³Ñ{ÔÖÖbÆŒèß¿?¼¼¼`ooäädµ˜sçÎÁÓÓýû÷‡ŸŸÄb16oÞ¬#‹±uëVx{{cÀ€˜7o^“Û•––www8::bèС‹ÅøòË/…õ®®®øæ›opòäIˆÅbØÛÛ7»Ÿjjj0mÚ4ØØØÀÛÛÎÎÎHOOÖ;v b±åååÈË˃ŸŸ`ܸq‹Å‹Å€²²2C,còäÉÉdH$;7cŒéÐŒ±.eݺu8~ü8Ž?þTúûä“OpèÐ!cÊ”)ˆˆˆÀK/½„¥K—B¡Pà‹/¾À?ü€ÜÜ\µ×}÷Ýw077ǵk×––'''L™2ÕÕÕ\™7n¼¼¼žžŽÒÒR¬_¿‘‘‘8qâ„Z_~ø!fΜ‰[·n!&&FcžÕÕÕxá… ‘Hð믿âÆxýõ׎ӧOx0ppΜ9 =ö¶–M›6ÁÛÛùùù8wîLMMñÕW_iŒ•J¥Âý牉‰ "`ûöí¸yó&òòòpûömTWW7úÃcú„ hÆX—2räHLœ8ñ±W¡ŸÔâÅ‹1jÔ(XYY!""eeeÉdxýõ×aff†ððpH$ÄÅÅ©½ÎÈÈ[¶l­­-¼¼¼°víZ#::°uëVÔÔÔ`óæÍðôô„™™.\___|ñÅj}ùûûãý÷߇••,,,4æùÓO?¡  6lÀˆ#`ccƒ-[¶ÀÊÊ ›6mjѶ;99aåÊ•èׯ† ‚—^z GÕºŸôôtØÛÛÃÒÒЭ[7L˜0¡E91ÆX{àA„Œ±.gíÚµk´îùçŸÇŸŸºº:DEE!!!᩾ÿ½{÷°zõjTUUáÖ­[X·nÌÍÍ1mÚ4nk‰DX²d rrr„×]»v gÏžÕúýf̘KKK¬Zµ 7nÜ@uu56lØ€‚‚DDD<µíjŽqäȵ"úàÁƒ¸wïž°¬š>O5 ’1Æô ÐŒ±.ë£>ÒØþÊ+¯ ¤¤èÛ·/þþ÷¿cÑ¢EOõ½gΜ‰ØØXH¥R8::âèÑ£øî»ï„A€öööØ»w/Nœ8WWW¸»»ÃÆÆnnnÂCa´annŽÿýï8{ö,\\\`gg‡>ø+V¬ÀäÉ“Ÿê¶5géҥػw/LMM…ÛX>þøcXYYÁËË ®®® Åœ9sÚ5/ÆÓ†©æbŒ±NìÊ•+°¶¶æVÉËËCUUìììÔîé-))Abb",--áëë‹»w´ÎÎÎ\%-**‚»»»ð¥R‰ìììF}åååÁØØX¸Cµ,‘H––†¢¢" >\˜…âaUUU¸|ù2är9z÷î oooµ§ø]¹r¶¶¶033{¢ýP^^Ž . ¢¢>>>0`€Úú¢¢"444 ÿþÍö“›› 333µùšïÞ½‹‚‚¸¹¹ÁÀÀ÷îÝC~~>\]]Ý{]XXˆÊÊJ¸»»C©T"-- ¹¹¹°´´ÄðáÃallüDÛÃcºÀ4cŒ1ÆcZà[8cŒ1ÆÓÐŒ1ÆcŒi hÆcŒ1Æ´À4cŒ1ÆcZàš1ÆcŒ1-pÍcŒ1Ƙ¸€fŒ1ÆcL \@3ÆcŒ1¦. cŒ1ÆÓÐŒ1ÆcŒi hÆcŒ1Æ´À4cŒ1ÆcZø?[ÃNªÀö9²IEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/Q7-10m-noidx.svg000066400000000000000000003777151231437614300237460ustar00rootroot00000000000000 PyTables-v.3.1.1/doc/source/usersguide/images/Q8-1g-idx-SSD.png000066400000000000000000002624311231437614300237240ustar00rootroot00000000000000‰PNG  IHDRÐß}™SsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìwX××Ç¿»°,e©"] Š °D"v‰Ñ$$v±%±$–hŒŠ%+ØK{‹±!XPPŒŠ ^÷¼øÌüfwYDcÌ{?ÏÃã³gν÷Ü;3×3wΜ+!"ƒÁ`0 ƒÁÐ é»6€Á`0 ƒÁxŸ`4ƒÁ`0 ƒQ ˜Í`0 ƒÁ`Tæ@3 ƒÁ`0Õ€9Ð ƒÁ`0 F5`4ƒÁ`0 ƒQ ˜Í`0 ƒÁ`Tæ@3 ƒÁ`0Õ€9Ð ƒÁ`0 F5`4Cknܸ͛7ãûï¿ÇÚµkqéÒ%”——¿k³þ1öíÛ'''ÄÄļkSÔrôèQŒ1 6Dƒ Þµ9ï£Fbcö¥I“&øâ‹/´Ö/++õk×°gÏ8pàµÛU*•¸té6oÞŒiӦ᧟~ÂöíÛqïÞ½×®ó]‰Æ£  €—½Íû¦gÏžèСÃ[©ûM³nÝ:899áÆ¼lþüùðóóûõÿäÿ'tßµŒ?™™™Çï¿ÿ055EVVÀÏÏ›6mB½zõÞ¥‰oŒíÛ·cúô騾};¼¼¼Çòóóqÿþ}¾#ë4óÇ wïÞhÛ¶-CCÃwmÒ{ųgÏpÿþýwmã-žžggg­tÛµk‡‹/ò÷¹‰‰ ²³³«ÝæÝ»wñé§ŸâÌ™3”––¢¬¬ R©=zôÀ/¿ü¢µ]ïš²²2üðà Ì-oó¾yüøñký» ''÷ïßGII /ëÕ«¦L™‚íÛ·càÀïÐ:ÆÛ€­@34’““üñǘòóó‘ŸŸ«W¯bÖ¬Y8vìâããß°õo-[¶àÞ½{3fŒ@¾páB\»víYõïÆÅÅݺuÜ9s@DïÚÆ†9Ð Ìš5 éééX°`æÎ ;;;€¹¹9F…]»v!''“&M”ËÍÍEnn®¨¾òòrdgg£¸¸Xe{yyyˆGRRJKKÕ–çžò qþüy¤§§#''yyy*ë%"dgg ^=V¦°°wœóòòììlôõõamm ¹\Η)))Avv6ÿŠ.33.\Ù®T*‘€´´4µísv¦¤¤àìÙ³ÈÌÌÔ¨ËQVV†ììl¤¥¥¡nݺüU~(//ÇÍ›7üü|•uåçç#''‡ÿ}ÿþ}œ:uJëÉÿùóç8{ö,._¾¬ö\”——ãÖ­[ˆ‹‹S«“ŸŸ—/_ò¿SRRpóæM•z±±±*ÇJ©T ®µ‚‚œ?Zõ¥2<À™3gðøñc•vdggC©TŠŽ ;;eeeZµS\\Œ .àÁƒþwíV<ŸÜ9WuÆ®2ééé8sæŒÚq¨<ö<@LL ŠŠŠÔ¶ €?®j Tõ111'OžÄ;wÔŽMÅ{šˆpýúu\¹rEíü¼›„„„×~¨OHH@TTFŽ ccãj—W*•GQQNž<‰qãÆÁÁÁ‰zzzhÒ¤ ¦L™‚[·nÁÛÛ›/Wyn,**B\\œ(Ü#++ çÎCZZšÊûRÕ½ÏÍSÜ\ÆQ\\¬Õ9S*•˜3gBBBDáµk×F:u²ÊóHJJ .\¸ vÞ^ß›7o"11Q«kH©TâÎ;8wîÿF´"999jW¯¹ù½òø•––"))IãÜÄñôéSœ?^cŸ`ôèÑHJJ®]»ªè㽃 5<þœôôôÈÎÎŽŠ‹‹Õêùûûºzõ*/óòò"///‘nBB E‹ ä™™™Ô¿’H$€±±1EFF ôbcc -_¾œÂÃÃI&“š4iuëÖŒŒŒ(''GÔîÁƒ EDD¨íGxx8ßvÅ¿I“&ÑÖ­[ :tˆ/³bÅ @ÇŽ£.]ºðö›˜˜PTT-]º”LMMùúºvíJ/_¾µ¿yóf233´Ý³gOzþü¹Z›‰ˆ:¤Òî1cÆð:kÖ¬!…BÁ“J¥ôÅ_P~~¾ ®àà`ªS§]¹r…yý¼¼<6\¾|™|||íëëëÓÒ¥Kz6l ccc^G"‘Ð!C(77W J&&&”””DÎÎμ¾““%''SII 8ôôôøþÌ™3GPÇÍ›7 Í™3‡FÍ_+Ü9ÈÎÎè÷ë×är¹¨oýõÙÛÛ úæççGiii¼ÎŽ;=ZP6==ÌĮ̀qãÆTPP q ‰ˆÖ­[Gúúú|;ü=Sñ|rç|ëÖ­¢:ÔõãÀdcc#èG@@¥§§ ô*޽‹‹ ¯G‰DÔGŽÎ;“™™jìã¤I“×"²´´¤7Št) €Ž9Bæææ¼¾ ?^e+ÞkvvvtéÒ%211¡ÐÐPv©ÂËË‹LLLªUfç΀>þøãj•ãÎóÂ… iôèÑüµÍ÷GQçÎãfkkKýõ_‡R©$+++êÚµ« îqãÆ8p @þé§Ÿ’\.¯òÚ>>dmmÍÛ}ïÞ="zå”    :}ú4%&&Ò„ õèÑCPWpp0Rݺué§Ÿ~¢øøx:zô¨Æ¨ÄÄD’ÉdT·n]Ú¸q#%''SBBýú믂s±eË@þþþCׯ_§©S§êܹ³ ÎÐÐP’Ëåäàà@Æ £˜˜š>}:) jÙ²% 8üüühË–-´cÇjÒ¤‰è?/ζ¶¶¦æÍ›Ó¹sç(%%…æÎKR©”BBBmªrŽ9B‰„|||(""‚èûï¿'+++ª_¿¾`\¾üòK@{öì!¢W×µŸŸRRR’ÆsHD´wï^@mÚ´¡¸¸8º{÷.ýðÃT·nÝ;Ð{öì!äëëKË–-£„„š:u*™››“‡‡•––ŠÆÞÖÖ–fÍšEqqqtüøqÊÏϧ:©©©ÈINII!‰DBcÇŽ­²Ÿ£G¦_ý•.^¼HÉÉÉôÛo¿Q‹-H"‘ÐÅ‹ºŽŽŽdeeEÖÖÖ4vìX:{ö,}÷ÝwdllL®®®TVVÆë^¾|™¤R)5jÔˆNž|˜ÜÜÜH"‘Pll,_O¿~ýH¡PÎi“&MH&“‘µµµ Mjß¾}•¶qóSLLŒè˜:ÚÐÐìììhÈ!tòäIZ°`ÙØØP­Zµ‹OŸ>¥ZµjQ:uhÏž=”žžN;vì +++2339Ћ/æç³õë×S||<1‚ ©S§N¼^yy9’¾¾>¿¸“™™IŽŽŽdkk+°aøðá$‘H¨wïÞôÇЩS§hðàÁ¤««K£F´_q¿{÷.ÅÅÅQ»víÈÚÚZ­ݬY3rrrªrœïÌf¨eÍš5€æÎ«Qo×®]€&NœÈ˪ã@GDDš7ož@·¨¨ˆLLLÈÍÍ—q´¾¾¾hÕ¤¼¼œêÕ«G>>>ùãÇIWWW«!Î!®ø‡&Ú××W »aÃ~…¢¢óT\\LúúúäééÉË233ÉÌÌŒìííÿéMž<™ÐÁƒ«´= €²ââb²±±!CCCÑJJHH Ó§Oó²àà`@“'O®²½ŠíÊd2º}û¶Z²²2rtt$===Ñjö‡~HèèÑ£¼,44”Ð?þ(Ð8p  ü¯¿þ"ôí·ßò2Î Ze0` sçÎñ²ÊŽ@yy9yxx¡¡¡èÁ+22R´ºUXXHMš4!333zðàîÖ®]«v\*âëëK‰„žD}öÙg€âããEvW|Fô¿Õ×ÊîÓ§ Ë—/‹ŽeggSVVÿWñ^àæF™L&zCµjÕ*ÑõMDtåÊÞ™¬¬{öìY""zòä I$úꫯçèöíÛ€fÍšUeŸFMèï¿ÿSç@ Ù³g ä , ´aÃ^Æ-Zlß¾] »téR p Ÿ>}JÆÆÆäââBåååý±cÇ:qâ/{øð!YXXPÆ )//>üðCÒÑÑ¡S§Nñ:ׯ_'êÒ¥‹¨o=zô ]]]~þ¸~ý:I$êÖ­›@/33“tttÔ:Ð ‰DRå›<Æû‹f¨…‹+«*;þìÙ³×jçÀ044ÄgŸ}&ËårôêÕ ·oßÅSÀÀÀ@ “J¥GBB.\¸ÀËׯ_²²²j¥±ª.}úôünÛ¶-ÀÃü\OO-Z´ÀßÿÍËΟ?ÌÌLŒ1ººÂÄ8Ü—Û—.]z-»nß¾G! @» ÁÁÁ€S§N‰ÊuëÖM«ú‹‹‹qúôiôèÑ®®®jõÒÒÒpÿþ}´iÓFFF‚c!!!jí üæÆµ²¼uëÖÐÑÑQ™ÌÝÝöööYÇŽ@cJÂÔÔTܸqü1LMMÇzöì }}}ÁyÑ××ÇŽ;P\\ŒŽ;bÞ¼y4h†ª¶ ŽÂÂBþƒ\+++•¶¾.·nÝÂÝ»w1tèPÔªUKp¬OŸ>Éd*¯/U×@hh(lll°zõj^VZZŠuëÖ¡mÛ¶‚k]ùùù8~ü86mÚ„•+WâäÉ“°±±AJJŠHW¡P K—.Yûöí@ýáôéÓ°²²‚§§§@·¦ãW]¸yÓÄÄDt¬N:055åÿ $Òñóó͹ÜuÚ¹sgÜËË ÖÖÖ8sæ ÿFPPàøñã€'N@"‘àÛo¿…¾¾>/çþÕ&M\RR `ccS¥nE*Ï‹ªÎ[LL $ o7Gå¾À™3g““ƒQ£FA*º/ªæJDEE!99¾¾¾Ø½{7f̘!øüСC(//} }ûöEYY®^½ àÕ5FD"ÛLMMÑ´iSµãàææÆÇx3þ;0š¡¯>:ÒwœûÀ°ºÜ¹s077‡D"ü­[·N¥ •'[ŽaÆA.—ã·ß~Ö¬YƒF¡M›6¯eŸ6T®»nݺ*åܱŠ3Þ¹s0yòdQÿ›4i¯&Š«[•Ñ©S'9 …­[·ÖªþÔÔT(•Ê*óÀj²ƒ“U¶ÃØØXäqãZÙ>…B…B¡òÃUmrÿ‘§¦¦ªµùÖ­[€•+WŠÎ‹‰‰ ŠŠŠDç¥Aƒ˜1c’““amm•+Wª­¿"Üašl}]¸~,^¼XÔsss”––Šú!—ËÑ®];Q]ºººøì³ÏpêÔ)¾Þ={öàÉ“'×Êž­[·ÂÉÉ ;vÄÇŒŸ~ú ‹/Fff&ž>}*Ò÷ññ=,sŽw¾óòò™™‰ÀÀ@H$®¿¿?ttt´²íMÀ=Hªú`xÙ²eX±b.\¨¶¼ª¹íÎ;044Ty_vìØ%%%üéââGGGœ8qpìØ1x{{ÃÖÖ~~~¹±±1|}}«ìS^^ Dc« KKKÑCuåó¼º===aaa!Ðuss=øróÈØ±cE×r‹-ˆçÊ®]»bРA¸yó&Z¶l‰©S§ Žs×qHHˆ¨Î>úHP'7_T÷>å ªú0‘ñ~Áò@3ÔâææU¦(âŽ7nܘ—I$•_ˆ«J'“Éàää„õë׫mÃÑÑQð[ݪ¸……z÷îmÛ¶aáÂ…ˆGjj*"""4ö¡¦T^ ©J^™LX°`š5k¦RÇÚÚúµìâU_©s_¨W^™622ÒÚáàê¯üuÿ›°CÓÖÕù\Õ—ø\– }}}µå¸1˜0a‚ÚùÚµk ~ð}8{ö,¿Ò¯ ÎM¶VDSÿ+ßc\?¦NÊ?4UÆÌÌLdžžžJÝÏ?ÿ³gÏÆš5k°`Á¬Zµ fffèÝ»·Z›8îܹƒ!C† aÆ8xð <<å¸}û6Úe¼ÿ0š¡wwwx{{ãðáÃHLLT9aeffbíÚµ¨]»6>øà^nff†¸¸8”––ò“>|XT‡‡‡öïßfÍš‰^1¿ÇÇæÍ›±yófDGGÃÀÀƒÖª,çÄ©KÕõ6àVYKKKøFëæV€Ž=ŠüQpìèÑ£P£ —ËqåÊ­í¨Ì›°CÇŽɸU8Mmrç%77Wëó2bÄܺu ;wîÄwß}‡ÁƒãêÕ«üʹ: «««ÑÖŠpobb"ú÷ïÏË‹‹‹qòäI•ýÈÏÏ#×—>øàDEEaذa8vìÆŒ£ña„ãôéÓ())Á÷ß/Xù¼yó&nß¾ýÚ;Úéééñ¡ •çœèèèתóuéÑ£¦L™‚… âË/¿=¾®®®ˆ‰‰Att4>üðC^^ZZŠ“'OB¡P²ƒ‚‚°~ýzDEEáþýû¼#ß¡CL:k×®Eff¦Ö»ü5jÔÀ+GPÛ·SÚâì쌘˜¤¦¦ îÇK—.!;;["»–ËË˵º–KKK1`À”––âðáÃ0`úõ뇸¸8þƒ«S.—WY'gß±cÇàçç'8¦ê>å¸}û6ÌÍÍEéþï7,„ƒ¡©TŠˆˆ, £xùò% „œœLŸ>]°R¿~}äçç#..Ž—aÇŽ¢v>ù䔕•áÛo¿UiGuw¢òóóƒ——/^ŒÝ»w£OŸ>¢Vup!IIIÕj³&´jÕ nnnX¸p!žÈd2ôèÑãµm—J¥4hNœ8¡òáˆ[Qä^_»vMçZ^^޽{÷BGGGà¼I?~,ذ‚ˆpàÀÈd2m:::"00‘‘‘*ß‘ ×í† ‰ & wïÞØ¾};rss1hР*óÚêêê¢GHKKCbb"/W*•8xð HßÙÙR©Tô@²{÷nMÀ«7I­[·ÆªU«TÆ`*•J•9Û5ñÕW_áùóçèÓ§ˆŸþ¹Vå¸Õ¾‡ ä[¶l©VûªèÝ»7òóóEŽÌ¾}ûj\wu¨W¯¦NŠôôt 2Dåü¥j¥]½zõüùçŸùÑ£G‘——ÇçàÂ@~üñGèééñß4kÖ &&&üô¶4çdr+©o’¾}ûŸ§={öˆtýýýQ¯^=ÌŸ?/^¼/))¼ùöÛoqþüy,_¾ÁÁÁX¿~=1vìX^',, …Ó¦MS™_<77—ƒÑ¥K`ÿþý‚·ÉÉÉ|x‰*nß¾­õ÷Œ÷ˆwóí"ã}bÉ’%$—ËÉ‚† F4jÔ(rrr"4räHQ™Ë—/ð*òøñãiܸqdffF:teá "ÙÚÚÒüùóiÍš5Ô©S'•¸\Ä•á2¾¨Ê%[9×/—…£U«VdiiIsæÌ¡µk×R×®] M˜0AP^ÕyINN&sss200 qãÆQTT­\¹’ÆGŽŽŽ´~ýz¾-###jÑ¢•””ðåýõW•i UqõêU222"kkkš7o­Y³†:wîLmÚ´eá "úè£5kÖŒ,X@AAAdffFÞÞÞ¢~\¿~LMMÉÈȈ&L˜@6l +VÐØ±cÉÞÞ^ÍCÝØWD©Tò9¢Û¶m[eß8>|H&&&dffFcÆŒ¡ÈÈH:t(ÙÛÛSýúõE)˸<Е‰ŽŽ&üø½Ê“leeEµjÕ¢™3gRdd$õë×¼½½ÉÐÐPë,3gΤÐÐP %cccÒÕÕå1B«:Š‹‹)<<œ$ Y[[ÓG}D³gϦùóçÓÈ‘#ÉÎÎŽ¤R)M›6/£.G>G—.]ø\Α‘‘4cÆ R(djjʧ¬¬HÆ ùÔ˜ᲕTNi§‰ììl211eI!Òœº2?&à¹õ‰^•‡‡éêêÒøñãiÆ 4|øp²³³#'''Ñ5qìØ1200 KKKš6mmÞ¼™–,YB_|ñ™››ósñ¡C‡H"‘ˆ²/qE¶mÛÆËV­ZER©”êׯOsçÎ¥-[¶Ð/¿üBƒ "###ÊÊÊâu¿ÿþ{@:u¢5kÖмyóÈÆÆ†Z·n­2 Ç­[·´ÊfÅxÿ`4C+¨OŸ>T¿~}Áf'«W¯V[f×®]Ô°aC’J¥äææF3gΤ[·n‘——mÚ´I¤¿qãFòõõ%…BÁÿÇóÁÐŽ;xk×®‘——íܹS£½999|NØêr÷î]Šˆˆ nݺ‘——¿ùÊáÇÉËË‹OEôjÓ///ºvíš ŽââbòòòR™"jÒ¤IÔ¢E ‘<--úõëÇo(`ddDÞÞÞôí·ßÒ³gϪ´{ذa*S1½Ú¨ 00LLLH&“‘——— uÇðáéC‡U¶U™¬¬,úâ‹/¨^½z$‘HH.—S«V­D¯Î_‡¨víÚ$“ÉÈÓÓ“V®\)ªoìØ±*³'N——— UGÛ¶mŽ8ç@Ï;—6oÞLÍš5#]]]rqq¥L$R^ž={FŸ}ö¹¸¸T*%¹\Nîîî4nÜ8JKK#¥RIƒ¦æÍ›Sjjª¨ü_|AÍš5Ó˜æ¯âøðC‡¥G©t srrhèСdjjJÔ¾}{ºté’Ú~dddЧŸ~JÎÎÎ$•JI__Ÿ<<<è믿¤ùS7ö•áò«ÚE§OŸæÏ…¥¥%õèуîÝ»GÆ £?üP Û¥K6l˜¨Ž .——Ÿs›#==ºuëF …‚,,,¨wïÞôìÙ3ѵ¡‰áÇói8+ÿU¶¯*¢££©W¯^Ô AÒÑÑ!CCCjÒ¤ 2D”òOÓÜHô*á´iÓÈÍͤR)YYYQXX˜(E#Ç?ü@^^^´|ùr|Æ äåå%z€¬Šo¿ý–ôõõEs‘ªëMÝ<òüùsÁœÊ‘••E$333ªU«uîÜ™nݺEƒV9æwïÞ¥^½z‘ …BA>>>ôý÷ßSff&åççS§N¨cÇŽ¢ÔqÅÅÅÔ½{wj×®À1Ž‹‹£Î;“••¿™K›6mhþüù¢ô¢K—.%777ÒÑÑ!Z¿~=mÚ´‰¼¼¼èÖ­[Ý‘#G’©©©Ê ¾ï7ÌfT›¢¢":xð ééé¹Ð‹4 IDATQÆ «Ü)¯òäSJ¥RãÆÚÀmJQy'¼÷…Ê9›ß$Õ=ÕE[Ûß¶è7Õfqq1¿{ÙÛ¤¢YYY*huúÚPÓû‹ˆ¨M›6dnn^å΃êx›ç¿â+ÿ*¾™ø7Õ¥-ÏŸ?'…B!ÚñóM¢T*«}îÞÆ\ù¦æ°—/_’B¡Ðê ãýƒÅ@3ª\.G—.]°qãFܾ}]»vE~~¾Zýʹ«B"‘¨Í  ………˜TÅÛ<ÿÿdÚ:m©øaã¿©.m177ÇW_}…_ýeeeo¥ ‰DRís÷6æÊ75‡­_¿R©TeŽiÆûs ¯Mß¾}ñ÷ßcûöíïÚ¯> ñöö†……nܸŋרQ`0ÿcåÊ•pssCXX¼¼¼´ÎýÌøo0cÆ =z´Êb¯èÝ»7®_¿.JwÉøo 3cÆŒïÚÆûK­ZµP»ví…£JDHOOÇ|€¥K—¾ñtKŒ÷ "‚T*Eûöíáääô®Í©eee äs³¿+²³³a``€ñãÇcΜ9oõM ã߇L&ƒ……Å¿r…ÿ߈±±±ÚÜÒŒ÷ Q5óé0 ƒÁ`0ÿa! ƒÁ`0 F5`4ƒÁ`0 ƒQ ˜Í`0 ƒÁ`Tæ@3 ƒÁ`0Õ€9Ð ^厾rå ^¼x!’={öL«:RSS‘””ô¶LüÏ’œœŒäääj—ËÈÈÀ•+WPRRò¬b0´£°°W¯^Åýû÷ßµ)ï”§OŸŠîÇÇãÊ•+ÕÎ]PP šŒÿüN F yðà~ùå\¹r077‡³³333³j×™œœ ¬X±_~ù¥@¶hÑ"Œ;¶Ê:†ÎÛ¤ {÷îÅÆ‘ššŠââbÔ­[>>>F‡x½G!//¯Æ)Ìž>}ŠÌÌL¸¹¹A*}óÏÎQQQˆŽŽæ›˜˜ÀÉÉ ŽŽŽhÚ´©ÚTrýúõ\¹r¥Zí­\¹3gÎDZZÚ{Ÿ¦î]‘’’‚ü‘ÿ-‘H`aa'''ôïßæææjËfdd`òäÉH$ˆˆˆ€±±±Hçùóç˜0aZ¶l‰¯¾úŠ—O›6 ééé‚6áää„V­ZÁÂÂâµú³k×.ìܹIIIððð@XXúôéóZuUÅ… 0{öl=zèß¿?¶nݪV¿¤¤kÖ¬Á¡C‡––===ØÚÚ¢eË– …§§§Êr<À÷ß/縸8¬X±B+[6lˆÉ“'ó¿W®\‰óçÏ#((üq5z­žuëÖaÊ”)‚û1""óæÍÃãÇamm­u]×®]CëÖ­ó1ƒñoƒ9ÐŒ÷ŠØØX¡¨¨õë×G›6m““ƒsçÎaÓ¦M°°°À‡~øFÚÒÓÓƒ£££JÇ ¦ :ëׯ‡\.G«V­àããƒÔÔT,_¾;vìÀ½{÷xÝñãÇc÷îÝ(**ªQ›sæÌADD²²²ÞJbÿØØXDEE¡^½zÐÓÓCqq1>|ˆÒÒRèèèà“O>Á´iÓàèè((gccóÆmahdzgÏSSSXYYˆ’’‚òòrL™2;vì@HHˆÊ²6l@TTÀÏÏŸþ¹H'//QQQ(**8Ð{÷îŵk×ø‡Âüü|¬Uªbîܹoì~ä®ËšÌÇ ÆÛ†­@3Þž?Ž;wî U«Vèܹ³è¸ºïß¿£G"99öööh×®¼½½«l/;;‡FÓ¦MEá7nÜÀ¡C‡››‹6mÚ ((Hë~œ;wðõ×_C___p¬V­Z ã}:j×® "µk×päÈ<{ö 666ðóóC‹- ‘H;wî„èšNOOǹsç(x%^VV†sçÎáèÑ£(//‡££#Úµkwwwc¥ SSSŒ5 HMMEjjªèú?wî’““1oÞ¤¤¤ yóæjï³k×®áÆøðÃs]aa!Nž<‰“'OB.—ÃÙÙAAApppÐØ®R©ÄÞ½{Q\\ŒîÝ»ÃÈÈðâÅ ;v ñññ011»»;‚‚‚4† 15æ]{ð †¶äææ’D"¡:uêPII‰Ve6mÚD …‚tuuÉÝÝ I*•Òwß}'ÐSµÍÉ-Z$Ð]·nÉd2ÒÓÓ#www’ËåôñÇS§N´Zž?> ©S§V©Û¶m[Ò××'T§NþïСCDDôóÏ?266¦Æ“\.'äééIÿý7_OÏž=ÉÐÐ_UäꉊŠâuV¬XÁëØÛÛ“D"!úñÇ«´“Hõ tEJJJ¨^½z€rssy¹——yyyñ¿ËÊʨW¯^$‘HÈÐК4iÂÛ3wî\^OÕ trr2Õ«WéÆjmMKK#‰DB£G+//'òööØ4eÊÒÑÑ!äàà@H¡PЦM›å?ùä@Û¶m#²´´$cccJKK£ääd²µµ%dmmMMš4!333@|r¹\´jKD´uëVÀŸ"¢}ûö‘±±1I$rvv&www222ÒêZTµÍѶm[@ÉÉÉ¢cÜ…‡ÒÍ›7 }óÍ7"½ê¬@WäÑ£G$“ɨ~ýúUöˆ¨y󿀒’’ò[·njÞ¼¹Võ,X°€d2éë듇‡éé鑾¾>­\¹’×=z4YYY244äï¥K—.©­7,,ŒÐÞ½{µ²ƒcðàÁ$“ÉèéÓ§tíÚ5µãÌQÕ ôƒH*•Rxx8õïߟär9effjmÓ… ¨nݺ$‘HÈÍÍj×®M>>>ôÍ7߈îÇI“&‰ì‰ˆˆ }}}ÒÑÑ¡† ’««+Éår àuT­@RXXÉd2ŠŒŒäåܵ(—ËÉÓÓ“œœœHGGGå5Í`¼I˜Íx¯èÚµ+ &MšÐÏ?ÿLÉÉɤT*Uꦤ¤žž5nܘ=zDDDôá‡Ú·o¯«­’’BºººÔªU+zþü9eeeQ»víHGGG+§%))‰ôôôH.—Sß¾}iÏž=Ã*4…pœ>}šÎ;ÇÿÎÍÍ¥¨¨(@ƒ èj áàÂJzôèA/^$"¢;wîPÿþý 8q¢Ê~Uå@ 0€ÐåË—yYezß¾}€¾ûî;Áëîû÷ïÓµk×øß•è³gÏ’¹¹9ùøøðç[daa!z;|ø0 Å‹ó²%K–úòË/ù0¡øøxòóó#}}}JMMåu9ÚÑÑ‘bcc‰èÕ+ÿÒÒR4héèèœ-¥RI111”ŸŸÏ˪ã@»¸¸­­­ 4¦¸¸˜þúë¯*Ç@}ûöm’H$äàà *“——G …‚:wîÌËZ´hAÖÖÖ¢p×u ‰ˆÜÜÜHGGG«‡e[[[ÒÓÓµ_^^Ndkk[e§OŸ&Ô±cGÊÉÉ!"¢çÏŸSëÖ­I*•ÒÕ«Wyݬ¬,µªØ°a SSS>|8:uŠ 4–yùò%R=xYÓ¦MUŽ3GUô¬Y³?g•Ú;wðòåK´nÝÇŒOnn.ôõõUŽªì}ûöÅÇÑ®];¬^½?ÖÊVMôéÓûöíCXXþøãäääT»Ž„„,^¼?ÿü3Ž9‚’’üôÓO*3Ú¬[·µjÕBÏž=yYÿþý¡§§‡õë×ר/©|­Ü»w·nÝâÿîÞ½ àU<©TŠÒÒR•õ”––B*•òñåê¸~ý:j×® ___¼]»vÐÓÓÃÕ«WkÔŸaÆ!11ÇŽÃÈ‘#Q·n]¬\¹ 6TçÍÅ*ðÁ¼làÀÉd¯5ΧOŸFJJ ÌËttt0pà@\¾|×®]«²n :vì(+ ´jÕªÊòÖÖÖ BDD>ùä>|Xcv¡ . uëÖÐÓÓÃùóçáåå%8ÎÅ99ãÇGll,”Je•v0o„wíÁ35%++‹_騸’éää$Xm©Hß¾} =yò„ˆ´_vrr¢ ¨¬³E‹Z¯@«âÒ¥KÔ¨Q#‘šV 7nÜHR©”jÕªE-Z´ O>ù„ÆŒCmÚ´­©[¾sç ’Ëå*ÿ´é—6+нzõ"týúu^VyšèÕ }·nÝøúFѯ¿þ*xíÍ­@+ 222¢[·nUice† B2™Œ¿V¯^Mh÷îݼÎÞ½{ «ê¯]»v¼>·]VV&j¯¨¨ˆ–/_Nnnnü˜‡††ÒñãÇzÕY~öì}÷ÝwdaaAÈÈȈÂÃÃEñÀª¨ÂQ^^N/^¤víÚÚ±c‡@?99™¯¯/mݺUðçááÁÇërÔdÚÉɉär9ÆãååůޢÒÛÒÑÑ¡¢¢"A%%%¤§§§2¥"J¥’¤R© ,¥"-Z´ þ÷ë¬@«jóÀ¤P(Aô¿·d~~~Z3‡¦h7ož ¾iÓ¦iÝ—!C†•1Ó'NÔ*úÞ½{4räH222"dnnN'N¤û÷ïó:Üuɦ“øøxêÓ§BâììL , ììì*ûÃ`Ôæ@3þ“……I¥RÞÉjÑ¢Éd2•¯ôÝÝÝI&“ñN޶tË–-IOOO¿Gô*ƵV­Z5r ‰ˆÖ®]KÀ«tWêèòòr¾ÍÊñ”ýúõÓÚÎÉÉ!4lذÙ^•››KuêÔ!Aú>U4ǃhÙ²eÔªU+ ø ‘s ÿúë/²··§:uêPbbbµl>yò$ … Ñ«ç,--q·ñññ€~þùg­êäM(•JЧo¾ù†lllH"‘ÐÝ»wùㆆ†Ô³gOQ¹… ŠhŽÂÂBú믿èÓO?%…BAÖÖÖUÆ«‹~øð!YXXµµµÀ™ÿüs@¿ýö/ãœQmÇ™C““Ã;¬êþ,,,D!J•ábU¡f\ÈMU4ÇË—/é?þ ^½z‘L&#___þw].Z´ˆºtéBR©”V­Z¥Ñ¶ŒŒ Z»v-5j”F}£¦°Æ‚ÂÂB”••A.—ói¬\]]QZZŠ˜˜îÇqóæM¸ººV;Áƒ PRR‚3gÎäqqqÈÍÍ­Y'ðj [‚0KKK”––òÇ8ÒÒÒ››‹AƒÁÀÀ€—ãÀ¢º---/_¾ÈkÕªGGG?~ÅÅÅ5îƒ:¦M›†'Ož`ôèÑ¢ô}ê°³³Ãˆ#pöìY¸¸¸`íÚµ"WWWœ:u FFF Ä¥K—´¶©]»v¨W¯¢¢¢’’‚3gÎ`РA‚ÔuîîîÐÑÑÁáǵ®·*$ |}}1þ|ìÚµ D$HíW·n]ÄÆÆŠÊýùçŸjëÔ××G§N°nÝ:Ìž=8xðàkÙgccƒùóç### ,”——cÆ °³³CZZšè/55–––X·nÝkµÉQVV†Q£FA©TbÒ¤I¼ÜÏÏ!!!ü_ûöíùc]»v$%% êJLLׄ««+=z$ªƒ»·k’:NÜ}mbbàUÿ7lØggg•ã|÷î]˜ššVkœwìØüü|,X°@e³fÍÂóçϱoß>õ4hÐpôèQ¼¤¤D4ÏV…±±1ÂÂÂðûï¿ã«¯¾Â… pëÖ-޾¾>vïÞ=z <<K—.U[_:u0tèP?~­[·ÆæÍ›Q^^^-›ŒêÀèjðóÏ?#,,  @dddµâT5çÙ³g0`ŸS•#;;3fÌ@vv6üüüx§xôèÑH$X¾|9gGDøå—_ãÆ«¶ #FŒðj+ܲ²2¯‹eË–i]ǪU«0gÎ<{öL ONNæ•víÚñòN:A©TâØ±c}kkkH¥R9rDðEDDòòòDívêÔ Àÿò.WdÆŒ¸wïFŒÂÂBÁ±ôôtу6”——ãÞ½{8tè±hÑ"¸¹¹aöìÙË;wiiiYvv6^¼xÁ篮Œ““N: tèÐAt¨C"‘à“O>ÁÕ«W1~üxÀ!C: …&LÀñãÇ1þ|QŒebb¢Vñ£°{÷näçç dœÓÀ=ø@Ó¦M‘‘‘ýû÷xÇ»mÛ6Q¿òòò°ÿ~‘£ÀÅ«/m|0`€Êqvqq©ö8s»ž6LeŸ}ötttªŒ­îÛ·/,--±uëV}Z$¿sçðy+¢§§‡;w¢_¿~=z4æÏŸÏKHHÀ7úEEEøûï¿¡££Ãv@d¼]ÞõøûÄöíÛ)!!Μ9CAAAl—¤˜'OžT*%dffF­[·¦fÍšñ2{{{AÑÌ™3I*•’½½=õìÙ“ÜÝÝ õéÓG£Z<ÐãÇ'Ô A0`¹»»SÛ¶m©M›6Z…püøã|æ gggêСÕ­[—•ÊåhåÈÍÍ%KKK@uëÖ¥ бcLjˆhܸqüë×>}ú9;;Ó_|AèÞ½{|=åååäââBÀ«\Ð 4 mÛ¶ñÇÇÇïØ­[7êÙ³'ùøøT*ÕêU5ÂÁ¥èãb‰‰ ýðÃ|z°ŠTá˜4iI$òöö¦~ýúQpp0™˜˜T*¥={öðzªò@gdd§§'i•zˆ(55•µ®˜û¹"………|ü¶³³3õìÙ“ºuëF 6]7šB8©V­Z@}ô5mÚ”ttt¨^½z‚˜Öëׯó¯Ü½½½ÉÚÚšLMMù\»\ÇãÇùxààà`0`_¬2»"šò@ý/Ä´iÓø\ÆšÂdΞ=K諯¾""Í!ø{nü¹ûxõêÕUf*©Ì–-[H&“‘©©)uêÔ‰ÌÌÌHWWW”§[Ÿ}ö WWW ãó–?^ WÝŽnݺñýmÔ¨‘±±1[¿fÍ^·gÏžˆsZW„K¹Ç3‡ª.v¯^½4ÚØ©S'ÒÑÑ¡‡jÔÛ¹s'Éår²²²¢¾}û’¿¿?ÙÚÚòñÑšB8¢££ x•G½{÷îÔ·o_²··'4bľœª<Ðååå|3gÎ$"¢E‹ñßHôîÝ›ºwïNæææ€V¯^­± FMÑ™1cÆŒ7ì“ÿgiÔ¨¬­­áàà€ÂÂBœ;wNð%:ãíbdd„áÇ£I“&033Cyy9ŒŒŒÐ¡CŒ1«W¯†™™™ L@@:tè<}ú^^^øæ›o0cÆ ÑŽ` …°±±ÉìììxYpp07nŒ¢¢"””” ,, ,€B¡@“&Màçç§±þþþ …££#  àîîŽîÝ»céÒ¥øì³Ïúzzz˜0aBCCakk WWWøùùÁÂÂ;w†‹‹ ”J%”J%ºt邈ˆØÙÙÁÁÁ|¸„D"ÁèѣѧO888ÀÙÙ­ZµâûŒ®]»ÂÐÐYYY(//‡‡‡¾ù愇‡ VHÕáææ´mÛ|ðÂÃÃ1qâDÌž=:uR›}£yóæhÞ¼9ÀÃà 6áÅ‹pttĘ1c°|ùrøøøÊ999 ú¨P(зo_èééáéÓ§hÞ¼¹`GFU˜ššÂ¾¾¾øüóÏáìì,ÒÑÕÕEß¾}ѺukÈår<}úúúúhÖ¬¦M›†^½z V»6lˆÀÀ@Q=mÚ´µµ5ŠŠŠÀÀ@Ìœ9?ýô“`‡E+++ôìÙ“ÏÓ­[7,^¼žžž°²²B¤GA IDAT`` ÌÌÌ```???˜šš"??ÅÅÅèÕ«æÍ›‡¯¿þZt«ÂØØ¨_¿¾èX“&M “É`ll KKKtíÚUef{{{èéé¡nݺ|V}}}ÂÃÃC Û¼ys´mÛþþþèÝ»7¾úê+L:3g΄¯¯¯V¶WÄÓÓÁÁÁP(ÈÊÊBHH~úé'­Â78zôè___H$<þ~~~˜5k¾üòK‘®®®®Úq«LŸ>}øÝ#õõõQ\\ ___ôéÓëׯç¯"Bjj*ºvíŠÐÐPµõ©gŽÆ#00¿_SRR`kk‹aÆ æ¶Ê8;;óóFÅ].+ãááàà`þþl×®"""àââ"šs¸z9{¬¬¬Ð¼ys"77R©ƒÆÂ… ÙA€ÿ]—œÍ‰¡¡¡°´´D~~>àëë‹FA"‘ ++ –––>|8–-[&Ê”Ä`¼i$DÕÌãõÿœ7âÒ¥K¸qã.\X£mŽ ƒÁ`0ïÿ¯b KJJªŒ[ÎÏÏGrr²Ú\’fff033CvvöËË`0 ƒÁxøÏ;ÐW¯^EïÞ½áää¹\ŽÎ;«Ô+))ÁàÁƒabb‚† ÂÜÜ;wîéuëÖ Ó¦MüyóðÃ?¼mó ƒÁ`0ÿ2þótFF”J%† OOOµzãÇÇþýûœœŒ9ýû÷G||<€W_cW\½NJJíŠÄ`0 ƒÁøïóÿ*:$$EEE8yò¤@ž›› Œ9sçÎåå666èÔ©¢¢¢ðèÑ#øùù¡~ýúxöìÌÍͱ|ùò·–”Á`0 ƒñïDóçéÿOˆ‹‹C^^žèƒÀ&Mšàøñã^9ÓwîÜÁ½{÷`mmZµji]ÿš5k°eË‘ÜÁÁ¡f†3 ƒÁ`Ôôôt‘làÀ¢¬PŒÿñŸáІG€(Í’§§'žùä“w^ç¤I“pü3Ã*¢ .¸"*.¹¥æ.îKÖW“Ê4K 5Í­¢4Ë,×R\*Ë­RËåç’š‘©Yjn¤©¥–¨˜¸‚ ȾÌüþ¸lÃ\6‡Ÿ÷ë5/g÷ÌD‡‡3çyÎoܾ}›äädüüü€¬¯};v,+V¬ [·n´nÝšE‹qïÞ=&L˜Pä>¸¹¹-Þ0iÒ$“_»=ŠÞÿý’î‚ÅqttdÒ¤I%Ý ‹ó0¾’/ dlQ'c‹1[Ô¹ººbggWÒݰhe¾ Ç;w7nãÆãÖ­[DEEe>Ïl7þ|FÅðáéS§;wîdçδjÕªXúqîÜ9¶lÙB@@@±œ¯,X¶l±±±%Ý ‹ãëë[Ò]°8±±±,[¶¬¤»aqüýý 2í…BÆu2¶“±ÅP@@[¶l1øF^¨{¤6RÉNGLLL±$fðööææÍ›Lž<<<<ŠíÜB!„Å!$$„>ýôSjÕªÅÚµkKºK«Ì/á((­V[¬Ás†Zµj{V®B!DqɘäÛ²eKIwÅâ•ù%B!„B'  Í$22’ÀÀ@¢¢¢Jº+#$$„ÔÔÔ’î†Å ,é.XœÔÔTBBBJº'**JÆ2¶¨“±Å˜Œ-†¢¢¢ $22²¤»bñ$€6“K—.áçç'X6’è£N}ŒI¢:I"T'c‹:[ŒÉØb(00???.]ºTÒ]±x’DhÞÞÞ²_!„OâÓdZ!„BˆZ!„BˆÚL‚‚‚d#•$ÑG¬“7&‰>ê$‰PŒ-êdl1&c‹¡ŒTd+oÓ$€6“øøxœ±··/é®X IôQ'‰>Æ$ÑG$ª“±EŒ-Ædl1doo³³3ñññ%Ý‹'I„f ‹ñ…BQZHÜbšÌ@ !„BQ@ !„BQ@›IRRRIwÁâH¢:Iô1&‰>ê$‰PŒ-êdl1&c‹:‰YL“ÚL:„··7~~~%Ý‹!‰>ê$Ñǘ$ú¨“$Bu2¶¨“±Å˜Œ-†üüüðööæÐ¡C%Ý‹'I„f ‹ñ…BQZHÜbšÌ@ !„BQ@ !„BQ@›‰,È7&‰>ê$Ñǘ$ú¨“$Bu2¶¨“±Å˜Œ-ê$f1Mh3‰ˆˆ(é.XIôQ'‰>Æ$ÑG$ª“±EŒ-ÆdlQ'1‹i’Dh²_!„¥…Ä-¦É ´™øûûK;!„BX¬Œ2vòÍ–i2mò—œB!J ‰[L“h3‘ùÆ$ÑG$ú“Du’D¨NÆu2¶“±EÄ,¦Im&² ߘ$ú¨“Dc’è£N’ÕÉØ¢NÆc2¶¨“˜Å4YÂaòUˆB!J ‰[L“h!„B! @h!„B! @h3‘ùÆ$ÑG$ú“Du’D¨NÆu2¶“±EÄ,¦Im&² ߘ$ú¨“Dc’è£N’ÕÉØ¢NÆc2¶¨“˜Å4I"4ƒŒ¢äžžžxyyáååUÒ]B!„0àç營Ÿ_fÌ"I„¹“Ú $›U!„¥…Ä-¦É!„B! @h3‘ùÆ$ÑG$ú“Du’D¨NÆu2¶“±EÄ,¦Im&² ߘ$ú¨“Dc’è£N’ÕÉØ¢NÆc2¶¨“˜Å4Ym²–H!„¥…Ä-¦É ´B!„ ´B!„ ´™È‚|c’è£N}ŒI¢:I"T'c‹:[ŒÉØ¢NbÓ$€6YoL}ÔI¢1IôQ'I„êdlQ'c‹1[ÔIÌbš$š,ÆB!Di!q‹i2-„B!DH-„B!DHm&GŽÁÛÛ??¿’îŠÅDu’ècL}ÔI¡:[ÔÉØbLÆC~~~x{{säÈ‘’îŠÅ“ÚLÜÝÝY»v-^^^%Ý‹!‰>ê$Ñǘ$ú¨“$Bu2¶¨“±Å˜Œ-†¼¼¼X»v-îîî%Ý‹'I„f ‹ñ…BQZHÜbšÌ@ !„BQ@ !„BQ@›‰ìêcL}ÔI¢1IôQ'I„êdlQ'c‹1[ÔIÌbšÐf"»ú“Du’ècL}ÔI¡:[ÔÉØbLÆu³˜&I„f ‹ñ…BQZHÜbšÌ@ !„BQ@ !„BQ@›‰,È7&‰>ê$Ñǘ$ú¨“$Bu2¶¨“±Å˜Œ-ê$f1Mh3‘ùÆ$ÑG$ú“Du’D¨NÆu2¶“±EÄ,¦I¡Èb|!„B”·˜f]Ò(-’““Y¾|9þþþTªT‰Aƒ1pàÀ’î–B!„03  ó)66–°°0¦NJll,o½õôîÝ»¤»&„B!ÌHÖ@ç“‹‹ .¤C‡ôîÝ›W_}•Í›7çûõ² ߘ$ú¨“Dc’è£N’ÕÉØ¢NÆc2¶¨“˜Å4   A¯×³gϺwïžï×È‚|c’è£N}ŒI¢:I"T'c‹:[ŒÉØ¢NbÓ©$ÂäädlmmsmÇ7hذ!Z­úßÓ¦M#444ß3в_!„({RSSÙ¿¾Úº»»Ó¤I“‡Ü£â!q‹ie~ ô?ÿüÃܹsù믿¸zõ*Ý»wçСCFí’““=z4›7o&-- ggg¾ùæ^xáƒv³fÍ" €;v˜é!„ÂÅÆÆ2bÄ—DG¿’g;½>/¯½lÛ¶¤H×;þ<û¶nSsŸ }‡ ¡Y³fEºžÈ]™ ÃÃÃÑétŒ5ŠíÛ·çÚîwÞa×®]yò$~~~ØØØ˜ë-!„ÂBÙÙ5"%åE­î£×ÿ[äk¹ººòÛ×_3ÏÄ‹\]þÆE¾žÈ]™_ýä“O²cÇf̘AÍš5UÛ€Î'NKóæÍ Ž·lÙ’РA¸págÏžåìÙ³¬X±"ß×8~ü8ëׯÇÏÏÏà–ó—ߣtlÙ²e|ÿý÷ÑK:æëëk1}±”c‰>–ÐK:–‘Dh }±¤cI„–ÐK:–‘Dh }±”cÙ“‹r>ðL+Ž>¿íëËguëæzÕ·ªVåÕ©S tŒ˜dÚ´i¬_¿žóçÏ#ò&4pëÖ-š6mjp¼E‹DDD ÓéÐh4ØÛÛÜ ²Œ£bÅŠÅÚç²`É’%”/_¾¤»aq$iس³3K–mí`Yäåå…——WIwÃâ,Y²ggç’î†Å‘±ÅXi[ªT©‚kŸ>¨…¸ççÇÇÅÅ¥È×y{¤ªpôïߟÄÄD£$ÂÏ?ÿœwÞy‡;wîüÐ-_¾œI“&q÷î]*UªTèëJ6«BQöÜ¿ŸÇ›Ex¸© ü>Ï??‹íÛ‹!X×ë‰Ú½›÷† cMz¹Ædlm™àî·9–¸Å´2ŸD˜U«VàÊ•+ôåË—±±±)–™Œ´´4±¶¶ÆÚZ>v!„¢4»qæÎ…â(™¬×ëIÒëIÔéHHK#1Çý„´4ƒƒI_Y‚»»»üA‘C```©©j.©©©„††âááQÒ]±(ëåëVC2¶¨“±ÅXaÆ–ÿþ__X·’Sõ`­+Xé•›VÖz°ÕNù×6Ž@Çjü|çŽ ëtY±NG¢N‡êr€¨(Pnwî(ÇìíÁÞžVZ-û´Z¥'Ìwr¢ï“O’¦×cUȸeïÞ½ìÚµ‹ãÇãééY¨s<*dt:v숓“çÎË<¦×ëù÷ß4hP±\ÃÍͯ¾úªXÎUV,[¶ŒY³fÉZÅ|}}åk³2}JÛZŇ-cBYmHÆu…[ô7½>ë~öÇz=h4†ÿfk“ó¹Œ@±0Ïéõzt ÜôzÒ²=NÓé²îëõèòñ\Lt4ë/fô'Ÿª×gÞÒ²Ýϸ݊гÓÿèÑiô0LšD¬­‘šzÍħ˜Èy\Xúë:w6Ñ4::+h7~¾n]hÞ‡¦MqÜ¿ŸÛgÎàX¿>$êt”/äìqÆ$_¯^½ õúGI™_Ïo¿ýÀœ9sHNNæã? OŸ>8::0uêTV¬XÁæÍ›iݺ5‹-båÊ•œ}@(Ê}= 4 ½>ë~Îç²=F¯Ïû¹ôã9Û>,9¯ùož3§ÌA?Ç FcðKÈóqöcƒ&ÛM«ÑdýwÎyL?—íçD«ÒV“ñÞ1ü%«úË7Ï!çs`ü‹5ó†ŸÑóÙ~±çl›ý§&óg(½möÇ9¾ óºÌ>à=½&Û{Гw aô\¶àÁ(ÈÈcT>£ì}0xœ£]Q^WÜòü,s>Ÿ£]æý\ÚœŒÎ]\¯+nùý1g@[Väw‹w[[ÛÌ]‹o߆#GàÂÃýKš4Ù¶í'âãŸÈåLR€«@Z¶„Áà ’¸88^ š¯_7~y͚м94kÙŠÄÇdzï›oè;v,¼Q«•‹¸W…¬6­ÌÏ@ß¹s‡qãÆËxìïïŸùµÍüùóILLdøðáÄÄÄàîîÎÎ;‹ ww=:õçȸt)ë9F‰a»v…jÕ ..òÚÔ©x+Ïs&¤è¸“Ö˜¸dø÷_eeưgq¼~Q š¯\1þÌ«V…-”À9—B8Ö¯Ÿy Q§3ñIä. €€€®\¹B½zyNº2@שS‡pµ¯@r°²²âË/¿dÉ’%ÄÄÄûWV¶¶ØW¬ˆµ]±žW!„ùceU‡ú˜h•È•+‡È:¢Õ*±l×®P¹rÖñòåËS¯N:]¼H^QÞŠyê•®ìÿ%ûkA´¸u‡å!@šaÃJ•”€¹ys%BχN} ßOBh{{{œ3gßEîÊ|]PZ­ö¡¬›s¨T îÝ‹ý¼¥ÙÝ«Wq®]­dúˆº|™* ”t7,Š.-û7nà’¾y€PÄß»(ã‹È"c‹:[TèÒ öT4[ââ”­¬ U+ðô„ÜBƒÎO>É7oòtLŒêó÷“ÕÿØÏð°K(Ë9²¤ØWÀ¦U3%h®U+ó¸­FC9++ìµZÊiµØ§ß îg{>û­°<<<ððð`ݺu…>Ç£Bh3‰•ívœ\·Ž>>ØË&3üW®Äkñâ’î†EIŽ‹ãäºuôŸ1£¤»bQ®:@“~ýJ¸'–EÆu¥ilIIIaóæŸÑéLƒM›Ö£}ûÇ w¡Ô8\í ÇÚ·‡.] B…¼Oáä䄦V-îÇÄÌBkõzì’“9’³gÏR3)‰rÉÉØ''£µ*Çá˜ÎHìÍÙä6Œ¬jÍŒZÊÛdŦÞyLL Ûw|ÏÞÃ{èß½?ƒ F[ ?÷¸¹¹ù: Y™¶å––q‹§c»õÌûx"Ö€µFƒµV‹µFƒUÆãlǬ5ÂoÞdùÓOóÙõëX§¥a–†mJ ¡ÀçÀ— $ÿyyÁ°aЧGŽY3x0DF*o¥ؼ9÷™îìvÿº›·æ¼Å þC_3=qù–†ú—ë³ä£% è_¸r¹QQQDEE1mÚ4œœœ$‰02m&1·n¼oîíÚQ¹]»¬ `x?ýq~îkÈ¥ªCöû* ªEä|ógb•:RëWŸ3'Õ &9žËó±Êë†ìÿ]u¨dÞCf†œ?%%ûç“ý3õ*š¯%{ÛÕ0²?oÎjjýÊþ~ÔÞCn=²WHÉ,äHXi4hM<—ó³ÎÙƒÇ9Úåëu*çxòüÈåçÁ¨yüÜd¶Éy¾ü|FÙÚnÚüÁÁ“³%‘©. .fðà¢í# ZeGåqÎjMyµÍùÙ©gù}.ãXLt4vóàÖØôÏE“m°ÊüM“ç/ñÁ{ã3>N®^…‹•ª.(÷/^Tv T*aT7ñ)YQ«N$= °œ³¦ åSR¸GöԻŠï=ý4Œ O=¥ÔpN×­œ:ƒÁ?ÿÀ޽СìÜ ç~­ÏýËk¾¯Þ'œìÓÔúz.×»Ìß1쩾‡Ö­Zç»ÿñ÷÷çÚµk´hÑ¢À¯”Hm&}Ÿx‚µ¾¾%Ý ! Ì ¸6tkr”X$Ç/Ýüþ2.¡’„B”‡ðøÏôú~‡Šh^¾¼zTòRµZ4Iihz G` Œ¡Ëo†f¸zæÏ‡ï¾cJr2 €åéO]¬žy7?¿\_^·.üñx{Ãöí¤Ñ[¶(3Òj†¿1œðΆÁs&-„w gä䑜=p¶ÀoÇÓÓOOO üÚGÐf’””TÒ]°8²[˜:KÛ-,û,'æ js\Gv"T';ÂÂ…_óãÇÈ>k›˜ø;;GÃ`Íöí[OùG$UcicKq9^¹©©Zš6…ÇwwX¼X)!—% 8¶ü÷Ÿ8¯[)JR êìÌ•û÷©Ìtrbì”)&OU¾k–²Êÿþ @Η‡‡‡sÇþäUÐË"­# §zuS³íê’’’°“ªay’ÈÅL"""Jº Gv S';“ÕÉN„pòäEþüó 0Hßòf«Uˇ”Ãêš²<¶Ô®(gÿ7{ɹû÷aéÒŒG÷ ü°=IWÀ¶ľ©Ïä}¡à`˜7þïÿ } m€¸ž=ÆÖªW?Kbá€K4¦åµþ¯1{ÚìQ²üÒÒk7k40hñS¦ÐzêH‚{C9ø+†_…[^€Ó->;ó_8ðÞ[ïöN‚ï(ÁqðÝ`‚uÁÔý$˜‹!èlï³Ø»;Û Rõjy*&U¤N:ùûP²ÉØH%--ÍtãGœÐfbkk‹³³3öÙ„Bˆü WWhÔHÙУkW¥>±ZiçÝ»ÿåß—æ8ú0¬ÉÖ²åÌ›÷°z\xÑÑÑpçWj3ØDËîÜLÌ×9©Sþ6 kI­šÇ® ÕöQѦî5¼•CçÏÃÇ+k,26(Ñh`ð`øðChÙ’ñ_år«ËPNyúÖ“spRÇ6‹åómŸãØÂ‘HmdV°|'˜{‰÷Ô;›s½–šuiÖÞƒGOc"Šv¼ëH£Bìz,©äŸÐfR«V-úç– „â‘tõªDPr[³Fy\£†HgÔ?Ê–Pp Nסb,Ä8BtˆþpOogyNŸvÂ6ñ>GøÇ<ÚMÇ™KŽ}óuÎêÕ«“pç:ï§àp?·Vz>¶¾O{×Jè?vÇYÕQ´Z¢öæò„aDºW%>%ø³§Ùwvº~Ùvÿs‚؆g¨Á¤•“ / µ-µ+Ö¦¡KCVnHç†ü¡!{64„{õ¹kcË+«`ÐÄå|pàbS¢+Vä­áo¡-Ć*©lÙ²¥À¯}ÔHm&’DhL’Õ•ÕDŸ¢$Bu’D˜›ÀKþ—”‹)Ëiò÷š àÕWÁߎ‡饣ÃÂ`Û6åJ¹a½°ùjN€n€#Tba[W¸µ¢@}Žˆˆ ?[Gh4\]] tîì>ÿÞ} +_ò; ¹£Ú.ø•†´ª\3ß玭ëÄêÄæ¥æ9Sã`­Ž¥}”™–š¦…M-à“®:.UÙÇ÷eÍ^§jà9cWWà/Ð4×P«b­Ì 9û¿õ+ÕÇÞÚð[ê)]àëÆðƘ#Fœ£Q£`œ¬jžBR÷„¡¹r½ IDAT¬kéÀîH9œÂ«s$å?zv9WèRt’DhšåŽ.eŒ$“$Bue9ѧ°$‰P$æf9“-ÉîÝðÖ[pùrÁ^çè}¤ÜOKƒ³gáèQ% >zTY  ì( µÞïYJü/”`Úû¬~›´´öù¾¾÷ AÔ ¥b[¤Ç¤¥qÅÝ_þü³`oåŠ×_W [€æ?[k™‘‚ê,ôÇZ;«9S±¢M>ÏŸDгžï+Á‡ à€„/#kÕü `Z²ò‘¥jaÃãðIW¸ì’ËI­Q‚hSbà§Þáãw?¦œu¹|õ7Ãë¯+I‘ʦ+— jÌ‚àßàßÏ \ú2„J$=x‡ë©ý¸¸›áÃ/:€–$BÓd'B3ðöö$9L!ŠSb"´jåÃ¥K³0,ÛÚúpôè,Ú·/¹ úòeðñ]»²Žµh±±O’”dzÓ‹J•Îðk®ÏeÓ~hFÚËòÞ3$ªþÒ”Û¡¹Ô€ËáÏcÇ80hÓ ëÀø¤Jzÿô;wÎ×93„…Á³Ï‰Êãõtüü]Ñ×~åð¤I¼c¸\!_§^{Ͱì¥Ú}†Ðû¡ü}ëoN8Ãõz<`FŽ¢,ñÀH`ƒ4¯ÃåéCH®S‡=zø¥/ù1õG³?þXø?®%n1Mf ÍÄßßooo¼¼¼d¶H!Šè÷ß•™¹üµONVv~›1¦Ns®KHPJ/\¨Ì²‚²]óœ90aÜ¿¿)_%öllòžimÔH¹ÛDgj…+Äër†sêØ¹3K5"&*ŠŠ*ÏÇ5bznÁsJŠ2M‘u»}›ð"8þc'DPÛÔ± Òµ(è¡T‚XŽ0gŸ…^L¼~fÎÌWßÝÓoÏ„ÃP”€Ù![›@­ÆÐÅÕ•­kóT½z9O£jõÂÕô›Ô›ÝrV=QhîhèR¹K‘‚gP6]™?_Ù üañóóÃÏÏ<==Þ…Ê  ÍÄÓÓSþ’Bˆ"º{&O†Â §II0}º²VøÛo¡uÁw:.°;àw”ÙCP&EGŽ__e“€ÊÙ‹½F—ÛŽàY4¦-Xmì7-bi.³ÐK+Vä®]•ªÙäÌû÷Ô+NTžË~ Ùðù‰(sF¸X ÛãÂ0gìS¬q…ÿ:Û2Îu(õò<4}¬)Ó^𯂠¸Þþzf5‡ZD¶`Ý÷ëŠÐÛ,»WÆ$_Æ ´ÈÐf"I„Æ$‰P$“$BuZáÆðöÛ©ødýfâ’S8QÁ…í ~ G·ùü”Õ¯_Ÿ_¾ÿ¥Ð¯·$’Dhš$š,ÆBˆ‚KKSÑ™3!>^9V«–2‹[”¿ΜQÖ Ÿ=«<Öj•2aóæƒCÞ¯ÍË–-0e ÜL_ ke¥¬Óþøc¨T©ðç-ˆ«ÑW²m'ož„}€Ðظæ¢VGÜHm› -¢Á66ó9;+;7ÌøvãéR§‹ñ‹¿ù†—_•@E”Ùç± ²eýú¬@9—20žyFÙ”e4~~ « }m(Ûì·¡¯ª„) ÖÀ“Qðõ0HËh¿ð$sÓ’Îu:3ºõh†4‚£m^¤Ó¯Ñ®Í._¦ÛÖ­ôèÓÇdû’æççdzς$–<™Ba‡ãĉM¶³²‚.]F1a‚-gÎ(Ç4ezþ|¥ÎqQ´n §N)3Ñsæ(3Ó_|?ÿ «VA¯^;_@€€:”u¬K%ÐoÕªh}-ˆŸƒ~æÕ_ÍÜÝ®íËmiØŒ}öY'’”Ê)ØÜ±¡êõª<ÛáYfý;‹ýûíû†žõ7@Û¯ ZIiIl<·‘ç6ÒÒµ%ãÛŽgxËá8Ú:¢ß¼Æã à |¤‡™vpØ1šéûfîssÝÀc÷nx饌2{0d|÷]î´üsùô=²æøBûÂ÷¿f žC…ð ¼þÔëŒn=š&U ¶ümüüùÌ™<™¥ x–Ef Í@þ’B<؇~€zUß,åÊ-!9ù+ÒÒ”%ÍšÁ7ß@+£åËÅ‹ÊÒŠãÙ¶uöðð!>þ6VVyog’!Cޱj•3©éµ€«WWó#Š¿¯¹IÕ¥2íÀ4>=öiæ2†w:½ƒo_l´6„……qìÏcœü÷$í[¶§sÇÎÔ¨Q#óõAAJ ¹ €ºGqè¶’”†?¢ËÊæ«`[ߤ®Œû Ö:=ÑöÐÅ öF@×::ì/Û3´âPÖ.[kÔG__%S§Sþúp½Æõèë\¹ÎµhÃû7îÞ qg"<“ãD9ËqDƒwª7ß}ù]¡?¿ÐÐPÜÝÝ ýzs’hË!3ÐfräÈ)c—ƒ$ª“$Bc’D¨®t&¶ÅTÍæ„¥x±t½÷ØæË(ÈØòØcJíä/¿T®ŸQo…É~jµ>¬\©Ü·¶V’gÎ,ú yA܈¹ÁÐíC9výÎöάõZˠƃ2ÛÔ¨QƒçŸ}žf5S[5Rê/Û¶u%~CWl+EòÜGk8­ýšÐû¡´~ÀÈõ{°ÖA¼ x .j ýˆè§œ'±A"{þÜÃ#poáÎõèë„D^céúëüz†]G[éöÕ®375š¹¦6BTÛ $çß^÷àñ¶…/—ššJjj~vB±•+¯ÁÞþPžm/£ |îŒ2vGŽ¡[·n…ëà#B"3qww—¿är$Bu’DhL’Õ•å$ÂΕRsUÖïšRбE«U68yæ3F©1:òoïÞJÞ´iÁûZ{Cö2âÇDÅ+Hµ­Ù–­/l¥ž³z ¶¼ÆGGغ/†÷߇ä{UÙñÎûŒ~m*#ú-£Ý‚É”KK%E Ïÿôe·{‚®NÖy"¤ÏÔ>ðT¶“W!³ú‡ˆ×_ßZkM­ µpsr£ŽSÜœÜð;æG`\ ”Ïý3p½íJß}so`Bi[þ÷¿ÿÒ#_mË—ÏãƒËEÆ$_¯‚®czÉ3¯B„"c Ç,LÍì:;ûðß³¨T©dþ¸~â NŸž…©~ZYù°zõ,¼½ÍÛÏ4}3Î`þÑù™K6&¶›ÈgO~†­‰e'ùq𠪔 |Œ‹³î†sj: ¼8¶53q‚g Ù¤Tᱚu¨ç↛“›A ìæäF Çh5†k§CCCé6²×{\W½Œ6BËà¤Á|¿æû¿Y¡JâÓdZ!„E)_ÞpGfs«WNŸ6ÝÎÕµhÕ@ #,6ŒaÛ‡qøêa@Y›¼ú™Õ i6¤Ø®Ñ³§òþ' eù?}qNUf¸g{x°­©‰­õàbåFÌÞqƒ˜:LîÆ‹ËaeU°~¸»»óɸOøhÙG\mw5³Ò:¨x©"­âZ±zÓê‚¿A!ŠÐB!D!<¬ ?..ŽË—/Ó Aƒ¯á\9ÀË?¼LDœ²¯À㮳mÈ6º4,ö>Ô¶ ãÇ}РÔä›Âb>½¹"P¶ÌM8Ü ÔAü(lmaåJemua:‚Þ]{óÎŒw>L’. F<7‚1¯Ž)ü‰…("  ÍDv"4&I„ê$‰Ð˜$ª+I„_QÇ–ÔÔ Úd;.‰´´´B]CÍßgþfÌ»c¸Ãâ+ÄãðÀÊTæë…_³çÁ枃N¯, ~­Ík,}j)öÖùßÛ9ßcËݻЯšÿ.pvÀ‡,Û7âÿ‚'aÌêÛ„ëõ!¾=®®Ê6æÅQ9¥fÍšlY½¥è'R!c‹:Ù‰Ð43ì‰$@v"T³lÙ2bccM7|Äøúú–t,NF¢0äïHXc¬™§¢Ž-ׂþ¦-mLó¯ù¿5 ˜:€3Îp­ë5¢ZEq­ë5Ît8ƒç›žÌúr:½Žò6åYÿìzV \U àò9¶ÄÆÂÓO+Å­&M¢ÕÏsñ÷[[;ˆô… µ!>Çëâõµ!j1åÊÙñ×_§ì`q“±EÄ,¦I¡Èb|!Ä£..êÕó!2r¦’ójÕò!  ä*ôxyMäöO{8Fh®mŽ¡å%ÇÖœ½¾¿Èý¼uë†vâZŸk¹7Ú†6âÇ1?Ò´êC*÷‘”¤ÏeHFŒ€uë2ת4oîÍùókÿÀy4T¸N‰mêÀý5@}Z´ðæß×>œ> ³¸Å4ùî\!ÄC£Äe‘‘ßam].Ï-³““Ï™«kª¬­mâþ`]P©¹LÁ”ŠŠåzs>õVyÏÝ¡kdׇ<§¦*[fσ)u³-ôÎZSî„ûÀõÛ@5ƒS岡eŠÐfâïï/©!9wï“OÂ_¼§góæM^¯jE… ÌÓA}û¶'2r*Ïï Œž?¦ÑXÇÇ›Åa[^rñ率‰F•á© E¾–*½FŽ„;•ǽzÁ÷ßg˜óPÍtQjdl¤âïï§§gIwÇ¢Im&íÚµ“¯Br$Bu’DhL}ÔYzaDôí çÒ'”_x¡.7Ö5<]QÇ–×_‰×_‰S§rüÓOé¤3œ…^^¡G&ŽÀ±U+¸sÊ•+RIްÓðÈǤ{djP¡¯yŒ-o¼ÿ÷Êýà§Ÿ”m 2¶ʘä6lXIwÅâÉ-f" òI¡:I"4&‰>ê,9‰ðæMèÞ=+x~åؼÙÔÌsñ(òؾ¾Lüá–åž-cbp|ï=ejÝÍ *T€¶m•5ß|¢”Ÿ¸pRRr½Dbj"ëþYG‡Õ¸kͧ7!øZî·OoBõZµ ÿžÈel™>–/Wî7o{ö(Û>"dlQ'1‹i’Dh²_ñ( UV\¹¢<~ýu¥pInŽbRj*ìÞ kÖ(AdzÉ@W £ ÄKvv|cg‡cLŒésZ[CƒФ <ö<ö7j:òUì!VmänÂ]¥Ý-x|=œÍ£ G[k–ô§CûâYw À¢E0uªr¿A8zjÔȵy«VÞüóÏZ“§}üqoΞ5ÝNX.‰[L“ïÎ…B›  èÝnÜPûøÀ矗lŸò¤$Ë­[áá†Ï5nÌÄ#ûÍ7t¾v?´ZóM.Tf©/^„À@åߌû7of½>5.]Rn?ý@màc`|E¬×j–§ZÛîlhx™ŸÏ_b JÎâN-T~¢m‚ç7†çAhhî "" $„ ÀÒš5aß¾<ƒg!„!  …B‹€èÓG‰Í>ø@YÑð0}òÑGþö[j–+—g»“ ;^)9Û¶)³ÍG6,_^©F1z4té‚#ðxt4|ú)ËêÖeÕŒJ»5”[¯^†¯ð 3¨Žû÷onœÜ‡õ¥`êF¥b-8®£Üø/ü÷Ðx¨Ò÷Εعãç}.›6ÅcÛ6ú''çÚf/âà Ïõê™üë¾þÖ[\Ù°Õ—/g ÜÉúwØ8z4ÎÁÁJмy³R[/»Ž• yèPeMs6gÌ ïºux Œ£©õÁ*p¨j+BwáçäGJïè 6iÐ:®¯—ïÎ3º†T¹©Ú‹Ð%¨íŸít{wïaWµ*¸ºBÆÊÍÃ#ë_£>™<™—¿ûŽþ!!™Çì#Ëj­–ûöAÓü•Æ[±bN¾Ú•&2¶¨“M“ÈÅLdA¾±eË–1kVÉm–`©|}}eÝY‰>K–,)é®X”ŒÂ’.yì˜Rç9:}÷ëO?…wÞ1ϵ«T©‚kŸ>\X³†Œ0p0‹¬íZ>sqÁ÷Ø1%xήjU%ñoôè\ƒÈý¿ïç»­ßq¯º#¿ÿEüü™L™4ŨÌ^tR4ëÿYÏÊS+¹uÑà¹65Ú0¡Ý†5†ƒJì7àâEÆðòìÙôÎÚF|5°1ãAD„rSKÍ\Û5lH'Ÿdïµk™³Ð¾ÀÚôæ{£FaW¶ |ˆdlQ››|“I"4YŒ/„(«„•5¥ ÃøñæíCTTï=ñk®oFrØd®$±²R¦ÊG‚gžÉµ,Hrr2/¿þ2‡#Ù4Ê:ÐÞÐRïB=V-XEÏn=9~†•§V²éÜ&âRâ2_oomÏÐfC™ÐníkµÏ÷{Y6o³gÓ?9™½¶¶„ŒˤîÝ!8BB²þ 3y®$”e!ÛUž\£¯\‘YF¡JâÓdZ!D¡üò <÷$&*qéêÕþ{׬2f¡Ïû-Ír<÷J% ê×W‚fooÈG98Ÿ|ø‰ŸHi—­tn:.׺Ìw‡àþ’;ÝÿËàu.Œk;Ž‘­FâRÎ¥Àï%ûÒ‹Õnnl\¼X½&sl¬Hgªs×¹. ±¶¦Ç¤I< Q@ !„(°„_„äd¥ZÛÿýŸ²|¸D$$ðއïYY±&½ü@àÚ UV­‚=ò]Gï¿ÿþã§s?‘Ò9—:ÎVÕ)ЍMQð4Xi¬Øx ãÛŽ§oƒ¾h(|½>;;;zŒÉÛsæÐcäÈ܃\GGhÕJ¹å”-¸sñ"//^Lÿlk¾W»»³qòäB÷Q!´ÙH¡1I"T'I„Æ$ÑG]I%nÚ¯¾ªTi³µ…­[aÐ ³vAñà¬XŸ}F•Û·qE šíQ’?«]›…þ ü|~ûý7ª›X"Q ¬“­™ÖmcŸK튵 ÷TŒ™<™î›7ã[Ø 7[pmô°²bïìÙ¸''jk›w`þˆ‘±E$š&;š‰$“ÕÉN„Æd·0u%±áš5JÞ]jª²ƒõÎ%<ß»³gƒ»;¼ÿ>ܾ À;MšðY•*,NÕûõ+Ôœý}eÓéA5*Õ`zçéÅ<ƒ2 ý«¿±0c&Ofµ›¾Àj77ÆÈìs&[ÔIÌbšÐfríÚ5¼½½ñóó+é®XŒ%K–H’´aÌÙÙY²äUxyy™µÇÒ¥0f ètÊ$çž=J>žÙDF´iP·.ÌšwÓwòkÓvì Ê… TæF߸¹ñ΂ù>ulr,›6óÜ÷Ïñý­ïáŽé×”×”h³tNNNÅv®Œe!•Òÿ•™Å,2¶òóóÃÛÛ›k* ¹Â|wn&žžž !J­ ”É^''%°S'3]üÖ-eÛéo¾Q6AÉЩ“Rlúé§øç߈ªh…·­ V¶°Ío£_­­­êiãRâØ´‹­ç·òKð/$¤&(OÔNõóèS4ÔwÍ«e)ò²ñHÈø£Ü»$²K  …BäiæL˜“¾‡FåÊðÛoʤoA}>>Ç6m¢|.m†›:[ÿJÑÑJäþÝw=¤gO%pNßP§Ó1ñ݉ütþ'Â<Â(×Ú\ãìá·X¾a9ë¿XO›VJ‡ãSâÙ¼›­ç·²'xñ)ñ×®d_ ¯Þ^„ņ±ÿú~Rë¤wPuÿªË·Û¾-ø‡PBŠ{Yˆ:  ÍD’I¡:I"4&‰>ꊚDèïïÏo|‰Úæé®_‡ððxàM\]=Ù¿š7/ÔåøÂ \]¶Œ%·nåÚæ&0·gO*½óŽRÚ#5[Û¿?|ôäØücÆü¬»½Ž„NÊ rBkÀ R*¥p¾îy†LÂÌÏf²ëÚ.ví6¨× àlï̠ƃÒl}ôÅFkCⓉ =„cãNË;^.Z®ÁýŒ;+>^««ká>ˆV¬KCÊ[ÔI¡i¹˜‰,È7&;ª“ÉnaꊺaTTgϾ˜z½•+Gqø04n\¨KàááAb›6ܼu‹Ü*1/*WŽ)‡+ ­A)=7h2ãüÄFí¯_¿Î†ÃHè’uð$Jd{À.·½Ì+>¯@¶õÚí*fÍýôÃÖÊpVÜÞÞžw²ë—]|·í;nDÝ œM9Ú5kÇ¿|@¥J• ÿA”[ŒÉØ¢Nv"4Mv"4ÙÑGa‰üüüxöYÈOýÕWðúëEOX aY÷Ð7¹ÀWZ- Ó§ç9å½fÝÆìƒÞÃį² Â° <Óø†4“Obg%3lB¨‘¸Å4™BaRq­VÈkz0ÅÊ †‡>€FLžïØéc諚žªêT•à7ƒq*/K„E'´Bó¸s~ú‰)±±,ÒhX’í Л@bݺx:¤ÔwΧZÀ_€‰¸¸’u% ž…ÅFê@›‰$ !5U%ÃýXÒ]°8©©©„„„”t7,NTTTf"aadÛõúቈ€¯¾‚¾}¡zu=C‡HÔë¹™­Ù"WW¦ìß_ à _Ï~T ¯fxð. Ëö8ê¸Ô)ä(;dl1&c‹:‰YL“ÚL$‰Ð˜ìD¨Nv"4VÚv ;uêãdÜÿþ—÷mà@N:Uèëf'”øõWxí59²Ð—ÎÛðå—н;Ô¬ ãÇÃþýY5jÕbÊ+¯°(½zÈM ±]»BUBhÚ´)}Ýû¢ Ïöëì$œõÐí”k>[Sø÷SFÈØb¬´-æ"1‹i’Dh²_ˆGK||<#›7gÝ•+y¶{µ^=¾ ÀÁ!÷2r>ÿüs–½÷5¬lòl––¤syûí·3%'+ñëöíàç§ì„­ÈØÕtá?š¨öqå üðƒr;qrþjqw‡çŸWn;‚Føùh×.լɤÇ ]J,!!Z½kq¯Ê=hhÒŸ¸¯Ôkö}Û—Ÿ{±PçâQ$q‹i²Z!Š™ƒƒí‡ 㯅 ñÌe™’¿µ5í‡ ËWð àîîŽMŠ ‡S®c•K›4 upww'9YÙðdÛ6عîß7lkgõë_$îâ*™—çµc‰ââÅ1Æô¥KJÀ¼};œ9cüÂF²‚f•tS>ÿœÉþ‰s›6EªÃ{ôÖQî=yÎCÝ£u©`Ur6åh\·1 ·.¤F…>·B¨‘Z!‚ñÓ§3æûïñ¼|Yõù•uë²júô|ŸO£ÑpA¬b-ãP_ú´ G®1ˆ/¾Ðàí 11†ÏÛÛ+{‘¼ð ¿ýÖˆÙƒ“ù‡S™“¶9éÇ©E£ŒŠçÎeÍçÏ¿ yó¬ ¹E‹<ß“‡‡Ž={2e^Þ|^ôè™v`•ž¨ÄÙ·Îâl/µå…—аuëVvíÚÅõë×ùá‡pqqÉ÷keA¾1Ù‰PìDh¬4îæàà@›¡CñW™…ö×jiÓ°!Û¶A¹rÊÍÁ!ë~ö[Æq ^,áwÆp+ #}° ÊìóÜH ‡g]«\9xê)%h0³ž«P¡1•Ùz7œ¡zõŒÂ­+ÊW´§Í¶m0m7jÓ&+h.àN+ËÖ¯ÇÞÞ¾@¯ÉnÛùmœ; À{žïálï,cK.dl1VÇsM“5аaÜœœ5jT¯^=_¯óööæÚµküþû﹇¥‹ìD¨ÂÛÛ[Öåpÿþ}fÍšUêv ‹gL‹lüï?ƒã/«€ü-ÞP謬ˆM³c ÖØË+è V0¯ÅštÁgâ­pkÖŽ¦+Ò²KìªV„  bŽmlHMMeXóæl½tÉhZ ±±asJŠál‹F:dÍõêìƒ)&©ºTš.oJðÝ`jV¨IÈ›!”³.'cK.dl1VZÇ–‡­W¯^¸¹¹ÉÏK$€.„êÕ«söJ€Ü IDATìÙÐ ‹ñ…x䄇ói›6t Ã3ý?p˜\ÈSê€ÁÀ6È\ ¼l§€¥•ìì bEÖêõ8ݽ˳:ÁÓ?Ñ€7(;zz*ósÏAíÚ…|Åçë¿¿fÜ®qÊý_3ö‰±%Ü#!ʉ[L“ï·„âaøç8ñaaŒÌzeýú¬:rDyï[Ð?ÿðËŽdÊQ“Ú°“x£, Ùˆ–nTã6Pû”'1}LJ‚ÈH†/¢ÌdgÌBë [zõ‚ÁƒáÙg‹o;ÂbŸÏìC³hèÒQ­G•p„’G&€NNN&44{{{ÜÜÜTÛèt:.\¸À7hݺ5®ôËBQŠìÜ /¿ ±±8mš6Å?(€6/¾ˆC­œ›X›vÁÏŸ „¹:Ó‚\`M¸Ä9ÞF øá·CÏ ^½àÁåc|?Û1ë˜ú:ÅŽK—x>ýKÉíZ-&OÆzáÂbùHŠÛ'¾ ,6 €{}Œµö‘ùu&„°e~Ä9vìo¾ù&çÎ#99™îÝ»sèÐ!£vwïÞåé§Ÿæï¿ÿÆÕÕ•›7oòñÇ3½Yòy‘$Bc’è£N}Œ•ªDŸO?…©SA§SÖ ÏœÉøwßeLË–ª¼‘;-טÂç¼À5¦}ñ†^£''å–ó>›ÇŠÐß©¡ç¹ôÑsœtÜûsÉŸWbÚÛÓŠ¡ÏÅçnÂ]ø/à‰OðB³ ž—±EŒ-ÆJÕØbF’DhZ™ß‰P§ÓÑ£GÖ­[GÇŽsm7~üxÂÃùzõ*7nÜàÿþïÿøðÃùå—_Š¥²«1Ù‰Pìf¬Tì–’cÆÀ”)Jðlo›7ÃÌ™™9Ú šïºÏ¦$ð*Kpa .$ðj¡Ï³sÏN>?ø97{ß$´l²Vn¡àfï›|zàSvÿº»Xú\\æûÏ':)Z¹ßg>šé2¶¨“±ÅX©[J€Ä,¦=RI„ýû÷'11ÑhúöíÛÔ®]›éÓ§3sæÌÌãõë×§eË–øù)¹îS¦Laûöí™™5mÚ”={ö˜¼®,Æ¢Œ»wOI®;xPy\½:üô´oŸÙ$>> ÐôÆyå•/Ñ霰¶VªÅ¥Ä„PÎ%kö,))š+Þ¤gÏžù:o“®M¸Ôë’2¢ƒfË•ãç'’y¬á¾† *T¿‹Û˜4\ÚÄÔDzÕëÅW”t—„(s$n1­ÌÏ@çÇéÓ§III¡iÓ¦Ç[´hÁŸþ™ùxñâÅ„††Fhhh¾‚çì×X¿~=~~~·¨¨(ƒvrLŽÉ±Rv,(H)évð ~@T³fpòdfðœÑÎÁÁ!3x.è5RSaéÒžèt?¢Ñx±qãbÖxõ(W2k–û÷/æìÙµ\¼ø#={öÌ×5BCC‰Hˆ 3çP W;Àew ŽÅºÄòÍ7ßXÄg?ëÐ,!|ûøèµrLŽÉ1õc1É´iÓX¿~=×®]CäMhàÖ­[4kÖÌàx‹-ˆŒŒ$5—­x…8èØ1ks‘öía÷n¨S§X/3{6œ8¡ÜúièÕK¹¯ÑhÐhrÛCд   âì㠎Ŷ…Ćíî•¿—9N–¤À¨@Öž] À€FhW³]ÉvHñÈ’%À’%Kxûí·‰ŒŒ¤J•*™Ç—-[Æo¼Á½{÷ŠTßÛÛ›¤¤$6oÞ\ès”E’è£N}ŒYd¢ÏêÕ0a‚²ö”µÏ (õ’‹Ñ@÷î–¦ì’}ꔲ¼Èœ=Ê>nDhh(&v"¼}xžíjœªÁ±eÇpww/ÔuŠËó[ŸgÇÅXi¬˜@“*êÿŸÈØ¢NÆc9¶X€aÆagg'K8ò 3й!JPá¿‹/bggW,»Y………‘˜˜(³ÙÙH¢:Iô1fQ‰>:,£Ï66J0½hQ±Ï110|¸<ÛÙÁÆYÁ3€¿¿?þþþ…>¿»»;÷”ÝYr£Â)ñàùä͓츸€‘­Gæ<ƒŒ-¹‘±Å˜E- 55•ÄÄDÂÂÂJº+Oh vúŽZçÏŸ78~îܹÌçŠ*,, öîÝ[,ç+ –,Y"[íª¿ø9;;[ÆV»±±à奔ªpqß~ƒÑ£Êå&L€ÐP徯/¤WÂËäåå…——W¡Ï!òÑÍ£•ísãáÍÂñõ/Ùàëýýï`omÏÌî3ól+c‹:[ŒYÌØb!öîÝ‹Ðù 4бcGjÔ¨Áßÿy,11‘sçÎñÜsÏË5:uêÄW_}Å€Šå|B3»~]ÙÊú矕Ç+ “{ôx(—Û´I™qè×Þz«xÏòæIº}×;5ï€Tø­ÜÏÖà>TÙ_»òvèëë™v`Þ~Þ$§%oGòá·Ë¿q0T©p2©ý$jW,ùmÄ…(‹ ÀW_}E§NJº+¯Ì/‹ŽŽfåÊ•\¹r…”””̯±ÆŽ‹‹‹ ÖÖÖL:•É“'Ó AZ·nÍ_|F£aüøñ%Ù}!„%8u žyÂÓ× ÷î Û·ÃCšå¼zU™}¨\Ö®Uöd).®Àk‹±ÉÊ2‡Óðlµg™÷å<.ž¸Àc ãƒo? É)‰›p5ú*ëþYÇ•ûWØ1t•ËU.¾åA¼8Ù91ÍÓ²6uB<šÊ|ŸYǹR¥J™‡ †‹‹ >>>ØØØ°jÕ*æÍ›Gûöí9räõêÕ+–~±eËš7oNóæÍ‹åœ¥$ú¨“Dc%šè³u+x{CB‚òxÜ8XºÒÏ­N#F@´²O«WCêm “D¸ãâ^úá%’Ò’°ÒXñÕ€¯x­Ík|·ô;Õלs’A[ñç?9rõWwd×K»h\¹qþßX!m=¿•Óa§˜Úe*.å\L¾FÆu2¶“$BCD£FJº;­Ì/á¨Q£þù§ê­nݺm'NœÈÙ³g¹wï¿þúk±ºñññ8;;cŸ=è'‰>ê$ÑÇX‰%ú|ü1¼ø¢<[YÁ’%°råC žæÏ‡£G•ûcÆ(K®sSÐ$ÂoÏ|ËmCHJKÂÖÊ–ï_ø>3xÎKµòÕ8øêA^lþ"!wC踺#®<ÜMLRu©|øû‡Tw¬ŽOGŸ|½NÆu2¶“$BCööö8;;gnü$r÷H•±+)²£¥LR’’˜±¹Bøþ{xꩇzÙS§ sgHM…Fàôi(_¾xνøØbÞÝ÷.åmÊã÷¢}ê÷)ðyfšÅìó°ÖZ³â+ÓfLñt2‡¯þúŠñ»•etËŸ^΄vÊu„†$n1­ÌÏ@ !DDF*;•dÏîîpìØCžãâàå—•àÙÆF¹|qÏø 3xv)çÂW*x˜Õc›žß„•©ºTÆþ<–É¿MF§Ï«^ÁŧÄ3çðTjÀ˜'N.„…! ÄÌ$::šÀÀ@ªT©RèM„…“À¥K—L7¼|'êݸ¡<îÜüü jÕ‡ÛA”*Ξ mÛýœ:½Žñ»ÇóÍßßP³BM~ñͪ63ñʼ k> wgw¼¶xq;î6ŸÿŒà;Álz~޶ŽEï8ðʼn/‹UJiÍí5­M±œW‘»¨¨(¢¢¢ˆŽŽÆÉÉ©¤»cÑd ‡x{{sæÌ† †§§'žžž%Ý%‹ ‰>ê$ÑÇXQ}’““içîÎÈÄÄÜ¥¤ðg\=ôzƲƒÉêÕÊ&ÙŽðüóÊýnÝààÁüíÉ’WarZ2#~ÁÖó[ðpñ`߈}¸;»W· ½Ê€M8©ÔÐoU½?û¹Èeæî&Ü¥þõ‰NЦUõVœ~ý4ò_†DÆu2¶“$BCy›7o¦uëÖ²„#²„ÃL*W®Ìûï¿/Ás6’è£N}Œ5ÑÇÖÖ–ño½E“¸8|îÝS¿ÅÆ¢×ë¥Ñ(Ƀ6˜%x¾uKI¥*Þ† ùßÐ0·$Âø”xžÙüLfðü¸ëãøò/ÖàÀÝÙc£Ñߣ?gÃÏÒ~U{þºõW‘ÎëëïKt’R†d~ïù žAÆ–ÜÈØbL’ yzzòþûïS¹²yÊT–f2m²_ˆ’—œœÌðæÍÙš±N"‡½@¨ ã6m‚ÁƒÍÒ'½úö…éÅ,6oVŠ~ŽÄ{üoãÿ8~ã8]êta×K»p¶x;ó¥éÓðÙëò“J RκžÛÀó=_às݈¹AÃ¥ ILM¤{Ýîò>T̽B˜"q‹i2-„x$ØÚÚÒkôh~±µýöî<.ªò{àøgØAÄ%qÅ$÷½0KJËLm×ÜCÍJÍ~.Y™e¦ùÕl³rOSËŒ\Ç4÷Ü¢Üs É]Pvæ÷Ǖ͹8,ÃÌ8ï×k^]îܹ÷ŒÁÃáÎsž£úü·NN Ý»×jÉ3ÀçŸç$Ï.yNJJâ?þà?þÈ^rêrÂeÚ/mŸþôY݇[©·x÷×w9sšEÏ/ÂÅQý—Ü"b"XzLiæÔ0ˆ¶5Û–tÈBˆ\²©œ;wÎbäJ+¹m%:N©Ü%22ƒÁ`ë04'""ÂÖ!hŽÁ` 22²Øçqqq¡cÏžl¾«/ö·õê1ôƒŠ}þ‚JNV–¬KKSú³¬Xžžæ_wûömÚ>Û–_*ýÂùvçI¨›@BÝη;Ï®j»ˆZ©02`$+z¬°ÉÊëuæadÏ·^~b9O}÷7’o˜}í{»Þ#أΑéON/r 2¶¨“±Å”¥Æ–Ò"«‘ŠNW¸ºƒ²Hh+qpp K—.R雋ú¨“BS+ôùç†þðKs•~lvq¡ãË/ã’ÏÔŽ¢ˆÍ·ê4èýu8À¸q±´kW°óù¿!œi}c•;ñGßyT:Að†Ì}f.:Û ïMª4áÐðCñ?~œv½­]‚\]ù¡Ç4¨Ô€){¦—G—]˜Üt2›nâbæEnzÝÄ%Ù…ø¨x¨ #ÆŒäY¡y’@ !J·ß~ƒçžƒ[·”¯?ý—ñãyª\9¦ÌžÍcÇÚ6¾rttDg4?/QgÔáXЬÜJ&·ŸLýJõ²a)R˜¼z2<¸+Ï'“  ;­ãø‚ãÐŦá !„Y2ÉÅJöíÛGpp0z½ÞÖ¡h†ú¨“BSE.ôÙ¶ ºtQ’gX°Æ”9&/XP"wŸKBóæÍñ¼yW¥aÒG.ž7=iÞ¼¹Õâ*¨¾Mû²½ÿvœö9Á d'Ϲ9æzŒ…KëZ2¶¨“±Å”æ¥×ë fß¾}¶Eó$¶???–-[FPù¹Že…ú¨“BSE*ôY¿^xA™pìäß}¯¾šý´‹‹ /tëfáHKއ‡Õ*Uƒk¹væ."®;ð¨ÿ£xxxX;¼I<“ˆccG¸Çâ ñãYøcñh[ÔÉØbJŠó bÙ²eøùùÙ:Í“)VR»¶Ìé»ÛìÙ³m‚&Iц)ooïÂ}¿¬XC†({..Ý»—\€V0çÐŽ48k'€j@Üç®:Ðàd–l[b£ÍÛºƒÔûRÍ—GFFF‘§¢ÈØ¢NÆS…[ÊÉYÌ“ZQº,\#GBf&¸»+w¢;w¶Úåÿý·àMQ ê³ýŸñæö7Á¼ú{ñÐßq6â,·+Ý Â Ô `É–%šX}C!J;I …¥ÇgŸÁ›o*Û*À¦MðøãV¹ô?ÿÀôéðÃÊoKùßoÿcÒ®ITr¯ÄŽÁ;hU­iii„‡‡дiS»˜ËÝ)°óÏ#¥JÊ=óvöÖ\!¤Bä&s ­$5ÕüÇ–eú¨³§BŸôôt®\¹R Glll‘¯S BŸ)Sr’gøõW«$Ï0p 4jË—[6yž¼{rvò\µ\Uvï¦UµV€2‡»víÚÔ®]Û.’g€'Ÿx’û¯Þéùãõ—¯ö{5ÿ @Æuö4¶X‹ª“œÅüPÙ¾ï>سŠ|½‚8u úö…&M”»ÎÊþ^€O>80ó8“ïùßÚñÓöMÀ·¼/{ƒ÷Ò¬j³<Ç„††jÙ7V‚\\\X>{95÷Ö4Y=Àã¬m3Ûòêâ%Ð2¶¨³§±ÅZ¤ˆPä,æI'B+&44”ÀÀ@‚‚‚d%Qª|6iÏšEà=îø ¨WoNž´üê™™0b,Z¤|]«–rçù,{\Nœ€>‚µk!kôÔé”Å÷߇–-!**ŠU«¶è|}út1©xÿ¿­ÿÇW¿ –g-v½´ K¾ ›:{ö,}Gôå²î27=oR.½宕ch÷¡¼ÿæûètæ×»BXž^¯G¯×gç,Rxš?I ­@ZbŠÒ,))‰—›7gåÙ³ªÏ‡:9qð­·?}ºe/l0ÀK/ÁJ¥M4þþ°s'Ô©cÙëÜñçŸ0mlØ“8;8@¯^ðÞ{Ьٽ__FŒŒØ4‚…G•eÜü¼ýØýÒnü¼ýŠr ÊÝÊ»Q£F89IYŽZ y‹y2Z !ŠÅÃÃ_|‘ßfÍâ1•»ÐóêÔañ¤I>_BBG޹÷Aéé0u*•CCi Ê<Š;À×·pÁÀáÃ0uªR˜ÅÑ^|QIœ5²Ìu2™ ûyËŽ/ÀßÇŸ]/í¢–g-Ë\@ƒ*W®LÇŽm†Bš$ÐV"òMEFFâçç'wîAÆ Í¨!#&MbXHýûožý¿étÈÏbŒFªf}´ŸšÊîÌLš>ø Òq°R¥|Ï™œœÌË/O¤ ?†AAí8°;û÷+‰óÖ\31aÀ˜4 ê×7®‚2d¼~0?†ÿ@ÃÊ ÙõÒ.|Ëßû‚˜˜@IDE[ÔÙãØRÒ QQQøû—ž)R–ššŠ«««­ÃÐ4]¬D&䛚3gS¦LÁÛÛÛÖ¡hÊÌ™3íîc3êÛ—}3fðx®YaóŒF–ìÙ£õT® -Zä}4j¤4;¹ÃÉɉ‘o½Eʼn JQ–;‹¦oÝ9Æl?ú(lÞ ^^÷Œ/55•={R¹ti†™wOLÌ,_Þ;sö:;àAðî»P¯žÙŽBIÏL§ßš~¬ý{-ͪ6cçàT-WÕìk³ ¥®"/[ÔÙãØRÒ²Š¥™J^W¯^•f*fÈh+¹D¢,Húè#†¾ÿ>!w¾Þ§Óq¸BÆßºuï:;+It‹Ê]ê-Hoܘ¾:°æÌÔÊÉô@Lýú¼|ì 01..ަM§pñ¢¹_’Y©ºrœ‹ ÃĉPmS3R齪7Ïl UµV켃JîùßMBˆ’&y‹yrZQ|6àñÁ{öÀ¼ºuù6, RR”¥+Nœ€ãÇ•ÿþõ¤¥)¯MO‡“'•Ç÷ßà tõòbƒƒA™™y.e¾/_ž£G ”<…«+ ï¼£,ìQ’ Étéζ³ÛhS£ ÛnÃÛMîš !„ÖI-„(ž'”N"™™Œ(_ž¡ÞÞpå }û*ËÖyxÀO(,ééð÷ߦ‰õ9½/ÅÇÓèyîBopt¤ë‡â\¾|‰¼Ø»·@Óµ dמ]Lûr1 wæ+—¯Ì„Qøôâ§ìŽÚ @»ZíØ<`3ž®ž–¹¨Bˆ% ´•H¡))ôQgW…>W¯*CÀÁ~ ààAÞZ¼˜Ý÷ZyÃÙš7Wƒåì¿t);™v>q‚®»w³áÚ5ž¢€zÀ÷õê2zt‰½¥-,—<=”_þý…k­¯åŒ¶ø}æï¤§¤C'èà×Mý7Qι\¡Ï/E„êdlQgWc‹•H¡:)"4O:ZÉž={F¯×Û:Ínaêì¦[XjªÒ=$:ZùzÆ xáFLšÄ¨3ŠÖ4¥zuxæeÒqH/]¸À÷<Àm`°ÁÍ®#FàììlÉwR"¾Yþ 뮬ãZ›kyoU8AúcéP ]jÄæ›‹”<ƒýu"´[ÔÙÍØbEÒ‰0/½^Opp0{² ¿E¾¤ˆÐ d2¾(• ‚+”í—^‚úþ^<{6•'N¤[J ½ê×'$<¼Ð taŠ{öœÂš5Å«È7Ô{´纜»çqu·×ålèYé¼'„ÐÉ[Ì“;ÐBˆÂ›1#'yn×.§•v xiÔ(¾¯]}1ï>'%Y8°{8sæ IUÌ_0Ñ'‘3gÎX!"!„–¤¹ bqqqìØ±ƒððpÂÃùví 6¤iÓ¦´nÝšÇ{ÌÖ! Q¶­_¯tem·õëó¬ãliÎÎÎt1‚éŸ|ÂþQ£ŠtŽ•+!6ÖÂÝCtt4±®æ/xËýÑÑÑ4hÐÀ Q !„°Í$Ðׯ_çóÏ?gÞ¼y$$$P»vmêÕ«‡¯¯/ÇgÍš5ÄÅÅѲeKÞ}÷]zö쉃ƒýÜ@—"BSRè£NÓ…>Ç+S7ŒF¨P~þªT)ñËxõUt*éîóâÅðúë{ÑéÆP¹rþù¾ÑX¼Â™¸”8~ûÇv% ¥Ïxþ|nûФI“"_OŠÕÉØ¢NÓc‹H¡:)"4O£ËÎ;éÖ­>ú(!!!tìØ•ßpW®\áÛo¿e̘1L›6“'OÚ Ú¢‘N„¦¤[˜:Ív »rEYq#1”ۺ͚YåÒ©©©„……úu ÀÈ‘`4záé¹›µkᡇîýšÂþÒ0bdoÔ^ÿ¹˜µ¯%Å tOä޲¨µšt¨”R‰êÅXòC:ª“±EfÇ’N„ꤡyš("Þ@Û6m-”BX€ä-æiâtóæÍ u¼³³³Ý$ÏB” Æå$ÏC†h>yþâ 7NÙ®T vì€V­Š^C¦ÍÿlfñŸ‹ÙüÏf2ŒÙϹ9¹Ñ³QO^ný2íýÚ£CG{Ÿö¼ýÉÛœ«²n4_‚ºgêòñ„%yB;¥‰únñññ”+W.{þZ||ªÂÚ>úH™®¨Ì‹Ð°O>·ÞR¶«T;•~-wËÌÌ$))‰òèhy3’%Ç–°üør.'\Îó\‹ûZðrë—Ð|Ý*æy®wPo:>Þ‘VÿÀŽÐt ìÄ€ÏàããS´7(„Âæ4—@ÿ÷ßøùùE­ZµHHH nݺ$%%‘ššÊ{ï½Ç´iÓlf¡I¡))ôQ§©BŸµkaòde»nÝ_q#?-ôùßÿr¹ï>øõW¸»Foç®Lút73n’ªKÅ[çMëû[3wÖ\Ê•Ëih’lHfí_kYüçbö߇‘œÙnž®žôkÚ—[¿ÌCÕï=/ÄÇLJѯŽfô«–ïž(E„êdlQ§©±E#¤ˆPš§¹e,þüóO5jD­ZµX¾|9®®®\ºt‰•+WòÓO?Ù8¢‘"BSÒ-Lfº…ýù' œ³âÆÆ`£D­ ݦNÍIž}}aÏÓäyì{céÿI=tˆÈG#ùï‘ÿkÆŠŒ´zª—/_æØ•cŒÚ<ŠêŸUgÐúAì=¿7;ynW«K»-åòøË,xnÙ乤I'Bu2¶¨ÓÌØ¢!Ò‰Pä,æi¢ˆ0·ï¿ÿžE‹ñÛo¿ЦMyä¾üòKÒÒÒðòòâÌ™3Ù ¶=ÉøÂî\¾ pñ¢²âÆÏ?óÏÚ:ª|½ÿ¾2Ó fMص x ï1ûB÷Ñû½¹öð5õ“܆ò{Ë“ð\ÞÄ«£?Ž’ IDATŠG·ÌË­_¦ae¹{'„(ý$o1OsŸoµiӆѣG“‘‘Att4‡æ‹/¾ÀÑÑ‘ŒŒ ¹³ DIJN†nÝ”ä”IÅNž'N„¬kµkÃîÝpÿý¦ÇM˜>kù$Ï Á'¢Á¡ŽîïÄË­_¦[Ãn8;­û¡BˆÒIs tƒ ðññ¡y󿤧§óÐCÑ®];€ìuŸëÔ©cË‹$44”àà`‚‚‚¤RhÛ!pø°²=lXÎrôæ›ðÙgʶŸŸ’<ûù©{#ù8š9¡tpèÀòÿ[Nm/YUQ¶èõzôz=¡¡¡Ú:MÓÜh€cÇŽ1hÐ ºwïΪU«²÷>|˜aÆáááaÃèŠ& €eË–IòœKdd$ƒÁÖahNDD„í.>u*dÕ<þ8ÌŸo»Xr1 DFæ]ky̘œä¹^=Ø·/ÿä9-- ƒ®ßkîÐÀ£Ý$Ï111Ù…„"‡Œ-êl:¶h”ÚØR–±lÙ2lŠæiî4€——ï¼óŽÉþW^yÅÑX†LÈ7%ÝÂÔÙ¬[ØêÕ0eв}ÿýÊ Eh]\ñññ4hð8îî9 7gf¦wŸ6ܸ·o§Õ©_ÿví‚5ò?§‹‹ žŽžf¯ít͉Ž=;÷-Xt"T'c‹:éDhJ:ª“N„æi¢ˆðöíÛT¨P¡P¯¹uëžžæ!jLÆšwäˆrÇ99<=aÿ~hÜØ&¡ÄÅÅÑ´é.^4÷ -Ž ¦púôl|}ÍŸwÂøâÂdÔÎP?À~;ü8öË1I¼„ešä-æib ÇúõëiÖ¬K–,1»^òßÿÍÈ‘#©W¯ž•¢¢”»tI)LNGG ±Yò\XíÛS äÀýqw2þÈ€*OÁçfŒ›!ɳB³41…cÀ€èt:¦OŸÎ»ï¾Ëã?Ž¿¿?õêÕ£bÅŠœ={–3gÎΑ#GèÕ«»wï¶uØBØ¿¬7.]R¾þôSèÚÕ¶1BA×ùŸ{x.ÓB§A7ðØëA9ïrÄ܃±¼÷kîÔ¸Rƒéc¦Ó§GŸ’ X!D© ‰ÚÑÑ‘Aƒ1`À~úé'Ö¬Yúuë8{ö,T¨P&Mšðàƒ²lÙ2»ì¤$MI·0uVëf4ÂK/)Ó7^~Y©ÌÓ$¾[XHxoly€ª•«úK(×2ømÿoD]Œ¢CPiûHZzkt"T'c‹:éDhJ:ª“N„æib GúõëÇÚµk9}ú4 \¸p[·n±ÿ~æÎk·?üRDhJº…©³Z·°)S”ÂAPæBÌ›gë€ieFPønaÛÏngðúÁd3ñtõdëÀ­<àó 6døáLo:žêd—É3H'ÂüÈØ¢N:š’N„ê$g1OE„¥LÆšóÓOз¯²]¯:>>¶ 8yfÏŽcÙ²)æ‹{öœÂš5êǼxŽË;’˜žˆ›“[n¥}ö–Z!JÉ[̓Ϸ„(kVš¥xyÁ¦M6MžÓÒ”óæÍKÝLý;æožýáYÓqÔ9Ò+D’g!„# ´¥L·öí©píN:铜;‡Á`à¶NdžŸ~‚bL‹ÊÈÈàpV×B3|}}ótýï?X¸/†ÜŸ:9)ËO''-¦èøhžþþi¥ó ðÍ ßЭA·¢L!„P! ´•H¡))ôQWÜBŸAÇ“0bÁ÷˜º (?p tî\ä뀲†û /L"5µ÷=3“éÚõÝfqMš4‰¿þú‹·ß~›µk×2OCË~ ‘[Ïø¹fM wí7??ð=¶EX4isç*Ó3æÎµLòœiÌdàºÙÉsPà =¿¨ø'B!ò¡É)}úôáСCôíÛ—¸¸¸ìýõë×gìØ±¼õÖ[8;;[=®;v°yófjÔ¨ÁÔ©S™7o£F²z¢ô˜÷É'ܺrÅìqžÕª1r‚™é·oþ}°kº]»èú4+€à\‡¬ðð ÿ¤IúäÄÒöîµüyGmÅê¿”õ¬;øu ¤WŽ:GË_H!„¸Cs ôÑ£GÙ¼y3ÇŽ£eË–üúë¯ÙϵnÝšøøx.\¸@ݺu­W||</^¤F4mÚ”ýû÷øõRDhJ } :"‚—.¥Y®™Tÿ÷ç:&L§ãè•B¸”øýwصKy9¢¬²qGÏ;(?èàçºuY;`@‰¼sªT)úkó+ôy÷û,8²€Ö¾­ÙÐw®Že§ðEŠÕÉØ¢NŠMI¡:)"4OsS8"""¨_¿>-[¶”î„YÒÓÓqrr*r‡©´´4ÒÒÒîyLbb"§OŸ&333Ïþëׯãéé™ýµ··7±±± wH®NŠMI·0xóãÙ\»6 !û±*×vC`síÚ¼ùñÇžü}O> ÞÞÊrÿû8“<;;C»vèÞŸþï¾ËŠ;]öV”/OÿwÞ±ÉÝçâRëöÕÁ¯øhßG<àó[lÁÓÕSí奖t"T'c‹:éDhJ:ª“œÅ<Í%Ð~~~œ={–k×®yè 6‘‘A½zõ |¾'NЫW/üüüpuuåé§ŸV=.--AƒáååEÆ ©T©«³Úþþþ$&&’””ÀßÿMË–- |‡CªYMÍž=»ÌWÉW®\ßNË•Ô.Ëõ|˜N‡oµjTìÞ YŸj88ÀCÁ„ °e ÄÆ*I¦N¥çGñs:¤?שCOÝ}.£ÑÈõë×éÝ»7·oß`eØJÆl@õ ÕÙ>h;UËUµe˜6$+p¨±E„™òöö–8THÎbžæ>ßjݺ5uêÔá7Þ`Ú´ièt: ëÖ­cÆŒtïÞŸïÊ•+dff2tèPÖ¬Y“ïqãÆcÓ¦MìÞ½›–-[2kÖ,úöíK:uhÓFY:ë‘GaÛ¶mtïÞ­[·òÈ#ûý 1nÆ &ìØÁÒóçMžûÜh䓃M_Ô´©rúÉ'•uàòIt:ýßy‡ ×^ãåºûœ{-gK2|<ûc¾Yû ‰IvMÆ+΋ º œiv£—w¶ÚŽŸ·_É!„B¨Ð\íîîNHH½zõ¢Aƒ899Ñ¥KiÕªU¡?jéܹ3ï4‹øã?HII19æöíÛ,_¾œ×_Ç{ €iÓ¦±dÉæÎ›@O˜0矞3fpíÚ5¶mÛVÌw+TvuÅ·NÂΟ§Y®ýa€/PÀß?'a~â ¨Zð»­= @¿~}‰Ü}NO‡7®ÌYøÓƒ±‹]$tÊyí-nA"°ÜžtcÓ[›hRÅKy!„… ¹)DDD°bÅ &L˜À¸qãX½z5‡Æ×××â×;xð 4mÚ4ÏþæÍ›ç)bl×®W¯^å§Ÿ~âßÿ¥Aƒ¾Æ¡C‡øî»ïÐëõyYE@YÊÒ¾ÈÈHÖ®]«‰X¬¾/#¶n…þýÑW®Lð¾}|~ç˜@üÏÃq_ ÑÑðÏ?è»v%¦cÇ<ÉsA®«Óéè1`7nܰøûøàÒÓ;Ç©_9“'‡òñÇdzƒgí‹dôè>¾Æò•Ëùõܯ$Ô¸“—°~Çó“Ÿ_¾©ø©Jú…†BµjðÒK°sgNòÜ LŸQQJRœ»S•‡‡¯¼ò óçÏgãÆ\¸p1cÆËÈ‘#-‡tõ1eÝÂú ÎOuê˜Ü…6?ôpqQæ8¬_—/Ü9pg)Ä‚Xµj•#¶œ_~©S•íF`éRË_#ØÁ¨Í£Ð»ê! t‘w>ýÉ*"Hê{«³øãÅe¾Õ¬t"Tgc‹5H'BSÒ‰Pä,æi²ˆðÓO?eïÞ½¼ñÆtèÐggç"Ÿë¿ÿþËw¥‡ÐÐPüýýeŽòرcY¾|9·nÝÂÏÏùóç[ätpp0çÎcĈ4mÚÔd¹ÂÉ~ºÿ~V9£TÕ•2IIÊ õ¬òƒåË-Ÿ<_M¼Ês+ŸãÈ¥#T`SÿMT-W•ÔÔTNž<ÉÕkWiÙ¢%5kÖ´ìÅ…Bàææ†··7...¶Eó4—@ÇÅÅ@Ó¦M2dõêÕ+‘öÃùqpp°xò P£F ‹Í§¶å¸hÝXôAYy£û„ ¥¶bù•WàäIeûw”U÷,éyæ‡gˆŠ‹à…/ðcÏñpöÀÕÕµì­.„6àïï¿¿?!!!¶Eó4—@ïÚµ ¶oß^¬µ—µ&55ÕÖ!hNdd$~~~öµ¬ß™3ðæ›ôz;9ÑÃ` ¤vmVn±KDDDаaC‹¯8¾þZYÂà©§à£,{þ½ç÷Ò=¤;±)±Œ ÅW]¿ÂA—·<Ã`0•=åJ(² µZtj+v9¶X–Æ­±E]jjj™¯11GsE„µjÕÂÍÍ ÷|V:°WçÎ#""B*æs±»BƒAYÃ-) GŽ£GÓÏÝcÆXôî³V }~ÿÆW¶k׆´ì •ÃäéïŸ&6%:>éô sž™c’<ƒúäGŠÕÙÝØb%Z[´DÆ–¼bbbˆˆˆàܹs¶Eó4WD˜‘‘Á³Ï>ËO<Á¸qãŠU@¨ÁÁÁ„……Ñ»wo ´uH¢(&O†iÓ”íqãȘ5‹;³nÛ¶R7}ãÊhÝZYDÄÕBC•"BK™:ƒI¿Nˆ7'7¾ëþ½÷¶Ü„BZÖå«W¯¦Y³fRDxšK ³V«Ø¶m^^^´k×Î$‰Öëõ6Š®h¤%f)°?<ödd@³fÊ2®®¤¥¥•ºb ƒžxBIš/†aÃ,sî c#É¢£‹ðq÷aCß Ö–?*…B+$o1O“ÄÜÜÜèÖ­›­ÃB‘ƒ)ɳ«+¬X¡üJ]ò Ê´¬äyøpË%Ï i ôY݇-‘[¸¿âýl°…ú•ê[æB!„•h.®[·®ÝÝa.)"4e7…>cÆ( !ƒRE×¼y‰^Ζ…>+WÂW_)ÛJ¡%\N¸Ì³?<˱+ÇhS£ ûm¤j¹ªz½ú¨“"Buv3¶X™š’±Eš§¹"ÂÒ*,,ŒÂÃÃmŠfØE¡Ï† °d‰²Ý¡ŒWâ—´U¡OX˜rÇ JX»6ûF{ü÷ßtÜV[Ѩc#Ú=ߎ¯~Å©ë§h»¸mvòÜ­A7v¿´»ÀÉ3H¡O~¤ˆP]Œ-6 E„¦dlÉ+<<œÂÂÂlŠæibôüÁªU«6l>>>|òÉ'÷<~öìÙVŠÌ2‚ƒƒ¹xñ"ãÇÏ^cQØ«W•ùÎׯƒ——²ríÚ¶ŽªDÄÅ)E‚gÏ*+mlßO>Yð×/ûq,ø€è€h¨³ßý”;égÒ1t5€¼Þæu¾ìò¥êJB!l+22’ÈÈH>ûì3jÔ¨!s ïAŸo]½z•УGÜÜÜ8pà€­C²8i¤b‡† S’g€¹sKmòl4*S¼³f©Ì˜Q¸ä922’÷½Ï…'/˜<—Ü$ª¿Âg_~ƸGJþ¾Bˆ¢‘F*§‰º{÷¸ðÀàëë[*hag,€_~Q¶_| °m<%hÚ4Ø´IÙîÙ&L(Üë_yë.´1Mž³UÏŠžt*ß©èA !„¢™ÏQ ÀÁƒmF‰‘"BS‘‘‘ [‡aêÌ™œ"5jÀüùV½|DD„Õ®µe |ø¡²Ý¨åÓºKñ—ÀãÞÇܪq‹í{¶þäw "##‹üúÒ*&&Fš3©ÐìØbcÖ[ì…Œ-ê$g1O3 tiwõêU[‡ 9š,ôÉÕmNÉ(­ÜRÞZ…>çÎ)7Ö33¡BX·Ê—/Ü9RSSI6&›?ÐþŠü«h"…>ù‘"Buš[4@ŠMÉØ¢Nró41…£,ˆŽŽ&88˜   ‚‚‚lŽ&h²tÚ4¥I Àÿý<õ”ÕC°FÑFr2ôè±±Yׄ¢¬nåêêJÇ fs¸æÀc+üîðööÖæ÷‹ÉX¢N¾WÔIA˜)[òÒëõèõz¢££©]Jë~,ES ôÆ‰ŠŠ2{ܘ1cJ> ”ÁKë€éÓ•í&M”jºRêÕWáøqeûí·•dº¨ZÔkÁ_×þÂX5ÿ}j\ªA§'e´BhYÖM¾¬N„"šJ ¿ýöÛg ´Ð¸„eêFF¸¸(ÝÝÜlU‰˜;¾ÿ^ÙîØ1ço†¢1~!A!»AeÝh^êø5jÔ(Þ…„BÐT½bÅ ž}öY[‡Q"dB¾)Mu ;6g·iÓ eK›…RÜna;àÚ5gÕç’’”¹Ïnné„„ü€£c‘/ET\½îMæ™°œ[:“^3ÜkPýluúôáÉý"H·°üH'Buš[4D:š’±Et"4OSE„åÊ•ÃÛÛÛìÃÉ„|Sš)ôùùgX¼XÙ~üqxóM›†SÜBŸ7œ _¦úø÷ßeË€eÔªåLqò®Ø”XºþЕ+ W  |µô+?·˜¾ñ}éxª#kLdל]|ñ¿/pp(ÞP#…>꤈PfÆ‘"BS2¶¨“œÅîô±£B!¬G3 ôùóçKíügP>j F¯×Û:‘eáœnƒ½zÁàÁ¶ÇNLØ>U§VоN{–-C‡ÎÆQ !„(.½^Opp°L +Í$Ð¥]@@@©¢RT6íöÏ?0nœ²]½º’Lk„–»…}}èk>Ûÿ«4FßW«cÉšH·0uÒ‰Pt"T§å±ÅVdlÉ+((ˆeË–`ëP4Oh+‘ ù¦lVèsw·Á¥KÁÇÇúq䣸…> ä.ë#Ö3f«²„¤oy_¶ Ø‚·›uŠz¥ÐGª“"BuRDhJÆu’³˜§™"ÂÒL&ãkÌÀÔ©ÊöèÑðÕW¶ÇBŒFe1‘‘#ƒ1–™=¾0E„û/ì§ãòŽ$’©àR}CöѲší–úBQr$o1OÉeKînƒÁÇ¥£øíèQ9²ü¹ÿ¹ù/üøɆdœœXÝgµ$ÏB!Ê4™Â!ÊŽÄD4H™ãàì¬ttw·uTÅ«$ÎmÚä$Ï–\ÌæzÒuº®èJL’2ÏvÑó‹è\¯³å. „BØ!¹m%҉Дջ… YÅ"~­[[纅TnaF£2uûí·!«†ÌÉ ÆŒðpg®] 6{êÕïi'¥'ñüÊç9«thœÒa CZ)Ð{°4é¦N:ª“N„ꤡ)[ÔI'Bódt±™ojΜ9L™2Å:Ý%7n„o¾Q¶•ÌS£fΜyÏygÇŽÁ¨Q°ξ`î\hÜà›bÇaÌ ßÚ~¼x€¡­†òAûŠ}Þ¢Ê*ô™={¶ÍbТ¬BYÝ'/«Ž-vÄÜØRÉØ¢îêÕ«Ô®]ÛÖahšZLÆ·±k×”nƒ×®A… pâÔ­kë¨8}ú4ÉÉÉfswwç¾ûðÞ{°`AÎJ¾¾ðé§Ð¿¿eãµyóÏ s½Îlê¿ 'ù{[!ÊÉ[̓߈V’ÕH%((HîYÛ°aJò ÊŠIžºwÇ•+Ìçá—ì·á䯿®ÌDñô´lL³~Ÿ•<·ªÖŠ5}ÖHò,„e€^¯G¯×J`` ­ÃÑ4ù­h%ò—œ-,Z›6)Û=zÀ¿ªµÂÅ¥ ±±cÌ{<{;0æÍSnª[Úá?òÎÎw¨íU›_üBy—ò–¿BÍɺɬ±ß•Z$«pX‰š*ñna¹» úú*É´]Pïvß}°|9üö[É$Ï{Ïï%XŒ#ÞnÞl°ßò¾–¿PH·0uÒ‰Pt"T'MÉØ¢Nróä´•”õ"ÂÞ{ß¿ûŽûr-wèúuZVª„‹CÎßq&'³÷Ä *V¬höœ±±±ùÿ з¯²tPiÑ"œ+U*Þ›°š™À²<{*W†Ó§ÁË«d®ø×õ¿ "-# WGWô}õ4®Ò¸d.VRè£NŠÕI¡:)"4%c‹:)"4OŠ­@&ãÃåË—™üðÃ|óßùsøfà@>ýþûóÓiÓØ3{6MÝÜLŸ¼}[yëÝÝ9ƒ‡‡GQB/Q-[sâÄ2³Ç¦k`a]N¸LÛÅm‰ŽF‡Ž•=WÒ·iß¹–B퓼Å<¹-¬Â×ׯöí9½b ò9æÓZµ˜:kVÏ9rüxŽ-_Î̳gó=&T§£ÊèÑšLžµàvÚmžùá¢ã£ø¸ÓÇ’< !„fÈh?ãÅ7 IDATa5ãgÍâÓZµTŸ; xµo¯oÁçÜzxxÐúÅ ½G³„ù5j0âÛ­_¬e†L½Wõæø¥@ñõ6¯3áÑ 6ŽJ!„Ð>¹m%2!ßô.t$à‡òMø©›S¯^…_¥Å¶‡‡ú#×s#žy†áßO ÊÔPZl‡wŸ#€’ïöÊÆWØvvA ƒø²Ë—%~Í¢’naꤡ:éD¨N:š’±Et"4OF+)ëE„YÆÏšÅäÍ›ùææMæS€«€WJ ¾;vú|@k ¸{ÅÊùuëòͤIÅŒØL‹-íý²ôøRÚÖlËÊž+qÐi÷))ôQ'E„꤈Pš’±Eš' ´•È7"žŽïGáuó&§¬áj‚›S›4QVÎHJRÉÉÊSRÌžv0œ¼ t¨£#­_|QówŸ †dàø]{ǘìSŽ+¼ôôtNœ8Áñðã´lÚ’-Z°âÔ ¦ì™€¿?ûmÄÝÉýÞ'²1oooù§Bguò½¢N’gS2¶¨“œÅ,] ®®øúú’T»6¯¾ÿ¾Å.9bÒ$Þ®\Ù.î>ÇÆÂ;ï(ÛuêÀĉ9ÏÍœ9³Xçž9g&—š^º÷õŒeÆ×3ŠukÊ*ôy…††fŠsæÌ!!!ÁÖahNqÇ–ÒHÆ–¼ôz=ÁÁÁìÙ³ÇÖ¡hžt"´‚2×ÑçúuèÝöîU¾®V Ö¬víò–™™‰ƒƒeÿ†ûîÛoéÕ·¯æè‘#•¿'Ö¯KÖ‚µéÚ†Ãm›=.à@‡¶²Ü……B” e.o)™-,ëèQèÞ²ÖenÓÖ­ƒ5Lµtò 0xèP‹ŸÓÒŽ…… •í®]-›ßÿ9'ÓN 3Ç@³Í,wq!„¢ ‘ÚJJõ„|ƒÆŽ…Áƒsæ;Ï™K–(Yb>Êb¡Ï·ßÂÁƒÊö[o)K×Ý­°…>Ɇd>ßÿ9÷y?ã·'¹y2üvï×Ô>Q›ÆÛO‹s)ôQ'E„êÊâØRRDhJÆu¥:g±I ­¤Ôv"Œ‰§Ÿ†¬…è«V…_Un³šQÖ }nÞÌ)ôóË[8˜[A }²纳ë2~ûx®&*ßcÍü›1¼çpª…Vƒô»^”Õ~«Æ{/¿GõêÕ‹öFl@ }ÔI¡º²6¶”š’±E]©ÍY,HŠ­ÀÞ&ã'&&’ž~wæ¥âäI* ŒãùóÊ×Ê|çš5K6@;5b,X lëõЭ[ÑΓlHfÁ‘|úqvÒ Ð´jS&·ŸL¯Æ½Ð¡ãБC¼6ñ5ndÞ ±B"ån—£’C%ÌX@›‡ÚXà !„(ì-o±©"&fÏœÉoóçÓÐÍ-ÿƒ’’ø9.ŽF#@Y®nþ|¸×kʰ£GaÑ"eû™gL“猌 >ó)[vo!>9/w/º>Ñ•7_GGG 'qžõû,®$\É~mã*ù ýônÒºìýmjß;þ$11‘³gÏR¯^=Ê™iH#„Bó$&FO˜À_+W2ûßó=æP¨à䟣G[->{c4*ËÖåW8xîÜ9ºíÎé:§Ii‘¢L¬Ê„ƒÇòãS?òã7?²5f«Iâܨr#&·ŸLŸ&}pÐå?«\¹r4oÞ¼„ÞBQöÈh+±§ ùžžž4îÖ÷Xík`tåʰsg‘“ç²Rè³d º³ÜòÛoçm×™™I¯á½8ñð RêÞIžcH©›Â‰‡OТ{ Æm—<7¬Ü•=W>2œ¾MûÞ3y.-¤ÐGª++cKaI¡)[ÔÙSÎb+¥ÿ7¯FØÛ„üÑS¦ðµŸŸês€Æ÷݇çŸBûöE¾FY(ôÉ]8X·nÎv–¯~Å_Õÿ÷\;sׄ¹Cz³t8 *5`EœyŠ~Mû•‰Ä9‹ú¨“"Bueal) )"4%c‹:{ËYlAŠ­À^'ãO7ŽŽ_~IÛÌÌ<û”/Ïü³gñ¬ZÕF‘Ù×^Ëiš²a¼ðBÞç;õêÄÎF;ï=™ÊM7áÄ–8êK,V!„ì7o±¦²s KšÚ]è:‡—乎o¾Q¶Ÿ}Ö4yˆMŒ5_‰àn:7Iž…BºÖ­[Ç!Cxâ‰'ˆŽŽ¶u8%.{.t®}_×­Ëè)Sl’ÝÈÌÌ)tsË¿ã`¥ •L×j¾[úã„B¡ ’@BJJ ݺuãôéÓ$%%êµö:!ôûïóµNYí€NGãnÝðôô´È¹Ks¡Ï’%pø°²ýöÛpÿýêÇuïÒrQw--wWMX¹¨rtïÒÝòAÚ)ôQ'E„êJóØRRDhJÆuöš³X“$Ѕп‚‚‚pqq)ôkíuB¾çÅ‹469|]¥ŠEï>—ÖBŸ7rº ªæöêW©u±Üε3wMØmhv£¯yµ$BµRè£NŠÕ•Ö±¥¸¤ˆÐ”Œ-êì5g±&YÚJj×®mëŠæèQF]gºvµØÝg€ÙYí¿K™wßU’h€/¿¼wo™«‰W‰y<ÖƒÎ_‡±‘‚€4(YŽæ·›³né:t:]þ')¼½½Kí÷KqÙ:M’ïuRfJÆuv›³XQ©M F#QQQ¤¦¦Ò°aÃ|‹ŽŽ&<<œx€x {||<ëׯÀßߟÀÀÀY“ŽÁìêJ?dÌ:|/V¶Ÿ{ž>ÿc3™ \7‡è Ã]‡s9ü2Wc¯r_Åûèþ|w† bÀ…BQ`¥.Ž'((ˆ?ÿü“[·nJ2}·ÌÌLFŽÉÂ… ©U«.\à¹çžcÕªU¸¹¹‘™™I\\@¡ç;—*Gðê÷·ƒÑ¶ÌL5*§pðË/ï}üÌЙüzîW†´ÂÂn ­¥B!Š«ÔÍÎÌ̤~ýú|úé§¼újþóF.\ÈÒ¥KÙµkÑÑÑ„……ñÛo¿ñÁP±bEÆŒØ1cxúé§‹—]NÈÏÈ€ãǕ퇲øéK[¡ÏâÅ9…ƒï¼“á ÀÿýÁ{”﵆•2癜9xRècJ }ÔI¡ºÒ6¶XŠŒ-¦dlQg—9‹••ººbÅŠ,\¸áÇã—O'=PèGy„'žx€&MšÐ½{w–.]JzºúºbS§NÅÏÏÔÔT:wîLÇŽ —]NÈÿë/HNV¶|Ðâ§/M…>¹ ï¿_Yy#?±)±ô[ÛC¦7'7~êõÎÙÏK¡))ôQ'E„êJÓØbI2¶˜’±E]æ,VVêè‚HIIáĉ4iÒ$ÏþfÍšqýúuΞ=«úºÉ“'ÅåË—9þ<¿þúk¯Ãwß}‡^¯Ïó¸ûö-Z”³¢Ú;Ж¼ÆìٳٳgvÞ¯™}aaa8p€Y³f±uëV8ýxæ™Yܼ¹8Àرa¸¹å¾A+¯¬#þÙÓŸñïþó·lÙ2M¼_-íË*ôÑB,ZÚDPP&bÑÒ¾Ù³gãíí­‰X´´/«ˆP ±he_î"B[ÇbË}Y9Éĉùî»ï敺9ÐqéÒ%Õ:ëù{–YHxzB®˲êÅßåâÅg1Îáèè€NWPfº$&žpr*ϼy¿ðúëUϱ%r ¿\ÿ< G£Œ ‰þ¢ÞŠïB!„Å¥3ªUØ•3gÎdâĉ&E„ǧU«V¬^½š^½zeï§Y³f¬_¿Þ¢ËCÙmOù¶máàAèÐvï¶u46ײe0'N,3{\‹Á?nzÜÉ«'yxñäR¨íU›ã¯§¢[EË*„BƒÝæ-VT&§pT«V €3gÎäÙÿ÷ßçyÞ’’’’HII±ŸÂƒNœP¶K`þ3”­BŸÄôD^\ó")†œœø±çù&ÏRècJ }ÔI¡º²4¶†Œ-¦dlÉË`0’’R¶W+ 2™@W­ZN:•gXX5kÖ´ø5ÿýwÆŒÃÖ­[-~îqꤤ(Û%°”­BŸÑ›G£üòú°Ã‡}úΩS§²»¼uéÒ…›7orèÐ!‹Æa—…,Y/¿¬lŸ9#s )úŽ•a+°nëvdû í8èÊäß®B!ì€]æ-VV*‹çÏŸO||<{÷îrþêîÒ¥ -[¶`üøñ´oßžaÆ1pà@¶mÛÆŽ;X½zµÍâÖ”; TðòÛÆbÇ"oFòڦרZ®*+z¬äY!„°s¥ò7ùöíÛÑëõÄÆÆòðÃg/Ïrîܹìc~øa¶nÝÊÙ³géÑ£{÷î%$$„=z”HLçÎ#$$„ððð9¿Å9¢ü·uk¸s‡^NZF}×ôåvÚmtèø®ûwT+oùùõB!„%„‡‡’'_êJåèõë×è¸:dߥ.i:oooÜÜܬr½bIO‡“'•íšÿ J¡ŸŸNNöñm[¸ãßÙùG/+wòß|ôM:×ë\ ×EDDÈ2Šw1 DEEá/Ÿ†ä‘U@X¹reG¢-ö6¶X‹Œ-¦dlÉËÍÍ ooïì©­"¥ò´988Ð¥Kûø! ‡¬6ž%´ØO¡Ñ“'CttÁ_óË?¿ðÅ/hS£ Ó;N/ðk¥ÐÇ”ú¨“"Buö2¶X›Œ-¦dlÉËßߟ.]ºàà é¡9¥ºˆP+ìn2þâÅ0|¸² õêÙ6JI—^‚U«úã̾¦þCÓ¹Ùç1I1x¹zqìµcÔõ®[Ò¡ !„awy‹ Èç[VODD•+WÖþÇ­Y󟽽Ëtò|å tëY‹²Üw_ :N•*ù¿Æˆ‘åœ#&Iùh}Ñó‹$yBa²Ö–ÇËËËÖáhš$ÐV^¯'00ÀÀ@[‡soY+p”àô ­;yž{þûOùº}{X»¶•*ÝûuS÷Nåï=ÊúâÃ[§O“>%©Ba„††M³fÍlަÉ$+©_¿>ï¼óŽö“ç´4«‚v»…mÚíÚå$ÏC†Àöí˜Mž÷ßÇÔ½ShR¥ _vý²H×—na¦¤[˜:éD¨N«c‹­ÉØbJÆ–¼yçw¨_¿¾­CÑÿ\™¶‘ ¬Þ÷ñÇðí·àâbzlll,‡"66–É7°nÆ ÜÜù©÷O¸;¹))ô1%…>꤈PÇ-±Å”Œ-êì&g±!)"´»šŒ¿h¼úª²ýï¿P·lÌß5`Ô(åí”++V@P鱿ïÿ×&¾FœK (»<ñ×ã¹ýÐm¨ Ÿ[È+¾bÝ7 „BXˆ]å-6"s ­$«‘JÓ¦MiÚ´©­ÃÉ_ÖügŸ2“<ÇÆB¯^°k—òu°q#´jezì¼%ó˜ºz*W¯‚³²/Ž8HvCK§–’< !„°Kááᄇ‡sîÜ9ê–‘ ¨d ‡•¸¸¸ØG#•¬8ÊHad$<òHNòüàƒÊªjÉóÿýÇÇß}ÌÕGr’çlÎÀÓs*†èÂ,-„BhDV#µy‹"I ­¤råÊÚo¤’–¦Ì«$ж.ôÙ»~NŸV¾îÙöíƒêÕÕŸúùT¢[Ü;9¾ÐêÓ¾˜V¬¸¤ÐÇ”ú¨“"Bu¶[´JÆS2¶ä•ÕHEóËíj€$ÐVbòOžÌ) ,á8À¶…>ß~ :ÁÍ›Ê×'ÂêÕàá‘ÿkNýs ̬Ä„Ÿ /VlRècJ }ÔI¡:)"T'c‹)[ÔÙEÎbcRDhv3áBxí5eûÜ9ðó³i8%!3SI–gÍR¾vqo¾ÁƒÍ¿¶í³m9Øæ ÙãÚlÃÁÍæB!´Ènò’;Ð"GÖüçJ•Jeòœ˜¨LÓÈJž+W†_-Xò P³zM¸mæ [PßOÖÏB!J3I ­$44”àà`ôz½­CÉ_)î@xñ"<ödýó7jBAûÚ¬ [ÉŽ ;`ß½«~²:FL(^°B!„ èõz‚ƒƒejXÈ2vV íBRSs ­0ÿ”B???œœJöÛðèQxá¸tIùúé§aÕ*ðò2ÿÚëI×±ikÿ^ ^@Ep9âBÚƒi Ë{¬÷ oúµíGófÍ‹oDD 6,Ö9ì]XXK–,Éþ:33“[·náíímè´'99w÷¢5í)­âââðôôÄáÿÙ;ï°(®¯¿ Ò¥ˆ(`Ebì6Ä^Ñ ¢±F-‰‰%‰%‰¿h,Qc×X¢Q°‚Šb{,`ìˆ ˆ(¶(ˆ…~¿?6»²²ÀÒQç}žyX÷Ìî0œ=sŠ–ä#ÊÈóçÏ)S¦Lq«Q¢øXï-7¦ÿþ™ÆÝÝÝqww§_¿~Å Õû…d@%> ÿòeHI‘¿."ô’%K˜1cFžo\ß}7—½{¯ “ig)ÑÑ÷IOß óŰhhb³û‡ú3"`_=ÀÒÈ’U¿¯âéù§ü²òâÊÅñÌðæ¯Í1{bÆw#¾cˆ×,ì>W®,r2À¿D[; `ìØœçKŒc\à86^Ù¨ë]«7˺,ÃÂÐÀËÓ‹ëׯsóÖMj:Ô¤víÚèè¼[:o|ìÆ³##£’ò$!!!ñ¢HÌŽo³”$ZBŽ"°\9øÀþpd2ðõ•w̉ºk(÷î`n`ÎÒÎKñ¬ã©"§££ƒ³³3ÎÎÎ…¡²„„„„„„D F2 %ä|À „åÊAÛ¶Ù˼L~É„ƒX¼J9Ö¥FVw_uiëBÖPBBBBBBâ}B œ+"’’’Š[…¬IL„ë×寋Ѐ.ªna9EV¿{§åNJãÙXט?ºÿA@ÿ€b1ž¥naÅI‰¶YJ’]D•Ü2v—/ƒÂ-¢øg(þnaoRßðÕ¯p]ïJd\$®¶®\ýâ*Cë-6½¤naÅ¢Œ]PPPq«Râ‘B8Šˆ:”Üä0Eü3©:¿I„ùé¡yîþ9¼ý¼¹õï- J0»ílÆ6‹ìÝútEL‰½N$$$$$>heì4I4üØ‘<ÐoãŸË—‡Ê•‹W 9{ŽÍýqÉiÉ|wä;\Ö¸(禕šòÏÈ×d\±Ïïqqq¬X±‚þùG#ù°nݺBÖ*g^¿~Mrrrq«¡BBBNmNßòüùó" “È?ééé<þ\Yr2'^¾|©¬ï.Q²‘ h‰·è" ßÈ+Ã!àâ¹-ÛùÏÃh´º³NÍ"M¤¡«­Ë,·Yœr û²Rûí÷‰´´45ÚDU!4^#--­€ÏPÎŒ3pttTnNNNtëÖ™3gòâÅ‹\Ï×¢E •ù²Û4‰Åøð!£Fbÿþý­¿|ùr¾úê«\ë]:t///¬¬¬022ÂÀÀ;;;/^¬±1š––ÆöíÛ™2e :t fÍš¸¹¹åY'Ú·oµµ5&&&áììÌÂ… yýúµŠìhÑ¢FFF˜››£««K¥J•èß¿?7nÜPÊ=}úGGGÆŒ“g½$òÏæÍ›qvvÆÀÀsssttt¨V­C† ᢫ׄ……Ñ¿,,,066ÆÐÐSSSÚ·oÏÎ;Ud{öì©ü­Y³&Í›7ÇÓÓ“o¿ýVã¿C‰‚A á("Jl@þ›7 ¸ùqŽÜt"LKƒåËaêÔ·†³¶¶|ü-1 a 8zòa­tæŸÇœssHI—7‹q¶rfCÏ Ô-_· O©@:æÌ¢E«™3ç ººå³•KL¼ÁîÝshÚ´i®×¸|ù2íÛF_?ûk$9ù_FvaêÔ/s½FN<|ø[·náááAÙ²e‰'00€€¥ç×ÂÂBãùj×®­Ò0âñãÇ;vŒš5kâä¤ÚAóCën8uêTBCCéÚµ+µjÕ"::š;v0nÜ8BCCYºtiŽs$$$лwolmm¹ÿ~ž¼…/^¼`È!ìØ±<<ùä“|CRRzzzùšãƒGH:ÞÞÞÂÕÕµ¸ÕPÏ™3BÈÉ…ص«H—?~¼xþüyŽr§N Q¯Þ[5A!Fž#œœ>öÝ„¾u¡ÓÌPÈúÊ„v'¡[£´°¨\SØ»ôãL3Ì@hÿ¨-~8úƒHNK.‚3ÌÞÞÞÅ­B±ãçç'LMM³Üÿë¯ËœU¹&Ôm&&ËÅÙ³gó¤Ã¥K—D™2 r\.‰©SäõT³eĈ—.]RŽ%&&Š@Œ3&_ó;vLbÒ¤Iy:þæÍ›³fÍÒH¾GÙ~®…Éîݻŋ/TÆÞ¼y#Ê•+'‘ãIIIbïÞ½âñãÇB!,--…M®u;v¬ÄرcEbbb¦ý§N#GŽTþngg'd2™¸|ùr&Ù—/_*õBˆØØXˆ=zäZ/‰ü“˜˜(ttt„xøða¦ýÏž=qqqÊßG-1gΜL²iii"**Je¬^½zBOO/“ì«W¯ÄôéÓ…®®®¨X±¢ÊïâííãÿWWWéQHè"¢ÄvõÉØ°ˆ=Ð9%>z'ÂÆoañbE]ço :„çO{=W:œÓH!­I Ï#ÂxvíéæòØ3G G6ôÜ@£ ï¤ )‰P";ôôô7n›6mââáW¿üò éééüðÙäïÝ»ÇüùóéÒ¥ íÚµËq~!dÿþýܼy“„„ìììèÞ½;Ùt#º~ý:k×®%88˜jÕª1xð`Z´h¡Ñ9¥¦¦²råJNŸ>Mtt4Õ«W§OŸ>tîÜYE.))‰'NpøðaNŸ>žžvvv|öÙg´lÙ2Û5ºuë–iL__ŸAƒ1wî\._¾LµjÕ²CWW7“N¹åêÕ«,[¶ ggg-Z„L–9ïÂÅÅ^½zEDD 4Èô”ä;ŒŒò¤Ëýû÷™;w.={öÄÒÒ’5kÖpþüy¬¬¬>|8mÕÐÿ÷ßY¸p!—.]"11‘zõêñÅ_`gg—IVñ¹ž={–;wîP¦Lš5kưað²²RÑcÑ¢E\¹rgggƵµjÑŸ~ú sss† ÂâÅ‹9qâfff|úé§J/í¾}ûð÷÷çöíÛ4kÖŒ~øCCCå—.]âÏ?ÿdäÈ‘<~ünܸAíÚµ7n\&Ïý´iÓ¨X±" `É’%œ={|}}Õ¾§ááᤤ¤Ð£G,--3í/S¦ŒÊïŠsÛ·o§C‡¨È߸qgggæÍ›G©R¥(_¾<ëׯ§~ýúìÛ·OEöÁƒ4jÔˆ1cÆpçÎj×®žž .T‰Õ={ö,õêÕcÅŠcll̲eËprrâìÙ³*s®]»– 6(ód2;wîäÓO?eÏž=üñÇtíÚ•°°0¢¢¢˜5kV¦/N·nÝbÑ¢E,X°wwwâââ°µµeË–-4kÖŒsçΩȯZµŠ7Ò©S'þüóOôôô²M@UÍÇרLkùòò0´‚*qûÝwßaddÄÞ½{ d>‰l(nøÇ€&KŠ:uäÏ¡»t)Ôe<<Æ‹R¥ÖŸl7SÓN¢V­ç*Èûö"&&óœ{„^o=exFV[õVÕ õÜ$ ž‚ áÉ– ³BOOäzÓÕ½$d²’Â!„&L€ò¾rçΡ¥¥%ÆŽ«"—’’"¬¬¬„‹‹‹ÚùÕ…p¤¥¥‰7n¨È¥¦¦Š>}ú™L¦¢‹"„¾¾¾Êñ§OŸŠêÕ« sss¯WÂ1dÈ???‘žž.„‡%ôë×Ohii‰þùGyŽ€Jhƒ‚Œ! ¹áÎ;ÂÔÔTÔ¬YS¤¤¤äúø¼„pxzz @m8FV(>ïR¥J‰Î;‹åË—‹ððpµ²¹ á¸té’òóû믿DjjªBþ¹jii‰&Mš¨È·iÓFhkk‹óçÏ+Çîܹ#,,,D¥J•Äëׯ•ã}ûö2™LlݺUeŽÄÄDñèÑ#!„üZ«[·®0117oÞTÊ\¹rEˆºu늴´4å¸ÄСC•×Õ£G„©©©033vvvâêÕ«Bù5ûÙgŸ @œ8qB9‡„¶¶¶VŽß¾}[˜˜˜ˆ† ªèkii)1lØ0]²ãÓO?€044}úôk×®D’UÎÉ™iÚiïUÉ'©aÁ!¤¤@RRî·ääüÕ/HÖ­[ÇìÙ³ùî»ï¨[·.óçÏÇÈÈHé¡­Zµ*:t`ãÆ*Im»víâáÇŒ1Bãµ´´´¨Y³¦Ê˜¶¶6ãÆCÁ™3g2cff¦L®([¶,<{öŒ­[·f¹Ö“'OX¿~=Í›7ÇÝÝ]é‘522büøñ¤§§+×ÕÕEKKKmrc¹rå4>?IIIôëׄ„V¯^­Q2sApûöm*ç¢dèœ9sðóó£yóæìÛ·Q£FaooϧŸ~Êßÿoj֬ɀÐÖÖÀÑÑ‘úõësõêU¥ÌÍ›79zô(5¢Q£·OòªV­J·n݈‰‰a×®]DEE±eË<==U® ‡ )¼®ÇçêÕ«´iÓF%t¢nݺ´nÝš«W¯rüøñLúNœ8@îÁuqq!..Ž>}úP§N@~ÍvïÞ@í“™F©$ÚU¯^6mÚpñâE.\¸I~̘1hiif2mÞ¼™ 6àèèÈÖ­[2d¶¶¶ 2„늮¿ÿáêêÊåË—:t(7oÞäûï¿§E‹Ô«W7’’’¢Ñš±µµàÎ;¹>VÑHåĉ¹>öcCŠ."lmmK^lë?ÿ¼-cQÄñÏr–33•ÑÒ¥aÆ 7.û6ܺ:º AiM-´4¾ñ•fÏž]ò®•÷==èÓ4Œ^Páñcðñ‘wº/n~ÿýwttt°±±aÀ€L›6êÕ«+eFE`` Û¶mcàÀ¬\¹’2eÊd2brâæÍ›,Z´ˆ .£RE(:::“|ëÖ­3ýµk׎ٳggûO<44”ôôttuu™>>SöcC2 ‹ˆŸ±a±Ô€ÎœDhh.È“s¢­K[–¬\›rÙ”‘`ZÊô½2 %ã¹àÐÓƒ/¾€ûì3öìÙ rå/8pàQQQJOan¨U«µjÕbĈìÙ³‡îÝ»óã?æË€VçÁWxä @F¨ºòxŠz ÃM!›“§Éœï¿Ùéúî¾wÏ!#êÖTŒ=}úTe¼víÚêO@ûñãdzdÉÆŽËO?ý¤öK‹……žžžxzz2wî\œœœ8zô(gÏž¥Y³f¯yçÎŒ3%aæ†i³”0Þ«B¢àQTà°¶–o%€2eäùŒšÐƵ Fw ›†b¦ÿ˜2aø„‚QNB¢£­­ÍðáÃ9}ú47nÜ`õêÕ!rm ®X±‚W¯^±nÝ:þüóO¾ýö[zõê…±±q–Ǩ ë8uê ÷g…¢êEß¾}³L*ž3gŽR^GG‡Ï>ûŒsçÎq÷î]æÎËÅ‹?~¼Fç–žžÎàÁƒÙºu+³gÏΗљWžÍíÛ·ç{®nݺaooO\\\&£¯ Qû'OžÌ´/((x–¢U÷´"·sæåK†&(®Ouc…e<>ccceOvX[[Ó¿•F99qäÈbccså1—È’ý1SDccAƒûE®™pxO>?@MÜÒ7JãVÆþý ~q‰@2˜í&D~cßSs\#ÛopḚaÃÐÑÑaÉ’%¬[·Ž-ZdŠgΉÈÈHôõõ3• {·#ZFÎ;—©sÞ¡C‡Ô–^SP³fMLLLؾ}{®»9V©R…o¾ùOOOþþûoîß¿Ÿ­¼‚ádzqãF~üñG&Mš”«õ Š~ýúáääĪU«”%ÌÞ%==]å±}VïÍ£GˆŽŽVv',LêÖ­‹ŽŽŽòs͈¢ª†Âh«]»6lÚ´)ÛN ŠäìæÌoC¬P·æ¡C‡ÐÒÒÊ—ñ™]¾Íõë×IHHP‰ÏN^‹]©R%ÖŽ‰‰aìØ±hii1wî\ 5–È+RGQâ:¾zŠdµÿâÄ ’/`ÇؼŽ5OЀpÀ–¼\†ã÷ç÷s¿ƒØzÚRþlyžˆ'Ä›Äcd€ásCFôÁ„1ïŸ÷YêD˜3 Ö¦sçM@öeÒ´µÁÒ²KžÖ°°° E‹pRSsöR6kÖ'Ok4VVV¸»»³bÅ „üöÛo¹ž£V­ZìÝ»—¹sç2a ٵkWŽe±¾ùæ,X€žžGŽaëÖ­Ô©SGmýe†††Ìš5‹Ñ£G3xð`~ûí7åcÿçÏŸ³eËiݺ5çÎãܹsÊ–Ç`dd¤LLËŠqãÆ±víZ:wîLûöí3%ßÙÚÚªÔ&nÒ¤ —/_&>>^¥#›Ây,µL&SÎebb¢ =È mmm–-[F»víhÖ¬ÿûßÿèÖ­ÕªUãÑ£G„„„ðóÏ?£§§‡››‰‰‰8880zôhzôè©©©œ={–%K–˜˜ˆ——W¡‡©YYY1bÄ–,YÂܹsùúë¯ÑÖÖfëÖ­|8óçÏÇÔÔ{yŒŒ¨_¿>µkׯÃÃ;w²víZŒ‚U«VŒ‡‡G¾Â'²#::šß~ûñãÇ£¥¥Å† 8{ö,žžžT­Z5Ïó^»vÏ>ûŒ±cÇÒ¾}{lllxùò%'Ožä—_~PÉIpww§råÊ 4ˆÚµkcddÄÕ«Wñ÷÷çìÙ³TªT)Sø†Bò¤èDªU«HLLdêÔ©y꾚©¡c†Ù‰ðäÉ·u¸vïVÙ5~üÂÑq€¨YÓ;Û­Z5•U‰‰BìÜ)隷ÿn©¯ñžç8V±bÎÝ ÇŽS–¨«ñ{ óB^ãîÍ›7ââÅ‹"::ºà߯"D*”s»…¬ÊØeÇÑ£G ÌÍÍÅ›7o²•UWÆîÉ“'¢ZµjÊ2\fffÂÄÄDìÞ½[büøñJYE»ï¾ûN4mÚTèéé);ûÙÙÙ‰ëׯ«¬§®Œ]zzºøå—_„¾¾¾D… „¹¹¹²l›ŸŸŸBˆ€€eÉ5sssaaa!Q©R%¥Lv(J eµ-X Zа^½zÈô6iÒ$Ë9Zµj•£ nݺ%š5k¦}ºDdd¤Êxdd¤ÄôéÓ•cŠ2vË—/666¢téÒÂÌÌLÂÅÅ%SIDKKKÑ¡C‡œÞJ%¢J•*ÊÏK___hkk+?»aÆ)Ë !ÿ/Uª”R^ñ¢V­ZââÅ‹*ó+®Ëw7###Ñ£GÊ#J É]D”¸€üŒ „ïTàˆ‰yFhèÞ­Žñ.+~Ijj:ÇŽÉ=ÍÛ·C\œªŒ•ôí+o€rìØ»3d߉PãDZøübj˜×àØ cT4–ÇXêëëÓ Xª‰,R¡„‚qãÆáîî®¶Ë[V(¦x{{ç˜<èääD`` J> ®_¿ÎŽ;ˆŒŒ¤V­Z¸¸¸P¶lYUbR+W®L`` L:•“'OŒ½½=mÛ¶U–S0}útÆŽ«2&“ɘ2e ^^^üý÷ß„††b``€ nnnÊJ]ºtáîÝ»œ8q‚˜˜ŒŒŒh×®ÆOkþüóO•ïòn¨Ë²eËxñ⺺º*ã .$îÝÝä&ŒÂÞÞžS§NÊÕ«W ÃÊÊ š7o®ô(+*‘DFFröìYbbbHNN¦råÊ4jÔ(“ÇÛÜÜœÀÀ@µ]ðÞÅÎÎŽÀÀ@µa>#Gޤcǎʦ= ÷°ïܹ“Ó§O«t"tuuÍTPGG‡_ý•Áƒsþüy"##)[¶,ÿ÷ÿ§–aiiÉáÇ RéDتU«LfþüóOµe ?ûì3š6mšéœ--- TIDU`kkËåË— âæÍ›Ô«W77·LŸ·O¦ë8;ªU«ÆÝ»w¹~ý:ÁÁÁÄÄÄ ¥¥EåÊ•qqqÉÓ½bÅ ~ýõWNœ8Add$Ïž=ÃÚÚ{{{Z·né=P\— ÿÛ±°°ÀÆÆ&O7²£ÄÙ,%™%¥Òé‡Ë Aƒ€fyyÁ_É“æ}ìƒ7žAŠ9“(^J”ÍRB‘<ÐEÄ©S§4hþþþÅ­ÊÛøçŠ5oûWÄŒÙ7æ£4ž%$$$$$Š E·ºNªHè"¢yóæ%dz¨ð@—Ð’ocöaé…¥ÀÛ° ëÒ%£Õ¸„„„„„ćл»;îîîÊ*Y#y ‹ˆ“D˜P` „ù%<<fcïvÊâYŽ4Ô!„ ,,Œ±ãÇ2ú‹Ñ¸¶vÅÄÄ„ÑûF³ìÂ2Ê:plбÎxya‰yZ!!!!!ñÑñèÑ#):$ºˆ(1bÆ„Yx øa ?üß}7n€LgÎ@…A>wჾÄS‹§ü[þ_No:ùsLÊšp£Ö Ðþ¸g’%$$$$Š—c³”`$úcCÿ\©Rf—ò;ìØ!ÿÙ¬YÁÏÇN£ß´~nãYBBBBBB¢ä#Ð tñÏW¯¾ •þôÓü/ûêÕ+FMÅ£V@]ù»J • …ÇÉx–(ÑHtQ"òãã!<\þ:Zá}y—Áüräè"­"Uçg€ÊTÖôšé `æ·3ó¿à{ŒÔ‰PBAdd¤Jþ„®®.666”-[6Oó…„„œœ¬‘¬““†††ÙÊܽ{—¾}û2zôh¼¼¼rœsÒ¤I„„„pèÐ!t(h^¾|É©S§¸s熆†ÔªU‹Æçi®ØØX¢££ÑÕÕ¥~ýú9<àêÕ«„……aii‰½½=ÎÎÎjeo߾ͥK—xðàæææT­Z•Æ«tŒKII!88sssìíís<‡ž={jüùå—´´4ˆ§bÅŠT®\9×sܽ{—‹/ƒ©©)6664iÒDíµš””ÄÉ“'¹sç)))X[[S¿~}ªV­šiÎØØXåï&&&ØØØ`dd”û“ü@:j€(t¼½½…««kq«!ÄÑ£BÈS… ÌV´N¹Xƒ³ô艣cÌȰ5E0Yu̶µ­HNN.˜EßS¼½½‹[…bÇÏÏO˜ššj,ûömñ—Ï_—¿¸ÿ~¡ètÿþ}±k÷.±qóF&ÒÓÓ eŒŒ1B™6;;;±mÛ¶\Ïgii©v>uÛ¥K—rœïæÍ›³fÍÒhý=zäês-HÆ/ôõõ3g“&MDXX˜Fs¼~ýZtéÒEXYY)·±±É“>÷îÝ:tPûÞ;;;‹={ö¨¬ëéé)d2™”?¡¯¯/3ÜÏccc zôè‘£‘‘‘¹úüòÊÑ£GÅÿýßÿ CCC¥Þ“&MÊÕ©©©bäÈ‘¢T©R™Þ±aÃùýû÷‹*Uª(e2Ê7mÚTEvüøñj? Ñ¡Cqúôé|¿% ooïÿϸººJÿ‹r@ò@%" _ÄaapíšüuA„oľŒåFÜÌ¡j:©j¡…v~[¾çHI„šAÿÑý¹«}—§ež¢¦ÅR >)û ë¯Ï³§6#qqq 7ˆó±çyfñŒ­,þ²À&͆M‹7Q£F8“ìY½z5Õ«WçÅ‹ìÙ³‡­[·Ò§OŽ9‚«««Æóøøø¨< »rå “&M¢OŸ> 帯¯/¦¦¦¬Zµ SSSŒŒŒpssÃÍÍ-×:4Ïž=ãÕ«WY†d|þùç|þùçåÉ€öõõEGG‡•+Wªw-Z´ E‹*²$$$ðý÷ß3bÄ帽½=öööŒ=ZíÿÎ|ûí· 0€Í›7ÓªU+åyHHH–ÊDŽ­aüsݺ`föß­¾|þõçÌœ;“³gÏf{ì™{g0«yVxn÷dø~ÒDØ‚Á=È&¼ìÕ²|7ú»\œÄÇLŸÏû¨ÏeaõÂ1aDæ¹`ä„‘Ü®{QVdÞiZ? ß¨~¡f!âéé @Ø™¾íÛ·§Fj=‘GŽÁÌÌŒuëÖi4wjj*ýúõ£lÙ²X[[coo¡¡!Ý»w'&&&ËãfÏžM¹rå¨Zµ*¦¦¦Œ7Ž””Ö¼zõ*-Z´ÀØØ˜êÕ«cllŒ——Ïž=S‘»xñ" ÀÚÚšêÕ«S¡BÊ”)ÃÌ™9çMd4ž3Ò­[7•ØÂdÛ¶mœ?oooã9#:::ôèÑ'`ÇÅÅQ¡B¥ñ\˜4nÜ333µÛ»ñâ»wï¦jÕª”-[–*Uª`nnÎÌ™3U¼ÁELL eÊ”¡‚%¡ש““S¾×577gÍš5˜˜˜ðÍ7ß”Œ|&‰ä."Šý..""䯳 ߈ŠzkgW¶[B£žsyPã©åR!~;óµ“k³eõå,:>šW6òç?rûÙm•ùÌ ÌéW§ÞÎÞ˜ô7¡ã ŽD5ST’Í®›Ñ±\Gzu/€ŒÅ÷)‰0g¢££¹§§ÞxVn™Îá‡ñ¹êƒL&ËÓ:‡®"­]ZÖ†ðÀäT¯^=Okä…ÊÿWв_¿~ 2„Ý»wÓë¬ßåË—“’’’i<+ÒÒÒgÚ´iÔªU‹äädðññ¡K—.„„„d ³Z¿~=B–,YBùòåñññañâÅÄÅűaÆl×;uênnnXZZòõ×_ÓªU+¶lÙ‚¯¯/×®]#88---’““éÖ­ZZZL:'''’’’¸rå /_¾Ôô­ËÄ™3gò•˜þþûo  ‘¼‘‘vvvܼy“_~ù…áÇS.›'ˆùeÆŒ¼xñBeìÆüüóÏ*Iu>>>ôïߟ 0þ|ŒY·nS§N%&&†+V¨^õêÕãôéÓLž<™¯¾úŠŠ+f)«ð"Ïš5‹òåËÓªU«<ß@þ´¢sçÎøúúFݺuó<×û‚”D˜3’­!©©©,_¾œ'N`dd„»»;=zôÐø²Ø;fì@˜zçÎÿ^è­æDÒ÷¼lŸáFZž[>çTü)Úôiä…“øëÆ_‹<†à­®”V):UïÄ çAtµïŠ®öÛG¿gwžeÐøAܺt‹GÑ0³5Ã$Å„ C'0Ü{xAžñ{‹Ô‰0gNÿ}š§fOs”{¬ÿ˜þ«ûC^B¡ãQÖ+Ïv ÓÇœ9¦È èàà`æÌ™¼ Éðôôd„ ¬ä#K IDATZµJÅP~øð!»wïÆËËKc丹ž.\PëÚµ+*T`úôéìÙ³www•ý·nÝ"** Ú´iÃýû÷ù믿˜8q"uêÔQ»Vzz:cÆŒAKK‹àà`¥aØ©S'œœœ˜0a[¶l¡_¿~„††òðáCæÎ«ò¾mÛ¶—:¶mÛF`` ÞÞÞJÝ ›ÐÐPlmm5>fÙ²e 8ï¿ÿžéÓ§ãââB‡øüóÏ $Î?#;wVùýñãÇL™2… *°qãF@n\Mš4 CCC‚‚‚”aRíÚµãþýû¬^½š1cÆdù¹ç… àááÁüùóY°`Mš4¡mÛ¶Œ5 kkÕÒ§ýúõSæ ¸ººR©R%Z¶lÉ Aƒh×®]žÖWäܼyó£0 ¥N„9#…phHbb"QQQL˜0Aƒñã?ªÄ¨åD±_ˆ;fã–‡o<¥T…™¼lòB½)ܪu‹!‡p4ò¨Òxv¶rfA‡Üÿú>»ûí¦WÍ^*Æ3€••û·ìçö¡Û\Ø}˾— =*ÏŒçœ)mXí4 ’MSÉ»›  AB©ôR©((zö쉣£#*T aÆœ:uŠvíÚ1qâD ðööæÐ¡CDEE)[»v-)))*q šòæÍ®^½J`` þþþXXXoÃF2R£FLhÇŽBpðàÁ,×çòåËôïß?“Wµ_¿~hiiqúôi033cÛ¶mJ|~¸|ù2Æ £J•*üöÛoùžOSÎsssiß¾=wïÞeýúõ8;;sòäI¾ûî;ªT©Â_|¡—]¼yó†îÝ»óäÉöìÙC¥J•ù¦{÷îѲeËL9;w&==½ÀK6jÔˆ;wî°mÛ6\\\8þd§òñaÁ „ÀËË‹ .àïïÏ'Ÿ|¢Ü)ÿ›PçÍm×®“'O&\Ñs ÑÖÖÆÃÃ^¿~ÍöíÛùý÷ßÙ°aáááÊ/[ jÔ¨ÁâÅ‹ùý÷ß fÅŠlÚ´‰þýûS½zudãLzÅÓ¢ Õ’(ÙHtñóó£gϞŭ†æ(<ÐÙÜ0üüþ‹ò0BXg÷ùåÍÊsgÌ õ³o¶ !QÐXXXР\öþ»W}‚`pǯN^8Zä=ž|`—̹:‡7öoÔ ü µ jgYüôÓOY6×Pààà@›6mX»v-3fÌàðáÃDEE±lÙ²\­åããÃÔ©S• 6jÖ¬I… ”qÞêÄòšD©˜ËÝÝ=S%VVVÊ×_}õƒfýúõìÚµ‹Ù³gó믿2kÖ,¾ýö[ÖŒˆˆ mÛ¶Èd2Ž9Rä‘¢bKXXXž&&&Œ9www*W®Ìž={HIIAGGƒ˜# ™4i;vì`áÂ…ÊDË’„¡¡!ÄÃÃkkkΜ9Clll¦p™LFÆ ùã?(S¦ óæÍcçι2 ¯_¿@Íš5 ì$Þo>èŽÔÔTs” Í”ä÷úõk‚‚‚ RƬ)øñÇÑÑÑaøpÍÊ5‰ðùs¸sGþ:›øçíÛå?Ë”¶ rrLK™æËx/ñõK‹ƒw¯7 õl\¶‘ÚWk£õ ómÌ Â€†ÿ6äÇÉ?æki§Ñøec ndÚ§õP ‡`6¯Üœ¯5 ’Q£FK@@+W®ÄÈÈHãd5èèè°hÑ"ÜÜܔɻwïÎòuuuð³{¬0FLLL”ÞÕw·:¨cffÆ—_~ɱcǸ{÷.ŽŽŽÌŸ?_£{ITT®®®$%%qøðáb1†ºvíŠL&cÞ¼yùžËÊÊ {{{RSSIHH(íä¬ZµŠ¹sç2fÌÆŸiµjÕÔ†i(ÆŠ¢>:È é†ÿý_{þüyŽòŠ/jOŸæœC¡ ((ˆ#GŽP»ví&´¡Ø ¼|pôË—/¸+tuÞfdؾCX¹Y‰é¿N/:ý%>:üüü„©©iq«QìLœ8QXZZŠk×®i|Ldd¤Dýúõs”=}ú´°´´3gÎTŽ¥§§‹)S¦}}}SSSѧOqÿþ}aii)¦Nª”½}û¶°´´‹/óçÏ*T€(]º´;v¬HNNVYoРAÂÞÞ>“¢k×®¢téÒPÎÑ«W/qõêU!„W®\®®®BOOOB[[[4iÒDüðÃ"666Çsmذ¡°´´Ìr[µj•м›››°´´‰‰‰*ã;wÎrŽž={æ¨GÆ÷yéÒ¥¢Q£FÂÐÐPBKKKT­ZUüôÓOâßÿB‘––&fÏž-Z¶l©ê1???abb"$rŸŸŸÄŠ+ò5Ojjª¸wï^&c('bccÕ:!4!==]Ü»wO<}ú4[½bccÅóçÏó´FI$--MDGGçh¨¥§§‹G‰{÷î‰ÔÔÔ"Ò.gÄãÇ‹l½'Ožˆèèh‘’’’£ì›7oDdd¤ˆ/ÍÞÆqÆû®ŸŸŸðóó“'Oþù§puu• èøàB84!66!D¦(EL܃Ôffg|Œ—[é-¯€G•+Sí¿¢ì…NBÜ»'EüóîÝoÃ7>ýn?»Íü³óA {4æÜ¸s¤§§£¥¥…¿¿?õ &ì$<<\müßÇŽ”D(‘WÒÒÒøá‡°··gðàÁùšK[[;×åá@µjFn‘Éd9®©­­¯5J"ZZZT®\9G9™L¦ì>Y’(]ºt¦zÐ…InòôõõsÕ´FBŽºœ‰w(n ¾0ÉÊ"±cÇ•ñk×® @ìܹ³@õðöö®ÿ…oL/WN„\¼X ógËÊÐqø°Z‘îÝå»Ë•"5UˆNuÌ@hÿ¨-.Å^*4ÕÆÿAy‘ é[¿‘[ž={&:tè ¬­­ öíÛWÜ*IHH”P4 Ï<Ð9óQz ¯Þ͈ˆPÙ_Tw©Ÿ‹Ú“ù&c5ë¾| ŠFaîî°7|7áŒl8g«ìkÏæ)‰P=R¡DnÑÒÒÂÌÌ OOO>ýôS\\\Š[% ‰÷ÔÔTRSS ½¶ý‡ÀGi@[YY¡¥¥Å7TƯ^½  ¬{ZœºÊd ìÕ«ÀçÎE;;PÓõjï^P”ÊîÖ3‘ñû¿ÀÂЂŸÛLf·„„DábjjН¯oq«!!!ñž³ÿ~8þ<Íš5+nuJ4¥]ªT)ÜÜÜ”³‚Ë—/coo¯,_4t„`\QzŸá­:‹øç;ä?ÍÌà‚Þ¯DÆÉ[´Îr›Eý2E¡¡„„„„„„D  k×®tíÚ•Aƒ·*%ž®‘ È Áûûû+=Ìþþþøûû+{ÙŒ?žàà`fϞ̓X·n;wîTÛu© ¸| ðÇ…2¿Zž>…»wå¯ÕÐoÞÀ¾}ò×mzE1ïì¯4ªÐˆ¡Ÿ -tõ¤N„ê‘’%$$$$Š©aÎ|ô÷ßÏÈ‘#9xð –––Œ9’‘#GrâÄ ¥L—.]X³f ¿ÿþ;+VäÛo¿åÇä‹/¾(î—*Å-àÚÖ­PÕx¾÷ï‡W¯ä¯8}Å›Ô7ȱ´ËRd~“©¡z¤N„ÅÁµk×ðõõÍô„^"3dÇ… 4’Htn)lãÀ±I:V«Fu(º0…ºzu05UÙ•œ €v2¥º Œ~f¹Í*Ý$$$$$$$JÕ«W§cÇŽR è¢dèqÅÁÁðÏ?…¿žÂ­&þùðáÿ"Iš- A7 €ŸÛüŒ…¡æê%$$$$$$$>F$ºˆxòä ¡Í›óT[[>°fMá.øøñÛ„jâŸwìLîCKy©ºz–õÙ°ÂJ2 %ªGJ"”(ž>}Jhh(Ož<)nUJ<’]Dܺu ÿ3gmÒD>°iÓÛÌ…AÆÂw<Щ©°kÐ~èʳ—t^‚¶L»ðôQƒ”D¨)‰Pâ}!99™ÐÐPž={¦‘üýû÷¹}ûv!k%¡))))¹úü$>|BCCñ÷÷çÖ­[Å­J‰çƒL",‰4oޜɓ'CÍšò–ÏŸËÝÀ΂ ZMáñãð¯qÔÙ€—“Í«4/=²AJ"T”D(¡`óæÍ*Õƒtuu±±±¡I“&4ožû¿ÙI“&¯a ~øJ•*e+sçÎj֬ɬY³ä÷·=z4AAAÄÅÅi¤CAòìÙ38zô(wîÜAOO{{{FE:u4žçéÓ§Btt4æææüïÿË“Nlݺ•7npçÎ,--±··gàÀÔªUKE6..Ž¥K—Ìýû÷155¥J•*´lÙ’nݺQ¦Œ¼nÿ‹/˜8q"ÎÎÎŒÌ!Yýþýû¹úüòŠ‚ððp‚ƒƒ¹téñññtìØwwwç8zô([·nUþ®­­MÅŠ©S§]ºtA[;kйsçX·nåÊ•ãçŸÕ7»|ù2Ë—/§OŸ>´iÓwå3fŒÊšÖÖÖØØØ`kkKÓ¦MÑÑÑÉr݈ˆÖ®]˹sç(W®­ZµbøðáÙêZÜ4oÞœæÍ›KOB5@2 ‹š.]ÀÊ >”'–­ˆ¶·cc•]Ûv¤BgùMÁXׄ9íæŽ…„ßάŸ62zzÙÊÅ&'3ßÇ'W’‚ÐÐPÆõéC…lþAÄ%%á9e ž…ð·|âÄ V®\‰µµ5ººº¼|ù’ÿý€nݺ±sçNJ•Òü6ÈãÇ•¿'''óüùsŒŒŒ(]º´Šìرcs4 ß'ºtéÂßÿM¹råprrâîÝ»>|˜+V°zõj† ’ãñññ”+WNeÌÆÆ&×´‚¥K—2iÒ$’““qttÄÁÁ»wï²wï^æÌ™Ã—_~Éo¿ýÀÍ›7qqqáùóç”-[9pàkÖ¬ÁÇÇOOO^¿~ÍÊ•+éÑ£GŽtQ±jÕªLº˜™™åÊ€¾rå +W®ÄÂÂ###’““‰ F9r„Ê•+«=vÞ¼ylß¾€~ýúeúrÉÊ•+qttT1 W®\‰žžVVVüûï¿Ê'§¶¶¶Ì˜1ƒÏ>û,“QBÇŽIIIÁÕÕ•¨¨(|}}9xð >>>èåpï’x…Ž···ðöö~;0i’ „L&Dxxá,Z©’|þýU†ÓÒ„0n·@0Á Äü3ó g} ‰|àçç'LMM³ÜŸ <«U“_ãYl‰ >uvΗ}6¯²YC€`k+ž?ž¯u²bĈ—.]B‘žž.N:%š6m*1kÖ¬|ÍìØ1ˆI“&åéø›7oæJ=zdû¹&S¦LP;sæŒÐÓÓúúúâÉ“'9ÎñêÕ+ñÍ7ßqëÖ-aii)lllr­ËâÅ‹ 7n,ÂÂÂTö½yóFüøã¢C‡Ê1ˆõë׋ôôtåxzzº8~ü¸¸víšr,66V¢G9êY ×QN;vLüïÿû÷ïÛ¶mËÓ5·`ÁåXhh¨ùD˜™™‰ÐÐPåØŒ3 V­Z•›S/p2Ù$y”ùØ‘<ÐEDXX¾¾¾Ô©S‡:C‡Â¯¿Êÿý®Y¿üR°‹=z11ò×ïÄ?="¡át*èÔb\“q»v.ÇÖÖ6W´ÐÐP‹[MéÒ¥qöðàÔo¿Ñ<‹DÔÕff ûé§|­3jÖ,–õíË7YĈ^ÔÒ¢F×®˜™™åkM‘Éd¸¸¸0iÒ$zöìÉáÇ™|{{{µž7u$$$pôèQnß¾Íëׯ±±±¡M›6Yzø^½zÅñãǹ|ù2vvv´k×NV  aaaüý÷ßDGGS½zuÚ´iCùòå3É%%%qæÌNŸ>žžU«VÅÕÕ•²eËf;ÿ/jî³Íš5£oß¾lذóçÏÓ¹sçlç044dîܹŸ“:žcccÜÜÜTÆÎ;Gpp0¯^½ÂÉÉ WWWtuuUdZ·n­¼6ƒ‚‚ LWfÍšÅÆ9}ú´Z™¿þú‹ääd¦NJZZýõ³gÏÎ×ÿòåËÓ·o_zôèAݺuY°`ÿÏÞ™‡Çt½ü“Ub‹l’Ø‘ – ö]¨¥ÖÒ(¡öŸjt£Uªh+TK¿¥öØ5b«½vjBH‹HD‚l²'ç÷GÌ4c&ÉLÈù<Ï}’9÷=ç¼÷νç¾sîû¾www k-ŠË—/3|øpêÔ©£¬7yòdf͚ŲeË=zô+÷_”Hpp0µk×Öµ:Åi¹h‰ÄÄD*T¨€‰‰IV^æ¶máÄ X³fφÂô‰R¸o€šýÕ‘)P*€_{,ÂP_w—ÀâÅ‹ùöÛoµf|¼)øøøH?è|0aútFoßNë»wÕö¥Çø¨W¯×ê£}çÎ,­QƒÄ§O)­aÿÂjÕXœƒOeQÒ¬Y3"##,_éÍ›7†ŠìÚµkùè£8tèP¾ è””,--IOOÇÖÖ–ôôt?~L™2eX¾|9žžžjubcci×®·nÝ‚°°0ìííÙ¹s'®®®¹ö—žžÎôéÓùñÇÉÈÈÀÆÆ†¨¨(,,,Xµjï¾û®RÖÇLJY³f‘””DõêÕILL$**ŠÁƒ³iÓ¦] XH%11Qת{d-ѰaCºu놣£cVÁ¨QY#"`ïÞÂíL@¨¯)‹Ï„á¦Ñ:*>H׎…ÛoY¸p¡4ž5 çü¡œ…Ö0“´ÂÐQúúЭÛkoã%ü µ=ûœE0•b†hüøñ¤¥¥±zõj5Ùß~ûš5kªÍæ„ ,àÉ“'<|ø¨¨(Ξ=K:u7nœÒhÏÎüùóiÓ¦ ±±±„††ròäIbccyÿý÷ÉÌÌ̵¿ àããøqã "22’£GâààÀ°aÃˆŽŽ²‚÷¦NŠ»»;OŸ>åîÝ»DFFòðáCÆŽ›¯c{™¤¤$¶oߎ™™nòå Talå…¡¡!ÄÆÆÒ²eK.\HHHH‘éwõêU’““U¶ùóç¨ß|ó Û¶mcΜ9<{öŒˆˆvïÞÍ;wðôôDQd:fgïÞ½%’B$/hš|¡“AôÏÃo¹ Û{ æ ]”¾Ï >Л7oW®\ÇŽÞÞÞÂÜÜ\bÿþýJYWWWQ£F Ù3gÎ@øøøhl¿ >Ð[¶l€Xµj•²Lámll,UäÇŒ#±cÇeÙË>Ðñññ¢\¹rÂÁÁA¤§§«Ô÷÷÷WÑ]Ñ×ܹsóÔ5¿(ÎïŠ+^©þ«ø@+ü™###ó]'""BŒ?^”-[VuêÔË—/ÉÉÉj²¢ôž={„èÛ·¯ÈÈÈBü•1 IDATçW\µjU5ù¡C‡ @ìÚµKc{¯êw¯ð~ÿý÷Åœ9sÄÌ™3Eûöí…¡¡¡ptt—.]R«óÿ÷·oßV–½ÿþûÂÐÐPíüÄúeÌÌÌ„¹¹¹òs¯^½ BCCÕd§N*±wïÞüz¡#}  é¡+LMÁÓ–.…?ÿÌš‰.¬µç3ÐÙPùíÒoÜŽ»€Þ©¯±å퉮—”\”³Ð?ýDëŒ V0ªfM(€ÿm^ŒeÉ¿ÿòù ë‹zzZ}VdXPP¥J~úé'<<<”eãÆcüøñ9r„Î;Y³ÏFFFŒ1¢@ý¥¥¥ñÇpáÂû¬PtÊÌÌÌ5äíDÐZ"%%E½pÔ¨,:==Ëú«¯^¿£ˆxø0ëÿ¯$Ÿ$=aÚ_ӲʞԢ•Þg¼ÈÈ£Sd¡fdaÁÈî «_Ÿ®\)Ô>ÚK›6%ñâEJ ííµêû¼bÅ •y +Uª¤öºúƒ>`òäÉ,_¾œÎ;óìÙ3¶nÝJß¾}5ãåDll,îîîܺu‹*UªP·n]”«†ÆÅÅ©ÕÑäŽÐ²eK*Æ# (ŒñuëÖ±nÝ:2aŠUÍ›7ãããòeËX´hÕªUcüøñŒ=º@þ¤‹-â‹/¾`àÀ¬Zµ*ßõ '''NŸ>MHHˆš¿z^”/_ž &0až>}ʼyó˜;w.£G.ôjÂÃÃéÙ³'–––ìÞ½[Å÷Zñ´jÕJ­ž¢¬¨ÜL)ûbbb8yò$cÆŒ¡mÛ¶ààà ”óóó#&&†Úµkãïï¯,ÏÈÈÀÄÄ„U«VŠýøñcâãã©_¿¾²L‘þ100P-ð600PE¦¸’’’"SíåôÖÇŽcøðá*72ÿ磼jUÖ‹á×%û „/f §™ÊÓ¤Yöÿ€¾Æ*j¹¡fäJ„#»/tadÞȉñsæ°ÄÂB'¾Ïnnn´oßž–-[R¹re5ã²ÎÃ|€¿¿?=býúõ$%%1f̘õõÓO?qëÖ-–/_NXXdùòå ><Ç:š]¸yó&9ÖSäT^¼x1BÛÑ£G•òõêÕÃ××—°fÍêÔ©Ã×_] äß~û oooz÷î͆ ´¾¨…"3§¬ùÅ‚ٳgcccÿÿþ›ïròCBB={ö$>>ž½{÷ªúŠïMñgGQVÐ¥B… ôêÕ‹õë×§–gZñÃhýúõôíÛW¹ 0€äädnÞ¼Éùóç_[ǨdÛÈn@¿Lq7 ýýý>|x¡fKy[‘´–ððð`Íš5ê¯2GŽÌúûï¿YK¾.Š //>¼Èï—Ï*»Ý½»Ó¯ßëwSÈ BÍÈ Â‚3aútV©Â1ºÁkcÈÊÈq¡F æV©‚·2oä‡ìÁ„¿ýö›2\AÀÄÄ///•r??¿ë?~œŒ.4 :@Íš5s¬çâ₞žž2kA~©X±"^^^|8–––øøøä˜. ***϶çÜÐаÐf 3224hlß¾]-“  †W|ÇÙQ”ië-ZçÎ1bàÀ@Öì÷_ýE÷îÝ RÛ:¾îÛ‡øøx¾üòKŒŒŒøøãÿRÂvïÞ@é~¤ 11‘{÷îѬY³\Xê’>}ú°fÍ÷0‰f¤­k† Éò‡†¬• _Å ´“¢Li>úó#2E&z&°!M›B.©\%’7’²eËÒôý÷]ĆíÿùøP¯_¿bûï~ýú´jÕŠ~ø›7o2f̳չakkKrr2'OžT–Ý¿_eå—‰eãÆÊÏ=bÛ¶mXZZ2pàÀëYYY1nÜ8víÚÅŠ+ÔöߺuK9»}÷î]5ƒDÁýû÷IOOÏÓ^¿~=£G¦S§Nøùù©å*~™O>ù„>}ú–––«\AQ,ýýøñcÚ¶m«6‹›À”)SòbeËÔÔT† ¢6[ªÈŽ…››[VŠÔBÀÛÛ›?ÿü“%K–hôq¨V­½{÷æúõë*oÂÂÂðóóÃÎÎŽŠ>ùaêÔ©0sæL kBÁÈ‘#qrrRÛ:wîL£Fؼy3IIIê+--Ö¬Yƒ““¡¡¡|õÕW*Y+5j„»»;~~~\»vMY>sæL„Œ?¾p\¢[t¼X¢È3šõƒ²"ûML„xݨ~[Û¬¶† +/¯T®8Hû„(Äv‰¤ÈÈoŽì¤¤¤‘6ºéGõ•󃯯¯23Æ£Gr•Õ”áêÕ«ÂÈÈH‰nݺ‰ˆ *(³x{{+e™1<==…µµµðððC‡¶¶¶ÂØØXlܸQ¥?M+ÆÅÅ ˆzõê OOOѯ_?Ñ AˆÕ«W !þ˒ФIáéé)<==…½½½Ä'Ÿ|’çyqpp€022¥J•RÛ-Z¤"ïêê*‘””¤RÞ¶m[e^dÄP|îÒ¥Kžz(X±b…(W®œÐ××ŽŽŽ¢G¢^½zÂÀÀ@ˆ/¿üR!Drr²²[[[Ñ®];¥€°¶¶VÉ2ñ:Y8bbb LMME»víÔ¶¡C‡*ëÞ¹sGÔªUK‰.]ºˆ~ýú sssQ®\9µ !!!Êsddd$a``ã¹×„¦•³3lØ0ˆ{{{Q¡Bµ %ÙùñÇ |}}…¹gá000fffÂÌÌLèëë+¿úõë í߸qCT®\Y˜šš Q¯^=ˆ>ø@¤¥¥åy¼E‰ÌÂQ8Èè--¡1ˆPÁÈ‘àë ÉɰaL˜ðj<|/r´&¹ºðÕ‘¬ Ds=žž@ÿþ¯ÖtQ ƒ5#ƒ_¼fß´~ºuëF…  äOÚ¡Cúöí«ôU͉jÕª1eÊ•  4àòåËüòË/Ü»wkkkV¯^M·nÝÈÈÈ uëÖJYKKK¦L™B÷îÝ™:u*k×®åêÕ«ôíÛ—aÆ©öïߟFÙrÓCÖêvû÷ïç?þàèѣܺu SSSÚ´iÃwß}§œmÛ¶-¿ÿþ;gÏžåÁƒ”.]šÏ?ÿœN:Q·nÝ<Ï˸qãxöìYŽû_ÖëÃ?äáÇjãÓ AƒhÑ¢…Æ6jÔ¨‘§ FE×®]ùã?¸yó&wïÞ¥Aƒ 8¡C‡R½zuJ•*EHHþù'çÎ#""‚ŒŒ Þÿ}š6mŠ———Ê‚,eË–eÊ”)]/^ÆÌÌŒ)S¦(§T©RL™2%GùìÁ¨5jÔàÊ•+,^¼X¹áرc=z´Úy(_¾<“&MʱݗϽ&š6mÊ”)Sr\ hÆŒØÙÙq÷î]Œ‹‹K®n-C† !::Zé_»vm¦L™BÓ¦M•2†††*çÃÀÀ;;;ìííqppÀÅÅE™™äeœ9sæ k×®åܹs¸¹¹ñÙgŸáååUà·Bº@æžZÊv^‚>|8¡¡¡üõ×_š„€Úµ³ü 6„WÍ °k¼Ht¿à§|¿€çwp÷Ï>¸ºB@À«5]Lš4I®D¨áÇ—x?hE ‹b9cIþ™={6ß|ó §OŸVfÂH$Š€àÜž3;v¤Zµj%þY”ÒZKT«V-çzzÿ¨fÒ(/ê }f<Û@ÛJÜý3+p±8Í>ƒ "Ì 9`I Š‚˜˜<ȼyóxï½÷¤ñ,‘H^™\m h­ñ×_Ñ»wo,X@@@ªi‡¼¼@ñªðUƒ _dà±5%Þ0ccš?ýŸr·c:$‰‰ŠŠÂÜÜ,,,øñÇu­’D"yQ¼ý;uê”®U)öHZKDEÙrêTGfÏÖ£cÇc´i³H%z;;xç¬ÿ7m‚ÄÄ‚wòbú¸õs>mñ)'vd­ÀT·nÖ&‘HÞ>,,,Ø·oÜ¿_ÎI$’WB‘Æ.{¬ƒD3Ò€Ö©©5yöl’rKHЫV±@l,lß^°<€yC/U‚*å«0¢æ4™Š›ûd*V7“ü‡¦E)$’Ü066¦[·n¸ººêZ‰Dòkâ h-’wR|Þy'k& îÆ‘Íoúb%˜ßu>ûw•Q.nX h¹¡fäJ„‰D"Ñ%ùYȧ¤# h­‘Wª X.÷äIÎwëONd­À”®šµcË þø#k_YÉ=Š2ˆP32ˆP"‘H$ºDºå4 ‹#GfeåX¹2ßÕîÎZ%,ÈZù}~%* 1ÅqöY"‘H$‰äMEÐÅš5A±¨ÁÚµá?‚þ ú'$¹:ãbí‚¿?dffíïׯˆt•H$‰D")HZk„ù̬¡È »wç*š˜–Èü-Þ¼H¼Aƒ(Ý7ªTæÍ_A]- ƒ5#ƒ%‰D¢KdaÞHZ[˜ƒšu¡Ü@ >wÙþýAáüR0á¹ çè;¼/n=ÝpëéFã¾±;®ÜoâÞš§OA‘!¯_¿ÿ /¿a‰äeâĉ˜˜˜(·òåËS¿~}FEHHHÛsppPi/·íÚµky¶ÍÌ™3ßzóæÍtéÒSSSjÖ¬I•*U(_¾<_ý5‰ù\ù5--E‹1tèPœœœ055¥N:Ò£ çLo“=sæÌ|Ð3gÎ,r6nÜÈØ±ciܸ1eÊ”ÁÄÄDúK4"]8´Å˱rµã8R¾;ö¹x1L¨dÀâKÀ½{8”×ã ¾Ìö1`õbì¿d£Çƒ•ÿ’’R (ÞîDèàà€¡¡¼ ³sëÖ-œœœt­†¤––FJJ cÇŽÅÖÖ–ØØXüýýY¹r%Û¶mãúõëzÍ:zôhâãÿ‹Á eÓ¦M¸»»Ó®];YkkëB;ŽâÀÂ… ‰ˆˆÀÛÛgggBCCÙ²e ?üðwïÞeÓ¦My¶ñüùs>þøcJ—.««+÷ïß/°qeiiÉ”)ShѢūФ™:u*>ÄÅÅ…²eËòèÑ#¼©Ð5)))”*UJ×jk¤å¢-ž¼Š¯K?îÓ4øð‰à·‡ÿýÚ&üîµ°°€^UYí°xñb¾ýö[¹˜ÊKøøøH?h‰ ãÆ£á‹Õ,XÀ„ X²d ³fÍâ÷¬Zúõ×_«|>vì›6m¢]»v:w­(j~úé'š7o®òƒ}úôéT¯^Í›73cÆŒ<¸–-[–ÀÀ@œœœ000ÀÖÖ¶ÀzX[[¿õçúMfïÞ½8::RªT)ºuëÆt­’NˆŠŠ’>Ðy hmaöÒÇh+âÃæ‘yI›JiLù* }£4Ò2ÒHËüïoÐ% ¤ïC°H‚§/Å6‰Èú›f ,ytÙ€Þ½³b ‹32ˆP3ÒxÎ?á))¬ŽŒÔYÿCml°71Ñz¿ãÆcÉ’%\½zoooY±b…šì­[·øòË/>|8}úôɳm!«W¯fÏž=“””DÍš5éÝ»7ãÇÇÀÀ@c½£G²téR®^½JÍš55jýò™„þùóçøøøpúôiBCCqttä½÷ÞãÃ?T‘‹‰‰a÷îÝ>|˜Ó§OSªT)e_½{÷εV­Z©•àååÅwß}ÇÍ›7ó4  qqqÉ×1åDxx8&L`ذaÊóÌäÉ“?~<åË—ç×_åÂ… ØÚÚ2vìX<==ól733“3fÈ_|AË–-•mÏ;—k×®‘@½zõ˜]£ì«pîÜ9æÌ™“ãþ?þ˜Ž;*?ûúú²uëV‚‚‚¨\¹2m۶嫯¾RÓçu¿ß·i<çôÕúÑútqnÏÌ# `8QŽæü¯ÿ‡wso>où9_µþŠoÚ}Ãì³i<} ¥2ah€z[Šè@kH4#)© PüÝ7$’Â@éBèlÓÕ‹ÝÔÔTô_9T¬X‘ßÿ¿ÿþ[MvéÒ¥ìÞ½[9ƒ)))Œ?žŒŒ ºwïN=¸wï'NÌÑ;qâ À‚AƒFÿþýY°`AžýÝ¿WWW¾ûî;bccéÙ³'AAAŒ9’1cÆ(儼óÎ;Œ=šØØX† F¯^½HNNfË–-ù:6M„……P³fÍWn£ ÄÇdzsçN‚ƒƒ•eOŸ>eçά\¹’Î;sûömš7oÎ… 2d[·n͵ͤ¤$ú÷ïÏœ9sèÑ£‡ÒxÞµk5Â××;;;Z¶lɾ}ûhÕª;vìPic×®]ìØ±ƒvíÚqûömÚ´iC58xð »wï¦cÇŽøúúÒ¸qcž>}Êüùó2dˆJ;™™™ôîÝ›Q£F€‡‡éééÌ™3‡æÍ›“””T§###*T¨ ¶±sçN>üïõì„ :t(=bÀ€ØØØðý÷ßÓ´iSbb4ÇI$yQÌç(ß>ôëãt͉U{WQ¦ œ< ÂæÍж-ŒÿR…6m vmfìiC~i¡êL­ 40¡®Í Âòå¡K­ŽD"Ñ2ééé,^¼€&/GŽÉÌ™3Y¾|9îîîJÙ¤¤$Ö­[G×®]qppÈWûÆÆÆ„……Q±bE•ò1cưbÅ >ùä•>öíÛÇèÚµ+_~ù%Í›7gúôé|ðÁ¹úSñÅܽ{—³gÏ*Ûýù矙0aË–-cĈ´hÑ‚þù‡³gÏ2yòdæÎ«ÒFrrr¾ŽíeÙºu+Íš5£~ýú¯ÔFa²}ûvNœ8¡Ì€0oÞ<ªT©ÂÏ?ÿÌ{ï½§±Î“'OèÕ«×®]cçÎôèÑ€„„Ƈ —.]ÂÜÜÈrjÑ¢'NÄÃÃҥK+Û:}ú4sçÎeòäÉjý\»vÏ>ûŒ~øccc222èÔ©;vìàÞ½{T¯ž•[uÍš5ìÞ½›™3g2}útô^,DàççGÿþý™7o3fÌxísÕ¸qcµ·ugÏžeË–-´mÛVy¾NŸ>Í’%KèׯÛ¶mSþè\¾|9cÇŽåûï¿çÇ|m}$%9­%Œ¢¨v¼}úð÷Þ¿)W®úúàë •+gÉ|ò \¾¬¡ò‹”vuÒé½Ã6kQà ¨þ$Ë­À¾ó@ÎÍt{ö„7Á÷_®D¨¹¡äe~üñG&MšÄˆ#°³³cÍš5XZZòÍ7ß`kkKß¾}Ù²e qqqÊz[·n%&&Fe&7/ôõõUŒçŒŒ ’““•î.\P«cmmM—l¿ÚK—.Mÿþýyþü9Û·oϱ¯‡²mÛ6ºté¢b”ëëë3nÜ8 +û€¹¹9†††DEE‘‘‘¡ÒŽÉ+¸ÑÄÅÅ1hÐ 233Y¹r¥Ò°Ò%-Z´PIfkkKãÆ Ò(÷î]Z¶lÉ;w8zô¨Òx†¬ï>""‚‰'*g€òåËãééIxx8/^TiOOO///}éééññÇclœ•GÕÀÀ@Ù_ö1káÂ…”+WŽI“&)gÈJV¹revîÜ™ßÓQ îܹCïÞ½©V­;vìPê¹råJ†ªò{zzbll̪U«ŠDŸ7™y$oä ´–h`Ó€{N¨üÚ°¶†M› cGHI÷ÞƒK—À,»Ï´—|ý5¤§³©^'~¬Y‹³WÎÒ&, ÈòëÐkèMü‹UÞ÷ D¨D(y™`hhˆ±±1NNN4oޜɓ'«ºãÇgëÖ­lذñ/^e-_¾;;;zõêU þN:Å÷ßÏ¥K—ˆŽŽVÉB pyÈN‡TŒ%€.]º0sæÌ\Óí)Übcc>|¸Ú~###¥qfmmÍÀY»v-ûöí£OŸ> 0€Î;«õ‰‰‰ôèуàà`¶oßN½zõ T¿¨ÐäÇݰaC.\¸ –!22’-ZP®\9Μ9£æ‚rãÆ ë»TøÊg¯ Y“mÛ¶U–7iÒº¹ººªùÅ*Þ€úœý›÷óu¬ýÆÆ¬»’õú±tièÖ­(Ž ðY¸p¡4ž5 gÉË>|˜ÈÈHBCC9yò$óçÏWs±hß¾=NNNÊ@ÂÀÀ@Μ9Ç~X T‘çΣS§N„……1vìX|}}9}ú4{öìþó¿ÎNÕªUÕÊßìió^&:: ÇtYžžž*éÞ6nÜÈáÇiÑ¢6l k×®8;;³oß¾|_rr2ï¾û.gΜaÍš5¼ûî»ù®[Ôh2⳦/§RÓ××ÇÐЄ„y¬K1—.]===•ÍÎÎŽáÇ«}o¹ùçG·gÏž‘ššª±O===Ú·oOÿBžáIMM¥o߾ܿŸ]»v©CLL *T |ùòjuíííTÞÚH²ÆsÞÈèbÂäÉYþÐ{÷‚Ÿüò x{g5 üý!>¶lɲ²/]@Ô«ßž¬×UÝ»gÑIIÀÊȈr˜1Ó6ÆÆy i‘qãÆ1iÒ$.^¼ÈºuëÐ××gÔ¨QjcõêÕ¤¦¦²jÕ*š5k¦,߸qcŽuNž<©VvìØ1@³q­ V­¬œõ;vÌ·_l§NèÔ©‰‰‰øûû3iÒ$&OžL÷îÝ󬛚šJÿþý9rä¿ÿþ»ZÜ›DÅŠñóó£S§N´oßžƒ*g„á¿sûùçŸãêꪬ¬¬¨P¡•*UbõêÕZésäÈ‘œ|77·ë999amm͆  ìsYºti<==éß¿?„††æ*Ÿ––ÆÀùóÏ?Y²d‰Zм—‰/öjÔ¨Á‰'°²²¢S§Nœ9sF¹¯M›6Z÷ñmÓ¦ gΜQº%3fÌÀ××—9sæädÙ¼ysR]I!((ˆÐÐP6l˜cjF‰$7Š×èÿsâÄ †® ˆÑ„¥%lÝ FF––åýìDDDð÷… üÝ¥ Ÿ9Ãß?ýÄßÏžñ7ð{¸9ð7FFãæ¡­Czmd¡fd¡äU©P¡ƒÆ××·ÀÁƒ 7nLrr2Ó¦M#""‚ÄÄD~ûí7匲&LMM5j‘‘‘¤¤¤°aönÝJóæÍU‚ _¦T©R,X°€þù‡¾}ûrãÆ 222HJJ" €/¿üRé:rìØ1&OžL`` )))$''súôi¶mÛ†……• 90bÄvíÚE÷îݱµµÅßß_e»s玊|›6m077WËðqâÄ e””åL¸¿¿ÿk-ÏýªT­Z•'NPµjUºvíÊÑ1Ré IDAT£G¬Yý°xñb¦OŸ®ô{~úô)äý÷ßÏÕ½æUùùçŸÑ××çÝwßåÀ$%%‘‘‘Á½{÷X¼xq¡-"³}ûvfÍšEÏž=4h!!!*[BB'N¤bÅŠÌ;—žžÎíÛ·ù"8ÿ;…ÿä ._¾¬ü>¾Ý»wïÆßߟ½{÷ŠîņΉ't­JñGHŠ///Ñ¡C‡|Ëÿô“µõê%Ä7Eó ÄÒ²eÅRÈÚôõ•ÿÿ¤WFÌ¥¼¨gTAܼy³¤pñööÏž=ÓµÅ///]« svìØ!ÌÌÌt­†Î;v¬Ä•+Wò]çÂ… vvv"---WÙ£G @L™2EY/ÜÝÝ ôõõ…¾¾¾°±±'Nœ€ðööVÊ @Ìœ9StïÞ]ÂÐÐP¢I“&"44T¥¿wß}Wã÷ºjÕ*aaa!add$ôôô ¬­­Å¡C‡„B:tH @èéé CCC¡¯¯/ÜÜÜıcÇò}žÃ–*³Ð€cz–˜˜œgÉ’5y¦r*N,^¼X)-ùŠR—”¬­­‰ŒŒäÊ•+Ìœ9SÍ÷X"‘H «Ú,% i@k‰×YÕgÈËÀÀË¿à þ[¦ô ,_ðå—L˜ð€Ë—/¿¾²ZB®D¨és&‘H$]"W"̹á@ÖÊ‚õ€öe8ôâÿ&€× Ip%‘H$‰DRÔHú $Œ…L¦#eI Œ7Ï÷Y"‘H$‰äMFºph‰ÂuÈwæ/°7àÍͼ!ƒ5#ƒ%‰D¢KdaÞHZK¶C~¿p‡¥…Ú¦¶‘A„š‘A„‰D"Ñ%2ˆ0o¤ ‡–(|‡üº…ÜžöY¸PºŸhBf‘˜˜¨Læ/‘H$’ÂáÔ©S´nÝ:WD˜7Ò€. .ääɓҵkW† ‚‰‰‰®Õ’HÞ:ªU«†§§§®ÕH$’·ŽÖ­[çi@KòFРjÕªL›6””¾ýö[âãã™4i’®Õ’HÞ:7n,gâ%‰DRl‘>Рÿþ4jÔwwwÌ™3gò]W:ä«#ƒ5#ƒÕIOOçßÿյŎèèh¢££u­F±CŽ-š‘c‹:rlÑŒ´YòFÐdãÆ|úé§øúú2uêÔ|×{]‡|cã¿05]“ëflü×kõ¡md¡fd¡: ,^¼X×j;N:Å©S§t­F±CŽ-š‘c‹:rlÑŒ "Ì›·Ú…#==ôôô\ý”¿>«W¯N©R¥”剉‰œ?[[[œœœ°°°ÀÊÊŠçÏŸsëÖ-6l˜§¯»¼vëÖ­9p ?+ö9P¯^½×êK"‘H$‰äum—··Î€NHH`âĉ\¼x‘   222Bh”]¸p!Ó¦Mãùóç”*UЉ'2oÞ<ôôôˆgóæÍ´hÑBi@wëÖnݺѩS'¼¼¼ lÚ´‰ÿýïüôÓOØØØ°lÙ2–-[†——™™™$''+ëáêêZäÇ“·nÝ*t¶‚¶™žžÎž={ µÍ×eÿþý*ß“.Úü÷ß ,Ô6_·yøðá<_«…ž9‘œœÌþýûuÞf^Ç\zæ†[4#ÇÍȱE9¶” Þ:ÚÊÊŠC‡1oÞ<š6mš£Ü¢E‹pwwgܸq”)S†¾}ûÒ¿~ýõW233ÕäŸ?Ž““:u¢Q£F¬Y³†¯¿þ:ßz¥¥¥½ÒñäÄÛð‹‰‰)ô@Ÿ·á![ ýòÛþKOO'&&¦@ýåÅÛð‹‹‹#..®@ýå…[4#ÇÍȱE·al)l›åmDOääßðàããÃW_}¥æÂ‘––F©R¥øðÃùý÷ßÕäÿùçÕÚKOO'$$„Š+R¾|ù|ëѱcGΞ=‹››FFF*ûlllT|¯CCCóUvùòeÌĮ̀Y³fëæT–––†‘‘‘2z^u3228þ<•+WÎQNˆP­Z5µövïÞM:u°··e_.;räÍš5£\¹r…v^ÂÃÃiÒ¤ ÆÆÆùªNjj*9Ê]½z•† bll¬ÖžŸŸnnn¯¥óËe }ûö…Ö^hh(!!!´mÛ6ßu/]º„³³3?ÎQîÂ… Êü¤Ùë&$$pôèQ\]] í¼¤¦¦ròäIZ·n]hçåîÝ»DEEÑ¢E‹|×U,j“œ¹¹9AAA4kÖL­îåË—¬‰ƒÂ:/¡¡¡DGGãââRh祠㕡¡!çÏŸ§E‹¯4^íÞ½›:ðôéÓB;/çÏŸÇÊÊŠÊ•+ël¼ŠŽŽ&55•êÕ«¿ÒxåççG¿~ý^ûÞÏ^vêÔ)ªU«¦Óñ*00gggLMM <^¥¥¥qôèQzõêUhç%55•€€lmmu:^å4ž*È>^)ü£££133ãÊ•+4kÖŒ¿þz³’h“i@‡††booÏÏ?ÿÌ'Ÿ|¢,ß»w/={öäøñã*7Úëòû￳qãFµr¹ÒD"‘H$]£)`ÐÓÓ“Q£Fé@›7ƒ·.ˆ0?ÿüsôôôˆŒŒÔµ*:'99”[JJŠ®Õ*\¾|™–-[R©R%œœœä³xçw”×I•*U000(ôø¨7‘ãÇÓ­[74h@¿~ý´š‹½Ø!ÞBÜÜ܄ڶvíZ¹U«V ;;;KKKñÝwßiUÏ>ø@üðÃ"))IŒ=ZLŸ>]«ýWΟ?/D:ut­J±áèÑ£"22R!Ä‘#GD5DzzºŽµÒ=áááÊó°aÃÑ¢E kT|øûï¿Å€„™™™ˆˆˆÐµ::'))IØØØèZbÇãDžضm›HOOÏž=©©©ºV«X±nÝ:Ñ­[7]«Q,puuB±hÑ"ñÞ{ïéX#ÝñVfá¸páB¾äFŒÁˆ#xúô)E¬•:§NbÑ¢E˜˜˜ðá‡2mÚ4­ëPiÚ´iÉþU«ìK¹·oßžGA•*Ut§T1 {¼BõêÕyþü¹µ)>¤¤¤ðÉ'Ÿ°}ûvœu­N±!55___š4iBݺuu­N±`åÊ•4kÖ 777îß¿/]5ðûï¿3qâD]«Q,HLLTŽ»vvv$%%éX#ÝñVÐEÆsxx8iiiT¨P.\¸@ff&úúo¥g¤X¶l­Zµ*ñƳ‚Õ«W³råJ\DgC__ŸN:qûömV¬X¡\˜Âаd?oݺſÿþËÀ)[¶,FFFìÙ³G¹KIçŸþ!((ˆÞ½{ëZ•bÁæÍ›éÛ·/U«V%..ŽíÛ·ëZ%Q²GŽ× $$„àà`*T¨ \!ìež?ÎÉ“'¹ÿ>Íš5£Q£FÊ}ddd(?+ g==½"×½¨ˆˆˆàâÅ‹„‡‡ãææ†›››F¹ .pøðaRRRh×®:tв¦Ú%11‘«W¯K³fÍrüÁvëÖ-NŸ>••mÛ¶ÅÜÜ\MfïÞ½,]ºô­ð~òä ×®]#%%…nݺi”ÉÌÌäܹsP·n]Z¶l©ö`ïÓ§7fݺuüßÿýGÕ†úEB\\—/_&88[[ÛÚ÷ïßg÷îÝùä===ˆ3fh”[µj•044¢OŸ>ÂÈÈHÌœ9SM.((è­ðîÑ£‡000P^/GÕ(7oÞ<¡¯¯/4h lll„¸zõªŠÌ¾}û„³³³ Ó‚æEÇ“'O„ƒƒƒòœä4 %''‹~ýú cccÑ´iSQ¦LѪU+ñìÙ3òééé¢\¹r"**ª(Õ/2vìØ¡¼ôõõs[.]º$¬¬¬„«««2dˆ°°°Ý»wIIIB!6oÞ,êÔ©£Üôõõ…£££ ÒâÑ…=¶(˜0a‚˜7o^i]ôÖØâçç'>üðCåç©S§æzÞŠ3=ÆÆÆÊ{(§±åÉ“'ÂÍÍMT®\Y :T8::Š5jˆ{÷î©È¥§§ ;;;qýúu-h_t–Ý!*T¨ 222„B<|øP”)SFù¹¤! èrùòeñË/¿ˆãÇ‹víÚåx!öìÙSÔ­[WÄÆÆ !„Ø¿¿ÐÓÓÛ·oWÊ 6L|õÕWâÙ³gÂÓÓ3ÇÛÀñãÇűcÇD\\œ(Uª”Æc‰eÊ”“'OV–-Y²Dèéé‰àà`eÙåË—ÅæÍ›…½½½8{ö¬ ÑÆ! ¿üò‹Ø²e‹Ø¸qc޹‹/ @,_¾\!Djjªhß¾½¨W¯žRæÀ¢jÕªâܹs"""BDDD¼±>±±±â»ï¾{÷îŸþy޹ùóç‹Ò¥K‹€€!„aaaÂÆÆF|ôÑGJ™“'OŠ””‘ –.]*Zµj¥•c( nß¾-vïÞ-"""„‡‡GŽcKëÖ­E«V­DZZšBˆ«W¯ CCCñûï¿k”Óƒ kl ÇŽ<7n–––âáÇÚ:ŒB§°Æ–Ç‹ªU«Š%êÕ«'µu…JBB‚ظq£?üðCŽcË´iÓ„………xðàBˆ¸¸8áèè(>øà¹]»v‰¦M›¹ÞEMaÚ-µkׇâ×_Ý»wׯ!K¤ýäô úúújY=jÕª¥2ËüðáCÑ£Gå_Ô*k…œrË—/€8þ¼²,::Zbêԩʲ>úH 4H¹½œ=åMäìÙ³9>äÆŒ#J•*¥’UcÆ gΜBd øÍ›7WÙÞÔ‡\væÌ™“ãC®víÚ¢sçÎ*e£GfffÊÙÖqãÆ‰š5kŠzõê‰?þøŸ)RÓØrûöm¨ÍœÖ«WO´lÙRc[~ø¡ˆ‰‰) 5µÎëŒ-ׯ_-[¶Ÿ}ö™8{ö¬¶Ô.R^wlQ”5jÔHtìØQ¬[·Nj9¹-•*Uï¼óŽJÙG}$J•*%âââ”eK—.;wî,R=µM^vËìÙ³UÊ_¶[Ž;&,\]]ÅÈ‘#ßš1÷U>ÐEÀµk×ÈÌÌT‹~¯_¿>'OžT~¶³³cÏž=ÚVOg¡§§§r^,--±³³#((HY¶hÑ"]¨§3®\¹B:u000P–Õ¯__¹¯E‹Ìž=›Ù³gëJE­“˜˜Hpp°šotýúõY±bwïÞÅÙÙ™¥K—êHCÝ ÈNãââ¢R^¿~}öï߯±ÎÊ•+‹\/]£[²gÖxyl©W¯[·nÕ•Š:!?c €§§'žžž:ÑQÛÄÅÅñðáC† ¢R^¿~}RRR¸wï 4ÈÑúmDa·h[²Û-íÚµ£]»vÚV¯X"Ó=ááá€æ‡\tt4©©©ºPK焇‡S­Z5Ê”)£R^¿~}å9+‰„‡‡«]+NNN•Øó’Û=”}I#·óòìÙ³›R*<<{{{Ê–-«R.Ç9¶¼Œâ¸5Mpeß_ÒvKÁ‘t˜˜ LQ§ÀÂÂ!D‰}ÈeddhŒd666VÉHRÒHLLT»VŒŒŒ([¶¬òZ*iäveß_ÒPÜ'ŠŒ ÷UI½222ÔΠȱEŽ-ê(®‡—ŸE%ý’vKÁ‘t`gg ¶È7055ÅÌÌLjéœJ•*qçÎ’““Uʯ_¿^¢óÕÚÙÙ©]+<{öLy-•4r»‡²ï/i(îÅyPpýúuÊ•+§6[Rc‹f䨢Nn÷Pöý% i·i@ööö€æT±¯$âèèHff¦Ê Çýû÷qttÔ¡fºÅÞÞ>ÇÁ¼¤^/ÖÖÖ”.]:ÇóR­Z5]¨¥s÷ÉÍ›7Uʯ_¿^¢ï!ÅØrûöme™[䨢 ,,,4žt£˜Ž‘vKÁ‘tдiSªW¯ÎÙ³g•eqqq\¿~AƒéP3Ý2xð`ŒŒŒ8xð ²Lø4lØ0]©¥s Ä£G¸{÷®²ìÔ©S”-[–wÞyG‡šé===ÈÅ‹IKKS–Ÿ>}švíÚakk«CítGƒ puuU¹‡BCC¹qãF‰¾‡cË”erl‘cKN :”óçÏ @ZZGŽ¡gÏž:Y™¸8 í–W@×i@Þ4ž}ºhݺµ°´´TæœÛvKÑ ÓØŒŒ "##pwwP~NOOWÊyyyQ¦LÖ¬YÑ#GhÔ¨6lxkýÎ,,,hذ!€ò/@¹råTä¦M›†³³3{öì!>>ž… 2bÄ­êªMbcc•ׇâW|dd$111JSSSNž<É´iÓX¼x1–––,Z´ˆ±cÇêDgmðøñc?~L•*U4hòelª_¿>ÇgîܹøøøP«V-öïßÿÖ.ýnll¬ñ(Uª”òÿ.]ºpâÄ |}}¹qã£Fb„ ”/_^«új 9¶hFŽ-šqvvÆÎÎNí²´´Tþ_µjUNŸ>ͲeË U«V¬\¹’Úµkk[]­ í–¢AO!t­„D"‘H$‰Dò¦ } %‰D"‘H$’ h‰D"‘H$‰¤HZ"‘H$‰D")Ò€–H$‰D"‘H €4 %‰D"‘H$’ h‰D"‘H$‰¤HZ"‘H´L@@ÁÁÁºV#W"##9~ü8ÑÑÑ÷Ÿ?ž\Ûxúô)ÇŽSÉ5+‘H$oÒ€–H$%wwwÜÝÝ R)¿ÿ>îîîüý÷ßZÓeÒ¤IüüóÏZë¯ ÄÆÆÒ¢E ðòòâòåËå† Æš5krmëüùótèЄ„eÛkÖ¬áñãÇ…­¶D"‘h¹¡D")œ;wÈZ±î?þP–'%%qîÜ9•ÜJ2{÷îåÞ½{ýôSÚµk‡µµ5ãÆãÙ³g8;;3|øpÊ•+ǨQ£°²²âСC*õ X¼x1•+W¦AƒüðÃDFF²cÇ~ýõW’““ùßÿþ‡‹‹ åÊ•ãã?¦iÓ¦¬X±B¥-www¾üòK¬­­©P¡‚F=·oß΃˜;w.-[¶¤R¥J,^¼kkk,XðJÇ^³fMf̘Mš4aàÀ8p Àíboo¥¥%†††tëÖí•t’H$m ƒ%I‰ã‡~ qãÆøùùáììüZmyxx(ÿwqq G*2ÎÎΨ”µlÙR\ЬY3Ê—/¯œ¥½zõ*eË–å£>"33!™™™<}ú”ŒŒ •¶ºwïž§ž·oß TªOIDAT¦\¹r¸»»+Ëôôôèܹ3—.]ÊçѪòòq6jÔˆï¾ûŽÔÔTŒóÝÎøñãéÝ»7vvvôéÓ‡‘#Gªè)‘H$Å i@K$’GÆ 4hÓ¦McÛ¶mjûõôôÔÊ^¸SP¦LµzÙËå/Å™˜˜¨|600ÀÈÈH™3999+++5ã¸wïÞ˜™™©”UªTI£nÙIOOÇØØ}}Õ&&&Ê Ä‚òòq*Ú.h`ëÖ­ cݺulÛ¶V­ZѺukŽ;¦ñ»H$]# h‰DR"™={6uëÖeݺujû\\\Ô²YL—.]´¢›D"‘é-‘HJ$ŽŽŽŒ9’E‹©í{çwصkaaa¤¥¥±mÛ6Nœ8Q¨ý?þœÙ³g“˜˜HTTsæÌÁÌÌìÿÛ»ŸÔ¡0Œã †˜Xˆ©?–Nþcb–«£‹ÆuóïppbrQFØ ‰ Ñ!]p'ð†æoŒ'!÷ÞÜûýl§Í9íéô¤yûV’>Ê‚Á öööôððàÏ{||T¡P0¾Þææ¦,ËÒé驞žžôöö¦³³3ÕëuíììŒm__±m[‰DBWWW#!úòòRÝn×Ûç ?¨€¿ Àëèèè—Ç·¶¶Ôl6µ¸¸¨™™kwww¬×Îf³º¹¹Ñ‚‰„®¯¯u~~î8??¯\.§|>/Çq亮b±˜R©”ÿS“““º¸¸P¡PÐÒÒ’lÛÖÁÁµ¶¶6Ö½}e_¹\N~ËÉɉ¢Ñ¨–——å8ŽÖ××µ½½ý[ï LÞ‡}„àV.—5==í÷ªÕjêõz²m{¤¦·ÙlªX,ʲ,­®®ªÓéèååEÉdRÒÇ[RÏó亮?g0èþþþÓZµZM¡PÈ/ÅŽ#‘ˆJ¥’<ÏS&“ñ»Pü¬×ëéîîNÕjUSSSJ§Ó#ñ+—ËšU8þÖshµZº½½U»ÝÖÊÊŠæææFÎ{ž§~¿¯x<þå:•JEápx¤_s§ÓQ½^W*•R P·ÛÕóó³ÇùT{Ýh4ôúú*×u5 T*•T©TdY–2™ŒB¡Ð·ö0@ `€  @Ѐ4`€  @Ѐ4`€  @ЀÈþ*K½òévIEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/Q8-1g-idx-SSD.svg000066400000000000000000003751151231437614300237430ustar00rootroot00000000000000 PyTables-v.3.1.1/doc/source/usersguide/images/Q8-1g-idx-compress.png000066400000000000000000002723121231437614300251250ustar00rootroot00000000000000‰PNG  IHDRÐß}™SsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwxÕúðïlßM¯$z‡P5R.RT$H)^‰» "(Vlˆ^ôwQPA/ ¼U ¢H»ˆBBB Hz/Û÷ýývÈff³©»I|?Ï“'É™Ù3gfwϾ3{Þ3cŒ1ÆcÕ¢ðucŒ1ÆkJ8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€f5R\\Œ£G"''Ç×MñºãÇcîܹ8s振›â–ÕjÅ®]»°páBÌŸ?ß×ÍiR>ûì3>fÍÔ3Ï<ƒ?þØgÛ/++ÃñãÇqæÌ˜L&Ÿµ£®þ÷¿ÿá™gžAYY™XÖï›eË–á7ÞhºëÛ?ü€¹sçâÏ?ÿ˶lÙ‚åË—û°U¬!qͪeݺuèÒ¥ ‚‚‚pýõ×#""­[·Æ’%Kàp8|ݼzóË/¿`áÂ…¸té’dYRR–/_Ž .x¿aÕðÇ 22·Ýv¾ÿþ{>|Ø×MjR¾þúk¼÷Þ{¾nk+W®ÄŽ;ªµî[o½…;ï¼:t€ ®Õ6–/_ŽN:! ýû÷G÷îÝa0п|úé§°X,µªÛW.\ˆ_ýƒA,kÈ÷ÍÆñé§Ÿ6HÝõíøñãX¾|9²³³Å2Ì;'NœðaËXCQùº¬q#"ÜsÏ=X·núõë‡+V wïÞHIIÁ矎gžyß~û-vîÜ ???_7·ÎŽ?Ž7ß|'NDëÖ­]–ÅÆÆâÅ_D§N|Ôºªýë_ÿBAAΞ=ÛhÛÈXc÷üóÏC­V£_¿~(((¨Õ‚¼¼<Œ;‡ÂàÁƒñàƒ¢{÷î0›Íøí·ß°sçN$$$@«ÕbêÔ© °õïÈ‘#øöÛo%'"“'OFll¬ZÕ¸9ݺuÃk¯½†Í›7ûº9¬žqͪ´~ýz¬[·“&MÂúõë¡ÓéC‡Ž÷Þ‹ùóçãŸÿü'–.]Š—_~ÙÇ­mX±±±úƒâìÙ³hÙ²%ÏŒÕÁÉ“'Ñ©S'( ôíÛ·Vß8-\¸‡ÂâÅ‹±hÑ"(×¾ì½ýöÛñüóÏã“O>Ahhh=¶¼a½úê«èܹ3ÆŒãR>yòdµ¨ñ>ú(yäüñÇèÑ£‡¯›Äêá`n™L&,\¸Z­ï½÷ž<; ‚€×_111xûí·‘‘‘!.{øá‡ñðÃKê>_ý5vî܉qãÆ!** ¯¾ú*üqL:D$©ûìÙ³ˆÇgŸ}ævß{ï=ñ«Èyóæ!>>ñññX³f €ò1nñññ8zô¨ø˜¯¿þñññ8sæ V¯^áÇ#&&3gÎDRRàĉHHH@Û¶mѧO·c1333ñÀ oß¾ˆˆˆÀСC«l¯ÓÑ£G_ýyyyb»?üðCqsçÎaÚ´ièÚµ+Z¶l)ÃÊž{î9LŸ>eee˜;w.úõë‡ÐÐPã6Íf3/^Œ!C† ""mÚ´Á¸qãðÃ?¸¬—––†»ï¾ݺuCtt4n½õVlß¾]RßâÅ‹1yòd˜L&<ùä“èÕ«ºwïŽ Àh4þóŸÿ`̘1hÑ¢†ŽãÇ»ÔqùòeÄÇÇcÓ¦Møî»ï0vìXDEEaÈ!Õ:®NÅÅŘ?> €ððp 0o½õ–Ë•É7">>^ruÎf³!!!“'OFqq±Çm¥¥¥aêÔ©ˆ‰‰Áu×]‡E‹¡°°Pò|:ŸóÊÇ–,Y‚ñãÇKÊ ñÄO ..ááá8p –-[&y¿T<öO=õ®»î:„„„àðáÈÇçŸ.Ûö¥K—bìØ±(--­r¿þúk̘1Ý»wGHHzöì‰Y³f!==]²îÌ™3ñôÓO#;;³fÍB§NбcG<ôÐC²Ç3//÷ÝwÚ·oØØX<úè£ÛSY—.]\Þš:zô(>úè#ôïßÏ=÷œl]‚ `Ö¬Y¸õÖ[ŲŠ}ãwß}‡ñãÇ#::Ï?ÿ¼¸ÎÊ•+1lØ0DFF¢OŸ>xøá‡‘ŸŸ/.'"Lœ8K–,qÙÞºuë/jñá‡bôèÑQJJ ¾üòKÌš5 ‚ ¸,“{½9û“É„'žx½zõBëÖ­1}útÙçÙù>ïÑ£:v숙3gºŒ%®ìÊ•+˜={6úôéƒÈÈH 6 7ntYç­·ÞB||¼¤_ÈÏÏÇí·ßŽûï¿ßå=üûï¿cúôébß4räHìÝ»Wvû›7oưaÃ…Ûn» ;wîtÛÖ{î¹*• ï¾û®ÛuXEŒ¹qôèQ@wÝuW•ë½øâ‹€6oÞ,–õéÓ‡úôé#Y÷øñã€Þ}÷]—ò'žx‚PTTÍ›7¦M›F†‚‚‚èôéÓâz‡"4nÜ8 £û￟ž{î9ú÷¿ÿMË–-#ôý÷ßK¶ûä“O:uê”Ûýغu+5ŠÐßÿþwZ°`-X°€¾ùæ""úüóÏ €ø?ÑÊ•+ Mž<™´Z-Íœ9“&L˜@ …‚zôèA'Nœ ˆˆºá†èñǧÐÐP@›6m’눈R©T4zôhZ´hµk׎ÐÓO?]åñOJJ¢ PëÖ­)00Pl÷Ž;ˆˆèÈ‘#H!!!ôä“OÒ+¯¼B}úô!ôÖ[o¹Ô5jÔ( ¥›nº‰LÏ>û,Íž=›JKKÝn¿°°zöìIèŽ;î 7ß|“^|ñEºõÖ[iÞ¼yâz'Ož¤ ¤yóæÑ«¯¾Jýû÷'ôÊ+¯¸Ô9aÂò÷÷§Q£F‰¯ g›}ôQúôÓOI£ÑД)Shúôé¤R©( €rrrÄ:NŸ>MhäÈ‘BóæÍ£·Þz‹ @è7ÞpÙæ]wÝEZ­Ö¥,--Ú·oO‚ ÐСCéÅ_¤^½zš2eЏžÉd¢¾}ûRpp0]¸pA,Ÿ?> µk×Vù={–"""(00æÏŸOK—.¥ë®»ŽÆŽKèñÇ×ýæ›o}þùç’zäö#55•Ú´iC‚ Ј#è…^ =zºçž{ÜûÐ3Ï!"¢¿ÿýï€n¾ùfZ²d =ðÀ¤V«©uëÖ”žž.Ö3bÄjÓ¦KÝ#GŽ$Ô»wo—ò¸¸8êÑ£‡Ç¶mذÐÎ;%Ëä^oÎ~ä–[n¡ÈÈHš;w. 2„P×®]Éf³‰ëÚl6û,µiӆР/¼ ®—žžN-Z´ Î;Sqq±X>~üxR©T”˜˜(–mÞ¼™t: š1c=ñÄN …‚þýï»lßù9Ó¿Zºt)ÍŸ?ŸÂÃÃÅÏŽãÇKŽQ×®]%Çž5}@3·œãóÏ?_åzëׯ'´xñb±¬&ô¾}ûÄγbÇzþüyR*•/–9hAè÷ßw©;??Ÿ ÝqÇ.åf³™ÂÃÃéoû›Ç}vć’,«*€nÕªeee‰åK—.%¤V«iÆ byff& ‚@7Þx£Xf·Ûéºë®#Nç|ÙívºóÎ;I©TVø; 6ŒÚ¶m+)2d ‚àRGii)uïÞôz=¥¥¥‰åÎÛo¿‡Çm=öØc@òACTX:Ý|óÍ€Ž;&–FêÓ§i4JMMË'L˜@húôéd±X\êÂÂÂèÏ?ÿË?ûì3@o¿ý¶Xæ  Ю]»Är‹ÅB$½^ïR‡\ pçwúùçŸ]ÊŸ}öY@ß~û­X–œœL4pà@²X,ôÕW_‘ 4sæÌ*ŸÓŒ3€Ë»ÉdO2ê@7ŽA|¸;O,÷ïß/–9ým·ÝFv»Ýeý%K–:xð KùêÕ« €xÒV•üü|IÙW_}EháÂ….åmÛ¶£Š¯GçIErr²XöôÓOKNN‡¸®·è{ï½—Ð?þX£Ç9ûÆÊï"¢~øÐĉ]Žƒ³OzðÁŲW^y…PJJ •¿†ôz=ýío#AÄ~*??Ÿ =úè£ÛöÜsÏJJJ’,s@ 3fÙlËŸzê)Éëä“O>‘|~=òÈ#bÀídµZ)66–üüüèòåËb¹Ýn§qãÆ‘Z­vyM|ÿý÷$͘1ƒˆˆþùÏJNžóóó)""Bh;OŠ###©¨¨ˆˆÊOÒ)66–ŒF£¸îáÇÅçN.€7niµZ—Ï7ÖôñæVZZ eË–U®ç\~þüùZmçý÷ß,X°J¥R,o×®n¾ùfìÞ½V«Õå1}úô‘Œ' ÆôéÓ±}ûv—á$[¶lANNæÌ™S«öUÇw܈ˆñÿ›nº ˆ;ï¼S,ŒŒDÏž=‘šš*–ýôÓO8zô(î¾ûn´mÛV,W(¸÷Þ{a·Ûñý÷ßת]çÏŸGbb"úö틞={Šåƒ'N„ÑhÄ–-[$›1c†ä«Z9D„Õ«WcРA¸ûî»%˵Z- =={öìA÷îÝѯ_?q¹N§Ã¤I“`±Xd“l V«ÅÿGŒ"¨Q£\^—Îãí6SQxx8n¾ùfñµZ &Àh4â¿ÿý¯Û}ËÊÊÂæÍ›qË-·`À€.ËfÍš.Ã`:wîŒU«VáçŸÆœ9s0sæLtëÖ ÿ÷ÿçvNv»›6mB«V­ð·¿ýM,×jµ7nœÇÇW%-- ;vìÀ˜1cзo_ûá4}útÉðƒüãÐh4Xµj•Kù‡~ˆÖ­[ã¶ÛnóØžŠ³Z”••!##ýû÷GLLŒìl‚ `öìÙ.¯Ç‘#G(šå´aÃÉñwÜq‡Ç6Õ'ç˜é˜˜ɲ[o½Çž{î9É:ݺusyå¹(0mÚ4—ãpûí·C¯×cÆ b™óµ¾gÏåý‹ÑhÄ믿"¾}ûûöíƒÃáÀ-·ÜâqŸ~ÿýw(•J´k×ÎãºÍš5 Füßù¼%''‹eëÖ­—~¦L™"©oÿþý8uêî½÷^´jÕJ,wö•V«»wïvÙÞÂ… ±~ýz<þøãX¸p!F… ˆëlÚ´ ÙÙÙxüñÇáïï/–kµZL›6 YYY8rä`çÎ(**Âøñã]†4ÆÅŹôÝ•uéÒf³Ù¥ßgM'2·œJVVV•ë9—‡……Õj;IIIÐëõ²&iii°Ùl¸xñ¢Kr\||¼l]>ø >úè#|òÉ'xöÙg«V­BHHˆ¤ƒ®OcÇŽuùßÜ5 *•J²ìË/¿ÿw}'OžÄĉ]ÖµÙlPëŽ7%%Àµ®ŠFމ7ÞxCrâ£V«e×—sùòeFÜpà ujÇ‹/¾(i‡N§ÃðáÃ]ÊœÇuôèÑ.å­ZµB`` ËRN7Ýt“äd`ĈÊǺóÇ222$Ï‹³}•Ÿ—©S§bß¾}Xµjôz=6mÚT­Ùi._¾ «ÕêèWlk]tû‘––&»jµZ² …Bö=É“'cÓ¦MX¶l‚ƒƒqìØ19r/¿ürµÆ§¦¦âå—_ÆÎ;‘——ç²Lnʸ~ýú¹KЫW/Ÿo«ÕŠ+W®`èС’\ çsí-ζfffJNN…B›Í†={öÈî¯ÜqOIIB¡¼>´Z-n¼ñF|ÿý÷ÈÎÎFDDââ‽{÷böìÙØ½{7bbb0tèPÄÆÆbïÞ½˜2e vïÞ ¥R‰aÆyܧ‹/",,Ì%ö$ C‡u)«ü¼åïÁèèhtëÖÍeݺ´Àµ¾òÈ‘#’ײsJÀʯåÅ‹ãÀX±bZ¶l‰ÿûß.ýóý±}ûvìÚµËå±¹¹¹b#FŒû ¹“Ž#F`íÚµ’ràÚkââÅ‹èÒ¥‹ì:¬éáš¹Õ¹sgžƒ7çòöíÛ{¬“dü àï﨨(ɲ¨¨( 6Lò¡èîl¿ÿþ0`V¯^… "55û÷ïÇc=&©£>¸üïì +—;—U<ÊOXZ´h!YΜ9ˆ‹‹«U»ŠŠŠ@€T,«œŒŠÀÀÀÕ/·Ÿum‡V«u¹ú \;®•?XËä^_rÛtžV•ØçüðlÑ¢¢££%Ëd? KJJ”KAAAnë¯ÈÙŽªÚZ]•s?¢¢¢d÷cÖ¬Y’osÜž?øàƒØ°aÖ­[‡Gy~ø!”J¥x5»*ùùù¸ùæ›Q\\Œx±±±èÞ½;t:î¼óNÉ7MζTæ|8÷Õd2ÁápÈ«ÈÈH—o¶Z×®]”÷‹•¿¹pž8 $$Döñr}[QQ ƒlÀí|Í!""*• C‡ÅÞ½{ADؽ{·xß|óÍbÂÛîÝ»W­×hûöíqüøq˜L¦j÷£ƒArÜ+?o@ùk_nŸÕjµË·zÀµ¾²U«V—/ûÛߪ}ø£>† 0sæLlÞ¼Ó¦MÃþýû%ßBTæÜ÷ªÚZ‘3¨8TÉé×_•ÝáÇã™gž©Ö~TeÈ!ˆÅªU«0sæLlذcÆŒ‘ **Û»w/.^¼ˆ×^{Mü†(bNŸ>]ë)${¬<»Ý^«zkcðàÁ€5kÖ`úôéõRgÇŽqäÈüúë¯8p Ë²={ö@¥R¹¡Î@ùÇÄÑ£Gñè£(¿rº|ùrø >øà<üðÃ.WŠu>üðÃ’¡3rmÊ_Ç•Ožå^{NÉÉÉÐétb ΚÍÜò÷÷ÇK/½„¢¢"<ûì³²W÷V¬X3gÎ`Ê”). mÚ´AzzºËX7øâ‹/$uŒ1øÏþS/íž2e ÂÂÂðÞ{ïaíÚµ¸ñÆѽ{÷j=Ö9NØ›w¼á†àçç‡Õ«W×û]»víŠÐÐPìÛ·OâäW]ù*YMèõzÄÅÅaûöíUN;Õ±cG´hÑ€Ùl®÷vTåäÉ“’aHÎ@uРAn×¥K´lÙŸ}ö™xU¹*§NÂc=†¡C‡âã?Æ{gƒâ…^ðøXÄÆÆâÈ‘#âU¶Êm­¨M›6®|8=zT2¦gÏžˆŒŒÄš5kÄ+quõàƒâ·ß~Ãc=†’’’jŸ fffŠmªèË/¿¬s{à 7àÏ?ÿ”ô9î¦"k(7Ýt&L˜€Ý»w×hºÄª8_§•‡$%%!-- ×_½ËIšóŠóóÏ?»Ý.þ?lØ0¨T*ñäEnÈçóUùØÖ‡AƒÁn·KÐÊû ”ëÐétXµj•ìçQeÛ·oÇŠ+pÿý÷ã_ÿúžzê)|úé§.Ï‹sˆOÅi"Ýqž¼Tn[III•w~MNNF·nÝê4="k„|’ºÈš «ÕJÆ ³òwìØA/^¤={öÐ?þñ@]ºt¡‚‚—Ç9gæ=z4}:EEEIfá0™LÔ¥K ¤åË—SZZ•••QRR­^½šî»ï>q]ç,+W®¬²ÝÎlo´nݺío@@ 2„6lØ@û÷ï§sçÎQÕ³pTžµÃh4š3gŽdrYëï¼óŽ8=ß‘#G¨´´”®\¹Bûöí£Y³fÑ/¿üâ±íîfápN»4}útJII¡ììlzûí·I¡PÐðáÃ]Ö5jµhÑÂã¶*:tè ‚@ݺu£]»vQaa!¥§§Ó–-[\Žý| Nu—œœL999´bÅ R*•4hÐ —:'L˜ ;ûÁÖ­[ mݺU²,((Èe¦ç,!!!tÓM7QRRч~H††êòx¹çeË–-€H?üðRvv6ýüóÏ4oÞ<Ú¶m•OãÖ½{w £K—.‰Ÿ1c ‚ ;µbe7n§1KMM¥ÂÂBzÿý÷)22R2 ‡Ãá ~ýú‘^¯§%K–Pjj*­X±‚ºtéBááá’ýpNC6dÈJLL¤¢¢"ÊÊÊ¢Ÿ~ú‰{ì1úúë¯=ûŠ ÉßߟP›6m$³u¸säÈ@×_=ýøãTZZJ;vì ˜˜ v™q¨|ŽaÆIêqÎܳfͱìàÁƒâsõÛo¿Qii)}þùçMjµºÚ³p|ÿý÷´fÍZ³f µnÝšôz½øÿ–-[ªU‡súC4uêTúâ‹/è÷ß§äädÚ½{7Ýÿý€&Mš$>ÆÝŸDDeeeCAAA´yóf*(( 'NP\\œd–¢ò×GDDLSçœ2N¯×»Ì’S•ÔÔTÁeš8'w³pÈõ#ééé€,X –ýù矤×ë©C‡”˜˜HF£‘vïÞM­Zµ¢ÀÀ@Ékâõ×_ݱcÇľrÏž=” ΂qñâE ¡ØØX*++#¢k3ðøùùÑ™3gÄ:ï¹çyä:}ú4Fºxñ"mß¾&NœH………âºñññ¤T*iÅŠTXXH©©©4~üx ‘…£¨¨ˆT*•ìgkÚ8€fÙl6zå•WH¯×‹A©ógÖ¬Y²ÓRÙívqÞRçOÿþýi÷îݲW®\¡;î¸CR¿Z­¦Y³f‰ëU7€NII!A(44Ôeº¡êøøãiĈ¤Ñh\:û†  ­ZµŠ‚ƒƒ%Ç cÇŽ’)ûä¸  ív;½úꫤR©\ê?~Ü¥?¹ýöÛÅ©K+ÐË—/'…B!; kÚ¢j|ÂʧÚ:sæ N:…½{÷bÕªU¸çž{°víZ·c‡ûí7$''£GèÞ½;, ®\¹â6QíÊ•+øã?——‡èèhôîÝÛe\œÙlFzz:ªL\;sæ ºwïŽyóæáwÞ©Õþ–––";;AAA ÿoÑ¢ôz=€ò¤’ÜÜ\DGG‹›Û__ IDATS¶åI2/^”MÆÊÎÎFYY™lâŒÑhÄéÓ§‘’’‚ÀÀ@´oßÞe `U222`³Ùd§ÏÊÇ™žù¤¯›"1iÒ$òóósù:ýµ8¯@W¾ã`S“ŸŸ/{Ú—’““I¥RÑìÙ³}ÝæEcÆŒq¹ «ÚÎ;I:yò¤¯›ÂÏÂQCW®\ÁÊ•+qüøqqÞÉ¿ªU«Vá†n€ÑhDff¦ìlÞd³Ùðꫯâäɓغu+^{í5·Wcc5·ÿ~|ýõרºu+ðÚk¯ùºIÌ‹Þ|óM|ñÅ(++“|Ĥl6V®\)ÎÍš k衇ÂÛo¿[o½Õ×Mñ9¥RÙ w÷«)»Ý޵k×"** ¯¿þ:žxâ _7‰ùZ­FÛ¶m«=sc¥P(жmÛ}ÝÞPNœ8/¾øă>(;/k¾zöì)™A…¹7~üx_75 ]ëׯǑ#Gðî»ï"88gΜ‘½ùcŒ1Æk¾þ2W - .\¸Nç69Æápà?þÀå˗ѯ_?—! ™™™x÷Ýw«œ,1ÆcŒ5Í~VïŸ~ú ×_=еkWÜsÏ=²ëåååaðàÁèׯî»ï>DEE¹ŒïÛ¶m²³³1fÌ >%%%˜4iRRR¼µ+Œ1Æc¬höC8±mÛ6\ýõX¾|9´Z-öïß/Yï®»îÂ/¿ü‚Ÿ~ú -[¶Äúõëq÷Ýwã믿ÆèÑ£a4QXX(®ßµkW$&&¢{÷îoÓËcŒ1Æšf@W“É$  ³²²ƒE‹áÅ_Ë;tè€Þ½{cÛ¶m’ºœtDDDC7›1ÆcŒ5"|éÀ±cÇ`µZÑ£G—ò^½zá矖}LRRRµëÿ裰aÃI¹§0ÆcŒ5´´´4IÙôéÓqß}÷ù 5MC³]W®\Éô<½zõBvv6l6[êß°a~ùåX­V—ò´´4˜ÍæZ•;v ©©©õV_ZZRSS]ÞDžk·ÛqèС*×KKK물^bb"RSSëÔæÊe{öìAqqq½Õ—––†C‡¹Ìùíé±þù'Ο?_åz‡물޾}ûêÜæÊeß}÷]½Ö—––†Ôè±G…Ñh¬r½ÄÄDÙÇ:—Õçq±X,سgO½—sçÎáСC5z¬sŸÝ­W\\ŒÃ‡Ë>655U|ßÖ×~¤¥¥áرcõz\jÚ_9û–ªÖ«ª¿JLLûŸúÚÇãܹs>í¯œ}KUëUÕ_íÛ·¯Îm–ëÇ}Ý_9û–ªÖs×_Uìwêk?, >ìóþÊ]ê$×_åääÀjµâرc²þX¾¼‹‹·5І &)÷Ýw egg»”¿÷Þ{€òóóë´Ý#FP¯^½êTGe[·n¥­[·ú´N£ÑHsæÌ©u}ûö¥ .Ô¨ž<þøãu~¾êZç7ß|CŸþy­ëìØ±cÚW3gÎôy , ôôôZÕyáÂêÛ·o¶çI~~~½ßݯ6uz:ŽUÕ¹páBZ¸pa¶ç ÷-ò¸o‘Ç}‹Tsè[zõêE#FŒ¨Ñöþjx Î圜œìrc€Ó§OC«Õ"88¸ÎÛ(..ÆK/½„áÇcøðáu®¯[·nu®£®uªT*Œ;¶Öu6¬ÞorNçÓ:;uê“ÉTë:‡ R£öUÇĉ}^ç-·ÜÿZÕ„aÆÕh{žèt:ÄÇÇû¼NODZª: P£mU÷-ò¸o‘Ç}‹TSî[öïßýû÷£¸¸˜o”䉯#xorwúÇ$´jÕ*—òo¼±^ÎØGŒAƒ ¢óçÏ×ûŒ¦¬!®è4 qE§©kˆ+:ÍAC\-n¸o‘Ç}‹÷-®òóóéüùó4hÐ ¾í0pà@DGGãèÑ£b™ÉdÂo¿ý†I“&ÕË6t:ÚµkW/W³cŒ1Æê[pp0ÚµkWïß¶4GÊ—^zé%_7¢!bÙ²eHLLľ}ûŸŸ£ÑˆÄÄDôèÑz½ …J¥o¼ñ ŒF#ž|òI\ºt «W¯FHHHÚ°oß>dffÂÏÏY»ÖäuêÔ ‘‘‘P(ø<®¢nݺñWg•¨T*téÒ¡¡¡¾nJ£Ž6mÚÀ`0øº) ÷-ò¸o‘â¾ÅÕ©S§°wï^««àý÷ßGII‰¯›Ñè,Y²Ä×MhtJJJðþûïûºNbb¢K¦=+Ç}‹<î[¤¸oq¥Ó録²2_7¥ÑûKÝHÅWk×®õi;cŒ1Æ<á¸Å³fš1ÆcŒ±úÄ´—$&&"!!Aö¶àŒ1Æc¾¶mÛ6$$$ðаjàÚKâââ°víZ_AJJJïòØ9sÆ×Mhtl6RRR|ÝŒF'''999¾nF£Ã}‹<î[¤¸oq5qâD¬]»qqq¾nJ£Ç´—dffúº 'úÈãD)Nô‘ÇI„ò¸o‘Ç}‹÷-ò8fñŒ“½€ã3Æc¬©à¸Å3¾í%<š1Æc®>¾í|&ÇcŒ±¦‚ãÏø ´—˜Íf_7¡ÑáDyœè#ʼn>ò8‰P÷-ò¸o‘â¾EÇ,žqí%< _Š}äq¢'úÈã$ByÜ·Èã¾EŠûy³xÆC8¼€¿ aŒ1ÆXSÁq‹g|ÚK8‰1Æc'V_ö>“cŒ1ÆXSÁq‹g|ÚKx@¾'úÈãD)Nô‘ÇI„ò¸o‘Ç}‹÷-ò8fñŒh/áùRœè#}¤8ÑG'Êã¾E÷-RÜ·Èã˜Å3ÂáüUcŒ1Æš Ž[<ã+ÐŒ1ÆcŒÕÐ^³p0Æc¬1ãY8ªh/‰‹‹ÃÚµk1qâD_7¥ÑàDyœè#ʼn>ò8‰P÷-ò¸o‘â¾ÅÕĉ±víZÄÅÅùº)Ð^Âò¥8ÑG'úHq¢ò8‰P÷-ò¸o‘â¾Å•ÍfƒÉdBzzº¯›Òèq¡$$$àСC1bÆŽ‹±cÇúºIŒ1Æc.¾úê+|õÕWØ·o ÄI„UàÚ 8›•1ÆcMÇ-žñÆcŒ1Æj€h/áùRœè#}¤8ÑG'Êã¾E÷-RÜ·Èã˜Å3 ½„ïê#ʼn>ò8ÑGŠ}äq¡<î[äqß"Õú³¹þþ8fñŒÇ@{%bŒ1ÆX}3™."3ó3 Ý>‚ ®—z9nñŒ¯@3ÆcŒ516[!²³7ÈŽÒÒSHOÿ6[¯›õ—Á4cŒ1ÆXâpX‘•µv{©Xf±¤#=}5L¦ ¾kØ_Ð^Âò¥8ÑG'úHq¢ò8ÑGª©&ú44N"”Ç}‹<î[¤šJß’“³ ‹ûqÉaac¡Õ¶ª·íqÌâÐ^Âò¥8ÑG'úH5ÅDoà$ByÜ·Èã¾Eª)ô-……‰(-ýÝíòÀÀð÷ï[¯Ûä˜Å3N"ô¾£cŒ1ÆjÊhLAfæzò¡šN×QQ÷ ¾¯‡rÜâ_fŒ1Ækdl¶ÞÆI„ò¸o‘Ç}‹Tcì[ˆÙÙ[«¼›`XØxhµÑ ÖŽY<ãÚKx@¾'úÈãD©¦èã œD(ûyÜ·H5ƾ¥°ðÊÊN»]8þþ½´ ³xÆI„µ@D¡Úëó`|ÆcŒyRV–„¬¬p7îY¯ïˆÈÈ„†½þÉq‹g|ºFÖ­[£cÇŽ˜:u*Ÿ¡1Æc¬^X­9ÈÉÙ÷Iƒ!ŸÜàÁ3«~j`íÚµ¸téRRR…þóŸ¾ncŒ1Æš8‡Ã|5iP~ì±B¡A‹Ó Tê½Ü2æÐ5ТE €B¡@hhh’Tx@¾'úÈãD©Æ˜èÓp¡<î[äqß"Õú–ò¤ÁÿÂjuÿ^ ›µ:Òkmâ˜Å3 k衇B·nݰwï^¼üòËÕ~÷âDyœè#Õ}N"”Ç}‹<î[¤CßRX¸Fc²ÛåÁÁCáç×Ë-☥:þI„D„¤¤$$''#44C† ‘]¯°°ûöíÃÅ‹qà 7`РA’uòòò’’‚×^{ ±±±xíµ×ú(vìØ›nº ³gφÃápY/447Üp-Z„­[·zccŒ1ÖÌX,YÈÉÙ wÁ³Z†ððIðvð̪§ÙÐÑÑÑxçwpüøqÜ|óÍn×{øá‡€¤¤$ìÞ½»víÂÇŒ/¾øPTT„Ÿ~ú ‹iiixÿý÷qçwzk7cŒ1ÖL8&dem‘Ev¹B¡Edä4(:uY2,Èß_ßMd4ûºwïÞ¸ï¾ûзo_¨T*Ùu.]º„ï¾ûS¦LÁP~[Ì!C† K—.X½z5Àn·cÉ’%èÙ³'fΜ‰ž={bÞ¼yÕnÈ—âDyœè#Õ}#N"”Ç}‹<î[¤|Ó·²³¿€Í–çf¹€ððIP«Ã=Öd:gBÆš &¢ðÇÂzk!Ç,ž5ûº:Nž< ‡Ã=\éÇÆÆâĉ€|ùå—8{ö,öíÛ‡  88¸ÚÛØ±c¦M›†xÀå'9Ù5qà¯TöþûïcöìÙ¢-©lÉ’%¦-¥Ì™èÓÚҘʜI„¡-©Ì™DØÚҘʜI„¡-¥¬b¡·¶{äȧ0¯í‹}…óçsÅÿƒƒ‡ã‰'ÞõX_ɉÌš: ©WRù{óQò[í^÷Θ¤gÏž˜6m8Vµ¿D¡S||úè#(®ç,Æ¥Ìd2q—q—q—qY3,ËÍ=†ÂÂíP(®k6›mP«•P( Ý1EŒ;ÜÕW°¯?Àl3C­TCqõæ*‚J@Д v ¬QûL&“KÙœ9s T*9‰° òcþbŒF# ((È¥Üù¿óUFRN:މ˸ŒË¸ŒË¸ŒËšv™Å’‰’’o\‚gÐjËC1µ:·C·õ‘½%¥'K˫Һ¬C6BÑÖ"øÝïE¨Âå±UµÏù·ó·R©”¬Ï\ñ”'ÀéÓ§]ÊO:ƒÁ€ÀÀÀ:o#11 ضm[ëbŒ1ÆXÓa·—]M´Ê.W(thÑbÁýÅ:»ÑŽÌgŠÁ³;úz¨kw}tÛ¶mHHHàùå«híÚµüþûï.å¿ýö›¸¬®âââ°víZLœ8±^êk8ÑG'úHq¡Î€é‚©Êm DøäpªÚM}7qâD¬]»qqqµzü_ Ю¿þztìØÑåŒ+??§NÂÔ©Sëe|W)¾[˜<¾[˜Tc¸[XcÄw"”Ç}‹<î[¤¼Ñ·äçï‚ÑxÎíò›¡×wr»Ü|ÙŒŒ2`Í‘¿z €°ÛÂ:*‚P÷y£9fñ¬Ù'æääà©§žìÚµ v»ñññ€7ÞxC¾ñùçŸãïÿ;æÌ™ƒ~ýúaõêÕÈÎÎÆ¡C‡Ð¢E‹:µÁùuÈ!C0qâD¾ ÍcŒý””œDNηËýüb1Ùíò²ÓeÈÞ’ ²ºÕÂ«ç<+O¶mÛ†mÛ¶‰1 'º÷—J"9r¤ÛeÓ¦MChh(Ö¬Yƒ?þÄ3ÏpÜâœaŒ1Æ«£¢¢_Ÿ¿Ëír­¶%ÂÂÆIÊ©FdoʆÃìpûXA) lBü{û×K[YÝqÍcŒ1VKv{ rr¶Áht?–Z©ôCDÄ]×°«øh1rwæîcg(ô DÞ ];éí¸™ïðh/9pàßÊ»Nô‘lj>RœD(“åqß"û©ºö-eegðçŸÿª2x%""¦@¥º6ô‚ˆ¿;¹;ªžU!*DÏŠöZð켕÷¼²½¦Œh/i×®ßÊ»Nô‘lj>RœD(“åqß"û©Úö-Däæ~‰¬¬p8ªNú ‡N×öÚcm„œ/rP˜(?K‡“6F‹èû¢¡—Nu×Pœ·òn×®×¶ÙTq¡ð`|Æc¬y0›/#'g ¬Ö<ëÄ!,lŒø¿£ÌÌÏ3a¾d®òq†î„O ‡Bí›ëœ·xÆc cŒ1Æ< r °ð €¨Šq -BCGÃß¿¯Xf͵"s}&lyU/ „‘!€P/Íf „h/±Ûí0™LP©TP©ø°3ÆcM…Õš‡œœ-0›/{\W«mððIP«CÄ2sšY³`/³»}œ ::qõÒæÚ°Ùl°Ùl°ÛíP*•>kGSÀc ½äСC|#•J8ÑG'úHq¡ žk7R9tèOÛÑpí%mÚ´Á|€±cÇúº)'úÈãD)N"”ÇI„ò¸o‘Ç}‹TU}‹ÃQ†¬¬ÈÍýD–*ëQ«Ã= ÁÁà ×B«‚ ýßlÍ}º™2@‰¨{£`èb¨ÝNÔ£±cÇâƒ>@›6m|Ý”F“½€ã3ÆcM‡Ñ˜‚œœm°Û=Ÿˆ\‡QP(4× @îW¹(>V\åcÕ‘j´˜Ñª Æ5´“ãÏ×3ÆcŒ1æ#D6äçïBQÑ/×U( ƒ¡›K¹Ãì@ö¦lSU>^ßQˆ)Phy0@SÄ4cŒ1Æþò,– dgÿVk¶ÇuõúNŸ¥òÚ­µí%vX³¬Èû.–̪‡|ô@ØØ0HÛ„qí%eee< G%)))h×®JΜ9ƒnݺy^ñ/Äf³áÂ… èÔ©“¯›Ò¨8ÃÃÃ}Ü’Æ…ûyÜ·H•÷-ç‘…‚‚½ rŸèÔÒÜMa”¦Ú`ÉÎ5Ç k¶SÕSÛ]­!7… èÆ Ïëú€s޲²2 ¾“ݘñ¹—ò8ÑGŠ“åÕg!ÁjÍ„Ñx‚ €J µ:*U0¡i}øsß"¯9ô-d#Øòm.²5× kŽ£vm¬úó°êÏÁ®ÍTÿÚ\É)BëÈ`±Lph¡ÏMi펕 €&BmŒÁ#‚!¨„ZÕãKf³ZmíOþ ¸wñ’ÌÌL_7¡Ñyÿý÷ñÒK/!88ØóÊ8f©0™ÎC¥ „V­¶AãùÁÌ’%KxÜY%Î$ÂeË–ùº)Š3pâĉµz¼Í–£ñL¦ó0ÏÃá(“¬# (•AP«Ã®Ô¡âo•*¤Q×õÙ·4‡fs/ž‡Õ«ÿ &ju(û<³–t ,阯˜aI·À–g9®¥n‘`ƒ] VÃyXÃÏÁ¡¬Yòh™ÉŠ»ÿ‡ùÓ‡TæV0d‚ÂXõ@¨‚:\ uÄÕŸp54( û¸VGff&ßÐN"ôŒß0¬ÖgQV– ³9Mfú!M$´ÚÖWê¨Ta„¦w5€±ú`·_ –˃f›­°Ž5 P©‚\‚ꊿ¯Ñø‚ÃaÙ| &Óy˜L`6_à:ÖW”P«Ã¡VG@£‰„Jåüâr+j¯µùj°ì ”-W,°æY™…T¥°èÏÁf8«.­ÆC3äPB›?º¢ëªð¡Ô!Ò Y®† i¾Ÿ%·xƽ›—œ?7nDll,bcc}Ýœ&‰È“éŒÆ³0ÏÂjÍóôX,™°X2Q\| Pè¡Õ¶ªT·‚BÑ4²¢«)»Ý“éÂÕ@ê|µnQ3›­6[L¦s•– P©«®k’På€Ãaˆ,nÿ'2Ãá°@TP*ý PøA©ôƒJå/þ­TÐØ¯¾Ö„Ãa…ÅrFãy˜Í`6ÿéq>c"»Ø?––^+•K`}-ÀP?£Ãä€9Ý kºõZÀœg)Ìâ¡üo‡pµLi†]›,` IDAT›& ²‘u ¤‚ʵ±Ô–ŽÐ‡@ÝæZ¬ WA¦† l¾re§N©S§pþüy´oßÞ×ÍiÔ8€öFƒàà`èt¬Õ„ÍVt5`N†ÉtGÕwwòÄá0ÂhLјrµD€Z­6:]yP­VG ¾>(ó&"+L¦4ñ*³Å’Žú4jÙØl…°Ù a2—,U*Ä€Z”’`¸<v–Õý £+J¥¾B@íWáo±ÌY®P4®± D6˜Í—Å“£ò€¹~Ž‘ K,– ÙÀºâÕjµ:juy`MdÝn‚ÃQþSþü™`3•ÁœUKn1,y%°ä—Ân4‚„kÁ²Ãß Ô÷s,CTjhÕàçß †NІûA®†*HÅÝ>N‡àà`h4Moø£·qí%áááˆ÷u3ùD‚Ù|eeÉ0ÏÂbÉhàV¬ÖlX­Ù()9P(´ÐhZ‰µV…BßÀí¸¦©&úÙa·—]GKWƒC½Œ‘å$By99Y°XÒa0\ ¤.y¼òØXØíŰۋ\¬÷º/^ÌCLL0”JwW™ v{ìö²j]•¿v5[…BWéG®¬âO݃o";Ìæ?a6_¸z•ùR­æÔÔtìX»„ÓŠuEåÃt r€l{±ö{ùïb;ìF™×c_GT”%ztþÑð ¨ÐùÅ b¤,ö-ÁÜ·@§NЩS'|úé§¾nJ£Ç´—T?‰Ð‡ÃŽò©òo×2A®~ ª‚B¡† ¨¡P¨ÑT¾št&úha4¦Àd: £1v»4‘É›3L¦s._I«ÕaWƒiçUêÈ'Ø’‰lWƒa#Ž21Øp8ª*“ŸiF¡Ð‰ÁtÅßîþ– ¸›saùûºüjÝn¬pÏ$)»öS^öí·Ç@äÀ­·Öß — ¨ Pèa·—ÀwW¯ëæÓOcîÜá ¬ŸHÈvu¼xÍÇŒ ‚BT ‚4Ð.¿"~íÿòÄèò!&Sˆ¬uÞ•+ñöÛµK8Ù‡¥|þd²F3ì%vØŠmp«q+ëú"J}y¬4(¡0(®ý­UA§kƒ¡ ôú.P©Ü'“6ç¾¥.8‰Ð3N"ô‚„„”•%áÝwgÀüV†¯ý® 1 ¾T«<– ‚ÊeyÅ2…B "@èjÛIü»üCöÚßrerËíöR)°X._-«;¥2€­Áƒp…B¦%´Ú(•þ¨xEC„«Çª¼¬âßrëÉ=F  Š«º€ô@Q¡ìÚÿ•—9àp]^çß•Ëìö2Õm¨L]”q¬ªó«õòUœ¯'ÇÕ׌óÄÒ!)“[O~ç1¯ø¸>_reKŸ_ùeD6™@Ø_ª‚ €F ®=ôúÐhZ_}¿[aµæÁj͖̓›-6›óÿBŸ¶™ÕŽRé‡Ã(ûYCVº›¯þ¶\ûí,#»÷ŸwA-@ ‚2@ ¥¿J¿òÀ¹â ¥Òz}g ]¡×wl’305&œDè_ö‡Ã«5Ç[[»z5°ùÏ=]þásõJCgh4-Vk.ÌæËâÅ’‰ÊYèuápX®Ž?¼Pou²rÎàÒfó”$ÊjK­Ž€^ß:]{ètídiA ¦…øžª¨üŠlA…€ºâïÔç{ÕžRpõĨ= Êôƒ½Ô sQ¬æ,˜M™åCØìÙ°+óÁ÷Ï›B£(’”bÀ¬ÐÉÛ§VG^ ˜»@§sšÁXCãš59J¥z}'èta0t’Ÿ\>gmüýû(O®2›¯Àl¾$Õå_Q3Öü©TAÐéœs{¨TuªïÚL áÐKÞ~X­òÁµÍVPoÉn‚ „B¡… h Ph%;VØí¥p8J¯þ.«·oº+¥Ò:];ñyV«Ã`¾lFÁ70ž30]]S JÄ@‰ò!É$ØAêØÕ¹°©sàÐäÁ¡É…CUj "…FqíªòÕ€Y¡½, ‚J¥sØ‹VÞ¢ÕÆÀ`èZåÐ ÆÐ^b±4ÄoòœèsFÓz}ùUf­6¦ÆãA ®-tº¶b™ÍVPé*uz£HÀªK¢Ose·;pùrÚ¶ õuS•üüò¡J!!—r…½¾½4—ß4Ã[P«Ë§ª“×å3s”ÔùbP UÃÿvþ_ÕÓr ÊD"#ìöRñÇ\Ûí%’rwcú…B®ôúöÐjÛA£‰—Y³¬ÈÚ›…²3׆³¥æ¤¢cxGÙºRB°„Aa ƒ]*,°Ã®Îƒ] »&M.ìê\8TE¨j@*mùi Të¡òƒ:ÄÚ0hàôwŽû¾ ‚JeùooÌGÍ ÊòøN„žqí%99|µ³²ª}ÊÞöâÐ •*¨Þ·¯RC¥ †Ÿ_ù¼ÜåSC¥_ ¦Ëƒêºßh¢æê%ѧ™)-µàÓOã…x&›Š~ý5 0jToètmÅaju‹Fzà A|ß5$¹;–'] P®NUYµòDÚŠAv©L2§Ü 5>\¡ÐB«m{õäHþy¶æYQ¸¿%¿•Hš±2q%ÞžøvÍ6JJ(-PZ"€ ÓÚAaƒM• R•5¤-Tzt- ÐDk i©&ZU`ã 78‰P'zÆI„^€’’y RÃ`è®3ôúöâ.fv{ñÕùV/U¸J]÷lø¦F¡Ð\®Ëp5±Ï…B¥²üA¸öwùÔuª« Š¥âo׿Ë*|µnlö_­×ŒpõŠœtz´Š358grP*ý¡ÕF¡©Ì¾ÓüÑÕù«ågQqήRu^N¡Ð@«m#ËÐh¢Ý^•µÛQðCJŽ—x-ÑO¡ûöÎ;<ª*ýãŸ;3™™Òéz•jATD:ˆ((EPt£â.* "º‹»ŠèÏ®‹(¨kAQ„ * –(¨€H/$B ¤'ÓÛýýqSf2“̤Âù<Ï}fæÜsÏ=Srò½ï}‹â¯¬‰Ö k¡CÛ\‹®…u£úWÞ]P9D¡.¾B¹LÐéZ3%3‚%S†ÚãuÙGû%IÆå²#ËvdÙQôh÷j+}h›çXþ‚” ¥Ù®V‡Ü©(°I¹g¸X¨Õ éFHH7@ÉÇj·Ÿwsû¸PÔS.Ú¯ïï×÷>Åo·¬öÊÊ­ëúh=†Tò]V…â´†N§&Üou§ÉIþ¶| ÿ,D¶×@u>•„*´(/L­d¿S£ S•¼.~® m‚Ë! ëˆ¤¤s,Yò `À€56®Z]û©z”´{Å?¾ñåƒ$©Ñj[ Õ¶ Q£~y6¥‚º4½ §ð.Ý'—X5k¢°IM"IR‰h ¾ß‚ËåÂ0Øoñ&—ÕEÁüQ€Ëø] šHE»‹ãâçêPµHfq³mÛ6¶mÛÆèÙ³çÅžN½Fè:¢Y³fŒ7ŽØØKO H’ºVD—ïJ„‚À+JßMC6ZŠ@ßde)©1/Åu¥6iÈk‹ì)ØY@þ¶|\¦À…³>NÏù¶çé=¤w-ÎîÒC¬-žtíÚ•ØØX¶nÝz±§Rﹼ̇³Ù\òÃ(,^¼ƒAW–åå—_¾ØS¨wú<)¶ öœÀ˜5Ñ"GÚ#TøÔ *Ð-þix÷·@ h˜ŽšÈû)Ûy[ÀÇhÂ5D Œ Ñ•Ä=f Z ‚z„å„…Üs±ž¼˜‹:DMÄ€® Ù1‚:@ü•ÕVký¯jUפ¤¤àpÔLY߆DRRÒÅžB½Ãáp’’r±§QïÈÊÊ* $”r©®-Ž|V\ cYFÀâYÒJDÞIËG[~}x…âY¬-ÞˆµÅ7B³øGè:âüùó{ õŽèSˆ BoD oD¡o.µµEvÉüQÀÙ%g15ù?4áýÃiõh+"G¢Òùÿw.ÖoÄÚâ¡Yü#‚ëáŒ/_XÏXÉÞ-#0?gI%Ú;”ÈA‘h"„¦ vºÅ?â¯O ‚:Æeu‘÷cVTT‚n!D ‰"(6¨Öç'*FèJ`4Ù½{7QQQôèÑãbOG — ÆCFr6åà,tÔ?¸C0‘C#ѵÐÕòÌA èÉÎΦk×®¼óÎ;Ì›7nݺ‘‘‘ðñÂ!ß›K5Ч¶>Þˆ@߈ BßÔ׵ŞkçüççÉ\xÖ6×Ò,¾MïiZ#âY¬-ÞˆµÅ7B³øG艈ˆàرc¬X±‚ 60zôh–,YðñÂ!ß›K-Ч®>Þˆ@߈ BßÔ·µEvÊäoËçì;g1'û¯ð&i%¢FFÑüÁæèÛêklbmñF¬-¾šÅ?"ˆ°Š<ù䓼úê«~û g|@ ¸<±¤[Èþ&û…Àª†t !úæh (¸¨ÝâñZvíÚÅÊ•+Ùµk×ÅžŠ@ ê!N³“¼ò(Ü]P &BCôèhBº„ÔþäAµiðÚb±°k×.víÚűcÇèØ±#sæÌñÙ711‘åË—súôiúöíËã?Nll¬GŸÃ‡3yòdÖ­[G“&Mêâ-à°ß@îæ\œFÿ~Î’JÉç1(•VxU *&==ÇÔ·{÷îÄÅÅÕòŒ._¼€NHH`Ê”)4mÚ‹ÅBŸ>}| èõë×3qâD&NœÈ°aÃøä“OHHHà·ß~#::P0ÆÇgŸ}ÆUW]U©y‡|oRRRhÛ¶-MƒÿVФ¤$ºvíz±§Q¯p8¤¥¥Ñ±cÇ‹=•zEqaÙ ýË‹µ¶Ø³íd›å¸% þºV:bÆÄ m¦­å™)\ÊkË‘#Gøõ_9qúC¯JÿkûÓ¨Q£j{©­-N§“§Láa£±Â~²l÷î*ŸÇjµ¢Ó‰¬/Ñà/w‡Ê©S§ÈÈÈ ÿþåö›7o·Þz+Ÿþ9sçÎåÇääÉ“|ðÁ€’…cðàÁôïߟ}ûöñÞ{ïñÝwß<áïM} ô©/ˆ@oD oD¡oêzm‘2y¿äqöݳ‰g•^EÌ-14 y‰g¸4×–œœnžt3ƒŸÌC¿=Ä+™¯0véXúŒêÊ5+ª=þ¥¶¶´k׎^r£ÝÎär¶ívz H»víª|¡YüsYŽ5 ‹ÅBbb¢GûîÝ»¹êª«xë­·˜9sfI{¿~ý°X,ìß¿ŸœœÞÿ}ãÚ·oϤI“üžW8ã AÃÄ’V$˜X`hP¢GE£S×òÌ.}œN'W¸š½ÝöBã2;eˆú=ŠE÷/âžÉ÷Tzìýö3÷ù¹œ+<‡Ùa&6$–ÁW æù§ž¯ò‹ãÇ3{þlÒ3Ó1ÛÍÄ„Æ0nØ8ž˜ù’$Ui̲lÛ¶üã%ú&'²Üî»ìûÔ ötÄÿþ7Té¼úÜöÏçþÉá¸ÃŠxNÜSžK{!—¿ñoòòò*uŽWß~•þ#ú³%r û$u@*;®ÜÁkŸ¾Fëz››ðx‹…Y³ž¥i‹ÖôÝ› Í7°ïº}xŒ?NýÁSßý›æÝâ¸ÿþy¬[·©ÚŸË¾}û8|øA~µã4¸÷: $Øc8|x´GŽxçˆ/Ò$;väöÛow¶@X ·ß~›Y³fqîÜ9š5kVÒ¾páBæÌ™CAAAµ|­âÅ•œ@ 4 ÷’·%§)€ AµDøõáD Œ@$lV•¡÷ˆÞì¿a…}ÔIjþwëÿ¸oÊ}¨$ÿŸïƒùØHÎ 8ç»CŒÎÍ·_|ÐóòòèÔù~²¢þ€»Ê)®–%Á²AŒÑ‹5kÞ hÜòX—À„;ezh ¹Bû8/©±a ÂÄ?5Áüxx)κu0nܸ*Gèÿˆè-@«U|ÐL&ÏÛ!Æ"'ý   jŸ£°°¼¼<ôz=z}Í%Æ¿”A„¾¹”}j‹K-Ч®A„¾©­µÅ–i#ç›,' Ô·Ñ3&† ÆÕÿR\JkKzN:ç¬åˆ\7œÍœLû`&?H„>‚èàè ·W…sýÝÆuy@tÑëhØ~|;ýõWÀÉ U;a|•‰ce躟\Ck ¬²ŒÕåÂârau¹J^ûj+ym2aMN&kß>¿ë,!ØøŽ1,‹NSü)Cæù´ÛžÀ±X,X, k$H³!#” ТE :DûöíKÚ8@tttÞ;wòì³Ï2jÔ(FUíñ‹/æÙgŸ%22òbO¥^ñòË/‹«þ2ú¼ùfõ¬7 âÛ¬Uµ25Tjzm‘2y¿æQð[²ÓÿM[uˆš¨áQ„ö ­1ßך ºkË«¯¾ÇñãÅ"Ú·oÆ“ON¯ÔØÇ²±5}+[OnekúVŽç‡ü4z‘ɳä“g-äx^:H*@R¶âç’ L„D–î³J°Ë·E*ÔätVqÝ’» ê£I$©‘$ 2’J’ IRƒ¤F–%¬Ã;@«Ž ©•1Tj@ ª Pi•Ç©Aü‘߈Q¿,E§Ñ¡×èÑ©uÏÕèP¹ô¨\:œv rVA'Ž¡?uŒà '‘d—‡$î/ÁVà–¢Ÿäf-t‘àî“°ô-à?•ú@Iç»iÓ&vîÜÉСC+}üå„pá@ñîС/½ôÿüç?KÚ»wïN“&M¼úW–øøxl.‹?ú<7YÆ%ËÞíì“)Z$ @–KŸ—ÝçöY®x_Q{Ù¾µ…ûÈîsÀí=•í뵿L¿ºD–e$ÏGJ¿§ªì« Êû­¸·—÷qß_2VÙcËŒ]üÅíyymH’ç~ðùº˜’eËí;÷XÊŠÚË[ÞÊûŒ+úyü¾ÊŒð>÷9TôÊ{_ÅmeÆq®22x·WÔ×­Íë÷Y4§²¿Q¯ï¤œß±\NŸ²ýjƒòÆ/÷³–ÁQèÀ‘ãÀ‘ëÀ–cÇ‘ë@¶½/©üqddôíô4êÓI'ùü |Wæ;®ÍϤìw[ü?Å}_y¯ÿýŸ÷8svàÝО€ 8BÀÞlCA ¢E‹u<óÌžc»Ó)»8Sx†”œTŽç'5'•B»Aé,¹­2û€ZrÛ'¹=ª:«¥_·þèc‚1ÙM˜fÌvsÉcq›Kv)É@?’8’ÄÂ:0fоü.*´É‡ÎÙÊí£ê»U¥"%HæX{™ähJ‚²•}Å@AOàÔjø4˜ϽÍäÉ“xÞÿ 4JZ˜Áƒ³fÍ}ôQôz=;vìàØ±cÌ›7¯FÎqØdâ­3gjd,@ Ô28MNœ'΢Íàô°2»\. -…俣×é oîó®¤:DMpç`4‘*°!°¤uB~~>iéiœ9†–M[Ò6®-•Ç N „| ²!ÊU´Ç¹G ù˜îÄÙˆs6[éq.g Ïp2ï$éùéœ*8…ÍisYâ.¦ #."Ž6mжֲeçL­}g›À Í¥V ë;ÜïÜmNf‡™e'—‘KnÅ Ð)®±­×(Ùí’¦ôQy´š­Ùº6~†,p@v„„éué#ÄrÁÜ!t>Ü곃áX,‹t\¸L@SeŸ¹#l*,:G±‡›J íŒ%IµCƒЧNâšk® 77Y–K·mÛVâS¹xñb†ÎW\A·nÝHLLdüøñU¾zóšÇ®]$ÌK×#è:bDŒ)‚‘Ái,#–Î ]2²s²IMOÅdÃä„BОÓ¡‹ KÇ.¨Õj$•„®}œ¾â[—ËÅ÷?Ï‘³G( -D–Ù•¼‹F{Ñ­E7F JU™ÀF‚WÕ™Þê!ÊWe®ոTIÎI.Ìg Ïâ”}\Fé£ÁÙ†6mˆŽöØŸy6“½§÷bjaòÈ&$¢3¢?q|@3תµhÕZÚ·hÏnÃnä°ò¿÷HC$cG%44´Ü>² éé°ï°ê7YÍ´¬àü«$T†^´HGS)ƒ6Öô2%c:OÙ{²$‘Îé¦áœjBN¨ŒC¶`“­h3ó±¸Ü|ðƒáL£Ò稵`·U) !!„„¶mÛVåx— ^@GGG—ë7Ù´iÓ’çݺuc÷îÝ|÷Ýwœ² §NÁ¡Cpø0”ÖêéÆ÷Ò^î“]øâ ࣹ3ôí ¡ À»SHtê;#uè@Ž&À•eº>|˜Õ[¾…V¥Vù‚Ž.pÚÀnÙ² ÒBhÛ¶mÀŸM1ãÆcܸqL™2¥ÒÇ^n4x°¹I“&%~?5!+˧ˌ˖1hölôá•nÈl{÷]q±U›ÑÈÎeËõÌ3{*õŠô?ÿwµœJ– [†mï~Àµcÿ.¤jYœN''NŸðÏÅ8ôŽ„<¤š¯N¤à¸á8Îæn–ßoÀ4ŪèŒtrüÜqN¤ ][E(ÊÈXV, f‡Yy´+‡³:¢ý¤î‹Fñ T’ŠZ”¸dÄEÄ¡×T>(¿}»ö´o×Y–±Ûí%™³*‹Ýngß¾ÌÆ ð×÷Ð"B ð}ÜÒÒá,ìÅ gwôAûKtzº"šÂBÏ1% Tª ²œ=9Ã>ŸVèÀ½r6º2nª&MÐu숮[7tíÚ¡S©Ð©Tè‹u*:Iòhûeß>V¯MûoÐÔ\$˜Ë|'gÔ9 ZÄΟ?O\\\•¿hðº>‘ôý÷Ävè ¬‹E1ä!ž½Ñ‡‡‹ß‹„pGž[† {¦½Äcà”¹Õ377‹Îâ%žedrÂr8u§Úɱ“ÇrcýÐîûSlßáiž·äM±&¾Lü}7=‡«Ãêáïë…ÿå×T Ù‚Ñô.®êÔ’ uÍ¥ð“$©Ê⹕J´ëßáÄIФ@‡«à`;ptg¸,Øòü¼Aþ=ù9€KNÀ%¡B¢{‰ë®•èÝæ?Oö…»øÃõ3ÿužEír¡q:ÑÙí$Ûí ¶Ù˜f·£v¹»þztÇ£»ùf´U¸£–L[•ƒŒµÖÑVäÖe‚–OKè¾ Yl0!!!•?))É«àŠÀ7B@×s¼2$”ió5íþ\®p9  ’¤ÒG÷ç(kIÙ6UÙŒ+>2â¸gcÁ½Ÿ>ý}dÌ©ŠÛ°ÓàÄrÒ‚õ¤Õ­˜‰ïgå_’ƒÈ-Fñ¼ )„»r0ÛÍô…ô õ…âPÙ¡(;‚ólW£V«}fòõ”=—¿ãªÊ)‡ K²’ýÁYVNPô¾-I2–è²yUdŸ}9!+9ƒŠÚÊî—»Œü[6oêË£o†Ñ£¡eK·ï¾lÆ(_mìó÷»VI’,{µðÙÆ O·VÒ©¸â@X”®Æý¬y¤’B*­J>6µnº &M‚;î€ÆE¥Åóò‚øß¶U̳l$Çô;‘²~n÷"T îÉmGT#šñÉ—‹ý~w1bÄNœÕjå‘'á§Ÿ~¡ø’„å‡1´ïPŸXŒN§«ÖyþºŽ¸®S'Þ{ào!LÑ»/‘Lͦ $…”ÞîéÈj‰â9¸?//ÍU ©µêŸâ ‚ì« |¦¬Âóû®(õœÏc}ô)Þ_QJ¼ŠR)ú|€èò•J®¼ôr~SË}UÚWvÌrÚ«óÊ~eÅoñó†„ËâÂxȈqŸKºå×PQ*hkÑ6W¶œkù4ÿEìEÙdÑ\&­Xöïl.dl·±ÜÖå6Z‡·®™ùT“ã¹Ç1ïû»Ü¿« IDAT bNW¬ÆeˆJ‹bäÄ‘D飈 Žòz<ÅÛ¯Eñç¯Q ã>¸ÆPþ˜;ÂöD"Û"8{>ü>š«ˆÏøx˜0*ˆÍ«U$µÉìK`q7j5 X*š›4)Óá×_‰X¼˜}yû‘œN €‡åE»wJq÷Å3üé§„× »¢N§ãÃEâr¹8zô(]ºt©vÜV×®]éÚµ+ 51ÍÐu„l³~‘ƒåÜÅI‰pÿ'[ÇÿpE%Bß\JÕÂê Q‰Ð7 ¾¡ æãf { ˜’LÈöÀ.“O朤Ud+Ô*ï5WÒH5 B×\W"˜µMµHšÒõ¯a4oi°·¬ d—Ì–[Ørb 3¾›Aßf}¹­ËmŒí:–¾ÍúVúíV§ìä›cßðîŸïò}ê÷È:YÉ;ÜÜ­SàöS‘2$ù¯Ík¼”˜÷¬YSÚ¦ÑtıM‚ŽÛÀWÜjðû•\ѹÓ§ÃÇÃ_)×}‰‰Ê6cLœ¨ˆéo¬ó;eÓ´ ð\[úö…M›|ˆf“ –/‡Å‹áÀë“ð뮣[D;¾ÿžk].µiû Ö¨p.‹J¥¢[·n5>®ÕjVl?åRG¤¦¦²wï^š5kV’FïrGT"ô¨Dè¨D蛆Z‰Ðžeǰ׀q¿G£ÒÇ/Û¹ŒÙƒfIPÓ ¡¬k®#¨q’ºbÕöÔº1§™¡+P޵Tõ“Ž X=Ñ¡!œ3*¥¡÷dìaOÆü²€Öá­¹­ËmÜÖå6µ„V]=ßÝò8Sx†wÈ}À™B·Z×@ð¦`Ì·šKý–·Å?tLêÈ3¯yÆdeÁsÏÁ{ï½èú!8{ Ö®Í ;û:ò??ƒ½ÏYä^V¥ZtHûuímA„öZ\®ý<ò<ò<Ÿ|¢hÎóç•̬líÛÃßþ¦leÝ ùå—_ú š7o^aÙí³gaÃøòK çÎe{Ýϼ<áÑÖ´i§xNIwÞQ&ž—WÚ®×Ô)Ê•Á•W2³ €‡z÷fVz:ÝÇ«Uñ\ddd‘‘Ajj*Ý»w¿ØÓ©×\V•/ñññ>|˜»îº‹þýûÓ¿ÿ‹=%@ ¨W8ÍNŒ ëik•ÇQ‡ª íJXï0´Íµ•r(v¸ÌÞ4›%.<`“C€&nÿ&]À1°ïnZFH88ŸTS*_ýšõG׳ÿü~¯qÃuáŒê8б]Æ2ºÓh"õ¾ùùùÌ|j&O$ßœODp=Úõàíßö(|"#ó}ê÷¼·ë=6Ýà‘c9BÁ=½ïáWýƒ³Ïòà³’~M:5  îÏ8>xöF QQÍfxóMxùåÒ,k*•"nŸZ¶„³gÏb+*òcâ|—øégÓ‰kÇèA£:H)ý¬ÕjiÑ¢…çgë€1ýÍ7àVgI‚Áƒ«ôøñJF·¬¬,z7mÁŠÜ½Êã'àüUøåÏŸ=Ú÷탯¿V¶b+8\ 1íyˆŠ/fþÄAN¯¾lßó³2éÅ‹aófOóuÛ¶ðÐCðÀãqü =Æw_|ÁÆääKN@oß¾íÛ·óÅ_н{waÌ©! ëQS ¼‘]2–T‹â¢qÔ„ì¨Ú¿#I-Ü9˜°Þaw ökaöE®%—‰_NäÇ?* …Íà‹•`X ‘ ºŒA¹ Àq-[ÎæàAÏ»hiyi|}ôk¾>ú5¿œü‡ËÓ‚®QiØf âêÑe,m#Ûðç_r×ì»HíŠÜØÍ_?S¢Ã¾|öÆgtèÑ¥{–òþ_ïs<÷¸Ç¸ýZöcúÕÓ™Üc2Áš`úôI~~sœNYŽßqè È!$“%ŒXÍõ¨Õ!DDœcΜÍüç?JŽãbF‚W_…ž=KÛNž¿’´!iJ1’²8 ü›p,c-ؤRᦠ㮞w1ýêé^>×}úijoß'e*ö!ËŸ¯D+fg+YYeg36+‹±M²±ŽÈâÂá ÓŽ.k˜…•;ñtg_Lç$ÓXÔR”<Î ì*Ú^ñ_Z±8´«‚×÷;Ü=o£‹ÜQ¾Ójúä“—½x…TGh@ T§Ù‰éˆ ãA#Ö4+²«êªY¥UÒMqÑеÕÕZ5ÎäœdnýâVŽf+•Ûú6ëË×S¾&ÈÜŠéÓáÃÁY‰˜FµZ韒¢d3[°ž|RyîÏÂø‰ñl]º•¼¾yåö‰LŽäo÷ü- ñœNÅÐ[[Ì~å Â|·ØžE€GÎ 71\òèï¹›Ë!/{<Ë™3Š}8Œf¬bwbá9Úq÷PjŠçAËÝJ‰ÃJ †^¸À·Ï<Ã-6Kãâøâ±šwÝ4\„€®#D¡7"ˆÐ7"ˆÐD蛺"”m2¦£Šh6§˜‘UÍ’Z"¸c0!=Bé‚J[½ÄîøZ[~:ñ¾œP,xG·;xÔg¼÷v¯¼……J?ââàøq)ÊФ‰•LJ·Þ‚ôte›1þïÿ`î\˜>=°Ø³;n»ƒV|Ħs›p5wyíWSq­t-ãÇŽ÷;Ö×_+Öp÷”t¥$Q6ˆ°Ò\½`oZ­ä‘YŸ»wçê¥K}ŠášÀÀ#¼À»È¤“ÆC(â¹zÜÿØcLùàΦ§3lÚ4´ÚÚ)vs)"‚ý#”Kq¾‚¨åËDèDè"ôM]ÊS² ÓA¦c—Óö…¤’еÕÖ3Œn!•òkö;OYæ¿KþËúŸÖsè¯C´îÚšvÍÚñÆü7ø>ç{fnœY,øïÿM§ÓÏÓç ‰Ó§KǸõV%÷ñ®]7‘à?“„N§ãá‡uÌš¥¤a{é%HN†Œ E@¿ôÌž 3g*™Ð*bÕ«¸Öý$îL$³U&ÄÙÐøtc5ÄÒ—Vxüo¿)¾Ìgó"¬YYpË-°s'³EAAÌ·ÛYÔ¸1³?þ* ¬²lE‘ç iÄó8oaàn·vÿ:å¡Õj0u*ÿyí5N ë³çÏŸ'..îbO£^#ò@×ñññ¤§§3kÖ,‘…C Ô\`>nÆxЈ)É„Ëâm  ô­õ„ö%¤{ê°ªçköÅ/¿üί¿îäýUor¡[¶nwûŒ Ý¬ÃÖÅ W€N­ãñN±éÕ»Ù½»´Û•W*qoƒUo.N'|ù%¼ø¢Rº˜ˆ%Úc)ڊسgë7¯g÷ÁÝ\ÙãJÆŽKß¾}Ëíè<õ”by.&:ôúxΞýÄïœ{÷Žgï^ÿýHO‡‘#!)Iy}Ë-LÍÍåÿ~ÿÿ Àò@ÇÄl63mÚ<ÜoÞº\..\H§Y³¶}Ç»‰©So¯Òyl6›6nä¶±c«1Û†CqŽ·Þz‹¸¸8aÌ©a²,c=mŸ߈ñ—©¢Ð6×Ú#”Сh"jï_ÍÛoÉW[vÃÔtÏ’Û¡`»Ã ß‚*JGßœoxñéÒ !­[+b÷î»ËÏ}\Ôj˜2&O†õë•ôp»v)YÛ^|-‚üC±N7/›fºˆ¾}ûV(˜‹9u æÏ‡eËÀUôU+éèþõ/xüñæ$'Çû§S§r&âÎáÊx.6×ßs,]ÊìÝ»2d_¾ñ†ÿ1*Ipp0Ÿ^ûwœ´Z­Ï‚*!,Ðu€¨è#ê%2XÒ-˜›01â,¨zU@€ Ø Ñ[ó4|1|øT~(Ø£³Ëïd>l™G€HÂÑùØc• ö« ›7+>ÑEÞ6ètpÿýŠËE›60mÚ<’“Ïù«S§æ¼úêK¼ô,^ –¢Ú"jµ’JoÁhQÓ‰:þøÆŒ)Jœ3^½äŠãý÷ÞãïÓ§×ðI¡[ü#,Ðu„"ôFúFz#‚}S• BÙ%c=iÅx؈鈩Z¥´4‘šѬmV÷AXçóÒ¡WñœƒÝVìb­‚,h4ð÷¿Ã³ÏBãÆu3¿‘#•í×_!½e X­ðî»JÖ©Sá?Α”ô‰ß±’“ãùê+ÈsKÖqûíŠu;%£ÒkËÆJv “IyýÒKÊ•‡—ºxk‹oD¡„r©#D¡7"ˆÐ7"ˆÐDè›@ƒe§Œå„EÍI¦j»g¨ÃÔ„^JHt­j/íœ?.œéP6@o'0p³.K!N­Üpƒÿq}jé„Á¥Ðuį¿þJ||<ãÆ«Õ¢—"ˆÐ7"ˆÐèã)ÙDzb:æ43QAQÕK¥UÜ)˜î!w ®Ñª€î¼òÊ{üßâ§1LÉ‚²úT}e6üøËÇ}‰ýj#V§gðµÆ†|ôœÇãJ ¶P”Òÿ„Uò <­¸th=lë¹_ µ|·Rs:}:“/f¬+t‚^Ï-3fÔØÚeÊÜv›’& woÅòܬYµÎu© ÖOŠ|C†~ÇærEÖÂ_ Ô–4 Y Y8òþ;û@¬R|𻇢o¯GÒÔmöŒn}¯%éÚà¯vÇW‘}ï!ï÷)pb(8•œÒ!!pÇJaÃw†;ïœÍÆm¤à ‡ÔéVsìØWþ/Ø÷íS"ûV¯Æqì“ÕPb…^ ñ¾ŽU© U«RQí¾µo圻gÏ)4>¸ŸH+wZChKfþ8°¢âùW†Œ 5JyÏ ¤öذÁÝqAƒGèÿ ´@ Ôsd‡LîO¹üQ@FJŸ¨BT„v %¤[úvú ¬,ùÖ|²5ç~$×]wðöUöäÖòÓííÝ["šIN.iÖc$‰u’Ä.—b}ŽŽfÕ!pü¸bÎÏ/ÇåRJ^§§ÃÏ?{Ÿ':Ú§¸nîr²‘ìã}zûø‚÷!qˆ‘4Wû{nïöÆ ÏÈ ¨<§i»ûéÓØílÅ ½jUíW–B@ A=ÆvÞFÖÚ,lçm£S+)纇¢k£«±”s•Åh7²áèV\ɦ”MX;ZáàÏeÛ¨¥sç î»O)4ÒªUù]³OâÀ¼y w–_F5mÊÌ™3K÷ì)Í))žHôï'2uÜ8î5ŠÛc­^Ï­  ™1ÃmÙŠöµ;§¤‚+&'GÙþüÓãtßç$5O"ñ™›ÂOjBзÈG©ÿ˜=›ŒûîcZaa¹}>š’ïùƒ”|Ï „€®#D%BoD¡oD¡7—e  ù¿ç“÷S²Ó·Ù9×” @THêp5¡Ý‹,ÍqzÊÍ¿VËX6¦ldåÁ•|sìLvSéÎvÀ:5tu–ï–œL¨«;v”ëñàÁ”`ý›orç‘#%o9%»]ñʲ&8˜ÿþ7j7÷ RS=rÍL˜ ¤Æ(ãÖ™3YóĬˆ‹ã˲™7bb”­_?ïəͥ–ê²[ZšRI¥ˆæ²“vÀ> ·Ûû€kF6l‚¨6J>æ6m m[å±øyLŒÇ©ÇŒϤçžãoû÷S\T= (^Yì(ÂýË'ž€W_­ànØ\–kKˆJ„þÊ¥Ž•½A„¾A„Þ\n>Ž<Yë²°œ¬ø–ýŸé¢Ws÷Ãw+–æJV÷Ù]v¶¤naåÁ•¬?ºÞ3O3€9 ŽÜ‡r }¬Æfy‹èó*ØØÈ°ÀËë©ÕjÆÎšÅWsæ0Ál`1ð,J5oXª×^SĬ;’¤$a.Í嘺§NŸNÿ—^bÆÌ™•»à†+®P¶²8pꔇ¨ž}äs6oæ[é݆…ÀA©ß—Wê¯\–ÐP/a}ïС,KIaZQ)î—)-ײ ¸÷Þ{/kñ —ßÚ(¢¡Da œñA î)$wS®ÿ"(4º¦Q㪜A#!!Ûo7· ù ‚•æAà„ß²n]¨G N§ì$1-‘Ov­dý±µ:ʸXÁѱpðNH Î $i6²ü,¨·AìãÐ3Ú@Ž47€á#Z¶|†ƒ¿¸v:ÜÙ£«“’¼ ï«+0µ¸A’àúëKEsË–ã‡~`РAµ~Çì™éÓ¹ãý÷é#Ëì•$ÖÆs#G*ë´48yRy¬À5ÃIÀçPb…Åú|wëÖ|™ž^ÓÓ4„nñ°@ A=Àit’½!S’Éo_u#5±ãb îPqæ‰ÀÍÏû$ K…–E6•3à‡w ëUp(yçì™å¿üƲ¿V²£p M™;kö`86N†äÑÄDè¹öZ¸îÅÐûöÛ°~=àç‡Áù] Û Ž6à¼Åñ¢ò¨ÕjÆ=ü0kæÎe¢›õVV«$ n¸AÍãÇ,šÝ6lX•æVYf¿ø"mÚIJ“'YÇ•+•àòäæz‹êâÇ´4ÅZ Ü‹bmžævè²à`î]¸°–߉@аZ .2¦£&²¿ÎÆi,?®˜Ð+B‰ƒ*¸†ò6K)Ðá ¸3ÃÓ¥¢£ íSàˇ!w ÓV§’³m&r£ÓÊþâÿN-¤ŒB:4™+4·rÃ5a\÷OE0wîìyªw=j™è`P½ù''ûï2å“O˜d³1R÷ïÕ*ã&L@½p¡’8ú ::š6£FññÐfÔ(¢}‰g€¨(eëÛ×÷þ‚HKcLZ“zˆ¿=KE¾Ï:ñåøñµõ‚Ë! ëDè"ô"ô¦¡úÈ6™ìMÙvüöUéUDŽ&¬Wi·¬¬,bcc«>‰è%0!ÃwPŸ ¸#Ö-$Û] »4Jw×dní4ŽADÒ¯4jTñ©š6 §S§HRÅóKªŠjV;J¾âwÞ~YF ܬ–$®”eâ€U;óå_\rÙ%f¿ø"}׬aÏ‹/V}ðpèÕ zõâ^»e÷ÝÇ€ÂB¶5jĽϖt Yë²päú/Š¢o§'v\,šÏ%{Û¶mU^Sòòò E§ƒlY´@4P(ÑBwÛOæ¡›ÆsmÏÊ‹ö%Kž«Òüþ÷?%Ï¡C™2}:“ž~šß’’è­×sû£¢¾ÄÄ3(Vè·o/ßú\IŠ3r$îß­];a}v£!®-Õ¡¸”wbb"#G޼ØÓ©×ˆ Â:@8ã ‚bd§L^b¿ »*^~%DäÐHÂû‡W:ÃF <úèÞ:þ\m¯¸ã5÷šÍëϽ^­ó­Z¶Œ?6lðÛO aþ[o•^\oݪX›¿úÊ#õ=]º°ü½÷PÏ™Ãê¸8V:tI èÚà›¯¾ba|<}ò c„€øAèÿ ´@ Ô¶ 6²Öea;ç¿(ж™–Ø;bÑ6ÑÖø<¶o‡×_‡µkc¡__ña´Þ®ÚçmÓ±#~ú‰¹¹¹åö1sûõ#R£Qœ¦ß}ðìÔ§<ü0Ü}·RßÛ)>ȵ/¼ÀìÙ³…xvcÌøñ|÷Í7B< 5„Ð@PËȲLÁöò~ÌCvø¹é'AÄ DŽ¬Ñ²Û.—’ã7à·ßŠÕMá‚ë3ÀI-j ¯ÿ 7°¸kW4üAX9}Þ‹Œdv«VJП{ª6NÉ¢ñðÃJ„b9¨ÕjÞX¾œªœØY²téÅž‚@Ð`¨¡0n?D¡7)))8þý?/7’’’.öꇃ”²å–/d›Œa¯Œ2ÈÝœëW{6LŸ:Tï\ ÊÝâá-UÄeqac2Šsó¶¢=Å^Ñ«Ðs‚Ñ´l©æpæa&­žÄ¡ÌC4 mÊò;–3¬ý°’98(ޤ¼ŒÉN`b÷î 8°´qß¾RÁüÛoŠ«FYZ¶TÄòÈ‘0|8DEyu™ùúëüߘ1œîÜ™ßpƒÿ5@ÄÚâ±¶x#*úæüùóÄÅÅ]ìiÔkDa%xá… âÅ_$))‰fÍštœpÆ.d§Œù˜Ã^æ3²³òKdRRŸ¯‚~ç>¢çPìÃEû;…–¡XÐba4ô›Ã˾‘8éÉ¿ÙÇQÊ&•S¬Ï]8À Ìø(‘¥K1ÙM m7”åw,§Y˜÷šµòãqÌšÅÔr²V, Cóâ‹LnÜXÍ›7+Á€eÑéàÆÁ#ƃFœ¦Š]4*B§ã¯¼¿¸ãqv®gW±‘Ó}^šT0ŽKRãÐèqéA¯Ç*99“­e-2Ý9Íx<—î5¨HPÝD‡.'0¶Nè³VÅ-}'1ášx¤°0 õÚ\*zödõáÃ^Vh'018˜5V+*—Ë{’;— æAƒ¼ªBFFFÀk©@ ¨„nñpá—-ÎB'†ý { Ø3¨ÈWšH a½ÃíJPt†•ì@WrhE.I@±çiMÅâ@%;ÑÚ`7‚ ‚H  0¸J¬Ð2°+]?£9)ÅkW+Ë?Vˤ  VHSËØSV“ÌæÒ‚Á!¥¾Ìíª_Þ[ˆg@p)"tŒF#§OŸ¦S§N¨T5WgFz#}|#}¼©é@ã!#†=Ì©f¨â=8I+Ú=”°>aèÚè$‰óçáíÿÀ¢E0-,’&`<|XtÜaÑ9”!I]Ñ›nfþ?í\Û»·R2ÐbñÞŠÚO%'³c[ ú¦çèdsñU6 *šû/ ÐÑ ¡ÐTæ=ÙlL²Ù˜L+´X ¬éÛ·T0_=T7t Ö߈µÅDèD蟿ºìß¿Ÿ^x={ö’’ÂÀILLôêg³ÙxàX±bN§“ÈÈHÞÿ}&NœX#óA„Þˆ@߈@oj2ÐÇ”d"sufÕ–@ßVOXŸ0Bº… Ò*ÙIIðßÿ§ŸBˆ5—y ü2è%8ÓŽ-»:@ÆmFh¾éÀßûOqþŠ£@öÞ½üíΛ0ÝåôXêleß³ÑpðxLÚUzv|’HŸŽ”D£±ôÑý¹Û£ÊhdÒîݬøã¦:”"0+ôz&½ü2ªG­Úguk‹oÄÚâ"ô"ôOƒÐgΜÁh4r×]w±víÚrûÍ™3‡o¾ù†Ÿþ™>}úðꫯ2yòdÚ´iC¿~ýª=ñCôF,X¾ÿ༉ŒŒ¬‘ß‹£ÐAÖ×Yþ;–!(&ˆÐÞ¡„õCQºlnÝ ¯¿®Tœ–e¸‘­|ÎÝ´æ§ÃaêðKcp¾¯“Q\3¥‡ vna_~wTGUl†’­ÐVèñÚ`3‘š­­ˆ Ò®Ó÷ÊË´k•6€ðNa¨ƒC :ZÙd’ËÅ„ž=™rø0kÛ·gÍÌ™•þ¬êbmñX[¼©©µ¥¡!4‹¼€¾ù曹ùæ›ؾ};÷Ú¶E²lÙ2f̘Á7ÞÀóÏ?ÏG}Ä’%KJô3Ï<Ã×_Mvv6Æ ãŠ+®`ÕªUu÷fA•‘e™ì„l\&Áp>PéU„^Qä¢ÑºôV¦ËëÖÁk¯ÁŽJ›'ó¥çø7/ ’•Äu1j¦Ms’S”bù@Û¢BÝNÒ9—gw< iLè8à£g¸^ÚQú¼£ÖÈ… zî¨T*&ÍËŠY³˜4wnº± AC¢Þ­Žyyy¬^½šùóç3~üxn¼ñF|ðA-ZÄÖ­[kåœ;vìÀ`0УLÚ¥^½zñã?–¼þ׿þEbb"™™™lÛ¶?ü°ìPårúôiöîÝKRR’Çf2™<ú‰6Ñ&Új§í¯/ÿ"')Ç£-5+³Ý\òZRIœ 9Cè˜PZÏmMÌ­1èZëHJJ"+ËÄ;ï(‰'&L€;’]ô'9Úì&ž–Ÿã˜ìÄÌî<ÈUŠxÎìŠå¹Äú\ÔF`,ÓVDHP1ÆÚ‡¶§wÓÞô»ªúL}i?œ¼VÙÈ)=6:?šÞ½{W鳚ô·¿±6.ŽeM›2¦ŒûZ}ú.E›hm5ÛV¬I¾ûî;öîÝK~~>‚Š©7:33“yóæÑ¦M&OžÌ§Ÿ~J~~>Í›7gïÞ½<ûì³ 8¾}û²zõj\¾R*U‘³gÏн{wöž={rþüù’s…„„Y²5jÔ(às>|˜•+W’à±eeyÞN¾œÚRRRX»vm½˜K}jKJJª7s©/m‡ƒ”””*g;ocÍGkÈ5åzôû>é{rM¹5"jx-kÉV×VÌMÌH%ÇEf&̘‘@—.Y<òHi¡½ÐÐÞý G´½éñ«ÇðÔSƒ¹)vöŒ"E«èlÅò\l}.j‹Éaýìõüõ÷¿x<êqvÞ½“üåã|Ɖñ)#s£çòó¤ŸÙ;}/;fí OD8P4`è®l%ç0Cë ÖÄÆÆVé³R©TÜùÄ4îÛ—œœœJ[ŸÚRRRp8õb.õ©-))©ÞÌ¥¾´¯-õa.³­X“¼õÖ[¬\¹’ãÇ#¨˜z‘ú‡~`ìØ±\ýõÌ™3‡¡C‡¢Õj½úedd°téR–,YBLL û÷ï¯ÔyF…Åbñ "\¸p!sæÌ!;;›h7ŸÁ%K–0cÆ rrrˆòQE+PâããIOOç§Ÿ~ªò ‘Ù³g‹@ÄÇÇ _Å2äý?{wóñ?püµ›ksÉ}ˆ+îTÜ’¸RTE+gŠ¢¨£´´úëWQ´zD©«Z÷Q”P­ÖMƒ8ãq'±ä¾w÷óûce‰Ý\Ždó|<ö!™Ï|$™¼3;¦L™òLk%•Äõ¹×YüÍZärk½ç“ÌÒ wºŒ$“$ ï¼Ó˜¯¾Ë•+ðý÷°|¹v3ŒÕ«ÃÄQ© :5“å¿éÊ×´vdðëÉÈY·èB®e¹¤ƒß)?Žüs¤Ð÷_î´¹æhgÐî‘—öU lužžž…nói9¿d²§j)=ÄØb˜[ô=ÏØR–µmÛ–Ê•+‹ï—|ÅhWWWÂÃéW¯^¾õÜÝÝùüóÏùä“Oؼyó »¾‹‹ ׯ_Ï@_½z33³2«ÕjÖ­[Gݺuõ–мªÄ€e˜°ô=O¢Oü®x²b³HKs"9¹c®çTÈøoælÍ–€Ýºw‡-[´ës4m Ÿ|ݪœDÞ7._@iƒºÂŸµµ3¶&2:ÕìD§;ñõ´¯¹Ýú6<½ó[6Tø¯+—®,Ò½xzz:7”crÓã&).ÚÓm.ØPùne–Ï]þ\Á3”îÀ9‡[ c‹>‘D˜Û¹sç8wîjõ³&õª0Šº~ýúEªoffF¯^½^Øõ=<<8þ}ˆØÁáÇù÷¿èгÍ›77øÎ Ba) ìííÅXRF@?-11kkkÝ&ø‰‰‰üñÇ8::PˆýR‹ªY³fØÙÙqöìY]™$Iœ9s†®]»¾kT¨PŽ;\Q„B“¦AªÌã ”,nÃ%ÙQÕy|®XXÀ€0~Jrvv~ám—fbl1LŒ-úÄI„¹ÅÄÄè"VÉŸÑì‘ãäÉ“¼öÚkTªT €åË—caaÁÝ»wY³fM‘÷]V*•̵k׸}û¶îó{÷îéêÍœ9“Aƒѯ_?*UªÄÖ­[Ùºu+ 6|!÷uíÚ5öíÛGttô i¯,˜7o)))%Ý £3kÖ¬’î‚ÑÉ9-¬°’O$“™¦ÿ„År¤:„õý“ŒæJ¨©†Ž÷`ø¨Þ¬þÆ¢uí6Õãõ~x}½ˆ ËÏ'Û¾FöÉc,™~†!‡<¿laaa„……•hŒ‘[ c‹¾¢Ž-e]tt4ûöí»p‚QìÂñ¤•+W²xñbÝžÏ~~~4oÞœ9sæ••…—/_ÖØ/šF£!))é…fo"CŠC¶2›»‹î"e?Ú222™·–ÌJ÷¸Øä GjØùB lA ¯DX»ÆhŸRY["›?“ÁÅr‚ %IÄ-3º÷·üüüøè£P«Õܼy“cÇŽñã?`bb‚Z­~©3 r¹\l}$¥”¤–PnRæ ž²²²ÈVÅlqŸÓ©Ç1;G¶6ÃÚ(–YÚðõÅtíZíþu‚ ‚€еkׯÑÑ‘úõë“-[¶Ðíû\¥J•’ìâ3 #88˜€€€—’)$ìM ónf®2I‚C‡L±ÊΠÇéý 9÷öL›eð––HÚ-8>ý¦M³§÷¡A({rT Ãß_?1ZxÌèh€ˆˆ,X@||<Çו;vŒÁƒce•×ÉÆË××W¼ò‘èc˜HôÑW˜DŸŒè æ>~6=6n„k×dœ±?wZ<ó ¿^ ü*A€òåaåJxóÍv/ƒH"4LŒ-†‰±EŸH"Ì-g’/((¨¤»bôŒ.‰ÀÎÎŽI“&ñÍ7ßPµjU]ùСCY°`A öìÙÅÆÆ–tŒŽHô1L$úè+(ÑG“¡A¹9÷–u÷îÁ¢EpíÜ5•áx†9vÚ@ÙµÀ{ÀIJpæŒÑÏ ’ó"ÆÃÄØ¢O$&b–‚Earr2¶¶EËfOJJ¢\¹r/©G/–XŒ//×ý÷I=—ªûüÔ)øë/P© Óí<;ŽæÁíkXžƒ"Ñ›…V½dà[£ŸFÞD.7ʹA„b!â–‚Åo‰Í›7S¯^=~ýõW233ó­{ñâEFŒAõR–ÐsöìYfÍš%f‹áK9¢ žÕjmà¼e ¨L þjŽäÕ5¨ö®å™«ëÍB¯’Á­ vtÛ¶[Ï‚ ¼²Â˜5kV®ƒåÃŒbXß¾}‘Éd|ýõ×|þùç´jÕŠ5jP½zu¸zõ*—/_æÜ¹s?~œÀÀ@öîÝ[ÒÝ.’Ê•+ Ö*  ¤JPñðï‡$'ÃúõpûŽ*Ïý\©p.7ð°õ`NÇ9þ/鯷duØ!…\Œ"€611¡ÿþôíÛ—ßÿ7²iÓ&®^½ŠZ­ÆÖÖoooš4i²eËJe„B¡(•ý~™D¢a"ÑGŸ¡DI£Ý²N“©áÆ Ø°RMnÏ6°Ž#É2‰ã53Êo_·ýšr&V0r$“ÃôL€ÕÖÖLüyA© žE¡abl1LŒ-úDanÎÎÎ8;;£P(Jº+FϨޫ”ËåñÇpéÒ%RRR¸}û6III>|˜Ÿþ¹Ôþð‹ùúD¢a"ÑGŸ¡DŸÄÿɸ™Ax8¬X“Aj…?¡Ño`‡F¦áAëz€¹oÏ¥\r´kóç#‚Yei‰ØR¥ =úõ+‘ûz^"‰Ð01¶&Æ}"‰Ð0³Ì(’Ë:±_ O¥Rå %IâæÍ›<|øš5kbccq Þ&ãØQ[.Å߃ÿ€¹6`2“›Ñ¨G#>þ¦rSmFa@ܸ¡m°Y3¤?þ Ç[oÑùÆ ì, °ÿ’¸UA£$â–‚‰÷·ŠÉáÇ>|8]ºt¡K—.%ÝA0Z)))ôì9ƒÔÔ Tf[Q• Ï $»,d¿Yc~×™€¤ÉX™T3ê<Þû¹¦cMÞmû.Þý‘ÉeÚEÑï¿iiÚ ÁÁ°p!2 úLšÄ·ÿ÷)¥³Ï‚ /Ú¶mÛØ¶m‡¦yóæ%Ý£&èbâççGHHˆX“'…`aQeÖ&xkÔKÓ•K¾Yø®‹åÃHUTPN»Ÿ†™-j½wo<úz C‚É_ÀŒÚš˜ÀwßÁرº¶zôëG//d2Y±Þ› ‚±êر#íÚµãƒ>(é®=£Z]–©T* … Ÿ…J¥*énÈÈÈ’îB‰KÍ>Íÿ{•JETTTIwÃh˜šš¢P(ÄÏO!ˆº˜ˆùúD¢a"ÑRMÎ@ó'ŽåγCf4¿üè-EÀÜnW¡SÍ·±0±Àºž5Ö–w¡Y3ضM[ÏÛŽÕ&–A"‰Ð01¶&Æ}"‰Ð0³L$±_(«âãã™:f RZZu›vîLÐûïXïÁƒ¸ùUC= IW&“dt8Ý÷÷ÇSÁñfy>>S;S 7$$hŸïÚV®„"žr*‚ðªqKÁŒr=Ajj*³gÏfýúõ\½z•ýû÷Ó¬Y3–.]ÊÝ»w™¯6NaaaZÒ]„jìwß±ÚÉ {0ø0®Öª…ëÖù¶s6î,„ޤæBo4féÈ5rÜÝht½‘þ M°3·Åî¿(fŒÑÏÖÖÚU¦NÁ³ B…††,–†‚ÑÍ@Ÿ8q‚¿ÿþ›ˆˆ6lÈîÝ»uÏ5nܘÄÄDnß¾MÕªUK°—Eçëë+Þ yŠ8-̰ÒvZ˜ëÖü\£)œ…þÙɉ‘ß|ƒ&Cƒ&]ûP§©Ñ¤kÈHÉ`ÏÅ=üuj;×c¯¢ÈVðVFk,Ò,P,³ÄÌ^û½¡‘4$e%Q΢œ¶Q Xݰ¦¹ì ö·ÖkË<=a˨_¿XîÛˆ“ c‹a¥ml)â$ÂÜ ((¨¤»bôŒnt‰ŒŒ¤V­Z4lØОN˜#;;SSÓR™"äë›7oS¦LÁÞÞ¾¤»bTfÍšUêþØùÍ7ÌëÞqMH¡j,PcA:`U“Šû«psïM]ý‡é9~÷8§bN‘®JGT£H2PVÇæz4­4jd2 dŠŠõR$A2?dˆ&ž¶#CmÚhgž_±@2g–(  „{b\ÄØbXi[^¶œ$Â’îŠQ‰¥råÊ%Ý £ft´§§'W¯^%..WW×\ô–-[P«ÕT¯^½{ølÄ7¢>1`VÁÉÌMù+Ó–øc†™®ü0Ðì¶æþ ÎÎ<°–‰’‹Ò}”V™3eÚ½ƸªãïkÎŽ+˱̺O]I €9­ñ{ô±ÖŽ2†D9BBàœm³abl1¬4Ž-/›½½½ø~1@Ä,3ºß87¦J•*Œ=šiÓ¦!“ÉP©TlÚ´‰™3gÒ­[7¬¬¬Jº›‚ ÉÉÉ4jô6ªÌX‚Ü_çÈUSü=—Ä-% ”JP*qZ>z¤˜š ”9‘fV g/K\ëÜGãà@f;?"öï¡nªáw›.p ùn|ú9Õg~ýÒïSAždt´¥¥%ëÖ­#00ÚµkcjjJÇŽIMM¥Q£Fb¿FA0"jµy tkNÿH¶  œÍÿL ³œãÀpÈù³È6*56ÄAvœNj3›}ÌÌ0µ´$R&Ãë©6夳Âô«vïÅ´U«âºUAAÐ1º´ w‘‘‘lÞ¼™óçÏcbbBýúõéÖ­&&&%ݽg’™™YÒ]0:"ÑǰҔè“™APFú]ÊÆT>2“ƒ¿ÎYCB5¸Úí8ÈÓ;âxßÚŽñ4ª¤ÄIRêf§yòg$;›úÙÙl¼5n“‚'Öœ’¤ãÿ&Šà‘D˜1¶VšÆ–â"’ ËÌÌÄ¢¤»aÔŒvt±´´¤OŸ>%ÝF$ê‰>†•–DŸ”Ó)$Ì?OŸ´lÌ$íòä^pëd¤Abµ'*[‘¶x¸6 M_¨Q˹BŽÜJމ¥ rK9òŒDä÷oc{ ùkÈoEáqþ$‰qø“Á&Ò AÃøµX7iR ݵqI„†‰±Å°Ò2¶'‘Dh˜H",˜QŸDxéÒ%"##yº‹¥í—EΞŠþþþº-b¡4K>žÌƒ%ç–-C–™À–Úpª<È@ç;]#Ö>–LÓL2c2)¶ GŽnÖË…Û‚^¥Rñ^:l¼r°I¡ iöl‚Gz‰w'‚ðj %44T³ˆ?¸òf”ôÌ™3Y¼x1ÑÑÑŸ7Â.çK‰)”%IáI\[t«50ÏR°­œxb²â¼ÓyŽÕ;ö¸ Ô•ö®ù÷ßåE¾Þ²yó°ýäºgdX«¿Ÿ?/ÞšAx‰DÜR0£;‰ðÈ‘#|þùçôìÙ“ãÇOBBB®‡ %ãø–ã¬òæ«×é‚çíUáD²©n‹¹ÓUNçžoš@Ô›ØÚ:<Ó5û Îêʕ٨PðÎG‰àYA(qF@pG‘Y IDATGGGãîîÎÌ™3iÒ¤ öööØÙÙåz”F"‰P_TT*•ª¤»at"##Kº zÂn†ñáäY7ãgZU¶¶|§»3G•uáA8aÏ ÇDTÐ>©ö:À†7!å‡g¾¶©©)]FŽdj¹rô>üùo¦ Q*•ºDBá11¶fŒcKIS©TDEE•t7ŒŽˆY fttË–-Q*•ÄÄÄ”tW^(‘D¨oÞ¼y¥òTÉ—mÖ¬Y%Ý¢þ¡ÕÒV|<ùc,wg0ð4Øh—<³Ç¬)‡bc’f /ަŒãì?þ0Ï5¹áàBHþPþMêËelói5=¦°q£ÈtA0v"n)˜QNçtìØ‘Ú·ooðy#Œù¡ÔSiT¬>»š™ÿÍâÒƒHd’ ÿKþ4ºöOʱSi£ç;ž-ñéÕKKÈV™åŽMpçÓ¶$iðöί† ‚ ”F@'$$àëëKݺuyÿý÷©^½:2™¬¤»%¥Æ¡C‡HJJàáÇœ†=o¢¹¹=Ù¡é¼ ©ï¨­G ¶©ï4v’]å[®tÞÉÄ]Ï U0Ùö3C¶áã-e½ëôûÚç7†N™Ép r-¶àD¢O^D¡abl1L$êc‹a"f)˜Ñ%ªÕj:wîÌo¼ÁÇ\êA»_­VóË/¿`jj*fE„—ªFíŽ\u8 oßÏ»Ò^ P Pza~ä3úÕ âϸg$ÀŠpïž¶nƒе+ÈdÈ-ä¸öqEQåùvÕAŒJ¥B¥RñÁ`bb"’óat‘ÜÍ›7˜4i3gΤeË–zAthhhItí¹=z”±cÇÒ¥KºtéRÒÝÊ0ez$ä<4þ5ÇýúÆwêÆûŸCÖß±dF%ÁªUƒçºuÏ 9nýܰ¨(ÞÖA(‹þùç¶mÛÆÑ£GiÞ¼yIwǨ]  P(èÚµkIwã…jÞ¼9 .,én¯€lY ´ºÂ,îÛr÷d4éjâVÅ‘y#V¯†;w´u^{ ºuÓÏVrÜú»aQ^Ï‚ eUÎ$_Î6vBÞŒ.€®Zµj©œa.ˆH"Ô'} {îDI®=V;¿Ík$0•™ IU³"†ì»é°f ܺ¥}¾V- ¹Ü¸aîjþì}zN"ÑÇ0‘Dh˜[ I„úÄØb˜H",˜Ñ%–UbA¾>‘ècس&úd©³øéÈOdT‹‡‚¾Ý"1ËÏk×Bt´ö¹êÕ¡W/Ë1µ3Åý}÷ žA$úäE$&ÆÃD¡>1¶&b–‚Eá¡C‡X¿~=ƒÆÑёٳgç[?$¤tf&Nô^&¤aõÙÕ|¹÷K¢¢!øÄð,´6 ë,büà÷à÷ßáÊísU«BŸ>`jŠ©ƒ)îÝ1µ³x‚ ¯·Ì(~3ÆÆÆN÷îÝQ(„‡‡—t—¡TøóòŸ|¾ûsÎÅÓ•™f–C•ñ,ûzÞ›'^å~÷¢ƒr6¶Î‘°aÃãà¹re SSÌœÍpà†i9£"AÁ¨ÅoÇnݺannNÍš5)_¾¼ ¡a7Ø´koÔ•yØzÐÍñKæùÔí€zðKØ*¡\$™cŸäI‡¤±Â„7®Al²öÅ+Bß¾`f†¹›9nýÝ0±1)™›A#g4@ß¾}Y¶l%Ý•—B$ê‰>ú¢¢¢Ø±síßjo0©ålÜY>Ûõ]ùKWæ p`¢ÿD|5£éÒÁI킹ù]ÆŽ…jÕ> ==›,ìŽØ Ï„Š§`çQðìáýú¹9¸ösÅÄʸ‚g‘èc˜H"4LŒ-†‰$B}bl1L$L$± _ŸHôylcèFj¶¬IËñ-õí(ZŽoIÍ–5Ùº€ë ×鿹? 6ÔÏVfV|æÿׯ\£³ÝDz¼kIz:8ʾ¡—õd2WÉÅiÚGì7‹Èú:œ;÷sÇvŽÜ¹Íwwèß,,°¨dÛ7£ žA$úäE$&ÆÃD¡>1¶&b–‚E!€½½}™‹ñ…üLùf ?ü‰øÆñ¹“þ$°?aO-÷ZœªrŠ,ufr3†4Âÿµþ?ÊÛ”çÆ hÑîÞ™ þ÷ÅA,æweÒƒdàFíÐðø@¢0ÀÓÁŠC†€• On}Ü™ç·÷ ‚ð*qKÁŒêý­?ÿü“蜭´ò1vìØ—ß>|ȨQ£8uêmÛ¶å»ï¾C¡G Ï.òR$Kv,!¾U¼þ“2HðIàè_GÁdN2z×íÍ´¶Ó¨îP€û÷¡}{mð 0{6Œß’~»j‘rø0&xG[¤'~Ô³€8SSü++,kXâòž 23< ‚ BaUýÛo¿ª^IП~ú)NNNìܹ“‘#Gòý÷ß3yòäé‹P6̘3ƒ; îä_É*\¬À_“ÿ¢[]qJ tê—/k?Ÿ0Æ×~‘èÇöÀÑ%w™: Lž:ŠÛö¾i®à9+K{ÊöñãÚÏ €o¿}\¿A–3MÚ‘I"O’Ĺ»ã_»6Öu­qéîR*2!D¢a"‰Ð01¶&’õ‰±Å0‘DX0£úÕimm½½}’pÿþ}pqÑF;ÞÞÞ„‡‡SØ%äbA¾>‘è¦IiÌÝ»Ÿx´:–ûó¹ûµõrh4Ú¼¿]»´ŸvHæ·~{ÍøÞy‡ GoâÚN§éÃDŽ>u½£ ~o¿MC\z”ŽàD¢O^D¡abl1L$êc‹a"f)Ø+óçyVVÑÑÑ( *W®l°ŽF£áÂ… ܾ}›Fáææ¦{.---×_cdee¡Ñh01)xׂ¼®ù*+m'J¾ :µgéÚÕìL~\¶õ©:ŸØjë I|Ý/›õ‡YL8m­Â©¾ó<ü«´ ƒ±¼…„)Ñ.×È’$Ìy4ûììÌÛÝëàØÉÑð)…FÊÞÞ^|¿P“®_ñ½b˜HÓ'ÆÃDÌR0£  íìì033+¸b:tˆÑ£GsöìY²²²hݺ5ûöíÓ«÷ðáC:uêĉ'pssãÎ;LŸ>]·Æ¹J•*H’Dbb"vvvœ?ŸBÏ‚`ˆ„„}k{výçouÎéf–ÌS8A‡d8Êÿe$<®ðxbšL\‰uîTÁS{0JÅŠøeeqdÝ:^OO爥%ÍÆuÀ©³Ó˽1AA(ãŒ&€¾qãÆKiW£ÑЦM&L˜Àœ9sò¬÷á‡Ã7ððð`õêÕôëׯóöÛoàïïϯ¿þÊG}ÄŠ+hÞ¼ùKé³Pö¥d¥ÐwS_¶^Ù ¾0v/ì4°Lþ ôa:ö?ýÀ“{¾hlí7óƒfÍȬՂØËU‘ä¹×¬UŽ:9‘|û6ª¤Óÿcÿ—vO‚ ‚ðª(%+ Ÿ¿¿?ß}÷½{÷ÆÎÎÎ`¸¸86oÞÌûᅦ‡ =±jÕª,Z´HWoêÔ©¬ZµŠJ•*Y¤Ý@D¡¾¨¨(T*UIw£Ø]O¸Nó_›³õ’v±FwŸšT¯èÉùGÏG>ú÷<à8’LÎ9겄!ŒµYÂõmç‘'ÆÃŽdŸLìí:hä†>üÚ·ç+‹¼ûËà—|g/J¥"**ª¤»at”J¥.‘PxìU[ Yp¥WŒ[ 1KÁÊ|]'Ož$;;›:uêä*¯W¯áááºÏkÖ¬ÉÉ“'¹}û6»víÂÕÕµÐר±c“&MbÖ¬Y¹7oÞÌUïU*›7oÓ§O7оWÙ°IÃðûƇcçøþ_¸7ß ßéW~5šïs^÷è1U¡àãÏ>ãÔ츙ÇS¾Œ±ú?‚v ¦jç: “ñõ䯉˜&C£»Æ‚°ÜM¼«ûÜëÝ×Èh« ÂSkÚŒéÿ¥ ²œDcè‹1•å$C_Œ©,'‰ÐúbLe9I„ÆÐc){2‰°¤ûR’e91IÇŽ™4iÇŽC(€ô éСƒÔºuk½ò_ýU¤óçÏç*Ÿ*Û R¿öí¥Å‹÷JVV{%8&™š^”BC÷/+6KÚ3atáó Òõÿ]×=vÜ¥+‹û#N’4Æñÿ'ÊD™(e¢Ì8Ërb’¿þúKŠˆˆ¤JBÞŒæ(ïâбcG222ô’CBB7n÷ïßϵŸê¼yóøè£ˆ®íóÄ‘˜eÃÆ•+É1‚~ùlµÊÆÅüùöï¶mH›7“ýÏ_˜gdç®lbÂQ {6dwàO“ÆÜÀ‚–™SØ%= Ì‰0ó)deÙ ý ÝÄÊ•[é×OûÒle61KcP§ªóì‹u]kœ»;#“—¢í6A„'â–‚‰%€»»;—sŽt{äâÅ‹XXX¼½§ÃÂÂ&44ô¹ÛJF~ýØR¥ y…¬j`‹‡=>„¶mÁÍ Dª žÓÍdÄwl Ë–Al,Ckvá»ìÕ\ÊOFÆ(ÎI]YŒsRW23G!IÁ@0ŽƒçÙÄ,Ï?x¶ªc…Kw< ‚ …Jpp°Ø_¾D T¬X€óçÏç*?{ö¬î¹çåëë˲eËľ­O(m‰>2™Œ÷&NdµµÁç×Èå¼wù2²±caï^xto¬`yC˜0¢ñ·¯à°} NúÛÉÅò C°%–or•?:¿U¼J<'ç<{Y•ªCR C$ú&’ +mcKqI„úÄØ’[@@Ë–-Ã××·¤»bôÊЯØg׬Y3Ê—/ω'teœ={–îÝ»¿kˆS}ô•ÆÓÂzôëÇOO½Yh5°E£¡Ç£ÏÓ+¸² ¥9oƒÛøç‹ÞL›s×ê\Á™ Ú¢Ý{#7U‚Š˜e1¨“ò žkYáÒÓ™IÙšy§…&N"4¬4Ž-ÅAœD¨OŒ-†‰˜¥`e~ tbb" ,`éÒ¥dgg3tèP†Š££# ]=~üxfÍšE£F˜3gäĉT­Zõ¹úÌÙ³géÙ³'þþþøû‹½xK³+W’6lÒÓue+«J•|ÿ}–WKbПÐHdȘÖv“_Ÿl°­† ƒ9}zY×lZ÷6ùªø¼gÕ,kXâÚÛ™iÙ žA„â‘óGù† ¨W¯žX£9HåeIKKÓ­;vppÐ}¤  ÇŽ‹™™¿üò 3fÌÀÏÏ€._¾|®½œó3räHFŽùRúagg‡——×Ki[(>aû÷ x·3ý4«Àbs8Õ4…jí«‘é› • Š]¶m¥¾[}ƒí\¼ß.ä=KÔt +•áCRž \ƒDð,‚ <gggœó†‰$BÃJÛØR\D¡>1¶&b–‚‰º˜ìÛ·Olc÷”Ò–è³§wzÅ&p¸ëíÍà†? L@ÖBF«û­p±rѽN­†õëÁÏÚ´mÛ´³LåʺÒ,,ÏödåÙ‹Š¸õuCn^öŒE¢a"‰Ð°Ò6¶‘D¨OŒ-¹ålc÷ôy‚¾2ŸDh ĆäeÀ¾}¨Ú¾©·ËÏPˆµ«õÖ m  ç5Ü¿r‡ÔTøí7øñG¸~ýqSп?ŒÝ»rñâ¨\—2:Onrçì|”‘#{=n£‚nýÝ+Ê~ðœ—72}úô’î† B©óî»ï2uêÔ<ŸqKÁÊühAxnׯC` ¦¤›A@oˆµÑ>•Ö“ÇÁ3€R³2ùâ X°@{aGG1FÒž±0uêââ¢uud*öGl0M0ÉÕkëºÍË›ãÚÏõ•žA»|áÞ½{|öÙg%ÝA„RcÍš5ܼy³¤»Qê‰Zò“š ]»Âƒ yNx<ñüÓ1l¤'Xñõ׋ªUƒqã`Ð °²Ê]=0ð]ÝÇšL ±«bɬ™÷Ú3s7sÜú»abi’gW‰ƒƒcÇŽ-én‚ ”§N*é.” ¯öV1:pà€Xý£Oô‘$0Ξ`nEGÖ¤𚃎V€¦Maø|Y;ëütðü$u²šØ±dÞÊäªòªÁ:æ®æ¸ pÃÄêÕ žE¢ ÂË—³úÀ%Ý£'èbâéé)Žò~ŠÑ'úL ›6pºqÆ~‰À­<êß0…S-(gU‰ <Á¤€x7óv&wß%óŽvæyAؽ:fÎfÚàÙúÕ žA$ú‚ ‡œ£¼===Kº+FO,á(&•+W.é.’îBÞ6o†¯¾ ÚÍ‚Öíï ‘-Üamcð8cµ™~€“îpåmHOÕÃyýõÂ]&%"…Û ©çò~ð]®:fNf¸ tÃÄæÕ žìííûûE¡ 1KÁD-O;{V»tC’HRÈèÐ+“DØ(ÝHYv RÜ î\üÌC–dvêþxðÏ’&ç[ÍÔÁ÷î˜Ø¾ºÁ³ ‚ @ “<Ð& ¦¤ ‘Á{—`B‹ ìø4Ž3)¶Ï dÖÑž¤RDš4 qëãȈÎÈ·ž©½)îÁÁ³ ‚ @“œ$€€±ú‘¨¨(<==155’oC•Ьî]1´ió¤vpÈ»t]J÷׺³S ~îKdÅd·.UBÞÉ“W•W©Ó ®½]ÅÌó#*•ŠèèhjÔ¨QÒ]1:qqq¤¥=ÎnU(¸¹¹!“=Ûé”ÑÑÑ…®[©R%L XäîÜ9¾øâ FMÛ¶m lsâĉ¤¦¦–ØšwµZÍÅ‹‰ŽŽÆÆÆ†ÚµkS¾|ùgj+--¸¸8,,,ž¹ €ŒŒ .]ºÄ7¨X±"5jÔ œáS˜xøð!—.]"&&<==õÖ³feeq÷î]lmmqrr2ØŽðj %44”ЪU«’îŽQ3’È¥ìËI"›7oS¦LÁÞÞ¾¤»À½¡A”?p€uua{·ºëõµœj•÷¡€…’z.å%Rvþg-¹°„5!k™–í㹋"'‰°(ë ### }tqûöí±Êo›”<ÄÅÅqèСBÕõõõ¥B… E¾FA†Ê–-[r•) ^ýufΜI“&MŠÔ^ÕªU ]÷Þ½{¸»»ç[G©T²eË–BOìß¿Ÿ„„„B÷áEúøãYµj÷ïßוÉd2úôéÃܹsqpp(° ¥RÉøñã9yò$/^D­VÓ´iSÂÃÃ‹ÜŸØØX†Οþ‰Z­Ö•›˜˜Ð»wo&OžÌk¯½hƒìñãdzdɲž¬ªT©ÂÖ­[©_¿>.\ Q£F 6Œ… ¹_BÙ•3ÉW˜?v_u"€.&bA¾>cJ Û59ˆvK7p²<ü;ù=Â{üе™5ÙÙpûv @… yà åÊ=µ×ñ»ãI KÌ¿rplïÈÚ)kŸãNʦgI" YÇ¢E€km¯#"¢á3eŸ,J]µZMvvv÷"”nbhá•©ŒäÙ9k7æjÈ6‘¡\±ˆà§‚çM›`ÎíÇÃ?®ý¬ûYÜûå^Á³¹›9àYx¡úõëÀÅ‹‘$‰¶mÛòî»ï¬ûçŸâååÅ¿ÿþ[¨¶“““1b5jÔÀÜÜ…BAÅŠ™0aB®õØOûá‡xíµ×°°° víÚ,X ¿çy^"##éÒ¥ ®®®( jÔ¨Á´iÓôc:wîŸ|ò 6ÄÒÒ333êÔ©ÃêÕ« ¼Æ“Ás333ºuën}¸©©i¡‚ËüüöÛoœ9s†aÃ†å žŸäææFß¾}¸uëÙÙÙ4jÔ(Wðü"„……áååÅßÿÍÂ… iР …‚ *ðÝwß!IúKÒ¶mÛ†ŸŸ666ØÚÚÒ¬Y3¶oßn°ýóçÏÓ¥KÜÝݱ´´D¡PЮ];®\¹’«ÞªU«hÔ¨666ØÙÙѪU+<¨×^ƒ ?~Ãv e\I&n¸°‘±}Q nÎrÉøé{¼» ÍUïêUíÜvvÚ“ 3¡•v) å&%šLM¾õ¬êXáàŒÜüñß²‘‘‘xyyé~ʺ—™D˜•¥=ô¦¹s:g΀&ÿ/q‰¹þ(ÖÎΙLFË–-™>}:4jÔ(Wݹsçrÿþ}Z·n]¨¶}èׯ_¡gÝUQß½y%IÂK7pà@ÉÝÝ]8p ´yóæ’îN‰Ûw`Ÿ44Hr÷t—Z½ÓJš2kŠ”ššZ,×Îî _ IDATVgKãþ'1iM=$I{`·¤5R¯nF†$5j¤«"mÚTpûFJØŸ ]Ÿr]ºþ¿|S®Kñûã ¶1pàÀç¼Ë²'>>^3fL®² Hµk×Îó5ÆýO‚뺯_ÞÂÖ3ôØ.ÁÚBÕ[±bíKù¿éÚµ«H÷îÝÓ•]»vMjÖ¬™H!!!’$IÒÍ›7%iøðá¹^%Éd2iìØ±Û_»v­H?þø£®L­V¬;pà@ N:¥+Û»w¯H¦¦¦Rll¬®<;;[ªS§Ždkk+)•J]yÓ¦Ms}]³²²$///©\¹rRbbb®ë9R¤£GJ’$I;vìiÉ’%†ÿ³žAÎýðÁÏôz@jÚ´i‘^ãçç'z÷›ŸÉÔÔT$©ÿþÒï¿ÿ.©T*½º 6¬Àv·oß.’­­­” +¿wïžHþþþº²ÄÄDÉÙÙYªP¡‚”––¦+ONN–\]]%gggÝ=¥¦¦JUªT‘Ê—/ŸëëŸC£ÑH’$I·oß–,--¥úõëKÙÙÙºçãââ$ ©zõêRVV–®ÜÂÂB¤MO ØÙÙÙ’•••H]»vÕµ-I’4tèP >¬+ûñÇ%@ªT©’”‘‘‘ëþlll$ooï\?€Hëׯ/ðÿÓ 80Ïß3›7oγy+Ý"•":txåòV©Tôÿ°?3Yk·–˜à4<ÀÔKSiÔ¾a‡Â^êõï¥ÜãåoðcøL ƒ ³žxã d?ê¯;""´ÞÅÍ“&Kƒrƒ’ø=ñÚá4r 9nAnØ·2¼ûˆØ­EŸ8‰°`}úô¡cÇŽøøøP­Z5ÂÃÃñ÷÷gĈ€v˹Î;³fÍRSSu¯ûå—_$)×Ì`Arf×$I"&&†“'OŽ ]Fñ´&Mšàêú8¡ÓÔÔ”·ß~›äädÝZ^CŽ;Fdd$ï¿ÿ¾ÞÖmýû÷´3ÔÞÞÞÈårþý÷_âââ }?y9wîÆ ÃÓÓ“Ù³g?w{…uëÖ- Ež[Õ2fÌ®_¿ÎäÉ“‘Éd¬\¹’÷Þ{š5k2wî\½9ŠªK—.¹’FÝÝÝ©W¯W¯^Õ•8p¥RIûöí±´´Ô•ÛØØÐ¡C”J%aaÚqþСCܸqƒ?þØàVz9³ÁÛ·o'==Î;çšUwqq¡U«V\½z•Ó§Oçz­;wÖ}njjJ³fÍíÏÉ“ë§ýýµ À†–ç´oß>×,l¹råhݺ5çÏŸçÒ¥K¹ê*Š\×,­rŽòîСCIwÅè‰Z(6¾œÀ†Ì (›)!g™ž hªh¸Üö2&@©T¾”k¸q€Æ‹v3ŒNW`ÆîGOT­ª]—ñÔÛk×BÎîNÍšÁ7ßäß¾*^E̯1¤^HÍ·ž™“å?(e-Ë|ë ÅÇÁAûõÞ»·èo¾¶A.v+Vd̘1lذäZûᇒ””Äï¿ÿh“Á–.]J«V­tÛ¡ÖìÙ³ñðð |ùò4iÒ„æÍ›óÑGp÷î]½ú†¶Åj×®7oÞÌó:QQQ€6jÚ´)¾¾¾øøøÐ¤I†  f<<<½^ºM›6zI­9ËAžn'§<1Q—¤œïOCe9÷‘£uëÖÏ´¦Pz‰5ÐB±¸~ý:o$³ekÁMàºïu†|<„СEn?##ƒãÇóßÑÿ¨R¡ -š¶ÐmKöýáï™´k* /%lØl†\Êغžšý¸t r&㜜à÷ß!¿œ ŒkÄmˆC“žÿbXËš–¸ôpA®·ssíIϰ‹ —Ã[ô–˜5kÖ¸'s‡¨V­‹/fРAlÞ¼™¸¸8~øá‡"]kÉ’%|úé§ðÞ{ïQ§N*UªÄÉ“'i×®]®=‹sJn³±±Èwv4g¶Üßß_ì<)çú9/^ÌÇÌÂ… Ùºu+C† áÓO?eÉ’%ºdÀ‚DEEѶm[$Ib×®]EÚðE¨]»6‡æêÕ«EÞÇ´ïx{{óí·ßÒ³gOüüüX¾|93fÌxæ>åµ£„ôDaN®OÎ×õI¶¶¶Àã¯uNÝ‚vo)L›Oçå·ûEaî#‡¡kæõ=[ÐÏžPöˆº˜¼êI„;öìànù§f¥ö<~Ä.Ÿ¾\ä¶·m߯ø™ã‰q‰!É1 “ã&¸þæJS÷¦hÚjؽ×ls·:`• 2¬Xuëæj+=!%åq•ü¶ÃL<œHÂÎ$Mþ‡£ØùÛáð¦bç%‘D¨OœDøbÈd2† ÆÄ‰9}ú4‹-ÂÉɉÀÀÀ"µ³qãF¬¬¬X¼x1...ºòäùš={ö0yòä\e;wîòßs6gf¼^½zŒ;¶Pýóòò"$$„:Ä»ï¾ËŒ3 @GGGÓ¶m[233Ù»wo‘gæ_„·Þz‹eË–1oÞ<½}¹‹Ê××gggîß¿$IÏ|Beaäü³sçÎ\Ivÿüóðø žœº‘‘‘ùžx÷d›9K„žnÓÐV/‚¡ûÈùž­V­ÚK¹¦±I„SaÅ$66¶¤»P¢Žœ>‚äôTyxjâ)21’zsëÑkC/¾Üû%kήá体¤f^±~ÓzÞÿþ}.¿q™¤úIPÔµÕÜkqPëP¶ÎÞ T±­ÄåƒM°»ùèë0eŠÁEÍ#F@Îò͉¡S'Ã÷#©$”›•ÄÿŸoð,3“áè‚C»ÂϳfÍ*\ÅWHÎI„E·Í÷‘™Y¸Ó ó¢Ñ/ðpø¹®ñ" 4 &NœÈÞ½{8p`‘Q&%%ann®7Cgh÷‡Ò[J±cÇLMMuëP ÉY;ýóÏ??ÓRŒ-ZмysNœ8AFFF¾uoÞ¼Éo¼Arr2;wî¤^½zùÖ?yò$Ç/rŸ D‹-X±b…Þ)“9"##™óhÍ””ÝR—§mß¾¥R‰——×K žš7oŽ¥¥%;vìÈU.Iÿüó–––4mÚTW×ÁÁAw L^Ú´iƒ\.×k3++‹Ý»wãääDƒ ^üÍ€Þ5Õj5»wï¦|ùòe~’ãUY CÌ@“WýTŸÆÞYzx)<¹Œ°£~=)[âÜÃsœ{˜; I†Œ å*àåì¥{TVTfâQ¶SNÝ€úP5²*çÝ;a¹ïgmyðÿ§W}Ù2í U+˜>Ýð½dÝËB¹EIVLþI9¦ö¦¸övÅܽðLhû±¬Hõ_Ï’D8vlo:v,LpÜ;Wr[Q4nܘóÊ´áëëûL×xÑœ Ôí‹\”äÁo¼ñ‡føðá|øá‡€ödÑœå†5'''Þyç¦NŠ .$<<œ>ø ßãÃmmm™7o½zõ¢E‹ºã«“““¹|ù2«W¯f„ ¼õÖ[üöÛoìØ±ƒ   jÖ¬IFF{öìáï¿ÿ¦Y³flѾ}{¢££éÒ¥ [¶lÑ ^©ûÄ»V:u2¸V9$$$WÙíÛ·ukëÖ­[àŒ¿L&cñâÅtéÒ…€€úõë—ë$Âdz|ùr3f J¥’Zµjѹsg:vìHíÚµIKKcïÞ½lܨ=au„ ù^óEððð`üøñLŸ>>}ú0jÔ($I"$$„k×®ñÅ_è¢qttdÆŒ|øá‡øûûóÙgŸé¶±Û¶m´hÑooo Ä’%K1bÁÁÁ¤¥¥ñõ×_“˜˜È¼yó .µxRRR8p cÆŒA¥R1}út|È©S§pww%×JI•%ä+åhªæ³N8ª¹Uû–7‘ÊH®Å_C-i×RJHÜNºÍí¤Ûìºö(c?¨@þ3»µ!h£ËßÏõëÃòåÚõO8wN;û àê ëÖé'‡i²5$îK$épRK6ž \{¹"·oò”//¯—>KäêêZ*wÖ:t(«W¯¦uëÖÔ®]»È¯ŸoY¹ÈwñâŲ6¥Ü#t)ñ.9ä§ÉÓp÷vgáÙ…J »t:þ€©/ž+ÛÍfŠRq$ž8Éÿb_`f¦¾ÀHVlLl0}®C£Ý診…È$¨ _§ÁàŸ 0WS¦xÍE`€÷ÞËîp'çžÏɈË?G™¦±&•WB¯ZÞ>–ÁÃÃC\õç 3ˆPS):¡¡¡Ì›7³gÏòðáCŽ?žïª @ x7 ~Ðù t)ñ®|ïEÝcľ\}v}-}v_È¥•JzÄ 3Õüº}¿ÕÐÅ; 𨨨‰èÿ}÷Û·²;"÷`¾GÀÖV˜OŸ®rÎÏOQm JغUá‘—ÎóÃÏI¼›˜ÿ R¡w4 ‹GŒñ¬Š¨Døú$''óàÁZ¶l‰‡‡Ýrú) @Q¦S§N%œø&ð®h–×AhA±±êò*fARº"Õ”£µ#ø“ðQâT¦þÎ|\™Ïs•¾‰Àv¹=zF÷¯Q£&»òèÈr‹á÷ÐÔäÛ³çTŽ¿xƒAJŠ"XpǨTI"îj<1Çb'ç]@ËD‹ }*``'ªO Ê?uêÔÁÛÛ»¬ÍÊ=}ôQ¡s£ Þ=„€¼6aña|rð<ý=Ði0«ý,æv™‹¶Æ%üÒqb'uùš‹ä”œK0¤ݱIº†ö¿ÿ*Jêé)6}ýÿö3·Wß­]Ë‚¶mY­¦tð#@·W/ª«IxÿÉ' ØŸ?Ú4L#|s4ÉAHI&ãƘ¿gކ®ˆà]CèRâM¯D˜[U¢÷0ö¯±D%FPÓ¬&[l¡}µöjÇ Æƒ…ôc./ñlQÔR¹OGøAQð#ÕôQ*Èd §Gu==´“’x*«ÐK,-™õë¯*]W¬€}ûû}{Ãøv/xº:)=ÿ„4Úµ±øÀ½¯ïëœ"ˆPD(¥‡¨D˜?bù¬”xƒ“““?m<Ž}±ûÀ»žv8}èÄYŸ³Ä§Æ3úÐhúïì¯Ï£ìGq}âõ\ų6iÌá‰#X Ä«‰…5N’u·cb˜‘œÌ’§šNN*~\/ÂÌ™Šý¦USYÚí)±ÿÆä+žeš2Ì:šQeB•Ï *ª£è•@PXÞDÍRÚˆèRâMsȈˆ £sG’Ö2MyüAÚ>üöC$#‰XGE¥­ŠY÷Á:ú×W-‰ÍËç\ ;Ž\ç, Ѽ…ïózš³œè‘LÊîìßþ ÆZZ œœœïßêIIh=ʣǕ«ÐKll˜µti6ž?‡ÁƒQÚh½`Iï—h¿ÌÕY·ª.Z cY¸Š‚EEª"‚ ôxÓ4KY ´@-.ã]¸ßæ~öÒÛÚÓ1ŽO W§^lì·+£\r®J© —±ðø6tQ¤ƒ3¦›‘3‘›Ì§,!"S*’ÜÄãJùgáÈÊŒ¶oϯ¡¡ŠÕçÖ­³}H¸ºBjHÎ<ç£niTÍ;å(2mæ]Í1nmŒL£dö @ xó.¥DHHx#ª¦]¸p¿t?Uñœ•ÎPó~M?œ«xN¾DhÝ.è|9])ƒ 4ñ`6­¸Ä]~ækÌÙI]Òé¬ìóâ´kýU8›«W¯ŽfëÖå7-îÅ‹ ÛÐÚ9UÍ(ÙééÁš5:,Xë×CF†bì;àë¯áóÏsÀ?­]‹¦¦&qWãx~4†íäÄ)t<ï¿ÖÖª}Ê[AD¨Š"Ì;vd»k¡§§‡­­-mÚ´ÁÖÖ¶Ðã¹»»¸íŒ3ò­Övúôiºté¦M›pssËwÌŽ;[&wbRSS9vì'Ož$((###4hÀ¸qã077/ð8éééܺu‹Ë—/óäÉlll3fL‘lzüø1^^^ܾ}›àà`lll¨_¿>C‡U±)!!ßÿ7n†¹¹9¶¶¶tîÜ™víÚ¡££„ cÍš5´hÑ‚¾}ûÉ®œ¨{ßÔkÚ´)fff\¸p¡@ãÊårîܹÃåË— Á‚Ï>û¬XläΛ¤YÊ ! *ܸŽäWê‡Anî¿´ˆ 3á矓ømi2_‡Mf[þ;Ýr0Õþ^M÷Ê?~ãÇ+° «WçŸÂôépô(¼| ³fÁš5°x1 qqq$$$dë—“AüÑxÒ§qþ<*Ž7nlD‹9&QÞ‚‚‚Øy`'§/Ÿ¦‚YzuìÅGý?R Šâ 66–{wpÜç8r¹œ®m»2ì£aT¨P¡ØæPÇŽ;8xð 2gkZZr¹mmm¦OŸÎ‚ 5Þܹs Üv„ oU¹c{{{îÞ½‹¶¶6µk×&$$„ÄÄD.\È?ÿüCÛ¶mó#00Æ“””¤<Öºuë" è 60mÚ4âââ033£N:œ>}šèèhfÍšÅâÅ‹™0a·nÝ¢[·nDDD ««‹ƒƒ÷ïßgÛ¶mÌ;ooo:uê(ôܹs?~|± è’ ..Ž*Uª¯~%žS ÌÉØ²š—v¢Uùõ„CãÆàåÿü™ž/ÁGAÇŽ0hз88,¤yóå´l¾‚aM6ócëÃüúíV­º€Ïà2ÙvzôÈ^aP»¢6VnVXô±âù-gÁ²8pböÝÙ®}˜?þdÔ¡Q8tsÀ÷šo±Ìqàï8ördÒÙIìµÚËþ*û™ra Íû4g×þ]Å2G~‘œœÌóçÏÙ´iVVVxxxpàÀB#IR¶mûöí,[¶L圕U.ÙwÞPìííÙ³g ܽ{—ˆˆ6lØ@LL ...rCÑÖÖfÈ!üòË/øøøÙ–%K–0vìXªW¯Î•+Wˆ‰‰áòåËDEE@ïÞ½9uꔲýðáɈˆ`Ó¦MÄÆÆrþüyîÝ»Gdd$kÖ¬ÁÒҲȶ• .¼Ö œY¶l§N¢bÅŠÅh@ðzˆèRâMªDø4à:mÓvRÇW®i€Ž\q©%é¦LŠå$pˆM¢È¥,uïî¦Pµjæñ÷÷ÇÖÖ-­¼?†½{CŠÕgwwˆŽ†3g2Ï~‡6&t%k’ITÓßÀà™î\2M¦íM1íhŠL«|fוU)j%Âõ¬gÁÉÄuûï ¤ÖKåŽíNÈåC—_ë‡ùÚkŒóGdÈlKR ‰ jA|ºìSjØÔ uËÖEž£0˜ššâææFtt43fÌààÁƒôë×K—.aff†JŸøøxnݺE5°Vç礆ŒŒ Ο?O`` iiiÔ­[—V­Z¡§—{¡¡ÔÔTüüü¸}û6 4 eË–ùþÿgåÙ³gøùùñìÙ3ìììhÙ²¥Úji’$qýúu.]º„¡¡!vvv4oÞ™,ïÿùmÛ¶e{lhhÈèÑ£Ù´içÎãîÝ»4nÜ8Ï1ªU«ÆÆ üœÔñôéSæÎKµjÕ¸téÙÝËjÕªÅÎ;  22’7nкuk7SSSÆ_d[®\¹BzzºÚs&&&4lØ0×¾·oß&==fÍš©=Ì… ÐÑÑ¡]»v*"ßÐÐ-[þ»³©©©>p]PüˆJ„ù#t)ñ&Uõ©VÏ‘gwÂøCz@RÖ³r …3DÐ CX¼Ù¤I…šgåÊ•¸»»cf–Îg--øì3>æÍƒ_…´4Ð'ƒî„Q܃R4^‰šÒ.ˆRT<<<„t2ƒ ãýòåK毛Ÿ]}J•*ùTï)FT\Ô®]€‰'2bÄ8ÀàÁƒ•í$IbýúõØÛÛÓªU+µcåD&“ñùçŸ3|øplmm‰åèѣ̜9“uc IDAT#FpçΕի¯¾úŠÑ£GsòäIôôôØ´i“&Mâ“O>áŸþÉs¾üwwwF››Íš5cûöíÌ›7rëÖ-´µµ `öìÙôèу_ý•Úµk“––ÆåË—‰-Ì˧$!!OOO,,,hÔ¨Q‘Æ(,·oß K—.joccC½zõ¸wïÎÎÎŒ5Šž={æy7   1¿¥Á!žŇ÷oR,òwz©ý’Í>›QÜN)$Á@¼?""¹yëf‰ èÈÈH¢¢¢Ø³g+W®D&“1|øp>úè#¦Nʺuë² ècÇŽñèÑ#V­ZUà¹,--™3gŽò±……C‡%88˜/¿ü’Ó§OÓ½{÷l}tuuY±b¯n 7޽{÷røðaüüüpppÈõy-\¸F±aÃåñqãÆ‘À´iÓØ»w/C† !00I’puuU^8hkkYèLž<™ÐÐP¶lÙR(w“×áþýûhhh`ccSà>»víbÖ¬Y:tˆƒ¢­­Í{ï½ÇŒ3èÚµk‘m©\¹r¶Ç‡fþüùôë×~ø¡Hc&%%ñí·ßboo@½zõððð OŸ>,Z´ˆ5kÖÙ^ ´ºÄÆÆLýúõßrÿ }ü-pòY(]rd³[Žxe¼“qeà“–x?‘È=‘h¦çü—‚—©€?FTÕ áßEšÖoŠæM2,ó¾í®•¬EU몹gœÉƒôjé„= #ƒ¼ç0K1ö†má'(M›6Íö¸B… ¬ZµJ™yAWW—O>ù„Å‹ ˜k×®ÅÐÐP)´ Š$I=z”›7oNZZQQŠ–=Riß©S'¥xΤ{÷î=z4OíëëKbb"ýúõSqÈ`7nÜ`È!tìØ333¾ù梢¢0`U “¡ŽŸ~ú‰M›61räHFŒQäq KJJ ššš…òùmÖ¬GŽáÁƒ¬_¿ooo<==ñôôÄÑÑ‘;vP·w ³ríÚ5\\\ppp`Û¶m*ïgaÈyÕ¡C´´´¸téÒkÙ(”B@‚‘#GrâÄ "##¹~ýz¡½Þ¤ ÂLž·6föI¸óß1o48ÃDÒ–ªì|­ñ D˜•¸«q<ÿç9’\ʵÍK´8†%qù¸v”WD¡*E "ìЮ•ÖU"¬nniàhéÈÅ/.Ù¶:íë@@žmŒ#Œ_[¼äÇòåË155Uænܸ±Jйñãdzxñb6lØÀ‚  ãСC¸ººbbbRà¹bccéÚµ+~~~èëëS¿~}ªU«¦L+£Ò§C‡*Ç:wî @h¨ê®L2+PþøãüøãjÛdÓéêê²mÛ6¾ÿþ{¦L™Â”)SprrbòäÉ 0 Pß5«W¯fÆŒ8;;óÛo¿¸_q`ggGPPOŸ>-Ô*4(Vs/^ (\AæÌ™Ã¾}ûøì³Ïðòò*²MOž<¡oß¾XXXð×_å雵k×Vy^ÆÆÆØÛÛ˜™{TP¦ˆ Âüù» Áš5k -p”zVÞ¤ B%¶ño §²ú¶D2'×.…!3P¦ ÄzÇýWtžâ9 cýÆŠg•ÕQ”J„666ônØ­§¹‹&ëKÖ¬ú±à® ê˜=~6¦7Ls=o|˘Ï?þüµVë ‚‹‹ nnn 2„6mÚ¨ÍÏ\«V-zôèÁï¿ÿNzz:7n$==qãÆj®ï¾û???~ûí7ðõõåàÁƒLª(Œ$Iªÿ£™"7+™+Õy‰÷Ì ã7òìÙ3µÛêÕ«•í{õê…~~~¸»»Ž‹‹ ³fÍ*ðóÛ´iŸ~ú)½zõbçΥ溑Iƒ EŒ×¡Q£F¬^½mmí×J©OŸ>}ˆçŸþyíô…OŸ>UYT’$‰ààà• Jž7R³”2B@‚×¹â~“òµ´À¤ú0y- ó§é$2žèÕÂÜ|ææËIMõ{­y–/_^ /KI.u(ŠX#€ž Ï¬HæÍNu$‚U)j%ÂU‹WÑ1¢#¦wL!«¦K†jgª1µïTš;4ϵA3r ƒª ¢ò¥Êd‹¹Í€JW*Ñ¿r¦ŒŸòZs''N$,,Œ:x0“K—.amm››[¶Ôpûöí˵ÏÉ“'UŽ?~€š5kæÚ/3pïæÍ›XYY©ÝLMU/`ìííùî»ïxðà-Z´`Ë–-¹¦cËÊÖ­[3f ݺucß¾}ÅZl§ Œ;---ÜÝÝÕ^Œd’µXKn```Pä‹€ŒŒ Ì;wسgO±R&%%©úÛ·oYèT•‚’áMÒ,e…Ð~úi&]]ÂÖ) Z¢X…^Y©"{O~ω9q¢3G®ÀØØ¸Dm‘Ò$"wFï›ÛJu?éü‹D:@X¶M.W—Zð. ««Ë¿ûÿeIŸ%´¿ÚžF>p¸è@Ÿà>ü»ú_þ7åÅ2ÏúåëÙýånºÜîBÓóMir¾ ntbÛ´mlþus±ÌQ\ôíÛ¦L™BPPP¡WŸA‘[8,,,Ûªr¦;Hnܾ}›k×®)GGGsøðaªT©BÏž=sígooO—.]Xµj—/_V9ʳgÏÅŠvN÷¹\NZZñññùæÞ¹s'®®®899qðàÁ|³XÌš5‹iÓ¦åÙ¦(4lØ)S¦pýúu Dttt¶óééé,X°€I¯R‡†……áîîNDDD¶v’$±téR^¼xQ苤L&OžŒ§§'kÖ¬á½÷Þ+ÚRCÖüΠ¸pEê;àMà­ó–$‰7npåÊnܸAJJJ®½?fåÊ•\»v 0vìXåÕuPPÎÎÎ|øá‡Ì›7¯ÔžCYS¹rUþ} õÁé‘I¥ 0ØÍ6mÚ”š‰Dl‹ %T½ÿx£FµÐÖö#Ü2† «hr[ÃÒÑ©P,éœo.cFŽaÌÈ—R. :rÂéD‰ÎQhjj2nÜ8æÌ™S¤àAP¬zyyÑ®];œÑÒÒbïÞ½ôíÛ—Í›Õ_0 8÷Þ{`ddľ}û cëÖ­ùþ®[·Ž=zжm[œiР±±±Ü¾}›Ó§Oó÷ßcmm——Ó¦M£gÏžÔ­[—ÔÔTÎ;Çõë×ùßÿþ—oPÞèÑ£ÉÈÈ ((GGG•óK—.¥wïÞÊÇ›7o&99™¥K—fk×­[7žüôÓOyÚ Ÿo™LƲeË8~ü8Ô©S‡ÀÀ@®]»FLL ß|ó ÉÉÉÌ;—… âèèHíÚµ‰çÔ©S<þ\PZX>|ÈêÕ«177çèÑ£=z4Ûù† fËÆRPêׯ¯¯/]ºt¡mÛ¶\¿~ÇÓ£G ”­mÿþý¹{÷. È0£|-[µjÅüQèù‚âà­ÐáááØÛÛcdd„‰‰ OŸ>U+ ƒ‚‚èØ±#–––ôïߟ'NàääÄñãÇiÞ¼95jÔàÂ… @ñT?z“‚·l83ÅíµîOõ1 •OkÖä#w÷b'¯ ÂôØtÂÿ '-*÷ ×£ÃÌ6˜´*xðÓ›€"T¥¨•ߺv튙™úúúîÓ·o_æÌ™Ã!Cò ¬Y³&®®®Ù*Î 8½{÷òóÏ?óï¿ÿÒ´iS–,Y¢L—Ö¤Ie[+++\]]3f cÆŒá·ß~ãÔ©S´hÑ‚?þøƒŽ;f›¯OŸ>$&f¿kT§NnݺÅO?ýĹsçØ¶mfffÔ­[—7âää@=øâ‹/ðññaÿþýT¬X‘^½z±lÙ2Ú·oŸïë2tèPÒÒrÿαȑ²ÓÅÅEm{ªU« ²èPÐϰ®®.K–,aàÀìÚµ‹›7orâÄ jÔ¨Áˆ#7nœrÁ§zõêœ:uŠÃ‡sùòe.^¼ˆ$I888СC&OžL… ”cW¨PWWWµo²bdd„««k®ç³º·¨{ßÔëß¿?Lœ8‘ï¿ÿ///tuu™7o_~ù¥Ê]‚&Mš(]tr¾–â{²äA„ù#“òr°zIMM% ;;;-ZÄ—_~©Ö‡lòäÉìß¿Ÿ»wïbllLzz:­[·¦J•*üõ×_jÇöññáÆ|ýõ×L™2…úõëãââ’¯Mnnn„„„pâDù_ÊÈ€ºãxäb†^†œxM4Ó3`Ú4(ÀªIa˜:uªÚJ„©a©„o '#.÷Ô`2-•V AÑýÒË+nnnÂ:±±±¸»»góƒ^³f Ë—/W[åL7³gÏfÑ¢E\¹rEíJ«@ x{É,÷ž×ïL×®]©^½ºø-ʃ·ÎZGG‡ äñþçŸÒªU+¥¯––]ºtáðáÃ*þf™$%%ËÌ™3ÑÕÕååË—¶ëMqÈß¶ ¥]™œVOPˆg€W+<ʼnº ¤À$Â6…å)ž5ô5°üØò­Ï ‚ÕQÔ B*—.]âçŸfĈB< µ¼)š¥,yëtAˆŽŽ&666Û-IPÜ*’ËåjÓ-¯möìÙÊmìØ±žsß¾}ôïß77·l[ÎÕ³²ûì3Ž?®|¬§§‡­­-;vdâĉèë.ž 0þ÷gÏž¥bÅŠy¶yðà'NdÓ¦MØÙÙå;æÂ… ‰ÅÅÅ¥Àvû÷ïgÛ¶mœ òlçÏÅ{÷¨\¹r¡íºzõ*Ãzôà=“<ÛŽ‹ãçÝ»yï½÷ =G~„††rÿþ}Fމ¾¾>QQQxzzrðàA6lØÀ•+W0Èç5ÈŠ}·­Ο?ƒƒõêÕËv®$ª¶–%_|ñ‰‰‰|ðÁÔ¯_Ÿ»wïràÀFÅãÇùöÛoó#88˜aÆ!“ɨS§>ÄÌ̬жDDD0|øpŽ?N³fÍøøã©S§\¾|™±cÇræÌ6oÞ ÀÊ•+™ëèÀ¬YŠýs³PIRxÌ—_s4)CʳA*:WDCWC…å‚Ϫ%ˆPWW§‘#9>oïåâfñSõêü°hÑkÙöŲe,ëÝ›yÑÑjϯ65e₯5Ga144dÒ¤IxyyqåÊ$I⫯¾ÂÚÚš)S¦¨´¿}û6ëׯgèС´nÝ:ßñÓÓÓ9|ø0^^^<|ø¤¤$êÖ­‹‹‹ ={ö̵ߥK—زe 7nÜ ~ýúL˜0‡=§äädV®\É¥K—xöìvvv¸ººÒ!Dz„„¼½½9~ü8/^ÄÐÐ;;;FEóæÍóœcÀ€*Ç,,,øè£Ø²e ·nÝ¢mÛ¶yŽajjJ÷îÝ ôœrãĉìÞ½›ž={2gεmzõêE¯^½…ßj\\}úôQÏ@±/R|ùå—$%%©=W§N>ûì3å㈈–/_ŽŸŸr¹œfÍš1eÊ;›4iR¬6 І··7GŽ!**Jè|7ÏK‰æÍ›ãááAçΕWxr¹¼Ôí¸zVì66 !q!ôºéÐôñ«'ô–§Ê‰ÜÉsÏçyŠg™† óîæTv©Œ†®øØ J‡±3g²¾F µçîæ;yõ9“­Z`gGŒšsIÀùš5yï•Ð)M2£èSSS‘Éd<{öŒéÓ§«½m¿téRV­ZE\^«œ„††Ò¿nܸA•*U°°°`÷îݼÿþûÌËôËÁÉ“'éÕ«áááÔ®]›àää„——W¾óâààÀÌ™3ñõõÅÚÚšíÛ·Ó¹sg~þùge»´´4ºt逸rå ŽŽŽXYYqìØ1¶lÙR ç¦Ž/^”ZZ¯Ý»w0{ö쵯T©gΜ!:— ¹âäáÇܻw/ÛvùòeV¬XÁÞ½{•í®\¹BÓ¦MùùçŸ100ÀÔÔ”uëÖÑ´iSNeáÊï¿ÿ>Ë—/§U«VemJ¹G¬@—û÷_âÔ©ÿn&%±~ýœKÕŽÌÕg--Èün¾q›ØäXœž‚VÚ+÷‹è´èW)ê"ò¢Ò4Ô¤ÒG•ЫY¶®+‚w]]]:¸ºrlÞ<ºçX…þI&cþŽP ¾$–ss_mjÊ„|íñ‹ÂÆÿVö&NœÈæÍ›Ù¸q#_}õ•²Ý‹/رc~øaSlVªT‰€€lmm•ÇRSSyÿý÷ùñÇ5jÕªUËÖç?þÀÛÛ›N:ðøñcZ´hÁĉ¹ÿ~žþÔ“'OæáÇœ>}Z¹âœh8{ölú÷ïOµjÕ¸rå —/_ÆÝÝï¾û.ÛQQQzn9¹víGŽÁÉɉªU«iŒÂrïÞ=@|^Œ>|8[·nÅÆÆ†Þ½{Ó§Ozôè¡vEúuÙ³gO¶ÇéééôíÛmmm¥Ÿ¸$IL˜0¸¸8.^¼HãÆ¸ÿ>mÚ´aüøñܺu«Dk‚Ò@,– Ž<~¼F¹EEM(u®_‡C‡û ™¿}™î…) ’x/‘gëžå+žumt©2¾ŠZñìïï_îÒD•2<ÿ‘žžŽ¿¿‘úª[…¾T$*§¦BJÊko-RS ²zš&>¥¸ú¼råJ<<<˜9s&uëÖeëÖ­TªT‰iÓ¦кukذa’ôßÝ¢?ÿü“ÄÄDÆ_๠³‰gPd2ùôÓOIIIárfyÓ,Ô«WO)žªU«FïÞ½yôèQž«Ð·oßæðáÃôïß?›»†™™“&MRf½”¾Ëê2ä—-D/_¾ÄÅÅ™Lƺuë Ý¿¨<|ø]]ÝBÝÙ´i[·n¥iÓ¦ìÛ·Ñ£GS«V-FŽÉ7JÐZøôÓOñòòbݺutíÚ€‹/rõêU:vì¨ÏvvvôìÙ“û÷ïsìØ±µKPtÊ[âƒòˆ¸ô+5¡øÙ®øj+}~ø$ 45!ËçBºë êÔKKµcHr‰Ø±¼8÷òvwƸ¥1Þ¯€LS}ÍÊ•+qwA„9A„ª¼N%B]]]:ººrtî\z¼º`[bb®®Š$èÅÄÔgÏX¾{7î¯|C×—êêóüùóÅóµµµeìØ±|ûí·ÙDØÄ‰7nÇŽ£G¬]»–Zµj:ÈÑ××—+Vpýúuž>}JjjªÒ--$$D¥}¦°ÊJ÷îÝùý÷ß Ìuž;wîŠôœ. ©© —³»wïààà€½½=³fÍbçÎ 0€!C†P»víB=7PøR÷îÝ›€€víÚEƒ =FQ©X±"Ož>è>µkóE)ŠƒgÏžåë‚1lØ0f̘ÁºuëèÑ£çÏŸçæÍ›,X° PÙ"Ξ=K×®]•9†]]]±µµ%$$„©S§’œ¬ú‘3ü—s:&F¹‚LŸíððp¥`ÎJÏž=³ù&ûøø°eËV®\É7ß|Ü9sèÛ·/ ,(°KDrr2ýúõãüùóüñÇjƒ K;;;®_¿Nppp‘DŒ££#ŽŽŽLž<™ 60vìXæ#}ï IDATÍ›WìzÏž=Ìž=›aÆñ}¦à+2}±ÕÙŸy,22²Xí¼>÷îÝãìÙ³Îoÿ.#t©Ñ(X@HI¹ú¬¡Y3Z…Ň@ãH0Jxõ£F@§„¾JQ÷2ïuZæZTv©ŒŽ•Nží‚ÒDWW—NnnxÍ›Çn++~\¼¸D智l+úôÁ4-ñeäûœ†††|üñǬ[·ŽððpÖ®]‹¶¶6£F*Ô8+V¬ ==}ûöѬY3åñU«VåÚÇÇLJ/¾ø"Û±³gÏäé[œ™ `„ ²S__ŸqãÆ1nÜ8 È«Õ&áf‘»# Y’oŠ:d`ÖÅŒJC+¡¡Wð”"T¸òWåu‚3ÑÕÕåão¾az ­>g2uÙ2&”rÞçÂШQ#:tèÀâÅ‹IJJ*Tð`& 6äéÓ§¬]»–ÔÔT$Iâ÷ßçüùó¹öyñâsçÎUþÏïÝ»—cǎѵk×C’$ ”ï¼!>>žÞ½{+ï@(S³&''+ÝlêÔ©ÃðáÃñõõeåÊ•Èår$IbóæÍœ:uо}ûfËË­î5 Îõµ”,"ˆ°H‚ÇÕÕU‚.’‰"sÛ/íß¿¿Äç$--Åœªžo½¾µ„;R¨¹©ô;)Ìq¶4/HzôÝ£mÁÁRâÃÄ"ÙöùçŸK111¯ù ß>\]]ËÚ„rGLLŒôùçŸg;¶zõjÉÎήŒ,*?ôë×O¤gÏž¸Ï¶mÛ$@ªS§Ž$—Ëól»}ûv –-[¦< YZZJ€dbb"I•+W–vîÜ)Ò‚ ”mOž<)ÒÂ… ¥ HúúúR… $@²··—‚ƒƒ³Í׺uk•÷5%%Eš5k–¤¥¥%Éd2ÉÆÆF255•IWWW:{ö¬$I’´aà Ex³T©R%ÉÜÜ\¤ÚµkKÞÞÞù¾.ºººÊþê¶íÛ·gkoii)™ššªŒS£F\ÇpqqÉ׎L|}}¥Æ+ûjkk+÷¤cÇŽI’$IáááRÍš5•çttt”m555%WWW)--M9®ŸŸŸHãÇÏ׆óçÏK€´iÓ&I’$éîÝ»y¾F:uRöŒŒ”zõê%’±±±ò=ëÔ©“ôäɵóôµWW×|gºté"~‹òAø@—FÀ5ÀêÕV:,X™‹¼9WŸãÃãI½”ÊÇA}H‹©H42°hùTÌDÇZ‡Ê.•Ñ2+ÚÇHªGªòºA„o3ß}÷&L B… îS¥JÆŽ›ïmýÎ;ãéé™- E­ZµxðàûöíãÉ“'4mÚ”öíÛ£««‹§§'vvvʶM›6ÅÓÓ“&Mš0iÒ$N:Å7hÒ¤ ݺuSÉ2±|ùr•;S:::xxx0nÜ8.]ºÄÇ122¢V­ZtíÚUY^{ôèÑtìØBCC©X±"Ý»w/pEµ¿þú‹ŒŒÜ]Õìíí³=Þ¾}»Ú‚X›7oεRŸµµul…ûŠŸŸ·oßææÍ›,”ðo>™éÕ-ÖdºÌlÛ¶† Š<+Ð¥„ÍKš6õ'"Ÿ«W·!I°hÑD† Ë5åòkãái`M2_uK$tY"é/þ[Ù y¡ˆ¬þâÕ--xµ2•2-zUÀ¸¹qÉ-J„ØØX<==9sæ Çç×_âY d£M›6´iÓ†k×®•µ)å! K ''G¶oW|üô̘aaàì ÞÞ &ˆ¹ÈÈÓäû$soC".$Ò¨ŽËç3\ïñËÇÔŒÓ2 jUEž;5hkb`g€q ãb˲áïï­­­(嚃{÷î‰ö9HOO'((ˆ:Y˜ EPPÆ £zõê|ñÅE ï)))j³«þCdá(%ÂÃÕûÓ§Cfv¤ `̘×_ž$'þz<;"x¼è1^³"¨‘.r²TÏÍÆãÑM‡Šq¯|þrøÃi[hcÚÞë1ÖØL³Á¢¯E±¦¨[¹r%ñññÅ6ÞÛ‚‡‡GY›PîȬD((:öööH’Dpp0K—.ESS³¬M唬šE ±ôWJXÄ[ðä×'hèj Ó‘ñC7 ´/hpý®Œ;jð«¡Œ‘Ÿh"Ó•!Ó‘¡©›c_G††®dq9Îx™¡ôgNN†W±, põªb¿fMõ.ÍQ‰Q$¥'QçE–ƒÕ«£[Eýúú60D»’v‰½ ‚sCøœ©"‚ ô(H€é»ŽÐ¥„”*‘™½4æç}aÝSxñ®®ƒ:/ KðºZ4t\¦%#=V}åsçþ˼‘ëêó+÷ê/%ôÃ@#¥hÙ˜êy @ ¼kŽ2ÄÀ† Efè}û ""ï>òT9ñ¹ŠçÄD¸rE±_£†bˉL[Æ-£[œ­–ÚìÁ /Lšh ñ,@P„€.%î„ÝaõÙÕ\ ¹’íxåÊ0`€¢J`j*lÛ¦ÁEåüyEÚ:€Žÿ;®¡¯Q3#*¹T¢úÿªó§íŸ„Tò§ÅÓW¹J³”ï.-D%Bõˆ´lªG%B@ äÍÙ³gñððÀÏϯ¬M)÷]JèjéÒ£~jW¬­rÎκuSì¿x;wB¹üs%9.]RìÛØ@={-LZ™`5ÒŠê3«S±E ̓è´x :i¯§œŠøÌŠŽ"T"TE AÉS¿~}œU  T>Ð¥D튵ՊçLÚ·W¸oܸ¡(€ò÷ßЯ_áæ¸p"Sµ Æ—T®£¶Ê˜ÏcœB²,-‚ÂÔ#‚UA„@PòT¬X‘Š+ŠTª@èRB¯ºzU@ž"GJ•§ÈUöŒ’x¶\ÎÓ`9~×$*W†¶móXºUt‘WÓç»å†„ MóæðþˆÜ»œ{|€!2@[[Eh@ A¾]JhUФµI¾í¦…–-!ô1ì8.gï9=º¨Ü2MúuõÑ2Ñ⇠äUJºo¿Í{Žs!çIÐ1TH/“Õg@ ‚7! K‰”””µ³´„C‡š6!Aƒac4ðñFrïË–)ö›5ƒ?̽mjF*Wž^¡A˜$¼ à+#-*ªGT"TET"Ìðüùsåc===lmm133+C«Ê–û÷ïsíÚ5ž={†……µjÕ¢eË–èè¨/%Iþþþ\»vèèhš5kFÓ¦M144TÛþÒ¥KèëëÓ¤I“"Û˜À©S§ ÄÌÌ {{{7n\àþIIIøúúâïïOLL U«V¥E‹Ô¬Y³Ð¶$%%qïÞ=RRRhР¦¦¦…Cðv!*IP⸺ºJ]ºt)TŸ½{%I&“$¤Zµ$)**÷¶Šv I»wç=®Ïc w¤qð_§[· e[qñùçŸK111e2wyÆÕÕµ¬M(wÄÄÄHŸþy¶c«W¯–ìììÊÈ¢òC¿~ý$@ekÒ¤‰äååUbó>|øPêÔ©“´;¿/R$>>^8p $“É$@ùôõõ¥'N¨ô¹{÷®Ô¬Y3e»Ì>úúúÒªU«ÔΣ««+5kÖ¬Èv:tHª\¹²Hʹû÷ï/%%%åÛ?99YÒÒÒRyÏ555¥O>ùDJKK++V¬š4i"ijj*Çðôô,ò󼸺ºæû;Ó¥Kñ[”" G)Qت>À÷ß+öaàÀÿÒÓe%1~úI±ß°¡¢]^œ Qø?++TPt,–/_þN¯’å†"TEæÏÞ½{9yò${öìaĈøûûóÁpãÆ™/>>žS§NZ"ã…Ï>ûŒ½{÷2lØ0Ž;F||<ááá=z”Aƒ‘–ãKôÔ©S4oÞÖ¬YCPPqqqx{{cooϤI“3fL±ÚÊСCIIIaëÖ­ÄÆÆâç燋‹ û÷ïgöìÙùŽ¡¡¡ÁÔ©Sñòò"88˜°°0vîÜIûöíÙ¸q#sæÌ)-W¯^E[[›O>ù„îÝ»¿îS¼EˆJ„ù#î—c¾þn߆íÛáÔ)øôSX·.{›µk!2ò¿öj’nd#3€°s¨®Hÿ‘_'à Fž$'-ZÍÕg)¡c¥ƒL«äÿÇÚµk‡••ÄÊÊŠ%K–°víZ~ýõ×lm_¾|Itt4¶¶¶j3õd%00===,--ÑÔÔ,´]ñññÄÆÆbccS¨>OŸ>¥^½zÙŽ§¤¤J5T\¿222ؽ{7¬]»Vé~a``@÷îÝUbzz:“&M"==‹/boo¯<שS'¼½½éÓ§¿ýö#GޤcÖÄú¯Á™3gHHH`Ê”) 6 {{{V¬XÁ¾}ûðôôÌ÷bQ[[›Å‹g;6xð`¨_¿>ÿüó?þøc¾¶lÞ¼Y¹ïîîαcÇŠðŒ‚w! Ë97µkg¹{÷gÖ¯7ÀËKá' —+ÒÞèéÁ¢E‰T¯>§<|š}ûPõ%T‹.[ÿg ´HN&bG>%>KªŸUE»¢v©Ï;dÈ–,Yƒ”Ç|||7nwîÜA’$ŒŒŒ5j‹-BOOOÙîÖ­[,^¼˜ÿý—'Ož`hhȧŸ~ÊÂ… Ù¶mãÇàË/¿ÄÝÝ'''þþûoÂÂÂ1b'OžD.—cccÃâÅ‹9}ú4ûöí#,,L9_ß¾} aß¾} :???222ÈÈÈ@CCƒëׯ3qâD._¾Lzz:zzz 4ˆŸþYy+66–„„êÕ«—«ïrV~ÿýwîÜ¹Ãøñ㳉çLtttX´hŽŽŽ|õÕWœ={¶ï€z¬­­”;™˜˜˜``` <_,,,ÐÓÓ£J•*¯e£@ È! K‰‚æDOfÎŒâ“O†΄„(òDç$9®_?@TTT®cù?÷'"!—2Îÿ¬´GªEª"‚ ¯¯/•+WÆ:wîL5øóÏ?±¶¶fÿþýüòË/9dAñ~uîÜ™G±`ÁÚµkG@@Ó§OG__ŸØØØl¶ÆÇÇóøñcºu놳³3ß~û-‘‘‘H’ÄÙ³géÖ­–––Ìœ9“:°}ûvvíÚÅýû÷¹pá2™ ªV­ŠŸŸK–,aÔ¨QXXXäúú\¼x€#rÏùéàà@£F¸|ù2EZÏI‡¨R¥ kÖ¬aðàÁÔ®]›””–,Y‹/2dH‘Æ bþüù$&&âêêúÚv ÞmDaþåRJœ>}š &зo_úöí[¨¾ææÅcC¦û†ÒÿYOZ´(žÁ‹ÀÊ•+qww~Ð9ððð~Ð9ȬD(ü óG’$|||øå—_èÒ¥ Ó§O'##ƒcÇŽQ«V-幨ØX¶lÙ‚§§'½zõâñãÇòõ×_3eÊå¸ÿgï<⸺ü.E± VìØÑ`ïÆ^¢±¡1FŒ]P£1Q“»&¢‰1v4±Lì%ŠØ¢¢‚ˆ ÍJ‘^æû;ºì ÒÔû>Ï>²wî½sfvœ9sî);w–ÿ®V­š>>Œ1BVž•|üñÇèèèàåå%·õéÓ‡6mÚD£F8}ú4sçÎ¥jÕªL:•§OŸÊ}¿ ÉÎJýúöðððlûå†ÀÀ@nÜ¸ŽŽ¥K—ÆØØ˜§OŸâííM||¼ÖóaggG‹-033#((ˆ-[¶ðèQѹ, ÞnzöìɪU«T^˜šèBBWW7ÛGaàâI©$h!e4+…@ðÎЬY3Œå<ÐíÛ·§mÛ¶@†¥ÐhQêÒ¥ …‚€€ #ÃÃÂ… ™={6­[·ÆÚÚšÁƒ3iÒ$5å;+îÝ»GéÒ¥iÕª•J»±±1öööœ={VmLéÒ¥±³³SióóóààÁƒ?~I’HOO—ÿ}ýØ”ñÉ'ŸðÉ'ŸÅŽ;X»v-ëÖ­ãéÓ§ìܹ+++nݺEHHµk×ÎòXBBBòͯøÖ­[|ðÁtìØ‘ÐÐP*T¨@zz:ûöícèСøûû³{÷n­æªX±¢üššÊöíÛ7nÏž=cïÞ½ù"¯àýBOO==½|qWz× ô{ÂóÄçø=ö£{($¡@ ï«W¯V LË+&L`øðáüþûï½þL©R¥hÛ¶-·oß&00PeÛ¡C‡€W¾Ð™‹@FÊ5;;;bbb ÀÆÆ333nß¾­Öÿã?`×®]*ínnnHÊ0-°±±¡~ýú¸ººÊ@2£”7))Iö½ÎÌñãÇ ¤zõêr0a—.]èß¿?'OždÍš5jcîܹÃܹs122ÂÙÙYk™sBùrüzfÈ¢|X._®äÞ½{¸¹¹©¤4²£¨u–·aú+$Šºªg°'úi`ª¤bá¾!‚Â4#‚ÕA„oÎòåËiß¾=;wæë¯¿¦bÅŠ}:‡¦G”+WŽË—/óÛo¿‘˜˜ÈæÍ›iÒ¤‰šl>ÄÑÑQ£ÜsçÎÍ2»Ç”)Sظq#+V¬àÎ; <˜;wî°k×.™={¶ŠûƯ¿þʆ 8uꔜ’ð·ß~cãÆôïߟêÕ«ÍÉ“'9wîñññ¸¹¹©ìóرcLš4‰•+WªX¾½¼¼dEþÊ•+ò99pà€|¾,•…ïE­³¼ ú= %=…Ëa—i†)7ÅAo޹¹¹VUíìì8þ<ŽŽŽ|öÙg¤¥¥Q¦LfϞ͒%Kä~VVVXZZ²xñbÐÑÑ¡iӦ̜9“Ï?ÿW —...ìÞ½›cÇŽ±ÿ~Ú´iÃàÁƒŒTvÍ›7ÇÝÝ={öЦMNœ8Á¬Y³ÔŠ…”)S†¸¸¸,å¾~ý:3fÌ`áÂ…r?zôè!+·%K–äûï¿çï¿ÿfÙ²e$''JxóæÍùé§Ÿ:t¨ÊÜU«VÅËË‹o¿ý–½{÷Ê©þÊ•+‡——õë×W“©B… $&&ÊŠff>ûì³,è*Uªðï¿ÿ2gÎ<<<Ø¿¿<çòåË™:uªJÿÒ¥Kcii©âöÒ¬Y3,,,X¶l™ì'®§§GëÖ­ùúë¯éÞ½»Ê%J”ÀÒÒRͯ:88Xå,--¹zõ*W¯^ÀÙÙY(ÐA(¤Ü¬§ ò„Òw/¯–Å£Gâè¸##‹lû%&>ÁÕušÚÍórØeZýÒ çóðãÑ—ÁÁPµjžäŠ®®®¬ZµJ£ {yþüy¶UïÒÓÓyüø1úúú”)S&_ö›œœŒµµ5õë×ÏSÙhI’ ÅØØ8Ûôs’$ñèÑ#RSS©X±¢ŠÒŸ <|ø'''ÜÝÝYºt)³gÏεœÚ’––FXX¥K—¦T©R¹ŸššÊ£G$)WÇ)x¿ÑF'yS½å}@X  ‰7 "ìÞ½;Ýsî˜ÊüÏraµjÅByA„šA„êˆJ„ù‹‘‘QŽ%£uttÞÈúxíÚ5êÖ­+[=SRRpvv&,,Œ¥K—æiN…BA•*U´ê—Ù©]»6{öì¡GÌ™3‡råʽQcvèêêRõ îÅzzz¢l· @A„9#^Ws¿¿?›6mÂÍÍ-Ë Ž¬(J‡|9€PY@¥˜¸oˆ B͈ BuÞ(ˆPP$¬Y³† *ЦMºtéBÕªUY»v-#FŒ`ĈE-^¶qèÐ!¼¼¼hذaQ‹#:"ˆ0g„éOK¢££8p C† !11‘Y³fqôèQ­o®Eé>ä<6O LÜKÿçvíŠL–×AašKfêˆ Â·… Ò´iSîÞ½K||<:t mÛ¶tíÚµ¨EÓ µÂ.Áû‚"Ì¡@kI©R¥ðõõE¡ÈÈIU®\9Ö­[‡««kK–=AQA„ņÑ;øµÆbbï.ÖÖÖLŸ>½¨Å‚A(ÐZ¢Tœ•„††ÊùD‹3J÷ ÙÿÙÜ4(:@ Þr„t8qâû÷ïÏUtvQU"TÊTÚ¶E1¨ì@F¡2“à"«„:©©©µ@ð^PÔÕ“ßÞ9:%%…?þøƒùóçÓ¿zöì™eßË—/ˉè{£ î IDATöìÉáÇåm÷ïßÇÆÆæÍ›'·Ÿ={–Ï?ÿœcÇŽå*µÓýû÷¹}û¶ÊçÙ³gy;È\àâIÅX¨ñìe¶Âbä¾!‚5#‚ÕA„@Pxˆ œyç\8ž>}Êðáñ±±A¡PpóæMý¼½½éܹ3=zô`åÊ•9r„¾}ûâîîNÏž=±¶¶V³^ºt‰O>ù\§ÓJññá@ûöò÷?SSÙ|æL¾åWÕDLR 7Ý`p1õAašA„êˆ B@ (ø€ñãÇãååUÄÒ½Hï0ÿûßÿ$M‡èíí-Òž={TÚ}||$@:pà@¾Êáàà uIé«òå¥ë>>ù:¿&RÓS%“ïM¤n£3ö+$yxø~sÃôéÓ¥çÏŸµÅ‡¢¡Øñüùsiúôé*mëׯ—êÖ­[D ´%..Nòóó“¢¢¢´ê$°Tśܞ3MøûûK!!!ù(UÑðìÙ3ÉÏÏOJLḺoHHˆäïï_R½Ý888äøœéÔ©“xåÀ;ç­ J§OŸª´+¿çä’L€3@@:4jÜ8ßçÏÌõÈë¼H~ñÊÿY¡ÈHaWŒAašA„êˆ Â¬quuÅÛÛ[þndd„••íÚµ£yóæ¹žÏÑÑQë¾K—.¥T©RÙö¹té:ubóæÍŒ;6Ç9‡ FTTT‘¤sŒˆˆÀÝÝ3g΄‰‰ õêÕcêÔ©Ô¨QCëyÂÃùvíW¯^%,,Œ5j0gέÇçöœiÂÞÞ[[[•ìRo#[¶laæÌ™ü÷ߨÚÚfÛ÷ÓO?ÅÛÛ›ˆˆˆ•)**Jþ}166fÅŠºÏÂ"""‚ˆˆLLLŠZ”bÏ{©@WªT €[·n©´ûúúªlÏO"€ï Æäñf˜[”ùŸeºAƒŒ"*àâðáÃÐ%J” ^½zj)î|}}177§fÍšù¾Ïš@Ibd\\¾Ï­ ÏOôÒ¡Í×þÏÅ(}@ È._¾LPPOž<áøñãÔ«Wßÿ=×+J ”òãêê À7ß|£¶­\¹rp$EG¿~ý8wî¡¡¡9r„pèÐ!RSSqpp %%%Ç9J•*żyóøã? Ì“mÛ¶%""‚aÆåi¼ `™:u*[·nåæÍ›žM«°±³³cƌԯ_¿¨E)ö¼“褤$$I’«Ü)ßõõõÑÕÕà³Ï>cþüù\¹r…-ZÄÁƒ3f †††ù.“BÁVI"uÅ ô&O†—ržÁž4 ã”âW@EI@@VVV*™PA„™-Xï;©©©å:ÿ:@Dr2Wbc @*íèhf†Iÿ]]]ºté´iÓ˜4iÇÇÁÁwwwÊ•+G›6mÔÆwïÞ¥mÛ¶¶$ù@W—_SSéóà}þü вð0æ!!1! -¦T”¬[·ŽE‹i|¾Ï¸¸¸?èL(+æÅ:*5µHh»R¥ UVÒªU+ â¬P(X·n—.]",,LíÁ¸zõj–,Y"»±åÄDZ²²B’$*UªD\\ÏŸ?§L™2lß¾^½z© §U«VìÚµ GGG^¼xAõêÕ FOOf̘‘/ò¼ >|<==éÖ­[Q‹S¬y·^^²ÿ~¼¼¼Ô>ƒ ’ûèëë³cÇ®]»Æ_|ÁÙ³g9zô¨Öoñ¹¥kÏž¸ZZÒ`ٲهÏLþÏU«‚†·è¢fÕªUByÖ€PžÕA„¹gÏž=²õoÒ¤IDGGËíJRSSÙ´imÛ¶¥aÆZÍmllÌ/¿üBtt4!!!<~ü˜Ó§OcnnÎøñãyñâ…Ú˜¯¿þšqãÆñüùsÂÂÂððð 00qãÆå¸¿¯¾ú WWW¾üòKîÝ»GDD‡¢dÉ’Œ9’„„ #®eÅŠôïߟ¨¨(xüø1÷ïßç£>ÒêØ2óìÙ3:D•*U¨[·nžæÈOfΜɀ $,,Œ¯¿þš{÷î©XE5±`Á™6m»wïÆÈȈ´´4H@@›7o&22’°°0~úé'¼¼¼pvvV›gÓ¦Mòøñcž?ή]»ämÀßߟ“'OÃ(Q¢³fÍ’W„Ž;†££#;wÆËË‹ÈÈH|||èÞ½;3fÌàÊ•+ùr®>ýôS9¿±òsÿþ}*UªDÕªUiÚ´)þþþ888P£F ‚ƒƒ $"";;;fΜÉéÓ§óEž·>}úàêê*”g-x'è¦M›bgg§öɼ´¨P(hܸ1#FŒ eË–êJ`^¶,(ß„¯\3g l_¯…ÿ³@ð¾póæM¼½½9~ü8ŽŽŽüôÓOèéé1~üxúöíKåʕٸq£Ê8wwwÂÃõ®ª P¶lYÆ'GêëêêÒ¡C¦M›&/ƒg¦L™2|ñÅ(÷¥?üîÝ»óï¿ÿráÂ…,÷õðáCÖ®]K‹-X²d ÖÖÖ²k…““¡¡¡òKÁ£G€ %àucˆ•••F×m?~<?æçŸ.Ëõ5â»ï¾ÃÊÊ sss,X@‰%8“Å3%%%þ÷¿ÿ±råJV¬X!ÇöíÛñõõeÒ¤IŒ;SSSLLLøüóÏéÞ½;Û·o'22Re¾äädV®\‰……€J¶†äädÖ¯_O§NÐÓÓ£ÿþôèуàà`îß¿/÷›3gúúúlذ;;;7nÌš5kò¼RqqqôéÓ‡¨¨(<<<°´´2VýRRRøê«¯¨\¹2¸¸¸ï^°¬ x']8Š#¾¾¾¸T«†½‘ö‰‰ðãðÚ²c~ââI§P.®øú? ‚ü¥k×®*ßkÕªÅ?ü §²ÓÓÓãÓO?eñâÅܸqC¶6oذ33³\[hpssÃÇLJ°°0’““‰}é*󺲤¤cÇŽ²ò¬¤[·nüõ×_øúúÊŠTf|||HIIÁÞÞžS§N‘žžŽ$IH’$§ÐS„·k×KKKœœœ¸yó&}ôÍš5SÛ¯¶8;;sàÀfÏž­UŽÂ`È!*ßõõõ±³³Óè~KïÞ½9wî{öìQs£¸xñ"õêÕãäÉ“òyMOOÇÚÚI’¸{÷®¬hØÚÚÊJfflmmÕ|˜;uê„››!!!Ô®]›„„®_¿Ž·o߯ÏÏOå7µ²²*†éééŒ1<<Ëj¾J—œüdÖ¬Yüõ×_¬_¿^ö{VDýúõÕ\ uuuiÛ¶-$%%H‚↠W‘ªÍ¥HHHÈPŠæÏ‡Í›!=–/‡_Í×ý\|x‘4)í•ÿséÒ ¥_ca#‚5#‚Õy“ ÂRºº4,€âHÚbXHËþmÛ¶Í1ˆ¬råÊôíÛ—mÛ¶ñÃ?ðË/¿ IR®Ü7-Z$’½n™Ý³g»wïÖ8F“UQ™‹?»|³ÊTyþù§šU­[·fß¾}„††âááÁŽ;˜>}:~~~¬_¿>Çñ‘²ïûï¿güøñ¬[·N«1Å‘víÚѱcGæÍ›Ç¨Q£Øºu«ŠÁÂÂÂ}}}bbbä—ŸœxSƒ‡ÒõcÊ”)¬]»öæÒ–uëÖ±zõjœœœ4 ²´´äÞ½{$''c`` ²íæÍ›˜šš¾Ê3dü>òó®!èBB™Šˆ5`Ð øóOؾ–,z¹A-€°m[(~{šAašʳ:oDXÉÐ!ïX¾â7aÒ¤Iìß¿Ÿ;vðÛo¿ñÁРAƒ\ÍáííM¥J•ÔÜöíÛ—å˜'N¨µ;v ÛÔeÊeä£Gj¥@+©\¹2'NdâĉtèÐ]»v±zõj5)3...,\¸Ñ£G³qãÆ<»¾øâ Œ™1c‰‰‰¸¹¹Éç Q£F¸¹¹qêÔ)™S KKKÊ•+lj'HKK“SˇbÆŒ 80Ë•nß¾§§':u’ÛïÞ½Ëýû÷å`Ã÷ YgdIñÔ¬ÞAîß¿››7nÜedsRäó¸g°'–/ ¶²Jy1ußEC×®]©U«NNNDFF2qâÄ\ÏQ¡BÂÂÂ2îg/¹uë‡Êr̽{÷d…2î‰ÿý75jÔ {÷îYŽ«_¿>dÓ¦Mìß¿_mûÿýGPPa-Ì\¼$99™°°0ÒÓÓs´ž._¾œyóæ1lØ06oÞœcÐà¸qã²u(.LŸ> 6pðàA(×F˜8q"åÊ•cÚ´i¨ŒIMMÅÃã@äY°`~~~8;;«§ ãòåËù²ooo†N³fÍØ¾}{–¿§2mÞúõëål!’$ÉyÁ§Nš/ò¼ ܸq777q U„º000ÀÌÌ,c™¬aà ÅöÜ9X¿>í#–˜Ó¥t.<¼@—×Ûµ{ãyÁ»ƒB¡ÀÑÑgggÌÍÍ:th®ç˜1c´jÕJV~Ož<É”)SäÌ™ùä“O6lööö˜˜˜päÈ’’’ؾ}{ŽŠíÏ?ÿLDDƒ ¢yóæÔ«WØØXîÞ½‹ŸŸÿüóVVVœ={–Ï?ÿœÖ­[S»vmñôô$,,Œ~ø!G…xÁ‚@F*TM^¶mÛ¦r¾þùç•’ÎJ2×"¸té’ì"ñÑG±uëÖlå(>ûì3Œ7n}ûöåàÁƒ”-[–;v0jÔ(6lˆ½½=UªT!<<ooo¢££5ß›2eÊ|||Xµj E‹èééÄ•+Wprr¢eË–o¼ŸÕ«WóâÅ éÝ»·Úö¥K—ÒªU+:uêÄÌ™3Y¹r%ׯ_§yóæÜ¸qƒëׯ3tèPTÆõë×£G/h’$É¿oݺuñññycÙ‹ ###ÌÌÌr\©ºÐ°°°P \˜=;C~þ<#¨0Çß||“è¤èWî7¡‚BjFª#‚³fðàÁØØØ¨¤Ë å2õ˜1crô}­_¿>sçÎUQh:wîÌùóçquu%44”úõë³wï^lmm‘$I%e\µjÕ˜;w.C‡ÅÑÑ‘;wróæMFÅ„ ÔrO3FMi«P¡çÎcÇŽxzzâï =zô`ÕªUòñ 0€ .JÙ²eY¸p!]ºtѪÂ&‹èëd’›:uªJ~c%Ÿ|ò ÏŸ?×8G“&Mr”CyÎ^Ï‚P§NæÎ«±ªßàÁƒÕÜ ¦N*€Q2zôhÊ”)ÃÙ³g9qâ}ûö¥[·nܾ}› 6àëë˃(_¾‹UÍÙ³gk,ÿ¬¼~^/–£££Ã¦M›=z4îîîr&Ž 0eÊúôé#÷mÙ²%sçÎUÉ’Æ £ÝkF£Þ½{g;îõø›+VеkW<<<¸{÷.-[¶dþüùË©80ËR×ÙUâ|¨U«µjÕbË–-E-J±G!I’TÔB¼ëŒ;–àà`Nž<ùªQ’22pܹVVðÆå½]¯¸2éÐ$.o„adø?{z¾ÑœÉŒ3D¡ÆŽ+ü 3Å¢E‹Tü ]]]YµjU¤¼z×™ÿüs¡< ˆ œ t!qëÖ-V­Z¥ZqkÌP.÷äCyoÏ`Oì‚^úˆb®@ ‚ÂÃ××—²eË2dÈj׮͢E‹ŠZ$@P̸pá«V­’SL ²F(Ð…D… èØ±#VVV¯Œ`ò䌿߰¼wø‹pîGÝåÿ¬P@6 ïÁûEÍš5ù矸uë·nÝÂÜܼ¨EÅ +++:vì˜cNyP  ’%Kbkk«~QNž %Jdüýãyžÿ|ÈyàµüÏõêA6Å ŠpÞw„O¯:©©©ji¶¹ÃÔÔ”ž={R/Ÿ«Ÿ ‚w‡ *`kk«1 @¡@‘‘‘š7”- /úåòÞyÀ3ØÝt°{ø²á-pßX·n/^¼(j1ŠY¥{ŸQV"AÁ“¥Î" t!‘­Cþ¬YÕ%)£¼wð ñÄ6L’_6¼ùŸE¡fD¡:"ˆP  D˜3B.Ô¬ Ê|›Û·CDDöý3‘šÀáÿ½r߀·Â-@ð6"èBÂ××Î;§¹ÃìÙÿ桼÷åÐˤ¤§¼R +WÎÈ--@ %çÎÃÅÅ__ߢ¥Ø#èB¢B… 0 ë svv¯²f¬_qqZÏí’Q,EV ßë³"ÔŒ"TG AÁcccÀD- t!‘€ YwrvÎøWYÞ[K<ƒ=©õ *(ãñÞZjFª#‚ à±°°ÀÆÆ†„„„¢¥Ø#èBB+‡ü~ý v팿W­‚´´‡HHx=ôz+ýŸE¡fD¡:"ˆP  D˜3B.Nèè¼*ï}ÿ>ìÝ›ãÛOnó,áÙ+ºT)hܸàd‚·€èèhüüü×ÚU,&&†   $I*`é2HHHàîÝ»DDDäiŸ©©©<|ø???ž={V¾]>|˜E‹‘˜˜˜ïsÿùçŸoÍêàíÛ·Y´h‘p,`„]Üpp€rå2þÖ¢°Šgp&ÿç¶m3q@ð^ðÑGadd$ÌḬ̀µµeÚ´iDä2£Onxôè...\¹r¥Àö‘~ýõWjÔ¨™™õë×§R¥J”*UŠ®]»j ŒJJJbΜ9Ô¬Y333¬­­177§{÷îܸqCã>ÌÌÌhݺuže  OŸ>”(Q‚ºuëR±bE,--Y³fVãSRRèÔ©¥K—¦jÕªÔ¯_Ÿ²eËR£F ¶mÛ¦µgΜÁÉɉöíÛSºtiŒŒŒ8vìX^+_ñóóÃÅÅ…û÷ïk=æðáÃ,^¼X(зo³xñb¡@0BÓ*$’’’´ëhlœ«òÞž!ž”‹ƒºO^6¼%î ‚³BÜôÔA„Y“œœLRRS¦Lá‹/¾`Ô¨Q 6ä8Gzz:1118::²aölÙ„ HLLd̘1ìØ±C+YÖ¯_ÏÊ•+‰ˆˆÀÒÒ’¤¤$Ò´p, |||˜7owîÜÑzL×®]™;w.FFF(ÙûA^¯ï÷ IPà888H:uÒ~ÀãÇ’dl,I I}údÛµöšÚÒ€ádôI:}ú ¥-<¦OŸ.=þ¼¨Å(v888µÅŽçÏŸKÓ§OWi[¿~½T·nÝ"’¨øÐ¿ ÂÃÃå¶´´4iðàÁ }ùå—²ßÿþûO¤•+WÈüy¡|ùò’¾¾¾¤¶-55UŠUiëÝ»·H¿üò‹ZÿÀÀ@©V­Z’¡¡¡¨²ÍÐÐPjÒ¤IždôððiðàÁ*í¾¾¾ µhÑ"OóJ’$8qB${{{­úûûûKÑÑÑ’$IÒÂ… %@úçŸò¼ÿüd×®]ÅJžaÆI¥K—.j1´bÿþý íß¿_ãv‡Ÿ3:uÏ¢Ð+*Åý}#88˜±cÇ2`À(‹¦d……E†+‡«+:”QÞ»^=µnããÿÌŸ‰J÷ }}hÕ*ÿ…/ DP˜fD¡:oD‡ÇÿÈg‰´§R¥Ièë—-Ô}êèè0qâDöîÝ‹ÜÍ·ß~Ë… xüø1 6düøñôîÝ[eü‹/pwwçøñãœ={===¬¬¬ppp`ذaœ|8cÇŽ•çxúô)=âƒ> zõêjçDWWùû‘#Gøûï¿éÞ½;Ÿ~ú©Zkkk–.]Ê Aƒ˜7onnn¹ÿ!4@ÇŽUÚ4h@¹råäíy¡}ûö˜ššjA¡V­ZyÞ—’™3g°dɾýö[N:EBBíÚµãÿû¥J•R韖–ƪU«8räÔ®]›>}úðù矣P(ضm›ìÎòÍ7ßàêê @Ïž=qttÌR–M›6áîîÎŽ;(Y²$±*qòäIöïßÏÊ•+9tè¡¡¡ØÚÚòÃ?¨]+’$±bÅ <ȳgÏhݺ5óæÍËrŸ111|÷Ý`6 IDATw\¼x‘‡R§N>þøcF-÷ùí·ßøë¯¿prr¢ÝkU‚“““qtt$-- 6È–óððp¾ûî;®]»ÆÓ§O©_¿>“'O¦k×®jûÿ÷ßY±b·nÝÂÆÆ†‘#Gbhh˜¥¼9qàÀ8@pp°$ÌáÂQHØÛÛóûï¿ç¬<+Ñ¢¼÷ùós+èæÍ3\@ÁkHHRj‘} pÒ2“œœ d(Ó!!!ØÚÚ²víZjÔ¨Á€àÃ?”•a%C† aôèÑ:[*ñ%R²îK÷ gç7V xKÐÖ…#.ÎOºa‘}’“èyÐ䑘˜(}øá‡ -^¼X’$I5j”Ú’xRR’Ô¾}{IOOOº}û¶$I’)ÒgŸ}¦¶¯„„ùïì\8Μ9#Ò¨Q£TÚ×®]+’¡¡¡J{‡d·†´´4•mC‡• …äåå%·¥¦¦JŽŽŽjí_}õ•DƋԬY3iñâÅÒ¥K—4ž·^½zI€äïï¯q»’.]ºH€ôðáC¹íM\8$I’þúë/ÉÊÊJªY³¦4mÚ4éÃ?”Œ¥ñãÇKOŸ>ÕzžÈÈHiúôéÒ„ ¤ºuëJFFFÒ‚ ¤¸¸¸\Ë”WŽ&MšH€4aÂùúHOO—† "Òÿý'÷]·nH .T™cúôé mß¾]nË‹ ‡rž×]$@jÛ¶­ôøñ«ÿ‹K–,‘ióæÍr›§§§H#GŽT™÷›o¾‘5޾}ûJ:::ÒÕ«Wå¶ÔÔTiܸq’B¡Pi¿xñ¢¤¯¯/õêÕKJOO—ÜÝÝÕþŸ¥¤¤HõêÕ“ÌÍÍ¥°°0¹=>>^êСƒdjj*EFFÊ}kÔ¨!YZZJ=’ûúùùIºººoì‘g½å=BX  ‰<9ä+ «dQÞÛ3Ä“ÖA_óñ–½1Š B͈ BuDaÎ|ýõ×̘1ƒQ£FQ¾|y:„••3gÎ$))‰;wR¡Bzöì)100àã?&55•-[¶P¢D J–,ÉóçÏÕþjœµk×.>þøc•öáÇËËôš3fŒl1 å?þ [·nØÙÙÉíººº8::"I’Š5û›o¾áüùóŒ1___.\H«V­èС*©â‚‚‚¨R¥J¶Ç¢Üž9ñMèС}úôáÞ½{¬[·ŽC‡Q¾|yF¥bÅÏ‰ØØX8ÀÞ½{¹sç4hЀ%J䛬Ú2uêTùúP(ôêÕ ÈȦ¡DùºëÍëß5læŸ~ú©J!³~ýú©É§tŸ5j”ÊØ×Ý1”Ü¿wwwúôéC³fÍäv]]]&Nœˆ$I”ݺuàîÝ»˜˜˜0zôh\]]9yò$ýúõcðàÁôêÕKE¹ÍŽ{÷îa``@‡TÚ-,,hÖ¬™Æôp†††jòݺu ÈðÝάtèë뫽XµiÓ†6mÚ°zõj<==Ù¾}; oß¾|ûí·,X°KKKüüüxòäI¶J´rÉÝÜÜ<çׂÈÈHš4iB‰%زe  àÞ½{lذN:±páB-Z¤Õ\5kÖ”óV_½z•_~ù…?þ˜‹/²råÊ|‘W¬¬¬hÔ¨‘J›­­-‘îP‰¿¿?µjÕÂÊÊJ¥o“&M(W®œ| ƒ RùÞ¨Q# Tä ÀÀÀ€öíÛ«ôµ²²¢V­Z<~üXnSfyò䉯kSWWWÍ2sæLN:ÅŠ+(Q¢»wïÆø5·K圾¾¾js*«ö*çT^÷;wVÛw·nݸxñ¢úIÈ‘‘‘Â:„ºÈó…øZyïÔ™0}uºÖáƒY|=û—®~RݺÁ‡o¢¡f„ò¬Ž¨D˜3ׯ_'""‚   NŸ>Í7ß|CéÒ¥ 4߇*Uª„®®.QQQrÛúõë9sæ :tà?þ OŸ>Ô®]›?ÿüS+Y166Öh±ÎJ­P¡‚šåTix(Q¢ …Bí3räHËôëXXXпþøãN:À²eËäíuêÔ 000Ûc D¡Ph LÌ üñ‘‘‘Ì™3‡1cÆPªT)š6mÊO?ýD¥J•puuÍu*9…BA‹-X³f åË—gÇŽ…šŽNy½ŽòeKiõONN&..Nã5¨P(¨ZµªÊ5X2* •U‰èèh,,,4Zð3Ë­|±Ê*`oÔ¨Q´ÊÔ¯P(ä¹Ë–-«beÎiN¨[·®,+@ÕªUs”5/å9g„º¸3`ÔªDÎÿ‚í#%ÛeHè¦C›³ÝöE?¢ORÒEß ï"††U±´•sÇBO/ûÀ§Â@©ü)É×9{ö,iiiÔ¬YS¥½}ûö´oßž„„<<ÁûŠ®nIŒkÙG¡0ÈYȦ|ùòT«VK—.É–+%ÊÊsYUÖ366fèСŒ9’àà`nÞ¼ d(Éúúú„„„¨Q.+ÿóÏ?*í§NÊU•¸† bnnÎÖ­[å¬"Y‘––FLLŒÆmwïÞ%((ˆ²eËR¶lFJÁÑ£GÓ¨Q#¶nÝÊåË—ÕÆ$$$0{öl•Œ oŠÒ]ÄÝÝ]¥=55•Ç«ôQÊ¥U¼HRR/^ÄÈÈHå…(99™¨¨¨"÷kmݺµÆ—¨“'O’žž®r *•M×WA¡\ÍP¾ä(9þ¼Z¶Ž×¯Mm~›ëׯ3cÆ ºté®]»X´hûöícíkñMmÚ´AWW—ß~û-ÇùZ´h®®.'OžTi—$Iö“,B.$Þ䯕8|8Ïô3~*çó¯ÚåôuÀ¿MÒ9ás¢@J˜"ˆP3"ˆPDøæ|ûí·¤¥¥1jÔ(Eý²õVP„@Þr]‰0/^”~°1’« v‹Ä"¤Ý 2¾‡™f|7d&]¼x1%/XD%B͈ÔAêˆJ„Y£)&ÒÓÓ%I___$===9ÝÛ;wä~ÿþû¯d`` ’B¡ôõõ%…B!5kÖL:zô¨Êœ‡–ºuë&I€Ô±cGyÛãÇ¥?üPÞ_õêÕ¥]»vIC‡•*Uª¤2O‡¤êÕ«g)û† $333 å¤òåËKgΜ‘$)#Õ—2%/SÙ)?†††Ò¤I“¤äädµ¹¤V­Z©111‘Ö¯_/¥§§«144TëÿúçõÔzšøå—_¤²e˪ëÙ³§Jú2I’¤‰'J€têÔ)¹mëÖ­òy}ýS²dIÉÉÉIJIIQ™cýúõSnÞ¼9ÛãðóóËö8$)#¦”~Y¥9Ûó‚··7¡¡¡,_¾œÐÐPŽ=ª± „2«IVXYYå˜òïÅ‹\¹r…€€ÌÍÍiР666jý"##yþü9ÕªUS n‹‹‹ãòåË==ÿþûëׯӠAš5kFbb">¤J•*Ï«6:É›è-ï B.ÆŽ˹sç°··×®”7p=ò:Û®oc§ïN®…Qi´ÖR/WUÂM âËÕµ'% Ú¼ÐeÃw9ÿ¦@ð.#èâM\\œ\NYÉŸþÉСCùæ›oøê«¯ŠH²œ äƒ> 11‘3gÎиqã¢I È7²SŽ•î;JE(ÐY#²pzеGW9•„Ɔ²Ów'Û|¶áûÈ÷Õ†Ú`¢nñ Û^wM‹‡äxhkVB(Ï X0uêT‚‚‚hݺ5&&&ܸqƒ={ö`mmÍŒ3ŠZ¼l©Q£GeïÞ½\¸pA(Ђ÷¥‘OSnk*B.$îéÜcìá±X­±bõ¢Õ|ØãCb“cÙç·m>Û8tŠtéÕ² öÕìÝd4úÕ$ÖL˜„sšæeÃ5º:|ñ“k¡K~€•••ÚòñûÎíÛ·5.7¾Ï¤¦¦D­ZµŠZ– 8M›6ñ×_µµ5_|ñóæÍËv¹¾¸Ð¨Q#µâ ÁûBæTƒu„æRX$BZ4îU¿ÇøïƳ(}ÿÆÿËÁ;‰O‰WéjcaèƣÙh$VfVÍ`àò5L»y“ÌiÉ€§M=œGŒ(Œ#É7D%B͈J„êˆJ„o}ûö¥oß¾E-†@ È¢aκ°PAÒ…Èv‘Lš= †¾Ú\¾dy†7ÎèÆ£iQIsû‰ßϦѣ™”)×é¦R¥˜øý÷$xÁ!”!ÍåYQ‰P  ¡<çŒP ‹ 4%1°Å@F5E÷šÝÑÓÉþçèÙ¯Õ¨ÁxooÙ œªQƒIýú´Ô@ D!•"à’»;ìfçàô®Ý;GåYÉ'‹óëk%e55å“Å‹ JL@ A&„]Xd*¸W:©4µ««ç“̉žýúqÊÚš$ ‰ ësÏ·Ôú,*jF¤eSGT"‚ÂCT"Ì¡@™*kš>7¥nݺyšjü7ßðk©RüjdÄøo¾ÉኆuëÖå{©Øw—¢¡Ø¡ "AÁYÔ"{„taQúÕŸ&~&Ì3C­‚–¶ôì×!ÖÖ IL~K­Ï ‚³Bª“Uaxx¸ÈW*¹@Y$%;DaÎ:¬ZµŠ³gÏ¢§§G÷îÝ9rdŽåZUH‡²×ÊÒµBW¦N˜úF²|ú[ž‚ü nݺ 8°¨Å‚· {{ûhAÎ:T­Z• ””Ä¢E‹ˆÕº¢–Ù#3loØ2}âtôɹ”wN¼­~ÏA~Ñ©S':uêTÔbà=Dø@ç‚ÁƒÓ´iSììì>|8çÏŸ×zlÏö=9µ÷T¾(Ïï "ˆP3"ˆPD¨™'OžðäÉ“¢£Ø!î-š÷uĽE3"ˆ0g„KvîÜɬY³Ø¾};óçÏ×zœpÈWGjFª#‚5sîÜ9Î;WÔb;ĽE3âÞ¢Ž¸·hFè,9óN+Щ©©$&&æØçöíÛjo[ñññœ>}šÓ§O«¼µ—)S âââ´~›νð@ E„Ð]²çS _¼xÁ¸qãhÔ¨FFFgÙwÕªU˜™™Q¯^=J—.ÍìÙ³‘$ €ØØXÜÜÜpssãâÅ‹ò˜ž={2þ|V­ZÅ¢E‹ úp@ ÅŒwNNLLäáÇôêÕ‹þýûgÙïÏ?ÿdæÌ™,[¶Œ/^°k×.Ö¬YÃòå˰´´ÄÕÕWWWHOOW±fûùùѤI“?ž¬¸}ûv¾û³åvÎÔÔT<<<òuÎ7åðáÃ9®:ôœܸq#_ç|S8Päs?~<Çeõ‚3+9|øp‘Ï™Ó1„œÙ!î-š÷͈{‹:âÞò~ðÎ)Ð;vŒ¥K—Ò²eË,û­]»;;;)Y²$dðàÁüôÓO¤§§«õ‹‹ÃÆÆ†.]ºÐ´iS~ÿýw¾üòK­åJIIÉÓñdÅ»ð‹ŠŠÊ÷@Ÿwá!+ù´ámÈ¥¦¦•«ýåÄ»ð‹‰‰!&&&WûË qoÑŒ¸·hFÜ[Ôyî-ù­³¼‹($¥ÏÂ;ˆ‹‹ óæÍ#ó!¦¤¤`hhÈ'Ÿ|¯¿þªÖßßߟZµj©Í—ššJPPåË—§T©RZËѹsg¼¼¼hÑ¢úúú*Û,--144”¿kÕvíÚ5J—.MÍš5s=6«¶””ôõõåê9MKKãÒ¥KT®\9Ë~Ê@„jÕª©ÍçîîNݺu©^½zžeÎÜvâÄ Zµj…©©i¾—ÐÐPš7oŽVcCCCINNFWW7Ë~>>>ØÚÚb`` 6ß¾}ûhÑ¢ÅÉœ¹íÈ‘#tìØ1ßæ &((ˆöíÛk=öêÕ«Ô¯_ŸÇgÙïòåËr~Ò×Ǿxñ‚S§NѤI“|;/ÉÉÉœ={{{û|;/DFFÒ¦M­Ç*‹dÕÏÜÜ???Zµj¥6öÚµk@†á ¿ÎKpp0Ož<¡Aƒùv^r{¿ÒÓÓãÒ¥K´iÓ&O÷+www:uêijgÏòí¼\ºt *W®\d÷«'OžœœŒµµužîWûöícÐÿÛ»ó (®<à_@@‚‡£pD.‡[Ep—¸FQA¢®.FïlpãÍf­«ë*–W¥’MÐÕòŠÑÈšxÄõ(EÅ‹ˆˆƒ (F¹DAäf~ûGjºXYa™ß§Š’~ý¦û×m¿×¿iº_O™òÊm¿uÙåË—1hÐ Qû«¬¬,Èår˜™™uº¿jjjÂùóç1qâÄ.Û/P(0`€¨ýU{ý©ZëþJ}¿syy9,--qóæMáܹs`Úée]PP™L†/¾øK–,ÊOœ8ÈÈH$''k4´WµcÇ$$$´)ç7ý0ÆcLlÚœ1c,X B4¯½|‘JEE@&“i”;99iÌï* ,àƒ1Æc¬—èu÷@¿ [[[¿üù¢µhÌgŒ1ÆcìEz™@0†††¸sçŽFyff&ÀÞÞ^Œ°cŒ1ÆØk@/è>}ú`ôèÑB¬–‘‘7778;;‹cŒ1Æëéze˜˜ˆ#GŽW˜9‚#GŽ //O¨‹7n`ýúõ())Á7ß|ƒÃ‡#66Vgq`äÈ‘pttDTTT—¥óº;v,lllàáá!v(=FBB¼¼¼ “ɤ¤$±CêÖ¯_899!** ©©©b‡Ô£|úé§000@ii©Ø¡ˆ®¾¾FFFprr~^|­¾JOOGHHìííáááÁç"&LŽuùóQ¯£äädŒ7>>>˜2eŠNÇbïq¨  ;;»6?ß~û­F½]»v‘T*%dmmMkÖ¬Ñiœï½÷­]»–êêêèƒ> +Vètý=Ujj*) rww;”ãüùóTZZJDDIIIäììLÍÍÍ"G%¾ââba?ìÛ·FŒ!rD=ÇO?ýDS§N%KKKR*•b‡#ººº:²³³;ŒçñãÇ$•JéÀÔÜÜL•••ÔØØ(vX=Êž={hܸqb‡Ñ#øúúRbb"ÅÇÇÓïÿ{‘#O¯…ãúõë/Uoîܹ˜;w.ž>}ûöżyó°|ùrÇÐê÷·Z-Þzë-ßËÊÊ T*áàà ^P=@ëçŒçÏŸ‹MÏÑÐЀ%K–ààÁƒËåb‡Óc466â»ï¾ÃðáÃ1tèP±Ãévî܉    ??ŸoaÔbÇŽøÓŸþ$v=Bmm­ÐïJ¥RÔÕÕ‰‘xzeÝYb$ÏÅÅÅhjjBÿþýžžž¸~ý:T* {å5¬‹lݺ¡¡¡zŸ<«}óÍ7عs'ŠŠŠpòäI±ÃéV¯^9sæðÑ­bôèѸwï¶oß.¼˜¢Oý> Þ½{?ÿü3¦M›†~ýúÁØØÇ^Ä¢ïîß¿ììlDEE‰J°ÿ~Lž<ŽŽŽ¨®®ÆÁƒÅI4úÝs¼‚¼¼<äää ÿþÂÂ^ôüùs\ºt ùùù ‚¿¿¿0ÏÈÈ---´:q600èöØ»‹R©DZZŠ‹‹€€€­õ®_¿Ž³gÏ¢¡¡aaa5j”Ž#Õ­ÚÚZddd ªª AAAí~a»{÷.®\¹üæ7¿D"iSçĉزeK¯¸º¢¢·nÝBCCƧµŽJ¥Âµk× P(0tèP„„„´9±Oš4 Æ Þ={ðÇ?þçÏŸ×EøÝ¢ººéééÈÉÉÁ€Ú=içççãØ±c(**‚¿¿?¦L™"¼åTÈÅþäÃIDAT¡PàÚµkX³f.CïV]Ñ·˜˜˜àÀÂôرcqìØ1Lž<¹Ûãï.]Ñ·ôïßfffÂ_n'NœˆÓ§Ocâĉ:Ù†®ÖØØˆÌÌL¡o‰‰‰ÑZ¯¦¦‡FVV\]]1iÒ$­CØîÚµ 3gÎì_(^5o€•+WbòäɈˆˆÀ¶mÛÏ?ÿ\á÷N“&M"cccZµjU›zÙÙÙ½â興222Ž—óçÏk­·qãF244$²³³#©TJuNž]•·(•Jêß¿?µ´´QII ™›› Óú†èNJOO§¯¾úŠ’““),,¬Ý122’†JUUUDDtêÔ)200 ƒ ufÍšEŸ}öUVVÒŒ3Ú=1¼’““éÂ… T]]M¦¦¦Z·¥ªªŠÌÍÍiéÒ¥BÙ¿þõ/200 œœ¡,==öïßO2™ŒRRR(//O›Ð-¾úê+úþûï)!!¡Ý“\ZZ mÛ¶Qcc#½õÖ[äåå%Ô9}ú49::Òµk×H©T’R©|mô©ªª¢5kÖЉ'èÓO?m÷$÷ùçŸÓo¼A …‚ˆˆ ÉÎÎŽ-Z$Ô¹té544PMM mÙ²…BCCu² ÝáÞ½{tìØ1R*•Þnß2räH ¥¦¦&""ÊÈÈ >}úÐŽ;´ÖÝ"쪾%++‹.\¸@EEE”@ÖÖÖTRR¢«Íèr]Õ·<~ü˜)//=zD^^^”••¥«ÍèR555”@999´víÚvû–åË—“••Quu5¹¸¸Ð{ï½§QïèÑ£Øíqw·®Ì[ÜÜÜèÌ™3ÔÒÒB›7o¦ñãÇëbz$N _A{'¹ÂÂB244l3ª‡«««ÆUæ’’Šˆˆî³gϺ;dhï$·mÛ6@©©©BYyy9 eË– e‹-¢èèháçÅÑS^G)))ížäþð‡?©©©Æ¨ûöí#tõêU"ú¥ÃÿÕ¯~¥ñóºžäZ[·n]»'9777zûí·5Ê>øà²´´®¶ÆÄÄÐ!CÈËË‹>þøã×þJ‘Z{}˽{÷@›+§^^^¢uYóæÍ£§OŸvG˜:÷*}Kff&M›6BBBèÏþ3¥¤¤è*ìnõª}‹ºÌßߟ~ûÛßÒž={tv·ë¨o±··§ &h”-Z´ˆLMM©ººZ(Û²e ýç?ÿéÖ8uíå-qqqå/æ-.\ éÓ§“¯¯/ÍŸ?¿×ô¹ÿ¾ºܺu *•ªÍÓïÞÞÞ¸té’0-•Jqüøq]‡'šììlhìkkkH¥Rdgg eñññb„'š›7oÂÝÝFFFB™···0oĈˆ‹‹C\\œX!ê\mm-rrrÚÜííííÛ·ãáÇË娲e‹HŠC=:§§§F¹··7N:¥õ3;wîìö¸Ä¦î[Z¬ñbßâåå…~øA¬Eñ2} ̘13fÌ%F]«®®FII fΜ©Qîí톆äææÂÇÇÚ½º7Rç-Úú–ÖyKXXÂÂÂt^ÄÃ=tƒââbÚOråååhll#,ÑcРA077×(÷ööö™>*..ns¬xxxÀØØXo÷KGm¨õ|}ÓÑ~©¬¬ÔÛ!¥Š‹‹!“ÉЯ_?rî[¸oy‘z»µ]àj=_ßpÞÒyœ@wƒÚÚZ†¨S³²²éíI®¥¥Eë“Ì&&&#’è›ÚÚÚ6ÇŠ±±1úõë'Kú¦£6Ôz¾¾Q·õˆjêv¥¯í¨¥¥¥Í>¸oá¾¥-õñðâ¹HßÛç-Ç t7J¥Ðæe ·o߆™™,--ÅKtöööxðàêëë5Ê333õz¼Z©TÚæXQ*•¨¬¬Ž%}ÓQj=_ߨۉz?¨eff¢ÍX}Á}‹vÜ·´ÕQj=_ßpÞÒyœ@w™L@{UÏÓG...P©T ´ººùùùpqq12qÉd²v;s}=^lmmñÆo´»_ $FX¢S·“;wîh”gffêuR÷-÷îÝʸoá¾E+++XYYiÝ/FFFprr'0‘qÞÒyœ@wƒÀÀ@ <)))BYuu5233-bdâš>}:Œ‘˜˜(”©|š5k–Xa‰.::eeexøð¡Pvùòeôë×&L12ñ`Ú´iHKKCSS“P~åÊ„……aÀ€"F'øúúj´¡‚‚ܾ}[¯Ûºo9}ú´PÆ} ÷-íyÿý÷‘ššŠªª*@SS’’’)Ê›‰{Î[þbòº©¨¨ ØØXŠ%™LF¦[¿Àa÷îÝd``@Ÿ|ò íÝ»—BCCiàÀ¯õ¸£ILL¤ððp 'CCC2d…‡‡Ó»ï¾«Q/..ŽúôéC~ø!-^¼˜,,,hþüù"EÝýöíÛG±±±Mèw¿ûÅÆÆj ÍW[[KžžžäééI»ví¢Õ«W“±±1­^½ZÄȻתU«(66–BCC €Ð†®\¹"Ô¹uëYZZRdd$íÛ·fÏžM&&&tæÌ#ï>¥¥¥B²±±!‰D"L·~yNbb"™™™QDDýío#ggg6l˜0vkoÃ}‹vÜ·h7kÖ, '777 ;'NœêÐàÁƒÉ××—V¬XA#GŽ$kkkaÌùÞ†ó–îÁÃØuRKK JKKÁÁÁ L777 õfÏž sssìÞ½IIIð÷÷Ǿ}ûzí}gVVVðóóá_°°°Ð¨·|ùrÈår?~Ïž=Ã?ÿùOÌ;W§±êRUU•p|¨¿Å—––âéÓ§B333\ºt Ë—/ǦM›`mmøøx|øá‡¢Ä¬ ?ÆãÇáàà€èèhaµ~°ÉÛÛÉÉÉØ°aÖ¯_WWWœ:uª×¾úÝÄÄDkSSSá÷1cÆàâÅ‹øî»ïpûöm,X°}ôÞ|óMÆ«+Ü·hÇ}‹vr¹R©´M²¶¶~wttÄ•+W°uëV( „††bçÎpssÓu¸:ÁyK÷0 ";ÆcŒ1Æ^|4cŒ1ÆcÀ 4cŒ1ÆcÀ 4cŒ1ÆcÀ 4cŒ1ÆcÀ 4cŒ1ÆcÀ 4cŒ1ÆcÀ 4cŒé˜B¡@NNŽØat¨´´ÉÉÉ(//×:?55yyy.ãÉ“'¸pá‚ÆX³Œ1ÖpÍÓ ÁÁÁFvv¶Fy~~>‚ƒƒñÓO?é,–Å‹ã‹/¾ÐÙú:£ªª #FŒ€““fÏžôôt­õfÍš…Ý»ww¸¬ÔÔTŒ5 555²wïÞÇwuØŒ1¦Sü&BƘ^¸ví€_ÞXwèÐ!¡¼®®×®]Óxƒ›>;qârssQVVöÊo7twwǺuë`ffP*•˜;w.RRR`kkÛá2Ƙ(ø 4cLoŒ=‡Æõë×;¬WUU…ÆÆF²††TWW ÓBÒÝÜÜŒ[·n¡¡¡Aã3>ÄÏ?ÿÜáºÊËË‘•••JÕnââb(Š6Ë€§OŸ¢©© PTT„;wît¸>"Bnn.222Úlcuu5!—Ë¡R©PUUÕá²ÔÊÊÊ‘‘Ñ&>GGGÄÄÄÀÄÄ*• Ïž=ÔÔÔàéÓ§m¾´ÔÖÖ"55÷ïßïp0ƘØ8fŒéÈÈH„††â³Ï>ë°ž4ʶlÙ777a:!!‰G…D"¿¿?lllðÃ? °°r¹®®®puuÅôéÓÛ¬£±±S§N…ƒƒ||| “ÉššªQ'-- žžžppp@PP$ ¾þúk:‰›7o†ŸŸ „ùóç·»] …pvvF@@$ vîÜ)Ìwss÷ß~‹óçÏC"‘@&“u¸Ÿêëë1yòdØÛÛÃÏÏ...ÈÊÊæŸ={‰UUU(((@PP`̘1H$H$€ÊÊJŒ5 ‰‘‘‘Ëå°±±épÝŒ1&&N czeݺuHJJBRRR—,oãÆ8~ü8JKKñÎ;ï &&Ó¦MÃÒ¥KñôéSlß¾ßÿ=rss5>·wï^XZZâÁƒP(2dÞyçÔÕÕøåÊô˜1càãレ¬,TTT`ýúõˆÅ¹sç4–µbÅ LŸ>=ÂÉ“'µÆYWW‡‰'ÂÆÆW®\A~~>æÌ™ƒ àÒ¥K~yppöìÙýÏÛZ¾üòKøùù¡°°iii077Ç®]»´Öurrî?OIIˆ[·nEII PVV†ººº6_`c¬'áš1¦W~ýë_cüøñÿó*ôËúä“O[[[ÄÄÄ ²²r¹sæÌ……,Xœ9sFãsFFFØ´i¬]»¥¥¥øñÇ›7oF}}=¾þúkxzzÂÂÂü1±}ûveã¯ý+lmmÑ¿­q|r¹ü•–.üîéé ˆˆˆÐ¨#—Ë¡P(4ÊBBB„‡ë ((o¾ù¦p•6##ýúõâE‹ R©@DP©Txòä ZZZ4–5~üøÿç½{÷`aaàà`¡ÌÀÀo¿ý6nܸñ’[«éÅíô÷÷Çš5kÐØØ“—^ÎÂ… ©TŠI“&aþüùq2ÆXOà 4cLïøùù!::Ë—/ÇÚÌ700hSöâwjæææm>׺L]þâCq}ûöÕ˜622‚±±±0fr}}=lllÚ$ÇQQQ°´´Ô(³··×[kÍÍÍ011¡¡æûöí+<„ØY/n§zÙ}päÈ‘(,,Äž={pàÀ„††bäÈ‘¸pá‚Öÿ Æ'ÐŒ1½‡¡C‡bÏž=mæyzz¶Í"11±K×ñâE455ÁØØpóæMTTT*úøøàìÙ³?~<ììì^y}®®®¨¨¨ÀÍ›71|øp¡üÌ™3ðõõ}åå¿ u­m4sss,\¸ .DZZqöìYŒ3F'±1ÆXgð=ÐŒ1½äââ‚ùóç#>>¾Í¼ &àèÑ£(,,DSS8€‹/véúŸ?ޏ¸8ÔÖÖâÑ£GX·n,--1yòd¿ÜÖ`bb‚¿üå/¸ÿ¾ð¹àêÕ«^ßÔ©SammÕ«W#??uuuذaŠŠŠÓeÛÕGGG8;;ãÔ©SIô±cÇðüùsaZ=|žúJÆëi8fŒé­¿ÿýïZËß}÷]”——cðàÁ0`þñ`ñâÅ]ºîéÓ§#11NNNpvvÆéÓ§±wï^á!@™L†C‡áܹspssƒ‡‡ìííáîî.¼¦3,--ñïÿW¯^…««+±lÙ2,_¾‘‘‘]ºmYºt):sssá6–U«VÁÖÖ>>>pssCTTfÏž­Ó¸c¬3 H=ŽcŒõbwïÞ…0ö°ZAAjkkáèè¨qOoyy9RRR`mmÀÀ@ÔÔÔ ¢¢...~¹JªT*ááá!|F¥R!''§Í² `jj*ÜŠ¡ž¶±±B¡€R©Äˆ#„Q(Z«­­Eff&òòò`ee???·øÝ½{„……ÅK퇪ª*¤§§£ººþþþ4hÆ|¥R‰––888t¸œÜÜ\XXXhŒ×\SSƒ¢¢"¸»»ÃÀÀÏŸ?Gaa!ÜÜÜÚÜ{]\\ŒgÏžÁÃÃ*• …¹¹¹°¶¶Æˆ#`jjúRÛÃcbàš1ÆcŒ±Nà[8cŒ1ÆëN cŒ1ÆëN cŒ1ÆëN cŒ1ÆëN cŒ1ÆëN cŒ1ÆëN cŒ1ÆëN cŒ1ÆëN cŒ1ÆëN cŒ1ÆëN cŒ1ÆëN cŒ1Æë„ÿé·Îß"ÿgQIEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/Q8-1g-idx-compress.svg000066400000000000000000004107361231437614300251440ustar00rootroot00000000000000 PyTables-v.3.1.1/doc/source/usersguide/images/Q8-1g-idx-optlevels.png000066400000000000000000003310461231437614300253070ustar00rootroot00000000000000‰PNG  IHDRÐß}™SsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwxÕúðïlßM¯$z‡P5R.RT$H)^‰» "(Vlˆ^ôwQPA/ ¼U ¢H»ˆBBB Hz/Û÷ýývÈff³©»I|?Ï“'É™Ù3gfwϾ3{Þ3cŒ1ÆcÕ¢ðucŒ1ÆkJ8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€fŒ1Æc¬8€f5R\\Œ£G"''Ç×MñºãÇcîܹ8s振›â–ÕjÅ®]»°páBÌŸ?ß×ÍiR>ûì3>fÍÔ3Ï<ƒ?þØgÛ/++ÃñãÇqæÌ˜L&Ÿµ£®þ÷¿ÿá™gžAYY™XÖï›eË–á7ÞhºëÛ?ü€¹sçâÏ?ÿ˶lÙ‚åË—û°U¬!qͪeݺuèÒ¥ ‚‚‚pýõ×#""­[·Æ’%Kàp8|ݼzóË/¿`áÂ…¸té’dYRR–/_Ž .x¿aÕðÇ 22·Ýv¾ÿþ{>|Ø×MjR¾þúk¼÷Þ{¾nk+W®ÄŽ;ªµî[o½…;ï¼:t€ ®Õ6–/_ŽN:! ýû÷G÷îÝa0п|úé§°X,µªÛW.\ˆ_ýƒA,kÈ÷ÍÆñé§Ÿ6HÝõíøñãX¾|9²³³Å2Ì;'NœðaËXCQùº¬q#"ÜsÏ=X·núõë‡+V wïÞHIIÁ矎gžyß~û-vîÜ ???_7·ÎŽ?Ž7ß|'NDëÖ­]–ÅÆÆâÅ_D§N|Ôºªýë_ÿBAAΞ=ÛhÛÈXc÷üóÏC­V£_¿~(((¨Õ‚¼¼<Œ;‡ÂàÁƒñàƒ¢{÷î0›Íøí·ß°sçN$$$@«ÕbêÔ© °õïÈ‘#øöÛo%'"“'OFll¬ZÕ¸9ݺuÃk¯½†Í›7ûº9¬žqͪ´~ýz¬[·“&MÂúõë¡ÓéC‡Ž÷Þ‹ùóçãŸÿü'–.]Š—_~ÙÇ­mX±±±úƒâìÙ³hÙ²%ÏŒÕÁÉ“'Ñ©S'( ôíÛ·Vß8-\¸‡ÂâÅ‹±hÑ"(×¾ì½ýöÛñüóÏã“O>Ahhh=¶¼a½úê«èܹ3ÆŒãR>yòdµ¨ñ>ú(yäüñÇèÑ£‡¯›Äêá`n™L&,\¸Z­ï½÷ž<; ‚€×_111xûí·‘‘‘!.{øá‡ñðÃKê>_ý5vî܉qãÆ!** ¯¾ú*üqL:D$©ûìÙ³ˆÇgŸ}ævß{ï=ñ«Èyóæ!>>ñññX³f €ò1nñññ8zô¨ø˜¯¿þñññ8sæ V¯^áÇ#&&3gÎDRRàĉHHH@Û¶mѧO·c1333ñÀ oß¾ˆˆˆÀСC«l¯ÓÑ£G_ýyyyb»?üðCqsçÎaÚ´ièÚµ+Z¶l)ÃÊž{î9LŸ>eee˜;w.úõë‡ÐÐPã6Íf3/^Œ!C† ""mÚ´Á¸qãðÃ?¸¬—––†»ï¾ݺuCtt4n½õVlß¾]RßâÅ‹1yòd˜L&<ùä“èÕ«ºwïŽ Àh4þóŸÿ`̘1hÑ¢†ŽãÇ»ÔqùòeÄÇÇcÓ¦Møî»ï0vìXDEEaÈ!Õ:®NÅÅŘ?> €ððp 0o½õ–Ë•É7">>^ruÎf³!!!“'OFqq±Çm¥¥¥aêÔ©ˆ‰‰Áu×]‡E‹¡°°Pò|:ŸóÊÇ–,Y‚ñãÇKÊ ñÄO ..ááá8p –-[&y¿T<öO=õ®»î:„„„àðáÈÇçŸ.Ûö¥K—bìØ±(--­r¿þúk̘1Ý»wGHHzöì‰Y³f!==]²îÌ™3ñôÓO#;;³fÍB§NбcG<ôÐC²Ç3//÷ÝwÚ·oØØX<úè£ÛSY—.]\Þš:zô(>úè#ôïßÏ=÷œl]‚ `Ö¬Y¸õÖ[ŲŠ}ãwß}‡ñãÇ#::Ï?ÿ¼¸ÎÊ•+1lØ0DFF¢OŸ>xøá‡‘ŸŸ/.'"Lœ8K–,qÙÞºuë/jñá‡bôèÑQJJ ¾üòKÌš5 ‚ ¸,“{½9û“É„'žx½zõBëÖ­1}útÙçÙù>ïÑ£:v숙3gºŒ%®ìÊ•+˜={6úôéƒÈÈH 6 7ntYç­·ÞB||¼¤_ÈÏÏÇí·ßŽûï¿ßå=üûï¿cúôébß4räHìÝ»Wvû›7oưaÃ…Ûn» ;wîtÛÖ{î¹*• ï¾û®ÛuXEŒ¹qôèQ@wÝuW•ë½øâ‹€6oÞ,–õéÓ‡úôé#Y÷øñã€Þ}÷]—ò'žx‚PTTÍ›7¦M›F†‚‚‚èôéÓâz‡"4nÜ8 £û￟ž{î9ú÷¿ÿMË–-#ôý÷ßK¶ûä“O:uê”Ûýغu+5ŠÐßÿþwZ°`-X°€¾ùæ""úüóÏ €ø?ÑÊ•+ Mž<™´Z-Íœ9“&L˜@ …‚zôèA'Nœ ˆˆºá†èñǧÐÐP@›6m’눈R©T4zôhZ´hµk׎ÐÓO?]åñOJJ¢ PëÖ­)00Pl÷Ž;ˆˆèÈ‘#H!!!ôä“OÒ+¯¼B}úô!ôÖ[o¹Ô5jÔ( ¥›nº‰LÏ>û,Íž=›JKKÝn¿°°zöìIèŽ;î 7ß|“^|ñEºõÖ[iÞ¼yâz'Ož¤ ¤yóæÑ«¯¾Jýû÷'ôÊ+¯¸Ô9aÂò÷÷§Q£F‰¯ g›}ôQúôÓOI£ÑД)Shúôé¤R©( €rrrÄ:NŸ>MhäÈ‘BóæÍ£·Þz‹ @è7ÞpÙæ]wÝEZ­Ö¥,--Ú·oO‚ ÐСCéÅ_¤^½zš2eЏžÉd¢¾}ûRpp0]¸pA,Ÿ?> µk×Vù={–"""(00æÏŸOK—.¥ë®»ŽÆŽKèñÇ×ýæ›o}þùç’zäö#55•Ú´iC‚ Ј#è…^ =zºçž{ÜûÐ3Ï!"¢¿ÿýï€n¾ùfZ²d =ðÀ¤V«©uëÖ”žž.Ö3bÄjÓ¦KÝ#GŽ$Ô»wo—ò¸¸8êÑ£‡Ç¶mذÐÎ;%Ëä^oÎ~ä–[n¡ÈÈHš;w. 2„P×®]Éf³‰ëÚl6û,µiӆР/¼ ®—žžN-Z´ Î;Sqq±X>~üxR©T”˜˜(–mÞ¼™t: š1c=ñÄN …‚þýï»lßù9Ó¿Zºt)ÍŸ?ŸÂÃÃÅÏŽãÇKŽQ×®]%Çž5}@3·œãóÏ?_åzëׯ'´xñb±¬&ô¾}ûÄγbÇzþüyR*•/–9hAè÷ßw©;??Ÿ ÝqÇ.åf³™ÂÃÃéoû›Ç}vć’,«*€nÕªeee‰åK—.%¤V«iÆ byff& ‚@7Þx£Xf·Ûéºë®#Nç|ÙívºóÎ;I©TVø; 6ŒÚ¶m+)2d ‚àRGii)uïÞôz=¥¥¥‰åÎÛo¿‡Çm=öØc@òACTX:Ý|óÍ€Ž;&–FêÓ§i4JMMË'L˜@húôéd±X\êÂÂÂèÏ?ÿË?ûì3@o¿ý¶Xæ  Ю]»Är‹ÅB$½^ïR‡\ pçwúùçŸ]ÊŸ}öY@ß~û­X–œœL4pà@²X,ôÕW_‘ 4sæÌ*ŸÓŒ3€Ë»ÉdO2ê@7ŽA|¸;O,÷ïß/–9ým·ÝFv»Ýeý%K–:xð KùêÕ« €xÒV•üü|IÙW_}EháÂ….åmÛ¶£Š¯GçIErr²XöôÓOKNN‡¸®·è{ï½—Ð?þX£Ç9ûÆÊï"¢~øÐĉ]Žƒ³OzðÁŲW^y…PJJ •¿†ôz=ýío#AÄ~*??Ÿ =úè£ÛöÜsÏJJJ’,s@ 3fÙlËŸzê)Éëä“O>‘|~=òÈ#bÀídµZ)66–üüüèòåËb¹Ýn§qãÆ‘Z­vyM|ÿý÷$͘1ƒˆˆþùÏJNžóóó)""Bh;OŠ###©¨¨ˆˆÊOÒ)66–ŒF£¸îáÇÅçN.€7niµZ—Ï7ÖôñæVZZ eË–U®ç\~þüùZmçý÷ß,X°J¥R,o×®n¾ùfìÞ½V«Õå1}úô‘Œ' ÆôéÓ±}ûv—á$[¶lANNæÌ™S«öUÇw܈ˆñÿ›nº ˆ;ï¼S,ŒŒDÏž=‘šš*–ýôÓO8zô(î¾ûn´mÛV,W(¸÷Þ{a·Ûñý÷ßת]çÏŸGbb"úö틞={Šåƒ'N„ÑhÄ–-[$›1c†ä«Z9D„Õ«WcРA¸ûî»%˵Z- =={öìA÷îÝѯ_?q¹N§Ã¤I“`±Xd“l V«ÅÿGŒ"¨Q£\^—Îãí6SQxx8n¾ùfñµZ &Àh4â¿ÿý¯Û}ËÊÊÂæÍ›qË-·`À€.ËfÍš.Ã`:wîŒU«VáçŸÆœ9s0sæLtëÖ ÿ÷ÿçvNv»›6mB«V­ð·¿ýM,×jµ7nœÇÇW%-- ;vìÀ˜1cзo_ûá4}útÉðƒüãÐh4Xµj•Kù‡~ˆÖ­[ã¶ÛnóØžŠ³Z”••!##ýû÷GLLŒìl‚ `öìÙ.¯Ç‘#G(šå´aÃÉñwÜq‡Ç6Õ'ç˜é˜˜ɲ[o½Çž{î9É:ݺusyå¹(0mÚ4—ãpûí·C¯×cÆ b™óµ¾gÏåý‹ÑhÄ믿"¾}ûûöíƒÃáÀ-·ÜâqŸ~ÿýw(•J´k×ÎãºÍš5 Füßù¼%''‹eëÖ­—~¦L™"©oÿþý8uêî½÷^´jÕJ,wö•V«»wïvÙÞÂ… ±~ýz<þøãX¸p!F… ˆëlÚ´ ÙÙÙxüñÇáïï/–kµZL›6 YYY8rä`çÎ(**Âøñã]†4ÆÅŹôÝ•uéÒf³Ù¥ßgM'2·œJVVV•ë9—‡……Õj;IIIÐëõ²&iii°Ùl¸xñ¢Kr\||¼l]>ø >úè#|òÉ'xöÙg«V­BHHˆ¤ƒ®OcÇŽuùßÜ5 *•J²ìË/¿ÿw}'OžÄĉ]ÖµÙlPëŽ7%%Àµ®ŠFމ7ÞxCrâ£V«e×—sùòeFÜpà ujÇ‹/¾(i‡N§ÃðáÃ]ÊœÇuôèÑ.å­ZµB`` ËRN7Ýt“äd`ĈÊǺóÇ222$Ï‹³}•Ÿ—©S§bß¾}Xµjôz=6mÚT­Ùi._¾ «ÕêèWlk]tû‘––&»jµZ² …Bö=É“'cÓ¦MX¶l‚ƒƒqìØ19r/¿ürµÆ§¦¦âå—_ÆÎ;‘——ç²Lnʸ~ýú¹KЫW/Ÿo«ÕŠ+W®`èС’\ çsí-ζfffJNN…B›Í†={öÈî¯ÜqOIIB¡¼>´Z-n¼ñF|ÿý÷ÈÎÎFDDââ‽{÷böìÙØ½{7bbb0tèPÄÆÆbïÞ½˜2e vïÞ ¥R‰aÆyܧ‹/",,Ì%ö$ C‡u)«ü¼åïÁèèhtëÖÍeݺ´Àµ¾òÈ‘#’ײsJÀʯåÅ‹ãÀX±bZ¶l‰ÿûß.ýóý±}ûvìÚµËå±¹¹¹b#FŒû ¹“Ž#F`íÚµ’ràÚkââÅ‹èÒ¥‹ì:¬éáš¹Õ¹sgžƒ7çòöíÛ{¬“dü àï﨨(ɲ¨¨( 6Lò¡èîl¿ÿþ0`V¯^… "55û÷ïÇc=&©£>¸üïì +—;—U<ÊOXZ´h!YΜ9ˆ‹‹«U»ŠŠŠ@€T,«œŒŠÀÀÀÕ/·Ÿum‡V«u¹ú \;®•?XËä^_rÛtžV•ØçüðlÑ¢¢££%Ëd? KJJ”KAAAnë¯ÈÙŽªÚZ]•s?¢¢¢d÷cÖ¬Y’osÜž?øàƒØ°aÖ­[‡Gy~ø!”J¥x5»*ùùù¸ùæ›Q\\Œx±±±èÞ½;t:î¼óNÉ7MζTæ|8÷Õd2ÁápÈ«ÈÈH—o¶Z×®]”÷‹•¿¹pž8 $$Döñr}[QQ ƒlÀí|Í!""*• C‡ÅÞ½{ADؽ{·xß|óÍbÂÛîÝ»W­×hûöíqüøq˜L¦j÷£ƒArÜ+?o@ùk_nŸÕjµË·zÀµ¾²U«V—/ûÛߪ}ø£>† 0sæLlÞ¼Ó¦MÃþýû%ßBTæÜ÷ªÚZ‘3¨8TÉé×_•ÝáÇã™gž©Ö~TeÈ!ˆÅªU«0sæLlذcÆŒ‘ **Û»w/.^¼ˆ×^{Mü†(bNŸ>]ë)${¬<»Ý^«zkcðàÁ€5kÖ`úôéõRgÇŽqäÈüúë¯8p Ë²={ö@¥R¹¡Î@ùÇÄÑ£Gñè£(¿rº|ùrø >øà<üðÃ.WŠu>üðÃ’¡3rmÊ_Ç•Ožå^{NÉÉÉÐétb ΚÍÜò÷÷ÇK/½„¢¢"<ûì³²W÷V¬X3gÎ`Ê”). mÚ´AzzºËX7øâ‹/$uŒ1øÏþS/íž2e ÂÂÂðÞ{ïaíÚµ¸ñÆѽ{÷j=Ö9NØ›w¼á†àçç‡Õ«W×û]»víŠÐÐPìÛ·OâäW]ù*YMèõzÄÅÅaûöíUN;Õ±cG´hÑ€Ùl®÷vTåäÉ“’aHÎ@uРAn×¥K´lÙŸ}ö™xU¹*§NÂc=†¡C‡âã?Æ{gƒâ…^ðøXÄÆÆâÈ‘#âU¶Êm­¨M›6®|8=zT2¦gÏžˆŒŒÄš5kÄ+quõàƒâ·ß~Ãc=†’’’jŸ fffŠmªèË/¿¬s{à 7àÏ?ÿ”ô9î¦"k(7Ýt&L˜€Ý»w×hºÄª8_§•‡$%%!-- ×_½ËIšóŠóóÏ?»Ý.þ?lØ0¨T*ñäEnÈçóUùØÖ‡AƒÁn·KÐÊû ”ëÐétXµj•ìçQeÛ·oÇŠ+pÿý÷ã_ÿúžzê)|úé§.Ï‹sˆOÅi"Ýqž¼Tn[III•w~MNNF·nÝê4="k„|’ºÈš «ÕJÆ ³òwìØA/^¤={öÐ?þñ@]ºt¡‚‚—Ç9gæ=z4}:EEEIfá0™LÔ¥K ¤åË—SZZ•••QRR­^½šî»ï>q]ç,+W®¬²ÝÎlo´nݺío@@ 2„6lØ@û÷ï§sçÎQÕ³pTžµÃh4š3gŽdrYëï¼óŽ8=ß‘#G¨´´”®\¹Bûöí£Y³fÑ/¿üâ±íîfápN»4}útJII¡ììlzûí·I¡PÐðáÃ]Ö5jµhÑÂã¶*:tè ‚@ݺu£]»vQaa!¥§§Ó–-[\Žý| Nu—œœL999´bÅ R*•4hÐ —:'L˜ ;ûÁÖ­[ mݺU²,((Èe¦ç,!!!tÓM7QRRч~H††êòx¹çeË–-€H?üðRvv6ýüóÏ4oÞ<Ú¶m•OãÖ½{w £K—.‰Ÿ1c ‚ ;µbe7n§1KMM¥ÂÂBzÿý÷)22R2 ‡Ãá ~ýú‘^¯§%K–Pjj*­X±‚ºtéBááá’ýpNC6dÈJLL¤¢¢"ÊÊÊ¢Ÿ~ú‰{ì1úúë¯=ûŠ ÉßߟP›6m$³u¸säÈ@×_=ýøãTZZJ;vì ˜˜ v™q¨|ŽaÆIêqÎܳfͱìàÁƒâsõÛo¿Qii)}þùçMjµºÚ³p|ÿý÷´fÍZ³f µnÝšôz½øÿ–-[ªU‡súC4uêTúâ‹/è÷ß§äädÚ½{7Ýÿý€&Mš$>ÆÝŸDDeeeCAAA´yóf*(( 'NP\\œd–¢ò×GDDLSçœ2N¯×»Ì’S•ÔÔTÁeš8'w³pÈõ#ééé€,X –ýù矤×ë©C‡”˜˜HF£‘vïÞM­Zµ¢ÀÀ@Ékâõ×_ݱcÇľrÏž=” ΂qñâE ¡ØØX*++#¢k3ðøùùÑ™3gÄ:ï¹çyä:}ú4Fºxñ"mß¾&NœH………âºñññ¤T*iÅŠTXXH©©©4~üx ‘…£¨¨ˆT*•ìgkÚ8€fÙl6zå•WH¯×‹A©ógÖ¬Y²ÓRÙívqÞRçOÿþýi÷îݲW®\¡;î¸CR¿Z­¦Y³f‰ëU7€NII!A(44Ôeº¡êøøãiĈ¤Ñh\:û†  ­ZµŠ‚ƒƒ%Ç cÇŽ’)ûä¸  ív;½úꫤR©\ê?~Ü¥?¹ýöÛÅ©K+ÐË—/'…B!; kÚ¢j|ÂʧÚ:sæ N:…½{÷bÕªU¸çž{°víZ·c‡ûí7$''£GèÞ½;, ®\¹â6QíÊ•+øã?——‡èèhôîÝÛe\œÙlFzz:ªL\;sæ ºwïŽyóæáwÞ©Õþ–––";;AAA ÿoÑ¢ôz=€ò¤’ÜÜ\DGG‹›Û__ IDATS¶åI2/^”MÆÊÎÎFYY™lâŒÑhÄéÓ§‘’’‚ÀÀ@´oßÞe `U222`³Ùd§ÏÊÇ™žù¤¯›"1iÒ$òóósù:ýµ8¯@W¾ã`S“ŸŸ/{Ú—’““I¥RÑìÙ³}ÝæEcÆŒq¹ «ÚÎ;I:yò¤¯›ÂÏÂQCW®\ÁÊ•+qüøqqÞÉ¿ªU«Vá†n€ÑhDff¦ìlÞd³Ùðꫯâäɓغu+^{í5·Wcc5·ÿ~|ýõרºu+ðÚk¯ùºIÌ‹Þ|óM|ñÅ(++“|Ĥl6V®\)ÎÍš k衇ÂÛo¿[o½Õ×Mñ9¥RÙ w÷«)»Ý޵k×"** ¯¿þ:žxâ _7‰ùZ­FÛ¶m«=sc¥P(жmÛ}ÝÞPNœ8/¾øă>(;/k¾zöì)™A…¹7~üx_75 ]ëׯǑ#Gðî»ï"88gΜ‘½ùcŒ1Æk¾þ2W - .\¸Nç69Æápà?þÀå˗ѯ_?—! ™™™x÷Ýw«œ,1ÆcŒ5Í~VïŸ~ú ×_=еkWÜsÏ=²ëåååaðàÁèׯî»ï>DEE¹ŒïÛ¶m²³³1fÌ >%%%˜4iRRR¼µ+Œ1Æc¬höC8±mÛ6\ýõX¾|9´Z-öïß/Yï®»îÂ/¿ü‚Ÿ~ú -[¶Äúõëq÷Ýwã믿ÆèÑ£a4QXX(®ßµkW$&&¢{÷îoÓËcŒ1Æšf@W“É$  ³²²ƒE‹áÅ_Ë;tè€Þ½{cÛ¶m’ºœtDDDC7›1ÆcŒ5"|éÀ±cÇ`µZÑ£G—ò^½zá矖}LRRRµëÿ裰aÃI¹§0ÆcŒ5´´´4IÙôéÓqß}÷ù 5MC³]W®\Éô<½zõBvv6l6[êß°a~ùåX­V—ò´´4˜ÍæZ•;v ©©©õV_ZZRSS]ÞDžk·ÛqèС*×KKK물^bb"RSSëÔæÊe{öìAqqq½Õ—––†C‡¹Ìùíé±þù'Ο?_åz‡물޾}ûêÜæÊeß}÷]½Ö—––†Ôè±G…Ñh¬r½ÄÄDÙÇ:—Õçq±X,سgO½—sçÎáСC5z¬sŸÝ­W\\ŒÃ‡Ë>655U|ßÖ×~¤¥¥áرcõz\jÚ_9û–ªÖ«ª¿JLLûŸúÚÇãܹs>í¯œ}KUëUÕ_íÛ·¯Îm–ëÇ}Ý_9û–ªÖs×_Uìwêk?, >ìóþÊ]ê$×_åääÀjµâرc²þX¾¼‹‹·5І &)÷Ýw egg»”¿÷Þ{€òóóë´Ý#FP¯^½êTGe[·n¥­[·ú´N£ÑHsæÌ©u}ûö¥ .Ô¨ž<þøãu~¾êZç7ß|CŸþy­ëìØ±cÚW3gÎôy , ôôôZÕyáÂêÛ·o¶çI~~~½ßݯ6uz:ŽUÕ¹páBZ¸pa¶ç ÷-ò¸o‘Ç}‹Tsè[zõêE#FŒ¨Ñöþjx Î圜œìrc€Ó§OC«Õ"88¸ÎÛ(..ÆK/½„áÇcøðáu®¯[·nu®£®uªT*Œ;¶Öu6¬ÞorNçÓ:;uê“ÉTë:‡ R£öUÇĉ}^ç-·ÜÿZÕ„aÆÕh{žèt:ÄÇÇû¼NODZª: P£mU÷-ò¸o‘Ç}‹TSî[öïßýû÷£¸¸˜o”䉯#xorwúÇ$´jÕ*—òo¼±^ÎØGŒAƒ ¢óçÏ×ûŒ¦¬!®è4 qE§©kˆ+:ÍAC\-n¸o‘Ç}‹÷-®òóóéüùó4hÐ ¾í0pà@DGGãèÑ£b™ÉdÂo¿ý†I“&ÕË6t:ÚµkW/W³cŒ1Æê[pp0ÚµkWïß¶4GÊ—^zé%_7¢!bÙ²eHLLľ}ûŸŸ£ÑˆÄÄDôèÑz½ …J¥o¼ñ ŒF#ž|òI\ºt «W¯FHHHÚ°oß>dffÂÏÏY»ÖäuêÔ ‘‘‘P(ø<®¢nݺñWg•¨T*téÒ¡¡¡¾nJ£Ž6mÚÀ`0øº) ÷-ò¸o‘â¾ÅÕ©S§°wï^««àý÷ßGII‰¯›Ñè,Y²Ä×MhtJJJðþûïûºNbb¢K¦=+Ç}‹<î[¤¸oq¥Ó録²2_7¥ÑûKÝHÅWk×®õi;cŒ1Æ<á¸Å³fš1ÆcŒ±úÄ´—$&&"!!Aö¶àŒ1Æc¾¶mÛ6$$$ðаjàÚKâââ°víZ_AJJJïòØ9sÆ×Mhtl6RRR|ÝŒF'''999¾nF£Ã}‹<î[¤¸oq5qâD¬]»qqq¾nJ£Ç´—dffúº 'úÈãD)Nô‘ÇI„ò¸o‘Ç}‹÷-ò8fñŒ“½€ã3Æc¬©à¸Å3¾í%<š1Æc®>¾í|&ÇcŒ±¦‚ãÏø ´—˜Íf_7¡ÑáDyœè#ʼn>ò8‰P÷-ò¸o‘â¾EÇ,žqí%< _Š}äq¢'úÈã$ByÜ·Èã¾EŠûy³xÆC8¼€¿ aŒ1ÆXSÁq‹g|ÚK8‰1Æc'V_ö>“cŒ1ÆXSÁq‹g|ÚKx@¾'úÈãD)Nô‘ÇI„ò¸o‘Ç}‹÷-ò8fñŒh/áùRœè#}¤8ÑG'Êã¾E÷-RÜ·Èã˜Å3ÂáüUcŒ1Æš Ž[<ã+ÐŒ1ÆcŒÕÐ^³p0Æc¬1ãY8ªh/‰‹‹ÃÚµk1qâD_7¥ÑàDyœè#ʼn>ò8‰P÷-ò¸o‘â¾ÅÕĉ±víZÄÅÅùº)Ð^Âò¥8ÑG'úHq¢ò8‰P÷-ò¸o‘â¾Å•ÍfƒÉdBzzº¯›Òèq¡$$$àСC1bÆŽ‹±cÇúºIŒ1Æc.¾úê+|õÕWØ·o ÄI„UàÚ 8›•1ÆcMÇ-žñÆcŒ1Æj€h/áùRœè#}¤8ÑG'Êã¾E÷-RÜ·Èã˜Å3 ½„ïê#ʼn>ò8ÑGŠ}äq¡<î[äqß"Õú³¹þþ8fñŒÇ@{%bŒ1ÆX}3™."3ó3 Ý>‚ ®—z9nñŒ¯@3ÆcŒ516[!²³7ÈŽÒÒSHOÿ6[¯›õ—Á4cŒ1ÆXâpX‘•µv{©Xf±¤#=}5L¦ ¾kØ_Ð^Âò¥8ÑG'úHq¢ò8ÑGª©&ú44N"”Ç}‹<î[¤šJß’“³ ‹ûqÉaac¡Õ¶ª·íqÌâÐ^Âò¥8ÑG'úH5ÅDoà$ByÜ·Èã¾Eª)ô-……‰(-ýÝíòÀÀð÷ï[¯Ûä˜Å3N"ô¾£cŒ1ÆjÊhLAfæzò¡šN×QQ÷ ¾¯‡rÜâ_fŒ1Ækdl¶ÞÆI„ò¸o‘Ç}‹Tcì[ˆÙÙ[«¼›`XØxhµÑ ÖŽY<ãÚKx@¾'úÈãD©¦èã œD(ûyÜ·H5ƾ¥°ðÊÊN»]8þþ½´ ³xÆI„µ@D¡Úëó`|ÆcŒyRV–„¬¬p7îY¯ïˆÈÈ„†½þÉq‹g|ºFÖ­[£cÇŽ˜:u*Ÿ¡1Æc¬^X­9ÈÉÙ÷Iƒ!ŸÜàÁ3«~j`íÚµ¸téRRR…þóŸ¾ncŒ1Æš8‡Ã|5iP~ì±B¡A‹Ó Tê½Ü2æÐ5ТE €B¡@hhh’Tx@¾'úÈãD©Æ˜èÓp¡<î[äqß"Õú–ò¤ÁÿÂjuÿ^ ›µ:Òkmâ˜Å3 k衇B·nݰwï^¼üòËÕ~÷âDyœè#Õ}N"”Ç}‹<î[¤CßRX¸Fc²ÛåÁÁCáç×Ë-☥:þI„D„¤¤$$''#44C† ‘]¯°°ûöíÃÅ‹qà 7`РA’uòòò’’‚×^{ ±±±xíµ×ú(vìØ›nº ³gφÃápY/447Üp-Z„­[·zccŒ1ÖÌX,YÈÉÙ wÁ³Z†ððIðvð̪§ÙÐÑÑÑxçwpüøqÜ|óÍn×{øá‡€¤¤$ìÞ½»víÂÇŒ/¾øPTT„Ÿ~ú ‹iiixÿý÷qçwzk7cŒ1ÖL8&dem‘Ev¹B¡Edä4(:uY2,Èß_ßMd4ûºwïÞ¸ï¾ûзo_¨T*Ùu.]º„ï¾ûS¦LÁP~[Ì!C† K—.X½z5Àn·cÉ’%èÙ³'fΜ‰ž={bÞ¼yÕnÈ—âDyœè#Õ}#N"”Ç}‹<î[¤|Ó·²³¿€Í–çf¹€ððIP«Ã=Öd:gBÆš &¢ðÇÂzk!Ç,ž5ûº:Nž< ‡Ã=\éÇÆÆâĉ€|ùå—8{ö,öíÛ‡  88¸ÚÛØ±c¦M›†xÀå'9Ù5qà¯TöþûïcöìÙ¢-©lÉ’%¦-¥Ì™èÓÚҘʜI„¡-©Ì™DØÚҘʜI„¡-¥¬b¡·¶{äȧ0¯í‹}…óçsÅÿƒƒ‡ã‰'ÞõX_ɉÌš: ©WRù{óQò[í^÷Θ¤gÏž˜6m8Vµ¿D¡S||úè#(®ç,Æ¥Ìd2q—q—q—qY3,ËÍ=†ÂÂíP(®k6›mP«•P( Ý1EŒ;ÜÕW°¯?Àl3C­TCqõæ*‚J@Д v ¬QûL&“KÙœ9s T*9‰° òcþbŒF# ((È¥Üù¿óUFRN:މ˸ŒË¸ŒË¸ŒËšv™Å’‰’’o\‚gÐjËC1µ:·C·õ‘½%¥'K˫Һ¬C6BÑÖ"øÝïE¨Âå±UµÏù·ó·R©”¬Ï\ñ”'ÀéÓ§]ÊO:ƒÁ€ÀÀÀ:o#11 ضm[ëbŒ1ÆXÓa·—]M´Ê.W(thÑbÁýÅ:»ÑŽÌgŠÁ³;úz¨kw}tÛ¶mHHHàùå«híÚµüþûï.å¿ýö›¸¬®âââ°víZLœ8±^êk8ÑG'úHq¡Î€é‚©Êm DøäpªÚM}7qâD¬]»qqqµzü_ Ю¿þztìØÑåŒ+??§NÂÔ©Sëe|W)¾[˜<¾[˜Tc¸[XcÄw"”Ç}‹<î[¤¼Ñ·äçï‚ÑxÎíò›¡×wr»Ü|ÙŒŒ2`Í‘¿z €°ÛÂ:*‚P÷y£9fñ¬Ù'æääà©§žìÚµ v»ñññ€7ÞxC¾ñùçŸãïÿ;æÌ™ƒ~ýúaõêÕÈÎÎÆ¡C‡Ð¢E‹:µÁùuÈ!C0qâD¾ ÍcŒý””œDNηËýüb1Ùíò²ÓeÈÞ’ ²ºÕÂ«ç<+O¶mÛ†mÛ¶‰1 'º÷—J"9r¤ÛeÓ¦MChh(Ö¬Yƒ?þÄ3ÏpÜâœaŒ1Æ«£¢¢_Ÿ¿Ëír­¶%ÂÂÆIÊ©FdoʆÃìpûXA) lBü{û×K[YÝqÍcŒ1VKv{ rr¶Áht?–Z©ôCDÄ]×°«øh1rwæîcg(ô DÞ ];éí¸™ïðh/9pàßÊ»Nô‘lj>RœD(“åqß"û©ºö-eegðçŸÿª2x%""¦@¥º6ô‚ˆ¿;¹;ªžU!*DÏŠöZð켕÷¼²½¦Œh/i×®ßÊ»Nô‘lj>RœD(“åqß"û©Úö-Däæ~‰¬¬p8ªNú ‡N×öÚcm„œ/rP˜(?K‡“6F‹èû¢¡—Nu×Pœ·òn×®×¶ÙTq¡ð`|Æc¬y0›/#'g ¬Ö<ëÄ!,lŒø¿£ÌÌÏ3a¾d®òq†î„O ‡Bí›ëœ·xÆc cŒ1Æ< r °ð €¨Šq -BCGÃß¿¯Xf͵"s}&lyU/ „‘!€P/Íf „h/±Ûí0™LP©TP©ø°3ÆcM…Õš‡œœ-0›/{\W«mððIP«CÄ2sšY³`/³»}œ ::qõÒæÚ°Ùl°Ùl°ÛíP*•>kGSÀc ½äСC|#•J8ÑG'úHq¡ žk7R9tèOÛÑpí%mÚ´Á|€±cÇúº)'úÈãD)N"”ÇI„ò¸o‘Ç}‹TU}‹ÃQ†¬¬ÈÍýD–*ëQ«Ã= ÁÁà ×B«‚ ýßlÍ}º™2@‰¨{£`èb¨ÝNÔ£±cÇâƒ>@›6m|Ý”F“½€ã3ÆcM‡Ñ˜‚œœm°Û=Ÿˆ\‡QP(4× @îW¹(>V\åcÕ‘j´˜Ñª Æ5´“ãÏ×3ÆcŒ1æ#D6äçïBQÑ/×U( ƒ¡›K¹Ãì@ö¦lSU>^ßQˆ)Phy0@SÄ4cŒ1Æþò,– dgÿVk¶ÇuõúNŸ¥òÚ­µí%vX³¬Èû.–̪‡|ô@ØØ0HÛ„qí%eee< G%)))h×®JΜ9ƒnݺy^ñ/Äf³áÂ… èÔ©“¯›Ò¨8ÃÃÃ}Ü’Æ…ûyÜ·H•÷-ç‘…‚‚½ rŸèÔÒÜMa”¦Ú`ÉÎ5Ç k¶SÕSÛ]­!7… èÆ Ïëú€s޲²2 ¾“ݘñ¹—ò8ÑGŠ“åÕg!ÁjÍ„Ñx‚ €J µ:*U0¡i}øsß"¯9ô-d#Øòm.²5× kŽ£vm¬úó°êÏÁ®ÍTÿÚ\É)BëÈ`±Lph¡ÏMi펕 €&BmŒÁ#‚!¨„ZÕãKf³ZmíOþ ¸wñ’ÌÌL_7¡Ñyÿý÷ñÒK/!88ØóÊ8f©0™ÎC¥ „V­¶AãùÁÌ’%KxÜY%Î$ÂeË–ùº)Š3pâĉµz¼Í–£ñL¦ó0ÏÃá(“¬# (•AP«Ã®Ô¡âo•*¤Q×õÙ·4‡fs/ž‡Õ«ÿ &ju(û<³–t ,阯˜aI·À–g9®¥n‘`ƒ] VÃyXÃÏÁ¡¬Yòh™ÉŠ»ÿ‡ùÓ‡TæV0d‚ÂXõ@¨‚:\ uÄÕŸp54( û¸VGff&ßÐN"ôŒß0¬ÖgQV– ³9Mfú!M$´ÚÖWê¨Ta„¦w5€±ú`·_ –˃f›­°Ž5 P©‚\‚ꊿ¯Ñø‚ÃaÙ| &Óy˜L`6_à:ÖW”P«Ã¡VG@£‰„Jåüâr+j¯µùj°ì ”-W,°æY™…T¥°èÏÁf8«.­ÆC3äPB›?º¢ëªð¡Ô!Ò Y®† i¾Ÿ%·xƽ›—œ?7nDll,bcc}Ýœ&‰È“éŒÆ³0ÏÂjÍóôX,™°X2Q\| Pè¡Õ¶ªT·‚BÑ4²¢«)»Ý“éÂÕ@ê|µnQ3›­6[L¦s•– P©«®k’På€Ãaˆ,nÿ'2Ãá°@TP*ý PøA©ôƒJå/þ­TÐØ¯¾Ö„Ãa…ÅrFãy˜Í`6ÿéq>c"»Ø?––^+•K`}-ÀP?£Ãä€9Ý kºõZÀœg)Ìâ¡üo‡pµLi†]›,` IDAT›& ²‘u ¤‚ʵ±Ô–ŽÐ‡@ÝæZ¬ WA¦† l¾re§N©S§pþüy´oßÞ×ÍiÔ8€öFƒàà`èt¬Õ„ÍVt5`N†ÉtGÕwwòÄá0ÂhLјrµD€Z­6:]yP­VG ¾>(ó&"+L¦4ñ*³Å’Žú4jÙØl…°Ù a2—,U*Ä€Z”’`¸<v–Õý £+J¥¾B@íWáo±ÌY®P4®± D6˜Í—Å“£ò€¹~Ž‘ K,– ÙÀºâÕjµ:juy`MdÝn‚ÃQþSþü™`3•ÁœUKn1,y%°ä—Ân4‚„kÁ²Ãß Ô÷s,CTjhÕàçß †NІûA®†*HÅÝ>N‡àà`h4Moø£·qí%áááˆ÷u3ùD‚Ù|eeÉ0ÏÂbÉhàV¬ÖlX­Ù()9P(´ÐhZ‰µV…BßÀí¸¦©&úÙa·—]GKWƒC½Œ‘å$By99Y°XÒa0\ ¤.y¼òØXØíŰۋ\¬÷º/^ÌCLL0”JwW™ v{ìö²j]•¿v5[…BWéG®¬âO݃o";Ìæ?a6_¸z•ùR­æÔÔtìX»„ÓŠuEåÃt r€l{±ö{ùïb;ìF™×c_GT”%ztþÑð ¨ÐùÅ b¤,ö-ÁÜ·@§NЩS'|úé§¾nJ£Ç´—T?‰Ð‡ÃŽò©òo×2A®~ ª‚B¡† ¨¡P¨ÑT¾št&úha4¦Àd: £1v»4‘É›3L¦s._I«ÕaWƒiçUêÈ'Ø’‰lWƒa#Ž21Øp8ª*“ŸiF¡Ð‰ÁtÅßîþ– ¸›saùûºüjÝn¬pÏ$)»öS^öí·Ç@äÀ­·Öß — ¨ Pèa·—ÀwW¯ëæÓOcîÜá ¬ŸHÈvu¼xÍÇŒ ‚BT ‚4Ð.¿"~íÿòÄèò!&Sˆ¬uÞ•+ñöÛµK8Ù‡¥|þd²F3ì%vØŠmp«q+ëú"J}y¬4(¡0(®ý­UA§kƒ¡ ôú.P©Ü'“6ç¾¥.8‰Ð3N"ô‚„„”•%áÝwgÀüV†¯ý® 1 ¾T«<– ‚ÊeyÅ2…B "@èjÛIü»üCöÚßrerËíöR)°X._-«;¥2€­Áƒp…B¦%´Ú(•þ¨xEC„«Çª¼¬âßrëÉ=F  Š«º€ô@Q¡ìÚÿ•—9àp]^çß•Ëìö2Õm¨L]”q¬ªó«õòUœ¯'ÇÕ׌óÄÒ!)“[O~ç1¯ø¸>_reKŸ_ùeD6™@Ø_ª‚ €F ®=ôúÐhZ_}¿[aµæÁj͖̓›-6›óÿBŸ¶™ÕŽRé‡Ã(ûYCVº›¯þ¶\ûí,#»÷ŸwA-@ ‚2@ ¥¿J¿òÀ¹â ¥Òz}g ]¡×wl’305&œDè_ö‡Ã«5Ç[[»z5°ùÏ=]þásõJCgh4-Vk.ÌæËâÅ’‰ÊYèuápX®Ž?¼Pou²rÎàÒfó”$ÊjK­Ž€^ß:]{ètídiA ¦…øžª¨üŠlA…€ºâïÔç{ÕžRpõĨ= Êôƒ½Ô sQ¬æ,˜M™åCØìÙ°+óÁ÷Ï›B£(’”bÀ¬ÐÉÛ§VG^ ˜»@§sšÁXCãš59J¥z}'èta0t’Ÿ\>gmüýû(O®2›¯Àl¾$Õå_Q3Öü©TAÐéœs{¨TuªïÚL áÐKÞ~X­òÁµÍVPoÉn‚ „B¡… h Ph%;VØí¥p8J¯þ.«·oº+¥Ò:];ñyV«Ã`¾lFÁ70ž30]]S JÄ@‰ò!É$ØAêØÕ¹°©sàÐäÁ¡É…CUj "…FqíªòÕ€Y¡½, ‚J¥sØ‹VÞ¢ÕÆÀ`èZåÐ ÆÐ^b±4ÄoòœèsFÓz}ùUf­6¦ÆãA ®-tº¶b™ÍVPé*uz£HÀªK¢Ose·;pùrÚ¶ õuS•üüò¡J!!—r…½¾½4—ß4Ã[P«Ë§ª“×å3s”ÔùbP UÃÿvþ_ÕÓr ÊD"#ìöRñÇ\Ûí%’rwcú…B®ôúöÐjÛA£‰—Y³¬ÈÚ›…²3׆³¥æ¤¢cxGÙºRB°„Aa ƒ]*,°Ã®Îƒ] »&M.ìê\8TE¨j@*mùi Të¡òƒ:ÄÚ0hàôwŽû¾ ‚JeùooÌGÍ ÊòøN„žqí%99|µ³²ª}ÊÞöâÐ •*¨Þ·¯RC¥ †Ÿ_ù¼ÜåSC¥_ ¦Ëƒêºßh¢æê%ѧ™)-µàÓOã…x&›Š~ý5 0jToètmÅaju‹Fzà A|ß5$¹;–'] P®NUYµòDÚŠAv©L2§Ü 5>\¡ÐB«m{õäHþy¶æYQ¸¿%¿•Hš±2q%ÞžøvÍ6JJ(-PZ"€ ÓÚAaƒM• R•5¤-Tzt- ÐDk i©&ZU`ã 78‰P'zÆI„^€’’y RÃ`è®3ôúöâ.fv{ñÕùV/U¸J]÷lø¦F¡Ð\®Ëp5±Ï…B¥²üA¸öwùÔuª« Š¥âo׿Ë*|µnlö_­×ŒpõŠœtz´Š358grP*ý¡ÕF¡©Ì¾ÓüÑÕù«ågQqήRu^N¡Ð@«m#ËÐh¢Ý^•µÛQðCJŽ—x-ÑO¡ûöÎ;¼©ªãŸ$MÒ½'”–M E–"""C¦¨(‚¸ë|ʼn‚'¯yE8PAEQAAªLAv)«-£»MWvrß?nÚ¦M:)móyžû$9÷Ü{ϽIN¾ùÝßý•=‚=жѢ‰Ò m£Eå×úÊ» †"¬›–W(Zm4!!ã3#¨3e¨ª¼®þX×z…BÂn· I$Éêx´¸´U¾®o[Õ}Õ$g(¨ÌþPÿç*•/^^]€á5¡¥P©üðöŽÇÛ;ó±Z,ÙNn9Žž’c½ü¨PT>w^ïl’3™Pe»Êý¸f‘(Ï`âœÙ¤1Èâ¬\ìz¹}^ýucþ̨T>õ´ôIN©ñª mç6ùKeçŒ$UnÚjî[þ>8?V¿êZçúþº_'ûíVÀU…²|ëº5ZõCQñ^6†ò´†6›ÿ:ó¨Ûô6Š’‹(Ù^‚di‚ê|JJG Ÿ¯JÎ~á«B髬x]þ\©Ú.B@7))™¼ûî: Ä Aƒšl¿*ÕÙOÕ#§Ý+üq'ˆ/ M4š6øùõoáÑT êÊô‚U…wå:©ÂªÙ…Mš…BQ!ÚÕjáû-¸°‘ÿzÕY¼Én²S¼¹˜âÍÅØMõ¿ƒ£m£Å#PÁÎâ¸ü¹ÊG%’Y\À$''“œœÌÞ½{éÙ³gK§U#t3Éøñã =÷‚B¡:+¢Ë}%BAý+*ª¼7ç³ÑRú¸'/ONy.Î+g“óyn‘¬ÅÛŠ)J.®¯¿pöŒñ$»}6½†õ:‹£;÷sKUâââ eÓ¦M-=”VÏ…e>lA CÅS ³páBJKEpeufÏžÝÒChu”úªRn-T弜[ìP²£„Sð×Âz‹gM¤†ð[‰¼;’yŸÏ;˃<÷sKUBCC‰‹‹Ã`0´ôPZ="ˆ°Îø@ h”î-¥hC–‚ú0{{84ŸáS/h0B·ÔÍùwK ‚óý!=ºßu˜³ÍõÞÆÃ߃€Áøõõ÷˜‚³ˆÐ@ ´"ŒÇ®/Ät²þÅ\TÞ*à{‰¯ÈŽ!4â[ÖL˜L­¿ªUssäȬ֦)ë{>‘’’ÒÒChuX­VŽ9ÒÒÃhuäååU *9Wçk‘•œå9d}–Uoñ¬Ð(¼2¶µÅ ­âYÌ-®ˆ¹Å=B³ÔÐÍDvvvK¡Õq^ú4"ˆÐèãDèžsmn‘ìÅ›‹9ýîiô‡ôuo(<øð'ú±h‡¢ÔÖýs.æWÄÜâ¡YêF6Â_ î02‘ÿc>æ¬úù9+” |zù8$á…)8;ÝR7âÛ'A3c7ÙÑ­×Q¼½¸~EEàïMа Ô¡ê³>>@P;B@7€²²2vîÜIPP -=@ œƒ”í/£`M¶[½ú{uò"px Ú6Ú³<2@P_„t=ÉÏÏ'..Ž÷Þ{3fOVVV½·ù®œ«>gèãŠôq"tOk[,…²—e“»2·^âY¥!21’ˆÛ#šD<‹¹Å1·¸Gh–ººžššÊòåËùñÇ;v,ï¾ûn½·ù®œk>Í…ôqEú¸Gº§µÍ-’M¢(¹ˆÓïÆp¸î o ‚ QADÝ…g{Ï&‡˜[\s‹{„f©DØHžzê)æÌ™Sg_áŒ/&Æ #ù«ó±äÔ¯Š wœ7Ác‚E€  Eº¥nÄ7´ìرƒ¯¾úŠ;v´ôP@Ð ±lèÖé(ÙYR¯ A‚ÇãÝÍûìN œ1ç½€6ìØ±ƒ;vššJçÎyâ‰'ÜöݰaK—.åäÉ“ôéÓ‡iÓ¦Z¥Ï˜ç€!(5«RP;999ìܹ³^}ûöí+tÊYä¼ÐIIIÜ|óÍDDD`4éÝ»·[½jÕ*&NœÈĉ¹êª«X²d IIIüõ×_rÆøñãùâ‹/èׯ_ƒÆ!ò]9räíÛ·ÇÃã¼ÿ6ˆ””âââZz­ «ÕJZZ;wné¡´*Ê«ÿÑ¿Ði©¹Å’o!ÿ§|ŒÇŒõê¯Ö2.M¤æ,LFÌ-®œksËßÿÍ7&£T^\k?»}ß|cdüøñ:ŽÉdB«Y_jã¼ÿ»;|øpNœ8AVV ¨±ßŒ3¸æškX¶lÓ§Ogýúõ¤§§³xñb@ÎÂ1tèP ÀîÝ»ùàƒøùçŸë=áïJk ôi-ˆ@WD {D¡{š{n‘lº?uœ~ÿt½Ä³ÒSIÈÕ!DÝÕlâÄÜâŽsqn±Ùa±L®u±ÙÑ1„f©›óÞôVgŸ;wrðàA¦L™RÑABBË–-ãé§ŸF¡PðØc Óé*,Óõ!&&¦#?ÿyûí·[z­´áJ`` ø¼¸¡±Ö¥óæü¬ÓA‚yõ ôIð!xt0*_ÕY™+bnqåLç–¢¢"¾ÿê+°Õí®Ó!>ž+‡mô±š¡Yêæ¼·@ׇãÇУG*í={ö¬XÌ3Ïî©S§¨J"P=¿w"Pµ_]ÇHth’Î;sýõ׋;[õà‚Jc7zôhŒF#6l¨Ò¾`Á}ôQ233‰ŒŒ¬hŸ7oO<ñÅÅÅøùù5ú¸‰"Œ@ œ7”ì*A÷››¾A‚*þý €R-lVç#ß,]Jé”)$Öà6d&õèÁ·{÷¢P(ÎèXß'%qÃM€f¨í ±ËjI~Tý€$¾ÿ¾ñw©„n©›óÞ…£>h4²š^¯¯Ò^VV€Z­>ãc””” ÓéðôôÄÓ³éãŸËˆ B÷ˆ@Wε@ŸæBºçlÍ-æ\3« 0¦×/HÐ3Ö“q!¨ÃÎü7¤)s‹+M1·L¸õV®{õUn;tÈ­¨úT«åæ§ŸÆ,I˜l6L’„ÉnÇh·c²Û+^»k+]¤·³ÿ°ÄæÝžp ÀI÷ƒ‘€ƒ¾>£ÑˆÑh¤¤¤äŒ ‡B¹mÚ´`ÿþýtìØ±¢}ïÞ½7‰àݶm3gÎdôèÑŒ=úŒ÷w>°páBfΜI```K¥U1{ölñ¯¿å>º*å·Y…/tUšzn‘¬º:Šÿ*F²Õ}ÓVå­"hD>½}ÎØâØ”œïs‹]’°W{”À¥­âQ’ÐétÌ?ŸÞx›$a•$l’„ \^[íöŠvçuF‹…m}»s_L[®’”ØT*,*&µƒFÃ_-—úy³?#£AçSP©©ò’žv{=6R [£ëç’ï† X³f Û¶mcøðáÛÉ‚pá@öîÔ©¯¿þ:O?ýtE{÷îÝ wéßP1Ûí,üøc$¨º8¾Ä.íõX'!W  Iª|^}Ók$©öuŽöê}ÏÎ{vNçT½¯ËújýšI’@¡¨úHåûÔ˜ugƒš>+Îí5}Fœ×Wì«ú¶Õö]þÅéyMm(U׃Û×åTL[Nïy•©ÌÑ^ÓôVÓ5®íTåóUm¿õ^ç<†ÚΡ¦ó*o«¶wÁ­p¨­o5ÑQýséî3êòžÔð9–jèS½ßÙ ¦ý×x­%°–X±X±Z1X°Z‘ÌŽóRÔ¼ ÏžøõöC¡U¸½n·«öŸÍkRý½-ÿMq^WëëÚÀýgÀݺ꟟†¬³X­¨<<Ü~–[ŠÕ¿®f—eaûíÜo¨\ö/PêJ ]û_Ž&:ÂÃÁ×ÕJl·ËB¹\4¸ËÃ#ë.<¨¹àNðÊdLe'y`ÖÝ̘1£Qç$\8êFX :0tèP¾ùæ{ì1<==Ùºu+©©©þðUç€^Ï;.Îÿ@ h1$°émØJmØJK©ÍÕʬu,µ òVáÕÕ @%XÊm¸rêô)ÖlXƒÁjÀ¦°¡Uh‰‰ŒaÔÐQ¨TÍŸÍÄ£ÑHÑî¹UkÇh€=ÀE@Ð8Üd0ÃTnèé aaXƒÃɶ‡q´8œÝ™a˜]…uHtí*/eeÁlüÆÌܨkS4»QæI—.]šðLÕ9ïô‰'¸ä’K(,,D’¤Š@Áäää ¿§… 2bÄzôèA||<6l`„ Lž<¹iƱcIÓ§7r$q#G6É>@PO$°•UËenÄrQ(hcµxÆxÖ~ CÐ(þHþƒi;)m[ZÅ´›[œKÚçiÜ~ãí-ã«k·ÃÞ½h’“¹UoGÕ×€ÙÿÔj®0𠀄•‚§sÌ©Ñ'Nàqâm¶À`À€¹„aô GFp\8þÂÀÇ€”%:Â9ˆžxÜût$i¼I#¾Q1III$%%‘œœÌ Ag–Kú|ç¼wá(++ãÇt»îꫯ®òÅËÉÉáçŸ&==þýû3jÔ(”Ê3šNLL䟂&¼óÎïë|¢ =Àèh”-lAhmä=Jh§N-=ŒV…ÝfCwò$Á±±-=”V…¾°ï  Ië¢ -¿À($ØJlXK¬ØËìHö¦ý¹óòÀ»«7J¯s#»Æ¹6·œ:}Šå¿/§¬]™û&h_О;'ßÙèc4xn1™`ÇغJJ*š-*Ø {‚-?SBO´;·Pv‰UþcU þyJ"•Îñ'Ì`#\Ê#Œ\´Ô£R±·7„…a $KR°ñð!n5PÙíhÍf<Íf<-–=Ê]Óg0räPÚ¶mÛˆ«7ß|3Z­V¸pÔÂyoöññ©·9<<¼Âï§©)uDË *ÙöÙg ™:Oÿ–J«"ùý÷?wnK£Ua.+cÛgŸ1úÅ[z(­ŠŒíÛÄ]-›œ%Üe&ùýÅ\zÝÐzŸˤR£Ä«³êðÖ‘]£¾œksËš k(kSƒxÐB&™œ:uªÑB±ÞsKQlÙ;w‚Ù\Ѭ×*Ù¬¶³ã"0j€lèÙ:všº(’;Bq¨bÿòo‡MƒgY':z]Ìð6m‰ÓèðÁc¿Ý]—ÃÈ+ÜŸœ$Á]¯rC¿|ûí°x IDAT;Ø$‰Ý¥¥$±©¨ˆä¢"²œ2{8£V(èéãE¸5‹Â¬ìÞ¶ ãÖ,9i4À~O•säè!»7Eìˆ`Ó[›\LeË–-lÙ²…/¿üRè:÷Λ‰îÝ»3uêÔ–†@ ´$»„ñ¨QvÑ8¤G²6Ξ£P)ðêê…o/_¼ºx]°fgÇŒ¡Û† DÕbÌ”$ Â’_~©×>OŸ>Ù!ô Kÿó³Ï²tÖ,n½î:¼¼¼Ðh4´iÓ¦^û,4ò̺gXüÏbÊ£mº…tã!o0eôzN<<›vÁöuà‘ú«ìWb&Ða·ÏÀÓÓëº]ÇuÝ®Ãd3±öÈZVXɇ~ ØTŒÑf„ #LjKÙê6X~É(•pÅðã"T£°Ç?} E+Do”ï’*0r$<¼ÆŽ•7)-õÀ¨:ˆBq1¬[_ãy*ºzà"ŸŸJ¡ ¯Ÿ}ýüx4ZθqÔ` Ù!¦7qH/Wf±H;Kõ€?Žƒ‘ãPô?‰dØ …{àØ¤)ŽóXûÄg€мŠHKKk°€.7òýûï¿ ÚîBDh@ 4+ÆFôûô”í/ÃVêþVv}жÑâÛÛïoTÞ¢ “3Ï/XÀƒ3ÃákëŽéQQ<¿`A½÷9bÄÝäçËwPKÙFÙ“rvˆôQc¸dè4|é@HÈöï_Sçþ>ßý9ÓN®>ðsW<ÇS—?…¾D8 Ò>`ìSï±jUZ®ív-×v»“ÍįGå‹]ËY9JƒÒÐËp…¬€>žZ }2¾º„¢Òg€ÄDxè!¨®G%Iâû>¬{@ƒ.'""¢ÆÕ¼¼èäåÅÏ\‹…¿œõ®’,åóÑ ûµ0öÒJ‹ùØ[ààÛÐÃHPqñññõºV‚Æ!t3a25þ–äùŠ"t"tEºç\ "4g™e}eXu ÷k.Gå§Â÷"_|zù  wŸ®KÌ-йsgÊúö%ó§Ÿ(÷èMÊg–L ¬oß}§ÔêH²³gzˆŸý‚¶_Ê"ÖPv`:X}‰ŒL¬u?ó2eõþLÿ³¢mLç1,»ŽAÐ—×Æ®ÅÅrŠd…¢rç×ZŠ-W°-*Ʊ¿ô’ì®röŒ¬,hÛVÔ#n„}ïdÑ|ûí•´]ðóóc´³%»‰S«ÊxÇw[o³±µ¤„ä¢"þ÷ÛϔŴ“3|¼üråFÃFÂÏ_B× ‚ÁD;¬ÛAÖ͹Qô<`ÿþý$%%‘’’ÒÒCi5,\¸ÒÒÒ–F«cöìÙ-=„VGii) .léa´:’““INNnéaÔˆ%Ï‚nƒŽS OqúƒÓ%5J<+<ø$øq[ÑG4"¨Fñ bn)gÚÛo3×) Îyf™Å´ÆåZàÊÿÂý£ª¶ß5îxž(=ëoï9Ÿ•9o½Åýwß}Ne êܹ3ú¾}9ýÓO´–8ÚOzµšÎ6@i©¬>ë°Öë¬VŽ\æjæÏØæoƒÞ/Uíл7¬Z“Ò8ªMÀ¯[h,ŒÐÝØø‘®ý‰S=‚Íÿ¸Üß®‚­’¿á¿¼k: %×À˜LxàøYð1Pþ9º€mÛ*ûöï_õuLÚüÆ»m.éw >šöåºrøh|×ÿÉe—^vFÇš¥n„€n&bbbZå@ 8³mûvú_rI½·±•ÚÐÐSº·TΠq¹<<ðé%gÑP‡¨ëìÿÊsϱáãiç`«‰¿ôz¶:Ô(¬ÓéxsÙ2N°àÕW¼}‹±gÓŠ‹™ ¼åÔ<˜–‘÷Ý'7xzÊøâ‹+—øxP*)µÙxûäIÞîÝËïS¦0ÖlÆ(LH€øxLwÝÍá3òö|ìÀ›íÚqm-½±˜Íf®9’­6 ÑÔìëí‚N¯¿ï¼#‹b†i“'óÆÚµü';›£¢˜¶q#tê‡Áöíò²cìÚ…Ùjeñ¸q¼vÛmd†„Tìº_j*ÞšÇw÷ÝWë ˽wøÉgÄz®b÷æ“>‡ƒŸ<§ùiùÑï?)Ž3$0[ÂÙF`½~$à_À5Ç/<”šÊqÇy« ^ž0ßü|P«)[²„Ó·áP¼¶î†ëÁcoi0èê1––aÓ¦MHF#½Þ}·Ö~F£‘M›61dÈFGÖP.ÍDvvvK¡Õ±páBQ‰Ð ³gÏ~ÐÕ("~ÐU) l*÷°isæ{çÏ7­\‰d•Ð֣ߧGŸZÿrÚîP(hÛkñíé‹w¼wƒüškcê¬YL[³†OÓÓXÌD–B{ ¢G"88¸Þû›”˜ÈÏ>ÃÙ¹<@Þƒ Ådz­kWî±ZÙ_RÂÍO=…¢z*´3Ä`00ý¥—ØÊãÏ?ÏÜÿþ·"¿r˜Lðî»ðÚkPP ·)pÓMðÚktîØÃÕWóÀÏ?×§OåѸ8y¹ýv¬’Äg§OóÊÑ£¤Ûí»NHKãåO>áúM›˜âëË•sþÇ> ;U³Øm %†€`tm2ÐeËAœñÆý¼Ñô ïÏòìl¾ÌÉá^]’ø½°ß yððaÆsKDãBBðr\S³ù$°¡ò šdðþtïRásRÆÖÑ—ðíž=Ýúûûó¡ÅBï‘#å*,[†eÄ,ýˆ,ÂX.Ë?@ rÝp€ªÕ [C† !åÏ?ëîx†dggsÖs.#*6‰‰‰dddðè£Vø AkbÛöíŒ[¼˜Ü[nA))èþá,ºx"m­m°íuï &àÙΟ¼»{£ò=;·”_¼óN&|ñ½ªý¤ÝååÅ›×_Optt¥¿€ó&;Ü:¹dffÒhO+àÍn ¤9çJNI­[ÁñGC­P¡Ñ¥Ñ¥ÕYþÜ©-J£!B£A]‡kÊÂE yóó7É(QcûC”SÿCŒŸ…iwLãáûvÝ@’`ùrxî9p¶„ sæÈ¾Ì¶oß΀ÁƒÙ²q#—8ÝY°K_åä03-ÃNÇ]½½™Ù¾=7ùù¡Ü³‡Â_·óóçчS¤ÛM•ÀyD Â6°ÑÖÝ^^^¼på L8µ²ª/û?%%|™“ÃW99œ®vwÖO¥âú°0&“ñýÏTT°–$f-]LþÀ˸Uo¥oÏ>üìÏáA•òu ðð`V‡<›‹rØ0(,”ß×%KàŽ;0™LÌ™ó>K­oW\ч#®¬»ãyFJJ )))¼óÎ;ÄÄÄcN- ´@ dßç«°Ú§Ix*G±tÑï\=~<]½¼ðh€_2€&JƒO‚> >xÈ?5ÿõÇŽÕ½±RÉØk®Áßß¿æ>lÚ$ç0ûí7¦îÙÃ’T‘i`7ÐÎ` øË/k?žsôZh(fƒžçì…¼ê@¦Ãú\A\|û­œ½Â׋$qÒdâ¤ÉTéSì¢VWÕÎbû›ÏñKÊRJà áêé P`Ÿ|i[góÜoÏqèÈ!Ìqòë×ÃSOÁΕm ð¿ÿÉeòª±hÅ ¤iÓX´bE…€þ.7—ÓÒØ_VVѯ½§'/¶oϨïù¿KµèRNª?€[¬øFB§à¾Ð&d;ñ7§å}˜§$âµéû(]AûùùÑÏÏ7:väÏ¢"¾ÌÎæ›Ü\tV+%6ŸgeñyVá=»2),Œ[""H_·ŽâaC1 ÆŸsç²çžÛÙᔪprx8ó:w&òØ15JÏᅫ`:­VË /ˆŠÀ‚¦AX ›ÄÄD@¤'­ ŒFv%ýË»ËÿBׯêúÝ»!:uXݼ¼èáãCçZÄ´:T]!šÕ¡®Âéë%KØðÀ\WKLˆ ˜æãÇQ:»GH’œ–Á!˜IN–]œx˜ôr¼Nôòâ­®] .,„¼<Ð׿8‡èš@®»J}Ò{ÉÚÄÇ‘–LfH%þ¡(¼Â@‚ÕɆýáÀj•Ý0^ªL§œñ AÃ.C£ÓèãÏó'öÉ'„­[Gxa!¡EEh"#å‚wÞéZ]ÙWþâûî#ý±Çˆ?ŸÿÍ™ÃÅÅüã$øÛhµ<˽QQU,å›7øqÏÀ®ðèaP‚ú¼ø=ÅH H ”…õÁPùñ@ ƒb- ò?ƒ‰1cÖIdÍŸ_õ@àñâ‹HW]…ÍÛ»ú0(²Ù(2ªø0W®,’¯ã 7ÀìÙðÌ3°r%ee<Õ³'¶m‹›TgK—Â]wÉz[¥‚ÇßùÏVd€ðt\ƒ¡pù·ò#@‰æ÷´‘$M¾æÏ—ÿÙlðÍ7òÒ·/<ú(Lž 5dyðS©¸#2’;"#É6™èóÚkd>ýte• ¯Ë/'áßñìØQ.Å=|x¥xž1£^âÄÜR"ˆ°n„º·BAs#Y%ŒÇ¢ù»Á5psq1¿®Z%[pkJ—f6³{7wÜ6‘½Ñ6V…—±B[ˆ¾Z2³ ® 㦰0†ÕèæQÝ †œr`)²Ugrõ""dÁ\.šÛ¶­ñ¼ ¸!.ŽÁ7ÜPÕú\Oô=QÃ:R\º Ô—Ѷ´Œ2z‚¤ôJ”ÿì%㛯i[XT.GÊʳ C}HoãÃæloû©9–8ëå—»ôóøë/&¼þ:‹ívNÞ<‰â'Ÿ!7,Œ\‹…³™\‹Åís£Ý}ÆAÇŽ•;xRS«ømå•”WFw`ž_Œ[ñ `½ür’ÛEq¥÷öÄ.eÌ®|îí{/·w¹eÍ?ãéÙÙ\^ZÊ)gñ б#þø#yyy.î?3fÀì¹&èóÊ+ÞÀ”ʇaÛ·‡/¡ëBÉÈÌÀ%¶œÅ³2SÉ`ÿÁ•â GøðCùŽÂGÉéö22d·”—_–Û'M’…oõ¼ã’ÄkLéO¸žœJEZ¯^|}ÅL>tHn»ÿ~!ž͆°@7⟜@ 8[ØÍv ‡¢ù°»¹î”s[Š‹Y[P ^V+BC Q;\0<@«FÝM¦£¦ÂÌD@@€Ë¾Š­VVåç³"'‡_ 1Û«?\£aBh(7…‡sE@ÊÌLì3fpãçŸó€JŤn ó_15+ ±c™ŽGª. űcÜsð ‹ßx£bhwO)æó€yà—YÑ7Ì;ŒG.}„‡û?ŒÖ®åÞ©÷ò[ÆoäµÏƒ BÓB3‚Þþï\M*ôý÷²{‡Ã©‚Ë.“…ô„ –”×µ+·Ý†µ†ÒáØløÜ}7Ó32˜yûírºº&ÎË}¡"tKÝÝ $&&²qãFÌøñ㛬èÁ¹Ž"t"tEú¸¢?¬'cC†4Aê º7p°µ¸˜5ŽbÞ*‰‘‘Døhñêâ…wwo¼ºx5º* Îj%)/¯srX_XXQN¹œ(³™×®eÒo¿qbß>žkÓ†ãLà²eôøë/6>\5óF#ièܲèŸE<üóÃXìP¡ì¿»‡ŽúÂ}ý@Rp.Ò`]ý‡"=û46S³ž¶Êu;=Àì©ÂOHÛ0?®˜Ö“/÷~I±©¸Ê>®Œ½’{ûÞËÝoÄÓóJæ 22ªdÈ?Ÿ‹£WÚñÂÛ¤ú¿žEëÛ¶gÚeÓ¸§ï=xyT-æ²wï^’Ö&±c÷.îu1ãG§gÏž ;ñ;e!ýõ×U3ª´m‹4e ÁŸ~ŠnÊÙ »T¿ýÆûÜ—œ\k¿šsKU’’’HJJªÐ,B@׌ÐÍ@y!•ßÿ½¥‡Òª˜:uª"tCbb¢˜´ª¡Óé˜9s¦ôì&;¿Púo)¿¦ü Àȸ‘õÚÖYòòⵚÛkH{xØÄÓK—2tøðFZÌ-î6l˜(¤RB@7âVˆ@ h ŒiFò’ò°êêP«Î¶’’Jó9ÑŠ;«Y:¦=|ÏÂ(ر¦N%oß>¾<˜C‡ògïÞØ x幈#CÐ÷ß³büx®:ôìŽÉAž>‰+'²!mÑþѼyýwÜœ¦ÇŽ[ýᙾuìe*•EÃeïèhèÔÉuéØjÒó½{'²{÷Ç+ñtg©\DWrIÿzõJäß—TÙ6%/…v~Äç»?'WŸ[¹b³'ªã>Ø:Ù¨‘#@G#\&‹çÝ,¾ó&öv-ÄÒ,˜Í°b…l•Þ±€g›w¶í»<=yãÈBk *4¡[êFÜ;‚VŽd•(ü½âÍÅÐ@“‡Ò[Ɇp#ϪóÉŒ‚`­šß{÷¦§ÏÙ,È…,fÌ€/¾I"øÏæÍügìX² àµÕ«y¿{w¬NiÌ ÇŽeÆüù Ð)))¤9—±®…«®º ödïắ®#M'o×+x C²¿ã®Ÿ²±÷(»ï·©sJ%8»|Ûí²—DFüñ‡kÿà`÷âºjiiéLã:'iPcÆl™¸Ð8掜ˬá³XuhíüˆuÇÖa7±4Bm)Â;{C×2ˆ§ùí“xzÖyÚgn»M^þþÞy‡'¾ý–'­V>­Öu/5y²Ï‚Ch@ hŘ³Íä}—‡9Û\ïmT¾*¼ã½ñéîÃÇy<|T®Ì¢V³¾W¯³'ž xóM9ßpyyhµy^|ˆ6/[†uÊ”ªÛjµŒeÝ4HD¿ðÂ$%]„BQKàð9‡à÷Ìß¹ãû;(³Èãó?r»—¿Çî¾¥0Gvoau¤÷©óØQQ°q£\åèQ×%3S. XNA¼lß^û~õÜÃV¢çž:ÇPŽF¥ab÷‰Lì>‘ô¢tº,ëåʲÚ7 R‚¹«ã*/n” ñÙcà@8Г'‰7޽»wW±B¿Õ®o8 ‚–@èfÂTKéÚ DèDèÊè#AÑßEè~×!ÙÜ› õ…y¡òWáÓÝïxo…Õ­Ïåhµ¤ÄÆòÃúõ\Û¨ °P$ƒê9PNó8 ;hJ9=h5KÒŽ‚0Ê•(Ò†qÙ@ˆ~ “íd±ùR—Xþ‘Ì@2Pûõ2›OÖºÞËKNƒÜ£‡ë:›M®>}ô($&ÂI—]¹F# °j•ûcùøTÖ’ÉJóªžB2àœª|Ô~µžCk 44”¨‘#ÙûñÇô”$ÞŠ‰á×_o’}_hsK}•ëFèfB|]–{„xv%00ð‚ù¼”ì*¡pMa­EPPÀ„;&4"È%ÏC¯, IDATƒÆ§OóPj*²x^ß«½›Z<Ÿ:%û9/]Zé§*ÞwŸ[€uüAªŸÖ¸[ÛÅsç+¯PØP­þBŸ‡ËNC”í‚ýoÀÑîp]RŒla&'~©«¸ý±ŽÜx#DÚè¼5 ÌãéÉãÑÑìšv Æ@6ÙÖŒJ5®öœÇµn+‹Ýr뱫€vÅß_ÇiiPRRu]YYeÍ” °Îã³*;UÏ º.’pŸ„F¿¹yâõ×yò·ßx"#ƒ¨#šÄú ÖÜÒ„f©!  (Ox¤¨¡¢ž­ÌFþùèSôn×;£òS:>¯N^.ë><}šÄóº¦Ïƒì–ñ¿ÿÕèç\:7·k{ö¸¬“€Ÿóó9i2¡kßžYðl÷îõ“æ[èó$Œ.”-Ì »*ÄæAáFXDCõÕ|÷äWtm_y=ž?žA¶Yö/ŸÕ¡žJ%—]ÖŸË.sã“ÑÂtèÿþ+?/,”…tZ¤§W}LK.O‚íŸÀ%¥®;Ûî ‡'áÕ¹Èu]+$44”¨#xì‡XÑDÖgàLZ š/¿ù€['NtY§?¤'ÿ‡|leµ¤sàÓÇq!(½”p˜»;„æ¢Ó§™âÏAüvÑEô©E<ïÚµ‹U‹WM%ᥒëî»>))²Ÿó‰•ëÜø9×Ĥë¯gÒõ²oqÒê$¹˜Ì‚L<=<éÛ£/¼ú*ÃŽá„ÉÄK¹¹ .*bP-‚ °Ðá¯VÏΗ€&)’M~AP`åõ8i2ñ–ã\.öóã–ˆˆ:Ï¡µ$/}jˆuìÙöí›ëÚÃîùÐ3¢Ê Óö†Bîc`zHlÆQŸO¼þ:FF6™õY 8„€n&D¡+"ˆÐ="ˆÐ•s=ÐG’$^wDjÝrãVhÉ,‘¿&ŸÒn,„ÕPz* ŒïE•ð¡—^à•+Yœ™ÉÎâ¹W/úúÕîßËÎU«xçôéZû=Ê£;vTMѳ'Ì› tµ0™LL¼{"É%É^TÝäö-Ù[øeü/L}q3¼|1ÛíLÚ¿Ÿ]_LDµê$6üø#¼÷ü¾!n8YU< Ç–{¶´é7+Šj ûÙcÇ08þ<¼Õ¹³[ýÝX,9„…Í®W¿úRáAczNþNîõÏ` ôçzÒ’„††òÜ+¯4é>Ïõ¹ål!‚ëF(—fbÆ $&&ŠRÞNˆ B÷œëA„:ŽEŸ|ÂSO<Ñdû<×}–­\Izß¾Ïo›4 c†‘¼ïó°Ö]ųƒ'ÇÛçÙGn¯˜´uz=åa{}A¡@c4ò–¿?ýêÏÁÁÁôºöZ -¢O Vè] ½òòÎsø‡…ÁË/×èç\=õ¿xÿ‚µkÕó–"$Ž8Æ;¯=ÈÌ~æÙœ2Ífn:p€õ½za±™Øy,E+Òøac…R¦Aß Ð¶Ú5Ü œrÛ½M˜Í•©ÿ))a©#¸ûúÐP®¨ÃÒ}6ùé§÷0kðôœÜÈ#xƒÀò‘ü(¨à\Ÿ[ššòRÞ6l`Ô¨Q-=œV¨DØ ˆŠ>‚ ‰Gžž¿ü¡õëÅŸ#dësϱcÙ?}: o¼ÉŸÓ—Ròw ’½öéWá¡ px þü1›ÍÜÙ³'_92kŒ ç·?”;.ZO=E`i)—ÌŸÏ«W×ÛzTPPÀãýúñY ÅHîæÁìçü µú9ׯܹïòÂ×OaW‹Ÿw)¬ !zÞ|ö«ä"^§~Äpxžœ~®:[h ŽzKÉÝu¼â39äßùS§C­P°¿ºx¹ú“ŸËT­nX3îª B·Ô°@ ‚&C§Ó±z÷nòæÎeÁ«¯¶ôZœe+Wr¬OP©ÔA'Å8Þžû+Á;£T(PJ…•ã±üµ5L…nŒRhêS%x(HO?Í´~ ¤ €m•µ¡ð;xWß{馛të5X£!fà@ved¸X¡w1@püœk½+¾Á˜PG¤/)ò)úûèûøtÄÐöÐm‡¼MÝ<ÐÒ!8–üÀb Žå¹Z¡±úÊL%Iyyü©Óð`Û¶çxˆÀË+±Î~íÛ·œå] 8—Z 4/ÌKƸqØ»ucõêÕ¼¢Ó]°VèÓ&Ÿge1ó½÷0=ÿ<Ý÷KôÛ©@ω 8év;Iû`W°çC±ÓÊNàñÇá7à§*u7ÝDüO°Q¥âóiÓ 7rr ;Ûý£ós½žÇÇϪåm†y_-Wï8Còôyœ²†àztöL6ؼ®ü/¨5Ðå%úfärÿv\3¨Q~Q(PðÏÀ]Üúà­*95x­l àÑ)âããƒE’xúØ1@ö16öŒÏ­5²|ùü–‚@p^#t3!‚]A„î9WƒË­ÏvGñŒŒqãšÌ }®ú˜ìv~ÈÏçÓÌL~-,D¹~#±GÒm­a¹ŽN Švíð:q°KvÀ.IûAò 8.a¯É».=]NEŽdéÒ…ÉÉhýüä(»ŒliÞ”'uØ¥Ps×]7R<[íV¶œÜÂÚ£kYsd ;3wb±C>à_ÇÆ9ðú°‡~0œGöF…åÆNÜÞ·/ÞN¾×ýúõá¤õ ›8ŒÃ=c‹´UJ±#‚;‡ÜÉËŽ€ËwNž$U/[ÁŸ%X­nÔù‹œ«sËÙä\™[šDX7B¹4¢¡+"ˆÐ=çb¡]’¸ù•WH7®²­[7>üö[|öìáÎΉodÁ hý>ÿ””°$+‹/sr(4[ˆÌ‚G ö 3ƒ‡AnÕþR§Nøüý7^qEE›o_‚G£ÔVºX%©Êb‘$&|ø!›nºIî°w¯ü8hS¦°åßA_ƒ‹„J%:‰ˆ€ððª<îåÅãO<ÁgŽŒoÇÆ2oÖ¬]‡ô¢tÖYËÚ£kYl=E¦j9†c¿Сßï" 8˜áCUL cdž2#-†9ì-+ãÔT>¯²ITTÿüúÎx”Í7sâð BbCó cγs2x:«•—ÓÓèèåÅÃmëpœ>Ï8ç–³MkŸ[Z Q‰°nDa3 œñç+6IbyN¯ìÛGêìÙðÜsU;¤¤ÀÖ­pç\äëËMaaL §óyàsšk±°4;›O33Ù[V†1t>ŽBˆAITf&'KK±têäv{ͱc\AŸþrMÞquÿÁ8pàÃ_,g÷ '"ß~›õr^èêB9$”J·Û,øpïÏz…/NÈjÿövaLyöùÏ#5nc°ø3íOÖYÃÚ£kIÉKqé£@A¿6ýÕi¿/Jeóê@¸ösèææ®œ ø¸=a–¤¦Î©øsm“$®Ú½› ¿å÷»vå6mjWii)ÞÞÞ(«ïô£GyÓ‘÷yeÜVã>‚ ¡[êFX Aƒ±HŸee1;#ƒ£ƒ\ÎyÂ׎qqðí·PZÊ`Oi)Ï?N???n gRX±žž®ÛUÃl6óËš5\wíµM2 À*Iü”ŸÏ§YYüœŸÂ$Ñá8\}Âótôô¤¯/f–¬ýËU5çH6wèÀgÉ_‘03¾^â™ÿ³wÞÑQUk~f2éR)iz ½‡Ž‰R¥÷x¹¢^®ˆÊ§"A@AA¤¥Šô @€ $ÒH%½Oùþ8é™$È$žg­³`öÙ³Ï>3“=¿y÷[€Yÿ÷DM™Rêù(OOfíÛÇ©O>Ñøž”J%ƒGæºì:)R˜›Øãá„X¾:ó‡NâôþÓù¹«ïÇÞçdà–q1ô"™ò’é×lmÜx0Ü1‰ĵsÖY wïÎæÃ‘ > ½c¿e&…cM îwôêí/2žŽD®–-éàëKdVŸÑÑԔΥ¤ê3QS<æIF¿DDÐÃÜ\Ï"""•BÐ"""“©T²áùs–††–çןšŠNDŠR|+¥£GÓåôiÒ&Oæ^négß”|SR˜L73³|1]¿Ÿ»ë׳ò·ßðpwG¯XQ—E¥RqóæM:wî\nß{iil‰ŠbGt4±YÙ4ˆ€žA`ÖR]\MLh×г\ßÜ¿}}IKÌ„c—ÕŽ'—(¹nü„Gi—q=ÐÿkõåÎáà¢E\¿q³\á—)‘ ËÝ@Ì”è ɵ¶ž»t…;w2iÒ$^‡o–|Ã%ÓKd6„ðƒ‡™›'ƒOž^b ]©.Ýí»ãîâNkwž]såä& ÿ9_Pí»¤n‡›Þðð7Ð … dºAæg9‹÷—¸†­ž{Z¶¤ŸŸÙJ%£ïßçVÇŽXjèÃ<ïɲóЦ”²+ """¢)¢€~MˆA„%© A„‡fX[Eµ1Ð'M¡`md$?†…U¨…£·ñ·:ës.Êf͈:z”ÛMš©«ËîØXvÇĘë«{59™«ÉÉÌ ¢·…㬭mmM®PÎÎÎæ÷#Gˆ÷ð`Åúõ|þÑGUrO;öîåÿ¾ÿž§·oç[W ó"'‡?cbØ…oJ uÀ%XB¿`0Ï’ÒÊØWÔˆ~HéÍ(Ðÿd‚ûA”R‡K™#H‰íxѦ“=q‚N r"+ ~ûmLeÓ±G(¬Ìn OHÚ)R§N⇠?‘3­ÀŠWÌhžå’ÅÞ½{Á˜‚Ê~€“…o4~ƒ¾ Ý‘>Àß§MÙø?.yèÜ’“!·ú8(û@Jæ™GOss–5n̬  B33™øð!ÇÛ´AZìý+¾¶\NJbo¬àš2ÞÆ†®fåE1ÖN´qm©nÄ BõˆA„åSs•K5pñâE¼½½‰ˆˆà‡~À¬‹°DX’šDÈÄÿß‹iÖ¬Y•ûùçŸsèС*¯2$ÉåüÁÊðpâsròÛ›ò…£#C h¶w/ zô€³gK'56–V¬`ñ‚,06f“~©©ìމawL O33Q'&òwb"3ƒ‚èkaÁ8Â÷ì!¬GrîÝcÃÝ»|:cF¥­Ð*•Š%›7çáÁ޽{™2v, øÚžJH`KT‡ââ¤+iô†ƒe<8èãjbBKctÕˆîèž‹»¤·‰!¸a0AvA$I’àèFx:Ò5¨ ·i¼÷ör9õ‘òkXÇûDÑ3·SïBOˆ¢NÍ^‡S¿X5Auîé @7N—=ò†Ë8ÉÝ ¼ÔŒ?ÁæK N¯7ho¼!ƒA:0zt!ý’|Ò°!W’“Ùé/Xðì œœŠô)¾¶ÌÎUõúR)‹5ªÜj0baIÄ BõˆA„å#V€E‹¡««Ë÷ßO@@v¥äq-ŽèŒ_;ñ˜:•½{ã~ñ"Ç·m«’1cbbhÚ¶-îÞ¬˜ÕD|N+ÃÃù%"‚$yAŠVÆÆ|éàÀ8t$T*•Æ»+:::è–²Ý~=9™]11ì%¼ðxr9’E‹På¦ “]¿Ž§DÂ{ï¿™Žæ2f2†eÇ©cûž=|p÷.iýûÓrÙ2öïÛÇÖèh¶EG•‘…}˜Ø êHe¸š˜àjb‚…†»%ìÜ÷ŒÐf‡x‡çuž£*^Eï\¸âÉÏËÝxóÍPäÉ€o¿… PèëóvÖl³ ¾€:G¡Nª0V‚ $ ÌÅÀ„¥¤ŸKÊJârØe|B}øcï„<)È]W7uyÛð ÌŒpò$<^²‹¾>ôî-fwwhݺdŸI“fqáBIÙ–­ìlo=:_êëT…‚.¾¾×ÁþÁZDDSDÝR>¢€~ ìììðóóô?˜ÀÀ@úýï<Ÿ>z7r~þü*±BOûôS¶[X0%1‘­+VTÁL+FTv6?†…±62’´B¹„Û›˜0ßÑ‘‘ÖÖh`w}iTÀ¥¤$vÅİ/6–è}û„ ݺtZ°@ÈöQHÌêJ$˜ÉdEDµ¹ŽŽÚ6S¾š:•°yó@*EzêJ}}¬ZõÁ%X‚óS0΂ÆÆ´71ÁYƒ ÇÂè;ès3á&£~ø9ÿ §ÔL¬«Ñk€‘ENɳ^òï¨6/µfjÿr,¾1X,…7îB‹bÁ{ àd[Hœ‹§§]»ŽÀØ2õÂy¢¸H`†þÉ>§ø£TåVL¼rnj%[d‚þm8¶Š-þ-Ù’¼|Àâ™úë+u Ñbã!#A½ë@ð¼`Š©)ôï/¼o¼ÎοDbXE) [±±Y3Æ=xÀ‹œF߿ϥöíÑ—JyñâuëÖåçðpB2…ß:9‰âYDD¤ÊW“b¤¥¥N“&MJä­ baIª:ˆP¡P0eæLæÏ™ÃŒ©S+=^–RIPF32x”ž.ü›‘ÁÀb³³…¼º––\V(hºoØÛ '•b–kͳ„š¶†ª±”®Y°€°áÃ!4”°áÃùlñâ*±B'&&ÒµOïÜ)±%œ‘Á’ÐP¶FE‘Sh3ª¯…ó ©3í+`Õï¿; 7¢M¡€¨(Á±¶kWê/^Ì¢Ù³I—HHV(H’ËIV(H.ãßL¥T*8w§ißÐä4Ô‰JŠ´~Kú''Ó³m[ç'Ñ“`ÜÒWôõ‹XK£¢¢ ~‰ô%©—.[ ñìRáØNhŸë"q»1ÿãŒTo c^~Ù»¨xΫ™’gùm” þ¿¡øoÈlc„#¼dŸ¢àÏ…09Š»–g{@Âgüï0w.hSá¾±66\INfex8¾))|üø1ë›5Ã}Ô(þ÷ý÷|Ÿ»‹ÒÌȈ÷ÊÈýOA ",‰D¨1ˆ°|j½€¾{÷.‹-âöíÛáææÆ… JôËÎÎfúôéüùçŸ( ,,,X¿~=cÆŒ©’yˆA„%©ê Â;w?b+vîdú¤IY¡å*!™™Eòãôted–•¥¾œòŽðÎ;EÛÆŽ…Í›á¿ÿ [©$N©$N‹(@b¢P¢yÌX²æÍã}ûˆôö¦Uƒ454¤©‘M ±70¨Åüåˉ5ŠùË—³:·¬öÃôt¾öŒ?cbPºÇ7êÖåÿéin^+T=ÙÙÙlo¡R)wîÌ·B«€°ÌÌùqFO22k`«§GÃèhšš’^Ü¥ÁÒ ss>ÑÑÁÐÑ1ßê©Î2šTØ"šÇîÝ‚˜7ù˜1œY·Ž3ï½WäRR).…u“Bÿ·)–"11‘£wî œ3‡£?þȸˆV%&r ..ÿÇfeÅ|GG:•R˜âu³býzžõï_Ð`b’/ž²;ubãÒ¥ÊÈ!6nÞEçY%OJ$$5hÀÝû÷iÛªU‰Óº–º·3Ƥ 2óÒ—Í‹aÒ$ ³}Sú*«ðÛG_ã¦+¡ñ;³ÑMÄëµ!í9<Ý•™ñĦÅ›Kس0² Šíd©3&Êt™e÷!+æ–¿{áå%üDÙ ^Ü‚“WÀàŒp2s ÐÁ,]2÷³¶ “HØ“[d%:;›…?ý„òljڿ?¦_çÎ }M.HÚŽ(žKbaa!Šg5ˆš¥|j½€öððÀÃCˆ¹zõ*™™%«f¥¤¤°uëV>úè#z÷ÒA-\¸7²fÍš|ýõ×_søðaâãã8p ­Zµb÷îݯïfDJeã΄öè énn|¹p!Ç]] ÊÊ"(#£¨X-ƒ:2MŒhRH˜æýßL&ÃcêT|GVûÜÄ·ßæÚï¿kœ‘#G¥"Y.çiTC³³‰*¾`98 —–†,9™ôB)3•JüÓÒð/Y¡s™¬ˆ ¾´ja¹¾Â¡Ã†á6>L›€T"aŒµ5_9:ÒÆØX£9¿²³³ùù×_1<ãðÒ…[´\Κ øô?ÿÑhÜí{öÐÐØƒ,õ–ÈlGG|®\ÉÐR)Æ­r]4ìËÞÊT(à»ï`Ñ"áÿØ=…þ p¨þ9’¿LÐKmOû›A4_µ òܼ-¢ë—_ÒµXÿ;w2yá'Ð>¾ì 2£Ó„Ne÷Q‹Ð2+–›Y¨¯¯Ï®–-°oJ;;áרQ°z5?N˜PÝÓ©…T“o‘˜˜ÈÞ½{ùæ›o5j½{÷æÝwßåçŸæâÅ‹¯äš×®]#55•ÖÅò.µmÛ–³…rÛΛ7 .‹6lÐøáááøùùPäHOO/ÒOl«x[¾õÙÍ BC!;›ø^½ðÚ³ÿ´42CB Ä `¢£C‹¸8FåÈmmÞœË:pÅÊŠð¸Ú¡Û[´àk''\_¼ E®r`` wT*aÿ»¸O{h(˜˜pG¥"00P£ûЕH°ÔÕåóçõæ›%ÇËÊ"{ÌFŸ?Ox÷îl16f•½=sìíjiI3##daaEæ’$—sóþ}þ å[Nß¿²I Eåà‘‘ÈÒÒ˜jgÇQss699ÏÕý^‚°›pì?ØÔ·/ÇÆçœ§gþQ¸Í{éRFxxht””VÍý ³D}ˆˆÈ?’Ý@úTxI¼TÂß?1bŒýí±j‰¾½~™×xö úô´B€¾ËY <¿›,0Ã? á!ð"÷x uwÕųÓÎŽ·¦éòå•E€LFú† ðå—jïÃÚÚÃd (î—Û– †ÉB_M_{ˆ À™:¿g‰¶ˆˆˆjÿl”Õf…ÃáÃ%åMLÐ71!ýî]­˜ŸØ&¶is[ž&9vì~~~$%%!R6Z# cccùâ‹/pttdüøñlÛ¶¤¤$êÕ«‡ŸŸß~û-nnn´oßž½{÷¢ÔТ¨ ‘‘‘´lÙ²H{›6mˆŽŽÎ¿–‘‘ù‡i¶»gèàíȯï¥ÉÅÓ´úû0õ}Vºœ³‰ÇȰÉ@"“”:^^Û–-q´k—. mu\–ˆ¡d&¤àø̽iF÷)ï1bæozÌüM9^Ƹ†ea¾}mÞÌ• ÌÌðš6¸AƒJ½Áƒóôö},ÎY¾Ó hÜt½ Ž—yzû>ƒÖèu©[·.¶¶ëhÝz:xæõëO*ÒÖºõîÞ½[ퟲÚÖüú+)&&‚õ9" ²LL˜³d‰VÌOÚ´f.ÚÒ&—Ë ÒŠ¹Tg[ž&Yµj»víâÉ“'ˆ”Vä>sæ ǧGÌž=›¨õkŒŠŠbÓ¦M¬Y³KKKî³,”‡»»;™™™%‚W¬XÁìÙ³‰§n¡dükÖ¬á'¦è IDAT£>âŋԩD6OOOBCC9wîÜKQ™5kV¥ƒ ­ß|“€yó]#ooV8;WIFä…ŠŠ”†L&£W/ *ÌS>ù„FFùÙ;ؾ¦Ê·Æäôt¶ÿü³ÆsMLLÄuÚ4ž}úi‰sN+Vp{ëÖU211‘o¿ýö¥|Ur‘ë#¹vø"FgÏÒ¼Ø{¨O…eºR]vaç͇”ö*'³€-õëÃñã a§OŸ2nÆ8ž?#6EPÒÖ¦Ö8¦9²{ýnœ+š[®–Ðgüxþžnغ&&wþòå„ ªö\èСE2rÔ*è“p&œ˜\»taßÍ›4/ð!– ÇŠ¿‘ ÄËÀ>(W<ߺ&À£GÂc++·t=kÃ>@©Tb¤kÄþ±û±}ÓšŸ=<ø&6Ví8?Ÿ4j.ýUÎÎÎ\;uGásÍ€^]{Ñ´iÓ2 Ôf|oÝâ¡™™ ž¡HÀiì°aÌ^ºï]»ªivÚƒ(†J"Åßß… i‰¨G+tÛ ä^ÐÕÕel^Æ‚* ~n~Ðû÷ïÓ±cÇüö{÷îaggW%_JzzzXXX`PÁªf"¥S8ó†Z$B{ô(’‘CÛøwn@_U’˜˜È_þþ(ª^Sº¸ð×Ñ£ü/1±FY¡_†Œ  ’¯% •JqéÔ‰€BVèºÜ@—$TÀv[[võë ?È R)*$lÜ$aé2 Ù9н§”öï¯e•ß÷ئúflº®æÁBÂÏ..$ÆÆ–°B'OÍÍiïë /ñH$š5kV%Õ/k³—.%vòdõ'MLxhf†ï­[¢ZD¤ °°°Ð8»Ñ?­ÐÅIJJÂØØ8¿ÀFRRû÷ï§nݺŒÈ­þV•tëÖ sssîÝ»—ߦR©¸{÷.ǯ’k4hÐww÷*KD`ÓðD_¿ÀU éR)ß­XÁ¿'O®ÒÂ8ÚÌWK—ò¬S'¡ðH)<ëÔ‰¯–.eÍ÷߿ƙ½^”éJâ¼â„<†¹¸6jľ‹i.—cD¦Ÿ‡gÏÐuu-u< ðïÜ#ŸKÂñy~C2|_°f|¬¾-6ÖJ}}>9~ü¥Ä³HQJXŸÕ Z¡ED4ÃÅÅv‰+å¢u:,, '''BBB°··'55gggÒÓÓÉÊÊbþüù,\¸PãñÒÓÓ9uê leggãååÀÀ111ÁÈȈ3fð믿æ*.[¶Œ„„þ£aЬòˆŠŠÂÏÏ;;;ìììªdÌšNe+¶kÖŒÝÿú^qqlÍ‹+\\p*nåoÓ†ÔÔTÌ ¥‚Óf*[-¬ž¥%3â㡜zZš7%%¥Hövbbb¨W¯^‘ö йsgµãÄŽCñ"ž<   B𔄠ð #üðRÛWñuÑA@'yQ ÀÓ®]iß½{¥ÇÏ ²²²ªôX5•›wîÐ:9Ö­ËoËHMÅÀظÈLFâ?`×¥,ÄJ„%+%***ÿµJÙh€¾uë-Z´À>×'pëÖ­èëëÄÉ“'ùæ›o*$ ãââðôô,Ò–÷øÆ4iҀŋ“™™ÉäÉ“INNÆÉɉÇãZ†Eª"}Xù÷ßÌrsc«·÷K¿6""""¯Q·”ÖY »téÂÇŒB¡ 44”7n°b…P’VGG…BAj®%çU •JE«E bwLLþÿÇÙØTãLDª’±žžŒûñGÆ>x 6YýF5k†ÔÍ ®^Í-ÿ*¤Äñ– ž--¡IpqAioÀ#”f*Rd÷‘YX³Æ¢òø8¬;wc×®ýùc6cÅŠöäi÷ÎáÏ?Áªaƒ·Á'T°úövèÍ‘‰G0×7/÷~fýô+r+¢V•õYDDDD¤úÐ: 4@£F044$''sssnܸÀíÛ·éÚµ+‰‰‰Uó,5ÇÓÓzõêň#þÑÛ­UMÛ7¸—–F[îtz™òÅ"ÚJiVh%0ØM±JP&&$´}‡¤:nàâR$@/33“•¿dOz{òºã…)ûxÎ l iÀE`% Ì BBv oìx¿(Á$íáâÁþqû1”j|?Óú%²E볈ˆˆ¶’WP%O³ˆèÒÑ: 4ByíÚµ$$$ðþûïç·ß¸qƒéÓ§×(ñœGçÎÅb1*Dø0={iiŒ³.{ ½&!úŒõð`œ¹9cÓÓQ"T°vö£ÈÏmÛ‚»;¸»“iß™¤E³n&Ð8‡„ô‚L8© •á€G¡^‰ÀEêÕêÙ ¡I¡ Ú>ˆGñBâçq­Æ±ýííèJËÎ]œWayƒÕSÙµ¥¶"®-%ƒ‹’gä›0aBuOEëÑʼ^æææÌ›7~ø¡HU­3f°víÚjœÙË]ÝSÐ:V¯^])wœÚê¾±¤XéáQQ‚³qß¾H6dÔóçìRÕÖçýffŒÝ°A(×|çüðÊî}ˆ;’XªxÖsÒã±Þ‹b­ºÏõê L €Àø@zmê•/žßëøŒú£Ââ }ÇŽ´/”g¾*ðññÉ$) ²kKmå½¶”B^¡HQDÍR>Zñó<%%SSÓ ='99¹Æ¤%ppp¨î)h•zÞ•+ ;™šÒØPó­tm§&íT$%%qp×®|ä²pnÑ‚>ýú•< û÷ÃÞ½BNïBY3Æ£%ƪT¬¶èH¡_/”Ó¦!-d]Œ?|È/¿üÂÞ½{‰-¥D®6rïÞ=–,YB¯^½^IIèwRS Ìõ­MÖ皆™™›–,áë2“}˜nØPÐûö Ç¥KEK—4h€¯‹ÿKà´]<[΀g,««ä‰ù9:èȯ Ô©S‡Ô;©¤ù§•zmË¡–d›dWè¾þ~ö7CÿJr–PÅpÉÀ%|Þóórž%"""R³ÉÛÕºwïmÚ´©îéh5Z! 'Mš„D"aÑ¢E|ùå—¸¹¹áââBãÆ©S§ÁÁÁK€±µÈÿ¹¦!‘H˜¹p!á|€g)[ær`UãÆ¼Ý»7¬X!ˆæ+WJŠf{{5 ƌឩ Cg»ó|x<¨àÇ› L„Ù(“»w™üádýzˆÇŠ»f`ÚÁ£Fd'j. £Ìžâ¾Ã yR‰”_ßú•÷:¾§ñóEDDDj*Í›7ÇÊÊŠk¹éaEJG+´ŽŽS¦LaÒ¤IìÞ½›}ûöqàÀ‚ƒƒQ(˜ššÒªU+:vìÈ–-[jd„Aœ÷«¤2>{rwº››ãP¼ò` §¦úŒš4‰QK–0ùþ}µ Ê}}&fe!qr*)šaôh3ºtÜÊq íÅó®Ï…>é_ž‡ô¼ºFuáZð5|Wøb+±U;/Y]uÝëVìfÚìã²ÃQTr%ºR]¶ÜÆøÖã+6ÆkD "TD¨žš¶¶¼Ä ¢XYYaee…A-û^}hU¡T*e„ ìß¿ŸÀÀ@RSS '99™+W®°fÍšûÇ/:ä—äe}®''ó$#¨]Ù7ò¨i>‰„‰óæ±ÃĤÄ99p8+‹QAAâ¹Q#!?ÜõëË—C×®ùâ 6-VˆñË%½Ä: l9äÒ†<¾öXý¤¤`=ʉžð„øxÈÕš@ ˜®àØ ¬{‚ÁBèü+¼ý*‰C™!^ã½´Z<ƒDXb¡zjÚÚò:ƒÕ#j–òÑÊ<е ±¢OÕ2'8˜ŸÂÂJ$„uëFý2|æE^*•ŠQM›²'(¨ˆz `ŒnÚ´ÀÒìêª~\²³³iêÑ”gnÏJíc“dƒÇUtžëP¯M=ìLì¨g*ükkl‹Õ@+,Ü„<Ð~~0lX"aaß‚#4X£#¡pÜé}]¸—#@WWŸ³ÿ>Mo‡Þ/ùjˆˆˆˆÔlDÝR>âþÖkâÊ•+¼ÿþû 2„!C†T÷tj,*`O®ÿs/ssQ íÚi<œžžúÊÒßW]¹.½öF’"Ai¤$"%‚ˆ”Èõøˆ5å™í3\ã]‘Fw`÷ÊödÅ:ƒä8mƒñ %m•¶ÀI}ZųˆˆÈ?’£GrôèQ®\¹B÷îÝ«{:Z( _]ºtaåÊ•¢O^%¹””DxVP;Ý7jgÏÂ’%pæ 7LFXXv˜˜0ñ§ŸT@<ü~ëwB$!‚ ®Wò|·ÇÝ0Í4Å0Éf›'#:5še9:9x·ð&5.ÿ8`äÖÐ9¨‡â­2‚ ­À¸Ž>ÿ9ºBó©-¸»»3pà@Þ}÷Ý꞊Ö#ª¹×„\.ò‹ñ2>yÙ7t$F×R­Õ>J%xy ÂùÆ‚v==$S¦0±ysv,XÀäÔT;:²Ò$‡~–ôŒþ7gžœžÀ„:'¦TÁYîLãèÆè=×£¯k_º´é€ ñéñ<íúKƒzüqî6qº·Á° C‡B• zeÏ!Í9GÏi<çêF "TD¨­^[ª 1ˆ°(2™ ™L†\.GGG§º§£ÕhUamFtÈ/IE}*ûr³oô³°ÀF¯5TCÑÊ@ŸœØ´ Z¶RÍå‰g˜=ž< 5g‡ÙbbÂÄyó , *ÖÞ\Kë_[ â°¯k϶µÛèr· f÷Ì t/êÒãzÌž˜áæìF—]òÇ Á±«#®mÇpú³eÄýt~ˆ§é±Öö9ÈW=¾ÂP“b;fð8¤”ÀD-D "TD¨­\[ª1ˆP=¢f)1ˆð5 :ãW gxçš5cz=5ûû"UKZ¬_?ýááíVVðñÇdþûßLÿl)¹^5Ä„zÿ <Þ)2Ôˆ}˜‰¡·!mÍÛÒ¤q“•Keæ2î»ÔgüT)‰‰BÛðá°};äum= 5÷Ýî—y»ÒÇR6{lfꤩ¾@""""µQ·”Vîo¥¥¥±lÙ2öìÙCpp0ÞÞÞtëÖÍ›7ÉW_}UÝS©vçºoèJ$Œ·¬_-ññðË/Âñ¢P¡{{˜3Þ}ŒŒÈLLäÂ…,"#z² ¸Í³ý µ%+ò´ k®¯aÞ™y¤åUÍÙ0l Ì–D"¡¿þtÔíHj‚ÿ$p ΊOÿ+E©²áÍŸ ÉŒGç–yý¥­Rý8@ýgõ<`°f¯‘ˆˆˆˆÈ?­tá;v,kÖ¬aÀ€XZZæ·7mÚ”eË–‘““S³{9|||ðôôÄËË«º§R#ÉQ©8ëï9¨n]êêê–ó ‘—", fÍ œ,XP ž[´€Í›!8>ùŒŒòŸ"‘è…Ž:@ÿbmæùýƒ‚黥/ÿ˜´œ4$Hø ÓøÿÇ¿ˆxP)Td…f‘x>QítårX{ÑœO¾7@©ccØ»¾û®¨xXóÃßi éêoÝØÏ˜Yãgagg§ñË%"""R›ðòòÂÓÓSt Ó­³@ûúúrìØ1nß¾««+gÏžÍ?סC’’’ÇÙÙ¹gYq:wî,n…£">gˆÏýá4ÞÆæUO­Z©l ÏñãÇÉ*ìSQ úúúxxxä]~øvîüóèÒ¾øBð‡ÐÀŸY@$E«JR~9ý ËÎ.C™¡¤QN#œ œùªËW´•´%ãHiéiä¤åt?2Ü©(T ¢R£h`Ú ¼ôt8r¹ë#pr‚C‡ m[õ³222âø¶ãŒ1–‹ÒíÓ…$Õ‘ÐðIC¦»Og·s4¼Gí@ "TD¨1ˆ°$baQFŒÁˆ#˜0aBuOEëѺÕ% €¦M›âš[lA*-0’çää “Éjdpˆè_’Õ«Wóí·ßbaaQnß<÷ }©”á…v%j#K–,©Ô­Ck×ÒòÈÊè <:KK!£†—WÑ2Ûƒ ¹_?Í/¬s kó¥4Õ5G_WŠ~š9úé0ȇžQ4Æ1rn}‹  Kƒ. ´ˆn€.)¤ä“™™ÉÞMÉÎn“ß–M·”»è&SÍVfsXu %íèÛW°<—§#7nÌÓ78qò'þ>ADD}ºôaØ×ÃprrÒü^µ„<+ш#ªy&ÚEEÖ–•][j#yA„+W®¬î©hÑÑÑ88”õ-"¢uÚÉɉàà`bbb°±±)" :„B¡ qãÆÕ8×Cü –DÓ+K©Ä+×Òöfݺ˜Õr«Re¿à¾\½šå·n13"¢Ô>3­¬ø2>^(¡‡T o¿ óæAÇŽ»¨ñì\N0 n{tUyî5 H ‚ Ÿ Yz ¡«´ýê §µ½#º¥8‘éèX#—T,”艼ûòuôHâ2~+W‚¦ ©TÊ›oò¦Ç›»G-DÎêÅzDñ\ ñó¢Q³”Ö)‘:àèèÈÌ™3Y¸p!‰¹\ÎX¼x1#GŽÄ¨ÿ¥HíçÄ‹$É匫åîUƒƒtíJèj­Ð¡qq8äþ(AO¦L¹s¡iÓ _/+'œúÎèoÙ™²Ø’bª‚Vé´"º’ödG•ºEÈ‚geUô(–`C-aˆ:€˜JDDDDäu£uÚÐÐ]»v1zôhš5k†L&ÃÝÝ´´4Ú·o/ækü’W<ÅHG‡!µÜ}£ªøïŠ,»v_ÔX¡—Ÿ ^gÌò87hP¢Ÿ¦˜)ƒéi劎²”¤ûz€©™Ÿ#Êw”…¬È©©Â¢ùõ2Ðá–@ 5,BDDDD¤– u„€»€€<Èýû÷ÑÑÑ¡mÛ¶Œ9²ÆVÆÑ$¨K›™÷Í7ÌþðClªÐ¬I OºBÁ‘øx†ZZb\CßÿÒ˜ý¯ñ"8¸H[Rz:æÅvYê6nÌO›6•? Jâpå ]]žŽ…N?$zz8|õ|ôÔ­[Ê@𑉛¼ PF‰l i9|0SHìWò(ëOD‰‚TÂ1Ɖ‹X“Iíú¼,b¡zÄ BõˆA„%ƒÕ“••…¾¾~uOC«ÑÚÕÅÐЉ'V÷4ªŒšDæS§ˆJNfËŠU6®&>GããIS¾³µÑ}ÃÁÑ‘7wì``¡ÌžÀ–B}Îèêâß¿¿úáêUá¸r®]ƒ¤$þ , ïÙ,37gîµkЬY¥çžz'•û¿¾M’sÈ@¡ÌA*Ü4¬m%H ¤H¤èê 5”’”)%s«à½K¾šÌõ OØuí<ж÷ L/(’öù¾ý}n4¾!þN¬ð'""""RkÐ:‚‹/®±ƒê¨‰A„…­ÏyT¥º¼@Ÿ¼â)ud2×B÷< >¤‡•g‚‚Í3@ÀP"–- Är÷YÖÿ]±‚Î'Or£ïYbf"×l$àX reŽ˜Y¶:-Ó©?F]ð:æÅ É ü”~†±†4ˆiÀ¢Y‹ûöØ ]/;;¸P¨ED…³…Ô¼‚JUD¨1ˆP=baIÄ BõˆA„å£u«KÏž=‰‹‹#**Š•H­¥mÔÄ Â"Öç<9íåU%Vè²}ärN%$ð¶µ5ºµm""B(›½mÜ¿Ï{À4` °Áz}ݺlݲÜÜÀÜü¥/åààÀþ'^Êúœ!Ï`ÕµUœÜtÇ'…òy¼hDg«¼Ù»~~Ó‡¿ÈÛuÞæïËBßQ}éѽÆÆÆjF.###.‚BR0Œtþúˋѣ‹·o_{_1ˆP=b¡zÄ Â’ˆA„êƒËG¢ÒB‡âåË—ãííÍÌ™3éÛ·/ºººå?I‹Éó%ª) —÷ùóܹ~ïΞ%þË/KvxöŒî»w3cøpFŽy%Ä]ilzþœéœn׎uêTù5^;iipà€ šÏ£HBd©”•NN´~öŒ …y㫯˜õÍ7Õ2U¹RΆ[Xè½Ç[Ž4‹Ì dgP“^2ø¢´@Ÿ}±±(rwÆkQö ÷aÃÛ¨ÿòóCOÍùlàœ©){,€[·Šž40€¡CÑìî¥7½7w.·lÁ·Š¬Ïܹs‡{ïѦEÚµkWB˜û„úðÅÙ/ð üi¥J)†ñ¶Áxîúu".FæíÜÞxCxŽD&ÁzŒ5FÍŠVL|Uˆ>êƒÕ#ª¦.6Ç IDATG ",‰¸¶¨G ",­K¬kooÁk·¾½jjRá÷ß~Kœ¯/Íš5CùÅ0oæätïOîÜáÛÈH ?ÿ¶nåƒçÏyPÜ-ACV¯^MjjÉL yÙ7êëëÓëøWW†-XÀF33µç6ÿŠˆ(Ï ôêë×CTìÙC†”*žAÈ Ý®[·*ùü¯^¿šVƒ[á¾Ügà¾ÜVƒ[±z½P›ðNô†ü1„Þ›{ç‹ç:²:¬Q¬aAý¸²k¾xîÐÞ|3÷¶t%ØL°ymâ }DŠâãã“H(R@ikË?%K–T÷´qmQOMÒ,Õ…Ö* Þzë-úõëÇìÙ³k|!Îø …‚ßÿ™LVc¬"o;Æq##Œ²²ˆ6 ƒìläÀøÖ­™zþ<#üýQ.††\ïØ‘:Up_‘YYØ_½ŠR¥â“† Y©…V±íÛ³£˜:˜ ìpq)S`òdhÔ¨Zæøñç³íé6’[•t71¹gBC†¶ D…ðço 3à£öñ^ø{ÈŸÈØ¶ ž?ú·k'd2”H@ª/Åf¢ ޝóvDDDDD^r¹¹\Î»ï¾‹ŽŽŽDXZg `Þ¼yX[[óÖ[oåWÆ©‰e°ó¸~ý:³fÍâĉÕ=y›{y€¯/ÙÙl&Ëd ËÉa¡³3AŒð ßí¢2ìE©…î„…Á¢Eü+&† ÅNmHø—»;\ºÃ×_W›x¾}ç6ûïîW+žRÛ¤€*Z…ŽD‡éí§øn ³ŸÏFñTÆŽâ¹uëBâÙ@Ší[Q<‹ˆˆˆÔRNœ8Á¬Y³¸~ýzuOEëÑJS¨Ëç®átïÞuëÖU÷44Æ?-g™™Ô½z9ð°ÏÏZ·æ«5k¸Ó¦ {cc9õâŸ?yÂòÆ+uÝ<÷ Gº•â*ñZIO²glÙçσR‰;0˜èYÀù6møÏñãÕ9Ó|–®YÊóVÏËîÔ ìýí9½ð4.†.Äìˆ!õY;wÄ<¶h#GæŠg#A<ë×}âDDDDj+C† aÈ!b)o Ð:íì쌗—WuO£Ê©IA„Gãã±—/#¶êë3¥{w$.À‹0a[&LàÑÌ™ÜÉÌäǰ0\ML˜lk«Ñ5Šú<ËÌäjn†‹j-Ý­R š÷î…””¢çÛµcz§Nlس‡SRØ`fÆô… «ìò• ô  †nåt2;ìpÑw!jK‘9üñ‡`dhÚF©tLt°j‹žºÐÉW˽{÷øë¯¿P*•$$$`iiùÚç Í¤åÆT´XMm'>>ž:uê -#åä?‘ØØX¬­­«{ZÅ?umiÓ¦ o½õV©çÅ ÂòÑ:][©iùyº}r2ïÆÇ³8íì̾sçàȘ1¢£1úóO¼îÝ£óªUÄI$¼Hs##:™–_T£xµ°=±±ä9Œ«ŽE>$DÈ×¼u+6¬Ê¦QÙja2Íþ¬UÆ‚xŽÊáÏ?…ÛhÜÆŽijÌ\†íT[t-«'Á××—o¾ù†öíÛ“““S+b"ª…B€ŽŽN5ÏD»?+ê_õüÓ^—àà`Þzë­2´X‰°|´B@_¾|™={ö0}útê֭˲eËÊì_KnÖ¤b|NN¾%xÈåË ºëë3ï›o„¼ÏÆAðÁ°oNþþì=›A?ýD¦RÉHnv숭^ÙËâïc^ñ”&††tÐ@€W ©©°¿`möö¬Ïyèé Y3<=ÁãDöŒéß}Çà‰ùö»ïªtJ• ÚhÛ¬-Wâ®@™ÍLÂMš5”Ìèöì)ø½àì ãǃŽÈêȰ›f‡Ì¢z—‰zõêq5×HDDDD¤rhâžQ“4Ku¡:::š«W¯òöÛoc`` ~YV3Ç_¼Èrè`í;ïà:n\A'++Á½á?à£èëçÇÊU«øè“OÏÊbÔýûœk׮̪}… ÊÈàV®«„¦î;7näâåö“±ø÷ßó-ݨTpá‚`iÞ·¯duÀNÑ<~<”±­ç>l'§LÁ½ ­ÏUÁû¼Ï¦±›Èž£ö¼YºãÏŽgÐÈ7Ø»Wˆyppê»Èd k¥‹íT[dfZ±DˆˆˆˆˆˆhZñí8räHôôôhÒ¤‰hmÒòÜ7l :Ðþw„h²âLœ(Ôuž>½¼ðsqaÃ[oq))‰>d}«V]3Ïú š èfmÚðèóÏù"w¾êHf÷è!ˆçà`ÁEcÛ¶…<êÕÒÎyz åµ5dÅÚµ÷}„&…2úØhr\rà(0!Ò1‹ F{{Nœ0&÷í¥aCÁCEWôlõ°b‹Ž‰è """""¢­‰°˜4i×®]«îi¼2jJ¡\¥â䋼õø1•J°6wêTú“êׇãÇaÝ:ÖlØ@O~å׋K}ZPPr¹(ȾÑÊØ˜ÖDuêÒ…§Í›“ ”r¬·´dV¿~àæ&ägþî»ñl`ãÆÁ±cBôÜÒ¥Ï¯Š€€€—z^Hb}¶ô!8!ZÁ¨‰£èp£Ž>ŽXÞ²¤íÙ¶L?6I½'М„Ô„õë ¿ôô@¿¾>¶ÓDñ,"""òO¦¦h–êDktm§¦ú$%‘˜+jß:tHh4Hˆ(+÷ÞCÏ×—ýýEÃØXfeeñ÷?ö≉‰EŽü‘ˆˆ®EEáŸëF1ÜÌ,?0Jfþô«J)cœ >>tèÐA£/^ÒÓÓ\ѵE¤ÊÉÊÊÂÇLJ””<<<*”û7==S§NѪU+š4iRj[i„„„àççG¿~ý077¯Ô}ˆˆ¼n´J@oÚ´I£~Õ% ç΋¥¥%§OŸæÃ?äÇ䫯¾ª–¹¼ ‚22LO`HžËCûö aa”âLêÐ;³,"‚»MšÀgŸquáÂ"5>n7i£† 0ºs‡¿ÿ !௘P&0rKЦð3‚hÎ[‚W™˜0óÈ!À±–r/æ¶ 6]p—Y<`1ózÍãž4$þÄ"U#žAø=aÔÜë1ÖHtDñ¬Í|÷Ýwœ;w.ÿ±ŽŽŽôìÙ“I“&U8ôàÁƒÉV󷤎]»vaggWfFŽÉæÍ›5J‘5þ|BBB42˜T5>>>ìÞ½› .‚©©)­[·æÓO?ÅCÃÝ6¹\Î¥K—ðõõÅ××—ÈÈHZ´hÁ¯¿þúRsÚ¶mÄÏÏÄÄDÚµkG÷îÝùâ‹/0+¥"ëÖ­[Y·n< Aƒ¸¹¹±dÉ’‚lCå••…§§'G%5Uدzþüy¹ïuabbb9r$‹/fÞ¼y¥¶•Ɖ'øàƒ¸}û6®®®_·¢„„„ä¿_þþþdgg³råÊWzM‘ÚV è;v”™Ø»º¹xñ">>>X[[3gΜ ùªÖ‡ü£…²Y Ù³Gø»{¥Æ\â⽌ N¼xÁÝþýù (ˆÛþ @ ðgŸ>ù}øùaÖ§@ff)#æ"‘I‹[µ‚Ö­ùÄÀ€ŸW¬àëÄDÁúÜ®ÿWÄóï¿ï$,,¶H[\\ VVE3‘ØÛ[óòߎºÍ mƒˆÏÞ³ÿÈìî³óÏ×'ƒ~eˆg€(ÃDlÆÚÔèHˆï¾[ɦM>Èd&eöËÌ|Ìùó[ʵˆ©ÃÇLJñã¿ÆÀ lËjVV NÄÓsb…¯Q<ÀÛÛ777 yñâ§Nbýúõ¬^½š‹/VªjX\\÷ïßÇÙÙ¹ÖoÝNž<™¨¨(úôéÀ äÌ™3œ>}šåË—3gΜrǧïÿ³wÞaM]}ÿ„½T–¢8À€ Š·Uë¨hÝ«¸gmÕ¾ÖºZíë¨ëž8yµâ¨u‹V7(\€"²÷ÉûGH$H@­÷ó5kÖ`nnθqã¸{÷.›6mÂ××—óçÏSIƒLF;vìàÀ,\¸I“&abbò¯ÛQ•3gÎ<¼{¿âããKyUe¡¡zÊ”€666Öøî¹¤yûö-ñññŠ2¨õë×ÇÏÏ©T*+.¢†‚C¾\@;feQ+\æO[(ÿgh‰Dì¯W·oó4-€±cYõâ3¯_ÇMG‡Ä:u°xôˆù¾¾ª©^4Pˆeê×—eË02Rti¬9s†__ÖXZ2mÕªZwI±fÍ1<˜û^ë|@¹8KýúKúVø-ºxv!.="–w\Kã´)üü3øùAL`9:…vâù&$WýG‹g€„„tBC——>¯XqY¡‚Ss#‹‰AZš»šž>¤¦†iMÙµk¶¶¶„‡‡3lØ0.^¼ÈÊ•+ùá‡4çÌ™3J{{{Ó§OƧÖjøOgÖ¬Y 0«\;k¡¡¡Ô­[—9sæ0f̵îfffìØ±ƒfÍšáè舉IÁ7pù1sæL:ÄäÉ“Y½z5:¹Š5?~œ¡C‡òå—_òôéSE¹ö;wî°víZ7nŒ¯¯/F9߃‡¦_¿~¬ZµJ#ãN`N¶¤iÓ¦ýëÝ'ÜÝÝ3f M›6å¿ÿý/¿þúki/©Ì#T"TO™ÐÅIff&!!!ä{QH$>|HXXMš4Qú‚MMMUºÓ××'33‰D¢ÑöiY¿“²³¹œsGÞóÑ#Yc… àâRÀYšaª£Ãц q¹s‡D±˜9?ýDãQ£ˆ°¶&þÚ5hÙ’–/R¾Je‘Ü L(ç³…ù>ÓV­bA·nDÛÛÓ¼E‹^wI £c¼¿x,Ÿ~pí•]=»‘”•RÖþø~Ñxä±’V¤ó95Uú<ËyL9ü0Ç©€>ekkk¦M›ÆÅ‹¹ví€Â*9|øð<ýƒƒƒ9xð ;wÎcÑT…T*åöíÛœ>}š'Ož’’B:uèׯ_ç?þ///¨W¯#FŒPˆ~Mæ<|ø0W¯^%,,Œzõê1`Àê©H/ùàÁN:ŵk×(W®ööö¸¹¹áàPðÍÔ7ß|“§ÍÆÆ†°gÏüýýi—kgL*TÐÈU¥ n߾͎;hÞ¼9yŽ÷êÕ‹ùóçóÝwß±lÙ2/^ Àùóç‘J¥ 8P!ž¾üòKLMMÙºu+ .Ì×zËæÍ›×̆œ\öõêÕãË/¿ÄËË‹¨¨(¦L™¢t^JJ ëÖ­£U«Vj_Ÿ¢’ X*œéÔ©“âï°°0öìÙý{÷(W®ÎÎÎ|ýõ×yÊrwûÀÔO‘²®YÊeÆþT¡B…b©EõêUš7o®ø‚1b„Ê~±±±´nÝš&Mš0fÌ*W®ÌÏ?ÿ¬8nccƒT*%!'îÁƒ4o޼о‡e•Ó±±dÉ«>,kìÜ9Oùê¢âhdÄ^GG´D"² éÕ¨ñ³gËÒÇ…‡³réR‡³gá·ß`Ì™x×P<ƒ,#Ç3‡Œõ¹0DD@ËþWølc—ñ¬Ƕñúè;ñ\‰ YFbbÿÇú!åñÃ|žÿ TˉHÊ©âyûömF­rÇë·ß~ã‡~ÐØÚ‰³³37näõëׄ……±|ùr\\\X¿~½Êsîß¿OëÖ­9|ø0ÑÑÑüúë¯4oÞ\£âX111téÒ…þýû³mÛ6"""X²d M›6eÇŽŠ~R©”¾}ûÒ AÖ­[GFF¡¡¡¬ZµŠ5kÖhôÜT¡•dlUƲ{÷n$IÿI“&Q¾|yvíÚ¥h“û»ºº*õÕÕÕ¥yóæÄÄÄðHnQARRÞÞÞ¼|ùí>x{{sóæM@‹´dÉ•çÍ;—¿þúKãçXXRRR8pà@žÇ¦M›˜;w.ÇWô={ö,NNN,Y²„¨¨(n߾͸qãhݺ5ŶF9eÆZ,ãJ$Ú·oÏ·ß~[à—ëĉ‰ˆˆ 44kkköîÝ˰aÃhÚ´©"°ÄÕÕ•mÛ¶ñÍ7ß°{÷nZµjU,k. þÌqß0‰øìÊYãG¾kïiaÁb[[þsõ*©&&P© ˆÑÎ8 ù8~£ÛU¸Ùü›ˆ4Œ ²n7ÐK‰6xï¤Bè0Z|.»Ïh]7ƒšO"Ñ×’²q£jòÇ”ãæ%¿ø2@l,tê$«´XXÒÓջ䗞žž íĉñôôdÇŽJÂ,-- OOO:tè ±xùòå¹|ù2mÚ´Q´EFFÒ§OfÏžM¿~ýòˆÍU«VáééɰaÃYQ 6mÚ0vìX"U³gÏæÜ¹slÛ¶ÁƒchhÈ«W¯1bß|ó ]»vÅÚÚš‡räÈ&Ož¬d¹‹ÅEþ?òêÕ+Ž;F½zõ¨[Bi.Ÿ>} @“&Mòícdd„½½=·nÝ"99…eÐÇÇGIDgff*Š‘½~ý:ß9üüüpwwg×®]eªò¯µµ5þþþJm©©©´mÛ–˜˜FŽ©hswwÇÀÀ€ëׯS'Çpß¾} :”9sæ:¨€@a)3èâÂÕÕ•+V0hР|-/QQQ9r„‘#Gbmm È*#Ö¬Y“M›6)ú-Z´ˆ={öP½zu?~\¨l e9ˆP"•r2§ú`·¨(´å&ÍbØöúÁƆªÞÞ² €¯_ƒ…º&&”ñÿâù½J„µÎÁÐs —‚:Œ¯¸—û‡gÎÀ&dP/,}­œ÷ )®ôx‚~hñЇTZ4Ÿà"ÙÙ² ŽÐÐÂ?"#AZF¼]:ÄÎ;YµjíÚµcݺu3s¦,€´U«V899±eˤ¹ý¿ÿýøøxƧñ\FFFJâdÖÙ3f’’‚¯Š˜kkk…xppp W¯^rþüù|çzùò%Û·o§mÛ¶Œ5 CC™ËRõêÕ™6m)))Š °¬¬,jÕª¥4†ŽŽµk×ÖøùÉÉÊÊbРA$$$°yóæEþÇäéÓ§ˆD"ªW¯^`?ùîç“'OèØ±#"‘ˆƒ*vDAöËw"^¿~]| /A$ C† ÁßߟƒâääÈ2ć‡3`À…x2d¶¶¶xzzòöíÛü†Ѐ²¬YÊ ÿz­ wîÜ!+++Ÿ]Æ •îÎíìì¸sçaaaœ;wN£Hg9gΜaΜ9,[¶Lé!ßF“Sm7“’ˆÚ¹¢¢èyñ¢¬±A–íÙóÑçõõõEª­-³>9žž$´oÏŒ\î2eåu)®¶àà—üý7Ìš²ÖeÀ0œ ­Á¨5Ù€ÁX¨s†ôÝl´}µÙÐf=§ ¤^=Y"’Ÿÿó3w×ÝE’.QÌñÌð:Uêü££ŽŽ>X¶:Jbƒ!|ñÅ·|õÕÅÃÜ<¦L½.ªÚbre†ù ÀÍM–¼°®]ËN¡Êï¾ûŽ‘#G2kÖ,üýýéÚµ+ÿý·’ˆ˜8q"/^¼P¬›7o¦bÅŠôÉ)X¤)¯^½â‡~à‹/¾ Q£F888(,ÛÏŸ?ÏÓ?·ªœÎ;èVðàÁ¤R)U«VeÆ ¬_¿Ö­[§šòó7nŒ££#³gϦgÏžxzz9£Bvv6ƒæêÕ«¬Y³†Ï>û¬Hã…råÊ!•JÕfïHÉ©Ò*OgçììÌ”)S ¤ZµjŒ7Ž–-[2lØ0š5k¦Ô÷ŸÎ¬Y³8zô(k×®UJ1(¿™øüóÏóœóùçŸ#‘H *±uþSÉý½+×$ݺucΜ9 —ü)3.¥IxNƉúõë+µ7lØ'N ‹•¢£uŠàܤI ”'Må{e¨ÝÜÜJ¼íDL ¸º¢]¡ÝrRÌÑ­[±Ìûݲe„»¹É¦L—/ÁÊŠ[W®LÍš5Kå5Ф­S§¡DEé’‘‘€§g ZZﮃÜm•*eqþü^¥sÓÒdîÝ7oº±r¥%Ñѹ&Ѫ¦=¡w0ØÈʨ‰;ÀoÄK™i³næ:†¹¼³îeEeÑ:³5ô”wV&ö‹uk u 1ndLÅ>Ô›5j(=~ü¸Ô_Sum—/_æcP®,] jâËTâã—/ËÞÃÒæÆT¯^ƒ|3 :”ï¾ûŽÍ›7Ó¹sgËÉñç{íæÆŒŸÆ{ëÖRy 4i‹‰Ñ%0pgž>ïãää@¥J:ÞÞ2W ™Þx7ž¶6JH6øŒ{ ¹µ%`™ Õo¶UÛ3qÑDÅá¬è,"vEPÓ¸&ïSÛR¶mÜÀK7K•ׯ(meÉ?³,P±bEµ….LLL6l[·n%**Š-[¶ ‰;vl¡æúïÿKRR.\ C‡Šö½{÷æ{Î7òm«Zµj¾çÙØØ279M²[T®\™Å‹³`Á®^½Ê¾}ûغu+ÑÑÑüñÇjÏ—J¥Œ7Ž={ö°`ÁfÏž­öœMÆ ñòòâĉù è'OžðäÉœœœò¬1"OP|÷îÝÑÑÑ)R¾s9:::¤¥¥åIÑúâÅ‹"YXNž<ÉÔ©SéÝ»7+V¬Ès\îjyãÆ z÷î­tL~½©sPþÞ•ÿ.ÿùoOmø1\8@ñI¾-$çÑ£Gèëë”ÜÔW®\ÁÝÝooïëc–‘Nª÷ïËá=ßÇÁŒŸæµÜúü>•*q+5õ£ùB—&ÑѲÊáVV2€#GäâY–ºÚÍ vî”ùÖ–«øº=RϹÑši‘þ,NÑ”#ÏÙ)ùû0Õ3¢bߊˆ´„lŸ"'N$++‹ 6°{÷n:vì¨äæ¡ AAAXZZ*‰g @*O±–›³gÏРAƒ|Ïkذ!ÚÚÚ;–7…cAhkkÓ¦M6lØÀ—_~ɉ'~À1yòd¶mÛÆÜ¹sùé§Ÿ 5çÇbòäÉXXX°jÕ*bsbPÞgÁ‚dgg3oÞ<µãùûûsêÔ)†Ž™™Y‘×U­Z5y¢å:t¨Èc†€€H“&MØ·oŸJŸtGGGàݵ•›sçΡ££ƒ½½}±¯õ߈··7îîî\‘'ÈÁÍ»TP< uëÖŠöû÷ï+Ž}(ÎÎÎe2*øÏÜÕs‚tèØ ±Õ« ÁÁÁÜJMU¶>¿~ •++Ls[¡ÿɼ~-{ȱ°€ž=¡OèÒrâ£HÈ…šb墑Y åØf“p^–]@'–‰ç¤ijƒ¿ªøIÜ7iR—Ö­A$*øÉŠD”+—77²&XZZÒ¤ÉfD"ŸûI$bêÔU¤9>6 6ä³Ï>cÉ’%ˆÅbÆ_è1j×®¯¯/üñ}ûödâäôéÓùžͺuë˜:u*7oÞäøñã8;;+*÷©ÂÚÚšéÓ§³råJ.\È?ü Hk𙙉··7ööö899q@‘ÔS IDATåÊbccÖV¥ÀóóóÃÈÈHmQ“éÓ§³aà À¸qãò”·´´TÃÙÙ™ÄÄÄ<[ÚaaaˆÅ²Ï®T*%##C1–‘‘‘Ú333–-[ÆØ±ciÑ¢»wïÆÅÅ---ÂÃÙ5k sçÎy|×?N«V­°´´$;;›+W®0tèP øñÇ œW­ZµbÓ¦Mxxx°lÙ2ŒŒŒ8tèø q5áÍ›7ôìÙsssŽ?®är–›°`Á¼¼¼5jM›6ÀÃð°0¦L™‚¹ù»ŒCIIIŠx yàeDD„âýªZµj±¤Ñý'âææ†››ƒ.í¥”y ¸¸¸P¥Jnß¾­ØæLOOçþýû…Þö̲Z‰P^}°¦–õåÛ¯Å}cÏÿþG¹˜œr¥z@åzõÐÎõÅõìÍâââ>È‚R°±‘YšÝÜdÆüü‚ÐjÖ­Ìí÷容 õÚ²~âx1;#ÈN,@<×5¢bÿŠˆ´? Ëó°a}6¬o±ÎÑ A|}÷ëÅÁĉñõõ¥R¥J ÿÆÂðÃ?àííÍW_}…ÚÚÚ„……±fÍÆŒ£òœyóæ±råJÖ®]‹©©)T®\™íÛ·«oñâÅÄÅű`ÁV­ZEݺuINN&44”´´4Nž<‰““Lœ8ìììH$Ü¿vìØ¡¶2¬Üáåå…——Wžã;vìPr#yõê•Ê EWWW¥´yþþþ ?åÞ½{k´Û8fÌ,--3f Ÿ}ö&&&˜šš†H$bÆŒ,]º4Ïy?ÿü3ׯ_Ç‚””ÒÓÓ±±±Á××WÉWº( :”7²yóf<==122"99™7*ÒÈ—.]",, ccc•.ãÆcÑ¢Eèéé±oß>ú÷ï³³3ŽŽŽÄÇÇóúõkºuë¦(:#gïÞ½Lœ8Q©-·@|ôè‘Ú<ŸB%BõüëtîÊFÁÁÁdee)ÊœŽ7ssstttøþûï™5kµkצI“&¬Y³‘H”çC÷!ëX¶l®®®yà—i çãd®=røƒ€þñûïùñûï?ú¸e‘ºuAÓø‹V5xù@Ù…ã}­“ vævDìŠ@œðžµ:†u ©8àÓÏŸ'N¤[·ny- BžUhäÈ‘j­kòb%ò, Ü b÷îݼzõŠ йsg¬¬¬ÐÖÖV8ìØ±WWW¦L™ÂŸþ‰¿¿?#GŽT™/zΜ9$縎É144dÛ¶mL˜0___Eùêš5kòÅ_(ªŽ=š (ªš™™±~ýzZ¶l©Q€÷ï¿ÿNfff¾Çßÿ~^³fÊþ+V¬ÈóäFx¸¹¹Ñ¦M®\¹‚¿¿?ñññ899ѲeK…«Âûìܹ“¿þú‹çÏŸS¡BœéСC¡üVÇŒ£rW@GG‡K—.±sçN©Y³&ݺuS¼þ 6Tôµ´´ÌsݨjË:°cÇÅëÕ²eK¥¢9ï“;SV‹-ÀÛÛ[©a=òÜDÉçÉ*Uª¨]ë§Â•+W¸råŠRŠDÕˆ¤ï;¬ýËxóæM¾©›<¨^X¿~=[¶l!44”-Z°råÊýö4ÅÝÝ„„–.]Š¥¥e¡þ 'ÆÄÐ3ÇïùÔÞ½tݺììà=_p»°Sm?''wüýÕ÷ÿßñl¹¶iãü?†•îUbSùM4¶É?Õ°–!•†TB¤óïÏ;wîdÁ‚y¶ÙÔ3dȼ½½yüø±`IP ßaQåVMtt4sçÎ¥B… eÒõ´¬ð¯·@W©REãHþÉ“'3yòäbYG… ÊÜ‘Ü}ÃXK‹ö¹Ò× ?iâ4&ý9‰i;!Y¶ ÷U†o qîNc·üų­•ÿûij@áIMMåúõë\»v0þ|A< hŒÜÈ'dáPÏ¿^@—ÊbU¹€þ<3}yšˆ\Éê‹›gÏžakk[¤¼Úÿd‚b‚èçÕÀ(Y”{å¯*SÕ·*Ï_='¾Nèëë—ö2Ê4Ÿ@œ~ÙÀÇǧL¥± HN&,GÔ÷”.00€"å?6ùú–E”ŠŸ‘ýûi¾¹¹B<^ësî͸ǭ“·88ù ãOåã•™”5‰½5÷2ÁmBžâ;rô«éc5Ô -=ác, £víÚÍ¡C‡%±4AžÆÎÇǧ´—Ræù´L¥H×®]Ë”/‘Üú,zÈ #´m«œc­˜Y½zu‰Íõ¡øø@x¸.àŽ®®¬¢]~qYÖÖyddg0í¯ilº½ -‘óÛÍçǶ?¢•“‚­Ëç]èòy~_ò;»"È|“ “~U}¬†Y¡¥/ˆgwèêê*‚½ ‹<&>uý‰"ÐÍtu© küŸU ýúTºCCøûoÐ À\Áó¸çô÷êÏÝ™¥¿’q%ööÝKçZóô•dHˆôŒ,P<ëUÑ£Ò°JhâY@@@@@ 4ô'ÈÛ¬,näTëêùêÕ»%èÿüO!%z÷y½™­[ 'ž?:̨££HÌ•‹oS£ úÀºœuž¾’ ‘{"Éx¿¿¼ž•VíÐ6Ì'±´€€€€€€@±#˜°JˆË—/—è“11Hr²ö”W³±‘ù%” Ïž=STò*‹H¥0bÈ+œÿ= ¢Ù¹™Ù™L;5~^ýHÌHD„ˆÙŸÍæÂ×TŠçì¤l"wG’ñ*ƒçÑÏUŽ©WI«Vh âY@@@@àã#÷¾|ùri/¥Ì#èÂÖÖ–;w©"ØÇFî¾QEW—¦òJ\¥à¾QÖƒ-‚?þýÞ½;¨(¦’ЄPÚìhÃÚëk074çØàc,ë¼ ­¼›>a„oWXž7\Ù§®¥®L< âY@@@@ xpsscçÎB,….%DYÉÅš%•r&§ú`÷ôtD©©²¥  Ërá‘#°p¡ìw{{Ø·´rn7ãââ˜>ž»wï’––FíÚµ±··/òJ‹3fðÛo¿)Ú¦L™‚±±1¿þúki-+_‚ƒƒ™1c£G¦W¯^%2gtt4ÉÉɘ™™k™mooo¼½½¹|ù2mÛ¶-¶yþ ” åò ",M‚RSy––@÷wºw/•õxxx°`ÁLMMKeþ÷™9.^”ý>h|÷òqß[¾Hœ$Rø?Py(%0…è£ÑH³ .[neßê}ˆt„òÜ…%>>Ê—/Oƒ ÐÓSá>󤥥qïÞ=Äb1NNN˜˜˜|ô9Þç›o¾áàÁƒJmúúú´nÝš¥K—Ò²eËBgooOFFþés¬6 (::š£Gjl¸rå !¹¿ƒJ™3g²gÏÞ¾}«h‰Dôïߟõë×cii©vŒøøxf̘ÁíÛ·yôèb±˜–-[âççWèõ¼zõН¿þ¥›š*Uª°yófzöì©ò¼¸¸8†Οþ©ÔþÕW_qèСB¯£4¹(ÿâÍŹsçÊÌÿ†÷IHHàèÑ£´/æÊ½W®\aÅŠܾ}›°°0–.]Êœ9sŠmN¹‘O“áOA@—eÁ!_n}è)ÿÒ­Z6,•õ”¥ ÂmÛ`Ý:ÙïM›ÂöíyûD&EªÏ[£)™)ÊmRˆ;G•µçšw1gÿ‚ý¯[@Fxx8ƒÆ"8=˜8Ó8ôÅú˜D›Ðû³Þ¬Z²ê£ìr¤¥¥1væX.?¼L’eÙZ٘ƚâTÙ‰=¿ï)V«œµk×R±bEbcc9zô(.\ C‡ܹs‡B¤¡Ü²e ÙÙÙŠ¿ïܹúuëèׯ=zôP꫉ ü'áííMóæÍ8p 6$((ˆmÛ¶áååE\\gΜQ;F||<ûöí£AƒŒ9’íª¾04ÀÏÏîÝ»“’’ÂâÅ‹éСfffܹs‡… Ò«W/.\Èüùó•Î‹ŽŽÆÅÅ…°°0-ZD§N044äþýû„‡‡i-e¥K—Ë ð?‰[·nqéÒ%š6mJÓ¦M9vìX‰Í]4KYGПríhhH­#GdBõA®^…I“d¿[Y·÷»Šæ/â^°çÞ<ïyò,édú –Öï¶‚%éÞ~KÚÓ´× m¤MÅþ1¨iðaOæ$88˜ŽÃ:âåem)¤K,›^nâfÏ›\ùó ÚÚEw‡IMM¥E·<ª÷IÇw»I$ñ*úÍ»5çÚñkÅ.6{õꥰOš4‰Y³f±jÕ*V¯^ÍÆ5gøðáJ›šš²nÝ:š5k¦²„¯T*%++«P‚F,ÐKVVºººjûedd ¯_ЇR™Ë—/+¹G4mÚ”~ýúQ³fMΞ=Ë‹/¨U«VcT¯^¤¤$Åë±{÷nç—“ÍøñãINNæâÅ‹|öÙgŠcŽŽŽôèуN:±hÑ"úöíKƒ Ç—/_ÎóçÏ9wîr€7iÒ¤ÐëȽžü>#š¾—š¾gR©©TŠ–Vþ‰>}ú¨§°ä÷<>ôZ-*R©”ììì|ç?~<Ó§OàÔ©S%* Ô#äþDH‹¹’ ³€öLM•EÉÁ'/ Ã o_ÈÌ==8|ÊUŒgóíÍ´ÙцÚkkó“ÏO<‹}À­‚Ç3 2cÌ1d¾ÍäÍ–7jųž••ÇVÄsJ¥ 7€×…xÎMfL*ðÓ²Ÿ>hžq3ÆñØá1’**\x,á™ó3†Œ×°ÊÎGdذaúúúØÙÙ±N¾µ£Ož<¡gÏžXYY¡¯¯OíÚµ™?>YYYJý=zÄìÙ³iÖ¬†††èêêâààÀŽ;ÔΡʷXWW—häZ¢­­ýÁÖÑ]»vqïÞ=FŒ¡$žå˜ššò믿’Íܹsíñññxxxо}{%ñ\XZ´hÁ”)S¸yó&®®®˜˜˜P«V-…µ;%%…1cÆ`ccƒ¡¡!­ZµâÙ³gyÆÉÊÊbþüù8::bhhˆ™™Ý»wWÙ7&&†~ýú)üx;uêDPPÊõuïÞAƒ)µ988°`Á‚<}9‚ƒƒŠ6 aâĉT¯^òåËÓ½{w>|À¡C‡puuÅÈȈʕ+³víZ_¿÷éÔ©*ï?K—.áêêJùòå111¡Y³fìߟwçÑPnÉ(“èBSÃââTl,byõÁë×eÚÚйs©¬'))‰“'Oâä䄽½}‘ƒ¡>„´4psƒÈH@;‹q+ÿbu¸'Ç/'#[ùýjdÕˆ]Fpfíν:‡¤z^!¥¦E+½V|ÙýKRƒR‰þ#IFÁ>ÓFõŒ°t³DKïݽìãÇ µÿ)sïÞ=‚‚Á(ÿ>i5ÓðüË“ÎC‹v­K%R|‚|Èî' xüä1ñññ%ê·œñ*wiÛ¶-óæÍãÚµk´jÕJ©ïºuë×Xt%&&rúôiˆƒƒÉÉÉ?~œ 6¬Rˆoذ¨¨(fÍš…©©)»víbêÔ©$''+‰@U\¹r…®]»¢§§GÿþýqqqÁÓÓ“Å‹sÿþ}Žä욥¤¤Ð¹sgÄb1cÆŒaîܹ¤¦¦r÷î]ÅëQäâ+·¥·8ùûï¿=zt¾}:wîL5”ªÂ’ššJÇŽyøð!ÇŽãùóçØÙÙ1`À `}úpïÞ=Å÷uvv6íÚµÃÏÏöíÛ3cÆ ðôô¤I“&}úðÅ_°yóf¦M›F½zõè\„ÿ‹C‡%111Ïz7nܨôžxyy1xð`X¾|9úúúlݺ•!C†ðìÙ3~üñÇBÏ]vgçSDÐ%„O©fá»o˜éèðÙ²F(á —/_2h ²ˆ ‰Ä´Ž)Æ ÆL<‰o¿ù¶D×2z4ÜŽ¸ ÝwcÐü1ÑðÎMœ*&UÒp#œFÐȪ&2zêh.^»H¤m$X1`bE‡jغm+ —ˆ»Å ŠÀ´ƒ)¦mó¾þË–-+õ€Ó ¾7|‰5‹UÛï¥ø%6v€¢tÞl¼5}ËíÛ·?È*XBCCqr æ˜1cX¸p!›7oVÐ!!!œ={–ñãÇcll¬ÑøVVVPë.R«V-|||xóæ UªTáÅ‹€Lœ5oÞ©TŠ¥¥%[·neñâÅxzzjü?æáÇ>|˜¾}û²K ÆÏ—_~©ôþW¬X‘µk×âç秸¶~ÿýw®]»Æo¿ý¦p5˜>}:uëÖå?ÿùžžžlÚ´‰'Ož°~ýz&åøÌ 0€òå˳xñâ<úc¡££Ã‹/î"¶¶¶ ·˜§OŸR»vm† B­ZµØ²eK‘ô¨Q£”þ–û©[YY±iÓ&@f­Ÿ3g†††øúú*n†Š““K—.eÔ¨QT­ZõCžò!ÏÂáããC×®]KmÿŽ¢k×®¥VÊ["•òW¬Ldt34D[¾ÍõÅ%ºÿ\»r­þ5^¹¾"sX&Q.Qw fáµ…ôQtŸ·½^{éôU'œº8Ñ⋌š:Ѝ¨(•}CBéúóÏì·p€±- …éZÑé1´áPN;Í«™¯XÑe…B<±ë~Î.?Ë2Çeô‹ìÇ2Çeœ]~–½¿ï%õÏTâ.,žµôµ°l¥R<‚xÈ—¯¿þšnݺѢE jÖ¬ÉßÿM‹-âÅÊÊŠ>}úàååEB» Õ-[¶ •J?~¼Æs‰D"D"R©”ÈÈHîܹƒŸŸÍš5PÚ.—Ó¤I% mmmºwïNRRRÁyÜ»wáÇ+ij¹¿ö… Ù6¾ŽŽgÏžUi,,AAAŒ=šªU«*¥Q+nÂÃÉDj…£••€" ƒ„‡‡sôèQlld•h¦mÛ¶JÖu===¾øâ ÒÒÒ8}úôÏÿ!ÈKy âY=‚úàZb"19>„=Ÿ?w ýŸ³²²øzÆ×¼jÿ T¸&Û'ãèî}»øzÈ×›’’Bïa½¹­s›øzñŠ+úfÌM.ö¹È¿û/ýÝú“˜‘È¡‡‡Ø°›Ë¡—‘"…œx/-‘l;0Üi8_9~…‰žú´d 6¤a®ì%â81Û"ÈŒÌ,ð<] ]* ®„®¥ú@õ|Öâ3ÌO˜S3¦À~5tj°k®"Í!•H>j8¯y]`¿ŠñⲸ±¶¶æ›o¾¡uëÖyÂĉñòòbïÞ½Lš4 ±XÌŽ;hÑ¢E‹±:V¯^ÍòåËUfvxùòež6U–»Î;óóÏ?+,§ªûË^¸p–-["‘Hf‰‘H¤ð•­X±"&Là÷ßçĉ´k׎2lØ0ŒŒ ðåQÁóçÏéÔ©ºººœ;w._ yqP½zu‚ƒƒ‰ŒŒ,0ô›7o"L.¸Y¼x1²í‘Þ½{ãææÆöíÛù믿òŠªÂÕÕUq¾;;; ï{)o‹‹S´¡¥¥EçΕÞ/©TJXXÑÑѤ§§c``À‹/°··§zõêJã6oÞœrÅXÈ+¿ç¡êZµ³³#44ôƒç5j¾¾¾xyy)¥˜”‹ó.]ºä9§K—.üöÛo~NÊ‚€þ»oh‹Dt;~\ÖX©’,_[ qÑç"OÍŸªÏrâijv×ÚB è ³&àcåCv•÷üS- äó&/ŸŒg¤'ç¢Ï‘&VæÓŠ®Ï´ÙÙy(ÕÊkV´@é/Ò‰ú_’´‚ý í ©øUE´ „ŸE£F¨™Z“˜Ô˜|ý  ƒ þÅpÚÛ¶/ò<ííÛs üÙÖùøAÇ€£‰c±û?ïÚµK­kûöíqttdóæÍLš4‰'NðæÍ/^\¨¹öìÙÃŒ3èÑ£+V¬ÀÑÑÍW@K¥Rž={†™™™ÂµD¾½ß±cÇ<â·Glß¾'Ožh´†‚|\ó;&ÏU••EZZfÌ[±333ÖôÜhkkçy…E"Éÿ»7¿ç¡îù• °oß>–.]J¿~ʵä±PEýœ”-]B”f¡\@·.Wó'd]º@ îùû iVïe£ˆLQr$ ˆÀq#fFf˜šafÿÏø°xN‡ž&Û%Q#‚·­ßr|ËqÈÙ5ÔN³";`0¢{Ã9äє͔”p-ø³ñH%éVp­€Y'3Ðà%‚5G$áµÙ‹öƒÛóÒõ%¼gÈÒ{¥G£ðF,ܶðƒæÙüÛfîv¹ËcíÇH¬Þûg µoÔf4ÇÇd„ L›67n°yófÊ—/Ÿ'€::„¾¾>›7oVªÒ'~SÅÙ³g™7o^ž6(Ø××ÑÑÝÍœ9S£õÙÙÙ±jÕ*V­ZÅõë×qsscÉ’% èW¯^Ñ¡C9þ¼ÒnRIñù石mÛ66lØoÅ·£G®ôœêÕ«‡H$R¹ o+‰@V]]]êÔ©ƒX,Ö¨°G­Zµ¸qã111J–þÀÀ@Þ¾}«Q%H•ÏûÒ¥K…[|1±gÏ.\ȨQ£T¾&rë÷Ù³g3fŒÒ1ùç¤f͚ſP ‚Õ#˜ÂJˆÈÈÈR™74=ÀëNÏ”ˆ—(áôub±8ïÕvxïf;[šÍãèÇ\ »ÆÉ§'Ù{/7mKµ¿«ahŒ¹¿96çmg1ŽË'.Phù¾ß8}ƒÁÒÁÔð©é=SÊÝ/GõKÕéÓ‹›Ý,SGFŒ‘‘?þø#§OŸfذaÊILLDWW7ÏöºÜ—U×®]#))I©íÌ™3èèèX¸Q£FT­Z•õë×+•,×”–-[âêêª(…]ááátìØ‘˜˜NŸ>MS5;qwîÜáÆ…^“:HëÖ­ñòòR™ÑäõëÆ IDAT×Ì™3}}}~ùåE{5øâ‹/È#&åã899}ôõª¢{÷î)2¤DÇŽ‘H$œ;wN©ýüùóÏgccÃåË—•¬´x{{k¾èbâï¿ÿfôèÑtìØ1ßœìNNN˜››sþüù<;8gΜAKK«Ì”Ï.-ÍòOB°@—¥UÕG©ú ¯¯ì--™ºéÒ¦ ›vn"Ý2ý]£ o¡gAljK#.-ޏô8bÓbIHOù-ç&(8€€ªfU±¾º“ƒdwÓ_}’)(óM&ÑG£ÉŒ(x«MÇT‡Jƒ*¡W¹pùb… ÂÂS­Z5.½T¬¥¼Ù³aO©”ò.,¦¦¦ <˜mÛ¶0nܸBÑ¡C.^¼Èøñã™2e ÚÚÚ¬_¿ž˜œïU©'­¬¬èÑ£¿üò ¦¦¦lݺÆŽ[ eÍÈȈ 6лwo\\\øñÇ©_¿>ÉÉÉ<}ú”}ûö1qâDzõêÅöíÛ9uêC† ÁÎÎŽ¬¬,|||8räM›6U[Rž£¸[·nœ¨< F/`Jÿ)yŽI¤2ˆM‹UëC:‡Øòx Ò »OH“ËóÛo2ñܨìÚU4ïI–„Ÿ¯%ªuÙ0°5 Ò€Jh ›<%‰©©)íÛ·/Ö9 •ƒÊ*ãÆcÛ¶m´lÙ²HÉï¾ûŽÀÀ@öïßÏþýûÖ±#GŽÐ¤I•¾¢ ,À××—: ‹ÑÒÒbÔ¨Qxxx¨¯W¯^øøø0qâD%—mmm\]]"²J•*ܸqƒÿýïŠ>¶¶¶Œ92OÉkUÈ ¥œ:uŠS§Nå9nkk«‘(Y½zµRÀÙëׯY¸Pæ*Ô»wotƒ `òäÉìܹSñ:éêêÒ¬Y3Ž?NóæÍóœ×ºukÎ;ÇðáÃùö[YúO‘HÄÈ‘#Yµj•Úy?&&&ܺu‹Y³fñóÏ?+9±µµU¬ ÀÜܜ˗/Ó·o_EÑ öïßÏwß}§Ñ|3fÌàîÝ»üñÇüõ×_0iÒ$4h'\I’œœ¬¸±õ¯Û!´÷ô%;þ0––pó&hXg@‰´à4bŽÇ Ž«í[®E9,ºYR;w²`ÁªÃ (óÇðÕW_±}ûvFŽYäq"##yõêööögKHMMåñãÇØÙÙ)ÃB||ÅØØ˜5j¨´òGFFòúõkLMMÕæRþ' •J !11GGGwOÞ¼yCDDvvv¥º"‹yöìÉÉÉX[[èÓüâÅ ²³³>Á…%""‚ððð¾ËT’H¥Rž?Nzzº"5cIáîî¨ÞíôóóÃÏÏ}ûöQ¯^=aG´]¸»»“‘‘¡²Tgqr4:·À@NGGÓ¥Ù__ÈÉõYˆ%bš­ræ^Š?DÁcD¦"2*¤¡#ÑA7L#š:¶C__ Ge7ÿ;Ü}C_K‹Î¹óÁ“€>÷âNL ²Ievw=Éðþ>?.\ŒTš¢tîüùóùöÛ1”/_>Wët*h˜¸¹~}[^¼˜ŠH¤:V/[‡z‰Õ©˜ji€ýü± ¢¡¡À? ‰DBff&ºººhkP¨)33©T*Dù—"hii)UªÌÈÈ@$¡§W´x±XŒX,Ö¨’lQ_gzzzh}”4Lï#ß%_»vmi/¥Ì#袤¶B亃©)Æ'OÊííÁÖö£ÏÈW^_!–ˆ1Ò5bfåtu©At´ìx•*°m›¼vK¹œÇ;6oÞüAó/\8… ó¶ÿÓ ¢âYà}:D`` âolllhÙ²%µjÕ*ôxK–,A,kÔwúô阪 J¸|ù2:t`ÇŽ¸»»«³K—.„„„¢Ñ>&™™™œ={BBB022ÂÁÁ±cÇbii©ñ8ÙÙÙ>'''ZµjE»ví <ïåË—œ8q‚7nššJ:uèÓ§ÎÎÎjç´··ÇÁÁS§N)µÙÚÚ¹ϼyóøõ×_yóæ •+W.ÒqæÌ¾øâ öïßÏ Aƒ>úø¹‰çÖ­[ܽ{—””ÜÜÜhܸq±ÎY–Ü7Ô#èSRN—éibWd}Åa}ŽHŽ Ç¾$f$¢%Ò¢å«ý|ÿŸw• € ÀÜü£O] BA”O‹ƒ{÷rÌÃ]5VÏT--þ»{7¶E¸‘¼sçÿ:u6µŒìlÆ.YBÇN =‡::ÄÁƒÑÓÓC$!‹ÉÎÎFGG‡©S§²råÊB·dÉsÓ»»»«Ðÿ$7nÌ£GÐÑÑ¡víÚ„……‘’’¯¿þÊÑ£GÕŠV€×¯_S·n]RSSm-[¶,’€Þ¸q#³fÍ"--:uê`ffÆÑ£GIKKcðàÁlذAå®Ü‰'>|8ñññØÚÚbhhȉ'¸s玒(.«ÄÇÇcffÆ´iÓX½:oF¦Òä—_~aÞ¼yH¥ï²4ÙÚÚ~RZ@=‚€þ!·>ô|ð23e²|wRR)))ùO§Òçh^&¼ üÕ߸xúKÌÌ`ýz<¸‹ÿPÊ`Aâǵ}{.ÎËÆW¯òí“|Ý´i‘Ä3€““ÄdzãÁ ÊÅòuíÚ¸´jU¤94%(([[[9zô(?ýô«V­ÂÙÙ¹P¹ôtåLoooúôéÃÒ¥K™3gÎÇ^v™ÂÉɉ ЧOtuuIMMåÀŒ=šAƒªÖ…AGG‡þýûÓ¬Y3š5kF‡Š´–åË—óý÷ßãììÌáÇ©^½: s…˜;w.+V¬àÙ³g\¿~Q®’ª~~~|ùå—4kÖŒÿýïŠk;==i-@©ì”EªU«Æ¬Y³hÚ´)Ïž=cþüù¥½$2ˆ  Kˆ’¨D(Ð Œ±ñò’5‚•ÜŒÿ#/ŠÐÒÒ#Y|4­'HõÓeë •im$d4 •u¾>žøÓSèÒ¶o‡ªU5›çÙ³gØÚÚ¢£óa—¡$CBÔ(ÒƒÕ[EÚ"*|V m+ Ò)›©é„ BÍ©Zµ*­[óìàAêäÓg“™ã~þ¹ÈshkkÓsêTŽÎ˜[.‹cn®kiѨŒŒJÆ ¨|ùò >œÄÄD¦L™ÂÑ£G4h7nÜ |ùò*¯Ÿääd©^½:U5üfggsýúuž?NFFvvv8;;ø<³²²ð÷÷çþýûØÛÛÓ¢E %?[u¼yó^¿~½½=ÎÎÎ*}j¥R)øùùahhHݺuiÞ¼¹Zߨýû÷+ýmddĨQ£ðôôÄÇLJ{÷îѼyóǰ²²RrµÊ-n5åÍ›7,^¼kkk.]º„¡¡¡â˜ŽŽË—/'99™7âééɈ#ÇçÏŸ'NœÀÊÊJÑn``@³fÍ(*wïÞE__Ÿzõê)µgffrýúuž={F“&MhÔ¨ÑÑѼxñ‚ `b’7è:==]qíÔ«WűÄÄDnݺ@DD~9žôõõiÒ¤I¡Ö|ãÆ $Õ®z¦¦¦y> >äÞ½{€l7BÕg%÷ký©ºÔ •Õ#袸«úÄfeq5§ZIO oáµk… èÈÌ„ˆˆP¡/tzr‰†´·pÈ kÁ©_02‚åËaҤ­ÙÃà |Ðö°8ILÔÞ(2#2Õö-é‚(EE",ß­\Éâ«WUZ¡Ó€«5k2ýݘÍеkézCÍšüþã4GQè”ã.òò¥l7è‡~àöíÛ„‡‡+ 2€uëÖñÃ?péÒ%tll,öööDGG+³³³©Q£žžž´mÛ6Ï9 ´iÓ†›7o¢§§Gzz:...x{{+ =Udgg³téR-ZDVVZZZH$Ø¿¿ÒöùêÕ«Y¶l‘‘‘“––†D"aòäÉxxx¨}nªhøöÎ;,Š«íÃ÷Ò‹€Q±€½$ Æ‚5ì…Ø±Cb71‰IÔ¨Q£&Ñ×å ÆÞ’X£ ¨A# Fl(EIDAš"‚ e)óý±îÆ•–ntîëÚK8sæœ3»#û›ç<ÅÖ___òË[âTM~þùgÒÓÓYµjU¡ÏJÎâŋٵkk×®UˆºÈÈHÎ;ÇG}¤xOA(“ˆ•#Fòމ‰aøðá¡§§‡T*¥k×®|ðÁÌŸ?Ÿ«W¯*‰c&NœH||¼¢mâĉüöÛo€Ì-ª_¿~>|˜Ã‡`mm]j+øûï¿_¤jÀ€ w–gÏž1cÆ >¬xÈ*((`„ lÙ²å•`v‘¤¤$ѺDÇÏ*¢²oÄ3))ä¿ðך™ QQ²eF³ar ²xÐ>b´á˜=Úk\zñ ²/ÁòˆçÜä\w%–(ž%Ú̘Qgj×^<ÃÛkñ(+ +´ŠcÛtt˜af&KH^Ž—æÔ© 56渊Ý l«Ðúü2'Nœ I“&Ìž=›ÔÔT~ÿýw¥~‚ °sçNZ¶l©RøªB"‘0gÎîÝ»Gvv6?æÐ¡CH$¦L™¢R´,^¼;;;ž>}ʳgÏØ³g|ôÑG%ηaÖ.]Êøñãñõõ%--]»v‘••ÅÈ‘#ó=|ø/¾ø‚Ö­[sïÞ=ÒÓÓÉÎÎæÚµk 0@­k{•ììlN:…‰‰‰Ìe§ ¸sç}ûö-²Oƒ hÑ¢aaaŠ´b÷^Tz÷ÝwY¾|9-[¶DOOÖ­[³~ýú"­±eeâĉ„……qøðaÒÓÓyúô)íÚµcùòåEž3vìXœœœ ãÆôïߟýû÷súôiºwï®ʳfÍâéÓ§<}úTa. < !!AéµpáBºvíªè÷é§ŸräȾÿþ{RSSIMMeåÊ•8p€O?ý´Ôó¾éˆâ¹dD ô‚Ü}Ã\[›ÎçÏÿ{  :--Z]‹b|‰æ¢óg΀¹y©§(7Ù±Ù<>ø˜üÌâ­EbA”·UVè,àŠTÊçÞÞ2Çx`0”¬Ð[7fsYŸ?~Œžž)));vŒŸ~ú €I“&ðÁP·n]¶oß®´ }þüyîß¿Ï?þ¨ö\¦¦¦¬x)Í©©)NNN$$$0oÞ<|}} V 6oÞ¬pÙpqqÁÃÃOOO‚‚‚ŠÜžOMMåûï¿§iÓ¦ìÙ³Ga!üè£ÈÉÉaΜ9]aa•ckk‹D"QX'»téB­ZµX±biiiŒ5J|W6oÞÌ–-[prr*sº² •J‘H$%Æ€Èåø¬¬,îß¿¯ä>±|ùr:tèÀ®]»˜={v¹|¡åÿº ÉÑÒÒ¢gÏžœ|9áÿK¼ÐÚ¾}{LLL®F•Å7˜8q";wfß¾} ·–€€ n#/3pà@‚ƒƒ ´H©]8JÁ”)Sx÷ÝwéÔ©Qr 5©Ì ÂËii<}‘Óu¨±ñ¿µ³Ë辑žj¸ƒ ºy庮ˆˆµsÑÊIHçñáâų–™u§×ýÏŠç»wïV÷þ“Ìß°õ/„Tp¥m[ú&%Att…½Æ'%q¨U+äwß–&M˜]…¾ÏëÖ­cÏž=ÿüsµ¯ïÀLŸ>>}úàîî^ªL!\_¿~½È>©©©„……amm­4¬_¿>€ÊL!ò*qqq²FKKK¥">rTµUiii <˜ÜÜ\þøãBqLMMÕ÷gdd¤R•øàM@Ð¥ <_”•é/ÐÝML¨)wßÐÑOÖ¥¥¶EC¸ZB©D ´¥–e_ŽºA„B@²g2©¾©ÅöÓoªO—:Õ^Š»¼ˆA„egþ† |geÅ•Fè[IåëÇOÊ!~nܸJ­Ï¥aöìÙ¤¤¤pôèQvìØQªàA9þþþ˜››3cÆ ¥ÔpîîîEžã­Âß\ÞÖ´iQ‰áwÞduuêÔQùRõ·ÂÎÎŽ¥K—J÷îÝ9pà€Z–³ßÿ)S¦Ð½{w<==«Ìïùe¦M›†––«W¯.2ðoÓ¦M<þœÙ³g+ÚÚ·oOÍš5¹téR¡óäÙ35jT!klÕª ús=ÿr¬MÐÓÓCKKKá’RVrss5jxxx¨t÷;w®Ð1y–޲Tô|“ƒKFÐÿqîgeú"7íPssðò’èÖ TäæT==CHœ þEœŸ kJM­²( B®ÀãÃÉ,ÞR]£m jO¨†ŽxK¿ÍÔ«W={VŠõYަ¦&|öY•[ŸKCÿþýiÒ¤ óçÏçÞ½{¥¶>˜˜˜ðäÉE°ÈÜA<<<Š<çÞ½{JÕÔÔTNž<‰••ƒŠ)èÔ²eKÈ–-[¸råJ¡ãñññ «jTT)/b>ä‚€T*-¶”œãÇ3aÂ:uêÄéÓ§Kü .\ÈܹsK·´´jÕŠÙ³gsçÎÆ§TÕ`×®]¬^½šÆ+¹éðá‡òèÑ#ŽÈóý#{ÜÝÝÑÓÓ+ä³\V† BóæÍ9~ü¸’Åyûöí$öêÕ‹¦M›âíí­pKÙ½táš6mZêL‘7.ˆPnß¾ÍÍ›7¹}û6999lݺUeߨØXÜÜܦU«VLŸ>]a ‰ŽŽføðá8::²råÊ*»†Ò T}0;ÂÂd¿”×ú–=‚oCÂ1èÔžáºp¹9<ÝŽPww ƒ”üÌ|xDÎÃâ-J&=L0u·ßDd|_DéãŠdüÔ©•êÏY^$ 3gÎdÁ‚eœ6mš¢´õðáÃÑÕÕÅÝÝ™aôèÑ 4ˆ‘#GR³fM<<<ˆå·ß~+ÑÊ»uëVúöíK=øàƒhݺ5Ïž=ãÎ;øúúrôèQêÕ«ÇÙ³g™7o  Y³fäççsåÊüýý™7o^‰neÓ¦M#//‡ªtƒX½z=ð“/ IDATµÂ—`ß¾}¤¦¦*2žÈqppPˆz©TÊ­[·E9úöí«V>êÿýïH¥R¶mÛÆÅ‹±··ÇÔÔ”ÀÀ@þþûoÚ·oÏÁƒ ]Ó¢E‹¸xñ"ãÇgݺu4oÞœcÇŽ‘——ǦM›*Ìšª¥¥Å¾}ûøàƒxï½÷°³³#55©TÊW_}źuëJ¬ÚX `éÒ¥XYYakkKݺuKµž={077çäÉ“…‚Û¶mË×_¶¶6nnnŒ9’:0jÔ(AàèÑ£äççãææ¦äÂãçç§(MKKdï¹Ü½nçÎtïÞ½Ì×-òfðÆ è¤¤$Ú¶mK5066&>>^¥€ŽŽŽæý÷ß§víÚŒ1‚ .н{w¼½½iß¾=ÖÖÖŠ@ Í H5QYùrÝT_Ÿ/o©•²|÷ˌۃÝ\¬BL.pOφ4¬kLó¦ÔÒ‰ä8::fåÚú,®a^jI¿%‘›\LPÌ™aÜéÍJ€/V",•-žAö7ÁÐаÒç‘Ó³gOôôôTV|+Š¡C‡²`Áµ‚6lˆ³³3¶¶¶Jç{zz²qãF|||x÷Ýwùþûï:t(€Ra“:uêàììÌ´iÓ˜9s&;vìÀÏÏ6mÚ°sçÎB¥®Hrr²R›µµ5·oßfýúõ\¾|™ƒbbbB³fÍØ¹s§ÂªÚ¿¾üòK®\¹Â‰'055¥ÿþ¬[·N-Q3vìØBÖÞ—©U«–ÒïNNN*û·k×N‘ýãÕB"êfsÐ××gëÖ­Œ1‚ãÇLxx8mÚ´aÚ´iÌž=[¥@­U«/^ä‡~àòåËáèèÈܹsé©fåÙÑ£GŠç=zt!ÿáÎ;ˆ»»;áááØÚÚ2dÈÅ÷êË[ýíÛ·ÇÙÙYeaUó}óÍ7Œ1ooonݺ…™™Y±k®W¯ÎÎÎ œ‹ìÿ²(0`7nÜ`Íš5Š]Ž!C†°hÑ"Z¼’­ÊÄĤÐgúêñ7±aÉHU‘ ÿa¤R)‘‘‘´hÑ‚µkײhÑ"•Á.sçÎÅÃÃÐÐPŒŒŒÈËËÃÞÞ++«"Só\¹r…Û·o³dÉ>ýôSZ¶l‰““S‰krqq!&&† .”ûú^&#?óË—‘ðyýúlüê+8q¬¬ œA$ñéñXo²&¯ ¦óE³ílØPA Á矮²¡4QJÒþ$òÓ‹Îñ,Ñ’PkT- Z½ž[èåÁÅÅEôƒ~ÁÞ½{ùöÛoK]L¾ýö[V¬XÁåË—• JˆˆTöööDEE•Û•C¤êqqqŠ·éÓ§ 6¿‹ŠásÕÑÑ¡U«VJA/ªøí·ßèÔ©FFF€l«ªwïÞüñÇ*Ó3,ÿfjj*óçÏGWW—g/Jg«Ce8äÿ™’‚ôEÉP[ + xjgàN™x˜IeìV© "̺ŸEâžÄbų†¾µ'×~#Å3ˆA„"å'00 60fÌQ<‹”‹ .°ÿ~’’’(((àÁƒ̘1¾þúëê^žH%!–Ì' ÕáÉ“'¤¦¦*Š È±µµ¥   Hk—ƒƒ_ýµâ5}útµçtwwgĈ¸¸¸(½^Íù«nÛøñãÙó¢m¬¥ÅûÿüƒKFwAI@—eŽ|!Ÿ;á8pç]ˆo¯ÐåYsIm!<Úÿˆ/Adr¤R¿¯ŽEdr$Z&ZÔù°zÖz•º±íõh“çQ{÷î¡««KûöíÑÑÑaýúõÕ½$‘ÿ8111Lš4‰:uê ¥¥… »wïfÁ‚|òÉ'Õ½<‘rðòß]¹&iÚ´)#FŒÀÏϯšW÷úóƹp¼Ìš5kTºp„„„`ggÇ?~¼¢=00öíÛãééɰaÃ*lêl—”–éóç³?)‰¬>bt­ZüþË/°v-hjBr2”£4êÉ{'q<è(ûÅsÍ3¦)b+‹gWŸ‘òg s7j[jSgR4ÿÛiêDÔGtá(iiixxxаaCzôèQå¹EÞLâââ áéÓ§XYYѺuëB¾â"ÿÔÑ$•¡[Þ4Þ¸ Bu©¼E/ÿ½2¾t*2ˆ099™S÷î‘%•BZC[¶yþçÎË%ž¶ÝÜ€DjŒð÷xzL.ïŠUµµ5éÒyvµxw=k=,Ç[¢¡÷æošˆA„"eÅÄÄDñÅ'"RQÔ«WOQQäí@ ",™7_¨ÀÊÊ €þùG©=$$DéxEâëë‹‹‹ Ç/÷X‹þ÷??øÆŒAòûï ’JáömÙÁrú??H{€W„,—´<¤†•âÿ àö“>(Q<´6 öäÚo…x±¡ˆˆˆˆHõpüøq\\\EyDŠæ­´@S¿~}îܹ£Ô‚¾¾~¥T$0`@…l…$''s."† @çØ14Nœø·C9ôŽ€/ª[̨]SÀ×ï}Möýâséu2Â|9H*~ ¯+â–™ˆˆˆˆHu0|øp†.îd©ÁÛaÒSÁÔ©S¹zõ*<d³þøãÆŽ[ª\«UI‚TÊÈåËyð’¶tÔ(ÉW­Zо}™ÇÏ+Ècw¬8ŠEVWH²¥vm(¦o™ÈOÏ'qO"ÙQÅ‹gÓ¾¦˜~»Ä³ˆˆˆˆˆˆÈëÏ) ;vìH:uøþE9ß:uêP§N~ùåEŸyóæñî»ïÒ©S'ÆŽKÛ¶mÑÓÓã»ï¾«”5ùùù•É…#0=•ÑÑt  ž——î߇—,äBãÆœ“HHèß$eW›'ÂN!ËzPà? ¨xësöƒlâ·Å#M”ÙG¢)Áb„&Ýßüdõ""""""¯ r1 Gɼ‘.Ë–-ãùóç…Ú;vì¨øÙÄÄ„sçÎáååEpp0#GŽdРA•VaÈÄÜ\­­ù¬‚Î?}Ê©'O8õä q/þþ;ŒS蜘ٳYôÕWì(§û†þ˜E;w–y}Û¶#¼HÂl! 44„¶mË<$¹É¹Äïˆ/$ž– \¦ÏZfZÔ™Zç­Ï ŠˆˆˆˆT‰‰‰¿¶±`¯o¤úu¤@"aëÁƒÜiÑ‚âUä…®«£ÃPss†YXÐ×ÔýåÈ“““9uý:‚­-ÄĨ_ÐÖæÔõë$''caQ:;´4_ÊÞà½ô²éÅßG[²”Ò* æj“y'“äÉäÛO·¡.–N–hŠRDDþ‹ÄÇÇãééIïÞ½iÑ¢E‰ý===IOOgâĉU°º¢ÉÈÈ@OOOåÎ`uPPP@NNúúê¹°––FÍš5‘”#þ¥$‚ƒƒ¹víãÆ£f9ë ”–­[·Ò¢E z÷î]l[iðññ!,,ŒY³fUÔ2• ÃÇÇGGÇJI‹[™DGGsíÚ5133«îå¼Ö¼5Þ¬­IëÚ•“·nAÝºŠæv5j0Ì‚aææ´72R™p">>ž‰ýú!¼È¢ 4¼d9›7I¿~ÄÇÇ—Z@ =ÆãÌÇLn5‹i/Òc—Ù}£R¼Sxv¥øüÎÆ1ëo&î…ˆTIIIXZZVª J¥dffV™˜øòË/9}ú´âw===¬­­éÖ­Ÿ|ò ¥¯M›6jw:þ|‰E4îÝ»ÇìٳٳgZúÇ$::ºZ´‡‡À××—ääd455iÒ¤ óçÏç£>BC£ä?F999?~œ€€ˆ§M›6:t¨ÔëÉÏÏgýúõ?~œ²²²hÖ¬]ºtáûï¿W)À&OžŒ¿¿?‘‘‘äç磣£C»víX¾|9ƒ *õJâÌ™3,Z´ˆ^½z)îùàà`‚ƒƒ=zt¥Z+gÏž³³³’XVÕVöíÛǾ}ûÔÐ)))xzzÒ©S'Z·n­ÖøW¯^eöìÙ´lÙ²RtHHÞÞÞBNN;wî¤{9|/;wîLçÎ ®À•¾™ˆºªÈÍ…=¬XÁÀqãp´´d¨¹9õÕ¨ôcggÇz;»Â&O†ÔTYåA77Yï2°õæV, -14yåó=J?V~F>>&;ºøu:¤µM£~ßú¢x~1ˆ°|LýòK&‰ÓÈ‘•6‡ëöíD'$°ùE¦ŸÊ&..ް°0&L˜€‘‘)))xyyáééÉŽ; ÄÈÈHíñÚ´iƒTúo&œ¸¸8üüü°³³£U«VJ}utt*ì:^æÍ›GFFC† ¡U«V„……áááÁôéÓ‰ŠŠRdo*Ž„„Æ@“&M¸ÿ~™Ðããã;v,—/_¦G,\¸333üýý9pà'Ožä—_~aðàÁJç9s&L˜€—/_ÆÇLJ!C†pôèQFVâ½/çøñã¬X±‚^½zUùv¿““öööU2WLL ~ø!7nT[@W6l`ß¾}bbbB||<2¶X‰°dD]U$'ÃóçèöîÍà€fMªö©/^dÁĉ´24T>)û7?Z·&ôùsÖîßOÏž=Õ;49”¿üÀ‡m?䪷ìËRKKæÂQrbsxtäùéùÅöÓ¶ÐÆÒÉ’¿ÿ‘o»~[å[‚¯;baÙ‰ˆˆ 07—˜;;bD¥X¡¥R)»¼¼È­QƒÔÔÔ*½¿ÿþ{lllÈÊÊbܸqxzz²víZV­Z¥ö8/§ô™òóócüøñ|ýõ×¹ä׎ 60xð`%7‰ 6РAV¯^ͼyóJÜų°°àìÙ³tèÐ333ôôôÊ´–¹sçrùòeÖ¬YÃÂ… íü1sæÌáƒ>`âĉDDD`nn®8þèÑ#¥{{þüùœ;wŽþýû³~ýú*ÐÕIY,ýo"sæÌ᫯¾¢uëÖ¬]»–E‹•{Ìììl²³³‰‹‹«”¢ro¢€®*ÒÒ`ï^²;vä›äÏŸ~b››Z[-={öÄÚÜœ·o£­ªCz:¹ééL´³+•x†S×I0£ý ¦|+koÛVD¨.Ï®?ãéŸOò…bû´2Àb¸ºlÚ´©Tk}[ÅsÙ™÷ý÷$ŒCF@G<<*Å íº};Ñ}ú µ°à› p+…p­HôõõùøãñôôäæÍ›,]º”š5kòå—_êÿÏ?ÿ°cÇF­Ößüü|¼¼¼8{ö,÷îÝãùóç4mÚ”1cÆ0äE%TUðË/¿LóæÍ™9s&:tPëš²³³qssãÆÄÅÅÑ¢E &MšTh«>++ ___¼½½¹víúúú4oÞggç-“£F*ÔfffÆ„ عs'!!!%ºÔ¨Qƒþýû«uMEáëë‹»»;}úôQÏrìííYµj3fÌ`åÊ•¸ºº*Ž©z0ìׯæææ¤¤¤”8wVV‹-¢gÏž…²P]¼xæÏŸ_¤ëή]»8sæ +W®ÄØX >lØ08þ<'OždñâÅøûûsòäIBBBpuu¥cÇŽ\¾|™S§N“'O°±±ÁÁÁAmšÏ?ÿœN:1aÂ¥ö³gϲÿ~bcc騱#}ôÑÑÑœ9s†eË–©ôé=pà§OŸ&..޶mÛ²|ùrLMMð÷÷W¼ïDGG`ccÃçŸ^â:åÈßï¢èÖ­c^JQÆÖ­[ AOOöíÛóùçŸ+Ö%§S§Nj¯A]|}}9sæ ÉÉÉ¢€.qó¼ªhÙ>ùìíÉ5ŠØÔT:—ÂÄ;eÙ2ö³E»ÏȈ)Ë–•jIYyYürKf‰êפõ óâ{Xmÿçi=&Å+¥Xñ,Ñ`ÚÏK'K4tÅÛN¤â‰ˆˆ P* Òø~çN¡øºÒ"•JÙuæ 9íÛ#X[óGX©©©:GioËÝ1?~Ì‚ ˆQl¼qãF~úé'¬­­ÕûñãÇ8::H:u°´´ÄÝÝ¡C‡òÍ7ߨ<çâÅ‹ôïߟ¸¸8š5kÆ©S§xÿý÷b«8bbbèСóçÏçÆÔ«WÇãààÀÚµkýòóóéׯŽŽŽ\»v¶mÛbee…{öìQëÚTñì™,f£ªRwýþûï|ñÅEöqvvÆÌÌLÑ·8Î;Ç“'O>|x‰}srrpuuåâÅ‹…ŽáêêÊãÇ‹<ÿÑ£GŠûþáÇDGGMZZ7nÜÀÕÕ•µk×2iÒ$bcc133#33=Äœ>}cccš4i‚Ó§OÇÑѱĵ¸ººòçŸ*µýøã 4ˆË—/Ó¢E îÞ½KïÞ½Ù¹s'®®®ŠÏ÷e–,Y¤I“HHHàáǸººÒ½{wòòòYi||< ó…–_gBB‚Zë”SPPÀÝ»w ½|}}quuåÂ… о'Nœ }ûöüöÛoXZZ¢¥¥ÅÚµkiÓ¦ wîÜ)Õ¼eaàÀlÚ´©RÄù›†h®²{÷&ñܹR3tÔ(Ƭ\‰³ +t.p¶Q#~WaY)Ž#ÿáiöSfu˜Å +RG@ç>ÉåÑáGä>Ê-¶Ÿ¦¡&µF×B¯QÙ¶9EDÔaÞ÷ß/·¦iiñ÷;ï ûÃh”Å™¿òOœ ¯W/Åïу³dýz6WRӒؽ{7¶¶¶€,¸jÛ¶mìܹ“•+W*ú¥§§sèÐ! Dƒ Ô»fÍš„‡‡Ó¤IE[nn.ŽŽŽ¬[·Ž3f›{÷îåìÙ³ ëlBB:t`Μ9„……¡­­r ù&ß¹sooo™¨4iK—.eÔ¨Q4iÒ„Û·osùòe¾þúkV¯^­4Frr²Z×ö*ÿüó'Ož¤cÇŽJ×[™„‡‡ðî»ïÙGGG‡æÍ›síÚ5ÒÒÒ”ü¬ÓÒÒØ²e ÙÙÙøùùqåÊ&OžÌW_}Uék_´h999¬X±‚íÛ·+ÜŠ^eÿþý:îíí­tÝ‚ °`ÁÖ¯_Ï™3gXÊ¢` ,Y²„:àããƒá‹íÓ#GŽàääTäyþù'÷îÝ£iÓ¦0þ|~üñGŽ=ʸqãèÓ§fff´k׎©S§–Êêü2†††…"ÓÒÒèÖ­¦¦¦Šq333™={6&&&øûû+v.]ºDÏž=™;w.çÏŸ/ÓD*ÑXE˜ùùÑÑÅ…Ž..˜ÎžMmcc®]»ÏŸCB„…ÁpþA*•rýúõBçØØØ(¹6Ô­[GGG¢¢¢ŠµBGDDàîîÎ!CâÀØØ˜?þ©TŠ»»»b]€ÊTo¥Í@ðüùsÆG^^;Ë‘K¿´Ü¿‰DBýúõ‹í'H‘ n9OŸ>eÑ¢E¬X±‚óçÏcff†³³s™ÞƒÊbذa*Åõ« ‰„O?ý .]ºTêy:Dvv6ãÇWˆgYº‹ îœ>}:M›6@CCCáFqûöíR¯¡4äåå1fÌÂÃÃñððPd®9qâ 8::*¹ÏôèÑ;;;.\¸Peßêféy›-ÐUD³çÏÙûü9×%„š5qIJ‚^½d€j2¸€Â üYëóí¤Û\{x €©í¦¢¥¡¥ÐM›BíÚªÏ R/¤’v9 JØ7êh„Ù@3$šª¹ÜÜÜøö[1ˆðUÄ ÂÒ£d}–£¥…N—. £U9ýUüûKƒóê#_UZ¡å® ºººX[[óᇲlÙ2¥TY³gÏfÊ”)œ>}Z±%¾}ûvêׯ_(›CIܺu WWW‚‚‚ˆ‹‹C*•*Üb"åAÌ/ѯ_?•m[·n-$_&44mu¿ĘŸŸD"QôiÕª;wfùò帻»3jÔ(œœœhÞ¼y©® d>ׯ ãÎ;üöÛoØ©ÊvTI˜šš"Ïž=+äÛú2ò‡³W…± ‚ ‘‘··7û÷ï§oß¾,[¶Œ+VTêÚÕ¥(Krll,®®®üõ×_<|øPáÚQPP ò¾* ù½5`À¥vMMMzõêʼn'Tž÷ªÿ·ÜW?11±Ôk( sæÌáܹsìÛ·O)n),, Pýÿ¨ÿþܺu‹ÈÈÈJÍÒ”œœLrr2QQQb6¨t‘ xgï§OKw²¾>ƒ±1Îùùìyð€/„÷žrZŸ5$Lo?A€+WdÇŠ²>ç?‘¢.ªøum æCͩѦøÔFb¡jDñ\:TYŸåHûõ#êÇ9>sf¹2rH¥RÚܼIž U¡Q#¼þø£J2rDEE¹].g̘1Ì›7íÛ·ãèèÈÍ›7 bùòåh–"Õ¥¿¿?=zô nݺôéӇɓ'Ó¨Q#øøãÉÎ.üw@Õ®¼­8÷ ¹`yôè‘Êü³ýû÷Wºn___~ûí7ÜÜÜX¶l™"ÿñêÕ«ÕÁR©”‘#GâëëËÎ;?~¼ZçUÍš5ãæÍ›DGG+ £££Ñ××/Òw½F >œAƒqéÒ%¶mÛÆ²eËJõYWªÒ¾%&&booONN `øðá4oÞ---Ƨò¾* ¹³ªœËÅåa®U«–Òïò‚:Åÿ*k×®eÇŽ,]º”)S¦(“û÷ÿèÉ“'•¶6í€úùù‘›[ü( « [ …†ummÑj×N!ˆ•^&&ªÛ_rÛ Œ¶³ãÃþ´±áh)­ÏÏsŸ³?d?ƒ› ¦qþþäº^•€Îyø"Eݳâ-æZ¦ZX:Y¢SçÍÊ+òú¢Òú,GK‹è6mÊ‘Ãuûv¢ŠÉÌPݾÐ/£§§‡‹‹ 7n$66–íÛ·£©©É´iÓJ5ÎO?ý„T*åÈ‘#JEr¿kUøùù Šó{±µU\pž<ÚÆŒLŸ>½Äµéêê2uêT¦NJdd$GŽaÕªU,\¸/yq©bÈÍÍÅÉÉ ///¶lÙÂG}Tâ9Í{ï½ÇÁƒùý÷ßi×®Ê>·nÝ"<<œnݺ•ø¨««Ë˜1cpssãï¿ÿ¦M›6Eö•çöVåzTÙjû÷ï'!!íÛ·+}ÖevhÔ¨.\(”ÂÏ××·Ìk­hŽ=Ê×_Í„ ”bäÈ/]ºTèáCîÚ¢n CYéÞ½;Ý»w] Õ@ô®Bö7mʤ›7aÏpu…U«`þ|˜9ƇÁƒeêÕÎllÀÌLe-m—åËÙmdÄn##\–//õ:„àYŽì‰}VY%¦—ýŸ_޹òž]FâžÄųAs¬fZ‰âY¤Êˆˆˆ 3Sö )•ª|¥÷èÁªmÛÊœ‘Cž÷9çwŠœC¨WÓÿ]­9^fÖ¬Y‚À¦M›8xð ƒ.Ñ×öU¢¢¢077§}ûöJír_dUøøø(ù^ƒ,;ÈcQ´iÓ===Ž9Rª5‚¬É¢E‹pttäÏ?ÿT™máeòóó™0aÇgÓ¦M•Vι$fÏžM½zõpss#66¶Ðñüü|–,Y‚ *Å–*nݺPb¦ÌÍÍñññ)4çñãÇÕšKî/ÏR¡.QQQ…Òw_•„<-ãÙ³g•Úãââ®?eÅÚÚMMÍR_ç«\¿~)S¦Ð­[·"BåÿGΩH2pîÜ9´µµ_»b.o3¢ºŠˆÐÔdÚܹŠ-¢ò0tÔ(F½ðq;VJë3üë¾ambÍ ¦²²¯ò¸ sshÒ €ç!YdÞÍ$3<AZ‚ð@Í^51yߤTÛ䨨ØTÈ{ò&!V"TŸÛÿüC;Ú““™™É’%KXµjÚÚÚxzzrúôiúôéSlZ, –.]Ê’%K˜3g?üðƒÂ&99™`oo½½=çÎ#**бc¢˜‹ IDATÇ*ú„††rîÜ9,,,9‰‹ÂÅÅ…£G2vìXìííeÁÜ/ѤI¥­}RSS =),¦r_dùXfff%úd²iÓ&ÆG‡زe Âüùó9þ̆ ˜9s&÷ïßgãÆ%>€È0`‰„mÛ¶add„¥¥%5jÔP âS…\®Y³†5kÖ`bb‚ŸŸ»wï.³ÛÉ€èܹ3»wï¦E‹ :”¤¤$E.븸¸2 2_u{{{NŸ>ÍàÁƒiÙ²%:::*sJE\\ŽŽŽÔ®]›C‡!‚’«Š––ZZZ888(æ:~ü8Ç'??Ÿ~ø¸¸8>ùäj¿ ôøñc…ϸü!ìîÝ»ŠÏßÎ΃2]·X‰P ‘JÇÙÙY¨e` äææVؘ'N=ZêónÄÝøo¾ûë;E{³ú¹B ž _¿—(D¯Œ¢–G©õz°æžY¦køì³Ï„§OŸ–éÜ7ggçê^ÂkÞ={kkëê^Fµãää$BTT”Ú縻» €Ð A!??¿Ø¾ ¬^½ZÑ#Ô«WO###ÁÈÈH077Ž;&ÂòåË}}||@X»v­`kk+èéé æææ ØÚÚ ÑÑÑJóõìÙ³Ðçš››+,^¼XÐÒÒ$‰P¿~}¡fÍš èèèçÏŸAvîÜ) a,,,3337n,\¸p¡Ä÷ÅÄÄDq¾ª×ž={”ú×®][ÐÕÕ-4޵µu‘c|ðÁ%®CŽŸŸŸ`cc#‚D"ôôô@ÐÖÖþ÷¿ÿúì<¨rN‰D"Œ3FHOOWkÞˆˆ¡aÆ èëë €Ð½{waÕªU )ú®^½Z„ÐÐP¥1>ýôSÁÒÒR±ùýSTA„ììl¡cÇŽŠÏÕÂÂBÐÖÖ~ÿýwÁÄĤÐ{ú›¨ª-99YpppP¬EGGGX°`°páBRRR}…¢äª±>,¼óÎ;бíííUž+gÏž= øøø‚ ^^^ÅÞs3gÎTœ.¼÷Þ{ ˜™™  Œ=ZHKKS9OQ¯—?×qvv.ñ{¦wïÞâwQ H¡‚+ ˆÂÅÅ…[ìùõWêÔ©C:uªm-Ó<§±+hZZD9GakÄÃË™¬[˜ƒè׺vUo,º:X:Y¢US´ ‹T{÷îåÛo¿UT{[¹uë ôìÙSeú6U\¿~Î;³bÅ –•hüèÑ#iÑ¢…Ÿd…$ÜÝ݉åÝwߥ[·nrñâEš6mªH–’’‚¿¿?¶¶¶Ô¬Y“¿þú‹[·nѺukúöí[È æïïOVV–ÊÊ©÷ïßÇßߟððp iÔ¨J–刈®\¹ÂÇ155¥ÿþjço>þ|±R¶¶¶J)Ä|||ÈËË+”áâÅ‹dee©£víÚEú5«";;›[·nLjj*mÚ´¡}ûö…‚ÜäÄÄÄð×_KAA5¢cÇŽ4kÖLí9A– ïôéÓŠÊ}=zô 11‘ÐÐPºuë†Ñ‹â]QQQ„……Ñ£GBfA¸u뉉‰Šû§¸þ s9qâ¡¡¡4nܘnݺѰaCE:¾—ß»3gÎP¯^=E®ó¢ÚäÄÇÇ‹úúúŒ9’ .(í „„„§2KHqc?|øþù“b ¡ÅÅÅB§N033#99YQ-T 6TrÍJ¥øøø(U"ìÒ¥K‘óÅËŸá˸¸¸ªÖILLTTnƒÚ‹FÐU€‹‹ wîÜa„ tîܹT+’´œ4ì–Ûa‘h£¦#Î œøûo8vLÖgêTPÇU²F»˜1G¢UöÌ""%! è²3lØ0þúë/ÂÃñ´´¬î刈T*‚ r! ÃÎÎŽ™Êîm¤8}íÚ5®]»Æhݺµ( ‹A4V­[·.s£rQٲɼ›ÉŸþIŸà>ô°û×OS^õWK ŠÉø€DK‚Ù 3ŒÚ]V\DD¤zHOOçÔ©S\½z•S§N±aÃQ<‹¼¬Y³†àà`Þÿ}jÖ¬Ihh(›7oFSS“~ø¡º—÷ŸAnäS•NRDQ@WUYÕ§ ·€ìH™hΠˤ K–Ó2(<3}3›6Vô— èzõ@£ˆ¼,šFš´0À¨ƒQ…eÙƒU#Š”•¸¸8&L˜@ƒ ˜;w.Ÿ~úiu/ID¤Jh×®gΜaÅŠ¤¥¥Q¯^=zöìÉÚµkËTdçmG ",Q¹TIII•:~AV™÷2É Í$+2 !WÙ3'öY,ž? }ÝÓRåäÀ#Y3¯¦iÕ6ׯ ¥­ Щ§S®Bª+ªF¬D(RVZ¶lYæt}""ÿeXdåC‘Ò“””TlîvQ@WææÄmŽCCW‰Ž ] 4t4èJmšºšHt%ª~q/iØügùdÞÍäyès²dC1Å“nÆË4%š´­ÓVÑ òïÛ† A×Jý–ú¶2D»–¶ª¡* ±¡jDñ,""""Rˆâ¹dD]ERÜÇå/©¡#Û- y©yj“•—ÅDzêR­jµÂ@û߈ø˜X èòPÀ×`Z_¼%DDDDDDDDŠCTKÿ1 ¤ -Ý9·o‘W Û¬: Ñ– ßDƒ–ì÷Öçš´±ÓÒ*y+KyWwï°Åo 7cŠÎYYÜŒ¿IŽV9Írè0£ 4Ärœ%:­kp%@Vù©:ЧEDD—§žýmâîݻս‘·???Ö¬YCPPPu/åµGÐU„®–.ý[ö§‰…zÉþ+-c-X?à`óƒîvû©ö¶2D¢-s¤¾yäÕD»w¯²e)pss###£ê'~ÍY³fMu/ADDDDä-¤eË– >\í‚Mo3¢ GÑÄ¢I•ˆgm m Z`ÐÒ+¾:öñ¦ñèké3¥Í¥¾~~ÿþ\Z "TD(""""RXXX`aa!¦RUQ@Wz õ0dFAN‚T  § øŸsY5û’¼’9ÃâßÌž?Âã®cß‹©ž©Ò©rmc#Ë-""""""""R2¢€®"´Ì´0¶7VÿA0XœÈ–hJÐo¦–±êqOð¤ù²ˆÃ™f*/Àå˲Ÿ«Ãú,"""""""ò_EÐUD©+JåŠÖ-››º€Àö€íØÕ¶£Ký.JÇCC!%Eösu h±¡jÄJ„"¯Arr²âw===¬­­155-æ¬7›ððp‚‚‚ˆÇÌÌŒFÑ©S§"«§ ‚@dd$AAAûLxúôiõLþãìì\ÝKxmسg`mm]Ý˨vœœœdN]J¯V­Z §Nª´y>>;vŒ)S¦Ãðáùy³rÒcÊ-¨111•2~Yøâ‹/8tècÆŒáìÙ³¤§§óèÑ#¼½½?~|¡Ô˜W¯^¥]»vüóÏ?¸¹¹Ezz:/^¤S§N|öÙgLš4©Bט””„““Ïž=ã—_~!%%…Û·o3iÒ$N:Å—_~©Ö8sçÎÅËË‹èèh’’’8räÝ»wç×_eéÒ¥jˆ¦¦&ÎÎÎ 4¨<—%ò!V",qïü $>=ž“÷N0Ávƺ…}¯åº[7H ùOQ]@nrù+}–Ú:Šô•Içα±±`äÈ‘4lØï¾ûŽ-[¶°k×.¥¾Zµjùþ\¿~€‰ »±É±µµ¥]»v#•JÑÑ)ÿƒ½½=666ìØ±ƒI“&ѬY3¤R)›6m"99™%K–”iܘ˜Ö¬YCff&ÎÎÎå^§ˆHQˆA„%#*—*⯿þbÖ¬Y :”¡C‡VÚ<„ÿAì3™5iV‡Y*ûÈ´žtèPiK)777¾ýö[ÑúÖ¬Y#úA‹‹ \»vMQŒ¨wïÞ|õÕWäååáååEëÁ ½zõ"%%…}ûöáááÁˆ#HLL$""‚/¿ü’Ï>ûL1®|€zõê)\"Z·nÍðáÕÖpôèQBBBøæ›oX´hÝ»w§~ýú…\)䤧§3kÖ,Ö®]«t-sçÎ¥  ¬¬¬4hï½÷sçÎå×_UXÃþùg¦L™Â’%KX¾|9ݺucÀ€̘1sss¥ùÂÃÃhÔ¨Q±ïg£Fàþýûòð*‘H¸qã .¤yóæ¼óÎ;ÄÅÅ¡««ËÁƒ7nœÚcÅÅÅáàà@VV1114lØzõêUîuŠˆ¼Ê©S§8uêýõýúõ«îå¼ÖˆA„UDŸ>}Ø´i¬Ôy¶ÝÜ€±®1ãÞUýGZ. ;v„ 0¶”1ˆP5¢x) Z¶lIݺuéÚµ+Œ1‚9s晙ɥK—°±±Qˆg9Æ àÌ™3Ô®]›FqæÌ®^½Z¦µøøør{èÑ£G‘©á† ¢ôûƒð÷÷g̘1 ñ,g̘1hjj*­±ÿþ}GGGî޽ˉ'ÔËÂÂBñPŸŸÏ‘#G˜0a?æôéÓ²^9ZZZhii•)˜ømCÐo;wP ªƒ®^…YQ@‹ˆüÇX¿~½" GyqvvfôèÑìÛ·'Nðÿ÷lÚ´‰/¿ü’uëÖ•x¾¾¾>999dee¡­­­tìéÓ§*Ï122*ÔWžvnĈt/âÒ«Áx/cll̬Y³>|8 4àäɓ俿¢­­M³fÍ™H·µµUy¾ „††¢££Sa©»Ž9‚ |úé§ «º††Æ £K—.œ9s†ÔÔÔRïÀijj2~üx6lØÀ¹sçHIIQø¤‹ˆˆT-¢€®"*;ˆ0¯ ]²4V]tÅÖRõ—…Ü}CC^1U9b¡jÄ ÂÒ£ßDëo¬«m~‰fõç‚lÚ´)‰D‘!ãe¼½½ÉËËSJ9†††Ì™3‡9sæðèÑ#FÍÏ?ÿÌÒ¥K166VˆÝüü|•óåååqñâE…‹@ZZš"xOZµjÈ„°ÜϹ,Ô©S‡æÍ›sçÎÒÓÓ133cèСüïÿÃÕÕ•±cǪ<ÏÃè¨(F¡aá]»²pëÖ-€B)úämW¯^åîÝ»…¬÷êÒ¦MHLL´H¥ –Œè]E$%UnŠ­a'HÈùÄ<ÿ èwß…êv?vss+•/àÛ‚:¹]E^A-Iµ½¨~ýŒ¾¾>½{÷&::š;wî(óôôþõ?~µØÈ2ytíÚ•ÌÌL…«GãÆ177çîÝ»…ú?€Ã‡+µ9rDåøEѸqcÚ¶mË®]»xøð¡Ê>¹¹²ßùùù*üüü¸sçuëÖUˆÊîÝ»ãääÄ•+W”åÜ»w¯¾ú ,X öšKBþ¼oß>¥öŒŒ Ž;@‹-íAAA:tHÉûÙ³g*ÇŽçÔ©S)É¡C‡.1""塲5Ë›€hú«"*»ª}ú(‚ KBCCƒ]»vѽ{wÞ{ï=æÏŸO›6mJ¥DFFrøðafΜ‰³³3¹¹¹ôïߟ֭[ãèèH‹-___Ξ= ÀªU«”Æß¸q#ÑÑÑ,\¸óçÏ3hÐ ,--¹yó&{öì!##ƒíÛ·«Ì””ĬYª _|ñ…J 3À¬Y³øùçŸÙ¼y³ÂºÉÁƒÉÈÈ`îܹ˜šš*úïÛ·WWW¼¼¼æ`ãÆ >222¸pá—.]âùóçìÛ·OÉOõܹsÌž=›ÿgï<⸺ü.Ý ( (X±"±×Ø»"ƽÄ[ˆ¦c”øi¬Qcì1MT[ì(ƆŠAE¥(ˆŠŠ€°Ôù~°îR,ê}ŸgwïÜrfîž¹÷”E‹É«ú‘‰Q¥Èûûû°jÕ*¼¼¼øâ‹/°±±Éþ?IðÎ!2æŒP ßÇñûÇá4íΊW®@bbÆçâ @ ‚Üaff–«,ÎÎÎ\¼x‘?þ˜‰'’ššŠ¹¹9nnnj;666ØØØ°`ÁP(4lØÉ“'3iÒ$5³ª… Ò°aCŽ?ξ}ûxï½÷4h›6mÂÉÉ ///vîÜIóæÍ9~ü8_}õ•F²råÊÉñª_ç½÷ÞãÆL›6ùóçË;S¥J•¢K—.4j”ÏÞÈÈ:IJeËdÓ8œYµj¨/ X[[ãëëËÂ… Ùµk³fÍ’MRÌÍ͹råŠüÂKKK^¾|)+š¯3|øð,h+++|||øôÓO9tèì0X±bE¾ÿþ{ÜÜÜÔê—-[KKKµ-s'''¬­­Y¶l™¼o``@£FøòË/5¢™”,YKKK(áááj×`iiÉ•+W¸rå ‘.\(ÐAÞQH’$éZˆ·•]_a­,ºwgñ¹ŒíÉ۟ܦVùZZë-Y³gg|‡+ÐàÍÆÎÎŽ)S¦èZ @ (r„K^·Å‹ˆˆP CTDEE1xÜ`‚Óƒybö„4) 1à:8ëÕgøW67‡ºu _V@ ‚·¡@çƒ'N°wï^9 Pnø¯ùÑÑÑ´tiIHËÈœ¥ð†Œ‚ßa¿,•z•ݲ%dã—S¤ˆL„Ú™@ KD&œyëœSRRøý÷ß™;w....rPzm\¾|ìììèÖ­GŽ‘Ï…„„àè舣£#sæÌ‘ËÏž=ˤI“8vìXžR¨†„„pûömµ#/tÃ>FH‹×”gæp¿Ù}>œð¡Ö¶Á½{Ÿ‹“ù†p"ÔŽp".N„9óÖ-ý={öŒÁƒãèèˆB¡àæÍ›ZëùûûÓ¡CºvíʲeËøóÏ?éÝ»7ÞÞÞtëÖ pb—.]bôèÑ8p€êÕ«çI®ë×ShÛöß`öII·Øºµ}ûöͱm\\7žÞ€ÚÙT*7oÜ$..Ž2eʨ*®öωP;‰P ºD8æÌ[§@W¨PØØXJ•*…‡‡‡Úêqf–,Y‚µµ5žžžЯ_?î޽˂ ´®ZÇÅÅѵkWÚ´iÃæÍq—«V­Êøñãs%—RY¥ò³L%Ú³[iãÚµkĘÇäX/Æ<†k׮Ѷm[µr•ml Z²Õ @ òÀ[g¡¯¯O©R¥²­#I»wïæ½÷ÞS³¿mÑ¢çÎãÑ£Gm Y»v-C† ¡aÆ4lØ0«ÐÉ@ üçH!%%…ôôtµZJ¥R£L__ƒÔ×ÞuR×rH¤hØ,)•J|}3úkÜ8C‰Ö6†(eűL$J‚Â!ó¼«T*Q*•ÄÄÄ §»dÍ[§@ç†'Ož T*©ûZ8Šúõë®ÑÆÄÄ„Áƒ«:tÈè1€Û?ÇF6nÜHpp°Z-777²-[¶PòQIõîŽÏÕ‹R/¥R²¤z½É“ݸ~=£?Uügmcè¢,88˜©S§ YŠSÙíÛ·‹,º.vx@P8džYñk IDATwÝÜÜpss£U«VŒ3†óçÏëXºâBz‹—xT&¯_âõë×iذ!»víbÀ€rùßÿ““^^^¸¸¸˜#GŽdëÖpàd¦R/öî%W6У¦Œâ—”_H³ÖþV¨©Ï0£al^µY­üØ1èÒ%ãóгg>. psscÞ¼y˜™ióŒ|w9r¤°ƒþ‡-[¶0oÞù$OJÙãǹrå W®\!22>ýôÓ\·÷÷÷§E‹¬]»– &äçRèØ±#öööœ>}:_í‹ žžžLœ8‘óçÏç˜ròäÉœ>}š˜˜œý‚þ /_¾äêÕ«\¹r…{÷îa``ÀªU« uÌâ@TTQQQ”.]Z×¢{ÞIºR¥JܺuK­< @í|Áœš“ÚÈÄ“Màп¢OÛ ( JÅ–¢]ƒv¬>²ZkÌF•]·nF@ðæqìØ1vîÜIåÊ•100 66–/^ðá‡ò믿j${Êooo”J¥ü=))‰˜˜J—.­±€ðùçŸç¨@¿I´hÑB^ù­_¿>wîÜáèÑ£¬Y³†;vпÿû ÃÞÞ^­¬Y³fyR ŒŒ°´´Ô0»ìííÕ”tccãwB åÂ… DEEå)Tï»È;©@—,Y’Úµkk„¸ ÀÜܼ¶ëaûœ?¶øoáNÌè˺/£§E†-FÕªU³l“š .d|.NáëAþðõõÅÞÞž´´4Μ9ƒ››;vì mÛ¶yZÅ|}å×ËË WWW>ÿüs>ûì3íÞz÷îMÿþýåhE’$qäÈzöìÉèÑ£éÑ£GŽJm©R¥pww§Q£F4jÔˆ:uêäYŽ÷Þ{¨¨¨|]ƒ ð™0aŽŽŽ4jԈѣGó÷ßëZ¤"¡yóæ4oÞ]‹Rìy+褤$$I"55@^i144D__€ñãÇ3wî\üüühܸ1¡¡¡ìÛ·áÇRöUòzÛ•©J枀ƒ™7ú#}£Û]» Ÿ‹£-2jGd"Ì;OSR¸«³ñÛššR¶Ÿc}}}Ú·oÏôéÓ5jÇg„ x{{S®\9Zµj¥ÑæÙ³gœ={–ºuëæÚT!&&†'Npÿþ}”J%ööötêÔ kkë,ÛÄÆÆâããC@@µjÕ¢K—.±é³# ???"""¨U«;vÔº–˜˜ˆ¯¯/.\ D‰T¯^víÚåhO¼råJµï …‚îݻӣG<ˆ¿¿?-s°w³°°øÏ ^¼x 6”W³Ÿ`~ûù¹RžΞý÷sqT W¯^-œµàáá!ì óÈËÔTüâât6~ã2eÈjS°¨ìŸU+™?ÿü3'Ož$22RCÙZ±bß~û­š-uv<}ú”J•*‘––F¥J•P*•<{ö SSS¶mÛFŸ>}4ÚDEEѬY3"""([¶,8::ràÀwö”J%³fÍâÇ bÅŠ|(¯ÜY[[3~üxâãã5Ú|ùå— 4ˆ˜˜>|ÈŸþɃ=ztŽã}ûí·üøã|úé§Å‘#G(W®C‡%û÷ï³hÑ"ºuëFLL ÷îÝãÉ“'„……ñÑGåêÚ^'66–ýû÷ciiI½zõòÕGA2{ölºtéÂÝ»w‰ŠŠbÁ‚„„„°páÂlÛ}ûí·Œ5Š)S¦°sçNLLL$‰À† ˆŠŠ"22’Ÿ~ú‰+W®0mÚ4~¶mÛ†?&&&F~Î>Ì78vìqqqx{{cjjÊŒ3HJJ’ë9s†Ñ£GÓªU+|}}yüø17nÜ wïÞÌž=›sçÎȽ6l˜ÃXu„„„P©R%¬­­iòOö°û÷ï3|øplmm çþýû>ãñãÏèÓç³\mc}ïû=Ï3>/ì¸=EîÿËTóSq\}yçÖ­[øûûsòäI&OžÌ?ü€žžž¼×µkWX¿~½Z»ƒ™ëÌ©eË–eܸqò¢‚¾¾>mÚ´aúôé<~ü6eʔ᫯¾’·¼»téBÏž=9sæL¶qeŸßÿ=ÕªUC¡PеkWÜÝÝyòä‰üRðäÉzô衶ʮڎÏãÇ'22’Õ«Wchh˜¯> ’š5k²xñbªU«†©©)sæÌÁÔÔTë=‡ŒÕÖqãÆ1oÞ<–-[&?ñ’åççǸqã3f ¦¦¦”*UŠñãÇÓ³gO~ÿýwÜ))),[¶LþÍÌ‘!99™U«VÑ©S'ôõõéÕ«={ö$**Š   ¹ž»»;úúúüôÓOòÿKݺuY±b …‚~ø¡@„„z÷îͳgÏðöö¦råŒÞ%K–’’œ9sä2sss¾ÿþ{€BÜ}¼­¼•&Å‘€€ªTñÀĤ5Jekþ÷?xÿýìÛ<ŠÄŠ‹+hS¥ =kä>ˆsPüó;#hà-¡çkÜøùçŸiÑ¢a«9~üxæÌ™#ûw¬_¿žÒ¥K3dHîw° ßdçÎ\»vˆˆ’““yõêwïÞըߡC {ÑÎ;³k×.9l›6HJJ¢M›6œ:uŠôôt$IB’$J•*…B¡¾›5k†­­-sæÌ!((ˆAƒѸqãçïïB¡P3‚Œ7 9 ׻ޝ¯/¾¾¾ÈÉåÚ taeeÅ!}‰Œ´`ãF8t! ¿ æûÌ'!%à УSÞVTæP|háD¨áD(ÈŠ;v`ee%Ƕ²²ÒPÇŒÃ×_Íúõëiܸ1<àðáÃŒ;6O&jOž<¡iÓ¦„‡‡ckkKíÚµ±³³“M7´ÅáÕæ|§*{ðàA–c…„„°fÍÖ¬Y£µŽª½B¡`ïÞ½xxx°|ùr–.]JµjÕ˜4i’¼Âš[¾ýö[¾ûî;FÍŠ+rÝ®°©U«–FY™2eHNNÖ(¿uë~~~ôèÑWWWóªìâsGFFª}Ï.³6ÙT;*ùÂÂÂHIIÁÏÏ/ËŒ½…a¾÷é§ŸâååŲeË4’¡…††R½zu­;Ñ-Z´ÀÛÛ›—/_æéùyqttÄÂÂBëËš@¡¹‰‰‰8::2w.lÞ éé°t)lØ ½~ðó`6\Í8Ù§VZÚæ- ŠJ65…b`Ò§áD¨áD˜wJëëS¯P å“"òÒoÞ¼¹FüáשP¡|ð;vì`éÒ¥lܸ‘ôôô<™o,X°€°°0Œ­û÷ï×°±VñzlýÌe*TÈVfÈxA}úÈ+ˆ£GÆÚÚš3fpçε6iiix{{Š<_|ñ÷îÝcæÌ™¦'QQQ²möåæÍ› 0€ºuë²sçÎ,ÿ?'Ož ÀºuëÔÒØ«² N™2¥@äyÓ¹qãžžž²Y• kÄ tadd„™™&&&Ô«—¡ØúúÂÚµ0w.dÞ}žsbÆúÆ|Óþ›<•9:P›6 ¼@ xã˜8q"Ÿ|ò eʔшϛ¦M›ÆîÝ»iÕª]ºtA__Ÿ'NðÉ'Ÿ¨%™È̘1c>|8-[¶ÄÌÌŒ?ÿü“„„¶lÙ’ã*äÊ•+yðàƒæûï¿§nݺÄÇÇs÷î]nÞ¼ÉÞ½{©V­gÏžeÒ¤I4mÚ”5j’’‚¯¯/|÷Ýw9ŽóÅ_pèÐ!µè*~þùg† &?|ø°V{ïÌù’’’¸té&&&ôéÓG-ô[Q1bÄLLLøè£èÞ½;ÀÌÌŒß~û!C†Ð AZ·n­­-QQQ\¿~¨¨($I*pYƇ¿¿?«V­ÂËË‹&Mš`ddDhh(—/_fòäÉ4kÖì?³zõjbccIMM¥W¯^ç,X@ëÖ­iÓ¦ 3fÌà‡~ N:4iÒ„Û·osíÚ5\\\4bI8ýû÷¶Ý’$Éÿ¿vvv/#o &&&˜™™åø"* t‘aaaA·nÝäï³gg(Ð/^ÀÆ0ujFùÉ“½w€‰M&bgš÷Î*ó ##ø'f±D8jG8 ^ÇÅÅ{{û<98µo߀!C†hU3S³fMÜÝÝÕ¢d´jÕŠ‹/²fÍÂÃé]»6»ví¢Y³f¤§§Ó:ÓöV•*UpwwgÀ€Lš4‰_ý•€€ÌرcqrrRoÈ!¼xñB­Ì‚3gΰ}ûvÎ;ÇÝ»w)Uª;vdÉ’%²3š*)ÆÅ‹‰ˆˆÀÌÌŒ/¿ü’N:嘬ÀÍÍ-[ûÎ×ÓrO™2EÎf›™Ñ£Gk\ƒŠÜÄø¯\¹2îîî8;;ËeU«VÅÝÝ]kjpЧ'NÄÜÜ\­lРA˜™™qêÔ)Ž;†««+íÚµ#00Ÿ~ú‰€€BCC©X±"&LPs¶300ÀÝÝ=K'ÂéÓ§kÍÚ§z~2›é( Ö¬YÃ!Cðòò"00¸¸8™8q¢š²ëì쌻»»^.;>øàµ{Öµk×lÿ.2g±\ºt)]ºtaÿþýáììÌÌ™3:t¨F»Þ½{k½V@ãž¿MT¯^êÕ«³uëV]‹RìQH…ñê)PcäÈ‘„‡‡sòäI¹L’2"pܹöö úúÐlC3.E\¢ŒQîO»EI‹<פ øùeØ?P¬úBÁÍÍM8jaäÈ‘Âú¶lÙ¼yó Õµ(o3gÎdùòå\¿~½X$ÅUD–ì~g:tè@•*UÄoQ6è"âuƒ|…fÌÈøšáT¸;p7—".0«å¬|)ϯ^¿Æçânÿ,2jGLX‚ü’žžÎ‹/ðòòbõêÕŒ;V(Ï Ï'œ tqëÖ-–/_Î… ä²áÃA’òKÓøâd†m^ÅR™ÑbF¾Æ¹pRS3>wZ ,AAA”+WWWWìììX°`®Eo.\`ùòåZCR Ô taeeE»víÔb¸š˜À?ŽÁ\IÛÌíèŒÌL_´ý‚ÒFÙÛ,f…ÊþY¡€|fµo(¶¶¶>|˜›7o”mìe@ x{{{Úµk‡•¶øº5„]D”*UІ j<”“'CɲJh73>n”÷S*T tíÚÉw¢XLªj¹\ S)nï¥J•¢[·nZÐ '¬¬¬hذ!¥t˜˜êMA(ÐEÄãǵ–—/NãWAÙ&Ôš‘~þÂǤ¥e˜pÀ›a¾±zõj9-°à_<<ò–¶] ‚‚$+Eð/B."²2ÈQÆXþ…éqîü1$ßcøûƒJ}â? 'Bí'B@ èáD˜3BÖ1‹Ï-&&éyÆ— ùu»QQùëKe¾oÆ ´@ Á›ˆP ‹ˆ€€<<<ðͤåFÅG±ââ œÍ߇ žY¦÷Î ª®+WΈ--@[|}}ñðð @×¢{„]DXYYÑ·o_µ só}æ“’Àš~rÔŒµk3â9ç•ý¦¬> 'Bí'B@ èGGGúöí+¢pä¡@‰‰‰8::ba‘‘%øy0®nÀ¥– Ímš3kVF]Uz,lúñ¦(ЉP;‰P ºÀÂÂGGÇlÓÝ 2 tñºAþ—§¾$%==… ;. O¨Q#ãüòåQ5rË›hÿ,œµ#œ@ K„aÎZ\}t•7v0Üi8u*dÄlÕÓû7½wHìÞû>U tٲРAAJ+Å“ØØX‰ŒŒÌµ9Xll,¡¡¡H’TÈÒe T*¹{÷.=ÊטiiiDDDȳgÏ AÂ7‹ãÇ3oÞ¼BÙ½ôòòzc²w3oÞûŒáÇóìÙ3Ö®]‹³³3Ož<)”qŸ}:»wïfÛ¶m|òÉ'ܽ{—   µú>¤yóæ,Z´gggÖ¯_ÏÞ½{™>}:7nÜ Y³f¬]»Vc¥R)ÏÛy%11‘ž={âíí͈#ؽ{7K—.¥råÊÌš5‹+V䪟çÏŸ3~üxÖ­[ǶmÛ?~<ÉÉÉŒ3†_~ù%W}lذ¥K—••III¤åÅ>°¹~ý:sæÌáŽê,´k×wwwJ—.]ˆ’ òûì¿SH‚BgĈRûöí%I’¤¦?7•˜‡TvQYéé«§Zë?}*I%JHHR¯^9÷¿woF]¤Ó§ RòÂeÚ´iÒ‹/t-F±cĈº¡Ø°yófÉÎÎN×bèœAƒI€"—¥§§KC‡•iæÌ™…2n`` H‹-*”þóC•*U$}}})((Hã\ZZš«VÖ¯_? V­Z¥Q?44TªQ£†dhh(ݾ}[휩©©T«V­|Éxüøq zöì©V$) ©^½zùêW’$éĉ 5oÞ|8ßã$;vì(VòŒ1B266Öµ¹âðáÃ íØ±#ÏmGŒ‘ãïLûöíÅoQèLsǧ½k{.™\G˜Õr%-´Öµ°È0åX·„À@¨];ë¾U憆дi!_H,_¾\×"K„aÞIL¼Ç“';t6¾µõ8ŒŒ,‹tL…BÁÇ̯¿þÊõë×åò¸¸8,XÀ_ýÅãÇ©W¯#FŒÀÅÅE­ý«W¯8xð ÇŽãÌ™3èééáààÀСC:t(¾¾¾Ì›7€_~ùE6ã¨U«ßÿ=’$±bÅ ¼¼¼xúô)Í›7gÆŒœ={–#GŽàåå%7þ|BBBX»v- .äìÙ³„……qûömŒŒŒHHHÀÃÃsçÎFµjÕèß¿?ãÆ“ûˆ'<<œFQCåq ===Ê”)#÷ññaÏž=´mÛ–O>ùD£¾K–,ÁÅÅwww5yÿ áIÛµk§V^£F *W®,ŸÏmÛ¶¥L™2ÄÅÅåª~µjÕò=– www^½zÅÿþ÷?,XÀÉ“'‰§M›6,\¸PÃ<==åË—óçŸrïÞ=ªU«F=˜2e zÿØþòË/¬\¹Èx6Ö­[@‡˜:uj–²üòË/ìÞ½›-[¶Èã®[·Ž#Gްk×.Ö­[‡··7ááá4hЭ÷`ÅŠìÙ³‡èèhš6mŠ»»{–cÆÇdzhÑ"Ο?Oxx8Õ«WgðàÁòNÀöíÛùã?˜:u*:tËSSS™8q"‰‰‰¬_¿ž’%KÅ‚ ¸víOŸ>¥víÚL˜0îÝ»kŒþüyþ÷¿ÿqãÆ jÖ¬ÉàÁƒåˆ^——^^^„‡‡ GÂ&EÄÓħ\Œ¼T,U‘éͧg[ÆŒ [fI‚œLæT t£F& Á»‡„$¥ê쀢qH{äädY)‰ŠŠÂÙÙ™~ø;;;úõë'Ûâ~þùçjmȇ~Hdd$ƒ ¢_¿~°uëVLLLäé2eÊ`ee…••åË——û=z4Ó§OGOOAƒ‘””DÇŽñôôdß¾}jãýõ×_x{{Ó·o_öíÛGñ–o3 IDAT:uhÞ¼9‘‘‘8;;óí·ßòâÅ zõêÅ;w?~}úP­Z5þüóÏs,ìܹ3¥K—æ·ß~S+?sæ >ÄÕÕ5ß}{zzGÿþýÿ«˜¹ÆÇÇooozôèÁš5k¨[·.)))¬]»–ž={ªÕMOO§k׮̜9…BÁ Aƒpss£[·n¤§§Ï”J677—Ÿ/SSÓle¹yó&ûöíC©TÊeþþþìÛ·ñãÇ3{ölÊ—/OÅŠùã?hÑ¢…†½ôÇŒ›››üÜ*•J:tè@pp°Æxaaa899±páBâââèÕ«·nÝbÔ¨Qj/e}ûö%00?ü¨Lé„¿üòK6lØ@—.]dåùâŋԫWµk×bddDçÎ9wî=zôа‘?räíÛ·ÇÏÏWWWjÔ¨ÁÌ™3Y¿~}¶÷)¿ôíÛ—-[¶ÐúM²Õº^1b„DC$¾F¢’“‹S®Ú¹ºf˜eKÒ£GÚë¼z%I††õfÍ*@¡‚b@nM8îJ!!_ëìHJÊâ´€Ðf‘””$›'Ì™3G’$I;v¬H{÷îU«÷þûïKzzzÒõë×%I’¤çÏŸK€4|øp±åÏÙ™p\¸pA¤¡C‡ª•¯_¿^"ãB­¼k×® õë×OJMMU;7lØ0 Ng²AKKK“¦NªQþÝwßÉý;99I_ýµtþüy­÷ÍÕÕU¤€€­çUôìÙS¤°°0¹ì¿˜pHRÆ{µjÕ${{{iÊ”)RïÞ½¥%JH#FŒž>Õn¾§gÏžIÓ¦M“Æ/Õ®][266–ÜÝÝ¥¸¸¸<Ë”_ŽfÍšI€4bÄ)!!A’$u¢Ì÷Æ  ¹»»«õ1}út 6lØ —åÇ„ÃÝÝ]¤G™~?þøc Z´h¡vo/^,ÒÚµkå2??? >øà)==]£îë&  …Ú5¦¦¦J&LÐ(¿víšdll,uèÐAJKK“Ž=*) µ¿³´´4ÉÉÉI*S¦Œ.—'&&J;w–J–,)=|øP®ëèè(•+WNízƒƒƒ%CCÃB5áÈMw±]T¤  9— fÉê%96Q%VÉ.½÷Å‹’’ñùM{aN„ÚN„‚¬˜?>nnn >KKKöìÙƒ Ÿ~ú)©©©lݺsssúôé#·122bèС¤§§ËæA%J”ÀÔÔ”˜˜RTÈ?˜˜˜äJOOOy…QÅÀ10ÈÚ:ð£>B___þÍöíÛiÛ¶-ï¿ÿ¾\®§§ÇøñãÔV³çÎË¥K—6l·oßæ›o¾¡E‹´jÕ ///y… 44›l¯Eu^U¿ hݺ5...„††òã?âííM¹rå:thž¶ßðòòb÷îÝR®\9êÕ«G©R¥ LÖÜ2yòdJü³Í©P(èÑ£MCÅÆ²€e6o5j”ÚùÂ`Ô¨Qj÷Vµ:žY>ÕËG}„B¡Ë?úè#þ"""øý÷ßéܹ³¼[ ¯¯Ï„ $‰ýû÷Ëå 6ä‡~àäÉ“LŸ>aÆQ³fMÖ¬Y#×ñññáúõëŒ5 [[[¹ÜÄÄ„1cÆÀÑ£.\¸ÀíÛ·éܹ³ZfÀjժѲe˼ߠ< œsFØ@™Rs¿ªùŠ5;×0}âtµ’×iÙZ´€óç3Ò{Ï ¯Ï™*ó …9ø›ÂêÕ«™7ožH¦òÂZ oooôõõ111¡Aƒ´jÕŠÙ³gcffFpp0)))tìØQ6éPѵkW9B…‰‰ £FbùòåXZZÒ§OúõëG¯^½4ÚfŽ{÷000PSzLMMiÖ¬çÎÓhc``@ÇŽÕÊ‘$‰„„ ¥ ÀÐÐPck½I“&lÛ¶åË—ó×_±}ûvöìÙƒ««+Ÿþ¹ŠÌÒ2Ã.=:::Ûy&::€²eËæ|á¹àùóçÔ¯_6oÞŒ««+¡¡¡òV¾»»{®3ŽÚØØÈq«ýýýÙ¸q#Æ ãܹsZ£‡ÖÖÖ4iÒD­¬aÆjQ`‚‚‚°±±ÁÑÑQ­nýúõ©T©’F””‚$ó‹#@:u022R“/88}}}Ú·o¯V×ÚÚšzõêq÷î]¹ìÖ­[¼|ù2×Ïæ¤I“8yò$+W®ÄÄÄ„£Gª½ì¨"ŽÜ¾}[£OUö?Õ"ŠªïÎ;kŒÝ¹sg|r¦+Ÿ<~üXØ@ç€X.*^3ëŠ3ËÕJcæôÞÚBªhGÇ çÃ7 ‘‰P;BydÅåË—‰ŠŠ"44.\ˆ¹¹9111€ö bVVVËu–-[†¯¯/:ubïÞ½¸¸¸PµjUvìÈ3¦R©ÄØØX^‘ÌŒJ¦×)_¾¼†’úøñcJ–,‰B¡Ð8†J«,VÊ•+G¯^½ðôô”m¢—-[&Û2׬Y€û÷ïg{-ªó9­Tç–={öÉôéÓ9r$¦¦¦899±råJX¿~½ÆÊN( œY¶l666ìØ±C¶/ ´½\¨^¶T÷[’$bcc³T¼ìììˆ-´$6Úì§ …Úx/_¾ÄÌÌLkÝ×åÎͳ™yeZ…Ja677§råÊjçT/kÆÆÆíJ”(Áˆ#¨S§Ž,«6¹²*+H„òœ3bZG¼2yETTuëÖͶ^ß¾P½:òe0i¨­ÓÒ2V§áÍ3ß #£JXZjnÁÚÆ¢DiàÔ©SçÎ;GRR’F4‚V­ZѪU+”J%‡bÖ¬Y̘1ƒ``` ¶Å­m¼cÇŽqáÂÚ´i#—§¤¤ä)n´*šF‡øúë¯sÝîu7nL:u¸uëÑÑÑT¨P-Z°råJ~ùåºt颵¿¿?þþþ´lÙ²À"?~€nݺ©•+ ºtéÂO?ýÄ•+W´*_9ahhH=X¿~=ÿ)ÙKA£P(pppàòåËÄÇÇ«ÅjŽåòåËT­ZU~®²{¾ {{{þúë/¨_¿¾\žššŠ¯jEêTÏf×®]³Ò‘™­[·²mÛ6 ÀÞ½{>|8¯UÕçÈ‘#éׯ_޲œ>}Zcº8ÅfW+Ð:Â<Æœ¹È¹]zï¿ÿU$#¡@ ÞeôõKR¢Duzzš«IE¹¹95jÔÀßß_^åRqäÈš5k¦µ­‰‰ ýúõcĈDEEqíÚ5 cÊÄÄ„h´Q…êRÙkª8{ölž²Ä9::R¡B~ýõ×í.%I’Wå^'44”   Ê–-++ƒ ¢qãÆìرC«I‰R©dÆŒH’”mè´¼¢ZÉöööV+OOO—#ƒd¶MLL$&&&W>!)))œ?y…2"²ÄÄÄèÜvµY³f¤¤¤h˜œ8q‚ÔÔTµgPeæQ¶ç9¡ziQ½ä¨¸|ù2±±±jeõêÕÃÜÜœmÛ¶åjµ?00É“'ÓºukvìØÁ¢E‹8tèK–üëóÔ¬Y3 s•Mò½÷ÞÃÐÐPCVmò Š¡@™çÅ$¨¤—‘Ö57Œù¯yF¦¿C2¿,¿‰ ´p"ÔŽp"ä—  IC‡%$$„äädþøãÖ®]‹½½=cÆŒÀÏÏ©S§rõêUIIIÁÏÏ­[·R²dIY±)Q¢­[·æðáÃxzzâïï/Ûˆöïßggg<<<øæ›o8sæ 6là£>¢zõê¹–ÙØØ˜eË–q÷î]úöíK@@iii(•JþþûoæÎË€ Ç&ÜÜÜ8}ú4< $$„Í›73lØ0RSS>|¸Ú çš5k(S¦ ;vdéҥܸqƒ¨¨(Ž9B»ví8uêãÇ×p†„Œø¿ª¸¸¯¯¿¤dfРA°bÅ Ö­[ÇÓ§O¹xñ"&LàÁƒ¼ÿþûj[ûsæÌÁÜÜ\M)òôôdêÔ©œ:uŠX±b:u" €‰'ª™ÏlÚ´ sss–-[¦&Kxx¸,³Ê¦÷üùórYV/$ù嫯¾ÂÐЙ3gâççGZZ/^döìÙòÕW_Éuœœ¨T©[¶laÿþýøûûk}Y+HÆŽ‹••‹/æôéÓ¤¥¥qçΦNª‘ݰT©RüïÿãÖ­[ôíÛ—›7o’––Fbb"þþþ¸»»Ë/‰‰‰ 8vìØ¾¾>3gΤW¯^Ì;WŽ£noo»»;dìØ±„……ɦ/ýõ'Näï¿ÿ råÊŒ5ŠË—/óý÷ßOLL Ÿ~ú)ÏŸ?/Ôû¤ë±7Ýÿx·1b„„óø©R›JRàíÀ<õñÕWšÙÌønm]B"¡vDè ™3ÐÆ.+~øáÉÈÈH$ 4h ݼyS®sñâEÉØØXghh() ÉÉÉI:xð ZGŽ‘ºté"•(QB#ÞóçÏ%y<[[[iëÖ­Ò°aäråÊ©õÓµkWÉÒÒ2K¹7mÚ$•+WNM@²°°Ž;&I’$¥¤¤HíÚµ“Cxe>ŒŒŒ¤ &HÉÉÉ}‡††J-[¶ÔhS²dIiåÊ•jáÌT˜ššjÔÏ|œ:u*Ûÿ‡­[·J+VÔhשS'éÁƒju§M›¦ÎmçÎò}Í|”(QBš:uª”””¤ÖÇÚµkµ†ܼys¶×qíÚµl¯C’2ÂØi é—U˜ÃHjÏ`ÅŠ¥C‡iô±yóf©U«Vr½‘#Gf+Kvaì2‡`Tall, 4H­ìÊ•+’š|nnnYf"üé§Ÿ$333g³bÅŠ’$Iÿ†Ü¿¿ZÛèèhÉÖÖVªR¥ŠôüùsI’2žão¿ýVþÌüÿlgg'ݹsGn'õéÓG$}}}I¡PHµk×–¶nÝ*2ê…$’5¿@fäÈ‘l½¼Ãú†T‹¬Æšo×Ðþýö97ÌÄÓ§P¥ (•Ыx{CåÊ À®]…$¼@ C¶lÙ¼yóŠt‹·8Ill,ÕªUÃÐÐ0ÇúQQQ\¹rEÎDèìì¬Ñ.11‘K—.&GÇPE­ÐFRR¡¡¡˜˜˜`gg§vNe>P±bE5j„B¡ÀÏÏO®óðáC”Je¶«Óqqq\¿~;wîP²dIªU«F£F4¢ÅÆÆâççÇÇINNÆÖÖ'''µP_Úxøð!×®]#22’åË—¡C‡Ô2Ç©¸{÷.iiiYöU¥J91FV¼zõ ???‚ƒƒ155¥N:²ƒXfžbýú©™¡‚7¡@o^½z¥øÀôîÝ›¹sçòÝwßéH²œyðà-[¶äåË—œ:uŠFéZ$ HÈN9V™öøúúÒºuk¡@gƒˆÂQD¸º¸æûALII!2r"ÐW.˼ q€WžC# A~™5k·nÝ¢E‹”)S†ÀÀ@<==±µµe–*g1ÅÖÖ–£G²sçN.^¼(h€ŒTÞ}ûöÕ÷Z ŽP ‹a¯Ipp0öööÙf-{¹}û¶F 8Ò§O¢¢¢8pàñññT­Zwww>ûì³ÀÙÙ™æÍ›3xð`þúë¯\·ùš'Bí'BMRSS ֵŎèèh¢££u-F±CÌ-Ús‹&bnÑŽÐYrF(Ðyä·ß~cÆŒlß¾¹sçæº0È×D8jG8jÏêÕ«u-F±Ã××___]‹Qìs‹vÄÜ¢‰˜[´#t–œy«èÔÔT”JeŽunß¾­ñ¶•ÀéÓ§9}ú´Ú[{¹rå°°°àÕ«W¹~›Ï»ð@ :Bè.ÙóÖ)ÐñññŒ5ŠúõëcbbB‰%²¬»|ùrÌĮ̀]»6¦¦¦Ìž=I’ˆ‹‹ÃÓÓOOO.^¼(·éÖ­sçÎeùòåÌ›7¯°/G @PÌxëh¥RÉÇéÞ½;...YÖûã?˜>}:K–,!>>ž;v°råJ–.] €¥¥%ëÖ­cݺuŒ1‚ôôtµÕìÀÀ@œœœ ýz²âöíÛnÏ–×>SSS9pà@öù_9räHŽ»…Ýgpp07nÜ(Ð>ÿ+^^ÙGp)Š>?žã¶zaÈ™J¥’#GŽè¼Ïœ®¹0äÌ1·hGÌ-Ús‹&bny7xëh Ž;ÆâÅ‹iÒ¤I–õV­ZEóæÍ™0a¥J•ÂÕÕ•>ø€ü‘ôôtú¯^½ÂÑÑ‘Ž;âììÌ–-[øüóÏs-WJJJ¾®'+Þ†¹˜˜˜wôy~ä^¾|™'ùrÛþ#—ššJLLLžÆË‰·áG.66–ØØØ<—bnÑŽ˜[´#æMÞ†¹¥ u–·…¤²Yx ñðð`Μ9¼~‰)))3zôh6lØ QÿîÝ»T¯^]£¿ÔÔTBCC©X±"e˖͵:tàüùó4nÜCCCµs–––ËßÃÃÃsUvõêULMM©V­ZžÛfU–’’‚¡¡¡@=§¶iii\ºt‰Ê•+gYOåˆP¥Jþ¼½½©U«vvvù–ùõ²'NдiSÊ”)S`÷%""‚Fadd”«¶$''£¯¯Ÿe½ëׯӰaCŒŒŒ4úÛ³g7þO2¿^öçŸÒ®]»ë/<<œÐÐPÚ¶m›ë¶W®\¡N:<}ú4Ëz—/_–ÃÖenÏ©S§prr*°û’œœÌÙ³giݺuÝ—û÷ïóøñcZ´h‘붪¤YÕ377'00¦M›j´½zõ*±pPP÷%<<œèèhêÖ­[`÷%¯ó•—.]¢E‹ù𝼽½iß¾=ÏŸ?/°ûréÒ%,,,¨\¹²Îæ«èèh’““qppÈ×|µgÏúõë÷Ÿÿö3—ùúúR¥JÎW7nÜ N:”(Q"ÏóUJJ §N¢wïÞv_’““ñ÷÷ÇÊÊJ§óUVó©ŠÌó•ÊÞ9::SSS®]»FÓ¦M9yò$í¼“ txx8vvvüðÃLŸ>].?xð ½zõÂÇÇGíí¿²aÃ~ûí7r‘éG ®Ñæ08dÈÆŽ«iÞ ÞÉD*Ïž=ÀÎÎN­ÜÞÞ^í|A1vìXñ @ ¼%¼u6й¡B… @ÆöEfîÝ»§v^ @ xwR¶²²BOO[·n©•P©R%]ˆ%@ xx'h:vì(+Ì*®_¿NÍš5©ZµªŽ$@ wÞJúèÑ£xyyÉ+Ì^^^xyy*×™6mW®\ÁÃÃÈÈH6oÞÌž={˜6mZ‘ÉNëÖ­±µµ¥OŸ>JçM¥K—.XXXàèè¨kQŠ ¿ýöõêÕÃÎÎŽž={râÄ ]‹T,ððð zõêØÛÛÓ§O.]º¤k‘гfÍB¡P¥kQtŽR©D__{{{ùx=í»ÊÕ«WiÙ²%•*UÂÑÑQü=zôŸôõõ Ü?êMÄÇLJnݺѠAúõëW¤±Ø‹Ò[HãÆ%KKKcëÖ­jõ6mÚ$Y[[K€T¾|yiÁ‚E*çG}$-\¸PJLL”Æ'}ùå—E:~qåÒ¥K’¿¿¿T«V-]‹Rl8uê”%I’$8qBªZµª”ššªc©tODD„|~ýõW©E‹:–¨øpá©ÿþ’©©©ôèÑ#]‹£s%KKK]‹Qìxúô©dmm-ýþûïRjjªôâÅ )99Y×b+¶mÛ&uëÖM×b œœœ¤£GJ’$I«V­’¨c‰tÇ[…ãòå˹ª7jÔ(FÅóçÏ)W®\!K¥‰¯¯/«V­ÂÄĄѣGóÅ_¹ Å‘&Mš¼ÛoµZh×®Úç'OžðèÑ#lllt'T1 ³¿‚ƒƒ¯^½Ò¡4Ҥ¤$¦OŸÎüA:ut-N±!99™íÛ·Ó¨Q#j×®­kqŠ7n¤iÓ¦4nܘ°°0a¨… 60eÊ]‹Q,HHHç]kkku,‘îx+è¼¢ å9""‚””ÌĮ̀[·.—/_&===½·Ò²FP@¬[·ŽV­Z½óʳŠÍ›7³qãF>|ÈáÇu-N±`þüùŒ9R8DgBOOŽ;rçÎ~þùg91…Á»ý3xûöm‚ƒƒ0`¥K—ÆÐÐȉXÞuîÞ½K`` }úôѵ(ÅOOO\]]±µµ%66–?þøC×"éŒw{æø„††„™™™œ!ìu^½zÅÙ³g £iÓ¦8;;ËçôõõIKK“¿«g…BQè²=ÂÏψˆ7nLãÆµÖ»|ù2Ç'))‰÷ߟöíÛ±¤EKBBׯ_çåË—4mÚ4˶۷osîÜ9,,,hÛ¶-æææu<ÈÚµkß ègÏžñ÷ß“””D·nÝ´ÖIOOçâÅ‹øûûS»vmZ¶l©ñÃÞ·o_Þ{ï=¶mÛÆ¤I“8uêTQˆ_(ÄÆÆrõêU‚‚‚°²²ÊòG;,, ooo>|ˆ³³3ýúõ“³œúûûsñâE,XP”¢*1·ñûï¿Ëß»té‚··7®®®….aQs‹™™%J”wn{÷îÍŸþIïÞ½‹ä šäädä¹e„ ZëÅÇdzgÏnܸA5èÛ·¯Ö¶›6mbèСoÅ ÅÕ[¾þúk\]]éÙ³'ëׯgÕªU,Y²¤(Ä/~èÚ†äMãèÑ£’………H€ôþûïk­.9::JæææR£F$}}}é“O>‘ÒÓÓå:U«V•¢££%I’$___©K—.Eq …ÂôéÓ%@R( }ýõ×ZëmÚ´I200ºví*õíÛW244”¾ùæzo… tÏž=%}}}ùy9uê”Öz‹/–ôôô¤ H–––’µµµtýúuµ:‡–êÔ©#=xð $/<ž={&ÙÛÛË÷$«iH©TJýúõ“ŒŒŒ¤&MšH¥J•’Zµj%½xñBkýÔÔT©L™2ÒãÇ SüBcïÞ½òßžž^–sË•+W$ ÉÉÉI:t¨T®\9©{÷îRbb¢$I’äéé)ÕªUK>ôôô¤êÕ«KEx5GAÏ-*&Ož,-^¼¸¤.| jnÙ³g4zôhùûܹs³½oÅ™'OžHFFFòßPVs˳gϤÆÿßÞÝEQÿqŸ ˆ€x â)‰ǃ a(6ü,RIsÒ-EÃ'F+' +ÅÑlšÌ§t4Ò |lòaRPC I"=ÀäéA¸A@îóûùÎ;ÈK¸Sïóšqd¿ûÝÝÏ®ûýî‡u÷»/‘‡‡Íž=›¼½½ÉËË‹ŠŠŠtê=|øÜÜÜ(//ÏÑwŸ®Ê[”J%õë×ÚÚÚˆˆ¨¢¢‚ììì„iKà ´‘.^¼H6l 3gÎPxxx‡'bdd$ 6ŒT*?~œD"8p@¨3gÎZ¶lÕÖÖÒ¬Y³:¼0<Μ9C¤V«ÉÆÆÆà¾¨T*²³³£¥K— eß}÷‰D"*,,Ê.^¼H?ÿü3I¥RÊÊÊ¢ââbSìB·Ø°FÕIDATaíÝ»—RSS;¼ÈåääÚ¾};µ´´Ðÿþ÷?ò÷÷êœ8q‚<==éÂ… ¤T*I©T>·/ú¨T*Z½z5;vŒ>úè£/r_ý5õéÓ‡ •––’««+ÅÇÇ uÎ;GÍÍÍÔÐÐ@[·n¥°°0“ìCw((( #GŽR©¤ˆˆˆû–±cÇRXXµ¶¶Qnn.õêÕ‹¾ÿþ{ƒõŸ÷—»ªoÉÏϧŒŒ *++£ÔÔT’H$TQQaªÝèr]շܽ{—<==©¸¸˜îܹCþþþ”ŸŸoªÝèR ”ššJ………´fÍšû–ÄÄDrrr¢²²2""R«ÕäííMï¼óŽN½Ã‡SHHH·ÇÝݺ2o‘Éd”žžNmmm´eËš8q¢)vá™Ä ôSèè"WZZJ=zôÐÕÃÇÇGç.sEEMžzÊó(++«Ã‹Üûï¿O666:£j¤¤¤:þ<=êð_~ùe?ÏëE®½µk×vx‘“Éd4~üx²÷Þ{…»­qqq4dÈò÷÷§E‹=÷wŠ´:ê[ €ÞS3fŒÁu½ûî»TWW×ašÜÓô-yyy4cÆ 3f }øá‡”••eª°»ÕÓö-Ú²àà`zõÕWi÷îݦ»ÛuÖ·¸»»Ó¤I“tÊâããÉÆÆ†ÔjµP¶uëVúõ×_»5NSû·¼%))I§üñ¼%##ƒ¢££iøðáûÂô¹ÿ?Ý ._¾ F£÷ö{@@Î;'L»¹¹áèÑ£¦Ïl®]»‘H¤s\$ ÜÜÜpíÚ5¡lÓ¦MæÏl.]º„¡C‡¢gÏžBY@@€0oôèÑHJJBRR’¹B4¹ÆÆFê=€;vàÖ­[ËåØºu«™"4íè4~~~:å8~ü¸ÁevîÜÙíq™›¶oi?²Æã}‹¿¿?öíÛg®ÍâIú˜5kfÍše–MM­V£¢¢o¿ý¶Ny@@š››QTT„ÀÀ@èðùé‘6o1Ô·´Ï[ÂÃÃnêðžI<ÜC7(//`ø"W]]––s„evååå8p ìììtÊ„cf‰ÊËËõÎ___XYYYìqé¬ µŸoi:;.µµµ;¤Tyy9¤R)ìííuʹoá¾åqÚý6tƒ«ý|KÃy‹ñ8î Q§åää"²Ø‹\[[›Á7™­­­uF$±4z犕•ìíí…sÉÒtÖ†ÚÏ·4Úv¢qCKÛ®,µµµµé€ûî[ôiχǯE–Þ†8o1'ÐÝÀÍÍ ô>råÊØÚÚÂÑÑÑa™»»;nÞ¼‰è”çååYôxµnnnzçŠR©Dmm­p.YšÎÚPûù–FÛN´ÇA+//zw`-÷-†qߢ¯³6Ô~¾¥á¼Åxœ@w©T ÀpÕγDÞÞÞÐh4: T­VãöíÛðöö6cdæ%•J;ìÌ-õ|qqqAŸ>}:<.4GXf§m'W¯^Õ)ÏË˳è6¤í[ „2î[¸o1ÄÉÉ NNNKÏž=1hÐ óffœ·èn‚Áƒ#++K(S«ÕÈËËCTT”#3¯èèhXYY!--M(Ó¾ø4gÎs…evQQQ¨ªªÂ­[·„²?þøööö˜4i’#3‘H„3f ''­­­Byff&ÂÃÃÑ¿3Fg>>|¸N*))Á•+W,º iû–'NeÜ·pßÒ‘Ù³g#;;*• ÐÚÚŠS§N!22Ò,_&~pÞò˜{çMMM %$$PBBI¥Ròðð¦ÛÀ!99™D"-Y²„öìÙCaaaäááñ\;Ú™´´4Šˆˆ ˆˆêÑ£ 2„"""hæÌ™:õ’’’¨W¯^´`ÁZ¼x1988Pll¬™¢î~)))”@QQQ€Þzë-JHHÐ𝱱‘üüüÈÏÏvíÚE«V­"+++Zµj•#ï^+W®¤„„ #BÊÌÌê\¾|™)22’RRR(&&†¬­­)==ÝŒ‘wŸÊÊJ¡ 9;;“X,¦Û<'--lmmiòäÉôé§Ÿ’——1B»õEÃ}‹aÜ·6gΊˆˆ ™LF„sçØ±cB’’œV¬XAcÇŽ%‰D"Œ9ÿ¢á¼¥{ð0vFjkkCee% 44„é‡ õbbb`gg‡äädœ:u ÁÁÁHIIyaŸ;srrBPP€ƒƒƒN½ÄÄDÈår=zõõõøöÛo1oÞ<“ÆjJ*•J8?´¿ÅWVV¢®®N¨ckk‹sçÎ!11›7o†D"Á¦M›°`Á³Äl wïÞÅÝ»w1`ÀDEE Ǩý‹M8sæ Ö­[‡/¿ü>>>8~üø ûéwkkkƒmlll„Ÿ'L˜€³gÏâÇÄ•+W0þ||ðÁèÛ·¯Iã5î[ ã¾Å0¹\777½6$‘H„Ÿ===‘™™‰mÛ¶A¡P ,, ;wî„L&3u¸&ÁyK÷™;ÆcŒ1Æžü 4cŒ1ÆcFàš1ÆcŒ1#pÍcŒ1Ƙ8fŒ1ÆcÌœ@3ÆcŒ1fN cŒ1Æ3'ÐŒ1fb …………æ£S•••8sæ ª«« ÎÏÎÎFqqq§ë¸wï222tÆšeŒ±'ÐŒ1‹ŠÐÐP\»vM§üöíÛ ÅŸþi²X/^Œo¾ùÆdÛ3†J¥ÂèÑ£1hÐ ÄÄÄàâÅ‹ëÍ™3ÉÉÉ®+;;ãÆCCCƒ°îäädܽ{·«ÃfŒ1“â/2Æ,Â… <úbÝÁƒ…ò¦¦&\¸pAç n–ìØ±c(**BUUÕSÝpèСX»v-lmmJ¥óæÍCVV\\\º"\Æ3 ¾Í³¯½ö:„¿þú«Óz*• ---:eÍÍÍP«ÕÂtKK‹t?|ø—/_Fss³Î2·nÝÂ?ÿüÓ鶪««‘ŸŸFÓaòòr( ½õ@]]Z[[eee¸zõj§Û#"!77WoÕj5ÒÒÒ —Ë¡Ñh R©:]—VUUrssõâóôôD\\¬­­¡ÑhP__hhh@]]Þ/-ÈÎÎÆ7:=Œ1fnœ@3Æ,Fdd$°lÙ²N빺º"55U§lëÖ­ÉdÂtjj*Äb1> ±XŒàà`8;;cß¾}(--…\.‡|||­·––LŸ> @`` ¤R)²³³uêäääÀÏÏ À¨Q£ ‹±qãF:b±[¶lAPPˆØØØ÷K¡PÀ××^^^x饗 ‹±sçNa¾L&Ã?ü€ßÿb±R©´ÓãôàÁL›6 îîî ‚··7òóó…ù'Ož„X,†J¥BII F˜0aÄb1Äb1 ¶¶ãƃX,Fdd$är9œ;Ý6cŒ™'ÐŒ1‹²víZœ:u §Nê’õ}õÕW8zô(*++1uêTÄÅÅaÆŒXºt)êêê°cÇìÝ»EEE:ËíÙ³ŽŽŽ¸yó& † ‚©S§¢©© À£;Ó&L@`` òóóQSSƒ/¿ü 8}ú´ÎºV¬Xèèhܹs¿ýö›Á8›ššðÆoÀÙÙ™™™¸}û6æÎ‹ùóçãܹs½8ƒˆˆÑ¿>Ö²~ýz¡´´999°³³Ã®]» Ö4hðüyVVˆDضm***PRR‚ªª*455éýÃcÏN cå•W^Áĉÿõ.ô“Z²d ÂÃÃáâ₸¸8ÔÖÖB.—cîܹpppÀüùóáììŒôôtåzöì‰Í›7ÃÃÃX³f *++ñË/¿¶lÙ‚`ãÆðó󃃃-Z„ìØ±Cg]¡¡¡øä“Oàââ‚~ýúŒóÀ(++úuë0f̸»»cóæÍpqqÁúõëÿÓ¾2Ÿþ9ÜÜÜ0räH̘1'Nœ0z=ùùùJ¥H$€^½záõ×_ÿO11Ƙ)ðK„Œ1‹³fÍŒ1‡‚\.ªuEDD?ûùù&Ož¬SG.—C¡Pè”3Fx¹F…¾}û wisssaooøøxh44 îÝ»‡¶¶6uMœ8ñ_ã,((€ƒƒBCC…2‘H„ñãÇãï¿ÿ~½Õõø~cõêÕhiiµµõ¯gáÂ…˜2e ÜÜÜðæ›o"66V'NÆ{Öpͳ8AAAˆŠŠBbb"öï߯7_$é•=þ–ÞríË´å¿×»woéž={ÂÊÊJ3ùÁƒpvvÖKާL™GGG2wwwƒ±µ÷ðáCX[[£GÝÿxìÝ»·ð¢±ßOíº}pìØ±(--ÅîÝ»±ÿ~„……aìØ±ÈÈÈ0øoÁcæÆ 4cÌ"%%%aذaؽ{·Þ’’’ÐØØˆ;wî`íÚµpttÄ´iÓ‰«V­ÂíÛ·ÑÔÔ„uëÖ¡¬¬ qqq]¶_ñôô„——Ž?®“D9r÷ïߦµÃçi_¨dŒ±g 'ÐŒ1‹õÙgŸ,Ÿ9s&ª««1xð`ôïß_|ñ/^Ü¥ÛŽŽŽFZZ ///œ8q{öì^”J¥8xð NŸ> ™L___¸»»cèСÂGaŒáè舟~ú çÏŸ‡<==±|ùr$&&"22²K÷­3K—.ÅÁƒagg'<ƲråJ¸¸¸ 002™ S¦LALLŒIãbŒ1cˆH;ŽcŒ½À®_¿WWWaìa­’’466ÂÓÓSç™ÞêêjdeeA"‘ $$ ¨©©··7€GwI•J%|}}…e4 õÖURRáQ í´³³3 ”J%F-ŒBÑ^cc#òòòP\\ '''é|Åïúõëððð€ƒƒÃ•J…‹/B­V#88Ô™¯T*ÑÖÖ†tºž¢¢"888èŒ×ÜÐЀ²²2 :"‘÷ïßGii)d2™Þ³×åå娯¯‡¯¯/4  ŠŠŠ ‘H0zôhØØØ<Ñþ0Ƙ9pÍcŒ1ƘøÆcŒ1ÆŒÀ 4cŒ1ÆcFàš1ÆcŒ1#pÍcŒ1Ƙ8fŒ1ÆcÌœ@3ÆcŒ1fN cŒ1Æ3'ÐŒ1ÆcŒhÆcŒ1ÆŒÀ 4cŒ1ÆcFàš1ÆcŒ1#pÍcŒ1Ƙþ‡'ˆv#%IEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/Q8-1g-idx-optlevels.svg000066400000000000000000004360471231437614300253310ustar00rootroot00000000000000 PyTables-v.3.1.1/doc/source/usersguide/images/Q8-1g-idx-sorted.png000066400000000000000000003107121231437614300245670ustar00rootroot00000000000000‰PNG  IHDRÐß}™SsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìwXTG÷Ç¿»tX`)"( V{ÅJ,D%êõõ÷Úb4±Æ×K±€5ÔØ"REŒ ŠJ±—ÒaaÏïž½/—»»,Š¢É|žgæž9sfæÞñܹ3gDDD`0 ƒÁ`0!nh ƒÁ`0Œ÷ æ@3 ƒÁ`0u€9Ð ƒÁ`0 F`4ƒÁ`0 ƒQ˜Í`0 ƒÁ`Ôæ@3 ƒÁ`0u€9Ð ƒÁ`0 F`4ƒÁ`0 ƒQ˜Í`0 ƒÁ`Ôæ@34æÖ­[ؽ{7.\ˆ­[·âÊ•+¨¬¬lh³ÞG…ƒƒbccÚ•œ>}S¦L››\]]Úœ÷Š©S§²6û›Òºuk|úé§ËWTTàúõë8|ø0Ž;öÊåÊår\¹r»wïÆW_}…•+Wbß¾}ÈÊÊze Ihh(Zµj…ââb.íM>7C† A÷îÝ߈îúfÛ¶mpppÀ­[·¸´U«VÁÇÇçõÿä? í†6€ñî“““ƒI“&áÀ333äææ|||°k×.8::6¤‰õƾ}û°hÑ"ìÛ·žžž¼kEEE¸wïJJJÈ:õA\\ÀÀÀ2™ ‹Å Äwß}§±] MEE¾ùæôéÓ‡7¶¼ÉçæñãǯÔö A~~>îÝ»‡òòr.mذa˜7oöíۇѣG7 uŒ7›f¨%??^^^8xð æÎ‹û÷ï#''/^¼Àúõëqùòe´iÓ mj½››‹´´4¥Nrÿþý‘’’__ß°¬vŽ9mmm?~_}õfÏžÝÐ&1ï † ^ybàêÕ«ðôôÄ•+W°fÍÜ»wEEE(**µk×°téRDFFâÒ¥Kõlý›cÏž=ÈÊÊÂôéÓyékÖ¬Áõë×Ȫw'''ôïßË—/5´9Œz†9Ð µ,]ºÙÙÙX½z5V¬X[[[€……¦NŠððpäççcΜ9¼|(((諬¬D^^ÊÊÊ”–WXXˆK—.áæÍ›Éd*ó+ÞòKJJpñâEdgg#??………JõòòòxŸkRRRÂ9Î………ÈËËC^^JKKúúú°¶¶†žž—§¼¼yyyÜ'ºœœüùçŸÛår9‘™™©²|…wîÜÁùóç‘““£VVAEEòòò™™ ®j¾TVV"%%—.]BQQ‘R]EEEÈÏÏçþ¾wïΞ=«ñàÿâÅ œ?W¯^UÙ•••HMMEBB‚J™¢¢"¼|ù’ûûÎ;HIIQ*¯´­är9ï^+..ÆÅ‹ñäÉêR“û÷ï#..?VjG^^är¹àZqq1òòòPQQ¡Q9eeeøóÏ?qÿþ}ÿ»w«÷§¢Ï•=#ÅÅż¶«Ivv6âââT¶CͶ¿ÿ>bccQZZª²LÜuem ¬ŽÉÉɈ‰‰ÁíÛ·U¶MõgšˆpãÆ $%%©?€ª¶ILL|å—úÄÄD„……á‹/¾€‰‰IóËårLš4 ¥¥¥ˆ‰‰ÁÌ™3ѬY3ˆD"èêê¢uëÖ˜7oRSSѦM._ͱ±´´ ‚å¹¹¹¸pá233•>—Êž}Å8¥Ë”••iÔgr¹Ë—/GŸ>}Ë5¤R)7nÌK«9ŽÜ¹sþù§Êq¨êß””$''ktÉårܾ}.\ྈV'??_åìµb|¯Ù~2™ 7oÞT;6)xöì.^¼¨¶N0mÚ4ܼyáááµÔˆñÞA † ^¼xAºººdkkKeee*å|}} ]»vKóôô$OOOlbb" ï¿ÿž—ž““C#GŽ$‘HD™˜˜Phh(O.>>žÐ?þH“&M"@sæÌ¡þýû“‘‘åçç Ê=~ü8 uëÖ©¬Ç¤I“¸²«ÿæÌ™CDD{÷î%tâÄ .ÏÆ EFFRß¾}9ûMMM),,Œˆˆ6lØ@fffœ¾~ýúÑË—/åïÞ½›ÌÍÍye2„^¼x¡Òf"¢'N(µ{úôéœÌ–-[H"‘p×Äb1}úé§TTTÄÓÕ»wojܸ1%%%‘½½='_XX¨Ö†«W¯’——¯|}}}Ú°aOnÇŽdbbÂɈD" ¡‚‚žÜ AƒÈÔÔ”nÞ¼IÍ›7çä(--ÊËËiôèѤ««ËÕgùòå<)))€–/_NÓ¦MãîEäååñäGŒAzzz‚ºýñÇdggÇ«›effr2û÷ï'4mÚ4^Þììl277§V­ZQqq±Ú6$"Ú¶méëës帻»sÏLõþTôùÞ½{:TÕãØ±cÔ¤I^=üüü(;;›'W½íœœ8Ù„„‰D‚:*èÕ«™››SII‰Ú:Ι3‡w/ FÑÎ;²öööäççG§N" N¾I“&tñâE¥u¬þ¬ÙÚÚÒ•+WÈÔÔ” ¤Ö.exzz’©©iòüöÛo€ÆŽ[§|Š~^³f M›6»·ýþèÑ#êÕ«¯Ýš6mJüñ§C.—“••õë×§{æÌ™€FÍKÿä“OHOO¯Ö{3..ŽÐ–-[×”ÝoŠq$99™yczxx¸@Ç•+WxϘT*¥Ã‡S‡ÈÕÕU JR©”×#FŒ œœNæë¯¿&ôÓO?ñòÆÇÇ“ŽŽõïߟär9UTTÐÒ¥KIOO76MŸ>JKKyù hذaœœ¶¶6Mš4‰¾ûî;@‰‰‰{MLLhÀ€jZ˜ñ>Âh†Jbcc Mš4I­Ü÷ßOhÛ¶m\Z]h™LFdbbB“'O¦³gÏÒ¾}û8ÇüàÁƒœ¬Â¶µµ%___:vì%&&Rbb";vŒÐÆå2„ xlMîÞ½Kÿùϸÿ(âãã)>>žs0Ô9ÐvvvÔ­[7 §íÛ·“³³3Ѳe˨Y³f´|ùrŠŽŽ¦¡C‡Z¼x1¯ìM›6qÍ–-[èòåË4mÚ4222"___µíŸ——GñññäååEÖÖÖœÝYYYDTå” nݺѹsç(99™fÏžM(00§«wïÞdhhH666´råJºté>}Zí Trr2éèè íܹ“ÒÒÒ(11‘~úé'^_ìÙ³‡¯¯/ÅÆÆÒ7hþüù€zõêÅÓ9hÐ ÒÓÓ£f͚ф (66–-ZD‰„:tè@£G&Ú³gíß¿ŸZ·n-øÏKá@[[[SÛ¶méÂ… tçÎZ±b‰ÅbêÓ§¯LeŽÀ©S§H$‘——­[·ŽiáÂ…deeEÎÎμvùì³Ï>|˜ˆªîk244¤›7oªíC"¢#GŽêܹ3%$$PFF}óÍ7dccóÚôáÇ µk׎~øáJLL¤ùóç“……¹»»“L&´}Ó¦MiéÒ¥”@gΜ¡¢¢"êÞ½;™™™ œä;wîH$¢3fÔZÏiÓ¦ÑO?ýD—/_¦´´4úù矩}ûö$‰èòåË|˜²³³iÿþýdeeEæææzíÚµÜx¶}ûvºtéM™2… ©gÏžœ\ee%ùûû“¾¾>7¹“““CöööÔ´iSž “'O&‘HDǧƒÒÙ³gi̘1¤­­MS§Nå•_} ÏÈÈ „„êÚµ+Y[[«t ½½½ÉÁÁ¡Övf¼_0š¡’-[¶Z±b…Z¹ððp@ÿùϸ´º8ÐëÖ­#ôí·ßòdKKKÉÔÔ”\\\¸4…­¯¯/˜5©¬¬$GGGòòòâ¥?~ü˜´µµ5šR8ÄÕÿCR În×®OvÇŽÜ Eu穬¬ŒôõõÉÃÃKËÉÉ!sss²³³ãý§GD4wî\@ǯÕv???²··ç¥•••Q“&MÈÐÐP0“Ò§O@çÎãÒz÷îMhîܹµ–W½\JOOW)SQQAööö¤««+˜ÍpàÌÏ›7Op¯×…””‰DôÉ'ŸðÒ_Aª¿DMœ8‘Ð¥K—vWÿFô¿Ù×·å@ºzõªàZ^^åæær¿êÏ‚blÔÑÑ|¡Ú¼y³àþ&"JJJâœÉš²çÏŸ'"¢§OŸ’H$¢Ï?ÿœ×Géé逖.]Zk¦M›FèÁƒ‚kªh´lÙ2^úêÕ« íØ±ƒKSLZìÛ·'»aÃÀs Ÿ={F&&&äääD•••<ù3fŠŠŠâÒ>|H–––äææF………4xð`ÒÒÒ¢³gÏr27nÜ ---êÛ·¯ n¤­­Í7nÜ ‘HDýû÷çÉåää–––JzÔ¨Q$‰jý’Çx¿`k *Q¬+«m âúóçÏ_©œcÇŽÁÐÐ'Nä¥ëééaذaHOO¬§öóóƒ/M,cÒ¤IHLLÄŸþÉ¥oß¾u cUW‚‚‚xwéÒàîîwww.]WWí۷ǃ¸´‹/"''S¦L¶6?0Žbçö•+W^É®ôôt}TÚ1tèPÞߊv­™Þ©S'hii) Ö²eKØÙÙñÒzôèjCÞ½{·nÝÂØ±caffÆ»6dÈèëëóúE__û÷ïGYYzôèo¿ýÁÁÁ?~¼Ê2”””pr­¬¬”Úúª¤¦¦"##ãLJ±±1ïZPPttt”Þ_ÊîAƒ¡I“&øå—_¸4™L†mÛ¶¡K—.¼{]EEE8sæ víÚ…M›6!&&Mš4Á;w²‰}ûöå¥/úùsç`eežìë¶_]QŒ›¦¦¦‚k7†™™÷ ÈøøøÆ\Å}Ú«W/^º§§'¬­­ÇíÃèÖ­àÌ™3€¨¨(ˆD",X°úúú\ºâ_MÂÄݼyhÒ¤I­²Õ©9.*ë·ØØXˆD"În5ë qqqÈÏÏÇÔ©S!óÝece“&M†´´4´k×X¼x1o#ø‰'PYY)Ø }ô***píÚ5U÷ l333Ç~¨²\\\¸5ÞŒ¿Ìf¨ÄÉÉ @Õ¦#u(®+6Ö•Û·o£¸¸‰D¼ß¶mÛ”ÚPs°U0aÂèééá矶lÙ‚>ø;w~%û4¡¦n¥éŠkÕ73Þ¾}0wî\Aý[·n ¯&J¡[™ѳgO8‰:uÒHÿÝ»w!—Ëk«ÎEZM;LLL™¢]kÚ'‘H ‘H”nüQV¦â?ò»w華955°iÓ&A¿˜šš¢´´TÐ/®®®X¼x1ÒÒÒ`mmM›6©Ô_ņ0u¶¾*Šz¬]»VP Èd2A=ôôôеkW.mmmLœ8gÏžåô>|OŸ>ŤI“4²gïÞ½ppp@=0vìX¬\¹k×®ENNž={&÷òò¼,+9E"''þþþ‰D%K–ð®>}^ë ‡fÍšAOOIIIÛQ“ú°C‘‘‘‚4Å,œº2ýRPP q¿L™2©©©øí·ßðå—_b̘1¸ví7s®ŠfÍšA[[[­­ÕQ8¼ÉÉÉ9r$—^VV†˜˜¥õ(**ª—ûËÖÖDXX&L˜€ÈÈHLŸ>]íˈ‚sçΡ¼¼ .äÍ|¦¤¤ ==ý•O´ÓÕÕå–2Ôs¢££_I竈yóæaÍš5øì³Ï/†¯B‹-‹èèh <˜K—Édˆ‰‰D"á½dwëÖ Û·oGXXîÝ»Ç9òÝ»wÇüùó±uëVäääh|Êß| ÊÔô딦4oÞ±±±¸{÷.ïy¼rå òòòx!ò÷ree¥F÷²L&èQ£ “ÉpòäIŒ5 #FŒ@BB÷¡Щ§§W«N…}‘‘‘ðññá]Söœ*HOO‡……… Üãý†-á`¨D,cݺu "Œ3F°ŒâåË—F~~>-ZÄ›qvvFQQ¸´ÒÒRìß¿_PθqãPQQ (µ£®'QùøøÀÓÓk×®EDD‚‚‚kXU¡XrqóæÍ:•ù:tìØ...X³f ž>}*¸.“ÉÔÆ¯VGóæÍáííË—/sq…ªX¹G…ŽŽ_Ùv±XŒàà`DEE)}9RÌ(*>_¿~·Îµ²²GŽ––Ï1¨O?~Ì;°‚ˆpìØ1èèè¨-ÓÞÞþþþ Uú†ˆx±nwìØÐÐPÌž=ÇǾ}ûPPP€àààZãÚjkk#00™™™HNNæÒår9Ž?.oÞ¼9Äb±à…$""‚gPõ%©S§Nؼy³Ò5˜r¹\iÌvu|þùçxñâ‚‚‚@Dø×¿þ¥Q>ÅlßÇyé{öì©SùÊ>|8ŠŠŠŽÌÑ£G_[w]pttÄüùó‘¥ã—²™vu 6 pèÐ!^úéÓ§QXXÈ]W X²dÉèêêr{¼½½ajjʽLkê@+œLÅLj}òÑGöÓáDz¾¾¾pttĪU«ð×_ ®———ó¾À,X°/^Ä?þˆÞ½{cûöíHNNÆŒ38™¡C‡B"‘૯¾R_¼  €û‚Ñ·o_à÷ßç}ÕHKKã–—(#==]ãýŒ÷ˆ†Ù»ÈxŸX¿~=ééé‘¥¥%M˜0Ö­[GS§N%@_|ñ… ÏÕ«W ¨Š‡£FQttt­v+‹ÂATŽPWW—š5kF«W¯¦_~ù…”FÛPÄo­ OŸ>%[[[ÒÖÖ¦)S¦PXXmܸ‘ÆŽËÓOúúúÔ´iSZµjmÙ²…zöì©4âƒ"qM_”Å’­ëW…£cÇŽÔ¨Q#Z¾|9mݺ•úõëGhöìÙ¼üÊú%--,,,ÈÀÀ€fΜIaaa´iÓ&š9s&ÙÛÛÓöíÛ¹²ŒŒŒ¨}ûöT^^Îåÿé§Ÿ”†-TƵk×ÈÈȈ¬­­éÛo¿¥-[¶P¯^½¨sç΂(DDü1 oooZ½z5uëÖÌÍÍ©M›6‚zܸqƒÌÌÌÈÈȈfÏžM;vì 7ÒŒ3ÈÎÎŽÍCUÛWG.—s1¢»téRkÝ<|øLMMÉÜÜœ¦OŸN¡¡¡4~üx²³³#gggAÈ2EèšDGG®ý‰ªâ$[YY‘±±1}ýõ×J#FŒ 6mÚ¡¡¡ÆQ8¾þúk4h 4ˆLLLH[[›û{Ê”)é(++£I“&‘H$"kkkúøãiÙ²e´jÕ*úâ‹/ÈÖÖ–Äb1}õÕW\U1òôíÛ—‹åJ‹/&‰DBfff\ÈÊ긹¹q¡1«£ˆVR3¤:òòòÈÔÔT%…H}èš<~ü˜€ÿÅÖ'ªj+wwwÒÖÖ¦Y³fÑŽ;hòäÉdkkK‚{"22’ ¨Q£FôÕW_ÑîÝ»iýúõôé§Ÿ’……7Ÿ8q‚D"‘ ú’"¢È¯¿þÊ¥mÞ¼™Äb19;;ÓŠ+hÏž=ôÝwßQpp0Qnn.'»páB@={ö¤-[¶Ð·ß~KMš4¡N:)‘ššªQ4+Æûs ‘˜˜HAAAäììÌ;ìä—_~Q™'<<œÜÜÜH,“‹‹ }ýõ×”ššJžžž´k×.üÎ;©]»v$‘H¸ÿxHû÷ïçd®_¿NžžžôÛo¿©µ7??Ÿ‹ [W222hݺuÔ¿òôôä_9yò$yzzrᡈªMðôô¤ëׯót”••‘§§§ÒQsæÌ¡öíÛ Ò333iĈÜFFFÔ¦MZ°`=þ¼V»'L˜ 4QÕAþþþdjjJ:::äééÉ ]¦`òäÉÔ½{÷Z˪Inn.}úé§äèèH"‘ˆôôô¨cÇŽ¼— ¢ªþëÞ½;I¥RÒÑÑ!Ú´i“@ߌ3”:fQQQäééÉ U¥ K—.¡æÍ›“X,&}}}rww§ÿûß¼0ªÚ¾&ŠxÇÊ@Qǹs績hÔ¨RVVM˜0Ì“íÛ·/M˜0A ãÏ?ÿ$OOO.æ¶‚ììlêß¿?I$²´´¤áÇÓóçÏ÷†:&OžÌ…á¬ù«i_mDGGÓ°aÃÈÕÕ•´´´ÈÐÐZ·nM!!!‚êÆF¢ªp„_}õ¹¸¸X,&+++:t¨ D£‚o¾ù†<==éÇä¥ïرƒ<==/µ±`ÁÒ×׌EÊî7UãÈ‹/xcª‚ÜÜ\=z4™››“±±1õêÕ‹RSSi̘1JÛ<##ƒ† F¶¶¶€$ yyyÑÂ… )''‡ŠŠŠ¨gÏžÔ£GA踲²20`uíÚ•ç'$$P¯^½ÈÊÊŠ;Ì¥sçδjÕ*AxÑ 6‹‹ iii‘——mß¾víÚEžžž”ššÊ“ýâ‹/ÈÌÌLé_Œ÷æ@3êLii)?~œÔñ§ IDATtuuÉÍÍ­Ö“òj>µ!—ËÕÜ¡ ŠC)jž„÷¾P3fs}R×þ¨+šÚþ¦í¨î@×W™eeeÜéeo’êvæææ*u UÉkÂë>_DD;w& ‹ZOTÅ›ìÿꬼ+Tÿ2ñ.éÒ”/^D"œøYŸÈåò:÷Ý›+ëk {ùò%I$¾@1Þ?ØhFÑÓÓCß¾}±sçN¤§§£_¿~(**R)_3¶qmˆD"•4¡¤¤sçÎ…³³óýü&©Gª¨kÔMmÓv¼‰2ÑÞ4uµ³®ò¯ó|@xx8Ο?o¾ùF£ÍƒÊx“ýÿ6ÃÖiJõï’.M±°°À矎Ÿ~ú o¤ ‘HTç¾{ce}aÛ·o‡X,VcšñþÃhÆ+óÑGáÁƒØ·o_C› jI›6m`ii‰[·naíÚµ¯í(0Œÿ±iÓ&¸¸¸`èСðôôÔ8ö3ãïÁâÅ‹qúôéZ7Å2ª>|8nܸ!wÉø{ µxñâÅ mãýÅØØR©ôpT‰ÙÙÙ8p 6lØPïá–ïD±XŒ€€8884´9¯EEEüýý¹Øì E^^ 0kÖ,,_¾ü~)a¼{èèèÀÒÒòœá111Q[šñþ#"ªc<ƒÁ`0 ã [ÂÁ`0 ƒÁ`Ôæ@3 ƒÁ`0u€9Ð ƒÁ`0 F`4ƒÁ`0 ƒQ˜Í`0þÖ$%%áÁƒ m£HNNFffæ[+¯¸¸IIIøë¯¿4’ÏÈÈÀ­[·Þ°U £¾a4ƒñžðâÅ „„„`ãÆ*efϞɓ'¿E«^G!==ý–áåå…¥K—¾Ñ2¯Æ³gÏššúFã ûúúbæÌ™µÊ!55¯UÞõë×áåå…ß~ûM#ù‰'¢_¿~¯U&ƒÁxû0šÁxO(,,DXXbccUÊ8p{öìy‹V½³fÍBëÖ­Ú F±|ùr´lÙùùù m Î;‡–-[âØ±c m ƒÁx`4ƒÁ`0 ƒQÔäÎ`0Þ{Ž= ###tëÖ çÎãf°{õê…öíÛ äe2Ο?ÈÈHìííáççWWWž\nn.Ž?Žääd˜››£C‡ðóóãÉ”––"""îîîpqqÁ™3g'''4jÔÙÙÙËåøõ×_¹<¾¾¾hÚ´)÷÷íÛ·ƒôôt4oÞ={ö„³³³ÒºFFFâìÙ³077G@@<==5j£ôôt\½zþþþ°¶¶\‹‹ÃDZøó×®]C\\²²²àææ†¾}û¢I“&‚¼Ïž=ÃСCqýúuœ={wîÜÁ’%K ‘H@DHLLÄÉ“'ñòåK4mÚ>>>ðöö†H$üúë¯pttD‡xº333‘€nݺÁÊÊŠK///ÇùóçqæÌáïﯲݪSYY‰„„üñÇ(++ƒºvíŠV­Zñä ñûï¿#99FFFðööFïÞ½ú¶{{{#** /^„¥¥%ÜÜܸå;‡‚¡¡! ]»vprrâòggg#** ·nÝ‚­­-ºuë&°¥z[Ÿ9sFFFð÷÷GÛ¶mk­/¤¤¤pÏE||<—nkk‹.]º¨Znrúôi¤§§£¨¨ÎÎÎèß¿?ìììTêÍÉÉÁ©S§píÚ5¸»»càÀ033ÓÈ&¸zõ*Ο?û÷ïÃÝÝýúõãõ³‚—/_"22ñññ066†««+ºwïŽFi\ƒÁ¨#Ä`0Þ 233 1B¥Œ½½=™šš ÒüüühöìÙ€5jDH,Óúõëy² ccc‰DäääDnnndhhHööö<¹¸¸8²µµ%‘HD-Z´ 333@£G¦ââbNîñãÇ€¦NJݺu#---²³³£aÆQ—.]H__ŸPãÆ¹ß‰'¸üK—.%@Íš5#¤§§'°[&“QHH ©TJ-Z´ --- #4iÒ$µm›””DhΜ9‚kååådiiI:uâÒÊÊÊhÚ´i$‰x¶™ššÒþýûyù D¦¦¦´uëV‰DdeeEÆÆÆôøñcºvíY[[²±±!’J¥€rss9hܸqÛ¶oßN(::šKÛ³gI$Aºººªm"¢ÈÈH®|{{{rww'cccÁ=uýúujÑ¢ æÍ›s÷T¿~ý(''‡' €‚ƒƒiøðá$‹ÉÎÎŽüýýiÈ!dhhHÈÊÊŠëÿ°°0.ïÆ9;;;‰D¤¥¥EK–,á•!—ËiÆŒ€ŒÉÍÍ´´´hÆ djjJƒ R[ï 6p÷°©©)gKHH=}ú”D"éèè““ÙØØ’H$ôÛo¿ñtÅÇÇúꫯ¨E‹diiINNN\[ݸqƒ'ïçç'x¾JJJè³Ï>#$‰ÈÎÎŽ……;vŒ';sæLÒÒÒ"]]]jÕª9::’¶¶v­÷<ƒÁx=˜Í`¼'¼Ž­­­M]»v¥˜˜"ªúOÞÙÙ™$ ÏáiÚ´)988P^^—VZZJ§OŸæþ...&[[[jܸ1%&&Q•3wî\Àsn´¶¶6…„„P~~>U9DD#FŒ ===¥uÙ½{7 1cÆPrr2ݸqƒúöíKb±˜’’’8ÙÍ›7š1cUVVQbb"çèhâL´iÓ†š6mÊåWpèÐ!@›7oæÒ–-[FhÖ¬Yt÷î]®MÛ·oO‰„=zÄÉ4ˆÄb19::ÒŸþIDUNyEEõïߟtuuyNUee%9s†JKK¹´º8ÐVVVJûðÌ™3µ¶··7™››óì—ÉdtêÔ)ž}dbbBçÎãÒW­ZEhÊ”)<Šþ4hw¯)úúôé‚—gΜ!H—/_&"¢Û·oÓˆ#EEEq²äî™LFDDÔ¼ys‰Dµ:ÐDD'Nœ ´wï^Áµüü|Ú±c÷r(—ËéÒ¥KôÁ™™½xñ‚“U8ÐZZZ´|ùr.=66–ŒÉÛÛ›§[™=þ|@ .¤û÷ïQLL yxx¹¹9WÞíÛ·¹1¡¬¬ŒËÿüùsJHH¨µÎ ãÕa4ƒñžð:4Šç¥Ï™3‡pNuEEI$µú‰ˆ6mÚ¤t¶¶¼¼œ$ Syy9ýÏÖÒÒ¢¿þúK KݬY3211áœnÉÉÉ€þõ¯qiÎÎΤ££C<Ù™3gjì@¯]»–ðœE"¢ÀÀ@200 —/_Q•3eddDÍ›78ÛG%´lÙ2.mРA€V®\)(³S§NäææFr¹\­mš:Ð2™Œ$ =ºÖú*ÃÁÁüüüÔÊ„‡‡«´ÇÚÚš´µµéùóç<ÛPjjª@^Ý®];ÒÖÖæH>$±XL\ZçÎ eeeñdW¬XA^ÛVÅÖ­[ 8p€KS8Ðfff‚ûcÔ¨Q€"##¹´šô“'OHWW—<<<åíܹ“К5kˆˆèâÅ‹€6nܨ±Í £~`k Œ¶¶¶‚õ³¾¾¾øöÛo¹ÉZZZ:t(vïÞ   £{÷î066æå»qã gÏž¼tøûûã÷ßÇÝ»wyk¦;vìsssí}þü9²³³Ñ£GÄÅň —ËAU/ý°²²ÂÍ›7T…ËÈÈ€¯¯/$ OO=ðý÷ßkTfpp0þïÿþaaaèÕ«€ªu¯'NœÀG}@jj*ŠŠŠÐ©S'œ:uŠg[YYttt8ÛªÓ¿AÚèÑ£1uêT`ìØ±0`€Ò5®š¢­­¡C‡bÏž=¨¨¨À¨Q£”ö¡*>úè#¬Zµ  Ànj޽{ Öì^»v €°ÿªöÞµkÒÒÒ`iiÉ¥;99 ÖЫC&“!11žžžHNNÆõë×yýïààÀ xýúu´hÑööö<=—Y%%%صk®^½Š‡¢¼¼%%%€¬¬,¼ŸŸo½{÷îŵk×н{w¥å\¿~åååh×®Nœ8Á«·H$‚H$âêÞ¾}{ØÛÛcîܹ¸qãFŒhiiÕ[½ †r˜Í`¼'hkW=®2™L¥ŒL&ãäªãääÄmHS`dd$зjÕ*4mÚ?ÿü38‰D‚±cÇbÚ´iœ¤p¸;uê$(ÇÇÇ¿ÿþ;îÝ»Çs˜”ɪCáÄÄÄ ..N©Œâ  uöÔ¥\KKKôïßáááÈÏχ‰‰ vïÞ ™L†O>ùD`ÛþýûqðàA---¼xñ‚—&‘H”n|›8q"*++±nÝ:L˜0ÚÚÚTżyó`llŒüÇŽƒ¾¾>FމéÓ§£M›6€{÷î¨êëšøøø`×®]‚ƒkêÚÿ÷îÝCEE’’’0dÈ¥2R©@Õº‚‚¥rÞÞÞõâL>}úíÛ·ÇÇáää„–-[ÂÕÕ8{ö¬ÒØÑ5_XÿµÃýû÷U–¥¸¿vîÜ©4$¥žžwï‹D"9rÿýï±yófüøã°³³ÃgŸ}†I“&ÁÂÂâUªË`04€…±c0Þlll ££ƒÇ+½.—ËñìÙ38::¾r7ÆòåËñàÁœ8qC‡Ehh(zõêÅv¡˜!UÌDW'99‘,êêÄ(òÏœ9%%%J©©©œÍÕËVf¦„„„ ¤¤„;#,, öööèÖ­›À¶µkת´íÔ©S<½ªê¯¯¯éÓ§ãöíÛ¸pá¦NŠsçÎ! €ç„ŠÅb”—— ò+»7nŒ•+WâáÇ8vì† ‚ÐÐPôîÝD¤¶þR©_~ù%²³³qãÆ!<<þþþÜl«¢þêÚ»æ,z]û_ѧ!!!(--Uú{òä ÀÄÄúúúJíIKKCeeeÊVÆ’%Kß~û iiiˆˆˆÀºuë¨2²¯ õS†¢}·mÛ¦òþÚ¿?'ߺukìܹ=ÂŽ;ðÁàË/¿Ôèðƒñê0šÁxOÐÒÒ‚££#’““‘››+¸ž€ŠŠ ´lÙòµË200@Ÿ>}†E‹!;;§OŸ´hѸ¿"##¡­­­Q¸4hÔ¨d2Š‹‹yévvvJ¥ˆŠŠÒÈé³²²Bll¬Àɬicmôë×5BXXqíÚ5Œ;–7{ïîî±XŒÈÈÈ:éV‡––:uê„5kÖ`ÿþýË娵kwÝÆÆ—.]ä;tèJ†††èׯvìØE‹áÞ½{jᩎb9ΦM›°~ýz¼|ù‡îþRÖ¶Š´ºô?P5‹\cccØÛÛ#**Jé‹CuD"\\\””„çÏŸó®9sF#;ÔÙT-[±´´ÄàÁƒyéêÚ?**J¦hu__*NžåävìØçÏŸcÒ¤IœlmôìÙr¹\©3ºhÑ"\¹rsçÎEEEïÚíÛ·qùòeîï)S¦ ¸¸¿üò —öôéSìÝ»W#;èèè 88qqqX´hD"BBBx2˜:u*ÂÃñaߘ˜ÈÍŽ×Æ¸™]мŠ%6@ÕqäwîÜÁÙ³gTÅyÞ¹s'¯ €ªãÞO:%èCe:kRQQC‡ –)òêêê†[[[:tˆ·ö÷èÑ£ÈÈÈÀ°aÃÔÆF®ŽbuÍ{X¼x12331mÚ4”––ò®eggó–ö|öÙg "üðÃ\Z~~>vìØ¡‘@UZ"**JÐMš4Á‹/À¥Ý¸qGŽQ©ïÑ£G¼%>™™™8räìíí1`À•ù1nÜ8ìÙ³aaa‚ë—.]ÂíÛ·TÍr'%%ñ®Ëd2ܹsDÄõƒÁx4ÀÆÅ÷š‡’¯¯/I$züøqC›Ãø‡QYYI£GæâûúúRûöíÉÀÀ€´µµyÑ(â@×$::šÐöíÛ‰èQ>lll¨wïÞ4jÔ(.Öo`` /¢À¾}ûÈÐÐÌÍÍiàÀÔ¡C@:tà…ôRDáP_™ˆ¨  €‹!lccC®®®\„‚ÊÊJš0a800ÉÃÃD"­X±‚Ó“ŸŸO;v$ÔµkW "+++.´^]bâ*bBP‘¢°°H¨E‹4tèPêׯ¹¸¸"9(â@+ÃÔÔ”LLLÈßߟ>þøcjÓ¦ ‰Åbrwwç¢~%$$p1³½½½¹¸ÉŠØÞŠ()))\[öéÓ‡FÅÅ  Rí£¤¤„¥¥%õìÙ“‚ƒƒÉÍÍD"uîÜ™VïÔ©S$•JÉÄÄ„úõëG¾¾¾¤¥¥Eîîî‚HP±ƒ¨ªöYYY‘««+ýúë¯Üõ™3g’X,&kkkêß¿? 2„¼¼¼H,ÓôéÓ9¹ÒÒRêÑ£ Ž;ÒÈ‘#ÉÖÖ–>ÿüs211Ñ( q±—q³!ù.^¼HÚÚÚ¤­­M={ö¤>}ú±±1}óÍ7€-ZÄéPDáøôÓO©Q£FÔ½{w6lI¥R’H$tòäI^™ÊÂØåääPÏž= µlÙ’†N}úôáÚ*<<œˆþ…ÅÍ͆ Bƒ âž§ÿþ÷¿Õ™Á`¼"¢Z¾2x <óçÏG¯^½ššªôÔ2ãMsêÔ)œ>}ÐÕÕE‹-ÄmôªÎÚµk!•J3©YYY ÅàÁƒÑ¦MTTTàôéÓˆ‰‰Á;wPYY ???tëÖ ‚Mˆiiiصkï$BÅF8………X½z5ºté‚=z(­ !)) 111xùò%FŽ 777îzll,Nœ8ÔÔTˆÅbØÚÚ¢GèÕ«ôõõ9¹²²2üðøxñ",,,н{wañâÅhÛ¶­ÚY¿š|÷Ýw(((@=¸“è”qäÈÄÆÆ"-- °··GŸ>}àïïϵï¿þЬ¬,Ì;W_qZ`JJ òóóÑ¡CtïÞ:tl½ví~þùg}ú ´´Tà@?{ö ¶¶¶X°`-ZÄ¥7oÞ­[·FDD„@—ÂVÄe0 ƒÁ`ü3`S§®^½ ™Lwww^º‡‡.^¼¨4OZZšÆú·lÙ¢ôHVUk± ƒÁ`0ÞÙÙÙ‚´Ñ£Gcâĉ `ÍûÁß~ ´&>žwZmy>|ˆÌÌLµr—.]âtÖ”‹ŽŽ~m›k¦:uª^õeggs‡zhš÷Ê•+())Q+WýpŒêrŠkõÙ.ååå8sæL½¶ËÝ»w_§¼Š:«’+((àBX]îÎ;Üs[_õÈÎÎÆÕ«Wëµ]ê:^)ÆurêÆ«¸¸8nü©¯z\ºt wïÞmÐñJ1¶¨“S7^EGG¿¶ÍÊÆñ†¯c‹:9UãUõq§¾êQ^^ŽK—.5øx¥j}úk÷×ëê¤JgVVµiÓ¦NåÕFnn.ï0ކÒY[;ªÓ9wî\š;wnÊ« 6¶(‡-Êac‹¿ÃØâááAu*ïŸ[Âp±œÓÓÓaiiÉ¥§¤¤@OOR©ôµË(((ÀâÅ‹y÷_‡êMÔuÕ©­­]ëáêtúùùÁÔÔ´NeÖFŸ>}x‡k4„NgggÁÑÃuÑ©îàŽWEÝ!oKg=x_ÔE§©©)üüüêT^mèëë£OŸ> ®³¶vT§³C‡u*KØØ¢6¶(‡-BÞç±%&&111(((àùC %4´ÿ6Q5}îÜ9@›7oæ¥ûúúÖË{@@uêÔ‰233ë}ã}æMÌèüx3:ï;obFçïÀ›˜-þ;ÀÆå°±E[øäææRff&uêÔ‰Í@×[  cÇŽ°±±Á•+W¸´ÒÒR$''cèСõR†¾¾>êe6›Á`0 £¾‘J¥ppp¨÷¯-G´/^¼¸¡x“¼|ùk×®E\\¢££‘››‹’’ÄÅÅÁÝÝ‹ÅÐÒÒŠ+`hhˆ’’üûßÿÆýû÷ñË/¿ÀÌÌìµlˆŽŽÆÓ§Oadd°²²ªª½÷8;;ÃÊÊ b1{«Ž››ûtVmmm¸¸¸ÀÜܼ¡My§°´´D³fÍ`hhØÐ¦¼S°±E9llÂÆ>7nÜ@TTΟ?æÍ›¿‘%:þö£Kqq1"""333XYYqWßý‚T*… š4i‚… âUfË…°M„Êa›k‡­~K(6*ÖÕ•Áƒ§ 3³U­rúú‘ÈÊzµÅÿ ®Å¯¿æAKKªVN.Á¥K¡pppx¥rÔñòåK>ÿüs@FFŽ?޳gÏâØ±cøã?4ÖÕ¨Q#Á:®ØØX$%%aĈ°¶¶æÒÍÍÍëÅþw…ÜÜ\ÌŸ?mÛ¶Åœ9sààà€ØØX9rƒÆÁƒ1tèÐZõDEEaΜ9J¥°··GVVÖ+ÍdÇÆÆbôèÑxüø1† † &ÀÚÚ·nÝBll,‚ƒƒñ×_aêÔ©€1cÆ "">>>øüóÏáéé‰û÷ï#..ûöíÃæÍ›¡­]5|•••áåË—(//¯³]oŠo¿ý§N‚ƒƒ ðüùó7öÒù®“••…™3gbãÆðððhhsŸŸŠŠ |ñÅ€‡âøñã8þ<:„‹/BOOO/..IIIÐÒÒ–-[°`ÁˆÅÂ9¨ÂÂBnS ¸GѲeKTTTàÞ½{HMMů¿þŠU«Váÿþïÿðå—_òt•––Âßß7nÜÀܹsáéé‰`éÒ¥xñâ6nÜXÏ­Ãø§ˆˆÄÅÅ¡K—. mλ 1Þ8ãÆ£qãÆ½–LÇŽã Z..êËQǤI‹ȬµŒÆQffæ+—£ŽÆ“½½=/-??Ÿ?~üµôOŸ>P||ü+å_±b ””äÔÚ÷o‚ÂÂB:wîœ =&&†PË–-5ÒóðáCÊÈÈ ¹\Nñññ€¦OŸ^g[š6mJ¦¦¦têÔ)¥2;vì ;wQRR V­Z‘\.Ⱦxñ‚—¾qãF@Û·o¯Õ–ï¿ÿ¾Ný÷ª$''Ó_ýED¯Ͻï(î›7¾±2&MšD¨¤¤¤VYWWWjܸ1/­¢¢‚|||mÙ²Ei¾ñãÇ“ŽŽ-\¸Ðü¡TÎÏÏO`‹â¾Û»w¯@þüùóÔºuk@»víâ] %ôÝwßñÒHb±˜îÞ½«Ô†ÌÌLðÆÆiÆßMü–:l Ç[‚-ÈuŒ1nÜ8@bb"žÄüùóÑ«W/¥2cÆŒÁÇ \;Œ1Bi¹¯eOu=z„‘#GªüíÝ»—'áÂŒ;^^^Àþóäåå ô¶jÕ굿jŒ;Vé c||ûÐyßóŽ;÷}‡sÏûœó^¹r%TUUÑ»wo4kÖ .\@ÇŽ«õ_^¹r%–-[SSS8::âÔ©Sh×®D~žçÏŸ‡öîÝ %%%XZZ"00íÚµ8|óæ ìíí±lÙ2ÈÉÉaĈ°±±AHH"""$º¶¯!"dff¢yóæŒ Ä·&88²²²øí·ß$’××ל>}Z¤rÚÄÇÇ#00P@™Ú³gºté‚û÷ïÃÅÅzzzضmìì옘Øàc8yò¤È—¸”” ¼ ÆÆÆ"00Ë–-È#PPPMMMœ9sNNNÊè³gÏ`kk‹õë×C]]LJ¥¥%NŸ>-0agg‡mÛ¶AOO...xðàºwï.¤Ø?xððôôÄž={ ­­ p8ÈÊʤ¥¥!//yyyÈÈÈ0uwïÞ gggÀØØZZZðóóƒ­­-"##ú™3gƇœœôêÕ ™™™èÖ­âããë÷eHKK‘®i§NBQQ~ùåhii¡_¿~ j°ùiff†_~ù>|ÀÓ§O™òÈÈHÈÊÊ m3meeÅœ¯ l¡0l¡hØ B øÎðÿéÂH*'êøñ]8.^¼HDD{÷îøÌçÚµk€vìØ!²}QËéYYYTRR" ÷éÓ'ÒÕÕ¥fÍšQUUSÎwáÐ××§¬¬,¦üÑ£G€ú÷ï/оrá((( jÑ¢…@ýììl233#*++#"¢?ÿü“ÐÝ»wÚäñxT\\,òúımÛ6@K–,©uݺ¸pTUU‘¼¼<™››K\§¢¢‚Ú¶mKHNNކJ~~~”™™)R¾¡]8Þ¾}KúúúdllLDDôîÝ;RRR";;;*((`dïܹChРAÕ¶WW999‘¿ ýõ +W®0eüyillL LùñãÇ ÍŸ?Ÿ)›9s&IIIQtt´@»UUUÏÁ Aƒݹs‡)+(( ;;;RRR¢wïÞ1å#FŒ äêêJ¥¥¥íÖ䑘˜HrrrôÓO?Qaa¡@¹‚‚uîÜ™)‹%.—K½{÷¦ÊÊJ¦|Æ  Î.UUUôàÁ222"ôúõk¡:;w& æÙ "´sçN!ÙÚºpðÙ²e‹@›EEE€lll„d?~üH¨]»v"Ûb]8Xê ëÂ!ÖÍòÑ››‹yóæaÞ¼y:t(tttœœŒ=z0K™£F‚ŠŠ öíÛ'PwïÞ½PTTdÜ$A[[ eòòò?~<ÒÒÒðîÝ;¡:½{÷†¶¶6ó¹]»v°±±Á… jŒ¨?qâ233ñÛo¿ Ô×ÒÒÂèÑ£‘””ÄX ÔÕÕ@ÈÊÅáp ¨¨(ñõñyüø1fÏž kkk,X° ÖõëBzz:JKKa`` qiiiÜ¿‡‚µµ5NŸ> ÁÇǧA–ì«£°°ýúõCII .]ºŸï[qq1†Ž&Mš0ò]ºtA‹-pîÜ9dgg³qIʈ#`ffÆ|îׯ¸\.bcc™2MMMðx<¡´€\.—y>|ø€sçΡE‹èÒ¥ #Ó¤I >ÅÅÅ8vì˜Pÿ£G€WÛ·oGYYfΜ eee¦ÜÔÔîîî¸wïó½:t<£F‚””#ëééYk—ž?ÂÞÞöööÐÓÓCÇŽÁãñpíÚ5´hÑB@6!!÷î݃‡‡cMïÛ·/´µµáïï_«~k‚Ÿõ€¿šÁÏcaa!$«¡¡--­ožM†……¥zØ,D}³pHж6PWÝèÔ)àÁƒ†O](,,Äž={***hß¾=ŒI“&1(•””0vìXøùùáÝ»wÐ××Gff&Ο?1cÆ@UUUâþˆGŽÁîÝ»ñæÍ|øðA ‚>55Íš5¨Ó½{w¡vzö쉨¨($&&VëÿÊ÷A òµä+êqqqprr°aÃ0{ölôïßíÛ·ÇÀ1f̘:¥Љ‰AŸ>} ­­‹/2ŠÀ·F[[\.·Öy—åääàééÉø†ß¾}ûöíÃÞ½{qðàA>=zô€””nܸ###‰ê5òòòLú»ºbmm kkkL™2sçÎÅúõë±aÑÐú0eÊ\½zþþþBJZnn.€ÿÝã/á—ñe¾'jjÂ)(¥¤¤^õõõñöí[øùùÁßß³gÏÆ‚ 0dÈìܹuº^999¡MqdeeA^^^ pŽFŒ%%%ŸŸ+yyyæùøzLµQ UUUÀ|Žˆˆ€‹‹ ˆgÏž1/˜<GŽ””6oÞ,Ð߯Üßß›6m’¸ïêà[ž[µjàóJ¬¬¬È Æ¸¸8TUUIüðe=þ‹Ëg*++‘œœ,ägþ_…oä9rä÷ÊëÂÑH°ù ••:wîŒýû÷£ªª û÷­-œœœjÕÎÎ;ѬY3cÉ’%6lºuë&ÒuƒÏß çÚ¾sçÔ¨ ò—‡—-[†€€‘GŸ>}ù&Mš`Ú´iˆŒŒD||™Dáëë‹äädÌ™3oÞ¼©µõ¹¢¢ppppk(++«q î[·n |æñx¸uë455kü£Ö©S'¨6_M˜››cݺuÐÓÓ“HMMM…««+ŠŠŠpýúuX[W¿¡´´´ÞŠ€(V®\ 999Ì›7¯Z+mzz:ÂÂÂ|þƒö¥ÅôKøß{C*«§OŸÆÜ¹s1jÔ(,_¾\¤L›6mׯ_(¯¨¨À;w ®®.à{ÜâîÝ»Bé´kkkìØ±ÊÊÊ̼211ºº:îܹ#4'øßAûöí%jŸŸQTZºN:¡²²ÇÛŽ££#ág/22²A|gΜ ¬[·Ž™§|÷;wî ..Nè˜?>²³³qñâÅ:÷[XX¤§§cîܹñ£G¡—þ®–üó’ÂîD( »¡hØÅúpüCðövƒƒÃ!±rÚÚ’ýQÅ AÝÜ+'#£&r©ø{0dÈhkkcÓ¦Mµ>û2¶nÝW®\ÁÅ‹áææ†ââbÌœ9S`ké¯ Ç0vìX”——cñâÅÈÉÉÁªU«Rt}MÏž=1`Àlܸjjjðõõ…ªª*Š‹‹ñäÉøûûcÛ¶mhÒ¤ 6mÚuuu¸»»CKK 8yò$Þ¿/ÒûKrrràêꊔ”,_¾ éÚµ+4–’’¸¹¹ È•——3 ß;%%…‘iÕª•X—SSS¬\¹sæÌ­­-Ö®] GGGW­Z…eË–ÁÙÙ/^ÄòåË1mÚ4tìØ&&&HNNFhh(Ö­[‡ƒQ£FÕØ§¤¼xñcÇŽ… V¯^-duQVV†²²2† ;;;?~={öÄ AƒPTT„3f ¨¨6lžKIIÁË—/™ÿŸç ßm§{÷îbýÐÝÜܰaÃÌž=sæÌAJJ vìØ!Ò*]–.]Š–-[¢OŸ>PWWG^^üýýQTTÄX{eee±páBÌš5 “&M¦M› ¬¬ŒsçÎáøñã°³³Ã!C$êï£|þüytèÐæææPVVF³fÍ0iÒ$ìÝ»³fÍ‚ŒŒ †EEEäååáþýû8þ<“ ÙËË +W®Ä¶mÛйsgtêÔ ééé˜6mê´Cæ—HKKcÑ¢E7n6n܈3fàüùóptt¬vyôèÑX´h<ˆAƒ‰í#66·oßfv"LHHÀ¡C‡‘‘nݺ19³ùŒ?ëׯÇÔ©SѼysXYYáìٳعs'ìííFŒArrrÕ^K`` uìØ‘¤¥¥ õîÝ›9—ššJ}ûö%Äápž‡®]» ´óøñc200 ¤¨¨HhìØ±4a„:§±û’ŠŠ 233£&MšÐÚµk mݺµÆöºvíJRRRÌ=­)Ýׇ¦¦&uîÜ™.]ºTmûááᤩ©É<+ÈÁÁ¡Ú´ŽDl;–úÃê$âáU³VÊÒ`xyy¡¬¬LhW³¯ev‰-&&G ‚8–/_Ž%K– ,,L¬ÿszz:²³³Ñ²eKTpÙÙÙ¸~ý:ŠŠŠÐ¾}{ØÚÚ"??©©©ŒÅ øôôîÝ;´jÕ eeexðàRSSÑ¡CÆz÷%ÏŸ?‡†††Èå°ÄÄDDFF"55zzz°¶¶ºîÄÄD<~ü™™™hÙ²%ºté"Q »OŸ>áÕ«W5Ê´nÝš±‚–——#66***î•••"ƒ˜øÔ*óDUUâãã™ewSSSØÙÙ‰Ls÷êÕ+DGG#==JJJ044DÇŽ™ 2>?~ÄÛ·oahh(v÷¿ììl¤§§£U«V““CNNRSS«•×ÑÑÔª¬¬DDD¢¢¢ ¦¦†víÚ‰tÝà÷SÖÖÖmdSVV†[·n!)) mÚ´A»víPTT„ääd˜™™1)õ¾œ—_§‘‹‹‹ƒ”””@z¶¸¸8<{ö YYY°¶¶†‹‹Kµéçøs0//ÖÖÖpttZeIIIA~~>lmmk¼ž‚‚$%%¡I“&Bn8ÑÑÑˆŽŽFVVš5k‘îP¸wïÞ½{'''X[[#==>|€Ø”vü<þf$_Ãÿ––Fee¥ÐoÅ×¼ÿ™™™055…ŠŠ k>,_ÏEEE4kÖLât”………Gbb"Ú´i‡Wº’““abb‚¤¤$€g6ˆP6ˆP4#GŽ„œœÜ^'© Vn¼¼¼œœŒHKK‹üÃÉ*Ðu#??fff°µµòdaaaù/RíååÅþùм¼<,]º”õƒþÿTVV¢²²½{÷†±±1;_j€ "l$Þ¿éÓ§ ù¡²ÔØØXlݺîîîÈÏÏǶmÛ¾÷XXXX~hXeH6ˆPLŸ>]dþqAØ ÂFÂÙÙ™Ù„¥þ<|øK—.…­­-üýýkÌ0ÁÂÂÂÂÂÂ"žŸþ?ÿü³Ètž,‚° 4Ë?ooox{{ïa°°°°°°°üa]8‰²²²ï=–ÿ0üT”,ÿ£²²’Ù&žå°:‹xXº‘`wõaaaaaùž°; ÃîD(Vg«@7ì®>,,,,,ß6ˆP6ˆP4¬Î"Vfaaaaaaaaa©¬ÍÂÂÂÂÂÂÂÂÂR Xº‘`òYXXXX¾'l¡0l¡hXE<¬ÝH°ù,,,,,ß6ˆP6ˆP4¬Î"Vn$”••ñüùsddd|ï¡°°°°°üaƒ…aƒÉÈÈÀóçÏ¡¬¬ü½‡òÃÃn¤ÒHdddàöíÛprr‚®®î÷ÎKhh¨ÀÒ‘ŠŠ LLL §§Wë¶òóó&‘¬‚‚ºví*Vnß¾}ظq#®\¹±ò–––4hÖ¬Y#Ñ8š¨¨(DDD -- :::hÛ¶-Ú·o_ëvJJJ‰¼¼<ÃÒÒ²Nãá·‰¬¬,˜ššÂÎÎVVVB²•••¸zõ*âããQPP]]]´jÕ ŽŽŽ““cäÞ¾}‹˜˜ØØØÀÀÀ Æþ9‚Õ«WK|ÿêKFFbbbPQQggg¨ªª~ó>ÿ‹,]ºˆŒŒ„¬¬¬XùŠŠ „„„ !!………ÐÕÕEë֭Ѿ}{¹Å'&&áááHKKƒªª* áìì,ô»ÂüŸËåBGGÍ›7‡††Fý/’…¥HNNFxx8222Øy+bùæxzz’§§g½eøäååѵë×hùºåt>ˆÞ¿_ÿA~Eii)Ý¿ŸÖnZK‡¦øøøïC:::@èprr¢Çת­°°0‘m‰:ŒŒŒ$jsÍš5€^¾|)‘<‰ïkC’››K-[¶d®Ãá0ÿ4h}úôI¢vnÞ¼IVVV$%%ÅÔŸ6mZÆtæÌÒÖÖfÚ‘‘‘aþß³gOŠŽŽfdÃÃÃÉÔÔ”9¯¬¬Ìü_NNŽJKKÙÝ»wò÷÷;†Í›7×êþÕ• ¾¾¾À û¦}þ¨DFF’:uê›õáããC$š×÷îÝ####‘sK^^^@¶¼¼œ<<<˜çG^^^àYسg€|u¿/JJJäííMIII yÙÕ’””D­?–µÑIþ«°.DC9äï>°î¸w þHþÆÃq´#¦ÍŸ†ŠŠŠéãÖ[°ïe¾ûb^Ü}ú„k×®ÁÍÍ çÎìY³$j'33JJJ˜4iæÏŸ_çñ,Y²C† šš.^¼ˆ¬¬,”––"..~~~xñâ®_¿à³Oâ!CœœŒ={ö 11………ÈÍÍEpp0~þùgp8œ:¥1HII­­-þøã¸¹¹}ïá|WŠ‹‹ñâÅ |øðá{yyy:t(ÒÓÓ±ÿ~$%%¡°°999 BïÞ½ä/^Œ€€ <÷îÝC~~>>}ú„ÈÈH,X°@䊂óûuïÞ=9r8räZ¶l‰£G6Öå Á ÃІ "”€ï­Áÿðôô$WWW±2âÞöþXó© V!,…Ð!÷«õÞ·Þc ¹BZ]´‹…ûàLå•‹•••Õ»ŸêÐÑÑi îÞ½»ÄVÆš˜6mZµÖÀœœzúô)EEEUkÉúÚýñãGzþü9•——‹”G 說*Ч/^ˆýNsrr(,,Œââ⨢¢¢FY~Û<O¨<--dddH[[[l_÷è×ÖK222äàà@EEE"e>|ø@OžtýH Ÿ'MÂë–¯1cÑŒ:÷QW† HHH@FFdeeñË/¿ˆ”0adddðþý{‰Ú~ôèÚ´i ´iÓ666PVVÆ´iÓPRR"²NNNºví ---ØÛÛCEE»wï–¨?"¦M› ªªŠ-ZÀÎÎÊÊÊX´h‘Ð*–-[àèè---8;;ÃÒÒZZZ¸yóf}p¹\‘VZØØØ //•••·¾,^¼X¿~=”””DÊhjj¢M›6þý­¯¯ÿÍLJSí1oÞ|8.]º„Y³faĈøøñ#-Z„¡C‡âÔ©SÌKÞ÷däÈ‘ÐÓÓñcÇPVV†Í›7cÉ’%pqqA÷îÝÇŽâE‹0räHlذ&&&HOOúNŸ>aÆ¡C‡8zô(455ñ×_aãÆÈÈÈ@pp°€|UUzöì‰#F`çÎÈË˃••”••1yòdf.N}®]»àëë )))L˜0ÀçO777DEEaÆ èÛ·/âââ0mÚ4ðx<‰¾Ÿ¶mÛø lnnŽÁƒCEE¥ZùvíÚ!44S¦LÁo¿ý‰ú©ŽÑ£Gã÷ßGddd½ÚaaaùøÞ&ðÿ Dè:È•ð‡°[…Ða+Lu‡ÿ'^®y¯æÕ.Å×—¯]8rsséðá䨨H222KDD·nÝ"´uëVú»ví""²ýÚ,§wìØ‘¤¥¥©¤¤„)ã»p|½L[RRB dff&°ޝ\8^¼xARRRäîî.Ô_Ïž=IVV–Y.^ºt) ÄÄD±c•www@W¯^­uݺ¸p‡Ã!;;»ZõµnÝ:&¸KVV–œiëÖ­"ç]C»p‡Ã¡1cÆ0e¡¡¡€<<<dóòòHFF†LMM«u;¨« ‡œœœÈß„¿þú‹Ð•+W˜2þ¼ìÞ½»€lDD qãÆ1e¾¾¾$--]£{Gee%™šš’ŒŒ œ6l [·n1e#FŒ 4eÊ¡¶jrá8{ö, ™3g ”óx<244$mmmÆeéÔ©S€æÌ™# {ûöm&XO’ ÂåË—“¢¢"êììLÛ¶m£ââb!ÙÈÈH²²²bÚ755¥‰'2¿C_#ê·ákš4iBZZZbÇYØ B–úÂЇuáh$ê믬¤ |ëwyŸÄ‹É äåå¿Ù0Þ¾} ]]]èêê¢iÓ¦ðôôlÛ¶ ­Zµ¸ººÂÂÂ{÷î¨ëççôêÕ«Öý¾yó×®]ÃÑ£GqèÐ!¢²²)))B²?ýô“Àgtî܉‰‰xûömµ}\¾|UUU˜6mšÐ¹Ñ£G£¼¼ÏŸ?&­ÞòåË[ëëùšeË–!88“&MªÓ÷Srrr@DµNß6gΤ¥¥aãÆ°°°À£G0mÚ4bÉ’% ¢o2ÞÀÓÓ]ºtÁ˜òÐÐPúÞTUUáää„7oÞ 99ù›Œ©6|moÓ¦ TTTæp÷îÝQYY‰%K– 11Qd;©©©xóæ œÑ¤Is|÷‹[·n Õû:OçÏŸ‡Ã¯¯¯@9‡ÃÁÈ‘#‘ÍŒï¶ôõJFÇŽk•³vñâÅHKKÃúõëannŽˆˆüöÛo044ÄŠ+dmllëׯ3AÌ{÷î…••ÜÝÝñêÕ«Z]/ðÙe ??ÿ›Íáš`ƒ…aƒEÃЇU ‰úîêÓ«s/pߊ¹]Å@o«Þx?ó}Ž›ónB#SLÞG .¥)©ü<ꉒ’<<<àááyóæáàÁƒHHHÀ¤I“ä&Mš„˜˜ƽãáÇxñâ&L˜P«, øé§Ÿ`ff777Lš4 K—.e²Bˆºwüåð/éÙ³'€ÏŠxuÄÇÇúõëyyycâĉÀ(5]»vÅøñã+++´lÙk×®ENNí]t6mÚ„¥K—2Ëë…k|©¨UUU̘1ƒÉ½nÝ:ÈÊÊbùòåB/N Abb" ###œ;wN ŸpRR€ÿÝã/á+Õü{û=éÒ¥‹Àg)))èéé¡°°)ëß¿?F…;wÂÜÜÖÖÖØºu+ þ_Q—ë•’’‚««k­Æ"‚•••ÐóÀw⿘¼yó²²²pqqhCFFF¢î_¢®®ŽÙ³g#::™™™X½z5¸\.þøã>|XH¾G¸párssqùòe¸ººââÅ‹9rd­ú-//Gzz:LMM¿K&v'BaØEÃîD(ÖºQ­üêë?nì8l?²¯_Wë£Ü,¢vÚ ]åºmÔ¢ë¤ iÜÉ»TãrªþD |Ô©}IÑÐÐ(¨ÃËË ,ÀÞ½{ѱcGøùùAFFÞÞÞµêoüøñ¸uëöìÙƒ=zÀÄÄ\.ë֭üyóDZŠŠ‹‹…ÊŠŠŠ Æø/ÇŽ«ÖjÆ·²s8ìÛ·«V­Â¡C‡pöìYÌŸ?Ë—/Ç7бcG‰®o÷îݘ9s&ˆcÇŽÕÙº.p8´lÙÑÑÑ(,,²fJŠºº:æÌ™===üòË/†Oƒ3''}ûö‡ÃÁåË—üêÿÝSþ=þ~™¨ 8êCu¿1¥¥¥ÕÖ‘äÅVVVÇÇÚµkqèÐ!œ>}Ó§OÇâÅ‹ñ÷ßÃÖÖ¶N×+-- EEE±ý=^V+cggÇŒ»²²¥¥¥PPP9¦º ©©‰ùóçCKK 'NĹsç˜U¯¯áp8èÓ§ºwï}}}<{ö iiihÖ¬™D}ÅÆÆ‚Çã¡eË–uo}`ƒ…aƒEÃЇµ@ׂ>}ú yóæ033ƒ‡‡G£¾¡)((ààŸÑüFs ÿ«“e€î]Ìô˜)ôTüÐ"¼¸ï¾š<@í‘ú›ôÇàþƒëÕGC¡¦¦œaРA())©µU’Ãဈ„Ü"øÛ™Vǵkׂ¬¬,<|øíÚµ«1ûÇÀ¡¨¨ˆ… ŠÜø¦´´”É( ÊÊmff ¼|ùRìµ9r'ND=pæÌ™-ã………سgOƒ)g_2sæL˜ššbÆ 8uê”Ðy‡íÛ·3>DZ±±xúô©È¶øî'ööö 6¾_ý•Ùð‚ŸríkÜÝÝÁårqቔ”<þŽŽŽ[!%ÅÔÔOž{ö Àçà5MMMŸƒ]ÅVúøøàþýûh×®|||˜˜ˆ'N mÛ¶¸wï^¯yøðáàñxèÕ« ‘––†sçΡ¸¸cÇŽeä6n܈Ÿ~ú ...˜={6455ˆ›7oÂ××Wâ”n222èÖ­Ž=Šììl˜››ÃÂÂÞÞÞèÒ¥ ¦L™‚íÛ·#%%ÐÕÕEzz:ÂÃÃqÿþ}&¦ÀÕÕîîîØ·ox<zö쉄„lÞ¼¶¶¶¥†{úô)¼½½æVRRBCC}}}Ìœ9“‘÷òòÂôéÓáîîKKKÈÊÊâÎ;¸}û6ˆ7nr›ILLdä> )) ¨ªª‚——Ö­['Ñ÷ö-ˆ‹‹ƒ¥¥åwëÿG¤²²ÉÉÉ077ÿÞCù¡(++kp·´ß+ýÇ?___²°° ®]» ¥wªŽ†Ú‰ð¿€‹‹‹P*.ql۶К5kÄÊ®^½š,,,èùóçLÙ‡häÈ‘$//ORRRdkkK{÷gÏ’……=|ø‘Ý»w/YXXP\\ùøøŽŽq¹\²··HëÅÇ‚æÍ›'T~÷î]êܹ3©¨¨’‘‘!Z¸p!³«á_ýE...$//OHWW—ÆŒC'Nœ{/_¾$ ‹´´4F>--,,,hüøñídggרÆáÇŎ…Oqq1-X°€:tè@JJJ€´µµ©wïÞtýúuF.==–-[FNNNL*;.—KúúúäëëK)))í……={Vì>LôæÍ""æWwlܸ‘©ËãñhË–-Ô¢E âr¹¤¨¨H;w¦§OŸVÛOuGvv¶Ø±òx Kƒðe¡®®.ów†Oii)[Æ– ”ñw8å—ùøø@JJŠÕIj€ÍÏõ€p@ ÿ3BÕYYÙoÄÂÂÂÂÂ" QÁ–l[öuÿÿü¿ÎoÎ" ëÂ0;Ç}½»[tt4$Âßÿ ///Õ»-–†&((^^^øûï¿¿÷P~xX`òd~½ÕlTTTƒåÐlß¾=:ÄlGËÂÂÂÂÂÒ˜°A„°A„‚ 8‡Bûöí¿÷P~xX@»ví`ff&ðÆ•››‹èèhxxx4H™™™ Ò K]øz«z²üVgÏ¿Þúǘ5k€Ï媪*&`oÍš5ÐÓÓ‡ÃÁŠ+0vìX¨««ÃÁÁûöíCÓ¦M1qâÄGjj*¼¼¼0pà@Ö ÍÂÂÂÂÒè°a¨©©aË–-ß{? AAA Bjj* ¿÷p~hþõ ô—ôìÙ³Ús#GŽ„††üýýqàÀ899aþüùõÞÝŽ‹‹ ûãÅÂÂÂÂÂÂòÃÂ7òñ ,Õó¯W µ´´$V\ÝÜÜàææömÄòæÝ»wHMM…½½½D[ ‡‡‡C[[fff0:qddd 99YâûÇÒpdeeáÍ›7°¶¶–xw–•½ý£PRR‚ÒÒRHKKW»#"ËçdîyyyÌg˜˜˜`À€B݈#!!‹-’HV[[Û·o+wäÈÌŸ?/_¾„¥¥¥Xygggxzz6úê!<<§OŸFDDÒÓÓ¡¯¯GGGÌž=›É<#ŽòòrDFFâÉ“'xöìòòòзo_üòË/uשS§póæMDFF"++ ¦¦¦°··ÇäÉ“add$ Š]»v!>>ÐÕÕ…••\]]1räHp8À7°ÿ~øúú¢k×®5ö€ßÿ]âûWWrrrðäÉ?ÿü3~þùçZ×ÿ¿Ñ£ñáéS(ŠQ¾Óåäp5"BhÃIس};.nÛ-1–¹×¥¥8s÷®ÄJXm BNN£,¿}û999X´h–-[&±B ÅÅÅÌN’|²²²›› ### ¤AÃ\ÀBff&:vìyyy888ÀÉÉ ÷îÝÃýû÷±oß>„‡‡ÃÊÊJl;'Nœ€···@™®®n­èÌÌLL˜0ÁÁÁÐÓÓƒƒƒZµj…ØØXìܹ›7oƉ'0lØ0ÿÛÄHNNŽŽŽèܹ3Þ¾}‹àà`ìß¿C† œœ€Ï/JèÝ»·Xº±5j®^½*P6}úôötYYàããƒ={ö|ïáàÁƒ „••¤¥¥‘——‡””@§NpóæMæþIEE–,Y‚ììlܼyãLJŒŒŒÜÓ§O(pO²²²èêꂈ‚¬¬,€­­-V¬Xþýû µ·eË̘1VVVpssCxx8Ƈ˜˜lܸ±!¿šz±víZÖ•ð+øA„¬ôgBBBpñâEÜ¿¿F·WÄòÍñôô$OOÏzÉÜ ¥•êêD@µÇEÚ¾bEÇùñãGò66®±L€&öî]ç>Ä¡££CFFFÌ犊 ºzõ*©ªª—Ë¥'OžÔ«ýiÓ¦ «Sý5kÖzùò¥DòÄÞûoÁÇiñâÅ”••Å”ñx<Úºu+ Î;KÔNDDíØ±ƒÂÂÂèÖ­[€¦M›V«±ðx<êÔ©q¹\Z°`•—— œÏÍÍ¥_ý•¶lÙBDDÉÉÉÄårIKK‹òóód+**(88˜*++™²Ý»wò÷÷;–Í›7×êþÕ:~ü8ÅÅÅÑo¿ýV¯9×|úô‰Ï7냟$ùFŒAèýû÷LÙëׯ©{÷î€V­Z%²Þ™3g¹»»:sæŒH9Q¿¡¡¡€–,Y" [RRB$]]]@çß½{GÒÒÒÔ¡C*--%"¢ªª*0` G‰½Þ†&))‰PRRR£÷Íòï@½å¿kþ‡àÒ­¶›™¡ðñc4©Ffóæ˜=»Î}hhh ™›"÷î…m5;¼oÔÕÅŒF|S—––F¯^½àáá???ܽ{FFF†„êDFFâéÓ§pww‡¦¦¦DýDFFâÖ­[HJJ‚ŒŒ ,,,àáá&Mªû¶?o¼sõêU¤¦¦¢C‡2dˆÄî9UUU¸xñ"ž>}Šââb888`È!"ýrãããqõêUÄÄÄ iÓ¦ppp@=jô#ÕÐÐXއ___¬]»¨¬¬;ÞöíÛ3ù@ÃÃÃ%º¶¯9vìîß¿ &`ÕªUBçÕÔÔpàÀfGЈˆðxÎ;Wíy´mÛ–ùœ““ƒ   DEEAMM íÛ·Gß¾}…ê}€SÓ²¿8Þ¾}‹<{ö ššš°¶¶FÏž=¡¡¡! ÷ðáCܹsoß¾…¥¥%úöí ™¨¨(Û¶m#´xñb*//']]]rttÙFçÎIGGGÈÒI$ÚòHH]]Ú¶mKêêꀚ5kF‘‘‘õùèíÛ·“¢¢"µjÕŠ uíÚ•rrrä!ÂOŽŽŽ€š4iB€,--)..Ž‘+**bä´´´¨C‡dffF‡þúë/q_eµ˜››“††F­ë………ÕÉíääD\.—ÒÓÓ%’&4`À‰äëcŽ'¡CMMÐܹs™ºÿý7””Y[[“žžcí,((¨¶Ïº¬zTTTP¿~ý©ªª’££#µhÑ‚¤¤¤hÍš5ŒÇ£éÓ§‡Ã!555rpp 999RRR¢cÇŽ ´ÉŸ»~~~¤¨¨HM›6%Ú±c3‡•••ÉÈȈŒŒŒ¨]»vLÝ7oÞ““#Ó¼ys@æææ%ÐϳgÏÈÀÀ€¸\.YYY‘¾¾>™˜˜Ð‚ êe&ú¼"€~þùg¡:ïÞ½#)))úõ×_‰ˆè×_%)))z÷îlm,Ð_òÇÚ»w/SöÓO?zðàÐx™™™ØëmhX 4K}a-Ðâa7Ri$îÞ½[ï­¼]ºuà 33Š8·ŸËÅ„ '§z}û¢ù§OˆÑGc[Ÿ¿äĉ ÈÈÈ`üøñˆˆˆÀ‹/ä^¾|‰{÷îÁÛÛ[¤ï£(¬¬¬pïÞ=äääàñãÇÈÊÊÂÍ›7ñáÃøøøˆ¬³`Á„„„ 66)))صkîܹƒ+VÔØǃ‡‡^¿~€€dff"%%gΜÁû÷Ž?Žˆˆøùù!;;áááHHHÀ›7oÄaUÇÝ»w‘Ð AI‰‹‹ƒžžôõõ%’wqqŽŽ.\¸€nݺáÀHMMý&c377GFF†À‘’’kkk(((0Ö˲²2Œ5 xòä ¢¢¢ðîÝ;¬Y³ÁÁÁX¹reƒŽëòå˸té/^ŒÜÜ\<|ø¯_¿FZZš€n@@¶lÙ‚Ñ£G#++ OŸ>e¢Æ‡ääd¡¶ç΋³gÏ"33iii˜8q"^½z=z4’““‘œœŒGø`8fÌDGGãøñãÈÌÌDrr2‚‚‚››++Ÿˆàåå…’’DDD ::ééé˜0aÖ¬YSïï…oÉíӧй£G¢ªª žžžOOOTUUáèÑ£õî—³³3€Ï‡|Þ¿B1zzzÐÐÐ`Îÿ°; ÃîD(+ï»wï~ï¡üø|o þ¿€§§'¹ººŠ•‘ämï^h(­þÊ } í5ø-×öø§ëó7·úFªGGG‡š6mJW®\¡+W®ÐÎ;ÉÕÕ•©©)Qjj*IIIÑÿýßÿ ÔŸ6mq8JLLÙ~m¬^^^€²³³™2¾oРABò¤¨¨H%%%L¾²@Ÿ:uŠÐÌ™3…êO˜0Ptt4­_¿žPLLŒØ±JBvv6šššeOêbÎÈÈ Ô±cÇZõEcÇŽ%YYY@ÈÉɉNž<)àÿLÔð>УF"‡C§NbÊ8@è÷ß­ªª"MMM’——§¢¢"‘íÕÅ}ôèQ@7oÞ¬QÎÆÆ†PJJŠ@ùáÇ M:•)ãÏ]¡vjòæ¯LžuêÔjëDFF ).|욬»|?à˜˜‰rO÷èÑ=zô@zz:‚‚‚pøðaLš4 \.&L[ÿÓ§Oèß¿?~üˆ´´4‘ÊumsÄóçì“'OÄî¨ÉŸûüÍD¾DÔ8%EYYÆ CJJ fÏžU«V1ñ%%% ¡Ì3|±uëÖzo Á±ÿró蘘¹‘’’‚¢¢"vã–üTªªª %%õ½‡óCÃ6aaa˜>}:BBB¤½)ë×cèÿcïÌâ*Ûþ@•EdWPQQwp)T\Ss!­L+3{ˬ޲^3­\*Í\r·LQs+ÜýÄTÄdÖóý1ÎÈ03²èó»®sçÜçyî3sæážûÜK­Zt/…'²´L=›/xTÞçÒ “ɘ8q"ÇgÆ =z”ñãÇ—ê Jnn.—/_¦_¿~jƳ$I%–7 ÕØ÷ï¿ÿbddT¢‘áéé  ÑdCõêÕãwÞáðáÃÔ­[W¯Ä¨ììl DXX+V¬`äÈ‘¥š³¼øä“O4¼º…ÉÏÏ'>>^çXÉÉÉÔ©S§|”CQºmðàÁ4iÒ„Í›7k½ÜÝÝÅ{\”ÿýCCC•LycggÇ„  ÃÃÃCõÞáææ¦êêX˜ÐÐP ôþÂbllŒ¥¥%©©©ÇJsÏ*Å¢ŸI’Ø¿¿^º”ć~ˆ§§'óæÍS%çmÞ¼™´´4æÎËÝ»w5¶ ––ÆæÍ›Ë4÷áÇY¾|9M›6U ïéß¿? øR\åßÊãU‘D¨‰H"TgÏž=L:•cÇŽU¶*Ua@WÎÎÎ,Y²¤ÜjØvíÞ‡~ýxóxŸ•X[[ÓväÈ õ>—–×_Þxã d2o¼ñF©Î¯Q£öööìß¿_ÍxX¾|¹ê±½6öîÝ«–‰¿uëV®]»Æ˜1cJ¬ÏÐK)Ÿ““}ú0dÈvîÜI@@€ê1?(âxOž<‰ýõ;wîdÊ”)\½zUmœ«W¯òÕW_hxŸ–¤¤$úõ뇉‰ ;wîÔ¨;­dôèÑØÚÚ²~ýzµp”Õ«WsçÎÞxã µ¸\m¯QNNŽjŸ.:¤v«|çεy¦M›†$I|ûí·ª÷3--Å‹chhÈÔ©Sõz èÞ½;'OžT…c(úH¤°°0•¬²’ÁçŸ.YZZJ¯¼òŠôÒK/I†††RóæÍ¥øøxµùÐRúܹs’‡‡‡$“É$///iàÀ’ªö°²ÂÀ¸qã$™L&yzzJƒ–¼½½%ccc©fÍšÒ‰'J¼Î³gϪªW·ÎÐWfí} ïÞ½[â?üðƒÎ×\’$I.—K}ô‘d`` ’“““Ô¶m[©fÍš 5hÐ@:pà€$I’´eËÕø¶¶¶RçÎUµ²iðàÁR~~¾jì²TáX¹r¥HöööRëÖ­5¶ ¨Îýûï¿%KKK©víÚR¯^½¤6mÚH€äãã£V©¥ð<Åmºª LŸ>]¤¦M›J ºwï.Õ¬YS222’víÚ¥’ËËË“FŒ!R“&M¤W^yE²µµ•ŒŒŒ4Þ]]4W¯^-™˜˜H2™LjРԡCÕ±K—.I­Zµ’©uëÖÒÀ¥Î;«j¦'$$¨d÷íÛ'™››K’¿¿¿Ô¹sgÉÎÎNšÿüs:uê¤zʤmpssS{ÏLLLhР 6ÄÍÍ­Ä®¦}ûöåĉlÛ¶sçÎÑ£GüýýK°)ª2I*¦g³ ÜP¶õ-©„>2MòóóiÕªr¹œË—/W™rQ@PYܸqWWW®_¿Ž‹‹Ke«#¨†›D7"º‚ÈÌÌD.—“——WÙª<¤¤¤ÉG}ÄÅ‹™?¾0ž D¡&"‰P¼¼<är9™™™•­J•GÐÄ‘#Gʵ Ç‹ÎÆñòòbñâÅŒ7ŽÀÀÀÊVI ª4"‰P‘D¨Ž² Ç‘#G*[•*pÙU½{÷fÉ’%•­ÆsÃàÁƒiÓ¦ Í›7/¶r‚@ ž Çk":ª@@@€^Õ‚^t„-¨–ØÙÙ=“VÎ@ º!@ A)t‘]Ù*àF$j"’µ#lݺ‚HLL¬l@ð#’5I„Ú6‹n„]Aˆ¶˜@ ¨LD¡&"‰P;ÂfÑ0 +ˆëׯ³qãFΟ?_Ùª@ hpþüy6nÜÈõë×+[•*0 +ccc¬¬¬055­lU@ 4055ÅÊÊ ccãÊV¥Ê# è ÂÆÆ†¾}ûÒ¸qãÊVE / "‰P‘D¨NãÆéÛ·/666•­J•GЄÈ×ììlär¹j“$é©Ç’$Im¬’6}3Ž÷îÝËÛo¿­÷ûùöÛoóûï¿?õ5”I’¸}û6'NœàÆäçç?õXåÒŽ^’$nܸÁéÓ§yøða‰²r¹œèèhŽ?Nll,¹¹¹2ùùùÈår½®-44´Tï_y››[æ{ùyàðáüýöÛ¥z4œ››Kll,ÇŽãòåËddd”(ÿþ}N:EDD ZerrrJý¹^I„šˆ$Bí›E7€® žE@~TTT¹Y”‹/–Éè*- 6ÄÌÌLmkÚ´)3fÌ %%¥Tc8qBc¬â¶¦M›ê5æÙ³gYºt)ÉÉÉzÉ/]º”°°°Ré]dff2iÒ$êׯOýúõñööÆÕÕGGÇRý³¸xñ"ï¾û.>>>Ô®]333þóŸÿ<•Nqqq`nnŽ««+:t nݺ8;;óý÷ß«GIIILœ8 š7oŽ7ÆÊÊŠ>}ú¨ñË–-ÃÌÌŒ5kÖèÔáܹs¥zÿž–M›6ñÚk¯Ñ¬Y3LLL033ãĉÏtβ——Ç'Ÿ|BHHÈ3›ãÂ… ,]ºT¯Ìr¹œÏ>û kkk7nLçÎiÖ¬VVVtéÒ…øøx5ùýû÷ãåå…;v¤M›68::Ò°aC~úé'5ÙŽ;ª>÷¦¦¦˜››ãééIÿþýY´hÑsmT‹$BMD¡vD¡nD'ÂjJff&Ý_y…£û÷ëmü= £Þ}—·ÇŽåí  g6GQêÔ©Ã_|Àµkר²e sæÌ!,,Œ#GŽ`hh¨×8ÎÎΫíûûï¿9rä“'O¦Aƒªý–––åwU€G±téRüýýùôÓOiܸ1aaa¬Y³†÷Þ{&Ož¬sœ“'OòóÏ?ãî·÷S aìØ±äääðÞ{ïѱcGˆŒŒ$,,Œ?ü¦NJ~~>Æ ãÀÒ»woZ·nMBBÇŽcíÚµäååadTu—¯•+WrèÐ!Z·nMÓ¦M«ü£ó¼¼<¾ýö[&NœH```e«Ã”)SX¶l/¿ü2têÔ‰äädΞ=Ëš5kxðàêó{òäIüýý±°°`êÔ©ôèÑ bcc !""Bc|###¾ûî;ÒÒÒ¸vígÏžeÊ”)Ì;—Ÿþ™þýûWè5 ‚j†$xæŒ7N7n\™e 3#8X2üï¥>£G—M¹;tHªóöÛRÓ>}¤¼¼¼g6Oaìíí¥† ªí+((š7o.ÒÖ­[Ë4þûï¿/Ò±cÇžêüàà` .]º¤—}ؾ}»êÚ¬vþÙ³g2dMš4ÁÑё޽{³oß>­º¯[·Ž.]º`gg‡¯¯/›6m*ÕuK’„¯¯/fffÇe2™Ú=píÚ5€b=Æ5jÔÐ{îáÇóÍ7ßššúÔ¡JU™ªþ$¤2I„ÚyžC™Ê a@Wå?káBâýüãÄ„¯¾":3³\·Õûöq¥^=05%륗X°iS…ÆBE™lfjjŠƒƒ¹¹¹k$•åçç3{ölär9ŽŽŽz}ìØ1vìØA=øòË/:t(ñññ <˜eË–i=gêÔ©lÙ²…aÆñÖ[oqæÌ:wîÌñãÇuηjÕ*:uêDhh(Ý»wgܸqœ={–ž={²~ýz•Ü… èÒ¥ ëÖ­£S§N3fÌÂíÛ·ñññaÑ¢Exxx0{ölÆOAA§NRÉ¥¤¤Ð¦M-ZDûöíùì³Ï077gÊ”) >\mÌ›7orðàAÞ{ï=~ùåÚµkÇäÉ“qppॗ^ÀÉÉIe@wîÜYuîúõëéÔ©ûöí£[·nŒ?ž¨¨(úôéÃÊ•+ÕæY°`£G&77—?þoooÞ}÷]6oެ׵»¸¸ “ÉØ±c‡Öð‹¢(ï—ü¹\®×%1iÒ$¬¬¬´~¡­îˆ$BMD¡vD¡T®üÅ ­¡_AAÖëÓÅÌ™3%@š?~©Ï}šŽÜÜ\©F’»»{©æêÓ§H2™LêÚµ«ôÕW_IÑÑÑZeË;„ãüùó’………äåå%¥¥¥I’¤x%ooo);;[%{îÜ9 |}}‹ïiB8–/_.ÒöíÛ5Ž~ï?üðC V¯^­&3~üx vìØ¡Ú§¼w›4i"¥§§«É—¡ ÁiÚ´©”••¥ÚŸ-µm¥X¬E IDATÛV²±±Q…ù<|øPu~"""$@ï×á½÷ÞSÉ·jÕJš>}º®UöܹsRÍš5%@²´´”ÆŒ#-_¾\ëçZ’JáPÒ®]; RSSuêZ!‚²"B8t#<ÐÕŒÂÞg#FÀÆå7IT4i…š¾È»u«0/tRRAAAѵkW6lÈÝ»w1b=zô`ذaØØØhçMŸ®~ n],-,øÂاrhÔòù¼yľû®úN™Œ¸Ç±ÐϺ"GNNŽêÑ­……cÆŒaàÀ 4H%cbbÂ믿μyó¸vínnnܸqƒ½{÷2yòd5ãV¹¹¹Ì™3‡ß~ûøøxØÑ[·náâ⢶¯gÏžãôîݛӧO‹­­­Ö¹®^½ ÀüAHH’$!Iª0•èèhºtéÂÀqqqaÒ¤I|óÍ7 4ˆqãÆÑ®];½¯MɱcÇ ¤Y³flß¾ƒŠùîloo¡¡!÷îÝ+Õy2™ ???üüü¸wï‡â×_eÇŽìØ±CþRždff2`Àø€‹/²}ûv–,YÂ×_MHH§OŸ.U,´òËPáJ=Á‹@xx8áááDEEáééYÙêTi„]A888X¦î>Z½ÏI:”=?ÿÌ?k×>õø&ÙÕUÍû¬$«kWÌË›cÆ”9Aª$œœœôŠ}œ8q"óæÍcÙ²eóÛo¿QPP öU>ùä¾ÿþ{^{í5ºu놇‡¶¶¶¬[·ŽY³fiM¶³³³ÓØgoo(bêŠCip4nÜXk ²···ÊSgffÆÕ«Wù믿X¾|9K—.eÑ¢Ex{{³~ýz\]]õº¾3gÎðÊ+¯àääľ}û*´Ã”±±1nnnÄÅÅ!IÒSyÎíìì:t(C‡%88˜O?ý”Ÿþ¹\ è‚‚FÍ™3gضm^^^jÇ•5ª•ïqa”ûÒÒÒÊM€ãdzk×.–,YÂÚµkY¶l-[¶dåÊ•´oß¾D”íxµéTZ£PyÏ6jÔHÍ­¤cÇŽ4kÖ xrï—ôù(-–––ª/S[·neðàÁ,\¸PëçÜÀÀ€–-[Ò²eK¦OŸŽ»»;çÎãðáÃZzmH’DLL XYY=•ÎU•èèhÕ{%P Ì ]‚4kÖ >\ÙªTy„]Adee•iáÊÌÌdCXy3fh¨[—s\¾|¹Lu¡?ùáNœ¨ý`z¡õ¡Q£Fª$¦/¾ø‚åË—ãããSêoÍ«W¯¦I“&üþûïj_ JªªqàÀ5o (B$àIè…6”ïÍĉõÒÓÈȈW_}•W_}•äädV®\ɇ~È/¿üÂܹsužAïÞ½©[·.¡¡¡888è<§¼éÖ­+V¬`Æ Œ5ªLc >œO?ý”Û·o—“v >þøc¶nÝÊ¢E‹ð÷÷×8®|ªq ¡¡¡åþX&“áïï¿¿?éééüù矼ùæ›Ì;—?þøj×®MXX˜Æ—“‹/’@ß¾}õž«8”÷ì„ èСC‰ã(ïýЪU+µcÊÏGYÀÌÌL¯Ð CCC ÄüùóKu¿¬\¹’‡2dȲ¨Z%™3gŽh¦Re¡h¦¢ÀÆÆ²²²*[•*ˆ® ÊÚÕgÖÂ…Ä»ºÂÅ‹Ån‰Íš1éq’§áÀáÃ\ÌËƒØØbçȲ¶&ø—_*µ"Ga&MšDbb"o¼ñ L,Îø/†ÜÜ\’““quuU3žÓÓÓù÷ß‹=¯¨1››Ëprr¢aÆŞ§Œá~š¬ï:uêðÁàààP¢nJÎ;G¯^½¨]»6¡¡¡j! EQzažEæõ¬Y³077ç³Ï>ÓOŠXß½{÷ŠøâJ(mÙ²@k‰¾§eÉ’%ÌŸ?Ÿ÷ߟw‹†.=ÆÇÇ@ãu—Ëå>|GGÇ¿8••Úµkóúë¯Ó®];µ{ÏÛÛ›{÷îqîÜ95y¥žú†‹™˜˜Ð¤I­UOºwïŽL&ÓëžUVî(úùHOO׫B (<ù©©©ZíÙ³‡¬¬,š7o®ÚWÜ=•——§*E©ïý²ÿ~>þøcÌÍÍùþûïõ:§:!ŒgMD'BíˆN„ºèj‚gãÆÌ67‡"ñ¹jÔ­KM__ ž.ÆU’øÜϯä9ñcyøða±q¾I@@õë×gýúõXYY•*y5bÛ·oÏ¿ÿþËo¿ýÆÀIHHà£>ÂÜܹ\®Õ;wþüy¾þúkÞ~ûm222øä“OHKKcÖ¬Y%†·téÒ…±cÇò믿"“ɘ4i 6äÞ½{DDD°råJ6mÚ„……_~ù%¦¦¦ôïßnß¾ÍúõëIHHÐÙ%-!!___“òóóÕæCK'B¹\.}ùå—’‰‰‰ÚùR—.]TÝ×>ûì3ÉÈÈHMÆÑÑQ7nœªlXqœ={Ví]unRR’¨vÜÔÔTúé§ŸŠ§¸íîÝ»%ê¹`Áû£nݺҰaäÄÄD5Ù]»vIvvvj²íÚµÓèD©«‹æ¹s礱cÇJõêÕ“ÉÁÁAu,;;[š5k–dff¦6L&“¼½½Õʽ%''«Ê*7iÁ‚z•±»}û¶Ô«W/ÉØØXãusttÔ¸ç¾üòK•Î…7CCCiøðáÒ­[·Ôä[·n­ñ¹«_¿¾ÔµkWé믿ÖùÙªˆ2v‚²"lÝÈ$I‡»QPf‚‚‚ÈÎÎfÆ %Ê€xĦ,Wïö'Ÿ|·ß~ËùóçiÑ¢E‰²iiidddP·n]5aff&§OŸ&++‹6mÚ`ggGVV©©©X[[«Jredd––†­­-2™ŒK—.qëÖ-Õ9EIHHÀÌÌ KKKcYYYDGGsóæMlllhÚ´©F‚_FFçÏŸ'11‘Æãáá¡×k’——§³|—­­­Ê[žŸŸÏýû÷111¡N:*™‚‚‚+hXXX”ªâ À£G¸xñ"III¸¹¹Ñ¨Q#µ’~J>|È•+WHLL¤víÚ8;;Ó¸qc'Ê÷ÉÒÒ²D¯.(ÞçG©®]ynqÔ®][UÊNÉ;wˆŠŠÂÊÊŠ–-[j- §œ§8ìììt>%ÊÎÎæüùóܹs‡zõêáååUì9¹¹¹\ºt‰øøxš5k†›››ÆëTøÞÕ•\Üç0++‹Ë—/Gݺuqww×z߃¢zÇÕ«Wñôô¤Aƒª×ºèg¯8ÒÓÓ‰ŽŽ&!!###œiÒ¤I±ç^¹r…[·nñèÑ#iÔ¨‘Ö„Ù¨0Éd2¬­­KU¡£:pãÆ \]]¹~ýºZ!‘D¨‰H"ÔÎÈ‘#111yám’’tÄÍ›7UÉFÅÉ€0 K‹Ò°ìÓ§ýõWe«#•NqtPPøS„””fΜ)⠋гgOœÅýR"º‚ñtåËéÓ§Ù¼y3Û¶m#//yóæU¶J@P¥Æ&"‰P;ÂfѨÂ!¨–ÄÆÆ²mÛ6ZµjÅÖ­[õ®‰,@PV„ºdddpæÌêÔ©CË–-+[emd@ ‚ŠFÐzòàÁ¼¼¼èÚµ+éééÄÄĦwcŠâêÚ @Pˆ$BMD¡v²³³µ&w ž B8ôÄÒÒ’+W®°aÃvìØA¿~ýøé§Ÿô>ÿY4¨@_æÌ™SÙ*T9”ê›E7­'FFF=y¹ KåUù@ ¨LD¡&"‰P;ÂfÑ0 Ÿ‚Ó§O³qãFNŸ>]Ùª@ *˜çÞ€–Ëåœ>}šÓ§OsåÊ7nÌ´iÓ´Ê8p€µkתc|øá‡…ø/^¼Èˆ#غuk± @ Ê›{÷îqæÌ½dÛ¶m+ì”gÈso@‡„„0räHìíí‘Ëåxyyi5 ·mÛÆ°aÃ6l½zõbÕªU„„„päȬ­­EF`` kÖ¬¡]»v¥ÒCŸpÐÐPK5nuF’$niñº‡x]úò¢ß+™™™Z÷‹$BMª[áÑ£G:4ƒö%ÊœfófùSÛ"‰P7ϽíëëK||<õë×§oß¾Èår­r3fÌ ÿþ¬[·€1cÆàêêʲe˘>}:< GôîÝ›ÈÈH"##qvv¦_¿~zé¡+ ¿OŸ>XYY•îâª9ÄÛÛ[|H‹ðÏ?ÿЧOŸÊV£J‘ÍñãÇyùå—+[•*Ell,5ªdMªbm,,,ÔöÍ™3GÄAA™DXâ óó»’Ÿ¯Ë06-Ó‰‰‰"ZϽmkk«SæÌ™3\ºt‰I“&©öÙÛÛÓ²eKÖ­[ÇôéÓ‘Éd¼ÿþû€¢õ' òL냮qÔ¨QŒ5Jïñ@ ( ÂxÖD$jGϺeì€ëׯТE µýžžžªcÖÖÖ|òÉ'jÛðáÃõžcË–- 4ˆ   µ-::ZMNìûÄ>±OìûÄ>±OÛ¾Û·o£N­eŸºœ®9‚Û$7fРA„‡‡#(™$IRe+QQ(C88 ¶Ñ¢EL™2…»wïª5Fùᇘ6m=ÂÜÜü©ç Ä·@ OOHHƒè áaëVž:ZØ-ºhÀØØÐL¼ÈÈÈ Fež#--”””bc°_DbbbÈËË«l5ªE½E¢OLLLe«QåHJJ"))©²Õ¨rˆµE;bmѤº¬-ÁŸÂÂ…Ïv¹\NJJ iiiÏv¢ça@NNN\¸pAmTTÖÖÖ˜š–-àäɓ̜9SÃûý"³xñbÒÓÓ+[*‡è¦‰è¦ððpñ¨U bmÑŽX[4©ÊkËÕ«ðÃàë 660|8„…=Û98ÀÌ™39yòä³è9@„p ˆnÔ¨ÁÁÁLŸ>]µßÃÃ;;»2½âQˆ@ Ï'ú>Y.ÚѸ(¹¹pø0üý·b»zUS¦fÍËEGeóÜWáÐWWWzôèÁæÍ›yÿý÷155åĉ\¹r…3fT¶z@ ª )))¸»¿„©içå rèÞÝ‚µkÕ+~Ü¿»vÁΰw¯"T£(îîп?Àƒ0thy^àiyî èøøx:tè@rr2’$©ÃÃÃUÅÓ/^LïÞ½iѢ͛7çÀ 2„#F”‹áááøB5KàyÆØ¸'ññºJᥠ—Ï 2ò‰—ùäI((P—¬QºuSÌФɓc!!gÑ]çù,Ц4—ñxüBBB§k×®¥>ÿEâ¹7 ­­­‹­ñhoo¯ú½yóæœ9s†]»vÇ;#ŸŸå&Þ¡Cñ(¤111¸¸¸”øHëEDt Ó¤ºu «(” „666•¬IÕB¬-Úk‹&¹¶œ= À­[šÇll _?…ÁìçEzà¨èС«WËE?ŠœœlnÞ¼ (j7+›5W9KƒÒÉ7räÈRŸû¢ñܯ.µjÕÒÛ‹lgg§Šû)otu"|Y¼x13gÎ|á:0êBt Ó¤:v «” „â©–:bmÑŽX[4©ÈµåÚ5õ¿==sÿþЩè㯫W¯cÆŒ 55•ñïçôÍÓ¤ÖIÀò°%v`ù˱´´,“®¢¡n^¨$ÂÊ"((ˆ›7o2eÊš5k&<@ <¤¤¤Ð²åLnßÖÂah8“>}~T…f<­}š––F§W:q¹Íe lÔã? ’ p?ãÎñ]ÇŸÊˆŽŽŽ&::š… âìì,¾p•€(c'@ð”äçë'׿¿"apòä§7ž^Ÿò:—½4g€›®´½Â„©ž~^º‚pvv&00PxŸ@ ¨"Pc•JÞаìúfffr*Ƴ’›NÅÒh§Íš5#00P„oèÁs]UÈÎήlª"ÑG;"ÑG‘D¨‘D¨±¶hG¬-OØø×Fþ·è¤¥‘•Ÿ……dAË-Y¹`%ÖÖÖÅž—™ k×ÂâÅUè€ñ°Cã Îã}pä$œ\6–ZG ‰„ôâR⸑rƒ¸Ô8N?Áó;:ÏM­“Jdd$>>>¥ž6‹‰‰‰nÁ±ºT±±±DDDààà *£÷¢#}´#}4I„ÚI„Úk‹vªÓÚ’——§w—Íúõëëõå:??Ÿ¨¨(¾þþkB…’Ú=²Ô7‰kÉ×híך_¾ü…6mÚP¯^=Õ¹11ðóϰr%¤¤<ÓÄ ÒÈrüÆÜY¡ e@×Tp9 ›ÞÕô) ¸“vGeßH¹¡f,ǥđ_Äùï|.3 $$$‹‡‡Ç³›è9@$VAAA\¼x‘Q£Fáíí··we«$‚€üü|æ-žÇî°Ý¤f¥bifÉ+=^á?ïþÃòˆ)xF¤¤¤Ð¤Ép22FéÌ$ à ü¡ûËuJJ .»ñ¨Ùu¤ÞÚ…²Àpy}†ô̆ ؽ[ámÞ» [K..0i š‚GçVd‹‡š%Lþ·9»ñÒˆ¶*c9>5žÜ‚\zÆÜÀù9¹þ%Ÿç|ЙK_¢fÍ’”Òäøñã?~œõë×ãááQm¾pUÂ]Axxx0uêÔÊVC /ׯ_gÐøA\nxyk¹"ë©Nœ=Á†^غb+®®®zwçÎrrrtÊãääT͘˜x”¤C*…‚‚™z™e|É·ãÀ òÛ%qðÚi{oâÞC9ew²Y¸¹ËiÕ. ÇY\ÎÏâÓÿË ß&¡dãÀ3ؤHb#"K³6³ÆÅÊ…†– ?­ªý^Ç´C¯ eëý­ÅÆA$ÐÞ¹}©g@å䋈ˆ(õ¹/€à9£  €¡o %²S$˜:`rW9‘‘ }k(§öžÒ»aXïÞãyð@÷Ôºusáž§ÔüÙ!I¦rÝå\å$JG¡óQõóX 6P¶vÈGwS@PÄEÇ€}-{Z5Ô0’•×6®­s¨• WrÉÿÑ­¢5Œhƒû4hÊŠ+ôPJP„]Aˆ$BMD¢vD¢&"‰P;"‰P;bmKpÁ邺ñœ(o3¸àtKðÁäô³F gê”sp*¥¶eãÑ#¸pd²'¨ÿO.{/ÿK~­"•) P4õ+œ7h ãh—!ì†fFf˜™bVÃŒx)žlJþ/K’ñSÐOL?é)¯ò æææßyœ×§¼Î©‹§Hµ~ÜHå¡¢‘ÊÊÝ+177/Ó"‰P7/îêRÁ\¸pÑH¥"ÑG;Õ)ѧ¢I„ÚI„Úk ì ÝEvó"F]8PèVÉvÎfWè.½ èò@’ 5’’àÁÅÏ¿+&$À½{EϾÜšRØ|ù÷_hÙRËd†¹àºZü ÍBÀ졦÷98 ô-´ï–2|Ø>y‰ÊH632ì†&†šFåðÈáüùðOu#¼Ž7ñïå_¼@)177góÊÍdff© iݺõS…mFÙHåÂ… ´mÛ¶î~bŠºñ\mHËNžxØêW…béwK9íšë=¯C Íã&—Mç;î™ÔV®Y³æS—ª+ a³èFЄ²‘Š@ ÏkskÈE«A§"¢Dñòª—å9Š¡C©kV·Ìsß¹sæ”þ<##¨[llÀÒΞ…,ÃÑàó7tI-$yRoÀFHXO'Ÿz½¹‡£©r*-„ô|u£¹†ÌÏš~xÕðgKÄYRä‘Pë$Ô/ÒB0±…¤Ù©Ç?—D:uØ´h㦎#Æ%†Ü¹`<‡Ëø7ógÖ§³Jÿ‚Tʧä!!!•­J•GÐ@ ú0Ês› ¤VZ%œüd'Àä4d·©…c ÃÊŸº~/q“’šÆÕôxãù1–@Ð ØÀÙž™œˆ[¤vØÔÈ¿F~ k1Œþîý±0± %%…½ß]$%e?lœõ÷ƒG’«}£œ·…‡K¡À Ð߀èЮgþ=ÃòÕËÙt?)i)x6õdlðXÚµmWª±Õa@W"‰P‘裑D¨‰H"ÔŽH"Ô΋º¶ää2ûðlfžMNý88Ê|²ÂI„iàõÈ‹>ÿ鯍 Ä?RÔ$Þyu';¯î¤VZ h:€Qž£ðkìG ¥+[ÓÙภ<Á^‰¦ewßù§´lY¤KßS’nxz<(^ÀhùœxÀUa4¿Òø†µF€{æÆÅ%Ò™Aújˆ¾ —aä Z£pÙ§s^ɘššòÎ[ïðÎ[ï<ÕùU ‘D¨›ku©DË?άº#}´#’5I„ÚI„Úy×–“·O2aûÎß;€Ì€7?y“ˆß#8oqžŒÆŠ$Â~P+¦­ÒZ±õ÷­ØÛÛ3§×Çf}Ôzþ¼ø'³’‘›Á†óØp~ÖfÖ õJjD¨5zÿ^…j)דCÛ8ˆ†ã(M–¼‚<Ò¸›~—;iw¸›v—»éw¹žt\ë$Ý%çêƒùÅ:üúŸŸ pÐY.7÷>P¨Æ±d y±(Ì¡ w¦éÏ)‰‰‰Ï$fûyBt"¬‚‚‚¸yó&S¦LU8@Pndæfòß°ÿòãñ)5[ض`ùÀåtª× €ÕV³uïVîÜ¿ƒ“­ƒüí¢T IDAT1väX­ãåä²7f/ë£Ö³íò62s •}» \2„^ùZÏ ÄŽÆ™í¸xi é *£øNÚ #ùNÚîgÜGB‹’ü Ôñ<€!©CÙüûŸ:^Õ¹s!Wæݺµ¡wï—u VÊóI”² ÇÂ… qvvΜh@ ª!¡×CysÇ›\K¾€±¡13ºÎàÓnŸblh¬’;rl±sQjÔ À=€÷2r3‰á×cë9tûˆÊƒn%Ï}î{`/&ÿ3Ñnë‰umk2ÉD޼D9ãDcÆŽ£×˜&&&ü÷¿Ï_Gàéß~ ÀòᆱdM^,„]Aˆ*@ (R³SùÏ?ÿá·3¿©öuª×‰ßüFK;m‘ŸŽZ5já–þçg¼ò$°m µ–|RM²µ·˜–!æ¦ ŽæŽ8™;áXÛQëïµ014á[“oùúÄ×d6ÍÔ:ùàrÇßž¾e¼ÒêKRRûbbT¿—Õ -ªpè0 +‘D¨É‹šè£ ‘D¨‰H"ÔŽH"ÔNu_[222ˆ¥Q£FÔª¥^ cÛåmLÞ9™;iw¨Y£&³zÎâýNïc +9`¸´kËîÝ0t(dfØÐ°Nsâ¤# +á$IÑ®ú­—ÞÂÑÜÇÚ dsGj;JFÔÍô÷§³;p7Gï%שHÜE.؇۳hæ"ר4T÷µeú·ßrsÀÕïåå…I„º©ž«K5D$jò"&úèƒH"ÔD$jG$j§¬kKLL ·nÝÒK¶k×®åf¨Ÿ<}’·g¼ÍCÙC2,2¨õ¨Ö’5K‚—àÒÜ…)»§°éÂ&•¼¯«/¿öÿ·:nz_šµeíZxýuEÓCCXº"®¶añý#`W‰÷`X·a|Ýãk½æÑÅ?üûÓße÷Ý<´{H–IuÕÅ&ɆU?¬¢S‡Ne¿:¯-*ﳿ¢Ãá¾íÛËÅ "‰PDaˆs@P>|*ÿí”ܹV­õ\½úG¹8–­^Æ«¿ Á;A½ùI.X±$Ï. Eå +S+æõ™Ç„6Ê<¯6~ü¦MS´Ý65…  0îß¿O§Á¸î{]»Z×ý®œØr[[ÛrÕéÑ£Gœ:}ŠøÛñøtôÁÝÝ™¬$WøóÏ„>b…‡¸º*v\¿Îø‹ËÅ -ìÝ´@ …((€¬¬Q@Ɇ±µuD‰ÇõåÎ;Ìúm ¾ šk@j÷TêÃ ŽƒøÉÿ'k;–ËÜE™1ãIA ؾ^~\ŒÂÖÖ–o¦~ÃÇ?}Ì­N·Ô»hˡޱzÌùpN¹Ï ],^èXç¢$%%±»÷WWöíØQn^hAÉZ ‚Jdæ¼™Ül}³d¡nÐëa/¶¼ºå™èŸ'Âò势ííaÏðòR—9d$í[µçõi¯s'ýòšrL3MqªíÄÊe+iÒ¤É3ÑO ÎÄÙ³¹;P³ÆßÍødî\~›;·´z±ÐU¢\PN:tˆ   ‘ÙZˆ˜˜òòò*[*Gttte«PåÈËË#æq¦¹à IIIªDBÁªÛÚ²ïPè“ÅQb£®—ižâÖ¹\‘,¨4žÝÜàÈMãYI“&MßÎÕ}W9þËq®î»JøŽðjij=^ØL²\Ýè5õá¤}¼Þ÷s!¼1\œ©Pÿ!ܲ†‹Vðp:ä¼Å•+ABÆŠü±† Ÿü^·®>¯V4 ¾¶ØÙÁŠúœû|RÕ×–¸ÇÆsücâ %'Óp-æœë@«Þ½©W¯ÞSÏ+:êFX.„èD¨‰èD¨щP“êÜ-ìY":Çb Ç&$&B÷îêR2øø(Œæ¡CñË H¢ƒp0ˆº?€G2¸¦AªD[A|[HŸFNΓn… ¬Zφó¸õH½‹amãÚ6 ä5Ï×èåÖ ##òó!>^aÈ)Â)Èy ^‡„ `ùž@ ”U²²`Û6íW\«–ºa}ïž6©9À*µ=ŽÏ¦œtµ¡*¯-ñÙÙôˆˆ N.àÓ† ùÊÅ…ÛßÏì£Gù©˜n™óê×çÓï¿/ÓÜ¢¡n„]AˆQ“ª¸`U„ñ¬‰•••¸_´ çâмW”Uíd2èÜù‰Ñ¬ÍI÷ᇣ°qXǺÿ[Izï4P…6äAë$ îì£á©kŒŸð& #²éâ&.Þ¿¨6F ƒômÜ—Qž£Ðt5k¨‡˜*Œ]¥÷ø‰-Tð‚|Ír ãøÆ HKS?–‘/*¶âYUÒÁ’ªº¶Ü~l<_l<ììÌ7;Ö«Wãöí¹vëE›¸_Œ;w.“÷„Í¢€Ás±1|ý5Œ­Ýh.Œ§§¡çv“Þ'Mk±×§âš^ã¿»¦C¡r»2dtuîÊk­^c˜Ç0¬Í¬Ëõ\] ÉÉ CúÆ ˆ‹Sÿy㤤”ëÔ‚ änN=##‰ÍÊ`Zƒ|ëVÈT¾Ÿÿ\¹Â7ÀÏEÎW¿>Ÿ•Ñû,Ða@  Ú¢o¹g[[E§=}"Æö‡î'Î)®ÄN Í ` ÐZÙ·b”ç(F¶‰³eÅxîêÔQlmÚh?îé çÏWˆ*‚r$1'‡ž\ÉT$œN©_Ÿù…‹rß½ ¾¾Ô»t g¥i} 0)ï³@?„]Aˆ$BMD¡vD¡&U=ѧ²xQ“óóõŠþþýW›„faiø'ürìuwE±¬eÉ7àU¯˜–}z’›{[[Ý ”rsµ6kE{E Í$ª´¶Ü{ìyŽ~l/'ï³H"ÔèDXA8p@´ò.‚èD¨щP“êÖ-¬¢xÑ:&&Â7ß(B Òf@k×n%$ä N9øí·`ÌÌÌ8|Xámþë/E%––`k;•˜˜™ ‹ë àšnIR®ÖÄ9Ÿzõ>æüyõª?çïgwÌnv]ÝÅ‘›GÈ-x<ø à>С%ó¡ýÉöœÚsª´/C…àåDdä*r­[¡[Nðìx˜›KÏÈH";”&8:²¬iÓ'åÀ££žç;wO›óçpûömþשÿ=q¢ÜÂ7„Ý¢ñì\ BHÈAþúë ä^ÑNN3xø0›íÛÍøåˆŠR?îå“'Ãk¯Áرsm!4ú†%ƒ±R* :§AìØvììF¤ç¦s ú»cv³ûênâÅkÌmnlޝŸ/gž%.+Ì´ëX÷l]¾œúe©_ƒŠ¢ysKÌÌ‚tʹ¸ˆ¾Ý•Ir^½ ÏAüêîþÄxŽŠ‚^½žÔ%üì3˜5Ku~½zõ0ëÒEõ» â´@ *KÔ›žh’šjB³fê ‚&&ŠÒs“'+j7+™6mÇb‡r§r¡rs…h”‡‘_4†÷q[âöÄË\ˆæ6Íéפýšô£[ÃnÔ0¨Áå6—é7¾×Ú]ƒÂMJ$¨s¶ƒ" o@©®¼"Ù°aAe« ÐAj^}"#9ûøFmoÏò¦M1=6Ÿÿïÿ Oxø¸Ï7ßÀ§ŸjŒóQu£Rt!’5I„ÚI„šT¥DŸªÄóšDø8 PÔIž8&LPTÒ(JrÊ4IR7ž¢°ÑgùäµÈ%ñ¯ðTü]³FMz¸ôPÍ.V.ã6mÚ”S;N1î½q\:‰ŒZÔ× vNm¾˜ò#†Œ(Ÿ‹­@ÄÚ¢Ie­-òòèsî§ôigǪfÍžÏGB¿~šªøûûïჴŽõ,<Ï"‰P7"‰°‚5I„ÚI„šT•DŸªÆóœDè뫨´ Ÿ|¢ÝxEÅŒl‡"Š“h$Õ0âvï°ûµÝ<øøú›É&k5ž•X[[³cÝ®î¿Jøáœÿë<^¬–Æ3ˆµE•±¶¤åçÓ÷Ü9N>zÀ0[[Ö4oŽ¡Òx>püüƳL¦H(Æx~V›E7ÂõWAˆ®>šTÅîOU‘´¡IUíVÙT·N„›ªéÄÞ6oÖ¯f³©‰)mÒWSÎÙ™…ýb`Pz¿‘L&£QáZ¼Õ±¶hRÑkKF~>ýÎãØcãy°­-ë=<žÏ{÷*JÌde,_®èõ^Á›E7€‚ræÆüñǽd‡ï‹‹‹Ë³U¨’ÈÉðp…M°gœ;§ßy¥‰êêÕ¥‹YL¦}fñBXÕ°z*ãY (/2óóñŠ"üqXÆ@6zx`¤4ž·mƒáÃ##X½FެD%! h@ (g"""˜>ýð’ÉC¸»Gèe@‡‡‡óÞ{ ©Q£f‰r¹¹™,Z4…®]»ê­oy«0–÷ì°0õxægAÏ=qv'";Š Ù¬s®Ó'M¶Š%UP@@T÷X¨[—?<<¨¡4ž7mRô™ÏËSô߸Qá‰TY„]Aˆ$BMD¡vD¢&Õ3‰ÐðÖ!“ ÷hIIIDDŒ ‡m$=þY8‰0D•\¨‹ýû÷óöÛ 15­S¢œ\žÌ’%SðõõÕ8–ž®0”•^æØXÍó ¡Cxô.^ÔK5½144dù‚åtÙ‰¼>y`Z¡Å% ülý>hxùN\ k‹&ÏjmIMMÅÒRQ"P^PÀ€¨(ÂϯX[³¹E Œ•OD~ÿ]‘%›Ÿ¦¦Š‚çýú•«>¥E$êF<Ï*‡fÖ¬YLš4‰Gã—ôEäk"’µ#}4I„Åþx{:ÒÒÒˆ‰yóçW•¸ÅļNZÚ“®~‘‘0w.ôì uë€ðÓOêÆs½z0~¼Â±vÿ>;Í›—áRK`ííµäùåA(Ø„Ú`ºÓû£ö4Ù߄پ³Ù°t󙸚!ÖMžÅÚr.*Š!ã–³ <ž}ÉÉô±¶fKË–˜(ç¥ÿÏÞ}‡Equþ-½¨€bÁн×Ä^+¶ì%FŒ-1j,X¢±ÇˆIŒ%$Ʊë§Xc‰»Q±‚Ø•.½Í÷ÇÐYv¸ïóì#{wvç ëå0{Ï=«aøp9y65…ƒµž<ƒÈY²B\úˆ³gÏb``À¶mÛ˜;w.ÅŠËòsÅ‚üôDQ˜z¢Ð'½‚\Døü¹Ü×È(õ-kÌh®ˆðÜ9y‰æÑ£àã“þqCChÛVÞ<ÀÎêÕSLٲߢR½ÿÊVttæÝ Ýð½ÁÊË+¡(´Ð–ãCŽs÷Î]Ê”)CÙ²e³ü:…˜[ÒË‹¹e’³37õõ¹vó&stt8š°sG \ëÕÃ(1y^¾„H A(.N.Àû"¼å[n+ZT^¶ag''Ε+gïùÆÆÆgÐû¬úoW_]`jë©Ô)Y'×^[>Ä$ggÞ E‹âgfOžÐ¶Q#Ö¯‰nB·ŸÙ³“Ûq[ZÂñãrz!_ taaax{{S½zõ\ÝòH¦'ŠÕ…>éåÏ"¬™>6”·}ŒLKðΤ[Ù ®ˆðC½£µ`|A¾Ñ "Gò2ˆï¿‡iÓ@_?N• |B}˜yB¾‚WÅ¢ ßµû.é11·¨'æ–ôrsnI¼úLÑ¢ò@¿~˜­]ËáÏ?Ç41yvrJþ8§L8qê(ï?QD˜¹_DxëÖ- @5ÐÑÑ¡}ûöj‹ŽŽfèС˜™™Q«V-J”(Á®]»r-± ?=QD¨ž(ôI/?ÂO?eýø-ä­^¿øÆŽ…É“åOv, õ~ýÖ®…‰Õ=[}áÎàïϞɻ^\¹"ï”qð ¼;Öúõ°b…¼kk \KpœãÉ7Çyò˜á:êÖUNò ðÍ?ß%sÿÞãwŒõ’¯l‹¹E=1·¤—›sË$ggÞ|úiò@Ñ¢”*Åã{÷@’à믓“ç àìYE&Ï r–¬(𿞿|ù’°°0Ìž={2®®°yë]¨½Òl}Wù\¿…Û“<ÚBãüóðvÝ“/p ¬7®U»¦z}hÛ¶-£FbÅŠ¸¹¹åÉ9/_¾Lhh(õÒì»Ô ANœ8‘tÆŒœ>}š·oßrîÜ9Ö­[—åsx{{ãî§gª[xxêö³bLŒ‰±ü5öìØØÈ t\œ'ÆÆá,]jùsÃ8v¬kÖX°o_-Žm”tK;yr666Y:¯‰‰ •+¯£jUjÖüŒÚµ“n)Ç*W^‡‰‰I–¾???( ñ¤æÄ$|­”çÎ;Šø»_pfO>XÜi1eŠ”ÑZ,bLŒ™˜˜ÐëÓOy[£†ÜA0¥çÏ!*Šð‘#ñ*]š±µkãù矄§ùhH ßGbNrøðaÜÝÝ Nh7.dL1 ôÛ·oùöÛo©T©dÓ¦Mcee…»»;óæÍ£]»v4nܘ]»vŸvÆÿp¯^½ NšµHõë×çõë×Iç211ÁÜÜ<éV4ÍG5ïsïÞ=¶oߎ««kª[ÚŽa…iìáÇìÙ³G±(iÌÓÓS1±(e,66–‡*"–”c7úѰ!œ?/•)ãÊ~LžlA‹-hÑ¢Ož<¡J•*I÷ÓŽYXXdé¼]ºtáñヌÙœcÇ~àÞ½œ=û3gÏþœjìñãƒtéÒ%KßGpp0*PHýs$66Vqïmyzz*&¥Œ%Î-9y½+!!„[YAÚ¤óÜ9y¬hQš7çáêÕ¸º¹)îï ñkWWWV®\ÉöíÛyüø1B&$8~ü¸dbb"uêÔI:|ø°¥ö8iÑ¢ERÙ²e¥úõëgû<]»v•lllÒÿòË/ ùûû§wqq‘) ÛçJiذa’­­mŽ^£ úæ›o¤ÀÀ@m‡¡8Æ ÓvŠ(}óÍ7Ú#Ih¨$}ñ…$É•AòmÜ8IŠˆÐl{÷î•öîÝûÁÏðàTºGi‰y¼÷VºgiéÁƒ¹yöÅKñRëõ­%æ!é-ГÜ}Ý3æææ9>G\\Û·o§^½zé–ŠV¢ÐG=Q蓞’Н_—wÍðò’ï[ZŸB¯^šÅÞ>g«V­JœoHÈëÕ‘àÝÓw˜—Íù<˜뮯ãü ùRÿ7Í¿¡a醫”÷ŠÒˆ¹%½œÎ-uuy1u*HÏŸã5|8º)>!wS©¸2e S²³5ݹs‡;wî—.K…”"–p4hÐ Óä9%}}}ú÷ïŸkçOl÷z÷îÝTã·oߦL™2¨ÞW”E˜››cdd”ã×Aó$IÞªeËää¹C¸yS;ÉsN…Ç„3xÏ`üªùÁ•÷xÂë‡Óð†œzzJcñ¥ô&ì ÓO ¢YEæÛÎ×J‚Rœ$1òþ}â$ ]• Ç;w8ŸfyéïÖÖ|5?ÿ¼_ŒŒ077Ç ízn!E$Ði›êþŸþ‰««kžœ¯E‹˜™™qûöí¤1I’¸uëvvv¹rŽråÊaggW ABA÷úµÜ}ÏÉ ¢£AOOîÄ{ü8$üþ¯<|LËõ-Ù~g;Ô†¢RQ,Ý,!å&E‘Pú\iª­5áÕ»WtÚÔ‰Y'g›ákç…)Ǧ€KwLõM5z~APçoon$ì7>9"‚ïW­â÷ŸÕÓããAƒ0ɬǽ‚T«V ;;;Ê•+§íPOq ô‹/(^¼8>>>€¼ÉyåÊ•;v,Ìž=;[¯ž´8þÍ›7øùù%ÝOÜhßÄĄѣG³jÕ*8€··7'N$00±cÇæÊ÷åëë‹»»;¾¾¾¹òzAb¡Zb¡,e¡¦ýó4hÇŽÉ÷«T‘‹gÌ€\lVúAüüüÒeæÈÃ#|¼æcn½¾@‡Êxü¿Çýé(ŸxÂÇW>æã+ó‰÷'þñ0^;½XÛk-&ú&ÄKñüàöm7´åIГ¼ø–Ò9ñä›omÀ¡–½jd~¹_Ì-ꉹ%½[GD0÷‰ü JTó0 ¥©JÅ™„‰á÷ŠùjÖ¬\7¯‰\%´½;-WWW©nݺI÷]\\¤2eÊHþþþÒÖ­[¥êÕ«gëõž={&™™™©½yyy%+?^*V¬˜HÖÖÖÒ?ÿü“+ßÓ°aäêÕ«KË–-“.^¼˜+¯YˆBõDáFzÚ("ŒŒ”¤‰%I¥J.2D’BB4Æ{e§ˆ0^Š—]$éÌ×I*œrtŠ›¥ç{¼õ®j˜ôÜb‹‹I[ooÍIø™ŠŒ”ª¯¬.1©ÈE¤Á/²ô<1·¨'æ–ô>tnéäî.qê”Ä©SÒñ>’'ˆbŤ°={¤UªH§õô¤ŸgÎ̃ˆóÖÅ‹¥eË–IÕ«Wï—L¨$I’´Ä§ô÷ß³fÍš¤=Ÿ›5kFË–-Y±bÑÑј™™áååE… òäüñññ„„„äJá`"GGG@pB~áé) º»Ë÷‹…ß~ƒ¡CµׇzýGWGöxÈÝXMôMXÿÉzÖ˜­×‰Š‹bÚñi¬¼¼2i̱‘cž-«˜wzóÏÈëG—u]ÆÄjû™ ‚FmôõexÂÕüaG²ÑÙªUƒýû¡vm–Î7a¼C IDATšÅÎuë8õäI¾Z¾‘’È[2§ˆ]8RjÖ¬ãÇ'..ŽçÏŸsõêU–-“;Méêê—´ô"/èèèäjò,‚öÅÄİcÇÿÈÊå‚û÷k²lYc{4m Û¶AÕªyc^ñò÷Â~»=~T±¨ÂÞ{iPºA¶_ËP×v+èRµ Ã]‡ó6ü-Ý7ráŶõÙF«&¹÷}ÿû8Ÿs ‰UÆ7Ÿk¯-êMt4Sîß TP¿üö›\M¼k$ìâõÕ¬Y”¬Z5ß&ÏBÖ(î 4@•*U066&&&333®^½ À7hÞ¼9AAAùêéèèȹsçhÓ¦ ööö9ÞzJ„ì ¢zõáøùÉäÈ0À XŽJÓ¦ÁÂ… ¯¯ óÀþûûºw(!Q!ØU³ckŸ­XYäøµ}B}ø|ïçüûø_ t XÜq1“ZNB•ážxY×ᯜzz •—G^æã²çø5!§9Âö„Ý´¶-\ÈÀÚµaùr¹²¸H¬KÌYÄèŒ)®ˆäDyèС888°sçΤñ«W¯2bĈ|•<'jÚ´)7nÉs ¢ÐG=Qè“^nVì2¹uÀÊJÞaÃÙYÙÉsFE„sOÏÅ~»=!Q!¨P1³íL >”+É3€U+Ž =†s'gôuô‰Ž‹fʱ)tßÒ×a¯sôÚ›nnJÚ2ïë¦_g;ys‹zbnI/Ës‹$qxÅŠ¤ä¹û•+ ì×\\ Lò òÞò7n¤iÓ¦ÚEñ™@›™™1cÆ –,YBåÊ•“ÆwÊÈ^¿ÎÙ”‚ÈÅÅ%O—ãäWÎÎÎÚAqBCCqqqÑȹ¬¬àÖ-èØQ#§Ë77·¤z‘DÁQÁ|²íœY€„DQƒ¢ìî¿›E¡£ÊÝ)_…Šé­§s~ÄyªZÈk\Ž<Û¯!æõÄÜ’^–æ–ðpBæ+++ŠDF²ªY3øòK D¨"gÉœ"–p¼{÷Ž¢E‹fë9!!!+V,"Ê]b1¾ hWPPõêÍãåËÌ:ŽѧÏá›¶mYÙ»7+,,˜Ð0ãN˜È[2§ˆ+Ð{÷î¥~ýú¬_¿ž¨¨¨÷ëááÁرc©šÏ*znß¾³³3çÎÓv(‚ äS’$Ñc`œö;qµùU¼Z{áÕÚ‹«Í¯òÍÞohؽ!üåäù“šŸpuÔU$ÏE Šò·ÃßlrØDQƒ¢HH,½¸”Vë[%%ô™9÷üÞø€îÕ»‹äYЮ‹¡iS.GEáâà@sSSÆ5È~n~qîÜ9œS5–ÔSÄÂ!C† R©X´h3gΤ]»vT«VªU«baaÁ£GðòòâÎ;ü÷ßôíÛ—S§´ÓRöCU¬X{{{,--µŠ ùÔâå‹9mpšðÚá©PAX£00Õ5óœæ1Ûfv®óe×ÐCiU¡ƒvâê««\ó¹F“ÕMpéî°†Ã2|^L| cŽABÂD߄ߺÿ¦Á¨!M›`ôhbââõÃÄ«Tè«T¬«S•æÿ_iJ­Zµ°´´äòåËÚEñ‘@ëêê2tèP† ÂŽ;ؽ{7{öìáÑ£GÄÅÅQ´hQêÖ­ËG}ÄÆ©U«–¶CÎ6##£|w^zøð!ÖÖÖè ŒÜàéé)Þ+iÄÆÆòôéSªU«¦íP´&..޵ÿ[KxçÉsâ—‰uÕµ ÔáRÌj3K+És¢ªU9?â<ßüŽŸÎÿDht(Ž®Ž}x”?zþA1Ãbøúúrèè!N]:E« ø÷çžÿ=æØÌÁÚÜúƒÏ/æõ ûÜÈg]»R2Ř$I„FGSÔÐ0i,âÙ3Z¿yÃàGGGnW©ÀôŠ©gZ°ÛÈ[ZZbii‰QB±¤1EÍ.::: 4ˆAƒ‰¿¿èÉ.ä§çââ¼yóľÛi8;;‹ugi$ú,_þak“ýý!›¯ÇÃÃÐi ãž'ü™"'Š+‡‡‡õêÕÓXlêèë賤Ó:WéÌнCñ õeÛm\ò¾D¯Ð^ì;¾çž#•‘À¸xCÍ5™ÒrJŽÎ-æõ ûÜbaaAÙ%XpäV cAÀ< åÌâtî7nÌÂaò§&5MLø®R%M†«U¯_¿¦bÅŠÚCѱ:#FFF"yÄQåË—‹pjæp177ÿàäÙÝlm!“ò Å{ýú5a†a©k‘*y3 SÔ/ìªtâÖW·è^½;ON?a¥ÛJžÙ>Cª&A  Ðè Q¢ˆ‰ŠÉÑ9ÅÜ¢ž˜[Àiùr–ZY%Ý7'uòì55bôªUD*`Mê(:eÊU"gÉ\áy7hÙÅ‹ùòË/9xð ¶C„BcçNhÝZ.¤/tt¶£¯ŸñMOoJ]ÞØ AŠÏô¸âAÅi °"§’&%94øß·øÕC´ÌàÀ"àÝÈ›IßMÒh|BáQ³fM‚6Ä'ƒÇ—N;³nï^ÎFD0ªlYÚ’_È<È—_~ÉÅ‹µŠâ)j GAÖ¬Y3–/_.Öä ‚ÄÇÃìÙðÃò}]Ý"Œ9Ìû™cmÝ*¯Ãû fÅ͈ ‰‚H £å‰‘PV§,%K–ÌàíªTUïžkËy·óŒJ(lœ–/g©­-?û¤N£}€àªU)vàÓ: [ðcÂèÂÀÎÎŽN:1jÔ(m‡¢x"›ÓØØX±(? Qè£^a/ôQ';E„!!0d$~ØS¼8ìØ¡G§NvyeÞñ ÷£÷ŽÞøµðƒÃ@/@ŸÔE„1PÖ­,[ÿÚªµ83sêÒ)âKÅgz\H|ÑÑÑd}ÿè”ÄÜ¢ž˜[dIW¡}|( <ªKÍÍq:tˆñ”ÐÉÒ¥zuÌ ÑûHOO===bccÑÕÕÕv8Š&–phˆ’Ö$*…覞è–^V;>x-Z$'Ïuë•+ЩS˜‡î¾½K³µÍp{îP§Gª¬†ñ}c¸ÜãûÆÔ:]‹?î ZUåîTR¥BÈÂyCÉðƒ“gsKFÄÜ’ >'33–"¿]H¸úÜ¢%Jð¿·op°´¤·B?ÍÉk"gÉœ":t¢£PPEDD0rä·Y*г··á³Ïò,–£Gaà@ ’ïú)üý7d³É©¢zpˆA»ñ.úƒëfý'ë!NŸ9Í?gþ ›M7ÚÛ´Wü§\ÿý÷]æv!°Y`ÆÅ@+÷Vœ?(–qy :Zþˆj÷nF +ÀÉÊŠA'NðiP/£¢0ÓÓã^Ó¦”M±½]a"ò–Ì)òs‰°°0~úé'vîÜÉ£G8sæ -Z´`Æ ¼zõŠY³fi;DA€¨¨(NŸŽâÕ«Å™ ,˳úçŸaútyí³Jß}óç£Ø‚À¬Xzq)ÓŽO#^ŠG…Š…2«mÂܧv]í°ëš¿–¥|üñÇ43mÆÑÀ£`¡þ«ËVüú㯚 L(BCÁÞNœÀ©n]–úùñÙë×7lÈz]]^&\ XR¥J¡Mž…¬QäŽþýûóÛo¿Ñ±cGJ”(‘4^£F ~úé'bbr¶Å‘6œ;wGGG\]]µŠ ä*•Êy3¨÷ÝÌòäÜ‘‘ðÙg0uªœ<›šÂ®]°`AþMž£ã¢±NÇœˆ—â1Ñ7awÿÝÉÉs>·mõ6Ýn„áÓ4ÉIX¹Y1¡çš4n¢•Ø„ìí[y?Ë„ä[[j^¼ˆgåÊt,^ÛäW¯hkfÆè²eµ¬ö¸ººâèèȹsç´Šâ)î ôµk×8|ø07nÜ Q£FœH|³Mš4!88ooo*W®¬Å(³¯iÓ¦â£4D¡z…½Ð'666Ý䇯¯oª}áß¾…… Ësû¶¼æ×Úöí…íà–-‰Å‚nÏݨP¬ûí£q™ÆêOècii©±sÊ‚«G¯2kÑ,N\9A@DFºF”)V†e¿,£aƒ†9>‡˜[Ô+´s˳gе+Ü¿/ßïݶnCC^˜™ñ®ysf^¼ˆ”°×óÚš5µØÇS»ìíí±··Ojh'dLq³‹§§'5jÔ Q£F€Ü0QLL zzzù²8D,ÈOOt S¯°w ¥_¿ œ4&IáÄÆº¢¯/ÅÇCtt8’ä ,§}{ùÊs>Ê#Ó¹ûö.½¶öâIК—kŽë@WÊ)“ásѰÏ|>EÑÓÓcÉÜ%ÄÇǧšçsƒ˜[Ô+”s˽{Ð¥ ¼|)ß5 V­]]θ¹á]¡1oßòlǘ=›Y•*QÓÄD»1+€èD˜9Å%ÐÖÖÖ.D,] “ä—¯¢¢Xuô(îeÊÈÉ3€½=&«V¡/ˆY¤¸ÚØØ˜íÛ·Ó·o_jÖ¬‰žžvvv„……Ѹqã,í+‚ò- ¤ºŠìã#ßNJ}lV óC)Dtt4'Oäø¹ãtnÓ™¶@¾<ø%Ü7`ªoÊ&‡Mô®Ý[›á ‚bùøø°xÙ2Vþøãûܺ!&ôôx½q#§;wæ”—§ƒ‚¸+V€“SòsŒñ­X‘3nnØ´m›§ß‡P0(.¹àÎÓÓ“½{÷r÷î]tuuiРù¶3NTV6Ê-dD¡zù­Ð'::kÇuéÛ¶Á£Gàé™|óðk{‚ƒ“M¿;},ÉýÂòKW.1rêH•zDdéH~_÷;Öß[cdk„»®;  î´ŸFeeëõóc¡&ˆ¹E½ü6·¤å´x1Ç®_ç[¬¬¬Ô´r%~sçrºU+N5mÊéîݹ§RÉk¡ݾ ÕªÉWŸãâÀ×ʕÿG¦ÿò —DMTT†b¿÷RììbllÌàÁƒ3?0ŸE„é‰BõòS¡ÏµkÃe‰¾~òR´||ä„úÆ ˜;WÞ²5Yb¿°ü³¶õÑ£GôÜ——í_Êm·D–ŒÄ³¦'¸í¡yƒÌ‹3’_‹óš˜[ÔËOsKZ>>>œ}õ ¿/¿Äiñb¶¬\™ôX@L g‚ƒ9ýÏ?œ*R„;{ö ©ùËPG‡–ÅŠñðŸð?^Œˆ€½{aÜ806æa¹râ*4¢ˆ0+݉ðþýûxzz’6ÄüöÃ"qOÅ6mÚ$m#ùÝõëСCÁÁ¶€ Qy‚$EѾ½![¶džQ¯Þ<^¾ÌìØ úô™ÇîÝÊLª›viÊÿƒŒJ6" Ä©xŸ÷δXP »!&°µY3(_ž²..,^°wNq34u‰ŒŽÍŠÅÖÜ[ Z+Æåóçé³cþ}ûª?QDÍ׬áÒÞ½yúý(•««+®®®I9K~ý…KyzñâŬY³†§OŸª}\Á9†ÄQ(HnÝ’—d›¡RbéR>üýÏ)Lúùùá£òÉ8y0#3#BƒB1² ´ d$ñê3åËðÊÞžaóçÃW_¥:N?6–Ÿ=öIl­­iU¬&i–}:-Y‚ÿ°ai?âJÅË¢Ð^…N¼È—ØÊ[ȘâèË—/3sæL¦NÊ€¨Zµ*ªüÚRL  {÷ S'ð÷P±b…9‰Ÿ† ²›7oâ_Ì?Óã̸yó&;vÔ@T‚²bþ|ˆŒd»;ÞãÆ%?X¾<„‡£òó£lHÍ._fÌ´ÑÑÁtÿ~(U*Ã׬mmM¥“'ßrCC?ŽØÐNxÅ%ÐOŸ>¥L™2,^¼8ß ª#ŠÓ…>ê)¹ÐÇË :v”»üôy’QD¨–˜[ÔSòÜ’–……×NždêíÛüÞ¦MÒÕç$Ð}Ü8ê¾~Mg “ ìßÅŠ½÷57¥ÙÉ+66–§OŸR­Zþ*PÎk¢ˆ0sŠ›]Z·nŸŸ_º¶½ù("LOú¨§ÔBŸÇ¡C¹``Ñ¢Ô»@å.ìI\ÜÓ¤±ˆˆpr¥oßÔ…Åk¿ÐØ/ܽž{Ùyw'§žœ"NŠƒxàeæÏ-â_„Úµkð¹E¡zbnQO©sKF¾ùñG&ŒÍÛ/¿Lÿ`ùòܬ\ׯYbo/oñc”ý¥P¡¡¡¸¸¸ˆæ;iˆ"ÂÌ)²ˆðçŸæÌ™3L˜0öíÛ£¯¯Ÿù“,q-Q~š¸!¥gÏÀÆFþ`Θ?_»1i“„?{=’æ§§ˆOÕ"‘2EÊPñ~EnEÜ"²šú+Ñ&MpªåÄüo ñ_¤ ¼‡÷«WXIÜ´iêxù’ÎΜpw‡ô‰µˆ¼%sŠ» `ggÇòåËéÒ¥‹Úǘó Bõò¥|å91yž>=ÿ'Ï’$ñðáCÞ¼yCƒ (Z´h¦Ï ˆHºÒ|òÉÉtIsiÓÒô©Ó‡~uúÑ®R;T¨è5¤nwÜ©‰¥»[ŒÖº­™;}n|w‚P0tž5‹¸!C2> \9Ԯϛ7ï -yDq tPPM›6¥^½z >\ ‚ùúÊÉóãÇòýI“ÀÙY»1å„$I,Y¾„µÿ[K˜E†˜™QѸ"¯ü›ÊiZDàêéÊλ;9ñäDº¤¹”i)úÔîC¿ºý°©dƒŽ*u‹áƒ[²ö¯µ¬Ù¹†€ÈŠgtÿÑŒ&Z BFþðòÂóÂðñA7&†ª>>覸x RaX¾<º11¬Þ°y3gj1Z¡0R\}òäIttt8vìÚ'׈"ÂôD¡zJ)ôyûV.ôò’ï ¿ü¢Xr«Ð§×à^œÑ=ChçäÄBxö‚vCÚq`Õ*Õ¬”œ4?>AL|Lª×(iR’Þµ{Ó¿nl¬mÐU½ÿ£ãQÃF1jØ(ââârµ8Zª'æõ”2·dæJH}}aõjJ°üñc^ÆÄøx"€aMš°óرŸKª'Š3§“ù!šU¡BŒŒŒ066Öv(¹J¦çââBè{öâ,¬œp‰7 @Þª.±ûíÈ‘¦x]£ }rbÃæ œ=Ohu5ï9Sð¶õ¦Ýçí(í\š/ö}Á‘‡G’’gKKF4š?ÿ'þèù*wÈ4yNIWW7×w:wî\R!¡LÌ-ê)anÉŒOt4wï¾JÅÿÊ–eÐüù\ˆ'"á˜ÕŒ^´(WΗsKA$r–Ì)®ˆ0..Ž=z`kkËäÉ“ó}!È‹ñãââX»v-zzz⪈ hAAò•çë×åûŸ6€Žâ~ÝΞº~ÄõV×ßÐ À ¨"'͵è_·?¶•m³•, ‚}Qññ´wwçRHTªÄ˜~ýàÚ5þî1&2R¾ú|íšvƒ- bcc‰eÔ¨QèêêŠ"Â÷P\&÷üùsf̘ÁâÅ‹iݺuº$ÚÕÕU¡åÈ•+W˜8q"={ö¤gÏžÚGÔ ;»ääyà@øóÏüŸ<ÇÅÅ”ùå¡~`}–]Š­µ-z:Š›"¡ÀúÊË+)yþ²LÆŒ ‰r§#XsãçÚÕg!½#GŽpðàA®\¹BË–-µŽ¢)ò§ƒ‘‘Ÿ~ú©¶ÃÈU-[¶ä?þÐv‚¡°0èÞ._–ï÷î ÿ]0v‡’$ ‰,|ئ‚•:йJç¼J„$+¼½Ù°É|;ssV~ÿ=?.?Ø«¬^ÍèãÇYèäÄ·vvZŒ´`K¼È'ZygNq tåÊ•óåæÌˆ"ÂôD¡zÚ(ô‰ˆ€ž=áüyù~¯^°};(åŸ&§…>zzz˜é™ezœÑ#ºtW¿}¦‰"BõÄÜ¢žR‹O2åÑ#*±{Çô·l‘lÕ vì]]:ÙÙaËÅ~¢ˆP=QD˜¹|þÁlþ!ä§' }ÔÓt¡Od$|ú)œ>-ßïÚví%•äF¡OÛ6¨n¾gKÌX°~eMÇstME„ꉹE=%>Žˆ ÿ½{ÄI&ºº¸^»FÉÄ8ëÔ Ŧ¹èŠ"BõDÎ’9E^¸p;w2bÄŠ/ÎO?ýôÞãó[ËMÑÑGPªèhpp€Ã‡åû:À¡CÔWÑözîeÐîAD튂@ÚŸÁ‘Pö|Yvÿ´›–Íź?AЄи8Z^¿Î°0¶ùù1°$(_.\€ ´eá$ò–Ì)âó­×¯_séÒ%z÷î‘‘—.]ÒvH‚PàÅÆBÿþÉÉsÛ¶òÅž‚–$666ó OOÏ=ÿÝ»wex bР vízÀÇÃ?ÿ@‘"¹}ÞˆåáÇÙzÎìS³™|t2%ŒKprØI:T¡!M›6¥gžù:yöóóK*$’‰¹E½œÎ-¹e¯Ÿ ž> ¦ž[?û ÐPyËŸíÛ¡MÅò!sKa r–Ì)&.èÄ‚üôD¡z9-ôiÑ¢7õêÍËðV¡Â÷ð@ÌttØçä„YBÿV­’+š5Hª'r–Ì)¢ˆÀÜÜœ7boo¯íPrXŒ/hR£FŽÜ¼¹1Ó㌌yñb#i´ØøX]Ùr[Þ«Zñjzkskí&114½~Çè¨Tøãºoß.?8>Ì™£Ý…$"oÉœ"Ö@':pàO>ÖyŸ‰'æ}0j0nÜ8ÜÝÝéС?ÿü3F­âJ(4ªV¥@%Ï‘±‘ôÛÕƒ^hPºdž£´ii-G&Bœ$ÑÿÞ=GDðÃñãÉÉó—_ŠäYÈw•@ÿùçŸY:N[ ô´iÓ(Q¢Çç믿féҥ̚5K+±BN¤!Q!|²íÎ<;@« ­84øæFæZŽL€)q"0€A^^LOlÇÝ»7üö›#„£¨¡›7o¦GÚ#Cnnnœ;wŽ’%K2eÊ”l­' òÓÝÂ`íÚ-¼xñ6Õ˜Ÿß,-K¥«P¡$£F ÑdhŠò¾na~á~Øm¶ãšÏ5ºVíÊž{0Ñ/`[Ѝ!:ª'æõ´Õ‰pƒ¯/+¼½hÄú äÚµƒ-[@G{åX¢¡z¢aæ5»˜ššbn®Ì+Foß¾%((ˆ’%KP·n].]º„$I¨Tïén–@,ÈOÏÅÅ…yóæ)öß\V¬ØÏݻߦ,H5R·îâ èwïàêU¸x.]‚»wó&VmJ,ôIÛDÉ;Ä›ÎwÆÓOÞ] _~lî½]m„©q‰„±v$'ÄÜ¢ž³³³Æ×´^ á+//JÅÄà:f ÆQQP¿¾¼¦–—Af4·v¯_¿¦bÅŠÚCÑ•@ç¥èèhž>}Š‘‘Q†oŠøøxîÝ»‡··77¦té䵓ááá©~344$::šøøxtuu3=¿x#¦'&,ÐÓ3¥ÝŸÁqrOÏäd91aŽÏûXµÉÜÜ<ÝûÅËß‹Îwæy°\Á?ªÉ(þèù:ªÂ³¹HœÕs‹zšNž_EEÑû΢âãÑ—$v;9QáͨTIn”¢€_pÔÍ-‚ÈY²B1 ´™™úúú¹þº.\`„ ܾ}›èèhlll8}útºãèÞ½;×®]£téÒ¼|ù’ï¿ÿ>is¥J•$‰àà`ÌÌ̸{÷.üq–’gAÈ)__èÚ®\  Œ«QÞ¾…„¥†ù^LL ëþZÇѳG ¤QÝF|1ð ¤R]7wåMئµžÆ’NK´­ ‰¢âãq¸{ŸsE)× IDATèh~]±‚¶·nA‰rò\¶¬–#„œQLýìٳΊëׯC:uRׯ_ŸK—.%ݯ^½:ׯ_ÇÛÛ›ÿý—R¥J¥}© ;vŒ3fàììœêö<±S‚Â4æââÂ÷߯ˆX49öäÉsÜÜ`ÊððpRçœn¬^=2ęٳŸ3bÔ©*•ús/îO·nSèÓg^Ò­^½žéÆŠ÷WÔßKʱ1Ncxÿ"¢+À9 ¨ Å#ŠÓ­T7ÅĬ±ÄN„JˆEIc‰•‹’ÆwŽÊËs dê£G°u+eïßg÷¬YèÇÆÂÚµ8ß¾­õ¿ƒ´c);j;mŽ%æ$vvv̘1ƒ«W¯"dB*Dºví*ÙØØ¤_¿~½HwïÞM5>kÖ,IGGGЉ‰ÉÑy‡ &uìØQºqã†äáá‘ê–êX1¦Ü±Kõê “ªW·—êÔ"Õ«7,é–r¬C‡Áéž.IûöIRïÞ’¥e˜$—J “ÀC‚”cRº±† ‡)âï@Scu:Ô‘‡Ä,$楸¥S}¦’Öm\§˜˜Å˜+ìcÃÃ% 77‰S§$£uë¤ ÕªÉØâÅŠˆOŒe<–˜“:tHºqã†doo/ 6L2¦˜VÞš`ggGdddº"ÂåË—3iÒ$Þ¾}›j?UÆO```޶C-1 †¬¶ÈnØÐw÷ÈËý\]å¥aa©ÓÕccGBC³þš…ATT5ºÕà¹Íó÷ _Ä}Áúë5˜ éÌš3‡E ð..Žׯs/a¢Û¶p!Ož„ à=õG‚2‰¼%sb P¦L¼öªLäáá¡¡a®ì%zîÜ9quuÍñk Êæç:@éÒ0lìÝ›œ<›˜€½=lÜ(V­ªÕPÉÐÐcŒ3?0êרŸ÷ ‚ Ö¿§N±rûvŽŸ:ÅP¤äyÆÖ­rò<`,[¦å(…ìpuuÅÑÑ1iy!cŠÙ…C›Ê—/ÀÝ»wiÕªUÒøíÛ·“Ë©¦M›ŠßäÒ(¨ÝÂ^¾”o‰J”€ž=ÁÁºtã¹atô[,,RïA÷]ÝÔë룣Sw+,èt t Hl&\ûX<ù˜R¾¥èbÛEóÁ)ˆèD¨^A[r*§GöéCÌ‹$¶;HèO?aïäDøÒ¥”»|™7ëÖÉW6mÒj—Á¬S³··ÇÞÞžAƒi;ų ТE ¬¬¬¸ví£F 22’Û·o'ÝÏ)щ0½‚Ü-¬R%ùJ³½=´m+/×PgïÞ_ˆˆˆH56gÎ,HÝ‰ÐØ¸[^…ª(¡Ñ¡Œ98Úð/ðI‰E„vò]_:V옮ð·°Õ+ÈsKV ´³ÃÀÇrÏ>{F»J•’îÇKÑVVl?r$K¯ÙÆÖó©S±Œä_]] ææ„7m îîÔ,Q‚9 ]®œüÑ›ò;‚ŠN„ê‰N„™+ðk ƒƒƒYµj6l &&†Ñ£G0zôhŠ'ìýµ|ùr¦L™‚³³37fÅŠœ?žk×®Q¹råÅàèèÈíÛ·éׯmÚ´¡M›69û¦­Èêè5¹?óã„Ôn¾¾Iÿ]ýñò——RZO|[úBâ”ñPì~1…5âðöØššj/`AP°ÍkÖ 7iÃÃyC›FFF¸GF&-ÚnbBì²e|–ð313±±± ªW ÏžÑÌÊ U«ÀТ¢Ðýáî»ví¢~ýúâ“ó÷(ðW ÃÃÓÖ[XX$Ý4hPR=qâDôõõY»v-?üðÍš5ãìÙ³9NžU¬X{{{ñQk!`œ…å»Bj«¯­f⑉DÆFУzþšöQQLž3™wEq“â í=”QÃrç“!A(¨A¿eËèïé‰ð£©)ϺwçÇÇ™F<°³bEv‘ákDÇÇãΰ°¤Û¥•+)v÷.Òýûrò `hˆ‰µ5;.\ ÇÌ™"yÎÇjÕª…¥¥%—/_Öv(ŠWàh++«T{9¿Ï×_Í×_'q˜™™åhí™ Dï¢ß1úÀh¶ßÙ€žŽ?tü§VN¨P1l_·]ËQ Bþ£««KïI“Ø9iŸ†‡³¹lY¢ÆŽe³»;Ó<`Ÿ‰ ½'MBWW—ø˜p'8˜;¡¡Ü‰ˆàNl,^’Dºv4°?̘‘jøÝàÁ¬8s†7³gkì{rŸ¥¥%–––6ž’){u"ºú¤'º…©§©naÚvÃ÷­þ()y®P¬g‡Ÿej«©ròœ‚覞èD¨ž˜[dƒFŒ`gÅŠ,15åÙˆðü9OGŒ`TÍš,rpàØ«W|´nENž¤†‡½_½bNH;cb¸—&yÖ‹‹£ö³g´ûë/ *WN¾úœÈÐèÎ9íæ¦Ñï1§ÄÜ¢žÈY2'h 9}ú´ØÆ.Änaù…¦ò”ÄnaÙªÿVÑr]K< gž¸éNËò-ÕŸ²[˜,q½¢Z~›[òŠ®®.=Çcu¹rÄ4o[·Û¼9[¬¬¸3t(·oÏõjÕˆH‘ «$ k__z^¼ÈŒ­[Ùòý÷Ü1‚0;;î9:~ô(Ѩ=_Xß¾|›Ï¶­sKj‰ÛØ¥í—!¤Wà‹•@lHžÿ> :ŒB’bÐׇZµ@__ý±eËêsàÀZÆ—_„D…0rÿHvÝÛ€¾Ž>‹;-frËÉé®: ‚ssü‘…€Ô¬Yòàµkðä ¥{õ¢^Xõ"#©K=êêéQÄÄŠSÓä?MMù÷Ú5:D@Ÿ>ž¯øÿþÇŽÞ½édk›÷ßœgDÞ’¹¿ZrêÉèÛ$i-ÆÆàæ}¤í¨òŸë>×é¿«?PѬ";úî EùZŽL ¦ˆˆ¶üóÒܹ©øè#*ÿû/wÛµÃ8UÏÓ-ÂÈÜœ ¿ÿ€Ÿ–±±øééaie@\DÓæÌáz>[Ê!Ù%hAx°0øôSð÷—ï¯['’çŒÜ¿Ÿ) ¦àäMDL–&–ص¶cÖ”YüþßïL96…è¸h>©ù í7bad¡å¨¡àúqÅ žôè¡ö1ïÎùñ·ß˜ëä”å×»øï¿¤üÐúïU«89k-bèW_%«TâÓ$¡àk 5äìÙ³b tJ/ô‘$øüs¸}[¾?m œ÷çÍE„+׬¤Ã¨*ˆ›ÍnâÕÚ‹ /°øöbJ|\‚ñ{ƾŽ>K»,eßÀ}ÙJžE¡z¢ˆP=¥Ï-šÁêýû‰ÿøãäÁçÏ“¾ŒiÒ„Í'O¦käô>†††%݆OàG1|üøTã†i LÌ-©%®>{ö¬¶CQ<‘@kˆµµ57nÃRPz¡Ï‚°güu÷î°x±fΛߊ=ï{²dÇ^u|•Üz;ADÕ‚ÚÁ ¨dV ·/ܘÜrr¶Ï! }ÔE„ê)}nÑ„ÙsæàÓ»wêÁ­[SÝ}Ö©?þöÛŸCOO×ãÇóuËt1·¤fooÏÆ±¶¶Öv(Š'Š5@,ÆÏöî…>}ä«Ð5kÂåË ¶ÅT¯Ý§íp«ãFcp΀cßæ¹æ„BJЧ|÷î¼J¸_SG‡ÒÅŠ!IRºåUK•âÏ•+5¤ h"oÉ\þýµQòÈíÛòÒ I’“æýûEòü>¯ß½~oò ]1šK/‰Z4`ùμJhtÒÿÕ+vhbí™ 2b ‡ ¤àï/ ††‚Žlß5jh;*劊Š"’ÈÌ4ƒOä}@‚PÈÝ|ü˜oK” B@«{öÔrD‚P0‰ZCDazJ+ô‰…~ýämëœÁÎNóqä§"BCCCŠéËô8×:´kÚîƒÏ# }ÔE„ê)mnÑ”Èøx»»¥¯Ž$ñw‘"˜Kþÿ™ŸæMsKj¢ˆ0ëD­!¢ˆ0=¥úLž §NÉ_S§j'ŽüVDظFcT/ß¿mUyŸòtéØåƒÏ! }ÔE„ê)mnÑ”ÉÇŽq¯xq¾»v ›.©ÿÏå·¹EÄÜ’š("Ì:QD¨b1¾ò­_#GÊ_7içÎA6ú Za1atߨ³ËÏBÀ4ý1¦7M™m3›éßL×x|‚PXxñ‚OÉMŠZxyáÖ«z ÍM!»DÞ’9QD(z.Àرò×¥Kƒ««Hž³"$*„î[ºsþÕyèfÿšS9†ð áPxå—g„ݦM˜¦íp¡ÀòŽfĽ{`hHÑðp¶èë‹äYò˜H …BÍÛz÷†èh00€ÿý*TÐvTÊD×Í]¹òò ­ضt'ŽŸàÈÙ#¼|ù›f6|2çñQ  ä! p¼t‰· ÍK\Ž¡ÊŠÚ J ‘@kHTT”¶CPœ‡bmm­µMø#"ÀÞ^¿–ïÿö´n­•PRñôô¤V­ZÚ#Ctþ»3×}®пn¶ôÞ‚žŽÝ»u§{·î¹~ÎØØXž>}JµjÕrýµó³ÄBKKK-G¢,Úž[4iå³g`À™3|þõ×A+m¥Ï-Ú æõ¢¢¢òUGImE„rúôi± GÚ.ô1®]“¿7.y ´¶)¹Ðçmø[lÿ²MJž‡ÔÂÖ>[ÑÓÉÛDEú¨'ŠÕÓöÜ¢)·Ã˜þø1ß¼á¹óS”<·h‹˜[RKÜ…ãôéÓÚEñD¡ˆÅøÊ³d $ôÀÖŽƒBp±*G|C}鸩#÷ÞÞ`x£á¬ûd:*ñ{¸ hZd|¡Ð9|fΔ¿®\víÉsf^¾{‰ÍF›¤äyÌGcXÿéz‘< ‚–L{ôˆ; {]ÏØº•vÓ¦‰äY4Hüô OO<âã¡H¹MwBÓ.!σŸc³Á//Æ7Ϫž«Pñþ½ŸAȇýýùõåKšzz2ßÐZµÒrT‚P¸ˆZCDazšî$·é–kl6m‚zõ4vú,SR·°Çi·¡åýe§´œÂÊn+5ž<‹naê‰N„êäN„o¢£ùÂÀ"l]»½~ÈÒs•4·(…˜[Ô9KæD­!¯·z’h²Ð'> /ù"*óæƒƒFNmJ)ôyð›6< ~À·m¾åç.?k%Q裞("T¯ ¿Ÿ× ¿¬øõWªÍž )Úu¿Ræ%s‹z"gÉœ("Ô±_û¦N…Ÿr¿>}äuÏìô$~tü«#>¡>̵™Ë¼öó´” r./_2þÁúœ=Ëî›7åÍë!—‰¼%s¢tJCpww§L™2”)SFÛá*›7''Ï À_‰äù}n¿¹M§Mxö€E1³íL-G%…Ûݰ0¦&´ê.ççÇš5kàÊ-G%4¾¾¾øúú@ñâŵŽ¢‰%âëëËéÓ§yúô©¶C)T®^…Q£ä¯--aß>05ÕnLJvÃ÷¶m“’矻ü,’gAв¨øx{xŽ$ñ÷?Pü»ï lYm‡&0OŸ>åôéÓøúúj;ÅW 5¤jÕªLœ8QÛahÍÎ9x0õZÍwï)RÄ UŠ­Ð ÀÅeFFF9>§¯¯¼Î92RÞÝi÷nÈ]¥µÕ-ìê««tý»+‘¬ì¶’ñÍÆk<uD·0õD'Bõ Z'Âs+aM·ÓŽØš˜À˜1Ù~щ0=1·¤Ö¢E Z´hÁåË—µŠâŒÙ%(ì òwîü—ÿýo,²Øe02Õ˜•Õ"##³”@Oº˜þ¹‡J¥›î1I‚'O <àK–ìÇÆ¦h¿ ÍpvvÖøº³ /.ÐmK7B¢BP¡bUÏUŒù(û? óJb¡Ïòå˵Š¢$ÚÛÛk9eqqqaÞ¼y˜››ÐóîÙÃóç3?ÐÀ€/§Oÿàó¼Ï¯¿ýÆø¯¿æX@+¼½hòàßÿý·üÑÚ¬CÓÆÜ¢tbnQïõë×T¬XQÛa(šH 5D¼J)ЬIw„ŽŽI–_íÉ“×ܽûkš×LÏÔt"ÇÇeùuµMÓ?àÎ>;K­=EG¥ÃÚ^kù¢ñ!3æææâœ"qV/§ï=}}ü׬aÄ{vòˆfԩÌŋst.u®ü÷3/¦æG1,> 0‰ŠbëÂ…è;9A:ôº"yNOÌ-ꉜ%sb ´Pà™™i;å:ñäݶt#4:]•.Ùÿ¥¸äY4Í®W/žT«Fe V77ss¾É£má&/YBøÒ¥ Z¸ßèh–¹¸PÓÔ4¹ª Z%®@ Š'NÈ݌䛱qò׉7C쿦ØqC½#à°ÃÈØHôtôØÜ{3êÐvX‚ #,`íçŸ3.((ÝcQÀikkÆõê•+çúæóÏ ~ü¿ðpþ«QJ—& tiðò¢ÂÛ·\8x»]»²";“Ÿ yF$Ð"ºú¨ó°&åÛ0 úöÍü™*èÐÏO4QèsÀë}wö%:.}}vôÛC-…v–AúdDª—E„v½zÑ×ÚšQîî¤MY×+ÆÈ¹ssd µëÖÅzÇ좣iceEÔÂ…ò}ú`°t)7®]㪎OmmstQD˜ž˜[Ô‹ŠŠÂPü²ö^4QžÂ^D¨ž ðaÝÂ$ âòϲælÉ­na¡¡¡|5å+Z~Ú’Ú]kÓòÓ–|5å+¶\ÛBŸ}ˆŽ‹ÆP×=ö(:yÑ-,#¢¡z¹Õ‰päüù¬M³ïep:$»Þ½¡T)hÔºw‡#`öløýwpu…Ë—áÅ ˆ‰Éô<_LšÄŸ•*q^_Ÿ¶¶É{mššb\ªBCù³re¾˜4)GßèD˜ž˜[Ô9KæÄh )ì òCBÔ¦/Ü(U ¶m==y9GddÆîؑܚ» ÉBŸëî×0v<&¾I|ÒøeŸË¬¼©“„QY#\ºÒµjן/¯‰BõD¡z¹ò^9{»E‹èÆ(Hº ½yï $ Þ¾•o7ofü:*•¼ ½••¼o³š? Ê–¥Ü7ßÐa×.¢‡ IõôàÏ?g¸›ãGÂÀÀ Gß’("LOÌ-êöœ%+D-ä)I‚eËàäɬ¯¯Mš@Vv…ºw¯`&Ð9Åg>ãa‡‡ Ÿú1ÉJ‚~ ³GW×ü‘< ‚FyzÂôé°? 'Ëkut/_}®Z•qß}¯^ügâ×>>Pô—$e¢}ëVºÓù/Δ±cÙZ¡T­ &iv"21áQ›6Ôo×.o¾_A>ˆH 5äùó縺ºR«V­B³íéSpt„3g´Iá²dÅT.yN¢ªf*®ì»Bש"@î¼4o¬[—¼>¬HìþÏÞyÇ×tþqü}³ÙCb‡Qµ+jTmjïJ:h©¢-UÔ¨ªÆ(¥Š_U[{oEQbUk¦H¬ˆ•D¶È¾y~œìÜìqž÷ëu_9÷¹Ï9ç{n’ç~î÷|ǤI ص‹QW¯²ÚÒ’–,œ’…€ÐPÍâ:uûñc D­V³òí·ùêý÷‰¬PæÍƒšmÅ»¹1eñbÎlß^2×.‘¤póæMnÞ¼I@@€ôBçÐ¥Db>âà^$~ýUù,xöLynj ††_gJJHJ GOÏ‚Œ¡ø ùh^ð‚SÔDŸã'éÕ¤\ç¨k¨9þ÷qf0£Ðç)Md¢fd¡f ”Døü9,Z¤2’17or96V¸}ƒjÕHÈê}NÅÄ„[¶¶œ¿x‘Íšå}M9 “³#×ͼlš¥0H]Jèëë¿ñŠAA0jìߟ>öá‡0nÜ'ÄÆ†eš»xñbFŒ™YÆýóÝÆ»~}GîÝŸ©¸&ÔêàrÕÒ·¨Ý¢ã£!{sÆÌèÀ³¸g…>Gi#»…iFv"ÔL¾:ªÕ°f Ìš¥xŸSéÝ<< ^½´¡®½z1·R%¾š3§Hv…'%1íÞ=~~ò„d!¨fhˆÉÖ­DØØ »v-Ï££Ñ}ö #!ˆS©P›™QÁÔõóçLœ>¿ÿü³Ðç—³#ז̤Þ%_¶l™¶M)ó¨„Hù/–”îîîÀ‹ŸÀ±k—"–SœbTª¤|>uëßÏËéÅ‹iœGÌæäd®øûc’“'F’#Ñ Ñ¬ûoS&N!º]4Ùjoe$zøõàÀ¦¥fŸDRfØ¿_‰s¾q#}¬E X¸rˆ5.ª÷v]` “ïÝ#8%FZ_¥âÓjÕ˜Y£Ñ!!¤~'&&2¦];Vûù1ªfMVž<‰¾¾¥R©°··/´ I~yYtKQ(?n9I™%2>ùÖ¯O4V®kkåù˜Ï>ãüï¿3ûîÝã©§‡é”)R<{á÷X~~9¿^ù•ÈøH¨\šæ¼ù sÞñNi™(‘°}Ã.9’÷D##¦ÌŸ••UžSçL›Æ™µk©llœë¼cbø{ìæÌÉœ˜Q«–džóçÓ>Is|îÊ5øM¶©Í7ÇîcÙ¿ËøãÎ$‹ôRuµZÕBwŸ.¾Á¾ˆŠÙo0©‚U´ÔmÉà~²ë ¤t±sp@gÿ~Ækèî—J ðYãÆùÏMœÈýY“Ë—s`mÍšXuê¤$úòí~Æ ;ŠXNÏÕjæÜ¿Ï’HL9§½‹œœ‘‡ù½O?åÍ-[ø¡ˆuŸ%IÉ!t)Qž:~øá NœP¡£“s €ñqqGB‰3ìÜYI¬REó>c§MãÝ-[hŸòA—±¡§ž͇ yi½ÏqqqÌøn§/œ&$<[+[Ú6oË7S¿É“Ãú«ëYöï2|žúd:Æ›5ßd|Ëñô¬Ó“gnÏþÑpΜ#Ì1 l€P°ö·¦U…VlúeS)_aщ>š)oI„í;ub¥³3æ/’Óú"kk&.\˜ïcV¬XÛðY»–WRƲö8ý^¥â;??剑ŒS§æ¯^f!ØÂ__ââÐQ©S¹2skÖÄ2ù:}ºÈuŸ³"“³#×ÍÈN„y#t)Qžºú$$@`à, ¯¥ä’‰‰:8vlî³Ó¼Ð´W«YÌN9ËËì}ööñfà‡ñ}Å—Ä–‰°î¶¼ËåÇ—ùã­?Øþ¿íT¨\Ÿ.üÄšËk OÛ×Dß„¯Ž`|ËñÔ·«Ÿ6naaÁÍ8sö {þÜÃõ×iP·}†ôÁµ«6.³HÈDÍ”Ç$Â1ß}ÇŠÁƒ™–íµàB­ZLêÔ©@Çü|Á¦ž8Áš€€Lk‹`+U*>¾ýJ¨<—_\ãïÜá@hhÚXs33VÖ©CÓLÉÒycjjZÜæÉ$B ȵE3AAA²Œ]H]J¼¨ˆÍšÁ¦MP»v>&ÿý7c¯^å]µšö¤÷!ôšaš½‰À Nbb"ÃÆ ãÆ7 ÕÙ”¢…+'rÃö¯xè^Ñ$뤇iT·¨ÎÇÍ?æƒ×>ÀÚØ:Çã»¶q-—‚9+²[˜fÊ“pN¥}§N¬¬U‹˜°°l^èÆÆŒ©UK©™˜˜ïGÅÄDlŸ=Ãx…Ì=N¿¾kÛ–.…&MJäš’“YøàßÞ¿Ol²òj¥§Ç¼Zµ]©:¹ÄV—&RÉ*ݯÄÁÒØ¡q©Ø%‘™§OáÌåqú4\¹) ÄíQ¾8Ç@šz0FWWIèÓ×/𣢾>¶^^øÜ¿Ÿ ý}õê|·qc‘/%**Šï~ý•û!!,™=€À„>¿{—MÂóT¨ÀÊ:upµ°(ò9%IÙF è0räHŽ?ÎÓ§Oùï¿ÿ ”ŒQž’‹Dd$¬Z¥Ü.}ò$}ÜÌ F†‰1©Z•æ_}ÅFöÖ¨Áï/ÂÅ‹ÊmÛ+WàÁ6L)çñÃJœÈ JdLdö¦'!@Æœ0]¨lRù¥Ï2ÑG3EM"<~ü8¦iñ¶—¢£Yºu+;vÌyÒ½{ébùÌ™Ì_œ3¢RA½zŒ©Y“'O2):Zñ>7kƤ  u©|ÌÔæÍ™@,`Û¡+V,Ò1f-^LȨQìùãfFF²1:š¯üüˆLùB`ª«ËlGG&T­Š^ ×ЄL"ÌŽ\[4#“óF è°jÕ*LLLptt,ð¾å)‰ðé#_ìøŠÜ;q€*ê,‘‘‘J·¯‡±ûóÏéý»Aé¦2~<Œ¼2c§MÃiÉÆöí«TÞxã EDÿú+LŸÁÁpö¬ÒÜÀÝ]©ÓêàP2¬E*ZT„2‡pœ!-€„”y/12ÑG3EM"ìØ±#u«Vå^^9þ·ÇÃ7Î,ž““áêÕÌ‚ùñcÍÐׇ¦MÁÕÚ¶…6mÀÆFñB7oNÌÅ‹¬°¶fÌwßê2’Z‘cÎÚµˆªUù~Á‚"3**Š=/’yá´‚«W¯rñâE®^½J||<«V­Ò8÷Áƒ,_¾///êիǨQ£¨__©¥ëïïŸv[´wïÞÌ™3§Ô®¡,ÀX2…¯ˆÊöZø€ÚÞÞXçòw¦Vs§~}vŸ:U„+(¢££Ób{SQ«ÕQ¹råLã4n\ö’+Û´iCÃ:u˜ûï¿ä”ž µlY$ñ\\»¶hÁF‘žùxªTŒkÖ,sür1$åe¤¸Å3`mÉ…¨¨(¶_¼ˆ˜4)Ûk÷{ô`ÖâÅi9Ê 2‰0;2‰P32‰0ˆŒøøxáãã#Ôjµøî»ïDN—8nÜ8Q¥J%„"11Q¼öÚk¢gÏž9ûìÙ³båÊ•ÂÚÚZÌž=[lÙ²%_6¹¹¹‰:üb´ÌÞûÄ7X ¡i"D]]!¦NâÉ“"„ "<<¼`;…… 1~¼zzB€˜ ârû²>Ü ~ŽR`ãÕ‚Ùf#ê.­+úÕvN·9ƒý*W / Q€k¸|é’øªbÅß⫊ÅåK—Jð ORR’hYµªØ¤¯/6§<Öèë‹n::iÏ7ëë‹÷ŒŒÄúÿýOÛææÈiOOáac“ãïÀÃÆFœöô,Ò9vïÞ-vïÞ]ø$& ±x±ææâˆ3Ø÷£©©8´m[‘ìÓ…Z[²0væLÁªU‚'4>jöî-"##‹ÉâÒÁÍÍMÛ&”9ÂÃÃÅ„ ´mF™£C‡òï%^¸.êÕ«‡N±i6l E‹˜¥´WÕÓÓ£C‡ùD‰ß¾ΟWª–¤Ðäµ×¨Wœ¢B€€zõhòÚk%u‰EBWW—qÓ§£¯§ÇÄD†$&ò^b"““ÓžNL$ªfM†¾ÿ¾¶ÍÍ×víðrv&ZÃkÑ€—³3®íÚé}úô)|7ÂãÇ¡qcøì3ˆŠ¢+àiaA<)ÞgggºX$û´E¡Ö– DEE±öÂ…\ï¬ÝïÞY‹úÚ@&„eGv"ÔLyÔ,¥Ž¶|I’“:$$Dbúô陯ÿýwˆ‹/«nnnÂÌÌLôéÓG¸¹¹ezܸq#Ûܲ2Öê•~bFi^©85¬­ËŒ}nnnâÆªUb¶¥¥¸”Á{{#e{dŠ÷¹,½§ý†ô–“,³FsÄßþnnnâÚÒ¥¢¿J•É ÝDBFoz†kÓ8fl,Dݺ­re±ë­·ÄW*hœ—ê}.Žk‹ŠŠ‡‡:u?ÿüsÚó¬c—/_Î÷9’’’D á“Ãõn76ëW®Ì÷ñž={&ºté"Ö®]+Nœ8‘öÐ4ööÛoýw)Äߋӟ.šêêfû½550Ö­+Ú9 ;vâ„f~OkÖâðaqhß>ñ£¥¥hi` ~^±B;ö•±n“& ll; ã>}DµEµEGGáеkÚsÛêÕŰaÃÊ„ÍrLŽeÌ-E“899‰>}ú'''á&=йòB—±óðð`êÔ©d½Äk×®ñꫯ²iÓ&†fèvwùòeš6mʾ}ûèÕ«W±ÙQ.ËÁÄÅqϾ5µ¢¼lV[Zâ¼n]‹ñ½)"ž>e¼‹ ëÂÒ}®—ý:0ëøqí–…°Ø0Z¯iÍíÐÛ¨P±yÀf× ›6›¿$%Qè ì34$xêT>èÒîßÿÌ?ïßWZ#ç‚°ÈXd+ øÔƆµnnJ’fê£zõ|WNȈZ­¦eõê|þô)¹Õ\9ª«Ë‹ã6fL¾½aÕ* ?ûŒYªY` ‹ [¯_Ïw~BRR-ªW瓈ˆ\í<¡Rñ†‡ïòIþŒŒW†\¿×®¥ÿ H›2%o 5:¥R±¹øè#èØ1ÏŠ5ÅB|<|ÿ½R†-¥í4¦¦0c†’¤›Rnr@“&ì¸r¥äm*ƒ\‰Ž¦õùóÄ'%QÍȈ›4Á*—CCCTe¸yŠDRÊ¥n)e^¸$Âüš—i<õyA¶òKyJ"ã>¡V”ͪ÷fyäIÎ9:2®Åsq$úXÚÙá4t(—V®¤iJõ‰€e'NÀ›oÂ/¿@ÍšÅ`qáIP'Ðok?n‡Þ`nǹŠxþùg¥ÉLr2nÆÆ ±²¢öãǬ­^-Ó¦)M(ZµÒ|Мŵ¿?##Y|“a—%ÀÄÐPÈzëY_3‹êÔGÍšÊëÐÕÕeÜ´ièOžÌ€ʶ `[­Z 5*ç7(9Ywî¤=†Þ¾Í`µš€ðœ*}Ð}ÿ}¥"DíÚé 4^OO±_|ÕÔ©ôÉòŸÑÎýuê0ò£4Ûwï^f‘|ýºbkJ7ºœøØÀ€Ÿ’“™’2ï'àc!`ÇåQ»¶R²ñÝw¡€5…óDøÇŠHöõM:T Ê’˜ùÁ Pq¨°kKTRƒ¼½‰×ÑAÏЭS)%ÌïE@&fG&jF&æ-{ÀK”œB8"## >ÿüóLã«V­€øï¿ÿŠÕ777áàà ÜÜÜŠ–ðSZ¬]›v{÷­ÄÚ_Dqhß¾b=Mq$ú¤.FÔ¨!ˆ‹*•˜mf–~˼B!–.B­.–s†‘»G¦% ¾»ç]eðûïÓm43ÂÓS¬^¼XÔÖÕ«/.úI#"ÄȦMEhÊ9BAŒ´¶¢n]! rM2ÌôÐÕ¢fM!ÞzKˆ1c»÷îÂÛ[ˆØX‘˜˜(úÖ­+’sØ»±±ømùråý¿_ˆcÇ„X¹RˆÏ>¢W/!\\r´g=ˆ­ ÂAL‘ ¢ˆ¤œl­\Yˆví„5Jˆ „ؽ[±3.N$$$ˆ~uêähçn##±zÉ!=âÏ?…X´Hww!š6UÂcòó>Õ­+Ä€BÌž-ÄŽBܺ%DR’Ò²¥xâˆ!õë ñî»Ùid$Ä;ïqöl¾Åy&úú ѳgæó4l(ÄÉ“Eÿû*ÃvmxýzZ’à€€°L»È[òÙ‘I„™Ù½{w&Í"É™—2„ Zµj4l؃¦7Ž_ý•àà`LMs*>UpÊÕ­oo¥}vL ¡ØÐ„+¿S¤¤²ï¹øzÜ8z¬\ÉÒjÕøñìY,ç̓•+IkôÒ¦ ¬YS¨’{Eá›Sß0óÄL:8vàÏwþDÿ›yZËÚ‚-HLL¤««+‡Ïœ)–;!W._fW·n|ÌŒŠéwè’<˜êñõõ…»w•Ÿ©{÷Òoñç…JU«ò»±1¦÷î1 ‹7VýMMÙV½:z÷îAÞßl˜˜€“jgg:ÅöÐPt€múú$´hÁ]]ÅûûäIþާ£Õªñ‹‘¶¾¾ôQgîü(€ÆÆl16F?,—¢Ü©T« BƒÊ£aC¥û¦‘‘ÆégNžälÿþ´Ù¹SIŒˆ€µkáÿƒ72ïðê«JxLjJg?ÀËË‹=?ÿ¬üîò¸Ö>#GÒøÀÅÜz÷ËÒæÌ±c ®ó¢³âÑ#>¾s€^66ìmØ0×p‰äE¦\ém¡e_¢äVÆnÖ¬YÂÒÒRøûû !”o¡5kÖ,‘o\©ÁùežgÏo ˆdT¢+‡„½½¶Ê?áááÂÕÎNÌþøãôÁ“'…pvÎìåóð")©TlÚtmSšçÙe¹‹ âóÏÓí±·âêÕLû<þ¼XmÙ®¸bd»vùÛ!9Yñž<)Äš5BL›&Ä AB¼ö𽝉 ú¦xˆ3yŸAü–“ÇÖÈHˆúõ…èÓGˆÉ“…øùg!ŽâÁņÖ¯\)¶ 5ˆ~..")ãïîÙ3!._bëV!æÎÂÍMˆÖ­…°µÕx΄vV;wƒX­ÉF!Ú·bÜ8!V­R¼Ã…ú= iÙR iÙRó‹žžB œÝoj*ÄèÑB\¹"ÂÃÃEï*U„äúèmm-«TI?†J%Ä{ï \(»_.EE Ó''NˆêçΉЄm›$‘h•r£[´È ) ›5k&ìíí…©©©„½½½°··k×®M›!\]]EÅŠÅÀE5D½zõăŠÝžÔÌÖ2Â1thÚ‡îRó¯Ñ¿¿¶*K<<²ßº‰QD«®nº¨hÖ,›p-nΜ†ß f#ì؉»!w1”jCõêBܾ]¢6¡Ô…®^¡BñÕ}~úTˆsç„X¿^ˆY³„1BˆV­Äo¦¦b{ñ— ¢¯J%]\”PÏ>SB7ŽSB92ˆäÜHJJ}ëÖ›2TÞÈááBüû¯6(v&D³fbµ‘‘ØÅÎ~::"¡ysEh.^,ÄÑ£E®sž•Óžžy×} bÞ`±³3ŸV­ªe‹$’²E™Õ-eé.%Ê\'Â3g`Ú4eÛÑQ©G«Rñ÷ߤ•MnÛ¶dMðõõ%).n¥B½zÊû±d‰R89–.Ujûjhþ¿ßþGöuèô]'FE§ï:Qåõ*Ì\ªÔzn«ïÌÎ!éâyÀØ»7ßâùæÍ›ÅviÚ@OO¯ØÅsRR¾»èúúúåR<XZZâÔ£éèð—ŽN=zHñœÜÖ–e¦‰ç·mm_*ñ\Þ×–’ $Ö–2§YÊ R@—=""""[ûp­ ƒ+1•°};XYpú´2EO/çÒÅÅòåË‰ŽŽ.Ù“ä¥ÕñµkСƒ2æç:)m–£¢øbö|qð ü;ûùj$Ô‚ÈW#yÚó)"QP÷¸­ID×'åƒÊݶlɱ¶&<<<ŠùâÊ?ÑÑÑ,_¾\Ûf”)&ÌË766|ccĹsµmN™"§µå³gL¾wG##~+ãÍ¡Š¹¶dG®-™‰‹‹#""‚GiÛ”2 á(ÜÝÝ9þ<;w¦k×®tíÚU{Æ$'C—.pì˜òü§Ÿ”Îd)¸ºÂٳЬ\¸ %µðóÏJxKŠp¦jUz™šôÀ—ä š=[†j¨É¶ŒË–)Ýú$’àëqã˜%@žD$%ÑäâEüãâÐW©8Ó¤ -Ì͵m–DR¦8|ø0‡æÈ‘#´hÑB†pä‚Ð¥@™Š%š5Kiç 0dlÞœöR|ÜÚšþÀÉ ÎaºØ Ö©*ų¤T°´´”â9üððašxîck+ųD")2R@—ZÈø†WB8LL`Ç05Í4%µ|”Ž€.3I„y1r$øøpÒÁšÁÀæ¦mâæÚÕŠt:™è“™è£™BR„¡$ŒkË¿QQ|q÷.ðrÆ=gD®-Ù‘k‹f´®YÊR@—~~~ܼyS;vIIJÒ`ê¹W®„úõ³MKÐÎÎ`o_òf•©$Â<5×ãcW¶v…:Ù½Ðj`…9h ¯Ö}µHç’‰>Ù‘‰>š9sæ g2~ó•0ÒÝž”Ä`…À@G‡mõëcù×{–kKväÚ’™nÞ¼‰_jIŽÈèRÀÝÝk×®1pà@\]]q-íøˆÏ?‡Å‹•íQ£”¹,66®Žøí·Ò5±¬¢jV^XÉÌ3 ÇÀ®*,8”9ú=ß ìÃ9³á UªTÑžÑÉKLXX.]ºÐ¯S'ž ξÇÁÎÎL¡I®¤~)ß¾}; 6”1йðò~/e6lÈ—_~Yú'Þ½;]<7n¬T…Ѐ··"žAÆ?§rÜï8OàzðueÀ ¹6â±ÏcØ<å§ ‹â}^lϘ<|²Ï‰™¶p!!~ȶ={÷ó33úÚÚJñ,‘äƒT'Ÿ ÷ÉÂñ"s÷.¼û®²ma¡Ä=iœšñ.pI7P)ëøGøÓ[Þ\÷fšxv±uáðˆÃx-÷âÀ‚`ïÈ/ºJyº_tU`ïÈÞïö2öƒ±¹Z"‘” aaa¾qáìLxŸ>°c5Œøõ%Ž{–H$%ƒÐ¥D©äÇÇÃÀ©<ÿýwprÊqzj¨]»ä̓²—Dø<ñ9ÓOÇe¹ »nìÀÜМEquÌUº8u E³x_½Ç‘:.$Gê¸à}õ-šµ(;ä7ÿìÈDÍÈ$ÂÌL[¸€^½àÑ#¨YUP¿T®üRÇ=gD®-Ù‘k‹fdaÞH]J•î LJ+W”íÏ>ƒ>}ržênÓ¦ôú~”¥$ÂM×6Q÷ǺÌ;=xu<*T¼×ä=î|r‡Ï[޾NæN‚*•Š¡S§2°B†NŠªß4™è“™è£™D˜Ž_p0›¯]C89)¡k±±0p ÛV®Ô¶ie¹¶dG®-š)uÍR‘I„¥€»»;aaaÌ™3Jö„6À;ï(Û¯¿'O*½¹sàÁ¨^]Ù^°&O.YóÊ—Ÿ\fü¡ñœ}p6m¬uÕÖ,ë¶Œf•›åº¯‚wú÷gýÎÅ* %IþIHNfÅãÇ|9cñ®®Ùî´ÕXº”ËkÖ`mm­% %’òC`` Ìœ9kkk™D˜ Ò]Jâé鉿¿ÉžÈÇ>úHÙ¶µ…­[sÏðrÆ??yÊèý£i¾ºyšx®lV™õ}×söý³yŠgP¼Ðk·o—âY"ÑØL½ øôÊâ5†©ôêÅ´… Kß@‰¤âïï§§'Ú6¥Ì#ÃJ‰W^y…‰'–ìIž?‡”Ÿ::°q#ä#ókýÓÚNÃÔÀ4#dFWW·$ÿÂßÎ IDATÌ”H$¹p*"‚IwïráÙ3e`Ç%çC¢V-þñÑ#P§·;ºß³§ôB#×MȵE32‰0o¤€.%J< Õ*Ø´IÙîÔ fÍÊ×n‘‘p=¥Ìqi×.®$§OŸÒupWêt­Cûiíi4¬¯tx…ß6*Ý`î…ߣϖ>t^ߟ§>Ô³­ÇŸ#þdï½8Yå\DÈDŸìÈDͼ,I„ |tû6 /\HkŒRAW—5jàöÏ?èáøÃió™3©¾xqÚóûö±{Ë"""´|%ÚE®-Ù‘k‹fdaÞÈ$ÂRÀÝÝ ÐÁøjµšÄÄÄœ'xyAûö~åÊè^¹+æë؇A÷îéÛ]»ÊD­qóÖMº¿ß¿æ~`™áæ×ÍqT9r«Ñ-âÕÊ·i C f·ŸÍ¸ãÐÓ‘L‰¶ñõõÅÙÙYãkÏÕj=xÀ¢ˆNñ(ëªT¼ëàÀœš5©d`Pš¦J$/ EÕ-/RA”Î;ÇèÞ½yÃTCˆAr2AR§€Ÿ§OÇ5ŸâÒÃ7tt”‚å‰äädF|2¿¶~µ?Œ ¢FqõìU¸ :Î:¼ßä}¾}ó[ìLì´b¯D"ÉNŸ‘#Yôõ×t}ë­´1µ¬yò„Yþþ&$¤÷°±a~­ZÔ¯PA¦J$IR@—\]]iR§‹ÿý“æÄ£ªWÇulÁ:á¥6PiØÌÍ‹df©ãééÉMÓ›ÙÅsFZƒéSNÎ?Ék•^+5Û$IÞ>z”G3}Ù²4} 4”)÷îáóüyÚ¼¦ff,rr¢½¥eN‡’H$’REÆ@—W®\ÁÃãÐñŠc<Ù‘‰>š)ÏI„‡ÅÏÙ ïÚ•ñ‹s2%ÉÏJOENNÜlÑ‚aöö´ÒzYêrZ–kKväÚ’úô郱±±¶M)óÈ$ÂR ¸‚ñÏxzr®&‡…e_hnNë½{ , ,€)S”퇡J•"™WêìÛ¿’ðJB®óm„×YÓR")+$ K÷îÜ›8 •Á;ѯU‹ñ½{3½F ¬òh%‘HJ™D˜7Ò]Žpmßž+Y¼ÐÏ+..Ïž@èèXþÄ3€SS'¸ $ç<Çä¶ ƒº *5›$IÎüͧ¾¾Ø-[ƽïs={R÷Ø199Iñ,‘HÊzT;†J$Iº”(®®>®®®\Q«yNŠ÷ÙÉ©PÞç7 5D[º°‰>[½·ÒqmGžÆ<…Z0tÄP^9õ ¶—lQùª0¾nLÕ“Ui4’c;¡R4‚R»ÈDŸìÈDÍ”Õ$Â$!8Êoo*Ÿ;Ç__®¤Ä$«.]BïÕW3{ŸSïÚ•éË–ùü2‰P3rmÉŽ\[4#;æÐ¥D±uõ9}šÕj–ËMMùxÁ‚B&cÞ‘¶ta}æžÇÐC‰KŠCOGÿõü›&lâêñ«œ˜w‚5o­aÏè=xm÷â—¥¿`d”[»²‰LôÉŽLôÑLYK"ôyþœÉwïRíÜ9z]»ÆÎ§OIHVb¬œŒ™S³&õ'©GÍ04ÄÏÙ¹È^h™D¨¹¶dG®-š‘óF&–ÅŒÿùç°x1CU*hÒ„Í—.ê0#GÂúõ`m !!PÖ´‰É‰ŒÞ?šß½~ÀÜМí·ÓÙ©³v “H^r"’’ØÌÊôš©®.ììx×Á¶––üyô(ƒ×­#*' Oà ¸*C9$­!“óFfj”7àãW_…ï¿/ôaRVmÚ”}ñN¿­ýðô÷ †E þþõíêk×0‰Dˤ†oØÚÚÛ1÷:@ïnÝrœ“,GÃÃù-0½!!Ä%§gòª€7,-qwp` tuÓ^‹|öŒ¡ÕªÁÕ«¹Ú`Ô²%áááXYYíb$‰¤„ºSRˆ×,\XlÇœ™r;[“€¾Ãï¬ âQ–ÉFF¸98àfoO­jÈî×Áýú›­‰D¢-d t)Q,ù)Þg {{( ñÏw¢ÏÙgiõK«4ñ<à•xº{¾ðâY&údG&úd'$$„#>>ññ)¶DÂ}‡áç₟‹Kš':*)‰_ž<¡Í•+Ô=žïÒij‰®.#ìí9Ö¨~­Zñµ£cŽâ¹4‘I„š‘kKväÚ¢™D˜7R@—§Nâ£>â@F\PR÷ur‚Bv4„tmdÍšÞœ¢’[¢Ïæë›ys후Ä(Â`J›)l¸ c=í8—42Ñ';2Ñ';SæÏça­Z<¬U+Í]TfþôQ]»Õµ+Ÿ-[Æ;7nPéÜ9FݺÅß‘‘ió^·°`uݺúè#N:¥mSÊ<2‰°pwwG­V³zõjôôôÐ+L“€¨(°µ…ÄD˜0~ø¡Ðö4i^^ж-”Åÿ‘oN}ÃÌ3Ð×ÑgeÏ•¼ßä}-[%‘”BBBh:j&Pé‡ؼt)&ÖÖ$$'“ ñÉÉiÛYÆk¿yò$G®_'¡woå${ö(–š7 Š¡!#ííqwp Ž‰‰¶.]"‘” III$%%1jÔ(tuuea.ÈèRBWW·h%ÕŽQÄ3)|#**=G›á \¿~*W®¬Œ©µëþ[€¥‘%;íàÍšojÏP‰¤Œ•”Ä€Ù³ HºÀ“·ß¦ýôéðÁ…?ðúõ0}zúóž=Ñùö[öèÁ»¼ee…NYÏ8–H$E"Õɧ›!ùW¢) Ë ©áffðÆ…>̹sš0¯ ýøñc†<ÿh¢Ì¢0Œ3Ä4Ú”QCFqÀä§î+.ñš–5ùcøÔ³­WúFJ$eŒ¨¤$ö…†²ýéSûù‘pï >¡fM¥3Rd$XXèØz*º/’Ф "ãÝ1==*4kưû÷éòÊ+Åt%‰Dòb t)Q¤€üäd8xPÙîÜ2´ò.(©ñÏ::ðúë…7©0ܼu“®ïuå~«û`„–€|yìK’ƒ’¡3´®Úš½C÷bgbWº–nÞ¼‰KbÜ_D’’’ð÷÷ÇÙÙYÛ¦š‚–œ{¦V³/$DÍaaħ~óݶ  R¶Sã’-,`Ð :?Τٳ1P©0ÐÑIûi˜åyÆŸ:*M-Â+%$“ ]º0kéÒ\KÚ•E|}}qtt,\¸Ü Œ\[²ó"¬-%A||<†º…JÒ‘I„¥D‘ºúœ?OŸ*ÛE߀tÝ XZéPB­V3tìPî·KÏçe3¹Q2XÀë1¯sÜíøK+ž>3FÛ&”9^„DŸ©óç35d¿gj5›‚‚èsý:ÏžeÄì IÏ–ÏŸc ŽŽÊ×®)GGn=|Hµšv––´67§©™ +T Ž‰ ŽFFT64ÄV_s==ŒttÐQ©Øwè÷\\@“ØÔÓã^†Šå™D¨™D˜am) d'¼‘º”¨^½záwN ßÐÑîÝ }˜ÄDø÷_e»´Ã7ŽŸ8ÎmËÛñ mW cXx ˆ»‡‘^ùk¿]\xûøpÁÇom›R¦°´´dÞ¼yÚ6£Ð„„„pÔ×—£¾¾ÙJÎE«Õl¦oŠhž"šS›“XêéáîàÀ Ò÷äI¢û÷OßÙÕ5Ó?óýÞ½ùrÁ‚Ù6kÅ ¢ºtÉñõ¨.]˜µbEŽ©m~øá,KÓCPN aÙ±´´ä‡"$忨I³¼$Èû[åTݼ9T¬XèÃ\º±±Êvi ècgc“û$D$FœœŒŽÎËùÝn·ßãáÁ„o¿åØÆÚ6§ÌpëÖ-¦|ó {6lж)…bêüùôê•¶½Äá¡l æPXX¦N~zz¼mkË ;;Þ²²Â@G‡Þûç¨Sr©[{àŸ ÉW¨ÈÉ“'yrïN?ý”ë¼'÷îqòäIÚµk—«•H$’) Ë:Àÿ)Ûž¥' ÕBÍéû§ùóöŸ`“÷üd’_ZííãÃ5==¨Y“kzzxûøP_&o0aÞ<.†‡sëÖ-êÖ­«ms Dª÷YôèÀ¦]»Øpð qff™æYèéÑÛÆ†A+Ò9E4g$((ˆ{÷F„‡çz>UïÞåK@·k׎@oï^‘D"‘H¤€.% DøÇéÛÅ$ «W‡jÕò·ODD§NŸbýÖõ´nÙš7ßx“FåºO‚:c÷ޱëÆ.öÝÚÇÓ˜§ ‘YDgH"LÅBÏâ¥Mü™8oÁ@@Á}û2qÞ<Ž–SkqrëÖ-¼ÔjB{ô`¼y^»VÛ&å›;±±¸ÍœÉýï3@L¿~°u+|ðæYD³a._ëׯÏ×õëg+hbâË‚L"ÔŒL"ÌŽL"ÔŒL"Ì›—ÏÍWîܹÚ5kزe QQQÚ·Ðù©áU«BãÆ…;F gÏ*?óë}^»i-M{7eІAì8}œÏwO§ÓôNtÔ•àààLsŸ'>g‡Ï†í†ÝB;zlêÁš+kñ èÕң½  Î°S†$B³;fŒì3²ðX޹pý:ç@ÉìÜ´ ,-9#[/\жiZg¼yuéÇóŸܺuKÛ&åHlr2‡ÂÂçµÿý—:GŽpÎÏjÕJŸT«""X_¹2Á¯¿Îúzõèec“«xΉ3gÎp&ã­% “sB&fG&jF&æìD˜O"##iÓ¦  ..ŽuëÖqäÈ4hç¾îîî@!8bcÁÆFùùÑG°reÁ OáæM¨—RRyåJåp¹±nó:&®›HxËpð6‚ºãáðVèq"¡Áå;pŒ?ýþd×]¹{„ؤØLÇ0Ò3¢³SgúÕëG¯:½¸zá*C¿J`Û@ÈR‰Ïà­ƒ[ã¹Ï³Ð×X‰MNfå£GL3†„>È\%"V¯¦Õ7ßðI•* ¬Xý—¬‘…ÏÍ›¸ÎKxjƒ°0šïÜÉ™õë³…8h‹»±± ã`h(žÄfŒgþåèØ1³€T÷îñþ¬^¸°”­•H$’¼)´ny‰÷·ò‰¹¹9×®]C•"`ìììX¾|9«V­*¹“þõWzÖ_)Æ?‡‡‡3sùLÂß ø;ÀØ® RÏRh‡wmo*÷¯L²kæä'33º×îN¿zýè^»;¦¦i¯µ£=û—ìçýÉïdDˆy& &X…XÑ»eo¾_ñ}‘®±<‘œÌ/Ožðm@oßVêøf­`i ––üsíÿDE1éî]>ª\™+WƾµÀËW¢£ÙÄOS¦Ÿ²`mÍ…äd,6o¦}Æt¶¶æ-++T¨P¨ó\¿~=__‚3—œÌɈˆ4Ñ|'66ÛZ ÁµÈH³ˆgQ«G÷ïÏw²ŸD"‘HÊR@çUÏߣG°²²*Ù“¦†o+^¬"* ­¬ Ke6Žþu”‡5*qËÞFÐmˆ"ž;vQ¼Ð¯ø#ª ÄEåæ…± ½ëö¦_½~¼åô†º9ÇM5kÚ ¯¿¼¸{÷.§NŸ¢¶sm7nŒY–„ªÂ²aÓ&ú÷틱±q±¯¸I‚µ|sÿ>÷ãâ”ÁmÛ`ôhÍ; „þêÕ$NšÄ“„fùûóm@ƒìì_µ*Í‹é}+ Äű)8˜õAAø<®$К˜(´4ˆ¸_åp•*  ’o¥ˆéNVV8äã Æ®ýûùrÎnç#Læ^Š—ùPX'""ˆQ«³Í©ndD7kkºY[󦕟~ù%§ûöÍùz{õbêüùÒ -‘H$å)  Á_ýÅîÝ»¹téR¾÷)Taª€~óMEDTýúëŠοÎý…Ú^ ɤxŸ;+/¨TÐc$DøÂk&7ógÍ É ¬Û=üÿ)©T*œ>z4Ë,(6ñˤùó¹óè_Ož\,Ç,.’…`sp0³ýýñÍà±t &ÄΎȌ헔LO ¬*Vävš˜àMBr2‚‚ØD+só|…wlÞ²…¡C†”ÔåeB­VsîÜ9\ól™”ÄŽ§OYÄ©ˆ2Æ“©¶oG¼ûnêA!0ªT++L,,° & ¥¬ã“„Ö².0€WMMyËÊŠ·¬¬xÃÒc á_¯\I«+»öï§_†$?€ø ^æCaaÜŠÉ^‚Q_¥ÂÕ‚n66t·¶¦~/xHHþýѰ¡òûÔ€ÐÓãÀ¿ÿÉ -“5#“5#“³#“5#“óæ…[]Ù³gW®\ÁÛÛ›øøx>¬qî… ˜;w.^^^Ô«W‰'ÒµkWüüüè–Ò¾¶oß¾|÷Ýwœ>}š±cÇrôèQ¬­­óm—ŸŸ7oÞÌ4VµjULMM5ïàå)ÛE ßxòîÞU¶ó“@XÕ¡*ø û°ÌŠ»]{˜í ½gûŽÂ`RÄZ˜™ÑÜÌŒæææ473Ã2®ËW®põáC>73»vöÒ2á±|9¡îîl8r„/Ç+^hìzú”™þþŠW5'ccf;:òë?bnŽÕž=i¯=?r„ ;§=‚K—reçNNGF²ìáCö„„$ÿDEåÞqõÚ5>˜0úõëójÆ%~Ík6ndî÷ßãwù2ºººÙ^O‚C¡¡l bhh¶È +T Ëó笵´äiª÷96vï†qãˆéߟWöîåäªU ãhx8…‡ž”ÀÕèh®FGóýƒêèàjaÁ[VVt¶¶¦±©)»÷ïÇ¿~}¢ºtáëÅ‹é׫þqq åPXÇsð2W14¤›µ5Ýmlède…™†ëxüø1Ãßz qÿ~®ï•ê­·xüøq¡pjaŸ>} µÿ‹ÊòåË™={¶l¦’Óš…Ô$BÙL%3AAA²™J¼pI„T©RT*ÞÞÞhºD///Ú¶mK—.]6lþù'¿þú+û÷ïOÑY9þ<ÇçÀªEëîîÎÖ­×03˜6ïÃÚµýrþà›;fÌP¶ˆgå£Güüä ¡‰‰iã::ÙÂ;Þ6Œã]»ÒñðaþÚ´©°—š/Ôj5 ºw' U+–891zdzU•sQQl bkpp&›A ÁfoÏ;öö425¥›»;‡ûôɵ߼Ão¿á9mZÚÿb²\|öŒ#áá ç\d$‰þ÷íôõIœ1ƒˆiÓ@Oƒýû±«R…G¯½–m®žJÅëtO Íx5§/¼‰Dò!“óæ…ó@ÛÙÙE… ððð`êÔ©ç-Z´ˆJ•*±eËôôôèׯwîÜaîܹô³gÏèÒ¥ mÛ¶å·ß~ V­ZŒÎ)v5 qq ‰‹û2ÃÈžçéáIÎ ??þÉPêÐÁÀ€i5j0ºR¥LåÊÒ#\ÍÐyµj1ÓÑ‘MAA,{ôˆÿ4„w¼…·‘T¯Ž·‘W¯]+Q/ôš xýubÞxƒ%´ë×Í!!l ân–d;S]]úÚÚòŽƒoZZ¢“ò·Á³Zå!öÕ‰‰üë¯4­£RÑÂÜœææ|U£Ñj5'""8Æ‘ðð´0Œ§§OC£Fr—$¡[7Í™)º’Ýllè–Sm!Ã$‰D’…î“AWW— ydä !عs'o¿ýv¦¹Ö­[3oÞ<ž¾@ïó—K–ð|áB¾\²„3­[s>6–™÷ïs2""Í>cc¦8:2®JŒutˆ‹‹#¹¿Ë¬c$$ànoÏ{•*q*"‚=b÷ãǨõô”ðŽùó•š… õîÍЯ¿fêO?¡’âãÑO9žJlzR|<úúúèèè R©Ð3¼/*”; I d˜§bbøvíZb¾ú T*n·j…ËܹХKÚïMW¥¢£‰ #«V¥oÅŠTH Èxm–––œ9p È÷&ô²±¡—Ò½Ç72Ïèh&Ï™£xŸ3ü é6kÆà{÷˜èÝ;óX“&pãÄǾd ×îÜápX¿>yÂ7÷ï3~ÂÞþóOš^ºD¥¿ÿÆðÔ)l‡åµ½{éïíÍ'wîà@÷Ñ£Yù27cbˆJ‰¯8q"ë6n䮣#XXp»F ªwíJ»?þHÏzz4_»–£¶¶L®V--|%?¿·›7oêwþ†¥%Ûë×gȶmŒNNÆòÁ¥Ž¸……ò¾DGãcdÄ;2âÆ ÜÇcø±c õña°ƒ¼½6vìÿۻ󰦮ôàß°#P¶Xe#‚"E¡bkÝQÑÖÑJ«¸µZº´Ói­v”:¶µj‹cÕªUkëRZõWÅ +Š¢"ÜÙU} „œß1Ë&!‰ð~žÇ§ÉÉͽç¦É›—›óžƒI'OâÍ›7119¡Éɘ4oÆŸ8±B!B„BŒ 1þý÷1êøqŒ¸qÓ’0,) cÆC†@ O–%C†»v™™ð55Å×®®È8.۷ÿ´Tž<·äÜÄb1RSSÛôYXÿÏ¢êÈHú÷—_}–½_jGFòÁƒð15mÓ1ÔÝ–ŸŸüü|­è‹6µ¥¦¦B,kE_´©MV‡£ }Ñ–6Ylц¾h²mÑ¢EX´h1kÖ,ÄÅÅ4­Ã®O6„£á)&%%ÁÇÇ¿þú+&Mª—|ãÆ ôíÛQQQ?~¼ÊúŽ]»2œ©×…ß~k¤øç­·€C‡¤‰fn.І!'O²z´£G1cšÞ¾šI`µåK”—ê`3­À;.QUUCCC¥éü‚‚‚/„ä û 33iò˜š¢ËÎxwÒ$8yyÁǃŽ ut`Àã)þWG†õn³ª*L˜6 Ÿ|¢t,ÝÄD¼^P¯ðpd‹DȉS]‘H©p­¥LuuaohˆÜeËPúÑGÒKú"°n°|9Ltuí¢SÉ IDAT±°{w|ìàËçüé?<<\%ãÎ^{ûmÄL™"M eŠ‹-[€þ³ÍûW ‘HXµJáWý˜,íÖ +çÌiÓî‹ŠŠ°råÊ6úô=7–,©K ëyéØ1ü8x°ÒŒÚ,êYñ)*Z´hrPUléHT[:š×_ŽŽŽô~iB‡ÂѲ! í²ûÍ y>¦Ø>û׈êj :Zz{ôè6%Ï@ÝøgO:…]sF]8ˆòÞ}ª’°g¤ê­„ë×qÛÔTš<uÃ8 UáÎîÝØòl,tK­ˆŒDγ±Ï Õúø mýzü¾t©ÒŒ55È©®VH¬Þ~\]†]–ÕÖ"åâEÀÍMš<€¡!xÞÌÎÆ÷ûºêë·êREÀº!â¶±±bò ææàÛÚâGSSôòôc HÇ­3ÆäcØe÷{LRÿ>cø}ÿ~l:U þÀª Æ/X>sf›~´°°hóÜág3op%ÏfäxQPâÌ’!n” )SEléHòòò——ת cU§L ííí·nÝRh … «V€h2þë/ ´Tz»Ó×u ´§§òz mKOÄqW€ay*b†Noóñë[Çï¼Ãý ™n›š"áúõÏÈQYY‰}çÎAüñÇn“>l"6oVš‘ÃZ_Öúúðjâ¥ÆÇ‘Xo‹ˆ@ÁâÅ Û²q㉮aa-ê{{[¼v-MžÌùX~h(¾ùúk•ÍÈQ[[‹Y‡£êÓO•äñ1h¶ïÝ«0#‡&|¶nJ€?cÖwßÜ¿=Š *øìBÈ‹&==—.]B^^^«¦êíŒ:eÝ¥KôîÝ7oÞTh …°´´DOY1›Jy@:ö¹²Ù7 êÆ^<'±¸tIz»¹©ë²«ÊñAj& gˆË°³WOX©îçO¥«ÏÎÈÑœ ‘‘((/‡M3‰à–3gžk^h}††p¨7™ü©³g±U ¨»ú,chˆûÎÎ8uö¬ÂŒšpC(”μÑð곌¹¹JgäÍ¼ÑØ =²9f……i¬(¥¨¨¢FöÇicúõƒÎB:©€€ 11QÓ]Ñz2‰D`ŒAü¬èKV]ª¯¯/ÿï½÷°téR\½z~~~HOOÇï¿ÿŽiÓ¦µÓê;ª }É›xÙe ôàÁM&›-qý: [@­©š÷'jô¤‹9„à.¦¸~Ô¦c7t!>½JK!زEÞVYV#…qÕ†Œ¡°°°EË¤Ï ÃÛ'6ð¥Kg®hƒÏ¾ý…óæq>V8j>ûöÛ6'Ðm]-ìÃeËPee¥°8KCUŒáÃeËóûïÏ}@:‹Ìªo¾AÅĉu?wpx`hˆûöaλï>×qÚºZ˜……–¤Ú÷´6 •¹ÑJ„Üh%Be´¡"±X ±XŒŠŠ téÒEÓÝÑj2ºôêÕ ë­&»ò‰¹sçæÏŸøøxÀÃÃ)))4hV¯^ÝN½: Àgÿ²tSÜäκ%Uðòùóu·›J ?žÇžô زðŽk|HÄóúûûïãï￯ÐÖÖBUt­ÅN=‹û=z(_}–14Äý=Ú|º­«…múâ‹/ÎÒVeeeؼbEóöéÓ¦¡Q´Z7Z‰­DÈV"TF±EÑñãÇqôèQ\¸pÆ Ótw´Z‡œ…ãúõë‰DJí... s73Æ  ‘œœ 777øúú¶Ë‹ððpÄÇ ñðá$TT66@X0wî›pss«Ûpýz@6V75hãP’‰¥«;83óÎ>Fp¢Œ§ ^e.öõB€}ÛVì¨ú½ú*›šB§ÁRÙõIª«ñrYêÿõB!„¼@h%ÂæuÈ+о-,Bãñxðöö†··w;÷ð÷÷ÂÔ©ŸbùràÑ#é´ÅõsguÃ7‚6'Ïpá‚ô¿]}.­­EÈõË`:f€¤šQòÜJŠ !„tÒ…T4A(¢º:FFÒŸ\ÿûßÕe¼*¾‘’<~,½ÝX=þÚY”èHÇY÷|z_.àÞB!^ll,"""ä³’‘ÆQ­&¶¶¶˜:5aaÒŽÿû?éByrÇK§ÍT:}À@—qg+¤?@è=½„“o,€O½oÙ*PD‘lµ0R§þja¤Žl%B¢ˆb 7Š-Ê(¶( …­mÓí”@«Mee%–.åCGGºR÷W_ÕÛ@6|àlóñd ´¹9Чâc÷*+±è~ºôŽè1¾r²A‹m>fkmÞ¼eeej?®¶‹ˆˆÐt´Ž¬Ð‡(Š•’:[¸QlQF±EŸÏ‡@ @ee¥¦»¢õ:d¡¶i8Ò$ààAédéé€m×Zé²ÝOŸS¦?ÿÜæcº»÷î£FI¯vËTK$p¿x Å$|z±¿oóñ!„Ò1Paóè ´š¤¥¥aÿþýHNN†lá<‘Ø´ @\œ4yT2|ãÑ#iò (ßøàvâ³ä0É9„Ã#V¶ùx„Byñ%''cÿþýHKKÓtW´%Ðjb`` á•WêÛÈH æ·gÃ7tu¥—ŒÛHV‹¯¾ZwûX~>¶?)–Þ)¾=ýFàe“—A!„bdd 41]+‘¢ZMø|>FŽ)_íH6Ýsa!P²ïY=p  ‚µçeC" €¤·s««19ù: .ÅÝT„ ƵùXmA…>ܨÐGúp£"Bn[¸QlQF±E‘««+FŽI«›¶%ÐjòèÑ#…ûcǽzÎH‡uÞMi£ †ou+úùFF€„1LHº‚rèl²vâ‡ákTr¬¶ BnT裌 }¸Q!7Š-Ü(¶(£ØÂ­aÎB”Q¡46ëV@øþflÂߥ ÉÉ€§g›ŽU^.ÈC,>ùøòKà?éiX–.]Úœ—ó;οúÚ>Ó!„B:*"l]V“[·naÆ ¸té’¼mÚ4`¢tøF®¡s›“g¸t©n:é  àRI þž.m(€lºPòL!„%—.]† pëÖ-MwEëuÈ¥¼µ‘­­-† ¢09¹Qm9Kb‡D!ð:·í8²_sy<À+@ŒA7 ˆ xò+þóîm;!„B:$gggáÌ™3šîŠÖ£+ÐjbbbÅÕ}Nž„®X8Šå彟ƒ,îÝøøÑ-ä>»­÷ Ç|]í©¬¥BnT裌 }¸Q!7Š-Ü(¶(£Ø¢ÈÖÖ>>>011ÑtW´%ÐjÂ9 ÿÙêƒ"}SÄ`ˆòòÞ­T[+Âüi¹8”ÿlné'1X×'ž]Û>DD•¨Ð‡ú(£BnTDÈb 7Š-Ê(¶p£"ÂæQ¡pÆg °·òòP>,/þ  0k°mÛóçÚ5éÌp¬€ÞÎxˆyªòðê“]8÷îÿ^Ï„B!6®@kʵk@^ÀdrBC¥Í{öÈ›[-6€¾XyCš<³Z˜¦~ƒ=ãþGÉ3!„BˆŠP­&B¡u?·>¾ ‘/¬"_Þû9ÄÆ˜wèQ%mH߉ÿ/„£¹c›úN!„Ž/66 …šîŠÖ£ZMlmm @ m%Ð~~€ €Àg³ËEFJçsn­Ó•ùÀ„lé¢ëø›Y ¦zMm{çÛ úp£BeTèʹQláF±EÅE¡¡¡ŠN”@«Iee%tyÌÜ\ !Aú@½Õ?þXúßÂB`ûöÖíÿÂ] ß6ocM1l2·aKH¤ zÞ~¨Ð‡ú(£BnTDÈb 7Š-Ê(¶(âóù¨¬¬ÔtW´ªÒ`ümÛ€9s¤·¯]úõH$€@Ü»ôè!ý¯®nóû—0I¸kTà%ÿ ÇG­ÀðžÃU|&„Bé計°ytZdÃ7ìíåÉ3èèK–Ho§¥‡5¾‹ŒŒ dddþ“‘!Ož‘}ózøRòL!„ÒN(V7‘8uJz{Ì¥‡§Oºv•Þnja•ÅkÖ`ñš5ˆ-.ÆÊô4icÙ=˜$ŸÁú*X‘…B!„p¢ZMþ8ñƽ3©Û~¨«¬7þYÆØ˜?_zûêUàÜ9ÅÇÅb1¢¢¢p23Ñ™˜pâ$ @m%üL3Ý c=ãö=¡BnT裌 }¸Q!7Š-Ü(¶(£ØÂM$iº Zh5)4.ć#8ÿå?¥ FFÀopn;¾4‘€õëëÚoo ßý0iå(}wÊÞ†ü_~‘>˜ð-pb& hdzP-*ôáF…>ʨЇr£ØÂb‹2Š-Üh%ÂæQ¡„‡‡cב]€#°õ0§Èõõ]ÂõFŸ3o°e‹tšè›7#£4 Œ¬þYÀý à“ÕÒ 7lÞp¾ÿôîßFI‘‹<ù&„Bi©¨¨(DEE!66AAATDغ­.Ž@ŸAÒäþWœ¦þvY²DZTÈðÕW@Ø‚0d Îî¸ÓÔmøöÛÀÁ@H5Œ^žJÉ3!„BžKhh(vî܉   MwEëéiºIHJÝí=vE8óM0ÌíÌ¡§£==èëè×ÝÖÕ‡ó|=°¶¶VÿÉB!„t”@«‹¸.Ú÷mjp?ñ_Ã=Ñ.[¸QlQF±…›H$‚¡¡¡¦»¡Õh‡šè—YÒÛG݃|ôóê?{?øØú ÏË} à ÐÓ²'lº ‡qßÿ¯ì&¦ÀãÇ“(^}–±±^ê ãR£ê‹” }¸Q¡2*ôáFE„Ü(¶p£Ø¢Œb 7*"lªAxx8bNïBú³:pPœæ‰ä³Éòmª$ì{ô³³‘Ô0ð_7vÍ–.^~™û £ËŠOP,L¥«.„Byn´aó(ÓR“âBC‡%†Àí$sü+l222 kcƒïsr°5'ù55òíutfcƒöÝ:£éúµ@JŠô_#$zfÈÉÉ£££:N‰B!¤S¢ZM*Í^.ÂK"`sRÖéÀ¹WãÏ´4ˆëýÐÝÐó»uÃ;;XëëfÍzŒåË?Ö¬€ï}T;V*î<ÕÝK\±1bjkkÕyZ„B!%Ðj"ruÅ9!>>øï›o"I @’lEBææXؽ;&ðùÐãñžûÑG=ðí·=Ÿ?¶O6ÀÂøGKŠQZ <}h”ÌÀ¹[‹àâ¢æ“j#*ôáF…>ʨЇr£ØÂb‹2Š-ܨˆ°yTD¨."øûã¹s‘ô,€êè`š­-®õïX__LêÚU)y¤«~ðôvƽEظ< wÿ¼‹×ïO’`gõâ%Ïú4† }”Q¡7*"äF±…Åe[¸Qaó¨ˆP ÂÃñ+/xçàÈt™6 ÿôôÄ\{{¼l`Т}ʨЇr£ØÂb‹2Š-ܨˆ°yTD¨áááØuë0uª¼Íú—_ <|vvvÍ>?** &õ‡p4²%~ûMú !„BÈó "ÂæÑh5±IKÿÍÌä÷ysæ ¤¤¤E 4!„BÑ”@«IPp0>˜5KÓÝ „B!mDc ÕDÿٲܤúp£BeTèʹQláF±EÅnTDØB!D]¨ˆ°y4º6lØ€óçÏCOOÇGXXŒŒŒÚý¸vvvØ]ݨ&·«­­Ý‡íÞB!„ÎŒèVpppÀ²eË ‰°råJ”––bÑ¢Eí~ÜW^yqq{Ûý8„B!¤y4ºÞ|óMøúú" S¦LÁÅ‹[ü\¯Œ }¸Q¡2*ôáFE„Ü(¶p£Ø¢Œb 7ÊYšG t+íÛ·K–,Áž={°téÒ?ä+£BnT裌 }¸Q!7Š-Ü(¶(£ØÂr–æuèZ,£ªªªÙmîܹ£ô×VEEbbb£ðW»••ø|>ÊËË[ü×|FFFë;O!„¢!”»4­Ã%Ðeee˜1c¼¼¼`ddccãF·Ý°a,,,лwo˜››ãÿød“’”––bÿþýØ¿?._¾,ÎÈ‘#±téRlذ+W®lïÓ!„B!Z¦Ã%ÐUUUÈÊʨQ£0~üøF·;xð /^Œõë×£¬¬ ?ÿü36n܈¯¾ú `ccƒ-[¶`Ë–-˜>}:$‰ÂÕìÛ·o£oß¾í~>¹sçŽÊdzµvŸb±GUé>ÛêøñãÍþêÐÞûLMMErr²J÷ÙVQQMÏࢎ}ž:uªÙŸÕÛ£Ÿ©ªªÂñãÇ5¾ÏæÎ¹=úÙŠ-Ü(¶p£Ø¢ŒbKçÐáh>Ÿ“'Obݺu0`@£ÛmÚ´ ˜;w.LLL0a¼ùæ›øî»ï ‘H”¶///‡@ ÀСCáëë‹;wâ_ÿúW‹ûUSSó\çÓ˜Žð%WTT¤òBŸŽð%W\\ܪþµÄ‹þ%'‹QTTÔªã5§#|É•”” ¤¤¤UÇkÅn[¸QlQÖb‹ªs–ލC/¤Ï>û O±¦¦†††˜9s&¶mÛ¦´ý½{÷àêꪴ?±XŒôôt¼üòËx饗ZÜ×_qqqðóóSZÒ󮮠††òû-jKHH€¹¹9zöìÙêç6ÖVSS}}}8::¶è¹µµµˆG·nÝÝNVˆàè訴¿#GŽ W¯^prrzî>7l;}ú4üýýaff¦²×%;;ýû÷‡A‹ž›êêjèêê6º]RR|||``` ´¿Ã‡ÃÏϯM}nØvâÄ 2DeûËÈÈ@zz:Üâç^»v xòäI£Û]¹rAAAJÏ-++ÃÙ³gÑ·o_•½.ÕÕÕ8þ<‚‚‚Töºª««Ñ£GçŠW‡ÆÄ‰ÛüÙ¯ß GGGÆ«äädxxxÀØØ¸Õñª¦¦gÏžÅØ±cUöºTWW#11¶¶¶WÅS™úñJ6Þ9??æææ¸~ý:üýýqæÌn2ÎÈÈ€““¾þúk,^¼XÞ~ìØ1„„„àܹs ´¶Ú¶möíÛ§Ô. ü„B!šÂU08uêTÌž=[½y1tÊ…T NNN íÎÎÎ «ÊìÙ³éMH!„ÒAt¸1Ð-ѵkWÒŸ/ê»ÿ¾Âã„B!„4Ô)h[[[èèèàÖ­[ íB¡`oo¯‰nB!„@§L õôô0tèPyÂ,“””www¸¸¸h¨g„B!DÛuÈ:::QQQò+ÌQQQˆŠŠBzzº|›… âÚµkˆˆˆ@NN~üñG>| .T[?322Œ7NåS鼨†>Ÿ@ é®h}ûö¡OŸ>prr˜1cpúôiMwI+DDDÀÕÕÎÎÎ7nâãã5Ý%­òñǃÇã!//OÓ]Ѹªª*èêêÂÙÙYþ¯á ´UBB {{{ú.0zôhùû¤{÷îÐÕÕUy}Ô‹èܹs9r$¼½½1qâDµÎÅ®uXäççÇlll”þíÚµKa»;v0;;;€Y[[³/¾øB­ý|çwØš5kXee%›3g[¾|¹Z¯­âããYbb"ëÕ«—¦»¢5Ξ=ËòòòcŒ>}𹏏0±X¬á^i^vv¶üuØ»w/8p †{¤=.]ºÄÞzë-fnnÎrss5Ý«¬¬d666šî†Öyòä ³³³c`b±˜²êêjMwK«ìÞ½›9RÓÝÐ }ûöeÑÑÑŒ1Æ6mÚÄþö·¿i¸GšÓ!gá¸råJ‹¶›1cf̘§OŸÂÊʪ{¥,66›6m‚‘‘fΜ‰eË–©½ÚhÀ€û¯ZC† Q¸ýøñcäææ¢{÷îšë”¨_¯Ð£G”——k°7ÚC$añâÅ8xð <<<4Ý­Q]]={ö ÿþèÝ»·¦»£¶oßøùùááÇ4„‘ömÛð÷¿ÿ]ÓÝÐ ò¸kgg‡ÊÊJ ÷Hs:dÝZšHž³³³QSS €§§'®\¹‰D9²†¨È–-[Øé“g™üÛ·oGVVþüóOMwG+¬Zµ áááT]ŽŽ†Š»wïâ‡~/L¡§×¹¿ïܹƒÔÔTLš4 ¦¦¦Ð××ÇÑ£Gå ±tv÷îÝÃíÛ·1nÜ8MwE+ìß¿&L€ƒƒJJJpðàAMwIc:wähƒôôt¤¤¤ÀÂÂB¾BXCååå8þ<>|øúúÊÓÕÕEmm­ü¾,qæñxíÞ÷ö’››‹«W¯";;~~~ðóóãÜîÊ•+8uêD"‚ƒƒñÚk¯©¹§êUQQ¤¤$Ãßß¿Ñ?Øîܹƒ .€ÏçcðàÁ°´´T򾯱cˆŒŒìc  pãÆ ˆD"Œ9’s‰D‚Ë—/#11½{÷Æ Aƒ”¾ØCCCѯ_?ìÞ½|ðΞ=«Žî·‹’’$$$ %%¶¶¶~i?|øGŽAVV|}}1qâDù*§‰‰‰¸|ù2¾øâ uv½]©"¶àÀòûÇÇ‘#G0a„vï{QEl±°°€±±±ü—Û±cÇâĉ;v¬ZÎAÕª««! å±eîܹœÛ•••áðáÃHNN†››BCC9§°Ý±cÂÂÂ:ÄmÍ[`ÅŠ˜0aÆŒƒ­[·bÓ¦MX¿~½:º¯}4=†äEÍø|>À°àà`Îí222˜@ `–––¬ÿþLWW—-X°€I$ù6...,??Ÿ1ÆXll,>|¸:N¡],^¼˜`<`+V¬àÜnÇŽLOO1‚…††2}}}öùçŸ+mwûöí1z̘1LWWWþ~9{ö,çvëÖ­c:::ÌÛÛ›ÙØØ0;;;–””¤°ÍŸþÉ<<ëÛ·/ cVVVlÔ¨Q¬²²’1ÆØþýûY¯^½äÿttt˜««+»}û¶ÏFuT[dæÏŸÏÖ­[×N½nªŠ-‡f3gΔß_ºti“¯›6{üø1300†‹-ÌÏÏuëÖ½ûî»ÌÕÕ•¹¸¸°´´4…íÄb1³³³cB¡P ½o?ªÊ[rss™……«­­eŒ1–““ÃLLLä÷;J [)!!}ûí·ìܹs,88¸Ñ7bHHëÝ»7+..fŒ1vüøqÆãñØÁƒåÛL›6}öÙg¬°°M:µÑ/†Á¹sçXLL +))a†††œçR\\ÌLLLØ'Ÿ|"oûþûïÇc)))ò¶„„¶ÿ~æääÄâââXzzº:N¡]|ûí·ì—_~aûöíkôKîêÕ« Ûºu+cŒ±êêj6dÈÖ§Où6'Nœ`ìòåË,77—åææ¾°…>ÅÅÅì‹/¾`ÇŽcüq£_rëׯg]ºta‰‰‰Œ1Æ233™ [°`|›óçÏ3‘HÄÊÊÊXdd$ TË9´‡»wï²#GްÜÜ\6bĈFcKPP d555Œ1Æ’’’ÎO5!·IDAT˜žžÛ¶mçö/z¡ªbKrr2‹‰‰aYYYlß¾}ÌÚÚšåää¨ë4TNU±åÉ“'ÌÁÁ¥§§³G±>}ú°äädu†J•••±}ûö±””¶fÍšFc˲e˘••ËÊÊbŒ1VRRÂ\]]Ù;ï¼£°ÝüÁ ÐîýnoªÌ[ÜÝÝÙÉ“'Ymm-ûî»ïبQ£Ôq Z‰è6hìK.33“éèè(Íêáææ¦p•9''‡3FþÁ---mï.«Ec_r[·neX||¼¼-??Ÿ`K—.•·-X°€MžüðÃþJ‘Lc±åîÝ» €Ò•Ó>}ú°AƒqîkæÌ™¬¨¨¨=º©vm‰-B¡Mš4‰ 4ˆ}ôÑG,..N]ÝnWm-²6___öú믳ݻw«£Ûí®©ØbooÏF­Ð¶`ÁfhhÈJJJäm‘‘‘ì÷ßo×~ª[syËêÕ«Úæ-111lÊ”)¬oß¾lÖ¬Y&æ>Ýnܸ‰D¢Týîåå…óçÏËïÛÙÙáèÑ£êîžÆÜ¾}<Oáu±¶¶†nß¾-oÛ´i“&º§1ׯ_G¯^½ ««+oóòò’?6pà@¬^½«W¯ÖTÕ®¢¢)))Jc£½¼¼ðÃ?àÁƒððð@dd¤†z¨²Ùi<==Ú½¼¼püøqÎçlß¾½Ýû¥i²ØRf†±¥OŸ>øõ×_5ÕEhIl€©S§bêÔ©飺•”” ''aaa í^^^‰DHKKƒ··74:~º#’å-\±¥~ÞŒàà`uwO+Ñtí ;;÷—\~~>ª««5Ñ-ËÎΆ££#LLLÚ½¼¼ä¯Yg”­ô^Ð××ï´¯KSŸ¡úw6M½.………vJ©ììl899ÁÔÔT¡b Å–†dçÍu«þã å-­G t;¨¨¨ùu2VVV`ŒuÚ/¹ÚÚZÎJf…I:›ŠŠ ¥÷о¾>LMMåï¥Î¦©ÏPýÇ;ÙçD6ㆌìsÕY?GµµµJ¯ @±…b‹2Ùû¡áwQgÿ QÞÒz”@·;;;PZ äæÍ›066†¹¹¹&º¥qööö¸ÿ>ªªªÚ…Ba§ž¯ÖÎÎNé½’››‹ÂÂBù{©³iê3TÿñÎFö9‘½2B¡fffJW`; Š-Ü(¶(kê3TÿñΆò–Ö£º899àþ€ÊëŒ\]]!‘H> %%%xøð!\]]5Ø3Írrrj4˜wÖ÷K×®]Ñ¥K—F_GGGMtKãdŸ“[·n)´ …ÂNý’Å–»wïÊÛ(¶Plábee+++Î×EWWÎÎΚ阆QÞÒz”@·ƒ Gˆ‹‹“·•””@(bòäÉì™fM™2úúúˆŽŽ–·É Ÿ¦M›¦©niÜäÉ“ñøñc°“'Oj°çí'//OþâóùÌÒÒR~¿þâ9ÑÑÑÌØØ˜3†ýë_ÿb...¬_¿~ò¹[;Š-Ü(¶p›6m1bswwgäïcÇŽÉ·ÉÈÈ`=zô`}ûöeË—/gAAAÌÚÚZ>ç|GCyKû iìZ©¶¶yyy€€€ß‹Åòí¦OŸìܹ§OŸ†¯¯/öîÝÛaÇYYYÁÇÇäÿ333…í–-[=z¥¥¥Ø°af̘¡Ö¾ªSqq±üý!û+>//EEEòmŒqþüy,[¶ ›7o†µµ56mÚ„÷ß_#}V‡'OžàÉ“'èÞ½;&Ož,ê6yyyáܹsøòË/777?~¼Ã.ýn``ÀùCCCùíaÆ᯿þž={póæMÌž=óçÏÇK/½¤Öþª Ån[¸yxxÀÎÎNé3dmm-¿íàà€ .`Ë–-HLLD`` ¶oßwwwuwW-(oi<ÆÓt'!„ByQÐhB!„BZhB!„BZhB!„BZhB!„BZhB!„BZhB!„BZhBQ³ÄÄD¤¤¤hºMÊËËùs矟Ïùx||<ÒÓÓ›ÜÇÓ§O£0×,!„t”@B:…€€àöíÛ í>D@@.]º¤¶¾,Z´_ýµÚŽ×ÅÅÅ8p œ1}út$$$pn7mÚ4ìܹ³É}ÅÇÇãµ×^CYY™|ß;wîÄ“'OTÝmBQ+Z‰Ò)\¾|€tźC‡ÉÛ+++qùòe…Ü:³cÇŽ!-- ?nóꆽzõÂÚµkall ÈÍÍÅŒ3‡®]»ª¢»„¢tšÒi :‡Æ•+WšÜ®¸¸ÕÕÕ m"‘%%%òûÕÕÕò¤[,ãÆ‰D ÏyðàRSS›’““!‘HÝ&;;‰‰‰Jû€¢¢"ÔÔÔ²²²pëÖ­&ÇCZZ’’’”ᤤÑÑÑððð€D"Aqqq“û’yüø1’’’”úçàà€¹sçÂÀÀ‰¥¥¥€²²2)ýÑRQQøøxÜ»w¯É׃B4hBH§‚ÀÀ@|öÙgMngccƒ}ûö)´EFFÂÝÝ]~ß¾}°´´ÄüKKKøúú‚Ïçã×_Eff&<<<àææ777L™2EéÕÕÕx뭷н{wx{{ÃÉÉ ñññ Û\½zžžžèÞ½;üýýaii‰7*lcii‰ï¾û>>>pttĬY³=¯ÄÄD¸¸¸ÀÏÏ–––ؾ}»üqwwwìÚµ gÏž…¥¥%œœœš|ªªª0aÂØÛÛÃÇÇ®®®HNN–?~êÔ)XZZ¢¸¸ð÷÷ 6 –––°´´âµ×^ƒ¥¥%BBBàáá>Ÿßä± !D“(&„t*k×®ÅéÓ§qúôi•ìoݺu8zô(òòò0~üxÌ;“&MÂ'Ÿ|‚¢¢"üðÃøå—_––¦ð¼Ÿ~ú æææ¸ÿ>ѳgOŒ?•••¤W¦‡ ooo$''£  X¸p!Μ9£°¯åË—cÊ”)xôèþüóOÎ~VVVbìØ±àóù¸pá>|ˆððpÌž=çÏŸ -œ>}:FŒÆX³ÃZ¾ùæøøø 33W¯^…‰‰ vìØÁ¹­³³³|üy\\c`Œ¶lÙ‚œœdddàñãǨ¬¬Tú†B´ %ЄNåÕW_ŨQ£š½ ÝRK–,App0ºv특s碰°‡™™fÏž >Ÿ“'O*|mÚ׈#ä·===cÆŒQØÆÃɉ‰ mƒ ’×€¿¿?^zé%ùUÚ¤¤$˜ššbÁ‚H$`ŒA"‘àéÓ§¨­­UØ×¨Q£šíçÝ»waff†€€yÇÃo¼k×®µðl5OVPI!Ú†hBH§õïÿ›³ýí·ßF~~>zôè[[[¬\¹‹-Ré±§L™‚èèh8;;ÃÅÅ'NœÀO?ý$/trr¡C‡pæÌ¸»»C ÀÞÞ½zõ’/ ÓæææøùçŸqñâE¸¹¹ÁÁÁK—.ŲeË¢ÒskÊ'Ÿ|‚C‡ÁÄÄD>ŒåóÏ?G×®]áíí wwwŒ7Ó§OWk¿!¤5xL6!„t`wîÜ|îa™ŒŒ TTTÀÁÁAaLo~~>âââ`mm ¬¬ puu ½Jš›› @ ŽD"AJJŠÒ¾222`hh(Š!»Ïç󑘘ˆÜÜ\ 8P> E} …HOO‡••|||Vñ»sçºuë33³½ÅÅÅHHH@II |}}áèè¨ðxnn.jkkѽ{÷&÷“––333…ùšËÊÊ••…^½zÇ㡼¼™™™pwwW{ÒÒRH$$&&"-- ÖÖÖ8p [t>„¢ ”@B!„Ò 4„ƒB!„V šB!„V šB!„V šB!„V šB!„V šB!„V šB!„V šB!„V šB!„V šB!„V šB!„V šB!„Vø š §#IEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/Q8-1g-idx-sorted.svg000066400000000000000000006666221231437614300246200ustar00rootroot00000000000000 image/svg+xml PyTables-v.3.1.1/doc/source/usersguide/images/Q8-1g-noidx.png000066400000000000000000002423411231437614300236300ustar00rootroot00000000000000‰PNG  IHDRÐß}™SsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwX×÷?ð7½I•& €""R#*ˆ¢Ø±—hbCM,шŠ-F½$±&¶äcO"ÑX° Æé‚R¥—óûÃßΗa—²Š æ¼ž‡çÑ»w9;{ï""0ÆcŒ1ÆêD±±`Œ1Æcìm 4cŒ1Æcràš1ÆcŒ19pÍcŒ1Ƙ8fŒ1ÆcLœ@3ÆcŒ1&N cŒ1Æ“'ÐŒ1ÆcŒÉhÆcŒ1ÆäÀ 4cŒ1Æcràš1ÆcŒ19pÍcŒ1Ƙ8fŒ1ÆcLœ@3ÆcŒ1&N cŒ1Æ“'ÐŒ1ÆcŒÉhÆcŒ1ÆäÀ 4cŒ1Æcràš1ÆcŒ19pÍcŒ1Ƙ8fŒ1ÆcLœ@3ÆcŒ1&N cŒ1Æ“'ÐŒ1ÆcŒÉhÆcŒ1ÆäÀ 4cŒ1Æcràš1ÆcŒ19pÍcŒ1Ƙ8fŒ1ÆcLœ@ÿGTTT ** QQQ¨¨¨hìpÜ¢E‹°}ûöÆ£F=† 0cÆ \¹r¥±ÃykÄÄÄ`ÆŒ¸zõjc‡ÂêÙéÓ§1cÆ dff6Êú‰qqq¸~ý:222%†ú²|ùrœ9s¦±Ã©¬¬ 3fÌÀÑ£Gë½í§OŸbÆŒ8wî\½·ý:¬Y³kÖ¬þ_VV†  <<¼£b²pýŽ‹GïÞ½¡­­¶mÛ¢mÛ¶ÐÖÖFŸ>}ÐØáÕ«¹sçâÈ‘#2_Û±cÇk99×—ÀÆÆ6l@xx8ÒÒÒ;¤·Frr26mÚ„û÷ï7v(¬ž…‡‡cÓ¦MÈÍÍ­µî¥K—0uêT¸»»CCC 8xðàK­÷ŸþA= ££ƒ–-[¢C‡022‚¾øâ ÄÅŽT»åÏ?ÿÄ‚ `jjÚ ë[¿~=¶lÙRçúeeeØ´i.^¼Xï±dffbÓ¦M¸víZ½·ý:ìÛ·ûöíþ¯¬¬Œ¨¨(LŸ>½£b²(7vìõ9yò$F…ââbLŸ>ݺu„††bãÆpqqÁáÇáããÓÈ‘ÖU«Va̘16l˜Ôk³gÏn°‹‡¼îß¿ãÇcêÔ©øþûï;ÆÞJ¿ýö¶mÛ[[[ØÙÙáÖ­[/ÕÎÚµk1þ|hhh`Ê”)prr‚‰‰ ¢££‰-[¶ ,, aaaõ¼¯Ï²eËн{w8::6ÈúvìØ===L™2¥Nõ•••±xñb¸»»¿æÈÞN3fÌ@×®]ñ矢W¯^ûÿ8~GåççcüøñPPPÀßÿ:¯õíÛC† A=0nÜ8DGGCCC££}ýfÏžÝØ!T+>>àééÙ¸0öûòË/±lÙ24iÒÛ¶mÃäÉ“ånãæÍ› „««+‚ƒƒaaa!¼Ö£G@`` öîÝ[oq¿n!!! ÅñãÇ;”j)++cÉ’%Æ«K—.hß¾=–.]Ê ô„»p¼£Ö¯_ÔÔTÌš5K”033ƒ­­-üýýñøñcQªû¾oß¾011ÁÚµk1eÊøûûËŒýÞ½{ðõõ­µ»CJJ ,X€Î;ÃÔÔæææðööƱcǤê>|¾¾¾HNNÆæÍ›ÑµkWãƒ>¨öÎíÎ;áááfÍšaÀ€ÕžªcnnŽ&MšÈµLUÓ§OGEE6oÞ,Jž+³µµÅòåËEe3gÎÄ„ ““#ܵ®ü‹Wtt4FŒXXX oß¾ µ±}ûvôîÝÏŸ?ʲ²²àëë‹þýû£¬¬L(ONN†¯¯/>\ë6­[·fffèÛ·¯¨<,, ¾¾¾ Ão¿ý†ž={ÂØØîîîÕó0`,--amm ???ܼySx½´´Txß%dz䯦ni’å*S‘lûîÝ»qóæM :fffpvvÆÚµkeŽå¹sçˆfÍš¡sçÎX¹reûæäÉ“èß¿?š7oŽV­ZaĈˆ^ÏÎÎÆ Aƒ0a”——‹–=vì˜ÌÏÍ®]»Ð³gO˜šš¢mÛ¶˜8q"ž={&µî‚‚̘1vvvhݺ5>ùä“÷ÑĉqåÊó&!öNzÿý÷ effV['55•P×®]…²­[· “ªïééImÚ´•…‡‡“)++S¿~ý(((ˆ,-- -Z´HT×ÙÙ™Z¶lI®®®Ô£GZ°`Mš4‰rssI[[›úöí+µÎøøxRTT¤Ï?ÿ¼ÚíHII¡ÀÀ@@H´`Á¡Ž‰‰ õêÕK´œšš¹»»“™™µoßžfΜIFFF€Ž?Nþþþd``@Ó¦M£Î;úàƒDmTTTPÿþý ÙØØÐüùó©OŸ>¤¨¨H-Z´ ¬¬¬jã&"Z¹r% <˜ÐàÁƒEqWTT ÒªU«è£>"rtt¤‚‚¡S§Nòóó####     úå—_j\ÿ²eËHAAlmm)00Ö®]K&L 333Q½~ýúêÛ·/­ZµŠÆŽKŠŠŠdggGyyyB½ .0`©©©ÑàÁƒéã?&UUU211¡;wîPëÖ­ÉÎÎŽfÍšE€V¯^-Z_¯^½H__Ÿ<<<ÈÇLJ֯_O¤¬¬L¶¶¶”““#µÎÝ»w‹Úøâ‹/™ššÒÌ™3iÔ¨Q¤ªªJºººôàÁ¡ÞÆ -_¾\(»ÿ>ijjRÇŽ©¤¤¤Æ}HD4fÌ@½{÷¦ 6иqãÈØØ˜ÜÝÝ©êi¶M›6Ô©S'©6ÂÂÂmݺUT>eÊ@4{öl>|8©¨¨¾¾>ÅÄÄÈÜ÷†††4qâD ¢ÐÊ•+ ]ºtIj½S§N%ú÷ßkÜÆ£G’¹¹9Mš4‰V¯^M“&M"sss@Û·oÕýæ›o >œÔÔÔèÃ?¤Q£F‘ŠŠ ©¨¨Ð£GDõ/^LÈÃÃ֭[GÓ¦M#}}}êÞ½; ¸¸¸c«Jr;pà@—),,$EEErss“k]DD:u¢æÍ›S§NÈËË‹‚‚‚hܸqDDtñâEÒÒÒ" ¤¯¾úŠÚ¶mKŠŠŠ¢ývøða@'NœÊ~ûí7@èòåËBù®]»]¼x±ÖØŒÉÇÇGªüèÑ£€† B***Ô·o_  &Mš:s挨þHYY™Z¶lI‹-¢ùóç“……©ªªÒüADD¥¥¥Hdff&œ‹k¼š>}ºP&¹>õìÙ“ôôô¨S§NôÅ_©©) ¯¿þZÔÆÕ«W©I“&dbbBAAA´|ùr²µµÎ]ß|ó¨þüùó ÓôéÓÉßߟÔÔÔH[[›nß¾-ÔÛ¶m …  e±±±¤««K...TTT$”:”PË–-iÞ¼yÔ¿RRR"sssJOOê•””››)((И1chãÆäççGíÚµ#+++rqq‘ÚG’óÃúõë«Ý¬aqýŽ200 ##£Zëikk“‰‰‰ðyèòòrrqq!---JNN•6ŒTTTèáÇB¹³³3 I“&Iµ@ŠŠŠ”˜˜(* "tçÎZ·3FækÕ%ÐhçÎBY^^©©©‘ŠŠ õìÙS”¤Ž=šÐÝ»w…²;wúꫯDm_ºt‰ÐŒ3j[r!;zô¨¨üçŸ&4mÚ4Qùúõë¥. ’ZEE…âããk]'уHEE…ºwï.ÚN"]Ž9Bhâĉ¢:[¶l!$”I’8555ŠÊ;&ÄW9Q-))!²°°µÝ«W/áâ^QQ!”ÿøãR_Îd%Ð’2*++ÊãââHII‰|}}Eëóóó#%%%ºt郃éééÕ)q»rå  ?üPTþÝwß ÉOeò$ЧOŸ&4yòd*//Ê£££IAAüüü¤¶YIII*NOOÙÊòóóIWW—ºwï^ëvæåå‰ö%Ñ‹Ïz«V­ÈÐÐJKK…rIݲeKQâpâÄ @³fÍÊâããIUU•<==Em?~\Ø ‘@ß¾}[HúåÕ©S'@cÇŽ•———“““©ªªŠ¶!77—lllHWW—222ˆèÅ{¤  @3gÎê}öÙgdccCÆÆÆ¢sŒ¿¿?ijjRqqqq¥§§ ÇOU’󎺺ºè MLLŒðeY"//LMMÉÐÐPô~¦¤¤®®.µlÙRKuÇxujJ  :Qqq15mÚ”š6m*j£[·n¤¤¤$:ïdgg _ò*'Ðaaa¤  @C‡sÉÉɤªªJ^^^¢¶GŽIŠŠŠtæÌ*..&777ÒÖÖ]ßöíÛGhÞ¼y¢e#""e’sgÕ’/ËÎÎÎRûèÙ³g@øbÆ'Ðï ÜÜÜj?„UµiÓ† “< ôÅ‹ }öÙgRuOž}DusrrHQQ‘éÉ“'¢× D(??_(“$ÐU ¬¬,RRR¢–-[J­³r=dÈj¿xõìÙ“”••Ew–³²²ÈÊÊŠÌÍÍiÔ¨QRïsM$½ªï_ZZ)((¼RÝ·o_RTT”ºcKô"aPWW¾`Höƒ»»»Ì8%wØ$ ÑîÝ» >|¸NÛ*Q^^NOŸ>¥ÔÔTáN^å»á’zþüù¢å HMMú÷ï/”­^½šÐ·ß~+ª[ZZJ::: –@ÿþûïÕ~éݾ};yzzŠþRSS…×% ôÙ³gEËEFFòöö–jsæÌ™€víÚ%”¹ºº’“““ðÿ¶mÛR@@9’ºuë&”›™™QÏž=kÝ&É9zݺuR¯IÎ;ƒ–zÍÚÚšZ·n-ü?88XøBZ•äó{þüy¡¬>hKKK©úÇ'ôìÙ3""JLL$2×ùé§ŸJ%Ð~ø! ëׯKÕ0`)((ˆÎG’/<&&&ôñÇÚ·oŸh¹Î;“ºººè †DÛ¶mEçI™Çµä3\ݵ»iÓ¦Ô¹sg™¯±†ÇƒßAÚÚÚÐÑÑÁÓ§Ok­ûôéShhh@MMMîõH¦ »~ý:üüüD¯Iúë=zôHTn`` s¤µ““<<<°sçN,\¸JJJ8vìRSSkíÇö*¼¼¼ ®®.*³··Ghh(z÷î-Uééé"BTTš4i‚±cÇJµ]TT„ÔÔTTTT@QQþáqqqÐ×ׇ›››¨ÜÄÄŽŽŽxðàÊËË¡¤¤$¼Vµ¿mMûì3 <¸Æý" EEE©A ÆÆÆpttÄ?ÿüS§vªÛMMM|ñÅR¯=yòEEExüø±¨¿nuÇÀäÉ“ñ¿ÿý{÷îÚÛ±cŒ¥>¿²TTT`Ó¦MرcbbbD}r}Û[µj%*«Ú®††Ú´i#|†}N%ƒô$”••áåå%³õë`nn¢Ø$TTT„óÄíÛ·…}_™¶¶6ºví**‹‰‰™3ùøø`Æ BèÞ½;Ö­[‡§OŸ¢¼¼<À×_ììlLž<ùùùHJJBJJJ¦5“ô÷733«¶Ž¬÷ÞÑÑ—.]þ/™¶OÖ±åãャG"&&FêóZªöÝ Ì •žžá’µþ=z`×®]¢²èèh¨ªªâ믿–ªÿèÑ#bccáàààÅ{{øðatîÜ?ýôÆÑ£G‹–{ðà´´´0~üx©6óó󑜜Œ’’¨ªª"66VVV°²²Õëܹ³Ôõ¨2ssównúÙ·'Ðï(;;;\»v ÐÔÔ”Y'++ YYY°µµ­S›Te•d`„¹¹9ŒŒŒ¤êà½÷Þ•5oÞ\”ðU6yòd|ôÑG8uêúõë‡íÛ·C___æ´tõEÖ #™¯IÊ%û¡  ………°°°@³fͤڑ”½l——SSS™Ëš››ãŸþAaa¡(Ϊ hM²²² ¡¡QmZ9###¨¨¨ÈŒãêÕ«xþü¹(®n¿Ö´¿«_êêê000¨vEEEÕËÎÎF“&MdN]hjj OOO© UQQ‘00Iž)óòò ¥¥]]]™±Ö5®ºý@íÛáíí-õ¾Tw xxxÀÙÙ;vìÀ_|»wï",, 2ßÛª/^ŒeË–aРA˜:u*`jjŠ_ý ,@ii©Ô2ÚÚÚRe ¢mÍËË ;É“$µ Ar”• |òÉ'øä“O#GŽÄ¡C‡¤ê˜™™AUUUT&™¿ZÖvHÊ*ÏqÝ£G¬]»çÎÎÞÞÞÈÍÍEii).]º$$‹|ðA­Ûdmm àÅ ÃêÈó5oÞ¼NÛQŸª‹ø¿ÏŒ¼ÇPmŸ«.]º@KKKT^TT$ $¬º\ii)rssaeeUãµ@²|^^Z¶l)UOMMMæ9O"))I¸‘Ã'Ðï¨nݺáêիعs'>ÿüs™uvïÞ ¢;m’dûÉ“'¢ºÅÅŸuë–èäЦMÀ!C0jÔ¨WŽyذa˜1cvìØ{{{œ9sŸþù;Åž––ÌÍÍ¡¯¯­[·Ö{ûVVV¸rå RSSEû½´´ÿý7LLL^iÖ„††âéÓ§066®1ŽóçÏ#>>^tǤ¢¢!!!ÐÕÕEÓ¦M_:Žê!""]ºt­óâÅ‹044¬qÛÛ´iƒ7n`ëÖ­ÂŶ&=z4š7o{{{,Y²ÞÞÞððð¨uYkkk\¾|‘‘‘¢_ $ïSUššš2’õ$Å6mÚàßÿŶmÛj£.¦L™‚I“&!$$¿ýö0qâÄ:-»gÏXXXààÁƒ¢DñîÝ»¯“$É»pá‚ÔÝPygâxÚÚÚppp@xx8¢££…óÛ«Ü‘?þ<>þøcÑk’'ãU¾kßµkW¨ªª ´««+ ```€V­Záܹsˆ…¾¾¾ÔÍ Y$ÉVå»Ü/Cò¹?þ¼ð<š¶£.Ÿ¹ú$9†.^¼(u½“u µiÓ.\ÀæÍ›«½¡SYVVF´oß+V¬@÷îÝáååàÅ/ÖÖÖÐÒÒªÓµÀÚÚׯ_—ºÁƒ””™7¤222••Å ô„§±{GÍ;zzzX±b…0Ïpe X³f ´´´DÐ-Z€ÔcOOž<)u‡ÁÝÝêêêØ¶m[½<\MM Ÿ~ú)Nž<‰Å‹ƒˆê|q^üìXµËÈëæíí7nS´Õ§N:Ξ=+*¿rå ž?^§ä®&’.;vì¨SU|íÚ5dee½r5©zñ»qã233¥º“Tåííììl™w «""Œ3ééé8xð ~þùg4kÖ £FªÓT„’XªÆ.š’L¢E‹ˆ‹‹Cbb¢¨ü—_~‘¹OŸ>Eppp­qÔŇ~lܸ?ÿü3zôè!óNXUD„ôôtØØØˆ’çââbœ8qâ•b’쿪ÇWRR¢££_©mymذeeeøüóÏe¾wòrrr‚–––ÌGhÿõ×_ :–555áîgÏâüùóèÞ½»ðZ÷îÝñ×_!$$^^^uúUËÀÀ¦¦¦¯œ@wèÐŠŠŠÕn‡ŠŠŠèËcCŸ‹mll`dd„©_rdÅìíí‚‚üüóÏujܸqHNNÆþýûñÓO?ÁÒÒ~ø¡èñîÞÞÞ¸sçN¦™ëܹ3JJJ**¯é ãÇíÚµ«S̬4JÏkÖ víÚEÊÊÊd``@[¶l¡»wïÒÝ»wiÛ¶mdhhH ´ÿ~Ñ2dffFFFF´yófЉ‰¡åË—S‹-ÈÀÀ@j»+VðbšµÈÈHÊÏϧ””ºpá}òÉ'tóæM¡®³³s­cbb„W]ºt‘k{çÌ™Cêêê´zõj:{ö¬hÊ®êŽ1BªI“&©Á…²yIF¡›ššÒÿþ÷?JMM¥¼¼<ºwï­_¿^4õQuªDøôéSÒÑÑ!333:}ú4åääÐåË—ÉÖÖ–”””èÞ½{B]É ByMUTT»»»03F\\=þœnܸAB½ÌÌL200 úã?(''‡ÂÃÃÉÞÞž)22R¨[Ý”rDD–––äéé)U.™Â¬ò€š^½z‘šš5kÖŒ~ùåÊÏϧ[·n‘‹‹ )((ˆÿÈZgQQÙÚÚ’ŽŽmÚ´‰©  €¢££é‡~ ñãÇ u׬YChÍš5BÙßÿMJJJ4hРZ÷cQQ5oÞœ (88˜ èÚµkdooOúúúRƒ%3·ôïߟÂÃÃéܹs4|øp211‘:¾òóóÉÚÚšôôôhóæÍ”””DùùùEÛ¶mÍ®PÓ¾¯ì³Ï>f7¨mšÃÊ$ƒk·mÛFYYYCC† !@§NêJVž.PÂÙÙYj°WÇŽI]]víÚEyyytÿþ}êÚµ«°ÿê2ˆ0!!vïÞM»wïze•ýÕD²,--iË–-JñññtýúuúöÛo…Y*ÏÔ©S'©s£Ä’%K„Ùâââ(--¾þúk©YT$–.]*¼?ýõ—P.™æ}÷ÝwuÚ"¢R³fÍD³¸UÞ‘,£««+*;v¬0ëÎãÇ)11‘¦N*sà¥dš½yóæÑŸþI.\:ŸVVÓ ÂÊç" YÇ—dv¢±cÇRbb"eddТE‹„ã³ò Â’’rpp ---Z·nÅÇÇSAA=|øvíÚ%šME2›NåY"""HEE…úôé# âMOO§¦M›’©©)íÝ»—RRRèùóçtïÞ=Ú°aÍ;WX>..ŽTUUÉÖ֖¨°°NŸ>M¤­­-ó:)™¹CÖÖ88~ÇEDDpâ•üµlÙ’BBBd.súôiÒÕÕꪪªÒÁƒeÎ]QQA›7oFËWþkݺµh:­º$ÐD/fI€ŒY0jMcÇŽ%333^L¥&ñºh¢sKæÝ®ü§¥¥%š²­:5]È®_¿N¶¶¶¢véôéÓ¢z/“@½˜I2¢½ò_Õ‘ï·nÝ¢¶mÛŠê4mÚ”Ž;&ªWŸ ´‰‰ ýõ×_¤¡¡!|©ÒÔÔ”š1¢ºu¦¤¤³qTþSQQ¦‚ —ºJH™Í›7×¼éÅ1`mmM„Xƒ‚‚„ù¡++--¥#FˆbêÔ©“ðV=¾’’’hÀ€RÛ¡ªªJS¦L©u?TuïÞ=@&&&ušãZâöíÛÔ¢E Q ýúõfòx•úñãÇôÞ{ï‰ößÇL .¬s-ùU÷wáÂ…:oë±cǨyóæRmH’Þ«W¯h¡U¿ IDATŠê×”@—––RPP)**ŠÚ5jeggKÕ¿|ù²pþª<½dFF†°oîß¿_çmùûï¿ež_äM óóói„ RûäóÏ?M{)‰uêÔ©ÔªU+¡^MÓkÖG]^^.Ìý^ù:÷×_I%ÐD/fÉ9r¤Ôö(++ ³DFF’ššuïÞ]ê ˆdö˜Ê3œDGG“———T›šššRÏEøóÏ?…릂‚)++Óª½Nº¸¸ˆžÙÀŸQ5wcï”'OžàÖ­[HNNÆœ9s ¤¤„ÐÐÐjûùeddàúõëPVVFçΡ¥¥…'Ož ¼¼\æ Œ‚‚DEE!&&:::hÕªZ·n-ª“’’ æáÀ‹ŸÂnß¾”””G$×$99åå倪¤¤$¨ªªŠf€HHH€¦¦¦T³gÏž!//–––¢¾|ÅÅÅHMMEÓ¦M¥¶ðàÁÀÌÌ ®®®uŠ¿  @è‡,kÀgYYîß¿‡ÂÊÊ Rí"-- FFFRƒ_ê"99wïÞE~~>¬¬¬àêê*õqYY¢¢¢æÍ›ÃÑÑQªzQQž233ѬY3899 þ$﵉‰‰Ô¶TTT 11ªªªµ³À‹ã#22Ïž=ƒ»»; 1vìXìÝ»WæÁÛ·o#&&íÚµƒ]Ç<~ü÷ïßGvv¶°:::uÞ•×ëââ‚yóæaÅŠµnWe………ˆˆˆ@NNÜÜÜ`nnŽüü|¤§§‹öaNN²²²`nn.5@1%% Rƒ­ÊÊÊðÏ?ÿ ))Ih[Ö±QÉç¨:¦¦¦rŸORRRðàÁdgg£U«V°±±‘¹o%³íÔ4è1##·nÝBQQœeÈþï¸SQQ‘j/)) åååR³7ÔFÒW·òÓk:ï<}úEEEB—¾ÊRRRpûöm(**ÂÙÙ¹Ö·©©©(..®ñ=”œ?utt„AtåååHJJ‚®®®Ôg¾¦ã+!!7oÞ„µµ5Q^^ŽÇC___æ@ß'OžàÞ½{xöìLMMáèè(¬/33¹¹¹2÷‘$fYï“äZðüùs˜™™ÁÅÅEæ¹=77×®]ð¢«\“&Md^'CBBàíí3gÎHÍVÃ'Ð/áÀؾ};~ûí·G̾©ÂÂÂàããCCC\¾|¹AGº×Fr¢X±bæÍ›×Øá°FR9~›Õ”@7–¾}û"44>”šR½›Îž= ܼy³Öi+Ù›ÇÏÏééé¸|ùrc‡Â*áY8ä”’’‚­[·âæÍ›())iìp^JçÎqæÌDDDàÎ;oDŒ>|666˜5kVc‡ÄØ;£¨¨+W®Ddd$Nž<‰uëÖqòü"™/33³±Car*++CûöíѧOŸÆ…UÁ ´œ¦L™‚µk×¢gÏžÊ+éܹs­34¤ÐÐPœ8q}ûöÅçŸ.5Ÿ*ûo111©ö'î·‰¡¡¡\ss¿.%%%سgÌḬ̀jÕªj§¶dï.¾)ñvRVVÆÂ… ; &wáþ}ûpýúulذzzzˆŠŠ’ë Œ1Æcìí÷Ÿ¹]RR‚øøx¨««Ë¼¼qÿþ}$''ÃÕÕUôgZZ6lØ€‹/6TÈŒ1Æcì ôÎ?HåÊ•+pssƒ¶¶6Ú´i#õ4(‰ÌÌLxxxÀÕÕãLJ©©)–/_.¼ŒôôtôíÛ^^^xþü9üÊÔ3ÆcŒ±·Ë;ß…#44ÁÁÁpssæM› ¦¦&šÊGbĈˆˆˆÀ•+W`ff†}ûöÁßß'OžDïÞ½QXXˆœœ¡~›6mжmÛÖ:½cŒ1Æ{w¼ó te¾¾¾(**’J Ÿ>} añâÅByË–-áää$ó1º’ZÖ3ëcŒ1ÆØ»‹oˆŒŒDii)ìííE厎Ž—¹LtttÛÿñDZÿ~©òêúb3ÆcŒ5”ÄÄD©²Ñ£GcüøñÍÛáï]’'ÿ´k×NTîèèˆôôt”••½Rûû÷ïGDDJKKE剉‰(..~©²ÈÈHïÔ×v”””àêÕ«~¾ªî|*!ë|•‘‘ÒÒRDFFʼñÇ*i„LJ7š^½z‘§§§Tù† ¥§§‹Ê¿ûî;@YYY¯´^ooortt|¥6ª:zô(=z´QÛ,,,¤I“&½t›.../WŒµ™>}ú+¿_¯Úæ©S§èÀ/Ýf«V­äН.ÆŒÓèmRjjêKµO...r­¯6YYY4}úôFo³¶ýXS›sçÎ¥¹sçʵ¾Úð¹E6>·ÈÆçiï¹ÅÑÑ‘¼½½åZß wᄹœ>|CCC¡üÁƒPSSƒžžÞ+¯£r»õÁÎή^Û{™6•••ѯ_¿—nÓÓÓºººr­³6¾¾¾PWWoÔ6mllPTTôÒmvéÒE®øêÂÏϯÑÛìÑ£š4iòRmêêêÂÓÓS®õÕF]]¾¾¾Þfmû±¦6;uê$׺ê‚Ï-²ñ¹E6>·H{Î-õ³¼‹8`aa¸wï<<<„ò;wiÞ…‹ÜëPß'­—iÓÆÆ¦ÞÛ|UoÊE®¾Û|ïÂEîuàs‹l|n‘Ï-ÒøÜòßÀ} ¸»»£Y³f¸qã†PVTT„;wî`ðàÁcŒ1Æ{Ó(-Y²dIcñ:åää`ãÆ Å… ••…ÂÂB„††ÂÞÞPTT„’’¾ùæhjj¢°°³fÍBRR~øáèëë¿R .\€ºº:†ZO[õn°±±±±1ù{\evvvüóYÊÊʰµµ…Ac‡òF144D‹- ©©ÙØ¡¼QøÜ"Ÿ[¤ñ¹E¶Ó§O£iÓ¦ zçþmóΟ] Œàà`èëëÃØØXøåÑÏ3fÌÀ·ß~‹}ûöaذa())Á¥K—`mm]/q„„„`ìØ±2ç”þ¯úþûïñüùóÆã³råÊÆáóüùs|ÿý÷Æ'44T4Òž½ÀçÙøÜ"Ï-bÁÁÁ;v¬ÌÎ1±ÿÔƒTËØ±c{öìiÔ8cŒ1ÆjÃyKíxáâÇä;IŒ1öùùùñOÒŒ±zñÎwáx[ðO±Œ1öúœ>}·nÝjì0cï¾Ý@ª>H–.]ºðÏ%Œ1öxyy5v.**ªÁ§ÿ{Ó•••!>>¾NSþ—CMM­±Ãx£ñè’––ÖØ!0ÆûãA„Òx¡lœ³ÔŽèÒ¢E‹Æ1ÆØÿÂ)MOO7nlì0Þ8œ³ÔŽè‡ƒâîÝ» cŒ1Ƙ”»wïâàÁƒˆ‹‹kìPÞxœ@7UUUèééA]]½±CaŒ1Æ“¢®®===¨ªª6v(ofŒ1ößÅ ô[£ ÒÒ–ÔZËÖ6þ¥×PZ ddÌ`Uc=“Úãxúúú¢Ÿóòòàää„3gÎàÔ©SèÝ»wÚ±´´”º³0cÆ Üºu 3fÌ€»»{½Æý&ÑÖÖÆßÿ.]ºe'NÄÅ‹áåå… Ô)îÙ³'bbbвeKDDD sçÎrÇÒ½{wtïÞ]îåcŒ±7wáh òv?`ÿG[[cÆŒܼyOž<ÁÈ‘#±wï^™õ÷ïß‘#G"55µNí§§§cýúõèÝ»7áææ†?ü5.·{÷nøùùÁÕÕãÆC|||·)66S§NE—.]ðÞ{ïa̘12§8ŒÇ–-[0xð`ØÙÙ¡sçΘ:u*bcckl_KKK”|8Ñ¡C|úé§ÕËÎÎF`` >øà¸¸¸à£>ÂÅ‹¥ÚûöÛoññÇ£¼¼kÖ¬AÏž=ѵkW¬X±B8÷={VØÏ~~~¸wôôtŒ9¿üò "##1qâD¸ººbÀ€2÷åºuë0nÜ8”••aÕªUèÓ§Ú¶m‹çÏŸ×yÿ×ð Bi<ˆP6ÎYjÇ ti¨ù@vöËý•”4Hˆ¯„ˆ`jjЇbáÂ…R‰aáÂ…xðàš5kV§6Ï;‡eË–AWW¾¾¾°°°À±cÇàááQm´lÙ2|õÕWhÙ²%:vìˆ#GŽÀÍÍ wîÜ©u}¿ÿþ;œ±cÇhiiÁη‚›››(ቅ‹‹ ¾úê+¨©©aĈpttÄéÓ§qõêÕ:m[UD„´´44oÞÊÊ óÔýû÷qèÐ!äææ e·o߯¡C‡0gÎ|òÉ'(++ƒ––:„Ž;Ö:àðÑ£Gèܹ3Nž<‰)S¦ÝCfÏž>}úà?þ€³³3ˆk×®…‹‹ ÒÓÓ…åSSSqèÐ!lݺ~~~ÈÊÊ‚‡‡”””••…C‡aïÞ½ðôôÄå˗ѦM„„„`øðá8pà€(–øøx¸¸¸`éÒ¥xòä Þÿ}„††bèС zéývèÐ!üòË/èÚµ+8;;;Ü»wsæÌ‘ꇞžžŽöíÛcùòåÐÕÕÅ|€{÷î¡_¿~Xºt©¨nII |||0nÜ8$''£GpssÃÍ›7E}dcccáììŒM›6ÁÔÔݺuCDD>øàlÚ´IÔæ•+WpèÐ!Œ3K–,¡¡!„   üùçŸèÓ§>|SSSüþûïxÿý÷Eïs~~>:„={ö W¯^(**BÏž=¡C‡bݺu¢uþý÷ß8räF…;wÂÔÔíÚµ½ô>×ñ Bi<ˆP6DXÄ^»1cÆP«V­h̘1tôèÑjëŒ3¦Ú6ÜÝÇ@uø«k=Y‹ ˆ«µž‰ÉbŠ‹‹{-ûÊÄÄ„,--Ee¹¹¹deeEè?þ "¢;vˆþ/ñ×_úþûïe¶?}út@aaaBÙÓ§O©  @T¯°°LMMÉ‚ÊËË…òo¾ù†™™=}úT(¿ví ˆÚ z_sssÉÄÄ„Z·n-Z>==ZµjEÖÖÖT\\LDDkÖ¬!téÒ%Q›”ŸŸ/sûjóí·ßZ¼x±Üˆ……š>}º\ËIöÙƒ„²ÀÀ@@ööö” ”oÞ¼™ÐªU«„²¸¸8QÌdddDmÛ¶¥øøx¡ÞÅ‹…ý]TT$”_¾|™Ð„ „²S§NRTT¤[·n‰â½yó& tâÄ ¡<))‰”””ÈÕÕUTРA¤  @W®\ÊŠ‹‹Éßߟ”””èöíÛBùˆ#HMM­NûMÃÒ¥K©¤¤„ˆˆ ¨M›6¤©©I………BÝÉ“'úùçŸE1x{{“’’Ý»wO(_ºt) +VH­3##Cø÷ðáà 9sF(ËËË#gggRWWíû#FêÕ«effQii)y{{“¢¢"Ð7„ú;wî$´fÍ¡Lò> ýû÷ å………Ô¡CÒÐРÔÔT¡|àÀÂ:%Ÿ™šxzz¾ÔqÏØÉÑ£GE9 «ßn ]ºtÁž={àççר¡¼ñ²²²0wî\Ì;C‡…‰‰ âããÑ£GôéÓ0zôhèèèà‡~-»cÇhjjÂßß¿Îë322‚†††¨L]]ãÇGrr²¨ë„¯¯/ŒŒŒ„ÿ»¹¹ÁÑÑÇŽ«ñîéþýû‘––†iÓ¦‰–744ć~ˆ¸¸8DFFxÑxñ3ze ÐÔÔ¬óöI\¿~_~ù%0þ|¹—>úè#Ñhïxq×Z–ãÇÃÛÛmÛ¶ÅåË—aii)¼¶~ýz(((`þüùPSSÊ=<<àêê*³@‡àìì,s]ÎÎÎÂñèСEEEøßÿþ'µü„ „ãVYY>>>¨¨¨€‡‡Þ{ï=¡ždƒ¬nKZZZ2dˆðuuu 6 ………RwÿÇ?ô±úáçç‡={öÈìÈÄxá;ÆÈxÙÜèÈàÊ•úçeäååaÛ¶mtèЃF@@€ÐWKK }ô¶oߎ””˜™™!-- ¿ÿþ;üýý¡««[çõ~úé'lݺ±±±ÈÈÈý œ˜˜ Ñ2|ðT;>>>¸sç=zT팒>ˆáááRɃ$QŠŠ‚»»;† †/¿ü @‡àççÿ—š^èÞ½{èÝ»7ŒŒŒðǼ1 GÕ/”044”ùóapp0–-[†!C†à§Ÿ~%ÉÀ‹dLWWëׯ—Z¶´´™™™ÈÌ̽7}ûö­slÀ‹/JáááÈÍÍ…®®®Ð7//Rõ•””êܧ[hkk‹Ê\]]¼ø‰ÕÎÎ?FII z÷î-$Ú’ä÷áÇ€'Ož ++ C† ¢bõ÷Oþý÷_|||dÆ$Õ_II ¾¾¾¢2É—“~ýú‰Ê›5k###™ï³§§§Ôñ)ù¼Uí«ª  €ž={V»Œ1öºpÝ@ªC¾¾>0cÆË-õf$Ð-Z´¨Ó€¼É“'cóæÍؽ{7‚‚‚°k×.”––bâĉr­oÅŠX°`zö쉩S§ÂÞÞFFF8~ü8Ö¯_ÃeMágmm @úŽqe’¹KJJ¤êijjbĈ011ðâËCBBvíÚ…;w"((‹-Bÿþý±eË–:÷ñþ÷ßÑ£G())áìÙ³¢»¶MOOzÊD%%%™ýX+**PQQ’’™ƒŸó­[·–;6B|Ož<^“µÞaÆU{‡».$wskŠ!++ d>åÔÌÌ êêêBgÏžUÛne’ú’cº2I™¤Ž„²²2´´´De’$]ÖZEEE™ï³¬uJ>oU?3:::¢_rXÍ¢¢¢`ggרa¼QÊÊÊÏO ®¢¸¸Xê&ãºp‡üú×®];tíÚ?þø#æÎ‹üNNNrOO·yófXXXàøñã¢;_Û·o¯v™ÐÐP¼ÿþû¢2Éì5%¨’„í«¯¾BÛ¶mkM[[Ó§OÇôéÓƒ~ø«W¯†³³3–,YRëòqqqèÞ½;ÊÊÊpá´iÓ¦ÖeÞTƒÆìÙ³ñé§ŸbÀ€8zô¨¨ë òòòdþÌÿºH.ºC‡ÅçŸÞ`ë­Lr¼…„„H½Ž¢¢"´lÙЪU+(**"..®Æ6%õCBB¤ü"YÏëú"&k–ÉS(ߤ/o£•+WòÃTª "䇩ˆ¥¥¥ñÃTjÁ} È«ˆåâký+/ÕÇ5'×aÕßamh“'OF||<æÌ™ƒØØX¹ï>—––âÉ“'puu%ÏÅÅÅ5>‚ûüùó¢ÿWTTàüùóhÚ´©Ì;h’¤»º)øjbccƒU«V¡Y³f8~üx­õáííçÏŸãÌ™3pp¨þA|©©©2û`W–™™ ooo$$$Ó«>}ZTÇÓÓS¸ƒ›kkkôêÕKT¯¤¤DH^$ý·„:mÛ¶mð»‚Æ ƒ¦¦&†Š=zàôéÓÐ××ÇW_}…ÇcôèÑØ²e zöì 555<}úgϞŽ{÷°|ùòzE__Ë–-ÃÌ™31jÔ(¬^½VVV(--Å¿ÿþ‹ýû÷£gÏžèÖ­[½®·ª¥K—ÂßßãÆÃ?ü€¦M›"$$›7oFóæÍ1a¡îºuëеkWôïß;wî„““ÊËËqýúu$%%aذaPRRÂüùó1sæL`Ó¦MÐÖÖÆñãDZoß>ØÙÙaôèѯe[4440uêTìØ±úúú8}ú4öîÝ  8𵬓1ÆäÅ ô[" @¾Äðeôêå…^½¼^ûzꓪª*Ƈ•+WbĈr ”غu+ˆþýûC]]EEEèÖ­V®\‰iÓ¦É\f÷îÝÀÔ©SQVV†òòrŒ7³gÏ®u}{öìÁìÙ³1þ|Ì›7:::ÂÉ’ŸØóðnÞ¼À‹A“………PQQAÏž=±aÆב˜˜( òZ´h‘Ì:qqqµ>Ž=33SêÉÁÁÁðbF‡/ÛéþôíÛüñ///œ9sÆÆÆ8{ö,Æ@II xþü91nܸ×Ë´iÓP^^ŽE‹áÈ‘#ÐÒÒBQQÊËËa`` šÉãu=z4±hÑ"CCC………°µµÅáÇ¡®®.ÔíÒ¥ >Œ)S¦ÀÅÅššš())AYY¾üòK 6 0uêT¤¤¤`íÚµøù矡®®ŽÂÂB¸ººâÀB_ìú&yZ¨¡¡¡h;~ýõ×Wz cŒÕ'’5ŠƒÕ«±cÇ¢¸¸¸Æ¾™cÇŽÀ?±Ý»w °··¯ó2K—.ÅâÅ‹VkÿçÇ#==¶¶¶¢©àÒÓÓqæÌ<þ:t€““rrr˜˜4iÒÀ‹A€)))hÛ¶-Š‹‹qåÊ$&&¢S§N2‹Ýºu 2{ôèþùç$&&¢;1ls IDATY³fpppÚîGáúõëHKKƒ­­-ºuëV§)ì ]c{{{¡ÛJII îß¿¡ÿ+ðb€¬©Æ$ÌÍÍkÄUyŸI¥¤¦¦"-- Rt¹wï444„8$±™ššÂÔÔTT799011U–——ãæÍ›¸ÿ>rssÑ¢E têÔIœ ¼˜5ãÑ£G°²²’ú5E²ïdm›äøqtt”J ÓÓÓqóæM<|øÚÚÚ°±±A§NDÛ—€œœ899ոπêÌÌL©ãR"11×®]CZZÚµk‡Ž;JMÑ(‘››‹ëׯãþýûÐÕÕ…«««Ìn>=Â7™™ GGGtìØQêW–ê¶«¦ý\õ}އµµ5/^Œ… âöíÛ¸ví¬­­Ñ¥K©ã>..ÏŸ?‡££c {ñÿxyyÁËË«NcÞU<ˆP"”mÔ¨QPSSûÏç$5Ẍ;W¯^Åœ9sàââ™uN å•““ƒV­ZÁÉÉIª_2cìíQ9~I.'Ð/®3|ËÎÎÆ’%K¸ôÿwëÖ-ܺu «W¯FÇŽùx©wáh -Z´y†½œû÷ïãÌ™3øõ×_‘““ƒo¿ý¶±CbŒ±7'CÒx¡˜žž¬¬¬xað, ÄÔÔ^^^µö9eu%K–@AA»wï®q† ÆØ›OQQººº¢þÚŒ±†eee///©îrLßfo¥O>ùŸ|òIc‡Á«'-Z´¨ñ!DŒ1ö&á;Ð ¤¡žDÈcŒÉ"™Š’ýŸ²²2©GÄ3ÎYê‚èÂO"dŒ1Ö˜V®\ÙØ!¼q$O"dbœ³ÔŽèÂòcŒ5&D(ÊÆ9Kí8fŒ1ÆcLœ@3ÆcŒ1&N wÈgŒ1Ö˜x¡4D(ç,µãºp‡|Æc‰JãA„²qÎR;N wÈgŒ1Ö˜x¡4D(ç,µã©4ÐÐPŒ;~~~ðóókìpÞX.\ýt¤££kkk4kÖLî¶rrrV§ºðôô¬µÞ?ü€uëÖáÔ©S°¶¶®µ¾ „o¾ù¦NqÔ·;wîàêÕ«HNN†‰‰ Ú·o:ÈÝNAAþùçdggÃÊÊ vvvu^VÞ}VÕãÇѽ{w|öÙgøì³Ïä^þM2kÖ,œ={·oß®—ö$ûöܹs077¯¶lýúõرcBCCahhXçö?~Œ ¬¬ ]»v…––V½ÄÍ{3#88¡¡¡èÒ¥Kc‡óFãºtéÒ¥Þ¾ýçääà굫 ‡“:uìTïÝ,..Æ7ðwÄßhfÜ <`ccS¯ëeÔ¨Q2:rwwÇ÷ßöíÛ×¹­ wïÞuªkii‰øøøZë={ö ÑÑÑuîÔÔÔ:Õ­OÙÙÙèÔ©>|PPP „ýû÷×é‘ÉçÏŸÇ´iÓ…òòrÀôéÓåºc#ï>«ª´´ÑÑÑÈÈÈx©åß$?Fttt½µ'Ù·¥¥¥5–=}úÑÑÑ(++«S»Ó¦MÃáÇEŸÅÈõʼn1öö‘Üä;vlc‡òÆãºÔW‡ü­;·bÍÞ5H3MCaTo©Âd“ u„µK×BEEå•×qþâyL]4©†©È1Ìb¤"Œ~6‚»±;voÚ }}ýzØ’ê™™™áܹs€˜˜:tûöíCÿþýqçÎ4mÚ´Ní888HÝÞ¸q#:„üíÚµÊÕÔÔêoÞEEE "|ÿý÷ðññ¥¥%.]º„uëÖáèÑ£˜={vúý¥¥¥AKK ÐÑÑy©;éü1¼¼¼`eeõ[Âä%Ùß/ó«DBB:tè€:àܹs¸téR=FÈKTT ª¢¬¬ ñññ rƒèmR\\üÎ]ë'Ð ¤>:ä/^¹¯mDn÷\¡¬Ä¤Im’°=q;bücpâЉWZÇŸgÿ„ÿ×þÈðÌ”^”U i6i8öìºèŠÈs‘PUU}¥õÔDEEE8ÉÛÙÙ¡_¿~HIIÁùóçqüøñ:3nÒ¤ ÜÝÝEe’;õíÚµ“z-++ ñññPQQMîÐfff")) öööry©¨¨@ll, `ggWã>ÍÊÊBtt4ôõõѪU+(+×üÑ566Ftt4„2ØÛÛÃÚÚ‡þìwXTÇ×€ßAšt°`CTD±W"(Š]ìcÀDco1–DcIL0ÆèÏ’Øbb‰½kb {C±£ ,J/;ß|»qÝ¥5™÷yöyعgfν»œ{î9gre@÷éÓ‡>}úœ/ÚÉÉ ''§lež?NXX...˜™™åyŽ×INNææÍ›˜™™Q±bEuÐEjj*!!!”-[;;;ciii\»v {{ûÒ¨¨(>|ˆ‹‹ >ü›õ~úô)ÿý7Õ«W×yÝîØ±Cý÷½{÷¤ý/aæÌ™2ú5TI„2Z“èèh2‰°˜(è…xçî~Þÿ3/j½Ðy<¥| §2N±qÛÆ|Ï‘˜˜ÈÈé#5ŒçWÖ‚[UoñéäOó=G~éÑ£é‘ŽŠŠÂÐÐ?üP§ìÀ100ÈuèÄÙ³g©[·.VVVÔ­[wwwLMM5j‰‰‰:ûÄÄÄТE lllðððÀÜÜœE‹åj>!sæÌÁÂÂj×®©©)“'OÖxì™ó† bccC“&M¨V­666j}Vèééé4Ë”)ƒ»»;±±±¹~œ_PfΜ‰B¡Ð(¡5qâDu[«V­°¶¶¦^½zXYYåúÙ®]»(UªžžžÄÄÄ™¡+}úôÁÔÔ*W®Œ½½½†A°oß> k×®eèСXXXP¯^=–-[Æ¥K—P(üïÿ㫯¾ÂÒÒ’:uêàääD—.]xñBû;xàÀ*T¨€££# 4ÀÒÒ’N:ñäÉ“|­Y`` …"ËWlll–}Uë­+$)**ŠFakkKíÚµ±´´dåÊ•ùÒQòî!gmd¡n¤ñœ3Òýް|írþ®ñw¶2ÏksæÌÑêÓ»woÚ¶mËÿþ÷?âããùòË/:t(fff|ðÁÙÎ7jÔ(.\H§Nð÷÷ÇÚÚš¥K—DJJ ßÿ=þù'cÆŒ¡sçÎìÞ½WWW"##9pàzzù»÷}òä W¯^¥N:9z±‹ƒ.]ºP¾|y6mÚÄÓ§Oùßÿþǘ1cðôôÌ6Þ}Ñ¢EŒ1‚îÝ»³jÕ*ŒŒŒHKK£Y³f}:ÏŸ×Ø±cñ÷÷×h‹ŽŽfРAXZZæêéˆ.:uêD÷îÝY¶l111|þùçbiiI—.]ò5¦D"‘üyóÿA%¹âôÅÓàžƒ>Ü~z›!»‡äo’S@åœÅb2bHHH(–ŒüØØXvîÜÉÖ­[100 eË– 2„M›6±zõjFŽ©–_³f |òÉ'¹ž£cÇŽtìØQýÞÍÍ ???BCCY°`ß|ó†a™žÜeË–©ßïÝ»kkk¦M›Æû￟¥ÁtåÊ~úé':vì¨ámÓ¦ mÚ´aþüù|öÙgØÛÛ«oæÎK¥J•¨X±"M›6Íõ¹½ÎÇLZZ_ýu¾Ç(Lj֬ɖ-[Ôï+UªD›6mX·nNZÁçŸÎwß}ÇgŸ}ƬY³Ôžöùóçsýúu~üñG† €——½zõÂÎÎŽ‰'ª×TETT!!!±~—.] ""‚'Ož¨CJÚ´iƒ k×®UЩ©©Œ5 Ž;¦þN4iÒ„òåË3vìX6oÞL¯^½ò´.îîÿó…OJJÂËË ===vîÜ™oºFÞ¶Ý»wcggǤI“¤-‘H$y@†pM"4-e EýĽ$”³˜0È÷?ð܃ƒØÙÙdHÕ«WÀÛÛWWW–.]ªÑwÉ’%T¬X‘6mÚäyÞ{÷îñǰzõjV¬XAùòåIOOçÁƒZ²­ZµÒxollŒ§§'wïÞ%"""Ë9öìÙCFF£FÒ:Ö·o_RSSÕœª¬ÞW_}Åõë×ó|>¯3}út~ÿýwœ¯õ) T¡9*š5k†Î5OIIáý÷ßgöìÙ,\¸ï¿ÿ^#LeÇŽXZZª¯fff´k׎ . T*5޵nÝ:ËD___xl}}}¼½½‰ŒŒ$55€îݻǀ´n(ýýýÑÓÓãìÙ³¹X‰¬BðÁpîÜ9Ö¬YCÆ ó=–Æ{+++êիǵk×äÆ ÿäN„ÚÈu#w"Ìé.& úÏ©gvß²²2k¡hëÖ–_Çþš¯9®‡\§ç×=‰qÎ&D ¥õK£¯ŸMœG)Uª”úñµªtÛ¶mµB2̘1c8yò$M›6åôéÓ\¾|™o¿ý6Ǥ±W‰ŠŠ¢oß¾:tlmm‰2?»×3×UžðWiݺ5üñ÷îÝÃÙÙYç\·oß C‡ZÇTeæîÞ½ dÐ `õêÕ¬\¹>úè#>ù䬬¬r}~YxÚ´iôîÝ›ü1O}‹’×C*LLL°²²âåË—Z²sæÌ!55•9sæ0lØ0­ã¡¡¡ÄÅÅé¬Ò’žžNFF‘‘‘êÚÈ@¶7¯ë™qJ¥’ÄÄD `Þ¼y,X°@K^©TæªL‹-4hƱ×+7¼Ê¡C‡èܹ³FÛÔñʺ¨Q£iTyzzæJG;;;ÆÏøñã ÁÃÃŋçÊ€^¾|9Æ £C‡lذá­HÌ/•*UbÆŒ´jÕ oooöï߯±«b5¸|ù2­[·.Ò'$¯¢ú<Ú¶m[¨cïß¿ŸáÇӾ}{æÏŸ_(c:tH˳®ºnó³K¤D"‘üWy{\Q’iÖ¤Á›ƒió° ®']q:ãD•“Uhr¥ »fïbôàÑžÃÁÁK/ñA‰p;åF™Óe¨p¢§U4÷ïß'888Ë~üñ‡F¸ÇsúôiêׯŸmõ???LLL˜4i’VÉ:Ȭ_¬ŠÓÕå-¬\¹2VVVêÐìXµjŸ|ò >>>lÙ²%[ãË—/Y¼x1{öìÉqÜ7Iùòåù믿ptt¤U«V?þOE˜¾}ûËwß}§³¯®õ,(5kÖ¤V­Z,X°@gܶ"ËRˆÙB¯^½¨Y³&6l(´‚}ûöiÄß»wk×®ñÞ{ïå9,H"‘Hþ˼»î¨wŒÂ ÈwrrbÿÆý@¦ÑfccSèäMLLXùcfmؘ˜LMMßÚÇ» 4 ^½zœ?žJ•*i%I儞ž;vdÓ¦MôèÑ???ž={Æ÷ßOãÆ9uê”ÎxjSSSZ·nMÿþýIHHP—ºûöÛo³¯B… Ìš5‹#FP·n] €³³3?æÒ¥K¬_¿ž{÷îaiiɈ#¸ÿ>íÛ·§B… DEEñûï¿óäÉ&L˜í>> 0€-[¶0iÒ$Ο?O»ví°²²"<<œC‡‘˜˜¨ö¶%J”àçŸÆÛÛ›:uê0räHjÖ¬Ibb"·oßfýúõ|ÿý÷øùùåiÜþýûóâÅ š7o®sÛ1cÆäk—0¥RIÇŽyÿý÷‰eöìÙ1sæL ¹Í›7³yófNŸ> d–ÖS%U®^½ºPv=•/r'BmäN„º‘;æŒ4 ‹‰¢Èp}Ç´¢ ¸½R...y6Ö8þ<Ì1yÐÞÞWWW²t‹-¢D‰lÛ¶íÛ·ãææÆÔ©S±±±áóÏ?×µ¶¶ÆÕÕ•Õ«W3wî\ÆÏ“'O¨U«üñÞÞÞ󹺺jy¤‡ F­Zµ˜4iS¦LáÅ‹ê݇ª®æÐ¦M~üñG¾üòK’““qppÀÇLJµkתwÌŠôôt\\\¸yó¦NU% ÈÜýÑÕÕ•råÊiȤ¥¥©«‚¨Îþ)õöª±ª5{õÇØÎÎWWW!%...z¨t³±±Q·ÙÚÚrøðaüýýùì³ÏX±bìÙ³‡… ²téRvíÚEjj*¥J•¢Q£F 8PÝßÔÔWWWÌÍ͵æ/Y²$®®®:¯}ÕõóªG¸Aƒ\»vqãÆ±téR"##Q(”-[–víÚQ§Nµl™2eÔk˜¶¶¶¸ººòçŸê<>lØ0ŒŒŒÔkûª1««MµÞ[¶laòäÉ|úé§ÄÄÄàááÁÚµkiÒ¤‰ÆøQQQêÏØÈÈWWWur+ 3/@òö#“µ‘I„º‘I„9£ò—°È äøñã4oÞ???Þ(ÕöÔòÇ-ïøûû³}ûvüðÃ\ë’×5{ÈÈHš4iÂÔ©S™6mZžû¿MLž<™íÛ·çêfÂÇÇ///¶oß^dú(•JnܸÁùóç9þUªTyÓª¼U¤¤¤`ddô¦Õx«‘t1QЀü¡}ûòôÂLJdÿ‘=22bÿ™3èéåýáÂâ Ø5>6%Kf+w+9™-ý•k#,/lß¾˜˜µ±ALL “'Ofúôé¹6ˆ¸té’FÛãÇyþü9ÎÎΔ|å<Ë”)S8'ð–MÓ¦M)Y²$uêÔ¡qãÆ;vŒ'N°lÙ2‚ƒƒqssËqœµk×Ò¿6‡<ÐÖÖÖ¸ººÊã·+W®P§N¶ØØXi@ÿK™9s¦Œƒ~ U¡ŒƒÖ$::ZÆAç€4 ‹‰‚^ˆïÈÑnݘôüy–2» ›2%_Æ3@¯¾}93g¿„„d)óø²mÛ"1žU899© ßôôt:D¯^½˜:u*íÛ·§nݺ¹§víÚZe›FͼyóX¿~=7.tÝß ùòË/1b¶¶¶@¦WzÁ‚Œ5Š!C†ð×_å8Ž›› .¤^½z$%%ѲeË<ë2pà@˜ç~’¢ÇÖÖ–Y³fQ¯^=jÔ¨Q¤ßkÉ›GÏÚÈ$BÝHã9g¤ýŽÐÜË‹•+óòÜ9̲ù¹\9Ö—ï9¬¬¬(ëëË•¥K©%„N™ø´lJ”(A›6mð÷÷gÉ’%üõ×_8;;óûï¿S»vm-ïdzÕ.\¸@§N°¶¶ÎÕ>>˜ššf9¾••_}õ•F›B¡`È!Ìœ9“3gΞžž£¾ 4 Aƒ@fø@$%%‰´´´,e²çØáÃâ›Ò¥…­×n±àë¯ ¬ë³gÏD ³³Î9¢@|Ò¶mçÈ{{{áìì¬Õ>þ|ˆ/¿üR¤¦¦ ѰaCcxzz {{{‘ššªulÔ¨Q§NR·mذA¢téÒ¢^½z¢téÒeË–W®\Ñè$±`Áabb"ªW¯.Ê—//Ñ¢E £!h}®·oß 6€033eÊ”€¨V­š¸qã†Z.>>^-gcc#5j$*W®, …X·n]NK™%UªTVVVyîwêÔ)ˆQ£F婟jÍBCCÕm&L€Xµj•066ÖÖÖÂØØX¢qãÆß“°°0ˆ©S§ªÛRRRDŸ>}„¾¾¾X¸p¡º}çÎÂÆÆF¢|ùòÂÄÄD¢G"%%E-·wï^ˆ   áìì,ÌÌÌDåʕŌ3ÄÅ‹ &Mš$<<âñãÇ\¼x‘»wïâèèH`` =*}$ïr'BmäN„šìÛ·Ñ£GsêÔ©7­ÊÛÏ›¶àÿ ooïers·wìðañík^è= èðç÷õ D€ïóÀbðLÙÛÛ ;;;±wï^±wï^ñã? oooˆJ•*‰—/_ !„úúúbèСýG% …¸{÷®Îñuy ³BåU|ò䉺MåMíÚµ«–|™2e„‰‰‰HLLT·ñšzÓ¦McÇŽÕê?pà@ˆ!„³fÍ€¸víZŽºæ†'Ožˆ2eÊKKK™çþEá3fŒ†ìåË—  ¤n{Õ.ÜÜÜ„8sæŒFß>úHâÏ?ÿÔhW*•¢lÙ²¢|ùòê6•ºR¥JZúª<ÐeÊ”J¥R㘋‹‹°°°P¿òä‰0225kÖÔgË–-sæÌQ·Äý:W¯^æææ¢N:êï…JG…B!¢¢¢4äçÍ›'1nÜ8ãIô¿éQÔæùóçyþMû/àíí-¯—1ÐÅDaä7÷òba•*ÄŸ=‹*ögÖ6kùL|+ üµk\~øÚÿß6ÛÞž±óæÊø9ñøñcÚµk§~¯¯¯ŸŸ³fÍRÇý–+WŽ:°f;ÿþ{LLLHNNfÕªUøøøP©R¥<Íyÿþ}~ýõWnß¾Mtt4DEE©ÙØØhÈ{{{káããÃÊ•+¹wï^–..\¸@Ù²eÙºu+B„(•J,--¸vínnnôìÙ“ &àëëˇ~H÷îÝs@ù:/^¼ }ûö<~ü˜íÛ·ãàà¯q ›=zh¼wwwÇÜÜ\§—4$$„&Mš`jjJpp0+VÔ8~öìY¬¬¬xùò¥zm•J%B\\\8r䉉‰˜˜˜¨ûtèÐ!Kݺuë†B¡ÐhkÑ¢?ÿü3 ”*UŠ+W®’’BݺuÙ¶m›z>!IIIèëësóæÍü,M¶DEEÑ¡CÌÍÍÙµk—ú{‘””Ä;wpwwÇÞÞ^£*üúõë…®äÝ@&j#“u#“sFÐï ÃgÍba·nL|þœ½xþ9Fy(ï–FÇÄðiݺ¬xð€( ÎÃWW×B#+Ù³gYº|ùò:“݆ ÂÎ;Ù°aýû÷gãÆ<þ<˰‹¬Ø»w/:uÂÔÔ”êÕ«S£F lmm¹zõ*7oÞ$>>^«*±îU5jÄÊ•+yøða–ôƒ˜6mš–q™?æ111T¨P?þøƒ~ø   ¾ýö[ÜÝÝ6l¹. —@‡¸xñ"ëׯkÂ7­…B±±1iiiZ² ..Ž©S§jÏwïÞ%55U«ìž sssž={¦a@g £ë&¬T©RjýT›älÙ²…;vhÉ›ššê¼~ Bbb":u"&&†ãÇãää¤>BçyU¯^=Ë›‰D"‘ä i@¿ƒ¼ê…^^¾Cf 6å|}}©T©K—.¥ÿþ,Y²ºté’§ù>ýôS ¹yó¦†×nĈYö¹r劖‘¢ŠÇÎλ«2v®]»–«ÚÓ>>>øøøðèÑ#¶oßÎÊ•+Iyñâ…–gZ"‘H$yG&…½áðY³èQª^yðDæ•Ñß~Ë^£÷9/(  Dpp0ëÖ­ãäÉ“|ôÑG¹.%™žÄ›7oÒ¾}{ ÃBÁ¶mÛ²ìwèÐ!­¶?ÿü“%JP¹rå,û¹»»°ÿþ\ë™åƆ Ʊcǰ¶¶fõêÕ9öIII¡k×®>|˜_~ù…>}úäiη ===~þùg† Æ”)SøüóÏ5Ž»»»sõêU"""ŠM§š5k°k×®b™ï³Ï>cûöíÌ›7Oç“333Ê–-˹sç4vô„Ìë jժŢ«äíC&j#“u#wOÎi@݉ðuš{yáо=‹Àû¬ÂÊÊŠº}ú«÷9¯ôïß### €B¡`À€yêo``€½½=$..Nݾ|ùòluïß¿_#–tÛ¶mÜ»w~ýúe[ŸÙßߟ5j0~üxNž<©q,55• 6¨ëñþñÇÜ¿_C&,,Œ¸¸80]¤¦¦Ò½{wöïßϼyóèÝ»7ÉÉÉ/ñJ­ïÈÈH7n̘1c´ÆRɧ¦¦™5¬UmÙêQØ( .\ÈgŸ}ÆÌ™35j”ú<¦L™@¿~ýˆŒŒÔè­3Ä¢ 8;;3`À6mÚÄüùó5Ö2k7–ÑòÓO?1wî\F­®<¢‹±cÇ’‘‘Á?ü n‹eéÒ¥h=YIII!99YýS©Tª?_]¡4’w—™3g¾iÞ:T;J4)l›å߈ á(&Š" ùºuèëëú¸¯òå¬YE>GA°µµ¥{÷î¬]»___±±91iÒ$FŒ““^^^DEEɸqãøþûïuö>|8M›6¥iÓ¦$$$pâÄ ªW¯®µyÉë²~ýzüýýiÞ¼9µkׯÙÙ™ÇJll,±±±”,Y’µkײjÕ*jÖ¬‰‹‹ ÿý7.\ÀÐÐ0Çy®_¿ÎîÝ»ÌЇ‘#GjÉ„……Q¡B Óˆ:}ú´:‘QETT”Öît .TÿÃQtÅ*qô«¯¾"11‘%K–àááÁ²eË9r$...xxx`eeExx8ׯ_§Y³fyïÉ ³gÏ&**ŠQ£F1wî\ÜÜÜHLLäÎ;DDD°}ûvªU«VàyæÏŸÀÁƒu†8;v 333† ÆÑ£G™1c›7o¦B… œ;wŽ/^0oÞ<\\\4úÙÛÛkÜ<þùçŸ ÏþEÈÏR™D¨™D˜3Ò€~‡)ö¸çÉ“'çyÎ&Mš°víÚ\%vêÔ ‡áÇSµjU6oÞL||<>>>tïÞ¸¸8¬¬¬4’ɼ¼¼ â“O>Á××—]»vñàÁ‚‚‚4hº¯Š   u؆ www.^¼Èo¿ýÆåË— ÇÍÍ=zйsgõn}3fÌÀÓÓ“sçΧ§'Ó¦MÃÓÓ3G´““AAAÙÊ”.]Zãï   ­Ä933³lÇiÞ¼y¶sÀ?kfgg§nk×®–––:wzœ}:ÕªUãÁƒ\»v wwwñññaýúõ\¿~¸¸8š7oÎØ±c騱£º¯««+AAAZ1ÄðÏÚé:7Õõ£20,,,øý÷ßÙ³gÇçÖ­[XYYѼysÚµk§+ÿþûïS¿~ý× 2M_½NÇŽ˳gϲ”WíBh``À¶mÛØ´i“z'¡C‡Ò­[7ç;mÚ´,w!|ýÚ•H$I& ñú3GI¡d÷Ÿ‰6ÔªU‹äädnÞ¼™§øg‰DòßÁËË ///¦M›ö¦U‘HÞz¤M’32º˜ù…Kll,—/_fܸq\¿~~øAωD’ 2‰P™D¨i³äŒ4 ‹ _¸¬_¿.\H@@~~~oZ%‰D"y«‘I„ÚÈ$BÝH›%g¤Ë®˜ù…K·nݨS§Žzs‰D"‘d|¯L"Ô´YrFÐ’w;;;D3‰D"‘H$’âB†pH$‰D"‘H$y@ÐÅ„ È—H$É›D&j#“u#m–œ‘t1!ò%‰Dò&‘I„Ú¼ÓI„’ï'C#–6KÎÈèbBäK$‰äM"“µy—““Ó‰Z…¾™>¥j–ÂÔÝC'ÃB[Ú,9# h‰D"‘H$’wŒÄk‰d¼ÌàÅ©¼8õkL=L±ð´xÃÚýû‘!‰D"‘H$ï®'h5§=K#åo¿\Hº˜ù‰D"y“È$BmÞÕ$ÂäûÉdÄg臂"„àÑ£Gœ>}šû÷ï“‘¡û77(•J’““IOO/D ß6mÚÄàÁƒIJJzÓªH$E†L"Ôæ]M"Œ¿¯³]a ÀÄÕ¤ÀãK›%g¤]LE@þÕ«W }Ì×¹~ýzŒ®¼âì쌱±±ÆËÕÕ•Ï?ÿœØØØÌ‰'Ð××ÏÕ8åË—'((H£m×®]œ8q‚¡C‡R®\9u»…Å¿+áâÅ‹,Y²„:ðÅ_P¥J>ÌêÕ«1bzzz :4ÇqΜ9ÃO?ýDÕªUiܸñ¹H$É?$ÝMB™¤Ôy¬TÍ‚‡oHr‡4 ßQfÌŸOÜG1rÆ ö¯^]$s9vŒpWWþ·nûõ˵áZPÌÍÍ=z´úý¼yópssãôéÓüþûïøùùåj'''&Nœ¨Ñʼn'èׯ7.T½ß&,,,¸qãU«VU·ùúúÒ£G4hÀ?ü+ºK—.ÄÅÅaffFpp0Mš4)Jµ%‰D’‰!º½Ïz%õ0®b\ÌÚüw‘!ÅÄ_ýE`` Û·o/ðX‰‰‰¬?z”Œ–-¹,7oÞ, µÿÃ<ïÒ…ð¦MYVDFznP(påÊ¢¢¢ðööÎò±ÛO?ý„··7‘‘‘¹ÿÁƒ|úé§Ô©S[[[œœœðôôdÛ¶mYöB0}út4h€££#:tàòå˹>§ .н{wªT©‚““¾¾¾=zT§Ü”)ShÚ´)–––T¬X??¿ÃwŒ5Œgõë×§V­Z„……å*–¹téÒ˜™™åú¼t±téR¼¼¼xúô)3fÌ Y³f˜››sþüyÒÒÒÔí¶¶¶ÔªU‹1cÆ aIß¾}qvv¦zõê|òÉ'¼xñ‚Ž;2yòdµ\xx8^^^¬]»6K}bbb²ÕûêÕ« <˜š5kbeeEùòåñññáÈ‘#Z²sæÌÁËË‹ÄÄD&OžLãÆ133Ë69iíÚµxyyÎÿþ÷?š7oŽ­­-žžž;vLgŸ-[¶Ðºuk©^½:~ø!>Ô){éÒ%zöìIµjÕ(]º4îîîŒ=Z+Îû—_~ÁÛÛ;;;ÜÝÝ4hÏž=Óyu=8@ûöíqtt¤mÛ¶ìܹ€'Ož0lØ0jÔ¨³³3#GŽÔ ‰™9s&^^^$''3zôhjÕªEÙ²eéÙ³'ááá²wîÜÁËˋ͛7sàÀüüüppp`„ Y®©${d¡6ïZ¡2MI íßF“ê&(J,|cûöíò×_hœÿÒ€.&*T¨ÀŠ+rí=ÍŽóçáë @t×®|<}:7 õµêÀn•)%K’ôÞ{ÌÛ°¡Xc¡_Geì”,YÒÒÒ "--MC.##ƒo¿ý–ääds5ö©S§øý÷ßñööfêÔ©ôèуˆˆºuëÆ²eËtö=z4[·n¥gÏž|òÉ'\¸p¦M›œã|+V¬ Q£F:t///¸xñ"-[¶Ô0ø®]»F³fÍX³f 5"((ˆ~ýúñìÙ3BCCsun¯“žžÎýû÷©\¹2%JϨ{÷îqôèQ>þøcÖ®]K“&M©©©Ùê½{÷nNŸ>M‡øúë¯iÓ¦ !!!x{{³k×. Ù[·nqôèQúôéî]»hÑ¢Ÿ|ò %K–ÌrüððpŽ=ÊÈ‘#™4i...´lÙ’àà`Z¶l©õO}ÆŒôèÑƒØØX>ýôS:uêÄÎ;©U«–Ö ÕŠ+hРgÏž¥{÷î|óÍ7´mÛ–­[·§–>|8ü1J¥’‰'Ò²eKV­ZE­Zµ4ÖNµž+W®¤sçÎ(•JºvíÊÉ“'éÝ»7çΣS§NìÝ»—:P²dI,X /ãÆ Ž=ŠŸŸçÏŸ'00¾}û²oß>4h 1g||ÿ?q=z°ï§Ÿøã·ß 4Ç‘cÇx^±¢†÷YERóæÌ›5«È+r899© èì4h³gÏfÙ²eñóÏ?£T*ùä“Oò4ßĉ™3g}ûöÅÓÓ“5j`kkËš5k˜1c†NO£V›½½=ixe…ª–u•*Utz*7nL•ÿ¿ 266æöíÛlÙ²…åË—³dÉ,X@ãÆY»v-+VÌÕù]¸pvíÚáääݱ±ÉU¿ÂæÕ²€:QÍÖÖVç¹T­ZcãÌŒòŒŒ „ê÷¯bdd”'úõ¤¬èß¿?›7oføðáÔ­[—êÕ«ciiɼyóX¼x±ÎëâõsÌ ¯zºU( =_¾| üs½ŠÊØT%]ª®?SSÓlç}ùò%FFF”.]:Ë1Uóæ¤kVí {½uGVßY¶ð¸qãÕªU{Ój¼U¨òBª‚ó©¨IÑ<¨o®Qy#ÇòKJJŠÖC‰&Ò€.& º«Obb"ë&ýóÏu X[sE©äæÍ›ª =qî\b Ò}°½Ð¹¡råÊ´iÓ†_ý•)S¦°|ùrš4i‚»»{žÆYµj...¬\¹RãÆ »ªGŽá½÷ÞÓh;xð ðOè….TŸÍ Aƒr¥g‰%èÝ»7½{÷æùóçüú믌;–E‹1kÖ¬û_ºt‰Ö­[cmmÍ¡C‡4<{oš *`hhˆ““SŽ˜˜˜`ccÃáÇQ*•tUÛëòé%~3gÎä¨ÛË—/Ù²e ÞÞÞ,X°@㘪zHq¢úç~èÐ!Þÿ}cþù'€ú)‰JöÎ;xxxd;æñãÇ9qâ„ÖÕ˜EeT>>˜ššrèÐ!Ê”)“¥¬Ê SœÛ·êëëóÞ{ïqàÀx߬ðõõåùóçZ•NöîÝ«%[¦LJ”(Á4Ú?~œe‰¸Wyúô)B\\\4Ú#""8{ölŽý ›æÍ›h}îñññœ8qGGGµ­ºÆ~üñÇ|Nhh(5kÖ,² †^Ÿ3**Š«W¯Ò°aC­Iá!gmÞ•¯'‚î½SŠ$|CÏ9#=ÐïîUªð­™d÷øÙÚ“V­´¤bÅŠøúú²oß>µLRR+W® ,, Ȭ¼xñbõùäÕãÿ* ,ÀÃÃ6mÚðÝwßѬY3 ¹ÿ>¿ÿþ;fffŒ?€éÓ§³cÇüüü˜Hìç IDAT}ÊøñãIMMeÆŒê OOOz÷îÍòåË122bРA”)S†°°06nÜÈ„ °²²" €~øùóçSµjUÚµkÇÇþÿùß}÷]‘ÏÞ½{Y¼x1½zõ"::š!C†0uêÔ"›S"y—É*|£„U u“1o¦øÇ‹Â,c÷oGW»œ˜>}ºÄˆ#r”ÕUÆîÒ¥K¢|ùòê’Y€èÑ£‡Xºt©ÄáÇղªÒcüñ‡¨T©’FŸI“&i”B»Œ™%ë¦N*ŒŒŒ4úëéé‰fÍš©Ë~Mš4I”(QBCÆÑÑQˆ/^d{ž¯–Ëꦖ €ðõõÕ'222Û1æÎ›ãšë*÷*gÏžõêÕÓÛÖÖV,_¾\C6$$D4mÚTsssÑ¡CñèÑ#ahh(  !&jÖ¬©±¾&LÈu»Ã‡  FŒ!¾ýö[­õS•±KJJÊq=T¨®¥ÐÐP­c®®®¢Q£FmOŸ>:uÒЧdÉ’âÇÔꟘ˜(ÆŒ#ôôô4äíííELLŒZ.""BxzzjÈXXXˆµk×jŒ—]YÀF WWW­öE‹i}×TeìΟ?/¬­­ÕsˆÅ‹kôW]ù¹Ærƒ,c'yWI‘.¦i—® ›&bÆä<@>6IÎ(„ÈeF$ß’’’ºu법ùˆMåÌ‹w{âĉ|÷Ýw„„„àææ–­ìË—/IHHÀÚÚZãQqbb"çÎ#))‰:uê`ggGRRqqqXYY©K‡%$$ðòåKlmmQ(„††òðáCuŸ×‰ŠŠÂØØXç£ð¤¤$nܸAxx8666¸ººj%ø%$$Btt4UªTQW6ȉôôt*º°µµU{Ë322xòä‰VR™R©ÌÖÛjnnžcÅ“øøxâãã±³³ËöÉÈÇ %==2eÊàææ–¥7?%%u¥Ž*UªðÍ7ßðÅ_hÈ¥¦¦rùòebbbhÔ¨–––:õQ]öööžìØØX.\¸€‚ºuëRºtik@¥ß‹/HLLÌSŒ¹®qT<}ú===¬¬¬´ú=zôˆ,--©Y³¦Î>qqq„„„ðìÙ3Ê–-‹‡‡‡ÎÏàÁƒ\»v {{{ÜÜÜ´6€Q]fffZóÅÄÄ T*µ®]Õ÷çÕïZ`` +W®DAzz:W®\!&&†úõëcii©Ñ_u çæË ^^^xyy1mÚ´õ®"“µy’_¿ fŸîSË +ƒmá‡=õéÓ##£ÿ¼M’Ò€. çСCÙÊ€4 óŠÊ°lÓ¦ [¶lyÓêHŠ˜§OŸjjôë× 6péÒ¥…“HŠžW èâFЙë/ÿÇhË´iÓÞê8èÈŸ#Iy¨]ÉËÐÞ§!NE2gË–-)_¾¼¼^²AÆ@2 ¿p9wî›7ofÇŽ¤§§3{öì7­’¤¨S§nnnÔ¨QƒÔÔTŽ9µk×=z´4ž%’Æ6o{aÚó4Æ3mígi³äŒ¬Â!y'¹{÷.;vì V­ZlÛ¶-×5‘%ï6S¦LÁÄÄ„ãÇsöìYêÖ­ËÎ;™;wî›VM’  TfS"ù¯‘Uò ÈÍSÞ4Ò-y'QÕF–ü·8p |ÓjHòIPPAAAoZ ‰ä!1$Qg»Q#J”–&Ü›Dz ‹‰Â؉P"‘H$’ürãÆ7­Â[Gzz:wîÜyÓjè$õI*©Ñ©:™Ô,xbmvH›%g¤]Lç‰D"‘¼ÎÌ™3ß´ oªßF²ò>£ÈÜ}°(‘6KÎHº˜ù‰D"y“È$BmÞæ$¬âŸK:—Dß,ë » i³äŒ4 %‰D"‘HÞ"R#SI{–¦ó˜L|;´D"‘H$É[DüÕxÝô T i@¿ Hº˜ù‰D"y“È$BmÞÆ$B!‰×tÇ?W2FϤèM7i³äŒ4 ‹ /‘H$’7‰L"ÔæmL"LH%=.]ç±RîÅã}–6KÎHº˜ù‰D"y“È$BmÞÆ$¬’%˜T+Úòu*¤Í’3² ·ä­bÕªU$&þóèÊÜÜœŠ+R·n]ŒŒŒÞ fo†¤¤$6nÜÈÍ›7‰‰‰ÁÚÚšªU«âííå\xx8'OžäÒ¥K( <<>ž .póæM222øè£044ÌÏ)I$I±#”‚„kº hccôŒ¤ßómAÐ’·ŠñãÇë|täììÌâÅ‹iÛ¶m‘Ì»jÕ*ÆÏž={¨[·n‘Ì‘WNœ8A=ˆŠŠBOOŠ+©¾ÁˆŠŠÂÞÞ^-/„`îܹ|ñŤ¤¤`ccÀÓ§O)Y²$ßÿ=Çט#""‚!C†0hР|ÐBúöí˺uëP(TªT‰°°0”J%:t`ÇŽèëg_néĉx{{`aaAFFñññLž<™?ü+V P(rÔÅÇLJÇ£T*ÕmþþþÒ€–H$ï ÉaÉd$dè<&«o¼]È[™bBäçžråÊñüùsž?ÎÙ³gùì³Ïxøð!þþþüý÷ßE2gbb"ÑÑѤ¦êÞõ©¸INN¦W¯^<~ü˜uëÖñìÙ3îܹC||<—.]b̘1hô1bcÇŽ¥aÆܼy“'OžðäÉ®_¿Ž‡‡#FŒ`ܸq…ªçš5kX·n­Zµ"<<œ;wîðèÑ#ºtéÂîÝ»Y´hQŽcT®\™íÛ·Cll,ÏŸ?çÔ©S4lØU«VñË/¿äJkkk† ÆŠ+hذaAOM"ù×!“µyÛ’³ ß0T`\Õ¸Øô6KÎHt1QXù)SHz3FžB_i¢Ýý@OOKKKêׯOýúõ e÷îÝìÙ³‡¨eccc¹|ù2J¥’Zµjammå¸>äüù󘙙Q½zuÌ0†gÏžÉýû÷LohéÒ¥Õý¸páúúúÔ¯_CCC=z„¾¾>j¹`bb‚­­-IIIœ;w…BAóæÍÕ2/_¾äÚµk<þ777­pŒK—.ñ÷ßÓ³gOüýýÕí …‚Úµk3gÎ-ùE‹ѰaC<¨a\W¯^£GÒ°aCæÍ›ÇÀ©ZµjöB.9yò$ùä@"âààÀ_|ÁŽ;8yò¤–×ûuÊ•+G¹råÔïK”(AãÆùòË/éÔ©'Nœàã?ÎQ— 6¨ÿ^·n]~NG"ùW3sæLýª$·!ZdCuWß(U­zÅç󌎎–qÐ9 èb¢°.ÄĉÄ+”±òŠ^I½b1 uѱcGvïÞMXX7;eÊfÍš¥ñÈ~ĈÌš5‹’%KªÛ.\ÈÂ… ¹yó¦Æ˜sæÌa̘1Lš4‰%K–Э[7õñ &¨³Ö·lÙB`` ññ™µ9Y¿~=ãÇÇÁÁ#Gލû¹ººâç燧§'cÆŒ!--úõësöìYÒÓÓ™1cß|ó ééÿdY÷èуeË–©oââ2?ãœÂT̘1¥RÉÔ©Sµ<Ó†††L™2…îÝ»óÝwß±|ùò\›nnn@æ Á«¨Þ«ŽçU¨Š»»{¾ÇH$ÿ gmÞ¦$¤;I(“•:wø†4žsF†pHÞ þøãàŸ/õ¤I“˜9s&äÖ­[Ü»w±cDz`ÁFŽ©îwíÚ5FŽI¹rå8uêñññ„‡‡³~ýz*UªÀôéÓ™6mëׯ'44”ÐÐPÆŽ À•+WèÙ³'•+WæìÙ³ÄÅűqãF†ÊÇuê{ðàA,XÀ¶mÛ SèãÆcúôéôéÓ‡}ûöqýúuÆŽËž={8p º:u066fëÖ­|ñÅ„††f»>W¯^¥D‰øúúf)Ó¾}{ —/_Îv¬¼Ð­[7¬¬¬ âĉdddpæÌ¦OŸŽ¹¹9½zõÊó˜OŸ>åÿØ;ï0§ÊìnêôJqè]ª¢6@) (eUD]À† ‚²bY± ‚X@¤éºþYtqDd]š8" "E©Ò™˜Ê$™ôûû#3™’›If&$x?ÏsŸ$÷Þ¼9InN¾÷ÜsλråJ^yåRRRª5†@ Ô6¼¥o¨"UD´ŒPÜ&B@ š£G2}útÖ®]KTTýúõãܹsÌ;—¶mÛ²hÑ"Z·nMóæÍyçw¸é¦›øä“O8|ø0àв,3yòdnºé&¢££iܸ1#FŒ`È!Ô¯_ß]Œ×´iSÚ¶mKÛ¶m©[·.o¾ù&²,³`Án¸áâââ}ºÜk9Ž?NãÆ+XGDDpÕUWyDâkBJJ ‡¦AƒtïÞèèhn¼ñF´Z-‡¢uëÖ~UR,X·n]î»ï>Z´hÁáÇiذaÀì‚pD¶É*RÜÕ. Ií»Z\„€"!ßNž<‰$IH’DëÖ­ùÇ?þABB‹/¦yóæìÞ½‹ÅBß¾}=žÛ·o_Û·o wïÞhµZ^ýuÖ¬YS­ïaçÎÄÇÇ{´T«,Ú_.ç`óæÍØívFE~~~¹eРAå¢Ã÷Þ{/GeÍš5 2³ÙÌ{ï½G‹-x衇(*r9[³ÙŒÍf#:Ú÷%¾èèhŒFcÀŠ%F#ï¼óëׯ§uëÖŒ=šöíÛ“žžÎÌ™3Ý©(þЭ[7fÏžÍßÿþw:tèÀÿû_yäÑÐ_ ¢ˆÐ“p)"42á´†GúÍâB@ !ü'!!3f0cÆ æÏŸÏºuë8vì÷Üs€»È¯wïÞÏ-YWâëÔ©Ã|ÀéÓ§2d <øàƒüôÓO~Ù"Ë2§N¢{÷îÑÝ:¸[ÅU¤gÏžh4åK JÒ0~øaË-%ÑðS§N•{ŽJ¥âÎ;ï$--sçÎñÅ_Ю];>ýôSÞ|óMÀ%ŠSRR8yò¤Ï÷rúôiêÕ«°Ön‹-bæÌ™LŸ>dz`Á8ÀÂ… yÿý÷yçwü«}ûöLœ8‘Y³f±ÿ~æÎËÊ•+yÿý÷b«@p¥#f"ô$\f"ô–¾¡ŽQÑ,øéB³øF‰@%ä«£Õhëx‰ƒ`5pçùçŸ÷º½$Òªô/Yë^÷øã3fÌÖ®]ËW_}Å—_~É¿ÿýofÏžÍĉ+µE’$9räˆÇ¶œœrssŸå9[TIaãW_}åõx(ÛÍ£"†¿þõ¯$&&Ò§O6lØÀ´iÓhݺ5ééé;vŒ–-[*>ÿ÷ßÇb±pÍ5×x}ª²råJ¢¢¢ÊuFøÛßþÆ”)SXµj•ÛÆª2zôh^yåV­Zå>YÕGzE„N³“¢£ÊéÑ¢‘TÁOßE„¾º–wsq7Ç…ÚŒRRü·aÃüñrÛ6lØà‘{«Ñh:t(C‡eáÂ…\}õÕ|ôÑGnéꯩ”ÚЦM¶mÛÆ‰'hÖ¬™{ýÆËuñEûöíWë½»îºËïçU¤U«Våºx¤¦¦’žžÎŒ3øøãŸ7cÆ ÷¾"##µZím/Y—‘‘Qí±£¢¢ˆˆˆÀf³ÕÔL@ [LMÈvYq›˜<%|)‚ZÇÍ7ßLóæÍÙ¼y3.\p¯/((àÛo¿¥~ýúîüè³gÏâp”ŸÕI§ÓKvv¶{]É,|{÷îõx½‘>wî\÷º¢¢">úè£*Ù}×]w‘’’ÂÔ©S˽v çÏŸwç5ïß¿ŸU«VyØðöÛoÐ¥K÷ºÔÔTºvíÊâÅ‹/G¾÷Þ{¬X±‚›o¾Ù :wîLaaa¹ÌkÖ¬áüùó\{íµåÖ/Z´ˆW_}µœø?xð Çût:,Z´ˆsçÎy̘––Æ«¯¾êµŠ@ Ô&¼¥oh4艙Tîo¿ý6Û·oG¯×Ó¿î¿ÿ~¿sIEB~àÐétÌž=›{o¼‘Q£F¡ÑhX²d ,^¼˜˜W¿êþóŸ|üñÇ 6Œ–-[b4ùî»ï8pàÿøÇ?Üc¶hÑ‚–-[2eʾþúkRRRúè#î½÷^Ú¶mKjj*-[¶$++‹ß~ûuëÖqèÐ!š5kÆ™3g1bMš4áæ›o¦eË–œ={–ï¾ûŽS§NQ¿~}wë=p¥š¬ZµŠ#FðÔSO±lÙ2wÑãöíÛùùçŸéܹ3ÿùÏí]³f×£²=®+2cÆ 6nÜÈ#<ÂâÅ‹¹ãŽ;øþûïÙ²e Æ#zÑ¢EüöÛo<ÿüóî¨õ«¯¾ÊÖ­[éÑ£5âÏ?ÿdË–-äææÒ AÞ}÷Ýrc¤¥¥±dÉ àž¼\' kÖ¬JO„ŒF£!&&†µk×z}Á•ÀÁƒiÛ¶m¨Í+ìv;'Nœp_Ù 6£ƒ¢?+IßBÓ}Ãb± ×ëCòÚµ! «@Ó¦MéÛ·/F£‘©S§b6›yâ‰'üz®HÈ÷’®¾2dÛ¶mcêÔ©,X°§Óɵ×^Ë?ÿùÏrËðçŸò¿ÿýsçÎÑ AúõëÇ„ ¸óÎ;Ëùã?²iÓ&6nÜȹsçÜ“¨Õj¾üòK–.]Jzz:’$ñꫯòðÓœœìÑm£_¿~tîÜYÑîÁƒ³oß>^yåÖ­[ÇñãÇ©W¯íÚµcöìÙîÙo¹å>ùäÖ­[ǯ¿þÊ×_MDDMš4áþûïgâĉîÖ{%4kÖŒü‘÷Þ{Í›7³råJòòò°Z­ôìÙ“M›6y¤ZÄÅÅUÚMÄ;vdïÞ½LŸ>íÛ·3sæL6lȈ#xñÅ騱c¹ýo½õV®ºêªr™©©©h4~þùgÖ¬YCTT­ZµbРA<õÔSåfƒ×Ä*ýû÷wO:S‚V«uç™WœÊ»ìÄ:Á•Š˜‰Ð“PÏDhüÝ^2£:zÖÒ 1¡o$Y–•o•²hÑ"¶mÛÆÒ¥K}î[’sZ™ãògAxñË/¿ÐµkWÞ|óM^xá…P›£ˆÅbaàÀlÞ¼™ýë_Œ=:Ô& !¡W¯^ôêÕ«Ü• Ôdþ_&æ“fõÚ:ZŽ]|¡I|#r «È§Ÿ~ÊĉY½z5Ï=÷\¨Í‰Å‹sþüy÷ã}ûö1jÔ(¢££ÃZ”êõzÒÒÒ¸þúëyì±Çøê«¯Bm’@ {ó)Oñ ¢x°6pE h«Õês £ÑÈ¡C‡¼vWHJJ"))‰üü|÷lw‚ËŸ·Þz‹úõ드œL||<×\s YYY|úé§©áFll,;vì ;;›>}ú„Ú@ P\<è%@èðç²Ïþí·ß˜6m¿üò 'Ožä¶ÛnS,вZ­Œ3†+Vàp8HHHà£>òèX0hÐ  D=˜4iÇ÷ËQDX»Ù¶m?þø#§OŸÆétÒ¦Mºvíꑇ®hµÚZc«@ ¸4ˆ"BOBYDè­û†î*]Èæ{(Aúæ²@gffât:=z4:uòºß3Ï<ÃÚµkùî»ï¸xñ"ãÇç¾ûîcçÎØl¶rÑëx´èª QDX»ILLdðàÁŒ;–qãÆÑ·o_!HA­BÌDèI¨f"´åذf(_‡è³Ð,¾¹ì#Ðýû÷wwضmf³g¾Qaa!K–,aüøñîÓ¦Mã_ÿúóçϧ[·n\¸p[n¹…V­ZqáÂ’““™?¾ßvˆjV@ „QæI¨f"ô}F -4‹o.û´?üôÓO –[×\s ›6m Aƒ9r„… ²uëV6oÞL»víü~Ý»w³téRÒÒÒÊ-JjàÒ äwÅ:±.ØëŒûJôúƒëÉ3å o¤G“  º}%šä…^`éÒ¥œ:u Aå œ;w(j¹„N:‘••å.(Ôjµ´nÝšØØØ Û( öc=oÅ–mSÜÑg\Q}  €Ùlö("œ={6Ï<ó 999$%%¹×ÏŸ?ŸñãÇ“››ë1™CUHMMÅb±°bÅŠJ÷q‰M .¢´("T"E„yó(ØZà¹A‚ÆÏ6F£öÜdFމ^¯š¤.ûh¨[·.Ç/' ;°îþ$ä[­VòóóküZ@ (Ýnµ !GÌDèI(f"ô–ÿÑ,",Ä3ˆ™ýAh\ùÍàê¬Ñ¥K÷ú}ûöqÕUWd.zÄ+VT¥Aõ¹ãŽ;BmBHâÙ“`šO›±ç+ŸÌÅtŠ š¾âÙ7B@7Ýtñññì۷ϽN–eöîÝË!C‚bÃ믿ÎĉƒòZ@p%rÕUW…ÚÁŽi¿Iq½¤–ˆjdk5á²Ð&“‰õë×pþüy¬V+iii€+CTT=ö , gÏž\wÝu¼ýöÛäååñä“OÅÎ&Mšˆ3>@ .Wd0PN߈l‰*Rôu¨M\ößVNNO<ñO<ñçÎ#;;Ûý833ӽߌ3=z4>ø 7fÍš5¬Y³†Î;ÄŽôôtRSSÝâ]Gy‰ ʈBOD¡2¢ˆPá[”¾Å“`ø§Í‰ñ rþsTÛ($MÍçš4B³øFè áÏL„W¢ÐGQèã‰("TF*#|‹2·x ßb>bF¶*—›…K÷ŠÍâQDRSS9~ü8cÇŽ¥cÇŽtìØ1Ô& @ çWžÇô‡g ‡:JMãÉÃ*”¹ÿ~öïßÏÂ… iÞ¼¹("¬„0úÚ.ot: DDD„Ú@ AÀiqRt¤Hq[Tû¨°Sa$$$ ÓéBmJØsÙÏD.4lØ0 }¥@ „7¦ƒ&d»ò…þpì¾ÑªU+ZµjÅgŸ}jSž0;÷¹| ùžˆBeD¡'¢ˆPQD¨Œð-ÊßâÉ¥ö-Þ&OQǪ‰h¾W¤…fñÐAB$ä{" }”…>žˆ"BeD¡2·(#|‹'—Ò·8LŠŽ)§oDwˆ†ðk¾áFh߈" fôàÊ¢ð—BrÖæ(nKy$}#}-ò¡[|#"Ð@ Æ[ú†&QÖâYàB@ @ G¡óI³â¶p,T! ƒ„HÈ÷Dú(# }<E„ʈ"Be„oQFøO.•o10‚—ÙÚ  …fñÐAbË–-¤¦¦’––jSÂQ裌(ôñD*#Š•¾Eá[<¹T¾Å¸O9}C[W‹®~øöXNKK#55•-[¶„Ú”°G‘Œ/Á•=ÏΙ÷Ï(nK¸=„Û‚lQÕºÅ7"-@ ¼BíHßø‡Ð@ ›€Ö7УMÖÙÁ¥Bè !ò=…>ʈBOD¡2¢ˆPá[”¾Å“@ûÛÖ,«â¶Ú}šÅ7B@ 1«'¢ÐGQèã‰("TF*#|‹2·xhßbØç帓 ªCTÀ^çR#4‹oDaÉø@ \þœ™{{®çÕ}=)£SB`QõºÅ7"-@PC¬ç¬Šâ ¦SL­\j„€@ ¨!†½Êé’J"ª}íIßø‡ÐAâàÁƒ,^¼˜={ö„Ú”°Aú(# }<E„ʈ"Be„oQFøOå[ìyv )TÜÑ<u´Ñ/cZ IDATºÆ¯ öìÙÃâŋűâB@ «ÕJ³fÍHHÿêÁBú(# }<E„ʈ"Be„oQFøOå[rÿ›‹lW.)‹êX{¢Ï 4kÖ «U¹“ˆ QDD2¾@ —'¦ƒ&Îv^q›*BE碎¬è„nñˆ@ @ TÙ&“ûß\¯Ûú$Ô:ñ,ð! @ ªAþ÷ùØ ”óíõ õÄÝd‹ÁBè !fõñDú(#Š7<E„ʈ"Be„oQFøOjâ[lÙ6.n¿¨¼Q‚¤AI ÕÀ¸"4‹o„€bVOD¡2¢ÐÇQD¨Œ("TFøe„oñ¤&¾%ç›d‡rYì ±èèkbZHšÅ7¢ˆ0ˆd|@ . û dÿGùê:ZM碊¨½1J¡[|S{¿]@ ‚ ã´8É[Ÿçu{b¿ÄZ-žþ¡ µ¾8~ü8ßÿ=û÷ïgÿþý8:tè@ÇŽ¹õÖ[i×®]¨M@p…ÿ]>ŽB‡â¶ˆfÄ\+¦í¾ÛS¤ýû÷óÀкukžxâ Ö­[‡V«%::š7òÔSOÑ¡C† ÂÎ;Cm®ODB¾'¢ÐGQèã‰("TF*#|‹2·xRUßbÍ´R¸SyÆAI-‘<(9P¦…¡Y|–ú7Þ k×®èõz~þùgL&¿ÿþ;_ý5iiiìß¿“Éľ}ûhذ!½{÷f„ ¡6»RDB¾'¢ÐGQèã‰("TF*#|‹2·xRß"˲«pЩ\:wSÚºÚ@š2„fñMXnÚ´‰Ž;R¿~}¿öÏËËcûöí 8ð[V=RSSÙºu+Ý»wgèС :4Ô& @ ¨†_ d¥|ÅG¯¡Á¸¨ta—ô›´´4ÒÒÒÜšEz',ô冨f öâ,rræƒ38MNÅíuGÔ%º]t­ºtÝâ›°?Ur8äçç—[wâÄ æÍ›W+rŸ@ Ônò6æyÏ‘­#/+ñ,ð°Ð+V¬ [·nîÇ?þø#-Z´`òäÉÜrË-¬_¿>„ÖùHÈ÷Dú(# }<E„ʈ"Be„oQFøOüñ-–3 w{)ÔH$¼< Ë"4‹oÂ^@ïÚµ‹~ýú¹Ï™3‡Aƒ‘ŸŸÏc=Æ_|BëüG$ä{" }”…>žˆ"BeD¡2·(#|‹'>}‹ìšq/É®ñ=âÑ$†¶#ðÙ³g9{öl@ÇšÅ7aŸ=fÌ4hÀ´iÓ((( N:¬ZµŠaÆ‘žžÎ¨Q£8vìX¨Í¬‘K$J"¢MØ·ßÅî,$g]Žâ6m²–c i¤ [Užë `÷·ßlL¡[|öènݺñ믿ðå—_Mÿþý$IDA­àÑI“xtÒ¤P›!¨%˜ÍæP›pÅã08ÈÛì}ÆÁ¤I!ÏgÏže_n.ûrs…TNØ è~ýúñí·ßÒ«W/&MšÄ£>JTT¿üò ­Zµ ±…@P9v»å›6±|Ó&‘›+ð‰Ýn§IÛ¶âX 1¹ësqš• £;DÙ2Rq›ÕjeÁ‚—Ò47wŽƒ=5{j*wޔ׏{ݼys>̵×^ËÔ©Syùå—ÝÛŽ9ÂC=BëüG$ä{" }”…>žÔö"ÂG'MÂ6t(¶¡C…E„ÊÔvßòè¤I\¨W/àW,„oñÄ›o1Ÿ0cÜkT|ŽJ¯"±¢×1GO˜À„×^ÃjµÌN%Ξ=˾¼Ó¦MÃd2tÌ`# }<1 Ìž=;ÔfT »ÝÎòÍ›‘ûöEîÛ—å›7LÜmܸ‘7d¬P±wïÞ€Y›‹K®Vðæ›¿b1øÎ;6V ?ÿüsÀǬŒ@Ÿ)Ê™Üu¹^Ÿß+Mœr-ƒÕjåóôtœ<ÂèÎì”e²¬V~5X—“ÃǼ~âcfÈþý´9û¨Qîýí£F, -4‹oÂR@*·‹©Œ‹/^KÇùå"„êât:q:•/-] 6mÚð1Ÿzê)6žÉdâµùóy`ìØ€ .EÑF°Û=:ò²wï^Öýøc@ÇX·n]ÀÇ,Á&Ëün4ÒwìX¬C‡‚$$a:”O>ÉéDx–}ý5˾þ:Ö–²gÏöìÙÐ1+ãÖAƒøðã:æœ9sê[€  òG'MÂ6lh4؆ XzÙòåËÎfÙòå\Лo¿=h¹·{öìá†^½:¦ÑhäTff¹uw\Äz^Ù‡éê눿1Þëx£'LÀ6|8òwðyzº¢/”óV+{Š…ñ¿22˜vò$cfèþýtÛµ‹FÛ·£OOçªmÛ¸þ—_´o:Ä+'N°èÜ9Ö<ˆÉl†¶mKnÛ6`Qè&MšÔxŒË°Ð'NdÀ€~ýéoÚ´‰!C†pÿý÷Á²êsðäÉ€Ž7é…˜ô ³2ýíoL›5+ cv¹ãŽ€:óÆŽÅ1v,ëvïZú‰'Ÿ ø˜·øâðáà :”Çt\oìÚµ‹ëºwè˜#ž}–SQQ¤§§lÌ—§McØèÑ5§D(~á¯8Á½Ðá矉NO§ÃŽlÙºî¸Ã½¿|ÇlúášlÝJò?Ò{Ï&=ÊâÌLö Xý<16 ¬ß»—õ{÷TÜ 7ŽaãÆl¼Êøðã1víÊßçÌ è¸#~8à´NÝ»ôø+K‘ÓÉ~£‘/23ùtÓ&äâãE¾ã>Ý´‰…§NñMN¿ äØlÕzñ3gÂüù®Ûq×èÑ8~˜»ð;ò‡aãÆ±¯   'xwÍW;v¸E§ã¢ƒ‚ï ”w– iP’WåT}–ûôq,N·1cx²X߸{7·oG÷ý÷Ô߶늅ñ#‡1õøq;ÇWÙÙü\XÈY‹»—&i‰ sç‚ÂçÈ(´ r²]AAsçÎeΜ9Ô­[—nݺѪU+Z¶l‰J¥âСC>|˜_~ù…ŒŒ ž|òI&OžLݺuCmº"©©©,Ù¹“Éý+oO›VãñœN'Ñ;`Ü¿•êÒžM›5‹WþøƒˆŸ~ÂôûïsÙòå<´lqg΀K¸&“‰ÄoÄ:w.üðCãË%K`©w¾X½š{RSù|ñbî>< cΚ=›ççÌaæÄ‰< ÈSÛž=9Ô£Wÿð/‘(KÓoäŒÙÌÎO>¡K—.5/==Þo½…cÔ(®zë-2tÉ8ª];ÌÝ»óR“&L+S[á ›,sÄdâ€ÉÄïF#ŒF˜L1™°ys£óçÃÕW—Ðl܇‚PÕJm£¢¸6&†Î11\[¼ÔÕjËí7øø¦C8ÀÚÿÛ¿7^ {öìá†âãî—Ù³éܹsǬŒ¸Ž)œ5 ÕgŸ± gOä‘™••EƒnÝèÒ±#;¿ù&VºŽÁÛ'O¦žÓIÆ/¿TkŒ"§“£EE-*âˆÉäº-~|Öbqµž?Ú´¾}KŸ¸a>\îX‰P©h¨×Ó¨’¥¾NGIˆeË—óPZò“O"-XÀÒ¡Cy°†A§³gÏÒ|Ø0l³f¡}î9Žù% 6¬Ñ˜•Qrl:ÆŒ¡ÙÂ…À)÷{xøa®_³†]ÿûV]Àø»rîsÌu1$Þ•Ì‹…?ÍfŽñ§ÙÌŸEE7›Ù3}:ævíJï² cǼyàGûʆz=): tºÒûz= t:÷ýœŒ š Žý­·ÇÑL™Â‰Õ«kô}ˆ6v¾ K]Baa! .dóæÍìß¿ß}†X¿~}:vìH=?~<ÉÉá= Pjj*KNž$"+‹¢Ч§Láƒâ(ÄSZ-ï{ùЍöí)š3iõj^kÑ‚—Ÿ{®Æc&\s ãÇ#mØÀÒaêíÌMùv;æ6m gOe´&ðÛ÷ßÓ0.ޏKÔw7ñÚkÉöYÞ}—¼ß~ Ș1;b7Žèùó1ìß_ãñ>L‡Ñ£±¿ñšüƒŸ|B›6m`©2»víâÆ¿ÿǘ14™;—“?ýTã1Sºv%sòd°ÛQ/_Îæ)SèÙ³gÆ|yÚ4¦gd Fä„ ˜þøÃ½­ZB¹•$Ñ,"‚QQ´ˆ`öСØçÍs¥o”E–ÑŒσŸξ¢"˜}DžSt:· ¾Z’xô®»°¿ñÚüƒÜíÛ‰‰‰©ÞRLó[oåDq T DŠ7>üøcžLOÇ9z4˜LÄ>÷pÌw4ˆŸ»tAóÍ7œY·Žúõë×xÌ”n ó¹çPÿßÿ±ù…¼&‡ƒcfså"Ù<õ”KD—=^dÙ%ž?øÔj¿mÖJ ŠÅôοý ÛŒ—‰‰D¾ø" 6l@+IèT*´’äõ¾N’Ð*Ü¿}à@ö :Ànz©p›¡~íµ€œàuéߟ݃CRÚ÷ßçðìU°Áu²“g·“g³‘g·“o·s^mç?Ãàfe?`·Ãøñ°paùïoãF´üA«)SH)ÂÞDr¤ŸÁ°ë àסC˧o”åàA®KK«Q_è‘#G¢×ë…€®„°îèËsÏ=ÇsÅ‚-//§Óö‚Y‘½{1ׯOëAƒxhÁbÔj÷[æ~ÅÇê ¼N§“¾þyî\>š0Ù3f\’(´C–y}Ö,Ì·Ý:òðá¼9q"ã&MB%I¨p ©øVUáÖ[wÌeË—s±];X¿ùî»÷æ›ô>Üí¨òJ–bçUn}™uyv»ër·Å¿þ %—­$ Û=÷Ðþoƒ¿ÿ$‘¨Ñ¤Õ’¤p›èe}‚Fãñù—ðÅêÕ\lÓš4áb›6|±zu£Ð³fÏÆtë­ðÕW˜n½•Y³g×( m—e?òöâË|öÑ£ÿœQÓ¦qÆbñXÎZ­©?6Yæ¤ÙÌÉo¾q‰ÜØX—8þyŠÚ·gÔüùЧOõ>”ìl—/¾B‡ì^¼˜ˆÕ«ÑÔ­‹Z’PjIBUæ¾{©ðXUö±Â¾ÆÃ‡9©ÕºMǘ1tì1/^ì~nÙÿ5¥ÿEe·©ËìcÊÊbOn.4o‹ãü[*Ï?4ŸøÛîTL£Úv VørU’D#½óìÙœ¿ûnÏß{Ÿ>H«W³§sgt:]õ>ï2œ={–½™™P¯äz)t¬W½™™œ={¶ÊQè´´4ÒÒÒØ²e‹{Î 2a¾\HMMeIf&LžìŠ0,\è÷s#Tªr‚:{Á2á®»\;¬YC‹‹i÷ôÓØeÙ½8ÊÜW| åWÜÇQrX<ñÌ %?ü•+]·#FøeEa-æ'ž@~ë-—C×çѦMõùÛoÃ7º¢Ï%Ȳ+ðÞ{ ×WkX ˆ÷òçýÉðᘧO‡¸8¸x‘È—^â­o¿Å)Ë8AñVö²¾ävÎwb›=Ûe¯Å‚fÒ$îûϰ k±ø° ÷z/Ûå3g\Ÿíôé¥o꥗\—‹ÿˆÀõÝhŠÿt4Å÷Ë lÿ}„üÊ+®ÏžE÷Î;4Y´È-–MGÕ¾„§Ÿ†—_†:uJ×ÍšÀ5×TýKX¶Ìu<ß{¯ë±Íæ:V>üÐcW„²v»˜ë®Ã2w®²€eô&`øõW¯³f•Ö‡rrpLžìº,\–ñã]¿…HåÞ´>yöY˜4©ôØ8sfφ‘HÖ­ƒŒŒÒ“^“ÉeƒŸþQâ4’Ëü.™8‘Üáá8ÅMõ쳌]¸ˆäd·_sTðŽ þÏQaÛ=„õ¥—\‚\'/úuü%iµn\ñ6©LJŽÝn'¦sg,| |¼È2ú§žÂ°gâ±"¬VN+ˆëÏî¹ûÌ™Pöê„ÁS¦¸uuxñExàR pàüûßðæ›Õ³2*›¯¾ >Õ ¢Â{¸îW¸ö pë­P<×D EW©ùóîHZDEÑ<"µDFÒ<"‚¦È6±;c­xõ iãFîÏÎfÙ¢EÕ³µ +V¬àå 8}a.ŽÜ\°Ù\¢pûöòœòçøñüùÀÞÿ¨«Ëʕлw©x6 &Lð[@—DJù¦MЩS©x—|þyE­–$4K­¶Üã›WOžÄ>yrù'JŒA§ý‹¡o½EnqÄ:×fsÝ/¾Í·ÛKO* ùÅûüYvÃ?¸òZKZ*ÆÅQtõÕLøäèÑïÏŃÿüÇõܱ¯×cïуe À_ÿZ½1çÌŠEŽcƸֿóŽ{•S–±âGºŽç²¥Q#׺£G+)*IB/ID¨TèË.•¬ûbíZleŽA¢¢ºv¥Åwß¡4ˆœâß©·«2P`·SPòûÌ˃ÂB·xpŽÅüÉ“áõ×½Ú_){÷BJJ©x×ohÚ4W`—HVÈ­*ˆäÊxtÒ¤ÒN-JHÖââÿ÷Áž›z:õt:º”ñ­Ë–/çß;–Ï11H:1ëÄ ÞsVYÆ&ËØŠOÈ+»áÜ9¦Úl8ÊŠg€PÙl<¦ÓY¯pŸ°8ŠÿeNPÊnsàòCJÛ.:ÄÞ¸8ä²Ç&À# 3‡æ ¸Ÿ[Œ(ÏYfܲÛ.`±ÙÜâ9î"tÜtîLôÎt>Üý“¨ÕÐäñèS¼b,î¼áíû“ûôáóqãøÄj­qzäÈ‘ÕÅ‚À"t°4ˆˆ§Ÿ&óã18Š#s† ‹Òºßy‡c–/FÐh`à@ê/]JË ÊEkº¼òý÷Ø*VÇëtH½{3`ãFú>ñr%Õ²·%Ñ×ÙiiXfÌ(?fl,R§NL8tˆ!#F”Še†X¦Òƒa?ŒýÞ{•Wúüsž¯_ß={eEJþ„•ĵûq…mV®ÄY1ÊòÐC®ˆ†ŸºbdÞ¼q£KØ–eøpT“&qõßþ†^¥BW,@tÅbD'IåoËl7œ<Ɉœ-Z”³E T<+I$6oî3ú¦x«°=÷À~NJB®x¹ð‘Gˆ~÷]ƬZElqzR¬ZM¬FãõqIêRÊ3ϸ¢d©S¹aCþe±xä¢ÚŠÓ Šq]ätòѬY|Ö§rEa3læ X€À¸q㼊âš`0ظo<ö˜çÆ[nÁ°b[®¾Úk.´½øó©¸ô{á2”ì3†”ùóùï† ^…±¦Š'î~ü1+o¸Á#Ê ßwçŸ{Ž‹¯½æ^Wèpc³‘k³‘Sæ·XqÝ–×^ÃP10ѱ#,YBôÅ‹h“’ÜWMJRÊ^IQÚ¶ë“O\Ñç²Ô­‹ªQ#qoß¾$ Æâ›õëÑGG#U’³*;|cT.nóÆø·ÞB.{ªìx>È/½Äd?ƒ9%tIMÅáå9ÎÔTv¾öZ@s¡›¬Ôí¨Q#œqq¬Ô骜 Ý¥v—y7íµ¨[—"›’Dlññwc\¥âÙn·óÙ×_#?ñlÝêu?k³f<2q"Kƒ4C¡àÒ#t°(i=¤Ñ`éߟ7^{­J9œN'ÑÛ¶¹£å8‚ øáÚk– =mÖ,ì½z•>#Æ÷'²®¸€É_–-_޵C‡ÒhÈÙ³pÕU V#?ø ‹_z‰9U˜Çd2±n÷nïÑ|IÂzÏ=<0v¬×Ž®Êç†æ>_ó‹Õ«Ѿ}iô¹„¸8TíÛóÉ… 6ÌkN¸Rnø¬Ù³y¾gOä’èó©SФ èõÈ=zúãUÎ…n›šê*ÎRÀ9z4k^~9 9š>ú(²Ò¤ bNHà¡‚‚*uäHOOçB½z¥Ñg‡23¡X ;FbijÏztäÐJÚb!®ÄÀ/¿t×”¢+úåiÓüêÈ*î{üqle£OÅí¶â]}im#Grßã{íÈ¡‘$w:X {öìá|ddùès q>2ùèQÚ¨#ÇßçÌÁé­%fTÆë¯çÃ?vwä(9±jVÉï3++‹ÆFciJAßBj*í?ü°Ê¹Ðéééônк;9GâÕ©Syü/©Ò˜Þ8èP@Æ)˲åË),ëo¡Ô·ÄÄPء˖/÷»ˆÛ=ó]Åès :°oÉ’jåÞ*±gÏNëõîß}EcÆ0lܸ*»V|Mÿts(b]¯áìÜ™_}Åc>ˆ:FMBïÊû‰geeñ÷‡r¥ UƵ×^Òî@c±XÐW3ýñJAä@ÔÔT–ìÝëÊǰۉxúiŠð{Œ’ÎrIîs¤5kÚ‘#ºcGL³g».o+½ÞªU¼Ö¬Y•:r$\{-Ó§—:ôyó\â·ø±ê£X2x°ßÎüžQ£øâäI¤nð¾“,#-Y‚ñÔ)"üȾHêÜ™¼iÓʧ ”PXHâË/“[Å¥1;b|÷ÝÒô™3])- ÑÏ>[¥Že;ox#9Ü7¦NUÞáìÙ*wäHéÚ•Ì)SJ´ÁàJ}?Þ½úí·«Ô‘ÃÝy£$÷¹"6›GGŽpÂ`0tóÍØÊ^Â/‰x•é»­}ê©*uäh~ë­œxòI¯"…³gi¶`A@:r|øñnjݸ¹²¾Á&1/¾Ha:u4ˆŸ,v|‹vÊN¯YS¥Ž)]»’ùÜsŠª~ü›øfÍ0THßú éÆKw2ˆÙ¿Ÿ‚'ü³KÿþìîÝÛ»€WGŽÍ›…öylê×_ç—÷Þó; Ý¥vßutè€Ö.1ø3‡¶ýݺ•úÕ† L¼ï>Z¤¶ ºStßGm¤wïÞ4iÒDä@W‚ÐA 55•O¿ùMË–îuŽÓ§yoÊ&ø1ÕgIßgóܹÞ{IÚíDL˜¾ÐÓfÍbêŽ0t¨÷l6ô3f`>zÔ¯1—-_ÎÃk×âTºô\‚Á@üK/‘ïgK¸´´4NøéøGŽYãvV_¬^͈•+qV2Ó¡jáBVŽáwGŽY³góüÁƒÈ•ä´I+V0³m[¿£ÐW÷èÁá>} búFYþü“6›6qè‡ü³2šÞx#§Š;oxCýúëüôöÛ~E¡Ož®üH~Èî+jÜ6,õÉ'Y·c‡Ïýô²LúêÕ4oÞÜç¾YYY4¾ë.l• ëºu~G¡Ý½Ç+ËaÏÎh_ò@³mÛ6¿f«U©TÜrË-~Ù­_?Îù1ëoƒ¸8vÖpöÓ“'OÒªKÔeþ7•p\¼È­Í›³ÅYE322hئ ªâβ›Ó>ßÓoÉÄÖs0wÿ%( ­%ˆ"BßÔ }êÔ)^yå¾ûî;Ο?ïžeîÙgŸ¥gÏž 2$ÄVNMÄwß}—)gºqãJ÷sœ>Í[Ï<óÏ>[­×)áùW^a£“èd™•~Hcv<ðÈ#üwçNßc:üôÍ74mÚÔ/[ƒIb«Vtê„ï}W¹ €ø}ûÈóóÄ"ª~}¬Mš(¦Ê¸±ZÑ:…ÉÏ™Õßw™……>÷»*6–µŸ}æ×˜Þ8~ü8mºvõëOî–¦Mù¾}IkÂÉ“'9v,V?òuûtéÂ[Õ-:«e|ûí·,ùâ Ÿ­×$àá»ïfÀ€Á0«Jô8ôS§PùðCò¶mœ;|دéwßÍw¢òq [ÎÊâ K%ðø(U IDATÄjµ’‘‘€å ÓÿLH²‚OPA½Gë‘Ò6%ȆB@û&ìt^^:u"&&†Áƒ3oÞ<Ìf3Ó§OçÇdgž¡Dˆ—ëׯÇn·ûÜO£ÑЯ_¿ X$Ο?OQQ‘_û¦¤¤¤¯ öâ4;Éù&ã>ï™ñ=âIì“D«Â¡[|öE„Ÿ|ò 6›½{÷’ŸŸÏ¼2í§ºtéÂG}BëüÇb±„Ú„°ãèÑ£4kÖÌkÜp#X¢øàÁƒ´õ6ÃÔŠÝnçĉ´ªnÏ×Ë”ììlêTlõwQ¯l‹¹bj›o Wºo):^DNZö‚Ò@ˆÃéàLþš&¹®"h4$ô¬¼pðJ@ú&ðÓטƒ2tèPt:Gn¯,Ë †YV5²ü¼ü~%1oÞ¼Zóý“™3g†Ú„°Ã`0”;y¸Øºu+[+iu¥"|‹2Wªo‘í2¹ësÉZšUN<­F–ì,íÒ”ô—$$m€çT¨…Íâ›°Oá˜>}:+W®dïÞ½äææÒ Aw ǘ1c8pà;ü(J %âRˆ@ ÁÇvÞÆ…ÿ\Àšeõ¹oT»(êð¼¢q%"t‹oÂ>}×]wqèÐ!¦OŸN^^ÌŸ?ŸÏ?ÿœx Ä @ 'dY¦`{ç>:çSŠÈ–‘HêÚ—*|‹2W‚o1î7’óMÎ"=°%ˆëGìí±œÅ³:VMýê“ô—$Œf£ð- Íâ›°/",ÁápðóÏ?“™™Yn}:uè^fÛpD$㇢#EämÌ#ªcñ7Æ#éj_äL &²S¦pg!ùßåã´T.:Tz‘WGÝ>šÈV‘HñûªMزm+"ºc4êhu¨Í (æf²¿Ìöè°¡DT»(’ïLFuy}Fèß„ýõ-³ÙÌc=ÆÚµkÝE„eéÕ«ß}÷],«‡³ÙŒF£—ŒÓâ$÷Û\ ¿º"NÖ,+…; ‰»5ŽØ®±¨´µæB‹@4,§-ä|“ƒ5ÓwwpýÎŒ{÷]bºM$Qí£ˆj%Ät#[eò¿Ïç⎋È™¼õyD^Il—X"[Dº¦™¬¥È™üÍùl+À×tš*½ŠÄ‰Ä^ãj)v»»ÝŽÃá@­'•öJîý÷ßç³Ï>cÑ¢EôèÑãi~m£;wîdâĉ <˜Áƒ‡ÚœËó 3ÙiÙØóËGFyëó¸¸ý" =ˆ¹>FüÉ €Óä$wC.†=Ÿ¢Ãë'Æ}FŒûŒH:‰¨6Q®œéÖ‘â„5Œ0ì3·>G¡Ã½NvȘ~7aúÝ„&ACÌu1Ä^‹:®v‰%ëy+Ù«³ý:Ô7ÖSwx]4‰µC/„’o¿ý–µkײsçNn¾ùæP›Ö„} ÇØ±cÉÉÉaÕªU¡6¥Ú¤¦¦b±XX±bE¨M +jRèã´9Éß”ÏÅŸ.ú%4ñâ{ÆÓ9&ì‹¢®„BŸª"Š•©J¡,ËvÈÛ˜ç»Àªš¨t*"[G¦ÛD…lBŠ+½ˆÐše%w].æ“ærëe£e–ûK*‰ÈV‘Ä\CT›¨°®Ž’e™ÂŸ ÉÛ˜ç;/_‰½‰ë‡¤R>…oQfäÈ‘èõz‘ÂQ aü3qѽ{wöïßj3jŒHÈ÷¤º…>–³2>ÌàâÿÄ3€½ÀNÎ×9œwò3|Ï/çBŸê"Š•ñ·ˆÐ’a!ó_™ä|íGw¨öI¦ÓêÄxÀÈ…Ï/pjÖ).¬º€q¿ÙÜßÛðÊ+"tšäþ7—Œ3<Ä3À­ Ÿ';eL‡Mœÿì<§ß;MÞÆÔ0€¦­£%¡WQ¢¤ðŽH ÕÅiv’ÿ]>…?úuÒ¨Ò«H¸=Øb1Ÿ0cüÝHÑÁ"&‡ÏçV†¤uE8K"Ó*}Íb7N³{¾ÝµØKïçÛq8p˜¨"TèRtèRtèèÑ¥èÐ$i.Ëß»,ËöÈߘÃX³ïÊÍ"ˆ¹>†èvÑAM“e§Á‰-׆=×î¾-ú³È¯ÀØbIê/¦ä® B·ø&ì¯oéõzî»ï>î»ï>Þ{ï=í·Ýv[¶l ¾a‚ b=o%ûËl¬þ<ù–mãÂЦkI¸=èvÑW  {‹ó_ þ ªèŽÑ$õOBëÊ…lId«Hìª500b:hª–@“m2¦?L˜þ0!iʈ髕ŴÃèÀQàÀžoÇ–osß/Y|u —È67c>^‰½Eµåœ…Üu¹XÎx®÷ç—™K̵1Ä^‹¶ž6`ãÛ/ÚË ä²·²­ê±=uŒšä»’]i(Á%&ìôÿþ÷?î¹çî¼óNzôèAݺuËmOII ‘e‚  ÃÅíÉÛìG¾›äŠU„J1’lÏ·WI¨W•NEbÿDb»T¯=ð-ʈ™}öºqãÆ=z4ÔfÔ‘ïɼyóxõÕWIHHðØV¸«ÜÿåúU|sm IIBQ QÇ©IœLÜ­q|_€q¯Ñ¯PË ™K3‰hAÂí D4 ¾ž9s¦È;«@I¡Ïœ9sBmJXQR@دm?rÿ›ë×DQm¢HüK"ÚÄ\Ž/ΑhAÒ_’0Ÿr‰iÓ&ìý³ãR±dç&öšH\D\µžï´(‹jm}-Úäò‹&I´ü`ói3¹ërýNeÓ$hHêŸDT;WJÃÌÉ.ߢŽRs<ñ7Çc9m¡pW!ÆÆ*Eþ'…» ]ð¡o¨§Îð:h“« ߢLVVMš4 µaMØž={–=z°xñbzöìjsª…HÆ÷G¡ƒì5Ù)ò¹¯:ZMòÉDµõïf˱‘¿%ã~c•zßF¶Œ$¡wú†âLÜœf'Ö,+£]=šä0‰Ú]¦Øóìäþ7Óaÿ¢¸šx IIòë7S]dYÆzÆêÊ™þÃä·¨¯µH®ÏU“¤)ÕÉtÉ:Ô êJ;@ø‹Ãà oc†ßüëÝ-i$âo'®{œß}¹Kz{î* X­É¥BRIÄ÷ˆ'á¶„ZÐK¬v"t‹oÂ>½{÷n’““¹í¶ÛhÒ¤‰GŽŽ;òÆo„È:A 1î7’óm¶¢ÚE‘<Øÿ|7m²–º­K|x ¶`üÃ?!]t¬ˆ¢cED¶‰$±w"º«t~½ÞåŽì±çرfYÝ‹í¼ÍC,I:É•_š¢w_×ÔÑDT\i8ŠØsìØrlØrlØs옙üÊ#•Ôq7Ç‘p[Â%ïL IúÆzôõ$öOÄz¶Œ˜Î°˜V&Nƒ&¡x‰wݪb\)ÖsV¬VlÙ¶K׺RÆ]Ühþ³|º™¤–Ð$jÜ‘ê²âZ«ò}ré„‹;/’¿%§Ù¿t¨«£HTåICTz±7Ä{C,– †ÝŒûŒ~¿î¥B©B›TüùßêèÑÖ P1£@PMÂ^@4l؆ †ÚŒ°BvÊ qYD÷&¹ßäb<`ô¹¯*BEòÀd¢¯©^× ]=uï­K\FùßåStØw¤ èpEGŠˆnM|¯xtõ®!í(t”ÊY6— ñ#·Q¶ÊXNY°œ*­´ºúÅŠ…µ¶ž6ì'¸ ²]vå‹ä¡l˱U»=YD3WñŸ®nðYI’Ð7Ò£o¤'©–³WÎôïFìy¾Å´¤‘Ü¢X“ A¯F› E¯v=Žõ/Â+Ûd¬™V,ç,X3\·ölû%ï/;dlÙ®ßKE$­T. ¤D\k“µ¨£Ô˜O˜ÉY—ƒí¼9Êš$ ÉIH!´>E~ž¤~I1ì6`>å_QjuPEyŠdm²m’U¤1 “°Oá¸HMM¥ÈPIJ–![eœV'²M.½ïëVi_»Œ¤–PG«QE«PG«ÝKÉcU”ªÜºp›b÷èÑ£Ô³Õ£à›¿ªù#[FRgH€N9k9cq écþ i$W˯„^ 5ʽóF¨ŠV'¶ó6lçmå󥚵®,’ZB[OëÔºÚúZ÷1{YúÈ®‰}lÙž"Ù^`¯RŠQž)€Ä¨Dmê5‰ý‰¹&&P–Ë9—˜67»üT±(.IŽö#J«€?3Ê6K¦[†Í-¬m.a¤º ¨"T~G~%­DBÏânŽó™‹]ßb½`Űۀá7NSÕ}‚:F]^ —¹-[¿l.+ß@ÄL„¾:¤¦¦røûÃ,xyHít’¢ÐVáQªÀ_fwº"2²Ãu0îÁqŒk?Îg¡J§"¡oq]«Wäæ“fò7çûÕñ£I%}m4±×Å"i$dIF’$$Uñ}•„$•ÞGÂ}ëí¾¤’H•zi– ¶\›;í¢D(Ûóª&Þ.5’JB[G‹.EGQloýû-f½8 ì.ñã´¹N$e[ñb/^W|¿Òõ%·ÙëwâÏ÷U¥çHŽBWK¯@µïZp=à*",ýà ¶k,‰½C*LBÉĉ½(W†ÓæÄ–U*¨­ç¬X/Xk¾öÇ ¶ Åbù¼õ’õÙ„}C=Ƀ“Ñ¥\9)F—šQmÍ´–»bŒmÞÐÖÕ’<0™ˆæ¡k· ®Üoëy«ëªA¢&ì®r ª("ôMXæ@9r„;vàp8ÈÌÌdÇŽ^÷µZûZ¸Ö"»r“&G•û„I#‘p{ñ·Ä»"xA"¢E)-R06‘ÿ]~ȪÑÝ'Å:ÞA€¦ç a;ÜQEªHìSÜ 7<}µ•VåÎã.‡Ó% Ëæ«»ÅõÅKs5G¥W‘Ð+¸ã¢ûDIÊ xdee±k×.¿öíÒ¥ õë׿Ä]¹„å‘?yòdºté‚^¯gĈŒ1"Ô& ‚ˆ.EGÝau7el5ˆjEdëHL]BÚßBž+ ISœfQ_‡®¾+oYW_‡¤—\—Â3ŠsK3]ÑïPEì.'$­äº,^§´eZd«H1[°Q¹ ÷4IÂ=§Í‰#Ï¡(®«U *AÌ51$öMD#¾ç+™9sæðÖ[G‘¤v•î'Ë0eÊ̘1#H–]y„¥€>~ü8Æ #//¯Ê9láŠÕ!"å©8[˜¤’ˆë^Üj+ :2H’Dt»h¢ÛFc<`$K¾b5} 9–}Œ–uZ^òש*š Úz¥bYW_ç*¢ô «µ“2¶ ¥¢Úšáêèá´úN0u8œÉ?CÓ¤¦z;a¤’P'¨Ëõ.¹¯ŽS» ë²³³±c'&:< C…?E„—•V…ªžJ1à4;=:­”ˆk§Åó· »JGÒÀ$"šÔ<]CÌrêIm,"”åå¡>öJ~ªökˆ™}–úr$Û]~…ÊU§Ò©tRµn6'£‡ÑÓ¨|?œs]ËΦ­£¥Î°:á9aIqרöQ÷Éÿ>߯\ÕeáÖ…!Í—WéU.q\O‹¶¾}}=Úz5¯”—Ô®žÐº«tPÒÎ]vMrS"¨-¬™ž?ŒV#Kv.aꀩ5²!ÜPǪËÏjW,”5‰¿N"Kf":Ôןé•Ee³œ†U„ ]ºžyêƒ+j]"°5 b® XA·˜åÔ1¡2b&Bß$ZÝØŠF•Šà Mýê´:=Dµ7Áí49ƒÚÂiꀩ AÜq$ôIûI%Ó9†èNÑö(H/¸$³¬KÔ–Aø`¾tóÙªHX è_|Ñç>·Ýv[­еI’PG©QG©¡n¨­ñÍ}÷½À™3ƒ€ÊO²$i }ûö¥iÓ‡uþŸ½3o¢Zÿøg’¦{éB)P¶" ,‚¢‚¬²(¢pYE•ÛŠ"¸¡àuA½Š?¯Šz½ Ed‘Ë&PP‘½ìЖ¥tM“œßÓ´Y&™IÚ¢çó<ód29yçd’œùž÷œ÷= ¥T‘©$’? ………T*ÕqšÀ¤I“8}ú´n¹¤¤$ÆŽ[ 5ªzl6_ýµ¡²)))´l©ßƒ?räwÆdºR§d1Mš~ÿý†Îpð ìØá¾>løí’‹LµЇ"66Öo™ª  ”¢¢ªÍ+\©h ÃaBˆÿ70“é§rÙ¯*þê>V«•9sæ¸s8dee‘””äv¼eË–têÔ©2«W­ÈÊRc+«¸&Ջж--ZÜÄ‘#µÿ™€„ØÂé8qÂPJÖÐÐPêÖ­kÈæ3Ï|ŠÕúœÇÑc@=›¯Ð/½ô*'Nè‹òºu“xñE}˜?Ž9BJJGL¦«uJÓ¸q{ölеyìØ1úõ{¸Íå¨8$”¢ ¶’‘±QצÃán¤¸ø#’ç¸paæ+V+üö›·X>^÷ô D¨OµVŸ±±±Õ¶w(‹­")©‹Û±©SãöÛo¯¢ù榛îá»ï2ÐO4šÉþýëËg²:úhqíµý9r$[·\ƒ5ùñÇåå>Ï¥èsâÄ 7î†ÉÔÀo9!l´m›ÈæÍ‹ Ù5j:B¤º-@ˆ5(Ê`·cõëçðaý›¨+Ž!<<œððŠe<øòË/yòÉw •}óÍéÓ§n9­ü¯[¶l cÇŽnÇæ?~F^Þ ŒÜ<Œs.^¼˜ï¿ÿÞíXDD¯¿þºWÙþóŸDFF®oeñû﹬Õ-—›«/†œ\yå-?î-ì:¸=ONŽc×®¯ ÙŒŒlFqñºå,–?ÈÏßk¬¢>°Z­ØlKĆ?Îqøp 7¹Ö8z} ÷uSÏáp¬2d/##ƒË.ë‚¢tÔ)YLƒ™<˜n¸®ZlÚ´‰ß~ëܬSòk6mÚdH@kçuN*[l*Ðü¯Çóx -O*–ËÉŠ›9vlàdÑl~ˆ .Ð={ÞËÑ£m×NOC-ò*ÿ‚V*W0™øAhè<Ó¬ ½ÛdŠn1PîÍ §r‰*'ˆðäIˆ×‰GЉ6mT±|õÕж-´jÎ>ýøñ•# ¥xÖ§Z h³ÙLllli®Ó?fÜxã‹„L›6×_?èͱÚÅÓO猪‰ÓÇŒù/ÇŽÝDù-§(“=:S÷&—Ÿ¯{Áá€üš6…ºu¡N²­n]µÁ2†‚ûw诜1(¦ À÷J›N ¹q6ÄfûB·”ÅRÑ›ñ¥…¢ÜdHèËó”¤(sаÅÊÈÿzñ‰E¯sƇŽív3Ç¿tmšÍú£G’?B€Ý®Þ((0þÞÂBX´H½×¸?ºîgg«¶`÷XC'9¹L$;s“& /}Ž» ”‘\Lª¥€nݺµááÖK‡tÔá³è{{¼bˆ÷U÷g_ôç+/ó:vòdÙP×Ï?«{öo´„€… }¿é.¨µöëÔ1v.IùBÆÌϯêšHþJäç«mÌ©Se99ÆÞëpÀ!¾·ðpÿ¯"À>ùD{þ¼:À×¾Q±XPaa]¶EE¹?wnþ¦¸b³Á´iª“£¸X}tÝËÊ‚'Ô! N¹,êÂu;Þ¸€6m‚ÌLõ3ÄÇ«7“áÔ¶ûP=tî?ÃåËáØ±2Ñ|ê”ñϤ…¢¨=ý¬,ЊåÌχÔ-Øíê›Ö ÈX&½ÝÀÅ"NŸV=-®[Q‘þ1çóÌLã7ªS§ qcõfY\¬ÞdûÅÅÞÞwlÀ!À}µ°S§ C¨QCÝbcËö]·¢"<¦ø¿.gΔÕÑYÏ@öÿÝØ¹@-»p¡:ÏѹY,îÏCBÔïÊçMîA„{öÀ§ŸB^^™ÇLkÇãõœ1CýozÖ+ͨ_DX°@í¸††ª×ÃbÑÞ×uÞm‹ðϪŸÝU,çæ¿Zõüá‡ò¿?P¬V¸çžŠXÐn[¬Võ7æLEl—Q\ ?¬_.X˜wÙí¶ÅfsYÇ'2Rý]8`¬}‰‡'Ÿ,o=UzõêE¯^½*fÄdàÀ 6ì¢ãÏ‚Е†· œ9SÝ@ý3:EhBBÙ~|Î}­ç¡¡jÝÓÓ Óèhxè!µ­7™T‘ëÜw}~þ<¼þºgۮݶԪ¥:BœBÙùèéÉÈP§]øw\zÈ B}¤€®4üÿíñã;Ëš5êVU¡ŠÔsçÔ–þñ¸a±”E_uUÙ£k¶®Ù³¯_\œºée‰³ÙT磌È=ºâŽâb8{VÝ|3ËëˆÝóçWìÜÁÄlVGMBCõ>K;}¨ IDATpûíeD_[n.Lœèy“Óô‰ŒT;+ÎN¢kG±¢ßÕ¥ñé`f³Úi‹ŒT·¨(u˜Úh›Ó²¥ÌäÙa d;uªâCëÆÐn[âãÕѨڵխN÷Gç~»vê(˜&“*Êý…l8çÞúÛ 2&´ÃÂ`×®2‘ìoDë“O´ô,¯r!!ðê«úçõwcÄÓ®æ+vÇf D˜ÆÆªÂ؈Í7Þð<ªÝ¶„†Â•zaGb¤xÖG è*dÀèÝ»LPyngΨ.n;*ªl8;ÐÇž=UÁ¨‡Ù K–¨º³¾Î:»>ž9£SiM&X±nºÉèt‡€h¿%„|HHˆ:Ú5åê#eaa°zµ¾7'/O R1r]EýZÃâžÃý+W³i±ÀÛo«7»ðpµÞÎ}­ç®Çœ7Ã@nr Æ:=Æn†  ¢å>2” êÝ»U‘bD¼ÅÆÂË/»_×@÷ÿû_uʃx@uñ›ž£ }dÜ«„¨-׊G×—ÓVå!ÄYô3X÷ZEDDý#Bèåñ\{í_Ø¥ý'D èJ£ü+ÊS…VEÄså¢Dh”1cF2fÌÈ`V¨šP9A„ÕuÔÀ5½Ÿ8Ôw9Vè¯? ÚA„’е-½{·cÅŠÁºå,;1Æó`úDê‚FÊcâÄ»8}Ú=@ ;;ÛË™”t—a›Çy`6Ôx f@k‡úÓËÚ ;gÂáFóbI¹ŠElììv½ÜíЩӵ†lÆÇÇÓ¾=—-ò"„ÀjÍ',Ì}ˆ _¿¿²™””Ĺs¿*{1°Z­,Y²€AƒjlèV¹¡>R@Wв“éÎÒçB º)+™Zµ"ÉÌŒ¢øÏUípì­àRêï ®þti¬Dèpd`6ßa¨\Åðô)?;À2¢¾¼ øï€ÞÒ¸Æ %$ä[Ý„°Ñ°¡1‘W³fMúö­…Ýþ^é±âb+{÷î eËöne{÷6~óWE¸žG±ü]OåÅÿ‚,Bü~~wW<ó¿:SYÝèQÆ­[·&>~"êoN¯¬Ÿè^ƒX,6Ìæ‡ÐËó,ÄzÔ @£Æ]€¾ <Ôp±iÜÓúÁ‡ü‚DÛ¶‰†2úÜ–ç~gÚ;|ðùìþi7ͯiÎ}wÜǘ‡ÿ>ß~{$O¿ö络׎šÀØ{¸ogâ3çù!11‘ììí¥ÏÓÓÓùlÑgìÍØË€›p×]w¼Šm5زÅÝ+îÜ9&L˜ÀäÉ•³˜J°ÈÊÊ¢]¯v-:Š=Y 1¿d¦~X}¾_ó=‰‰ëTË B}¤€®$†½‘iÓ¦¹ó\fÖ?½œSÆdðÇöí+ƒbÇ›•€j< ÑÇ ÊÖég3÷ƒ£GùsÉY­VV¯^MóæÍivƯ³²v`óŽÊñ"N…Ãq³¹¿Æ+ý½Ê¥A…S§®'Wù{|¸F”x‰~„Ÿ&c>I´hEíÚ÷œÕ­[×ðÒÂF‰ŽŽfùòrDˆú!&&†Úµ÷P\¬¿xLïÞúK*ëñÄOз¯Þ0±Js½ˆÖüç]ëQÖØÜœ»îº‹»î ¤R1~üq uL0¼¢ê=÷ÜÌŠÏi¼â~Ìb±ëĪÂÈ÷åáÈ‘#\wóud&g"®Ð~9÷ /zœWþû ?|ýC@+×^(¼À…” îâÙ•fs(‡Ü¢à:uŠënºŽã¡Çq\°æË5<>ñq^xøžúù Ù‹‹»$ÅsƒkPxK!$—·aãÐñC4¸¦‡¾?Dí ¤Y’âYE£ÙQ%å%-- €Y³f•ëýëׯçÅE$½ôÒôèÑ£\繘,^¼˜Õ«WóÁ²°ßb‡Æ./ZAùR¡¡£!Ãú x)ïÝ»wÓu`WΙÏáHr \P0Ÿ1s÷­wóáÔƒÿaJøå—_ظq#ݺu£M›6í<å!åê2ZdhÏù.Û{ûÜ_éõ’H$úX­V~úé'®¹æš É'^‘Hv¯lÐ èË„škj’õG–Æ‹Ú$·NæDŸà¯y.€:_ÖáÐöC„™+6Àf³×,޼[òÀs1+;˜¾21¦ç&MœT¡ó‹­[·2dÔÎäŸÁ®Ø 'œ›;ÝÌç}Ôó¤\BF‡ ¨ë£ÀIh´¥‡v*÷9*ª[þ H] ¤¥¥1wñ\â“ãù×?þŨûGUÈÞæÍ›Y¸D]ZoÈ !têÔ)ÕÔäï£ÿÎòµË)¤³ÃLbt"Kg-¥mÛ¶ÛJ¼"‘ì>Ù>gl˜¾01÷©¹ »Ýx÷•_®dà#± °¹Û lSh’Ý„½[÷\W¼öæk¼4å%lul8ê:00r2„y‘ñOŽ/·Ý;vðÉgŸ°é§Mt½¦+w½›«¯|ºÅä©“yâó'pôôrBùZaò=“ó`Ňå=9sæ äÚkÍK¬Jl6[§!éSXXÈŒ3XøUÉö–!<ðÀ„aõ$kIî³`Ì{ÌÍÍeòÛ“YºNâØs ?öx€#e³}ûv7nðp¼/¾ÿþ{¾Xù—5¾ŒAƒQ£F ý7ùaÏž=Lyw k¶®¡~R}†õƈ#‚RWO–.[ÊßÇý¨BìqvÌçÌ„ç…3ç?s8 °l}òQ¦îžŠ¸Î÷-^ùAᾦ÷1î¹qœÊ=Åé¼ÓœÊ+yôx~:ï4¹ÓsÁÈGÿQ–(jFÖ¤fDMñá±(¨¿]ní·qßú `ùØÂÁoR¯^½€®Ãáàí·ßæó¯>çdæIº]×Ç|¬\÷6€ûÆÜÇœ5s°õµËàž²C¡Æ/5سeIIIå²íÊ™3gH꜄}¨ÿVó|3§ÓO“HD³ R@ë#t%––ÆìgC0­5ÑÂÔ‚]›wlçûï¿çÆÛo$¯NŽËTqd:`"êdk¬¥]»vA«óéÓ§iÖ¾®º ý9ÏCÈ!Ü{ë½ÌxÛ`ž.à£>â¾ïCtsù¹A½NÛ6ˆù4†œýÆæ*Z­Vâ/'X>øÐ"ÊF…§»>Ík/U0ÿV ý†õãËŒ/qÜä×`M»úÝÞœ|3«¬ Èfaa!]níÂŽÓ;°·¶ƒ(óN3W']Múªô€ÄV½6õ8~ëq]/Q½Uõ8úóÑ€êêg^x†·?~[ "Z`Ê4Q;¢6éËÓiTÎÜa6›ñÏgÙúedgfsíU×òÜãÏÑÍhš æÍŸÇƒÏ=HR€ÃìÀRláÊÆW²aù†€F>Œ°pñB†?>œ¢ÖEˆêo_9¢¶3Œ'ÌÁå[+øö¿ßΪ­«°šK´=”[¯¿•s”ËÞÇŸ|ÌÈgGRtu8Gô@ØŽ0Þõ}†ß=Üïû娱ctèÓÓE§±ÇÙQò,ùÆ¥Žã_/ü«\6/YÌ=ßCQí" »‚ù°™¶õÛòÍÊoÊÕa6b Ób»Æ¦zûrA9 {(–u ו[liñÈSðîÊwqôs€kf‘B0-71²÷H¦ÿgºa{I-’È”é>Ý< ÷xÓ"àŒ‰b€™À}€¿Œ6ø¸ßpUÝ0+f"¨Y“=ÿÞƒ#M'÷ä¯ðäeOòÆDãóÖ7oÞLïa½É»2ÑX¨‹ZÁü­™›ÛÜÌ— ¾ ¨Îk×®¥÷c½±ßîCÔž†ú›êsä—#~í8„ƒ¬ü,2ó39wšÌ¼ÌÒ΋óØ/«~aßÉ}ÐÁ”- 3†ÌàþûË÷E 6Œ°°0) ý t%––ÆìofCªú\Ùªöúßç}Ã6víÚEÛ>m±Ýns‹QÉ…!üºæW.¿üò Ô9åê2:fx›•`žofí»k} ™üâ|Ž_8^º½8æEö4ÙãžDá+ ;nâW™£Ðí…nDZ"½¶ˆ·çæmà³ÝŸ!Úùù ; òãHòäx¼IOO§û¨îØïðÝó7/4³nê:n¸áÃv›\ׄÍ”yY–R¶>Æî’é?hO·(°°ïÌ>öÙÇÞì½ì=³—ýû½úéµLš¸ñ_7Ò(®b¹=Ö‹©GˆÉ¸g¶UçVìÛã‡ûÍ5 ,Ë,¬ž½:à©E_}õ·ºü¶ùê<¿Ö`þÞLûÚíIÿ*S )j€ÞÈÆ#ÕiD®NÛƒ¾6œŸ×þ´ÿPzz:Ýïí®ÞT=G²‹Àü¹™5ï¯ èº8j]Q‹³ÍϪžEç4ëæª71~w<™dt]¾þúkú<ÚG­§gÜp1˜˜Y3=°zúcóæÍt¿«;Å}‹ÁÕ!çÓ&Í‹šóë·¿dóýßçÁ7Ä>¨äZ»¶-{!akÙ{³²9lÄ0æšïÞéwrÂ…“ùkfP<ô»wï¦õßZc»Ç¦-N„|ÂÎå;KçÌgdsøüáÒ-ã\FÙþù N¾sÒ[»¶-NJ¼Å®˜‰‘‰ÔŽªMíèÚ$E%Q;ª6 þ¹€£mºÍ»õâ4ú¥£þ=Šìül² ²ÉÎÏ&+?«tÿláYBG ½É3³.†{ß¼—&ñMh’Є¦ MI‰KÑœB’““CR›$І©žâB`p‹úº²Yá¶äÛXð‘ñÎh|³xÎÝvνãã²JáwýƒÆk ã̼L² ²õ¯Ë€h￘²MaZ¿iŒ=Úðçp¥gÏž4lØP h?H] ¤¥¥1ûçÙn —e¶…Â}…†ot‰ÍK¦?øŠ9 ñ+ã9³·âkí®X±‚¯ Äq«Ÿ?rD/Œæ•O^qÊÎí|‘G£O€¾è'ܘ Â]Üøâsà|ŠüRæBÄß#ˆ‹ <$œˆ",eûá!áDX"t÷_¾ÿeN÷< ñ~Îušlj¾öøðö´·÷ù8=|_kÓzOöy’ëû_ÏÞ콪X>£>Ë9†Àã//‘Y1““¬)®!êÝâÕ¿Ê?WüÓ÷”‘|ˆþ<šóûÏþ½Ÿ>}ší`jÕ¼))Ûz[zäíŸûé\Òþ“†½ŸÎÅ9¨¹²&Y{ŒÏ õGLJ ¹w䂯4ò!fA 9Œg‡èÞ¿;£6úÒÞ Ýòº±aùÃ6£G“wgžï›>DÍ"÷PÅÃ5×PÏç㺘֚x©ïK†ƒÃJÑð"ŸañÊ…!qC ÏEÝ»w/-ûµÄ6ÌOÐp\¹÷JvnÚiȦ?®ìz%¿¶ùÕ½CáI&DoŒ¦Áè>˜¼b§À@î#ežØ!bnï/{¿T,׎ªMbd"&Åû¿š‘‘A³îÍ(N-ö)ô-³-ìݰ×﨓C88WxÎMT{ í¬¼,–<¿qŽD9l<$™õkÔwÕMâ›ðdÚ“ºæïùÃ@Èü~^ø3-[¶D ¸Pts…ç8[xV},8ë¶ÿêý¯bÿ»ŽÓâ0°࿘  $E%ogË{[wø¿.æfޝ;^îi#r ‡>2 GaK¶Ñá¹$_•L¨9´t s{j¥ðL!g-g}‹g€x8qž +&™‰Ía£ØQ¬>Ú‹ í;ß³cú-uzÁëÈ屯3ö“P:m*0Ñ¥Yò‹ó½¶‚âìÂ¥‘²ãí1Ó E8 ¬.â³øÏq°ÿÔ~L/™ˆ°DhzÏÇ#B"X1yŽAþ¯µãz¯¿û:¨~bd"qäÒºqåÄ׌çŠúWq.ƒ“¹'Ý„¸]Ø9’s„#9GHG;[­ÈZ4ŠkÄöw¶ûf„¼yL™2…1cŒÍ¹îÔ·Ö¿i‹gÑVð¿…ÿcÇŽ†ç‰?ôχ°ós“‹ƒ3ÉgxïÃ÷¸mèm;ŠÝþ®ûþþ?ÅöbŽ:F~|¾oñ êu©‘Çä•“ixYC,fKéÞb²¸µ³Q,øö÷oá?6›Ã·s¿e÷©Ý(! V»Õm+²¹=?|à0ñ~=gDBAB/.z‘ä”dBL!˜3!¦ÒÍlòxîãõùÎ'¿…ÿëâèáàµé¯qçÈ;qGéfwØËöEÙþÌI3)jë[<ˆ«Ëf-cþ¯óKíØ…ÝçþÂÉ ±]­“q§ü¾ñwR—¦bµ[)¶»]Ûb‡Çs×]Ë82p“ÿÓ‘¹çsù=ëwí¯ÉIÃØ†4ŒmH£ØF¬]¿–‡¸l{r:µéÄÝ­ïÖ9yÉGnÔˆñ#ÇóêœWÕ¹¾®£¡9`^næ…‡^вeRL$D$á~nôøhòìyþ;‡ ùÊdÎ[λu*ÂQê‘_h}YùÓø϶«lt|¤#!·„p¾ð¼û}Ç“"ô²2ªÄâ¶>K\xµ"k‘•DRTµ¢\öKŽ;%F&bVÊ.BÝéu9™yR;8 j9jeεÄ7R@W¢–àû¾÷9w×­¨kèàhäà¥w_ªøbP§1f#(‚ÐÈPêF×%9&Ùk«W£É1Éäß–O‡!°µðsS:-´dcÚFŸE¬vk© ~åÂ+L;8MÍAê‡k㺣 ¸€[…¶BÃûÅ—µ» ®d ¥õôKþç*Sòº‹>MŒL¤YB3š&4¥YÍ’Ç’çqáqdÜ‘A³žÍ(îÇK´ÆÂöõÛKotV»•#9GÈ8—AÆùŒÒGçÐð‘œ#XíV73™ù™dæ•̱ÔY¼G4Œ},Ïç=¯Ûa 5‡r0û ï›C ¶66D«á­| YWÁ›K®n‡K4Œž<šÑGÊ7ìYÊfÀÀ´oG#cß F‧l~²l l´x¬´0`s3` [•£¡ƒÿ›ñÆêéÏ®:eLoÎçòw.7öŸû}ñ XC­ ýl¨±»Þ¨#f:ØCíÌù~ޱ3˜QÀd21°ÅÀR‘Ü0¶!âÔÇZ‘î˜Ü¹Ôº²…õ µ÷ž!œ¥»ôW+tå¥g_¢ïþô¾«7ÄìvÌfb”V¶:¨ÁÃÏ=ôÏ/{^9Ñ"¢~âÈÁ#˜L&Nåbÿ™ý8{€ýg÷³ÿÌ~öŸUŸŸÌ= VüwjÔ…œs 9-ÌáfìV¤Ì„2uÜTjEÕÂb2âýÑfÛêm4íÔë «÷ý/,‹-lÞ´¹Üö%ƺ²ðÔG¡UïVXêX4=C® Â#Sx­x‰1³bÆb¶b Áb²¸í‡˜B°˜-^ûûìãTæ)ÿo ´(”cÏ£fdÍÒ¨iŸ$°Ø»qnÙœB× Âóþu8[vmñÎÇ‹O½ÈÌ3±^iõyRv)Üxͼ~Óëþëç»°—Šék—]Ëáó‡ý_—ó›À#Ý¡ ¸ Ì{n+(õ¢»>ßãØ£Îyó˜7ìè# ÂÁ7#¿¡YÍfĆùÿb5jÄ‚7?yû;¸NÑÌó23Oß÷´›—(ÔªuÆ7Ñ´)œ¸p¢t~¥S`<}¯Ì_yO#ñ$ V¬ráݱt ®“áê°ª ‡¶âÐÞCú6­ó9çDV”8 Ó@¹ èvJ9‹÷oÏÙ?sýßÇ–”5BR@W®ØáYáìú—~& ûl6u;ÖÅÖÙÿ¢ù™Œô âãâK…²®°Õ`k«­t¾¯3ö¦~zÕÙP;F+g”9ïΡ0µ%/ÁÖÑ¿WƒrX!fo –l¨1OJJâù‘Ï3á³ 8ú;¼͇!懾ÜXDµ+fÅL”%Š(K3ߘɭãnÅ~›Ÿ µfæMšÇÍÝo6dÿš×°ýÔv÷éé¸úœ‚æšs]òu†ëýÚ‹¯Ñ÷ƾ 1€\%GŒÓÑ"še.£K—.†m:Ï9ªÐ¡¾{øwÄËê©Îãpý5×Ó©C'ÍŽbé»Ú‘ÜR¸Å]”[m”úpjÔ¬ÁåÉ—ûí :Ÿ:ïS÷§'¡Ëµ]Úg¨ßN¦§mÏ}{±Ö[cóê9»c>bæ§¹?¡„(nCüZÃÿGÚáÙŸÅqµ‹Z<\òè2'ÚtÈÄÄÿ›HƒÆ ¼„§Ç_Ø„¡z†d„phá!Ì¡flvaÇæ°•nv‡Çs¯tô#–_®;Êbµ0ëÎY˜Séf6™Ëö•²ýùyóùèI.¿—mx(‡ÛÂÙ>f{©-§ ­ýOê~£Ÿ=Ѝï§ch…hÍÁÇ*¾€UÊÓ)Œœ:G_?±›L¼øø‹Ù½}ÐíôèÚƒzppíA 2 ˆ¨AãÚYÿÓú ¯V\4ñìä—¿ðüËÏóÖ‡oa‹*Éð“­føùîÛï Ÿ?ÊEë¤ÖDŠH._(ëœi´-Ê>…‰£&rÏÍþæK•ñͲohÒ¥ ÅwkvÔ•_nhvCP½óÍš5ãäï'ÉÈÈ`ñâÅ <¸Ü<‘+ê#ƒ+Ï BÓ&f>:“{ÿ~¯a-:¶`w«Ý¾‡p3 Åîü¶ù·ŠW¸¡ï lŠÚ­4^,Ë'ú"¾øí·ßxwæ»|½åk’“’¹ëow1räÈr×uÚ{Ó÷ê8Šãh .¤bÚo¢idS¾[ý]ÐòËÜ4è&Öe¯CôîÞ-(n¨qVl0l/##ƒf76£ø?Ó-æZØ»¶|×ÚÉÅôuïß¡¥õ3d¤gP·®ÎäÃâ›Åsîös~½Æ¦ÿ™ølÌgÜ~ûí†l¶êÜŠßZý~.ƒùc3»¾Øexu@t¾¥3›6Ã> ì™ØòµÿQWbšÄ{{®ï9˽ š û¬ö§Ò­o76ÕØ„h®}+P~Wè^ÐuËÖ¶é‹cÇŽ‘Ò9[ªÍ÷´…½Ð9»3é«ü/ƒî$??Ÿš-jRxO¡Ï©ÊV…û›ÝPêÍ„f œíç;öÄô…‰ÙcgsÏ]ÆD–­:·â÷¤ßÕ´¡(?+\~ìrvo5¶ºåŸ•`ä˜_¼t1wüëßÁÄg iM§~324ãbwÉbî~ìn ¯/T§\F§Á¼ÍL›˜6ü°þ‡€³U%2ˆPŸKçÛ¼ÔÀQù8„‘]F$ž¶®ÞJÄêÐÊf¶"ÿɶ¯·¥ªVl ÍÉ6˜–› µþ…Ànÿ8œOþýI¹]Ë–-ùïþË[þ`ý²õÏzˆœ½9,z|#¢FðJ‡Wø}ñïìÞº;¨âàKþÇ]Ÿ tV(¦u&øLëL„Î å‰.O$žAnñ̈g0jV‡Œ]Éó§fžñL…½ ÓK´né:¢·EÃqí ¬P{×XÃâ`Þ”y˜¿ô3±: ꜫcX<lZ±‰ÐU¡n<®(é ·u½-(â`ãIø>ågïž‘ò³B¶6~á{¾¿«f¯Â¼Ø¬zÍ<±‚y±™U³ËC¾vÙZ·'¢ü¨QÏw$ò¿%ÿ Ȧ/êիǸáã0­2yO»8 ‘#ë‘‘‘Ìú÷,ÌŸkü‡P½u2ê$ž¾Yò á‹ÂáÇ VU<÷hØ#hâ`ç¦ô2õ"dnÊO å'…¹!ôRzñÛ–à8G.e*ìÅj¸ˆh)ó­ñ IDAT!B®!W„ˆˆÆbø¨áå¶ç^~A„¦„ åVEp?‚¡Óu&QÿÊúâèѣ岹eËQã²Â|•Y(}aêj–Ë,âowþ­Üõ¼pá‚hÓµˆ¸€{úßÈ#‚²¨G0—÷ǹsçøôÓOY¶fÍ5cè¡ÏÍ×"++‹éÓ§Ó©S'zö섚ªäææ^ôeÍ/6»wïÚËŸ›ÍÆ¡C‡hÚ´iUW¥Z!W"ÔG èJ --dzn]Åçþ™xüñÇ™0aBЧY\ꤥ¥ÉF˃sçÎ1aÂ&Ož\ÕU©V,]ª¦ 8Ðsy¹¿6²mÑF¶-ÞȶE¹¡>R@Wr2¾D"‘H$’K©[ô‘³Ú%‰D"‘H$’Z"‘H$‰D" ) +‰¢¢¢ª®Bµcß¾}Ølþpø+²{÷_;׫6›}ûöUu5ªYYYdeeUu5ª²mÑF¶-ÞȶE©Yô‘º’8u*°¤ì¦L™Bn®FâÖ¿8'N¬ê*T;rss™2eJUW£Ú‘žžNzº±GþJȶEÙ¶x#Ûm¤fÑGVr2¾D"‘H$’K©[ô‘h‰D"‘H$‰$¤€–H$‰D"‘H@ èJBNÈ÷Fúh#}¼‘>ÚÈ BmdÛ¢l[¼‘m‹6R³è#t%!'ä{#}´‘>ÞÈ@md¡6²mÑF¶-ÞȶE©Yô‘A„•€œŒ/‘H$‰äRAê}¤Z"‘H$‰D" €ª®À¥‚ÕjeêÔ©¤§§Ï€èׯ_UWK"‘H$‰DRÉH´Arss9qâO=õÆ cüøñ¬]»Öðûå„|od 62ÐÇè£ "ÔF¶-ÚȶÅÙ¶h#5‹>R@$!!7Þxƒë¯¿žo¼‘ÔÔTæÍ›gøýrB¾72ÐGèã ôÑFj#ÛmdÛâl[´‘šEDX„ôìÙ“#F0|øpÝòr2¾D"‘H$’K©[ôùKy ­V+V«Õo™¼¼<þøã‡Ï2Ï>û,uêÔ1$ž%‰D"‘H$.þôúçŸfÈ!¤¤¤ÆÍ7߬YÎjµ2|øpbcciÞ¼95kÖdÁ‚^å&L˜À®]»˜3gÎÅ®ºD"‘H$‰¤ò§Ð'OžÄáp0bÄZ·ní³Ü¸qãøâ‹/X¿~=999<òÈ# :”mÛ¶•–™0aÛ¶mcÑ¢EX,–€ê!'ä{#}´‘>ÞÈ@md¡6²mÑF¶-ÞȶE©YôùÓ èÞ½{³xñb^xá’““5Ë\¸pÙ³g3zôhºvíJLL /¿ü2µk×fêÔ©€Ú ¿ôÒKìÞ½›6mÚмysFŽi¸rB¾72ÐGèã ôÑFj#ÛmdÛâl[´‘šEŸ?½€6ÂÖ­[ÉÍÍåÊ+¯t;Þ¦M›ÒTuMš4¡  €ß~û;v°cǦM›føYYYÌ™3‡¥K—ºmžÞ£¿Ò±É“'³aÆjQ—êtlÖ¬YÕ¦.ÕåX\\“'O®u©NÇÈÀ«E]ªÓ±É“'W-êRŽ9ªC]ªË1gÛRêR•ÇœšdüøñrŠªA¤€Ž?@Ë–-ÝŽ·nÝšS§Náp8P…ððp·-Ði‰D"‘H$’KŸ¿T»[n¹…ÂÂB6lØàv|Ò¤IŒ7ŽììlJO:•Gy„3gÎ_îóÊt0‰D"‘H.¤nÑGz ZµjpðàA·ãû÷ïÇb±WásÈ ùÞÈ@md 72ÐGD¨l[´‘m‹7²mÑFj}¤€†ÒàÂ_ýÕíøÎ;©S§Š¢TørB¾72ÐGèã ôÑFj#ÛmdÛâl[´‘šE9…ÈÏÏ'99™‘#Gòæ›oêjƒuêÔaÀ€̘1£Bç•C!‰D"‘H*ÊéÓ§9yò¤¡²uêÔ!))©\瑺EŸª®ÀÅ&??Ÿ¯¿þPxV«•¥K—pÓM7Mdd$<ðÓ¦Mã†n mÛ¶¼ù曜={–‡z¨*«/‘H$‰DÀ_|ÁË÷ßÏ­&ÿV9üsæLFŒQI5ûëñ§÷@9r„víÚi¾–žžNÓ¦M°ÛíŒ;–Ù³g“““CJJ ï¾û.·ÜrK…ë––Fzz:]ºt)M;%‘H$‰D(íjÔ`í… ÔðñzpcL ßçälÛ™ÎΩY¤Ú7z(‡ƒœœœ :IKK£¨¨ˆyóæÍæŸ}ûö‘’’BHÈŸ~ $ vïÞMóæÍ«ºÕ ›ÍÆ¡C‡J;¼gabbbפz!ÛmdÛâͥض|ðÞ{|è!þåph¾þ¼ÉDãiÓ¸oÔ¨rŸcذa„……IíDèÉd ªxv"'ä{#}´‘>ÞÈ@md¡6²mÑF¶-ÞT´m9rä©poß¾ºÛ[¯¾”:ß7j«£¢Ðò/ç«£¢*$žAj#Ht% 'ãK$‰DRµœ;wŽÍ›7*[«V-ŸÓ?=iÊŒâbü-­6¨ýðbI¢‚€),„ƒáÀ8p€–-ãàºuüËCÂÃû R·AŽoI$‰D"Ñåã?6”8,,ŒáÇWè\GŽáii˜Š‹uË^Õ£ϼô’!»÷ö뇞´üA¢o¾™Ï¿úÊÍ¡=Æ®·ÞâAþÈbà±ðp¶úÏBÀñãn"¹t;xNœPË”pÐx JçB;½ÏßWPà¸áC6ãââèqà tݰ^~ÊÝ`±0÷ÓO ×õÅ7ߤý”)Ü_X¨é…~_QèóÈ#›ë-Œû‡©^f£DG3:6–7Ž/õB¿a21º¼nIÀH]I|ñé§t,IŸjtô¿ÿÍý÷߯ûÞÕ«WóBjª¡´5ÿ7{v…®{ú÷gïºu„ê, “©(¬úùgC7-›…a&“Û/›Ï<ò?­Z¥[ÎƇ«Vºqú£Oûö?®[."9™/·m3d³u:Ô¼pÁíä;Dº|×ÈŽ‰a§ÁNTÓ¸8èÞ8GD°ïÜ9C6}qìØ1ÞxåÁ,®´¼új=Z·ÜÁƒéß®Ñ.+É  Èáp4hÕ«,X k3##ƒ>W_MœN=íBТW/>Z´Hצ?fΜÉ{O?YG˜Ú…Q¯¿n¨xçw˜ñôÓÔr¹Å%ö]¿ëL‡ƒ^1cÆèڼꪫxë§Ÿø@çºÜg2qÕUWéÚÓãÖÛo§ÆÂ…¼åç|g€¿ÅÇþ¿jÙܤPvƒ ÔfÚ˜1,^½š~êyœ˜Xañ 0`ÈZ̘AšŸßË,Eá÷!C Û¼cøpO™Âý.6w®!„3…ƒÜ/&ÎËŠ¾}yÙÏuyØdbâܹ†múã®'Ÿd뫯ò¨ëb~ˆˆ`ê¿þeØæ´E‹X§½J<Û6àà !\$wîLBB‚1ƒv;ääÐwøpÞŸ9“‡<êZŒúÝm›=þýoÃõÄl† à²ËÔ­qc÷ýZµT/t<< ›BÑ)1QœòQÏS :%&úñýÒ>"Bù8ß;Š"^yî¹€mÞÙ½»øÚGÛÒ5$DdϘ!Ä»ï ñÚkB<óŒ>(İaBôé#DçÎB´j%DýúBÄĸէ«G§‚˜àëw” Äu× qÇB<ý´ï½'Äš5Bìß/Dq±¡Ï2sútñœÉ$ž3™ÄÌéÓ¾žìܹSÌ›7O´jÕJ¤¦¦VØÞŸ) +ÔÔTqS‰Ø âkwßxc@6æ|ø¡xΨ}Îds>ü0huîP³¦ÏFR€xÐdË—/¯r›×GG‹$°¨“g}TÌðñßœ¡(âÙGõoÀn¢¨Hˆ¼‚ º[,,<~< Î………t‹å;«Õkί:„†²ñüyÂÃÃÝ_,*‚‚uŽeaaÙ~ÉcŸ{ïeâáôñ°ù ðL:|ùÌ3àp¨›eûzÏ}¼Öiút¾(*òúnÇ* ]oº‰Á—]føš8é4k–o›=z08%Å[ò8|u]Õ×:®XÁâ¢"êzØ|@Qܾ=·8¿;²Êßë.¯9„ ã–-lv8ÜþGv “¢°¥];5ï¬ëûœhí èøóÏ|ëp¸Í×´…- b²Ù ¸l6÷­¸ØÝ® %à6ú40øVóãz`¸Í…þ/px¶œ6‡¢â¹Î…¾X î¿!‹bcÕ-.®lßÇóöÆñ­ÕŠ˜ª(d=ñDù3oäƒ÷Þ¨pæ Wd}¤€®\ô×Àœ¤$ævì¨6PÅÅ`µÚÿ¸ €?¬V×x­ýĉ¬·Z‰ò°ùŠB­>}x°s粃¾n:_;–“CÚäɬ±Û½>[/“‰Y£FQ/"B½1Ûí꦳_hµÒmÅ ¾s8ÜD‘:˜LlìÖpE1|ÓwÞŒÛÿø#ß áuãì`2±­woL‹zspþn<÷³Xxzölš¯]˽×Ät²XØ2{6&‡Ãû&m³©×BãØg¿ü¶•+ùÇwqèÊwii†D„뱇óõºuü×Ãf&0 ,ŒÍwß ÎùÄZ¾6dáB>q‚×e=0µNà.0ý Ó’mô¶mô8~Ü«Ã5X_£ÓÝÅra¡O!äd?0Xîq¼?0 hâ÷ݳUMr9– ôŒ%«›_•Øárì0ØRN›þx¸UØ9ùؼ^N›Ï€\ŽÍ2€WÊió+`0ÕåØÃ¨×ºâk÷zó*ªÐ Ìo[ÆS¨7c×𴙨7ã7ÊiÓÔßæfpóž}„úyÊ{óï|‰ú{?„è3hêA*€e@-—c7£ €`’ 6xï,MO¸…@7Ôß\vɱš¨×j#îã}zô^œ¡‚?ãQ¯¿aœ g×ußãydzgùBj–¼u,Ð56–ÁžÞóèxú4_ÁYÔ¶åI kBƒ##Ëêæº9ëä絎»w³Øf+õB? nÚ”[bc½;PZ›¿×=^s×­+õBÛN&[n¾“¯Î›¯ý’G‡t\¹’oö¡þ‡:›Íl:TíD»vŽ]7cÇŽeIn.I”xŸcbøvÖ,o'G¶ëÛ´aSa!¡À… ÿøϾöšz¾r2´GÒ6l )0ÂbaéɓƃýÐ>"‚¡EE\¨ïóÅB®D¨Е@ZZ‡gÏæiàãÈH榤¨ Qhh™7Ok_ãØÇ¿ÿÎ+W–¦­y^Q¸¢†_ye™gÓÕ«gäÑÏkþ™e6[©Çø!àÖzõè]îëÑqß>–Ùí¼ L@£·Ö®M¿ðð2­«×Íõ¹ÖkBÐUl:½Ð/¡/>XîZúæ†ê±pÒ ˜J=§ßé-7¸_¨(tÛ¹“ï„à^TáÙAQØØ¡áfs@7|×›qûU«ø®dø¶Ôû|à eöZ›¯×tš Oï™U¬n!À%O×%$„Ï„`[a!/ þV^ú˜L|W»vYy‚BãØŠ‚Vgfâ\{,`2±¹~}ï¡uÏÇr¼6$'‡‡m¶R/ôz`ªÅÂÂøxß‚ÁSxzl£¢ÇÙ³8cä‹€õÉÉLïÜYõ¶9=n®:Çögeñx¿~¬(ñÈõ aòwßÑä²Ë ‹â@X¼p!ßÜy'“²¿ÅűÅ`:5=›8Œî‚ͯ¾úŠÅ}û2Ãá(ͼ±%3³B6ýñôèÑ\þÞ{ÜG‰÷yÔ¨€§?yòܘ14œ2…-BÐQQ8üÈ#¼òÎ;²ùÕW_±¢o_¦:-O ©/nmÙRl±àfÞðE‡¸8‘â±rdɨT›%9‚™yÃv»]´ … Ú)Ø×n·‹vaa"D» wJL¿ü̾h!Þ,gæ _ÜÙ½»¸ÌdÙÙÙA³)„K–, ª½Ê&55Uê t%àÌÂhæ _83r;ó†/œÙ3Ê“%£2m¶‹Š@¼Ä̾pÍÈhæ _8E‘-ˆ‚È™‘£€àfÞðÅS£F‰÷Q3oëæïÌÈQžÌ¾X¾t©xÈd§@tLHŠM83r”'ó†/œ®`u¶„P3rô }ƒœyË,w+Šè¤ïõbÙ\µj•h2=ó†/ž5Jt§|™7|ñ죊N Ÿy#V­Z%âá¢w*œ¼òÜs¢ahhPmfgg‹G|0¨6ÿ H­Е@jjª¨m2Åûì¤CÝûìdùòåbˆÉOñÅ´9mÒ$ñEQ½Ï•@¿k®oÉûìdÔwˆ!ADB¨^èn”¼ÏzØívq]X˜x7È7ÿëãâDš¢Åûì¤cB‚¸CQ.ª÷Ù‰Ó  ï³g‡+X-'·¶lY)Þg'-‚æ)¾˜6[×­[iBÑn·‹+’“ƒÚáµÛí¢eÆAïD?7~|Píé±råÊJ=ß_) õ‘ºHMMÑ"555hÃ:s>ü°R¼ÏNZÖª4O±“¦ AæJ‰ˆ¸èÞg'G5Ìæ xŸˆÚ11ADv»]\ä›±?ž5J4 êùæÎ™#R‚Üa\¾t©hU«VPmúã¶Îƒæ}v’:`€H0 ¨6÷íÛW)Þg'‡ºÍo¾ùF\ˆÂ(gÏž ª==ŠŠŠ‚ns×®]A·y©S\\,öîÝ[ÕÕ¨6,Y²D¤¦¦ŠÆK­ƒ\Ê»’h{ÝuAŒ?ü^Ï„a—u;wRÛ5p+ô¸í6ºwïT›ŸoÜH»ví‚jÓõêÕã[¶P¯^½ Ù ç¦þý½óøV“ÉÄ΃1äU^^›6–;õ|}ûõãëÖÍ@¿hßA+£÷ÅaúÒ¥A·Ù÷n³I“`'­óOƒ ‚nsÑ¢E´nÝš¸¸¸ Ù ¦-#„††ê 7ß|S…y››Ë”)S˜ÞÈ@md 72ÐGD¨l[´‘m‹7²mÑFj}¤€®$äª>ÞÈ@md 72ÐGD¨l[´‘m‹7²mÑFj}da% 'ãK$‰D"¹TºEé–H$‰D"‘H@ h‰D"‘H$‰$¤€®$ä„|od 62ÐÇè£ "ÔF¶-ÚȶÅÙ¶h#5‹>R@W6l --¥K—VuUª 2ÐGèã ôÑFj#ÛmdÛâl[ÜYºt)iiilذ¡ª«Rí‘A„•€œŒ/‘H$‰äRAê}¤Z"‘H$‰D" ) %‰D"‘H$’º’ò½‘>ÚÈ@od 62ˆPÙ¶h#ÛodÛ¢Ô,úH]IÈU}¼‘>ÚÈ@od 62ˆPÙ¶h#ÛodÛ¢Ô,úÈ ÂJ@NÆ—H$‰Dr© u‹>Ò-‘H$‰D"‘€Ð‰D"‘H$IH]IÈ ùÞÈ@md 72ÐGD¨l[´‘m‹7²mÑFj}¤€®$ä„|od 62ÐÇè£ "•†ÿÉ IDATÔF¶-ÚȶÅÙ¶h#5‹>2ˆ°“ñ%‰D"‘\*HÝ¢ô@K$‰D"‘H$ ´D"‘H$‰DR@Wß|ó iii,]º´ª«Rm>ÚÈ@od 62ˆPÙ¶h#ÛodÛâÎÒ¥KIKKã›o¾©êªT{¤€®$RRR˜5küÿöî>*ª:ÿø›ç ypAD@PD…M­I]]MK-mÓÕMk[ÏÖÒ¶©«Öv¶-mm3]³tKÍÚÔ#¢¦æ‰Š(J%ò$¨ €ÈÃóýýÑáþïÆQ¸˜÷ëOÌw¾sç3·¹Ÿùpù~îèJ«ÁF96ú¨±ÑGŽM„rÌ-rÌ-jÌ-¦Æ 6ÀÏÏOïPZ=6j€‹ñ‰ˆˆ¨­`ÝbÏ@Y€4‘X@k„ßê£ÆF96ú¨±ÑGŽM„rÌ-rÌ-jÌ-r¬YÌc­~«}äØè£ÆF96Ê1·È1·¨1·È±f1M„àb|"""j+X·˜Ç3ÐDDDDD`MDDDDdÐá‚|56úȱÑG>rl"”cn‘cnQcn‘cÍb hpA¾}äØè£ÆF96Ê1·È1·¨1·È±f1M„àb|"""j+X·˜Ç3ÐDDDDD`MDDDDdÐá‚|56úȱÑG>rl"”cn‘cnQcn‘cÍb hpA¾}äØè£ÆF96Ê1·È1·¨1·È±f1M„˜5kêëë±víZØÛÛÃÞÞ^ˆˆLÔÕÕ¡®®Ï=÷ìììØDØžÖHJJ -Z„ÄÄD½C!"""RILLÄ¢E‹’’¢w(­Ï@k€—ƒ!""¢¶‚u‹y<­.ÈWc£}ÔØè#Ç&B9æ9æ5æ9Ö,æ±€Ö䫱ÑGŽ>jlô‘c¡s‹s‹s‹kó¸„CüSµ¬[Ìãh""""" °€&""""² hpA¾}äØè£ÆF96Ê1·È1·¨1·È±f1´F¸ _>rlôQc£›å˜[ä˜[Ô˜[äX³˜Ç&B p1>µ¬[Ìãh""""" °€&""""² hpA¾}äØè£ÆF96Ê1·È1·¨1·È±f1´F¸ _>rlôQc£›å˜[ä˜[Ô˜[äX³˜Ç&B p1>µ¬[Ìãh""""" °€&""""² hpA¾}äØè£ÆF96Ê1·È1·¨1·È±f1´F¸ _>rlôQc£›å˜[ä˜[Ô˜[äX³˜Ç&B p1>µ¬[Ìãh""""" °€&""""² hpA¾}äØè£ÆF96Ê1·È1·¨1·È±f1´F¸ _>rlôQc£›å˜[ä˜[Ô˜[äX³˜Ç&B p1>µ¬[Ìãh9r³fÍÂW_}¥w(DDDD*_}õf͚ſlÝžÖ“#""¢¶‚u‹y<­.ÈWc£}ÔØè#Ç&B9æ9æ5æ9Ö,æ±€Ö䫱ÑGŽ>jlô‘c¡s‹s‹s‹kó¸„CüSµ¬[Ìãh""""" °€&""""² hpA¾}äØè£ÆF96Ê1·È1·¨1·È±f1´F¸ _>rlôQc£›å˜[ä˜[Ô˜[äX³˜Ç&B p1>µ¬[Ìãh""""" °€&""""² hpA¾}äØè£ÆF96Ê1·È1·¨1·È±f1´F¸ _>rlôQc£›å˜[ä˜[Ô˜[äX³˜Ç&B p1>µ¬[Ìãh""""" °€&""""² hpA¾}äØè£ÆF96Ê1·È1·¨1·È±f1´F¸ _>rlôQc£›å˜[ä˜[Ô˜[äX³˜Ç&B p1>µ¬[Ìãh""""" °€&""""² hpA¾}äØè£ÆF96Ê1·È1·¨1·È±f1´F¸ _>rlôQc£›å˜[ä˜[Ô˜[äX³˜Ç&B p1>µ¬[Ìãh""""" °€&""""² hpA¾}äØè£ÆF96Ê1·È1·¨1·È±f1´F¸ _>rlôQc£›å˜[ä˜[Ô˜[äX³˜Ç&B p1>µ¬[Ìãh""""" °€&""""² hpA¾}äØè£ÆF96Ê1·È1·¨1·È±f1´F¸ _>rlôQc£›å˜[ä˜[Ô˜[äX³˜Ç&B p1>µ¬[Ìãh lÙ²3fÌÀ°aÃpýúu½Ã!""""°€¶@MM &Mš„ôôt ½Ã!""""°€¶ÀÓO?øøxØÛÛ[üX.ÈWc£}ÔØè#Ç&B9æ9æ5æ9Ö,æ±€Ö䫱ÑGŽ>jlô‘c¡s‹s‹s‹k󬦀6 ÈÊÊBnnn£sŒF#222˜˜Ø¬ož¦ž“ˆˆˆ¨µaíÒ´v_@;v ‘‘‘pqqAPPf̘!wýúu <˜3gºv튿ýíoGKDDDD­]»/ F#†Š?þÑÑÑΛ7oŠŠŠ““ƒüü||úé§HHHÀîÝ»5Œöî?¾Ù׳YºÍºº:ìܹ³Y·y¿Q]]­ë6üñGddd4ë6ï×W_}¥û6÷íÛgöÏê-gcª««‘˜˜¨û6ͽ斈³)Ì-rÌ-rÌ-jÌ-Ö¡Ýб±±xûí·1uêT¸ººJç\½z_~ù%žyæx{{¦OŸŽž={âßÿþ·2ïå—_†ŸŸlllÑ£Gßuµµµ÷÷BîÐ>äJKK›½Ñ§=|È•••YßÝhëruuu(--µèùÌirååå(//·èùÌan‘cn‘cnQk¹¥¹k–öȪ¾HeäÈ‘¨®®ÆÁƒMÆ1jÔ(lÙ²“'OVÆŸxâ ?~EEE÷õ¼>ú(’““ “ûºté'''åvnnî]¥¦¦ÂÕÕ½zõ²ø±ÕÖÖÂÁÁ=zô¸«ÇÖ××#%%ݺukt^ÃZò=z¨¶·cÇÁ××÷žc¾slÿþýˆŠŠ‚‹‹K³í—‚‚ 0ŽŽŽwõØ‚‚ ØÙÙ5:ïÌ™3èß¿?UÛÛ¾};"##ï+æ;ÇöìÙƒ¡C‡6ÛörssqéÒ%<òÈ#wýØS§N!$$×®]ktÞ‰'«zlEE8€ðððfÛ/ƒ‡Flll³í—‹/âÊ•+4hÐ]?öÈ‘#ˆmtž››233¥zljj*ÀÓÓ³ÙöKnn.Š‹‹ÚlûÅÒ|eoo”” 4èžòÕŽ;”ëõ7×~III§§'ºuë¦[¾*..†Á`@Ïž=ï)_mß¾'N¼ïcÿö±#GŽ Gºæ«ŒŒ „„„ C‡ç«ÚÚZ8pãÆk¶ýb0––†®]»êš¯˧ nÏW ë‹‹‹áêêŠÓ§O#** ß|ó HŽ4€õë×cöìÙ8wîBBB”ñ„„¬X±555÷téº}ô6oÞ¬oHüDDDDz‘5 N›6 sæÌÑ!š¶áÞ«Âv¤áO ¿øÅ/LÆ»ví £ÑˆŠŠ têÔéž·?gξ ‰ˆˆˆÚ‰v¿úntíÚ••e2ž™™ ''§û*ž‰ˆˆˆ¨}a  {÷î€sçΙŒ§§§+÷, ÑÑÑðòò©S§”±êêj¤§§câĉ:FFDDDD­M»/ ËÊʰråJ¬\¹ÙÙÙÈÍÍUn_¿~`ooÅ‹cíÚµøûßÿŽ}ûöaòäɰ±±Á¼yóZ,¶ÜÜ\ÄÆÆÂÇÇñññÍ~)¶jĈðôôDpp°Þ¡´›7oFß¾}áëë‹1cÆ`ÿþýz‡Ô*¬\¹ðóóC||æÏŸßÂý¿#GŽ`ÕªUxàðì³Ï"!!A³çnÍhÝ¿ÕJ :Ôäç«W¯¢°°Ðê×è7|ñôìÙ·nÝÒ1šÖ£¦¦/¾ø"¶mÛfriNkg0ðé§ŸbÀ€èÓ§Þá´ ëÖ­CTT"##‘““½Cju>úè#üþ÷¿×;ŒV¡²²RÉ»^^^¨ªªÒ9"ý´ûºµ*((@mm­r…ÐÐPœ8qF£¶¶í~e ݇>ø111V_<7øÏþƒuëÖ!??»wïÖ;œVaÉ’%˜5k–É/ÖÎÖÖ=ö.\¸€µk×*_Lq?×øoΟ?ü“'OFÇŽáàà€;w*_Äbí~øádff">>^ïPZ…Ï>û &L€ÊË˱mÛ6½CÒugŽûpéÒ%dee¡S§NÊ7„ÝéÖ­[8|ø0rrr…ˆˆå>;;;Ô××+· g›½¥âäÉ“(((@dd$"##¥óNœ8}ûö¡¦¦C† Á°aÃ4ŽT[•••8sæ ÊÊÊwwwé¼óçÏãèÑ£ðôôÄ#<777Õœ]»vaÍš5íb tII Ξ=‹ššŒ9R:Çh4âøñãHKKCŸ>}0xð`ÕûøñãñðÃcãÆøÝï~‡h~‹(//Gjj*²²²Ðµk×F?´srr°cÇäçç#""'NT¾å4-- ÇDzeË´ ½E5GnqttÄÖ­[•Û#FŒÀŽ;0a„¿¥4GnéÔ©:tè€'NƇ={ö`ܸq𼆿f0žž®ä–¹sçJçUTT`ûöíÈÈÈ@ïÞ½1~üxtîÜY5oýúõ˜>}z»ø…â~ëxýõ×1aÂŒ3~ø!V­Z…·ß~[‹ð[½×´5IIIÂÓÓSÄ!C¤órssEpp°pss vvvbÁ‚Âh4*süýýEqq±Bˆ#GŽˆ#FhñZÄ‹/¾(@¼þúëÒyëׯööö"..NŒ?^888ˆ7ÞxC5/33³]¬3fŒ°³³SÞ/Î{ë­·„­­­ ]ºt^^^âÌ™3&svïÞ-BBBD^^ž‘·œ’’áçç§ì“ÆÒPuuµ˜8q¢pttÎÎÎ"&&FܸqC:¿®®N¸¸¸ˆ+W®´dø-æË/¿TŽ[[ÛFsË©S§„§§§Ó§OîîîbÔ¨Q¢ªªJ!ÄgŸ}&‚‚‚”¶¶¶" @dffjøjšOsç–óçÏo½õV EÝòš+·lß¾]<ûì³ÊíW_}µÉýÖš]½zU8::*ÇPc¹¥¤¤DDFFŠnݺ‰§Ÿ~Z‘m2¯®®Nxyyµx?TKk®º¥°°PtêÔIÔ×× !~î1svvVn[ÐJMMï¾û®8tè2dH£oıcÇŠ>}úˆ²²2!„‰‰‰ÂÆÆFlÛ¶M™3cÆ ñÊ+¯ˆ7nˆiÓ¦5úÁÐ:tHúè#éü¶ÞDØ\¹%##C¬ÜöòòÂÎ;µO7™™™°±±1Ù/ðòòBff¦2¶jÕ*=ÂÓÍéÓ§;;;e¬_¿~Ê}ƒ ÂÒ¥K±téR½BÔ\ee%²²²Tk£ûõ뇵k×ââÅ‹ Áš5ktŠP W§ 5ïׯ¥Y·n]‹Ç¥·†Ürû•5îÌ-}ûöÅ–-[ô Qw“[`Ú´i˜6mš.1j­¼¼—/_ÆôéÓMÆûõ뇚šdgg#,, ]?Ý5Ô-²Ür{Ý2dÈ 2DëðZ%^î¡ÈÃ`0è–î УG8;;›Œ÷ë×OÙgÖ¨  @õ^ †ƒƒƒÕî—¦Ž¡Ûï·6Mí—7nXí%¥ àë닎;šŒ3·0·Ü©áuËNpÝ~¿µaÝb9Ð- ²²”KÔ5pww‡Âj?äêëë¥ÌŽŽŽ&W$±6•••ª÷Šƒƒ:v쨼—¬MSÇÐí÷[›†ã¤áŠ Ž+k=ŽêëëUû`nanQkx?ÜùYdíÇë˱€n^^^ ú2sçΡC‡puuÕ#,Ýy{{ã§Ÿ~BuuµÉxzzºU_¯ÖËËKõ^),,Ä7”÷’µiêºý~kÓpœ4ì‡ééépqqQµÌ-rÌ-jMC·ßomX·XŽt ðõõ ?@î³F0&hyy9rrr cdúòõõm4™[ëû¥sçÎxðÁÝ/=zôÐ#,Ý5'ßÿ½ÉxzzºUC ¹åÂ… Ês s‹Œ»»;ÜÝÝ¥ûÅÎÎ~~~ú¦3Ö-–c݈ž={"99Y+//Gzz:¦L™¢cdúš:u*””¤Œ54>͘1C¯°t7eÊ\½z/^TÆŽ9‚Ž;bôèÑ:F¦Lž<'OžDmm­2~ôèQ 2]»vÕ1:ý„……!<<ÜäÊÍÍŹsç¬újÈ-{öìQƘ[˜[óôÓO#%%eee€ÚÚZìß¿cÇŽmôËhÚ;Ö-÷@ïË€´5%%%báÂ…báÂ…Â××WtëÖM¹}û8lذAØØØˆ—^zI|òÉ'"&&FtëÖ­M_w´)III"..NÄÅÅ [[[Ñ«W/'ž|òI“yK—.öööâù矋-...böìÙ:EÝò6mÚ$.\(¦L™"ˆ_ÿú×báÂ…&—櫬¬¡¡¡"44T¬_¿^,Y²D888ˆ%K–èyËzã7ÄÂ… ELLŒ CGUæœ={V¸ººŠ±cÇŠM›6‰™3g GGG±wï^#o9EEEÊ1äéé)ÜÜܔ۷yNRR’èСƒ3fŒøóŸÿ,üýýÅÃ?¬\»µ½an‘cn‘›1c†ˆ‹‹€òÞÙµk—2'77WôìÙS„‡‡‹×^{MÄÆÆ åšóí ë––ÁËØY¨¾¾EEE€èèhPn×ÕÕ)ófΜ ggglذû÷ïGDD6mÚÔn×¹»»£ÿþ ü\\\Læ%$$ $$;wîÄÍ›7ñÏþÏ<󌦱j©¬¬Ly4ü_TT„ÒÒReN‡pøða$$$`õêÕðððÀªU«ðüóÏë³®]»†k×®¡{÷î˜2eвnolêׯ:„7ß|+W®DïÞ½‘˜˜Øn¿úÝÑÑQz €“““òóðáÃñí·ßâÓO?Źsç0gÎÌŸ?=ô¦ñj…¹E޹E.$$^^^ªcÈÃÃCùÙÇÇGÅ|€´´4ÄÄÄ`ݺu Ô:\M°ni6B¡wDDDDDm×@Y€4‘X@Y€4‘X@Y€4‘X@i,-- YYYz‡Ñ¤¢¢":tÅÅÅÒûSRRpéÒ¥&·qýúuù为ºâ§Ÿ~BZZzõê…'žxUUU~>3=|øp„……!##%%%X¹r%.\ˆo¾ùÆd[¯½ö¦NŠ+W®`÷îÝÒ8«ªª0nÜ8xzzâèÑ£ÈÉÉÁ¬Y³0gÎ>|Àσ3gÎD\\„f—µ¼óÎ;èß¿?òòòpòäI8;;cýúõÒ¹~~~Êúóääd! „|ðÁ¸|ù2rssqõêUTUU©~!"jMX@‘UùÕ¯~…Q£F™= }·^zé% 2;wÆÜ¹sqãÆ „„„`Ö¬YpqqÁœ9sàé鉽{÷š<ÎÎΫW¯F·n݆åË—£¨¨_~ù%àý÷ßGuu5Þ{ï=„††ÂÅÅ/¼ðˆµkךl+::úӟйsgtêÔIç¶mÛŸŸ7ß|ƒ†··7V¯^Î;ãwÞ¹§×Þ«W/¼þúëðòò€0yòdìÙ³ÇâídddÀ××{{{Œ9òžb""Ò›‰Èê,_¾?ü0¶oߎûÚV\\œòshh(`̘1&sBBB––f26xð`¥¹¢¢¢ðÐC)giÏœ9ƒŽ;bÁ‚0BÀh4âúõ먯¯7ÙÖ¨Q£ÌÆyḸ¸ ::Z³±±Áã?ŽS§NÝå«5uç댈ˆÀ²eË`0öTzƒæIDATàèèx×Û™7oâããáåå…ñãÇcöìÙ&qµ6, ‰ÈêôïßS¦LABB¶nݪºßÆÆF5vgÃ]gggÕãnk¿³)î0¹mggåšÉÕÕÕðôôTÇñññpuu5óöö–Æv»ºº:8::ÂÖÖô<ð€Ò„h©;_gö-mŒE^^6n܈­[·"&&±±±8xð ôÿ‘ÞX@‘UZºt)úôéƒ7ªî U]Í"))©YŸÿÛo¿Emm-§OŸFII‰Ò¨†}ûöaÔ¨QèÒ¥Ë}?_ïÞ½QRR‚Ó§OcÀ€ÊøÞ½{~ßÛ¿ ·ìj"ÎÎΘ7oæÍ›‡“'ObàÀØ·o†®IlDD–àh"²J˜={6V­Z¥ºoôèÑøú믑——‡ÚÚZlݺß~ûm³>ÿ­[·°téRTVVâÊ•+X±b\]]1aÂ?/kpttÄÿøGüðÃÊã~úé';vÌâç›4i<<<°dÉäää ªª o¾ù&òóó1wîÜf{]Mñññ¿¿?MŠè;vàÖ­[Êí†Ëç54Tµ6, ‰Èjýå/‘Ž?ùä“(..FÏž=ѵkWüõ¯Å¢E‹šõ¹§NФ¤$øùùÁßß{öìÁ'Ÿ|¢4úúúâ‹/¾À7ß|ƒÀÀ@ÃÛÛAAAÊ—ÂXÂÕÕÿýïqìØ1ôîÝ>>>xõÕW‘€±cÇ6ëkkÊâÅ‹ñÅ_ÀÙÙYYÆòÆo sçÎ C`` âãã1sæLMã""²„h¸ŽQ;vþüytéÒE¹öpƒÜÜ\TVVÂÇÇÇdMoqq1’““ááᢢ¢%%%ðóYÒÂÂB+1ÈÊÊRm+77NNNÊRŒ†ÛžžžHKKCaa! ¤\…âv•••HOOÇ¥K—àîîŽþýû›|‹ßùóçÑ­[7¸¸¸ÜÕ~(++Cjj*ÊËË=z˜Ü_XXˆúúztïÞ½ÉídggÃÅÅÅäzÍÈÏÏGPPlllpëÖ-äåå!00Pµöº  7oÞDpp0ŒF#ÒÒÒ  4NNNwõzˆˆôÀšˆˆˆˆÈ\ÂADDDDdÐDDDDD`MDDDDdÐDDDDD`MDDDDdÐDDDDD`MDDDDdÐDDDDD`MDDDDdÐDDDDD`MDDDDdÐDDDDDø?¹š\_¹l§>IEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/Q8-1g-noidx.svg000066400000000000000000004220331231437614300236410ustar00rootroot00000000000000 PyTables-v.3.1.1/doc/source/usersguide/images/compressed-recordsize-shuffle.png000066400000000000000000001546121231437614300276640ustar00rootroot00000000000000‰PNG  IHDR@°AàÚ²sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwX××ðï²ÀÒ» E)¡ª(EÄ‚%J,hl=ÖÄü"±$Fc×Hì¢&&64± ±ÄŠ%¢ "UºˆT=ï¼;a`A² ÷ó<<:wÚ™;»3ggîÜa†a¦‘“u Ã0 Ã0M%@ Ã0 ô8,b†a¦Åa Ã0 Ã0-K€†a†iqXÄ0 Ã0L‹Ã †a†aZ–1 Ã0 Óâ°ˆa†a˜‡%@ Ã0 ô8,b†a¦Åa Ã0 Ã0-K€†a†iqXÄ0 Ã0L‹Ã †a†aZ–1 Ã0 Óâ°ˆa†a˜‡%@ Ã0 ô8,b†a¦Åa Ã0 Ã0-K€†a†iqXÄ0 Ã0L‹Ã †a†aZ–1 Ã0 Óâ°è=¢¢"Y‡ñÎJNNÆ«W¯š|½ÏŸ?Gfff“¯÷}•ŸŸgÏžÕjÚ’’ÄÇÇËd¿6"B||<Š‹‹eJ½#>>D$ëPÞ)xñⅬè"55©©©²ãÄ 6~üxx{{cذa˜:u*þ÷¿ÿáâÅ‹Uׯ_‡§§'߸Ìââb˜››#$$¤±Â~gœ9sOž<©ó|Ý»wÇÁƒ!¢šÍœ9³gÏnòõ¾¯áèèX«içÎ //¯IƇ5kÖÔzú»wïÂÓÓÑÑÑõ^·Daa!ÌÍÍqûöí[¦,ܹsæææ(((x«ùCBBàé鉸¸¸ެnvíÚ…uëÖU;þñãǘ?>zöì kkküý÷ß5.ï³Ï>ÃÒ¥KëÓ•+WpïÞ½z-£²˜››ãúõë ºÜæ€%@ ìÌ™3hjj"44^^^èÕ«¦¦[[[ˆD"Y…úNúòË/qöìYY‡ÁÈØßÿ]»v!00zzzõ^ž…… k=½ªª*lmm¡¤¤Tïu3|²>ö]¸p+W®Ä—_~‰ððp©Óœ8q®®®ÈÈÈÀ„ °hÑ"èèè4zlkÖ¬ÁÞ½{t™C‡Å¢E‹0vìØ÷þêcC“—uÍч~ˆ3fpÃOŸ>…§§'†ŠÓ§OC^^vvvX²d ZµjÅMW\\Œ°°0dggÃÌÌ ÖÖÖÕ®C,#11šššÐÖÖ®vºgÏž!22jjjèСÔÕÕYYY())žžÂÂÂðüùs¸¸¸ð–UTT„Ç#==ÊÊʰ··—zHKKCDD444`ooÏ;°ååå!44EEEptt¬ñd–‘‘’’dee!>>жm[ÈÉÉáÙ³gˆ‹‹C~~>ŒŒŒÐ©S§j—SqÛE"ôõõ¹:‹ˆˆ@||<,--akkËM›››‹W¯^ÁØØÑÑÑxöìzôèyùÚ}EJJJŠÜÜ\¸ººrõœšš EEEèêêVÙV99¹j룤¤‘‘‘HII¢¢"Úµkƒcxõê^¾| @JJ ttt¸“xjj*îß¿---899U9àÑ£GÈËËC‡xŸÍ’’’l‡¡¡!ìíí! ¹qiii‰DÐÖÖFqq1ž?LŸ>½J2SXXˆÛ·o£¨¨®®®ÈÍÍE«V­ ¬¬ KKK,Y²FFFÊoݽxñmÛ¶E||<ž>}ŠîÝ»óꈋÄÄD¼~ýæææ5~‡¥‘V—jjjPUU¼xñ÷î݃H$‚““Wǯ_¿FDD233aggcccnœX,ÆÃ‡‘””„N:ñÆ•”” 99&&&——Ç‹/  ¡¥¥Å­÷Î;PUUEiii­¶%//aaa(,,„••Ú¶m °¶¶Æ’%Kкuk@RR’ÔeJ¾û@yýß¿ùùùpppà¾Ó™™™ÈÏχ©©éãºrå ª=nfggc„ X½z5¾øâ‹ZmkEÅÅÅ EAA\]]¹}—’’%%¥*Çд´4(**B    ¯^½âކ††ÜgìåË—¸wïˆNNNܾ‘ëUUUÑ¡Chhhpã–.]Š`Ïž=˜>}z·©Ù"¦AéêêÒÖ­[«”ß½{—Ðõë׉ˆ($$„Pvv6ݾ}›Œ©U«VdmmMrrrôÉ'ŸQQQ K—.QYY?ž¬¬¬())©ÚX|}}IAAìììHKK‹”””($$„ˆˆ¾øâ ²±±¡Ž;’ªª* ÒÓÓ£;wîpó÷éÓ‡”••ÉÆÆ†ŒŒŒHEE…8À‹Å4þ|’““#}}}RQQ!züø1?~œttt¨M›6dbbBêêêtêÔ©jã;v,ÉÉÉ‘²²2ijj’¦¦&eddÐõë× ’‰D"rss£ÜÜ\n^sssúù矹áU«V‘ŽŽýûï¿DD”@îî¬L:u"@@Ÿþ97ýúõëI__Ÿ¼½½I$‘ššÒÅ‹«—ˆhäÈ‘äàà@VVV¤¦¦F¨mÛ¶MDDóçÏ'kkk‹ÅÜ$\V´bÅ  …Ô¶m[’——§Î;Sbb"7ÞÝݦL™B&L EEE@b±˜zôèA_}õ7Ý­[·¨M›6$//O–––¤¡¡A€Îœ9CDDaaa€’““‰ˆèСC$‰è³Ï>#ÒÐÐ ]]]:vì·Ìýû÷“@  ¶mÛ’ …B6l•––Q~~> +W®T»íýõ #GŽ©©) +VQùgT$‘ ikk“±±1…††ò¶É‚„B!™˜˜@  9sæQjj*uëÖäääÈÌÌŒ-Y²„›7""‚ÐîÝ»ÉÞÞžÐĉ‰ˆhïÞ½¤¢¢BêêêdjjJªªª€òòòªÝŽS§N‘¶¶6µiÓ†[ߢE‹ˆˆèßÿ%”‘‘ADDÖÖÖÜ÷]SS“D" îû}úôijÕªQÛ¶mIUU•Ž9Â[߸qãHNN®Úx¤ñöö¦Ï>û¬Jùwß}Wå;[^^^Ô¥K255厖––ôìÙ3""úüóÏÉÙÙ™7Onn.©©©ÑüAß|ó ÉËË“¢¢"W’ãõ¾}ûHMM,,,ÈÀÀ€tuuéòåËÜr¾øâ îX¯­­MJJJtãÆ Þº¾ýö[êÒ¥K¶©¹c P«.""ÒÑÑ¡5kÖQÕ¨gÏž\ÂCD”™™I‡""~$‹iòäÉdmmÍœ¥ùçŸïyþüy.9ùâ‹/ÈÀÀ€N:E¥¥¥A;w&OOOnúÐÐP***â†W¬XÁ;yíØ±ƒ”””èØ±cTZZJ¥¥¥´gÏŠŒŒ¤ØØXRVV¦C‡‘X,&±XL«V­¢Ö­[SqqqµqÛØØÐÆye™™™¼“aFFñ¦«˜­]»–tuuyÛÞ»wo5j½|ù’ˆˆž}:ÙØØPJJJqH2?ÿü3/‰‘øâ‹/èÃ?ä•mܸ‘äääxÓgddÐñãÇiãÆ4yòd^Ì...äåå%uýË—/'KKKzüø1EFFRdd$wB ¯6ni Qù—þîÝ»´sçNúá‡èƒ> Y³fqã% ЦM›HOO;ñ•'!èĉ\,‘‘‘äììÌý ^¿~=™››óÖ@zzzÕÆJTž3†W¶lÙ2ÒÔÔä† DC‡冻téB‹/®q¹DDÙÙÙôçŸÒæÍ›iÞ¼y ÊA­"É ÊÜÜÜhÒ¤I¼m÷óó£6mÚц HNNŽ233«ÌûôéS.騨uëÖÜÕITXXÈ›fâĉÜAZâàÁƒoL€æÌ™C#FŒ¨R>~üx²³³ã•íØ±ƒpuwwwÞ‰W¢b\åDDDo¼¤¢¢Â›>((ˆxß—ââbºzõ*mß¾V®\IÚÚÚ´iÓ&"ª[ôúõk^ù¨Q£ÈËË‹·wìØAòòòôúõk:~üx•<EEE$''Ç]Ñ“puu%ooo"ú/ŠŒŒäMãççGªªª¼-W¯^}cdkkKü±ÔiÕ%@%%%Ô³gOúøã¹«/?ýôµiÓ†"""¸í¾qã ‚*û¯®ªK€œœœÈÑÑ‘Nž}úÔ¯4ÙÙÙ\ۛʱH‹C2NÒ¶¢ºÏ·D\\ÔÕÕáââRçØ*RSSCII ^¿~ ‘H„Ë—/cĈhÕªœÑºuk…Â7î3i*oCLL ’’’0jÔ(^yûöíñòåKÄÄÄ@WWU–±X,µî‚ƒƒk\oll,ºwï^ã÷DšM›6aæÌ™066†¹¹9F ÔØxÁ‚HKKéS§¸8bbbðòåKŒ3†7mÇŽQXXX§˜êâÃ?ÄÇ °±±ÁÎ;áììŒû÷ï£K—.µ^N§N°yóffΜ‰ñãÇcݺu¸ÿ>âãã1uêÔ—ƒ¸¸¸*û¾S§NÈÍÍE§NðÍ7ß`Ñ¢E˜7oïX/iC”7ìWWWgÝvTÀ &ræÌäååÁÓÓSêx'''ܾ}ááá Á–-[0lØ0Þ#³ººº˜:u*>ûì3\»v –––5®sÕªUX¸p!BBBpöìY|úé§PSSàAƒ¤N¡PCCC„……aÍš5¸zõ*wà¼{÷.öíÛÇM¯¯¯_ícÂúúúèС®^½ZcŒÒP¥.¾ýö[XZZâÏ?ÿ„‚‚H]oQQ–/_ޝ¿þNNNèÑ£ œ:uŠkˆÙ˜$ Â% Tûõë ìØ±‰‰‰2dHqddd`áÂ…8|ø0—`¦¤¤`Û¶mo¾¾>|||ðÕW_U;>..eee¼ŸFÚ111\ÂOž@€Ž;bêÔ©X²d bccy¿çÍ›‡íÛ·£GøðÑœœ\í:ããã¹»——6mÚKKËj–ÒÒRœ‹<< ç%AoÚ‡eeeˆˆˆ€¼¼<\\\0oÞ|8Í;—ŒIKK‹k;SZZJmÛ¶%GGGš;w. 0€tuu ·Îääd244$]]]úòË/iΜ9ÔªU+®¡¦©««ÓÌ™3iÅŠ4bÄRVV枊¨.n Z¼x1Íœ9“²³³i÷îݤ¢¢B>>>4mÚ4²°° MMMúâ‹/¸ù*?æëëK­ZµâÚ4?~œ”””hðàÁ´råJòõõ% Z½z5IotüøqRVV®6V¢ò6@ÚÚÚôÅ_Ðÿþ÷?²²²"--­*murrrHUU•:vìXãò$\\\ÈÖÖ–æÌ™CÆ #@aaaÕγmÛ6222ªRž™™I–––dmmMK—.¥¯¾úŠ<==y±Œ=š„B! :”¾ûî;rqq¡Ù³gQù(B¡ú÷ïOóæÍ#---êÚµ+÷t“¤ PåöÏž=#mmmÒÕÕ¥éÓ§S·nÝHWW÷m€BBBH^^žÒÒÒxå………dooO­Zµ¢E‹Q÷îÝIAAkÈKTÞHò´QE•ŸûñÇINNŽtttHSS“ºvíJ(((ˆˆj×Hòt¢äi¥¯¾úŠtuuiÊ”)4qâD211!Z¿~=Õ­ ¤Q¬DQQ¹ºº’‘‘-Z´ˆ¾ýö[0`ikksÓ,^¼˜Pß¾}iÅŠÔ»wo2d•?!‰ÈÝÝ–,YB­[·&+++.vI ICy IƒqUUUš2e õïߟôôôjlTTTDB¡†N«W¯¦ùó瓞ž÷Ä\å6@fffdhhH¾¾¾¼¿ŠOO©¨¨ÐôéÓiÅŠ4jÔ(RSSãÅZ—§À6mÚD‹/&+++êС-^¼˜¸ñ¥¥¥äêêJvvvôã?Òˆ#H$ÑÚµkk\®——éééѬY³héÒ¥dffF­Zµ¢ØØXÞtiii¤¨¨HU–ñÏ?ÿ‚‚Íš5‹æÏŸO$‹iÀ€¤££CóæÍ£ï¾û޼½½IQQ‘ hß¾}¤¯¯O3fÌ µk×Ò˜1cHWW—÷”,Qù1®rº–Nè'­aS/æææ000@§Nàíí-[¶à“O>áM# ®®OOOÈËË£OŸ>ÈÉÉÁÓ§OQ\\Œ‰'Â××€ûóôô„––„B!† †üü|¼~ý666Ub°±±¡¡!’““‹¶mÛÂßߟëKæÌ™30`Ñ»woøûûsË’““È#PXXˆØØXxzzbÍš5ÐÑÑA¯^½   uuuLž<ÊÊÊHNN†¢¢"æÌ™ƒž={BNNÆ ƒ££#ž={†ØØX˜™™ÁÏÏöööÕ¶Ó°··‡³³3|,_¾°··‡••7¯››×vdÀ€PPP@FFaggdee!** ÊÊÊ;v,Æ‘H@¸¹¹ñö‘žžw+M@€=z ¬¬ =‚««+öìÙË „B!¶oߎ%K–ÀÉÉ©¦ÀÛÛB¡?FçαeËhhh W¯^\¿"ÒÃÝÝW¦¢¢___¨ªª"66YYYpuu…ŸŸ×îgøðá°³³Cnn.233ѯ_?̘1***ppp@ïÞ½‘’’‚ŒŒ |úé§Øºu+wE÷¹¨Øæ@CCÇGQQ²³³1|øp,Z´­[·æÚîHcdd„€€Þ yyyL˜0 ˆˆˆ€µµ56mÚ„Þ½{óæwrr‚……¯L ÀÁÁ+÷ðð€ºté‚™3gbÞ¼yøá‡°xñbèëëC @UUžžž\û+ƒ*WL$ûD(¢[·n077ǃ`hhˆ 6ÀÒÒ]ºtáÚòÉÉÉ¡W¯^ÐÔÔ”ºí:::ðôôä}Gäåå1iÒ$!>>)))°··Ç?üÀÝšìÛ·/ÜÝÝ‘ŸŸÔÔT¸¹¹añâÅÐÐÐÀ|€!C† 33 ðööÆîÝ»¹þªTTTàéé n½ŠŠŠ;v,ÊÊÊðüùsôéÓ«W¯æöwå[¦’X{õê…çÏŸãÉ“'X´hïvŒššwyùò%œ¡¦¦ÆûëÒ¥ ÔÔÔ0xð`¸¹¹!)) 111hÓ¦ ¾ùæ8;;óêÈÎÎ={ö”Z¯Ý¿EEE°··G»ví //tèÐÛGãÆP~Û_GG?üð|||j\®@ @ïÞ½QPP€Ç£GØ»wo•+.[¶lÁÊ•+Ѿ}{Þ8SSSôêÕ (++CŸ>} ¡¡ØØØ !! °²²Â÷ßÏõ5ellŒääd®~üýýy·¬‰Ó¦MÈ#ªm†Ñ ˆØ ]Z¢3f ::šwYœi<˜7o’’’ ¬¬,ëpÞyû÷ïǬY³)µAt}%''ÃÈȈ;nܸkÖ¬A\\ëiT[·nÅêÕ«_ëŽVëkçÎX°`âããkì8·¥iöm€ÊÊʘ˜ˆœœ©ãÅb1RRRØ‹ý˜FµnÝ:L™2…%?µ4~üxî*Íëׯ|ù UJ:í IDAT.„®®.\\\`ee…åË—c÷îÝ,ùa•X,Ɔ 0}úô&K~BCC±`ÁüüóÏ,ù©¤Y_š;w.vìØ]]]äää gÏžøí·ß¸Ë¾Gޝ¯/455‘——‡€€|ôÑG2Žºi„……!//]»v•u(Í^aa!‚‚‚Э[·y¯UK‘••…+W® sçμî BFFîܹƒŒŒ ÁÅÅ¥IÞõÄ´l¯^½Â… ¸æ MáÆ(((@ß¾}›d}ï“f8pýúõƒ^½z̘1³fÍBNNLLLpéÒ%¸¸¸ ((cÆŒAjj*ûÈ0 Ã0Í\³¾6nÜ8®Ñ¯ŠŠ ÊÊʸigÏž…­­-×Zÿþý¡¦¦VåQ`†a†ašŸÑâ7ß|ƒk×®aÔ¨Q8p €ò7çV~s°¹¹9¯¿•ÈÈHDFF6i¬ Ã0 #KJJJðòò’u®E$@rrr011ÁÕ«W‘––äääT¹Õ¥¬¬Œ—/_rÇÆáÇakkÛè1¦¥¥AKK JJJo5||<ÌÌÌlÚš¦©nœ´òÊe¥¥¥Ü>xúôé{µn¯_¿Fffæ[?UT—}”››‹ÒÒÒÛ•4Tgdd@MM÷øråéšªŽ«‹±¶êºÞ´®¬¬,ÈËËóz–¨nI˜¨ØFCÚç¶òºŸ={}}ý&¹…^Ÿ:®ëü²<^TÞGÈÊÊjð6aÒHûÔE]ê¸6ǖƪãÊeÅÅÅ EZZZ­b¯É® ¢¦7iÒ$®ó¼ü‘{ D×®]iÏž=Üð²eËhÙ²eMÛÞ½{ßø¶ášÔ%ÎÚL[Ó4Õ“V^¹,;;›6lØÀ+8pàãi©©©´}ûö·ž¿.ûèæÍ›töìÙ§i¨:þí·ßèñãÇ5N×Tu,mÝuQ×}ô¦u={–nÞ¼)u\uûèÒ¥K\§£Ò>·•×ýùçŸSDDÄcnõ=.½/Ç‹Êû($$„ë ³±IûÔE]ê¸6ǖƪãÊe©©©d``Pc,ÍE‹J€.\HcÇŽ%"¢£G’µµ5o¼®®.ýóÏ?ÜpS&@/^¼¨ñMÃo’ššÚ ÓÖ4Mu㤕W.+--­ò親㒒’j{ήºì£üüü*=±VÖPuœ]¥æÊÓ5UK[w]Ôu½i]¹¹¹”ŸŸ/u\uûèÕ«W\éÒ>·•×½}ûözm{]Ôw=ïËñ¢ò>Š‹‹£½{÷¾1ž† ísPu©ãÚ[«Ž+—µ¤¨Ù6‚~ýú5|||„ØØX=z»víˆ#ƒ BNN¶nÝŠôôt¬^½ÚÚÚo|7NcÑÑÑ©ó—+ªØëgCL[Ó4Õ“V^¹L(ÖéÅ• I^^^êÏk«.ûHEE…ën¡: UÇZZZUn»ÔåóÐÐê³îºî£7­K]]wk°¢êö‘¤'⊤}nß×:®ëü²<^Ôæ{ÔX¤}ê¢.u\›cKcÕñ›–Ýœ5Û6@’·š/]ºÉÉÉ066ÆÆáíí ‰D¸páæÍ›‡5kÖ cÇŽ ªö Lã6l˜¬ChöX7¾>}ú°7m72===™ýHešŸfýôÓO5NÓ¡C7QDLunܸŽ;Ê:ŒfÕqã{ðàtttÞúAæÍrssñèÑ#|ðÁ²…išuGˆõ%yO,{_,ü_qñâEY‡Á0ï¡PȽìUš´´4888´ˆ§Àší †aZ®{÷îáÛo¿­ò¦x†iÉÊÊÊpäÈ‘ –„%@ŒÌ¥¥¥µØFxM¥%Ö±““öíÛ'ë0æQTT„#GŽÈ:ŒwF³} ŒyÊ:„fÕ1Ã0 K€™›6mš¬ChöX3 Ãð±ˆa†a˜‡%@ŒÌµ„§ dÕ1Ã0 K€™cíS«c†a>–12ÇÚ§4>VÇ Ã0|,b†a¦Åa #s¬}JãcuÌ0 ÃÇ FæXû”ÆÇê˜a†%@ŒÌ±ö)Õ1Ã0 K€†a†iqXÄÈkŸÒøX3 Ãð±è Š‹E(*’uÍkŸÒøX3 Ãð±è Ö­›€YGѼ±ö)ÕñûïÇ„··7¶oßÎ+OHH€··7òòòdYó3bÄlÞ¼YÖa0L^Ö¼ëJJáïL*ëH†ijb1ðúuÓ¬KI©æñ·o߯­[·ð÷ßcذa000äææâäÉ“xÝT¶–––\ý2Í»T ¡¡À;²Ž¢ùbíS«ã·sê ¬Ü4µ¹Õ>lØ0˜ššbåÊ•oœ699gΜAXXÄbq­¶·¤¤áááFbb"oáÑ£G8sæ xãÊÊÊׯ_#33AAAxòä 7>)) çÏŸGAAo¾—/_"%%D„‡âìÙ³ÈÈÈàM“––†ììl@qq1’’’¸qb±>Ä_ý…ÈÈÈ*ÛSXXˆ[·n!88OŸ>­2þÑ£G ½{÷PZZʕϞ=^^^¼ióóóqõêU\¾|¯^½’º…K—.¡¬¬¬Êú˜w 1ÕZ¶lDѤI²Ž¦ùÚ¾}»¬ChöZZŸ8q‚† ÒË!îÐØ……5Ç2lØ0š5k8q‚)66–ˆˆÂ½xñ‚›vÉ’%$ÈÌÌŒäää¨[·n”ššZãòÃÂÂÈÎÎŽäää¨M›6€&L˜@DDYYYÔ¯_?dnnNhÆŒTVVFDD)))€>ýôSRVV&mmm@~~~4sæL …¤¥¥EšššÀ­såÊ•dddDݺu#%%%’——'555:þ<7M÷îÝiâĉ4iÒ$RTT$ôúõkzöìyxx’’uêÔ‰Mªp ¾víµnÝš ÈÒÒ’ääähܸqDD”““Cîî¢BíÛ·'%%%jÕª•””‘³³3­\¹’[ÖÅ‹ÉÀÀ€455IGG‡tttèôéÓÜø~øŒiРA$‰HUU•ŒéêÕ«5ïÔ&VXXH"‘¨ÆiRSSÉÀÀ ‰"’-v¨–^¾”uÍkŸÒøX7ÞÞÞprr²eˤŽ ÄÚµkñçŸ"..QQQ(,,ļyóª]fII F DGG#11ñññptt,]º‰‰‰Gll,.\¸€½{÷bÿþý¼åˆÅb$$$ ++ ?þø#üüüPVVÆ]Å™2e ~ùåÞ<¯^½Â´iÓ››‹§OŸÂÝݳfÍqÓüúë¯066FHHDnn.¾ÿþ{©u“——‡1cÆà“O>Á³gϘ˜ˆI“&ÁÇÇ/^¼à¦ËÊÊÂàÁƒ‘››‹—/_ÂÑÑ{÷î­¶ÎÙc P-€5†f˜F(D¢¦ùj×êÕ«qèÐ!<|ø°Ê¸€€8;;s'{KKKŒ;üñŠª¹ÏvíÚ5<~ü ,€……ÀÔÔ³gÏ8pÇGûöí½{÷F÷îÝPé 8gδjÕ Ð¿À’%K §§Ç•…„„ðâhÛ¶-|||   SSSL:QQQ¼[pÓ§OÇòåËáè舎;"11ÁÁÁ=z4RSS…ÒÒR8::âêÕ«555$$$ .. ¯¯Q£Fqã^½z…ÿýDL™2òòU›Å!==óæÍƒºº:TUU1þ|äääpÉ–¤¾¦N EEE…Bx{{#((Hj}3ï–ÕA¥.LaíS«ã·3xpyÛœ¦ø‰jWÏž=Ñ¿|ýõ×UÆÅÄÄÀÃÃWæáá±XŒøøx©Ë‹‰‰H$ª2¤§§#//Oê2cbbªQQQ±J™ššÊÊÊjl°-¹êT±­œÿT%YïòåË1jÔ(¸¢ÿ¯Èï¿ÿ¹¹¹°°°€‰‰ fÏžÔÔTÀ¸qãðÑGÁÓÓšššŠ‹‹ñìÙ³*ã´µµ! ¥.³ºåÕGXX€ò+CÕÑ×ל8q÷ïßçý­X±àêêŠÐÐP„††béÒ¥Ƙ1cººº8|ø0°{÷n@=x·´$ôôôðâÅ ¼¬Ð¢°°III²ýLÓa ÐØÙ=æ ûûË(fŒµOi|¬Ž›Œ5 Ë—/ç•÷èÑÿüóï*Ë™3g`ggWí »sçΉD8qâ¯<::ŠŠŠpuuÅùóç¹òÒÒR£G ¸EåOš8q¶¶¶hÓ¦MµÓÙØØÀÐÐ;vìà•—––r·¼ÂÃÃ!ààà€éÓ§cþüùÜ“`=‚X,F›6m0bÄìܹ%%%Užnʯt.^¼È•]¼xÅÅÅèÞ½{Cl6##¬ 7èÜù.>ìÀ = lÚèêÊ0(†a+V¬€¯ìË/¿Ä¡C‡`ff†Ï>û —/_Fhh(N:UírŒŒŒàçç‡ àäÉ“èׯnܸÂÂB\ºt ?ýô>üðC8;;ÃËË €‚‚–.]ZïmˆŠŠÂÇ kkkœ>'NDLL ÜÜܰuëVtéÒ…›££#>øà^™­­->ýôSdgg#22"‘cÆŒÁgŸ}‘H„¾}û";;ÑÑÑ())¯¯/&Nœ@777…B$&&"55Ý»wÇš5k¸öC@ùU1SSS@¿~ý`gg‡¸¸8ˆÅbÌŸ?¿Jû+SSÓ*q [·n5ïØ&TZZŠÕ«Wã›o¾©vš¼¼<øûûcÁ‚M™l¨â³† $7ÔÒòCÅ'H­¬€¨¨º=µÁTÏßߟݢid-­Ž±oß>Ööé·jÕ*üöÛoRŸfc^QQ´´´ª}(ÿ±äààÐ"œ`m€já³ÏÊ{j•ˆŽ*Üfê©%˜e…Õ1Ã0 K€jA[9’_Ɖg†©öÒQF–XTK¾¾üáÀ@ =]6±47-áR«¬±:fÞEÖÖÖèÝ»·¬Ã`Z(–Õ’»;бãÃ%%ÀÿwÁÔk§ÑøX3 Ãð±¨*7£Ø¹¨åK–™°ö)ÕqóvñâE,Z´ˆ~üø1îÞ½Ë ¯]»¬Ó2 1a„{;®lÖ¬Y¸yóf•ò’’ܺu«Nëg˜ÆÆ :ðñTUÿŽØ«^†‘µÈÈH;vŒ^¾|9¦L™Â Õ9yýú5öïßçÏŸ×zžÃ‡KM˜öïß77·:­ŸaK€ê@C;–_Æz†®?Ö>¥ñ±:nYüýýqáÂY‡_ýFFFøüóÏe ÃTÁz‚®£iÓÊo}Iœ> $%&&²‹é}ÈnÑ42VÇoçAúl¿³½IÖµuÀVÈËI?$ß¹sÛ¶m«Rîáá!5¹8sæ RRR0þ|®¬¬¬ ?ÿü3Nž<‰¢¢"Œ?“'O®Ulb±3gÎÄ!C¸·¼åæÍš5 ‹-ªÒ#5 4]»vÅùóçá[ùI†‘1–Õ‘“àâܹS>\VìÚ°þ´ß;17>VÇo'.;¿üÛ4}^lôÚXm¤¥¥n8%%k×®…³³³Ôéoß¾Çó ;w¢W¯^èׯÎ;‡)S¦@]]#+÷ñ!…œœ ±bÅ ^ôûï¿ãìÙ³UÞÉ%¡¡¡ îå¥ ó.a Ð[ðõý/Ê ÿý eÃ0Í—••æÎ  ¼mNÏž=1räHÌš5«ÖË;v,öïßX°`¼½½±iÓ¦Z%@@yíææ†ÇsW{vïÞI“&AAA¡Ž[Ä0²ÇÚ½…1cMÍÿ†““?ÿ”]<ï;Ö>¥ñ±:n>fÏž¼¼<ìÙ³§NóIÞÑ%!IfjËÕÕØýÿýDDDàÖ­[¬}óÞbW€Þ‚Š 0n°uëe¿üx{Ë.¦÷kŸÒøX¿‡ÖØ>°iÚ)ß|e×®]øý÷ßqçΨV|$õ-¨ªª¢´´´NóøúúâÛo¿Å÷ß={ö _¿~077¯W #+,zK¾¾ü((ˆ‹ر î؉¹ñ±:~;fZf˜Öùݨ»Û·ocöìÙ8zô(,--ë½¼ . ]»vušÇÇÇ .Ä‘#G€Ÿa˜÷ »ö–:t<<þ&ªiÈ0 S/ééé6l¦L™'''¤¥¥!-- YYYµ^F||<²³³QPP€Ý»wãÂ… ˜1cFâPWW‡æÌ™‘H„AƒÕ8}YYŠŠŠPRR ümä5½‰œašK€ê¡òê={Ê_‘ÁÔ kŸÒøX¿ßþøã$''cË–-044äþ¼ëpß=<<FFFÐÒÒ´iÓðå—_âÓO?­s,Ó¦MË/0eÊßðäÇ–-[ ¬¬Ì5´VVV†²²2^¾|Yçõ2LCÉ:ˆw•ßÿ?ÛîWÍ3îEEåýÿ¼xñ_Ùï¿W}súè#  ~ýúѼyój½\ÉùhÛ¶mõޱ® I$qÃ’ïKAAeggS|||øpDGGcÇŽèÚµ+233qïÞ=¬[·Ïž=ƒAƒÅ~óæM$$$ 33òòå§Ê“'OB,#>>ž›îàÁƒuZ®µµ5¶mÛ† &`ذahݺuƒÅ\W’ï‹ä%ZZZHKKƒ@ YLM©Y'@7nÜÀ¨Q£°fÍŒ3†7N,C®B£¢¢"dddÔë $S¦ß}÷_™¿?x SUZZšL-«ã·ôêUÓõl*W;*>>—/_®Rnii)õ!yyyÞñM"<<ýõŠŠŠ0|øpØÛÛ×241<777X[[såeee8xð zõê…¶mÛV™OOOýúõƒžž¶lÙR«uå·fÌÌÌ0~üx®,##ÁÁÁØ»w/w<755…³³3&MšÄ½pUâõë×øã?ðøñc|üñÇpuuåÆ=xðIII¼&Ožúè#XXX OŸ>õZï”)å‰ÄÝ»À¿ÿÖk‘Í룦ñ±:~¿¥¤¤ 00ûûã?0qâÄj÷믿þŠuëÖñÊNž<‰¾}ûâúõëØ²e ÜÜÜp÷îÝZ­_NN‡ÂÒ¥KyåçÎÃôéÓ¡®®.u¾víÚa„ è]¹·Ø7øý÷ß1qâDÞ•]]]àúõëU’¡P%%%n˜ˆàì쌭[·âÑ£GðôôÄ·ß~Ë?u꾫øKÀ?ÿüƒ/¿ü€ððp¤¥¥quþäÉDEE!118yò¤ÔØ= GGGœ;wX²d‰Ô®UÆ?þø£NõÂ4¬f› …B,Y²îîîRÇwîÜŸ|ò ¢¢¢pùòe 2—.]zãÛßÄĤüVXE¿üR¯E6{¬šÆÇêøýÖµkW^dbb{{û*'ñšX[[ãéÓ§øë¯¿€:`åÊ•µžÚ´i8yò$¯QôîÝ»1räHhkk×i{j’ššŠ'OžTI„B!,X€}ûöÁÂÂ>>>øé§Ÿ.u9[·nEHH±~ýú:%}úôÁ¨Q£`kkËÕùÈ‘#ñÑGÁÅÅ8zôh•ù²²²àëë‹-[¶àüùóضmîÝ»‡ààà*¹û÷ïøøxÞí4¦i5Û[`JJJÕ¾Å(oÛ3£‘îíûúüú+ðÓO€†F£¬Ža˜ÆÒ·/ðøqÓ¬K$ªÕd;vìÀÑ£GqçÎ:½ÔÔÎÎŽ»R£®®Ž¾}ûbïÞ½µžðàÁhÕª0þ|ddd௿þ•+Wj½ŒÚ´A‘vKmÁ‚øä“O°yófܹs§N¢E‹0vìXìܹÊÊÊ@€ž={ró9::"** /^¼€®®nƒÆ[Ñ­[·››‹ÜÜ\øWx3¶ÂÂÂзo_®ÌÄÄB¡ÉÉÉ033k´˜˜ê5ÛH–ú÷ÌÌIbŸŸ<|ñ…,£zw±ö)Õñ[RSlme'$$sçÎűcÇðÁÔkYfffÈÊʪõôòòò˜ùK—.ÅG}Tïåݸqƒ÷Äkm|þùçˆŽŽÆµk×°gÏøV~bÐÑÑAAArrrxåb)Äåääàãã###ܾ}»Öë‰DRûª/ ÃÏϯJÿq^^^¼i%Û¨¯¯ßàq0µÃ F2iRù+2$€›7eÏ»ŒµOi|¬Žßo%%%>|8\]QBË IDAT]ñõ×_¿õ2$¢££Tí“ê´iÓ ÀÌ™3ñìÙ3|úé§oË›Ö!//¨JOßݼy}ôBCC¹²ÒÒRìܹ)))uz€¥wïÞˆŽŽæzHOKK«¶Qs]¸ººÂÄÄ .äu&Š{÷îñ¦ŠŠ‚œœ,,,ê½^æí°¨‘ÞÞü² ·„†aj- ×®]ÿÿþ ;;;ØÚÚÂÖÖ¶N È®]»Ð©S'xxxÀÆÆ¶¶¶X¸pac™6mÌ+×ÓÓCTTœœœ ­­ ggghjjböìÙXµjú÷ï_ëmpvv†ÚµkGGGØØØàÕ«Wµž¿:ªªª8t肃ƒaaa~ýúÁÂÂ...HMMåM{ôèQtéÒ¥A3u# "’uï*I#êšS×äâE â%% %`Ÿw>Ö>¥ñµ´: ľ}ûšÍ­¿ääd©ï1ÔÒÒ‚ƒƒRRRðìÙ3®¯›˜˜äçç£cÇŽÊû½QSSCll,"##amm?üPj_Aeee¸zõ*œœœ Qá ±XŒ«W¯ÂÎήÊí›7nàƒ>àúSKJJÂÓ§O«,»{÷î5>q{àÀÌ›7ÑÑÑU„°°0<}ú066†‹‹ ï³™™‰GñAçååáîÝ»ððð€B…Kó—/_Fbb"úôé999$$$pmšâãã‘ GGGnúèèhó:Ñ ƒªª*¯MVaa!BBBðôéSèééÁÝÝc~~>lll°lÙ2|þùçÕÖCC+**‚––VR¦¥¥ÁÁÁ¡E¼?%@5¨oDTÞ~òÉ“ÿÊ6læÎ­l͉¿¿?»EÓÈZZ7·¨¥)++ƒƒƒ<<v ¬ ¬1tm´¤³¬°:fÞ'B¡¿þú+°{÷nY‡Ó :„-[¶à÷ßoÒ䇩Š%@lÂ~÷‘‘€”í†a˜ ìííQPP€ÉÍì]B>>>(,,DçÎeJ‹Ç F¦« ŒÁ/cWøZÂ¥VYcuÌ0 ÃÇ &P¹«ŒãÇŒ ÙÄò.bí4«c†a>–5nÝ€öíÿ~ý¨CôÍkŸÒøX¿ÿ†Žõë×7Úòïß¿33³Z÷Lœ 333©ï⊉‰ÁÎ;:D†iP,j"•Ï?;v”?%Æ0 S©©©UzGnHÅÅÅHHHÚã²4b± (..æÊ ŽY³f5j²Æ0 %@MdÜ8@Eå¿áØX 8Xvñ¼KXû”ÆÇê¸å*((h²u­\¹ƒÆMÖí=ó`/Cm"ššÀèÑÀž=ÿ•ùûýúÉ.¦wE`` »EÓÈX¿+99XTéÅ–¶.(ÖÐ1aEÛ¶mÃÚµk«”ÏŸ?³fÍBII ¾ýö[üòË/ÈÎΆµµ56mÚTå}TÕÉÎÎF—.]°aà 4迯\ÁäÉ“«}ïÖªU«°jÕ*¬^½û÷ï¯ÕºFVXÔ„¦Mã'@þYÞ3´‘‘ìbz°sãcuüv²JKq«‰ÞÖ]»OåFŒnøðáÃØ¶m÷>¬ï¾ûX·n<==ñóÏ?càÀ åz‡®‰¶¶6:vìˆíÛ·ó ;v S§NÐÖÖÆ‹/ê1ü{Ø-°&äâ89ý7\Z 4³>¾†iúúúppp€ƒƒ¶nÝŠ}ûö¡]»v(++Æ 0yòdLœ8æææX»v-,,,°aÆZ¯cÚ´i8wî’’’9998vìK¦™fƒ%@M¬ò#ñ;wee²‰åÿØ»ïøš¯ÿã¯l2$H$1“ØÄ޳¨Ú»´¨Ú´tQ£¶ZýR£Ã¦Aù¡F”{ošH+d/‘½îïËÍý$AÂýäæ&çÙÇç‘|Îçs?÷ä”xßsÞçœâBä§ÈO´qÉKÿþý™6m”{X%''Ó¹sgɽ;wææÍ›~v×®]qttäÏçSV·nÝJÕªU µëº gb¬ˆ½ÿ>|ñ¼Øx8$€Þ½µ[/mù)òmüfÜ­¬¸ Þm+#“æÿ¼Íðáé]»6 ,P•¿Øì3÷~OÉÉÉ’@_GOOñãdzbÅ æÌ™Ãúõë?~ýôS† ÂÍ›7ùâ‹/8yòdŸ¹hÑ"æÎ«‘ú š% bbÀ€¼ Б‘н»òkI&òSä'ÚøÍ(²d§fÉñ:ÀÇÇG¶Ÿ5**Š7’žž^ ûÓÒÒØ¸q#ÑÑÑ’r…BÁ÷ßϰaà õþ£F",,Œ®]»ªÊŽ;FíÚµiß¾=³gϦwïÞ8::âââ¢ZÌÏßß_#~PP®®®ÄÅÅÑ¢E ôôô8þ 6°eËnݺÅèÑ£™7o^¾uÏÊÊbÙ²etïÞ777f̘ÁÓ§O%÷ 2V‰Õn‹Cë×Ã;ïHËTÎ+‰D~ŠüD—,¶¶¶¸ººª===6mÚ¤ÚÈtëÖ­ôíÛ &NœˆMš4!**ª@Ï/_¾<üú믒ò5kÖ¤ Hr«X±"/½žŸ¨¨(.\¸À‡¹–ÆwssŸ~ú‰ÐÐÐW>#88˜>ø€úõëÓ³gOfÏž-éµ9}ú4gΜ‘¼æÊ•+>|{{{,--177ÇÕÕªW¯Ž¹¹9åË—ÇÕÕ•ºuëæûÞƒæÏ?ÿ¤wïÞLž<™‹/Ò·oß<÷½ÿþû’UÐ>1V Á®]ÊEÿû/§ÜÓªT)y»Æ‹üù‰6~3zzè›ÑçD½‚ßÚ¢E Z´h(‡Š–/_Î×_Í»ï¾ ÀÌ™3™0a‚*€=z4¶¶¶üïÿcÑ¢Ez‰'2uêT–-[†¹¹9lÚ´©À¯/¨«W¯R¦LUŽÍ eË–eÙ²eÌž=›ªU«âàà@Ó¦M8p Æ CO/§Áìììðóó£lÙ²€²WfïÞ½|ùå—ªC»ví¨S§fffLŸ>]U^­Z5š5k&)SwôèQöíÛGpp0Õ«W yóæ4hЀëׯӰaCÕ½mÚ´aîܹdee©†ùíP1en@ëÖpÿ~Nù‚Ê hÒ$íÕMJ‹Š½+Ò!µƒ¶«ñRYYY >œzõêñý÷߯ãÇéܹ³ê>===:uêÄ•+W üì!C†0cÆ ¶mÛÆÇÌþýûÉÈÈ`È!ý¢¢¢TÁCn}ôï¿ÿ>»víÂ××—+W®0bÄ–-[ÆùóçU„™™™*ø¨Q£­g~.]º„……Ež!?CCCîܹ# €ªW¯Nvv6T®\Yöº ¯' bÌÖ‚6m@}¢ÅÔ©`oýúi¯nš޶«Q¢‰6.™fÏžMpp0W¯^Eÿù: Tʵª wîÜ)ð³Ë–-ˇ~ÈÚµkùøãY¿~=£F¢Œú¢e••…±±ñK¯—)S†#F0bÄ@93¬k×®œ>}šN:åûšüzX …f*¬æÙ³gX[[ç™ñ6lØ0UÝ FFFd–ÔdN$r€Š¹Úµaÿ~05Í)ËΆáÃáÜ9íÕK“D~ŠüD—<ÿý7üñ{öì‘äÜ899addÄ©S§$÷;vì¥y,/3qâD._¾Œ··7‡’e(ÕÚÚšGå) ####OyݺuÑÓÓ{m^º *¨CMª_¿>tëÖ~ýúIŽ*UªHî DŠé€V­`ûvPÿP“š }úÀ­[Ú«—¦ˆüù‰6.YnܸÁ˜1cøá‡¨T©ááᄇ‡“€‘‘ü1žžž9r„gÏž±dÉ™ä®N£Fpæ ”+§•*½5‘Ÿ"¿ÒÖÆ^^^xzz–ú¡¿§OŸJíÚµUSä‹£uëÖ1g΂‚‚°²²’\S(DFFOõêÕ%ÉÎ……B¡È“¥ )))„„„P¹reÌÍÍ%×Μ9C—.] ÄÉÉIãï]P©©©XYY©VÑÎOxx8®®®¥bí0Ѥc¾ý6ïz@þþп?pûb§´ÿ#UD—N–––Ô¯_¿X? œ¦oggÇ_|‘çšžž¶¶¶Ô­[÷­‚P&‚Ëü€2i¼N:y‚Ÿääd&MšÄ„ ´üy‰H­\ ½{KËŽ‡Q£@ûóD~ŠüD Å™¡¡!Û·oÇÇLJýû÷k»:õÃ?ààà ZuZ(>Š÷Ç!_°mté/æ”oÛ•+Ã/¿h¯n‚ o¢^½z\»vMÛÕиüQÛU^Bôé(SSØ·êÔ‘–/Y¢ûì3jÔ¨Áüù󉌌ä¢ú´*àR•*œŽ×Fõ mäHøé'iYh¨rµèØXíÔ© D~ŠüDë¾Þ½{³ä-f8¬[·ŽíÛ·ê5~~~,Z´ˆ)S¦0kÖ,¼½½_ûšÓ§O¿võgu>ÄÁÁ!ßø€€6oÞ\˜* B•šið)))ܾ}›êÕ«pûöm4h º®¯¯³³3AAA´nÝZUî]»6–¡¡tȵ:iq5s&ø ÀÏ„‚*Ñ=@ê&OžL“&MèÞ½; \ÝT}‹uÀ‚¨¨(é §Og÷âÅÌùö[<<<òl‹QÏ—-ƒAƒT%€rçøáÃáÛoµ_?q.΋êüm%'òàÁü"9ŠÌ×+###ßÒ_eÇŽü¦þÉHMzz:™™Ò÷/S¦ —.]âüùó¬X±‚3gÎ0}út~ýõW õÞ…5kÖ,ú÷ﯯ¯¬ïSÚ½øû²ÿ~<<<èØ±#‹/.ôŸ-]U*öûúë¯Ù»w/'OžT-ƒ¾`Á|||عs§ê>wwwÞÿ}&L˜(ÿpÌïØ€íÎÎ ‘i u9¤¥Á;ïÀéÓÒòI“`Å íÔéeJÛ>UÚPÚÚXS{EG{qãF ÕêÕ:tHA_ÿå]´Íš5ÃÅÅ…¸¸8Õ„Ž1cÆðÛo¿ahhȬY³Ø–ÏúË–-£_¿~Œ7;;;¾ÿþûçï×777|}}9uêLš4‰E‹¡¯Ÿÿgã;v0tèPBBB¨Zµj¾÷:tˆ=z P(xüø1íÛ·gãÆtèÐAuÏþýû™9s&W¯^%44”ZµjqçÎjÕª%yÖ¬Y³8uê.\xuã "ö“*Ñ=@ÙÙÙ|úé§9r„Ó§OKö€±··'$$Drÿ£G¨R¥J¾ÏÚž»g¨˜31½{Am”Pn£±`vêô2"?E~¢K†ýû÷ãîîιsçøâ‹/ظq#6l`Ê”)xyy©ŽvíÚ‘™™©ÒŒŒTMxüø1›6m¢OŸ>œ?ž©S§ò믿æD½àííM5^ú{2·ªU«âääÄêÕ«%å«V­¢uëÖo½·— ¼›”••ÅÀÉÎÎæäÉ“y6¨ëÓ§Ó¦MÃ××WWWΜ9C||<]ºtÉ÷ycbHÌÊÂÜÀ (ª¯VVÊ…[·†ÇsÊçÎ…*U`ôh­UMBä§ÈO´ñ›15­‡ƒÃ·Eò^¯ÊÿyaÊ”)|þù瀲GèúõëlÞ¼™ñãÇS­Z5ªU«ÀÁƒÙ½{7'NœÀÖÖö¥Ï›9s&S§NU=ïìÙ³lÛ¶÷ß?Ͻÿüó›6mbëÖ­è©O7}‰'òá‡Gùòå ÅÛÛ;Ï„A(j%6zöì{÷=/Ô­[—ÀÀ@¬­­Y½z5:t J•*DEE±iÓ¦—~"IÉÎf_L Ãuh  jU8xÚ·õüÉqãÀÖV9M^„ü) mWã¥Z´hÁÊ•+%eÁÁÁ¼ÿþû,_¾œV­Zêy­[·æÐ¡CyÊ/\¸À°aÃøæ›o2dH¡žÙ¯_?,--ù믿˜6mžžž¸ººÒ¼yóB=G4­ÄYYY¡P(òª{FŽILL ÿþû/ôνÅz.Û##å®¶,\\ÀËK9,öBf&  W¯j¯^/”†±fmm\2ÅÆÆJ>à%%%Ñ¿̸qã ý¼¨¨(,--%eÇŽ£{÷îÌ›7o¾ù¦ÐÏ422â£>bíÚµ( 6lؠʳm*±PAáää„A†¶¼ccIÈ,øLâÄÝ6oõÜÆ¤$èÕ ‚ƒµW/ù)EA´qÉtôèQš6mª:ÿè£033ã÷ß/ô³ GŽ¡eË–ª²9B‹/æë¯¿~ãzŽ?ž›7oò¿ÿý¨¨¨|‡Ø¡¨•Ø!0M±LMåéóÅsÒ²³ÙïS/Ά°0øôÓœ²ÈHèÞΟmî‰üù‰6.Ž;F‹-044äÏ?ÿ$00ÿû¿ÿ`éÒ¥üý÷ß,Z´ˆÝ»w«^Ó¨Q#œó}Þ®]»°³³#==U«V‘’’ÂÌ™38uê£FRåR®ZµJõ:gggɬ®×qpp {÷îÌ™3‡ &`fföÊûýüü ãÞ½{ÄÇÇãíí¹¹9íÚµ+ð{ Âëˆè5DFrþùâ‰;"#u6øäeBô¢E9eÁÁÊž “'á5¿—AÐ’fÍš‘™™É÷ßOpp0 4àÔ©Sª]###éСûö퓼îã?ÆÙÙlll$×,,,øã? £I“&lܸQµ\‚‘‘îîîDGGç™Ö»wï—@*TÀÝÝ=OùgŸ}Frr2“'O–”—-[wwwIþåÞ½{9þ|7g[[[.\H5D$hT©XèMyxxjaÁÚfÍTeÆúúD´iƒ•¡îÆŽ |ðlÙ"-ïÙþùŠúG+mkÔhCikcM­T’9991mÚ4f̘¡íªED¬$Uês€^§ò³g8©}2IÏÎÆ+:Z‹5z{zzðçŸÐµ«´üàAåì°¢&þ‘’ŸhcA)À\ÝÆ;tt6˜:##ؽ\]¥åžžÊu‚Š’ÈO‘Ÿhc!·1cÆH¨¡´PäÞãh\±%`¯ e¯Oî›,P®-BÉ5oÞ¼|su¡´P417§¶Ú0X†BÁ{ÁÎN¹ZtÅŠÒò©S•k…Ò0Ö¬m¢A¤DT@¹{vèØÞ`¯R·.ìÛê‹`ggÃ!ðí·ž.ïû‹üù‰6AP Í‹#º ƒ½Ðº5lÛêëAfdÀwßAóæò®-òSä'ÚXAJ@ÔÐÌŒz¦¦ªóL…‚Ý%¨ OX±"oùõëàæ³fAZZÑ×KA4M@…»¨$ ƒ½0~¼2÷ÇÞ^Zž•?ÿ¬œ5vá‚fßSä§ÈO´±îÛ·o¾¾¾Zy﨨(RRR tohh(žžž~ö³gÏðôôäÙ³goX;©ÔÔT6n܈ŸŸ_žkYYYDDD™Ï–F×®]ãÀ©CFF^^^¬X±‚ÐÐPU½vìØÁªU«ˆ‰‰aëÖ­¥K—âèè¨*»{÷.ýúõÃÔÔ;;;LLL¨U«Ÿ}ö™êž;vðÃ?ê½òsÿþ}ªT©Âìٳٻw/·o߯××—Ê•+óã?²sçNBBB˜4i—/_.ðsMMMéÙ³'/^|ë: Jº»œ±8›™ÑÀÌŒ›IId)슎fRåÊZ®™æ•/¯\hèPe¯ÐãÇ9ײ³aéReâôúõPˆ-ò%òSä'ÚøÍüûᅩ1¢HÞ+""ÙžÿÃ?`ª6Œÿ:é ïJ IDATé餥¥±{÷n\]]¹~ý:3fÌ _¿~Ü»w===ÙêºqãFvïÞÍÁƒi¦¶ÿëœ;wŽõë×ãããC¹r刧sçÎ899±{÷n7ñ8sæ Ë—/gÉ’%­ûîÝ»iÔ¨GU•}ýõ×¼óÎ;y¶)Œ÷Þ{I“&1vìXnܸ!kû—"z oooš6mJŸ>}å0Ø7÷ï«®oŒ,‘Ð ={*{ƒ¾øÖ®•^»{:v„É“aáB07×JA6<}ú´HÞ« »]½z•ƒ¢P(:t(uëÖàüùóܾ};Ïý:tÀÉÉ  Ôf9ìÚµ‹úõëÏáÇ166føð᪞sssÉP—.]øòË/ùðÃyüø1ÕªU{m]£¢¢ø÷ßéÛ·/åË—W•GDDpðàA†šïëèß¿?™™™ÄÆÆ¨]æÏŸÏ˜1cpqqQ•9s†>L½zõ¨Zµ*íÚµcÚ´iyžömÛˆ‰‰aäÈ‘899©®=z”råÊѲeKUÙ¹sçÈÌÌÄÝÝ .°e˪T©‚§§'VVVXZZ²k×.š7oާ§'vvvôèÑ#ßú_¸pcÇŽ‘œœL‡òÜ÷Ýwß±~ýz¼¼¼èß¿ÛEÈŸ{K—.±rårÕyîa°3OŸ^‡ÁÔ•+kÖÀÑ£yMT(à? aCåõ7!òSä'Ú¸dØ´i½{÷æòåË,^¼˜V­Z€^^^ªã÷ßg̘1°|ùr6mÚ¤zÖ—_~I¯^½ÑÑÑ;vŒ>úHR^¿~}€|ógÌs}j ÆÕÕ///NŸ>MíÚµÙ¿¿êúÒ¥Kù믿$¯Ù°aüñ¾¾¾¯C‡Ü¿Ÿ}ûöñðáCLLLøõ×_ó½711‘M›61tèÐ<ÃË3fÌÖ­[§*ËÎÎÆÓÓSãògΜÁÒÒ777Iy­Zµ:t(“&M¢aÆŒ7ŽU«V©’“Õ™˜˜pâÄ Ž9ÂÑ£G9r$;wî,p&Mš„««+}úôÁËË‹ß~û™3gR³fMFމ—— ,Èóº‹/òÓO?qåʶnÝÊæÍ›Ù²e .ÌÓÖ½{wNœ8Qà: /' ÈÌÌfÏž]ªóܽ@ÛKÀÞ`ef¿ý§NAíÚy¯¯[ @a&Sˆüù‰6~3½zõ"..®HŽ2eʼ¶> 6TõšT¨PŽ;æ›H;zôh¬¬¬X¾|yžkê\]]166ÀÚÚšvíÚqåÊ•<÷edd0hÐ RRR^ûÌÜÆOPPçÎàСCÄÇÇk<·*<<‡Ü]ÔÏmÛ¶ .мys®]»Æ§Ÿ~J­Zµøî»ï$÷U­Z•Zµj©Î›4i¢ª·œ>ðõ×`˜«O1<ú÷‡áÃáeûÇŠüù‰6.Ô‡=n޼ɉ'4h \føðá,]º”6mÚèy‰‰‰€rš··7—.]bäÈ‘€2hnÛ¶-œ;wŽÊo1ÛµOŸ>XYY1pà@\]]iÒ¤É?ëeœœœxøð!©©©’òß~û‰'òXm-¤¤$.\ˆ©©)­[·.ð{tîÜ™‹/ªþ?øùùqA+ÃöíÛ—àà`V¯^-)ß»w/ѹ~qQ«V-1 ^DT6ç$æÞõbB!¥xŸøñG¸t 5Ê{}Û6eoP~“D~ŠüD— ‹-¢Y³f´jÕ :tèÀäÉ“˜9s&qqq,[¶Œzõ꩎U«V½ôyß}÷uëÖ¥V­ZôìÙ“ñãÇ«ª+W®àïõêÕÃÎÎNu|ñÅ…ª·¡¡!cÇŽÅÏϯ@çÍ›‡›››7oæÆ¸¹¹1lذW¾¦M›6²wï^Iy5عs'ÕªU£råʸººbeeÅ®]»Øºuk¡zÊúöíKDDŽŽŽ8;;óî»ïz˜1? 4`ÅŠ|ùå—¸¸¸ÐµkWlmmyÿý÷IÏ5ËxçÎtïÞý­ßS=EAŸÐ²cÇŽ‘ššJ‡4>vü2ÌŸ?_un`ááX[+ƒŸ–×®qEmùöÅ5kòyÖÆ(é22”ÁЂÊïsëß_¹ç˜]Ñ×M(¼¼¼ðôô,Q=_×®]£bÅŠLƒ èÔ©“ª'àúõëÄÄÄäy““Õ«WçÆ˜˜˜Pûùì'''&MšD“&M £I“&’õsbbbòä½P¹reÉP™º¸¸8üüüèØ±£¤<>>___ÜÜÜ$ ß©©©\¼x‘V­Z©¼ˆÌÕ«nffF‹-^ÙFS¦LÁÏϳgÏJÊ333¹rå >$!!GGGZ·n-™ÍvïÞ=ž>}*é %$$„V­Z©ÊÒÓÓùçŸÐ××§k×®„……‘••…³³3þþþ˜™™Q³fMÕk®]»† Õ«WW•={–ºuëb£6šÃ¥K—Të,µoß^RÇ€€5jÄÿýG£ü>i¾Fjj*VVVyzÉÔ…‡‡ãêêZ:†Í:bÏž=жmÛ*ÌÍÍnnnŠÙ³g+Ž=ªHII‘í=¿ýö[E•ª( çøá£T×?z¤àÄ ÕÑòÚ5ÙꢋüüŠfÍ åjAÒ£B…bÓ&å}aaaÚ­h)PÚÚxÏž=о}ûj»Å𣣣bÉ’%Ú®†F=~üXann®øóÏ?µ]ËÎÎV¸»»+ ôÆÏHIIQ˜˜˜¼òž°°0…­­í¿‡.Ñ™!°~ýúqöìYbbbX²d ¦¦¦ 6LöD°Z-¤Ý›ïø[õý`ÔGa/'$ðà‘uiÓ¨\¼¨ì Ê=A.6V9Kì½÷ÀÓ³ä|J/®JROˆ ¼L•*UX¹r%S¦L)Ô>[º`úôéÜ»wï•CšBáèÌ@JJ d×®]œ8q‚:0dÈBmðW$íãïÅÿ©Ê àÁ#ªVv ÍÿqAm忟œøJ­›SPºu >úHåfi ½{C½zÊÙdõëC­Z ¡u¡”*‰C`‚ð¶Ä˜”Îô­]»<==éß¿?wïÞeÛ¶m²?/Ô6ë}Õœó¬,øeeN_îdè¥p:|AÔ¯¯œ.ÿË/Êéóêž>…¿þ‚¹saà@e²´©©2 ê×O9»lÓ&¸|¹ð«L ‚ B~t&rssãÃ?$00Ý»wãíí-Ëz ¹aDÃÒÄæSÞWP(”kKä»öìÁEP/]¤¯Ÿ}þþ¹wÏûI#3‚‚`ï^åF«£FA«VÊÞ¢*U kW˜6M¹ÙñãϪö‚šÒðiN¡0t&jذ!+V¬ 00I“&qêÔ)êׯÏ!CdïÇJ—K÷÷ÉæêÍ5T11¡­¥¥äú¶R¼&PAÔª'O*·Ô03(Ü0Eh(;¿ÿ®Ü—¬KePdi©\“hôhøùgeðtû¶2˜*íÄP ‚”Nm…qçÎΞ=Ë™3g8{ö,ÉÉÉE²ÔàΣ©Tåc"Ÿ(—DÏÊ‚ ¿£Å"åPØJ•8ûô©êþQQÌ©QCözé2==eð2t(øúN$0Õqë„…þ™ Êuˆ.]’–+ƒ®úõÁÉ ªWWîjÿâ(ྎ:M¬$‚ ¥3мyóXºt)íÛ·§k×®|òÉ'4nܸÈVÃlÓ£%^ësVü¼r"ŒÔ´PʘTf ÓïÞ%ûy>¹b"ÉÉÔ35-’ºé2å.ò¹7¬NH OPÁÁù¯-ô*éé <òcm­ „¥‘£#Ô¨¡ÌGAJ €¦L™Â7ß|£‘U7ßĬÉKñZï¦:÷õ…ÏÎÀNÿ‡½±1í--9¯º¾#2’oÞpßœÒ&<<»\«"–+-[*u™™Ê (w`¨L¦~ÑÑÊãêÕü¯Wª”7@zñ}P€M¼µ.¿6A(Ít&²³³#66–5kÖpçά¬¬èÙ³']»v-’÷oÕ´•ª•%2D™àœ•'öïa`' ÇÐJ•$Ðö¨(——W‡h ¡n]åÑ·¯ôZxxÞ (0BB”Ë/¾©ÈHå‘ß²"zz`k›pô"@26~ó÷֔´± Bi 3ÐãÇiÑ¢U«VÅÝÝû÷ï3xð`¾øâ æÌ™S$uè=`ë—oQ_:FpèßÔ¬<„ÖÖL»s‡¬çÿÒ$%q3)‰Ê,_á4õ³òȵ?IIÊeAApï/èµÂ>ãÅ¡§'ß×ß¿îÈ]§·¹?÷ ù­òº{Þä5…}î˾›ûÔÛ\ýüUåoRVEFFÒ­[+""îk»*²Ó™ cccöï߯¯¯jXË–-±Ìµ ³¦¹ls!¦e ßU~ªkРµªñàn "ñ?ŸÈý6{q´ëËI´#2R@¥˜¡aNÂóË$&æÌ4{Ý­ù%¹eg+·É¥¡¨T¢\9ùwX(t&úù矉‹‹cáÂ…EšØhdMè7רøî;ª²Q#>bþüùªó“'Á­Ï|íúÒßÚšI·o“ñ¼cínJ ÿ={FS ‹"«³®)mù)¹™›+'§×ß«P@LLÁ¦œµ‘ÂÒÛÆE#°D‡~­ê t 9@ÂÛÓ™¿©*T ((H+ïûŸiOÒ0©bÀàÁƒ%ãKFf,Œ*е|yª÷EE‰èŠsPq£§§Ìé±¶gç×ߟ–QQ°j•½{O$- RSÉ÷ë«®æžôte/UV–²ç§ô8tD|¢«€ÈÞžÎä%&&âîîÎáÇ‹,ÉÐÃÃŽó;à8Ã’Kš¨®Õs®GЭœ€ìóϡׇSéÔè76†‡3:0PuͱLî¹å¬"-¥É‹@H=(Êï{MÜ—ßµ‡BQ4__uä®ÓÛÜ›û7w~¿É_wÏ›¼¦°Ï}Ù÷osß‹ãE;ä>ò+Ó²Ò&;;›´´é<}ú«¶«";éúå—_¸qãÕ«WϳF¼Ú Ìr ÛN_€ç‰qÆ “ô8mzn~£Ÿµ5Æúú¤?ÿø{?5•+ÏžÑBô ¥Ð‹„d±~‘ áᑸºîJ~¤3“T'OžŒŸŸ×®]ãâÅ‹’£(¤>-KÜ¡hÕù!Ò$1__Ðö”G‘ÞXÒ½|yÉõí‘‘ERO]$V땟hcùÅÄÄù¶‹B ¯”žžN¬Zz ¼b¥¦¦ªþ°ÛØØP¯^½|¢úýªïqVKÂÈΆ3gàJз ©TIòÚ¿£¢(…½©âåå¥í*”x¢åwâÄ ž¾é޼BDGGsöìYmWC(!ŠuO½zõ6lGŽ![Ë•1 ɈΙ˛»èäI°È¸Jff}*VÄDm¸G©©\JH(ªªê‘-?ÑÆò4hXQf•+W‹ S¬ ;;;îß¿OÏž=ùþûïqrrâÛo¿åAA÷ Ѐ2„©¾ÏÎÖ'|yNróàÁƒ%÷úúBrB6ççSÎО¹–kÃ`‚ ‚P<ëÀÌÌŒQ£FqúôiŽ9BFFíÛ·§k×®lݺ•ÔÔÔ='<<œ»wïæ)÷ññÉ“S“ëcavJrتÕ÷ÎÎÎ4hÐ@uþb,:ÂÃ`%òSä'ÚX~"H~"HФb©«]»6?þø#<`ÆŒìÚµ‹5j0eÊ|}}ó}Mff&ÁÁÁŒ=šßÿ=ÏõÞ½{óÁ0qâDÕqùòeÕõG­bÑ#gè-9Ú”§gr6<ÍÝ tâT4ˆåIô zW¬HYµa°'iiœ9yˆüù‰6–ŸÈ’ŸÈ4I§  èÕ«»víâÆÔ¬Y“ß~û-ß{=ÊØ±c¹qãÆKŸ÷çŸâëë«:Þ}÷]Õ5ÿNTп,¹?lþ5Õ÷¹ó€üü”U^ œ‹¹ïæÊ Ø!†Áòù)òm,?‘$?‘$h’Î@AAA,_¾€ääd¾þúk>ÿüs²³³ùì³ÏX¿~}¾¯ëÑ£'OždРAoô¾)eÊ`ßᙤ,ê$d>Uvuׯ_?Ï0ØéÓ`–~‘¬¬$†æÛEvi\]KAŠ €þüóOUnÎÊ•+9t膆†|ðÁoýì~ýúaoo»»;ÇŽ“\»xñ"×ûÙr“Óø>ÿ/+ËÈ•w8yòd¾³ÁÊègs>på®_ÇÌÀ@u-ìòeΨu“Ÿ%ñ<&&&ÏïâT¿’p~ôèQI¶ëSRÎoß¾ÍÉ“'Y¶lçÏŸ×úŒë¢¢3ÐÇi×®;wîdΜ9üüóÏø@¹'XÊì߸ô“ ª½0€fW›bѬœêuêyF3f@Ÿ>P¯Ñ9ÎgÕfàÍ›ªk•Œ mݽœg ‚ ‚¶…‡‡ãêêZ*fŽêLÐÇ̼yó¨W¯ƒ ¢Zµj\½z•ôôtìììÞ虹»ùRSS‰ŒŒÄÖÖ6Ͻe?Ny}?IYØü«ªïó80›w+VÄ\m,2=SE°™ ‚ ùÓ™¨aÆpàÀ<==ål°={ö ÷Šž”»wïâééI@@xzzrÿþ}@Ùeݹsg~ùå6lØ@Ïž=qrr¢K—.ydm}«(IQ¤w&YÉYÀˇÁʦŘtúX[K®o’>«4+ Ÿ4´M´±üÄ:@òë š¤3@FFçÏŸgÑ¢E¤¥¥÷Ê×$&&òàÁÚ´iC›6mxðàjœ¾yóæ 8   Nžþ#Ï0ØÞèhÒKɲ゠‚P\èLôçŸräÈêׯÏÀéÒ¥  <˜¬¬¬¢­Œ¾>ö#,%E±f¤>Hòö½«dð„&©`d¤ºŸ™ÉáR> &òSä'ÚX~"H~"HÐ$ €‚‚‚xò䉤LOOË—/¿v- 9XÌ‚¹^°zmû^¹2tî< ??ˆUn¢qîæLˆa0 ‘Ÿ"?ÑÆò9@ò9@‚&¾þíºzõ*GåâÅ‹K>ÉÞ½{— *`+ (U«bßà>wnÔT…oKÀa‚:uêШQ#üýýå0Ø™3з/%cH½ ¬ S½îŸ˜Ò²³1Ñ×™xT£D~ŠüDËoРAÚ®B‰'r€M*öÿâFEEáëëKdd$=Â××___üüü¨S§»wïÖZÝl¿n‰>iªó´dSbw?òö8¡üja˜A¹˜Íب ƒ%dfrPtë ‚ B‘Ñ™• ¯]»†¥¥%µjÕ*²÷̳tnYYܲø‘ˆ”öª"ëÆ‰¸ø¾ÇíÛ·©[·®ª\Ovî„  ,«§ªeuhÎtúá•*±ÕÙYŽ£Ø ã m…‚m,¿˜˜,--14,öë:+==ÄÄÄ¢]ú¤”+ACÁÁÁܼy3ÏîZe`@åÁ¦’¢SÒÃÓ©S§7V•«Ï³3¡Ÿù3ÉëöÅÄRœ~¶"$òSä'ÚX~"H~"HÐ$ €¬¬¬˜={65kÖdñâÅZI|Î¥ÇL Q+ú„/øxù¢ˆz@ÊÃï°56V]KÌÊâ@LŒÜÕ-–D~ŠüDËO¬$?‘$h’Î@ï¼ó7oÞdýúõ\¸p5j0~üx®_¿®ÝŠ9:b_玤(lS4(òN‡÷÷WÎÐOòfµ´·´ÏA„¢¢3Ð ;wf×®]¬[·Ž-[¶Ð¨Q#:vìÈîÝ»‹~= çìfº¢GÎú) æÄŠ víÚ¸ººªÊÕ‡Á, ÓiŸé-yο±±$iégЯPòå IDAT¦Ò0Ö¬m¢å'Ö’ŸXHÐ$ €’’’X³f ®®®Lž<™iÓ¦Ȉ#˜;w.ëׯ×J½Œ>èµÉUIYèó Rs÷½˜ þ+öjÃ`ÉYYü[ ‡ÁD~ŠüDËOäÉOä š¤3³À¶lÙÂäÉ“©]»6S§Neذa”)SFu]¡PðôéS¬¬¬4öž¯¦&vðÿðßÙRu®¯ŸEë¨<ˆy@:uTåzzð÷ßP±"dÿÚžbIDNò³»•G7ÆPOOS?† ‚ ˆ˜V •/_žC‡qõêUF- ~@¹*´&ƒŸÂ*ÿC?Ê¡:ÏÎ6 ⾯Ӝãÿ<çT|<¢Q© ‚ è( €Þ}÷]ÜÜ܈‹‹ãÇdÀ€¬ZµJÛÕRÑ«[;‡IYØ:åjÏ/› P!éšY˜K®oŽˆ`úÝ»²Ô³8* Ÿ4´M´±üDüD IÅ>züø1Í›7ÇÞÞžU«V1sæLŽ?Nƒ X¼x1K–,ÑvUì?wFœá¬¤˜r$œŽÎ“tý:¼Hõ)o˜Ê/–ÿQ³lYÉ=¿>~Ìüä®r± òSä'ÚX~"H~"HФbŸôùçŸsçξüòK.\ˆ=ÂÐÐk×®1lØ0îܹóú½Âä–Æu‹?ˆÉhª*²ï˜LÝïÒ´iS|||TåŸ|ýû+¿ͪMë¶þ´óñ!4-MòÈ_k×fZ•*oóc‚ Bˆ bäÞ½{Œ;–öíÛóÉ'Ÿàèè¨ZjÞÅÅ…×<¡™˜`ß=CRyƬgYyzÔ‡Á*éßÁ:;ŠCQAm0€OïÜᯈAAМb¥¦¦bii €¥¥%Fj‚‰‰ i¹zL´­â‚÷0&gŒ:+˘Èå×ó䩃êÁñ_ábfÆ¿ bf` ºOŒ d ž_>ih›hcù‰ ù‰ A“Š}¤P(8~ü8Û¶mãèÑ£DFF²mÛ6ÕQÜè5j€]éêÔa+R³fMš4i¢*S(àÔ©œ{2ŸîÀ­\9ö¸¸`¬Ÿó¿&S¡`ðÍ›œŽ—·òZ"òSä'ÚX~"H~"HФbŸ4eÊÎ;÷Ê{|}}eyïBç=—ò¿-\šYå®_Jͯ¹òûáU|ýõת²† á×_s^WÑi «ÀΨ(†­ö¿§œ¡!']]ib.5&‚ šPšr€Š}¤Mo‘œŒ¯åzâ3ªŠªôLAÿ·:ÔªUKU¦¾("À“ìzŒè|Ku}]Xã‚‚$¶12âl“&Ô1•îB/‚ o«4@Å~L'™šbß)YRqTÇ*Ž4mš3C,÷0˜­^I©9è>¶·çg''És¢22èæïÏãb–ûô6JÃ_4mm,?‘$?‘$h’€dbóÃ;ñLuž™Q†¨Uy’¡ÏùÞPOÁñë3%׿ª^¯ªW—”=JM¥›ŸÑÒgºJä§ÈO´±üDüD IbìÞxì¹»¶?ð8²êܪFåŽ7¦fÍšª²ÜÃ`©Ù4mrœJå;Hž5.(ˆuaa’²æwuÅBmÖ˜ ‚ ¼)1&h„ý¤j’óø‡å±O¯D³fÍTe œ8™sOý,||ºò4áŠäµ«ëÔa¤ìê³gô¹~ÔìlAA(8ÉÈìó”3¸-) ›{>Ï¢ˆÿ—nƒa¢ŸÁ¥ÿÚ“˜˜3»M_O-õëÓ­|yɽ'ãã@–w䕆OÚ&ÚX~"H~"HÐ$ÉÉÂû6Ò¿¬áû34`¤ìá­TöK{wŒIãâµ¶$&úç”éë³ÇÅ…VåÊIîÝÍØ  ÝA^ä§ÈO´±üDüD I"’Y¥ï;c@Šê<=ÍËS™¹†ÁTˆý”ÃQÒÀÆP‘ÌåÿÚ‘”tCUff`À† i`f&¹wcx8Ÿéèò'NÔvJ<ÑÆò4h_$ó ²¨\¹2}úôÑv5„B@23poI¥ >’²ÐÿÝÌ3ìàÞƒŒê|™e$åúÙϸú_’’TeŒŒ8ܨŽe¤÷.{ü˜ï>ÔðO ‚ %€Š€ýX;ÉyÜòômÓ[RvþüyÌSÍéÝú0û䳺Yq\óé@rr ª¬²‰ G7ÆÎØXrï7÷ïóÇ“'þ ä%òSä'ÚX~"H~"HÐ$r³`®ÿ@u®@ã•a4oÞ<§L¡`Ïž=´¯Þžþb¿tÆ;Ù™1üçÓ‘ä䜤êšeËr¨Q#¬ %÷~r÷.;"#eùYä òSä'ÚX~"H~"HÐ$++ì›GHŠÂ÷¤2h 4zÇŽ m0ŒJÕr0ׇöÌŒ|};‘’’“ëÓÈÜœý bª¶P¶BÁÈ[·8¤#Ÿ”D~ŠüDËOäÉOä š$ "bëÑ}ÒUçi)¦t3q“ÜsîÜ9ž/vøUÛ™<³˜È!iÜDzzèó 螪¬­¥%»4ÀH/góÕ …‚7or!!A†ŸFAt›€ŠˆaÏvØXúIÊŒÖÅI†Á²³³Ù¹s§êü·wçFv/Žä ‚ÒÒãç׉ÔÔª²*°©~}ôÕ‚ ä¬,zùûs=)I³?Œ†‰üù‰6–ŸÈ’ŸÈ4I@EÈ~¤•ä<6 z ”ýúë¯ÄÇÇ` gÀ¶AÛ9ü´)Çr¥ô¤¦>Â×·©©TeÃ*UâÚµ%÷ÅefÒÝÏ{))W"?E~¢å'r€ä'r€MP²š?€²z93´èÓáŽt·÷»wï2|øp²ŸooafdÆþØæÀÉ(éóRSàç׉´´UÙÄÊ•ùÁÑQr_Xz:Ýüý KO§8ù)òm,?‘$?‘$h’€ŠRÅŠØ7z,)2ø×”Ñ£GKʼ½½™93gWx[3[ö8À÷Ës&ZúÈ””{øúv&--'°šS£ŸU“îCv/%…î~~ĉ.zAAPQ³û¦%zä!©‰üôîZ¶l)¹oñâÅüõ×_ªóúÖõÙ9t ƒŒ9#}fJÊ]üü:“žž3w~qÍšŒ¶“®?t=)‰^þþ$geið'z{"?E~¢å'r€ä'r€MP3îß‘Šfþ’²Ø…ìÙ³{{{Iù¸qã¸r%gWx÷î¬ëëÉü›z\È%'߯׷3ééÊèô€uuëÒÏÚZrß…„ܼIz1ÚA^ä§ÈO´±üDüD I"*jzzT"ÝÇ+ÚÇ cöìÙƒ‰‰‰ª<55•þýû«¦Æ wÎwä››p)סää@üüºž®Ì˜6ÐÓc›³3sí (6–É.&;È‹üù‰6–ŸÈ’ŸÈ4I@ZP~A_Lôr2š³„ÿp™V­Z±zõjɽOž[¶lA_?çS\\}úô!Am“Sggö ÝCÆÌº7síúìÙüý»“™©¼ÐÄÜœ\\(«öÜ,…‚÷oÝâX\®„¢"P>ih›hcù‰ ù‰ A“J|äêꊇ‡nnny®]»væÍ›cdd€¹¹9...’µwä¤W£ ¶5¥ÁÖ£_£ÈˆÍP¿÷Þ{üðÃ’{nݺň#TÛettèȆ¾HÉÒã+¸õLú^ —ð÷ïAV–òB++v4h€¡Úæ©iÙÙô»qƒËE¼ƒ¼ÈO‘Ÿhcù‰ ù‰ A“J|ô*ááá’!&þŸ½3¢HÿðÓsgr_„„CADoXQQ§š_2`»"¿À´‚}—}Gm|ôÌ™3ùûßÿÎM7ݤ¨·víZžzê)Åùn9ãFgÆì‚¿ì„ýUðî»uÇTVþȃÆå2pU|<׬]‹TFŋ¹r÷nöÖ¬ ßÖ¯×_¾þ5íq½PÌ×¶qGñ§+æ'L˜À믿ÞaüéŠùE‹)æ ¶?]%¿víZfΜɨQ£˜5k‡ƒP d‚ ~øa^{í5¯mæÌ™ìÝ»—åË—{m—\r ãÇgêÔ©ÞGÃM›âr±?öEò«.T˜3¦E‘ñÚpoÞb±pÁð믿*êýç?ÿaÒ¤I Û½ŸßË[¿¾E¤fŸ §E(/sgœñjµç±øy¹¹Lm0ì×C¯g˰a>O  ë"‚ C„ÄÄDJJ”kJÒ½{÷ösB­æ´¯® B:¬0gÍ­ ôóº`XX«W¯¦[·nŠzwÝuÛ·+{‘æ›Ïؾc©rÂc;ápµò’å囨½û*Ün õèÁÌŒ E\› ¶oç“vŒ…´`#Ú8ðˆ À#b€mIH  1cÆðóÏ?{EPNN‡ââ‹/nW?Tç`Ðó*4˜¼6‰}wbͶzm={ödÕªUÞÇôÁÓ3tíµ×RXXèµiTVL\Á°¤a^tÄGe²{÷5¸ÝžóÏÈÈàÏ©©Š:96öìaÌÎì3›Ûò%+ñ)G´qà1@GĵŽ*—‹l«•&ß–—óiq1Kòó™}ü8O=ʃróÞ½\±k#ý•þ?ÿÌàƒ±\rI°]oºüØæÍ›yûí·ùùçŸ8çœsxàøÃþÀƒ>ÈÆ=z4ÿýï¹ùæ›yöÙgÏ䇒Ëf°û¿£ ^TNdº…aû/G¥¯Óªo¿ý6“'OV{Á°qãF…8Ê«ÊcäÛ#9^yœ-¼:2”“Awƒ¯A¥Ò#wìÛÇû'Nøø¦‘$þœšÊŒôt¢4Ÿr@ ´nY¦ÚíÆäryS™ÃA™ÓéMåN§­~Þu’?ïQ‹SñþûmüŠ:]^ðûï¿+l§Ÿ~ºb(iË–-ìß¿Ÿ!C†0bį½½V+G{=OVÁ…9å:5ýV)c„¦Nʼyó¶É“'³hÑ"…mOÑÎ_|>¶ bkDPºº‚Áƒ?E¥Òã”e¦=Ê«ÇãðóÑHÒéx©wonKJRO A¨bo TK MSÉìríõ$hÈG޲kÀÊg*ì^O!é¡~Þ¼Óéä²Ë.#33SQoÞ¼y<øàƒ Û7G¿aìcq¸ÄéൡÐ3LyÝøø«4èT*OÒïf3Ób}#ãíçFE1ï´ÓÞ`‘Õ“¡  €¤¤¤S> qDž’’¢££ÑˆÒ€a·Û1™L~çu;²Ì »|»|›|»»r§Ó#\š+þn;3Qo¿MÅÛ€#PC8–ͶIبë¥R© ÿåD ­{l¿¤¤„³Ï>›£GëVÕh4¬_¿žÑ£G+Îùþ®÷¹ýÓÛˆ¯A© DPBÂ5 ´IÒzmŸóè¡C³ZiˆJ’˜œœÌ ½z¯Õú”·” (…´=¢ÏÊ•+=z4ñññÁv¥Ë’——ǶmÛÂ7EµËå55¦ v¿Ø)q8èŠ?„‘j5qZ-±'iµÄÕÛÕh<ùzuœee\<|xH<8!PKTÞ÷*;›º»É°˜jÎ:v9šè:ÛîÝ»9ï¼ó0™ê¨ãããÙºu+½zõRœó¹MÏñ̨‡¹C!¹ÁSî ×1hÐr$©î·›—²³y);k½Ék‰Ójy.#ƒûRRPKb`L –R‡Ã¯©í¹©µUq©-0ªÕDÔK15"¥¡hQˆœš|ŒF£˜è¶¥„ÒcðB5A0²LÞ§9ð›2(ál;ƒº¬~œ4Ÿ~ú)7ÜpõßÊ3Î8ƒ~øˆå$@÷¬¹‡ÅÛÐMïé j(‚'púé+DÀ1«•Gbuq±_—‡FDðúi§qAt´ßr@ÐqqʲÏpOáŸú1,õˬn7.YÆ'x·v¿¥6·,ãj¡­ÚíÆîçF,˜¨%I!TšJá-­§R¡  ¥@ È ¬Œ}iorÂtžÂÜû±HÒf¥°=ûì³Ì˜1Ca»îºëøä“Oêý9ÝNÆ}4Žõ‡×Ð]s‡y¶õéÖm~ˆ$©}Üúº´”i‡±¿‘GãoíÞ—ûô!¹ÞiM!âShãÀÓž1@Õ.N§OPm“â¥!cë`¢Â/N'X,б‡õ‘€DŽ$Žäz)N«m‘X1¨ºÎŒ2B € €×ÿ¶óëù;©vgxmnÎüb 1W&{m²,3qâD>ùäÅñ3fÌðñ¿Ê^Å…K.d牀§赡ž¡ú$&N¤ÿ…h4±>~9d™W繬,L~º™#ÕjžÎÈàáÔT´ÍÜňø”À#Ú8ð*è¸ÍÆöª*v˜Lì0™Øn2ùÉ JJ`ÿ~8ï¼æë:•Š$?Â&Y¯Wغët'5\ÔHt `yùC~ùk N½6ÞÂYGþˆ>¥NµTWWsÞyç±k×.¯M’$V¬XÁ 7Ü 8gnU.#ßINe)OOPBƒN­6Þ½ÿIròŸÀσïy69r„üÌÐßhäß}ûrY?µ!t5œ²Ìïf³Bèì0™( ‘u™ZC¤ZM²^OrCqÓ@ØœÊáŠ@ ã €â±ÏóÛ×çS_„Dg˜z`’¶ÎvìØ1Î>ûlŠëÅ鄇‡óÃ?0dÈÅ9wîæ‚%Pió¬þžs‡JÄé|?‘‘gsÚióˆŠ:ǯßWT0õàAvÖ Æ®Ïø„^íÛ— ±¶˜@€ÉåbW¡ó[uµß‡ Ú µ$)âSÂUªFcWƱ„ÕÄ«¨ñ<ªª9_klÞýflzI¨öš´ B €Ž%€°Û9’ñ"Ùù£æÔ }W(—îøöÛo¹ôÒKëedd°uëVu7ÙÀ•^‰Ãí¹Ëìs†ªIÐù{zB"9ùnz÷þ'Zm¢O©K–Y—ÇÓGRægM$ƒJÅ_ÓÒøkZaõÆÌE|Jàmx‹*°ÛBg‡ÉÄ!‹÷)|õj%‰x­ÖGˆ´TÀøËw†8–@Í$¨C ÐÁ Ëfg¿5”;+ì§¿™L·ûû+lóçÏ÷™qÔ¨Qü÷¿ÿõù‚^ºs)w®¾Ó›×«`jÿx®J2!»m>~h41ôêõ))ø ’.v8xòÈøý’Ï0˜Ó·/×Õˆ1ŸxDž+V~î¹ÕéÃX'ìöS:o´FÃЈoÁéááÍÆÖuEZ; õ$:ž°²‘_&”a£®'G­¶sÖö‘ψRÔ½ï¾û|–Ƙ2e o¼ñ†ÏyŸùîf~;Sa›~&Ï IÄ\¹Á¯/grÚi¯}¡ßòmUUç½û³»ygÇ; [”>м‹ÞòX,‡üúÓ½û-ôéó :]²O™ ¼[PÀߎ¡ÐϰV’˜–šÊ-Ý»“¦×'‚»ÛÍ!‹E!pjO[%«%‰þF£Bì ˆ A|þAF Ðq²Lî™Ïpp÷(…¹Û,œþ¿+¶¢¢"FŒAvv¶×¦ÕjÙ¸q#^¨ì¹q¸Œûpÿ=ò_ŸK^ÓïJ^<ûLJóÿËUíS®VG’‘ñRS§)–Ò¨¥ÂédƱc¼‘›‹³áG®´jÆôÃÕjÒ ÒôzÒj¶õó©z½x\õ$1@þÉ·Û}Î~³™cVkëWÒ®¬„ðph kT«®;gDD(âà-CÄ!€@@••ìM]@a•ò©¬¾OI}IiÛ±cçŸ>æz&&&²mÛ6ÒÒÒ”§µUrý²ëÙxt£Ï%ãÂâX0ö9i¾£°p¹_·ŒÆœvÚ¿‰ã·ü·êjþ|ð ™ååuÆ5k …cú*I"E§ó+Žj÷£Åb”>„r Ùåâ`mONƒ6]*á»ïH8ç†÷衈×é”}»""(ð$:¸\[wóëÈíT»ëDŒ$¹úÕ¢/»|ùr&M𤰠:”-[¶`lƒ##3ë|žøï˜¾³=O<}"¯\t3…ÙOQ]½Ç¯o‰‰7ЧÏ †4¿åË yìðarl¾AÖ§J”FCz½¤úâ(Í` ‡Nr?H6·µ$u¸Þ3»ÛÙíÆâvcq¹Z½o©=¾‘ýj—‹B»½MºÔ«Tô £¿ÑHÿÚmMŠâ[ÐÉHt|`ž½œ_ÀEˆÑª9ëØt Ö·xê©§xá…¶o¼‘eË–ù=÷¡ÒCܹúN¶ßâSÖ=¼;‹®~“ááY;6§Ó7ÐY­6’–öwzöü *•Þ§¼ÚåbNN?VTm³‘mµ¶Ëâ…I"U¯'£F¥ ¤×ô&¥ ôÔëÑwÂá ȶZÙg6ó{MªÝ¯U+„4’¤Øo,©¡éòy•$am¡ˆ9•GÁM½ž~ Nÿ°02 †ЂÐA Ð9@Ѹ—Øó嶘ޜyà$uݵ,ËŒ?ž5kÖ(ê>ÿüóLŸ>Ýï¹Ý²›Ù?ÎæéožÆæòí­¹ãÌ;˜5f:Å9/PPðø¹× ëCß¾¯•ßkÔO)s:ɶZɲZ½¢(«f›m³‘o³µéݼ?$ I§«G RºÁ@d'b³¹Ý°X<§ºÚ+vö[,˜õâ¬u„«Õ~EN?£‘ˆV¾Çí¹X¨"b€@ ó g¼Äñ¼ æ´]ô^v‰ÂVUUÅÈ‘#Ù»w¯×&I«W¯nr\}oÑ^nÿôv~Éÿŧ,5*•%×.á <øUU¿ú=G|ü8úöKXX…½5ñ)v·››l›Í+’²¬V¯@ʶZ±´Ãlº±M“)± žæ)u8½8µÛ£Vkë{NZgÕÕPIiz½BàÔî÷Ðëý,ðrrj-0A"(ð$:‘äì\vžö9åöõ­ ^”DÂ䊺‡æœsΡ´´Ôk‹ŒŒäÇdРA^Ãévòâ÷/òü¦ç½3G×çþ÷óÊ¥/SYüGNÇá(ñ©£RééÙóqÒÒžD­Ìü?EG£½HYV+Eí°¶’Q­öi§ žá¶zù”š8¤†ÃVõ‡¯üMp²¨% |ŸÀ 2•Š0•Š0µc;±fiï¶AykëF©ÕrHS B €Î%€ìŸ~Ƕ닱SwªQ[9k×¹„­¨»aÃÆŽ‹«ÞIŸ>}øù矛í^Þ^°Û?½ß ó)ëÛ›wÇ¿ËÈäA=:üüEȲoŒÁFŸ>³ILœÐÚ—yʘ].²jz޲¬VŽÕlkmùv{ÀcS´’D²^O±ÃÑø°ÕI£Ñ0Àhd€ÑÈÀší£‘ÞaaÞh—,ã’eœÍ$4_ÇÍ-ËaÓŒP1Ô¬%:B €Î'€*ZÈŽ7ú"S¿WÎðœkP…)ï‚çÎËÃ?¬°3†uëÖ¡n&þÁæ²1#s³~˜…KVþ€«$|˜Å¯ÞÁ IDATþøË^|ˆÊÊýž'6v QQÿ W/ÿ³I»ÛÍñ1t¬ž0ªJ96[P{R$ Í`ðŠ›ú‚§»Nç÷1Pà1@GÄ!€@ç@9C_àÐÎó¶¤s«ðÃÕ>uï¾ûnÞyG9ûó´iÓxíµ×Zt­s~äŽOïà`éAŸ² YzÝRÎNAAÁ{9òWìö>õ>ÿ\Ím·]HDÄP""†1”ðð~'Tì¸d™<»½®ç¨A/R¶ÕÚ&«zT*N c`x¸Bèô kõjØ¡<P{!b€ˆ Ás"PcñÚܲ†=WÿŠ£H9ŸORR«W¯ÆPoÕi»ÝÎõ×_ÏZ|ÍÑ£Ù=e7÷ ¿Ç§l牜ýÖÙüsó?1pæ™9ýôeèõ=©÷0Z3ÈX,‡(*ZÁÑ£ÓÙµëJ~ø!…~Hb×®±9òw —a6ðxÝ‘ˆÓjÑnâ'¾Ì‚MII NgÓ=›‚SÃn·+ž^N!€º0ÆiãéÕ>…ÍjbßȵÈneÇ߈#xûí·¶‚‚.ºè"vïÞÝâkFê"yëê·øò–/I‰LQ”Ù]vžÜø$ç/>Ÿý%ûéÖíFÎ=7‹ÜÜg4h9ééOwE«{tìö”–~Mvö¿Ø»÷&~þ¹?›7G±}ûù<ø ùùoSUµ ·ÛÚªóv%V¯^lº<™™™TTTÛ.Mqq1›7o¶‚.‚k‚Î<æÅéäP¯YääŒT˜3n²“ññe>Õÿú׿òòË/+lqqq|õÕWœsÎ9>õ›¢ÌZÆC_>ÄG»?ò) Ó„ñâ%/2mä4$?SÑÙí'0™v`2m÷n-–C§Ô³#I †^h4‘¨TFÔjc›m;jÀ¶@ ´†P¨ º„äœvôYC…½_}+CÞN$îOƒuÝn7<ð‹-RØ###Y»v-]tQ«¯¿jß*î_{?Eæ"Ÿ²‹Ó/æñïÐ+¦W³çq¹L˜L»¢¨ºú7Üî¶_PµµH’•*¬Õâ)2r8±±—!I¢3V ·ìÆâ´`v˜1;ÌXžýZ›Åa!¯(—÷>͉½ÅÁv7àÔ]EØÖlá—k ±ëµiÕfÎÚ}.†±>õ{ì1æÌ™£°………±jÕ*ÆŽÛêëVrÿÚûùô÷O}ÊÂmáŒ>ŽŒ˜ EJNǨmz¶hYvb6ïÃdÚAUU­0ÚÓYÖjƒ…ÁNròd’“ÿ„N˜¹zÄ<@'Tçr¸”ZJqËnܲY–=[ä6ÏÛì6lfñqñèÔ:´j-:µÎ³¯ª·_cת‚ß3ë–Ý8Üì.;—‡ÛáݶÖfqZ°8ª°9*±;*q¸ªp8«pºL¸]f\.3²lAv[d[M²£ÂŽ'Z• ƒô*«ÙÔ(l:,øÒÀ²—-Í¿¸NŽ@MЕ@ù´%ìüwr½Ð¯È¸b†å]JïÛ1sæLžyæ…M§ÓññÇsýõן”ìú€©_M¥ÜZ^gÜŒð_¿[x7aÔdµf)zŠL¦X­Ù'ås{!I®!%å~bcÇ@›­R%æj:û<@&»‰RK)eÖ2J-¥ŠTfñc«©g²›ÚÏÉ* èßòCü £ÆSÃ2À+Dì.»B”4fk(bT¸ SƒQ FgÛ0ßTYý¼Aêv˜ãM!€]Mþ2‡·+cy’Ï+¥ÿÿ‚föìÙ<þøã ›Z­æwÞá¶Ûn;)r«r¹gÍ=¬;´î¤Ž¯O¢1±q“N¸6Ü[×é,ÃfËÅå2ãv›Ûlëv[Úü©³°°Þ$'ßKRÒ]ètÝÚôÜ‚®‹KvQn-oV´ø7þÖ÷ TxD…¦F\h$ÏV-yzCÂ[ LêçÃ5Ê2m'œÊK A—@˜ÍìI~‹¢Ê3æÓ5$=ßC.\È”)Sp×›ÝX’$Þxã xà“vå­_ßâѯ èdCÔ3º'ÉɤD¦™LrD2¡ù5ƒÛmmµp2›÷S\üi“1L’¤%1ñ:’“ï#6v4mÙ+$è¸8ÝNʬe”˜K(µ”Rb)ñ –Z›?qSa­ð™Œ´³¡x$ê!A_·­­(µbÅŸxñWøòåÍF–½Ql7Ž@MÐ%àܾŸ_Gü‚Ù]÷˜ºJ²3|C"þ˜î÷˜>ø€»îºËgž“—_~™¿üå/'íK±¹˜Í{7S©­äXù1EÊ©Ìi—»ÒXC¬W¥D¦(Rm>92™0MX›_Ûá(¡ `)ùù‹0›÷7Y7,ì4RRî%)éN´Ú„V]GÄ1@nÙííui(fê ™†¶ [çyœÞ¨5©‹D%©PI*$Iòl‘üæµ*ˆÕº‰Ñ:‰Õ¸ˆÑ9‰Ñ8‰Ö:ˆÑ8‰Ò؉Ö8ÐJ¾½ªN'X,Ùñ'„2.´Èè‘U:d H*’*¬æ‹pÔªp´š´ÚH´êHôÚhôÚh Ú˜zb„×ÕWQ©<ÛÂÂJ† O…V´žÍ°þ zí ¿þÙŠ Oï‡[Ö±çª_9+»;šß‘[o½•ððpnºé&ìv»×þÄOPUUųÏ>{R¾$(ØVà7>Å%»È«ÊóFÇÊ‘UžEvEv›¤2keÖ2öíi²^Œ!¦QT¿G©¹Àíúhµñôìù(={>Jyù·äå-¤¸xn·Ý§®ÅrÇÿÂÑ£O‘˜xÉÉ÷Ó²§òV¯^-b€Ú·ì¦Ø\L©€Õ'(0xöM'غa+º>:*Õ•^1Sn-ï=2цhâÂâ¼)Ö«È7L±ažr½Z_s»½»=›-›-»=·Á~ÇÉ?]TQû÷ÃyçµÍën$IZ‰Z‰FéݯËG5Q¦Ì«Tá¨TþÝ–“A÷ˆn”—}K~þBŠŠ>mòÇE¥2˜8‘””ûˆŽ>¿_EÇB–ÝTÛŠ¨°äa¶a²Piɡ’O•µ³­‹½»£§« Üf-„©e… ©Mб¹ý £Ú}‹[ªpPE QÇ ×ÅaÐ%®ïFdXÑa)Ä¢‰Õë‰Öi‰Ò© W«Ð«\­/Nge‡.M,z} z}tºÞ}&®ž@ñ/\ZS.¢€”$B@r~Û3>£ÒÞ×k“p3äxbï<³Ñã¶mÛÆå—_î³.ÏÿýßÿñÞ{ïµj.”`ƧÈÈ”˜K<¢È”¯Gõóù¦üvïM2h ¤E§‘“AÿØîœQHªjj׉& LJÊ}tï~M4Ð1c€ì.;&»©.ÙŠ©¶b¶a±cw–a·—ápVàrUâvU\ä6#É´ØÐ`G§r`P¹0¨ÜÔrÐ~Î*+!<Ôê 9ÐIP©ôèt1£×§Ôˆ›úûž2='v»“ÉD\\\< „¡!€lkbÛÕù8ˆñÚ´ê*Fì9}ÿÆç4ùí·ß¸ôÒK}þQ®¹æ–/_Ž^¯oäH%eŽšKI£©6Ÿ_•͸™©%à¬X¸:΋÷<ÑÒjµ‘ÄÄI¤¤ÜÇGmoq×ÎkqX°8-XVϾÄÅ^…ÕY噌ÍiÂê0awVcsVãp™±;«qº,8¸]&p›ÀmF…5V4ØÑKt*'FµL¸Âk†:F¿áÉóÝw0lDEÛ“`!¡Ó%ú4Ê}­öäçIÊËËcÛ¶m\sÍ5mè· >B €Ð@e¾Ï®W{('IŒ9ÁàŸ/GZL£Ç<•0]\1ãÙj6·ÛÞ¤@j.ïrU"Iú&ITƒ`\ÿeµB.4å­ÀB €Ð@˜-ü–²„âŠA>E‰ýOÐЦør©  €K/½”ß~ûMa>|8_ý5 Ï[ÓãS:Uö*¯0ªEõÅ’¿Åeãupe²§W¨{½ÈÒRèèa6· »¬Á.ëp¢Ç-pcDVQ©#P©¢Ðh£ÐibÑiãÓŦKÀ¨ïF¤¡;‘aID‚¶þS¨®Öžˆ ÀJHü§ ê0†1`ÓXöüa=eÖŠ¢¢ýÝ©Hÿ†~³’H˜v¶Ï¡III|ûí·Œ;–mÛ¶yí¿þú+]t6l %%Åç8sÔ4F¤.’ÁÝ3¸Û`¿åf‡Ù+Œê÷ ¬Èâ‘ߎ‘®;ÁU)2çÆÃæÍpªanYÂ…„[ö˜!Kjd4 i­gDÒ"©ÂQÕ h4Ñèµqèu±„i1‰0tàM¨)B­ŽB’:wôpfff§^ ¬3P\\,b€m†èj‚몥²ŠÜ± 9üã¸ñ dN~‚¾Æ£‰õ-«¬¬äª«®âûï¿WØ{÷îÍÆÉÈÈ”ׂXVÏ[É6L•›PIZt#Zµ½&&ƒ6½&‚0m$M$a:O^¥Ò!I:ÅV “]ŸPê¨ BVÕ`^ô%¿?”G¥£¯O™A_΀E}ˆ¹ý ßãÌf®»î:Ö¯_¯°§¦¦²aÃú÷oÅRÎ@ h7BIuö'OÄxï• Ë½Ž^C¶"¡\Ìj‹aÇŵ·Ù¥<ÎhäóÏ?güøñ {NN]t»víRØCá-؈6<%%%>kå Ú»Ýî3÷˜@p²$h)1žôᬗ* Wg7,%ç»¶%~JÕšŠNÇŠ+¸å–[öÂÂBFÅÏ?ÿìµ­^½:Pî jmx233©¨è<‹˜vFŠ‹‹Ù¼ys°ÝtÄX„úXCÜÙù»äCŽ:‹†óíJ¸H¿¦ŠôO®Eª7;ŸÛíæ`Ñ¢EŠú‘‘‘|þùç\|ñÅíâ»@ šG ~P¥%Óûàã ý{>a*å?‡Œšckbø5a9Õ›²êŽQ©X¸p!>ú¨¢~UUW\q_}õU»ø.A}BZ=ýôÓ EW6Oô‹73bÏHRzüâSVUÑ_FàøíŸ#»ë:gϞ͌3u- ãÇgñâÅ÷9Ô …»¹`#b€ˆ´%!-€7ß|3ÞôñÇÛ­Nz@ýŽ?Ê)ÇÐK%Š2·¬åðû‘ìLúÖu?¼3gÎdÖ¬YŠºv»{ï½—;^x?þ˜Ÿ~ú‰¢"ßIþ'ˆ <"(ðˆ A[Ò1@ûÛß(//gÁ‚~ËE PËpnÛÏÁ+Öq¢Øwõxµd¡ïC*’ÿ}¹×¶páB¦L™‚Ûínò¼ôîÝÛ›zõê¥Øoéb«@ h"(„øæ›o¸ñÆ™2e ›6mò)¯¨¨ ¼¼\‘ê#òåhFôg`ÁC ºå 6ò岋ý¯ëÙö!öC¥”——sß}÷±téÒf— 0™LìÚµ‹Õ«W3gΦNʸqã8p aaa¤¦¦rÞyçqçwòì³ÏòÁ°~ýzÅ?nGh‘y‘ùŽœ·X,”——“••Eee%¡Ò/ÒèòË/çé§Ÿæ†n 66–+®¸‚•+W*ê¬[·Žwß}W‘ê#ò5yµšÄ&óÛƒÛ‰Úã-_Ç:JŽ÷`kÿ˜wÓSÜzë­,_¾µúä–?e™ÜÜ\~üñG–.]ÊŒ3¸í¶Û¸üòËINN&<<œÁƒsÁ0mÚ4æÎËçŸÎŒ3øî»ïذa_~ù%?þ8+W®ä£>béÒ¥Üwß}ÌŸ?Ÿ¹sçòÊ+¯pë­·2sæL¦OŸÎO<Áµ×^˃>ÈäÉ“¹óÎ;¹ä’K˜8q"ãÇçÊ+¯äø_|1çwgŸ}6ƒ âôÓO§oß¾¤§§“žžNBBÑÑÑF†ÊŠ+p¹\mû~4È׊Âóyé‚ù’’–,YÒaüéŠùÅ‹+b€‚íOWÉÿøã¼ûî»<üðÃ,[¶Ìû}ÔÕ é!°†LŸ>={öxã%ÄØIâpý[Z› £Oq÷¹œö͵h’#8~ü8¯¼ò äÈ‘#ŠTYYçƒC¯^½˜6múÓŸˆˆˆhóó/X°@¬·`V®\)Ö 0yyyb-°JC`BÕcæÌ™ìÚµ‹U«Vyóõ·‚Öa]ó3¿ÿßÊÍý|ÊôšrúÏîNÜŸÏmôø’’QT›Ž?Þ%ïRbbb¸÷Þ{ùóŸÿL=‚íŽ@ 1„ zè!ÆOß¾}Ù½{7wÞy'‹-â†n„j,Vr®x›#ßõÃΧ8å¬\úl¸uŒ¡U§u:dgg7*ÊÊÊÚê­VˤI“xôÑG6lX°Ý!B(  ¦£P»8ÑÑÑüå/¡  €ž={òÚk¯yÅ 3úíCĽ÷ûî=J•-CQœ÷KuÿAc# ?3šð‘IÎï…Õäi5÷‰0”——ûF&“ N‡V«mtÛTÙÉnýÙrrr˜;w.K—.Åb±(üw8|ðÁ|ðÁüñä±ÇãŠ+®@’NnEö‚‚’’’NêXAË())!::ºÙà~ÁÉc·Û1™LÄÅÅÛA ¤{€šCôµ-ryYcÞ!ë—AÈÔ?¯a ×P7¦¯Â†QKxT)áIVÂû¨1Ç0,)#ÒÒ {w8I1ÐÑ(..æÍ7ßdÞ¼y6ZoàÀ<òÈ#ÜvÛm ­ë11@GÄxB©H &(0T½þ5¿?ZLµ³u1.jlÉ"œc5Ç ïf&¼—„¡ ¤§y„Qzº'¥¦‚ÎwÈ­#c³Ùøàƒ˜3g{÷îm´^·nݘ2e S¦L!11±=]!€€@Ä]PÊÑ?~HξÁ> «¶5VŒ#¼Vq”pU6†$•¯0ªr»ÁåòlkSkò5û²SÆe·UÂe«Kn›Ê³o—pÛÕ¸ì*Or¨=y‡—SMÄé’æŽE×;ÖûzdYfݺuÌž=›76úº ·ß~;<ò 8¥6HPƒ@Ç4ÿ¿üd7ÆÜHÌaTWÅátû>:2¨±N–GqŒpŽ¡§7z\¼ÉM®›g¿åeî6£“p’8°„”gG3¡¢lÇŽÌ™3‡ÿüç?8ÿÇKãÆã±ÇcÔ¨Q~눠À#b€ˆ Çç±ÇãÆoTü‹ À#b€ˆ `ÇiêÁÐõQ©œt»TKÊŒaD[÷DœÉdbñâÅÌ;—£G6z|jj*Ó¦McòäÉDGG·‡Ë  ¨³`Ï·S½·šê=Õ˜÷š½[G©ÿ!£¶EF­“Qk]¨t.Ô:7jŒJïöìëñìëeÔz•^F&¡6€3·‚‚oÃ0»’›¼BDš“”'ÐýödÔ‘ž§ç\.«V­böìÙüôÓOÉŸþô'~øaÒÓÓÛô• ‚®‡@@ ö"Pñ)ö02ï1{{Žœ¥NTFêp5j£UxÍ~¸ºÎÞ`¿©2UØ).§g±R6}yoR\u&r1Ej½›î·$‘òPÃê–ËØ²e ³gÏæ³Ï>Ãívû?V­fРAÄÅÅKll¬bß_>&&æ¤×jëj¸Ýn ÉÉÉ!77×ï6//˜˜ïšo SZZáááÁ~)x„Bµ">p»±/]CÁÌŸÉË>+M÷ E #ej:Ýnêæa‡æÕW_åÝwߥººú”]’$‰¨¨¨‰¥úù˜˜ z½•ªã¯·l³ÙÈËËkRÜäççãt:OùZ Š£ôôt?Ô `Ó¦MüñÄåráv»[œN¦~Gúy”$ £ÑHDD„"…‡‡®¦ýH$ò÷›)ûëJò~L „ói\Dh"%’îêAÊý)zžž+--eÁ‚Ì›7üüüörÛ¿ z½ÞoªIm™žS§ÓQ^^Þ¤¸)..jÕ'""Â+†ü¥äääN!*[‹ÛíæÄ‰äææ6™BiäÖ¢Õj}ÄQcb©©d±X¸öÚk9qâD°_RÀ¨ „•°=;ŸüeUä;Çb£éIc.Ž!åþ®O@¥Sa·Ûù裘3g»wïn'§D«ÕÒ³gOÒÓÓIHHÀh4zSxx¸"ß›^¯¸Ïf³Ù¯˜©¡¹¹¹´I› mˆŠŠ¢¢¢"Øn!€š@ öAÌQÓ 'N ¿þ¥ÿþ™¼ªÑ”rN““Gjµ$ßLò½É„õC–e¶mÛ†Z­¦¬¬Œ²²2JKK½ûå+++;Ô@°‰ŽŽ¦G¦°°0T*999dee‘MVV–7åää4:—S°P©T­Pþì*•м¼<¿B§©i“P@bÆ.AÐY½zµˆjŠîÝ‘ž–ø¿W¿d Ö—ÿB~ÎPò¹;¾Á Ž"Ù/e“ýr6q—Å‘r Ûò¶ñÀ”ZuY·ÛMyyy‹ÄRý|EE6› ›ÍÖ)”Z­&))I!fRSS}Ns̵ó56+·Ûí&//O!Šê§ììì6‰Ýj n·“É„Édj×ëž,:t„†#*THHŠmí_C»¿zÍÕ‘jþÚ I% $µäÙW+m¨ÀŠ‹lÁì6cqY0;͘fªոܮvóµ« z€š@ô :$.¬\‰üòlŠ #k(c84ñe­ï¡'jdÚD-ºn:´‰Z´Ý´èëöµñZÏmâp8¼b(X)**ªÉž›¤¤¤ÇÕÈnG‘{¾Ý“ ìØòmžýl’V…&Ñ€&V‹6V‹&FãI± ¶1¿m]RRÒ¨@ÊÊÊ¢¤¤¤MßŸŽ‚„D4Ñ$ÔûK$Ñ'Id°]í°8p`iðgÅêcköO²ÐÓГŸÌO¯ÑU¨ „tx23á•W°|µ‹<®¢€+pprJ* MœF!’¼bÉpÒÄkYAŽ='#×›Ôäà ”XE×½&¹ÁíÓ°L-ntÈèjöµØ<û­·éjÖì87C$ ] AÐ1@mÀàÁð^xnsçÒmáÓ˜+cÉãj ¸ŒÍlæÚvý¤Úá G‘ö¶é©Û™¶y¬|;ÛÆ0¢ˆj¾ò)"»¥&áëhºŽ-F´‰h²^ pa @!rj“!Ò„*-ÒÒ gOH;“¼È ØY\Ì5gœ&“'UW×í·$ßÁÑkq£i Š´õ„RCUßV——e¾åMåe¯ ÄXˆ!0A§¥²-‚¹sqçQÉ騉ÁQ“ìD{÷=ùœDµðG5t‘p£¥%è)EGI] ³ I‹§“Èšƒúy-T~lT81¯8šÂ’\z‘ÒS뉜zÛ@­açp4.ХРO}§Ó³­MMå[[·õ¶ÈHÈè˜Á?+:ÎüX"4þ‚P#* ¦MCõñÇÄ|ù%”–Bi6”íôìWT@½û 'Ñ~Å‘áÝä$ vt5‚F!lTåè¢è»kÑ¥èѦG#%w‡ädHêÉ@R’'oôLD‰Û UUžö-/÷lûÇ¡¢WqÎbÎRÎ2ÎJG•ŒÓ¬Æi×ã¬MJñäÙwøù{Zƒ ;aäFŽGàDU–ä"¬·}¿h¤ô4Hë =G{ÄM÷î¬ µZˆõ¤Ž€,׉¢æÄÓ©&»½ÉrÉáÀn2q|ëÖ`·J»  舠¢ÕÂí·SpÙe¾mìv{~”KK¡¬ ©´mYÚš<õ·¥G”6‹pƒØâ(¶¦;_¨kî*k÷UÞÔ|¾5uÕ~“Q¡ÁŒŽâaS#nô&tñ OÒ¡ëiD“W'd’z×l“ [·ÿP—””F£ñô^DG{~ìA]“üJ§ÓpªÍç"—U!»Ü ÕôØI¨êíר¥zû~ëÔvøÕ¯§R#I¾çmX‡°0ÏkM»RS!@,vɵÀ$ÉóªÕÛÊ Ø0th°Ýh„xü¶±JqqžÔZ¬V¨K>‚Éßr’ÔtÞÇæIÆ|*çÁó㜔ÉgÕ‰œÈ¶œ:33“Ñ£G·Íz^ ÄÇ{’$:RÈlûQ\\̶m۸暶g„&"¨ D @ B‰PZ µk à @ ´!€A'î4‚hãÀSRR"ô 0v»ÒÒÒ`»!è"$:«W¯¶ ]ÑÆ'333$ &ÅÅÅlÞ¼9Ønº"¨ D @ B $@Ð…HtBáN#؈6<"(ðˆ A[" èˆø”À#Ú8ðˆ À#b€m‰ˆj$‚PBÄ @ ta„P¸Ó6¢ˆ <"HЖ$:">%ðˆ6<"(ðˆ A["b€š@Ä  ”1@@ ]!€A'î4‚hãÀ#b€ˆ´%B ‚ŽˆO <¢ˆ <"HЖˆ &1@@ %D @ AF AÐ …;`#Ú8ðˆ À#b€m‰@‚ #âShãÀ#b€ˆ´%"¨ D @ B $@Ð…HtBáN#؈6<"(ðˆ A[" èˆø”À#Ú8ðˆ À#b€m‰ˆj$‚PBÄ @ ta„P¸Ó6¢ˆ <"HЖ„¼ÊËËcÅŠlذ‹ÅlwB’÷Þ{/Ø.tyDž5kÖPRRl7º4ùùù¬[·.Ønºš`;L6mÚÄĉ3f ÇÇb±°qãF¢¢¢‚íZHa6›ƒíB—G´qà±ÙlˆÊÀ"Ë2v»=Ønº!Ý4}útfΜɇ~Èwß}‡^¯çÃ? Š/¥¥¥§ôÝš!Ž–ÔmªNceþì m.—‹¢¢¢f¯œNç)Ý¡·æ=2›ÍTUU5Y§­Ú¸¼¼›ÍÖâsšS¹vkߣæ®UUUÕ¨økì=2™L˜L&…Íßç¶³¶qkæ÷EKþ…¿ÏAkhM·ä»%PmÜܹ»2!+€, ›7oæšk®@’$ÆÇúõëƒâÏš5kÈËË;éã,XЦu›ªÓX™?{C[UU•È<•/™ÖP\\ÌŠ+NúøÖ¼G»víbË–-MÖi«6^·nGm²^{µ±¿k·†Ö¾GÍ]kË–-ìÚµËoYcïѶmÛØ¶m›ÂæïsÛðÚÕÕÕít*mÜÚãƒù}Ñð=r8TWW7ëO[àïsÐZÓÆ-ùn T7wî®LÈ>ìØ1zõê…ÍfC§ÓðÎ;ï°páBþ÷¿ÿžÇßÿóŸÿ0`À€€ûSPP@LL ƒá¤Ž?vìmV·©:•ù³7´9N HMMõÚvìØÁСC[äû©`·Û)..&%%夎oÍ{TYY‰Óé$..®Ñ:mÕÆ………DDD`4­×^mܘ-¥µïQs×*--E£ÑøÖnì=*// &&Ækó÷¹mxíßÿž={Þ"ßO…SiãÖÌïQee%ôëׯE¾Ÿ þ>­¡5mÜ’ï–@µqC›Ífcûöí!Ñ+²1@V«µZíµ©ÕjE7äM7ÝÔn?@ tNöF¼³²(>>ðtm×*ü²²2ºuëæ­3`À€véý@JHH --ÿýïŒ;€_~ù…áÇ·èø'NðÌ3Ï——Çé§ŸÎÌ™3½Ci‚¶¥´´”Y³fñâ‹/Û•.É_|ÁçŸîÍ?õÔSŠ¡AÛ°iÓ&>üðC4 o¼ñF°Ýér|óÍ7,_¾Ü›ïß¿?<òH=êšlذÏ>ûŒÈÈHxàzöìl—Nšxýõ×yýõ×™>}:yyyÌš5‹mÛ¶Ñ«W¯fÍÉÉ¡¨¨ˆ3Î8ƒéÓ§Ó·o_&OžÜ^‡6›Ûo¿Ý»w³wïÞ`»Ó%yæ™gÈÈÈàâ‹/ %%Eˆù6&33“þóŸÌ™3‡Þ½{+âµmCee¥w’Ä}ûö±bÅ –,Yd¯ºeeeLœ8‘õë׳gÏ^|ñE>þøã`»uÒ„ìS`S§NåùçŸgÓ¦M”––rÎ9ç Õj}êÍŸ?Ÿ³Ï>›aÆñüóÏ#Ë2©©© 6Œ­[·rìØ1qdYæÆoôû´Ã¼yó¼mü /xçS™:u*O<ñ„˜›©üðÃ<þøã>ö’’n½õVú÷ïÏe—]ÆO?ýä-ËÉÉáØ±cBü´ƒrÇwøØÍf3÷ß?dôèÑÞIûæÎË7Þȯ¿þJnnn{»Ûi¹óÎ;9pà€}É’%Œ9’!C†ðÔSOáv»‰ŠŠ"##ƒŒŒ Þ~ûmžzê© xÜùxñÅY»v­ýÛo¿å’K.aÀ€Ü}÷ÝTVV¢×ë)))aãÆ”••µ¨³ #ÒàÆodðàÁüðì[·Îg6è•+WòÒK/±`ÁÞÿ}–-[ÆÜ¹sp»Ý¬Zµ ­V«¦ø2{ölÎ?ÿ|V¬XáÓÆË–-cÖ¬Y,\¸÷Þ{>úˆyóæñÜsÏqõÕWsÖYgÉëÎENN“'Ofüøñìܹӧ|„ DGGóõ×_sÓM7q饗RVVÆèÑ£‰ŽŽfýúõ\xá…b¢¹&¨ªªbÊ”)\rÉ%üüóÏ>å÷Ýw¥¥¥|ñÅL›6‰'rðàA²²²p:DFFrûí·‹ÞÌf˜?>£FbéÒ¥>S8¬[·Ž'Ÿ|’×^{eË–±~ýzÅðø·ß~KJJ ½{÷no·;k×®å†nàé§Ÿö¹)ÍÍÍåꫯæàË/¿Än·sûí·c05jk×®eòäÉ\qÅAò¾ò/¿ü"gffÊFÞ¿¿¢lܸqòœ9s¼ù÷ß_:t¨¢ÎçŸ.?úè£íâkgeÛ¶mrff¦¬R©äC‡)ÊÆŽ+¿öÚkÞüÒ¥Kå³Î:K¾çž{äI“&É“&M’ãããå¿þõ¯íív§¢²²RÎÌÌ”üqy̘1в£GÊZ­V®ªªòÚÎ=÷\ù­·ÞRÔ›0a‚üÛo¿µ‹¿›Í&gffÊ/½ô’<`ÀE™Éd’ ƒâó=qâDyÆŒòE]$›ÍfY–eùõ×_——,YÒ®~w6vìØ!gffÊQQQò/¿ü¢(›4i’üÌ3Ïxó«W¯–ûöí+˲,»ÝnyÔ¨Qò‰'ÚÕßÎÈáÇåÌÌLyĈòÂ… e¯¼òŠ|õÕW{ó999²F£‘?ûì3yêÔ©²,ËrII‰K’äSvàÀþüç?{ógœqû÷ïgË–-8NÒÓÓY±bcÆŒi7;#µ½8þÚøàÁƒ<öØcÞüàÁƒùý÷ß“9’ýë_w´ɨQ£8tè;vìP”ùäN?ýtÌf3GŽéôÝÚD§Ó1jÔ(ï4õÉÎΠOŸ>^ÛàÁƒÙ·oW^y%sçÎ妛nbÍš5¼ÿþûíæsgäÌ3Ï@£ñý‰:xð 'NôæÌáÇq:,[¶Œ‹/¾Xñ4¯À?½{÷¦wïÞ~ç*;pàƒöæ{ôèADDz½ž­[·’››Ë¡C‡u:#B5CQQ‘"`122‹ÅBtt4+W®¤°°Ë/¿œ›o¾9ˆ^vn mEuu5‹…°°0î¹çž`¹×%(**ò¶e-QQQ¡×ë™?>F£‘eË–‰Ý“¤áwx¾/ŠŠŠxì±ÇxóÍ7Y°`sæÌ¡{÷îAò²óãïûB–eŠ‹‹±ÙlŠ›)ÁÉQTTäótWTT‡ƒùóç³`Ázöìɼyó‚äaÛ P3FEL„ÅbA£Ñ0hРN¯~; ááá>m¬Õj“q tj4lcðìFEEqÕUWqÕUWɳ®CÃï ð|–£¢¢Ðh4L:5Hžu-ü}_€çúî»ï–[]Ц¾/† ưaÂäYÛòAÐÍ‘œœÌñãǽùììl’““ýåNmœ’’"Ú¸ INN&??—Ëåµ?~œ=zÑ«®Err2ÕÕÕ”••ymÙÙÙ¢Ûß111¢ç² INN&''Ç›¯ý\wµùÁ„j†‰'òî»ïâv»xï½÷˜0aB½êZˆ6<#FŒ ..λÐhVV›6mâú믲g]‡=z0räHïÜ3¥¥¥¬]»V|–Û˜‰'òÞ{ïáp8ñ}&L˜ÀÚµk9qâ|ðgœqF×{².ØQØk¯½VNOO—¹Gò™gžé-«®®–¯¾új911QNMM•/ºè"¹¸¸8ˆÞvN®¾újE6Ì[f2™äqãÆyÛøâ‹/–KJJ‚èmçdçÎrzzº/ 9==]ž={¶·üûï¿—»uë&÷ë×OŽŽŽ–_}õÕ zÛ9),,”ÓÓÓånݺÉZ­VNOO—üqoùo¿ý&gddÈ}úô‘cccå'žx"ˆÞv^n¾ùf9==]V©Trrr²÷)/Y–e«Õ*ßxãr||¼œ––&ŸsÎ9r~~~½íœüóŸÿ”ÓÓÓeƒÁ ÇÇÇËéééò¾}û¼å3fÌ###å~ýúÉiii>OãuBz&h@ ¡‰@r$ äH AÈ!@ ‚C @Ð&;vÌ;•A°ÈÎΦ¸¸8¨>‚Î@A$;;›±cÇòÐC)ìsçÎeåÊ•m~=§ÓI¯^½“¶'YYYœ}öÙŒ=Z̸,Z„@AÄd2±mÛ6V­ZÅ7ß|ãµïÙ³‡£GѳÀ°|ùrÎ9ç>ÌÇlwA'@  ‹¢×ëùÇ?þÁßÿþw¿å;wîä­·ÞRØžzê)ÊËËxÿý÷ùî»ïø×¿þŤI“˜7o²,³dÉn¹å,X@uuµâøßÿ‡~˜›o¾™¯¾úJQ¶iÓ&&OžÌ¤I“X¼x1µSmݺ•¥K—’••Åœ9sX¶l™_+**xê©§˜0aO>ù¤×Ï-[¶°xñbŽ=Êßþö7víÚåsìÞ½{yóÍ7)((àßÿþ·w¶æœœyä&NœÈ¿þõ/l6ÿûßÿX¼x±÷ø5kÖ°aÃoþµ×^ãÈ‘#dffrß}÷qË-·ðÜsÏygÏ!€‚.Ì=÷ÜCii)«V­ò)Û·o}ô‘Â6kÖ,***øä“O˜0a‹…+¯¼’Y³f1`À~øáÆŽˇ~ÈìÙ³Ç?òÈ#ôëט˜®¿þz¶nÝ À_|ÁÝwß͈#˜8q"óçÏç•W^Ÿó>ŒŸ/‰Á`ÙÙYÜn7äææX·nÝí‰MMM:tˆááa>}úÄôô4{÷îeff†cÇŽqÿþ}óÑ †a‹ÅÌö±XŒ+W®ðõëWp»Ý¥¬¬ €p8Œ×ëeýúõ”••ÑÕÕÅÜÜÜ’k‘Õ£ ‘ßœa´µµqæÌJKKÍ÷ÓÒÒøöíÛ²úù·×ÇÉÍÍÀårÑÕÕÅþýû—^ø_¸\®oQQQ±¢þú\XÇ077Çøø8.— ˜`hhˆ{÷îñöí[š››q:œ={ÇÈÇã ÒØØˆÍfãÔ©S+®KD~ ‰XÀÁƒÙ¼yó¢µ@Á`‘‘>~üÈ÷ïß¹yó&³³³ÿë: ‹¢ß¼yC__ÕÕÕTWWÓÚÚj~þùógnß¾½ä~+**ˆÇãô÷÷ó ª‡‡‡©¬¬\q­‡¦¿¿Ÿ×¯_“J¥èîîÆn·S\\l^³··—ŒŒ òòò())att”gÏž …xôèccclݺ•h4ÊŽ;–*Edõ(‰XD{{û¢][yyyœ8q‚M›6‘ÍÇMy­Dee%ìܹ“h4j®;êèè ;;ŸÏ‡ßïÇãñ,ÚUõ_¶lÙBgg'ápŸÏGUU×®]Ãï÷¯¸Öòòr.\¸ÀîÝ»ñù|\ºt‰žžs¨°°ôôtêëëù¯ÚÚZü~¿9}÷áÃvíÚEAA………dffrúôé×$"¿Ž‘ZØ‹*"""bËQËQËQËQËQËQËQËQËQËQËQËQËQËùj8¾ý6ì¤IEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/compressed-recordsize-shuffle.svg000066400000000000000000001004111231437614300276630ustar00rootroot00000000000000 Disk space taken by a record (original record size: 16 bytes) 10 3 10 4 10 5 10 6 10 7 10 8 Number of rows 0 5 10 15 20 25 30 Bytes/row No compression zlib lvl1 zlib lvl1 (Shuffle) lzo lvl1 lzo lvl1 (Shuffle) bzip2 lvl1 bzip2 lvl1 (Shuffle) PyTables-v.3.1.1/doc/source/usersguide/images/compressed-recordsize-zlib.png000066400000000000000000001175131231437614300271670ustar00rootroot00000000000000‰PNG  IHDR@°AàÚ²sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwX××ðïîÒ»T)R”"*JØ"±$hìÆM°F Q1š`4F£±ÅhL"v‰kŒhì%Æ.± "Ui"*½Ãyÿðe»Šóyž}tîÜ™9{gwö0s玈ˆŒ1Æcˆ¸¾`Œ1Æ{Ý8bŒ1ÆX£Ã cŒ1ÆN€cŒ1ÖèpÄcŒ±F‡ ÆcŒ5:œ1Æc¬Ñáˆ1Æc'@Œ1Ækt8bŒ1ÆX£Ã cŒ1ÆN€cŒ1ÖèpÄcŒ±F‡ ÆcŒ5:œ1Æc¬Ñáˆ1Æc'@Œ1Ækt8bŒ1ÆX£Ã cŒ1ÆN€cŒ1ÖèpÄcŒ±F‡ ÆcŒ5:œ1Æc¬Ñáˆ1Æc'@ÿqqqÈÏϯï0ÞX‰‰‰ÈÊÊzíÛ}úô)ÒÒÒ^ûvÿ«rrrðøñc¹ê!..®^ö«2âââPPPPß¡ÔJAAâââ@DõÊ%55Ïž=«ï0*INNFrrr}‡ñF⨎3þþþ4h>úè#|þùç8}út¥ƒÅ_ý___nݺ???tïÞ]ꀡ££ƒ–-[B]]½¾B}#}òÉ' ­ï0X=;yò$6mÚ„ƒÂØØ¸ÖëkÞ¼9ÌÍÍ宯­­–-[BCC£ÖÛfÒêûØwêÔ)|õÕWøä“OpçΙu8///¤¦¦bܸq˜3g •Ûòå˱eË–:]çÀ1gÎŒ9ò?ö±®©Ôw QïÞ½1uêTa:** ¾¾¾8p þøã¨¨¨ÀÙÙAAA011êàöíÛxñâlmmáèèXå6JKKñèÑ#èëë£I“&UÖ{üø1""" ££ƒ6mÚ@WWðüùsÁØØ·o߯ӧOáéé)µ®üü|Ü¿Ož<¦¦&\\\dRRR===¸¸¸Hز³³qëÖ-äççÃÍÍ­Ú³ÔÔTáùó爋‹X[[C,ãñãLjENN,,,Ю]»*×Sþ½«««ÃÔÔTh³ððpÄÅÅÁÞÞ-[¶êfff"++ –––xøð!?~ŒnݺAEE¾¯HQQnݺ…ÌÌLxyy 휜œ 555Uz¯b±¸Êö(**BDD’’’ ¦¦†V­ZÁÌ̬Ú²²²‘‘+++@RR …ñääd„……ÁÀÀîîî•~€rssqïÞ=dgg£M›6RŸÍ¢¢"üóÏ?xöìÜÝÝ¥æåååáÉ“'°µµ|¸ÊxGŽIb±˜455I__Ÿôõõ)55•þúë/@æææäììLêêêÔ±cGÊÌÌ–µ³³£ 6ÓK–,!CCCºqãÅÇÇ“··7ijjR»víH$ч~(Ô_µj™šš’¿¿?©««“ŽŽ™››ÓéÓ§«Œ—ˆhèСäêêJ¤££CÈÚÚš>|HDDäèèH¥¥¥Â2ùùùdllL›6mªr½£G&555rpp kkkRUU¥•+WVËúõëÉÜÜœ~þùg211!´}ûv*--¥Ù³g“X,&ÒÖÖ&gggŠ– %SSSRWW§¦M›’D"¡o¿ý–ˆˆüðCòððZ&33“ttthÏž=´`ÁRQQ!555¡-ÊŽ×[·n%jÞ¼9™™™‘‘‘={VXÏ”)S„c}“&MHCCƒ.]º$µ­/¾ø‚:tè Ð{jè8ªcU%@DD†††´|ùr"ªœùøø QZZíÚµ‹ˆ¤ ÒÒRš0a9:: gYÎ;G¤þù§œL™2…ÌÌÌèðáÃT\\LáááÔ¾}{òõõêߺu‹òóó…éÅ‹KýxýôÓO¤¡¡Aûöí£ââb*..¦Í›7SDDÅÄĦ¦&íÚµ‹JKK©´´”–,YBM›6¥‚‚‚*ãvrr¢5kÖH•¥¥¥Iý¦¦¦’………T½ò Њ+ÈÈÈHê½÷èу† FDDI***Â^ÙËúõë)??ŸJJJhРA4jÔ¨*c%z™ÙØØÐÉ“'©¤¤„®_¿N­Zµ¢aÆQtt4‰Åb:qℰ̶mÛÈÈȈrss«\ï;w¤~`6mÚD‰„rrrª\fýúõ€ú÷ïOþù'=xð€RSS)$$„ŒéÁƒDôò‡¸W¯^Â{KJJ"===š>}:=}ú”ˆ^þH•}þ:wîL}úô¡ÄÄD*..¦M›6‘H$¢ëׯÑ¿ PÇŽéСCI túôi@k×®ÞëªU«jL€V­ZE®®®•ÊOž<)$¥¥¥K;w¦þýû u¼½½ICCƒ‚ƒƒéæÍ›FD$•egg“ 8PHžž>ž}š&OžLNNN”””Tme™ 6H%1e¦L™B½{÷–*[³f ‰Åb©ú©©©´ÿ~Z³f M˜0A*fOOOòóó“¹ýE‹‘½½=Ý¿Ÿ"""(""BøA¼sçN•qËJ€ˆ^~é¯_¿N?ÿü3-]º”Z´hAÓ§Oæ—%@k×®%cccá‡èe€8 ÄAÂ_Á«V­";;;©mnß¾Œ«Œ•èe4bÄ©²àà`Ò×צû÷ïO¦;tè@sçέv½DD/^¼ ßÿ¾ûî;š5k¨tP+¯ì PE;v¤>ø@ê½/\¸š5kFDD«W¯&±XLiii•–ŠŠ’Žòš6m*œ](K€òòò¤êŒ?^8H—Ù¹sg ÐŒ3hÈ!•ÊÇŒCÎÎÎRe?ýôêÞÞÞR?¼eÊ'@'Nœ¨ôCDD5žÒÒÒ’ªüøqRUU•ú¾Ð… è‡~ ¯¾úŠš4iBk×®%"Å ÂÂB©òaÆ‘ŸŸŸÔ>üé§ŸHEE… iÿþý•þà)“ŸŸOb±X8£WÆËË‹üýý‰èß(""BªÎÂ… I[[[ê– .Ô˜µlÙ’Þy癤U•‘½óÎ;ÂÙ—o¿ý–š5kFáááÂû¾té‰D¢JûOQU%@îîîäææF‡¢ˆˆ:wî¹»»SË–-«ýãÍÏϤÊfÍš%|ψ^~Ëo³eË–´dÉaZV4cÆ jß¾½Ô¾/ÛßIIIFèûï¿—y¬/“])Qjì¸ÐkRZZŠû÷ïcâĉ2ç/Z´ppp€¹¹9Þ}÷]ÌŸ?_èÏŸþ9þúë/;v¬Æîîîøì³Ïˆ™3gÂÍÍ 3gÎİaà ‰d.ãææ†ÒÒR$''ÃÆÆAAAXµj|||`oo’’þŽŽÆÐ¡Ce®+::iii>|¸Ty»ví““SmìÝ»wï¾û.Š‹‹Ñ±cGXZZB, q”Ù¹s'.]º„Õ«WKõŠŽŽ,\¸°ÒºÕÔÔªÜ®ŽŽŽÂ±/ßcFF²³³¡££ƒiÓ¦¡_¿~HHH@rr2nܸ½{÷V»Ž•+W"((ÞÞÞprrúT|ÏÉÚ·ÑÑÑHJJÂ7¤ÊËú`DGG£]»v•ú)•Í€.]ºH•wîÜY˜WÕ¶cbbгgÏjã•åÅ‹Bß›Š±ÈŠ£l^Yߊª>ßebcc¡«« OOO…c+OGGEEE(,,„ºº:Ξ=‹!C†ÀÄÄhÚ´)$IûL–Šï!:: 6l˜TyëÖ­‘‘‘èèhÁÕÕµÒºâââPZZ*³íNœ8Qívcbbеk×j¿'²¬]»Ó¦Mƒ¥¥%ììì0|øp|úé§Õv$þôÓO‘’’‚Ç qDGG####FŒªÛ¶m[äåå)“"z÷îwÞyàä䄟þ C‡ä^O»víðÝw߈ ‰0mÚ4Œ3+W®DXXâââðÑGU»ŽèèhÄÆÆVÚ÷íÚµCff&Úµk‡ `Μ9˜5k–Ô±¾¬ð²c¿®®.ÛQ'@¯ÉÑ£G‘ ___™óÝÝÝqõêUܹs—/_ƺuë0hÐ ©[fŒŒðÑGaìØ±¸xñ"ìíí«Ýæ’%K0{öl\¾|¡¡¡xÿý÷¡££ƒþýûˬçÎH$˜››ãöíÛX¾|9.\¸ 8¯_¿Ž­[· õMMM«¼MØÔÔmÚ´Á… ªQª0dÀ_|{{{üþûïPUU™ÛÍÏÏÇ¢E‹ðÙgŸÁÝÝݺub€Ã‡ 1•©¬CxYÕ·Þz Í›7ÇO?ý„GáÝwß­6ŽÔÔTÌž=¿þú«`&%%aýúõ¯©©)F…yóæU9?66%%%RŠ´£££…„ "##Ѿ}ûj·«¥¥õJãTu666®”t• ™P¾SvMš4i‚¬¬,<}ú´RgÚÚ˜1c „7 ?àüñG¬ÛÔÔظqc•ó_¼x´´´JëËïC///¡<22²Æv{Õ}øÖ[o!<<·nÝÂÅ‹±fÍÄÆÆâ—_~‘YçÎØ¼y3®\¹"Õy×ÔÔööö•’we²··G||¼TYÙwùùóç ­+<<æææÂçaàÀ044Ä–-[péÒ% >¼Òþªxü355E—.]pðàÁ*·³xñbâÊ•+ Ř1c ¥¥…wß}W¨SPP€ìììJ§3¾ þ58~ü8Fމ¹sçVyWÈ;w ‰Ð¶m[|ôÑG BLLŒÔ_³fÍÂ?ü€nݺ¡wïÞHLL¬r›qqq‡ÝÏÏk×®…½½}• Kqq1:„nݺA]]Ož<îV+SqÌ¢Î;ãØ±cR4¦§§#-- >>>¸rå þùç©e«?ÄÉÉ OŸ>•*{òä œœœ„ä§  ©©©•–8q">ÿüsbÀ€Âx-[¶„™™~üñG©úEEEu> áØ±cR‰®H$ÂÔ©Sñã?â×_­qÜ ÔÔT\\\„2yÆ‹ªŠ¶oß^i Ͳä¡sçÎHOOÇ™3g¤æGEEÁÙÙÆÆÆøóÏ?…ò„„ܹs]»v­v»mÛ¶Å¥K—¤Î¢Uüa‘ÅÒÒRæ~éÚµk¥õ…††ÂÚÚZ¡ÄÖËË ªªªØ°aòóóADØ·o_­ö{òä Ú´i#üØ¥§§#33³Vë,ãããƒýû÷WJ ËöaÇŽ!‰pèÐ!©ù>„‘‘œ¥öa^^Ξ=+×>¼sçŽTTÓ>,))Axx8TTTàéé‰Y³fa„ U{nݺ…€€lÛ¶MêxSö¾ÃÂÂpåÊ©ò””©2?Žï¾û®Ú¸äÕ£GüùçŸRǘÇC]]½Æ¤¿¼ÒÒR?~\êX ¢¢‚I“&aõêÕ8|øp¥c¬ãŸNœ8˜˜©ò²ß‡²ÁB ЧO¬Y³•Ú;>>DôZþüϨ¿«o “‘‘yxxÐСC©wïÞdccCªªª4sæL©zûYZZ’ŸŸ-^¼˜>ûì3²µµ:LW¼ ¬  €zõêEÎÎÎ2ûm…„„™™M›6V¬XAÇ'ÊÊÊ"¢—}€ÔÕÕiðàÁ4sæL²´´$¡ïLqq1Y[[“››Íœ9“úöíKFFF@Øfbb"™››“‘‘}òÉ'4cÆ 211:jŽ5ŠtuuiÚ´i´xñb2dijj wET·žžÍ;—¦M›F/^¼ ÒÒÒ¢Q£FѤI“¨y󿤝¯OS¦L–«xX@@™˜˜}öïßO4`Àúꫯ( €š7oNË–-#"Ù}€öïßOšššUÆJô²P“&MhÊ”)ôù矓ƒƒTê«“žžNÚÚÚÔ¶mÛj×WÆÓÓ“Z¶lI3fÌ Aƒ‘™™ Û·oW¹ÌúõëÉ¢RyZZÙÛÛ“££#ÍŸ?ŸæÍ›G¾¾¾R± >œ$ 8¾üòKòôô¤?þ˜ˆ^Þ"‘H¨OŸ>4kÖ,200 N: w7•õªØÿàñãÇÔ¤I222¢É“'S—.]ÈÈȨÆ>@—/_&JII‘*ÏËË#211¡9sæP×®]IUUUèÈKô²PÙÝFåU¼ ì›o¾!±XL†††¤¯¯O:u"tüøq"’¯PÙ݉ew+Í›7ŒŒŒhâĉ4~üx²²²"---Zµj)Ö¨¬Sl™üü|òòò" š3g}ñÅÔ·o_jÒ¤‰Pgîܹ€zõêE‹/¦=zлï¾KD/o€PWW'ooo ¢¦M›’ƒƒƒ{Y ²ŽòeÊ:ŒkkkÓĉ©OŸ>dll\m üü|’H$4xð`Z¶l’±±±pÇ\Å>@¶¶¶dnnNR¯òwOiiiÑäÉ“iñâÅ4lØ0ÒÑÑ‘ŠU‘»ÀÖ®]KsçÎ%jÓ¦ Í;—¶oß.Ì/..&///rvv¦o¾ù†† Bêêê´bÅŠj×ëççGÆÆÆ4}útš?>ÙÚÚ’‰‰ ÅÄÄHÕKII!555êܹs¥uœ;wŽTUUiúôéHáááTZZJ}ûö%CCCš5k}ùå—äïïOjjj”››K[·n%SSSš:u*­X±‚FŒAFFFRwɽ<ÆUìCרIÊêÁjÅÎÎfffh×®üýý±nÝ:¼÷Þ{RuD"tuuáëë ôìÙéé鈊ŠBAAÆ€€ˆD"áåëë H$ 4999(,,„““S¥œœœ`nnŽÄÄDÄÄÄÀÚÚ7nÆ’9zô(D"úöí‹G¡Gظq£°.±XŒ!C† //111ðõõÅòåËahhˆîÝ»CUUººº˜0a455‘˜˜555̘1>>>‹Å4hÜÜÜðøñcÄÄÄÀÖÖ .„‹‹K•ý4\\\àááþùšššèÕ«Ú·o777ܽ{ZZZX´h:wî 888ËvìØQè;Ò·o_¨ªª"55nnnpvvƨQ£ðüùsåûèééaðàÁÈÏÏÇ‹/0xð`Ì™3M›6úîÈbaaíÛ·C$I¡PQQÁ¸qã ªªŠððp8::bíÚµèÑ£‡ÔòîîîhÞ¼¹T™H$‚«««PÞ¹sgŒ5 :tÀ´iÓ0kÖ,,]ºsçÎ…©©)D"´µµáëë+ô¿233«tƤlŸH$téÒvvvøçŸ`nnŽÕ«WÃÞÞ:túò‰ÅbtïÞúúú2ß»H$‚¡¡!|}}¥¾#***øàƒ`aa¸¸8$%%ÁÅÅK—..MöêÕ ÞÞÞÈÉÉArr2:v숹sçBOO-Z´À»ï¾‹´´4ÄÇÇÃßß!!!ÂxU"‘ZZZðõõ…–––°]555Œ9%%%xúô)zöì‰eË– û»â%Ó²X»wOŸ"22"‘sæÌ‘º£££#G222àáá©W‡ ££ƒ cÇŽHHH@tt4š5k† ÀÃÃCªœáãã#³]Ë C~~>\\\ЪU+¨¨¨ÀÌÌ mÚ´öÑèÑ£¼¼ìohhˆ¥K—bÔ¨QÕ®W$¡GÈÍÍÅýû÷Ñ­[7lÙ²¥Ò‘H„uëÖ᫯¾BëÖ­¥æÙØØ {÷îGII zöì ===Œ5 NNNˆG||<ðõ×_ cMYZZ"11QhŸ7J]²&"Lš4 C† ©²Fc$"âº4FS§NÅÇ¥N‹3åÙ¾};fÍš…„„hjjÖw8o¼mÛ¶aúô鈈ˆÙ!º¶aaa!ü€®Y³Ë—/Gll,ÎΔêûï¿Ç²eË'÷@«µõóÏ?ãÓO?E\\\µç66 ¾PII =z„ôôt™óKKK‘””ÄöcJ»_ IDATµråJLœ8‘“93F8KSXXXçëŸ={6ŒŒŒàéé ,Z´!!!œü0¥*--ÅêÕ«1yòä×–üܺu Ÿ~ú)6lØÀÉO ú ÐÌ™3ñÓO?ÁÈÈéééðññÁ/¿ü"œöÝ»w/ ¯¯ììllß¾o¿ýv=Gýzܾ}ÙÙÙèÔ©S}‡ÒàåååáøñãèÒ¥K<ת±xþü9Ο?öíÛK QRSSqíÚ5¤¦¦Âžžž¯åYO¬qËÊÊ©S§„î ¯Ã¥K—››‹^½z½–íý—4èhÇŽxë­·`ff†¬¬,xzzbêÔ©˜>}:ÒÓÓaee…3gÎÀÓÓÇLj#œœÌ2Æc \ƒ¾6zôh¡Ó¯––JJJ„i¡¡¡hÙ²¥0ZŸ>} ££SéV`ÆcŒ59ØÎÎNj¼•ˆˆDDD¼ÖXcŒ±ú¤¡¡??¿úCéE$‹aee… . %%VVVHOO¯t©KSSÂô¯¿þŠ_ý-[¶TzŒ)))000€††Æ+-[[Û:«[]ªæÉ*¯XV\\,ìƒ2QQQ5Žj] ‘––öÊw)²233Q\\\m¿’ºjãÔÔTèèèHݾ\±Þëjãªb”—¢û¨¦m=þ***R£ —©j•Ý0Q¾†¬ÏmÅm?~ü¦¦¦¯åzmÚXÑåëóxQqåææâùóçuÞ'LYŸE(ÒÆò[”ÕÆË pëÖ-¤¤¤ÈûZý Aôú}ðÁÂàyß|óð À2:u¢Í›7 ÓÁÁÁüZbÛ²eKO®Ž"qÊS·º:UÍ“U^±ìÅ‹´zõj©²~ýúÕO]HNN¦~øá•—Wdýý÷ßZmºjã_~ù…îß¿_m½×ÕÆ²¶­E÷QMÛ ¥¿ÿþ[漪öÑ™3g„AGËÈúÜVÜö‡~Hááá5Æ\j{\ú¯/*î£Ë—/ t*›¬Ï"icyŽ-ÊjãŠeÉÉÉdffVm, E£J€fÏžM#GŽ$"¢ß~û¥æѹsç„é×™={ö¬Ú' ×$99¹NëVW§ªy²Ê+–Wz ôëj㢢¢*GΖ‡"û(''§ÒH¬ÕU¿xñ¢Ò(Ìë½®6–µmE(ºjÚVff&åääÈœWÕ>ÊÊÊFL/#ës[qÛ?üðC­Þ»"j»ÿÊñ¢â>Š¥-[¶ÔO]õ9P„"m,ϱEYm\±¬1%@ ¶taa!F…ãÇ#&&¿ýö6mÚ„!C†ú÷ïôôt|ÿý÷xòä –-[†&MšÔøle144Tø‰Ëå•õ³.êVW§ªy²Ê+–I$…\Y—TTTd>ñ\^Šì#---a¸…ªÔUTºì¢Èç¡®Õfۊ¶¥««+ui°¼ªöQÙHÄåÉúÜþWÛXÑåëóx!Ï÷HYd}¡HËslQV״Áö*{ªùüù󑘘KKK¬Y³þþþuuuœ:u ³fÍÂòåËѶm[?~¼ÊG40å4hP}‡Ðàq+_Ïž=ùIÛJfll\o¤²†§A'@ß~ûmµuÚ´iƒ'N¼¦ˆXU.]º„¶mÛÖw ·±òýóÏ?044|åXÍ233qïÞ=´hÑ¢¾Ca @ƒ±¶ÊžËÏ‹eŒ•¹qãîܹSßa0öJ$‰ð°WYRRRàêêÚ(îk°g€cLöîÝ‹'NÀÅÅ¥¾CaL!%%%Ø»woµ Pc «w)))¶ÞëÂm\·† ‚   úƒ1…äççcïÞ½õÆ£ÁÞÆþ;`ÇŽxòä lmm±cǤ¦¦¢_¿~øì³Ï ¿¼è1wî\tïÞM›6ÅÞ½{‘››‹Õ«WöìÙƒÿýïHHH@ëÖ­±hÑ"ØØØrrr°zõjœ?yyypppÀ‡~(´Ãž={ðË/¿ %%æææ0`ÆX¶l „®]» °téRœ>}êêêxçw0mÚ4ˆD"À®]»˜˜wwwlܸééé;v,† 55µšw,«_cõŽû§(·1«Î’%KpîÜ9œ8qBæüÈÈH¸»»ãñãǘ>}:Š‹‹áëë[mÂãÆÃœ9sЬY3Œ;éééXµj`óæÍxï½÷`hhˆ€€\¹rnnnB¤´´4lÛ¶ ½zõ‚¶¶6FŒ .ÀÛÛ½{÷†™™ „+V`Ù²eÂ6/_¾ŒyóæaÙ²eðöö†­­-.\ˆàà`¡Î¾}û0gμýöÛˆˆˆÀ³gÏ_~ù%áááyóæ!33;wFNN`ìØ±Øµk†މ'B$aëÖ­€mÛ¶aôèÑèÔ©>ùä´jÕ +W®¶¹{÷nDEE Ó~~~ ¿¿?:vìˆàà`ó¯\¹‚àà`|òÉ'ððð@×®]1iÒ$ìy§ bU ¦àààúƒ1ö™;w.-]ºô•—ÿá"@ù/3³šc騱£ð^>øàjß¾=•––Ò±cÇH]]]¨7bÄj×®Ô²ƒ ¢:T¹îË—/úý÷ߥʟ={F%%%djjJ³gÏÊ‹‹‹I__Ÿ,X@DDׯ_'&ÔÙµk ääd¡låÊ•äææ&LO™2…¼½½©´´T( $###aºE‹4pà@ÊÎÎÊHMMŽ=*”åç瓎Žíرƒˆˆ hÕªURï§  €ˆˆFMÝ»w—9ˆÈÜÜœ6oÞLDDGŽ!t÷î]aþ¾}ûH$уˆˆhúôéäîîNEEEB©S§ÒàÁƒéUåååIíWY’““ÉLžOÀ—Àcì5êÜøÿ«-J¥­­Xý/¿üØ·otuu¥æ]»v ýû÷—*ëÑ£fÍš…ââb¨¨Tþ)¹rå 444УG©rCCCÄÆÆ"55Ý»wÊ% |||põêU©úFFFÂÿMMMÆÆÆB™••"##¥–ÑÑÑ.%@çαråJ¤¦¦ ëèܹ3´Ë5RXXŠ‹‹±uëVlÛ¶M(×ÐÐÀÇ}ûöEpp0"""йsgôë×Oˆïí·ßÆûï¿ wïÞèÕ«ZµjU©]€—íijjŠÖ­[ e¾¾¾‰D¸~ý:Rmkccƒ3gÎÈ\'S'@¬Þqÿåã6~s¸¸¼|½i¬¬¬0uêT,X°@¸LU&++ fffRe&&&(**B~~¾ÌνÙÙÙ000€––V¥yYYY sáááUÆX>©)#‘Hª~SÿOSSPRRReÌÌLH$Œ1Bª|øðáBB²cÇìØ±{öìA`` ¦M›†7bäÈ‘1bš7oŽuëÖaݺu˜9s&&L˜€Ÿþ¹Ò¶²²²„D¬Œ®®.444„¶‘Ež÷ÊäÇ}€X½ãþ)ÊÇmÌä1oÞ<¤¤¤`Ë–-R厎Ž8{ö¬TÙ©S§`aaQåM-[¶DJJ îÞ½[iž½½=Äb±Ìu:99Õê=ÈòçŸÂÈÈæææUÖiÕªŠŠŠ`oo©WÙ™±XŒ±cÇâ?þ@RR† †åË— ëðòòÂÎ;ñðáCìÙ³›6mÂãÇ+mËÑÑ÷îÝCZZšPö÷ß#77W)ïŸÉÆ P 22 ˜XßQ4l<<<°qãF/û½÷Þ{(((€ŠŠ ôõõ±}ûv™ý£ pèÐ!¼ÿþûضmJJJàææ†}ûöñe®×HDDTßA¼©.\ˆ/¿\++àÑ#@ÆågV¸Šòq×    ((¨¾Cyí [[[©Ä5ÉÌÌDFF,,,*ýÀ§§§#992“EM:>ÄÑ£G‘œœ 333…ÇÍ)**B\\ŒŒŒ`hh(5/77 ÐÓÓ«ô*..FBBˆVVVPUU­q[111PWW‡¥¥¥B1¾Šüü| ??¿Ê:)))puumÏäK`rHHx9z+S|ÜÆ¬.¨««£uëÖ %? §§‡fÍšÉ<»a``ggç:I~ÊSQQA³fÍ^iÐ@UUU888TJ~€—g¶eþA¡¢¢[[[ØÙÙÉ•ü@óæÍ_KòÃ*ãHN»w×w ÷OQ>ncÖXXYY¡E‹õûà>@rúí7`Í@Ì)#cŒ½±æÍ›Wß!°ÿþ9—Sr2pñb}GÑ05†kÍõÛ˜1Ƥq¤¾ ¦Ü?Eù¸™¢ÒÓÓ«Mœ“’’‘‘¡Ð:Ë:W7 aEqqqÕvÚeìUq¤€}û€ÒÒúŽ¢ááþ)ÊÇmÌõí·ßbÈ!ÂôÌ™31wî\aºW¯^ذaƒBëŒŠŠ‚å\­¨¨vvv¸$ã.”›7obÔ¨Q mŸ±ò¸P ÔÔ QXøò.‚'O€sç€r¯aŒ±FÁÚÚú•kááá8sæ Ö¯_¯ð(ÆÊ㨎Žp÷î¿îÙ½› ºÆcÔ(·ñ›#«0 ÏrŸ)};±ÍôšUGVž=«‡¾¾>š4iR©|ôèÑ˸ $''ׯ_‡D"A‡äN’ÊÆÌiÚ´)444„òÒÒRglèС:t(6lØ€%K–Ô¸ƪÂ}€j`o=½§Ÿ>NŸ®¿x"þaV>ncVѤI“žž.¼fΜ kkkÌœ9SîuäääàäÉ“ÈÌÌDhh(bbb°nÝ:¹–UUUÅ|€” AHH†*ó,cu‰ H$Åx÷]é²={ê'ÆS†Ã‡cÅŠØ¿?Œå^Îßß_xn–ŸŸ|}}qìØ1¹—ÿè£#<>55GŽᄽ| LC‡;vü;}àðÃ@ÜÞhqÿåã6~sèªëÂFßFéÛ1Ñ6‘«Þƒ0zôhlذµÚf»vípýúu¹ëÛØØÀÏÏ›6mB÷îݱ}ûv8;;ÃÛÛ»Vq0&þ —Ã[o@ÙÑŸ=Nžüüê7®†‚û§(·ñ›c”Ë(Œry3nßÎÊÊÂÀ1zôhŒ7®Öë Wø¹V“&MÂСCñüùs„„„àã?®uŒÉƒ/ÉAM ð÷—.ãË`u‡˜•Û˜UDD7nŒŒŒ°zõêZ¯¯¨¨§N‚¯¯¯BËõíÛ&&&˜2e ðþûï×:ÆäÁg€ä4t(°uë¿Ó?þÈùÀ_Æ{£üüóÏØ¿?üýý1}út¡¼}ûö˜8q¢\ëøá‡ðüùsÀŽ; ««‹9sæ(‡D"Á‡~ˆ/¾ø}ôtuu«­ŠsçÎáÖ­[ÈÌÌDPPÔÔÔ°hÑ"…¶Ë'@rêÕ 04þÿ»Ž/€?ÿúõ«ß¸|ÜÆ¬¢-Z 88¸R¹ GROU2dTËýÅ7uêTèëë#,, ±±±˜4ifÍšUí-ð&&&®TçÃ?DIII¥‘% ‚ƒƒagg'”©¨¨@CCÞÞÞB_¡7a€Föß#¢ò÷2) .”úwâD $äßùcÆÛ¶½þ¸š7ò%%ã6®;AAA000»†±ÿ‚üü|Tûlµ””¸ºº6Š(s  *=}èPPP?±4$üì|ÜÆŒ1& ôè”"##8~¼þâaŒ1ÆØ«áH**À AÒe|7Xí5†S­õÛ˜1Ƥq¤ a䧪¹œÊäpðàÁú¡Áã6fŒ1iœ)ÈÇ05ýw:+  ­¿x|ÜÆLQ+W®Ä!C„é­[·b×®]ÂtïÞ½±aÃ…Ö [[[$&&ÊU¿¸¸¶¶¶¸té’Pv÷î]|ûí·˜6mfΜ‰ÿýï(**R(ÆN€&‘ï½']Æ—Ác Í‹/¤.ž>}çΦ‘‘‘¡Ð: ’’¹êâãã…»–JKKáââ‚Ý»w£¤¤ÏŸ?ǤI“¤5ÆäÅ㽂aÃ^> ¬Ìï¿yy€¦fýÅô_ÆcÔ(·1«­íÛ·W9¯°°b±*J~@¢X,Æ¥K—¤ž¶{÷n >œ?ãLa|ètí ˜›ÿ;“üñGýÅó_ÇýS”Ûø ²k`k«ü—§gµalÙ²¶¶¶•^ß|óÌúÓ§OÇìÙ³¥Ê¢££Ñ·o_èééAWW3fÌ@ii©\Í››‹V­Zá—_~‘*¿yó&š7oޤ¤$™ËU|Pj||<,--ab"ßÃ_+Ãg€^X  ¬[÷oÙîÝ/˘â¸Šòq¿A²²€øxåo§†»3 777aúСCX¶lz÷î-³~jj*´µµ¥Ê:„ `ñâÅØ»w/Ö¬YŒ3¦Æð´´´àåå… 6`ĈBù¦M›Ð¢E XXXTÛ·çÿûΞ=‹¿ÿþ7n„D"©q›Œ•Çg€^QÅA}y&ˆ1Æþ Œáêê WWWhhh`ÕªUøñÇáîî.÷:>ùä̘1X¶lºuë†;wʽü¤I“pñâEDDDòòòðË/¿È•°oÚ´ ÇŽƒ¡¡¡ðøÆÁ Ð+êܰ´üw:78r¤þâù/ã1j”Û˜U%++ ÄØ±cå:sS: &&Fîú^^^puuŦM›ûö탦¦&Þ}÷Ý—=}ú4=z„÷ß}úôABBÂ+ÇÍ'N€^‘HT¼ñ`÷îú‰å¿Žû§(·ñdÔ( 6Vù¯k×j …ˆ0f̘˜˜`åÊ•µ~kÏŸ?¯ñiîMš4 Û·oGaa!BBB0aÂ…:S>¹¹¹R·Ê3&îT C‡kÖü;úòò¾‚ßÿFû§(·ñDW÷9H,Y²×®]Ã7¤žôþªNœ8nݺ)´Ì¨Q£0{öl¬Zµ /^Ķjž0]XXXéÉï‘‘‘333Åf'@µÐ±#`m >>B'gWWWx{{#$$ñññ044DŸ>}°xñb¥AÄQ}ñ¦Z¸p¡Ô¿²\½ xyý;­®¤¦zzÊ­!Ù¸q#_¢Q2n㺅Ït0Vßòóóa`` Œ¬-KJJ \]]ÅÜ º–:tììþ.(¸¿©bø‡Yù¸cL'@u âÝ`ül0ÆcìÍÆ P6LzúÄ =½~bù/j §Zë·1cŒIkÐ ÐÚµkÑ¥KØÛÛcìØ±øóÏ?¥æ=~~~R¯Ó§O+¼ww E‹§ j}ãÁcÔ(·1cŒIkРЃ0wî\=z;v„¿¿?ž={&Ì?sæ |||0iÒ$áåèèøJÛªøh ¾ &?|ÜÆLQû÷ïÇ×_-L‡……áÎ;ÂôüùóqDÁáï“““1nÜ8©ãpuJJJ0nÜ8Ü¿¿Ò¼ââbܾ}¿þú+®É1è#c5èhÆ 0`1yòd899aß¾}Ruºví áeeeõJÛªxìäIàùóWœ1Æê×Í›7*LϘ1sçΦ8 •É#==Û¶mCŽœN,--ŶmÛœœ,U¾gÏ¡[·nÆ’%KŠƒ1 ”——‡ÈÈÈJƒl>|÷î݃££#|}}!‰^iýíÚŽŽÀÿJŠâb`ÿ~`âÄÚFÞðñ5ÊÇmÌjëÀ¯||¬KÛ¶môiÓ‚¡ÿ꽺§Æ3V•}¨¼)S¦ÀÍÍ }úôÊÞzë-dggãÊ•+=z4úôéƒÂÂB©å¶nÝŠ… J½Ê+?ýò,пÓ{öTCˆ§+O—ïŸò&ÄÓ§ËÚøM‰§¡L¿Š3éé¡ô׌¨¨jã8qâÆWéµwï^™õwïÞýû÷K•`ùòåèÞ½;zõêUi~urssñÁàÊ•+Rå 7n\¥³>ÀËGa|þùçX´h‘ü¨“Çx4FeŸç#GŽ`áÂ…ðõõÅ·ß~ÛhÊF1â¼yópèÐ!œ={¦¦¦2뤧§ÃÞÞ;wŸùB,ïî]ÀÅåßi‰HIŒk=cìMRÛ7&%arÙ©b%2SSCJ5´ ÃÙ³g…鈈üøãØ»w/Œ àܹs¸pá`ذaÐÖÖÆæÍ›­ZµBTTúõë‡.]º`ïÞ½¸zõ*Ξ=[åóÀîß¿V­Z!>>ÖÖÖèÓ§ôõõ±§\§ÉÅ‹c÷îݸ{÷.ŠŠŠ ¦¦†S§N¡G8yò$Þzë-ܾ}×®]Cjj*ðÎ;ïðHÐrà¥5èOLii)fÍš…¿þú çÏŸ‡q5™ˆ,--‘––öÊÛkÓhÕ 9]RìÛ¼ò*cL)\]]áêê ÈÌÌD‡ðñÇcðàÁr¯#00K—.þïåå…uëÖÉý@ÔI“&aøðáxúô)LLL@DزeK¥Gb”‰‡X,Fÿþýáîî‰D‚eË–ÁÁÁgÏž…–––ܱ3Ö`/•””`РAˆÅÙ³g+%?QQQ8þ¼0Ѝ¨(téÒ¥VÛå»Á×þÒ¨oÜÆ¬*D„Ñ£GÃÌÌ +Ë?ØPzžùÓ±cG„—ý(‡ÀØØÛ·oœ>}Ož<Á˜1cdÖOMM…™™"##±ÿ~ìÝ»7nÜ@DDÖ¯_¯PìŒ5Ø3@YYY8tè@WWW(wrrBDD²²²0dÈäååÁÀÀ¹¹¹Ø¼y3lmmkµÝ¡CòWÌΞ<ÌÌjµÚíàÁƒ|›¶’q¿9º4aÏ IDAT`KË–Jߎ¦X¾¿o¿úê+ܼy7nܨõe$---Ë]_EE'NĦM›ˆ >úúú2ë[XX 33jjjBYóæÍáææ&õ@WÆäÑ` T×½ÉÍÍ )))HNNF~~>lll ‘Hj½]gç—ý€Êî-» 6eJ­WÝ`ñ³òq¿9œ´´àô†\ªùã?°téÒjûG*âÔ©ShÕª•BËLœ8K–,Áï¿ÿŽH™¯ÈÑÑÙÙÙˆ‰‰AóæÍ¼<ÛïÞ=xzzÖ*vÖø4ØK`ò‰D°°°@óæÍë$ù)×Ácoº¨¨(¼ÿþûX°`¬­­‘’’‚””dddȽŽÈÈHdgg#==ß}÷nܸiÓ¦)G³fÍЯ_?Œ?­Zµª6‘ñööF×®]1iÒ$ddd ==K—.EFFF•—Í«J£N€”¥btá ãŽNöÿ¸Šòq³Š6oÞŒôôtÌŸ?æææÂ+@»6.\¸CCC˜˜˜`îܹX³f zöì©p,xöì™\g*·mÛ†ÜÜ\ÁÌÌ ëÖ­Ãÿþ÷?´mÛVáí²Æ­QÜÿª½ ¾<77 ,ìßéᆭO¯›¸š7ò%%ã6®;µ½ ¾¡IOOÇãÇáàà ׶ݲ³Uöööuz¿!ãÛà¥ñ %©xh÷îú‰ã¿€˜•Û˜)‹\\\^kòM›6…““'?ì•q¤$ K—€„„ú‰…1ÆcÒ8R’-§‰€ß~«¿xÞdáTk}ã6fŒ1iœ)QÅ'Äóe0ÙÊ? Œ)·1cŒIãH‰† ‘ž¾rxô¨~by“qÿåã6fŠºuëBCC«œðàAÜ)ðLNغu+rrräªODغu«Ì£fffVÛ™—±šp¤D¶¶@‡ÿNU>^¡88R:¾ V3|ÜÆ¬¶‚ƒƒëýÖÿèèh¼ýöÛðõõE|| ãM1dðé§/ÏþÀµk@l,`gW¿q½Iø9UÊÇmüæÈ}‹Ì¿3•¾±¦¦Ãª~¼Åýû÷qåÊ•Jå...ð(Çÿ“H$2o9¿rå Ž;‰D‚#F E‹rÅ———‡Ý»w£W¯^°²²ʳ³³ñÛo¿¡ÿþ•ž ¶mÛ6ˆD"¬X±êêê€M›6ÁÉÉ —.]B§NäÚ6c'@J׬àíýò6ø2{ösçÖ_LoþaV>nã7Gú™tDNŽTúvÔÌÔªM€¢££¥:ÇgffâÌ™3X¹r¥ÌhÆ ÐÖÖFçÎ…²Ÿ~ú ëÖ­ƒ‡‡Μ9ƒÕ«WãÚµkÂsºª£©©‰U«VáÖ­[X»v­Pþ믿bþüù5jT¥eѶm[!ù^>LWW<àˆ)„/½ül0ÆØ›¦ÿþ8xð <ˆ@GG¾¾¾øøãå^‡——bbbðûï¿#..Mš4Á·ß~+÷òعs§Tgæ|ðÁPUU­TßÙÙwîÜAB…AÕŒŒŒ*•1VN€^ƒÁƒ‘èßé›7¨¨ú‹çMÃýS”Û˜UgÑ¢E Þ={ ¢"ÿ…víÚ #@£k×®¸zõªÜË=Ø¿? <<W¯^ÅG}$³~@@lllàé鉀€ÀÙÙ=‚™™™ÜÛe àK`¯…¥%Ð¥Ëˇ¢þ{wUuÿü5û–Lv[Øw° ¢(¨ Å¥Š¥Š”**îKEV¬µ¥V[­Ô¯ÚBq¡b­­XlÑ"(¸€ ‚,ʾ‚!ûžÌþûc É„$$0gî,ŸçãqÉ=³ÜÃ!„ÏÜû¾ç÷Ö[ðË_j×§h"ùõdŒ£‡½¿ì²•ǘҶ_ïË–-ã©§žâÓO?%++ë´ŽÙ³gO>þøã6?ßétrÝu×ñòË/3mÚ4.\È%—\BÏB’ÉÉÉ|ùå—¼öÚkìØ±ƒŽ;rÛm·qÎ9ç´ø!Z"P„\s@-‘ÿ˜Õ“1Ž©ãRI—ªu7ؽ{7Ó§OgÞ¼yœuÖY§ý~k×®¥oß¾ízÍí·ßÎÈ‘#Ù¾};¯½ö/½ôR«Ï·Ûí!+Ö¿ñƤ§§3f̘Sê³H\r ,B&O}£ÑÞ¼vîÔ®?BˆÄVUUÅĉ™6m7Þxã)½‡Ûí®ÿ~Ë–-|öÙg\}õÕíz#F0bÄ®»î:, W^ye›_ûÅ_0kÖ,fÏžÍfk×q…(B²³á‚ BÛ$ $ùõdŒESÿ÷ÿǶmÛøàƒ0`@ývÿý÷·ù=üqFŽÉÈ‘#ùÁ~Àå—_ÎÌ™3ÛÝ—Ûn»Í›73sæÌ“®î~ýõ×sá…2pà@ÆÇìÙ³™5kV»)„\‹ k®Æ—Çßz }T³îD ɧ¨'c,šš>}:çŸþ í:o¿ùæ›™ ><¤Ýh4²zõj† VßvóÍ7³mÛ6úõëǰaÃÈÈÈhóŸYˆÆtÀñ)úDSsæÌ ùzº  sgðùÚ¶n…AƒÂòöBˆ˜={6©©©šÏŠ,D{ÕÕÕ‘ššÚêjùùù 6,!ÎË%°êК~@’Ë`B!DäIaMדHò)‘ c,„¡¤Š°I“ ñihMÆX=É©' NRilÂp:ökj‚EQMv}Š4ɧ¨'c¬žd€Ô“ ')€4–”/¼Ú¶mÜs6ýÑ‚LЧžŒ±z“'O&##CënĵÎ;sÕUWiÝ '¤ŠS§Â-·„¶ýíoðhÓ!„"ÞI%ž{† m»ãعS›þD’äSÔ“1VO2@êIH„“@QÂf ÎÔx¡ªªàÊño“G’OQOÆX=É©' NRE‘`޼ж͛aÖ,mú)’OQOÆX=É©' NRE™nn-X¸³D !„*H…æÍ ž jì–[‚ ¦Æ#ɧ¨'c¬žd€Ô“ ')€¢Ã<ãc³5´UTó@n·výREò)êÉ«' õ$$ÂI  (5thðΰÆ6l€Ô¦?*I>E=cõ$¤žd€D8IÅn¹%8GPcÏ?òa^!„8=RE¹_„>}BÛnº Ô¦?*H>E=cõ$¤žd€D8Iå’“ƒy ‹¥¡­´4xfÈãÑ®_á$ùõdŒÕ“ z’á$P >æÎ mûòKøå/µéO¸I>E=cõ$¤žd€D8I#î¾&M mûÓŸàý÷µéBˤŠ! B û\=äåiÖ¥°|Šz2ÆêIH=ɉp’(†¤¦Â›o‚ÉÔÐV\ ×]>Ÿvý:]’OQOÆX=É©' NRŘ³Ï†'Ÿ mûì3øÍo´éO8H>E=cõ$¤žd€D8IƒfÍ‚+¯ mûÃ`åJmú#„BÄ)€bN‹A×® m~?LŸ±õ|Šz2ÆêIH=ɉp’(FedÀâÅ`46´ÀO,†b‰äSÔ“1VO2@êIH„“@1lôhxì±Ð¶U«àñǵéÏ©’|Šz2ÆêIH=ɉp’(ÆÍž —\ÚöØcðñÇštG!„ˆ RÅ8^{ :ujhóù‚— µëW{H>E=cõ$¤žd€D8I:t€×_}£¿Í#Gàg? N–í$Ÿ¢žŒ±z’RO2@"œâºzî¹ç3f }úôá†n`ÅŠ!ïܹ“ &Я_?&MšÄÁ^b}Ü8øõ¯CÛ>øžzJ›þ´‡äSÔ“1VO2@êIH„S\@;wî䡇âý÷ßgÔ¨QLœ8‘ââb<]tãÆcåÊ• <˜K.¹„@,œ2iÁ£ ¡¦mk×jÓ!„"ZÅu4þüú3uz}ðRX‡ m^op©Œh¾l.ùõdŒÕ“ z’á×PcµµµìÚµ‹œœvíÚÅàÁƒë×ëõ 4ˆ;wjÕŰèÔ)ŠÖéÚrsaÆ ÍºtR’OQOÆX=É©' N SÝyç >œK/½€ÂÂBìv{Ès’““)lrëÔ¢E‹˜3gNÈÖX4î_rIðöøc-ü÷¿ðÌ3ÑÑ¿¦ûó)ÑПxÜ?>ÆÑÒŸxÜŸŸI“&±ÿ~>þøc233C¿êª«øî»ïØ´iŸ}öeee\|ñÅZtW‰®]ƒ‹¦6Îíß3gjÖ¥fI>E=cõ$¤žd€D8Åí%°²²2ÒÒÒNhïß¿?;vìàÿøwÞy']ºt¡°°W^y… &Ô?7–/5öÀÁüOcù Üu—6ýBé˜ñäO‰}ôuuu\pÁ$''Ÿôù©©©'Ógúôé\{íµäææÒ½{w C¸ºUž|2˜ýY·®¡íà¼ó`øpíú%„Bh%f.UVVò‡?üÎ;sî¹çòÈ#ÔE§Ãd2Ñ«W¯¸-~ ~~óMHMmhs¹àšk ²R»~—Ÿ4´&c¬žd€Ô“ §˜)€&NœÈš5k(..æ™gžÁn·3uêTRÿ¯.ZÔ£,\Ú¶g»áMS’OQOÆX=É©' N1•ª­­åÿûK–,aõêÕ\pÁ\sÍ5Lš4IÉñâ%ÔØ=÷ó?½ø"Ür‹6ýB=)3g€^zé%²²²X´h?ùÉOسg‹/VVüÄ«¹sOÌýüüçðí·ÚôG!„ÐBÌ@£Fâúë¯gÇŽ¼óÎ;,_¾œÚÚZ­»s,xë-hœ#¯­ 檫µéS"|ÒКŒ±z’RO2@"œb¦:t(óçÏgÇŽÜqÇ|òÉ' 8k®¹Fë®Åœ>}‚—½Û±C»Ûâ%Ÿ¢žŒ±z’RO2@"œbæ6x€Ý»w³fÍ>ûì3Ö¬YCMM ºÆ³ü‰6›:V­‚—^jh{õU7n¸!²}i¼˜PCÆX½É“'kÝ…¸×¹sçµÀ„81sèÑGeøðá¼õÖ[ <˜·Þz‹£Gòæ›ojݵ˜õÜs0thhÛ]wÏ !„ñ,f  »îº‹ÒÒRþ÷¿ÿñÀ0lØ09ûsšl¶`Èáhh«®æ"¯’|Šz2ÆêIH=ɉpŠ™(;;›ÊÊJž|òIn¾ùfxà>üðC­»ó €yóBÛ¾ý6xgX¤H>E=cõ$¤žd€D8ÅLtøðaÌ’%KHKKãÀL™2…'žxBë®Å¼n81÷óÒKðÆ‘9¾äSÔ“1VoòäÉdddhݸ& N1‚ž;w.&LàÅF·/mݺ•3Ï<“x«Õªaïbß¼yðÕW¡ùŸ[n£¦LÑ®_B!„ 1s(//‹/¾8¤mРAdffòý÷ßkÔ«øápó@6[CÛñ<Ð/~>ŸºcK>E=cõ$¤žd€D8ÅL4bÄæÏŸOQQ^¯—E‹áóùèÞ½»Æ½‹C‡ÂóÏŸØþôÓpé¥plèÃNò)êÉ«' õ$$Â)fÖs»ÝLš4‰÷ߟAƒ‘››‹Édâ7Þ`üøñJŽkµÅ_þ÷ßOh{N¼óŒ¡M¿„B¨•HkÅLÈl6³lÙ26mÚÄîÝ»IMMåì³Ï&%%Eë®Å»ï†aÂٟÆÿ‚1c௅34ëžBqÚbæØSO=ÅìÙ³6lS¦LaüøñRü(4f lØçÚ^W7Þœ0±é¢S•Ÿ4´&c¬žd€Ô“ §˜)€ÒÓÓ)((к ¥sgøøc¸óΛ?ÆŽ…päÏ%Ÿ¢žŒ±z’RO2@"œb&TUUÅ…^ÈŠ+"6×F¢f€š³hÜqGð PcÙÙðöÛ0z´&ÝBF‰”Š™3@úÓŸøî»ïÈÉÉ!555dê͘k×BÓîòóƒ ¨6MZ!„ˆf1‚¾óÎ;¹öÚk59vEiÎ4)´Î<3˜ š:¯BâñƒÓëÖÁ /@{ç¤ÌÏÏ';;;¼!dŒÕ+..&%%£1f~­Æ·ÛMUUéééZwEĨ>TWWWxËÊÊbÀ€Ín* Zp>›þo¥Çˆ%°|9<ôЉýýïÁKa¶ï=%Ÿ¢žŒ±z’RO2@"œ¢º*++cÀ€L:••+Wâ÷û#Þ‡G d~×%âÇf<ùd0û“”úØ7ßç Z¹²íï'ëT©'c¬ž¬¦ž¬&Â)ª  ììlöïßÏå—_Îï~÷;zõêÅo~ó8Ñ~t=dæãw>Šè1cÁÕW/{õïÚ^\ —_O=¥M¿„Bˆ“‰êÀáppà 7ðé§Ÿ²råJ<çŸ>?üáùç?ÿI]ÓÛ’ÙüÊ¡ˆ'Ö ,‚&N m÷ù`öl˜<ªªZD¸Û@k2ÆêÉ<@êÉ<@"œ¢¾j¬oß¾üþ÷¿çÀÌš5‹%K–н{wîºë.6mÚ¤ôØéßvVúþ±Ìé .‘ñøã oòµd œsìÚÕòë%Ÿ¢žŒ±z’RO2@"œbf –òÚk¯±uëV.\Ö÷ž3gc;¶~_ÿïLÖcÄ›>€iÓ é‡4§^{ äò½BD/™( íܹ“çž{€šš~øaxàü~?÷ߨ‹Ÿæ|ó·ýÊë.½Ö¯®%ÖXEEð2Ù£‚Yv!„"DÌ@¯¼ò EEEüõ¯åƒ>Àh4ò³Ÿý,b}ÈøVîk‹ž=áóÏaúôÐö@ x™ìÊ+¡´´¡=>ihMÆX=É©' N1SE=cõ$¤žd€D8ÅLÔ¡CvìØÁÑ£GÙ´igŸ}6TTT(;îÞ5!û_¿¼WÙ±âÑ=÷ÀªUÁ5ÃÛ»Î=ÞxC樉cõd õd N1SÍœ9“G}”0yòdºuëÆúõëq»ÝJ§ø?Ò+ôö¥Ô-”+^\BãÜsCÛkj‚éûï·[›¾ !„HL1S :”mÛ¶ñþûï³hÑ" ÿþ÷¿Ñét­¿ø4Ôö?r¬û~+_.—S°íÕ¹3|òIpEù¦þïÿòq:ƒ3Hß|3üùϰf TVF¾ŸñJ2@êIH=ɉpŠ™ÀãñðùçŸóôÓOàr¹(mœ¦UÀ˜ná`¯Ú¶¯^Ø©ô˜ñÊd‚ùóá•Wš.˜º—+¸ŒÆßþ÷Þ çŸ))ЧL™O<ï½yyZõ>¶IH=É©' N13ÐÆ?~<_|1›7ofÇŽìÙ³‡óÎ;£G*9 4gÎÒ¿ÍáŒwzÕ·ìUÇ {/ ûñÉ7ßÀ¤Ií_8 ++x›ý°a0|xðk¿~Á5Ê„Bœ:™( ÍŸ?ŸÙ³góæ›oÖ·õéÓ³Ù¬ü/êœ[„\ËÙoåë¾PzÌxwæ™Á\дiÐ¥³ [}úéàë ‚ääàŒÓ·ß À—_3FB!DsŒZw ­***0`@³Y,¥Ç>çÒóxµçrºï ^·Ñà‹ùÛyñ¹'y¥hMF¼þzð‡Ñ˜ÍưiSösgpM±¶¨­ ®I¶n]C›^<3ÔølÑСÐ)sìùùùJoÁ PJJ FcÌüZ9n·›ªª*ÒÓӵ1shôèѼøâ‹!sþ¼üòˤ¦¦FäCÙ¡áçæü_T‘¥K—’™ ãÇË¢­[ƒ!课‚^†§Ï=޶¿¯ß;vÀâÅÁ…Y/½4Æv:ƒóýô§ðØcðæ›Á‚KátRš“ z’RO2@"œb&äóùøÙÏ~ÆòåË©««#;;›ÚÚZÞyçÎmzu˜ÏÍ™3‡/Þ_ƒëІ;<:HþÐÆY£äØ¢y~?ìÞz¦hãF8zôôß[§ ^Žëß?xæ¨ÿ†ï{ô8q¡W!„ˆ7‰”Š™sµƒþóŸlß¾mÛ¶‘ššÊÈ‘#q:9þ¹?¼ ¶¿á2ØÚùßIaz}Caríµ íùù¡ѦM°gOûÖ àðáàöÑG¡Y,Á;Òš+Ždî;!„ˆ=1S½òÊ+tíÚ•ñãÇ3pà@S¦LañâÅ"p Pé#tßßp7˜ssgåÇLáȧdgÃe—·ãª«ƒËm/Š6o^;•‰Ã]®àe¹­[O|,##´ :þ}ïÞÁÂ)HH=É©' N1ó/uçÎ'Üê®ÓéX·n¥¥¥dff*ïÃÙ·öÁý߆S =öÚØøÉ× ¿p¤òcdz¥K—*YªÁá憚^!Íχ]»‚!ëãÛ®]°oœÊ}úhÚŸ3oêÿ¿ û=÷ØÙ¼ö~0úLí:ãb!Ÿ’’gŸÜ  7·ùâèСàã§Êå‚ýûƒÛÉúÖRqtü{ȧ[·èãX' õ$$Â)fþ¥îÝ»‹ÅB¯^½Ðkx?òÇñZ÷èv0xL€Ož—ètÄr>E§ƒœœà6~|ècuuÁ3DÍGeeáëCyypÛ¾½µg-Ål¾› å›Õ Fcpi’Æ[¼“ z’á3Pjj*³fÍâ¾ûîã®»îâæ›o&--M“¾É£ÛÁ†»Á’6ÉÝ`§#V‹Ÿ“±Zƒ3OzâcÁ¼Q^9râ–—,jÂçvÜnp»Ãý¾m§×7_ -··öX[Ûõú7®õýSk›Ì믇7ß·ôXÓå›[1Ï9Ý×´ôý©_èÿVãÉ©S¬$$N_ÔÏtÜ믿ÎwÞIß¾}¹ûî»™:u*V«µþñ@ @yy9©©©a;fsó÷Ñ›+0L5×ïûõÐñ‹tŸ}FØŽ/DþM·æÚ[j'r:¢¼ü)­»¡\ÌœJKKãƒ>`Ô¨QÍ>®ÓéÂZüœÌÅ×^Âë® Kn°ÒûaÕ³ëüO)€„hN¢Ü &bOk…R¢9zô(çœó@  ¨ñ£ý€ÒÒRþú׿²~ýz.¹äM/ΣKnÃe0ÛFÉXœ É©'c¬žÌ¤žªy€‡ž½H·X@B\³&êo<|ø0gu:ubÁ‚<ôÐC¬ZµŠÁƒ3wî\žyæÍú6xzèíï½v%±sã6z»–.]ªu➌±z«W¯¦\«9DQQk֬Ѻ"ND}è`÷îÝ<øàƒ<ùä“lܸ‘C‡a4Ù°aS§Ne÷îÝJŽÝZè¸v]IçE=cõ$¤žd€D8E}è®»îbíÚÖ ŠM›6)9v[2@Ï^ºˆa+zÔïïTÉ-['(é“B¡J"e€¢þ~Íyóæ…å}Ö­[GII —]vYHûüù󩨨i›0aƒnó{÷Ÿš +ö{îJf÷·;è;tÀiõY!„jDý%°Óuøða.\ÈôéÓY¾|ù ÿþ÷¿ç›o¾!??¿~«««k×1.¿ñJŽvj¸öoôÊe°öH„OZ“1VO2@êIH„SÜ@EEEäææ¶:qÖ½÷Þ˳Ï>[¿1¢ÝÇù¾ÉÝ`¦ Y-'77—;wÒ­[7ìvû)§Ï”4XÙ°ßk§“;öÒc@ïSz¿D" ¡ª'c¬ÞäÉ“µîBÜëܹ3W]% ¡Šðˆû3@'“œœÌï~÷;.¿ür²²²xâ‰'B_´hsæÌ Ù;¾å-WQíe‹€àe°÷žZÝâóe_öe_öe_ö£aÙ²eÌ™3‡±cÇ2wî\<‰ êï —ûgŸ}¶Åç|ýõ×\pÁ¬^½šQ£FÕÿp4ý¡iɳ?|•au¯ßß9¤œÛ¾ýñ)÷9QÈ:UêÉ«'k©§j-0Ñ ‘îKø3@9’°sçÎSz}ï))¡û;R8´s8º×$Ÿ¢žŒ±z’RO2@"œº*..¹>77—Ý»w·ëøÆ&Ü6‘‚Ž «è½°ì«N»ŸñNò)êÉ«7yòd222´îF\“ §¸/€Ö¬YÃŒ3X±b+V¬`ÆŒ|õÕWlÞ¼™œœ&L˜Àõ×_ψ#˜9s&guÖ)ïû!GBöõ_Ë/D!„"ÚÄýÅê>}ú0cÆ f̘QßÖ³gO.ºè"Ö®]ËæÍ›©««cÖ¬Y >ü´Ž×ý'vø¨a¿÷ŽTrw [ß§õ¾ñLò)êÉ«' õ$$Â)îÏegg3vìØ­C‡†ÛÌ´iӸ馛N»ø˜x×Õvh¸ fòÀÿøái¿o<“|Šz2ÆêIH=ɉpŠûH yƒ›\[—©QObƒäSÔ“1VO2@êIH„“@ t»Ê²ßkG*ùóZx¶B!"M ®¾o EY —ÁÌnø÷ïO\‡L%Â|Z“1VOÖSOÖá$"yƒ¿mX'¡½–H>E=cõ$¤žd€D8I¤H—+Í!û½¶¥’øH ÏNl’OQOÆX=É©' NR)2iÖdŠ3.ƒYÜ:–>ñ¾†=B!ÄqR)¢×ë9<$ô2˜ÿ«4zÝ$Ÿ¢žŒ±z’RO2@"œ¤R(ûŠÐ ÑzoË àˆüGÔ”äSÔ“1VO2@êIH„S¬*Ú»|S~¿Ÿw³>&­¤¡ÎÜy×>nûËMaèB^²¼ ½^On“Ë`¾/RZx¶B!"E Å:\nÙïµ-ƒâ£Eõ&:%Â' ­É«' õ$$ÂI Å®ùÅ5”¦7\e´ÖÁ’ÇßÕ°GÑGò)êÉ«' õ$$ÂI Åôz=‡š¬ æ‘Ë`!dŽõdŒÕ“y€Ô“y€D8IY—êBö{mË ´@Nã !„Z‘(¦<8™²Tý¾­VÇÛË`ÇI>E=cõ$¤žd€D8I&³™Cƒ†´¹×$kÔ›è#ùõdŒÕ“ z’á$P„d\êÙïµ-“²bù$’O‰cõ$¤žd€D8I!×<4…òÔ†»Ál5°ä÷ÿѰGB!Dâ’(BLf3…æ0j?shÔ›è"ùõdŒÕ“ z’á$P¥ýåØk[¥eõ&zH>E=cõ$¤žd€D8ÉZ`­8ݵÀšrÕÕñAÇÏqV4ÔûþßnzzFXÞ_!„8²˜PÂbµrpHAH[ͧrL!„ˆ4)€",åbOÈ~Ï­™TWViÔ›èŸ4´&c¬žd€Ô“ ')€"lÊCWSál¸êè¨ÖñÖãïhØ#íI>E=cõ$¤žd€D8Ia6‡ƒƒB'E¬úØ¢Qo¢ƒÌQ£žŒ±z2z2')€4|‘+d¿çöŽ L!„ˆ$)€4pí/§PÕh%Œ¤Jø×÷2˜äSÔ“1VO2@êIH„“@°9ìz7XåêĽ &ùõdŒÕ“ z’á$F’ÆÕ†ì÷ØÚÚêz£-ɧ¨'c¬žd€Ô“ ')€42å—WS•Ôp7Xr¥Žýa‰†=B!‡@q$'±PaH[Ù*“F½Ñ–äSÔ“1VO2@êIH„“@r\zÉ«ÇÖŽ¸êê4êv$Ÿ¢žŒ±z’RO2@"œd-°V„{-°¦*JËø¬ÛFÕºú¶Ü_çò³ßþLÉñ„BˆÖÈZ`""œi©TÒVº21/ƒ !„‘$ÆlT‡ìú:›¿”XaèDø¤¡5cõ$¤žd€D8I¤±«ù1) W!^¨ü½ƒ¢òÂV^_$Ÿ¢žŒ±z’RO2@"œ¤ÒXJZG§ iË9`eá´iԣȓ9jÔ“1VOæROæá$P¸eþ vžúÉqø‡ƒX°`žF=B!â›@Qâ‡/  ÚÑp)ÌìÓ3=ØV°MÃ^E†äSÔ“1VO2@êIH„“@Q¢÷¨þ]º>XïÝßü.uÞøžHò)êÉ«' õ$$ÂIæj…êy€š³pð{ôÞæ¨ß¯³ÁÇ¿_Æ“÷ÍX„B$&™HhæÂù}¨³6ì[k¡ç‹çóîÎwµë”Bg¤Š2}.ìOєҶþÛSøä_r¸â°F½R+>ihMÆX=É©' NRE¡Ÿ.šÈÁ¾®¶‹V]Â}óîÅðiÔ+u$Ÿ¢žŒ±z’RO2@"œ$Ô -2@Çm}o y“J0»µQLñó[ùõ…¿Žx„BÄ?É Í ¾â ò\Ú¶%ƒ¼ç޲æ|B!N‡@QlÚkW×ÃÒö£Õ“¹ÿå{(­+máU±'>ihMÆX=É©' NRE1£ÅÄÀ?¤â56´¥”é˜öÞƒÌüÏLí:f’OQOÆX=É©' NRE¹3§Ž$÷òг=þéLÒÛI,X¿@£^…—¬S¥žŒ±z²˜z²˜')€bÀuÿ¸”ü®¡w]µêzžü÷|WðF½B!b—@1Àê´Óý7Vº†¶Œbw}ô;¦¾=•Zo­v ɧ¨'c¬žd€Ô“ ')€bĹ3G³|h¾à¬u=¸¶?³–ÏÒ¨Wá!ùõdŒÕ“ z’á$óµBËy€šSYPÎò3Ö“uÔPßv4ÛË7]ÍËÓ_aÒÀIöN!D¬“y€DTJîBÖlCÈ¥°ŽùFüäÌüÏL•Ò®sB!D ‘(ÆŒ½o,û.¨i;ç‹þœ·í~úÎOcr©ŒDø¤¡5cõ$¤žd€D8Iƒ&ýýyLÞɧ¨'c¬žd€Ô“ ')€bPZN&öŸ‡~Òìœgâ¡5Oñø§óÉÁO4êÙ©‘9jÔ“1VOæROæá$PŒºìÑKØsneHÛykÏ`ì¡1Lg:%µršX!„hIÂ@ëÖ­cùòå'´×ÔÔð§?ý‰[o½•矷ÛÝÌ«£ÓU‹Î¢"¥á&>£nøèa Êò¹éÝ›4ìYûH>E=cõ$¤žd€D8Å}tøða.\ÈôéÓ›-€.»ì2>ÿüsFÍ{ï½ÇäÉ“5èå©éЯºÛëBÚrXùkÇ»;ßeÞ×ó4êYûH>E=cõ$¤žd€D8Oþ”ØVTTDnn.ééé'<öÕW_±cÇ>úè#L&W_}5™™™ìÞ½›¾}ûjÐÛö›ðäå,\ù½¿qÔ·]øÙ(Võ>‹ÿ·âÿq~ÎùœÑñ {xr’OQOÆX½Xúð«$$Â)îÏ 6Œ9sæ0jÔ¨Û°agu&“ €¤¤$† Â×_énž–K_BURÃ¥0³f®šƒÇãfêÛS©ñÔhØ;!„"úÄ}Ôšüü|œNgH[zzzH^bÑ¢EÌ™3'dk,ö»ïNÝ UÁþ²€Þ{Üÿ坨þöv~¾üçQÕߦûÇ;úûÇÇ8ZúûÅÅÅüú×¿ŽšþÄãþ£>’Òº?ñ²¿lÙ2æÌ™Ãرc™;w.‡D0KaÜwß}<ûì³õmsæÌaÛ¶m¼õÖ[õm_|1'Näž{î©ÿáhúC­^ú?z~g«ß¯³Áìïfs‡­¼9ùM®|†½kÙ‚ äb2Æê½ýöÛŒ7Nn…WèÈ‘#¬_¿^.ƒ)$Ka$ˆ¬¬,Š‹‹CÚ èØ±£F=:=þµ7µ¶†zÖZ w®þ=·þ÷V”Шg­“ÿ˜Õ“1VOæRO2@"œºúáȺuëꋠdzgÏ.¼ðB{vjzéGÙÔÐe2úmwrïú_Pî*çº%×áõËmºB!DÜ@kÖ¬aÆŒ¬X±‚+V0cÆ ¾úê+ú÷ïÏõ×_ÏèÑ£¹ãŽ;;v,>ø`Ìž˜úòUìï i»dõe (íׇ¿ä7ÿF£žµ,NµjMÆX=™H=™H„SÜ@}úôaƌ̟?Ÿùóç3cÆ zöìYÿø¼yóX¸p!#GŽdñâÅ<öXì­¥Õ˜N¯ãìg»à27\ sTë¸{Õ“<¹æIVí_¥U÷š%sÔ¨'c¬žÌ¤žÌ$Â)aBЧ"ÖBÐýãºwéº8%¤í‰ÿâÅaóé”Ô‰-wl!Óž©Qï„BD# A‹˜7uÑÈíº¬Ç«'Ó½¢ ßW}ÏŒ¥3´é˜B¤ŠSF‹‰3þ˜‰·Ñ\ßÎr³>ž À{»ß㹯žÓ¨w¡ᓆÖdŒÕ“ z’á$PûÁÕgrøÊ²Ð¶o²ùÙÖübå/ؘ¿Qƒž…’|Šz2ÆêIH=ɉp’ P+b9tœ»ÚÅÛ?¥s®©¾­$ÃÏ7]C£˜~ýøæ¶op˜­¼‹BˆD  7Ì ½›„¿Ñßtz±ž?y€]Å»¸ûý»5êB¡ )€À97žËKB/…ø:‡);ƒKc,Ú´ˆ7¾{C‹®’O‰cõ$¤žd€D8I” ®}íbŽf7ürÖ`òª[I« .{û²ÛÙWºO“¾I>E=cõ$¤žd€D8I” ™Étþ•…€®¡­ÃQ~ú4® ®[räW–uªÔ“1VOÖSOÖá$P9ÿ®óÙ76t­°Q_ôãÊ}W°.o¿Zõ+-º&„BD”@ fÊëP’é¯ß×`ÚG?Çî±ðôÚ§Y¹oeDû$ùõdŒÕ“ z’á$P‚qvJ%ù~H[§<}öG¸þß×SP]±>I>E=cõ$¤žd€D8I”€Æ?üCöœz)ì¼Ï‡rÑ¡±äWåsÃÒ™)¢$Ÿ¢žŒ±z’RO2@"œ¤JP_=‡²Ô†3AF/Ìøh6Ÿ€å{–óÌÏhÕ=!„B))€TfŸŽ˜î ½ã«ÛA ~þxýþÃ>Ì •÷Eò)êÉ«' õ$$ÂI  vÅ㗲笪¶ó?ɹGÎÀã÷0ó?3¹÷÷âõ«ûÅ.ùõdŒÕ“ z’á$kµ"Ö;™#[±aÌ^’+&Ú×§†[§MÀ§o¸DvQÏ‹xkÊ[dØ$ã „ñJÖ £ó9xnª iëµÇ΃ë~Ò¶jÿ*Î~él¾+ø.’ÝB!”H0éÙ+ØwFmHÛ…a|Õ¸¶}¥û8wá¹,ÝÞË)‰ðICk2ÆêIH=ɉp’HpÑ ý¨µ7ì[ëàº÷ä’? y^•»ŠIoNâ±O Ûmò’OQOÆX=É©' N’jE"d€{ó–ÿÐñegH[a;'~Å£9áùW¼šWò*“#R]B¡d€DBºæ… XÒ–U``Ì‹çñÂ;ï2¢hXÈcK¶/aôÂÑ(;Á^ !„§O QO§×qÞŸs(ÎòŸðX¿-Nžxñæ|ú,Iž†ke›nfäK#ùäà'§|ÜDø¤¡5cõ$¤žd€D8I$Bô»x—|7‚}?*Åk }ÌâÖqáªðòÂw™¶í§õíE5EŒÿûxþºþ¯§tLɧ¨'c¬žd€Ô“ Pù|P\ »wúuðÁðÆØ-â ç䯒jE¢e€šÚúÞÖý"žÛlÍ>þÝŠxñü_ómæöú¶ÛFÜÆŸôgLzS¤º)„‰)€Š ()ÒÒàÖÖï++ƒ¯o§Óópz6,ÊÓàYÆ“?E$ªÁWœÁà+ÎàÝ_¼¡•´’ІC6gòÇóY3ú+ž9oµ†:^ØðÛ ·±äÚ%dÙ³4ê¹BœŸÊË¡¬,ø½×{âÖÞöSyMãv'ØŸÆELYøOŒ,4§ÜšÁ÷)=)H:“²®¨2vÀ¥ËÀïKAïNÂ\kÃ^e"¥\¾Ç§Š8:H$NêÇü³ÊxûÆOèþa _ÃcÖ:øáGç0xûø÷/ñ¯ÿâ³CŸ1òÅ‘,º”aÙÃZ~ãcòóóÉÎÎVø'2Æê“’’‚Ñ(¿VUq»ÝTUU‘žžÞú«« ˜òòÐïÛòµªªõ÷nƒïÓºS”C‰­ ælêô™xý©àubª³c«2“\aÀ\ÔGÁIpKtò/U´‰³S*7-ÿ1›ßù†MÒ}‡%äñNGLܹøNÆ ¿šùçÿŠìaôßF³hâ"¦ šÒê{/]º”Ûo¿]e÷žŒ±z«W¯fܸqddÈr1aQSEE![ÑÞ½¬ß¾«:vl½ˆ‰ò0ºO§§ÚšJ¥9…jK Õf'u&'µÆ$ê \^½ÞŽ+>¬à·¢ó&cp9°V[Hª4a«Jƒ›×9÷2}bă%ÔŠDϵæûÞÃðª”2Ý ÕÚ|2æsžõ.ƒ›_]ð+÷:N|®"¸\'3­nÅÅP[{ò÷UÈ«7RiI¡ÊšFµÙIÙI1 —ÉY_¤xtv¼z;>lø°âX,èüt>z¯ƒÇ€ÉcÄäÖcvë1×o(‰f>û–|õ­»¡œœ§dÒ³WPr_!ÿžù9=W§ÐhÝTlµ:.[9š¡ÛÿË’± xœÇùöè·¼6é5’ÍÉÚuZqújkƒJIIðkqqháÒ\A£ø’’Go£Ò’J…5•jK*ÕæjÉÔÛÜz½/6üÇŠüt>3z¯ ƒ×„ÑmÀä6`qé±Ôé0»Úc[#Àql‹%n3T¦ë¨ÉÔãÎ2@G†&¬,$w²ÖÅFVW;zS%kGO¤¢Eé=²¸ùóá¯Ùúërö„^ërØÌ½ÿ¸— ΜÌ_.x„sKÎåÝëÞ¥wZïçI>E=cõb.ä÷ôNj˜ÆMk[;ÏÌøtj-™T™“ƒgQLIÔ™’©3Øq¸ô6<½ ¯Î†WgÅOðLŠ?`F°€ÏˆÎoÂï àñxIó¦6*V~k¦X±Ûâ•_åiP}¬¨ñw0¢ï`Â’m&©³•ÔÎV2»ÚÉîb'#ÓÚ¦÷ÌÏ×öÌ[$ÅÈ¿TÍF\7’3¯ ðöÝ˰¾žDrEèéÝaßtæù¯ðñ˜O]v¯_÷O.îyqýã’OQOÆX=M3@55Í+­5-ÞA¤£Æ˜L™½åÖ *-éT™‡PãHÁ•’Š[ïăÀNÀgCçµ`ðš1x=zŒ^F·‹[Ùºà:¶5aì'6·¨˜bv²›~œwJÃ-:p[t¸¬Ü6›¯M‡Ï®Ão×°ëÁ®»ÀÁaÀè0`Ë2“ÒÅFFÙ]ídu´b0$F^GɵB2@íW´ç(Kg~IïOS‚¿øšÈÍqñöØù\|çÅÜ{ν‘ï ±¤¦rsOÜ„£GŠ™º†%l¼:+¥ŽŽ”Y3¨°¤SeN£Ú”†Ë˜ŠKŸŒG—Œ/`'à·¡óZ1x­Ý&,uF¬µzl5ºfÿí&š€\Vp+PÜ6ðÚ‚Šß®Çotv=z‡½CÑaÀ”lÄœdÄâ0`I2`K6a?¶9’$%›HN6£‹ÒP"­&g€DXeöéÈÌÌW¯|ÁîßUÒu¿9äñn‡,Ü÷Ú,6n?Ì=3ïâO7ÿfƒ¹…w"޹\pøpóNnnð±’p„e·n ×ÚäSëI•pÑ»£xvÍß8ó‡ƒéøgM8ƒƒÏÆ 3h×ñ8$ 0òû!?¿¡9trsYýå—Œs»Éøþûàå)¿ŸzJlÝ8˜ÖŸG/*LgSÇ•’†Õ˜Œ³Ô„µNGÁp2µþ³ã²ê¨q¨KÒávêñ&ëñ9õà4€S!Õˆ)ň%Í„-Õ„-ň5É„ÕnÀ–dždÄ‘d‘dÄn2 Ó©”#Gް~ýVÎ7.Ÿ‹O Uîªúb¥þûFLÓ¶ÆíMÛ\¾f‚Y€³{bL“( VH(¼Ö,øŒƒ¨£Ë¡–× s›áû®5TfÅ—q€¤nùdw’yöz EGGÇöX$¤ââú¢æ„íСà™c‹Eú1‘—Ò{ý^Šk‹)¬.¤ º€ÂšB « ë¿TP\[ŒÏïÃð €?àÙж¶<§¹çt:ƒ³ÁŒÅhÁb°4ûÕl0·øX[_¯C‡Ûç®ß\>WÈ~s›ËÛ†ç´á}Žoþ@Û–º'ç§NÊ?Šÿ…}å ˆ˜1·ŸÏ¨=¼qËÉx'{õ‰Ï1»¡û>;ìë ôNÆÎx˜IDAT ÖÛ»T±%éüÖo19¶`ìîB7xÉgŽ¢{ÿ³é›Þ—$sRdÿ@"öTVžXÐ4-rÝæíÖÛ8”6#ξ[Ï£:y2¾Þ™kSH*·â,7 +Ê!™àn”fê¨ÈÖáêjB×ÍŒ5Ç‚-ÓŒ=ÍŒ#ÍLrº™ä4éRŒ­œqñø=ÕÕ1Õìª.¤°°Â mÇ/­-%€|NñG QF‹‰Ÿý}¹³ðßÛ60èë J(!–×ö±Õ@ÝIÀÀcÛ5T¤(ZSN™í ïóäzWSž^Di¯ÎøÀ6ì,ºõA¿Œ~ôLí‰QŸØ?êqŸòûƒ™š#GNÜn(tÊC?ÕVYR9˜6ï“ûRb9—Ú>ð{30W'ã,·‘\®ksþ¦‚ 80о˷. ”tÔQÝÙ€·« c7 öV2zØéÜ;‰½$[[þùuûÜÇÎÆæ`q!…‡Z>SSXSHY]Y»úU|€°iÝ‘ø`Ôq˜8Ì’ÌI8LL˜ØUµKë®E„\k…\Sï_zƒý}9ãô?¡c®“ôâSŸÓ¢$ÓOi‡̦½t©Þ@NéÇä%²½ƒž‚ú÷Á8x(é=’“’C÷”îä¤äi–ä…: ,ˆÝ Pqqó…Mã-?¿ÙõŸŠ’:q(­?ùŽÞ”Yr¨#ÜéXª“I-1㨠_tö>a8Ãq6Yf²"J;é©ëlÀßÍŒ¥›…äž6²z:èÖ×IN¦Fglü?%µ%­>JAuÕ­jøþøvüLM…«"l†¨W úkÝ‘È1è ˜ fì&;N‹3X¨˜8LŽ6}߸¸iú½Åpâ4‘‰t¼@­(òªâ›¥[9¼¶ã.#Ùû,8+Ní?©€ ²½Tfb3ì¦gÅ×üàȧ¨`oìI‡½é›i¦2§#þ^=±õìG×´îtOí^_$uqvÁ¤o9·$NQEÅÉ ›#G‚·‹7£Úâä`Z?ò“{SbíN•!¯/ƒ+[•ÔÖºf_6~=wÐQÑI»› ]W¶+)½ìd÷tÓ'™N©6\Þº š¦íE5EøÑsŽIo"ÓžI–#‹ŽdÙ}m´ŸjME¯ÓŸ°étºÛh¦­Ï ¸|.\^Wý×㹚Æmáx,@ >Kd6˜ÛµÏµë5ÍG¯‹ìD‡R @  h±{G9Û×RôYºÍudïÖa«9µ¢È¯‡â /Ui5ø¬ÅXuGHwí#§b;½ 7cð×p 5XíI‡½i°/CGe·Žzö sFOrRr‚ÅQ£"ÉiIŒ»&NÊç fhòóO^ØT7;Æ«7’›Ö—¼”¾ÙºSeê‚+îT,ÕRÊÌ'Ì8®‚×%tTvÖãéfBŸcÆÑÃFzoz'‘”]K¹+ŸÜò\ò*ó8Zu´ÙB§Ò]©¼¯meÐÈ´gžPÄ´´ŸjM•…ŒH"@‰ŒQádù”¾Rè; föÀçó³}K;×R¶®óÆZ²wù1yN~,½² d:'Á õhŠ€B”fø©H«Ág+!Íõ=ãí§ËwÛé]¸ ‡ûKòœ_ÖFŸ;ƒ´7 ;9IïØ#äÒÚñ)'%‡NI"÷I®¢ ‚™˜‚(( ß>² †àÝKnwðëél-½G?Oå§t'7gŽžT˜»QKo¦ÚdåfÒJ èeoR n*xLP’­£ª‹_WÆî’zØHïmÇÞÍ‹)¥sõj*r9\q˜Ã‡É-Ïåð¾ÃämÎÃís7¼Y `"¼2^§'Ã–Ñæ‚&Ý–³Ûí¦ªªŠôô–3ƒB´•@Bsí£Æ`Ð3dx:C†7üt»ýl^WȾÏK¨üºûfÙ{}!«ÔŸŒ.éEzÒ‹’€$ 8‡2`P– <­¯½³?Ÿó¾?Àä];è[¸‰”Úb [Ø›¶¥þÒÚ²cÅÑÞt(M6ÑÕÙ5ä¬QNJ³Ò7½/ZîXMÍ M«ß7sÉh)‰P@§§ÌšF^Z¿àÄ~ÖªõÙø|ëœØ*m¤•1»sJõªÚu6(í¨£¶‹ŽSŽ…äžV,]üè²+¨µçQPu¬¨9^àTärdÃÜëÜ'?@c€„eÊå$sèèèüšüÚ¸íxA“aˈøe­±~ýz®ºê*­»"â€\k…\‹m••n6^ÈÁ/J©ÛPMÒæ::(Yç¨"%@YFGFC>)žýt®ÚIï¢ÍdT¥ÒÒP Õ_ZK£:TC7·•þ¾4z¸íd×Ȩò‘\V‹©¤ ]uMXúèÓé©¶¦†¬Ê]kN9¶*w.ƒÞŽGoã³áÓÙð̰˜Ñùƒ«rë}&ô^F¯ƒGÉ­Ç|láK‹;rg¼&¥YP­§®“o' “Ÿ@¶ ]V5Œ2\Ö" ª Èmt'¯"¿ § ÃH¯Ó×_vj\À/lš¶ÙŒr›“І\"$'›siÆ\Ú¥¾­¦ÆËžíeäí¬¢dgu{ëÐïs‘|ÈGZ¾ÿ”'­s–ëp–ÛÞŸÛ N ð-P™,Ž\IåGé[vóóvѽd+:Ôš’¨5%á2Úq6ØØg°áI¶ãI±áÕ[ñé¬x1ãÓYðc!0CÀ3Ÿ߈ÞgÄà3¢÷0zô<:Œ^=f·³[¼DX{lkÂ|l‹=”¦AIe™.Ê2«)I+¡8%ŸBÇAŽX¶‘§ÛF•·™lMá±-’ÌItsv£«³+]]ÉNÊnölM¦=3aÎÒ+¤š‹ä5v»‘3Fdrƈo}÷xüìÝYΡíﬢfO-ºnì½dæ0zNíÔQr¥ŽäÊãÅQ6ðÜÀ'%±žÒÑšw²¹–"­2Žf¹(H¯¦0­‚‚”BŽ&¦0)—ûŠ,»ñê[¹…+7I%›“é–ÒPÜ4.tŽ·§XRꟲ˜PB2@"œä_ªÐ\´¬Se2é0$CNœîÎçós`o%·•S¸«Šê=µö¹°ô’q8€¥.º¯$¯a W™ÜD 2=dÔR^NAJ1Îï)H:D}¶¸ ÚÎ]ã´8[,jŽ··÷ξիW3nÜ8222õZHH„“d€Z! ÑV¹‡ª9°­œ£;*©Ü]ƒŸ ËAé‡|Í.ù¡…€.¸ÖZ­ÕË ÎâÇeñQgöâ2û¨3{p™=Ô™ÝÔ™]¸L.êŒuÁ¯¦\ÆZêŒÕõ›ËXI¡ —¡’:Cy𫱊€ŠU tèp˜8-NR,)8-Î-ÅšBš5­¾°9^è$›U,Z!Dì“ ¢]ºå8è–ã€ËN|ìè÷5ìÝVNþŽJ*öÔàÝ[‡ù€‡”zæ¢Ñ¡#ÉœtB±rBÓLQÓø¹ÉædÉÖ!N‰@Bsñ¾NUÇNv:v²ÃÅÂú¾^¿—ýeûÙU¼«Ñ–Ï®â]äUä….`YEðÎþSd3Úê§Ö·›ì!ÓìÛMöï“ÌI$›“[-j’ÌIqW¸HH=ɉp’©BsÑ’Š5F½‘¾é}é›Þ—+ú^òX§†=%{ê £ÿ½ñ?†ŽR¼œ¬iü}¬NœI’RO2@"œ$Ô É !„H$‰”НsÐB!„m Ð\"|ÒКŒ±zÅÅÅx½^­»×Ün7%%%ZwCÄ )€„æ–.]ªu➌±z«W¯¦¼¼\ënĵ¢¢"Ö¬Y£u7Dœ P+$$„"‘HH!„"ŽI$4—Ÿ4´&c¬žd€Ô“ §„.€rssùòË/C¶;vhÝ­„ó÷¿ÿ]ë.Ä=cõþóŸÿP\\¬u7âÚ÷ßÏòå˵ =â¼yóxùå—éÚµk}ÛèÑ£™7ož†½J<555Zw!îÉ«çr¹H¥Z@·Û­u7DœHè3@“'OfÓ¦Mõ›VÅOIIÉiýÃnÏ%޶<·µç´ôXsíMÛ|>………'=¾ ^¯÷´>¡·ç都¦†ÊÊÖ×Þ ×—••ár¹ÚüުαÛûwt²cUVV¶XüµôwTUUEUUUH[s?·±:Æí}½–¿/ÚòïH•æ~Ú£=cÜ–ß-ªÆødïϾŠÿùÏ8räÈ)¿~Á‚a}nkÏié±æÚ›¶UVVòú믇´Î/™ö(**â_ÿú×)¿¾=G[¶laíÚµ­>'\c¼|ùröïßßêó"5ÆÍ»=Úûwt²c­]»–-[¶4ûXKGëׯgýúõ!mÍýÜ6=vuuuÄ2@§3Æí}½–¿/šþy<ª««OÚŸphîç =Ú3ÆmùÝ¢jŒOöÞñ,¡oƒŸ={6ùË_°Ûídff2mÚ4~ùË_¢×ëÂ9sæ°xñb  ¼/ùùù¤¦¦bµZOéõ Ga{nkÏié±æÚ›¶y½^òóóC.;nÚ´‰aƵ©ï§ÃívSTTDçÎOéõíù;ª¨¨Àëõ¶ºhc¸Æ¸  €¤¤$ìv{‹Ï‹Ô·ÔǶjïßÑÉŽURR‚ÑhÄétžðXKGeee¤¦¦Ö·5÷sÛôØ;vì [·n8Ž6õýtœÎ·÷õZþ¾húwTQQA~~>ýúõkSßOGs?íÑž1nËïUcÜ´Íår±qãÆ„8+”ÐÐÁƒÑëõ˜Íf¶lÙÂ-·ÜÂm·ÝÆÃ? ¡I(Z!D"±Z­\vÙeZwC¹„.€šúÿíÝyPSWûð/›P„²(‚* ZT¬TePJÀŠc§ê¨£•ZFJÝp@êVÔ*L…Z‹(q[K*®-ê¸!’°(K-"XQ@Êòüþp¼¿7 }‹¾Hò|þ✓›|9f.7çžlÞ¼¹¹¹8}ú´ª£0Æcì Òè»ÀÚÛÛ…» ¼¼}ûöíÔ±ÕÕÕˆ‹‹Ão¿ý|öÙgèÕ«×›ŠªÑêêꘘˆ7ª:JtâÄ äää혘…zX×ÈËËà««Ëwš¾¹¹¹8tèÐ2dV¬X¡ÂD=Ó™3g ccc,Z´ Pu¤×¦ÑW€ÜÝÝáïïGGGܺu {öìÁùóç;µV¢²²555>|8¢££áèèˆ tCjÍÒÜÜŒyóæáÖ­[¸s玪ãôHqqq°··‡ ÿþ\Ìw1©TŠM›6!))  ëµXר¯¯6I,,,Dff&RSSUœªgyüø1$ ~øá`ãÆ8xð ªc½6¾ ,.. 8}ú4tttàãã ¥Ç%''ÃÃÃnnnX¿~=ˆ¶¶¶pssC~~>îÝ»'üñ`ˆÒáÝ;wîæxÆ Â~*K—.ŪU«:\ÄÊ:vñâEDFF*õ×ÖÖbîܹ2dÞ}÷]üòË/ÂXee%îÝ»ÇÅO'Éår|øá‡JýX¸p!†Љ' ›ömÛ¶ !!!¸víªªªº;®Ú ‡L&SêOMM…——FŒ˜˜´··C$ÁÞÞöööØ·obbbTXýlܸÇWêÿùçŸ1yòd8;;cþüù¨¯¯‡¾¾>jkkñÓO?áñãÇ8p  w!b”””DãÆ#$“ÉÆ233ÉÎÎŽ®\¹B·nÝ"WWWúâ‹/ˆˆ¨­­"##),,ŒŠ‹‹U]m$&&ÒØ±c €Ò\}÷Ýw$‹éêÕ«tóæMrqq¡íÛ·ÓçŸNÇŽ#"¢1cƨ"¶Z©¨¨ ˆˆ²´´$???¥q___Z¼x1•••QJJ S]]={–vìØAkÖ¬!OOOjnnVAzõP__O‹-¢³³³ÒøÜ¹sI"‘PII =z”ŒŒŒH&“ѨQ£h×®]”••E^^^TPP ‚ôêãË/¿$@W¯^U;uêõíÛ—.]ºDwîÜ!Z·n0.•JiÉ’%ÝYíäääPPPikkÓž={Æ*++ÉÈȈ233©¤¤„ÂÂÂ(00ÚÚÚhùòå´lÙ2rrr¢¼¼<¥ï\ÑÕ«WI*•’®®.)ŒMŸ>’’’„vzz:5Já1999ôñÇwKVuuåÊ’J¥¤­­­TùûûÓÖ­[…öþýûiôèÑA¡¡¡Jæææôé§ŸvwlµR__OR©”"##•  ²²2ÒÓÓ£§OŸ }cÇŽ¥½{÷*<.88˜nß¾Ý-yÕQss3I¥RÚ¼y³RôìÙ3200PxK$Š¥ &Pcc#íØ±ƒRSS»5·º¹~ý:I¥R‰DJPhh(ÅÅÅ íï¿ÿž‰ˆ¨½½|}}©ººº[󪣒’’J¥äîî®T%$$P@@€Ð®¬¬$]]]ÊÎΦ¥K—Qmm-1¢[3w5^ýÒÛo¿ ÐÒÒR“ÉdX¶l™Ð>|8ŠŠŠpá´¶¶B,#33~~~Ý–W=@Çs,—˱råJ¡íêꊻwï*lBæåå…øøø7TÃ××ÅÅŸ~ýºÂ˜\.‡X,†‘‘‘Ðçêꊢ¢"9r...hllDii©ú_Ö~ƒzõê___<þ\i¬¼¼0hÐ ¡ÏÕÕ………˜6m¶mÛ†Ù³gãØ±cHOOï¶ÌêhäÈ‘]]å?Qr¹‰Dh»ºº¢¤¤­­­ÈÈÈ€¬¬¬º-«ºrpp€ƒƒC‡{•Éd2¸ºº mA__ùùù¨ªªBqq±ÂcÔ@ÿ ¦¦FaÁ¢±±1ššš`bb‚Çãáǘ:u*æÌ™£Â”êíáÇ s,‰ÐÐЀ¦¦&¼õÖ[€ˆˆUÅëjjj„¹|I$¡¦¦úúúHNN†¡¡!222xîkúë¹xq¾¨©©ÁÊ•+±k×.ìÞ½III¾Û”)ëè|ADxôèš››þ3Å^OMMÒÝ]"‘---HNNÆîÝ»1`ÀìܹSE »@ÿÀÐÐPá;Zššš ««‹aÆ©}õûoÑ»wo¥9ÖÓÓSØ•  ÿÍ_çx±`W$aÆŒ˜1c†Š’õ=W/ÞË"‘ºººXºt©Š’õ,/€ çÏŸ¯ªX=Ê;_¸¹¹ÁÍÍMEɺ–FßÖÖÖÖ¨¨¨Úååå°¶¶îð£öz:šãþýûów!kkk}„/½ÿ>òòò¤âd=‡ ¼¼¼„½gêêêpüøq~/w1‰D‚´´4´´´àóÅ›ŒãÇ£ººðÍ7ß`øðápppPq².¦êUØÿ$‹ ÙØØÐÈ‘#…±††  KKK²µµ¥ &УGT˜V=(̱›››0öìÙ3š>}º0Ç>>>T[[«Â´êéÆ$‹ÉÜÜœ H,Ó–-[„ñsçΑ••999‘‰‰‰°뼇’X,&+++ÒÓÓ#±XL‘‘‘ÂøíÛ·ÉÞÞž Dfff´jÕ*¦U_sæÌ!±XLÚÚÚdmm-ÜåEDôüùs !sss²³³#OOOzðà Óª§M›6‘X,&277'±XL………Âxll,“““ÙÙÙ)Ý×hôNÐŒ1ÆÓLücŒ1Æ4@Œ1ÆÓ8\1ÆcLãpÄcŒ1Ãc¬KÜ»wOØÊ@UÊËËñèÑ#•f`Œ©.€ëÊËËáïï%K–(ôoÛ¶ ‡îò×kmmÅÀ6ìN÷ï߇‡‡&NœÈ;.3Æ:… Æz gÏžáÊ•+ÈÊÊBnn®Ð_PP€²²2&{3:OOO”””ààÁƒªŽÃS\1ÖCéëëcíÚµX³fM‡ã7nÜÀÞ½{úbbbðÇÒÓÓqöìYÄÇÇ#44;wî!55aaaؽ{7Ž¿{÷.–/_Ž9sæàÔ©S cyyyX°`BCC‘’’‚—[åççcÿþý¸ÿ>’’’‘‘ÑaÞ'Ož &&ÁÁÁˆŠŠr^¸p)))(++ÃêÕ«qóæM¥cïܹƒ]»vá÷ßÇöíÛ…Ýš+++±bÅ H$ÄÇÇ£¹¹pùòe¤¤¤Ç;v gΜÚ[·nEii)@*•â£>BXXÖ­['ìžËûw㈱,""uuuÈÊÊR+,,Ä·ß~«Ð—˜˜ˆ'OžŽ9‚àà`455aÚ´iHLL„³³3.^¼8p[¶lQ8~ÅŠprr‚©©)‚‚‚ŸŸ8qâæÏŸwwwH$$''#!!À‹B,** 3gÎDii©ðúÿ©µµÞÞÞ(((ÀìÙ³!“É0~üx´´´ÀÌÌ †††°²²‚³³3D"‘Òñ2™ k×®…¿¿? ñäÉÔÕÕÁÓÓOŸ>…D"ÁéÓ§…¯ÑÒÒBtt´p|tt4Ö­[hnnFTTÌÌÌpîÜ9ÁÛÛ|𪫫ñ믿vú߇1¦:ümðŒõ`ºººX¿~=bbbøÊÇÇÅÅañâÅ^•••Ø·oÀÄÄ X»v­ðø“'OÂÂÂPSSƒÔÔTxxx 666l@hh(ÀÀÀÑÑÑXµj@,ãìÙ³ÐÓÓë0Gvv6ªªªpýúuhkkãý÷߇¥¥%Ž=ŠØÙÙaìØ±ÿÛßÅÒÒ—/_†`óæÍèß¿¿ðûLž<–––¸víÜÝÝñüùsÈårÌÍÍQZZŠêêjÃÅÅfffÉd‹Å˜9s&ŒŒŒ0cÆŒWžcƘjð Æz¸`ÿþý¯|¬ŽŽŽð³‘‘´µÿÿ”all¬tÇ•–––𳇇ªªª¼ø*** ÎÎÎpvvFdd$z÷î-<ÖÜÜüo‹Ëå?~¼ðúZZZðöö†\.ïôïbbb"?/ŸÓÇÇG!ðaà —Ë¡££___œ?‡FHHfÍš…¬¬,\¸p“&MÂÒÒÖÖÖ˜4iÒÒÒÐÖÖÖéLŒ1Õá+@ŒõpZZZØ´i,X___¡_WWþùç+=ÏkÿUQQ¬¬¬¦¦¦HKKÃ;ï¼ÓùàÿÁÔÔTiñvYYüüü^ëù^>çËu<ÐÖÖ†ŠŠ ˜ššxqEèüùó¸yó&rrrPRR‚ØØXaáÂ… üøã(**Â¥K—mmmÌ;÷µs1ƺ_bLL:ƒ RX 4~üxܽ{µµµhooGFFZZZþ§×y¹(º  'OžD@@ 6lÆqäÈ‘N?¯ŸŸŠŠŠ •J¼XP]XXˆ)S¦¼vÖ÷Þ{R©·oß!==:::3fŒðšÙÙÙ044D¿~ý0nÜ8ãâÅ‹ðööäææ¢¬¬ C† Axx8FŒñJE%cLu¸bLCÄÇÇ+ܵկ_?Ìž=èÓ§Μ9£ð‘×ë˜2e Œ‘#G"<<\Xw”€>}úÀÖÖNNN°°°P¸«êŸ8::bß¾} „­­-¦OŸŽ¯¾ú NNN¯uòäÉøä“O0zôhØÚÚbõêÕ8xð phèСÐ××Gpp0€W¼fÍš'''áã»`Ô¨Q Disk space taken by a record (original record size: 16 bytes) 10 3 10 4 10 5 10 6 10 7 10 8 Number of rows 5 10 15 20 25 30 Bytes/row No compression zlib lvl1 zlib lvl3 zlib lvl6 zlib lvl9 PyTables-v.3.1.1/doc/source/usersguide/images/compressed-recordsize.png000066400000000000000000001221351231437614300262250ustar00rootroot00000000000000‰PNG  IHDR@°AàÚ²sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwX××ðïÒ{¤ˆ€1""v°EbŒ"Q±K‚õ§Æ†Q£ÆÆ{7–ØbÃÄÞbŒ5ÁŠX(*]D¤Ãyÿàed`EYVá|ž‡GçN;sgÙ=윹#!"cŒ1ÆX-¢¤ècŒ1ƪ'@Œ1Æ«u8bŒ1ÆX­Ã cŒ1ÆjN€cŒ1VëpÄcŒ±Z‡ ÆcŒÕ:œ1Æc¬Öáˆ1Æcµ'@Œ1Æ«u8bŒ1ÆX­Ã cŒ1ÆjN€cŒ1VëpÄcŒ±Z‡ ÆcŒÕ:œ1Æc¬Öáˆ1Æcµ'@Œ1Æ«u8bŒ1ÆX­Ã cŒ1ÆjN€cŒ1VëpÄcŒ±Z‡ ÆcŒÕ:œ1Æc¬Öá謬,E‡ñÞŠEZZZµï÷Ù³gHNN®öý~¨^¿~§OŸÊ´lnn.¢££r^åˆììlE‡òN²³³ "Rt(¤$<þ\Ña”øøxE‡ñ^⨊ <~~~ð÷÷ÇW_}…Y³fáܹs¥Þ,þþûoøøøàÉ“'n3;;¶¶¶¸r劼Â~o;v <¨ôzmÛ¶ÅÎ;åQùÆŽ‹ÿýïÕ¾ßÕáÃ‡áææ&Ó²&L€¯¯o•$ ƒ ¢E‹d^þúõëðññÁÇßyßE233akk‹«W¯VÙ6áÚµk°µµEFFÆ[­åÊøøø **ªŠ#«œM›6aÉ’%eοwï&Mšooo8::âÌ™3ånoÈ!˜1cÆ;ÅtñâEü÷ßï´’®\¹[[[üý÷ßUºÝš€ *vìØ1€§§'ôõõ ___´oß^ô†¡££ƒ† B]]]Q¡¾—¾þúk?~\Ña0;sæ 6mڄÇÃÄÄä·gggsss™—×ÖÖFÆ ¡¡¡ñÎûfbŠ~ï;{ö,¾ÿþ{|ýõ׸}û¶Ôe:///$%%á‹/¾ÀÔ©Sadd$÷Ø-Z„­[·Vé6{ö쉩S§¢ÿþü·UMEÑÔD;wƘ1c„éGÁÇÇ={öÄü8;;#((uêÔ–ËÎÎÆ­[·ðâÅ ØØØÀÑѱÌ}àÉ“'Ð×ׇ¡¡a™Ë=}úáááÐÑÑAãÆ¡«« HIIAnn.LLLpëÖ-<{ö žžž¢meeeáÞ½{HLL„¦¦&\\\¤¾ $$$ ,, zzzpqq½±¥§§#44YYYpss+÷Ã,)) ¹¹¹HIIAtt4ÀÚÚJJJxúô)¢¢¢ðúõkXXXÀÕÕµÌí?vuuu˜šš }†èèhØÛÛ£aƲ¯^½BZZ,--ñðáC<}úíÚµƒŠŠl¿"¹¹¹ Å«W¯àåå%ôs||<ÔÔÔ`ll\êX•””ÊìÜÜ\„‡‡#..jjjhÔ¨ÌÌÌÊ!-- /_¾„•• ..FFF‡x||Ô´iSrpp @ÖÖÖôðáC""š4i9::RAA°NVV™˜˜Ð¦M›ÊÜî AƒHMMÈÚÚšTUUiÉ’%寲zõj277§7R:umß¾ hÊ”)¤¤¤D...¤­­MÎÎÎôøñcaÝãÇ“©©)©««SݺuIYY™~úé'""ºÿ>}ôÑG¤¦¦FVVV¤¦¦F?ÿü³h]´oß>²±±!4gÎ""úá‡HEE…ŒŒŒÈ‚´µµÉØØ¸Üã¸sçú°¸y󿑲²2Y[[“ŠŠ 5kÖŒžùûûӀʌ•¨0ª_¿>9s†òóóéúõëÔ¨Q#  "¢ˆˆRRR¢Ó§O ëüòË/dllLen÷öíÛ¢˜M›6‘²²2½~ýºÌuV¯^M¨[·ntêÔ)ºÿ>%%%ÑæÍ›ÉÄÄ„îß¿OD…Ä:uŽ-..ŽôôôhܸqôìÙ3"*ü*zýµnÝšºtéB±±±”——G›6m"‰DBׯ_'¢7 P‹-èÈ‘#ôàÁЉ‰¡sçÎZ±b…p¬K—.­0Zºt)5mÚ´Tû™3g„ä   €¢¢¢¨uëÖÔ­[7a™–-[’††Íž=›þûï?ºqã‘(JOO§úõëSÏž=…ä)11‘”›)))Ñ‚ „sH:uöýäÉÑïäƒHMM~ÿýw¡ßeM€ZµjEÁÁÁB_ž>}šÔÔÔ„Äììl¢Â$:tHˆ%<<œ<<<„¿‚—.]J¶¶¶¢}nß¾LLLÊŒ•¨0êׯŸ¨möìÙ¤¯¯/LwëÖzöì)L7oÞœ¦M›Vîv‰ˆ^¼xAG¥Ÿþ™&NœHJ½©Wô PI-Z´ aƉŽ}Μ9T¯^=""Z¶l)))Qrrr©u=z$$ÅÕ­[Wøv¡(ÊÌÌ-3tèPáMºÈÎ;+L€ÆO½{÷.Õ>xð`rvvµmذoê-[¶}ð)ž>}ºÔUø –––hù“'O’ªªªè÷%;;›þúë/Z»v-}ÿý÷dhhH+V¬ ¢Ê%@999¢ö€€òõõà 6ŠŠ åääÐÁƒKýÁS$++‹”””„oôŠxyy‘ŸŸ½I€ÂÃÃEËÌ™3‡´µµE´üõ×_&@ 6¤îÝ»Ký#­¬(77—¼½½©{÷î·/?ýôÕ«W„ã¾|ù2I$’R篲ÊJ€ÜÝÝÉÍÍŽ9Báááô矒»»;5lذÜ?Þ|}})00PÔ6qâDá÷Œ¨ðuX|Ÿ 6¤ùóç ÓÒ ñãÇS³fÍDç¾è|ÇÅÅÑ7­ZµJê{}‘ôôôR‰RmÇ5@Õ¤  ÷îÝÈ#¤Îÿî»ï˜››£G˜1c†PϳfÍÂßÿ'NTXÐéîîŽo¾ù“&M„ àææ† & ‰Dê:nnn(((@||<êׯ   ,]ºÞÞÞ°··G~~>ÿFDD OŸ>R·äädôíÛWÔîêêŠ×¯_—{IwïÞE=——‡-ZÀÒÒJJJBEvî܉˗/cÙ²e¢¡ˆˆÀœ9sJm[MM­ÌýêèèT:V ð_¾|‰ôôtèèè`ìØ±øôÓOƒøøxüûï¿Ø¿¹ÛX²d ‚‚‚вeK899 u%¹$iç6""qqqø÷ßEíE5puu-U§T4Ú´i#joݺµ0¯¬}GFF¢cÇŽåÆ+Í‹/„Ú›’±H‹£h^QmEY¯ï"QQQÐÕÕ…§§g¥c+NGG¹¹¹ÈÉɺº:.\¸€Þ½{£N:ððð@ݺu¡¬¬\á9“¦ä1DDD &&¢ö>ú/_¾DDDŒÑ´iÓRÛŠŽŽFAAÔ¾;}út¹ûŒŒDÛ¶mËý=‘fÅŠ;v,,--akk‹¾}ûbòäÉåOž< ∈ˆÀË—/ѯ_?ѲMš4Afff¥bªŒÎ;£{÷î'''lܸ¸qãš7o.óv\]]ñóÏ?ƒˆ ‘H0vìX <K–,Á7¯¾úªÜmDDD **ªÔ¹wuuÅ«W¯àêꊙ3gbêÔ©˜8q¢è½¾¨† (,ì×ÕÕåa;Šá¨š;v éééðññ‘:ßÝÝW¯^ÅíÛ·qåʬ\¹þþþ¢[fñÕW_aÈ!¸téìííËÝçüùó1eÊ\¹rÇÇÀ¡££ƒnݺI]þöíÛPVV†¹¹9nݺ…E‹᯿þÞ8¯_¿ŽmÛ¶ Ë›šš–y›°©©)7nŒ¿þú«Ü¥¡C|ûí·°··ÇÑ£G¡ªª R÷›••…ï¾ûß|ó ÜÝÝÑ®];! 1婨 ¼¨@õã?†6lØ€'Ož G寑””„)S¦`Ïž=B‚‡Õ«W¿U<¦¦¦0`¦OŸ^æü¨¨(äçç‹ ŠEÚBÂ<@³fÍÊݯ––Ö[?RÖ›´‰‰I©¤«hÈ„âEÙ144DZZž={Vª˜ö]Œ?þþþX·nðþÇTɶMMMáááuëÖ•9ÿÅ‹HNN.UX_üzyy í<¨°ßÞö~üñÇ Chh(.]º„åË—#** »wï–ºüÎ;±eË„„„ˆŠwMMMaoo_*y—'{{{<~üXÔVô»œ’’R©m………ÁÜÜ\x=ôìÙFFFغu+._¾Œ¾}û–:_%ßÿLMMѦM>|¸ÌýÌ›7“&MBHHŽ?ŽÁƒCKK =zô–ÉÎÎFzzz©âéÚŒoƒ¯'OžDÿþý1mÚ´2ï ¹}û6$ š4i‚¯¾ú AAAˆŒŒýõ8qâD¬]»íÚµCçÎ[æ>£££…»¯¯/V¬X{{û2–¼¼<9ríÚµƒºº:…»ÕŠ”³¨uëÖ8qâ„h€ÆÔÔT$''ÃÛÛ!!!¸yó¦hØØØrÇqrr³gÏDm‰‰‰prr’Ÿììl$%%•ZwĈ˜5k&Mš„Ï>ûLO£aÆ033ÃúõëEËçææVùX$D„'Nˆ]‰D‚1cÆ`ýúõسgO…ã%%%ˆàââ"´É2^TY¼½½±}ûöRi%­[·Fjj*Ο?/šÿèÑ#8;;ÃÄħNÚcbbpûöm´mÛ¶Üý6iÒ—/_}‹VòƒEKKK©ç¥mÛ¶¥¶wüøqX[[W*±õòò‚ªª*Ö¬Yƒ¬¬,8ðÎû%&&¢qãÆÂ‡]jj*^½zõNÛ,âí탖J ‹Îa‹- ‘HpäÈÑü‡ÂØØÎÎ΢s˜™™‰ .Ètoß¾-J‚*:‡ùùù ƒŠŠ <==1qâD >¼Ì÷žÐÐPâ—_~½ß÷7"jOHH yòäIüüóÏåÆ%«:àÔ©S¢÷˜àà`¨««W˜ôWPP€“'OŠÞ TTT0räH,[¶ ÁÁÁ¥Þ ¤½ÿy{{ãôéÓˆŒŒµ}> j``€.]º`ùòåppp(Õß?UË Å]}«™ŒÉÃÃúôéC;w¦úõ듪ª*M˜0A´\É KKKòõõ¥yóæÑ7ß|C666BÁtÉ»À²³³©S§Näìì,µnƒˆhóæÍdffFcÇŽ¥Å‹Sß¾}©N:”––FD…5@êêêÔ«W/š0aYZZ’P;“——GÖÖÖäææF&L ®]»’±±1öKææædllL_ý5?žêÔ©#j0€tuuiìØ±4oÞ<êÝ»7ijj wE”·žžM›6ÆŽK/^¼ Í›7“–– 0€FŽIvvv¤¯¯O£GÖ+yX`` Õ©SG¨i8xð ihhÐgŸ}Fßÿ=’-\¸ˆ¤×>>000€²²2üýýñúõkäääÀÉÉ©T NNN077Gll,"##ammuëÖ cÉ;v ‰]»vÅ“'OСC¬[·NØ–’’z÷îÌÌLDFFÂÇÇ‹-‚‘‘Ú·oUUUèêêbøðáÐÔÔDll,ÔÔÔ0~üxx{{CII þþþpssÃÓ§O Ì™3...eÖi¸¸¸ÀÃÃ7oÞ„¦¦&:uê„fÍšÁÍÍ wîÜ––¾ûî;´nÝ...pppÖmÑ¢…P;ÒµkW¨ªª")) nnnpvvÆ€’’‚û÷ïCSSýû÷ÇàÁƒ¡®®‰D+++´hÑBtŽLLL„KiÒH$´k×ùùù¸{÷.¼¼¼°eËQ\ ¬¬Œµk×"((îîîå½|~~~PVVƽ{÷ЬY3¬\¹zzzhß¾½0®ˆ4–––hÙ²¥¨MKK ÐÖÖFdd$RRRàåå…9sæu?½zõ‚³³3^½z…ääd|üñÇ3f ´´´Ð´iStèÐqqqHJJÂÀ±jÕ*á9Âë¢xÍžžzõê…¬¬,¼xñ½zõÂÔ©SQ·n]¡vG lß¾‰Dô …ŠŠ ¾øâ ¨ªª",, ŽŽŽX±b:tè ZßÝÝvvv¢6‰D‚¦M› í­[·Æ€мysŒ;'NÄ‚ 0mÚ4˜ššB"‘@[[>>>Bý•™™Y©oLŠÎ‰²²2Ú´i[[[ܼyæææX¶lìííѼys¡–OII íÛ·‡¾¾¾Ôc—H$022‚èwDEEÆ ƒ……¢££,X°@¸4Ù©S'´lÙ¯_¿F||ž9ØÖÖV4ÞJxx8ÂÃë5VÆcL‘444àëë«è0ä®V$@JJJ°²²Â_ý…„„XYY!55µÔ¥.MMM¼|ùR˜Þ³göìÙƒ† Ê=Æ„„@CCã­ÖŽŽ†M•-[Þ2eÍ“Ö^²-//O8E=zTá¨ÖU!''ÉÉÉo}WQeÎÑ«W¯——Wn]IUõqRRtttD·/—\®ºú¸¬eUÙsTѾRRR ¢¢"]¸HYç¨è†‰â5Ò^·%÷ýôéS˜ššVË%ôwéãÊ®¯È÷‹’ç(##)))U^&´×AeT¦eyo‘W—lËÎÎFhh(dŠýƒ¦¸!ˆªß°aÄÁó~üñGáA€EZµjE[¶l¦gÏžM³gÏ®–ضnÝZáÓ†ËS™8eY¶¼eÊš'­½dÛ‹/hÙ²e¢¶O?ý´ÂxªB||<­]»ö­×¯Ì9úçŸèøñãå.SU}¼{÷nºwï^¹ËUWKÛweTöU´¯ãÇÓ?ÿü#u^Yçèüùó £E¤½nKîûË/¿¤°°° c® ïú¾ô¡¼_”.ÙOfffåÆRSÔªhÊ”)Ô¿""úí·ßÈÑÑQ4ßØØ˜þüóOaº: çÏŸ—û¤áŠÄÇÇWé²å-SÖ}ºš"be¹|ù2š4i¢è0j4îcù»yó&ŒŒŒÞúFV±W¯^áîÝ»hР¢Ca5@ñ]='–ŸË+òï¿ÿâöíÛŠƒ±·¢¬¬,<ìUš„„4mÚ´VÜVc¿bŒ1yØ¿?NŸ> E‡ÂX¥äççcÿþýå&@µ '@Lájm^uá>®Z½{÷FPP¢Ã`¬R²²²°ÿ~E‡ñÞ¨±w±ÇáÇBÇ}Ìcbœ1…9r¤¢C¨ñ¸cLŒ ÆcŒÕ:œ1…« w(÷1cŒ‰qÄŽëSäû˜1ÆÄ8b Çõ)òÇ}Ìcbœ1Æc¬Öáˆ)×§È÷1“fïÞ½X¾|9nܸ!jüø1V­Z¥ ¨j¦¹sçâÒ¥KŠƒà S8®O‘?îc&ÍòåË1}út 6 ÅŸŠŽÉ“'+0²šgïÞ½ St¬ š)×§È÷ñûcûv`êTùïÇÔ¸u«âå† †-[¶`ß¾}¨pùׯ_C[[»ÒñäææBUUU꼌Œ hii•»~ff&455EmYYYå>|–ˆ••Uj=Y¤§§CGGG꼂‚äçç—y<ÙÙÙPWWµ••üdggCYY**åKÛ&{7ü cŒU£Œ 1Qþ?II²ÅS¯^=Œ;³fÍB^^^™ËíÚµ õë×‡ŽŽÌÌÌðÓO?¡¢gi'%%! úúúÐÐÐ@Æ ±cÇ@NN&Ož CCCèèè Q£F8s挰î;w`ccƒ-[¶ Q£FÐÒÒ‚þý÷_lÚ´IˆÅÏÏO”\|ûí·èÙ³'ÆŽ+l»S§NH*Ö!>>>X¿~=V­Z…Ö­[£yóæ “žÑ£GÃÀÀººº¨_¿>Ž=*¬÷àÁtêÔ ZZZPWW‡¶nÝ  0A=z4 ¡¡¡###Œ1BX×ËËKôЇÂÇÇ:::ÐÕÕÅ矎gÏž óç΋Ï?ÿ3g΄‰‰ ŒŒŒ0|øpÑq°wà S8®O‘?îcVžéÓ§#)) ›7o–:ÿôéÓ:t(†ŽÇcæÌ™˜?>Ö­[Wæ6 еkW}úééé ¿¹êÔ©ttt‚ßÿ±±±èÙ³§è8>ŒçÏŸãèÑ£8vìΜ9ƒ_ýUöËÊG¬L³gϦٳg+:ŒoíÚµŠ¡Æã>®:Ó¦M£ ¼õúk×òÿ13«8–-ZÇ2þ|²°° ŒŒ :qâ©«« ËuîÜ™¼½½Eë>œlmmËÜöÑ£G ]¾|¹Ô¼¬¬,RSS£ùóç‹ÚëÕ«GDDtýúu@Ož<æ:tˆPnn®Ð¶~ýz²±±¦GM;wm÷»ï¾#uuuÊËË#"¢ ÐäÉ“EËܽ{—Э[·„¶‚‚²°° uëÖQݺuiúôéR÷Ë/¿$ÊÊÊ’:ßÜÜœ¶lÙBDDÛ¶m##Ì?w¿ÆGíÛ·mcúôéÔ¡C©Û—Eff¦è¼JOf²¼xjþˆ)×§È÷ñûcÈ >^þ?·oW.®ñãÇ£  +W®,5ïîÝ»èСƒ¨­}ûöˆŽŽFFF†ÔíݹsÆÆÆðòò*5ïÑ£GÈÉÉ‘ºÍ’µ2‰Dø¿®®n©m‰.IÓªU+dgg#66Vh«[·®h™»wïB"‘ sçΨ[·.êÖ­ sss$''ãñãÇ€I“&á§Ÿ~‚““† †S§N ë:OŸ>…¥¥%üýý±víZdggKçîÝ»ptt„¥¥¥ÐÖ²eKhhhˆŽ¿ø±€±±1›[…¸š1ƪ‘¦fáÏûF[[³fÍÂÌ™3±víZÑ””ÄkKÛfEÅÀ%•L¤)ºL¥¯¯_æ2ªªªPQQÁ;wJÅPTh=yòdøùùáСC¸té>ýôSLž< ,@Ë–-={ö $$3gÎÄš5kp[J&*­?srr——WîñËr¬Lvü S8þ‹Fþ¸™,¾üòKañâÅ¢vWWWœ;wNÔvúôi8;;—ygR“&M––†þùGÔž ;;;èêꊶYPP€sçΡiÓ¦Ut4oœ:u NNNå&@M›6E^^®]»Ѳ²2ˆ999°··Ç”)SpäÈÌš5 ÁÁÁ “9Œ17nÄ™3gpçÎDFF–Ú—««+ž¸¸8lܸß|ó -Z ðòϪU«-*V~[wîÜÁêÕ«aee…Ý»wãàÁƒØ½{w¹ëØØØ`äÈ‘2dæÎ WWWDFFâСCøì³Ïзo_8;;còäÉhÖ¬ž={†ß~û Æ ôíÛ 6DçΡ¦¦†7ÂÛÛvvv¥öõù矣qãÆèÓ§¦L™‚””,^¼Ý»w‡««ë;?“ '@xùÒ±±@±Kµ¬Šñ³üq3iÜÝÝamm-jëÛ·/Ž9‚””¡ÍÓÓgΜÁ¬Y³Ð¿ØÙÙaÇŽèÝ»w¹Ûÿã?0wî\ìÛ·6l@£F0aÂ@PPttt°iÓ&¬_¿ êbtuuáíí-JÄ áíí-ºT§N´mÛV´_===œ9s·nÝ‚µµ5öïß=zó½¼¼P¯^½Rñ®Zµ nnnøõ×_ñý÷ßÃØØ­Zµ‚··7ÔÔÔ0lØ0lß¾óçχžžºuë†1cÆ(¬Z½z5öìÙ"‚‡‡6mÚ$l»U«VBÝ‘²²2.\¸€I“&aêÔ©PWW‡¿¿?¾ÿþ{ay‡R—ÃêÕ«OOÏrûœÉNBTÁ@µØœ9s0wî|ý5°d‰¢£aŒ½‚‚‚```€   E‡Â¤3f >|(*Pf…²²²```Pªþ¨¸„„4mÚ´V\6ç ìÛWxc)“Úð‹¦hÜÇŒ1&Æ bb€Ë—EÍÅÏ©’?îcV[tèÐA4  ceá íÝ ´n­è(j&®O‘?îcV[|þùçŠ} ø ýöPP è(cŒ1V8’Q|Eþ¸Ye…††âøñãeÎ?|ø°ÔAþÊóòåKlÛ¶ ¯_¿–iy"¶mÛ_©ý0& N€*aï^EGP3q}Šüq³Ê:pà~øáaÚÛÛ]»v¦¿ùæüþûï•Úf\\†ŠçÏŸË´|^^†Š{÷÷Ûo¿Ic‡1Yq P%8¬\ (qÚX¥¸>Eþ¸Ù»š={v¥S!GÅöíÛqüøñrGvf¬"Š5¿çÔÔr“£HLþühß^ÁA1Æ>X·“nãläY¹ïG[M_ºYæü{÷î!$$¤T»‹‹ <<|‡‚ŽŽ|||ð¿ÿýOæmxyy!22GEtt4 ñÓO?ɼ~`` vîÜ)™xóæÍ6l˜ðÄxÆä…  ØÛ?‚žÞ›égÏ€EfïˆëSäû˜•ç»ï¾Ã7°oß¾JÕù¸ººBCC`bb‚¶mÛâêÕ«2¯?hÐ dggãàÁƒ€°°0\½z_}õU倱·À—À* ¬œ‡=€;Þ´íÛt˜c®&fM0Þk¼Ü÷£§®WñB~ÿýwüøã¸xñ"êÔ©óNû´µµÅ… d^^OOýúõæM›Ð¿lÞ¼ü1lmmß)ÆdÁ úô'@‡k×ïÁ 5×§È÷ñû£U½VhU¯•¢Ã<|øÄêիѬY³wÞÞßÿ ‡J­3räHxzzâÞ½{رc6nÜøÎq0& ¾&ƒ? ÞL?œ9£¸xj®O‘?îcVRzz:üüüп :ô­¶‘““#üÿÖ­[øë¯¿*ý(Š¢¢æ~ýúA]]ݺu{«X«,N€d ¦øù‰ÛöíSL,5×§È÷1+iÙ²e ÃÉ“'ѰaCáç믿–yßÿ=<==áéé WWW|òÉ'1bD¥c ÄÍ›71bÄ©·Ú·zõj´hÑ‹/ƳgÏТE øøøTzŸŒñEõélÛöfúða`ýz€oT`Œ}ˆˆ¶mÛ–j755 >½zõÚgÏž-JN~ù嘚šâöíÛˆŠŠÂâÅ‹+LDêׯóçÏÃÌ̬T,öööpssµ«¨¨àüùóhÚ´©ÐöÉ'Ÿà£>-WQÒĘ4œÉ¨S'ÀÈHI)œ~ñ8u øôSÅÆUp}Šüq³’lmmË-6.9¯Q£F¢iOOO…I¬´´´¤&IRÛ%I©v;;;~«| LFªª@Ïžâ6¾ V5¸>Eþ¸cLŒ JèÓG<}ä­˜Xj®O‘?îcÆã¨:tLLÞL¿| œ<©¸xcŒ1öv8ªÀß_ÜÆ—ÁÞ]BB‚¢C¨ñ¸™4±±±HKK“Ûö322ðäÉ™—ÏÍÍEtt4òùyC¬pTIâéà` ØclØ[àúùã>fÒ´iÓ»ví’Ûö‹n±—Uxx8lmm‘˜˜XjÞ‘#GT•á±ZŽ Jòöþÿ.Q@Zpü¸ââ© ¸>Eþ¸Ù‡êÊ•+X²d qíÚ5E‡Ãj¾ ¾’”•Ï?/|F‘}ûJß!ÆcR=<}*ÿý¨ª%ÆË)OJJ ^½zUªÝÄÄ::: /Qݺu ÉÉÉpssÆ ’Eff&Q¯^=Ѹ=ÙÙÙˆG½zõ¤®‚Û·oÃÈÈHæ}1& N€ÞB@€8:zÈÌ45Ó‡ŒÇ¨‘?îã÷ÈþýÀ¨Qòß™P‰Ú¯… bÆ Ât~~>ÒÓÓ±mÛ6 2=‚¿¿?ÂÃÃaff†ÄÄD,Z´&Liû©©©pppÀþýûáWlhýU«VaõêÕxôè‘ÔõÆ/|pìÿþ÷?ܽ{Wæãa¬"| ì-´m ˜›¿™~ýøãÅÅó¡ãúùã>fY´hRSS…ŸÏ>û žžžèÛ·/€Â‘¡ÍÌÌ…èèhlذ_ý5®^½*ÓöÍÍÍѽ{wlÚ´IÔ¾yóf|ùå—PRâ#V½ø÷””€b#ÄöîUL,5×§È÷1«ŒeË–áìÙ³8xð ÔÕÕ‹/bøðá°´´„²²2¾øâ XXX`ÇŽ2owäÈ‘8qâbbbÿüó=z„aÆÉëP+_{K}ú+W¾™>v¬ð› mmÅÅÄû˜˜®®Õ³Ÿ·pþüy|óÍ78q⬬¬ ï+®uëÖÂE@@.\ooo¡½N: ¡¢¤>|ˆÆ˼}‰D‚ÀÀ@¬Y³ãÇÇÞ½{\uÀX%ð%°·$‘½{‹Ûø2ØÛáúùã>fÉÊÊ‚¿¿?ºté"qrr‚©©)N:%´ÅÇÇãÆRŸ(_ž¡C‡".._}õ,--Ѿ}û*‰Ÿ±Êâo€ÞAŸ>Àòåo¦/HWWq1}ˆ¸>Eþ¸YEfÍš…ëׯ£Q£F¢×K×®]ѽ{w,Y²_|ñ®]»lݺÍ›7ÇСC+µôêÕ ¿þú+–.]ZáòÛ·oGXX._¾ŒgÏž!((–––7n\¥‘±â8z-ZÖÖ@ÑHïYY…·Ä÷ï¯Ø¸cL&L@³fÍžžž˜={v©etÿÿ/ºÂÎλwïF\\¾ýö[Œ=**eŒ8;;cÆŒ¥Ú§OŸ|ñÅ¢v333Ìž=[Ø'¨ªªBCCݺuÚÔÕÕ+uœŒI#!"Rtï«9sæˆþ•fòd`É’7ÓÝ»>%žÉŽëSäû¸êÁÀÀ€ËÀ>8YYY000@V9ÏoJHH@Ó¦MkÅó¹èõé#ž>y2˜*+×§È÷1cŒ‰qôŽš7lmßLggüYS9\Ÿ"ÜÇŒ1&Æ P(y7ؾ}Љƒ1Æc²á¨ ”ûçôi 5U1±|ˆjõfEã>fŒ1±­X±mÚ´½½=† "à ___ÑϹsç*½ww Aƒ7Ó99À¡Cï}íÁõ)òÇ}Ìcb5ú6øû÷ïcÚ´iprrÂÙ³gáç燧OŸÂØØ@áïcÆŒ³³³°Ž££ã[í«O`Á‚7Óûö•£Öâúùã>fŒ1±­Y³Fø¿££#6lØ€૯¾ÚÛ¶m[êù6o# @œ9¤¤FFï¼iÆcŒU±} ¬¸ÌÌL}‰§NBÛ¶m±hÑ¢r×KKKCLLŒÌûÉÊÊÂãÇEÔ¦¥¥!$$#FŒÀÎ;eÞVMVôzþý÷ß1gÎøøøà§Ÿ~Bnn®b«&µb$èéÓ§ãÈ‘#¸páLMM¥.“šš {{{ìܹ¾¾¾d º¸;w—7ÓÊÊ@B`bò.Ñ3ÆÞ'ï:ôº¸8Œz𠊣*ÍLM ­Z•»Œ¹¹9fÏž‘#G";;***PVV®’ýgeeAMM JJoþÎNMMEll,>úè#¡­_¿~8~ü8R˹uvëÖ­˜1câââdÚ÷_ý…víÚáõë×ÐÒÒ $$ÉÉÉèÒ¥ 8ð–Göáâ‘ Åjô7@?~‹/–™ü€,--‘œœüÖûkÜhÔèÍt~>P Çc»wï¢C‡ÐÕÕ…žžfΜ)Ì:t(lllJýüóÏ?€=z`åÊ•ÂòNNN˜;w.Z·n èëëã‡~戒ðòòBZZšÌß:„††ÂÆÆááá¢öÍ›7£U«Ve–2ìÝ»ÑÑÑð÷÷—i?¬æ«± P~~>üýý… .À¤Ä×0=ÂÅ‹…éãÇãÑ£Gï\]òÑ<(bÅjÃ_ŠÆ}ÌÊrðàAôéÓ—/_ƈ#°páB=z@áâ>,ü899A[[.ÿÿUw\\^¾|)lëñãÇØ¶m†ŽþùÄŒ3páÂ…2÷òäIxzzBUUU¦xÝÜÜ ¥¥…Í›7‹ÚW¯^Ž;B"‘T²XmUcïKKKÑÿ*iñ' ;99!<<iiièÝ»7233a``€ŒŒ lÙ²666ï´ß>}€âWÌþüHLÌÌÞi³5ÚáÇù6m9ã>~4ÑÖÆx++¹ïGOÆKY³fÍ^Íš5ÃåË—±sçN|öÙg°³³–Ûºu+BBBpõêUèè蔹½… "àÿG‡õôôÄñãDZ{÷nøøø”ZvãÆ8}ú4Ξ=[‰#1þ|üðÃPUUEhh(nÞ¼Éã]±J©± A¹wu¹¹¹!!!ñññÈÊÊBýúõ«äÚ·³saÐíÛ…ÓE—ÁF~çM×XüÁ,ÜÇïVúúh¥¯¯è0ÊÔ¼ys\½zUÔvýúuŒ3{÷î­ôXi­ZµBddd©ö£GbôèÑX³f ¼½½+µÍÁƒ µ½zõÂæÍ›Ñµk×Rwù2Vž{ L‰°³³«²Â?€/ƒ1Æ>\)))¢oÍŸ={L:Ÿ}öY¥·—””~ºd IDATQÛ¾}û€õë׋Æe“•¡¡!°iÓ&deeá×_å$ŸUZ­N€ä¥dô×_€ w¤ÖZ\Ÿ"ÜÇLD„³gÏÂÝÝ——‡>}úÀÕÕ³gÏ®ôö222péÒ%Ñ>K–,Á°aðk×. 6ì­c9r$NŸ>eË–AOOŸ|òÉ[o‹ÕN5ö˜"9:M›7nN¿ýŒ§Ø¸ÞW\Ÿ"ÜǬ,ÁÁÁ°µµEff&Ö¯_ÌÌL|ýõ×€o¾ùÿüóV¬X½{÷ ëxyyÁÖÖVêö¶mÛeee<þ«W¯F:u0jÔ(Àž={0yòd 6 ‰‰‰X·n°^³fÍЬY3™ãöòòB“&M0kÖ,Ì;Wt»½4W¯^EJJ âââ••…'NÀØØžžž2ï“Õ,œÉIŸ>o Ø»— ²ð³üq3iZµj###L›6 111pwwÇ•+W`aa pÜž-Z`÷îÝ¢õôõõakk Ô¯__4OSS‹/FJJ Z´h…  EÓÚÚÚðööFDD"""Dëihh”™™››£eË–¥Úƒ‚‚°nÝ: >\Ôn``oooQR´k×.ܼy ¢¢‚… ÂÝÝ Z¬V „ø¶*;bq€½ý›i‰xò¨†›?crô®!Ödøå—_„»ÀØû…Bã 9iÐððx3MTxŒ•V~Ñû˜1ÆÄ8’£’»„Ίá±;äû˜U‡qãÆUú6yÆ… 9êÝ[<RxŒ‰q}Šüq³ê°xñb¸¹¹): Æd ÙØÅîþ°¿ÂÂaŒ1ÆØÿã»Àä, (>¨êÞ½À¤IŠ‹ç}”€ºuë*:Œû¸jýù矊±JËËËStïN€ä¬wo`òäÂoàÚ5 * (cZ‰Ç¨‘?îãªSôL«ÔÔTQûƒ`mm DU;¤§§#11 4Pt(¬)S¦(:„÷'@rV¯в%pùò›¶}û€iÓÓû†?˜åû¸êøúúÂ××WÑa0ÆÞ×U~6cŒ1ö~á¨ôêU8b‘ÿþ=R\<ï£Fþ¸åïùóç\c!g999HIIQt¬†à¨XZmÚˆÛø[ 7xŒùã>–¿óçÏãåË—Š£FKNNÆ¥K—«!8ª&|¬l\Ÿ"ÜÇò׫W/+:ŒÍÂÂÝ»wWt¬†à¨šôêXñÍ›ÀýûŠ‹‡1Æ«Í8ª&uëíÚ‰Ûø[ B\Ÿ"ÜÇòÇ5@òÇ5@¬*qTø2˜t\Ÿ"ÜÇòÇ5@òÇ5@¬*IˆŠ†èc%Í™3Gôï»JJ,,€üü7mwïUÉæcŒ±w’€¦M›ÖŠoù jdj üÿ ²þˆ1Æ«~œU³€ñ4'@\ŸR¸åk€äk€XU⨚ùû*Å@rïpû¶ââyp}ŠüqË×É×±ªÄ P536:t·ÕöoxŒùã>–?Hþx V•8R¾ ÆcŒ)'@ г' ªúfúÁàÆ ÅÅ£h\Ÿ"ÜÇòÇ5@òÇ5@¬*q¤††@çÎâ¶Úü-×§È÷±üq üq «J<P9ªz â~ùøâ‹7Ó ðâcŒ)ÄäÎÏPS{3üû¯ââaŒ1ÆjN€D_èÒEÜV[/ƒÕ†¿4ûXþ¸Hþ¸ˆU%N€ˆŸ VˆëSäûXþ¸Hþ¸ˆU%®*‡E=9ÆêIH=É O’ÈEGkg|"#]meeZÈjõ]¿T‘|ŠzrŒÕ“ z’ž$Ÿ:T»3ÌÝÆpÏ=¾éJ’OQOޱz’RO2@“¤òc×_¯äîùçA>Ì !„ÇG ?÷ê«Ð·¯¾íφ|Ó$Ÿ¢žcõ$¤žd€„'IäçbcµE=9ÆêIH=É O’(tî ï¼F·ÿÍC‡àÊ+µÁýäSÔ“c¬žd€Ô“ ð¤ .€þûßÿrúé§Ó·o_®¾új¾üòKÝöÌÌL¦OŸÎI'ÄÅ_Ìžb}âDxøa}Û_ÀSOù¦?í!ùõä«' õ$$<)¨  ÌÌLî½÷^>ûì3FÍ…^Haa!6›³Î:‹‰'²jÕ*Ì”)SpÂ)“<ôV5n[³Æ7ýB!üUP@óæÍk8ÃsÓM7Ñ¿–-[ÀªU«ˆŠŠâ®»î¢W¯^üóŸÿ$//µk×ê^£{v6lÞì‹î·›Ñ¨] ëÜÙÕVW§M•áÏ—Í%Ÿ¢žcõ$¤žd€„'u䮺ºš;wÒ³gOvîÜÉàÁƒ¶F Dff¦îy×-X  ³ ºvÕBу«-+ fÏöY—ŽJò)êÉ1VO2@êIHxRÈ@7ß|3ÇgêÔ©äçç¥Û'66–üF·NMÒ>ù„´‡&--´´4Ýv\Ÿ2E»=þH ÿû<÷œô¯ñº{>ÅúŒëõÇØ_úŒë3fÌà…^ð›þãú«¯¾ªËùº?Á²¾bÅ ÒÒÒ˜0asçÎÅf³ Î@½´Ñý÷ßÏÇÌ·ß~Kç#ׇ{ì16mÚÄÒ¥Kö?~<—]v7Üp }s¤ýóŸÚÆ~€ÓO÷zßU]L˜ ÏÿX,°zµ6¡ªBÑXnn.©©©!qÙ<¨Ï9n¿ývV­ZÅ÷ßßPütíÚ•¬¬,Ýþ¤[·nÍ¿ØÊ•*»êqf³6U†ûM)6Ìš%%¾ëWsBáÍ×ä«' õ$$<)h »ÝÎÅ_̾}ûøöÛoéÔ©“nûùçŸÏ¶mÛØ|$àüÃ?PRR¤I“šÁ+€@›,uÑ"}hß>¸î:Ÿu©Y’OQOޱz’RO2@“‚öXII :thÒÞ¿222xûí·¹ùæ›éÖ­ùùù,\¸éÓ§7ì«»f0hS­»ßb î¾[Ëÿ¸{ñE¸åßôG!„’K`~è믿æÓO?¥¼¼¼Mû'$$àt:›,õÅÀW\Aaa!Ÿ~ú)‡Ö?M8Ðh Å@ñä“Ms?wß ›6ù¦?B!„¯LT^^ÎOiøšcõ$¤žd€„'Ltá…²zõj yî¹çˆŠŠbÖ¬Y$¸ÿUWíË/cr­fœp,X oÛ½ŽÜðæS’OQOޱz’RO2@“*T]]ÍçŸβeËHOOçÌ3ÏdæÌ™\|ñÅJÞ/--¿?ñaV««qýz5JÉûyÃm·iùw¯¾ ×_ï›þ!„ð’òC¯½öIII,Z´ˆ‹.ºˆÝ»w³dÉeÅO½}½{ëô2X½¹saøp}Ûí·ÃÖ­¾éBá S=š«®ºŠŒŒ >úè#V®\Iuuµò÷ÝÝ·¯¾!À  ðpøàˆuµUWky ÊJßô)>iøšcõ$¤žd€„'L4tèPæÍ›GFF7Ýtß}÷dæÌ™Jß·I´v­ÿ$ØN}ûj—½Üedøî¶xɧ¨'ÇX=É©' áISìÚµ‹7ß|“… òùçŸSUU…Á}”?Šओ\ v;|õ•Ò÷ô†Y³šæ~Þ|S[¼Í}.0¡†cõf̘A¢ûÐëÂãRRRts q<¦z衇>|8|ðƒæƒ>àðáüÿþûêß|Ú4ýz€_«÷ßÿÂСú¶[nÑÎ !„Á,`  [n¹…ââb>ÿüsî¾ûnRSS•Ÿýi¤Pd¤–ŠŽvµUVjy /Ä«H>E=9ÆêIH=É O ˜(99™òòrž|òI®½öZî¾ûn¾òÖ¥¨  "µž“Û¶yç½0^zIß¶u«vg˜·H>E=9ÆêIH=É O ˜(;;›Áƒ³lÙ2:tèÀþýû¹ôÒKyì±ÇÔ¿yd$œy¦¾-HÎ\}µ¶¸{í5xï=_äSÔ“c¬žd€Ô“ ð¤€)€æÎËôéÓY¿~=sçÎeÙ²e¬^½šGy丧Ãh“ ½ V綾´³A>üÐ7ýB!T ˜(''‡I“&éÚ D§Nøý÷ßÕw qôþ8Gèh-éj«ÏýíoÚÍoªH>E=9ÆêIH=É O ˜häȑ̛7‚‚êêêX´hv»^½z©ïÀÀг§kÝj…ôtõïëEC‡ÂóÏ7mæ˜:Žz“|ŠzrŒÕ“ z’ž0Ð]wÝEll,;wfÈ!$&&rÏ=÷°xñbŒF/ý3‚ü2Àu×Á /h3È»ûúk96nôü{J>E=9ÆêIH=É O2ûºmÆŠ+ؼy3»ví"!!SO=•øøxïubÚ4ýÊAXÜz+¤¦Â¥—‚û•“ƒáôÓáå—aölŸuO!„8nsè©§žâ¾ûî#55•K/½”É“'{·ø˜4 Ìn5ãž=°{·wûà%§Ÿ®í;Vß^S×\£ ˜h³yæ½$Ÿ¢žcõ$¤žd€„'LÔ±cGòòò|Û‰¸¸¦AžHIo¿…›onºmÞ}úðüƒýû÷{µc8 nw5L™îƒ/nßÙÙ^í“?¸äX·ú÷×·Â9çÀSOù¦_B!ÄÑøuÍÕW_Í÷ßϪU«°ÙlœqÆœ}öÙ¼ûî»Þ™xõ`¦k%1FÒïbgê ¨A^¨o·Ûá¾û`Æ ¨¨hý5$Ÿ¢žcõ$¤žd€„'ù}ä®_¿~<þøãìß¿Ÿ;3eË–Ñ«W/n¹å6oÞ¬ô½·ÖÅPë~ìœsô;„hÚðH}ÿú—þÄÀ²epÚi°sgËÏ—|ŠzrŒÕ“ z’ždp:N_wâxäçç³xñb¶oß΂ <úÚiiiüs„†õWNèÄ_N¢­¬] cƸvŽ×f 5Ìì"J|ñ\v4þ‹ƒ\¾Bÿ•››KjjjHœ5˜3@™™™ü÷¿ÿ ªªŠû￟»ï¾‡ÃÁ]wÝåñâ§9óf¸VN9:vt­—–jEQˆ›:6lÐF pWV¦]&{è!ðQ–]!„h0ÐÂ… )((àå—_æ‹/¾Àl6så•Wz­¿ÖÅ`­ÿëm2ÁäÉúBø2˜»Þ½µQ¢¯¸Bßîtj—ÉÎ;Š‹]í¡ðIÃ×ä«' õ$$<)`  púé§°téRxàžzê)rrr¨¬¬T÷ÆÖ’†/íÆÞÎv; â·Ã·&2R»äõüóÚtî>ÿ\;öë¯ÚºäSÔ“c¬žd€Ô“ ð¤€)€:wîLFF‡fóæÍœzê©”••QVV¦ì}c õáêyûs­Lªßù—_À×¶ú™Ûnƒo¾Ñž °g¡zï=£Æä«'ã©'ã O ˜èºë®ã¡‡bÀ€̘1ƒ=z°aìV+ÉÿºzÐÀ|}A³¹.[}n¼kW6̵Ñé„/¿TÖ—@uúéÚî™qÐf¹ì2¸ë.°Z}Ó7!„¡)`  ¡C‡òÛo¿ñÙgŸ±hÑ"L&ÿ÷ÿ‡Á`Pö¾c‹#¡Îu†ÉnŒäÝl·1ä2X›¤¤ÀwßÁM75Ýöïç§ }íµð °z5”—{¿ŸÁJ2@êIH=É O ˜Àf³ñã?òÌ3ÏP[[K±{šVg,«õƒØÌÛï6*tãèË/µ3A¢ ‹æÍƒ… !"Â}Ërjkµ+ˆo¼sæÀgh# ôí —^ =Ÿ~ 99¾ê}`“ z’RO2@“f M›61yòd&MšÄ–-[ÈÈÈ`÷îÝŒ;–Ç+9 ”––ÀïSzðªõĆv³£šê‰Ó0 ÚT艉úÓë×7)Zèüò \|qû'NHJÒn³OM…áõǓNÒnÌBqìd ?4oÞ<î»ï>Þÿý†¶¾}û¦ü?êÞA“¡ÎUàÔ#y?çÈY!‹Î:Kÿ¹ vT#Fh¹ Ë.ƒnÝÚ÷Üü|m²ÕgžÑž?hÄÆj#Nßx#ÌŸ¯ ÉTU¥¦ïB!_À [\VVÆ€šÝ®ô½û$ô$©êCòãF6´½¸o+—w?2 è´iðñÇ®'¬\ >¨´OÁ 1ÞyGûÄa6'³ilÞìZ23µ9ÅÚ¢ºZ›“lÝ:W›Ñ¨r?[4t¨–]5¹¹¹JoZ(>>sˆ¯’Õj¥¢¢‚ŽîƒÐ qŒæ иqãxõÕWucþ¼þúë$$$xå‡áüÄxÝúk$öú«‡s@k×BI ¢m–/_N§NÚ¸’÷Ü£EÛ·kWþ^yE OÑÑm]‡22`ÉmbÖ©Sµ0v\œ6Ñå—Ã#Àûïk—Êá¤|M2@êIH=É O ˜ ÝnçÊ+¯dåÊ•ÔÔÔœœLuu5}ôcß_í!õ ´´4víã¤M¿Éõø½~)Ìêv’¶2`€vÊ¢Þ‡jS¡ r8`×.ý™¢M›àðáãmƒA»׿¿væ¨××'œÐt¢W!„6¡” ˜sµ&“‰wß}—;vðÛo¿‘À)§œB\\œWÞ¿_ÇÞ$V~Da£Ë` дiúhåJ)€0]…ÉÿèjÏÍÕD›7ÃîÝí›wÌé„ìlmùúký¶ðp펴æŠ#ûN!OÀ@ .¤{÷îLž<™àt:¹ôÒKY²d &/Üt^‡XÞtˤ¬³F`w:1 Ztd²V@‚Ðíà‰|Jr²ö_à~5²²R›n£¾(Ú²E»$v,‡×Öj—å¶ooº-1Q_Õ}â‰Záä$¤žd€Ô“ ð¤€ùIÍÌÌlr«»Á``ݺuÓ©S'å}¸ðÙ¼¹)LQØŒÑ,ÿ}/—¤œãÇkƒÛÔÔh;çäÀ¶m0dˆò~ºåË—+™ª!:ZË 5¾Bš› ;wj'ìê—;aï^8–qì µÉ_üQßn4j—ÎFýû·ÿηã¥ê —ôôt&Nœ(Óa(TPPÀ† d: á~ŸÚ°a_}õ+W®$))‰‘#]— vïÞÍÆÙ´i“’÷vÏÕKüß¿)ŠÞ°~†¥ˆïÇ]¬­L›_|ázgž¿þUIß„çÙlZÔ\q䉌‘»èh­(êÑC f§¤hE‘û£üBx›d€üH~~>›7o&//êêê†K]ƒáÇóÀxµ?ç&D³Øí2ØÏ5a8œNŒõ—ÁÜ  •+¥ ‹ë Íôéúm¥¥ú‚¨þë]»´[ðÛ«²R»,×ZíÞ´8j®PjÏqB!4~¨ÞƉ§oß¾^{ÏæÎýV´›Á›v)²¡íÿôäÂä>ZÀäH> €°0(*’¿PGÈù§²²š/Žôά(ññ-Gõ_C.=zæ1’RO2@êÉ ?´gÏÂÃÃéÓ§FÞ<¨c_:T­ 86µ¡í?»7iЀЫ—k~«ÒÓá¼ó|ÔÛÀÈùƒzöÔ–É“õÛjj´3DÍGž&ª´T[vìhm¯å„…ÝHd$Ê—ˆ0›µ©IÜ—`' õ$$<)`  „„î¼óNî¸ãn¹å®½öZ:tèà“¾œÉ»n·WÿT†0€vì•W\W®”è(µø9šˆmäé¡C›nËËÓòF99pèPÓ%'G+j<çF¬V­&÷ÕX}Fcó…‘ÉÔr{kÛÚÚn46] †Ö×­mï¼£_O|ÝÒ¶ÆÓ67¢'ö9Þç´ôõ±ìg4¦pÚiç“—ç:îCÓ¥¹ö–ÚDè ˜hÊ”)lß¾o¾ù†—^z‰Gy„Y³fqÛm·1´¹¿0 Ý;hïþºŒÚ=ÎVS,+ïcz—ÞÍ@B4Ò¹³¶´¦ªªiQÔ\±(sž9Z&„¿i©P ENg""®ðu7¼"`  zgugu|ð×\s ¯½öãÇgΜ9\pÁ^èäNý‰¯üœÒF—Á¦wé “&iiZ›MÛ°g6"Ÿ³K&3@*EEiß6GûÖ))9z¡tøp.µµrŒÕ*â À_«Ä TžÍ9®¼^[ç ^""Bàš5ö“ZYYÉ;ï¼Ã¼yóÈÎÎæ¶Ûnãšk®áûï¿çÁ¤  €¿üå/^éË´¸Þw ¸®©6i—ÁbcaÜ8øö[ׯ•+áÖ[½Ò¯@È  -ƒµ¼Ïüù˹ᆩ®¦a©ª¢Õõ¶ìÓÜsjj´ñ”ìvýüÒ‰€d€Ô)6’Ç/`î{çw¸ùæ›éׯ·Þz+³fÍ"""¢a»Ó餴´”„„½gswÕÛœŸÁð­.ƒ|6°7çtéO=¥Í¾YïÜsaÅ õKˆ@Ô¸ ª_š+–Zk?–ç8Ú%¸ú¥ñz[ÛÚ²Oý{¶÷ëö>§ñoîæ~“mŸcyN{_·¥¯g¿ú¥þø7^ško©M4w/¥¥OùºÊÌ :ðÅ_0zôèf· ?G“š4€¸Ê/(‹ÖÐöï=¿hдiú(=]›KÁ_æEÂBån0xZ+”BÍáÇ9í´·)€üÆþðŠ‹‹yùå—Ù°aS¦Lñ饓)qá,uûù¡úÈ-ÆA×®ðûïÚzUüðœ}¶÷;$¤žcõd õTäzõ"=<Ü „Ä5küþ&ÀììlFE×®]™?>÷Þ{/ß|ó ƒfîܹ_‚­³ IDAT<÷Üs>ë۽χ붖S<«ò³´•©Sõ;ËÝ`-Z¾|¹¯»ôä«—žžN©¯Æ¬^½Ú×ÝAÂï3@wß}7»víâž{îáÉ'ŸdÓ¦M>€øøx,Köððpjkk}Õ5îp8ëÖ«Í ¤dCÇŽpê©úÝ'J Bᓆ¯É1V¯°°ººº£ï(Ž™Õj¥¨¨È×ÝAÂï §ÓÉ7ß|Ã’%Køê«¯ÈËËcÉ’% ‹¯–<„˜Ê ]Û3»Ök_L›¦ßYr@Í’|ŠzrŒÕ“ z’žä÷ [n¹…5kÖ´ºÏæÍ›•¼w[2@¤Ïãƒkº¨ºb*Ͼ~þÜoÛ‡‚m²"!„ÂÏ„RÈïÿ¿ôÒKyuëÖQTTÄ´FgeæÍ›GYY™®múôé <¸Í¯}ï€Óù$# Úá¬2wà»ÂÆŸr $&Ba¡¶ci)¬] §Ÿ~|ÿ!„B¿¿v¼²³³Y°`W\q+›¹õøãóË/¿››Û°ÔÔÔ´ë=Æv=™è ýe°¹»Ök!èÉ“õ;Ëe°&Bᓆ¯É1VO2@êIHxRÐ@deeµ:pÖœ9søÏþÓ°Œ9²Ýï31Z(Ó+ŽŒ±.9 £’|ŠzrŒÕ“ z’žä÷—ÀŽWjj*©©©”””´¸OVV™™™ôèу¨¨¨czŸ{ŒaEf´aD+Íù±(—±S§jCŒÖG­~ùòò sçczŸ`$¡ª'ÇX½3føº A/%%…óÏ—‰P…gý £‰åÑGåœsÎ!))‰Ç{L·}Ñ¢E¤¥¥éwõëg¦ 'ª2-jØöô®ŸI›?_›£~§¾ü²Éóe]Öe]Öe]Ö}±¾bÅ ÒÒÒ˜0asçÎÅf³ üþ.0O¹ãŽ;øÏþÓâ>ëׯçÌ3Ï$==Ñ£G7|s4þ¦iÉÒçñ¹ÛÝ`1u…”Ÿ} Ü?<ù¤kÇË/‡·ßn÷¿!XÉlÜxLïŒ$Ÿ¢žcõf̘Abb¢¯»Ô$$<)è  Õ«W3{öl¾üòK¾üòKfÏžÍÏ?ÿ À–-[èÙ³'Ó§O窫®bäÈ‘\wÝuŒ5ê˜ßïŒ(‡n}U¹ ,˜4I¿£Ü &„BøLÐ_¬îÛ·/³gÏföìÙ m½{÷ଳÎbÍš5lÙ²…ššî¼óN†~\ï÷×~§ñåžr@›µÜ܉%yŒœ6 Ü/C¬\ >x\ï,$Ÿ¢žcõ$¤žd€„'ýOjrrr«¿ø|Ì—¼š3¹Ç("¶Ì§&f@CÛÜky¯qè§Ÿ ¨H›45Ä-_¾\.Ñ(&ÇX½ôôt&Nœ(—Á*((`Æ rLxDÐ_ó…Ó#íºõ•e5Ы pE8ºÛáC™üaVOޱz’RO2@“¤RàÎ~£×]^%æÎl//’Q¡…B?!èyá;umOd¬iZ}ñ…ÜÌSå rŒÕ“¹ÀÔ“¹À„'I¤ÈØýHšŸ—VÃøñéjÌÍ…-[¼Ü3ÿ#cÔ¨'ÇX=H=Hx’@ŠÜÙw$î—ÁŠÌɨ«‚ ô;Êe0ɧxcõ$¤žd€„'I¤Èy'Œ&¼r®í‰Œ%$„Bø)€1``tx­®íÓ⊦Ð?By¹{æ$Ÿ¢žcõ$¤žd€„'I¤Ðí}Rq¿ VhéÂînáÈ@ŒØlðõ×Þ|ŠzrŒÕ“ z’ž$BöKXå^·ïX-—Á‘|ŠzrŒÕ“ z’ž$B œ^£kû_q¹@B!„I¤Øœ>Ãtë–dö=E› µÞ‘áåžùɧ¨'ÇX=É©' áIR)vIŸqXª];ð œ~º~Ç> $ùõä«' õ$$E=9ÆêIH=É O’È‹®ì7su¶«Á`âß©=õ;}õ¼ô’w;æc’OQOޱz’RO2@“¤ò"“ÁÄ0S‰®í­p ¬ßñ®»à矽Ø3!„"´Häe7ö [Ï1'S°ä]ˆ‰q5Z­pé¥PPàåÞù†äSÔ“c¬žd€Ô“ ð$)€¼lv¿ ˜jr\ 3OØsáõ×õ;feÁe—ÃáÝú€äSÔ“c¬žd€Ô“ ð$)€¼Ìl4s²Aÿ æÃ¼<øãaÎýΫVAZš÷:ç#’OQOޱz’RO2@“¤òzê/ƒe™“)´VÃܹ0fŒ~çý >ûÌ‹½B!‚Ÿ@>pmÿ‰˜j~w5Ì<¹,øðCHJrms:áŠ+`ÿ~¯÷Ó[$Ÿ¢žcõ$¤žd€„'Iäf£™!ÆB]Ûû‡üêÖ Þ{O?Btq1̘µµ^ì¥÷H>E=9ÆêIH=É O’ÈG®ïq’ný ± Ŷ#ΤIðÈ#ú'lÜ·Ýæ¥Þy—äSÔ“c¬žd€Ô“ ð$)€|ä/ý'b¬u]–p-<“éöÉæþûaútý“^{ -òN…Bˆ &XŒ£çç½ÜC®ƒÞz úôÑ?ñæ›aË/ôÐ{$Ÿ¢žcõ$¤žd€„'IäC×öè«[?`ìBYÕÕK—BD„«­º.¹‚(k ùõä«' õ$$¼éÜ`{öÀUWiwˆɧ¨'ÇX=É©' áIRùP˜)ŒäéÚÞÈÍ£IYóç?õ×êÛ>ùžzJiÿ„Bˆ`%]ÓMŸñÉ1'sݯ?4Ýñŵ³Aî|ÒÓöÎ;$Ÿ¢žcõ$¤žd€„'Iäc· œ„¥b—®í¢:Þ<´_¿cD,[¦å‚êÙí0k:D “|ŠzrŒÕ“ z’ž$E˜ÂYÐ7¬îw„¸vçn6WTèwîÝ/Öî«——3g‚Íæ•þª ùõä«' õ$$E=9ÆêIH=É O’ P+|‘r÷Ôš§¸oÇ:è{›®ýÜÄD>2£ûx@pÊ)à>vP÷îðË/”ä¥ !„d’~áoãþÆÙáeðû§ºöO ¹ß>ýÎÀ‚ú¶ìl¸ì2h|½Bâ¤òc ,¾h1I9ï@éVݶ§äíÇõO˜9n¿]ßöÕWððÊ{z|Bᓆ¯É1VO2@êIHx’@~.9&™7/\€aû? V?süõ™™¬++Ó?á™g`ìX}ÛãÊŠ{zì$Ÿ¢žcõ$¤žd€„'I¨¾Î¹»ûË»ynë'0üE0†7´§„‡³~ÄRÂ]määÀˆÚݺi·Â›L®¶âb¸ä¨©ñ^§…B?%P€3…±dÆbJ7ÂźmëË˹.3Sÿ„³Î‚ýKß¶iÜz«âž¶_(|Òð59ÆêIH=É O’(€ôëØ—þðì_?è¶½sø0O<¨½÷ÂùçëÛ,€7ÞPÛÑv’|ŠzrŒÕ“ z’ž$ VøSÈÝ]Á;Û—Áˆ— ºOC»Ñ`à“!C871ѵsI Œ{ö¸Ú""´yÄRS½Øk!„þN2@¯½|ÞËœß ¶=6×'N‡ÓÉe;v°£ªÊµsB,]ª=õjj´øàƒ†¶I“&qá…rÛm·5|s4þ¦ñw³—ÏæÍ-o‚1†¿1®[á Àÿ Â:¹žðâ‹pÛmúùãµËa^0þ|n¼ñF¯¼W¨’c¬ÞÒ¥K™8q"‰îSÑ:tè6làüÆs ‘K`!"))‰ÂÂB][^^]ºtñQ<ãÅ?¼H¿ŽýÀQ Û[qÃ6'pÅŽl«¬t=áÖ[µ[áݽÿ><¯`Qùìžcõf̘!Åb)))Rü éèì³Ïfݺu EPvv6»wïfüøñ>îÙñ‰ ‹aÉŒ%„™Â 6¶=¤›.£Ânçü­[)t?ÍùÚk0hþ…îº ÒÒ´±‚„B„„P) ̾î€j«W¯æõ×_gݺuÌž=››nº‰ÓN;þýûsÕUW1nÜ8&NœÈªU«¸çž{þ Àˆ®#xòì'¹ë‹» l;ì|úÿ­aû¾šflßΪaÃ0  Ë–Á©§By¹¶“Ýÿü§6RôÛoCŸ>-¼ÛñÉÍÍ%99YÉk cõ ‰Çlú_«>cµZ©¨¨ cÇŽ¾îJàp:µ‰¯óó¡ Àµ¸¯»}Ý%?Ÿ+}Ýg/ ú Pnn.º¶Aƒѹsç†õ5kÖ™™ÉÉ'ŸÌ¨Q£Ú5Tω“óÞ=Ïv}¦5ô½º]¢Û禔æt’«áõüOão‹ØXxá¸új÷Sò)êÉ1VO2@êI¨ª:j£[/,l÷Yü{ãâx*Æ´ úèxz_•ϰ—‡ñ{Åï€N~ :ŒÒíóòI'qcJŠ«áË/aölø]?ª43gÂ+¯@B‚Ò~ !DЩ®†²2(-Õ?6×Öx[i©VÐTWý}ŽS¨@r®6È%E%ñÖEo1eñœ8à·G`Ä<ˆtÿ3g×.FE1¾¾¨™2~ýUèãõ/øÁڨыC€g¥„¢Ml¶c+Z·Èíå¡’ú”3@­†3@õîÿú~ž\ý¤¶ÕF¼ ¦¨†í,ÖIïˆý_yE CWUéÛFøÛßà‘Gtã ɧ¨'ÇX=É©çñ PM deÁÁƒú%+K[ŠŠ´ÂÅ}*¡@”:ARRÓÇFm‡†~zHÜ/?©!âщ’¾/Ÿs~†ªƒÚ™ ¡OP?]FÍÆ[·òãˆÄÔO—pà 0a\vüò‹«Ýá€'Ÿ„U«´¥ÝsDí´|ùrɧ(&ÇX½ôôtÉ)VPPÐö ù¹M ÷õü|õö4‹¥ÍÅ II˜Ø®©Î(|êÉ VÓ €}%ûHŸJYm™ÖÐcô¹A·Ï…:ñÑ!n³ˆa³ÁCÁ3Ïh¿XÜEEÁ¿ÿ ù‹²¾ !„NYYëÅMv¶ÿ]r ƒ¸8m‰×?¶¥­cGíQ¡PQÎ…Þ ½yå¼WøÓ²#ƒf-è>ÐerÃ>Ë xxß>íÝ[ÿd‹E;ã3m\y¥öË¥^U•v¦èóϵñ„ÜG™B6§S›§>ÇÒÜR]­Ýid·kšûºµmmÝÏn×îj:xPËÖx‹Éä*HÚZ¼4·­qÄ@ø”@!fÖY¬Ú»Š76½¡5ìœ Q= v@Ã>ÿ:p€¡ÑÑÌt* Á„ Z@ú†´[æÝ-_?ÿ o¾ “'7}n $Ÿ¢žcõü.TW×zÑRV¦ùÕ–}üäB¨<6 É]»BϞУ‡öèþu—.Zñí©w~ÄO~R…7=Îó¬9¸†ÌÂLpXaÛƒF½ŠÓâúµrMf&ý¢¢Óô:tÐî[´æÌq œÚ­óS§ÂwÀO@xøQû#ùõä«ç• PQ¬]«}Ð(,<úY™ SlÚ< PÇŽÍ6õ))à/«ð:ɵ"Ø2@î6çnfô룩µ×j ±0‡ÁõË 9,Œ·dR‡-¿Ðž=pÅÚ/åÆN>Y HìáÞ œNÈÈ€t-™™~s6Æç""š4‹9sÓn’A/59•§'?Íí+o×Ê3pd> þÞ°O®ÕÊä-[¸»GëÝ›0c33Äœx"üð<ú(<ö˜~ÄÑ_…Q£àé§›Î6/„Ы¬„uë\ÅÎO?AqñÑŸç ÑÑú Lã%:Z»œd4jmýúXž ½ziw8 q¤ asN›Ãª½«X±s…Öpx•Šî1«a'07+‹¯‹‹ywÐ DE5}!³Y›3lÊílÐþý®m55Úe²Ï?‡… µkêH>E=9Æêµ;´¿þìί¿zvâa£bbZ/\š ö6^bcµÂÃÈ\`“¤ q /XȰùÃ8T~HkØû*1&5ÝfRçvª}SE#7nä¹Oä÷i3Ü[¶À-·h“§ºûüsí’Ø‚pÞyºM’OQOޱz­f€¬Vm-÷‚§¹©fŽ&"FŽ„1c {÷Ö š˜04Ð" µk !ŽB2@­æ »ôýéœýÖÙ8œ®ñ}Fú3y=ÿÂÞf‚”çwêÄ‚þýéÔÚàZï½7ÝÔü­ª7ß sçBd¤'º/„ÿ9|X_ìlܵµíäd;Vûp1v,Œ¡%#„"’!eâ ¹ÿôûyì‡ÇÚÖþöuJfò4ÞjôƒðIA'—•±hÀ¦´t*úOÒ~a_y¥–r7o¤§kéÔTOÿs„ð®ÒRíòÕ–-ÚÝY?þ{÷¶ÿuL&2ÄUìŒ ÇãBxŒœjE¨œ¨sÔ1~Ñx~Ìú±¡Íb´ðòy/Óm:7îÜII]î9àŽîÝy¢O› Hƒ6ˆÙO@Zš6.‰»°0xì1r/¿œä®]=û:’ò§vïv;õËñ´ãSe|<Œí*vN;MËÛˆIH½P:ÔÂ_-jÌF3ï^ò.  m6‡ë>¹Ž5ÿÅÆ©œ™ {Žøwv6§ýò ¿UV6ÿÂF#<ð€ö©¸_?ý6«åg+Wj·Ô7.’„G,_¾Ü×],åå°f ¼ü2Üx£–¹‰Õæ¼›1C»ëñ“OŠ€t Õ±‰ûö…«®‚ùóµ"ª¨Hû¾øa8ûl)~Ú   €Õ«Wûº"HÈ V„Ò zK[ÊÌgâDÿmqVï³X2ã}^˯äû÷ëÒ‘F#Ïœx"·tëÖò‹WVÂí·kAè–˜ÍÚiÿ¾}µ¥_?×ã 'È e³œNØ·Ïu6§þìξ}Ç7ÞNx¸6DýÙ±c¡¹‘Õ…ð3¡tH  V„bðÁö¸æãk¨²UéÚûtèÃdz>¦*¢'—ïØÁîfÒç&&òFÿþtn-¨ùÑGpýõÚ'àö0›µñ?Ü‹¢úB©wïvÍx,BPe%lݪ¿|µu«~$óca2ig†† s=#GJXY¤P*€äã´hbæà™œ”x,¹€ƒ¥Ú÷ïeÌ‚1,¾h1›FMgή],lôCòia!'oØÀÂ8§¥ëô_¬å®¾¾þš\ M锺:í2Ùž=M·™LúâȽ@êÝ;äÿMÈéÔ&ß­¬l~©¨hº¾s§VììÙsü£('&jÃ9œ|²Vðœ|²6ÒyD„ÿÍ„$$©ïÚ¥ýñÞµËõµŠâ¨>sT_¹G=zh!î@P[ ۷æMÚ²y³vÖ£¢Â×=ó:鋜ú³:m˜ÈWˆ`JP~ôžb1ZxùÜ—Öes>ŸƒÍakضlÇ2vífù¬å|=lOgeñð¾}ØÜŠ”m••œºq#OõéÃmÝ»sÔqi##µ±P† iº­ºZ+„ê #÷éСc+Žêê\¯ÑXD„6×Y}Aä^$ùò¶ý’­¸q/v~ûMî ;³ hz «¥‘Í…AMεBÎé}à{f|0ƒüª|]{§¨N,¹”ñ½Æ³¡¼œËwì`gUU“çOëØ‘EÐ¥QÇ#ù”ÆÅ‘{t¬ÅQkbbôE‘û£'ó ÙÙZã^ììÛ×î—isÎÊßÚÄ›11ÚcýÒÚz—.Z±3p Ò³:’RO2@ê…Ò )€Z!PSKrÁ’ Øœ»Y×n1Zøï9ÿå¦Q7Qi·sÇîݼÞÌ\GI o Àyn9‰ùóç«§ªªÊuY­~Ù¹S{TñCÞ±cóÅQ¿~-õâp@ffÓb§ àøú’˜©©Ì7¸qôh°Ù\K]~ÝSmv»v&¯=EJãõ–¶EDßñPH2@êIH=)€ PKªlU\óñ5|°ýƒ&Ûny/üá,F åçsýÎÙlMö»¹[7æžx"‘¾§b IDATÎØ”—7_íÜÙþÛôÛ"9YÀÎÎÖŠ­[µBíxôêÇkKjªöØ£‡gú-„ ¡TɹZÑnQ–(ÞŸñ>ú ã¡ô‡t“¨¾²ñ~Ëÿe\ÆÅIIœÇU|S\¬{y99|[R»2,&ÆÛÿ—ØXm‚É#šn+*ÒEî_ëØ1¹¹Úòý÷Gß·%f³v9§¾ÈIMÕ–Žý5…"ÄÈ VÈ £[±s—t9eµeºö^ñ½X>k9©É©8¹YY<¸oV‡C·_¸ÑÈßccyhøð£¤ýInnÓ¢¨>wÔÌ‘Ç,:Z˯¸;C‡¶;Ë4ãù1É©' õä mtÞIç±öºµ\ðÞì*rÝIu ôãÞÇ¢ qé K¹§G&%$pÙŽdº]ê©u8øÇâżT]͘¸8ÆÅÇ36.Ž‘±±-ä’“µåŒ3ôíN§vY«¹³F{÷j™–tîÜôVß¾¹ùòåjsV‚ôôtÉ)VPP  á1r¨r¨íJjJ˜µt_ìù¢É¶Ï|G&>‚Uv;wîÙ뇵úzaF##bbϸ¸8ÆÆÇ“è£9Ûí°¿«(Ú½[+¢ê‹_ÞZ/„„Ö )€Z!PûØvî]u/Ïþôl“mô¿€Å/&6L» êã‚®Í̤°µ3"ôŽˆ`ì‘3DcããÉP΄¯…RäÇ×D 1LÌ2—·.z‹³þvå3?fÌëcØS¬ÍãuA§Nl=åf$%‘ÐÆ@ñ¾šÞ9|˜[víbø† tX½šÉ[¶ðýûù¢¨ˆR°E¡ðËÌ× ©“ïA¥¬V+E*îÎ!I áqWž|%ß_ó=Ýbõó&mÏßΩ¯Ê×û¾ kXÌã99ì8õTôïϵ]»20*ªMèr»¯Š‹ydÿ~¦ýú+׬áäõë¹qçNÞÊÍmv¶úPµ|ùr_w!襧§SZZêënµ‚‚V¯^íënˆ !—ÀZ!—ÀŽOnE.½k³×êÚMÏM}Ž9§Íiñ¹ÅuuüTZÊeeüXZʺòr*íöv÷¡sX˜.\=$:šX“ £\:Bˆ&Bé˜Ü&”IŽIæÛÙßrÓŠ›X¸yaC»Ýiçö•·³åð^>÷eÂLMÃÍÌfþ˜ÈŽÜQcw:ÙRQÑPýXVÆšš£ö!Ïjåã‚>n4¢r”ÉDŒÛÛh½µö–ö•¢J!‡@B©pS8o\ðÃ’‡q÷wcwºÎâ¼±é väï`þ„ùœ|âÉ­¾ŽÉ``Dl,#bc¹µ›viíPm­® ú¥¼\7kkªìvªìvòŽýŸÖıUC££95.΃½hžŒ¤žŒ¤žŒäYuŽ:*¬TÚ*µGk%sbiÿÙö@$?©Â+n?ív†tÂÌgRTí 1þ”ýî›ÀCw?DÿÄþôïÔŸNÀd0õ5SÂÙ‘”ÄŒ¤$j6”—³æHAôSi)ùí¸ËìxkQuv‡<Ø«ã”ô d oq€Ô Åq€êuXíVjíµTÙª¨°V4+ _)`*j+)¶ÕPl­¡´ÎJ©ÝFy]•v'•UN¨q¨q°bÂn S$˜£´G“öyÞX_ÿ³½B2@­ çí-ÞËK.`[Þ¶÷ 3…ѧCú'öç¤Ä“8)ñ$úwÒ¾îÝ¥]ï·«ººá ÑšÒRöÕÔPm·ãßô§ÇÇó@¯^L“O·ÂÔ9ê(¬.$¿2Ÿ¼Ê<ò«òɯÌox̫̣°º»ÃŽÃéÀ‰‡Ó¡[œN}[[öin?§Ó‰Á` ÜN˜)Œps8á¦ðfÃLa-nkëó °Ú­ K­½V·ÞÜR[׆}½N­ÃN &j±`5„a5X°©3„ã4Gƒ9ÌÑ Šk‰Ò0FÏLñÙËT?ý¾G^ËŸÉ áU}:ôá§kâÊÿ»’åÍß™dµ[É(È £ £É¶¸ð8­ jTõëØ˜°¦sŠõ‹Œ¤_d$W»]þq8T9TØíÍ.•íl¯°Û©t8šLóÑ^«KK9ç×_˽zqA§N5=ˆ6‡‚ª‚†"&¯2OWÐ4n+®.Æé—üˆ)Lõ…Ê‘b¥áë0Çk–ˆˆnº±}SÛÏHx]LX ýñ#Ò¾MãÑïÅYá„6·ZV[ƆCØphC“m)±)Íž5êгÑõ­n428žds:ÛU8m,/ç«F“Äl(/ç¢mÛͽz13)é¸Ö’RÏW «ÝÚìY™–ÚJjJ¼Ú?²V ò(ûÌÚb¬´¸µYÀ`j¦­™ýMZÒjqM°(c¨ ^r ¬r L½5Ykxî…çH<#‘ÌÂLvî$·Â³·_ZŒzwèMÿÄþôíØ—^ ½èß“^ñÚc§¨N}¿öXWVÆ¿à……-îsRT÷÷ìÉ]º`>ÆBhþüù’RléÒ¥É9œŠª‹8\y˜¼Ê<ò*ó8\áúº~©?SÓx"â `0CDˆHÖ/•°/ÆœÒzAÂÂpnpipe4mÔ>ðÅšÍÄ™-$XÂH°„ÓÑA‚%¼á¦ŒX³™“‰Úâbþ8a‡÷íóõ?E9)€Z!o”Õ–±³p'; w6EõK…µÂãïe‰ÒD=ã{ꊤnqݰ-_w[**xìÀ–àháGò„ˆîíÙ“k’“ ÷ç‰bE5u5-4Û ª twKúšÅh¡ST'’¢“èÝ™¤¨#në   Æ&‹Á`hÚ†»ÓÀa;²98dsc³“cµ“c³“m­#Ïfoñç ™ bŒâM&âÌ&Ìf:XÂè`±`¶`6¤H‰mt7©{m4zälq¨Œ$P+¤ò?‡Ê5E™®âh_É>êj¦!0Œ¤Ä¦´Z$Å…{æVöŒª*?p€÷òò¨káG3%<œ¿öèÁ ]»åáËx¢íœ8É«Ì#»,›¬Ò,rÊs8\q¸ÙB§ÜÚ¶é^¼Ád0Ñ)ªS“"¦¥õ„ˆ íL£YÖÖ²¿¦¦Ùåw«5¨ œH£‘x³Y+TŽ<ºo2é×íc2ùMÞO Hä-žÈ§Ø6öïÕFª.©5'><¾É¥µúõžñ=éÓ£¡ígmöVWóäÁƒ¼yøp‹áê$‹…;{ôà–”⎒;‘ PûÔ7Y¥YZS¦=Ö;ÙeÙä”ç`µ[µK.æ°FB˜å`÷Þ4,Fƒ‘ÄÈÄ64#;¶» qåuu×ÕQÒÌ’o³y§À©«ƒêjˆmu7³Á@˜ÑH˜Á€Åýë#aF£Öîþu£ý-‘FcËE[qDgd¥€@Þ¢:Ÿâ~ImÉ~–ä`éA”à`éA%—Õ³-tëÞ¤H˜4~ûÑ9ºs³ÏË®­å™¬,^;tˆê ¡³™9Ý»s{·nt´4©N2@.Nœ®8LvY6û˲Ø]ú;{Ë“UUÌ¡ªRrk+(²ÖRg ׇ^MÑnØhW0Öxd$óテáÃ!.œu`+ƒºò#eúuݶr°•jG §˜°:Gw¦Ktí1F{to«/h#ÛT\WØíº¢¥¥b¦ØfkÒVj÷î%)£Á@JX½""8Ám‰-)áðÖ­L9ï¼ ‹Ñè7gS‘@(TUé ¢ƒ¥9Pr ¡ípÅaå·LJÇ7ܽæ¾ôKìGlX,‡­VžËÎf^N-̉c2qs·nÜÕ½;]šN/ŒêœvU–U]BNU¿×Vr¸¶ŠüÚ mVŠmu×Ù(®³QnwPå4`Åâ[ÅÏn?6 £ÅB'KÍf:Z,t4›I<òØÑíÑêp½˜©/`êêZ¼¤ê Fƒnaa …MãB§GxxPU $¡T…v\^ cdG:Fv$59µÙíµöZ²Ë²uE‘{‘”U–EMÝÑç%kMim)ë­gý¡õM¶%Ç$7Dí8Qƒù¬*‚r»þZ…ÝÎÓòBv6×§¤pOt÷¯?ðõìN;åµå”Õ–QZ[JaM‡ªËù½¦‚çïù”pS8'v8‘;œØìöú¼Hã3Gîg“ «[¾Íýhr+rÉ­Èåûß»MQº_Œ±ûLìf}¢Úáàùìlæ:Äìädîíу¨ÒÒc>ÆNœTÙª¨´VRi«Ô}]i­¤ÜVM©µŠRke¶jÊlµZk)ª«£¤ÎN©ÃA…Ã@•ÓD!ŒZCvS˜cµÅwäLLÄ‘åÚo(?ÿ-e6´ ke%uQQ9-^® dQG‚¼Í-I‹®Èé~ÌC6´Fæžäç¿ZD(ôyª èÝ….Ñ]8%å”f÷©´U6{ymWá.v¦é€ˆ­²Wá<ð6ö¬!å|èñGÓ?cu8xõÐ!^?”C¯pê%㩪³Q娣Ú^GÝN­ÃAÓÕáÄêtbs€Í` Îi vŒ8ÆZ±hÆ0·õhšŒbiÂ,~ÎXœV" bMF:˜-$†…Ó%<†Ná‘ aW÷»xâ`ëïÄ[ºt)GŽ$11‘j‡ƒ"›¢ºº†ÇB›M×Vè¶­Èf£ÐfSZ8µVÀmñ— o(Î&Ô‘ P+$$¼¥°ºP7ÞQý²»h7U¶ª£¿€Á]ÿ=ÿáí›/-à9j1ÕUbvTî´(ƒƒƒ8“Ž–0R"âèݑޱIôïJJd|ø*þt!¦Æ­p*l¥€*©«#üÈJm]ä’“h É !¼*12‘1ÝÇ0¦û]»'9e9ÍGº±œ68ô1ü¾ºLž—AdwüKŽÁéÀì¬!Âi#Ñ;±Fˆ7ép$ô›d §sDÉáQt‹Œ£{t<="ˆ4ϯ±£‘”ðpRü4»%D0 žß"`ù{È— è×îqÝ9«÷YºmuŽ:ö•ìk¦8ÚJκ«qvž=¯€èÞPTŠr˜Ž,f§‹¢ NbMZÓÑl&1,œ¤°pº„G‘K׈ô;:˜ÍGÇ(øj.°P" áIò“*|.Ð3@¾b6šé×±ý:öãÜ~çê¶UÙªØ]´›ÌÂ|\Á×ß­!é¼3 7‰4™µÅh"Êl!ÚF´)Œhs±–m1‡I\Xñ–H"Œ&ÂFm17 ;òõñ½,ÒÓÓ=2˜h™d€„'I¨’BJB)äûX¿B!„—I$|.>iøšcõ ©«S3!¯ÐX­VŠŠŠ|Ý $¤>·|ùr_w!èÉ1V/==ÒÒR_w#¨°zõj_wC ɵB2@B!B‰d€„B!‚˜@ÂçBᓆ¯É1VO2@êIHxRH@YYY¬]»V·dddøº[!ç­·Þòu‚žcõ>ùä }Ò[qt¿ÿþ;+W®ôu7Dé_zé%^ýuºwwM0nÜ8^zé%ö*ôTUµa®+q\ä«W[[‹D*Õr:X­V_wC‰>0cÆ 6oÞܰøªø)**:®ìö\âh˾­íÓÒ¶æÚ·Ùívòóóúþ*ÔÕÕ×'ôöüUUUQ^^Þê>ž:Æ%%%ÔÖÖ¶ùµU;ž÷nïÿÑÑÞ«¼¼¼Å⯥ÿ£ŠŠ ***tmÍ}ßê1nïó}ùû¢-?Gª4÷}Ðí9ÆmùÝ¢êíµƒYÈ@þâ“O>áСCÇüüùóç{tßÖöii[síÛÊËËyçwtmÇóK¦= øðÃùùíù?úõ×_Y³fM«ûxê¯\¹’}ûöµºŸ·ŽqsïÝíý?:Ú{­Y³†_ýµÙm-ýmذ 6èÚšû¾müÞ•••^ËÏ1nïó}ùû¢ñÿ‘Íf£²²ò¨ýñ„æ¾Ú£=Ǹ-¿[T㣽v0 éÛàï»ï>^|ñE¢¢¢èÔ©—]vÿûß1µº0--%K–0`Àå}ÉÍÍ%áÿÛ»Ó ¦Î· à$e ‚²DDTT¨€hµ b¥*ƒZVë80.£ŽVj)uÃA©[Q«0j-®u[kên‹Z7dߡʪ¢€(p¿ÏûÁICîß'Îóœ“sñ$s¸9ˈÅâVmŸŸŸ;;»6[÷ŸÖy]_sí¯¶544àþýû —oܸ—eÏž=CEE¬¬¬Zµý›¼GÕÕÕhhhøÇ/ml«1.++ƒ¡¡!ôõõ_»^{ñë2¶Ô›¾Gÿ¶¯ªª*ˆD"H$¥¾×½G>˜˜˜mÍ}n_Ýwzz:lmma``ТìoãmÆøM·WåñâÕ÷¨ºº÷ï߇££c‹²¿æ>oâMƸ%Ç–w5Ư¶Õ××㯿þÒˆ³B]@[[:u­[·‚Y³faÙ²e^Ðø¦hÆcšD,Ã××WÕ1Þ9.€^µvíZœ9s§NRuÆcŒ½CýXSS“p¹ ѵk×m[ZZЍ¨(üý÷ßèÛ·/¾úê+têÔé]EÕhUUUX·nV¯^­ê(ÒñãÇqôèQa9""BáRkçÎÃîÝ»!‰øIÓwàÌ™3Ø»w¯°Ü»wo,Z´H…‰:¦Ó§OãðáÃ022œ9s`kk«êH­¦Ñg€ÜÜÜàëë ܾ}Û·oÇ… Zt¯Dqq1ÊËËÑ¿„‡‡ÃÁÁ!!!íZ³Ô××cúôé¸}û6îÞ½«ê8RTTìììàåå°²²âb¾Éår¬Y³±±±°··W¸_‹µêêja’Ä´´4ìÛ· *NÕ±í°eËaŒW­Z%̧2þ|,Y²¤Ù›XYó.]º„ÐÐP¥öÊÊJL›6 ½{÷ÆG}„?þøCè+..F~~>?-”••…Ï>ûL©½¶¶³gÏFŸ>}0bÄaÒ¾7"00ׯ_GIII{ÇU[3fÌ@ff¦R{BB<==1`ÀDDD ©© ‰vvv°³³ÃÎ;¡‚ÄêgõêÕ8vì˜Rûï¿ÿŽQ£FÁÉÉ 3gÎDuu5ôôôPYY‰ß~û <@=T¸ £ØØX:t( ÌÌL…¾}ûöQ÷îÝéêÕ«tûömrvv¦o¾ù†ˆˆ)44”¦NJÙÙÙªˆ®6Ö­[GC† !JcõÓO?‘T*¥k׮ѭ[·¨oß¾´iÓ&úúë¯éÈ‘#DD4xð`UÄV+EEELæææäãã£ÔïííMsçÎ¥¼¼<Š'###ªªª¢³gÏÒæÍ›iÙ²eäááAõõõ*H¯ª««iΜ9dkkKNNNJýÓ¦M#™LF999tèÐ!244¤ÌÌLrqq¡­[·ÒÁƒÉÓÓ“RSSU^}|ûí·äååEèÚµk }'Ož¤®]»ÒåË—éîÝ»äîîN+V¬úår9Í›7¯½#«£GÒ¤I“H[[›¶oß®ÐW\\L†††´oß>ÊÉÉ¡©S§’¿¿?566ÒÂ… iÁ‚äèèHçÎSQú¶Á]»vär9‰D"ÊÈÈPè7nÅÆÆ ËIIIäâ⢰ÎÑ£GéóÏ?o—¬êêêÕ«$—ËI[[[©òõõ¥ 6Ë»ví¢AƒQpp0QPP™™™Ñ—_~ÙÞ±ÕJuu5Éår U*€òòòHWW—?~,´ 2„vìØ¡°^@@ݹs§]òª£úúz’Ëå´víZ¥èÉ“'$‹>ß2™Œ"##éÃ?¤ÚÚZ""Ú¼y3%$$´knusãÆ ’Ëå$‘H”     ŠŠŠ–þùgrpp "¢¦¦&òöö¦ÒÒÒvÍ«ŽrrrH.—“›››RC~~~Ârqq1‰D":|ø0ÍŸ?Ÿˆˆ*++iÀ€íš¹­iôMÐ/½ÿþû---¥¾ÌÌL,X°@Xîß¿?222pñâE444@*•bß¾}ðññi·¼êhРAš㬬,,^¼XXvvvFzzºÂ$džžžˆŽŽ~÷AÕ˜‘‘¼½½‘7n(ôeeeA*•ÂÐÐPhsvvFFF8€¾}û¢¶¶¹¹¹êZûêÔ©¼½½ñôéS¥¾ÂÂB@Ïž=…6ggg¤¥¥aìØ±Ø¸q#&OžŒ#GŽ ))©Ý2«£D"å?QYYYÉd²³³3rrrÐÐЀäädxyyÁ¢ݲª+{{{ØÛÛ7;WYff&œ…ekkkBOO)))())Avv¶Â:êˆ  Q^^®pâ‘‘êêê`llŒýû÷£¬¬ cÆŒÁ”)ST˜R½•••)Œ±D"AMM êêêðÞ{ƒU¯C(//Æò%‰D‚òòrèéé!..úúúHNNæt[éÕcðâxQ^^ŽÅ‹cë֭ضmbcc[ü´)SÖÜñ‚ˆPQQúúz…¦Xë”——+=Ý%‘HðüùsÄÅÅaÛ¶m°µµÅ–-[T”°mpô/ôõõ¾£¥®®"‘ýúõSûê÷¿ÂÀÀ@iŒuuufEåèí¼:ÆÀ‹v% ÆñãÇ«(YÇñê±xñY–H$‰D˜?¾Š’u,Í/€ gΜ©ªXÊ?/\]]áêꪢdmK£Ÿk KKK Ë………°´´löRkæÆØÊʊǸ YZZâÞ½{hllÚŠŠŠ`mm­ÂT‹¥¥%jjjðàÁ¡­°°Ç¸5w¼011á3—mÈÒÒÅÅÅÂòËÏuG›Œ  !“Éðý÷ߣ©© ˜˜ˆ€€§êXxŒß=777tîÜYø¢Ñ‚‚œ;w“&MRq²ŽÃÚÚžžžÂÜ3UUU8vì–Û˜L&Cbb"ž?€ïB@@Ž;†ÒÒRÀ?ü€þýûÃÞÞ^ÅÉÚ˜ªïÂþ/ð÷÷'©TJÈÚÚš(ôÕÔÔŸŸ™››“ }øá‡TQQ¡Â´êÉÏÏOaŒ]]]…¾'OžÐ¸qã„1öòò¢ÊÊJ¦UO7oÞ$©TJfff$‹I*•Òúõë…þóçÏ“……9::’±±±0k¹²²2’J¥daaAººº$•J)44Tè¿sçÙÙÙQÏž=ÉÔÔ”–,Y¢Â´êkÊ”)$•JI[[›,--…§¼ˆˆž>}JdffFÝ»w'ºwïž Óª§5kÖT*%±XLfff$•J)--M茌$###rtt¤îÝ»+=×hôLÐŒ1ÆÓL| Œ1Æc‡ ÆcŒi.€cŒ1¦q¸bŒ1ƘÆáˆ1Ö&òóó…© T¥°°*ÍÀS\1ÖÂ××óæÍSh߸q#öïßßæûkhh@=&lOpwwLj#xÆeÆX‹pÄXôäÉ\½zÄ™3g„öÔÔTäåå©0Ù»±wï^xxx ''{öìQuƘàˆ±JOOË—/DzeËší¿yó&vìØ¡Ð‡’’’pöìYDGG#(([¶l!!!S§NŶmÛPSS£°}zz:.\ˆ)S¦àäÉ“ }çÎCHH‚‚‚—S¥¤¤`×®](((@ll,’““›ÍûèÑ#DDD aaaB΋/">>yyyXºt)nݺ¥´íÝ»w±uëVÜ¿›6mfk...Æ¢E‹ “ÉúúzÀ•+W/läÈœ>}ZXÞ°arssr¹³fÍÂÔ©S±bÅ aö\ÆØ@Œu`ÁÁÁ¨ªªÂÁƒ•úÒÒÒðã?*´­[·=8p¨««Ãرc±nÝ:899áÒ¥KðõõÅîÝ»±~ýz…í-ZGGG˜˜˜`Ò¤IHII?~3g΄››d2âââàE!† & 77WØÿÿjhhÀðáÑššŠÉ“'#33Æ ÃóçÏajj }}}XXXÀÉÉ ‰DiûÌÌL,_¾¾¾¾HKKãGPUU<~ü2™ §N¾DKK áááÂöáááX±b ¾¾aaa055Åùóç1iÒ$ >Ÿ~ú)JKKñ矶øýaŒ©** sçÎð¢ˆ(..ÆÎ;ÆÆÆˆ‰‰ÁòåË…õOœ8.]ºÊËË‘wwwDFFbÕªU ˆÅb„‡‡cÉ’%©TгgÏBWW·Ù‡FII nܸmmm|òÉ'077Ç¡ŒèÖžIDATC‡ˆîÝ»cÈ!˜1cÆksss\¹rb±°víZXYY ¿Ï¨Q£`nnŽëׯÃÍÍ OŸ>EVVˆfffÈÍÍEii)²³³Ñ·o_˜šš"33R©&L€¡¡!ÆÿÆcÌS >ÄX±XŒ]»v½ñ¶:::Âφ††ÐÖþÿC†‘‘‘ÒWZZZÂÏîîî())ðâTXXœœœàää„ÐÐPëš™™½¶ø€¬¬, 6LØ¿––†ެ¬¬ÿ.ÆÆÆBñóò5½¼¼2ôë×YYYÐÑÑ··7.\¸€ýû÷#00'NÄÁƒqñâEŒ9àïïsssXZZbäÈ‘HLLDccc‹31ÆT‡Ï1ÖÁiiiaÍš5 ···Ð.‰ðìÙ³7zZ~UFF,,,&&&HLLÄ|ÐòàÿÃÄÄDéæí¼¼<øøø´êõ^¾æËûx ±±EEE011ðâŒÐ… pëÖ-=z999ˆŒŒ„¡¡!fÏž èÒ¥ ~ýõWdddàòåˈˆˆ€¶¶6¦M›Öê\Œ±öÁg€ÓcÆŒAÏž=î6lÒÓÓQYY‰¦¦&$''ãùóçoµŸ—7E§¦¦âĉðóóøùùaÕªUBmm-8Ðâ×õññAFFär9€7T§¥¥aôèÑ­ÎúñÇC.—ãÎ; "$%%AGGƒöyøðaèëë£[·n:t(²³³qéÒ% >pæÌäåå¡wïÞ˜1c ðFE%cLu¸bLCDGG+<µÕ­[7Lž<öööèܹ3NŸ>­pÉ«5F^½zaàÀ˜1c†pßQLL :wî 8::¢K—. OUýìܹþþþ°±±Á¸qãðÝwßÁÑѱÕYG…/¾øƒ ‚ –.]Š={ög€úôé===xqÆkâĉptt.ßÝ»w...èÕ«úôéLŸ>½Õ™cíG‹^>‹ÊcŒ1¦!ø cŒ1Æ4@Œ1ÆÓ8\1ÆcLãpÄcŒ1ÃcŒ1Æ4@Œ1ÆÓ8\1ÆcLãpÄcŒ1ÃcŒ1Æ4@Œ1ÆÓ8\1ÆcLãpÄcŒ1ÃcŒ1Æ4@Œ1ÆÓ8ÿ%Gž~p¡ÝéIEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/compressed-recordsize.svg000066400000000000000000000713241231437614300262430ustar00rootroot00000000000000 Disk space taken by a record (original record size: 16 bytes) 10 3 10 4 10 5 10 6 10 7 10 8 Number of rows 5 10 15 20 25 30 Bytes/row No compression zlib lvl1 lzo lvl1 bzip2 lvl1 PyTables-v.3.1.1/doc/source/usersguide/images/compressed-select-cache-shuffle-only.svg000066400000000000000000000761331231437614300310260ustar00rootroot00000000000000 Selecting with small (16 bytes) record size (file in cache) 10 3 10 4 10 5 10 6 10 7 10 8 Number of rows 0 2 4 6 8 10 12 14 16 MRows/s No compression zlib lvl1 (Shuffle) lzo lvl1 (Shuffle) bzip2 lvl1 (Shuffle) PyTables-v.3.1.1/doc/source/usersguide/images/compressed-select-cache-shuffle.png000066400000000000000000001617631231437614300300400ustar00rootroot00000000000000‰PNG  IHDR@°AàÚ²sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwTTGûðï.°tPŽŠˆ€`CQ”"{W4v£¢Æ‚-ÆX‚%¶$þ$FQ_»&H,‰¾1@ÔˆØPA±+ "½ Hçùý±/7\véMa>çx2/^¼ÀìٳѣGtéÒÆ ÃæÍ›ñæÍ›*cõêÕX°`AÔ'''ÞÞÞxÿþ=¯|ýúõøòË/ëäuiòäÉØ¹s'·|þüy^ÇGŸ>}¸f5sãÆ ´k×ÅÅÅ€“'OÂÞÞ¾Jû.\¸Æ CAAĺ˗/cÉ’%å˜ˆuëÖaðàÁ055Å*<×–-[0a„*Õ«<=‚¿¿­ŽQVtt4:wGÖéqKVVÚµk‡û÷ï×hÿääd8::âòåËu\3Innn2d/X;}ú4:t耞={bÀ€øã?àèèˆÜÜ\€§§'† V«óž>}ŽŽŽ(**ªÕqêÓ´iÓ°~ýúZãáÇ066Æ•+Wê¨VÍ €>"7nÜ€••bcc1cÆ ,^¼¦¦¦Ø¿?<==¥N)))øüóÏÍ+744DÛ¶m¥Niß¾=tuu¹eWWWܸq£kÔ8|}}qâÄ œ?\ùýû÷áááÙ³g#00Pê¾ÁÁÁèÞ½;‚‚‚0zôh¬_¿íÚµ«÷:{yyaóæÍuzL+++üç?ÿÁÂ… %îáæHVVæææPWW¯×ó\¾|‡ÆùóçѪU+®|Æ øòË/‘™™‰ eË–077‡PXwŠ444`nn@PgÇü <7nÄÔ©S‘ÝØÕù$É6v˜ýðð´´„ïûuëV„……ñ¶ÍËËãGœœŒ.]ºÀÐаÂcãùó爌Œ„‰‰ ÌÍÍ%¶‰ÇóçÏ¡¦¦†Î;C^^žkn‰…²²2D"ôõõ1aÂòö•——‡ŠŠ DFFºuëÆ rñ“§OŸB ÀÂÂZZZܺ‰'âàÁƒØ±cG…¯©¨¨ÑÑÑÐ×ׇH$Bjj*w­²²²ððáC|øðÝ»wçýa/yíaaaxûö-Úµk'qíÃÃÊ:ÀÔÔ”·.** ZZZPRRBff&rss¹×{÷î¡°°–––^ÿùùùxüø1RRRжm[îóÞ¢E ¬^½š»~ ÈÉÉ‘Ø_OOòòòÄ÷èãÇKKK‰Ï4k׮Œ%K`ffÆ]›ÀÀ@¼xñöööˆŽŽ†––zõêcccˆD¢ †°°0¢[·nnÛ§O˜™™qŸ³´´4äååAWW·ÜÏayˆaaaˆŠŠBÛ¶m¹×oÞ¼ATTòòòЦMtìØQbÿÜÜ\<{ö °°°¸o ððáCÈÈÈ {÷îûâéÓ§x÷î,,,`llÌ[¿bÅ ;v ûöíÃÊ•++}=LÄ|4ÆO666•nwçÎj×®ihh©©)ÉÊÊÒîÝ»¹õsæÌ¡qãÆqËQQQÔ§ORTT¤®]»’@  ¹sçrë‹‹‹iùòå$ I[[›”””HCCƒ^¾|Iººº€TUUI]]lmm‰ˆhÁ‚4räHî}ûö¥þýû“‰‰ ©««“¬¬,õêÕ‹²²²¸mîß¿OFFF$##C&&&¤ªªJB¡þøãr_«ƒƒ-Z´ˆ[Þ±c©ªªRNN%$$ &""[[[Z¿~= 2„)))‘ºº:©««SAAýüóϤ©©IãÆ#‘HDªªª¤««K—/_®ðº:tˆ”””ÈØØ˜ H ÐO?ýDDDÞÞÞ$''GÓ§O'‘HD***$‰èĉ4`À’——'%%%222¢®®$''G&&&dddD²²²´uëVný¥K—ÑÞ½{ÉØØ¸Âz†„„@  7oÞ”»ÍÚµkÉÚÚZ¢üìÙ³$//Oïß¿¯ðe­X±‚Ú·oOÝ»w'%%% …¤¡¡A÷îÝ#""jÙ²%}øð·Ÿ……¹»»Ó/¿üBòòò$++˽WÇŽ#"¢«W¯’¾¾>ikk“±±1)((p눈víÚE dbbBººº$##Ã[ODôÛo¿qïy¢££ íÛ·zôèAhüøñDDtáÂÒÔÔ$CCCjݺ5©¨¨Ðùóç¹}K>c¨M›6$ ÉÉɉˆˆrssiÊ”)€Œ M˜0²³³¹ýåäähË–-äììL€ºwïNDD×®]#]]]îQSS#tëÖ­r_ÇãÇ©mÛ¶¤©©Ifff$##Cƒ ""¢ÂÂBÀÝëcÇŽå®·ºº:)))ñŽÿèÑ#277'uuu²°° ¡PH[¶l)÷ÜDDOžôÍ7ßpÇxöì/Øøõ×_I pï{M ;vPÏž=+ܦ¼híÚµdddDŸþ9éèè’’õë×îÞ½[áñV¬XAššštîÜ9*(( —/_’0—üÙÚÚrßyyydjjJ+V¬à^ç•+WH(Ò³gÏÊ=ÿÎ;yAL‰@ñññ\YeМ9s¨oß¾”˜˜HDâ<:::´gÏžrÏ/-RSS£“'OR~~>åå命½=-_¾¼Üc :”zôèÁý`IIIá‚¿˜˜zûö-·í›7oHEE…~ûí7""JMM%---úâ‹/¸ûûéÓ§Üý?pà@266¦«W¯Rqq1%%%‘¾¾>ýòË/D$~ŸºvíJ ,àå;wî âÕ3,,ŒÐ»wï*|=Œ$–ô>|8‚‚‚`dd„7bРAÐÑÑÁâÅ‹‘œœ ðóóCZZFŒׯ_#44æææ‰D¸{÷®Ä1_¿~«W¯bÒ¤Iˆ‹‹Chh(Š‹‹ÑµkW\¿~pðàA899a̘1‘‘ŒŒ ¾øâ ÞãÞª1búõë@ØÙÙáï¿ÿ!<<«V­âr‡ZµjUéchggg>>˜;w.×䦡¡5kÖôõõ¡««‹ÀÀ@8p^^^PVVFDDq狤¤$¬_¿ž»¿---1kÖ,““ZµjÞ÷å“'O0qâDDGG#44-Z´@»ví$rÛµk™Z}g4W,è#cmmßÿD„ˆˆøøø`ãÆ(..ÆÞ½{¢¢"Þ 'ÿJëõPòܰaƒÄº’v÷ˆˆLš4©Î_‹ŠŠ —œ÷æÍ(((ÀÖÖ¶ZÇèÕ«”••qëÖ-„‡‡c̘1øüóÏaccƒÜÜ\ÖIÝK×U999ìÛ·+V¬ÀáÇajjŠ™3gbñâÅPVV–º¼¼<ˆˆW¦¬¬Œ>pË{÷îŲeËгgOtìØ‘Ëy©M–´´4Ôxÿ.]º`êÔ©Üò©S§ ®®ŽK—.IÜw)É{÷î:v숅 ¢S§N ‚žžΟ?_áR@|oæææbÊ”)¼rKKKäççCEE{öìÁ7ß|ƒÝ»w£cÇŽ˜;w.æÍ›Çå‚•ÐÓÓã~HT¤lòlDDRSS%î³®]»r¹_€8X/ï5¸¹¹ñÊìììpèÐ!AFFFêy_¿~ ===©¹%Y·næÍ› èèè`øðáX»vm…‰ìG…——‚‚‚¸{°ä»cþüù¼m+ Xñý§¯¯_­:Kóöí[bïÞ½8tèoµµu­Ž]Ùç½äµ—÷žbܸqPUUEÏž=¡«« ¡PÈ}n#""`ll\¥|©ÒuJIIáö÷šššÄ=YYYhiiUéÞføXô‘011Á’%Kðúõk\¸p€ø ²²2>|X¥ã”übüïÿ‹6mÚ”»Í«W¯*|8¦M›†¶mÛâùóçøë¯¿¸àáââ ¼~ý/^DHHïØÿüóAµ›``РA˜2e 0cÆ èêêâÉ“'¸xñ"ž={###xxx`æÌ™¸~ý:ÆŽ‹'Ož $$¯^½Âwß}‡Þ½{ÃÜÜ&LÀ¹s瀓'OVx^[[[ôïßC† Á„ PTTT¥ûc„ 044„½½= áííeË–AQQQ¢iuîܹHLLDll,¯©«äýÞ±c,X€«W¯ÂÒÒáááðõõÅîÝ»Ëø²ÿþpwwGBB‚İÕµoß> <0`’““áïï>}úT:8gmÈÊÊb÷îݘ2e 1~üx¼xñ·nÝÂÛ·oñÅ_`ÇŽxõê._¾Ì{Bjmmyóæa„ :t(z÷î ???˜˜˜p9VÑÐÐÀ®]»°páBܾ}]»vÅëׯáçç‡Í›7óš¢ÿúë/´iÓíÛ·¯KѤÉl–Â4Š9sæ S§N …HLL„ŠŠ F#GŽð­ÛÛÛcРA\R³–––.]ŠBFFfffܘ!;vÄ”)S¸DBEEELž<Ó§O‡¼¼<ÔÔÔ0gÎ(**"&&"‘nnnppp€P(ÄÀ¡¡¡'Ož }ûö\³…©©):uê@Ü´ceeÅû tìØ‘ËaèÝ»7¦M›†ž={bÁ‚X½z5¶lÙ‚åË—W˜3 ­­ YYYLŸ>ûB-#füøñ¼|—’z”ä;X[[ÃÂÂ>D‹-0`ÀJŒ]’ˆ(M‹-`mm„„„††BAAÛ·oçþ  hkkó’oôõõyƒ@ @›6m¸†Q£FA$áùóçèÖ­<==¡ªª GGG¨©©qãÐ8::rÍgMG¥éëëãèÑ£‰DðÃÂÂ’’‚öíÛÃÚÚ²²²PUUåoÔ¨QÐÕÕEpp0Š‹‹±lÙ2¬]»¶Üó•°¶¶†šš>|KKKìß¿½zõâm# qâÄ L™2 à­SVVƸqã””ØÛÛCWWŸ}öƒW¯^A__«V­‚ôôôйsgÄÇÇ#,, ªªªðððƒjíÚµ055ÅäÉ“+| òòòprr‚ªª*¯|ôèѰ¶¶Ftt4"""жm[|ûí·èÖ­ºté‚#F   oß¾…‰‰ 6mÚmmmhhh`Ú´iÈÎÎFhh(ìììpäȉ?X¼ñ‹ñFŠŠŠˆ‹‹CçÎqàÀ¨¨¨ÀÑѱÜÁ œœ••…ððpdggcòäÉX²d „B!w9::¢eË–HOOG·nÝ ªª î_çΡ«« Œ9’»ï[¶l‰ùóçcäÈ‘å6‹éëëãøñã‘‘Aß¾}yëTTTàääÄ÷G[[›{VöóahhˆÙ³gãÇ aøðáæÞ•î„Q¢}ûöãìTÔ[ZZbôèÑ(,,äÆvÚ¼y3tuuѧO˜ššâñãÇhÕª~üñGXZZ¢G\'áÇ£[·nÈÊÊBBB°lÙ2.Ǫk×®É⥿·»wïŽqãÆqÉöêêê˜={6Æ999nŸE‹aèС?~™*h”¾gL³ôîÝ;®;7‘§§'iiiñÆ bêÆÁƒ©E‹¼.ǃ’®ï±±± vΛ7o’P(ä¿ÄÔ¯£G’ššZƒ¾ÏÍѯ¿þJŠŠŠ\W{¦zDu”!Ú¤¤¤@(–;J.S;³fÍÂùóçѾ}{dff"!!'NœÀÈ‘#»jMNqq1 ðññáýblLÇ‡šš¼¼¼ä|qqq°³³Ã¤I“°uëÖ9'#ÎÓ:t(òòòàççWéHÏLõ=þýúõömÛàêêÚØÕù$5›héÒ¥^yqq1¶lÙ‚ƒ"++ ݺu«U^S¾äädÜ»w ÐÓÓCÏž=¡©©ÙØÕj²’““qóæMôêÕ«Nº%×áÏ?ÿ„••Z·nÝ ç|òä Þ¼yƒaÆUškÆÔ­’Æž={ÖjXFº»wï"-- ƒnìª|²š|tñâE,X°ïÞ½ƒ›››D´`Á<}úëÖ­ÃÀQPPP­.§ Ã0 Ã|zš|TBÚ ÈÈHtèÐKÚd†a¦yhÖÝà`hhOOO<|øªªªX´h7ÆË—/ñòåËF®%Ã0 Ã4…fÑ´Ö¬ ¨¨((**bذapssÃ;w0qâDüý÷ßèÛ·/¼½½áíí ssóz¯K||LuÞ£÷ïߣ°°„À¼ IDATånSW׸dÈ„Ò#Æ–Ý®¡®qyu¬ªê¾G•+55²²²PSS“XWÞ{”žž¼‘u¥Ý·eÏ mmíiB¯Í5®îþù}Qö=úðáRSS«5š|MI»ª£:׸*ß-õuË–ååå!$$ñññUªû'­±ºŸ54777rssã•mÛ¶FÍ+sqq¡Õ«W‘»»;¹»»7HýŽ=JoÞ¼©ñþÕ©gU¶­h›òÖI+/[–––F»víâ• 6¬ÒúÔ…¸¸8Ú·o_÷¯Î{H¾¾¾nSW×øÔ©SôâÅ‹ ·k¨k,íÜÕQÝ÷¨²sùúúr³°—UÞ{äïïOþþþ¼2i÷mÙsÏ;—ž?^iëBm¿—>•ïÑ;whÉ’%•Ö§.H»ª£:׸*ß-õuË–ÅÅÅ‘ŽŽN…ui*šutúôi277ç•999q7CC@)))”——Wãý«3DU¶­h›òÖI+/[VXXH‰‰‰¼²†ºÆ”œœ\ãý«óeggÓû÷ï+ܦ®®qZZåææV¸]C]ci箎ê¾G•ëýû÷”-u]yïQff&effòʤݷeϽoß¾¥¶çùT¾/ʾGaao臎ÒÝ»•V©Ö¤ÝÕQk\•ï–úºÆeËšSÔä›À²²²œœŒ÷ïß?êÓÒÒ‚²²2FŒÕ«WcÇŽøâ‹/àçç‡{÷îaïÞ½ ^ÏŠšJª¢²š«»mEÛ”·NZyÙ2‰ÑnЬ¬l­ºÝWç=ªÊˆuu¥=¢¯ÎýP×jsîê¾G•«ìˆÎ¥•÷•ŒÔ[š´ûöS½ÆÕÝ¿!¾/Š‹„ 6VAA@L  ÄÆ*ýïqYJ @ÈÊyy€°g³”vTGu®qU¾[êë;¹²c7eM>ºyó&¶oßÎ-Ïœ9ß~û-ú÷ïøøø`ÕªUسgŒáããÃMÝÀ4Œ±cÇ6vš-n–júÄSçÄÆ=z4Ôy™¦ˆ@µäåå…Û·oⱫÂ0˜˜ÈÈÈ4«¨¹HH6m êš€¾þ¿ÿ øËß}ü÷¿ÿnWwçfš'ÕÉ“'s#M3 øùùIL;ÓÔ¥¤¤@]]²²Mók53øñG`çN ;»êû©ª–Ô””éé• Ÿ$Æ*@ ÄÆÖüµ0 À †a˜:áïï''§&7Áo~>°¿ø LR’ôm:wœ¥?Á©eg*ŽxlÌdÁF²'@L­±ˆa¦4µ "ÀË X¿xóFú6FFÀæÍÀäÉõÛ%(Ixþ7ˆ@Lm±ˆa†áñóV¯=’¾¾U+`Ý:`Á@$j˜:•…51µÅ †a˜:Ðr€îÝ>þþÒ×++Ë—_}%ÎíiHâ'@ÿæ±'@LmÕóCK†a˜æÁßß] &Lll¤?rrÀÂ…â®ï›65|ð”κ @Ü­¸¸áëÁ4ŸîO†a˜È§˜lÜ> J®€‰Å ÐíÛ7|ýJSU”•õ‘-Î**f:‹SØ †a˜f&#C<ˆ¡‰ pà€ôàç³Ï€à`àÔ©Æ~J”ù™5ƒ1µÁ †a˜:’’‚Bi‘ÄG$/ø¿ÿ4[·>Hncm \¹üý7н{Ã×±"zzùR¹e–ÍÔ €†aêÀÇœT\ ?˜š+Vˆ'(-«Cñ´÷î‰ÇôùµlùoÀž1µÃr€†aêÀÇštñ"ðÍ7âIK¥ÑÕÜÝ9s€½[ûöÿްˆ©üvg†aj"0øúkàÆ éëÕÔ€U«€eË%¥†­[M•ÍbM`Lm°ˆa¦|,ã½x¬Yœ?/}½¼¼¸Kûš5â H?%ZZÿް'@Lí° †a˜:ÐØ9@11âf¬Î¥?B!0c†xÌŸ;?½à”•ù9@ì S,b$LŸ>£GÆ2ÏÎ}||àææÖHµjzbccáè舀€€Æ® SÆß(¡¦§‹›º:tçST$¹Íðáâi-ŽÚ´ið*Ö™NXSwXØG¢°PúXuM(¬|îÈÉÉaùòå âÊ_¿~«W¯Ös ›999˜››Cµ1†Õe>y¹¹ÀîÝÀöí@Zšômlm;€¾}¶nõ¥ì|`ññâI[‚Æ©óicO€>+WŠŠõÿÏÞ¾jõùꫯðäÉœ={¶Òm_¼x¿þú oÊ›2ZЬ¬,ܹsHMMå­ËËËÃÝ»wqùòe‰uÙÙÙxûö-q@æç燄„nýóçÏqýúu—#?!!©©©ÈËËC`` üýý‘ÍÛ&22999€÷ïß#))‰[—››‹»wïÂ××±Rž»§¤¤ ¼ú”¼ž   \ºt ¡¡¡\y«V­°zõjXZZò¶OLLÄßÿû÷ @b]Éx3ÁÁÁ¸ÿ¾D]˜ÆÑPãGŽˆŸø|ýµôàÇÂBÜ vëVÓ ~@Q1 ÿ~'¥>¦ S=Ä”ËÝÝÜÝÝ+ÜÆÍÍvíÚUës¹¹‰ËÔï?›Ê뢩©IgÏž¥¥K—’™™ÑÏ?ÿL:uâ¶KOO§¡C‡j×® WWWnûòœ={–Z¶lIJJJ¤­­M²²²ôŸÿü‡ˆˆ=zD&&&¤¨¨Hzzz¤¨¨HGŽáö=uê‰D"š>}:‰D"RQQ!yyy:yò$9;;“¼¼<)))‘‘‘=|øÛoàÀdccCmÚ´!UUU@&&&EDDÅÅÅ€vìØA$¨OŸ>DDtýúujݺ5iii‘‰‰ ‰D"®¾DD{÷î%EEEjß¾=ééé‘P(¤Ñýû÷ÉÐд´´ÈÔÔ”„B!5Šˆˆ²²²ݸqƒ;–‡‡‰D"244$‘HD:u¢°°0nýàÁƒ©_¿~daaAjjj$++KVVV”’’RùÛ€|}}iРA]uæÌJNN®×sääY[—ÿùnÝšèÈ¢J>‚Ÿ¬˜˜ÒÕ½À{Í¥>æLˆ‹‹#Æ®Fƒ`O€˜r­]»±±±8vì˜Ôõ6l@XX?~Œ×¯_ãÚµkðööÆÁƒË=fDD¦NŠyóæ!&& €¦¦&Š‹‹1uêTtéÒQQQˆŠŠÂ–-[àêêÊ{rRPP€Î;###8p æÎ‹1cÆ ==éééÐÓÓÃéÓ§yçNOO‡——222pûömà»ï¾ãm³k×.Lž<Ïž=ÃÑ£G‘žžŽñãÇcÑ¢EˆÅ«W¯pøða¸¹¹!!!YYYX²d vïÞððpÄÆÆâÑ£GèС`ÕªUèÞ½;Ф¤$L˜0Aêµ Â²e˰ÿ~DFFâõë×ÐÓÓÃìÙ³yÛEGGc÷îÝHOOG||<’’’pñâÅr¯9Ó0"hñbñôeih?ü Npž5 ‘©×j4}}}˜˜Œä•±Dh¦¦Xô‘“wOmˆUÕªU+¬\¹6l@nn®Äú'N`̘1èܹ3ÀÁÁŽŽŽ8qâD¹Ç|ˆôôt@§NàääPQQAll,˜2eJ¹×FOO³fÍ‚ŒŒ 0gÎܸqƒ×¼8hÐ 8;;C @SSNNNøûï¿Ë½æLÓpò$pè¿LII<Èáë×âft…Æ©[Cbó1u…@‰~'5Ö÷¿êv8Z¾|9òóó±wï^^yzz:RSSaggÇ+·³³CDDD¹Ç‹ˆˆ@Ÿ>} $e䵈ˆØÚÚre²²²°±±©ð˜òòò "^™ŠŠ >H›è¨”îÝ»#&&†W&ò?(((À´iÓ0qâDLœ8Ó§O‡¹¹9    OOOœ={ššš°°°ÀÎ;¹\¢ 6@(ÂÌÌ zzz˜7o—Ã$íõK»ž%ëÊ£¬¬,‘ÏÄ4¼úÌzþX°€_fl,~â³u+ ®^/§ýèäççCCƒŸÈ ¦¦XÄTHEEë֭ömÛxcœ¨ªªB$áõë×¼íÃÂÂЪU«r§­­W¯^I]תU+‘D2õ«W¯*jÉÉÉÈɹÉ+cM`LM±ˆ©Ô¼y󠦦OOO®LFFvvv¼&¤¢¢"\¾|öt5³³³Ã»wï\*‘¡¸¸°²²‚ŠŠ ï˜Ïž=ÃÛ·o+]ºtÁêÕ«1vìXL:íڵË/àããƒ?ÿüjjj°±±‹‹ ,--…sçÎáÎ;€#FÀÌÌ }ûöE^^N:…¯¾ú rrrÈÏÏçkÆŒ8tèÌÌÌ0kÖ,<~üþþþ8zô(šCr#áÐ!qîOi“&I6‡5'l>0¦®ÈlذaCcWâcU’DëèèXî6~~~hÑ¢z÷îÝ0•j }ûö…––·Ü©S'î)GÏž=ˆ¹¸¸ ##áááøì³ÏpôèQèèèTxìÉ“'ÃØØiiiÈÈÈÀˆ#0wî\(((ÀÆÆ¶¶¶x÷îÒÓÓ1gÎìܹ“—›£££#ñDÈÀÀ@â=hÛ¶-¬­­ˆŒ-,,йsg$&&bÔ¨Q8xð ÷ÿIÍÑÑQ¢¹ÉÙÙýû÷GLL ^½z]]]¬\¹ÐÖÖ†••âââeeeüøãÜy¹ë“““ƒ3f`Á‚ … …ptt„ºº:„B!¦OŸ%%%¼|ùmÚ´ÁÎ;1|øp®.]ºt©©)¯ÌÔÔ:uªðº7¤ððpܹsS§Nmìª4˜””ˆD"‰<²šzô7Ž?@ª™™xv÷Ê3mªòóó‘””#G¹2ñ„®LÝÈÊÊÂþýû±råÊÆ®J½PÙìQ†SV#.]ºFFFXºtiÃTŠ©‘Ï>û :tHæfꇟŸ<<<àççרUi0gÏž…““S4ƒef=z¥Óå»wÅs}5W±±±¸v-S¦üÛ &‰;x°Ñ ëF||<ºuë†øøøÆ®J½c9@ Ã0u .s€æÌá?°woó~ñSçÉ“Gòºûçç))W'æÓÅr€˜faÆ PQQiìj0L¥öîÊŒá‰3Ä2bzz@é΢qqë(ÉT{Ä4 ¶¶¶èÒ¥KcWƒiÂêb à``ùr~Y§N@©˜ÍZ~~>RSSY"4S'XÄ0 Sj;Pz:àâ"nÒ)¡¢"ïGʸ¡ÍRrr2nÞ¼ÉFƒfê €˜jÙ·o>Ì-"<<œ[ž={6oŒŸª Áܹs«¼}ll,fΜ)1S< þþ¼ì ) Ój›4s&¿YØPÿÒ××ÇÈ‘#¡¯Ï/gSÍ"úõ×_±gÏžr×geeaÒ¤Il>¥*¸vínݺÅ-7?ÿü3·|âÄ DFFVë˜ÑÑÑÎVVzz:Ž?.1ýCqq1–/_Ž/¿ü²Zçg˜Æ¶s'pá¿lÞ<`ò䯩ÏÇŽ51u¡I@ÁÁÁ˜6m\]]ËK©¨¨...øë¯¿ÝÀ5üô=zôHbFõưlÙ2hhhT+b˜ºTÓ À@`õj~™•ðÓOuT±&¤$ˆ=bêB“îfhhˆÙ³g#''Gb²Ì‹-B§N$FåmhgžŸÁ?¯ÿ©÷ó·4Æ*»Uå®ÿå—_påʉò/¾øBêt{öìA¯^½0lØ0®,##kÖ¬A@@Z¶l‰o¾ùFb’ÏòDEEÁÝÝ6l€‘‘W~ÿþ}ìÛ·7GikÖ¬››6oÞ\áÄ¡ S_üýý«=PJ 0q"°CuuqÞ¼|=Tò—œœŒàà`èéñ§Ã`O€˜šhÒ®®.tuuqþüy©ëøá$&&ÂÓÓ³Ò9¡êÛ­··pàþz?M…ºuëÆ-߸qþù'-Z$uû?ÿü ¼hñâŘ0aF…#GŽÀÙÙ¡¡¡hÛ¶m¥õkÓ¦ nܸƒbË–-\ùO?ý„ÌÌLˆÊWKK ZZZPSS«ô SÆ_­í‰€iÓ€²žÚ·¯ÃŠ5!%9@OŸòËÙ ¦&štXEΜ9ƒsçÎá—_~á¦AæØ±cذaï_iÕMøýØ999aéÒ¥Xºt)F€€ìÚµ‹›Ú¡*öìÙƒãÇcÕªUxþü9TTTpà@Õ‚;@€yóæáرc(** ~¢töìYÌŸ?¿F¯‰iqâD(++ãÈ‘#€îÝ»c„ øæ›orrr8uêï×ð!C   €?þøCê¹þûßÿb„ ÈËË$%%ÁÐÐgϞň#°ÿ~üðÇ@ ÀóçÏaii‰·oߢuëÖ¼c-[¶ !!!ܶ­maÛÚ¶QÎ]ÖîÝ»áçç‡û÷ïC¡ô¤;5P\\ Ì™3§ZûÍŸ?ÎÎÎðõõÅãÇñ×_Õª Sߪ’T\,îÚ^:gE Žª"×ì•äYZ6RŘOR“Î:{ö,ŒŒŒpäÈ9rFFFøóÏ?»Zµëׯcåʕؽ{7D"âã㬬¬*ãÙ³gÈÏÏG\\Ö®]‹¬¬¬j t055Å´iÓ0fÌhkkW¸}AArssQXXˆââbäææ6zÏ>†)kãFàêU~ÙªUÀðáSŸOYÙ®ð¬'S]Mú Ðøñã«Ü3CZ×ïæh÷îÝ(((Àä2#°­[·›7o®Ò1¼¼¼¸mÕÔÔàíí “j×eÞ¼yXºti•’Ÿ]\\x½ýѵkW<|ø°Úçe˜š¨,èòe ìYýúI–1å+ĦÃ`j«I@Lõ={¶Âõ¿ýöoùÁƒ¼å’Þ‰‰‰HJJ‚™™Y¥I¡#GŽä Ksssƒ›››D¹………ĸNå%X3LC©((6˜2EÜVBK ðöª3ÍüOÙ ÒØ ¦ºØG©ÚÚÚ•6[1LSRÞÓæÂB`Ò$ )éß2¡øõWÉf¦b¥s€ØhÐLm5é †a˜Æ¶n¸{{Ù²F{õ“ÇšÀ˜ÚbÃ0L6Ø_ßÏßÎÙpwoÀŠ5!%s, š©=1 ÃÔdddpËoßÓ§‹§¼(¡§xy‰›À˜êKNNÆÍ›7°'@Lí± †a˜:P:¨ pqþ÷° ##Nzf©q5W:¨U+ñÀ‘%³6ääéé@‹XAæ“Â~‡0 ÃÔ±¯¾îÞå—mÞ ØÛ7N}š"ÐÕå—±§@Lu°ˆa¦”äýþ;ðS™iý†V¯nœz5%¥s€Ö ÆÔ €˜j À­[·Ê]âÄ D•Ÿ¾ ¢££qòäÉ*oŸ‘‘cÇŽ!;;»Zç)Ovv6Ž?ާOŸJ¬+**BBB‚Dr+Ü»wÿýwÔ!??¿ÿþ;<==‘@ÅùóçáïïÀÀ@¤¤¤àÁƒ8þ}:/^ UUUnÛAƒÁ =\oæÎÄñ½x yy…u¦> IDATqÞºz£V«I*›ÄšÀ˜Ú`ÐÇbÍ`þüú?¢b•7;w.dee±oß¾jÂÚÚÂÿ ukdd„îÝ»ãÞ½{UÞÞ¼yøé§Ÿ kkk\¼xyyy˜8qbµêQ™øøx^sUiüñnÞ¼‰ƒ"((ÇŽÊ+°nÝ:¬.ÕŸ¹mÛ¶\ðVVVÕêÑVS¾¾¾022•+WpåÊ@VVrss†=zðê"‚€uEªS‡¥ø~ø(uù™zÄž1µÁ …¶öG5D¬‡‡.]º„û÷ïC^^¾VÇ222BRé©°+ѱcGØÛÛãàÁƒ°¶¶ÆáÇ1}út())Õªeegg£eË–å®ïÛ·/—P••…õë×cíÚµ˜9s¦Ä“ òòò(**ªÓzJ@ Ñ4éææÆË "''§Î¯asöîÀ@—{{u,ZľVëKÙ --@V(¥";xÿ(ó`©Ø'•‘píÚ5|óÍ7ðõõEëÖ­k}¼Û·ocÈ!ÕÚgþüù˜7oV®\ ???½Â}ìííQPPÀçÌxyyÁÀÀèÖ­Z´h¿þú ÞÞÞUîÒ£GFTTŒannŽQ£F•Û“­:ºu놟þ‹-BçÎ1`ÀhkkcÆŒÝÙ³g1hРZŸ“‹‹“ìÞng,^Ü8õiîÊæ±Dh¦ªXÃóõ×_Kt À% »»»CFF†+?xð / øçŸ`llŒÀÀ@$&&¢wïÞ¼„\iìììx]ÇK×eÈ!°µµ•¨‹¿¿?´KåLMŸ>]¢™­l—õ²1eÊìÞ½£GæÊ]\\0fÌ!** YYYh×®lmmyÍH‹-’˜Žcüøñ°³³ã–Û·o˜˜\¸prrr8p ¢££yÉÈP/ÓeèèÑ£Ð+óÍ~éÒ%XZZrË ,ÀøñãqïÞ=ÄÄÄ`åÊ•èׯ”••¹m>|ˆ;wîàСC^ ¦êæÏÒÒþ]VTŽÒÒR ®®.u\,¦n”ÍX"4S Ä”ËÝÝÜÝÝ+ÜÆÍÍvíÚÕ0bê\dd$)**’——WcW¥ÎQïÞ½iÊ”) ~n___4hPƒŸ·¾ýú+Àÿ÷ãâugΜ¡ää䯭`C.\à•mÜÈ?–/o¤Ê5qqq¤££ÓØÕhì§ Ó¬µmÛ»wïÆ¼yó`nn.1&ЧìË/¿DBB|Ë&«05’,YÂ/ëÝûßæ°êæ¹1Õ'-ˆ=bjŠ@L³7gΜjMÆú©Ø¿cW¡IùòK %åßeyyqÓ—eR6*–ÍÔûè2 ÃTâôià÷ßùe6;þ»œ’’ÂõöcêG~~>RSSye, š©)1 ÃT 9øß¬,kk૯øeþþþÈÈÈh¸Š5CeçXSs,bx addTîœuáÀÿÏÞ™‡Çx}qü“É.HDlA{KmU[µüPªT)jíbkKÑ¢j-Š"U‘¢EQ%-ÕØšXk‰µö¥–X²/²O’™ß#“y³Î$3™™ä~žgžÉ½ïûÞ÷ä&3sæÜï=G§Äˆyæ âÀú2M ÈÁGf"sظ46C* PÅŠ"!ÉMT¥Šôo Ål˜À, >$99Ù`ãÇÅÅI’ DRR>”ôÅÆÆrìØ1FÅž={ôm¢@Àž=°c‡´ïË/¡qcãØ#ȉL–³Šˆ ´A8@½‘‘Ajjj±ÝoàÀ >œÐÐÐb»§ t &Hûš5Z¸„Èðä¦!„± ÌDXùø1ÛÃà ~ŸÆlhÐ@ëó‡αcÇrôïܹ“V­ZÁøñãù믿HKK£]»vøúúR¿~}­Æ?|ø0cÇŽåŸþ¡J•*êþo¾ù†£GæÈÒ¬y s1@[&MRm}ÏÄÚZµô•WžÃÀÀ@:wî,–Á HöZ`™!´ 0ÈDx’ÂÍÊŠ&Âܹsy®a×gŸ}Fhh¨:#ñ€HMMå?þÀÙÙ™9sæÐµkWnݺ…½½}ãwìØ‘ääd6oÞÌ´iÓU1Òµk×òé§Ÿæ— `ß>زEÚ7c4mš÷5"áÉM¤ê—¶EH  b L/µkׯÛÛooo‚ƒƒ fÏž=888pæÌŽ?ÎÂ… éÚµ+Í›7ç‡~ $$„Ù…y`mmÍû￟ŸŸºïðáÃDFF2lØ0CýZAžÄÆÂ¸qÒ¾&Mà‹/Œc `DHP„$Њ3gÎ0qâD¶nÝJݺu¸ví¶¶¶’ÚW®®®4hЀk×®i=ö˜1c¸{÷®z©ÍÏÏÁƒãää¤ß_B Ђ©S¥ VVª¥/këü¯ Ó—Hl…±f"L©QƒÁÙ·2€rÙ÷îjAXX `æÌ™ôìÙSÝommMFFiiiØÙÙ©û“’’°.èÓBwwwzô误/^^^øûûçÈõ!*gG“Ï>ƒêùBTä¥K`‚ÂPj ³gÏM=Ô})))üöÛoœ={€>}úбcG>¼õ…»îN„©žžÎ AƒhÙ²%³fÍ’kÚ´)ééé;vŒ^½zpóæMBBBhšŸX"ÆÏÛo¿M:uðòòâå—_ÖÛï hÃóç0v¬´¯Q#˜3G»ë…Èðä¥K`‚ÂPâ ÇsàÀ–,YBÏž=%ÐÕ«WY¶lC† ÁÚÚš)S¦Ð¹sg¾ûî;#ZlZL›6³gϲjÕ*‰®§M›6¼ôÒKôë׉'òèÑ#œœœøæ›oðòòbÀ€:ݧgÏž¸¸¸0þ|||| <ÿĉ$$$‰L&# €jÕªéìx ™|ú)„„dµ--UÑ [[ãÙ$ІïEFF‚³³sŽcM›6åâÅ‹êvÛ¶méÖ­K–,ÑjSIÄ‚Ž;R¡B@•´°uëÖlÛ¶Mrž³³3üòË/Ìš5‹+V’’BçÎY¾|9VyíÜÜÜhÙ²¥¤ÏÒÒ’Y³fñ믿òÎ;ïHŽUªT‰W_}UÒçëëËÇqpp 11‘ŋӥKá ÅáÃàë+í›:ZµÒ~Œ¨¨(óýß ¹\NBBBŽ÷ó*UT  U;.’’ L#)0Jü+5sSlllŽcÙ—ºbcc‘Ëå¥ú ÌÒÒ’   u[swVnØÙÙ±|ùr–/_®õ=ÌàÁƒsô7Žqٷ߯¾ú*’¾Í›7k}? ?`Ìi_ƒ0ožnã áÉKdi •*Ió6={uê³³Bì{\.gÉ’%Œ3FâmÚ´‰¹sçJš³¥ùýõbŠíéÓáÁu2üø#ØÙé6ÞÀY½zu±Û_šÚëׯ—8?šÇUË`YígÏŒo¯¹´÷íÛÇܹséÔ©Ë–-#--Ò€…R©TÛˆâ`òäɬ\¹2Ç1¥RÉСC áàÁƒêå¯ÌŽìÿ4ÙÇõððP/TlW®\™#rgj= ;ƒæ»àäɰb…ñlŽ^½`ÿþ¬öŽ0hñì1WBCCñöö.e†J}(==aÆÆ_ýUjµ?Ai#) Þ_êüÔ­ n<‘Èðä•„Z ;¥ÚŠå7Þ@¡P°ÿ~Ê–-kl“A11s&Ü»—Õ¶°?¿Â g‰‹‹Óq‚\‰ŒŒÌ3G˜H†(Еï8q‚#FpðàA<Ȉ#8sæ [·n%((ˆ§OŸÒ½{w:uêD§NøùçŸlµ@ 0$'OB6¹~:~Ì ´É+꘴-r ¢Äowª[·.#FŒ`Ĉê¾ZµjЯ_?uQOM2 ‚’Gr2Œ•µe V-X¼Øx6 ŠŽˆ t¥Ä;@U«V¥jÕª¹«^½:Õ«W/f‹1ùòK¸};«ma6€ƒCÑÆy€ O^y€@dƒèN‰_è†B¡`Ĉ\¿~Ý`÷Ø¿?³gÏÖúüóçÏçšT™¾ïß¿¯ÓýÿùçFѬ½ÏÄÄÄpäÈvìØÁñãljŒŒ”_¾|9?ýô“N÷Ê ¥RI`` £FbèСÄÄÄ‘‘A@@Çgذa<}ú”#Fh½#99™?þX,ßÀéÓ9wx ]º}l¡2<ùi€„Z +Â2”éJ) Ã?äŠ|íP(lÞ¼™g|÷¸zõ*¿ÿþ»Öç?|ø-[¶äèOIIaÈ!,Y²Dë±¢££4huêÔÁÃÃCÝ¿råJÜÜÜèÝ»73gΤsçÎT­Z•÷Þ{O}ΡC‡øçŸ´¾W^lذ!C†P¹reêÕ«‡R©ä›o¾aìØ±¸»»ãááAbb"›7oÖúÕÞÞžÿýïŒ3†Ó§OÙÆ’HjjÎ¥/77XºT?ã áÉOT¥Š*š—IL ¤¤“a³DÄjM„{ŸÞãñªÇ¿OùÖåi~º¹ÁïchúöíËáÇIJJÊUÇ•‹-ÂÅÅ…Ï?ÿ\ÝwðàA¦L™ÂêÕ«3f ¶¶¶¤¦¦rðàAöìÙ£wÛ÷ìÙÃĉ™9s¦¤ï‹/¾`ì‹jœÙ£SÚðúë¯óþûï3eʽ8j%¹sáÆ iŸ¯/”+gszÆÚZ• :<<«ïÙ3•¾K È á òeùòåüûï¿9úg̘Aƒ P*•¬Y³†½{÷ªk͘1;-+ÛŸ>}šõë׳víZI¦}ûöqüøñ<£;ßÿ=r¹\"n/ˆçÏŸãããÃÖ­[±´´T÷Ÿ:u '''>üðC,^|…´µµ¥OŸ>ôéÓ'Ç8[·nå—_~A&“1zôhúô飾nêÔ©ôë×Û‰¾úê+š7oNŸ>}øúë¯ "%%…Û·oÓ¦Mîß¿Opp0...œ:uŠ®]»Ò¾}û÷eþüùœ>}úôéäI“ɲ¹sæÌÁÕÕ•'Nä:Fi%88g¤çý÷¡[7ýÝCh€ O~ P逄$б&È—Úµk«ë©y{{óßÿñǨs&M˜09sæÐªU+úõëÇÆéÑ£‡Öã{zz²}ûvvíÚ%éŸ7ožäƒ=;®®®xxx蔸òСCØÛÛÓ«W/Iÿ믿Nll,‹/&)))ß16oÞÌ?ü@—.]¨[·.ýû÷çܹsêã;wîäÎ;’köîÝË•+W¨_¿>VVVT«V oooÜÝÝiذ!5jÔÀÛÛ›5jä¸ïóçÏiÖ¬7nÜà£>¢gÏž¬\¹’¯¾úJr^åÊ•éÚµ+üñ‡ÖóRÒ‘ËaäHÈÈÈê«^t(_§BdxòÓB tC|U1,¬-ÙÞÕõýû÷Wÿ|úôifΜÉîÝ»©^½:wîÜÁ××—;w2`Àzôè——ûöí£wïÞŽïääÄàÁƒÙ°aÆ àßÿåüùóìØ±C'[ âܹs´jÕ*GÜW^y…‰'2oÞ<æÌ™ƒ§§'-[¶dìØ±´iÓFrnïÞ½Ù¹s§º©gj¥eÙð·ß~›I“&Ñ­[7‰¾hôèÑôíÛWíœe_[ºt)666ìÛ·Omrpp`Ö¬Y9œ víÚqøða­ì) ÌŸW¯JûÖ¯GGýÞgàÀúPƒü4@ªãÒ¶B òC8@&B¥u¨³ÔtK‡……1`ÀfÍš¥Žð£T*éÔ©“ú¼FQ¥JÎ;§•ª*ðmÚ´áöíÛÔ¯_???ºuë¦÷|L¸¹¹åzlÕªU̘1ƒ={öpåÊŽ?ÎÆ™2e ß~û­ú¼ì"W777ƒî˜ËäÌ™3XXX0dÈu_LL 111DGGK–ÜÜÜJEm¸t -’ö ={Ça¹€º  A¤¥¥1pà@Z·n-îÆÇÇcggGùòå%çWªT‰øøx­Çoݺ5ÞÞÞlذ¯¿þš-[¶àçç§7û3IOOÇ!Ÿd/U«Ve„ êöìÙ³Y¶lóæÍ˳LJnzCÔާaÆ âØ±c,X°€ÄÄDär9çÎcôèÑ9νxñ"M›6-ò=Í™ÈÈœ¥-† ¾} wO‘Èðèª A~H/ß~û-)))têÔ‰jÕª©;wîD&“±k×.ÂÂÂðòò¢víÚñÇPAǯÙeË–eèÐ¡ÄÆÆæú¡ž¦M›booÏÁƒÙ°aöööju^ôèуÇsôèQIÿÀQ(¼ù書¹¹Q¥JLÇŽub9\]]©[·.111Ô®][§1r£yóæìÙ³‡Ÿ~ú‰råÊáàà@ûöís,5ÆÇdzwïÞ;ÝJ+V€æ†¾J•r?”2ž=‚âÇÕ4÷!´ /DH ”RSUZM>üœœŠçþQQQ¤k&è¹\Nttt¾çˆ@[„$J›6I¿í—)S¦ßý…Èð¤á ´G, ³'=–,‘ö£Úþ^\”fíUq¡Häh‹ˆ ³gÛ6ÐÔ´ÛØÀgŸÏñ ¶H ˜5 E΂§ï½Õ«¯Bdx´Ñ‰@[„$˜˜ˆ‡‡§N*ÔõéééÌ;—3gÎh}MFF‡fÞ¼yL˜0¹sçrõêÕ¯[ºt©N%7vîÜI›6mr=#C´À<ؽnÞÌj[ZÂ矿Bdx„H O„ÈDˆŽàùóÓ¿­m ªU˻ԄR©äáÇ$''jüŒŒ ¶oßN:uhݺµV×üõ×_ 0€®]»âîîÎŽ;øúë¯Ù¹s' È󺘘²•&$$"é‹ŽŽæÂ… Œ5ŠáÇӱcG­Ç˜ JÛC†€1rr á)L ÈHHKkk&0K„d"DGðøñ*ƒß§|ùÖù:@ÙINNÆÎÎNR>?lmm¹©ùu<III”)SFÒ÷ÒK/q÷î]jÖ¬©îkÙ²%‹/Î×Ò=zô <<¼À°ºÀ4Ù¿.^Ìj[XÀŒƳG`|lmÁÙ2_ÒJ%„†‚ÆÛ‹@ˆ%0A;vŒ-ZP¶lY*T¨€¯¯¯úX§NðððÈñȌƼôÒKìÛ·€gÏžáááÁ²eËhÒ¤ eË–ÅÅÅ…­[·ªÇsss“8?­[·ÖÉ)Ù½{7 4ÈQô‹/¾È7éáÙ³gyðàÍš5Óú^ÓaÁi»hÔÈ8¶ áÑFbL ÂäÊÎ;™:u*'Ož¤W¯^Œ7Ž7nðý÷ßãïï¿¿?»wï¦lÙ²Ô¯_ŸÊ•+ðèÑ#•&èáÇlݺ•Ù³gsüøq:tèÀˆ#xøða®÷V*•ýôS.]ºÄ¥K—Ô×½öÚkÔ«WOk»ßzë-&OžÌ²eËØ¶m[ç’ššJll,ÿý÷¸¹¹ÑÈX[‰²p¡jks&/¿ Eð™õ†Ðm5@b L  ÂH°´´¤cÇŽØØØ0aÂbbbèС»wïVkiär9^^^lذAr­——...´oß^½#,¥RÉ—_~IZZ½zõbáÂ…êÜB...¼úꫜ9s&GéºuëæéÕ®][½Û,;;;fΜɑ#Gèß¿¿äXµjÕhÛ¶­¤oÍš5DEEQ¥JÂÃÃY¼x1ÿûßÿ„d¢Ü¾ ¿þ*íÚAvĘ@,”JÍïRMæÎ+yÎÉ“'ãááÁäÉ“‹Ç(3#$$777Ο?¯Þ&(ù°råJô:î¨Q°qcV»qcÕ’˜–y: JTTŽŽŽXY‰ï•†B.—“PঋädÐÌ·*“Aj*ˆ?MÁ„††âíí­S–}s¥Ôh€BCC¹{÷n®ÇΟ?϶mÛ¸råJ1[%´åáCزEÚ7s¦i8? 4@Ŷ {{pÒÈ*¢P@X˜ ˜%%ÞJOOçÞ½{Œ1‚5kÖä8>uêTÌ¡C‡èÝ»7óæÍ3‚•%—råÊ1iÒ¤Kb®|óª¦S&uë AƳ';¤bÅŠÆ96À IDAT6£D£­„ZP0%> xøða/^ÌÝ»wñôô”»ÿ>ëׯçÞ½{T©R…{÷îáååŇ~(ÞÈô„““+W®4¶3'4~üQÚ÷ùçªÊïAn¸ºÂ‹äõ€p€9)ñ =z”ë#GŽðòË/S¥JêÔ©C:u *f+A~,_))Yíš5áŦD“Aä2<Úæ!„L‰w€ò#44”J•*IúªV­Ê3¯ ;vì`åÊ•’‡&B7$äMö×KaÚÑѰnº€iÓÀÚZ?ãë«È’%KLÆž’Ø^´h‘D”ßùª­ðYígÏŒo¿©¶ÿþûoV®\Iÿþýñõõ-=޼²”0iÒ$å¤I“$}³fÍRdÓ¦M„岿6%%…ððp”¹¤ÙúóÏ?¹pá‚^lˆeãÆøùù©ç-22___6oÞLtt4›6m"ES@“qqqlÞ¼™šêQ#âã11Yí `ÂãÙ“BdxŠ¢"hAvJ¼t÷î]6mÚÄõë×¹~ý:›6mâþýûtìØ†ÊŽ;|ÈÞ½{iÓ¦ »víÒ»í›7ofúôéœ8q‚cÇŽQ¶lY~üñG–-[FPPÇŽ+Ô¸ .¤J•*Ìœ9SÏ놯/„‡gµË–U9@¦ŠÈdxtÉ”½Xx8¼ö @)Èäíí··wžÇË•+Ç,(&”œœ\,ßãããµ:O©T²wï^N:EåÊ•9r¤z½x÷îÝ<þ<Ç5ƒ ¢L™2( lllHLLä×_¥W¯^;vŒsçÎQ½zuF…ƒƒ¯¿þºÄ1{ûí·9tèÿý·Ö¿×¿ÿþËÕ«W2dˆ¤ÿòåËü÷ß9ê‚eÒ´iSêÕ«§SÚ÷ÄÄD–-[ÆêÕ«±Ï£ ŠH¹¸¸àãã£.‡àææF·nÝHHHÈ1Î7øý÷ßqpp`ذa’õù­[·Ò¾}{ÜÝÝÕ}»wï¦Q£Fxzz²gÏŽ9Bƒ Ø´ižžžÜ½{—Ó§OÓ½{w6mÚDÓ¦Ms]ËW*•øûûsöìYèÛ·/Mš4Q—Éd|ûí·¼öÚkÌ™3‡jÙ¿Jr9,]*í›0 ¨€ ¨)SÊ—‡Ì·ªŒ •d„g‰Râ#@‚Â1vìX>ùä.^¼È§Ÿ~Ê«¯¾Š\.TÉ%ýýýÕ¹sçòÑG‘šš À| .jÍÈ‘#iݺ5³gÏæüùóLš4‰Ž;ªµW¹ñôéÓ) ò###ƒwÞy‡àà`IÿÇœo^§Aƒ1bÄîuèÐ!2220`€¤¿Q£F„‡‡çº|X¶lYI{÷îÝtéÒ…Ó§Oãëë‹««+êããÆËñ»L›6#GŽª¥¶””Ž?Ž¿¿?—.]bÿþý(•JŽ9‚¿¿®:¹\Î믿΄ xþü9ǧuëÖ9´TíÛ·§zõêüþûïZÏ‹>ùé'xü8«mgŸ|bS´Fh€ . Uáù# A®Œ;–Û·oÀÕ«W¹~ý:Û¶mÀÇÇGíüÌŸ?Ÿ¨¨(üüü¨P¡BžãM›6k×®qèÐ!Nž<ÉùóçÙ»wo®çÞ¸qƒÀÀ@Þÿ}­íõöö¦uëÖ’ õwîÜáĉŒ7Nëq´!((ˆ.]º`gg'é8p uêÔÁÛÛ›N:1eʶmÛFRRRŽ1<<<¸uëþþþ\¼xWWWöíÛ§µ kÖ¬ÁÚÚš3fàïïÏøñãY¿~= Ê•âïïÏ;3㺕+WrëÖ-=zÄÚµk9pà&LÈ5 Ú½{w£$ÍȀŋ¥}£Gƒ†ÔÊ$ ã‹„Z?Â2-ZDLLŒÁ‡ÖÊž—_~YýsÆ ñôôäìÙ³’sbccéß¿?ãÆãí·ßÎw¼V­Z©nÛ¶-UªTáܹs9Î{úô)=zô [·nŒ3F+[3?~<¿üò‹ÚáðóóãÕW_Õ»¨=44ýåË—çúõëüòË/¸¸¸ðçŸòî»ïR¯^=üýý%ç6oÞœòåË`iiI“&M8yò¤^íÌýû÷ãééÉ?þȺuëX·n)))\½z…B!9×ÝÝÇša˜bbǸw/«mm Ÿ}VìfèŒÐ]4@ „Ђü)ñ s!3ƒ©âáá! =+ †Š››[Žì·ÚP«V­¡ìÐÐPºwïN£Fرc‡Î¹(Þ~ûm¦L™ÂŽ;6l?ýôË—/×Ù¶‚HLLÌ3Ú%“Éxûí·ÕáãÇ=z4Ÿ|ò ýúõËsL[[Û|—õÅ“'O¨V­7oÞT÷Y[[3aÂ222ɲ¾9;;çªõ2$J%,\(í6 ÜÜŠÕ A A, òC8@‚Q(œ>}š  XæÎ˵kׯRÇŠ”‰‰‰\¸pA¥¼ïܹC=hÑ¢[¶lQ‹¨uÁÞÞžádzaÃ*V¬HZZZŽ>pvvæÉ“'9ú …ĨQ£C‡eøðáDEEi!°µµÕ:w.Ô®]›ZµjiU öÉ“'’¤ ÅÁï¿ÃµkYm™LUôÔˆŠŠÂÑÑQ-€è¹\NBBÎZªáEHb L+iii€jÇÐöíÛ‰‰‰áÍ7ßà÷ßgùòåìÞ½­ÆËÜ•’’Š+ÉdjçääÉ“´iÓ†7ß|“íÛ·ÊùÉdüøñœ:uŠ/¿ü’‘#Gi¬¼¨]»6·nÝÊÑ?räH¾ùæÉޝððpV¯^MÓ¦MuZéÒ¥ GU·ýýýsM¸¨+ä矖$b”ËåüüóÏ9νuëõêÕ+ò=uaÁi{Ð (f Ð]5@"$ÈñUE+  eË–<}ú”{÷î1oÞ}zž×tïÞyóæñäɪW¯®î¯Y³&³gÏfƌԩS nß¾M“&MtÎ@ýî»ï2xð`NžmÚ´‘,ùøøäÐÍž=[²”Ø·o_žûì3Ο?O\\õë×§]»v{~úé'Ê–-K=ò }’=úÓ§hü*Îd)‹%0&ÊÜŠ •ÎEó97&OžŒ‡‡G±d-6GBBBpssãüùó%ªÆÚÞ½{8p —/_ÆÓÓÓØæè•ÈÈH<==Õù C@@+W®ÔºVÛñãСƒ´ïÌÐØdÏûóÊ+б£ql”<„Zfã­[·???@%Â}÷Ýw™?>£G6²e °\»Ù’dcµ‰ …¨fxt­B-ȳq€®^½ª}úúú²`ÁΜ9ÃÉ“'‹%ƒ®@ Ð?‹©²?gÒ¼9¼ñ†ñì) BdxtÕˆ òÆl ;;;RSSIIIáìÙ³êm»J¥’§Â¥ÌŽ{÷`ûviŸ9åýÉŽ¨fx £ /ÌÆêׯS¦LQçriܸ1Ož<áñãÇT­ZÕØæ Y¼XUù=“† A‡œ—Vˆ%0A^˜Ô»woÖ®]Ë믿Îö_/]ºÄâÅ‹±¶¶6²u@?†ìI¦gÌPe6W„ÈðF$"@‚¼0émð±±±œ:uŠîÝ»ciiIÿþý%Ç{õêe$ËAQXºäò¬víÚ0dˆñìÑtîÜY,ƒÈÈH‚ƒƒuZ A^˜´°|ùrÆÇðáÃ9r$uëÖ5¶I9ˆ/Y3m‰‰‰ÉóXx8øúJû¦OWeé5gD ã Ph¨JxoÎÑF~0é·'''Ž9Âýû÷Ù¸q#¯½öŒ5Š·Þz‹2eÊÛDÊ—/ÏÚµkY»v­±MLŠ.]ºäÚ¿b$'gµ«W‡#ŠÇ&Aé£|ypp€ÄDU;- ¢¢ÀÅŸv ŒYÕS(>|˜ü‘¿ÿþ›~ýú1jÔ(Ú´icûiS LPtBCC…ÝÀ˜ÊÇÆ‚›ÄÇgõ­X%¡”ž¨fxt­–I½zp÷nVûòexé%=WBµÀL™LF·nÝØ¾};7oÞ¤I“&L˜0///~üñGc›'($þÙ3á ôŽ©ÌñêÕRç§R%;Öxöè‘Èð&!´ wÌÊÒÄÙÙ™?þ˜‹/òóÏ?“¬S˜ãÇ7¶ %S˜ã„XµJÚ7y2˜ÀJ¶^y€ Oa4@ªë¤m!„€9@·nÝbÕ‹wϤ¤$f̘Á'Ÿ|BXXÍ›7/ÖÊÕ@wÖ­Si/2qtñ²"$È ³q€6nÜHdd$ßÿ=ÀÊÊŠaÆÙ2AQ) kÍÆÆØsœ’Ë—Kû>úHå•D ÃS˜<@3$ ˜‘ôðáCÚ·oÀ®]»˜5kK–,áÉ“'$fÊûf‰©èSJ2ÆžãTm?ÎÄÁ¡dŸ5 ã/ X€9@•+WææÍ›„……qéÒ%ZµjÀóçÏyþü¹‘­SЧ”tŒ9ÇiiðÍ7Ò¾qãJÞ6d¡2<…Õ‰%0An˜4zôh¾üòK<==8p 5kÖ$88¹\nÛ{AîlÝ fµmmá“OŒg ô!DЂÜ0›„Mš4áúõë„„„¨£?–––ìÙ³ ‘ÒÓ¬1•5%cͱB‹IûFŽÌùTy€ Oaóå– Z 0›Б#G¸|ù27ÆÒÒ€f͚Ѯ];#[&(*ÆÖ§”Œ5Ç¿þ ·ogµ­¬Te/J"Bdx «rr{û¬vj*BK-(a˜Ï¢E‹puu¥mÛ¶Ìš5‹#GŽ’’blÓEDh€ 1æX©„… ¥}ï¼ÅnJ± 4@†§° BhANÌÆêׯ'Nœ **Šo¿ý–2eÊ0xð`œœœŒmš@ È…}ûàÊ•¬¶L3fÏAéFl…dÇl €äädöíÛÇš5kX»v-¯½öÛ¶m+Ò˜ üöÛoœ>>ܼy“ &pôèQ6lÈ Aƒ 5žL&C¡P¨îS*•8–¤ÜüøçÐxY%?ú#0}Ę ;fãܹs‡Í›7³qãFþúë/’’’Š”hçÎøúúÒ¬Y3ÆÀ¤I“$çœ>}š   ÉCÑ.z[SŸb ö”ÄvæÇý¤ÑŸ :w†¶m w?SiGEEqäÈ“±§$¶>,Ñér½*”Õ~öÌø¿©´oß¾MPP+W®äÔ©S¥Fk6З_~I³fÍØ¹s'^^^ìܹ“°°0vìØQè1÷íÛGïÞ½Y´hœûŒŸþ™x§5k„ÈðrŽW­‚¤¤¬¶«+”Æ•d¡2.\¸Àœ9sصk÷îÝû¤ç×/ á1ÔÇÇÃÚµÒ¾©SÁÆÆ ·3i„ÈðU$–Àš˜(oooµÓsóæMzõêŽ{÷Œl• ( á1Ôÿ½j+q&ÎÎð"©z©Ch€ OQ5@"$ÐÄä#@ …‚™3gÒ¬Y3FŽÉÍ›78rämÛ¶-R5x@PxRSaÅ ißGAٲƱG (¡hbòŸŸ;vì`âĉԪU‹·Þz‹Õ«WÓ·o_¾ýö[–.]jlEDh€ !æxãFÐÖÁ&NÔûmÌ¡2<úÖ‰%°ÒÉ;@ÿý7_~ù%#GŽdöìÙ¼òÊ+|õÕWìß¿Ÿ‘#GÛ< ãï9ÎÈ€ìß=ÆŒ‘–(m á)ªÈÙYªOKJ‚çÏõ`˜À,1y(..ŽªU«ªÛõêÕcôèÑtèÐÁˆV ô‰Ð}ÏñÎðßYmkkøä½ÞÂì8p K³X Uda'€ˆ•fL^­P(8zô(±/”–W®\!66–íÛ·«ÏýôSjˆí&% ¡2<ú˜ãì;¿Z·†Î‹4d‰Bh€ >4@b L‰…R©TÛmHMMeóæÍ¬ZµŠ6mÚ0sæLêÔ©cÐ{Î;Wò,”VîÞU Ь>èÛ×x6 …!"*WÎj—-«*ê+PŠ··w©ÈÐoò‰3±µµ¥oß¾ôèу-[¶°oß>c›$”¾ùFêüxyAµ¨QpqQ%îÌ$!Aõ”>Ì aâĉ4jÔ¹\ÎíÛ·™4i’±Íè‰ÒðMÃØeŽŸ>…Í›¥}Ó§«²ê ²µÀ OQkÈ-ÈÂä  ðòË/S¾|ynÞ¼ÉêÕ«qww7¶Y="vñž¢Ìñ·ß‚\žÕvw‡!Cô`T CÔ3УQ% ¡2<úÒ‰Ì` LPò ãë§¥ÁòåÒ¾ ÀÑQF•0D-0Ã#4@}""@ ?ÿ gµíì`ÊãÙ#èá @8@@h€ .s¬PÀ’%Ò¾‘#¡J=U ã/ X€X˜¢˜áÑeŽwï†Û·³Ú––ª­ï‚üµÀ >jTª¤Jå鯦>Wu9Yx*)RH}¤zVÊ•XØX ³•©žm´.Ì5ˆ¬.ÅŠÙÔ3¢˜ 4Ò¢\¸Õ:¶l1ž=AQI‹L#åá Çæa )Rس.•òÉ)T!'äR XXçï •iP††[bae8O©4Õ @ æàA©ócaŸn<{‚‚P¦)I}¬ŠØh:9™QœÔG©d$eä¸îe#ØZÊ4%i˜ûñ„Ë Ø×µ§Ö‚ZÅkX E8@£JÕìÅyzEÛ9^´HÚîÝ76Q%Œ¨¨(±i²õ‚R¡$-2´ð4äárÒÂÒH|–HÌÃìÂíÔŽŽü™¥¢ô,d¡ý)…d:4ª‡<\.y–üžFZTÊ ã.?YÈ,°qµ‘88Ù«ò9?ê¬|áÑ6ƒ IDATÚ®¬¶)l…·°²ÀÒÊÊHûíkÛ.žoCÔ}Q{£x²ú Õ?ŸY…¥Ô;@ÇgòäÉܾ}™LÆåË—ñðð0¶Y¥ ¡2<ÍñâÅ ¹´Y3èÑ£ +A˜¤H i19š´ð4äªÈLZ„ÆÏ‘Æwh4±*o…uekl*Û`]Ù  Ò+§SµaÕ,§†m¡vE™[2ÄZ‹jKü…xuß½ÏîáØÁ‘²MËÑ2óÅ„^©ÅÏÑ£G4hsæÌáwÞ¡\¹rÆ6©T"4@†'¿9~ð¶o—ö‰èî§(ùn2òP¹ÔyÉ-R™†2Ýt™LâÐô,³•æê}úô)ÁÁÁ4ý_Ó"Û’=¢©;@2¶7"¸y°z)Q‘ªàúàë´8ßË2–F¶Ðü(Õy€:uêDÛ¶mY”]üð‘HPøðCðñÉj׫7o‚Lä‰7äarbÇs0†èƒÑÈCM$oÌ+g+­+GÓùÎýì™Ô rr‚˜ãÙ£-¡?…ró½›’¾j£«ÑÀ·~Æ/Ey€Jí[œ\.çøñã$%%Ñ­[7^yå¾üòKäréK\\±±±’‡&¢-ÚæÜ¾};–”œÁ´iYαí+­mEª‚˜Ã1\œt‘`ï`NU;Åwop÷§»ç'ÉõEm'ʱ®dC#œ::a×ÏŽêTÇcŽõÖÔ£ÆÆxzóòÕ—y%ü^Šx‰W"^áåk/ãèë®Ô[]÷/ÝqçŠUg+_qľž=VŽV&3¿•+ƒ¥e¬Æ1 3ûòjW^•*CUui2ÿ~Ï6<#|gx¡ÇONN&66–‡òüùsJK\¤Ô:@?F¡PàééÉŠ+Xºt)ûöícJ¶ŠlÚ´IòÐD´‹ÞÖü¦a ö”Ävæg?þÁ›HIÉj;:nbøpãÛkŽí¨¨(~”z“:]Ÿx5‘•#Vr¥ÇNT8Áå×/ã÷ —Ô»\_Pû€Å,‡¦“'¼O¨š¹Ô[[Kï]R94×TÍíe·y%ü…CäÍ鎧©·¶s=¨þauöÅîé“^XW²fóO› ýû¦íçç'©V”ñ,-ÁÁAz|ÍýÚk¨výïëc_Ç^ò÷¾=ö6Vl(Ôxÿüó›6mbòäÉìØ±ƒŒŒ¢íÖ3JíØãÇ©Y³& 888°wï^>øàBBTJ{±V<¬[·Nh€ LnsîîªçL–/‡©S‹Ù¸®]»tÒÉó–µbÅú4Uë{YU°¢lÓ²XWz±¼TÉ:ן­œ­°•œS™ ¢ÖË${Ù—'à•Wô2´Á‰?Ï…W. LËúwlçˆ÷Qï"•Ê(MK`¦³ [̸ººbggÇ7hÙ²%ØÚÚÙ²Ò‡p~ Onsìã#u~œaìØb4ª„QP Eª‚¸“qjOÂ¥¬ÈNAXXYP¾Myœ»9S¡[ʵ,‡…eÉql´ÅÕÕUoÎj<©dêBhMʽ\ŽÚ jsoÚ=u_Ü©8Ì}@­ù¢T†6”ZH&“1qâD>ùä¶lÙBHHk×®å½÷Þ3¶iÁII•+¥} eÅnZ½’x=QíðÄ˵&U^Ø×µW;0žèCÑÄŒ!õ‰ËZNVTèR Ý*àÜÍ»Zv´Ö<‘Ëå$$$àì쬗ñ\][TJ…Ê)”¥÷0•”BRS OKc“§'M^È$L ðüÉ“à—‚‘‡«Äñ’RE©Œü(µ m âAh€ æ§§«¶º?xu|òdX±Â8¶™;I7“ˆø-‚?îÀó¾'å•嵺ÎÂÒ‚ò­Ë«žr­Jç²–.èªJW*y’šJHæ#%…G?߉K%Á2-ß1*Y[ó··7MÕ ¢Dså+’%U—ÿ¹ÐøwÝ+—& p€òA8@‚’È–-0lXVÛÆî݃5Œg“¹‘øo"¿E±+‚Äk‰Z_g_Û^íð8uq2©¼8憓Ëyô"Z£é䄤¦ò(5•P¹…>â*Y[èí— ;A÷>½GÈòI_½Õõ¨þ‘n¥2J“$^}A)B©T•½ÐäÝw…ó£ ñâ‰ØAäo‘$ÝNÒê«òV8uqRkyìëØØÊâ']©$>#¹B\©4øsdZ!))<‘Ë‘+Åò;F¤¥ÑåòeþnÚÔd Ú‹j«Jeœ×(•ñé‹R/ q_nH`t„ÈðdÎñÞ½ªÂ§™Èd0}ºñì2i”ðüìs"vEñ[)÷Sò=ý9Ï)kY§—³žò­ËiK²)¡î''s51Qò¸•œ\lŽééœ ú,[$—A„-„ÛbgÇgïÙRÓÖ– ü ¡Š—Ëérù2M›ÒÈ k U©Œf¹”Ê¥2rC8@£#jžÌ9Î^õåÍ7¡~}ãØdŠ(JžŸzáôìŽ 5¤`³ÌFF…®¸ëq—VSZQµ®ù;óORSÕεÏד’H4v‚¼¸8¸u ÚµÓêtK ªÙØPÓÖ7;;jÚÚJ~®fiKÍr6dúo©À3 3Š­LÆw«ÇS;AÞÞ4,S&ç Œ}]{êûÔçÆð꾤IÜ|—ëõS*£$!4@ù 4@‚’DPtî,í;š77Š9&ƒ2CIܱ8•Ó³'ù³‚ëlÉìd8ww¦ÒÀJTìSÑlµ•:@ßÕ«‡X£á…Éåt¹t‰@ooƒGKñxÅcõŽ˜ü°v¶¦b_•ÓS¡kd6º/­T ¬(d(•œ‹çHL ‡cbøçùsRõ°óÊF&£½½$šãåà@m;;dhgŒ¾k©Æ”¶ó«¶¶~}”À: 'è™F$¨¾‰9A5?yQ*ã€F©Œ…¨ðš(•ÂJ<ß|šŸ•^^Чñì1$ ¹‚§ëžòpþÃu=Ö•¬qéç¢rzºT0¹íê7’’8üÂá9K\„É–Ôµ·Ï±tUÏÞ¾@‘pI'{(?ÈðyáýÝ z±EÞ¤œ ðÜìIpÓ`äa¢TFv„$0:Bd UͯŸTs<}:”¸Ï<%„oçþ÷Iþ/9ÏÓlªÚPéÍJTX ÇŽzÕBUô$5•Ã11‰‰áHl,OSµ¯#–‰àag'‰æ4vpÀ³Llõ 66ú®9#@Dµ¾á­×8ùij*/_&ÈÛ›zö¦“ðÒ¦Š ž?yr¥GV©ŒÔ'©Üu«P¥2J¡Ò?ÇŽÁ¤IšâN•ÈÆ 1ž]† æp ÿMÿø ñ¹·©jCåA•©4°å_)…Ì0ÞŸ® Øôt‚bcÕQž[IÚe—Τ†­mŽ¥«FeÊà`i>»Ót¥84@ùE€2±ÖÕ¯B©dƒÆOSSÕËauMÈ rîæLÍOk²4«TF䑃;s?¾v-|ðAñÚd(.&poú=bÅäzÜÊÉ ·Ïݨ1±2{ãG?R NÅÅ©¢<±±œ×I°\ÕÆ†×*TP=œœŒ.H.)„„€›[VÛÅ""´»V Œ¹u ¿l^Su[[“s‚”iJ.´»@|pÖ™ŒægšKJeˆ<@À¬HJ‚%K`éRU¥€ìØÛ«£1cŠß6}“r?…û_Ü'ì—0rÛÛ-³•Qý£ê¸ÍtÃÚÙx…RÉ…„µpùD\):—Ë[YÑÑÑ‘®/œS­AeîT­ªZÎôE£¢ - ¬µø×±|4@ ü¨á=шÕ1'HR*#þE©Œ7†Ü Ep “ø’PÜH`t„¨hüò L›û% Ó¦…Ò¢…yÏqZdç?äé÷OQÈs:2 ª¼[¯=°s+þèHTTáÖÖ%$p8&†À˜2*ÛÊd´-_ž×*T k… ¼\®–%N¬U4 ¡²¶–F}”J …š5µ»Þð­_¥RÉF¨ÉãÔT:]ºÄQooj›ˆd_çE©ŒaY¥2¯'rgÒRY*C8@£#4@…ãüy•ÎçäÉÜ{{êUС¬[çO‹æ9ÇI<þö1!KCòÌããÜÙÚKj{Õë뉉ÅÆª´<¿ÿNLãÆP¾¼V×Ê,,ð.[VáqrâU''½d6.ÉB*!´æ²×Ó§Ú;@ ú[nx Ú”‹dBNP•w«s(†ÐŸ²ì|æû çnª,ç¥ á ŒŽp~t#, f΄M›¤ÛÛ3©T ,€÷ßWU{óœceº’g~ÏxðÕƒ<ës•kYŽ:ßÔÁ©³ásš(k‰‰}áð%"Mc«½:ëÙÛ«#<œpÖfE Æy€@%„¾|9«­:;2 ü4@ü¤á…dF‚š5£–‰è¶ê­­GÜ?q$ßÑ(•1FU*ƒâIêmH 0ärUDgþ|xþ<çqkkøøc˜=‹ß>}±;‚û3ï“t+÷Qöu쩵°•ߪ¬Zƒ0Jàj6‡'2­àšašá²y m6è‚YX°±A”J%ÿgïÌ㛪Ò?üdëšîiÙ·"-; *²8:¢‚+ˆ:¢è€Û ãÏÑQgÔÜ7dTÅeTT\ "ÈeiYÚBÛtß›&Mr\š&MÒ6mÒt9O?÷sï=çÞ›7§ÉÍ÷žóž÷]e—`ì´]OPgA*­œ*cï{mÃÉ ©2z­éÚCåž Àï –ùòKxàÈÌt]ùåðïC²›aü®ÒÆå[Ê9ùðI*u¡ð€€øú=ÞÄ»Qh¼«|$à`uµ,v**Ø\^N‰'‚§²’ðˆ¦DG Çeá  ð,tK( V †¬¶A9uu6Çèþ@… cÀs8ñ€]ªŒm¨_î9² ç¼SA§Eø¹çȸÿ~øö[×õÉɰd \qEó×éìm\“^ÃÉGNR²¡Äe½*TEŸúÐçÁ>¨Â¼çFœ<›ÊËÙRQA©‡=<¡*FD0%"ëæÍÌ¿àzÅÆzÅ>3¾òjK, æP*¼V}`'‚²ëêl=AAõ¹¿eß—Qº±1UFé«¥DÅDùѪŽC ßéÌ?Ìþ¢¼-’ãö¸šH!u-\غ麵gŒœúç) Þ/@²:ÏiW¨èèèÿ¯þ$´Ï9Á*I°sZþ¹ ‚'L¥bRDS"#™Éø°°ÆT·ÞÚ.û-ã+ O£A·{ôaÔÐÔÏß"È>U†¾1UFª©gDˆH èDX,ðöÛðøãP\ì\¯TÊÎÍÏ<#;;wUÌef²ŸË&wi.Ö:×±qâfÅ1ðÙiûì™ýÕÕüÔ xÊË=š–¡Vs‘à«ÕŠ©éÝo÷5 R(X9l’$ñQa¡­<ëlOÐæÑ£ýîÀ9+Ïaÿeûmqµ‚$ÿ÷NuB üNWñOñ5›6ÉÓÚp]ÑE²ô˜1ž_»³´±µÎJîÒ\²ŸËÆ\æZŒDN‰dà  ŸÐºéäM1Z­|\XÈÒÜ\vW¹NáŽ(µšÉgÅΔˆFkµ­ÎŠÞÞ\`‚–ñ•·œ ]¡R(XuÎ9X5.DЦN ‚¢.¢ïßú’óbŽ_íèhDÐ ßY¿~½¿Mð+YY0kL›æZüôí kÖÈù½Ú"~Àÿm,Y%ô+ô캃p)~BSC±a£7n“ø9c4òè©SôÙ¾yG¶JüÄj4\Ç«ƒ³oüxJ&Mb}j*÷÷îÍØ°°V‹sUTTxl· õ³uëV¯_·©*.v=ôÜVT œs7ÄÇ;”Ÿ:+‚N·!ñ­·ðôÂÎ ó·ŠÈÖ "˜À—ÔÔÀsÏÁË/C]s}Hˆœ¹ýo“SYtUJ¾*áä#'©9Tã²>°O ž@­ mJTúsEKÏœáÅŘ[¸Å0ÅnH+%4ÔW³è]ŒØX9 FgÎ@’—ó„š%‰›fm“dcƒƒÙ4z4}½û‚b8i`×è]¼¥x‹U«üjKG új?°nüßÿAn®ëúo„_ô,mg£rG%':Iù–r—õê(5ýþÞ¤…I(ƒ<ëŒ6X­|XPÀÒÜ\öWW»=N©PpEt4WÆÄ0%2’sBB>éðaÖÙ‰ “ƒÍ1º·EPðÀ`^H û±l¿ÙБ$ð;Å?¥#°Zå(Î/¼àº~ìXxí5¸ðBï¾nG¶qmF-§þqŠ¢O]§ÔVÉÉJûý£ê(ÏnAÙuu¼‘—Ç;ùùÍÆè‰R«ù£NÇŸ’’:lº±ðò=¾òyìàÁÆ}o9B7E­PðÑYô©:a0Ø|‚ü)‚¯ '㉠¿½~G"|€~Çßþ)EU\skñ“_ýæ}ñÓÆVƒ•Ì…™ü–ò›Kñ£P*èu[/&dL`ÐKƒ<?iåå\wèƒvìà…œ·âgDh(o%'sæüóyiРµ"|€|¯|€À7SáÝ¡V(øxøp®k2•óÄÙž ÜNàÔ*¿ÓYcÔx“S§`æLHOw,×hä™_?ÞêšmÂ×ml8a ýútª÷»ŽŠ¹"†Ï$tDë##×Z,¬>;Ìu¨ÆµÿȦWÇÆ²0)‰©‘¾Ï æŽY³fùíµ{ ¾Š¾› ïŽ4'=ÿÙż8n×”ägŸ îŽ@Ù¼®¿ÞÑ¿ä8>Ÿ~*OoïÊ^ÌÑÛŽb®pž6~^8_HäÔÖ “Suuü'7—ÿæçSÞÌTœ†:÷$&ú}± ëÓÑ@£P°&%…ÒÓYßD5ø% ä3ĘÀïèí2'w7Þ|.½ÔYüŒ)wu”øñEK‰“œäе‡œÄOð `R>Ia쎱­??”•qÕÁƒ Þ±ƒ—OŸv+~Fkµü÷ì0×svñSRR‚Ù›s§N˜L&JKK[>° tä˜=…‚ORR¸ºI •Lƒiû÷“'†Ã|†@¿Ó}€Ìf9MÅÝwCSw•k¯…_~~ý:Îo·±©ÐÄþK÷“óBŽ-zl 7'0~ÿxâf·ªºÚbáõÜ\†ïÜÉ¥û÷óeI VSÙÕ 7ÄÇóó˜1ì?ž;t:‚”ëö%|€|/}€üÑÔ€F¡à“áùª‰ʨ­eÚþýä›L^M³$‘g4²»ªŠ %%¼“ŸÏSÙÙ<¢×c5Êë¯×q€šAÄ´…²2˜=~üѹî±ÇàÉ'¡+gS¨ØVÁácÌs|2U(´dIjyîðqƒe¹¹¬Ðë©h¦×$N£áÎÄDîILþŸ’•4îët× Ô€Éjåúôt64é2N !môht-çë±XÈ7™Ð›Lä›LäÛvåÅõõ.6Âÿû_*V‰8@=‚%K– ×ëyñÅýmŠ ‹sô¨ìì|ü¸cyp0¼÷̙㻼řWÎpâo'ÌŽ7ÎÀ>¤¬Mi6‚³|[ZÊÒÜ\¾))iÚqäÀø°0&%1'>žÀNÖÓ#èž4í*(só©TgC€Rɧ))\—žÎWv"èXm-ïÛÇ{ÆQi6»4 B§Êbé8ƒ»8=^­\¹’E‹1vìX›Òcé.q€¾ùnº šŽ‚ôî ë×øqþ± Ú߯–* Gÿx”¢µÎÓÛ£.bø‡ÃÑĺNK_i6³B¯ç?yydÔÖº} BÁ¬¸8þÒ»7}9%ÎGˆ8@¾Ç—q€!*JîÁ9fWa¡³0ò5J%Ÿ¥¤pmz:_Û‰ £µµœ¿gOÇÓÍéÑViii,Y²„'Ÿ|ÒߦôhºƒÐ’%0c†³ø™8Qvvö§øöµqÍávŸ·ÛYü( ßcý¹q¤Kñs¬¶–…™™ôÞ¾ûŽw+~zð¯þýÉ>ÿ|>>¼KŠ>@/}€ÀŽÐMiA—û@è5 R(Ð06,Œ+bbø£NÇcýúñlBû÷ûìu;=öQåðáÃÜ}÷Ýlܸ‘Í›7ûÛœMWŽd2Á]wÁŠÎusçÂÛoËO–þ¦­m\øQ!ÇÃRãØ­®ŽRsΪsˆ¹2Æ¡Ü*I|}v˜ëûÒÒf‡¹&„‡ó—¤$fÅÅÐ †¹D ßãË8@ ÷öØÇêêHGè¦*•ü/5•É[±“ÖÏ| V*Ñ¢  W@º³K¯€[¹. €8Æe²_½^Ï«vYë»3]ÿ®Ó ¸þúëyÿý÷`ïõæ‚+V°hÑ"‡Å±ßs÷ aðàEMÄÏ"”J9×Ê•ðÜsÇ^Oö¥z‰Ì…™€½ŸdçïSáרÒÀ+¢ÜÅÈ‚î×õá²/¾àÃáÃI=š£çÇ#?ýDíäÉœ˜0­cƺv-K‡ áýúq‡NÇŽ×^c´VKB@J…Âöz6l`Ñ¢EL:•Å‹SßLš™îDœöàƒrôèQf̘ÀöíÛÙ¾};ýë_¹ãŽ;8ëißðáhú!x—®è´o\}5ää8–‡‡Ã‡•WúÇ.wxÒÆÆ3FÒg§Sùk¥Sn¾Ž!ˆ  l-f3<ÉÛyyn{|¹'1‘;u:â[1“¥+"|€|/}€yÄ1UÍ¢Eð¯ùä¥\RZ*ÇûÏÜ'Jn`ÀxåpÛ!f6C]¼ ­Þ®**böòåll¼¬Ò#¿©S§NE­V“••ÈãÊuuudeeaµZýk\dýúõ]jìÓOá¶Û iv†Aƒà‹/`øpÿØÕ­mã²Ê8|óaꋟ•ÁJ†þg(½nwQŸñçÌL·qJ.Œˆ`aR×ÇÅ¡îÊsÿ[AZZÓ¦M#&&¦åƒm¢¸¸˜]»vùl¬©׆À$©QdÔÖ:­Ïdøæ3;7Õ¢2¸Z‚1ÒdDAÔÉÛ§êººŽ¬0I1uh,MMƒr†£º¨ž§ôÈ ¦¬X±‚+V°iÓ&‡rÑ$hÊ“OÊO…M¿5_ kׂ}}‹ÙÏf“õÏ,$«ã› Lʺ´£µ¶²<£‘?gf:ä0j@©P07!ûz÷fŒVëT/tVÖ®…n€^èÆQ¦+âá…g…ŠñÒìºa»®Îù¦Ñ‰y8<œz€Cì<¥¶n¿>ùĹîÞ{áÕW¡«Ž|˜ËÌ™{„’¯œ»¼c¯ŠeØûÃPGÊoNÞÎËã¡“']0L åää.;“KЃ0™ä€]GÊ˱cLÿí(å#‚³?þ»yþ4RàKºè-ۻ̛7yóæùÛŒKg÷:sFö÷i‚C£×^“Ó]tvܵqÕž*Òg¥SwªÎ¡\¡R0àéô}¸/œ¹Ê¨­eAF[ÊË® Tòhß¾<Ò·o·˜ÑÕ„ïi“Pq±ƒÈ±mŸ:%G:´#ÌËöú• ‚‚ä%8¸ÕÛ5 û>üÐßÖwâ›*ð;Ùè×_åÜ]Ms‰ÆÄÀºu0uª_ÌòWmœÿN>™ 3±Ö9ú½Ä0üãáDN““˜.Ç~ IDATÖK/æäðTv6F>rDDðNr2ç„„øî t:¥ÕÚèâ¡3¬GÛV+DD@d¤ãå\ÖPáq·©[ ³Nžt9ÇŽ9g"öF©%Á¶m7¬£B˜Lßa!(Cƒ!$D%ök;±"ñMZ‹ÿLn‰ìd ø¬—P(5Üÿ'xüqðd$ºJ¯gÿÚµ¾kˆN„ðjáÔ³Yµ ,€¦É˜SRdgçýcW{±ÖYɸ7ý{Îâ#.ˆ`ø'à L’ƒí¬¬dþ±clêñ „©T<7p ÷&%ѽݛ; åå°s'ìØ@uuËâÄI4½ŠVÛzÁÔ°m4:‹œãdz·ƒR¢9F2y$råì‚¢‚ÅH3k)8„Í;ƒy}E_§É‚Çê"êŒR ×\÷ß“&µÍÖÊJy¦Ú²e®ýž“’àå—[Ÿ†G¯×3zôhôMŸúº!¢H h‚Õ ÿ»˧)3gÊñ9ºh_¹á¤ôëÓ©ÞWíT×û¾Þ zi ‚‹…ÇNâµÜ\— gÄÄðúСôé Q»#õõ²ÈÙ±£qÉÈèRŽ´­¢ºZ^Μéø×V©ä¹äÉÉ0l˜¼$'3iþ0¶eÄÙÛów3¦u—4`õjyzúáÃî ƒ;î€ûîsLÀÚÂÃáßÿ†?þþügh×77n¼Qž^¿lY眥ê/„øÎäTU7ß 68×=ò<óŒüÔÖÕÐëõ¨wª9zÛQÌ厉*­Šäÿ&C<KK¹;#ƒìº:§ëÄðêàÁÜß!vw%Ú唕å(vöì‘{p˜€jÀ£É–áá6qc/t2\Ĥ ì d4îçç·,€ôz9vÏòå²Ë‘;úõƒ¿üæÏ—Íò&©©°i|ô<ø s´45J~ýE‹ºîCœ7Hàw:‹ÐÉ“rOÓ'·  xçøÃücW{‘,ïÜþ“¾DÓH…!ç„úY*!ÃB(®¯çÿŽ烂—×¹­W/– D´ÆuÒÓžN«}€**äqö‚ÇW© 9‹+ÇWc[µ]U%g-/w^»*++óxˆ®Ø8ER*¡o_×BÇÃl¦žÄÚ·Oîíùè£æßÊùçÃ_ÿ*ûú:»üM7É÷±'Ÿ”m³4›å¼…}$÷pßr‹oméì$ð;AülÚ³f9ûK&&Âÿþçç³ÚŒd•¨Ü^IɆŠ×3騳ƒAüñ$¿ŒJ«buA÷?N± ?ŠAA¼™œÌ¥QQaz—Åe.0³t;G¶m(K­†‘#aÂùß²0éìC”ƒ{ÑäbX^ÎU*•,lì…Î!òûõMõRÓžI’{ˆÿýo¹WÅj5\½ìß3a‚WLk5Z­,pî¸.„~p¬ÏÏ—s¾õ–<,6rdÇÚ×YHÐãY¾\¾I4u <÷\X¿Þù‰°³b®0Súm)%J(ýº”ú×N¡ ‚Á/&iaYuuÜ} oK“-ª îëÝ›§ú÷'Ä×­Ý…œG±³{·ü#ßúö•9–qã¼ö#ßi>ë\܉¾dîz€jjàý÷å^•ÌL÷çGFÊ“'.„>}|ggk6 ¾ÿ^ž±ú׿ÂéÓŽõ?ÿ cÇŸþ$÷EDøÇN!ÀïøËÈl–_ݹàÝwåéÎŒ!Ó ÷ò|YLÅÖ ¤z×= ¥”M4I¤¬MA;1ŒWΜá±S§¨i `”VË;ÉÉŒïnދղµKC$ßV,%ååD”•µí¦ãÇ; ‡nz¾ÎÎÍžž.ûÿ½õ–ÜåŽAƒäûÉí·{6í¼#˜5 ®¸Böa\¼Øq¸Îb‘ã™}ü±œí²ËüggG#ÀïøÃ¨´fφŸ~r,W(ä›ÄßÿÞ¡æ´É,Q±µ‚’ %”|YBmFm«ÎÛ¦ÜÆí3n'ùdŽ™¿g¿UU9¤Tòx¿~<Ô·o×ÍÝ• [¶È·Û·Ëãš BŇY®Ó€i@‹Q€”J9–BƒÐ™8QžšÓ½ë;_çç -[äÅS¦ÈÃ\3gvîaHˆ|o›7Ojß|ãX_X(‹·ñãc°Z»jNÏq€šAÄêž9"߬Nœp,×jå)î>¼·¶‰úÒzJ¿)¥äËJ¿-ušÅåu¤šèË¢‰™CôåÑX"U<•ÍK99Ô»øÚOŽŒäí¡CÚÕ9Ò(x¶lqîç÷7:]£Ð™0Aîéél]ÇË.EÍ¡ÑÈquî¿_BêŠ|þ9üßÿÉ›þ.â€t3D Gñõ×òðVe¥cù€rpÃÔTÿØÕ”šÃ5¶^žÊí•H–Ö=§„$‡3#†˜1DLŠ@¡–{q6——sç® 2j{Œ"Ôj^8‰‰? ¡Å"O½i3Èm©M\}µ<ÜõüóòЗcÔ…N'ð B üNGù-^ ?,:´gÊÙI06Öç&¸Åj²R±¹‚â/‹)ÙPâ”›Ë ‚È‹"m¢'xH0I"Ã`à›Ò"öVU±»ºš´'\øM\ˆ !±³Î2åèÇ ‚ç—_äéÖmE¡hŒØÛ°4Ýwµ´â˜’º:"FÝÝ•;á*Çè±H6Lî-¹õÖîå‡$Ǻí6ùý}ñ…¿-êX„øŽðzôQxöYçò;ï”§ú#´©ÐDé×ggm}WŠ¥ÊÙÙš8 1—Ë‚'è’p«Œ|U]ÍÞêÓìÛSÍÁšj›:6oÝê0¶×+ €¥C†0+.ŽNEUlÛÖ8œõÛoιHZB£‘‡™.ºH^FŽ”Õr)ùÈ·)mÝ:¦%&Ó~!;árïNe%\z©, .¿Üg›NÁ€òØ×_ßÿlé,éÓ|ŽðjáÔ=øüs9™ý']­–ãxüùÏk‹d‘(ù²„ÜÿäRöc™S`Bw„Ž%øŠ(ô³g¨™}µ5ì«®æ˜Áà2UEsüQ§ã¥Aƒˆê YË‹Š‡²¶lýû2t·Hh¨<ätÑE0y²¼-Dˆ ,_.”zbÚˆœœÆŒ™KIÉwþ6Åçt‚; @à;Nœ»wí5BT¬] ¿û]ÇÙQ_\OþóÉ{#ºì–‡·JL†pbj›'XÙf Ïx6_RVÛlÌÛÉÉL‹ŒlÛ¼ANŽ£ÃòÑ£ž_#:ZÎÙÐÃ3nœÇYÅ‚æè±YýF@€„FsÀßftâ®!ð;¾ò2äH¬ej5|ù%\x¡×_Î%U»«È]–KáÇ…Xë¬Í[§$}’šoϳðóh uAv K=ŠÖh­ÕÚ–Þ55\4`@ÇOm?rı‡''Çók$%5ŠÉ“å)äp<¢]¹À­¢#|€=ñMø_ùÝ{¯<¢bÏ‹/ú^üXMVŠÖ‘»,—Ê_+›=öøPÛΗØ~>d µ")<ËÐ?(ˆÑZ-cìOß&—/_δŽx¬ÍÌ„¯¾’ÅÎÖ­ò—§ Ò(v.ºô¾> Õ¹Àm¦£|€=áÔ Â¨ëòÎ;r8z{fÍ’‡¾|…1×HÞò<òßÎÇTà^Èá§‹á×Bf ñFìÑ(  uèÙ­Õéï“ >ûL•Û\r$W(•²“rCÏE¢‚ ½^ÏèÑ£Ñëõþ6Åçˆ A·cÏ9=C‡Ê©-|Aùærr—åR¼¾ÉìþyBß >¿¾ºªZÈ0®V3*4”1aa6¡“B@g 5{ì¼ý¶œ ©µ±xghMž,wÇõ´D S ÀïxÓ¨¬Lîé±êŸ~*§[ò– »,—šƒ5n“°{œÜÛ³ý|y¿)½m"§ak@p°WC‘y­F¹1ßz 6onùøÐP8ÿüÆá¬ ºí -áä{„À›ˆoªÀïxËH’`î\8uʱüÍ7½áÙpÜ@îë¹èßÓ7›’¢66N‡õ×Ài€GkµÌ×é¸!>ž¸BÔî6>rDîíY¹’ƒ„Lž,Çš<ÆŒé13´„ï>@o"|€šAøu-žy{̱ìž{\g{÷ J¾)!wY.¥K›Ý“ÝOîíùî÷`hÒÑ®VsS|< t:Æu…,ëuurˆì·Þ’gq5GLŒo`Á9l®@ è’  ‹ñãðÏ:–{.¼òJÛ¯i.7“ÿn>y¯ça8ap{œU ¿\Ÿ]{Ç8×_ÁŽââQ©ÚnPGqø°,zV­‚ÒÒæ:U§}ÝuÐYÓi „øöú§äæÊ Nís|ÅÄÈž_¯ú@µ»çƒB,µî£WDÈÍŸ_ …ñŽu± ·öêÅ|Žs:AvõÛØ`§È½õ–œŠ¢9ââ{{†õ®¡]áä{„À›ˆoªÀï´Ç?¥¾fÏv 7£TÂêÕзoë¯#™%Š>“c÷Tü\Ñì±Ç’åa®Ÿ.†z;÷pITóu:®‰íT3¶Ü¶ñ¡C½=ååî/ PÀ´iroϵ׶MYvs:ƒ„„U²b±Z°H–V¯ÛrŽÅzö¼³Û  ÛîÖm=¦¼¸œücùL¿r:½Ã{Ó;¼7ñ¡ñ(zHörwHàwÚãœûàƒ°}»cÙãÃôé­;ßT`"ÿ­|ò–çaÌsnÙ¬†MSåa®#ç8Ö%r{¯^üQ§£“„‡66à“Odïð¦×”øx˜7Oîí<ا6vufÍšåµkד[•Ë™Ê3äVž]Ûíë«õ˜,&'A"µ6¹\gùÇËmÛ¥†¤ð$› rµôÒöB¥èÃÏ‚E A—eÍxí5DzßÿÞÙÈ•Û+É]–KѺ"¬&÷)*Šcá‹«àË™Pn—BK­PpeL t:¦GG£ê„©œ8p@žÉµj•c~¦(r¢´;ï„k®‘³« ¼‚ÙjF_­w4öûyUyÔ™[Î'©·Ö“UžEVy–ÛcT º0[”–DRx¥ø¬÷$„ø¶ø9óç;–õé~(¹ÂZg¥ð£Br—åRµ§ªÙëï%sm»ÇÁÁÁüQ§c^¯^ôê Ã@GŽÀÆè?ø€^»w7lBÜ~»ÜÛÓEÒOtê­õdçgcÖ˜Ñ׺8új=V©ùœp‚f°&ÀÃPRÉ™Ê3œ©<ãö âCã„ј!\;ìZúFx0žÞÅ)¬-¤>¡ÞßftB üާ>@ÕÕr’Ój»\¡²¯;÷‹âÏ‹9vÇ1êKݱðý¥²ð9i÷û¤Tr]\óu:¦FFvnoƒª*øé'øæظ²³X¸la….½Tîí¹êªÙÛSg®£ÂXAE]…Ëu¥±Òm]E\o0à0Ðð³Ï»J¡B¥T9­• ¥Û:O×®®Õà‡£P(P hqÝÚcÛvmI-'ÓO¢ ° ÌjSµÛ¶ð ‰‚š j Øßø°pÿÆû™Üo2·Œ¼…Ù)³‰작˗gñ/‹yoï{ÅvΡ|o#Àïxê´`ܱaÏ’%raWœ^rš“;‰duí‘›$Ïäúær¨Ö6– e¾NÇÜ^½ˆêÌ3{l<[·ÊžáMpjaî¸CîFëß¿#¬ì$$ôÕzÛHVyúj}³âÆdñ<­K†{ç2¡šP’“lC3öëÞá½I O", ÌIœ(Çñ¾£¨0VØzÙ–qÔÐWbh!xg3HHlÎÞÌæìÍ,üf!3†Î`îȹ\>äòn1d¶'/l{u‡×Ùz'ƒH èt,] ìXvóÍð§?9+™%2ÿœIÞ›yÎu ØyžÜÛ³ó¼ÆZ•ŠÏ+„©Ð±— 0þþœìÜ<-2’‰‰\K`g VhµÂo¿É‚ç›oämk+g …„È))Dãõ˜­fr*rÈ®Èv)pÎTžÁ"¹®í âBâèÙß¶$hœD‹½à Õt2ñ+è”HHlÉÞÂê«YwxåuÍEŽO4}ðtny W%_EºãýjªLU,ßµœoÿ7ùÕùn›Òo Lz„郧÷¨\`=Z]pÁœþù$''óã?²yófrrr8;½Y ÎÁcɉNí¹ï>ç<_…Ÿrô¶£Xë…ÃÑaðè3P«déàÁÜ™˜H§  @îÝÙ¸Qîíi)˺=çœ# žéÓå¬ë~ÀXg®ã`áAöæïeOþöäïá`áAŸÆ°ið¡±8ý"û5nGô#DãÿÔ#‚îÑbdCÆVXÍ×™_·8,ά᳸eä-Lí?Õç‘« j xõ×Wyc×n…šW»š‡/|˜‰½'ÚÊ…ê!˜L&›Ø2dO=õ7Þx# PGÑœÐW_ÁÌ™²L\›69ÎØÎ~&›SŸrÊÔ¾e2<ûˆ äÓ”&ú˱¹ öìq\²²Z~X˜œ°AôôëçÑË·7ßZµ©š}ú}6¡³W¿—ÃE‡½>\¥@A/m¯fŽ?ž¤[ƒÈæ{:c.°²º2>Iÿ„ÕV³-g[‹Ñ¸û„÷áæ7sËÈ[HOõª-'ËNòÒ//±bß ·"¥†?Œü]øçÄžãTß“Pþ¦Ú‹«ÕJee%ÖÖ;¼†»8@YY0w®£ø‰—³84ˆ«ÉJÆèßwþ²~t¼½ÎŒàÓ””Ž \xú´³ØÉsž‰Ö"#GÊCZÓ§Ã…¶+F'±–J ¥6‘Ó x2K2½’fA©P’–H¿ˆ~.EN߈¾ªºfVùÎ ¬»S\\Üá>@-Å]ãîâ®qw‘UžÅ?`õÕ->êòøÓ•§yaÛ ¼°íF÷Í-#oáæ7£ÓêÚlÃ>ý>^ØökÓ׺bÖhY0v=ÿ¯ôïÝæ×êNtÿ³|ùr4 W_}µCùŠ+X´h‘ÃbØoÿ¾ýsC½Ñ(;,+k<^¥‚éÓ‘”$ï×—ÖsïÐ{ÄÏ V`VÃKƒ·î„qŸ}ʦѣmâÇ«öK‹î»OŽÀø÷¿Ãe—±(4TÎÂzÍ5ðä“,Ú°ÁAü8^­É~d$‹†‡wß•§¶ïßÏ¢  Ù¿ç¬øi«½ mÜ´þÁ<ÈW™_ñÔ–§¸nÍuDN$æÅ.]u)}ÿ/û˜Œ’ŒFñ³©Ép±ÏE}/bîȹL>5™ÿ^õ_~¸õŽÿå8ÿ°üƒÓ÷Ÿfë[Y}ÝjÔ[ÔÌ;ŸK^Êè!<÷ÔsmzaÖ¬Y,]º´ÓØÓ÷ßzë-ñão{šî¯xe^ô(Gþt„]wîbâñ‰$„&4°Éápö}¼¿{ÞKzóûU¿çÚ{®uìØÒëÍû¿yL_=1oŽáãCcIk"~6AlH,ON{’ìÿË&|{¸ƒøi¸Þ† X´hS§NeñâÅÔ»ˆ%ÖéÑC` |þùçÌ›7o¿ý–óÎ;ÏVÞðáhú¡øž;ï”ÓVÙóÌ3ðÈÛ†L®<€!ÓàpLµþù$§ä?C†ðG]ÛŸª°ZáØ1Ç^½{›Ï©Õj5Œ#{x_~¹ÉчSðO•Ÿrð×Ù“¿‡‚f—¹ oD_ÆêÆ2¦×ÆêÆ2V7–İNâc%t,’…NþÀê«ùß‘ÿQS_Óìñ!š®v sGÎåÒA—:%qµJV>?ö9Ïo}ž¹;Ý^§_D?¸àþ8æùʼn!°ĺuëøË_þÂ7ß|ã ~GSÿ”+œÅÏŒr' @ù–rÒ¯MwJk‘—(Ïô² dsJ ÚêïS_‡;Šýû¡¦ùW³ˆò\þ†eÄŸ9/KHì×ïç§S?‘–•ÆÏé?S¡i»XS*” Žl9czaŒn 1Áb¸§áä{:£PK¨*.t— ºŒš5¬?ºžÕVóý‰ï]WÕÖ×òáÁùðà‡$„&pcêÌ5—ñ#X}`5/n{‘c%Çܾ^j|*_ø07¦ÞˆZ)>‹ÍÑ£[gÉ’%¼þúëüðà îÅ8ö°÷OÙ¿î½×±~ÀX¹RN[¥_©'cA†S÷C©ðØÓ0¢oëRRHh­¿O]œJÂ^ì<(Áµ•ÐP5ÊQì îó<[éEé¤Jã§S?±9{3¥†ÒÆÊýÀøÖ]G­T“—ÂÝ›ØÝk4ÚmË'÷`„ïéŒ>@žª å#þÀFü‚š>>ô1«¬v”° ¦€Ww¼Ê«;^%X,çœs侓xdÒ#\1ä ŸÏ2ë.ôØ!°ŠŠ "##IMMu¸aõíÛ—•+Wb¬£©¨€ñãáøñƲ  ض ÆŽSÿ°š~@VyV«ÏS `ÆÐ<<éa.ìs¡WlC`=€ÐÐPÒÒÒœÊCBD 1ož£ø9÷×èáVßt”Â5Î?ð+æÁÇ·+ysèPnw7Í»¸^~–-sL!ï)ññ"§Aô Øòy^âTù)ÒN¥‘–•FÚ©4r«Z™ì,áá¶¡«†¡¬a±Ãœ| AÇ2,vO_ü4O]üÛr¶±úÀjÖ^ëØ‹k‡Z©æ¦Ô›xxÒäĥt°µÝ‡+€Ôj5S§Nõ·ä'Ž•+{±~½cù¼ypëU&ö]|ˆÊí•uõxáa8vE ?§¦rnX˜ó…KJ`ñâ¶ ŸÞ½{uÆŽÅ6ý¬ƒ8SyÆ&v~:õÙν_ÍÍ”~S˜6`#BF0%uŠè÷!ÂÈ÷tE OP `RßILê;‰×.¯3¿fõÕlÈØ€Ñb$DÂü±óyàüèÑ×ßævyÄ7Uàw^zi=¯¾ê£fÔ(X¼°†Ýb<åЫ"Bö÷‰žÉîáÉoêïã‰ðQ(ä^{¡3f ÄÅyã­yDAMCOfi¦G燆3¹ßd¦õŸÆ´Ó•0 ¥BŠ[¾|9SS§úÀjAÂÈ÷tu OPpͰk¸fØ5”וóýÉï¹xÀÅbâé±>@­Aøùžü|YoØçúŒˆ€m¯–Qz_:– Gÿœœ¾ðÈó0k|KFmïïSR"u-]ê^øètpñÅŽb'"Âï¬e k ù9çg›è9\tØ£óC5¡Lê;‰i¦1­ÿ4Æ%ŽÃY ] ÀÇÔÖÂîÝòÔö¦‰Î?¼9¢ù™`vÔæ{ÇÀ3O+yyìPn³÷÷iðIL”_lÁyJº©6Usºò499œ®8ÍéÊÓNëÚúZ®¤â‚>0­ÿ4.p1ç&‹FéÛ™e@Ð]Hàs$ 22à×_aÇy}ð ýä+=Ð ÿ=÷$!oœvºÆ×WÀÚGùvt*ãü}Z+|yDެè%ác´ÝŠš†uK™¢[C€*€ I¸xÀÅL0‰½'¶yvV{s ZFøùžîî$èXÄ7UàuJJ`çÎFÁ³c”7«ÖÈþ{„¿;ÔH 9¥…þîHv¦¤§Ñ4 ŸeË ªÊõ%uö— IDATºFáãA°A³ÕL^U^³â¦-ÓÍ[ƒZ©æÜÄsmCZö½`u°W®íI.0AÛ>@¾§'ù |@‚vQ_//lèÙÙ±2=óÝ%†Ûy)h/Š{qŒðÌ£0ö¦Þ¬4uYYcOsÂçá‡á®»œ„„DAu[q“S‘ƒ¾Zï6™ ·Ñh;Ìæ´|Qß‹|lPˆß3kÖ,›ÐíILLâGà5„xDNŽãPÖž=r0eOQ(`ȸbh5—m;@P™É¡¾4žx^Éß®Jæ{¬yáÓ«—,|î¾ÛAøX%+›³7³æÐ>=ò)ŵŮÏ÷2ª@úDô¡Ox·ëȠȱE Î$pKu5ìÚå(xÚ:1 O”™Ë†Õrn\ ƒÔ5hËj¨;V´¡žRJ ¢qLÿä@X¶$÷Æ÷eìk¯µ,|zH>Áòp‘„Ķœm¬I_ÃÚôµíNøÙµRMbXb³â&>4Þ«¯Ù^„ï>@¾Gø ¼‰ø¦ö`JKåièyyòÒ°Ÿ/'>OO—“ {Bõ V×rAR £"kI°V¢7 .ª‡íÇÙg´ÙÊV®BîÖÞ16¿¨å»´¯ˆû²{á“ÐØãsVøìÈÝÁšCkø$ý£$7 T(IMh¶÷F§ÕÙâët„ï>@¾Gø ¼‰@Ý{aãJà4l·'ßgõô£†Ô’SÅjâ+ „T˜Á dŸ]ZAƒøùßµ ¹*‡/¯ú ª 7™ËäŸ{îà`öäïaÍ6Yô´&‡Nx`8ƒ¢¹8IáIÝrj¹?¾Gøùá$ð&Bu!ÊÊ\÷ÖØ—åç·Í'ÇQ˜èO-ýTÕô©d º†þÆ:Âjí…KÎ.mÀ¬†Ó}à›V®9¹„›oÿÊõññ6ás°úk¶?Úô5/=îúx;"#¸fØ5ÌIÃ%/é–G ž!P'Àl†S§ ;Û½Àñ¶°Q#‰‰(LDQO”ºŽ¨à:¢4F¢”F¬Fú× ¯;;fÜŒFµ†¡“Õ_^N÷k’™È€B’6ÿÄ?7üDê©SÎ'ÆÅÁC‘1ç>:ù9kÞÏ‘â#-¾ž6@ËUÉW1'e— ¾¬Çg7>@¾Gøùá$ð&â›ÚAH’,d22—ÌL8yRžNÞ^‚±‰‰hê‰Ò‰ ª%:ÀH¤ÚH&¢­f"ëÍDÕ™ «k’ÅL»Ží2j9]Ev?Yè”öM¯:bÔ =™É¸½{¹ù‡ƒ ÌÏGs6âr µé…ââ(ùó¼{~«O¬âÀ»kñµC4!\9äJæ¤ÎáŠ!Wx-†Nw@øùáä{„À›ˆ\`ÍЖ\`%%²¨i*tއšÏ^_ìT…‰(µ‰¨Z¢DiêˆR˜ˆ’ê‰2›‰4š‰®µXßqÿÊzMcNnuý%‚b«ée9ÍÐÌ£$ïÚEòÁƒD¹sbvƒ5&šŸçLä±aùl-ÝÛâñAê ¦žÎœ”9ÌLžI¨&´ïH "˜ YjjENS±SZÚºk„QO/EñÚjBj‰¨%Ji"Êb&Êd&Ê`!²ÆŠªa–¨ôÕ;r½Ð)¨DêcAYN¢éɇ1uûvú§CÙNmˆ å½ßÅðpòiª¾†fÚ1@Àïýž9)s¸*ù*ÂÃÛõÚ@F’$¬V«m±X,­ÚW*•„……†RÙµfH z.Bµ@FF2/¿ì(vr[˜a­B"VQGBX5 ÚZâjè¥0o6’`¨'¡ÒJI yØÉ COž") "B8XUQ êbXb”("%4¡&‚Jè[uŒQ÷3{ëVÂ~òN<«FMQïhNèÙmäU›Rj¨ pßE¦VªùÝ€ß1'u×»VôîìdµZ1˜L&—‹B¡ €ÀÀ@mÛh4Þsˆ÷Ĩ¾¾žêêjªªªlkûí–ꪫ«1›Í­(­ÝoB¡@«ÕNDDááán·›« E¡P8]¿½>@f³ÙöY1ÛM×F£‹Å1*¼«öiZæ«cÄ©ÅbñéRSSƒÉ䘶»"P |ôÑM|ô‘cYˆÂLBxµ,p‚jIPˆ·I0Ö“Pe!¦ÒŠÒŠÜcÓ½6fµ,fÊ¢ < ‘æ+R¨ Uˆ‘@M5¡Ê ÂÍ…ÄTçÒ«°€yyÄŸ>CØž"ÙQÉ›ö¨ÉO çX¼Šß¢jù-¢†Ãqp<ÚŒEi—Okà|¾J¡bJÿ)ÌI™Ãuç\GlH¬WíëIxÓH’$ 555TWWSSSã°][[ë @š'®Ooú#å ͉#ûukÊNœ8ARRf³¹E!clO ŠNŒ$I¶÷™ÛÒ“b3(•J› ²H†ªª*âãã[/®ê¬ž6ë¡„‡÷Œ^uáÔ ‹-âØ§×ÓK2o2‘Pc&¡ÜJhmÇ5Y],hJ£ÁnŨ­ÇT‹BSMåÕV›KTm.qÕzâÊˉ//'²ºEýkë՜օr(NbgD5‡b­¤ÇÃÉ(°:?Ä5‹“úNbNêf ŸEBh‚oŒîA”––RTTd(®DKk·qÛº/áááT¸‹ÅÖ=@-pס6¸iÚ(Œ‡²X u¡ÕHåHåJ¥„Öa* ¦6—¾Œ«¨ æpE»ýlÚ‹1HÍÉ^ì®g_´‰ôx8Y‘f¬Š¶Y(˜Ð{sRæ0;e6IaI^´º{bµZ),,$??¿ÙE¯×wÛÞ÷Q((•JÛ¢R©ÜîÛo[,[ÏÇ‚®‚@>@RÈ=6%±jÃë0U R—lÍ'Òtš^Õ'I.Éâw¹Efyaþ»°*Ô«©VQ¬¤,ÐÊ‘0#éq‡ãd¡“aFR˜ÛüªE "96™ä˜dÛ:ÚͰþüønº.&“ ½^ߢ°),,l×ÐOwB©Tà0e¿H’ävh¤Þñ'Ú€Z­&,, ­VÛªuÓ2­V‹F£iQ¤4'Z\í»ò»ñ„†!°ŠŠ *++©¬¬lÓv§Sf[‰Z­öh¨³©?—«öiZæ«cÄ©J¥òéRUUÅ¿þõ¯æ²› P0@qœDe¤Sh5hJ ¤ˆú3ÄÖž¢wyŠò )é¸'ïê(‚Š ¨ô|»*PêÏ.í£—¶—ƒÀiXˆ€J¡r:~ùòå »Û»Èl6STTDAAÃâêÆÚçCOÏiÍ1f³Ù©§´´´K²ˆPKÑÆÓè*3èWr‚ˆÜh»Ÿ%!PÜ6S–þ^«ƒ3Ä¥ÐñtZzksM&………N¢ÆÕRRRÒ%„DGM||<ÁÁÁ.K[¶»Ë‰B¡°ý€ :*•Ѝ¨(¢¢¢ümŠ #P ܳk^›Ï•Pya¯…ü0÷Û¦¶?Äú  z‡÷v8ɱÉôè‹ït——––¶JØ”••yéu} 111$&&¢ÓéÐét¶mû2N'~ÜÀBµ« Ceá’¯=+d\lëµPï#a¨ $XL:ˆ`µ¼RÙÊÚ[ƀȄhBÜÚ`2™¨¨¨ ¼¼Üiíª¬i]ee¥˜–Ú¥RIlll«„MkbØX­&Ìæ òósˆ,HRãâ›}+ÐS¦qiÜo®N:{¾ëº¦õ®¯cmRgOs~î¶÷ÝS^n@«Õ RIH’ùìbq³6Ÿm+×u ÛÇ8'Û¢k÷ö¢T*‰‰‰!!!øø8â㣉‹‹ ,,Ø…saÓ³›H-žãÚ©±åý°° "ˆ×JLL … «µ«Õpv©Ãj=År«µŽêjÇŽlÇX,ÇËkù_|"}’oټƌBÅ/””À±cpÁþ¶¤{3rdÏø Ô‹†fPZ”„жZË€ÚPB”!ý $)µUZR£²ªP˜(­J¤z K½…ºº:ŒF#uuu”KÉ7æ;”FêëëDˆðaqD£QFll(11AÄÄ­!:ZITDFZ‰Œ4i$,Ì€$Uc±A’ùÛôVQR"/¾@ˆß3eŠ¿-èþÄÄñ#ðBµÄëòÊŠ•ª³ï T* U¦$2R"*ÊJT”Dd$DEAt4g…¼­ÕZP(Êò¯í§ÙÍ@ è"$h3j5hµòÚ¸íjßUYp°„B!QZj¥©}- PhP©‚)+ 66Ù§ qñÍ~ƒÏBûýJ¼ã×bOs¡ Üm;î»;§¬¬–ððÔj …úl›¨›lËk¹\×¹>ÎUüúžûDµ¶Î3­F?0«—¶e2ûm“ÉdójÞ‡ÉÕgÄÕ箥ãÞ£ÕΫ]™ó¾ýñŽ>qí?_FþL7»ÛÎíëËß#Mê¯WWgD¯ßBO@ NŒB*•¼(•Ûö‹§åǫÕâ^Ìhµà­ÉE[·úvˆF© D¥Ò¢RiQ*ƒhÎÁ\ûó´tŽó¾«ë8ï+•(•Á(•Ag×ò¶Jå\¦T·²¼±¾ÁqrùòåÌœé\`×ìܹŽiÓ¦ãoSº-yyy;¶‹±cŘ®¯Ðëõ<8Úßft"X3,Z´ˆ÷ß[™«:WëÖÔ©ÕŽ"¤AY»$Je€M¬¨TavÛ®öËÔjçsŸ–@Ðz½žÑ£G£×ëýmŠÏ¿ -ðÞ{þ¶ ­47µeÎÝÀrïDÝè°¯P6[ßÜ~ÓsÄ‹BÑòÔn@ Ú‹@-“s“'OC¡ÐØÁkÖJ¥Æe¹ûz×ǸÏš*öûàj(¦³£×ëéÕK8ù¹{ùÛŒnMII Nù£ÞÃd2Q]]M´pxÿMÍËËcÛ¶mDEEqá…ìPòäÅÜzë"ÿ×CX¹r%=ô¿ÍèÖˆ6ö=_|ñW\q þ6¥Û’ŸŸÏ¶mÛ¸ùæ›ýmŠ УЖ-[˜={6—\r §OŸÆ`0ðã?¶;‘ŸÀ3jkkýmB·G´±ï1"v—iH^+xƒî‘Ѱ<úè£,Z´ˆ>ø€Í›7È|à[JKKÛõÅöÄa­5Ç6wŒ»:WåMË, EEE-¾¾/0›Í”´#Ò 'ÿ£ÚÚZªªšå­6.//Çh4¶úÚ¾¦=¯íéÿ¨¥×ªªªr+þÜýª««©®®v(sõ¹íªmìéùþ¼_´æ{ä+\}<Á“6nͽÅWmÜÒµ»3=V ¶nÝÊUgç_+ ®¼òJ¾ûî;¿ØóÅ_——׿ó—/_îÕc›;Æ]«ò¦eUUUN"³=7O(..fíÚµm>ß“ÿÑضm[³Çx«7nÜÈ©S§š=®£ÚØÕk{‚§ÿ£–^kÛ¶m8pÀe»ÿÑ®]»Øµk—C™«ÏmÓ×®©©Ál6·ÆìvÓž6öô|Þ/šþêëë©©©iÑoàêsà ž´qkî-¾jã–®Ýé±Óà³²²0`F£‘€€Þ{ï=Þ|óM~ýõW@žÿñÇ3lØ0ŸÛ£×뉌Œ$((¨MçgeeÑ¿¯ÛÜ1îê\•7-3›Íèõzz÷îm+Û·o£Gû>î„Éd¢¸¸˜ÄÄÄ6ïÉÿ¨²²³Ùܬ³¦·Ú¸°°­VKHHˆÛã:ªÝÙØZ<ýµôZ¥¥¥¨Õj—ÃÚîþGåår¤ñÈÈH[™«ÏmÓ×>zô(}úô!44´U¶·‡ö´±§çûó~ÑôTYY‰^¯gèС­²½=¸úx‚'mÜš{‹¯Ú¸i™ÑhdïÞ½=¢W¨ÇúÕÕÕ R5¦kW©TÝ7Þxc‡ýh@ÐhëƒxW£Ç  †h­UUU6…_VVF||¼í˜aÆuHï@ ‚Ž¥Ç  ØØXúöí˯¿þÊôéÓؽ{7cÇŽmÕù<ñÄäåå1|øp-ZdJx—ÒÒR/^̳Ï>ëoSº%_}õ_~ù¥mÿ±Çsêx‡-[¶ðÁ V«ùÏþãosº?ýôŸ|ò‰m?99™ûï¿ßuO~øá>ÿüs¸çž{èÓ§¿Mj3=Ö`éÒ¥,]º”G}”¼¼</^Ì®]»0`@‹çž9s†¢¢"FŒÁ£>ÊàÁƒY°`AXݳ0Üzë­ËG}äo³ÚL°páBž~úi¶lÙBii)çws*†×_sÏ=—1cÆðôÓO#I½{÷f̘1üöÛodeeÙ~<î‘$‰n¸Áål‡eË–ÙÚø™gž±ÅSY¸p!=ôˆÍä¿üò >ø SyII ·Ür ÉÉÉüþ÷¿gÇŽ¶º3gΕ•%ÄO+ÉÌÌä¶Ûns*¯­­åî»ïæœsÎaÚ´ilܸ€W_}•n¸={ö››ÛÑævYæÍ›GFF†Sù»ï¾Ëĉ9r$=öV«•ððpú÷ïOÿþýyçwxì±Çü`q×ãÙgŸeÆ Nå›6mâw¿ûÆ ãŽ;î ²²’ÀÀ@JJJøñÇ)++kUgAg¦G €n¸ÔÔT~ùå6n܈Á`p¨_·n/¼ðË—/gÕªU¬Y³†W_}«ÕÊgŸ}†F£qp¦8óòË/sá…²víZ§6^³f ‹/æÍ7ßdåÊ•|øá‡,[¶Œ§žzŠ™3g2nÜ8?Yݵ8sæ ,àšk®aÿþýNõ³fÍ"""‚o¿ý–o¼‘K/½”²²2¦M›FDDß}÷]t‘4× UUUÜ{ï½üîw¿cçÎNõwÝu¥¥¥|õÕWÜwß}Ìž=›ÌÌL²³³1›Í„……që­·ŠÞÌxýõ×™:u*ï¿ÿ¾S‡7òüƒW^y…5kÖðÝwß9 oÚ´‰ÄÄDØÑfw)6lØÀõ×_Ïã?îôPš››ËÌ™3¹çž{øúë¯1™LÜzë­1uêT6lØÀ‚ ¸üòËýd½—ÒîÝ»¥´´4I­VKÇŽs¨»òÊ+¥%K–ØöW­Z%=Úá˜/¿üRúë_ÿÚ!¶vUvíÚ%¥¥¥IJ¥R:~ü¸CÝôéÓ¥W^yŶÿþûïKãÆ“æÏŸ/Í™3Gš3gŽ#=üðÃmv—¢²²RJKK“|ðAé’K.q¨;uꔤÑh¤ªª*[ÙùçŸ/½ýöÛÇÍš5K:tèP‡ØÛ1RZZšô /HÆ s¨«®®–‚‚‚>ß³gÏ–þõ¯I“'O–jkk%I’¤¥K—Jï¾ûn‡ÚÝÕØ·oŸ”––&…‡‡K»wïv¨›3gŽôÄOØöׯ_/ mÛÏÉÉA§Ó¹Ê´ Wmœ˜˜(ÚØ‹èt:òóó±X,¶²Ó§O“””äG«º:ŽššÊÊÊle999¢½Œ«ûEdd¤è¹ô":Ž3gÎØö>×Ý->˜@-0{ölV¬XÕj`åʕ̚5ËÏVu/DûžñãÇmK4šÍ–-[¸îºëülY÷!))‰‰'ÚbÏ”––²aÃñYö2³gÏfåÊ•Ô××â~á f͚ņ (((`õêÕŒ1¢ûͬó·vgàꫯ–úõë'RRR’4jÔ([]MM4sæL)..NêÝ»·4yòd©¸¸ØÖvMþ¿½{ ‰ªÛÃþŒ&^R qWæd]HÊA³"ßL¼¤£i„DRD,LE  ³QQ*¨”jXTD‘”—$#22˜•’dWµu>ˆû¼sì=Ç<ïiŽÎóû4³×¬½ÿ{yX{­5:ßñÊ•+å¶¡¡!áïï/ÇÞÞÞ¢¿¿_ÕNOuuuB’$¡R©„R©’$‰¬¬,¹½¢¢BØÛÛ µZ-¬¬¬ÄÉ“'õXíôôöí[!I’°··&&&B’$'·¿xñBÌ›7O¸¸¸qèÐ!=V;}EFF I’„‘‘‘ptt”Wy !Äׯ_…V«*•J8;; OOOÑÛÛ«Çj§§ôôt!I’P*•B¥R I’DCCƒÜž’’",--…Z­ÎÎÎVãͽ4&>#"""ƒÃDDDD‡ˆˆˆˆ  "ú[¼zõJÞÊ@_ºººðþý{½Ö@DÓÑ ÔÕÕ???ìß¿_çøéÓ§Q\\ü·_oddóçÏ×Ùðwêì섇‡Ö¯_Ï—‰hR€ˆf ¡¡!}ú¤Ó¿±±@dd$nß¾­ÓöðáCÄÄÄ <<999ß‚¬¶¶yyyèììĉ'PTTôÓz‘””„ÐÐP$$$ÈuVUU!''ˆÇóçÏ'ô}ùò%Ο?7oÞàÌ™3ònÍÝÝ݈EXX222ðíÛ7@MM rrräþׯ_ǽ{÷ä÷§NB{{; ¼¼{öìATTRSSåÝs‰èÿÑ ¶{÷n  ´´tB[CC®\¹¢sìøñã””” 44_¾|Á–-[püøq¸¹¹¡ºº~~~¸|ù2²²²túÇÆÆB­VÃÚÚ!!!¨­­ܼy»ví‚»»;ÂÂÂpîÜ9dff b  B{{»|ý?F£A}}="""ÐÜÜ /// ÃÆÆfff°··‡››fÏž=¡ss3’““áç燆† b``žžžøøñ#ÂÂÂpçÎù§A åþ‰‰‰HMM|ûö °±±AEEBBB Ñh°}ûvôõõáñãÇ“þû‘þð×à‰f°Y³fáèÑ£HJJB``à/÷?räöíÛ`,Dtww#;;`ee…ÌÌL$''ËŸ¿uëlmmïÞ½Cnn.<<<’’‚´´4„‡‡”J%qèÐ!€$IxðàLLL~ZGYYzzzðìÙ3aÛ¶m°³³Ãµk× ÕjáììŒ5kÖ ::ú/ïÅÎÎ555P*•€cÇŽaîܹòýlذvvvxúô)ÜÝÝñõëW´´´@•J…öövôõõ¡µµK–, š››!I‚‚‚î’ŽŒIDAT`aa­[·þòwLDúÁ ¢N«ÕB©T"//ï—û˯-,,`dôÏ–––V\) ùµ‡‡zzzŒ=‚JHH€››ÜÜÜsssù³*•ê/ô´´ÀËËK¾¾B¡€F£AKKˤïÅÊÊJ?ãçôööÖ©aéÒ¥hii±±1|||PYY‰ââbhµZ£´´UUUðõõÂÎÎŽŽŽðõõE~~>FGG']éG€ˆf8…BôôtÄÄÄÀÇÇG>>kÖ,|ÿþý—ÎóïÞÿ«¦¦&ØÛÛ¬­­‘ŸŸuëÖM¾ð?±¶¶ž0y»££7nœÒùÆÏ9>FGGñúõkX[[ª¬¬ÄóçÏqãÆ ´µµ!%%Ø»w/ÀÖÖwïÞESS=z„¤¤$aÇŽS®‹ˆ~Ž€Í›7ÃÅÅEg.——Ñßß?~ ¨¨ÃÃÃÿÕuÆ'E×××ãÖ­[ --Mnÿüù3JJJ&}Þ7¢©© åååÆ&T744`Ó¦MS®õ?þ@yy9^¼x! `llŒÕ«WË×,++ƒ™™°víZ´¶¶¢ººpÿþ}ttt`Ñ¢EˆŽŽÆ²eË~)T‘þ0ˆŒŒ U[ˆˆˆÀ‚ 0gÎÜ»wOç‘×TlÚ´ ®®®X¾|9¢££åyG™™™˜3gœœœ V«akk«³ªê?Y¸p!²³³'''øûûãâÅ‹P«ÕS®uÆ 8xð V­Z'''ÄÇÇ£°°PZ¼x1LMM `lÄ+88jµZ~|×ÛÛ‹+VÀÕÕ‹/†¹¹9vîÜ9嚈è÷Qˆñµ¨DDDD‚#@DDDdp€ˆˆˆÈà0‘Áa""""ƒÃDDDD‡ˆˆˆˆ  """28 @DDDdp€ˆˆˆÈà0‘Áa""""ƒÃDDDD‡ˆˆˆˆ Î?UæÂ´DIEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/compressed-select-cache-shuffle.svg000066400000000000000000001035071231437614300300430ustar00rootroot00000000000000 Selecting with small (16 bytes) record size (file in cache) 10 3 10 4 10 5 10 6 10 7 10 8 Number of rows 0 2 4 6 8 10 12 14 16 MRows/s No compression zlib lvl1 zlib lvl1 (Shuffle) lzo lvl1 lzo lvl1 (Shuffle) bzip2 lvl1 bzip2 lvl1 (Shuffle) PyTables-v.3.1.1/doc/source/usersguide/images/compressed-select-cache-zlib.png000066400000000000000000001251561231437614300273400ustar00rootroot00000000000000‰PNG  IHDR@°AàÚ²sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwXS×ðo ì2EA– p  ¨µîjQê¬G«¨UœUk­­Öݺwkq ¢Õ–¨‚'C– {¯óû#?®\’@ŠÂù<Ï=wÜÜ„7÷¼ç!„€¢(Š¢(ª‘“u(Š¢(Š¢š €(Š¢(ŠjqhDQEQT‹C Š¢(Š¢ZQEQÕâЈ¢(Š¢¨‡@EQEµ84¢(Š¢(ªÅ¡EQEQ- €(Š¢(ŠjqhDQEQT‹C Š¢(Š¢ZQEQÕâЈ¢(Š¢¨‡@EQEµ84¢(Š¢(ªÅ¡EQEQ- €(Š¢(ŠjqhDQEQT‹C Š¢(Š¢ZQEQÕâЈ¢(Š¢¨‡@EQEµ84¢(Š¢(ªÅ¡P3Äçóñþý{YW㣕››‹¤¤$YWC"EEExûö-³œ››‹wïÞI´oii)Þ¾}‹ÜÜÜFªÝ™™™HMMmôóH‹Çã!!!AÖÕh0„¼}ûÅÅŲ®J­jºÿž>}Šääd¡òììl¤¤¤4Eõd*-- õ:FjjªÈkHIŽ@™gÏžaúôéèÞ½;ºté‚¡C‡bݺuxóæÄÇÀìÙ³¤>………8uêrrrXå+W®ÄW_}Õ çhHÞÞÞØºu+³|éÒ%VGE¯^½š¸fusïÞ=˜››£¢¢ðË/¿ÀÕÕU¢}çÌ™ƒ¡C‡¢´´Th]HHæÏŸ/vßôôt|ûí·øüóÏaee…}ûöÕx® 6ÀËËK¢z‰ÐÐÐz£ºÄÄDtî܇nÐãÊJ^^ÌÍÍñßÿÕi‡~ýú!$$¤k&Ìß߃fkgΜA‡àää„àâÅ‹èׯŠŠŠ{öìÁСCëuÞ3gΠ_¿~(//¯×q“V®\Y¯cDEE¡}ûö¸yófÕªå¡ÐGäÞ½{ppp@rr2&OžŒyóæÁÊÊ ?ýôöìÙ#“:ñù||ñÅHLLd•›˜˜ÀÔÔT&uª‰…… ™åY³fáÞ½{2¬‘l\½zÇŽÃ¥K— ««Ë”ÿ÷ßؾ};¦OŸŽ°°0‘ûFFF¢[·nˆˆˆÀ¨Q£°råJ˜››7zOœ8uëÖ5è1°ÿ~Ì™3Gèn‰¸\.lll ¥¥Õ¨ç ÁÁƒqéÒ%´nÝš)_½z5¾úê+äææâÁƒÐÑÑ ääîO‘®®.lllÀáp옣Ï?ÿkÖ¬Á¤I“ŸŸ/ëê|’¸²®õÁ–-[бcG³>¼ß}÷âââXÛ#::<]ºt‰‰IÇ®¨¨@LL Þ¾} KKKØØØm“ššŠ˜˜hjj¢sçÎPRRbš[’““¡¦¦EEEÃËË eee¬}•”” ®®Ž@YY]»v:GQQ"""ŸŸgggäçç£U«VPUUY襤$hjjBSS“Ù?33FFFÌ6 hÕªÔÔÔ0gÎ(++3u*//Çcž™™™±ŽÿòåKÄÇÇÃÍÍ \ní‡ÌÌL<~ü°±±a‚­‚‚¼ÿ¦¦¦xýú5âââààà@LL x<úôéÃú²/--Ell,’““ÁårakkËzmuõí·ßbáÂ…èС«<22QQQÐÑѹ_EE&OžŒQ£Fa÷îÝRŸ·¢¢=BZZœœœ˜àëýû÷¨¨¨`®G%>Ÿ²²2¨©©!;;›Õä׺uk¨««<‰ŒŠŠBvv6ìííYA. x²ñäÉp8ØÙÙAOOY7~üxüüóÏØ¼ys¯©¼¼‰‰‰066†¢¢"222Àáp˜k•——‡¨¨( [·n¬?앯=.. 077ºö/_¾Dll,:tè+++ÖºøøxèééAUU¹¹¹(**b^C~~>ÂÃÃQVV†Ž;Öxý+•””àÑ£Gàóù055e>ïÚÚÚ`®_ZZ …ö722‚’’Á=úèÑ#¤¦¦¢cÇŽBŸ!QV¬XùóçÃÚÚš¹6aaaxöì\]]‘˜˜===8;;£}ûöPTT¬ñxqqqˆ‹‹ƒ‰‰ ìííkܶW¯^°¶¶f>g™™™(..†¡¡¡ØÏ¡8„ÄÅÅ!>>¦¦¦Ìë€7oÞ >>ÅÅÅh×®lmm…ö/**ÂÓ§O‘ ;;;¡û¶´´QQQ——G·nÝ„ö/++Ó'O””;;;´oßžµ~ñâÅ8räöîÝ‹¯¿þºÖ×CUC¨†§§'éÑ£G­ÛýóÏ?ÄÜÜœèêê+++ÂårÉÎ;™õ3fÌ cÇŽe–ãããI¯^½ˆŠŠ éÚµ+áp8dæÌ™ÌúŠŠ ²hÑ""''Gôõõ‰ªª*ÑÕÕ%ÏŸ?'†††ÑÐÐ ZZZ¤wïÞ„BfÏžMFŒÁ£OŸ>ÄÃÃXZZ---Âår‰³³3ÉËËc¶ùï¿ÿˆ™™‘——'–––DCCƒÈÉÉ‘‹/Š}­nnndîܹÌòæÍ›‰††),,$„’––FÈÈHB!½{÷&+W®$„2xð`Âápˆªª*ÑÒÒ"ZZZ¤´´”ìÚµ‹´jÕŠŒ;–(** bhhHBBBj¼î ªªª¤}ûö¤M›6„Ãá;vB9uêQPP _~ù%QTT$êêêDQQ‘;vŒ 0€())UUUbffF>|ÈsÖ¬YDAAXZZ333ÂårÉwß}Ǭ¿qã@ÊËË !„‘öíÛ×Xχ‡CÞ¼y#v›+VGGG¡òsçÎ%%%’““Sã9ª[¼x1±°° ݺu#ªªªDNNŽèêê’ððpB!Û·o':::¤  €µŸ $¿þú+QRR"\.—y¯Ž9B!äöíÛÄØØ˜èëë“öíÛeeef!„lÛ¶(++KKKbhhHäååYë !äôéÓÌû/Nbb"@öîÝKºwïNOOOB!—/_&­Zµ"&&&¤mÛ¶D]]\ºt‰Ù·ò3€´k׎ÈÉÉwwwB!EEEdâĉiß¾=@¼¼¼H~~>³¿‚‚Ù°aéß¿?áp8¤[·n„BîܹC ™{DSS“ ýõ—Ø×ñèÑ#bjjJZµjE¬­­‰¼¼<4h!„²²2€¹×ÇŒÃ\o---¢ªªÊ:~tt4±±±!ZZZÄÎÎŽÈÉÉ‘ 6ˆ=7!„<~ü˜ /_¾dʲ²²ˆºº:@455‰––9qâ9{ö,QWWg¶ûî»ïˆƒƒ³œ™™IFŒA¸\.qpp òòòdðàÁ¤¤¤DìùýõWÒªU+fyéÒ¥ÄÒÒ’xxx¢¢¢BÌÌÌȃj|ïÞ½#®®®iÛ¶-‘““#½zõ"„>'ˆ‰‰ ±±±!\.— 2„U¯;w À|_¬ZµŠBÈÀ‰««+±±±!ššš„Ëå’nݺ‘ŒŒ fÿgÏž‘.]ºuuuÒ©S'ÂápÈòåË…ê¹yófÒ±cÇ_ % €>"¿ÿþ;@È÷ßO®_¿Îú@BHnn.111!kÖ¬a>lçÎ#ŠŠŠ$>>ž"yxxñãÇ“ììlB!qqq„Ëå’àà`B!ûöí#ÊÊÊäÂ… ¤¬¬Œ”••‘ƒ’çÏŸ3ž}úôôtBˆàÙ½{·Øó‹ €455É/¿üBJJJHqq1quu%‹-{ B2déÞ½;óƒ…Ïç3Áß»wïHBB³í›7oˆºº:9}ú4!„ŒŒ ¢§§G¦M›ÆÜßOž}ûö‡Ã\\\pýúu@DD^¾|‰%K–0¹C­[·®õ1tÿþýñøñcdgg#)) X´hΜ9 ƒ»»»Ô9úúú˜7o”••!''‡Ñ£G3uENN***xôèÓ{ÃÖÖd¶QTTÄ7ß|ÃsÀ€022bšåпܹs‡ÙÇÎÎÆîÝ» B^¿~-Õë©*>>^¨ùER (//Ç!Cpûömܽ{ÚÚÚ>>µîçïשּׁ,9rðçŸâÍ›7ðôôD||ŸÙŸÃáÝ7šššB?ö¸\.ôôô$º·)6}¤8,--1þ|¼~ý—/_ ø¨¦¦†¨¨(‰ŽSù‹ñ·ß~C»víÄnóâÅ‹Sý¹´tttPTT„”””Z¶«âr¹puuŽ{÷†U«VÁÖÖíڵÕ+W‰Ÿþ¹Që^i„ =z4"##q÷î]lݺøþûï%>FÕ?rÙÙÙð÷÷ÇÁƒ1yòd‚?;vì¨W=544êüehii‰³gϲÊ ¬¬,õ¸%111Àü1ìØ±#ÜÝݱgϘ˜˜ [·nèÑ£kŸê¾>ŒŒŒj¼ß§OŸooo„‡‡#44ÈÏÏg~­WâñxÐÖÖ–ê5TÖÁÖÖVl¯¹èèhÀ‹/DÞÛ­[·fþ VŠ‹‹ƒŽŽüˆ¢ªªŠŒŒ 3 É’èÔ©þúë/<}úÿüó‚‚‚0tèPæý¨.""sæÌaº¨WÒ×ׇœœ"##%ê P©>÷_U•Ià{÷îE¿~ýê}}ú4>|ˆ?ÿüIII@ëÖ­™.´}úôÁ¤I“0tèPÌ™3III8uê¦L™Âä°ˆóàÁLŸ>fff8þdûÖ­[àp8R7ÁÀ Aƒ0qâD¸¹¹aòäÉ044ÄãÇqåÊ<}úfffؾ};¦L™‚?ÿücÆŒÁãÇñðáC¼xñëׯGÏž=acc///œ?iiiøå—_jÿüs¸¹¹aÀ€àñx E¯^½jœ³>¸\.vî܉‰'",, žžžxöìþúë/$$$`Ú´iؼy3^¼x‡ƒÖRGGGøúúÂËË C† AÏž=qíÚ5XZZ29V5ÑÕÕŶmÛ0gÎüý÷ßèÚµ+^¿~k×®aݺu¬¦è?þøíÚµƒ……Ec\ŠfM~µ¨äJ&f̘N:ANNéééPWWǨQ£pèÐ!Ö£uWWW 4ˆIjÖÓÓ 0pà@ÈË˃ÃáÀÚÚš3ÄÖÖ'Nd UTTàíí/¿üJJJÐÔÔÄŒ3 ¢¢‚wïÞAQQþþþpssƒœœ]]]<~üL³…••:uê@дãààÀúr8ØÚÚ29 ={ö„œœœ0{öl`Æ X´hQ9úúúàr¹øòË/™/ÔÊ1b<==Yù.•õ¨Ìwptt„¢¢¢ ­­€ÃáÀÄÄDh4èÊDDQ´µµáè舴´4ÄÆÆBYY›6mbþ r8èëë³’o9ŒY‡ÃA»v혆‘#GBQQ111°··Çž={ ¡¡~ýúASS“‡¦_¿~LóY›6m„šŽª266ÆáÇ¡¨¨(ô?..|>ptt—Ë…††ëx#GŽ„¡¡!"##QQQ… bÅŠbÏWÉÑÑšššˆŠŠBÇŽñÓO?ÁÙÙ™µœœŽ;†‰'bÀ€¬ujjj;v,Áçóáêê CCC|öÙgpssûwïðâÅ cÉ’%pqq‘‘:wîŒÔÔTÄÅÅACCÛ·oƒjÅŠ°²²‚··w¯AII îîîÐÐÐ`•5 ŽŽŽHLLÄ«W¯`jjŠU«VÁÞÞ]ºtÁðáÃQZZŠ„„XZZbíÚµÐ×ׇ®®.|||ŸŸØØX¸¸¸àСCB°ÜÜÜXã‚1ŒTTT’’‚Î;cß¾}PWWG¿~ýÄfèî¼<¼|ùùùùðööÆüùó!''ÇÜCýúõƒŽŽ²²²`oo ¨««3ÿ:wî CCCôèÑ#FŒ`î{øùùaĈb›ÅŒqôèQÈËË£OŸ>¬uêêêpwwgû£¯¯Ï< «þù011ÁôéÓQPP€ØØXB0lذsïªv¨daa!4ÎN›6mj ê;vìˆQ£F¡¬¬ŒÛiݺu044D¯^½`ee…G¡uëÖøá‡бcGtïÞéä1lØ0ØÛÛ#//iiipssÃÂ… ™«®]» %‹WýÞîÖ­ÆŽË$Ûkiiaúôéðôô„‚‚³Ïܹs1dÈ¡¿”dÒ÷Œj‘’’’˜îÜ„²gÏ¢§§Ç+ˆj?ÿü3ÑÖÖfu9þTv}ONNn²sÞ¿ŸÈÉɱÆ_¢×álj¦¦f“¾Ï-ÑñãÇ‰ŠŠ ÓÕž’‡Êmø|>äääÄŽ’KÕÏÔ©SqéÒ%XXX 77iii8vìFŒ!ëª5;4h 88˜õ‹Q–† MMMœ8q¢IΗ’’L˜0ß}÷]“œ“äé 2ÅÅŸvíZ­#=SÒ‹‰‰Aß¾}±qãFÌš5KÖÕù$µ˜hÁ‚€íÛ·³Ê+**°aÃüüóÏÈË˃½½}½ò:(ñx<ÂÃÑ––###899¡U«V²®V³Åãñpÿþ}8;;7H·äú"„à÷߇ƒƒÚ¶mÛ$ç|üø1Þ¼yƒ¡C‡ÖškF5¬ÊF''§z Ë@‰öï¿ÿ"33Ÿþ¹¬«òÉjöЕ+W0{öl$%%Áßß_(š={6ž>***:t(üýýñÏ?ÿ`üøñ¸~ý:úôéƒS§NáÔ©S°±±iôº¤¦¦B[[[hø~I½}ûVâa×%Ù¶¿áÛ6 IDAT¦mÄ­U^½¬¬¬ ©©©¬ný/_¾9ÚjC+))Ç«s>Œ4ïQNNÊÊÊ ««+v›†ºÆ•C&T1¶úvMuÅÕQRÒ¾Gµ+##\.šššBëĽGYYYÀYWÔ}[ý܉‰‰Ð××o’&ôú\ci÷—å÷Eõ÷¨  R&_W¢îiHs%ùni¬k\½¬¸¸>DjjªDuÿ¤ÉªûYSó÷÷'þþþ¬²7’Q£F±ÊÆG!„’ÀÀÀ&©ßáÇɛ7o꼿4õ”dÛš¶·NTyõ²ÌÌL²mÛ6VÙСCk­OCHII!{÷î­óþÒ¼GaaaäêÕ«5nÓP×øäÉ“äÙ³g5n×T×XÔ¹¥!í{TÛ¹®^½ÊÌÂ^¸÷(44”„††²ÊDÝ·ÕÏ=sæLSkB}¿—>•ï‹êïÑ?ÿüCæÏŸ_k}‚¨û@Ò\cI¾[ëW/KII!5Ö¥¹hÑЙ3gˆ «ÌÝݹš2âóù¤¸¸¸ÎûK3„$ÛÖ´¸u¢Ê«—•••‘ôôtVYS]ãÒÒRÂãñ꼿4ïQ~~>ÉÉÉ©q›†ºÆ™™™¤¨¨¨Æíšê‹:·4¤}j;WNNÉÏϹNÜ{”››KrssYe¢îÛêçÞ»wo“ÇRßó|*ßÕߣ¸¸7dË–Ãäßk­R½‰º¤!Í5–以±®qõ²–5û&°¼¼<ðx<äää<êÓÓÓƒšš†Ž€€lÞ¼Ó¦Mõk׎   &¯gMM%’¨m†fi·­iqëD•W/“——í¶©p¹Üzu»—æ=’dĆºÆ¢ÑKs?4´úœ[Ú÷¨¶sUѹ*qïQåH½U‰ºo?Õk,íþMñ}QQ¤¥ÉɆˆˆÞ½’“ädÕÿÿ/(ãóB.(.äq6KQ÷4¤¹Æ’|·4ÖwrmÇnΚ}tÿþ}lÚ´‰Yž2e V­Z(++#88K–,ÁîݻѾ}{3S7PMc̘1²®B³G¯qãëß¿?‘[>ÿCSÌT j’“ÁOµiÊÄh  /ÊÊû5rå©f­Ù@ŸþyÙìÖÖÖ¸|ùrÖˆªîï¿ÿF—.]d]f^ãÆ ]]Ý:wdøT=yDE‰lRROjN€§,œL ª~š}ÔØÂÃÃ#ëj|Ò”••%š!™ª;q×XQQ±Ö B)Éxzzʺ MêÕ+`Å àÌA³TÓ0 ˜:'9èÞ½©ÎK5G4ª§'Nàï¿ÿ†¬«BQR)))App0 €(©¤¥k×?ÿ ”–6Üq[µŒ?ükÓ†½¼~=ðÛo¶OIi¸sS- €€··73Ò4E}*²²²ê5– ÅÆçó¡¥¥.·y~­ææ?ülÝ äçK¾Ÿ††ø ¦²ÌȨmø$Á0V%òè"9¹î¯…¢QE5ˆÐÐP¸»»7» ~KJ€Ÿ~äÑ'@T}5òCKŠ¢¨–!44ÙÙÙ²®FÄÅ^^@¢ƒ`ÎA×÷µk›>øªæÝ èVQÑôõ šO÷§ EQÔGäSÌJIÖ¬ÊÊ„×s8Àøñ‚h ‹¦¯_U€šš1òó9@åå@z:ÐBgq }DQÕÂdg 1´´öíü|ö œ<)ûà§Rõ‘Ÿi3U4¢(Šj|>e¢"‰Hq1ðã‚€æ»ï€‚ám›7ë×nÝš¾Ž512*Á,ÓDhª>hD1bcc%”Ç'OžÈ¨VÍOEEîܹƒwïÞɺ*Tú˜s€**€£G++`ñbÁ¥Õuè ˜Ö"<\0¦ÏÇHGçC@ŸQõC ŠáååÌ›7U~ëÖ-¸¹¹É¨VÍOqq1ÜÝÝqþüyYW…j@žžžå ˆW®]»S¦ Âë ½{˜A"4‡ÓäU”˜…Ňq€QõC“ eìî]àâÅÆ?®.°jUíÛ5 ÇÇ’%KЩS§Æ¯X ¤¢¢‚””hjjʺ*T3,] Ü»'z½¦&°d °p! ªÚ´u««ê9@´ ŒªÉØÃ‡ÀŽSSÉ `ÅŠ¸|ù²Øíž `Ô¨Qxùò%22I–ß|ó V­Z,Y²)))èÛ·/Š‹‹™×sñâELš4 S§NEqq1Ž? ‚ŸŸúõ뇅 ¢C‡øñÇ™sž8qoþ?¿@EEÜÝÝqâÄ Œ7]»vÅÒ¥K±hÑ"fû¿þú Ë–-êU«Ð³gOôèÑS¦LÁ•+W${S©F'ë wï€3ór‰ ~ää€É“cþlÝúé? ¦ÆÎ¢O€¨z!”X$00°ÆmüýýɶmÛê|ŽmÛ̺ӸÿLMk¯KçÎÉÎ; !„Œ?ž¸ººB9þ<ÑÕÕe¶1béÝ»7kßÏ>ûŒxxxˆ=öÍ›7 Ê*çóù¤°°hkk“Õ«W3åEEEDYY™lÚ´‰BÈÝ»w òòåKf›}ûöEEEÂçó™²µk×’>}ú0Ë“'O& `Ó××—´k׎Y644$ÞÞÞ¤°°){ñâ‘——'þù'S–““CÉÅ‹Iyy9QRR"û÷ïg»¸¸˜BȘ1cÈðáÃE®#„MMMrúôiB!§OŸ&È›7o˜õG%\.—$$$B™6méÛ·/)//g½¶É“'“ºÊÌÌ$ZZZuÞŸú8df²d !**â?ÿÆòø±¬kZ11ì×eb"ë5?)))ÄÀÀ@ÖÕhôÙ¹Œ¹¹Û¶5þy¤M7Y·nìììpíÚ5¡uááá˜1c«ÌÃÃ6l{¼ÿýšššèÓ§«\WW?FVV<<<˜r%%%ôîÝáááBÛWÒ××*kÓ¦ ^¼xÁÚG½ÚtÔ...Ø·oòó󡦦èÛ·/”••™mþûï?ÈÉÉ!((AAAL¹ªª*^¼x999 4K–,ATTzõê…aÆA[[0xð`øùùaôèÑèß¿?>ûì3XYY‰¼6ááá077‡™™Sæáá²²2<|ømÛ¶hkkC®ÊŒ“¦¦¦"ߪe(*vî6m23EoÓ»7°y3Píc÷ɪ>Xjª ú˜·© €dÌÁAðïcÓ¡CLŸ>Ë—/ÇòåËYërssa``À*ÓÓÓCAAÊÊÊDæ@äååA___äºÜÜ\yÌÊæ(QäDL?ÍårAÿ HB9Iâäää@YY&L`•O˜0¶¶¶€ .àСC8þ]tðÓ¶-pèðèQó ~€ÇãA[û>«Œ&BSuE J,###ÌŸ?_h¼š¯¾ú !!!8~ü8 qêÔ)cΜ9b5jÔ(bþüùxðà***pýúuüøãhݺ5Ƈ½{÷âþýûÈÌÌÄêÕ«ÁçóáççWïב€çÏŸ#33»víÂÙ³g±xñâ÷éÕ«œáããÃ4©%$$`ëÖ­ AFF¦L™‚ØØXBPPP>ŸÏ4ñ-X°·nÝBII JJJšš [[[Vs]¥I“&AEEHIIATT¾ÿþ{8;;3=ʨ_SŒ4ož`zŠêtu-[ ÎS§òòZ ™166†¥åVM„¦êŠ@T–.]ÊäµTš:u*æÏŸ)S¦@KK “&MÂôéÓk €455ñÛo¿!>>Ý»w‡²²2¼½½™§0AAA011Aß¾}a``€;wâ×_m±ˆRRRлwoèêêbÉ’%ðóóÃW_}Uã>rrr8þ< `ggEEE˜ššâðáÃhÕª”••ñúõkØØØ@]]ºººÈËËÃ’%KÊÊÊ6lÔÔÔ ¡¡;wb÷îÝ"›íôõõqéÒ%\ºt íÚµƒƒƒŠ‹‹qêÔ)¦«>Eýò pà»LUX¶ xýøúk J[³Eç£ ‡Ð$±V¯^Íú_” ÀÌÌ ,hšJ}DŠŠŠðêÕ+˜››CUŠ‘Ô²³³‘›› ccc¡€ ##éééèСäàgì”)S³gÏ"99FFFPPPê%%%ˆGëÖ­¡££ÃZ———‡wïÞA[[[(§´´IIIàp8011©57„‚W¯^A]]†M0ÅuVVÌÌÌ••Õèçj 3(&pvòó?”µoüù'ЦMƒŸî£URR‚ùóó°o߇'©ë× &v¥Fjj*ìí푚š*ëª4:šMÕ™²²2:vì(õ~ZZZÐÒÒ¹NWWWd3Q}q¹\´k×®Nû***¢C‡"ש««39AÕ)((ÀÜÜ\âóp8XZZÖ©Ž”ì…††ÂÝݽÁ›ÁòóSTT ~””€³g[Vðr€ #Qu: ÚFÕm£šµvíÚI„PT]5VÐìÙ‚'@UmÛöñÍÔÞŒ1`;ˆ6QuEŸQÍÚÚµke]Šª³¹?UM˜ ŠZ*:ÕPè Š¢¨ÀçóQVVÖ`Ç‹ŽôúªÊÚøùç;Å'§¤¤ªªì±Áè ª®hDI$33iiib×'%%Õ8hŸ(%%%xûö-***$Ú¾¢¢oß¾EII‰T硨¦Ðãåæ ò~ŠŠ>”©¨ò~ª lÞ¢ðx<¼}˨r4hŠ’ €(‰¬]»“'Of–}}}Y½ã\\\pøða©ŽùèÑ#˜››#SÜ8þÕäääÀÜÜ>Z÷÷ß MÏAQM©!s€f̪Íè‚  ÁD§-™±±1¼½G°ºû—”|¾ìêD}ºhU'fffhݺµ¬«Gáîݻعs'3‡j‚‚€3gØe“' 8¤ŒŒ€7o>,§¤Á×õ‰¡Œåç £Pü|W …+Ç…‰¦‰ØõÙÙÙ"ŸÄèèèˆì²>}út±ózEFFBII NNN¹SRR‚äädCQQ‘)/++CRRŒ«Ï‚øÑÑÑøï¿ÿ ¥¥…œœ‰ÎEQ¡!ÆŠŒ-b—uêìÙSÏÊ5%%%ÈË˃‘‘.+JN¦OÇ(éÑHÆ=<„…×6úyLµLñvÁ[±ë8€uëÖ1ËÈÍÍÅÆ ´ý´iÓ`nnŽ]»v1eW®\ÁêÕ«QVV†ÜÜ\ôéÓׯ_—hÄòòr888`ݺu˜;w.S~þüyÌš5 ÉbºzøøøÀÇÇ[¶lÁÏ-9;”’¹úŽ”•Œ'hÒ©¤®.Èû‘bœÑfÇã!22FF´+>·nÝj Q)êc¶u+pù2»Ì×ðö–M}>vt, ª!4ë&°ÈÈHìØ±.\À¬Y³DnS^^ŽqãÆáÞ½{0`@×ÐTÒ„©–i£Ÿ§¦üŸª=z„™3gâÈ‘#ušæ¢ª®]»â?þx{[[[¸ººâÀprrÂáÇѳgOt¦ûÔ' ®9@aa@õVf`ÇŽ¬\3Q™dlÌž.‡>¢ê¢Y@&&&˜>}: !nÎ×¹sç¢S§N2šÃ4Ls˜&“sW—™™‰Ñ£Gcîܹðôô¬÷ñbbbÐFÊÉŠüüüàëë‹~ø‡Æúõëë]Šj uÉâóñãªã'ji ò~””¡’Ÿ8q9@ô Uͺ ÌÐÐýúõƒ‰‰è§[¶lAzz:6oÞÜÄ5ûøTTTà‹/¾€……6lØPïãâÎ;èׯŸTû;ÊÊʘ9s&òòòàååUïºPTS6ˆÀÇHLd—<XX4påš šD5¤fÕäìÙ³8þ<~ýõ×Ç9räV¯^ÍúWUddd#×´ilß¾ׯ_‡¶¶6æÌ™???øùùá—êÕrŒ€€,Z´íÚµCÛ¶máïï/U=1uêTœ>}S¦LrÕÏD¸|ù2 ‡€€úÔH Õ§n¨~ÓåÆ[Þ¸¸z•½¾GÕ;öã¨ßǼü!H°\},õûÔ–+{ðöë×?üðJKKÑpˆ¸¶¡fdÁ‚ +Ã××€;vÀÑÑÞÞÞŸôôtÙVLFÌ3¢ 4ˆµì]­{Je8qâD‰Ïill,2¸lÛ¶­Èreee¡ò!C†`È!Ÿ“¢‹¤9@iiÀ_°ƒ]]àôiüÔ¦2hĈ02âã?¬KI¡%­\¹’µ‰1cư滢(Š’”$**]Û«æ¬p8ÀÑ£€iãwýäUæÕ³ã*ÕÂ4ë sçÎÁÌÌ ‡¡C‡`ff†ßÿ]ÖÕ¢(ª…Z³¸}›]¶d 0l˜lêó)«žM{‚QÒjÖO€<==%îÎ}óæÍF® EQÍYm9@!!@õüü¾}…Ë(ñªæU ‘ö£¤Õ¬ŸQE5•ÐÐPdgg‹\—œ Lœ(h«¤§œ:H3MýÇÃýû÷ÐÑ ©ú£%‘uëÖ±ò£öïß³gÏ2Ë...8räˆTÇ|ôè‘T½rrr`ff†¨¨(¦,** ßÿ=æÌ™ƒ… âÌ™3(¯š]JQMDÜ8@eeÀ„ Àû÷Êää€ãÇ…›q¨šUÍ¢cQõE J"HKKc–oܸ¿þú‹YNJJBNNŽTÇ,))A||¼ÄKEEâããQ\\ ÈÍÍ…ƒƒ.\¸BÒÓÓ1uêTšÈN}T¾ýVн½zÙÀ²©OsA›À¨ú¢_©:9wîœØuÅÅÅàr¹——oÔ:())!""ŽŽŽLÙÁƒ1sæLìÙ³šššz~ŠªJTÐßÏÞ® 0°‰+×LTÍ¢IÐT}Ñ'@²vè`fÖøÿúô©±AAA033ú·k×.‘ÛO›6MhLžçÏŸcÀ€ÐÐЀ††ÄÎÁV]VV¬¬¬ðÛo¿±Êïß¿: ##ChEEEVð °°°€†††D票†R=(!øòKÁ”•ŒŒ€'M`”ôjÊ¢O€(iÑ'@²–“ÃÌBF<==áââÂ,Ÿ³Ìºu€««lêÓq8€¡!{2Ù”Q’£b)–ŒŒ Œ=þþþ[uVÆ:pvvÆëׯ%ÞÞÃÃ8tèÁS(###|öÙgµîûÏ?ÿ )) C‡ÅÀÁãñê\oŠª >Ÿ²²2\¸ìØÁ^7d ›z5'%%%¬æpÚ FÕ}$kÓ¦£F5þy$l¤¢¢_|ñ¬¬¬dFõŒŒ ©rq8|}}±cǬZµ ‡¬Y³¤jÎ?~<.\ˆððp:GÕ¤BCCanîŽiÓØ]áÛ¶Ž<± ê§ê\` šªÉš¦¦àßG`ùòåxñâ"##!×Yš7oÞ„———TûL™2+V¬À–-[+W®ˆÝ¶¸¸JJJ¬²¸¸8`šó(ª© î‰Þ½ªc!*(gεÌJI¨j@ŸQõC péÒ%lÞ¼¸qãSnaa'''‰ŽñÇÀÊÊ %%%Ø¿?²³³ñõ×_KUV­ZÁËË +V¬€——ôkÈýí·ß°}ûvLš4 VVVxùò%6nÜwwwtëÖMªóRT}-XhDtwssCXX˜ò¡C‡ÂÉÉ –––PPP`Ê;wî £*?¿zöì MMM,[¶ ‰‰‰°··GXXÚ¶m+öœpsscæÍ›‡ÄÄDÌŸ?ŸUÎåráææÆ$9;;;ÃÞÞûöíCBBôôô0f̬^½šö£šÔÉ“ÀO?ñh¡òkuÔ(`áB™V«Ù©:@ŸQõÃ!’ÔÒUŽsS}¼›ª,X333,X° i*EQ $++Kª©H(ÑâãÎÜÜsÜ´Bûö‚§AZZ²®]󒜜ÌÊ †ý°ÞÕ¸{WF•k&RSSaooÔÔTYW¥ÑÑ'@EQõ0s&› ‚q€””y?4øixÕs€hU´EI‚@EQu0kû­’R(~ü1›Ž÷ÓˆªÎæT«>â} DIŠ@”DNž<‰­[·2Ëÿý÷bbb˜åÅ‹ãæÍ›RóÍ›7˜2e òóó%Ú¾  S¦L9ºtii)>|ˆ“'OâáÇRÕƒ¢¤uô(põ*»lófOtïö9• IDATNüiLÕs€eìmhDIŠ@”DÂÃÃR%ÙÁ××Ìò¹sçX‘$ø|>Ž=Š¢¢"‰¶/))ÁÑ£Gñþý{Vù±cÇ ££ƒþýû#00[¶l‘ª%”áîí..À¼y²©OKW=ˆ&BS’¢½À¨:¹~ý:äååe] ìÙ³+V¬ÀñãÇ1räH‚§AÕXüü€ÌÌË**À¡C@f&ZZZàJ0í U7ÕÇh"4Uwô“*c722p"=½ÑÏÓZA?XXˆ]åÊœ;wN¨|̘1BœÁS}}}Lœ8‘)+**Âúõë%%%øûûchÕA:j••…… ÂßßöööLù«W¯°~ýzlݺUhzŽüü|¬Y³›7of‚B+RTC9qøí7vÙºu€•pî\(ÜÝÝÑŠÎ{ÑhªÏЮðTÝÑHÆb p´ œ2UV®1200`ÑÑÑ8zô(Æ/rû[·nÁÜÜœ­\¹#GŽÄðáÃqòäIŒ1áááè.AŸ`mmmÄÅÅaçÎÌlð°ÿ~ÄÄÄ@WWWhÀ¾dddÀÕÕǃ­­-†úQ<¢š—´4 ÚàäèÙóCs˜§§gÓWª…•DŸQuEs€(€““,X€ ÀÇÇwîÜÁ²eË0xð`‰±jÕ*œ9s_ý5þûï?ØØØ`÷îÝïïçç‡3gÎ çÿ]kÊÊÊpìØ1øùù‰Ü>>>ƒFpp0ÂÃÃáããwwwK|^Š’ÄW_|þ‡e%%AÓWÌLÕM‚¦êŠ~t)–òòrL˜0666X·nTûjhh°–{öì)Ub´——”””pòäI‚ÉU‹ŠŠ0a‘ۧ§§ÃÔÔ/^¼Àùóçqþüyüû￈ˆˆ`=E¢¨ú:s¸p]¶z5`kûa™Ï磬¬¬IëÕÒT IÐTÝÑ&0ûLG‡mlý<ê6 -_¾¯_¿Fdd¤PδTUU¥úƒ ¬¬Œ)S¦àÀðõõÅÁƒñå—_BEEEäöÆÆÆÈÍÍe%ÚØØÀÎΪOËMQuÄãAhpCGGà›oØe¡¡4¨±‰Ê¢M`T]ÑHÆìÔÔ`§¦&ëjΞ=‹   „……AGG§ÞÇ»uë–Dù?UùúúbÛ¶m¸zõ*®^½Šèèh±ÛZYY!-- iii0øÿhhEEEˆ‹‹ÃðáÃëUwŠª4w.PuäEEàða úo šÔøDåÞ‹òrÁrn.—¨«Ë ‚Ô'…6Q€'Ož`êÔ©X¿~=ôôôššŠÔÔT&G±±±(((ŸÏÇ–-[ðüùsÌ™3GªzXYYÁÝÝ>>>èÝ»7ìììÄnÛ¿tïÞ3gÎD^^222°nÝ:”••ÁÛÛ[ªóR”(/§O³ËV®:u’M}(arr€¾>»Œ>¢$A € ·U~~>.\###æßÂê#¾ÕàÆÐÒÒ‚¡¡!V¯^ýû÷£gÏžR×ÅÏÏ|>_lòs%999?~)))ÐÑÑ¡¡!Ž9‚3gÎÀÊÊJêóRTUÀìÙì2 @ôö4¨ñ‰Êh"4U7´ Œìܹ;wî»~Û¶m¬å+W®°–ãããHNNF‡ ¤¤Tã9A*÷òòY®­­-Tnmmˆˆ$''#??õÎ]¢(ð÷t}¯¤  hú7Î!Íj|¢r€šMÕ €¨¥««Ë¥µ©Wÿ HQõpå ðë¯ì²eË€®]ÅïCs€Ÿ¨ A9{™>¢$A*SEU‘•øú²Ë:w¾ýV6õ¡jGŸQuA Š¢¨*-bÿårM_µÍ°Bs€Ÿ¸ Úžª QEýßµk‚`§ªo¾$Í!44ÙÙÙS1 € èþýûBå´ Œª‹…‡‡ãÚµk¬²¢¢"?~þþþð÷÷ÇÍ›7éLâbDDD $$DìúsçÎáÙ³gR“ÏçãÈÿØ»ïø&Ëýÿã¯$Ý›¦,A(Z@6ĉGPÑÂ7¨¸Šƒáèñˆ€‚âÀƒ"žªÈ£.dXQ @Ë,ÐÒ½Û´I~Üt¤‹´ÍÝ;Éýy>y´×4¹¼lÃ'×ý¾¯+>Þám+Ìf3ñññdddÔº/''G¶¿Í’—÷Ýg¬W/xñEÇ~>66VÐ*«/$§ÀDSx|”’’ÂÒ¥K¹ýöÛk@ÿý7óçϧC‡téÒ…'žx‚iÓ¦iÔS×¶|ùr,XPÙ¾ôÒK?~|e{Ú´i Hu9rä“'O¦  À¡Ç1yòd’““+mܸ‘^½zN`` 7Üp§äÝO4ÁSOÁ‰Um“I™ :ÇÅŒÂÈ h ¿ ,##ƒ'NÔyeÒ%—\ÂüQÙ2dW_}5óæÍ«wû¡xå•WÒx©Õ}ûö1fÌ}ôQ6nÜHaa!·Þz+÷Üs6lдo½üð,YbìÉ'aà@ÇŸ#33“ÐÐP»­Y„s™Íf j½Ÿ·i£,ˆhµ*íÜ\(*‚€ :)܆Çÿ¥ÆÄÄCNNN­û¼k¤srr0›Í-úV¸¯üùª¿Ž)ÈDTlT½÷ïÙ³§Îý³úõëÇÅ_\ûùL&Luì/¶e˾ÿþ{|}}¹í¶Ûèܹ³CýËËËcÍš5Œ=š¶mÛVÏÉÉaíÚµŒ7®ÖÏ|ðÁ3gΜʾ,^¼˜þýû³gÏž:û-DM0eŠý±=ॗ÷<²úê[Èd‚¨(ûu›NŸ†nÝZ¸ƒÂ­xü)0G™ÍfæÍ›Ç”)Sì £øøxâââìnÕ%$$4ëu³¿Ë&qr¢ê·ä§’ìÇÁƒY»vmåíÃ?dòäÉlß¾½ÎÇ¿ñÆ,«±PÊo¼Áĉùý÷ßyùå—8p 'Ožth‚ƒƒù÷¿ÿÍÛo¿mw¼bükî4pòäIúõëgWˆ]rÉ%xyyqàÀ‡^Wïj^µTó÷[ígŸ…£G+`4‡‚Ÿ_ãž/66¶Öï¯+ü÷yRûý÷ß·+~ªß¯œ«jŸ>­}Ý¥½nÝ:âââ¸ì²Ë˜?¾n²°[]Kîz ©S§°páÂZ÷Ùl6n»í6Nœ8Áwß}Wyú«â—£æ/MÍçíÒ¥Kåó7VÊÂ’žHjÒÏ6†_g?ul[ ‹ÅÂ5×\ƒ¯¯/_}õF£‘'žx‚ýû÷Wæ¨ÆŒC×®]+ßð;wîÌW\Á{g··7iiiôë×Ûn»W_}µÎ×IHH`À€dddÁ¼yóxóÍ79qâDeQÓ§O&NœÈ¬Y³ÈÉÉ¡U«Vlß¾AƒÇ¢E‹Ø»w/­Ïnd³ÙhÓ¦ 3fÌhÔ6z”““C—.]êœÕ‹Ÿ~‚Q£ ú»àÔ©Pcásá®»Ö¯¯j¯\ &h×w•ššJLL ©©©ZwEuºŸ*//çŽ;î -- 6Hö˜>}:ÇŽãÓO?mÔ¶111•³gmÚ´aèСìܹÓ៿ûî»ÉÌÌä믿”+÷8À=÷ÜSçã{ì1ÂÃÃéׯ>ø S¦L¡GdddTî/D}ŠŠàž{싟 .€Ù³›ö|²úê[$-Ïã3@ ÉÉÉaüøñDEE±~ýúsî]¥†€^´½«í¹ØLÞ‘çXÅí¬•+W²xñb¶mÛFXXX³^³k×®$&&:üø¨¨(nºé&>øà®¿þz–.]Ê 7Ü`— ª.<<œ„„>þøc’’’h×®S¦LaðàÁtíÚµY}žoæL¨vA!,]Úôà¬d€ÔW_d1DÑx_mÙ²…>ø r&bÒ¤I<øàƒ 4ˆO?ý”Í›73lØ0®¹æšÊŸ¹çž{¸ãŽ;Z¤áW‡~uËïU—¿þú‹{?üèèèf?߯¿þJ÷îÝõ3<ðW\q‡bÅŠ¬Y³¦ÁÇóðÃW¶—,YBÇŽ¹ôÒK›Ôg¡[·B¸? #F4ý9e/0õÕ·rŸ}[VÃçâñÐ\À¤I“˜4iR届Ùo¼‘Þ½{×ú=Îdgg3nÜ8zè!&4ñÄyõàÜo¿ýÆŽ;ˆoÔsŒ9’îÝ»3~üxÚ´iÃå—_îðÏnÞ¼™éÓ§3wîÜZWø Q¡¸î¾»ê’i€®]aî\íú$šOf€Dcy|Ô¶mÛzO¡tèÐ:´p\ÓܹsINNæ‹/¾àË/¿¬<ËË/¿ìÐs̘1ƒÏ?ÿ³ÙÌüÁí·ßέ·ÞÚè¾<ðÀL:•ùóçc0|lll,éé餦¦rüøq.\È”š×4 QÍóÏÃÁƒUmƒ>ø›÷¼²úê[d5hÑxò—*˜2e ×^{m­ãíξ«<òÈ#VŸ3gŽ]`|åÊ•´oßž?þøƒ'NÃðáÃ|Í=z°iÓ&BCCíŽß{ï½\rÉ%µNc±iÓ&zõêUyì ))‰ž={rÉ%—ЪU+Çÿ£…îlß^û ¯ûîƒFL4ÖK2@êk($!hÑXº¹ ¾)Zâ2x!´¢·ËàKK¡o_¨¾e]§Nð÷ßPÇ2SÂÍ”•)Û–Tÿ­¸XYÏI8N.ƒBg_ü€²ý…?žÁÛ[Y º:™ ‘Háñàµ×ìÝs\}µó^CÖR_Cë¡EãH$„ðhf3Lž KÕ±`Áç¾Î¦M›ÈÍÍuî“ ;lÙ²¥Þû%-CBÐBöòËJΧº÷߇Ùûf“u€Ô×Ð:@Êýöm™ ‘ !„ÇÚ½æÌ±?vçðÏjÓ¡.9&Cf€œà‹/¾àhÕvÒB¸…ÒÒR­» ª²2åÔWõXN»vPÇ~ÈN!멯¡u€@Vƒ#©Í4nÜ8ºté¢u7ÜÚ¯¿þÊСCµî†G«oŒçÔœñ sç*3@Õýç? ÖRQ²úZdH4ެÔGÖB¸žŒ èÜYÙñ½Â-·ÀòåÚõI¨oÇ<¸ª ý¥]Ü‘¬$„nì7싟¨¨Ú›Ÿ Ï#3@¢1¤šÓÃ' ­éiŒssaÑ"ûcÏ> jŸ™’u€Ôw®u€Ú¶Uöv«™©,ƒ D]¤š[»v­Ö]ðxzãwÞQŠ  ðÀê¿®¬¤¾s­äãS»Ð•Y Q)€„æh‰tN/c\XXû*¯©S›¿Ó»#bcc%­²s­rL8N !„Çxÿ}%]!$yD»þˆ–'— GI$4§§|ŠVô0Æ¥¥0¾ý±‡†°°–y}É©ï\  á8)€„æô”OÑŠÆ8>ÞþÓ~@<ñD˽¾d€Ôw® H$' ! Íé%Ÿ¢%Oãòr˜7ÏþØ”)Êåï-EöSŸ# 9&%3@B··|99RÕöñ§ŸÖ®?B;2$%Мò)Zóä1¶Zkoxz×]СCËöC2@ês$$3@ÂQR Íé!Ÿ¢5Oã5k 1±ªm2Áôé-ßÉ©O2@™d/°È^`B¸¾~ýà?ªÚ·ßŸ|¢]„¶JKÁϯªm0(Ǽ½µë“;‘½À„ ¬_o_ü 0c†výÚóõ…ððª¶Í:ø·\4@Bszø¤¡5OãÙ³íÛãÆA¯^ÚôE2@ês$rL8F ¡9OΧ¸ OãÍ›á×_íÍš¥IWɵG2@P;-¨‹¬$4çékÔ¸Oã—_¶o­ä´"ë©Ï‘u€ ö \ &ê"3@B·³clÜhLËÙáZdH8B ¡9Oͧ¸OãšÙŸ#`øpmúRA2@êkjHf€D]¤šóÄ|Š«ñ¤1Þ³Ö­³?öÜsÚô¥:É©ÏÑ „ …#$$4ç‰ùWãIcüÊ+Ê¥Í €«®Ò®?$¤>G3@r L8Bf€„nãàAøüsûc’ý5É)0á)€„æ<-ŸâŠo½õàâ‹/æÑGÅ××@N ¡¡·Þ‚ǯj)‹!†‡k×'á>ŠŠ 0°ªm2Ai©,p.z:æñëÅÄÄSïýÁÁÁÌ’Í„„p)f3¼öšý±”âG8. BB /Oi[,pæLí™!¡_ L¸>=|ÒК»ñÇCJJUÛϦMÓ®?Ž ú“Ù^4L ¡9w̧¸wc‹EÙô´º{땺,É©¯1 ´h˜ÇŸ®Ïö©ruî4Æ+WBrrUÛÛž~Z»þ8ª®Õæ…s5f/0 ´h˜Ì !\†Í¯¼bìŽ; S'mú#Ü›œ ‘HhÎÝò)îÈ]Æøÿƒ½{«ÚF£²é©; ú›’ Ñ)€„æÜ)Ÿâ®ÜeŒg϶oO˜Ý»kÓ—Æ’ ú›’ ÑÉ Í¹S>Å]¹Ãû-$$Tµ ˜9S»þ4–d€Ô×Ü @¢:™B¸„š³?cÇBŸ>ÚôEx†š3@r LT'М»äSÜ™«ñ/¿(·êÜm}RÉ©¯¹ ´4°ZÜ)ᶤšs—|Š;sõ1®9ûså•0p 6}i*É©¯±   åV¡¼ÒÓUè˜pKR ͹C>Åݹò'$(ùŸêž{N›¾4Gll,Zwã56¤üŒ}[r@¢‚Û@999lݺ›ÍÆ·ß~ËW_}¥q¯„ÍUsÝŸaÃ`äHmú"<¡E}ܦZ¼x1K—.`õêÕÜ~ûí¼üòËÜ{ï½÷L4—«çS<«ŽñÞ½Pó윻e*HH}Í¡Eýܦúûï¿+§>—,YÂìٳٱc[·nÅb±hÜ;Ñ®žOñ®:Æsæ(«?Wè×®½V»þ4‡d€Ôר È ¨ŸÛ@~~~”––RRRÂÎ;:t( œ;%%½[så|ЧpÅ1NN†+ì¹Óº?5IH}MÉI$êã6Ð7ÞÈO<Á?ÿùO.ºè"¢££9yò$)))´mÛVëî !iî\eç÷ ]7ݤ]„g’S`¢>nS3†E‹qÕUW±âìÇÆÝ»w3wî\¼½½5îhWͧxWã”øøcûc3f(«?»+É©¯) ™õqé(''‡õë×Wf|ÆÇŒ3ètvkèë®»ŽGyDË. 'pÕ|Š'qµ1~í50›«Ú矷ܢ]œA2@êkJHf€D}\ºX°`]ºtaÖ¬Y$%%iÝ¡W̧xWã3g`ÉûcÏ> ^n¾3¡d€ÔçŒ Pjª}ð^è—K@aaalܸ‘Ÿþ“ÉÄW\ÁÈ‘#ùè£(**Òº{Bˆ&xã (.®jwè“&iÖááBB 0°ª]V™™ÚõG¸—.€*tíÚ•—^z‰#GŽ0kÖ,6lØ@—.]¸ï¾ûؾ}»ÖÝÍäjùOä*cœ“‹Ù{ê)ðñѦ?Î$ õ5%µgä4˜7)€*F®¾újV¬XAbb"}úôáÁ¤wïÞ|øá‡ZwO4‘«åS<‘«ŒñÛoC~~U;* î»O»þ8“d€Ô×” HZÔÍ­  êÂÃÃyôÑGùã?øä“O(®>§.ÜŠ+åS<•+ŒqA¼ù¦ý±©S! @›þ8›d€Ô×” òsöm™àFÐxóì»gQQ3fÌ`Ú´i¤¥¥Ñ¯_?~øa{(„hÈâÅöÙ‹ÐP?[ÑdHÔÅm  ÿþ÷¿dddðŸÿü‡o¿ý///î¸ã{&šËUò)žLë1.) ì=òˆRy É©¯© Ù^ÔÅm  cÇŽ1|øpV­ZŬY³˜7o'Ož¤°°PãÞ‰æp•|Š'ÓzŒ?üP¹ü¸B` rúË“HH}ÎÊÉ)0nTµnÝšÄÄDÒÒÒØ½{7 //¼¼<{'šÃò)žNË1.+ƒW_µ?vÿý©MÔ" õ55$§ÀD]ܦº÷Þ{yþùçéÙ³'±±±tìØ‘„„Ìf³ì&„ ûôS8v¬ªíë Ó¦iס?‚uq›µWûôéþ}û8qâDåìÉdâ‹/¾ÀàÎ RSS¥ˆU™VclµÂœ9öÇ&O®ý’'ÈÌÌ$44/w_ÒÚ…™Íf oÔÏÕµ´n3´qãFþüóO¢££1™LôíÛ—¡C‡jÜ3Ñ\ZçSô@«1þüs8x°ªíå¥l{á‰$¤¾¦f€ÂÂÀß¿ª]Z MÈR ã6P~~>sæÌ¡}ûö 2„Y³f±qãFJJJ´îšh&É©O‹1¶Ùà•WìÝz+téÒâ]i’R_S3@ AhQ›Û@7Þx#[¶l!33“×_€€&NœHXX˜Ö]BÔaÝ:س§ªm4ÂŒÚõGè›\ /jr› ¸¸˜uëÖñÎ;ï°hÑ"®¸â –/_Þ¬ç,((`õêÕlݺ«Õê¤žŠÆÐz=ÐbŒg϶oßtôìÙâÝh1²úšºÈ ¨Ím  %K–E||<ãÆ#))‰+VpÓM75ù9—/_Î¥—^ÊÎ;Y´hçwiiiNìµp„d€Ô×Òc¼q#ìØalÖ¬íB‹“ úšš¹^Ôæ6ÐàÁƒ¹óÎ;ILLdÍš5|óÍ7ÍÚÿËjµò /ðöÛo3oÞ<–/_ÎW\Á[o½åÄ^ GHH}-=Æ5g®»bbZ´ -N2@êkNHN‰šÜ¦êÓ§ï¾û.‰‰‰<øàƒüôÓO\tÑEL˜0¡IÏg4±Z­lÚ´©ò˜Íf#Ô“ÖæBÛ¶Aµ?+Àóg„ë“S`¢&·)€:ÄG}Äÿû_6lØ@QQQ³Öúì³ÏX²d }ûöåþûïàñÇ·{ÌöíÛÙ¼y³Ý­:i7¿]=Ÿâ ýñÄvÅ·ÄëÙÏþlfÔ(2D½×s•vff&7nt™þxbû‡~°Ë5æç• ªöéÓÚÿ÷¸JûàÁƒlÞ¼™… ò믿ê&ë6ÐóÏ?Oß¾}ùì³ÏèÝ»7Ÿ}öiii¬\¹²ÉϹnÝ:ÆŒÜ9sHOOgëÖ­üõ×_Nìµp„d€Ô×Rcœ”_mL/³?›6m’} U–››ë´ Ì ƒÍf³iÝ G¤¦¦···SžïÔ©StéÒ…ôôôÊÓ^Ó§Og÷îÝ|óÍ7ÄÅÅÙ}B4lüxXµªª=hlß®]„¨•Õ#Z~~ÐŒ©ÇJMM%&&FWçºÍšímÛ¶%++‹÷ߟC‡Ƶ×^Ë•W^Ù¤ç³Z­Frss+  víÚñí·ß:Jïb IDAT³ÛBèFb"¬YcL/³?Âõ…‡+ûЕ–*í’ÈÉQV‰úä6§ÀRRRèÝ»7«W¯¦U«V=z”ñãÇ3»æå&:ï¼ó¸ù曹뮻X»v-ï½÷¯½öO<ñ„“{.ÎEŸ4´ÖccÇŽå·ß~cþüù¬^½š-[¶ðÒK/5y;Œ%K–píµ×òùçŸó×_±bÅ î¼óN'÷\œ‹d€Ô§ö?5×$9ô´O±¬¤¾æ¬r)¼°ç6§ÀNž>‹ÅBçÎ5îú•žK—Ú{æeï/!\‰¬-ªs›·¨'Ÿ|’àà`Z·nMtt4<ýôÓ|òÉ'åÖ­IH}jŽñ›oBQQU»}{Ðã™dÉ©¯¹ 9&ªs›ÊÁÇLJuëÖ±k×.^|ñEV­ZErr21ž¾¾¾HH}jq~>,ZdìÉ'ÁÇG•—si’R_s3@r LTç6  111•EObb"×]wÉÉÉ÷J4‡d€Ô§Öÿç?Ê¥ÄÂÃáì¢êº# õ57$3@¢:—Ÿ²Z­Ìœ9“¾}û2yòdظq#C† iÖnðBˆ¦+-…7Þ°?öÈ#¤M„8ɉê\¾Zºt)+W®ä±Ç£k×®Œ?ž·ß~›n¸×_×^{Më.Šf’ úÔãÿþª?m` <ö˜Ó_ÆmHH}ÎÎÉ)0}sùèÇäùçŸgòäɼð 6Œýë_¬_¿žÉ“'kÝ=á’RŸ³ÇØbšŸ=¦L±ßj@o$¤¾æf€ÂÃíóiEE—焎 ·äòPnn.mÛ¶­lwïÞ{ï½—#FhØ+áL’RŸ³Çø³Ïàð᪶·7L›æÔ—p;±±±Dè¹lÍÍ PíŸ@fôÌåCÐV«•Ÿ~ú‰œ³IË={ö““Ê+*3qâD­º'„.Íkß¾ã8ïgd€ä˜¨`°Ùl6­;áˆÒÒR>úè#Þ|óMÌÌ™3éÖ­›ª¯g÷U½JJR‚ÎVkÕ±µkᆴë“M‘ž­[Wµƒ‚”M}…"55•˜˜]¬Ðïò !Vðõõå†n`ôèÑ,[¶ŒuëÖiÝ%!tãÕW틟޽¡™YT!4©,ÜY¡ @¹ ýq‹èĉ<öØcôêÕ ³ÙÌÁƒyüñǵî–p=|ÒÐZsÆøÔ)øè#ûcÏ>«¬ª+ªÈ^`êkî^` «A‹*._Íž=›Bbb"o¿ý6;wÖº[‰ä*>õ5gŒ_ÌæªvçÎpË-N蔇‘½ÀÔ×ܽÀ*HH€@¿üò iiiÌž=›Ö­[c0jÝ„{“´úš:ÆYYðÞ{öÇž~äB§Úd/0õ9c H(\þmì›o¾Ñº BèÖ;ïØç#Z·†»ïÖ®?B8ƒ¬-À f€„ç“ úš2Æ……ðÖ[öǦN'uÊÃHH}ÎÈÌ …@Bs’R_SÆxÉÈ̬j‡„ÀC9±SF2@êsVHf€¸Á)0áù$¤¾ÆŽqY,X`ìÁ!4Ô‰ò0²˜ú$$œIf€„µ|ò ¤¤Tµýüà‰'´ëÎ$)€„ ú3ÆV+Ì›glòdhÓÆÉò0’RŸ³2@r L€@ÂHH}ã5kààÁª¶É¤\ú.& õ9+e¿”C^5ûi…›‘HhN2@êkÌÏ™cßž8ºvur‡<¬¤>ge€ŒÆÚ3š2 ¤?R !*}÷ìÚUÕ6`útíú#„Z$$¤š“ úãš³?cÆ@t´ ò@’RŸ³2@P;$þH$4' õ92ÆÛ·ÃæÍöÇfÌP§?žH2@êsVjÏÉ)0ý‘u€„æ$¤>GƸæìÏÈ‘0dˆJò@²úœ•9&d¨RII 'OžÔºBhbï^øê+ûc’ýžL.…º/€~ùåú÷ïOTT½zõâèÑ£ZwIw$¤¾sñܹ`³Uµûö…Ñ£U‘ úœ™’ ¡ëè§Ÿ~"66–{'N••EÇŽµî–îHH} ñÑ£°b…ý1™ýi<ɩϙ A ƒÍVýsŸ¾\vÙe 2„95ÃgÅÅÅÙ}Â=ü0¼ûnU»{wHLTÖJÂS>m_……Av¶výq©©©ÄÄÄèbf^·oqf³™_~ù…¢¢"®¾új† ÆóÏ?Ùl¶{\nn.999v·ê¤-mwn<˜Ã‡Ú=‚gž©*~´îŸ´¥­V»uk0™rªÝii®Ó¿–l“““ñcÇÈËËC/ó"º-€RRR°Z­ôìÙ“7Þxƒ×^{uëÖñD¿ùæâããínÕI»ùíêŸ4\¡?žØ®ãš÷?ôP<%%UíÐÐxî¼Sûþºc;33“í«I—êŸ'´—.]j—jÎó™Lhÿ;ï8·¿îÒÞ¶mñññL:••+Wb±XÐÝžKII¡cÇŽÀW_}ÅC=ĉ'9ÖR/^,—«¬®1ÎͅΕ¯,€'ŸláÎyˆU«V1jÔ(ÙCE§N"!!Ái—Â÷ïo¿òù–-0l˜SžÚmÉ)0hß¾=~~~ìß¿¿òXzz:¾¾¾öJŸ¤øQ_]cüî»öÅOx8Üw_ vÊÃÈ^`êsæ:@ÊóÙ·%­/º-€ŒF#=öÓ¦Mãĉüúë¯,Z´ˆ»îºKë® ¡º’X¸ÐþØ£BP6ýB ²´¾é¶ø×¿þÅСC¹òÊ+¹÷Þ{‰eæÌ™ZwKwô0Õªµšc¼t)œ9SÕ T Ñt²úœ¹È ÞéºòóócΜ98p€}ûö1cÆ L&“ÖÝÒYH}ÕǸ¼æÏ·¿Ê³7Í#ë©Ï™ë,†¨w²˜Ðœd€ÔW}ŒW¬P?¬àãÓ¦µ|Ÿ<ì¦>gg€ä˜¾ézH½±Ù”m/ª»ýv8ï ƒfÝòX’R_Kd€ä˜~H$4' çûùgèßî»ÊÊ”1îÒn¹EËžy.É©¯%2@2¤’š“ ó?O? Ÿ}VóeŒŸ~¼ä¯^’RŸ Aë—¼ ኊ`ÞÉ)0¡9ɧԖ“qq°hÔ5©ªœêzôQÇ®ì’1VŸ¬¤>µ2@‚Ö')€„p!‹²}ÅóÏCFFíûF%={¶vB4ŸÌ铜š“|Šbófè×|°îâçÿ€„å ¯Æ?2Æê“ úÔÊIZŸ¤šÓ{>åèQˆ…Q£`ÏžÚ÷w꤬åóóÏзoÓ^CïcÜ$¤¾–ÊedÔ}êYxƒÍV±¸¨)..Îî«ÎTXsæ(Wn•”Ô¾? @Ù¹ýé§•­,„ꉌT¶Á¨’:h×­¤¦¦£‹YcÉ ¡U«`êT8y²îû'N„W_…Ž[¶_BèUûööЩSú,€ôDN Íéá“F«¦O‡ñãë.~úõƒ-[”ý½œYüèiŒµ" õ©• Bë‘@BszɧäçÃ7*»¶×Ô¦ |ðüö æü×ÖËkI2@êS+r)¼É)0¡9=¬QsäŒ {÷Ú÷ö†ÇW.{ Qïõõ0ÆZ“u€Ô§Ö:@ 3@z$*ûé'¸ùfû|(—²¯^­\Þ.„Ж@ú#§À„æ<9ŸòÞ{pÕUµ‹Ÿ‹/VNwµTñãÉcì*$¤>53@r L¤šóÄ|Jy¹²MÅ@Y™ý}ãÆÁ¯¿BçÎ-×OcW# õ©™’ ý‘S`Bsž–OÉÎV®òÚ¸±ö}Ï=/½CËöÉÓÆØIH}jf€dGxý‘ àõ×_ç™gžÑºÂ$&ÂÀµ‹X±þýï–/~„çVs(-MÙ›Ox.Ý@ü1qqqìܹSë®è–§äS6l€Áƒ!)ÉþøyçÁ/¿ÀÿýŸ6ýÏcW& õ©™òõ…V­ªÚV+œ9£ÊK ¡ëhÓ¦M¼þúë¼ôÒKZwE×þæÌqþJ[ÚÒn¸žnß~ÿ}múSVŸ~ íÛÇ1r$¬]«|ÀúòË8zõ‚¸8eeg½Þºu눋‹ã²Ë.cþüù”Õ¼tÕCér7ø§žzŠÄÄDÆŒÀ¶mÛØ¶mO>ù$wß}7>>>€ìßRRSSië Ó$°{·òiìøqûã!!°|9\w6ýª;ޱ»ÉÌÌ$44//¹¸V-f³™‚‚ÂÃÃUyþéÓí·ª‰‹ƒ_Tå¥ê”•¥¬¶hQý%WèÚ.gOˆéi7x]Î]vÙeDGGsôèQŽ=JFF%%%=zkÍóBuî–OY½†¯]ütëÛ¶¹^ñî7ÆîH2@êké PK;pzHÙyæÌs? œ »áåý¦æ…Â1ºœª)>>žøøx6oÞlw\f€DM/½¤|*¬ùWsùåðùç ÒS!D øüs˜0¡ª=v,|ù¥z¯·q#¼ñ¬__û=¥‚Á 9#FÀk¯AzzíÇøúÂSO)ÅS@@óú$3@B;EEÊeì/¾XûꡇàÛo¥øÂݵDº´âãá’KàÊ+áë¯ë.~•÷–ÄDøê+xúi8x~L¦ÚÏ9{6\t‘2C-#0iÒ¤Z³?¢å¸ú'”eÏ®Ï>³?îí ÿùr¾ÞÕc®>Æž@ÖRŸšëº§ÀÒÓ•äΕ+G÷ì©ûqçsç‰Ê{Ë…VÝï¼ 0lXíŸ=~bcáê«•Ój¢aR ͹r>eûv0ví²?ß}§ìõå\yŒ=…d€Ô§v¨®Õ › Ý»¦LN”ä´´ºwé¥Ê•_GŽÀ³ÏÚ/ÊXSLŒ²¸êGA›6µïÿþ{èÓGyž‚‚æõß“I¨’Ò·O>QÞ¸JKí÷î­äÎ?_›~ !ÔÓªääTµSSë.2b³)§ÅßxCù T£n¼žxB¹°¢)òò”Âêw”M˜kêÐ,p|%zÉ ¡cV«òÉéÎ;k?cÇ*WzIñ#„gª9 Ô˜Õ ‹‹aÉˆŽ†k¯­¿ø †ÇW®Þª¸ª´©BB”Bë?`äÈÚ÷Ÿ< '*jìÛ×ô×ñDR ͹Ò'ü|åÒÒW_­}ßôéÊ‚dÁÁ-߯ær¥1öT’RŸÚ hZ:5ž^9Íuß}õ;+³1))Ê>çX‡·Q¢£••ç—/¯eØ´I ^O›¦¼Ï )€„ p•|ÊáÃÊÞ]ëÖÙ÷óƒeË`ÎeÊÚ¹Ê{2É©Oí 4.½{7Lš¤6/¿ u?nÈåûädxòIeÖF-·Ü¢ Ÿ~Z¹P£ºòreßÂ=”÷4½sÓ·sáI\aŸªÍ›aàÀÚŸÜÚ·‡Ÿ~‚ÛnÓ¤[Nã cìéd/0õ©½œû˜Í¦\–~ùåз¯D6›k?——’»Ù¾~ýU¹:«æåëj Rf±÷ìQ.µ¯éôie¯Â#ê¿M¤º·x1\udfÚ0@ÙÉ}à@mú%„hyõÍ»ï*³'×_¯œRªKX˜2ûrø0¬Xƒ©Û߆ôì©\öùçÊ*Ó5ýò ôë§ä‘ô8y)МVù”òreQ±¬}õÄ-·(»0×u.ÝIH}’RŸ ½{•ü_ÇŽÊûÅ¡Cuÿ\·nðÖ[Êú=¯¾ZwÁ¡•ØXeAÅ™3áìV—•,¥ß^¨,Ш§ëÂ¥šÓ"Ÿ’•×\£|¢«Î`€W^Q‚„~~-Þ-ÕHH}’RŸ ŸV6HÍήûñ»µ<>ªœ~rEÊjÑÿ­\¡VÓ™3Ê×_Õªeíe È:@žiÿ~åröädûãAAÊBd*G „.,) ºwoø1ÞÞJ¾ç‰'”SHîèÿƒ©SáèÑÚ÷…„r$ìÜ©¬£á©$¤>É©¯%2@µ/SïÙS¹XâÄ å4’§dýü .N¹úUþ¤šk‰|ʬYÊ•5÷õ¹ï>å*‰ÈHÕ» )É©O2@êk‰ T8W]¥ìÖ¾oÜ?øû«þÒšèÚU9%öõ×е«Eëî´É5@2@žáÿƒqãì¯nðòR–äíú%„pM‹+käôê¥uOZÞñãiôí{™™ lbæ!¼´î€jJN†»î²/~ZµRÖŸâ íú%„p]z^7ÔÇdž··>VG”S`BsjåSŠ‹áæ›íøòòRVqÕ[ñ# õIH}-‘ú!МZù”‡‚?ÿ´?öê«0l˜*/çÒ$¤>É©¯¥2@B$Ôɹ¯>€)SìÅÆ*§¾„BÔ-55•˜˜]ÌË ð8»v)+²Vwá…ðá‡ÚôG!„ë‘HhΙŸ4²³•™ž’’ªc°z5;íeÜŽ>ÍiM2@ê“ p&)€„朕O±ÙàŽ;àÈûãï½çÙ‹:B2@ê“ ú$$œI2@ {™=ž{Îþ؃ÖÞðT!DÝ$$„›Ù¸^xÁþØ€°p¡6ýBáÚd!D¡¹ÔÔTÚ¶mÛäŸ?yn¹Å~›‹ˆXµ ||œÐAÐÜ1ç–™™Ihh(^^ò¶ª³ÙLAAáááM~[™ò¼rÊsËÉË.#;³„ܬRò³Íg—QœS†9§Œò< F£.‚èÚ!Ÿ(¼#½ñŽòÆ;ÒŸ(Œþ2‡àÎä/UhníÚµ<ÐÄ¥WËÊ`üxHO¯:f4²eЩ““:èš3ÆÂ1›6mbÔ¨QDDDhÖ6¬6+«‹ÍâðצüŒÅzöçÎ~oÃFE¢¢âûú¾:ôÊ×rÀ 6‹œôNï;Íè!£ickC¤%’àÒ`Ì9åäf›)È*¥$§œ’œ2̹åXrË!ÏŠ!Ï‚W¾ï|+Þ¥u|öV•’ëoS€©² ªùµfÁäåW+/ F}ì´î¤škÎ?ÌO=Û¶Ù{þy=º™ò0Rü¨/66ÖiÏ•Q”ÁÉü“¤ä¥p2Oùš–žFÞ±<Ì)flglPfÀ`ó‹l& V#F››òÕdóÂhóÂh3Uk•¶Õt¶mªv3b´*÷›¬¦j5b:ûÕh5`´ÏÞoÀ`3`²zc´ú`´rö~ƒòÕ F›“µ¢m¨|ŒÉjÀ`«øÞxö{ì¾7Ø.Š(â8Çë¼ÏçìMm–" –cJŽ•œûÁ€ÁhÀ+«Þ©®Jf™Ô#p[+WÂ[oÙ»úêÚY !\E¹µœÔ‚T¥°©VàœI=CáñBJO–cÈ¢U^¢òÛ•ATN³»Tä­u÷E3Ù¬6ÊÒË(K/£h‘C?c 4UF!CBhý­  2‘ÔlR Í5%Ÿ²?Ü{¯ý±Žaùr嘰' u•YË8vúåÞå¤Ù8§2É;iÁ’æ‹Of‘ùmˆÊ‹"*'Œ¨ì èžM@±Iëÿ·PN9Å\ëD•ã,&( TnEVÊ|˱x›±y•‚±/ ñ±àcÍçXëv膡7¡¹–¡¹U7¯XöÉRhÁR¨Ì2å'äsòí“øvð%j|­'´&dpˆS‹¡3Eg(kSæ¼'taR Í56ŸRP lrZPPuÌÇGÙæBÃø…K“ PýJÊKÈ-Í%·$·Î¯y¥yä–ä’]šGnAÅ96Jó X MPà…±ÈŸr’NpaP çGTv0²ú“9ßz2'âܬFû[†!—ý†D‡¥ ¨z!c£ÜÇŒÍTŒÑX\YÄø•çX–Cpi¡%™„¦Ñ¶ “ˆ¼<"Òóðv`áJ³—?ôïÏš#xwØ02BC+ï * Va–á¹.,òæüBo"ó YNYz–‹SÆ¥ôd)) SHY˜‚_'?¢ÆG5!Š!M~Τ¬$æÿ:Ÿÿþñ_ü"ýœÒOW'ë5@ÖrM·Ü+VØ{çxøamú#\‡ ©©Í9ÊÑœ£$åçxa9…¥åX(Ë3P^hÄPà¡È ï"_üK(õ'°$€À?Kü(ö!°Ø‹ÀboŠŒ (“sþýj«²Â!=J¹ØÀ`Ãf°aÀŠÍ`ƒ °b0ذaÁ€rÌh;û=VŒX ›#V 6 ,•1Ù,låmÊ±Š¶Éªó²Y0ZË1Ù,xYË1ZË0Ú,•}áìÍvöµ+ŸmW>ë9_÷?MÞååJ“—GDn.yy„Ö?nÈó…\?È­ãkŽ_Ý÷zÃ5Épï.èž©<—ÅhäçK.aõˆ¬>œ“‘‘çüÿf‡„psTã‚#èP`RNƒe”aN7W~_qj¬úñò¬rlVÇÿ‰öëâGë ­‰šEpÇfÉvÞż­óXµoV›r)mÈÏ!änôüE=¥j€@®çí·á±ÇìÝz+|ú©6ý-Ëj³rºà4GsŽòwÆq’Že‘~¢˜âT†t/²ƒˆÈ !2'ˆˆl_"²Œç·Ì©Šæ(÷‚ÌÈŒ´QRJi`6ï¼LYø[Ó);ETÁ1ÚfgÒ6+‹¶&>~~`2)ç|›úµ9?[ý«Á ¬Caµ*K²W|_WÛÑcõ=Æd‚ ƒÐІoaaD±¥¤2L^qj²zÀ<%/…´Â´Ê :ƒ .; SvÁMûÁ÷ìï’Í``ÇE±zÄÖŒÁávíúúܫΠIDATÄÍQQÜI¯ÀÀk³Ú(ÏRfŠ‹8óù2¿Êth&É¿›?Q”ÓdA1AµîÿñÈÌÝ2—ï_ë>)€„@-ÄÑ|Êöí0b„ré{…Þ½aÇ8Çûˆî¹KÈb³p<ï»’|(ôE”œ²BºÿL?B³üˆÈö'2Ë›VÙÚÎÈÔ”GbÂ>Ïcö¬+ùg «o&Sþ¶3„”"¢ø8móŽÓ.3ƒ°êçuÛ´ .€îÝ•[Å÷\ Û휱P}Ê­åœ.8]Y%e%ñùÞÏù#õÊDŽÔߡWºýÏÿÙ­[e1´·K‡^³G@@e1ÔßÁÿ§Öb+™ë39³ò Y_ga):÷AÀ…DMˆ"r|$ßyÇÜ-sI8•PïãC%ç—‡úãΤj€@-cñâÅç̧¤§C¿~’Ru,8v=Uî pdŒ[BzFH!ùP*éÇ (:eƘfÀ?Ó‹L"2½‰È2âí¢Ìr“¥þVÌ>eX|Ê)÷.S) …[éÔSÁ…MÁÚçÇËRÏ?R­[×]ätï®Û"§!§N"!!믿¾Å^soú^–íYÆò¿–s<·ê²û¡'”BhÂ^¨ñûz cGÖŒÁšüƒ„=zÎ~~Œ‹Œäæ¨(††„`4œ;Ùl)´¹îl1´! kIí¬šŽEcsïÍl꽉cQÇìÍ%Ü}?ÿÿ/RO{þVº.€¶mÛÆ×_Mjj*Æ cìØ±DV;§+k°Záškà‡ì¯\ &hÓ'Q›Õbcÿ® öì8Aæ±<ŠO•bL³áŸa$4ÓDD† ?Ç–Kq~ߌPâg¡ÌÏJ™OåÞfl^f¬^% Å ÊÕ?¾Våêÿò\ÌÙ•åR’EHI&þå¹17þÅ£¢ì ›ê…NHÓC«¢eÙ°ñó±ŸY¶g«ö­"§D™! -[ÿRN‘õ=]û玷n]Y míÓ«…Mn<[ ÃË‘b(ßBÆ—¤–NÖ·YXKÏ] i}„ͽ7SpuSb§0ú‚ÑºÚ L×ÐСC2d=zô`ãÆüôÓO?~Ÿ³û'Häž{NÙ贺Ǘ}¾´d³Øøë÷4~û1‰œ„B÷éxÄßÒ–]œ¤(ÐJQP åþEL9ø2 *O£Uñ Úæ%²è8ÞäbB¥K±¡U+W¾vêT»È©vÕð ¥–RÖ\Dz=ËXh=f‹R÷?¥B·þÁuüÊ¥µjÅÚáÃY=b›ûö¥ÌtîåZyy162’›"#é@ÉD°ÉDÉTï,ѩӧXýæjÊÖ–qqÒÅxYÎ}ÁwP/_¢øü¿ù¬ÊÊ:çϸ;]@f³¹²ØèÞ½;ÿþ÷¿™8q" PKi(Ÿòõ×0v¬’}¬0t(lÞ Þ².œÃš“²YlìI8ÍŽIäí*$l¯‘ŽG|T-vŠl䇘1cõÎÅÇM€õ a¥§ˆ,ûLŠŸÆrt ›ÅÆ®ÇùíÇdòwÓjŸWe±s!þÍêG‰?ä…–SXŒÕ·£)_[:!e©D§]ÞÚ寻¨Y(×Ë ‚‚”¼LÅ×êß)30õ0ááNËÚ¸Â^`ž.##£Å3@çÒʯ÷÷¿ŸûûßÏÑœ£|ú×§,Û³Œ¥>‰,í}Ò”Bèö=Ъ¸ÚÏåçsçwßqçwßQàïφAƒX}ã¬ïÓ‡|Vsµ… … v'«Lí!ª}e3ÿ<ØÐ6Ü!y0âgµ bv+[ŽÔ%³[¿&…»‘5sÏZ¼x1ÞÞÞÜpà vÇãã㉋‹³»U'íæ·«ÿÃ\qi©²ØavvÕãM&=:Ž\«ÿîЮãê÷Û,6¦Üý‹þý=ónZÇ’‹¾áNßÉä=BÏçŒ XÈ–Äÿg7ÓO¼Ýó×l¿Ï>yì›Ì±¾;y·ÍL,.&¢ýtúúDzÝ|%Êoá.ëãL6ÍáHÎ&¶ßÊ?£Ï0èŠ(>ì„÷ô'”sžo½EÜ 7(+\~ó lÙBÜÀáÃpæ 7kdgÃñã°oq×^«„ÅÖ®…O>!®M˜;ž}¦L!îï¿áòË¡o_èÜ™¸ œ6Þ±±±¼ýöÛN{>i×n¿ÿþûvÅÖý©ÙŽ_ϬÌbÿÃûI¸/ÁIƒ9s~»ÚOƒK.‚Ÿ;Wûù³_ƒŠ‹¿y3=§N%}ôh¾|ùe&>ŒÿGaÿñÍjç­‰gݘ¶n^  ŽçÏKàWÃ6â‰g*S‰'žK&z ëS`þ÷¿ÿ1iÒ$¾ýö[Xy¼â—»æ/¹Pß}÷Á’%öÇfφ™3µé»³Ylüúk"»6§ðR"}ètØ_sÓOceDÚHoŸÁÿaÆD:µ:EïÞç:`¸2³R׌Œ—®'…Yl~8üËö,ã‹ý_PXVH eÅ»þ„¨ú×p¤ÜdbóM7±fâD¶wè@®ÅBÙL®ÅBi#O™5$"Fþ—m†è¿aù¥[X²ó9§=¿«Ò}´jÕ*{ì1Ö¬YÃàÁƒíeÔ̧ÄÇÃäÉö3¾üRYoMœ[iQ9ëþ·‹ƒOšàEøþRÚš›ž›ÈŒ°q¦C¤F""OÓ«#ƒÿýû+á_“ ú\1Ô…e…¬M\˲=Ëø>ù{LånHTN‘]yøìÙõ1™ Úr £‘ üýɰÿZßñ€ ɧ U+òƒ‚(ðó#ßd¢ÀfÃb³™í>ZÞ¯Þj 3žA×Ð믿λï¾Ë—_~I¯^½jÝ/P˨¾FÍŸÂ!P\í\y×®ðûïJlCÔ-+½„ _þÅÉé„ï2Ò%ÙÏnõã/ù’ëq,7‘iíó±¦f8@çÈ4úõîˆÿ€Ápé¥RìÔcÕªU’R™ë©%­0¯`Ùže$œJ k¶2+4y7´ËwÒ‹tî }úØßzô¨7DYlµrøôi.>œ´#GœÔ ×¥Û(77—°°0¢££íÞ°:uêÄÇ HÔÒrs•_“’ªŽùùÁÖ­Ê"ˆ¢Ê¡#yüòÕ²~Ì ÍŸÞœwÌ«áOõÈ ·‘Ö¡k` aƃtL£t'|ûPfv:w>÷“!š%1#‘e{–ñé_Ÿ’’u”ë*³B£“ÀäÀu9åa!x]c_èDG7i)=­¤Û¹ÚÀÀ@6mÚTëx@@€½“&Ù? ìý¥÷â§Üfcן™$¬O¦ds6íÿö¡íi#çç7⪬ìpHk_€%èa¦ƒt8ÃØèóðî)ô¿:k¿R´zÔ3²'/_þ2ÿ¾üßl=¾•e{–qgßÏ HËâî?àž]Ð)J½`_ìmmÀë’¾ ùç}t>¯êW†‡é¶òòòâ²Ë.ÓºåÇÇ·eíZûã“&Á½÷jÒ%Me›Ëعí 6Ǻ5Îû|i•ÊIZ?‡Ÿ'µ…œ¶éøì'$ôcœwß¾pé5Ðù>µº¯[’RŸ»g€ÎÅ€á†3¼ÓpÞºö-ÖZϲ=Ëèy`m3KɈ `ò¥÷2mÈ4:…Ê©èæ’¿T¡¹×^[Ë›oÚÏ>\r ¼û®Fja‡óŠHø)” 'ðþÍÌùû½,„‹ðuè9l8u^)E' öÛO.ÙŒy)†aà÷¿ÿ>Þ.°˜'“u€ÔçŠë©ÅÇäÃ=oäÆž7’S’Ã÷‡¿çò®—á/¿_Î¢Û #$¤¾Ó§•%YÒÒªŽ…†BB‚²“€§È·X8–]ÄñÃùd$’w(—¹„ì1Ðí ŸF.h\î© )9BHÈzõ2sá5×`6\Y-R!š@2@B¨¬¨H¹²kÆ ûâ”ËàÝ©ø±gÌfަpêpÙGŠÈ=R€ùp>'-Ÿ1yÆ@XU׎¯ãQìo#³}"$â½qÙu7á5à>»í„B8F ¡:› „íÛaÇåë_AyåeÚ©€²Ð3ÏÀ7jÕÓº•Ùl¤”pìp>g’ ÈÎȧì;’Ë×ïýIÈWe\¸ßÄ 4&Ÿc5A~ˆ™Ò BlÞ™øq†Ð²"Š曆Wg/ŒÝ;ãwQ4!õÅpA?8ÿ|e¯7  õIH}’Î$ ¸k¨zaSWSñ}i©s^¯sçªBgÐ e늖¨ Ÿ.àïÿ‰ÏÿJèµÛtν°Ê¼!½CÞ^éXOVzŒöùÉ´6ÃÖÅc·óñëñ‚îЭ›r-~§NÊ.ÌB¡’.);»îÙšêÇNŸnZ&ÇQÁÁ0`€ýìN›6ê½^M©™Å¬Zºëêzíò¦o94”籘àt—,¢ ›š¹šÀK‡bìyÑÙçjåk»v-Ö!„®A P^GŽÀ±cõ8j6u1¡woûb§W/å¸3+Ÿ’oæ³vS´2›Þ;}‰6x×ûx›NuÎ#Äg ƒò¿à²«.ÇtS,\öºr~N‡$¤>É©O2@™ä/µ…ØlJ!sð ýíÐ!8|X¹œ¼¥……)—ŸWÜÚµ«ú¾CèÓ‚‚ÔïG]ù”Â’r>[þ'™ËÎÐk‡?=Šà\ûbîXˆ_àú—}Í?n‰×Íãað<å’3“ ú$¤>É g’ Pš’ÊÌTŠšš…NRªÓÏš‚ƒë/lªßüý[¦?Ž2[¬¬^¹‡“ñ)ôØDpþ¹&­} „íâbÓwt7ŸØÿS*7!„& Ѡª"§f±“•¥ÞëÔ.bê*nZbÖÆY,V_®ÝCÒÒ£\°-„vÙÚÑð@Fë2ÌQÑ#ðgß<¿ñ·@×™-Ôc!„ž@  s8x° Ø;'O:ÿuÚ·‡®].n\hY™FËÌ+e÷Îc$m9ôÿÛ»ó¨¦Îôàß„„Ä!¬•E‚¨ˆŠÛ¸Œm]PqêÆOkÙªý9ŽGO—§ckmG…ã~\ª£cqªÌµ–¢NUt¬cM¥bª­Z)²(( @ BBÈûûƒzFÍ„4Éó9‡s¸ï½7÷É{ÃåɽÏ}/js5” áRÑ ÞÁjUƒö¯é«Üõ¨÷Í…Üý[ÌŒ Iäà¹wz(zëG5@æG5@æG5@Ä”è/µ‡½ŠC‡LóZ-ãá·ü<ú}ÀÀÉÉ4Û°$΀[7Ê‘­È†òæCŠùp*ï÷ Ü«øp`À@80~³ÇqÿƒÖ×ôÕ® U}nã9Ÿ«˜öê¸Ì{ pý}½ÛB5@æG5@æG5@Ä”¨¨ HLLèÒ:ÎÎÆ‰ÍãÉŽM|ia@i^5~<—âÊ¡+dVˆáZ!†§R‡æg{ùz'†ò¾Åp ¸Éÿ; îs£­f°AB±vTDÚ%µ ÓV¢c CÊ´ÔWh“™‡‚Kw¡.Ð_.‚³²<*á¨ÄÀ4ßt$ ÷û–¡W¿Lý](zÏžšäµ !„¶PÔþý 0cF£39r¹éÇÂéŒFƒƒuj-ªkêQS«A½ªõ*54* t5ÐÕ6¡Y­Gs}3Xkx åÁAˇƒ–¾ŽNŽ¡ŽG­„:µ<ˆ´<ð{ü…/LóÀÎ&!PåÕµ¬MRxn*¸úé4D ß¡0sJ,Ý®nFTd~Td~TDL‰þR;°`Á'Ý~P£×£LS‡’2ÊÊâaI5Ôejh•Zj `5<ðëùÔ !Ô:üœ”8@¨u€£ŽG¢Æ–ÄD܈´0zäƒøçŸ½~þ± ¨ô2 Æ½W˜k5ÄÏÕÁw°C^Œ)£&ÂAÒú¯¤¤$O¥äÇœ¨Èü¨Èü¨ˆ˜Õµ#!!M|>~ÿþûPÖ4 ´¨•%JÔܯ…F©®ZC-¨ã_/€ AGbâHøpQ’tøœ*k¢rgPyi¡–=„ÞUw-Üû9`à8 ;.ît¦B¬ÕΘ'â§µßB $ ;™l‰^4H€Jo=jÜjÑ(«󬆓¼£<0úùÐÇox ³6„B¬%@=´t­é…€ÖÑ€&GôŽ :¡za3š…ÍÐ ›`èa6¡YØ&l6 õ€¨7ƒ/j†C/€'„Î|8Jp” ÑK&†Ä]7)|=žC¸ßH„³¿ªO1?êcó£ ó£ bJô—ÚCÅhÅzèÄzèE:4;jaiÁDZ8ˆup4AЋA(àè̃ØYˆ^²^pqÃÙÓ/Ä^2ˆÜÜÁ“8µ äðô'¡[ ªO1?êcó£ ó£ bJTÔŽ„„„%†š€FÒ ­¸ :‘z‘ÇF4‹´àýœÀšÑKʃ“»2/ ¼ýdp ôK__ð<ÜZ ¢;!„üBQ á|ÿ»Ï°ø½·á*h9ëB!„«gÑl¬Km@oÈ ¢äÇŒìᛆ¥Q›_UUôz½¥Ã°i:*s>qšØJ€ˆÅ}ñÅ–ÁæQ›ŸB¡@MM¥Ã°i•••¸pႥà 6‚j€ÚñhÄî„H!„X{ª¢3@„B±;”‹³‡o–F}l~Td~TDLÉî û÷ï#==gÏž…F£±t8v)%%ÅÒ!Ø<êcó;~ü8ªªª,†M{ðàNŸ>mé0ˆ°ëÛà333…ððpC£Ñ૯¾‚T*µthv¥¡¡ÁÒ!Ø<êcóÓjµ ’JóbŒA§ÓY: b#ìú P\\pðàAœ?"‘´H,*•ê™þ°»r‰£3˶·ÌÓæµÕþd[ss3”Je‡Û7½^ÿLßл² V«Û]ÆT}üðáChµÚN¿¶¹=˶»º:Ú–Z­~jò÷´}TWW‡ºº:£¶¶>·ÖÚÇ]]ß’Ç‹Îü™K[Ÿƒ®èJwæØb®>îèµm™Ý&@.\à†Tçñx˜5kΜ9c‘xŽ?Žû÷ïw{ý¤¤$“.ÛÞ2O›×Vû“mjµºU’ù,™®¨¬¬Dzzz·×ïÊ>ºqã²²²Ú]ÆT}|úôi¶»\Oõq[Û¶•••…7n´9ïiûèÊ•+¸råŠQ[[ŸÛ'·]__ßc5@ÏÒÇ]]ß’Ç‹'÷QSSêëë;ŒÇÚútEWú¸3ÇsõqG¯mËìö6ø¢¢"ôíÛZ­ŽŽŽ€üãØ»w/.]º åö÷Ï>û !!!f§¬¬ 2™ b±¸[ë!00Ðd˶·ÌÓæµÕþd›^¯GYYüýý¹¶k×®aĈŠýYèt:TVVÂ××·[ëweÕÖÖB¯×·ûÐFSõqEEœ!‘Hžº\OõñÓb쬮¶¥R© Ú¼¬ý´}ôðaËe2×ÖÖçöÉmߺu }úô““ùM}–>îêú–<^<¹jkkQVV†àààNÅþ,ÚútEWú¸3Çsõñ“mZ­?üðƒ]œ²Û ÆÆF€Ãcupp0: Ûcÿ4!„_‚î~·6v›=zb³Z­æ2üêêjx{{sË„„„ôÈÙB!„ô,»M€<==€K—.aúô逫W¯âW¿úU§Ö///Gbb"îß¿Áƒ#!!»”FLK¥Raë֭ذaƒ¥C±I'OžÄ‰'¸éøøx£K=Ä4233qðàAüío³t86çܹsøüóϹébùòåŒÈ6={ÇŽƒ‹‹ Þ|óMôéÓÇÒ!u›ÝÖÀ®]»°k×.ÄÅÅáþýûغu+®\¹‚¾}ûv¸nII ”J%†Џ¸8ôïßK—.í¨í‹V«ÅÂ… ñã?â§Ÿ~²t86)11˜4iÀ××—’yS(ظq#¶oߎ   £z-bµµµÜ ‰999HOOGrr²…£²-ÕÕÕˆŠŠÂ™3g 6àСC–«Ûìö.0X¶lÖ­[‡ÌÌL¨T*Œ;B¡°Õr{öìÁ˜1c0räH¬[·Œ1øûûcäÈ‘¸|ù2ŠŠŠ¸äécˆŽŽnón‡Ý»ws}¼~ýzn<•eË–aåÊ•46S\¼x+V¬hÕ^UU…×^{ Äo~óü÷¿ÿåæ•”” ¨¨ˆ’ŸNÊÏÏÇoûÛVí xã70hÐ Lž<™´oçÎˆŽŽÆ÷ßÒÒÒž×j-Z´yyy­Ú“““1nÜ8 6 ñññ0 J¥ D`` öíÛ‡øøx Dl}6lØ€ŒŒŒVí_ý5¦NŠ,^¼µµµ‰D¨ªªÂW_}…êêêN,ø%³ë¢££Š‹/âôéÓ­Fƒ>|ø06oÞŒ¤¤$¤¦¦"-- ;wî =zB¡Ð¨˜š´¶mÛ6¼øâ‹HOOoÕÇiiiغu+öîÝ‹””|úé§Ø½{7Ö®]‹ˆˆŒ5ÊBQ[—’’,]ºsçÎÅõë×[ÍŒŒ„««+¾üòKÄÆÆbÚ´i¨®®ÆäÉ“áêêŠ3gÎ`„ 4Ð\;Ôj5Þzë-L:ß}÷]«ù¯¿þ:T*Nž<‰·ß~QQQÈÏÏÇÝ»w¡×ëáââ‚… ÒÙÌìÙ³aaa8pà@«!NŸ>U«VaÇŽHKKÙ3gŒ.ýõ×ðõõEPPPO‡mU222ðÊ+¯à/ùK«/¥¥¥¥ˆˆˆÀ›o¾‰S§NA§ÓaáÂ…‹Å CFF–.]Š3fX(za„]½z•) &Xnn®Ñ¼Y³f±íÛ·sÓ©©©lĈFËœ8q‚½óÎ;=«µºrå S(ŒÏ糂‚£yÓ§Og;vìà¦8ÀFÅ–,YÂbbbXLL óðð`ï¿ÿ~O‡mUjkk™B¡`+V¬`áááFó ™P(djµšk{þùçÙÇl´\dd$»yófÄk´Z-S(lóæÍ,$$Äh^]]‹ÅFŸï¨¨(¶fÍ6qâDÖÐÐÀcl×®],99¹Gã¶6×®]c …‚I¥RvõêU£y111,11‘›þâ‹/XÿþýcŒ ÆÊËË{4^ktûöm¦P(ØèÑ£ÙÞ½{æmÙ²…EDDpÓ%%%L °cÇŽ±eË–1Æ«ªªbÆ ëјMÍn‹ ÷¨ð™Ç㵚———‡?þñÜôСC‘››‹¬¬,èõzÈår¤§§#<<¼ÇâµFÎâ´ÕÇùùùx÷Ýw¹éÐÐPܺuËh²qãÆaÓ¦MæÔй¸¸ ,, ¸víšÑ¼üü|Èår8;;sm¡¡¡ÈÍÍÅ‘#G0xð`444àÎ;VZÛœÆ £ñ¸{÷îúõëǵ…††"''3gÎÄÎ;‹ãÇ#55µÇb¶FÇ­ÿEåçç#**Š› ÅíÛ·¡×ë‘––†I“&ÝÍKÚ„   6Ç*ËËËChh(7íççgggˆD"\¾|¥¥¥(((0ZÆQÔ¥RiT°èââFWWW>|x饗0"3Å# ¯IDATþ| FiÝ***ŒúX*•¢¾¾½zõ,Y²ÄRáÙ¥RÉõå#R©J¥"‘{öìD"AZZèvÓ“Ç  åx¡T*ñî»ïâ£>BRR¶oߎçž{ÎBQZ¿¶ŽŒ1TVVB«Õ}™"Ý£T*[ÝÝ%•JÑÔÔ„={ö )) }úôÁîÝ»-¡iPÔ‰DbT¡Ñh 0dÈ«Ï~)œœœZõ±P(4Œ‹ gód-»R©³gÏÆìÙ³-™íxòX´|–¥R)–-[f¡ÈlK[Ç  åôâÅ‹-–Miïx1räHŒ9ÒB‘™–ÝAwÄÇÇÅÅÅÜô½{÷àããÓæ¥Ò=mõ±¯¯/õ± ùøøàÁƒhnnæÚŠ‹‹áççgÁ¨l‹êëëQ]]͵ݻwúØÄÚ:^Èd2:siB>>>())á¦}®mm|0J€:…þóŸ0 €””DFFZ8*ÛB}l~£G†»»;÷ Ñ»wï"33óæÍ³pd¶ÃÏÏãÆãÆžQ©TÈÈȠϲ‰EEE!%%MMMèxa‘‘‘ÈÈÈ@yy9à“O>ÁСCmïÎ:KWaÿÌ™3‡Éår€ùùù±áÇsóêëëYDDóòòbþþþlâĉ¬²²Ò‚ÑZ§ˆˆ£>9r$7¯®®ŽÍš5‹ëãI“&±ªª* Fk®_¿Îär9óðð`b±˜Éår¶mÛ6nþ7ß|ü½½Ypp0suue~ø¡£µNL.—3ooo& ™\.g+V¬àæß¼y“²~ýú1777¶råJ Fk½æÏŸÏär9ãóùÌÇLJ»Ë‹1ÆYtt4óðð`lìØ±ìÁƒŒÖ:mܸ‘Éår&‹™‡‡“Ëå,''‡›¿fÍæâ₃ƒY@@@«»ñl]M!„ûD—À!„bw("„BˆÝ¡ˆB!v‡ B!„ØJ€!&QTTÄ e`)÷îÝCee¥Ec „XJ€±A÷îÝÃôéÓñ‡?üÁ¨}çÎ8|ø°É·§×ëÑ·o_£A{ÒÝ»w1fÌLž<™F\&„t %@„Ø ºº:\¹rGŹsç¸öììlZ02óøüóÏ1vìXܾ}‡²t8„+@ !6J$aõêÕøóŸÿÜæüëׯãã?6j‹ÇÇ©©©8þ<6mÚ„˜˜ìÞ½Œ1$''cÁ‚HJJB}}½Ñú·nÝŸþô'ÌŸ?ÿþ÷¿æeffbéÒ¥ˆ‰‰Áþýûñh²Ë—/ãÀ¸{÷.¶oߎ´´´6ã­©©A||<"##±jÕ*.ά¬,ìß¿………øàƒpãÆVëþôÓOøè£PVV†¿þõ¯ÜhÍ%%%X¾|9¢¢¢°iÓ&hµZÀ¥K—°ÿ~nýãÇãìÙ³ÜôŽ;pç΀B¡À믿Ž `íÚµÜ蹄_6J€±aK–,J¥ÂÑ£G[ÍËÉÉÁ§Ÿ~jÔ¶uëVÔÔÔŽ9‚ÈÈHh4Ìœ9[·nEHH.^¼ˆéÓ§ãàÁƒØ¶m›ÑúË—/Gpp0d2æÍ›‡Ë—/Nž<‰Å‹côèÑˆŠŠÂž={°eË-‰ØªU«0wî\ܹs‡Ûþãôz=&L˜€ììlÄÆÆ"//ãÇGSSÜÜÜ ‘HàííH¥ÒVëçååaõêÕ˜>}:rrrPSS•J…±cÇB­V#** _~ù%÷h‡¸¸8ný¸¸8¬]» Õj±jÕ*¸¹¹á›o¾Á¼yó0a¼úê«(//Çwß}×éýC±zcðððÀ;wP^^Ž‚‚ <nnnÈË˃\.ÇܹsáììŒÙ³gw¹ !–Ag€±qÑÑÑ‹Å8pà@—×uppà~wvvŸÿÿ‡ —Vw\ñx<î÷1cÆ ´´@Ë%¨U«V!$$!!!X±bœœœ¸e=<<žšü@~~>ÆÏmŸÇãa„ ÈÏÏïô{quuå’ŸG¯9iÒ$£† ‚üü|888 ,, .\ÀáÇ—_~GEVV¦L™˜3g¼¼¼àããƒ)S¦ %%ÍÍÍŽ‰b9tˆÇãñ°qãF,]ºaaa\»@ €N§ëÒë´7ý¤ÜÜ\x{{d2RRRðâ‹/v>ðÇÈd²VÅÛ………ïÖë=zÍGu<ÐÜÜŒââbÈd2-g„.\¸€7nàĉ¸}û6Ö¬Yggg¼ñÆOOOüç?ÿAnn.¾ýö[ÄÇǃÏçãµ×^ëv\„žAg€±/½ôúõëgT 4~üxܺu UUU0 HKKCSSÓ3mçQQtvv6N:…ˆˆ@DDÖ¯_ÏÍohhÀ‘#G:ýºáááÈÍÍ…B¡ÐRP““ƒiÓ¦u;Ö3f@¡PàæÍ›`Œ!55øõ¯ÍmóرcH$èÝ»7^xáàâÅ‹˜0aàܹs(,,ÄÀ±hÑ" 6¬KI%!Är("ÄNlÚ´Éè®­Þ½{#66AAApwwÇÙ³g.yuÇ´iÓ0`À >‹-âꎶlÙwwwøûû#88žžžFwUu¤ÿþØ·oæÌ™Ìš5 ÿûßÜíX§NŠ÷Þ{£F‚¿¿?>øà:tˆ;4hÐ ˆD"DFFh9ãõòË/#88˜»|÷àÁŒ1 À Aƒàää„… v;&BHÏá±G÷¢B!„Ø :D!„»C !„Bì%@„B±;”B!ÄîPD!„»C !„Bì%@„B±;”B!ÄîPD!„»C !„Bì%@„B±;”B!ÄîPD!„»C !„BìÎÿþ˜8¦7IEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/compressed-select-cache-zlib.svg000066400000000000000000000777301231437614300273570ustar00rootroot00000000000000 Selecting with small (16 bytes) record size (file in cache) 10 3 10 4 10 5 10 6 10 7 10 8 Number of rows 0 2 4 6 8 10 12 14 16 MRows/s No compression zlib lvl1 zlib lvl3 zlib lvl6 zlib lvl9 PyTables-v.3.1.1/doc/source/usersguide/images/compressed-select-cache.png000066400000000000000000001264211231437614300263760ustar00rootroot00000000000000‰PNG  IHDR@°AàÚ²sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwXÇÿð÷ÇÑ‹ UDDQ@ª(M»(v£X£•h4Æ€±DÍϯ%Ø"öÞ‰ÑXˆJì¢ (MêѤ3¿?.¬,wÀQOa^Ïã7»;;··w÷¹™ÏÎr!EQEQ-WÚ  (Š¢(Šjj4¢(Š¢(ªÅ¡EQEQ- €(Š¢(ŠjqhDQEQT‹C Š¢(Š¢ZQEQÕâЈ¢(Š¢¨‡@EQEµ84¢(Š¢(ªÅ¡EQEQ- €(Š¢(ŠjqhDQEQT‹C Š¢(Š¢ZQEQÕâЈ¢(Š¢¨‡@EQEµ84¢(Š¢(ªÅ¡EQEQ- €(Š¢(ŠjqhDQEQT‹C Š¢(Š¢ZQEQÕâЈ¢(Š¢¨‡@ÍPzz:ÒÒҤ݌ÏVNN¤Ý ‰ &&†yœ““ƒ÷ïßK´mqq1bbb““ÓH­û$33ÉÉɾŸÚˆ‹‹“v3 !111(,,”vSjTÝù÷òåK$&&Š”ggg#))©)š'U)))ÈÈȨWÉÉÉb!%9}f^½zoooôèÑݺuƒ‡‡V­Z…wïÞI\ÇÒ¥K1gΜiO~~>Ž?Ž>°ÊW¬Xo¾ù¦AöÑÆ72ƒ‚‚X8pŽŽŽMܲº¹}û6Ú·o²²2À¡C‡àìì,ѶsçÎ…‡‡Š‹‹E–]»v ß~ûm•Û¦¦¦âÇÄ€`jjŠ]»vU»¯5kÖ`ôèѵ«*OŸ>EHHH½ê¨,>>Ø·o_ƒÖ+-¹¹¹hß¾=þý÷ß:m/àêêŠk×®5pËDùøø`àÀ¬`íäÉ“èØ±#lmmÑ·o_œ;w®®®(((lß¾õÚïÉ“'áêêŠÒÒÒzÕÓ˜&Mš„+VÔ«Ž'OžÀØØׯ_o Vµ<4úŒÜ¾}VVVHLLÄ”)S0þ|˜ššbçÎØ¾}»TÚ”žžŽqãÆ!>>žUn``CCC©´©::t€®®.óxæÌ™¸}û¶[$—/_ÆÁƒ ¦üßÿÅæÍ›áííÐÐP±Û†……ÁÚÚ>ÄðáñbÅ ´oß¾ÑÛ|ôèQ¬ZµªAë´²²Âï¿ÿ޹s犜Ã-ǃ™™ÔÔÔu?×®]Þ={„Ö­[3åþþþøæ›o““ƒG¡U«V033—Ûp_E033‡Ãi°:?G ÀÊ•+1qâDäååI»9_$ž´@}ò믿ÂÜÜÁÁÁ¬7ïÚµkÉZ·°°OŸ>…@ @·nÝ```PmÝeeeGLL LLL`ff&²Nrr2ÂÃ᪪ ÈÉÉ1Ã-‰‰‰PRRŸÏ‡¾¾>F’’Ö¶rrrPVVÆ£G //îÝ»‹ì£  >D^^ììì——MMM(**ŠmwBBTUU¡ªªÊlŸ™™ ===f¸¸8hjjBII sçÎ…¼¼<Ó¦ÒÒR¦ÈÈȈUTTbccáââ¯æ·Cff&ž?޲²2˜™™1ÁÖÇ‘––CCC¼}û‘‘‘°²²‚ŽŽ <<½zõb}Ø#""‰‰‰àñxèܹ3ë¹ÕÕ?þˆ… ¢cÇŽ¬ò°°0žžžÄÞÞ¾ÆõþùçÒ¾}{¢¡¡ALMM Ç#[·ne–OŸ>Œ5ŠyK‰‚‚éÞ½;áp8dÆŒÌò²²2²hÑ"Âår‰¶¶6QTT$äõë×DWW— ***DMMôìÙ“BÈœ9sÈСC™:zõêEÜÝ݉‰‰ QSS#<ØÙÙ‘ÜÜ\fÿý—bbbBTTT—Ë%çΫò¹º¸¸yóæ1ׯ_OTTTH~~>!„””€„……BéÙ³'Y±b!„‡C‰ššQSS#ÅÅÅä·ß~#šššdÔ¨Q„Ï碫«K®]»Víq $ŠŠŠÄØØ˜´iÓ†p8²eËB!Ç'²²²dòäÉ„ÏçeeeÂçóÉÁƒIß¾}‰œœQTT$FFFäñãÇL3gÎ$²²²ÄÄÄ„GÖ®]Ë,¿zõ*@JKK !„lÛ¶WÛÎLJCÞ½{Wå:Ë—/'666"å§OŸ&rrräÇÕî£2___Ò¡CbmmM —Ë%äÁƒ„B6oÞLZµjE>~üÈÚ®K—.ÄÏÏ>|˜ÈÉÉǼVû÷ï'„rãÆ ¢¯¯O´µµ‰±±1‘——g–BȦM›ˆ¼¼<111!ºººDFF†µœBNœ8Á¼þU‰'ÈŽ;H=âééI!ä?þ šššÄÀÀ€´mÛ–(++“   fÛò÷Ò®];Âår‰››!„‚‚2a€dôèÑ$//Ù^VV–¬Y³†ôéÓ‡p8bmmM!äæÍ›DWW—9GTUU r÷îÝ*ŸÇ³gψ¡¡!ÑÔÔ$:u"222¤ÿþ„BJJJæ\9r$s¼ÕÔÔˆ¢¢"«þ§OŸ333¢¦¦FºtéB¸\.Y³fM•û&„çÏŸ$**Š)ËÊÊ"ÊÊÊQUU%jjjäèÑ£äÔ©SDYY™YoíÚµÄÊÊŠyœ™™I†Jx<±²²"222dàÀ¤¨¨¨Êý>|˜hjj2¿ÿþ{bbbBÜÝ݉‚‚QPP FFFäÑ£GÕ>÷ïßggg€´mÛ–p¹\âèèH¾ObffFx<4h«]7oÞ$DVV–ù¼øé§Ÿ!„ôë×8;;333¢ªªJx<±¶¶&Ìö¯^½"ݺu#ÊÊʤk×®„Ãá~øA¤ëׯ'æææÕ>J<}F.\¸@+++²aÃò矲ބ’““C ÈÊ•+™7ÛéÓ§ ŸÏ'±±±„ÑÈÝÝxyy‘ììlB!‘‘‘„Çã‘àà`B!»ví"òòòäìÙ³¤¤¤„”””={öׯ_3_ /^¼`µC\djjJþþûoRVVF’““‰––9sæ !„üü|bbbBLbbb!„¤¥¥>Ÿ_môóÏ?“îÝ»3íì숒’óåD444˜¡bD!ZZZäàÁƒ¬:ûí7ÂçóÉÖ­[I~~>)--%cÆŒ!cÆŒ©²DNNŽh†‡‡“«W¯B„‡Ã!6l`ê2d‘““#$??ŸGGG²lÙ2¦Ž—/_²‚#GŽ‡Ã¼îu €Ö¯_Olmm«]§ªhùòåÄÈȈŒ7ŽèèèEEEÒ»worÿþýjëóõõ%šššäÌ™3¤¸¸˜¼~ýš8991sùàÞ½{™mnܸAdeeIRR!„%K–0AC9@@455ÉæÍ›III !Dˆ*++@@ÒÓÓ —Ëe½Æ?&·nÝbÕóñãGÂãñÈ;wª|åçz‡È¡C‡ÈëׯItt4‰‰‰!ŠŠŠäàÁƒ¤´´””••‘õë×---&www'äÙ³g„BRSSÉ/¿üB!dݺuD[[›„††Byøð!iÓ¦ ñ÷÷gö-++K´µµÉöíÛÉË—/É«W¯HNNÑÓÓ#^^^$>>žBHTTT‡‡ð" "ÊO娢¢¢"Ò³gOæ³£°°˜šš___æy^¿~p¹\òòåË*÷¿qãFVSîñãÇINNfÊj €¦OŸNzõêERSS !Â<:::$  Êý‹ €TUUÉ¡C‡HQQ),,$ÎÎÎdÑ¢EUÖA!ƒ "=zô`~°¤§§3Áßû÷ïI\\³î»w²29qâ!„ŒŒ ¢¥¥E¦M›Æœß/^¼`Îÿ~ýúcccrãÆ RVVFÒÒÒˆ¾¾>9|ø0!Dø:uïÞÌ™3‡ ”ÿùç€<|øÕÎÈÈH€$$$Tû|(Q4è32xð`<|øFFFX¹r%ú÷ïÌŸ?påÊdffbÈ!xûö-"""`ff>Ÿû÷ï‹Ôùöí[ܸqcÇŽERR"""PVV†îÝ»ãï¿ÿìÞ½nnn1bddd ##ƒiÓ¦±º{%1dÈôîÝ:::prrŸþ xøð!¢¢¢°dÉ&w¨uëÖ5vC÷éÓÏŸ?Gvv6‡E‹áäÉ“€ÐÐP¸¹¹Õ:‡@[[óçχ¼¼<¸\.FŒÁ´U.— <{öŒ¹z£sçÎèׯ³ŸÏÇâÅ‹™:ûöí ===fXNVV}úôÁÍ›7™mºt邲²2# OŸ>!oß¾­Õó©(66VdøERqqq(--Å AƒpãÆ Üºu êêê8p`WŸuîÜ#GŽÇC§N0uêTÜ»w999PSSäI“X¹lÛ¶mÃèÑ£E†*:{ö, o߾̒µµ5 ðøñcðù|ðù|>>ÈÊÊÂþýûÿý7Þ½{OOOÄÆÆ"""mÚ´––îÞ½[e=õ9ÿ**..Æþýûáåå…ŒŒ DDD 33ŽŽŽÌg—¤ºt邉'BVV|>C† ©öýž˜˜ˆàà`̘1ƒrÓÐÐÀ?üÐ×ׇ®®.BCC±k×.=zJJJˆŽŽ ¼ø"-- +V¬`ÎosssL:•ÙÇ€àææ‡ƒÖ­[ÃÅÅ…õyùüùsxyy!>>PWWGûöíErÛ·o™z}f´T4è3cccƒ³gÏ‚‚èèhcåÊ•(++ömÛÒÒRÖ &ÿŠ»ê¡ü éïï/²¬|Ü=::cÇŽmð碬¬Ì$ç½{÷òòòèÙ³g­ê°³³ƒ’’îÞ½‹¨¨(Œ1ãÆƒ½½= Ú m¯ØVqdee±cÇøúúbÏž=055Å×_ùóçCIIIì6rrr „°Ê”””ðñãGæñ¶mÛ°páBØÚÚ¢sçÎLÎK}®`ÉÌÌD›6mê¼}·nÝ0qâDæñ±cÇ ¦¦†«W¯ŠœwÕ)ÏKHH@çÎ1wî\tíÚ>„žž‚‚‚ªý"„çfAA&L˜À*777GQQ”••€eË–aëÖ­èܹ3f̘Y³f1¹`åôôô˜Õ©œ<ŒŒ ‘ó¬{÷îLî Ö«z>>>¬2'''¢´´222b÷ûöí[èéé‰Í-©Î?þˆY³f¡K—.ÐÑÑÁàÁƒ±|ùòjÙ÷íÛ‡£GâáÇÌ9XþÙ1{ölÖºÕ¬€ðüÓ×ׯU›Å‰‹‹CII ¶mÛ†ÀÀ@Ö2›zÕ]Óû½ü¹Wõš†††bÔ¨QPQQ­­-tuuÁår™÷mtt4Œ%Ê—ªØ¦ôôtf{‡#rÞ¨ªªŠüØãñxÐÒÒ’èܦØhô™âp8011Á·ß~‹·oßâ?þ ü¨¤¤„'OžHTOù/ÆóçÏ£]»vU®óæÍ›jë©üE^[­ZµBAA’’’jLØ®ˆÇãÁÙÙ·oßFhh(~úé'tîÜíÚµÃÅ‹†Ý»w7jÛË;#FŒ@XXnݺ…7"##6l¸ŽŠ_rÙÙÙðññÁž={0eÊÂ/-[¶Ô«***uþ0411Á©S§Xe²²²——¯õ¼%áááÀ|š››ÃÍÍ Û·o‡¬­­aooÏÚ¦òk¥­­ ==½jÏwoooŒ?<@HHüüü——ÇüZ/' ®®^«çPÞ†Î;WyÕÜÓ§OoÞ¼{n·nÝšùB-‰V­Z1Á8ŠŠŠÈÈÈ@aa!“,‰®]»âîÝ»xùò%þùçlÛ¶ ÌëQÙÇ1wî\æõrÚÚÚàr¹ “èrõ9ÿ**Oß±c\]]ë]_m”n¾yó&&&"Ë}}}ѧO8p€ HnݺÅÚþýû÷øøñc•xÔ´BîÝ»Wãö„dddÔéÜnéèØgÄ××Wd¾ŸÒÒR\½z•ŽrrrBvv6Μ9ÃZ/;;©©©"uš™™AGGGd—ââbf_NNN¸rå 3 ü"ÐÑÑššZ½'V´±±œœ¶oßÎ\uòLj£¦²>}ú ((pqqŒ3þþþÐÐй¢¦¢N:5Ȥ>|@\\äääàää„~øÇ¯1p¬NZZJKK™a 2i_›6mj5oTEîîîxõêkž™¿þú yyyprrªU]—/_†••ë’ëyóæáøñãØµk—È>×®]ÃÌ™3ñý÷ßãçŸFpp0 0eÊ”––âèÑ£¸xñ"ììì0hÐ Ì;žžž€Ÿþ¹ÖÏMOOëÖ­ƒ¯¯/vîÜ BLMMÁápªý ó?-Z„™3g2ëŽ3~~~LÏIU† †uëÖ!##uº´N صkWŒ3ˆÇ±cÇpãÆ:Õ{[œ1vìX 8‰‰‰5 IÂÝÝ«W¯†@ ¹Tûĉxüø1þþûo$$$`éÒ¥hݺ5s m¯^½0qâDxxx`îܹHHHÀñãÇñõ×_39,Uyôè¼½½add„3gÎ ""W®\a­3lØ0hii¡¨¨cÆŒa-ááá°´´ÄöíÛ¡¢¢WWW¨ªª2óиºº2ÃgmÚ´:ªH__ûöíŸÏùÂŒŒDzz::tèðx<¨¨¨°ê6ltuu†²²2,\¸Ë—/¯rålll ªªŠ'OžÀÜÜ;wî„k.—‹ƒb„ èÛ·/k™’’F…øøx¤§§ÃÙÙºººøê«¯àââ‚÷ïßãÍ›7Ð××Ç’%Kàää===XXX 99‘‘‘PQQÁæÍ›Eæ Z¾|9LMM1~üøjŸƒœœÜÜÜ ¢¢Â*>|8lllèèhâ§Ÿ~‚¥¥%8ºuë†!C† ¸¸qqq011ÁÏ?ÿ mmmhhh`Ò¤IÈËËCDDœœœ°wï^‘/,ÖüE€p#$%%Á»v킲²2\]]«œÌÐÍÍ ¹¹¹ˆŠŠB^^Æo¿ý\.—9‡\]]ѪU+deeÁÒÒ***PVVfþ,,, «« {{{ :”9ï[µj…Ù³gcèСU‹éëëãÀ‘‘A¯^½XË”••áææÆš÷G[[›é «üþ000€··7>~üˆˆˆB0xðàjsï*^„Q®C‡"óì´iÓ¦Ú ÞÜÜÇGII 3·ÓªU« «« GGG˜ššâÙ³ghݺ5þïÿþæææèÑ£s‘ÇàÁƒaii‰ÜÜ\¤¤¤ÀÅÅ .dr¬ºwï.’,^ñsÛÚÚ£Fb’íÕÔÔàíí OOOÈÊÊ2ÛÌ›7ƒ ùñKI@*מQ-RBBs97!„lß¾hii±æ ¢ÆîÝ»‰ºº:ë’ãÏAù¥ï‰‰‰M¶Ï;wî.—Ëš‰j\ûöí#ªªªMú:·DGŽ! Ì¥öTípi  Ñf ==\.·ÊYr©ú™:u*‚‚‚СCäää %%ÄСC¥Ý´f§¬¬ ýû÷³~1JÓàÁƒ¡ªªŠ£G6Éþ’’’àä䄱cÇbíÚµM²OJ˜§3hÐ âÊ•+5ÎôLÕ^xx8z÷î_~ù3gΔvs¾H-&Z°``óæÍ¬ò²²2¬Y³»wïFnn.,--ë•×AUM àÁƒHIIžžlmm¡©©)íf5[wîÜ]ƒ\–\_„\¸pVVVhÛ¶m“ìóùóçx÷î<<2331`Ài7å‹Õì ‹/bΜ9HHH€H4gμxñ?þø#úõë‡âââZ]rJQEQÔ—§Ù@åÄõÅÄÄ cÇŽLÒ&EQEQ-C‹¾ þÖ­[000ÀöíÛñäɨ¨¨`Þ¼yÌ|¯_¿Æëׯ¥ÜJŠ¢(Šj:òòò-bh­E@±±±PPP€‡‡|||ðÏ?ÿÀËË þù'zõê…ãÇãøñã033kô¶$''C]]]dú~IÅÄÄH<íº$ëV·NUËÄ•W.+))Arr2ë²þ¨á&â IDAT¨(±³­6´¢¢"‚:çÃÔæ5úðáJJJ ¡¡Qå: uŒË§L¨8clåõšêWÕFIÕö5ªi_àñxPUUYVÕk”••¬™uÅ·•÷mmí&B¯Ï1®íöÒü¼¨ü}üøµšM¾®ÄµQ›c,ÉgKcãÊe………xüø1’““%jûMZ—Ÿ55âããÃ*ûå—_ÈðáÃYecÆŒ!K—.%„âççGüüüš¤}ûöí#ïÞ½«óöµi§$ëV·NUËÄ•W.ËÌÌ$›6mb•yxxÔØž†””DvìØQçík󅆆’Ë—/W»NCãcÇŽ‘W¯^U»^Scqû®Ú¾F5íëòåËÌ]Ø+«ê5 !!!!¬2qçmå}Ϙ1ƒ„‡‡×Øæ†PßÏ¥/åó¢òkôÏ?ÿo¿ý¶Æö4qçAmÔæKòÙÒXǸrYRRÑÑÑ©¶-ÍE‹€Nž[ëW.kIP³ËÍÍ…@ À‡»ú´´´ ¤¤„!C†`éÒ¥X¿~=¦M›†+W®àÁƒضm[“·³º¡IÔt‡æÚ®[Ý:U-W^¹LFFFd¶Û¦ÂãñêuÙ}m^#In€ØPÇX\}m·†VŸ}×ö5ªi_•gt®¨ª×¨|¦ÞŠÄ·_ê1®íöMñyQV¤¤‰‰ºxøxÿHLÿûWX–žðx@a!ÀmÄ»YŠ;j£6ÇX’Ï–ÆúL®©îæ¬Ù@wîÜÁºuë˜Ç_ý5~úé'¸»»C^^ÁÁÁX²d `llŒàà`æÖ TÓ9r¤´›ÐìÑcÜøúôéCïÈ-Fzú§¦<˜©Ô$& ƒŸJ·)«Bk½QR"ÜFO¯‘O5kÍ>0`@µÙì:uÂüÑ„-¢*»wïºuë&íf4kô7¾§OŸBCC£Î2|©^¼ž<Ø$% {jÎ/t@b" €¨úiöPc{ðàÂÃå݌/š¼¼¼DwH¦ê®ªcÌçók¼A(%OOOi7¡IEGË—'O ‡¥š†>á­s=šj¿TsD z:zô(îÝ»‡.]ºH»)U+EEE¦U+))ÀÏ?»wÅÅ W¯¦& ¯ÿé¯MöãÕ«óç?­Ÿ”Ôpû¦Z&5€ñãÇ33MSÔ—"++«^sÉPléééPSS×â{pêy1C87¦@€¡´ˆª7QE5€æ–Dpô(°bðîøuŒŒ€U«€ñã÷’t <áùS €¨ú¢EQÅrå °t)ðô©øå­[?þÌ™ðùMÓ¦ÊwG¡C`T}ш¢(ª4‡ „OHˆøåJJÀ¢EÀâÅÂÜž¦$ìú”D{€¨újäNKŠ¢¨–!$$ÙÙÙÒnFDF£GööâƒYY`î\á¥ï?ÿÜôÁP1èáÕheeMߪùørªPE}F¾Ä ¤$`åJ`Ï ¤Dt9‡xy  ;thúöU¤¢()é#/O˜TZ ¤¦-ô.T =@EQ-Lv¶pC`×.ñÁÏW_aaÀ±cÒ~ÊUžù™ƒQõA ŠqøðalÞ¼/_¾d•GEEa×®]RjUóSRRÜ¿_ÚM¡Pzz:JÄEŸ‘ÂBàÿ4k×?Š®cc\¿üù'`mÝôm¬Žž^€ æ1M„¦êƒ@cÆ X¶lfÏžÍ*öì~øá)µªù)))ÁñãÇñæÍi7…j@ŸsPYpà`j øú oPZYÇŽÂÛZXeÕÞˆ’‚‚‚‘íjBÁÇ«|žeee(--û| °°r¦¹•——ÇëׯŮ[PPYYYÈÈÈTÛ¦ÊuRÒ÷¹æ]¼,[&¼i©8ºº€Ÿ0}:ð¹_À֡çy€QõC{€¤,/Ox5CcÿU5ƒke&&&˜>}:~øá”Us‰Åž={```eeeèëë#  Æº1bĨ¨¨@AAæææ8uêa@3þ|¨««CYYݺuÃíÛ·™mïß¿###8p¦¦¦PTT„©©)^¼x€€´iÓªªª=z4«gå»ï¾Ãرc1cÆ ¨©©AEEƒ Bff&³Ž<ˆÿýïppp€««+ ;;ÞÞÞPWW‡ŠŠ :t耫W¯2Û½xñ®®®PPP€¼¼<:vìˆcÇŽý÷ºæÁÛÛjjj——GëÖ­1þ|f[ \ºt‰yüòåK899AEEªªª7n22>uõ/Y²&LÀâÅ‹¡¡¡---Ìž=›õ<(ª¢ÐPÀÙ2D|ð£ª*LnŽŽfÏþüƒ@4ˆQõA JÄO?ý„¨¨(æË¼²sçÎaþüù˜7oâââàëë‹eË–áСCUÖY\\Œ~ýú!99‡«W¯0kÖ,MMM$ÑŸÀŸ OOO©Ü5+ øþ{aóž=Âùq*Ÿ¢¢"BÀápDê ¶Îª‹ˆÔ ÊýW”šš (Ws›jYYY())‰MV.O¢öóóƒ——‚‚‚pûömôíÛ+W®ÄòåËáææ†èèh?~÷ïßÇwß}‡ÀÀ@üóÏ?"õ‰;ž(++«ö¶ ’¹zõ*,--Á¯æ.ŽVVVÈÉÉÁóçÏ¡®®Îúãr¹(--Eqq1ÌḬ̀téR\ºt ¾¾¾8þ<a£¦¦†Y³faïÞ½¸xñ"îß¿/¶wÊÒÒ¯^½Brr2SvýúuÂãF}š*¨´Ø»WØãóý÷⃟.]€  àîÝæü€‚Bäå?õÜ–”H~EUF{€¨*)((ÀÏÏ3g΄††S¾bÅ |õÕW˜1c¼¼¼púôi\¿~AAAUÖ5dÈôèÑžžž˜7olmmŒ¼¼ß~ûm½ŸÇ£G°sçNèèèàðáÃ8þ<.\¸Pí6æææ˜4i¼¼¼°råJ˜››#** §N”)SàææGGGøúúÂÊÊ III ¢E‹C‡…½½=ÜÝÝÁår±}ûvxxx@[[[d_&LÀš5k0|øp|÷Ýwxÿþ=þïÿþãÆCÇŽëýü©¦77·F+(z÷ÎÐ,NÛ¶Â[[Lž Ô0“ÂI @]= ÉÉìa01o+Šª €(† XeS§NÅåË—Y¹=...¸xñ"V®\É|IŸ9sƒ ª²n.—‹ëׯÃÏχÂöíÛaaaÁ «V­B«V­pàÀÀÎÎ<@ëÖ­jjjpqqa ‰ijjÂÅÅ…µ899±ÊÔÔÔŒ/^ÀØØ.\À€˜å={ö„~å¾uûöíÃŽ;pøðaDEE¡uëÖèÝ»7 ªªŠ±cÇ"00 PWWǨQ£0mÚ4ÀôéÓñûï¿ãàÁƒàp8°··Ç¶mÛ˜º{õêÅC|>wîܯ¯/,XeeeLœ8+W®dÖïÔ©t+ÝôÈÐÐ=zô¨ò˜SM«)æš?_|ð£¡!œëgÞ< š©°¾xúúú01Š ¥HLºw—^›¨/‡TÎ"¥þþþ¬ÅY°`ŒŒŒ°`Á‚¦iU+_ý5²³³qîÜ9i7峓••###deeI»)”öìT¤¨øø‡ÂÔÔ¤Ó®¦6f ðßôa„‰ßÿýî @rr2,--YCòÍí¢(ŠjéééPSS«6q½®ÂÃ9sØeÆÆÀßmÚ4øî>[EEEÐÐÈðiHž^ FÕM‚¦šµ¯¾ú C† ‘v3¨ ±î–—Œ-ü·œœœ°¤%?€0(?ÿ«ŒÎMÕí¢šµŠ³)STcj¬ 9s„=@mÚôùÝ©½)èëë£oß¡8xðSí¢êŠöQE}¦…¹?+:Ö’ÐûQ …@”D>|ˆk×®U¹üôéÓxõêU­êLOOÇþýûY÷ȪNQQöïß@P«ýPTShèy€ž>^õUQ§N€˜ÉÙ[Œ¢¢"(*f°ÊhUW4¢$rôèQlܸ‘ylccƒÑ£G3}}}« Äy÷î¦NŠÜÜ\‰Öÿøñ#¦NŠèèh‘e{› Šj* ™”“#Ìû©8A¸‚‚0ï§š Ì›=@€˜vPùlÐU[4ˆª“µk×V{+‰¦rúôi;v ÁÁÁhÛ¶­´›Cµ` ™4}:ðæ »lÛ6 ¥Çøúúú?~(¼½?‡EE@z:ðß”a%1IÙãäǸs«Ñ÷£*§ŠiVUO–ñìÙ3|ø€³gÏbÀ€¬ ÿ²²²„#FˆÝÎÀÀC† ǹÍE}‰¶mNžd—M™L*ö|Žôô€wï>=NJ¢U{4’²[1·°ðÏ…¾C5Ãj ÈÈHÖ­,222pûömìÚµKl´iÓ&´oßööö¬²ââbXZZâÆزe =z„6\««¢¢‚U«VáÍ›7X³f S¾ÿ~lÞ¼“'OƇD¶spp€ƒƒÒÒÒhDIUCÌü79:£kW`ûöz6®™(**Bnn.ôô4XPb"í£jæQ„Ý÷AAA ™3gÀçó1hÐ LŸ>]â:ÜÜÜðîÝ;\¼xï޽ǹ‘jU8fΜ‰}ûö±n»±gÏ̘1ƒÞùœúìÕ7(+K8ËqQѧ2eeaÞ¢b4°¸sçŽÈ•`4šª ú­B‰Xºt)bccqäÈ‘Z–––̽ºtttгgO†ŠÊ·î£U-bìÈ‘#ÈÌÌļyóÄ.ÏÍÍÅôéÓ1uêTôïß¿IÛf¥g{ŸF߆‚FÍ+8qâvî܉ÐÐP¨««×kŸíÛ·Çëׯ%^_KK #GŽD`` †Š={ö`ذa"7¥¨æfãFà?Øe³ftOñè\@TChÖPXX¶lÙ‚³gÏbæÌ™b×)--Ř1cpûömôíÛ·‰[¸ºÀÅÐ¥æ›ÀóçÏáíí½{÷¢k×®õ®ïÞ½{èØ±c­¶™={6úôéƒ7oÞàøñã8{öl½ÛAQM¡®9@¡¡ÀÒ¥ì2++@ÂÑã¥<H_ŸýƒŽöQuѬ x{{#??UÝô~Þ¼yèÚµ+Š*¼·@™™™1b¾ùæŒ3¦Nu3ÿøð!îß¿ýû÷תtìØ£G†ŽŽÜÝÝëÔŠjj!!!pss«Õ0Xz:àåTœ?QMM˜÷#'×ü „……AOo(«œöQuѬs€tuuáêê ±ËýõW¤¦¦býúõMܲÏϺuësçÎÁÌÌŒùûñÇ%®cÙ²eppp€µµ5ììì0vìØ:Ý‹köìÙxúô)fÍš‡Síº¿þú+€øøx888`РAµÞ'EÕWms€&MâãÙå{ö:4pãš šD5(ÒøøøVÙÉ“'‰½½=ùøñ#!„>}úÝ»w³Öñóó#†††ÄÏÏõW‘““Ù´iS£¶¿)¼y󆄄„ˆü½~ýšBHTTyúô)³þ³gÏÈ›7o˜Ç¡¡¡$66–‘ß~ûܾ}»Æ}~øð„„„ââbVynn. !999¬òââbB>|øÀ”EFFŠ´ùîÝ»u:-Mff&QRRb•U>¿éãÆ{¼f !€†BÂ?{ûϧ}Ÿóc ü˜ ŸœÜçÕ¾/íñ… ˆŸŸqqq!¾¾¾DCCƒ´Bšÿ$â ,lÞ¼™)Ó××ǬY³ ££زe lll0~üx 8àïïÏú·ªºŒŒ˜}PÔ—"++ FFFÈÊÊ’vSš…ÚäݺôéT˜ñööÀíÛÀRRb”çihh@NŽ=e@z: !ÙµT5’““aii‰äädi7¥Ñ5ë!°êÌ™3ùùùˆ‰‰ALL òóó‘žžŽÔÔTi7¢¨/¤ó¥¤ãƱƒ àÄ üÔ¤| @ôJ0: FÕV³N‚®ÎŠ+XÃÂÂ0räHL™2EJ-¢(êK&ɽÀÊÊ„—¶Wü²æp€ ïÓ¢•çÂ(6öÓ²¤$ÀÜ\J £¾HͺèôéÓ022ÂÞ½{±wï^áÂ… ÒnEQ-ÔÊ•Àì²%K€Áƒ¥Óž/YåDhz%U[ͺÈÓÓSâ;4_¿~½‘[óù‹‡††”””¥þ¼¼Eff&¬­­Ñºuk‰ëÏËËCZZšÈðV~~>RRR`XEFè½{÷ŽV­ZI¼/Š’¦^Þ^¹¬_?é´§¹ C`T}ÑHÚŽ.lüý11¯þÓO?áÈ‘#Ìã’’äååáôéÓ5jÂÃÃ1räHÄÄÄ@SSزeK•÷\«,%%&&&¸zõ*ël6lÀ©S§ðâÅ ±Û-Y²àíí´Š?©)JÊÄå]ºlØÀ^¯OÀϯ‰×LTÌ¢IÐT}Ñ J¬mÛ¶!++ YYYÈÌÌDß¾}áììŒaƂɓ'£cÇŽˆ‰‰All,6n܈o¾ù¦ÊÀ¥2cccôë×LYYYöíÛ‡Y³f5ÖÓ¢¨FS9(.˜¹ß¾} èÕ«³—Ë…££#³LC† AëÖ­qèÐ!,\¸{öìÁرc¡¦¦V§¶RÔçbñbàþ}vÙªU€³³tÚÓq8€®.ûf²II€ººôÚD}Yh$m&ÿ>CQQQ˜8q"~ûí7ØÙÙ1ååÉÎÑÑÑèÑ£SÉÊç© ÇÃôéӈɓ'#((·+g‹RÔ¢<èüy¶la/4XºT:íjN*æÂa°ÊPçÎRjõÅ¡C`”Xyyy1b¼¼¼àííÍZfaauuu\½z•)‹ŽŽÆ›7oлwïZígúô鈈ˆÀܹsann[[Ûi?E5µ<}šiÓØåmÛ {,¨ú©˜ÐÙ ©ú¡=@”Xß}÷ÂÃÃakk‹Ù³g3å#FŒ@ÿþý±~ýz|óÍ7¸{÷.LLL°oß>¸ººbìØ±µÚOÛ¶mááá'Nà÷߯qýÀÀ@DEEááÇÈÏÏÇÒ¥KѾ}{š8MIÝ!žèÙ¨8¢¬,pò$ð_jUOs€šMÕ €(ÆâÅ‹Ñý¿|¤^½zAGGGdò9€fΜ SSSœ:u ©©©X»v-fÍšUí-+ºuëÆ\Æ^‘¿¿?¬­­1nÜ8V¹üüü //Ï”ñù|ÈËËcäÈ‘¬2Š’¶ €GØeë×ÒiOK@{€¨ú ÅX¼x1óÿ ä%¹ººÂÕÕUâú»uë†nݺ‰”[YYÁÊÊJ¤ÜÀÀþþþ¬2z…õ9:v ع3€Ê?V‡oš)¾Zq9@Ñ ª6hEQT=ÄÆÂØÂñ/cc`ÿ~)6ª™ªœD ª>hEQT=̘ï#¼˜œœ0ï‡ÎæÐð*çÑ!0ª>hEQT×®±Ë~ý¨0;ÕˆhU4¢(Šªƒ„À×·bI:œK0ož´ZÔü!£ÂôÚZZ@…[¯!/øðA £¾H4¢(Šªƒ™3Ù_¶rr!øßÿ²é|?¨r— T¾X•öQ’¢EQT-8\¾Ì.[¿Þ=zÐ Så a{Q’¢EQT-$%‰^ÞîäÌŸ/ö´t•ó€h"4%)QŒ®]»âܹsuÞ~ýúõ¸~ýºÄëBp÷î]¬Y³sæÌÁŠ+p¿ò$Å8pàÜÜÜ$ÞÏ­[·Ð¾}{±ËîܹƒK—.I\EÍž df~z¬ ìÝ df¦£¤¤Dz k*ç4šª;z¼”=ÎÍÅ­¬¬FߪŒ ¦Uþ¤¨$..¹¹¹uÞÇÙ³gÁãñ$¾!êãÇáìì www˜ššââÅ‹X½z50wîÜ*·ËÉÉABB‚ÄíÊÏÏGLL «,;;/^¼€··7ìííááá!q}TËuô(pþ<»lÕ*ÀÔ8}:ÔÍ IDATnnnФ÷½h4aaaôRxªAÐHÊneeaaTT£ïÇP^¾Æ¨¢‚‚ðù|p¹’wV×{“ŸŸyyyÖ­2Ú¶m‹W¯^ÁÔÔ”)2dÖ®][mÔ&L˜€/^ -- öööº/ªyHI¾ý–]æàði8ÌÓÓ³éՈˢ=@T]Ñ!0ŠåÉ“'èÝ»7TTT ¦¦†5kÖ0˼¼¼`dd$ò÷ôéS@ß¾}±wï^@ii)ŒŒŒ°fÍØÙÙAEEêêêØ¼y3SŸ––+ø{{{dV_¨ÁÝ»wѾ}{ÄÅűÊûí7ôéÓ§Êí.^¼ˆ˜˜ 0@â}Q-Û7ßééŸËÉ ‡¾jñj4 šª+úÖ¥XNŸ>)S¦àÞ½{˜4iV¬XÀš5kÄüµkךššèÔ© 11Ùn…‹`Þ¼y¸wïF…… ",,¬ÊýÿùçŸèÙ³§Äíutt!ûöíc•૯¾ªÍS§¨*< œ=Ë.ó÷:wþô8=æ56Ir€è%):&eVÊÊð10hôýhð${©W¯^I“&lmm‚ÇÃÍÍ &&&ÌzxõêÂÂÂXwk¯ìÿû °³³Ã… pìØ1ØØØˆ¬»yófÜ¿wïÞ•øyq¹\̘1¿ÿþ;V¬X.—‹Û·o#&&Ó¦M“¸ŠªŠ@‘É ml€ ÷„„РÆ&.ˆQuE )sQW‡‹ºº´›Q%;;;¼}û–Uvûöm,^¼/^„¡¡a­êstt©Nœ8Å‹cÿþý°µµ­UÞÞÞX¹r%®^½Š`Ïž=9r$´´´jUE‰3o–öé1ŸìÛÈȰף9@O\ŽŽðµ(->ÎÉrsee)4ú¢Ð!0ªZPQQa¿ÿ£GƪU«ªÍ±©Jjj*Ô*Ý%òÀ˜:u*:„ &ÔºN]]] 6 ÈÉÉÁ©S§0{öìZ×CQ•;œ8Á.[±èÚU:í¡Dq¹€¶6»ŒöQ’ U¥ââbܼyÖÖÖ„ãï£F‚««+¾ûî»Z×—™™‰°°0ÖUW?ÿü3æÏŸ3gÎ`ìØ±unëìÙ³qþüylÙ²íÚµƒ‹‹Kë¢(ÈÈæÌa—YYK—Š_Ÿæ5>q9@M„¦ê†Q,gÏž…®®.>|ø€;v€ÏçcþSÜ.X°ÏŸ?ÇŒ3püøqf'''´mÛVl}(**BRR¶mÛ###&7'00~~~˜3gbcc±sçNf;GGGtïÞ]âv»»»ÃÈÈþþþظqc뇆†";;ÉÉÉPPPÀ•+W ­­Í{åã#¼ô½œ¬¬p諪t:šÔøÄå4šªQŒ^½zAKK ‹-Brr2lmmÊ| çääÀÖÖ‡bm§««‹¶mÛÂÖÖV$âóùX»v-rrrгgO¬_¿rrrUUU¸¸¸ <<ááá¬íZµjUeÔ¦M‘¹{8~øáÌ.[¶ ¨.&§9@O\°œý˜öQ’àBˆ´ñ¹ò÷÷gý+΂ `dd„ 4M£¾¥¥¥àñx¸pásõyÉÊÊ‚‘‘²š`&ò/IV`nÎîE°°þýWØ D}~üý•+?=þî;à×_¥Öœ/Zrr2,--‘œœ,í¦4:šDQUÁ¢Eìà‡Ç}ÕüРÆWU½žª Q‚ÃáÀÇÇÆÆÆÒn EIìÊa°SÑâÅ@5oš”jxwîÜ)§C`T]´˜  ##ƒu냂‚œ9s< ¼•‹‹ di?w½q¹\Öm/(ês÷á0s&»¬KÀÏO²íiPã«*ˆ&ASuÑì{€°gÏLœ8ÿÏÞ™ÇÇt½ü3“}‘‘)J%4¨ÖöE”V‹ßmiµEmQj­Jµj§¶R¾ÚTUÅÒê¯ZÚJEÑ*J‘„$2!‘EdÏÌïc2sgIf2÷ι÷y¿^óšœsïÜû82“Ï<çsž³ÿ~αÿýË–-Chh(Z¶l‰)S¦`êÔ©"EJ„˜L›ddèÚ..,tϳO80”"êƒä3@¹¹¹ÈÈÈ@@@€Ñ±|§NªiwïÞ=ö/^ ///‹ï‘˜˜ˆ²²2^â%{A¿³:6mâö½óððÖ_#//þþþpµpÛÂz***P\\lôyÞ´)+ˆ¨V³va!PRx{‹$á4Hþèèh“+] §º PQQaÕXÿþýáééI+ilàŸþ±ªæa=æÆxÊ”)"DãXãÇsûÚ¶æÏ·î:THxÌÕrqš4áÖmÊÎZµ²s€„s¡‘ “&MÒLš4ÉìñòòrMß¾}5o¾ùfMß¼yó4-Z´ÐÌ›7óЇÚÔ¦¶s·ßxC£´y¥R£9rÄq⣶eíNØÿŸöÿòÐ!ÇŠÏ‘Û?üðƒfÞ¼yš>}úh¦Nª ÐÈÙÔÒÖé1eÌÕh4xþùç‘‘‘_~ù¥fúË’:@A8/11ìO¦–É“•+Å‹‰¨ƒ?ý¤k'$#Fˆ³Bu€dDUUÆŒƒœœìÛ·Ï*ïÁrx£‰ ±1%%À+¯pÅOëÖÀ‚õ»Õsu€2BÖ#kTPP€'žxjµ?ýô|}}ÅI–ìÙ³Gì$±1³g—/ëÚ °ysý³THxÌÕ¨"a=’@‡ÆØ±cñË/¿à—_~ÁرcqüøqÀ×_äädܸq?þ8úö틾}ûíuEËk¯½&v’‡Æ˜Ë‘#Àš5ܾ‰Þ½ëÍØØX2@ Œ¹:@ì·Mµ€ˆºü*°Ö­[cìØ±;vlM_DD`èСˆŒŒ4zö8AÒ£´xùeÝ’iˆˆ-/&Âv(DX‹äPpp0‚ƒƒM Ehh¨#" Q©Tfÿ~ 1Ö1w.pñ¢®­PÿûàãcÛu©ð˜«P5hÂz$?F8>äOcƱcÆ+¼&Lúõ³ýÚäžÚ<@d‚&¬E6Ëàë-ƒ'éP^tê\¸ ëkÞø÷_ Añâ"ø¡²’m[¢ÿ­´ðô/&g„–ÁAHŒ¸8®øØö$~¤›«­eˆÚ DˆŽ¾iˆÜÇøÄ `éRnß+¯=Æß=¨ðÔV #4a$€Ñ!ŠðÈyŒ+*€—^ª«u}¡¡ÀòåüÞ‡<@ÂS› #4a´\ªQ#b>}6nüýù½Oll,¿$Œ¨­;ÎmSˆ¨ Ê!YNŸ.äö½ððä“âÄC MÖ@ˆ¹ûSìǸ²’M}éÛrš5Lì‡Ì 䞺<@T š°@„èÈÙŸb/ä8Æ‹± >ë× s?ò µ ʵA BtäìO±rãÜ\ã­-ž}xúiáîI á±ÖD ¢6(D„äX¹()ѵ›41Þü””"¬!:rô§Ø9qa!°n·oÆ @èÚÉ$1³?„cA ÂH¢#UŠ#!µ16ôþôî ôê%N,ZÈ$<õõQˆ0 Bt¤èOq4¤4ÆgÎ{÷rûÞ{OœXô!ðXê"4a ä"DGŠþGCJcüñÇli³–®]Ä‹G y€„ÇRM–@ ‚ œ†‹;¹}äý! ¡)0ÂH¢#5Š#"•1^´ˆíý¥%* ° !`È$<õõݺÐ aˆlJ¥BZZšÉcÿý7¶mÛ†3gÎØ9*–?ÅQ‘Â_»lÝÊí›=›UvÈ$<–z€¼¼€† umµÈÉ00Â)‘¼ªªªÂåË—1vìX¬]»Öèø;#Q£Fá×_ÅàÁƒ1þ|¢”7Rò§8*Rã%K€ÊJ]»uk`Äñâ1$66b‡!i,õd„&êFò&è`Ñ¢EHKKC»ví8Ç®^½Š7âòåËhÚ´)._¾ŒÈÈHLœ8‘>ÈÂP©€Ï?çöÍœÉv~'S„„.èÚ$€C$Ÿ8p ’““M®ÐHLLD×®]Ñ´iS@«V­ÐªU+$''Û9Jy#Š#ãìc¼|9PV¦k‡‡/¼ ^<¦ ðXêÈMÔäPm¨T*4iÒ„ÓŒl½¯ øä“O8}¨m{[ߟâñH±­cG‰ÇšöíÛÀ† 5=€éÓ77LjOKRR/^ì0ñH±½páBލ¶óÙRx];;[üøµýÛo¿á“O>Á°aðiÓ&ùyL˜4i’fÒ¤Iœ¾9sæhFÅé0`€fùòåF£™7ožfòäÉšüü|ÎCjS›Ú¶ß_£a•4 _Ó´©FSRâ8ñQÛ1Û+W²ßíïÎøñŽŸ#µKJJ4ùùùšôôtMjjª&((H#~I1é2yòd\¼råJ$&&b¯^YÙ®]»bÒ¤I=z4âââ æ™ ûrçТŸ¯ë[¼˜e€¢6vìFŽÔµ~øA¼xœ•J…èèh§Ÿ6·YOuîÜ'Nœ@uu5 ¬¬ çÎCg1·”–!rx£‰³Žñ§ŸrÅO£FÀ믋OmHxlñ‘ š0Dò(-- ñññ8þ<Ο?øøx\½zЧO´lÙÏ?ÿ<0jÔ(ÄÄÄ }ûö"G-/¤P£ÆÑqÆ1.-V¬àö½ý6Р8ñÔÕKëd‚&êFòS`§OŸ6úðETTàÎ;X½z5RSSѱcG¼õÖ[ðððš#Y½˜4I×öõeÅÄ‹‰pJJ]ÛÅ(/§Ò u!§)0É×ŠŽŽFtt´Ùã 4ÀÚLˆ ŠŠ `éRnß믓ø!,ÇÛðóŠŠX»º¸yÓ83DÈÉO޾iˆ³ñ–-@f¦®íé L*^<–@ á±ÆЮðDí"DÇý)Ά3qu5ÛôTŸqã€{õJò 5 €ŒÐDíH~ Œp|¤°O•£ãLcœ\¾¬k»¹ï¾+^<–bªÚ<Á/Ö욨Êá0h4ÀÇsûÆŒš7'¹¡)0¢6H¢ãlþgÄYÆøûïsçtm¥’mzê Hx¬õQˆ¨ @„è8“?ÅYq–1^°€Û1hÓFœX¬…<@Âc­ˆ2@Dmˆgò§8+Î0Æ?ÿ œ8¡k+ÀìÙâÅc-ä[=@$€}(D„C`˜ý2èÐAœXi`˜¢)0B@„è8‹?Å™qô1>tˆ=ôq¶ú¤ä[=@99€ZÍsP„ÓBˆgñ§83Ž>Ɔٟþý‡'–úB á±ÖäëËZªª€[·ŒpJH¢ã þgÇ‘ÇøÄ æÿÑç½÷ĉÅbcc(v’ÆZ{ ·M> B‹Ó ‚‚9r ÑhðóÏ?ã‡~9*‚ lŰîOÏž@Ÿ>âÄBH2Bæp´aÃlÞ¼°{÷nŒ=}ôÆ'rd„­8º?E 8êŸ;ÎÎ9›÷G y€„ÇZ@FhÂ<@*×xOȇ@ 6Dbb"~ÿýw¸¸¸àÑGEŸ>}ðå—_¢¤¤Dìð‚¨+W¥¥ºvh(0v¬háÇÏðñѵ++¼<ñâ!‡@Z"""0þ|\½zsæÌÁ¾}ûвeKL˜0ÇŽ;<ÂFÍŸ"EeŒ €uë¸}Ó¦îîâÄÃ'äžúx€ã,Mƒ€“ -J¥=ö¶oߎ””tèЯ¿þ:"##ñù矋QOÍŸ"EeŒ×¬îÜѵ›4&L/>!ðÔÇš0S }ðÖ[oáÔ©Søê«¯PªŸS'œ Gò§HGãâb`Õ*nßäÉ€··8ñð y€„§> ö:n›2@àD(55«î}z–””`Ö¬Y˜:u*rrrйsgLœ8Qä ‚¨ ¸Þ €Þ¶„=  a §@_|ñrssëׯÇÏ?ÿ WWWŒ3FäÈ[qŠ”{ŒËÊ€å˹}o¾ÉDT ðÔ×D;¦ptíÚ5ôêÕ °k×.Ì™3‹/FVVîÞ½+rt„-8Š?Eʈ=ÆŸΖkññaÓ_R‚<@Â׈¦ÀÀ‰PPPRRR““ƒÓ§Oãᇡ¨¨Häè[pŠÔsŒ++%K¸}¯¾ 4n,Nþ˜Û÷Üs@Ë–vÅ.HxêëÈMã4hèС8|ø0òòò°bÅ x{{cÔ¨Qhذ¡Ø¡a‚½{3gtm¥˜5K¼xyCKá CœF@ii)öîÝ‹µk×bݺuxôÑG±mÛ6›®Y\\ŒÝ»wãÈ‘#P«Õú(V¯^ÍcÔ„%Hxì=ƆٟAƒ€èh»†`wÈ$<¶x€h Œ0ÄiP‡ðé§Ÿ"%%¯¿þ:<ˆx#FŒ¨×õ”J%Ôj5’’’jú4 ü¥T›Ÿ DàèQ@ïm@úÙÂñ¡)0§@péÒ%|ùå—øâ‹/°oß>”””ØThÇŽØ´i:uê„W_}0iÒ$Î9ÇŽCrr2硵moëûS!)¶µclûq³?Ɉ‰ºwî~ŽÒÎËËCbb¢ÃÄ#Åö8 k^Ï2@ºvv¶øÿGi_¼xÉÉÉøä“OðÇÈÆë4hîܹèÔ©vìØÈÈHìØ±999HHH¨÷5÷îÝ‹ÁƒcáÂ…¸uëŽ9‚³gÏò5a ä{qZðãÜ>¹d’’’h_B),,äÍD B¡Ñh4ba *• pssãåz7nÜ@Ë–-qëÖ­ši¯™3gâôéÓØ¿? ..ŽóLDí<ó °k—®ýÈ#À±câÅCZnßô-Zžž€ 6RÉ¢R©-‹Õ¹NS³=88·o߯ÆqéÒ%4lØO<ñú÷ï_¯ë©Õj(•JÖ fÍšáçŸæ3l‚ ))À·ßrûä’ý!Ÿ€¶]y9k—•¬J4!Oœf ,33‘‘‘ؽ{75j„ôôt<óÌ3X`¸ÜÄBÂÂÂðßÿþ/¾ø"öìÙƒÏ>û K—.Å”)SxŽœ¨ 9|Ó{Œñ¢Elï/-;ƒ ~[‡ê -u€š#¸8Z¶l† ‚¿þú Ë–-ÃîÝ»qøðaÌŸ?¿ÞÛalÚ´ O<ñvî܉³gÏbûöíxá…xŽœ¨ ò Ðc|ý:`X“töl@NûS á±¥@Ká .N3–••…ØØXN_ûöíѸqcdgg#""Âêkz{{cúôé|…HÔª$NãÒ]#zRRR0hÐ \¾|Yä¨[ ð5Æë׳¥ÄZ€{EÕey€„ÇVe€}>¤V«1{öltêÔ /½ôRRR‰‰‰èÞ½»M»ÁQÊË•+¹}o¾ øúŠAÔy€}^mÞ¼ xûí·gžykÖ¬ÁÓO?+V`éÒ¥b‡HØy€„Gˆ1þâ @ÿ²>>ÀÛoó~§<@Â÷ˆ¦Àäà  ß~û sçÎÅK/½„÷ß={öÄ|€Ÿ~ú /½ô’Øá<@ áá{Œ««ÃïãÇs·ä[=@\ZI PTÄC`„Sâ𨰰ÁÁÁ5í6mÚ`ܸqèÝ»·ˆQ|B áá{Œwì®\ѵÝÜ€©Sy½…Ó‹@9+@;`«H¡ôþœ ,œqx´Z­ÆÁƒQpÏiyæÌ`ûöí5çŒ5J¬ðB–,ZÄm„…‰ AXCH«\®%;h×N¼xñpxÔªU+ìÛ·ûöíãô/Òû&äܨT*N–à>ÇøÇ3gtm¥’>”;yyyð÷÷‡««Ã¬:-(..F@@@½¯AFhB‹Ã¿S×!$Çž={hL`øã… ¹íáömy¹´S“””„˜˜šÜÜ\œ8q‚×¥ð4&_^Ò‡Äðð5ƇGŽpûfÎäåÒNÕgGô IDAT[=@e€/€æÏŸ“'OÖz­""û`˜ý0èÒEœX¢>P5hB‹Ã  ãÇãèÑ£xùå—Ѹqc±Ã!€<@ÂÃÇŸ> Xñ0k–M—”ä><@T šÐâðïÔmÛ¶aýúõØ´i† ‚iÓ¦!Œ–›H ò cl¸òë‘G€˜›.))È$<|x€h ŒÐ¢Ðh4±ƒ°„òòr|ùå—Xµjºuë†Ù³g£U«V‚Þ3..ŽóLr%-Õj]ßž=ÀÓO‹AÔ‡[·€  ]Û×—mêK0T*¢££eQ¡ßá !jñððÀÓO?bëÖ­Ø»w¯Ø!„lX²„+~"#½¨! ³ÂZŠ‹ÙƒN!€222ðöÛo£}ûö¨¨¨ÀÅ‹1iÒ$±Ã"xBß4ÄÆ–1¾qøòKnߌ¬ª.¡ƒö[÷¨4¡ÃáЂ еkWøùù!%%kÖ¬A‹-Ä‹àZÅ'<¶ŒñŠ@E…®Ý¢ðì³<%1h/0á±u/0-ä"'@‡BNN,X€   ( £áÜZxê;Æ·oŸ}Æí{÷]€:C{ u€@Ãá?Æöïß/v![Ö®åú#‚‚€—_/‚àªMN"¤y€„§>c|÷.°z5·oòdÀË‹§ $y€„‡@ ‚Aˆò O}ÆxÓ& /O×öóÞxƒÇ $y€„‡/e€À ¦ÀéC á±vŒ++å˹}¯¿øûó”Ä ½À„‡<@ŸPˆ #¾ú ÈÌÔµ==)SÄ‹‡ ø„"ò 5c¬V‹sû^z hÚ”ç $y€„‡/M  ðX3Æß~ \¼¨k»¸°¥ïDíHxøò5iÂ-åPT””Ø|YÂÉ Dˆy€„Çš1^¸Û5 ˆˆà9 Bu€„‡/RiœÑ¤,ü DD ¿üœ<©k+ÀÌ™âÅCBA> ‚!:äKÇØ0û3x0%@@„<@Â×0ö‘’$€Ñ!ðX2ÆÇŽÉÉܾY³„‰GŠHxøòÆ š“Tˆò %cl˜ýéÓèÞ] €$Õ¾<@M”ª¡¬¬ YYYb‡A¢pîðÃÜ>òþR†–²@‡B—.]ФI´oßéééb‡$;È$çªI‡ŠO íÍ›7s<@¶\ÏÅðñá_»–ßx¥}ôèQÄÇÇcòäÉHHH@uu5ä€l§À233Žââbøøø~øá¼ñÆÈÈÈ@S`öbÆ ´^`Lqa!Т{Ö²|9ðÎ;vN"ìÚµ 111´†€Ü¸q'Nœàm)|—.ÜÊç‡={òri§…¦Àd@HH<==qáÂ…š¾[·nÁÃÃCÄ¨ä ‰á15ÆŸ~Ê?À„ v JbÐ^`ÂÃg v=n›ŒÐòB¶H©Tâí·ßÆÔ©S‘‘‘?þøë֭Ë/¾(vh!8eeÀ'ŸpûÞz ðõ'‚ª-od] úƒ>À|€þýûÃÅÅcÆŒÁôéÓÅKv¨T*‹†¤1ãÍ››7uÇ}|˜"êO^^üýýáê*ëUA©¨¨@qq1x¹^Hà…R„!Íq!¿d…@ƽÇÍ›@|<С/÷# Y¿S===±páB³Ëà û°gÏšý1®ª–-ã? ÙÛHJJ"ÀäææZ窪²²t‚&#¸~½æç™—2‡\Ýù?Þ{èóè£Ào¿Ñ®ÀD¶&hK 4!E¶nƌѵÝÝË—°0ñb"«Ñh€œŽ 1:* VÛ~¯&M€¤$ 2Òök98r2AË:DrC£aÛ^è3z4‰Âªª€;w€Š ûÍcÊþðOll¬p¯®þú HLŽÊËm¿®»;ûíŸÍ‰Œî»-t0øÞ Œ]“Û®u?°uëX&hÃî ´™ ZQàT"‰³d ·\d$0dˆxñrá;rw®µ ukã©«6mê4 Kà P­H¡`»k4Ü%òZ””D"ȉ÷o>áHÔj¶ç×W_€ ã3Øç8Á/6{€²²˜ØILdúìÌ©P-[O]µk§·¶ÛyʤOîP°i0ظ‘ûBm&¨MÞâ#„ƒ!:äâŸß&MÒ7w2PË–À³ÏŠ—”±ÚTPÀþXj³<©©ÖÝ0,Ìxêª}{¶³­D±‡¨Ö …‚Mƒ©ÕÀÿþ§ë×A­[ó#! $€Ñ!ñÃׯï¾ ìØax„ñ»ïÊ~ÆC0êô••ü¡Ëòüý·u†åà`¶1§ö!²!Y „ðYl‚6D¡` †¥Zµde±$‚ú($ PR,^ ,] ”–÷òbâgüxûÇ&[Ôj¶‚Ö¸|ø0A–âçôéôïÏíA%ÁÁLËh«äå••€››/V(€M›Ø‹?ÿ\ן•¥Ëµj%DØ"D‡<@¶ñÍ7Àôé@f¦éã#FÓ§«Ð¥ ±äååÁÿæM¸j§µ’’¬«¨ìátïÎÄNÿþ@׮̼LÔ „ÈÍ”¾u‹µ5¶º=<Ü 苠/¾Ðõgf²LÐÁƒlUáp8Þ:GBvìÙ³G윒¿ÿzõž{δø‰ŽfŸ½ À_Ñ ÂùólUЈHjÓ…íÛo¼|ûmÝâG©d¥¸§O~þ™Ÿ”¼÷+ÔDâLjÜÜ\>|˜÷ëZm„6D©d^ ±c¹ýZtåŠ ÑBA BtÈd99ÀìÙ@|ŠŠŒ»¹o½¼ÿ>Û.‰°†UWÖ<¹¹Ö]ƒŒËNA½Ð†(•lL£ÑÖŸ`hEPr2‰ ‚!:䪛~¦Ne»˜â‰'€•+ÍWã§1¶8{V'vdŽX ÉàïçW2. † ÀÊjÐu¡T²ô¬Flݪë¿~]gŒnÙÒ†|Aˆªdž €)S˜EÄmÛ+VO>YûuhŒM Ñgΰ?HÉɬxÒíÛÖ]ÃÇèÙèÓIj5bÆC MÁ¢PÏZ@µ¡T_~É~ǾþZ×íš.D"HtH¢C˜)(ââØÖCUUÆÇýýÙT×[oY¶\—ÆÌ›¡/x²^ð4hÀœç}ú°ÇCÕVp'0âBy€l6A›B_mÛ¦ë¿vM— jÑ‚‡õ…A8ÕÕlEíܹ¦í&J%37/XÀÌÎDüóðÛo:ÁcͲt€)ÍÿüG'x:w¦ÕY„÷ fˆÖhX½ -éé:c4ùÂDƒ!:äOa$'³í+Μ1}ü?ÿa&èN¬¿¶¬Æ¸¼ؾX³†Õ °†F€Þ½u‚':Úâ]ÑmÞ Œ¨¡<@¼™ Máâ Ñj5«I¡E+‚’“I‰Õ"DGîu€ÒÓØX–7%~š7gŸ›¿ÿ^?ñÈdŒ339sX»±c-?Ç3eyú43=ïÙÃŒW;[,~¶X¡-;¶u"T C”›kzê¹Þ¸¸0/ЈÜþ«W™ÊÈàñf„¥(4mp¸¸8Î3AðÉÝ»ÀÂ…Àòå¦wHðöf;·¿û.ÛÊ‚0áC,ÛóÝwuÿÕ Òewúôa«´ ûÄI84sýef¡¡<ߤªŠU.ݹ“Ûß},dqùiáP©TˆŽŽ†J¥;Á¡\-AˆÀ®]ÀäÉlË SŒ,Y⟇ŽIi)3–®Yí`gˆRÉ–È ÄÏØ/F© á  7@®®ì÷V£aZ®\Ñ£ÃÂx¾)aš#DGß4´¨ÕÀÌ™À3Ϙ?;³=3¿ù†_ñ#™1¾v `x80nœyñÓ¨0mpù2+¢ôÚk‚‹Ÿ¼¼GèCˆ æàA–í6LZ4iìÞÍ–·z\½Ê*@nÞÌ*Bš#0?xýuZFLØŒÝÀ¾%$°ÕaúYÚ´4'È05EðM¢#Š >û 0ÀXütìȦ»ì%~œbŒžz hÝš-3'~¢£™8ÊÌdËèDüHx„ôÙu L77`Ç–ÅÔçÒ%&‚ìˆü DˆŽÓûSLPUÅfd^{ ¨¬ä6 øãûVÁwØ1..>ýhßž)Å~`NqC\]Ù·äC‡€S§€—_<=ío-Hx„ô‰’Ò¢A†Ó{/2$D0UUL\ýý7°w/3!~ø!üfÎDLEÿ÷s@¨P-P ¢>äç³U^‰‰ÆÇÞ{˜?ŸJÏ - X»–íš]›hhÒ˜0Ms‘‚ôt "B×nÖL„äKE›/ß»—Ûß¶-[`¨ÒLq÷.L*{6÷sn®é/føùa± Ä1^9o+rÚ ^– ¾}û"** éééHOOGnn.ÊÊÊžžµ™e„p8›h÷n W/cñÓªpô¨ã‰@À1þö[¶ÉèÆ¦ÅOÏž¬Üÿµk¬€DÅ@ {`o½¦ÀRS7Þ`{üΞmÙV`W¯²/aƒ¯:%,C– CâããäädN?e€CæÏgß ß5ýú;w}1u[RRXôwße )'N4®MT^,XÀ’±»wó³T!`ìØ±FÙÂ~8ú7ÌL¶g׎Ü~77`ýz6_ïè¶^ÆøâE¶Kõ«¯¯÷ww>ø8yÒi—±Û í&¸Ê˹ý‘‘Ìpß}âÄeWþü78{ÖøXƒl7ö7Þ ÍÍÉШPP k«T¦EFmhw}Y¹’}Q2‡R  L™ÂVÔ‡¢"&¬Ö®eû› ,_nù6<ä"£V³oN/¼`,~† a+½$/~îÞeŸÊÝ»›?ƒ³¯¶'’ø!$…aÈšjÐ¥¥¬DT+âlNü4hÀÖ¤¥éV•Ö??&´Núô1>ž•ŒÅjœ?_ÿûH@„è8Ò7;wØÒRSûâΜÉöôjÐÀþqÙŠUc¼?Ks}ò‰qþ?(øææÊ ç7H'‡<@Â#´¨ŸZ¥æÎeÓ\&˜-Z°lLf&{{ÕQ‡×*¢¢€ädV‡ÌÐ˰Z‰>LJû¥j!DˆŽ£x€®\aþݽ{¹ýžžÀÖ­l¶ÇDåx§À¢1ÎÍFf_]¯]3>þâ‹À… ìë$ay€„Gh`úôi`ìX&l>úˆ½…Lѽ;[bù2ðÎ;,k#Ï>Ë Ðï¾ËjèSUÅö-lÛ–}¦É'ý8'¤„#ì–œ <ü°ñ7·ààAàùçE ‹7êã­[ÙÚ¯¿6>Árùññ2Yë_?bcc(v’Fè½À€º§À4–í×UzøòK¶' !®®ÌwsìðÇlu–áòu¡ðõeYì3gØR{C²³Y©®Þ½Í¯F“$€Ù³a0`—ÇíïÚ•íäþðÃâÄeÒÓÙ§¡á×WöuõßÙ„ 0—º{øôS–=yê)6¥dІ YöåÊVý‘G„·6Úµc+Âvî4=c}èÛ ~Ò$ãÊr€!:by€ªª˜‡÷õ×WO<û,[ñmj.Ý1cµš™¢¢ØrC|}u]¾ÜöÒ²2<@Â#†èÜ9æÿ gŸ—.™~]«VÀêÕ¬~Ï’%Že‘‹egÏ6®TQ]Íâ¾ÿ~–ä•Óºp@„èˆáº}xüqöN…øøcf$ôô´{X‚Áã3g˜ÙiÊöµVOOVóçÄ V˜„°ò  ßg¤æç›>¿O¶8ââEà­·Øô“#âíÍÞÚÿþËl~†Ü¼É 4>õT ÔjyLuS Z :@Òä¶œýòen¿¯/³Àl1²2àÃÙ†B••ÆÇ{÷fkxõKÏ„ÌHKÚ´©ý77æï™2…M!9#ßLžÌfÁ ñó›ŽÂBKa%e€YñÓO,ùa(~""X}ÉŠŸƒÙ´ÖÇ‹à³Ï˜œÄ!s §Àô dÓHéé¬Pª³Š€•û¸pQ4ÎvË£¶ BtìåZ¶Œe~ŠŠ¸ý}ú°‚ÇQQv Cxª«Ù'Û7ßÓ§> UL ËÑ2t([ú6a4´ò =<@>>ÆËÔÛµc‹%22Ø4’T¼žž@\ûì—¿Z DˆŽ=<@sæ°•†uý&L`«$7<a()Žgœ×^cé-??¶[ûsϱé®ß~ÃÙîà`¶4ä»ï¤ói.2ä{x€Ý[bÀ¶[ûùól`//Áo- lJìLjˆj±Ã±äªòIƒï¿† ã®npueåãß|S¼¸¬æÖ-VyíÔ)ö|ú4«xfín¯¼Â„Qm»-„ŒÙ°YâÚ·;ûsýz:uƒ¼¼Z61“®b@Brù2+`¬/~5bÉG/®ZÑhX}±sê”u›™¢U+frމá'N‚(P›U4ÜÝ5ps“GuD@„è¨T*ó~ÝÒRà¿ÿåørueU\{öäývõ£¢‚ÑÏêüó±QÉZ€è蚇*, ÁÿùBòòòàïïWcÁ¨¨¨@qq1¨"9ÁôN%DgÏž=‚l‡ñÆLKè³d‰ˆŸìlVqlÇ&~L-I·†–-™ÐéÔI'zš7眲güF™AIJJBLL m‡! ¹¹¹8qâ„àÛaò€<@µ@ çåÿÆçöÅÆ²©/Q¨®öícýø£qéiKpsc¦½Ì¢£Yí}‚ P©TˆŽŽ­B¿=¡ !9NždYõ¹ÿ~àóÏEæêU`óf–ñÉʲüu~~¬n~V'2Ò¸Ž=AQ/H¢Ã§(?ŸezÊÊt}ÞÞÀîÝ@ƒ¼Ü¢n**ØòòÿýHL¬{s°0ÈÑ žˆ^ëòå³"tHxÈDð Õ"D‡¯:@ ÛÔüêUnÿgŸÙ©Èá¹s¬6~H0jpà€yñ ¬]Ë6àÉÈ`Îì?†î»÷¢„bì·&7¨ðØ«!ÈT är.,Þ{Û÷úëÆžòÊÝ»@BËö=Zû¹~~l›ùñã.] Š ¢~ˆ œŒÄDàý÷¹}]»Ÿ|"Ð ÿú‹ÕÔÙ¾¸s§ös{ô`¢gÄ6GAˆ Btlõ§de±ÄŠ~AäÀ@`×.ž=ÃùùÀÖ­,Ûs¦ŽBa/¼Œ<ðAÔò y€„‡<@ŸÐ;•[êUVÏ<Ãv‰Ð¢T2bP §~h4l'õM›€o¿åº« Q(€þý™è:Ô¡Vl ÎEŸ IDATUk‰Ðáu€4Ð@­Q£Z]jMµÅÏõyMµúÞëîý¬ZG…ögsÏõ=§ ·Ù©Ù8h ÂüÂæ† Ÿ (d²{9Á/$€ѱåó´iÆÖ›¹sm J¥bK×7oÒÒj?74xé%¶ÇVË–6ÞXHüOll,o×Ê-ÉEÖ,de"«èÞ³^[U¬BEu…‘ Ñ@–Î Û7Ôüì¦tC¨_h 2õö †‹ÂEĈ G„á´$$«Wsû{ÌØ d1Ö+tu bÞžúp%ê¦J]U±ÊHÐè·oܹ²ªZ2‡Ju%Ò Ò‘^nö… š5hfV …6E¨_(Ü”nö œ@„èÔÇŸrá›iÒ'<ضMYÅÕ«¬Jâ_Ô]¬°uk–é;p"O y€„¥R]‰kÙ×PåVU‰y£*VA­Q×}AÂ4Õ*xYù2M52‹2‘Y”iöò â£6m0¬Ý04÷çc>Ý9¸Yr•MmÜžÇI DˆŽµþ”âb¶Éiq±®ÏÝmsa±ýšb…žž¬>ϸq@ß¾¼×è±ä2OYU Ë QXVhò¹¨¼Èì±Â2v¼´ª8 %‘ú¹(\à¢t1zV*”fYûlêZZŽB¡€Š:Ÿ-=@ÍÏ%y%¸rî ŠY\Qlv,¬A rîæ çnþÎþ»¦Êþ)èÝ¢7Fwg"Ÿ¿‡?/÷s4Òn§aÙËðÅ©/àÙØSìpìÕªªä˜<û,[}®ÏÚµÀĉ¼øüy&z¶lòòj?·C&zÆŒ5ªw¼„ýÐ@U±ªfJ$½ ªbU­â¦¢ºBì°ðqóA¨_hÍÔŒþs˜_BýBÑÀ½‘8Q*äWÛ¶°¼°&˦}hÅ‘6—WZÇ{ÝB<]=1øþÁÓq žhó„$¦ÌNfŸÄâ#‹±ëü®šì¤ßï~(L”~QOÊNÅš5Æâç¹ç,?ÿüê"ÖU¬Ð×—Uq?xøa›b%øG­Q#»8›#p®\«ùùzáu”W—‹¦Y´Ó,æD¶OªY!ð÷ð‡´oÒÞì9¥U¥F^+ýéÉÌ¢LäÜÍ©sz²¬ª »Îï®ó»èˆ‘#0æÁ1èÖï–àüvõ7,:¼¿^ùUìPDƒ!:–úSަNåöEF7ÖñÂíÛ™o§¤Äü9<ÂDÏÈ‘LI gñUkªqãÎ ³'£(Ã!36. øVû"°Q ÂrµúÏÍ4“DÖ@,ê[ÈËÕ ­Z£u@k³çT©«]œ]#ˆÒn§aç¹8¥:eòü¼Ò<¬?±ëO¬G«F­0ºãh<ßñy´ hcUlöD­Qã»”ï°èð"œ¸qÂìyŠ ç›æ¯4V 4f6lØP§?åÖ- sg SÏÃØ ðçŸ@»vf^T] Ìœ ,[fúx@›Þ7ÎN›…‰‡%clªÔUȺ“U«À©RײúN\•®ðóðc™Oÿz=ûºûb×®]¢×’:7nÜÀ‰'ðÔSOÙížçnÃÖ3[±íì6\/¼^çù„>‚1ŽÁÈÈ‘hìÝØÖMEu¶ü³KÿXŠ‹yÍž÷`ÓñjÔ«øà™ Ê–þV²@GÅ?þ•J…ž={bÈ!hÜX÷ KÈ1P«Çg{‹ê“Àv—0I^žnCRCbbX¶gøpÀÃ÷xåL•º × ¯ãZá5“'³(ÕšjAchâÝ-¶¬y4õmj$Zô› ñÒ@ ~¿ö;¶žÙŠ]çw¡ ¬ ÖóÝ”nØz Fw§Ú>OWû‹ïTÜÁ†°òèJdg›=¯O‹>˜Ùk&¶(«½Àd-€zôèîÝ»£mÛ¶HLLÄÁƒqýúu¸ß«àKÈ1xï=¶Ñ©>“&Õ²Ï×éÓÀ°a@z:·ßÝ™ˆ&L"LÙQVU†³7ÏâTö)œÌ>‰“Ù'qöæYAkØh=4ú§EúŸý[ÀÛö[#„¥¼º{/îÅÖ3[ñÓ¥Ÿêœ–õóðClûXŒî8}[ö¼ruÎݬ:¶ ëO¬7+ÔPàévOcFÏèÖ­¦ŸL¨¨¨¨;ЦM|øá‡5j@ö¢6Ê?C†pW©÷è$'n¦¬Û¶±)­ÒRnH°{7Э›‰I[=@ÅÅ8­:]#tN©Náü­ó¼OW) @°op­GŒoÒ–@{ #î–_–çv`ë™­8rýHÕ¸ÃýÂñ\‡ç0ºãhDñ;õ~%ÿ –þ±ñ§ãÍ~qSºáùŽÏczÏéx ±ñ>…r@²~§ê‹µZ¢¢"¨ÕT¤ÌÞ˜«Q“žÎ,:úâ'(رÄø©®¦OV¬0¾ALü8 X(¬©t»ôvÈÑ žKy—xÙfA©P"¤AZø·0)ršû7‡‡‹sNK:Â^`R'77×î ºhäÙ¯vy¯vyééøúìרzf+RrSLžŸQ”ÅGcñ‘ňŽÆèŽ£ñ\‡çÐÌ·Y½c8­:ÅGcç¹f§˜}Ý}1¾óx¼Óý„ù…Õû^RB~E#̰aø¹¹áé§ŸæôÇÇÇ#..ŽóЇڶ·õÿ0k——³b‡ùùºó]\€ãjp½Ü\¶ÆŠà^ˆ{è!–.º'~áß+F[;Ɔǧ͞†/ýˆÿÆ£áÀ†\ˆ_ Àô_§cûÚí¸˜wQ'~’ÁÅD;È'ÿiþŒé8½¯öÆæ§6ãÀ övfWÏFÆ” ~ù0¶ß ×ß]1®ó8 ¸oÚ´ÁÂÚe<„hÇÆÆbÍš5Û7n䈱ã1lÇ9ÿ™ƒ /àÄ„è–Ö M}šêNH朎ÓÛOcÚ/Ó¶" }õ†½>ŒSر®û<·D§Ï:aû¿ÛQd ~’ÆÞ1?f>®M¾¿£~ñ£½ÞÞ½{‡¾}ûbÙ²e¨¬”G%hYOiùþûï1vìXüüóÏxX¯ö‹ö—Ãð—Žž Øìú,XÌžmpâ©SÌïsí·ßÃX·Ž-'W ®rü:'³O"çnŽM×lîß›uF§àNèܬ3:7ëŒ!Žø!쇡?%>ÞXü  Ìšeð¯¿f«¹ ý>¡¡lÊë‘G‰×Ð@ƒTÿà·«¿!)= ‡ÎB¡[ý+»*J´h]#r:wB§fèEÓ=ZÈ$<Žèª … oõ8oõ8=){°õÌVüzùW“ÓU%•%Øvv¶Ý†¦>M1*jÆ<8‚:`ë™­Xrd RóRÍÞ/*( 3zÎÀ¨¨QpUÒïbmÈztV¬XO?ý@ûö櫈¢ïOùçà7¸Ç#"ØÎ5[pUU1¿ÏÊ•ÆëÕ Øµ hÚÔø˜Ä9wë’®&á·«¿áൃ¸]z[wðYvW¥+"›D¢S³N5b':8¾îÒ+É'äGôYƒ›žïð<žïð€¯¾^}ÕØï|û-е«=Bµ+™E™5bç·«¿áZᵺ_¤G€Wú´èƒ˜ˆtðî€>Q}(5. ägôYƒ ôjÞ ½š÷Âê'Vã§K?aë™­Ø{q/Ê«ËáíæqÇaj÷©hîß\ìpz§¢³té¬ZÅ­QóàƒÀ§Ÿ‚ù}¦MV­2~aïÞÀά8ȹ›ÃÉð\º}ɪ×ûyø¡w‹Þˆiƒ˜ˆ<ØôA(¬Òņ Ð7ª¯QZÈ$<Îî²ww m7CÛ EAY~½ò+úEô£…<"[%Hx²³N€½ÕØþþÀ‰@kÿ[Ìlü·ÞbEøÛöÍ»7qèú¡ÑsþÖy«^ïãæƒ^Í{!&"1-cÐ%¤ Mgaä"))þþ›-mÏ1(E´.üè7 ÈÈàôô6l^|Ñn±Ö‡âŠbdeàzáudf £(Ã蹤²Äªkzºz¢GxÄ´ŒA¿ˆ~èÚnJy¾ ‚ ø†!8 pñ"pìpü8{>{–Ín1TX éÓ¡E[€^¯e{Ù„‡ß}tébÏð(¯.7+j´Ïuím î.îx$ôô‹è‡˜ˆt ëVïÕY¶îFÔ y€„Gê ¾Ð;•à¼<àÏ?u‚çøq  V=°Àkè×» ‹J§/®6>¥o_¶ X“&Â}*unܹQ«¸©ÏrsKpUº¢kHך)­žÍ{ÂËÕ‹—k[³Q?È$^^ÀÆÀèÑõŽW­QãൃHø7»/ìFnIn½¯e .÷G¸_¸Ù熞 í Aa Â,ÅÅl5–¾à©ï€€¶5×#0ÑóÈýùh˜“ > Õ{ïÁµ¼œû‚-X}Ÿš*ˆ–£G®A¹ì<·Óæ ? qUº"¤AH­â&ÈDZ–æ“HxÈ$<ä"ø„Þ©2æöm¶ ýÆ öÐþœ ¤¦çÎjµõ×uscu|zt­Ä£WðPƒT„ÜIe= ¬Knݪ9Ÿ9€ôè×HH7¶ê¾Ç³Ž#áßì8·Ãê*ÉZ” %šú4­5{ÓÌ·YM}g<@ÂC á!Á'$€$ˆ¾°1%p´?&]êK§°[xªm*z6NE¤K ‚ S¡¼” lº¢¿ÔË,œ?Ëï¼,Y¸XVÏædöI$œc¢Ç’=tü<üЪQ+³'Ô/T’KËIüy€„‡<@Ÿr"òóMgkôû²³ëçÉ© ”£5Ò핊˜TtòNEDE*üU©Pfæ™6ÞÀË øßÿ€çž«óÔ³7Ï"áß$œK@Úí´:Ï÷÷ðÇÐvC12j$úß×_’‡ ‚°@@Upõ*píšy#”°Ñâá&šà‚paÈD;E*òMA[¤¢ñÝkP¨«R—y¸¡«+pß}@Û¶P…‡#øõר(³§§ä¦ á\þMÀ…Ü u^Þ×ÝOµ} ##GâñÖË~wsò y€„‡<@ŸÐ;ÕNh4LÈ\¼È}\º\¹Â–“ó‰JÑ·j©çf.7ÑTy Õ7áQm¢*±ÀiÒhÛÖøqß}Ì,`φ xÍ„ø¹œ¹&Ós&çL·òvóÆ 6ƒ02j$žló$o5t¤y€„‡<@ÂC ‚Oh/°Z¨Ï^`yyLÔ ´4àîÝúÇâòZÅŒþsâ|4Åõ¿™ÕÁy­[›:Yu©k…×°ãÜ$ü›€¿³ÿ®ó|OWO l=##GbHÛ!ðqó©ï¿‚ BöÐ^`D­Ü½«9†bçöm˯£„ÁP! ™hkE–Yaã‡"Ë/,”¤ 1-rZ¶”õ_•u' ;ÏíD¹Ï<Mÿww<Öê1ŒŒ‰§Ú>?¿zß› ‚'$€êàâŶX¾œ+v²,Xa­„M‘ƒ0d"5Ïú?‡à\Q÷*)»àéɦ«‚‚tÏ-[êDÎý÷ Øt‹‚²¤æ¥âbÞE¤æ¦"5/)¹)8wå4¾µ‹W¥+x#£FbX»aTDÐJÈ$<äò|BïÔ:øæ›gñÍ7Ü>4&Å¾È Á ¸gc5¸¹±::AA\QcîgÅ–*u®ä_Aj^jÈÑ>›ÝC+ÀCÆÝ. ôiÙ##GbøÃÑØÛºº@„ò y€„‡<@Ÿ¨âââ÷Ac#‘Š,¸£Â¾Á(•LÐfiÌýܰ!ÛoB nÞ½iRä\É¿‚*uý³Z (Ыy/ŒŒ‰Øö±hêӔǨ ‚ ˆÚ Qü%ÜÅ &XÂÃÙ#8ؼ¨ ´ÉgSʪÊpéö%#‘s1ï" ÊjÝÞÝ*Pà‘°G02r$ž‰|¡ By»6AA˜‚èÄöÆýÙÃúú4jeUe5ÒªRö\YjÔ§ßoM_nI.®^‡ZS½0ÌàîâŽVZ¡mã¶hضæ9 :íZ¶ãí>„1äò y€>¡wj}iÐÀXÜ>¼½°LJ~Y>òKóq»ô6û¹øòOßk—æ#¿,%•%&EŒa_Eµ§ß¬$Ø7˜#p´Ï"à¢0ÞâbÆ h÷ !!ðHxÈDð y€j!..C¿ÿÑC† :,wšø£ IÜ ô@®k¥±¨¹'dôEÍíÒÛ(«°„³Hx¹z¡M`“B‡–¥A8'ä"jèóôT»¯ÀÝì»@¶ØÑØó 38m·EsÿæP@8“5AA  :(RAÌÕìæðpñ€—›<]=áåÊž=]=kúlíoàÑ #àíæ-ø¿…ü)ÂQ¡V£T­Æõìl¡@µF£{ÐVk4PÐhXI˚ǽvmÇ4÷^oî˜Ñqçª Žéc(Ùz+%µgÁkJ àæë ‹ ª4TÝSÏU÷ÆÊì1ƒsLJûRó0hsŽ›8WYË1ÀŽ×rýë»(p¹wM¡~FU*îÞ…WÆ&ÿ¯kû1õ{W×yÚ£òÞ³‹ÞÏÚ˜ŒÚúçóüz@WçV;y£ÑïÓ;fÔgð£×Þ;–_T„ê¦òX}Kȸ)ÝÐг!¼ÐÈ«y62ù³¯»¯E"ÅÃÕCRÙ¡ý)ej5Š««q§ºåjcS·áIÃYaSsÄF¯1uN×Ñ(W«QvO¤hŸK««u?ÛÐ_¦VC­áÿþ ß„°<têøÑ°`äå©©@bG"iü:v;»@ÈB” %z64/¼îµkùÙ×ÝWìð}ñ£+ú;†íªªºÏÑû¹šln$~ìAŸ>bG }Iü¼A¨&aâfÄÁßÓß©².æ¦ Ó¥µMSh³ej5çg“m¦Öãµ]£TOôT‘X!‚ ì  :8Þ«U¨Ôä£J£Aå½ù÷šgµšÓ¶äÃgí9ææ³a¢ÏT0=ãðܾÍj&¼ã¦PÀËÅîùùpoÜ.Ðù*\îy ønk= |øJøðµèSÛt§¦¶ó,xMI~>¼ýüàæê ×{ãázïábø ˜=fꜬ,³¾*Çç#š¦!–RBfR*š™¹Ib¦hj*Â"O‚À¢<º²r(ósÙåùaÜÝÏë̽sw>܆3wî4j£ÑöIòÁ$À`%@-:åìŒS99|‡¡ßΜéÒ[4¦Ìanl ‘@ÐìW@}k“ë´°¬ís´µé!à9¢'ÿs_?‰õ¹Æõ­(º¾aàdTTùúj‰’t–„óçi .VXXˆÔôtü÷ÅùEoaä¿ÿòF· y€š±zõj„{yñ†AéñT²bñäÿ¦–µ®#jÔ µ$4„B4Ñ<@Dç5÷h+´”i» ÜãÉÕ„†¦—Œš¯of¹qÛ†äÅ„’B!Ý€ xæåaÒ„ 0yê¼IãÿíåMÔ7µNS÷³¡¥LÛ2 ýV̳®¨¨½h .Es-u=zX×£w‘Îdð?©………HIIµµ5Ƈçž{N­~rVVñaˆ‰‰ÁŠ+øC¯Qw½Ã‡cúôéøL"LJ;wî %%sçÎå;¢ :JNN†T*…··7nß¾ÚÚZüþûïÓDfݪ¦¦†ïôõq×S*•“_’ÎÅž< FHgðŸBCC±zõjìß¿§OŸ†©©)öïßÏK, …¢C?Øm°Öšu›[§©:måË=z„’’’·ßT*ÊÊÊÚݾ-û¨¦¦•••Í®ÓY}|ïÞ=(•ÊVvWëȶۺZÚVeee“É_Sû¨ªª UUUjeÚŽ[]íã¶¶çó|ÑšŸ£®¢í8h‹¶ôqkÎ-]ÕÇ-}¶>3ب¶¶gΜÁŸ<~mdd„3fàĉ¼Äsøða¶»}TTT§®ÛÜ:MÕi+o\VYY©‘dvä$Ó¥¥¥ˆowû¶ì£«W¯"%%¥Ùu:«?Žìììf×ë®>Ö¶í¶hë>ji[)))¸zõªÖº¦öQjj*RSSÕÊ´··]]] •JÕš°;¬#}ÜÖö|ž/º:TWW·OgÐv´E[ú¸5ç–®êã–>[Ÿìcð9990`”J%zôèøæ›o°{÷nœ;wÀãÇàøá¸ººvy¾{÷.ÌÍÍaffÖäzÝÕÇMÅØZmÝG-mK¡P@(j½­ÝÔ>ºwïÀÊÊŠ+ÓvÜ6ÞöÍ›7ѯ_?ôìÙ³U±wDGú¸­íù<_4ÞG(**‚‹‹K«bïmÇA[´¥[snéª>n\¦T*ñÏ?ÿÄU!ƒôàÁ€±±1Wfll¬v200°Û~iB!Ï‚öþ!®k 6j˜­µ²²’ËðËËËaggÇ­ãêêÚ-W!„Ò½ 6êÝ»7ú÷ïsçÎÁÇÇpñâE¼ØÊ)Ö‹‹‹ŽÂÂB :«W¯æn¥‘Î¥P(õë×óŠ^:zô(Ž9Â-‡……©Ýê!#99û÷ï‡P(Ä—_~Éw8zçÔ©SøñǹåÁƒcÙ²e^·n7ŸÊÒ¥K±bÅ š›© Îž=‹åË—k”—••áí·ßÆàÁƒñꫯâï¿ÿæêòóó‘““CÉO+Éår¼óÎ;å555X´h† ‚I“&áøñã€íÛ·Ãßß—.]BAAAw‡«³æÍ›‡ŒŒ òèèhŒ;ÇGXXêëë!‹áää'''ìÙ³aaa¤R)är9rss¡R©`aa   ºšÙ‚ÈÈHxyyaß¾}S8?~!!!ضm —ãÜ RIDATâââpâÄ µÛãüñàììÜÝa딤¤$¼ñÆøßÿþ§ñGiAA|}}±xñb;v >DPPD"¼¼¼””„àà`¼öÚk–Ëåøè£¸e777ܼySm²±cÇbãÆ]¨³°°€——233qùòeµ:¹\‰Dsss®ÌÍÍ ééé8x𠆊ššdeeéüeí®Ô£GxyyqÓh<-//0pà@®ÌÍÍ iii˜>}:¶oߎÀÀ@>|±±±Ý³.1bh}±¬\.‡T*å–ÝÜÜpëÖ-¨T*ÄÅÅaâĉjOóíœáìì¬u®²ŒŒ ¸¹¹qË}ûö…¹¹9LMMqá 33Sm]D P JJJÔ,ZXX ¶¶–––HHHÀÝ»w1mÚ4z9_ܽ{W­Åb1ª««Q[[˽œvÁ‚|…§JJJ4^ô+‹QRRSSSDFFÂÌÌ qqq4@·Ÿ+€Çç‹’’|ôÑGصk¢¢¢°uëVzajh;_0ÆPZZ ¥R©öÇiŸ’’§»Äb1êêꉨ¨(ôë×;wîä)ÂÎA P ÌÌÌÔÆDÔÖÖB(â…^Ðùì÷YѳgO>611Q›Œ‹ ŽiÜÇÀã»b±3gÎÄÌ™3yŠL4>We±X ¡Pˆ¥K—ò™~Ñv¾ÿ‚ž?>_a长Îîîîpwwç)²Îeðƒ [booÛ·osËyyy°··×z+‡´¶>vpp >îDööö¸sç=zĕݾ}}ûöå1*ýbooêêj”——seyyyÔÇLÛùÂÊÊŠ®\v"{{{äççsË Çµ¾ÍF P ¤R)¾ýö[Ô××bbbàççÇsTú…ú¸ë=½zõâ^4š››‹äädÌ™3‡çÈôGß¾}1vìXnî…B¤¤$:–;™T*ELL êêêÐù¢+øùù!)) ÅÅÅ€ï¾ûÆ Ó¿'ëø…ý,˜5k“H$ ëÛ·/1bWW]]Í|}}™­­-sttd&L`¥¥¥0è™  !„b˜è!„B %@„B18”B!ÄàPD!„ƒC !¤SäääpSð%//¥¥¥¼Æ@Ñ ”¢‡òòòàããƒwß}W­|ûöíHHHèôí©T* 0@mÀî”›› Lš4‰f\&„´ %@„衪ª*¤¦¦"11§Nâʯ_¿Žììl#ë?þø#ÆŒƒ[·náÀ|‡CÑ”¢§LMM±råJ|öÙgZë¯\¹‚¯¿þZ­,,, ÷îÝÄÆÆâôéÓØ¸q#°sçN0Æ·Þz QQQ¨®®VkóæM|ðÁ˜;w.~ù嵺ääd# {÷îEÃd.\À¾}û››‹­[·"..Nk¼÷ïßGXXüüüÂÅ™’’‚½{÷";;Ÿ~ú)®^½ªÑöÆصkŠŠŠðÅ_p³5çççcÙ²eJ¥Ø¸q#”J%àܹsØ»w/×þðáÃ8yò$·¼mÛ6deed2.\ˆ·Þz kÖ¬áfÏ%„<Û("D-X° …‰‰‰uiiiøþûïÕÊ"""pÿþ}ÀÁƒáçç‡ÚÚZLŸ>puuÅÙ³gáããƒýû÷cË–-jí—-[XYYaΜ9¸páàèÑ£˜?>F ©TŠÈÈHlÞ¼ÀãD,$$³gÏFVV·ý§©T*xzzâúõë DFFƺº:X[[ÃÌÌ vvvpuu…X,ÖhŸ‘‘•+WÂÇÇiii¸ÿ> ÆŒƒÊÊJH¥Rüúë¯Ü«AŒŒŒʵ Åš5kJ¥!!!°¶¶ÆŸþ‰9sæÀÓÓo¾ù&Š‹‹qþüùVïBèmð„è1¡Pˆµk×",, ³fÍjsûððp,Y²Àã$"??{öìXZZbóæÍX¹r%·þ±cÇлwo@II ¢££áááU«VaݺuˆD"„††bÅŠ‰D‚Ó§OÃÄÄDk‡BAA._¾ @€7Þx¶¶¶øé§Ÿàïïþýûãå—_Ƽyóšü^lmmqîÜ9ˆD"À¦M›àààÀ}?S¦L­­-.]º„Ñ£GãÁƒËå`ŒÁÆÆYYY(..Fff&† kkkddd@"‘`öìÙ077ÇÌ™3ÛÜÇ„~Ð Bôœ¿¿?D"öíÛ׿¶ÆÆÆÜ׿ææþÿ”aaa¡ñÄ•‘‘÷µ‡‡ <¾WWW¸ººbùòåèÙ³'·®M“ÉÈårŒ?žÛ¾‘‘<==!—Ë[ý½XZZrÉOÃgNœ8Q-†^xr¹ÆÆÆðòò™3g¼þúëHLLDJJ &Ož ˜5klmmaooÉ“'#&&=juL„þÐ Bôœ‘‘6lØ€àà`xyyqåB¡>lÓç4·ÜXzz:ìììVVVˆ‰‰Á¸qãZøS¬¬¬4ogggÃÛÛ»]Ÿ×ð™ ãxàÑ£G¸}û6¬¬¬<¾"tæÌ\½zGŽÁ­[·°jÕ*˜››cÑ¢E€Þ½{ã·ß~Czz:þúë/„……A àí·ßnw\„îAW€1Ó¦MÃÀÕÆ?7oÞDYYêë뇺ººm§aPôõë×qìØ1øúú|}}±nÝ:®¾¦¦lõçz{{#==2™ ÀãÕiii˜:uj»c}íµ× “ÉpíÚ50Æ ccc¼ôÒKÜ6:333ôéÓ¯¼ò 233qöìYxzzN:…ììl <óæÍÃðáÃÛ”TBøC !bãÆjOmõéÓpvvF¯^½pòäIµ[^í1uêT<ÿüó1bæÍ›Ç;Ú¼y3zõêGGG¸¸¸ wïÞjOUµdРAسgfÍšGGG̘1_}õ\\\Úë”)SðñÇcÔ¨QpttħŸ~ŠpW€† SSSøùùx|Åëõ×_‡‹‹ wûîÎ;9r$žþy 2={öDPPP»c"„t#Öð,*!„Bˆ +@„B18”B!ÄàPD!„ƒC !„B %@„B18”B!ÄàPD!„ƒC !„B %@„B18”B!ÄàPD!„ƒC !„B %@„B18”B!ÄàüÄ›ÄJ" »IEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/compressed-select-cache.svg000066400000000000000000000760651231437614300264210ustar00rootroot00000000000000 Selecting with small (16 bytes) record size (file in cache) 10 3 10 4 10 5 10 6 10 7 10 8 Number of rows 0 2 4 6 8 10 12 14 16 MRows/s No compression zlib lvl1 lzo lvl1 bzip2 lvl1 PyTables-v.3.1.1/doc/source/usersguide/images/compressed-select-nocache-shuffle-only.png000066400000000000000000001520351231437614300313440ustar00rootroot00000000000000‰PNG  IHDR@°AàÚ²sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝgXg×ðÿ²°€ô.E¥)R"¢5бcìX#FQñÑhbõ±c¢‰±ÄÞE-&j"6éÒ{ïå¼x˜×e©º€Êý».>pÏÌ=ggvgÏΜ¹‡GD†a†a˜6D¢µ`†a†ii,b†a¦Ía Ã0 Ã0mK€†a†isXÄ0 Ã0L›Ã †a†aÚ–1 Ã0 Óæ°ˆa†a˜6‡%@ Ã0 ô9,b†a¦Ía Ã0 Ã0mK€†a†isXÄ0 Ã0L›Ã †a†aÚ–1 Ã0 Óæ°ˆa†a˜6‡%@ Ã0 ô9,b†a¦Ía Ã0 Ã0mK€†a†isXÄ0 Ã0L›Ã †a†aÚ–1 Ã0 Óæ°ˆa†a˜6‡%@¨ŠŠ ÄÄÄ ¬¬¬µCù`ÅÅÅ!??¿µÃh”ääddggsÿÇÅÅ¡   Q˦¥¥!..®¹BãbbbP\\ÜìëjŠòòrÄÄÄ 77·µC›¬¬,¤¤¤´vR×û/##!!!())™‹¢¢¢–¯ÍÈËËC||ü{õ‘››‹˜˜”——‹)ªK€šAYYöìÙƒ¢k×®pttÄ—_~‰“'O‚ˆÕGrr2 &–˜>|ˆ{÷î µEFFÂÙÙÿþû¯XÖ!.×®]ƒ³³3222¸xñ¢È|ÖÖÖøý÷ß[:¼w2jÔ(lÙ²…û¿G8}útƒË½~ý;wÆ¥K—j>yòäz÷ßéÓ§1uêTXYYÁÊÊ uÎ[PPüý÷ß ÆU—ŠŠ ;v iiiïÜGM|>«V­BïÞ½4~èÖ­[‡I“&½óò?þø#&L˜ ƈj CCCœ;wŽkËÏχ‡‡tuuѳgOœ={îîîØ³g7±±1®_¿Þìñ5$$$¤Qq$$$ÀÙÙþùg Dõn~ýõWôîÝû½ú(**Bÿþý±dÉ1Eõqc ˜UVVbÔ¨QðóóC÷îݱbÅ 4ðôôDaaa«Äµwï^øûû µÉÈÈÀÔÔrrr­S]”””`jj IIIU ‘··w+GÕ:¼¼¼àéé‰Ù³gsm………8rä¼¼¼ð믿֚lTVVbΜ9˜6môôô°lÙ2Œ?¾Ùã---ŸqãðêÕ+±õÉãñðÓO?ARRË–-[¿³öíÛ£K—.;/// >óæÍãÚ._¾Œàà`DEE¡  #FŒ€‘‘455›=ž¦:uêV®\Ùà|¦¦¦PPPh¨Z––Μ9ƒ]»váÆ­N«“lí>5OŸ>Å… ¸_Eo{ô褥¥…Úâãã 555ØØØ@JJªÞþ333ñøñcHIIÁÆÆFä[ZZŠW¯^!-- ¦¦¦ÐÓÓCnn.òòòPXXˆ˜˜€¦¦&´µµáãã@qq1’““ѱcG$%%áÅ‹°··¯5AЉ‰ÁóçÏ¡«« ###äååqýÔ”ŸŸÌÌLèëësmÉÉÉPRR‚¬¬,p1êêêÂÒÒ>>>PRRBqq1222¸Ë  ¬¬ eee®¯‚‚A__õn¿j/^¼@BB455annÎ%[‰‰‰““ƒ@ À¿ÿþ‹víÚÁÚÚ’’’ÈÉÉAPPºuëmmm¡þRSSŽœœhhh Gx¿ßçÏŸGHHˆÈÙ¯¼¼<\»v <¯Îe?Žß~û Ïž=CçΛ¼îôôtŸ›FDxþü9’’’о}{˜››sÛPJJ »ví‚««+–/_]]Ý:cÏÈÈ@EE455QQQÄÄDèèèp늈ˆ@hh(tuuaee%²³²²ðâÅ ðù|˜›› }Æ ðèÑ#‘Èç/''ÐÑÑ!11€ððp¼zõªI‰KBB^½zYYYtïÞJJJ€/¾ø‚»TYRR‚¤¤$‘eeee¡¥¥ÅýŸ””„gÏžAQQ=zô9Õtùòe<{ö gΜáÚ233qâÄ ØÚÚ¢´´‰‰‰ÐÕÕÅ¢E‹ //_oyyyxòä JJJ`cc55µz燒’$%%mmmÖÚï£GÀçóaccï ‘’’îØ¡ªª EEE‘>ÔÕÕáããÃ}®«·©¾¾>RSS ;;»F'HÉÉÉxùò%annÎm댌 ¼~ýYYYPUUE=DŽùD„ððpÄÄÄ@__¦¦¦"ýGDD 66}ûö­õ;#::/_¾„––lll„ŽEæææøú믱f͸ºº6êõ|²ˆ«/^º|ùr½ó•——Ó¼yóHBB‚,--IVV–,--)99™ˆˆâãã =þœ[æ¿ÿý/ÉÈȱ±1©ªª’¶¶6qÓ=zDFFF$!!A:t G_}õmß¾III‘’’)))Ñùóç)%%…Ðãljˆè?þ 4}út’’’"eeeRTT¤ýû÷s먬¬¤ùóçÒÒÒ" ’““#KKË:_ë­[·ˆÇãQZZ•””’’­_¿ž›gΜ9ôÅ_ѵk×UTTÐ;wHVV–$$$¸ØÿóŸÿ‘šš1‚444HUU•ø|>1¢Þíž™™I¶¶¶$''Gfff$##CTQQADD¦¦¦4hÐ ÒÑÑ!âñx4xð`Úµk)((ŠŠ IIIÑܹs¹> ©««S·nÝH^^žŒ)>>ž›§OŸ>äëëËý¯¡¡A¿þúk½±º»»Ó—_~Yçô²²2@/^™Ö½{wZ¾|y½ý×”——GÈÝݤ¥¥I^^žÐ¤I“ˆ¨j¿iiiÑÆ…–;~ü8)((PNN™™™’——'%%%255%"¢¢¢"úòË/IBB‚¬¬¬H PïÞ½);;›ˆˆ’’’È‚¨[·n$¨K—."1víÚ•6mÚTïë˜6m 0€¾ýö[’““#ôâÅ ÊÉÉ¡‘#GŸÏ'kkk’””¤þýûSqq1·lõgLYY™”••IVV–®\¹BDUŸMMMRVV&UUURSS㦭_¿žLMMiëÖ­¤¢¢BèüùóTZZJ^^^€tttHUU•ääähÀ€õ¾Ž ””uíÚ•TUUIZZšnß¾MDD+W®${{{""ú÷ß¹ÏFõŸ„„×ee%-[¶Œø|>™››“œœ™˜˜Pttt½ë5jMœ8Q¨ÍÇLJ$%%I ’’YXX‘™™ýøãÜ|€Î;ÇýòäIRQQ!}}}ÒÕÕ%EEEºtéR½ë×ÓÓ£aÆQûöíIEE…ø|> ŸÏm—;wÖº®ê÷þ_ýEDD÷îÝ#4mÚ4’’’"’——§;vÔsee%-Z´ˆ$$$HSS“ÚµkGªªªÊ}^UTT¨k×®¤¤¤D:t ÐÐPnùäädêׯ :pïU"¢;vššyxx@ jß¾=]»v[¾°°&Mš$ô9ëÓ§åääÅJ(""¢Þ×ó©c P3èÑ£ÉÊÊÒ¬Y³è—_~¡/^p_°Õ¶nÝJ:t ¨¨(""ÊÍÍ%;;;š={6‰&@wîÜ!)))îZZZJÓ§O§^½zQÕLçÎiäÈ‘CDD‘‘‘Ü~Ú´iäáá!C] Ð7ß|Ã}`|}}ÉÜÜœ[& €¤¤¤èĉT^^N•••´xñâz ââb’••¥3gÎÑåË—INNެ¬¬¸y¬¬¬Èßߟˆ„ "¢;wRçÎEúUSS#{{{ !"¢gÏž‘„„½|ù²ÎXV¯^M:t ‚‚"ªúRß»w/UVVQUdmmMÁÁÁDTõ倹õ\¼x‘åååqÛþÙ³gÜ: ÈÊÊŠ¼½½¹¶¦&@$//_ï—D] PII ñx<š0aYXX¬¬,éèè•——×Ù_õ—Àرc)>>žJKKiÏž=€;È®\¹’:uê$ô~îÛ·/Í›7ˆªÀèÎ;B}¯^½šºvíJqqqDD”••%”¤y{{“©©)•––QÕ6Ý·oŸHŒß~û-õïß¿Î×@Tõ^çñx4gκwï…„„PAAÍ;—lmm¹©©©¤§§G›7o&¢ªÏÚ±cRee%]¼x‘nݺE¹¹¹¤©©IóæÍ£ÜÜ\ÊÏϧE‹‘ŠŠ effQU€FM7oÞ¤ÐÐPÊÈÈ Í›7“’’:uŠ***¨¢¢‚¼¼¼êM€>|HèÞ½{\Û­[·¸÷åÛ PM.\ YYYzøð!DQQnܸ¨¨(,[¶ ;vtîÜ_}õU“ã÷õõåN5Š»,G…££#<<<ÀçóÁãñ ¡¡QoÒÒÒpppÀÝ»wTæ®^½áááGAAž?Žþýû79Ö)S¦ [·n áêÕ«uÎ///ÜÜ\<~üD@€™3g ]ñôô„¹¹9 gÏžPQQÁ´iÓ¸õ¸¹¹¡´´”+*—‘‘¹¹9BBBpðàAìØ±|>‘‘‘M~=Õ222ŸŸ##£&/"‚´´4vïÞ§OŸâûï¿ÇöíÛñí·ß6¸üôéÓ¡«« )))Ìš5 zzz Ìž=ñññ¸rå àùóç¸{÷.¾þúëzû À¨Q£PPP€°°0¤¤¤ÀÞÞž{¿ËËË#;;Ož<PµMg̘!Ò‘‘¢¢¢| ƒÆ®]»`gg‡nݺAFF?ýô<==‘°°0dff¢OŸ>\ û÷ïG§N0wî\ÈÊÊ‚ÇãaÈ!pvvF`` RSS±páB(((@NN‹/FVV.\¸À­·k×®8~ü8\\\`bbUUUüúë¯pssÃÈ‘#!!! ¨««×õå¤p—ºœ¹÷e]ÂÃÃ1qâDìܹŸ}ö·í‡  C\\\½ÇÈÎÎFnnî;½ÿj:tè:vìKKK¼~ýáááèÓ§’’’Qﲓ&M‚……ÀÄÄfffÜçûüùóÈÎÎÆâŋѮ];(**báÂ…HNNƵk×Þ;nX±bTTTT ###ëý\ÀÅÅ#FŒŸÏŸÏÇÔ©Sabb‡ž={"<<‡ÂöíÛQQQÁõ—™™ÉÝ´P½ï”••…j˜4440þ|ÈÈÈ€ÇãaĈBÇ»€€xxx ??aaaHMMz¿­±Ÿ¥O«jíÚµÃÂ… ±páBäåå!(([¶l»»;о}{DFF¢  @äYW2QýÁ3fŒP»……²³³ EEEôìÙS¬¯¥ú@\}÷Mtt4¦OŸÞä~\]]qêÔ)TTTàÂ… xòä ‚‚‚pâÄ ØÙÙA]]Ý»wK¼õÝ)T}×”££#áììŒ+V W¯^u.#---t÷ž¤¤$¤¥¥¹‚ö„„Œ1±±±ppp@‡ !!QïW ÉÊÊ€:ëªcΜ9ÜÔØØ!!!8rä¾ÿþû&õciiÉÝ~«««‹#F`×®]2dvîÜ 777×¹|QQqôèQ¡dW6gÎÃÖÖÊÊÊèß¿?V®\ KKK¡ùµµµ‘žžÞ`Ì5ëzQ\\Œ}ûöá×_šVýYgMDdd$´µµ…ê©ttt``` ô…X[]VTTT“ˆ˜ššÂÏϾ¾¾X¶l,--1þ|L˜0¡ÎÚ²üü| >ãÇÇ”)S„bóæ —\Vkß¾}ëÇûïíõ§¤¤`ìØ±Bí–––M¾«ïíÏwdd$ŒŒŒ„꜌ŒŒ ¡¡ñ^?>ê[7€zcŽŒŒyÕ2220zôh<}ú}ûöEÇŽÁãñ¸ãDuÌM©Ëy{{äçç#55‡ÆÙ³g…æ«­6²±Ÿ¥OK€š™‚‚\\\`ee555\½z“'O†¦¦&/^,twE}455abb‚Ÿþ¹Îé¹¹¹HNN)ЭF¼¿>***ïô«ÁÕÕ+W®Ä•+W`ll mmmŒ=ëׯÇC¿~ýê]^±U æñãÇñæÍüóÏ?øå—_àèèˆäädî—^c¼ýEçï"DEEq˜ÞÞÞç8«‹-ÓÓÓ›|—^ÇŽ!%%…ØØX.ª–Õ_lMñòåK >œûÞ¼ypqqÁ“'OðÛo¿áøñã"˼½¿dee¡¨¨??¿:oÝÖÕÕÅùóç…þù‡;ã™’’n¾ôôt¡øÆRSSƒ„„¶mÛ77·ZçÑÔÔ¬sŸ©««#-- yyyܾ),,DBBBƒg@ÛµkWk‘rCV­ZoooüóÏ?¸zõ*¦M›iiixzzÖ:ÿ—_~ eeelÛ¶Mäu=ºQwCU{ûýW]xý®455aii‰Û·o¿W?5©««#>>%%%\‘qff&222„ö‰¸ŽQß{hÿþýGdd$w¬Y·n7Ä@õ]táááïôcPNNrrrX·nÈåÚ¤§§ ݠбK`bvéÒ%‘iÕŸ{{{Ü¿_è3VRR‚7oÞÀÞÞ•••¸yó&7íÆ(++ƒƒƒC½ë¶°°ÀÍ›7¹_ú••• h÷æÍäææBII ÄæÍ›affVç—ëÆqïÞ=œôôôê<†7„ÇãÁÑÑ"I_mûú]?KŸvHÌ2220eÊøùùÁÖÖ:::xøð!þý÷_Ì;—ÈjãÆ°³³ƒµµ5FŒ²²2üý÷ߨ¬¬¬u º¹sçâÔ©S°²²Âĉ!''‡   ܹsyyy000ÀòåËñÕW_áĉèׯþüóOHJJâòåËpwwDzeË0cÆ ¨©©ÁÝÝýÆY¾|9nܸKKKèëë###&&&B·3×FBB...8sæ F êKé‹/¾Àï¿ÿ^oäää‡áÇÃÊÊ fffú…S› 6àùóçŸ#GŽ`Ö¬YÜ6¯‹w™pðàÁ¸yó&RRR`ccSç2÷îÝÃ×_ OOO 88ÑÑѵ^J{ôèV¬XÞ½{ÃÏÏkWWWǺuëàç燫W¯Â‚;{tÿþ}¤¦¦r‰gMprrÂùóçß©6ïmÆ èQ£ààà€É“'CCCÁÁÁ¸té"##¹¡0šªG˜1c† ‚ & ¸¸ÇÇÂ… ¹K²ƒ ¤I“0vìXÂÎÎC† y¯×SŸµk×âòåËÐÓÓÃäÉ“QQQ#GŽàâÅ‹˜:u*üýý1lØ0tîÜwïÞEtt4wæñxرc<==OOO„‡‡ãúõë5|ëÖ­prrB¯^½0dÈäççãÎ;ÐÕÕJ¬ñäÉìß¿¿Y¶ÃÇ‚¿fÍš5­ħÄÒÒPVVFqq1ÒÓÓaaaÿüç?˜3g7Ÿ‚‚fÏž @€ÈÈHäææÂÞÞ¾¾¾PQQǃ@ €³³3äääÀçóñå—_B__111HHH€™™6nÜÈ]òrqqƒƒ ‘””„ž={bùòåPRR‚šš¾øâ DDD   ÎÎÎPWW‡””\\\ //eee8;;s ǃŒŒ œ!++ yyyL:ýúõÀðý÷ß#33……… ´§££kkk¡j§N`hh(tPâñxPQQ³³3x<$$$àéé‰ÌÌLÄÅÅÁÎÎŽûbtpp¹ä÷ÙgŸq…à5õî݈EJJ úöí‹ï¿ÿž;…ÎãñЫW/¡_FÕ¿(ß®™àñx\QºœŠŠŠ ,Z´#GŽ„¡¡!W_Âãñ`mm-ô…noo/T¿PSii)¶nÝŠ¹sç ýª/..Æ£G %%ggghjjBRRfff\ššš˜0a¢¢¢‚ž={â—_~©u•·ñù| 6 !!!ÈÉÉÁôéÓ±eË‘º“øøx\¼x‡OÆÉÉ ººº†®®.¡ªªŠY³fqcœ¡_¿~X¶láààÀõ”žž777øùù ½îW¯^aÅŠرcGƒƒî™ššŠ kkkcúôé())ÁëׯQ^^ŽÁƒcÁ‚““ƒ¤¤$¦M› ¤¦¦¢´´'NÄèÑ£!%%777˜ššr—€/^ ¡utìØQ¤ÏÀÀ®®®ÈÉÉAyy9,XtéÒ¥ÎÄÛÐÐúúúHHH@DDttt°wï^nü#X[[#77jjjèÒ¥ äåå¹?555ôîݲ²²˜5k…ŒŒ ôêÕ ~~~õŽÅS^^ŽÍ›7cîܹÜXFÕÌÍÍEê¾zõê…:pñU>ÀÃÃVVVˆ‹‹CTT àçç33³:dzâñx°³³ó©æçhèС000@DD–/_.4hª´´4<<<””„ÔÔT888Ô9†ŸÏ‡‹‹ ”””Àãñ¸Áê×þö1¹®11}útÈÊÊ"!!ÞÞÞprrBûöí1dÈ®þsæÌ™˜2e :uê„=z¨zߎ5 xóæ ôõõ±~ýzhkkƒÇãAWWvvvBÛCMM NNNª’Þ™3g¢²²ááá(..Fÿþý±téR¡ñvìØ¸¸8¬[·®Ö×ÑVð¨%/2½’’äæærר³²²`gg‡iÓ¦aéÒ¥­ݧ¥¬¬ &&&3f 6lØÐÚáqrr‚……þûßÿ¶Ø:yyùF=B„yåååèÚµ+†ŽM›6µv8Œ˜$%%ÁÔÔ;vìx¯Ç±| XÄ4Ill,:wî ¨ªª",, $2Š IDATvvv8wî\ƒ#Ë2Mwûöm|þùç8þ<ØÚá¨z®œ­­-^½zÅÕ´5·ï¿ÿ›7oÆ“'Oꚯ»wïbÀ€8sæŒP­óq*..ÆçŸyyy\¸p¡ÞÑäÛ‚6›¥¤¤Ôz™¤cÇŽb{駨²²OŸ>EXX*++ѵk×zk˜÷÷×_¡´´´Á;åZJhh(âââÞ¹§©ÊÊÊpùòeqc11-çï¿ÿFqq1{lÂ'àÍ›7xüø1œßénÊOM›M€ˆ999Bm‡Æï¿ÿþA?˜a†a˜÷×f šŠ‹‹abb‚|0¿´†a†iì6øÿÙ¾};LLL„’ŸÐÐP„††¶bT Ã0 Ó²dddðù矷vÍŽ%@¨WeãÆ¸~ýºPû±cÇpìØ1˜šš6{ ÉÉÉPVVõ¶)bbbЩS'±Í[ßyõCµ`Á¡'êV[½z5­^½ºEb8pàEGG¿óòM‰³1óÖ7O]Ójk¯Ù–••Å=õ½Ú!CŒG’’’h÷îÝï¼|SöÑýû÷éÊ•+õÎ#®m|ôèQzõêU½óµÔ6®mÝMÑÔ}Ôк®\¹B÷ï߯uZ]ûèÖ­[tëÖ-¡¶ÚÞ·5×=cÆ zùòeƒ1‹Ãû—>–ãEÍ}ôàÁš?~ƒñˆCmhÊ6ṉ¥¹¶qͶ¤¤$ÒÒÒª7–OE›O€"##©]»v&2­% ŒŒ *))yçå“’’Ä:o}óÔ5­¶öšmåå唚š*ÔÖRÛ¸¬¬ŒÒÓÓßyù¦ì£‚‚ÊÍÍ­wqm㬬,*..®w¾–ÚÆµ­»)šºZWnn.Ô:­®}”——GyyyBmµ½ok®{÷îÝïõÚ›â}×ó±/jî£èèh:pà@ƒñˆCmhÊ6ṉ¥¹¶qͶ¶”µùK`ß~û-&L˜PïÓ¬[B}—J£¾';¿Ë¼õÍS×´ÚÚk¶ñùüÙ\$%%ëù¶!MÙGo_Žª‹¸¶qm§è›ò~·÷YwS÷QCëª~¨gmêÚGµò[ÛûöcÝÆM]¾5ù5—ºF{n¬¦lãÆ[šk7Ô÷§¬M'@ HKKÃæÍ›[;”6mäÈ‘­Â'mãæçêêÊÆVifêêêpttlí0˜OD›~¼®®.nܸÁF–mey@'ó~Ø6n~Ïž=CAAAk‡ñIËÍÍEHHHk‡Á|"Ø8@õ¨~Nl}Ï‹ý÷ßñòåË– ˆa>"ººº-6Z4Ã0⑜œ ++«6qX›¾&GŽÁ½{÷Øý ó–„„ðù|–1 óÁb Œ? ,hí0惈­[·¶v-*##JJJ”d‡ÕæRZZŠüüü÷¾i„a€6^Ä0 #.·nÝy¾ #^éééøë¯¿Z; æÁ~ª0 ȇ‡Gk‡ðÉÓÑÑÁ°aÃZ; æÁÎ1 Ã0 Óæ°ˆaF 222P^^ÞÚa|ÒJKK‘™™ÙÚa0Ÿ–1 È«j~¬ˆ'VÄ0 #¬¨ù± FœØ †a†aÚ–1 È«j~¬ˆ'–1"¼¼¼0|øpܽ{W¨ýòåËðöön¥¨>=‰‰‰pvvÆ;wZ;F X Póc5@Œ8± Dl,”ÔüëQPÌÌêŸçòåË’’¢E‹ððáC®=** 7oÞlæÛ)))˜ššBAA¡µCaÄ€Õ5?VĈK€>þþÀ¶mÍ¿[[àÁƒ†ç[ºt)¾ýö[œ¢¢¢ðúõkX[[CKK ðòåK¤§§ÃÁÁÿ‚3%%RRR““ÃãÇQ\\Œ^½zANNŽ›'&&ZZZ••Enn.JJJ ¡¡(..ƳgÏ™™ KKKèè轞ŒŒ ¼xñ`jjÊÅSýz‚ƒƒ‘••…Ž;ÂÄÄ ®®hkk õ•ššŠ'Ož@]]’’šÆç󡤤„§OŸ‚Çã¡GÚæ Ã0Ì[ˆ©ÓêÕ«iõêÕõÎãííMþþþï½.oo" ùÿlmŽEMMNž¸÷ú§&))‰´´´Z;ŒÁj€˜:­X±‰‰‰8xð`­Ó׬Yƒ×¯_#88QQQ¸}û6Ž;†€€€:ûŒŒŒÄĉ1kÖ,$$$ %%wîÜšš*++1qâDXXX 66±±±X¿~=fΜ‰°°0®²²2˜››#''9990`f̘#F ;;ÙÙÙÐÖÖÆñãÇ…Ö#GŽ ''÷îÝCYYÖ­['4¿¿?Æ8pÙÙÙðððÀ¼y󘘈ððpüôÓOðööFJJ òóó1þ|lß¾HLLijgÏ`ddøæ›o`ccƒÔÔT„……!-- £G®uÛ<|ø .Äž={ƒ¨¨(hkkcÚ´iBóÅÅÅaûöíÈÎÎFrr2ÒÒÒpñâÅ:·9Ó2X Póc5@Œ8±K`ˆNª.O5·îÝ?¯ºº:–,Y‚5kÖ`„ "ÓýõWL›6 æææ'''8;;ã×_ÅìÙ³kíóСC___´k×ЧOÀ£GðüùslÙ²…»ôôõ×_cÕªU8zô(Ö¬Y––Æ’%K¸>û÷ïÌ;—këׯnݺ%´nWWWØÛÛìììðÅ_ˆ$»wïÆðáÃ…^cII  „ÈÈH€……*++WWWÈÈÈàéÓ§ÈÎΆ²²2º¿µ‘åå呀ׯ_ÃØØªªªµnËêm£­­)S¦tuu1}útŒ3ÑÑÑÜåE777¸ººÔÔÔàââ‚«W¯ÂËË«Ö~™–Áj€š«bĉ%@ˆ ªþ>4‹-ÂÎ;±sçNHKKsíÙÙÙÈÌÌäŠjöööؼysýEFFÂÎÎŽK~jNãñx\B’’’°µµå’ÚHKKƒˆ„ÚäååQXXXïk³±±ÁÞ½{…ÚÞ®ªŽ©¬¬ “&Mj755EYYddd°k×.|óÍ7ؽ{7LLL0mÚ4|õÕW••Åš5k0kÖ,˜˜˜ }ûö6lV¬X}}ýZ_mÛ³zZ]õUrrrHj‰ z†a˜O»ÆÔK^^+W®Ä† „Nï+((@  **Jhþׯ_C]]½Îþ455^ë4uuu¢££…ÚÃÃÃëíó]£C‡õΣ©© 555<}úTä¯úL‘——bbbpûömŒ7ë×¯ÇÆVVVøçŸŒµk×âÁƒBg˜Þ¦®®^ëö¬žÆ|ØØ8@ÍĈK€˜Íš5 ŠŠŠØµkׯçóaook×®qm¸~ý:úöí[g_öööˆGPP×VYY‰ÈÈHX[[C^^^¨Ï¼yó¦Þ>ßEaa!®\¹‚Ô;Ÿ££#pùòe¡öÌÌL¤§§#++ ‘‘££#|}}1dÈ.É{þü9ÀÜÜ3fÌÀŠ+ƒ²²2‘uõíÛOž,T°víZŒ7®IëªMII ¶lÙ hjjâï¿ÿF^^6lØSSShkkãåË—011Áõë×Õ'aÚ´i|ˆ>}ú@^^ÊÊÊØ¼ys£× îa¢ÕN:KKK‘ˆVÛ»w/bbb0uêÔF¯«¼¼;wîÄ‚ Àçó¹öêKk5Ÿ7&//¥K— µ•””À××êêêPUUÅÔ©S‘ššÊMŸ0a6lØ ´Ì‚ °hÑ"À’%Kðûï¿ãСCèÔ©† ‚©S§âúõëØ¶m:uê„I“&ÕDD ÈÉÉÁÞÞž{ }µ¢K—.øù矽]¦M‹Ž† ÂÃ={Z;"¦™°ˆ2kÖ,œ={–ûsuuEqq1ìíík?11QäÖß'N`ÆŒ¸wïÆŒƒ%K–ˆ$4u100€®®.öíÛ'Ô¾gÏ888@ ÆÓÑ÷ïßGaa!F%ÔþùçŸCAAžžž8sæ ëìãÁƒHKKÃ… pùòeܸq¿ýö7=%%E¤f!==iii€ùóç£{÷î>|8Ξ= øúúB__“'OÆÙ³gk}]^^œœœ`jjŠ/^àÕ«WÐÓÓøqãPóñ~cÆŒÁ¥K—šºy˜&b5@ͯYk€ŠŠ€Õ«nÝ€sçþ¿ýäI #£yÖÉ´*v ìÑU£+ܺ¸µÈz꣫« ]]]À7päÈ\»vkkŒ~ø»ЫW/üñÇ8tèÈÓÚë2{ölÌž=Û¶mƒ‚‚bbbpãÆ lÙ²¥Ñ14Æ«W¯Ð¹sg(((µËÊÊâòåËX³f FŽ hß¾=Æ+V=ˆ±oß¾ØóÖ/ĉ'âÂ… Üž†èëëC^^ZZZ°²²âÚedd ££#Ôö¶ýû÷ƒÏçÃßߟ{øæÊ•+aaa˜˜póš››ãÇlT<Ì»c5@ͯÙj€NŸ-j«E,)~ù¥j:óIa ÐbVY˜ÕcVk‡Á‰ÅرcñÃ?ÀÑÑñ½úêÙ³'¢¢¢=¿‡‡,X€£GbæÌ™8pàììì`nnþ^qÔ”žžŽ:Ô:ÍÁÁüñ¢££Œ`ÿþý8~ü8bcc¹º&'´œººz‹¬¨ù‰½èÕ«ª:Ÿ?þ¨¾}ûXô b—ÀÅÅÅ9r$Œ¯¿þú½ûËÌÌ9ËRLž<¨¬¬Ä0{öì÷Ž£&@€¢¢¢zç100€»»;6l؀˗/#>>¾Þ‘hk&DPYYùÞ±Ö$%%{{{„†† ýeee‰\®,**‚„„¤¤¤ÄÃ|”òò€%KKËÚ“mmỿ€۷[,<¦e°ˆ1kVÕ™¨½{÷¾w_ÅÅÅøûï¿Ñ£G&ÇðèÑ#lÚ´ =zô{ÇR“¦¦&Þ¼y#Ò~ïÞ=ÄÅʼn´§¤¤¨ºDÖX;w9#T\\ÜÄHEY[[ãÑ£G¨¨¨€²²2÷§¨¨(’„½yóšššB…ÞŒø± æ÷Þ5@DÀ¡C€±1°y3PV&<]Jª*1 «*„~›Ž‡Ì‡¥Í'@D„ .À××3fÌ@vvvk‡ÔªvïÞC‡ÁÓÓgϞűcÇpìØ1<þ¼Ñ}?~7nÜÀñãÇáææEEÅ&ŸÁ166†‹‹ V¬XÉ“'7xéæÉ“' DLL 233ˆ{÷îÕ»ŒÞ¼y#ryîöíÛ066ÆÌ™3€³gÏÂÇdzgÏÆ AƒðÙgŸ5úu 6 çÎþ}ûpìØ1 6 §OŸnôòu™ìY`Í~öä àèxyµ]¦06m€Y5JNŸþwóóihÓ5@¹¹¹3f ž={///£¢¢¢µÃjUÉÉÉèÛ·¯ÈhÎ^^^077G·n݄΀|öÙgÐ××çþwrr‚ŠŠ æÏŸÔÔTØÙÙáþýûPRRªsêêê"·œU·ˆWTTˆ$OrrrprrsçôéÓ¸{÷.@UU7n„¡¡!úôéSçzŒŒÐµkWüòË/ðóóãÚ§L™iii\½zwîÜA~~> °hÑ"Ì;—;Ãbhh(r¶EOO½zõâþ8p üüü°uëVHIIaÊ”)øì³Ï¸Âe°²²BçÎ…úéÙ³§H}Rß¾}¡®® êòÝýû÷ñÝwßaãÆHHH€¾¾>FŒ!T„[ZZŠ'NÀßß¿ÎíÀˆ«j~ïT”™ ¬\Yu§¶ËÑ;[¶ÿ»áãâUÝ T Šxð Pc( æ#Fm˜¯¯/¹¸¸Pyyy­ÓW¯^M«W¯®·oooò÷÷o†è˜–ðÛo¿‘¢¢"%&&¶v(Íbýúõ¤¯¯O¥¥¥-ºÞ+W®››[‹®“a„TTíÙC¤¦FTuñKøOF†hÕ*¢ÂºûØ´IxCC¢ÊÊ–{ ­ ))‰´´´Z;ŒѦ/íܹÓ§OÇ?ÿüƒëׯ·ùË_mѸqã`ccƒ™3g6K±rkzòä Ö¯_mÛ¶±èÀj€š_£k€îßzöfÏ®} wwàåKÀϨ¯¦ïË/…‹¡#"€›7›7óaj³ PFF233±qãFøûûcÓ¦MèÔ©“ÈõåƒbÍš5Bo jÁ¨q“ÀáÇahhˆÿý·µÃ"B`` ¶lÙ‚á5‹9[PÍÏ˧üÿ­[·°bÅŠ&žOñÿåË— £Eæ_²˜<°·?†ðT`špå pö,``ÐðúwìÞ(u T ÝÚÛC\ÿ_¼xkÖ¬³³3~üñG”Õ,ÿDñˆj ÛF„……ÁÔÔ%%%ÜèÂk׮Ņ ððáCÿÿæ¨m$Þj ,@§N°`Á‚æ™a>غu+[;¦-(/¶o¯:£“›+:]^¾ªhá¦?ÜôÎÀÙùÿÿ—’âãMÍ÷ ùC•œœ ++«ϬµµÙ3@Õ#§½UÕß«W/ÄÄÄ´RD Ã0L“ݸQ5žÏâŵ'?ãÆ¡¡À²eïödw''ÀÄäÿÿ/+«z@*óÑk³ ¼¼<,--qþüy®íæÍ›°´´lŨ†ùX± æ'Tôæ 0z4пU=OMææUƒ94áQ>µªyK|@@UY4óQkÓ·ÁïÞ½îîî B\\BBBØ){†aÞ {XóKOOGÐýûöê°aPX(:“²2°v-ðÕW€¸ÿœ<X¾¼ê¹`\¿ (žþ™VѦ ;;;<þ7oÞŸÏÇÀ¡¬¬ÜÚa1 óbã5?G0ì›oªšx<`êÔªÄHCC¼+VU<<€Ã‡ÿ¿mß>–}äÚì%°jZZZ7n<==Yòó?111by\C]rss‘˜˜Øèù k}dÅ»*++CLL òòòš´\JJÊû Ã_Cll,ÂÃÃ…n¿ŠŠBdd$ÊËËÓèK*¥¥¥ˆ‰‰A~~¾Øâc˜Ê·ßÆ՞üôì ¢4óÑhó #¬¼¼øûï¿›m{öìÁ Aƒ=`` ºvíZë´]»vá¿ÿýo“Ö?þ| <¥¥¥\[ff&V¬XGGGtíÚ®®®ðöözœÆäÉ“Ens~/_¾„““Œaff†¸¸8<|ø½{÷F·nÝ`bb‚ׯ_ÃÀÀ‘‘‘ê“ÏçcÑ¢EpvvFIõiz¦E± fT^üôJýÑШJzþùxköfáètë&+†þ¨µéK`”;€“'›=ݺ»v5ÿzšÙµk×ðàÁüðØ8qb£—»zõ*8€àà`®V#-- ½{÷†œœF…Î;#,, øë¯¿ðèÑ#±Æ¾ÿ~¨ªª"-- ²²²àóùðõõE·nݸ˱M9CT%@‡†¹¹9Ö®]‹õë׋5f¦a¬¨ýñšŠtA†ñùÀœ9ÀþSUóÓRfÎÞò$ ðñ$ع„K€>UãM4·&^ÚJIIAQQ‘H»¶¶6÷,®¢¢"ѽZFFž}úÀÜÜû÷ïçÚ qìØ1‘§À¿¯«W¯¢G"O^÷ôôŸÏ‡™™V®\‰Ó§O×Yt;v ;;ÉÉÉHKKÃ¥K—ÃüMMMìÚµ ÙÙÙØ¿??~ >ŸßÿÙÙÙøá‡D–»qã–-[†;wî 44)))èß¿?æÎ+4ǃ»»;®^½Úè˜ñ`5@ÍäìY  ªj€tu¾}[/žšÅÐ/M¼dÍ|Ø%°ży@K<³IQñõööFNN÷åúçŸ"&&§N‚ŠŠ €ª37ëׯÇÉ“'1þüFõ;{öl¬Zµ 7n„´´4Nœ8999¸»»¿s¬µ‰…‘‘‘H»šš"""°iÓ& 55àææ†M›6 erssC¿~ý¸åœqõêULš4I¬±Ö´ÿ~¸¸¸@YYaaaüöÛo(--åçFFFBÅÛLË`5@Íä­[ÏÓõîau\"n}úÝ»/^Týÿ¿møú¶^LÌ;a ЇÂаêïõóÏ?ãèÑ£xøð!Ww 999XYYqó)((ÀÜܼÑw/ÀĉñÍ7ßàìÙ³3f ~úé'L›6 ’’â}{feeA[[»Öi:::ð÷÷‡¿¿?RRRp÷î]øùùaôèÑ ­³O999¤¤¤ˆ5ÎÚDFF"..cÆŒjïÞ½;rrr ñÖ­¿ÚÚÚBxaZ«jiiU56ÿ£`ØêÕ­OµY³€¯¿þÿÿ÷ïV¬`ÅЖ1 zøð!æÍ›‡'NAQWWGAA’““¡££¨¨¨@TTFŒÑèþ1nÜ8ÀÚÚ÷îÝÃo¿ý&öס¨¨ˆŒŒŒçÓÒÒ‚‡‡ÜÝÝ ƒF­ƒÇã5Ë“”555amm½¨7ÈÈÈàÎÈ1ÌGíøñª3,ÕÌÍ«þZÛĉUÏ«‰úÍ›ª§ÌÒºq1MÂÒU¦^©©©9r$|||0¤Æ‡ÛÖÖׯ_çÚîÝ»‡œœômâ5úÙ³gãæÍ›ðõõÅçÿÇÞy‡GU5qøM%é%ôÞBB³Ð»tR0Q‘ÀÏŠ¨(ÒK@Z ôŽ¥÷ŽTé$Pôì÷Ça³{7m“ìæn9ïóìgö–Ù“À=ç73íÚ¥šý•J”(Á7’ÙýõWþN!oëÖ­äÎ[‘–õêÕãÚµkI㇞9‡õhÖ¬kÖ¬I¶²såÊ•dÇÞ¸qÃ,ó'I©2ú•—ØÞ½MZŒ4ÓäÏ/ÅÐ6€\’¤ÉàÁƒ çÞ½{ Q²¯¯/5bìØ±øùù±eËòçÏÏ’%KèÝ»7M›6ÍÐ}¼¼¼ðòò"((ˆ7¦{ü´iÓxøð!—/_&44”/¿ü’:uêЧOŸTÏiÑ¢“&M",,L±etîÜ9FŒAݺu©[·.9rä`ÿþý\¹r…ùóç+ô5éÑ¥Küýýyþü9111lß¾—)õ+Ê Ÿ|ò ÁÁÁÔ©S‡~ýú‘3gNŽ?ÎÁƒyòä‰âØÍ›7§™A'1RdbþûÒoÓ†ãû÷Ó¹sgõüÒâï‹éÆ[¶ÀÝ»Pª”j.I2† €$ ™8qbR¦Tûöí©[·n²ã´)ã&L nݺlܸ‘¨¨(f̘¯¯oš÷xë­·È›‚û§Ÿ~âŸþáwÞQØ«W¯ž¬sŽ9psscÀ€I6—4ïÛ°aCJ•*ÅÂ… ùâ‹/’ìsæÌ¡_¿~ìÙ³‡»wïIÏž=ñññQdŒõë×/ÙÖRÇŽyñ*CDµèÒ¥KóçŸâîîÎÁƒÙ¹s§¢’õ矮ÐM˜GýÚDùóçgâĉ.\WWW8ÀÊ•+9tè÷ïß§~ýúÉ 9r„+W®Ð½{÷4çBbz¤ÈÄ,[¦7iB‰zõ°€ÐGШÔ® gÏŠqB‚Шê–Äx4Fm',•€W¿ÈiüB1‚råÊ1B¿:¨ÄbY°`Ÿ}ö—.]ÂÝÝ]mwLŠF£áÍ7ߤZµj,\¸PU_¶mÛÆ/¿ü¶mÛTõCbÅT¯ú sæÀàÁêù“üú¥(J•ÅMÕ…^BCCñðð ÔúœI Ä®8p 6ÄÇÇGÑ Ì;v,wïÞåÇTÛ»Dj€LȉÊàÇÕ¼½‰µ –~ý wnÝøî]È@]0‰ºÈHbW888°lÙ2† fS©âQQQ4lØÝ»w'm›I²—={ö¡¶¶ø™wÞgÿþýêø”yóBïÞJ›C[ 2’Ø… ¢k×®Êî²træÌI—.]R,ô(ɼ½½¥Ú$&ŠJÛ«†Ç%J”° ´>†•¡·miñ‹G@‰D"±vï†Ý8_>Ë®¯S¿>è'Š$&Š.ñ‹G@‰Db¤ÈDnõènn–§Òb¸ 4¾²€£Ä"‘D"‘˜©2ÑѰfÒ¦×ùÝâ4@Zúö…W-‚±‚eD=3‰ºÈH"‘HL€Ô™€áÙ3ݸdIhÖ,ih‘ €×^Ã"¬R mñÈH¢ &&___.^¼˜©óÙ»w/÷îÝËÐy‘‘‘üóÏ?¬\¹’½{÷“î9«W¯æ»ï¾3úûöíãÓO?Mñ½[·nqçΣ¯°k×.üüü’ÕËgÇŽqàÀd•š§L™ÂêÕ«3t¯”HLLdûöí¼÷Þ{ôïߟèèhâããY¿~=>>> 4ˆÛ·oãëëË3ý‡JDDD0tèP‚ƒƒ³ìŸD’a {öéc= F ·Ávì€ZïH,+ùͲ}îÅÄp:2Ò쯫QQiúG`` !ú"Ä KóæÍ3ô€ß·oEŠ¡cÇŽLš4‰-ZP¹reΟ?Ÿæy'OždëÖ­FßçêÕ«%³GFFÒ­[7f̘aôµBBBèÝ»7uëÖUTœ2e eÊ”¡[·n|ùå—4iÒ„¢E‹òÉ'Ÿ$³yófNœ8aô½Rã§Ÿ~ÂÏÏÒ¥KS¾|y4 cÇŽeäÈ‘T®\™’%Kòøñc‰Jçç®%_¾|´jÕŠþýû§;ÿ%R”E?Túèmk€¼¼ÄK‹F#ÅÐŽl…a!üxç¿Þ½köû4Ì›—Þžf»¾››!!!)¶ºHܹs³hÑ"zö쉣£#!!!4hЀO?ýTÑhÕ4oޜÇM›6mŒ>o„ Ô¨QƒôªÀ1aÂéÓ§...DGG³iÓ&öìÙcrß×®]˘1cøðöo¾ù†w_5j<}út†¯Û£GÖ¬YèQ£2`Ú;²XYµ ô‹“Ö¨-cÂÃÃ9~ü¸enƒX2D7^¸&M‚tÚôHÔA®IR$<<œ‘#GòÆoÐ¥KN:•ôÞèÑ£ñõõMöÒnõLž<9iõàñãÇøúúrèÐ!† F£FèÑ£‡b‹ÍÓÓ“^½záøj©»xñâtíÚ•«W¯íïîÝ»ñ÷÷'!!Aa bÒ¤I©ž·dÉþý÷ßû¥Fhh(‹-Jv݃R¶lY Ô—ÌÍÍ ooïd«K†ùóçÓ®];zôèÁöíÛïðÁ?~\a=z4»ví`̘1=z” 6àëëËâÅ‹:t(W¯^eÙ²eøúú²ÆPLúа°0†Îo¼A«V­˜5kV²c&OžÌ¶mÛä*P ,b˜ýe°ú¬ÒÒ§Ði …õëÕóG’&2’¤ˆŸŸÏž=£K—.œ;wަM›ˆæ¤I¯cÇŽ±oß>Ü^¥ª.[¶Œ¯ö¾_¼xA`` íÛ·'11‘N:qìØ17nÌóçÏS½ÿ±cǨZµªÑþV«V °eË…}ܸqI~¥D©R¥(W®9rä0ú^›7o¦dÉ’É:Þ·nÝš[·nñûï¿§«aš>}:+W®¤]»v+VŒvíÚqåÊ•¤÷ÿüóOnݺ¥8'88˜K¯ÚT¯^J•*…‡‡¥J•Jj¸Z¶lY<<<(^¼x²û†……Q»vmîܹÃgŸ}FË–-™4i?ÿü³â¸Š+Ò°aC6lØ`ô¼H$™æÖ-ÐÏîrp™UÖFž<É7)†¶X䘅P2Gêè§Qš‰*¯º¸§ÇâÅ‹éÖ­ ¾æÏŸŸ… 2jÔ(E·÷Õ«WsëÖ-<˜Ô!>%Ö­[G³WÙC† ¡hÑ¢¬\¹??¿dÇsüøqvìØaôç*Q¢:ubÞ¼ytêÔ Ú¢[·n1pà@£¯c ÇŽãÍ7ßÄÁÁAaoÛ¶-~~~Œ9’‘#GR£F êׯÏСC“­0ùøø0WOpüøqÖ¯_ϨQ£ŒòaÀ€ 2„N:Ñ¥K4hÀðáÃy÷Ýwyë­·€ä[`“'O¦T©RÉV‡~ûí·dñ7ß|“ÇåDh€ò厳³üo5Ã,_.43ZÞz Ê•KvXll,‘‘‘,X0û|Ë(þþ ¿ªºk\¿+ªç“$Eä¿T aTéÒŒ*]Zm7’Ð×𸹹áááÁ¿ÿþ«8æÂ… 8¹sçR»vm£¯W¤HªT©’b¦Ù¾}ûðññICg:vìHHHÅ‹gþüùtïÞ¢E‹fè:é–bË gggæÎË„ X¿~=gÏžåï¿ÿfÞ¼yLœ8‘‰'&kد«L™2™Î¼ËGŽ!::šÞzý‹þøcV­Z•lKÌÄÇÇãêêšêû¥K—æã?„ÖgĈLŸ>¯¾úJ`è“ÒªFÿ±‰xþü9^^^x{{+ìÇOÒ`iqqqQüÌ%ic8§#9sôµf..ðJÄoˆÅk€´øûë €E‹`ÊÑÕ^b1ÈH’.ÑÑÑ}ú0qâDZ·nÍ¥K—’^  ÄÝ»w7nþ†…ÉR &&†èèh‰O*&˜µjÕâäÉ“Éìü1ï¼ó[¶láþýû„„„Ä7ß|C·nÝ2TàwÞaéÒ¥„……qõêUFŒÁÍ›7>?5†κuë˜>}:ÑÑÑÄÄÄpðàAE*½–S§NQ§N,ßÓ^u€2F#ô?ú¼êüž]HŸ\¹’)†¶8d$I‘™3gâææFùòåYµjkÖ¬IZ ˜6máááÔ®]›âÅ‹'½Ž9’êõfÏžM¾|ùhР×®]cÓ¦Mä΀³gÏ’ÀèÑ£©^½ºâµ1ƒýtŠ)B÷îÝyùòeRÀ–åÊ•#gΜ=z”Ÿ~ú‰œ9s&m_¥Æ;ï¼Ã¹sç’ Œ}||xúô):u¢dÉ’”(Q‚÷Þ{®]»²`Á‚ }"##)Z´(5kÖÄÑÑÑ$«1M›6eùòåüüóÏäÊ•‹†2eÊ$ÓÖd„ÿþûâÅ‹§™a—YÂÃɈˆ L™2ɶæFÅ®]»R\é2†mÛ¶ñË/¿°Í°²¯D¢ÏàÁ0ožnüÞ{B/c+¼ñègR~þ9üø£zþAhh(ÉZüØ"rH’*îîîT¯^=ÓÁ! °‰àà›o¾aãÆ)®P¹¸¸P¶lYÊ•+—¥à B… f ~@d¢U¬X1YðsâÄ ¦OŸÎ÷2kEbNbbÀ°eN*Ù_V‹á*ТEâsK,IÌŠ‹‹ uêÔ!W®\j»bRjÕªÅôéÓùá‡2Ý7͉ˆˆ`ìØ±L˜0Ö­[«íŽU!5@dËxúT7.^Ò)}a5 -½zAþüºqx8¤R¡]’ýÈHbVÜÝÝ9}ú4ÕªUSÛ“3dÈöíÛgS™RùòåcÛ¶m™*C`ïH P1ÌþêÝÒYm¶* @Μп¿Ò&ÅЃ €$‰ÄÈ^` "6mRÚŒØþ²š:@úè7G!ü~ÕÒF¢.2’H$Iö²zµR Sµ*xy©ç9yýuÑÚCŸ9sÔñE¢@B4›6m² żDb,Úf¸ö„ì–Œl}aˆUôK 8p@7 „o¾45KÌü—šE:uê„»»»ÚnX5GŽ¡aÆj»aÓd÷×­[— *dÛý,Ù ÌHîÝÛ@úYE/°”èÙ†‡'OÄøñc± –FÑG‰ù±ëèÎ;Ü»wOaËŸ?†»-[¶¤eË–¦vM"‘X²˜‘,_ú}ßxŒ –­Rb¥gÀøõWmöl©Œ]@3fÌ`Þ¼y”*U*ÉöÖ[o1cÆ ½’H$&“Û_V¿¿2Ú¿.^„5ÔóÉα{´··7§OŸNzÉà'û‘ú)ó#çØüÈ:@Fpñ"è·qvµrŒÄêêéS½:4n¬´É”xU±ûH¢>ëÖ­SÛ›Gαù‘u€ŒàÏ?•ã6m pa£O·º:@†V†^¼¢¢ÔñE" ?ÿü“¢E‹R£F ¾þúkõ÷¦]»v±nÝ:ÅK9Îúøƒ>°(lq¬cKñÇÇÞÞÞìÛ·Ïbü±¸±Fúùó•ïè-Ó»ÞÑ£G ‹ú|ÆŒ]]AO$¿îéS RÝ¿3gΰnÝ:ÆŒömÛHHHÀ°k ÐСCùè£puuåìÙ³ <'''ÆŒ“tŒ»»;åÊ•Kõ†ïɱ˱Ëq ã(÷ð¡nœ'åzö´ÿ²c\¹²hø:mšƒØ{ï=Uý+T¨†˜˜ .Œƒƒö€ì¯Ç÷ßÏîݻپ};`\7xIÖ •¥ÌŒœcó#ë¥ÃС0k–nܯ,Y’¡KXm }._ÃLã³g¡V-uü1@vƒ· ·»nß¾M±bÅTòÆ~‘úó#çØüH PÄÅÁªUJ[&²¿¬^¢êu³fJ›C«‚]¯Õ«WvíÚQ©R%Î;Çìٳٿ?€\’H$“°q#è×ï)ZîßO·ù©Í²bôé£çË'æ#W.õ|z…\²&MšÄ‹/ؾ};NNNœ:u*)ø‘H$‰‰ÈDçw›¦{weö[D„Š$ÙŠ]oVwèÐ:¨í†Ý#õ)æGαù‘ Txþ6lPÚ2YüÐ&4@®®àë S§êl³gàAª¹dØõ Ä2úó#çØüH P*¬Y£¬uS¹24h©KÙ„HË! Ÿmuô¨²H¤ÄìÈH¢:úu€$æAαùñöö–PSÂpû«oßL_Êj{¥DåÊм¹Ò&ÅÐÙŠ €$‰DbBCa÷n¥Í^zƒaeè¥KáÅ u|±Cd$Q{È6P9ÇæGöK+@¿ªpƒbå#“Xu/°”èÖMdÄiyþ–-SÏ;C@Õ‘úó#çØüH P ˜¸ó»Mi€\\`à@¥Mnƒev](=d ‰D"É$†œàÞ=Åf•\¿.VÅôÅ'N€§§*îÈ:@‰D"‘díœV­dð“+йÑG®e 2’¨Ž=|ÓP9ÇæGj€ 0Üþê×/Ë—´9 C1ô²eB$1+2’¨ŽÔ§˜9ÇæGj€ô8|XlíhÉ• ºvÍòemN¤¥sgÐ/T)ÅÐÙ€ €$ª#kÔ˜9ÇæGÖÒÃpõ§KÈ“'Ë—µ©:@úH1´*ÈH"‘H$¦#>V®TÚdíŸôYññP¦ „„èl3fÀ‡f› ²D"‘H$eýzeðS¡‚ ~2‚³3¼ÿ¾Ò&·Á̆ €$ªcß4ÔFαù‘ àÏ?•ã,t~O ›ÖiñóG½GóÙ³¢¬€ÄäÈH¢:RŸb~ä›»×…… ´>&Îþ²y @ٲЮÒ&WÌ‚ €$ª#kÔ˜9ÇæÇîë­\)4,Z¼¼”½ÀL€ÍÖ2ÄP ½r%<}ªŽ/6Œ €$‰D’uLÜùÝ®éÐJ–Ô£¢`Éõü±Qd$Q©O1?rŽÍ]k€®_WêT¡wo“߯.4@NNR ÈH¢:RŸb~ä›»Öö­jÑŠ7ùmìB¤ÅÏOBZ.\€ÔóÇ‘Du¤>ÅüÈ96?v­2Cç÷”° @éÒо½Ò&WLŠ €$‰D’yŽ‡Ë—uãœ9¡{wõü±% ÅЫVÁ£GêøbƒÈH¢:RŸb~ä›»Õ®þt꯽f–[ÙHKûöb%HKt44m W¯ªç“ ! ‰êH}Šù‘sl~ìR” úUécÆì/»ÒÐùù)m.@ƒ°y³:>Ù²XÈ^`‰D’;w*;˜,¡¡à⢞O¶FX´j%*Bëãà0~¼ø»‰½À$‰D"I ”ãwß•Á©)RJ^V@£‰¡kWÙ1>“ÈH¢:öðMCmä›»Ó=|ÁÁJ›™‹ÚHK®\°|9LªLذAl‰ýû¯:¾Y12’¨ŽÔ§˜9ÇæÇî4@ B\œn\¥ ¼ý¶Yoiw CFŽ„; pa¥ýòehØÖ®UÇ/+E@Õ‘5jÌœcócWu€4˜3Gi2Äì·µ«:@©Ñ¢…(=àé©´?=zÀر˜¨ŽoV† €$‰D’1vî„ÿþÓsä__Õܱ;Ê–U¡û÷WÚ5øæÑKìÉu|³"d$Q©O1?rŽÍ]i€ +{{C6¬~Ù­(%ÜÜ`ñb˜>œ•ïmÛõëùsêøf%ÈH¢:RŸb~䛻х„á­>†‹Í„Ýk€RbØ0ص ŠSÚ¯_‡F’×i’$! ‰êH}Šù‘sl~ìF´`è¯tÕ¨gË­¥(š4º  ”ö—/¡OøüsQ´R¢@@‰D"1ŽÄD˜;WiËñ³ÄJ•‚þ÷ßOþÞO?‰‚•ááÙï—# ‰êH}Šù‘sl~ìB´};ܺ¥çÌ dÛí¥(rä€yó`Ö,puU¾·{7Ô«'Oªã›" ‰êH}Šù‘sl~ìBd(~~÷](P Ûn/5@Fâï{÷BñâJû­[ðÖ[B<-‘½ÀÒBö“H$’WÜ»'Ò¯õµ$Âo¨ç“$mBCE†ÞÉßûøc˜6-YëÙ L"‘H$}æÍS?µkËàÇÒqw‡={`èÐäïýþ;´l d¿_‚ €€iÓ¦ñÅ_¨í†Ýbß4ÔFαù±i PB‚%»¥ IDAT€ôɦÔw}¤(¸¸Àˆì½9”ïíÛ^^päˆ:¾©ŒÝ@‹/& €£GªíŠÝ"õ)æGαù±i Ж-p÷®nœ;7ôë—ínH P8P<¥K+í÷î‰4zÃì>;À® ={ö0mÚ4&Ož¬¶+v¬Qc~ä››®d(~îÝòæÍv7d ,R¿¾¨Ô´©Ò+Ê ‚ƒ~ƒ[Ç& §OŸrà•ÈK£Ñ°}ûv6nܘæ9/^äƒ>`íÚµäÏŸ?;Ü”H$ëãömغUiSaûKb"Š…¿þ‚áÓ¿7w.»u#—äFÙD4kÖ,æÏŸ@pp0ýúõã믿ÆÏÏ/ÅãcРA¸¾* ¥ýe1ü%’˜–Y³fÉ-3#çØü¬^½šæÍ›ÛÖ6X|¼H}¿_g›=[µêÏ÷ïßçøñãrÌ”œ:Ý»ÃÍ›I¦Ñyóò½­êÙô°‰ 777bbbˆŽŽæèÑ£¼ù曀Ø»¯ÿ÷Íš5ãõ×_çæÍ›Ü¼y“ððp¢££¹yó&‰¯¾éH²ù`6?rŽÍ··7 ÚPð°q£2øyí5èÛW5w¤È Ô­+tA­Z©íI¶ã¬¶¦ k×® 2„Ù³gS½zu^ýuîÝ»ÇÝ»wqwwOv|ÇŽ“V@ls½xñ‚ï¾û.;Ý–H$6Ä_Á?Šn6ƒ¡ø¹o_È“G_$æ£P!ض ÆŒ¿Äv‚M¬uìØ‘3fкukV¬XÀéÓ§ùî»ïp1¨r)±ŒÊ+‰²qr‚~àéœ9ØK˜Õ@OŸ>eË–-$¼ªLÚ­[7ÆŒC™2eèСü±Q×òõõeïÞ½ærU’²Fù‘slá³ÏD¡Ýøø=@S¦¨í•‰˜;ô%¢ €‡‡zþ ëeÑ:±ÆÍMm7²«A?}ú”=zpåÊ ÀÀ©T©’Iï!EЉ$5^¼;B6$oÏhÖ,Û]2qq¢`ž~›„ùóaÐ õ|’d‰Má/Ãyøâ!a/Â{–ô÷‡/&C"B[FÄ^ÛA[­(þüìÚµ‹7n°páBZ¶lI¹rå4h={ö$W®\j»(‘Hl”û÷¡cG‘@“S¦Xy´~½2øÉ—O?”X š½|”j cä<ŽzŒãÖ;ò:e‘K5°ÚHKùòå™1ž/šâK?Èyõ˜DÌZÎ Viqtt¤M›6´iӆdztéR†Jll,#GŽd\ºµXÖ­['Ó´ÍŒœcÓ°q£ØöŠŒTÚ½¼ 2r—/7D*ü”)É ([×®Á®]J›…T~·Ê:@£§Ôh_!‘!œ—/_2eÊbccùâ‹/ðôôÄÓÓSm%‰•’Ÿ|ü‘ü½‘#á‡ÀÑQô—lÜX4ÜÖòõ×BNc5ÄÆÂ¢EJ›…¬þd'‰šDÜ9ÀõÇ×S\© åYÌ3U}trp¢P®BIKZAMÑÜE)³8¤{ÝÐÐPÒ?ΰ‰háÂ…Iõ~fΜÉöíÛiݺ5ýû÷g‡Íå°]¤>ÅüÈ9ÎϟûïŠqú8;Ìʎ=⫯òѾ½î¿Õ ą̀S'›Î*kÖ@X˜n\ €˜ ÁÜ «¯²èô"–œYÂgwÌr´pvt¦Xîb¸çqOñ¥ÔÌYG«­dcØDtëÖ-|}}ÑgìØ±ôèуš5kòâÅ rçέ®ƒ’4‘úó#ç8ãܾ-ÄÎçÎ)íùòÁªUкµÒ¾gÏš7oN£F…8|Xgÿúkq¼U`(~~ï=° š0æÐ=‹yÆÊ +Ytzï4Ùuõ)˜³ ")ž§xŠN¡\…ŒZ¥‘˜›€Š-Ê¥K—ðððàôéÓ4hЀgÏžñìÙ3Y8òÁl~ägŒcÇ sg0, ]®lÞ 5j$?ÇÛÛ€ñã¡C=8.^Lù‹âòe0,kaÛ_¦ê–¨Id×],:½ˆµÿ®%*>*Ã×Èå’+Õ•ýW±ÜÅpurͲÏÓcŸŸo½õx{{SºtiŽ?Nll¬\ö—H$bÍè×¢ ž‰ =OÑ¢iŸÿÎ;"+ìÄ 1ÖhÄ*вeæñ×dÌ™£7iÕª©ã‹™ÈÈW~·üt¬Ò‘2ùʤؼæúZ6y-16ÕªU‹‹/rçΤÕ'''Ö®]‹ƒƒ\N´t¤>ÅüÈ96Ž~€/¿Tv€èÕKhƒÓÚ zôèùòåÃÙÙ™qã [7Ý{+WB@T©b¯M@L *m¸j˜ PF¶¸œœh]±5¾¾t©Ú7gËÙþ“˜›PPíÚµ‹3gÎðúë¯ãää@ݺuyÓj«Ù²O•ù‘sœ6ññ0x0Œ<ø;–/O_ ³gÏ""Dû€.] vmÝ{‰‰ðÍ7&vÚ”¬Z鯅 C÷îêù“ ÆöKÔ$²ó¿ø¬ñÁ}ª;C6I3ø©Z¨*ß¶ü–[ŸÞb«ÏVzÕì%ƒ;À&ê­[·Ž©S§&A-Z´ E‹¼õÖ[¸eAÀ'ëI$¶ÏÓ§àí¼öŸ««Øzï½Ì]wÕ*e•³3\¹åËgÞW³Ñ¸1蟮¬”£GÃGÁ«^ÔªqåÑϲøÌbî>»›æ±ùrä£×ë½è1F¥dÇ-¡¡¡xxxj(€³Alb ¬k×®tíÚ•ØØXNœ8ÁîÝ»éÝ»7ÏŸ?'::Zm÷$‰…rã†,ÿû¯Ò^° Ð5mšùk÷èÕ«ë®ß~›\j£:/*ƒe~:L*¶ÝÜ`Ò$3ø—1]2j‹ËÑÁ‘VZáëáK·jÝä*c@TT[·n%88˜={öвeKÞµ ú’Ô‘úó#ç89‡‰­*ý²7•+æM×ëèk€@G;VªµŠ,±Ò¥³è¼)1L}oÑBL‚,Y" Gƒh?a¼R!˜­(üÊâª\°2¾¾ ¨3€RyK™ÏA‰Ua ¹sçR¤H-ZD·nݸví+V¬ »îaK’#õ)æGα’+ÄsÞ0øiÒDF™+ëk€´ôî­Œ%bcáûï3á°¹ˆŠ‚Å‹•6#Sß·n…Aƒtš©{÷D‰sräê>øãÊþR–6KÚ°ìܲTƒŸ¼9òâçéÇA¸2ì _5þJ?6¡:wî3gÎ䯿þ¢^½zx{{Ó¾}{ræÌ™¥ëJ Db{|ýµX©0üŸ¯˜7OhLÉÂ…"PÐâæÿýÅ‹›ö>™"0^‘ X1¸s^UÖO£GEùâ…ÒÞ¾=lÙbZï?¿ÏŠó+Xzn)'CN¦y¬£ƒ#-Ê·À××îÕ»“Ó9kÏ{Dj€¬ŒZµjñǘ˜ÈX½z5Ÿ}ö 4 ((Hm÷$‰+2½ <„veüxóÜ·˜<nÞãèh¡/ž6Í<÷ˆÛ_¦ü\¹"tS†ÁÀöípë”-›5·"b"¾̲sËØss‰šÄ4¯T°ïÕyuP&ŸÊJl‰Õ`ÀÕ«WÙ¿?ûöícÿþý¼|ùRÖ²¤>ÅüØû?~,êòüóÒîæ&Vhz÷Îú= 5@ZœaÌåÎÒìÙ¢ÞPzEÍÊÙ³b¿O‹âçhÛÂÃS~?1æÎ«l%&!†-W·°ôìR6_ÝLt| , @,^s}ž5{âëáKã23~C‰Ýc ñãÇS·n]‚‚‚¨Y³&AAAÅüØó_½*ª8?EŠÀîݦ ~ e __(¥'?yù~úÉ4÷Í4†«?mÚ¤™£íÚéV²´Ô«§ÏŸ/2ÞŒ!Q“ÈÞ›{¼q0îSÝé¾²;Áÿ§üNQNxÄx°¸ÛbB?e~çù2ø‘d›Ð…††R¨P¡¤Žð¦Bj€$ëæŸÄÊÏãÇJ{õêB°›5y~ÿ† ÓóäÛEfjlž6/^@‰ðì™Î¶f²|µ11båçï¿•öÁƒE|‰Ê-±Õ«E€Ô8óà KÏ.eùùåéÖë¨_¢>>µ}èU³îyìw%3; +ÃÝÝÇ3gή^½Jþüùiß¾=­ZµRÛ5‰D¢+Vˆ"†±±J{Ë–â?öúãç'ªA‡„ˆqd$üü3L™’½~brôƒŸ% S§MLŸäÁO—.0s¦H{ïÛWl}i™5+ytóéM–[ÆÒsK¹v1]+¬DßZ}ñ©åC•B–ÚCDbÍØÄØÝ»w©Y³&ÁÁÁ(P€›7oÒ³gOþ÷¿ÿ©íšÄìá›†ÚØÛÿýwÊÁŸlÛfžàçÑ£Għ±÷ãæ& ,ëóÛobk)Û1Üþzÿ}!VJ?íõyûmCikþfÎïÚׯã¨GÌ<>“·¼M…_+0v÷Ø4ƒŸ¢¹‹2¬Á0ûæê°«Lj6IüÄÆÆòØp9ϸt Μ+qÓa+@S§N¥S§NÌÑ+±záÂ<==9rd–ÚaHÌϺuëøÀ/Úö4Ç×®‰6VúÁƒƒ¨¿3j”ùî»gÏš7oN¡B…R=æƒà»ïtõ‡""`útóe ¥È©Spì˜nìè("ؽ³´AÚãÇðÇ¢¡¨Ù‰Œ„¥K•¶*?¯_C‡*m Šà§dIˆKŒãÂà œ 9ÉÑ{GY{i-_<„ÚißþRo$‰™ ç*œÅcû,X ôj鑘(¶¯_‡uv(WN¹Z¤ Œòä1›ÛVƒM@Ÿ}öû÷ï§hÑ¢Ô¨Qƒ;wîàââÂòåËqt´ ™“Mcï5j²{˜ã?„={”6ŸìÛ^J­!yòˆŸ t¶Ÿ~bfÿ¶¾l™‚´”. ï¼£8dÿ~Q !á•Á%ŠeÎ2蛓L»v’SûNqîá9b V©P­p5|jùзV_*¨%÷µ½À ª’:—½\»Çgíhø{ㆲM‰ƒƒøÑë¯i_ö„M@®®®lÚ´‰Ó§O'e5hЀXC¤Ä"±'}ŠZØúO*ÚXèóÖ[¢&MvaŒHË'Ÿˆ G+€ ;SŸ~jf'gÍRŽýüÝKžyNûNíqŠ¿zþ—Ǧþ›ûãÇaÕ*Ñ6«Øº("BÌ“~@_²¤²ÈdFÈ‘j×/}ââD²€~`tñ¢°Ù“rĪ Ý»w3~üx|}}¸ÿ>“&MbË–-4iÒD]ç$FcëúKÀÖæøåKèÜY—¬eútÑÒJ 2¢Ò2r¤(†øò¥ß¹‹¥Û“ þ{ò_²`'üeòN¥ž…Üz¶›ùaGÅ.šè a5(—ÓO{{âUÂwr»ä6ú3åÌ ˆŸ…–Ù³MÙºhèPÑE‹ƒƒXA3u¼çâ"Ð5k*íññpøp8ݺí5í -«€"""ßj+W®ŒŸŸŸ ~¬ [z0[*¶4Ç ôë÷ªèžÇ'OÝÎNŒ©dH‘"" ýçŸu¶ï¾ƒAƒR.Ìœ Iàϳ²èô"N…œ""Ƹ2ÒþÇ•ã¹^à✃eÅŠIÇô6U«g‰DbŒk×*m:X@wõL2j”¨¶ýªúâaøjaϲs˘ü÷d®=¾–¡ë7º µèƉÎNô¶„•Møþ[eiQ`/+Áˆ-ÈÆ…žEËìÙbµK’œ›7Åv¨>uë&BKL‹U@+VdëÖ­l5Èãûî»ï’þ. ËÇõ)–†­ÌñÂ…JÍ }ÃòåŠlnUȨHKñâBàúûï:Û7߈-#ÇD–[Æ”¦påÑ•Ô/¢GÞyñp÷H(wúv  +ìØµ»ÿnÉ÷ß(Ï+_^¤DçÍ›!÷SÅß_-Y"~vY©ud‹ „!ä×ïM›3§(Ù”Õ@T’6V͘1Cm$&ÀÖô)–ˆ-Ìñß'/Z\¬˜¨|›mm$Ò 3 -£GÜ9:êÕk‰ Ÿ·‚¿â&sùÑåTÏ+˜³`R £}U*X m:×Ó§°^9iWógÄåuŠUžM#{{‹mÉGÄ8"B4P4(ó×´E Ðÿþ*mÓ¦AµjêøcOØl S ëI$–ÁիШ‘²ÇWΜ°w¯²´5ãïsæ&BÍ h:Ф\yÐÕÉ?O?>mô)• VJû¢Ó§+Ê G•¬D°+ÄÄêòÝóä´ë™–>ÿ\¹5Ù ¨Ï$>,¶ õSÒ;uÍfÕ"44BCCÕs"›°ê É“'sòäÉ4Y§ß1N"‘XOž@ÇŽÉœ.Zd;Á µû¬ÂÁušÂS<ÆÕÉ•AuñUã¯(·´qž3G1ü&|ˆ"øqqà`ó? ²Ù¦MÂu€£Gáôi¡5²wž?[_úÁ»»èÿ%ɬ::rä‡bРA.,ëY+¶¢O±d¬uŽSkp:y²¨7cIdF¤ACðÅ`&ý=‰óÏC ÿ¹8ºàëáËØ&c)›/Í÷ïUï^çàÊìߤ±ƒƒÐT™³l@•*м9ìÞ­³Íš•¼#‡±Ø’hØ0Q©Y‹6¨—²ìê eË–1sæLæÎK§NøüóÏ)UªTú'¾âüùólذk×®‘/_>zõêE£FÌè±$%lAŸbéXëš¼Ái¿~¢U€¥‘  kþ]体8÷ð\Ê%:ÙÌè7ŽÁÊgܡٳÃ`MwÂЕÇþñG±anüý•вe¢w[fº‘ÛŠhåJQßGŸáám[uü±WlBC`` ¿þú+5⫯¾¢bÅ”ª|)iݺ5•+WÆËˋ۷oóÃ?°sçNÞ~ûm@j€$5ùñGøâ ¥íí·á¯¿D…[kDƒ†u—Ö1iï$Î<8“â1'4gúÃ?ãàqEêÕm¼2ÄãÇ¢„°6·hÎöÒ õe¦NÍä‡È qqPª<|¨³Ížm\±G[äΑ¹¨­” P«–ø[ÂïµÔY9rä K—.\¾|™ßÿÚµk3\Oø—›7oÆU/Ïpÿþý=z4)’H$ê°~=|ù¥ÒV¡‚¨ÿc ‰Ì°þòzöp:ôtŠï;98Ñ·V_ú–O‡)•“Ú!?.ÒÓÛ·ÏÀÍÁÏeª&?ýú‰à2»pq™_zÕI˜5Ë> ÄDQÞ@?øqse¬õ÷ÚšqTÛ¬rçÎ>ùäjÔ¨All,W®\1*ø’‚Ÿ˜˜¶lÙÂÙ³géÑ£‡9Ý•¤€=|ÓPkšã´œfJqýºrùÁLtví2ÒŸ½{E ðWgGãF ïQ¿>xyàâ’ÎùfW¨­[ë>ˆU Œ^o̘1ìß¿ßìþšc|ü8Œ§|¿R¥† Sß¿M›6@³f͘:u*qqqØ+¦mÛ¶ Í—1$$$h®^½ª©^½ºfÑ¢EIö‰'j&Nœh&ï%‰>/^h4žžHšÖ½fÎÌÀEîß×h&OÖhJ—V^ÄÃÃl~²ùÊfMý9õ5âË!ÀAÓ3¨§æüÃói^§kWåGhÚÔ¸û_¬Ó[qâúi*WÖh>ÌúgË kÖ(?O®\ÍÓ§êú”]DFj4Uª(?Ñ¢Mh¨Úž%'$$DS¬X1µÝȬzhÛ¶mh4š4_ÆàèèH¥J•èÞ½;›7o6³×‰ÄmƒSò^#F@ºÉk ìÜ)òåË” „ÒTŸÓ§áß” š‚DM"Û®m£Ñ¼FtXÖc÷“ïï8à@÷êÝ93ô A=ƒ¨Y$í}ÃL·¿ÿV¶–H‰ aT<³Fi+øÛ·‹jÏjÒ©”(¡¿|)zžÙ#F$/å°`¨d.Q«€²Bbb"—/ëJÌk4Ž?N… TôÊ>±&}еbésüå—Éœvì˜NƒÓðp¡æ­RE³Y³FYUÎ¥KMâ+ÀOþcå…•|¾ãsš.jJ¾ïòÑ~n{ŽÜI¹Ìq—ª]8é’àwƒ©U´–Q÷ðò‚wÞQÚÒÒmÝ '?Y„+±I¶k2y×[”ÏD½©qv†÷ßWÚ ¤Jéb µkaÞ<¥í£D_‰ºØDXfˆ‹‹£víÚxzzR¹reN:E||<ÚËW ÂZkÔX–<Ç !¨>Ú§Ž)}EÛ·OH‚ƒ!&&õ »¸ˆl-Ë—Ã×_gØ¿ÐÈPŽÝ?Ʊ{Ç’þ|õ(ù7r€^³ÏNU:Ð,Ïâž¾/Àøñ°e‹n¼s§h%Ѱ¡ò¸£GáÝžN%*+?;äOu ªºìç'z_iîçΉ>Xo¾iÜùÖVèþ}!×§Fì+Pb40 IDATA I›¨”Yž~ʉ½w4)à¹ûìn†?×;•ßaR³IÔ+‘õ­[‹ÚGZ:tqZ._õ‘ê„ïâ/Z%Ù\sâôà>äÏŸeLIÇŽ ¯40 yQ@[@£¿Ûú?»9D[§Žz~¥‡¬d'(P€Ž;ªí†Db—\½*d;úÁOΜ¢dRðsü¸z–/¢‘ÔÈ• z÷Oýú:{—.¢ô°–eË’ ¨ø(N…œR¬î\}t ™ûNèžÇÆe3òÍ‘4,Ù0ýŒdÂåCtóf¡•òô+ mÛŠÝ@”ûIN>½-.øñ#Ò€‚‚à—_ @õ|2Ó¦)nß~kÙÁ½a×Ä2°Ö>UÖ„¥Íqj N¡~0o¹|NœHûB5jˆ'jÿþ)?ì}|PÔ²@>mÅ‘'8ÿð<ñ‰ih†Ò _Ž|x•ð¢AÉÔ/QŸú%ë“+.W†{CãÆÐ´©²~Ì”)¢oTûöpëã]1hüìïoR?LEûö"ÀÕêÔ££ÅÏ}Ĉôϵ–^`§OÃW_)mmÚ÷%Ù‡ €$ªcÉú[Á’æ8µ§³?>GÏ¿gƒßxö,õ ¸ºŠ *¢àeÜK<½Ahd("ðàÅñ÷¸{|“Ç™ü‘"ÐÉùø9·‚çsº’ñþº9»ááîAýõEÀS²>U UÁe5ÁÕ«WÝ ,£Œ¯ €Ö¯‡fÍàìY1Ä\Ð[J«S'¹PÈBprZ ‰u¶Ù³ ¬A}ûB¬N‹NáÂ"`Íî”’´±k PzH Dbzüü`þ|ñ÷ÄГUL(:‹Ê¤yÞ³RE8ÚÁƒMKqÍ)BèDÆF¦zÞŒÍð¡^Vú’:0 [ÊÇ:98Q³hͤUú%êS«X-\]R>!yóMQÑ4\§"幡3þñ‡-”û÷¡lYeÒÞÞ½b¥ËÚùè#1ýú¬['vc­©’H$3ðÍQÌ~@e÷ãøÇþ‰ï³¿(ÿRéTï«Â¬z°³B‡p%åcScY-eÔí_ÈÙ¢\ bŠÔ/Y?i+˳¸'¹\r¥~1?>yZ<@v(ƒŸÜ¹EQ% ¦D ±ªWžÙ³­?Ú´)yðãïo=Á½! ‰êXš>ÅÉ®9>r’!'’oEE>àáã´¾øŒ¿ B‹àÆÚóݼ0× æyÂýײæÓÁÒp3?”{Õ€2O,.ú¥¤`NÓiI=zd –öí¡^=¡ ×çó×fÃs=Cß¾ðZ'-ð÷W@ÁÁBÌV¿7KÖ=x š¾êSµªCK,ITÇ’ô)¶JvÌñš×Ð'¸± ± {Ù§0ø$¼ÜSß©"ѶW«=›+CBÊ´º:¹R4wQÜó¸S,w1Šå)–ôg¹üå(ôü/˜ö{Òñµw‡OLûݳgÙ4@ZÆW®&x¡UØFåA*~6¤M(_n¼Z¼Š-ÛFJýKÕi4àë aa:›««Ðßç²ÌE R”&R$‘ÇÒsKñ]ç›”Uå¨w®ÂÇ¡ýU1N‡¹aA]˜ã7ôR¡s8å hî¢ËS,ÅÀFßVÀ-êóç¡–^f  \IH F¤¿Ÿ>-ÒÆ/õûš¢¿×àå•|‰È‚ùö[e¶T¥JBombáéÓaøp¥íûïá‹/Ôñ'+H D"‘ɼ“óðßäO¢&‘âÏáýS0ø”‰Hû¼s5 q¨CîµjHáüÅùÎ °ÉïfÂ6¯¿.ÊKkÓ¦ââ`õj2Ät÷ÈÄ*lX—HÑþs•XÙJê A"L[ êÚ5Ñõ¾U«´Ï³$Ο‡Ñ£•¶-Ò^É’X2’¨ŽÔ™sÍñô#Ó±m4Ô»û@Ž4Jë<¡ç<ߣÉRjU«†q]±LDß¾ºDo0@æÖiéÖ v쀷ŸoƒÛ·uoäÍ }ú˜õÞ¦¦X1èÚV­ÒÙfÏN=²4 PLŒøµŠŽÖÙ „Å‹­o˱Ûf¨Ëaݺué$Éæ˜ãï|ÏðmÃÑ ¡ðK^™zðs˜Fø²ˆ÷ÛÝãíc?Cµj&÷']úôQ>•öíKÞ5> ìÙ³‡ˆˆt–½L€ƒÃ«òG³f)ßðñ`V†¡diýz±;™áááìß¿ßüNÉèÑ¢Ÿ™>sæ@É’êø#É2’¨Ž@›SÏñ„=øò¯/¡ïY¾:ù–W$y˜?u9ÅâTí÷ Ê™rƒÓì LÑ4K‹F+V˜ìòÞÞÞf@+¸{WÙ%¬FülH‹P¹²n'ä¦D‰%,F½}»Ðþè3h¨Ñ)±d$‘H2Äç;>gÊ?S’Æ_ï†Vÿ)™åô%¸ÏÌâ4¸»ÃÆÝ·¯r¼t©:~d•yó !A7nÔÈj›L98$߉œ;W×1Þ Y_ú)D•+'ˆ$– €$ªcÙjcŠ9Ö áÃÍòÓ¡Ÿ’l]/Á—;ÿ¸´äã„_yŽˆvræÛeÊdÙ…¬Ó³§ÈÓræ \¼h’K?zôˆøøÌõË ºRÚZ¬tõG‹¯¯è”®åæM¡s2$66–Çú äTâý÷•ÛtÎÎ"–¶ÂH»F@Õ‘ ó“Õ9NÐ$0pÝ@fŸ™d«ò¯sP4 u)M¸$àˆo÷‹AƒYº½é(TH´O×ÇD«@Ù¥bóf±¦%~èÕËü÷5#… 'ß:2”8eh€fÍ«™úLšõë«ã$óÈH¢:Rd~²2Çq‰qô îKà™À$[îXØäÌkѺè'Î1ã‚ GWÊwòdx÷ÝLßÚ<nƒ-_n’Ëf›höl帱Ìfå.bmÚ÷î)mjk€.]‚Ï>SÚš4/¿TÇIÖD"I•˜„¼ƒ¼ º¤°/ß’“ª”Û=%þÆ1t_ƒûõƒqã²Å͌ѥ‹r¯âÆ”»ŒZ"—.Á¶mJ›•oiiÒªW×SÚéS“ØX;GEélùóÃ’%¨'ì—d ùc“¨ŽÔ™ŸÌÌñ˸—t^Þ™ —7(ì_Ÿ.H§ÓQ ÛB‡AÌepÒø7„N×"É•KŸÑÇÛ`Ù¢úúk¥:øí·¡fMóÞ31ŒåæÎUj½ÕÔ§N)m³fYˆ¶M’)d$Q©2?ãç±Ïi¿´=;®+•¨ýž”fì¦g Ûg/>ÔÌH)AAJQ«Åáã£Aƒ³k€®]Kž¶o¸cå  ÜÍ3ÌöWK´{7Lª´ `õÒ+»G@Õ‘ ó“‘9~ý„Ö‹[óÏ­ö–®Õ\­"œ Ñ%>˜hÜpr’šR¥Lã·ÙhÝZDjZÂÂ`çÎ,]Òì o¾Q.‡Ôª•|%ËÊ)P@$êé£/†VCôø±vôSÞ+T€ßOý‰u  ‰D’DØË0Z¶àȽ# {£b^l[ÿŽuí®5ŽôŒ_Î-Ê&Ù¦L–-³ÍÝÌãìœüI»l™:¾ÃÍ›ðçŸJÛØ±6ÙoÁ0Vß¶ nÝRÇ5ŠôÅØNNâG¡zM+I–‘Du¤Èü3Ç!‘!4]ؔӡ§ö·J¿Åß§ëâ|ä˜Â>N3…´NwêdeÙ0†Û`ëÖÁË—™¾œY5@߯ë PµjòÎFxã ±¸¥%1Q§'Ën Ðüù¬´M˜ |”X?2’¨ŽÔ™ŸôæøvÄmš,l¿áÿ*ì-Ê·`—Ã{¸ÎV*š79uá[Æ$+T°Âo¾ åÊ鯑‘°aCª‡§‡Ù4@÷î%ï 1v¬M§Š¡çÏ;¯Ù©ºz†WÚÞ|SL½Ä6°ÝA«Aj€ÌOZs|íñ5/l̵Ç×ö•;°µæ·äøHù¸éRŸ„ÅhÑNΜâ[rþü¦÷ÛìÖÊÂ6˜Ù4@?ü r°µT¬˜Üo£e¥‚›f—(>^,¾x¡³åÍ+’œÌ~{I6! ‰Ä޹v‘& ›p;â¶ÂÞ£zÖ¶]€kÏÞŠÂ'ÑN¹é·–gäM²ýñxxd›Ë¦Å0ضM¨^-…D.¸>cÆØüS8o^èÝ[i3¬ÿhjž=ƒµkÅêSÅŠpL¹ãËŒÊC‰õ# ‰êH ùIiŽO‡ž¦é¢¦„D†(ìýj÷ceå¸ô÷Eõ˜0 èêÎ ,ú8Y-5kBíÚºq\¬Z•©K™E4uª²ò^™2"%É0\´Ü¹._6H£“'Er]“&¢KJ÷î0gÜV~ o_QØSb[ÈH¢:Rd~ çøÈ½#4lNøËp…}ˆ×»â4ùkغUñÞ¯#Xîk¹—üö›ù|Î6 ÅЙÜ3¹èÑ#˜9Si=ZÙÌÕ†©W<=uc~û-k °0±5`¸»‹ßá±caß¾ÔË@•-+V9%¶‡ƒF£_Ý@¢O@@€âO‰ÄøçÖ?t\Ö‘ç±Ïöá ‡óK»_D³ÍN…O¹4¡IÜ.âq `A8qÂF¶îÜO9íçupy×¥K«ë׸qð¿ÿéÆ%JÀÿYx…IÓ2gŽR]´¨øq¹ºw~B>,v6·m¿³Æ>ñÜÜ qcÑÏ®Q£Œûn­„††âááa+órH"±#v\ßA»?Û% ~¾jü•~®_kýzO‰p×t‹ J ~D›~@:ëÆÉ¤fš§O“/¯eWÁˆ­'ýz; NZܽ+Òæ{ö]æß~[t9~<ýà§JøäQ}úñcرþ‚{ÃYm$’ÐÐPÜÝÝÕvæ åhÄQÞ]õ.1 1Š÷¾nñ5c5pºwßW$8ºÐ%v(–d?Ú·Ï6׳‡¾}á½Ê×K—Â_dè="_¾|8;›à¿ÕéÓ…*WKÑ¢6Óô4#äÉ#v(uÕ cùý÷Hzõ*˜tLLŒØÂÒ®ò\¸`üõ_{ Z´€víÄËf‚z‰QÈ ‰êH ù7s=‚z$ ~~nû³~@<`ÏžU¼?"qy3iܦ Lœhvw³Ÿž=•Úš³g3ö$Å„ çÏá—_”¶‘#•M²ìeÜ'4@Û·‹²ÄvlëÖðÓOéÿÈDÆâ—_ÂÞ½Bfµn\ËàÇþ+@Õ‘u€ÌË‚S Xä´ˆ„D])GGfv˜É¯!ÂðûïÉZ-¬pîÇïñ'Ë”ú`›¬¿W° XظQg[¶L©ÁIoooÓø2cŠ¡›‡òþ†÷IÐè‚''u]¤ ~LÖUüRŽ: ŠŸ“4vu…Õ«ÅÃÄfI©(bv爼| Ó¦)mŸ~*ö‚옌ìþ99‰ŠÍ“'‹ éáCñ£Ôf~I$Zä Du¤ÈôœxžÞ«{s!ìÕž@$\]XÖcÞ5^­V„†‚··¢ÏÔ —ütŒ & ݖ˯¿BýúÙøÔ sghDFŠñÍ›pèxšI4@³f‰\m-ùòÁ°a™¿žÐ«—ˆÑŸ>Eü2T¼_²¤XáiÛVl‡YeUrI¶#W€$ª#5@¦eÖñY4˜Û@ü\7g7Öö^« ~âãáÝwEŸ-ôŠû“ëTL2õ(M’+tíª´-]jôéYÖEG‹Â‡ú &‚ ;'W.mýÇp`?9r@«VbºÎSf~ÉàGb,rH¢:RdžD?áýõï³öRò<áêíª³Â{µ‹éU=5J¤Ïè1™ l¦CÒ¸V-ý ;ÀÇG©…ZµJ,±ª“e ÐüùÊ`4O±ý%à£ÀÁ¡mÚt¦Y3I$YA@‰ °ïö>|‚}¸óìN²÷ü<ýøµÝ¯ärÑ{b¬X‘,ÓhwŽöLŠ™4ΛW49µ«M«VP¤ˆn*,Lô`0wÞl,|ÿ½Òöá‡B-DÃä8‰$+È-0‰êØCÅQs‘ I`Òß“h¾¨y²à'_Ž|¬ô^ÉÜNsyöH¯¦Ìùóàç§8ö¾[¼c–’¨÷_B` T®lV÷-gg±-¨‘Û`Yê(JkÉ™S¤¾KÄÆš®˜D" ‰êH Pæ¸ûì.-[°7@‘åШT#þßÞ‡EYµÿÌ0ì (² ¢"*.ä’– ¦–š ¨Yf™i¥½™þZÔ\ÞҲ̷rI37L Å}É,EÍ-W$7Ded`†y~x˜f˜¹?×5<çYæÌ ÷œsŸsb§Ç"¢ûg®¼Çyyl²ÃÂBå±2¡ FïF.š)Ë>þX5ÆlT_lß>6:«uÎ’˯¾â—M›Æ&?$*))AJJб«aÖ(HwûîìC×µ]qúái^¹…ÀŸõý ¿ù7üœý”åÓ§OgCº'Mxç¼-_‹XtSn‡„°²ÍVŸ>@ëÖ•Û……Àþý5ž׺̰m˜X¹-³ü,¢ÂÓÓ£F2v5ˆ‰0ëhâĉpttD¯^½àçç‡ÿþ÷¿Æ®!ZË‹ñÞá÷0&j r¤ü®{{ý–Z ¡…šô¾¥KxEëEï!“”Ûžž,=ÈÒÒ Õo:ÔÍ d …j´9e ûAB ʬ !C† ##iii8rä¾úê+ÄÇÇ»Zf‡r€jçVæ-ô\ß?]þIeßðvÃqýÝëÔzÚsÓ£¢€ xe±6}ð¾¬2«T(vîZ¶¬~¶ªýñ[7A‹:åíÜ Ü½[¹-Ÿ|¢Û5Ìå}2ëhòäÉp.Ÿ4¢cÇŽ ÄñãÇ\+óC9@5[wezüÜ7žÜà•[YZaÅK+pèÕChaÛBýÉbß”)¬µ¡\žuKŒ”î‚ •ë_}û-ðüó©~ÓÓ±#еkå¶LƆÄk¡sDZeÊ«š4‰­9BÔ¢ ¢OfU•ššŠ¸¸8ôéÓ‡W~óæMÄÆÆòUÑvý·«æ5†ú4¦í¿/þ°a˜~h:¤r)P¥±¬K;lî½³zÏ‚õ×ûùg`Ø0L/OzŽ °btqRáU^Â>}ø¡ñ_o£Úž8¼½Û·k=>,, ÉÉÉ÷«lï݋ت«wZZ"¶Zæy£º`ûÉ“'¼ c×ÇT¶SRR‹¨¨(ܼyŠ*–L@$ F…wÞyÁÁÁ¼}éééHJJâ=ª¢mÚ6ÔöÙä³»n,vßÞ]yÀSöeR×I¸:í*l$6êÏ?xá$M›ܾ]¹À'X†S , dsð5ôëkôÛãÇ#I ¨Ü>sI—.éïú_~ ÞÞW_ERµ.´Fu?hÛd·³³³‘””„ØØX$''ƒkè5ðŒDÀ™Ë+Õ ¨¨£G†««+¶mÛË*ÙŸ‹-â}%†Akñ)8–ü½‹O.VÞî`借Fþ„‰'ª?ùúu`þ|àÐ!^q:w¬#0º8JYnoÏŒìØQÏ/ÂT„„§NUnýµÆÖ;t ­Ü¶°nÝÚ·¯_}M\ii)$ \h‚HƒIOOG·nÝÌ"7Ó¬[€²²²0xð`´mÛÛ·oç?¤áPP¥Ô‚T¼°å,ˆY üôðìkÓ¯©~îÞÆ‚ƒU‚Øà‚ëL,ÞÀ+_¿ž‚­t ¦SPõÜŸðp ~jr€ˆ>™m PII :vìˆN:á£>R–ÛÚÚ¢W¯^¨ˆ4¬ýñû1eÿdKù£`ös³±tÐRˆ,Dü“=/f3 —ñ&¥±Êc fnçç·}ð[æŠh‘“xx°¥**üû/T÷kþù'ðâ‹•Ûk¹ëܹî×$DOÌ©ÈlדÉdðññA~~>/ÀiÕª"##W1bvŠåŘslV_Z­²ÏÍÎ ‘c"ñR›—ø;ÒÓÙü1ëÖñÿ9WÕ»7ðå—Ø“73Çòw=÷œêÂãD `èPþüIÛ·×o¦È/¾ào¿ü2?„Ù@ööö8yò¤±«A`Þ9@·³nc|ôxÄeÄ©ì{±Í‹ˆ‰–vU&æÉ;ùøñGÍË3tíÊþɆ†"6˜<¨Ìb+,ìÜɦœ!µðê«ühÇ`ÉÖrSE­r€Nþþ›_6¾+kÚ(ˆè“Yç‘ÆÁ\s€Ö_]?÷P ~D",¼ G_;ZüH$,¨iÝš%⪠~Ú·gÓ8_»„†âÌ–Ã[P°, 6ÃóŽ€——A_ši5Še‹WHJÎS9¬V9@Õ[†žy¦þu4”DôÉl[€Hãank%>MÄ~ÿÞ=¨²Ï¿™?vŒÝ^^, ÅÅÀš5,èÉÌTA__`áB6‰^y"ÿáÃ,¯V*­8ˆÝã/¿^xAÏ/ÈÔÙØcÆ[·V–mß®2kdXX˜öëœ?TŸhõóÏõTIó@k}¢ BˆT.Å‚˜踺£ÚàgBÐ\›v?2ËïiÛ˜=[}ðãî¬ZÅF€½ù¦2øÙ¾¥•T?ÌÂ…À§Ÿâ•™ê£Ávîd«¸ë¢zëÏàÁ,O‹b£3‡ÑÑ·¢¸*_œþÅòbÞ>;‘6Þ„íc·ÃQdüú+С0}:šªz1WW`Ù2àÁàý÷++å®Õ«×^ãÿo€/¾H f¬‡ÁƒYòT…¬,àØ1Þ!Z×»røýw~µþèŒÖ#úD1:Sκ™yƒ"!|W8å=RÙÿ¼Ïó¸2í &w› ìÝ té¼þ:pÿ¾êÅY3΃Àdz®™*¾ø˜1ƒ-1UA(d#ä›77Ý{Ü „B¶^HUÛ¶ñ6µæUŸ÷§ö :¡ ¢Of;PmÐ<@¤®òJò°0f!V_Z ¹BµUÀÓÁß ù†Mjxì0opù²ú‹ÙØ°Èæ“OXëO5Ìš¥:§µ5Årx‰\¸T]+ÐÎÈÈ`_µ‰‹ºuãG¦þÉZ•idh BHpà°éÚ&|vü3<)|¢²ßÊÒ ³zÏÂüþóa108}ZýŬ¬€©SYpäá¡ö¹˜2¨>u•ƒ¹RÏD*õî øû³8(,d7yÂíç-YÂ~z÷¦à‡F€ºÀˆÑ™Ê'‹©Ñû—Þ˜r`ŠÚàgXÛa¸ñÞ |Ý{>ì'¾ ôë§>ø±´dIÍññ,ÉYCðS\ Œ«ü4oÄÄðƒS¹ÇFW=Ø©Ò ¦6èÎ :š_F¹?uF9@DŸ("F×Ôs€ž>Á”SÐû—Þ¸˜zQe›fmp`™xí$b6|ºú?E€e+GD7o7~~Ÿ3?_u‚bðöfóìuïÎ/oê÷¸Ñ˜Xm¶cÇ€l¶t‰Ú %K…¢rû™gØÜ?¤N(ˆèåiA9@D¹BŽUWaÑÉEÈ+QM~µÙan¿¹˜ýÜlˆ-Ål˜1cXÞHu#G²DÙ®]k|ÞÌLü\½Ê/`©%­ZÕõ‘Z bc+·×¬Þ}Wõ¸û÷Ùä”U×hÛ³‡ýÒH™SµR'O ÛÚn˜õÇ,µÁϸNãpgÆÌí7—?Û¶ª?Ý»³ÀèàÁZ?Éɬç¬zð œ9CÁOƒ¨Þ ¤i…ø¥KùÁOçÎl‚&BH£@1º¦ôIãQÞ#„ï Ç ÈA¸™ySeg·Î89ù$~ û ÞŽÞ,ùuÞ<69OI ÿàˆÖ_UËÉðîÜa½gññüòþý“'-4ŸÛ”îq£7~<°³g‡ù9@ògŽØïAµõÈn(ˆè@ÄèšB~J±¼ÿ=õ_® Dô-Õügkgü8ìG\›~ |°Â¢" ,LýÊá ²u»ªÍå£É•+¬å'9™_>bpô(›"H›¦p› ooþ>ìØÁÏZ¶ŒÍæ]¡}{¶6 ©Ê"úDÃà‰Ñ5öµÀöÝÙ‡þø‰OUöY,0%x –ZŠæ¶Í+w¤¤° x®]ãŸ`clÚŒWëç?y’]Š-jZiâD`óf6G_Mû=nr&Nd+»Wؾaqå‹Ú¦¥±$öªæÍ,èóf}ÑZ`DŸè/’ îdÝÁK¿¾„1QcÔ?½½{ãâÔ‹ø9ôg~ðséЫ—jðãáÁþiêüìß ¦ü¼ÿ>ëa©MðC ,Œ· þý—=à›oøÝmÚ¨®%F1: €ˆÑ5¶ü”‚Òüߟÿ‡.?uÁ±ûÇTö»Û»cóË›qnÊ9t÷¨6Þ<*ŠMnøø1¿<8¸xèÙ³Öõز…ÍóSÌ_: ŸΦÒ%¤±Ýã&¯Y3™V‘½aäiiÀÏ?óýì3åBµ¤~(ˆè@ÄèK~ ‘×#Ñw- IDAT°2ËÏ-‡L!ãíYˆ0»ÏlÄψÇ]߀U"Ž-b ²Õ—aå6DËÛ»Öuùþ{6bÕADð¿ÿÿý¯î¯­±Üc“R­U'fûvä-]Êÿù·jLšÔÀ3]”Dô‰æÒ‚æ2W_ÅŒ#3p>å¼ÚýCü‡àÇa?"°y êN©˜<عSußܹl~šk>ÿ\uíLKK`Ãà7j}bhR)в%¿ÒÒ’µ®^ ¼÷^Ã×:2§y€(ƒ€˜µ¬¢,Ì;1¿\ý N¡²ßÏÙ+^Z1&¯{ü=šåýT%³ˆ¥úœ1Z(ÀÌ™l^½ê—ŠŠbOC6©aÕµHª?žžl¡6BH£D]`ÄèŒñI£Œ+ÃêK«°2?_ùY%ø±Ú`qÈbÜ~ÿ¶æàçêU–ÓS=øiÙ’-Æ¥Cð#“±©‚ª?Àï¿×?ø1‡OsFQ¥,o%°ÿû?½½¡ ¢O£kèü”ÓOã™uÏ`Æ‘È-ÎUÙ?¶ÃXÜžq ,€µÐZýEöìaó¤¦òË»taÉÎ}úÔº>R)› xÇ~¹«+pâ›@º¾(È@ÜÜ1”s‚»¹Ó¦«V&‹r€ˆ>QDŒ®¡æ¨IÉOÁ„Ý0`óÄeÄ©ìïØ¢#þšô¢#¢áëä«ùBK–°aÐEEüòQ£Ø¬À:¬G‘—¼ø"p俼bQÓ=j})­h ±´TNkÀµ¢|öìZOrIjæ"úD1 â pU ~»ñ›Ê>'±V¼´×§_Ç Öƒ4_¤¤„õSÍŸÏF}UõñÇÀÞ½€½}­ë”‘ÁFÌWÿ@Û®+ëС֗"ÆT}ŽJ|&¤  ˆ¡óSöÇïGØÎ0Ê yåðf·7?3³zÏ‚ÐB˘€Œ $„-jZ••›ÙyÙ2fúMJúö®_ç—wëÆ‚_- PuA9@Ô»7àï_™ôá‡:¤ö(ˆè@Äè ™Ÿ²ïÎ>„ï W™Ó§§gOœû<6ŽÞˆ–v-µ_$.ŽÍì|ῼysàøq6^×®±EMïÝã—÷íË–½(O)Ñ+Ê2°ñãY£#ðÁƮɢ ¢O£3T~ÊÞ;{±+‚üX,ðÃÐðÏÔð¬×³5_äÀ­µ ~6«ó”)샩 S ~`Ù2àçŸoðPPC  ã ¢Oø-»ax{{cÊ”)J¥ t(ã˜>}zåFv6 PΟGú±=»~“dšÏÕÈÎèÕ xî9öèÝ›­éUlÔü¥Kür`ëV6PcÇ»ÇÄ ÂÂÂŒ]“çéé‰Q£F»ÄD˜}äîîwwwú„l,dz¼s瀻w•»tê°ñó« vž{èÒ…åùÔÓÍ›ÀˆÀÇür77àÀ6 ŒBHÓcö]`5Ù¼y3-ZÄ{TEÛuÜŽÆÃ¢¶m‘þÎ;ÀæÍÀÝ»à õÛb1Ч0{6ED°e×mÛ°(3V?õ©ïŸÝ»/ªü,BÇŽlÔü³Ï6¢ûYÃvÅˆŽÆRSÜÎÎÎÆ‚ M}LqûóÏ?çÍdìú˜Êö¡C‡°hÑ"„„„`ùòåÉêÒìÞôÐ0ør~ø!àûï¿W–UürTÿ¥!õpö,°t)[g«ÜZÚ:hÒí®Ox¼4–>Ý»³ È€~ù…­†Q}¤×àÁl¤—ŽK„ÝÚµk©ÌÀ¢££1pà@¸ºº»*&+-- —/_¦n0¢að„èÛÑ£,ðùûo•]Uÿ-—Yq-s>ìqÙW„o߉ƨö ó†ÇqÀgŸ±Äæê¦LÖ®mÜÉΚPðcx”dx”Dô© ¾•“&C¡öì¾ú ¸zUóqÏ<ƒK½}ðYá\ðâPhÅŠ­,­ЀЩ®T LšÄZxªXìöé§ R B! Àì èèhÌ™3GÙ¯¼oß>¬\¹¡¡ óO×$ÉdÀ¶mÀ×_ññšëߘ;ß âðÉùOÀ¡²7ÖÊÒ »#vcdÀȨ0ðä 0jËí©ÊÚˆŒÂÃÕŸ×TÐ<@†×æRpÊ”qœò!WSV±-WSÆ+Wsïœò2 ÛÛ£A_ÍDôÉì °°0jºÖ©”%Ï,_OŸòË;t`kzµnÝ Õ Eee¸UT¤ rþ-ÿš¦Ë,硃"S+‘`Uj*N俢¸Z#S(`ˆàŽ6 oëV\¹q1û R™™,èY½Zu…õ VV,±æ“Oì&ÆO—~Âúÿ­GVQ–Ê¡bK1öŽß‹am‡¸âÌÆÀôé¬Ç®ª^v¤Žd‡ŒÒRd”–"½üÁû^&CFi)<¬¬àne±VV•ÛåeͰÛJÆqˆ¯èT<H¥ùgflOårÏÍÅñÜ\eEÚÉ9{23±25ghvqƒ¡ˆè.9™us­_Ϻ½Ô±³Þy˜=§ä÷°òŸO±ïÎ>”qeªÇJ±“ûÆïÃжC [w°‘^óç³ÄæêÞ|“-v*¼ ª©ä•q2ËMAMEYŽLVë€áNQ‘Öýb ‹Ê€HK°äfe¡@ öÕs€‡ÅÅ*ÎÝ¢"È Ôð. `YõÀR à—ëXÆ+WSö¤´W YËùcê™zГÒRüüø1Ö¦¥!µ‰·ü5‘Ú»{—%6ÿú«j³I…fÍ€™3!}w*¶¦ÁªÃðï“5^R:åtÂúÿ¬GooÃ'Û“'³µPyõ_~ÉR“L‘1s€8Ù‚šŠ€¦¢,S&ƒÂ½ò% ãaq±Öã,´‰ÔK‰G¢YÏžx`e………¸UT„¢25¿ŽDÚÛÚ¢³‚ª<š‹D*ÁŽúЬá<*.ƉW ”}E> 2oÜ0¹ ‹ùùX™šŠ™™(U(t:×R €•@+ ˆ-,*¿/ÿj%°òªßW;¾j™¬¨ ôJÊÒ‚r€Ê]»Æ†²ïÞ͆¶«ãî|ôN†•·6cãµÈ-ÎU,gkg¼ÙíM¼ßë}´iÖÆ@çËÌFfKUemÍ&¢7®Aªa’¤ ®àB~>nòZj2JK!§·™ ´¶±á:íì`k ‘†V§¦ ¹¤„]‘H𤴴Î×s‰ð†»;ÞõôDÛ&ÜmVªP`gy7×Åü|­Ç†8;c¦—z9:ª4–zþÝ ‰ €3gX?Ñï¿k>¦ukpÿN„øâ‡kkqxã§Ppš?Á¹aF¯x­Ëk°Ù ÒªJJ€]»€ TGz5oìßÏ–#µw_*Å…ü|åãºDb°nM\D"´´²BËò¯îVVl»¼ÌÝÊ «´>=.)Q~_ñêø©»¾Ü­¬øŽ½=:ÚÚÂNë×56>b1|Äb¼Ü¼¹²,¥<(º\‡ ([&Êädü/9/¹¸à=//ŒpqE SKJ°6- ??~¬õ5ÛZZâõ–-1ÃË Av ó^in("|r9[¦âÛoY¤I§N(žó!6Jñã•q'êŽÆC-–83zÍÀ@¿*û •Ÿò蛹ù—_XëOuíÛ³—êï¯÷§ntês ÊÊp±J°óO~~­»5tU=¨á4Nù~7++½µŒäÉå•RÕ`©Z ”¥­‹.?Ÿå¾U bœ„B^kNÅ÷®¦–d¦#o±Þb1F« Š*¢+Ȩ Èå,ïÐÁ€£998š“?kkL÷ôÄ4o¤÷öL^V¦¦bOf¦ÖÑ666xÏÓoyxÀ¹)N;ß„ÐÝ%̬è×_Œ ÍÇ=û,Òf¼o\ï`ÓõÙȨ¹é¶¹msL}f*Þíù.|}4§ïü”¿þbÓ4¥`„„°Iª›5ÓÛÓ6jµ½Ç€Û……¼Ö›EEõÊË©ÔT `x-6jtá$ÂI(D{[[­ÇÉ9O4G—¯]ƒoŸ>è^þ‰=ÈÎ>^³Î”¨ ŠR+‚¢ò€è܃ȽyS¥É6©¸Ÿ>x€…IIˆhÑï{yáYGdž~ *¤ ¶gd`Uj*b%Ç ¼èâ‚™^^Ö„Z³š:ÊÒÂäs€rr€;Xàsù²ÖC¹A/àŸ7a‘à4ŽÝ?Æ›µ¹ºîÝ1£× Œk¡µž+­^~>°e °f pGscà­·€Ÿ~2½‘^u‘#“ñ‚‹È«¾l-ØÚ¢·£#žup@ke N #5Äô”*Ø•…5µþŒƒÞ÷ôÄ„–-acaÑ@5dcMZ~yü9ZZK…BLvwÇûžž¨!øn(”DLWYðÇ,è9p€%Èh"@6r8¢_n‡Ï%qÿÁ<‡Š,D놙ÏÎDï>ú¯·7o²Öž­[-°àè¼ñðþû¬ëËÉ9ÿVkݹ[ÃðpMœ„Bôrp@''eÐãB%10+ LpsÃ77ÄI$X–†m(TÓÔ{µ Sâã1çþ}¼éáÑ IÓÇss±25³³µ¶šv°µÅû^^xÃÝö&˜÷ÕTPd.nßfAÏÖ­ÀãÇÚup@î¨!XõœËžFaòa‡zØ{`Zi˜Ö}Üíë–c¢k~Š\ìÛ¬Zœ:¥ýØ  ô¼ö`o_§ê5I2ŽÃ­ÂB\“HpM"Á?IIøW,®Ó°l Ê[w*ììŒ>亱i k™ºªóu±·Çº€|ãï-ééX“–†x5}®\ÎKš~ßË ÃõØÍ$)+ÃÖòn®[……³0ÒÕ3½¼0Ø\úÞ9úK5eOŸ¿ýÆŸê«|V' °o/œñÅ $œ|²P“8\á9Ÿç0³×LŒí8"‹ú}ò¯m~Jz:›{qÝ: 5UóqB!ðÊ+,ðéß¿^Uk$ee¸^è\“Hp­ 7‹Šøó‰9ÂV{­…"/Øééèú”Z#C­F*©[ ÌI(ÄÞÞøÀÛÇss±:5²³UÖFÓwÒt‚TŠÕ©©Øœž®µÛØE$Âww¼çå?ë†I µC9@Z4É …øóOôìÛÇfþÓ¢¸•'N…øá+ÿ4œB’Öc­…Ö˜43zÍÀ3Ïè¯Î58{–µöìÞ­yþEðð`“OO›Æ¾¯ª¬|ù¹®"\…B¸”O$×”<)-U:±å_ŠŠê¼„‚H @W{{^ÀCK¦.¥¤ëÒÒ°þñcÕ‘dUˆ-,tJš®¢V¦¤àhNŽÖ¿»®öö˜éå…WƒT”Dšžøx–©½y€ÜÖç{{ᛀLn™N¦õøVN­ðnw1µûT¸Ú4̧ۢ"`Û6–ßsýºöcû÷g­=cưÄf©BòùA¿‰Ê\/°$ÄŠ€ÈU$R>š«)«Øn¨¹ZH¥¼×pM"©÷˜Þb1/Øéîàë&ôæLHmx‹Åø¢uk,ðóCtf¦Æ¤é…[32°5#Ý𞆤é<¹›ÒÓ±:5÷4-ÿ¶ É+-Z`†—ú99éýuý¢¨)ËÏgk:lÚ¤:½quâÚ;ãÇŽø-°…V÷k¼|‹x©ÙL ô…ærK¤&RÀÕÐg#AÕ {÷ØH®M›TWh¯ÊÎxýuàÕwe(ñaÁÁä{¬ûç®TªÒü­öÆ–'—ãõ[X¨ š´m»…sä‡ÛEE¸VPÀkÝ©ëh¬ VVèfo`øaxÛ¶ð¢aÙC9@†§ëZ`"€—4½&- ¿jHš¾R%iú­ò¤é…«RS©áœ nVVxÇÃÓ==éo¬ ¡¿Ô¦F¡Nœ`ÂÞ½š#-—æj…õK±¥+‡Äf𗦔‰€‡€øQ@ü(Üê‹5Ö¨9ÔÚpqá?\]k.S7¡éÞ½ûàã3«W³jcbx èåÚIp¨D‚µ9%@Žö—¥o% ÒJJtjhV-(r°´D‚TŠ……(©ÇLÄÚÚØ ØÞž<å_[ZY)Y»v-¼:uªósšQá©Ëª­.ööX€eåIÓ?¥¥©]$7W.ÇwÉÉX‘œ\c×r/GGÌôòBD‹°¢–Ô&‡r€´hT9@÷î±¼žÈH¶»E"vwä°©pÒà´¥¹; ÃYÐso(PlØf[++ÕÀ(.®Ú–à[´•í$´“@(L\¿‘"ÚØØ ¿¬ Ù2²e²&·F•ØÂAvv®èt±·§¡´„Ô¶¤iM*ò†fz{£§ƒƒkØð(ˆD¤Å@¾}'\nFË-ËR”;Ó Ø ìêÈ¡@[+lnëòVžÑÀÃ~€¢á~ JKÙh.åß–­ð/^fÁÚI¿B@TÙ"ÂÐeá^kH°½=<Õ4MçËåÈ–Ë‘U)r¹ÊvÅ1ÚšÂõ©™Pˆnõwp@7{{t°µmr‰Û„4Vƒš5àfÍj•4í%ã]OOLõð€[•ÖUÒtQdd2pü8[¶áÁ 5…ƒ_b  7c,vÃÚ'ªKv¶t¶tîiêç@j/eמñv @Ë–€·wåÃÍ ((²³Ù„Ñ99üïuÎÅp€W1à/ÚH€6…ì{÷b 7‡5Õ•…:ÚÚV:èjgÇZæa8 …p ÑZ‡á©% …Ú )KCà”-“!W.×:1Z+kk•€ÍWCf µÞ©D9@†§kPmUMšÞ™‰ÕU’¦û;;c†—Æ4oN>L ý¥AEгk©ž“øãÞÀ¼-ðÅC­çKEÀÞ@ÖÚs¢5 P÷7)³ †àî(´Ì‰V.î,¸ ||øÁŽ——îËBVCÕƒ£´<9 ‘,’à‰ƒyÍ QìQN¬¡åäÌ™ZÍQã(¢«²5$¸|í†î{[XÀS,VÛ¢¤‰‚ã𴢩<(Ê‘Éà-£›½½ÁgQÖ÷zkDå^}r€jC$`¼›Æ»¹áFù¤†´»é¢ -ô™$“±E:+‚žÜ\à\Å(@(â\­ñç}€MÝ€¨ _Íÿ^[Î AV#1À}†¶‚v~¶ðð`‚‚ã •"®°×%ÄI$ˆ+,ÄÃæª O±X%©×߯†f&„¢ ¢AÏÎÀþý€4·ƒp_á Fâ¼ }¾Hu"»›»wÕ|°ìØ¢#Fµ…QíGáY¯ga!0LkH®\Ž8‰„:……ˆ“Hp£°Pen]Y °±AתÝ?hAëJB1 €ôL&c1W´ôˆŸf`$abþ¬1§Š…À¾@ôüÙ†ßÅe)°DßV}•AO[—¶z­¶L†ÅŸ/•*[tâ$$×s>€Âêbo.vvʯììûä å§ååž¡r€ˆy¢¿T=(-­ zöïZ=C(â(¢.BP‹… 8ðKfþ-xZ%ÿÕÁÊCÛŨö£0¼Ýp¸ØÔý_ªP ©¸¤R$VùZñ}F8YYX ÐÖVèt-ÿê®aäå§Ýcã Ã3t1/”¤…¶ ªAÏïûKÑíéI„â Bq°Æ$æ E"à/à`{àp;àq•)%|}Ú>£ÛFˆ_¬,k7ìRÁqH))Áƒò &Q*U~ÿ@*EFii×RÇÃÊ ]Ë碩xmm!¢Ñ„ÒäPQ«´8vŒ=göe¡oþŒÂ¬Ä18  V×Hq€ãþ¬»  »Ç3mŠQíG!Ø=Xã5²e2µ­7‰ÅÅxX\ ™bZk t²³ãu_u±·¯óJÊ„Bˆ1QTƒ²2K:Ä™ã÷ÝÆÀ‚x± çaš€9pѵò ®•¯R.€AnAØz BüB0Àw€²kKªPàvQ‘JëME°“_Ï5¢´q áomÖ66¼n¬X¨U‡òS î±áQáQÑ'úK­Á¥e=á*›……‚hÃÕnÉLM][ZtÂû~!Øz zz÷C¶ÀñEEø·¨»“2Xü‰R)ÒõÜMU•H €¯µ5Z[[Ã߯Fìø——z>u(?Åðèåå}¢ --Z„E‹×êXu][Í;¢{ëáhåÙ¶Ží‘&·@¼TŠø¢"¤êaT•&-­¬T›Š`Ç[,Ö¸*9!„óF9@¤FU»¶ö9âßÖ>pwë÷=ÐÙÞy–NH,‘áŽB<ðT¿Lv––h­¥Ç–Æ$„B´¢¨–JD"ÜðõBtwòÁ _p®íÀÙú l„Vzù¥P÷KÞb1?À)ÿ¾µµ5ZšØB|”Ÿbxt r€ r€ˆ>Ñ_j ~· 7ü|íâN 1X ð³¶F{[[ØØ ½­-Ú”;­¬­Íj9å§Ýcã ã ¢O”¤Å¢E‹°8$¤^×p‰ÐÞÖí˃œŠ€§­Mƒ/âI!„hC9@D'b ´µ±Q¶ä(66FUE!„í(Ò—XÌkÍ (ÿÞÏÚšFVÕå§Ýcã ã ¢OÔSƒ±·náj(è×)}úà¥GßRIDATx×®X€ÿx{c˜‹ üml(ø©§}ûö» &î±áÅÄÄ //ÏØÕ0iYYY8s挱«ALåi¡m-0B!ÄÔ˜SµB!ÄìPDŒÎ>iÝcÃËÎΆ܀ëô–”““cìjaöPZZvíÚ…¿þú R©ÔØÕ1K‘‘‘Æ®‚É£{lx@vv¶±«aÒ?~Œ£G»ÄD˜õp…Ó§O#<<ƒFrr2¤R)Ž?GGGcWͬ» &î±á•””€R* ‹ã8”––»ÄD˜u мyó°hÑ"lÛ¶ §N‚X,ƶmÛŒR—œœœzýaëÒÅQ›cµ£iŸºòêeeeeÈÌ̬ñù A.—×ëº.?£¢¢"h=F_÷øéÓ§(©¶¸®1»¼êóܺþŒjz®‚‚ÁŸ¦Ÿ‘D"D"á•©û½mª÷X×óù~Q›¿#CQ÷{  ]îqmÞ[ ukº¶)3ÛH*•âÌ™3Ê)ÕFŒcÇŽ¥>@ZZZÏ_»v­^ÕvŒ¦}êÊ«—¨™õy“ÑEVVvíÚUçóuùÅÅÅáìÙ³ZÑ×=>zô(µ×P÷XÝsëBןQMÏuöìYÄÅũݧégtùòe\¾|™W¦î÷¶ús6XP}î±®çóý¢úÏH&“¡°°°Æú胺ß]èrkóÞb¨{\ÓµM™ÙƒOJJBëÖ­QRR«òÅE7mÚ„uëÖáÂ… Øð÷ß~û ¯Ozz:œamm]§ó“’’àçç§·cµ£iŸºòêer¹éééðööV–ÅÆÆ¢[·nµª{}”––"++ žžžu:_—ŸQ~~>är¹Ö ÛôuŸ¯«‡èÇéÓ§±mÛ6…B¬^½ÚØÕ19'NœÀÎ;•Ûí۷ǬY³ŒX#Óô×_aÿþýpppÀ»ï¾ cW©ÎÌ6V®\‰•+WbÞ¼yHKKÃòåËqùòe´nݺÆsSRR™™‰Î;cÞ¼yhÛ¶-¦NÚµ6/%%%˜4iþý÷_ܺuËØÕ1I‹/†ŸŸ ðôô¤`^ÏbbbðÕW_aÅŠð÷÷çåkýÈÏÏWN’xûömìÚµ 7n4r­LKnn.ÂÃÃqìØ1ܼyK—.ÅŽ;Œ]­:3ÛQ`0sæL|ùå—8}ú4rrrЫW/ˆD"•ãÖ¬Yƒž={"88_~ù%8Žƒ··7‚ƒƒqéÒ%$%%)ÿyÍ8ŽCDD„ÚÑ«V­RÞã%K–(çS™9s&>þøcš›IçÎÜ9sTʳ³³ñÚk¯¡}ûöxñÅñÏ?ÿ(÷¥¤¤ ))‰‚ŸZJHHÀo¼¡R^TT„éÓ§£C‡8p rÒ¾~ø¸zõ*RSSººMÖäÉ“q÷î]•ò7¢wïÞèÒ¥ æÏŸ…BGGGøùùÁÏÏ¿üò æÏŸo„7=K—.Å¡C‡TÊOž<‰Aƒ!00o½õòóó!‹‘ãÇ#77·V™Y@   œ;wGU™ :::Ë–-ÃÚµk±uëVDEEá‡~( ìÙ³"‘ˆ—LMT}÷Ýwxþùç±k×.•{…åË—cݺuˆŒŒÄöíÛ±jÕ*|ñÅ E÷îÝTë¦%%%S§NÅË/¿Œëׯ«ì ƒ““þøãŒ?C† Ann.''';v ýúõ£‰æ´(((À{gAƒáâÅ‹*û§M›†œœ>|ÿùÏŽ„„<|ør¹˜4iµfÖ`Íš5 Á–-[T¦p8zô(æÎ‹ï¿ÿQQQ8vì¯{üäÉ“ðôô„¿¿CW»I9tèÆŽ‹Ï?ÿ\åCijj*BCCñî»ïâÈ‘#(--ŤI“`mm:tS§NŰaÃŒT{=áwåÊ.&&† …\||ú¨AêÚT]¾|™‹‰‰á,,,¸{÷îñö :”ûþûï•Û[¶láºwïνýöÛܸqã¸qãÆq®®®Ü'Ÿ|ÒÐÕnRòó󹘘nΜ9ÜàÁƒyû9‘HÄ(Ëúôéí_¿žw\XXwãÆ©oSTRRÂÅÄÄpË–-ãyû$ gmmÍûýç.\Èõïߟ+**â8ŽãV®\Émܸ±AëÝÔÄÆÆr111œ££#wåÊÞ¾qãÆq‹/VnïÛ·kÛ¶-Çq§P(¸.##£AëÛÝ¿Ÿ‹‰‰ázôèÁ­[·Ž·ïÛo¿åBCC•Û)))œP(äöïßÏÍœ9“ã8ŽËÎÎæºtéÒ uÖ7³M‚®ª"ñY ¨ì»{÷.>øàåvçγgÏB.—Ã××»víÂàÁƒ¬¾MQE+Žº{œ€Ù³g+·ƒ‚‚pçÎÞ$d½{÷Æ×_møŠ6a Á½{÷ËÛ—___ØÛÛ+Ë‚‚‚Ý»w£cÇŽ(**ƒš|³¶!YYY!$$D9FU=´iÓFY„Û·ocøðáøá‡0~üx8p[·nm°:7E]»v…ªÿ¢®Ü Âýû÷!—Ë…ðFóõüýýáïï¯v®²»wï"((H¹íåå{{{ˆÅb\ºt ©©©¸wïˆ dffò •Jáää„èèh º&HNNVn?zôj»rHݨ»ÇžžžtõÈÃÃ?FYY™²,99^^^F¬•iñðð@aa!rss•e=¢{¬gêÞ/œ©åR<<<’’¢Ü®ø½6µùÁ(ªAxx86oÞ …BˆŒŒDXX˜‘keZè^=àââ¢\hôáÇ8}ú4^yå#×Ìtxyy¡wïÞʹgrrrpèÐ!ú]Ö³ððpDFFB&“ ÷ C áC‡‘‘øõ×_ѹsgÓYgì,ìÆ`ôèÑœ¯¯/€óòòâºvíªÜWXXÈ…††r-Z´à¼½½¹þýûsYYYF¬mÓÊ»ÇÁÁÁÊ}‰„1b„ò0€ËÎÎ6bm›¦ëׯs¾¾¾œ««+gmmÍùúúrß}÷rÿßÿ͹¹¹qœ““÷¿ÿýψµmšž¹\ŽÖ­[ó&lH>DÏž=1pà@šq™R+b‚$ ._¾Œ={öàĉÊò›7o"11ш53Œ;w¢W¯^¸ÿ>vìØaìêBš €1Qb± ,ÀgŸ}¦vÿõë×±~ýz^ÙüùóñôéSÀÖ­[qêÔ)|ýõ×7nV­ZŽã°qãFLœ8k×®Eaa!ïü;wîàÃ?Ä«¯¾Šßÿ·ïôéÓ˜:u*Ƈ 6 b ²K—.aË–-xøð!V¬X¨¨(µõÍËËÃüùó†¹sç*ëyöìYlذ‰‰‰øôÓO§rî­[·ðÓO?!==?þø£r¶æ””Ìš5 áááøúë¯QRR¸pá6lØ <ÿÀøë¯¿”Ûßÿ=ñâÅxï½÷° "%%¿üò ÀÉÉ ß~û-,X <þÈ‘#hÞ¼9 337nDÏž=±páB,Y²ãÆX[[cÞ¼yøøã¾¾¾8uêD"‘Úzìß¿©©©ˆ………ÆŽ‹-Z`ïÞ½ˆˆˆ@«V­Ð§OLžçþýûakk www<÷Üs¸wïÎ;‡~ýúNœ8ÄÄD´oß“'OF—.]t * !ÆC!fâ믿æÚrwwÇøñãáïïüõ×_¼.¯º2dÚµk‡®]»bòäÉʼ£o¿ý...ðööF@@š7oÎUU“¶mÛâ—_~ÁèÑ£áíí#FàçŸF@@@ë:hÐ üßÿýºwïooo|úé§Ø±c‡²¨C‡‹Å ÀZ¼ÆŒƒ€€e÷ÝãÇÑ­[7´k×:t€&MšTç:BŽ€«‹J!„b&¨ˆB!f‡ B!„˜ €!„bv("„BˆÙ¡ˆB!f‡ B!„˜ €!„bv("„BˆÙ¡ˆB!f‡ B!„˜ €!„bv("„BˆÙ¡ˆB!f‡ B!„˜ÿh‡×fñ~IEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/compressed-select-nocache-shuffle-only.svg000066400000000000000000000744651231437614300313710ustar00rootroot00000000000000 Selecting with small (16 bytes) record size (file not in cache) 10 3 10 4 10 5 10 6 10 7 10 8 Number of rows 0 1 2 3 4 5 6 7 MRows/s No compression zlib lvl1 (Shuffle) lzo lvl1 (Shuffle) bzip2 lvl1 (Shuffle) PyTables-v.3.1.1/doc/source/usersguide/images/compressed-select-nocache-shuffle.svg000066400000000000000000001020411231437614300303700ustar00rootroot00000000000000 Selecting with small (16 bytes) record size (file not in cache) 10 3 10 4 10 5 10 6 10 7 10 8 Number of rows 0 1 2 3 4 5 6 7 MRows/s No compression zlib lvl1 zlib lvl1 (Shuffle) lzo lvl1 lzo lvl1 (Shuffle) bzip2 lvl1 bzip2 lvl1 (Shuffle) PyTables-v.3.1.1/doc/source/usersguide/images/compressed-select-nocache.png000066400000000000000000001652541231437614300267420ustar00rootroot00000000000000‰PNG  IHDR@°AàÚ²sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìwXÇ×Ç¿—K•Þ«60HÓˆ""`/±£˜hì½ÄKÔ ¿è›bìHbÔhÔ{Á†½¢   "EzïçýcÃz—[èa>ÏsØÙ)ggÛÙ™3爈ˆÀ`0 ƒÑ„Pjh ƒÁ`0ê¦1 ƒÁhr0ˆÁ`0 F“ƒ)@ ƒÁ`0šLb0 ƒÑä` ƒÁ`0Œ&S€ ƒÁ`49˜Ä`0 £ÉÁ ƒÁ`0M¦1 ƒÁhr0ˆÁ`0 F“ƒ)@ ƒÁ`0šLb0 ƒÑä` ƒÁ`0Œ&S€ ƒÁ`49˜Ä`0 £ÉÁ ƒÁ`0M¦1 ƒÁhr0ˆÁ`0 F“ƒ)@ ƒÁ`0šLb0 ƒÑä` ƒÁ`0Œ&S€ ƒÁ`49˜Ä`0 £ÉÁ ÿ(%%%ˆŽŽFQQQC‹òŸ%66ÙÙÙ -F¥HHH@zz:¿‹œœœJ•}óæ bccëJ4"Btt4òóóë¼­ªP\\Œèèhdff6´(µFZZZŒJ!ïúKIIAhh( ¤öÅÄÄ //¯>Äk2deeáÕ«W5ª#33ÑÑÑ(..®%©Þm˜Táûï¿GŸ>}Ю];xzzb„ 8zô(ˆ¨Ru$$$ÀÖÖáááµ"Ó­[·píÚ5AZTT¼½½qóæÍZi£¶¸xñ"¼½½‘’’ˆŒŒÄ™3g¤ò¹ººâСCõ-^µ1b6lØÀowìØÇ¯°Ü³gÏвeKœ={VæþñãÇ+<ÇǤI“àââ”””ÈÍ›““[[[üý÷ßÊ%’’ú¨Îå-,,ć~ˆ§OŸÖZ"‘»v킲²2–.]Zkõ¾Ë˜™™¡U«VuÞθqã0tèPÌ™3‡O;wî>|ˆçÏŸ#''Æ C›6m`bbRçòT•cÇŽaåÊ•æSUU…½½=´µµëAª†ÃÔÔ'Nœ@PP._¾ÜÐâ48Ê -@cãþýû8}ú4ÿU$É;w ¦¦&H{õê>|CCCtèÐ*** ëOMMÅÝ»w¡¢¢‚:Hݰ………xúô)Þ¼y{{{XYY!33YYYÈÍÍEtt4ÀÄÄæææX¶l,,,ùùùHHH€µµ5âããñøñcxxxÈT¢££ñèÑ#XZZ¢M›6ÈÊÊâë)Ovv6RSSÑ¢E >-!!ºººÐÐÐ^FKKK8;;cÙ²eÐÕÕE~~>RRRøiÐÓÓƒžž_WNNnß¾-ZÀÖÖVaÿ•ñøñcÄÅÅÁÄÄŽŽŽ¼²õúõkhjjBUU7oÞD³fÍàêê eeedddàöíÛxï½÷`nn.¨/)) ÈÈÈ€±±1:vì%¥š}_#44Tjô+++ /^„H$’[öðáÃØ¿?"£G3338::ò}¨¢¢‚   ôìÙË—/‡¥¥¥\ÙSRRPRR”””àõë×°°°àÛŠŒŒDXX,--áââ"Õiiixüø1Äb1÷XNNîܹ"’ºÿ222““ ^¿~ ccc¨ªª"""ðôéÓ*).qqqxúô)444о}{èêê>øà~ª²  ñññRe544`jjÊoÇÇÇãÁƒÐÑÑAÇŽ¥žEå9wî„»»{¥¤„„!""ÑÑÑhÑ¢ìíí¥êŒŒDLL ºwï.óñâÅ ùä@¦¦¦dllLšššäìì,÷XCBBH$Ñ›7oˆˆ¨  €tuuiíÚµ|ž™3gÒ|@DD/^$TRRBW®\! RRRâeÿâ‹/ˆˆÈÐІ FÆÆÆd``@b±˜† ¦°ßSSS©sçΤ©©I¤®®NÆÆÆTRRBDDöööÔ¿²°° }}}‰D4`À "mmmÒ××'š={6_çëׯ Ñ{ï½GZZZÔ¶m[zõ꟧k×®´jÕ*~ÛØØ˜öîÝ«PÖ!C†Ð„ äî/**"tæÌ©}íÛ·§åË—+¬¿iiiQ`` B™KKKiáÂ…¤¤¤D&&&Ô¬Y3200 °°0þ~Õ××§víÚ‘®®.5oÞœÂÂÂøò Ô£G@Í›7ç¯U"¢ÀÀ@244$___RUU%mmm233£‹/òåssséã?Üg]»v¥ŒŒ œaaa€"##Oc‡)@u@ÇŽICCƒ¦OŸN?ýô=~ü˜Á–±iÓ&jÞ¼9=þœˆˆ233ÉÝÝf̘ADÒ Ð•+WHEE…¿A iÊ”)äææFDÜ ¦eË–4|øpŠŽŽ&"¢¨¨(þ†Ÿúè#¾üðáÃÉÉɉ?wiiiü_`` ©ªªÒæÍ›)//JKKÉÏÏFŽÉ—_±b988ð_©©©Ô®];Á³§ŒvíÚÉU› ̨øóÏ?±víZ\¾|&L@ûöíѾ}{ÁTÆŽ;0tèP"<<¯_¿F·nÝäáíܹÝ»w‡‘‘ÂÃÃñüùsxxxàÖ­[ÈËËÃåË—ñüùs,]ºÖÖÖ€–-[bÖ¬YU–ÕªUü0ñˆ#øi øå—_àéé ___ˆÅbˆD"+¬OMM ݺuÃÕ«Wp†¹þþþˆˆˆ@DDrrrðèÑ#ôêիʲNœ8ï½÷ÀÉÉ mÚ´Á… äæ×ÒÒBff&îÞ½ "‚ªª*¦M›&˜5j:u‚¾¾>&OžÌ·Ó·o_òFåêêêpttDhh(öìÙƒÀÀ@ˆÅbDEEUùxÊHIIAvv6Ú´iS岯^½AMM ß}÷îß¿¯¿þ[¶lÁgŸ}Vaù)S¦ÀÒÒ***˜>}:¬¬¬ð믿f̘W¯^áüùó€GáêÕ«˜;w®Â:wìØ#F ''áááHLL„‡‡½kii!==÷îÝÀõéÔ©S¥êiÓ¦ ž?^á1 0AAApwwÇ{ï½uuuìÚµ £FBzz:ÂÃÃ‘ššŠ®]»ò2ìܹ666˜={6444 ‰0pà@x{{ã×_ERR,XmmmhjjbÑ¢EHKKÃéÓ§ùvÛµk‡Ã‡ÃÇÇvvv000ÀÞ½{Ñ·o_ >JJJPRR‚‘‘‘Bù˦“þùç~ªËÛÛ›¿.å±cÇbÛ¶mxÿý÷ù¾_rr²À¾²BII ›7oFß¾}eæ111‘{ÎŒŒŒðæÍdeeñç&77qqqŽ€6kÖL¦‘rE|þùç˜7onܸ .`òäÉPSSèQ£dæŸ0aôôô°yóf©ã9rd¥VC•!yý•^W8;;ã?þ¨Q=å122«W¯PPPÀ§¦¦"%%EpNjëÙQ]C;wîDDD¢¢¢øgÍš5kxe«è"""ªõ1¨©© MMM¬Y³FêCYÉÉÉ‚ M6VËœ={{öìAii© ýüùó "ØÙÙ¼¼¼ðã?J9:|öì™Ìz½¼¼pêÔ))çieù;wî eee)Í¿ìf´³³«ß,ݺuÃo¿ý†‡+å;£lÅ̲eËàëë 8p """ðóÏ?+üê±³³CjjªTŸV‡ÐÐPZ´h‘#Gbûöí(,,ÄË—/«]gbb"lmmyE¥´´”_ U]ŒŒŒ ªªŠ/^T¹¬X,F÷îݱoß>Azpp0ºvíZ¥ºÂÃÃñâÅ x{{óiÝ»w‡ƒƒ† ôïߟߧ¡¡-ZH]kÝ»wÇŽ;¤ÎaÙõûøñcÜ´í‡~ˆ-[¶ ;;[Jq([SU444àææ†~øAj_Ù=âááëׯ ‚¼|ù(--Åï¿ÿÎï»|ù2ŠŠŠÐ­[7…m;99á÷ßç¿ôKKK+th÷òåKdffBWW}úôÁúõëáàà ÷åúÕW_áÚµk8zô¨Ôª ///ìÛ·OÊ1¡¼g À)ŒêêêÕºþÊãåå…k×®áÑ£G‚ôØØØ9KôôôD^^þúë/>íÂ… ‰Düu^[ϽÊâáá_ýUàL4-- ÉÉÉHLL„•••àCKÒÁd‹-`ee%÷^"‘žžžØ±c‡”Ò'ë\W÷^jL° Z&%%'ND@@:wî ܺu 7oÞÄìÙ³yGV_}õÜÝÝáêêŠaÆ¡¨¨ÿý7JKKe:¢›={6Ž;Œ;ššš¸}û6®\¹‚¬¬,ØÚÚbùòå˜5kŽ9‚=zàÏ?ÿ„²²2Î;‡!C†`éÒ¥˜:u* 1dÈjùY¾|9._¾ ggg´hÑ)))°³³,g–…’’|||pâÄ Œ1÷RúàƒpèÐ!… ——D"† 888Tê G_~ù%=z„@[[gϞŀ¤¦ZªÂ¤I“лwoøúú—/_F|||œ–©¨¨ÀÓÓÁÁÁåà®±uëÖñÊÄž={põêUŒ=...€Í›7ÃÍÍ #FŒÀûï¿'N ,,Lʦ,>ýôS 4qqqøå—_пÞ†¤Œ9sæ`úô騴i“Ô´ÏàÁƒ±dÉ}––†¸¸¸`Ïž=˜3göíÛ‡–-[bìØ±000ÀáÇñùçŸcüøñ˜7oF???ˆÅb8pÓ§Oçû\Ë–-ã§  €ßÿ‰‰‰èСƒÜ2×®]Ãܹs1jÔ(ØÚÚâáÇxñâ…Ì©´;wî`ÅŠèÒ¥ øt###¬Y³¸p᜜œøÑ£ëׯ#))‰W<Ë£¤¤///WË6O’ÁƒcĈèÖ­Æccc<|øgÏžETTï £ªtìØS§NÅÀ1fÌäççãðáÃX°`?%Û¿|üñÇ=z4Z·n www 8°FÇ£ˆÿýï8w¬0~üx”””àÀ8sæ &Mš„7bðàÁhÙ²%®^½Š/^ðÏa‘H„ÀÀ@Œ5 ·o߯¨Q£ß~û­Ò^Ã7mÚ///¸¹¹aàÀÈÎÎÆ•+W`ii)P¬^¿~{÷îaçÎuÒï âÕ«W¯nh!ÎÎÎðõõ…žžòó󑜜 '''|ñŘ9s&ŸO[[3fÌ€ªª*¢¢¢™™ ¬Zµ úúú‰DPUU…··7455!‹1a´hÑÑÑш‹‹ƒƒƒ¾úê+~ÊËÇÇݺuCnn.âããÑ©S',_¾ººº044Ä|€ÈÈHäääÀÛÛFFFPQQ´´´ ‰ §§ooo^¡‰DPWW‡··7444 ¥¥…I“&¡GèÝ»7¾þúk¤¦¦"77·BG{puu4hlmm UUU,_¾\à4UMM ¾¾¾ˆGRRºuë&ׇ”X,†tuu!‰xÁ²c—|&Ëóy¤££ƒ)S¦@CCqqqPUUżyóàåå333 8·ÿœ6m&NœtìØwÝŽ1%%%xùò%Z´hµk×ÂÜÜ"‘–––pwwô‡¡¡!¼¼¼pJï´iÓPZZŠˆˆäçç£W¯^øôÓOþ‹5kÖÈ<ަ‚ˆês‚”ñÎSPP€ÌÌL~Ž=-- îîî˜ýôÓ–®qQTT;;;øùùáË/¿lhqxyyÁÉÉ [·n­·6ûôé--­J…aÔœââb´k×C‡źuëZF-{{{×à IDATÖ(Kc€)@à‚ýikk Œ-²‰‰‰AË–-akk „‡‡ÃÝݧNªÐ³,£êüñÇèׯ‚ƒƒÑ§OŸ†W®sçÎxúô)oÓV×|ýõ×X¿~=îÝ»§Ð 4£v¹zõ*z÷î'Nl½ï&ùùùèׯ´´´púôi…Þä›R*))Á°aߟ¯ÐDll,† ‚ÄÄDäää`ìØ±Ø²eKC4fJKKqÿþ}„‡‡£´´íÚµShËÀ¨9ýõ +\)W_„……!66¶Ú¶8U¥¨¨çÎC›6mx_LŒúãï¿ÿF~~> ›Ðxùò%îÞ½ ooïj­¦ll4JhöìÙ¸téÌÍÍ.½9r$š7oŽ 6 ##;vÄ7ß|ƒáÇן° ƒÁ`0êF7Ô±~ýz$$$T5º¸¸ÁÁÁ˜6m.ù¨Q£˜}ƒÁ`0M€Fµ þرc8rä~ÿýw™ÎÙ$)[å!¹ZÈÖÖVàS",, aaau&/ƒÁ`0ÿ5ÔÕÕѯ_¿†£Îi4 Ð7°råJ„„„ Y³fæOOOᮆ†222øíƒâàÁƒ°··¯}Ë‘==½jbGGGÃÆÆ¦Öò*Ê#oŸ¬ôòiÅÅÅHHHx ŒŒ¬pyvmPXXˆäääj»÷¯Ê9ÊÌÌDqq1 äæ©­>NJJ‚–––àº/Ÿ¯¾úXžŒ•¥ªç¨¢¶RSS¡¬¬,X\†¼sTöl´‘uÝ–o;66&&&õ² &}\Õò ù¼(Žrss‘ššZ/Œe]U¡*}\™gK]õqù´‚‚Ü»w •’ý¦žƒ¯ÖóæÍ£N:‘ŸŸùùù‘››“ŸŸù[’¸¸8@ÙÙÙ|ZPPõèуßö÷÷'ÿúŸvïÞM/^¼¨vùªÈY™¼ŠòÈÛ'+½|ZZZõ½ŒV(OmOß}÷]µËWå]¿~Ο?¯0Omõñ/¿üBOŸ>U˜¯¾úXVÛU¡ªç¨¢¶ÎŸ?Oׯ_—¹OÞ9 ¡Aš¬ë¶|ÛS§N¥'OžT(smPÓçÒ»ò¼(Žþùçúä“O*”§6uT…ªôqež-uÕÇåÓâããÉÔÔT¡,…F£=}ú”Ο?Ïÿ.\HŽŽŽtþüy***’Ê_XXHÚÚÚtóæM>mÞ¼y4uêT~»> ””*((¨vùøøøZÍ«(¼}²Ò˧SRR’ ­¾ú¸¨¨ˆ’““«]¾*ç(''‡233æ©­>NKK£üü|…ùê«eµ]ªzŽ*j+33Sæ‘üs”••EYYY‚4Y×mù¶¿ûî»{U¨i;ïÊó¢ü9zñâíÞ½»ByjY×AU¨JWæÙRW}\>­))@f ÌÞÞ^0U•€;wîæ1;tè???,]º***7nV¯^   ¼~ýû÷ï—ŠV]_(š*© Š";W'¯¢<òöÉJ/Ÿ&‹+ YW(+++ô|[U9G•™†­­>–5D_•ë¡¶©IÛU=GµUÔSòΑ,/¿²®Ûwµ«Z¾!Ÿ•¹ê yÞž+KUú¸2Ï–ºêãŠênÌ4¨®{Ž~ýµ¡¥¨w؃Á`T“ýûS§„i99¤`¼3ääåL8’”š†jÐ4Ž’Á`0j™øxà“O$SRh’ÓõBaa!R¹á6Fmñ¿ÿ±±o·Åbü£¢ÒpòÔ#Lb0Œj0m–&™ÂÙÀ… !Qã‡ÙÕ2Ož7 ’r&MBb0 †,öîΜ)Ÿê € ãñð!ðúu}KՀܽËYƒ×1̨–™=(*z»maì%KNžz†)@ ƒQ^¿æÍ¦ùøå"ïàâÅú“©A9tèÖ `^õß-~þøãaÚ†  Æ@{—` ƒgÿþýØ´iBCCé‘‘‘ؾ}{IÕø(..ÆêÕ«qãÆ†…Q ¦NÒÓßnki?þx{¿µš€°r%0z4—lß^ç˨™ P-‘‘,^,LëÕ ðóky¦1x¾ùæ,_¾3f̤?|øŸ}öYIÕø(..ÆÁƒÑТ0ªÈîÝÀ¹s´uë@_ÿ­ üöPZZ¯âÕÙÙÀðáÀÚµoÓ’’€ƒë´YfTK¬Z%œ²TUNž‚ùj`vìà®Åº¦ysàÖ­ŠóM›6 AAA8{ö,XaþœœhjjVYž¢¢"¨ÈXi@DÈËËC³fÍ–ÏË˃††† -??êêòQòóó¥ÊU!77Wîq–––¢¤¤Dæñ@AAÔÔÔømuuu„……ÉÌ›ŸŸˆÅb…2•¯“Q÷¼z,X LëÕ ˜>ûùr_|û-•Åm§¦r÷\çÎõ+g Ì:•gëV`üø:kšÙÕ÷ïKOW.^ ØÙ5Œ< j`rr¸ Šuý{ó¦rò´nÝS¦LÁgŸ}†RŸ¯»ví‚••´´´`aaÀJ|=¼~ýÆ ƒ¶¶6444ààà€#GŽàš¹sçBOOZZZprrÂÕ«Wù²7nÜ€ ~úé'´mÛÍš5CÛ¶mñøñcÂÒÒ:::9r¤`deñâÅ=z4¦N ]]]hkkcÀ€H“X¾ãææ†½{÷bÆ èÒ¥ ¼½½˜üðCÁPÿ’%K0fÌ|úé§000€±±1f̘!8FÝ2u*7sP†¶6°s' qÛ**@Â2n쯿€Nd+?pç‹û_†ˆ ZWRò6ÍÆ†›Êl‚0ˆ!Å矎ÈÈHþe^ž'N`îܹ˜3g^¾|‰E‹aùò娷oŸÜ:‹ŠŠÐ»wo$$$`ß¾}xúô)¦OŸŽû÷ï.\ˆsçÎaûöíxöìºvíŠ>}ú &&7âƒ;wbË–-¸{÷.,,,àêêŠàà`üøã¸~ý:"##ñý÷ßóí&''ãäÉ“044Ä¥K—°iÓ&ܾ}[0¥‹Ù³gãÊ•+;v,ÆðóóëW¯páÂ$$$`ܸqðõõERRÀ××&&&Gdd$–/_ŽÄÄDÀêÕ«ñÛo¿á?þ@bb"~þùgHØG¼|ù999€¬¬,ôèÑfff¸sçŽ=ŠððpAÒ””:tEEE8þŸ»»; 8PPÖÏÏåÖýË/¿zøð¡Ô¾ôôt‹Å´yóf>­´´” iÑ¢EDDtåÊ@©©©|ž}ûö‘ššš ®õë×SûöíùíñãÇÓСCy–.]Jºººü¶™™­^½ZçÆ¤¤¤D‘‘‘|ZQQéëëÓþýû©¤¤„ttthÍš52÷Ã?$OOO*,,”¹_GG‡:DDD¤¤¤$8¶àà`AMš4‰ $¨cîܹ4xð`™õW†´´4A?0dóò%‘ŽŽð~êÓG:ß‘#GèÎdA>±˜Hâ´¾›-X ûÁ2hÑ‘#Â4¢×¯ëD”¸¸8:uêTÔÝèIN&24”>åˆ'SSÓ°þa#@ Ì´iœGÙºþUÆþG’%K– -- ;vìÚ÷äÉøøøÒ|||ŽÉ¡U BCCaeeGGG©}eå$ë‰Dðòò“'Oäʨ­­ "¤ð#4òpwwGFF†àKÒÔÔTJ^ððð€™™ÌÌÌ`ee…ììlÄÄÄ@II .„¿¿?0eÊü!±¤têÔ©xòä ¬¬¬àëë‹;w¢HÒ߆Ož<³³3ôõõù´îÝ»C,+<~CCCÄÇÇ+E‚IJÔKÿ899ÕòÑ3*KLŒ´«”~ý€É“eçOIIAqq1úö¦¿“v@—.qË×Ê+?ffÀ•+BåàV~éè¼ÝNL®u±˜ P5()fÍâ&½Ê°³“¾¸› lˆ! øûûcÚ´i000àÓW­Z…>}ú`êÔ©ðóóÃÑ£GqéÒ%œ>>pwwÇ¢E‹àêêŠøøxœ¬Ê=Ü&NœˆóçÏ l{¼¼¼pæÌð/écÇŽaÀ€rëVRRÂ¥K—àïï}ûö!((ŽŽŽ¼ÂðÅ_@__?ýôáææ†›7oÂÈÈ «« ///Á”˜¡¡!¼¼¼혚šÂÃÃC¦««‹sçÎáñãÇhÙ²%NŸ>~ýúñû»ví )™wïÞï¾ûû÷ïGdd$ŒŒŒàéé‰.]º@GG£GÆÎ;ñêÕ+èééaĈ˜4i`Ê”)øá‡°wï^ˆD"tîÜÛ¶mãëîÖ­¯ ©ªªâ¯¿þ¢E‹0þ|hiiaìØ±àóÛÙÙÁÌÌL Ÿµµ5:vì(·ÏÕgûviÛM›KKùe|}}ùÿ==…å/\x ¢"n*뇤÷ÅyT4_?{6§ô”4ܼÉýÜÜjMDfTE¥ÍÍ­æc@Då­H<«W¯ü•Åüùóaccƒùóç×PŒ*1aÂdddàĉ -ÊŽôôtØØØ ]2®ÑÑ€£#ç츌e?•Ïúõ†^½8ÏÐÿYRR8cæ+W„é"°z5÷•3½- ¡¿€±cî1uÌÇû÷¿ÝÖÖÂÃss¹Eàââ"˜’o¬0 ƒÁø"`Ò$¡ò£§'{P¤úè#Þ.‡ÁPÄóçÀ’%´Aƒ¸Y„Êàëë CCC~[ÂÌ Àp5Ø7ßC†-½N!ºu ¨Î L‘ˆ³#’äûï¹r-Àl€*ɦM€¤1‘ˆ3†® Æ`Sƒ)@ £ÉS6õõot€AÍ/ÊOƒýgâ‚ãÆqŽ ËÇû?ž+ç´"–¤'’>¶âã²¥þˆ$PàÎI×® #Ϧ1*Å­[·ð›+ΣGâéÓ§Uª3%%{öìÄÈRDaa!öìÙƒäää*µÃ`TD` ´ Ì–-U3—´8ÃgÉîgÏ8ë%!ðö–6LVRÖ­öìáV‘_~^½úwCG‡S°$©¥ø`̨,X ­ÉýuÃÉó†)@ŒJqàÀ¬_¿žß~ÿý÷1R½í¢E‹*H²xñâ&NœˆlI‹SäææbâĉˆŠŠ’Ú÷ÓO?É ³Á`TDT°l™0mèP`̘ªÕ#iúúÒ+Àtèî].’{yoä::ÀéÓÕvŒW\Ì-ûûo‰Ä¹s…†Ó×®q‘âk³ª€_•mûòKà_w" !LbT‹ÿû¿ÿ Z =z#FŒÀŒ3*=’Ä`”AÄÍH®Ð24¬^(«ò6@ÀÈèÈÎ9?Ló/­Zׯ |xUÄÞ½\Ô doÏ IR £@ÌHÒFÎ;S§6Œ<ïÌbs/á®D_©8c ÑQÓÁ$WùÆÀ>ÄÝ»w¥Ò;tè 3ƒX,†X†AÝ_ý…ß~û jjj3f ¬­­+%_ff&Ž?Ž~ýú þ¥§§ãäÉ“6l˜ÌrVVV4h”••¥Âl0±e ·D]’ÀÀ*›ÀÈ¥o_ÀßÿíöåËœ=°‚wµ gò¿ÿ•3Ôàã=ÊM‘T“ÂB®j€ä0w®ÐùÑÁƒÜ4›±qµÛc(àë¯ÈÈ·Ûb1·¬±². š Lj`®D_Á‚ u?’b­k­Pzöì™ ”Ejj*®^½ŠíÛ·ËT€6nÜ[[[tîÜYVTTüþûïØ¼y3îÞ½ KEîsÿE[[_|ñ"""°víZ>}Ïž=Ø´iƇLÉÜÿÒ¥KtéÒoÞ¼a £JDDŸ}&L>œs”[RRR ««+`Û©7¢T„;+‹péÞ½šBW…Ü\ÎG–òÌ™œö§ Øneرƒ‹™p¦'ššÿî8hÙòíû‚®@ùN¯………ÈÎ΄æa€ëã/¿¦Íœ ¸º6Œ<ïl Œ€¾?yò$Nž<‰cÇŽAUU À”)S*]‡^¼x3gÎàÅ‹PVV– ¤*‘H„iÓ¦a÷îÝ‚°»víÂÔ©SYäsF­RZ*=õedÄ}0W—ò6@g[\~&¨^ì€bcnݤ•ee.TPP•Ÿ¼<@â[ÅÅÀ””¸ð’|÷tŒ±*Àl€ä0w.ŸÿvÛÔX³¦áäyG`o†Ë–-CLL ~þùç*)...|¬.SSStíÚ7oÞ¬tùI“&!%%gÏžܼyááá˜,/ü6ƒQM6m*g³N/«¶ÒȲÀèúunè©üˆ¨×xyyÕ$(ˆ[á.Iù>ŤICBàlM®f$ƒ“'sç„ië×sÑ{ iTS`{÷îåWêêêb̘1X¬`eÃîÝ»qèÐ!AÚûï¿5õ¨9»š»b^çyuÞŽF册:„ï¿ÿׯ_‡žž^Ú´µµEXXX¥ócøðáØ¹s'Œ]»vaÈ!RA@Œšðì°r¥0mäH.Þg]PÞн{@RRÍ”-¹üô0}úÛPôe´kÇ­ôjÕªVšÉΖ½²ZJÒÓãâI:TÚ²˨¹¹À¼rïoïª/al¢4*èÞ½{˜2e :v숗/_bÔ¨Q°´´Ä‡~(3xx8`ÆŒ|šimY?V/k/xY{Uœ±xôè&OžŒüík!tõµk×ЦM›*•™1czö쉈ˆ®æ’xf$AX7Õ%ÉÂ…Üh£R4*¨ŒK—.aÉ’%PVV†ŸŸŸÂ¼aaa ÂáÇe®2j*|õÕWˆŠŠÂ‰'`ooÏÿV–Ÿ+PÀòåËÑ¥KtèÐnnn=z4>úè£*Ë2cÆ ÿÇ)""‚ß¾~ý:ÅÄÄÐÉ“'iëÖ­tõêÕ ÛÌÌ̤***¤gggSHHeee Ò‹ŠŠ($$„233ù´gÏžIÉü÷ßW«šiii¤©©)H+}7–íâb¢Î‰âT"SS¢%Kê§ý‚"MMaû·o×°þß' â+È 21!ú÷¨íãùôSÒÖ–lÒŸZ´n#£|I ‘ '_Ù﫯þ3×Ç;·}àÀÛó]ö;~¼Úõ>}šüýýÉËË‹-ZDÔh” Qii)õîÝ›*]fܸq4Fâî•¥ð”gÞ¼yBb4=ÒÒÒHWW·¡Å¨¾þZ '@tâDí¶‘œœ,¥ÌKòÁÂö×®­Acß}G¤¬,}P..D115¨X1‹ ›35%:}Z˜fc#§ð7ß3Z[sši((( ”””Ç;MF‘¹¹°/ ¨µêãããÉÔÔ´Öêû/Ó(§ÀίLçÎñXjBZ>íÛ·g6ŒFÆ“'ÀçŸ Ó>úˆ‹÷U›(²jÑhÞ<ÎÉ]y:ÇýÅM…Ôññœ«I–/çÌ{$í¾££×¯eT0y2 ¡ñv;&®’ ÌÜÅ,é@]½Ö‚Í65T\\Œ;v ÷_Ïfñññ8~ü8ºvíÊç™={6>Ìo8pyÿ.IJJÂÞ½{ѳgÏúœÁ`Ô%%ܪ/ÉUáffuó¾PdH+@ׯsFÃUâøqnyyV­âÂZHúÜ©eþïÿ„«ç¬¬€3€fÍa^™v@Ò˳«x"š¼ Ѓ\¬I–/çƒÁ¨¾ù¸uK˜¶}{Â_U›V­„nxŠ‹¹Ø`•&'GÚ狆pè«c>½| üðƒ0måJ@MûßÃC¸O¦HC‡„ÈX6Æ çÄRÂS>Z·æÜ0ªE£ñ¤¢¢‚Û·o#;;qqq°°°€¶¶¶ Ï«r‘ˆ¬`eeÍ:üzzˆ…AõCNNRSSѼyóJå/((@||<¬­­+\ Æ`”'4”‹*ÉÇs+Æëy~€$é×O8tá 'ί4«W £¹+)qÁFËkuÀ_pO˰µåœ<—ááÁù*C®ääxyW$@oÝ*ô¤€&íh÷n鈳oµPF•i4#@ehiiÁÎÎNJù‘‡®®.ìììš¼ò;v¬Sǃ'Nœ@‡*ÿÞ½{°µµ•éžàСC(ÿvc0þ…HzêËÂBø’®m*²¤—ÃWÚèñc.~‡$³fÕ‹ò ìÙ#Ló÷F´//ÆýûÜ€•LÊí߯x]½MÖ(5Uz¤Ç×Wú‚bT‰F§17ýõ¾ùæÌž=wîÜihqÿQ‚ƒÛ·…i?üèë×]›Ù€碧Œ˜Î?Q…Ìš%4z63«·`—¦í줽X[Xp£Berà HŽçæ»vUJ–&i”œ ÌŸÏý-CK ظ±ádj$4š)°w–7o€¸¸ºoGUxï½JgOIIAVV–Tº±±1?ZVXXˆ -- :t€QÜéæääàÍ›7RÓ[yyyHLL„µµµÌr×®]Ó'O _—o2Æ;OyÝ`ôh`àÀ†‘E--n´$$ämÚ… €½½‚B?ýÄ…¤ž‚]>yÂù,”$ ‹¥ózx/^¼ÝþûoNá“B,æºåËߦmÛ,XPëNß9ˆ€ðp®óÊ~ÏžIçó÷ç¬Ð5‚)@ ÍÏ?s7~]cmÍ­O­$Ÿþ9~þùg~»¸¸9998zô(FŒ'Ož`øðáˆŽŽ†¡¡!RSS±yófL›6­Rõ'&&¢uëÖ¸xñ"zõêŧóÍ78räˆ\÷K–,Lž<od#b4y.\ŽþˆDõã ·26@g$©ýú«´m3OZðé§Â´=¸uüõ€¿?Û« ''ùAc=<¸Ù¬2äÚÀÔ©œ&•ŸÏm¿xÁy³4H¡<Î(?Ÿ³Ò/Sv®_RR Snõ¡óÑïлw­F9ir4qu›!mÛ¶!==éééHKKC¯^½Ð½{w 2D„qãÆ¡M›6ˆŽŽFLL Ö¯_Y³fUÚïRË–-Ñ»woìܹ“O+--ÅîÝ»1}úôº:,F üèÏðáUü¬6•±¤—Ã_¹òVâ³Ï„QGUU¥ñÔ÷îÇŽ Ó-6+oTU&††@ù Õ²–÷—ã·JLä\,^Ì…0ÑÕºwçFÃΜ©Pù€éÅÛ°s2|}¹v>>ÀºuœÑ?£j0ˆQ!kÖ¬Á;wpäÈ(++#44wîÜÁôéÓaffeee̘1ºººØ/ù X3fÌÀ‰'xç“—/_Æ›7o0nܸº:F#çÏ?9_€’¬XQ?mWÆàFQÌÍßnçåqrKqó¦ôÚóÅ‹+˜/«=>ÿ\¯¬S'`Èùù„³r¼”ËC_º<}ªP¦wʈˆ3^ÿá`üxnɺ™|nýzàŸ„KëäP1îÁ˜ƒa8?ÑßWTüñ°d о=`cÃÍ.ž9ÙV1æÀccÀÙ¹îÛ±°¨V±³gÏâË/¿ÄüÀóçÏݺuãó)))ÁÝÝßW ###ìÛ· ,À®]»0zôhèÖƒm£qR~ôgÀÀÕµadQD߾•U.}úHd(-å¼=K¡ØØpÎwêþá^¢’Tds­¤Ä jH®lûûoÀÑQNWW [7¡ÆXo#\µNn.§´JNgUru› KÀçS=ðÑæÎȆV¥ŠÆÄß}ÇýÔÕ9p¿Ö­«.Jc‡)@ ͘1ÒÞQÿ#DFFbìØ±Øºu+ÜÜÜøô2c稨(tìØ‘Oöì™Àž§"”••1eÊìܹãÆÃÉ“'qµ¼±'ƒQInÞäÜâHROú€ÊÛÒ Ð¯¿rƒ㦼Îã~ááò«ÍÏçë 8³6m¸…p³nÌ}›cÈ!''Æ ƒŸŸ&Ož,Øçèè===\¼x‘O‹ŠŠBDD<==«ÔΔ)SŽÙ³gÃÁÁ:uªùMò#>>܈D}QY €3^•\ðô䉄ÄiÍmÈàƒjGÐ ¸r…›’¤²+î+íºŒáÃKË·ÛÙÙÒN‡$h0 ÒR. EP÷ÁjcÃÉ=jç\êöíÊ)?**Ü\âüùÀ‘#Ü à/8ëñ™3¹ùÑ/Œþáš,£lñ\¯^À† œû„¨(N/îߟñQDDçJªOÎkÈÿ³wÞaQ]鈅¢bE±+jì±k”{IŒÝØbÅšK°aù4Æ+ö^±`T,` ÖE@E…JG¤s¿?Æ-sw]Ø»÷î2¿çÙ'Îì-g'ËîÙ9ï9§/©?ùömá—ÅÐa;@ µÌ™3OŸ>E«V­ðÒO¸þýû£gÏžX½z5&OžŒÀÀ@Ô®]»wïFçÎ1dÈ­îãää9rÛøz5ìØ±aaaøûï¿‘––†yóæ¡FL8]ÌyüX5d£ÏÝ€h€4¥lY eKºVÎ… À¸q :eGÊÆF#°®à¯Û矓MhÓ†4F•ù¯^‘’<#ðææä‹_ù¦›7Ó§«U[ëUtå )?pó&ñF´nÜRxÊÕU±»Óªùÿ©¿ýF{÷¦}E€´›:•<ÒÒHv¡lwH¹$ŸÔTR+KÖ‹¶Q#²3äáQ¼ÚŠ1ˆ!gîܹhòIÔ¾}{T¨PAåY   & nݺ8vìÞ½{‡+V`âĉù¶¬øì³ÏäiìÊ,^¼Í›7Ç7¼¬ªU«ÂËË ÖJ?m,--ammPsŒâ·7-Øuu%ÙâRÆÝv€üü€qµüIi e~úI°ï|.\P‘/[¦ùù²Æ¨ÊenÞ$E‹ódÂrYÙî°0àüyò,qq¤Œ8ߣքڵépVƒ…êÑ–(õíP@($:*ÓûDO.s†nÜ ‚é¼xò„<þ÷? L™ 03뤵Í ÇÈ///ÎËË+ßc<==¹_~ùE?1:$!!³µµÛŒ"Ìq¦¦G\ òðõÕ¿±±±\VV–ÆÇÒ6—·Ëä¸úõéIŽËÌÐjš–-éÛ÷î­ý5<=ék̘¡ÁI#GÒ'¹»«=,##ƒ‹‹‹ÓÞ(M¹qƒãªV¥mÉëaiÉq®®7gÇ:Åq11:3ã—_è[Õ¬Éq¹¹…¿^r2Ç<ÉqãÆq\•*¿´2e¾×Ùk‘2LÄ`0 š+èd©fÍÄ©ú¬ á"傿ãתöÅøõWºé–€œ>­Z@réRí¯£µ !/eüüÔV@L”›KÞH;Ó g•)WŽtÒ]½šl“%'“í­5kH{OY²º€ßv„Bm$É)]š4Ýݾ¼¼‡ÉËusS_Õ»¸ÀB` Ã`yõJµUƒ¾êþðÑF/žî݉¶:^c!xJã#H³à8iSfÐ ÎÒ¾ôàÉÏWúÒ¢‰[Þº¥0hË•h€bbÈZóSbÓøñäEÕ­«ÛûæÁÕ«´li |û­nïѤ yüø# ·ùù‘PÙ… tÝMc‡í1 ƒeõj:ù¦A’Xd(ÈšyoÄtØ@©r°v­Þì8røçÅØÔ”tª( •+“$)ÙÙÀ;œÈ/Œèã¨éG¨S._&^ßù11!ÞÁõëÀ˜1zs~Uñó€¤\œPØÛ“^y{÷’Äsçâ`nþ¨àæ1 ƒ$2ؽ›ž›?¿h¡‚¢‡l-ëÀôì ôÆôÁŸô+Vè4¤’99ÀâÅôܰaÄ™,,… ƒ D—ÈNN&`•ÈÌÌD|||á “‘“C¶¼zô ßúÊ8:’­+H–šy÷8uŠž+Hü¬KLMfͲ`añP7æ1 ƒdͺ“@Íšªí¥ô‰¶ ¨Z6 [-hýK|­V€Ë:ìÛGÔ37'MP‹B¡ ÕoûÍ›©ô>h€"#IŠà²eªÍʺv%Åw4©ú(»wÓïé ô-–0ˆÁ`ïÞA§2óæ‰+èÔ´Åò娒.æÂl¥«$ HV–ªÐyÌ V­¢]W«Æ¨ÊLœHD/2ž?§ÂSEÖ;GB^üækffd!þú‹ôëŽSmýÆÊ› s€ †ÁñË/t³ÇªUI¿Iƒ"8XE糓°í^‹|ÀµÂt Ö’2fføVY`¨†7oÞàÇù“'Ož„¹¹¹Æ Q=oeEÂhÇKNXãëKwß(]:T<{Š,&2ÍJ•‚gÕª‚ßÇAÂ^Ë—/LjO[Ï­Zµ‚¿¿?öïß.]º víÚòã6oÞŒgÏž!((ˆêÖÎgݺuèÕ« uëÖ8sæ :„–-[ª»~ýzܹsZì•›ššbüøñضm-ZSSSܸqáááøV×õ㢲}»ê®_/+&j€8ŽlW)Ç:ªW';B Ýá•…°~~@ïÞº·sÝ:Õ]´Y³t€„Á”ïÐ^™ï¾#q:Yó§O{ú4‚¬­UÃ`iiÄæ×FHç#G ××CðÅÏÇ¥J‰cKqƒ9@"ÓÉÎ$ö‹D™Ö­[ã¥rn&€7n`îܹðõõEõêÕµºž«««ÊõàÈ‘#˜;w.|||ЪU+­®9vìX,Y²/^„»»;vî܉ ¼õãz%3Su×bòdº™¨Ø¨Ú¾]µ'ÄÆò&Y={Ò_†Êb]OJ(3w.²®KÜÜèv^Zé€*U"!«Ã‡åS•DŸÓ§éãž=#!³'OT¯1|8‰›JÔ£xõФ¿+ÃÂ_úƒ…ÀùÒJ1†ÈÈH <Ë–-ËWc“ïÞ½ƒ-ïÓvÏž=3f öíÛ‡aÆåqfÞT¬X}ûöÅŽ;’’‚cÇŽá;ö)bTìÙC‡ J”n×BÞ¿'½¥”éÓ‡<>Ñ­ÝøýÅ yö·ÎøßÿHL†££°»hy5FÕ~°3gåŒN eKUçÇÆ†§÷퓬ó‡Ê"]]Ï>Ïžâs€y’••…«W¯¢yóæH Ž¢sçΘ3gŽÖ×KHH@PP•uµtéRL›6 'NœÀ!C mëwß}‡?ÿü6l@µjÕЉÕ7rr€U«è¹ñãõÖ*KcòÕ}ÿ=w²±QIg+]šhw•Ñå.PLŒª.xÞ<¢Õ uQeÙíÑ®éÿ‰ÌÜ\į[Gâ#G’²Õ|ªQ#w3¦H¶ MVñÑ”a¿Ûô 1(Nž<‰Š+"99[·n…¥¥%¦}ú6cÆ üóÏ??~<+mK»¹¹Á)êi;vì@ff&¢¢¢°eË8;;˵9;v쀗—&Mš„ׯ_ã7¥ýWWW4Ñ¢J×®]áììŒÅ‹ãg¾RV ·nÝBRR¢££Q¢D \¸pŽŽŽrg!¤+äZZ’°ÔÈS ÒÔ ýž=k×c??’ž® V®¤}…*Uô“AçæFoÚ;kqiӀѣ±‚vïFŸ‹éf2Æ'17)ÔE(€S§hM›ƒ‰ä1ôs€rÚ·oòåËcÖ¬YˆŽŽF«V­pëÖ-ùzJJ Zµj…}¼tÔŠ+ÂÉÉ ­ZµRq„,--±bÅ ¤¤¤ ]»vX½z5¬¬¬eÊ”A§NðôéS<}ú”:ÏÞÞ>O¨J•**µ{LLL0þ|ìÝ»#Gޤžspp@gÞ'îîÝ» dggcÕªUpsscÄÈÍU­W3z4i}!5Ôj€²³‰XI¹qƒ@;¨îît1Â+Wˆþ©¨U®#"TŶ ùä/è 77àÀÅX+@J"Ï ¼Êú|ø êü”)üþ;9Ö@àÿÿ5J?ÿ?JpŒ<ñòòâ¼¼¼ò=ÆÓÓ“ûå—_ôc‘ÍàΜ9#¶)ŒüÊ•¢›1q"}MggŽËÌ,úu5áÑ#úÞvväujÅ‚ªë({´hÁqaa‚Ø.ÁÁª/#8Xl«QQQ\… Ä6C/0 ƒÁ,Ju8ß|Ô¬)Ž-¡¢Šˆ/¦><ßø‰ À/_UTÐË—ªZ“Ÿ~¢×BÂoŒš˜¨acTe¾û07G&ª ÐôéÀÍ›Eo_¯g~ÿwé|*§ÆÐ#Ìb‚‰‰ <==QSªßV ÉãëKúTÊ05UM¤’þþþT!PÌœI—s¶µUéþ®Žž=鱟_ÑìZºTQJ eqxQbA15Ú¶¥ç´ƒU­ ôïX©pú4ÑûˆÝWKÒÓU%aLü,F¥zòä þüóO„……ÁÖÖ_ý5ÚòÿòxüùçŸðó󃽽=FŽ©ÒšQ8LMM©¶ †¶ðw ò©Bi€.\ U‡•ñö¦Û;äAd'H&zôˆ´Ð¢•Ÿœà``ÿ~znñbÀÌLûk77Ú‘ T-ô\ Ó¦¡ò±cèÓ¶-)lX­šNmÔGÒ *ýû‹gOqƨv€fΜ‰ˆˆ¸¹¹¡L™2èÒ¥ ò<~Û¶m˜1cš~ªÚ®];•®á Cÿ\¾ ܾMÏ-X Ž-Z“žLJϵh¡qÊUùò¤›ƒ2…Ýòò¢ O7j|ýuá®UŠÔUF‡dÇçúuƒu~Uñó·ßê/É 1ª ³gÏÂRi;4 wïÞEûöíÕ¿víZlܸQÞª!44»wïÆ’%K´ºïåË—‘žž^xà ò{vùrzìá!ÙNrâââ`kk óU«è †¦¦ä[ÏTóß›îîÀ½{бŸŸ<\c?Ž£ç–.ÕÊ Ñ¦ iŒ*“H½|IúÁjÛ}>ó»ïð!%º7R<~ ܺ¥›š&ˆgOqǨ ™ó“‘‘Ë—/ãñãÇòîä|’““J5ÞtuuÅE~]òèÞ½;¬­­‘˜˜XxË9=ÒªæC{òZã™3gŠ`MþW¯ÒsŸÚeItqvFY~ÕÆ‰Iµb-pw§C€ýEJhã¼,ZDgß·h!^¨¥dI IÚ© $.´!66AAAª½À þîOÏžt¡H†ž; MÌÌÌ8+++îСCyóüùs—••%ŸÛ³g׬Y3ùØËË‹«^½º<^]Z<³1ëvììÅuë&-ûòתEå6{•,Éq Z_/+‹ãlmÉë—]îÎÍÏ¿{W±~²óÏŸw}<=i{fΔÀÿ/=ŽSR8ÎÒÒ‹J}2Dö9s†óòòâ:uêÄÍž=›sppàŠFéåääp¡¡¡\ƒ 8µÇ„††r¸ŒŒ ùÜ®]»¸Ö­[ËÇ^Ôb0º#(Hµ>Š.êàè~Ñ"€ãöî-ôå  /µd‰æçöèAŸëæVh3tÆÑ£´MJµÅ‚ß§_Õª—-¶Uª°:@Ž©©)j×®àìÙ³j‘u ‹‹“Ͻ{÷4ÈÒ`è–èèh±M0z eù™_íÚ‘)’çÃÄMŸªX§NÀˆ…¾daÓáoÜPí0Î×T‰ºÆ¨iiÚ]#33ñññ(AøµÆ×6ƒÆh ÜÜ\-¶ F!¬ñ¿ÿ’Ò.Ê‚öàåÿ¨(È«YX¿þZ¤Kº»Óã;wHÁ‚à¯Y·nZöÞ~cÔ¬,-£‚h€òËì•*ÿ Ü¿¯››ãƉgƒ`4"謬,|öÙghÞ¼9êÔ©ƒ ;;û•Š`Ì›7£GF«V­«V­ÂСCˆ¨¨(ÄÇÇcÔ¨Qb½„bËw¬ ˜àÂ{{Ó¢ÝæÍ/¾Ï‰‰6nÕ lÖ,ÀÅ¥H—­V ¨_ŸÔòH:û¥K€º–c2þú‹d‰+³lY‘ÌÐ)ê£vê¤ùù•+W6H4_üÜ»7qâb4••¢££ˆÈÈH|õÕWèÑ£•ìØ1TTÊ»ìÖ­îܹƒ7nÀÁÁ]ºtæ3ÅšÐPR Nƒ©ûóü¹"¿ mÖúI'—vwW8@©¯˜Ÿ´h=öð\]ubŠN(rcT$) 8|˜ž3€ß#Å£q€ÒA\VÓG®j> œáÌòE%::šrLºGêk¼j]°ÏÅÅ€ªã¾zˆ` À¼}{@G?¤ÜÝå‚êùé€Îœ!a2&&ÒÚýTu@·n‘]?ÍÎÏÌÌć ªÐÞ½ÀÇŠq­ZÀ矋gCÑh€†‹!èS )¯ñ›7À¾}ôÜüùš)ŠÎ'È  5tvéŽkkÅ8"B}#QŽSÝý0hÖLg¦è„F€2eã„àéSÍÏ7D ?ü5a‚½·æ1DÇô)†Ž”×xõjºYg­ZÀ!âÙ£5Ÿ AÊ:u€J”PÕȨÛ:~œô “ajJª>K SSÕœ6a0CÓݸA;x––À˜1âÙàaƒÁ¨(€_¬}Þ<Kæ÷Ô¡¨¦Ã_¸@ssIÏ/e¾ù¦ÈlÁÐI_0¿û3p éõÆÌbˆŽ¡Ô¨1d¤ºÆ?ÿLz‡Êpr .SI” èÜâ§Ã߸A×Ï9pxöL167'ߥJQ Cª œ8AÏIx#‘š¨¦ IDAT¶X †èHYŸb,HqãâT!ÿ½uÆÎÌ"#|Ò™šê¼Syƒô%ÓÓ½Ò²³~ïæQ£€Úµuj‚N‘5F•ñâiŒª †¤Ú½ÈÈPŒ]\ˆ¦‹!˜Ä)ëSŒ)®ñ/¿©©Šq… Xî̓Â' PåÊDè¡còª ½{7ÝxÞÒRgø‚!kŒªÌÍ›šk( ޶m£ç&NÇFÞ0ˆÁ`è¤$`ófznöl:ãÉ øþ’£ãð— ~ì²»ÀOs?^çP‚`ì: Ë—°0ÅØÆ9R<{êaCt¤ªO1&¤¶Æ›7'H†ƒ0i’xö%(@¶@5źu£ÃFÏŸ“Roß*æJ”0œâ‘…u€ EÄí~ý5`g'Ž-Œ¼aCt¤¨O16¤´Æ©©tq?ðôJ•Çž"¡äùHªTIÛØÚmÛÒsëÖÑãÉ“n¯søÐýûš5F5 PTðÇôœ#Ð 0ˆ!¤¨O16¤´Æ¿ýF2dd”)LŸ.ž=EB)~€²æžóu@Ê”*EÊ UªÕ«+ÆYY¤ahA‚hçNº3J³f@ëÖâÙÃÈæ1 ½‘žNRß•™<Ù€ÃzÒª: efÌÊ•ìÖ‚`Œ: Ü\`ûvzNB¿=<˜Ä©éSŒ©¬ñ®]$D ÃÆ†4N7Xø ªU»Uóæê;;" 74Ú·§Çš8@R×;Ge”. *ž=ŒüaCt¤¤O1V¤°ÆYY¤í…2&peÜÔTàÝ;ùÐßÌ I¥K v;SS GÕù9s s¿tó&IÏ©k€øâçáà TÛVL`Ct¤¤O1V¤°ÆûöÑ¿Ž­¬È—·ÁÂk1ÈÙeöæø: r刀Ü)LcT)k€^¿Ο§ç$ðgÇÈæ1 ÁÉÉV­¢çF&bXƒEú={ÒÄçÍ3܆¢6F•Û·Ëkb ¯í³Ïij‡Q0ÌbˆŽTô)ÆŒØk|ô(ª››?ü ž=:çÅUª„låô¨PhÚ”ü»re" 7d´BKU”M²¿”a»?Ò‡9@ Ñ‘‚>ÅØs9X±‚ž:T/& ùçä I¹º£@ȲÁ, Å m ©j€NŸ¦û™98_}%ž= Í`Ct¤ O1vÄ\ãÓ§'OcSSRÅØàáí êÝeË–ü¶={’:×7M ê£ÆÄä}¼T5@|ñóèÑØÖ¥Â ƒ!(ÞÞôxÐ  ^=qlÑ)| @m0ø´k¬]+HÏU½£®1ª¡é€BC+Wè9ÖøÔ0`CtÄÖ§ÄZã €{÷è9£ØýT5@vv‚k€À‚8‘Æ‚6a0)j€¶m£Ó÷»vêÖφæ0ˆ!:L$|}é9£ÚýÔ:@ƒú÷Ç#€ïݺE2«”Û~ÒÑ}üHzÛ)Ã>Æ ¶Ä`0t ¿ês‹ŠêÅFƒH5€Œ•ÆéƨññÀ³gâÙS‡ÑI€NNÀ—_Šg£p0ˆ!:L$<úZãà`àÄ zÎh2¿”Q#‚f Âcj ´mKÏ© ƒIEĘ™‰c £ð0ˆ!:L$<úZã+èŽØ aZpt4ÉÓ–agØÙ1 PÑD$ н{@PblnN²¿†Ó1D‡i€„Gküò% (3¾ªŽÃàÉ#žÕ*íÛÓcu4@¿ÿNûô*WÇFÑ`;@ C'üü3 ª]øúkñì 5hFÑiÓ†#……ïÞ‰g:’“ƒé9öûÍpaCt˜Hx„^ãøxÀLJž›?ßHuy8@LT4J–š6¥çø»@bk€öïRSãZµHO†abTPZZ.]º„ýû÷ãåË—ÿöí[ܾ}›zëÁR†2L$Á4@Â#äge›7ÓsÓ¦q¨Q’G <Ó77`ãFŘºyøçÅØÊ 3FS:¨>¢:@FFªV­ téÒ%Ïs „ßøn=ƒÁИ£GÈHŸT)#o À4@‚¡®1jz:`m-Ž=Êð¿&ʕdžn0ª˜Ìù+++888 44TD‹šÀ4@Â#äÿò =3†d†%99ÀÛ·ôܧ ¦*:üƨ™™tcT±4@ññÀ±côÛ¸6|ŒÊR& oÞ¼‡‡G¾Çíß¿ŽŽŽpqqÁòåË‘«\ÄÀ‘#G°~ýzê¡ }¬¬O‘‚=Æ8–­±®¯?mÚzÜ»§›˜¬ÇìA¯I7T_hã¥KIÌO6.]°±@4@«W¯––½8¦wÖSa°•+WR }ÙçãCv¢2vq:tÆzéb|åʬ_¿ýû÷ÇöíÛ‹#Ï!ÏŸ?çÊ•+Çíܹ3ßãÂÃù7oÞpÑÑÑÜÅ‹¹êÕ«s+V¬?ïååÅ͘1ƒKHH ʰ1籇GGº6‘‡‡GÇyzrÜš5’°Oçc__Nù'´l)-ûŒ`¼e‹ò'p½z‰o_ݺ {ŽÛ¸Q\{t=þøñ#—À…‡‡sÏŸ?ç¹â€ ÇqœØN˜. AÏž=1gÎL™2E«sW¯^+W®ÀÏϰxñbê¿ CÁ‹@ݺtåçÀ3ñh7¤iþuü¸xÆ …­|ýæÕÂ0Œ"ñèïàÄÆŠ—muå Э›blcü÷`k+Ž=B¦M› i‚Q…ÀnÞ¼‰nݺaÅŠjŸ/^ 66V>懻޼yƒ *n'ƒ¦8ü¡‰k¼aíü´h´{´•J¹}[ç÷“ù ™H7¨kŒ*«N"†ˆ/~2ÄxŸâ†Ñ8@©©©èÚµ+\\\ðüùs,^¼‹/Æf¥üÜN:Q1ÐÖ­[cáÂ…ðññÁìÙ³±gÏÌš5K ó‹5¬ðèzÝ»é¹9SÓ9Ì‘‘@D„Nï) òéÏêé†ü£ê»PL ÀÿÓaâgãÁh 333Ì›7®®®y3kÖ,tíÚU>^²d RSSáçç333=(¹åªÞ¿O·‡7t^¿¦UßU«ò!ÓéŽR¥€&Mè¹›7õ«⋟‡' [Æs€¢Ã4@£«5 îÞUŒML€y®×èI?ÒÍ“ Z`0 nQÓ—("8{–žcSÆs€ †Æðwzõœ©Ùý‘aLa0ÖL¯ˆ¥JM~ú‰t=‘Ñ®IÏgÌbˆÓ .ÖøÕ+Õ”à…ýžçÎå}’1;@J)ðÓé¾tïœ,œ(%XµŠüoå—x`»?Æ s€¢Ã4@£‹5Þ¸‘þUÜ´)Ðú*o÷‡ßÕ˜ ÞÓé–ªUU£^¹¢{ PR°lq|~ü‘TVÆÁ«VѽŒòåÉsááÀ…ůç×­ˆ[8vY•² >Ø`Ct˜HxвÆ'O’82J”&q¿Ò½0œœH—HÀ8 þîOõꤢL¤{øQãâbqäˆv ÈHÀÓ“lØýü3©ÎÀ§bE`íZâøüðPºtÑì6TÖÞ\+¶ zÅhZa0 ¦ž¢¬1?õ}ì°tØìàm Íœ©( ÈïdiŒšð× ckû!dQ/^”ÍT†••f ×¯Õ«I?¯Œ õÇT©|ÿ=0~Ë—/ǸqãD¶Œ¡k˜Hx »ÆëÖÑã/zæ¢â~Þ6ù¤I¤w ++â)cè»@Ô˜H(h(ׯ«×…„¶,õêÑ~–KÀöí@XyÛZY a±áñþã{ø<ôÛ ½#YèÉ“'òtÇíÛ·ÃÛÛwîÜA`` r”‹‘0 ¦žÂ¬ñ›7À‰ôÜÊ6§é¦§ÖÖä'4cÓiОi€„nŒ‹/(=úÓ§À°a@ƒÀž=êÛÏÕ©CІ„ãÆQ-ܶÜÝ‚´ì4ùØ$ÓDDkô‡d kkkddd ==wïÞE»vípØÊ™Ó OaÖ˜_ø°qcà³ ¼Â‡#G*¨žÌw€nßÖúþ’BC «¤{èÆ¨•ôA` ðø1ðÕW@£FÀÁƒtŸZ û÷Ïž‘Ý!s¦zU!-; [þÞB͙Dž’ì«ìׯ&L˜€ßÿ 4@£F‰ˆˆT¬XQló £æÃFPfµÇu`•ÒNŽ©)É+Vßzð€Ä$ õ§7ӉЛpÿ¾b|ø«V­‚…¡~ˆ2ÔÂ4@£íïÚEˆ«Pèùp5}P¿~$¶ ŽÚµåÝôtàÑ#­l ññ¤¶Œ’%IÁL$ P&€xDFªw~š5#e="»CÌùÉŸ\.ënÑB¿Ñ GÅD -©·Gbb"Î;'×øôïß?þø#ªU«ðððÀÔ©SÅ4‘!L$<Ú¬qn®jG‹ÅƒžÀÔï<=ùý÷ù_¨ukzl¨: w˜H8P,Õ:@­ZþIv‰ú÷'ÙŠŒ‚9| añaò±µ¹5¾mô­ˆéI9@ðóÏ?ÃÙÙ ,@XXXÁ'0 ¦mÖø?H*± kk`ôû5ôOîŽUÃ\|ŒE­¡Ä4@ÂQµ*I]—i€d´kœ?ܽ ôî-–u†ËšÀ5Ôxd“‘(W¢œHÖèI9@vvv¸|ù2®_¿333tëÖ :už={ðQ]ùNƒ¡sø©ïSûEÀúÔ!z² ÝÀx "ò3ÀÔ¤À3„§cGúß—.‘š@îîâÙdȼ ÀHÅߤ©‰)f»ÎÑ"ý#)HF5°téR¼zõ ,ÀùóçáììŒ &à¶¡g“0T` áÑtƒ‚€^„až¯éiÆÀ—_|±Ö­éXDh( ‘’Bà ¦–N€nÝ2qæL<®]ºuÛ"ÃfÍMz÷§O½>¨[¶®HÖˆƒ$ ¦¦¦èÑ£>Œàà`4nÜ“&MBÆ ±k×.±Ícè¦Mט¿û3 k"ÊžTÓôT‘…½=-’æ8«04˜H|û-°wo,rsµëÆP%86gžÓæ´Ë#£Óˆ‘´¤Œƒƒ¦M›†`ß¾}HKK+ø$†AÀ4@£ÉGDÇŽÑs«ªo¥ÛfW­ ªùAÄ4@’ÀÔ¨\¹²¼@.£ðü|ëgpPhú\«ºÂÍÉ-Ÿ3ŒÉ:@ÏŸ?džO©(?~Ä?þˆÙ³g#&&Í›7Ç”)SD¶Á0.6o¦«è6m:çyMOgÌЮ–¡;@§QhÃPˆIÁ¾Gû¨¹¹nsE²F\$ëíÞ½±±¤8ÓÖ­[áççsssŒ1Bd˺†i€„§ 5NM¶m£ç6¶Ü (ŸggL˜ Ý ÝŠŽ&5Œd88eʨ=”i€„'33ññê{14cÓMÈÈQtˆ­ãP}ëõÑ"ñ¬ôúõk´oßpüøq,X°«W¯Fdd$RSSE¶Ž¡K˜Hx ZcZŸìX.íoóšž~÷Pº´v7nÒ„äÑˈ‹#-¸ -*@3 ðÄÆÆ"€¯ÒˆÎøzN¦f¥bkÐVjn–ë,˜šHÖɾjGGG#&&>DëOEÕ’““‘¬\••að0 ðä·Æ¹¹ÀúõôÜú.À$4D1aexzjc RžWCÚÒ"ži€„G_ ø´xô>Ø™9™‚ßKŸì¼¿ñiŠ´ò6å1ªé(-É:@ãÆÃ¢E‹P¿~} 4NNN Bff&ëÆ`è__@¹æ¨¥%0è%¯ééˆ@aÿî 9 Æz€Kþxþ·LjS#ËG_ˆ.¿Üþ…š›Úz*J˜—É"ñ‘¬Ô¸qc<}úç΃ÀÌÌ §N‚ «snT0 ðä·Æ¿ÐŸ‰XÒí:,î)ÕÛ21É»é©&rAD- ¦}i€N<=8úïQL??]ðûéƒãO#<1\>.a^“[MÏ YèòåËxôè5j333@³fÍЮ];‘-c覞¼ÖøÁàêUznòÞîOß¾@½z…¿¹ºÎð™Z` I¡ PrF2.½¼$où{ –^[*è=õ¿íŘfcPΦø´½P‡d ””¬\¹•+W†««+,X€Ë—/#]9#ƒa0 ð䵯üÝŸ±mÿE™€sô¤&m/òÃÙptTŒ33‰dhá1 ðèCt6ô,•%^W½ð[Ðo‚ÞWHüÃýq/êž|ljbŠY®³D´HHÖêׯ‡uëÖÁÆÆC† ئ1FÁÿ‡Ós‹Kòšž¶o¸ºýf†¨ÊÉÞ¾UŒMLX°b€,üÅgʹ)8þô¸ž­Ñ koÒ @-ûZ"Y#$ë@ZZ|}}±yóflÙ²ݺuÃÁƒó=þÒ¥KØ¿?^*·³Î‡ÿþûÇŽÃ¥K—Xui‘` áQ·Æ[¶Ð-¾:ÕŠ@Õ뼿¯¢îþÈ0D("‚® Y±"Òσi€„Gh PZv·Wû\.—‹á'‡ãÊ«+‚Ý_þ}ÿ/·ү©8¶½P‡d íÛ·£|ùòðññAÿþý†Ã‡cÀ€yžÓ¶m[üðÃ8wî:wîŒntË»~ý:š5k†Ó§OcéÒ¥èØ±#K±¦þüüþ;}̆ëiÈÅèÕK7¢Ä°ûÃ4@Â#´èBØ|Ìú(Û[ÛÃÜÔ\>ÎÈÉ@¿Ãýp?ê¾`6èšµ7×Rm/:Të€6UÚäsFñA²PÛ¶m1räHãäÉ“¸páB;4‡½{÷pðàA„††âñãÇð÷÷Ïóø `ñâÅ8pà®]»+++8p@×/…QL$<ü5Þ»—Ô$”QÓ!ŸÝæ•‚ž3G³¦§šÐª}­/h¤ˆ–)ðL$ÜÚä7˜|PjzZ¹20l˜înjk Ô¯OÏI}ˆÕ*VdædÂ7Ä—šÐ`†6k{Кw©ïÐc_D}ˆÒ§‰Z³ñÎFª˜cýrõÑ«®ŽvuÉ:@Š={ö`÷îÝ8þ<>~ü¨q  €€¼yójŸ‰‰”/_^>W±bEDEÑoèÛ·oãêÕ«ÔC6.úXYŸ"{Œq,[ã«W¯âÜ9àùsù(e‘îÿnø4úÄŒ€¥¥níiÓÔ³wîHf}ÔŽ_½¢í­Q#ßããââpùòeñì-ãK—.Q ]^ÿò«ËHz®aV,UÙ/ˆ¦k–ë,üàö®8÷Uâ+´ÿ©=’2爽>Êã”Ìl>º™z¾—e/j7Kv|HH®^½Šõë×ãæÍ›ÈÍ5Žâ!YhÑ¢EhÖ¬Ž=І âèÑ£ˆ‰‰Á‘#G <7$$ýû÷Ç–-[P¥JµÇÈÒée5†dÿÎ4”ú$FÓ òóSß7´ÜÓwJ"i[[`âDÝahµÜò÷÷g} &))I0 ?üÕ¯~?ê÷ªî«ðEí/¨c^&¼D‘ž-½ò,ÛïmGj–âýX±TEô¨ÕCD‹¤‡ Ç)ç¼J‡èèh”-[Z‚ž={bΜ9˜2eJžÇ½ÿŽŽŽHHH§ÖoذçÏŸÇ… ‹/¦þË`:“þ¤2L‘‹Î.° ®˜üþ{`õjÝßüáCº/˜½=ÑIµ²{•*¤V€Œ—/YÌHÉárPqmEÄ~Œ•Ï]qŸ×ü\å¸GàÏçRó}êõÁɯOÂÌÄ R ;757ÔÄÛdEï®Þ˜ßa~çFGG£iÓ¦Å";W²;@+VDJJ V­Z…±cÇböìÙ¸téR¾çܼyݺuÊ+Ô:?/^¼@l,yƒ—+WÕªUÃíÛŠ’ÿ÷îÝCóæÍuûB ÁßýYÐèPÎ¥%  A£F€bœä¡ÑŒ @9nf89‰gCP®…_£œ‡èâÜEå833tªu æÿ|þ'&œ™ ¸šräß#”óSÒ¢$&µš$¢EÒD²PDD6lˆ'NÀÞÞááá{öìÁÊ•+qöìYŒ?^/¯¡ 8üÒ›èèhÄćÑó3³xm/†*UÆss E zNªa°×¯é‚NNÄþ|`u€„G¨:@'Ÿ¤Æ}êõ¡Òß•±6·ÆŸßü‰ÆŽ©ù]váÇË?êܶÂÀo{1¶ùXØ[Û‹dt‘¬´víZôîÝÿý7Ö®]‹'N K—.UÛÃÌÌ óæÍƒk>UkgÍš…®]»ÊÇÓ¦MÃòåËqýúuÄÇÇ# 5Ø·Þa á9}ú4¶l!2†T¹û缦§sN‘5”z@ZÖX } D NŸ¢æ4È»ÞØYÛÁo„œíœ©ùU«T:®ë›¿^þ…G1äc33Ìl;SD‹¤Kþ?iD$22ƒ ¢æ\\\P®\9DEE©8*ÖÖÖjufÍRí}òÕW_å›ZÏVHxFþÕªÑs+íÿD*Môî­šª®k ÕÒà‡ÿóŠ¡{„¨tëí-ü—¢Ðz•¶,­‘X¸R©J¸8â"Úïjw©ïäó³ýf£¼My ÿl¸NíÔþîÏà†ƒU5A²;@-Z´À¯¿þ*×ìdggÃÇÇ999¨^½ºÈÖ1†Å¾}Àû÷ŠqÛ2OáüïYú ]µ½È¾ôè ÅǬP±þò¨ë+3+έãPç‡GiËÒò9Æü1&Ï–Bò(æþzù5ÇÚ^äd Y³f¡téÒpttD£FP¶lYÌ;ûö탩©­€fà IDATdÍf¦ŽÖ®¥×xsu^ÓÓví77áqr¢5FYYÒì _ˆi€„G Љgtú{Aá/>Í+5Çé!§aif)ŸËÎÍÆ £ƒp;âv>gê~ÓÓ.Î]ТR‹<ŽfHÖ“°´´„¯¯/îß¿///?~/^¼@Ó¦MÅ6¡c˜HX.^BBk\Ý<ÍŸñZ¾èc÷G†!„Á á1 ðèZt?ê>ÂÃåãæ%ðe/µ¾N×]q`À˜š(¾R?f}„ÇA<}ÿT¦ÈÛä·8üä05ÇÚ^äd M›6ÅàÁƒñùçŸ#** mùÅÔÓ Ëºu Xã͵×Ã$[©éiýú€€ý•Tà;@·õû+Y#ÂÃ鱆 Ö LXt­⇿zÖ% u­A.ƒ°ùKºòr|Z¶0µ@ﺽ‹|]¯N^˜Üj25 Tk ]ò{ÐïHÉTü]W.]Cä^Æ„ä +W®`Ñ¢E3f ~úé'¸¹¹aÉ’%8wîÆŒ#¶y ` a8p¸rE6: +d`tÒú OORýYŸ”*4lHÏIi¨`L$<ºÔñwºÕì;k;\{Ó›ðUCº¼ÊÈ;xd ²r³ò8«pdædbÃúïÚ³'%Êf¨Gru€’’’P±bEù¸N:7n:vì(¢U !a Ý“˜̦vÀ¿ÃêF;`ýD©½C™2€Xkߦ ‰Ïɸs‡Ô!’…t€„¨”Ëå"úC4Þ$½¡¯“^ãUÂ+¬w_®5º|!#A— ¾þ§¨á/eLML±¯ÿ>Ä}ŒÃåW—åó~/ü0úôhì°ŸêÊ^þsP¥ŽÑÄ–436B$çåææâÚµkHLL<~ü‰‰‰8|X¡n2dˆXæ1Áüù@LŒblS‚ÃätŠ,&L ßÅ M`ÇÅØv€ ÇÌ*Îò#"9"߃3!gŠ•¤+^&¼ÄÃè‡ò±™‰úÕï§Ó{XšYâôÓèìÓ÷¢îÉçþsåmÊc½ûú"߃‡ŸoýLÍo1¶V"ý]’s€jÕª…óçÏãüyºˆÔªU«äÿfqMíú1ŠÆÝ»Àï<ó†/öÀ⤞šžj?ìï¿I]")t†/D@4@¶¶¶0ÿÔ3,—ËEÔ‡¨|œø´¢éY|C|ñKOq[/è“ÌÌL|øðEº?üÕ¡z”·)_¤kª£”e)œ~íwµGH\ˆ|~à p,é¨Qwöü¸vOÞ=‘ÍMÍ1£­ˆ×†ä -[¶ˆmCÏœ>}š…ÁtDN‰jåæ*æêײ,£:¨RE¿Æ)Ó°!Ñ}ø@ÆII@p0Рx6É(ÄPàÛ@¬Û¹f5Í“ƒ7Io™©s½Ÿ°ø0<{Žzeë z©‹   "‡Á„ ñ)oS~Ãýà¶Ë U-¸²Ž%1®ù¸B_›ßöbH£!p*ãTèë7$ç1ŠÌùÑ›7«V>89ͦ¿TLè£éiA˜š’l°«WswžŽ!LJ Â$x•±¤#ªÙV“?ü_ùSÍ.φœE=×âáéB™‰;Š« Lu€ÀÙΆ_@ÇÝ‘˜ž(ŸÿÎ÷;”³)W¨ðÛ½¨{ð÷§æXÛ íœ´téRÜ¿?ßcXÖƒ¡Êÿ‹Ñs#FÍþú=éḸèϰ¼hÓFÕ=Z,kqq@ŠR™€Ò¥Šn ÚŠˆäˆBÝÎÊÌ N¶N¨n[rr”ÖæÖ*÷›|V‘fíâ‹Y®ªžê9ùì$8(ÚÀ´©ÚUJ ¿Úر1Î|s=öõ@Zv ‡ËÁ7'¾Á…áЩz'­®Ço{ñyÍÏѤBÙ[œtçÎܺu ß~û-Ê•+'¶9 =À4@ºaÆ ú»ÛÞX?î ÐÙÑä+¬Ï¶ù!Å‚ˆüÝgç|ÿù+o¬$ƒ¬A)oS^îÈT·SurK:j äQǃßxsIIÅBøª Љ§º-~¨ í«µÇ‘ÁG0àÈyÕæôìtô=Ôׯ\ÓØ O DZQs¬í…öHÎ:xð ¶nÝŠíÛ·£wïÞ˜3gªV­*¶Y a ¢ãç£?±jàà=à8œÆ§fmÛ:ˆ`¡øBèþ>~llıÐ:üµáμÿøž ÂRuKáØˆc¨i_N¶N(a^Bç&V³­†ÆŽñÏ;RF ;7~a~*ugŒ‘¢j€Þ|€7t¡ êÂ4é]·7¶÷ÞŽ1(êÚ%e$Á}¿;¿ DMûš^ã—Û¿ ‡Ë‘›Th‚Ïk~.ˆ½ÆŒä !ÚÚÚbÞ¼yxúô)\\\гgOŒ;/^¼Û4†@0ç§h¤§S¦ÐsmÛã+ùÊû`ÈWxáB½Ú–/•*‘îð2²³Âß‚£…”ž@‡!\€YÝfÁ½¶;ê–­+ˆó#£WÝ^ÔØ7ÄW°{I‰¢j€NŸ¦‡¦›jäpèšÑMGcu÷ÕÔ\ô‡hôØ×1©1yœEHHOÀÎû;©9¦ý)’s€dXYY¡oß¾pwwÇþýûáë[<þÀ mY±Pþ}`fü¾9 &sx½€zô ú)!µÎðZ¤À¯ \C ZJ8è­ÿß:v¹\nG3dˆþâó½Û÷*ï— /à¾ßÉÉyž·õï­TK §2NÒˆ•†) ’t€Þ¾}‹éÓ§ÃÅÅ™™™ §§§Øf1‚õ+<ÏŸ«é’˜>øìÚ& DQw$ÚÌLÖ^ZHÍÒpè]ê;l¼³‘š›Úh*lÌô¾k[µ-Ê–Pˆ³c?ÆâN¤4TS”^`‰é‰¸òê 5§ïðŸ5=Ö`d“‘ÔÜÃè‡è{¸/2r2TŽÏÈÉPyßÍh;榒S³’s€¼½½ÑªU+”)SÁÁÁØ´iªW¯.¶Y aY}…gòd 3S1®ZXæ ,]Jwº}{Õþ[RÀ@ 7VP¿Â+–ªˆº)uõÖ ÌÔÄ_Ôù‚š+a°¢ô;r†ªËT¿\}¸”7Ò&ØÙg§Š°ýjøU =1TeWoߣ}TˆÌÖÊã[Œ×‹­Æˆä 7n &&ÞÞÞptt„‰‰‰Êƒa\0 Pá ›6lJ®ZD ʰ·Çw'è­ÉТ`®ôëõÍ **ïã…„ã€×¯é95Y`o“ßⷠߨ¹`Ø×ÃP¶€”y]Ru@EÑI)ü¥Œ¹©9Ž>ŠvNí¨ù“ÏNbÒÙIò±º¶[NDiËÒz±Ó‘œtáÂp—ïƒÁ(î¨6;¾üPç`ûvú /¯kÙˆ† Ш='Ö.PTQ”Ë([–Ôâ±ôÚR*Ç<ÆÛä·z·ÃHÍJ…ß ?jNìð—266ðꋆåé]Úm÷¶a‘?)îåâ‹àØ`ùs–f–ðlä!EAr£øÁ4@ÚÃovZ¢©3H? õëS¦H{¥Ó üŸ‡>ÔœWg/XšY"..ÙÙÙHcgm7'7jÎØw «:zéÙ çÖÙÎÍ+5×¥iEÆÞÚ~#üPͶ5¿üúrlº»I¥íÅÐÆCQ¹te}šht0ˆ!:L¤êš.\ÔxtZ5&¶n`n.í5–JAD  ¯«^òvP·l]¹ˆÕßß_o ü0ØÙ³z½¿¾)¬Hªá/>UJWÁÅQΆ.ìyÞ7ÞÜM`ÂRßus€¢Ã4@š£®ÙiƒÀ\ÏL`ïÑÝø‚e%½Æü   úê‹Ràÿy÷?9LÍ-í²f&f€AƒéU¨:@W^]‘·Y0F £JÏNÇÙPÚ1”Rø‹O½²õpnØ9”²,%ŸSnÝîµÝUÂe íaƒa@lÙ¢ÚìtëVÀbËzº¹¹4ÓÞÕQ¿>`«ÔÆ!%xúTÿv°´èÊ"*+§I…&¢W_®_®>jÙ×’Ó²Ópùåe-’½ü 2?ÈÇ•KW†«“«ˆL«Ê­pòë“°0µPû›œ8\¿Nü)íÐküïûÑ~W{¼J$a¤ûQ÷ÑÙ§s­å8:ÒU—srH6˜¾È#ì\è9Ü|{“zjy×åj/!–(>U¡µÑår¹8L‡~ )üÅgË—[0±ÅD±Í0*˜ÄÉëSD$$D}³Ó&õÒ¹¼L gOµ×rƒþ B§ÝõnañäÝtÜÝÉš]HÌ0˜4 ¯,¤¦{×í¶Uyu‹>!–¾¬ó%%–½yï?¾Å!ÑFð&€rÀËX•A÷šÝ…2MpLMLamn-¶Fs€¢Ã4@y£®Ùé’% ;=Ê»ÀÏ?óO—#Ô_{} ]÷tE\ZœÚçCâBÐqwGùÎP¾ˆYQtüéq<ˆVÔ0Iž»?€x €4cmY¹¥|œËåâ\è9Qlm4@'Ÿ¤Æ½êö‚¥™¥f1 æ1åàAà2¯¤Ë† @©”(`åJú‰)S€zõôgH¶‘û~w¤d¦PóuêPãW‰¯ÐqwG„Ä…äA íå:WÇOþ?Qs_7úŸUøL6iIqJ‡×¾dÈá/†0­4cÆ Ì˜1£Àã-ZkkkêQØnÃŒÂÁ4@ª$&³fÑsÀ€ À>( »¡\9Òð4t½ÆGþ=‚þ‡ûSý•à§N?áÙÔgþÙpj>"9wwÄ“wOò¾h³fd'KFd$yèžt17”jÿ üö=·hѧ$©3N©/P£FÀ„ ^SWk¼ææLôHµ„051ÅŽ>;0Ëu5·­÷6Lk=:?.-]÷tÅ­ˆ[êo F,=PÚ!Ë53Å-(„Û–f–ðê”ÿ ® %cƒiª:ñŒNgá/†:ŒÎ* W®\ÁW_}…É“'ã:/­8)) ‰‰‰ÔC6fc]ŽãâyÍNÑ Á§>§‡#10>Ù2ÀLQDHû^YˆïÏ|O=o‘iCal³±*Ç›ÀK]—â·'¤II豯®†_U½_ãÆô˜÷k_×÷úµÜ©Lak‚Ù'c:0±ÅDT³­&Üýu8î\±35þó]Ylûô1~óañad"8°²ð ì“â8-- ‰‰‰xýú5’““ÁqtóUc¥Ø;@={öÄ¢E‹0pà@ØÛÛã‹/¾ÀñãÇåÏ_¸p>>>ÔC6.úXYŸ"{ÄïƒûT„ȇ4;ÍN~øÔÑ}úÀ‡W¿&¯ëËÖ¸0öqà0íü4xßð*ž+a^cLÆP AÕ¿ªû*,L|:ÿCæ|yàKÌ[;>þÍzD(dý•Â_>Âl÷³üÇ :. ÏãzqqqØÅ«Ê­ï÷OäõHX™YÉÇ/ü_Pâs±ßßEïܹ“Ò©;ž =º×ìŽ2Ve$a¿TÇ·nÝ‚f̘#GŽ G¹(¨1Ã)žžžœ§§§ÖçÍŸ?ŸëÛ·/ÇqçååÅyyyéØ2Ÿ­[·Šm‚$ˆŒä¸2e8ŽlGǨQŸž\²„~ÂÒ’ãBB4¾va×8;7›yj$‡Å eV–á®…_ÓêZÿ üŸÊu,—Yr§ƒO+ÊÍå8{{úµ>|X(Û5æ×_©ûíh®°ï‡¿~Ðø2ÇŽãbcc4T3zîëI­ñº[ëÄ6IgDFFrüñG¾Ç4úµõúwÞß©'댃¨¨(®B… b›¡Šý ˜š²eÑ'LDà7;up֬ɄâWCœ6 ¨C§›çGaÖ8#'ƒŽÂÞG{©ùr6åpeÔt¬ÞQ«ëÍm7›¾ØDìËÌÉÄ £ƒpäß#dÂÄhÝš>QhOýꓞÜÖÊß»}¯æõˆ­’aÌU¡ Ò…Ä…P™†f&fè[¿¯>Lc F÷Mÿáć‡#99ÉÉÉGjjªüùæÍ›cµÒ—ÉÔ©SqéÒ%„‡‡ãÌ™3Ø´i† &†éŒbLžÍN˘7øøQñDùòD- ©Y©èu°—J+*¥«àú˜ëhQ©E¡®;µõTlﳦ&ŠžìÜl =1>}È„¾ "ò O}dg·› ‡ÂÞ[øBè¯o 9#9£ ¾ø¹“s'ÍûÑ1ŠFç`ôèÑxùò%^¾|‰Ñ£GãŽÒh­ZµP®\9ùØÖÖsçÎ…««+–-[†õë×cà@–1 OŠ{ ¼šŽòå€NÉÆ²e€­­V÷ÐfÓñùÞÏU: ×´¯‰ßÞ@ƒr ´º7Ÿ±ÍÆboÿ½TSÇ\.ßþñ-¶mÕ&Ï ·#»\3ÛÎÔê2b×’Qî\Ê»ÈÇY¹Yð{á'¢Eº£ :@¬ø!CŒÎrwwÇÕ«W©G×®ŠÎÍÇŽÃØ±cåcoooäÀaòÙÉøÕ„×õÙ3 %B‘ûò5~eüØþG”²,¥ÕuÄ®¤Œ±†Áò«ô:é5‚þS¼wL`‚þ úëË4†btÃð(Πàÿ£ç<=‰ŸƒTw?Ö¯§ÒÞ5E“5~“ôvwÀ£˜GÔ|«Ê­pmÌ5ªÈž.Ø` N 9¥ÒàqÊŸ_E)ô”›KŠ# AJ L©Á鿀Y¥*˜Üj²Ö—’ŠPu€Î‡ž§j7*ùi€ø»?®N®:Ï2Œ æ1"2y2‘¡;9‹ƒh~æÑ)âèßèÒE;BâBÐ~W{•~];ãò¨Ë‚é(<êxàÌ7gTªôž/Ë sûï1]W)ÜXÐi¡ÁwÝnçÔöÖöòñûïq7ò®ˆ  1´…9@ Ñ)® <›–Q@+÷Á²´ü”V8ò[ãG1Ðawªw@œ“óÃΣ´eéBßWº×ìŽ Ã/P÷¹S…w@Ðñ³ôšÆ8Ú`ló±y?RÑ$û‰ßûÊÂ`yi€¢?DãæÛ›ÔÜ€ôeÃ@aCtŠ£()IµÙi¯^d“oÞk×ÒOΘÔªUèû嵯7ßÞDgŸÎx—úŽšÒhˆÚð”Pt¨Ö—F^’ïZÜ©Ê;@(86¯\¥æ*6v¥tIÚ % `œ: ¼4@§‚OQ!¾æ•šÃÙÎY–1 æ1D§8j€æÏ§›ÚØ›6}üð–¦x²BÒµ¨[ã¿^þ…ûz 1.?¡Åp ÐŽ@ai]¥5®Œº‚r6åð°"a®ôdt4r^¿ÊóÜÂð“ÿO¨ž@ëbê¶èYèëII Ê™vb!"9"Ÿ3¤O^ þbæ1zæúõ|š‡ÓO._”)£SNŸB¯ƒ½š•JÍÏm7¿÷úªÓ£OšVlŠk£¯¡¬m%<¬H?·aƒjúÂò0ú!Ž?=Ž´ï“ºÉr“%àêäJÍÃ.Ÿ¸´8\ ¿JͱðC˜Äâ¤úøøöÛÿ³wÞáQm¾7ÙTÒ©!„Þ{G¥‹HP”ªE^Ô`ED|”(觯J€ "¨HD té„ JiÞÏ÷Ç!›œ-©»Ùìfîëʵ;sfΙœlv;ó›ç)œìZ¶„wßEÞî>eвCûör‡rRø¯9³†‘¿Ž$+7KÑfN¿9üïñÿiw­pZÖlɾ—öq±Ò{$=°õÃÈÌÍ4гäÌÜ3 -D9Peò壽 ¶-|›™Fbôy€6_ÙLN^Á}oY³%Ík4¯è¡ ,!€f§*y€>ø@óG¥’gƒìì€Õ«á„V œo¾#¤fɿNjŽ-b¦ äJÉU¨Xôä"f®>xé€ey$ºv•oÚClÂÂÙ>äžh¤ Xx5á*½VõâZâ5í3ðËù_8wï\ÁyT6ôÌ­«ld¥ °Î¨ÐÚË_Ï6*­]„˜kõ:$çö*ÌäÉЧÏÃÂÊ•pú´²¶½Ï?2ŸC>VÔUϭήvÑ´zÓr»Âñð€¦…Æ,I8ž:Ëæç73´ÙPEÓÈ‘ôZÕ‹Ëq—õ9y9Ì ™¥¨Óf .·µ¼CVê.ÇJ\ IDAT]#ôÑÛG‰K‹3ÓhÊN¾(+7KGĉå/AiH`v¬ÑTìÒWRÌT.É0j<úh¹®ûýÉï™ú·2ɘ‡£.–NkŒ£G±·µg㨌n5ZqèNòz÷æìݳšºàÐ` ¦áÔ6j‚zÏ‚ÈHåy­ÔP×µ.½;jÊyR…ÿeÆ•|ÐîˆÝ<È,¸×5kò¨oùþwU!€fÇ=@}aae• V¬(díùì3e20GGø_ù¢0¯;·ŽÉÛ&+ê\ì]Ø>v;s¦Ï1ÐËèÞ]Y~˜Um£fÝðu¼ØN¹dx/õ}W÷åøãdæfòé¾OÇÿÓá?4Jw‚ÌB¥kÖ|¸.Yv*«(kØŸïÒ^þz¦ù3мgAIH 02ÿþ«»«=0úöEx8u*|ýµ²Á»ï‚Ÿ_™¯¹éò&&lš Èˆí¨vdËó[è^¯{=-=3@ùتlYõÌ*;+EtBzÖ `òÖÉÜLº©©wT;òQï B+±ªûòÑöí¼¶Óh¹Õ*’\)—?¯ü©¨Ë_‚² ÀìX“(#^zI¹ôåïÿprgË9ñ×üù[Š‚ºuåed経Þ8Z‘ÉÎÆŽßGÿ®Ù1eÑ÷¸m[pr*(ÇÆ*Œ K–ðN÷wÝ’2“XºJQ7¹ód|\}L"€*³ ‹OjW«­)?È|ÀÁ¨ƒEô¨|dee±íÌ6…ÉÃу~ ú™qTKE Ù±&ÐGÁ•+e• Ö~y—‡ÃÓOÃÍ›º¾ü²ÌË/¢ðìúg‰MmU¶ü<âgžlü¤¦Î¢ï±Z ;*ë Íå3ï‰y|ø˜áøI.ö.|ðØC¡iT™=@ ÅÁM+ê,m,..Že,SÔ=Õô)ìlìÌ4"%#ÀìX‹èȘ7¯ lC??ò-¼Ü~ÿ]·Cƒ°};ŒW¦ë¿sœ€uŠX(*T¬ºR'"®Åßã"–Á óY¿Ï˜ÓO¿ßiJ÷)Ôt~HñÆ åÁrÆ‚ÊïhjÙ> ïºÞ„º‡*êÄò— ¬$í¥¯vœá”CF|SÞñUµ¦O‡óçaР2]ïܽsz‚~ð]åÍíUJ(€f<6ƒ¯*=VžŽžLë9­ ¢ z€6ˆ½­½¦¦Ø!WÙ9rëw’ïhÊÕìªéÄ„JŠ@³cÑþ”‡Ìš—/ƒ3i|Å{œ 3í2é6ìÖ Nž”—½œËt­°ø0_ó8 é Šú¯ÿŠÉ'ëícñ÷X[:Ù† ¼S{LeqÀbM`¼éLÇÝÁ½ Aô¸Ú»Ò˯—¢Î’f~=û+¤”7Œ£ÚÑ|X4B ÌŽEûS'#¾þóhÅ4æ¢FëƒÐÍ ¾û–M½e$òA$Ö ànê]Eýǽ?VÎphaé÷??¨]`à%3ÎÏjrçɬºWÞêöVÁœ¸U(«ªM¹vàåSÙ=@ùXrrÔßOü…2žˆå/AyH`v,ÙŸ’™ ïá§ÜÑl#nè61.]’3 –#ÊstJ4ýW÷Wlëy¶ã“>ŸÙגﱆR,ƒåóRû—8ùêIœí ͶEEéî³·×í\J,Áºñ€öGî')3É@ëÊéèSDIQÐL.;Ø:蘺‚Ò @PV$‰¿ž^Ê–ðæŒâWÝã¾¾òÖ÷ äÙr—Ç€5tr]½ÚéU¿‹Õ¢ñÈ‘u+¼õ¨²þŸ|{5¦Yõfšrv^6;¯í4ãˆJÆï—” 6ˆ«½«™F#°„˜‹ô§œ?OJ»GxvçdÜÑZö°µ•ƒ^¼C†èï_ d>à‰µOp1ö¢¢~\Ûq,X\¢sXä=Ö¦ 3@zÑÞf$d  |,-9ª„Äú ë!H, Ê‹@³cQþ”ôt9haÇŽ¸œûWçpn‡Îpü¸l *gj€ÔìT¯Ì©èSŠúa-†üL06ª’ý [Ô=6DçÎÊ%ÄðpHH0ÜÞÚ3@FØ–ãÝíð…ÿ¥ˆ"^ÙØ¶•« W! ˆ’—¿žjú”¹‡%°p„˜‹ñ§ìÜ ­[Ã_èì@JÆ•+“¿ÁöÄQèÐÁ(—ËÌÍdèÏC9|ó°¢~PãAü<üçRå>²˜{\nnТ…²î˜žvÅa¢%0Kñ<æû˜bW\lZ,Çï7㈊fîá¹òW ™<ûéåäeÖ1 ,!€‚â¸wÆŒ'ž€ë×uÿÉP挹H³Åo—Ëä\˜ì¼lFþ:’Ý»õ½ýzóûèß±\ªÆX«â É>ÑX?§².ƒ¿sœý‘û5e*¦ö˜jÆ ¬!€f§ÒúS$ –/‡æÍáçŸuߢÏòoÖßÄŒ%õŒvÙ<)ñ¿gKØE}WŸ®l³'µ“ž†©´÷¸´TbdI °fö ÷~œ–5[šo@«A Ù©”þ”‹¡W/xåHLTÊÆ…¼EK.²‰gøáyuÆHHLÚ¾¾ò®=#`I €Î5èæ£”ÛÂ+WPÄùGæ“+ÄlêÔ´3&Í0ãˆÖ„Õ  uëÖñí·ßÛ.;;›©S§ÒªU+}ôQ~ýUO<AÕ`÷nhÓæÌ¬,å±jÕ˜[g.¥ã§ õëË›½ŒÅ‡»?dѱEŠ:v½°‹ZÕjïB–Œ­-t꤬+Í2˜‰¶À[*•y,1#‘•§W*êÞíù®™F#°F¬N8q‚ñãÇóÊ+¯píÚµbÛ¿ÿþûœ?žM›6Ä«¯¾ÊѲÆ” ³ûSbcá…`Àý³ |=ñïżK.³Ë—oéëóŸóÿOQW×µ.»_ØM=·òû‹Ì~‰v@ÄÒü¿šh ÝÊ—(sEýç?ò¦0c°ðèBfìQNë×t®É®vÑг¡Q®a5 (ŸÚ„;À,Ͳ·¬¾[}M9-;={Ì8"™¬Ü,]¨¨›Ò} ÷îsPÏÒ´@P¬NÕ©S‡>}úP¯^ñßšãããIHH U«Všº6mÚpåÊSQ …Yü)W®@Ÿ>0q¢ì#)Œ Lž —.‘õô&LógæS¯Ì›gœa¬<½’);¦(ê<=Ø9~'-j´0ЫôXtPh¨œ”­$˜PYš(í ˆ•aì§s?­)»;¸3©ã$êÖ­ËÓO?mÆ‘ ¬ «@¥!66gç‚D‰®®®šz€àà`‚‚‚?…e +ôA»v°oÊ£T«:‹ƒ»;?ĹsŠŠ¥¯òŒgý…õLš: ‰‚™Jûöl»öuÚ—ûüV[öñÑìÜ Ù¯Z²þ‡•A%ƒv+ã,UŠß¯‚ËŠe°½J#´¹Æ÷õ¿_kÆðjçWqµw­÷ËË[·n%((ˆ>}ú0wî\²µ½Z+*©¸u" eÊù[õ7ß|c°Mdd$þþþ¤§§ãèèÀòåËY³f м8´_4ãC:uL¡½{!0PžýÑÆÉ fÍ‚wßµ€Ó§¡kWåìÏĉ°bEù†q5á*[¶0ýŸéääœÜQíÈ_cÿ¢¯ßò]@v+ŠáÃá÷BÉ1,€·Þ*¾Ÿ——2¬AL Ô®m¸})ˆÇÝÝõÃ×¥ž“Nõ/«“ž“®©;x†¶µÛše<;®îàÉuOjÊv6vDL‰ÀÇÕ‡¬¬,RRRðòQ MELL íÛ··.ß ªô P­Zµ°µµåæÍ›šº¨¨(|ŒDP2LêO¹–-ƒ= o_ýâç‰'àÂxÿ}øÉÎÆhK_Q¢ æÅM/â;ß—&‹š0õï© ñcgcÇo£~3‰ø+óAÙ|@(ų³ÑÄX¦ÀIíD¿ýuæÜ¯|<×ú9|\å÷丸8á*'€^ýuÍVw'''Xñð+}ZZ7ndĈæb•Ãèþ”Ü\ؾF–Ìpäˆn»Úµá§Ÿ`Ç/ÈgŸÁÙ³ÊæßîîKtJ4?û‰I›'Ñha#ü¾ñã¥?_bÍ™5ÜLº©ÓÞVeËOÃbp“Á¥ù-K…Uy€ lH{ ¼w€åz€ òl‡ ÕIÿ2­ç4ÍsáËš«-7ndÚ´iš­’›6mbÑ¢E<õ”œ9øÏ?ÿÄÓÓ“Q£FòY@@ëׯ'99™áÇóì³Ïšmü‚rpá¬^ k×Bt´áv*Lš_~ žºÁCCáóÏ•u/½O>©ÓIî½±—ˆöDìáJ|ÉMô*T¬º’-…è.;Ë1rÉ»v ââ F Ã}L¸ÞÒ h…&}ŽÜ:B\Z5œ‹¸Ÿ&@ãýyÈã 7ÛRœÀú±:4bĈ"gpnÝR†zoР/^äæÍ›xxxàêZ¶T‚²S.J|¼œ§+8Nž,º­ <þ¸éù‘Gô6ÉΖÅNa ÌŸ_PNÌHdä~öDì!$"„ó÷Î+ŒÌÅá¨v¤G½ôkÐ н^÷â;•«óU«­Z)§éŽ…€Ã}LœÕR=@õÝêÓ®v;ÎÜ=Èùè¶_ÝÎø¶ã+l ·’n±þ¼2LáÙ@x€FÅòþSMDýúõ‹o$0 ›6m*ÝMv¶¼Ä Û¶éFmÖ¦ysxñE?¾Ø¼OŸ®ÙP¤aÁÒdÝ;@È1y†'4&”<)¯Äõ·µ§«OWú5èG_ÿ¾ô¨ß[‡÷7¥¾Ç–@·n•J…„„зo_‹] h @ /ƒU¤ZptÙyß<ÚÖnËÀFÊ<3qqqœ8qB,ƒ Œ‚@³SâæÐPy‰kÝ:9zsQxxÀsÏÉNfm¿ˆΜ‘½?Ø¥AýÃÐ`5»†ðÜéäœ,y„_µšNÞdÁÓ /Ôg;çâ;š«? G„^¾¼ \œÈÄÈÒ½ƒCšáók¿_ý›œ¼Ô6¦ÿ˜HÊLâû“ß+êÞí¡›öBx€ÆD AåæÞ=Yð뺒µ±µ•3“N˜C‡‚CÉfY2s39y„±_„=nÔ; ¶ò¬R,@1“=6*Ú×i¯™áyÌï±2gm”}™á%IöxéÃÄÈÒéæÓÎ5ˆK‹àAæF¤“_{ù©å$e&iÊu]ëò|›çM~]AÕF ÙÑñ§deÁ–-òlÏöíʽèúhÙR=ãÆÉ»¾Š!'/‡ãwŽk<<‡o–c 4/ÙxU¨h]«µf†§·_o<=JÖÙLXä&®®œ,—ï߇°0hÖL{'BµdÈB~p“Á¬9S hkØV“  œ¼Y ¨{»ÛÛØÙØé´ 1±ÌÿTU¡ñ§?.‹žŸ†âzy,quéRì5¢S¢ùåü/ì¼¶“ƒQIKBóÍ53<}üûTøî˜òb• ùo¿§PGõ  ØXHM-({xÈ?FÄÒ=@MtÐÜs‹èQ~~½ð«"<„‹½ ¯tzEo[á!€æåΓ’ä=/ÝV­–ƒN˜O? ööE6ÏÌÍdó•͇ó÷Õ¿É•rK<¬îÐHžáéëß—:.–={buâ'ŸnÝtÐ /è¶«€å/K÷<Ñø Ô6jMÎ+ñW¸šp•Æ^MvMíÀ‡“:N28£*<@c"À<:ß|üQËÅ­[,q• rïñ;Ç  æçs?“˜‘Xl{îûÁ¾Ñ—U³ú2áY±+Ð"(i@D¨D¸;¸ó˜ïc„ÜÑÔm ÛÊ”îSŠèUvöDìátÌiMYm£6Ùµm„TÙÙð믲ð9qBSè̯T¯cÆÈ§cÇbO“ÃÚ³k  æBì…bÛ×u­K¾ü½¬/9á}!±! OL°Â8˜Vé]tö,ddÀÃÜ~*`ÈÒ=@ù4 P máÛL&J´gF´Ÿ»ŸÁöÂ$0&U.†À ÄÅÉûËýüäYœBâ@“¥J­–—¶~û îÜ… ‹?Y¹Yl¼¸‘!? ¡Þ¼z¼÷Ï{EŠŸöuÚ3ÿ‰ù\~ã2‘oÝ&fñZrŽýG#~¼½emfX].°|êÔ_ß‚rv6œ:¥Û®¥æÓF;-ƾûHÎJ6úu.Ä^`ÇÕŠ:íÀ‡Úˆ\`cbÙ_U•›óçeE±nü­ÜÍšÁ«¯ÂرP«V±§=}’U§WñóùŸIH/Ú,]Ó¹&cÛŽeBû ´«ÝNSÿù纣—-Ó›Ã*°Zȳ@QQå£G¡gOeá*1ͪ7£±Wc®&\ ;/›×v2¼Åp£^gÞ¿óÔûø÷¡“w§"ûÀ˜$0.’$Ggþæؽ»è¶”)0háØ-¹›zW³ÄuþÞù"ÛªmÔ4 `Bû 4 ÐÙN{á|ò‰²Ïøñð0]œÀÒèÞ6l((ëó™x ¼µ1¤é¾9R0º5l«QPþ’uaŠ›ýŒ@ã’"+\¸Âà ·sv–ÕÆÛoËq\0ìOÉÊÍbKØ‚CƒÙqu‡fgŠ!ÚÖnË„öÛf,µªéŸIÊÍ•mE…³gx{Âz›[ Vë‚âÐyy©¬3 ÚZ<@ o‡/,€þ ÿ EQ))‹Ž-"+·àŸ°E n2¸Ø~Â$0&Â$(7nÀ´iP¯¼ù¦añS¿>|ñܼ K—jÄèúSNÇœæíoã3χ¿Ž`kØVƒâÇÓѓ׺¼Æ±—q&ð ïtÇ øøßÿt,H,]j½K_ùX­dŸXaÑqãܽ[P¾sG©xk×–…¸‘±@oÿÞ¸9¸iÊ÷Rïqìö1£œ;5;•%Ç—(ê¦ö˜Z"q%<@cbù_UæáàAy™kÓ¦¢·±÷è!/s ¦ü*D`` ÷Rï±öìZVŸYÍÙ»E§¼°UÙ2°Ñ@&´ŸÀÐæCKœXôâEÝ¥¯qãdßµµcÕ ''hÛVi~>v¬`M³‚¶À[‹ÀÎÆŽ²ñâFMݶ°mtó)Y^½¢Xyz¥"Cšáèí‚Ù´­a[ &*-ŽQFj• S{L-Õ9„H`LÄ  €ØX9Ãú A†Å«+¼õ„…ÁæÍ%?«BWÑjq+]ñt©Û…CqéõK¼ÿÈûå? òìÎ[o銟V­`×®ª³ôUeP©týfùË`ÂTfš(Ê»#v“‘c8¨iQhÏþ<Óüy6*óØ‚ò"@æçŸåoËë×ë?Þ°!ÌŸ·nÉÎáF%{ãŠzÅ µƒ˜øçDîgÜWsT;òå€/ùcÈô¬ßÓÀJÇþýЮ¬Í´yåye¤U+£\Ê¢ˆ‰‰1÷LO÷îÊòÑ£rjŒ[· êll”©3ŒH||<99Eù-u:(Œ-W”_~Êø0++‹„„¢£¿ %E ªÎ;òtɘ1rÎ.múô‘·º‡‡ËË]nnºmô !±ôÄRZ/nÍß×þÖ9Þ³~OBC™þÈt¶ü©;+TZrså-îýú)?ïÜÝåÍkË–É;¦«"Vïý£¢ä@ˆùøøÈ»M€µy€òÑžÚ¶µÔçÐN{ѳ~OzÔëQêóÀ˜T•Y±BžõÑ7]Òµ«¼Õ=$DH6%©\O¼NÿÕý™¼m²NEg;g¾ô ^:@³êÍ€òûSnÝ’…OPnH¢îÝ!4TÞ•_•±zȯÙœ?/ç=)Œ —¿FŒAõê%7ôZ MÊ'€bÓbY}fµ¢®¬i/„H`L„ªŠÜ¸?“&ö7V''øê+8|¸ÈLìúÈ“òXptm–´Ñ;MÞÇ¿ç&Ÿãínoc£2ÎKoófyÉkÿ~e½Jÿý/8`²¸w‚ÊFÍšòRm>99°q£²ðÿ”š à¨vÔ”£DqîÞ¹÷ÿîØw ßPc¯Æ mV…¶` *-BU%$IÞÕºµìÖ¦W/yßø´i`k[ªS‡Å‡ÑkU/¦ì˜BZvšâ˜«½+K–°çÅ=4ôl¨Ó·,þ”ÌLÙäJl, ,oHÓÖqmÚȳB“&•érU’*ãÃ> /Y³ Gýx9也q_ïÆ€¯­(i:„æ5š—{ Â$0&BYùùæÎÕèã[¶ÀºuPŠ7çÔTÈÈÉàý]ïÓ㇜¿§+ªÆ´ÃÅ×/2¢¥q“@îÞ mÛÊö$m&O†cÇ E £^R`M˜IY;¶*[žlü¤¢Nß2XÄý~¿ô»¢®¬S"%“’¯¿.§«×=>i’ìõ2D÷ØCòòämå?ÿ,›ˆ u’K‹Ã¸LkÏÿý\I)ªj:Öeý3›Y7lÕÊÿ7ߟ’“~‚¶eÅÓ~û /GG='I•ñ4n¬_ì›4¹Ü IDATXY»Jæšÿï|Å{F—º]èå×Ë(× 1aâ,•;åìž‘‘ºÇ4 2ýû+ª³³áâE8u NŸ–Ïœ‘u”†—¡çèô=¹ªL*oaׯÆÞx>ÿœt›jœ9R tNŸ–­A™™zÎi› Íÿ€.KÀ¯þë>ð…-ßÃÕ'4U‰‰òÏéÓú»xz‚ŸŸaäî.·«Q#öíå_Mû×ùà9Ç—ˆè\>ª”øy¬‚ЈÆ] ®Œx:zÒ³~OöGäžÙ¶•·»½ À’ãKHÍ.ˆSáïáÏð–Ãv}á«úXùþûïùâ‹/øàƒˆŒŒ¤gÏžœ8qÉ ¶oßÎ_ýE¿~ý4užžž4Ú2°i“°0:ZçPZýfl~f%Ûzrª«¼¬¥mÒÁ#:}V€Ë]ým$œ|þùdº–j¸ùI;h¡æòòf´K—ty{ÃÚµr’S ÔèóY(.;›ù·n±þÞ=r$9¿º”ÿ˜ÿ#ä]×®ÓW¦m4í$‰\ÿ ð—àad÷)é*¦ìÝ‹JE^^7èõÜCÊ㎭c£RašGU¡çšº¶Q•á÷,k`«R¡V©¶ [—ÿ¼}tŽkõ)\_¸®0Ráç’dø˜çÚýòŸ%''“SE(Z•š;w. .dÈCÏKxx8«V­â“O>1اgÏž|óÍ75IJ o¾)oa×"W¥æ+iŸÜœEÆ¢˜cTyÐä/輚l—ËðskÈ{MWàÕ¬7zÊ9Tó¢¢tc+––û÷óg}b€:šú'Ÿ„Õ«å]ûãC:uŠoh-tí ]ºÈñœåõX¿ âããqwwGm‚éʘ¬,¾¾y“%wîZì7S£"_ü&O’@e«h–%A–1Ç›“#ïxu-Ý—1Aép+eJ$KÅjPRRáááôìÙSS×£GvîÜYl¿‹/R£F jU² ‘‘»à'Z.{ç´8ãghÇDi%§(AÖv—è¸Û.ËÉuÕã*D¿ý˜Üy2C›ÅÎÆNoI‚»w•¢¨ðOddiÒ& ;;9éÔ©"¢³±±TP®$‘ž—GF^^Ácn®¦\Ô±ŒõëIÏË£†ãkׯÇÄc5…èVf&ÿ‹Šbyt4y†¿¬T<§· ½Ï eÅjvåïrq+ä¼õòò*r÷‹Z­æèÑ£ > @bb¢¢Mpp0AAAŠŸÂ«œ¿ë‘G‚0Z{Þæ¬ÿÓl?V!~‚€,ìù˜OéÂqN±Y뷒ϧRÉ›aüÛO Í§£P¿ç ýf’{RKüì•<=y§û;¼‘ö»_ØÍˆ–#°³±38^• êÔ;‚xî99ÛÆÒ¥Ð½{—/CZš¼R7iR¿ü_|иqÍ›ƒ“&PH£Fpø0$')Ä©îwU+ç‹cŸÿÃY³ˆÊÈàLJ {ïßç¹éÓ Ž‰aÉ;Ì»y“þï¼ÃG¼wío„‡ÓñÍ7{éÃΟgðÙ³4 ¤Ç©S´?q‚æÇŽá1iµÆíÀìöíCýÒK¸8@ÍC‡ðý÷_šNžL»'èvê}BCyrêTž=ž1/2ñòe^Ÿ1ƒw¯]cfDs"#ùzöl>¸~¿#GzþÝìÈè֣˕¤°,žAÓ{¼ƒ“ Ƙ••EJJ ^^^Å7”‰˜˜6è‹+g…Xrww§sçÎlÛ¶Is$ìܹ“¡C‡jÚ¬]»–f͚ѥK"""hðpgˆ$IìÝ»—V­ZéžÜˆäåÉ;Ø?üîÝ“ëlÈã ¾ås>¤©:}®úôæì[?ðâ“ù¢…rKxJV kÎýÄÒK9c`?úCZÕlÅä.“ßv»ïßgwb"R¾ž6o Ü>ìhcƒ£ Nmlp²µUÔé<:ž+IlˆåŒ"Ø•L®$±%>ž-ññÔ+4+T¿Œ³BeñNIaNd$ÄÆêìÐɧ¡“øúòb:ØUsœƒ­7zœM—usÌ jûLN. Ð#|Æ ú±G'OO˜?^|QSu5á*ËN.#84˜8=Ûáó±UÙÐ4€É'óDã'Píû³i©r1jJÉùÔTy†'1‘}TŠ|Sv*ÝÜÜèš“C{__!ã©Vcof?‰¹8š”Ä÷ÑÑürïiEÌ ÙäÏ y{P½ºÁÙ˜¢<@ÿ$&2'2’ýÚáÎ ÑÉÕ•™~~ ­QÃBþs¡ó÷9]0¥ýã³?2®í8“]Ox€L˜”›?ÿ”ãÙ\¿.—Ûq†9ÌdºÉ9ïøqò’W:ääå°%l KŽ/a×õ]ŠoYÚx»x3©ã$^îô2õÝê›à·1-¤$"#ƒÝ‰‰ìILdÏýûÜÍÊ*q_ÐÎÅ…þžžô÷ôä1ww\lmYºt)ãzß2ÝÜÜèææÆ¼FXw÷.Ë¢£9«gV(O’Ø϶øx|˜X§“¼½ñÕÊʫϴ5>ž9‘‘M2¼LÝÃÍüýyÒ?Ô‡4¢@õÝê3ºõh“^Ox€ÆDÌAYf€.]‚·ß†þ‘Ë͸Â'Ìb¿¢Ò'bT*6 fφ-¸|›å'—óé¸|ÛàuT¨è× y¦ù3¨m„–µTîee±ç¡iywb"¥ ±ÝÄɉþžžôóô¤Ÿ‡Õíô¯ÏѤ$–ݹÃúØØbg…=œ¢5+$ÄÆ2'2’ÓzU>}<<˜éçGÿÊœ~§NÜ9A—å²°ž;p.ïöx×Ì#”1$(5÷ïËI;¿ûNŽÖîG$³ø„Xƒ-ÞH ‚Ï>CêØ×v²tý‡l¹²…\É𯗓/¶{‘ÀÎ4­n8Ñ« r‘˜“ÃÌLîdeiofdpðÁΧ¦1¿§K]úyxhfyŒåOÌ ÍoÜXã24+ôW|<ÅÇS×ÁÿԩÄ:u8šœÌg‘‘»ïô0ÐË‹üüx4?°Ó©n'ê¸Ô!=;—;¾lîá¥B r’—+VÀŒrÊ®:Ä0ƒÏx…ï±ÇÀÒE¯^ðÙgD·oÄÊÓ+Y±`÷#мN7ŸnLî2™Ñ­Fã¨.AÎ/ !.;›ó7oÒ¬^=jØÙUšm¾%åANw²²ˆÖ7…£³²ÊeTöT«éSHð4/dì/)ÂgU:ÜÕj^÷ñáuŽ$%ñ}³Bw23™Éìsç Z5°µÕsFxªzufúùÑÕ­ò„ (/*T4 ÀËÉ«BBkÀ˜T”·µŸ> ^$ð%_òßâLšþ;#ÍžÍöFy|r.ÛB¶‘“gØÐZÍ®cÛŽerçÉ´¯ÓÞD¿…éÉ“$"33¹”šÊå´4.¥¥iã³³1j<ÔjjÚÙQÓÞ^~Ìÿ1P6U@¸”Ü\£OàµLRVœllxÔÝ]#x:º¸”{7ðY•înnt8+ôãÝ»|ççôÍðœ>-‡c/$pTÀðš5™áçG{—Št2¬Å0ÚÕiW!× 1 "0äºuKÞÖþóÏàJ2ï0Ÿwù7 [µ"ù®ÞVœZÁͤ›E^·u­ÖLî<™qmÇUª€…Å‘‘—G˜–À¹œ–Æ•´4“%rt±µU¢ň&[Ð+f¢µÊɘq[­RÑÕÍþgyz¸¹UÙY–¿ù³B÷î‘®çµm«R1ºV-føúÒ²Z53ŒP (Â$ÐKFÌ+g,—ÒÒ™Æw¼Ï—ÔÀÀÖôF8õêÓ̪{…í×&“{Õð‡ª£Ú‘á-†Ø9G}5Ño`²³"'ÿùŒ ƒaüMEJn.)¹¹¥6W466xÛÛS×ÁA~´·ÇÛÁvÕªÑËÃË&‚ÊI77zö =œR«TŒ¯]›üühâT±¹õA騄üñ¼û.ÜŠÈf?0“9ÔåŽÞ¶¹ÞuØö\GÞô>MTÚ|¸jø¼­j¶âåN/3¾íx¼œ*Ϻ¶DedèÌæ\JM%6?y™pW«©•œL’»;ñÙÙ—\ÓÉÆo-QS×Þo­ç^fÞ™%<@¦ÁC­æ Þðñaç4®Qƒ†VºÔU 1¨bck1`ìÝË8Ö2‹Oh€~Ãr¦—«yóN£pÒlÿÂÈIíĨV£x¥Ó+ô¬ßÓ„£/žè¬,ÂÓÒOO/øIKãZF†Qý-Þöö´¨VÎδpv¦ùÃǺ,]º”ÀÀ@$à~N±YYÄfg—MlþOV–¢œÿÜg[[½¢&'ÿ¹G_šá2=I'NàÞ·/d2„H`L„¨‚‚‚˜3û#žÍûƒOù˜\ÒÛ.£š µcN§RŠÈ¤Ñ®v;^îô2ãÚŽÃÝ¡â¶ÀÞËÊÒ8áéé\MO'ňÂF¥¢££Fä<î& i¹¹z…‘!Á”•—§3;£Y–*$nL5^@ ¨Ì@Ãñ¼.t@–õL{[æwÍåd’蔩·‹½ ϵ~Ž—;¾LWŸ®&g|v¶ŽÀÉÿ)Mê„’àhcCÓ|‘ShF§™³3lÞu¶µÅ×ÖV'*¯@ E!P1è?Y¶°¬3|öX.w ÌvwòîÄ+^áù6Ïãjïj”±$æä(ÄÍÕB‚'ÑÈ"dCs=³9 š¤QøSL¸Ç¦§¨\`ã <@c"þSKA® ·‡O{C”ž,77Æ´Ã+^¡CeºFRNŽÞåªðôt9fŽ‘q±µ¥±“Mœœhâì,?:9ÑØÉ‰ÚöE¬çáO1=â›}¹ÀÆEx€ÆDx€Š ((ˆ O>ARÁúV0«/„éyoë^¯;/w|™Ñ­GSÍ®d1?ò$‰°ôtN&'s29™S))\JKã^)_–”jEŽFè;u*Hä ò#<@ [šÁÌ~p¶¶²ÞÃуñmÇór§—iS«M‘çÐ;'SR8œlÔ`{N66²ÀqvV ''êŠ\Q@ (¨ž~^Y~Ô÷Q^éô #ZŽÀI­èÌ”bÇÑÆ†FZ³8ùbÇÇÁËÊ¢U€ð§˜qMð™áñŸZª;Uç…v/ðr§—iQ£…¦ÞbÇÁƆ†ŽŽ ?N¾'§¾££ÅŠœ¢þÓ#î±é Ó#<@c"<@EÄyÕyÖÍ\‡½ÑÅŽ‡ZMGWW:¹¸ÐÉÕ•.®®øy‡•@ %Ex€n7ÊÀs—Ê-v<óÅN!ÁÓHä À,T Gêׇû÷KÕÇËÎN#ròˆ@}þÓ#î±é Ó#<@cR±a{­vv ôòâ__6¶jED÷îÄ?ò;Ûµãÿ6dDÍšBüæM›Ì=«GÜcÓƒÌ= «&..Žƒš{+Ax€Š ((ˆOúôÑ”kÚÙ)fu:¹¸ˆ @ °„H ¡Wd$ïÐÉÕ•ú"žŽ@ VX+†¾ÕªU+ÑØËCyîqiû›óýBûo”””DLL M›6-ÑØËƒ¾×Ai(Í=.É{‹©î±v]ff&§OŸ®³BVãÊÈÈÀÖÖVSgkkkpZ±$íŸ{î¹ ûР2PÖ/â–†Õ êÕ«òTu¾bOLL¤V­Zenß¼yó ™ý@P±XªQ£¾¾¾9r„AƒpòäI:vì¨isíÚ5ÜÝÝ©Q£F‰ÚÅÝ»wùä“O¸sç-[¶$((H³”&0. Ì;—Ï?ÿÜÜC±J¶mÛÆ–-[4å™3g*–zÆaÿþý¬[·µZÍwß}gîáX{öìá×_Õ”›5kÆ;ï¼cÆY'»víâÏ?ÿÄÕÕ•É“'S¿~}s©ìHVÄÂ… ¥&MšHÁÁÁÒçŸ.yyyIׯ_×÷ññ‘f̘QâöEqóæMéÔ©SRvv¶4}útéûï¿7úï#¤ŒŒ iÔ¨QR‹-Ì=«%((H –"""¤ˆˆ)33ÓÜC²:öìÙ#=þøãÒ¹sç¤ÔÔTsÇ*yðàæ5ü×_I/½ô’¹‡du$$$Hýû÷—rss¥³gÏJÏ=÷œ¹‡T.¬fÀ›o¾Éœ9sØ¿? |˜iÓ¦éÔÇÇÇ3nÜ8š5kÆÀ9zô¨æØ­[·¸qãuëÖ3™% <<œ_|Q§>--ÀÀ@Z´hAß¾}5Aû,XÀ¨Q£8uê·oß®èáZ,&L ,,L§~åÊ•tïÞ¶mÛ2sæLòòòpssÃßß~øáfΜi†[Ÿþ9[·nÕ©ß»w/ýû÷§yóæLœ8‘¤¤$ˆg÷îÝ$&&*>_-3 °JÁ¼yó¤ž={J€¦8¶aÃÉ××W:qâ„tîÜ9©uëÖÒüùó%I’¤ÜÜ\iÚ´iÒØ±c¥«W¯šcèÃܹs¥=zH€Î½úå—_$???éäÉ“ÒÙ³g¥–-[J .”>ýôSióæÍ’$IR·nÝÌ1l‹âæÍ›Ò¤I“¤š5kJ Ð9Þ§Oéµ×^“"""¤+VH®®®RBB‚´oß>iÑ¢EÒ| uíÚUÌARR’4yòd©~ýúRóæÍuŽ7N9r¤tíÚ5é?þ\\\¤°°0©}ûöÒ’%K¤ßÿ]êÞ½»táÂ3ŒÞrøî»ï¤Þ½{K€tòäIűíÛ·Kµk×–þý÷_éâÅ‹R—.]¤Ù³gkއ„„Ho¼ñFEÙâØ²e‹4lØ0ÉÆÆFZ¶l™âØ­[·$iÆ Òµkפ±cÇJC‡•rss¥)S¦Ho½õ–Ô´iSiÿþýf½qH’¤“'OJ!!!’Z­–®\¹¢8 Í›7OSþñÇ¥öíÛ+ÚlÙ²Eš:uj…ŒÕR9qâ„"ÙØØè AƒIß|ó¦¼zõj©S§NÒ¤I“¤Ñ£GK£G–ªW¯.½ÿþû=l‹"))I ‘¦M›¦#€"""$;;;)99YS×£GiùòåŠv#FŒÎŸ?_!ãµD233¥éË/¿Ô@)))’£££âõ=räHiÖ¬YR¯^½¤´´4I’$iÑ¢EÒÊ•++tÜ–Fhh¨"¹¹¹é Ñ£GKŸ|ò‰¦¼iÓ&©qãÆ’$IR^^žÔ§OéîÝ»:^KäÚµkRHHˆÔ¹sgôÕW_IO=õ”¦|ëÖ-I­VKþù§ôæ›oJ’$IñññRÛ¶m+tÌÆÆjLÐå!ßø¬R©tŽ………ñÖ[oiÊmÚ´áÊ•+:tˆœœüüüذa ¨°ñZ":uôßãððpÞ}÷]M¹uëÖ\¾|Y„¬{÷î|ñŦ¨ãêêJŸ>}¸zõ*¡¡¡Šcáááøùùáâ⢩kݺ5W®\á·ß~£eË–¤¥¥qýúuËŸÖ6!öööôéÓGF£0QQQ4jÔHS׺uk.]ºÄàÁƒY°`Ï=÷›7oæÇ¬°1["íÚµ@­Öýˆ gäÈ‘šrëÖ­¹ví999¬_¿žÞ½{Üý+( aÆ4lØPo¬²°°0Z·n­)ûøøàâ₃ƒÇçöíÛ\½zUÑÆ¨bccæ\]]IOOÇÝÝ7rïÞ=žxâ ÆŒº ÕIDATcÆQZ6÷îÝSÜc777RSSIOOÇÉÉ €I“&™kxVAll¬æ^æãææFll,,^¼gggÖ¯_¯ø[JŽö{Èï±±±¼ûî»,Y²„¥K—2oÞúÞ/$I"..ŽÌÌLÅ—)AÙˆÕÙÝåææFvv6‹/féÒ¥Ô¯_Ÿo¿ýÖL#4Bƒ³³³"8bzz:jµšV­ZY¼ú­,T«VMçÛÙÙ)‚q T>´ï1Ȇ]777† Â!CÌ42ëAû½äײ››jµš7ß|ÓL#³.ô½_€ü=qâDs ˪(êý¢C‡tèÐÁL#3.Vµ Ìx{{sóæMM9** ooo½K9‚²¡ï×­[WÜc#âííMtt4¹¹¹šº›7oâããcÆQYÞÞÞ¤¦¦’˜˜¨©‹ŠŠ÷ØÈè{¿ððð3—FÄÛÛ›[·niÊù¯kk‹&P1Œ9’àà`òòòX³f #FŒ0ó¨¬ qMOçÎñòòÒ$ŒŒdÿþý 6ÌÌ#³|||èÞ½;+W®äž[·n¯e#3räHÖ¬YCvv6 Þ/LÁˆ#غu+wïÞ`íÚµ´iÓ†† šydFÆÜ.ìÊÀСC%??? |||¤víÚiŽ¥¦¦JO=õ”T³fM©^½zR¯^½¤¸¸83ŽÖ2yê©§÷¸C‡šc)))R@@€æ÷îÝ[Š7ãh-“3gÎH~~~RõêÕ%GGGÉÏÏOúúë¯5Ç8 ÕªUKjÚ´©äîî® ç (9÷îÝ“üüü¤ZµjIvvv’ŸŸŸ4mÚ4ÍñóçÏKþþþR£F$OOOiúôéf­å2fÌÉÏÏO²±±‘¼½½5»¼$© 8jõêÕ%___©k×®Rtt´Gk™üßÿýŸäçç'9::JÕ«W—üüü¤K—.iŽÏš5Kruu•š6m*ùúúêìÆ³¬&¼@ AIK`@ ªB @ ¨r$ Ê!@ ‚*‡@À(ܸqCÊÀ\DEEgÖ1Ë@ À ‰ŠŠbРA¼ñÆŠú °qãF£_/''‡ (‚V$‘‘‘téÒ…¾}ûŠˆË D$X!)))œ8q‚ßÿ={öhê/\¸@DD„Gf~ýõWºvíʵk×øùçŸÍ=@`$X)|üñÇ|ðÁzŸ9s†åË—+êfΜÉýû÷øñÇÙ·o_|ñ£GæÛo¿E’$V®\ÉØ±cYºt)©©©Šþ—/_fÊ”)Œ3†íÛ·+Žíß¿Ÿ—_~™Ñ£G³bÅ òC?~œÕ«WɼyóX¿~½Þñ>xð€™3g2bÄ>üðCÍ8:ÄŠ+ˆˆˆà¿ÿý/gÏžÕé{ñâE–,YBLL .ÔDk¾uëï¼ó#GŽä‹/¾ 33€#GްbÅ MÿÍ›7³k×.Mù›o¾áúõë„„„ðꫯ2vìXfÏž­‰ž+*7B V̤I“HHHà÷ß×9véÒ%~úé'EÝܹsyðà¿ýö#FŒ ==Áƒ3wî\š7oÎáÇ4hëÖ­ã믿VôçwhÚ´) 6ŒãǰmÛ6&NœHçÎ9r$‹/櫯¾d!öá‡òÌ3ÏpýúuÍõ “““Ãc=Æ… xî¹ç ãÑG%;;OOOœ©U«Í›7ÇÍÍM§XXü1ƒ âÒ¥KüðC<==9pàÆ ã±ÇãùçŸçîÝ»;v¬Ä@`>D6xÀŠQ«ÕÌ™3‡™3g2tèÐR÷ÿä“Oxíµ×YDܺu‹~øwww¾úê+>þøcMû¿þú‹5jËÊ•+éÒ¥ ³fÍâ³Ï>côèÑ8::2cÆ ¦OŸ€ŸŸûöíÃÎÎNï8þüóOnß¾Mhh(666 >œš5kòÇ0jÔ(|}}éÑ£&L0ø»Ô¬Y“#GŽàèèÀ—_~Iݺu5¿Oÿþý©Y³&§N¢sçÎdddŽ$IT¯^ëׯs÷î]®^½JË–-ñôô$,, ???žyæ\\\2dH©ï±@ 0bH °rF…££#«W¯.u_[[[Ísll Þ2\]]uv\©T*Íó.]ºpûöÿ·oÇ,­daÇÿ1‚`CŒa*51APÁBÐCL‘""i?€‚ˆ`% ~±Š…*ˆ`£˜&j/Š£¤ÅÊÖB‰n!6»ì®7 ÷ÂÎóëfÎy§{xÏ{~¾Ž –—— ‡Ã„Ãaðx<ö[ZZþ6üX–E4µ÷w¹\Äb1,Ëúö·477Ûá§²æÈÈHU ===X–…Ûí&S,ÙßßgjjŠl6Ëáá!$ 2™ ­­­´··“H$Èçó”Ëåo×$"¿Ž:@"ÿs.—‹õõuæææˆÇãöûúúzÞÞÞ~hzþ3Ó4ikkÀëõ’Ïçþ~áàõzÿ2¼]*•H&“5­WY³2ÇP.—yzzÂëõ_¡b±Èõõ5ÇÇÇ<>>²ººJSSóóóøý~NOO1M“««+VVV¨««cff¦æºDäçPHÄÆÆÆèì쬚ŠF£ÜßßóòòÂÇÇ{{{¼¿¿ÿ§}*CÑ···œœœN§H§Ó¬­­Ù¿¿¾¾rppðíu“É$¦iR(€¯ê»»;R©T͵ŽS(¸¹¹áóó“Ün7ƒƒƒöžGGG466âááËËKb±ççç”J%º»»ÉårôööþP¨‘_GHÄ!666ªnm¦§§éèèÀçóqvvVuäU‹T*E0¤¯¯\.gÏmnnâóù0 ƒP(„ßﯺUõoºººØÞÞ&“É`lmm …j®utt”ÅÅE0 ƒ¥¥%vwwíP$¡¡¡ÉÉIà«ã•Íf …BöñÝóó3ýýýƒA"‘‡ÙÙÙšk‘ŸÇõY¹‹*"""âꉈˆˆã(‰ˆˆˆã(‰ˆˆˆã(‰ˆˆˆã(‰ˆˆˆã(‰ˆˆˆã(‰ˆˆˆã(‰ˆˆˆã(‰ˆˆˆã(‰ˆˆˆã(‰ˆˆˆã(‰ˆˆˆã(‰ˆˆˆã(‰ˆˆˆãüëëó~âIEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/compressed-select-nocache.svg000066400000000000000000000761021231437614300267460ustar00rootroot00000000000000 Selecting with small (16 bytes) record size (file not in cache) 10 3 10 4 10 5 10 6 10 7 10 8 Number of rows 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 MRows/s No compression zlib lvl1 lzo lvl1 bzip2 lvl1 PyTables-v.3.1.1/doc/source/usersguide/images/compressed-writing-shuffle-only.svg000066400000000000000000000730161231437614300301660ustar00rootroot00000000000000 Writing with small (16 bytes) record size 10 3 10 4 10 5 10 6 10 7 10 8 Number of rows 0.0 0.5 1.0 1.5 2.0 2.5 3.0 MRows/s No compression zlib lvl1 (Shuffle) lzo lvl1 (Shuffle) bzip2 lvl1 (Shuffle) PyTables-v.3.1.1/doc/source/usersguide/images/compressed-writing-shuffle.png000066400000000000000000001600631231437614300271730ustar00rootroot00000000000000‰PNG  IHDR@°AàÚ²sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwXW÷ðïÒ« RD ˆ  MÔX[Ôˆ% F¢1v_ˆ11úþŒ½E- &Ñ$ŠFˆ± ‚¢¢ ‚ éíüþØ— KwAá|žg˜;wæÞ-fÎÜ+""cŒ1ÆX3"ר`Œ1Ækh1Æc¬Ùáˆ1ÆcÍ@Œ1Ækv8bŒ1ÆX³ÃcŒ1Æš€cŒ1ÖìpÄcŒ±f‡ ÆcŒ5;1Æc¬Ùáˆ1ÆcÍ@Œ1Ækv8bŒ1ÆX³ÃcŒ1Æš€cŒ1ÖìpÄcŒ±f‡ ÆcŒ5;1Æc¬Ùáˆ1ÆcÍ@Œ1Ækv8bŒ1ÆX³ÃcŒ1Æš€cŒ1ÖìpÄcŒ±f‡ ÆÞ2D„èèhäåå5vWj%66ÙÙÙþí{~~~­¶GBB‚,»(,,Dtt4ŠŠŠdÞV]äåå!::999Ý©yõêRSS»ŒÕˆ &sßÿ=,X Q–˜˜777H”ûùùaþüùÕîoùòåøì³Ï„åÀÀ@Ü»wO¢NHHñâÅ‹7ì½t8pƒ –ïܹƒ   ‰:™™™hß¾=®]»ÖÀ½«Ÿ÷ßþþþ€¬¬,´oß¡¡¡5n„:àöíÛÖ`ذaU¾~D„}ûöaÒ¤IèÖ­† Rm[hß¾ý½222pôèQäææÖ{å‰D"LŸ>#FŒ@II‰ÔöÛ˜<==±bÅŠzo¿`Á,^¼XŠ=b¬r1™SSSömÛ••%”áÔ©S8~ü¸D݃ÖøßpÛ¶mall,,¯^½GŽ‘¨£©© ())IáH®®.ÌÍÍ…åÇã›o¾iÄ5ŽÜÜ\Lž<«V­Â‡~(”'%%áàÁƒ5jþøã‰÷L©œœ¸¹¹ÁÇÇݺuÃÊ•+k €¤!66&LêÙ eeeœŠ‹‹###Œ?^øo955yyyÈÈÈ@tt4 M›6èÒ¥ –.] ===â3iiih×®ž>}ŠgÏž¡ÿþPTT¬ÐßàñãÇèÔ©Z·n¢¢"a?奥¥!//†††BYll, ¡  Páy888 W¯^BŸ222„Ë €8@*+==·nÝB÷îÝa``Pã±...ƽ{÷ðêÕ+´mÛ–––‰D€˜˜èé顨¨7nÜ€ºuë‘H„¤¤$ܾ}}ûö…¶¶¶Ä>cccñìÙ3dggÃÈÈ=zô¨±5Ùµk±hÑ"‰òÄÄDBYY¹Êm7n܈;wî ,, -[¶¬sÛqqq¸{÷.ÌÌÌ`aa@%&&¢]»v——êæççãåË—h×®âââ/^¼@aa!TTTкuk¡ndd$?~ ccã Ǩ°°wïÞEJJ LLL„v eË–X·n–,Y‚Y³fAUUµÊ¾'&&BQQ:::(((^g())ÁƒðìÙ3tèÐ]ºt©tûˆˆhhh GÇ9-- ¡¡¡ÐÐЀ••TTT„u•}Û´iyyyƒnݺÕê5€¨¨(DEEAKK =zôž÷äÉ“…:ÙÙÙHJJª°­¦¦&Zµj%,GGG#<<èÕ«—ÄkÈX•ˆ±`iiIK—.–»téB¿ýö)((ÐóçωˆèÚµk€ˆˆhêÔ©4hÐ Zºt)©©©zðàÍœ9“ÜÝ݉ˆhÁ‚¤  @ÊÊʤ¥¥EZZZtïÞ=ºrå  ŒŒ ""Ú½{7iiiÑøñãIQQ‘455I__ŸÎœ9#ô)77—FM¨]»v¤­­MêêêB[•Ù»w/µlÙ’JJJˆˆ(..ŽD"=zT¨3pà@š?>íܹ“LMM‰ˆèÀ¤¬¬L Bß>Lééé€&MšDêêꤣ£CŠŠŠ´`Á‚jqTTuîÜ™´µµ©K—.¤  @}ûöÖ+**ÒØ±cIKK‹tttyyy‘¯¯/)))‘ŽŽ©ªªÒºuë„mBBBR—.]HYY™lmméõë×B:pàeffúûï¿«ík·nÝ諯¾ªrýÇ…×»¬ÜÜ\jÙ²%íÝ»·Úý—wçÎ@cÆŒ!yyyRWW'äããCDD/_¾$EEE:qâ„Ävk×®¥Ž;RAAijjjÑ¢iiiÑàÁƒ‰ˆ(99™>üðCRTT¤^½z‘œœ9’ŠŠŠˆˆ(,,ŒLMM©U«VdnnNòòò4pàÀ Ï«E‹ôóÏ?Wû<iÊ”)4cÆ RRR"”››K/^¼ {{{RQQ¡ž={’H$¢©S§ Û•””Ð’%KHNNŽôôôH]]´µµ),,Œˆˆ~üñGRWW§Ö­[“šš™ššRhh¨°ýôéÓiàÀ´lÙ2á³Niii4pà@@&&&¤¥¥Eªªª4kÖ¬*ŸCaa!=š”””¨k×®¤©©IšššEDD&L "":vì˜ðÙ(}öŸ››K$''GVVV¤¤¤D666Âçž±êpÄÄgŸ}FÖÖÖDD”˜˜H***”››Kýúõ£C‡Ѻuë¨[·nÂ6S§N%999š3g]½z•ÂÃÃ)''G""ÿQX²d‰D{•@ ôÝwßQnn.•””ФI“ÈÍÍMØféÒ¥¤§§Gþù'‰¿¨ÝÝÝ« €ž={FèÞ½{DD´uëVRWW§Ñ£G‘ø––>}šˆ$ "¢… ’«««Ä>K Q£F Áá©S§HII‰233«ìËÇLÖÖÖB0öúõkúᇄõŠŠŠ4pà@ŠŽŽ&""???@£GÚÙ¼y3µmÛVØ&))‰ž>^"H6l˜DÀ“––F¬ÐÇñãÇ“——WµÏÃÑÑ‘”••iåÊ•JwïÞ¥ââb|8ˆ¨BÂtYHIIÂ5440mÚ4‰:3fÌ€‰‰ —#½½½…vŒ/^ ** €ø’œ©©)nݺ…½{÷â‡~€ªªª°¾>ž?999tèС^Û–öë§Ÿ~Â;w0wî\|þùçÂ¥ÓêÌ›7-[¶„ºº:–-[‘H„?ÿüðÙgŸ!00<üþûïHMMÅôéÓ«Ü_nn.~úé'Œ?ÉÉɈŒŒDff&úõë‡K—.¿ Â~µµµáááQa_:uÂÓ§Ok|^^^øê«¯Ð»woôèÑñññÀG}„„„DFF¢°°½{÷ú°gÏØÛÛc̘1PPP€¼¼<<<<`ii‰cÇŽA$ÁÛÛJJJhÕªæÎ‹ÈÈH\¿~]hwÈ!ؾ};lll`ii 555>|'N„³³3@II©Â%Ôò444ŸŸ7n ¨¨"‘£FB›6mªÝîÒ¥KX²d Ž;&äîÙ³£GFvv6"##‘˜˜;;;áy3VÎb ÂÑÑD„Ë—/#88Xœœœ0{ölãòå˘5k–Äv¥ù+² ¡¡œœJJJ[!« „„„`ܸq ùsç°ÿ~üþûïxýú5Þÿ}´hÑâú*‰ ®®.Ün^™Ï?ÿ=‚••tuu1xð`¬\¹R"麬Òü"Ê444@HD¿ÿ>Fމââbôë×FFF““Cqqq½ŸKZZôôô„©úX´h‘ÿcnnŽ«W¯âèÑ£ðôô¬õ>ÔÔÔЩS'áÎ0ôîÝ;wîÄ–-[°mÛ6L™2¥Ú×îéÓ§ "lÞ¼;vìXgjj X¹r%<==aii  :+W®Dûöí%ê"99¹Æ~—ÿL”£kÖ¬©°®4'**ªÊ»+£¢¢Ð§O‰| ¾}ûBAAQQQ°³³«´Ý¤¤$¼~ýºÎŸWWWÌ™3'N„¼¼<¬­­ñÅ_`èСUn‡qãÆaÍš5B°•““ƒ„„øùùáôéÓõK=cÕáˆ5ˆ-Zàý÷ßGPP‚ƒƒ±eË€ž?ŽÓ§O#''§^gJÉ?âõ!//MMÍZý^ž‹‹ >ÿüsœ:u Æ ƒ¢¢"ÆŽ‹'N@[[»Æ?oÚ÷R;vÄÅ‹…ÿÜwíÚgggÄÆÆBN®~'{W­Z…Î;ãôéÓBÂxddäõSSS©©©())©s¿:vì@œÐ]6ZCC=ªÓ¾rssñìÙ3 esçÎ…··7¦L™‚¿þú [·n­°]Ù×K__°oß>ôëׯÒvºv튿ÿþááá¸~ý:¶oߎ!C†g„J%''×xö¤2¥}8yò¤p|*«óøñãJ×éêêV8£÷ìÙ³j“ÿq /_¾¬S•””°uëVüç?ÿÁµk×ð믿bذa¸uëÞ{ï½ õóóóáîî'''|þùçB¹ªª*444°zõjL˜0¡N}` àÛàY0`~þùg} ¥¥Uçýš››Wz§H]}ðÁøù矅 èÉ“'øçŸjÜÎÙÙñññظq#ÆŒ7nΞ=‹¿þú«ÚHZ}€°°0aŸøöÛo‘œœŒ´´´zï311Bð“——‡W¯^½Q?ŒŒPXX(ÜUUÆÆÆèС>,”•””à?þÎTÔV`` п¡l„ PTT„»»;  q·–©©)”••%^/===téÒ»wï–ØwII ž}:|||Wa<¡èèh‰áj«S§NhÓ¦M…> ïe;;;œ;wN¢ÍŒŒ $%%ÁÞÞ/_¾Äýû÷…ugÏž…ªªj¥I)uuu˜™™ —ñû#11±Úþ>~üùùùÐÓÓÃðáÃñý÷ߣeË–UhsçÎE^^öíÛ'Q.‰Ð¿ìÙ³§ÂJu †YóÄg€XƒqqqÁš5k`oo/‘Ëãèèˆo¾ùË—/¯×~GމQ£F ÿ=Ï;·^ûùöÛo1xð`têÔ FFFÈÎΆA·Ôêéé¡GˆŽŽ†««+ }ûö°´´Dxx8lllªÜvøðá˜5k¦L™### 0 Ú?:Õñôô„‚‚œ!''‡'NÀËË«Æ<¦êLŸ>ÞÞÞHNN†ºº:Ο?ÔÔTX[[×{ŸíÛ·‡©©)NŸ>9sæH¬{ôè~øáa¬ï¾û:::˜3gŽp‹úöíÛ1bÄdddÀØØ~~~PPP€OmO: À½{÷ð믿böìÙèÞ½»°^EEŸ|ò Ö¯_íÛ·Kl«¡¡gggaàB,[¶ »wïÆ°aÃ'''$%%áâÅ‹0`6oÞŒqãÆÁÈÈýû÷GQQŽ= ooo‰Ï@qq1ðí·ßÖùx*((`÷îÝ7n"""`kk‹¸¸8üùçŸðððÀªU«àëë‹ß~û mÛ¶Å”)S@Dðó󃿿?† ‚#FÀÆÆS¦LA\\NŸ>uëÖ g—ª²jÕ*L›6 ÷î݃ ––++«*·9~ü8öìÙ#äý\ºt ºººÂ?e6n܈þýû£oß¾6l²²²péÒ%´nݺÂe1ÆÊ“÷õõõmìN°æ¡M›6PPPÀG}„N: 円†ÐÓÓÃĉ%N¹‹D"XXXT:¶ˆ¹¹9ºví @|YÄÞÞ÷ï߇H$€ ®®Ž-ZÀÑÑQÈ5iÓ¦ lmm%öÓ²eK899_&˜1cìíí1bĬ_¿·nÝ‚¡¡!\ís344„«««0Æ þCoccƒ¾}ûJÔ522΀ijjbÔ¨Qˆ‰‰Azz:кukÈËËÃÑÑQâ’ˆH$Â|På%äææâéÓ§HOOǨQ£°téR‰ÎÁÁAâËÉÉÁÉÉIâÌ›’’œœœ ®®+++XYY!,, jjjøê«¯`ccƒ=zH¼†vvvhݺ5D"‘Ð÷êÎæ%&&âøñãðòò’È-IOOGDD455áèèMMM(((à½÷Þrq:vìˆ#F ""Ož<««+„²²2>úè#L›6 ÊÊÊÐÐÐÀŒ3 ¦¦†øøx(**bîܹB°<~üxèëë#22:::X³fD¢¶H$‚¹¹¹D°VVVèÓ§’““¡ªªŠÕ«WÃÎÎÝ»wGçÎ+}={ö„¶¶6^¼x˜˜XZZbÇŽïKKKXXX ##mÛ¶EÛ¶m¡¡¡!<Ú´iƒž={¢U«VðôôáñãÇÈÍÍ…‹‹ –,YòÆyw¬é‘´{Çedd@$ _œÏŸ?‡••öîÝ ww÷Fî]Ó’œœ 333|÷Ýw˜1cFcwGP\\Œ:ÀÛÛÞÞÞ ÒfAAzöì‰aÆá¿ÿýoƒ´Ék‚9@ÅÅÅxþü9ÒÓÓk½MRRÒ;3ñ$“ÐÐPèêê¢K—.èÓ§,,,àîîÎÁ èêêbË–-X´h‘#ó6ð÷÷GJJJ…ád©ô’mm.á1Ƥ§IòööÆ÷ßV­Z —üüü ©©YiýØØXŒ9‰‰‰ÈÎÎÆÇŒ-[¶ÔûŽön+((@hh(¢¢¢ ¨¨ˆ=zT:“žsçÎA[[ûrФ)44ùùù.•ÊJzz:‚‚‚ЫW/a|&ÆXÃhRÐáÇ1pà@ã¯Ì™3Gbæð²ÆŽ‹víÚá»ï¾CFFÞ{ï=¬_¿žÿãgŒ1Æš¸&uªcòäÉ„‘jjj(..®ò¶Ò¢¢"œ>}Z8MKK ãÆÃÏ?ÿÜ`ýeŒ1ÆXãh’·Á¯\¹—/_Æøñã«]ôÕ«W(((8íܾ}{\¾|YX~øð!>|(óþ2Æco •ï|m šd$''‡¶mÛ"$$ hÛ¶m…:¥IÒe‡WUUEFF†°|ôèQ=zTb04YIHH€¶¶¶0t}]EGG×zø÷ÚÔ­®NUë*+/_VTTTá5yòäI•#ØJSAA’““kœs¨*uy233QTT*ëHë¿zõ Âȼ•Õk¨c\Uk«®¯QMm¥¦¦BAA¡Ò[¢«zJ¿ÊAPÙû¶|Û±±±Ð××—øN‘•79Æuݾ1¿/Ê¿F999HMM­ô;]Ú*{ÔE]Žqm¾[duŒË—åççãöíÛHHH¨Ußßi >ýjš>}:Íž=»Òu¥3Rgee e;vì gggaÙÇLJ|||dÝM"ÏÖüìÙ³zo_—~Ö¦nuuªZWYyù²´´´ 3w:´ÆþHÃË—/içÎõÞ¾.¯ÑÕ«WéìÙ³ÕÖ‘Ö1öóó«0kzùz uŒ+k».êúÕÔÖÙ³géêÕ«•®«ê5 ¤ÀÀ@‰²ÊÞ·åÛž9s&EDDÔØgixÓï¥wåû¢üktíÚ5š7o^ý‘†ÊÞuQ—c\›ïYãòe/_¾$ƒjûÒT4éè‹/¾ ‰'Vº®  €455éÆBÙüùóiæÌ™ÂrC@)))”ŸŸ_ïí_¾|)ÕºÕÕ©j]eååËŠŠŠèÕ«We uŒ )99¹ÞÛ×å5ÊÎΦÌÌÌjëHë§¥¥Q^^^µõêWÖv]Ôõ5ª©­ÌÌLÊÎήt]U¯Ñëׯéõë×e•½oË·½sçÎ7zîuñ¦í¼+ßå_£gÏžÑþýûkì4Tö>¨‹ºãÚ|·Èê—/kNP“I‚.((À¤I“pîÜ9<}úþþþØ»w/ÆŽ+ÔéÝ»7Ö­[PTT„‡‡|}}ƒ«W¯âÇlÐñ?ÊÒÑÑ’’R½·oݺµTëVW§ªu••—/“——¯v‚EYRPPx£i!êò©©©U9üB)icmmí —]êò~¶7i»®¯QMmijjJ\,«ª×¨t´á²*{ß¾«Ç¸®Û7æ÷Em>G²RÙû .êrŒkóÝ"«c\Ó¾›²&“$//CCC¬X±qqq022¦M›àææ&ÔéСtuu…å 6`Á‚pvv†ŽŽ¶lÙRí¼ML6xØÙãc,{...õÎaµ£«« {{ûÆîk"šTôÿ÷ÕÖ9qâ„IJ²²2vìØ!Ën±Z¸rå zôèÑØÝhÒøËÞÝ»w¡££SïXÍ233Ž:4vWXФB”¶Òyb«›/öƈˆˆh˜1ö122‚««kcwƒ1V °²²jw5™3@åÈ‘#¸rå ,--»+Œ½5âââ //Ïcì­ÅLœ8±ÁfŽfì]€M›65v7TJJ ´´´  À_«²RPP€¬¬¬jÇ×b¬¶šÌ]`Œ1Ö˜%ReÒ—œœ,1Z?co‚ÿUaŒ1)3fLcw¡ÉkÓ¦ FŒÑØÝ`MŸbŒ1ÆX³ÃcŒIAJJ ŠŠŠ»MZAARSS»¬‰àˆ1Ƥ€s€ds€˜4qcŒIçÉç1iâ3@Œ1Ækv8bŒ1)à Ùã &M1Ƙpìq“&ÎbŒ1)à Ùã &M|ˆ1ÆcÍ@Œ1&œ${œĤ‰ ƓΒ=ÎbÒÄ«ÀÃÃnnn ‘(?sæ æÏŸßH½jzâããáèèˆàààÆî “‚1cÆ U«VÝ&s€˜4qô[¢¨Hü599@I©ú:gΜ¢¢".\ˆ›7o åOŸ>Å_ý%ã6ŠŠŠ°°°€¦¦fcw…1Æš>ô–X´PU•ý£ÿÚõç‹/¾@XXüýýk¬ûàÁüñÇxöìY­ŸoVV®]»†ààà ×ôóóóqýúuüùçŸÖeggãùóçÄY@@…õ¸téJJJ$¶KLLDjj*òóóqõêU";;[¢Ntt4rss™™™HJJÖåååáúõë8{ö,âãã+<Ÿ””#88X¢?¥ÏçæÍ›8þ<"##…r]]],]º]»v•¨ÿêÕ+œ;w¡¡¡(,,¬°®4×äÖ­[ ­ÐÖ88Hö8ˆI±*ùøøOµuæÏŸO7n|ã¶æÏ'dÿ°¶®¹/­Zµ"òöö&sss***""¢­[·R·nÝ„zééé4dÈ@íÛ·'äéé)Ô¯Š¿¿?µlÙ’ÔÔÔH__Ÿèûï¿'"¢»wïRÇŽIUU• IUU•~øáa[???RRR"RRR" RVV¦Ã‡“‹‹ )++“šš™ššÒ;w„í\]]ÉÚÚšŒISS“PÇŽ)&&†ˆˆJJJ­[·ŽH$‘ ]ºt‰ÚµkGzzzÔ±cGRRRúKD´}ûvRUU¥:¡¡!ÉÉÉÑîÝ»‰ˆ(44”Ú¶mKzzzÔ¹sg’““£‘#GQVV a_›6m"%%%jÛ¶-)))Q·nÝèÑ£G;àŽ IDATÂúÁƒ“½½=YZZR‹-HAAzõêE)))5¿° èìÙ³4hРÆîFƒ:qâ%''7v7𴏏8:uêTcw£I{ùò%4v7ŸbUZ±bâããqàÀJ×ûúúâÑ£G¸wïž>}Š   =z{öì©rŸQQQøøãáåå…¸¸8$&&"88­ZµBII >þøcôèÑ111ˆ‰‰Á×_ OOO‰3'………èÞ½;222‘‘WWWÌœ9£FBzz:ÒÓÓahhˆãÇK´žžŽ#GŽ ##W®\Aaa!Ö¬Y#QgãÆ˜8q"ÂÃñÿ~¤§§c̘1˜;w.âããñøñcìÛ·óçÏGbb"²²²0oÞ…¡¡!>ùä‰z±±±Ø²e ÒÓÓ‘€¤¤$üþûïUsÖ08Hö8ˆI@o EE@Y¹aµ¥««‹E‹Á××yyyÖ:t£FB÷îÝpttÄ¡C‡ªÜçáÇ«V­‚¶¶6ÀÖÖîîî¸}û6ÂÂÂðé§ŸBOOŠŠŠøì³Ï ¢¢???aÊÊÊX´hTTT ''‡ÀÐÐsæÌŠŠ áì쌠  ‰¶]\\`gg‘H 6 çÏŸ—¨³sçNxzz¢K—.077ÇéÓ§‘ŸŸ?üQQQˆŒŒD=PRR‚[·nAAA***¸sçÒÓÓݺuƒ““@CCñññxôè@GG“&MªòØbÚ´i——‡‘‘f̘‰Ë‹ƒ ‚‹‹ D"Zµj'''œ;w®ÊcÎc¬"€Þÿý/—'ûG]o8Z¸p! °}ûv‰òôôt¤¦¦ÂÎÎN¢ÜÎÎQQQUî/** 666PSS«tH$‚­­­P¦  kkëj÷©¬¬ "’(ÓÐÐ@NNNµÏ­wïÞˆ‹‹“(“““üHDEE¡°°“'OÆøñã1~üxxxxÀÂÂ………PQQÁŽ;àïïV­ZÁÒÒ6lr‰|}}!''sssÂËËKÈaªìùWvkxœ${œĤ‰ V- ¬\¹k×®•ãDSSJJJxúô©DýGAWW·ÊýéëëãñãÇ•®ÓÕÕUH¦~üøqµû¬¯{÷î¡]»vÕÖÑ××G«V­pçÎ 777âa¢££„ &à믿Ʒß~ °²²Âõë×qïÞ=¬^½×®]¶+OWW·ÒãYºŽ½Ýx Ùãq€˜4qÄjäåå…-Z`ÇŽB™¼¼<ììì$.!ãÏ?ÿDÿjn5³³³Ã‹/pëÖ-¡¬¤¤QQQèÕ«444$öŽçÏŸW»ÏúÈÉÉÁÙ³gáêêZm={{{ÄÅÅáÌ™3å©©©HNNFZZâââ ¢¢{{{¬Zµ C‡‚¼°°0@÷îÝ1sæL¬X±ÑÑÑîî€þýûãöíÛwŸ={zzz°°°xÓ§ÌdŒs€ds€˜4ñ8@¬FJJJX½z5&Ož ¡|ýúõpvv†••†ŠŸ~ú EEEøÏþSå¾ÜÝÝñá‡ÂÖÖ£G†¹¹9~ù帹¹áË/¿Ä7ß|ƒ  ((&&&Ø¿? „Q£F½ñóøñÇ‘••uuu=zøòË/«Ý¦GXºt)ÜÝÝññÇ£}ûöxðàΜ9ƒß~û -Z´€µµ5Ƈ®]»"&&'OžÄµk×LJ¹¹9>øàäççÃÏÏ_|ñQPP ÑÖ”)S°wï^˜››cÚ´i¸wï±ÿ~¨¨¨¼ñógŒ1ö/y___߯îÄÛª4‰ÖÑѱÊ:ÐÖÖF¿~ý¦S äƒ>€žžž°Ü­[7á,Çûï¿@üߨ¸qã‘‘'Ož`àÀØ¿? ªÝ÷ĉaff†´´4ddd`øðá˜9s&TTT`mm [[[¼xñééé˜1c6lØ ‘›c``P጑‘Q…×ÀÄÄ}úô N0¶´´D÷îÝñêÕ+Œ9{ö잣H$ ~­Ë_nrqq³³3âââðøñc´nÝ‹-‚ƒƒôõõÑ«W/¼|ù=‚ºº:þïÿþOh×ÅÅE8>¹¹¹˜2e >ýôSÈÉÉA$ANNŽŽŽÐÒÒ‚œœ<<< ¦¦†‡ÂØØ6lÀ°aľˆD"ôèÑ;w–(ëܹ3ºuëVíqoHOž<Áµk×ðñÇ7vWLJJ ”””*ä‘1é)((@FFTUU»+MVVVvíÚ…E‹5vWdNDå³G™ 46¬.Fôöö†©©)¼½½¦S¬^ˆN:UHæf²€M›6!  ±»Ò`üýýáääÄ—Ád(>>·nÝâË`2”+++$$$4vWdŽ/1ƘŒ3¦±»Ðäq“&€X³àëë ÆîcŒ±·@¬Y(;¶c²’’---((ðת¬ ++Kâf Æê‹³õcL x Ùãq€˜4qÄêdçÎØ·oŸ°|õêUcÒÀãÉç1iâˆÕIPPþþûoayôèÑØºu«°|èÐ!DGG×iŸ±±±ÕÎV^zz:}úôJ§£Ø¶múöí‹¡C‡ eX¾|9‚ƒƒÑ²eK,[¶¬Â$ŸU‰‰‰|}}ajj*”‡††bçÎÓq”µ|ùrÌŸ?_}õUµ‡2&+<Œ%''ó8@Lj8zKüýüoìÝ-óv¬¬« €ŒŒŒ`ee%,‡„„à·ß~Ãܹs+­ÿÛo¿AEEE"úì³Ï0vìXŒ9?üð\\\ “ûgllŒìÙ³_ýµP¾yóf¼~ýJJJ•n§§§===´hÑ¢Æ6“Hö8ˆIç1 NNNðöö†··7ÜÜÜŒ7 S;ÔÆ¶mÛpðàA,^¼ÐÐÐÀîݵ îD"¼¼¼pàÀŸQò÷÷ǬY³êõœcŒ±ò8b•ÊÍÍ…»»;† †9sæÔiÛ²gaäääðþûïãÁƒµÞ~Ú´iHNNf`÷ó󃡡!X§~0ÖRRRPTTÔØÝhÒ *½û“±úàK`o‰q]ÇÁB×BæíhT?Qi)OOOÈÉÉa×®]oܦºº:òòòj]_OOîîîØ³g†Ž}ûöÁÓÓS˜°”±·çÉç1iâè-aÛζíÞŽÑŠ·lÙ‚€€„††¾ñ^%%% ÄŒ3ê´Ý¬Y³àâ₳gÏâÞ½{øã?Þ¨ŒÉçÉç1iâK`LÂ¥K—°hÑ"lÙ²JJJHHH@BB²²²j½ððpàåË—X±b²²²ê4Ð!888 sçΘ©¹¹¹øûï¿‘[[[˜™™U[?66qqqeÚÚÚ°°ýx<Œ±¦…s€d¯¾9@%%@x¸äÙ'Oj·mzºøRX™© YѤ ~ýúAAAæææX¾|9:uê„‹«ž`tûöíØ»w/Ú¶m+”ÙÙÙaûöí Ñ]ÆXÒP9@%%â³òò€’ ¨(~4‡OµÍÊÌ®_ÿ7عvM\V_wîpÔ5©ŒŸŸ,--ùùùhÛ¶­ð_YUÆŒ#•ÑŽcLÖ I“€'*__ • Œª+«kyUu••---ÉŸ-Zˆ×ÉÚ“'’—³îߊuehØÚññâý•ºsps“^ÙÛ¡I@¥Á(++CGG?®6bŒ1iuPn.0z4pölÕu Åœ™t¡^ª ŽªúYU™¼¼8(%% QQ:B°sõ*ðêUÝû%/ôè!xJ¥gy¾ÿ^2º}[*‡‚½m¨‰ !zñâE•u–,YBêêꤧ§G]ºt¡¯¾úŠŠ‹‹…õ>>>daaA7n”x”åääT¡¬) ¢Ë—/W¹þàÁƒ]§}>þœ:Tëúééé´ÿ~ÊÊʪS;UÉÊÊ¢PXXX…uEEE”@………Ö]¿~¤Ò‡üü|:yò$mß¾ˆˆ(''‡üüüh×®]”––F‡¢çÏŸ×j%%%äççGÁÁÁRé_]={– Tá³Ñ”—Oœ8AkÖ¬‘Éþ33‰ú÷'6@eÍgY]HC×NÕkû–-‰ºtÙH_}Etñ"QVVÕÇûúuÉí¥ÿ~y›–/^¼H7n$777Z½z5µjÕŠšƒ&EFF’®®.íÛ·¯ÚzÑÑÑôüùsJHH óçÏ“‰‰ }óÍ7Âzòöö¦´´4‰GY³fÍjVиqãhÚ´i²¡¡!Í›7OXVPP 'NÔiŸ§N"%%¥Z×'•k×®%WW×:µ?uêTêÓ§½~ýZ({øð! 6Œ ÉÉÉQÇŽiñâÅBùóç“££cÚªLéûµk×®äêêJ—/_¦ëׯ“¶¶6õîÝ›œ)<<œ”••é×_­õ~Ož}Jÿ § Е•‰TUÓHQ‘H$ª¸¾9/‹DDææD“&¥Ñž=DááD%%µ?þ99Dòò’ûOM};Þ²XÎÉÉ¡´´4ŠŽŽ¦ÈÈHÒ××§æ I]€GaРAðõõÅôéÓ«­kbb"üîêêŠO?ýýõ–-[&”kiiA[[»Ê}(Kë÷ÚµÀÁƒÒÙWu¬¬€£G¥¶»;wÂØØXjû«¯}ûöáÔ©S8sæ >øàƒZo÷×_áÈ‘#¸wï444ˆ/e899ÁÒÒ§NB÷îÝñìÙ3\ºt »víªÓ´ µáïï¾}ûJLøºpáBŒ1ßà=áîîŽiÓ¦aÆŒ¸ÝHçðËvx¹n˹¹ÚpußÁô¿€ž=óç}ýë……ÚÂe0ñCr¹  úõõ­Ÿ—'N2ÎÈÐFFFéï@f¦6$g)ÿ]*e55 o_ÀÖV¶¶@¿~€øf¼úUUÀ܈ˆøwý;€“ÓÛõþÖ²ªª*TUU¡­­„„ˆD"4M*ºrå Æõë×c„ ÖGEEAKK ºººÄ3•ËÉý;ÚóçÏa``Ð`ý•˜DFʾj‚9¸~ý:}„²€½½=._¾,Ì•vàÀ´jÕ jjjøå—_`oo M›68p`¥ý¿|ù2þúë/äççÃÑÑ®®®ë¿ùæ´iÓgΜÁ!Cj}\XÝI;(:0ˆŠ’,ï×OœTþ£¬  ~¨ªJ¥y©ÉÉDPTÕïÕ­ÏÎ.Ý[€,ëHäîôì)ý»à¬¬€ˆˆ—ÅtÛ`«É@ÙÙÙpvv†ƒƒ"##áëë ÐÕÕÅܹsˆgŸ:u*Ö¬YèÛ·/ŒŽ;",, ÄåË—ë)¼îÞ½‹3gÎËñññ¸yó&NŸ>]iäãヱcÇ¢{÷îBÙâÅ‹¸pávî܉;wî@SS³Æöµ´´°`Ádee ¯lÚ´ ÁÁÁ˜2eJ¥Û•&ºß½{·Ög;^¾|‰lÚ´I¢¼4™>((H€J•ž%*‰Þ½{£k×®(((€¯¯/„@dýúõèÕ«—D´{÷nˆD"ØÛÛãŸþA||<òòòPRR333èèè %%ÿüó233aeeUi´xñbìܹnnn(..ÆæÍ›±|ùr,_¾\¨£¥¥…‘#Gâøñãɘ4ÇŠŒ?/^H–;;§NåÞ†o555ñÃаþû(.C'ãáÃ[˜2EöÓaXYGŽü»|çŽÌ›d ¬É@òòòXºtiµu.\«2“º|ùå—¸páÎ;‡víÚáöíÛ•þ‘oN<==áéé @<°¤­­-¦M›†áÇ×zãÆÃ7ß|999DGGÃÒÒ{÷îÅ‚ jÜVUUS¦LÁÞ½{…¨¨¨‡ÂÚµkë÷¤ª ôêÕK¢ÜÒÒnnn˜>}:6mÚkkk¼÷Þ{9r$Z·n-QWEE/^¥>úþþþÎÄTeÞ¼yøùçŸacc#ñüŽ=ŠiÓ¦áóÏ?¯²ïß}÷>|ˆŽ;Nœ8|öÙgÁæ AƒàããS«þ°ú“Ö8@wîŠÇú)køpñíï q[ùÛF^ÐѬ­ÛÀÚºaæ+?ÿß Öô4™HEEE8ëS•… J,:C‡•a¯ê`ùr`Ö,Ù·S‡óã3g΄‚‚vîÜY§&úôé#\Z455EïÞ½qãÆZoïåå…Í›7ãÖ­[èÓ§~ÿýwäççcüøñuêGM$.W•õË/¿àòåËØ³gnÞ¼‰àóÏ?ÇÊ•+%m‰³D½zõÂáÇ¥ÚÏÊœ={¦¦¦¸pá.üo¢¢¬¬,äååáÑ£Gxï½÷$ú"j6×ößUW®C‡ŠG.kÂàСæ1ØáÛ¢ÜÿExðÈÏožhSŧ·…¾¾øñ–Ø´iΟ?ÐÐÐ7Nô655ERùg«Ñ¥Kôïß{öìAŸ>}°oß>xxx@MMíúQ^vv6Z¶lYåú>ø@H¨ÎÊʪU«°bÅ L:µÂ™ RÊÊÊ(..–j?+‘H„‡J”ÏŸ?_"w tttPTT„ÜÜ\©Cö¯7ͺpA<ØÞ¿ù.bžžÀÎ@™tÅfKšsÕDW02Oˆ ˆ“ÌÃÃÞ½eÞ4k ü‘baÙ²e8~ü8Úµk÷Æû»råŠp™¦¶fÍš???<~üðòòzã~”§££Sa.8@œ_ž††fÏž’’„ÖavDeeeäU6Ãâ233ƒºº:6mÚTáQþ2n\\ÔÕÕ9ø‘±ÀÀ@dddÔkÛS§€aÃ*?‹»wsðS*99¹Aó4Ë_ã< ¦…?VLBll,Ƈµk×ÂÑѱ^û(,,~?{ö,ž={VçüˆÑ£GCEEcÇŽ…Ä(ßÒbff†gÏžIô6lØ€¹sç">>^(ËÊʺuë ¡¡¾}ûÖº ggg\¹rEÿ»øŸþÁÍ›7߸ï£FÂýû÷qàÀ‰ò“'O"½Üõ“ÈÈÈfŸÛÖÆŒS¯èŸ~ÆŒ_^)kõjà¿ÿ•R皈ÚÎ&-5m1 +V¬@rr2víÚ á±eË–ZïcÚ´i°··‡¥¥%† ‚E‹Õy:%%%L›6 wïÞŬZäF-Y²ýúõÃÑ£GñÏ?ÿ _¿~ððð¨v›þýû£°°Pbü@œ3säÈÁÈÈVVVÐÖÖÆü£GÖú–~pssCLL ÌÌÌ`aa‘#GBII©ÖÛWÅÊÊ [·nÅܹsѽ{w 0úúú˜2eJ…€Îß߃ zã6™ôíÚLž ‰±rD"`Ó&`ÕªÆë+ŸÄPÓ"""jìN¼­J“ª«K®ööö†©©)¼½½¦S2^i¾Ž©©)LMMyyyaÜœÐÐPèéé ƒ!^ºt fff¸zõ*^½z…~ýúI$äV&%%áááèß¿¿Dyjj*îÝ»[[[‰ !''7nÜ€ŸTY¿544$n?¯ÌŒ3ðôéSüõ×_å………¸yó&bbb••…öíÛÃÖÖVâ2Ò“'Ož={ e/^¼ÀË—/ñþûï eùùù8uêáêêŠØØXˆD"XXXîܹ---‰dê›7oÂÐÐmÛ¶Ê.]º„®]»JœeHJJÂ7cccØÛÛC]]]XçÎôéÓ÷ïßÚkØ´i¬ÍÆV× õë%K$ËäåÅóPÕ0†k³Õ9@€x ¦²Wï[´'¨7å{ `ee…„„„ÆîŠÌqTæ57111èÒ¥ öíÛWå ‹ïª’’ØÙÙ¡C‡øñÇ´íæùûû×z •+¯¿–,ST_;VFlâããqëÖ­» F$žõõëËž<:thæEs €økÖLLL°eËxyy5Út²2{öl$&&bÛ¶mÝ•f¡69@DÀ¼yƒUUà×_9ø©ICç‰DâQ¦ËâË`M@¬Ù›1c233+ ˆø®Ûµkž>}Zí\v¬áÓ¦[·J–kjЧ¶àºßNœÝtqÄcR’’"ÜíW^A0~|ÅùŽut€‹‡è`PPP€ÔÔÔm“¡›.€cL ª(798yR²¼uk 8(“/ÏjÐÐã|¨)ãˆI(**‚©©)®\¹"³6vïÞ?ü°Öõª(((çΓV׫·Êr€23Aƒ€ò¹à&&@HЭ[v° hè èÚUr ’/€ääí“€X111ÈÍÍ•Ùþ322$¬INNbbb$ÊÒÓÓqéÒ%LŸ>¿üò‹´»ÈØKIÏÞ"Ynn\¾,y{5{{)+]ºH–ñY ¦ &ÅÅÅÈ/?”­ 3ÍâVMön(›ô5ÅÊ ¸t (3¼«ƒÆÈø2XSÅ“¡¾%6½x£¯^ɼnêêØû¿A kÃÃ×.]ªP~üøqôíÛIII˜5kΞ=‹ÂÂBØÚÚbÏž=èܹs­öáÂxzzâêÕ«000Êׯ_ààà £4—Ý@.¥±¦+?HKRWú³ôwyyñ¤–mÚˆúú²™[+00NNNÈÌl…€§O%×ÛØgÎ|S^ý%''7è8@¥zõþw™ ¦ ·Dt^®gf6v7*ðõõEf™~}ñÅHHH@×®]ˆçìÊÏÏÇéÓ§¡££ 0‘‘‘PUU­qÿÈÍÍÅÁƒ±xñbâü¶oߎE‹ÉæI±·NI ‘QySÙÏòeu™oVA00 ŠJeËê:Øð˜1cðààêúï â¥\\Äž–¤›ÕCcä|¨©âˆUËÌÌLø}ïÞ½¸uënÞ¼ uuu\¿~!!!¸pá\\\ˆœŒŒpìØ1L:µÆý+**â“O>Á¾}û„èÂ… HNNÆäÉ“eòœXÃHO~ü±vLf¦xÀ†PT$PÊ)婨††£òÁ’††¸þ?ÿˆžË'ÈŽ ;&Î%aï¦òÐÇâ [E¥qúäƒ V+ׯ_ǼyóàïïŽÿËÞ ‡²²2ììì„zmÚ´¹¹9ÂÃÃk½ï™3gbíÚµ¸téú÷ï}ûöá£>âüÞq‘‘â‰>ßUyyÀ³gâGu45ÅP\\ ²²´PökuÒ$àÀÉ»ˆXý5ô\`¥Z¶ŒçÏÅËÅÅ@Xað®ãå[bAÛ¶øH__æíhÊË×y›ÄÄDŒ=Ë—/Ç2ÃÕ***¢¸¸………P)ó¯PNNk½ <{öìA×®]ñ믿6øXìݦ©)þ#Õ²¥8Ǧìï99â¤ä¸8ñÏW¯Ä—ܤåõkq°‡(JÚ IDATp ¾ÞË Ø±C6ùFÍUcåâ³@¥ ¾ Æл ·„‰Š LÞÂó©EEE7núôéƒ+VH¬ëÙ³'ŠŠŠpéÒ% :ððáCÄÆÆJÌ^³fÍÂøñãÑ¡CtíÚUb6uÖô)(ˆ'¬*ˆ©îwmmq¢sm â`¨ôQ•ý=-­®ÏbŒðÛ_ˆg{gÒÕX9@€8úôé—9èÝÇ«ÖâÅ‹qãÆ l޼ǎÊûõë‡=zÀÍÍ óæÍÃóçÏ¡­­õë×£k×®=ztÚ2dtuu±fÍìØ±£Æú—/_FVV’““!''‡€€Ö9ðb²£­ V»@FS³áú¥  ¾ ½¦[Ñssk’âãÅg˜Êúê+ñlï¬iáD覇 &A$ÁÁÁ-[¶ ´ÐÚÚGŽ‘¨§££SSSøùùaÅŠظq#òòòàää„ 6@¡š¤cccôéÓG¢L^^+V¬À‰'0qâD‰uzzz°··—(Û³gbbb ®®Žììl|ûí·pvvæè-bn.yëð»FUèÐAü¨Nzº8zø0êêZ4ˆ¿Ve¥±r€€Šн{âÄ}‘¨Á»Â¤DDÔP÷^¼{|}}%~VÆÛÛ¦¦¦ðöön˜N1öÀ¦M›P~ˆ&ÌßßNNN¦Ã`Òßh9@€xh„²—F=:uj”®ÈLBB¬¬¬šÅ ³œžÇcRPÙ\`Lº3¨xèöíÆé“€cŒ±Zà< ¦… Æ“‚²s1Ùh¬¹ÀJõê%¹Ìл Æ“‚ÀÀ@ddd4v7š´äääF#ŒÏ5-1Ƙpì5vP—.’Sš¼|)X“½›8bŒ1ÆjAAøß<Ð> ôîâˆ1Ƥ€s€d¯±s€¾¬)áˆI())ÁÔ©S!³6Μ9ƒÿüç?µ® //¯J×½xñÏjš­²œ«W¯búôéˆŽŽ–(OKKÃÅ‹qìØ1„„„ ¹Ü´Þ6lÀ¡C‡êÔVeˆ˜>}:&Mš„´´4# ˜û ‡ßå‘ßqœ${p"tSÂÐ[‚Š%y%²T? dII <ˆ—/_Êì¹Þ¿§Nªuý˜˜üøãÊóòò0a¬[·®ÖûJMMŸqãСC˜šš å›6m‚±±1† †åË—ÃÉÉ ­[·Æ”)S„:þù'®^½Z붪²wï^L˜0úúúèÔ©ˆëׯ‡§§'LLL`jjŠììl²ºã Ùkì €¡›³ý-µ( /6¿y;-¬[ ÷µÞ2oGÖFމ . '']Ë_”¯ÆÚµk¡««‹¥K— eçÏŸÇ‚ °uëVÌœ9ÊÊÊÈÏÏÇùóçñË/¿H½ï¿üò æÍ›‡åË—K”­\¹žžžPáìTm¸ººâ“O>Á‚ ¤¨1Æ*êÙS<ýEé ‰çƒSSkÜ~±ºãˆUkÆ  «P¾lÙ2˜››ƒˆ°mÛ6üöÛoÂ\`Ë–-ƒJ-g¶¿ví¾ÿþ{lß¾ªªªBùï¿ÿŽ*Ïîìܹ˜:uj­ŸKff&vìØŸ~ú òe¦¿rå ´µµ1gΈþ7±²²2†ŽáÇWØÏO?ý???ÈÉÉaÆŒ>|¸°ÝÂ… áææ†þýû õ¿üòKôîÝÇÇW_}…   äååáÑ£Gèׯž={†[·nAWWW®\Á€ðÁTh7==kÖ¬Áµk× ¤¤„áÇcþüù“û÷D®Ú´iƒË—/Wº&;)))ÐÒÒªv<öfs.°Ršš€™%^.)ÂÂkëFë«'¾ƪeff+++áñôéSœ>}€O?ý>>>èÛ·/ÜÜܰÿÿÙ;ïð(ª¶ß[Ò{ $’@Hè% ]”–E׆ú*ŠRTøh* ,4C/J ( H‹ÔÐIƒR6eË÷Ç&›ÝMÛ„Ýìl˜ûºreçÌÌ™'›dç7çüÎóüø#ýúõ3¹ÿæÍ›³~ýz6oÞlÐ>kÖ,ƒ»1õë×§Q£F¢©2öíÛ‡““ 0hê©§ÈÈÈ`þüùä—ö6"<<œï¾ûŽÞ½{Â!C8yò¤nÿƹvíšÁ9;vìàܹs4mÚ¹\Ž¿¿?aaaÑ¢E 6lHXX Ë(S~ÿþ}Ú·oÏåË—yóÍ7éß¿?‹-â³Ï>38®^½z<ùä“lß¾Ýä÷EÄ<ˆ Ë#ˆÓ`µñQE Hì$H,¯G«z!C†è^?~œiÓ¦Aƒ ¸ví+W®dãÆ 6 €~ýúѪU+vîÜÉÀ+íßÓӓ矞U«V1zôhΟ?ÏéÓ§Ù°aC•b­Œ“'OÒ¹sgììì Ú}ôQÞ~ûmf͚ŧŸ~JóæÍ騱#¯¿þ:]»v58vàÀlܸQ·]ìgêܹ³I1Œ5ŠwÞy‡§Ÿ~ÚÀ_4~üx ¤gÆS` ,ÀÞÞž;wêF›\\\˜>}z)Ô½{wöïßoR<"æcøðáÖ¡Ö#hпýV²-®³MD$š,hB“M¬F¹Ü»waÆ1}útÝÏ©S§Ðh4<þøãºãZ¶l‰¯¯/'Ož4IL˜0®]»rõêUš6mÊêÕ«yúé§iܸ±Y†äädËÜ·xñb¦NÊ–-[8wîýõ?þø#ï¾û._ýµî8c“k`` EWÌsâÄ $ /¼ð‚®-==ôôtÒÒÒ ¦ŠJÎ""ÖBªˆH¤R >|8]ºt10îfeeáè舻»»Áñ>>>dee™Ü—.] cÕªUÌž=›Ÿ~ú‰Õ«W›-þb”J%...åî÷óócÒ¤Iºí3f°páBfÍš¥›ò3¦,¿‡¦ØiF²²²hÑ¢Ï?ÿ¼Aû¤I“p6r_ÚÙÙQXXhöD*FôY!x€ ´:^ëª`Ö^D€ˆ¿.‘Jyï½÷HMM%<<\7ýЬY3 …æÞ½{\¼x‘¦M›Vé'N$<<œM›6áèèhòèQUðññ!66¶T{Ym õ') 222L¾†··7÷ï߯vŒåÑ¢E 233û,M›6eÒ¤I4kÖŒß~û­Ìc5 áááÌž=›ÐÐPž|òI^xáÖ®][ÃQ ‹œœ5jıcǪu¾R©dæÌ™U’*•Šýû÷3kÖ,&MšÄÌ™3¹páB¥ç-X° J%76nÜH×®]ËÜ·wïÞR¢EDª‚è²ù$AAAlذٳg³qãF† VîyéééUÊVšM\\œA[ZZgΜá•W^a̘1ôêÕËäþDDô=@–Gh Ðú€ô+æˆÈv¨U#@úü÷¿ÿ¥}ûöôíÛ·ÌýÉÉÉ8;;ëÚÜÜÜtíŬY³†™3g|ésêÔ)³Ä›–¶—Û·?³ø×Ý»«ª—B¡(U<´"ˆŽŽ.·.Wnnn©¶¶mÛrýúuvíÚÅòå˹|ù2íÛ·gþüùUе:ôë×ñãÇ jX½6aüÿ"n‹ÛµmÛÐ=Ó@ !>S¶wîÜÉÌ™3yüñÇY¸pa¥…¥k µ²ØÔ©SÙ¶m‡¢^½zeC£FP(8::°råJÖ®]Ë_ýÔl-°ë×'¿øú0w÷.tèPþHSvv6nnn̘1ƒ;w…›› ,àµ×^àñÇçöíÛ¥Î=~ü8~~~´mÛ–¹sç2pà@îÞ½K·nÝxóÍ7 çâÅ‹x{{³xñb^Ò/¤cÄo¼ÁÞ½{¹qãF¹ÇL›6£Grøða"""˜:u*§NÂÍÍMwÌÇÌ7øõ×_ùá‡øä“OHHH(ÕW·nÝèÕ«Wˆ®‡±˜ˆ%R-°b “::jжZF¬f£¨ÕjÞyçöíÛÇŸþY®ø¨W¯2™Ì`J$66– ÔD¨‚gãÆ¼÷Þ{=z”0aÂ._¾ ÀŠ+غu+[·n%""WWWš6mª{¿cccÉÉÉ´ž ˜˜~þùgf̘Á_ýEÏž=;v,111e^[£ÑðÇð裚oß¾}ILLä×_Õµðí·ßÒ¿ÿê¾ ""&#z€,=@~~àë[²—ÑÑÖ‹GÄtjÍ£ŠJ¥bذa¨Õj:„««k©cÞxã zõêÅÈ‘#qrrbÀ€¬^½šùóç“››ËæÍ›™={¶¢oï~Èåž¿ŽƒCC“Ž[ºt)}úô K—.ìÞ½›Ÿþ™Ï?ÿœ-Z莛6m¹¹¹¬_¿©´|=½zõj:tè@ûöíñðð`óæÍ¼ÿþû¥Ž2e ÷îÝcÆŒ&ÿ\...Œ=šU«Vñú믰uëV4 #FŒ0¹‘ê"z€,=@ 5BëvFEž½TD Ô”••ŶmÛ ¦@š5kFt‘ß¶m^^^Œ9€E‹1`À6lØ@VVÆ cÈ!5Û´iC÷îÝY¹r%|ð`É’%UîGD¤:ˆ Ë#Dˆ%1l•Zåª.¢ø©€ÔÔTΞ=Ë#<@ff&C† áÕW_­ÐÈ\ÑÑÑÄÆÆÒ¹sg@;}ùÖ[o±páBöíÛW-ñSÌĉùùçŸY¾|9={ö¤iÓ¦ÕîKD¤*ˆ Ë#DˆÈVUDÊä§Ÿ~B*•’ÀÒ¥K b̘1¼üòˤ¥¥ñÈ#°~ýzÝ9O>ù$uëÖ-³¿Å‹3lØ0®\¹ÂòåËéÒ¥ ƒ `îܹ,_¾œÿýïDEE¥÷éѧOBCCMŽ{ĈLž<™… òË/¿Tz|dd$ùùùdddpóæMöîÝK`` -[¶4ùš""P³ œ‹ÚE; R;){‰öµ½‰D׎¤ÆBª„ê Õf….ZûAJ ÄÇCCÓ,—"VB@"Èd2zõê…½½=“&M"==ž={¡óÒЪU+V­2Ì)ÔªU+êÖ­K=J­ÀÓh4|òÉ'2`ÀæÎ‹D¢ýt®[·.=ö'Nœ(•A:$$¤\¬[mVŒ££#Ó¦MãÀ¥ü\þþþtëÖÍ méÒ¥¤¦¦âëëKRRóçÏç¹çžˆ ɈÌàæÔ›Ü?qߤã%r#QTüÚ¾H8é¿¶—T,¨Š^—Õ&s•áÜ—Ö.ØÕ±³ð» <¤Rm]°¿ÿ.i‹ŠªšRe©(L+D™®Ô}W¦é½ÎPâñ¨u‡ÖEæb£kìF­Ìd.j2Pm%..ŽÀÀ@NŸ>­[&Rûó™—¬3YÜšv‹´ß…Ÿ°ÓÞß×6®¸´vÁ¥ .­]pnéŒÌùÁoÚBõü÷¿°r…7”¸£äÝ× ùÏsFb&½eš²Dä·¥+Ñ(M»Ë\eø óÁwŒ/^Ox™}”ïaÊ$މˆˆˆ˜ÈÈHžxâ êÔ©c¶>×ÜúøI“ÀFU îv7´?JÄšD*Á1Ø—6.âÈ)Ô ‰Ìô;xJJ §N²ø4˜*WEaR!…©…e =SüzD²’‘¨J:Y çWZ ¶l‰á‰$†'âà€ßh?|ÇøâÜ̹ò“E ˆEqssãwÞ©0)¥ˆHmÀœ ‚»Üžu›»«î–=2 —–.hT4…Ôj4…4Ô…j4ÚvZªI£Ö ¸®@q]AÊ–]»ÔAª:+)*Geöó  e¦’¤B îè¾ ïm'é½¾W€*[Uy‡FXÃn•—OÌÜbæÆàÞÅß1¾Ô{¾vÞß4duˆEñôôdÑ¢EÖCDÄ&Pf(‰ý2–„Å ¨r˾ {=åEð¼`Ü©|åje©¸ýA÷¦’s!‡Ü‹¹åÆ]ê|5ÙQÙdGe´Ë=ä%ShEâÈ¥µ‹á]…©ZÑR¦°I29ê|µÉqY©ƒ¹·;/;íwo;ä^r]›ò¾’¤õIäÇç—yþý÷¹â>7Þ½AuðãKþuØÕ2'¼ˆˆˆˆxZ¡&~I<±ócQ¦+Ë<Æ­“Áó‚ñêãer¿™‰L‚Ô±f2žhÔònæ‘s>‡ìóÙä\È!ç|Šk 4*ÓG£”™J2f’yÔ0­€Ô_вŽÇTG “ MöÍÔ$d## ;î#§E';|šÈ‘{•a£×&uªüwüE03H\›HJD ªœÒbS] &9"™äˆdìêÚQï…zøñí£˜êÅQ‰ˆˆˆ˜êx€4J ‰?&rû³Ûä'”ýdïÜÌ™Æsã3ÌÇ\¡Z ‰T‚SˆN!NÔR’C§&÷r®VÏÑ £ò~æòH¾›Ì•»WèNws‡n€D.ÁÎÇ»ºve —rFi^~CÆ/¿–Œ¸üßhxë-3Æ%•àõ¤^Oz¡Z®"ù·dî­½GÆ¡Œ2§; S IX’@Â’\Zºà;ÆßÿøâРì)Ƈ Q‰ˆˆˆ˜*y€4ü[2·¦ß"÷jn™‡84p ÑÌFøó«’QXˆH¥¸¶wŵ½aFeº²”(ʹƒ2³ìQ°:Ô©¶ø‘ÚK±«g‡½¯=ö¾öØù–¼6Þ¶«cW-SOX{ø¥¤³E"Ê\eø½ì‡ßË~äÇåsï§{$®M$7ºì¿§œK9Üüè&·¦Ý³'~cü´KêͰ:ÏVˆˆˆH ’¾?›So’u*«Ìýr/9ASƒhðf“¦El¹—Ïžžxö4,«FÓF¹Ñ¹¨ }îYëgV†(€D P*•üôÓOôíÛÿ½¶J¥"%%…:uê˜ä£8sæ IIIôëgZÙëׯsæÌ]1Ü%&&†ÈÈHžyæ|}} öåååqÿþ}|||t ‹ÙµkþþþfÉ‹”‘‘Á–-[P«Õ¼üòËÈårRRRزe ööö<ûì³lß¾çŸÞ¤¢°™™™lݺ•Î;Ó¢E‹Žïa¢2PÖ©,nN½Iúþô2÷Ëœe4x§"÷|¸?šq t¤Î€’éDRCÆ… 23ñ ñÅÞ×™›°F/ŒÐÅ‹ TBM–‡sïìŽ{gwš|Ý„´]i$®M$uW*šÂÒSdªl‰kI\“ˆc #¾£}‘õÖ{jIj÷ã…H•ÉËËcܸq\ºt©ÚçK$/^lò9—.]¢G8::âç燓“£F"=½ìE1›7ofÞ¼y&_çÏ?ÿäÝwß-sßСCùøãMî+??ŸÁƒsøðañsàÀ:uê„‹‹ ¾¾¾8::Ò±cGV¯^­;föìÙlÚ´Éäk•Ço¿ýFÆ Y½z5áááäåå±jÕ*‚‚‚øùçŸY½z5±±±Œ7ÎäU\¾|™~ýú‘–&ü¤{B¢¼Z`¹Ws¹8ò"§;Ÿ.SüHäêO¬O—ë]žüЋŸòÈ%(ê)8Wp§'Á‰hРd;?._¶N,R{)u‡Ô¥õ–Öt¿ÛÐ%¡¸u*ß›GÌœn>z“Îùk0Rë!þ§ „©S§²bÅ ‹_§S§NìÛ·ÏbýÛÛÛó믿Vit#!!6mÚðÍ7ßDdd$¯¾ú*“'O&<<Üb±LŸ>ÇsôèÑ*NýꫯÈÈÈ`ùò庶ãÇÓ¿^~ùe-ZD@@ÑÑÑl߾͛7óꫯš5öððp¦L™Â'Ÿ|¢kûá‡X¸p!“&M0¨«f*sçÎåàÁƒL›6o¿ýÖlñšuže¦Õ}Êû%ßËjSÝW¡ÌT"‘Kphà€CCÝwûö84p0kIcP~B>·?»Mâ‰åæò©7²?oŒSˆ“Ùâ¨Íµ˜>aaP²}ö,´ic½xìêØÑàÍ4x³¹—sI\›È½Ÿî•»¤ÞCíQÃZQ …BQ#•¤³²Êö£ÑhرcÇŽ£^½zŒ7OOí<}DD÷ï—®E4räHœQ«ÕØÛÛ““æM›0`þù''Ož¤Aƒ¼òÊ+¸¸¸ðÔSOT€5jûöíãàÁƒ&ÿ\çÏŸçÂ… ¼ð íÿþû/7oÞ,U¬˜víÚZ¥´ï999,\¸%K–àäTrãÚ¼y3uëÖeùòåºiÀÀ@ž~úi²³³Kõsùòe¶mÛ†‹‹ £GÖ½¿?ÿü3=zô ((H×AË–-iÞ¼9[¶láÀ4kÖŒ5kÖмys®_¿ÎñãÇéÛ·/kÖ¬¡]»v¥¦ß@û»Ýºu+ÿüó... 4ˆ6zŸÐR©”¯¿þš>}úðé§Ÿše*T­Pˆ’²„JyF¿­¬aüAî)/[o7pÀ®nÕ’Ê)Ó•ÄÎ%~Iúè#@;RѺuk~ùåÆŽk0ísáºuëÆêÕ«ñò*ÿCýÃ?ÔÝHÿþûoºwïÎŽ;}z)3yß¾}9tèPµ:_MaFéU(¶†:Oâ¦ÅME¹ÇH¤ËV†;î¥ö;7w¦ñç¶‘ËGÈÔT-°ÁV>2W~cüðãGü™x®?}ÝÚ!Õ¢óæÍ«°ê¼¹05Km§Nt¯[´hAóæÍùçŸ;v¬®=##ƒ!C†0aÂFUa;—˜êºu놯¯/'Ož,%€îܹC¿~ýxúé§yíµ×Lе˜‰'òÎ;ïðõ×_ãììÌêÕ«yì±ÇJ‚<(‰‰‰4jÔ¨T»»»;—.]bÓ¦MlÚ´‰]»v±xñbüýýY¶l™ÁÏÚ¡CÜݵ7J™LF›6m8zô¨É¨ºìÞ½›æÍ›óÃ?èÚòòò¸pájµ©´d]DPPçÏŸ·h|X·½uëVîÝ»÷`£õ)­[·Ž3gÎèÚ X·n]©c¯\¹Bhhhµ¯%uÔféªø1™³ §P'<ŸðÄ÷?¾~Hè’PZG´æ‘¡[B7zæ÷$çÛšG5§ñœÆ¢ø±)))ÑWÅ}@#â©H™ 6ŒŽ;rçÎnܸÁ¬Y³t¹}^ýu$ /¾ø¢Á9¿üòK¹ùúöíK‹-ˆ%;;›ï¾ûŽÀ¢1á-[¶‘‘ÁºuëJ݈—/_®^¦Ð¢E zöìÉ_ýEDDD¥Ç8””.^¼Hll,‡bÈ!L™2¥ÜsúöíˬY³HHH ^Ö³€€f̘ÁÔ©SiÒ¤ ‰„«W¯Ò¦M6nÜhòÏZCóóÏ?ÏÑ£GÉÌÌÄß߇/`8~üxΞ=K·nÝèСr¹œ¨¨(‚ƒƒ=z´î8…BÁîÝ»Y»ví_óa@"—ð„*?Pä°ˆÈVˆNNNDFF‘#GHOO§Gyb~ûí7”ÊÒ¾ˆ&Mš°sçNš5kf°oÏž=¤§§SXXH÷îÝiذ¡nߤI“8p`™ñTd`~íµ×Ê,kNbb¢.žbú÷ï_ª¼ÃôéÓÉÏ7LV¿~ýr¯ Ð¥KZµjŲe˘;w®®ýóÏ?gÊ”)üóÏ?ÄÇÇ£ÑhhÚ´)]»v5˜[¾|y)ÑŒ3 ¦ DBB[·n%00Þ½{sòäItÇüñÇ´jÕJ·]ü»Óÿ]…„„©›2H$,_¾œ>ø€Ó§O“™™IÓ¦MéÞݰÀäÚµkquu5¹ÌˆˆˆH ­[kË_LÞ¾ àéYái"5ŒD£Ñ3A‡(^•UÑê¬É“'Ó¨Q#&Ož\3AÙqqqrúôi³Ô¾ ;vì`øðáüûï¿4oÞÜÚᘕ””š7oÎÌ™3yóÍ7«ÕÇÞ½{Y´h{÷î5stÂ¥²Z`"Ž­x€Ú¶ýE”‡A¯^V Çd «RrX[Ŷ'çED¬Ä³Ï>ËèÑ£ J%P*ÆèÒ%((°N,"e#XtáÂÝPçÊ•+™3g'NœàèÑ£5’1WDDD¤*ˆ ËcK ooЯ˜SX¨A"ÂA°ÈÑÑ‘üü|òòòøçŸtËt5A%r!0|øð*%»©:¶ähßÞp[œ‚@ƒæÝwßÕåniݺ5 ÄÇÇãççgíðDDDDDD*Dô Á  ²lÙ2žzê)Ö¯_@TTóçÏÇÎÎÎÊщˆˆˆ"z€,-y€ ´W‚ A-ƒÏÈÈàØ±côíÛ™LÆ!C ö0ÀJ‘‰ˆˆˆTLdd$O<ñ„8 fARRR8uê”ÍLƒ  ÿµN¦RX_|áŠZmZG[GP૯¾b„ Œ3†qãÆbí*%++ë¡Èš)"b*éééÖ¡ÆóY[ójÍÐŃV™™pë4nlݸÊãðaX´Èw÷‡c°APÈÓÓ“pëÖ-~üñGúôéC£Fxå•W1bÎÎÎÖ±îîî,[¶ŒeË–Y;£Ñ@~~饰R)¸ºV½?užM^’I È\d‚™ØîÝ»·µC±:íÚAddÉvT”pÐÖ­ÖŽ ft-0µZÍþýûùá‡8xð ƒæ•W^¡k×®5r}Sj‰<8‰‰‰µÚØ®VÊðñÇÚ‚ˆÆ|ø!|ñEÕúÌ<–ÉÙgAï¿7x~0SË<¾¶¿ÇB@¬fyl©X1ï¿_]²=c|ö™õ⩈€ˆw÷)dfVñCÉȳbÙH¥Rž~úiÖ¯_Ott4mÚ´aÒ¤I´jÕŠ~øÁÚቘ‰­µø±ãøqèØÞ|³´ø‘Jáõ×ᣪ֧F©áÚ¤kâÇ¥¥ ï”{Nm~…‚˜ÈòØR ble%Ø©SZñó0!h¤··7o½õgÏžeݺu( k‡$b&&NœhíÌNJ ŒÝ»—½ò£cGøûoøî;ðòªZßñÿOö¹lƒ¶Ð¡Hì$åžSßc¡!æ²<¶æÛY ö0># V]¹r…Å‹Ú ÕS§Nåý÷ßçÞ½{tèÐA¬T-"HÔjøö[hÚV¯Öú~ôñòÒN‡8;W½ÿü„|nzÛ ÍoŒž==«´ˆˆˆÅhÑJ¶ãâJLÑBB@âÇ$%%€+Vðûï¿#—Ë=z´•#17µeÝ?ÿhEͤI`¼J"W_…«WaâDíôWu¸>ù:ªì’R0r/9M6©ô¼Úò 1å±µ<@r9´jeØ&´i°ë×áâEýµµB©Q+€bbbèÑ£›7ofúôé|ñÅ$$$““cåèD̉­ûSRSµ^ž®]áôéÒûÛ·‡cÇ`Õ*¨ûé5Òö¦‘¼9Ù -x^0v>•'µõ÷Ø=@–Ç=@ |ñǃTúp<0 VÕ«WèèhîÝ»GTT‹æ îß¿Ïýû÷­ˆ9±UŠZ ߯îZ¹²ôt—§',] 'OjÅÑ]+O͵7¯´¹wvÇÿ5“ηÕ÷Ø–=@–Ç=@ üš`ÆH&‹µN 5Œ`Ðøñãùä“OhÞ¼9Ç' €S§NQPP .ç±:§NiEÍ„ ¥çó%xùe¸rÞxd²¿^ì¼X7JŒÿ™„¦ß6E"-ßø,""" „<tïžvAF1 ÈdqÖ ¨¬jÓ¦ —.]b÷îݬY³™LÆ–-[HÄýÚ„-ùSÒÒ´ž.]´#;Æ´kýkÖ@½z湦⚂Ø/ ŸÈ¼Ñ×ö¦gO´¥÷ØV=@–Ç=@ ý\пm]¾¬MŠ*¶o׎fÓ®]!I®õªA+€8À¿ÿþKëÖ­‘=B·oßžîÝ»[92sc þFëáiÖL»t]mäôð€Å‹µ G5ﵯ¾qu~Éíýíi4»Q•ú°…÷ØÖ=@–ÇV=@nn\²­TÂ… Ö‹Gã†~ýò¬ˆ¬ÊÊÊbÞ¼yÔ¯_Ÿnݺ1}út8@^ÞÃóËyXº?åÌèÖ ^{M›ßǘÿü¢£áí·Í3Ý¥OÒÆ$Ò÷.) ù:¹{Õ² ý=® ˆ Ëc« æ4Xv68`ØöÌ3šª+€Ì‘#GHMMå믿ÆÙÙ™çŸOO1߉HÍžÿý/tê¤ÍÛcLëÖÚâëÖ%liª,7Þ½aÐæõ¤õž7ÓÜšˆˆH!D´gáT\h(4múðLã ºhB¡`Ïž=üöÛoDFFÒ§OFŽiÒ¹‰‰‰dggWZM>..Ž„„ƒ6OOOš7o^í¸Eª†ÐêTi4ZÏ”)œ\z¿››¶–Ï[ois|XŠ[3n‘§äÓIê %tYhµúÚ{\kY[¬VŒW‚O l8¬…`G€V®\‰kÖ¬aÈ!\¿~õë×3tèÐ ÏS*•ܸqƒ±cDztéÒJ¯³lÙ2Èĉu_K–,1×!bBò§DEi=<¯¼R¶øyáíê®wßµ¬øÉŽÊ&a‰¡0ø0ç¦ÎÕêOHïqm¥¦=@©……5v-¡`« (=ôï¿¥SgÔ$……°k—aÛÃ&€û¨ÒµkWÆŒÃþýû‰ˆˆ@*•òÌ3ÏàääTáyû÷ïgþüù\¿~ÝäQœáÇóí·ßš#l‘j JF|ò‰¶L…JUzË–Úœ>OÿüsÔzkœ?ΡC‡ ¾ô·|[?GMM^ÿß¡mÛCŒ«ÿÔS²äHøñÇC|ðØÙÕL|û·íçæG7uÛQDá3Ìïg¼¨ÿâ÷X¿ïÚºššÊ£%5U9?O­æ›;øøÖ-º9ƒ÷‘#<»nßÄÇs.; ”ºcþòÇÜÒ[+¤÷ÃÜÛj5÷ìáJb")……¤+•ì:p€l• …ZM¾ZÍÈH4&ögí:u ··m³N< lÛ%ŸwWiÒä‹-âØ±c÷ÀÚŒ`§À>ùä¾ùæ{ì1ž|òIÞ~ûmÚµkgv4iÒ$Þxã ìíí9wö2™Œ©S§šõ:"å³uëÖ¢ÉÌ„3`Ù²²§»š7‡%KàÉ'µÃÄ5Éïۖ:I YTý1òB†Ñ—/óÛêÕ¸ŠäÒ%f62dr¶çYôa›,lBÀûÕê/¥°Á.pÔŠÉù$ ½Ù}É‹n<úm2Ð &ý6ƒsÊ8¯¼¾ ΑHp‘Jñwp ¾½=õõ¾»™;iS%h€sÙÙ,òñü™‘AVY ¼d \] pt$BÏ¡ï •r«kWü­ñG[Ch€Ð'¸¡PTz¬ÐI$:d¯”që’ Ú/'d¼4T¦LÆ_nr¹Á¶»L†‡\Žü.¦MƒyóJ¶GŒ€µ¯ {(²Ç vÈÏÏ´´4¾ÿþ{®]»†§§'Ï<ó O>ùäõ{ãÆ <<<¨[T–[­V#Õ›øŒÅ××÷®!",nÝ‚;á—_àøñ²6 ¾ùª§5RÃÕÿ^5?.m\høNÃjõw%7—çÏ[ý†¡A;Ò¡às–«Lf ˆüííK‰¤úöö8?€Pº¦Pp0=ééDfdRÅ•[­\\èíéI//zyzâ)—£Ôh9q‚˜¢©¯|µš¯ââXؤIµã:edXýo¹º¨42•J2‹Ë¤4+Ù§VÝ­zŸNR)r9r¹N¹Ëåx½v7:¦øuÄ9Pòwý°­þ*F°(>>žN:ѰaCzõêÅ­[·1bÿûßÿ˜>}z¹ç]¿~#GŽpéÒ%Ö¬YC¯^½hܸ1½zõbìØ±|þùçtîÜ™~ýúÂùóç ·Ùe–¶Š¹sÔ¨TÚâ~;wj¿*Ö ÕNwõík¶ËW‹øÅñäœÏ)i@ÓM‘È«þ¤w(#ƒ¡.®_—*- l0wŠ%ÉV©¸š›ËÕÜŠëyÈ储¬Ñ${{rÒÓÉrtäϬ,fdp0=¸*|jìèH//z{yÑÛÓß2Fuä ðƵkº¶oïÜaj` uª9b.tÖF(• Pàê鉓TŠ ­ÀÐ}ê¢×Â“ÜæC¡V£(( ±  z|h$#C’+gN ŒgµÂÉ¡°‚-̯P¬Z¸p!Ï>û,ßÿ½®íâÅ‹tèÐ÷ßGGÇ2ÏËÎÎæöíÛºša·oߦcÇŽºýï½÷azVüÏ>ûŒýû÷óûï¿ÀÙ³g ­^²9‘êaPFüþ»VðìÙ©©ïì¬þàëLw铟Ïí™· ÚüÇùãñ¨G•ûZ“˜ÈëW®Ph4âÒìÌv=8(ÔjòÔêÒߋ̤ÆûE¾S÷å«Õ(kÑM¨øéýr%BÉéÈmÛ‚»»É}ûÙÛëÄN//•ó¹fÌ+þþÌŽ‰ÑÝsT*þ/!Ï52ùÚ¶BŽJŦâ)¿ÌL¸r…ß&NäéJ½Cq¤ÖßÖ{­¦´ˆRi4¨5òÔj²U*²U*²Š¾•Õ®ßV TC±D®J4®J.åzU¦ÜVoäÙÖ¬hĈ >œQ£FéÚ4 6äÈ‘#ºK"z€„ÍÕ«°c‡Vô9¢}84…ÁƒaÑ"ª~Z³rqøE’+ñtØÕ±£ó•ÎØÕ1ýi^|rësÊX=ÎÏïš5ÃÎ ¦ä⛲ŒŒA;†7!¥‰m*0ì[ï˜âö”ÂBîp'?_÷=± À*Óržr9{zÒÛË‹>žž´tq©v_ ââøðFI©/¹œ˜nÝjÜÛdiÖ&&òrt´n»¡ƒ1]»"µ“}¡F£EJ%Ù*k6¨X°TN*pVѹ—Š‘/›&ªî+•ÜW©P[øo×}õj2×­³è5„€`G€yä–/_NŸ>}¨[·.J¥’uëÖ¡R©ÊK¤F),„¿þ*™ÚÒ›¨¹\›Ýyà@xöYmΡ¶'Í@üÏ®’øÉS«Í£ f`Np0SË>± ²yPÓ¦¹ÑÉ¥„‘þ÷»Ü+(@õ7g™Œô)=\]ÍvãžX¿>óbbtSéJ%+øÐŠ¿oKð£‘w´¯¯Íˆ;‰/¹¯¢Ôñƒ›Ã‚³%û³ïÀû›ÞŸí¨Xf‘*¡,~}_©$Óèõý¢cRóUÜLT‚‹:2UƒV½÷Þ{9rD—Ÿ'..;;;~ýõWÓ²ˆíS‘(%E;¥µs§vŠËÔMÞÞЯŸVôôë^^f ØL¨óÔ\{ÓPÅyt÷ÀÿU“ûH.,dÐùóü}ß0q¢£TJxóæŒ¬§-œ*Ö3DÔ³·§ž½=a®®å§ÒhH*,,W$ÝÉÏçnAIhîßÇÎÕ•.^^Z§']Ýݱ·Ðç•›LÆÛ òÙíÛº¶¯ããy»aCkÉgäí¼<gd”4(• qp°^@f ];mÂÁâ™±+W@¡€JŠè€nUXƒ*^û»ï@ç6khÿ¨’Í{ôÄ“JElj*Ÿê¸Õf+€ìííÙ¹s'QQQºU`;w¦ º¦/Ábìºp¡d”çï¿K>(*£eK­à8P›&Dè31scPÜ,YÙ"‘K]ªý„3˹¹ 8wÎ hoìÛZ·¦«ž¥¦s-Õd þööøÛÛóHÇ)5V¯_Oÿ®] (5ÁÛ ðU\ÙEËéï°úî]ÞhPÕ[£0 OL4ð‘=¢Rq÷ôim] ÅÅE›èêUí¶J¥ýÌëÔÉò×6Èþ¬”0üi;‚ G› ™gù`€`P1aaa:Órtt4 à†Þ¼·ˆí3nÜD~ÿ]ëçÙµ ôh+ÄÞzõ*=ÁÁ Ó¬ä^Í%îKÙo5Àµmù£úLOgØÅ‹dŸZ8;³«m[™iEñcYä ^x¡Æ¯ëmgÇÄúõY¨wÃú2.Ž×ë×·ŠçËœhÐ }&´mËsþ¦ •°°ÚÜŽ–@÷ïÃÁƒ†mëò÷b7NªV«™6míÛ·gܸqD Å8p€nݺUZ ^Ävˆ‰!C Ní4Õ²e•‹__7~ûM»Òë?àí·mGü¨rT$mHâÒó—Pç— m94p ñ,ÓŒý«ïޥ߹s¥ÄO//ŽuèPJüˆÔnÞ ÀA?—Y^?—•éÓÆ8œ‘a0ºé$•2ÒÇÇŠ™kÔÛ³ô'PBCµ£æ3‚Z½z56làã?&..Ž#Fðúë¯3uêT–,Y¸q㬢ˆ™˜=»xH6(ߟ¦5/¨}J²µ[UŽŠÔ©$oJ&uw*jEé9½E!È\+ž³ÓÓnÞd~ll©}¯úû³¢iÓrŸúEåIMMÅÃù¼f?VýííyÅÏwîè򾂮2ÆÆÌÂÆ¬1ýâビZMZZÞ6žÓÊȸøéÃ>ú@ä“O>aìØ±Ü¹s‡Ï>ûŒÝ»wÓ³gOë'b6nÝÒVaײ(™¢qú> IDATr‚>}J¦¶lqº_•£"uW‘èÙU¶è)Æ»Ÿ7>Ã+~²U¨ÕŒ¹|™ÍɆ+Æ$À¼à`¦T²òGôYžÈÈHžxâ êÔ©Sã×þ00•wïê–ö_ÉÍå·”FØèˆI¶JUêo}¬Ÿ)))œ:uŠçž{ÎJ‘™ctîœÖëh)ïzAìÞmØ&   ÌÌLƒ'ÕÐÐPÆ/ŠŸZÆçŸëçí™H:Úz4BïÞ¦¯ˆª\i»ÒHÚ˜DÚî4T¹•×yrnæLè’Šo&ðÜ… œ0Zéå$•²¶E †›p“Åå>|¸Õ®ÝÈÑ‘}}Y«7j27&ÆfÐæädrôê¤88ÐÇÓ©——Í‹ít~ñLeN޶0ª¥jÚFFj=@ÅøúB×®–¹–-!8¤V«9|ø0EKÏ;GFFëׯ×óüóÏ[+<3pó&¬]kØöÝwÚz\¶†NôlJ"m—‰¢§©3>#}¨7².m*N„w1'‡çÏs»Œ•^Û[·¦K²‹Ôn> dÞª©¨ìlv¥¦2À #RŠñô×??›žÎ+‹°0mjb¢¢,'€Œ§¿ž{Îr£M¶„àP“&Mسg{öì1hŸ?¾îµ(€l›9s ³6·l™ÈС¶ãOQåªHÛ¦ÞÚ™j’èq u¢ÞÈzøŒðÁµi+½ö¥§3ââÅ’"ŠE´tqaW›6&—MÑTXËTL gg†úøð›ÞÔÑÜØX›@7 þÔÏý¼\ô·[PP@vv¶Í{€ ´:{FŽ4ÿu4ضͰMœþÒ"8´lÙ2k‡ bAÊýéÑc+‰°§hÔ 5©»SIÞ¨õô¨rL=!NøŒÐŽô¸†™&zŠYy÷.ÿ½zµT¹†§¼¼ØÔªU¼ÉŠ ËcMP1Ó бÌLed𸧧Õbª*á÷îäþyÔÃТ9ñÚₚ3BŸ8wõ*λ¹i=–"@"µCï6+ê·ß óƬV¨IÝS$zvš(zšè‰žöU= ]é5åÆ ”‘ˆì5–7mZ­²¢ø±<ÖôÓÁÍ~ÞÞìMKӵ͉±TVz#—õëׯâjNOõë6žLÛlNÍš5‹3gÎTxÌVãߨˆMpã××ûôSa-k׉žâé­ìÊEccGÝô–Û#nÕ¾v®JÅèèh"ÊXéõE“&|Pí¾E¦ }ééœÌÊ¢“[õÿ6kŠCÄèùÝœe²Z“ûǘ¦MÁÙrsµÛ‰‰ZS´¯¯y¯c|»2ļýÛ2‚@'Nœàï¿ÿæ•W^¡nݺÖGÄŒ”5ú3x°õý)ê<5i{´«·L=u#=nüÆ’XPÀsçÏs2+Ë ÝI*å§-ú€7k¿ÇÖöó˜‡yxð—^á¼¹11liÝÚŠQ™Æús5Àкuq×{?k“H*…¶máøñ’¶¨(èÛ×|׈ŽÖÖ+ÆÎú÷7_ÿ¶ŽàÐ/¿üŠ+X¹r%Ï>û,ÿûßÿhذ¡µÃy@®_‡Ÿ~2l›9S;úc Š:OMÚÞ"ѳÃDѤ'z:™ïiúBNΟ'Öh¥—¯½=;Ú´1Ë“»è²âÝwß%<<œ¾}ûÒµkW¦M›F“&M¬žH51ý ƒAƒ´¯kêÆ\,z’7%“²#U– ¢'P+z|FøàÞÅüKÎOKcä¥KÜ7ZéÕÚÅ…mÚd¦²¢ø±]ÍÎ4ýo6‘¤G¦“ùg&ÊLeåçéáèHAu¨7²zXl¤'G¥bwZ›““Ù•šjP×Èg™ŒŸZ´`ˆ¸êQÄÌ(5BOœ0(¥ònÆ|bŨ ÑŽ7X°ºY3^ñ÷·^P5È‹/¯¿–l¯XjÙËÌ(,,i»t L±€%&&F¢‘(­c¬V½{÷Z;3qõ*üò‹a›ñèO…h çbN‰à9œIaZaåçéáPßÏ'R)ïðÁº¶¥ |`õ•†÷•J"*Éý£OmË¥ÐùóÚÇr4`¥äçƒQ-qQ•ƒ(€D,¬Y†£?<Ï>kxLò¦d.޼HºD’IýÚyÛáÑ˯'¼ð|Ü—Ö.óñè“ZXÈÖ”6''s =B|hÁNN /=Ö.C ŠË#¤<@ÆL¬_Ÿy±±¤Í‹d(•,¿s§”Iº¦Ù˜œlû'ÈÑ‘'ŒrÿèSÛòÔ«þþ%K ídu§¬ý„ò~~еëƒÇYˆÙ¹r¥òѵBÍÿÝ 2äržw6ÄIj½ÅÀÆÓ_/ûùÕúÜ?efX±=*ªúÈxúë¹ç„UoQHˆHÄìÌšU’× cG8Ð𘸯âÈ‹ÕÓHÃ휾ÌM†çc%×ö®H¤5÷ß›XP@D‘èù33³T’²hîìÌpFøøÐÖµêàkÑdy„ê*æí† YGvшKRA«îÞå-+%‡¹¦PpT/m€x¹’J µÑÚÜhúÓVQQð UïG­†íÛ ÛÄé¯òæªˆÍ ë×¶þäßÉ'v~¬nûGxeÈ+N Äí7$òš}\IÈÏ'¢hzëHf&jDOkÝHë+#z€,P=@ÅxÉåLª_Ÿqqº¶±±L¬_;+ „çþñô¬4Kumôù2B?®­*_Œ›ôî]ý¸j;¢1+Æ£?:Á€†ÇÜšzË IáPÏ¡4û¾vuknYn\~>›‹FzþÎÌÄ”ÌRí\]u¢§¹9RµÖ ¢ø±: fü/þÄàáñ`1Õv„ý¨"b3{ºtÑÎ?£V¨¹1ÅpÙ»÷ÓÞÔX‡‰˜æOÉS«9Ÿ“£Õ9“Í…œòMHDhŒT"¡»»;#||êãCÃZ>Q.z€,-x€Šù(0u÷îé ÿç²³Ù™šÊÀ0poHJ2(ÓØÑ‘^žž&[[=@ ] ¦Ïƒ qú«rD$òÀ\º7¶þÄ.ˆ%?®dÎ_"—Ðä›&åö™£Rñov6§‹¦±Îdgs)'Ǥ\<åQ×ÎŽŽnn ¬S‡¡>>øÛÛW»/[¦¹³3CëÖe³ÞHÌœ˜˜@ÆÓ_cÒÜ?Æ<ÈPBœ:U²-‘À A扫6# ‘¦¬ÑŸ~ýJ¶óò‰û"ÎàœúêãÒR»|üj|Þô—‡aÜsË޿ݵ¿ Ãål^žù-âË ÆÓÓD섺Õ\ü GEˆÛãH ¦FG+Ð/7o²7+‹;ýü¬~/$ñir²"ÏRós ÎìÙT"€@ž«HedÀï¿+ó„² !€UæðaSã±÷çÜXå²wïÖÞ„ÿ7œ÷aðQÍêÕÓO_uðõ¥½¯/2½ 8Ýýý¹Ûߟ]ïÅÙ—/óMëÖV¿×O7orÕ8öO .½wÚ¶…Ï?/M[âúî;(Þã€ØØªï#V×ß&‚*c<úc÷çÆ7ÈÜ­9ÍÞoÆé‚<¦^¼Xš™žN£ˆºûûëGwÚùøà'ÄŽÕ Ûãh ¦FGóÀ‘#úô·©©ÍÉ¡•·x16??Н± qfTÍ-VUHP%’w†Žþèòt\xý‚¢<ä‘üz0ìÔ)}(~ï¿þbOûö¬kÙ’± Ò# @ˆ+#âÙG‰dL¿  ÚûúêÓðÎ¥KV½GFQ›SSy•þ玦èØ1(**»~Aüðƒ2O ËHP%ŒGŒãþîö vWÓt~Sæ]¹Â^ƒU M˜ LË6Fx€lÏÀÆmÌ”Fé/SR8——gµç_ã†âGOŒ§'=«`¶vvPHDF–¦óóå ¦ËâçŸK·Ïhкt±]ûœ !€•æï¿Ëý1Þí òåHÎGèx+!A‘? 4”'¢Ò ‚šå±Å¿ZIb®£Cl4ýõŒˆýS&• ˆh<°ûÈ#r e$¨4Ó§+Ó]º(ãþïöîêFä´F¶=iii•7_Q‹Q«TL6ú49™DÓrU9•›«õ­lìC I7XµæŒXêÒé`Ëež˜þªB *ÅÁƒ¦o:CAdvÙûŒÆ¼›q•¿oÝRä/‹%ÔÍMøSjÑǶÇQ=@%11y u:æ_¹RΖal~¾'  Êûì9»,@þ)ï_‚¯/ôîm»v9#B *…ñèO×®pÿý¥iãeï>·ûpcˆ/3L•C JM úØö8²ÀU¥b¢Ñ(ÐÊë×I1\c]I´’ÄgÕŒýcˆ³{€Àrdü›æ@X)+‡@‹9p¾ýV™g(ˆÌ-{^ЄgΞFcà˜nàîÎ’æÍmÙT@P†7h X«Õ²01±ÊÏ·ýæM®L£ù¸¸0¨ø‡Àv Ο/M»»+c° ,C Eìß/G5ÄpôÇܲ÷Üé ˜sù¸âš§4à‘EžØ§Êöˆ>¶=6ß lëV˜9öîµÍóób½z¼³~=éű2ÝÜXš—ÇÃÅpÓ×—o¾úJGÅ ûäyÇO#¡DƒÍß8ó^`†´m«@‡ÁצGzõÿši›3á´hß¾}¤§§ÓÏpyRlÙ²…ü‘ÀÀ@ž~úibcck …Ž…ñèÏÝwCŸ>ò¹¹eï ^‰à±‚‹L}Ezx°¨Y3“ç_̶Gô±í±É^`:|ý5ÌšeYX`+à“—Ç+_}Eü°aú¼…2fãF¼*±*ì‹{ï¥À@ü4¹~{æÎU+A¥‚à`¥(2úˆ9sæ0yòd.]ºD·nÝ8pà1115Ó``ß>ضM™g(ˆŒ—½»‡¹óÉàDzŽâšU-Z "< £ÕÂ_ÈÂçäÉ¿ý+_Í{O<Á­âØ@)¬|è!^ýê+‹Ÿc­Ñgï3?ü€ÊœøY¥¦Ê‡Á¶&¨Tr´ÀæÍåM¯bcå-Ôcc¡Y3yKu' <t劼·•ªRûI pºo£ÔÔT®\¹bññüùóY¼x1=ôgÏžåã?fºñr§:ŒñèO÷î¥Ë-Í-{—¦…3ç¦rDhDx8ýÊøŸŠí}l{¬âÒhàÓOáw”&cúö…'žµmÖ±/j4Ì3È›ÿ⋌|è!ÌNTåäÀµkpý:\»Æ Ž}qqúb•$ñÌ?V»]…’DvJ A))ò:pCÔjhÔ¨T>FE9T„ÀÛn“gKlW/BV–lŽ6BÛ¹³›»Ÿá³½ÿ^gM—ôqmi3¦È|P¹ë§N… äµÏ£Go$~âAÙ<&¾m[…ø±åßÓÀÝ67*Ê_óM´ÓXæ®×JëJbÿ¬] À³-ZÀ¿þE|r2̘«VÁwßÿ裔$ïꙘHüóÏËC+VÀ›oß¾=<ô´oáá|¤R)Äòîå¤óòàÈâ7n„Ù³á™g kW⃂ 4T65Nü}÷É ãÄ ((°ëëIž+M:“&ųs§ò/4@úëu:HM%þå—a÷n¹OW¯–ÿ¾‰aøpxä⣢ 6–­~~Ä»ºÒ3,Œ%÷ÜCÛÜ\ê*I*kRÖ±3f .,³Î¹sçhÞ¼9¸¯@øøãY±b{÷îÕ¿˜Œ_¤u…={ä@‡†ìØ={ÊËÞ÷ÅíS¬üR=äϽ²Ð¼¤FEF²TÄü”deÁ’%ðþû²ïÅ®®ðÔS0y²¶=y€ÒÓaÑ"X¼Êš¦÷ð€gŸ…×_;/ƈöôä©úõÛY¼séCÂÂÊÜÌÔxãÓAaaÖ?@aQÙ>>õéSº µ„‚Ù7U"ˆ ’áž–¢ÓÉÿ/;ñDñ¡g“|(¬åéÀšl”“Qç¦ÀÖ­[Çþýûð÷÷§cÇŽ|gàfûöíô1~cÕAþúËt½ÄnnÙû©§½ÙTºDV|Ü¢…EAÏÄ>U¶Gô±í)w/°7dA o¿m^üxyÁرpá,_nwñS¤FPˆæäðm£Vé ߦ¥)òžµ¢ð.w/0hÕJ6ÅLœ({ªþø’“åþÞ»>û ÞxCžRl×¼½­Ö6‡ÇÕÂÂ(Š%ÙFæúÚ†ÓMíÚµ‹U«V±oß>:wîÌÈ‘#¹óÎ;hذ!Æ cæÌ™üòË/<ùä“ôïߟëׯ“œœÌo¿ý†¿¿ž»ÿ~9àl ={ÊÓ_§ž9EÒ§¿òB]yhm9^¥Yc6äýrŒÏAàêU˜7>úHö¡˜ÃÏ^zI?µt›ˆ'ŽgCJŠ>ÝÙϽíÛ›Ô[zõ*£ÏžÕ§›Õ«ÇÙâÏÞZËÕ«æG.^´I´íA­–ÍÝ!!–ÁÁòtŸJERRmÛ¶Uxa§›kÖ¬Æ c˜A¯ÆëÏ7lØ ˜ èÝ»7{÷îå?þ ((ˆ^½záååE]æÏ?•âJGÌ-{ÿx„J!~b½¼˜mÁÔ—@à´$$Àœ9ðñÇPXh¾NPŒ/¿,ùÔb¦DG+о¬,~¹y“ÞF+¨Œw~Ʀ]##å£W/e¾F#{µ4ùXXXzn.¯2åÖ=}´ì›Ü)Ä•"2ñGv…yG÷ 6/h‚‚l"Á™p:Ô Aƒr½]]½@LLŒ|hÀ[o)Ó÷Þ ÷Ü#Ÿ/{¿ÙÒÏî+Ý#H­R±6.Žz•xó Šíqˆ>–$ÙwQòhx˜Ë³´®Z­4š>–çש$iiiøß¸ë»ïºueÔ¯ãÇÃÈ‘àãcµûÛ’¶>>ôf›ÁôÖ¬K—èXNnÝÒ§Õ*O[ÙOY£{¹¹É##väÿâ•[òýŒ A‚ÊâtHP=~þY> )™4·Ûûô4HnÈ QQt5ÜÊØDŒÛcÕ>Öéä¥ËW®ÈÇåËÊÇ«Wå/ÿÊŠ{ RÉ_tæÄQYe”í8r„^\Öß%{SFŒpȈÅS5R ìÉÊ¢KñûÝxôçÞ€Yùï¬+{•`º??ôáHUG žÄD:T™×»·|ËÜnïû{ª9|Gé‡}+ooÞ®ÂHš?¶§R}œ‘a*j ¯^- QëèHRé”CNNÅõË¡ÌÀš6…I“àé§-Úð³e)€s IDAT¶r—¿?=øÝÀÀ=ëÒ%¾mÓ†"ÃØ?Å ³ÁˆcDDD?P¶zà‡~)Õ„ò Ò”«EUªÒáWãeï:wïÿ·Tü¸ªT|‡‡˜w®Ýäç—ŽÜ”%p²³íÝJç U+˜2† ‘w@w¦FG+ÐÖ´4Ždgs¹ €d¯“Ÿ«+j©¡Û‘ˆ‰‘íaÆ‹Åæ§ÖA /¾ÅÑôL™wÝe~Ùû%®DÄŸÔ¨}}«to‡ð§82ãÆ‘ôË/4(Ùr ¶¢RɇZ­<Ìå••o.O§+5˜O­¸Ê' ð\Ûµ“·«0À¡öŸ²„ûéèë«ðú̾|Y(`phh¥|€–R£ ZBÛ¶ðÛo¥iwwèßßnÍq*„°t©>b½žþýåp%`ºÛ{F°ŠÏ‡–~àÝîãÃÑÑU¾¿ðÙO?…÷ßg3`Õ “=-QQò”†ç‘‘²Ç¥²Æ^Š¢²Î+*×hرg½z÷&xÐ ûý-5ÀÔèh;vLŸÞ’‚ñø–-¦¿ îy€ÀTõê%{€ÕÇéâY“ºè?dŸ¡¥£Y3y4( @^ö~ðÎƒŠ•_ïN„ïÏÝT*öuè@[YÍR§HN–§a*ÍÖϯlq :¤‰W`$ Íþý/Ã3ëåÅéÎk¶QNLZœ;'OƒÝ¼)ïŠb&“Õq€u‚«WaÐ ¥øññÍ›KÃ’/{?Û~0Xz9-:ZˆŸÚʨQ¦âÇÃC0e‰›¨(ð÷·O{ ˜Ü¨O÷P«ù$.W+|™Š}ª¬Lz:Œ­ÈÚ-Ç âÇf”»˜“á¢RñzT”I~ŸÀ@zxØì¾åî&T!€ê Ë–Á£„xfÌ(M/{/t‡_(-‰á6+m$( ÐVfÌåО›/~û­Ó,Å®­ 8à:4W1¬A"ÄŽ­ÌÏ%Ôµ8@Û"Pc×.ùûÑfÍàÿ+]ˆcnÙû†ATüÙv§Ÿ¯™ùõ'¨lÛ&ïxmÈ”)Ц}Ú#pZÜÕjÆ|¸ºòXHˆ[$T!€êW¯ÂÀ回ÁtÙ{Z0|^!Ú³xêËÅŠS)uaµA•/¼ ÌkݦN}\¤¥¥Q䨻‡W‘Âà qsd_§Ã’^ÙUATG((ý=FÑê¦g€Œß3Lv{_5òêÉç³7¦…—ÖDx€¬Äĉò~&%¸¸Èsnn¢k€ºä*ÁËÅ…± ðl ,d 5qþå ^z öîUæM™Rjz¸uàÇ9¦Xö~&~ì+ŸßíïϘâ;k"<@V`Çøè#eÞ¸qЩ ú¸&8°ÌÝÀœš—"#ù.=Î5Ox€ÖDŒÕV¬€Õ«•yƦ眣9é{„¢,åþÒ—äeï^..¬‹C-VÕ>rsáùçå=Kˆ- å-ØWW¾2F!€œœÝ»á•W”yƦçÜÓ¹¾ï0štåß+^,]ö>·IšÖ«g“6 J5™6 Ο/M«T°j•"Z³ècÛS=@%4¨¡­É…H`M„rb®]«Øôœ1ŸÃ½S˜\¨¸öãgaý`ù¼W@/EFڬŸR öìE‹”y/½Ý»+²DÛžºèªi„H`MÄ^`åàÈ{Bò÷£!6È¢  ±€îù‡ü‹ùŠ:_ )ùãëâ‘Nˆ{?Õ> äMNœ(Í‹Ž†cÇd¥+•¤.í&F€œ”Ñ£MÅÏäÉ¥â§0¹Ã}›ˆŸM)ÎoÚTˆŸÚÊŒJñ°r¥?@`B9!~(ү̜)ŸkÒ5¾ï0¹§su¾>x¹4=<<œÿFDظµÂŸR%‚¹s•yÇÃ}÷™­.úØöÔePM!<@k"“ñ矦¦ç¦MKMÏEYEé{„œ£9Š:¿ô†y¯•nt:"<œU-ZÔH›…?¥’ÉbÇðË6"Þ{¯ÌKDÛá²=Â$°&"qýº<ÅUhàgöö–MÏ ÍÑr´ÿQn¸¥¸n×ÝðÎäRñó߈VÄÆRS ÞEŒšJ2oüó2oùre8o#DÛžº¨&q€ÖDŒ9 ……rPÃëוùk×Ê»!èòu{ä™»•¿P÷u†éo¶xŸÌkXü*É©S0}º2oÈ_ @P)„r^~þúK™7i’<"$i$Ž?~œ›¿ÞT”j o¾ EÅ〣"#Yfñ#ü)¢ÓÁsÏÉ«¿J >¨ðRÑǶGx€l𠬉@NÀÊ•¦» ôë³f¤•8ñï¤mKS”ŸhSfC‡œÉÒæÍí2ò#ü)òÁ²ÉË8Ï‚¸EÛá²=Â$°&"P98B ={äx?†¾Ÿ¦Maÿ~ð—8õÌ)’×)w@=ÛÆ-€ìâÕÒ¯4lÈ¢fÍj°Õ‚Jsñ"´i9æõG• ^@`%D C”$û~Ê2=ŸyÖDü$ÄÀkóJÅÏ!~ƒ#”â' @6>  Jä ”˜ž¯]Sæü±lz>7ö×>R&6„ñïA¦¿œÅûµ@üÔ…_ÕbåJøõWeÞ{ïAx¸ÅO!úØöí 5ÈAyõUS;ȤI0h\œz‘Ä…‰Š²äú²øI’Ó¯EE1¿iÓjmùJ9\½ ¯½¦Ì»ï>9P%}l{„ÈöÀšP9ÔVЪUðüóʼ¾}aÛ6¸2ç§^T”¥Ã+‹áZqPç×5bN“&5ÔZAµxøaغµ4íã#ïõm¿6 §Ex€µ–½{å; iÚþïÿàÚâDñ“ žKÄÏd!~‡ÿýO)~æÌâG ¬€@DR `jzÞ´ ò6\ãÜØsŠú·|aÂ|¸ÜHNOŽfv-?uá—F¥¹qÃtO“îÝaÔ¨*=ècÛ#<@¶Gx€ÖD A£‘ƒš3=‡NæìȳŠü\/˜ø.œ/¶ù¼ÍÌÆk¨µ•CøSÌðòËf»ÉÓV¯UÕ"5‰>¶=Âd{„H`MÄ^`«¯ÂîÝʼ×_‡žêN ;…¤+µrxÀäwàTœœŽ‰á­˜˜škl%ûT±y3|ù¥2ïí·¡yó*?¥ècÛ#ö³=b/05ÈX³Æ4äKß¾0áî4Ž8¤-?7˜: ŽÜ.§ßnܘ7„gÄq¸yÓtš«S'7Î>í'ELÕröí3ý>lÒ>zñ&'GÒ”ŠŸ"Wˆ‡ƒäôL?ŸbÀ¸qÊmÝÝeìâR­§}l{„ÈöÀšT‹IN–Mφ{_z{Æ·2¹ôÔ1tù:}¾N ³¦ÂŸÝäôì&M˜êâ„?EÏöí°v­2oÊ9²e5}l{„ÈöÀšˆ8@å`Ï8@ Ü{/¿×¿~ç¡ï¦(«ô—¦¤‚9“`ûýrzn“&LlÔ¨[+¨6Ùٲйt©4¯M8xÜÜì×.@P§¨Kq€„¨–2v¬©ø™9<‡°yGÐd)‡ÙŽ)?óš6eBTT µR`5&MRŠyêKˆ@ ° BÕB>þ–.Uæ ¹+—^ߦ0]£È_6 ¶/Šx¯iSÆ9 øIJJ¢Aƒön†ýøãX¶L™7atìhµ[Ôù>®ÒÒÒð÷÷ÇÕÕº«YYœL=ÉÉ”“úÇ 7/ R©pwqÇÝÅ7µ›þÜøps)§ÌJ×yºz⪶ý×Iaa!ÙÙÙÙü^çG ZÆþý0r¤2ïÎFyŒ¾p˜ÂäBEþêç`à ùüýfÍÓ°a µÒºlÞ¼¹î.ÓÎË“÷õ2œ‰Ž•ÝìV¤N÷q ±cÇzõêEppp•®OÊN2:'SOríÖµŠ/®Œì8’ù÷ÏÇËÍËf÷HMMåÀuj)¼NÒ¡•´è$’$!!V”¸W$$ÅyIYUë¥ä¥ ¹Ö gŒð•CM{€nÜ€ Ñ`Óh¯> øݵ|EÝχªòù¢fÍxÅAÅOgÂyg÷T*عî¾Û~mØ ¤ãRæ%‘s2å$7óoÚ»yÕ&.$ŽÏ|NûðöönJµHÈH`æÎ™Ü*¼…V§Õ‹ãóaRÑyyÏQÑóÙ¿~dþâü†~1TK(*’wr7?²Úÿ°‰øùêñRñóAóæŒŽŒ¬Á– ¬Æ¾}°p¡2綾ì"~J~JHŠ_š†:IgQž„„Z¥ÆEå‚‹ÚEqÓj•s/@-Ôr6ý¬‰Ð9zš¼¢<{7ÏfœJ=E—U]˜qï ^ëöšÃýŸ%$–í_ƤŸ'‘]˜mïælŒ@µ„±cåþ%ø¡áÓÐø\ÏUÔÛú,} TÀ’æÍåâ§NúS å©/­¶4/&ÞyǪ·¹’u…W¿•ïþù|0*öú… èÅ¡0*97N ªòž£äP¡’U*›¤ó2óH.JæÔÍS\¸y"ub¹ª]iØ”–¡-iÒ’–¡-‰ ‰£žk= µ…&‡F§1›o«#O“‡FWêMÔè4LúyߟýžÏ|F”Ÿõ|‰¶ôK?Çs[žcç¥W8BÕV¯†%KJÓÞñ¡ß|Rrõ~ºŒT°,6–#"j¶¡6Â*þ”‚ÙO“—¹¹¥çÆéÂBÐéJ­V™.ï°fÝÄD8~\ù7¬\ >>Õë‡bŠtE,Ú»ˆ·v¼EŽ&ŽÖóT[­$ó~y:4'€ Šo7oâB∠‰SˆfAÍpS×ÞÕ€9š^ýþUVÿ³Z‘ÿû¥ß¹}ùí,p9CZ±Ê½láÒI:Þßó>oüúF­+ü%b[…¼ ªx_@*ÅyIYUëI:‰‚"ƒàsNŒð•CMx€V®„_”¿<ѲÄóMó•ó¯t—}±’VÄÆò_'?|÷¼üÛœp©HÌ”¤óóK;ÐQyî9XµÊ*OõWâ_¼¸õEŽ$±Êó ¬K¨W¨^àŠ(ÿ(ý—‘#²éÔ&žßòjÑ‚áá5ÜR I0y2Ìkï–ØŸˆ¥ºŠÜ̿ɤŸ'±òàJýÊŽÊP2­Sò«ÐðgYyÆ×”{ì3îndƒ‘®ˆ¹»ç2ã÷hMG;ú5ëÇGdÕé;AíéЕ+WxôÑGINN&''‡§žzŠÅ‹£V›7â½ñÆÌ›7O‘wÿý÷³eË›·5>¦O/M»"1CuœÛµÊ• ÿ´ƒ7ß›ŠU±± wñ“ŸÏ<£ßñ< ¨c %Ë—ƒ¿µžbÝ‘uŒß>ž97LÊü=ü™ÔvcúŒ1ñ°×4Ö\)SÞÊ›’ÃÐàm‹tvf6QaQ´nКÁ-lº¼¶îÎÿù‘E{1éçI q‘‘@ϵ=™Ü}2oõx«Jqƒ¬á:”tˆáß çŸ¤LÊ=y¿ßûg%¿(ŸÙÌfîî¹j MÊ›5gÙƒËèÓ¤Z'Èh ˜ôó$íYd2-ëíæÍÂ~ Ñ~„ÍÛ±ïê>†3œã)ÇMÊB¼BXÒ ƒolóv8uÉä4ß7nÜ °°hƒÐ7nÌå˗˽î×_å‰'ž`Ô¨Qì4\‡^Lff&ŠÃKÓEE0t(¬\YZD!ïºþ¡?Ùds±1¼6ò}T,WˆŸªÞßîé;¡kW2ŒÄOFË–°w/Ì›o¿MÆÈ‘0f ü÷¿ðŸÿѧ<ø ¼3l×®dDGË‘’£¢ $„ F!~jÍßkÃôöóÛi½¬53vΠ0G)~<4Ä÷Œç訣ôiÒ§V´W¤ën:ïVï÷}ŸžúAY1i–£Éáù Ï3`ý½qÚÚ÷ONMfâO鶺›,~”!Õ3€/Ћ{÷—½ÒyyydddpéÒ%²²²p’q‘ qTòôððÐçÕ«WÌ̲£YöíÛ—7ÞxƒÇœÀÀ@xà6nܨ¨óÃ?°víZÅaˆ%é‚0¾ø@.oF6¹älÑEý/~`ü{ã¯âÓ¸8r·m«ôýj]úóÏá¾û =EiŸ>¬}úi’ÜÝkW{kiúzöuî{/}×õåüÍb!y¨´nŸ&}ï5ž·z¼…‡‹‡âú’_sµéïq¶tZZkÖ¬©5í©Méû›ÞÏÑQGis­¢œCòê±6ËÚ°ýüö ŸoõêÕ¤§§[tÿ]—wÑæ…6ÌûsZI«¿È^¥Mƒ7ÑýfwB½B«ý÷9zú¯¿þbíÚµŒ3†õë×£5ŒOæÄ8Íصk׈ŒŒ$;;ooo–/_ÎÆùå—_,zŽ©S§rüøq6oÞ Xg ,'} ›Ð¦ªNá!)_dç›Âäw =Lź–-VåûÖfÌ€7ß4Í>>ü\]Y±b…اªt’Žå–3õ—©d˜ ú> XÐwÿnýï2ŸCô±íÙ¸qcµö«+¬þg5¯~ÿªŸÊ*^íò*súÌÑ xc®]»V¡(G“ÃäŸ'³dß³«!Ÿ¹ãÞï÷>žÕûCœ1怄††âëëˉ'ôy§OŸ¦iÓ¦?‡››[™+ƪBF†<ða(~žâÓ9n"~vÝ £—ÀÍ0ÿsñ£ÑÀ°a¦âG¥‚Y³äèÅ»f‹/æ²ùûúßܹêNFom"~Ô*5£:âÔèSåŠ}\ 8Pˆ x®Ýszñ#;+ò%$îYH§:)ŒÓ†DDD”+~~¹ø m–µáƒ}˜ˆŸ(¿(¾ú=kÿµVˆàDÈÍͧŸ~šøøx.]ºÄ_ýźuëxöÙgõuÚ·oÏ\ƒ¸3£GæçŸ&!!o¿ý–>ø€¡C‡Z¥=7n@¯^¥‹ÜÑ1“<ÇE“EÇŸ•—ºK^j¾hÕŠ']üdd@¿~ðÉ'Ê|y:lÊû´Ë¸Ux‹Wx•Î+;sàÚ“òöáíÙ3bKû/ÅߣzK肚¦YP3vßÍ´{¦™û;zã(>êÄÂ= -Žg•UÅ¿ý/}>íÃÅŒ‹Š2*^èðÇ_:N¿fý¬ö7§ZÿÞ{ï1vìXî½÷^‚‚‚X¼x1]»vÕ—7mÚ”}Úßߟ×^{¤¤$¢¢¢X¸p!?þxµÛ‘˜}úÀéÓr:ˆBfrŒ–d)êiÜ`ÞkòQ|Õº5|}«}»’ ¯ô:yR™ß|cv£Ï:¹X9l8±1?ŒáÚ­k&e~~Ìè5ƒ—:¿T©(±¢mOZZþþþ¸º:ÕǪÍpU»2£× ú5ëÇS_?EBF‚¾¬@[ÀØDzíì6>yìýÒtsq€¶ÝÆ [_ 1+Ñø4 lªGVÑ+¦—Íÿãá4 [PÐùó²øIHÓÍÉfG Emôf ¼1CŽõÓ;0/Zµ"ÄÍÁã¡ìÛ'Ç´INVæ7kÛ¶Asó1?„?EæÂÍ ŒÞ6šïÏ}o¶|P«A,ì·ßÊoƒ"úØöPÕÉ*Èbô¶Ñ|vä3“²àzÁ¬|d%Å=¦ð¥ç¥3æ‡1f¯Q«Ô¼Üùef÷ž]'RV‡ºä¨*+€Ž—=?ׯËéî¤0•Sx`jvž2n„ÁÄF˜Ý¸1.*ÇÝ€M›äuþyF› vë&üŒ¼ ”j ™ÿç|fîœiv3Æ&MXÚ©¾8=ë¯çÅ­/’‘ŸaR6¢ýö[ˆ·›7›NmbÔw£HÊ6ý’nÜ‚5®¡[T·šh²ÓQ—«µBß¾VÌù?\âY3~ŸÝwÁÌiàæíÂÆ¸8 5y.‡cÁxí5Ó IŸxBöÕò=ÙìÉï—~gäÖ‘œL=iRæîâÎÄ»&2µûÔZÅY °ƒoL·¨n<½éi~KøMQ¶êïUü–ðmÂÚ°éÔ&“k]T.Lè6øžñâý"°!€¬À®]r¬¾¬,Ùì<‘SôÆtO¦ÿ= «F@ o/¾nÝš–^>4«ÕÂ+¯À²e¦e¯¿ï¼#¯úªgò§è$ùEùäå‘§ÉS<æå+ò¶ŸßÎ'‡?1û<½bz±ìÁeÄ…ÄY¥]ÎÔǵá²Q~Qüòô/Ìÿs>oìxCéü\Ê9Î]=õ”×´ kÚG×Ð1¢c ·VàȈwj5ùñG9Èan.›ã̘çO€í÷ÀÐPÖÆÅáëb¹µV’ ƒËÞC\]eAôüó?ÕæÍ›mæOÑJZr5¹äjrÉ)Ì1+L Ë/–Ö5·³teócþýóùÏíÿ±RÈØ²2;vì +¡V©™x×DîkzO~õ$§ROɹÀ5 …œtS»1¥û¦Þ3Õiö”ÔÂTy€6m‚!C °šs‹Y31;gÀ´™pªµŠYóz£F6nu põ*<ô:¤Ì÷õ… ä¹@ ÑI:næßÔ ½XÑä(òJҖ敤«+Hj *žïðü^zIò2äÁå]^}ÊW0ùEù¬ú{svÍáê­«ÊÂS€¼‹%‚ÄÛÝ/7/¼Ü¼*%P*ûèîâ^q£ì€ðÙá²=ª\É'“Á|H1 RT ¦N…Ù³+6;ÿÞO͇͛ó|x¸ZjE$I^Ñ5ožiÙ¨Q°x1”cèÎ+ÊãÃòîîw¹ž}Ýl÷;Ý ÷ × /7/…`ñvó6›_^YI¾X+#Äí8p ½›àôT´˜@P„²I‚W_…>Íγ9FHfç¬öì¼í6:ûùÙ©µV$/þóøê+e¾Z ï¾ ãÇ—yiŽ&‡åû—3ÿÏù$ç$›­ãåæÅ‹_äµn¯ÑÀGLÏ æ¨$IÍðá°v-ô …Ée˜§Ì†ÛâÙѪ¡Ž¾¥@Jм­Åž=Êüzõ`Ý:yí¿² ³Yº)ïýù)¹)fëø¸û0ªÓ(ÆwO˜w˜¼ÚÀI<@µá²=Âd{Ìí&T'0§Ø–²v-…„1 Ìí3—0oy×ûÍ›7ÛäÏ”"úØöìØ±ƒÌÌL{7éIMMe×®]ön†ÀIq€Ê!>>ž9ÓßäuNÑ« ³óÿ½ fM«– tÔ--។ǥK¦õââä ‡+²3ò3X¼w1 ÷,äfþM³·ð÷ðçÕ.¯2¦ËãF j1"jXhIDAT@ÏBþ!å²ï³ó¥y±×Q¶´ÐéàìYS±“šZñµ={Â×_ƒA£ô¼tîYÈ⽋É,0ÿ«7Ð3±]Çòʯàïáo¥?D ‚ê#P‹Ÿ³slö·lY;·´((€cÇ”BçÈÈÉ©üsýç?°j¸ËË»SsSYð×–ì[­Â[f/ ñ a\×qŒî<_wß o!ü)¶Gô±í Û#<@k"Þ©•àB˜öŽŠWºÈ[ZT¼Íg •%oIa(vNž¦êÏéî­[Ë{}MœÀœ¼÷×{,Û¿ŒìÂl³—…y‡1¡ÛFu…·›·Å·1jlècÛ#âÙžÔÔT8 – ¬‚ð•C||<=§÷d³ó²én¬í`Ç--’’L§°.\×éW__¸ã9šs»vо=´jÅfî¤ì$æý9V«É5û |0ñ®‰¼Ðá¼Ü`:P f ‚/†ÀÁq>ìnÓšèšØÒB’dac,vªû‚ +:%G³f 2˺vëswÏeåÁ•äå™}ºHßH&Þ5‘ÿvø¯8(‡B  ˜3 "‡…óGóæxÚjK‹+Wäååÿ- C‡ä©­ê豩Ø1³WWjn*  ŠãÂÍ üzñ×2wRò‹bÒÝ“x®ýsx¸xT¯JM úØöí 5ïÔ p‹9Íê=­û¤·nÁo¿ÁO?ÁöípútÕŸËÅE^¢n,vCó' &b'Gc¹1:& †ÉwOfXÛaVÝóJøSlècÛ#<@¶Gx€ÖDx€Ê!>>^ñXe´Z8p@;?ý$¬ŠI¹^=hÓF!tÒšFp± ÉDØTEà”EÓÀ¦Lé>…§ïxÚæ»– À~ ú\¸P:Âó믑Q¹ë m[òÚÄ‘Ô<‚³Ñ> Òp1ër±¸ÙEÂÏ ä|_}S±Á±Lí>•¡·ÅEU —û @PE„²²Ð)=.X~­«+i·7ãl›HŽ5tç¯Ð|öºÝàRæ² ƒtäÃx»ycöhÞ®F„ð§ØÑǶGx€l𠬉x§VFžÊúé'ùØ¿_žê²ô¨ö´ôeCd_7¸I–Ç)à”\XÉÁ¢òðq÷1+n¢ý£‰ ˆ!Ä+Äz7«"Ÿb{DÛá²=Â$°&ÂT& Ó§KGx~ûM63[H¶;4wgSTÛ›H\ °N}Ý}MÅM@´þ<¸žø0e@OëãÇaÄYô\¹bñuW5„›JüÔF¢SVúþ~~fGnJŽ zb(X ‚Ê"P ܸÑâº'Ca{Sø© ü#‘cáJqo7o:Dt mƒ¶&£9ua÷táO±=¢mðÙáXñN­)Þðs“RÑsÕ¯âkÜÔn´©ß†Î‘éщΑiÚ µÊFAáO±=¢mðÙáXá*‡øøxâ§O×§ \aW£RÁs¨Hå숪BEóàæ ±Ó¶A[±m„@ j%Â$Ðs¤>üT,xvFCž[Ùu#|#b§cDG<­äv@`5„ª€;FšÏ÷÷ð§Sd'½ØéÙ‰HßÈšmœ“ ü)¶Gô±í Û#<@k"Þ©àéêIÛmõb§sdgš7GE9ó_‹þÛ#úØöí 5 rˆçºê:KÞX‚›ºœ¹/@ œáè —Â…ø'¦¨¨ˆœœ“C£Ñ I’þÐétŠ´ñaëòš¯ÈÊÊ¢¨¨Èæ÷© $°;Ÿb{DÛžêz€t:YYYdffVxdee¡Óé׫T¦SòÆy¥«ZG­VãææfÑáîînq]ãúÙÙÙ¸¹¹éEJvv¶YñbÉQrmaaåƒÔ:3~~Ätq„ØáO± ééé\½z•ÄÄDÖ¯_O=Ðétú£ägYiKóÌ¥K(ù¢,ëÑ’:•©k.O­VãêêZîáââRaŠê}ÿý÷tèеZm‘ˆ13™™™dgg×È/|@ #ÀîñS94 ׯ_׋›«W¯š=òóó×}òÉ'vj±@ Ô>„Ô8ÆCýùùù¸¸¸è]þÊ6Î3WfnˆÞQÉÌÌTˆsçÆb¤@ °"...x{{›îîî¨T*Å¡V«Mò,)³FyM››ËÖ­[kä^öF A¥Ðjµ&C÷•=¬=Ô¯R©,N...¨Õjý£ñyeÓÕ©+IIII q“““cµ~8*• ???üýý‡¹<\\\×›{OçU¥Ž%×èt:4ÅGaaa¥ê^'I¾¾¾f‹¥‡"íááQþ?§‘””ÄöíÛíÝŒA  8yò$6l ¨¨¨ÌC«ÕV©Ì¸¼äCÅÖ> Kê–eȬ_Ð’$é? ¥øúúIddYYé´lÙ¼X|•þÒT«U¨TêâG•¢\®ƒÉ5ÆõJžÇ°T¨T ¿¤UH’„J¥*~«‹åך$aT}~)*ƒ<•Áõ’Qº´NI™V«5xŸiŒÞƒýûP£)ë=ZDQ‘éûX«-Ò_£ÓiÉÎÎÁÇLJÀÀ ³‚¥¢Ã××שF3mÁµk×D Õ¨¾üòK¾üòK{7C УV«iР>a4hHýú>„…yª&8XKpp>Ù¸»§RX˜ŒFsY ²wÓë¨T×qqñÆÅÅÇ¢C­ö!7ׇüüò¯QÕá “Kˆˆˆ°ªø‘$:]¾Á‘§8—¤"$I‡üþ‘ªq^²8ÀšçæîWýzÙÙÙÄÆÖ“B jµZ¯¯oñð¾/^^^èt:ƒ_Ö:“ѱ’ssyÎä‡ñññ&<<„ ü©_ß›ÐP7BCU”G``¾¾ièt×ëe>F#‚šG’´eQT”eÕçU«ë¢zÅ£j.ÅÂH]ü¨L«T.eæÓÖº^õS>ÊuTV-—$m™‚¥ä\«Í+·¼ô\,7Gt´X/˜àî¯>>îøúºãããjp¨ñöVàí-áå%áí­ÅËK‡—W^^<< tº«H’–ôt(k•ч <Å"X»!I*´Zù$ÐjÕhµ*t:ÐéTÅç*}ž$©õr}ù¹ Ët:’$ò¹œ'×)Í“ïMq]ôm‘ÏK¯)­Oq}ù("((—ÀÀLoáå•”?ÍhþÅ"*îcAuÉÊoo0²æX ùK;&Å67pŠŠ /|}íÝ3 Ptï. ™;Ôê²ËªR^2ýon@ÃØa\Ç0m:®®àãC±)=ÜÜŠ€"*ú’.­V™Þµ ÊÕ.²Õ™m3È}WÎMîV V{°gøäšúÀÌ••}n|ñs³[2UeMm”5]R~½ò?|-Í+-+ûƒ»ìó’T—-[¶Ð¿êׯoï¦8-ׯ_g÷îÝ<ùä“önŠÀ p*´sçN DŸ>}¸rå yyyüòË/eîl[ÙúÛ››kï&8=¢mOAAS…d¨H’$vnX §Š¬5uêTâããùüóÏùý÷ßñððàóÏ?·Z}[’žž^­7vRR’Uë–W§¬2sùÆyZ­–”û,ã-**"--­Ê×Wæ”››Ë­[·Ê­c­>ÎÈÈ    Âz5Euî]ÙÿQE÷ºuëV™â¯¬ÿQvv6ÙÙÙŠ®ìõöü¼0þi4šÛŽÇÜë 2T¦-ùl±UWôÜÎŒJr’1Û„„7nLAAîîî|üñÇ|øá‡ìÙ³§Jõãããùâ‹/ˆ‹‹³yû“’’ÀÓÓ³J×'$$cµºåÕ)«Ì\¾q^QQIII4lØPŸwèÐ!Ú¶mkQÛ«Caa!©©©DDDTéúÊü²²²(***׬i­>¾qã>>>xyy•Y¯¦ú¸¬6ZJeÿGÝ+==WWW³ÓÚeý222Ðç™{ÝßûÔ©SDEEáíímQÛ«Cuú¸²×ÛóóÂø”••ERR±±±µ½:˜{T†Êô±%Ÿ-¶êc㼂‚þùçŸ:1*ä4 ü|9F†áÉ...e+ZRÈ!5ö¥!Am ª?Ä §@ÁÁÁ€³g϶wSœ’ï¾ûŽo¿ýVŸž6mšbªG`vîÜÉ矎««+K—.µwsœŽ_ý•/¿üRŸnÑ¢cÇŽµc‹œ“Ÿþ™o¾ù___FŽITT”½›Tu$'bñâÅRóæÍ¥µk×J³gÏ–‚‚‚¤ .èË###¥©S§Z\¿<®\¹"ýý÷ß’F£‘&Nœ(}ôÑGVÿ{’”ŸŸ/=ñÄRË–-íݧ%>>^Z»v­tñâEéâÅ‹RAA½›ätüúë¯Ò}÷Ý'=zTÊÉɱwsœ’ÌÌLýkxÛ¶mÒ³Ï>kï&9éééRïÞ½%­V+9rD2dˆ½›T-œfÀË/¿ÌÌ™3Ù¹s'éééìÚµ‹ÆëËÇǽ÷Þ[fýÎ;ãff'ÍeË–Ñ©S'ÚµkÇÌ™3‘$‰† Ò®];öïßOBB=zô¨‘¿Ñ‘‘$‰'žxÂìj‡%K–èûxÖ¬Yúx*/¿ü2'N±™*ÁŸþÉ„ LòÓÒÒxê©§hÑ¢÷ß?{÷îÕ—%&&’@DD„É´€³gÏòÌ3Ϙäçææòâ‹/Ò²eKzõêÅ?üÀ¢E‹xâ‰'øûzõjM7×a6lgΜ1É_³f ]ºtáöÛogÚ´ièt:üüüˆ‰‰!&&†U«V1mÚ4;´Øñ˜={6[·n5Éÿí·ßèÝ»7qqq >œ¬¬,<<øàiòäÉRçÎÅP9deeI#GŽ”¢¢¢¤¸¸8“ò§žzJ4htþüyiÓ¦M’tæÌ©mÛ¶ÒòåË¥¯¿þZêÒ¥‹tüøq;´ÞqXºt©Ô£G <¨(ûþûï¥úõëKýõ—tâÄ ©S§NÒŒ3ôå;vìF]ÓMv8¾ýö[iÀ€’Z­–>üðCEYbb¢äãã#mذA:þ¼4tèPéÑG•´Z­4fÌé•W^‘bcc¥;wÚ©õÖA I’<(íØ±Cruu•NŸ>­({ðÁ¥ èÓŸ}ö™Ô¶m[Eo¿ýV7n\´ÕQ9pà€´cÇI­V› ~ýúI .Ô§?ùä©C‡Òˆ#¤ÁƒKƒ–‚ƒƒ¥×_½¦›íPdeeI;vì&L˜`"€.^¼(¹¹¹I·nÝÒçuíÚUZ¹r¥¢ÞÀ¥cÇŽÕH{‘‚‚iÇŽÒܹsMPvv¶äéé©x}4Hzë­·¤{î¹GÊÍÍ•$I’>øàiÍš55ÚnGãСCÒŽ;$???4xð`iúôéúôæÍ›¥fÍšI’$I:NêÙ³§”œœ\£íuDΟ?/íØ±CêØ±£‰š7ožôðÃëÓ‰‰‰’«««ôÍ7ßH/¿ü²$I’”––&Ý~ûí5Úfkã4&èêPb|V™ÙdêÌ™3¼òÊ+út›6m8}ú4»w令¨ˆèèh6lØ@Ÿ>}j¬½ŽH‡ó}|öìYƯO·nÝšS§N)‚uéÒ…9sæØ¾¡Œ¯¯/={öäܹs:tHQvöìY¢££ñññÑçµnÝšÓ§OóÕW_ѪU+rss¹pá‚ãkÛwwwzöì©£aÈåË—hÚ´©>¯uëÖœ¶2æ>/ÄÈ¥ '11QŸ.y];[|0!€*`РA¬]»NÀ§Ÿ~ÊÀíÜ*çBô±í騱#AAAúF/]ºÄÎ;0`€[æ)EGGKjµZ ׯò’¤Òà¨ÁÁÁR£F¤Î;Kׯ_·ck“wÞyGŠŽŽ–<==¥àà`)::Z:yò¤¾ü­·Þ’|}}¥ØØX©Q£F&«ñœ§Ù ^ ÀRĘ@ ‚:‡@@ êB @ ¨s$ Î!@ ° úPöâòåˤ¦¦Úµ À1H pB._¾L¿~ý=z´"Ñ¢ElܸÑê÷+**¢qãÆŠ €5É¥K—èÔ©½zõ—E$8!ÙÙÙ8p€¯¿þš_ýUŸüøq.^¼hÇ–Ù†/¿ü’Î;sþüyþïÿþÏÞÍ€@“âááÁ›o¾Éäɓ͖>|˜•+W*ò¦M›FFFŸ}ö¿ÿþ;sæÌaðàÁ,Y²I’X³f C‡eÅŠäää(®?uêcÆŒáÉ'Ÿäûï¿W”íܹ“矞Áƒ³zõjJBíß¿ŸO>ù„K—.±`ÁÖ¯_o¶½™™™L›62eÊ};wïÞÍêÕ«¹xñ"“&MâÈ‘#&מ8q‚åË—“””ÄâÅ‹õÑš;v,ƒ bΜ9°gÏV¯^­¿~Ë–-üüóÏúôÂ… ¹pá;vìà…^`èС̘1C=W Ôn„œ˜#FžžÎ×_mRvòäIþ÷¿ÿ)òæÏŸOff&_}õ$//þýû3þ|âââøóÏ?éׯŸþ9ï½÷žâú±cÇK@@ `ÿþý|÷Ýw >œŽ;2hÐ –-[ƼyóYˆM™2…ýë_\¸pACŠŠŠèÞ½;ÇgÈ!œ9s†»ï¾FC`` ^^^„……‡ŸŸŸÉõgΜáÍ7ߤ_¿~œgÏžE’$‚ƒƒ¹páÉÉÉœ;wŽV­ZÈ™3ÿß¾ý½2ßÇqÎÙdÍhj¥°ÙR(I±eÍ$±f%9v¢ü”¤‰òȉ9BI9!;ÎEìí@räÔû@vßî«û¾ÙU—º¾¯ÇÑw¿>ß÷v°^½?ïÏÔÖÖ244„ÕjeppðË¿±ˆ|u€D~s###”––²¶¶öåÏšÍæüµÕj¥¨è¯¿ŒòòòN\™L¦üu{{;wwwÀÛÔìì,^¯¯×ËÔÔ‹%ÿÞÊÊÊ ?™L¿ßŸ¿¿Éd"Éd>ý]***òáç}Íîîî5455‘Éd0›ÍƒAR©[[[ŒŒŒ‹ÅØÙÙáøø˜P(@4¥ªªŠššB¡‰D‚\.÷éšDäû¨$ò›3™L,..211A0Ì?_\\ÌÓÓÓ—Öù¯Çÿ”N§©®®Àf³‘H$èêêú|ác³Ù~ÞÎf³„Ãá‚Ö{_ó}Ž —Ëq{{‹ÍfÞ:B©Tг³3ööö¸¹¹a~~«ÕÊää$‡ƒƒƒÒé4§§§ÌÍÍQTTÄØØXÁu‰È¯¡ˆôõõQ__ÿaÈï÷suuÅÃÃ///lnnòüüüS÷yо¸¸`ŸH$@$aaa!ÿúãã#ÛÛÛŸ^7“N§I&“ÀÛ@õåå%½½½×ÚßßO2™äüüœ××WÖ××1›Ítttäï¹»»KYYN§“ÎÎN®¯¯999!pttD6›¥±±‘x%%% o¯X,†ÇãÉoßÝßßÓÚÚŠÛíÆçóa±X/¸&ùuL¯ïgQEDDD B 1 1 1 1 1 1 1 1 1 1 1 1 1 1œ?˧™2Ö>ÙIEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/compressed-writing-shuffle.svg000066400000000000000000001003721231437614300272030ustar00rootroot00000000000000 Writing with small (16 bytes) record size 10 3 10 4 10 5 10 6 10 7 10 8 Number of rows 0.0 0.5 1.0 1.5 2.0 2.5 3.0 MRows/s No compression zlib lvl1 zlib lvl1 (Shuffle) lzo lvl1 lzo lvl1 (Shuffle) bzip2 lvl1 bzip2 lvl1 (Shuffle) PyTables-v.3.1.1/doc/source/usersguide/images/compressed-writing-zlib.png000066400000000000000000001211741231437614300264770ustar00rootroot00000000000000‰PNG  IHDR@°AàÚ²sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwX××ðïÂÒ;R¤HQ‘¢QlˆÁ+ˆ½~VÔ$j¢Á¨‰%‰…X¢b‹Qì-&¶4;±¤ISzg)÷ýc_F–º »¬.çó<ûèܹ3sv¶pvæÌcŒB!¤Q’w„B!  B!„4:”B!¤Ñ¡ˆB!%@„Bit("„BH£C !„BJ€!„ÒèPD!„F‡ B!„4:”B!¤Ñ¡ˆB!%@„Bit("„BH£C !„BJ€!„ÒèPD!„F‡ B!„4:”B!¤Ñ¡ˆB!%@„Bit("„BH£C !„BJ€!„ÒèPDȆ1†èèhÈ;±ÄÅÅ!77ÀûØ ÅZ6!!III² PTT„èèhË|[’(((@tt4òòòäŠÔ¼}ûiiiòƒZQDdnçÎX°`H[rr2FŒ .ˆ´cþüù5®ï«¯¾Âܹs¹é|ùùùu^GE<>>>6lJKK¥¶^yš9s&¾þúë:/¿`Á|ùå—RŒˆªQDdNSS[¶lANN×võêUœ9sGé»ÿþZ [ZZÂÊÊŠ›^¹r%:$ÒGGGPUU•Â3###ØÛÛsÓÀ÷ß/Ljä#??“&MÂòåË1hÐ ®ýÝ»wØ¿?<==ñLj¼gÊäååaĈ@›6m°lÙ²Z iˆ‹‹Ã¸qã¤ztCMM 'NœÀ“'O°yóf©­÷cfee…fÍšÉ; ÒðåQ|}úôAqq1þý÷_îèÇÕ«Wáêê*rô£¬ÏÎ;©©©())‰‰ JJJ Œ3†ûµœ––†‚‚dff"::`nnGGG,Y²ÆÆÆ„G&ÒÓÓѬY3¼~ýQQQèÕ«TTT*Åûüùs¼zõ vvvhÚ´)Š‹‹¹õT”žžŽ‚‚˜™™qmqqq033ŸÏ¯ô<ÜÜÜо}{.¦ÌÌLî4 LÊËÈÈÀ½{÷ðÉ'ŸÀÔÔ´Ö}]RR‚GáíÛ·°´´„““x< &&ÆÆÆ(..Æ;w`jjŠ6mÚ€ÇãáÝ»w¸ÿ>\\\ ¯¯/²Î¸¸8DEE!77hÛ¶m­qÔæ—_~ŠŠ >ÿüs‘öääd„„„@MM­Úe7n܈àñãÇ000xÛñññxøð!š7o„,99Íš5ƒ²²2×·°°‰‰‰hÖ¬âããoÞ¼AQQÔÕÕÑ´iS®oxx8^½z++«Jû¨¨¨>Djj*¬­­¹í€Ö­[‡Å‹ÃÏÏÕÆžœœ B p¯3”––âùó爊ŠB‹-àèèXåòÏž=ƒ¶¶6Ú¶m+²ŸÓÓÓmmm8;;C]]›WÕgÑÜÜÊÊÊ`ŒáñãLj‰‰A›6mÄz 22‘‘‘ÐÓÓCÛ¶m¹ç=iÒ$®Onn.Þ½{WiY4iÒ„›ŽŽŽÆÓ§OajjŠöíÛ‹¼†„T‹ÒœœœØ’%K¸iGGGöûï¿3>ŸÏbcccŒÝºu‹`IIIŒ1ƦNÊ À–,YÂ455öüùs6cÆ æååÅclÁ‚ŒÏç3555¦§§ÇôôôØ£GØ7–™™ÉclÇŽLOO3†©¨¨0fbbÂþüóO.¦üü|6räH€5kÖŒéëë3---n[U b¬´´”1ÆX||<ãñxìðáÃ\Ÿþýû³ùóç3ÆÛ¾};³±±aŒ1¶oß>¦¦¦Æø|>ûXFFÀ&L˜À´´´˜¡¡!SQQa ,¨qGFF²V­Z1}}}æèèÈø|>sqqáæ«¨¨°Q£F1===fhhÈ0___¶bÅ ¦ªªÊ ™††[·n·Ìõë×fffÆ™ššëÖ­ËÎÎæú˜šš²}ûö1ÆËÊÊbØ¿ÿþ[c¬mÚ´a«V­ªvþ‹/¸×»¼üü|f``À‚‚‚j\E<`˜··7SVVfZZZ `Œ1–˜˜ÈTTTرcÇD–[³f kÙ²%LGG‡`ºººLOO 81ÆXJJ 4hSQQaíÛ·gJJJløðᬸ¸˜1ÆØãÇ™ kÒ¤ ³··gÊÊʬÿþ•ž—®®.;yòdÏ£wïÞlÊ”)lúôéLUU•`ùùùìÍ›7¬gÏžL]]µk׎ñx<6uêTn¹ÒÒR¶xñb¦¤¤ÄŒ™––Ó××g?fŒ1öÛo¿1---Ö´iS¦©©ÉlllXXX·¼ëß¿?[ºt)÷Y|úô)KOOgýû÷g˜µµ5ÓÓÓcÌÏϯÚçPTTÄFŽÉTUUYëÖ­™ŽŽÓÑÑa‘‘‘Œ1ÆÆÇ&OžÌcìÈ‘#Üg£ì€[~~>›Ÿmذåçç³ÒÒR6aÂ6bÄn™%K–0cccvùòeƘð‹ÚËË«Æ(**Š`=bŒ1öóÏ?3---6räHƘðžž;{ö,cL4bŒ±… ²~ýú‰¬³,òôôä’Ã3gÎ0UUU–••Um,'Nd]ºtá’±ììl¶gÏn¾ŠŠ ëß¿?‹ŽŽfŒ1̰‘#GrÛÙ¼y3³´´ä–y÷ˆà¦“““™™™ äÚ$M€âããöâÅ‹jûT—•µÏœ9“µjÕŠ©««³æÍ›³7V».ÆÞ'@ ,`iii,''‡-[¶Œ)))±—/_2Æ„xÝÝݹeŠ‹‹™••·îÇ3ìÍ›7"ëž8q"óðð`)))Œ1ÆD’´!C†ˆ$<ééélÿþý•b3f óõõ­ñyôîÝ›©©©±eË–±°°0öðáCVRRÂȼ¼¼XFFcL˜ «ªª²Ó§O3ÆÛ»w/SUUeÇŽcEEE¬¸¸˜íß¿Ÿ=}ú”½~ýš©©©±µkײÂÂB–’’ÂÆŒÃìííYQQcL˜)))±Y³f±7n°§OŸ²ÜÜ\6kÖ,fmmÍ®\¹Âc¬°°¹¹¹Õ˜>|˜©««³˜˜Ƙð3ròäIϽe PE›7ofFFFܲ˗/gNNN,..ŽÛ·NNNì믿®q?ÂcTDDß¾}†œœ\»v ]ºtºº:z÷îk×®žëÓ§ÈrC† Á–-[àêê ''§OÔÆÐÐ ,€ºº:x<¼¼¼péÒ%n~pp0FŒ¾}ûø|¾ÈaöªØØØ yóæ¸~ý:àäÉ“X·nþüóOäææâÙ³gÈÍÍ…›››ÄñΛ7«…:t(c• ¦ËÓÖÖFjj*W®­­iÓ¦‰ô™>}:¬­­€;éïïÏmgàÀxóæ "##OÉÙØØàÞ½{ ž={ ¡¡Áͯ‹ØØX())¡E‹uZ¶,®ƒâÁƒ˜3g-ZÄ:­É¼yó```---,]º<—/_Ì;!!!xþü9àܹsHKKƒOµëËÏÏÇÁƒ1f̤¤¤ <<YYYpuuÅ?ÿü@ø:$%%qëÕ××ÇäÉ“+­ËÎί_¿®õ9øúúbÕªUèСÚ¶m‹„„\¸pcÇŽERRÂÃÃQTT„:p1ìÚµ ={ö„··7ø|>”••1yòd899áÈ‘#àñxð÷÷‡ªª*š4i‚9sæ <<·oßæ¶ûé§ŸbëÖ­èÚµ+œœœ ©©‰`üøñðð𨪪V:…Z‘¶¶6 qç΃ÇãÁÓÓæææ5.÷Ï?ÿ`ñâÅ8räW¸k×.Œ9¹¹¹Grr2ºwïÎ=oBjB5@¤AôîÝŒ1„††âÚµk\BàîîŽY³f¡¤¤¡¡¡ðóóY®¬~E´µµ‘——ÆJKKW)GŸ>}pýúuŒ=?ÆÅ‹±wï^œ;wÙÙÙèܹ3tuuë+ǃ––w¹yU-Z„—/_ÂÙÙFFF8p –-[&Rt]^YýcŒkÓÖÖ®ýÉ“'>|8JJJàêê ())¡¤¤¤ÎÏ%==ÆÆÆ\T]|þùç\ý½½=nÞ¼‰Ã‡cæÌ™b¯CSSvvvÜ•a]»vE‡°}ûvbË–-˜2eJ¯ÝëׯÁÃæÍ›±mÛ6‘y666€eË–aæÌ™prr‚©©)ŒeË–ÁÖÖV¤¿™™RRRj»âg¢,]½zu¥yeu<‘‘‘Õ^]‰N:‰Ô¹¸¸€Ïç#22Ý»w¯r»ïÞ½Cvv¶ÄŸ™~ýúaöìÙ?~<”••Ñ¥K|ñÅ}ú`Ñ¢E8sæ † Œ5 ÇŽƒ¾¾~­ ê{™–-[âÊ•+Ü/÷_~ùˆ‹‹ƒ’RÝö._¾­ZµÂÙ³g¹‚ñðððzÅ©££ƒ´´4”––JWË–- ºË@kkkãåË—­+??QQQ°°°àÚæÌ™L™2ÿý7~þùçJË•½LLL»w«k•Ûiݺ5þý÷_<}ú·o֭߯[ñé§ŸrG„ʤ¤¤Ôzô¤*e1œ8q‚Û?UõyõêU•󌌌*Ñ‹ŠŠª±ø&˜˜(Q¼ªªªøùçŸñÍ7ßàÖ­[8}ú4† ‚{÷î¡cÇŽ•úÂËË îîîX´h×®¡¡mmm¬\¹ãÆ“(Bº ž4 ¾}ûâäÉ“ˆˆˆàþXhii¡sçÎX¹r%:uê===‰×koo_å•"’êÑ£Nž<É%Aøï¿ÿj]ÎÃà ظq#¼½½£GÆùóçñ÷ßטI+vxüø1·ÎÉ“'cíÚµHIIAzzz×™œœ .ù)((ÀÛ·oë§……ŠŠŠ¸«ª$aee…-ZàÀ\[ii)þøãîH…¸BBB ЫW/®mܸqPQQ——úöí+rµ– ÔÔÔD^/ccc8::bÇŽ"ë.--EDD€÷¯KëÖ­áãダ€ÄÇÇWO(::ZdxqÙÙÙÁÜܼR ÅÅÅÜ{¹{÷î¸xñ¢È6333ñîÝ;ôìÙ‰‰‰xòä 7ïüùóÐÐШ2!)£¥¥…æÍ›s§áû#99¹Æx_½z…ÂÂBcèСعs' ªMÐæÌ™ƒ‚‚ìÞ½[¤Çã¡W¯^صkW¥1”$M†IãDG€HƒéÓ§V¯^ž={ŠÔòôîÝßÿ=¾úê«:­wøðáðôôä~=Ï™3§NëY»v-;;;XXX 77¦¦¦µ^RkllŒ¶mÛ"::ýúõØÚÚÂÉÉ OŸ>E×®]«]vèСðóóÔ)S`aa¾}ûÖøG§&3gΟχ‡‡”””pìØ1øúúÖZÇTøûû#%%ZZZ¸téÒÒÒÐ¥K—:¯ÓÖÖ6668{ö,fÏž-2ïå˗سg7ÖΆ `hhˆÙ³gs—¨oݺÆ Cff&¬¬¬ >Ÿ€€€Z·=uêTôíÛ=ÂéÓ§1kÖ,|òÉ'Ü|uuu|öÙgX¿~=¶nÝ*²¬¶¶6<<<¸ ÕÕÕ±téRìØ±C† ALL ÜÝÝñîÝ;\¹r}ûöÅæÍ›1zôhXXX W¯^(..ÆáÇáïï/ò())Á… °víZ‰÷'ŸÏÇŽ;0zôh<{ö ݺuC||<._¾ŒÉ“'cùòåX±b~ÿýwXZZbÊ”)`Œ!88ÇǧŸ~ŠaÆ¡k×®˜2e âããqöìY¬[·Ž;ºTåË—cÚ´ixôèºvíŠ . ==ÎÎÎÕ.sôèQìÚµ‹«ûùçŸ`ddÄýx(ïĉ ÂÀ±páB®ÝÑÑóçÏÇÆÑ«W/¸¸¸`È!ÈÉÉÁ?ÿüƒ¦M›V:-FHEÊ+V¬X!ï Hã`nn>Ÿ±cÇÂÎÎŽk733ƒ±±1Æ/rÈÇãÁÁÁ¡Ê±EìííѺukÂÓ"={öÄ“'OÀãñзo_hiiAWW½{÷æjMÌÍÍÑ­[7‘õÀÝÝ€ð4ÁôéÓѳgO 6 ëׯǽ{÷`ff†ÖøÜÌÌÌЯ_?nŒ@ø‡¾k×®pqqékaaÁÓÑѧ§'bbb‘‘7774mÚÊÊÊèÝ»·È)‡=zTûGÉÍÍ ùùùxýú5222àéé‰%K–ˆ$pnnn"ûXII îîî"GÞTUUáîî---8;;ÃÙÙ?†¦¦&V­Z…®]»¢mÛ¶"¯a÷îÝÑ´iSðx<.öšŽæ%''ãèÑ£ðõõ©-ÉÈÈÀ³gÏ ££ƒÞ½{CGG|>;väjqZ¶l‰aÆáÙ³gˆˆˆ@¿~ý°ÿþ½²ª ,, Œ1|ñÅX²dI¥¾?Fdd$+Õ½ 4ZZZxúô)СCX[[ÃÇLJ+Äåñx1bþ÷¿ÿASSÈÎÎFDDrss1~üxÌ›7OäôßùóçqðàAlß¾½ÆBggçJ§ºZµj…I“&!##/^¼€ššÆŽ‹iÓ¦AMM ÚÚÚ˜>}:455‘Ì™3‡K–ÇŒ„‡‡ÃÐЫW¯)Ôæñx°··IÀÙÙ:uBJJ 444°råJtïÞŸ|ò ZµjUåsh×®ôõõñæÍÄÄÄÀÉÉ Û¶my¿899ÁÁÁ™™™°´´„¥¥%´µµ¹‡¹¹9Úµk‡&Mš`æÌ™`ŒáÕ«WÈÏÏGŸ>}°xñâz×ÝÅÇcÒ*@ ä#—™™ Ç}qÆÆÆÂÙÙAAAðòò’stŠ%%%Í›7dž 0}úty‡Ã)))A‹-àïïÿÙ¦@ @»ví0dÈüðà ²MBˆÖ•”” 66b/óîÝ»æÆ“DvÂÂÂ`ddGGGtêÔ ðòò¢äGŒŒŒˆÏ?ÿœ«‘ù?~©©©•†¥²S¶âœÂ#„HBò÷÷ÇÎ;ѤIîtBpp0tttªì‡áÇ#99¹¹¹˜8q"ë|Å ù¸ „……!22***hÛ¶m•· ÒsñâEèëë׫¦HšÂÂÂPXXXéT©¬dddàêÕ«hß¾=7>!¤a(TtàÀôïߦ¦¦Üø+³gϹsxy£FB³fͰaÃdff¢cÇŽX¿~=ýâ'„BœBê˜4iwÃHMMM”””T{Yiqq1Ξ=Ë œ¦§§‡Ñ£GãäÉ“ /!„BäC!/ƒ_¶lBCC1f̘jG}ûö-Èag[[[„††rÓ/^¼À‹/d/!„ò¡PWW¯õÊWE  ’’,--qýúu$%%ÁÒÒ²RŸ²"éòÿkhh 33“›>|ø0>,2š¬$%%A__Ÿº^RÑÑÑbÿ.NßšúT7¯ªöŠmÅÅÅ•^“ˆˆˆjG°•&@€”””Zï9TI^£¬¬,ÃÐаÚ>ÒÚÇoß¾…¶¶672oUýjW£¸$}jÛVZZø|~•—DW÷•}7”‚ ª÷mÅmÇÅÅÁÄÄDä;EVê³%]^žß_£¼¼<¤¥¥Uù.mU½$!É>ç»EVû¸b[aa!îß¿¤¤$±bÿ¨5øíW›5kV•óÊîH““õmÛ¶yxxpÓ, @Öa2Æ„wkŽŠŠªóò’Ä)NßšúT7¯ªöŠmééé•îÜ=xðàZ㑆ÄÄD¶}ûö://ÉktóæMvþüùûHkWºkzÅ~ µ«Ú¶$$}jÛÖùóçÙÍ›7«œWÝkÂBBBDÚªzßVÜöŒ3سgÏjYêû½ô±|_T|nÝºÅæÍ›Wk<ÒPÕû@’ìcq¾[dµ+¶%&&2SSÓcQ }ñÅlüøñUÎLGG‡Ý¹s‡k›?>›1c7Ý Pjj*+,,¬óò‰‰‰Rí[SŸêæUÕ^±­¸¸˜½}ûV¤­¡öqQQKII©óò’¼F¹¹¹,++«Æ>ÒÚÇéé鬠  Æ~ µ«Ú¶$$}jÛVVVËÍÍ­r^u¯Qvv6ËÎÎi«ê}[qÛÛ·o¯×s—D}·ó±|_T|¢¢¢ØÞ½{kGªzHB’},Îw‹¬öqŶƔ)L´@ À„ pñâE¼~ýÇGPPFÅõéСÖ­[PQQÁäÉ“±bÅ ÄÄÄàæÍ›øí·ßtüò ¡ªªZçå›6m*Õ¾5õ©n^UíÛ”••k¼Á¢,ñùüzÝB’×HSS³ÚáÊHkëëëW:í"ÉûAÚê³mI_£Ú¶¥££#rj°¼ê^£²Ñ†Ë«ê}û±îcI——ç÷…8Ÿ#Y©ê} Iö±8ß-²Úǵ­[‘)L ²²2ÌÌÌðõ×_#>>Ø´iFŒÁõiÑ¢ŒŒŒ¸éŸ~ú ,€‡‡ Xã}›ˆlа²GûXöúôéSçz"###ôìÙSÞa¡P Ð?þXcŸcÇŽ‰L«©©aÛ¶m² ‹ˆáÆhÛ¶­¼ÃPh´eïáÇ044¬ó… ¤vYYYxúô)Z´h!ïPˆP¨¥­ì>±5Ý/öÎ;xöìYÃDˆ”©ªªbüøñòƒòHJJ‚³³s£¸ LaŽÉË¡C‡pãÆ 899É;B$"ðçŸRDi”(’‚ñãÇ7Ø£ ‘–ŒŒŒz%CD¥¦¦BOO|>}­ÊŠ@ @NNNãk".…¹ ŒBä)$$Dd U"})))"£õRôS…B¤ÀÛÛ[Þ!(ÿüstèСÆu—””`Ë–-¸|ù2²²²ðÉ'ŸÀ××mÛ¶œ8qû÷ïÇÛ·oáââ‚o¾ùFFF„§çÖ¬YƒÅ‹cãÆxøð!Úµk‡ü¯_¿Æ?ü€¸¸8Œ3“'O†ŽŽ ((ùùù044Dpp0ÒÓÓ1|øp|ñÅàñx >ÚÚÚÜQ™µk×8€£G")) m۶ŪU«`nnÈÊʆ  @{{{øúú¢S§NܲÇÇÛ·oaaa///îÆ£ß~û-|||йsg@^^¾ûî;\»v ÚÚÚðôô„¯¯/·ïöìÙƒììl´lÙAAAÈÍÍ…¼½½éÞSº˜ìѽÀˆ4Ñ)0RÉš5kðûï¿ãæÍ›UÎðà\\\’’‚yóæ!;;ݺu«¶™Ñ£G# -[¶Ä¤I“˜˜ˆ-[¶~þùgLœ8æææ˜>}:®^½ŠN:!''œœŒýû÷£_¿~044ÄØ±cqñâE¸¸¸`È!°²²ÂðáÃñí·ß"00Ûfhh(.\ˆ-[¶ W¯^033Ã’%K°~ýz®Ï‘#Gàïï#F ""‚+²üâ‹/ðÍ7ß {÷îøòË/‘˜˜ˆž={¢°°{>§NÂĉ1mÚ4âàÁƒ€­[·ÂÏÏçÐ IDAT½{÷Æ‚ `gg‡ 6pÛÛ¸qc·±q#c€ìÖÖµÇòÉ'Ÿ°ÀÀ@ÆccÆŒa½zõbŒ1vâÄ fhhÈõ6lëÖ­›È²ýû÷gÕ®û¯¿þbXHHˆH{jj*ËÏÏgúúúlÅŠ\{AASWWgk×®eŒ1víÚ5€EDDp}vìØÁTUUYjj*×¶råJÖ£GnzÊ”)¬oß¾"ÛôõõeVVVÜtÓ¦MÙøñãY~~>×öêÕ+¦¬¬Ìþùç®-++‹©ªª²S§N±’’¦¦¦ÆvîÜ)²îÂÂBÆc^^^lèСUÎcŒ1]]]väÈÆcGŽaXTT7ÿþýŒÏç³ØØXÆc>>>¬gÏž¬¤¤Dä¹M™2…ÕUzz:ÓÓÓ«óò„Å“˜˜ÈLMMåFƒ cµræælÜ(ûíHZn²jÕ*899áÂ… •æÝ¹sÓ§OióððÀwß}Wíúnß¾ ]]]ôèÑC¤ÝÐÐ?FFF<<<¸v555tëÖ wîܩԿŒ‰‰I¥6 ¼zõJdmmm‘éîÝ»cÇŽÈÍÍ…–– gÏžPWWçú„……AII [·nÅÖ­[¹vMMM¼zõ JJJ0`¾üòK}ú ÿþhÕªU•ûæÎ;°µµ… ׿ááââb͚ܿ5èëëCIéýA[kkë*_B!µ£HÎÚ·>>4vvvøì³ÏðÕW_᫯¾™— SSS‘6cccäå塸¸¸Êˆœœ˜˜˜T9/;;ª\gMc~”OÊðù|0ƪb!š¤êdeeA]]cÇŽi;v,'OžÄž={pâÄ cîܹسg<==1}útØÛÛcÛ¶mذaæÏŸ9sæ`s_UíOðù|nßTEœçJÕÉÕi¢ R­€€„‡‡ãÈ‘#"íööö¸zõªHÛ•+Wмyój¿üèèèJóÊŽŒ”_'c !!!Õ5©Ë—/ÃÚںƫ°œœœ gggŒ1BäaooPVVÆŒ3páÂ$%%aРAøé§Ÿ¸uôìÙÁÁÁxýú5öìÙƒ­[·"%%¥Ò¶ìííqïÞ=®Þ ®]»†ââb™<"T${TD¤‰ R-333Ì›7¯Òx5³fÍÂåË—qðàAäççãðáÃøóÏ?1{öìj×5bÄ4mÚóæÍÃÿý‡ÒÒR\¼x6l€‘‘FíÛ·#44éééX±bRSSáççWïç‹/^ ==?ÿü3Ž;†E‹Õ¸L×®]áââ‚I“&q§ÔbccñÓO?áòåËHKKÃÔ©SÆòòòššÊâó÷÷Ç•+W  ””GGÇ*¹Nœ8X²d ñàÁ¬_¿...ÜeäÃGãÉD¤‰ R£Å‹su-e¦M›†yóæaêÔ©ÐÓÓÃĉñÙgŸÕ˜éêêâìÙ³ˆ‰‰AÇŽ¡®®ŽñãÇsGa¶nÝ KKKôìÙ¦¦¦ Äo¿ý&•±ˆÑ­[7âË/¿„ŸŸfÍšUã2JJJ8qâLMMáääUUUX[[cïÞ½hÒ¤ ÔÕÕñúõk888@[[†††ÈÉÉÁ—_~ PWWÇ!C ¥¥bË–-Už¶311ÁéÓ§qúôiXYY¡}ûö(,,ÄáǹKõ !„HQAµV¬X!òoUüýýaccÿ† êRPP€ÈÈHØÚÚBSSSìå233‘ ssóJ AZZÞ¾} ;;;(++×;Æ©S§"33ÇŽCBBÌÌÌ ¢¢"Ñ:bbb`dd‘y999ˆ‡¾¾~¥:ž¢¢"¼yó<–––µÖ†0Æ mmm4mÚT¢ë"##666ÈÈÈù¶ª’=ª’½¤¤$8;;#))IÞ¡È}RI©««£uëÖ/§§§==½*çÊäËÏçÃÊʪN˪ªªÂÎήÊyÚÚÚ\MPE***°µµ{;<-[¶¬SŒDþBBBàîîN§Ád(%%÷îÝ£Ó`D*(" ÍÊÊJ¤¸˜Yñöö–w j€ˆ4QDÚÊ•+å!„ABˆ¤¦¦¢¸¸XÞa(4@PãØ`„H‚ "–ôôt$''W;ÿÍ›75ÚW@€èèh”––ŠÕ¿´´ÑÑÑm‡†@ãÉD¤‰ "–•+WbÊ”)Ü´¯¯¯ÈÕqÝ»wÇÞ½{%Zç£G`kk‹ôôt±úgeeÁÖÖ÷ï߯4ïÆ•nÏAHC¢q€dj€ˆ4Q ©É; ©ræÓÞ>í}äáX?žžž˜3gŽT†õöì,,,$ZÆÏϾ¾¾øñDZwï^¬^½ºÞqÒ¨Hö¨ˆH#„EÏãÆC‹-ðÝwßÕ{}ùùù¸zõ*z÷î-Ñr#GŽ„ºº:f̘œœŒ5ªÞ±Ò¨Hö¨ˆH"€M›6áâÅ‹5jfϞ͵wïÞ“&M{ 8pàš5k†ùóçK‡ªª*¦M›†õë×cáÂ…PWW¯±ÿ™3gpóæMܾ})))X²d ´µµ±lÙ2‰¶K!¤q¡ˆÖßTj/+‚8p :vìȵ?Üô‚ `llŒ{÷î!..óçχ¿¿—À›››#  RŸ9sæ@CCÓ¦MiWWWG@@,-ß×3©¨¨@]]nnnpss¨©©IðÌ ‘ª’=ª"ÒÄcŒ1yñ¡*»ÕCù[>Täïïøûû7LP„HIFFlll‘‘!ïPÂñãÇ©HƨHÆ’’’àì쌤¤$y‡"sôS…B¤@šQ ‘&*‚&„BH£C !„HAjj*Š‹‹å†BHK“ý½Iã@ !„HAHH233å†BKIIAhh¨¼Ã ‚ "–U«VaÊ”)ÜôÎ;qìØ1nº{÷îØ·oŸDë|ôè‘DE¸YYY°±±Áƒ¸¶`ýúõ˜={6,X€£G¢¤¤D¢8‘Höº(:(÷uC %@D,iiiHNNæ¦/]º„ÿý—›~óæ ²²²$Z§@ @LLŒØ Kii)bbbPXXÈÎÎFûöíqòäI0Æðöí[L›6M$Q#„qwî›6£G––€­-ðùçòŽŒÈ ]FêäøñãÕÎ+,,ŸÏ‡²²²LcPSSÃÝ»wÑ©S'®m÷îݘ1c¶mÛ]]]™nŸòh Ù“æ8@ïÞ7o7nwï•û……Õ{Sä¥PŸÔüü|üûï¿HJJB·nÝмyóûÇÅÅ!>>^¤M__² SÔž=ÀÊ•²ßŽ¥%Pùó­[·â‡~¨Ô¾hÑ"Ì;·R»¬¬¬DÆHzñâúöí‹þù|>óæÍÚ5kÀãñj /##...øñÇEq‡††bÚ´i¸}û6””DXªªªŠ$?‹-Z@GG§Öm"Mt/0Ù«ë½ÀJK§Oß';7nâ-›‘!<fc#q¸ä§P ««+ø|>ìííñÕW_ÁÎÎW®\©¶ÿÖ­[$2²p÷îݱuëÖ†W(+ ˆ‰i¸íUÃÛÛÝ»w禃ƒƒ±mÛ6ôéÓ§Êþoß¾…–––HÛ©S§°|ùr¬]»‡ÂO?ýxyyÕº}}}}´k×Û·oùrÛ¹s'Ú´iCCÃk…öíÛ‡ëׯãîݻؾ}»XI!ÒÔPã•– ^(+ªª€ŠŠðÑ<‰[”•ܾý>Ù¹uKØVWP¤ˆê# '''ÂÓ0–––ܯ²êx{{ã—_~i¨?X¦¦¦055<|ø[¶lÁ¯¿þÊíOq,]º³fÍtêÔ ·nÝÂo¿ý&Vïß¿ÄÆÆÂÊÊ ™™™8~ü8N:U벿üò Þ¼yGGG:õEVQ0aPîúeÉPùĨ¦6I۫뫦èêzz¢ÿêê çÉZD„èé¬'O„‰¢¤ÌÌ€nÝ€„áúÊüéÂBá)»ˆà¿ÿ6!$8søõW`Ë–Møî;`ñbÀÏ?~† zöÚµll6ÁÐPxzO[hÒd ¬¬Bѳ§p™3g6UH~ªÇÀptÜ„U«€+W€ÌL`òäMز?^˜ü”íoggÑåË.…ÿÞoÒœþû￱iÓ&xzzb×®]g@O¦€ÂÃÙ‘‘Û½{wý¢££Yll,KJJb—.]bÖÖÖìûï¿çæ0–žž.ò(ÏÏÏmܸQ&Ï£¡-^¼˜ÙÚÚ²ÔÔÔJóüýýÙ€¸éÁƒ³9sæpÓVVVlóæÍ"˘™™±yóæU»½»wï2,%%…kKIIajjjlíÚµLUU•%''sóÒÓÓvëÖ-Æc•ÖyõêU€Ý»wOŒgܸ¥§§3]]ÝJm4ýaM§¤0Ö©cc@úÿÿ˘šcéLE…1¯òüÆ<Íã1foÏØ„ él×.Æž>e¬´TüýŸ—ǘ²²èúÓÒ>Œ÷ƒ,¦óòòXzz:‹ŽŽfáááÌÄÄ„5 u ^¾|‰`ÅŠðññ©±¯µµ5÷ÿ~ýúáÿûþþûo,]º”k×ÓÓƒ¾¾~µëPkˆÜ àôéÓX·n–,Y‚K—.qí-Z´@çÎÅZÇüV­ZA `çÎÈÌÌÄç¢Ñ¤IŒ5 _ý5F“jûž={›6mÂĉѪU+DDD`Íš5pwwG‡$ÚncUñèZÅ÷:MËw:?_ýú ¯`úÿ„GG.]LLÞ÷/.ŠŠô¹Ó`‡è´@Póüºö/(gfê#3³ìÿ@V–>D&Tü.•δ¦&àâtë¦nÝWW@x1^Ýö¿†`o<{ö~þƒ€»û‡õþÖ´††444 ¯¯¤¤¤Fs‰B%@7nÜÀ˜1c°~ýzŒ7®ÒüÈÈHèééqµ!¥¥¥"—VÇÆÆr…ÀMll,ÜÜÜpóæMÜ,wò{ðàÁèܹ3Z¶l ®ý“O>™™7íêê ]]],]ºqqqpvvÆÍ›7ѬY³j·©££777‘õÀܹs‡y󿉴óù|¸¹¹qEÎ...pvvÆŽ; cccxyyaÅŠæL>Ò®ŠŽúö"#EÛ]]…u@—ñù‡††T6/5yyIŠªûMóssËÖ&++C‘Úví¤œ³3ðìÙûia$Ýmùâ1Ƙ¼ƒ†ÜÜ\4iÒnnnèÚµ+×ndd„9sæ,--1uêT®¾¥S§N8p Z¶l‰ÇcÇŽ …óÿŸ.ã¦üX7ùûûÃÆÆþþþ²yb„ÈHFF†D·"!5;~ü¸ÔÆ &?oÞˆ¶{xëg´µë½‰JI‰0zõ*/^ÜÔ)²¿Æ?_~ù~zòd`ÿ~™oVî’’’àì쌤¤$y‡"s sHYYK–,©±ÏÂ… ¹ä¾ýö[üõ×_¸xñ"š5k†û÷ïÃÎÎNÖ¡B´Æzðèß_X8\ÞСÂËß䬻D”•CC KstéÒ0÷+÷§] ¦ˆ&RWW¯ñH L€Ê¬'ràíí]§èƒooáé•òV®â’÷Ľ˜´P¤Ø("b¹sç._¾ÌMûúú" €›>~ü¸HB$ŽÔÔTìß¿bõØ¿?ÞU¨ýõ×_a``€>}ú  Ê»Úò!úå`Ò$ˆŒ•Ãã›6Ë—Ë/."T±ˆ ÅBg•I\¼xÊÊÊòÛ¶mÃ×_ƒbøðá„Gƒih’Ž´~½ðvå)+ ïCUË®–@ @NN d{=|(š†S ”ÉÙ¥´4ªËü$d¤¢‚[´¨vþ¹sçpüøñJí^^^Urþõ×_abb‚ &pmX½z5._¾ 555ÌŸ?_ì«ì222°`ÁÌŸ?_d¨‚ÈÈH¬^½?ýô“È •€pì§o¿ýëÖ­ã’•V$¤!„„„ˆ=вeÀw߉¶©¨O‡%£@JJ îÝ»×`§Áš7tt„÷b„ƒ2¾~ ÔðUJ>"”ÉÙ³¼<ìo€§¬ÕÕkL€LMME‡bÿþý3fL•ý¯\¹[[[‘hùòå>|8†Šàà` 6 wîÜAÇŽkO__/_¾D`` öìÙõïܹÏž=ƒ¡¡a¥û._¾Œ´´4ôêÕ »wïFJJ 1xðàâèi\Ĉ1`þ|àçŸEÛ54€ãÇO?•Qp ¢¡k€x<á(Óåë®< HQP tîÜþþþð÷÷ǤI“põêU,]ºƒ {ß|ó Ž=ŠÏ?ÿaaapppÀ–-[Ä^ÞÏÏGEVVáÊýõWøùùUÙ?&&<ƒ Ÿþ‰;wî`Ò¤IpwwGaÅŠRB䬤˜6­rò££#¼µ%?&*„V\”%%%;v,°jÕ*‰–ÕÑÑ™vuu•¨0zÔ¨QPSSCpp0áÍU 0vìØ*û¿}ûÖÖÖxõêNœ8'NàöíÛ¸{÷®ÈQ$BBMã À˜1•o¥`h\¹¸¹5@€  !Ç*C…Њ‹NÉYìupùv´Å<%ôÕW_áõë׸wï^¥šIijjJ40œºº:¦NŠ   øúúb÷îݘ}бcG̘1999HKKêU«P\\ŒñãÇK´]B¤-5Ux÷öŠÉ½½°¨–’Ÿƒšàè(ÚFG%@€ðj«ÜÜ\,X°fffÜcÁ‚b¯ãÒ¥KÐÓÓCÓ¦M±bÅ ìܹ®®®Çâçç‡ÔÔÔj‹ŸË())áàÁƒHLL„š6mŠ}ûöáèÑ£hÕª•ÄÛ%¤>Ê×%$½zaa¢}œþ,-å G @§Á#€ÀÀ@V;ãÆ"ÓçÎ™Ž‰‰¤¥¥!!!vvvP«å¶É:uc¬Rû¨Q£ªl××ׯÔnoo»wï"!!¹¹¹hÑ¢E½k—Èǧ°HOÞH´ìß²ÿ++ ojin.|˜˜ÈæÞZe5@YYMз¯p¼˜òºvþüÐ×—þ¶ yÔÂBèÞOS¤("RehhØ`£´–gnnÞàÛ$ÒSZ dfVÀTõoÅ61ï¦@XÏaj*š•=Ê·Iú6öööÆóç@¿~@|¼è¼>}„7<ý@Êý>Zò¨è¢¢ˆ"5Â#Õ%.Õ%2YYÂABq±0A©˜¤T¤®˜™UNŒ*&KÚÚÂþÿý  ,-oøpàÈa- ù8UL€^¼¾×ÕÕå‘J€!u’-ü%|ÿ¾ðÿÿÏŸ‹ÞØócVPDE 5ÑÑ&Bññ©ÈÉÑCù¯Õ €}û„GHý5ô½ÀÊVV@l¬pº¤xüèܹAà RFKBH­RSEû÷W¯î¨Mmtt„¤ „56åÿŸ—',JŽþûö­ð”›´dgááÀ€p __`Û6ÙÔ5Vòª„GÊ @˜üSôq£ˆˆåîÝ»ÈÈÈ@¿~ýªœüøq´nÝŽ¯­Ajj*~ÿýwŒ7®Ö‚i@øëïСC2dŒ* ‘‘‘ ±ÖCj–ð>É)KxÊñËŸèéUŸÄÔô}}a¡³¸Š‹¤$áó,{”%GåÿŸž.é³x?Ð_ïöN¤K^5@€°úìÙ÷ÓTôñ£ˆˆåСCxþü9—uêÔ ¶¶¶8vì`Ñ¢EX´h‘D PTT¦M›†¡C‡Š•¸äååaÚ´i¸uë—]¹rsçÎÅ‹/ ¤¤„ÁƒcûöíT-&Æ€'Džä亯Çlm…µ3’$2î¢"S|¾ð2ôÚ.EÏϯ=IJHa*oÕ*áÝÞ‰b¡BhÅC ©“ï¿ÿÚeÕŸròìÙ3 2sçÎÅ•+W››‹ñãÇã³Ï>ÃùóçåÛÇ¢°ã&æURV€Þ?œ]]éÆ(/»~×vçïŒ a"ôâE*´´ô0`}­Êмj€€Ê УGÂ<^ƒ‡B¤„>©r–û,Ùw²e¾eme{W;ÿÑ£GUÞ?«C‡hÛ¶måõ)+C¹Šó¡¡¡¸|ù2ÔÔÔ0aÂX[[‹_VVNž<‰¢iÓ¦\{FFNŸ> OOÏJËAGGkÖ¬ábùå—_бcGÙiÛV˜$4vúúÂdzg!èÔé} ‘>yÖÙØX–ÍÉ"";»…H %@r–~) "d¾ukõ —/_âôéÓÜtZZ®_¿Ž;vT™Hlܸ¶¶¶èÒ¥‹H[QQœºcMM IDATñ÷ßcóæÍøï¿ÿ`aaQk|:::Xµj^½z…ï¾ûŽkß·o6mÚ„É“'Wº-G||<:tè ’ˆµk×|>ááá”Õ‘¦&Юh²Óº5 ¢"ïÈ>lUÝ ŒH—>Â/}J~¡: ECG€H%K–,ALL îÞ½+Ñm%œ¡òÿ)MMMÑ­[7ܹsGìå}||ðÍ7ßà?þÀ°aÃpç΄‡‡ã³Ï>«²ÿ¼yóŒ:`èС(..Ƶk×’’ÂÝžÔL]]ô-©»ÔÔTèééOƒþÈŒ|¸HMPy†††¸wï~ýõWDDDÀÌÌ 3fÌ€««+lmmë;!’*»X“&T$+ò¬è¢¡HÎ û°¿|~ÍTôøñc|öÙgسgÚ´iSïõݸqvž ÷óóCŸ>}ðêÕ+>|'Ož¬±¿ŽŽfÏžÍMïÚµ Íš5C§Nê3!uE5@²'ï GGá-M …Ó‰‰Â5ML䩪"€ôôtxzzbÖ¬Y=ztÖQTTÄýÿîÝ»¸}û6FŽ)Ñ:ÜÜÜ`gg‡Q£FÁÔÔb/{õêU,Y²Ë–-ãNÅBˆ´ðù Ê££@/J€`íÚµˆŒŒÄ©S§àààÀ=–I0¢ÛÒ¥KáêêŠ:ÀÅÅcÇŽÅøñã%ŽÅÏÏ>„¯¯/xµ ²áíí 777ØÛÛcРAøþûï1cÆ ‰·IH}¥¦¦¢XQn„öHKK“k OƒÝ¿/Ÿ8HýÑ)0˜1c T©ÝÌÌ 0gÎäææríkÖ¬F¹A`Ž9sssÜ¿qqqpvvF=jܦ½½=BBB §§'Ò>}út´k×®Òi,mmm„„„ÀÉɉkóóóCDDЮ];ˆÿ¤ ‘"ª’=y×T­HxŒ}(·3üð¬X±BäߪøûûÃÆÆþþþ !R’‘‘dddÈ;B>¡¡@Ïžï§€çÏå´%%%ÁÙÙIIIòEæè!„"¦víDoñòeåûÁ‘%@„"T${B ŽмùûéÒRàñcùÅCêŽ B‘‚dffÊ; …–’’‚ÐÐPy‡Aã)J€!D ¼½½©ZÆä=P™Š…Ðt%ØÇ‰ B!DtH1ÐeðRpêÔ)DGGË; ÒÈ @ùk:ù|@Y¹úþ…eÃÙ© {Éž¼ïV¦bôø±°H‚['’}RëÉÓÓ666òã£vãÆ tëÖMÞa|Ô"#íÛßO+++VÚÚÂéêöñš5k&ÀF€Æ’½a °°ŒŒ€”át^žðj0¹†E$DãÕ@œq€ùLž 8ð~zäHàøqùÅCˆ¢ë×øë¯÷ÓÁÁÀرò‹GZh BÈG##£r²Cw!D¶hDè%@DîÃ/ Y:tÈÏ?me%üuZícÙ£q€dïC¨ ÝìãG ‘»Ó§OË;„ZPè´OåbLÚDzGãÉÞ‡2@W‚)ªªÕ‘]XPþž±JJ@t4Ь™ÜB"¤Q()Ž ]þèkBðÿ÷þhQ !ä£PñèOÿþ”üÒ”•6mDÛè(ÐÇ… "wá—†,äå ëÊ«®ø™ö±ìQ ì}H5@Bì("rGõ)usì•õ~ÚÄ:´ê¾´ej€dïCª¨ècG!¹óóó“w¥Š§¿¦LTTªîKûXö¼½½å‚ÂûPîV†®û¸)Ô Í›7£GhÙ²%¦L™‚K—.ÕØ¿¨¨ .DëÖ­Ñ£G=z´"%¤~^¼*þž>]>±ÒXµm+zÅeD“#¿xˆd* ÇâÅ‹ñçŸÂÕÕ#Fü{çEµ¿ñw[R!„@z/†*`¡*U@Då"zñ^ÀbDƒ ~rEŠÒ‚ .õRUZèÒ;¡$Þ³m~L¶Ìîf³IæìÌÎ~?Ï3ÏÌ9sf曓d÷sÞsÎ`¤¦¦[~òäÉøë¯¿°iÓ&ÄÆÆâŸÿü'Ž;æÅˆ €ü)eÁ±õç¹ç€Æ‹/OuÌò±Gn  aC[šã€s礋‡(Š@‹-‹/¾ˆÆcܸqhÒ¤ 6lØà²,ÇqXµjfΜ‰F¡W¯^xõÕWñÓO?y9j‚ü)¥Ã`ÿLKjý¡:fy€Ø#7@Fh_FQÈžüü|\½zO<ñ„Ëó©©©HKKC‹-¬y­ZµÂ•+W¼"QùSJÇæÍÀãǶtÅŠ@Iöªcö :”BeŒÜ<@¡}Å  ñãÇ£M›6èÝ»·Ëó‹¾A‚ƒƒ­yaaaÖ| qqqˆlöPšÒÞN »¿bñÚk@… ò‰Ò”ö§´PÅ âó$½mÛ6ÄÆÆ¢[·n˜;w. üEÎ=uêTlÞ¼ûöíCÕªU]–¹sçêÖ­‹üü|–.]ŠŸ~ú @3A{‹¤¤$T¯^]ê0|‚;w€úõ³Ù–wê”s3¼#TÇìIMMEDD´Z\Ë ½^œœDFFJŠ•¤$áìÏAA¼Z£‘.¦ò@3Aû(f³&LÀü+~ jÕªÐh4HLL´æÝ½{5kÖôF¨„äOñœ+„â§]»’Å@uì ÈÄ9z€ªWªU³¥ øQš„üQŒ2™L2dnݺ…}ûö!**Ê©Ì;ï¼cê^¡BôïßË—/äååaýúõ4—‡?Å3Ìf`åJaž§Cß©ŽÙC öÈÑÚWQŒÊÎÎÆæÍ›±uëV„……A¥RA¥R¡iÓ¦Ö2›7oÆ9»1ŠóçÏÇ–-[P¯^=<ñÄxî¹çðÒK/I>A”È®]€]ƒ%‚ƒ×^“.‚ xÈí›(¦³ºbÅŠ(ÉÎtïÞ=Aº^½z¸xñ"Q±bE„……± ‘(ò§x†ãÜ?Æááž]KuÌò±GŽ €¯¢˜ òP»vm?Bþ”’yôغU˜Wܧ® :fy€Ø#G@ÈW!DHùSJfÕ*~D M›O?íùõTÇì!{äêjÔˆŸÚBJ àÐá@È@á8vѺ_!Ôj~]0{¨Hþ"$Çæ›(W¯ÚÒÀ¨Q¥»Õ1{h-0öÈm-0{¨Ì÷ DHùSÜãØú3hP¥JéîAuÌò±G® €/BÃÉ!Jñddë× óÊÒýEuÌšCŒ=rõ$€|j"óË/@~¾-]§Ы—tñášV­„Ë_ܼ deIQ2$€É!Jñ8v½õo¸,-TÇì!{äìªPhÒÄ–æ8àìYéâ!J†!9äOqÍÉ“ÀéÓ¶´ZÍ  ²@uÌò±GÎ €ºÁ| @„ä?Å5Ž­?}úµj•í^TÇì¡y€Ø#g@È× D2$/÷ÿØCsÿ„¼!ä["$‡ü)ά['4PV«¼øbÙïGuÌò±GÎ ÀY]¸ÐŸ„|!DHùSœqìþzã   ß!9äO±a0?ý$ÌÃüLuÌò±Gî €/Aˆò§ØØ¼xüØ–®Xc…ªcöˆ=r÷$€| @„ä?ņc÷×ë¯AAå¿/Õ1{ÈÄ_ð5h„…ÙÒééÀÝ»ÒÅC ‚ wîü!Ì{ûmib!¢l¨TÀ“O ó¨Hž"$‡ü)<+Vf³-ݾ½óiY¡:fy€Øã  €F‚ù $€É! /|V®æ‰9ó3Õ1{ÈÄ_ðäòÊ1µAˆùS€]»€ÄD[:$xõUñîOuÌž¡b¸Õ ·ø‚ ä+P AÈGóóðá@x¸4±Q>Z¶ÎÜ~û6‘!Y8D1"$Çßý)[· óÄ^øÔßëØˆ=¾â š5æ=+M,Dñ"$Çßý)«Vñ ZhÖ èÒEÜgø{{ò±ÇW<@uƒù$€ÉñwŠc÷—Ø­?Õ±7 y€Øã+ €F‚ù²@8|ø0€ã8ìÚµ [û ÂÇ9p¸zÕ–F’.‚ ÄZ€älÐ’%K°|ùrÀ† 0räHÌš5 cX¼’âÏþÇÖŸÁƒ¨(ñŸãÏuì-ÈÄ_ñÎèâE@¯—&Â5²@ýõ—µ©séÒ¥øòË/qìØ1>|&“Iâè1ñWJv6ðßÿ óXé{­coB öø’(2¨[×–6xDÈÙ     ¢  ÇG—"W(ÇqxðàÄÑbâ¯þ”µkÜ\[ºn] W/6Ïò×:ö&äb/y€ MašºÁä…lÐàÁƒñÁ _¿~hÖ¬Z¶l‰û÷ïãÞ½{¨^½ºÔáD¹qìþzë-~!‚ ”ù€älЀ°páB<ÿüóX»v-àÌ™3˜={6t:ÄÑbâþ”sç€ãÇmixóMvÏóÇ:ö6äb/y€ &wd%€222°}ûv«Ç祗^ÂÔ©SñÄOú÷ïwß}WÊ ø£?űõ§O V-vÏóÇ:ö6äb/y€g$÷É `ΜP˜Í FbÈÇqœÔAXÈÈÈÀË/¿Œ«W¯bÔ¨QxóÍ7ѰaCÉâ‰ì B þüèß_85þÆü0‚ ”EåÊ€}£ÕÍ›@½zÒÅãŽÝ»çŸÂÃ'#3sŽÔá0GV-@+VÄž={pàÀh4ôìÙ]»vŪU«——'uxQ.?滹žyF(~ªW ..‚ Øñä“´œ}@þÖP,+d¡^½zøâ‹/pëÖ-L›6 ;vì@ݺuñüG•:Ü£k“’’““Sâjò‰‰‰¸ÿ¾ ¯bÅŠhÚ´i™ã&JGRR’¬ŒíÇ{x&Oæ0u$, ˜1xï=ökx‰…ÜêX‰¤¦¦"""Z_ù£ðAôz=rrr)u(¥FŽ#Á»¿–&©m ÐÒ¥KQ¥JÄÅÅᥗ^Âõë×±víZ 2ÄíuF£7nÜÀèÑ£ñý÷ß—øœ… bÀ€;v¬u[°`X?árò§œ9Ã{xÞz˵øyõU~t×øŽøäUÇJ…<@ìñUàÜtö¬óÔÞÄ`þ÷?až¿ Ù~„wêÔ £FÂîÝ»ñßÿþjµ}ûöE… Ü^·{÷nÌž=ׯ_÷¸gèСX²d‰ae@þ”Œ `út~™ “Éù|óæüœ>Ý»{?61C+¡C‡J‚âñePÍšüD¨av6pãPB'3öíìõzµjüÔIȶ¨U«VX´h._¾ŒqãÆaÿþýhÖ¬Y‰]`}úôÁ¾}ûèÈðŽV­âGw}ÿ½³ø þïÿø–!_?AÈ9ù€…ôþ=R#ë÷ÚµkXµjV®\‰;v //É@kÖ¬AÕªUѼysÌš5 f»1ÎGž}û›=”.Ú~Žo>ÿìY uë}=Úþ­Çv~øp`åÊ}øøc@§ó~|b¦-u,—x”˜NMMŇ!5rŠO éÝ»w æ’:žÒ¦+W¦7o–&Ž6olŸwWÑ Á>ÌŸ?þù§à;PÉÈVMŸ>mÚ´Áo¿ý†-Zà·ß~Crr2~ýõWQŸ3nÜ8\ºt çÏŸÇ·ß~‹eË–aΜ9¢>ƒp·ý)™™À„ @»v®çâhÚøãà×_¥[»KlÈÄžøøxZ§1™™™>뜻»®_—&Ž'û±?ÁÁ@Û¶ÒÄ"%*Ž“Ò†UoŠövK³¿· Û.°{÷î¡E‹ذa*Uª„Û·ocذaøòË/Ý^wýúuÄÅÅáâÅ‹¸xñ"âââpëÖ-ëù®]» Z…:vìˆÏ>û qqqøðñjÕ*Lš4‰ÙÏE8#ö›†É:L™ÂOVX¿>?K³+ñÓ¨°s'°~½²Å?¼ÍI ­Æ_] Ì‚F´j%Ì;}Ú»1\½Ê¯EfA§ú÷÷n rA¶hîܹxñÅqâÄ Ì;6lÀ¡C‡ðÅ_¸]#''·oßF—.]Ð¥Kܾ}[Ð/?iÒ$ôèÑÚž1crss±k×.h4œ>}1ŽV}‚)bøS22xÏÎßþÆç|öY`ÎàÂ×僃Y³xPïÞå~¼ì!{h öøò<@¤ æøQЭ¿–¡?"Û.°û÷ï; eoÞ¼9¢¢¢ððáCÔ«WÏåu111nŒcëNÿþýÑß_å¯L(ë5W¯[·òÝ[‡ñ zÂàÁÀüù@:ez¬OB󱇦Þ`/ÏdAnÈ_»¿  víÚaÑ¢EèÙ³'¢¢¢`4±zõj˜L&Ôñ§o.ŠÁ<È žmÛ€k×<»N«ågw0xñE¾ž B ¤@IIB+€J ä½çË Ù  I“&áСCÖùy¡ÓéðŸÿüG`Z&|wëT¥¤ð ömÛìÚ%œ¹Ô‘‘@Ÿ>¼èéÓ¨TIÄ€}Z Œ=´{|y-0 O>ÉO8h™jçÊ ?(a‘QؼY¸üFûöüH4E¶ÿ©ضmΜ9cÖ±cGèõz©C#DfÓ¦M‚.š¿þ²µò9bû (‰æÍyÁ3`Ð¥ o8$x똟øøxtïÞ•+W–:Å’’’‚„„Ÿî áçºz•O›Lüg^‡ìŸMÝ_Bd;+._¾ŒþýûãÆ^yÍä ùui¶nåç»}۳뀮]m¢§~}–QAˆÃ+¯ØæÝ€~þñ¶ÏÌʪTìÛ.\à_í¡y€$Äl6ã³Ï>ÃŽ;ƒÉ“'£iӦسg†Š1cÆH"!wî'ò³.{:nµj@¿~¼àyá~­.‚ _"&F(€¼áÚ±C(~5r?þ†ìÐòåËñ믿â³Ï>Cbb"† †üã˜:u*,X€7ß|Sê ‘˜9ÓÒ$› xJL o^0€o&f°œâ!{ÈÄ%x€iŒÐÔýåŒìþS÷îÝ‹éÓ§côèÑ€`ÆŒؾ};ž{î9iƒ#DãÖ-~vžMlþ” €ž=m][þlÒ ò±‡<@ìQ‚p@çÎñ^GVã{ôz`ûva   ÌÌLÁ›j£F0fÌ? cÖ,ûy{Æ¢re~=š€=¼3"Ÿ ñÚˆ=J˜jÔà»ó-kææò £6nÌæyññ¼ÈBµj@§NlžåKÈN™Ífìß¿€sçÎ!##k×®µ–1b„Tá"pó&ðÓO¼~à×ã"‚ðbbø©=,œ9ÃN9v È®µÉ—jРvìØ;vògÏžm=&äÛ|ù¥pÖææÍ“0dùSXB öˆ=JñÎèôi`øpñŸÃqüü?öP÷ìþS.\(uC\µþ<óÌ&¨TÔEÃò±‡<@ìQŠðžúØ1àáC[:,Œ÷X2@„²zøYQ—,¡/fÖøay€Ø£à=äØýÕ§ÈæY¾†ìÐ_|S§N¹-C+[û&7n«W ó>ÿœ†µá4n yy|:)‰7EW«&îs¿._zIÜûû2²@ÇŽÑ#GðÖ[o!**JêpqÕú3x0ùS¼Õ1{ÈÄ%y€Ôj ukáâ¤gν{‹÷ŒË—ùµÆ,ètüD²ì|à¿üò >ùälܸIII9r$¦L™"Øßãúu`Ía^l,ßúC-zì¡:fO||<2=]­—()))8tèÔaˆën0Çûî݈qŸáËÈNEDD`Ê”)¸xñ"š7oŽÞ½{ãïÿ»×Öÿ"ØàØú Ä“?…=TÇì:t( £$à,€NŸ÷þ4û³{d'€,bРAèÓ§Ö¬YƒmÛ¶IQF\µþ÷‡ ‡e ЃÀñã¶´JÅÏÿCØ¥JLLÄûï¿æÍ›C¯×ãêÕ«˜0a‚ÔaedæLÀd²¥Û´¾‰øÃªÃRCuÌžÔÔTí›9 ÑÑëõHKK“: ÑhÕJ8!áµk6StyÙ¼™ŸÈB‡´¬#²@_~ù%:tè€ððp\¾| ,@:u¤‹(#×®?ÿ,Ìûüsašü)ì¡:fy€Ø£4Pp°pög³™_L ¨û«dd'€<ˆääd|ùå—¨Zµ*T*•ÓFøŽ­?mÛÚ¼?ȟªcöˆ=Jó|‹¸=btƒefò0•n IDATëÙCÈÙ  ;w‚ã8·á\½ üò‹0ϱõ‡ ŸaáÚ¾0léÆfÍÊ_¥!;D(W­?®^Þȟªcöˆ=JólF‚Q÷—g"˜põ*ðŸÿóbc]—% {¨ŽÙC ö(Í8  óç…/Ž¥¥°pXKœP1"˜ðÅÂâví€_t]–ü)ì¡:fy€Ø£DPÕª@¶t~>ÿYVöî²³méêÕNÊ~?%Cˆ+W ÿý0léÆfÍÄK©"ÊŒcë§óþ8BþöP³‡<@ìQ²(›¢Ñ_e‡Q&Μáwö”µõ‡ü)ì¡:fy€Ø£dà,€þú p§© ;…y$€<‡Q&[J3ï#äOaÕ1{ÈÄ¥{€¢¢€š5mé‚~éâØ½[8R¬zu S'vñ) @D©9uJ¼Ö‚ ÂFiŒÐŽ »òsžAˆ(53fÓ:•nÞGȟªcöˆ=J÷žû€Ìf`Ëau•@D©8yÒùŸÎQ•ò§°‡ê˜=äbÒ=@€çèÏ?ùUã-„…={²‹K‰ÐZ`D©p;;/¼P¾{’?…=TÇ졵ÀØ£tà¹r|§éÛ`“R¡ Âc€­[…yåmý!‚ lÔ¯„‡ÛÒ©©À½{Îåhø{ù!DxŒ£Ñ¹KàùçË_ò§°‡ê˜=äb?x€T*àÉ'…y§O ÓýܸaK”m6‡á'Nð3ŽÚ#VëùSØCuÌò±Ç<@@ÉÝ`ŽÿÎÝ»lcR"Šõ?~iiièãÁð¤-[¶`×®]¨T©F…Æ{!Bß±õç™g€^½Ä¹7ùSØCuÌò±Ç<@@éu• ŵݻwË—/ÇÈ‘#±ÓqŠLüøã˜8q"bŠþâºté‚Û·o3ŽÒ·8~ؾ]˜GóþA°ÁJLäGãZP©øùˆÒ£8”’’‚ÄÄDDFFzT~îܹøî»ïðöÛocÖ¬YèÙ³'V®\É8JßÂQì<û¬¸Ã-ɟªcöˆ=þà€-Ζ¾u ÈÊâ'¡í؈Žö^lJBq(&&±±±èäÁ|àYYY¸víºtébÍëܹ3Nœ8Á2DŸâØ1`ÇažØ­?äOaÕ1{ÈÄñ Wtç8àìYþxãFaYêþ*;Š@¥ÁòVn7æ022Rð¶‡ØØXÁfÒÓ#G ÓuêÄ¢GqŸgïO‘úçUjÚRÇr‰G‰é¡C‡bÁ‚²‰G‰éüQà’:–i¾Ì–>s˜2%® ²>oÛ¶mˆE·nÝ0wî\ ø*޳_ÒR9Lœ80þübË\¿~5Baa!ŠfZ¹r%–,Y‚cÇŽYÿ8ÿhü…£Gù‰í‰ºu“$‚ ¿á›o€I“lé7ßäG{eËkÒÄýb©e!)) 111~Ñmî×-@UªTÀ÷Ý[xôèªU«&UH²ÂQ÷uíÊFüøÃ?šÔP³‡<@ìñàÚM£¿ÄÅïК5k¬Ÿˆˆ´oßÿ³›àæ÷ßG/±Æwû0GŽ»v óXÍúLþöP³‡<@ìñà,€.\pþL&T>×vèÐ!,[¶ ÇtìØãÆÃSO=¨U«FY³föìÙƒ×^{ ýúõÃÇ‘œœŒ}ûö!""¯»À^xøã[º[7¾û‹ ‚ðuëwî¸>W£pÿ>? ^Lü© Lq!6lØ£GÆèÑ£­yõêÕ³¯[·Õ«W·¦{öì‰cÇŽáàÁƒˆŒŒD÷îÝìÍeÇŸ Å@k~Ax›˜˜âÐÀâ‹Cq¨zõêãHgGW/€ºuë¢nݺ £ò->ÿ\˜îÑxî9vÏKJJrû;#ÊÕ1{RSS­Vq«²A¯×#''ÇãyÞ|˜çy,P÷Wùñ;ážÝ»ùÍÖ=€äOaÕ1{ÈÄòÎ> ááLGB” zU!¬Ü»¼þº0¯gO~æg–Ð:Uì¡:f­ÆY ÌBq¨o_~x¢|P (,† =²å©Täý!‚ŠºuŠó©ûKH€±cÇ@>ýxúiöÏö‡ÑRCuÌšˆ=þ4ÇV € _?ibQ$€,\ÄÅ óúõ¾øÂ;Ï' {¨ŽÙC öø›p@Ý»ó ¢ü(n 1ñ‡y€ä}>öK¿4lÈ·¹jz%‚ ¼Gj*pý:‘¤§mÛ²{ÍDø÷ïÆ ÅOh(?Ý:‰‚ é©\™ßñ¡.0?ÅbzNN¶å©T|WX‹ÞÅÞ4¤†ê˜=äb?z€vòSÆŠV ±2u*ðòËÞ…ü)ì¡:fy€Øã ‚ärƒR=@‹ï¼#ÌëÛض P“$&‚ð[üÉD_w~Æ¡CÀĉ¼† _~!ñCAøô•çGÜ¿ *?Ó³?¼iH Õ1{ÈÄòbBÈO(,äý=ö¦g@Ó³#äOaÕ1{ÈÄòbBÃàý„wÞŽæ}ú©4¦gGh*öP³‡Öc¿­F°…Z€ü€%K€åË…y}û3gJAAH …sø0ðþûÂ<¹™žÉŸÂªcöˆ=ä"ÄD&_ <§éÙò§°‡ê˜=äby€1¡y€ÜàËóéõ@×®ÀÑ£ÂüuëxQDAŽÐ<@„Ïóî»ÎâgêT?AR$?ü,]*ÌëÓ˜5KšxJÂÞ4¤†ê˜=äby€1!¤0þüÓÙôÜ ¼LÏŽ?…=TÇì!{ÈDˆ y€Üàk ‡víø½…¾+¬eKéâ"‚ |ò>‡^ÏOjh/~~¦g?A!„Bxï=àÈaÞ”)¾azö‡7 ©¡:fy€ØC BLH)€¥Kæõé|ù¥4ñ”ò§°‡ê˜=äby€1!|Átô(?ß^oËkÐ8q¨TIº¸‚ ߃<@„O”Äû~ìÅOH?Ó3‰‚ ‚(@>ŠÅôüà0åJß3=ûÛ†ÔP³‡<@ì!!&$€|” ø9ì™26LšxÊùSØCuÌò±‡<@„˜È rõ-[¼ý¶0¯wo`ûvùNvHAÈò²åØ1àw„y ÿù‰‚ ‚ðúÊô!’’€!CœMÏ7ú¶éÙÞ4¤†ê˜=äby€1!ä# ü¤†®LÏ­ZI“X?…=TÇì!{ÈDˆ‰VêϘ08|X˜7y²ošž;v¬Ô!(ªcö õ…i×}œèèh 8Pê0…@-@>ÀŠÀâż޽¯¾’&‚ ‚ðuHÉœãÇñã…yõë+ËôLþöP³‡<@ì!!& ù U&Éɼ鹰Ж§Ä™žÉŸÂªcöˆ=ä"Ä„ærƒ”ó @€ãÿú¯¿Ç{=‚  y€Éùàgñ3y2‰‚ ‚&CV®.æ½ð‚rMÏIII¨^½ºÔa(ªcö¤¦¦"""Z­¸«Y…Y¸”r —_²îo¦ß„J¥B€&šèÔ:ë±ã¦Ó¸9'ÒuAÚ hÕì¿Nôz=rrrÉüY„ò!$3NœÆæ5h¬]«Ó³#›6m¢aÚŒ¡:fO||<ºwïŽÊ•+—éú¤œ$'¡s)åd?(ùb0®ý8Ì}a.‚uÁÌž‘’’‚„„¿ oæÌ0q&˜938ŽÞµbq¯pàÇ–se-÷8ÿ18­8cÈäo{€=Úµîݳ兄GŽøþd‡Að_fw2ï8‰œK/!½ ]êðÊMÓ¨¦øyÈÏh[£­Ô¡”‹Û·1ëÀ,dë³a2›¬"ÄñØ"LJ:vw’î'ᑹGù†~j’ F#?©¡½øø9€Hü¬±¼ràošö{3gö(µJ JZ#8Ö¨ø´Z¥ÐæÌ"ô&=®¥]s:WR® ߘ/ux̸œr–uÂÌ3ñq—}î÷Ìâ‹0e÷äès¤‡` ™ðÁÀÂ<1=“?…‰Y‰˜°cþwú@(œ„ŠTo˜¬bÈ^YŽ…SI‚ÊÝ=,› *~¯R1Içgæ#Ù˜ŒËé—q3ý&ŒfqæÒªµhP©šUi†fQÍЬJ34jŠ Ú Ð›ôN›Álp™ÏjË7äÃ`6Xã5˜ ˜²{ v\ÛÕCV£vxmQê`뺞vßòw¸s ä„" $–/¾ÿ^˜§dÓ³#äO£Ùˆo}‹Ïã?G®!¸ ½ÔQ 1q|3¿ý—§Os@]e´À„èBÐ4ª)šF5ˆ†‘ ¡SëD T\r ¹˜°c–Ÿ^.Èßg?Z/nÅýcDË¢<‹…ÈÌ™ñÍÑo0}ïtY¶ÎY¿El« ¨TE{¨Ç–se-Ç™9í&ŸS0ärƒ7<@K—cÇf»ñúõy34 t Ê‘{G0vÛXœK>'u(„ ªW± {±S;¢¶õËÈÙxy#ÞÞò6RóSÎl= û-Dx`¸‘ÏÅÇñÖæ·pìþ1§saa˜Þu:êV¬[b %ËóÞÆŸæRd Ðãdž   ©Cq˼yÀ‡ óBB€Iü¥'½ SvOÁÒ“K­#;Jƒ¥[ÇòVhÿÆY\žã5–}I&O)»Þ¼ *Ô©XGÐee9®\¡l£ÄäÎKM_B§ZðÆÆ7ðÇÍ?çÖœ[ƒCwaõK«ñÌÏH¡ £Ùˆ9‡ç`æþ™(49·vôiØ?¾ø£¨Ýw„üP”JLLÄ AƒœœŒÜÜ\Œ9ß}÷ÔÅŒŸ>}:¾þúkAÞ /¼€-[¶056˜1C˜§ÓkÖ­[3¼¬ PùYsn >üýC<Ê}ät."0Sb¦`b¯‰Nû&po#æHw#o,›½Á›E:'3µ«ÖFËê-Ѥr¦ÃÁåJÐØõ·]øöè·˜²{Š@\ÜθnqÝ0õÙ©ø¼ëçeš7H Й¤3xkó[8tÚé\¥ Jø¦Ï7xãÉ7Ê|ÂwP”š4iºuë†yóæ!33íڵæM›0dÈ—å ^{í5Ì›7Ïš§Ó±ïkÿðC¾õÇž  `ýz æ—ä*;WR¯`ܶqˆ¿ïòü«-_żÞó°iÍ&iåÕ"ªQñÍýrö·”†õë×£{»²Ï¤TPab§‰èY¿'^ßð:Î?:o=gâL˜u`~¿ñ;Ö YƒF‘Juïòx€ô&=f˜…Ù‡f»ô n:‹ú/BÐ¥¾7á›(Æd4‚³gÏ¢iÓ¦€O?ýwïÞÅš5k\^3eÊddd`É’%.Ï‹í2›y¿ÏÒ¥ÂüÐP`ëV [7QCøÆ|uð+Ì9<z“Þé|£ÈFXÔzÕï%AtÁSh*Ä”ÝSðíÑoºeCt!˜ßg>Æ´Ã<Žã÷ã­ÍoáÂã N碂£ð}¿ïñJ‹W˜Çá ø“È·&ipãG ×ëQ§Nk^½zõp÷î]·×íÝ»ÇÇøñãqÀq:€ÌÌLddd6{èÞkzãFú >óŒ­l¯ú½ðað‡ø¼ëçÔ ®·Ô±œ~¥¥SSS±bÅ qï¿t)pãpð °v-âFæûÐGŒDÜÓOýú½{Ï?¸ø·©gŸž~q11@ÇŽüómÚ ®ys eK Y3 IÄ5jÄ;­S¨]quêÕ«U«•+#.:盩+T@\T Õòëò¨Õ|:070††"®^= &èÞxùeÄõí |ò ^Xw ׂ>ÆÀ#O çM ÍC n”Àkµ¨~¿ñ{‰õ±|ùr¤¥¥yT‡îB«¶Â×~ gâ3‹þ_j„ÖÀÆW6âÙôgQ%¸Šx¿/M9rqqq˜8q"~ýõW˜L&øŠé{ðàjÖ¬‰œœ„„„/^Œõë×cÏž=ÝcÚ´i¸pá6mÚ@œ.°Ü\`Ð À1„ڵݻÆË|kŰdÉò¹ÁÌ™±8a1¦í™†ÌBgA_=´:æõž‡W[¾Zì=¨ŽÙ³~ýzÏ×3™€ädàÁàþýâ÷龿0™xßÇÙ6Ç´ãÆò¼Z DEÙ¶Ê•##yZü© L1&è*Uª ,, /^D‡W®\Aƒ <¾‡N§+vÄXYÈÈà_ÎŽæ7lÈ ¢'žíQ> }1Ï©‡§ðÏmÿDƒ§sj•cÛÅW=¿BD`„ÛûŒ<HLä»+ ~_ÚcOË€FÃkµ|뀫coœ³lE-n7P•}DÜСCùƒŒ ×bÆþ89™ÿ‚% 5Qyü†Ô‡Àå‡Å–P’ý9À›E›kŽm D¥""Ü‹$ǼʕË-š|ÅüÔ:£FBll,-Z„`Íš5غu«µLÛ¶mñÊ+¯`òäÉ€wß}ƒFÆ qþüy,X°?þø£(ñït|§øYbF~B©Ù³­„I¨†=ÄlNÍî!©" ò‡j@Ío5k Ä¢ÌÛ{ÇÖ²ìl -ï¶KOw}ì*/3“ÿâö=€ô*"ö¢éÆ h.¯»Y¡ Êâºqƒ?·o óŸ{ضÿÌ"„?…çfúM¼»ý]츾Ãåùa͇a~Ÿùˆ‹v}ƒ‚`Å àÿþ¸sGpj ªa¶¬Ð€G³©T¼ÑØ^ظÚW®\®n9ŸÀlFvr"flšˆýg6!2¨”~_D*`@•.¨cǃ¤$$¤§c`TŒ*W3nà^^LjÀ¤âýD&5`V«Ð ª1Z×l ­.ÐÖ%«ÑØ6wi˱EèY6Ç´ãæî|y¯5›yÁ˜š ¤¤Ø6ûtj*PXþu¼&‡‡cŽ›DJJ+€.\žxèÐ…Ý·/°aP¡‚¸ñÊ@oÒcîŸs1ëÀ,—‹1Ö¯T û-DŸ†}\ß 'X²ø÷¿âŒ‹*P£ߢÀûd,Çž¤KSF«µIF›/ÈñØ[ç,fÖ’6“Éã–·„‡»5ÑÑüïÂO}îøõ¯»m,2 2œÎi;óûÌGˆ./oÄøÿGRŽóß{“ÊM°bÐ t©ÝÅ!Ëœ¡@rI®D“C——¿ ú‰“'yÏOªÃ:€C‡?ÿÌ7„#ûïìǸmãp)å’Ó¹M>yúL{všëYœÓÓï¾ã7»¡Á4`Ø0`êTÿ[c¥¬x"–,‚É1/*Š_Ð(¯´x]jwÁ¨£°ïö>Á¹e§–aßí}hUµ6^Þèt­F¥ÁG]>Bl·XÙÍzîUBCù­n]ϯÉζ ¢ôk×phÂfáÉ @"pè¿„EV–0ôh`Ù2þ;ˆ(%y€ÌœÆäó‘oÈì Œ‚¼ßoüŽUgW¹¼O÷ºÝ±¨ÿ"4jê|29™_KeñbþƒË:0j0y2Ш_Ç"þœŠÆbj.%©©©ˆ ¤ÕrR;¼6öŒÚƒ¹ÎÅôøé‚™Î¯?¾Žë÷¯­é­ª¶ÂŠA+Ð>º½—£UaaüV¯ kׯ ?ùÒ¢ÿÕr²k?Éa^ž0ÿÝwùs¥wß‹˵ÀLœ y†<äò«Ïu)Lì÷ʼnO˺ZYº4T ©Š¹/ÌÅßZÿÍùäÝ»À×_󪺠Àù<À÷³¾ý6ðÑGüdSEÐzkì‰÷| Â-j•Ÿ<ý žoð<^Ûð.§\æOäx  ŸÔ©uøôÙO1í¹iŠYSŽðärCI ù YG NÊ<&JÆÌ™‘^n(V±bÈäYÒžæYÒå$ÞBÞn÷6f÷šJA•„'¯^åGt­YÃ{Z\¼ó0q"o®%…oÌLJ»>Äâ„Å‚üv5ÚaÅ h]ºvÅ„&B$JdõjàÍ7§¾ø×¿€)S¤‰É—0˜ ˆ;‡¯~…Û·¥GRž¬ö$– X‚Nµ: Oœ;Ç+éu늖L˜À79V¬È>X‚ð2´°¨ÿ"ôoÜßüwdd ¶[,>êò´jú #Êýõ”E‹øïû¶3• X°€ 'ЧÐTˆ§W`ö¡Ù¸›Y´Pm€PIà µJ Ú ¨ «àѾUµV×~œðƒüØ1àË/ùyŠk ­Y“_êŸÿ‚ƒKŒKI>+¹’ššŠˆˆhitú7ê“cNâÞã{xªÑS%_@%@ÿ©¥dÎ熟~eÔ(ibò ŒXvjfšûÙ÷…'/`ä]´’€ë‚¬ .•@)í>@SŽá~{÷ò->îÖ®«_Ÿ76]ª¡…äby€Ø£ÊS!ùR2ÐHêH%@¨L›æìí þóÞM8“oÌÇ ?àÿÿæ¸^ã'à©Ô­a(Áº`` Ñ…¸ÌwwÎ’ïÃa·mã[|Ž-¾L‹¼¹lĈ2 +$ñÃëZ`3¢££1p`I«„gòŽãm ó+Tàн{K—œÉ5äbñ‰Å˜ûç\$ç&»,¬ ÆØöcñq—Q=TAÝ3F£maPËæ*}õ*?k³»uº:t>ý4ˆ†Aˆ  à85Þz ˆ‹懇ó/îÏ>+IX²%GŸƒ…'âßþó»,ŠñÆãÃ΢jHU~´Aq ‹X(,´­8^žtqb¤¾;ö2 ]/šW)¨>èü&Ôá붺ñsÛ(†_ŠÂq³,j¿µl |ò Ф‰ÔQAø$€<¤I`÷n V-©#‘–”¼Ì;2ßÿÙz×ëPEGáãöðÞ£º¨0÷7`Ǽ1¸’€âשÒhl+ŠsìN—_š²eXGŠ5äby€ØC BLè?Õž|øýwÿ^aàQî#üûÈ¿±èÄ"äès\–©Rs#G`DBt³¿)~…r •*#F`Sp0ÆŽïZ´ÈPLø"äby€ØC BLÈ䆨ØX,_þwœ?_ÛoWHÊIÂ×~% KgÈsYæIsU,H{ Oï¹õ¥Ëîo¨Ñ}úo¼ È‹‚ Bˆ°2jÔjT¬ø©ÔaxÙ0çð,=¹ùÆ|§óAFàÍ;‘˜|½:꿘¶º¿aË–üìů¿PW A!1$€J@§Ó—\ȇIÉKÁíŒÛ‚ífúMì½µ×åJêîï^ÅпLÌNছ+* xõU^ø´m[l1ò§°‡ê˜=äby€1¡ÿT…ãJàØo¹†ÜïQ+ øÛY`Ìyê?2€_½´t:~.›7Þ àÓ%@þöP³‡<@ì!!&ärCll¬`/GÄ8®¨`†\Þ8 ô¼ ¨Kú+‰‰á[z^{ ¨R¥LÏ$‚ ¤…<@„l`%pt& z Ô°ì펟¾ „;÷€ ©V÷ô¼ñкu™â ‚ äC!Çó å^ ’÷²î!áA®§]ˆ›;™wŠr^&›©‘í,p,é¨<@U–¶¿€àÅùÖž>}üäOaÕ1{ÈÄóq Ífmùf3òM&äÙççÙ;¥]\“çâú³€ðnÝ$þɽý§z™ô‚t$tù™ÖVû®«ê¥ U<:Ш¿9vEÙ ý+¾‹¹H`ä;ŒjqÊ+R\Ù²”÷zKQ.´*Â4„j4Ójù}Q:¸H¨ÕP©P$:i»ã•JvwΚ.:Öøág.ÍDX¹67UÊ6ÕŽ3@“&¼¡¸ys E ~ߨ‘(ÃÉ ÿa4â‘^GƒuÿØ!mÙ§õé«U*¨‹ö*Wéʨ×yvi€ºë(R $B‰¶H<Ø‹§½ƒˆqw>L£A µb¾ }ë–@™ÄOPMèXDNóæ@Æd*vùSxrM&«hyìBÄXEN‘Ð)• HK"#aâ8˜€Äˆøde!!%þk[+JÑzaßB¡S© -Úì]¦Õjʺ»ÎUYo·øÛ<@[H•‡ €fÍlÇ"xê×÷9Ž'8y&r‹&ÕìM&äM¦å*Ïñû¼Œÿþa/½MÑ«ÆîCV8ç9î‹Ê¸<çp/µJ|w {\Òùb¯Àyxln׊“g2±ûå:øñúI/‡£ÿÂѯát¾¤´ÝõG¶nE§'ŸD•Ê•Ý(ŽFíeÑ $h-0BLHyBHˆMèØ·èÔ­[*¡£7›‘f4"Í`@šÑˆtûcƒ…EóN˜&{SiÊNÂÅȪՠ_?d–´îQ.dð…¡v<9ÖhD)¯õ‚èè1jógø;ÑÑÑ$~Ñ Tó'LÀÄo¾sL&^¸äå!Í`à…Œ˜±ßÛ‹œ\–oø„߮բªN‡ª‚}y•u:¨U*A —½èu™çB[Z°œòìÒ€óÐÝ ¢‚ ¹@¨?û,Ö9ƒt;QCfN‘)ò§ø;AjµKñb¿·?_£¨ÅgeõlP7ŒèÐZ`ì!!&ôŸZW+WÆÕÌL©ÃZ• Á BÔüäY!ECR{»ó®ò\]³vÙ2¼Þ¿?Lv“«‹ºáŒŽy“°•TNP| …e“ ðè¸4e=ºøQS*v­8a òþ>7 µÀØC טóÍ0å˜ø-×ä|l—gÎ5»-gÈ2 {~w©$¯@ó¹!663D\W§R!R§C%­Ö¶·;® V; /öÆ^ã*°Îj+Ô…A„#œ‘n&‡½‘L(öœåØ©Œ‹ò0Pê`54!h‚5P‡¨¡ Ö@¢±æ«ƒÕPÈãóŠ3s0ç›aÎ3ÔNj ˱`ï"ß)Ï…p1çšÁ™Åý_¾«3W‹zO9B-@e ¼H¸X„L¤V‹JE{{aã˜*Æ>˜ fpzf=¿ç ¶c§½µ¬Éù¼s}Þ ˜]5ÔµÁÕ‚±V 4µ¡ ¥aü„2áŒLÙü‹1ÛÈÁ¥÷Ũt*¨´*§½Z§v™ïQÇóZ83ÿ¿ÏŠþÏ åL¹2ÝC®¨´*@òD49žà‘P±ä»,[@ëšÉ@%ðúùóxï7­6Žs_pFNЄ(hbÌ͇)'™¹&¤çš…M’¹vM’¹&˜ K1Òàh+jX+µX+Aµƒ¬Ç–½&¤t"‰æbRë˜3q0e™`Ì4Z7S¦C:Û3c.,ßU²‚h@/ ¬0ˆ|ä# %¯Æ9³Œ€7Ö`$|@%ÐEB&¢0׌{¹&Üq!tÌzÿQøÆ #ŒFäþUü ‘ÚJZ^YD‘ƒ@ ªu[ó4  g*æÍµ¨9f¾i梲f»<iWåź^®E@õèªéP=U ÒŠoP–£ˆ3 Å‹@¸d#fÓE­.rà4N£ Ú áR‡¢X2‘‰+¸‚.ðÎZ`¾‚:P M¨†ßBø½:Ä9ϺwS.-? ñ½ã¥þ‘¼y€Ü‹n3ºI†"ÑUÖYEQ@õÀŒ’›Üõæ’›ç‹ZÌäò¥XjT|ÝT@@µ~owlJÕ ‹ÒA¥–f4—9ß c–‘0–}¶]º8á’!OñB”k7žV•Æù8ŸwWÎÅ9û<ÎÌ »¡rü2E{Ù¼˜ªM‡®¶àbö–.¸âÎ…Ø ;1£Òˆ÷9@k²F¥SA †*Àa_\~€Êí9W×@áÃBÞ+DabÑþ^¡hýÙ†T ©äœkEY…À† )·­l Ò¨ «¢+Q(T €.RÇqd ”l“P¼Øï³‹ÉÏ2YÏqFå)•FÅ¡„i{m˜Ö–vw.T¨aki4Úµ:í^›òœ7rP©‹¼Aº"o®”i­ªô׸¸‡\—+猜[$ðì8æ]À¥)Nĸ|ˆ;wJ¡õªràÀ 6 ½zõBbb"òóó±gÏ„‡»PÚòòòò¤AñP³§°°d©d ÇqÐëõR‡A(EµM›6 ±±±øù矱ÿ~â短:d©R©Ð¿üþûgÍ–-[ðàÁƒ2_¿dÉQ˺+SÜ9WùŽyÙÙÙN"³<2¥!%%ëÖ­+óõ¥ù;w‡v[F¬:Þ¹s'nݺ嶜·êØÕ³KCiG%=ëðáÃ8wîœËsÅýŽ ÈsõwëøìÜÜ\FOÂ.7å©ãÒ^/åç…ãïÈ`0 7×ý¡báêï 4”¦Ž=ùlaUÇ%Ý[É(füíÛ·Q¯^=" €7…®\¹?üðŽ=Z¦ò±±±X»v-š6mÊ<þ¤¤$T¬XAAAeºþöíÛ¨[·®heÝ•)|Ç<£Ñˆ¤¤$ÔªUËšwæÌÄÄÄx{yÐëõHIIAttt™®/Íï(++ F£Ñ­YS¬:~ôèBCC\l9oÕqq1zJiG%=+-- Z­Öe·vq¿£ŒŒ @ÅŠ­y®þnŸ}ùòeÔ®]!!!Å^ÊSÇ¥½^ÊÏ ÇßQVV’’’иqcb/®þJCiêØ“ÏVuì˜WXXˆÓ§OûE«b<@ƶ³F£)¶YÑ“ò#FŒðÚ—AAȲ¾ˆûŠ@–ÙW³³³­Š===U«V-sù¦M›z¥õ‡ ‚ 'žxGEŸ>}'OžDÛ¶m­enܸˆˆDEEyTÞÉÉɘ1c0"e²{÷nlÞ¼aaa7nj×®-uHe‡Sß}÷רQ#...Žûꫯ¸ÈÈHîæÍ›Öó5kÖä¦M›æqyw$&&r§Nâ ÷É'Ÿp?þø£è?ÁqÜðáùfÍšIŠb‰åâââ¸[·nq·nÝâ ¥IqìÝ»—{þùç¹óçÏs¹¹¹R‡£H233­ÃÛ·oçÞ|óM©CRiii\Ïž=9“ÉÄ;wŽ1b„Ô!• ÅŒ€÷Þ{³fÍ––†C‡¡^½zÖó“&MB=Š-ß±cGèt:§û.Z´:t@›6m0kÖ,p‡Zµj¡M›68qânß¾®]»zågôe8ŽÃðáÃ]Žvøþûï­uüå—_ZçSyï½÷ðÉ'ŸÐÜL¥àÏ?ÿÄG}ä”ŸššŠ‘#G¢I“&xá…pìØ1ë¹{÷îáöíÛˆŽŽ¦–L¸víÞxã §ü¼¼<Œ;Íš5C÷îÝ­“ö}ûí·>|8N:…û÷ï{;\ŸeôèѸzõªSþŠ+ЩS'´nÝŸ}öÌf3ÂÃÃQ·n]Ô­[Ë–-ÃgŸ}&AľÇW_}…mÛ¶9åïÛ·={öDÓ¦MñÖ[o!++ HMMÅž={žž.ø~õI$`²`Þ¼y\—.]8ÜÕ«WçÖ­[Ç=ñÄ\BBwþüy®eË–Ü7ß|ÃqÇ™L&î£>â^ýuîúõëR„î3Ì;—ëܹ3À©®Ö®]ËÕ©S‡;yò$wîÜ9®yóæÜwß}Ç}ñÅÜ–-[8Žã¸§žzJа}ŠÄÄDn̘1\•*U¸^½z9ïÖ­7~üxîÖ­[Üòå˹°°0.--Û¿?·`ÁnêÔ©\ÇŽ©È YYYܸqã¸ÚµksM›6u:?räHnذaÜ7¸7r¡¡¡ÜÕ«W¹˜˜nñâÅÜÿû_®S§NÜ… $ˆÞwX¸p!×µkWwòäIÁ¹;vpÕªUãŽ9Â]¼x‘ëС7sæLëùøøxîÝwßõvÈ>ÇÖ­[¹!C†pjµšûá‡çîݻDž††rëÖ­ãnܸÁ½þúëÜ Aƒ8“ÉÄMœ8‘{ÿý÷¹Æs(zq ÄqÜÉ“'¹øøxN«ÕrW®\œëß¿?7oÞ>žû補Э[·8NÇegg[ó:wîÌ-]ºTPnèСÜ_ýå•x}‘ÂÂB.>>ž›3gŽ“ÊÉÉá‚‚‚߯ ã>ÿüsî¹çžãòòò8Žã¸ p+V¬ðjܾƙ3g¸øøx.<<ÜI½òÊ+ÜŒ3¬éM›6q 6ä8ŽãÌf3×­[7.99Ù«ñú"7nÜàâãã¹öíÛ;  ¯¿þš{ñÅ­é{÷îqZ­–Û¼y3÷Þ{ïqÇq©©©\ëÖ­½³Ø(Æ],Æg•Êy…ë«W¯âý÷ß·¦[µj…+W®àðáÃ0¨S§Ö­[‡^½zy-^_¤]»v\×ñµk×ðá‡ZÓ-[¶ÄåË—“uêÔ ³gÏf¨†nݺáúõë8sæŒàܵk×P§N„††ZóZ¶l‰+W®`Æ hÞ¼9òòòpóæMßoÖfH@@ºuëfFÞ»wï4h`ÍkÙ²%.]º„~ýúáÛo¿Åˆ#°eË–ÿoïþc¢®ÿŽ?ù5.~Ç‘'Äæ…i è„ùcRÂPºÃÖ˜ÓÕZm¹Ì0X3' uÕ²–P9Ò,³µœÌŸ¹h-„püˆ_#8´XàçûóS'”Ƚ¸{=þºÏç}ŸÏçu﻽yñþ¼ßï•••·,æÙ(&&`ÒËÚl6¬V«º½hÑ"ZZZ¥ªªŠäääœý+þETTÔ¤k•555±hÑ"u;<<œ€€|}}©««£»»›ææf‡÷ÌF’]‡ÝnwX`.00¡¡!‚‚‚¨®®¦¯¯+VíÄ(g·¾¾>‡:Öjµ 244Äm·ÝÀúõëžK°Ûíj]^¥Õj±ÛíøúúRRR‚ŸŸUUUß…˜ºkÛ o/ìv;¯¾ú*{öì¡´´”]»vqûí·;)ÊÙo²öBQúûûvøgJLÝnŸ0»K«Õ222BII ¥¥¥ÜqÇìÞ½ÛIÎ I€®ÃÏÏÏaqÄ¡¡!¼½½Y¸pá¬Ï~ÿ+üýý'Ô±Ãb\’ý®­c°«ÕjIMM%55ÕI‘¹ŽkÛ ÿ-kµZ¼½½yùå—™k™¬½€ñ?ÐëÖ­sVX.åßÚ‹ØØXbccÙÌr©Y`7ƒÑh¤³³SÝîèèÀh4Nz+GLÏduøà®\¹@EE‹ÅÉQ¹©ã›/..ŽõA£ííí?~œŒŒ 'Gæ:ÂÃÃyøá‡)//ÆðûŒ#GލÛo¿ý6­­­ÔÖÖòüóϳvíZ¶nݪ®ž+„øo“H¶~ýz.]ºDMMÍ„²††>þøc‡};vì```€`±Xâ©§žbÇŽDGGsúôiRRRøè£Ø¹s§Ãñ7nÄl6£ÓéÈÈÈ ®®€/¾ø‚uëÖ‡Õj¥¤¤„ââb`<ËÍÍeÕªU´¶¶ª×ÿ»ÑÑQ’’’¨¯¯gÍš5455‘˜˜ÈÈÈÁÁÁøùùFtt4Z­vÂñMMM’’BCC\ºt‰„„~ûí7¬V+_}õ•úhòòòÔãóòòغu+ÃÃÃäææ̉'ÈÈÈ ))‰gžy†ÞÞ^¾ýöÛ)?Bç‘§Á á¼½½yóÍ7ÉÏÏ'==ý†ã7xñÅñ$¢««‹½{÷Dqq1êû>Lhh(v»òòrâãã),,dÛ¶mdee ÑhÈËËcóæÍ˜L&Ž;†Ï¤qÿüsZZZ(,,$ €^x€ÐÐP¾þúkùæ›oÈÏÏÇÓÓ“gŸ}vÚq !n é ¬X±‚yóæ9ŒJLLäÂ… \¼x‘+W®P+Gån½IDATUUÅÈÈÈÿu«ƒ¢ëëë9|ø0iii¤¥¥±mÛ6µü÷ßçÀS>ï²eËhll¤¶¶PÝÐÐÀòå˧ë“O>Imm-çÏŸGQ*++ñòò⡇R¯yðàAüüü˜3g>ú(ÍÍÍœ>}š¤¤$Ž=J[[÷Üs999Üÿý7”T !œG !ÜDQQ‘ì­9sæ°fÍ¢¢¢ áÈ‘#·¼¦cùòåÌŸ?Ÿ˜˜rrrÔqGÅÅÅ„„„Ùl&44ÔaVÕõÜ}÷ÝìÝ»—ôôt"""X¹r%ï½÷f³yÚ±.]º”×^{|ˆˆ¶lÙÂþýûÕ  àëë‹ÅbÆ{¼V¯^ÙlVoßõôôðÀ0þ|,X€¿¿?Ï=÷Ü´cBÜ:ÊÕ¹¨B!„nBz€„Báv$B!„Û‘H!„nG !„B¸I€„Báv$B!„Û‘H!„nG !„B¸I€„Báv$B!„Û‘H!„nG !„B¸I€„Báv$B!„Ûù™àêÆF¨P„IEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/compressed-writing-zlib.svg000066400000000000000000000746131231437614300265170ustar00rootroot00000000000000 Writing with small (16 bytes) record size 10 3 10 4 10 5 10 6 10 7 10 8 Number of rows 0.0 0.5 1.0 1.5 2.0 2.5 3.0 MRows/s No compression zlib lvl1 zlib lvl3 zlib lvl6 zlib lvl9 PyTables-v.3.1.1/doc/source/usersguide/images/compressed-writing.png000066400000000000000000001241221231437614300255350ustar00rootroot00000000000000‰PNG  IHDR@°AàÚ²sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwX×úðï²KgTÄŠØÁF±K‚±DlQ½h¼Ñ{£QcÁ‚%j,ÁÞƒ-s%EAADAzïœßûc`X–ºËÂò~žg3gfÎÎ.»ïžyçcŒB!¤QSv!„Bê@„Bit("„BH£C!„B €!„ÒèPD!„F‡ B!„4:B!¤Ñ¡ˆB!@„Bit("„BH£C!„B €!„ÒèPD!„F‡ B!„4:B!¤Ñ¡ˆB!@„Bit("„BH£C!„B €!„ÒèPD!„F‡ B!„4:B!¤Ñ¡ˆz†1†ÈÈHäää(»)UÌÌL%mÏÍÍ­Ò¶ïÞ½Cll¬"›ÈÏÏGdd$ ~¬êÈÉÉAdd$²²²”ݹùðá’’’”Ý B*EQ¸;wbÑ¢E¼²¸¸8Œ3/^ä•>| .¬p_}õ,XÀ-áñãǼ:·nÝ‚³³3Þ¾}[ËÖË×¾}ûðÑGqË>Äõë×yuÒÒÒ`mm¿þú«Ž[W3½zõÂñãǰ¶¶Fppp¥Û]¿~mÚ´Áƒ¤ÖåååaĈ2_?ÆvïÞ©S§¢sçÎ6lX…Ç …µµu­Þ©©©8rä²³³k¼²¼¼¼0jÔ(Ém¿Ê4{öl|ýõ×5Þ~Ñ¢EøòË/åØ"BÊGQ8lÙ²\Ùõë×qêÔ)üöÛo¼º•þ¶°°@«V­¸åo¿ý¿þú+¯ŽX,Fûöí¡¡¡!‡g ?ÆÆÆ°µµå–8€uëÖ)±EÊ‘O?ý«W¯ÆÇÌ•ÇÇÇ# cǎŹsçxï™bYYY3f |}}ѹsg¬ZµªÒH¢££1yòd¹önhjjâĉxòä 6mÚ$·ý6d­ZµBË–-•Ý Òˆ”Ý¢ú „‚‚üùçŸ\ïÇõë×áààÀëý(®³sçN@bb" ѬY3âÝ»w077‡‡‡÷k9)) 999HMMEdd$ E‹èСV¬X’ž‰ääd´lÙ¯^½Âëׯ1pà@¨««Kµ÷Ù³gxùò%lll`ff†‚‚n?e%''#''Í›7çÊ¢££Ñ¼ysˆD"©çáää„nݺqmJMMå.ƒ’©´””Ü¿]ºt©©i¥çº°°?Ƈ`aaŽ;B ¢¢¢`bb‚‚‚üý÷ß055EçÎ! wïÞ044äí3::¯_¿Fff&ÌÍÍaggWi;*³}ûv¨««céÒ¥¼ò¸¸8ASSSæ¶7nÄÇ‚&MšTûØ111xôèZ·nöíÛdqqqhÙ²%„B!W777ïß¿GË–-xûö-òóó¡¥¥333®nXX^¾|‰V­ZI£üü|ûŒ}ôÑGlÅŠLGG‡`Ïž=c³fÍbãÆcŒ1¶hÑ"&‰˜¦¦&300`ìñãÇìÎ; KMMeŒ1¶cÇf``À<<<˜ºº:‹Å¬Y³fìüùó\›²³³ÙøñãÖ²eKfhhÈtuu¹c•Çßߟ5iÒ„1Æ‹‰‰a€9r„«3dȶpáBÆc¿üò ³²²bŒ1¶oß>¦©©ÉD"×ö°””€M:•éêê2###¦®®Î-ZTá9Žˆˆ`íÚµc†††¬C‡L$±Þ½{sëÕÕÕÙ„ ˜322bØœ9s˜ŸŸÓÐÐ`FFFL[[›­_¿žÛæÖ­[ kÞ¼9ëСÓÔÔd}ûöeééé\SSS¶oß>Æciii ûóÏ?+lkçÎÙš5kd®þü9÷z—–Íš4iÂüýý+ÜY>d˜»»; …LWW—`¾¾¾Œ1ÆÞ¿ÏÔÕÕÙ±cÇxÛýûßÿfmÛ¶eyyyL,3L__Ÿ°¡C‡2ÆKHH`ü1SWWgݺucjjjlôèѬ  €1ÆXHH³²²bM›6e¶¶¶L(²!C†H=/}}}vòäÉ Ÿ‡³³3›6m›9s&ÓÐÐ`Xvv6{ûö-0`ÓÒÒb]»ve€}öÙgÜvEEElùòåLMM™˜˜0]]]fhhÈBBBcŒ}cŒÅÅÅ1---–ÍØþýûcŒ­_¿žuîÜ™Ûæ³Ï>cjjjlÞ¼yìîÝ»ìéÓ§,++‹1&ùRX¾|9ïxå@"‘ˆýøã,;;›±©S§²1cÆpÛ¬X±‚™˜˜°+W®0Æ$ÔãÆ«0zýú5À?~ÌcìçŸfºººlüøñŒ1É;}ú4cŒ1ÆØâÅ‹ÙàÁƒyû,€ÆŽË‡§Nb,--Mf[>ùäÖ§O.KOOg{öìáÖ«««³!C†°ÈÈHÆc‡fØøñã¹ãlÚ´‰YXXpÛÄÇdzððpn9..Ž5oÞœmÞ¼™+«nðçϟˬ#+*.Ÿ={6k×®ÓÒÒb­[·f7n”¹/ÆJ E‹±¤¤$–‘‘ÁV­ZÅÔÔÔØ‹/c’/^n›‚‚ÖªU+nß!!! {ûö-oߟ|ò suue Œ1ÆÞ½{Ç ÒFŒÁ x’““Y@@€T=<<Øœ9s*|ÎÎÎLSS“­ZµŠ³G±ÂÂB6tèP6nÜ8–’’“Ã,001ÆØÞ½{™††;vìËÏÏg, €=}ú”½zõŠijj²ï¿ÿžåææ²„„æááÁlmmY~~>cL©©©±¹sç²;wî°§OŸ²ÌÌL6wî\fiiÉ®]»Æc,77—999U9r„iii±¨¨(ƘäoääÉ“,&&†{Š ²6mÚÄŒ¹mW¯^Í:vìÈ¢££¹sÛ±cGöõ×_Wx aŒ1Ê"uÂÍÍ ÁÁÁÈÈÈÀ7ЧOhiiÁÙÙ7nÜ ¹,6hÐ Þv#FŒÀ–-[ààà€Ž;Vxy 2FFFX´h´´´ 0nÜ8\¾|™[øðaŒ3nnn‘HÄëf/••Z·n[·nNž<‰õë×ãüùóÈÌÌDhh(233áääTíö~ñÅ\.ÄÈ‘#Á“J˜.MOO‰‰‰\B¸žž¦OŸÎ«3sæLXZZw9ÒÇLJ;ÎСCñöí[DDD\’³²²Âýû÷áïï={ö@[[›[_oÞ¼ššÚ´iS£m‹ÛuèÐ!<|øóçÏÇ’%K¸K§ùâ‹/ФIèêêbåÊ•¸rå `Á‚ ³gÏgÏžERR¼¼¼dî/;;‡‚‡‡†´´4888àæÍ›$¯Cll,·_CCCxzzJíËÆÆ¯^½ªô9Ì™3kÖ¬A÷îÝagg‡wïÞáâÅ‹˜4ibcc†üü|tïÞkî]»0`À¸»»C$A(ÂÓÓ;vÄÑ£G!àãã 4mÚóçÏGXXîÝ»Çwذaغu+ѱcGèèèàÀ˜2e \]]R—PËÒÓÓCnn.þþûo@ `ìØ±hÑ¢E…ÛݼyË—/ÇÑ£G¹À]»vaüøñÈÌÌDXXâââЯ_?îyRÊ"uÂÙÙŒ1ܾ}7nÜàÌ;………¸}û6¼½½yÛç¯(‚žž²²²ÀCQQ¢££¥°ª4hnݺ…‰'"$$—.]ÂÞ½{qöìY¤§§£W¯^Ð×ׯU[tuu¹ÛÍ˳dɼxñööö066ÆÐ¡C±jÕ*^ÒuiÅùŒ1®LOO¸Dô'Ož`ôèÑ(,,„ƒƒÌÍÍ¡¦¦†ÂÂÂ?—ääd˜˜˜p9R5±téR.ÿÇÖÖwïÞÅ‘#G0{öì*ïCGG666ÜaŽŽŽèÞ½;~ùålÞ¼[¶lÁ´iÓ*|í^½zÆ6mÚ„mÛ¶ñÖYYYV­Z…Ù³g£cÇŽ055ÅðáñjÕ*X[[óê7oÞ •¶»ìßDq0ºvíZ©uÅy<2ﮌˆˆ@Ïž=yù@½{÷†H$BDDúõëWîqãã㑞ž^í¿™ÁƒcÞ¼y˜2e „B!úôéƒeË–aøðá2·‰‰‰Áĉ±víZ.ØÊÊÊBll,>ŒÓ§OóêŸ{B*B©úúúèÕ«®_¿Ž7n`óæÍ€~ýúáÍ›78}ú4²²²jÔSð¿ÄkB(B,WéxYƒ Â’%KpêÔ)Œ1êêê˜0aŽ;CCÃJ¿ jÛöbm۶ŵk׸_îÛ·o‡««+¢££¡¦V³ÎÞÕ«W£]»v8}ú4—0V«vŠÅb$%%¡¨¨¨ÚíjÛ¶-IBwéh===¼xñ¢ZûÊÎÎÆëׯannΕ͟?>>>˜6mþøãüüóÏRÛ•~½š5kؽ{7Ê=N§NðçŸâéÓ§¸wï¶nÝŠaÆq=BÅ*í=)OqNœ8ÁŸòê¼|ù²ÜuÆÆÆR=z¯_¿®0ùðþýûjµWCC?ÿü3þõ¯᯿þB`` FŒû÷ï£GRõsss1nÜ8¸¸¸`É’%\¹¶¶6ôôôðí·ßbòäÉÕj!ÝOê››Nž<‰ððpîËBWW½zõ·ß~‹ž={ÂÀÀ Úûµµµ-÷N‘êêß¿?Nž<ÉAáááøçŸ*ÝÎÕÕïÞ½ÃÆáîî˜8q".\¸€?þø£ÂH^m€nŸžžžøþûï‘€äääï3..í۷炟œœ|øð¡Ví477G~~>wWUu´jÕ mÚ´Á¸²¢¢"œ;wŽë©¨ª   äååaàÀ\ÙäÉ“¡®®ŽqãÆÁÍÍw·–••455y¯—‰‰ :tè€;vðö]TT„ððp%¯K§Nàåå___ÄÄÄH'ÉÞ¡ªlllТE ©6pïå~ýúáÒ¥K¼c¦¦¦">> Àû÷ïñäÉnÝ…  ­­]n@RLWW­[·æ.!’÷G\\\…í}ùò%rssabb‚‘#GbçÎhÒ¤‰ÌmþüùÈÉÉÁîÝ»yåÄ®]»¤ÆPªn0L'ê"ufРAX»v- ÀËåqvvƺuëðÕW_Õh¿£GÆØ±c¹_ÏóçϯÑ~¾ÿþ{ :666077Gff&LMM+½¥ÖÄÄvvvˆŒŒÄàÁƒÖÖÖèØ±#ž>} GGG™ÛŽ9ÞÞÞ˜6mÌÍÍáææVá—NEfÏž ‘HWWW¨©©áرc˜3gN¥yLñòò‚ ««‹Ë—/#)) }úô©ñ>­­­aee…Ó§OcÞ¼y¼u/^¼Àž={¸±v~üñGaÞ¼yÜ-ê[·nŨQ£ššŠV­ZáðáÉDðõõ­ôØŸ}öÜÜÜðøñcbîܹèÒ¥ ·^KK 3fÌÀ† °uëVÞ¶zzzpuuå.ÔÒÒÂÊ•+±cÇŒ1QQQpqqA||<®]»777lÚ´ 'N„¹¹9ˆ‚‚9r>>>¼¿ÂÂB\¼xßÿ}µÏ§H$ÂŽ;0qâD„††¢oß¾ˆ‰‰Á•+Wàéé‰Õ«WÃÏÏgΜ……¦M›Æ>ŒãÇcذa5j1mÚ4ÄÄÄàôéÓX¿~=×»$ËêÕ«1}út<~üŽŽŽ¸xñ"’““aoo/s›ß~û »víâò~nÞ¼ cccîÇCi'Nœ€¿¿?†ŠÅ‹så:tÀÂ… ±qãF 8½{÷ƈ#‘‘›7oÂÌÌLê²!e ýüüü”ÝÒ8´hÑ"‘“&M‚ WÞ¼ys˜˜˜`Ê”)¼.w@€öíÛ—;¶ˆ­­-:uê@rYdÀ€xòä ÜÜÜ «« }}}8;;s¹&-Z´@ß¾}yûiÒ¤ \\\H.Ìœ9 À¨Q£°aÃܿ͛7ÇСC+|nÍ›7ÇàÁƒ¹1~ɽ££#z÷îÍ«knnÎõ€‰ÅbŒ;QQQHII““ÌÌÌ  áììÌ»$"п™_JNNNÈÎÎÆ«W¯’’‚±cÇbÅŠ¼ÎÉɉwŽÕÔÔàââÂëyÓÐЀ‹‹ tuuaoo{{{„„„@GGkÖ¬££#ìììx¯a¿~ý`ff@Àµ½¢Þ¼¸¸8üöÛo˜3g/·$%%¡¡¡‹Åpvv†X,†H$B=¸\œ¶mÛbÔ¨Q Exx8Œ€€€ ½âª>úÁÁÁ`ŒaÙ²eX±b…TÝDDD`óæÍRy/ü1tuuñôéS´oßÝ»w‡¥¥%¼¼¼¸D\@€1cÆàóÏ?‡ŽŽ\]]‘žžŽððpdffbÊ”)øâ‹/x—ÿ.\¸€C‡á—_~©0Ñ_ ÀÞÞ^êRW»víðé§Ÿ"%%ÏŸ?‡¦¦&&Mš„éÓ§CSSzzz˜9s&tttðîÝ;¨««cþüù\°ìááfÍš!,, FFFX»v-/Q[ ÀÖÖ–,€½½=zö쉄„hkkãÛo¿E¿~ýÐ¥K´k×®ÜçеkWâíÛ·ˆŠŠBÇŽ±mÛ6Þû¥cÇŽhß¾=RSSaaa èééq-Z k×®hÚ´)fÏž Æ^¾|‰ììl 4Ë—/¯uÞQ}&¯B¸ÔÔTîƒóÍ›7°··‡¿¿?ƧäÖ©–„„´nÝ?þø#fΜ©ìæp ѦMøøøÀÇǧNŽ™——‡®]»bĈøÏþS'Ç$„¨`Paa!Þ¼yƒ”””*oß`&ž$Š ccctèÐ={öDûöí1nÜ8 ~ÀØØ›7oÆÒ¥K¹™úàøñãHLL”>@‘Š/ÙVå!D~TªÈÇÇ;wîDÓ¦M¹Ë ‡†X,.·~tt4F¸¸8dffâ“O>ÁæÍ›k|Ç iØòòòŒˆˆ¨««ÃÎήÜéˆü\ºt †††µÊ)’§àà`äææJ]*U”””\¿~ݺuãÆg"„Ô • €8€!C†ÀÔÔ”eÞ¼y¼™ÃK›0aZ¶l‰ü©©©èÑ£6lØ@¿ø !„§R]Ÿ~ú)7a¤ŽŽ eÞVZPP€Ó§Os§`âĉ8yòdµ—B!Ê¡’·Á¯Zµ ·o߆‡‡‡ÌÑE?|ø€¼¼<^·³µµ5nß¾Í-?þÏŸ?Wx{ !„úBKK«Ò;_UJ@jjj°°°À­[· ©:ÅIÒ¥‡×ÖÖFjj*·|äÈ9r„7š¢ÄÆÆÂÐк¾º"##«<ü{UêVTGÖºòÊË–H½&ááá2G°•§¼¼<$$$T:ç,ÕyÒÒÒPPP###™uäuŽ?|ø===ndÞòêÕÕ9–ÕÆªªîkTÙ±’’’ ‰Ê½%ZÖkTüÙPz‚òÞ·efÍšñ>S¥6縺Û+óó¢ìk”••…¤¤¤r?Óå­¼÷AuTçWå³EQç¸lYnn.E›6m”Ý¢Tj Dy+ž'¶¢ùbÿþûo„††ÖMƒ‘3 L™2EÙÍ „Ô±±±°··ow©L²üú미sç:vì¨ì¦R-yyy8þ<@„F‰ 9˜2eJÍMˆ¼¤¤¤Ôj,—˜˜ˆDô±ª(yyyÈÈȨp|-BªJeî#„e â ¤Jä/!!7Z?!µA?U!DÜÝݕݕעE Œ5JÙÍ *‚z€!„ÒèPD!r˜˜ˆ‚‚e7C¥ååå!))IÙÍ *‚ B‘ÊR<Ê"òD9@„"”¤x”Dä‰z€!„ÒèPD8ÄO?ý„§OŸòÊÃÃñcÇ%µJõÀÏÏ÷îÝSvSˆQâQ‘' €gÆ X¹r%¼½½yå?ÆW_}¥¤V©ž‚‚9r/_¾TvSˆQâQ‘'ÊR²]»€Õ«œ–-ÿý¯òz³gÏÆ¶mÛpîÜ9 >¼Òú™™™ÐÕÕ­v{òóó¡®®.UÎCvv6ttt*Ü>;;ÚÚÚ¼²œœœ '¢dŒ!''Gj»Ê0Æ••%óy¡°°°Üç¹¹¹ÐÔÔä–µ´´ðüùórëæää@]]B¡°Â6•Ý'Q>ÊR<Ê"òD=@J–™ ÄÅ)þ_µö´mÛ3gÎÄW_}…¢¢"™õvïÞ èéé¡E‹زeK¥û~÷îÆŽ ±X mmmtêÔ ÇŽ h,XCCCèééÁÎηnÝâ¶½wשּׁ¬€víÚAGGíڵÓ'O°e˘››C__&Làõ¬,]º“&M¬Y³```±XŒaÆ!99™«Ó»woìß¿?þø#àìì HMMÅŒ3`hh±XŒ6mÚàòåËÜvOž<³³3´µµ¡¥¥>|øÿ_×L̘1ÐÒÒ‚±±1,XÀmÛ¥Kœ;wŽ[~úô)úõë±X }}}Lž<™×Õÿå—_bêÔ©X¶lŒŒŒ`bboooÞó „Ru)ÿú׿Î}™—õûï¿cÁ‚˜?>Þ¼yƒ%K–`åÊ•8pà€Ì}æççcðàÁˆÅðìÙ3Ì™3>,^¼çÏŸÇŽ;ðâÅ ôíÛC† ATTIGTTüýý±yófüóÏ?hÑ¢ºuë†Ó§OcÏž=¸{÷.ÂÃñ}ûvî¸  DÓ¦MqõêUüôÓO¸ÿ>ï’^tt4æÍ›‡=ÉŽ~ IDAT7nà“O>§§'ÀÃÃo߾ťK— OOO¸»»ãÇ$¿ø›5k†°°0„‡‡cåÊ•ˆ‹‹øùùáÊ•+¸~ý:âââpèÐ!äæærÇ|óæ 233ééépuu…™™‚ƒƒqüøq„……ñ&)MLLÄÑ£G‘ŸŸ .à÷ßÇ©S§pâĉª¿°D¡(Hñ(ˆÈ#2ùúú2__ß ë,\¸mܸ±ÆÇظ‘1@ñKËÊÛÒ¥K¶yófÆc+W®d­[·fyyyìĉÌÈȈ«çèèȆÎÛÖÃÃuéÒEæ¾>̰ÇK­KIIaB¡mÚ´‰++**bM›6eK–,aŒ1vãÆ €%%%qu8À455yûúá‡XçιåiÓ¦±1cÆðê,_¾œpËfffÌÏÏWçÞ½{LMM…‡‡seùùù¬I“&ìàÁƒ¬°°éëë³µk×–û|'OžÌ ÀòòòÊ]¯¯¯ÏŽ=ÊclË–-LMM÷ÜNŸ>Í;_^^^läÈ‘¼},X°€5ªÜýWErr2ï<Ú9vìKHHPv3TZLL ;uꔲ›¡ÒÞ¿ÏLMM•ÝŒ:A=@J6{6ðþ½âUÉÿ)íË/¿Drr2víÚ%µ.44...¼2„……¡°°°Üý=}úèÒ¥‹ÔºâíJïS ÀÉÉ ¡¡¡2Û(‹Áã•q=4²8::"55•÷KÒÔÔTª½Ð¯_?˜™™ÁÌÌ ÈÈÈ@TTÔÔÔ°xñbøúú¢S§N˜9s&®_¿Îm?kÖ,„††ÂÂÂîîîð÷÷G~~~¹í E×®]ѤI®làÀ …>ÿ¦M›âýû÷>WRwÜÝÝÑ´iSe7C¥Q‘'J‚V2É£¾144ÄŠ+°fÍlذ·N$!''‡W–••¡P5µòcj äåå1@ µ?åîSVb1©ýyüÒ>|ø¡P===™uÔÕÕ¡««[n²rqµ¯¯/<<<ˆ[·nÁÍÍ ß|ó ¾þúk¸¸¸ ""GŽÁ½{÷°téRøûû㯿þ’Ú_yç3''EEEܹ)OUž+!„òÑ'(‘iÁ‚ …Ø´i¯ÜÞÞAAA¼²+W®ÀÎήܠìììðáÃ3gÎT¸M§Nðé§ŸÂÃÃß|ó :uê„ððp;v Ó¦Mƒ‹‹ ±dÉtëÖ ïß¿G`` /^ 5júôéWWW¨©©aÛ¶m>|8š5k&u¬©S§â»ï¾Ã˜1c°téRÄÄÄà¿ÿý/&Ož ›Z?R7‚‚‚àââB—Á(!!÷ïß§Ë`D.("œž={‚W6}út\¸p—Ûãä䄳gÏâ›o¾á¾¤Oœ8aÆÉÜ·šš®^½ ___8pÛ¶mC—.]¸€aÍš5hÒ¤ °eËôîÝÿý7Œprrâ]kÚ´)œœœxÇ155E¿~ýxe8þ<žùß|ó WßÖÖfff¼öYZZ¢G2Ï9©[4âQ‘'+›EJ8~~~¼Ëããã+++øøøÔM£Hµ|öÙgHMMÅï¿ÿ®ì¦Ô;)))°²²BJJв›B©'bccaooÏ»$¯ª(ˆBä€r€r€ˆRëÊfƒºB9@ŠG9@Dž(ˆÔȺuë*œJ¢®?~‡ÆùóçѲeKe7‡4b”¤x”Dä‰ %{û7"o(ü8úšúðêæ%sýãÇñÏ?ÿH•wïÞ½Üé„B!„B¡TùíÛ·qåÊhjjbêÔ©°´´¬RûÒÒÒpòäI :”7à_JJ 1vìØr·³°°ÀÈ‘#!‰¤¦Ù „Bd¡HÉnDÞÀ¢K‹~KË  /^ð¦²HJJ­[·°cÇŽr 7ÂÚÚ}úôá•åççÃÞÞüñ6mÚ„þùæææ•¶O,cÍš5xùò%¾ûî;®|ß¾}øé§Ÿàé鉴´4©íààà€øøx €ˆR%&&ÂÀÀ Â lIíäåå!##ƒ75!5E9@€¤û>008qâ4440lØ0Ìœ9³ÊûpqqÁëׯqöìY¼~ý"‘Hj"UYfÏž½{÷ò¦Ýؽ{7fÍšE3Ÿ“zr€r€ˆ<Ñ· ‘²bÅ DEEáСCÕ <ìíí¹¹ºLMMÑ·o_üý÷ßUÞÞËË ‰‰‰8wîàï¿ÿFXXf̘Q½'@ˆ¸»»ÓD¨ F9@Dž¨¯Vɺ5}*ü8FÚUë2>zô(¶oߎ»wïÂÐаVÇ´¶¶ÆóçÏ«\ßÄÄãÆƒ¿¿?F…Ý»wcôèÑR“€B!µE’9Y:ÁÉÒ©òŠu $$3fÌÀž={йsçZïïÎ;°±±©Ö6ÞÞÞ4h^¾|‰#GŽàäÉ“µn!ur€r€ˆ<Ñ%0HNNÆØ±c1wî\Lœ8±FûÈÏÏçþÿ¿ÿý÷îÝÃøñ㫵'''ØØØ`„ 055…««kÚBH]£ Å£ "OÀ÷߈ˆüþûïhß¾=÷XµjU•÷±råJ888 {÷îèÝ»7&MšT£¹¸¼½½ñèÑ#Ì™3 ÂºÿùÏààà€-[¶ ::6lXµIHmQâQ‘'ê«%€Y³fáã?–*oÞ¼9`þüùÈÌÌäÊÿýïC[[›[>zô(Z´h ::öööèß¿…Ç´µµEPP xå3gÎD×®]ѳgO^¹žž‚‚‚бcG®l̘1èÕ«¯ž††F%Ï–BHcG´mÛmÛ¶•¹¾M›6¼å²ÓN888ZµjUåcŠÅb8;;K•ëêê–[.‰¤ÊmllªgDˆ"PâQ‘'ºF!r@9@ŠG9@Džè§ !„Èͦx”Dä‰z€!„ÒèPD8ÑÑѼDgyËÌÌDttt•ëçææ"22Œ1…µ‰yILLDAA²›¡Òòòò””¤ìfAáôèÑC¡þþûïèÞ½{•ë?xðÖÖÖåN‚zôèQ|óÍ7òl!µB9@ŠG9@Dž(ˆ4(·o߯;w°aÃôíÛWÙÍ!„C9@ŠW×9@‘‘@J `o_g‡$uˆ e‹bb  Ôø9•ILLDzzºT¹‰‰ tuuHº£=z„äädtïÞÆÆÆUÞff&âããaiiÉì0;;qqq°´´,w»;wî 44Mš4©ò±!¤2ùùÀƒÀ;%˜`Ð àêUe·Ž(@Êvè°h‘âci)ù9SEÿú׿pèÐ!n¹  ™™™8~ü8ÆÐÐPŒ7‘‘‘hÚ´)’’’°iÓ&Ìž=»Jû‹‹CÛ¶mqùòe¸¹¹qå6lÀ±cÇðäÉ“r·ûòË/3fÌ@|||•Ÿ!ŠFã)ž<ÇŠîÞ- vþ÷? 'Gº^pp­Eê)•úKÍÎÎÆŸþ‰ØØXôíÛ­[·®°~tt4bÊô¾¢}ûöŠlfƒ°uëVlݺÀÃØ±c‘œœŒÑ£Gƒ1OOOØØØàúõë066ÆÎ;1wî\ôíÛ·J©¶n݃†¿¿?aïÞ½X²d‰BŸ!ŠšCpÿþýj_+*ž>å÷WmÛ”ÉoG+«j7—Ôs*988@$ÁÖÖ_}õlllpíÚ5™õ·nÝ XXXpeýúõã¾ø‰ÄÚµkŒàà`ˆD"|˜›'*77ܯ2YÜÝݱ}ûöºj¢4 kWŧE‹mvîÜ9üûßÿÆõë×ѬY3À«W¯€7×—šš¹uU1räHãÀX´hvïÞI“&IÍ F‘ÈϦNŽ+}q0T:0ª¨¬ºå²êjjúú€ÿ_}}É:E ç_ÎzòD(VWóæ@ß¾À»w’ý{ø3F~í%õƒJ@¥'ÉÔÔÔ„‘‘^¾|Ya¤tS§JõPxx8>ùäüüóÏèÝ»7W^œì=zpå/^¼àåóTF$aæÌ™ð÷÷‡§§'qëÖ-ù=Bê¢s€²³ñã d×ÉÏ—<²²Ò„‘ÉúWV™P(ÉJLÌ@D„ìܽ |øPýv …€$à)~÷òìÜÉ€<Ë© õ SQ·nÝbZZZìíÛ·2ë,_¾œéêê2Ö¡C¶fÍVXXÈ­÷õõeíÛ·g7nä=Jsqq‘*k¨LLLØþýûcŒedd°Î;³9sæHÕKKKc†††lݺu\Yxx8À~ýõW™û?pà366敽yó† …BæááÁºwïÎ[w÷î]€¥¤¤HíËËË‹9²ZÏð%''3]]]^YÙ÷2-W}ùرclíÚµ ÙZc2ld+õh<˺ºŒééù1‘èT¶oÒ„±6²5k»v±Œ ÙçûÞ=þö­ZÉÿýRŸ–¯]»Æ6nÜÈÆŒþýö[Ö´iSÖ¨dÆŒÙîÝ»+¬ÉÞ¼yÃbccÙåË—™¥¥%ïKÝ××—ùøø°äädÞ£4ooo• €¼½½™šš›>}:›3g÷¸xñ"cŒ±;v0¡PȆÎ.\Èôõõ™³³3+**’¹ÿò Æ5jÀvîÜÉ+//Úµk[¾|9ëÒ¥ kÛ¶-[¾|9Û¾}»<ž~£“œœÌôõõ¥Êh¹~-'$0Ö³gñr2÷…®©É˜¶v2SWgL ^ߘ—Ælm›:5™íÚÅØÓ§ŒUýüge1&ò÷Ÿ”T?ÞŠXÎÊÊbÉÉÉ,22’………±fÍš±Æ@¥.’Ë0}ôüüüàååUaÝÒcÍ <Ÿþ9þøã¬\¹’+700€¡¡¡Ì}hÖÅî:²lÙ2týÿ|¤þýûÃÔÔTªNñ@³gÏF»vípìØ1|øðë֭Ü9sxcú”eggÇÝÆ^šŸŸºwïŽÉ“'óÊ-,,àëë ---®LCCZZZ7n¯ŒÔLÙ׫ì{–•»œmˆÁƒ%w0ý ’´ÁË—fÍJêùù†Üe0Ƀ¿œ—WñúšÖÏÉ‘$§¦"5µøÿ@Zš!ø³ƒ”ý,•ϲŽл7з¯!úöÉÍx5;ÿÚÚ€­-Z²þáCÀÅ¥~½?䵬­­ mmm"66¶ÂÏqU¢RÐ;wàáá 6H}™’œ.‡¥¨¨jj%³¼yó¦Ü/ýÆbÙ²eÜÿ§V!/ÉÙÙÎÎÎUÞ¿ììì¤Ê»uë†nݺI•[XXÀÏÏWFwˆ‘úJÞ9@‘‘€›Á/wpä•ý]&IÚÚr9¼ÜdeÉúEëK¦(ÌV­Œx¹;]»Êÿ.8{{ 4´dYÉ÷D¹T&ÊÌÌ„««+œœœÆ}qcþüù'''|öÙgX»v- wïÞ:t(Ú¶m‹Ð<3„‘ç8@aa’àçí[~¹«+pê §WëCÔÉ£yóšï£°P ½|™€çÏïcÚ4ÅO‡aoüúkÉòÇ ?$©c* …B¬X±¢Â:‹/†}©I]¾ùæ\½z—.]BË–-ñàÁØØØ(º©„$¯q€>† ‘ŒõSÚÈ‘’ÛßUèª{• …€‘ЧO ôéS7s•ÿ‹îS=*iiiI].)kñâżåáÇcøðá l!„TÝ;Àðá’чK›<Ø¿¿q vX_”½*ÿì›Û8PU¥VyB!•ILLD?ã·Z®^•ôü” ~fÏ¤àŒ”””T'Ç26ÌÍK– J'£U@!„ÈAPPRSSk´í©SÀˆ¥“}%–.vìÔè“€d.°ºÌÓ,{Œò€T ýYBˆ¸»»×(úÐ!ÀÝ]ry¥´o¿þó95NETu.0y¡HµQ§*!„(ÉöíÀܹ’¡öŠ ÀÆÀÂ…Êk‘(›Dj¡ Âéܹ3~ÿý÷o¿~ýz\½zµÊõcøóÏ?ñÝwßáóÏ?ÇêÕ«qïÞ½J· ¨Öün7nÜ€µµu¹ënß¾sçÎUy_„ÈRÝ  €Ï?ç?B!àïOÁ,u™H÷=zĽHÃF=@Jö #7Êf=*€¾P¯Jâxóæ 222j|Œ“'OB$UyBÔ`àÀpuuE»vípöìY¬]»[¶lÁ¼yódn—žžŽ·eH©@vv6"##ye©©©xòä f̘>}úÐÝ€¤Öª3ЪUÀwßñËÔÕ%—Ã&LPPU@BBîß¿_g—ÁZ·Äb =]²œ–¼z´iS'‡' F’ÝHIÁ¢ðp…ÇRK«Ò¨´œœhhhðFÊ®LE½7ÙÙÙÐÒÒâ ±Þ²eK<{ö íÚµãÊFމuëÖUÉÃÔ©SñäÉÄÇÇ£OŸ> =iª2c’ÞŸæ—kkÇÆ)¨q*¢®s€É(Ó¥ó®>¤HUÐ%0ÂóðáC 0b±ø®ÔÏTXYYI==zpssÞ={………°²²Âwß}‡Þ½{C,ÃÐÐ?ýô·?^ð}úôArrr•ÛûçŸÂÚÚoÞ¼á•ÿüóÏ4hÌíΞ=‹ÈÈH :´ÊÇ"¤6 éÓ¥ƒ±X2µ?õ%B«. €ÏñãÇ1mÚ4ܹsŸ~ú)V¯^   Àwß}‡ÀÀ@îѪU+4mÚ¶¶¶€wïÞñnŽŠŠB@@æÏŸ;wî`üøñX´hîß¿/óø—.]Bß¾}«Ü^GGG0ưwï^^ù–-[0dÈêo¶ö²~üñGŒ1€dîµ3gÎàðáÃèÙ³§TÝŸ~ú ÷îÝßþYå祦¦†Y³façÎX½z5ÔÔÔpëÖ-DFFÂËË«Êû!¤¶dåegãÆ/òë›™W®;×a#¸ºÎ¨H•Q¤dN††p*;­s=Ò»wo¼zõŠWvëÖ-,[¶ gÏž…¥¥eµöçèè(µ?8zô(–-[†}ûö¡W¯^ÕÚçŒ3ðÍ7ßàòåË:t(vïÞqãÆÁÄĤZû!¤6ÊËJK“ pxë¿ÜÒR2òs©ß¤ ê::u’ŒÂ]ܹ÷ö- )š4lt ŒT()) b±˜[މ‰Á„ °fÍš sldùðá xe˜>}:8€©S§V{Ÿfff=z4üýý‘žžŽcÇŽÁÛÛ»Úû!Dž%³·— ~lm%Iµü4 šš@‡ü2êR ™òóóqýúutïÞ€äúûøñãáì쌥K—V{ÉÉɸÿ>ﮫo¿ý ,À‰'0iÒ¤·ÕÛÛ§OŸÆ¦M›ÐªU+8QR©c¥s€Þ½‚ƒùuìí›7:¸ê­’”‘Ðe0UE—ÀÏÉ“'aff†´´4üòË/ÐÐÐÀ‚ >>> Á¬Y³päÈn›~ýú¡eË–åîÏßßyyyxÿþ=¶nÝ +++.7Çßß¾¾¾øüóÏ…íÛ·sÛ9::¢k×®Un·««+¬¬¬àçç‡~ø¡ÒúwïÞEjj*bcc¡­­‹/¢Y³f\°G–Ü\ 9Y2‘hñ¿Åÿ %“Z¶h!y4k¦˜¹µŠs€ÒÒšÂÍM2^LiŽŽÀùó@=¾â]ï)#$B8P²Lj ˆpú÷ï,^¼±±±èÕ«îÞ½Ë%u¦§§£W¯^8Pú“’KP-[¶D¯^½¤! ¬[·éééèÛ·/Ö¯_MMM€¾¾>œœœŠÐÐPÞvMš4‘™››KÝ#ðÕW_aÿþýðôôä­322‚³³3¯lïÞ½xñâÔÕÕQPP€ï¿ÿýúõ£HIŠŠ€ÔÔò˜òþ-[–“Sõc‰D€©)?(*~”.32ªÞspwwdzgÀàÁ@L Ý A’ Ouu«·O§Œ €z€T•€1Ø[???Þ¿åñññ••|||ê¦Q Daa!D"Μ9ÃÝFê—””XYY!EŽ#‘çäHz>d.²™´´ú7Å€–м¹t`T6XÒÓ“Ôÿçà£$ ²¥ =*É%! Sr2?  Œ É{DÕÄÆÆÂÞÞ±±±ÊnŠÂQ!¤FÒÓ%¿„<|ùÿóðìYÉÝ2 ]NðúµäQ±XÅÄ$"#Ã¥?V§Nöí“ô:‘ÚËËËCFFŒªÛ=WKMš­ZÅã­!!@5oX%õ ýY…X¸p!Z·n­ì¦9HLä:/_ÖŸ^±Xò%Õ¤‰$Ǧôÿ³²$IÉ11’?|\r“—ôt , ‚¸\2ž3ضM1ùF•²r€Ée°ÒÎ?|HPCGQ555Þ´¤áx÷®$È)xÊÌ4"w"`` ;ˆ©èÿ††’KUUPÄÆJžgñ£88*ýÿjÌÈòÿJÆZ¶L2Û;‘/eå’DèÓ§K–)¨á£ˆFŒ1àÄ ~ÀWóý €µµ$w¦:L©¡¦N$’܆^Ù­èÙÙ•IïÞIz˜J[³F2Û;Q-”­z(’ƒk×®!§:·¡Räää 7¨Â$æå öíîÝKöö€¾¾|Û©,ÚÚ’Y¿+›ù;%E=ž]]|ô}¬*вr€éèñcɠΛBä„þRkÉÍÍ ZZZr½“¦±yôèQµÆü!U—Ÿ/éá‰}„ÂBés\T´¨JûÑÐÌYU:ر³“ ¡¡ä„ž=Kr€ˆü)3ÈÊJÒcY|i4#llê¼)DN(ª¥#FÐmÞ¤Þ ~ü±zÛèè]»òƒNuuÅ´QU”7‘/eæ’^   ’å(jÈ("D…ùûW¼ÞÀ@’ÜYètë&¹¬Ew."­lôð!0q¢òÚCj‡ ¢t±±±033Sv3TÎóç’I7%b˜á£JîÝÖ­)‡A^a`` ú£0ÊÌ$7¥Q"tÃF¿óˆÒ*» *‰ßûˆþý‹uë€ $ɽüÈOPPRSS•Ý •–€Û%Q}£;ÁT @Dé¼½½•Ý•“Ÿìß_ºÄ³f)«5ƒ»»;7oQ eçuèÀŸÒäý{ÉÀš¤a¢ˆtê_²l` éõ!„ÔœH$¹! 4êj¸("J×&Ý«ke“ŸÇŒ‰¥[Ö,11ª2Z=•——‡¤¤$¥¶¡ìe°”ÓR{¥£ ùŠŠ®\á—µhAçXÑ(Hñ”P"´*¡ÛˆÒQ|íÙßì³{w`Ý::ÇŠFã)ž²s€J„V+³/· IDAT%ÔDˆ )*öîå—Íœ©œ¶¢Šºvåß=ùâ…ô|p¤a ˆ(åÉÏ¥K@ttɲ¶60e ãº@9@ŠWr€ÄbÉøYÅŠŠ€嵇Ô@Dé(H~Ê&?O˜ ¹ŒÎ±âQâÕ‡ €.ƒ© €ˆÒQ||øœ9Ã/+¾üEçXñh Å«9@€t"4Ý Ö0QDˆŠ €XÌÖ0@yí!DUQj ˆ(å§ÈGÙË_¥“Ÿé+å)^}Ȥ þ—¤a ˆ(å§ÔÞÍ›’»QŠ©«žž%ËtŽr€¯¾ä™›ÆÆ%ËYYü¿?Ò0PD”ŽòSj¯lïϨQ@³f%ËtŽr€¯¾ätLPDH—’?Î/£‰O Q,ºá£ˆ(å§Ôί¿ÙÙ%Ë­ZƒóëÐ9V<ÊR¼ú’М`ª€ ¢t”ŸR;e/yyjeþ²é+å)^}Éè˜*0Ƙ²Q_ùùùñþ%¤¾ zö,YVS"#–-•Ö$B…ÂBɨХ{_ß½š7W^›ä!66ööö¢×˜z€iÀÊöþ BÁ!uA(:wæ—Q/PÃBQºÆðKC²²$ù?¥ÉJ~¦s¬x”¤xõ) D膎 ¢t”ŸR3ÇŽii%ËÍš#G–_—αâQâÕ§ €ò€:‘²@QS3e/M›&±PömZYïcÅ£ Å«o9@%B7d*•–/^ U«Vå®OLLDRR:uêÄ•uéÒaaauÕDòÿ(?¥zNâãK– ÊÒOè+ͦxõ- Dè†Le ¹sç¢[·nøè£Ê]ÿÿß :::\™X,æÊ‹íÛ·~~~¼Gi´LËu½Ì¿üå‡)SmíúÓ>Z¦åÆ´Ì€üxP}h_U–Ïž= ???8;;ã¿ÿý/òóóѨäHÐ+W®Ä©S§pýúu4+=%v)QQQ°²²Bvv6´´´»víÂþýûqëÖ-4t]‰…™™™²›Ñ DE­[EE%eÿü#Ý _cÅKLL„D"º¹VQòòò‘‘###e7…ËýYKK’-*¯MµA#A7PEEEX¸p!®\¹‚›7oÊ ~ Y³f …ˆŽŽæÊÞ¼yssóºh*)…òSªnÏ~ðÓ£GåÁ@ç¸.PâÕÇ 33ÀÔ´d9'Gr—&©ÿT&*,,ĸqãðúõk\¿~ÆÆÆRuæÍ›ÇÝê®­­áÇc÷îÝ€¬¬,?~œÆòPÊO©š¢"`ï^~YUo}§s¬x”¤xõ1 Dè†Je ôôtœ:u gΜX,†@ €@ @ûöí¹:§NÂãR÷(þôÓO8}ú4¬­­ÑªU+ 8cÇŽUFó ©Ô¥K@©KèèS¦(¯=„ J„n˜Tæbµ¡¡!*Kgzûö-oÙÚÚ¡¡¡ˆŽŽ†¡¡!Äb±"›Hd ü”ª);öÏ„ €¾~Õ¶¥s¬x”¤xõ1 ¨¡R™ ÚhÙ²%?JDù)•ûð8s†_&kâÓòÐ9V<ÊR¼ú˜PÔPQD”ŽòS* ±Xûö@¿~UߞαâQâÕ× ɨÐÅ€2H=D! @ÙË_4ï!õ‡ššd^°Ò¨¨þ£ˆ(]co¢6nÞ^¼(YÖÐ<=«·:ÇŠGs)^}› ¬4º ÖðPD”ŽòS*V¶÷gôhÀĤzû s¬x”¤xõ5 ¨!¢ÛˆÒQ~Šl))Àñãü²š\þ¢s¬x4†˜âÕ× € †ˆz€©Ç~ýÈÎ.Y¶´ÜÜ”×BHùºtáOñê–¦¼öÊQD”ŽòSd+{ùËËK’pY]tŽr€¯>çik¶¶%ËŒ)¯=¤r¥£ü”ò”,«©I š s¬x”¤xõ9 Ë` @Dé(?¥|e{†,,j¶/:ÇŠGã)^}Î(jh("¤ÊÊ’äÿ”FcÿR¿QÔ°PD”ŽòS¤;ÆO 45Fެùþè+å)^}Τ §OzKÔ_¥£üie/M›ÔfŽM:ÇŠG9@ŠWßs€LLsó’åÜ\àÙ3嵇TŒ ¢t”ŸÂ÷ü9Pö3¾¶—¿è+å)^}Ϥ{JßÈ@ê €©gÊöþ89I&[$„Ô”ÔpPD”ŽòSJäçû÷óËä‘üLçXñ(Hñê{@PCBQ:ÊO)qê_²lhÈc†:ÇŠG9@ŠWßs€þ½ó¢jÛø½›Fzè¡IèAz_ªA‘©‚ÔÏ(ExAz“Î * "Ò$4CEjè¡…é$$Ù$;߇Mvv7›ÝÍÌÎììó»®¹vçL{r6;{Ï9÷y@È‘ DHùSò0ìþêÛ(R¤ðç¥:ò‰#x€ªT|}óÖ‡¥‹‡È@!<þþ›_6dˆ4±a*P·.¿ŒZä BrÈŸÂX¿ÐjóÖ52¾‘Ú Õ±øH|ÁÐH0G!9äOaÂgÃ~™™Ÿ©ŽÅ‡<@âã €|@ŽB!R«„0?8pxô(oÝÛøøcáÎOu,>¡B¸Õ ³8‚ ä(P AÈCós¯^€ŸŸ4±Q8j׿gnŽ’’$ ‡È@„ä8»?åùsàÏ?ùeBO|êìulÈ$>ŽâòðjÖä—ýû¯4±ùCˆg÷§lÚÄ ê¨YhÑBØk8{Ûò‰£x€êsH’ãìþÃî/¡[ªc{@y€ÄÇQ<@sd+€’’’pâÄ Çq8pàþ4ì' çøqàÖ­¼uww éâ!B¨HþÈV­Zµ ëÖ­üöÛoèׯfΜ‰Áb<’âÌþÃÖŸ®]%„¿Ž3×±½ ø8Š0@ׯ4±¦‘­ºzõjnSçš5k0kÖ,œ9s'Nœ@NNŽÄÑBâ¬þ”—/ßç—‰¥ïµŽí y€ÄÇ‘<@ÅŠAAyëYYLòA¶¨H‘"ÈÌÌDFFΞ=‹¯]¡Ç!&&Fâè!qVÊÖ­@ZZÞzPо½8×rÖ:¶'äGò@ýúüuꓲ@]»vÅØ±cÑ©S'Ô¬Yµkׯ“'OðøñcJAÃî¯AƒØ gk(–•ÒQ©R%|÷Ýw¸ÿ>¦NŠ}ûö!((C‡ÅéÓ§¥¥ûS´Z`ùr zu`ãFÀ°Íµþĉb ô:–äGóŽ5ì?¤ŽÀ¾ÈRéP«ÕèС¶nÝŠ¨¨(Ô©SÇÇ›o¾‰õë×K!Jö§œ> 4jŒi<´Z  Lš$~J®c¹@ ñq4à8#Á"#Ç¥ŽÂ¾ÈZéS¬X1|ùå—¸xñ"6oÞŒôôt©C"B‰9jââXRÃ-LühÔ8u øñG hQñãQbË Ê$>Ž–pœ‘`ÎøŒ$[tóæM,^¼ðêÕ+Lž<ãÇdzgÏРA|ñÅGHÆhµÀªU¬»kÝ:ãE•+3g€&M¤‰‘ ûQ³&àá‘·þèß-HɈ 6 ..°råJ8p®®®øä“O$ŽŒ¥øSΞe¢føp 1‘¿M¥>ûŒM|:lëþ²'J©c9C ñqD«+ðæ›ü2¹uƒÝ¹\»¦_¢•*»"[ôàÁ´lÙ°cÇL:sçÎÅ“'O¦?áð8º?%>žyyš5Ο7Þ^¿>ú¾v­8Z‚£×±#@ ñqD áíA­vŽ&Ù  R¥J!** Ïž=Ã¥K—ÐäuAJJ RRR$ŽŽGõ§hµÀêÕ¬»kÍã`Ù2àÜ9&ޤÄQëØ‘ ø8¢ÿH0CäâòPš@ìŒlÐàÁƒ1}út#44*T@dd$4 ÍFHNd$5ŸnÜŸ¯RŸ~ ܼ |ñ›æ‚ çEÎ-@Ïž±:T*ÀÅå‘tÙÙ  :uêàúõëØ»w/6nÜpqqÁÎ;¡¢#…#ùS˜‡§iSÖ²cHݺÀ?ÿ°|?¥JÙ=¼|q¤:vTÈ$>ŽèØ}AÿgëÆ 3SºxôÙ½›µfë¨[7 *•s$–­ Ç¿ÿþ‹ÚµkÃåõ#týúõÑ¢E ‰##„Æü)Ç<<5j°¡ëZ ¿?°x1ó½ý¶41šÃêØÑ!ø8ªÈר\9o=;¸zUºxô1¼5„„dHˆÈV½|ùßÿ=Ê–-‹æÍ›cêÔ©GF†ó|8΂Üý).Í›C†°ü>†ôëDE£FÉ·»Kîu¬È$>ŽêäÙ –𠄇óËÞ{O&MSv@¶¨k×®ˆˆˆ@||<,X///ôîÝR‡F8 ‰‰Àˆ@ãÆ,o!µkÇŽ›7³ù¼‚ òCŽhß>~W\µj@õêÎÓ+ò D…#==ûöíÃo¿ý†#GŽ ]»vèÕ«—EÇÆÆÆ"55µÀÙä=z„'OžðÊlsÜ„uÄÆÆÊÊØÎqÌÃóõ×lSC|}o¿¾üRü9¼„Bnu¬DâããáïïWGù§p@4 RSSQ¬X1©C±9Ž3ìþêÚUš8¤B¶-@kÖ¬AÉ’%±qãFtëÖ wîÜÁÖ­[ѽ{w³ÇeggãîÝ»0`–-[Vàu–/_Ž÷߯ Ë]–.]*ÔŸAX€œü)—.1Ï A¦ÅÏdzÑ]cÇ:ŽøäUÇJÅî øxû]K&8ª0nú÷_ãÔö$+ øë/~™³ ÙÞ›5k†þýûãСCøý÷ß¡V«ñÞ{ïÁÓÓÓìq‡œ9spç΋[qBCC±jÕ*!Â&l@þ”¤$`út6MENŽñöZµXNŸ6mì›È¡Ž•Nhh¨¸ˆ‹Ža¦Ã‡»wYú^'j­vdP¹r,ªÎGøò%û 褣G}½^º4Kíñü¹4ñHl[€êÔ©ƒ+V ** ÇDZcÇP³fÍ»ÀBBBpôèQñoF„"à8`Ó&6ºkÙ2cñããü÷¿¬eÈQÅá ¼| ìÙŒÇÆQ—*ôêņ!޾͆"Ξ-u”„ÈÉdØ(Ü¥‹ý§è‘Yÿ¹·o߯¦M›°aÃìÛ·¯^½%Ж-[PªT)ÔªU 3g΄VoŒóéÓ§qôèQÞ¢­~]?G=¯ÿï¿À[oÅ€úO=yÛ{õ6l8Š 77ûÇ'亮Žå×ãããn0¤ÆªóedàèÂ…À´ilØa±b8úÁÀÂ…ÀåËÇ4pôçŸû÷Eù{d·®ÑàЯ¿"áæMÖŒ’˜ˆ£ýņ2¥§™™8ÎëW’UüŠç¯ÿñ‡4ñpðÇ@ÞýîªT9ŠE‹áäÉ“¼ß@%#Û.°éÓ§cáÂ…xçwо}{Œ5 uëÖ\ >_|ñÜÝÝqùòe 2...˜*Ž“Ò†•?±±±(^¼8ÜtÞV2fÌÀ¢E‹¬:nîܹ8|ø08€°°0È}%ŸÍ› XúwC¼½ÙÃ÷¸qìéðÄÅ1Wã‰ÒÅÀòêç-:ad¸äW.Ä1ÞÞ@™2ì‡Lñõµo]pkÉ9|˜ùxŽg?¨–ââÂ~¥*T`"U‡‡k*SFø˜åDZ1ÚwïJ‰õ¸¸ä ¤Lw_\¹ïƒT°%ÇÓöõÉL†‹a¹Ÿ˺ZÈS¦ßŸ·Þ³'ðë¯ì}ll,êÕ«çÙãeÛˆ„„¬^½·oßF@@Þ{ï=´oß¾Pç½{÷.üýýQâõ´ÜZ­j½ŽÏ‡¢téÒ…º!/îßgVŠŸNŸ6½O¬§¡BûÆ&7o;KÿƒÁq¬¥CŽSDøøð‘)‘T¶,{<¶•Û·óÏ‘#¦3išãÍ7¶mví€V­Xë]v6kJxð€í“™ üð0¾íqÊþ‘þÙVrrX³sr2<4Òß–`­ çôôdBÈß?Oé^M•l;ø»?ŸÜÓ9Ûè/²@?FãÆQ¾|y´jÕ ÷ïßGÏž=ñÕW_aêÔ©ùwçÎDDDàúõë€7¢U«V¨T© U«V0`fΜ hÒ¤ BBBPµjU\¹r›6mrØa–ŽŠÐ9jrrØä~{ö°Å\³nµj¬»«cGÁ./=GÝ»³Lޝ‰@Y€ HMe]·n™ßÏß¿`¡T¦ âÓÒàÿò%\g¢çðaÖ=b •*1±Ó¶-[L=Œ¹º'²™vu¬ZLžÌº_”Èëù 5Róña" 'ÇxÑjÙ«<;7„!=-6¶ÒDÐBø!Eår³ü€•Lxx ™A×R‘­š?>>øà¬^½:·ìÚµkhРÆ"EŠ˜<.55ÑÑѹs†EGG£Q£<Í=nÜ8ÔÓ³âûí·8tè8€ *àâÅ‹¨V­šHa !<@IIÀLðìÛWpŠ//Ö »Âq,‘ѬYÆÛdC§môÓ:®œœðÁ,ç¢ÈÈ ¶mã—«TLI9¢Q¥b€ÜÒfsKõm(Œô—§O™[ÞÔPAKñòZ¶ÌëÖjÐ@¸îaØ“U×Õ™˜ÈFƒé‹"%°aý“OGüìÁ£hQ¶(Û8ûŒÝáLê OsRl)F–•)ÆÅë3gŽè#…DçuîŸ\ºuƒÆÓ ’„#$R ÃÉO5ù¡>²@‡ÆôéÓ1pà@üßÿýÞ~ûm|ûí·Ø»w/(ux„@ܿϺ÷üo¦§'ëÒZµ xü¸xøî; IæÿsJÒÓÙÓïœ9ür•Š•­]k¶Ëc—áÝœ#GŽ Yª¡Ã'òGÚݼɚI•ÔT`Ç~Ù€ˆ‹‹SD¢ZCtù²¸zU£tiÀt’¡ :99™7Z¥Zµj-y€Ä'44Å¥ž½A–"AŸÙ³¥‰Å8NtcÀ€Ü·eË–U„°Ÿ2|ö ágMpfd×öÝwßáÂ… f÷¡§YÇäî]fQÐç›oœxd—!¯^±yŽ~ÿ_®Rs粄wQS¦û÷ç­ÿý7pîœ}R >$sC IDAT–£GY‚0^^ŠÉýcHõêìÏ{õŠ­ÇÆ2StéÒÂ^Çðç²[7aÏïÈÈN9s§N AƒP¢D ©Ã!ÄTëO×®äOÀî~]º°*}<=-[˜·£P§§:É=@:Þy‡-ÿü“W6{6°s§t1YŠáħݻ~~¹«Jò©ÕÀ[o§Oç•]ºtì(Ü5¢¢XFnn¬g`È® ìçŸÆÄ‰±sçNÄÆÆ¢_¿~˜4io!;wØï¸>aa¬qÃé[ô®^e£¹ ÅOéÒ,®ÙϾŽí€,<@: ½@üÁFÊ™—/séuÊòâwƒ~íÛ´±©]±ÈNùûûcÒ¤I¸~ý:jÕª…Ž;â³Ï>³Ûü_„8¶þÔ«Ç&1œÜŸrà3y>|È/¯]›¹ê¶pê:¶²ðé a~ |ÿ½tñXÂöíyýA›Ó¬m[Þ.JòÆèâEaÏOÙŸÍ#;¤ÃÃÃ~ø!BBB°eËìÙ³Gê1ÕúCÞ°T×;ó3”@‡À‰@ÅŠÒÄE(ÃV ­[Ù0L¹b*÷Âob¶ÅÄgÏæ­«T¬—ÈC–èÑ£G5jjÕªFƒ[·naôèÑR‡EØÈŒ@NNÞzýúü'§š§*3“%0ìÕ >œ_1ðùç,ÇžïAœªŽ%B²¹Àò£[7–HGN3ÓË‘»wùž%À¨û ` %̦£N~BÂÛ·ù`…á?øÙ7Ê•æÜJAvhÖ¬Yhܸ1üüü…¥K—¢"= ;,·oOLýÍ7üuÅûSÒÓÙÈ®>}€’%Yßßöíü}Ôj`Þ<Ö*$‚‰Vñu,dåØÿ”¡grãFàÉIÂ1‹aëÏ;ïUªí¦4—?û³VËæêþ*Ù  þùÏž=ìY³PªT)¨T*£…p [4ÈóþèP¤?%-‰œ>b¢§Gà—_˜ÑÓ//6÷ÑW_‰Ž"ëXfÈʤ£O~ÒLøáÉÂ1 ÇgG5Ñú(ϰq}„èKNfóéCÈÙ  ýû÷ƒã8³ áܺüü3¿Ì°õGQ¼|É|=z0ÑÓ«ðë¯üI ©X‘ô¢ä„¸º²Äšú¬^ ÄÅI)æðòrÜI\m@ ÐÞ½@VVÞzõêüÞP‚!;D(S­?¦ÞÚŸ’œÌÞ]»² J?þ˜uw¥§çLùòÀèÑÌópÿ>Ш‘èa:t;²óé4ˆM?®#- X¼Xºx 1ìþêуÍ×`¥y€qF‚Q÷—e"DáÖ-Öã£OX˜é}Ο’˜ÈÒõ¿ÿ>=Ÿ|‡ùS±"0n››éáC`Ñ" eK»rq¸:v@dçÒááÁþ÷ôY¶Ìxô¡¤¤g>ϧû Pž0@W®°†ÌLÀ`.q@ù â¨O)_Â^ÿb‡å÷ËMäK¿~|ósÆ@d¤tñšøxöXµcÎo_ÎÊ•ÐP¶8Â4„rIMe"\¿õäûïMÒöfíZ6ÇŽŠY«¨“y=Ë–åOXzýºí]Vûöñ³=²!ñ–Vill,êÕ«ç­ÆÔDÎÍ›–·þÈš/˜_¢Cv<˜Í±dNüT«Lž œ?φöÎKâ‡`Ô(~ÙÂ…æ»jía÷×§Ÿ:ø„õ6övéâ”Uj$€Áùî;6œSG£F¬·(?dõ¤ ¬XÁ2Ж)Ãòòüý·ñÌìúÓ¦ÿþËúþfÏægᕲªc…"[ŽQ£˜Òñü9k‘ŠÛ·YÂO*@fP¢n$˜VËÒŒéCÝ_ùCˆ”¨(6JŸ‚Z$÷§h4¤¦¦¢X±bâÄ%*ëA?y2¯ìÒ%ëÐõëì£ÔáæfÙý×™!!…iý)”?%+‹Ý)Ö¯gÞæÍÙD¢µk³Ù¤-b ͉•Š÷ÃÀƒÀ™3À„ Š?y€ìì=@:*Vúöå—Ížm¿ë'';wòË,èþ”ë ß føoÓð÷/\LJGæ*„£`èýiÚ”õ?[‚Åþ”Œ –%Lתsá3!gfZ°Z ´hÁšÝ»wgÙ™ y€ÄÇ!<@:&MbÞÝ—öòeÖ=ln¸¦PlÛÆ~_©€`Jõ… FÝ_ÖCˆ(4ׯ³)¯ô)´÷'- +× Ø… 3̸D‰¼1ùÝ»ó§ g"8˜}vìÈ+›5Ë>Ȱû«JTƒÂµ=yÂO4«RO:MCˆ(4¦ZBB,?>öÖ->}Ê;7oòOj-ÌÀܰ!{mÐxã ÛÏçàH|Ƥcʾ:}š¥hÓF¼kÞ¼ÉF]ê° ÷>Jõ¬×ÞÅ%/ûÆ;Ìm5 üÁÓѸ±õjgÄA¾©„\¹|Ù¸õgút ¾sèÔ »îÜÁ°ÂÌÈR¾<_è4lH­;ìÚµ‹ºÁDæÈ‘#hÓ¦ Š/.u(–Q¿>б#pà@^Ù¬Yâ ÃÜ?-[Z嵋‹‹Cdd¤"»ÁŠjÔ` Ý4ÿþ ¼ývÁÇZª¨ûË2H…",ÌøÉ£ ¼?Xë΀ÀíÛ°êg9(ÈXì”,iÍœ?âãP S§òPx8д©ðײ1÷>JöL“êÀºÁ @IIÀ±cü2@–Aˆ°™ÿ56ÞYìýY´ˆŸß•Š%ÇÐ: °l¶AÃ;ï°Vý‘U³g³>¡ùûofVÑáä¹LQ¯iK|@ýÅOæ]½ºí©:$€›1lý±4ؓçkb¾ñ»ë„Nýúl8;!ä‡óé˜:•?dóÏ?ÙhË:u„½Ž¡ù¹G6Wƒ(ÙØf„¦Ñ_¶Cy€›¸tÉø!Ñ¢ÖŸœÖì­KÅ`—·73`nÙŒˆĒøÊ$>“Èþä½|ÿ½°×HJ2þ¥¶²û Pv ÀX]½j~àkf&°?¿Œå"l°õÇâ¼?óæ1ÃV¯&Ó²ÈH|BCCÇmÈ”)üõ_eƒ„bÛ6ÞC‚‚€Ö­­>Ò=@%J°ù˜udd°óü8t(oú € ~mÖL¼ø” Âj.\°±õçêUà›oøeÝ»}úA¶Ð­‚ßœ`î\áοaýÓO)÷O>X“ѰQ­KªVk DXÍ·ßò×›5³ ïOv6»éi4ye%K«V!66Vð >TÇâìÂ$ꔵ˜<™_ö¿ÿþÜQQüV_+sÿè£ÑhPø˜dŒ¥> ­ؽ›_FÝ_ÖAˆ°Šóç¿t†‚È$³g³¦#}V¬J–$Š :‡õéèÓ‡uMéÐh€ùó ^Cóóþcó<{J÷–  “'Ù¬ñ:|}víÄ‹K‰"¬ÂPì4otèPÀA—.3gòËz÷f3@ƒü)ö€êX|Ú®®l¦x}Ö¬^¼°ýœ99lÎ1}l0?ëPº°\>Ó¼÷àî.NLJ…a1‘‘l„¬>¶þh4¬¹[?QE` °l™àñQH âHxõŠå첕ƒ˜˜¼uÊýS•+óÁÆÇ›î‰¤áï…‡a1†Fç-€wß-à 3Ø|úüø# ÷¤Lþñ¡:‡öéððÆã—-_ØÚµgØýÊ Úˆ3x€T* n]~ÙÅ‹üõ«W»wóÖÝÝ-ÌÁFð DXĹs,ã¨>¶þDFsæðËú÷gCô ŠøP‹Ã{€t è'LNf"ÈZ‡‹¢û pPp7˜á×¹MÀß_ܘ”ˆbÐÙ³g±ß0CT>ìÞ½_|ñ¦M›†[·n‰™cbØúÓ²%о½™23Y×—þq¹rÀâÅF»’?E|¨ŽÅÇá=@:||€Q£øe‹±î0kغ•ÝtT®Ì Ð…À<@€õˆº¿lCqèñãÇX·núõëg‘Z½z5ÆŒƒz¯ÿãZ´hèèh‘£t,Ξöîå—˜÷çÿþ?«¬] Ab0jšŠ/˜!Ú »¿(÷Ř@±Ñ¸:T*£FuÂB'€âââðèÑ#‹çŠ™?>–,Y‚!C†`æÌ™h×®6&írr ÅÎ;ï0ÜòÔ)ã᳃ç›,ˆü)âCu,>Šðé(Z”u…é3>?—9®_gON: ‘ûGgðÀ›onnyë÷ï))ì½a¯b“&@Ù²ö‹MI(NÕ«WaaahfA>ð””ܾ}-Z´È-kÞ¼9Î;'fˆÅ™3À¾}ü2³­?é鬟_«Í+{ã `Á‚|!ŠøP‹b<@:ÆŠÉ[ü˜%G´ÃÖŸÖ­Š ’³x€<<ø3ºsðï¿ìýÎü}©ûËv'€¬A÷Tì§7æ°X±b¼§å7",,Œ·è£ôõ~ýøë+†¡m[3Ç·m èù¨Â`ýúÜætS×Ó÷§Hý÷*u]WÇr‰G‰ë¡¡¡Xºt©lâ)ôz`  f„ûúk–ÛÇÜñ99lbc¼þþÀÀ‚Ä·zõjžHVõ%ð:ëË[¿t ˜4) ÇóŽà [¯·gÏ„……¡uëÖ˜?>²ôÓ–(ÇéOi©ÆŒXd&‡Å;wP­Z5dffÂýu© 6`ÕªU8sæLî?‡á?³pú4Kt¨Ï‘#fæ0üç¶Q¿õgÄÛF!=U«ò3üü3ðñÇù³w/йsÞº¯/ xy‰§Y¸Ÿ‘`à@6Ú«ÿ¼²5ÌO–j ±±±¨W¯žSt›;u PÉ’%°¾{ÏŸ?GéÒ¥¥ IVê¾V­ÌˆŸ´4ö Õ?•+ÿýo×q†/šÔP‹¢<@:*Vúõã—}ÿ=ë“ÉCeÏž‚‰gñ¦Ð4úKXœNmÙ²%×ããïïFá/½7D{³ã»ƒS§€øefóþ|ý5?3—JÅn„$=#ŠøP‹â<@:&Mb“¥ê¸rÅ8%¼Ž„ãm¯»¿„ÀY<@€±ºvÍøžL¨p(® ,""k×®ÅÙ×#š4i‚áÇ£iÓ¦€òåËcÀ€˜ùznªððpôéÓ:uÂÓ§OñìÙ3=zþþþNÝÖ¡ð÷ßyë­[³î/“>Ì’éÿ+ÃÚp ‚p|zõ¶oÏ[oÒ„?ûŽåË‘#óÖ«Vnß?>…Äz!MQ¦ ðä‰ð™œ© ÌUꄦjÕª0`èe­¤7óðöíÛ˜»Þ®];œ9sÿüóŠ+†6mÚÀËÉûªOžä‹ÀLëÏË—lþ }ñS½:›ý e0e _= „‡çÃ0•û‡°™zõò@]ºPZ¥Â¢8È8†47tõ BPPˆQ9ß|Ã_oÛÖL×ñãùßPµšÝ==-¾^ll¬ÙÏŒ(@Kq¦<@: »ÁÜÝ™‹(<$€,_n|ÿêÔ øî;;'$C‡òËÞz ˜>Ýæë“?E|¨ŽÅGñ †­@Û·»wóËôFá ‰³y€cÔ¦ k`# y€Ìà  þa>ý©_ªVe­A¦š^Ñ·/K…¯ÃÍ ‰ÍÏ­G„²à8 N–™ÏÕ«7oÚ7&ܹ$%‰‰¬z4ïzÎäRàxMÂRžU«²û–Iñóì™±ZjÚ˜0A´ ‚pÜÝÙT8:€nݤ‹‡ ¬„ñä j…é>ÿœ CÐQ¤ëúrq,.gxÒªcñq*ŽÏ?J”`ï{÷f÷!!$$€œ„ÌLæïÑ7=fLϰy³ñL|³f5jùSćêX|œÊ¤ÃË ;–½8PôË‘ˆò™AI ÁƒuëøeS¦0=c’˜¦Œ’’òÊZ¶dó™ì+#Â)INf™SOœ:BÈD(ŠU«ŒÅÏ{ï3f˜9hð`¾øñòbÍE$~‚ÐÇßøí7©£ «¡_3…sâ0j¿Ì¬é`jiß>~Ùܹ@•*¢Äè ORCu,>NéÒh—ˈ@ &&ÆÓóíÛÀ¸qü²6mŒG‚ ùSćêX|œÒdgÈD y€ÌàÈ hÕ 8}š_¾};E&¹}›‰ý¼¾¾ÀåË@PX¡A2<@„Ã3r¤±ø™<ÙŒø¹u hÝš/~`þ|?A„â ¤@~üX³†_Ìœ™ÏQQLüÄÄðË †#DÎð¤!5TÇâãÔ ;A BHH)Œ“'MÏUª˜1=߸Áº½ž>å— ¬]+Zœú?E|¨ŽÅ‡<@âC BHÈdGó=}Ê&h××2ÞÞ¬+¬vm\¿´mkœqèP6v^¥5^‚ B^ˆp84–éÙ°!gãÆ|Äϵk¬åÇPü F⇠‚P<$€—_§NñË&MÊÇô|å ?ÏŸóËGŒ`SÅÛYü8Ó†ÔP‹y€Ä‡<@„RkÖ«WóËBBò™æâòeÖíõâ¿|äH`ùrIZ~ÈŸ">TÇâC ñ!!$ä2ƒ#x€NŸfù~4š¼²*U€s瀢E v¾t hßž?»;À\Ó‹‹+A!oÈD8±±Ì÷£/~¼½Y¦g#ñsñ"Ю±ø3†ÄAátrPt¦gÃÔ=6˜0=_¸ÀÄaßùøñÀÂ…¢Æi Îð¤!5TÇâC ñ!!$$€”Ñ£YÎ}&Mzö4Ø12’u{%&òË'L`YžeùSćêX|È$>ä"„„<@f«híZ`È~YÇŽÀÞ½ÉÏ:t’’ø;ý50gŽèqAŽy€ÙræŒñÄìUª¿üb ~ΜÞ}×XüLžL⇠‚pzH9±±@÷îÆ¦ç; LϧO³–Ãæø©SÙ³í«58Ó†ÔP‹y€Ä‡<@„r²²XRCS¦ç:uô Nždâ'%…¿ãôéffC•ò§ˆÕ±øH|ÈD‰«Ô–1z4pâ¿ìë¯ LÏ'Nï½¼|Éß1, øæ±C´™aÆI‚â¡:ŸP“i× !)[¶,ºté"u„B ä¬_¬\É/ëØÑ 7럀N€ÔTþŽß}ÇZ‚ ‚È…ºÀdÎÙ³lŠ.}*W60=;ÆZ~ ÅÏÌ™!~ÈŸ">TÇâC ñ!!$$€d̳gÌôœ™™Wf”éùèQ sg -ðìÙÌôì?E|¨ŽÅ‡<@âC BH(¤Ì”•Åæ,5ü®oÛôêõzåðaàƒ€W¯ø;Í Lœh—8 ‚ åàLy€È$SÆŽ5?_­'~ÂÙøIOçï4oðÕWv‰‘ ‚ @2dÃ`ùr~Y‡z¦ç¿ÿ>üÐXüüð0nœ]b’ØØXJ†¢¡:ŸøøxøûûÃÕUØÛjJf nÄÝÀ7r_ï%ÞƒJ¥‚»‹;Ü]Üá¦vË}o¸¸¹˜Ù&ÐqE\‹ÀU-þωF£Ajj*Š+&úµåCHfœ; Î/«RغõµéùÀ kW #ƒ¿ÓÂ…lfwd×®]4L[d¨ŽÅçÈ‘#hÓ¦ Š/nÓñ±©±FBçFÜ Ä¼Œ)ø`0¼ÑpÌï0^n^¢]#..‘‘‘N5^Ëi‘Ãå@ËiÁq80׊νÂã½×m³u¿é/À¹:‡3†<@f°·èùs aCàñã¼2ooàÔ©×É÷íºu㻢`ñb`Ô(»ÄH„íh9-$?097^Ü@bFbÁ'9Á%‚ñS÷ŸÐ L©C)ÑIјy|&^j^"G›“+B ßë„IAïÍ£ óIßq?$‡+ßÐO-@2!;›%5Ô?ËT§ØL§†CÂ`éR`äH»ÅI(ÝÓ!Ž÷¤©ÿªå´•qà V©á¢r‹Ú…÷ÞEÅÖÕ*e@Õähp;ᶑйwéÙéŸÀA‰Š‹B³µÍ0£í Lh1Áá>gVœ[I‡&!U“Zð„CCH&Œ ?Î/Ë5=ïÙôèÁŸL¥–-3Nä€?E<¥<Âè}£ñ×Å¿ ©ž0äŠ!}a¤{o(œ TæÎ¡[TP±W•J”õôät<Ë~†¨Ä(ÜK¼‡l­09\Õ®¨R´ j–¬‰š%j¢fÉš. OWOhr4FK–6Ëd¹XKzV:²´Y¹ñfi³0éÐ$컽›»oF¿ ‚Ô ®èNÂ|¶û3p¼à E@H¬[Ç´Œ>¹¦çÝ»YÓ¡øY±Pˆ§ƒü)“­ÍÆâ3‹ñÍ‘o–•\ÐHê¨øäp¬™_ÿÇÓ¡¹ €o7o—Fp‰`žØ©Z¬*ÜÔn*,iYi½o4Ö]\Ç+?öàÞZùVv^‰Þµ{ r-1<@ZN‹…§búáé²lÓ ~ØVAP©^¿BÅ{¯Ûfë~œ–Cf¶AOƒB!ìáZ³†é­ÞƒxåÊÌ ]ìø.Ö”¥÷¡R«VC‡ŠáØœz| Ãö Ãåg—¥…0AI¯’¹G_ìTð¯ûcäˆìŒÚ‰!»‡ >=Þh[¿·úay§åðóð“ ²ü¹þâ:ý1gžœ1Úæëî‹é­¦#( ¨ÀJ1·ÛÊäà¼xñ¾¾¾(R¤ˆÔ¡˜eÁ`üx~™·7°s'PìØN࣌ÅÏêÕÀàÁö ”p31éÐ$¬9¿&wd‡5èºutO…úOœù•£{-Èä)e×›=PA…Šy]Vº÷Å=m%&wºwC³òÍðéÎOñ÷½¿yÛ¶\Þ‚ˆ‡ØÜm3Z¾ÑR¢óÈÖfc˜ql2sŒ[;Bª†`õ«í¾#䇢УGðá‡âÙ³gHKKC¿~ý°dɨզxÓ§OǼyóxe:tÀîÝ»E5, øö[~™›°e ðÖí߀޽™3Z‡ZÍš‹ =6{C Â³åòŒ?8ÏÓžmó÷ðǤz“0¦ý#‹~¸½r¤Œ¹‘7ºEßà-Æzjr**”ª€ÚµQ£x Q‡ƒË•2>epà“X|z1&šÄÑIÑh½±5&¿3ß´úƦ¼ABx€.Å^ ?ábìE£mE‹ÅÂ…ø´^º^ IDATî§6ŸŸp%€Æ‡Ö­[cÁ‚HNNFÆ ±k×.tïÞÝäþYYYèÓ§,X[ææ&~_ûøñ¬õGŸ"E€;€Î¯¶}ú‹Ÿuë€DM Èd;7ãobøžá8}ÄäökŒ`×–](â*¯Qkî—³¿ÅvìØ6 mϤTPaL³1hW¹úþÖWž_ÉÝ–Ãå`æñ™8x÷ ¶tß‚jŪYuîÂx€49Ì<>s"æ˜ôu îŠW ŒO«ÏM8&ŠñeggÃÛÛÿþû/‚ƒƒS¦LÁDZeË“ÇLš4 IIIXµj•ÉíB{€´Zæ÷Y³†_îãüù'ÐúÙ6 _?cñ³aп¿ 1Ê #;³ÿ™¹'æB“£1Ú^­X5¬è¼í+·— :‚`dædbÒ¡IX|z±Q·¬·›7…,ÂàâwéŸ}rƒþ„k/®m+áUË:-ÃGo~$zŽ€3y€+Iƒž?FƒŠ+æ–UªT >4{ÜáÇѫW/Œ1Ç Ç¡HNNFRRoÑÇÒõìl o_`Íþö€€$:´~ú з/’ôÅ‹ ’V®ä‰[¯OëÊY?x÷ j¯¨Çg@“Æ?Yk†+#® }åö²ˆ—Öw=ýe:v\ˆýýö³–½öiYi²}ºoëžkœúúÏâžaâßÑb] &~ èwêŽë_\Ï?R×—TëéééHJJƒ’’…´‹ˆbîƒôððÈ-óôôDrrþÙ,;vìˆéÓ§£G(Z´(Þ{ï=ìØ±ƒ·Ïþýû±qãFÞ¢%ë™™,‡áÖ­·½ti`Èhzm=ðÉ'@NNÞVàÿÃFƒ™Þm¹¾Ü×õŸ4ä\ן¦>EÛ±mÑqKGÜM¼Ë /åíÛ¾r{Œ÷oZ}Þñº:–Óߣ´õøøx¬_¿^6ñÈi½C•¸2â êÄÔámÇ%6z¬ÎŠ:8x÷`ç[·n,º~ÄÃÔù¼朇.'÷zó*íüh'ÞI|%½JúïsôõS§NaãÆ3f ¶mÛ†œœ8Šé‹‰‰A¹råšš oooÀÊ•+±cÇ„‡‡[tŽ©S§âÚµkصkaºÀÒÒØ¼¥†!T¨„ïÏBµåcXN}\\˜º·0¹3äΪU«Èd-§ÅÊÈ•˜>əƂ>Ð' :.Àǵ?Î÷TÇâ³cÇŽBÍæ,¬»¸£÷fù©ôPA…ÑÍFcNû9¹Þ˜˜˜=@iYi˜|h2–]fr4ä§u?ÅÂ…(Z¤háþ…B]`HÉ’%áëë‹ëׯç–ݼyUªT±ønnnù޳…¤$àÝwÅOÕªÀ©ßbPmHkcñãê üü³ÓˆôÃl† O/ éÚ¦¹w¤‘øQ«ÔÑx¢FF™?Õ±= %ñcŸÕÿ —†]B“rMxå8,:½W7æ§õ)[¶¬Yñ~?uVÔÁÒ³KÄO¿ Ø×w6vÝH⇠ äææ†þýû#,, <À©S§°eË 80wŸ `îܹ¹ë#GŽÄ¡C‡?ÿüK—.Eß¾}‰çùs M6‘©>µkgæÿƒr]'Oò7zx°~²^½‰p\^j^bôþÑh²¦ "c"¶7(Ó§ŸÆòNËáïá/A„a;U‹UʼnA'0í?ÓŒ’ý]y~W7Æ¢Ó‹,Îg•’™‚¡EûÿµÇý¤û¼m*¨ðyÃÏqí‹k©"Øß@8>ŠÿÃ?`ìØ±hÛ¶-Š+†%K– yóæ¹Û«T©‚%Jä®ûûûc„ ˆE… °hÑ"ôèÑ£Ðq<~ ´oܼÉ/oÜ8Úm1¼B¿âôXŸØo¿±œ ÊÄgûõí³ b^ÆmóóðÃŒ63ðE“/¬ÊKu,>ñññð÷÷‡««¢n«¢áªvÅŒ63R5ý~ï‡è¤èÜm™9™{`,öÞÞ‹MÝ6åM7•hïí½ø|ÏçxœòØð¨\´2ÖvY‹6AmDÿ{ÇC1 1°Åt÷.?ÑÑüò-_aO™!pÛþ³ñAíÚ±–=qæL?…q/ñFî‰}wö™ÜÞ³VO, Y„²¾e­>7Õ±øÈvR2S0rïHl¾¼Ùh[qÏâXÓe ºwãy€Ò0fÿ“ǨUj|ÙäKÌn7Û)Rgò‘2ƒµèÚ5æùyú”_>¨Õ]¬‰ïõUs3MœÈf=u±ÿœ/„<Ðäh0ÿä|Ì<>ÓädŒ•‹VÆòNË©ùžP<Û®mð=Ô‘d´mpƒÁX²ÞnÞØµ#þØTãéÅk`ý‡ëÑ¢B {„¬8œIQ[­@œ?tìÄÌ8»å^Lþ·/sDëãëË ÐåF8.ÇÃð=Ãq#î†Ñ6wwL|{"¦¾3UvYœ B >zó#´¨ÐýwöÇÑ裼mk/¬ÅÑ裨SªvFí4:ÖE傯Z|…°Öaô}!,‚DD;))ye*pø½Þwèzâ[À°‘-8øýw fMû*S”äOÑrZddg =;éYé¼×Œì ^ÙÁ»±éßM&ÏÓ&¨ Vt^àÁ‚Ä¥¤:–+ä† ~Þ?óOÎÇô#Óy™ÎXƒ;Oîžücꔪƒõ®G£²ì-áÈÐ7µ8À’êç+ @"‚úáÍKн;°q#k"ˆ;X—ƒWY¯ð*ëÒ4i&…‰þk~âÅÒ}MÍ,m ¥¼Ka~‡ùøä­OªÍ·&>GŽ!@¨UjL|{"Þ­ò.úüÖQqQlÃ+1j°U7µ¦¼3Sÿ3U1sÊöƒ<@f(È´s'Kףћ ®àhÑn(¦ËÔ«ÃŘ5 øúkq‚uP´œ‰‰¹%W¬d¥ñÊtë––éÖ +Hì… * i8sÚÏ¡%¡Gzv:Æ•‘+yå Ë4Äú×ã­ÒoI™2!Q ›7úÃ?Æ/Øä6n‰üé+P¢ðË/lxÈÒfa㥘ýÏlÞðWg¤néºXõþ*4+ßLêPBvxºzbEçè\½3>ûã3$e$!¬u¾jñ\ÕôFØý÷ØÀŠÀÈ‘yÖWdc&` Y;7jÄòû¼ñ†Ýã”#™9™Xq=æDÌÁÃä×Õ¦ð‘4,ÁP«Ôðtõ„§›§E¯uJ×ÁðFÃE¿‘“H|È$.«uÆùÁçñøÅc4­ÖTêp@ßT+™;˜4)o½4ža»ªÞáŒg’ÇgŸË—³ ÏNNFvÖ^X‹9sðäåþÆ("yu‚ÄÛÝ^n^ðró²J Xûêîâ.ÎRHÈ$>äÕ+žÝxT“:B ²‚©SYÊÍq ¿©BQ†3ÈØëá,] bßeHzv:~Œüÿ=ñ_#›Œ„¯{ÁõCþñ¡:ò‰y€!!P‰µøþ{–ÜЉÄÏó´çøúÐר´¸¾øÞ¤ø)å] ÿ}÷¿ˆÉ-'[$~ò§Øªcñ!øˆò™!,, aß~›WP¼8ååDSZĦÆbÞÉyX¹ ¯²^™Ü'Ð'ßžˆÏ~/7/;GHAy€c6d­>+J‰]ˆyƒ¹'æbÍù5HÏN7¹O9ßr˜øöD m8”A K4ˆMiQDy?òq¯âÍ[î%ÞÃáû‡óI½‚_Lj9 Ÿ5ø .…Ÿæƒü)âCu,>äòBBßÔøóý÷ñÁºuR‡a3¦Žþ’–•fñ¹‚‚0¹åd ¨7@Ð9¯hž*ñ¡:š L|âââIÓa‚@ 3„……ñ^åˆ'?ª­‚)ïLAÿºýEŸµœ ‚ò²Á'?ª¯Ž©ïLEß·úÂEå"Úu‚ ÂÞ’S#2&wîðÄ̓äHÕ¤Šzmo7o™\ê—©oáCþñ¡:ò‰y€!¡oªIÌHDdL$Î>9‹sOÎá쓳xšúT´ëù¸û˜7ý+"( %¼JˆvmK!ŠøP‹y€Ä‡<@„È …õ¥g§ãRì%œ}r6WðÜI¸ÂU¹¯»¯±¸ ¨˜û¾¸'ÝŒ ‚ Ë a59\®¿¸Î;Wž_A¶6»Pçõóð3Ùr£[ŠyRS0AAX ¹—xçbÎåŠ O/ØlHövóFò Q/°žQkŽ3ÌžNþñ¡:ò‰y€!¡oª>ÙÙ…K|J˜G£Ñ !!Aê0…@ˆœ]»vI‚â¡:Ÿ#GŽ 9Ù¶”„eÄÅÅ!""Bê0…@ 3v.0‚ ‚p$ÈDA¡`H’ã ORCu,>äòBBy€É¡5â’Åqظczœ[¦ßïmØ ÎYðÞð8Ãm**Ë”¥2XW›Ù¦ 6³Íh»‰}ÕÛôcÔÅÌé—ü=œÁvÝ6£2ƒcþ:tíÚ¶E¹’%Aˆå"„„!9$~¬G£ÕâyVb5<Ó-YYyïõÖ²²ÀծɧOK¶²)]¸v .*¼]\àcåâ­V›Ý®VQâÕ²eË *~´‡ ­6wI7xŸÍqо¿Üëýmy¯Ežˆê½©ë ±_jj*²ªW¬Žå BRr8Ù—o 3‘¡Õ(ftK"uµÈ–ŽCJv6RþŒ<õ’§‹ 8Žƒ‹JõëV/µJ—ׯ¹ëzïÕ€éuŽ×oS|—…Üž£'Z KîûœóÛ_¿j´ZA?#¥àW±¢Ô!Ø@°­vm\¾z•×Ô˜oâ·uý‹˜¯†71S¯keÈÔj‘ÉqÈ|}³Ð½Ï´b[¦V M>Ûr8HHÌÌí“_×F~Ý%ígêæjî†kio›e˜••+n’Å5Ô1!))€·7àâ"ÊéÓ_ÿp¿ÈÊåüAv6žøúJ ¡H@T‰ˆŠ‹“: e˜iÖÖ5Ùæ>«Qæ«ðP«áuú4ü»wÏ-Óo]St­¨,xoxœá6ýfvKšÝõ×µf¶å×”¯¿®5ئ£.fÞˆÁßcø€¢ÛfTfpLÖåËÈ©W™>> D"9¸yhÑBêH@ˆ24Z»ZÒnn(íîŽ@ww”~½º»£´›¯,ÀÕøÏ¤Yù´l €u¥åä õõ’¦Õæ½×/Ïïýëý ·gPw P¼¸ âG ˆZ OQ«ÙûׯEÔj¸vÉЪlÉ{ý.þ¼77(ÀÒýLµ¢§¦¦bÉÍ›‚Õ±œ!DHŠZ¥‚›J•o+€3á¦R劖üÄŒî}QWúêÊ• ~®®ðø3Êá¸\1”öÚ ›óÚ¤«ûÎä¼~Í]×{¯}}£uŽç™~ Zû„Ü®V©xBE_°ðÞç#jôß{¨)Œ!±±±Xñð¡Ôaغ‹ÀGW¯â£Þ½y?΀ù&~[÷Ñ¿±ˆýjxã3|X+ƒ»J×7 µzëæ¶mÏg›«J…ØØXæûä×µ‘_wIAû™»¹ZZÆÛÓ7ns7s]™Ÿ«k®È)êæ&šñ» :& O||<üýýá*’8uQ©àïê '¿©©©(F~6B÷MЉ‰Á‰'P´hQ¼ýöÛðôô,ÔþÁqqèV¢„˜!;=ÿûßÿ0qâÄ|·ë››Y³+<Õ1QxvïÞN:¡téÒR‡¢Xž>}Š'N OŸ>R‡B(E  ãÇ£gÏžhß¾==z„ôôt„‡‡ÃÏÏOý qxõê•Ô!(ªcñÉÌÌ4J*I ÇqÐh4R‡A(Eu€N:aaaøé§ŸpìØ1xxxà§Ÿ~l1IHH(ÔÛš©,Ù×Ü>ùm3UnX–““ƒ/^x}1ÈÎÎF||¼ÍÇ[ó½zõ /_¾4»Puœ””„ÌÌL‹Ï-6…¹¶µŸQA×zùòe¾â/¿Ï(55©©©¼2Sÿ·ŽZÇÖ/åýÂ’ï‘X˜ú?°kêØ’{‹Xu\й•ŒbPzz:"""r³„ªT*tîÜd±Ù½{7bbbl>~ÕªU‚îknŸü¶™*7,{ùò¥‘È,ÌMÆâââ°}ûv›·æ3º|ù2Nœ8av¡êxÿþý¸ÿ¾ÙýìUǦ®m Ö~F]ëĉ¸|ù²Émù}F‘‘‘ˆŒŒä•™ú¿5¼vZZšÝæ+L[{¼”÷ ÃÏ(++ iiiÆ#¦þ¬Áš:¶äÞ"Vtn%£âÒfJ•*!33îîî€ 6àÇÄiSX²XX¶nÝŠàà`ÑãE@@Š)bÓñÑÑÑ l_sûä·ÍT¹aYvv6bccQ¾|ùܲK—.¡^½zÅ^4 âââP¶lY›Ž·æ3JIIAvv¶Y³¦PuüüùsøøøÀËË+ßýìUÇùÅh)Ö~F]+!!®®®&»µóûŒ’’’¹e¦þo ¯… *ÀÛÛÛ¢Ø CaêØÚ㥼_~F)))ˆEu;LÕ`êÿÀ¬©cKî-bÕ±aYff&.^¼è­BŠñedd\ô²°º¸¸äÛ¬hÉþ½{÷¶ÛAAÈ[Ä Å âÅ‹`MÕ:Åž˜˜ˆR¥JÙ¼pp°]Z‚ ‚°/Š@%J”Ào¼Ó§O#$$pþüy4hÐ wŸ»wïÂßß%J”°hs<{ö ß~û-bbbP«V-„……åv¥Â’€ùóçcöìÙR‡¢Hþúë/üù矹ëÓ¦MãuõÂpüøqüôÓOpuuÅòåË¥Gq>|¿þúkîz50vìX #R&‡Âü___ >*T:$ÛáÄ’%K¸jÕªq7näfÏžÍ+VŒ»wï^îöråÊqS§Nµxs—™™)uHŠãðáÃÜ»ï¾Ë]¹r…KKK“:E’œœœû?¼wï^nàÀR‡¤8¸víÚq999ÜåË—¹Þ½{KR¡PÌ(0øòË/1sæL?~ ˆˆˆ@¥J•r·7mÛ¶Íwÿ&MšÀÍÍÍè¼+V¬@ãÆQ¿~}Ìœ9Ç¡|ùò¨_¿>Î;‡èèh´jÕÊ.£#Ãqzõêer´Ã²eËrëxÖ¬Y¹ùT¾üòKLœ8‘r3YÁÉ“'ñÕW_•ÇÇÇ£_¿~¨Q£:tè€3gÎän{üø1¢££Q¶lYjÉ´€Û·oãÓO?5*õê† †š5k¢M›6Ø¿?`ñâÅèÕ«.\¸€'OžØ;\‡eÀ€¸uë–QùúõëѬY3¼õÖ[˜6m´Z-üüü„   ¬]»Ó¦M“ bÇcöìÙØ³gQùÑ£GÑ®];cРAHII‡‡âããŽÄÄDÞï«C"±“ ,àZ´hÁànݺÅÛ¶}ûvî7Þà"##¹+W®pµk׿.\ÈqÇåääp_}õ×·o_îÎ;R„î0ÌŸ?ŸkÞ¼9À¨®¶nÝÊU¬X‘;þ|8W¡B.88Øh{¿~ý¸ž={rwïÞåvîÜÉùøøp·nÝâêÕ«Ç­\¹’ûý÷ß¹fÍšq×®]“ zÇaùòå\«V­8ÜùóçyÛöíÛÇ•.ýÿíÝ}PTUð/°Ä,ËË+Ä-Ð E†—´@Hq$Ai—%iÆÁ©iÔÑ$3``rÄA-)… ¨(ÄlšwDQ'¬A„ey‰·!X°H^"^Îó·Vè Ùv÷÷ù‹sÎ}ùíaçòãÞsÏù»qã»{÷. `|»J¥b;wî\è NEE‹‰‰aæææì£>Òiëììd"‘ˆ;wŽ577³øøxÍ&&&Øž={ØîÝ»™L&c•••zŠ~~PÄûᇘJ¥b€©Õj¶ˆˆ–Í—‹ŠŠØêÕ«u¶©¨¨`o¿ýö‚Äj¨nݺÅT*377Ÿ–…‡‡³ãÇóå‚‚æççÇ™R©dJ¥’I$öî»ï.tØå·ß~c*•Š%%%MK€Z[[™¥¥%»ÿ>_·víZvúôiíär9«­­]x Ñèè(S©TìÈ‘#Ó ÁÁA& u¾ß …‚¥§§³uëÖ±ááaÆc~ø!ËÏÏ_и MMM S©TL,OK€”J%;xð _þꫯؒ%KcŒMNN²ÐÐPÖÓÓ³ ñ¢ææf¦R©˜¿¿ÿ´(++‹EEEñåÎÎN&Xyy9ÛµkcŒ±þþ~¶jÕªy¾Í èÇñ`à³Ù kL566b÷îÝ|yåÊ•P«Õ¨ªªÂøø88ŽÃ¹sç¶`ñ"???3÷±F£Á¾}ûø²··7t&![³f 233Ÿ| ÌÖÖ¡¡¡hjjBMMN›F£Çq‰D|··7Ôj5JKK±|ùr £¥¥Åðok?AO=õBCCùi4þªý+h{zzòuÞÞÞ¨¯¯ÇæÍ›qâÄ ÄÅÅáüùó(**Z°˜ ‘̸°¬F£B¡àËÞÞÞhnnÆøø8Š‹‹ò·oÿ’?yxxÀÃÃcƹÊáííÍ—]]]!‰`ee…êêjtuu¡©©IgCD Ð?Ðjµ:ÌÙÚÚbddvvv())Aoo/6mÚD‹ó=†ÞÞ^>‹ÅÂÈÈ¿8mbb¢¾Â3 Z­vÚB¿b±Z­VVVÈÉɵµ5Š‹‹u~dö¾VS× ­V‹}ûöáÔ©SÈÍÍEvv6-˜úfº^0ÆÐ×ׇÑÑQ¦ÈÜhµÚiow‰ÅbŒ!''¹¹¹xúé§qòäI=E8?(úÖÖÖ:“#ŽŒŒ@ `ÅŠŸýþ[ØØØLëcKKKɸ(z<÷105`W,#22‘‘‘zŠÌx<|­¦¾Ëb±»víÒSdÆe¦ë0õzûöíú ˨ü¿ë…¯¯/|}}õÙü2ª·Àž©TŠŽŽ¾ÜÞÞ©T:ã£273õñâÅ‹©ç‘T*Eww7&&&øºŽŽ¸ººê1*ã"•J144„_~ù…¯koo§>žg3]/ìííéÎå<’J¥èììäË¾×Æ6?%@ÿ@¡Pà“O>Áää$ °°r¹\ÏQêã'ÏßßŽŽŽüB£mmm¨¬¬DLLŒž#3®®®X³f òóóLMàyáÂú.Ï3…BÂÂBŒ ëÅ“ —ËqáÂôôô>ýôS¬\¹zŽlžé{ö¿Att4ã8Ž`®®®ÌÇLJobQy\Ôþ“IDATQQÌÙÙ™¹¹¹±uëÖ±¾¾>=Fk˜¢¢¢túØ××—od|‡„„°þþ~=Fk˜nß¾Í8Žc‰„ …BÆq;vìß~õêUæââÂd2³³³ã§s ³×ÛÛË8Žc...ÌÒÒ’qÇ’’’øöÚÚZöÌ3Ï0OOOæààÀöï߯Çh ×¶mÛÇqÌÜÜœI¥Rþ-/ÆþœU"‘0wwwȺ»»õ­a:|ø0ã8Ž …B&‘HÇq¬¾¾žoOOOg¶¶¶L&“1ww÷ioã£Y žB!d¶è!„BL%@„B19”B!ÄäPD!„“C !d^üôÓOüTúÒÞÞŽ¾¾>½Æ@1 ”b„ÚÛÛŽ;wêÔŸ8q%%%ó~¾ññq<ûì³:“.¤¶¶6ॗ^¢— !³B !Fhpp·nÝBYY._¾Ì××ÕÕ¡µµU‘=_|ñÑÜÜŒ³gÏê;Bˆ ˆ#eee…´´4¼÷Þ{3¶ß¾}§OŸÖ©KMMů¿þ (**•+W™™ ¥R‰“'O‚1†üü|ÄÇÇ#77CCC:û744`Ïž=ضm¾ùæ¶ÊÊJìØ±J¥yyyx0Yuu5 ÐÖÖ†ììlÏïÀÀRSS!—Ë‘œœÌÇYUU…¼¼<´¶¶âÀ¸sçδ}ïÞ½‹S§NáçŸÆ|ÀÏÖÜÙÙ‰½{÷B¡P 33£££€›7o"//ßÿüùó¸té_>~ü8ZZZ*• o¼ñâãã‘‘‘ÁÏžKùw£ˆ#–˜˜ˆ{÷¬lZ[}}=>ÿüsº£Gb``PZZ ¹\Ž‘‘lÞ¼G…——®_¿Žððp|öÙg8vì˜Îþ{÷î…L&ƒ½½=bbbP]] øú믱}ûvøûûC¡P ''YYY¦±äädlÙ²---üùÿj||ÁÁÁ¨««C\\„±±1888ÀÚÚ...ðòò‚X,ž¶cc#ÒÒÒŽúúz àÞ½{ Äýû÷¡P(ðí·ßòKƒ˜™™!%%…ß?%%€ÑÑQ$''ÃÁÁW¯^ELL ‚ƒƒñꫯ¢§§ßÿý¬?„ý¡Õà 1bï¿ÿ>RSSýÈû&„èÝ"ÄÈÅÆÆB(¢  à‘÷µ°°à‰D07ÿó’akk;í+333þ瀀tuu˜z•œœ ///xyy!)) 666ü¶‰äo“Ðh4 âÏoff†àà`h4šY;;;>ùypÌV¬XF „††âÚµk())All,¶nÝŠ²²2TUUaýúõ€èèh8;;C*•býúõ(,,ÄÄÄĬc"„èÝ"ÄÈ™™™áðáÃØ±cBCCùz@€?þøã‘ŽóÿÊS«ÕpqqØÛÛ£°°/¾øâìÿ {{ûiƒ·[[[6§ã=8æƒq<011ŽŽØÛÛ˜º#tíÚ5ܹshnnFzz:D"Þ|óM€““¾ûî;¨Õjܸq©©©077Çk¯½6ç¸! ƒîb6mÚOOO±@AAAhhh@?&''Q\\Œ±±±Ç:σAÑuuu¸xñ"¢¢¢QQQ8tèß><<ŒÒÒÒY7,, jµ*• ÀÔ€êúúzlܸqα¾üòËP©T¨­­c EEE°°°ÀóÏ?ÏŸ³¼¼ÖÖÖX´h^xá455áúõë\¾|­­­xî¹ç€U«V=RRIÑJ€1™™™:om-Z´qqqððð€££#.]º¤óÈk.6n܈¥K—ÂÇÇ ü¸£¬¬,8::ÂÍÍ 2™ NNN:oUý“%K–àÌ™3ˆŽŽ†››"""ðñÇC&“Í9Ö 6àwÞŸŸÜÜÜpàÀœ={–¿´lÙ2XYYA.—˜ºãµuëVÈd2þñ]ww7V¯^¥K—bÙ²e°±±Á믿>ç˜! ÇŒ=x•B!ÄDÐ B!„˜J€!„br("„BˆÉ¡ˆB!&‡ B!„˜J€!„br("„BˆÉ¡ˆB!&‡ B!„˜J€!„br("„BˆÉ¡ˆB!&‡ B!„˜J€!„brþ wT¤.·—©IEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/compressed-writing.svg000066400000000000000000000727501231437614300255610ustar00rootroot00000000000000 Writing with small (16 bytes) record size 10 3 10 4 10 5 10 6 10 7 10 8 Number of rows 0.0 0.5 1.0 1.5 2.0 2.5 3.0 MRows/s No compression zlib lvl1 lzo lvl1 bzip2 lvl1 PyTables-v.3.1.1/doc/source/usersguide/images/create-chunksize-15GB.png000066400000000000000000002074041231437614300256150ustar00rootroot00000000000000‰PNG  IHDRÐß}™SsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝy\Óõðׯ1nD‘C¹|ˆ?üÿüó¡›Ò BCCñý÷ߺMÎÞ½{±`ÁC7ƒÒP€&„4˜ýû÷ÃÇÇVVV@§N`aaÑ£G<œÞ»w!!!¸}û¶Ê±œœ|ýõ×j5Û¶mñcÇ”Ê-Z„ˆˆµ¨iøý÷ß±yófC7ƒÒº„æ§´´'NÄ‘#Gàëë‹Õ«W£K—. …¸yó&~üñG 2"‘È`mLNNÆ—_~ tíÚU阓“–,Y‚ÀÀ@µ®a…„„ÀÝÝ]©líÚµ;v,&Mšd˜FBHBšRï6oÞŒ#GŽàÍ7ßÄÎ;aff¦86~üx„††bÙ²elaÍœœœ°téRC7£Á„„„º „Ò¤ÑBH½ÊÍÍÅÒ¥KѪU+lÙ²E)<ËYZZbÍš5Šû¿ÿþ;‚‚‚‡mÛ¶aÈ!hÕªŽ=ª¨³k×. >NNNèܹ3Þ~ûmäåå)·  +W®Ä AƒàââGGGôë×ß}÷cŠzÄçŸ CPP‚‚‚ðÅ_àz§ƒ‚‚ðûï¿+Ÿ1† 6 _¿~hݺ5ž{î9Ì›7EEEJõnß¾   œ}P\\¬tÞÿýï˜0aîܹƒÉ“'# „ŸŸ’’’”ê~öÙg=z4._¾ŒqãÆ¡_¿~øí·ß 2Ö}òäÉ EÛ¶m1þ|XYYaøðáHNNÖø\ !ÏF!õhÆ Û±c‡ÖÙ¶mÀœœœX^^žÒ±ýû÷3,44T©<::š`ï¼óŽ¢¬¤¤„‰D"•ó6Œ³ôôtEÙüÁ°ˆˆ•ú7nÜ`ضmÛeÇŽcØ´iÓÔ¶}þüùв³gÏ2ÌØØ˜Ý½{WQž••Åø|>{þùç5¼"ŒmÙ²…`ÑÑÑŒ1Æd2kݺ5{þùçÏcL,3 6yòd•6µjÕŠåä䨜ÛÖÖ–3F©L °‰'ªÔ‹ÅÌÛÛ›ÙÛÛ³ÜÜ\Eyii)1b377g>¬ñ¹L›6™››³¬¬,•ceeeŠïÈ(=ÆÛ¼y3ÀV­Z¥(ûóÏ?6gÎ¥sÄÇÇ3ÇÆŽ«(»rå ãñxlüøñL*•*Ê333™™™4h¢ìüùó {óÍ7•Ú°iÓ&€ ‚Ÿ+!äÙ@=Єz%ïµuuuÕù±AAAŠ^R¹o¾ùfff˜7ožRyÏž=áãã£4ÌÂÌÌ 7Ü"//™™™˜4iJKKñßÿéÜ&9ùЋ7ÞxC©|Ò¤I066ÆþýûU3dÈtêÔIqßÁÁݺuý{÷4^oذa€Ó§Oîܹƒœœ,^¼fffŠòK—.A(bèС*ç9r$ìííµ|†êEEE!11ï¼óZµj¥(722Bpp0JJJpöìÙÏáææ±X¬öyóùªÿ ?^éþk¯½‡}ûö)Ê6oÞ >Ÿ?þXé:t@ÿþýñçŸ*†ílݺŒ1„„„Àظb꣣#FމsçÎA(€b%’×^{M© &L¨ñ9Bž-4‰R¯œ™™™:?¶ê¸[¸{÷.,--1sæL•cÅÅÅHOO‡X,Vç}ûöaÆ ˆ‰‰X,VªŸžž®s›ä!п¥r[[[ôîÝ/^DII ÌÍÍÇ^|ñE•óøùùáÆ¯×¡C¸¸¸àÔ©S Å™3g`aaAƒ¡_¿~8uêæÌ™ƒ3g΀Ú=zôh]Ÿ¦ yè=wîÆŽ«t¬¤¤Wã9¦NŠo¾ùýû÷G@@F…3fÀÓÓS¥.ŸÏÇÀ•Êàç燄„0ÆÀãñ •_¬î½'‰ððáC¸¸¸ 66¦¦¦X¾|¹JÝÄÄD0Æœœ ___$''ÃØØƒ Rªçää„N:áþýû5>WBȳ4!¤^y{{t~l»ví”îK¥RÀÝÝ]Ì+“—Éd2ÀîÝ»1cÆ 2«V­‚¯¯/ÜÜÜpãÆ ¼ñÆJ¥:·I®  -[¶T Èr...`Œ¡¨¨H鸵µµJ]§õ5‡ †ˆˆˆD"œ>}ýû÷‡©©)†Š5kÖ@&“áôéÓðòò‚›››Êã«.UWò±ÝÎÎΰ³³S9>{öl•e«òööFZZ¾ÿþ{„‡‡cåÊ•X½z5^~ùeìß¿Šº–––°µµU9GÛ¶mqçÎH¥R˜ššâÉ“'°²²‚“““J]''' <&&&€¼¼Š²Áƒ#;;‘‘‘¯7xð`…B­–ìÝ»7ÕŸó¥K—CV!„Vá „Ô»ÂÂBÀ°#F°ƒ²˜˜våʶsçNÖ»wo¥Õ ä«Füûï¿*çÊÉÉa­ZµbNNNlÏž=ìÑ£G¬¨¨ˆÅÄİ 6°EÝ)S¦0léÒ¥,33“¥¥¥±¹sç²V­Z©¬ªÁc]»ve:ubßÿ=;{ö,ûï¿ÿcêWá(((`ÌÞÞž;vŒ=}ú”]¹r…ùúúª´]¾ ÇîÝ»UžÏ´iÓ˜.½;wf˜½½=“ÉdŠòqãÆ1ŒÇã±ììl¥ÇÔôz2¦~ŽÅ‹3cccÆN:Åþúë/¥cؤI“ØíÛ·™P(déééìäÉ“lÊ”),66¶Æç0gζ}ûv–À$ »{÷.{çw¶nÝ:E½2sssæììÌ"##™P(dW®\a;wf|>ŸÝ¹sGQ·¸¸˜yxx°-Z°-[¶°´´4V\\ÌâââØöíÛÙœ9su% óõõe–––lݺu,%%… …BvïÞ=¶k×.¬¨+ ™³³3kÙ²%;~ü8 …ìòåˬsçÎÌÎÎŽVá „0Æ£Mi‰„-[¶ŒYXX0J7vøðaE]M/>>ž 4Hå<lñâÅŠz¬k×®JuzôèÁNž<©6@?~œ½øâ‹ÌÒÒ’P„Juš1Æîܹ£´ò[«V­Ø/¿ü¢T¯>ôûï¿Ï° &(•Ë—¹ëÚµ«Êcj “’’ØŒ3˜‹‹‹â¹É•••±õë׫ýYúøø°”””ŸÃ„ õy<ÀÚµkÇæÍ›ÇJKKõÈÚµkÇŽ?΢®••‹ŒŒT9oZZ{ùå—UÚdjjÊÞ}÷]¥ºYYYìõ×_W©kll¬²dÝ;w˜‡‡‡¢½<-_¾œMœ8‘4!„1Æ15_%„z"“É””„˜˜XXXÀÛÛJcn ‘——gggÅjêо}{•:ƒ BJJ RRR  ²²2ôèÑCíÄÂÊíÅ“'Oàì쌮]»V»‰Off&bcc‘““gggøùù©)‹qíÚ5<~ü}úôAË–-‘““¡P¨2Ù•òì¡M!¤Q¨  !¤1£I„„B!„耖±#„Ò(899¡´´ÔÐÍ „h‡wïÞÅÑ£GUÊ ¤XâH.)) ?þø#’““ˆàà`Å‚ü„B!¤ù¡h5nݺ…ÐÐPôìÙSi¢SÛ¶m•ô70|øptèÐÝ»wÇÚµk±oß>DEEU;…B!„4mÔ­Æ0iÒ$”””Ô¸‹ÚСC!‘Hð×_ÁÈÈéééèܹ3-Z„ è±Å„B!D_ha-Ý¿gΜÁÈ‘#[ú¸¸Àßßß}÷[G!„B èLš4 C‡ÅìÙ³qåÊ¥c‰‰‰€.]º(•wîÜÉÉÉj·/&„B!MVÃØØÇG§N••…ƒbçÎøî»ï0cÆ Ü¦˜+ëÒ¥ JKK‘——{{{À´iÓpùòe8;;+ÕussÓó!„B©^jjªÒýŒŒ ôìÙ{öì1P‹?êVcüøñ8qâV¯^]»v!++ Ý»wLJ~‘H¤óù¢££‘šš ©TªTžšš ±XÜhÊ’’’”þ5Ä5þþûï:Ÿ/55UqÓå±.\Ðúòº5Õ‹GNNNµõ®^½ª²!„¼^~~>bccÕ>öìÙ³î½ÑP?ËšÊnß¾¸¸¸:ŸO›ŸeÕ2]þ-dff*þ"U—÷_TT”ÎMJJR´µ1½7ª–]¿~IIIµz쓤ÇðLsEÇ,OtÌò„ÐVç×OÛ2ù犮Õåß‚6ŸC111ŠÏuõîß¿›7oª}¬|§Eu={öl­^}—ÕÇϲ¦²ÌÌL\¾|¹ÎçÓåÿ9]þ-¤¥¥)>[jª§éýwá­››› ©TŠŒŒ DGGƒÔÀ`›ˆ71ááá »zõ*cŒ±'N0,22R©ÞìÙ³™±±1“ÉdвÁƒ3???½¶·6~ùåöË/¿4è5¦M›VçsÔ¶º\[›ºÛ¶mcÿþûoµÇ#""Øü¡öØ7؆ ÔóòòÒª†V?Ëšœ={–íÞ½»Îç©M;uyiÛNMí¨éxuÇBBBXHHˆÆkZ>[nH›UPq;)V[>[8ôÙ¢}¶h>æççǬñÚÏ2¡¥¢¢"€‰‰ ÀÛÛƒ1cÆ(êÅÆÆÂÓÓSiù;ŠáY§NücÇŽ­ó9jÛN]®­MÝÀÀ@899U{Ü××·ÚU\œœœTÖ—ëׯŸv4°úøYÖÄÝÝ-Z´¨óyjÓN]ÞcÚ¶SS;j:^ݱ^½zi¼nc@Ÿ-ºÕ¥Ïúl ûÙÒ2‹¡Ñ2vjddd(WÎËËCÿþý‘GÁÔÔ€öËØ 2pæÌý>Ò$#<<ÜÐÍ M@dd$€†u³ØZRqÿ50ÌÔpíiÂè³…h‹r‹fԭƸqã •JѹsgäææâäÉ“011Áá¾úê+ >ýû÷G÷îÝqìØ1téÒ³gÏ6`ë !„BHC¢­Æ—_~‰S§N!%%Xºt)¦M›¥zˆŽŽVlå½`Áµ[y»¹¹© Þ'¤:!!!†ni"šÊŸäIã@Ÿ-D[ŽŽŽ†nF£FZ`À€ZÕõòò¢E‹4Ö“/{Gˆ&aaaôgV¢ù*Íz©7ôÙB´•••EKíj@ZOèH´EÿÁmQp&º Ï¢-Ê,šÑ:ЄB!„è€4!„B!: ­'4‰h«êî{„T'77¹¹¹†ni"è³…h‹2‹f õ„&m………º ¤‰¸pá‚Òv„Ԅ>[ˆ¶(³hF“õ„ämÑD¢-šDHtAŸ-D[”Y4£hB!„Bt@šB!„P€ÖO´E}ˆ¶h!Ñ}¶mQfÑŒ´žÐ€|¢-šèC´E“‰.è³…h‹2‹f4‰POh@>ÑMô!Új®“÷ì9„¬¬<€Ã£¸å©8‡t<¸ž  ôÃ!ϤM}¶mQfÑŒ4!„<#öïÿjîY èŒáÃè¡Eê-Z´ii¡ð.lÇ^àÚúôÙ‚þ1\€–J¥(++ÓXÏÄÄFFFzh‘zwïÞÅÓ§O5ÖsuuEÛ¶mõÐ"Bš> ЄòŒøüó½HIùDc½=64@›šÚè]~¯@I¥£®L|þvý6¬Š¡¬ÌOc=1¢¢vë¡Eêñ. &j¬çç·.ì×C‹iú(@ë È'ÚŠ‹‹C§N Ý ¢ƒœœk¬ggg[[Ûz»®|¡½½½VõMMmQL«gddØ`ÚTµGJŠæ×ÊÍ-¸áS  Þ ¦Ï–Kzjчëׯ£  @c=OOO•!b± ¡šÖ,P€ÖO´Fc›˜^½^ƒP8Pc=oïx\¸QoוO lJc¡ËʉDù&+߉´;—P$$ææ€™YÅWŽ–h䄺MžP(„D"ÑXϦ¦¦zh‘z£GÏÃÓ§Áëõì¹ ýµW©,++‹ÆAk@ZOèH´Eá¹é11i‡¬¬¥ëyx×ëuu Îb1^µñè°|¹jЭ.ðêRO&«å“UãÆ  CÕrccÕP]Ý÷u)+-ս͌©¿Éd wL*•_=¼¯6‘ëÜy8JK{h¬×©Ócœ:µG-RÏÌÌ™™Áë1ö—JeÍ(@BH#ÅP\ T+,¬ù¸ü¦E‡™Bj*°xqÃ=¯†VZʽ.……†n çâE€Ïç~ž™Hĵ‘Ç3tK7cãöxð`£ÆzîîÁ ßb0  !DOòò€;µÄ……õÛc«+>SÓêošŽ×¦ÎÛos½àštê|þ9üD" ¤DùkmÊ*zkëWcÏpí`gtïÎݹ›§§æÇ’ÆE*5ìgdz€´žÐ$B¢-šDØ4”–IIÀ“'Ú=&! ‡õ‡›DhaaÀư¶†âûª÷7n²³5ŸµgOàŸ 7–ØÜ\»z­Z“'×ïµËÊTCuu¡ûý÷µ{=]]3¸ž]u7>¿a-X o§¦I„ÀÓ§À™3ÜM®j¨îÞðð¨Ë«ül()þû{¯ˆÅÊ·ªeu½_µ¬®¿°Ñ$BÍ(@ë M"$Ú¢I„Nœ8¬¬õ||: 0ð¹z¿¾P$'s!9) HL¬øþÁƒÚ•31©9ìjsÿüù 07ÆÓn,tx¸vÏv'â––ÜM“O?Õîõts–.­sÓjí‹/äí¬y¡·70d×}çNŰŸÇS§¸›\Ë–Ê:0h×®Ÿ„‰DÜЦû÷¹¿iãúuÀOó*‡M"ÔŒ´žÐ‘h‹Âs…Y³6 5õ=õzôø —/ÿX«käçW„âª!9#£~ÿôîïDEqá×̬îç›<¹é¬¾¡ ±øll¸åá¬ÊZÀTZ1†ÀÂ86ÆòÔÚ@ã-š­ð::ß~Ë}/‘p!úêUîví×›*â’Ÿœ<ÉÝäìíUCµ««ö­ z ùùšgºÐ_}µ@ûkA"árJ ’SR*n÷ï™™ú†cdÄ g’ßÌÌj¾_Su븟SmPfÑŒ4!¤Ñ23³¤±ž‘Ñj1ƧU“’¸Þ5mµn xyq=u^^·©S¹si~>€ƒƒö׫obqº"˜Ö„1-בk 'NlUìœgyOÇ#6(((„5zàin'BW×— ÙL2†}­´!‘<†6k<ËdáÕÔ´bØÆìÙ\™X ܾ­ªcb*þ“›Ëý‚UqÎÖ­•u÷‹úë'&–!))\c;MMƒ5Ö©ª´HKSÇ•¿ô¨þÇ wè¬Z¥[ 6®ÇT¶sgí4ÑŒ4!¤ÉcL9Wþ>9™‹¨ >ŸûϽj@–ß·¶Vÿ¸¦2Ü!*j kñƒ‹Ë‹zhMõ|||*âAÄ=\¸×ǃƒ+ÐÛpkëV6l˜;:vüPc½  azhMõ–/YY75Ö |§ÆãУw“‰€›7¹0-Õ±±K&æäüÁÝä•')vï´iS›gV¡¬ HOW Æòï>Ô~G€û,hÛ–ëíî®üuÚ4n—&­[¯¾Z›gCš ÐzB“‰¶h¡î._Ú·×®®@Àý'X5{yqåµÙ÷ ¬¬@ŠÆz•{øêƒ®;vìØQé¾T*ÅúmÛðÉܹõÚ®úv-6Ò€çpùÖ- €á¶¯jûö/ Ý­L›6@ÅgKAAB–/ÇÖµkë|n33 woî&'*‡ê«W¸¸ŠÞ¬,à÷ß¹›œ³37QÙÙÜ:啃rZšnsx<ÀÉI}@vwçÆ­›˜¨l}ö7$±8À_ë1¦ÚõN“5k"oƒ¦&mÑ$ Ú.)Vul¢ú€ìåÅõ0óùõÛΙ3‡ %%\c½~ýÆ×ëuëºá7»vaÅîÝÜ·/zÖgÓê͹‹Qhk ´°ÅÃô4<-(€-´û…(“¶,]¿û®_ÇÄóç1°ÿz¿Ž…з/w“+.æ6¾©ªïÝ«ÕÚŸ?!A»uÊÔ‡cnÂcmó!cRÚ,½cص 7lxyy)ëªþ…&jÆc¬)¬NÙ´ Éa„hrïpîð÷ßÜ-55GXž IDATÚìžæâŒ°°pEXÖ²Cö™&•JÑåÅ‘ðÞ{è»g.þü³ÁÚRÆ„2ŠËÊ ,+C±LÆ}-+ÃúC0¦ÅGÜo=……ÈmýB½o°¶6u˜<Éヒ^;vàÒ/¿¬-EEÜJò¡‡C* ×â‘ÁÂѲeõÙÝ ò á£V!-Myù‘H33å ŽÑo¿ýZÃ4¢222ð^h(k‘E(·hF=Єƒ`Œ›Õÿ÷ß¡¹¶¨qq©ÿõ€›»ovíBêÀ€µ5âíìpùêÕj{¡Åòp[õk•°«Íqueâêfo]¹ww  üOÖÖ8óàVÿö¼áenÎÝÌÌàYþ½›@#ÚJ¯ZKׯGÊÈ‘€¹9۶Źê…Ö†•0`w€èhí&ãúûsŸ66 Û¾êlØð©ÒýsçÏãÕ©S‘‹†Jíõ`aXN>y‚SgÏbØàÁ†nN“Gš¢eeÜŸpåùÂ…êgˆÛÙýûÿþËMB"õK*•bëádžòÆŒÁˆE‹ÐqåJµaWf¨?Tþþ;ðú àx¥²AƒP›3gâfQ‘ÊCLx<´33ƒ—¹9<Ë¿Êožff°lÀŸb±›vîÄ‚÷4/½ØÐdŒ!W*E†D‚ ‰Äb¤äçãûK— ä)aaØÐ©Ú››ÃÛÜM`F¬¥¥á³:!6àñ‡bù¦MX]þÚ668—žŽÂ9sºaèz@ZOh!ÑVs™D(‘W®T Ǹx‘ÛšZGG.0ÈõFùùq“|:v¤]]'Ê}°e ’ä^d°²ÂS\¾z•[{«ŽŒyXµ g#"tz¾"™Lc(~$#W*­ÝÔ4¡ÛŸ½jïøØ±ÀW_~~`ÒÅb¤‹Å8[ez>Wy¸®°=ÍÍk|mjOXl„Ÿ5À¹ëNÊæo܈¼·ß$kÖ`Êû1”1Æ}÷‹RYùW•ïËëV{¼çåæ".9xå@þ‹/jì…¦I„šQ€Öz#mécÒF¯^¯A(¨±ž·w<.\P ¸^ey`¾r¥úU3¼½+ó€Ü$m>¼BÑËZeÚ°™Ñ68'–”àóû÷q0;ì·ß€#*zŸå¬­aÔ¶->‰ ¾"ǯüäNª_/ÌݧŽCnn.ìííá"ÀE P»ÀÝ“ÒR¥@\éût±XixJqYî㎚_.x<¸ \¨®Ôk-Ù›¶mCêС€­-bÍÍqûÎtõóCaY™"?ª! ?©Ë¾ðZš˜ÀÙÔN¦¦p–ßÅ÷á_}…½Ÿ•Ñæææ°íÔ ï<} ¾ŸKJPÞƒ_Tiádcx áH„SUÖ7*6ÓÞܪ„kw33­Ç¤ÛÚ>†O°â~NIòmJàVx æf­åžžŽº¾µ_â£!ìÞ ¼Vi2£½Ð”Y4£MÈ3ÈĤ²²–j¬çá¬ø>/·,Ÿðwó¦ú x<Àǧb8Æ€µß$!°‘.­ÖTdJ$ø"%ßed@ÊPZ Þ¥K`U{ŸËå‹V¯ÆÅÇõÜReŸ……ÁÈÏv‘‘°Éq„ ¥»â˜Õ?wa—vùVlÚ„Õ<¹ÆÆènmîjvÁËdH‘÷ZWé½®:4¤Œ1܉p_$RݾR*…ѯ¿¢¬|mµì±cÑ;$üë²{GF<LL”‚°“špìdjZãðŠ‚‚̸u ²aê7tyúâ‹øKÍŠ ¨…BE¨–­ü¼ÊCrI ’KJUåÜ&<<* ©Ü{í&€_)\_»vTñ½P(D×ñã‘ûn(\öìÁùCáÚ¿p•Ÿ{i)€Õ„âl‰D©z‚jö£/‹qúÒ%­—´“?§-Z¨{$«kùÄNa¥p-–ÉpW(ÄݪáZ,/* lÉ@ÎË/cà¢E0ùì3Ö¡G_Έǃ½‰ †ËC±C¥¯­ËÉËlÔü¼ú.Zá¬YjÏÿ((¿îÚeð9ä+o ê{³\M½Ð4‰P3 ÐzB“‰¶ÛN„ЫWÅŒ>}¸e¤ˆáÉ'¾>[Úh#``•pÍÀ…ëÊÁ:A(T„ëyoqd$ؘ1´²‚ÈÙ¢ØXµ3ŽyìäX]®r¿•‰ êÒÏ{îüy$¸¹U;ÎYæãƒC_mð9+oT§†^hšD¨h=¡7"ÑVc Ï]ºp•ÔÑ8;¿ææ¢ëÕ«ˆ©4ñ­ƒ…Vzx`|ëÖl]ó'‹±çÄ H.T{<{ìX|¸z5ΔXC3ôg‹|™¼¶Õ„ë‡b1bòó1%99'*?øÕWáòí·˜?z´RïpëòPl¬ÇÍs>\±¹={r³¨«‘le…U[¶`Å‚zkWU%%%è}ìXudeeˆ½_%@SfÑŒ4!ϘóçôtíêZ[Sxn¬.<}Šäd\¬´[Kڵà gg½ŠgÕÚmÛ𠚉ykkÄXX(Vä Õ“™Ù¾gWO.ge±³3úfd gzo_e[—.ÕüWå.]àë뫟Uãç; zýæŽ4!ψ‹%K€Ó§ ÝRÿ#49Çóòe-Œñ‰›>pq9mv¡7¿Ÿ< SSî·ÒjÈÇ”EZ#¡PˆÿÒjzôs^~ó׬ÁùC‡ôÜ2e}úô1èõIã@ZOh!ÑV}O"ü÷_.8Ÿqõ„&mÕ×$Âèh.8GUZ˜Õ‚[–îС¤¤¤h<‡LV÷ï¤nr¥R¬|ðÛ=R,ÉeÄã!ØÉ }rrÐ*=v´^6ÑBc› \Õßÿþ‹öFFÜbóÕ …8EºÑ$BÍ(@ë ½‰¶êúÜ•+ÀÒ¥Àï¿W”™›ï¾ ,\88-[AJŠæëôë7¾Nm!µW\V†õééø*- •v©{ÅÞ«<=ÑÉ¢ú=Ð Q£1‡gøûèQÍ•ˆ^PfÑŒ´¥¥¥X¼x1„B!–/_ëJkh~ñÅÈÏÏWªoccƒ/4ìŒEHC¸v ÎÇW”™›ï¼|ò àXi÷ÛÙzoÑŽ”1ìxôË<@–D¢(Ø¢Â<=ÑÛÆÆ€­#„P€ÖhÍš5X·n$ BBB”ô®]»`ii‰öíÛ+ÊìÊ×[%D_nÜà‚så%~ÍÌ€Ù³¹àììl°¦0²³±èþ}$•TlÒÍÊ «==1²eKÃ5ŽBˆ Ð5¸{÷.Ö¬Yƒyóæ!,,Lm)S¦ D‹Ý†h!Ñ–¶}nÝâ‚óÑ£[l À¬Y@h(ЦMö“ÔŸ¨ü|„&'ãFQ‘¢ÌÃÌ Ë=<ð†£cµ›>T݉š4öI„¤ñ I„šÑzGÕÉd˜1c,X€Žõ0Î&mU÷ËšÜ;Àøñ@@É…gSSnŒsR°y3…ç¦ârA†Ü¼‰ Û·áÙÁÔ›Ú·G\Ïž˜\Cx¸å»¢‰¦ÏBä(³hF=ÐÕØ¸q#„B!.\ˆýû÷W[o×®]سgìííñüóÏ#44¶¶¶*õh@>ÑVu}bb€eË€Ÿ®èq65fÌ>ýpuÕ_‰f%%%077W{,^(Äg÷ïãpNŽ¢ÌÚÈó]]1ßÕVFFZ]clMÛôREcŸDHÊ,šQ´‰‰‰X²d ¾ûî;˜˜˜T[¯W¯^˜2e Þ}÷]ØÚÚbÆ  DQ¥?ÃÊ]¿~{÷îEdd¤ÒMþ'X9*£²ªewﯿøùEâС\0˜˜o¿ |óM$–/ÏU Ï¡ÍÏzYLl,Úxy¡¨¨H©Þ#±oÇÇ£ó† 8œ˜0åó1×ÅßdgãVVJáÙÐÏ£1”={"‘¨Q´…ʨ¬¹–É3Ihh(öîÝ‹ÔÔTšQ€®‚1†™3gbÖ¬Yè¡aÉŸ~ú ‹-Âûï¿ãÇãСCHLLÄæÍ›õÔZÒœ%&“'¾¾ÀO?AœgÎîݾýhÝÚЭ$ê|°r% Þy_lÜxZZŠädxGGcgFdŒÏãáMGGÄ÷쉯½½a£e¯3!„Ãã1&ÿc0€'N`Ô¨QX¶l,--—/_FDD¾øâ bäÈ‘j[VVwww<ÿüó8pà€¢<88b±zy¤i‹ŠŠÃ¾}ðã»S§Ÿxx¶}¤f1±±†ì3ÐnÍÌ\·ëóòð¸ÒZΣZµÂjtµ²ªÓµž‰I„7K­«’à50ÌÔpíiÂh!ÑÖ¤I“ hØO h tfff ıcÇe9åã###!‘Hª Ð………ÈÏÏW;šäMåË~cá##`Ê.8{y¶}D;¬\‰ìñÜ4FŽÄ¢I“}llæé‰-ZÔ˵äi,4ÑFc߉4´¡f «0`.]º¤TŽéÓ§ã·ß~ƒ““àáÇ077GËòµY‹ŠŠ°jÕ*…B >\å¼ôF$ÕINæ‚ó¾}×I##nøÆ¢E€··¡[ظ”––ÂØØp]eŒá‘D‚T‘©b1ˆDH‰ð@,FÂÝ»H”Éùzð~~À‘#èÈã!¬KŒ­çžb ÎDž‰¶(³hFº–Ο?àà`tíÚÖÖÖøçŸ ‰0kÖ,ŒOÛ?«ºu…âbõ:w6æMÛ±b°g<8|>×Y¹x1СC7¶ ŠGß¡C‘vï,,,äÅeeH‹¹P\9$—}(£´º‘oááÜBÜ•ð^y/?±6H{ !„èh-ôë×»wïVšñâ‹/â§Ÿ~­[·PTT„_|}ûöE¯^½ ØRbh"‘’’Â5Ö+, F‡€TÊÝçó‰%K€zXv¼Ùš»bžÎ˜›6a•UÅdK$J=ÇUržü‡¢ƒÖ&&h‘$[[ˆ«ìFʺvÅÏ_~‰%EE°ªã˜gB!h-x{{ûÊßÑ­¬¬0fÌŒ3F«sÐN„¤²ìlî+ŸL˜Àg®Œ&ú¨[Œ¡lÈøòK|>w®J/´D&CšX¬2´BÞ›œ&C$Ÿ™©%c.Ú™™ÁMþÕÌ í¸•—YaØäɈ0Aí9RGŽÄ7bÍçŸ×úù«óLL"$õ†>[ˆ¶h'BÍ(@ë M"$UɃs—.Êå4ÑG½¹+V ë•W©/¼€WV®D×3”†[dJ$ÐuY!k##.W È•¿ocj >¯¦ý¹•7n@ùœˆªd]»â`X×s/4M"$º Ï¢-šD¨h=¡7"©¬kWààAõÇè?¸ 2Æp«¸®^Å9‰hÕ PæïË–áÄ€@ ½$<ަ¦Š㪽ÇíÌÌ`Wç,Z„œ.]€›7«­“æêZï½Ðœ‰.è³…h‹2‹f  1€šÿÖ,Ä …8óø1N?y‚¿žujµ½Ç®ü†ß/ê“Ù³ñzZZÍ•z÷F÷nݼ-„BhBˆA=‰pæÉœ~ügŸ<Á£ªóÒÒ¸ß8Ê{Ÿàvânûù5ØŠÚ=b„A¯O!D¿h+o=¡I„φºŸ#..®î'iIJ$ÈÎÆÛññðŠŽ†û¥K˜‡ýYYJáÙÁÔÐù×_×_W{®´^ÀŠM›ôÕôF'77W1‘Mšûg ©?”Y4£­'4‰°y+->üÈȨû¹ÂÂÂê~’FäIi)ŽææâƒÄDø^¹§þÁ¤ØXìÌÈ@rIÅÍ¶ÆÆxÙÞ½½q§Gdöí‹eFFÈ31Qí}.Wêg! õõt• .(&¢Isûl! ‡2‹f4„COh@~ó•™ ¼öpþ|ýœ¯©Oô–•áÂÓ§8óä Î<~ŒkEE©ÙxÄœÏÇó¶¶jg‡!-Z »µ5Œª¬v1gñbduéÄÆV{½4¬Ü¼+?ù¤ÞŸKcG“‰.šúg ÑÊ,šQ€&¤þù‡[ŽîÑ#î~«V-áé SÓš7`€Ã7NGpvvÖùqRÆp© g?Æ™'Op© 5k-›ðxèicƒ!-Z`ˆúØØhœà÷î›o"(5¨nç?ðóCïçžÓ¹Ý„BHmQ€&¤–¶nå†mÈ7®{çàë¯×k ÏÑå«W1üå—qÿ¿ÿвšµŒådŒázQ‘"0_xúÅee*õxü­¬0ÄÎCíìÐßÖVFF:µküK/éTŸBÑ ÐzBò›’.,ïÝËÝ73ãÂôôéõs~Cì6Í~ú)>]»ÛW¯V9[\ŒÓåC2þzòOJKÕž§£……bHÆà-ÐÒĤ¡›þL£‰.h'B¢-Ú‰P3 ÐzBò›‡”`Ü8àÆ î~»vÀáÃ@÷îõw }ïvùêUĵjæë‹¨³g‘ŸŸ§8ýø1Δ/-—)‘¨}¬›™†´h¡ÍmèW¯h'B¢ Ú‰h‹v"ÔŒ´žÐ±é‹ŠÞxÈÏçîDDT»@D­éû?¸ùkÖ 782z4Ü?þ…S§ª­ÛÚÄCÊÃò;;x››ë±¥¤* ÎDž‰¶(³hFš V­/äsãBB€•+=lr× .]¹‚›vv[#zz¢ððanAkkkØc ­­b³¯¥%x5Ÿ’Biö(@Rƒ‚`êTàèQî¾µ5°gðÊ+†mW}8ýø1Æ,Y‚â÷ÞS>ðê«èyâ6­\‰@5KËB!Ϻ&ÞÖtÐ$¦'6èÑ£"<ûø—/7|xnèÝÂâ„B¼tç†:„â6m*zŸå<=‘ýèÚ—–Rxnäh'B¢ Ú‰h‹2‹f õ„&6-‡½z÷îq÷_}ˆŽô1½¡v Ë•Jñ^Bü®\Áñ¼=Ÿ®]Û í õ‡v"$º ‰¶(³hFC8ô„ä7 eeÜøæ¯¾âîqcõ¹É]}Oô‘ÈdØôð!VXëå…Ñ­ZáÆªUšÿD×»7Ú·oßÀ-%„Bš ÐzBò¯;÷ßä?¢3€-[¸ ¡.»…¥ŠD½YY`åeö&&XæîŽ·Û´qù¤À€€€zj-1$Ú‰è‚v"$Ú¢5£I„zBò±˜5 xûmî{SS`ûvàûï žÚMô),+Ãg÷ï£ãåËø±<< ø|,puEb¯^x·m[Ex&ÍM"$º I„D[”Y4£h=¡ùKZ·²Æ•+Ü}à矹•7 M—‰>eŒáûŒ ,NIAV¥í¶_sp@˜§'< ù›ip4‰è‚&mQfÑŒ4yæœ9ÃM”/Ÿ;hðÓO€ƒƒA›¥³ùù˜Ÿ”„ÿŠ‹e½ml°ÞÛ}ll Ø2B!¤y£Mž)kס¡Üru0oðå—€qú—[\Œ“’ðG~¾¢¬™Â<=ñzSû-€Bi‚šPlhÚh¡aÓ§sÃ4ÀÒ’ëó\]âæ+##C7‘4B4‰è‚&mÑ$BÍ(@ë ȯ›Þ½Çãñc«ëÙÙfɶÞ|ر07×G k§Œ1ìÈÈÀ’û÷‘#•r…?þˆ77lÀ*OO¸Ò©|!…&Ú k–ã ³²²!C† k×®†nN³••Eã 5 ­'ôF¬›‚+Ü»®EÍ`˜˜ëÖï¿ßЭÒL"‘`ÕÆXºp¡Ê±?òóñqRb+m„ÒßÖëB µµ>›Iš( ÎÍ[dd$>üðCX[[ãÚµk055­ÓùÂÃñoß>¸ººbàÀõÔJýHKKÃéÓ§1dÈ•ÿOSSSñÑGaË–- ë eÍhí+Ò¬˜˜p; 6†ð vìÀW‡áâ¿ÿ*Êîã…Û·1êömExö27Çá.]ðw@…gBàÛo¿Ejj*þûï?=z´^Î9{öllÙ²¥^Î¥O7nÜÀôéÓqíÚ5•cööö˜6m O!zEš4+]ºýúº‰D‚ïŽGñ²e˜¿f ²$¼€«Wq¢|A;cc¬óòBl׺µ[Li,ÒÓÓqâÄ ¼õÖ[°³³Ã®]» ݤFËÃÃááá2dˆ¡›Bž!4„COh¡~˜˜º6ìØÔÁƒ ÜnÝž»vAXÞCbÂãaNÛ¶XÒ®ZVi4Mô!Ú¢I„Íמ={ “É0kÖ,cÇŽxøð!Ú¶m«TïØ±cøí·ß°zõjØUYâòÓO?…ƒƒ>üðCdggcîܹH$¸víÞyçE½mÛ¶W¾ÂORR6n܈[·nÁØØݺu"%Á= IDATÃǬrÝ ÀÝÝ“'OƺuëpáÂ899á7ÞÀK/½8pàŽ;†û÷ï£_¿~X¶lÌ+MJ‰D8|ø0Nž<‰ÄÄDH¥R´oßÓ§OÇСCõ~ûí7lÛ¶ °}ûvDEE1sæL;v “É Æ¤R)žýôÓZÓÉÉ ÷ï߇¥¥%z÷î(—H$˜3gÌÌÌpéÒ%x–¯ýyëÖ-ôë×ÿûßÿ­è©€‹/",, Ÿ|ò ''íڵÄ бcGÄÄÄÀÚÚ2™ “&MÂÁƒñßÿÁ·ü3ÐÓÓiiiprrRœ³¤¤½{÷Æ¢E‹0uêT´hѯ¾ú*LLLpáÂÌ›7¯¼òŠÆç{ëÖ-|üñÇ0`Ž9ÛJÛÏfffÖê5|–PfÑŒz  ©gKJ°âçŸQÖ§Ryé¸qØ·u«ZEi*víÚ>Ÿ7ß|SQ6uêT$$$àüùó rͨ¨($''ãã?V„gèÖ­fΜ‰+W®àêÕ«JqvvVêÍmݺ5zö쉧OŸbéÒ¥°.ŸÍçó1²|'«Û·o+êÛØØ(…g077ÇܹsQPP TWWëׯGii)¶nݪž¨\“Ú MH=º/¡Ë²e(>\õ ¥%Z·VZ‘ƒB*+((ÀáÇ1xð`¸ºº*ʧL™‡Ý»w7Èu“’’}ûöU9Ö§¼3 >>^©ÜÇÇFU6y E/sÕòª½¿gΜÁرcѾ}{XYYÁÌÌ ï¾û. 99¹¶OñññpvvFÇŽk}BjBC8ô„&Ö c†nfgŸ<Á„›7‘wþ<°r¥Ú:ùcÆ`þš5¸ôË/Õž‡&mÑ$ÂæçÀ …(,,TšèÖÖÖ8tè6mÚ+«š7–R'..®Úcyyy 2Z¶l €¢Q™¹š]ªŒÕ“—Ëd2EYdd$Ƈ.]ºà…^€Ú¶m‹»wïâÓO?­Óÿ›999ôï¢h¡f õ„ä× c-\]*Âzöì ·6UöÍÇø(1¥¿þ ŒU}ÅJ½ÐÏWâ!×\w #õv"l~víÚcccdeeáÏ?ÿT:fii‰ŒŒ :tÓ§OÅæ*OŸ>Uê±€üòå2åjš ,óšžžŽ.]º(KOOÀ-WŸ6lØSSSüñÇpqqQ”—””ÔùÜ*CNˆöh'BÍ(@k––†ýû÷£mÛ¶˜2eŠÊñ#GŽàôéÓ=z´ÒÒ;•ѱö.\ÖÿŒÉ;W"“áÝ„|Ÿ‘àß¾ g بYì_®T$ÂÁ߯6@Sx&Ú¢àܼįÆ"::o¼ñöï߯r¼¸¸ŽŽŽØµk—"@ËCóßÿ­4l"::Z±J…\xx8Nœ8¡Ä•uëÖ ÷Ú /¼ tìÈ‘#àóùð÷÷¯Û¬âáÇðññQ Ï×3]•|³º¶«Ó«W/œ>}ÇŽS,«G´G™E³FC¯™3gâÔ©SèÑ£‡J€ž?>¶nÝŠ×_ùùù ºuë0wî\µ¶ù).‚ƒ™ 03öîm<á9S"Á«11øçéS€«@€È_~Ás´“ !¤䛥Tž>>¸xñ"Ž=ŠîÝ»ÃÄÄŽŽŽèÙ³'F…Ý»w£wïÞ˜:u*ÊÊʰiÓ&?~3fÌ€»»{½>____=z?ÿü³â—Á7*–Þ«¬C‡022¡C‡˜››«L”ûä“O°{÷nÌž=|>AAA022Bff&Ž?Ž™3gÖës!ÏšDXƒÝ»w#55#FŒP9‹õë×cûöíØ½{7"""°bÅ ,\¸Peœ©½… ò¹-X±ðñ1l{ä®"ðÚ5Exîgk‹«Ý»Sx&„ÔŠT*Å?üðöÎ;¾¦óãï›)‘D!b%!¨­µŠ”Ú¥ÖR¤(QÕªh•µÕU»Z£Ôl©šU›¢¨ˆÈ0""!‘È›œß'‰ìÜpWnž÷ëu^Î=çÜçùÞëæ¹Ÿû=ßAÅŠóüÎÉ ÿþ™É„U«VeÚ´i™åæ,--iÖ¬_ýužÕ&¦M›F¹råðññÁÅŇ̸äµk×òöÛo3dÈlll°±±aüøñøøø0þ|µ¿æY³fQ¡BÞÿ}ìíí±¶¶fÙ²e¬Y³&×µŽŽŽLš4‰³gÏÒ¼ysùè£ò󮮠ƒbggG×®]±²²¢\¹r8::2wî\µ¿AÉC!IÅ!=Kû<|ø0ó×ñüù󉈈àìÙ³™ç¿øâ æÍ›Gtttf‚ÅÿýGíÚµùî»ïøôÓO3¯­á›é ¡²rúôi¬­­©[·n¶ãÁÁÁܽ{—-Zdó€'$$pþüyªW¯ž-V;66–ýû÷AݺuyóÍ7Q(üóÏ?xxxàèè˜müØØXBBBˆÅÞÞOOOâââ¸téîîî899e»>99™3gÎpýúuRRR¨^½:íÚµÃÒÒ²€ÿ A¿~ý077!… t>ôèÑGGGV¬XO.íããùsçx˜û ²¡téÒøúú²|ùòÌã–Ž=ªÕ×Pœyö êÔ{÷ ti¸zªW×­M©’„p0óïÝävÜ‹jÔàãü2_‘Áƒ‹EK ±¢ ]B´6k‹@U¼¼¼¨\¹²ø¼€øóôíÛ·sáÂ…3–ÃÃÃñôôÌvÌÔÔ77·<+nDEE±iÓ&vïÞmËð e ŽÉÇFŒˆ"]§2w.\»¦[ûb”Jº\»&‹ç¿ÿ¦\BÖ«—)žÕ9ï† ôîÿCÓÏc>>>øøøè…-š:vìØ1’’’ô–â~,C éƒ-â˜~ËÐ$'NdÓ¦M GèDGG3zôhV®\‰®Í)‘\¼Ñ.íÚÁÈ‘ºµ'01‘¦—.ñGzI¨*¥Jq¸^=ZÛÚêÖ0@ :A„päà³Ï>cÿþýL:5óØ‚ ˆ‰‰aúôé4oޜʕ+9„Dy2Uxòj׆ˆ(S®]“ë>ëŠýÑÑüïæMž)•¼ooφš5±ÌÑ}K hÂ!h¡[ GO ‚é …‚äädüýý3EFF’ššŠ¿¿?K—.¥råʸ»»³gÏž $E’072bcÍš,¨^]gâ9 @'ó ŠQQQBh”Pââ∋‹+Òs4±¶$%%›ÙüuIHH &&F-c ^¡Y Gh-!òófèPˆ•÷W¯–㟵Eä‹x]¹ÂêðpœÌÍù«~}>ÐEüHD¢@UD¡ (‡µeá…ܹsG×f”x„f)¡%D@~nV­‚C‡äýAƒÀÛ[{sÿ‡ÏõëÜKÿ•ý– ¿Ö©ƒ£™îëËŠDªˆ$BÃE©Trûömnß¾M™2e¨W¯¶ù4oJIIáÒ¥K$%%ѰaÃ\MÀž?N\\k×®Íõܨ¨(ŒŒŒ2˱‚\ºÕÌÌ [[[9wîæææÔ¯_ •ìOKK#22##£l¡÷îÝ# €ÄÄD©[·.¥J•Ê\\\ðôô¤{÷î´iÓ†Š+²5£ÎgŽ9BùòåiÖ¬mÛ¶¥råÊìß¿?Û5K–,ÁÑÑ1OÏn‹-èÚµk¶cÕªUÃ××—õë×S¦LÚ¶mKóæÍñððàÒ¥K…ÚŸ€®®®>|€ÔÔT† B•*Ux÷ÝwéÞ½;M›6ÅÖÖ–sçÎe>wôèÑ8;;g{/óÝ~ýõ×ÌkŸúH¥×.UA¸Ý´„È—¹u ¾úJÞ¯V æÍÓœ ¼ùÏ?™â¹véÒœoØP/Å3D~ ’ ‹àà`ÂÂÂŒ••ï¾û. ™qßqqqøøøÎÇ_yÞ’†Ð,…#<ÐZBäCj*|ð$&‚‘lØšnð÷{t4ýoÞ$V©ÀÇÎŽkÕÂÊØX³¿"‰P *"‰Ð° ÀÍÍM¥ëó=–ë\Q~œuì¾}ûbnnÎò|n·nÝ>|8›6mbãÆ¸¸¸0tèP>úè£<¯Ï‹;w2qâDz÷îÍÌ™3³»}û6ñññþðVy®’ŽÐ,…#<Э1gœ?/ï ­Zix¾»wévý:±J% `r•*üZ§Ž^‹g@PrɈqÎK æ…¢ˆµê¥ôFQYIIï¼úºcÏŸ?Ÿäädz÷îͳgÏr733cÕªUܽ{—eË–Q£F ¦M›†««+—/_.tüóçÏ3pà@Þzë-6nܘ˾R¥JQ¡BΜ9“ïöÆoé5 !<Эðï¿0uª¼_«|û­ææJLKch@["#(mlÌÆš5éio¯¹Ià5Éð<¿NˆC^dÄÈ_¿~=[Ìñ‹/¸ÿ>j¨}ß§OªV­JïÞ½iß¾=¤larvvvŒ5ŠQ£Fqáš6mÊš5kX¾|y¾c‡……e&VîÙ³'WÙ:OOOΟ?OõêÕ±k½@ ´–(ÉI„))rèÆ‹`b7BëŸZ¸—œLËË—3Ås•R¥8Ý A±Ï¢¡@UD'BÃÂÉɉ.]ºðóÏ?sñâÅ\ç•é¡hE%C˜ïܹ3Ûñyóæåë~¼½½Ù³g×®]ÃËË+Ûg3¯øc'''ÌÍ͉ˆˆÈwÌgϞѵkW’““ùí·ßòÇC† ÀÏÏÔÔÔ\çEüsÑ(ÉšEU„ZK”ä€üiÓàêUyßßš4QϸÛwí¢w™ÿŽ¥çD¾x@[[[~©]›ò¦¦ê™PKÌž=[ÄA T"#PÄBË–-£eË–´hÑ‚AƒQ¯^=ž>}ÊÑ£Gññña̘1E³uëÖÔ«W-[¶ððáC:tèÀÙ³g¹{÷.NNNjµ¿C‡üþûïtëÖ6mÚpøðaøßÿþGbb":tÀÅÅ…öìÙCJJ Æ Ëw¼¹sçrýúuÚ´i“çºØ·o_êׯOË–-™8q"3gÎ$ €=zP¡B}@ˆgªálxT©R…k×®ñå—_ròäI~üñGÊ•+GÓ¦Mi•%q¤qãÆyzZ­¬¬hݺ5•*UÊ<¦P(8pà£FâäÉ“„……áååņ ðóó£LŽBü­ZµÊUEÀÞÞžÖ­[S¾|ùÌcÕªU£uëÖ˜eiDÕ¦M:ÄW_}Å”)SXºt)¾¾¾lÙ²…uëÖI™2e¨S§óæÍËVn¯V­ZÙ*‹899Ѻuk$IâìÙ³¹ljŸ¥ê·ß~‹—— ,ÈœÇÁÁ ˆêFE¤¤j–¢ òÊ*¨•Áƒ%O%%AÆpó&˜™ÉbZ]9otî̵¡Ci½cžS§²2<3##–»¹1LdZ †Á%¬H|ù¸·9´]CMRRuKQhƘ4IÏS¦¨O<ïÜ»—Ð:u |yN›šrâÔ)¨Z•ŠffüZ»6͵ÑÖP A‰E$j‰’ÿ÷ß°p¡¼ÿæ›0a‚úÆžºjqï¾ @J¯^°};¬­¹Ø¨‘Aˆg‘D(P‘D(( ê^[öïßO›6mD›l¤¤i–WAh-Q’’Ÿ?‡Áƒ!- ,,äªê*½¼sï^BêÖ•ËyØØPªBVš˜PÉ@ÚŽŠX=ªˆN„‚¢ îµåæÍ›œ8q‚û÷ï«u\î)IšåUZK”¤€ü/¾€;wäý™3!KÙÑ×fÚêÕÄgIHêÙ“ sæ¨o#bΪâãã# *£îµ%C8;;;«u\î)IšåUZ V†•+åý·ß†O>Qߨ;÷î%¨ví—Þç ll¸aaÁ¿×®©o2@ H†€ÎZñC ()-P±±0dHXYÁ†  Î*rSW¯&!‡÷9ƒHÆÎš¥¾É@P B@ J2B@k‰’?v,Ü»'ïÏŸU«ªoì{÷rËÓ3·÷9kknXZ„Z$ TE$ Š‚º×Âa¸”Íòºˆ2vZÂÐò÷í“=Î:ÀðáêÚòå¤í[˜*8˜å®›òì³–/g˪Uê\ˈN„UEAk‹R©$"" Ê•+§–1úƒèDX8B@k Cþ FGÃGÉû¶¶°v­zÇO“$ÌgÌ@zúc…‚ãõëÓÐÚ:ÏkM‹YÛî¼âY *B8 Š‚:×–‡’––&Â7 CÖ,êBhÁk3jDDÈûK–€ºïæ-~ð€ ÏŸƒ™ã\\hno¯Þ @P$¨ªÎX= !b ¯Åöí°m›¼ïãªwü¤$¾  †…SÅb-:çܹs4mÚTÇ–ºAh-aˆùÁÇËûvvðý÷êŸcø­[`¨£#mmmÕ;ž": TEt"u®-h!  CÔ,êFÄ@k C È߸öî•÷ûõƒ^½Ô;þ£/€£™óªWWïzŒH"¨ŠH"u­-¡¡¡DFFR­Z5ìENŠAbhšE´:66–‹/r÷î]$IÒµ9Ãýû/; ::²eêŸÃïömž*•,wwÇ6¿úÏ@ Ð*áo½õ–Ž-t‡Á èßÿN:áää„­­-Mš4ÁÕÕš4i¼yóˆ×µ™Åš¡C它?üê.º'*Š_? §½==ììÔ;@ ^™Œð ! %ƒÐ{÷î¥I“&ôîÝ›²eË2jÔ(¶oßÎ¥K—8xð 3gÎä­·Þâ‡~ J•*̘1ƒgÏžiÍ>C È_µ ’÷‡ .]Ô;~¬RÉÇ·oPÖÄ„ennê  ’ª"’EA]kËéÓ§ÿlÈŠfÑ$s_|ìØ±Œ=š!C†`›G²Y‡$‰ƒ²dÉ*W®Ì| û ! ?8Æ—÷+W†… Õ?ÇÁÁ„§ÿá.¨Q#ÏŽƒ†ŽèD(PщPPÔ±¶Ü¿Ÿ .P¶lY4h Ãz‡èDX8# ƒ‚‚0R¡Ä™B¡ S§NtêÔ‰´´4-X&SÜ?ˆiiðᇠ¬_66êãxL ?„‡ðNÙ²|èà Þ Š B< TEgAQPÇÚ²mÛ6$I¢gÏžÑùU7Å]³hƒ áPE<«ã9%•ŋ᯿äýQ£ÀËK½ã'¦¥á{ë`ilÌjwwõN ‚×fË–-ôíÛWÇ–ºÅ`<ÐYÙ¾};fff™Þ™Ó§O3eÊnݺŸqã;v¬Ž-,^À—_Êû5jÀœ9êŸã›ÐP‚˜^¥ Õ,,Ô?‰@ !Î;ÇÇ ½®fÍšÔ¬YS  êçöíÛ\ºt Ú¶m«ksbzÊ”)|ûí·™G¥¥%]»vå‹/¾ ÿþTPw×B(®ù©©0h$%‘‘\ÿÙÒR½süÇ‚{÷hbmÍ'•*©w‚bF@@€YÅŒ9~~x_¸@A­~þU(¸2r$ß,_®¶y3íD¥ ¼îÚ²uëVz÷î-îà8ÉÉɘ››ëÚ ½Æ ÿîÞ½‹‡‡gΜáòåËüøã¬X±‚¶mÛräÈ­ÛT\“çÌóçåýÏ>ƒæÍÕ;¾R’vë©’„©BÁÚš51V(Ô;I1Ct",~|¹laööø@¾ÛWWÆLŸ®Öy‹Ú‰P©T’””Tè–ššªV;úÁë®-"|£äP\5‹61Ht¹r刉‰`Ïž=4lتU«àááÁÅ‹éׯŸVmÒ×€|¥R‰2½aIN®]ƒ©Så}OOS¦O7VûüóïÝãrz]î •+S·tiµÏQÜI„ÅÆM›²ÈØÇóôB_62¢rÇŽ”SsÑô¢&vnÒר¨¤F½xk§N,X¿þuÍ诳¶\½0Füã IDATz•›7oR¥Jš5k¦>£z‰¾j} ô;ï¼Ã?ü€¥¥%[¶láÓO?Í<÷àÁêÕ«§Cëô‹7ÞèD||î6Ù’AJŠüØÌ,só­j;0!©¡¡Ô´´d’««ZÇ×ý ÉŠ©©)ÆÆêÿA"Ðc,`q×®LIo”•EÎÎ,œÇÖ–¦íÛkÔŽÂ R©Ó^åÊ•qrrR¿QQ˜M(gnyüS²[döJIèhy{ °¶&ÊÊ ¬¬°+[V%QndmMï>}Ø2gr »ÀÁÏ-z=ÛÔÀñ#GˆH¯ _6̆·]2ÏÝ=Á£‡òû÷FƒxÖ©£‹#¯º¶Ì›7¥RÉðáÃq(¡õùK"‰°p F@/\¸ððpºwïNóæÍ122Ê&žÊ—/¯RÌ`bb";wîäÒ¥KDDDP»vmºvíJýúõ³]wöìYŽ?žëùÝ»w§V­ZÙŽ‰€ü—ˆ/ùøömž¥{>W¹»cm@±À …‚>&°%/ôwŒ+Äç$B Þ8C3gòn^èå¶¶ŒÒƒÐÏçÌábŸ>Œxò$ßk|]\˜6mZÁÅÇgÆY…² nLL jUpswwùߌ­re¹Î#0öüùÌXèE®®,ܾŠ’<˜š qq*‰mŸ‚ÎÇÅÉ-Ds`„,¢·²_|%·Í##(_>óßW¬çþåòåüÑ¥ _åñ“á‹þ±jU~È(P/P‰WY[¾ûî;’’’:t¨ø+AˆÿëÂ1íåå…—— :tˆ½{÷âëëK||<íÛ·§{÷îtëÖ {{ûBǪQ£ÑÑÑÙ’¼V­ZÅÈ‘#9tè]ºtÉ<îèèÈÙ³g5òš ‘m‘‘ìMÿRì_±"Ë—×±Eê§ç€ôž9“~¹<|O?Æ£Iù¼‘‘ü¯Žö‡KLLr èçÀE##¾Ø±~ûM.ü±YX佟×c3³×~/Û´kÇÊjÕHxò„¼~ßÊ´n££#$&BPPÞB9"Bµ ŒÀÕ5»8ÎËUªÈ"º2*r‹ŠzµÊÆÆ`k+o¯ƒ$ÉÞìgÏ 66›¸îK¯¯¾¢_DƤ{ŸË—ÇÓÅEÎ~üò ïIK“Ïå‘(™'ÖÖª 튡lÙ̧5nÚ”%<‹ŠÂ&aOš˜Ð°OŸ";GÔͨž=y^H.AJj*­zõbÄĉZ²J}<}ú”•+WbllŒ¿¿¿®Íô ƒÐXZZâããƒiiiœ9s†½{÷2wî\|}}i֬ݻw§GÔ¨Q#Ï1Lòø’lݺ5/^¼Ð¨ý†Ì“”Æ`gjÊâ|ÞÿbËóç°w/Š­[é”ËÃ÷0.û¨X­€?¬‘Î+€Ÿ<� ŒŒTÛ…œéíÍŠ[·ø<÷m¾•ÓBCÁÅÜè­P°%½"Ç>;r2’% ž<ÈHYPöo~a'qqòvçNáöššÊb:]P)S†%LJÌ}éÊJ•XóÙgE{?4@GG¼wï¦Ažþ •-KÍ&M´h•ú˜5kqqqôïß?ßïK ¤bp:+FFF´hÑ‚-Z0gÎÙ³g{÷îe÷îÝœ:uªÐ1ÒÒÒ¸|ù23gÎÄÙÙ™:d;ÿäɼ½½INNÆÃÃ??¿\±× ’>½s‡Èô ‹jÔÀ.=¦X“œ –-°o¤'fõD¾Mžg <­Zž=e/ž$É[ûOŸRÓÆ¦ÀkòÝ/ʵ’Äp¥’gÏÒ>½úsà‚¥%ã]]å×’±%&æP iirèD •ªÐX $@6/ô- L|<Žù5qpÈíEvs“ûÑk¸U|ã¦M¹ öºÏ9yN„9Þùï¿Ü•7 9T£|yÈŽ–' ª íÈH92¯:))ò h ,â«,—]…†bY¾¼{^¦ÌK}Ö-¯ã9½fÝùOfÌ`ìo¿±!½gNsÕ«3Vǹ%‰ðêÕ«,\¸333¾þúk [&Ð7D¡ H%”çÏŸzÍðáÃ%äŠkÒo¼!ÅÄÄd;ÿÝwßI>>>Ò¤I“¤ž={J¥J•’ÌÍÍ¥#GŽd»nРA’«««´qãFi×®]Ù¶Çg»VÛÇjÕ”®ªvIðXz©²²kÒdÐkÍûGt´Äôé»wKÿýWg¯W-Çvî”oß.I~(IeÊHH»@zœñÆÙÚJÒÐ¡Òø÷Þ“V”.-I MppTšcРAZ}m 'O–™˜HHƒJ—–vïØ‘÷s%):Z’îÝ“v-_.=>~\’NŸ–¤Ã‡%iï^i×gŸI—,‘¤eË$iî\IúæiWÒãaÃ$iÈIê×O’ºw—vÕ«'=nÚT’4$Irq‘vY[KK•’$…"ó˜õ==Ò¼dž^®œ´ËÃCzܧ$MŸ.I[·JÒ?ÿH»~þY>/<öºkÉ–uë¤*Òßÿ­½×¡TJÒÇҮ… ¥Ç¿ü"I›7KÒ‚’4a‚´ËËKzÜ®$5j$I..Ò33ÉŸ:R" $)}›ÁÛÒÖ¬oy|^T>fb"IvvÒ.éq½z’ôÎ;’Ô³§$ *íòö–ûûKÒ’%’´i“üÿö[éñ‰’&I±±’”–&õíÔI:bd”çß•-+ûóOÍ¿§*4hJ×¥¦¦JMš4‘é³Ï>Ó©Íâ˜vŽe¬#þþþÒÆ¥ºuëf~^ycPhI’¸zõ*J¥’F¡P(¸sç›6mâܹs4iÒ„/¿ü •bç&L˜À€8uê?ÿü3­ZµâÏ?ÿ¤bÅŠ|úé§Ùš´ÄÆÆR£F >þøc²ec“W$ŸîINN’€ 9}?ƒ¬ÇT¸5žÏSS€•±1+óðÐë=’§NÁÖ­°y³Sš•R¥ sg4:v33šíÚÅúÿþÃ' ³òÆÍ›7 jÆ ìÞ½[C/$7Ãýýù`ófZäà@‹ôp¥\”*%oåÊÉ!µkËU2HM…–-³Û½»hÇÊ——½Ý °kÔ­ ææ´IH`å!¸¤Žnez÷ÆqÛ¶¼Ç {ý7¦QUèU?/½ âŸÿ-°*ŒÚ16–ïT©Règ£1°ªJWR ðPa„G³æ˜Ô© ööðâ…\)%6VŽw72’ïxÄÄÈ[a!wJåË$Ò¼âä÷î-øùFFtµ°`µ‘^9îÐ$ç+TàSÕB‹´€ªI„Ë–-ãÂ… 8;;3nÜ8Í%ÐK^å®VIC!Izò—­ú÷ïÏ–-[¨^½:çÏŸ§Q£F<|ø[[[=zDÿþýùé§ŸŠ>ž¡C‡ ñ,€þ«<ùóÏ?©V­sçΠ\¹r¼ýöÛL›6-3‘gèСŒ?ž .ðÖ[oå;Vxx8+VÌ&²·nÝJDDo¼ñF¶ë²v? cÓ¦MÔ­[7Wˆˆ¾%ž< ³fÉû ÀôéêŸãEZÃnÝ"M’072âŠÞ|XK„†Ê^æ-[àêÕìç hÞ\Íï¿/W P…B÷ûïÉmt"̉……Ó—.ÕꜯÂÈY³ðyï=Φÿ—t^'‰°8Q§Ù[>p€÷Ô/übubfö²2HØÕGæÒÊ•4JKcUÙ²ŒX¸PEÉÙÁòîÝì ¹qqpù²¼å¤lÙ¼aÝÜä„HIJJbâ_!ù­,k¬­¹T¯ûöí£bÅŠÌ›7Oåñ†‡H",ƒЗ/_¦a×7Ð2öëÖ­›yÌÒÒ’*UªpëÖ­ôêÕ«Y·no¾ù&VVVœr£ -¡N„Å•6íÚñÛ©SÂûœÎßéHTé°Zœ±··§gß~z}gï“3ðÛ¿ŸïÃÂ8W­ã ÊûÂädλE|ÎRŒOŸÂùóò–“ ò¯2“£ºÈgsç2ùÄ RîÝcC&¥[8³v- Ç@—ÍR—[[„††£B÷ÏJ•*éôGã¾mÛx BžEӷߦaZCŸ GW£"’–––Íc\PˆFaaß#FŒÀÙÙ™+W®ðäÉÞ{ï=ZµjE×®]Qdi)¼~ýzþþûoM_–[YñïÓ§$$$0nÜ8zõêU„W®>>êÖvP?'P©ÄaÈ&ÒÑU“l^²„¡§OSÐ=€ã&&\˜1£Ø h}Ò,úŠÁh€ëׯóÍ7ß LlÙ²l¿T£ThÛëàà€¯oá sÞÞÞx{{¿š±:â§Ÿd‡+Èyo#GªŽ4IbØ­[¼HKÃX¡`mÍš˜*^/xã§+Ê+~0[·¦Kß¾ÙÆÇËÙô[¶ÀÈõf³âê*{™ûõƒúZ¾E, †OfÌ`j\Ü«çXZÊ•gòú‘'wÚÌK\çü^˨§}üxæ¡Ï€ÉÀ9†Mf¦¤ð8>žvõë3·{w9V»Ly³¶ÎlS¯iÆ}û-¡2âÙ³|¯ñuuåãI“´bO~ø/]Êï;óUw–—T«Æú,Uº†‡A è7npãÆlÇ–/_®#kôÐPøøcy¿bEH¿[§v–>xÀÙôð“J•hb­bÆ|œ=vŒî;vPP/¬&&Ü,_ž. ß&=p@Íû÷g68ɤbEèÝ[ö67k&Ç9 Ák`kkËÂ53¸µµVÖ Aîs11Ù[×gÝÒC"2È% ³¦4w''SØvå Æ9 yî A±e4¦Qe³±QiíèíMŸjÕråJž^èÿ€ íÚé<æ¿AÆ,õôäÙ£Gy¶š?jjJ«?ÄL±‘½Á`ô¢E‹X¤Ã[:…¡ë$ÂÔT02~د_/—QU7aII|@5 ¦W©¢–qý-bú¹s¬Êç$À„Ê•Yß´)|ø¡\C8g­æ²eá½÷dÑܶ­\QCÑE¡ xRR’…`k MšÈ[N¢¢2Åu÷£G™¿c?¤;VGÒÀ Ͼ™’$q<{–»”§ªdá…ïßy‡uŒÈéô:;3ë£ä E캪î}¿ÎYòÏ?LÊù=¬«Z•uż~¶H",ƒÐúŽ®“gÎ|*çç'—/ÖÃyžš À÷îîXªI¤:;;Sªys‚¶mËÓ }ÜȈ&XvïžýDéÒàí-‡gtè ™lI5#’ªRR’¯¼5kÆÚcǰ{ï=nmÞL(0*ý’õ?ü@ÝV­d§Ã«l…5¬)‚ïô†@6/ô@…°{óÍW|#ÔK`) Ùb¡ѲC‡bï}I„…c0TUöòZZZjõíë‚äçÎÉͽ”J¹yÜÅ‹rC9u³)"‚Aé?tp`š½¨<`z³fyz¡ûëK“t:v,°Á‰@ (&\QŠ, Mz›C»â-NtÉÇZ¯Ç?& ˜6m_ýõë š”ô²6¶DøA ‘e _` O÷Z.û¬ïÞ@`-`Ö¨øúÊßCzÚ‰¸ t­[Šãž8q"‹/VéÚõë×g~8 øxøßÿdñln.'jBL÷îÝQ¦¦âáêÊÖ_~ѵI/É!Â?üî;Ö¥WäXèêʬ;äP=c̪U,îܙɱÞÍ5‡Éžª5käzßÏŸËIGë×Ë·€}}åF åòŒ8#´S›F |òÉ'L˜0›7oâïïÏ©S§psscðàÁ¹¶ðކ.’wìÿfÚµMUÔÙͶÈH|ìì襉ìD€pîߟRJ%A野›˜Ðdøp, H<¤‡Á…¥RiNàèÑ£tëÖÄÄD¾úê+BC³õ5Ð7:z{s¬Z5® •7ò£AƄԪÅnSSšŒYåÊ0q¢\rðða¹DjFÈè0v,89Aÿþr/=¢Õuáƒâ€ÁèªU«2sæLîÞ½ËöíÛ‰§]»v¸»»3sæLî½jö°šÐváƒðÑGò~ùò°q£f*µ=S*@–»¹©›xyÁ;Œæ§—Æ[éêÊÇ_~©™9uÄ삚0Yøûï¿3 ‚üøí·ßèÖ­III|ùå—̘1C×&©Ä‡S§ò~™2|ªçk☠ð/WŽ!Y+o(ðÎ;rÓ®²w-KN–K¬zyÉ »fÏÎÝ(GÇèºðAqÀ`tFFFtèÐ-[¶ððáCÆÇ®]»¨R¥ ýõ—ÎìÒf6«$ÁÈM÷V¯–ðj‚ ÁÁ€ï¿/-·E¡@UD¡ 'çΣqãÆ\¸pÚµksþüyÚ¶m ˆµEoiÒD޵|ø~øš6•§¥É!u*¿¿ò¡DaáL»-[¶°{÷n¼¼¼hÑ¢/^¼`íÚµy^ëããCýúõµjŸ6ò?û 2ÖÇo¾É»««:¸Ïüô¤Ì†ÖÖŒ«TI½ÄÇË­ÏŸ—÷ëëÖÉ58K¢¡@UD'BAVV®\ɧŸ~Jrr2ݺuã§Ÿ~Â:=áÄÚ¢÷XYÉwZ‡ “ï¼þðlÞ,ß}ôæÌ¹s¡Mùšž=å@t",ƒÐX[[sáÂ.\¸PàµîîîZКþ îÛ+WÊû-[Ê9 š U’zëJIÂD¡`­‡Æêô '$@—.pú´ü¸gOØ´ ÔÔ¼8 ¾àª"„³ä.­C† áСC( &NœÈŒ30ÊátkK1â7`éRY0ïØ!‹é“'å*ÇŽÉ›ŸŸ\SzØ0¨SG­Ó ñ\8# g͚ŬY³tm†NxôHN(SFþÁª)gí‚{÷ø'.€ñ..ÔψÙRIIàí ÕR¼½åR?jÊ"ÅÍ›7ãççGLL •+WfÆ ™!ÀÂBÉ­[²Þ´I®Nõ䉜è´x1¼õ–GݧOfìf¯V­°Š‰)pø¸/h;p £3’§*#”‰0x°ü·rަzŠ%&òMh(î––L®RE}ƒ''Ë-¸‘wê¿ü¦¦ê›C  „Ç3bÄ~ýõWÌâÅ‹±±±Ñ±eáá!—Ùš9vï–Åô‘#²WúìYy;VN†6ŒÓfÉZPï {{Z{{kñETzåÊ•"]CXX˜†¬É¦ò—.…ƒåýþýáÿÓÈ4H€ï­[$¦¥¡~pw§”ºÜÜ))ðþû/_H»vðë¯/»7•0D¢@UDaÉdçÎÔ©S‡_ý• *°gÏÖ¯__¨xk‹`f½{ßÂ;ðå—QF6.NN¸oÒ¿#GXbk›ï0‘À#OOê¾ñF®s"‰°p F@·jÕŠ¶mÛ²wï^Ò øµȨQ£¨T©/^Ôš}šH"¼q¾øBÞwu…+Ô>E&k>äxú­ áNN¼]Àe‘P*¡o_9ˆä䈽{¡T)õŒ_ ª":–,®_¿NûöíéÕ«‘‘‘¼÷Þ{ܸqo=ˆbm1@ªV…o¿•kFïÞ-ç¥ç Ù\»ÆOžp:Ÿ§.°·çó%Kò<':ŽÁ„p„††²páBˆ©©)îîîÔ¨Qƒ*UªMPPwîÜ!44Nœ8A£F´fŸºò““esR’ïüãrü³&ONfüNK;C IDAT;8››3§Z5õ œš ÈÞf€-`ÿþ—šJ("ÑG *"‰°dÍäÉ“ùþûïIMMÅÉɉùóçÓ¯_¿"#ÖÆÄºw—·û÷åÊUëÖáÆG@ó—?Õ®§÷D¡*Œº|ùò̘1ƒ°°0¾þúkjÖ¬ÉíÛ·Y±b'Nœ lÙ² <˜k×®±cÇ­ŠgMàïÿ²¿ˆ¿?´j¥¹¹FݾMlz÷•îîØ¨#©/-MÞÞ¶M~üæ›r½KM®‚b†R©dñ⟹¹±bÅ ÌÌ̘4iEÏ‚D¥J0y2csàukÔÈå…^`oÏgšê´VB0t¶¶¶|òÉ'º6C£:ô²Ã`“&rÃ"MñËãÇìN±ìS¡ÝÊ—ýA%IÎÞ¼Y~ܨ‘ÿœ¥^©@ ”d8À¸qã2ã–ûôéÃܹs…gP :FFб#~—.1¼vmšß¿ÈÞ爼ÏÕ0´¾£®€ü¨(Ùq+I²³ö§Ÿ4Wåí©R‰ßíÛ”35eIêxäHùö@½zò/uÅT"ÑG *"‰Ððøí·ßhÞ¼9;w& €FqòäI¶nÝúÚâY¬-%êôêÅ™ôÄÿïTð>‹$ÂÂZK¨+ ß×Wîö °h¸¹©eØl\LoŸ=.(ˆG/^°°zu*¨£*Ƙ1r†0@íÚrq¹r¯?®!}ª"’ ƒ´´4~ùå4h@×®]9sæ ®®®¬_¿ž .вeKµÌ#Ö–’‹ßÔ©,­\™(à¡ Þg‘DX8¡¯¨ã¶ÛêÕr’-È%“‡ {í!sqüäIºöéêßgCzÕåÊñƒÃëþùçrÝ=ëY9öö¯?®!}ª"’‹7J¥’Ÿþ™Y³fez‡ÝÜܘ8q" ÀTÍuðÅÚRr±±±¡¶¬[ÇbŸE¨Pá]L „O?•÷œäúéšà‹ x>kçO??Jó½»ûëüå—°`¼_£= +¾þ¸@PÌHLLdÓ¦MÌ™3‡êԩ×_~IïÞ½1N/C&¨¿©S1·²±ÏjBèb@JŠ\².! ذÔ‘Ë—“ã'Or§R%pu%A’àáC¾mÕ ××­ÉŸ>]×f =yòdî¤×-ÎÉìÙ³u3ø:ù“'Ã¥Kòþرо½šŒÊÁ„ï¾ãI×®òƒ¾})ûë¯ø9;¿Þ ³fÁ7ßÈû..pì˜ü¯ _D¢@UD¡þóâÅ ¶nÝJ›6mðôôdÉ’%ÄÆÆÒ±cG8ÀÅ‹éÑ£‡VijX[ª"’ Ç ôÞ½{yüøqžç~ÿý÷Ì[fÚäUòOœ€¹såý7ÞxéÈU7ÇOž$ÈÙùeÀ 051!,4ôÕ]°@ÝÙã|ô¨ìˆHô¨ŠH"Ô_BBB˜8q"...ôë×'N`ooÏ_|APP cÇŽZµI¬-UI„…S¢B8¸~ý:õë××úܯÊ=GJ•‚Ÿss Gº÷yøðlÇ"ß{±ß~Ëž5kŠ>àÒ¥rÒ È±ÎGʱςB‰>UI„úEpp0»víb×®]œ9s†´´4Z¶lÉÈ‘#éÙ³'æšZÄU@¬-UI„…c0:$$„N:r[ï¾}ûR*KìnZZ!!!ØÛÛS§N]™Y$FŒ€{÷äý¹såªošàøÉ“VªôÒûœ½= ¡jÕªªøý÷r¹ºô18rD®º!ÆÕ«W3Eó¿ía‘ãMȈ#ŠÍwŽ@ PƒÐÖÖÖôíÛ€U«VÑ®];*Uª”yÞÄÄ„7ß|“V­Z‹DM›^v¹îØüü47—ÿÂ…Ääð>gîãS4/ôúõr£ë;ÿù§æ”¿@ h™´´4NŸ>)š³†V¨PooozôèÁ;ï¼£So³@ Ð,# íììø&=YÍÁÁ®]»fк¦(ù!!0z´¼oo/kRMqüäIn:9åR/ôæÍrqjI’; :$w‰€€jÖ¬©k3Å€ŒB;;;[bؼxñ‚£G²k×.öìÙ“->´ZµjøøøÐ£Gš7oŽ‘‘þ¦‰µE *ÉÉÉâ`!Œ€Îʈ#tmB.T ÈOM…ÿýââäÇkׂ:z˜ä‡ÿìÙ<·³ƒ-[PåLL0Êá¡OT*™4>?-_žÿ@Û·Ë=ÆÓÒÀÆ„F4g¸3{öl«(P‰ŒB ­~âãã9pà»víâ·ß~ãÙ³g™çêÕ«G=èÑ£o£šºbm¨Ê£GDt!¤€6lAAAùž÷÷÷×zö³ªÄ3àÌyÄèÖMƒFï¯ZŹô’ó«WgÜ«”˜ÛµKVý©©Pº4ìßo¾©fKKâ N *B8ÎÉ“'ùüóÏ‰ŽŽæÚµkXXXä{í£Gøí·ßصk‡&)) ###ZµjE=ðññ)ZNˆ!Öªñ\8) œœP*•™%Iâüùóжm[¬¬¬th]þœ95ÎkÖ|Ù¸OS^Vù›6É1Ð@ %Ž\>Æ—_Mæüùóå+žRRRÇÇLJ3fàççW,i ýÇ ôï¿ÿ΋/²³³³Ó»„yóàøqyÐ èÝ[ósŽ "1- °ÂÍ-WÍç|¹|:t€gÏ@¡€uë Ú*9y£ZÖ¬YCDD!!!„„„ðìÙ3ÂÂÂxðà‘‘‘<}ú¥R‰¹¹9ÆÆÆ¼xñ‚O>ù„­[·²}ûv½j²%Š') ŠâU̓ÐÐPöîÝË7HHH uëÖtîÜ9ÏqïܹÃÏ?ÿLpp07fðàÁ”.]:×u9“/]‚¯¿–÷«Uƒ¥K_Ëd•ØÍÞô¤£aŽŽ¼ic£ÚÿýÚ·—]å ¬^-+~F‰>U)‰I„öe챯ëLݺu ¼.))‰ˆˆ"""xøð!„‡‡³dÉüüüpÑôí>=D¬-UI„…c &&†•+WòÏ?ÿðäÉ“Ìv«]ºt)ô¹[·neõêÕ4nÜKKKf̘¯¯/³fÍÂßß?óºË—/Ó¾}{ÜÝÝiÔ¨óæÍcóæÍüñÇØä§Y“ä¾#))rñæÍ`m­¾×ž‰iiŒIO,ojʬôÄÁ}[¶pçæÍüŸøø1lÚDƒ„Z,[&·ëh ‘è#P‘D˜?¥J•¢J•*T©REצè bm¨ŠH",…$I’®P7´lÙ’èèhjÕªEµjÕ8uê111ôêÕ‹íÛ·—×/¯Ž;ò×_ñìÙ3LLäßï¼ó/^¼àøñãsÿþ}<==ùú믟¥”ÆàÁƒÙ³ço*Uj @x8 ‚"3xð`@$ž„†ë=è†#FP±bEîß¿ÏÿýÇþýû‰ŠŠbÛ¶mìØ±ƒmÛ¶øü¼n[´nÝI’2c«CBB8zô(:uÂØØ€J•*Q¿~}Ö¬Y“ëù11-¹~}ׯoàÉ“ €¼YX÷™?>~~~šÝ/îÝ»ÇÌ™3155¥W¯^:]g÷îÝìÙ³‡O>ù„õë×óÁ¼FTñ¯ñÜWó,-÷ïPÊÒ’™Ù´©õðð@Y©aÿ9~ÝÄ„ªd*ˆùköìÙ†AšBB!´‘{‹ÐUll¬¡C(ðŒ²ˆpðàÁüïÿ#::kkk\]]5ÿšòòòÂÁÁA3vذa ÓaW‰Ÿ~ú‰qãÆBùòåÙ¿?;vÄ××—î/uâ9r$+V¬ 99YS¨èããó¼U÷êL×mØÐ‡“'3Ï oÝbîóµÏk«UcP©RÙŽ æÇ5XøR+Üw+Vä׳g%BŽ ¡wRD¨QncW«V-œœœt«ë†ú 6D¥RqúôiÊ—/§§'—/_Î@_¹r…Š+¼ÛÕ•gÏX¦žSnéè˜cò à±s'i ÜÊÇMM©Þ½»$ÏB!„ÿa” ôË[È½Š»wïfØd_©T²páBLLL¨Y³& ^öжm[öìÙÃçŸ®ÙÆîܹs|‘Þ!Å€F߸AŠJ…¹‰ ‹µ­ù>r>ûŒÏ€yff,LKãgww~>]¡ !„B*F™@¿®nݺannNõêÕ‰‹‹# €Ç3cÆŒ ˆóæÍ£C‡´hÑ‚úõë³sçNjÔ¨Áˆ#²¸jRÇòdžˆ>~ Àزe©‘EgDˆuñ”< Òš4áÇeöÙ€¤[˜ÐUQìD(^Ü[„®¤¡vFYD¨T* dÚ´i´jÕŠ&MšdøÚ½{wŽÏ_µj½{÷ÆÌÌ ÆŽËÅ‹™:uj†qõêÕãĉtêÔ‰¸¸8&L˜À²I<#²8–÷ž¤¦òÙóõÞn Ósê•–¨;»,\Èg«V1ÞÁeöÙ`¤ÐGèJŠEnȽEèêåîÉ"kF9=yòdæÍ›GíÚµiÒ¤I¦Ei›­©[·.uëÖÕéµ*Uª¤Ó’ gç;T¨à“éx­Zº­ÁÖÕ!!>ðÁx!jº- ý“¢ ¡+iá-rCî-BWÒÆ[;£Ì’Ö­[Ç„  Ô¿¶ß~»y¾ß¼ÎÅÅñ˽{t(^œ~%Kf?xÛ6˜;Wý¸n]X¼XsJ’g!„Bˆìå€::½R^¿NšJ…¥©)‹r*¼~Þ{OýØÑ¶nkk½Ä)„BQØeÝ©S'6mÚdè02Hoè’_V޿ϱ'Oø¬\9ªÔ4KÁÒþü¤ñ‚B!Ä+1šúe×®]#!!!Ës …‚²eËbgg§×˜òcAþoááœ~ú€)*àae•yPZôïÏ··ã§Ÿ Q£€K—Ô'M‚nÝòäµ…~H·0¡+éD(rCî-BWÒ‰P;£L ?þøcÖ®]KçÎY°`›6mâý÷ßgÀ€4iÒ„† Ï¡C‡¨W¯ž^bÊ‹ùáIIL  Z±bŒËªppÑ"õÏíÚÁŒ¯ýºB¿¤ÐGèJŠEnȽEèJе3Ê5Ð}ôÌ™3‡={ö`ccÃ;ï¼ÃÌ™3177'55•£GR9»½“óX^,ÈëOÓÒø¥J,LL28v ÆW?.[VÝuÐÌìµ_Wè—ú]I¡È ¹·]I¡vF™@Œ1‚#FO|||¦}R­¬¬ Õ~˜~11l~ø€wJ•¢ÍÇ<|}ûfl–R¢„"B!„0nF›@§+V¬X¡oݬT2úÆ ìÍÍ™ÿß‚À´40àE³”„Æõ¥B!DÑ`” ô¸qãrÜ]c̘1´mÛV½Þ‚üùaa\‹àwwJ[Zf0u*¨ÿßÿÁ‡¾òk ÓnaBWÒ‰Pä†Ü[„®¤¡vFYD¨P(°²²Ò|) Ξ=ËöíÛ‰‰‰ÁÌë‚_uA~hb"3CC¨ckËGnnøúœ9êǵjÁ’%¯¦(¤ÐGèJŠEnȽEèJе3Êè¬n*• ___&MšDõêÕõÓ«.È{ó&ñii˜‹+WÆìåÂÁ7`Èõcغ ùr!…>ÙÙ´eÛ÷o'ì~^ž^¼ÓãÚ´jcè° JŠEnȽEèJе3Ê謘˜˜Ð³gOŠ/ÎÖ­[ ŽNþÅöçÑú”.Í/NÆÇC¯^/š¥¬^ zÚQD}ºwïM;5åýÍï³Éq YÎrúÌíC¯!½xö|_t!„B_ŠL΂۷o: ­•JÆÜ¼ @qssæü·pðåf)'‚ÌD #”––F7Ÿn¯yœøZñ`óü„#D7Œf{±í úpAcBQôåŽ3 çï¿ÿ&001cÆè=¦Ü~wç·ø¶bEJXX¼8ùË/°aƒúq›60kV^…) )ôyaÖ³¸Rö Øf}^YZÉ?§þ!àŸ€"¹œCŠEnȽEèJе3Êè=zP­Zµ _íÛ·gß¾}̘1ƒ¾}ûê=¦Ü,È¿™Àœ;whhgÇeʼ8yü8Œ§~ìæ›6I³##…>/ì=¸—Ä ‰9މ®Í﾿ë)¢‚EŠEnȽEèJе3ÊèÍ›7“ð|ö6]™2e(Uª”"ÊÝ‚üoÜ I©ÄÔÄ„ÅUª`š^8ù¢YŠ…lÙ%KæSÄÂPŠb¡Oª2•Û1· zDУ ®E]#èQ'ÃO‚‰–';@пAz‰³ ‘"B‘EñÞ"^jg” tÕªU Â+ÛÉÞèh>(S†vvêéÍRÂÂÔßÿð4mj (…±yòä ¡¡¡T®\++«|{˜Äu‚üèZ†dùVô-R”)™Ÿ Ôá¢Ï l™²y«B‘£L X¹r%çÎ#::ºuëFË–- Z¶ž¥¥ñÉóÂÁ|[±â‹“Ó¦¿¿úñ;ïÀG BalvïÛͤ9“x¬xL\±8â(iR’óWP³FÍWºfš*Ç!/ä—’åÈøH®áh列‹1•c¸þð:ª’ªì_ƒïR”)X˜Zd?N!„È#F™@‡‡‡Ó´iSîܹƒ³³3îîîlß¾ùóç3zôh-Z¤÷˜t)"ü&4”»ÏÇÍ©T‰âæÏÿólßþ¢YJ°ti~…) }ú|5ç+~9ø QÍ£àù2úbI ¡ÃÈü2ázuë•íóŸ$=ɰÜ"=Y¾}ƒä´d­¯ofb†»£;^.^Tu©ªþÓYýgIõÒ¤¸þqÔï\Ÿë­¯kbÌ ¸ [½·Ò`iV÷XM½Òõ^áo£p’"B‘RD(t%E„ÚeýÉ'Ÿ`jjÊ¿ÿþK½zõ055%66–åË—óÙgŸÑ«W/½·òÖ¶ ÿj|_Ó¡«P+*†TÄrˆ%çžãBÄ-kÄ”S˜ÖrZ‘˜N/ ”µÐBú¸·ã!ë µ0Q©T9|6Z8¹ºº2}út>øàƒ ÇU*ÞÞÞôìÙ“/¿üRoñøøø9p´=wŽ€Ç131áLýúÔ¶µU7KiÒ.^TÚºUÝ} 9YÝ,å?À€ `„þåg·0*~>ù3'OÂáœÇV¸^Q>£ò-–üTÍ¥GÞ?œösP˜)HU¦2óÐL,mÀ™ûg ^ž‘N„"7¤¡Ð•t"ÔÎ(—pôîÝ›‰'2cÆ C‡¢ñßjVðᤩTX˜˜ðK•*êf)¾h–2w.4k¦ÿ`…AåWÑFhl(ïù¾G@H¸WÀâ²)5þS çSÎŒí3–²e oƒ’ôÙè®U»òžï{œ¸wB3ýy³Ïù²Õ—…~m´ì¾!rC „®díŒrúøñã<~^”WP­ºŸ£±±Œ/WŽjÅŠÁ_€ŸŸzÀ€0v¬#Ædù™åÔZ\K<îŽîø¯ógVóY¸ïw§øÅâ˜Ü2¡ÄÙTñ¯ÂÊV2îÃqŽ:o¼<menEª2•Y‡gQi}Nß?mèð„BBF¹Ý AƒHNNfóæÍ†ȼLtJ UOžäQJ å ®6j„Íÿþ=z¨÷{®^Nž”ýžÅk ΰÃØssæØpïáÌs>v–ê6ñIII\¸p[Á·¨Y½&ÕªUÃÌH×Ü= ÂÇׇ÷N`njn4³ÑFK¶±Bïd;íŒr ǰaÃ2d={öäÍ7ßÄËË Íy///Jéy]ñË ò§óèùVz <=± †ÁƒÕɳ4K)âòª[؆‹øx÷ÇÄ$ÆàfçÆònËyËó­ ã  6¤aƯýš—‹GÞ?ÂÇ~àË€/ILMdÖáYl¿¶UÝWÑÀµ¡CÌéD(rC: ]I'BíŒr ÇØ±c Å××—Q£FѦMZ·n­ùÚ³gö‹ä±ÐÐP\Ê–åŸXv_ÝÁí-''zÙØ@ïÞð|9+WBÕªzO¯[èIï?zóÛþO“<ª=ˆK^Ê”<Ef&fLxcgGœ¥IÙ&\zx‰¦+š2Õj¡Ú©CŠEnH¡Ð•jg”K8ÂÃÃINÎþ— ‹‹ ¶éÍJôÀÇLJƒÇŽÖ§%bcyЧ SS.5lˆçˆ°nzয়¼yz‹KŸmW·1r×H"ã#(iS’%o/¡‡—›e%M•–a6 F‰¬î±ºÐÍF-YÂ!„ÞÉíŒr Gzó”‚$29™´xðÍ7ÀçÕªá¹zõ‹ä¹eKÙñŠbcøx÷Çl¸¸As¬Oõ>üÚåW\ŠÉÇûÙIŸîZ¥+ïmãaǹy™&Ë›0±ÙD¾jý 3ùS!DFF™@§KJJâÌ™3™6÷òò¢téÒÙ>/%%???üüü ÃÕÕ•îݻӲeË ãþùçŽ;–éùýû÷ÇÃÃ#ñøô-azöÄq×.&;9Á'Ÿ¨•)›7ƒ¹QÿçùdÏÍ= Û1Œð§á8Y;±¨ó"ÖhàÈ //Ž =Âücó5³Ñß~ÇŽk;XÕc ]}¸BÝåèË—/óöÛoãääÄo¼A›6m2|íÛ·/ÇçõÕWtëÖS§NaeeÅÖ­[iÕª_}õU†q{öìaÆŒøúúføÊríPzr\«ÊóçYòæ›ü–œÌ33u³”zQ´èÚ-ìiòS†ïNç 5ÉóÛUÞæÒ‡—$y~¦&¦™ÖF_޼LÓåM™ì7™¤´‚×™K:ŠÜN„BWÒ‰P;£L GŽÉíÛ·Yºt)!!!Ü¿?ÃW¿~ýr|~·nÝ áàÁƒ¬ZµŠàà`ú÷ïÏŒ3xðàA†±•+Wæøñ㾚4i’ù¢11š‡Ïúôá_¥’4à`³fмy^¼ma$t)ô   ÖâZ,?³{…=+º­`çÀ”±-“ß!µôÙè¹æbenEš*Ù³©ókŽ…eþÄɤˆPä† ]I¡vF¹fàܹs¬X±Bk¢œÿ&À&&&øøø°yófnݺ•ãòl•,©y˜V·.GÝÜH»s‡ßvî|¥…ñÊ©h#>%žÉ~“ùùÄϨP×ÿ¶óhÇÊî+)ï £òŠ©‰)Ÿ½ñ™¦‹á±°c\‹ºFó•Íßt<3ÚÌÀÊÜÊÐaJ'B‘+R&t%µ3ÊèªU«òäÉ“<½æÉ“'177§Zµjއ……ñöÛoóÖ[o1~üxBCCuº^舄5lˆ½½}žÆ)Œ×±°cÔý­. O,D…ŠbÅXÔyûï—ä9ŸTu®JàÐ@Íl´R¥dÞÑyÔý­.Gï5txB! Ä(èéÓ§³dÉ’<[ïuåÊfÏžÍøñãqrrÒ/V¬mÛ¶ÅÛÛ…BÁ/¿üBõêÕ9uêTæ‹Ü¸ÿ HZ\מ<É”pûúúfZÓ(ÇŠö±¤´$&˜Dó‰Í¹v€fåšq~äyÜî¹õ(ªÀÅlLÇLMLñ|èI@ßš–m  žþ¼9nù„Ô„\]OŽåîX@@‰‰‰"9&ÇŒõXz ×äÉ“Y»v-wîÜAäÌh–p´nÝ:Ã÷¡¡¡T«V-Ë®ƒ“&Mâ­·tk(‘>ÃܨQ#¦OŸžáÜ—_~™áû‡R©R%>þøcŽ?žñB‰‰Ïži=ªPùK–°ðÛouŠE /ÿÃïÌý3 ñÂ¥‡—@–f–Ìê0‹ñMÇcjbÊ%.0Ò¢ÅÓÉ“À¡üxüG¦ùO#Q•ȯÿþŠ_„«º¯âro T*Yµaÿœü‡È˜H*¸V b©Š4ϧ:) ¹!…®RžwKÙ3šF*é›~ëbذa:ýB{ðà-[¶¤D‰ìÛ·O§æ+£FbåÊ•ÄÇÇcff¦‰ÍÃú>þ{{Ú Œ¥¥%-6¤‡ŽÉ¼(|||X¾r9³Ïbæ¡™¤*ShàÚ€5=ÖP½DuG(®E]Ó¬õ,õòC8³î ×Ê\#Ñ=,€D°¿jOSES¶,ß‚]žÆáëë ùZhi¤’g|||d´ÐIÛ¶m)_¾¼ü¼äÀhè„„”J%666yr½ÈÈHZ·n Ðy­ò AƒøóÏ?yö즦ê2>>><[³†-À SSŒÃÔÌ“8…ñ¹y™Á æÌý3X˜ZðE«/˜Ü|2æ¦Fó¡Q¡§T)_ÌF'%Ÿ@w «ÚÂGðvôÛìÜ Eù& ´z'µ3š5Г'OæÝwßÍ“kEEEѾ}{,,,Ø·o_¶Ésxxx†ïƒ‚‚زe 5Ò$Ïé.+ÀÂòåùøë¯ó$Na\”*%ßùžúKêk’çZ%kqrøI¾hù…$ÏŒ©‰)Ÿ6ý”ó#ÏS6¨,4&ëäÀ?=Œ_€Ÿ>CB‘Oä7r¾þúk.\¸@ÕªUéÔ©S†s3gΤ}ûö€z›—æÍ›ãééÉÅ‹9yò$¥K—fÙ²e™®©tvæÄDª÷è!;oˆLnDßÀÇ×G³³ƒ™‰›MdzëéXšÉl[AVŹ nqn„ÕËq\låX6îØH»6íô™Bˆü" tºté‚««k–çÜÜÜ47nÜȉ'¸wïuëÖåwÞaèСY®s¬\§ãŸ>åŠÌ>‹—¨P±èä"&˜D|J<©l¹‘Æn ÐÕÓä§ÚÙõÓ×òôuÓ‹]\\òôºÂ8I¡ÐURR …ÂÐahF•@ÇÆÆrîÜ9­ãÊ—/Ÿa;ºÿzóÍ7yóÍ7µ^§oß¾ôíÛW§Øž={FHd$ææFõW.^Chl(ïù¾G@H&˜0¶ÉXýþH’çBÆÖR{1q”ÄŠ³+è_£¿nÏÑ"½ ¡QŠ<3{ölYÓ*t!ÍT´0ªlîàÁƒÔ«WOë¸U«Våj׎¼P¾|yIž‹•JÅê «Ùþ÷v"¢"ð(ëÁÀéÚ©+ËÏ,gü¾ñš™KGV÷XMË -Aû¿ÝDÓ´nSþ½ÿ/Ê2Êì]…G¥1lÇ0ÆíÇÀZî=œ® ^ùu%q¹!ɳЕ$ÏÚUFרQ#&Ož¬uœ···¢EUhh(}?èË%çK$”O€Jp<î8{–ï¡ÎÒ:Xt¶à@øÍø‘ F2·ÃÜ<™‘†ñÝß±·Ã^®¹\So_÷_±àç²½’ÐØPž&?eéé¥,=½”º¥ë2Ü{8ïÖ~…ƒÞcB‘{F•@—)SFfd„A¥¦¦ÒupW.6¾/ï¨h k?柇ÿÀ¯@w(k_–ÝVбRGC…+òˆµµ5¿/úAcq£ê R\SÀH›[6ÔŠ©ÅŽ-;pvqfÿíý,;½Œ×v¢Lá܃sŒÞ=š û'зz_†×N³rÍ ý–„BäÀh¶±+è’’’ ‚Ѓ٠fsÝãzÆäùe%RОö\úðR–És^µ úå]×›3Ÿa’û$ZœmAÝ£uiw¥sÛÍåè(QSSÞ¬ô&öû“»ãï2»ýl<<ˆO‰gÍù54_Ùœ‹k°àø¢¢r|ÍGI7B¡3¹·]I΢$Ðzaè„ø÷#ÉC˧:”.ŸíÇõ³gÏ·Ȅ>( ¾™ò ‡¶âì¾³Øz€QïÂÄÄ$ÓØR6¥ø¼Ùç\ÿø:þCüXs 3uÕû•È+ŒÛ7·ùn¼³õBP‘¹çU`` ¦PmäÞ"t%9‹vF³„c„ $'':ŒlÉ‚ü¢!:>Zû {¸}áv¶§¥Ð§h1Á„6îmhãÞ†¨„(Ö_Dz3˸y…¤´$6^ÚÈÆKñtòd˜÷0|êúPʦ E„"wäÞ"t%9‹vF3íææ†‡‡‡¡ÃEœƒ•E`qàVÒMû8Qä8[;óI“O¸üáe‡2¤ÎŠY¨»˜ÞŒ¾É¤“(÷C9zÿÑ›½7÷¢Tå°ë‡Bˆ|c4 ´A“ÚM0 3ËqŒmˆ-Ý;t×SD¢°jV®«{¬&üÓp~éü uK× E™Â¶«Ûè´¡ªÈ7ÿ|CØ“œ» !„È[’@ë‰,È/jv­IÚá4HÉfÀ¨õ¬}{e߀G }ÄË|ØðCÎŽ8Ë©á§î=;Ku·ÓÐû¡|µû+ܸÓucWv\ÛAš*ÍÀ‹‚Jî-BW’³h' ´žÈ‚|ã÷ûÅßú¿¡ÐLÿ2ÅôîKÿ{©Àê†õ/ÖgÛÊm9^G }Dv¸6`i×¥„β®Ë¨Wî@š*]×wÑ}SwÊÿXžiþÓ~¬õz ܸqƒÔÔT=D/ Mî-BW’³hg¢R©2—v‹<•ÞõP 8Œ×ú ëññõ!M•†­¥-õþ ¿Í~>u˜'‰Op¶u¦kû®Œ5.Ë„xU".°ìÌ2Ö_XÏãÄÇšã&˜Ð¾b{†×N¯X˜¾èðx4ÑÓFóØä1‰Å±yfCY»²lX´råÊâmdï\*,Nxñ}?´·4\F- Eî-BW’³h' ´žÈ‚|ã²êÜ*†nŠR¥Ä^aϾAûx£Üyrm)ôºÒÖ‰ÐÒÌ’þ5ús`ðnŒ¹Ai«Ò`ó5ÓÜÒð;æ—Ç‘Š‚@î-BW’³h'k õDºú•gW2|çðÉóÿí£IÙ&yv})ÚºÊM'ÂJÅ+Qʦxó@3¸úà*÷žÞÃÍNþ¹·]I΢Ì@ ‘ +ή`ØŽa(UJü=èïBWÙf§oϾ4³i†"X‘ù¤ N90Óg&g>?ÃçÍ>ÇÝÑP7jñ öãƒPz^i:mèÄšókˆMŠÍ£w"ôAî-BW’³h' ´žÈ‚üÂë·c䮑/’çAûièÚ0ß^O }„®´fÅw/ã=ÇSî`9¬/[C(Ø_°§R@%~úŸýœz¥ë1»ýl‚Çs|Øq>iò‰f=tª2•½7÷âãëC©¹¥è¾©;/m$.Y¶¾+èäÞ"t%9‹vÒ‰P¤£Oáõë¿¿2ú£Q¡¢¸UqöÞOý2õ –¯-::šSÿžâêÍ«4¨ÝoooŠ+–íx*‡fóåÍüyåO>{˜á¼µ¹5]ªta@Ít®Üks-Û}èJ: ¡w’·h' ´Èbá´øÔb>Úý‘&y>0øÞe¼ –—¦J# 8€M—6±íê6bc2œ·µ´¥[Õn ¨9€7+½‰¥Ùk$¼’@ ¡w’·h'K8„È¢“‹½[=óìdí„ß?Iž…xÎÌÄŒöÛ³¼Ûr"&D°ë] ª={…=qÉqü~ñwºmìF©y¥xoû{ì½¹—Teª#Bˆ¼! ´žÈ‚üÂãç“?óñžÔÉó`?ê•®§·×—B¡«Üæ S ºTîÂÚžk‰ø,‚mý·Ñ¯F?ŠY¨—ƒ@Ru •…ß“¤'øù²éÒ&Ü>@Š2%Ãù²öeéW£ýkô×lyîÂ9Þó.ÖÅ-qg0˜JØQüožØ&°uÅV\\\ ðn„0~’·h'3ÐB?ÿ1Còì7ØO’g!òˆ½ÂžÁu³ûÝÝ<øìK».¥G;ÌLÌ{ÆÇ~ ñòÆTü©"Ÿýï3ºïΕæWH+¦NžL!±l‡Ü1pÄ@ý!!D‘' ´(òæ›Ïø}ã(Q¬þCü©]ª¶£Â89Y;1Ü{8àÞ§÷XÔyÍË7Çäy–ü8˜ùóçs§î°Èî"pJyŠ¿÷ÿ­¿À…â%’@ë‰,È/˜æÇg@I›’øñ§VÉZI }„® Báë(eSŠÑ Gsø½ÃÜw‡ùç«—pÄ®9?7Ö#–­{·ê%Nc!÷¡+ÉY´“ZOdA~Áóý‘ï™°ð"y®Y²¦£’B¡»‚VDø:ÊÚ—e|Óñœv‚ÊΕµ?ÁüÏûsúþiÙÑCGroº’œE;sCPT”/_ÞÐ!ˆ—Ì92‡I&êY0ÿ!þT/QÝÀQ©IцÐU= B¾(eSŠÜÈyP ÜL»Iƒ¥ p²v¢{ÚWlOûŠíñtòÔO …ŒÜ[„®$gÑNhQä|øSü¦ê_Ô>Ts©f਄éšÖnʉ»'H)—’íÓ«¦(+ªgž£¢Ùzu+[¯ª—tTp¨ I¦Ûz´¥¤MI½Ä-„(:d ‡(R¾=ü­&y.m[šƒ>%y¢€ùfò7T ª‰YŸ7yhBgç΄üÂònËXs`†$946”gW0pë@JÏ+MÝßêòÙߟ±ç枥<ÓÓ»B3™ÖYox3Íä‹€/(c[†Ÿª:W5pT™áååeè0D!^@hlû![YY±}Õvúê…• (QO÷¤‚M° o¹¾Å†¥°··çýzïó~½÷Q¡âbÄEü‚ý8pû‡B—‡ ç#Îs>â<óÍÇÒÌ’&e›hf¨º6ÄÜ´hü*”{‹ÐURR …ÂÐahÒHE|||¸sçþþþ†¥Èšqh_| €«+þCü dò êŸY«(táëë ïZè´´46¹â›¬HJIÆÆÚëAŽ4ŸÖFësS”)œ;ÁÛð öãD؉L \@½Guk÷Ö´¯Øžví L-D~{‹ÐUÛ¶m)_¾¼ü¼ä@h=Ž>†õõ?_3ýàt@<  ŠsÃ%„Ð͹TXœðâû~ ho™ëËÄ%ÇñOè?øÝVÏP_zx)Ëâ®v®´óh§™¡vµÓ²Ÿ T*Y·qÿ øcR¹|eÞéñmZiOô…(ˆ$oÑ®h|n%Ьé§óõ?_àfçF€O•tØ"KaTl-méR¹ ]*w âYþÁþêêÛ~„Ɔþ4œuÖ±îÂ:ª¹TÓ$Ó­Ý[c¯°ÏpÝàà`z íŵÒ×HðH€rðOÜ?ü5÷/š-kÆïK~ÇÆÆF¿oV‘ï$FëË€/™qh Þc6`H€lo%„Ô;ð ¬95Õ-ÁoFßÔ,÷ðö':!€«®rõÑU~>ù3f&f4tk¨I¨”j@·¡Ý¸Ôð¼œ#ÛBTÃ(v…ïÂçc¶¬Üb€w(„ÈO’@ë‰ê×_0óÐL@<ô9H¥â• •n¤ÐGèÊX‹ ÁÓÉO'OF6‰R¥ä܃s¸}€·x'„ÔÒTi;Îñ°ãÌ<4‹¤º§fLž_¢tUp*€cÇÑ´ISý¾¡,ȽEèJе“ZO¤«þLõŸÊ·‡¿ œ}9ú¤bñŠŽJw&L wïÞ†CgΜÀÛÛ€ZµjQ¿~}C†dLMLñ.ãwo&6›HRZGïÕ,÷ø7ü_ÒTi¤ÜOÆ9_+Ê#Še[–Ѱ‘áwû˜={6«W¯&""‚;wîP­Z5lmm “(˜"""¤™Š’@ë‰ü êÇ¿)|øåÊ0$ P%χæäÉ“X[[:QHìØ±ƒ„„’’’xüø±¡Ã1: 3mÜÛÐÆ½ ³ÚÎ"6)–ƒ!yoÏ{Ä“ó“a•ï*ÖÌ\C)›R¸Ù»áfç¦ù³¬}Ù Çì,íòí}téÞ…j-«ñ´ØSâmâ±}lKË2¬œ¿’ÕkäÛëŠÂGrí$ÎBtt4¾¾¾üûï¿öÐ¥R‰©é‹^@“LbΑ9€ºY€Oކ ï•%&&²iÓ&Þzë-C‡" ‘½{÷ívvƒÂîU»ãéìÉ)Nå<8p¥JÉý¸ûܻϿü›íp;K»LIõí’6%15É]´ñÓÆ³öòZ¢ÚF‰úX 1ÜMºKÇQY6u;vÎÕ5…(Ê$ÎBóæÍ‰ŒŒ¤iÓ¦X[[3oÞ<æÌ™ÃþýûiÖ¬™f\`` ]ºt¡víÚÔ©S‡o¾ù†-[¶°k×.¬¬¬ øŒWxx8Lø€àÁqwt7l°B£V§Jþ}ø/ª’Ùï[,´#úÀ¹–3÷žÜ#ìI÷žªÿŒ|™i+½§ÉO5…‹Ù175§Œm™,m7{u²íj犵¹úS¬S§O±éô&¢šDe¾˜Â[‡3fúZ4m]þÍ€¿ Y‹+ *I ³0wî\Úµk§I‚Ÿº~ý:£&"üI8‰&‰Ø(m¨ãY‡_¿ÿ{{{í¯Mþá¢$ÐYèÒ¥K†ïíííéÝ»7[·nÕ;{ö,gΜáÇÔ,#¨\¹2U«Veùòå™h)"|=>äÓÙŸr§ÃÍÇ.@O°ðµ`ÿôý…:yõÒ!DÁgggÇÊ9+ñïCH½”¥Ÿÿ¿«„b·Šáuß‹M6eû|K3K*8TÈñž¥BEä³¾µ¡ IDATÈLÉuú÷éŸ&?ÍôÜè„hõv|q€d³ÊGå¦âËß¿d§íN\йàlí¬þ³˜s–ßÛXäÏÞÖKV-a溙„5 ƒ—ò·«QW9Ùù$;–ï šWµ|yíÜ gô¤Ñ„< !A™€­©-­¶â»/¾ÃÒ2÷Í~ )"ÔNh¨T*N:E­Zµ4ÇnÞ¼ á@õêÕÙ»wo¦kÈâë™2k !Þ!™“çt–@}ðÛé‡çн׳™YVSYBˆ‚¨Å-8û÷Y¦ÎšÊ™ÓgHHKÀAá@×v]ùdÔ'ê4^… &”´)II›’x—ñÎvÜÓä§f®_N´÷›ï'‘Äl“gL!61–·è—ÂL¡I¨µ%Ûéßÿ· Íݹs‡o×~KXë°Lç”ÎJn¶¼É€pfÿƒß'·lß§s?ånã»ðÒ©Â.°»ínöý¾¯PÿÞ/̱ëËëýŸ]D,X°€³gÏ2cÆ Í±ôåÿ&Ð5jÔ 66–øøø ÇÏœ9ÃÚµkñõõÍð•¾k:9–õ±ÀcPŒŒ‚€—þšSʧ°zÃêóëâu¤Ÿç×9@bbbˆ%»cÉÉÉü<çgŽì8™ÿaìà± î?8Còœß±ØYÚq-ðuíë2¤Î¦¶˜Êâ.‹j=”º%êBÚKOþϽ€ãàééIc·ÆT*^ G+GL‚L2{þܤ´$î=½Ç…ˆ ì `Ë¿[øíßߘyh&ãöcÐwƒè¼¬3–5¢ÒÂJ8ÌvÀüsJ}SŠš‹kÒzukzÿÑ›7§½É¸mã˜l>Ý?êΆw²Ž/Hƒ«%¯²yëf½üfwlÕªUŒÿvЍ„(®?ºNjñTbLcˆIŒA©ÊzZš*‡ÏòPõðÅÁ[ðwÊßê ’P@Ë6æ)åSñëfDÎ ñR"Ń‹cãhƒ¥™% 3Ñg£)S;G;æ f îž½ËAåAÕãÌ\»qÛv·qvqFa¦ÀÒÌ’³÷Ï’šF‰g%^<÷É]B‡h™¨yîÒ K óËþQ;¸l™S§OÑ¢E‹œß(´LTR±”­={öУG&MšÄ×_áÜ–-[èׯ ]»všã `ïÞ½öbõññ!))‰7ê-vcÓ¾O{üªûe]¨“.F[ŽfÑìEz‹+?( ¶oß.ÛØ‰\IßÆî¿³µ…Þ¹TXœðâû~ h_¸×—Âÿú?¶…m#¡ABÆiàrÂ…ùïÍgð€Á¯ý:J•’˜ÄMržx?Š”áûÿ>NU¦Â6 —¶vÝ_;ÔWçKÎËažBó‡Í™8m"®v®”±-C)ÛR˜™èé‰R©dåú•ìòÛÅè‡T­X•!}‡ÐºEëlŸ3pà@ «W¯Ö[œ…Ì@gãÀôêÕ‹O>ù$Sò êº.^¼˜!¾rå U«VÍ4^Š_Ï€®8±ëq5ã²SòrIÏ|ý_†–SaBBõêuÅÔ4óžä/S©RéÕ«>³f{¥Z´HT”ö l//;¶mûù•^C¡?ë¯ãjã«Dí"¶T,±V±¸Ä¹P<ª8¿Ìø…ömÚçÉ똚˜âl­^±I±´>ÒšsªsÙÏì<„ÚµkãUËä´d’R“HJKÒùqžÈ)¾t6x5ÀšCf&f”´)©N¨íÊàjçªù*cûâûWÙç;;·nߢÏð>\-u•$÷$¨ÇžcÇœ´]Õ–µ¿¬Í²i—j' tþùçºwïÎ|Àœ9s²S¯^=¼½½Ù¹s'cÆŒÁÔÔ”7npíÚ5-Ê<*?ˆ¯çwÞaÜÂqP(•ù¼åmKÞªô6Ò{ly-§â˜¤¤$ââjrïÞ-Wy̵kÓ_9†¨(W¯®Ö:ÎÖÖç•_ --ÈÈHlmm±µµåñãÇœ8q‚’%KR«V-Ìͳ¾EÅÅÅqñâEž>}JíÚµ)]ºt¶¯‘ššÊÍ›7¹~ý:Ô©SGGÇLãbcc¹páÉÉÉÔ®]›%JdR©ÄÅÅ…äädNŸ>µµ5uëÖÍ0.,,ŒK—.Q«V-ÜÜÜ2œKNN&::¬­­ çüùóÔ¨Q#ËûÄ£G055ÅÉɉ””Î;Ç£GèÔ©S¶ïYˆÿ211áôÉÓ¤¤¤páÂnÞ¾I½:õ¨\¹2&&ºd„ùËAáÀ°~Øä?‰¸ªÙO”” *öåÛ¨T©R¶cr’œ–œ«„;«Ç üp-k„³Ø20M•¦i¦ÃýìŸjfbF)ÛRY&×/'ß%Š•È1ÑNLL¤Ë .\ky ^nMaÑ £ùëÞ_ 7œõ¿­Ïô\ÉY´“: ƒ "11‘'NФI“ ç:¤Ùžæ§Ÿ~¢K—.´jÕŠ:uêð×_Ñ¢E  dˆ°š¯qoÅXÙZ‘è‘N@$”-Í€¦˜;}®¡Ã¹t÷î]<<<øê«¯HLLdîܹšøzõê±}ûvÊ•+§¯R©øé§Ÿ˜4iR†½ÕûõëÇ’%K2%ƾ¾¾Œ=šððpÍ1KKKÖ¬YÀuÿÍ7ß0kÖ,ÒÒ^TZ½ÿþû,\¸bÅ^T¯vîÜ™ÇóóÏ?Ó³gOž={¨?‘:tè …‚N:qòäI@ý¡~ø1c^ì­{ôèQÚ´iÃ’%Kعs'»víÒœëÕ«k׮ůæÅaÍ›7ÇÑÑ‘)S¦0pà@âããqpp–Ýâ•XXXP¿~}ê××²ØØFÍÆ¿6r,âÊR™?‰+v©ïµï•“gPohiözK€žv}Êw—¿SÿÊF©k¥øsΟؕµ#üix¶_q¤©Ò2<7M•¦9ŸsSsJÛ–Î`¿<³ýûâß¹]ývÆäùå×qKãïsæÌ¼½³ßáEdMè,Lœ8‘¸¸¬ÿüòì`óæÍ9~ü¸¦•÷—_~)­¼óÁŒC3Øre XB‡Ñ˜]s6{ý÷r!èMê7áí)ok–ÔˆÂiéÒ¥”,Y’ŋӴiS–-[Æ¢E‹øüóÏùý÷ß5ã~ýõWÆÇÛo¿Íwß}‡££#ëÖ­cÚ´iÄÄÄð÷ßkÆîرƒž={R¯^=Ö®]K:uxüø1Ô¯¿þš3fðî»ïòÅ_ P(X¼x1sçÎ%99™µk×fˆõþýû 6ŒO?ý”~ýú±mÛ6¾ýö[Lbb"åË—çûï¿'**ŠiÓ¦ñé§ŸÒµkW<<2¶•Ÿ:u*-[¶äòåËØÙÙ±bÅ ¾þúk¬­­Y¿>ãŒÐ75j‹-¢E‹²$L­=›ö0tÌPOQ&•ƒ óGæ” .ÃÈž#™2~Š¡Cdêø©lm·• Ž xæó×-èQ§Íë7 N©:Ù^+½°2§$;üix–]+S•©„= #ìIæmÿ8ˆÖµâ‘î‘lÙµEèW E„z E„¯î¯ ¿è½¹7*TTq®Â‰a'p´Êüñ»1É©ˆðñãÇÔ¬9]§%Óqw×6.kAA>$%­Ö:®aCNžÔ>.;!!!xxx`nnNDDNNNšs^^^ܹsG³%dzrš@TTT†FýúõcË–-š¢^•JEåÊ•‰åöíÛÙ¶'ŽŒŒÄÝÝ{{{ÂÃÃ3|”ݲeK9þ¼f»Ê&MšpâÄ V­Z…flÇŽÙ¿?­ZµâàÁƒšãkÖ¬ÁÇLJŸ~úI3 }ðàAÚ´iƒ 111XXXhÆ·nݚÇsõêUªT©¢ù{¸ví+V¬`èСÙþ]J¡Ð&((///C‡¡“   üý¹tã­¶¢UóV9.ÕÒ·ˆˆúëËy³ó<©ð€‡PúFiz7èÍ‚Y ²]‚ö*R”)<ˆ{ 5ÑŽNˆ~ñ¤@7-VÂ[×ÞbϦ=K¡v2­'2c”{".0hÛ T¨pP8°cà£Ož!ï:ÆÆÂùóyr©|×¶mÛ É3@›6møí·ßˆŒŒ¤D‰ܺu‹ÈÈHºté’©ËWÇŽÙ²e GŽ¡]»v„„„pëÖ-ÆŽ›mò páÂâããéÛ·o¦u ;väðáÃ;v,Ã~ïffftíÚ5ÃØ-Z°ÿ~zöì™áxëÖ­ ÍôÚ-Z´È<tèÐþù‡#GŽhhP¯_íܹs¶ïC]Ìž=»Ð$D^^^:Ù/Uª‡v"à`»üwq=ø:ox¿A¯q½²ÜHàuY˜ZPξåìËå8.15‘ûq÷ Î{GÞã7r¾ðc¨Z1ëdtÎ$ÖùAÌÈøHºoêγ”g˜™˜±©Ï&ª:çýM© Ê«[®®Ð°á«=×ßžêqKíÿÖ€úÀ³gÏ(Q¢!!!v½I—~,½Cèµk×´.íI¿fÇŽ³¼æ_|¡¹fºJ•*áìœq‡ô‚ÃF2±¾üþ«U«V™ŽµmÛ€Û·og8^£F5û& §Â’<&mZ·¡Më6†CÃÊÜ G<=h^µ97ÝDå’ýBÇ;Žôø$óž|’³h' ´(pR”)ôù£!Cø¾Ã÷¼å){"çVÓ¦ð矯öÜêÕáêÕ¼''ºì¾ÕRV]Ó¥¯m¶µµ²N\³ºfTT”Ökêknv3Èé5_.\Ì*!„Ðfþ7ó9Øù Á‚³î¡ -ÓºUk}‡f¤•·(p>Úý‡B0¤ÎÆ7oàˆDAP±bEöïߟé\ú±Ê•+hº©9s&Ï®™×üýý³}Í×Ùe@!Š/Ϊ¹«ðô÷Ä4â¥tO 6×mhz½)ëgÞÂNèFh=yyË-‘½E'±ôôRš–mÊ’®K ‘þI]oÖÜÝÝiÒ¤ §OŸÎ°Ä!%%___ ½z©Û˜9;;Ó³gOþüóOŽ=šéZ©©©Ô¯_OOOWWŸ¯¡R)iÖ,óZa]yyÙéÔ$ÅÛÛC똼´`ÁÚ·oO“&MøàƒpttdË–-œ:uŠ™3gâîî®ûã?ròäIZ·nÍ!C2lc×­[7ÆŽ‹…… .¤G4hЀ¡C‡¢P(ذa·oßfÉ’%™ŠóJË–-iÙ²%£FÂÎÎŽõë×sëÖ-~ýõWòå5EÑV˜ŠEÞ±µµeáw sõ)"ÔNh=‘Ĝ݊¹E¿-ýHU¦bmnÍöÛ)m[4‹¦r*"T(=úG¾Ç ¯öÜVVV´jÕ*Câ›ÎÝÝV­Zaeõ¢ @ãÆ9{ö,“&MbݺušN„[·nÕÌ>§«P¡/^dÊ”)>|˜õë×ãääD£FhÑ¢…f\§N8qâÓ¦McéÒ¥šN„óçÏÏT\èííåñ®®®´jÕ {{û ÇMMMiÕªU†5Ò 2333–-[Æùóç©R¥ ëׯçÝwßÍ0®Q£Fš5ÝB¼Iž…®$gÑNöÖƒôýbåæ•µ'IOhº¢)W"¯°©Ï&ú×èoਠÇÊÊ __ß|[: +}èÿî%ýºdh!D^‘¼E;Y- J©Ròî¶w5ÉóÔS‹tò,„Bˆ‚Oh=‘ùY›â7…]×wÐë3ÚÎ0pD†' !òƒ ]I΢$Ðz"E„™m¸¸9GæP«d-Öõ\‡ ºï£k¬òª¡(˜ÜÝÝùꫯ¨[·®¡CEÌìÙ³ ‚($$gÑNŠõDägtòÞI†í€K1¶ÜŽ­¥JAÞu"“»»;Ó§O7t¢’õ¬BW’³h'3ÐBÓssOS±0µàÏ~âá¨ßíЄB!^•$ÐB¯S鱩áOÃø¹óÏ´ªÐÊÀQ !„BèNh=‘ùjÃv ãTø)>lø!#ê0pD !òƒ ]I΢$Ðz" òaΑ9l¸¸€6îmøé­Ÿ QÁ$E„Bˆü E„BW’³h' ´žõù»®ïbŠß*¯È–~[07•Ö¬H¡"?H¡ÐUQÏYt! ´ÈwW"¯ðÎÖwPª”ØYÚ±càœ­ –B!Ä+‘Zä«è„hºmìÆÓä§˜š˜²¡÷j”¨aè°„B!^™|†®'EqA~ª2•¾[úr+æ3ÛΤk•®Žªà“"ÂÌ|||X³fM†¿›¬Ž 0___uºî¯¿þšíÿ›ü±,§F%((///C‡! ¤¤$ …¡Ã(Ð$Ö“¢¸ ÿ“½ŸàìÀÀš™Ü|²#*¤ˆP&OžLlll–çFŽ) ´0*³gÏ–uÐB'²Z Y¡'Eíqéé¥ürê¸6`e÷•ލðÐ5i»{÷.=÷¤Þ›õ¨Ö®ͺ6cá’…yËÓ§OþÉpvjˆW;/wnÌ„¯&œœœ§¯ó*/^LLLÌk_§sçÎÄÄÄü{wWsöÿüuÛ÷V%I$BÖ’,#3e+BegŒÁ`Ã`¾ŒìÌŒc²3¡±2Y²Œ¢Œ"$­*Jû¾œß3ÝŸëÞê¶Ü¥¼ŸÇ}pÏç|ÎçýÉÇç¾;÷œóá{)))5C”„HJž‰°>¶œ¥1¨š4»›I7ñÅ¥/†j†š%9JFšÓþ#ûñýÞï‘Ü+°øÿòÂþAà…@\222jÝ÷›o¾á]ãÆ9r$ÊËËQYY üôÓO˜5kV£c%„BjÐ1ù&–Âý„;²Š³{?Û‹>Æ}$UËSß$Â9Ëç µò\£Z¯73n6é©cßmüñãù’g.e Î0ûíoô1àĉxõêÏëÀàp8ppphT›˜8q"PTT„øøx8::böìÙ¸ví_ý[·n¡M›6PWWGÏž=±zõj”””4鼑Fô$B"¬!gi*ê“Ö>‰Áç¬þÉü°´ßRLé6EÂQµLõM"|ûP®»·FoÑM¨;6nGFpªGÕG©i).þu³¦6¾W×ÀÀ€çý³gÏðÕW_ÁÆÆÇŽkT›ÕÕÕðööƸqãøý÷ßaii‰õë×cÈ!ܺªªªèÝ»7,,,<|ø§NBDDÔÔÔ}n„HšDH„E“ëG ´˜´ö quØjœ; Ùq$ü]鑱U׈²²2”@ˆÞQMà]ü;¼Ë{׸ *„¨£¼ÍyÛ¸öÈÊÊÂÈ‘#¡  €‹/BCC£Ñm¹¸¸ð¼·°°@ûöíqÿþ}žò—/_òLÌÏÏÇŠ+°k×.¬\¹;vìht „HJž‰°Z{ÎÒ(&Mv*öþwó€Îzq|ìqÈpht((**BS¯(ç ýúÃÂ΢޺‚\ùë Ò«ÓëäUX˜6®ý•––ÂÝݸ~ý:ÌÌÌšÔÞÀùÊŒßÿoß¾åN(üp¥ lß¾¸zõj“b „ÒzQMšäaúCLýs*@[Iç&žƒ†bã{Iýºw쎸ì8TëÖ>ÄÂ8Ålùƒoˆ„°~~û3–ýµ ÅÖ‚WÈxLþbr£Úc ~~~¸wïÑ«W¯&·™ššŠÎ;ó”%''ƒÃáÔ»:‰¼¼<ÌÍÍñêÕ«&ÇA!¤u¢nB1iò3‹2áqÂÅÅåÈâÔøS°Ô©}Ù1"œú&þ²ñtxШå’R‰SÁÌQ3<À¼óÐ-·+x»L¦ ú©ôÃp×á>F•+Wâäɓظq#FÝäö ,,Œç}ii)ÂÃÃall\ïúÎ¥¥¥HHH@ÇŽ›%B¤M"$Âj9Ks£ZLZÛ$ÂòªrŒ99)ù)€í#¶Ãµƒ«„£jê›D¨©©‰3¿A·;Ý øL(P 0 7¬ö³°jɪ&ÅÀáppîÐ9 ~54€ @ ÷Pî…î8ö[ã&ù½ïÀøá‡0{öl,Y²¤ÉíÕ8}ú4*++¹ïÏž=‹ÒÒRLš4‰[öìÙ3äæòþ†žžŽ9sæ ¸¸nnnÍ!ÒÀߟæ¦á´¶œEh‡˜´¶ù³/ÌÆ”;€=f`~ïùލõf]å®¶]ñ ôŽ<†àÁx›ð½»÷†ï"_têÔ©YâhÓ¦ þ ú —B.!èJžG=G7›nðZæ…þýú7Ë1æÏŸYYYÂÏÏg›¦¦&vîÜÙà6 ¢¢‚à³Ï>ëW¯pðàAtèÐ'I?~ü86n܈~ýúÁÜÜ/_¾Ä­[·P]] ¬ZÕ´_B‘64‰«µå,¢@ 4i°í÷¶ã`ôAÀÓØ5r—dúHÉÉÉÁg’|&ùˆô8#GŒÄÈ#EÓö;),,äÛÆáp¸ïÑ£_o± ²^½z¡ºº‡ÆêÕ«qöìYäççÃÇÇ›6m‚®®.·î'Ÿ|‚ׯ_#** —/_†‚‚ Ì™3rrt{$„"‡Õ7à’4YMÏZkøíÿòËËp;ê†*V3M3DÌŠ@ÁH&£¤¤„   Œ1BÒ¡$$$(--•t(Í+ºøå½¥=WÉÅCÈG 5å-¢Bc Å¤5 Èžý' ŠUAU^Nü“’g ßi !¢@“‰°ZCÎ"j”@‹IKŸ[š‹OŠÜÒ\pÀAÀèØéÛI:¬V©¾I„„Ò4‰«¥ç,â@ ´˜´äùU¬ 'àyösÀjçÕÛy¬„£j½„™DH! E_ÇaµäœE\(&õZze).¿¼ g3ß9}'áˆ!„B$‡hR§ƒÑ±ýÞv@wƒîðœzö"„Bi½h&1‘öù999˜ÿÍ|Ä&Ä¢°¢ 0ï`Ž úÐVµ-þœð'TäU$j«G“ !¢kkkI‡AZ€²²2(**J: ©F=Ðb"ÍòÃn†¡·GoS>†èþшwŽÇÃ~q¦ú Êÿ(‡|®<Îx©&‰šDHšDH„%Í9‹´ èZäççãáÇˆŠŠBrr2úõë‡ñãÇóÕ[¸p!_™±±1–.]ÊS&­òóóó1kÅ,ĉçÿuÊ€;`fG#GI„÷Q¢I„„Q I„DXÒš³HJ k±mÛ6¬]»úúúÈÉÉAII‰Àzçΰ··ç¹ØÞ‚š´[±~ìjÿ.BxÓá ~?ò;fúÎkl„B!ÒˆèZøúúbÆŒ011¹¹yu¿üòKîS{ZšÈÇ‘¨îS÷2ó2\üë"%ЄB! 1еjß¾=LLLš­=iDX\Q\%àMöÑC|\“cÈÍÍEIIIý• !MBO"$Â’ÖœEšPt3ؼy3üýýaddWWW,Z´ÊÊʉïc,£I„Í/&&k×®ø ’tx"!-×sSËÂÂÂPZZ*±´ô²šäYb¡2é*«ÉI¾ùæ:t¤~ÔÝD×®]ãþ}Ñ¢EسgfÏž“'OÂÛÛ[‚‘Õ¯¬ª ¡ê¡¨|Z ˜ ®#ûZƒÛ†ó g±ÆFjgkl ‡z’ìW%%˜²jf|ùe£Žñiß¾ÐLM…\bó*+aì䄟oÔ1$!88#FŒt„BZ8û˜\6’¹¹9FŒÝ»w×[·¨¨˜9s&¶mÛÜ †ÇJPk IDATÒôÕYqE1±ø{·ï¥ÕÅHII AAAµ&zk¿øŸýú+ìëêágnŽQQÐÒÒjT A'N wæLøÖZgm›6ø,8ö={6ê››Ë7„ãÉ“'ÈÏÏX_AA=zôà){ùò%ÿüsܸqCà6}}}ddd***ðí·ßbË–-þýÅ£¤¤ööö8~ü8:uê$°ÒÒR())5{Ü„H;z!=‰°~4º QPPÀ}Ÿ““ƒï¿ÿÕÕÕpuuå©+-“³K²áàÂMžGXŽ@Èä¨+¨ÃÙÉ;~ØË§.cÃw(y–ú&jii¡½›¢dÿ÷ÝijŠëÖ5)‡ƒIß~‹#jj‚Ѧ ü÷ Ks»párrrx^GއÃÁ Aƒ¸õ~þùglÞ¼S§NÅ»wïPPP€³gÏâùóçpwwGee%_ÛÞÞÞPVV†ºº:qúôi‘œ!ÒˆžDH„%-9‹4£º7nÜ€¹¹9ÌÍÍ‘ššŠ£Grß× ‚¡¡!áää}}}`åÊ•8p O{Ò0›5½0Nœð ý`Lç1øsŸP–S®gO"NÂL"\°nv ¸¦¢´ýZææ€–V“^îsæàbI >LCEÙû jjjÐÒÒâ¾1gÎôìÙ“ûubee%Ö¯_eeeìÚµ ZZZ••…‡‡&OžŒgÏžáèÑ£Ü69zöì‰iÓ¦áûï¿G÷îÝqãÆaÙ²e"9B¤ }O„% 9‹´£!µ°´´Äš5kê¬ãççcccÞ[Ú<Ï~×C®HÉOÌq˜ƒ_Ü~-ç‘ã„߇c¡£8´ïÖ ZÎÎÍv wžGŽ`rv6ä úÞç÷bÔ¨Q(((ÀíÛ·a``ÀÝ–WUUa„ ˆ‰‰ÁÅ‹ù¾ÕÑÓÓ¼xñ‚oßš2zÓ¾}{@jjjSC&DêÑ$B",šDX?-&’?í>œ:s“çµÎk)ynò$š±ÐͱòFmܽ¼pÑÜY_ïó¢E‹pñâEìÚµ Æ ãÛnii ‡ƒÐÐP¾mW¯^tèСÞãDEE%ä£@“‰°haý(qÈ¿‘t®‡\‘SšØ6|¾súN¬1ÆiÈ“kz¡¿jÓ¦É+oÔ¦fEŽájjbé}þñÇñÓO?aéÒ¥˜5k–À:êêêðòòBZZBBB¸å8uê”””àããÃ- åY•£¼¼çÎÃwß}UUU :Tt'Dˆ” I„DX4‰°~4„£ ŽÆØ“cQRYŽ vÚ™=fJ:,"" ׯ‡£««HzŸk¸{y¡¼¼\,½ÏË—/‡¬¬,ž={žmÚÚÚ8pà`ýúõ¸sç<<<0fÌèéé!88 ضmLLL¸ûMž<UUU°µµ…¶¶6›› yyyìÙ³ffµ<Š“B€èVæôÓÓð>íòªrÈÉÈ!À#Þ]¥û‘â¤i4551üƒD³¹q8x¾×£Û\àëë‹Þ½{sË&Mš„ŠŠ õ544¸ïС=z„ 6 ""Ož<££#öíÛÇHXÃßßaaaxñâž†Š &`ïÞ½8~ü8¦L™Âýè³Ï>ƒ¶¶6· ˜˜˜p߯[·¹¹¹ „³³3·\WWÞÞÞÜ÷ööö1bLLLðàÁœ;w[¶lAHH"##é+PBi¥¨ZŒòóóyÞˆ>ïÓÞ¨¨®€‚¬þÿ%ÏDhÜÞçÊÊ€¥%ðøqóääIÀË‹§H½ÐöööÐÓÓã)sqqDGG׺ߋ/PXX¾žûO>ùðø¿Ÿ‡½½=:uꄵk×bÉ’%¸{÷®Àá2<€† RgÌúúúxøð!~øá|þùçØ¿?2220wî\ÄÄÄ`ÇŽõŸ8!„‰z ÅäLð\s ªÕªèhØ}§ôÅÊ¿W‚AE^g½Îb˜Å0I‡I¤€0“?ì}æòð€ñO?aƧŸ69Ž¢ìlì-*Bž™O¹(z¡û÷ïÏWÖ·o_@JJJ­û%&&Öºÿ Aƒøöÿã?àïï;wbëÖ­hß¾=¾øâ LŸ>;‰011±Ñ__ÊÊÊbÍš5øõ×_ŽeË–5ªBD&aÑ$ÂúQ-&ª(Xx–÷ –\ K \𾀦¢Y=´<ÂL"\¶m²gÍâß ¬ŒÒ08%¥É+røí܉¼±cn«é…n®9ž>}ÊWVóØáºVâÐ××>¾QçòèÑ#TTTÀÆÆ¦Qû"*”<aQò\?J %E`½nkþUHë¶dãFd;9……‚_UUx®­Ý¤9–þð’‡ ©ý……Hrtl¶±Ð¥¥¥<îyyy8yò$455y&î}H[[“&MBrr2.\¸À-óæ wÿI“þWðâÅ =ݯ_¿FUUwã×_ ‡ƒ9sæ 77—§nrr2÷ï¡¡¡HOO羯¬¬ÄÕ«W1uêT€§§g~„BZÂ!AÕíªqýîu,_¸\Ò¡¤§­-Ìÿë­•’*šp + Œ‹þQ½:z‡ÂËË kÖ¬ÁéÓ§add„°°0¼~ýûöíãY1C 6àþýû3f † „††"''ûö탮®.€'Nœ8={öD§N ##ƒ¿ÿþ/^¼À7ß|Ã}·¶nÝŠ¥K—ÂÚÚƒ ‚ªª*bbbPXXÈMÂ÷íÛ‡ÀÀ@tìØ&&&ˆŽŽFVV8–,Y·÷'xBiU(AKÙÊÅb…H·ú&îö÷y ßΟ/òc€¦¦&–-[WWW¬]»û÷ïGDD\]]áëë '''žúnnnh×®OYÍr;vì@dd$RSS1~üx̘1ݺuãÖsuuÅîÝ»qëÖ-¤¦¦BMM óæÍðaÃйsgž6,Xggg>|ÑÑÑxóæ ìììxzÿúê+tìØ?Fff&zõê+++øøø G"øiÒ44‰‹&ÖÃúÜ`Ò`~~~¸ø~°!˜%; ¿mýM"qé$//óçÏcĈ’…´ !!!ðððàKÞâEW¿”üÿ{OEÀUArñ´`~~~4šÅÅŦ¦¦t½ÔÆ@‹‹&QÛÇm1{ÊlñÇB¤ZCžDH!¢dˆ‹&Öh Q~® ¯Þ^èѾê%„BiIh ´¸xÈfÉÂ0ÍÓ†OÃêe«%!„Bi J Å¤O›>è¥Ü Ç „ó@g´mÛVÒ!)EÓ!¢@“‰°haýh‡˜¨(«à'ÿŸà9Ö“’gR'ažDH! å/†|Hë™™)é¤õ@‹ È'Â’••ÅÖ­[qâÄ I‡BZ´´4I‡@¤M"$¢œ¥~ÔMˆ”Ù¾};Œ%iaŒ±}ûvI‡A!ê&DÊÌ;sçΕt„B©õ@‹IYY™¤C -D\\œ¤C -DVV²²²$i!èÞB„E9Ký(®Eyy9"##±gÏøûûãÕ«Wë1ÆpäÈÌš5 Ë–-ÃÝ»wÖ£ùDX4ч+<<ááá’ƒ´to!¢œ¥~”@×¢oß¾èÕ«.\ˆo¾ùÏž=ã«ÃÔ)S0oÞ<ÀÓ§O1hÐ ?~œ§^rr²Xb&„Bi”»ÔÆ@×bÓ¦M033Caa!ìííÖ ÃÑ£G‚áÇøwüê—_~‰±cÇBAAAœ!B!„1 èZ 2–––uÖ9xð ”””àêêÊ-swwGVV.\¸ ê›]\\œÈÇÈ5¹ÆÆÙc S722©©©µn‰‰A||¼Àm¸wïžÐñH£æø·¬Kbb"¢££›ÜNcâlÈ5&lœõÅQ×vQÿ¬Eî- «K÷º·to‘v”@7ÁóçÏamm YYYnY×®]¹ÛÞWQQ!ÖØƒ>äVWTryyyÂ)aô!÷/I~Èåçç#??¿ÞcKÝ[V—î-to${oi 9‹¤q=7¸NÑÑѰ··Gpp0FŒÁ³ÍÔÔÄÑ£GyÊutt0yòdüøãkkk$%%ÁÁÁòòò|ÐÓÓ“ªkãò‡BSS Þ·òY†•;CNî߇qú/^x¯A??aËj&N™ššŠìÿ‚0÷¡‚‚´mÛmÚ´XïÕ«WÈËËCçÎùöUPPÀ»wï`cc÷ï™3g0fÌ©º6ú¡9Ê222œœ ;;»&µ×Ï” ù¿ÀápPVVÆý6¼±×_xx8LMMëÜ·æs5++ šššˆŽŽ†‰‰ ž>} "%ÐõhŽÚ××÷ï߇¡¡!ßþ„B!’ôá„Áׯ_£OŸ>PDÒ&6‘‘ž>ãÇÇôéÓ1lØ0”——K:,"åNœ8AƒÁÀÀ@Ò¡HŒìš5kÖH:ˆ–(11÷ïßGvv6ŒÖ)**Â_ý…k×®AVV–ïA*`gg‡>}úàï¿ÿFjj*œE9'Æ^¼x+W®àöíÛ022‚ºººÀºçÏŸÇ¡C‡ðèÑ#hiiñŒ‡Ÿ:u*¾ùæXYYáÞ½{ÈÊÊ¢•^Z¡üü|ܽ{W®\AZZ:uê$°^RRŽ9‚3gÎ ;;:u‚¬¬,wûðáÃáââOOOâÆøä“OÄuDLŠ‹‹‰ØØXèèè@YYY`½¸¸8üùçŸHII¾¾>O½7n ==ðóóéS§ ¥¥UëµGZ¦æº·Ô˜7oæÏŸÿqc¤A®\¹Âôôô€999 ¬—œœÌ¬­­™¶¶6ëÙ³'“••e_|ñ«®®Xÿï¿ÿf;waäDìíí&##ð°°0¾:ÕÕÕlÚ´iLUU•yzz²~ýú1eeeÌ­3`ÀÖ©S'Ö©S'Ö¶m[¦¥¥ÅæÍ›'Æ3!¢vöìYÆáp¸×Km÷–0===fggÇ&MšÄtttØ'Ÿ|ÂJJJÖÿã?ØÈ‘#E9‘777&++Ëý,toaŒ±M›61Ö­[7¦¯¯Ï Ù£G¸ÛÏœ9æM›Æ}¿bÅ ¶víZQ‡OĨ¹ï-±±±ÌÀÀ€UTTˆ!zéE t=|øíܹ“ݸqƒ999Õz!Ž5ŠuîÜ™ååå1Æ a‡2ÆËÏÏg<`UUU,##ƒMŸ>}ûí·â: "&§OŸf<`7oÞ¬õC.44”`—.]â–yyy1SSSVUUÅWûöílöìÙ¢ ›HÀ³gÏØùóçYzz:>|x­÷–°þýûs?¼=zÄää䨾}ûcÿÞ[þøã–’’ÂBCCY÷îÝÙ©S§ÄuDLvîÜÉNž<ÉŽ;Vë½%22’`{öìaŒ1V^^Μ™­­-·ÎÛ·oY»víXbb"ËÌÌd¶¶¶,&&F\§AÄ ¹î-5¾úê+¶téRQ‡-õ(n‚Ú.Ä””&##ÃÖ­[ÇSÞ±cG6lØ0ÆcÙÙÙlèСÌÜÜœõíÛ—­Y³†egg‹#l"wïÞ­õCÎÛÛ›©ªªò|;Ȱ+W®ðÕ¿téß ´.µÝ[ž={ưM›6ñ”ÛÚÚ²~ýú1Æ+,,dÓ§Ogýúõc¾¾¾,((è£ï)jÍ꺷̚5‹)**²ÊÊJnÙÑ£GvçΞ2{{{æââÂ:$ް‰„4åÞRcþüùìÅ‹¢ ³E÷‘Á?ÿüƒêêjØØØð”wíÚ·nÝèèèàÊ•+’H™§OŸÂÆÆ‡[ÖµkWî¶¡C‡òÔ§±¬¯¸¸8@—.]xÊ»v튀ªª*öíÛ'ö؈ô‰ŠŠâÃZso‰ŠŠâ>³ÀÛÛÞÞÞ‰‘Haî-5~üñG±Å%ÍhHKK øBÌÊÊ¢΄GZZß/[–––PVVæ^K„uß[rrrPRR"‰°ˆ”JKKã»V¬­­!//O÷£æzÔñG÷Á(ââbà{’“ŽŽct!UUUPPPà)ãp8““CUU•„¢"Ò¨æzøpޚ뇮ò¾ââb¾Ï!yyy¨©©q?§þÿÞñágÝ[jG ´Ô,WWó•HØØX(++CSSSa)edd„ØØXž²¤¤$ÀÈÈHBQiTs=|x½<~üêêêPSS“DXDJò}¥§§#''GಪäãE÷–†£ZÌÌ̾k¶RÃÒÒOž<á){üø1w!5j®A× ]+äCfff?‡j¶R£æþ!èz¡{‹`”@‹@¯^½Ð¾}{ܽ{—[–ŸŸÇÃËËK‚‘iäããƒÜÜ\Ü¿Ÿ[Œ6mÚ`ĈŒŒH›nݺÁÎÎŽgrrr2bccáãã#ÁȈ4òòò›7oÀ- ‡ššFŽ)ÁȈ´©¹·\½z•[F÷–ºqcLÒA´$ïÞ½Ã÷ß Bee%ÆX±bÚ¶m ÀÔ©S±hÑ"ØÛÛc÷îÝHLLDDD}uöÙºu+®^½Š¼¼<Ü»w½±Îê!IDATzõ‚ŽŽœ±|ùr@ee%F…èèhøúú"55'OžÄþýûáëë+á3 â’™™Éý÷~ðપªÐ»woÀ¾}û`bb¸zõ*ÜÝÝáââ‚îÝ»ãøñãÐÒÒBXX444$?¯cÇŽáþýûÈÈÈÀÉ“'1vìX˜˜˜ GÜ„§¤¤½zõ,^¼©©©øßÿþ‡U«VaÕªU’ ŸˆÝ[Dƒ–±k ªª*ddd€û¾²²’[Ï×תªª8xð ®]»{{{=z”’猩©)ºwïprrâ–¿ÿõ©œœΟ?_~ùáááÐÓÓÃåË—1dȱÇK$GAA{­ÔüYCQQ‘û÷¡C‡âæÍ›8räbcc1cÆ Ì›7>à>2yyyÜÏžšo6322››Ë­£¬¬Œ[·naåÊ•øù矡««‹Ÿ~ú ³gÏ–HÌD2èÞ"ÔM!„BHÐhB!„B€hB!„B€hB!„B€hB!„B€hB!„B€hB!„B€hBH«Éó46QËÊÊÂõë×yÖ†o¨èèh<þ¼£ªÛ³gÏ––Æ}çΤ¦¦ŠíøñaÌ„".”@BZ´””Ìš5‹û”GøøøàŸþáÖ™1cöìÙ#¶˜ÂÃÃ1xð`6º… bÛ¶mÍUíòòòЧOždt̘1 lrÛ?n–v¹|ù2ÆŽ+’¶ !¤.”@BZ¬?ÿü]ºtÁÝ»w1iÒ$>|óæÍC^^ß·ZšY³faôèÑb9ÖÏ?ÿ [[[îã}›ÓÅ‹±dÉ’fo¦M›†¸¸8\»vM$íBHmèQÞ„)//³fÍBÿþýÄóHÚyóæáüùó÷{ûö-ÒÒÒйsgž}ÊËËQRRMMMžúùùùPTTäÖ-((€¼¼<”””jm«®˜åää ªªÊ-KKKÃëׯajj }}}nùgŸ}YYY@uu5òóó¶©®®Î­¥¥¥xúô)ôõõaddToLEEEرcvïÞ]k”””––¢cǎܲÊÊJBCC22¼}1………‘‘¬¬,JKKQ]]Í}Ä´‚‚TTT¸u+**ðôéShhhÀÜÜ\àñóòò‡¶mÛ¢}ûöÜr555Ìœ9ëÖ­Ã!Cê=WBi6ŒBZ €=yò¤ÞºvvvláÂ…lôèÑLVV–`FFFìñãÇÜ:¿þú+ÓÔÔäÛ×ÌÌŒmذ§­E‹1OOOn[†††,**Š[çìÙ³ ËÉÉaŒ1V]]Í.\ÈtuuÙ;wcŒ]¾|™uîÜ™ÉÉÉ1===€Í;—Û†““›={6cŒ±§OŸ2_wïÞeŒ1VZZʾúê+&++Ëäåå6pà@–œœ\çÏæäÉ“LVV–•——ó”ëëë³U«V1GGG¦¨¨È0;;;–Àcìõë×LNNŽ;vŒg¿ââb¦¥¥Å6mÚÄ6lØÀ¯——cŒ±ªª*æïïÏ”””¸ñÚÚÚòü›$$$°¾}û2999Ö¶m[&##ÃìííyŽwç΀%%%Õyž„Òœh!¤EЉ‰:wî,Tý_~ù666HNNÆÃ‡¡­­}ûö5êØ»ví‚™™ ZÇX—••a„ 8wîîܹƒ¾}ûV­Z…áÇ#??oß¾Evv6&Ož,°N:¡¤¤„û*..ƧŸ~Š: S§N€•+W" ÇŽCnn.îÝ»‡ÒÒRLŸ>½Îs‰‰‰™™äååù¶mذÆ CFFþúë/TTTÀÛÛ`hhwww¾ó>uꊋ‹áçç‡åË—cÆ 033c Œ1œ8qð믿bÕªUصkÞ¼yƒØØX˜˜˜ÀÓÓ“;ùróæÍPTTÄ›7o™™‰¢¢"lÚ´‰çx–––Üó „q¡šÒ"%$$ÀÔÔTèúÆÆÆX·nŒŒŒ`ooqãÆáÒ¥K:¶6nÜØÙÙaâĉ¸|ù2_½ÜÜ\ > ¸{÷.¬¬¬ü;l!..–––PVVèèè _¿~Çáp ¤¤Ä}­^½·nÝÂÅ‹¡­­‚‚üøã˜={6<==¡¢¢‚>}ú`ùòå¸zõ*’’’j=—˜˜nú¡öíÛcíÚµÐÒÒÂàÁƒ±xñbÜ»w>Ì;ׯ_çY-dÏž=;v,Ú´iSçÏpýúõ?~<¦M›---ØØØ`ýúõxúô)nß¾ xôèLMM¹Ãj”””àêêÊÓN›6m ¡¡A 4!D¬(&„´Hzzzx÷îÐõÝÜÜxÞwëÖ /^¼¨ulq]>ýôSp8î{{{{$$$ðµåêꊪª*\¿~mÛ¶å–ËËËcþüùøòË/akk‹µk× ½ÛÞ½{±cÇœ>}ÖÖÖ€øøx”——ãï¿ÿ†§§'ƱcÇr{ØëZ/99·}˜¬Ö¼úô)ÀÅÅVVVØ»w/ 66wîÜÁìÙ³ë<‡œœ¤§§#!!'Þÿýï••寻páB¢]»v˜?>ÏÊ*ï300@rrrÇ$„æD 4!¤EêØ±#’’’PTT$Tý÷'îàN|«®®ž„ø}eee|ejjju¶U£ÿþxô袢¢øÚX·nž={†Q£FáðáÃ077çžð¡«W¯âóÏ?Ço¿ýnyyy9 W¯^9r$ÜÜÜðé§ŸÂÓÓàóÄ¢Öõž•””¾¯bÁáp0gΠ¼¼{÷î…µµ5œœœê<šxmmmyâuwwǾ}ûпÀøñ㑜œŒ… âþýû°³³ƒ§§'_{iii°°°¨ó˜„ÒœhBH‹äéé‰mÛ¶aÇŽøöÛoù¶—””p‡GÃÆÆyyyHKKƒ±±1€‡ddd4:Æ;wB__#FŒÀ¹sçx’^àßñ»þþþð÷÷Çòå˱eË|õÕW“ã¿5ÇÆÆbüøñøúë¯1uêTžmÖÖÖàp8(++ƒŸŸ_ƒbìÒ¥ öïß/p[hh¨À÷5CQÀ××+V¬ÀñãÇqèÐ!|÷Ýw<û¨¨¨ðý¢¯¯mmmdggׯ®®.–.]Š¥K—"((£GF\\·÷===EEEèÒ¥‹PçK!Íz  !-RïÞ½1sæL¬]»›6mBqq1€{ŒƒƒƒÑ­[·µ×·o_¨««cïÞ½¨¬¬DRR¾ÿþûZ{¦…µiÓ&,Y²nnndgg#44Œ1n½’’”–– <^ff&ÜÜÜ0bĬ[·Žo»¦¦&¦OŸŽÃ‡ãܹsÜÞ²²2œ:uªÎøºuë†ÔÔT”––òm‹‰‰ÁPYY‰/^`×®]°±±AŸ>}¸utttàåå…/¿ü%%%ðõõåicàÀÈÈÈÀýû÷yÊ/^Œ .àØ±cÜoª««Ì]ò.00'ùÎËËðÿ=ØðâÅ @×®]ë6lÀ–-[PUU…;wîðõ¢‹Zff&”••¡¡¡!ÖãB%ЄB%..oß¾…£££À'BHkE 4!„B! @ËØB!„Ò”@B!„Ò”@B!„Ò”@B!„Ò”@B!„Ò”@B!„Ò”@B!„Ò”@B!„Ò”@B!„Òÿ«T8ªœø™8IEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/create-chunksize-15GB.svg000066400000000000000000003727101231437614300256330ustar00rootroot00000000000000 image/svg+xml Automaticchunksize PyTables-v.3.1.1/doc/source/usersguide/images/create-index-time-int32-float64.png000066400000000000000000001677161231437614300274460ustar00rootroot00000000000000‰PNG  IHDRÐß}™SsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìwXÇ÷ÿß÷ÒQé(MAŠˆ¢ X#v°Çk”X¢1ĨјØ»Æ’øµ7Ô AT#Š€(*Š"½Êåžßüî~Xî.ØÍ¼žç>ÊìÙ™3³³³ggÏœÁ`0 ƒÁ`È…ð]+À`0 ƒÁ`|H0šÁ`0 ƒÁhÌ€f0 ƒÁ`03  ƒÁ`0ŒFÀ hƒÁ`0 £0šÁ`0 ƒÁhÌ€f0 ƒÁ`03  ƒÁ`0ŒFÀ hƒÁ`0 £0ú rãÆ Êõ»ÿ>¶lÙsssž?ŽÐÐPn h*Ïž=Cpp0¶lÙ‚¥K—b÷îÝ …H$z-z¾-***ðûï¿cÒ¤I°´´ÄÀß©>ׯ_‡¹¹9NŸ>Í¥EDDÀÜÜçÏŸçÉ–””àèÑ£5jÚ´iÃë#—/_Æ‚ `kk sssTTT¼µ:üðññAŸ>}^{¾²®ÿÛfòäÉèÒ¥Ë;+ŸQ7ŠïZ™µk×âìÙ³rÉnÞ¼ùùùÈÈÈx/zÉÉÉðööÆ·ß~‹I“&½öü‹‹‹‘‘‘òòòמwAAÁ{ۮOŸ²— ™™™°°°à¥9::¢o߾ظq£\òåĉðóóCff&—¶yóf,X° QùˆÅblÛ¶ K—.Eii) E‹(**ã›o¾¯¯ï+éû¶øñÇñã?ÂÇÇS§N…‘‘Ñ;Õ§¼¼(..æÒÊÊÊ‘‘’’ž¬¯¯/Ž=Š‘#GbÖ¬Yh×®àâÅ‹0`ºvíŠQ£FAMM ŠŠïÿ£7''=zôÀÌ™3±páÂw­úôéƒÖ­[ãСCRÇžãÆƒ¿¿?ºwïŽ7ÂÎÎÍ›7Gvv6bcc±víZlß¾ýƒ1 ÿøãXXXàÌ™3ïZ•:qwwGRR’Ô8}þüytïÞÇ—J€ÀÀÀwþBÐD"RRRðìÙ³w­ àþýû¨ªª’yÌßßb±øµ—ÙµkW$%%½ñë–žž^ç ÀÞ½{ßÈÄãÕaô¤.CXUU666RéåååPTT„‚‚—VVV†ŠŠ hjjB  ++ Ïž=ƒ££#'÷òåKܾ}&&&õÞì"‘wïÞEQQ÷°mˆ—/_r3ZeeeÜ®¨¨(u~~~>îܹXXXðt¬555BYY™K«¨¨@YYZ´h¼xñÉÉɰ´´„Ay=}úéééptt„ššZƒeggg#55&&&033ã“´óæÍ¥fÊËËQ^^uuužÞuñèÑ#dffBQQíÚµƒ––VƒçÈâÉ“'HMM…¦¦&lll ¢¢"%SYY‰ääd¼|ùvvvPUU•+ïÂÂB((( Y³f¼t‘H„ââb4kÖ JJJªûEii)w}rssqÿþ}tìØ‘“ªgIÿý÷_hii¡M›6ReA  yóæ "$&&B$¡}ûö2ëVÉ5ªÝžMMM^z~~>TTT¸¾Ñ²eKîÚŠÅb¨nÚ5Y×K,#..ŠŠŠ°¶¶æÕ»>æÌ™ƒ9sæ¨6¬šb@Ÿ?þþþ:t( þÏ#ÏÀÀ^^^ðòòBXXï¼’’ˆD"®]>|ˆ´´4ôèу{ªªªššŠ‚‚™ã„¤ïKÆ% ………‹Å2¯…’’’Ô‹ŠɽþøñcØÛÛsí^ûÞª¨¨À;w  akk+³½köa"Brr2Š‹‹åú ^TT„„„´k׺ºº2eTTT`hhÈÝSeee(**Bnn.,,,8ÝÕÔÔPVV†ÌÌLhhh@MMMªïIxüø1EJJ ºuëÆ»GêËéëöôéS¤¥¥ÁÚÚšW_‘HÄÝ\„B!444lÇšºXXX U«VRÇ+++QRR­ùùùHJJ‚­­-ïž%"@,C$ñîII=tuuAD¼ük+ ÐÔÔ”š¤ÉÍÍÅÝ»waoo/ÕÏ•••ahhÈ»^ReIPRRâ•••¸{÷.ž?###XXXHõÙ‚‚TUUA,óꦡ¡¡Pmmí:_D"’““Q^^;;;™Ï»ââbˆÅbîºIîk[[Û:ïG"B\\Š‹‹9½2 Æ[¹¸¸È<¶bÅ @<àÒæÏŸO(11‘œœœ Šˆˆ "¢… ’ºº:wlΜ9$‹yyWUUÑO?ýDªªªœœ@  ¹sçRYYY½:ïß¿Ÿ;§æ¯f=ž?NC† á744¤   ¹ÚERFxx8—¶yóf@‘‘‘äååE€Ë{áÂ…RyäççÓÀ9%%%Z¸p!­]»Vª]‰ˆnݺEvvv<Û·oO·oßæd®]»FŠŠŠ4tèPÞ¹Ô¶m[222¢ìììzëG¶¶¶Rí7~üxÊÉÉ‘«}ˆˆÂ¨mÛ¶¼<444èÌ™3œŒX,¦M›6ñ®³¢¢"ùùùÑË—/yùyzz’™™/ÍÀÀ€ú÷ï/UöŸþIèøñã\Ú®]»…††Ò€¸ë£©©I$"¢íÛ·“¶¶6§ËÀ©  €—·µµ5¹¸¸PHHéêêr²FFFtåÊ•ÛeãÆ€RRR¸´ÌÌL®?}ú”KOII!´qãF.MSS“† FDDIII2ûzÍ¡R"ïïïOÜñ¶mÛR\\\ƒúÖ& €ÐæÍ›už)**ÒÝ»wuÞ°aÃHSS“âââÈÜÜœÓ?//ˆªïÅšõ4eÊ***âòøå—_EGGsiyyy$ O§ŒŒ @Ë—/¯S'É=Zû·ÿ~""ª¬¬¤åË—“’’wLEE…Ö­[GUUU¼¼$}8 €ë{µûymÄb1}ûí·œþ€úõëGçÏŸçéA$}/HÆèÚ?ÉøUû7kÖ,.¯+W®¥¥%ïx—.]x}™ˆhÖ¬Y€’’’xcVRRÉ7–ùùùŠ‹‹#777NVAAÖ®]+UÇÚ¿†Ú‘¨úsvvægooOÿþû/Oîøñã€Îœ9CÆ ãÆÉsI$Qu¿ªëž|òä ¹¸¸µµ5/ɸòçŸ’ŽŽwN§N(//òòò¨W¯^Ü5WSSãoDDáááR×_SS³N}jŽ?þø£”¬¶¶6íÝ»—W†ŠŠŠÌ¼$×¶ÿþd`` ÕÎÛ·oç=÷…B!Í;—ÊËËyr’qþæÍ›Ôºukž..\àÉ>yò„ÆŽKúúú<]Œë¼Þÿe˜ý–©mxÖ¤>ÚÜÜœFŒA!!!´qãFÒ××'SSSúꫯÈÖÖ–vïÞMAAAäîîNèÔ©S¼¼,X@ÈÇLJNž|8]¿~bcciÚ´i€fΜÉËóuÐ&&&Ô»wo  ýû÷“¥¥%5kÖŒV¯^M¦¦¦´fÍ §áÇZ¹r%/okkkÒÓÓ#š;w.]¹r…–/_NdaaA•••õ¶Mll, ;wriû÷ï'¡PHB¡Ž;Æ¥ïܹ“Pll,—VÓ€.++£¨¨(RVV¦¾}ûr}=**Š'oddD†††äççGW¯^¥E‹‘ªª*uêÔ©^]eѺ¢¢‚ÈÞÞ¾Ñå 6Œ”••©uëÖ´zõjºqã…„„Pyy98p€PÏž=éòåË”@ß~û- O>ù„ËCò¢QÓè:sæ ÷¶k×..}ß¾}€{á—ÅãÇ)**Šttt¨[·n\›?{öŒˆˆæÍ›GhÚ´iCÑÑÑ4bÄ@ß}÷//200 ###Ú·oÅÄÄPhhh½m"¹ÇÇGñññ”@“'O&CCà èôôtŠˆˆàÆW‰î’:yxxPóæÍ¹ô´´4""ºzõ* …B²³³£ 6Pll,­ZµŠ ÉÈȈ÷Â"1 MMMé›o¾¡k×®QDDåææ6j,“Ðæææ4xð`ºpá8p€ìíí ݸqƒˆª'#‚‚‚Mœ8‘Ó=&&¦Þv|ñâikk“ŽŽ>|˜îÞ½K'Ož¤–-[’ºº:=zôˆ“•ÐFFF4räHº}û6ݹs‡f̘Aè›o¾!""‘HDQQQdhhHNNN¼{R2)P—­§§GFFF´xñbºrå ùúú’P(¤1cÆ»»; 4ˆ‚‚‚h÷îÝdjjJÍš5ãMjÈ2 ###)<<œ÷“<_çÌ™ÃÉ-Y²„¶oßN7oÞ¤ÔÔTÚ·o÷|¾zõ*'wãÆ ²³³#333^Ý$[² èM›6P=!Eÿþû/ùúú©gЧ§'µhÑ‚ZµjEÓ¦M£ˆˆZ·néëë“¶¶6åççs²#GŽ$@@;vì øøxÊÈÈ ÞKã0ú-ÓTzâĉ|˜Ð¢E‹xéÉÉÉ€:wî\oÞDõÐîîî<Ù„„îa'áÚµkTÏêÖD2Y»]û÷ïO€{Ë—ð÷ßúú믹4±XL^^^¤¢¢B111´{÷nêŸQ“ɬmnnn½r"‘ˆÚ¶mK:::õʾxñ‚Z´hAÆÆÆR_ œœœHAA’““¹´×e@wéÒ…'+yÙª¿œH¨¨¨ UUUrppàÉ[[[:zô(/]b4Ԝ唅X,&]]]>|8—6qâDrvv&gggš:u*—>|øpÒÕÕåµOMZ‚ŠŠ 3Ffy’Y¥Ú/OÆ #žnŠ˜˜H¤¾Œ•——s3l’Ÿd6¯¦ž5û8Qu?355%eee*..æóöö&Â¥µjÕŠúõëÇý=gβ··§=zðÆŸqãÆ‘ºº:UTT4X/Yý/--”””¤Æ‘ªª*jݺ5©««ó¾2:qâDƒå“ŽŽŽÌûÆÂ¢Aš¨úÅ«öì²ÉŒm:uêDŠŠŠ<£’ˆèĉ€Ö¬YÃ¥IîY/Üue—.]’ºÎºæõ!" %€ÿeïÉ“'€üüü¤Ê¬‹E‹:tè/=00{’ 1 õõõ¥¾"ØÙÙQ³fÍxÆ¿™™yzzÊ,·.ZÖdR§N 4ˆ—¾mÛ6@‡æÒdе¹yó&©««“««kƒ_s333IAAÆŽÛ þjÐ………¤­­MZZZR“ ={ö$@À›í÷ôô$€ÿÕèöÆÉ“'¹4sssòð𨷌ÿÁÂØ} Œ5Š÷·$üØ€x~[VVV000àEW†H$Âüùó¥ò7nÄb1nß¾ÝdÝþþûoÀ'Ÿ|ÂK·¶¶†¹¹9nݺÅEh µëngg‡–-["##ƒK‹ˆˆ©ƒ‰‰‰Ôb°ŠŠ „††¢ÿþR¾è066Æ­[·¸4@€C‡AWWÇÇ‚ àáá+V4ªqqqÀo¿ý†Ý»wCOOD„ÔÔÔzÏ{ðàîß¿Ï>û ÚÚÚuÊÅÄÄ ¨¨ýúõ“ò=4hªªªÙ(å¡®¾ikk [[[.]YY]»v•ùCUUC† á¥õíÛx×Y½zõBxx8·èÒ¥KèÓ§úôéÃù‹Åb„‡‡£W¯^rûæ×EË–-áééÙ$}_yyy¤ý»êhÚÚÚ¼_||¼”Ü€x?xð™™™èÞ½»”¼——€êplz÷î+W®p!Ù¸6çüDÃÂÂàîî.×:Y\½z•••R÷¶P(„——JKKqóæMÞ1EEEôïß_®üããã‘››+ó¾y¡Ñ€êu 1119r¤”ÏóСCѼysÞ$¡ö5«o,óôô„Ì|jß³nnnPVV~å¾+鵯UïÞ½¡¤¤Ä=+jÒ·o_žÿ¾äü’’üóÏ?¯¤ªªªT›IƧáÇËLOOO—;ÿÌÌL 2†††8{ö¬ÔZ“òòr„‡‡ãèѣؽ{7‚‚‚`bb‚”””&Ô¦šøøxäåå¡_¿~Rër¼¼¼@D¼û¨#GŽÉK“5^õîÝQQQعs'ž?Þdÿ+0ú¡{÷% k§€¡¡!Ï`•hÞÞÞ¼ßèÑ£¼ÚC?55ÊÊÊpww—:&¹I3(ÕFVŒŒx¡…ÒÒÒxåÉÒ¡¦¾UUU¸xñ¢T{…B$÷`í—àíVû~066–Z<)k¼úê«¯àææ___ÁÝÝ'Ožd!`ë€Eáø@¨ý†ÞPzM$«~Ož<)u“Kx•U¶jjjÜŠêÚ«€%«Šå‰¦PòÔQò柟Ÿ/µâ»vx ‰á3aÂL›6­Þü$ˆÅbìܹ@õ¬BPPÜ›iÌœ9øí·ßàååÅ \»víœ9sê\Ñ-AÒ¦ …2’ÈIf'kR32@}Ô53[VVVç9¯Ò7›"+ ‰a ¨¨¨p3J*** åÚ .£²1¼ª¾¯JË–-¡¥¥…””ˆD"ÞLTÇŽѱcGÕñÛ%/j5QUU•Š Oÿ©yKÚ1,, <€¢¢"<==¡¦¦†fÍš!,,Œk'YF’¼4¥_Ë)൑DryÝHÚþóÏ?ǘ1cdÊÈŠT»^MË€7×UTTê Ç–ŸŸ%%%©YSYò’4y£ÕE}_š^å+”H$¨Q£’’‚àà`©Ùÿ¬¬,L˜0ÆÆÆ „7Aàèèˆììì&—-¹eõͺîy¯wûöíñ÷ß#66GÅéÓ§1fÌØÚÚ">>þ{ïÌ€þ ùŒ®¤¤„ž={6)ÉM[{ö¨ž Fhh(ÆŽË¥‹Åb\ºt o<¾³ä 44”ç6 I«‰¥¥%”””¸™ayøá‡Ž;wâĉX°`\]]áààPïyD„K—.¡{÷î˜>}:ïX`` \eK6dhÈÍF2+"÷÷âÅ‹<™ºÐÑÑÁ;w¤Ò%翯´k×ÆÆÆ ƒ††ÜÜܸ‡ˆ››ŸŸccc®=ëCEEEf_Ÿ3f öìÙƒ½{÷âóÏ?åüjöŸÚHÒj¾h›šš¢mÛ¶œÝ¥KÎÀóððà h]]]ΠU½FŒÁ;&o¿®šc‡$´ Y/¯É•——×ä1hÚX&/õùuaaaË—/#99™gT>}ú °··—2\CCC¥6j‘ŒÙ5ûÛûtOúúú⯿þÂd~¥¸~ý:ÊÊʰ|ùrtëÖKðàâââ¤fèS7I›„„„`Ñ¢E¼c’ûôUîprr‚““6lØ€•+Wâûï¿GXXúõë÷Jù~l°×‰ÿÆ ƒ––V¬X!s&Q¶>lll ¯¯/Óˆ“øÓðÒÿþûoäææbèСrÇÇm*ÞÞÞPRRÂÙ³gy3ºÑÑÑRŸË”””0nÜ8×iÖ|»ǪU«0jÔ(Ìž=ÇŽC³fÍ0f̘}»”••‘••ÅÓëéÓ§—«n­ZµB¿~ýpäÈÄÅÅI—äÛ¥K˜››#<<œ7[WVV†   hhh4èjii‰‡âÞ½{\Zqq±Ôµ}‘øä†‡‡ó\$~ÐW®\‘{öÙÃÃã•Ö¼ Ö®] }}},[¶ AAA2eúºQ“Ö­[ÃÅÅqqq¼mÅ«ªªpîÜ9(((ÀÛÛ›wNŸ>}àà`©6ŒŒDDDÄ+ûœ÷éÓ:::¸xñ"ï~ËÏÏGhh(Z·n 77·&çß¡CØØØ <<œ‹} T÷{Y>»¯MMM 6 §OŸFtt´Ôqúÿ±B2–ýõ×_Rñ¾%4u‡>mmmØÛÛ7ê>¼àÔÞGòwmßk ÚoºæxUZZŠ¿þú ÆÆÆ<7 ÄÇÇ×ùmñóÏ?ã×_ŲeË0yòd™2’/µÝgŽ;&S¾{÷îHOO—ÚÝRh×®"##‘““Ã¥¿|ù.\@³fÍ0pà@y«ÃCVŸ³³³ðfÜÿ>tØ ôCCClݺS§N…f̘sssûŒ›%HMMŹsç0jÔ(¬\¹Ïž=Ãøñãaff†ß~û @µ¡qðàA <¾¾¾Ø¿½º=;wîÄ€0|øpbÛ¶mpssCDD„\ÆÅŽ;àææWWWÌ™3ŽŽŽÈÏÏGxx8z÷î ___(++cãÆ=z4\]]1{öl¨¨¨`ß¾}HOOÇæÍ›¡§§Wo9³gÏÆ¹sçàêêŠ3f ??GŽA×®]ë|@¿/ôî݇Byy¹”1·lÙ2NF¼¼¼pḹ¹¡sçÎPPPÀ–-[^›®wïÞÅ×_ à>ðÿ÷ÿÇl_}õç‚RÚÚÚ8}ú4&OžŒÁƒ£W¯^èØ±#ÌÌÌ››‹èèh„„„ÀÔÔ´ÎMAjóóÏ?£_¿~ðôôÄüùó¡££DEEaÁ‚R r{÷î_ýÏŸ?—jó¯¾úŠûÿ« ¥¥…Õ«WcöìÙpwwÇôéÓ!‰°k×.a×®]rm˜TB¡ëׯÇðáÃáææ†Ï?ÿ¿þú+Úµk÷ÆSíØ±ÑÑÑðððÀŒ3àììŒŠŠ Ü½{øâ‹/äÚÚ]2–yyyaêÔ©pqqá'Ÿ={cÆŒÁÊ•+›¤£——6oÞŒ^½z¡C‡ÐÓÓÃòåË딟={6þïÿþË–-CVV\\\pûömlÛ¶ mÛ¶•š1ªg;%㕲²2öîÝ‹¬¬,›5kGGGèèè4Ø>@µ¡æèèUUU^›téÒ...R†µ©§M›SSS\¼xQQQ¼/4²äêYEGGG™‘1jSQQÁ['iIš<3QÀÿfåÖ®]‹ÈÈH_W{ãÎ;X¾|9‚ƒƒ±k×.(((ÀÜÜ àE011£££LÔÚcÙ¾}ûdŽe@õ×,Éõª¬v[¾|9ºté‚àà`DFFÖ;ÉTψGEEÁÏÏ.\ÀÎ;allŒ)S¦`Æ Rc,]ºÿþû/vïÞÔÔTØÙÙáäÉ“RQ#† †àà`sÑ_^¾|  z|”ì–+¡®qÅÀÀ€'jëîèèÈ[CÓ¼ysÞDDèСàìÙ³RywïÞ&L€ŽŽ.\¸€ `Ïž=8~ü8\\\ð÷ßcëÖ­RQ8<==†àà`\¾|eee\Ô¬¶mÛJ}!4h"##áçç‡ï¾û/_¾DûöíqôèQŒ7Ž'+kœª£"9::ò®éŒ3†S§NA$¡k×®˜:u*ÆŽ+sgËÿ:jÌ7>ÆGCEEÅ+-ì«ÊÊÊ7î²ÑµVÉ#/ ßø"‰×Õ6ò\?"‚X,–;ZHmÛ†Œ÷ƒ—/_69d\mÞÇ>PUUÅEÌy¼«:WVVBQQñ•C,¾­±LêïNœ8O?ýþù'Må}ìo¯Ê›®¡ªªêµ—ñ:Ç‘•«§2äæMÏÞ¹ñ  ÑƒÉÛ´_WÛÈsý$¡¬šÊÇö û¯ð:zïcx•>-ïªÎ¯klxŸ®Ycëô>éþºxÓuo¤ f<7Ì»Ee0 ƒÁ`0> ˜Í`0 ã­bkk ??¿WÚƒ€Áx—0hƒÁ`0 £°hƒÁ`0 £0šÁh€­[·~‰Ô䨱crÅ€gÏžaÁ‚ï}œç·ÅÓ§O{÷î½7;Ÿ1ÞD„¬¬,ܸqIIIR¡ÑêâÙ³g¸uë–Üá_•¢¢"ܾ}wîÜAEEE£Ï‰D¸ÿ>®_¿Žû÷ï7¸‘Ö«påʬX±b±ø•Á`¼ˆÁ`ÔIll, .-//ð~:::äììL¿üò ½|ù²Qeìß¿_*¿º~ýû÷—+ÏÉ“'“¼·wRR µk×6Jï×…¯Ž-Z´ :ÐÊ•+©°°ð­é±xñb²°°ÒeõêÕ$‰äÊ#++‹–,YBŸ|ò éêê3fÌÖ¼iøùùñꪨ¨H4jÔ(Š‹‹{cåVVV’ŸŸ¼±2šÂÑ£G©M›6¼6äèèHAAARò•••´|ùr222âä…B!ÙØØÐ™3gd–áââB***MÖñÉ“'4fÌ\™***4oÞ<*--•+RSSãÕSSS“¾ÿþ{¹ûù7hîܹ俿Fêêê€6oÞ,S6==èĉrדÁøøøbÆ0¯‘üöööèÛ·¯Ô±víÚá³Ï>üûï¿ ‚¯¯/¢££qàÀ¹ËèØ±#V¬XÁK;räîß¿???^ÀKK˦Uä=GKK ~~~€ 00+W®Ä_ý…Ë—/¿ñÐe@õF<={öÄœ9s`ee…þùÇŽÃÒ¥KQZZŠü±Á>ž·=rnn. €èèh <#FŒ€……n߾ݻwcøðáðõõÅŽ;^›ŽD„áÇ#** &LÀرcQPP€}ûöaÛ¶m(++ï¿þÚ`>b±óæÍC‡ ¥¥…›7o" +V¬@aa!6lØÐ`üñvìØKKKØÚÚâŸþ©SÖÌÌ ÞÞÞX½z5FýÊq®Œ÷†wmÁ3ï+‰‰‰$è·ß~ã¥Kf kÏçççS³fÍ%%%½RÙýû÷'”——פó?´h333^ZUU988€·6S+•öøñcRRR"UUUª¬¬l0‚‚ÊÊÊ"¢êÙB|3Ðááá¼ôü‘аaÃÞH¹eee€fÍšõFòo Ý»w'tåÊ™ÇËÊÊxûúúZµj•”lQQ}òÉ'€þøãÞ±W™NII!Ô±cG^zaa!µhÑ‚š7oNUUUMÊ;++‹”••IGGGnù‚‚"":~üx½3ÐDD‘‘‘oõ^f0ÞÌšÁ¨ƒÍ›7CUU&LK^SS“Û~6..7oÞ„——Î;'Sþ»ï¾ƒÜþ‡ñññ˜7oœœœ «« 2·nݪ󜊊 ,Z´:t€¹¹9&Mš„§OŸÊU„‡‡ÃÛÛfff077ÇðáÑ””$SnÑ¢EÜÇVVV˜ÜÝݱ}ûvœ†:vì(•ÖªU+xyy¡¼¼™™™ æ¡¡¡###y«)ÅÅ‹áåå…«W¯Ê<>þ|Œ3†«Sff&6n܈@GG&&&èÛ·/þøã&ëðÙgŸA(rí¥¥¥X¼x1:wî }}}ôèÑ›6m’òk-//Ç¡C‡0iÒ$ÃÀÀ:uÂ?üHNNÆÐ¡CTÏbzyyÁËËKjûá;vÀÅņ†† h£¢££aiiÉ{x6D~~>€ê‹Ÿ~úIJ.''ëׯG‹-äÞEê÷ßÇ¥K—àéé‰o¿ý...¸rå ÷¯,FމëׯcòäÉ6lN:ggg<~ü¸ÁòÖ¯_>}úàêÕ«ÄŽ; ¡¡@€ÒÒRôêÕ «W¯†±±1V­Z…Ï?ÿêêêo²………‹Å\»ÃÙÙ›6mB‡°xñbhiiaÑ¢E6lïÜ/¿ü“'OFQQæÎ‹o¾ùÎÎÎ8{ö, Y³fœ[ˆ®®.:v숎;ÂÎÎŽË㫯¾Âܹs¡®®???ØÛÛcÒ¤IØ»w/‚ƒƒQVVÆÉ&&&"88'ND`` <==1iÒ$((( ¨¨...X¾|9JKKáëë uuu¬[·½zõâŠæææÈÉÉÁ¡C‡|áºtéD"fÍšUçvÙVVVèÛ·/ñèÑ£F´~Ý´oß6668yò$o±bRR®_¿ŽAƒ5y÷¸‹/"..ÇcîR¸uë[LÈøxxÇ3à Æ{‰H$"òññ‘:V— Gbb")))‘‚‚¥§§Ñ7ß|C(11‘'ûóÏ?ŠŒŒ”Y¾,Žüü|)¹gÏž‘ŠŠ ¹ººòÒ%.<׃óçÏš9s&—&Ë…#>>žiÈ!¼E‘/^¼ -ZP×®]ye©©©Qvv¶”~ò|R–å‘Mfff€BCC‰ˆhÅŠ€,--©¨¨ˆ'¿hÑ"@ûöí“ÙçÏŸoPY8p€Ð_|Ñès›êÂ1tèPRSSã>‘KX½z5ÏÍ <<œÐöíÛ¥ò§Ýe¹pTTTÐçŸNh„ DD´téR@;wîä?sæL@§NâÒ´´´¨OŸ>õêSŸ GZZ)))‘§§'W¯r Þ|JJJ5j/]âpøða¹ô¨I||<¾øâ ˜™™aÍš5>¿©|öÙg(++ƒ¿¿?/ýàÁƒ°²²B÷îÝ&&&ªgækº—·Ý`ݺu˜2e F…–-[b÷îÝÐÕÕåê|ôèQ…BŒ;–wÞøñãðÛÖÄÄOžÉÉÉèׯš5k†àà`´hÑ¢Qç¿ ƒ ‚¾¾>/’Ëõë×q÷î]Lž<™KkÛ¶-† ‚­[·ÂÈÈÓ¦Mßþ)·Ï·„;wîàúõëHNNF÷îݱ|ùrܽ{&&&‰Dxøð!œ¡­­Í;OÒÞ5ÛvîܹHIIA«V­0dÈìÚµ ÅÅÅrërÿþ}À'Ÿ|"u¬_¿~užW»Oddd ¤¤"‘>>>¼þ;a¨ªªreI055Åúõëñøñc\¾|_|ñ^¼xyóæñÚ]2&4äß/9®««[¯œ¼ˆD"tèÐ;wîÄ¡C‡ðüùs<~ü?üðfΜ‰áÇ7*¿ääd¤§§ãÁƒøî»ï°lÙ2 <øµè* I»IÆVãC‡Ð † Ú´iõú/öèÑÉÉÉHJJÂÕ«WqøðaôîÝ[JnöìÙÈÍÍÅéÓ§{öìšš&NœØ(&L˜€uëÖaðàÁسg¢¢¢””Ä-¶“µé‡¬€Ö­[@½›DäççCEE†††R?Ìš5‹3¨,--ñðáClݺ°zõjXYYÁÇLJ7sW­[·Frr2’““ Œ3FfÈ+sssÞß’zHêU555èèè °°P.=€êÝ>}ú@$!$$ÖÖÖrŸû:PRRÂøñãqíÚ5n1ÜÁƒ! 1iÒ$žì¹sçWWWœ8qDûöí!wy‡Brr2âãã„U«VAGGPVV†ªª*™m«¤¤}}}^ÛΘ1©©©øüóÏqûömÌ™3ÆÆÆX¿~½\º¼|ù¤^„êJ“P»OH^&õôô`dd$õ=zt!ôÔÔÔ¸¨©©©PUUÅéÓ§¹uVVV e€×Fr\2–¼*—.]½{÷0eÊŒ?Í›7‡æÍ›‡Þ½{ãܹsxòäI£ó577Ç×_ 777\ºtI®õMA2–¾®ö`0Þ9ïØ…„Áx/©ªª"UUU™¡¼êò®//333rww§ììlRRR¢I“&Õ{Nm蜜@ýúõ“’µ±±!<H‰ïoMÿT ÖÖÖ$ 9ŸQY>ÐãÇ'¡P(åk,ééé4oÞ<@hP^–´,$>Ð5}`‰ˆÄb1©©©‘¡¡¡Ô9wîÜiÔµJKK#ÒÒÒ¢˜˜¹Î©‹W c÷ï¿ÿúöÛo©¼¼œ´´´¨oß¾õžSRRB'Ož¤-ZPÏž=,£®0vµÑÑÑ!)¿ê´´4@žžž2Ï‹ÅtíÚ5ruu%%%%zþü9•——×é½~ýz@{÷î•:fiiY§tí0s……… ÁûL Äó=ÿçŸ8¿ëºxüø1©¨¨P›6mH,sé¯â-¹^µCã­Y³†ÐñãÇ›”wÍ<už¼>Ðýïß¿ßdŒ÷ 6Í`È@(ÂÆÆ¦ÑŸþëÊkæÌ™ˆŒŒÄ×_ÊÊÊF»oHÜ-jF+ªWàK\)dQ{#‡"%%;w®×ß±W¯^‹ÅØ»wo£ôª7NX¹r%„B!‚ƒƒ}~cèÖ­ž>}ŠøøxÞ±¿þú ê OV“ÌÌLôî݆““S²D„ôôô76[סCtêÔ ‡F@@òóó1eÊ”zÏQWWǨQ£Ð§O\¾|¹I[<ËÂÅŹ¹¹Ráj[@WWWÌš5 •••\_TQQ••ÒÓӥΑ¸nÔךšÚ¨{±E‹\ôy®Q]îLùùùˆŒŒîKDçÎñé§ŸÖ¹aRUU¾þúkTTT`Á‚¯mãI˜€€©c’´š‘b ‘žž.×väD„€½½=—^^^Žôôt™îaåÞ½{PWWg3ÐŒ‡wmÁ3ï+³fÍ"UUU*))á¥7všˆèéÓ§¤¤¤DÈÞÞ¾AùÚ3Ф££CÚÚÚ@%%%tëÖ-rvv&:g [µjEûöí£¢¢"ºsç¹»»KÍbÉš®ªª¢nݺ‘ªª*­]»–|ø0}õÕWM………”““C§N"eeervvn0yg cbbH(’ƒƒ]¿~ èüù󤧧G:::”››KDD>¤Q£FÑ… èÉ“'T^^NW¯^%'''RTT¤””.Ïy󿑆†mÚ´‰ÂÂÂèÚµkܱQ£Fq×!00öîÝKmÛ¶%GGGÀE¸!ª{šˆèæÍ›¤  @666D/^¼ ¼¼<Љ‰¡ï¾ûŽ…£uëÖ4bÄ:yò$ÅÅÅѽ{÷hß¾}܆(µ£ñdff’¹¹9¡%<<œ222èìÙ³Ô³gO@ÞÞÞ¼Ùg¢êhEEE™ýkÿþýõF¨xôèikkS³fÍhÙ²eO‘‘‘4{öl@T^^ÎÉoÞ¼™Ð®]»¸´ÈÈH6l8q‚nÞ¼I7nÜ uëÖ‘‡‡ Ñ£GóÊ”Dz©ýµàÙ³gœÎ’{ýÓO?åÒ222¤ôo×®]_+Œf@3u’’BB¡÷"jšMD4zôh@Û¶mkPVV»ÀÀ@ÒÔÔäÂy úꫯ8CH–}óæMÒÒÒ"@@HQQQª>uíD˜››KS§NåÊ“ühÈ!œœÄà‘è€ÌÌÌháÂ…2Ã€ÕæuÐDÕ¡Ètuuyº:88PBBBƒyKÜ-êûÕ44ë3 ëËcþüù ê"!''‡”•• MŸ>]êøÑ£GIMM—óæÍiðàÁtïÞ½ó—×€&"ºpáéëëóÊjß¾=ݾ}›“yòä ðú‚P($)W¢ÄÄDš8q"'_Óý¦¢¢‚|}}¹¾Þ¶m[ÚµkN¯f¨µú h"¢ˆˆ²¶¶–ººººtôèQNnæÌ™Ü‹hÍŸ²²2M›6Mfx·‚‚š8q"þNòSRR¢Ÿ~úIæÎ•...õöýû÷×{"""¨}ûöRç¹»»ÓÝ»wy²² èØØXÒÓÓ“:_MMæÍ›'å²U—-ya­ëW{ÇAI>M 'É`¼ˆ¹d›Áø1vìXÄÅÅ!11‘û+‹‘™™ 55µF…ŽòññApp0²²² ¥¥U¯lvv6ÊÊÊ`jjÊ ç•››‹èèhtíÚÚÚÚÈËËCAALLL¸MrrrP\\ sss¼|ùÿüóòóóÑ¥Kèëëóʪ¬¬ÄãÇ¡­­Í •'áùóçHLLDvv6 agg===)}ããã‘›› '''n¡•<<|øÆÆÆõÊåçç#??ÆÆÆun>SZZŠ„„<|ø666°±±‘kcˆªª*<|ø°^CCCnS"BFF”””¤ØÉrM ¡¡Á-Г‡¬¬,¼|ùzzz2ÑUTT 66?ævÒ«v°>$ý¦f½ê£¬¬ ÈÌÌ„µµ5llld^‡¤¤$¤¥¥A$ÁÃÃC*zGMˆˆ[\& ËWóXAAw¯x{{#<<œÌ‹/PTT33³:]%ªªªpïÞ=ÎÕÉØØ;v”êUUU¸sç233QRRcccX[[7E£¬¬ ñññHMMž}ûpéÒ%üòË/˜3gŽ”ì“'Oêu­©ë:×Ö3>>iiiPVV†……lmm¥ä ‘›› ]]]^±XŒ¸¸8dff¢¢¢fff°±±‘¹³`yy9ž>}Š-ZðÚ¡¢¢¢Þ‹-[¶„ºº:÷···7222ÝŒ f@3õGGGnÛá¦ggg,^¼Xîˆ Æ‘‚‚©¹«W¯ÂÃÃcÆŒÁ±cÇÞ‘f S\\Œ>}úàŸþÁñãÇ_yÌ´´4XYYÁßß#GŽ|×ê0¯ f@3 °gÏaèС>÷âÅ‹ ÁéÓ§!‰’’Òà ƒñ_fݺu8räzöì ¤¤¤àÌ™3ÐÓÓCTT”TȺ÷/^àÈ‘#PVVÆŒ3êüZò_áÚµkˆˆˆÀ7ß|óÚT2ïÌ€f0Þ «V­ÂÁƒÑ£Gøúú¢k×®ïZ%ã½æöíÛØ³gâãã‘““ccc899aÉ’%ra0Œ7ÉGg@ÇÆÆâ?þ@bb" 1xð`©Í-¢¢¢ˆ¼¼ >Šh‰÷Ç‘€M›6!((ß}÷Ž?þÎw?ºrå ·h‘ñvÈÌÌ”9 0Þ¬Íß>¬Íß>¬ÍµùûDRR’TàŸbZ]]ššš°··G«V­¸ô‘#Gâûï¿G\\œœœÐªU+””” ##ƒŒ?>>ž;OII ÚÚÚ¸s篌ÄÄDà¶nnÕªÄb1îܹƒÎ;ó䌌Œ¤ÂâõïߟùF¿ETo!Ëx;°6û°6û°6gÔæcìS¦Ly×*¼÷|3Ð@uDŒ²²2^šäo@»ví€g‹Åb$&&rÇÀÒÒ’3˜%Hα´´äå/%W3/ƒÁ`0 ÆÇÅGc@OŸ>111HNNæÒŽ;EEEn÷·ÁƒCOOçÏŸçdBBBPVVÆ{Ûš>}:RRRšš  ÚEäÂ… èÔ©œœœnnnh×®Î;Çwûöm<|ø½¹1 ƒÁ`|Ä|.0qâDœ:u ŽŽŽ1bRRRƒ~øeeelÛ¶ “&MÂãÇahhŒ?½zõâååïïž={ÂÇÇ ˆEPP¯ÌmÛ¶ÁÇÇ}ûöE»vípúôixyyaäÈ‘oµî ƒÁ`0Œ·ÇG³‘ PAãСCˆŠŠ‚††† OOO)¹·½•·dFšù@3 ƒÁxßavKÃ|Tôû ëˆ ƒÁ`0>˜ÝÒ04ƒÁ`0 ƒñ6øh| ?töîÝËb.2 ©©‰­[·¾k5 ƒÁ`Ô›~O¸rå 3 ÿãQ£FÁÁÁ#GŽD||<'—‘‘±cǨö;v,ÆŽ‹iÓ¦Õ››6màèèˆÒÒRÞÌw¯^½xÆ3(**búô逄„„×UEƒÁ`0Ì€f¼7ܹsþþþøþûï1zôhäååA__¿ÿþ;ºuë†ÇTÿ{÷ÅÕ=püKG,ˆb¯ØE,Øcш±`{mQcŒ=–ägÉ›7&ÑÄ[b7±£h»‘ˆÆ¨{ ØPT,¡ˆXæ÷Ç «+ ¢ÀÂr>Ï3ÏîÎÜ™9» r¸{î½¶¶XXX`kk‹­­mŠ$øygÏžåÔ©StèÐ ‹—Æ“\?]¡B…7|gB!„0%RÂ!²;w@•*Uذa,\¸o¾ù†²e˲jÕ*V¯^M³fÍXµjUš×š8q"‰‰‰øûûãïïOïÞ½™={öKc¸víëÖ­ÃÅÅ…zõêeÔ[B!„ Zd;}úôÑ'Ï:tÀÜÜœK—.¥ûZ«V­âÉ“'<|øüùóÓ¢E Š/þÂs?~LïÞ½yüø1?þø#VVV龯B!L—”pˆl§[·n¯ííí©P¡÷ïßO÷µîÝ»GDDgΜaìØ± >œ®]»¦Ù>>>ž®]»ròäIV®\IÆ Ó}O!„B˜6éÙŽƒƒCŠ}hšöÚ׬]»6µkׯ××—]»vqÿþ}Š+fÐ&11‘^½z±wï^-ZD¿~ý^û~B!„0]Ò-r•îÝ»£Óé8zô¨Á~N‡‡‡ÞÞÞÌ;—aÆ)B!„Bdw’@‹«R¥Jú™9^Õ‘#G V?LJJÂÓÓ“M›61}útÆŒ“¡q !„´H ‡È±Þ}÷]Ö­[ÇúõëquuÅÊÊŠÊ•+süøqV¬XAŸ>}¨T©š¦áççÇîÝ»ñòò¢uëÖT¯^]#F°~ýzÞyçj׮͞={ îSµjUœœœ²úí !„"›’:‡HLü˜gì0þuÐØ0pà@®_¿ÎG}DLL  $""€5kÖ°|ùrƒöffftëÖÅ‹cnþôË—àë닯¯oŠû|ûí·Lœ81߉B!rI s€"EŠP±b’’V;½š5«¾Ñù_}õcÆŒ1èÙ4hnnn©ööz{{§Xü¤~ýúìÞ½›øøxõû6lHDDþþþ\¿~˜˜Ê•+GÍš5Ó¼v\\\š±–,YòuÞ¢B!L”$Ð9€‡‡Æ#C•-[–²eËì+Z´(E‹Mµ}µjÕÒ¼–µµ55kÖ4Ø—'OZµjE«V­^Ë‹®-„Bñ<D(„B!D:H-„B!D:H-„B!D:H-„B!D:H-„B!D:H-„B!D:H-„B!D:H-„B!D:ÈB*B!„oèðáÃ\½z5KïY³fMêÕ«—¥÷Š$ÐB!„ohÅŠx­[‡£•U–Üï~Bò‰$ÐF" tðË/¿0wútHJ2v(zU\\X³~½±ÃB!²:«tº,¹—›µu–ÜG¤NèàîÝ»_¼Èè„c‡ÀŸÀ¹¸¸7ºÆùóç¹sçNªÇlllxûí·صkãÆcÅŠ4kÖìî™Q=zÄñãÇ9qâæææ¸¸¸ÐªU+ìììÒ<'""Ú´iƒ¥¥üè !„9•üÏ!ŠZZ21›$Ðó€Uoxï¾ûŽÕ«W§z¬X±bÜ»wPÉj`` ÑÑÑoxÇ—Û»w/&L`Ñ¢E4iÒ$Õ6¾¾¾ôîÝ›ÐÐPÌÌÌÐ4 € &0}úô4¯=dȼ¼¼•L,X0ã߀B!²„$Ш6nÜH¹rå öYék©ˆˆΞ=Ë£GR=¾{÷n:uꄳ³3ÞÞÞÔ­[NÇ©S§xüøqš×õòòbÛ¶mT«V€€€Ì _!„YDhaTµkצZµj¯}~XXAAATªTé…½ºQQQܸqNGåʕɛ7oºî£i“'OÆÁÁ?ÿü“üùóëµhÑâ…ñ1‚‰'rîÜ9I …B ó@‹éÒ¥KÔ­[GGG4h€ƒƒÍ›7'((È ]PPM›6ÅÁÁZµjáêêJþüùéß¿?áááúv'N¤OŸ>´oß333ÌÌÌ0` J7Μ9È# ’ç—ùøã)\¸0Ÿ}öÙ›¿i!„™"::š;wîpñâEþüóOvìØÁ¼yóX´h3fÌ ,,ÌØ!ŠlFz EŽs÷î]7nŒ kÖ¬¡N:;vŒqãÆÑ°aC®\¹¢ïŽˆˆÀÉɉ &àääÄíÛ·ÙµkK–,!..ŽM›6ªFÙÚÚš¯¿þš3fаaCŠ/À¹sçhܸ1 .d÷îÝDDDP«V-ÆGåÊ•SĹ}ûv~þùg:„MV|4B!^`ݺuüôÓODFFòðáC"##‰ŒŒ$111Õö–––¬Y³†Â… gq¤"»“ZUÆ 177ü"ä§Ÿ~¢[·niž3eÊ¢¢¢X»v-îîL>11‘áÇ3}útý€¾ºuë²nÝ:ý¹5kÖ¤}ûöÜ¿ŸÍ›7LÙ²e©P¡ÎÎÎÔªU‹V­ZÜóÚµkÌš5‹ßÿ·Þz €%K–°zõj¼½½y÷Ýwõí>|ÈСC6lM›6}ÍOG!DFòðð €iÓ¦½´­™™sæÌÑ;)ij$FÕµkW ö999½ðœßÿKKKZ·nm°¿C‡8p Å9·oß& €{÷˜HÑ¢E ¤lÙ²/3¹ÜãàÁƒøûûë{¨?N“&M2dúžæqãÆaaaÁ·ß~ûÒk !„ÈfffL:•èèhæÏŸŸf;+++6oÞ¬ï¤ây’@ £š4iRº&&&rãÆ 5j”¢¹lÙ²T¯^Ý :::OOO¼½½IJJÂÆÆ†âÅ‹÷ï<Ö!!!¯tß%JЬY3}ò ª½yóæøùùqþüyêׯÏÞ½{Y¹r%;vìHW½´Bˆ¬ñõ×_³~ýzBCCSË›7/Û·oׯG Djd¡ÈQÌÍͱ´´Ls^èèèhƒiðÆŽËÖ­[™:u*ÄÄÄpãÆ }ÏCò<Î/SªT)@-‚ò¼wÞy@Ÿ¸oܸkkkfÍšE«V­ôÛáÇèØ±#;v|Åw,„"#9s†6mÚ¤š<ÛÛÛ³oß>IžÅKI-rsss*V¬È¹sç¸ÿ¾Á±K—.qëÖ-*T¨ ßçççGõêÕ™8q"U«VÅ€mÛ¶¥¸vrâÚ`’úõëêÞç%ï+]º4U«VÕ×H !„Èbbb?~<õë×çøñã)¦>-T¨¾¾¾i.¤%ij$9N·nÝÐ4 ƒý[¶l K—.ú}<|øN§ßÅÎ;S\·iÓ¦˜™™¥š$·lÙöîÝKLLŒ~LL {÷î¥`Á‚Ô¬YPSâùùù¥Ø’—"ß±c;vìxƒO@!DzøøøàììÌœ9sÐétôïߟ˗/S±bE@­€ëççG½zõŒ©È)¤Zä8Ÿ~ú)«W¯fäÈ‘S«V-Ž;Ƽyó¨^½:#FŒÐ·íܹ3³fÍ¢cÇŽôìÙ“'Ožðý÷ßS£F Ž=jpÝbÅŠáêêÊœ9s¸téeË–¥~ýúôèÑP3ptíÚ•òåËóᇰ|ùr¢££Y¼x1ùòå˺A!ÄKݾ}›Ñ£Gó믿êÂ%K–ègZ²³³£T©RøúúRµjU#F*rI sˆ+ññ4¶µ5vÜÓé(hþf_^”(Q‚ªU«¾t~ä PµjUƒä4þüœ:uŠQ£F±dÉBCC)^¼8ƒ bΜ9×üòË/‰eõêÕìÛ·*UªÐ¯_?Þ}÷]ú÷ï½½½Áý6oÞÌöíÛÙ»w/§NÂÌÌLŸ@»¹¹±oß>&MšÄ¼yóHHHÀÙÙ™e˖ѵk×—¾çÒ¥K”‘!„È<«W¯fÔ¨QDEEakkËäÉ“™0a‚Á8¼½½ Jÿ„xfÚ«Ž¢¯-y5»U«V½V›'N°ÿþŒì +VŒ; @ |YﯦiÄÆÆ¦{ ï´$$$ išÁ?ÄojãÆŒ3†{÷îeØ5…"·‰ŽŽføðá¬]»€¶mÛ²hÑ"*Uª”¢mhh(ŽŽŽrßÀêլʫ½œ›µ5uÆŽÕ¯{‘^%oÉí¤:¨_¿¾~›HéUJ'ÌÌÌ2,y5G¨BˆìåìÙ³ôêÕ‹ÀÀ@¬­­™1ccÆŒI³}F%Ï"÷‘Z!„9ÞâÅ‹7nqqqT¬X‘7Jç“È4’@ !„"Çzøð!ƒÖÏÄÔ«W/–-[FŒ™0e’@ !„"Gò÷÷§wïÞܸqƒ|GGGºvíJÆ S\ÿòåËlܸ‘   êׯπ2t¾a!„"7 ÆÃÃÇ0|øð«Î ‘ÕLfááÇùßÿþ‡···ÁlÐnïÞ½¼õÖ[=z&OžLçΉ׷IJJ¢W¯^Œ3sssÎ;G³fÍôSä$;~ü85bÏž=äË—éÓ§Ó¦M=z”%ïY!„0e[¶l¡N:>|˜B… ±uëV.\(ɳ0:“é(T¨ÇŽ{a›Ñ£GãææÆÖ­[2d®®®xyyÑ·o_vî܉——~~~´lÙPƒF»»;––êcûä“OpqqÁÏÏ ÆG5X±bãÆËÄw*„B˜®ØØXÆŽ˲eËhÞ¼9ëׯ§L™2FŽLÅdz _Å‘#G¸|ù2;vÔï«S§åÊ•3Xï}ÅŠØÛÛë“g€N:¾}û¸~ý:üñíÛ·ÇÂÂ'''jÕª%kÇ !„¯éÌ™3Ô¯_ŸeË–aaaÁ”)S8pà€$Ï"[1©:**Š.]ºÐ¶m[FŽI@@€ÁñË—/àììl°¿fÍšúcW¯^MQ7|Nr»äÇ5j¤¸Ö•+W2àÝ!„¹ÇãÇ™8q" 4àï¿ÿ¦L™28p€/¾øBßQ%Dva2%ÖÖÖ´nÝ®^½ÊªU«X¶l>>>¸¹¹¤ž@ïÛ·MÓ033ãþýû½Ï•*UÂÚÚZ]+..Žððp *¤ßŒ···AÛ.]ºdÀ;B!r¶ýû÷3tèP®]»@Ÿ>}X¸p!FŽ,wx>? ¦lÙ²FŠ&g0™zøðá >\ÿ:66–êÕ«óÑGqóæM#F&„È’’Ôfiÿì]¿Ç«ÇøxhÐ:t0vTB¤[hh(ãÇgÍš5”/_žÅ‹ë;¾„È®Là7Iêìì쎅GÀÚžû÷NˆW±víZÆGhh(Œ;–/¿üRV4‚çó“ç{¤EJ&›@DGGèçxNžp=µúÙÉØ+UªÄÁƒ ®uéÒ%ƒk$?^¼xwwwƒk=›P !Œ¤Q#øû璘ÍÌTïóõëjۺƎ…9sR?ÿøqxflDª²:yöóƒo¿0Þ~ììÀÉ)kãøñGõDóæpèPÖß_äXþþþLš4‰è ººº92!^É "L®INv÷î]V­ZEåÊ•õË#7iÒ„*Uª°cÇ}»3gÎpóæM  ß7xð`"## ’èíÛ·S²dIÞ}÷]*T¨@óæÍÙ½{7:€   Î;gp-!„‘ètP¾¼JOžT=ÒÃéÓðïÏ1sçÂúõ/¾Î´ipà@ê[÷î™þ6 ìÙ£[·†åË¡o_èÒj×ÎÚ8„x çÏŸÇÝÝÆsàÀòåËÇܹs9vì˜$Ï"Ç1™èúõëS¾|yªU«¦Ÿb.þü¬\¹Ò Ý‚ èÚµ+mÚ´¡J•*xyyáææF=ôm:tè@=èÒ¥ ={öäöíÛüöÛoüüóÏú9 fÏžM»víhÞ¼9õêÕÃÛÛ›Úµk3xðà,{ßBˆ4,^ -[Âó£÷ëÔ; iSøë/X´<<Ò¾Ž‹ ´j•©¡¾²7Ôã[o5 !ÒãÊ•+|ñÅlܸMÓ°´´ÄÓÓ“/¾øB¦¦9–É$ÐË—/ç?þàÖ­[TªT‰:0pà@ Úµk׎£Gê—òþæ›oR,åmnnÎ/¿ü¢_Ê»V­Z|ñÅ)–ònذ!þþþú¥¼'Nœ(Ky ‘]´nö1++•4ÿõœ9£j£Í3è ¹Í›!&*U‚fÍRoãç§’á|ùà™?ÞÓ´gÜ»÷´¤äüyxv¾ùþýŸÆÿÏ?°k\º!!ªÜ£jUhÜX•[¼ÈåËàåÁÁ Bñâê‘fÍÔµV¯õüþ}ÃX:wNYýð!ì߯b¿v *TPÌtê¤þ{'&FÓÊ–Uí¾ÿ>åqNÓÞ{O3ÆðXr4hZš–ö}„ÑkŸþ¹V²dI ÐÍÒÒRëÖ­›öçŸ;¼,%=й‹ô@ !r—Û·Õpÿùª×MM‰àæ¦zGK”€'O 0¾ÿ^M÷í·ªWö«¯Rž›/ŸêµmÔHõD·j¥æmÞ´ òäQ™1VÂÞ>ícUª@ÇŽª·ùèQÕóž,<\=–*•öù/ê¥OËÒ¥ªžÚÕFŽLyÜÜ\õ^ïÚ+WªYQRóå—2×t6’””Äž={X²d »víÒÏDU¦L>üðCL‰%Œ¥™Kh!Dî¡ S åÂ…i·õóK¹¯ukøðCè×~ùE%Ñ={ª™:žW«–*Q:FR}F ¤Þ>#ݻǎ©?îÝ{ºèJò,AA†í4€íÛU¼ ªÌ¢N7ãôiõøöÛi·©UKÍe©b-^³~ýÔ¬%C†H|ð@- ~á‚zo,$$DŸ,9r„Ó§OÿÜ R{{{Þzë-Ú·oOçÎ)ŸÖ·BˆTI-„0MÉ+ &%©$îE K;¢¢Ô*{+V¨×vv Ó©’dÃòå)¯5i?®JFÖ­3LÔ<Ì-[ª$üwTòšœœ`ôh5ÝÌ™ªç·n]•ôß»îîªldÆŒ”ç^¸ ê§?þXÕ‰;9©™:®_WÇóäµk Ï©PA%å+V¨^ö/¾xzìäɧóLOªþ˜øñG6 ÆŽUƒ­¬ÔÑaaªÝ[oeÌçËèt:Î;Ç‘#GøóÏ?9räˆ~йgU¬X‘&MšÐ´iSš4iB50™U„€$ÐBSÕ¢…ê ~vv†¯ûöUu·«rŒØX5‹EÙ²*Ñ:4õÄ÷òe8uJ%È“'«dôyMš¨äÖËK%ØíÛ§¼Z\\T2šÖŒ³g«’󿩏ÏS‹˜|ðL™¢’à–-Uïñ³¾ùFõÖÿù§J¸CBTIFùòðÞ{ª·<µ—Ë—«øýûÕ9QQjÿ³å#yò¨$ÛÃCÅuñ¢𘔤þ hØP 2|þótpP±+öjŸM.pïÞ=.]ºÄÅ‹¹xñ"—.]âÔ©SÄ<78ÔÚÚšzõê$ÌÅäs"C™iÚóó‰Œ6àßUÏV­ZeÔ8„Bd÷îÝÓ'ÈÏ&ËáÉ+F>§H‘"4iÒDŸ0ׯ_›,ŽZ 0V¯fUÝÏÍÚš:cÇ2}úô ¿¶ä-/'=ÐB!„ܽ{WŸ$?û˜V¢lccC•*U¨Q£ÎÎÎÔ¨Qƒš5kR¹rå,Ž\! ´B‘ ?~LpppŠíêÕ«\¼x‘ˆˆˆTϳ±±¡jÕª‰²³³3•*U’¥²…È&$B!ÒIÓ4îß¿Ÿj‚œ¼ýóÏ?/¼†­­mª‰rÅŠ%Q"›“Z!„xNZ½ÇÉÛ­[·xòì¬,i(Z´(eË–5Ø*T¨€³³3*TDYˆJh!„&M§ÓNXX¡¡¡/Ý –³NKž>þµ5M#666Ý ðãÇ3üýØØØ¤+vtt$ϳKµ !Ä’Z‘#ét:}ÏdTTTªÏ“¼ä¯ïŸý?µç/;žm“’’HHHxã„÷ÙGc¯—eiiI¾|ùô[Þ¼y ^¿ê±B… Q¸paòåËgÔ÷#„’@ !²¥ÄÄD®\¹Âùóç¹páçÏŸçï¿ÿ&""‚¨¨¨LéÙÌ ,--±¶¶ÆÊÊJÿøìó´mllÒ•ì>{LVÅB˜I …Fl(_¸p¿ÿþ›øøøžgeeEþüù ¶|ùò¼¶¶¶&)) PÃ’¥öüedz¢­¹¹ù+'µé}´²²2¸§Bˆ×# ´"Ëݺu |||8vì=Jµ]áÂ…qqq¡fÍš¸¸¸àââB©R¥ô‰²ôl !„0I …YâÌ™3lÛ¶ N:ep,oÞ¼Ô¨QCŸ$''ÍÅ‹7R´B!DÚ$BdŠ„„<¨OšƒƒƒõǬ¬¬hÕªîîî´k׎Š+JiBˆCh!D†òõõeÅŠìÞ½›ÈÈHýþ о}{ÜÝÝyï½÷°··7b”B!Äë“ZñÆt:^^^Ìœ9Ó <£T©RtîÜ™.]ºÐªU+Y˜B‘e,XÀ¢E?‘U³8Þ»w‹®Ys+‘ H-„xm±±±üôÓOÌ™3‡   òçÏÏàÁƒéÛ·/õêÕ“Ò !„Q„„„píÚ?Ê¢;NË¢ûˆì@h!Dº…††òÃ?°páBBCC(Y²$£GfèСRž!„ÈÌÍËc²èn?¡Yt/al’@ !^Ùõë×™3g?ýô“~!“5j0~üx<<<¤DC!D® ´â¥=zÄäÉ“Y²d :€V­Zñé§ŸÒ¾}{)ÓB¼±€€"##)]º4Å‹ÇÂÂÂØ! ‘&I …/´uëVFÍ;w077§gÏž|òÉ'4hÐÀØ¡ !LH¡B…hß¾=7nÜÀ‚bÅŠQºtiJ•*•â±hÑ¢899‘'Oc‡-r)I …©ºuë#GŽÄÇÇ€ºuë²lÙ2êÕ«gäÈ„¦¨hÑ¢ìÚµ‹úõëKHH!!!/<ÇÕÕ•Í›7S±bÅ,ŠRÅÜØ!²NǼyópvvÆÇÇ;;;fÏžÍñãÇ%yBdªêÕ«ãééùJm{÷Ÿ$ÏÂ($Bè:uŠF1vìX¢££qssãâÅ‹Œ?^ê…Y¢C‡/œÁÆÆ&E{{{{|}}e…S‘­I-D.qúôiÜÝݹu뎎ŽxyyѲeKc‡%„0a‡bàÀ\¿~€.]º0oÞ¼ö,çÍ›7«ÂâµÉ B!rÍ›7Ó¬Y3nݺE­Zµøë¯¿$yBdNÇ”)Shݺ5ׯ_§R¥JìÚµ‹_ýUÊ2„Ih!LÜúõëéß¿?IIItëÖ5kÖH"Óܾ}:Àˆ#øî»ïR-×"§’h!LØ–-[ðôô$))‰Ñ£Gãåå%ɳ"ÓøøøP»vm:DáÂ…ñööæ‡~äY˜I …0Q;vì OŸ>èt:†ÊüùóePŽ"SÄÍÍÍ›7ËÒÜBˆ @×®]  Ož<,]º”÷ßߨa ‘å$"‡‹‰‰¡[·n„……Ѻuk~ýõW¬­­–ÂÄlݺ•…““[·n¥N:ÆK£!r¸>ø€‹/R±bE¶lÙ‚­­­±CB˜NÇĉéÞ½;QQQ´oßž“'OJò,r5I …ÈÁæÌ™Ã/¿üBž#FŒàï¿ÿ¦E‹üüóÏí.\¸@ãÆñññ¡@Ì;—·ß~›ˆˆƒvãÇÇÃÃƒØØXBBBh×® ,ÈÜ7(rµ•+W²dÉ,--ùå—_(Uª”±C2I›7Ã[oAٲЫL˜ß|~Í›C½zðûïiŸ¿kxzBP 4n o¿ :dÝ{HM‡0}:œ;… AíÚP¥JÖÇqý:  œõ÷)]ºt‰-ZðÙgŸ‘@ß¾}9þ¼$ÏB¼ˆfb¾ùæ­I“&ZÆ 5wwwƒc¾¾¾ íÙ³G¿oèÐ¡š£££öäÉý¾÷Þ{OkР¯iš¦Ý½{Wsppоúê+}›‹/j€¶jÕ*ý¾)S¦h¶¶¶ZXX˜Á}===5OOÏŒ|›"ºzõªfkk«ÚìÙ³ŽIëÓGÓ@ÓòæÕ´¶m5mÔ(M›6MÓú÷×4[[uÌÚZÓþú+õóTm’7++õX @Ö¾gùù©,,4íìYãÅ¡išöçŸ*;;ãÆ‘ÛEDDhü±fii©Z¡B…´_~ùÅØae˜ &hÖÖí ~3w«¨yfÝÍ´vÖÖÚ„ 2峓¼ååLª: €éÓ§³bÅ ÌRù~uÕªUØÚÚÒ¶m[ý>wwwBCCÙ±c gíÞ½777ýWãÅ‹§~ýú¬X±ÂàZÉç'ëÔ©qqqlܸ13ÞžÈå† B\\îîîŒ?ÞØá˜´âÅaÚ4†}û`Á˜<V¯†K— dIˆW½Ì©qu…I“ÀË ‚‚àûï³6þÔªÇúõ¡V-ãÆ"Œ+))‰eË–Q¹reæÏŸObb"½{÷æüùóôìÙÓØá ‘#˜L tRR|ðãǧzõê©¶¹|ù2U«VÅÜüéß 5kÖÔ¸~ý:š¦Q£F ƒsùí·ßˆÇÚښ˗/Sºtiƒyw«U«†¹¹¹þZBd”Ÿ~ú‰ßÿ‚ ²xñbc‡còæÌIû˜“̘￯’éðpUñ¬Q£ _¿j½tfº_=–+÷ò¶ p÷.$&ªö¯~Ÿ°0u¯üù¡pa°³{½xS“”¤êÑ‹…|ù^ïü«WÕù¹uÊô?þøƒÑ£GsæÌ\]]™?>Í›77rdBä,&ÓýÃ?ɤI“Òl’"1.Uª„„„pÿßß2ÎÎÎí’Ï{ðàþZÉÉw2;;;Ê—/¯¿Ö³‚ƒƒñöö6Ø„x÷îÝã“O>`æÌ™”(QÂȉg§Ü¾~=ã®;g”/¯z°CCSo3r¤jӪĎüšS§ªöÉìØ¡^'o§N©ýÀ矫÷fc£çŠUìêªÎKKX|ú©JJUíwÙ²7¯ºÞ3_ÞáîÝ»«çÆR¾<¤öÞ¯¿Bƒ*i®XQÕ•Wª3gª¤øy>½^x¸ªWoÓFÅWµªúv ·¹uë½{÷¦E‹œ9sGGG–.]ʉ'$y)ò“àà`c‡”í™DtPPÿýïÙ·oŸÌH LΨQ£ˆˆˆ eË– <ØØáàßÎ;ÌÍUB–QFR ä_Aÿþ°s§aïõúõ°p!XY©v¯²j{ž<*q|òD½¶¶6ì}µü÷·À™3*Ù¶µU%NN ÓÁùóêX§N*ñüæÃë?y;±c*Ö TÒüä‰*9~û ’ÿ×ÍŸ_m÷î©×Ï÷[[?}®Ó©Á†É xÕªàì¬z¸OžTƒ;wï†={TÒŸ,) nÞTÏwî„!CÔsuMM{ùçf*®^½ÊìÙ³Y½z5qqqXZZ2|øp¾üòKY¹Tˆ7` ôÿû_ªU«Æ7¸qãaaaèt:6nÜHëÖ­)Z´(%K–äâÅ‹çÞ¹s‡ˆˆJ–, @±bÅ5*ùÙæäóŠ- @É’%9yò¤Áµbcc¹qãFª“Í—-[–.]ºd̹ƶmÛðòòÂÖÖ–eË–¥ZÛ/²–¦ÁÊ•êyÆ*Ì(VVðË/ªÇw÷nÕÃúïb“\¹Æ©çß|£föxãÇ«í«¯à‹/àÝwÕ=žW¼8,Z}û‚½ýÓýII*:fÏVußÏþÑà㣒g•,W®lxÝ  øû璘׭SS6mª’ûä?FR³x±ºw‰°j•Š=Y@të~~0w.Lœ˜ú5TÛ‚ê~ JTLÝ_ýÅÌ™3Ùºu+IÿvÓ·k׎9sæ¤ø†Uˆçóù–üåL¢„ÃÒÒ’0qâDýÌÅ‹™8q"×ÿý޵J•*êÿ18þ¼þ@… 033K‘h_ºt‰²eËbýo÷H•*U¸}û6>Ô·  ))I-!ÞDdd$ÇàóÏ?—ÿ¯²‰™3áÀUüÃ}''øñGõü¿ÿUÉæ“'j:½¨(5]fŒ!­UK%èÏ&Ï zÙ‡ QSø%$–c€šã G”Ésòûyï½ôǪJJ@ Þ|6y5÷O?©çÓ¦©Ï&5ÁòåO“gP¨˜ª½{÷Òºuk6lˆ——ÿùÏ8qâ{öì‘äYˆ b ôêÕ«õ½ÏÉ›««+ï¾û.7nÜ ñ¿]5 ..Žß~ûMî¶mÛptt¤cÇŽ€ª‰nß¾={öì!áßnŠ{÷îqâÄ ƒ¯Ï  ??ÙöíÛ±µµ¥wïÞ™ý–E.0aÂBBB¨U«ÿ÷ÿgìpª·ó¿ÿUÏ?ùDͺw‡#Ô ¾Þ½UïïéÓPº´J&³ê‹NÕYÇÅ=íñ¾vͰM‘"êqïÞ§eaÏUË\¡<3q’ÆUMtt´Йšçtš¢ÄÄD6lØ@:upssãÀØÚÚòÑGȦM›¨—Yÿ³ ‘K™D Ç«zûí·ñðð gÏžôêÕ‹{÷î±{÷nÖ¬Y£ïY˜1cmÚ´¡Y³f4jÔˆ;vP¡BFŽ©oãìì̸qã:t(~~~<~ü///æÌ™C¡ç‡ä ‘N‡bÙ²e˜››³bÅ ,-sÕj¶tü8tîü4©}¾8£}÷ê}>}Z•/XZÂÏ?«™-2Kx8,YÞÞpë–ª5~¾^8(Èðu÷îêŠÐPÕÛܬôì íÛ«äöu%O»gk«Ižü6))儸 J:5JyjÕ^?†ìîüùó¬_¿ž 6pëÖ- ,ÈðáÃ=z´¾$Q‘ñLö·òˆ#È›7¯Á>333Ö®]«_Ê»zõêLš4)ÅRÞ...;vL¿”÷رcS]Êû»ï¾£iÓ¦øúúâààÀÞ½{e)oñÆâââøðÃÑ41cÆÐ Ac‡”ë;nnªL sgX»V•6d&Õ{:hzݽ{æ.~ù²J@>Tå)5kª­`Aõ^ïÜÇÕòßÏ*QBÍr1dˆØ÷Çj¨^]ÍòÑGé› TÍ7¨žåW™5ãîÝÔ÷›Ú¤5ÁÁÁüüóϬ_¿^_‚êÛÓ±cÇ2dÈ¿«„Ïdè÷ß?Õýfffôë×~ýú½ðüòåËóÙgŸ½ô>ݺu£[·n¯£©™7o—/_¦|ùò|ýõׯ'× Tõ·ªWuóæ§3Wd¦»wŸ زEõH7i’9÷óðPÉs÷îªÚÑÑðøÚµ*NMݺpâ„JÂ÷ìQ ÷ïWƒGŒ€C‡RŸžîE’k±;w†/¿|yûǧÙèd…ððp6oÞÌúõë9|ø0Ú¿Ýîvvv¸»»ãááA»víä›*!²ü´ ‘<|ø™3g0}úôߢˆ¬uù2¼ý¶*ehÛ¶n5œf-³$%©„öŸToj`aŸ>jÖ ‡Œ½ßÇ*Xº4õ2‘äá©REm£G«’Žo¿UóOÿò |ö™êÑ~UÉSöߺuê¼úy¦âñãÇlß¾õë׳{÷ný˜ Ú´iC¿~ýèÒ¥ ù^gE!Ä“ZˆldæÌ™DDDàêê*KêÙ•+*y¾{W=nÛöjó.g„¯¿V3},¨zn‹5ãŨD>#%/cmú:]úïéè¨æ•^¾\•¾>M “ÿˆU‰öó½Ýðt€æ¹spö¬áâ5¦êÚµkìß¿Ÿýû÷³oß>=z¤?Ö°aC<<<èÝ»·~:U!„ñ˜À—[B˜†{÷î1þ|¾ùæ™óÙˆ®]SIsH4oÛ·Nƒ–™üüT ª×¹\9uïM›Ôª€¿þªSÉH•+«™=âãÕb-Ï›5 ž›ÙSïØ±´§;}úé±gga|v9ñãÇS?·iS5ϳN<]ŠüyIIª\$'zðà7ndðàÁ899Q©R%†Š——=¢råÊL™2…+W®àïïÏèÑ£%y"›h!²‰©S§KË–-qss3v8¹Ú‡ªAs zI;tH»íçŸÃóc‡OœPÓÜ%KࣖàNV±âÓ9ŸA•lxx¨¤qÄ•@&«^]Í;=hšºY³Œë•ÍŸ_-±íí­ì<©VŒŒT=ï[·ªúïÝ»Sž»`ZL¥gOU ]±¢úÌŽWõâ ÊP\\žžcg§>Ó;ÕyMš¨AÍw<®{áBU÷}æŒZ¼eØ0uB…Tiǵkê‹§+-fg111:tHßË|þüy}=3@¾|ùhÕªmÚ´¡M›6ÔH®cBd;’@ ‘ ±lÙ2¾ýö[#G#ââž>nÁÑ>ú(å¾ðp8x0å~ÎpDÄÓ皦Vù Q5¿ß}—òüÁ×Wõ÷ê¥b˨2ùE‹àöm•ü¯XñtÁ335¨°D‰ÔèbÅT¼råÓŸåæ–ú¬%?ü z¼ûíéªö99.^\õ|¯¦ò›>=åõ--¡uë×~Û™*22’Ó§Oë“æcÇŽék™A-Ö¨Q#}Âܸqc(D!?©BdÿûßÿHHH sçÎ)¦UYoáBÕûú*R[Ø­AUÃü2ÏŽÿŠ…ÿû?µU«¦¦±KÍÒ¥¼¦SLÌ«'ОžÐ¢…ª§NM‰ªÎzûvUwüä Ô¯¯–+/YÂÂÔ{²³3§OŸæÔ©SúÇäUpŸU£F }ÂܲeK™rNˆJh!ŒìÂ… lذsss¦M›fìpàêúfç;8–j¼Š¼y_íœWm÷¼rå kScn®J9ÜÝS+\8íû*¤¦›{… ¿Úâ0•*©­{÷—·µ´|½ÏèUݼyÓ Q>}ú4!!!)Ú™™™Q¹re7nLÛ¶myçw(ajS ‘KI-„‘}öÙg$%%ñþûïãòl¡¨¨ži–eÒEFZ½z5GÍÒ{º¹¹Ñ¥K—,½§âÅ$" $/ÛÝ·o_,-åÇîuišÆþýûY¼x1>>>ú3òçÏOûöíqww§C‡2@SdšppÍeQéÏA33 ,( ´ÙŒü&"“ÅÄİeËÞÿ}#G“3………±råJ–.]ÊÕ«W033ÃÍÍ¡C‡Ò¾}{¬­­¥È-Zj«²h¥H7ùÿZˆlIh!2Ù¯¿þJtt4ÎÎÎÔ«WÏØáä(GeñâÅlÚ´‰'OžàèèÈ Aƒøè£¨P¡‚‘#"‹Œ„Ç!o^x¹Ìà!!õcææ 3Ú& ´™,¹|£ÿþFŽ$gˆ‹‹cÕªU,^¼˜sçÎé÷7mÚ”aÆѣGlllŒ¡9ØÂ…pèüõ©}£FÁ‚é¿V«VpáBêÇ †ÐÐ×SˆìNh!2Ñ;wðõõÅÜÜc‡“­ÅÇdzbÅ ¦M›FHH j›ûõëǰaÃd !2Â7ß@H88@… pýú›]ÏÌ † I¹?_¾7»®Ùœ$ÐBd¢õëד””Ä;ï¼#³A¤!11‘U«V1uêTnÞ¼ @µjÕ3f ä“_ÄBdœ+ Z5pr‚åËSO~ÓÃÜ–,ɘ؄ÈA$"IùFÚt:6làË/¿äÚµkTªT‰ÿýïxxx`nnnä…0AíÛç¾ÂêÕ`m­’v ÃãÂéÓP·.´haœ…HI …È$§NââÅ‹äÍ›—îÝ»;œlCÓ46mÚÄ”)S \¹r|þùçxzzÊ4Bä4‰‰ðÏ?ªî9­YC „sçà§Ÿàî]øê«§Ç®\ŽÁÖΞ͚˜…xCÒÅ#D&Y»v-ݺu#oÞ¼FŽ&{øõ×_©]»6½{÷& €R¥J±páB._¾Ì| ɳ9N§’ã’%ÁÎ*V„ï¾K}vŽï¿gg˜6 |}Õ¾øxèÕ bb`Íu!rùm%D&ùõ×_ðôô4r$ÆȰaÃ8pàÅŠcâĉ :[[[#G'„xm ‚‹‹4xý:ìÛŸ|Û¶ŸŸª‘Nfg›6AƒЯœ9£5ž> Ÿ~j¼ò!^C¶H u:¾¾¾œ={– .píÚ5Ê”)ƒ‹‹ ...¼ûî»äÉ“ÇØa ñÊÎ;ÇÍ›7)X° -[¶4v8FÇ´iÓ˜9s&ñññØÛÛ3yòdFމ±ÃB¼ _ß”s=A»vðÇjj¼1c ר¡z¢VÓà@ãÆªWZˆĨ%ÉÓVU­Z•víÚ1kÖ,®\¹B©R¥ añâÅtíÚ•òåË3cÆ ¢¢¢Œ®¯ÌÇÇ€öíÛçÚ²„½{÷âââÂÔ©S‰§OŸ>ðÿ÷’< a R[(ÅÉééœÒÿ®ÀšÂ@×®*yΟ6n+«Ì‹SˆL`´ßì‘‘‘¸¸¸'OýtU)Ú=~üæÌ™ÃŒ3øã?¨Q£†"âÕmÛ¶ €Î;9’¬w÷î]ÆŒæM›¨\¹2 .¤mÛ¶FŽL‘%š5SÿúñÐP8~\=‰Q¥åÊeMlBd£õ@›››³hÑ"9rdªÉ3@žÿ\-”2iRÊk}ö™šênéRµï£à?ÿ={Tr-D‘mF75jÔˆqãÆ1hÐ ºvíÊŽ;°²²bÙ²eüõ×_FŽPˆ—‹‰‰á÷ßL?¾sçýúõÃÏÏ€Áƒ3kÖ, ,hÜÀr‘˜¥÷tuu¥Q£FÿÏÞ}†GUíoÿNBï¤!Ô B‚4)RƒQ=r@,t=)GxñJEB/RT@D0Ô:„z I&ó¼ØÌ!…@23™Éý¹®¹&Ù³÷^+!Æ;k~k-§¶)™èý÷ÍL’úî;ãÆsÖúd0J.îÜ1I?Æ__c-èóç×òä)SàÙgïù²1êl2Á¼yP À½×¾þvì0úÖ¨èçKÜ@–б±±ìÝ»—-ZÅòåËùõ×_ñ÷÷§V­Z9r„òåË»¸§"i[³f ±±±Ô¨Qƒråʹº;³lÙ2úôéÃÅ‹)\¸0ß|ó :uru·²o¿ý–Eß}GI'­`p*>žC†(@»³ téÔ_/UÊþóÒ¥aÔ((VÌþø+¯@áÂÆ¨rt´1úܲ%T¬h¬²qÿ†(»wCïÞÆHvíÚö¯,‹ýû Å-d‰m]žÎ:‘pîܹT¬X‘çž{0jŸýõWhÉò<½|ãÎ; :”©S§РA¾ÿþ{Ê”)ãâže_!f3³Ìf§´Õ*µmšÅ}´oo<Òë‰'`ôèäÇúö5éѤ‰ñHMÍšÆCÄMd‰èbÅŠQ®\9þúë/æÍ›G—.]l¯›L&nܸáªî‰¤Kbb"Ë—/<3@8p€zõê1uêT¼¼¼øàƒX¿~½Â³ˆˆd;Y"@ƒ±áDûöíiܸ1‘‘‘¼ñÆ€1™ð¯¿þâÉ'ŸtqEÒ¶yóf.\¸@É’%©S§Ž«»“©¦OŸNíÚµÙ½{7O<ñ¿ýöcÆŒÁÛÛÛÕ]qº, §NÊ´iÓ`Íš5<ñÄDDD@ëÂì"YÔ/¿üK¸™>žþýû3}út†Ê„ ðòÊ2óŒEDD²Œ,1‰°nݺԭ[0ÖÒŽŽ¦D‰äpÒîZ"qãÆ ÛVóÖÍÜÉ¥K—xñÅY¿~=9rä`Ú´iôë×ÏÕÝ‘Œ°X`ÎØ²ÅØbÛd‚ \Ý+‘%tR^^^¶%ìDÜÁÆIHH råÊ”,YÒÕÝy(‡¢M›6:tˆ"Eа`Áš¤µ[˜ˆ¸‡¾}aÖ,ðõ…Ê•ÁR&@\|ðãÛq±, EÜ»–oìÙ³‡æÍ›sîÜ9X¾|9+Vtu·D$£¢£ð\­lÛΚÇ0m\¿®-Ù‚ E2È Ý©|cçÎ4iÒ„sçÎѰaC¶nݪð,â)Ž3ž6t^xÉf4-’W¯^寿þÂd2¹MéÃÖ­[iݺ5W®\¡Y³f,]ºTk«‹xŠÏ>ƒŒ„É“ëׇºuÚè;`íZ#h߸åÊa;µÉúgÏ#ÚQQpí*•*A»v11ðý÷Ækqq÷Ú£|¤U+ûû­Xüaܯti£oíÚ%o7, N‚þÓèëš5ÆuÏ<fìû$’A Ð"°iÓ&Ìf35jÔà±ÇsuwhÆ ´mÛ–ëׯӺuk.\H®\¹\Ý-É,ï½g„X€ ŒI„£GzÚ40À˜TX²$äÌ ?ýãÇCHÌŸÞÞ÷îM›Bl¬QO]®œ˜/_†añb8}FŒ€;wŒkFŒ¸wýK/Ý Ð/µÙK—÷*S.4j§;u2&=&ýc~êTãk¨XºuƒÛ·õøxhq9•pˆdÀŸþ @pp°‹{ò`¿üò ­[·æúõëtìØ‘Å‹+<‹xš7àçŸß~Û¾±±÷Bm@ÌžmŒŸ> GŽ5Ó;âEðÕWö÷{çãúyóŒúæƒáÒ%ˆŒ„^½ŒsjÕ2Î)S ¾×fl¬1rmõÚkFxþ¿ÿ3Ú?t®^…¡Cïé”tï}d\síš1Ê.âbY*@ÿøãQ°`AF ÀîÝ»éÖ­f³ÙµIÁ_ýÀSO=å➤mÅŠ´mÛ–[·nѵkWæÏŸ¯¯¯«»%"ÎÖ¼9ôì ùòÝ;V¼¸1ú[ Ìœi~d$”- ]»£ÆV+#ÖéõÛoF@ïÞÆŽ5F¾ÁXäã¡N˜8Ñøà~¡¡ðÖ[÷ê¹ÝàÝ>ñ|Y&@öÙgôîÝ›R¥JÙm\¥J~ùå6oÞìÂÞ‰¤ÌôÂ… áÎ;ôìÙ“¹sçâã£ê-‘líÔ)øåc„øË/çÂ…áèQûóÚ¶…ãÇ•5îía¬]k<¿ürò×L&#Øß¾}¯~;©=½]É2ÿý÷¿ÿ͘1c>|8ï¾û®í¸¯¯/ìÝ»7Ím¼÷íÛÇÊ•+9xð 4kÖŒV­Z¥X—º{÷nÂÂÂˆŽŽ&88˜^½z%Û´%11‘9sæ°iÓ&{ì1BBBl›½$É?þÈÑ£G©S§½{÷ÖÖÇÙÄ¥K—8~ü8ÞÞÞºº;)úá‡xõÕWIHH 44”ÿýïÛ]ðÖ­[ÄYk/$W®\*Cy.Ü+¥HLLù³ù^ô[oÁÖ­ðïš5á…Œ‘ä‡ù½g Æ/¿ )ý²ÖOGE£ÑIioÉ‚²ÄôÅ‹¹pá]»vHö?x‹Å”ÞÖIbêÔ©Ìœ9“›7oϰaÃ(Q¢3f̰;oõêÕNΜ9yï½÷hß¾½ÝÿøéÖ­o¿ý6^^^üý÷ß4hЀ÷íâ´}ûvêիǪU«È—/&L yóæ\»v-#ßq»ví råÊäÎÛŽInÅŠ¶ðž1z÷6v*üúkxòI£<äØ1cÔºpᇫ«–Ls>9qâeÊ”qQoÜC– ÐVÏ?ÿ<Ï?ÿü#]›3gNÂÃɉ‰aÅŠ|ôÑG\¿~?ü0“{)Ù]DDf³™råÊQ¸paWwǶ³`dd$+VäçŸ&þü®î–ˆ¸BùòƤ¼úõ“¿6t(T©?üçεÉ~hLœ9ÓXNÎú®U°i¬_{÷™3ƈsǎЯŸqmRO=‡ÃêÕFо}Û~„ÚÏÏý^¸Ð¸ïþýÆñÚµ!(È¥NªOxî¹{kF‹d!Y.@ÇÄİ}ûvâããíŽ×¯_ŸâÅ‹§y­Éd"((0j–óçÏϧŸ~ÊðáÃÉŸ??~~~€±äõ<0ÂPÉ’%mµ×%J” ""ÂîÞQQQÄÅÅÙî‘ô^+V´»W®\¹ìFŸÊ”)£g’•Ê7âââ áÏ?ÿÄÏÏ5kÖP¬X1WwKD\Åßߨº;5mÛûõécÿ¹ÉdŒT?ûlúÛ.VìÁë6wêd<¤wïô·ërqÀ%v›€ ²`ÀJÃýùäþiI.ËG®Y³†gžy†%Jжm[BBBìÖÀò0‚ƒƒ‰eß¾}Ø>·Šˆˆ°½F¹Æýúþ{XŸ÷îÝ›ì^Iµx¦;w®Љ‰‰ôèу_ý•B… ±jÕ*Ê•+çÒ>‰ˆã:tˆI“&%[VU\Í(L ÐÈ4¾RYÛDÜ\–Ћ…W_}•¢E‹²víZbbb¸råŠÝ£Y³fiÞãäÉ“vŸ'$$ðù矓3gNÛD¿úõëÀòåËmçíÚµ‹ãÇÓ;É_º¡¡¡\½z•ßÿÝvlÙ²eøùùÑ¢E Ê—/OÆ ùùçŸm+}=z”¿ÿþÛî^♲ÊôСC™?>¹sçfÙ²eYvCy4ŒzÔ7ß|???8pàÒ3‚+Nf¢Ð ãßíW`8ƪA%€>À"àªËú'™)K¼ÃpíÚ5Î;Ǹqãxúé§éõêÕÃßߟJ•*qþüy6lØÀíÛ·ùòË/É—/Ÿí¼Ï>ûŒš7oN@@aaa´jÕŠÎ;ÛÎiÓ¦ ;w¦cÇŽtíÚ•S§N±víZ~øá»-'NœHË–-iذ!µk×fñâÅÔ¬Y“ÐÐÐGÿfH–—Àž={×èÙ³góßÿþ///~üñGX—‰·vX Ì~‹Ã{ÁÛ@Í+¯¼Â—_~i+9”¬Å X„ëëp7ÙÀbàtZõïdw’¬.Kè‚ R½zu"""9@/X°€µk×røðaŠ/ÎÈ‘#éÞ½{²·³[¶lIxx¸m+ïqãÆ%ÛÊÛËË‹yóæÙ¶ò®Q££FJ¶•wݺuÙ¶m›m+ï#Fh+ïl`ÿþýÄÆÆR¬X1J•*å’>üñÇüãÿàÃ?¤ýý“oDÄ­DKH 'pýîkæ¸8òäÉC›6m˜={¶Ã³ÅbÁl6“˜˜ˆÙl¶{¤tÌQçfV[7n$!!ø`ï>'}¤tìaÎMzìóUwÆ&ŒÑhî³®½çîcÆ¿u#ŒÐÝ (›Ùÿ°âY"@L›6ÁƒãííM›6mzeƒàà`‚ƒƒÓunÍš5©Y³fšçxyyÑ«W/zõê•æy|ðÁé?W—oœ={–bccéÖ­#GŽtI?DäÑÅbŒPþ„š0‚–u미$çšL&òæÍË… hÚ´©ÃB­%éÞå+§µtëî#½,@. 8à<æˆN‰Cd™]½zu .LÏž=S|ý矦U«VNî•Hr® ÐqqqtêÔ‰Ó§OóÔSO1cÆ §÷AD2f-0ØÎó- 111¬[·Î½J™ÉdÂÛÛ;ÙÃËË+ÅãsŽ£__½z5›6ÅlþF‘Äý¯Tާ÷õûÏiE'Nð)ð%0㥤ò`ü±TèŠ1ê\ÛÿnâxY&@‡„„pèÐ!HÍš5íJ*jԨᢞ‰Øse€îß¿?ááá/^œÅ‹“'O§÷AD2æy`pãíþÏî~ì~¡:ã IDAT…®Ì)\“'O>úè#Ê—/ïÔpêεÖW¯^%<|fó;Nj1ù1ÞUø #<{y;À³@w -PÒI=ÇÉúúõëlܸ‘ СCWwG$U‹…]»vÎÐS§NeÆŒäÈ‘ƒ°°0í%âæÊÿ¼ûH¶?c¬ÔpÈͽ:èÛ·o3jÔ(Ö¯_ïòÕ$u{o1‚sa Ðh†Qª!ž#K,c—/_> .œ%vtIËÑ£G¹zõ*ùòå£B… Nkwݺu¼û¤6lè´¶EÄñ|0Vcø7Æä²`&àçåEžùä®\¹òà D\ÄZ¾Q³fM¼¼œóŸÏÑ£GéÒ¥ ¼ñƶÕ7DÄs^}|xóÍ7‰ˆˆ`Ê”)Bxx¸«»(v¼´FzÀóe‰€={ö°aÊ+Fýúõ“F5Jo[‰Ë9»þ9..ŽÎ;sñâE5jÄgŸ}æ”vE$k©X±"+VdÀ€$$$°ÿ~WwIìX'Jv‘e4ÀsÏ=çê.ˆ¤ÉÙzĈìܹ“’%K–lr­ˆd?>>>ÚuTÄŲL€ž9s¦«» ò@Î Ð+W®dòäÉxyyñÝwßQ¬X1‡·)"""–%j EÜÁ¹sç8sæ 9rä Zµjm+::š^½za±X1bM›6uh{"""’~.¾técÇŽåÙgŸ¥K—.|ðÁ\»v-Õóßxã *W®ìÄŠØÛ·o•*UÂ×××aí$&&Ò³gO.\¸@pp0cÆŒqX["""òð\ ïܹÃÖ­[ñóóàÏ?ÿäâÅ‹©žÿÒK/9«k"):~ü8åË—wh;ãÇç·ß~£`Á‚Ì;Ÿ,Si%"""¸0@—,Y’Ï?ÿÜ6)jÅŠ®êŠHº;v €råÊ9¬-[¶0zôh¾þúk‡¶%"""Æ¥5Ð'NdÚ´i®ì‚Hº9:@_¹r…îÝ»“@hh(]ºtqH;"""’1šD(’NŽС¡¡œ8q‚*Uª0eʇ´!"""§-’NŽ ÐÓ§OgÁ‚äÊ•‹yóæ‘'OžLoCDDD2‡Ëg'EDD0yòäžBÙ²eÐ#‘ä8uêùúôéÓ <€ÿüç?Ú ADD$‹sy€Þ²e [¶lyày•+WV€—9uêf³™ $Ûf>£þñpõêU5jÄÀ3õÞ"""’ù\ ûôéÃÿûßž—/_>'ôF$eŽ*ßøî»ïX±byòäaúôé˜L¦L½¿ˆˆˆd>—h___ *äênˆ¤Éúüùó¼ýöÛ|øá‡T¨P!Óî-"""Ž£I„"éàˆ=`À.^¼Hpp0o½õV¦ÝWDDDË¥:oÞ¼Zm@ÜBfè FΜ9™1c^^ú[VDDÄ]¸´„cúôé®l^$Ý23@_ºt‰0zôh*W®œá{ŠˆˆˆóhØK$Ž?dN€~ë­·8wîµk×fèС¾Ÿˆˆˆ8—´È˜ÍfÛÐþþþº×Ê•+ùî»ïÈ‘#3gÎÄÛÛ;3º("""N¤-ò§N"!!‚ fhŘøøxÛªï½÷ž6LqS Ð"YõÏS§NåСC”-[–#Fd¼c"""â Ð"úÂ… üûßÿŒíºsåÊ• =WP€y€ÌУFâÊ•+<ûì³tíÚ5s:&""".¡-ò Ðûöíãÿû&“‰É“'g^ÇDDDÄ% E £úÝwßÅl6Ó³gOêÔ©“y—P€y€Œè•+W²zõjòæÍ˸qã2·c"""â Ð"iHºôÃè„„ À°aÃ(UªTfwODDD\@Z$ YzÚ´i8p€Ò¥KkÇA¢-’†G-߸|ù2£G`„ äÎ;s;&""".£-’†G ÐcÆŒáÒ¥Kѽ{÷Ìˆˆ¸Œ´HŽ= @Ù²eÓ}ÍÁƒùâ‹/0™LLš4 “Éä¨î‰ˆˆˆ (@‹¤áüùó<öØcé¾fÈ!ÄÇÇÓ½{w‚‚‚Õ5qh‘4\¼x€¢E‹¦ëüµkײ|ùrrçÎÍ„ Ù5qh‘4ÌgŸ} eëDDD²h‘TXGŸsåÊEž`ذax¥w˜FD$ xþyûðlåç]ºÿùgúï×¼9T¬&ӃϽx&O6Fº“¿¾nñú¦Méo_Ä•4-’‚ÄÄD®\¹Ü Ð?ýôGŽá‰'ž gÏž®ìžˆH¦Ê—ÏxÎèdÂÔ-j„óテ˜ø¿ÿ»÷Úж-äÏ»v9¦}‘̦!4‘\¾|™ÄÄD¼½½)T¨&L`ðàÁäȑÕÝÉ47nõÑ&4jä¸v¦M3FÀÇŒ Œc±±Ð­›ñ>žo¿ý–ððpüüüèܹ35kÖLv¯={ö0þ|Nž¿þúëy˜]DD²¨o¿…þýeè~ýÕ¹µÇçÏ›¯€±ƒá±cÎk[$³xÌô¬Y³xá…ðñ1¾¤Ó§OS¾|yÞÿ}-Z[¾4ˆW^y…™3gЧO‚‚‚X±bíÚµ ,,ŒU«Vñ×_Q«V-:uêÄ Aƒ8xð ­Í·Þz‹F±bÅ L&  FÌ;—¾}û:óË—Lvùòe, +W®Ä××—Áƒ»¸W""7w.ôí ¥J©”)ã¼¶-èÑΞ…¡Cá³ÏŒzèÝ»nDWó˜èöíÛÛÂ3@©R¥hÖ¬QQQ¶ckÖ¬!::Ú”êÕ«G±bÅøæ›olÇfÍšEÙ²emá mÛ¶DFF²eËvîÜIDDmÛ¶Åtw̪U«R¡BfÍšå¨/SœäÖ­[ìß¿€W_}???WvID$ÃæÏ76O)QÂÏåË;·ýñãr‘ž=mÅ?ùĘÀØ»·sû!’Q ïÏž={ ´‹ŒŒŒ ›TÕªUí‚vdd¤ÝuI¯±ÞÃú\í¾)˶×Ä}ݾ}€Ã‡ãååŰaÃ\Ü#‘ŒYº^~|}áÿý?0›5˜“>Ξµ¿fãFÈ• êÖM~¿˜˜{×>m»víÞ±#GìÏß´ >øÀXÊî‹/Œco¾ ;²eÆF*"îÂcJ8î7|øpbbb=z´íXtt4¾¾¾T¨PÁîÜjÕª1oÞ<ÀxËþÌ™3¼üòËvçXttt´Ýóýa<00… ’`7"~âÄ /^lwnÇŽ3ðŠ#Y´Åb¡K—.T¬XÑÅ=ɘùó!!Áx¤¶ív¿~ä YáΈ‹K~îĉÆ(rR+V0¶·†è‹R c)»¤gÌ€;aøphØj×~ô¯QÍýùäĉ”qfmòÈ=mÚ4¦L™Â¬Y³puwÄ Ý¸qÃöñ›o¾éžˆˆdŽNàÉ'Ó>çé§í?/WFJy’áóÏCîÜ©ß+鮆»wá¼Nc)»ûÏ[´È!ß·OZ܃ÇèÙ³g3pà@>ÿüódÛ-ûùùGTT•+W¶ß»w/%J”Àd2Q²dI"""쮵®æa­ƒµ>ïÛ·ÆÛ΋ˆˆ X±bv£Ïeʔш³±Ö>—(Q‚ ¸¸7""b<FÙ²ä\;Í›ôhÚÔx¤æé§“‡wqžûóÉý#Ò’œGÕ@Ï›7¾}û2iÒ$ú÷ïŸìuëhôýKÛíÛ·Ï®¬# Õm½‡õyïÞ½vçEDDhÔÛX´Â³ˆˆˆÜÏcô¢E‹èÑ£&L`РA)žÓ¢E üüüX¶l™íضmÛˆ‰‰!44Ôv¬wïÞ?~œ]»vÙŽ-_¾œ€€êׯÀÓO?M`` Ë—/Çb±FÈŽŠŠ¢·¦»µÈÈHbbbhÒ¤‰k;#"""YŽÇ”ptëÖ Â ³ÏŸ??k×®ÀÇLJÏ>ûŒîÝ»sþüyJ—.MXX;w¦M›6¶k:wîÌœ9shÑ¢;w¶-_g]OÚjÊ”)´k׎¦M›RµjU,X@“&M’M@÷’tIÃâÅ‹»°'"""’yL€;vlŠÇsåÊe÷ù‹/¾È¦M›l[yúé§É¶òöõõeéÒ¥¶­¼ƒƒƒùôÓO“måýÜsϱuëVÛVÞcÆŒÑVÞnκ…»Už0&¶úgM wb±ÀˆFHX·6n4>7ÎÐS§ÂÛoƒÉ~~#‡~ǃ.]Œ“lÌËÆðüópçŽQO]¶¬1)ñʇŸ~‚“'vïÜ1®Iº¯YÏž÷tL ôî +W“Ë”… aüx£íÙ³!éæÁ“'“ Œ×cc!o^£ï Ðâj*álmíÚµ?~œbÅŠÑ¡CM ·d2!sÉãóaÃŒÏccáÝwc•+ÃwßÁõëpê= ÑÑб#ÌŸw#²yûm#/\h\sð \¾ À+¯ç<óŒÑÆãC‰÷ÚŒ…»{SЧžÇŒ1îuèÄßyÇhû“ORþººw‡?†k׌Çĉ™û}y Ð’­YwìÕ«9rä EÄsµliß¼yï+Q˜t˜7/Ìšeþ¡CP±"„„£ÕV•*ÁÝ1‡tY½V¬0VùàcŒçO?5&<~ü±ºï׿? x¯Ï=–þvEE%’m?ž¥K—j;nD¨-"žêäIc4ùäÉ{¥… #ÒI½ð‚Q‹>žFQ©R%Ûq@‹ˆ§:w^{ –/7j¦ïçußûÒï¾kLÜ5ÊxÔªeLøëÞªUK»Ï;'oî…øC‡’èÒ¥Óߎˆ³(@K¶åIi¡ˆxª.]Œ‰#F@ðÄP €1 \«üý·ýùuë£ÂK—;®YcŒO˜`<† I_»ÖÒŒþý‘îÔT©’üXÒ‰…"Y…´dK'OždÛ¶møøøÐ±cG»×¬:—~k‹ˆ9sÆÏ-[+_$e±@TTÊ×yy“ ;v4–±[¼úö…áÃþøãn»zuøõWhÜØ( qwšD(ÙÒ‚ °X,4mÚ”"EŠØ½f6›ðñÑß—"â9NŸ6ž­ø’Z¶ìÞ:ÎiÉ—ÏÍÏ?o,—wâĽ׊5–ª»q#ùu ÏIWåqg Ð’-YË7:wîœìµÄ»‹¨šL&§öIDÄ‘*U2VÒX¹¶o¿wü?ŒU.îß7êêUø×¿’O,Ü¿ßXcÚ××Y¶ªRÅÕ_}/Ú_óâ‹Ð´©1z=t(Ü}£ÏæÏ?á½÷2þ5Š8‹´d;§OŸfË–-x{{’ìuËÝ™5^)ÍtqSùóÃèÑ`6C½zÆ%•+¿òбÑJRqqðÑGð䓯D¾† ] «V5jš¿øÂ~«ðaÃŒêÁƒ¥æL&cã«o¿5BôĉP¬˜Qs]«–±³a:pwO+· ÷¨%Û±–o4iÒ„ÇRXPÔ:­-"î¨bEcÅ kÙDRï½g¬žñÓOÆŽ‚Õ«“;v„o¾±¯O.Z6l€õëmÁÏž…š5¡S' 5‚tRÏþƘ”ø€k,\z9¸g6 ì^²„%K–8«EgnÝýø0 ø(tFiÿ?B %›HÏêV Ðò¨<Ȇ ‘˜Í]Ôâ6'µã©¼Ï+€Y@ÔÝãfŒ°•\ Ø)ýƒ?½¼È_¶,NjÑ3ܼy“-[¶Ø~Ÿ'gýýžè†QÖÑÈë„Þ‰'P€wáÂÖ¯_Édz`ù¨„C2ÆÛ;³y‚“Z ®8©-OVóîã=à*°XüŒQº‘HÒ2€›´ƒÐ³V>>ÔêÚ• œõ3åþ"## ¾oI;ãßóiŒÐü ?LäÑ(@‹Ç[¼x1 Ô¯_ŸR¥J=ð|@‹dgÎw»•À|``ÆL"Cï¾êŒ-éEPPW®\ÁÛÛ›„ ðÐÁeÖÔOÉ8hñxÖò­¾a¥-"÷XG§GbŒNW¦¹‚·wÌæÑNjí;Œí%DDD<›j Å#=jý3Üö„-"""™OZ<ÎùóçÙµk>>><÷Üs}½uÚÝK8DDDÄ1 Åã¬]»‹ÅBPP xèë5-"""iQ€“‘úgð¬hÉ| ÐâQ, k×®=@kZDDDÒ¢-åï¿ÿæìÙ³.\˜gžyæ‘î¡hI‹´xkùF³fÍy+n@‹ˆˆHZ Å£ddù:+Õ@‹ˆˆHZ Åcܺu‹M›6 О²¡ˆˆˆ8†´xŒßÿ;wîP©R%Ê–-ûÈ÷Ñ´ˆˆˆ¤EZC‡eäÈ‘DGG';'..ŽöíÛóÞ{ï‘3gNÂÃà fõêÕvç­[·Ž   6lØ@îܹ5jmÚ´!66ÖvŽÅb¡gÏž 0€ýû÷Ó¨Q#~øáÇ~¡bÇZ¾Q·n]¼½½3|?@‹ˆˆHZ>>œ;wŽ-ZØS¶lYòåË—¬þúĉ,^¼ØîXÇŽ3þEesÛ¶m#11‘Ê•+S¤H‘L¹§F ED$;¹?Ÿœ8q‚2eʸ¨7î!Û@‹gÉìúg¸7m±X0›Í™v_ñ ÙjÚd2Q²dI"""ìŽïÛ·???»ç}ûöѸqcÛy+V̰J”(‘ì^ÇçÆ¶{X•)SF#ÎÙõÏpoŒQèÌXÙCDD$«º?ŸÜ?"-Ée»耀€Tt@@€ÝóÞ½{í΋ˆˆ°½Ʋy{÷î%111Õ{‰ã$&&²mÛ6 s´———mò¨Ê8DDDä~Ù.@÷îÝ›ãdzk×.Û±åË—` aO?ý4,_¾‹ÅÁ8**ŠÞ½{Û® åöíÛ¬]»ÖvlÙ²e<öØc´mÛÖ9_P6¶gÏ®]»F¡B…¨R¥J¦Þ[KÙ‰ˆˆHj<ª„ãwÞ!<<œ«W¯ðꫯ’'O:uêİaÃèܹ3sæÌ¡E‹tîÜÙ¶|Ý¢E‹ìî5eÊÚµkGÓ¦M©Zµ* , I“&v›7oÎ+¯¼B×®]éÖ­gÏžåçŸföìÙZÚ ¬åAAA¶ãÌ’#Gâãã5-"""ÉxT€¦D‰ÉŽ'ÝÞÙ××—¥K—Ú¶òæÓO?M¶ôsÏ=ÇÖ­[m[y3&ÙVÞ&“‰9sæØ¶ò®R¥ #GŽÔVÞNòçŸF€ÎlZÊNDDDRãQºk×®é:/GŽ„††šæyÕ«W§zõêižc2™èÑ£=zôHw?%süý÷ßÉþøÉ Ö‰„*á‘ûe»hñ ‰‰‰ìÙ³€5jdúý5-"""©Q€·tøðanݺEþüùñ÷÷Ïôûk3I´¸%kùF```¦O ­Â!"""©S€·d ÐŽ(ß@‹ˆˆHê Å-9:@«ZDDDR£-nI#Ð"""â* Ðâv®_¿ÎÑ£G1™LV ´ˆˆˆÜOZÜΞ={°X,”+WŽüùó;¤ @‹ˆˆHj ÅíìÞ½p\ùhZDDDR§-nÇ‘;Zi'BI´¸GO @‹ˆˆHê ÅíDDDŽ Ð‘Ô(@‹[9vì×®]#oÞ¼<ùä“kGZDDDR£-nÅZ¾Q½zu¼¼÷ã«I´¸•½{÷èÐv4-"""©Q€·räÈ*V¬èÐv4-"""©Q€·røðaÊ—/ïÐv4-"""©Q€·bväBдˆˆˆ¤NZÜF\\'Ož ­#ÐqqqmGDDDÜ´¸cÇŽ‘˜˜Èc=FÚ–F EDD$5 Ðâ6œU¾ª‘Ô)@‹Û°N T€WR€·avô  I´¸ gŽ@[tBB‚ÃÛ÷¢-nÃÑúÇ$11³Ù €···Ý97oÞtHÛ"""â> Åm=zp\€^¶luêÔá§Ÿ~ââÅ‹€ ¯]»Fxx8¯¾ú*ãÇwHÛ"""â>|\Ý‘ô8{ö,7oÞ$wîÜ”,YÒ!mtìØ‘¹sçÒ­[7۱ɓ'óŸÿü///¢¢¢Ò¶ˆˆˆ¸@‹[°N ô÷÷Çd29¤Ö­[“3gN»cI7Ryá…ð÷÷wHÛ"""â> %K:sæŒÝçÇ L™2k3_¾|4oÞ<Õ×è°¶EDDÄ}(@K–´~ýzZ´hÁž={ˆ‰‰ hÑ¢€1"=dÈ®^½š©ívìØ1Åã+V¤E‹™Ú–ˆˆˆ¸'hÉ’^xáÖ¯_O`` ¥K—æ‹/¾`ûöíÀéÓ§)X°`¦¶Û¾}{¼¼’ÿg1`À‡•Žˆˆˆˆ{Q€–,©`Á‚<÷Üsœ:uŠƒpèÐ!¶mÛ†ÙlvHIEñâÅ©_¿¾Ý±¼yóÒ»wïLoKDDDÜ“´dY©•S<õÔS<ûì³i7$$Äîó=zdúH·ˆˆˆ¸/hɲ:tèjÙ„#'ôÝÜ à°¶DDDÄý(@K–åççÇ3Ï<“ìx‘"EèÞ½»ÃÚ-_¾<…  B… :¬-q? Ð’¥¥TÆÑ¯_?rçÎíÐv­}ðàÁmGDDDÜ´di÷h///þùÏ:¼Ý×_R¥Jêð¶DDDĽ(@K–V¥Jž|òIÛçmÛ¶¥\¹ro·V­ZŒ?ív/"""ö %Ëkܸ±ícgîسgO§µ%"""îCZ²¼víÚÆäÁ´¶Úq½?-Y^‡xüñÇyÿý÷µ ˆˆˆ¸œF %Ë3™Lôèу^½z¹º+""""÷0vìX‡/]'"""’· ð,"""Y…´ˆˆˆˆÈCP€y Ð"""""AZDDDDä!hñHß~û-áááNm³U«VtìØÑ©mŠˆˆˆó)@‹S|ôÑG|úéT§µwóæ5JÆÝ&ÈË9o²ün2Q¨P!h‘l@Zœâúõë\¿þ8 #œÔâÛ4á6³ÒZ+__§´#"""®§-Nãåõ8ð’“ZûpÎIm‰ˆˆHv¢I„"""""AZDDDDä!(@‹ˆˆˆˆ<h‘‡ -""""ò EDDDD‚´ˆˆˆˆÈCÐ:дsçN,XÀ¹sç¨_¿>½zõÂÛÛÛÕÝÑt,[¶Œúõë³cÇräÈÁ°aÃèÔ© ®îšp“®îB¶£ï¹óé{î|úžËýß}Hv¢èG”˜˜È Aƒ á‡~ wïÞ±téR:uêä⊈ˆˆˆ#hú­_¿žcÇŽÑ¡CÛ±zõêQ¬X1f͚庎‰ˆˆˆˆC)@?¢ÈÈHíŽW­ZÕöšˆˆˆˆx•p<¢èèh|}}©T©’ÝñjÕª±sçÎdç¯^½š¦M›Ú+S¦ŒCû˜•üùçŸÄÇ*8©Å¶:±µ3ññœ^±‚³gÏ:©ÅÓ÷Üùô=w>}Ï%5ÎûÙ¸y÷ù" MnÍ*36Nœ°¯ìß¿?-[¶Ìð}=™´4hÐ ÙgvS»vmj×®íÄ8±­¬IßsçÓ÷Üùô=—Ô8ÿgÃsT©R… ô³žèGäççG\\¤Zµj¶ã{÷îÅÏÏÏîÜÐÐPBCCÝEqÕ@?¢€€"""ìŽïÛ·ÏöšˆˆˆˆxèGÔ¤IÊ•+Ç’%KlǶmÛFLL ½{÷v]ÇDDDDÄ¡L‹ÅâêN¸«eË–Ñ¥K7nLùòå™?>Ï>û, ,ÀÇGÕ1""""žH#ÐЮ];¶lÙB:uˆç?ÿù .̶áùرc˜L&¶nÝjwìÆ™ÚN«V­ìFùß~ûm‚‚‚ú>… bòäÉižsãÆ Ž?žêëçÎcäȑܺuËv,&&†)S¦Ð¯_?zöìÉ'Ÿ|±cÇ’]ÁèÑ£y饗èÖ­_~ùeŠçeÄúõë1™L¶YÚW®\Ád2±jÕª‡ºÏäÉ“yüñÇxÞÉ“'¹zõjª¯ÿý÷,]ºÔîØÊ•+9r$;wfÈ!„……¥zý;wøßÿþGhh(/¾ø"ï¿ÿ>'OžLÿ’Åmݺ“Édû98{ö,&“‰õë×;½/kÖ¬aúôévǶoßΘ1cèÚµ+dúôéܹs'Õ{üôÓO¼ùæ›tìØ‘·Þz‹]»v9ºÛv¦M›FýúõñòòbôèÑé¾®\¹rvçwìØ‘—^z)Ùy§NbäÈ‘ÄÅÅÙŽEGG3iÒ$úôé믾ʧŸ~šêÏèÁƒ2d;wæÿþïÿ´j*FŒA­Zµ¨U«O?ý4mÛ¶e„ ܼyóÁ§ÓùóçùòË/¹~ýz¦Ý3½nÝºÅØ±c©R¥ ¹sç¦H‘"<óÌ3|ôÑGÉÎÝ·o]ºt¡B… (P€àà`ƇÙl¶;oéÒ¥ÔªU‹èèhg}ÙŽt=ýôÓ|ôÑG|óÍ7ôíÛoooWw)Kñ÷÷O3e†*Uªì{‡……%[ª0©… ²dÉòäÉÀ/¿üB5˜>>–ýû÷ÛŽ'&&&»_ýúõ-… NñµG±nÝ: `9sæL†î3iÒ$K‰%x^‰%,ãÇOñµððp‹¯¯¯åÚµk‹ÅbùÿíÝiTWÚð?4û¦ ì (« !"â.‹¨‘m0—8†èãžDM4:'›1jpŒñDQFÍdQÁ h\pEd‘Mvž÷/5Ý*8bŸß9ý¡oߪºu«»ú©{oÝZ¹r%)))Ñ¡C‡DùRRRHKK‹fÏž-JŸ1c™™™QYY™(ýyÕÕÁ¹sçݺuëw-ǃH"‘ÐÕ«W‰ˆèûï¿'!ÊWXXHVVV4lØ0ÑqøòË/IQQ‘233Eù_ä±Úµk©©©Qkkk·—533£Õ«W ï}}})((H*Ÿ‹‹ ­Y³†ˆˆJKKICCƒ|||è·ß~ò444P`` ©ªªRqq±>xð`²··Ê×ÚÚJNNN4~üøn—÷¯.((ˆlllDi«V­"”œœ,¤577ÓÕ«W)33“ZZZd®«±±‘RRRèòåËTWWGDD---”””D(==ªªª¨¦¦F´ÜƒèܹsÂ2ÕÕÕÔØØ(|^SSCõõõDDtûöm:{ö¬hù’’:sæ •——‹Ò«««INNŽ-ZôÄ:¨®®&===òòò¢ææfÑg/^$‰DBk×®Ò"##ÿç’¿2 Y—„……Ñ_|!J»té988PEEIÐÎÎ΀LMMÉÁÁèÖ­[t÷î]rpp ³gÏÒôéÓÉØØXˆ?ýôSrtt$MMMÒÓÓ#QÐI$@ñÅ&ÊsðàA²··§^½zÑ„ (''‡(11QÈÓ@üñÇdeeE}úô¡Y³f 'ÈØØX255%999¡üu555¤¤¤$ìï¢E‹HQQ‘ ¥êïáǤ££CþþþO¬çÅ‹“žžÞ3ýéËÒ9€®­­êþÑý ¥¾}û’µµ5­]»–¾ùæòóóòtÐYYYäááAZZZdgg'ªÏ×^{ÈÐÐP¨¯ŒŒ áó÷ߟ&MšDDDeee¤¢¢BóæÍ“Yîµk×’¼¼¼pì‹‹‹I"‘ÐâÅ‹ŸK½tÅæÍ›ÉÏÏNŸ>McÆŒ!mmmòóó£ÒÒRºÿ>“®®.YZZR||¼Ôòß}÷999‘††ÙÙÙÑÖ­[¥ò|õÕWdmmMúúúF?ÿü³èO¯¢¢‚èÒ¥KD$ûøIÿvîÜI¯½ö?žÆGÚÚÚäããCEEETUUE¡¡¡¤¯¯Oýû÷§½{÷J•kÏž=dnnND퉉 ;Vf=ÅÅÅÚ¿?µ_0êêê’»»{j¹gÌ›7ŒŒŒH^^^tîéü½îààà@±±±Âû®Ð$‘H(--ˆˆæÎKjjjRxy555iÖ¬YDÔ~>““mƒ¨ýü€ŠŠŠžu×ÿ’dÐ'Ož$´{÷n""ŠŽŽ&RTT$‰DBúúútøðaÑ2aaa¤¡¡AÚÚÚ¤©©IŠŠŠ”M.\ %%%@JJJ¤¬¬LfffDÔ~Ñ·bÅ ’““#RPP o¿ý–zõêEß|ó°n333Z¶l¹¹¹‘¼¼<éèèQûE¦««+ 555@o¿ý6555Qee%)++ÓªU«žXŸ}ö ¬¬,™Ÿwì[Ç…Ð=‡p°.ÉË˺s;ÔÕÕ!==ÍÍÍ2—Y¿~= $$›6m¦M› §§‡¦¦&¤§§#44õõõضmÞzë-ÀƒðÁàäÉ“øê«¯PSSww÷'Žu»{÷®hìà™3g8p£FB@@ÒÓÓQ]]-ZvÆ ¸xñ">ÿüs,Z´†Z8;;#$$ BùçÏŸ/,›˜˜ >pöìYŒ1fffReÔÐÐÀäÉ“qöìY©ÏP^^Žü‘‘‘˜;w.äå{æ§ÙÚÚŠôôtÔÖÖ i8vìÞ{ï=lܸIIIøøã‘““#ZöáÇƘ1c°gÏôéÓG4¿ùG}MMMx{{ õõh]ÄÆÆÂ××påÊ444à7ÞYÎÐÐP´µµáÌ™ögz¥¥¥¡µµŽŽŽX°`ƇiÓ¦‰ÆÛ?o%%%8qâæÏŸÀÀ@lÚ´ ééé˜9s&^ýuèëëcçΰ´´ÄìÙ³AÜýÑGaÙ²e?~<âããáííåË—ã믿òlٲ˗/ÇØ±c±oß>´µµáÍ7ß•¡¹¹éééÂ}²Ž ý¸wïNŸ>ððpøúú"""999˜6mBBB ­­;v`ðàÁ˜3gŽÔo866>>>€¢¢"ܹsGæ` ý^ á»]\\Œû÷ïÃÍÍ +W®„««+ñÓO?u÷<³ððpøûûCUUUtî)++“ú^@zz:*++»µÃ‡ÃÄį¾ú*€öß¿››ôõõ¥òöéÓžžžBµµµ‰¾/åççw«,/£„„íÃóóó///äåå!''G8ïwÜÃrüøq8p/^Dee%jkk‘šš ===8;;ã—_~Ð>.½¡¡A¸!&&Ÿ|ò Ö­[‡²²2¤¤¤àûï¿—9VzÓ¦M>|8ŠŠŠPTT„¶¶6€ˆpñâEÔÖÖ">>û÷ïî¿ÑÖÖ†¿¿?6lØ€°°0ÄÇÇ˼$++ úúú¢çN<ÊÅÅuuuÏý>ö¿sÏþ$†Nï¾û®(­sëfW‡ptä{Zk,QQQI$úñÇ…´Î-Ðï¾û. >\xïíí-´tذaµ¶õêÕ‹,--Eù‚‚‚(00Pxÿ¤!!!!4gÎá}ïÞ½iæÌ™Ý—5kÖª®®¥^üñc—QUU „„"jïE@QQQÂ2MMM¤¡¡!jñÙ¸q£TýeffJµˆJJJPZZ*UÏÏ­ìššš¢õË*»±±1 D=#'Nœ€¢¢"Æà¿ÇêIßíÖÖV!Ÿœœœ°ý«W¯béÒ¥HNNÆ”)S°zõê'ÎÚñgÑÐЀ£G ¿999H$’'ÞÐÖÒÒyyy¡wiãÆ¸~ý: € ÀÂÂBè!SQQéùø“©««Cbb"’““ahhˆ-[¶ !!rrrÈÍÍÅðáᥥ%ä722ÂàÁƒ‘›› †ºº:Œáïï}ûö=¶÷ôQ7oÞ”ú?qqq‘ÙKØ9_ff&ðùçŸÃÏÏOx]ºt Â÷E[[Ë—/GAA.]º„ððpDFFbäÈ‘Bž¾}û>ñǎ¥åS÷‰=/ç|kì¹ ÿq q;;;Ñûšš8::¢oß¾˜]twñÓ<|øPÔ:ÊÌÛ9]EEE˜%âIN:…ææfQ`nee…›7o>v™›7oÂÈÈêêê¢t[[[ÀèÑ£¡ªªŠ5kÖ`õêÕ=6SÆ£jkk¡¬¬,5˃±±±Ô”Z½zõ’*»²²r—êëÑá@{]íw×›››Kå¿uëÚÚÚ„ºé8žð@ûôbß|ó ZZZzdúH]]]ÑÌ:ÊÊÊ 5¥ß£õPZZ ¸~ýºTv˜9s&Z[[…?ˆ>Ö??Y¿EÑö»Rv ýXyyy õÙq¬÷ÝnkkCqq1&Ož à¿ÇÊÏÏO”Ïßßñññ(((ÀÀ»µ=éYÎcLJ¢¢"\\\„4++«'vŸçççÃÂÂB¼¦L™‚ÔÔTDEE¡¤¤óçχ²²2Þyç©ïúõë÷Øé[[[eþþDè7èèhüýïÇ’%Kpþüy˜šš>v» Rÿ?2¿7‡ï444@"‘`êÔ©2gèê¸Øì ‘Hàää'''Œ38zô(¼¼¼`mm#GŽ ¶¶Vt¡Ð!;;ÆÆÆ2ϧ¬gp 4ë###©ù$»2þT^^mmm]ÚFRRŠ‹‹±oß>¬^½Z˜ëòþýûÝ*«¹¹9Ž;&J{Öyt%‰ÌeLL <==E-Eîîî¸pá®_¿.•¿²²ñññ¢–^YìííÑÚÚ*sœfO°²²Bcc£Ô±ì\]%«¾îß¿””Q@5tèPhii!22RæzvîÜ %%%!@4hH ¬©©’’ÒjúÈŽ 800‘‘‘R/áO®ó˜øŽVìÇÑÐЀ¦¦¦0¯w‡‹/>—²âããEÇÊÄÄVVVؽ{·Ìeöïßúúzá»mjj ™Ç €ÔEØ‹ddd„òòrÑ9éYê.&&“'OmîîîHNN–9o|II ¥~ÿöööøì³Ï°gÏ,Y²'Nœ€ŽŽŽpáȺÆÊÊ çÎÝ+S^^ŽŒŒ á€ÌîÝ»%%%hllæ:×ÐЩkkk=zT”–œœÜ¥ ¯¢±±ZZZ–z=é^—Ž‹¨Žÿ‚iÓ¦ˆ°qãF©¼uuuضm›¨·Œõ;Æì÷IDAT< Y—Lœ8ÉÉÉœ’µµµˆŠŠzêrÎÎÎ]uuuÑÖÖ&´´µµaÛ¶mÝ.khh(Ο?¸¸8nß¾o¿ý¶ÛëÚËßÔÔ$Õú'jQ€åË—CWW³fÍByy¹^WW‡Y³f¡­­ Ÿ|ò‰Þѵøh¾õë×CUU¯¼òÊ3•·»<<< ¯¯Í›7£¼¼­­­Øµk×3ßÄäìì,ÕrsssQƒŽŽV®\‰¨¨(ìÚµK”ÿСCˆˆˆÀ‚ пí½#GŽÄ?þ(üÁUTT ..cÇŽ•jÉù= >ýû÷GDD„ÔC„:Z§555áêêŠøøxá†ÀÊÊJ:tè‰ëVPP€»»»è'33.\x.eOMMEEE&Mš$¤ÉÉÉáË/¿Ä•+W°jÕ*Qà‘‘eË–ÁÃÃChVUUEXXbbb„óêëë±wï^ôïßÿ‰­}=mâĉ¨ªªÂ•+W´·\îܹ³[ëè¸Èèüû_±b…Ðcöèæ555˜1cTTT°zõj!½s vùòe=zK—.æ•g]3gδ´´`Ù²e©eË–A"‘`öìÙþÛ@Ó¡ªªJ4œhðàÁÐÕÕELLŒèØ,X°¥¥¥˜9s&®_¿Ž}ûöá“O>‚‚ÂSÏ;¾¾¾°µµÅÚµkqòäIá­´´ñññÚož3g®\¹"ü÷åççcÕªU——.º0wî\üóŸÿDDD„PÆ‚‚Lœ8zzzذaƒTîܹƒÂÂBÑ«óMÈìÙpͺÄÓÓuuu033ÃÔ©SabbGGǧ.7uêT|ýõ×pvv†§§'îܹóؼ...°µµ…««+ÂÃÃamm+W® _¿~Ý*ë¼yó0iÒ$øúúÂÚÚ3f <è*¼úê«6l<<<ðÞ{ï!-- wïÞ†ššš8|ø0*++1pà@x{{# VVVHKKÑ#GDÁƒ««+ Œ7:::HMMÅŽ;лwïn•óY©©©!22?ÿü3ÌÍÍaii‰µk×",,LèòýíoØ»w/áéé‰ëׯK ßè°hÑ",\¸o¾ù&1mÚ4Œ5 ¾¾¾˜5kÖ­['Ê¿eËܸq¦¦¦ D¿~ý ¢¢‚Í›7?óþ÷555üðøvíôôôˆ9sæ`äÈ‘¢Èõë×#??ýû÷ÇÌ™3Ñ¿©aM²øùù!** ŽŽŽ˜8q"&Nœˆ &<—²ÇÅÅÁÝÝššš¢t|ýõר°almmwwwŒ1öööRÓ~ø!ôôô`bb‚ÀÀ@˜šš";;;vìø]/vÌÍÍáàà€±cÇ"00æææÝ´÷¼UUUÁÓÓS”®­­Ã‡£¸¸666ðññ¿¿?¬­­qóæM$&&¢oß¾Bþo¿ývvvxýõ×áââ‚Ñ£G#((K–,y.ûú2±··Çwß}‡]»vÁÜÜ ÀÁƒ±{÷náAXqqq077G¿~ý`kk Œ5JxÀŽœœÞÿ}|÷ÝwPUU†:¹¹¹aûöíˆÇÀ±téR¬^½‰ä©½)ÊÊÊ8xð àææCCCÂÄÄD¸XVRR¹sç0tèP¨««ÃÀÀVVV8sæ þõ¯aÈ!Âú6oÞŒuëÖaÉ’%ÐÒÒ‚0,èÌ™3011‘*ÃØ±cѿѫ»L69ú_²²—Fff&öíÛðöö†™™âââ0cÆ ¨ªªâáÇˆŠŠB@@ôôô„å*++‘œœŒŠŠ C"‘ÈÌ´·ÊÄÇÇãܹspttD`` bccaaa!ÜuäȨªª 7H¥¤¤àÞ½{ð÷÷ÖÓÖÖ†óçÏ£¨¨C† Aqq1&Mš„‹/bذa€ÈÈH899ÁÞÞ^X.;;.\À¬Y³DåÊÎÎÆ¹sç ««‹+W®à—_~Á‰'dÖÓ¯¿þŠC‡!##---pttĤI“¤‚âììl$''#??DsssÁÈȨ[ÇåIJJJDǨ©© ;wîÄäÉ“EÁ|YYΞ=‹^½zaĈB‹FLL €öcŸ––†éÓ§‹Ö¿gÏ 2DT‡µµµ8yò$JJJàåå[[[$$$ˆÆ‹>*55gΜƈŽ=Z8Fݾ}±±±ÈÍÍ………¦OŸÞcOé»pá $¤•——#::ÁÁÁ¢ã¹gϼòÊ+”f@û÷~ÿþý¸~ý:$ ¬­­áíí-ú“+((ÀþýûQSSƒ &ÀÞÞÑÑÑ …¦¦&êëë±{÷nøøøˆ¾qqqHNN†‰‰ ¦NŠ»wïŠ~W¯^ŵk×*,óàÁüç?ÿÁÔ©SѧO!=** 666prr‚ƒƒæÍ›‡ððp™uróæM;v ×®]ƒ¡¡!œœœàáá!3(®©©Att4ÒÒÒ`dd„ààà:>333—/_–ú-WUUaûöí(--ŨQ£ˆíÛ·ÃÍÍM¶­8zô($ <<<ðþûï#==]˜F­³šš9r " :žžžR%wïÞEtt4rss¡¯¯1cÆÈ¼y—µkjj’º·¥³ÊÊJ\½z‰¯¾ú*zõê%ú¼¨¨¹¹¹——ǨQ£ÛÒïÞ=455Iõ–TVVBGG‡”)SpêÔ)Œ;@{K¯†††ÌÆ"B^^òòò ¦¦†JçKKK‘——‡ªª*èëëÃÎÎNê;Ó¡¾¾×®]í[·°bÅ hiiáäÉ“¢üuuu¨¨¨¹¼ŽŽŽÌqÔ¬{8€fI555Âɳ¾¾³gÏFRR ÿç1˜Ó§O‡›››ÔóŸÕ£u´ÏÑìää„M›6aÁ‚ÿÓºSRR°lÙ2œ:uê5N™I{ðà<<}+W®Dyy9²²²¤n~Ñ 1zôhØÚÚâÈ‘#ÏÔsÈž Ðì/I]]...PWWGbb"ZZZ°sçÎÇ>¸ãe¶fÍ8pÈÊÊBFFÆŽ‹¤¤$>3Æ^zæææ(++ƒššªªª`kk‹½{÷Šzœ~O hhh€††FÌFÄdãšý%?—/_FEELLLàîîÎSC=FEEŽ?Ž›7oB"‘`РA˜û |ðèá3³gÏFcc#~øá‡¾ÏŒ±ç‡hÆc/ððp!11QH»zõ*œœœ0þ||õÕW¢iëêê7Þx?ýô“0G½h½ÍÍÍÂÔf²èôôt :999¢ÇL3Æþ\xcŒ±—JSSvíÚ%5­åŠ+```€O?ýTj !Ç# ­­­¥Öý´y`mm;vü/»ÀûqÍcì¥rãÆ 455Iµ§¥¥ÁÍÍ ªªªO\ÞÏÏZZZxã7°}ûv”””tkû¶¶¶ÈÈÈèv¹c@3Æ{©äää,--…´_ýeee]zܸššbcca``€·ß~ÆÆÆ3f âââЕQ‘ÖÖÖ¸víÚ3—Ÿ1öûãš1ÆØKåÖ­[PVV†žžž¦¢¢EEEÔÕÕui®®®HHH@^^¶nÝŠúúzøúúbñâÅO]ÖÌÌ ÅÅÅhnn~æ}`Œý¾8€fŒ1öR133Ccc#ªªª„4‰D‚àÆÝZ—¥¥%æÎ‹ÔÔTá‡~xj`\RRCCÃ§Ž—fŒýqqÍcì¥bggÈÏÏ¥ûùù!11ñ±ó4·¶¶>q½#FŒ@EE ž˜/77W(cìωhÆc/+++())IÐ~ø!Œáç燴´4!½µµû÷ïÇ›o¾ HHHÀ¿ÿýo455 yÊÊʰuëVhkkÃÂÂâ‰ÛÏËËãš±?9 cŒ½T‚ƒŠÒÕÕÕqöìY˜™™ÁÑÑ&&&:t(´´´ðÖ[oÁÙÙpÿþ}̘1½{÷†££# CCCTVVbïÞ½PPPxì¶oÞ¼‰¬¬,̘1£G÷‘1Ö³øA*Œ1Æ^:¹¹¹°··G~~>úõë'õyVV®]»†û÷ïÃÜÜ£G†¶¶¶ðymm-RRRPXXˆÖÖV˜˜˜ÀÃÃêêêBžââbÀÕÕUH[´h Û£ûÇëY@3Æ{)M:æææX¿~ý Ù^]]LLL””„aƽm2ÆzÐŒ1Æ^J÷îÝÃíÛ·áääôB¶W]]œœŒ9ò…l1Ös8€fŒ1Æc¬ø&BÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺhÆcŒ1ƺáÿóCi¯Ïª|IEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/create-index-time-int32-float64.svg000066400000000000000000002504541231437614300274510ustar00rootroot00000000000000 image/svg+xml 2.1xfaster 1.5xfaster 25x faster 21x faster PyTables-v.3.1.1/doc/source/usersguide/images/filesizes-chunksize-15GB.png000066400000000000000000001602771231437614300263550ustar00rootroot00000000000000‰PNG  IHDRÐß}™SsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝyXUUÛ?ðïaFqBqB™DKÅ10sb EL…Ì,‡4¥i¥ùV¯–¥¥½•å@f¦>išòˆSh*ˆ2)àˆŠ2Ãþýqbëñ{o…s8ðý\×¾€µ×^gÀÅíÚë^[%‚"""""’ÅHß """"2$  ‰ˆˆˆˆ`MDDDD¤h"""""@)ÀšˆˆˆˆHÐDDDDD 0€&""""R€4‘  ‰ˆˆˆˆ`MDDDD¤h"""""@)ÀšˆˆˆˆHÐDDDDD 0€&""""R€4‘  ‰ˆˆˆˆ`MDDDD¤h"""""@)ÀšˆˆˆˆHÐDDDDD 0€&""""R€4‘  ‰ˆˆˆˆ`MDDDD¤h"""""@)ÀšˆˆˆˆHÐDDDDD 0€&""""R€4Q-)..Æ™3g““Sm™.ÅÇÇ#==]/¯MDT_¨AôÝ ""Cs÷î]Ì™3§Êó&L€““:wîŒÅ‹cÉ’%€´´4­2]jÞ¼9†ŽÍ›7ëüµ‰ˆê }w€ˆÈ`ãÆhÔ¨Zµj¥u~РApqq½½=š7o®‡V®cÇŽ•ö—ˆˆäcMDôüýý«ÍMKKÓ]gd8{ö¬¾»@Ddð@Õ’ ** nnnpss“¬_XXˆ={öàÌ™3P©TèÕ«FŒ•J%ëõÊÊÊpüøqìÛ·EEEèØ±# WWW±Î¯¿þ ;;;ôíÛ[íšèqãÆÁÈèaºLjj*<ˆóçÏÃÞÞ/¼ðºuë&«DDõh"¢ZrãÆ „„„`ñâÅ’ô™3g0aœ?Í›7Gyy9îÝ»‡ÁƒcëÖ­°¶¶®öú ((wîܽ½=7nŒÌÌLáÎ;b½)S¦`øðáb½nÝ:lÛ¶M«½û÷¬ °°°¬Zµ ,@aa!:vìˆÌÌLã“O>Á¼yó”~{ˆˆ wá "z ׯ_Ç¡C‡4ŽcÇŽ)j£  ¸ÿ>~ùåäääàêÕ«X³f :„… J¶ñî»ïÂÈÈYYYHKKCbb"nß¾Ÿþ¹Úë¾ÿþ{ܹsGãØ²e T*üýýannøÏþƒ·Þz ¾¾¾8}ú4ÒÓÓqþüyŒ5 ï¾û.Ž=ªè=2ÐDDOáàÁƒðññÑ8Ư¨¯¿þéééxûí·ñÒK/ÁÜÜVVV˜1c|||°nÝ:Ü¿¿Ú6nݺOOOØÙÙ‰e&&&:t¨¢¾ÄÇÇcìØ±ððð@dd¤¸|dÁ‚077ÇÚµkáéé èÚµ+V®\ @=;MDÔPp ÑS2dþçþG£¬bÖV®'NÀÈÈvvvøÏþA ÊËËÑ©S'”——ãÒ¥KèÞ½{•mŒ7Ë—/G@@^~ùe :-Z´PÔììløúú¢Y³fˆŠŠBãÆùùùHHH€——þúë/±oýìÔ©’““½‘!cMDôZ¶l //¯§j#%%‚ `òäÉ•ž777Çõë׫mcáÂ…°²²Âš5k Œ?sæÌgŒ«“ŸŸܹs111hÛ¶­xîòåË'OžÄèÑ£+½¾¼¼\ò5ˆˆê .á "Ò3[[[XYY!??………•/¾øbµm4oÞï¿ÿ>222°ÿ~Lž<Û·o‡··7 ª½¶¼¼&LÀ™3g°eËxxxhõ^ýõ*ûW×¶ë#"ªM  ‰ˆôÌÝÝyyyˆ‰‰yê¶ÌÌÌð /à›o¾ÁêÕ«q÷î]ìܹ³ÚkÞ~ûmìܹ«W¯Æˆ#´Î·jÕ ¶¶¶øý÷ßQZZúÔ}$"2t  ‰ˆôlΜ9hÞ¼9^{í5\¼xQã\AA¶oß^íõ………رc‡Vp{áÂ×2WfÍš5øâ‹/0wî\̘1£ÊzK–,Arr2fÏž¢¢"siiiˆ­¶DDõ ×@é™~úé'„……ÁÝݽ{÷FÛ¶m‘••…³gÏ¢ʵǀ:€=z4lllУG´nÝýõ’’’àããƒáÇWyí§Ÿ~ ˆ‰‰©t-÷áÇaff†×_ X»v-vî܉ž={ÂÄÄiii8{ö,æÍ›'î-MDTß/Y²d‰¾;ADdˆÌÌÌàíí —*ëXXXÀÛÛ:uª¶¬K—.˜2e ¬­­QZZŠ[·n¡C‡ƧŸ~ZíŽfff0`¬­­‘ŸŸ`Ô¨QX¶læÏŸ͹’hôÙËË ]ºt£££ÖñüóÏÃÈÈ*• #GŽÄСCѨQ#ܾ}‚ ÀÍÍ óçÏÇ«¯¾ 333åßD""¤AÐw'ä¸|ù2N:…ÔÔT¸¹¹Á××·Òz øí·ß••___ ¾þúk\¿~&L륤¤ÀÝÝ]ãÚŠdšŠGÑV|tuuÕ¨çîî®ñ¸Ú¤¤$­ä¡Š¯SRRjèQ]R¯¶±;v삃ƒÑ©S'˜™™ÁÂÂ'Nœg›AÀõë׫  ³²²4>>»»»ã×_Eii)LLL••:ööö°²²Âµk×IJÐÐPœ8qvvvu;vìXˆˆèÉeddh|Þ½{cãÆzêQÝWof ËÊÊðꫯâï¿ÿÆ’%Kðí·ßÂËË cÇŽÅ™3gôÚ·ãÇ###C\fR!##Cëú,»té’Æ?¢ÚxÇ?u{â¡äÚGŸò&umEÝêê%%%áÆUÖ;uê”Öã+êݾ}çΫôÚƒÖ¹ßÚúYVWvöìYñA OÓžœŸåãeJþ-äääˆwœžæ÷oïÞ½Š¯½té’Ø×ºô»ñxY\\œF2·Ükݯ:cìïÃ19f4^‰ B»;mª¼¶ºïŸÜ²ŠqEéµJþ-ȇű¥²z©©©8sæL¥×æääˆcËã×>>‚»»»â÷ªkÛ·o¶oß^«¯QÙÏN©'í§’×–S÷믿bcc«<)ìÞ½»Òs§OŸ>ÿüóJÏ988ÈꣾÕÄϲ:ÿM>'é§’ß1¹ý”êGuç«:·`ÁaÁ‚’¯­oO<¶ì.„×î=<Jª¬Ê±Ec‹4Ž-ÒçÜÝÝÉ×nÈêÍŽ#GŽÀÈÈýúõËT*ú÷ï?ÿüS,sttD||¼Æµÿ[wrrÒø˜˜˜¨‘ /ž€®]»bÏž=•¶åèè¨QnccóÄïMWœký5Ÿº'í§’×–S÷¹çžC›6mª<ïææ ‹JϵiÓ¦Ò§¾ê‡\‚šøYV§S§NhÞ¼ùS·ó$ýTò;&·ŸRý¨î|Uçúôé#ùºuÇeu9¶plô;¶BÌ¢wúŽà•ªj:""B lܸQ,»ÿ¾Ð¬Y3aèСb™>¶±óññáÿäH¶Úž}¡úCwôJÁ 4IãØBr1n‘f3Ðqqq˜1c ==·nÝÿ‡}øða˜™™aìØ±øòË/1eÊ„‡‡£C‡عs'Š‹‹ññÇ‹m#""C‡EPP¸}ÝöíÛ5^sÕªUð÷÷ÇàÁƒáââ‚mÛ¶ÁÛÛ[cG!C† $$FVVöîÝ‹ˆˆ˜ššêà;CDDDDºfòÎÈÈ@DDD¥çæÍ›ccc@ii)ÂÃÃqâÄ Ü¿ŽŽŽxõÕWѾ}{ktý(ï°°0!22òi¾ Ô@\¸pA'·¼Éðݼy@=¾Ýº§øõ‘ħ9–€«AÌûÔI[H®˜››k<û‚4Dmè‘‘?þøCß]!ÆA‹dÙ±c€Ú_3ª7  kÇ’kðàÁèØ±#_ªÁ‘HG¸ç3Éŋ䪷3Õ Ž-$ciõfh"""""]`MDDDD¤hyü @DUyüé{DU¹yó¦˜HH$…c ÉŘEh¹víš¾»@bÙ²eú˜Ç U‡c ÉŘE“u„ òI.&ú\L"$%8¶\ŒY¤qšˆˆˆˆHÐDDDDD 0€Ö.È'¹˜èCr1‰”àØBr1f‘ÆZG¸ Ÿäb¢ÉÅ$BR‚c ÉŘE“u„ òI.&ú\L"$%8¶\ŒY¤qšˆˆˆˆHÐDDDDD 0€Ö.È'¹˜èCr1‰”àØBr1f‘ÆZG¸ Ÿäb¢ÉÅ$BR‚c ÉŘE“u„ òI.&ú\L"$%8¶\ŒY¤qšˆˆˆˆHÐDDDDD 0€Ö.È'¹˜èCr1‰”àØBr1f‘ÆZG¸ Ÿäb¢ÉÅ$BR‚c ÉŘE“u„ òI.&ú\L"$%8¶\ŒY¤qšˆˆˆˆHÐDDDDD 0€Ö.È'¹˜èCr1‰”àØBr1f‘ÆZG¸ Ÿäb¢ÉÅ$BR‚c ÉŘE“u„ òI.&ú\L"$%8¶\ŒY¤qšˆˆˆˆHÐDDDDD 0€Ö.È'¹˜èCr1‰”àØBr1f‘ÆZG¸ Ÿäb¢ÉÅ$BR‚c ÉŘE“u„ òI.&ú\L"$%8¶\ŒY¤qšˆˆˆˆHÐDDDDD 0€Ö.È'¹˜èCr1‰”àØBr1f‘ÆZG¸ Ÿäb¢ÉÅ$BR‚c ÉŘEšÁ$^¾|§NBjj*ÜÜÜàëë[i½²²2üüóÏ8zô(îÝ»WWW„††ÂÖÖV¬SRR‚7"66mÛ¶EPPºwï®ÕVBB¶nÝŠÌÌLôéÓaaa077רS^^ŽððpÄÄÄÀÆÆ£GFïÞ½µÚâ‚|’‹‰>$“õëðáø}û¶d='''¸¸¸è G•;~ü8rss1~üxìÙ³§ÊzèÒ¥‹{¦©¢ŸRôÝOCù¹? Æ,Ò "€^±bæÏŸSSS‚€—_~¹ÒúöíÛ;v,NŸ>Áƒ£I“&ظq#œœœÄ?4ÅÅÅ5jþúë/!66Ÿ}ö¶oߎaƉm[`CåÈËŽ«òt­{ØÏêåå±±Úå‚ðäeJêTß¿úàÚµk\-E00]»vBCCµÊÿýï *•J°··zõê%¼÷Þ{‚½½½`ff&9rDA(//LLL„… j\{÷î]€ðñÇ ‚ Ÿ}ö™@¸~ýºF½?üP022JJJA„V­Z S¦LѨS^^.XYY o¿ý¶X*î°Q¶k=zŒ×¸~ûöíÂ7XÆ2–±Œe»‹„Ôã…‚I×áµ{‚PR§úçàúÏX~CP‡ZÇãerëÕVÙø®W[e¡5\¯¶Ê|dÕsvö©¿§rʶoß.lß¾]X°`°qãFÁÇǧÒX‹2ˆ%r”––BxzzâĉøøãqéÒ%´lÙï½÷ž¾»W¥ü|€wJˆˆ ý{À¶mÀÔ©@zº¾{Cuÿ¶×o*A¨êfFÝäìì ///­[Ü6lÀ+¯¼‚ððpLœ8Q,Ÿ6m"""——•J…víÚ¡gÏžˆŠŠë;v }ûöÅúõë†Í›7#$$D#9ÆŽ‹#GŽ ''àéé KKKÄ>r/)-- ;wÆòåË1oÞ<@XX6n*¿uSÓ pszöž}V}xxÿlöADÔ0í)~}$ ™c ¸ê~åáß»w«£GÒÒŠ3a¨nID7·0¬^­]OWK­ýúsq ÃgŸI׫-S¦Èë§«kV®Ô®÷4Kb”,“™81 YYÚ¯¯- nÀ¬YÀ˜1€‰-š ÀmU«c@?ÎêuíÚPðØâ¤‚‚˜˜˜ˆÉ€ŽŽŽˆ×¨sîÜ9ê=ý˜˜˜¨@ÇÇÇ‹ç*^óñ=5+ÚÒÞš®êÿŠ–”§O«ï¿W—™˜..ê`º"°îÞhÔ¨Êf¨ž¸páœõÝ 2 „666zîIýrç°¿:`Þ³ÈÊÒ®S1ççK·×¬àãS³}Tâáß ª[Z´†×E*'÷ï[óæÀСµÛ—êXZʯ{äˆúhÛxã `Ú4à‘ÇRÔYEEEÕnÜ@² ‡}ûö…««+¶lÙ‚²²2Àýû÷±{÷nôïß_¬7uêT¤§§ãÌ™3bYtt4œœœÐ¯_?@Ïž=áîîŽèèhTLП;w)))âÿÊ*ÚºwïþüóO±,** mÚ´ŸŸßc=¬<‰ð™g€·Þ¬¬–—–gÏë×o¾ ôë4m ¸¹¡¡ÀêÕ@L ðàÁ“|·¨.ãÓÂH.>‰°f‚zã“O€Aƒ€V­€±c~Ð ž¹s}û€Û·;;ýõùÉplÑ¥víkkõçYYÀ;/¿¬ßdM9ø$Bi1‡3fPoWwëÖ-xyyP?ÈÌÌ °fÍÀÑуƶmÛ`jjŠÏ>ûLl+88:t(‚‚‚Äíë¶oß®ñš«V­‚¿¿? lÛ¶ ÞÞÞ˜0a‚XgÈ! A`` ‚ƒƒ‘••…½{÷"""Bcë<µÊ³Y[¶>ÿ\ýyy9pñ"ð×_ê#.N}Ü»§>_V$&ªM›ÔeFF@×®šË?zôš4‘÷½}öÙQ¸{·…d=GGcìÙó½¼Fé©ð–ÉÅÝ7žÜ;ê@¸b–ùŸ•y7FŒP:iž/(È ½×oYY‰dÚTXxÀãÿùX¹òòÒ*ÏéÂÃ~VOßý”ûsïСüDF_}¥þOZq1¡>ž}V=A\÷–lri±:##•ž›7oŒÅ¯“’’ðóÏ?ãâÅ‹èÖ­^{í5­Û›º~”·z t€Z¯Ñ£Çÿ!.ns•ï]€”u ýh`}çN•—@¥ºtÑ\þѳ§ú6âã\\Âpþü†ªûG¯^a8qBºQ©Á5гÌk™SOJ<®[·‡óÀ@uw± å‘ÎÿùÏ>ܺ%ÝOWWgôìé)Y¯¶J?ŸôçþßÿªémÛÔK7+ØØ¨“R§OWÏP×\-Í hC†¤¤Lš4U뜋Kx{÷¯äªê]¾ü0˜®øxëVÕõU*õr‘ŠYêž=ÕÇ€  ‰ŠŸ~ÚŽ«W¥oÍöèá‚_¤ƒU®"j{¶%:žj%–_š‰;í®[“ ¤rs5g™+»+me¥9Ëlo_£o…HKv6ðÍ7êãÑ;ÆÆ@@0k–úwRŸ@Kc­aaa(**Bddd­¾Nzºæ,õ_7nT©iJJ6H¶ÍZw˜DHr)M"ìÜy4ÒÒÞ•¬×«×—8qâ§§êÛÓhß~$®^áhŠ1x¸Äl®#ÅûöýGþ(~-걯b–ùøñÊg™]\4g™ÿYXïql©[JJ€_~QÏJÿó d‘«+0s&0i’f~”®„„„ÀÜÜœt5 b t} ‹ùööêc̘‡e™™ÚË?ýo‰Ì¥y7oªgrììÔGË–UoûS† {×®Uò—ð1îî¶ÿ·zT{ø$B’Ké“ÍÌšð’¬glüOÓ­§faÑÀpÅÐÜÁ¨ ý³ed´·okÎ2_¿®Ýž•ð ƒæºr›\×8¶Ô-¦¦@Hˆúˆ‹SÒ‘‘ê'2&&3f aaê`ºKÝõO"”ÆZGôõ‹Ø¡ƒú5êaYVÖÃ`úÓOû÷¥ÛIM† {øµ™™z+ž¶mÕ•­[«oK=­ÌÌ2YKMÌÌžþÅôŒà ÏâÅ«‘šZÉ^g4èYL:¶Æ^·¡'ž=«Þ1£¼\ûœ«ëÀyÀ€†3Ë\Ž-uWÏžê]_V¬Ö­¾þZ}Wùî]`Õ*õÎ[Æ©—wŒ¡Þ@ 61x–ƺjÛV}øû[¶çÏ+o£¸X=»™Y}=##u]U€]€·iS?þÀ½ôÒ›ÈÊÊ“¬×¿¿+>ýtžzT¹üü|KÖ³´´Ôë^ §N—)T§sçÎâ^ðúq ))+%ë%'ϯÑZ)¹ önݶnUþ覊Ï++{Úó–ÉÙ[òù§Ö¤‰æ,s‡òÚ ªKZ¶Þ}xç :øòKàÀõ¿Ý={Ô‡ƒƒzvzÊõžØ¤  I`íZuòCv¶z»âóŠãÖ-í?Ðååê%#99ê øê´lYu -÷ª¾%$ä!9yƒŒšaµÜ“êyxŒDa¡t»ƒÃ üù§þÖ¾ôÒ"dg‡IÖóô\Œ'ªÞͦ¶™h#YO¥ª™i£¼<õ>Ĺ¹ê~^ÝÇŠ-1¥\¼ŒW#]­U©omWÌ2kíJd ŒÕwŽGROr}õ•zûÚû÷K—€·ßÞ˜8Q=+íî®ï7<  u¤¨¨ê'ss ¿Äf!%%ê@¹ª»â¸~½òÄž[·ÔGB“÷óêUà³ÏÔ³QVVêeVVº]í„.}Œ;áêÕ/$ëuèV«ýbaÑ%%ã%ëKï[×Ë |/ËÍ}ôQÒ³ó óI„=z˗뻆ƒI„†©[7`ÍàÿظQýyR’zbéÛoÕÇ Aê=¥>2|þü#''W²ý# $Dóáo|¡4Ð:ÒžêcjúpÍuuÊÊÔAteÁõ£wNŽü$Ç YYê[_RT*õC* ´« ¼«*«É€œ‰>uGq±:¡§°(*Òþ¼â£œ<@½f·Cu0\3wU*žBøp-´¹¹ú‘ÌÖÖÚÃëßi IDATB÷îê=šw•*û\n™’ócÆW®H÷“”áØbØš6Uɳf¿ÿ®ž•ÞµK}§÷ðaõÑ®z?é×^¶l9Œ 鉒ôôZ4“¥1€Ö‘ºú‹øÜs¶°¶“¬çååZc¯ilüpiFuAýÇ>;ðó22j¬ uÐ#7ð‘RÊ«Ÿ–¦~$°‰‰öÑ­Û|öYåçjê¨ì@eAýžA}”—ëös¹7nnÜPÏÂHºr>ôcMoò™Ÿ//p62R?øèñX;(ÔªÓ¨QÕíþç?òhKKõVoú‰¯ÚÁà¹~P©€¡CÕGjªzyå÷ß«ïN]½ ,Z,] ˜™Y@ÎÒ²ÊÔÕ˜¥.aÝÀmÚTw·|S©ÔOh²±Q§rôêì߯Œóò4ÇËäÔÉÏ—DUäråä_HO èÝñãê`ª®»t xýu}÷BšµµzMcõA±:x®í,{"2|;«wîXºøé'õ¬ôß«ïžÉȧ§Àšê¦MÕGM(/<tÿ½zÛ!)&&êÿ”–><äÎ “<&&€……z6ÓÂBóóÊÊžæüرê-§¤89©·ªÒ—¢¢+hÚTzgAy+¥–^…©éf˜-aTþðI„¦F™0U=|ay¹Â5^Dõ”¥¥úqàS§Gލé­[kþ.=ÄZG =‰°¡22z¸ÎYŽèhyôsϱ±Ú奥@bâ888k×5},^\ù'סƒz—•J}éöó3ÔëÚ¥ôè¡^ øh€[ûË¥¯%JŸD¸wïäæJ'µoï'Y§6mÛörssasª1lÿûð›Û5°)îÛ?ÜjÃÁa‰zg¸˜DØ0 ¨>bc¥·š­ “¥1€Ö‘†DX›:t0–õ”®]mk¿3µÈÄøüóÚOôùüsyt»vê=Iõeþ|yõÌÍ¥×Ô×GJŸD¨Ï½²•èÓ§Ï?Ÿ': è¸òÏÖ“baÃò4“L"”Æ‘HGø‹øtöîý^ß]Ðþ3<¥¥ihÛvd=SÓš]”ØÐŸDHÊpl!¹³HcMTƒ¬¬ráä&Y¯m[™kBj‰ ”¸#§fmw¥Z……7Hïñ\V¦ßl™¸¸(((¬×´¦ç‘^1€&ªAýµSß]%0З//‘¬çí=´ö;Sï¾›‹k×nHÖëÖMÆæßµ¨Y³fhÖ¬™^û@DDºÃZG˜DHré"Ñgùr™‹‹õlèÐôÝ…:Mi!5lL"lXJJ.ÈZZfi©½g&“¥1€Ö&’\Lô!¹”&RÃÆ±¥aIH؃BO÷ªìî“¥1€Öþ"’\üGr1p&%8¶4,Í›7âk³Hã³®ˆˆˆˆˆ`MDDDD¤ha!ÉuáÂ}w ÄÍ›7ÅDB")[H.Æ,Ò@ë“I®eË–é» d bbbÄDB")[H.Æ,Ò˜D¨#\Or1чäb!)Á±…äbÌ"3ÐDDDDD 0€&""""R€´ŽpA>ÉÅD’‹I„¤Ç’‹1‹4Ð:Âù$}H.&’[H.Æ,Ò˜D¨#\Or1чäb!)Á±…äbÌ"3ÐDDDDD 0€&""""R€´ŽpA>ÉÅD’‹I„¤Ç’‹1‹4Ð:Âù$}H.&’[H.Æ,Ò˜D¨#\Or1чäb!)Á±…äbÌ"3ÐDDDDD 0€&""""RÀ`–p\¾|§NBjj*ÜÜÜàëë[mýS§NaÿþýèÕ«^xás%%%ظq#bccѶm[¡{÷îZm$$$`ëÖ­ÈÌÌDŸ>}sss:åååGLL lll0zôhôîÝ[«-.È'¹.\¸ggg}wƒ @E¡ž{B†€c ÉUTT¤ï&ƒ˜^±b0qâD,Z´[·n­¶þƒ0vìXüë_ÿBTT”ƹââbà½÷Þƒ¹¹9bccÑ·o_ìÝ»W£ÞÁƒáåå…ÇÃÒÒ‹/†¯¯/ Å:‚ `âĉxë­·`llŒÄÄD 8Û¶mÓêä“\Lô!¹˜DHJpl!¹³H3ˆè‘#GÂÛÛ•Î?nÁ‚èÓ§ ´ÎmÙ²{öìÁéÓ§áéé 3f fÏž¤¤$±Þœ9s0hÐ ìÚµ *• 3g΄‡‡"""0eÊÀþýû‰C‡áùçŸLŸ>³fÍB@@LMMÅö¸ Ÿäb¢ÉÅ$BR‚c ÉŘEšAÌ@»ºº¢W¯^²n'9r‘‘‘X½zu¥ç×­[{{{1x???$''ãèÑ£€¸¸8ÄÇÇÃÏÏ*• àââGGGhݺuhÚ´©<€¿¿?rrrý$o•ˆˆˆˆê8ƒ å*((À«¯¾ŠåË—£uëÖ•ÖIII»»»F™‹‹ 99Y㣫««F=wwwñ$%%‰×>ÞVJJÊS¼""""ª«êUýþûï£}ûöâ‹Ç ‚€ëׯW@geei||<8vwwÇ7PZZ*Ö{¼Ž½½=¬¬¬´Ö8q›6mÂŽ;4ŽÇŸ"Æ2–]¸p¡Îô…eu»¬âI„u¡/µU–––®‘{R×úgHeO"¬ }aYÝ*«ˆI.\ˆM›6áÒ¥K êÕ›úøñãø¿ÿû?|óÍ7úîJ¥òóóõÝ2Lô!¹˜DHJpl!¹îÞ½«ï.Ôy*A}wB gggxyyi%Côïß7Ö˜}~ã7Я_?Lž<þþþhܸ1Úµk‡ž={jìÎqìØ1ôíÛëׯGXX6oÞŒä@;v,Ž9‚œœ€§§',--+ÖIKKCçα|ùrÌ›7€ DDŠì)~}d Ð9–€«Aä¾4Æ-ÒêÍHdaaääd,X°@,ËËËCLL Î;‡ qãÆpttD||¼ÆµçÎ899i|LLLÔ ãããÅsеkWìÙ³§Ò¶kðÝ‘”Û·o###]ºtAãÆõݪÇêÍŽ --MãhÕª¦L™‚´´4´oß0uêT¤§§ãÌ™3âµÑÑÑprrB¿~ý={ö„»»;¢££Q1AîÜ9¤¤¤ˆÿ+«hëÞ½{øóÏ?Ų¨¨(´iÓ~~~:x×DDÔDEE!,,¬Æ–a”––"--Í oÕß½{iiib>Ò£vìØ=z ..N=£†Ä 踸8xyyÁËË éé騵k—øuqq±¢¶‚ƒƒ1|øp :3fÌÀ!C°gÏ­mïV­Z…ÇcðàÁ˜9s& oooL˜0A¬3dÈ„„„ 00o¼ñðÃ?૯¾ÒØà“I¾ŠD")I„Tÿ-]º7nÄ|€7nRÃÆZG¸ Ÿäb!ÉÅ$Âúï‡~€……ÆŽ èÔ© „ÈÈH­'4JÉÍÍ•UoÆ ¸zõ*>úè#ôèÑ`llŒåË—£[·nø÷¿ÿ’’kºuë†O>ùÆÆÆ033ìY³`ii‰cÇŽá믿wÂzî¹ç0|øp\¹r©©©âõžžžç΃ OõhéÓ§O£wïÞ°··â6Æ,Ò^6†žtìØQß] Á¤ ’+00Pß] Z´qãF”••¡U«Vؼy³X^VV@=;"»½¡C‡ÊªwåÊ¡uëÖZçìììééékÑ¢…VÝÆ£Y³fuE9Yì]»v!00fffpvvF·nÝ`ccƒÒÒRœ;w÷îÝ“Õ÷ÇåççãæÍ›ü¬¿_Ò@ÕA뜿üòK|ùå—ZçÿøãdddˆÁޱ±1JJJ ‚FКŸŸ¯èu[·nòòräææÂÚÚZã\Å’¡ŠÝ/jÊÌ™3annŽÔÔTÝ3>úè#8pà‰ÛmÔ¨š6mÊUªq\ÂADDTÇ>|)))˜6mRSSµŽ-[¶ ¼¼\㎕òóó‘ ÑÖéÓ§µÚ¯ØîÎ;ZçÅ><îÈ‘#011ëÔ„‚‚¤§§#((Hkë¹Ê–(U×÷ʸ¸¸àäÉ“²ëÉÁZG¸ Ÿäb!ÉÅ$Âúë‡~L›6 :uÒ:‚‚‚`gg‡ 6@øçq áááâÙÙÙX¸p!Í$B[[[XXXàÈ‘#Z3Ô¯¼ò š6mŠ÷ß_cæ6<<GŽÁo¼¡±7óÓ²´´D‹-°ÿ~§ ÿøãØ»w¯VýŠDÀ}ûö‰ï½:|ðîÞ½‹éÓ§ãÁƒbyyy9Ο?ÿôo bÌ"´ŽðöÉÅ$B’‹I„õS^^¶nÝ ggg­¼ FFFFjj*:˜8q"ºuë†+V S§NxñÅÑ­[7Œ7€f¡‘‘¦OŸŽ“'O¢E‹èÚµ+üüü­ZµÂ·ß~‹Ë—/ÃÅÅðòòÂäɓѻwo,Y²¤ÆßóüùóqõêUØÚÚ"((ýúõÃÛo¿7ß|S«îˆ#йsgüë_ÿ‚ œñõ×_WÙöˆ#°xñbüüóÏxæ™gàïïØÛÛcþüù5þ^êÆ,ÒŒ—ÔÆ¿Ò°cÇ4kÖŒ ?$ OH.ggg8;;뻵'¥ 8_öðk/S uýŸ÷IOOGÓ¦M1uêÔj—J888ÀÚÚíÛ·‡ƒƒŒ1iÒ$˜˜˜ÀØØ]ºtÁûï¿ãÕW_Õhoذa2d<==áààOOOôêÕ àææ†   ˜™™áÆèر#¦M›†¯¾ú VVVý033ƒžyæ­>vïÞýúõÓ*oݺ5|||Ä5Ö @÷îÝQ^^Ž¢¢" 8+W®„‹‹ ìììàíí-îDbjjŠiÓ¦¡{÷îpssC×®]ѧOq—6mÚÀÇÇGcçooo 6 –––¸yó&ÌÍÍ1dÈÌ;·ÒdɆîàÁƒø÷¨:|”·ð‘˜DDO€ò&Ò Æ-Òêÿ剈ˆˆˆjhá‚|’‹I„$“I Ž-$ci  u„ òI.&’\L"$%8¶\ŒY¤q1™Žð©>$ל‘\Lð!%8¶\ŒY¤qšˆˆˆˆHÐDDDDD 0€Ö.È'¹˜èCr1‰”àØBr1f‘ÆZG¸ Ÿäb¢ÉÅ$B€   |þùç’õj{lùøã1yòäkoîܹøÿïÿÕX{$ciL"Ô.È'¹˜èCr1‰`çΰ°°¬WÛcKll,Ž?^cíc98­#\Or-[¶Œë I–ŠB®…®Ÿ¢¢¢ðÒK/ÁÖÖï½÷ºt邬¬,DEE!==Ï<óŒX7>>xå•WðÊ+¯`Ó¦Mزe `óæÍÔcKqq1’’’´^K$%%!''G,+))ARRÂÃÃqåÊ̘1¯¾ú*¾ùæ¬^½Íš5ÃÒ¥K«ìÿýû÷1vìX:t›6m‚¿¿¿ØE‹! Æ C›6m™™‰;v °°7¤¦¦jÌ÷êÕ o¼ñ†Æk”••á£>B^^LMMÅ~8gΜÁÌ™3áåå…?ÿü›6m‚§§'¹ÆWµk×ø=’"P­ BCCõÝ ""ò»H^»÷ðH(Ñwt&//Ohß¾½Ð¾}{áöíÛÕÖ511T*•päÈò~ýú FFFׇ„„fffZm „iÓ¦‰e—/_5ÒÒÒÄòòòr¡uëÖBëÖ­5ÚðõõlllA„¬¬,¡G‚µµµV¿úôé#tèÐA()©þçÙ½{wÁÓÓ³Ú:o¾ù¦@X½zµXöÙgŸ „;wjÔMLLaaaÕ¶IŒ[äà""¢:æðáørå Þyç´hÑB²~¯^½0`À²Áƒ£¼¼¼ÒåJŒ3öööâ×*• ÞÞÞ¸~ý:òòò´êŸ;w^^^ÈÍÍÅÿû_­~yzz"''‡‚ Oܯ/¿ü_~ù%fÏž7ß|S,G×®]µÖP»¸¸àÙgŸÅžø5‰*p QsñâEê`SŽÎ;k•9;;€Æ:è'!Õv“&MÄò¼¼<ôïßÖÖÖ8zô(lmmµ®>}:~ûí7¼øâ‹èСF…iÓ¦ÁÝÝ]vŸvíÚ…¹sçÂßß_kì””˜šš¢OŸ>‚  ¼¼‚ 33·nÝB~~~¥‰‹Dr1€Ö.È'¹.\¸ þq"ªNE¡ž{B5­¸¸`ff&«¾‰‰ôŸóꔫ› –Óv xyyaß¾}ˆŠŠÂÔ©SµêtïÞiiiˆŒŒDdd$¾ùæ|õÕWðññÁ¾}û$_ïï¿ÿÆøñãѽ{wDFFjì6RVV†¢¢"tìØQ#ñqµ±CI}RTTsss}w£Nc­#L"$¹˜DHr1‰°þêÒ¥ uÐÛ·oßisÙ²e°²²Bqq1®]»¦1;|õêÕy SSSìܹãÆÃ´iÓPXXˆY³fiÕ333Chh(BCCqëÖ-|ðÁX»v-¢££«ý}ÎÊÊ‚ŸŸš7oލ¨(1á°‚±±1 ,¨‘÷Ô1‰Pÿ ¦#üE$¹<“\ žë©ŠíèV­Z…²²²isÆ èÔ©àСCç"##kä5upüË/¿`ܸqxóÍ7±bÅŠjë·lÙ¯¿þ:õÓ «òàÁøûûãîÝ»ˆŽŽFÛ¶m+­7räHœ;wÑÑÑOþ&8Æ,Ò8MDDTÇ´jÕ Ë–-ÃôéÓ1hÐ ¼ûî»ÛØM˜0½{÷VÜî¸qã°xñb,\¸7nÜ@Ïž=mÛ¶ÕhÿMLLðÓO?ÁÂÂóçÏGAA>øà@¿~ýˆþýûÃÎÎ X¹r%T*† Ve›o½õâââ0gÎdgg#;;[ãü€`ee…?üÛ·oGpp0.\ˆ!C† I“&HOOÇï¿ÿŽ;wî`ýúõ5ú~©áaMDDT½ñÆhÙ²%Þ|óMŒ5J,···¯tm±ŽŽŽøî»ï0cÆ q犊5˳Ó5ÅØØëׯ‡¥¥%/^ŒÂÂB|òÉ'hÓ¦ –,Y‚‚‚±®­­-¾ùæ <¸ÊöÒÓÓ«V­ÂªU«´ÎŸ={îîîhܸ1Nž<‰wÞyK—.Åûï¿/Ö±··ÇÛo¿]ƒï’*•ð4{È,aaa(**ªÑ[dT1‰äª÷I„{Š_IÀžc ¸6ÌyŸ«W¯âÊ•+°µµ}â@÷ѱ%??IIIèСƒ^~ŠŠŠžžŽ[·n¡U«V°··„R“JKKqñâE}¦µ/byy9ÂÃÃŒ=ú‰;ˆˆˆ Õ÷ßï¾ûááá˜8q¢¾»CTë b»+VÀÁÁ'NÄ¢E‹°uëV­:ëÖ­ÃóÏ??þøØ¿?ªQ¯¸¸xï½÷`nnŽØØXôíÛ{÷îÕ¨wðàAxyyáðáÃb„¯¯/ Å:‚ `âĉxë­·`llŒÄÄD 8°Æ³™‰ˆˆê²+W®Ú·o¯çžé†AÌ@9ÞÞÞððð¨t¦Pg'%%‰›ÏÀüùó±bŠ̘1}úôlÙ²{öìÁéÓ§ÅG¤Ž3³gÏFRR’xíœ9s0hÐ ìÚµ *• 3g΄‡‡"""0eÊÀþýû‰C‡áù矠~Dé¬Y³ ‘Á'’\L"$¹ê}!Õ¨Ú[@×/|¡4ƒ˜vuuE¯^½ªýazxxhÏÄ@7%%E,[·nìííÅàüüüœœŒ£Gâââ???¨T*€‹‹ 5Ö­[·M›6ƒgð÷÷GNNŽÖîL"$¹–-[¦ï.ˆ‰‰ ‰¤ÔæØR@?m¢#Õ ŒY¤Dý¤Nœ8pwwËRRR4¾ÔÁ1$''k|tuuÕ¨çîî.ž€¤¤$ñÚÇÛz4h¸ Ÿäc!ÉÅ'’µ5¶äçç#77ÖÖÖ°´´¬•× ÝbÌ"­ÞÐ×®]üyó0nÜ8xxxP¯Y¾~ýz•tVV–ÆÇǃcwwwܸq¥¥¥b½ÇëØÛÛÃÊÊJëoqqqØ´ivìØ¡qTÜ‚­À2–±Œe,{(--]#÷¤®õeóG—oÔ¥þ±Lº¬"&Y¸p!6mÚ„ŒŒ Põêe}éô®R IDAT÷î]øúúÂÆÆkÖ¬Ñwwˆˆˆê­[·nàò j`ÓµkW!44´ÊóyyyBß¾}'''!;;[ë|Û¶m???²ØØX€°~ýzA!22R :tH£^PP`kk+~ݽ{wÁËËK£Njjª@X¾|¹X*Œ?^î[¤îüùóúîˆ7n7nÜÐw7jÏî"AxíÞÃ#¡Dß=2hµ5¶üðÃaÚ´iµÒ>éÞøñ㫵HêÕ t~~>|}}qíÚ5üñÇhÓ¦VGGGÄÇÇk”;wħ"U|LLLÔ¨¯ñ䤮]»Š×>Þ–£££F9ä“\L"$¹˜DHJÔÖØrêÔ)¨r—,2<ŒY¤Õ›º°°HMMÅüQå­¤©S§"==gΜË¢££áää„~ýúzöì wwwDGGCêÀ8%%aaamÝ»wþù§X…6mÚÀÏÏOãu¹ Ÿäb!ÉÅ$BR¢¶Æ–Ьú÷ï_+í“î1f‘fû@ÇÅÅaÆŒ€ôôtܺu ^^^€Ã‡ÃÌÌ k×®ÅЩS'k\?sæLLš4 Œˆˆ :AAAâöuÛ·o׸fÕªUð÷÷ÇàÁƒáââ‚mÛ¶ÁÛÛ&Lë 2!!! Dpp0²²²°wï^DDDhìMDDTÝ¿ñññhÚ´©V‚>Q}f´8Ëòøl‹±±1 _¿~øßÿýßJ¯th333üöÛo⣼ûöí‹Ï>ûLëÖ“Ž;&>ÊûÃ?Ôz”·J¥Â?þ(>ÊÛÕÕ‹-⣼‰ˆ¨A8qâÊÊÊàåå#£zsS›H’AÐ;vÄ‚ ª­ãåå%ÎJK155ÅÔ©S1uêÔjë¹¹¹ÁÍÍ­Ú:FFF Õzdøãø$B’‹O"$¹ø$BR¢6Æ.ߨŸø$BiüpA>ÉÅ$B’‹I„¤DmŒ-±±±@×7ŒY¤Ä t}Àù$“I.&’5=¶‚€ØØX£OŸ>5Ú6éciœ&"""ÅŽ?ŽÜÜ\xzzÂÊÊJßÝ!Ò)ÐDDD¤Ø?þ ÒsOˆt´Ž0‰äºpႾ»@âæÍ›b"!‘”š[JJJ°e˨T*„„„ÔX»T70f‘ÆZG¸ Ÿäb!ÉÅ$BR¢&Ç–={öàæÍ›èß¿?ìíík¬]ª³Hc¡ŽpA>ÉÅ$B’‹I„¤DMŽ-ááဗ_~¹ÆÚ¤ºƒ1‹4Î@‘lwïÞETTLMM1nÜ8}w‡H/@‘l¿üò 1|øpX[[ë»;DzÁZG¸ Ÿäb!ÉÅ$BR¢¦Æ–5kÖ&NœX#íQÝØEhá‚|’‹I„$“I‰š[¢¢¢púôi888`̘15Ð+ª‹³Hc¡ŽpA>ÉÅ$B’‹I„¤DMŒ-K—.,Z´&& !ê+Æ,Ò8MDDD’vïÞS§NÁÁÁË7¨ÁcMDDD’8ûLôhá‚|’‹I„$“I‰§[öíÛ‡cÇŽqö¹`Ì"´ŽpA>ÉÅ$B’‹I„¤ÄÓŒ-~ø!Î>7ŒY¤ñ_ŽpA>ÉÅ$B’‹I„¤Ä“Ž-ß~û-Ž= GGGÎ>7ŒY¤qšˆˆˆ*•™™‰yóæÖ®]ËÙg¢0€&""¢J½þúë¸wï¦NŠ_|QßÝ!ª3@ëä“\L"$¹˜DHJ([6n܈ݻw£]»vøôÓOk©WT1f‘ÆZG¸ Ÿäb!ÉÅ$BRBÉØ’¹sç¾ùæ4kÖ¬¶ºEuci\̤#\Or1‰äb!)¡dl™>}:rss1iÒ$øúúÖ^§¨NbÌ"3ÐDDD$Z¾|9vî܉6mÚà‹/¾Ðwwˆê$ÐDDDøí·ß°páB˜šš"22ÖÖÖúîQÄZG¸ Ÿäb!ÉÅ$BRBjlùûï¿ñòË/£¼¼kÖ¬···n:Fuci  u„ òI.&’\L"$%ª[®]»†€€Ü¿sçÎÅk¯½¦ÃžQ]ØE“u„ òI.&’\L"$%ª[ ˆŒŒ Œ9’[Öc8MDDÔ@•••aòäÉ8vìÜÜܰyóf14 ’¢“è²²2\ºt ‰‰‰HLL„©©)ÜÜÜàææ†Ž;B¥Ré¢DDDôÒÒRL˜0[·nEëÖ­…&Mšè»[D¡Vèû÷ïã믿ÆÊ•+‘““hÞ¼9JKKqÿþ}@—.]ðî»ïbÒ¤I033«Íîèä“\.\€³³³¾»A "ÐÆÆFÏ=!CðèØR\\ŒqãÆaçΰµµÅЩS'ývꌢ¢"˜››ë»uZ­Ý§Y»v-ìííŽÅ‹ãرc¸yó&rss‘——‡ììlÄÄÄàõ×_ÇÇ lÛ¶­¶º£w\Or1‰äb!)Q1¶T¬yÞ¹s'Úµk‡?ÿü®®®zîÕ%ŒY¤ÕÚ ôíÛ·ñÿٻ󸨪þãŸvdqETÄ%÷}É\rE­ÔL±|²òyÚ,Ÿ¬~=Ùª•VfÙb†Z™[â–Kî’Š n .(n€Ê² 0÷÷ÇÀÈÀ ƒƒú}¿^÷5̹gî=³pùÎá|ÏY¾|9>ú¨Éý^^^xyyѵkW^{í5Ö¬YC\\\E5Çâd@¾0—$ sI¡(‹2226l[·n¥~ýúlß¾___K7MT1³”®Âèwß}×ìºjµš   ŠjŠBñÐKIIaèСìÞ½›F±}ûv4h`éf q_’T[!„âM‡ؽ{7~~~ìÞ½[‚g!îA…Л7oæÇäìÙ³ÄÄÄð /ШQ#zöìɾ}û*º U‚$ sÉJ„Â\²¡0Çš5kèØ±#gΜ¡{÷îìÞ½›:uêXºY¢ “˜¥t: Ç;ï¼Ã'Ÿ|b¸¿yóf>þøcŽ?NÇŽÙµk àÆX[ß¹)çÏŸçСCÄÆÆÒ¢E  d²ÞX½z5†nݺñÌ3Ï›Ó2''‡E‹±oß>¼½½5j­Zµ*v¬ÈÈHV¬XÁåË—éØ±#ÁÁÁŲRu:K–,!,, OOO‚‚‚èСC±cÉ€|a®™3gÊ8ha–‚B -LQ…3fðÁ ( M›6eÛ¶mØØØXºi¢Š»v회ƒ.E…ö@/^¼˜©S§råʾøâ ÆŽË7¸zõ*›6m">>ž¼¼¼R{¡?ÿüs|}}7nï¾û.+V¬0YoÕªUtëÖãÇ£V«yõÕWyê©§Ðét†:Z­–¡C‡òöÛocggǾ}ûèܹ3›7o6:ÖŽ;èÔ©»wïÆÁÁÿýï 4ˆ¬¬,CEQ7n¯¾ú*VVVDEEѽ{w“³‰ÈQ˜K‚ga®áÇKð,LJMMeøðá̘1;;;~ù墣£%xf‘˜Å J‰ŠŠRT*•¢ÑhEQ”¼¼<ÅÍÍM™6mšQ½(ï¾ûî©8p@ÉÊÊRüýý• &«“““£x{{+ÁÁÁ†²;w*€²aÃCÙâÅ‹@9r䈡,((Hñóó3:^`` 2pà@E§Óž•••òóÏ?êlÙ²E”;wÊ^xáÅËËKÑjµ†² &˜l³Bˆ;ؘ­(ÿJ½½EæXºE÷…èèh¥iÓ¦  Ô«WO9xð ¥›$î3·”®Âz Ïž=KÍš5©^½: ŸiÃßߣz 4àòåËw>ž!C†Êzö쉫««QÞ‚ hР­[·6” <˜3gΰwï^"""8qâƒ6¬’ؼys7n\ìX...ôìÙÓP6dÈ®^½Êúõëïøœ„Bˆò”——Ç_|A›6m8uê={öäСC´k×ÎÒMâSa´¢(uh“Μ9èÝ û@ŸÄhT§à1õ n‹N,_ôX§OŸ.v¾‚û111Få2 _˜K’…¹$‰PˆŽŽ¦k×®¼ùæ›äääðÖ[o±uëVjÖ¬i¨#×a.‰YJW¡c SRR=z´a‹‰‰áçŸ6*Ûºuk¹œ+>>[[[7nlTH||< ê¯_¿^b]P¯àÖT0ž˜˜Hnn®¡^Ñ: 4ÀÉÉ©XÒà¾}ûX¼x1¡¡¡F[Ñ?~R&e3gά2m‘²ª]V°aUhKE•]¸pÑ(÷¤ªµÏÒe¹¹¹|úé§´iÓ†ððpüýýÙ·oŸ~ú)ÖÖÖF-X‰ÐÒm–²ªWV“LŸ>Å‹…¸³ ›…ÃÕÕ£o¼uëÖ%''ǨÌÉÉ顬îââbé&ˆûDHH¡¡¡–n†¸$ÊçåáÍk¯½ÆáDZ²²bäÈ‘Ì;ooo“õ%AY˜ËÓÓÓÒM¨ú,=»¬JJ"üòË/@‰ŽŽ6*ïÖ­›ÒªU+Ã}oooeðàÁFuöíÛ§Ê/¿ü¢(Š¢,]º´Xr ¢(ʨQ£”Zµjî·jÕJéÔ©“QØØXP>ûì3C™ ÆBˆ» I„&%''+S§NUlmm@ DAQ®$n)ݳ¡ŸŸ'Ož4*?qâ„a@ãÆ9qâ„Q‚ÇÔ+¸-ú/Œ¢Çò÷÷/v¾‚ûE‡’!„÷"77—yóæÑ¸qcfÏžM^^o¿ý6’((D%«°!†ÈÈH³êzyyÑ´iÓ{:_¿~ýðööfݺuŒ1€]»v‘’’Bpp°¡Þ¤I“?~}hÞ¼9«V­¢W¯^Œ;ÖP§oß¾Œ3†áÇóÔSOÏæÍ›ùý÷ß‹MV/+ sÉJ„Â\²áƒïرcL:•mÛ¶вeK¾øâ {ì±2K®-Â\²aéTŠR1óÍ­Y³†áÇÈĉéÕ«—aNå¢<<<îøF]ºt‰ßÿÝä¾7ß|+++Ãýª¸”wA¸\¸„¢ 6iáÏBÿ½{Å*¬ß§J9yò$3gÎä·ß~C§ÓQ»vm>üðC&NœXìošåMâ–ÒUX­ÓéØºu+¿üò ¡¡¡Ô®]› &Lƒ *â”U–|…â.<„txx8Ÿ~ú)k×®EQyã7˜6mÕªU³tóÄCBâ–ÒUØ×XµZM¿~ýXºt) ¼ùæ›üõ×_øøøðè£ò믿’™™YQ§B!î[¶l¡wïÞtêÔ‰5kÖàììÌ´iÓ8þ<3fÌàYˆ*¦RþäææÆ‹/¾Hxx8‘‘‘4mÚ”ñãÇóÁTÆé«I"æ’Õ„¹d%Âû›N§cÅŠ´mÛ–þýû³sçNjÔ¨ÁÇÌÅ‹™5kµjÕ*·óɵE˜Kb–ÒUê@ª°°0æÌ™Ã’%K¨W¯^±±Â2I"æ*X-LˆÒ¬D(î/†9sæÐ¬Y3ž|òI"""¨_¿>sçÎåâÅ‹¼ýöÛ¸¹¹•ûyåÚ"Ì%1Ké*|0ÙÅ‹Y¼x1‹-"!!#F°zõjúôéSbRáƒH²Y…¹dÌ™0—̾qÿP…;vðÓO?ñ矢Õj dêÔ©<ýôÓX[WìŸd¹¶sIÌRº ûm=}ú4/½ô;wî¤K—.LŸ>'Ÿ|ggçŠ:¥BQ¥\»v,X@LL ŽŽŽŒ;–É“'¦dBÜ_*,€ŽŽŽfûöí´hÑ‚êÕ«³nÝ:Ö­[g²nŸ>}xùå—+ª)B!D¥IOOgÓ¦M,]º”µk×’““@‹-˜œÞ½{ckk[­BTE6ôÆ©[·./¿ü2§NÂÔŠá999ìÝ»—±cÇÒ¨Q#>\QÍBñÊÎÎfîܹtíÚ''§Rë§§§³jÕ*žyæjÖ¬Iß¾}™7o—/_¦Y³fLŸ>ððp®\¹Âüùóéß¿¿ÏB}ú¡Xo]Võæ’¤ a.Y‰P”…\[„¹$f)]¥ô@²råJ“û–-[F```e4C!„Bˆ{V)ôË/¿Ì¥K—èÕ«6l ;;›ï¾ûŽÖ­[såÊÞxãÊh†B!„÷¬Rèzõê±eËÒÒÒx÷Ýw¹páS¦L!##ƒ¿þú‹FUF3,Jä sI¢0—$вk‹0—Ä,¥«´iìÚ·oÏáÇIHH <<œË—/sæÌz÷î]YM°(/Ì%‰>Â\’D(ÊB®-Â\³”®R’O:Å7èÚµ+^^^xyyö]¼x‘ÔÔÔ~´ Èæ’Da.I"e!×a.‰YJW)=Ðßÿ=ݺuãõ×_G§Óí[½z5³gÏ®Œf!„BqÏ*mGß¾}Y¸p!#FŒ ==½²N+„B!D¹ª´ºk×®ìÝ»—cǎѳgO*ëÔU‚ Èæ’Da.I"e!×a.‰YJWi4@óæÍÙ¿?ÖÖÖtìØ‘ãÇWæé-Jä sI¢0—$вk‹0—Ä,¥«”$ÂÂjժŎ;?~<ݺu£W¯^xxxTv3* Èæ’Da.I"e!×a.‰YJW©=ÐX¾|9/¾ø"ëÖ­³D„B!„¸+•ÒýÞ{ï¡VÇê*•ŠY³f1hÐ œœœÊå<ÉÉÉlذ;wÒ¡CŒ··w±zaaa¬Y³†´´4zöìɘ1cŠÕIOO'$$„C‡áããÃèÑ£ñóó+V/""‚U«VqíÚ5ºté„ °²²2Ù¾ÅüÁøÑ£ïý‰ !„B‹©”hÜÜÜLîëÑ£<òÈ=ŸcýúõÔ¬Y“?þ{{{,X@½zõøê«¯Œê…„„ЫW/Î;Gnn.Ï?ÿ<Ï=÷œQÔÔTúöíËÌ™3qrrbÓ¦MtìØ‘Õ[·n]ºtáСCØØØ0mÚ4FŒAnnn±ö‰‰á¥÷ÞãpDÄ=?Wñ`“Da.I"e!×a.I",JQ¥"EQhÛ¶­ÑP‘öíÛ£V«ÉËËôlRRR6l˜¡ÎÀ±¶¶6ú„„„ТE |||°²²bРAìÙ³‡óçϰeË.]ºdt¬Ž;R£F “¸ì:uÀÍhggé…w$,a®áÇ?¸Á³(wrmæ’$ÂÒUØWùÏ>ûÌðóäÉ“™WWWÖ¬Yc¨³qãFrss 6”Ill,õX  IDAT Ÿ²nÆ tïÞÝp¬~ýúQ¿~}£c…‡‡“˜˜ht¬99aíí gÏrôÖ-:>ÌÛçÏ“­ÓÝÍK „B!*I…ÍÂáîîÎÉ“' ÷µZ-»víâé§Ÿ6ªW§N{ž°ÛÚÚšÏ>ûŒÿüç?4nܘþýûóÏ?ÿpìØ1^ýu6l€££#_}õ“&MââÅ‹xxx°lÙ2ž}öYÃГ&MbÅŠôèуáÇsøða¢££Ù¼y³¡Ž••óæÍã‰'ž ÿþ4jÔˆ+V0tèP†Z¬ÕNžÄ#îé9·nQ]«åŒŸ9ŠÂ§—.±Z£aaÓ¦tvq¹§×D!„BTŒ ën×® ,Z´NÇ’%KHNN.6]TTõêÕ»çóM™2…ýû÷3bÄ’’’èÝ»77n,–DÌÎ;ñõõÅÚÚšü±X"£‹‹ [·nå­·ÞâÖ­[ 0€ððp:tè`ToÈ!ìÝ»—víÚ‘““ÃgŸ}ÆŸþ‰µuñï%CºvåÒš5F[¶mDþú+Û¶åggNedÐíÈ^‰!£Pâ¡xxH¢0—$вk‹0—¬DXº [‰à‰'ž`åÊ•¸¸¸ššÊc=f´"a^^>>>L™2…ÿþ÷¿Õ ‹ æÒ¥Klß¾½Ä:¹ŠÂ—/óþ… †a>öö,ð÷§»{e5UT²Z˜0—¬D(ÊB®-Â\²aé*ôJôÇðã?rüøqúõëÇàÁƒöŸ;wŽ>}úT‘ͨJo­RñVýú ÷ôä¹Ó§Ù›’BlV;Æ¿j׿ __\Lôl‹\°„¹ØÀYT¹¶sIaé*4"³²²âÅ_,q¿ŸŸŸüBÑÔÑ‘=­[óM\ïÄÆ’ž—ÇO ü•”Ä~~ *²ŒB!„¨\[‰P”L­RñJݺœhßÞ0|#.;›Á'N0.:š2Á¹B!„ÅH]Iîf@¾½=ÛZµâGÃðß®]£ùÁƒ¬HL,ï&Š*B}„¹$‰P”…\[„¹$‰°t@W’k×®ÝõcÿU»6'Û·7 ߸®ÕòdT#"#¹ªÕ–WE1sæLK7AÜ' ³t3Ä}B®-Â\÷³<,$€®$÷: ¿ŽëùµY3ªÛذZ£¡ù„\½ZMU„äsÉJ„¢,äÚ"Ì%I„¥“ú>ót­Zœlßž'jÔ 97—‰§N1àøq.eeY¸uB!„>  ïC5mmYÀª€¼lmØœ”D‹ƒù..Ž ›Ø[!„BH]Y*b@þˆ58Ù¡¼¼HËËcÊÙ³ô:z”˜ÌÌr?Ÿ¨’è#Ì%I„¢,äÚ"Ì%I„¥“º’lÙ¾…C;ðøØÇùá—(¯ Ý­­ iÚ”-[RÏ΀Ý7oÒòàA¾¸|™¼Š[hRTIôæ’$BQrmæ’$ÂÒUèRÞB/88˜EGAN1N4½Þ” ¿o fÍšåvž´¼<¦;Çññ†a\\XèïO@µjåv!„¨²”· HâéHteQåßZÁ-ÿ[jyˆ‘Ï,×S8[Y1ßÏí­[ãëàÀÔT9|˜.\ G¾+ !„BÜ3  -ÅŽØá•”û¡{¹¹q¢}{^¯WµJ…V§ã.Ðîða§¥•ûù„B!&@W–ÜâEéMÒYµqU…œÎA­f¶¯/{Û´¡yþðã·nÑ1"‚·ÎŸ'K§«óŠ{'‰>Â\’D(ÊB®-Â\’DX:  +Kº‰2ˆKŒ«ÐÓvtqáHÛ¶¼Û Ö*yŠÂ¬K—h}èÿ¤¤Tè¹ÅÝ‘Da.I"e!×a.I",ЕÅÕDY½y”w·¿Ëùäóvj[µš}|8ض-mœœ8‘A÷#GxùìYÒóò uãââ¨Ý¨Öqg’´!Ì%+вk‹0—¬DX:  -)2ëfòñži<·1}õá·¿‘™[1s8·vrâ@Û¶|ìãƒZ|G‹ƒÙšœ ÀÔ?FóÄ|2wn…´A!„â~'teI®ÚNBýëõiÓ¦ ;.ì`ÜŸãðžíÍK^âpÂáro†µJÅÛ p¤];:»¸p!+‹ÇŽcìîÝ„i4äÈ»wK/´B!„ @W’Vç`ÖýöÙ"_üiÅè“zmFäK‘¼Þùuj8ÖàfÖMæšO»ÛÑúûÖ|sà’2“ʵ=Í kÓ†/7ÆÑÊ €¥sç7lû÷—^h ‘Da.I"e!×a.I",ЕÄCÓ²õÛ›9 Ssó¸åáÁkŸ~J@f÷›MÜÔ8V>¹’¢Véßšc׎ñòÆ—ñžíÍè•£ùûüßè”ò™AC­Rñjݺœh׎.Z-ää@:ä¶jÅüíÛ‰HL,—s óI¢0—$вk‹0—$–NV"¬ÁÁÁ°h!…Êb/GŽdîÊ•&s%õ !GCXxd!±7cöÕw­ÏÄÖ™Øf" \”KG¿ô˺v5Ð= §Oã7q"AžžyzÒÁÅŰ&ŒBT(Y‰P‹•K'=ÐòEݺ¼1gN‰ûëºÔåÝïrî•sl¿±c±·¶àRÊ%fìšÏW>ô[Ò?"ÿ ;ïîÿÝÇ^Æ8xhÝNâLr2³.]¢SDõöícÊÙ³lMN&W¾{ !„â!$´ÄVuêPßÙ¹Ôº*TôñéÃo#~#ajóŸG¯Û‰‡Ÿÿ›1«Æà=Û›—7¾Ì±kÇÊÜž©Ìåü±ÏE©‡§ÎƆ^ç¸ìl¾‹‹ã±cǨùÏ?L8uŠP†ŒBSá !„B<È$€®$…û‡¿Þ‡5 K˜1Âá”ÕÝìݘÒ~ “#ˆ˜Á”öSp·w )3‰o|Cëï[ÓöǶ|wð;nfÝ,µ]%ö>çÓµj…ý™3œmÕŠo›4¡¯»;Ö*}8œ›Ëâ«W ŠŒ¤ÆÞ½ŒˆŒdɵk$çšXvQ˜M}„¹$‰P”…\[„¹$‰°t2ºs~Ñ"v“?öÙÊŠ¹¦zl=<à±Ç ýæí]ê±³r³X}j5?GüÌöØí(Ü~;í­íÙl$Ï=ò½öBebôò“'³ÒϯÄÀúøqþëâÂGo½èçu «56'%‘Y$ð·V©èåæF§'Ã==ñ¶³+õyˆÛ‚ƒƒeÜ™0Khh(Àƒ»˜ŠŒ.WrmæêÓ§õë×—ÏËH] ‚ƒƒ9þÇDdg3¥n]þ»cõ/^„M›ô[d¤é€ú`º{w°µ½ãyboÆòË‘_9ÂåÔËFû¹7bb뉷¦®K]Cùw‹sâôi@ß›{9–É7¨åY Ÿú>¸¹¹гCF›æ‘‘—ÇæädþLLdýÜ,Òû¬:¸¸’ýK{¹„BOh!,B’K't%fÏÆüyý:?›šy#.6oÖÓ[·Bþª€FªUƒ^½nÔMš”x>¢c˹-ü|ägÖž^‹6OkاV©éïÛŸgÛ<ËPÿ¡ØZÙ’ššÊø)ãÙ“¼‡¤zIà$BÍØš μYó°±±)õyæ( ;oÞdub"¡ Zm±:Í«U3ÓmÍ.„xˆI-„EH]:  +App0‰×¯sl÷nöž]·ŽÅ¯¼ÂÈ¡CË¥]U…$úsI¡( ¹¶sIaé$€®ö¯ôtؾýöpsçŠ×Q© mÛÛÃ=:u‚B©_Mg¾N¼…ÏN³[ Ÿ×ó ,ê$µªÕ*×§p]«eͬNLdÛÍ›h‹ W±U«é“?£Ç0OOjÙÚ’MÀð᜛:•À9s8þ×_åÚ&!D!´!C8J't%¨´â¹s·göرC`åê >j¨Û£Áᣄ¦–|ØNîî †fžÍèÕ°½}zÓ³AOjV«YnÍOÍÍ寤$þLLdcR·ŠLõ§V©èìâ‚óÚµlur"·m[œ7nä—®]¸^h!@ a!@—N®D__˜2E¿iµú!Ã=Ž××II?ÿÔoÀF7&©õóS›ê…Þ©†³Þ@=ýýhM4Ñšhæš@óÍõuÃÞôlØ“Ž5LÅ<.ÖÖŒ®Y“Ñ5k’­Óñwr2«5Öj4hrrÐ) ÿh4°s'üï¤=öÿ™5 û®]iæèHC{{Ô’Œ(„Bˆ $=Е  ‰péÒ¥–kD|‚÷ß/ñ±* ½½!¨nV­š!Èö4cŽëÊ$‰>Â\’D(ÊB®-Â\’DXº.€>räýõQQQxyy1xð`úôécTgß¾}„††’œœL=xúé§Qù·vv6!!!„‡‡Ó Až|òIš5kVì|ÇŽcåÊ•ÄÇÇÓ¹sg&L˜PlÑ‘*?–(9™S 0ûí·ù©ÐJ‚O?w\;°fMt> ¹îåÌ)×öÚ^ãoÕyN¹æpÕɸª -j¶¸P7ìIu‡êf5ñÕ1cØÁ±çžCסC±ýÖ«Wãsþ<µ‚‚pn×ŽèŒ .fea·»ºÍíÀºP¯uy ‰‰‰¡sŸ>\:}‡{:–  …°ˆ*·TÔ•èÛo¿åÕW_¥S§N4kÖŒèèhNžl8ÞË/¿Ì€ø3?!ïù矧M›6¬\¹’±cÇVÆÓ.WSçÌáÓ½{™wå ó6ä§+À±HÿóÆuáŸãâ Ð?4l³rht%‡F&Ε§†K® çÜsÎã8ûÜçò›‡ uã&ø´}”ÎÍúѳaOÜíÝHsq!áÙgKn¼µ5W‡ !×ÞÞPä VÓÚɉÖNÆ]áyŠÂ…¬,¢M×É…zà³t:ŽßºÅñ" ¿¨U*ØÙ )èµ.<$&&†ˆìlrûõã×Y³xëßÿ–^h!„âðÀÐ3fÌÀÉɉýë_Få…‡S¬_¿FÃ!C e=ö„„„è àïïO“&MP©T <˜)S¦päÈÚ´iÃÞ½{9sæ Ó§O7«uëÖÔ«Wbtvvñ¤»ª¦N:ØvéÂ’U«xä©§p,<ƒ>Y¯zu0Õœ ±±ÅësçôåYY†ªV:ðIÖo}Ï”*Àà ‰ÕæsÚ’¼Ý±jâÇ?rqÔ¤PûÇq1Ñöë*¸nmE˜‡£† »ãó´R©ðupÀ×ÁÁÕ‡\Ójõu‘àúJv¶a8ˆNQˆÍÊ"6+‹¿nÜ0z¼mz:nññ¸%$pmÝ:RòÇkÇöîMgZÛÚrS«%àñÇùøûïM¶Ï‰>·nÝâ…7ßä×ü/–âþðÀ'Šr%I„Â\ÙÙÙØÙÙYºUÚ@‡‡‡Ó·o_~ýõWÖ®]‹••}ûöeòäɨóÿÝ~æÌš7onxœZ­& À°ô=‡ýúõ3:~ÁcbbbhÓ¦¡~`‘¥¹›7oΩS§ŠµïÚµkåð,+ÞÔ9sè½{7Gßy§ì¶³ƒ¦Mõ[QŠ¢Ÿ ÄDïµr.•Æ8­‘®ß¸’ Âéd€åEô©£›«é!Ö¬wwýæá¡¿5õeÀ„Z¶¶Ô²µ¥§››Qyz^§ êBÁõÙŒ r õºk«Uãz“&\wt„úõ «@*:pÜÇÍóÏ“«Õr­qcÆž}š)S¦°gÏ~ÿýwâãã©V­ 40z|`` [·nôC>’““‚l€€€àv Z­6YoÇŽ(Šb”˜¨ÑhX¼xq±?üݺu3ê= µhY:u8“PþçP©=xP_Ö£Gñz¶¶†ÀzåšÕø¥_ÇñÂY/_¥æ,Öë ÐØ ôBÑ—…SÓaøA¡G³øw ÷Ç…Ýlmñ,¦ÝÝ ÕjéÖ¨ž^^† ;ôüyºuìˆgƒ†à;tϺõê…§§'miëìLhh(¯æ?·\Eá|f&!+Wbݲ%íìXNÚŠäèÇŒ»º  BÜo¿ÁäÉ\ËËcÿõëú†…A` ¡“&±æ³ÏPâV½º>À¶µ%wÏštè@Ýš5 AwÌßÓ½[7{yQÃÖg++³ÞøË—ñß°'; ]³fä¾ó§ÆŒaÁTV981p E?“?|ò 5’’(qšÊÑôtFÕª…Mþã蔺 Ä—…ƒÊnó'¯½Æ™cǸ¤ÑPËÕ;’32pwt4*ëĸÿü§ÜÎ[8›ûØ7^z  ZþP¢Ü¼<þض îÝ e9ŽŽ¼øÊ+»E;ÆÕøxj÷Ä~ŸÎ..$&%‘á“MJÄ->Œ¿Ÿ]{ô y‹•Þ¾‚²Û¶±~Ý:üýüpvvF«Õ²açNêU¯n(hÙ¦ gbb,ö{¤ÒéÈLOgÀ£òÇ’%ì?p€+7n0jà@C½C‡4r$]»w¯ôö”íܶ«ññ†÷×ÞÁÍ»w3¸OC™³³3-Û´¡y‹»6}ôÁÔòô4¼¿9¹¹,\º”±Ã‡Ê4ÉÉŒ;Öb¿G±±±ÜÈÿ/é¾}ûð÷÷çô¹stnßÞP@§Nˆˆˆô‘¦&LÅ=tÁD"—/_æòåËx{{ðþûï3cÆ Þ|óMÚ´icÉ&ŠÒ¸¸@ëÖк5ÖVVxº ¤g¦ðÞ#M¨Þ2‘Æið×è›?$X¯†U ÜqÊ ­®^Õo2¯mŽŽ·{²=<ôCQ5oo¬ÝÝñsw§ÃùótspÀ³A>;|˜O33¹Y´—´];¬øGòòP¹¸ ÉÉ!1'‡’T€äÜ\’ss9›™ ©©¸vÍh( /ê_»‹ýÒçN§OSÛÉ ¯5 =Ûšë×9“@C޶¶ úè#f„…ñT\Ÿ9:ryòd°±áz›6ìØµ‹§srXU¿>Ë'Ofýúõæ½N`ЈÔ˜1ƒ§òŸsK77r_{Zß|ÜTý+7ÊÅ…¡Ï}:±±±4lØÐP¯}ûöXYY±ÿ~ªW¯N¿~ýŒ>Ù¹s'½{÷fùòå<ñÄ„„„0qâD:DÛB¼pêÔ).\¸`(“é`î]¿'ûñ·ÿß` ®;aé˜_ÚÁô!  ×ëà~ ÜÁ#Ü3Á=ËôÏÕ³ÕÔ̲Æ-SÁ>+U9þ ¬Y“Mß|5‹/s® çÿ~ý•õê½=ØÛ“ãèˆÆÕ‹ ''Ñ8:¢±·Gcg‡ÆÖ–D4VVhÔj4@VñÓÞÌLÔ³f¡+˜C[«Åéwh:jê¦M ð÷ÇÑÊ GµúŽ·%”Û«ÕÜË ”ÜÜ\F6kÆê˜"U*ú ȵ7ߤє)9y’àÕnÝX²gO9¼wO£ÑðÚ#°äòeÚ׮͡3÷Ö[,IJ"áïÏê“' CÊ,!&&†y={òU|<Ù@@ãÆœ›;—ö“&q >€'7fÑñãMxÝ¿w/Û† ãM+t<Î R;v"<âÜø€1ñÓ±c8I®L‡`ó A¼£Ñ ´lÒ„˜Ù³éù¯±3ÿ¿•OûúòÓñã¦sJ,ÐÎl  IÎ}þ9½^xù U¡·ßw};[øú3{6}&Of[þëYÕÞ÷l Ðϳ_|Aÿ^`SþïQp“&üpâ„EÇoZ·Ž˜ñãù÷Í›ä-||8óÁ<õÊ+ü‘¿˜ZI¯§Ä-¥{ z üýýÉÌÌ4*+¸_ðöóóàäÉ“†Z§ÓŨB=.7&**ÊèX'Ož4ì+|¬'NÐ'Ož4™¤q?$VeFMàಃÜly“”®ðÎqè›óÝ ;Pý/f~ø5j/5ç’Ïq.é1ÉçˆIŠáJêtŠ®Ðu€~ñ+>¸.²kf[ã§®/4ÈsÆ;×O­î ÕniQݼ©_É1922ŒÚmÖÌdð  tìÈ’ xkãF  vþp (-Í'ÝÞ«+‰nnúà»Ð–èáÆÝMÁ>ggn89‘gj—|ŽØäX“K“›¢V©©ëR—Æñu÷ÅßÙ‡¦V5i¬ªNýx··oçCýpŒÂ[f&Á¹¹„ÜÅës'ŠJE²““Q }ÅÙ™© Y_|a\Y«Åö½÷ð™2… ;;2ìíɰ³#Ó’ÓØXض &Mº]öÑGðúë%&ˆZ) 6yyØæåaS°ét†Í¶È}E¹½OQ ÷mt:lEÁ¶ ^á ý4™¡r5*Š _}¥Oª½|™.³f‘åïÏ”=PT*tj5y*ºü-/¿Ìè~Ñ}pûq`Øwöða a‡Åöé '¿\§R‘ªÕsö,×BCɘ5Kÿbefâúþû8ŒM+??¬T*T`øb¸ÍÿV¦}*UÙ—nMJ V&Ó~›=ÚŽúY¾~ìI¿¿‡ýãÓ¥S'¬­¬LŽÜº—2Ðÿ¾+3ñ§R’ÒÒˆ‹ˆàâ–-Üš9S¿#-}„CPÚ¶ÅÊ‚ÿu(”–ÆÁ÷ß'×Þþv;SS©ùÉ'8 F‡ví°*á‹gÑ…ÆT&^’¾´šÌ¹-[HûüsýŽ”jò öƒU©÷=áÈ.lÙBZÁp’´4jþ9Ò±}{Ãë©*ø}Rãû›û)|¿hEA¥VëoUªÛ·*ç/]"içN8À/¿• ââxêå—Q¹¹•Ø›/+–îé~æ™gX±b­ZµbäÈ‘œ>}šˆˆ>üðC5ÒÏDlkkËܹs?~Ï–àæ={B½z·Ë\\¸Þ¤ xyq±ª´ÓÃÒÓ_OWWš4zõªÖû~ó&þ{ïìL‚Ô­ËyS×ÒfU*i¿©~΢eù9 Åx{ëÔGº}ü:uØáçǤG)±7_b–Ò=0=РŸAcñâÅìÛ·† FÏž=‹Õ“¥¼ïoçÎcgØNŽœÿ64ñë7ˆr¿†ý·ßâõÜs†×§ØkVÂëhNYY©hµœ !÷Ãw¦¥á0{6õóÛiiJn.ç.,ÞÎÔT¾úŠúõ(“½°¦Žgª^ `YŽ©ää÷Ãä~ô‘ñΔìçÎÅ+­‹¿ï¹¹ú÷}Æ ãii8~û-ÞÏù¼¡×úóퟓH¢~§.v„ž[áRãÇej3‰Hˆ(S;¬Ì ´‹nN¶Å{žó nÝ);ÜÖ–„Ö­Y´v-*9‹\ 8åo#œ¤~Gæxl,?}üqe5ÏH®¢£(äètü½c“Z´àfÑ¡.õêQ[«emÆxÕ®ýb>j•êöÏ`|¿ÐÏ(Šé JÞw‡í£~àƒ  âOÆÑÛ–-ù¬V-zuèpçs™:wiuÊxÖÊ•üÓ±\/ÒN77\kÕb½¢àãs7o[¹šñÇ|ljnggœë×çwEá‘üœKúhÅ >0ÕNœëÖe¹ZMËJ¼®–ä“•+™aj1,WW\½½Y“—W%ÚùÑŠ|0thñÎÎ8yy±ÎÚš¦õë¶ Ü©ì^÷þ}ÒéøjÓ&¦÷ëGVÑ/1uêpàæMâââ¨S§ÎŸ¬0éꮪ‚ƒƒÉÎÎ6šÕC<˜†ÆÚzk¡à¿Œ:¨vÒ ÇÞqÐ&§ 4$)3ɰ%g%“‘“À¼œ0³Ø¨mpw0îåÞ6#‚LC÷ŠM2ÔÈ„«. +ˆ\³:xû±aÙRl°·¶ÇJe¢×µíÛ¿Ÿî³>!ï•×K¬c÷Æë\Û¶ׂy´-¤ýСš2Ÿ÷¹ÀåËŒ;tˆ%_]®ç,kavv6͇ áüÛo›®‘A»ï¿çàÚµåÕÄ»¢Õji1|8¾½>`ÄžÛÓA~=âQ>Ù’Bß+øû×_-ØJ}¢zË‘#‰É_q´˜´4zþú+;—-«Ü†‘M‹  bFÖ/ðTTZ½—.e{þš –’M‹aÈyë-ÓRRè³lÛª@;GŽäìo˜®–FÿÕ«Ù´paå6¬ˆœœZ ™ÿþ×ôð¸8žúçþøî»b»ÆŒƒô@ßÁÕ]•Ý/+Š{3ýßÓÙÿÎ~®wËï6S žúÑõY¿l½a¾ò²ó²?a<ï½÷žQpmjKÎL6üœšŠRBjLŽ.‡ëé×¹ž^¨+Ï(Ô+ž“¿‚n¼¾ÍäÁ —¨ñy C=µ öÖö8Ø8à`í`øÙÞÚÞ辩}¦êÝiŸƒµA“'‘7â 8z´Ä×<»]{Ƽ8™¿~ÿãïLÅÚºcç40<Ô«Ç®ÐPÊu¸QYW"ü|þ|.?úhÉ9çíÍÎ={èeÉù€ü‘‹}úà«+¡‚«+'lm‰:y’€"‹YU¦Ï¾ý–‹}û–\ÁÙ™h''"Žá ®Eðùüùúvþþ»~ˆQQÎÎD9:rüÄ ZY]·2Íþþ{.Ýéõtu%ÊÞÞâíü|þ|.äO:`’³3G¬¬8uú4Mý‹çÝT–o.äR%­®S‡½É^hY‰°t@Wù >:uèĨGF±$b imÒŒÐå‚Ç?LvºÉàÀÎÊŽe¿–½·*OÉ3 ¨K º³ôuŽX!›B38ÃÍ‹Táv0/G—CŽ6‡4mZ™ÛxW²làx¡±º Ø^máÿÜk!×¶wÑGVªÿ~ú)9M›âò×_€>s?3#ÇB yIvv¼7{6?õä”u ï°ðpþŸ½;‹ªz8þDveQ„QPÀ­pßÓLÉ5÷…4¿-–fVj»–i«ZYýÌrËÜ55MË-÷E•DV‘íþþ0&Gf†eèy¿^¼lÎ=÷Þs‡Û‡3ç9§Qv69Üïé‰ÁÛÇG=Å^nV›ví2iºq#îvv¸]K¡Fbsòòò¸›™IýŒÃd9Ü_)5;5•E«V1wæL“µsÛÎÝdíüvÑ"j7lÿLË›••EÊíÛÔuqQ·3ëÖ-æýðŸ¬ð‰Yt“!F ƒñÿ{¯X̬og‘V'ÛÖ·qÊpÂá¶ >Z@×NÅô\ÉÓ£ŸfS½MP\^b< rD=¸›s—»¹w¹›s—¬Ü,õßÍýçu1Û ÊròsŠ9Y€:ka¾ÁœŽ“;ÒÌ¥™úÇ×És3ãôDGG«‡S§AƒEþñ$´Ø– a÷ÿÐËÊÊÂjjMhR±û}RSS©ñð*¤Peigrr²ÞC”L)--McïŠ 33“ììl²øøxÜœ‰°µµ•ÉJ b?‰„¨¤žñ,!ÃCˆŽŽ&æR MüšP¿~}S7KíÃ7>äèä£$u(b¡¼.x±pãBjÖ¬Y&çÌSòôº ¶¾ós┸¢'˜¸¹¹ì¼´ScjAËj–4­ÛT#¨t ÄÞ¢ì?ä¼½½Õ‹,‰òaeUv³Ð”§Ê”Båigež <ØØØZÇÑѱˆÚÂP@‰¬Døß£R©hذ! 64h¿¨¨(­«Y–¥@ÿ@ÆtÃG~àVË[šC5îó~gfMšUfÁ3@5U5ì,ì´Î R”ôþéÌ<9“»^w‹¬c{Ö–î}ºs­æ5N_;}?“ûãÉ'çxâ¿‹©PáUÓKP7wmN3—fÔ³/û,ôüüür_¶ÛÐ$BñßfŒg‹¨d%BÝ$€6I"úš3gŽQ¾6›óÞÛøï~ù.©v©¤Ú¤R3½&µsj³hÞ"Z4oQîmÐå͉o²îÉus:Z:ÌÌ®˜Ñ»AoV¿~Üx¾’Ï…›O WÿœL:©N TPîÏç}ûoÖ[§>Ž““FOu3—f4vjlð¬#±±±¼0õî$™Ÿ‰Êަ4å»Ï¾+—?C“Å›±ž-¢ò“$BÝd ´ÈX"QÑݸqƒØØX7n\ᾊ¼víCþ7„ÕOæžÀMpºäDz=X4oÖÖÖÅ#1=ñ߀:ñ$áIáDߊ.ræ+s+õæ.÷{ªœŠìAÿ%ô¦}5øÖñðÀb~fÉf¦cnfNÓºMiZ·)#FªËãSã ¹tç’z{VJÖý¡%ÅÈiÃ×뾦æc5©_£>î5Ü©k[3UÉ K“D˜––ÆÉ“'¹sfM›€………îM ;;›ôôtì²ëbQÁ?¶*âtfäÙ"ô%I„ºUì'Q"I„B_’èSñ¸×pǽ†;Á>ÿö€§ÜKáTÒ)N&䃭p‡;ÅÄÂ/‡3`õ¿[W7«Ž›½îîÔ¯Q_X«ÿÛÁg[ç"ƒì’$æçç3óÓ™,Ù¶„[N·H³I£æ†š8Ýrbö”Ù ì;Pïc•·ðÓáDo>K¶Yyæ¹,qiµ2YñÍ ¼¼¼LÝ<µÈs‘¼ôöK$¦&rWu—f5ðs÷cáç +Ô´a~ø!¶um '37+‚»óæÄ7Ë}ÆCäååñã²Ùº{+7SoâáêÁ¨£èÑ­‡©›VHxx8þõ'‘Ñ‘thÕî]ºW¨)K $''sôØQÎÿ}ž A4kÖ¬ØÜI"ÔMÆ@Œ%¢jëØ·#5û«ÐÊnç€ö†ÛÜÌü~ýP`ý`Àíbç¢wOö€Ñئlã®÷CS*PóHMÞíó.“_šlX#ËÁ;¿ÃÅõgéžßA=øü 8kE½ƒõøéýŸ*D@µpÙBf.›IÂc  ©’Ux…{±nÁ:M×ÀìÙ·‡qSǃâüïǾu´5~I~lùy ÎÎÎ&lá}ç/œgØ‹Ã8çvެY`¤A­èZ´·kÏ/ÿ÷ ¶¬ði*wîÜaäK#9œz˜äzÉàf7Ìp»äƳ=ŸeÆ´êÕþL)//7g¼Éê=«¹ér“,Û,R¨T›Ï¦~Æ€§µ¯T%q‹n@܈BTmÿ·øÿxcǤ5*z‰óºûê:;»úv\I½B|JŸù¹ñ÷žƒz²§Æîy<4ŒTZGj1³ÿL&ŒŸPh?‰[t“!BQJÏ?ûÆå&—™úÍTžyöT¨0S™¡R©P¡Òø×LeV¨¬¬¶½:ëU['ïWÄÅ@´W4Ÿýð£CF«÷4Ž :ÏÃeºö)nû¤÷'ߪè÷ ˆrâçU?2*D½¿±Mz{7û»èoHjÀIó“lÚ²©Lu õÖ¬·¸à{Akð ï’ÏÎ#;9tä­/ù <¥õù7ŸY7Rkð åŸÅ/»áù‹Ï¼VYZ¾j9GòŽžTp+èŸ-ÿŒÁqqq1~+9é6‚îÝ»ÇÊ•+MÝQ H¢Oå”––FÈË!쿹Ÿkn×ÀT7T¸Åº1¬ã0>yÿ“2gzóîMâS≌‹$!-;fw õj?d°è«ãàùÀV w™4µd~ž†'£»iïÈþz™¢ÿØè~žìzÞY^t·íê’«Üëw’¢òMS æÙšÔïW_ë± ý·¸k-êßóp§·Ž\‚›ÐðZCš?Ó¼\ß³â¶-û` ÝÔC‹´ºÝTÝè2´K±ïÉÃePº?ð,ûâ­/ˆjU|;ã`n«¹¼úÒ«ÅÆ ÃÒÒRz ‹!=ÐF"I„B_’DX9ÙÛÛ³né:"""رw§ÏŸ¦]Çvôø ez®ÚÖµ©m]›ØC±4¤¡Ö$›wo ª¿ûý;nq«øƒç†-ÀXöô騭Æý`¿¢³•£  î·2V÷UÁû´¢þpûÚmn_¿m¤Fi‘­GZpñ¯‹\<{±Ü›S¤LtߣuàÏmògÝ?Ñ"í’ÑÝN7Ø}hw¡Z’u“ÚHäFú’à¹ró÷÷Çßßß(ç*nö‚ ;Ðùß¶pÏp¶ÜÝÅ,ܨº¢bêÀ© 5Œ|%_ð=üoyn›qt§òOñ‰0¸ó`‚ûk¥(tž‡ËtíSÜö{p‰Kë:tz¼];w5ÙûùÛ¶ßH&¹øÞò[àííM€o€ÖcúïÃï›>ÿž­v–L2‹?S ¦KMœœ‹(--ØØX5jT1—ñÞ–MÎêt’““IIK%ÿ% ¼û5®m½pá»þÚÅÙè³thÕN:Q·nóœ™HHHOôz‚Ml"!)??FI‡vLÝ4 ¼ÿéû=s”´¬4jÛÕ¦ßý˜ð\áùŠM)::š—Þz‰«iWÉÈÍ ¦EMš=ÚŒ¯g]!{)ðÇ®?˜òÑîp‡{6÷°NµÆËÉ‹¥ó—¹jb×®]iРääCh#ÐgBò^xŸþ'§¢æB»øøxÖ­[Wâ Jˆ k[6„=Ð6ÉšHꎨxòóóMÞã¬KFFW¯^ÅËË‹jÕŠŸjGRÑMžDDVVƒ ’›UÌÅÅ…¬,ÃW¯BQ6*zð ÷¿­4õxüª¤âÿÆ…B!„¨@$€6/„(k%I"ÿ]’ ,ô%1‹n@‰¬D(„(kûöícß¾}¦n†¨$æÌ1ýŒ ¢r˜E7m$²ª¢¬Iâ¨0„äØ}IÌ¢›ô@ !„Ba  …B!„0€ÐF"ò…eM’…!$‰PèKbÝd ´‘”v@þС¯qúô-õjÔHçСÐãóÏä‡vP­šU±õrs8p`¥,ú"„‰$ÊXh¡9sæÈ8h¡—k×®É8h$€6’ÒÞˆqq·8wn‰Îz>>!%>Gtt<.Ì<‹­çìüééé¥  oݺE^^uêÔ!77—ãÇ“™™I‹-pppкO^^/^$::///5jTìjJ‰‰‰DFF’>úh¡:999œ?žØØX|||ðöö.4!~ff&©©©899annÎßÿMll,íÚµÃÊÊJ£Þ¨S§þþþ…ŽsãÆ ÌÍÍ©Y³&™™™9r{{{š5kVè:222HKK£N:T«VK—.A»ví¨]»¶Î÷Wü7Hà, !Á³Ð—ϺÉaO=õ=öûöí£N:´nÝš®]»âîîÎúõë Õ?rä5Â××—àà`š4i‚··7(T7..Ž=zàææF÷îÝyê©§ðöö¦oß¾õþøã<<<ð÷÷'88˜F@DD„F½… âêêÊÞ½{yüñÇñöö¦{÷îÔ®]›åË—ðÎ;ïP³fMžxâ š5kÆSO=Eff¦Æqüýý6l .ÄÁÁ.]ºÐªU+}ôQN:¥Q÷믿ÆÕÕ•ýû÷€——}ûöåèÑ£%z¿…BQv$€&“œœÌðáÃ7n§Nâ›o¾ÁÖÖ–qãÆ‘’’¢®wõêUºtéBFF¿ýöׯ_ç÷ß'''‡®]»réÒ%uÝôôtÚµkÇÁƒùöÛo¹t鉉‰lß¾-Z¨ë={–'Ÿ|vïÞÍõë× #11‘Ž;róæÍBí=z4ÞÞÞìß¿ŸÕ«WÓ A^zé%^{í5~ýõW,XÀ‰'1b¿ÿþ; ,(tŒ£GòᇲiÓ&®]»Æ¦M›ÈÊÊ¢k×®¤¦¦ª?tèPºuëÆñãljˆˆ 00°´o»B!J©JሊŠbÆ øøø0`ÀmŠ¢°bÅ öîÝKÍš5éׯmÚ´)tŒØØXV¬XÁÅ‹iÙ²%!!!ØÛ,⺠IDAT۪Ɵþ‰¥¥%½{÷¦[·nZÛd¬ùqqЬYÉö½r¥lÛ¢KFFü1'N €[·nñÞ{ï±{÷nuñÇLff&?üð½zõ G|õÕW 8÷Þ{OÝ<{ölX³f Ï<óŒú\...<ñÄê×ï¿ÿ>yyy,\¸N:п™0asæÌá³Ï>ÓhoÓ¦Mùå—_Ô¯ÓÓÓ7nóæÍ#!!WWW-ZÄÚµkY¿~=o¼ñ†Æ1nݺł xòÉ'èÓ§³gÏfìØ±|õÕW¼óÎ;õýüü˜;wn ßaQÕ$J>‚ÐGTT76u3D%pïÞ=,--MÝŒ ­Êõ@çææ2bÄÞ~ûm–-[¦±MQFÅ„ 8wî;vdåÊ•õΜ9CëÖ­ùõ×_©Q£sçÎ¥K—.ܾ}[£Þ”)S1b™™™\½z•ž={òÕW_im—±Võ¹wN*Ù–N×r¥R©8p FYA{ùòeuÙP©TtïÞ]£n×®]©V­‡R—ýñÇ8;;kÏÚ8pÚµk§QÞ£Gcèß¿¿Æë:¤ž¬¬¬hÕªqqq…Ž¡R© ý‘ÕµkWþúë¯Bõ m!´‘•…!d%B¡/Y‰P·*×ýÙgŸ©˜‡íÚµ‹+V°mÛ6zöì À‹/¾Èĉ8p L:• °oß>ªW¯Î[o½…ŸŸß|ó ï¾û.‘‘‘|ùå—,Y²„1cÆ0cÆ ¦NÊÈ‘#©U«–ƹ5 ßΊè×éÔ)ˆ-ÓæËÝÝzõêi”¹¸¸÷{§ DGGãïïOݺu5ê:::Ò²eKNž<‰¢(¨T*Ο?OÓ¦M‹=oFF‰‰‰ôèÑ£Ð_ØÞÞÞ4hЀX-oÄã?®ñºN:ZË ¶={¶P¹¿¿¿z¿xxxS¨~—.]нñß&I„Â’D(ô%I„ºU©訨(æÌ™Ã¢E‹P©T…¶/Y²+++¯òûöíKrr2›7o !!­[·òä“OR½zuà~PתU+-Z¤q¬‚ý “••ŪU«Êãòôâæ6”ì矿)ŒFÛïH[[[­c’áþWØÖÖÖêcÙÙÙißÚXZZbnn®uþÜÜÜ\îܹƒµµµÞíÕ÷:­×‘——Ç;w°±±)´ÍÖÖVïc !„Â8ªLŸŸÏ¸qã˜2e ¾¾¾Zë\¸pFiL/æïï¯Þƒ¢(4iÒDc_???._¾Lvv¶º~ýúõqttT×iܸ1fffêc‰²Ñ°aCˆŒŒÔ(‰‰!&&†GyD]æëëËÅ‹µ&ä077ç‘GáäÉ“…‚è#GŽššŠ——WÙ^Ä?´]lj'HIIÑ:ÍžB!*ž*@óÍ7¤¤¤0}úô"ë\½zµP`\¯^=jÖ¬ÉÕ«WÇýøùùiÔ+ØïúõëêcßlllðôôTëAGŽaÙ²elذAãGVÓmÈ!¬]»V£¼àõСCÕeãÇ'##ƒ·Þz EQ4êçææjSQÖ­[§ó˜e-4Ts¡›°°0† V¢ã=|Úî+)«še+V„¶”WYllYYY¢-•½¬`%ŠÐ)«Xe1ÉôéÓY¶lÿý7¢xUb ô¥K—xçwؾ}»zØEEóðœÀ†züqòòBtÖsv.~ÁâùsìØ;˜™[¨T=ïåíÿû‹/æƒ> &&†V­ZÎâÅ‹ñ÷÷çÕW_U×*òx7.4täaÇgøðá:Û&„Bã«c —.]Jll¬ÆOóæÍéÑ£±±±´nÝ€²²²Ø±c‡zß7âääDŸ>}€ûc¢{õêŶmÛÈÉÉ ))‰cÇŽñÜsÏ©÷ Qï_`Ó¦MXYY•ëøY!„BaZU¢Z_]ºtaĈ <˜!C†””ÄÖ­[Y¶l™ºgà“O>¡{÷î´oßž   6oÞŒ——/¿ü²ºŽŸŸ¯½ö/¼ð»wïæîÝ»„††òå—_šŒ·¡â¿CV"†•…¾d%BݪD´6&L(”¦R©X¾|9 ,î'tíÝ»·ÐìM›6åСC<ýôÓ¤¦¦2yòdvíÚEÍš55ê}ñŬX±ÜÜÜøý÷ßÕËR?LVõ¯¿þ:#GŽ4u3D"+ CÈJ„B_³è¦R”‡æÀe®`¸GqÙÏúÔBæÍ›'C‡DÕ³-Âøön’54ùO}q*„IHL¢[•íB!„¢æÌ™#ã …^®]»&ã uÚHÊêFÌÍÍåÝße硤d¥`gi‡¿·?ó>š‡ƒƒC™œà‡e?°tíRnݽ…E5 ê×®Ï7ƒ§§g™£$Ú·oÏíÛ·±±±)õ±’’’ ý…]ÇÂX$p†àYèK‚gÝ$€®Dâãã  æ\ƒsde«ËOÜ8Á¾>ûX8k!]:v)Õ9233 ÌQ³£¤µLÕýòS§δQÓxù¹—KuŽ;wî””TävwwwlmmµnËÎÎ&)) WWW,,, mÏÏÏçÌ™3œ={–€€üüüP©TZåàà€••UÉ.B!„ÿY’DXI(ŠÂÀçrªÕ)²=³5·ÕQˆîÍøéãIII)Õyþ7ùìqÚCšÿ¿Á3¶Ð5Y+f~*¼TçXµj¾¾¾Eþüõ×_Eî{äÈ|}}Y¿~}¡mwîÜáñǧY³fŒ9’¦M›2`ÀÒÓÓKÕ^!„BˆIm$¥M"\½n5gÎ@Q# Ì ¦y o|ðF‰ÏqéÒ%v]ÚE^½¼"ë$µIâ•÷^)ñ9FŒÁ¥K—4~"""ðôô¤Føøø”踓&M¢]»v\¿~Û·oóñdzqãF&Nœ¨µ~@@–––4lØaÆqñâÅÒ\–F'+ CÈJ„B_2ñn2„ÃHJ›Døë¿r÷‘»ÅÖQê(,ݸ”íó¶—è'3HvÖñal ׳®—èøìíí±··W¿ÎËË£oß¾\¹r…-[¶àååUâãΟ?_ýzúôélÙ²…eË–ñî»ïòÈ# R©xüñÇiÙ²%Š¢°qãFV­ZEXX[·n¥k×®¥º>!ŒE’…!$‰PèK’u“ÚHJ{#^I¼®ºëeçe—W²“$zÌp”©Ê$--M#.I“&±eË.\H=J|mo×®]Ù¿?ÇŽSÐ_~ù%ÖÖÖê: ,`Ë–- 8¢££µŽ¯¢¢‘ÀYB‚g¡/ žu“º’hÚ¨)Ýù ‹©¤@]Ûºô ìU¢s\É»Âî¨ÝäÕ.z€ƒÊ¡Ì‚ç¹sç²`Á¦M›ÆøñãKu¬Ž;Y«.{0x033#88˜‘#G²xñbΟ?¿¿©Ú"„BˆªKèJb䀑¬™¹†›A7‹¬c}ɚɃ'3­ß´ãf‡›´ì×’¸&Åô`ߟº%£ü° 6ðúë¯3xð`>þøãR/>>¾È2}¦øóõõîÛ@ !„¢(’Dh$¥ß¶u[º¹uÃìJ¿²Thߘ7'¾YâsÔ®]›W†¼‚í)íSÈ‘ GÄÊ +9~//ú=ÛàÞÁeÐÊûf½3‹qÃDZxåb<„c Gžìù$£‡¦zõê¥>þ‡~ȵk×hÔ¨¯¿þz¡íS¦L)ÑŠÞ½{ÓºukF…••+V¬ 99™ÐÐPu»“““iÒ¤ 4nÜ•JÅo¿ýFjj*vvvüüóÏerBƒ$ CH¡Ð—$ê&t%Ô:¨5­ƒZ—ë9¼¼¼øðíËåØôíÛ¸¿øÉÃrssprr¢oß¾ÿ#k+suu¥oß¾L:•K—.±lÙ2Ξ=‹¯¯/?üðݺuÓØÿ­·ÞâàÁƒ;vŒÌÌLüüühÙ²%o¿ý6®®zLu"„Bˆÿ4  …ÑMœ8±ÈÅMÔ´iS6lØ ³,((H]ÄСC‹<¦³fÍ*A«…B!î“1ÐF"ò…eM’…!$‰PèKbÝ$€6/„(k’D( !I„B_³è&C8ŒDä !Êš$ CH¡Ð—Ä,ºI´B!„Z!„BHm$2 _QÖ$‰PB’…¾$fÑMh#‘ùBˆ²&I„Â’D(ô%1‹n’Dh$2 _QÖ$‰PB’…¾$fÑMz …B!„0€ÐB!„B@h#‘ùš222HMM5u3„¨Ô$‰PB’…¾$fÑMÆ@‰ È×4pà@ÂÃÃIJJ*×ó|ðÁZËÍÍÍyçwÊõÜB”·‚B -ô1gÎ-ôríÚ5­ƒÐF"7¢i̘1Ck¹¥¥¥ТғÀYB‚g¡/‰Yt«2tRRaaa„‡‡“••…¯¯/ãÇÇÉÉ©PÝ#Gް~ýz’““iß¾=£FÂÌLs4KNNK—.åàÁƒ¸¹¹1hÐ  ëÌ™3¬]»–øøx‚‚‚ ÁÒҲ̯ïõ±c¹°u+µªW/²N>p­N~?~¼Dçý徘8‘F66ÅÖÛ÷.QIIT«V­Dç1¶1cÆÈ‡B!ÊL•  ýüü°²²¢M›6(ŠÂÌ™3ùôÓOÙ¿?~~~êzëÖ­cذa<ñĸ»»3eÊ6oÞÌêÕ«ÕAtvv6}ûöåøñã 4ˆƒòÅ_°~ýzzöì©>Ö®]»æ±ÇÃÏÏ÷ߟµkײyóf¬¬¬ÊôúÆN™ÂŠß~cV||‘uVÙØ@Côѻֽÿ>K¢£‹¬ |?|x™Ïׯ_'&&¦Èí¾¾¾888¨_ß¹s‡#GŽpáÂ<== ¢N:eÚ&!„BmªL½dÉžzê)ÌÍï_RBB^^^¼ýöÛ¬_¿€ÜÜ\&NœÈˆ#X¼x1Ï>û,­[·fË–-ʶmÛ8yò$Íš5`À€Lœ8‘óçÏ«Ï9iÒ$:vìÈ–-[P©TL˜0€€~ùåÆŽ«Ñ¾ÒÈ÷kÒ„ë$ïØAá>õû½Ïë<=YRâsX[[4|8»?þ˜Î¹¹Zë|áîÎ{Ÿ^âs%,,Œ_|±Èí[·nåÉ'Ÿà÷ßgôèÑ\¿~233±··ç»ï¾cĈZ÷ÏËË#??ŸêÅôà QÙ$jû¦Mˆ‡EEEѸqcS7CT÷îÝ+—oÓ«’*3 ÇÓO?­žêÕ«G·n݈~ 7uûöí\½zU(êžËE‹©Ë–,Y‚‡‡‡:xèÓ§.\àÀœ8q‚ˆˆúôéƒJ¥î÷‚{{{k.PI„“çÎe®³³Ömkll8eJ¡¡(†z~Ú4þÏÓSë¶hÀ¶C\]]KumÆŽËíÛ·5~bbbðôô¤víÚ4iÒ€øøxúõ뇣£#ááádddð÷ßãííͨQ£8zôh¡c‡……ammµµ5>>>L:•ôôô2¿!ŒMV"†•…¾dâݪLý°œœΜ9ƒ¿¿¿ºìÂ… C: ^?h_¸pAc¿÷)8FÁ¿]õ¶•Å€|u/ôCåùÀº¬,¿ö8:–êÇÚÕ• øxvk9ÿîîL)‡Þg Õ?666Œ;–ÄÄD6n܈»»;ü1YYYÌœ9S=&ÝËË‹¹sç¢( ï½÷žÆqÆ ãúõë|úé§<öØcdff–˵a,ýúõ“DB¡7Éú’$Bݪl=uêTnܸ¡1ÙÕ«W±°°ÀÛÛ[£n“&MÔm)ŠBbbb‘ôÕ«W5þ}8÷÷÷çÆä>4âĉ,[¶Œ 6hü:‡«¶^è5ÀÀü|ÌRR  ~ž¿wÿ{è¼åÙû¬ÍøñãÙ³gK–,¡]»vêòcÇŽ¡R©èÞ½»FývíÚQ£F Ž;¦QžÀ¢E‹xûí·Y¾|9ׯ_çÕW_%**Š™3gåZÊ›¢(¯µÝWR&e•µ,66ެ¬¬ Ñ)“²ªZV“LŸ>eË–qùòeDñªÌè}÷ÝwÌŸ?Ÿ%K–àããcêæ”©‡ÇBçëjÕbõÈ‘ðÏP’Ò²‚b÷Ñ£tÎÏà‹úõËeì³63fÌ`Ù²eÌš5‹¡C‡jl‹‰‰¡I“&Ô®][£ÜÜÜœ:°eËÒÒÒ°··(”ìhaaÁìÙ³ùæ›oؽ{w¹^‡B!ª(¥ŠYºt©bff¦|ûí·…¶Í;W”sçÎi”wêÔIñóóS¿öððPúôé£QçàÁƒ  ,^¼XQEY¹r¥(»wïÖ¨7hÐ ÅÙÙY£l̘1ÊСC‹m÷˜1c”1cÆèº>>н½½’]h[Æ +++%//Oçùëׯ¯Ô«WÏð†W@ÎÎÎÊÊ•+MÝ a7nÜPnܸaêf”Ÿ­÷e|ê¿?grLÝ¢JíáÏ>!Š2tèP½c’ÿª*5„cõêÕŒ;–¹sçjÑ¡ 7:22R£<22RcX‡…êúh™µ]S$BaI"ú’$BݪL½~ýzFŽÉœ9s˜8q¢Ö:=zôÀÍÍM›6©Ë>Ì7xî¹çÔe!!!ÄÅÅ®.Û¼y3>>>´mÛ€-ZàïïÏæÍ›ÕcP###‰ŽŽ&DK@[Öò'ÏKo{û2™y£(ÏO›ÆlWW£Œ}>þ<ýû÷§aÆ„††j̨¢Ñ¦çŸà§Ÿ~Òû»páBíp¼ôõë×5ö?sæ £GFQúöí[Ö—!„QI¡0„$ }I¡nUf ô!C077'44”ÐÐPu¹½½=;vìî“ýꫯԳ1¸»»Ê AƒèÝ»·zŸAƒ±|ùrzôèÁ AƒÔÓ×Ì']`þüùÓµkWüüüX·n;wføðáå~½~Mšðæ‚ ,bÞã²`mmÍôÿû?üZ¶,·søä“O¸}û6 6d̘1…¶¿ÿþû4oÞœ>}ú0|øp–,YÂÅ‹ """‚;vйsgúçŸfÁ‚4kÖ "##9wîpÚÃW_}µÜ¯K!„UO•  ‹šQááȾ}ûÔKyñÅ…–ò¶°°à×_U/åݦM¾øâ‹BKywéÒ…C‡©—òž1cF¹-å­Í3£F•û9:÷êU.Ç}â‰'hÔ¨‘úuÛ¶mÉÿ'aQ›{¤W¬XA·nÝØ¼y3¿ÿþ;žžžÌ›7W^yEã÷8|øpÌÌÌ8uê‘‘‘ØÛÛ3bÄÌÓO?].×%„BˆªO¥(Í%Ê\HH÷îÝcåÊ•ÅÖùŠMÎÅÅ…yóæš±DT}U~%ÂmÙöÀ*®“¬¡I•é÷1:Y‰PèkذaXZZJLRŒ*3º¢“ùBˆ²&I„Â’D(ô%1‹nò§¼‘È€|!DY“BaéMú’˜E7éB!„Â@ !„Ba  äÞ½{º+ !„’““Õ‰„Bèeê&ˆJBbÝ$€6/„(k’D( !I„B_³è&I„F"ò…eM’…!$‰PèKbݤZ!„BH-„B!„$€6/„(k’D( !I„B_³è&´‘È€|M‰‰‰\¹rE¯ºwïÞ%66–ŒŒŒrn••‹$ CH¡Ð—Ä,ºI¡‘È€|Mýû÷'))‰ØØXu÷ìÙC¯^½X¹r%C‡-ÿÆéðÍ7ß­uÛäÉ“ñðð0r‹Ä•$ CH¡Ð—Ä,ºI-„BCCù믿¨S§N¡m£G–Z!„¨âdG%•““C£-HHH(×óô1‚ [¶”ë9*#www’’’ ý´hÑÂÔMB!D9“h#)ëù ~ú‰¸6m˜2k«¾ý¶L]àtD'ÌÌxÁúõî].爌ŒdÏž=äååѱcGôÞ711‘½{÷rñâE¼¼¼èСîîîEžçÏ?ÿ$%%:w·w¡z'Nœ <<œøøx\\\èÖ­›ÖzB˜ZA¡“““‰["*ƒ¨¨(7nlêfˆJàÞ½{XZZšºšÐFR–òsrrønýzîMÊï¾#!!zõê•Ùñ Lž=›ëC†µoa›61 8¸ÌÏ1oÞ<¦L™‚‡‡ äææ2gÎÞxã û®\¹’^x´´4\]]ILLÄÚÚšùóçóÜsÏ©ë8q‚§Ÿ~š„„jÕª…££#ñññäää (ŠºÞÍ›7y饗X³f æææ¸¹¹‘˜˜HNN§OŸÆßß_ãüYYY¤¤¤P·n]T*UÙ½)Bè© PÆB }Ì™3GÆA ½\»vMÆAë C8Œ¤,oÄ?ýÄåŽA¥"¾o_¦ÌšUfÇ.p:"‚³VVàà@jÏžÌøî»2?Ç•+WX°`/^$&&†äädºwïΛo¾ÉÁƒ‹Ý÷ܹsŒ5І GBB‰‰‰4kÖŒÿýï9rD]wÚ´iܼy“ððpnÞ¼ÉßÿMZZ«V­Ò8æóÏ?Ohh(sæÌ!==¸¸8233Ù°aµk×.Ôv{{{\\\¨Q£={öäÔ©Se÷桇~ýúIð,ô&Á³Ð—ϺIt%SÐûœ5uêý‚zõXÀêÐP(˯q?ù^xáþ››s©I“2ï…ÎËËcÒ¤Ixyy`ooÏìٳپ};sæÌaãÆEîûÑG‘——ÇÌ™3ÕC6œùä“OèС3fÌ`Ë?c·¯]»†ŸŸêý---2dˆúõÑ£GY·n!!!L-xosssúöí«qnÆŽ‹¯×L¸ôIDAT¯/)))„……±}ûvvîÜÉþýûyüñÇKÿæ!„¢Â’èJæÁÞgµÁƒá¡ÞÔR¹t j×uQZ9õBwíÚUãuóæÍqrr"<<¼Øý"""°°° S§Nå­[·¦Fœø€Ó§O³cÇòóóyþùçuî/„BˆÊMz ¤,’ õ>¨Wùùú¨Äç8A„¢@µjEž#µ];Þûê«2»®%K–­~ýý÷ßðâ‹/»ß„ €ûÃ2òóóÕåß|ó /¿ü²ºlóæÍu._¾  žÆ®{÷î´hÑ‚yóæqìØ1º·oß&-- €˜˜öï߯±ýÂ… ¼øâ‹DEEѯ_?ÌÍå‹!„¢*“OúJ±V-úœ:¿ýVl=߆ K|ŽÌìl:::‚Ž•kh:a¨zõêagg‡¿¿?íÚµ#**Šƒ¬3™¯wïÞŒ7Žü‘Ó§OÓªU+N:ÅñãÇ4hÇW× ¦~ýú´lÙ’ºuëræÌŽ;†¯¯/Ï<ó fffüøãôë×6mÚÐ¥KyäâããÙ½{7$00 .ЫW/\]]yôÑGIMMåôéÓò]9$Z !„¢b‘ÚHJ›DòÌ3„üì•—Ö-[Ú²e¹ž£ÀèÑ£IKKãµ×^ã믿fÏž=899ñõ×_óÒK/aföï—#^^^L:µPÂá¢E‹èÕ«›7oæüùóøùù1yòdFŒ¡Q/,,Œ]»vqáÂÎ;GÇŽùðÃiß¾½F’D³f͈ˆˆ`Á‚;vŒÓ§OãææÆ¼yóÔCJZµjÅüùó9räW®\ÁÁÁ±cÇÒ¹sgFŒ¡Ñn!Ê›$ CH¡Ð—$ê¦R\ŠM”‹._¾ÌÎ;‹­2FMÎÅÅ…yóæé5Ÿ¨Z6lØTá$ÂmÙö@çÃ$kh"ý>%"Ÿ1B/]»v¥Aƒr¿CžDF"ò…e­Ê΢\H0$ô%1‹nò}³B!„Z!„BHm$e±¡B<(99YH(„.QQQ¦n‚¨$$fÑMh#)‹•…âAûöíS¯F(„.sæÌ1uD%!1‹n’Dh$ú Èß·oŸz6!ô•’’bê&‘$BaI"ú’$BÝ$€® &Nœhê&ˆJjìØ±ôêÕËÔÍB!þ3$€® Z´h!½B!„•€Œ6/ô%‰>B_’D( !Ï¡/‰Yt“º”Nœ8ÁÛo¿ÍsÏ=ÇO?ýD^^žÖz2 _èK}„¾$‰PBž-B_³è&t)lÚ´‰¶mÛrìØ1ªW¯Î›o¾É€ÈÍÍÕ¨wùòeµP!„Âp»OÆ@—P~~>'N¤ÿþ¬\¹€Z·nͯ¿þÊ€LÜB!„BQ¤º„vïÞMll,}ûöU—Q§NJ› Uîcä6lØPêc”´†œ[ŸºÇŽãÊ•+En?sæ ÑÑÑZ·%%%qèÐ!½ÛS•Åï²8±±±„‡‡—ú8%i§!÷˜¾íÔÕŽâ¶—÷{]ÞäÙbX]y¶È³äÙRÑI]B.\Àßß_£ÜÏÏO½íA999FiWiȇœauËëC®²Ìë,r÷™òC.55•ÔÔTç65y¶VWž-òlÓ>[*CÌbj*EQS7¢2zï½÷øä“OÈÈÈÀÜüß‘0&L`ùòåj7&..ŽV­ZQ½zuã8;;cii©~}ùòe“•åääP½zuõêåqŽØØX:vìXªã=8.Ë}=Jûöíõ:Ǿ}ûhß¾}±õbcc©U«uêÔÑZïØ±c899áééYhߌŒ ’’’ðóó+´oXX­ZµªP÷Fyý.‹+;}ú44nܸTÇÓçwYšÿnß¾Mzz:ÞÞÞ¥ºÿ~ÿýw:wîlо'NœÀÉÉ©BÝ—8q}ôQƒöµÈ«ÎõØëÔ­S KKîVÏ"Ï,Ïà÷Oß²‚Ä© ”Ûÿ ú<‡ÒÒÒ¨[·.uêÔÑZïÒ¥K¤¤¤àëë[h_ nݺ…ŸŸ_¡}ÃÂÂ0`@…º7ÊëwY\YRR—/_&00°TÇ3ä3¥€!ÿ/¨T*îÝ»‡··w±õtÝûöí£AƒÅî[𹚜œŒƒƒáááÔ¯_ŸsçÎ!´“º„  ÇŒÑ#GpuuÕ8†¬ô#„BS{8aðêÕ«±téRµ¨â“$Ârss#;;›óçÏÓ¤IuùÙ³gqssÓ¨+7 B!DÕ!c KÈÇÇ€ˆˆòÈÈHõ6!„BQõH]B;wÆÓÓ“7ªË>Ì7 1]ÄB!D¹’1Ð¥°iÓ&žyæ:uê„——k×®¥]»v¬[·Nc\´B!„¨:¤º‚ƒƒ9pà­Zµ"''‡O?ý”°°0½ƒçâåå…··7!!!ܸq£œ[,*»„„jÖ¬É /¼`ꦈ ¬uëÖÔ«WOOO<==Ù³g©›$*°'Nжm[ÜÜÜhܸ1wîÜ1u“D”ššª~¦xzzâääD³fÍLÝ,“‘hº|ù2 4 ''‡W_}>þøcS7KT`ýúõ£FØØØðý÷ß›º9¢‚jݺ5K–,јPm’““ ૯¾¢ÿþ¤¥¥akk[hÊU!6nÜ8|||˜:uª©›bÒmBÓØU¯^úõë“‘‘a≊lùòåøøøÐ¢E S7ET¿ýö»wï&??ßÔMØ?þÈã?N«V­ˆ‹‹ÃÑÑQ‚g¡Szz:aaaŒ3ÆÔM1  MlòäÉ4kÖŒíÛ·3cÆ S7GTP×®]ã믿–{Dè¥m۶ܺu‹ ШQ#Y A)**ŠèèhžyæÆG=ÈÎÎ6u³D·jÕ*:v숋‹‹©›b2Õ>øàƒL݈Ê(66–#GŽpóæMêÕ«§µNFF;wîäÏ?ÿ¤Zµj…R $((ˆÃ‡såÊ:wî\Î-Ƥ( /^dûöíìß¿777ìííµÖÝ´iË–-ãÔ©S8::ªW!xöÙg™>}:>>>:tˆäädúôéc¬ËF’ššÊÁƒÙ¾}; 4jÔHk½¸¸8~þùg¸yó&5¢Zµjêí={ö¤k×® <˜ôôtöìÙC¯^½ŒuÂH2339vìgÏž¥V­ZX[[k­ÅƉÇÙÙY£Þž={HLLäèÑ£„„„°fÍ‹¼÷DåTVÏ–&Là•W^ùoS„A¶oß®899)€(:uÒZïòåËJãÆ•š5k*-[¶TªU«¦¼üòËJ~~¾Öú‡V|}}˱åš7o®Š™™™(»ví*T'??_;v¬bkk« }___%%%EQEÙ¶m›¢R©”ÐÐPEQ%55U9~ü¸’——§$%%)ãÆSÞ~ûmc]†0’uëÖ)ÇWöîÝ[ä‡Üü¡Êo¿ý¦.2dˆÒ A%//¯Pý¹sç*Ï?ÿ|y6[˜Àùóç•M›6)‰‰‰JÏž=‹|¶´oß^i×®úÃëÔ©Sй¹¹²hÑ"EQî?[Ö®]«ÄÇÇ+üñ‡Ò¬Y3eÍš5ƺ a$óçÏWV¯^­üòË/E>[Ž;¦ÊÂ… EQ”ììl¥sçÎJÓ¦MÕunܸ¡¸»»+±±±Êµk×”¦M›*gΜ1Öe#(«gK×^{Myã7ʻٞХPÔ¯˜™™)}ô‘FyÆ •=z(Š¢(7oÞTžxâ ÅÓÓSiÓ¦òÁ(7oÞ4F³… Ü$mƒ>Ï–_}õ•ÑÚU‘É,å !!Ð~#&''K†³ÐPè-ooo¬­­Õ÷’Pü³åöíÛܽ{×ÍTBBB¡{¥qãÆT¯^]ž-BCÁý ­ãOž-ÚI]233pttÔ(¯U«Š¢È(4äååaaa¡Q¦R©077'//ÏD­QÁýðð<½÷Ü/âA™™™…>‡ªW¯ŽúsJø÷Ùñðg‘<[Š&t9(˜®®à+‘gÏžÅÚÚS4KTPnnnœ={V£,..Ž´´4ÜÜÜLÔ*QÜß/ØÛÛcgggŠf‰ ÊÕÕµÐçPbb"·oßÖ:­ªøï’g‹á$€.€ö±`›¼½½‰ŒŒÔ(‹ˆˆPo¢@Áý í~‘{E<ÌÃÃCëçPÁ6! ìììxê©§LØ2QÑ<[vìØ¡.“gKñTŠ¢(¦nDerëÖ-fΜ À† ÈÍÍeРA¼õÖ[Ô­[€¥K—òì³Ï2yòdš7oÎ÷ÿßÞ½EYõqÿÂr‘›.æ JróR:¡1F`Y‰y#s4-¦¤2q@4‡ÊK‰Ž‘†áØhšƒ–“†„÷„ÀÔM$/‹\dù½4>¯+—\_Äáíû™Ùç<çùsfùqöìy>û /^ÄÑ£GùÖÙ¿ÈG}„œœhµZäåå!88NNNxê©§hjjÂĉqêÔ)DGG£¬¬ ™™™Ø¼y3¢££ñ¨³TVV*?ïãÇC¯×cذa€M›6ÁÃÓ“ƒððpŒ3ؾ};ðÓO?ÁÞÞþ‘õŸ:WFFòóóQQQÌÌLDDDÀÃÃ?þ¸’ðÔ××#88‡²²2,_¾ HHHx”ݧNÄ×–‡ƒÛØI¯×£¢¢ÊqSS“R/::¶¶¶øâ‹/pàÀ᫯¾bòü/ãéé‰ÀÀ@À¨Q£”ò»ß>533ÃÞ½{±~ýz>|...ÈÎÎÆØ±c;½¿ôèXXX(¿+w¾Þaii©|ÿôÓOãçŸƶmÛPXXˆY³faÞ¼yü÷/£Õj•¿=wÞÙ¬¨¨ÀÍ›7•:ÖÖÖ8tè–.]еk×ÂÙÙŸ~ú)bbbIŸéÑàkËÃÁh"""""#p 4‘˜@ 4‘˜@ 4‘˜@ 4ýß;vì˜ÁÝØ¶¿þú 4ØÞX§NÂüѽjßÙ³gQ^^®çææ¢¬¬¬ÓÚ÷ö™ˆ¨³0&¢.íÒ¥K˜3gŽr—G___L›6 ¿ýö›RgÖ¬Yظqc§õéðáÃ=z4jjj8Fll,Ö¬YÓ½j›V«Åðáà ’ÑçŸ;wîüŸcŸ9s¦Câ´&;;%6Q{˜@Q—õí·ßÂÏÏGŽÁË/¿Œ­[·bÞ¼yÐjµ-î¸ÕÕÌ™3“'Oî”¶Ö®] åö¾iß¾}X¼xq‡Ç€3f ¸¸x(ñ‰ˆÚÂ[yQ—¤Õj1gÎŒ1»wï6¸%í¼yó°wïÞV¯«ªªByy9 dpMcc#êëëѽ{wƒúÕÕÕ°´´TêÞºu æææ°²²j3V{}633ƒ­­­RV^^ŽË—/ÃÓÓjµZ)î¹ç R©ÍÍͨ®®n5f·nÝ”z ÓéPTTµZ^½zýcŸjkk‘ššŠÏ>û¬Í:—.]‚N§Cÿþý•²¦¦&ÔÔÔÀÞÞ¦¦†s1555055…J¥‚N§Css³r‹i ØØØ(uoß¾¢¢"ØÛÛÃÛÛ»ÕöµZ-Š‹‹Ñ³gOôéÓG)·³³ÃìÙ³‘˜˜ˆ±cÇþãX‰ˆ:ŒuAÉÉÉ@~ÿý÷¬ ±±±2yòdQ©T@zõê%gΜQêlذAºwïÞâZ///IJJ2ˆµpáB‰ŠŠRb¹¹¹ÉÉ“'•:»vírãÆ inn–ØØXqvv–ÜÜ\ÉÎΖAƒ‰™™™¸¸¸™;w®cÔ¨Q#""EEE ÕÇ‘#GDDD§ÓÉ¢E‹D¥R‰¹¹¹‘#GJiii»ÏMff¦¨T*ill4(W«Õ’ !!!bii)$ @4ˆˆ\¾|YÌÌÌ$##Ã຺º:qpp””IJJjÑß)S¦ˆˆˆ^¯—ääd±²²Rúëïïoð3Ñh4*fffÒ³gO155•   ƒörss€”””´;N"¢ŽÄ%DÔ%ÀÕÕƒ º¯úëׯ‡¯¯/JKKqâÄ 8::bÓ¦MÔöºuëàåå…‹/âÔ©SprrjsuCC^|ñEìÙ³¹¹¹ $$$`üøñ¨®®FUU®]»†W^y¥ÕD}}½ò¨««Ã¤I“ðØcaàÀ€¥K—"==¸yó&òòò Óé0sæÌvÇRPP///˜››·8—””„gžyøñÇqûömL:àææ†ðððãÞ±cêêê0}útÄÇÇ#)) ^^^ˆ¾þúkÀ† €uëÖáêÕ«(,,„‡‡¢¢¢”_®Zµ –––¸zõ**++Q[[‹””ƒöúõë§Œƒˆ¨³0&¢.I£ÑÀÓÓó¾ë»»»#11½zõBPP^xá|ÿý÷Ô¶««+V®\ ॗ^Bvvv‹z7oÞÄøñã¡ÑhpäÈ 0ÀßËŠ‹‹Ñ¯_?X[[œœœðä“O¶Úž‰‰ ¬¬¬”Çûï¿C‡aß¾}pttÄ­[·ðÉ'Ÿ &&QQQ°±±ÁðáÜœ”””´9–‚‚% ½WŸ>}°lÙ2888`ôèш‹‹C^^Nœ8˜;w.ø Õmìî•••e«µØ+W® Ùµk—\¸pA,,,ÄÅÅE†*...âææ&Û¶mSêß½ÝöíÛ•~©ÕjƒÇñãÇEDD«ÕÊÔ©S€888ˆX[[‹››[»ÏaCCƒ¸»»KzzºA¹Z­–7ß|S¼¼¼ÄÝÝ]T*•¸»»Ë±cÇZÄÈËË2mÚ´çš››eÊ”)âàà òê«¯ŠˆHcc£,\¸PT*•ØØØˆŸŸŸtëÖMœœœäÊ•+""âìì,vvv(^^^booß⹟;w®Lœ8±Ý1u4‘»¦@ˆˆº ºº:A£ÑÀÃÃ6X§|ñâEØÚÚì QSSƒ²²2 0ÀàF 'OžÄŸþ‰¡C‡ÂÓÓ.\€££#œœœÚŒU[[‹K—.)±ÚŠ­Ñh "èÛ·/jkkqôèQTVVbðàÁðõõ5Sii),--¡V«qëÖ­6?dèíím°VY£Ñ °°·oßFŸ>}ø³è©©©HOO7X«}þüy8;;ÃÆÆùùùÐét m±þø{©K`` Á6}÷ÒëõÐh4°µµ5¸ÁËåË—qæÌTWWÃÓÓO<ñ„²„¥©© ÇGii)<<<l°¼åúõëèÝ»78€vÇHDÔ‘˜@ýËÕÕÕÁÛÛ™™™=z´Q×êõz„‡‡£©© ?üðÃCêaë’’’°ÿ~ÞÊ›ˆ:h""BUUÌÍÍáààpß×$%%aõêÕÐëõÈÍÍm1‹þ°UVVÂÚÚöööÚ.h""z ÅÅŨªªBHHH«w2$"úÅšˆˆˆˆÈÜÆŽˆˆˆˆÈL ‰ˆˆˆˆŒÀšˆˆˆˆÈL ‰ˆˆˆˆŒÀšˆˆˆˆÈL ‰ˆˆˆˆŒÀšˆˆˆˆÈL ‰ˆˆˆˆŒÀšˆˆˆˆÈÿXö¸OI°ÂIEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/filesizes-chunksize-15GB.svg000066400000000000000000003701641231437614300263660ustar00rootroot00000000000000 image/svg+xml Automaticchunksize PyTables-v.3.1.1/doc/source/usersguide/images/indexes-sizes2.png000066400000000000000000001671711231437614300245770ustar00rootroot00000000000000‰PNG  IHDRÐß}™SsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìwTTW÷÷¿CQ@R”.M@D°"(VtĆ[Œ‰Æ$F£>1ÑD5ÖØ±DcAEeÔ`;"ХТ€ ˆt„Áöûï½ÃÁQŸür?kÍZ°OÛ·œs÷=wŸ}DDàááááááááááQ¥­Ï? Þ€æááááááááái¼ÝHòòò°oß>üôÓOøöÛo±råJ=z/_¾ää €ŽŽrrr>‚¦M'//Ÿ}öìììЦMèèè ººúƒêddd¼·6¢££¡££ƒE‹½·6Þ7b±‹/†³³3ôõõ¡££ƒ¸¸¸®Ç£GpòäI„„„ 55õëËÈÈÀÙ³g±zõjÌ;6lÀ©S§PXXØ ÚþßåŸ:æ|H~úé'èèèàÊ•+ —‰‹‹ƒŽŽ¾þúë÷¨Y-®®®°²²’’CGG;wîä䎎ÆÀann]]]øûûKé=dÈXXX@WWC† yïúóp™:u*tttðøñã÷ÖÆ¬Y³ ££ƒ„„„÷ÖFcزe tttpôèÑ­Ê{Cåc+ðObݺuøùçŸQVVÆIØ»w/&MšÄÊÊÊÊP\\Œš›ù¨Q£pãÆ xxxÀ××ÊÊÊT‡×¯_£¸¸555ï­ªª*£¢¢â½µñ¾Y°`6oÞ Œ=ÐÓÓû m×ÔÔ`À€¸wïž”a»víZØÚÚ6©Î’’Ì›7üñ‡ÌtMMMbñâÅ011iRÿ—ù§Ž9’ŠŠ £ªªŠ•UUU¡gÏžèØ±#vïÞÍ)ó!ÇŠ’’”””HÉÞ¼yƒââbTVVJÉsrràãリª*øøøÀÒÒvvvj'B¼½½!‹áíí kkkŽaþObáÂ…¸víN:CC펛7oÆÁƒ±eËtíÚ•“þ!žgååå(..þ`“]áááøá‡0sæLLž<™“.‹Q\\ ‰DòAôùð´‚9rß}÷LLL°cÇx{{C[[OŸ>Ž{÷ ±X,UfáÂ…˜8q"Ú´ió‘´n<Ïž=Ãõë×áîîŽ[·n}4=æÍ›‡1cÆÀÀÀà£éðOàСChÙ²%âãã¡¢òa»sMM ÂÃÃaee…þýû#;;M®/==}úôÁ³gÏÐ¥KÌ›7h×®ÒÒÒpÿþ}ìß¿;vì@=ØŒGÃóoa„ puu…³³3+«©©ATTÔÿì‹Gß¾}qøðatéÒEJ~öìY”””`éÒ¥X²d‰TÚ¹sçPXXˆï¿ÿ+V¬øê¾RSSÅy‰ø_ ##QQQ(..–™>gÎøûûÃÈÈè½é0sæL 8fffï­ºäçç#** Ç—™îçç‡víÚÁÓÓóƒèó1à hÙ¾};`Ïž=ðõõeåööö°··Çĉ9ƒo¯^½>¨ŽÍAvv6 sçÎU=z|Ôöÿ TVV"??ÜxB[[ðã?¾“ýå—_âÙ³gøæ›o°víZ©cÒÓÓƒ‡‡>ûì3ìÝ»÷ƒÍ²óüߣS§NèÔ©ÓÇV£QXXXÀ‚#oh¼þ_Ëy€îÝ»£{÷îïµ www¸»»¿×6ƒ­­m“¿DþSà} $11¼!ê»9,_¾B¡ùùù¬l„  …rŒ¡^—={ö`øðá°¶¶†½½=ÆŽ‹{÷îÉÔáÈ‘#èׯ,--Ѷm[ØÚÚÂßßçÎ{ë1Ž7óçÏ\¸pÕéСClžššlÞ¼ƒ ‚©©)ºvíŠO?ýT¦o×õë×! qêÔ)®YYYøüóÏÑ­[7´oß ÀÚµkþT{óæM…Bœ8qBfú˜1c0{öl)Ypp0„B!bbbÀÀ@tèо¾¾Øºu+›/::³fÍ‚ƒƒœñã?rô’H$ …X¸p!ªªªðË/¿ÀËË ¦¦¦èׯ.]º¤Ðqµ/Y'NäÈ/^ ¡Pˆ={öHÉsrr8ýsß¾} …ˆDDD`ôèѤû’P(ÄáÇ9mbÞ¼yèÖ­,,,0|øð&ù†††bèС033C¿~ý°bÅ ¹×4<<B¡ÿý7 )) B¡!!!€+V°:_¼xB¡û÷ïPëRŤÕ#«««±~ýz <æææèÔ©&Ož,s,?pà„B!bcc)S¦ÀÁÁnnnRùvïÞÍ>«0vìXÄÄÄpê;wî„B!.]º„ääd|öÙgppp€½½=¦Nм¼<©üB¡QQQ€3f°ÇSÿ¾mˆÝ»wÃÏÏæææpqqÁäÉ“e^·¸¸8öüedd`Ö¬Ypqq½½=¦L™ÂfΜɎK–,au9r$›gÍš5 …xþü9+ËÈÈ€P(ÄÆQXXˆ¹sç¢[·nprrÂÂ… Ùu ÅÅÅXºt)¼¼¼`aaQ£F!33“£÷¦M›  ñôéS)ݲ7V¯^Íæ}öìV­Z…þýû£C‡011A=°hÑ"ÎózýúõX»v-€ÿÞÌ/== ‰  qíÚ5Ž®ŠŽÅuÏiYYŽ;˜™™ÁÃÃkÖ¬‘铘˜ˆ)S¦ÀÑѰ°°@Ïž=ñóÏ?sò¾ ¼­ íÚµ)còmDFFB$I¹v£¨¨ˆó‹‹‹ƒH$bu v†qذaøôÓO‘˜˜OOOtêÔ aaaðôôDpp°T{kÖ¬A@@îÝ»+++…BØÛÛãöíÛ 9ò‹Åb¼yó@íà*‹!‹Ù›Z,ÃÇÇ_ý5RSSÑ·o_èêêbÿþýpqqÁÙ³g¥ê{öìD"þþûoxyy!44FFF011y«/XTTD"ÊËËYYQQD"ÂÂÂàããƒ'NÀØØååå8sæ ºuëÆ1pß¼y___,]ºyyyèÝ»7òòò0|øpüõ×_2Û.**Bß¾}ñÍ7ßàéÓ§èÓ§:tè‘HWWW©—‘!C†àéÓ§˜?>ç%eêÔ©8vìzöì©¡YTT„îÝ»cáÂ…ÈÌÌD¿~ý ©©‰;vÀÉÉIj ª¬¬d﫚šöZ1ׯ!fÏžC‡±‹Šœœœ ¡Pˆï¿ÿþ­åߌa0gΨ««7ºüµk× ‰°|ùrLŸ>999èÔ©”••Ô¾Ì9;;cÇŽÐÔÔD¿~ý™™‰ÿüç?èÞ½»”·ššD"ǰ۽{7D"6mÚ$å ›––†püVåñàÁ899aÙ²eÈÉÉAß¾}áî¬,Îù¿|ù2œñÇ@KK ýúõÓ'O0þ|ôìÙS¡6™~˜––&3ýôéÓ—’%%%A$!$$ƒ ÂÓ§OѾ}{„……aöìÙØ²e ±X¬;Guu5†ŽéÓ§#..½zõ‚±±1Ž9777|8‰ÅbV~âÄ €æÌ™#UföìÙ€~ýõWª©©aå?&CCC233“:þÔÔTÒÒÒ"}}}öZoܸ‘Ј#>æùóçš8q¢Ôõ>rä ²±±‘’—––êÙ³§Âm…‡‡K "¢‚‚ruu%JMMmT} ?üð µk×6ºl¯^½?¾Im»»»244¤˜˜©4‰DB¶¶¶$($$DJHhÞ¼y¬<--ÐøñãYYyy9©««“¥¥% ‹/²iÛ·o'´cÇŽ·êY]]M]»v%´~ýzNztt4ûwee%YYY‘’’ýõ×_¬üÍ›7@èûï¿—*/kÌ9|ø0 ß~ûM¦Nêêêdgg'%ûþûï ©¨¨Hkrr2µjÕŠ444ÈÄÄ„N:Ŧ=~ü˜Ú¶mKÊÊÊôäÉV^QQÁöÛAƒQII ÕÔÔЮ]»uéÒ¥¡ÓƲoß>@[¶lae7oÞ$diiIÊÊÊTTTĦMš4‰ÐƒXÓÏêWee% nݺÉl÷îÝ»ì1Ìž=[jìùî»ïMž}: 5kÖHq>$===²¶¶¦ÊÊJV¾páB@€‚ƒƒ9õ-[¶ŒKëŽQ999Ô¡CjÓ¦ åçç³rf\@»wïfå4pà@@[·n•jcøðသ>}Êi¿!˜{¬OŸ>ì½GT{ßhii‘¶¶¶ÔsôÂ… ¬n_|ñ…ÔskÓ¦M€†*ÕÆ¼yó………ÉÔaôèÑ€>|ÈÊâã㥞gŒ-Q\\Ìöc š2e ½y󆈈^¿~McÇŽe¯]]&OžL(..®ÁóñâÅ 233# ŠŒŒdå ”™™ÉÉ¿bÅ ™cÍ‘#G­X±Bf;kÖ¬!R÷KcÇb"¢¾}û211¡¤¤$Vž˜˜H­[·&UUUÊÊÊbåL¬ÿ¥¤¤È;-M‚7 ¤¦¦†–/_NìM€ôôôhòäÉ”––Æ)£ˆ]XXHvvvœUII ijj’•••Ô€ÄÀ<7nÜHDµFrëÖ­ÉÖÖVj@l,ò èššÒÑÑ!555™Ç3eÊ@Û¶mceŒíèèØ(㙨aÚÐÐPÊ€%ªøƒƒƒ”Ü‚ÓÁ„B!Ç€ÎÈÈ eeeòðð©×ï¿ÿNèÈ‘#RòêÝ»7ݹs‡ÔÕÕÉÌÌŒ :Þ²²2RWW§V­ZÉ,3lØ0 5è4Õ€–ÇÉ“' 5©ü»ÐmÛ¶å<`RRRèÏ?ÿ”úÕ7´zûöíœòGe_¢êSXXH­Zµ"uuu*--e妦¦Ô¶m[öÿ‹/Ú»w/)++Ó‚ Ø4æá¨ÈKê±cÇd>€eLhôèÑœ´W¯^‘¦¦&µhÑBª/4·ýÅ_pòûúúÊÕk̘1€Nž<ÉÊZII‰Ó+++ISS“”••œŒ`ÈÊÊ"$ YÙÏ?ÿL€öïßO¤Œz©ëHôn´‘‘çå3%%…çañâÅrûÉ_ýõÞ è‡’’’õéÓG¦~ÌD‰H$beŒÀÉ_TTD-Z´ :ȼ†Ìs`óæÍ¬Œ1 ÈÉÏ\ÇiÓ¦IÉ›j@[ZZ’@ zYa`Œ­eË–±2Æ€nÓ¦ •••qʸººJNNfeïb@qžg{öì!¤¥¥%õBHT;ù€ÆŽ+%WÄ€.//'777œgXCèëës^r›b@7e,f è?þøƒS†y.Ö½ÿg̘AèÎ; _Sá]8D `Ñ¢EÈÉÉÁ¡C‡ðé§ŸÂÞÞùùùØ·o:uꤟq]$ üýý‘’’‚mÛ¶¡ÿþlZRRÊËËáì쌄„ÄÅÅIýZ´hHNN(++c̘1HMMÅ!CpðàA…ýnáÙ³g(**býfë3bÄÿõ¯Kß¾}¡¤Ô|·Z×®]¡¡¡!%ëСÚµk'ÿ¶¬¬ Ož<‹‹ ë‚S—aÆqd±±±¨®®†££#çœÇÅÅ¡U«Vþ{Þ&Nœˆ©S§âúõëèÓ§jjj…ŽéÑ£G¨¬¬„———Ì2 ߦžžŽõë×cΜ9?~<Xÿû'Ož4K¹?d¹ö„‡‡cêÔ©R¿º¾{uñññáÈ?G¡PÈIÓÑÑ——*++ñèÑ#VîííÜÜ\¶lxx8”••! Ñ¥Kö7áÊ•+033S(DãðÖ¼ é­§§‡^½z¡¢¢‚ý¬ÿ>µð‰Yà+k¡/#«ï³æææœ~¨¦¦†ž={¢ººZf,ýú´k×666¸zõ*ëê'''Œ9jjjìµyøð!²³³áííýÖzÅÍÍãbdkk ccc…co3~È~~~œ´¡C‡¾÷¡111¨©©‘;Æ1nõÇ8@vÿJJJBEEœñàÁƒ·>«êÒ³gOŽŒ¹^Í˼¼¼Ož< 8éo{nµlÙ’#gžòÖÜ4–Î;sžgL?rttä¸ÿ5ÔÇ‚ˆ0qâDܽ{Ë—/ǘ1c8yÄb1<ˆ~ø“'OF@@P]]Ý,Ï…¦ŒÅ ŠÞ+ãÇP»¦kÅŠxøðá;ë-> G#ÑÕÕŸqã0nÜ8µþ9K—.EXX¦OŸŽôôt…}8?ÿüs\¹róçÏç,@b|üNž<‰“'OÊ­£î‚üb±GŽÁ¹sç ¬¬Œ=z`òäɘ:uê; ÌÌÂSSS™éæææRùêÒÜ¡{ä…¶ÓÕÕEAAû?snŒeæ—î‡9ïüñ‡ÜXÄuë®ËºuëpèÐ!TTTà›o¾‡‡‡Üòõy—óÛXþóŸÿ`Íš5KKKèééACCƒ](RTTôÎm4<þéé霅ºB¡}ø¥¥¥aÆŒrë‘u¯5æÜº¸¸¨5öîÝ‹°°08::",, nnnÐÖÖ†V­Z…‚‚deeáÕ«WrC9Õ‡Ì---ßš·1zË2š}}}ŽŒyÁ“Õ™4Y¾ø õ[yedáãム  Ü»w;vÄíÛ·ñÕW_ASSžžž ÖîׯŸBõ*BCÇ è¦OŒ!"k\RWW‡¡¡á{ËŒq[·n•ZZYcœ¬þÅÔwüøq?~¼QõÉ:Ÿ½"==DÔ¤qõmÏæšhhÎ>Ö ,À‰'0mÚ4™k]¢¢¢àç燼¼<ÀØØ­[·†@ @UUÕ[þ+BSÆbEï•Þ½{cùò娲e ~øáüðÃìš°ÿüç?2ÏwSá èw¤gÏžøóÏ?annŽœœ$%%)6hÅŠøóÏ?1räH¬ZµŠ“®¥¥ vuÿÌ™3åÖSw¶ÒÌÌ ÀªU«‚ãÇãÆ¸~ý:®\¹‚4ákaÞ‚em"€]`кukNÚDZ€-–×ñe sÞ¿ýö[™oè Ÿ|ò GöÓO?± G>Œ… *üòð.ç·1„††bÕªUèÓ§>,¥ßÍ›7?ZèE\»v 4hTš±±1û0{Ûñ˺ךrn™™ððpLš4 ±±±ìŽ•>>>X¹r%®\¹ÂM²fædÁè¢Èâ¿æ¾'HFŒc"ú໌¾+ÞÞÞ Bxx8òóó!‘HX#ÙÇÇK–,ANNk@+zm>uÇ%f¼©KYYY“Ò* Ó&¹FmÛ¶åÈdõ/¦¾)S¦àóÏ?—[Ÿ¢_ãš“wéCo{n0×ñŸÀÎ;±víZx{{ËŒôED9r$ÊËËqþüyøúúJM¸ÙÙÙ5j‘¬<>ÄsŽñøî»ïpêÔ)ˆD"ˆD"¬[·Dlll³mÄÃÐÍ@»víàèèˆøøx…fïŽ9‚üݺuÃdÎ ;::rss5“ ÔsçÎÅܹsÙ7¹àà`¬ZµJî[õÛ°¶¶ û3\]y‡šTÿû ]»vPUU•;k[w%5sÞ uÞOž<‰Í›7ÃÓÓÓ¦MÃgŸ}†‰'âÒ¥K ¹¯|¨ó{õêUµ¡ê÷ÍåÒ„B!víÚ… 6àË/¿„¦¦f³Õ]÷ÜÊrÛ‘unagg‡k×®áâÅ‹¨©©a ±=z@]]aaaÈÊÊ…Ýìíí÷ïßÇ€Ö{àÀ é- &f¶¬û=33Sfè¨ÿeúöí @€°°0¼zõ ªªªèÝ»7€Úë°dÉ\ºt W¯^…•••BK0}ôCœ KKKܼy?æ©999(--}¯43Æ5úÙÒP}MyV5†¦\£víÚACC>Duu5•‡¡¡>ô¶çFݯHòþi,/^Ä—_~ {{{?~ªªªœ}:qùòe…Ãæ˜ššÂÞÞwîÜAll¬TZUUvìØ%%¥wþÍ„ëªï7[SSƒ-[¶¼SÝïÂàÁƒáï|õÕWœ]=ß…þýûCII üñg¶õîÝ»ˆŠŠ‚ç³¢··7JKK±jÕ*hjj²þÀèÞ½;.^¼ˆëׯÃÉÉIæl,† UUUlܸQ*D£,˜Y ]»vq|Ã#"" ''§·¾3pÔï·dn[ý¿Žžž:uê„[·náï¿ÿ†‡‡ë«êîî---üþûïÈÏÏW¸¿¨¨¨à“O>AJJÊ{Ÿ‘g^†¶mÛÆI“%kn<==ahhˆ4‹ÿ|‡бcG\¸p·oßn eÃÜçyÑèß¿?òóóeº—0cž¬çÖõë×9m•”” 88:::R/ MÑíC€Ñ£GCWWÿý·Ü¯òž $ÓHnÊ17u,~W455Ù~×®( ¼­ #FŒ@§N°yóf\»v ÙÙÙHIIÁÞ½{áããƒÊÊJL:•õÉ‘EAA†‰D‚åË—ãùóçœLÌD%%%ÖÀóóóî]»••±XŒGáĉ>|8«6##^^^8xð ž>>‹ÅHMM•2’UTTлwo¶6æÚ¸¹¹¡¼¼_ý5NŸ>«W¯ÊôÛ}WÆŽ GGGv±Vnn. °qãFlذ¡Y¿¾È¢U«VذaÊËËáåå…cÇŽáùóç(//GJJ ‚ƒƒáíí­ð‚²²2vîÜ "ÂСCñǰϪ´´4?~Æ ãÄ–n,̦-kÖ¬Á¾}ûpõêU©8Æòøí·ß ¬¬ŒéÓ§#88ùùùxøð!¦L™‚ððptïÞ]LX :ׯ_Gee%F·Ý»wcûöí¸zõªBãþûD,cèС())Á²eËP\\̱7˜ûÛÎÎúúúˆŠŠÂš5k““ƒ/^`íÚµX²d‰LÃÛÁÁ-[¶Ä¥K—°zõj„‡‡ãêÕ«rÝ3€¦ÅaüøñXºt)âããQVV†¢¢"œ?žu•mÎ5|; à„°c~jjj4}útN(šú!¥’““e–¯û«6*<<œ¬­­eæÕÓÓ£¨¨("ª!ª®®Î¦11޹¹9ÅÇÇ+tœ Å&ª Ã¥££ÃÑeôèÑœðkLø¢Õ«W+Ôv] cW?¼ƒ““ijjÊÔYSSSê¼899ÑéÓ§9aìNž}Œ6˜˜äääÀÞÞ º@DFF¢²²ÎÎÎhÓ¦ +gÎ=©{´1<}úÑÑÑHII––lmmáíí-ó>ŠEll,ž?ö3|}ê9uÉËËÃ… ‘‘v‡Ìëׯ£E‹RÛ2?yòœãjûRZZ8+ãsss‘œœ kkk6d]MM ®_¿mmm™ ¬“’’ðòåK¹}H̘Ô.ä®;¾àþýûPSS“†/==™™™èÔ©ç‹aMM 233‘““ƒ7oÞÀÜÜæææ(--Ž{÷äö»w²Rf¨­†ŽáÚµkˆ…µµ5zôè###DEE¡ººZJ÷/^ %%…3Ž4t222ðäÉ8::Ê|[[[γ“¹_tuueŽ{yyyÈÊÊBII Z¶lÉÙN\………¸uës»»»³n›¯_¿ÆÝ»wahh;;;©z$ """ ¯¯Ïú˜3®]»†Ö­[Ké’’’‚/^ÀÍÍ -[¶dëhˆúãó³gÏм¼<899ÁÝÝmÛ¶ELL JKKѧONåååÈÈÈ@^^jjjеkWhii!++‹$+‚†¢c1óŒ©ßÏÿŽ7666Re“““ììlTWWÃÔÔ¾¾¾ »Ú) o@óððððððð€k@óðȃ÷æááááááááái¼ÍÃÃÃÃÃÃÃÃÃÓø8Ð<<<<<<<<¨.¤Ð†h<ÿnxhžFÀ»pðððððððððð4Þ€æááááááááái¼ÍÃÃÃÃÃÃÃÃÃÓxš‡‡‡‡‡‡‡‡‡§ðQ8xþµœ?8vìú÷ïÏÊÏœ9ƒ×¯_³ÿ«¨¨°;3ÊÛÑKBBB•¿G ïàhll cccDGG+”?..^^^˜4i6mÚÔ(½š›””ÄÅűÿ èêê–––R;˜}hJJJpéÒ%¤¦¦âÕ«W055…››zôèÑä:=z„ ²²;w–¹«æÿ HHH`ÿhÓ¦ ,--aaa%%~.¦>W®\A||<222вeKÂÝÝ®®® ÞÓ<@||<ЦM8;;£K—.øä“Odæòä ¢¢¢`mm-sGØÆréÒ%v×KCCCtêÔ lT¯_¿Æ;w‹ÜÜ\TWWÃÌÌ ½{÷–¹» "”——#66Ïž=aìØ±2w}ñâ\\\ðÕW_á‡~hR[<< ѬƒóðüC¨ªª"ruu夵oßžp~JJJHÏŸ?ot{ÕÕÕ2ëlèwôèQ…ëoÑ¢ÙØØ(œÿîÝ»€¦OŸÞècinV­Z%÷ØÚÚÒ™3g>Š^sçÎ%uuu™zùøøÐË—/®«ººšúõëGºººRõ¬]»ö=AóñÓO?ɽF;v¤ .|=V®\Iîî˜øAÚk ?&OOO¹çËÚÚZf¹¼¼<:t¨Ì2ššš´sçN™åþüóO@_|ñÅ;éýêÕ+¹z»¹¹Ñ‹/®«{÷î2ë4nÜ8ª¬¬T¸.‘HDNNN¤¬¬,UWUU•Ü2³gϦ–-[RNNŽÂíðð4~šç_Ipp0’’’pôèQ¹yBBB ­­ ±XŒsçÎáøñã8pà222påÊ•Fͺ)))áðáÃùÂ… ‘™™‰ßÿ†††RiŠÐÿÆÀÀ@µ³pGEtt4FŒÛ·o£K—.TŸØØX888`æÌ™pqqA›6m ‰°k×.„‡‡#00çÏŸW¨®šš„‡‡ÃÊÊ ýû÷Gvv6"""Þó4?S§NŘ1cÔ~Å8räâââ0lØ0DGGÃÑÑñ½¶ŸžžŽ¨¨(©/DÿKˆÅbøûû#>>S§NÅœ9s`mmÒÒR<|øgÏžEXX§\ZZúôéƒçÏŸÃÏÏ_}õºv튼¼yòdDFFÂÛÛß}÷zõê…›7oâ÷ßÇÅ‹1qâD\ºtI¡ºúô郑#GÂÃÃíÛ·G^^.^¼ˆíÛ·ãðáÃÐÓÓÃæÍ›ªëáÇxüø1;{¿sçN¼yó¦Á2ÿùϰmÛ6üúë¯Øºu«Bíðð4šmÁóð| ºuëF:::$‹9iÌ týÆÇ“@ Þ,z899zøðá;ÕóazÉ’%œ´!C†1bÄ×+%%E¦<11‘{üø±Âõ±ÿðÃÿÈèß~û“Ö·o_@&Lxïz|öÙg€îܹóÞÛj —/_&Ô³gO¹yjjj8²Áƒš={¶Ìôôôt233#tîÜ9©´æ˜.//'À™Ýôè RRR¢×¯_7¹ "¢£G222R¸Lii)UWW³ÿ·lÙò­3ÐDDÞÞÞÔªU+*--m²¾<< Á;®ñüëxðàîܹ¨««+\ÎÒÒ^^^lååå9r$fÍš%·Ì®]»  ©p;D„ÂÅņ††prr¨Q£Ó`Ùªª*üúë¯ðöö†™™üüüž%­ËîÝ»1|øpX[[ÃÁÁcÇŽ•ÛvHH|||`ii‰¶mÛÂÎÎþþþMj·>Ó¦MP{¾ëʦL™ظq#† ccc¬[·ŽÍ“ššŠ©S§ÂÕÕfff2d¶oßjľQò|“гgO@RR’Âõikk+œW—/_†P(Dpp°Ü<Ó§OÇèÑ£QYY  vöûÀèÛ·/,,,Øk4zôh„‡‡¿³N²®„‡‡c̘1°³³ƒ F…ÐÐP™u$$$`Ò¤IèØ±# `ii‰^½zá—_~aóLž</^ÌŸ?B¡B¡ýjÁ••…)S¦ÀÞÞ:uÂ_|Ç#$$B¡±±±lÞââb…B,]ºøñÇáíí \¸pÍ÷èÑ#LŸ>îîîh×®¼½½±víZTWWKµ˜˜°±±‘{¾êûí^¼x¡¡¡hÛ¶-V¯^-Ó¯×ÂÂË–-Ì;—Óî»òæÍ|òÉ'°··—J³²²‚‰‰ ”””Þ:óû6|}}¡¤¤‰D¢p_ÔÒÒj’ýرcQZZŠ#GŽ4º,"ð4Ï¿æ3d¯^½]VMM @­Q¢©© ‰D‚   ©Ep UUUøùçŸqåÊ8;;+ÜFEEƇk×®¡}ûö …hß¾=NŸ>nݺÉu;©ªªÂøñã±yóf´mÛ...¸|ù2†Š={ö(Ôvee%üüü0}út$%%ÁÓÓNNN ƒ‡‡:$•ÕªU7nbbb`ee¡P;;;DFFâØ±c ³<êžo†óçÏ#44_~ù%¾ÿþ{ÃÅÅ…Íó×_ÁÕÕ„úôéƒÄÄDÌœ9ƒ ‚D"yg½^¼x Ö¸ø899!44+V¬™~ïÞ=ìÞ½b±˜}9\ºt)&Mš„û÷ïÃÆÆB¡¶¶¶¸~ý:N:õÎ:ɺFK—.EÿþýqñâEtìØ...¸rå † ‚ H•ONN†§§'BBB ¡¡???xxxàõë×RÇYYYÉŽ•••‹Å‹Åì‹PkÀº¹¹aÿþý022‚››îÝ»///œ={"‘/_¾”ªS$!,, ~~~ضmTTTàèèÈÞ'ÇŽƒ‹‹ ‚ƒƒ¡¯¯Aƒ¡¸¸óçχ···ÔýÔ®];€H$RØÍ„¹_~ù%Z´h!7ß„ `dd„ääd¤¦¦*T·¢hkk£_¿~ÈËËã¸i\¿~Ïž=ƒ——tttÞ©}ûö¡¦¦þþþ2_š“>}ú@³¼ÈóðÈä#Ï€óð|p„B! „„™éò\8233III‰°‹¦BCC Íœ9“Sω'ä¦1ÈráH$Æù”›ššJúúúdbbBåååRi-Z´ äääD¹¹¹¬<66–Ú´iC­[·¦üü|V.Ï…cÙ²e€æÌ™C‰„•çääP‡¨M›6RõPË–-9 +% =zôHîq×¥!Ž#FòóóceFFFìÃú.EEE¤¯¯O-Z´ Ë—/³ò²²2òõõ%´qãF…ô’ǹsçÙÛÛK}Zn ïâÂ1|øp¹n ³gÏ&tüøq"ªuhݺ5ikkK]7"¢7oÞ(ì‚Ò Ç€;–ˆjï9%%%255•º233ÉÚÚšPDD+Ÿ3g ;vpê®ïFó6Žþýû –’ÿøã¬ÛÍùóçYynn.+ïß¿?J•{þü9µjÕŠÌÌÌ8nVK–,!´~ýzVVZZʺZ´jÕŠ&MšD§N’ê“õñöö&$‰ä機÷äÉ“¬¬¹FEE‘ ÑܹséôéÓ4oÞ<211!KKK©k¦(×®]£Ã‡ÓêÕ«iÀ€¤¡¡A>>>r}ª¢.DDmÚ´!}}ý&·ÅÃÓ¼Íó¯ÃÙÙ™Hù¤Ö…1 CCC)22’®\¹B ,`Œnnn¬qYSSC–––Ôºuk*++“ªgàÀ€âããåêÒXhÆðº{÷®”œ1 CCC9e£xåÊ•¬L–]TTD-Z´ :È|8ù„”””èÖ­[M®ç] è“'OúòË/¥ä•••¤§§Gúúúl”ƒòòrÒÐÐm¦10ô¬Y³ØkD^^^$HEE…½'íÛ·Sã;tèPV6mÚ4@111oÕ£!úþýû€úôéÃI{óæ µk×N®­ªªJOŸ>å”c^Hê¬ ‰„LLLÈÂÂBJž@nnnœèîîîÄy1fôR$²s®ê¾È4—MDôðáC²²²’ÒÝÌÌŒ’’’šTß Aƒ¤êrvv¦ØØØwÒ±143¾ÊëyxÞÞ…ƒç_ÇË—/¡¬¬ŒÖ­[7˜oðàÁðôôDß¾}±zõjdddÀÏÏÇŽƒŠJm@€/¾ø%%%R¾vOŸ>ÅÅ‹áááÑ(÷ºÜ¹s¿ýöfÍš…qãÆ! €õ}òä '¿ªª* À‘6 ×Gµ>III¨¨¨€³³3Ç ÞÞÞœ:˜XÅB¡“fmm '''¼xñÖ¯°°ƒF^^~ûí7xzz6ºŽæ`È!Ð××GHHˆ”Oê™3gŸŸñãdzn-Z´€P(DLL „B!Ž9‚ÒÒÒ&·½mÛ6öÍœ9W¯^…©©)BBBØÄ ]ƒáÇCII‰õj£¯µ>«+W®DZZZ“tcüÑûöíËISUUEï޽喵¶¶†™™GÍú×ï 077ÇÓ§OQQQÁ–騱#¢¢¢¥K—bðàÁÐÐÐ@TTfΜ‰Hù03þ½u]`äÁ䩪ªzkÞÆ GGGˆÅb¬X±‡ÂÊ•+Q]] ggg…ÝÀêòË/¿àܹs8zô(/^Œ¬¬,tíÚûöíkvýeѦMrÛáái.ø0v<ÿ:B X6mÚ„V­ZAYYÆÆÆì¦õ™6m/^Œ;w² ªvíÚ…šš|þùçÖO"‘ÀßßgÏž…ºº:,--¡«« UUU”””ŠŠŠ8å e.¶a YFw]¿ÊãÇãøñãró=}ú”ý{ñâŨ¬¬ÄÑ£G eeeôìÙ“'OÆ”)Såç8lØ0Œ1ÚÚÚ055…ƒƒ4448yuuue.}üø1ÀÔÔTfæææˆÇãÇÙ‡«"”––bàÀˆ‹‹Ã¢E‹0þ|…Ë67ªªª?~<6mÚ„³gÏÂßß°wï^µ¡æê²lÙ2ÔÔÔàĉ‰DPUUE¯^½0uêTLœ8±Qm=ƒ†@ €ŽŽLMMѱcGÖ`j¯¶¶¶ÌT555!##UUUPQQ··7~ùålݺ‹-¢E‹`mm#F`áÂ…ÐÓÓSH·ŒŒ à„ƒd`ü“eadd$Sžšš "â¼ÀÕ'33SjÑ©@ @—.]ØÐ‹………سg–,Y‚K—.aÇŽìâc[[[dffâÑ£Go Èô=“ó5–gÏžá³Ï>ƒ¶¶6âââ¤6Œš>}:œœœ0sæLv!ª¢Ô =9zôhL˜0]»vÅ—_~‰¢mÛ¶ÍzõaÆù÷íoÍóï„7 yþuàùóç(**‚®®®Ü|rwÿª‹žžÆŒƒàþýûpppÀž={ ££ƒ±cÇ6Z¿•+WâìÙ³øâ‹/°zõj´jÕŠMÛ°a¾ýö[™åä-Z*++©zd¡¥¥˜2eJƒ†Ý…DæææƪU«‚ãÇãÚµk¸ví®^½Ú¨™&6ºÆÛ`¾Ô‡‰tÁs}˜Ù×·}}¨Ëëׯ1xð`ܹssçÎÅòåË.û¾˜2e 6mÚ„}ûöÁßß¹¹¹8þ<:uꩼ:tÀ‘#G™™É^£Ë—/ãòåˈˆˆ@PPÂívéÒå­×H[[ùùù¨©©‘ùBWZZ MMM©¯8?þø#,X€“'OâÔ©S8sæ Ö¬Yƒƒ"66oÕ1úd½\h𫃼ûIKK ¸|ùrƒF˜¼6]]]Ì›7)))øã?péÒ%Ö€¶±±Á¥K—دòH$ì,{ÇŽl¯±\¸pb±œÝVõõõ1zôhlÞ¼/^lÒ¤ƒ­­-úõë‡S§NáöíÛ>|ø»ªÞ Ì5Wäþáái,¼ Ï¿kkkµ³.Íó0ܵkNŸ>/^ 00°ÁUõò¸zõ*~ûí7ŽÑ[÷Ów} PXXÈ‘§¤¤¨ Ã×ÌìWnn.<<<äþììì8eMLL0oÞ<ܺu iiihÙ²%öïßÏF«øP0×–q3©KMM RSS¡¬¬¬ð,ZEE†Š›7oböìÙR¡ò>&;w†³³3Î;‡¼¼<ŒÜÜ\ 2999R!ûƒ\– Ež«RÝðuŠÂ¸4èêê6Ø'íçƒ  mä3Ó5¹cÿþýxùò%:vì77·FKC0í¶lÙRf:#¯ëªò®Ôýjñ> "<{ö mÛ¶mÔ 3¢ð4Ï¿&|Ý;wš­NtîÜÁÁÁظq#4y¦F,ƒˆ8./_¾DHHHƒeeÍ&nÛ¶ 0pàÀËvèÐ;vÄ… pûöíFjý_¬­­Y¿ï½cã¾cÇNÚ©S§¾}û*ôð‹Å6l®^½Š3f`Ó¦Mo-óâŠܾ}éééW¾‘L™2‰‡ÆÞ½{¡ªªŠ &(TÖÁÁ}*//oV½º[¶l‘Ê#–-[²÷kÝ{ÈØØ€ìÉîÝ»ÃÂÂÇŽãœÿ°°0Ü»w¯GQˈ#?ÿü³Bù=z„üü|¹é̆Î;³²Þ½{cÔ¨Qxõê¾ýö[™1žSSSY–,Y¢ öŠãää 6Îxýö«««ÙÐvRiQQQ¸}û¶Ô MC¾Ü¸zõ*ÀÕÕU*-66·oßn¶û199EEElìþæB,#''§Yëäù‡òQ—0òð|˜ä¦L™"3]^»·±sçNvµyC;‘ÕEVŽ êׯݼy“ÊËËéÊ•+dopR> IDAToO€vîÜ)UO‹-¨eË–¤¥¥EAAATXXH9994wî\6rH]ä…±‹ˆˆ @@zzz´k×.ÊÌÌ¤ŠŠ zøð!ýõ×_äççGaaaDT»;š——¯_¿&cccRRR¢_~ù…²²²(;;›V¬XAJJJ¤§§'a̘1´lÙ2ЧÒÒR*(( ÐÐP255%@ í„ Ù¹sgÚ¹s']¹r…n޼ɦ‡„„úä“OhÍš5$‰hñâÅÔªU+rww—…Ã××Wæ±ÔÔÔP÷îÝÙ0}qqqTVVF¹¹¹Ißÿ½Ô8D-Z´ 3fÐñãÇ)!!²³³éÆ4cÆ @ZZZœ¨OŸ>e£_ôíÛ—Ž=JéééEëׯ'mmm@sçÎåèÈDáèÕ«—Ì{4((Hê˜eñæÍruu%äïïO111TPP@±±±4jÔ(@ŽŽŽœ[••• ½zõŠ•EEEQçÎiÓ¦MFÉÉÉtëÖ-úý÷ßÉÄĄзß~ËÑwïß¿/%/((:uuu@[·neeõÃh2×ýùçŸ {c9þ_ý5 6¬Yëäùg" jÄÞ¶<<ÿGF`` :„qãÆI¥Ý¾}b±=zô€ªªªÂuJ$´o߉ÙÙÙ2£GÔ'::eeepww—ò£¬®®ÆéÓ§‘œœ ¸¹¹ÁÉÉ /_¾DJJ lmm¥"ܸqêêêèÖ­rrrpóæM¤§§ÃÕÕ½{÷æèRZZŠ{÷îÁØØXæ¶Ãb±·oßFrr2^¾| ###ØÙÙ¡GPVVæäKIIÁ‹/ ££ 0@*_CdeeáñãÇ077‡¹¹ù[óGFF‚ˆÐ½{÷óEEE!>>ùùùpttD÷îÝŽèwïÞ}« Š ëVÔúÕ?zôFFFœ­Àãããeú¨3p>‘¿ æÜ@Ïž=e.†«¨¨@dd$RSS‘›› ]]]ØÚÚ²Û*+ÂÓ§OñôéSXYY¡}ûö •©¬¬DDDîß¿šš8;;£G2ý…“’’ììlLMM1`À¹‹x ™™‰ââb¨¨¨ GRéD„‡BMMõw9r$Nœ8üü|6‹D"ADDÚ´ióÖp“©©©ˆ‹‹CZZ455Ѿ}{ôéÓGæµäädÜ»wÏŸ?GEEÚ·okkkôìÙó­!²²² ##;v쀑‘"þ{÷åÑ5pø·,Ml€bAÅ‚‚ˆQ¢&ö^‰½ƒÄcï%¾Ö(öcKìh"&öÔ7*Š5j±¼¢ØT ÒÙýþàcà HYtÏ}]^ç™™ç¸8ÌΜ9}:͵ûaaaš}é±²²ÊôÆÃS§NqõêUBCC5_÷é•}šëׯóäÉ,,,°±±á‹/¾HwC_ò÷]­µØoÞ¼áüùóïŒÙÙÙYkó½{÷(W®Ç×,©Ë)¶¶¶„††‘f% ¡?$zI¥RáììŒZ­& GÊ­\¹’aÆ1}úôL¯™B|x!!!8::R®\¹ “ͼfÑ¢EŒ?[[[NŸ>ýÁK¿}  ÄÖ­[¹qãFŽVà¸yó¦fìŸþIóæÍsllññ‘M„B/°téR^¾|ɱcDz=NBBaaaøøø0sæLÊ”)Ä r0R!Df…„„зo_|||¸sçÁÁÁlÞ¼™ÆËܹsub–7OOO (ÀĉÓÜd(þƱcǘ9sfŽ—¯;tèæãƒæèØâã#3ÐB¼‡€€ÍŽz¼½½5[!r×­[·¨R¥ ±±±Z×ÍÍÍY²dÉ;Ëü ‘'''.]º„Z­¦H‘"„‡‡ë:$¡C’@ ñž={ÆöíÛ)^¼8-Z´ÐF"„Ðèèh.^¼ÈljÇÎÎŽ*UªÈצx/111,XP«dßíÛ·3µoC|š$NG\\—.]âŸþáùóçôèÑ#Í jµš-[¶àç燅…;v¤^½z:ˆX!„ÂáÇSÕÒ_µjƒÖQDB×d t:êÕ«‡‹‹ £FbÒ¤I\¿~=UµZ››C‡’v^7lØ­[·æv¸B!„ø@<¨µÙ\¡PÈ:h='3Ðé8zô(eË–%22’š5krðàÁT¿};vŒfÍšqèÐ!ÍÉZƒfÇŽ<|øðƒU*„Bˆ¯R¥JܼySëZ¾|ù¤œ“èt4kÖŒŠ+¾³——¦¦¦Z¥l¾üòKÂÃÃ9pàÀ‡Q!„ØíÛ·S%Ï´ÞÞÏÏO‰¼ uÕ}‘iÁÁÁT®\YëÀˆjÕªiî%‹‰‰ÉõØ„BñþöíÛ—æu…BÁþýûiРA.G”{2s ˜¾’ú=ûLסè yÍsŸ¼æ¹O^óÜ7cÆ æÏŸmÛ‚‘ìÝûîCÕªð÷ßð÷ßDDD|Ô³¶±±±˜››ë:ŒCÃÿO ‚»wßÝX¡€G’’h33 iݰüÿúôÉŽ÷àááALL GŽÑ\Û»w/E‹¥}ûö:ŒL!„ï-EA€4©Õpû6¤8¡Pè™NÇ‚ صkQQQŒ=š3fШQ£¤·v€&MšÐ§OºwïN= ãàÁƒlÞ¼Yj@ !„³7oàÙ³ŒÛÅÇg9rJ:>Œ¿¿ªëvvvtîÜYóyFGy§¬Â!»Y…Bˆ¼- €š5kBíÚpá¿7Фç´Ô«&&püøGÿ³^ò–Ì‘ú“ˆB!ÄÇC“@—*•´¾Y­NJž “f›!éãÄÄïYZBµj’@ëY-„Bñ¶°°dc㤂ɬ­¡`Ág¥Ÿ=94M¯H-„Bñ¶ÄĤÿC¿~²´›‘ôD'ËÌziñÉZ!„"-ÆÆàîž4ãœrÅ«B‘”P÷ïŸTîàùsÝÄ(tBh!„Bˆ·&Í<—*•ôùÛ 4h'ѹ£Ð)c'>z»ví"..N×a=åââ‚­­­®ÃBä°°°¤š4ù7y†´hø7‰þùgY­G$½þýû£P(rü˜t!2Ί+$â˜ôA‘"Ú7Tª?N™@CRíägÏ~ØàDž! ´ø(…†† $Õâöòò¢cÇŽ:ŽJè›Ê•+ë:!DkÒ¤IÚ7Ò›N&åÞôŠ$Ð⣴téR®]» Ë7„Bä˜tOÎ(zE6Š’*å[iB!ć–26ôIßÉ¿!„BˆŒ¼k ´Ð;’@ !„BdäÕ«?–õÎzOh!„BˆŒ¤¬óla¡»8Dž ´B!DFRž4( ´Þ“Z!„"#2-R2vB|=zÄÙ³gñ÷÷'_¾|8;;S¯^= .üÁžyéÒ%®^½JëÖ­177ÿ`ÏÉŽ›7oR¿~},X€»»{¦úrüøqÂÃÃùüóÏiÖ¬Yú媄"¥˜øÿ³I …$ÐB?íß¿Ÿ\}fýúõ©]»v–û-[¶Œ &‹µµ5ÑÑÑDDDP¢D ¼¼¼hÕªU¶cZºt)K—.åüùóXYYiÝóööfþüùøûûãää”íg| <~ü˜¨¨¨Lµ_¾|9#GŽÄÌÌ KKK<==iÞ¼9»wï¦@8Z!ÄG/åò 33ÙD($úiÝêuøö£˜Q±\yÞ½¸{L÷œžåzܸq,^¼˜–-[²nÝ:Ê”)ÀÅ‹éÕ«mÚ´á÷ß§[·nÙŠëÅ‹ܽ{—ÄÄÄlõÿøùù1bÄ:wîÌÖ­[166ÆÛÛ›¾}û2räHÖ¯_¯ë…y,ßo‘Zè'54Khư„a¹ò¸a¦YΕ+WøñÇùâ‹/ðññA©TjîÕªU‹³gÏR±bEÆG‡0ýÿ‘ÐÐP"##©T©¯^½âôéÓ$&&R¯^=Š)¢ãÑ£G„‡‡IK"^¼x@éÒ¥³4+{ãÆ ‚‚‚P*•8;;k’|€—/_J™2eÈŸ?ª¾wïÞ%!![[[­ë÷îÝ#00ØØXœœœ¨X±b¦ãyÛªU«øá‡4K6z÷îͺuëØºu+‹-ÂB~ !ÞEhñÙD(Dµ`Á˜5k–VòœÌÒÒ’‘#GrïÞ=¶nݪ¹>iÒ$\\\Ø·oVVVtéÒ…:P®\9öï߯i7fÌV®\ @ƒ pppÀÁÁ#GŽd*¾gϞѩS'ìììèÚµ+;v¤\¹rüç?ÿÑÌh?zô/^œªÿ›7o¨Zµ*“'OÖ\‹ŠŠâÛo¿¥lÙ²¸ººÒ£G*UªÄÀ‰‰‰ÉÜ ÷–}ûöQ¸palll´®W«VèèhþüóÏl+„Ð#’@‹·H-DuùòeŒŒŒhÔ¨Qºmš7o®i›Rdd$C‡åÈ‘#¼yó†[·náàà@×®]¹{÷.6l`âĉ\¿~ˆˆ"""h×®]¦âëÔ©ÇgþüùÜ»w«W¯2`ÀæÎËÆppp N:lÚ´ uÊcp;w‰‡‡‡æÚ·ß~ËÆ™:u*ׯ_çöíÛLš4‰uëÖ1oÞ¼LÅ•RDDÑÑÑT­Z5Õ½jÕªððáÃ,+„Ð3÷ïÿûqÑ¢º‹Cä’@ ‘GSªT) Òÿ2­P¡‚¦mJ‰‰‰ 0€  P((_¾<óçÏ'..ŽE‹`ff¦YöQ¨P!ÌÍÍ177ÇÈÈ(ÃØöìÙÃÉ“'2d&L téÒ888ðóÏ?S¶lY,X iëááÁ­[·8yò¤Ö^^^X[[Ó¢E üýýÙ²e ݺucæÌ™ØÙÙaccÜ9shР .$!!!¯Ü¿BCC“å”’¯%·Bˆ4={Ož$}l`•*é6‘'Èh!ò µZZ­~gò hî§µ °Y³fZŸ×«WΟ?ÿÞñýõ×_Ô¬Y“¿þú µZJ¥B­VS«V-öìÙCll,&&&ôìÙ“Ñ£G³iÓ&6l$­}>~ü8&LÐ,O9sæ jµšºuëjÆL·jÕªøùùqçÎ,­‡V©Ti.I¾–ÜF!ÒtíÚ¿—-›T…Cè=I …ȃ •*UâêÕ«¨Õj Ešínݺ€½½½Öu¥RI:u´®™ššR£F îÝ»÷Þñ%?÷]Õ?ÂÂÂ([¶,æææ|ùå—lß¾åË—cffÆæÍ›Q«ÕZË7’Ç1bDºc>|ø0K ´µµ5AAA©î%_Kn#„iJ¹D®JÝÅ!òYÂ!Dåàà@\\gΜI·Í‰'4mSJLLLµ¬C¥RqãÆ Š{ÿÒ}EŠÁÈȈ¨¨(ÍLñÛÊ–-«iÿÕW_ñúõkvíÚÀ¦M›¨[·.•+WÖ’*‚¤7æ»Öƒ§ÅÒÒI …Ù aaI+ðÖ÷Z¡¿$";v, …‚iÓ¦¥Ú€I—,YBÉ’%éÝ»wªûGÕú<00ˆˆ­¤5ùð”—/_f)¶ªU«ŸêéiÑ¢¥J•ÂËË‹S§N¢5ûœ<&ÀÁƒ³KFÚ´iCDDDªÍ‚AAA§Zê"„¾¾ÿ~\½:ÈÁKâÿI-DU«V-¾úê+Ž=J÷îݵ’Ü4hÀÓ§O™;wnšu›7lØ@Äÿ—^JLLdéÒ¥ öoMêä |Hwƒ^\\111ZzõêEÙ²e9rdªÙÝ'Ožhfš“àææ†¯¯/žžž˜ššÒ£G­6íÚµÃÅÅ…iÓ¦¥ÚpøêÕ+­R}Yñí·ß0uêT͵ÇãëëK§NRÀ(„MPPf3`“&M(]ºtº}.\Hƒ 8yò$ŽŽŽ4oޜ֭[§jwìØ1ÂÃÃ9vìÏŸ?×,ñèÔ©åÊ•Kwü† 2cÆ vìØAPP?¦V­Z¸¸¸¤ù{{{~ùå"##iܸqšcV¬X‘³gϲgÏüýýyðà5jÔ`„ ´oß^Ó®D‰üôÓOšª™9s&Í›7Ç××—ððp&NœÈ—_~‰™ì¦B¼ES™çÍ›¤ÿB&¿×ý! ´ÐKjÔü­ø›™Š™¹ò¼ñÞ«ÕªUÓ< $#®®®¸ººfØ®hÑ¢tïÞ]ëZ:uRUòHKŸ>}2Oß¾}3lc``@çÎéܹsºmÌÍÍ4hP¦Ÿ I fn!„þZ»v­ö…6mà­wÌ„Z襶Úb]&÷ª/ØaGÍš5síyB!²nïÞ½¬Y³æß 5j@­Zº HäY’@ ½”ÕÙK!„Ÿ¶Í›7óõ×_ÿ»„ÃÜR,"%ÙD(Ä'fíÚµ<~üX×a!ÄGcáÂ…xxxhW#úâ 02Ò]P"O“ZˆOŒ‘‘&&&ºC!ò¼ØØXFŒÁ„ 4õö5‡+,¨ÃÈD^' ´B!ôÎ… R•³¬Zµ*^^^º J|4$B!„Þˆ‹‹cÊ”)Ô«W+W®h®×¯_???9\IdŠ$ÐB!„Ð >>>ÔªU‹Ù³gkÖ;ãé鉯¯/:ŽP|,¤ ‡B!>i‡fÚ´iüý÷ßZ×Ù´iS¶êì ý&3ÐBˆNbb"aaaDGGë:!DväÈ>ÿüsZ·n­•<1kÖ,Î;'ɳÈ™â#pìØ1Ž?Ž¿¿?ùòåÃÙÙ™öíÛ¿÷7þ˜˜bbb(\¸0 …BëÞ¡C‡8~ü8#GޤdÉ’ïõœœvãÆ øé§Ÿ2¬éÀÁƒ¹téaaa,X*Uª0lذw….„ø8EFFâííÍêÕ«ù矴î)•JúöíË´iÓ¨P¡‚Ž"ŸI …^Z¶lYªo¬Z·nÝh×®]–úDFF2bÄ6nÜH±bŨ]»6OŸ>eΜ9L›6 OOOÆŸ*ùͬyóæñý÷ßJ‰%´î?~œùóçÓ³gÏ<—@gżyóسgUªTÁÊÊŠsçαÿ~~üñGþûßÿÒ A]‡(„È/^dÍš5x{{©uO¡PУGf̘½½½Ž"ŸI …^:vø0Á>>|–KÏûC©¤råÊYN ûõëÇ®]»;v,óçÏG©TðâÅ zõêÅĉQ*•Œ;öC„ýI˜5k7n$_¾|¨ÕjöîÝK§N4hÖ.|!ÄÇåéÓ§ìܹ“uëÖqñâÅT÷ ;väûï¿§Zµj:ˆP|ª$z«%°4—žU7§Y=z”]»vÑ­[7-Z¤uÏÜÜœ}ûöaggǬY³èׯE‹àܹs<|øÎ;söìY|}}ILL¤Y³fÔ«WO3Æ™3g¸|ù2{÷î¥páÂ4hЀR¥Je*Æèèh8@PPJ¥Ú¶m«¹ïÞ=þúë¯tÇ<|ø0‰‰‰Z}ñññ! €ØØXœœœèÔ©“æ—‡¬ªT©’ÖçÉ?PkժťK—HLLÌöØBˆÜ÷èÑ#víÚÅÎ;9yò$‰‰‰©Ú”(Q‚¯¾úŠo¾ù†òåËë Jñ©“Zˆ·oßÖ$ÍgÏžÕœ˜’-Z´`àÀ¸ººbh()Žøpä_—yÔõë×155}çÛŽuëÖÕ´MéåË—lÚ´‰›7oR¢D âââèÝ»7“&M¢eË–8;;³|ùrŠ)Â÷ßÏùóçS­~—¸¸8ºvíJLL ‡¦Y³fÄÅűqãF†Ê¢E‹˜>}:ÖÖÖ´lÙooo.\ˆQŠ™ø-[¶‡‡‡æš»»;AAAìܹ“víÚa``ÀîÝ»qsscÒ¤I¬[·.Ó1¾íèÑ£¼|ùöïßµµ53gÎÌöxBˆ'""‚“'OrüøqŽ;F```ºmíííéÞ½;ýû÷§\¹r¹¤Ðk’@ ‘©ÕjnÞ¼‰Í;Û%ÿ°¸qãFª{ Ð$ÅÆÆÆLœ8‘;wòÃ?ðË/¿¼W|¿þú+ÁÁÁ,Y²„–-[/_>† š5kX³f Ó§OÀÃÃâãã×_~©cÓ¦M888ðÙgI+Ñ“+Œ1‚Î;kÚuïÞooo6nÜÈŠ+055ÍVÌC‡Õü¢‘?~|||4Ïú'<<œS§Nqûöm"##)W®ÎÎÎ8::¦ÛçÕ«Wœ?žàà`ÂÃÃ144ÄÆÆ†zõêåXE‡[·nqïÞ=\\\ÈŸ?¦ûÞ¾}›»wïP¬X1ªT©’nÛ;wîpçÎ4ÛFGGsîܹT}LMM±¶¶ÆÚÚ:ÇgwŸ?®I˜?Î¥K—P©T鶯^½:]ºt¡K—.ïü&ć" ´yB¡ÀÌÌ,Ã:ÇÉ÷Óú!Û¼ys­Ï)T¨PŽlšKž Š‹‹cÕªU¨ÕjT*jµš"EŠHdd$ àË/¿ÄÂÂ///MH@@óçÏO5¦‰‰‰fÌäqQ©T„„„dû‡å©S§ˆ‰‰áÔ©Sìܹ“Æ3vìX.\øž¯†ø˜~ùåöíÛGhh('OžÔJéÝ»75ÂÑÑ‘’%KrçÎNœ8ÁúõëÙ¶m‰‰‰ìر#Û±õïߟ¸¸8òçÏÏ›7o²=N¡B…xôèûöíÓz7'ÙåË—9uê… âÕ«WéŽcjjªõ5ÊéÓ§9tèsçÎåÎ;x{{g*¦gÏži%ÌAAA&ÌåË—§qãÆ4jÔˆfÍšIÝv‘§H-DU³fMMÍâN:¥ÙfïÞ½899i]OLLäâŋԩSGs-..ŽK—.åÈ©[eÊ”A¥RqâÄ MõŽwñðð`ÕªUx{{3dȼ½½iÕª•Ö,_™2e€¤å!Õ«Wï3âææÆøñã9}ú4­ZµúàÏyƒ wîÜI•ŒÕ¨QƒN:Q½zuΟ?ω'4Ë“ ©šË¯¿þªÕ§|ùò4iÒzöìÉž={4ï¼dÕªU«Ø¿?;vì`ðàÁï•@÷èÑ///~úé§4èŸ~ú HúX¹reºãÒ±cG­kƒfÛ¶môèуß~û•+Wbaa‘ªoxx¸&Y>qâ—/_Î0a®X±"5¢Q£F4nÜXó=Aˆ¼HŽò"úî»ï055eÚ´iiþ0½}û6«W¯ÆÑÑ‘nݺ¥ºŸ\a#Ù¹sçˆŽŽ¦fÍšškÉk¨?~œ¥ØjÕª…Z­ÖzÛ÷]\\\¨R¥ ^^^øøøðäɭ̓ÉclÛ¶-K±dWòR–²eËæÊóDÞP¸pátg2«V­ªYº”é1Û¶m«y—(;ï’\½z•qãÆáææF—.]²ÜÿmÅ‹§sçÎ=z4ÕþˆÈÈH~ýõWjÖ¬©Ù„œU®®®£V«‰ˆˆHuÿþý+VŒ®]»²bÅŠtg›íììøæ›oزe <àÆ¬[·777IžEž' ´yTÙ²e™6m—/_æóÏ?çäÉ“DGGóâÅ vîÜI:uHLLdÙ²e©J±°víZMÎÔ©SQ*•Œ3FÓ.y†mõêÕ\»v°°0bccµÆºpá‚fcOòŸÒ¥KS£F Æ··7ñññ@Ò,wò †oóððÀßߟ)S¦`aa«««Öý† Ò©S'.\ÈŠ+4‰HBBçÏŸgòäÉY~ ãââèß¿?§NâéÓ§¼xñ‚0|øp6mÚÄ_|‘jù‹Ðo<ÈÒiu«W¯&!!-Zh– eVll,½zõÂÊÊŠåË—g©ï» <µZÍÏ?ÿ¬u}Ë–-¼zõŠÁƒg{ìíÛ·GµjÕÒÜE­VÓ£GTí“KÑ1dÈ47mذ±cÇ2bÄFމ••ááá$&&R§NÍ©¬øý÷ßÙ¸qcªëmÚ´aݺui®·úiëÖ­S¶lY7nœn»uëÖ‘@HH'OžäŸþ¡}ûö©<ÊŒ‰'ÄÑ£G3µ*³5j„ƒƒ^^^Ìž=[S½æçŸ¦P¡Bôîݛݻw¿sŒøøx¼¼¼4Ÿ'¯>qâ•*UbÁ‚iö+Y²$•+WF©Tj–c4lØâÅ‹çØßO]“Zˆ<®{÷î´oßž   ‚‚‚055¥F888¼³””»»; 6äôéÓ¨Õj4hær…~ø~øàà`ž?®I°‡šjýcJ*Tà»ï¾ãÊ•+ñøñcJ–,‰‹‹Kš'•(Q‚‹/êtÀdæææ¬_¿žÿüç?ðàÁŠ+FõêÕµ6E–+WŽ3gÎdX:ÌØØ˜ˆˆΜ9CHH¯^½¢T©R8::¾³Ä—Ð?þþþ|ýõ×(•J~ýõ×w®c>|¸ÖRêÕ«3xðà,ÍZCÒ†ÙeË–1jÔ¨wV½È®Aƒ1räH¶mÛ†»»;gÏž% €¡C‡fªZHll,_}õUªëæææŒ?ž¦M›¦Û×ßß?Û%'…øH-ô“BÁfCCåÒIUwããI?͘™™uêÔÑÚ˜åÊ•ËôÁo/e(S¦L¦Ö!:::fº´\52ÕÎÖÖ[[Ûtšfzý¦±±±fc’i  eË–ÄÆÆ²víZêׯÿÎö¨T*ÂÃùxñ"K—.¥]»v 8Õ«Wgê™Éû˜3gNNü5Réׯ“&Mâ§Ÿ~ÂÝÝ]ó.Ó Aƒ2Õ?_¾|üõ×_šÏãââxøð!^^^ 8uëÖqäÈ ,˜ª¯$ÏâS' ´ÐKƒ‡§uûö¹úÌìnØB|8ÿý7­[·æÕ«Wlܸww÷ û¤üe³~ýúôêÕ '''Ö¬YCÏž=35›<`Àž?ÎÁƒ?X²Y¸pazõêÅúõë9zô(Û¶m£Aƒ™®Ä£T*Ó\"Ö©S':vìÈÞ½{Y°`³fÍÊéÐ…Èó$zéS.[V²dIÙ'D&øùùѾ}{bccùí·ßèÚµk¶Æ)V¬mÛ¶eýúõœ8q"S ôÙ³gIHH ^½z©î%oä­]»6 …‚U«VÑ¿ÿlÅ6hÐ Ö¯_OÏž=‰‰‰ÉôìsFZµjÅÞ½{9zô¨$ÐB/I-Ä'fîܹºAˆ<ï?þДÜ»w/­[·~¯ñ^¿~ é*={öL÷“ßÿ˜˜Z·n……Å;—3e¤víÚÔ®]› .`ee•í_ÞöâÅ ŒŒŒrd6’@ !„Ð+ÞÞÞôë×333öïßOÆ 3ìóðáÃtK­;wŽРA­{þþþœ8q‚2eÊhÕx^¶lYºÏòññ!&&OOÏ,åžßÿ{÷îQ´hQŒß{¼hÊãe´^\ˆO•$ÐB!ôÆ… pssC¥RQ©R%6lØÀ† RµkÑ¢}úôÑ|>nÜ8Ο?OÛ¶m)[¶,E‹åÉ“'œ={–}ûö‘ÀŒ3Rm”=~ü8cÆŒ¡I“&9rHJvT¨P!Ê5iy{ÉGò&ÂãLJ³³s¶ê³ ñ)Z!„Þˆˆˆ@¥RpñâE.^¼˜f»B… i%Ðõë×çŸþIó°“Zµj1~üø4ëŸÌ´ªŠR²dI>ûì3ÜÜÜpww—jBoI-„Bo´hÑ"ÍSò22tèP†Jxx8< ""‚bÅŠQºtéw€2zôhF¥g=yò$Ëñxzzâéé™éö}ûö¥oß¾©®W¬X1[¯‘úDh!„"“Š-šå㺅Ÿ9ÃVñÑyóæ ‡âþýûºE!„’Zˆ<.!!%K–àêêJ™2e°³³£gÏžüþûïï=ö;w4‚Þ¶qãFZ·nÍ­[·Þû99íþýû´iÓ†?þø#Ó}._¾Ì×_££#–––8;;³xñ⥠V«9yò¤®ÃBä0YÂ!ôҨѣ8øçÁ\{žL;¯¾ú*Kýnß¾MÏž=ùûï¿iÚ´)îîîDEEáëëKÏž=Ù»w/kÖ¬¡@ÙŠËËË‹ï¿ÿžÐÐPJ”(¡uïúõë>|8ÝZµ“;wÒ¯_?ÌÍÍi֬ŋçÆìܹ“±cÇê:<ñ‰ æ›o¾¡I“&©ÊÛ !>n’@ ½t#äÁ‘Áà;Ï3úÛˆÇg©Z­¦W¯^°yófÜÜÜ4÷T*&L`ñâÅ/^œ~ø!§Cþd„††âææF­ZµØ·oš{ñññ:ŒL|ªðôôdÖ¬Y¨T*–,Y¢ë„9Lh¡¿JusçQÊÿ)³Üç—_~áܹs >\+y000`Ñ¢E?~œ•+W2dÈ*UªÀöíÛ¹zõ*S¦Laݺu;vŒÄÄDZ´hÁÀQ(lݺ•#GŽ0{ölòçÏ€»»;UªTÉTŒwîÜaíÚµ¡T*qqqaäÈ‘š±®]»Æ¦M›èÕ«Wªú¸‹/ÆØØ˜áÇk®…‡‡³jÕ*‰ÅÉɉQ£Fe{ã–§§'111¬ZµJ+y9EMä¼þù‡¯¿þš€€ŒqvvÖqTBˆœ&k …È£’×8O˜0!Ý6£G&>>ž={öh®ýñÇ,^¼˜~ýú±lÙ2,--yúô)ƒ ÒªkûèÑ#ÂÃøyó&ÿûßÿøßÿþ§9’8#{öì¡zõêÌ›7èèhBCC™|ˆ““®®®šã–llløå—_2uŒ³ïâëëËÀ¹yófª{¹⃓h!ò µZÍ;w°¶¶~g»2eʤY)£OŸ>šäÐl`\»ví{Ç·yófž={ÆØ±c5É3ÀçŸNÆ Ù±c‡æš‡‡QQQZ×Ôj5›7oæ‹/¾Ð,=ñññáúõë åÇ$::š.]ºðüùóì¾ BϽxñ‚o¾ù†fÍš¥™<çÏŸŸ¦M›ê 2!ć&3ÐBäA …‚¢E‹ñÎvÏž= X±b©î5oÞ\ësJ”(‘j¶:;®]»À±cÇðóóC­V£R©P«ÕDDDðüùsž?Ž¥¥%M›6¥L™2xyyi’øãÇs÷î]¦L™’jÌÀÀ@ˆZ­ÖŒ{ãÆ nܸ‘¥õ¤É3ÞjµOOOºtéÀˆ# cîܹlÙ²Ek ¶™±{÷n†JhhhºmZ´h!G] ñ‰’h!ò(;;;ž>}JtttºmBBB4mSR*•iÎ^—*UŠ—/_¾wlOŸ>ÅÈÈ•JE\\ñññ$&&¢R©prr¢_¿~¨T* iÃc¿~ý8yò¤f¦ÜËË 333ºwï®5&$mìK3!!•JEÅŠéׯfffYŠÓÒÒLMMiݺµÖ½:ÿ&îBdVbb"æææŒ7îkœ]]]s92!Dn‘h!ò¨úõëãç燗—ƒN³Mò:â/¾øBëzbb"§NÒš…~ýú5Ô­ûoé‘ìnn²µµ%>>žyóæejít¿~ýðôôdóæÍŒ7Ž;wÒ¹sg *¤5&$ͧŒñ}ÙÚÚL\\œ¦:@TT •8DÖ)•Jš4iB“&M3f  >œS§NiÚЮ];F)„ødZˆ¯^½zŽ>Oè'''ìííµ®Õ©S'Í¥UBˆOƒÌ@ç€/^pçÎjÔ¨!åŠDŽ)\¸0«W¯¦wïÞÔ®]›éÓ§S·n]¢¢¢8räóæÍÃÚÚšåË—§êkddÄæÍ›qtt¤eË–\½z•aÆ‘?~ÆŒ£i׬Y3”J%3gÎÄÝÝ œµ~ðoÙ²…ãǧzFÓ¦MñôôäÍ›7ôêÕ‹%JðàÁNŸ>MPPëׯ×jïááÁ€˜4i666©6WU«V#F°téR”J%ÄÆÆ†ÐÐP.\¸Àü¡U®/³¾þúk–,YÂĉ¹ÿ>-[¶ÄÛÛ›µk×booOß¾}³<¦)ÅÄİ}ûvš4i‚¯¯¯f‰âÓ$ ô{ðõõeöìÙøùùa``€……;vdÉ’%äË—O×á‰O@ÇŽ9þ<ýû÷gÔ¨QšuÅffftíÚ••+Wbii™ªŸ™™;vìÀÕÕU³ÑÐÚÚš#GŽh%Çæææ¬Y³†Ý»w3pà@"##Ù½{7;vÔ´Y´hQš±-_¾œúõë³`Á.\¨¹niiÉ!CRµïÞ½;#FŒàñãÇL™2%Í_6/^L… ˜:uªV^ @­õÒYaiiÉÙ³géÚµ+“'OfòäÉ@Ò¯Õ«WkU*";öïßÏ«W¯¨R¥ ›7oÆÖÖVÖ? ñ‰“:›¢££éر#ŽŽŽ\¾|™²e˲cÇúöíKÑ¢E™5k–®CQ ¹ô,uö»:::rîÜ9¢¢¢¸zõ*fffØÛÛ£T¾ûtÃÏ?ÿœ§OŸrõêUÔj5UªTÁÀ õª­þýûÓ¿͆@ccc é¿3f¤;¾‘‘J¥’©S§róæM?~LÉ’%©P¡††©¿µ,XˆˆT*•æo300`øðá :”Û·oóàÁŠ+F… ´]{{{¢££3½~¹lÙ²œ?ž§OŸŒVVV™ê+DF~ýõW ©tdéÒ¥™9s&ŽŽŽ:ŽJñ!IMwïÞåÕ«W ,Z´ˆ   G'2b 0€¿Iú“ bˆyï1ÌĮ̀]»v–ú(ŠLÿ 700Ð*¹ehh˜f"ü6CCC*W®LåÊ•3l›^âœV,¶¶¶š…oS(Ù*fee%‰³ÈQÏž=ãàÁƒ( ÍIŸ'NÔqTBˆMèl²³³ÃÚÚšóçÏãææ$ά9YMä]‹.fò¤É¹úL›\}žâÃÛ¶mñññÔ¯_Ÿ²eËê:!D.‘:› 404mÚ;;;>Œ‡‡_ýu®ÅñôéSM-à¬ÊŸ?¿æ(h}óvÝäOI:u2\Þ!„È[¶lÐÌ> !ôƒ$ÐïÁÄÄ®^½Jbb"Ož<¡P¡BéVâøæ›o´//¯÷Žá?þМî–UUªÔàÊ•€÷ŽAä-éÕŒBä¬Û·osúôiŒŒŒ²½ÉUˆ¼ eYÑÄÄDÝò‘:›^¼xA£F5jóæÍÃÀÀ€'OžàààÀëׯY±bEª>k×®ý Ǻš˜Ø{=‹½VðkŽÇ"„ú"yö¹M›6iVÃâc‘rB/&&F³1V¤ORɦýû÷Ï!C4• Š+F·nÝØ¶mjõ{”]È¢¤oÓ,þ‘ß„â}ìß¿€Þ½{ë8!Dn“:›’“æ7nh]¿qãJ¥RTBˆOXLL þþþÀ¿'s !ô‡LCfSóæÍ)Z´(&L`üøñ888°k×.|}}=z´®ÃBñ]¸pøøxÊ—/OñâÅuŽ"—IMÅ‹çðáÃŒ5Н¿þš˜˜¬¬¬˜8q"Ó§O×uxB!> 3gÎP¯^=G"„ÐI ßƒ³³3~~~¨ÕjâââäH`ñÁ={ö  ( ëPtêùóç¬Y³†Ö­[ãä䔥~ÆÆÆzÿú‰÷' ´úMÖ@ç…B!ɳø`ž}ʈ#4É3@¾|ùèÙ³'„‡‡g)Î"EŠàêêJHH³fÍâÙ³g<~ü˜I“&$•"³îß¿Odd$fff”+WN×á!tDf …ȃ ¥K—æñãÇïl÷èÑ#lllRõoÚ´©Öµ2eÊ`kkËÍ›7ß;¾äïiÓ¦1uêTÔj5*• µZMXX‘‘‘<~ü˜âÅ‹ãììLµjÕðòòbìØ±ìß¿ŸçÏŸãáá‘jÌŸ~ú‰õë×£V«5ã>þ€Š-š¥XW®\IXXÓ¦McΜ9¨T* 1bË–-Ó,c"3’gŸ+W®ŒB¡Ðq4B]‘Zˆ<ÊÎÎŽ?ÿü“gÏžQ¤H‘4ÛüïÿÓ´M)½Ú䦦¦DFF¾wlQQQÑ«W¯tÛäË—OóñW_}Ř1c¸xñ"µjÕÂËË‹¢E‹Ò¾}{­1ºtéBÁ‚Ó3­µÌ)]º4gΜáÈ‘#R´hQš7oÎÎ;4…ÈŒä5óUªTÑq$B]’Zˆ<ª]»vüùçŸüøãÌœ93Í6+W®D¡Pкuk­ë*• ___­eOž<áÊ•+Z3ÓÉ%¸T*U–b³³³#>>ž:d*íÓ§&L`Ó¦M”.]šC‡1dÈMåä1>ÿüsš5k–¥x2b``@Ë–-µ*ìÙ³sssœsôYâÓ–<- ´úMÖ@ ‘G 2„Ê•+óÃ?àëë›êþŠ+8vìnnni&Ôú<¹ªGÛ¶m5×>ûì3®]»–¥Ø\]]100`öìÙiÞûð•äµÑÞÞÞlܸ‘„„­å-Z´ þüÌ™3µZá˜ïc×®]œ8q‚1cÆP¨P¡W|ú$B€Ì@ ‘g±e˺té¢94¥nݺDEEqäÈ8@ýúõY¸paª¾¦¦¦øúú2vìXZ¶lÉÕ«W™6meÊ”aðàÁšv 6$_¾| >œ:`aaA—.]´ê"O›6 KKËTÏèÞ½;7näÁƒôêÕ‹%JðàÁNŸ>Íýû÷SPâááÁ¾}û˜9s&Õ«W§fÍšZ÷mllX°`Æ £N: 0BCC¹pá¾¾¾šä%+–/_ÎéÓ§©]»6J¥’óçÏóÛo¿Ñ´iSFåñ„~KþeSh!ô›$ÐBäaÎÎÎ2eÊŽ?ÎæÍ›122¢zõêÌ›7qãÆ¡T*Sõ311áèÑ£xxxðÓO?¡V«iÔ¨6lÀÌÌLÓÎÔÔ”cÇŽñÇpôaFcŠ IDATèQž?Ž‹‹ •*U¢X±bØÛÛ§*‘—ìûï¿§{÷îÌ™3‡aÆE¾|ùpqqaÀ€©Ú·oßžZµjɰaÃÒsÈ!T­Z•)S¦0nÜ8^¿~‰‰ ÕªUã›o¾ÑúûÙÛÛgªfu… Xµj¿ÿþ;8::j6?¦õÚ ‘ž‡òòåKLMM©P¡‚®ÃBè$ÐBo=Ù˜ÐÌ–èèÔK2«P¡BšƱ±±f*ñ³±±áرcš¥)KÃ¥”|€Ê¬Y³´®3†1cÆdøœN:¡R©xýúõ;kVqáÂ… Çkذ¡æÐ”—/_¦9fùòå5(3Ò®];ÚµkG\\ Z¿@‘Éï€ØÛÛË/_Bè9I …žRâë i,-þ@bsd”´*kd$½Ä9'¼3yήœÓØØXkÓ¢YÈò !„$ÐBOíØ±“ÄÄÄ\}fn$²BˆçòåË@ÒõBý& ´ÐKFFFŸlBÛ­[7œœœt†ŸI …É$âÓ®];]‡ Ä')9®ZµªŽ#BèšÔB!2ƳgÏÈ—/ŸTàBH-„Bd$((€Ê•+c` ?:…Ðwò]@!„È€¬B¤$ ´B‘I …)I-„Bd@6 !R’Z!„x•JÅ•+W™B$‘2vâ“pîÜ9]‡ ôPdd¤®C¹ €7oÞP²dIÊ—/¯ëp„y€$Ð⣧T*Yºt©®ÃzD©Tbh˜ôí3;Ç«‹‹ŸŸ 4Ðq$Bˆ¼BhñÑkÚ´©®CzD¡PЪU+† ¢ëPD.IN 6l¨ãH„y…¬%¥R©ë„žR«ÕºAä"µZÍÉ“'™BüKf ÅGiòäÉ$$$è: ¡§dÙ†þ¸víáááXXXP­Z5]‡#„È#$¥‚ ê:!„H^¾Q¿~} …Ž£B䲄C!„H‡¬B¤Eh!„"RC‘I …Bˆ4„„„ððáCòçÏO­ZµtŽ"‘Z!„HÃøüóÏ5u¿…$B!Ò´aÃz÷î­ãH„y$ÐB!Ä[.^¼È¥K—(P ÝºuÓu8BˆI …BˆÿwïÞ=Ž=ŠR©ÄÝÝ]×á!ò(I …Bˆÿçåå…J¥¢M›6”,YR×á!ò(I …B 66–µkײ|Cñn’@ !„ÀŠ+xðà¶¶¶¸ººê:!D& ´B½÷òåKæÎ ÀìÙ³åèn!Ä;I-„Bï-\¸gÏžQ«V-ºwï®ëp„yœ$ÐB!ôZhh(?üðóæÍC¡Pè8"!D^'ïQ !„ÐkC† !**ŠæÍ›Ó¼ys]‡£åÌ™3<~ü8Ëý®\¹‚¥¥e¶*‰T«V [[Û,÷BŸH-„Boy{{³gÏLLLX¶l™®ÃIeêô©øúú¢4Vf©Ÿ:.…B©iÖ~Ì¿y“ÀâÅK5jT¦û$ÄÄðøÒ%Þ<~ŒB©Ä¼\9¬à=fò#ÃÂHˆ‰ÁÔÜSsól#ć" ´B½ÆðáØ9s&öÙ³gÙz^ݺuiݺu–ú$ªQÕU¡j®ÊR?“E Ü{©éÝ;>Ký¾ý6_–Úÿ³v->Æ‘§u½x´ÿùgJ×­›¥ñÂÿ÷?~vr"16–FÓ§ÓxÆŒ,!ć& ´B½4xð`ž?Nݺu;vl¦úìÙ³‡ý¿î§œQ¹,=ëZÜ5ì«ÛsèС,õ»yó&”ÉR—\eR¸0_L˜€Mƒ*]š—wïòßÿrnùr¶vèÀЫW1³²Êôxj•нýûcV¤¯=ú€‘ ñ~$B¡wV®\Éž={055ÅËË ¥2óK$\]ø.ñ»,=¯§¢ /¹såJ–ú=Q'äéÚ±{wST-±ªR…ŠmÚÆåß~㮟]ºdz¼s?þÈ“  Ú­ZÅn9J]äa’@ !„Ð+GÕ¬ñ]¸p!ööö¹ò\Wµš¥ñY[RQÄÈ€ç¨?PDN‰š5¹üÛoåÏŸé>ÏoÞäèäÉ´X°€Â66i¶ Ü´‰“sçòù¸q8 ¹ÿæ [ÚµC­RÑÇÇãÞûï Ä»H;!„zãÆtëÖ„„ À°aÃtÒ'çÅ;œ_µŠ|––ThÖ,sÔjö}ý5%œœp2$ÝfÕÝÜ(hmÍÁáà K±ý¡C¹ëçGÃÉ“%y¹Bh!„záåË—tèЈˆ5jĪU«tÒ'ãÔ¼yx5nÌÒråø±|yJ:;ó­¿?FF™ê~Õ*œ=‹ëºu( ÒOMtñöƤP!¶wïNÜë×nÞLà¦MÔŸ8ÛV­rê¯$Ä;I-„â“C×®]¹~ý:¶¶¶ìܹ£L&w"cJ## MMQ'&ðòî]^Þ½›©¾/îÜáÈwßÑ`òd¬ªTɰ}%è¼e !!lëÚ•?† ¡Ì_ÐdÖ¬÷ú;‘’@ !>Zÿþ›ã3fpé—_RÝóß°ã3fæïŸ£Ï<5oÇgÌàõÇ9:®øp¢££quuåÈ‘#.\˜ýû÷S¤H]‡õI©7v,}bôýû ¾t …RÉÆ† yrùr†}÷ó …Ë–¥þw™ß˜Y¡ys>?žÿþCºþö†²­Käù×&„ž‰ åÑÅ‹<ºpÐþ!öÕ+ºïØYÑ¢éö{vý:û¿ý6Ãñ{íÛ‡I¡B9ï»<üûoN|ÿ=š7§º››Ö=ÿ ¸ú4…Ë”¡DÍš9öÌSóæûò%•Ú´¡`©RYêóâ 11( ë4sITT:tàØ±cXXXpøðáLÕ{ÙW¬Z5NžÌo;rå÷ß)VµjºmŸ\¾Ì­#G¨Ð¼9N˜ ¹žü êÍC‡ˆyñ‚j½{Sê³Ï4÷Õ*¡/I¹$"·H-„9÷ãJç„±ÄØØwö}ýš»'Ndø U« è“}pmçNN™"o7ç‚7oÞЮ];Nœ8A‘"EøóÏ?©™ƒ¿L‰ôÙÔ¯À“ Êö) ò/Îã  ý{wWSþÿüu»­ÒBÚhUd_JƒŠ˜l‰,Cöu cŒ}_F3ŒacŒ1ÌoY³Ž,#;Ù³&%*¡’ŠŠöûûƒî·”tÛN^ÏÇ£G·s>ŸsÞ™¡WŸû9Ÿüxο#±wï"1<¦mÛæ ÐþK—âÑÉ“hÿÝw¸ºv-öxx`ìÕ«PÖPl#¢âb€&ªB²22‰5­¬`lg5mmÜøûo…®¡¬¡!GŽ|ô¼šŽNIË,Ý׬Aê«W¨Õ Ð¥ЫW/œ?úúú8yò$š5k&tY¢#ËÎ.ð¡¿;ÞÞæMó¹u ™©©¨Óº5@¿qc̈ŽÎ×?ÂÏ^:ÀaÆŒ|;Fúû㌧'õ뇎‹¡Ž½=¼ÝÝqdâD¸ÿóO)}gD…c€&ªBšÛ1c ®« àÝ"E´’T ‹Ê ºÒUšÓ6¨r¹{÷.z÷î°°0âÔ©Shܸ±Ðe‰Òßöö0ut„±­-´Œñ6>aÇãö¶mÐ61Ág,¸»$„…aáû‡ õ6>û ‚Ž©)z¾ÿ·«~Ïžh3u*.­Z…º..h2hP‰¿/¢Oa€&ªB4 ËýžÉÑÑxø~ûb«.] U»v¾6Ù¸ãí Yv6 ›5ƒ±­m‰ïûÐ×É110stDÍzõò7Éê단K—ðúÉÛÙÁÌÉ F-ZàŽ{xzõ*tLMaù‰ulŸ]½Šˆsç}ëjXZÂÒÅæíÛçi“‹Ð#Gä«Dߺ…›^^òóR42$ßµ?FĹsˆ¹so^¼€~ãÆ0iÝæÎÎÖˆè[·PÓÊ fíÚ!9:‘çÏãÉÅ‹Hމëï¿:Ï] vïÞÑ£G#%%7†¬­­….K´ š6ÅÍM›peÍù1‰’{x ãâÅ mã]>#G"9:£ÎŸ—@§eËéïCãÆ¡¶½=jò¿9•1h"*Sš¸³};< SŒôóË÷´ü©ùóqqÅ TÓ×Ç×¹6G(‰sK–àÉ… pÿçŸ|:=) ÿŠÊåÚ®«V!;3'fÎDý=> e2Ž|û-®®]›ç¸ß¢Ep˜1W¬{‚£FÉ¿9t!‡É¿V­^=_€¾òÇ81s&2SSóݻɠApûóÏ<‚öíÃù%KÐlèP@"ÁöîÝ‘ž”$?ßÁÓS´:++ óæÍÃòåËýúõæM›Pk–©Þ^^pÿûo$†‡ãM\4ôô cjúѹÈCBŠt]sggxÊòïÀ8(×ßÙܤªªøêÊ•¢NTB ÐD¤¬ôt;/‚‚ ¬®ŽZ660lÞ͇‡²ºz¾ö%%ôݶ ë[´À“‹qzþ|túùgùù‡¾¾¸øË/€D‚¾[·8B]Úv÷뇰ãÇ¡ª¥…Ï/†™“Ò’’ðàÀ›6 FÍ›òǦMCÌíÛpöô„©ƒàÞ®]ܸùV]º nçÎýF0`ÿ~\X¾QhÔ¿?š,¿Ö‡¿P›6 —V­‚²º:gφUçΨ¦¯èÀ@œýþ{ÜõöFJl,†Ÿ}aÇCIE_^¸ƒ\9Ytè33›6í“×y~ã¾ D­\K¢Yué‚ô”ÜÛµ W®”hš5Ñ woÜÞ¶ PËÆ z÷.ðº1·oãòï¿CªªŠÑþþ0¶³“Ÿ3lÖ 6½zam£Fx|ê‚}| ¼NìÝ»h:dúlÞ ‰T °üüó"üéT>6lÀŒ3˜˜ˆš5kbÇŽèÊÝ舨Œq#"*²:Ÿ}†Î+V`ðáÃséúø Ý¼yPÑÔDâãÇØÕ·/bs-E•›å矣ý‚€LŸ#ð*"ÿ‚7/^¼ÛElÑ¢rù.ÿþ; ÉÀyÂsû Š4 Þêë¯ó„ç¶_~ xñ‰å»>æÌÂ…ee¡ÕøñyÂsu]]8Íž ¸¹iSת©¡óÏ?Ë󅇇£sçÎ3f áââ‚ëׯ3<Q¹à4IíV­0æòå|Çmzõ‚ݸqøÛÞ)±±ð5 c¯]+ðÎ "âÜ9„Ÿ=‹¿lmñ6>zz庋ØË¼-.ˆTU;âÎöí…^'gÚÆ‡t--IÏž!+=RUU…ê{~ã€ÂGŒMÚ´Äx^¿aC…7y©,²³³ñÇ`Þ¼yHIIŽŽV¬X¯¾úJèÒˆ¨ ©:##***B—A$Z:ffè²r%ö†çׯ#9:ÕŒòµ“H¥øbÇüÙ¤ ÞÆÇ úlÞ m“ò)T&CÂãÇíB¦®¹ù'/¥cjZàñœeÙÙÈ|ûV¡ñæ ^GEöôïÿ¾d “åû A–••o¤9'Ä‹M@@¦OŸŽ€€@=°~ýzÔé/ DTq‰2@‡……aÆ ¸uëîÞ½‹ÈÈH¡I“&hÒ¤ † »Þ%¢â³ìØQþ:æöí4$FD -×ÊHʺ4¹Üá³°û´1DqÚ(êm|¼¼>‹ R­Ú'ûȲ³óhUMÍR¯MHAAA˜7o8ÐÓÓÃêÕ«1¤€¥ÿˆˆÊƒ¨tPP~úé'ìÚµ 5‚ † SSSDGG#,, X»v-\\\0þ|8½ßn”ˆJF5×raoÞØ&51{DvFtÌÍñ*">#Fàë[·Êeõ ‰’t-,ð2$IOŸ~´]âûõšË›v:PÑÔDFJ gÏíƒEõäÉxzzbË–-ÈÊÊ‚ªª*Ƈï¾ûú¥¼¾0‘"D _¼xV­ZaðàÁ D“&M>Ú6** kÖ¬AÏž=qøða8::–c¥DâôìúuùkÃl™|`ôh¼Šˆ€~£Fø2 Û»wÇ“ ðï!~êT™Œê~¨f½zx‚ð³g ܼ$;3çΕɽ¥ï§’´¶3@"~Æxvíž\¸Pet||<~úé'¬]»©©©H$2d/^ K‘NO©èÒ^¿F£G¨ai 5STì;PÕÒ‚®…E)VKTöD³ ‡¶¶6"""ðÏ?ÿžÀÄÄ?ÿü3¢¢¢¸½+Q)ÈÎÌÄ©¹s¼Û8¥F!çÊ xÿ~(kh ß®]PÓÖF?oohÔ¬‰ð³gqîÇË¥Vûñã·¶nEüÇùÎßÜ´I¾c`iË™ëó `ArÖ‡¾¸re¡£äbôðáCLœ8fffX¹r%RSSáêêŠÀÀ@lÛ¶áY@‘çÏ㯖-vâ„üX„ŸþjÙÞ/eYq|úô"µM{õ ‰ááÈÎÌ,öýˆJ‹h´šššÂoéijjB÷ƒ¼ˆÄ,+- ágÏÊ?¢åçžÈ4û—­-ü—-CÜýûò)™©©ˆ<›Ú·ÇÓ÷»€u_»6ßüâèÀ@Ÿ1àºz5 Þÿ’«mjŠ^7x·ƒ_YüæVÏÍ fíÚ!+- Ú¶Å­Í›‘†è›7á÷Ã8üõ×г±)“{ç,›÷ôêUÜÞº‰ááH{õ i¯^ÉÛ´ž4 u>û i¯^a³‹‹|íÜâîßDZiÓÞm@#gΜA¯^½`ccƒ?þø)))pttÄÙ³gqäÈ4/ÂÆ6Tþ”54 cn^nsî/ÿþ;V[ZâUdd¹Ü¨0¢™Âñ)×®]ý{÷жm[Ô¯__èrˆ‘òâ6çzØ/·œU€w;ã}—‘‘çüËœš;W>Ò¬¦£ƒ´×¯å½))+ÃqÖ,4ê×/O¿ô¤$ì0Yiih2p l?Xn̦W/´ž4 —ÿûÆø[· ¡§Wâïµ0÷ïÇîþý~æ |FŽÌsÎÙÓÊjj85oTµ´Jõ¾M Â…ŸÆ‹  ì>\~\µzuÌ}ÿ`¥D*E¿;±øpDúûcK§NPÓÑAMkkd¾}‹W‘‘HON8UâÝöÒÓÓáííß~û 7ßoß®¬¬ L:Ÿ}ö™ÀÒ§ÔíÔ SÂÃ….ƒH¢ Ðþù'~ýõW¨ªªbôèј6mpùýúµ***غu+ P*÷{ùò%~úé'\¾|¯_¿F£F°páB4jÔ¨T®OTš”ÕÔ`îìüÉv­Éì²d Ÿ9ƒ§W® %6i¯^AY]5¬¬PÛÎíæÏ‡^¿œÞz¿=w-ôøë¯ï×yÅ $GG#9&7ÂaæÌ"}?ÚuêÀÜÙ†ŒP·l %eeT76ÎwNCOÃŽǽݻué^?yc;;˜·ksgg7 œŠbæä„ôään‹-UU•ÿøç¨¤¢‚±×®áÚ_!æÖ-$=ŽÌÔÔ|«mèZZbÔ¹s¸þ÷߸·{7^áùõë()¡š¾>L`Ó«}ñEž~5,-aîì\à/Epp0¶mÛ† 6 :: ««‹±cÇâÛo¿…éG–¤Š'éÙ3Dœ;3'§|KQ>¹págÏÌÛ·‡Y»v;v ©u;uÊ1™ OŸFÄùóPVSƒu·n0jÙ2ÏõbÞoÒrø04 FÍ›çùÿ=%&NžDÌíÛÐ44„E‡0¶µÍs«ÄÇuù2꺸 ;+ OÂó7Ðdà@Ô¶·/?ªD £££1a´lÙ7†§§'?~ŒÔÔTìÚµ Õ«WÇòåËñ矖J€~øð!:wî 45kÖÄÍ›7ÎMR5}}Œ|ÿCMQ­'OFëÉ“¼[:-íÕ+¨ëê~r:ûo¾ý7ßÚFªªŠ~»v)\Sƒ>}РOŸϹ®YSh_%ee4ç8·œ" ZOyðáÃ…^W£fÍBÿŒ•54ÐfÊ”B¯H`7v,ìÆŽðn$_YC£Ð gZ~ù%Z¾ß ±"yþü9¼½½±}ûvÜÈ5ÿ»^½z˜®®®P*‡•W¨ü„Ÿ9ƒóK– ±‡úlÝ ©ª*ÞÄÅa»«+bïÞEíV­òõ 9|6½zaöË—PÕÒBĹsØ;p NÌš…ƪ©Á}ÃèZXàÌÂ…sù2jÔ­›çÑøwèPÔïѽ6n„†ž^GEÁoÑ"\þýw˜99¡Q®ijptêTôX·͆…D*ýøê8DÕ¿\~~~h×®üëœ×íÛ·—³³³ƒŠŠ ®^½Z¢{=xð>>>øâ‹/ž‰*™+kÖ`ßàÁýï?$„…áíË—xzå NΙƒ}ïG¤ÛL™"Úí°ËJrr2|||àááCCCŒ5 §Þ?Ù©S'xyy!&&»ví‚››óüú+TªUƒÛºuò]8«Õª…ë×C–•U`5mmôÞ´IþK¢yûö¨ïæ†äèèWÊ)È©ù󡦭¾Û¶ÉŸ¡Ð61A×÷õ\Ê5£éàÁh>b„|4[Y]]áï—ª.Q@§¤¤À(×îg9#ÌjjjòcJJJPQQAzzz‰îu÷î]€……ºté‚›7oÂÌÌ “'Oư÷o9QÅ”™–†»ÞÞ¸ëíïœDI m¦LóÂ…TVùãÈ‘#8räΟ?ŸçßV[[[ :„qsÑI|bï݃Q‹ШY3Ïqc;»|Çrµh‘ï3''Üøç¼zòúEXnöùõë¨Q·."ýýÿ·í}v6d2ô5‹{÷òõ)p.6Q‰*@@BB‚ƒƒO߯¡šgºFÎ[‰%ñøñcÀ‚ àááI“&aÛ¶m>|8²²²0òƒ'ûàÇ„r®ù‹ßÿ}‰ë "ÅÙ ½úõéï×QQx/ÐÑòóÏ9²oß¾ÅéÓ§áëë‹#GŽÈÿ-‰D[[[ôèу Bƒ ¬”„üü9Œ>²ì`΃R/`9Ùœ@ýÁj@Éxó)±±xóò%v~䙈œv¹ØÕâ/ur¹óH&×Ù.Ñè-[¶`Ë–-yŽ•ÅrH9£Ú]»vÅŸþ èÑ£‚ƒƒ±téÒô°aÃòŒ†‘0Ô´µaãîww¡K©ðRSSqåÊøûûãüùó8{ö,RsÍÕÑÑA—.]àêê WW×<ïRÕ£mb‚ä÷««|(9:ºØ;F¥Z5¨jiÁ¼]; þï¿¢wüÄÐUIîÌ’––†%K–WL%!ª=cÆ 8°Hmm?XÖFQµk×ð.4çæêêŠåË—###*ï·íÍannuα"¢ ,...\€¿¿?üýýqãÆ|SÞš6mŠîÝ»£{÷îpppÈóÎUmM›â¡¯/Rbb ih(?þäâE¤&&–èÚ9s”so<”ðiSD]¾Œô¤¤J÷ÀmE`‘k+õT>LY$¢úW¯U«VhUÀ¾e¡E‹H$ ËsüÑ£GÐÒÒÊž‰ˆ*¢‡æ Ì9Sàr³°°€££#Ú·oîÝ»Ãäƒ5‰r8Μ‰àýûá3j¾Ø¾ê5j !, ‡ÆŽ•?TX\ºïC^Øñã0jÑ"ϲ³§'¶u튣G£×ÆyBtJL bîÜáœg*U¢ ÐåÉÊÊ ;w†··7F…:uêàþýû8zô(ºwï.tyDDy¤§§#((7oÞ”ܺu ‰Œ J¥R4oÞŽŽŽprr‚££#êp5*"Ç¿„ IDAT“¶mÑeåJœ˜5 ËkÕ‚¦¡!’Ÿ?Gç+pkóf¨hhûÚõÜÜ ka“sæàЍV«ÚN »qã`Õ¥ \–.ÅYOO¬®[µ[µ‚Ц&^ED úÖ-4èÕ‹šJ•¨ô¢E‹púôé"µ]¾|y‰çFÿßÿýzôèKKK4kÖ ×¯_GóæÍñ믿–èºDD%[·nå Ë÷ïßGFdU¯^mÛ¶…££#ѦM.ÍIùèÕ¯gOOèçÚ$LÏÆæÝ±v¾l;m껹!ÒßÀ»eéjÖ«¿~€±]ž¶íæÎE +«|÷ÓoØΞžÐ³±‘S©V ßÜ»‡ÐÿþCbx8ÒSRòÔã4gôê…»»váEP2ß¾…±­->›8õsM·4jÙΞžÐ57/Ù Ui¢ ÐAAA¸páœ?ù 4¦X˜››ãÊ•+¸xñ"‚ƒƒñã?¢S§NœHDe.++ ‘‘‘ Ehh(>|ˆÐÐPܹs‘‘‘ö166FóæÍó|ØØØ@ú~\¢©Y¯:|°r”^ýúùŽÉÏÙØä ¿!‡!=9ùÝÔ‹\œæÎ-°­† ¼¶Jµjù6D)J¿ÜŒZ´ÈW‘¢D•ô:wî \ºt ýúõèQ£Ð¾}{HÊðI[ ¸¸¸ÀÅÅ¥ÌîADUSvv¶<$çäœÇt={4lØ0_XÖ××/ç¢ãÓ§£ÎgŸÁ°Y3Ȳ³qîNÍŸMCCØ}õ•Ðå• Qè/¿ü#GŽÄ‰'°iÓ&tíÚ&&&9r$†333¡K$"’KHH@TTž>}šïsxx8 ÝôI[[ÖÖÖ¨W¯žüs‹-ШQ#>ÈL‚IŒˆ@ÀªU€L&?fÖ®Üÿþ*ššVFTzD wÀtëÖ ÝºuCBBvìØ///xzzÂÅÅ[¶lá:¥DTfd2’’’˜˜ˆèèèÃqÎë·oß~òzÚÚÚyrîÏÙ˜‚HH{÷âíË—xõä ²33¡W¿>Ô´µ….‹¨T‰.@çV£F L˜0Íš5Ü9spâÄ DGG3@ÑGedd 11¯^½Bbbbž×~.èØëׯ‹¼Û©ŠŠ j×®:uê N:011‘¿633C½zõ8í‚*•œ©E666ÐÐÓ+÷ûËd2\¼x-Z´€&G»© ‰6@GDD`Ë–-ؼy3¢££ñÅ_àÇDól1JD✜\ä°[й¢Œ в²2ttt```'ç~]§N”é3De-55~~~8xð þûï?4mÚ{÷D555XYYÁÂÂýû÷G÷îÝÑðƒ•BˆJJT:%%ûö탗—üüüàèèˆyóæ¡ÿþÐâÎDTdff"##ééé }.j›¬¬¬O~dff©]qÛªOiÐÔÔ„ŽŽtuu‹ü9÷kŽ|‘˜=zô¾¾¾Ø³g ¢¢‚””tìØûöíƒj 7L)©V­ZáÈ‘#h×®®^½ŠùóçCKK ={öDïÞ½áââ¿£Tb¢ Ð_}õöìÙWWWìÞ½Vï×–üp·@àÝF( ÕT¼~ýÑÑш‰‰AtttžÜÇbcc \ç·ª‘J¥% ¿:::\Š’(—Ü£Ì>>>xùò%¤R)Þ¼yàÝß¹:àèÑ£‚‡ç¶¶¶8wî:t耔”ÄÅÅaóæÍØ»w/RSSѲeKxxxptšŠMT?%²³³‘™™‰C‡áСC…¶õõõE·nÝÊ©2¢ü¢¢¢õÉp¬è”‰D¨¨¨@UUªªªò×%ù¬¢¢©TZ¤eeå"·-í>êêêeô_Œ¨ê ömÛàíí°°0(++#===ßü~˜™™aÈ!رc‡@Õ~Ü´iÓðóÏ?ËkOJJ\¹r˜;w.ÔÕÕakk‹iÓ¦áóÏ?¸bª,D .\ˆ¯¿þºHm›5kVÆÕ½“{ å[·nÉ?âãã‹Ô_*•ÂÀÀFFF…~BSS“£§DT,YY@BB&–,Y‚©S§B"‘@ö~)ºÌÌÌûdddàÁƒøª®ïœóŽ]FFüüüàççeeåïRLUƒ¨~Ò6jÔrmëITÞâââä[(ç|.pjEµjÕ`nnþÉ`\«V-()) ðÝQU"•5j(£OŸ/Q«V-lÚ´ ÷ï߇T*EVV–#¡¤¤‰D©Tо}û¢oß¾022Bûöí¨–*Ñh™LV¬§Ù‹Ûª6™L†<£Ê7oÞijgÏ lojjšoW8kkkc"ªpŒŒŒ0eÊ̘1ÉÉÉ8}ú4|||pøða$%%A"‘ȧ–eee!,, cÆŒÁäÉ“®<¯¤¤$´k×éééòð,‘HP½zu¼}û7Fÿþýáææ†æÍ›Ë³ÀÍ›7…,›* Ñ蘘¸¹¹aêÔ©0`@¡»peggã¿ÿþÃêÕ«ñÝwßÁÙÙ¹+¥Ê*55§NÂpøða<þ<_5554nÜ8OPnÖ¬jÖ¬)@ÅDD%S½zu¸»»ÃÝÝpÿþ}øúúb÷îݸqãÔÕÕ‘œœ,Ÿò1iÒ$+~'99íÛ·Ç;w ¢¢ ¨©©ÁÕÕ}úôAçΡ««+t™T‰‰&@`„ øþûï1kÖ,ôïß666°¶¶†©©)ž={†„„„àСCHJJÂôéÓ9׉ õâÅ >|ÄñãÇåO€¾¾>lmmó„eÎA&"Ñjذ!6lˆiÓ¦!%%E>:}èÐ!L™2ÊÊÊøæ›o­1)) ݺuÃíÛ·Ñ´iSùj-Z´´.Ñü¤WRRÂèÑ£1bÄìØ±ëÖ­ÃÆ‘œœ,o£¦¦†† b„ 7nªU«&`ÅTQãàÁƒ8xð òEŸ>}ЧOÜ»w†††øé§Ÿ‰eË–1<Q©m€þ÷ßѪU+\½ziiiˆ‹‹˜ššbåÊ•¸qã†ÀRiÉÎÎÆÚµkѨQ#øøø@SS¿üò ÂÃÃ1wî\.¤ODDD¥J´zÚ´i˜:u*îÝ»‡Ï?ÿ\~¼zõêhÞ¼9®_¿.`uTZBCCáää„o¿ý¯_¿F×®]q÷î]LŸ>êêêB—GDDD"$Ê€ˆˆŒ7À»m¾sÓÔÔD||¼¥Q)Êy—! µjÕÂÖ­[qôèQîHDDDeJ”jkkC]]!!!¨_¿>$‰üÜ«W¯pîÜ9Œ3FÀ ©$²²²0wî\¬X±ЩS'ìØ±úúúWF$n}¿è‹cÇ)Ü/#- **R(++ú#G‚eËV`üøñ ß“ˆ¨,‰2@K¥R¸¸¸à×_E³fÍä#ÐqqqX¼x1TUUѾ}{«¤âˆ…‡‡üüü ‘H0oÞ<,Z´(ß» DTú^%àé@ÁåÓÕJàä”…víÒê·n*ÒÒÒ»Q9e€€õë×£]»v¨[·.  ªªŠ¥K—B&“áÿþïÿ¸"C%ôâÅ tìØAAAÐÕÕÅ–-[гgO¡Ë"ªZô4P°ÏaÀÜprR¬ÛÖ­RoDDT>D MLLpÿþ}üý÷߸víâããѯ_?Œ9Mš4ºN:…úõë ]‘¸´¥¥%.^¼ˆÚµk£M›6º$*‚ÌÌL 0¾¾¾¨Y³&Nž<‰F ]‘hÐÕÕÅÑ£GѳgO|þùçØµk—Ð%Ñ',Z´>>>ÐÕÕÅñãÇѬY3¡K""""’õ*9TTT°aÃX[[cðàÁPWWº$úˆ»wïbÙ²eH$Ø»w/ììì„.‰Dæ·ß~Cxxx±úöéÓÎÎÎ¥[U:¢ Ð €ŽŽNžcsçÎ…••–.] mmm*£ÉÎÎÆ˜1c‘‘1cÆÀÅÅEè’H„vìð‹w`jªØ&7ndšˆˆÄ W¯^]àqxxx”s5TkÖ¬ÁåË—add„+V]‰”L&CçÎÙ<8[¡~ãÆi”QEDDTÙˆ&@gff"** ZZZÐÓÓÓ'O••õÑö†††ÐÐàÄŠ""" ,ð.Hëêê \QÁD £¢¢`ii‰qãÆaýúõ°··GLLÌGÛûúú¢[·nåX!æë¯¿Frr2ÜÝÝѯ_?¡Ë!"""ú(ÑhcccÀÐÐpäȤ§§´}Æ Ë«4ú„íÛ·ãèÑ£ÐÖÖÆÚµk….‡ˆˆˆ¨P¢ ÐjjjhÓ¦ük[[[«¡¢Š‹‹Ã”)SK—.…‰‰‰ÀN4º02™ çÎC@@lmmÑ¥K¡K¢÷V­Z…¸¸8888`üøñB—CDDDôI¢ÚHÅÛÛ5BjjªüXrr2¬­­Ñ¡CÌ;]»vÅ´iÓ¬’r¤§§ãŸþðnó‰D"pEDDDDŸ&ª}øða4oÞ<ÏF)6l@xx8¶lÙ‚ððp|ûí·X½z5=z$`¥{öìAll,lll¸æ3U¢ Ð=ÂgŸ}–çØêիѽ{w 6 æææX¹r%TUUqñâEª¤9 ~óÍ7WBDDDTt¢š––%¥ÿýN‚ÇcÑ¢EòcªªªhÑ¢ž>}*D‰ô^``  ©©‰#F]Ž`’““‘™™Y¬¾PSS+劈ˆˆèSD5mii‰³gÏʿ޳gÔÕÕó­÷œ˜˜È:–3ú}ú`Ö¬YX¹r%öïß=z`Þ¼yyÚlܸ***hÛ¶­@UÒ¦M›ðöí[899¡Y³fB—#8==ÀÚZ±>êê¢z󈈈¨RÕOa‰D‚¥K—")) ±±±Ø¿?êÖ­›§ÍðáÃʹ£Ú°a`„ WBDDD¤8Q@çPSSûh@600(çj(·çÏŸ#((jjjèÕ«—Ðå)LT#ÐTñå<äÙºukhhh[ Q10@S¹:sæ  cÇŽWBDDDT< ÐT®rF  ‰ˆˆ¨²b€¦róôéS„††B]]mÚ´º"""¢bm€>pàÀGÏ:u ~~~åX ÿ›¾Ñ¶m[®‚BDDD•–hôĉáëë›ïøùóçáîî‰D"@UU§o‘ˆ6@/]º À7äÇ._¾ 777Lš4 íÛ·°ºª‰‘ˆrhàݶÞOž<››.]º„øøxtëÖ £GÆÒ¥K….¯Êyúô)=z |öÙgB—CDDDTl¢ Ð0gÎDFF¢K—.ˆ‡‡‡~ûí7¡Ëª’}:aff†Ù³g#<<`hhÈ<ÊQhh( ^½zWBDDDT2¢ ÐQQQ°´´,´•••üµ¯¯/ºuëVÖeÑ{>X[[ \ QɈ&@ÉR+ŠfÍš•a5ô!h""" ÑhuuutèÐAè2è#r4§pQe'Úe삃ƒ)ÿ:66¿üò ¦OŸ.ŸMåC&“!,, G ‰ˆˆ¨òÍô‡\]]±bÅ ˜™™z÷î{÷î¡zõêØ·oCt9zöìÞ¾} MMM ]Q‰ˆr:%%áááh×®àÚµkÀÕ«WñðáC¤¥¥áÎ;WYupþ3‰‰(tZZ@MM °{÷nØÙÙ¡~ýúÐÐÐ@›6mäÛJSÙc€&"""1e€®Y³&7nŒ³gÏ"##;wîÄÀåç“““¬®ê‰‰‰Ô©SGàJˆˆˆˆJN”ˆþýûÃÒÒ/_¾Ä—_~ HMMÅõë×Ñ A+¬:¼ûņˆˆˆ¨²m€^°`nÞ¼‰9sæ 885jÔ„……ÁÃÃWXuÄÇÇ€ü¿Qe&ÚU8 qãÆhܸq¾cëׯ¨¢ª‰#ÐDDD$&¢ Ðééé B­Zµ`bb‚{÷î!##ã£í­¬¬ ¥¥UŽV] ÐDDD$&¢ ÐÏž=CË–-1nÜ8¬_¿...ò‡× âëë‹nݺ•Úý/]º„ÄÄD8::2˜€S8ˆˆˆHLD ëÔ©ƒû÷ïËCš¿¿?233?ÚÞÔÔ´Ôî}åÊ899!++ hÑ¢E©][ 8MDDDb"š­¢¢’geòZs8==£F‚««+>\.÷¬l8MDDDb"šý¡ÐÐPܺu !!!ÐÖÖ† ìíí¡««[ª÷Y´h 0zôhèddd %%G ‰ˆˆHD “““1{öl¬[·2™,Ï9}}}üñÇððð(•{bõêÕ¸~ý:‚‚‚Jåšb“˜˜¨^½:”•E÷¿UA¢K4“'OÆÖ­[1kÖ,ôë×–––HNNFHH/^ŒÀÐÐÎÎÎ%ºOFFF¹sç¢~ýúE ÐòíÅÀ¢D5T9Ûª««« \ $<<\þ:çç6NTúÑ£Gزe 6mÚ„¡C‡ÊëééÁÜÜ:uB—.]0þ|øûû—è^Ë–-CVVfΜYä>[·nÍ3 ûý÷ß—¨†Ê ==À»9êDDDTñxyyÉ_¶ý¨ôéÓ§aee•'<ç&‘H°hÑ"888àÍ›7¨V­Z±îóèÑ#üøãزe ž>} ˆðn9=cccæë·`Á‚*7›³74QÅ”{@/55K–,®˜JBT:<<<ß΃jÒ¤ àÉ“'°±±)Ö}"##‘žžŽæ;çææ†®]»âèѣź¶ØähUUU+!"""*¢ ÐOžDHDDDbÃMeŠ#ÐDDD$6 ÐT¦ ‰ˆˆHl ©T…‡‡cÓ¦Mò¯ ‰ˆˆHl ©T™››cÆŒpssÃÑ£G‘””ÐÐм|ùkÖ¬Á•+W„,“ˆˆˆ¨Ø”….€ÄE"‘ÀÉÉ Ä‘#GäÇGŒÌÌLDFFÂÔÔÁÁÁVIDDDT|¦RçììœïXpp0>|ˆôôt,X°ÊÊü݈ˆˆ*'¦*uèVVV:th9VCDZ¿~=nÞ¼©p¿À¼ ""ªd ©ÔµhÑÚÚÚxýúu¾s}&ÞÑC‡pß×-%…úÅ)É ‰ˆÀMe@*•ÂÑѾ¾¾yŽsô™¨âp•Éð›L¦P=©â¡X""1b€¦2áììœ/@sô™¨t#::Zá~/_¾„Eé—CDTe0ÍP™øp4GŸ‰Jß²e˰yóf…û©°+ýrˆˆª h*vvvPUUEzz:Ž>••®èŠ9˜£PŸ’/Y|UDD$~\ÆŽÊ„ŠŠ Œ5jÔàè3‰4•™¾}ûf̘ÁÑg""" h*3½{÷†••fÍš%t)DDDD¥†šÊLëÖ­±dÉŽ>‘¨0@S™QSSÀ„.ƒˆˆˆ¨T1@)€šˆˆˆˆH ÐDDDDD `€&""""R4‘ ‰ˆˆˆˆÀMDDDD¤îpAE’ššŠ›7o"""2™ }ûö…ªªj‰¯ˆúõë÷ÑMWüýýqõêUDGGÃÖÖíÛ·‡±±±B÷ºpáž}3fÌÀ;w™™)?==½]ûÅ‹èÒ¥ ââânnnÐÒÒÊÓ&11cÆŒÁ¾}û‰2™ ðööF¯^½Š|¿U«Vɯ3aÂüñǶ[ºt)Ö­[à]¨Ï Oœ8ñãÇØO"‘ wïÞX³f êÔ©S予ˆˆ¨rá*Ô£Gpÿþ}ØÙÙaüøñÐÖÖ.µkOœ8 ©©ùÑ6S§Nž}ûУG„††"-- .\€©©)¾øâ œ>}ZáûêèèÀÛÛéééùÎ¥¥¥açÎÐÑÑ)ô½{÷†¯¯/|}}ñï¿ÿbÚ´i¨_¿>öïßÞ½{#++KẈˆˆ¨r`€¦B 4III¸téþüóÏ|#ÄÅuàÀìÚµ ¿þú+ªU«V`›ÀËË ÆÆÆØ½{7¬­­¡¢¢ìܹÙÙÙX¹r¥Â÷öðð@||<:”ïœàááQè5,,,Э[7tëÖ }úôÁÊ•+qòäI(++ãÚµkòi)DDD$> ÐT(MMÍÎK.®ÄÄDŒ?]»vŰaÃ>Ú.00еkWhhhä9ײeKÔ­[¾¾¾WèþýúõCõêÕáåå•—jÔ¨¡ÐÔ&&&hݺ5€wß#‰4•»©S§âõë×X¿~}¡íž>} Ð××/ð¼¾¾>d2nß¾­Ðý555Ñ¿=z111yîwüøq 4jjj ]¢¢¢pùòeèêêâ³Ï>S¸?U ÐT®Ž;///,^¼…¶µ´´€Ž0GDDø_ÐVÄÈ‘#‘™™‰mÛ¶ÉmÙ²ÙÙÙ9rä'û‡……ÁÇÇ>>>عs'&Ož ¨««cÑ¢E¥>jODDDÊS¹IJJÂØ±caooI“&}²½½½=TUUqèÐ!DFFÂÌÌL~n×®]xþü9€âèvíÚ¡nݺؼy3¦OŸàÝôFÁÞÞ'Ož,´ÿ¡C‡òÍ¡–H$Xºt)¾úê+…ë!""¢ÊƒšÊÍìÙ³ñìÙ3:tR©ô“íMMM1sæL,Y²mÛ¶Åܹsaaa+W®à矆ß<±† IDAT¡¡!bbb ¢¢¢p-‰#FŒ€§§'nܸÔÔT„„„`ùòåEêß»woŒ7 “ÉðâÅ =zsçÎÅÞ½{qìØ1Ô¬YSẈˆˆ¨âc€¦ráçç‡õë×cîܹhÖ¬Y‘ûýðÃÐÑѧ§§|Ù;eeeLœ8R©¿üò êÖ­[¬š†Žï¿ÿ^^^HMM…T*ÅСC‹Ô7gޝgdd„U«VaéÒ¥X±bE±ê"""¢Šs ©\œ:u 2™ û÷ïG‹-ò|$$$Ñ¢E‹<öI¥RÌœ9‰‰‰¸uëüüü˜˜ˆ_ý>ð¿¹Òв°°@‡°cÇìÚµ ]»vUxwÃõë×àëë[¢ëQÅÅh*FFFhÞ¼y±û«ªªæ¹Ž‹‹Ã‰'`dd;;»b_wäÈ‘1b„üuIeddx·õ9‰4•ºàà`$&&¢^½zòí¾¿ùæ|óÍ7¶700À‹/páÂ…"mÔ"“É0þ|¤¤¤à§Ÿ~Ê·F´"¾øâ ÄÇÇC"‘ÀÝݽØ×ɱeËïÖ©&"""qb€¦B¥¤¤`ëÖ­y¾Þ­X‘³w÷îÝó¬1eÊ;v ;wîÄ€Jtÿ?ÿü¯_¿†ƒƒôôôpïÞ=lذLJ½½=ÆŽ[¢ëkjjbÊ”) ÷ ’oÄ’û!Â3gÎ@SSžžž%ª‹ˆˆˆ*.h*TBBÆŸïøŒ3ä¯>œ'@—¦G¸]÷ðáÃñ×_A]]½Lîû)ÇÇñãÇóïÚµ+/^Œ&MšP•h*”¾¾>Μ9Sh›WÕX¾|9æÌ™ƒÆé>>>HOOGµjÕòûùçŸÑ·o_ܸqñññhРZµjU¬•7~øá|ûí·hÚ´é'ÛÚÙÙáÌ™3000ÈsÜÝÝ 4ÈsL"‘ÀÀÀ%šNBŠIHH@ZZšÂý233Ë ""ªJ ©PjjjèСƒB}Y¦>zN*•ÂÁÁ¡Ð6EUÔ@5jÔ(ðû®]»6j×®]âZèŽ;†Ç+ÜoõšÕ V¸Ÿª*`o¯p7"""9h"Ôº5kpîØ1(¸ýùÃÌ4À@[Åî'Ù$ S¬Q. ÐD$,™ Ã33ñ›‚S+ôT”¯!j)x?‰‚퉈ˆ>ÀTˆˆˆˆˆÀMDDDD¤h"""""0@)€šˆˆˆˆH ÐDDDDD à2vTæ.]º„7nÀÞÞöÜÁ‚ˆˆˆ*9Ž@S™Û¾};&L˜€Ã‡ ] Q‰1@S™ËÙúúùóçWBDDDTr ÐTæŒ0@‘80@S™Ë~öì™À••4•9Ž@‘˜0@S™ËŽEvv¶ÀÕ• t ü÷ß8p ¬­­¡§§{{{üõ×_ ‰ÐÓÓƒªª*²²²+t9DDDD%Â]?ýôÞ¼yƒ)S¦`ÕªU¨]»6¾þúküøãB—VáäLãà«ð}C†B³º&¬¬¬ê X*|;"""ª„ ©Â¸ÿ>öîÝ‹­[·"44jjjHKK+°mjj*Ѻukøûû£fÍšyÎ_¾|÷^DK´T¨†PILei°ò÷W¨_‚²„šˆˆ¨Š`€¦ CEE 4€»»;üüüpïÞ=T«V R©ÉÉÉùVàÈÌÌDpp0lll“<ç£1¦A±åï®àº ¿)Xû!‰ià !DDDU4UÖÖÖ°¶¶Fÿþý2™ ¡¡¡¸zõ*.^¼ˆsçÎáÁƒPQQAvv6RSS!“É{{{\¾|fffDDD$v ÐTaI$Ô¯_õë×Ç!CYYY ÂÕ«Wáïï___ÄÄÄ ::­[·Æ¹sçP¯^=+'"""1c€¦JE*•¢iÓ¦hÚ´)F HOOÇíÛ·qõêUlÞ¼3gθJ"""3hªôTUUѪU+´jÕJèRˆˆˆ¨ à:ÐDDDDD `€&""""R4‘ ‰ˆˆˆˆÀMDDDD¤h"""""0@)€šˆˆˆˆH ÐDDDDD `€&""""R4‘ ‰ˆˆˆˆÀMDDDD¤h"""""0@)€šˆˆˆˆH ÐDDDDD `€&""""R4‘ ‰ˆˆˆˆÀMDDDD¤h"""""0@)€šˆˆˆˆH ÐDDDDD `€&""""R4‘ ‰ˆˆˆˆÀMDDDD¤h"""""0@)€šˆˆˆˆH ÐDDDDD `€&""""R4ý{wVUµ>pü{dP BqÂÁÃP M@ME Å!,ÅáÖM‹´B½iƒæ€&™†Þ1ÍÌR2 qÉrÈqDLaÿþಯÇêQÄ÷ÓÃó¸×^{uVûœóîµ×Z[!„BèAh!„B!ô ´B!„zZ!„B=H-„B!„$€B!„B@ !„B¡  …B!„ЃÐB!„BèAh!„B!ô ´B!„zZ!„B=H-„B!„$€B!„B@ !„B¡  …B!„ЃÐB!„BèÁ¨¬+ð¬Û»w/+V¬àÒ¥K´nÝš`hhXÖÕB!„Oˆô@?†ØØXZ·nÍîÝ»166æ£>¢Gܹsç©Õ!;;›¼¼«OíõDñn(YÖµx¾H›?}ÒæOŸ´¹å“ô@?¢‚‚FŒA`` ?þø#!!!xzz²fÍzôèQÆ5B!„O‚ô@?¢øøxNŸ>M·nÝÔ4ªW¯NLLLÙUL!„B–––Œ‹G'ôcð÷÷'11‘–-[’——ÇÔ©SY¹r¥Öªú(((àØ±c¬\¹’„„½Oþ1cÆðÃ?èuÌ•+WèÛ·o‰Kï=ŽcÇŽÑ·oßRý¢»×”)Søå—_ŠÝÎæÍ›ÕmOOOÞÿ}uûòåË\ºt©TësøðaÂÃù|ùr±ûoݺ…F£)±Î%‰‰‰y¨¡@.\xfz&/ñÉGE£Ñ””¤W¹Ÿ~ú)ÎÎÎÌwæÌ²ôY¢ªœ:uêáááŒ3æËoÝ%66–ððpþóŸÿ”Êëùøøðÿ÷êvHH¾¾¾¥RöóhÙ²eDFFêüésÞõÕW´iÓFÝ^»v-¶¶¶<‰*?UYYY8::²téR¢££iÓ¦ sçÎ%  ÔÞßÉ“'™2eJ™Ð={ö$11‘¹sçrâÄ :Ä—_~©GDDàççG‹-ضmÉÉÉL˜0%K–вeK­x!77—ŒŒ d‘µ'LåÂîÝ»GGGPêÕ«§˜™™)FFFÊ„ ”üüü‡*ÃÍÍM5j”^¯{æÌP¥Ú÷µ}ûvPΟ?_êe©W¯žòÎ;ï»PƯn{xx(ï½÷žºÝ§O¥sçÎ¥ZŸU«V)€räÈ‘b÷çææ*;wÖ»½,X ˜˜˜<0Ÿ“““Ö{.Ïf̘¡JNNN±ûÏ;§tîܹĶ,Éøñã''§æ311Q,X WÙåÑ–-[@Ñh4ÊŠ+tö7jÔHÑh4Š··w©¼ž···ÖgnÚ´iÊØ±cK¥ìçQëÖ­ 套^ÒúûöÛoºŒÏ?ÿ\qppP·þùgPîܹó$ªüTyyy)]ºtÑJ1b„Î÷lrr²²lÙ2%66V¹téR±e}Z155U†®³ïðáÊ‘‘‘®¦}ûí·  dddÜ·\ñxda9pàÀ^yå¼¼¼ˆ§fÍšäåå1oÞ<Þ{ï=RSS™={öˉ׻÷»N:\»vªU«>jõ…Œùõ×_˺τڵkK[éÁÇLJ˜˜zôè¡¦íØ±ƒ³gÏjõN–¶ân5 ýx{{³fÍš²®Æ3£U«V\¼xgggÆŽËäÉ“©V­YYYMPPÙÙÙ´hÑ‚ÔÔTš7oNff&‡&99™S§N1uêT €F£ÁÉɉիW“““CPPëׯÇÁÁ””¾øâ FÍO?ýDïÞ½ÂÏÞ€øùçŸÉÎΦaÆ’˜˜H¿~ý8wî 6äøñãøùù±lÙ2*W®Œ……¦¦¦¼k…¢(Œ7Ng_ãÆéÓ§3gÎdâĉÈÀ‚§EZº ÃÖÖ–uëÖQ³fM 0Ð åßÿþ7sçÎåàÁƒ@áÓ###9qâÛ·og̘1Lš4 (¼UûçŸj•}ðàAÆŒÃûï¿ÏÚµkIMM%22R½u••ELL ÿüózÌÌ™39vìIIIŒ=š#F°uëV­r³²²X¾|9~ø!ƒ b„ ¤¤¤<±6*mãÆcãÆ$&&âêꊫ«+ , ÿþDDD0kÖ,\]]©\¹2[¶lÁ××[[[ªT©BË–-ùñÇõzÝÜÜ\\]]Ù²e‹švóæM† ‚ Œ7ŽèèhüüütŽ?qâ¾¾¾XZZÒ¤IÖ®]«î äôéÓÌ™3G}O‰‰‰Ò<åÂéÓ§quuUÏ}(rÄ‹/¾ˆ‹‹ 3gÎ$""‚þýûë¿{÷n|||°°°àå—_Öj rss7nœÚV§OŸ~oë‰ aÆ ZŸå àïïµµµNþ3gÎлwoj×®M5èÕ«gÏžÕÊsêÔ)ºté‚¥¥%îîî¬ZµJ§œqãÆ1lØ0u{âĉ 2D+Off¦Îùèêêʺuë9r$ööö8;;3wî\–,Y‚‡‡–––ôêÕ‹Œ ½WO®ÒÒÒèÞ½;'OžÔJŒŒdüøñeT«²•ŸŸÏ?þˆ7fÍš5|ùå—L˜0Ó§O“’’B·nÝ0`©©©@á¸[·n‘žžNBBàüùóÔ¨QƒvíÚÀ¾}û8zô¨º8ÀìÙ³‰‹‹cýúõ?~œ“'O²qãÆbëõÍ7ß0iÒ$RSSIJJ"++‹   <<|8üüóÏ@á8é°°0>þøc:uêÄñãÇÕ±OÓ§O×êÅØºu+žžžÄÄÄ››ËÀ %,,L½â½víaaaZÁCXXcÆŒÁ××—³gÏò믿Ҿ}{6mÚ¤æY·nŸ}öYYYT©R…ŋӬY3vîÜù$š©ÔÓ¢E œÕ1‡:t Óþõ×_3oÞ<†Ê’%KHMM¥k×®¬X±‚5kÖðꫯ̆ úu Ø¿¿V@ðÆo°jÕ*†ÎìÙ³IJJbìØ±9rDëØ;wîЫW/ZµjÅ¢E‹¨S§ƒ Rǹ=š5jàë뫾§‡ \^ݺu‹ýû÷sóæM ðâÑ××—?þøƒÏ>ûŒ/¾ø‚Å‹3cÆ 1üiii <???¢££166æÝwßU÷GDD`ddDpp°ÚV5jÔxªï¯´5oÞuÄÍ›7Y¶lÔÉ›ššŠ««+W®\aêÔ©Ìž=›ÔÔTÚ´i£Ž½ÌÊÊ¢uëÖ¤¤¤Å Aƒ5j”ÎüÙ³gINþ߃œÎ;§ðåçç³ÿ~õ ­û÷ïgèС\¼x‘Y³fáååÅСC3f _ý5Æ ãÓO?eóæÍêÅíóææÍ›¬^½Z§‡rÏž=$$$”Q­ž¾Âo¼AÍš5Y¿~=}ô¶¶¶ÌŸ?kkkÂÃÃ133£FŒ?žììl–.] Àõë×)((àܹsj™¶¶¶|JðâÅ‹iݺ5]ºt ^½z¼ýöÛÅæmݺ5ýúõ£R¥Jh4~úé'ÒÒÒøâ‹/ÔïWWWúôé£þžCap¿páB²²²6l...8::òÛo¿©yÎ;‡­­m‰õ¬]»6PxQ,žÂQÆŠ~øK t¬¬¬°³³Ó öìÙCJJÊ}?T|ð­ZµbãÆ˜˜˜0mÚ4Ú¶mûPõúóÏ?9{ö,(Š‚‹‹ ß|ó ;v {÷îôéÓGë///ÆŒ£Õ»Z^9;;S£F *Uª„ÎþË—/sôèQªÜõÜ~ýúiåiß¾={÷îeÞ¼yꬾ:Dll,QQQêĬŽ;bkk«3$??ŸáÇ3xð`\\\¨_¿>{÷îÅÍÍ wwwÌÍͱ··/ö==ë6lØÀîÝ»Y·nÚ;ß¡C¬­­©[·®VÞŒŒ ¾úê+õ¢ÈÖÖ///.^¼HÍš5ñòòÂÀÀggç ÕV!!!ÌŸ?Ÿ°°0–/_NÕªUñõõå»ï¾ÓÊ÷ÙgŸ¡ÑhˆÅÜÜ(üüÖªU‹Å‹óöÛo3þ|.]ºDBB 6 ϹW_}µÔê[·n]õÕÏÏåË—3eÊ.^¼¨ýõ›6mÒš\Ѭ[·Ng’ðµk×ʨ6妦¦T®\™áÇããã£þ–%''Ó¡C4š¿Q£FØÛÛ«r!!!¬^½^~ùe ÀÀ8tñäÉ“ôìÙS+­]»vÅæ}íµ×´¶>Œ‘‘AAA(ŠBAA\¿~+W®¨ù*UªÄ›o¾É›o¾É¥K—X²d 3fÌ [·nüý÷ß4hЀZµjÝ÷ïÅ‹xñÅïû~Dé’ºŒ}^xá…óXYYi}àºtérßàùÊ•+ìÝ»—iÓ¦©=ÛææætíÚ•½{÷>°^={öÄÂÂFC»ví´†q˜˜˜M||<çÎ#''kkk¶mÛöÀ²ŸmÛ¶Õ ž¡0€MJJâ÷ßgÛ¶mdeeqþüùÇZ*¨¨7¯S§NjšF£¡}ûö:½ùtïÞ]ݶ··ÇÆÆ†íÛ·ãææöÈuxV8p###¼¼¼Ô´ªU«âé驳úÄ‹/¾¨{xx`hhȶmÛt.ü*’àà`>üðCöíÛGLL o½õ†††:ùprrbá…껢(ØØØ¨w>öìÙƒ½½½nݺ¥Ž½õ÷÷'++KkåÅ‹?ð¢£S§N;vŒóçÏ…+Ïܯ.¢dEÝîwðàA­‹Èç]«V­Xºt)¿þú+NNN´mÛ–ôôt6lØ Ã;xð mÚ´ÁÔÔ z÷îÍ{ï½Gpp0Px‡*22’m۶ѬY3õn€——+V¬ %%…ÀÀ@vìØÁ¬Y³tî”ÜËÐеk×Ò AÞzë-¬¬¬077Çßß_ Э­­Q…àà`êÖ­‹µµ5Ý»wWŠ&Lž<™E‹±dÉ5j„••~~~êäýâÆ?wíÚ•víÚiýÅÆÆ>~£ @†p” ü1[·nÅËË‹Q£FѲeK.^¼Èüùó9~ü8K—.}¤¡S§N¥G¸¸¸Ð»wo-Z¤^q?¨‡ùAúöíËœ9sprrÂÒÒ’3f”øD¹'ièСlܸ‘àà`ÂÃÃy饗ÔÕBæÌ™CõêÕK<ÖÏÏO>ù„ àèèÈ AƒÔu=ïehhÈÛo¿ÍĉÙ·o999ìÚµ ÿbƒp???õvb‘‘#GòÖ[oi¥™˜˜CÏž=iРuêÔ!77—vìØ¡osHŸ>}hÞ¼9vvvLœ8±Ü~饗tÎÇI“&Ѹqc­´jÕª1þ|ú÷ïÏÊ•+±²²ÂÒÒ’   G ¢{õêÅ|@||<ÖÖÖÌ›7OëëYedd¤5V¾8#FŒ ))‰:дiS|||HKK#..ŽèèhºwïNçÎéÝ»7}úôaþüùܸqƒëׯӴiÓû–ݪU+ìììhÒ¤ ¾¾¾lݺµØ%Ń0xð`"""8zô(ÆÆÆ$$$¨ë ?î]Bµ8½{÷&((ˆäädÌÌÌt>Ç‘‘‘Lš4‰3gΟŸOýúõu~SXlGU`` êvÑXìúõë«i÷Þ!(bggÇ/¿üBvv6ÉÉÉT©R…Úµkcll aÚ°ayyyê|šÚµk—8°_¿~ôë×ôôt’““ %%%EgžChh(¡¡¡%5—(%@—•+WfË–-Ìž=›ØØXæÎKÍš5qwwgùòåZ+ h4¼½½‹ ÝÜÜ´&üøûû³yóf.\È¡C‡ ÃÐÐ;wªÇ›ššâíí­51ÁÇÇGgb…½½½Öøé#F Ñh˜+‰ŠŠÒ:_îÊíÛ·Õm–.]Ê!CHHH --fÍš1tèP¼½½Õ|K—.¥GìܹGGGzõêÅÎ;µ&L 0@kl¹¡¡!þù'ÑÑÑ\»vyóæÑ¹sg<<<´ÎÕ¨¨(@044T'(éܯ(|ßåÔ"##iÔ¨‰‰‰4hЀ/¾ø‚¿ÿþ[kò²···Vh“&M?~üsõP JÜoff¦÷²žŠ¢0{öl033cÕªUL›6×^{í¾¯u/sssa˜w366Ö È¤ZµjT«V_ý•Ž;òÆo°fÍ•\Ä“¥Qv¨¨üüü8qâ'Nœ(몈ÿÊÈÈÐZ…åСC´hÑ‚O>ù¤Ø'O=Ïîm«7âëëËÂ… ‹}˜ŠB<*EQÔ‡9)ŠB¥J•ðóócΜ9Åv²ˆç‹Ðؾ}ûøî»ïh×®™™™,]º”ßÿï¿ÿ^gMcQv&OžLLL /¿ü2ÇŽcïÞ½´lÙ’¸¸8yÄú=ˆ§iÓ¦ìÞ½›cÇŽáççGllìsÕÛ&„xzn߾͵k×tV÷Ï7  +° .0vìX:Dvv6 6äwÞáõ×_/몉»\½z•ßÿ“'O¢Ñhhܸ1¯¿þº|QãÒ¥KlÞ¼™””LMMiÞ¼¹ÖÚB!ÄÓ ´B!„z{žB!„BèAh!„B!ô ´B!„zº‚X¹r¥úøn!„BñäH]AäääpéÒ¥²®†B!D…'´B!„zºœˆ‹‹# š5kFhh(YYYêþ]»vÑ­[7ðööfΜ9÷-/))‰îÝ»sçέô>úˆ ¨Û&L 22’õë×ãëë‹««+ãǧ  €S§N1pà@œéÚµ+§NRËÈÈ {÷îüõ×_Œ?777ÜÜÜøæ›oJ©E„B!Ê'£²®€€èèhþõ¯Ê!CÈÎÎfãÆdffR¥Jâãã騱#M›6eôèÑlܸ‘aÆqöìY¾üòËbËLMMeõêÕÜ»Ìw||<ùùùêvRRGÅÄÄ„Áƒ“ššJDD·oßfóæÍ´nÝš¡C‡I¯^½Ø³gPød¦Õ«WsòäIj׮ͰaÃØ¼y3#FŒ nݺtëÖíÉ5˜B!D’ºŒÝ¸qƒ‘#GòÁ¡¦÷íÛWýwxx8ìÛ·FàAƒø÷¿ÿMDDǧfÍšU‡Ó§O“––FµjÕ8{ö,S¦L!::š`eeEÿþý9}ú4öööê±õêÕcݺu 4ˆ]»v±lÙ2  …BQaÉŽ2vàÀ®_¿Î›o¾Yìþüü|öîÝK§NÐh4jzçÎÉÍÍ寿þzì:´iÓF ž‹¶ ð÷÷WÓÜÝÝÂàún={öÔ)+--í±ë$„BQ^I]Æ.\¸€­­m±û¯^½J^^...ZéEÛ¥±òFݺuµ¶+W®Œ¹¹¹VP]¹reòòò´òÖ¯__çØ{Ç] !„BT$@—± ’’RìþjÕªaffFbb¢VzÑö½Áo333ÒÓÓÕ´[·nqôèÑÇ®³B!ÄóLè2Ö¼ysêÕ«ÇŒ3t&ü)Š‚F£¡C‡üöÛo\¿~€‚‚–/_Ž••nnnÅ–[˜oݺUM[¹r%™™™Oè!„B<da311aΜ9áííÍk¯½¦®Â±zõjêÖ­ËÔ©Sñññ¡^½z 0€M›6qâÄ ¾ûî;^xá…bËurrÂËË‹!C†°iÓ&Μ9ÃÅ‹iذáS~‡B!„‹á§Ÿ~úiYWâyרQ#‚ƒƒ¹qã‡"''‡=zжm[ ©^½:ýû÷ÇÐД”\]]™6mj¼½½Õ´   E!55OOOfÍš…µµ5nnnZtóæÍiܸ±VìììhÛ¶­Vš‰‰ íÚµÃÊÊ cccÚµk‡¥¥¥V>GGG\]]K­}„B!Êrï¸!„B!D‰d ´B!„zZ!„B=H-„B!„$€B!„B@ !„B¡  …B!„ЃÐB!„BèAh!„B!ô ´B!„zZ!„B=H-„B!„$€B!„B@ !„B¡  …B!„ЃÐB!„BèAh!„B!ô ´B!„zøG`ë»a,Ù½IEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/indexes-sizes2.svg000066400000000000000000001731311231437614300246030ustar00rootroot00000000000000 image/svg+xml Full PostgreSQL 23.4 MB Sizes for index of a 1 Grow column with different optimizations (PyTables Pro 2.1 vs PostgreSQL 8.3.1) Originalcolumn 1.49 MB 15x lighter 3.4xlighter PyTables-v.3.1.1/doc/source/usersguide/images/objecttree-h5.png000066400000000000000000002452201231437614300243530ustar00rootroot00000000000000‰PNG  IHDR•Ê­zû pHYsaa‰f΀ IDATxœì}w`\ŵ÷™»UeÕ%K²%Ëror‘lcÓ   ¦cZh/„GBz¾ð!¡„ ó( ð€BÀ‚`llÉ6Æ€mT\%K²Uvµ+íîÝ;ß3çÌÜU±$Ë„„ûCX»·Ìœ9sæœßœ™{Å÷Ô&–eY Ëzù÷\fààÀ8pà o0n©ç~õÃ0 Ãp¹\lWý¶D"ñâ››SÒ³ Ë*Ü.׿ZJ8pàÀƒÄâfËî-‘pðüSºÝnV»ýÃ?¾µ%­`R^~Q[—‰[fb ë_-çç€Ñ/Àðˆ-Ë—ôðkŸŸ™Vü…ôþ0À5Ø·²NÒ&ƒ úR¥”‹I}‘í c­¢ƒ”MKcàÒ´¹ÞJÒ‡¸Œi:·„ÞÝŠ"ÛÆŽ¬šH!ôacd vQlŒ†m»,I\j’ɹvÊ/ªhÖC.Eé ‹%_ F¡mËÖs)‰}Œ3Ò“t)ŒkÕ“¶µFSðÀHv€ZŸm€]5IHCvཨÀCF™ é?¨9è^—ávAŠ×ÈMõ´¶î ·Ö-=ež;‹ùR³Y£¶5uwÇ.5hpÝ•‘s‘AG™,£À„®\„VÍS(6£y"^™v#0àœ1¦‡"Žá/;ÕUYj„ãó˜ ÝV(Ê;Ã2ìqà¯b4ì…Ðù¸Æ«4v"%m‘lHÒ-üÚ<5ÓcæqÀŠVHݨš’úO%-Š—cÀ-éç¤Ããøêæ30¨HñÈKrÅô¦G$p\ö‰ªA jLùsnÉŽ”°@NËF­d0VQ ÜæÕ˜êÔczàQv)ZI¶h+ãȼ´îÂP –% íŽJµ”™’<ÒTˆµi!ÒæÆÑìÔŽ6N‘H ýÅ»~.£Šô’õ8˜t²â>(ç6é^,nåH'CÒ‰”¸ÇÒ­ÇÆ ˆ$q)h¡Üâjt1M4w<*­H‡RPŸtŠ#ËN‘Î8p°5ˆI§ ­±ŒƒE¡˜«± ÈÓD 2¢Ü\k“¢0(–°Hnã(z‰Lë5œ¤aÚ—jçÊY¡xšUعº¡?ìHâ‚4Ã1#)³æMˆQÉK•o•“ ИyRoƒ”ƒké5he}ºUTÞ¯M&{)ÆA?CB|h ÅËs‹áýÑhÔU<~VÆØE=‰ÎîÄ¿V¾30eýrhËÑ+ŒRÙ£í¼ÂøE í6G¶qø©] |Hó1¯‹Õì6CQp›j”ç¹Æå¹Fe)Ì`¬+ÊYBi…³K"’? C85 cj˜ÚÄW1V+‹©MÑ“QùšÃ£D’Ú"6Ó4cš \§ *"0-à1[³5ÙeYªÚ¿\«SôR+F õÆ•œªêzr¶ô‘Šcª j#i¨£´Øf·%TÔ,cL&Ȉª¶h¹6Í T2EPZ¦šÃ4K½,© úeݪõ N 43"þ;©(´1¡Ñ":ÇA‰Êt¾"N3B1©8ºUAÒF’2ø*óT…2ÔRR•ÚdZ7‹n’AZ#æÈ¹êf£;ÁQc„8£ÎPç`Œ†‹âiú¿âVä JE$¿6x˜Ð œÃhiŒJJ‚Då¬Ir€>œ†VWŸ³àŒ+‹e˜ V뎈2pZ²\M8 ?FE¹n6âê#Ȩô®SÙ2r0*F£âV²¬D"n™q‹[¢DÆŒÁÞ?r…Œ†!Œ™à xjƨMëÞvÇ»Z±žàý@zWašœr!LåÒAÍ»M–qqš»p€âeÌrIJÌ×´1Ôq´üŒ–›f|,ÝÏ F¬`7ÛÉ9¤ú ?à*Í6г Ôc|²?ìÑ…’“J®$Á†óWЦ±x… ¨*%NÍW«HÄ/œY¸nʹ2K4’¢€¶ȱ:PsNÍ}©ëõÙ-€ (û”D@ú6ltͲŖˆƒ½bžæê€Zε¼ׂ™¼“J€œXe2ålœ‚:6iVèˆ1h€8Çl­™‡pŽ8€’`¡êlj¾²(=‰%[Î(ÿ*I‚ÊÊp2m4ˆ“^Èé3B9WlKÝÚïϱ÷ç­ˆ4iñ]Š/SúÄGy¢s*S(›ÌhÁJ'gd: «QÜ&ã0(í0¨¤-ã89àrJ3´ñ<=¿‚w MR—© Ô¶ iH³%.ÏmMæ‡$ÿçêÂdý‹s–Œ8Ä×e٘Ǵi=‚²1­‡E«(-Åäà”I>$Lºé³’\2 {‘âÌÂ!(gèõ¤v4w-G4%}¶¤1*J¦kŒŠ8]/F…9J™îÒú²ÞÃdTœ<ê¹£âý2*- n%ftr¡|QffºÏír™‰DG¨§¾)´½9ârû˜qè-Ú#RÈHáp„é1yšÏïjq Ý%¬!“ªÿðd L(Líœ?slÉ„\yõÉcƦÀoïÛѦã àç”g¦¹àΗ¾<'¯¢,w½Òpý©¥©¾~¥oîˆýæ/;¨ñ¢c‹¨¨ƒ¡ø0.d°§¬&W8Ÿ<&}ny`tŽ/3Íã2 I|Ú^ùÁÁΈ ¸¼C¼Jz:©>׉3s¦—¦g¤¸#ÑÄŽÆÈÊ#¦W)œhóŽH‚|¨t;Œ@$Ê÷%ø°i%`L¶QYêÚyÐj8hi¼J/PúzL^3Œ’L9WDàÐÐ@äÉZâ])gjÞÉÉåb0 @–‰ Þeèê¤ â’­µI5¹ƒÅZ1!@_“¨ÆXl5^‡Á0‹æ¨¸üF]ÆP_äë‘ÔáöÑcÄ 5v%9ª}³”‚Ë +o£ ”f1Ÿ„ 3ÆQë½ÈÍ÷Ro ßVL ´Ø€É@ÏLõ2píhô+ CõJU»¹}™ÄV®Ò›­ÈŸ0¿Ó›B‰ÉAµ­N©ø @D 4"دD³åÅ2r ˆ  ÄD€i«¾¸å •U£aÐ’6^ŒH ‡šœÉÈÔY†Íx“è”R©N§S§«U*Õoÿ †2“,X3×dVL‹«d°6H¹29š@dçA$Ód—³®ä$¸Ú¼íùž’¡_F6îGÔ•¶s £â¶&©¥‰¾U?«~:£ê?G¥3*èVÔ ê‰„™é‰8·ˆ¹<ážDs‡iñ¸Á˜×íQ^0½,ööææ éq¹ÜGº‘Âa câæ)CôOB·³Aà¹_.ÛÛPû#™¯¾ß±nÕÊ.ÞÔf èÇË R£ÚÕÚÝÖ5\fó¯Æ<Í‘sÆ`á¤ÌYe¼ ¯ÇÅ Æ2ÓÜU2¿yZi ÅmZ¸W€1¯Ç¸vIÉ1S³³Ó<.ƒRÜ•ã3®?µ43ÍCi)L{à₊ Œ²áâ1d ³\ÇŒwÍ(r-(w7ù–½‰±9FVŠÁ…S €Qšœ7N›¸Z+Áð‡þ‹Ü<ÍÆ@zVÀð­ñ"e\º":k‰qÏ@s ý 嵄c×"¡üIF«yqrÖÀ¤¿¶eN”Hz›¨ÚGû´MªÄ’¡Œfã”ïTù1I&ÄaFR†+=SM #‰Gÿb†€È`qYšèzéÞ §'÷@ \r°8É(÷1ÍÓ â¦´(s]²xƸpãœI˳ .ë#˜3BPg€¸]ÿ~~ú¸Ë b±r’ëâ‚s,ŠEgØ78 [&ù9<5Šóy¸¼ÄÁBKÌ»žapäËD™”çá¢O91šÚŒn k˜ ŠÑ'P6Ï’A {WÔEçNd‹š©?æ¤ЧA²qÊÖ’È«ìml¯ (›`DZÈfqÖC\ŒÓ„JÝÈRÁ"é8f‡Å¶B»H$;§ïäĤVQ1œ $¢*ɯ¦¤c‡QA/FÅy:åuô<˜Æ¨xoFÅpÃ1*Άʨp„:˜A0*ËJdy{N­*&\­±–ÎX[(ÞÑe¶…â-±ÖÎXÔtŸZ5&Ó³¬~·H!#…Æ2S®Å‹§žŠ&ÿÀßžmëß}ùws&uýüʺÂÌŽ÷–ì úŠRƒ™9}^0?vj¶a°œtÏ»Ÿ´SRlñŒœ1¹~Xµµ}ïÁžƒ]ñmûÂì 5uDw·ö|°3´©!´uw׬²€øÉ¿7njmjmÝÓÕ6pæØÀ¨,¼·­£;ÖGÃyÁ ý™"àe={þZÝúúÆÖmû Óü^Ãç1Ì„U·¿[N0ú 3_2;ozi:¬ÙÖþûÕMsRqšÏcd¥y¶ì ÉÈ ÔTNJmÇrà ÕÃfqUïJìïH4w´b£©“Ǭ4ר׮¼&¶Èk3\ÁÕpŒƒ¶O\:GéÒ¤02Òú8rÒ€<]Í—¥›¥pË€áR×9¨–i< =nnœ@ê©ÕNQñ@Î ôE:`´$£…5q'±uÚ¨rŠ™Ê™. &6IfÒ<]!äì#Š£8P n5å+º¦§.Ä’ÁHj°ˆ¸¨%$ÁŸ6À°È°ódê…¢¾6Q·Ë®}!ÂÄ4ª¤}$û}ƒ‰‹v½þ%¹ðdýÙd#}1Ê!QcìÖº5¢˜ÒzÍܹØ8Åh9TU.MD}æ2 ¢©744Q?²û1„¿dc‘ÐSžLËÒES‘â°Rc R©°'0+ÜhÃp¹ûQM1èK’…£4B·¸`­•ŠõcCìÞDº ÀÁÈiŒ“+…½vÕ¥ÔkÚg† …Q†^Y"“ %Óé+A¥\£–ä&*™É×Ô‚\G'©Ø&¢±LÎÒ…rts²3*E÷Q¡Ï âz(pn%z§V÷$ŒÎp¼;f1†ÁÄcOpËâ.—1~TêÇ;nëUìˆ2RÐ…ù´¶þO¿ÿ]AQi #KÓÒ´ó/Ï?”;ª$™=€0nƒü®®ÆÍCΪíÙ¶þÙÛ—@óÎ]ËîZ¶pÓµ§ú¾zçÔ}{»û[ŒÆ­÷vUŒ ø<ÆÔ1éî €ÁØÌ±’-mÙ€ÅÓshI®nDÜëuØl ¥ÃбÿúrInÀ›ê38‡`·Y×ÁÅ5Ò|®“*r§–¤3€mû¯nh ÷ôM3‹s|‹gäŒ+HIõ¹ºcVmSä­ÅÄÙü ï)sòÊ RR}®D‚wDâûÛ£¯nh uc*sËØ‹ïíÇXWO÷êÛΜW¹/åP¤ ‹TcsÇgm¼±é iZk¶µ35;+Í=­$-Åëꎙ4n(¥ÄqéHxiÛåP”ÉZCVW”Ÿ4Õï×™“ ]›÷%&¸3R Ø‘žàÚ¢èÄ x1¸9®,pPÏíÏ“8r¥67¦æˆ@›·J±AzPK°6%DR)”nò."-A„€#_ÑÂê °É# ƒœÊ0©|†¶oL.¬pÙnb[Ò¡‹sÊ5!»"wˆþK$œzJ®2XvŠ&4Wî/‰`·¨& QRùèì- žèI]LÝ(ÃÞopÎE ±Ô$›ã> bR6ÆU“‘LÚII&ÁA‘Hê9^$S¨CdÏ{S.Þ«pEC„mâØ «Ã(¬Ò´ét6 2¨*°¸²BÆ êpÙ¯x6ß–íµ±(•Àžzµìáab TÓËxhe˜%34*µÌ'5…+ÚÉ\Jó7r( €Ì"GCÀé& _"•IòÂH~Ždë)3šç¨~eJÃB)àŒó]¢‘vc‘Š–Ä\%¨TÊ ‡9ö2Xl¼JPqÌôSŸhœÑáj×#h *ÝQö³ä‡³-ÀP—*år• ²›7u.à½R>T³ôkvg=L3>µÐíñúƒ¡X<ÁÝ®>îŒ'x$šÈ ø¦Œòlk‹{<¾Á²}Ëûyö¾¯^òí‰3æ ¦‘‚.Ìê7^xÿïüdãêëúHAñØ–} ÿ⺮΃)~ßè«oŒ0CÞ]_2e¾ø°§ ö@dêvïÅ+kö¥ pצú ø ÒN0¡(5Íç€íû‘èÐ’{%yþ±ù)ë ›f‚g§yª&d^÷å;¹w/9¾xNy†ßcø<Ƭ²À׿4Æeôa“G§]jiÅØ@šßŽ&R}®Ùã7œ^Z˜åƒ±«N3£4=Ýïêê6{â‰ü ï̱@Š}×CÞ¼;f¡¿äà÷È+ÛºâÝààgÀ²RÝé~´wÅÍ„ôëÍQQ{q®Oz ±î'Sàj?‡‚©ð35äÐG¾_ŸXWgrÆÜ.H˜ü@—•›†SJ¥ãÂ@¦¦r´á‡’ñ*¥Á”ëÖVmüÆ•|ÂE¡Êh:ʼnZ`Ä[-Hj9Š…Ê«— yAJTp\Ã8Ê00s$’ºç‘M#º£36òþ4Õ×BP“9¤@ºjM}¤&¥O žÀ<—4PêsàÜÂ(ˆezèâz S‰0Špι…­á©‚‹õ`˜ÒãRfÙCtˆ®Jj½ýDŸ?}A”«‚hLëoZ^•©M!3^mÉžâ`q´1$Ð[ªyP«ð;&–8Z¢Ð–\”“¦ÓwÍwÆz8J-ÄgŠU÷bÔZ2-Q‘fx\‘298u?h°Jqb)ÛeT“ÚŒ#c:Ú2OvÐÏ0”×Hû—ö¤RBX³¶Œ@wH‘,°œ+Ú†‡éNKàš‹@ŽD’J7 ”Ìpš˜9’~FJ&𬨍JP>o+ݻЈJpqŨ”zT‚J1*ŽK~\]ÇìK~BÛ’¹X.¿ØVž¢î¤¼ûàÀy<Ú]V”[qÓê<Ø´âÅ:4º]Lüt´î]ñâÁ¶Æ¸iEãÖØ¢Œx´;y†d/DÜØÞ²ûOËÿ'ÜÑüÒc·t´î*d¤`æ„Ó/,)-7xü÷÷Ý\ÿñúgî»Ù fiÙøN¿` ™TUn_–oB/™ãÉðG¸kGc$MÀ”Ñi>Úþ*Úq5x|¼·ëžWwþìùÚ;^n¸åÿjÿñQ;d§yf”’®lÇïx¹áWªßÕÒ Å9¾¤}]`0öµ£F¹ Œ˜w½ÒpûKõ÷ýugÜä>ñ•ª|ÈI÷d¦ºàÏë[nÿcý/^ªÿù uÏþ³©«÷K(¤‹`4ãÀ³ÓlG&¦&ÕU”ãËHucðµ…£®:yÌó „啦0ÆB=¦Øƒ¶xFÎ׎:nzöè\ßÖÝ]ÝfeŒ3°äP㙾kO)Ió¹ÌfUc{$!\7°Ä>aöéXL,ÛØW·¥Ë“CËâbÁŠsÎ,¼4nñºƒÖ˜,×ÄWA€™VmÏ.u-ç>f¼«;ÎrèˆBF*º*&C Nà|]F_‹Ñ{ Á°pHZèŸ-¾‚£×)C=ˆPÇ1¶Q(S§¤ ¥('Ã"³€Ó´Ÿƒ\š’¢jÜILâ¥þ…Ø€*y„Ë7 q¡)²+K©‚s ðê5š\ꆒdÏÔ4‘I³(ÖÈðg·méÈ9n!Áf R­Dú*S P6–3 =3`«EVK›Ð3îâ¥6Z2€E*’&§‘-ÎpC;àÌ¢Ϲ0]}šN= ª½*ƒÀ dQÄ¿8ÆrÛÅúéó®—(+50ñ@?d4š“adùBx¡yñFKÙ0iùœƒFž€Îs‘9Á¡açBׂ!Éâ…qª\·$³ÜL›¸3Ç$2eNvËÐðÈÞ¸j2?Ùÿ–hšà`’d8‘jÀ¹…\Šsà oT†$†¨”*NE Ò°•OîYäpû"ÇR®EJƒIפ¹’CêGw5¹:`qÀPÔ0ò,\Ê93 ¸¥qe™£ÅÞW:Ç|Åä#-ò^.¹E£Fäâ>½Ì˜ìJ¡¢dò0" ãÒC‹L—ÉÈÂ1jPgqÔ@©&õ?ø‹[¦ÇYÂân[°øÌÑcJÝÌ|í™_í«ßòÚ3¿òÖè1e Åíb ‹ÇãÜ4ã·(¤«}?ÝxÉ ?§»:š(d¤~’„q»XAј³.ûvvvVfF ;+ëìKoÊ/-N ,ŒÀöT…v­¯­^sá¶3á¥M5Á²`Éåð·Ç ¼´3ËŸ1ðíëƒ 'gÀìqѸå÷ðáî™àߘpù £§ŒNë}ÊÓkq·÷³q»Uª?ùM éx$â¤Øb0æ÷‘hâÕ -§Wæg¦ºçMȧBÝæò·÷5µGû•`\~êe'§x]=që©wö54w—s!J…ˆ\o8*Åó{ †ã\¨B= t§"!¬‚cjÊÅ€uDà“fsb¾;;ÅU”ilÙg¾³#îb`YL0ëŒT6:ËH$dâŸq ÝK åÅR<‰©>ì©zÊ[à~ 1-¹iÐrE@ó_à@yl†Çµ¼6Çæâ´Z*"îò‚c°4B“ãÅÉ·È¿a„=‚»kTm¨g¦'-ÔN30¡Ck*Ì& ê [«Ž ©ŽÉ¯rΊ{1€B†²-ŽÙŽ@ù9]¥õp\M@±æ¬0ù¼ÃK)ë%´$XÎÚeúBÍí¦û¤­hÖ¯©ºai ±ÍF Z™¤OE£(›H#†Ðÿ”§BîŒ:RÃUŽ5iÎLìþA•26ëÕ4 Ò`¥­XhròÁW4ɶãíˆå$ô2ñ¥¯èíEµdP÷3n3<êFÐ͘ãî9aÚCbÒðq°ˆá(Ç ¦xe 2“ÇäõD/h`‚ÊZéòsU&ЊVAH%snŒÚÍÐ?ªk4ÿ!¬™¥Uk¨ 4“#“K8ÉÑ××tóÃë[£P q¤.ÓªàJ6ñe¯pÔÒ©ÌKãÍ”á¤2ìQ™êÒvrÂjw·vRý^·‘›_tî5?[ñÜ=ÝáÎ5¯>æuñ¬1¥§\ðŒìè‰Y:¢€k7ýR¿e~c.¸së{U',í¯‘B’0Ðy°©fås™t1`7¼õlNþw2sF Ð"ƒ%U¡]ëÌ]vu*Ôì†ÊR¸åtøáŸöî‡Yg@Ù´y ¿6zàvè9Šç<ã Sݸ±‰öZ £sý‚Qí=ØóÌêÆŽ°9mLúe'÷yqVš»5‘‹€H¯ê]xdsCè¹w›ú,gݎΚº`Iž??Ã;&×?obf Å}âÌœ?ü£ïë`fiúǹ]¬3b>ñöÞý1møÌm–°,‹C{—ÙÕ“H÷»²Ó*Ã(É6NšìÞ%an !ÄÒÍÙ¨`ð´Ua§V¢ eÓ“^ D#eô¡xœc¾]úHŽîx;Ðetú¨,ï1S²™’-N…ºÍת[Ñ'àb½F­ÄÍ>DÅËSíÔÊL°–—/çÜíb£2ŒÒ\c뾄â@L1”‹á/Š;*³$®ÒÖ1¼’“#ÿ©»IÈÏzâ úHZ”ù";úeW‚ õ»’¾06hÞ^£+ b^ŠãmÄÝdE‚…{ZI8 TË“Àʳ@l,“͵q,“’8ê@i2‰féÙ¬~˜‰Á1N3Ê3`(V´H†rÎò®t©ýÏ”¾PT]fèeºù÷íåãzß§iPªGO‹s¤*FÙ,—–h´,X*ù”À‡é™JÅAM˜^ü ¤å ’BA‹âÊÂûcQ@¤GŒxƒ–Ê|Ðx!"ƒŒ›quzU"²ÚaM¢j©6 Òæ,Þêk|4:ûáRHbð7ÓZ‰î h4@_©)í,µha {\Ñ)•¾’çlܸ•“¼ åá§ŠØöž¾è”&Ñ)àè•I›Ì&cœ¹´G®i{è”TœáòøRëöíñ²½ž,‡‰§¬ Æ”Œ)§ËB=æÁ°Ù ÕîmÏ((Iúë.ý’„ )$ 3jb•ßï=avJz6dfç-:íŠÆºÍY%³)Ì¡I•{ëïZBЬT€_žgüiþÚcž ¤C¿ ¢~p0ßÕÚ3÷•cízâÖò·÷9¯`tޝ4ÏÿÉÞðší]ØçÅ¿_ݸhrÖÔ’tƒÁ'{ÃÞÐÒçäÙ¶/ü»×w/žž3nTJºß•H@sglWkwM],‹¯ÝÞQ’çÏ x2RÜñ„µ¿=º¡.¸åä€OžcõĬ‡Vì9iVîô’4ú35o~p #l¨—SQ¤ — ¢qÈN5|nhïæÑ¸d3¹ÇÅÒ<,'•åºÂQ^ÝÆÉXÂ…×rÈtAÞ£%®dMŠY c¥t%®0µ‚]ã8ò mYÙØ#j`cW"‚I÷ƒÎIm¿Ò`¡”9ÚQ!›Åè™eZ&Þû>(‚%}5Uµ•1Õ?Ç’³f=$q,Ðh–n=5€Ö-änET¥­é¸¦‰7ØrZ |4öl2ß-œ å(c˜ÅÉH”ypôڌ…*Uܯ“lû@o fÿïëFuÓkT4EµPKÚr’F˜¤Ù!gÞ›6%‰¯)s òŠIƒÆ§Þ ´oOR:m £ÂÀ©³(uTÒ/-UEä õ ¸ŠH­Ðsc´€¥D$"EZËÛ Õq%•Ú}.Ì”KºªmjDêÃȈqÄ•…3´éL×IË|²ºsÈÊ0¹ê¦¦‚ŠK‰²í©©$cC'¤ö€“3I§ð„š|&ïÂÒ²SÊK§8š´¢SŒt2"tŠàñ¥¸S2w5Ô›|J<á• 9énÚÐOð¶.³¹3ÚÜÖݸëSoZ¦Ç×Çë–F¤#Ò¢¬”QŽõ§»éý[)ìì Ç6wF›Ûƒ†ÝrË-…ó.ß׋š}¸¨Ð®õæ-Û°ž¯†_/…?5ÎË{C`ì²Sú× ]° @²…ìÓ5´¾qN± ð¹Yv øYªÒ¼ÌçÃ`. ƒhœ7´Úº,À™%曈_`-*°’ŸãX/©ä7ð¸_‰¨“ˆ ˜ G.BìCËüË LŠy$ÍÝ´ßÈÇ(ueÓ'òÔ².½$Í µ9E%©©´ÿXV¤Eí³)õƮ݊Uɺ¾”Sñ*R-UÖ¯Þµú*$ùˆN€‰ðiæÐ T•ö±:$øH¿4î‚V©16 Ú‹A$·¿k¤‰x•íQê@a ·ZS…ŒZ§^{oä¶"´#È;Å«Âé;P¯6"á—£"‰q´¢:”žÔòUL†dœ™2*ŠÆ –®d²ƒÓý~!ƨ\ ­²žÜV-&ªqK….EãR4o’YfÊPiŠ¢ŽS•é§÷)Xå'•áÐEÚúmœô Zg`eª$l—üïO*å”lqÅÖ#ŒhO$ØÖ”ºGìò¦ægxÒü†Û0LË ÷X­Á¸ ïnø4Êü9E>ê‘+äóÓ"ŸÛåÙ¿áÉ2UÍŸ®|Ô2˜WO¿×¶>“5qþP³Sú†r`t„<ŸZÜæêõ«Ï`äî‰Ãþ¸µ?¤®Ókãx¥-~¢{uHï¢{3 ¸8!wQ„Q‰+lz\°“-÷€þW:FLCË]ãÊgHç…a‘N)ÉËGåÄw #Š£-"èý¹Æ•¨=Hv„„Ú3M˜@#§ö‰qtqÒ‘kÒ2Ùƒj®nÆÕÔXNsd¦Ò’L¾‘Géζü@I+CËqpRm÷jÊViõbqT¶(j¼–)º¡ ©3euœþ×c&öŸ–ÅE´^è; eG?é,{ û•Aò‘´z‚SG ] 6Âà˜Ó¢n Š¸NqTmL WLÞRZE=ÉtÆ^Œè< k ${°u32*J#âž!\á%m©åC,NˆGà &&Hÿ» ª"µ‰ˆ¿-Íh_Lb9-žt`.à ˆu…;Ôïnògä² ¼>ÿ-äsÕ"H_¹Ì}p?ÙuCê•ß<²Dñ‹ ´éeo(I¬ü*‘íZ°ÅNá pf€|»³E3ôZÉ2V*2§äJbW4N \ë]QìÁeÀC±+Z£¦ B)ýŒðQ8‡§üC"ÂH‡Ï,Æi]³$" Õ–îÈÅÊ{ˆÚ"¥äº9&ÕèálÎ5z Èðd»‰ ‰[ñ1=¢Y”9@éé0mRϸ…iZ #¬?-Óëó°?"…|®Z$0©ŠŸøÌù/KÿÊ3yÓõ¾# Ía~¤M^íëè€OÖÚ–*0&0ËâÊQèñBÎh¾ŒsS¬XR,:I!öèSþÁ³+!òáÄMQ¦æe-øD`ºˆ¦}L±-Š4´¿I1óåD°0v0U•F°ΪITÕ8XZb‰ö‘Ø8îÑ9 ¤),§ôURŸôÊfáFl9tà`©©å´lY3y©Æ€’ùÈ}9šÅÉþ²5 Tu,³ß¨±1¯äcIèÏ?õZíóÞAªþ¾Ð¨±¹HÞ×/PJAÂDÈô¥:½‘´n æ¤7BÆU=ÿÄã`¡¨dšŽü(í“z›)÷ odÒ Ô< = `"ˆ$e€I†̲ٓ¬Hed¹HõÊHÙˆ9#9[¢Ù—LJá‹ Tg23ô¤¡¬B±©;Ë’i¢ªÒÇ¥/ôŠ”fÂ…H}ŒdG¦¦¨ŸqX‰Ú…KÒ2¿ F³,%Ùáë7r®=‰ƒjù,è”c^ŠÛëõ§gģфå–Å Ãåöy|>·Çg f_ùˆ2R!a$©"^¬cÌ”ù0e;ôuÊÁȃ!ëA7‰³š9ØÜ²äCô6vÌ<+Z`ñ$Pî_eCtv%é”4vÑ1¡·4vÅmÁ¤7»’ JZ¤wãÌ‹²YœèǦžÖ•¹ã }‘x­°ØQN{b”sg`Éé-£%YLÄ'nÉPRá*ˆ©ô”zƒ®p€c‘†´m HG 9i`À,N mò&ÿ`‹q‰Qv 6G6›x ¦%8Ö/­Åz’òwßBºLÝÁIPš£Æe€æÖB)±½ƒ)ØÏèÎ^}½rº7íuú»‚ÙOð¾h“NRé:¼h¬}Qq0”Ê£hv꥘ŒIbN€¹KÆ@'Y5“F,’Ø(âòdçB ª‰Û(ãš} æ¡–ÐD5sÅÒuv¥H¨Ê‡Q œfJ’,‰÷–XÄ%£z¥æÌ“lÆP'1ÚáΈ"¡”G PßÏŸæx@q)™ìÓ$.…‡\ªŸe>]­ÒàDjvenáÐSShD¶Mô\Ž8t:E ?ò¹œ>!óxSd:_hCß[ñY2R¶0tvoTD骤AÙ[ÄuOÂõzm V„!ýHn W®Fk¤MBÎÁHAÝÎmÕ(·S™!ÖzÁž¨´Íülƒ[‡¤4E®LÙ0{av»Ã¬ uÝiÈ+íÁŒ¾1ÿ´>KW(·ÉpNÃì¥$·E39×R³hÜÇ…•©ãÀTÏs ž•ß±+´ êZsæŒÚ$}æÖ’¢ê!-oÀmÚHŽ>jÈhƒJ»Åfð¤‚pLÿ+µÃAªßÓ?ÜBün7Ø{òPVæmŸök·HôùŠ¥!1P/F€Þ¤êGgæf8pàÀ_üâÕ µdR‰$ÿ8pàÀ‡Dò:8pàÀƒaÀ!U8pàÀ#‡T9pàÀŒRåÀ8p0 êz=—èÀ8pà pí_'SåÀ8p0èÏÔ8pàÀ8è¶<•ÊTñÁ«çž{¾¢bVÒÏšw×<úèc %®Ñ? DÕ‹ŸÐÝÝM÷5VTÌzáņWæÕW_óÍëo!“±~Æo^ÃqÇ_YYuÚi§ßqçmmmG¨®Ä믿^Q1«¦¦†Ž<Ø&4¯_öÜóÿWQ1ëÓOkáðzüˆvÁ0‰Dz[8ýœsÎ9Ã+vÄ… úÁæææŠŠYË—/ÿ—ˆäÀ_8ØéÓ0ÿöß¾ÿýü‚|ú:eêÔ¦ýMãÇ—¾x‡D[[ÛþðìUW}ý3¨ëpðôÓÏÜ}÷Ý3g~ó†ës²rv|ºãÙgŸ{sś˗?^VVö¯–n TVV@MÍFñjjªý~[[[CCøqãÄÁ55YYY&Œ€œœìϦ÷?ø|¾_ÿúnúzË-ÿ3vléW\!¾¦§þEr9pàÀƒÏ;†Iª.ZHÁU`éÒ¥K—. ‘Uõ£žzê©ó/8?žþT7<|°yó=÷ܳdÉ’;ï¼Ã0 8éä“Î8óŒ‹.¼ø»ßýÞ /ݱã†nWÌœùû?ü¾¿+ï¹ç7yyy>úˆ×ë€ùóç_tÑE>úȽ÷Þ;¼ªàšk¯yù•Wžxâ¿ýío%zàw¿++û›{ïq»ÝPQ1ó´ÓNÿß'þ÷Ç?ù1<üàÓ'O¹óŽ;Ä¢ÕØ±¥çž{þÔ‘µ+îê ]ÒûTaQ!c¬iÿþÁ”sHyâñØOò“ÙsæÐ-K—.}â‰'¾ûÝ›ÒÒÒà¥_JKK=õ´S‡$?TÉmU53gÌØ¸±ÆãñL›6-++«©©©q_cñèâšššôô´É“'õy{<ûŸ[n™8iœwîy/ÿéå7ß\)VÐÏ0ŒÊʹÖ¯¿îºk-˪®®9ÿüóž}ö¹H$’ššº~ý†iS§¦¥¥õôôÔÖÖÝzë­'œ°X”¹xññCm~غ>KY õ‘ØC2éx<þÃþ`Ò¤ÉpÚi§}ôÑÇ>úØ^èqÓK8pàÀÁ ÃÌTÝö‹Û–?±\ü$Å ñx|Ãú §œ²Døt`Œüâ›6¯^üüüóÏ?ÿ¹çž;xÐö0],ÛúÑG'Ÿ|²Ã@~~þ¼ùóªkª ~ôñÇ'Ÿ|’ç0yò”±cÇQQû„e%yÍ`äñûý³fÏÖïúÚ×¾ÅþöÚßD ¯üùÕ¯œþ•”””¡J8zÌèQ£FÕTo€šê3gz<ž±cÇæääTo¬€ê5³çÌq¹\}Þ£’¥ÓÜ҇ꂻÌ_°ùƒ-±Xlûöí¡PèŠ+®ôx<k6À†õæÍŸ/t2~|ùC?ôÜsÏú駜ثBïÂFVB}$.bùÝwßE§†jÒwîܹôuÑÂ…uµŸŽx8pð…Â0ç 3gÌHÚ¨Þ'‚ÁPÜ4Ÿ|ò©§žRëœ[‰Ä¡YÅÀ¸êª+_zé¥ÇìÒK–éÕY–•“«_™›³í“O ê²,+/×v6??ÄEMOKKKKÛÛ¸·÷©ýMû9ç…EE‡,d0ò¤¥¥;ÈÍÍ9ñ¤/½øÂÏ;÷¼·Þz«½½íÜóΪü•••ï¾û.缦¦fÑÑ‹ÄÁ¹sçÔÔÔ,˜?¿q_ã¹ýïL÷z=úWf0Ë4a]0@÷Àüùóâñئ͛·mÛ6iÒäÜÜœ9sçlذ¡°¨¨­­mþüùâ²xà¡zøáGÚÛÛòòr/¹ä’+®¸"IQÃÀÀâRx#(aÒHlnnÖ’I§¥§é[™ÐÙ†T8pðÅÄ‘Mìé.—ë’‹/>7:`r IDATûœ³G¶äì윋/¾è©§ž9åä%t0##`F{ÛAýʃím™™™BÃ0B]]úÙP¨+5%udEeŒÍŸ?ï½÷ÖvvvŠª ï¼óuÔñÕír%å)bÑ(ÒGžóÏ;ïÊ+¿¾åÃ_|ñ¥Y³fMœ8qx­¨ªª|íµ×¶lÙòɶmß¼A¾GjÏÿßÿUWW. wÁÀÝ&NÌÊÎ^ÿþûÛ>Ù¾`Á<X0þŠ+ =Ïœ92iW\\|ëm·@}}ÃËúÓ½÷Þ—Ÿ_pÆ_ª´IX¼C ¯ãI˜„¡šPW(‹Å(­ÕÚÒ ™™#+•üãÈþ™¯×[UUµ~Ãú’’’qv~á—_~¹ßç}äÑGôêfΘ±råJš‹·8°aý†yUóÀçóMŸ6mÝûëèúÖêêj„¨—_vy,»ýö_ê‹;ûöî{ä±Ç&NœxÌÑLj#ù£ šöï·,K| uuíøôÓÔ§ªªj„ñ¿¹ç7ÕÕÕÃNS¾­jùòåœóY³*ÄÁ9sçîÞ½{ÅŠ~¿ÚŒC-ó]0@÷cl^UÕºµë6nÚ8Á|˜?Á¶mÛß~ûí3gøýþ¤êÊËÇÝôÝ›|>_mmíPEíÅ;¤ð}bd%ì-ðLÈ4Í·ß~›¾¾ñÆ™™™ã' “”;pàÀÁ’T ú…êCÆÍ7ß´s箯_uÕk¯½V]]ýÎ;«îÿíoï»ïþÃ/9##cÙ¥—¾÷ÞZýà7®¿¾¡açßpã?ßýçÊ•+¯»öZ¯×{ù—‹³×}ãº5ï®yæ™gâñXssó~ø#G-T ¨sæÎùÖ·n|ýõ×/»üŠW^~eÕªÕ>úØ…]äq¹î¾û.ZdYròI=ôP(Ú¹sçÍ7ßìv«]JÖçÜóί©©ÉÈÈøò)§ CxqãÆåää¬^ý)S&§¦¦ŠƒâóêÕÿ˜UQ1¼ýËwÁÀÝóæÏûpëÖžžž¹s*…ôÐwÜ vfddΘ>ý ‡üæ¤>±lÙ%Ï>ûlGGY¸ð¨|ð¡‡úö·¾ãñxª*«î¼ó®ââbqöØc޽óÎ;xàÁ{ッpTáeW\zäD½âŠ+¦M›öÔÓOÿúž{ÄÛ&Ožòè£egçÐ5S¦L½õ¶[zè¡'–?QRZríµ×Æ¢±Ã—ç”%'ÿòöÛÏüê™´”3Îq ÇZ _@kù6Ùƒ/Mª ©q/8‰kƒƒc-/ µ|›ìÀÁ’TñÁ½Q]$®…SHBÓ˜a°¤ƒ±¸Å «3€ªŠÑ‡)î<˜÷ì¼…õ”›™•Uê_åÎÈj!œs˲\.×PoL$†a0–ÜÃÃÖò/G"aµ´FzzÌqeYÃ.¤­=ÚçñM›>üÒ‰UÃ.¶?ìÙ»·;ÜM_SÒRJÆŒ@Îãs2rÓZÞ[»véÒóú<Õ¸oÏaÈuñy 8$’Þ¨>¨L•ÀÀ‰kÃ`³gØf]ç=QÓ4-Ó´vïÝŸ•¨ÞRçðªC \—š½>%‹eϼªcgÓÁV»*Og^ÿP‹q¹\»7U×½üB°í Ûãå–F›9×3f<ž•?ªüìsKçTr>2¯ès¾Ì±kwÛ¢E³×¬ùð0Ë™>}¼ø ì<†0óí¿W,¯ª««‹Fc‹OGV­Z][[;a„Ã,ùó0r‡a-<ð`mm-}mim€»îº+/ONB"áÐ{ï­ýóϔœóh4šHXtÄå2|>ßð&!à {6oÞö׿eFÀQ]˜_V6ŒB8pp„€¤ªÿT•ž¦ê¤Êe0Ë`.ß×3oîÔ ?9BÞùλïÈÌÌK˜f² ÷¦êš‡~pxÅ®ZµêU«[[Û,‹[VÂ4ÍŒ@ÚâŸyÆW cÈ/¤hm ·¶E¦åvÁP$7fÂãv™¦‹%L31Õÿè‚E;`O- ­L=PQÛ6Å·üÞ!TÁ9gŒ­{èþ¼ÕqÂq)3ÇXñËâÀ8XÀ-žHpËâÜàœC¤ù@÷­ß^·äÜ£®»a¨Òq8ÖòYÂåò0áp7@6<Ønk‹Lœ?¬çŒL¢ªªâðy•eY-­­ÑîžÎ`(‘0O9eIOO­ªª\³æ½-[¶ff|)þÂQ£§®$|6#÷p¬åwVÕÖÕN/9eg0gžyfqQa,€®PG8úó#&m8©¯«cŒ…ÃÃ`>Ÿ˜1yÒD¿sža7ù@]݇¯þ¥ ›;nܼ‚Q¬»Ûr»÷¾öÆ»õõ¡¢Â…]˜UT4Ô9pà``OU *S5¼4µÁ˜Ëep¤¦úÓR=Ó¦Œ«ÞÒ0<ïüæ¦à]ní}üªEÆi óÇ›ôß7Ü`ÚÉ!p{ìüaT·k×Î{ï{ È>þø“gΜâñDkkÛ‡nYµêígŸ}îûßûneeåÊ<ÐÞ3qR©á2ÆfÿŽc1sT~Æ®;]œåñ®ØÖë ‹A@§Õ¶{ÿ;-U‘?¼{í5K_cl÷Æê¼ÕM=÷«áæ]¶lãÀ™Ûe¸ÝV"nÅM`ÜðxÜ)©.—3`†Á ÃSZ`dgd¿ñâîù‹Jç­QIÁE ‘Š8üdLoöÓÕ¥VÓö5¶†¿´´ >¼2c.—+‘HlÚ$`‡É«jkkóó òrs[6ož0~Â’.(++«­«=fJ0ܱcǤI“†]—޹àp¬å„Å‹ó›{Äg±ü l·ío’¥µìo|iáp¸agÃÄ£ñ”Ô”£Ž:jÏž='//oÍš5‰Db¨b©É¡ææÍ~5µ­­rÊÔ KJY$¡$`tw—Æb¥@Âpízá¥wöì6Ç_tÁùiÙÙ‡,–P<º¤¿åÑâÑËõ¯û¾>øb8øÂâˆ?ýG©q¿ÏåvÅEÙi©þáÍz/¿'_qZÒÁå+[¯ú隃mm©–L@g‡7eCXåX¿aÃã?yÉ%—-]zŽØ›‰Äzz¢™™YãÆ[²äÔ×_ý§ÿïÖË/»ø¼óÎ|±Ñh<Òs» XSóiIaNF€¦¹ÜlxÒ›×S!îµBO"ãcE3Ö½U=$Rõ¯¼8}ñ±áæ]í«Ýó¸¡'‹u§–fŒ+w§ÌH¨kÿ®žöVoz&¸ÝÜ0bí)ÙYñ™åõ¯¼t8¤jDVýzs©gW]]‘X, …Å×ú†fÓtüÄÍ›kG y±•`F"‘X²ä¸`°+ ôÑöÃÒ4EE…0pð`[ïkf"===55uÿþæÃ©«7Fpäö‡aXËc=þøòåÐÒÒúÁ–-ï­] ?ýéOrrràŒ3Ïa ˆètw÷ PTš[ZÞ|ó­éÓ§õwa9<çÀ%%%°ÿþh´ïMu`HM~oùÆ–Ï9õTwQ1Äb Òð¢c´]Á`9@y #ÔþÃYçL¸÷žésæ ^¤>y1ªÆ}_Ÿ‹G/wx•‡Äú1fìí¿¯yûï¶ã;v|zÑÒÅC* êƒ-íòk,—œ_~Ô™ß^÷ƒSXÝ~ˆwƒËœsÃ`œCÜ„+yIðعs×£þïw¿û½N8æƒ>)))òx¼¡P¤§'fšf{{çÞ½M dÝ}÷ÏÆŽ-]°`Á KŽÅÍX@mmmFFF^^Þˆl!èêêŽDº»º"ðñ'{óxâì`°»µ¥eTAé0 dŒ¹\ÂÚp¸»»;:ŒÕáÞ0MÊËÇmßÞ7?+/gš¦eY‰Äíd`ŒìÈíCõ-ÓgL?å”S|9D£Ñp8|Ów¾=®¬,##ó¦ï|û0…)**úê™gz½^Ã0Ün·eY¦iÆb1¡áƒÛjëjãq™ÈŒÇã–5œ ˆƒoòþ×߸ª¸xÓóÏGrrŠÊË'ååõfTÀX‚ó}mm=)Çttu U¤$^¥3*px•CÁÐR8CÍÕ㜌]yÙRH${÷µÄâ&çü¥—^²°Ý‘xÝÀ0€õ»`Ê„‚ާüüîGšÛ Ò70 Ø^0‡âú‰Äý¿ýÝ¥—^qüñG¯][ãñ¸7nÜZ^^æñxâq³½=ØØØ4qâèmÛjKKË/¾øšþèÇo¼þš×;¨}O±˜‹›Â~åÄ٥ʹέöU†¯ºrÀr%º2ÌÑsZ÷vv¶›æ× —ËŠG9pæq[ñžÜI3g|ó—¾¼b0c¡]ÛšÅãÒŠËÓŠËSæm{úÑ®NÃð0—8©GÿàPÖòÆ+fΜA_Ÿzú™ Î??##0a„ÚÚÚ)S¦”••¹Ýn¨¨¨ˆÅbÛ¶mÁLU(Ô÷ƒáM›ë|¾´ªª){÷¶®ZUsܱS§XƸeY‘Ha†aÌ™3SBp¨OÆã1˲ °°°°°P?¥ÿή®.˜8qbOOÏîÝ»GdpÄGî!1Hß²háÂqee+V¬hiiu»Ý99Ù7ÝôqŠ> Ù™YYY™k×®«¬œë÷û'6M3‰twwÇbñ`°3ž^TTÔÔÔär¹ŠŠŠêëë§ÆC69`¾1%ó:;¡³3ôɶ÷B¡D^^騱c}^`Ìâ¼¥³³±üO}öÙ®Œ ð¼ýÖø‰‡!Lï|•ΟˆW9pà``ŒØÓý1À].‹™]án‹s—˰†ûˆÙ0ÀÛߨY¬6RæŽ:mÍ.èî_Ù©öÁCáÝ5k22rÎ=÷«[·nOIñ——··?þ¸®°°(7çÌ™œ››‘‘xãu F~ýÉ'Ÿ¼æškSx,ÅÍ„eµ#ç|¹’sÎ9om A¢;3ôWov:´¹ ê …½ui3;‰îHþ´Ñ Á-nYœ¹]¼ÛÌ,™0ý¿ðå‡vlÜù·§»öÕ»RÒÀ23'Ì{Æ•iÅå“/ùþ'OÞ–àsR›«FväŒ!ù–¢¢¢÷×­]ºô¼’’1´§êð‘™•9göìM›6mÚ´iÞ¼y¦iºÝn–ÅÃáȺuëB]]3RRR8À0^\"pÈ&×ïÛ_‰Â”©ÐÕ`Ç¢ÎNèìlÝöÉ?Âa#˜pÒIy?üaa¾í¹ 3¡oó`•‰C“ªÃyB‡[œsnq‰D{¢1ÓLøý^nÁ°ŸÛß° ‚ïx¾…¿\œ|jŒ=ÝÄzx´;‘–é~ø¯»çL)QXµjuIÉ´xœWRS³5ŒdggVTL¨©Ù'æÍ›š•ˆÅâ›6mcÌsà@[YÙ¤¿þíoƒ&Uf<žvõlØÒ`š ÓLÄMËL$ò­ÇÍØÝÙâií‹¿5Û¡žht;a8Ëp¹x"^þµ«üc‚¯ÿðÑŸuw˜¼ô¿J¾råÇß²ïý•ÝmMÓ®º%}̤ыNݹúeWJªxpèÕ) ÉZÌ„i&LˆÇl{çL™ýgqn&¬„ÅMÓ²þ?{çÅñð·»W8®Ñ«¨4i*bC±'{‰Ø±ÆÄK,‰1±÷н$1‰cïí§± vDê\Û½Ýýý±xž”ãî8t¾>|v§¼y;³w÷öÍ›Y¦p^‰G@mÆOE‰ÜIM.À…§@`@2@3@3@1— *5`<ÈˤsÒuÞA<žKM™È²æ²³ó|}%œŽŽŽò÷¯}ïÞƒzõe2i“&a ÃÊåbŠ¢ÿý÷FFF>Ÿ/¸}û2†yyæ1pž*–¤ô4EÑ”ž¦ô4K©Â=.ñd^EÍSçÒ§3<Ó0š$IæµQ¥×ÓaÁΜ4E:ú;Öi ýpßo:uÐÑÅÞÙ DN.vÎnÏžÝâ×wœ<¸©ðú)šeÁ?™æß-,ð «×ëëÕ«›ÿ*ÄN(ôóóKKKS*•\LUrr²L&óóóKNN¶Î_U|FŸó€À/ó´ è Ï?T(<:vlöäɋӧ¯|õe¡o…dšf9ˆS$×êUEšðTÃyS†IIIáR¬±«2žÀíó€LËzŒÏ}rí®žpØ´P.qK³˜±—ZýÉ-2ï– /6hÔ¤Vm_!ŸÇ#pÇ1 HŠv÷®©×Ózš¡h†ÒÓzšÑÓŒR¥}ùìqê3+Ý-8Ž»¹¹7mÚôü¹ó¿~ýú`ˆ¬J}–š™•Ýþ#ww÷òÌ•yÉ9s9;üö‘Ë>zžà|p_;:»yX¨_`88B ŸGWþ êÔ‰+ÌhµŠÃ‡ïnÛvúäIªysÿ1£Ã—k#ƒ]e”òFˆ0AÅ®þÓéh’Ò³Àâ8Æçñ§i†eY+¦´8H=P4€B <t4èÐÓ@Ò@Ñój¢OG²*†bgAB€Es$I …vIITªüV­šÖ¨QãúõÄÆCÄbËIÒ'Oþ—“£!âþý«/_¦ŠÅ. m®|’¢IŠfV,èùŒž¦u:½ nz×b” —$§¦+dÁšŸ¡Rët$ð”žÖjHŠ¢ø|¾T*2«% =%ööå9¸äÜýOùø¾@îÈRºÂŸ|€e ‘Dùì™›!võ»x)_>Ê驲ènÑëiÎhÈÊÊ6$ê´:.TE¡Pr‰D©T*•J/¯¢îÉ2Ñå½H=41$(É%Dr^ÁcH?þ·0 oDØ(jÏžµZíÈoºÙY´Ø›°,KQöŠn­[¨×Ó¥y†J³­ëÖ­ËÅ¥§§› …$\e.¨”øîuäGCu8_xå¸Ã¦…§>y&¿CÕì«?¹%bæÝÒ³÷'1}û»ÈÅ"¡@(à8–—¯ÎQj 4:µ–ÌWëòÕº ©Ö’R3/Þ–‘xÑj•0 suumÓ¶õ¡ƒ‡…BaPPŽãz=’’rïnB¯^»ºº”'îМK¾ùB/ókLkÉ'jM"€´•÷}™àG·}wî Ð6¯W7ÈÇçÁ?ÿÔpv~¸mÛù'” $H;Ä-neoooµnŒç‘E…@X„Y¯©±bõ·)3Ͱ†Y Ç07¸¬¬#_ :-€š z(H„<;`@$ @,¹ AbMY`(Ð4Ã04AàII322üù|†áAp?pz=›‘ñ<==Y©ÌÁqŒahógèt:Ф(©Ä.ºeM3Z-©)PØ)΋\!ŸV¬Ê¥)Ÿº5iERôÚ?©ÕN—“£äªSzÚ,£ŠeY–Å0I€¦H†¡p‚ õ8†á8Žóy Cë)R€8‡< ™òÙT–Ü-ÜáééiXQÅalc€J­¾?±Y³HK§ZôÚ‚GÛ>ýðƒÜÌLÁÓ3”£'# xB*n·Ì©F££$Úg×FN›"Y&¶ØU˜š+Ϫ@†e ž*½ž¦i½Á &“ÉŒKBÔj5EQVúQ"ÚÁ‘M Èåû?gþìÿZyJÈSæ}3Kõ‘ V3£Ì»åô©ÿÍ›?ÿàž¿oÙéꈰ8†Ñ KÑ´žf@O3”žVª´R37ÏûºwÎ˧—kÇO Üœœ{ôì±cÇ?†Õ¨Q#99ùêÕ« Ëåå‘ÌQæ%»Ù³”«#CR´–Êå´ŽLUèdDʇun ÞIçëß=%ß²¹À×·V×®ýðƒÏìß·ÚÄ¢âàì*dQ!ebýkjÀ ǵÁ'¤Õé†ÅqŒ[ˆÇã÷ŒË#†e¬ü~.ÐI(5Ð è ÐëAGø\?õ0ëÙCàAVŠîåc_¤Ã™{ðû¨IaamAët:gy<ìùót;;»ØØž2™˜$õ ÃJ$v=z´ùãô7ÒD"Ë⥧ôænIQz’¢…zZ¯§Õj­VKÑÚ<9öÃ< €eçfåÛûhµ¤J¥Q©ÔµÆJßË`|>èµ@…<;{ÀpŒ ¸ŽÇ0#x€ã<¡ˆ'Ã`'ø,SÞ˜*cʼ[ ¶T~A~‰rrr¸ý‡ÀÑÑÑÒ€ª»û–¶|qý¢½°ñ4§•™ñü¤Ñ’0ˆˆNn7h/’,ódN(ø´Æø‹„]E¡IÍYN%ZØÜª@Š¢y<œa,¸ù¹µŸ:ÖÄê}Ãs 7QÅÍ6Zv Ü'Wæ ôÃ6-Ä”yDÊûËç8‹J9r¶6ºaËóÉ-“ÒîOO>½{ß¼}gò÷ßN1ÛÛÓ]Èçq³à,ËRz†¢™¥æÑƒ¤ÿíûkÈÀO[4tuu+¿>R©´OŸÞ¿ü²~øða‡5j¤M,*cJ»äz> ïŠ\kv\Bku­ØÎё֑a²øß/’$iš¶³³Ã0,§€¾Ÿí6já¢I#¾ˆª_¿<*¡@u¢œØxõŸŽ|‚cÀ0,À¹TXÐ3Àà¸µÑÆùZ t -úWUÜ îàäÀòA…«‰\…<ÔS£€oÄ·iÓÚü&ä2ifæ ww­VS¯^XÿþÝÄb¡^Ïnß¾_¥Ò~þù'ööv#F  …GŽì··çååŠÅæ> êtEÑjµîñ㬗/ó)JOès½<€rL@ëñÛ©š'\£}bgÇÓh´$I2FFn¶“ehB $s_2¥SPC¹]ÅÓû|±'ø€!è)ÄÛ_àà¦~ñD—ŸÇ³‘Œ þ¸£XlDz°sç¡Ó§OQ)ò‡ éÇãგp/++7)éFçNæîxNRz­ŽÔ“$޳öö<ŠbyJ†ñAM¥?¾ñ¿Âz€1|>β ERIqÁ㎠…B¡Ð,#˜e¥iž½$/%)ïÑ=§ºÍü» ºûç|]’Ò©@¯Ó*¥¼VPû€öÅ#=©%DÒòï§`éZÑ2wúQƒZ&“ɤR+”qêr²XÜ£ð¥xÏO- ¯¡§žÁ­›¾Þ5E ݲž ü@wûÈ$—î;-m‚›øÃ0ðⓀÅW²–ìÆ0ŒV«£(ÒDî?I’ðêÝ;ÆæcøäâO±ôt΢…Jø×Mû^¬Tåûä–ˆ™w‹££cTóæ>K—öëÿIßO};~¢L,Ä Çp–…§/rNíß ê—Ë–.õõóåó¬\ÂY9˜sÉRÉ’qŸ^½„ßìØÅ]_æ„Ú{¶”ÔlÌÙ]z˜Û.‚=t]±õä­cç÷8:œ¯Ê4ïåàõq¿ÐÎGœw1ûÏ€ ïšþC ,¥¢Vÿa° `°,7X8IÁý¢à˜•“j}áöèJ0Æ`TÑ çŽiÀ 0€]Gkùƒu§Ž·mÛöâEŠ££Ë¡C'cb:=zæðáƒ|>Žãü#GöóxDÿþ=víÚ›£Tª^¾Lùê«Mf ×é¨üµ€À ´ …š¢ô¼Ì'L=ºôørzí<žT_ Ñëi>'IŠÔS|–JDB¡Ç1óY–Áp Ø==´A^+HØ04ö‡''·¥žÝÿòÖyJ­t«Û¼VÇÁ¹›&ãé‹k§øö2–ÅXšaÙòÎÈ䲨¨æ\ØÇ#B(òx<gggÇlÞ¼%4,Ôº`‘5¸KÉ3çW_ÍÍ ”‰žÄ/Ý ñ‹Y¥ÿ/ ˜´+ºöý½³Þg¼}Ò³‘ú[àÔŒ¿mXÐgLDD=î øª@½ž±(¦Ðô‹Pp×ëõ\ÚdwââQþºYœEÅ2<,OM¤\sèÞ)oïaF"…r|rKÃÌ»ÅÎÎÎÏßoÝÚ5ûöèûq—¿wÀ ´ºÄ”—Ë&2ð“èè1~þ~„-:¡¦ìB–`æ%« ML—ÈæíUù:åµc»/îþU¥n°ÍÉcåö£Žò ¾uu­û8»;EÞ!Ú§çÀQ8èïo‘2‹ª4g°ˆ ÷ŸmÑЬŠmfJ6ÃÃÀ€a gq!(U„ZGdæ£x‰9[ÖAßM˜0yÊŒ¶m{&$<Š‹û%55™ €aš¦âàÁÝ w322µZúüùcsæÌ,5lJN­Ò0|Eéišfº €¼øï5{âÆC¥ƒÆÞ›¡ô\ä;†±4­gh=†öö–½À #„¢‚Œ”G;WôýVZ§ahÍ UúZiïè&v¯ _—›‘zò/š¦ ;;.â¬_E`áÝR7,ŒÛbÑôÆ2©ÔÒuŽg¢6ŠÌC}<Ñðà?|.yÌöò‡ %ZM§²ùNÃdµ;ójw~tàãà gÓ“,2ªJÛÛE½øª@n7)ó—5ÐzÚôËg¸ *0ÚªÊnþU0¶¨tãçëkÙ÷ˆÆÔ$qû–¼G§œ]Y³ïs31ÿnÁ0ŒGAAuše6Å0lÙ’]zö~žþòÀŽzvíÐ,²©Ÿ¯/ÏF{˜áÜùó6”fþ%;céyiR{™‡Ô£ÃÇ®-»¨rUyOn_–ôÔnŸwÒ­vkïo¨g/_Ö®]ÛVÚ"a)þî?²t lìÎÛv®{N—þøÈÇ!CÏtÏ?ë&ß%¶ ø–mA>xЀõ¿mhÖì#™ÌQ °cš¢t,«ÇqÂÎN˜žþ2?_uþüñþý{wëÚÕ|ÉN£!fôz=Ë2 Ã0¤^ûüi¦G)uaõ4÷sÈãfJO ‹ †aYÃqgxBIfâuíoÓj´î!õ­'ó cYÀÌËR=½óâÆ¿zR‡ ìpŸ&)½JòVÆk0ónyüäÉã'Oʔ֧wŒ!ê­¿\rx«·ôÎa!ä«Â=#?õmÔ…ËR)hMŽj¾Ðã…Ò+YþB S_ˆa޳ cÝjÚÜ´Îx* âÐ6΢"', :÷aVýÏQûÎјBÁ»}Kpᬶc6ÇaÑw‹ƒƒC«–-{÷î[·Aã´çÏŽíÜxþÜWW7[ÅQ¡]»Ôu Mwó7ó’£Bêÿ±a{£ÎµÝÜÅ|‰X põ÷héžß8Ÿ8çÖ7àƒÔ‚Ûïßýä´3ýA˦M­S 9«ˆòS‘ïþÃJXIVž™;7q©ãôðÑÃG*µà°©¢¯\à±ØÏßO*µø »wïooïé3fH¥n¾¾Á‰T(1Œž¢ô¹¹¹Æ¿|™2}ÚÔ?îi‘XZ[§$y¸J¥¤(=EéI±›. =Kˆù”€fšeA¯§5&_©S«Döo¡$srU¿Èâ×r#s€ã<;±*ûEâ®5w±gmŒÇÇÕd¿Ð(x")!ã<>!’h³ž¨Ò^:Ùà '¦ï–Áƒ¦¤¦”)ÄÞÞÞê]Ôö’6ŸM˜Z<+Oë)¢<¹t°~͆”¦€WpGª·dç²1gU i4 EQQê‡Ôب2¬´ê‡üÑýôŸÖwé týpåþ£²nTsT„EeŒ™ß->5|€¤hni¤¯o>æÕ¯_¯â„›¾ä­[û?ðܺéÈnõU—öõZÕtqqó%„Ö®Q­0®ÌKMÎ?I'·ž<òäÊÓ¶ug6û´~h¹^^‰@ ÊÉ«ïkÓU€yŽë×Ûl²À°lá:p€sŸp›K²ÜÚl‹‹íüÜÝÜHÊÜ- |¾T*3už1Í›7Û»g÷æÍ›<”“£`h–¦JOI¥¢N;|ñÅKe~Ø:tÛö3”^Ÿœð„aX–ehšaÎgUøŸ-ta±ªN ¾›ÐßÒVüzöÖÌ‹;ÉEŽ´Ž‚ x<Œ ÔÊ\u^6Æãá„€goOˆ¤8Ë04Ii³ž¤>-ÈQùõŒ±´¹’u(ën …Þ^e¿Lú‰¿2Q ›²÷¼ó¶ž\“å%t}þà¡KW æþʤøª@KŸ(bbzíܹ«Äà<6ôT1, özýA`a^rézõ³“Ÿsëv­þä–‰1Ï3ó²rJÞ‰£Z`Î%× œWïÜùû—§õÿy4”Ôo]ëÀýSO±ô­gŽÞ<›å8¸i«ˆñ#\\\Ì|­» ƒ °˜77ª²ñê?Á«Ÿq_Ê4ð,0 ËÃp–ÛùhXLoMäŽãR©D*µl~ª~ýpãÓ[·nš_×ÎÎnøðáÇ·¨EtíÚ¸k×r½D¢LX–­Ù°Ñ¥è>ŽGvPõýE.R`–fX–ÁY¶ÐÚ¥š¤Y–f€eõj­*í¥ »  fXXD£ò´nþÝB„Ôª5}6¡Nûá»×ïS÷q]ÞV¨ ùùX"Ö«•—ͼt%® ,q… ibbz•˜®Õj‹XTå4°*ô“[åy¯èµËç òól¨Lå`Ý%7®[·qݺ,Ë^ºyëïåÇîäÝM8šØ©aãY£º¹¹VЃ°›­þ#ü»Ism¤•-¹uë¦Á®²È¢ª¦p¿Ü;••¼{G^B:AðY†}mN³†ÿÀ²8`@ÓB§ (¿½Ã"Y´æ¿D¬{Sd%#vö ¼~ÓcùI¹¾Ê¯³Øq¶^Úª@’’’bxÍ_9y»Ÿ\«ï–ã{¶T„>•€Õ—ŒaXóáÍ„ƒ…{s ˆJ›>}ºG“!³tZ=;–àâÜÔUügÒ4õ뇿UUà¸[•Æ{x·¼‡—Œ@¼Ã,8Rv|Ü×Yøâʆê´ú¯< ‹ªÒxîD¥ñÞ-ïá%#ï–í•Ç9®+HÄ;º[æóÞ-ïá%#ï<6~÷Áî„ù¼‡wË{xÉÄû€Á¨2cO€°t· Ìç=¼[ÞÃKF ÞEÞØSÁö¯ÊB x):ýwíÚõ·¢@ Du#Ðø¤¨QÕªUËJT@ ¢ºrbW†ñiQ£J­VW¢2@ ï(¦ @ ÂzªÌZû‡@ xEÛ yª@ l2ª@ l2ª@ l€Ñê?T…@ a>ol¨Ž’9¸U59…çȨB˜òT!ª82ªˆjHù‡¸Ï~U“ƒ@˜2ªUŽ"ßc˜£³OX0mÆÇ`¯5:†Ô3J í$d=Lâ)s*MÕ÷™@_·æüàÒõä¤ä ó+NÓ¹mó üíÏ_äYQ r™(¶Wdp€g@-W>Ÿ€Eký÷®ù*!lBùMN@U“ƒ@˜õF•Z­nذaiöï?`­V6`ëÖ­ë×ÿšžþB,_¹r¥ÌòC‡ì~þy]™%/]ºtëÖ­/¾øÂLM,X¸oßÞóçÏ›YQäùs tå×ÔP$[X8«Ð’Œ“¯ínw%)ÿ»žµfì"òo¢eÏ•A ¯û ÞÍàé£7ó_ØË<Í®Z8¾ çWÛywŠ-/ð®NÒ;F§¤ÜÛ—›®rò 7[%„ (¿g³‘‡É¶ró±>¦J(._¾Üp:eÊ”Zµj >œ;•H¤åU­¼xñbÖ¬YŸ~úI·n=„B¾m…_ºtiË–-æUK)òE–˜’›–ÉÃx8­×ã8ŽaÍÐN°À2 @8ˆ‰‘•Ï,Ü—”ð4Ç}V †aRf>Ôägˆå^æV|ã+þ»Uf"¨4äß{ÏŸ8°¡qãÆ_Œa~E„mÁqÛ1UM *„ÙXï©Âq<::Ú2sæL7WWãã)BŠ"ù|ÕZZJjj Ã0Ü+44Ìœ¬dÁÌ’,°æÊ4&LͧˆÓ>ÌÏ~˘:Œ^˲,ÆÒ$Fð0–&q‚ϰ Æ€`èê F‰~J+œe3úxpÇ‹/æ&/Ü{õæÓÖÍ{vhàé.—IìX–ÍÉSÇßMýóŸKY9…•_³³ó¸±½¢šã8\¾þdíŸÿ*”ã€cŽaþµ\ûuo\/Ø[*µË/ÐÞ¸›ºyçeÃÌ`fVþº'3RÒÂ#_/×Â0SÙT\,TJâÙŒ´ÞæîåW3¨•Er H•åmžþ„-cªX£ôE íÛ¿oÑ¢EË–.½Ÿ˜Ø»wÌäÉS )1qùòW¯]Õét¡aaãÇkÔ¨‘A‚éÜ"œ?~õêÕ÷îÝ57a|€¿?üøã¤={ö@LL 8pÒ¤IÅ«=rt媩©ÏkúÔ5jô+›Š€II+W­ºyófnn®L&kݦõ÷¾spt€yóæýùçŸîîî§N2Q¾°_nܸ1oîÜ„û‰.ÎN 2d—iºnjjÊ‚ ¯^½ªR©äryXݰ¸%qb±Øt_™¨U-(òM(Ë_lx™¸)'=Ï9Øé)IÙµZE‹‡Oî<¬Ýöúy²†uâ Ó$²¾öòa*ôKZñ”øëùàÚ–ü< ÿ–¡u< Tiiir7i‡6¡õê8Ÿ°ÅD`d2ýþÇOÏÂIöQuÜðq³vf÷†§ ƒ& jMÛ•Ç#h†ÉÊÌtqqiÔ¤~o§lzžIqÅ„ö5ƒ;ØK=Œ+"›ª’)·cÈ U4½D‹ŠK¯\‚1Tšƒ(C­'÷Ïf¤%»{ù•XÝBK±q º!V©ÌŸ7oÞ¤I“üüüHŽeÙÄÄûƆ„Î;×ÞÞ~ÇŽÆ Ûò×_a¡¡`:·.\øòË/[¶lµjÕjF³jÕÊØŸîܹËËËë» ßµlÙr„ïÖ®[àç/–HŠk{æì™qãÇuèØqêÔi999qKã4MhH(W2ýÅ‹:AuzÅô’IdÏž?ûõ×_¿5róŸ›૯¾bhzÏÞ½{vïœÇcYÖDy®+Ôjõ÷ßO1â‹Úµjÿïÿ[¸p!Ø ÁƒL·ß|3’ÇãÍ™3ÇÅÅ%;;ûâÅ‹$IÚÛÛ›î«ÒjY7Ö•O‘çK>,_JkÕ: 訬笰¥~©žÌð]0F«Ë|Á¸{³š|ÖA@àhÒ§2;}G}Ÿìùs§Àw&lØð—^§qìåø'›þXáÔv‚'äÛ‰'}?ö믿öòtpy™”é|ƒYõ,5õã^}tšüŸþ¥iddHp@˜—âNšÀØUÅãáßÿÇ#ÒÓÓ»téü,õYX½ˆC÷J$âA×wT$u76y4ýWùTh,T³‡O¼trC‰…K“ãÒ^ÙU¾!­'œá,*.½49„ù¼2ª,ÚRÝ<(Šœ6uZDÃפK–,quu]ÿÛz@‘‘‘ýûõûyÝÚ+V–™[„•+WÖ®]{ÅŠå<êׯױcÇß~ûmÊ”)ŽŽ..®àæêæåí]¢nkV¯ ^¼h÷™©U«VLL „æ¶jÕªU«Â瘈†!!!=zôx”X§Ž\.K%8ŽK6QžKÔh4ãÇoßþ#®@vNÖºŸ×}òé'|>ßD]­VûðáÃ9sæ|ðÁ\víÚ•Ù“&jUŠU<¥Ì|pë>Áçß?ýœfh{i~^vCZâÌäž»sò?ÇðìKº|ªa7œŒªÊÂ+àõïPíº]Ýj6áŽï%¥×«×xÀÀX7Ww”Zóµkz]¾“àân0™,˜Ÿ‘UÞg׉䦑‘Ù4üìÚc€u1Hôótv”Žã+V¬˽0œaDE5Oû껀F±FJ[UèN¨l*4ª4á%¦›ãÖ^ÙUœEÅ¥˜Ð0Å›[ªóФ›+¤dOû:;;»ðᆊ¢þûïÊ!ƒù|¾!±MÛ¶[þú‹eYÓ¹Eš!IòÎ;#FŒ ‚ËuuumÒ´é•+W¸ÓÂÿÆú¡ÓéîÞ½;räHCÉ   Úµj¦/)ŠZ¿~ý±cÇÒÒÒH²p~áñãdžžz3b¬Œò|>¿M›Ö†*íÛGïÝ»ïÁÃ!Á!&ê …BÿÕkÖ¨Tê&rŸpÓ}UZ­jDÑ-€È\öív7£Fk6óQÂ%…_'ìIN|–CÓá]È·j™Ì+ƒÉ¤ yì+‰7—hv;†ÁÔ±]š„×.^ÞNd§UgaØëŠÏŸ?¯Qç¡Ø!_]˜âìä¬ÌN6–ì /œ¶vwwwww7Êú\ÃðB-Þ°©ÐPÙØÂSUªœÒ„—RØT•€ºmá•EÅ›Ö0AE¾¦†}cú â1¤( Š¢þøcÆ ›Œä04M³,k:·H; …‚aGG'ã,g'Çû ¯ŒªWZ–¤§R©dÆÙÙÙ8×ÅÕÍ`Ξ=ûÈ‘##GŽ o Û+•ÊjuZƒ­oške–K$<ÏPÅÁÁòò,Ëš®»fÍš5kV¯]»vnnŽ‹‹KìÀØaC‡•ÙW%ÖªF¦U‘çK>³4­ºu´à¹@óð ™ TpÁÍãTü}–rÖ$ž(¸–Lz„«³nŠ<;ðp¬ü˳0º£p¬°Û}Ý8‹*>>~ØÐ!Ïž=ëØ±ãŸ›·–£imUy{{+p9Žc®N.%;'[O©í5¥²ÐàÚ¹óŸ/_­ì3†¡u|}1Ãlà8AXDùWpCV¢œÒ„—\¸t9tûgQqþª:õJõå£[a)£Ê²ù¿2Õ‹»s$ A±b?îõqqi¦s‹¤H¥RÇsr²³²srär¹‘ÝÃÙT%è)‘HpÏÏÏ7ÎÍÏÏ·‰¸” 2dÀ€\Öƒ€5˜Œìæ£9åUùù$Iòù…›;äæf€\&eYÖt]OOÏY³f@rròî]»—-]æêâÖ¡C´é¾*±V·n]‹wEÕ¤ˆýG¸^§}©ðÇìµi‰ÎzÒ…¥^¤Ý ÕëEû(-±&…KøJ^^®¿¿Ëí¹ð¶4¯ I=wtþf.×íR‰ˆK¼}ûV^Û¨ýøÏGöy]§0Щp€&þ0)î×ã2Gyÿž…S‡gþý—/9³àÑӌ쥳“¬k×nÝc.gQ¾ —‰Z5 pw•ý¶õ<`& À°ŠD,v”óåR‘²@[]€x“ ©²ÐSUªœÄ[ÿã,ª úÀ«y@îØüF#Þ03*ïÝ qãÆÿ]ùoô˜Ñ\ ”ù¹Å ׫[÷øñãÜ dfe]ùïÊÇ=‹%" ÃBC/]¾4hÐ .%3+ëÑ£‡înnÀ0Œ^¯K^¯•;qâD‘Ö)½ÞpZfy ôúÓ§O·oßž;=zô˜\.÷4§.‡ŸŸßø ã·mßöðáÃnݺšÙWƵL«j5ª0 ì%>-$ÏoÒµ£ôÏ’“®ºùƸ0î'œûõtTߺ¯fzy>g_²<EÒTž¼dYðáŸ>üóÏ ÿ×ë“’3ò 4R‰hÀ€ØÚ~!žÞµ}¼^oÝY$xÜÛÛ{ë/?Nïܹ½k×®ZaÝŒ[avͦ3“Fv …¿ý¼(OQ€ã„L*€Sg®>¹{Ø·ng7é/ Œ#«`ÞüÜA¯¡Ë\XW(ý: 7N_»çîåÒàÃâ…+:¦ŠSÆÐ:÷ŸSÏ„>„ùØpúïI±âsd0aÂøÁƒ‡|6ü³¾}ûº¹¹å+ îܹÍ2ìè1£ËÌ-ÂW_óõ×_5ú“Oúi5ÚuëÖ ‚ÁC¿r™òTÀ—_}9rä¨?7mê׿_NNî”)Sø|>§1†a‘‘‘;ÿÙÙ®m;//¯ãÇïøûo0ŠÐòó÷×jµÛ·o òùuê˜.Ï+‰–Æ-U(µjÕ:uêô¾›0G`¢nZZÚ´iÓ¢£ÛûúúéõúãÇOètºÈÈH–eMô•‰ZÖuåSä{Œà¤"=ïê9uäÅ?dS5TfžòZ û —Ìŵº{ù*û‹LZ®ÔC…£˜ªÊ"#S¹ê·Ã=£ëzרã8\:8³~»ñÓ—ìù´kP½zuë…ž>sqíêsq‹çÖáâœ^ а¡C¾ñõG}@ıcG'ý0‰/rvõ©÷f¸9\¿“úåøe]Ûú7kÞÜÅÅ…¢¨¤¤ÿ]¾´uÛV¦ÀtìÔÇ6ê0 ÇÑ›ã+‹>z%6DæÁÝËïô¡_‹—w÷ò³HW%4âµýd86!0ÛUE&ÅŠÍ‘@``-[þZ·ní‹”J…L& û¤?®˜éÜ"4k¹jõêŸ×­;vŸÏoÔ¨Ñü… ===ßT/ݨjÑ¢å‚ù Ö¬]³lùrwwÁƒ…3gΜ9sfLLŒ€ÏˆX¼xÉ¡C ZmÛ´‰‰‰Y½zµB¡puu=vì˜éòÀ‚½H4oþ¼ $&&999Ž?n@ll™mI¥Rww·7¥§§ã8°pÁÂfÍ"Y–5ÑW&jY7Ö•O‘§C™mW³³v¥GS u ¸ó\ ½^ÇB$¿i£×2X;ÆR´Ž/ i"=H¡‡ËJãÔ唿¶ÿ“št†Tçq7†aŸ)~˜·+ùÖ°‚¼t‚'pòjÙñËÄ«; F@K Ãâ~9ñÅçŸg>»k·\™2ke΋û,Ë8y× ïJ|ã.†a9jÉš­ñ?ÎX¡ÈzL‘* #ììeÎ5k´À0,3» Ë§óþ;¶¸D%1´ °²(1†©nÃê–ú>³¢z˜ŠÉ±Hˆi9%jhZa>Øô×Wõz IDATéÓ=š y”©ÑP üØYVZÑÍ›·Å߸9öÛo E%*‰¨®äååíÚ½»wL¯ƒÅÆ(’ëéU£HJTTTÓöcž¥¿ñÝçr—:¾¾dY; ²`’?öVdÙ@ua Ÿ<ŽjÚ <.üw÷¶oU“Ã+rž«ÒN\¸€Þ+úž’žö¬´¬¹‡” âþ®v/®l@^qD•£ÈÃa e–ú¦Yv’ Yë@ * [ìSUå æcãÕˆòƒ\îDu¤BWÿ½E9„ù £ Qå@_dDu¤üûT• õvå 惌*D•£ü_…¢’©ðï…kíZ6¶®ú©sWëTA9„EUðê?ÂJM…@TG‚N»juÝ*+0AE¾¦°ÈåŽ@TS‚뾓r3AF¢ÊŒ*@TG¬1ª233ÕjuÙåï=öööVÔB/1E Duä•QevPUȈÍ[¶”]€†æ¾xQÕ\´`b îtÉ’ëÇO¤TŒ^aŠöÕ?¾p÷ÉS.ü¾óìÛÕQEyÓv²ØSÕµ[÷®ÝºÛLÄ{À½»·­¨ÅYT·nYS@ l‚Á®B ÌÁÆ;ªØ¿™\›ÀYTöw»výZ…6Ô¨a#–e®ÇÇWh+ˆ*çFECoï@_}>ü³k×®›s #"5jøõ7§ŽŸHAFÂ"llTYmQ‘$y7áÃ0¡Á!"‘ȶZ!ª/×®_ûvÌ˜Š ]gYvÙòåP¡­ ª hèÍçÝè«ÿý÷z|ü”É“Ë,9köìJÐñNòÖéØŽ}'–ü¶vüg_•ß®2‹«KPPP9…œ={æ·õëß4ªŠ¦TfÍšíííÍ „‚·«ŒuTÎh£÷4ôæSÝûЦ鷭â]ÆÆ;ª›ï©RiT©9Ï3³^®Üõó'íú|Ú½ÃμUußÞ_^=ÓqÇ€ò’ˇtú´Mûú嬼SOy{Uêì æn DÌÞ°,ýe†i!§NêÜ©c o/?ßÚ>ý411±H«W¯vˆnïíåÕ ¼þÚ5k éqK–Ôªéc8½w÷nì€þ~¾5¼½:wîtéÒEc!÷îÞtØgcÇ;°ÿœÙ³üýüºuÝù;u,((‰D|ðÁô3j×ö-m\ª,,šþCT$hèͧº÷•±ÍäççµjÕJNN~{!Þ)lSeEÜâñËÇ%›=dº]-róËå/µ™Åè(JKQÅä‘™ó§òÿþ¿îÙÞñyËž;2ÞÜ€€€›6q&W£Fš4n´rÅŠ…‹qÔjõ´éÓ»ví M##33_ÆÅ-ùløp>Ÿo,gÆôéîîîÿìÜ)  U«VÑí?Š[²xã¦?`ú´©nnî{÷í·³³€fÍšsµ¥2Žã>5kDO’Ô-^¼¤id¤™-šÎ-ŽJ¥š;o~ëÖ­ ]»v.œß»o/gTÉd²¯¿þ¦id¤X,¾uëæÊ+:uìxúß3îîîŒV ’ªµk×þòË/ñ¥,½:tˆ@`÷óÏëÊÙÜ¥K—nݺõÅ_RŒÛ-ž‹¨8*yè-eÁ‚…ûöí=þ|%·["Uʨ²bD FU`àë×úùù=xPÍVð ª&åþ¦$LWùý¿ ã?kÔœ¤I©T“š|]A¾®@©+Pèò Huž6O«Õ†ûÝð¶3'ŠKÐétñññݺw78±<<Ÿß¡CÃi·îÝsssïßO0–C’ä¹sçzôèÁY0€aX‡/]¾ $©»xñb¯˜^œEe"‘¨IÓ¦f·h*·DÄbq«V­ §Á!!éééÜqDDÄŒ™3»téÒ¶mÛÑ£Çlÿ{Gvvö/?ÿlõµ¼'8;;Tø;ê/]ºô믿–Önñ\D%P9Cx‹p|ÆG`` šþC”ÛxªæÎžeœòã« A.nÝø?—î(w\qp¥ˆoÿÅ'±N˜ÇgÞ?Mèi†fh–aI†\š¸ÄSì3õ—EÙ/í%²â* †a\\\]]\nߺe8•ÊdÆN)ggÈÍÍ+"‡¢¨U«V­Y³ÖèŠ ?W …’¦i7·rùu$‰ñÎ.eµh*·Dìííåóx}Km”µJ,°Eä·[<QqTòÐ[ŽÍnŠ"ùür-®Z÷¤å#Â}—Þ½{·´,¢<ØÆ¨úáÇŸŠ¤pœeüŸcÙ§q¹y¹ÿœ4.`ík`›OnW¨ò1–°#ìvì'ņŸ«Ê›óõ®N.Å•Ëå8Žgee'ffe9::Nó•J’$‚Âoìì,ptt0®"“É‚1â˱±%µ"ãñx/ËŠ—·Ó-šÎ-?z=]÷î«d£jݺu¿þúëµk…;¹=rt媩©ÏkúÔ5jô«¯ñÂÂI‰‰Ë—¯¸zíªN§ ?n\£F¸¬E íÛ¿oÙ²e‹-JH¸/—Ëbc~ñÅç0oÞ¼?ÿü¸…™îîî§N2´[þ™F­Y´h¡P(9j”¡€½½ýŒ3òyÜê¿;þž5kv‘(u˜9kV—Î{öì1tèPOOO…Byýú5–a'O™ÓgÌìÚ¥s÷n]¿úúkww'ß¹{gÞ¼ù¬Ñhþøã÷ "„AhXXñ”57Ý¢éÜS§N}Ò¿ßê5kbbz—ÙÃôôô ong'º}ûÖ†?þðöö1bDÙcƒxÅšÕk‚ƒƒ/ZÄyøjÕª¯,ó%K–¸ºº®ÿm=猌ì߯ßÏëÖ®X±’+@Q䬙3ë@¿~ývíÜyôèÑèèh¹\.–Jp÷ò.a3¶sûöé»þ·õßÿ½X,€ï‹Å»t©Øë1=ôPúàš#\­VOž<¹I“&P?<¼eËÇŸ8tð77§V©§N›–žžîééÉ•×h4ãÇoßþ#ˆh‘“µîçuŸ|ú ŸÏ7ç&œ6uZDÃÛõ hµÚ‡Ι3çƒ>àRÚµkgÈ¥(ê§Ÿ~ €.]ºÞ¹{×LmMç–9"怌*D…R^£ªaDIJeKKL/³®Xh¯ÔC°Ì«ç –epÀÔ:-WªEm۶ݶýïE  <˜ÏçGEµøuýo†ý@,ÿüË/?Núáλ..Î3fÎ4ÞOÁ0 V÷ø‰“ .øéÇŸòòr#""†ÎåÖ¯_ÿÐáÃóçÍÿnµZíããÓ¿ÿ'\V§N <Þ¼ÜÜ\[·ïO)QsÓ-šÎe†¦i3 ÙæQÍwíܵsç?:ÎÝݽoß~ø‹-«^T²§ÊϤÓéîÞ½;räHCnPPPíZµX–e)Šúï¿+C† æóù†ºmÚ¶Ýò×_…§,ȤÒÀ:u ¹^5jdddr‹\ÚqTÅrczǬ]·vÿýýúö£(j÷ž=]»v³³³CžªòcéÐsEM.[tpáõD ööö7æN¥R‰‹‹KóæÍx¼ÂÉ×Ï222<<<¸ò|>¿M›ÖíÛGïÝ»ïÁÃþeÞ„vvvá ÂmuŸpr„B¡¿¿ÿê5kT*u“Æ_ǰÀç F44´ÕßÞ#šÑ°rIoªi×®ñã‘1Ün¤pôØñâ¹jµÚø58¿þº¾´VêÖ­·yË–âéA,‰‹[g"Å FL·h"÷ƒ?ÌÌÊ6!߸â¨Q£G]Z+Õˆ·5ý§T*†qvv6VÀÅÕûuäVüñdž 6 ahš6Ìòðãº8†Ñ´Þ(ýÍp7gˆŠä:99}øá‡oßÑ·OßcÇåææôíÓYT6ÁÒ¡‡²÷M±¯„êŠD"ã\‚ Dv¯S0 ZÿúVK$.YT ¾¨×¿¬ÜË¡LyªØ¢Þ·oßaÆݼukÇß;ÂÃÃQe,z®(”>¸</ò¥Jê´¬TRZÝWU‹ªa(¯ÊÏ'IÒ½›› r™ÔŠ›°œDyzzΚ5’““wïÚ½lé2W·nݺ ùù:ΰHèåË—æh[fn#R #"J|<.­p|¼Â̈÷™W´Â›ÐûTSæÛiŒéÞ±»O7Ú¥®}ØÐ&CDv">Ÿ?¸Ù€—ún|—múµµ}Hõÿ]Žàäì>ŸøB§#MÌü¾² ^©ñÊ–2”·‰æÍŸ·`Á‚ÄÄ$''ÇqãÇ ˆµò&´E_I¥Rww·7¥§§ã8°pÁÂfÍ"Y–å´?o¾ÚšÎ-cDÌÜU>OŸ>µ²wï=ØôéÓ=š IÊPk(¦t•—Vtóæ-ÁÁAݺ÷¬Dõï÷îÞ>xðPlì€"éž^5Ф`X»ñãÆŽß:vÚsëÖí3¼Æ~û­BQQÁ yyy»vï€ mņìÛ¿ÚÔ©»vïö­]ûmëR½©vCÿ±¨¯âââÞ/9YqäpO˜<åÂ￯eÙS¯&¢*’žö¬´¬Y`/ ÝD/®lx›1U6¬‹@¼ó<~üøÌÙ3k׬iÓº ²¨ë`Íàm눨Ƽ͘*ÖE¼ÛTvLU•dþüùׯ_¯W¯þ“~¨âªV;PšOÙ}eëõ†¶¥Ê*†x7¨B1Uf×ý×BÁEcáÕdTÀÚµ¯ß±]ÅU­v þ4Ÿ2ûêÛ±ß~;ö[Ô¥ˆ÷“jé©j·ä¶áøÔøz¦O­Ö§ByñâÅòåËnÄÇß¾}[§Ó]¿ïS³fÙÕÞWQ…¨PÐЛOuï«ê®?¢ŠS]cªN¯gl0™>­‚<}òdÏîÝŽŽNÜË¿Q  ˜*D…R-=U• Iê¡9‰æÓ¤iÓ„û‰ðóºuçÎ+—~ïÈS…¨PÐЛOuï«ê®?¢ŠS]=Uí–Ü6žæ3}Zœ„{÷† T7,ÔÓÃ=$8hÔ¨‘9Ù¯_¨7mêÔÐà3gÎtˆn_ÃÛkê”)¥%šsøðaWç›7o·Óëã>üpÜÆ=ÿncÎÃ¥uTN+ˆ*zóy7úª2¾ªï=ÕÒSUdjÏôi‰¤¥¥…†… ˆ•ËäOSž._¶là 2P(”“úqÎÜyuêÔÑiµ¥%šííí½qƸ¥K¹ê?>{öl\ÜR3/Á‘™™©V«+H¸á½ÚÚ ¢ ‚†Þ|Þ¥¾BÖ¢B©Ž«ÿl¨þáG}øÑGÜqÓÈÈzõê·jÙ"áÞ½ÐP.‘$u‹/ii\«x¢ 9A 4xÅŠå3gÍ’H$°iãF‰DÒ+&ÆœkDp4jØÈüw ZÝË2Ý ¢ Ò0"ÐЛÃ;ÓWȨBT(ÕÒS¯¬%ƒýdú´8$I®X¾|ß¾½©©©$Iq‰>4U"‘¨IÓ¦EjO4-gà A‹/úçŸC† %IrëÖ­}úô5<ó!ÌeËÞþ¸üMtíÖý‹/¿I{žêåíƒþ¿?ÿ·oÝܪu›*ÍY¥8{æßêÞW3gÎxÛ* Þ}ªœ§ª´ÿ¶ÕsâÄï÷ìÞ=iÒ›4‘H$м¼Î;iuZC‰D‚aX‘ZÅMËquuí񵑮 ‡ zàÀþìì¬!C†ØöBÞy®ÇÇ;fLñ±°,Ë.[¾œ³¨ý¯þŸ=cé¦wˆjòT!*”*ç©*íŠx¡LŸçŸ;¾9ò‹#¸Ó„{÷¬Ó¹L9C‡íѽûµk×6nØØ¤Iƒ' a>†™óº.ëHM}ý+‹x¯àýÂ…óƒ‡ µ¢Tüê?/oC[«W¯jÑ@¥R¹º8—öתe xµ ´4ã–,©UÓ§´\[U±9gÏž]¾|™qŠ™ZUœòYYY_õU`€­š>}ûöIJJ2dqƒeØ-im!Š`Q_U©Ž=}úôéÓ§M#ÖQåtèéZæ`<ç[Ñm½{XÔWUªcÍÿZ@ ¬ ºzªÊ¨^9$%%=yòxþ¼¹:t |ÛêTc*ÓSÕ¡CÇ€€3+ªÕêÏ?>tØgcÇ;°ÿœÙ³üýüŠ;´ÆÆòxü•«V»¹¹effþûïi’$MK6¿ŠB¡˜9sæÚµkØ?zÔ( þüê+°|“[©LÆÐô¶mÛΜ9 ¯è÷ÃÍ›7»uí²hÑbw÷äGÉ÷JXäq÷îÎ:Õ¯¾jõ±X¼iÓÆ˜^½>©)) FŒ=fÊÔ©eô/Àýû÷ƒƒß\ ùßÉ“:N(Z4XE(î©2Ý–u­¼«XÔWU­cÏž= ­Zµ2qŒ@XGµŒ©*ÿŽê•ä&^¼x±Q£Æ -zÛº¼§¼|ùò—_~¹sçö½{ $Iž;wí~Û*¼#Xí©JMI9tèP½zõ#"\¾ü”âÛ/Se&b±Øø¹68$$==½H‘H´pÑ•ªETTpHH™ÛnYTE 7íÚ¶[·víýû õêÕ·n“ÛÒ IÝÅ‹GÃYT¥#Ï;÷Í7ßœ†uèÐñ×õ¿r§>>>/2^šÙhÅö©z?aY¶E‹yyyÜiiÇ„u¼§1Uˆê…Õ!Sá œ;wníÚ5íÚ}Řv IDATP¢N¾Õ1UöööÆæÇ£õúâŶnÛÞ"*jñ¢E­[·ªºbÅò2ÍDó«H¤ã÷sË 77&Nü~õêU±±wîÚ}êôé={ö@™›Ü–†B¡¤iÚÍ­ ׂB¡ (jÕªUÞ^^†¿%Kçæä˜Ù1ò"žE^†ar¹Ü iÆ©ª¸¶Þ=,ê+Ô±ˆ÷ƒ§Š°AØJ¥ÅT!æ`lm˜ÀjO•™øøø¬\µ’’’¶lÞ}ú„…‡+++—–”Hk ‹Åºqã:·™œ|MGGÇÎŽ Y‘[%E%&“)ô’¢¢ÒСC“’’¨Tª( JJnnn99Ù6?ÓâꂌïUYY™›û€Ó¬¬¬ÌÎÊòòö’@‚žªö[«ëѪ{Õ¡n,ú#§Šû@ðµ\ƒt :eEuHw£íFǪjΨü~•"åååÓ¦N9}úÔƒ233ׯ[G£Ñ<‡‹J lÕUUÕ;wž9s:7÷ÁÖ­[RSRÖ®]§  À-Nûþý{&“yíZ’8Enmíì¨Tê©S' Ћи®††…Wûæ3iâµkI<8wöì–-›…„GD¼}ûvÊ”ÉIIWssܼysçÎ;""¸ïÎÈÐ`çÎ-*ð>зïâß¿pþ|jJʯ¿ÎÖÐÐ\¾|…8sE#hI·ßZ]Ñ÷êÞ½{F†IIWÅ,{†Z÷#ÝÜkD2:åî?HwC ¿ÑÿŒ*Á‹íê©ÒÒÒ2119˜PQQÅb Âñã'FŒ!­)jjjGÛºesaa‘žžnXx8§ž¨È­———¿@äîÝD"ÑÈÈèÅËBÞ«7oFîŽÜ°~=…B177Ÿ=ûWA!ýúõ¿}çnTÔžm[·Õ×µµuœœœ.\øý2вÙl„-ÖyŽ IIׂƒ‚‚ƒƒ Æ!CŽ=&•-c‚»ÿÚo­®‡è{…"›ÍæÙÙ¡n,tDAÚLhh¨‘ë¼×Ud*OÔnnèÙ³çììl'ùLinkN‹Å*,*{ó¶bêd¼@}ŽLffæ…óç>Ì«ªªÒÑÑ5zô¶mAFFFòÖKj½¼q#ÃÏo_¿±‰_3rÝÚ5ëÖ9Æ{¥¼xñ2,ÌdÍêÕd2¹:œýóÏ=QQݺeòsI…ššškÉɇï¤g*o ¹rå²`YZˆ8dgezxvšÝÄò¥ Ü«ðð°à  šššG úº±±ñì¹s–ïß“nÝœ Î=yòŠÞ“ç€È/•Í]Џ^PUÄÙ¨~ý÷ôð*Tu¹xªPmll¼sïí×j5Ssû»÷^r!u b¢£_½z5w®츹þþéii&xÃó§x‘Bø¯cäTA:íó…tp&L˜ÐâkD,~6Ÿ:_ª+é™JŠX( Xl0ÆÃáõ›ª/ÕÊv6æúºÊ4;»RçÕëÏ;©ýÅlîL4iÃ[Àо¿ýüùóÒRSÿ7‡ßµÓmi»ÇþGN•pQí½ûÒuªº'(Š4¨ººšÓlî5"òÜýw==-<<ŒûxZPÀy!:cýb©ÅlÑxÐ=×Çä~§.ý‹¡®ÆFªÀô±Òyüœùõ[ UqSSR† bjbâáîvãÆ ßiSÿ÷ë÷ìÁ3Ñ8ý÷îÝóöofjbiÑ{Îÿþ÷êÕ+®´¥K–Œõ ¯üÉ>>þsýx6w@_IèÁC†kHB$EÑ;·oß¹}ûÕëW€œ9wnß~öóînЙ=UB¤„ˆôTuOÄukC !OOÕÓ‚‚  `Áþ;"DÑÖÀYôTj¤ õŸÉFºôa£<{h+ †FC,6Šbñt66ïiõ0g&ÀÐ@OPÈíÛ/Z´pÆŒû¢£ëꈻwí¬¯¯wppäà; pÿþý_gÏ5jôŸgÏQ(”=‘»'x{Ý»Ÿin.ÖŸf´ñ‘—› àÖ¼†€6xªX,Öºõë¹Í;w<<<âââx‡AOU7zªº-ÿþû/ÀÕÕUÄkD2ä¼ûO‚ä'A åMÆ(} §¡’"A MÎ%¾ªb++ªd¿kÊyM«üÜxa£jßÞ½... qšaèÁ¼ÏD‹Ü½ËÚÚúLb"' ÞÅÅÅu KÜbžë'â€6ÞaD"14t»½½Ã¸qãZsWº8U8®@À/%(ZTÝÁÝîú£UUÕ÷2¶Í½†@$@Î9UUœØŒÂí¤· M‚°Y‹˜ äÝgj¯n¢_AÆ":yEµøz† öìÙ³mAAÜ+++[[[Þ1|g¢Ñéô‚‚‚µëÖq÷¹¹»sËÙµˆˆÚx›@¦P’®%ãp81%wÚÛ!=UÝè©êž (êââÂ9xÐÜkD2äï©Úµ#‚·gë€ Ç>ã}æô³Ø(àï=,5,L”ØJ8"­kÄD³§`á^~D¨Môú2Rù³ò¿ë .ÚÐЀ ˆn]ÞÎ?7ùÎD#‘H‚èéý$M_Oïå‹b¾S´q`0èþþs _&§¤ZXXˆ)" EÕ ž*"uäï©Ú¼u_çGï36jëÙÔF†·»~‘‰ªáªè˜ª&ämø\ÅþVE©©h¨/'ö,c-Œ«  ©©‰Åbkëjy;‰Ä:55µæôÔÒÒÂb±55?m ©®©ÑÑÑá¼Æãq|Þ:€&·)â€6ƒÁ˜?oþãG®\MrppŸž*ˆÔžªî ÌC‡´+r>û†è)l6ú¼”<„ ÑˆÁ”ÑðyÅÈ«èÇ äS9óCí] éKy™Œâ1 ·9Þº—º eeåÜÌÈàö¼{÷®´TÔ*%%%ggçô´46›Í驪ªz“ãææÎiW|þÌU¾¡¡¡øçÃk›; siÑÂYY™ç/\€9’B‘B™*‘Û| EÕ »ÿº'âÿY€@$@ÎgÿI`T5‘Ùe©®ý”+H€ÆÂ’)(Bg+0ƒ¦©†1ÐÁêi¡<»ŸÍÌÔ@¨õ6äçç¯X¾,//÷ÆóüõõõyÃs‚lÚ¼åÍ›7~sæÜ¹s;=-mÆt_%%¥œ«>>>ĺº¨¨=$éíÛ· .à«êÞÜm€õë×eddÌœ9«¶¦6=-ó(,|ÙŠûØÕio£ ~¿vC %Ý=F¤]‘gN•³“Óþýí1«ò£oUsM¼º ©€U ™Ì"“Yµæ¬ÆF hdªø°Ñßš¥¬,¼hç˜1c=¶gOdrrrïÞ½7oÞrøÈaMMM¡ƒ9Œ1ââ¥Ë{£öÌ PPP6ÌíØñÜz ööqñ QQ{ÄÆZXX¬[¿žN£óNq@[þ“'€ÄÄ3‰‰g¸ã,X¹'J„>)¿_»!0§ HyæTMœäÃ;X̹ߪi¶Ø^=°X @®–¢‚¡©u,• ( …‚¡Ññ÷Ÿ1܇XŠ3eêÔ)S§r^‰Ä+–Oš8‰ÓäT#œ2räÈ‘#G6'pÖ¬Y³fÍâ6§Móåàââò×ß·'f爻…°ÛÒÞ¿aNU7æTuO # Ò®È9§J‚¹l óãWúù;uþ]{îvÍ…»ÕïV_¸ûíâݯïV]þçëÕ{UWïW©`êmmL›B&“ƒƒƒnßþ;???5%eºï4UUÕ™<&¤ãP]]Mn7TUU•ŸË9@øÜÝž!Ý þƒ´+˜ÐÐP#×y¯¿’©LÞ¼§OŸ‰ušššnnnAÁ!|ÇÅH‘í!!W®\î¶Ç‰½¼q#ÃÏÿ(Cc3¾ f亵kÖ­sŒ÷JyñâezÚ„§ÂªwJg''--­v]È{÷ï•—·ì“f±XgÏ+(°|ÿžtëæ@PpîÉ“‡Pô^û« éˆ|©¬hîRDz=@Ugc¨úõßÓr®S%ÁÜÑ#µ<¨%”””Î_¸Ðv9bÒ\<ÒAðð.o r ;+~ôbÒîÕ½û÷ ÿAÚ9ש’Ö\HWåiAÁêU«x ±JE÷ÇÆÂxH7Uv¥óyª Ý #ÁqFb"N,@ qsªŽ0Òñ‘¸ •© ñÎNÈd²¾žnsw7Àö¾»ætމŽîÕ³u©ÐL‘:ÙÙÙ±±ûy{ÄÔª”ÿúõë–-›½Æ335Ñ×Ó-ÿô‰÷*çÃ’ÍZ^233—,^<ÀÑÁØÈ°/Á.0p…èSójjj–-]jcmÕ«§ùÌ™3^¿~-3Uù€‰êv¥‹xª — "]”••Ož<Åm®Y³ÚÒÒ20p%§©!²¤=}=¾SºÛcŠÔÉÎÎ:qüøªU«¹=òÕêã‡)ÉÉNNή®®999]f­ÎNLttCCÃܹþæ={¾{÷öÈáù¹¹™™YêêB±`2™Ó}}kjªÃÂÂÕÔÔbþˆ™ì3)+;G__ÈÙ¬íJYY™ŒW„t7ºHNÌÇêÚÈþ·#‡›äóß¿¨M›6ñö´ˆ¿€¿@«•`Ša0芊BŠåÊW+×AƒJJ_Ž>ÜÞ†Ž,×êìDÇÄðn—¶ïo?þ¼´ÔÔÿÍáßä HJJ***LNIuww¸ä:Ð%>.NÆÛwœœÎž;'þà‚R»êé’|7ªP€~/HÐ6:¸§êæÓÊ+¹XOÖ3 æ¿×T:Â`! T¶†2ÎdOW!•®²³³Ÿ>Íçý)/Ñõb¢£cc÷üÔrfOffæ…óç>Ì«ªªÒÑÑ5zô¶mAFFFbªÑ©éÈù’ââM›6=}𝥥µè÷Å«Wÿ‡ÁûÉ~øPœ››ÛÔÔ¤££ã8`Àñã'ÓK0…ó¯ë@\ÜŽˆoÞ¼Ñ××[¼xÉÒe˸ºíÙùäÉ“ÚÚZmmíÑcÆ„…†õÐÕå{øÈÑ;"ŠŠŠüüüpxüÑ#Gúzºccã/ ùþ‰íÙ™——רØhff>sæÌ 7 ޖ⢢]»våååÒétGÇ!C†Jp{E%]d¹Vg‡¯Íà!C_¾|:ø¯[79ÀÄÄÄÃÓ3##CÆF•±‰iÈ$Ÿ/•Æ&fâ<Ÿ8ù§,ÕƒtR8¶ׂê^žª?R>:™ZP™ ,ÀpnP0Ј2¶wOå_7n˜Ò+ìZ~3F|D6´ÊßÞÅ¥Q5nÜxñ+–Q(”E‹ÎÿmÁšµk¯§§ïÜaei)èКëç‡Ç+ÄÅ'TWWgfÞg0¢%‹?…D"…‡‡:tÈÚÚæúõô• †sReeeß~ýæøùiij}üô1vÿþ¹þsoÜÈà™Û´mëÎ]»ûôéC§Ñ4456ûâÅ‹YYÙžÿïÃóçÏ'Mœ@ öîÝghdøþÝûâ’b @QQ¡·——ƒƒc|ÂA55µÄÄ3¾Ó¦eܼåèè(ÿôÉÙÙiåÊUÁ!!-Üß–hÕ‡i?òrs„¾}…^---µ³û)û`Gøçî]:®¤$ü$±öÀÅÅE|‹*??_fŠAºÝ+§ê+‘v£ºƒÇ²Y,,‹Á`؇šEœ¶î WÓˆD¥½®¬£J¬O{Ð*;„CnnÞµk×òóŸTWWkiiyzz®Y³ÆÀ@ø1Û¬­­Åÿž&“É»vGzzzFŽ™›û 5-•Ϩ¢R©¥¥¥qqñ^^^œžñãNj۪) #22²_¿þ_ßéÏž=‹‰‰^°p¡‚‚¨ѣGÍ6hð`{{w·’âbî—ƒAß·/zÐàÁ\iššX,Ö¼gO¡k…n100LMKWVV4ç| 544¼š”ÄùÊôðð;ftLô¾3‰ƒÃá°8)¸…ZõaAÚ "‘ºÝÞÞaܸqÍ ¨ïooÏÛ£¥­¢h}}½¡¡¡Lt€üü|ñí*„ü`€@DÓ½þ÷ßÔ”555©(¦¦¦æááÁmÚ‚ŸˆŠŠŠ­­mÔÞ¨&2ÙmØ0;¡Å²[­š¢¨¨ÄkÜŒ1òð¡C¥¥%öö ã@llZZjyy9ƒÁä xûö-רRQQq$n5]ƒž——·rÕ*ŽEÕü0FNNÎòå˹N 3nÜøcÇqšæææ_«¾‰¹(¤ƒC£Ñæ)”¤kÉ8NÞêˆzª 2 {yª”Ô´t¾žþö*±îK½®9 }ùôº¶—Ç`ðõí‡Â·½GüÂþüžŠöPPªTל©ª%ÄîY¿~ƒ`|¤Å ™L^ºdÉÚµk­¬¬nÞ¼Ì Ðð":h‡h{Cb£*44Ô¢7·I°µ[µzõ­›·¦ùN“†^@UU•×ÜÁãñlKpØ…‹—¢öDîÛ»wKmÁâ%KWŠ6­ÄŸ¢®¡Î› ¤¥­ ë›6mLINÞ²eë@WWuuuR}½··Nûo®ººø…UI¤6›m`ЂkD"1™Ìøøøƒq;„Íf³Å\ÒY`0èþþs _&§¤ZXX47L[[‹Dúé7*©¾ƒÁÈøx(è©‚È€îå©RÀTAƒM£Ð©:³æ3ªdƤ|c}~(èa½ú+bhŠRQmEVÈ—ŽŽŽ`|¤Å …BÙ:qâD΀êêoÜ ¯ð‚&<´èo‡p°°èÍÛtvqT}«’±æææqñ €×¯_Ÿ;{6"<ÜÈÈxæÌ™R™Ò@"ñæ¦pÊéèh®^¹²|ÅŠß/æ\*)nÓ—„––&ÿÖÒÝÓÔÔÄáp‹/™ãç×–å ƒ1ÞüÇ]¹šäàà b¤]ÁÏ'x–”–ôêÕ[´ËSê@ODHy·K÷TáñxfCõ›¥M¤òÒû¯?Òzc}­ I}Â$Þ}ü©–ýµøá›§,/̨ ƒÁØ·w¯§‡»Eï^¦&&£~ùðöí[î^ëg’‘H,--á’““3yòd¾ ÉÃGø–ãúÛOœ<ÙÁýíÒBZ¥>ÿý÷_€MyUùëÓ§OXx¸²²riIIˣśÂb±nܸÎm&'_ÓÑѱ³# Âd2544¸—Ò¯§·¸œ’¢“ÉzIQQièСIIITª¨tC%%%77·œœl ›ŸiquHgÅb-Z¸ ++óü… ®®®¢ïUYY™›û€Ó¬¬¬ÌÎÊòòöj5"??_L‹ê‡§ i5]ÊSÕÜ3w°5õgN¯,ª2ó´B«ß•<$Yzéa>ÔÔhZ8ñæ%‰¢©iR…T³Å7WZ ²hhjò:¥tuõÀ 1ƒ&búÛ»R±~H$Ò¾}ûv„áÃ=eiN•——¯Zè3y²M‹•ž–F£Ñ<‡‹:›¶USTUUwîÜÙØØhccsýúõÔ””ˆˆœožÃ‡ÿ™˜èååmnnžžžvæôéµµµ³£R©§N0ÀIIQ±o¿~¼WCÃÂ'Nðö™4qé²e††FÊÊ ‹ wïŽä1ÁÛ{Ê”ÉóçÏ766&‘ž>ÍG4(8˜óî\º®\¹m[P‹ú (z==PXT¸s÷Žž®ž¡¡!or½´åZõë×eddøûÔÖÔ¦§}?ÐÂÂÒ¢{À½{÷~=+áàA_ßéßéÓ:¸ø÷ß·nݦªªóGŒ††æòå+d¬3ôTAd@—Ê©j‹²Ùä5}V¤¾ýÀ()L»¦ç·™¥(S—úêNSþ{†‘#¥æ¹Šñ8ñ=U-Y †¢¢"§Y[[~h¸ˆ4ßßÞÅh» D§ÓW­ZM¡R?ŽÅbeiTiii™˜˜LH¨¨¨Àb±áøñ#FŒÖ55µ£Gmݲ¹°°HOO7,<œ›®¿vÍOwEEÅAƒŸ:uzÂoÑÚzyyùûDîÞM$ŒŒ^¼,ä½êààqófäîÈ ë×S(ssóÙ³Ò¯_ÿÛwîFEíÙ¶u[}=Q[[ÇÉÉiáÂ…ß/£(›ÍFØbçÈf³ûm>·¹qÃÀ˜1cÏ_¸ ÎôV!˵:;ùOžÏ$&žáv.X° rOE6›Í=²SAA!)éZpPPppƒÁ2dÈÑ£Çd¹ïï»Î0§ ÒþtO•˜àpXöd…Q¥U¾Òe1ôPæ×Êâ¾,– }Wùª'«®Ð€¯'ZY™ ÍU䋈da2™ݺÅÝiŸš’Ê Ðü$öGÐ$(8/PðøÛ/]¾Ü¢¿ “ÉX¹jÕ«W%'Nœìi.ÉùtBk·†…‡óU/rt玈¢¢"???ΡÅ÷îÝÛµçÅ‹ŠŠŠC‡ Ù¾ÝÖÖ–#mé’%oÞ¼¾s÷®üÉ>>ZZš‰žå <·#bÇ›7oôõõ/^²tÙ2ÎÈNäTëªxx—· 9• ?z1é÷ zª 2€×¨’B~I«ÐÓPI‹ €…‹&ç_U±•U²ß5弦U~n¼°GˆQµoï^—„ƒ‡8M0tÈ`Þ }ß¾èAƒÿëŒÜ½ËÚÚúLb"¸¸¸¸t‰;p jï^qÔf0‘‘‘ýúõøúNöìYLLô‚… Z{º F‚'bR^^ÞN’!H‡zª 2@λÿaˆžÂF€‚Q¸ý’v1»éÔíú“Õü«îÌíÚÝ¿<)GP=ý œÍÀ<ï-îó† öìÙ3/oon••7ÇAEEÅuÐ n“N§LòñáXT###7w÷ÜÜb¾SEE¥!C†r›#GŒ$‰¥¥%bNïæÈr÷_BB¼³Ó2™¬¯§ÛÜÃÃÝ °=$¤/Á®9c¢£{õl]bŸS¤Nvvvlì~Þ1µj'å333—,^<ÀÑÁØÈ°/Á.0pÅׯ_¹W9–lÖ‚4Çüyúzº+–‹SSS³léRk«^=ÍgΜñúõk™©Çzª 2@þžª];"x{¶þr"‰¼Ïœ~üýo£‡¥†…‰[ G¤ u €Ø€höÔ,Ü˵‰^_F*V¾ñw}ÁEÑí¡ËÛÙã禺º:oȉD"!¢§÷“4}=½—/^ˆùNÕ5Ôys§´´µDb½˜Ó!2FYYùäÉSÜæš5«---Wrššš-JÐÓ×ã³ÔÛcŠÔÉÎÎ:qüøªU«¹=òÕ*&:º¡¡aî\óž=ß½{{äðáÜÜÜÌÌ,uuõN½V—áÖ­[|(óŸëgmeidh@°³={VSS“ ( ¦pBwîÜ1|¸©‰ÉG‡Còê6/À¿¿¾ÆF†;ÛÀÀuµµ|s³²²Æcfj¼mÛÖ?bb8QNûþ@ ®W\Tà?·µ±‘¡ëÀ{£¢„Þ–â¢"¿9s¬,-ÌLM¼½½>Ìÿ–òÂkåøòå‹dÒ:ÎZ]ƒ]»vš˜šÎñó=ì¯[79ÀÄÄÄÃÓ3ƒç/°l€Õ!2@þžªÖ.ÑDf—}¤úÒ®  K¦°:[ x„ 4M5Œ:Ë£ »ŸËÌÔ@¨õ6ÌùßÿV,_6Çϯ®Ž¹{—¾¾¾èÒ›6o™5s†ßœ9 . R¨{÷F)))­ ä\õññ‰Ý¿?*jÏҥ˪««·nÝÂ;⠪ªºsçÎÆÆF›ëׯ§¦¤DDìàd©£(z==PXT¸s÷Žž®ž¡¡!oš|7G–žªqãÆó}¹Š€B¡,Z´pþo Ö¬]{==}çŽ+KKA‡Ö\??<^!.>ÁÀÀ ºº:3ó>ƒ!$ÛO²)"6–VVVöí×oŽŸŸ–¦ÖÇOc÷ïŸë?÷Æ ž¹?írÕÐÔDØì‹/feepxþ¿ÏŸ?Ÿ4q@Ø»wŸ¡‘áûwï‹K„„HŠŠ ½½¼ãª©©%&žñ6-ãæ-GGG@ù§OÎÎN+W®  iáþ —› ôíËi¶êÃjãZ>ž={vúÔéÛ·o·¸9·´´ÔÎî§ìC‚៻wy÷2zª 2@ž9UÎNNû÷ÿ!´_ĬÊoŒþ½UÍ5ñêj€¦T1d2‹LfÕš³14* ‘q4ªâÃBFk–²²ðÿ±cÆŒ=zôØž=‘ÉÉɽ{÷Þ¼yËá#‡5E&ÊŒ1ââ¥Ë{£öÌ PPP6ÌíØñÜz ööqñ QQ{ÄÆZXX¬[¿žN£óNWSS;zôØÖ-› ‹ôôtÃÂùõØlöo¿ÍçŽÜ¸aGCNÑ,H[xúôéÁƒ ¥¥¯ˆD¢ªªª££Ã²eËEL±¶¶ÿ{šL&ïÚééé 9rdnîƒÔ´T>£ŠJ¥–––ÆÅÅ{yyqzÆ/Zl«¦ˆØX:jôèQ£Gs† <ØÞÞÁÃÝ­¤¸˜k(îrÕÐÔÄb±æ={ ]+t{ˆajZº²²2€wï/a¡¡†††W“’8_™cÇŒŽ‰Þw&ñOÀ`p8×j79‘H Ýnoï0nÜ8NO«>¬6®á…Íf¯]³æ·¿õíׯÅÁDb}{{Þ-mmEëëë ÛMG~`NDÈÓS5q’õ¾UÓl °½z`±€(],ECS&êX*P( C£ãï?c¸±!gÊÔ©S¦Nå¼&‰+V,Ÿ4q§.4Ø?räÈ‘#G6'pÖ¬Y³fÍâ6§Móåàââò×ß·'âñøêšZÁ~‰=UZZÚ ,ì¡Û£¶¦ö⥋óæ\¼x‘@ŽûAMMÍÃÃ۴#£E***¶¶¶Q{£šÈd·aÃì„Ù·jŠàÆÒÇ•––ØÛ;0Œ±±ii©ååå “3àíÛ·\£Šo—«h z^^ÞÊU«8UóÃ999Ë—/ç:!0̸qã?Æiš››­ú&æ¢\h4Ú¼€2…’t-‡Ãµvz‡]«“røð¡ššêM›6Ë[‘V=UÐÎþ6…ùñ+ýü:E „JaRi ™F¥2iTÊ 1Ø*’­isBÈdrdänOOÏ=t+ÊˈUUUÉcA:U'Nœ8q"·éåíõË/¿¤¦¦I+›MUU•×ÜÁãñlaçy_¸x)jOä¾½{·ÔÖ,^²$0p¥hÓJü)"6–nÚ´1%9yË–­]]ÕÕÕIõõÞÞ^4ú»7øv¹Š†Dj`³Ù-¸H$“ÉŒ?ø£AØÜ|D `0èþþs _&§¤ZXXH,§£­ÕI©ªªÚ¹{w$›Í&‘HœNƒI"‘ÔÔÔðQcmm-î0¤úz £¥%êÐ ©=UÐÎþ“ ëY8ìg€áÔ/EQ•ï{ìP€r*š¢(¬©±¨oM<ÿîí»+—¯‰ušššnnnGŽëÑ£G»j‘;:::8¾ýJ‰6‡¹¹y\|àõë×çΞ722ž9s¦T¦ˆØXzõÊ•å+Vü¾x1çRIq›¾$´´4ñxü·oU¢‡ijjâp¸Å‹—´˜¿,& cþ¼ù=ºr5ÉÁÁA*2;ÂZ—òòr*•ºzõªÕ«Wq;¯]Kºv-éҥ˿ŒÅ7ÞÎήàçÉJJKzõê-Úå)u § ":Ÿ§jôq£"PRR’eÆRsñDˆ˜´1QN§³ÙìÚÚšcÇO())úúúʾF‡>}ú„…‡Ÿ!á¼½§L™<þ|ccc©áéÓ|Aƒ‚ƒ9ïÎu Kàʕ۶µ¨Ïúõë222üýjkjÓÓ¾‘[XZôïo/z¢Èr­N†††››o‡340àvÞ»wï×Ù³ôõð>ýࡃ‹ÿ}ëÖmªªª1Ähhh._¾BÆjCODt>O¤ÒF(8(¸±©áÛ·ê‹/-Y²äð‘#[ž&%´´´LLL&$TTT`±Xpüø‰#FHkŠˆ¥qqñk׬ñôpWTT4xð©S§'Lð*„‹———¿@äîÝD"ÑÈÈèÅËBÞ«7oFîŽÜ°~=…B177Ÿ=ûWA!ýúõ¿}çnTÔžm[·Õ×µµuœœœr=(Êf³¶XAØü'O‰‰gÏp;,X¹Gx}¬¶ ˵º6(‚°Ùlnœ]AA!)éZpPPppƒÁ2dÈÑ£Çd¹ïôTAd&44ÔÈuÞ«¯MTâÓl^ÑÙ³çììl}&?!.=Íi’Oˆ턼օˆCQá´72üüøËR›˜ñõ`0#×­]³n3`¼WÊ‹/ÃÂLÖ¬^M&“¥¢ “Éšä3ÉÈÐèôéïGÐÔÔÔ\KN Ù.ù²g{HÈ•+—‹KD•®…4GvV¦‡§(—!„K׸W­òTíØQüþ=éÖÍ)€ àÜ“'¡è=y¿ˆ|øRYÑÜ¥ð´:€ª"®‘ú×OÿØ4„þx´ yY6ТêÚH«~:³¶²þü¹BŠÕ!H§VT‡´ ?›OR>¦&=MTÝÎöC^ëBdƒÄV‹Åâm’H¤/^ôêÕ UHwæTAd€”sª:¸§JÏZgÀ(K6»Ù¯R:a±Q ÑTÅg•Õ¿¯óË/ƒ™FF戩۾}cýüŒŒö ½ä¹y³»ºú®嘘hlÞìîêj2`€‘²2ÞÂ"öÃ!êAxY¹r•±±±ÁVEEåËç/×R®555þ¾øwyë%5àÆRDL`NDt/O•ÙÀÞZx}u\ œ®:NW§«ÿñ§«„êè«ê;š›ê)ëÍÒ‡àa%TÈ/¿XlÞì.UõÅÂÒRgÖ¬~µµÔ¼¼fƒ»]‰=Unîn¯^•ÆDÇŸ?ÎÎÖîôéÓ.Î.ÐSt7 § "º—§êSec ‡Áa6ƒÁb0A,‹€¢X5%\5Û@E’þð¹±½Õn¹¹å††û«W9²·œµ‘-[?³gÍš-P(ÚRH7zª 2@ÊFUßý×ÓHyÓ4a¢( `а0X<EXX,E€Á RÏŠû÷_µj0E·>n43‹±·7 9dˆ™¾¾j]5#ã͆ ·kj(¼‡ 1‹ïèhôíyÿþ‡11yB5tp0ܱãOÏ^JJ¸§O¿lÞ|';ûç‚t_SšA¤@ODt/O•‚’ªzý]RÅ?D²†™ «+ úÚ‚ú/ß>VØ;"µ_¨N¡VEÕCIu´ „ððL0ÀÁá€ÅBffš/^T8ñ´¾žfa¡³e‹{JÊlw÷“ÜYêêŠgÏNÛ¹3ëõëÚÉ“í¢£Ç¢(úÇù„;:>x°àéÓ/ÉMMŒßw¹sÇذùù‚Ö@ZôTAd@÷òTá°Å©°™t&Àd7Ô¢x]„Ô~Epš„Á$m]ƒŠªá±X!ÍÖÕQI$:‚ ¼Iâ7o¾½yó-çõƒå_ —ÙÛ¼|ùÓ©¦¦°qãík×J8 Õ‚‚<ãã3™?Õ?ŒŠóåKãØ±Òh,À?ÿ”=~¼((ÈsêÔKÞ—®ôTA 6=U åDõî©Âápl ©²¬‚F©©xùþó7&Ê¢’ÉÊŽ(›þñùëêF¤¾üUå[*ƒÁ ±©„£¨ˆ þòåÒ††-4ZP~þb€­­w“‰¤§¿â6¯^-îÑC¥>!#GZ\¹Ṟ¨( ÒÓ_»»÷W®‹Ä‰ê-"ïw@d¬S‘]ÊSÕÜ3w xU-k·ÚOõzýŒPÒ×òWd#-Ì·¦w j}ƺ²*?骪=ê‚ÛàLHðž5«pð?yyt•~SVþïÞ’H4^§Tu5УÇOGÒêè(+(`7l¶víPn'‡Áá¤løvF õ@ÚôTAd÷‹EÛ^O]Þžªæž¹à°AheOiµxF*fy `™ÑÊ Xï+KƒQñŒúî+KÇ‚ÞP¦¨ãŒþÊœ9ûö寯>â4íí øhi)+*â 6§©¯¯ ¨«£òŽ!‘èl6ºÿÃ'`ux‘20§ Òpl'®ýÔ½êTa±X6“QO1Æ(iÕVè׳,‹R[®Kd[À®ý¬ßˆéÍ àêªÅ`„Ut:KAË#£¨ˆkh s{|}ûòMQPÀúøØr›3gö««£~ãC£±îßÿ0r¤ÅÛ·u¥¥5¼qÞZ׆ÿ Hž*ˆ è^»ÿ°à”Tô ʵeˆ“:»¶ªò–Ñ0M¤²¢¼XÙhˆ:ýCµRÑ©$´9OUQQµªªÂÒ¥Ÿ<©¤ÓÙ/^Tݹó~Ñ"çÔÔÒê§Oï»x10žLfîÙ3ZGG™³ûÏÏÏaíÚ¿ø²ÔëÖý•“óÛ½{‡=ùü¹A[[yÐ S,³eË]óÝ\st4xyYWWS¾|i|ð ¼µ·«sQ]]M¡PZ'ªªªí$t( § "ºTNU‹Ã°8,“RG~SL¯ä÷•h ƒÕÐDy[V6±åí3f9•¦TŠÖ6©èÐ0ÍxñRSK͈ø¥G•ÊÊF3³˜ùóSŽôâÅRƒýàAùôé—sr~ãÒÔÄøßÿ’ðrt4ª®&¯_ÿ·`=ÀóçU®®ÇBCGìß?¾G•º:ê¿ÿ~Ž̹ŠÃa¯\™Á|ðàÀo&Né Àœ*ˆ è^žªòÛå·»ÝiÄ KK c³VMÁ`0Ÿ&-ÊË¡é t  § "º×î?HGàKe…˜n*.²Üý—ïì4€L&ëëé6÷ðpwl éK°kNç˜èè^=Í[õ6%˜"u²³³cc÷óöˆ©U;)ÿøÑ£™3¦÷ëK024°²´˜9cúãG¸W9–lÖ‚rçÎíI'öêiÞÓÜlÌèQ•ÒÑé¸RO_¯ú[õéÓ§¦Nr믿ìí:õZ]€3gN¯_·ÎËËkÇÎx¾°¨°¶Fxý&“9Ý×·¦¦:,,\MM-æ˜É>“²²sôõõe©0ôTAd@÷Ê©‚ÈÉÕe_P ‡ÃMòù/ÑjÓ¦FFF¼=-âïàïЪE%˜"E º¢¢’`¿|µš>}Æôéÿmz:mš£ƒýåË—ÛÃБåZòOŸ¶mݺlÙò°ðð'%%&§¤º»»\ rè'Î\)=UÐj£ E··4@>9¿òZÒ"ÅE/y›­JTçБ«t–oÚ´ééÓ|--­E¿/^½ú»'&::6vÿÇOå€ÊB‚ƒsss›ššttt 8~ü„ºº:Ÿ( ¦l ¹råò¸¸;Þ¼y£¯¯·xñ’¥Ë–quÛ³'òÉ“'µµµÚÚڣnj ë¡«Ë;÷ð‘£;wDùùùáðø£GŽôõtÆÆÆ/^òj(.*Ú³'2//¯±±ÑÌÌ|æÌ™6n¼-ÅEE»víÊËË¥Ó鎎!!!C† ÖZtuuñx…ö˱“×ZŽsçÏ¡(ºvÝ:‚ X‘§zýu릱±1Ç¢˜˜˜xxzfddÈØ¨‚ž*ˆ ²§êzzš\6RÉk]Hk‘ QÈÖ¨7n¼µµµ˜ƒ)Ê¢E çÿ¶`ÍÚµ×ÓÓw²´thÍõóÃãââ ª««33ï3 Ñ’ÅŸB"‘ÂÃÃ:dmmsýzúÊÀ@ ³déR@eeeß~ýæøùiij}üô1vÿþ¹þsoÜÈà™Û´mëÎ]»ûôéC§Ñ4456ûâÅ‹YYÙžÿïÃóçÏ'Mœ@ öîÝghdøþÝûâ!_b]˜ BèÛwÈ¡S¦NMºv­‡®nD{æâÈr­ÎK=žžÜžÃGH¤Š !…ß´µµH$o©¾ƒÁhiiµ·ž¼äçç‹iQýðTA ­¦ÛyªdŒÐUÍm³“è˜Þ‡}ûùó祥¦þoΉeÊ ÕÛHll¬¡±±¯¯ïîÝ»e¶(/æææqñ €×¯_Ÿ;{6"<ÜÈÈxæÌ™R™Ò@"ñæ¦|ýú ££ ¸zåÊò+~_¼˜s©¤¸Má --M<ÿí[•èaššš8nñâ%süüÚ²œPv„—-íTku:líìž‚"¬°3Síìì ~Ú]RZÒ«WoÑ.O©=UÐ=U#£_ò†ùD7)).žàß¿__c#C‚m`àŠºÚZîUNAȬ¬¬qcǘ™š„7×)BÎÍ›7õõtŸ?λ®ï´©£GýàK"}ÂÂÃÖïPx IDAT•••KKJ¤5…ÅbݸñßI”ÉÉ×tttìì‚0™L î¥ôëé-.§¤¨Äd2…^RTT:thRR•J%AIÉÍÍ-''ÛÂÂÂægZ\]6›ÍÛ¬¯¯ò䉕••¢:ÔZ &îÞý‡Ûs÷Î===S3!ÛPÆ÷ª¬¬ÌÍý^´²²2;+ËËÛK6ªrž*ˆ èvž*¾Ðžè¦PZ»£ª¹NrÆŽkjjzæôé˜?þàL/++ËÎÎŽ‰ùCPŸ¼Ü\7K¦ã#Jbë‡Íf‡†lÿõ×Ù6}úp„ 2/ÐP^^¾je ÏäÉ66}X,VzZFó.jD«¦¨ªªîܹ³±±ÑÆÆæúõë©));žÃ‡ÿ™˜èååmnnžžžvæôéµµµ³£R©§N0ÀIIQ±o¿~¼WCÃÂ'Nðö™4qé²e††FÊÊ ‹ ÿÃ#"&x{O™2yþüùÆÆÆ$RÃÓ§ù(‚sÞë@—À•+·m jQ¿9sÌÌÌììUUUË?•Ÿ=w¶±±aÝúõ-N”Y®ÕÙ=zŒ»»ûêU+¿|Ùlnnž’œœ½wß>N$úÞ½{¿Îž•pð ¯ït€ïôé\üûï[·nSUUù#FCCsùò2Özª 2 »íþ“B¢º;ª„vŠƒÃáüýˆ ˆà”&J>ùùùgNŸquuåÍšb0óçÍüèÑ•«Ié`2YVT·´°mLT—lG•dr† s³³³ ÍËËå ±X¬E dee^º|¹3–%”Y1uuµòöàp8===¾ÎNÐæTAd@×ñT‰IÕ±X¬;ª$–3oþo›7mÔÖÖž2e ·sýúuþþµ5µéi߃›–ýûw蚥mAª^%õƒ@º#ÐS‘]¤NU«hcEõ¸¸x++kOwÛ>6—/_>uê´djˆ#gòäÉ€Y³gó;šÿä 1ñÌo¿Íç>Îþù§djȘ÷ò¨¨Î%777,,œ·§=Þ#é€Àœ*ˆ àzª¤‘¦ÞÓ»TTÇ`0‚´“>åååí$t@ QiWä¿ûïÕ«Wµµµ A"‘øîÝ»gÏžåææfdd\¹r%**ª¹‰ññq {÷îã5žz÷¶X»v]QQá?ÿÜåô,]²dô¨_x'NöññŸëÇmùÍ™ceiafjâííõða÷Òö¾»¬¬¬qcǘ™š„ß¼yS_O÷ùóç¼}§Må[¢¹é-®øáC™ÿ\?k+K#C‚íìÙ³šššÄy¶mÛúGLLCCƒ¾ž®¾ž®ƒ}2åÅ—ÊŠÖæªËr÷_BB¼³Ó2™Ì¹‡BînàÇçÛœÎ1Ñѽzš·êmJ0EêdggÇÆîçíS+(?^€¾žnàŠåÜ·%›µ |Üû矉'ôêinjbtÍÔÌÌÔÔôÌéÓ1üÁPVV–ó‡Pù|Ó[\q®Ÿ¯Ÿ```P]]™yŸÁ`ˆ{7X¿~Âf_¼x1++€ÃãÛ.³{¢¬¬|òä)nsÍšÕ–––+9M žhusèéëÙÚÚ¶jQ ¦HÁðqGÐ pëÖ­(**v±µ:)>üõ×ÙÎÎÎÄ))+_ºxaõêU(ŠúÍ+8˜ÉdN÷õ­©© WSS‹ù#f²Ï¤¬ì}}}Ùkm&H»"Ïœª÷ïßãp¸˜˜++«OŸ>ikkãp8“Éd2™?~ܰa _¿~‚s{õê-xÉÔÌ ƒÁT|Ëjhhx5)III àáá1vÌè˜è}gÿä `0èûöEâIíò÷8p 6<"B]]x挺ºú4__¡ò§‹X‘J¥–––ÆÅÅ{yyq?^œwÁEGGGCS‹Åš÷ìÉéi»Lé"Y¢ºì Jáp¸I>ÿýKÞ´i£‘‘oO‹øûøû´jQ ¦H‘æÂÓòÕŠ™LÞ¼icHÈömÛ¶v¥µ:/×®]œ;AGG0vìXW×—/_jT%%%&§¤º»»\ rè.cµËÊZ}œÒ*äYQÝÛÛ{ìØ± .|ôèQÏž=8A²²²ÀÀÀK—.I¬ "ÆÏƒ‘““3yòdŽ}À`0ãÆøèwŒŠŠŠë A¼³æúû3Œ«W¯p$\¸paÆŒ™Í•;â›.zE[[Û¨½QÇ/).–Š%Ñ2ÛˆÕeþk-%ÅÅ>“&™™šôëKØ¿ÿ¿Ào8L̬S8!È;wn>ÜÔÄd€£Ã¡ƒyu›àß¿__c#C‚m`àŠºÚZ¾¹¼ái¡ác¾¸^qQQ€ÿÜ>6ÖÆF†®îm&@/"Æ-»ví415ãÇïnd¹Vç‹Ãâp8ÎK‹ÕÔÐÄá„¡üu릱±1Ç¢˜˜˜xxzfddÈH×p곈ùpvr’±z®A»xªš{:e–ÏúKiûÜ]§›™™UTT(((¼ÿ~Á‚=RVVnn! uuõŸ> ^ú\Q¢¨©YË_Û$‰ÉdÆÇÇ<¹Ë{ï½w˜ð}ïÞ¼Ùºu«ƒƒãÙ³g~˜=›ÅbM›>òþý{W7·1úiéië×­;nì¹sçù¶ýæö´ž¾~õÛÇü>|è߯¯‹‹ËêÕkÌ-ÌS_¥>M~Z½IÂïqg¤§{z²ø!0tÉ’ZÎ/!„Dï‰þý÷ß«ÿ§ë—UÇ}¿qcÇ:xpÑÂ…Asæhjj:t0%%yOôÞ §¤¤T™ÊÅÙ%îêÕÒÒR^÷Rî'&ŠYØNzúJ*cªý¬Ñ¡Ók!ç¯îNNþÑÆÆæÙ³g£FJHHpqqyóæ ­X,Vûöí¯]»–››K]æ¹pá!¤cǦTQSS­ò'¹´´„}Bˆ¾¾¾ªªê÷ßO·W:qâÄýûß»wooô^oooWW7¬u5Ú¸i3!äùóç±11ËÃÃ-,,‡.ä(j%¨NÛL;qG©×E|ü­ï¾›È¿ÆÈÈðÖ­¿…lâàà úß颢¢ŸWFvèÐÒ¹sçøø¿N>U%TIpV¬M8Ndd¤›[sBÈ!C}jffFÑÔÔôóóûóÏ›‹CCÕªuÊ…ðõõsvv^vëVü¦Í[jßà_¢ïÑÉÉiYxøîÝ»R’“…EÕ]hh–••‰R§‘øJ—p ! æ/°´´ Ö¨khÐxÝ«AƒíÛ·ç-:»¸|øð¡JÞØÂ¢"?__g—Z¯‚ˆµ‰††&¸éÜ©ó¶­[SR’[´pçp8Ö¯?}úTFF‡óÏÿŠ—/_òBUõ»ÛBp8¥·nÝú!0PÈ%dòï=î™3gV¹ÇýëÎ_©ÅFe~ü$âN·mÛúùsÖüù D,_²Ü—¢{øðáÈý½[Oš˜8q¢¥¥e~~Áýû÷¸•ÜÅÿN È„‰ß-˜?ÏÐÐpàÀb¯=fddþ0»ÿ€ŽŽNååågNŸ.))éбc­GÁ¯™³sqqñž=»=<Øšzúú‚ê” YTçr !m}ÛòÏ¡JGG‡?©U”—W/&ÁXÑ7ÑÕÓåŸUÄÀÐ’››G™?ÞÉ'.\ÔÊÛ[WW7?/¯OŸÞ%¥%ÿm[íî¶ùùffµ\Z宺ˆ>~ü¸*2råÊÈŠŠŠüü|j%‡S–ŸŸß A±:BŒÚW=¾,ÌÀÐð·˜êÌtèÐáÇ .è?`@õ  x§”’Ÿ—Çb± d×âáÓ UŒ{§:t¨è…gÏþ¡eK-[6‡…-ÍÍÍ%„4oÞâè±£&&ÿ}MM‹î7mþå—UÖ¯oÚ´ipHHiI)ïY7·æ¿_¹úË/«~ZôS^^®¡¡›ÍžáÇùúù¹Š<‘ûª¬­­Ÿu-.nÎܹÔ\ƒ×®]5rÄæ-[† J2tè–­[¾Ÿ:uÑ¢ŸtttÖþo­žžþÌ™³äÒr*T¥¦¦ÚÙ}óá¾ÔÔT\Ä‚º“[¨’ƇäÕÔÔ9J{µU,\0ÿÖ­[^^­~Y½ZÚûŠÄÿ[ÜÜÜx“ǶmÛÆÛ»õ˜1£c~‹ š#»/œrW—–M4h°cǯ‹.xò$ÉÔÔdYx85Ÿ!dãÆMsçÌéFkŸ={¢ûöíSc% ` ùv zõû€,Vçà¹s‚ƒ= !½zŸ|ôèñ²eVs‚‚ŠŠŠèjL¿~ý,,ÌwîÜE-~þüùø‰K–,¥«~[ºdÉ‘#‡Ÿ&§È»! éæëí;ÈíCŠ¥œ«ððe¡‹çääð¯tvvNI©úòÉÏωML´KMÍ¿xa !dqhüîÝ[¹Ük²k.0‰,ËN~&„èhª5³Ô˼͸êPïI0PÆëš„°˜0³<È^•+RIII¸F4B¨™’ñ@õòòrþ±Ò oß¾íÕ»B€rB„©B¨Y“åd AAsÌÍÌ\Ü\45µRRRŽ>laa0fŒÌ mB>  Õ!TT!TøÂ’§ûâ…Kç/œçp8¦¦¦ýúõ›1c†¡¡®T('„**„*)Ϩ>aü„ ÕæC¢PZU UbÌ@ j ºX›p¥FJÇLãÉf‹ø5É1±±žl¶´Ûõ®TLI6P Ž¼¼< !1±±µ–ôd³½¼zôÈÝÝ¡ … ¹D¡R§QÜ;ÿS=B¢ùB¨Pý211Õ%sÿþ}Bˆ§§§A&ä…þIF‰_ì‹ÈBÈTª‹Wª„ëÛ·ï»wï${ ‚0!—(ôOê4bòOPhU k˜Q]¾ÜÝÝ322${ B0!—(ôOê4"TBC¨™w º'›½<""`ÌÑ7÷{ÁhÁ„\¢Ð?¥÷b‰õd³%ýň¡ dM¬kTÔw ÆÄÆŠR˜zß±0<ÙlK+›{÷îyyyá§d?½!Òy±{²Ùø6e „*`¨îÝBf̼F!ÄN”MóE/ @£ÄÄü]»#„ò?ëð“HãÅž˜˜¿k÷5QJòKMͧޅD‡P2%ú@õà`OBÈïWÒ¥Þ&€jºwkL½ ˆ¡ dMôêÁÁžxS&+çM=ê1BÈ”d3ªȯ¯¯¼›uOo…åœâOožè4ó$D Tì ºFåëëKˆfn^ÉâPšÿÓÔÝÌ™3åݨ“øøx___zs•š†¶Y“jšÿ,ÒX5@­]ÛîÜ­òn@Uaa^¾|)ïV@˜™™±ÙlÚs!\Þ#„*©ZªyȰ9PäääH¶¡±±1“ë)..~ôèQßý}}}‡ –––V—J€’››+ï& hŸ-·ÿ@Î$˜Q@œœ{{{ɶ}õê/Ç0­z±X,iT õÓútÕC;Üþyªu :ÞýAtÙÙÙ'Bˆ½½ý«W¯LLL˜VÄ5Ђi} föUøàöÈðkTUBUTT±*>-I›@aÕ=…S50­z¡¯¢cZƒá}ÜþÓ9ê1ïñµàÂeÚ2†ÑÑÑiãÓÚÒÊÒ¬aCUUUBÈÅ‹—ž$=¾•¥…E›6>VV–šššååå_¾f}Îzš”œúï·ª™››ÛÛÛBRSS33?Jû(ÄEW\`Z=ôB_Ä´>3û*Üþyªu zÿõ©´ÄËOB™ù'M6tuu==¿ùÖØ¿þú3;;×ÞÁAÐ&VVV#†UQQ¡544LLŒMLŒß¼yóþý;kkBˆ¹¹™oÛ6„çÏžåää0íæÓÞµ™üîÏ}©²kÚÔÕÕÅÜÜLOOOEE¥¨¨èÍ›´¿âoÊ»i"aZÜLkK IDATƒáoìjZÿ=–c;@9 ¨^ÇÓ_{jjåå墯 ‡SëäÉ“-ÝÝÇŒá­rNZ{·¢Õ† n'Üf©¨4±µõiã“““‘žacÓˆó·o³³?›ššÖ±ôbZbf¨B_EÆ<Ø-íšþ×uÔÓÓkÑ¢¹Ížèh.WÈvLÁ´—3_V5B¨™’lFuþ~s­‹2ÓÌɉÍni``¨­­E),,ÌÈxËßí×·““#!äð‘£lÆîÚ= ¿ë]]\„TØ¥Kg–î„Øý>~üDíbü¸±&&ÆÅÅÅÛwì,(øråÊÕôŒŒvíÛ‰x„¼¼¼]»v³=½,--Ë**Ï_¸ôáý{---BȨQ#,-,¨Â‹C¢?qòͦ|D_z]áK7^~|ŸZ½¤¹•]Ï/þÕX!¤Ê&5®”žúÝW‘ŒT{8ÜJnÜÕ¸ýû÷¿|õªiÓ¦+Wþlaaaddh` Ÿ™ùI[[»ŽõKÓÂB€@b T'Õî>_”廿…¥…••UQÑ×?êééëë뻹¹š™5ܽg¯††É®]º˜˜B23¿œ>ur@ÿ×wéÒYH…›íAùRXxèðas æŸOôU$†P²V?ª³X,ÿ¾M›4©þ”¦–fAAÿ%¢ää§Þ­[[YYB´µuj\¯£Ó Ö 32Þ~üøÉÜÜlð'Nž240 „;~ÜÜÜÜÈȈ¿mUš*üXnßÙ¹s·šºª——׌3ôtuÍÍÍ>|x׸qjj±j“1©¾û‹ª„þèÓ¹Ùùkÿ$ª>›ÕÚ`f_E2ŒíᘘÐ_[[›ÃáÌ3777·Sç.²8#uƒ¾ŠÄª@¦$›Q]ôÁ2ûƒdnnF 'O’‚ƒƒ?|øÐ©SÇ 6PÏV”W𷤬¬ÜÔ´aõ¶ñ¯±Â'IIææfÍš9÷îÅ!„]ºxÉÛ»5åU 9'†yùù&&¦:w&„äååÝ¿¿c‡„UÕÌÌL›oNñצª¢BX,òïwi©¨¨POq¹ÜÊÊJBÈ?ŒË­¨¬éœJDQBÕ¹¸ê}ÿãûÔó×Hß.ÎÂÛC/Eé«H†±=kk+ÿ~}555 ‹Š~˜ýCbbbž½tuui?´S”—û*U kâ~ëŸXƒ?d†úp!$%%¹¼¼|ä¨ÑCRž7” õ"VøìÙó6>­4hв¥;!äâÅ‹åååvöö„‹EU»£SV^©­­]\\\ãÞ»ví¢¦¦ò*õͧOŸJKK›y·jE=õæÍ›Æm !¼OBÙÛÛe~úoòÏü©Èµmû¯ÇÍÕµs玄«W¯=IJÒÑÑ™2ù;BHFFƱã'~Ý)D—úìÕdê}¿_W—³WÉÇ÷©çâH¿®²»‘ª(}É0³‡ãà`ß³GwUUÕOŸ>MŸ>=--½WïÞ––VÒ=4Q”P%ß¾Jª€YeðÇÇÌÅÅÅÚÚÚƒ ²³³³¶±1þö\õ{p‚Þ2¨õ"VXQQñôi²·÷?Ñçø±ãööêêê„}}½ñãÆòW¾páBêÁÿþ·^M]½Æ17·07·¨²ò?®¿xñ¢eK‹õéã'.—Ëb±F5jô(BÈ¶í¿–••ñ ïßÛß¿?ïènÅÇ¿ÏüàééE-¾ÿþÒÅ }úö«qïuWãY=såiõñ°æVvþÝ\Õ ½wª1¼½ûws=s…||Ÿzö*Òicf_E2 ìá4oîÖ¹SG‹õ¥  rÕ*c#“>}úéëë—rJsrr%=PÙA_EbU SõfFuNYÙ‰§Z¸»97s¶··¿{÷î;w,˜Ï+P%Eñ/Ö¸^” ©O’žzz²UUU_¼xñøÉ“ƒ ïÌBbccÆŽ_}€Tü­[„Ëurr´µµÕÓÓ+++KOO»xéò¾½ûlmmML!_¾\¼x¹¥{ K ê¯NôžÝ#FŽët1ùR‡ ¶™[ÙíÜw¶ÆõbÕCªå9*W‰ÛžºP”¾ŠdØÃqrt Êèé믊âmûúõ›ßbbùG@2ú*C¨Y>P]ääæ9rüVü_Ÿ?VWWoܸq`Мkqq„w÷–„‹—._wåÕË—U6´¾Ö )ÚÚÚT¸9zô¨±‰‰™™µ¾ àKdä/û÷ÇŠ~?~ÊÊÊ:}úLfæ‡üüüÒÒRUUU}}v ww^±W©©gÏMLL,*,äþ;!ôÉSgÎ=óîÝ;jñIRÒá#‡ÿ¼y“Ò®}û’’’5kÖîÛ·—bmm-z“ÄUã{eÿîn„¸Õ± *\OU5®”Eé«H†=A²³?ÿõçŸþýÅûîEE¤ }•!TLÕ›êssóƒó¯qrúf°d·nÝ»uë^}CAë…Whog×¶­®ž‹ÅÊÌ̆ý]¨?ÿ @QHuFuP*Mš4¹wï^«çÖ×Ý»w›4iÂÀzh‡—•4ÔãÓú ÑW¡ TLÕ:P@,Mš4¹{÷®ÄÛ2¶z!Tè˜ÖÇ`l_¥FU kâT®iSw“ºyaZƒ™}•!T³ TÐ/+ÓúŠÒWA¨™Â@uÙÃË @6ª@Ö T¿uKsåÏ-œœ¨Åø[·R_UýJ€úÍÎÞηm[êñ;w·oFKµU²P2%â@u*Q9;7«µ$@}’’òŒÂËUu‡¾ €”ú*5B¨Yå[ÿ¨D¥¢¢vïþ½ºìËËÓ‹Ë­¼Ÿ˜X—JdÓÍî۷Ϲsçi UüÐWåD{_E„*`®{÷ïJ|ç‚Ëå®[¿žR—Jdƒúï*d¸aÝ¡¯JHÚ}•*ª@¦j¨^‹Åª¬¬”l_u¯@6xÿ]¥ }P2è«TP²&|Fõê¸\nÝwJK%õú* $dÖWáA¨™ÂŒêL€¾ €4 T¬‰~Š‚wPUÀtU´ÃË @ª@¦Ä¨Nè~÷ÿôéÓŽ;ž¦@+„*)¹T‚—€t T¬1dFue†—€4 TLI0Ph‡P U k ™Q€^UÀtU´ÃË @ª@¦Ä¨ž••õõëWÉö¥££S÷Jdƒ÷ßU6ª¤¡ dMôê^ž^1±±uÙ——§—[YÇJdÃSVó£¯JBÆ}‚P2&Ö@u.·²Ž»ãr+ûù÷ïçß¿ŽõÈÀÍ×e°ôU@©È¬¯BA¨Y} úýÄÄ À@‹%ÙŽ¸\îºõ둨ø¡¯JE6}„*`4‹UY)á߀ŒŒ zP ¯ =*òn(—ÔÔ×ÔXuÑq%U½ªÍ›7y²=„ïîæÍ›ëׯ«µUk£¢l7ë@j$J“èEõU$“žž.ïæ0BÈ5P]Þ­èæÍÖ¯¯µ˜iCÓf͚ɠ=Ò@c_%33sá½{õ´±¶jhj’ÔJ ¡ dÊή©½ƒX›ÐøîO §”2nÜøË¿_‘Ò.HÚ›7'Oœ022ööö–w[ä ¡ díÃû·bMª.¥PõæÍëqcìí,ÌÍ\œ›9¢°°ð§ŸýoíÚ‚‚‚†¦& MMÜ[4'„,]²ÄÕÅùÆ={t·±¶ZJ¾½ýGøûï[½zö°¶²rsuY·î›ˆ§NžômÛÆÚʪ};¿sçÎ }ú1räwwwggçwïÞÉ»EŠ¡ dŠÆê-=<þüóÏ­[·tîÜ¥Æb«=pðŸ¯ïšÕ«;thßÜÍuÆõB6ÑÕÕ>¯††ÿ¢ŠŠJEy9!¤   ²²ÒÄØ„ÿYãodO}Àí?5±F© QÇ‘5Ú¸i3!äùóç±11ËÃÃ-,,‡NKÛxôõõUTT²s²ùWæææ4hЀÞˆEP<¢ú*„}ûö%$ÜF ®TÓ êRÿ×uæÖ\LÄúœœ–…‡kii¥$'B454ËÊÊèj¼–––‡‡Ç…óçyk^½z•’’BWýô¨s€ºÀ•*©Tñª×¨T%VÊÈÈüavÿÊËËÏœ>]RRÒ¡cGBH3gçâââ={v{x°554øGJI&äÇÇŒ=kæŒ199¹‘+nذ!þt|Õþbáë«È =õBÈ5P]ô›€"¼û‹ª ¬¬¬¶lÞüöí[—;wuêÔ‰Ò»wïqãÆG®\™››kaañèñë¤{÷;vüºjUä‰'š4i²`ÁÂmÛ·éëë×±Z€º ±¯ÂårÏž9Cy’ô„råêSSsss!Ÿ–¨¯ª@¦ììš2á;jôõõ7mÞRãSªªªQk×F­]Ë[³,<|Yxx•bsƒƒç *ðë¯;ù4pÐ êqnnî¬Y3ýûù×ñê‚Æ¾JEEÅwßMä-ÎûñGBH÷î=ö8P×V(„*5qª‹ÐQþ§3ïSEF®ìСƒ±±ÉÛŒŒ Öëèè 1BÞí ‡ššZÖçìÚË(„*`:‘ïS0t>C55µW/_9|$77G__ßÏÏoûŽ_åÝ.PjŠÞW`&„*PT\.÷ê•+„gÏŸBþüëOc##SSS6[ÞMû†¦¦&îƒÓ(z_€™ª€é½§———‡„ðW¬XAiß¾ýÆeÔ2€zGQú*Ì„PL'(T©ªª&&&Š^xÐW„*`:„$Ú¡¯ UJ! @ª€éðî ¡ ˜¡ €vxYHB0Þýh‡—€4 T(„*i@¨¦Ã»?(„*`´¬¬¬¯_¿J¶­ŽŽ½¨7ÐW„*`.O6;&6¶Ž5ܼq®öÔè«H BÔsí;t”wD"›ú*ÒƒPÌu?11(0ÅbI¶9—Ë]·~}?ÿþô¶ ÐWE!ãB0‹Åª¬¬”lÛŒŒ zP ¯ =*òn@-¸’ª^ÕæÍ›<Ù²?„1ª1 T¨¾ŠdÒÓÓåÝ|æB¨P:4öU®_¿>íûï=Zº[Z˜»º8Ïž=+33SöGÀ¸ýLÇ„Ï~s8¥š¢¬P6k£¢ ÆŽרqãW¯^nß¶->>þúõºººòn€¬áJ0]j~ÉOŸN?®¹›«¥…¹‹s³Ù³gådgóž]ºd‰«‹ó7zöèncmµ$4TÐJ!õ\¸p¡¡©ÉÇù÷;dð n]»Ð|ŽÄDãË*jíÚkü2|øð… mÚ´9=-íô©S²?(¹Ã•*`:¯TõìÙËÁÁzüþý{W7·1úiéië×­;nì¹sçy…óó ÿ´hÅÏ+œœJKJ­RO=¬­­÷FG¯ýßÿ¨Í_¿~}óæÍµkÿW¥12Fã˪ÊcŸ6m!>| «~‚PŠ*>þÖñãÇïÝ»›••e``СC‡9s昙™ ÙÄÁÁ÷ k·n]»u£·öñiѽ};¿ä§O]\]©•Néš5Q­}|øk¨¾RH=ªªªãÆß°a}øòåÔ­}{÷êêê2¤JcdLzwÕoÅÇBx¯#¥‚PL'èÝëÖ­_¾|6t˜•õ›7¯÷íÝwûÎS'O6hÐ@”j9ΆõëOŸ>•‘‘Áá”Q+_¾|Éûc ­­íݺu•­ª¯^ÏØqãÖ¬Y}ôè‘ &r8œ 6sRcIÐWá—››¶´E ÷ž={JµÌ„PL'(T………5mÚ„·èÒÌ90(èâ…‹ƒ‡ ¥ÚùóçyòøÄÉSM›6¥­¡ ¡ ˜NÄÁwîÜ!„8::‰R¾²²²¬¬LOO·æÌÙ3´M”z|}ýœ—……ݺ¿ió ö 3M›6á_±¯Âáp&N˜x;!áÈÑcîîîÒjãaJ`:Q>æ——·fÍg—Ž;ˆ2¥‚ŠŠJ‡ŽÛ·/55µ¬¬ìøñc{££%h›ˆõL˜øÝ­[ñ†††”`/´qþ¾Š —Uyyù”É“nܸ¾ÿÀooo™ ƒ TÓÕú¾_RRôµ¸xÍÚ(QB!dãÆMööÚ·kæäxøðá={¢%kž(õ 0€2bäHMML Œ@c_%$$øüùóÇÈþœ}æôiêß“'ePr‡Û ØÊÊ8?>{–¼k×îÆ‰¾¡¹¹yìþýük²>ÿ7ùç²ððeááU6©q¥ðz(W®\!„Œ?AôæHU­½ŽÒÒRª¯òëÎT_EPÉ{wïBöíÛ»oß^ÞÊI“&E®ú…®Ö( „*`:!ïæeeesçÌ}˜¸}ûvggçZÿNÈÞóçÏß¼y¹òçž={9::Ê»9"«¯róÏ¿dÓ*æC¨¦•ÊËËü1äï„¿·lÝâîîÎÀDEY¸`þ­[·¼¼Zý²zµ¼Ûð…î«0B(ªˆqq׆š—“{å÷ß©•7jÖÌY¾ ãwìø y7  ÝW`,„*`:Aoë>"„=vôè±£¼•#GŒX°p¡ŒZPï(D_€±ª€é…ª£GÖ¸}k€Z¡¯ UÀtI´C_@ª”B€4 TÓáÝB0Bíð²„*`:¼ûÐ/+i@¨P:UÒ€PL‡wPUÀhYYY_¿~•l[zPo ¯ UÀ\žlvLllk¸yã:]í¨ÐW„*`.//O//Ï:VbieCKcdàiÒciï}éA¨ñ´ïÐQÞM‰Œ;UJä~bbP` ‹Å’ls.—»nýú~þýém@ý€P \X,Vee¥dÛfddÐÛ€úDEÞ YãJªzU·†êæêbanfo×tø°¡·dDL€+U ¹ôŒt##ãÙ³0mhšõ)+:zÏ A/^ºÔ¢…»¼› kUJ‡Æyª†6tè0Þâ Áƒ[º·8|ø0B(!„*¥#½É?MLLÔÔÔ%³ Ðª”í¡ª¤¤¤¢¢"+ëÓúõë555ÆÒ[?€B@¨€Ü¿Ë–Í))ÏrssuttZ¶tŸ1c¦§gísð2˜Ÿnbbzðà!é7€qª”Ž +Uoß¾500œA¨ñ´ïÐQÞM‰Œ;UJä~bbP` ‹Å’ls.—»nýú~þýém@ý€P \X,Vee¥dÛfddÐÛ€úS*(®¤„W;qÂø†¦&³gÍ”ÍQ0 BÐàâÅ‹ýõ—†††¼ 7UJ‡ö+UEEE æÏ[²d©šF•€òB¨P:´‡ªŸ^… ¤ò IDATaem=& @–GÀ4èR(ZGG‰åÁƒÑ{¢ÿýw‰?TP?àJÔà‡f;;;/\¸@x±ŠŠŠ¹sæ|7é;W77Ù4 €±p¥ @éÔz¥*.îÚíÛ·54Ô ·–ÂÛ¶mýü9kþüZ²€2À•*¥#|àTQQQÄòð¹sƒUUոߖ­RÏÇWEFΟ¿ ¢¢"?????ŸÂá”åçç———ËãÈä ¡ ¾±~ýzsKË!C†ÔZ2##£¸¸8((ÐÁÞŽú÷õë×ãÇ9ØÛݸŽï¥ƒÛJGȽ'II:|èà¿E…nÖ¬ÙÉS§ù׌>Ì×Ï/00ÈÕÕ•¦Æ( „*¥#('UTT„-Y:jÔHG''ª WhÓÓÓóóóã_£ªªjnfVe%€’@¨P:‚rÒ¾}û²s²g̘ÁW@ØôTÀ¡ !$+ëóæÍ›.\XYYùåËje§üË—/ÚÚÚ"N•ž–Žo\å…êJ§Æý½{ÿ®¤¤déÒ¥¾ÿ*..>Ἧ¯ïß·þ>£:\©PB5Æ#»¦v»víâ_3}úôV­ZMš4ÉÑщ  VU@!ºº ZµjÅ¿FUUÕÔÔ´ÊJ¡ @éˆsÙ wýD…P tDÌIñññ¢„*¥ƒœ øô p¥ @éàJ€4 T(„*i@¨P:UÒ€1U4À•*¥ƒ+UÒ€P \²²²¾~ý*Ù¶:::ô6 >A¨P"žlvLllk¸yã:]í¨Oª”ˆ——§——g+±´²¡¥12ð4é±Ìö…ê4@¨ B ªh€P@„* TС €U4@¨ B ªh€P@„* TС €U4@¨ B ªh€P@„* TС €U4@¨ B ªh€P@„* TС €U4@¨ B ªh€P@5y7 vöv))Ïìííë^UnnnÝ+sçÎÛÙÛÉ» !„*`"ß¶m !çΗwCdÊÎÞŽúÏ/¥ÊÑW%$˾ B0”oÛ¶Òûë „ÐWå$Õ¾JUÊ}©B¨ÉÏ/¸s箼[ „*` .÷‹ÕYÞ­`.÷ZÝ+A_@fª€Ahù<è«T!Õ?4Uõú*2ƒÉ?h€P@1nÿý;qÜcéµê%L Ê@ŒP…‰ã@2²œx @^čމã€FÞ¿•whƒ1U4@¨¨]jêëÔÔ×XÄ¢.€èªDbiiiieƒE,*á"ˆ“ÔÎή)ÿß,bQy@t¬°°0 ï ÏÞ|åTB–4•w“À²“Ÿ !:šjÍ,õ2ïD‹q¥Š¿ïòþýûÆ/^¼8,,lΜ9[·nýðბ‘õlDDDdddaaa•ÇU„„„lÚ´ÉÑÑqýúõ...%%%M›6å/îÜ9ÿ€€€I“&egg‡††æææzzzž={–Ú|ûöí7ž1c†££ã±cÇvìØqäÈ‘¡C‡æää,]ºtïÞ½="„¨©©ÙØØ4oÞ\]]=,,ÌÂÂâãÇW®\Yºt©‰‰‰(­zøð¡ŸŸŸ§§gpp°®®îŽ;Nž<ïååET³ð­ªïÈÒÒÒÂÂbêÔ©¿üò Õ˜?Z[[¯Zµ*88XxmçÏŸïׯßðáçM›–••µhÑ¢¢¢"Þ¹ʇ÷oïÝ»?1±Ö’žl¶——§è5Kxû/&&¦¢¢bäÈ‘„‘#G®[·îСCÓ¦M·žÒÒÒmÛ¶ùùùÕølxx¸Ï¾}û¨ÅæÍ›;;;ó(,,ܰaC×®] !=zô¸~ý:ªŒ TTTš4iB•üúõkRRÒž={ @­éß¿¿è­š7ož¥¥ååË—µ´´!]ºtiݺuDDĉ'„Ô,d+A;6lØþýû###UTT! „Œ=ºÖÚ–-[æááqàÀ‹Eqttd³Ùµœ}åC%ªÐÅ‹k-¹<"‚"z®’p úÞ½{ÝÝÝ©ˆãããÓ´iÓ½{÷JPŽŽŽ¯¯oOß½{wàÀ¼5NNN®®®üetuu»téÂ[lÞ¼ù»wïíÈÕÕuÙ²e›6mzüø1—˽UçÚµkÆ £Ò !„ÅbùûûÿùçŸBj¾• Ã7nÜ»wïâââ¨Åß~û­k×®–––Âk£ÎÕ!C¨DEñððprrrŒÊIÄDE 3F” Z<’„ª»wï>}úÔßß?ï_ýû÷ÿû￟?.nUzzz¼PE~~~ee¥©é7c¼ª,6hЀs55µòòrAû:þ|ÇŽÃÃÃÝÝÝ­¬¬V­Z%(ZUiUnnnYYÙêÕ«µø,_¾<;;[H͵nUãá·k×®I“&¿ýö!$99ùþýûãÆ«µ Ô¹²°°à¯ÊÒÒRЩPr"·NInÿQ¥V¬X±bÅ þõûö틈ˆ ÂQ÷ï>þÌ¿2;;[WWW² mmm£££ !ÉÉÉ»víZ°`••ÕØ±cEi‰ªªjPPФI“D¯yذa·ª‹Å X·nÝÖ­[ûí7]]ÝAƒÕÚê\åççó¯ÌÏÏ—ø\Ôod¦Z‰}¥ŠÃá8pÀÇÇçÚ·<<<~ûí7á·ÕÄ¢­­ÝªU«“'OòÖ<þ<))IÄÍ555ËÊÊj|ÊÅÅeÍš5ÚÚÚOž<¥*--­N:]»vÍÁÁÁù[Bj}«*ÆŽ[XXxüøñØØØÁƒëèèÔÚê\]¹r…WɇD<:%T墔®T;w.;;;**ªS§Nüë¿ÿþûéÓ§ÿñÇ;w·NA–,Yâïï?~üxÞ§ÿÌÍÍ©ܵrssûúõëÖ­[[µj¥©©i``ðÝwß 6ÌÙÙ¹¼¼üèÑ£ÅÅÅݺu±%QQQíÚµëܹóôéÓ­­­óòònß¾]YY¹råÊ´´4A5 ÙJȾœœœ|||,XðîÝ;êÞ_­m „,]º´_¿~ÿûßÿf̘‘••5aÂMMMÞ¶—/_îӧϾ}û¨1ïJŽ?3ÙÙÙBlmmSSSëR§ØWªöîÝ«§§7lذ*ëG¥­­-ÙpuAúöí{àÀ„„„îÝ»ÿôÓOaaaööö¢l;`À€©S§†††úøøôéÓÇÐÐÐÆÆ&**ªGþþþ÷îÝ;tèP÷îÝElIË–-ïܹcmmÔµk×)S¦ܾ}»¾¾~§N†Ú¦MÞ¶••••••"/@ýÆ»E%*Š]]®T‰1ù§ÜçØÍÉÉiܸñòåËçÌ™#ß–€‚ _ºxqNN!¤Æ19)))Ôƒüüü˜ØØ)“Ž–|òOÙ+,,\²dI×®]MMMÓÒÒV­ZÕ Aþ;b .DÕ8V[â1ìŒUjjjÏŸ?‰‰ÉÎÎ600èÔ©Óþýû«Ï i|úÑ¡JKK ß²´SºP U4`ÄäŸ íõë×Ò¨Wª@‰x²Ù1±±¢½f„*Pl_¾|ÉÌÌd±XEEEêêêòn(#ÉCUBBÂÚµkoÞ¼ùùóg}}}ooïÉ“'2„ÆÆñÄÅÅݾ}{Á‚¼5‘‘‘………ÒÞQHHHLLLff&]»¸víZTTÔßÿýåËÿE‹™™™ÑU¿BàÿõU?ç¢[¸páš5kÊËËÍÍÍ333mmm§M›F}«·”þ‡€¢»Ÿ˜ºx±ˆ…—GDxyyŠXXÂ1U›6mòõõMJJ Љ‰Y¹r¥Áˆ#¤4B\\\dd$ÿ333WWWìˆ^k×®íÒ¥ËçÏŸ#""öïß°oß>6›ýìÙ3éí”á$>çiii‘‘‘_¾|IKK#„´iÓÆÖÖ–î@=ôUâ½’äJÕ­[· tðàA5µj˜2eÊ£GÊÊʪ—/--åÿr_ZL:uêÔ©ôÖ)mñññ?þøã°aÃ>>ÚÚÚÁÁÁTGõïßßÐÐP[[ÛÏÏïæÍ›¼m?~iÒ$CCÃׯ_¯\¹ràÀþù'!dÉ’%{÷î}ôè!¤J’ãí.88xÆ NNN‡ž>}º££ãСC !çÎ9rd@@À¶mÛ²³³/^œ››ëéYÃýQA;***9räŒ3~úé§cÇŽ-Z´ˆW¹ðƒâÇår©óVýkv 4wîܸ¸¸>}úÔzòkÝc•3߸qãFmß¾}ÇŽT—/_ÆÅÅñëø‹0`€ººztt´……Åǯ\¹Âápj= žϹ(u®Y³¦wïÞãÇß¿?ïë0Û¶m[ZZ*ÁIe#+Ub‡ªüüü‚‚‚¦M› /VZZºmÛ6???ÞšyóæYZZ^¾|YKK‹Ò¥K—Ö­[GDDœ8q‚Ò»wïÞ½{S%ýüüØlvóæÍ?~Ü¢E ccc•&MšÙÝÎ;ÝÝÝ !Ó¦MÛµkב#G¨Üîãã³oß>ªdóæÍküJjBˆ nذ¡k×®„=z\¿~W¹ðƒâWPPPPP`ggW}¿7f±Xééé‚OçjÝcõ3?uêÔU«VEEEéééBvìØ¡§§7jÔ¨ëë÷õëפ¤¤={ö 0€*Ü¿QŽ‚§ú9±NkkëÆBš4iÂû…ªªªÖ¸ÑM  lž*___Þ"‡Ã¹víZHHõWÂb±üýý7nÜÈ+yäÈ‘´´4Þe‰gÏžµhÑB”ÝR‰ŠÒ´iÓwïÞBŠ‹‹ïÞ½ûóÏ?óžrrrw„»®®n—.]x‹Í›7§*¯õ Ä"ÊÔ®¢ì±Ê™'„L™2%<<<66vÚ´i':::  Aƒ5îB¬_œŽŽŽ««ë²eË ;vìØ¼ys‹%æq×Ðzë¤÷׊NzóT‰=¦ÊÀÀ@__¿Öˆ§§§Çÿ·077·¬¬lõêÕZ|–/_žM˜9sæš5k&Ož|åÊ•ÄÄĸ¸8BuûIUÆS«¨¨P·Bóóó+++MMMùŸ­²X« ð‹ššUy­ÅO___OO¯Æó–žžÎår©ë.‰²Ç*gžbnn>xðàíÛ·BŽ;–••5mÚ4A»÷wþüùŽ;†‡‡»»»[YY­ZµŠËåÖz ÂÑ[§X¿&¨÷¼¼½S§N Û·ooÛ¶­ˆ×ÿDÙ£­­mtt4!$99y×®] ,°²²;v¬£¨• :EÜ\ÜC¥rïÞ}§ªZA‘îkÖ,##£áÇ /)ú1¦¥¥}÷ÝwÆ svv.//?zôhqq1uÉMøQð«rÎ Õ)1á'íòåË}úôÙ·oßèÑ£k]€úA”¡Ìâ’p úìÙ³½½½×®]»víÚÏŸ?´nÝúرc}ûö´IË–-ïܹDÝóööž5kõìž={¦Nêî¡áççwôèÑvíÚñ¶0`ÀÔ©SCCCsrr¬¬¬Þ¾}+zSûöí{àÀ¥K—<<|ݺu5wGªºyóæyyyEEEýøã999„Ë—/7lØW†ÍfGGG‡……EFF:88„††òß8w<Æ ›5kÖøñãyãµE$d†††666QQQiiiªªªÍ›7?tèP÷îÝk= ~UÎyRR’ :«ÐÓÓóòòÒÑÑá­ñðð°¶¶ë!•••¼‹j ~F¨b………YxOxö¾à+§‚²t ÀAÜ–V6‚žR 9997^¾|ùœ9säÝ2uêÔ]»v;v¬ú¬˜´Û»wï„ ’““qÛ ”Yxø²ÐÅ‹‹‹‹ùWÚÚÚR_ÔÁ/333&6vÊdCr—üLÑÑTkf©—y'ZZS*0Gaaá’%Kºvíjjjš––¶jÕª Œ7NÞí"„-[¶¤¥¥3æúõë5NqN‹äääW¯^…††úûû#Qo¯TQ³HÚÚÚ¦¦¦Ö¥ÎúªÔÔÔž?“m``ЩS§ýû÷WŸÖ\.ÔÔÔ.]º$í½Ìž=ûÆmÚ´Ù²e‹´÷ x¡ÊÑÑ‘·ÒÎÎîÅ‹×YÿC•–––’Ïî•+WäÝf¡BUõ8ŽŽŽ)))’ÕYÿC@T¨ªq–%‰Ç°#T€ÒaД Š ¡ €U4F¨’ä»ÿ(×®]ëׯŸ©©©¦¦¦½½}PPЧOŸø lÚ´‰Åbñ¡¿ÿ¾‰‰IË–-³²²¨5³fÍb±X·@\¯_¿–Fµ^©Z»vmpp°ODDDÆ =z´qãÆ#GŽÄÅÅ5kÖ¬ÆMzõêåààpéÒ%ccã:´@BžlvLl¬è…E¯Y’+Uñññ?þøã°aÃâãã§M›6dÈe˖ݹs§´´tøðá5~KÚÍ›7»wïîêêzõêU$*—û‰‰R*,É•ªÕ«WkhhlݺUEå¿Lfoo¿xñâ9sæ\¸p¡Ê×*ÇÅÅùûûûøøœ>}ZWWW‚=Ð%tñbK.ˆ½Z±¯Tq¹Ü¸¸¸Ž;Vÿ¦ê[ãââøW^¼x±oß¾íÛ·?wî( q‡^‰}¥ª      iÓ¦ÕŸjܸ1‹ÅÊÈÈà_9gÎ{{ûS§NijjVßDCCC]]]Ü6Ô³>ý'¢¾}û¾zõjåÊ•5>«§§§¯¯/í6ð«¸uŠ}¥J___OO¯Æ bééé\.·Q£Fü+W¯^meeµlÙ2mmíùóçWÙÄÌÌÌÆÆFÜ6Ô•™lmm«?•––&Yb_©b±X]ºt¹qãFNNN•§N:EéÒ¥K•òÛ·o7nÜ‚ Ö­[We“™3g>xð@Ü6Ôu-*55µÊúÔÔT‰¯TIrû/$$¤¤¤dæÌ™ü³'¤¦¦FDD´hÑ¢wïÞUʳX¬Ý»w>|Μ9Û¶m“`4â%§/^ðV¾xñB¦·ÿ!íÚµ‹ŒŒœ?~ZZÚ„ LMM©É?544:Ä?Ϫªjll,‡Ã™1c†¦¦æÄ‰©õ›7o.//— ’áÏL)))ÎÎÎ)))u½.áŒêóæÍóòòŠŠŠZ¸paaa¡µµu@@ÀÂ… -,,îIMíСCƒ š\ÓÃ)öZkllœ={ŽH³m›ûYjµÚh4v{<b*ª8nºXÂç³ÙìK—.–•íH›%®ØWÁss³9‹ÅJœ–˜8-ñÖ­[++J+ÿõÙg^^^ S&O~nàsdÏ£55£"Ln–l *,,4íòõõ™:5‘út›;jZ­vÙ²¥~~/@||ü… mfÖ&2r&“i¯ÑcF3ÚôuŸ?t¨RRRcc£¯¯¯©ñ¡œTÞþCõæü… ±±±ÄÝf</4,ôô™ÓdADFÝ{Ø-6n¼\.ÿù§Fóq´ZmÝ©º âL?ü€F£EFFÕÿhµš3õõ“&M4UTƒƒÃðÏHµ×&6›Fnúøúü~ó¦éëÀÀa¹y¹ãÆÅ„‡œ3gŽd“¤µµõÓÝŸ€^¯Wš±“Çãe¤§WT|¶ÿþ¸¸Øýû÷ÇOž\¸²ü/|{üxLt4tvv^¸x1:æÞ3qÞÞÞXžäŽƒÁ$«[ˆ·Î¬Mæ‘wÕaÓ¦Í/¾øbxx„P:cF \½zõÞÔ|R!d‚WªB}…B¡4 ®ýž1o|ƵßåK—ÈMG.×üú‹k?Ëãhuº;wíÚuoqŒÑhÐëõ P¨ôz½›ïAButdÓh´ÏHµ×&ËÁ||:nèb9TP` ——×…óçàü… i³îÝ´:{öG›Q)*¥ª]­¦Óé,ËÔxê䩎ŽÎÈÈHP*UƒÁÕÅÕüS.÷oZnsGÍ‘ãh¾,‰ëìV™µÉlYk‘ƒU¾ ƒN§säܫު««-yð“ =mîVT–ðJB¨y#+ëõ×__¸`QÊÌêŽM›61™ÌŒÙd‹µ~ý…Báíí-“ÕJ¥Ò7ß̳^Ëü曹bqÆ+¯¾:㥗ÜÝÝ•JÕ¹óçŒã¢E  '7'==cöìÙb±ØÇkúåúå†+Ë–.€!C†ttt|¶wï°€aöL†¯ŸŸu‹ÍÈ©g¤ÞûÝw'²²² WÆÇÇw{ˆ/Êvïïàooïpùòå½{÷>ûì³³î^†±V_¦TR:>667/O$šß³#=wîVssŒÙ"ªy¯Ï[0aþ;ùÓ“¦·µÉKJŠŸy¦ò­Ô¹‹‹¿}Ûv‰D"‹[ZZVw¥4a±X?þXu[5xÐàê£G«ªªÈÌFS%t¥á ûöx?WWž›[Ÿo¹u¾FŒñù¾Ïc¢£xxT9RQQaùC9©(‚¤8hèqDVTB¡@zoÿ!„úªðð‘¥¥¥‰${qƒÁ E«WhþL>›ÍZU´ªèƒU—¯4ôëçš›—kþè;Y1øù½°gÏ?K%’¢¢ ¹““sà°a/¿|ç=Iþþþå廊‹%+W¬Twt 0 1ñΣmÑÑQÉÉÉ%Å%r¹œÇãUW±n±9õŒÔ{ƒ^¯'¯âPŠ••‡+++5››[BBÂo¼A^s²&à jkkÉÛj6Éjdþýû÷'[ÆŽ»º¨¨¤´ôÐáÞž³²²ÊwÊ5»[g:wC‡ú¯X¹B"‘”m/óôòœ7ož¦Scþq6›UTTd3³z½>77ìiZ\?vÌØâ’­#·Î׊+Þ[þ^RÒ‹L&#„Ï_·v­8=ÃbjŠ“ŠDDŠ )zì˜WTÖkªhË—/64ãʯŠvþ1ÍöC¹!ô÷Ùºm{櫯<à }´ñ‹/öËd²î»¢ûMš8eÊ”¹s3»ê —Ëãâ&ÌŸŸÕÃGýYk׬•”þ…Üu9B=ÑÃoA•ô«Òë^À¶'^Àý­n'^©B=ö Ź³çª««ƒ{;–ÇÒ_Z´´··—””Žæââú¿_-+ÛÁb±z%< Ö‘#ô7±®¨¬û`Q…zì}ÿÃ÷9‹s—,Éë¾7ê‚ ®]½&•”ËÛ8Nh¨¨¨h•‹‹KoÇ…Pï°YQáš*„Ð(rlä™ú3½Å…Éd>ÊÅ@¹y¹¹y¹l:„þ”®**ë5UøJ„B!Û(**Ó×:šìŒEB!„ =©¨n^=¯ÓÜyA?¡ëî¥v!ôwðù[·mïí(BO)­×‰u[Q™ÖT¹ "˜ö Óéü»E¡^' „BA÷ýBè‘èIE5Àc Ns`Þùšz½žöֻﻼCZ‘bt„B!:š`²L÷þܳX¬ç8†Öˇì¦MŠnn”iµ=z“/B!„ÐÓìÎ:*m'Ád¹ "öZ¾¥¡&~\„`)/‚¯T!„BQ!k) ˜F–üœ‘ÎÓÓ#†ÚiÚ¸Íÿf¨oÐôênC!„zŠL éÕ õ îM]w;ZàN'"Šï¥R©N|_GSÝ´ñÌB!„Ðýì8¼à 6›MNÿ?…+Ë˯ññvIEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/objecttree.dia000066400000000000000000000067311231437614300240140ustar00rootroot00000000000000‹í]]oã6}Ÿ_a¸3 ¸ŒH‰úhš)f»èî»`Ð>…!ÛŒ£YòJò¤~Ùß¾”ä|ز¬O*Žs Ì$v(]™—<¼çR÷ÇŸþ\ù£o"н0¸S¢G"˜‡ /XÞŒûõ—ïíñOßý¸ðÜä¿eä®Fòˆ NßÝŒï’dýÃÕÕýý=ñ·±›„ñ½ ‰ÅÕÿ\ßw¯d£«ñÇw£Ñó,ÜÄM?Û}ê&IäÍ6‰îJÜŒgîüë2 7Ábœ·Úµ›‡~¾¹þÍø»Ûì5¾Úæjï<'νv—b ÷kù©5ùrœ6§^‹èð´«u{²I²]š”œ'ýÿY›]«X6 –¿ûd|—_Òs»ÐRdåFK/(âÈ{ãç7‚›1&owlG³ ‹>Ü“æp³aáüaá¢aá¼xº£$r½¤9 C_¸AŽšDÑ'ž»¾ìb§Ìê`Å­—$aÅõߺ~\Ç€üãGokê¹ËÈ[œvܽ%g¹÷ÉÝôOE·+?ûVÑÙ¿y±7óű«÷‚¤·Óoû9ýá·“ÞO‡wì ùÙJ§‡…-¸àͧ‡åÆ[ˆ¸¢›í·)9ÓÝ®ÙUÕ]?lW÷ÆäLÖ„ïnE´;ý/aG»¯v7Ö<³?œýGÌ“a¿ýû_£ïG?ûnœò@6$Õð7ãÏÚþ]9´CžIx…Û²ŸúŽF4ÊøD#ÔdüÐàâpQ†2›‚DÒ7Xú"rˆcæ8îZç9¬IlÚVøb5‡QP$=˜!eÃHÑÈÇiÊ Žá<uÀºÞò.)3‰©í½Zv@˜öèÒ­ç‹ÏYOüð‹üõ/GÉS°8‘Ó^]ÙB:èJ‰²ó»3y¬;/œšzÝ¢Íz‰8ž>þ!VJbí&^:$)‚z˜ÃjµO½ÚÕ4©;Ю¿)»s÷‘»à Ê`Ü[éŸÓù[MÇRCkwþÝmšJN,¦¾–Å´7ŒÄ].÷©w¯·*3aŸç”,„µ–†ÈqׯFØ_Å7CHÄŸ‰Z‚P.ìüémX‹ÓÏF·îÊó·7ãU„ñÚ‹ñ(N¶)’Ä&?ÅÏá&òDÔÿaœns¶}p ßÿ5ôŸg¾÷ß¶ÃÅ:ô·«0ZßyóVWtxA.fžRÉô×êK‰Ý”§>\ÅãWóá‰7w³;Óñj}9öñËévovƒHÃK)^I§«xæ=•LP#ö>ÔŒ~úèpÈ{Î:ì~ǫĥ½ösUpÏ;°2“JiÜá¢|³òã•ÑÛöžþ½$‚{"ìQ6î/C `ùÏÞàäýÞœÄç‹þ$‚ÜñqoÀÅQ½UQ+#SVìù^²='‚ÍjçÂÏã/ÍaŽ,ÂjÆ@[ܾÔk§ñ<<Þ[ZÃ' äGQ&ƒùÑߣp³ÎRÛ2Èâ…»{á\ÎÉè×=ŽFØÔuÔ}·ÒÓ‡tA/¸‘—¸Á\L˾¸§›ÈºÉ¥O´}ÍKi¢B$J/‹¡08h_Ð(ô¾{ù=–óà «šørCn‹|lN¯ïrmQ»¹zܯ^°(-Ã*ÁJéäÀ}óäMe׬X®¡ožeßìoyÔ;1ûÕùâR‰Y¿Æ˜˜˜˜aò11CßlÚ7"žGÞ:eo/ÒEÿÿMá §¾Õ%ħ(r·—º„è×8,!°„ÀKL~XB` ¾Ù´o†§³=TöN÷$BçĪ¡öªÁã‹•°TÀRá— W½&£%bµöÝD¨Ú½ôpþxv>}¶ÛL;ï@¶ˆÆ'T#*wS+E0ø5³ˆ%Ñ8ÑÕî:îhTÇòì&W¸ÉØ8Üiaõ¿Éø _÷Cö;¶c«ñ…m5îÁ$ì5Æ^cì5Æ^cì5Æ^cì5Æ^cì5V¸GrúmzÙÛ¯°Å[Œ•¹Oú Ú5½ÔMÆË§€…Á‹”z{^ÄàEð"U^” ôôµ&FVyQfæ"<í¢ñÓ.®.M‰d]•HN cÂt¢q¥OBæDw2ñkj*A-B5µÏAînÜYh’ýKÏÙ<ôGèС?B„þýú#ôGèС?Bì[Ì"ˆü*ó¡$}Ê}­Õªò Ì:~áECÈ'ì È'!!Ÿ¼eùDï*Ÿ0“,“¨¥R?‘8º5aŒ8¦Å¯u‹P‰jfZJ”Ì;E'lIÉ”J* ’ $H*T ©@R¤I’ $H*T ©(‘T$ƒÕJ*ì H*#ü–ƒÁF×`°ÎˆiLtNlÇrƒ%w2Ãr® pcbPbmQëƒ{0¯N0Ø’f9Ï_¦ÂÈ0äy_÷qá/á=¢Âˆ _XT¸;‚ #(Œ 0‚ #(Œ 0‚ Z¿¿÷Ò6/Ù¾ÿãb#Èe!"¬Òäw1Ñ€$‰=5á?ðŸKðŸx-ÄbXçÑœÎóú'ˆÂû!ýÆ4à7ð›s -Tæ®×"X RÙy8*•¡RYy•ÊsxçœJ4gÂl µ9 ÇHq Çr®u‡8ÎDgÄrç4t7¯NNƒ=dNƒ1Àn·çic²”5ä5 ¯y Èk@^ò×€¼ä5 ¯y Èk02~Ñ;ÝòmHØéiI™ ©,}é«Òyžv`À} 0©Â$ç݋՗’~7µBd‚Èt>"Ó´##Åãíëõ{_…AMÜX=è±Ù[=êqçVûÕ e ¥#W%X)kT;U¾BAÔìA5tÆ'Ô Ž®sµ’¨nçH¶­ót£·ž!Ûòs+–E»yv¨NtgÿÕ¿0ú¼6å‡ìÁÎF!ŒB…0 aÂ(„Q£F!ŒB…0 a´a4[q¡ª”(;/ ì¸ýÖ®‚²eÛ‡–ŠãpîåÃÝSÌœîbæVç*YŒ¶Å'&±L¦´L–Òs Êøµ|k:J‰c·E’»ifIûÊ[ÚÄÒ¹F«š:õRè¬ å ¹¸x{.Š×P2—׈,o’0 7‰ÂÔ©8~çˆ÷ÂKûæ>(¿k´Ž`QN`*‡·HÞ»A&¾ÕÆO¼µïÍKfóþgÚ( !Üå2K7JIJß\¨ff!::Po+°yù(úÛÃ_Fwn°ØE°“PÒ ùóé¯7ã‚"\8i_<ÇîÌs,bM¨N˜JŠc“O(#:¿ÖM¢qÉ&¨Ixû\„z§hZy#í$JŸ Ÿ]uí†R-ÀmÀm05Û ¡½8·¡û܆'ž#ÓÝÁæÁÁƒ#§‡P–¡œè ©ÕˆE3”<ö“rfHºÄTGŠÆ•6c”húIÚ“ša6i˜ò(zz„Ù ³è:Ð¥Ð#³ ;b/ÅŽ¨ö:èQŠÂè‘N‰cäôˆZgFN…ÒB‡ ÚIrÔÍ@##Ìm Gè@è@¯–é/FŽhn³‰Ü$¬ýD^s¿­éd@Ô´$CâY4I×mÿèãZ ©ÌÂò–©Î8­ß¬ÓÃA‘@‘0Ã"¡¡½8EÒ;P$þb‰u¦HùDžÖKP\¥Ã̬̀Lƒ™ Pº•UmPJ‘J,,oiœ7ÉKçfýfœØ((f8Ìp Hè@¯—"ñÉx1Фw¥H¹2V—\™ÚIHŽÆøµ|ç˜YÉ4M¦’!•XÞÒ®Pвvµ›9Dëf!&80$t t eH¬C¢/–£M^(ÒD× k?‘×gH)5Yδ,ˆd3j(§HEK¦…4)­l¨ñFMMº٠¢¢„yD èe’Ì.LÉz1¦Ô¹(:5³ªáœXŠcIfV_3p3¢Ô m%¦lx2­['ºY·™¤Q"˜&6Ll`Fè@—Bj¶ÍŸÚê˜QYY$Ú¹.’Itw”êp"“0;ÇÉ¢Gœ0 «Kú@ÛÂÖ+‰Ôƒ}gW)Zí½Xÿ,$+ýøPé×ô "¡  "¡  "¡  "¡  "¡  "¡ ÒÑX "5.ˆ”­¸PiÔlõ¥,R6pA¤A( ïóŸ/=bU:4l«€û¼I÷A=1µ=´ (›¼POì<|õÄú­'víÈHñxÀAûú£EýÞWaP7VzlöVzܹÕã~õ‚EhéÈU VÊÕN•g\û®TµºK¢¶Î9ë˜ò]Oµìµ¨qM´lŸŒhV[人hFÖQF­!•Q~²úWFŸÖäª(TQ¨¢PE¡ŠB…* Uª(TQ¨¢PE‡u~ï-¤m^²}ÿÇŪ£u Šªt ù]ÌD4 IbOMøüçü'^ ±ÐynýÐMt÷û¼~÷ †ÌʑӎiÀoà7o>Ç]¯Ep±ù8HÅA*Î%¦â\½ùô»óSplâè¼ÓC[j=Ç&¶Ã×ÌJÓ Ò‡šJ³ºÛvv›½u¢;û¯þSÜ(r·yRûð)}ƒ´¤5 ­i Hk@ZÒÖ€´¤5 ­i ØìÝÏfïlÅŰÙñqìV>:žyBä‘#D~ö!òü½ïnEôñ]þFþ[Js?¾û?m-¡×ŒPyTables-v.3.1.1/doc/source/usersguide/images/objecttree.pdf000066400000000000000000000531661231437614300240340ustar00rootroot00000000000000%PDF-1.5 %µí®û 3 0 obj << /Length 4 0 R /Filter /FlateDecode >> stream xœ½YÛn7}×Wy‰ŒF ‡wMm(‚¤z‹‚@¶džn•åþûž¡–«XZÛÒÊ©[Ôh9Cž¹ÒÿtHðkq!^•¸¸î(Á¯'BËh¬·^ÜBö'~¯:Ÿ> %•8ïXñ—øÓøÑ¿%%#²AœM:dµTÑ +½r"¨ 5™$}Œb1ïćµáÚâN­ÔÉÔ*I¥MQ¹Ò•ïúr2åŸ,["ë¥1Ñ+:HèO‰DÒyýµ§zxTô¿v>uš'£ã#Ó×ËÅåƒ ñF¼˜apz5:[.1XŒFò›ÃàÅÑç>CÔ#i)¬¨Î:³Ù2ë8YÌnæèÞ~-Xt|R‰^1t×È ŽŽF¼çN3VkÕËáé˜;©~u>º>c_\Η—³é Ãßþqýë胳,e»S^B#è=à­õ †‹Åƒ»ÝV0ƒ ÙØò˜çåܱ¹·+•É àQ­2Ô~åÓË ­ÕQØQŠƒszOŸ–ÊAùv™s4£Û2–ãÐ]VÉû¤o‹sôãÎÑíœãQd)Â9@Ô»gqNQ™4œk•û÷6O€ÂÞAIˆ)˜¦*¾‹wô¦w܆wôãÞi oˆúÄmˆêPZk‰;ßµN‡éuÑ™KZÚàƒ 6*8äëóZ¥€?½¼3Ћú°å9ÜOn&§£E5çrʸ’ߘë)×s63:‰‡³ö¥Ù¬YEÿt1»Í\y{ wµÉ£THê(m‘ŒÒF‹`mr8Ÿ”óöM2 tœK"ª$‘NÚ)}8¨GÖ*m” ™[tÁ-RÈ.£•LZ[w&£%é¶rU×üó6ÇÅÇÙ-žÏÝí„“C¦}†–ͧ$‡@µù¶åæu@í‹Î6’NÐ2øŸ™HÕ²ª9-oAXmÀ˜K0˜ÃªK¥ÒkÉœ¹¨l‘È)G)(fòÈ «mhê­Í±Pú[ÕU[#“(»ñY¶î'( o¢}÷¼:¬uYûNK¶8÷àÌ#köh˜Q$@.Ÿ<Ç+I¤&I²¹ü;âolíÑ@ÍÍ>€ÆÂ'QK|⤅ڈï<³Þ-I ¶ Ù”Ë)zxžÉʇVçŒA¿`gKPÈâø ŒâŒ’”Û’Âzž¶Rfpޝ «%õ#¯=z‹ Ñ ’ªV?m¦LHˆSÌÔT–@) ã ’šdtž¶T€Çc$Ȥ†>  ˆŠ3t ’ˆÈQñt ”) ‡û+;Y€ÈB*VJÑ.·(¥ ç»¸Ç‚étÊhMÄ=IŽ_‡†Ä'¶m Æi®ìfÏfÈøDî•à¶gÖJ%`ðàcEåþí˜ïS’u(] Ñ2åPLöíÆ›çr»tã–gÅjë ÞóÆP¶×[×`É6çÌíŨ%ÊP´Ï@ôkÈQkl{žïP]£¶`5¤äÊ+Ôÿæë‡hþ×ñl¸4|û\¿€I 6‘¹ÒwT/š·3¡â½)®žèT]¥b]žƒé„?2:}Û5àËÚ¥ˆFǺ˜TÜêz§[Ò»õFP1(¹ûÙ«R|¿ƒf«“Ú“à¡„µæõ'«ØsšÐìvÜkœN¥SVªVJÑ/ý«•°¶ÇwWG½\LÝß/Çy\S1­2ÀÆJà®\u•Ç7ô' ~tuï×[Ý^ º|-Øã{ÁJ!Ь&|z^*«•¦•Þ„ƒT÷.ßæóÇ% ºoùY'¾ ^-‡fU 5‘O®:ÉçKÌš¯· ónæü±ÞD”.Q` kNxPm+¢.‚õ"‡€C…Ì“ó};OZý/ûG¤c·ˆëÙÞQDIè%Ôk°@x{Ï*Ú[Á eX[â./ª:(ÑcémÈ Á{  ^Â×YDºqÉú%ó¿G4Zxãí]hX²þ~ÉH¬ÿÙ ¢ endstream endobj 4 0 obj 1708 endobj 2 0 obj << /ExtGState << /a0 << /CA 1 /ca 1 >> >> /Font << /f-0-0 5 0 R /f-1-1 6 0 R /f-1-0 7 0 R >> >> endobj 8 0 obj << /Type /Page /Parent 1 0 R /MediaBox [ 0 0 1020.472473 1190.551147 ] /Contents 3 0 R /Group << /Type /Group /S /Transparency /I true /CS /DeviceRGB >> /Resources 2 0 R >> endobj 9 0 obj << /Length 10 0 R /Filter /FlateDecode /Length1 10216 >> stream xœåzy|U¶ÿ½uêVïÝUîìétÒt–…‚,MXÃ&ÛH@`‚D4€"" “ Œ ˜d42ˆ^1‚I؇Aˆ"è O\À–ÇÏaQè\Þ©ê—™÷~üþxŸÏûuqnÝ[˽gýžs+J1’Ä=­hêÜÏxõ,!1¿'D˜8í™§Ý—«tfBâ>Çq挹=,<~—Œomô‰…3ί÷«}B¢ÏΜ>µðö·Qíñþ>¼Öm&^Ð7Ù8¾Žãv3‹ž~öÚ‡í.’ã£OÌ™6•‹¶àç''Ц>;—S qã|Ä=÷©és{9 Ãî BtqD 3y¥8“mAnu$q©”¨£žêÙ2A$-g]ˆ|&p&¡$)Þ$%i¦H‚ó .ø%¯ÔYøþ)©¾C(É'OŠ ÅuÄDRýòšÖ°×t`bE¯5JfùL¯@°—6ŸÖɤJ’3Iñ(IÙIа¾Ëûoãè>qP«vjé>œ·™1N\L"Hg´Á ¨T®4™© ‘¡ƒdä¯|Aœ:£×¥ë%*''÷(]S<É’;YÎ>4Ë)Æí™õäÒ²††.Ûæ½õ¦Pß6T¨¯za÷[m+Å‚mÓ.¢Y~÷ уë™H2ÛßѨ#±ns´MGê£uåö¤2÷þ„òvMöêh c¬(Ø·(9ûtDZ‚ŸOÉÉAFZ.]^ÈWå«JŽ’cÏQì9™þ„ÌÄLwfRfr ©¡5B±Æ´9²&ª&º&¦&Ö:™ªì:"³|ݺgwW<ÙV¼’ݵÛ4;]½§ËÖĈrQ¡©÷kZôĆtÏžÞ.yëÄÜ¢+ÖM9ððŒÆü²C½SÜBÖ“s§Ï=½·Ãð¶¥[ ûÞæÆƒ +vëÚš:fŒo]ÈfE¨Û9(k2ù“?5Ún3ˆ:’'éœær74ÅŒ‘uD±éGH#•¶‘ñÑ#bzäëÃêÌã†Õ)ãÎßCbïè1!Ø+PBÕh^Ÿ*t"Ó?8SÌd™R¦.SŸiÈ4fšúFöêÝ7¦ol߸¾ñ}úºJ D,a%R‰®D_b(1–˜*"+¢*¢+b*b+â*â+*\:™jÒÇПö×¶$?6tåœmÙƒF=°-gh^Îo$Më;|:\2ð¿Ð¶@XúݼÅ_¶ K¯ÍUÏbAA¯¾ƒT»/F»ç£.œ$ž ô§H Æ2Ã)²ýÍBß‹)h²T'€/"%’o—‡$ Íƒ”E—Ñõ.©—¯_E{£ :“ÂÖt¢%ÝD‘I–Ï® ™RÌ~±Gþ¢Çšæwø9ê¾öÑ­qméòí²0e¢´ûhœÝ:ÑAÍÔÏÿ~hó›u›ˆ@1Þ…/1ªÕ˜ó[„Íd»¸Yb4A$z5²|h„ÀuŒ®,U-Ї^âOLå§x?²{+Ê*¡¬©d¾ßo1 VST¢KotÆ(W¢+7Ám4¹E')£DG™ó@t¹"–{›”êö FSbœŽ<'Yót’#y`{ùz *á’ª„{jù«ò«ö(Íí‡Í×ÉÖïÐtZ;!™:ÓþžH]ÔéÐÏS³]hÁnÙ]3„tŠNÖ„¬»dñ¤ÝCW­ |8¶~Ö£ûÆ-ZqC?ðÕ?œ{âV1gWzúè±Ã†z¬±›–lmôxš²³§M(é"X׿öoIš¬ËÐÇõìÄ.þh+ÓÛ ž(ô ¾Þ¨7ã$Ùnu âZ‚½Z|Zè®÷jQ=8'ŒPŽÈ¨SÄRÍ ›ù”)óO^?ÆÿƒË»™@~öò‡@Æ®#ª‡?j,×Óz[¬/ÃA}³Ý¤ ‚M”¬Ä®a`KK’5'ñÅ´ðuHh6r†ñãòy3½ñöëüÊĉ ø²âNyóéMK¤×/‡p²õàEýÇ ¬¹Ìr@&e1"ËA.74Aul„ÝL¤A±èÀ¾{Fà×UäËôÞCü$tÍûh§æÑ;ôcxŸ§^*ŽÞ82oÝä?ïmÜ^°¡_Â}êÀ£GÇÎïõëùùßN^| òÂ|Ú}I®'›š@DùŒ¦u æUˆ§]4p×K¨«_×K/ÿëz)ð/ë%åÿR/I޶-ZÁDIê( ý<ôö'‹º„Ø*R&¿à¨²U¤ÔR­«u‹`ÍbL&r¢K¶[Ð&šqT—¹yUæh­P,©Ë§ƒü<¨LéN ×ÛZ:ïü •ù·9iïÔ7Þi|côÆA¬µ–¯•müê•¿ævàëR·¹f§×‹¼Í@ÞF±kÈ[/RL,æôتøÈ*°¼mÞ)W*¡Úã2“n>)Y ¨Ì… ©šòŒŠAÎÿlÌètS‹´$qÔèãÏðK¼…úi”í£F×þöСý‡ûOïð ݸxØZEçÐÙ´:»ÛÉyü$ÿ+ºwkb]»õ¾Å‰S~e¯j#=hVm5¦Ù+ʾ°½"ÐH¡PW“|¨Tlnx|îó«÷ìéòæ“Û·ÑmªÁTs ïlÞ6µðbÛšøCâ8ôic;Ëψ#¶Üê(×7Y«i3LPì¦ÁQ˜…ŸE7šê~x{ïçžp\ÿ"Þ`ª.<þõ7ïϨ¦×V†b}åš¶c’±bìx~˜_ÁÐ>6žÞ‡{ˆ§ù¨ƒIh§0ΪlP嬴UGŸ¹›ä³gG«¶ùÎÝ6d"âg}XµrcõÊ•ÕWž¾ÕÖvóV°í–p™æÑXþßÍøW4†æñE¼„þŽ–Ò•´„—h6 LjŸÃ~˜Ø± É;mFJ&ûËMÑ”Hi‚ ñb ‹7ÄcM‰æ’B;)¬ƒÔAçÑw6çÝXŽ”£ËÒ÷2Ô癆šÒçÆ›&˜gÁ ñ16Ë0ÝôŒ°ˆ=«Îð”)ÍfŒ!'M4&; X¡ëebœ`œ%<Æf ŰP,±çŒÅÆèÉt²’GÕÔc¢žúæš?nn®ç·ëÞÞY‡È!·­ VÝ©f´m Ùh¿(|O3#Ñ~ü‰l–@DДԬ}"lœ,'x"èÍà¡ÓhÆI¾”‡Þ„öM½g_j_5éI´h7¡}ƒßOöÍŠ´;‚ÎÓÍžÝU “4ÛÖ •%{€÷ºp‘÷ |Cq‰V}¬ík”qÞôpÆzó!¨g a¬™†Tß žÕŸÁ€–GQw ÅOøøòëïðOèZùÜï69?¾Þ·ØïåP®¾ÏÕÖ&ú2h‘<c:¼Wñù~™«ïƒG¸Œ¢÷Ê iÀ¤¨óå+ïOßÈm¥Ë——•-_^ §„þ?ÖŒO‘=ªÐîã¹é£sŸži=N•óÊ)a>0¯?¨æ½X…û“îàÒ‘î³#!, –ùî×Pj‰y zó#4'ØLsøÄõ;ŵµâÒF¬ÂyÇà¼z¬ÚûR•¡”TÙ$b3èÀgî¢sÉ™v %”Ð>DÛyÝÝ9%É£DhøÝÙÝEî¾ì.)(¤Ñ|¯å§¶Þ¡Cè°;w° ¾ž—ðçùK¸Ê’knÆ®ŽÄû­’*‹H{b6ëÉÔ NK ÷Eð(Iâ<žvœwBÎo·²N¡b&âíRŒk;©ñ´DY-jh›ƒ9 £[‰²cÅe“‰zRd‹Õb‹²Z-¹r„…X åºfÅz0B‘m£Žèìz{žu`„ú¡A‰ÒÊ5ääÜÛcýStk{.b`÷°S:Ù+ÊÏÈ‚^ÑÛcÌ1–kŠ­‡’§äÙ'XŒ“Éd*镆 ¤î4Ë@±¦N<-9}õ¤õE£' æ6Óátp39?ÿ¡çÈ‘§´ˆ#ƒëቼ¨¯í¨/É?â¥ÁKð2Q‡$yÅ ^0ꑨ×Fâ€1—€®ŠÖA©ô:I…/b`F“üi aXÆÝ2,\èßÏzš”:Ф4©ÀCjD0‹¤_AW zUJHR‘’À#ÌlE<ÿË zìÔì¶Oœbž6vÜîD—óŪí Qž"´D¦û;`,¡,QT`ê 0/Kˆ¿DÊ€dC®™˜GêÐ>×—|ÿÒ.Èfª ¢¬MJõ E&xq.BU9õî§é ýü©ïÞÆÁö;5Z-“+v×öˆi‚ê‰."¸@Ôv"êV¢%SkaSðê~Öúc¾sùÇú­‘âw@•(T±R©2è=’ ˆ‡šðå–PùŽž„éΛ¤°lo–ÔJ{òtÔû´[ðh­8xÃÐÛ­µ!ŸÞ€óz0_Ç“Ñþ$Þ‹ÒGÇÄBTœW’X®¬¼a©rTФJ ²Q FWT² íÔÒ/pà@è;‘ºàõ÷ô£ù)û.Ê7!Ù«bA7Ò½ —~ÚG/ÑéÐ5é|aGp~#Î.´®äá£s=2õ<5M(ìÑZ[[{ˆ¦÷YTõà’rûŸèâ»ünÁ§û}¥búIÄôˆ‘:âòÛÄzi·POÞÁýôV7tAmCÔ>š$©›þ$ÝvíÇ=PÅÁ UfA†Z¡IÓ¿}7EõãN.T„kšÇðšÂ›·P™ˆk ÷Ö„zº›©k’Þ’ nÿB†Ðšï* ƒÀó¯]“?ü½B+zŽ&í°ºöècªŒ‰U²ñ-‘–‘ĪÈJ¹Ú›ì")–dO#½8e x)p¿†ý2´ûɤá²U«Û0iyÜB¨žýù–.ô}aÄÙµ³Ï~wý3üžz¨cèzþÙÒõë—®(-e»özSùþuáãüÖ?¾ç7é|º–.¢k۞ػeËÞ¿üyGæê¾µ;òÝž Fÿˆ¨rË [-URb™{k|•§Rªv¾Ù!2‚€#Æ•"» 9ÑaHì€þr&¶@HupùW?‰¿Ø„`ê×Núu›ø·7ýèчٺsç†Ë6­]1¡iæÂwóÎQ¶ S¼ô×oSÚËîZ¹æùê­‹Šæ-nß~¯Û}þÅ[B86Cý.ªá˜…äú©,ˆSÄ)“® ݦÔ@ÍFâÒ‹’ÍÜÎ*ƒg´OžjÌ«½{P%†«*M‚,ué}ì.ðôã¤+GŽk[É‚ßÂÁ¬-|-ܯ®]¡Ö¸vF×,D0J^yEr±\p‚è¬2à«Ô$2%Ò ocŒOTº8Œ.s&¦ÙU—JŽÖh• jמcÿE¸÷IÎE£"<Žè¯r©é«ƒ‚…FÎsü“#O{ï8­¹I¥×ÚþöÙÚª^ÞsÌÙÊgÒ…¯Mn[ÅZÿþáúFaBÛÕçW,]Þ‹¦j{Ñ®X™ÈÔ¬¯—h9–TÒA£¡C—,6Ó/¿¾]ò©[vÔ¡ê´hÚzÿ¯êWA1õÔ´t)/nP?¾½ù¶ä¨ýè´Š`œªÙ°ƒBVQ›1HGâÙ…Õ+)&wéX:•>‹õÿ„çîw¦»§{{RòÝ»êßHH C ðþ’ðý¼Ÿsÿþý£¸Æ§t#ÝD_Á£&|Æã(=Š÷uHÖûOw"qø¼úV¶$Ûö¤IÇŠÃIÒÂOIZk ¼Z›¬µ‘HI?[=Fk3‘<¿àª#Rg¤X$@ØQBFº ^Ôo; ^M$¸ÙÁl©þɈµ•w`)èy„´ûoåýÿõ×@ŽkTK×ây†ve¥PŒ:¼w4 ×dzú\=NWѽØßJ‚Ø.#ßS#¡Ý±×„ïæ‹Ixµ‚lÒÞ¬€¯É|ØGNc }{_ÓÀwéi’D/àl«~ZšpÔŒíbh‚|šH‹Èª:ÿb\s)ð,ŒÁ™?OáÕÈJ<Ö‘-döUΖ!ÿŸ‘]¤Œ\'„Ëd"ö÷’CÈG?ÕÖ ­X? µBoa>wgÛH6Òe¤•Ì 5â“™šî¶à»›p•GÈ&ÖÊ6¨úÀs+â¿ § RƒäÐyP Uo[é>Úcò4¾¿˜ŒƒIð$œ§ËE¸.“ , È,r’µ"jTè<¤BšAŠÚ±X•OX ÐZrç|~Àqr¶I“˜]Â6’D™gàµMZ[j%™|·QïkN‡ˆƒ /ÞY,ÇZf3¾™Šš!ddãêsÈb¶&tZ<ÒØ¨Äù5mÐ,¡7Ù$ÌÀ\!ÜDmΤ;®‘À®’åt—úÁR·„ÌS7²[Ú“În¹NðæÖùGç»NHJëü«¡[Ö¹ëȨ:ËBwÃÝ»£òÅ86¡ŽÅ×W_'z=ÿ«›Ó:•ïn Q„§X0/ŽÍÇ®:ÂËx}àힺjó⿼‚:÷´™îÕòjOÏÕòôžib ãuû¤¾ü·¶^7H¢^sùzôJ»w¾³º²&ýop¨ÞÔ¾Z¨­®ˆ'¨yëÎê»Ö¾þÓχ™‰”ÔŒ´©i±xŠ^V=©iŽgáygèžú<ƒçUHï!í ½¯½£ÎS¡Z:ÜoBšš‹Ö†Ïë&a¿&<ß±ð\a~¼H…á9n"mÀçOЧÏÃ|® ßW×jË3 }›j§ZÍH»~‘x‰–ô ‚ø(Ô¢«nR ª ûúm„ÜHêù ï¤Ï 1ýé:¦­R„}Ô¯¥+R9&޳„Øp-n³l-„Èã¶¢à îQì¸npI^‹Ày=pm'b¿×u⺑ÈO®µžèõÿhVò fÌFc®ÁZT«Veã©‚YBÜ#”PçÎõ“Y¿xê$•°-ÑþÇ~„Öâî[EëËZkCjÕú–ßfý¼ÔB–àÈŒ) ÷cć­Q›Ï =¥GÀªÓú’ö Óú¢v´+‚v…ú'pàÚ–@÷}ðc#ü°nÝ,g·8Ü: Þ¼1Ý,‡›%âë)ìÆ¸á¯§À?¾Ï`ÿ¸ ßgÀÿápÃøàª¾«„²àh¸{ÊWüv0\¹\È®TÂåBø†Ã×_ű¯9|_r¸ô8|Ááßáâç1ìâmø<.TÂß9|ÆáÓóNö)‡óN8W Ÿœu²O8œ]cbgðñø¨'´â µ'œápúC#;ÍáC#œâð7'W+ìd<ü5>àp¢Ž—yÙqïs8¶Žr8Âá0‡C-¬…C3‡ƒÞãpç;à€ýfhz·‘5qxwßdön#¼["îkô²}“aŸ_lôÂ^{*¡¡¢ÛÍ¡Oõ·áœk‡· ag!üÅ uvø7;¸¿ þÌa;‡·ìPËáÍmVö¦¶Yá­ {£=lUàõ-iìõ%°% þÄa3‡×8Ô¼Ãj áÕWdöj ¼"ð‰Ã˸ÈË6Z zC:«æ°!ªpýªJ¨|©‘Urx }ë¥Fx©D\ÿ¢—­Ÿ ëýâ:à°ÇkáE/T 2*úÁ (í Xc‚r¼P^e¨´2/¬V`‡R+9¬X®°–+ð{Ë8<¯ä²çÇÂR%ÏBñï–°b¿[K\ð‡ÅVXÄa‡g8ÌÚÌæÛ`~%þsâÓfxú€8ÏóüâSžä0—ÜÙcÙœJ˜]ÔžÍ Eíá û`‡Ç|0ó6<Ú38LçPÈaÚ#.6Ã#Df¸`*‡¿å0e¢‰M±ÂäB˜tÆÁØhôè|Œçð‡ßÄŰ߸`‡±Æp½FqxÐ#9Œ il‡á0¬= Í‹fC»C^;Ë‹†!£Ùƒq4¸áhP# Œ†xa@w蟫°þvèß øý1·Ÿå*Û õó[Y?ôk päïkf~+øh Žú𠬝ú6P¿¿Pìá7²Ðû6ôâð@{èÉ!œS=ºÄ²à;‡niÖCö0èš˺ƒ,]ðv—XÈŒ… ìeDCº!’¥7BZç–怴A]¶³¬°ÎÐYe·RìÔÑË:qèˆOvôB¡'ëÀ¡=‡T)6ðFæ2ï@hg‡d›%sHr§±¤%àNƒÄaà•]8Ä£nã9Ä¡Uâb –C ‡hQ8CÔ ˆt¦±È\p:dæL‡ ø\„ìø¾ƒ‚’+¹ ã ²rHw6«™Ùl` éÎj12«¬!ÝYPw#XPw»D³̪ouMŒ(‰‘ƒ!ô2è8H8µÄ9P8¸ÅBzEhí@háò5´ÓÿžùŸfàÿñ—@þP£ endstream endobj 10 0 obj 7150 endobj 11 0 obj << /Length 12 0 R /Filter /FlateDecode >> stream xœ]S¹ŽÛ0ìù,7ÅB2Ï5 6‹ˆ“IÊ+ ¦Z.ü÷áp …ÍÑp8ïàc÷zürÌó&»e §´Éiα¤Ûr/!ÉsºÌY씌sØÞ¿Ú¸Ž«èêáÓã¶¥ë1O‹Ùý¬›·­<äÓ縜Ó'!¥ì¾—˜Êœ/òé÷ë‰Ôé¾®Ò5åMöâp1MÕîë¸~¯Ivíðó1Öýy{<×cÿ¿k’ª}ï˜RXbº­cHeÌ—$†¾?Èaš"åøßžÙóÈy ocƒ:Wiß×E .5\—ŠwÄ;àH-±­X ×¥úôôé+öºáºTlˆ °"VðÙÓgì‰=<K#–¢^5ýDÍLÞ5ž98äà×!®b-ªÕòBþþÌ_#øq=yÞ³Z ãĵÄXÑSÁÓÓÇÃGò˜5ªV#yÞ±'=1Ôh yÞÛÖ7j|Óð^ îÅ;ò±˜›Bnš f¯4zey×w­˜³BΖ=·è¹¦§†§f?5ú©™n¹ÑÓ¶þS_ ÛûTaìð>>æ9ÜK©£ÜQ›aLïœÓÇ;[—§Úï/Õ‘ã¾ endstream endobj 12 0 obj 449 endobj 13 0 obj << /Type /FontDescriptor /FontName /SGIDRT+DejaVuSansMono /FontFamily (DejaVu Sans Mono) /Flags 32 /FontBBox [ -557 -374 717 1041 ] /ItalicAngle 0 /Ascent 928 /Descent -235 /CapHeight 1041 /StemV 80 /StemH 80 /FontFile2 9 0 R >> endobj 5 0 obj << /Type /Font /Subtype /TrueType /BaseFont /SGIDRT+DejaVuSansMono /FirstChar 32 /LastChar 121 /FontDescriptor 13 0 R /Encoding /WinAnsiEncoding /Widths [ 602 0 602 0 0 0 0 602 602 602 0 602 602 0 602 602 0 602 602 602 602 602 602 0 0 0 602 0 0 602 0 0 0 602 0 0 602 0 0 602 0 602 0 0 0 0 0 602 0 0 602 0 602 0 0 0 0 0 0 602 0 602 0 602 0 602 602 602 602 602 602 602 602 602 602 0 602 602 602 602 602 0 602 602 602 602 602 602 0 602 ] /ToUnicode 11 0 R >> endobj 14 0 obj << /Length 15 0 R /Filter /FlateDecode /Length1 8260 >> stream xœåY{xE¶¯ªÓÕ3Óóê™Ì#“Éc&a‚á•!uêw~çtB(!D!åH`Òô %'¦¬ºÇEBؽ“þ83Ðo{W#!®‘ØÎ/.™2},ûŽyªð©S˜SüN 3hu|fÐÔÉŠÈ7Þ#„xq>é1;ložÃöØn7uúÌGŸxЬµ×c{êMš@ÈĪØ~`ú„GKäñrglÏÃv ä‘É%ó&¬ùÛ+ 1¼N)R1¯Ai $©žH4'Ê4õ]jäÏ0‰¤745w%jSsSsFŒ#èÁb‰´ÌËYQa°]½ôˆœ†ÏªÉÌœ² ×J|¬eÔJTImÁÇÓ›3¦<á%¼œK´0ÙH‹E¥ìú¹Y{–‘‚Öo¸"Í%.â'ÃAâþ’š›øj ý"vµs¿ey¼ßÍŒn+ĬöÞñ¸î忇3…;s¹ùŒ*._P/ädlMOèŸÀh!ui·ÌÙM ‡J‚»§¦$Ë®´ì°®|þáó¥e¢LoÓ\šL´¯X<;2õq•u+ž?à ќѕfQ/uÒ^bÏÒâÒY¶ÉZÜú\Ì/3ñ’ÌpŒ¼ÉI6YV:—Çš:Û³¡³»c,J§í[“L½6Ÿ7Fõ¥ûúû8Š–ÌÜ.g·LgVw7EÊk÷ì©Ý¼gÏf:•.SÅr±BL¡+¤ã¢¥ùœh¡Ò¹f*Q¯(ËD…(¢+éýôt¥.ÓFÔß}¨{3‰'=Ã~¹ÚrØNªÝËíûýkcû`€3k1YãÔ”-3ó²v*gš5ù¾½±uTb$•Êì‘¥«Èíò@?TŸ&VJrj{7sæÌ˜1wîÜ[g¢Š¸rhÖÖ¢’·¾ªjý†Õ«7°£ Å{â:¦÷ '®Aq4ÙZëÅfb'}Ã>3#†ÕÜvŠlá_YÖªÜ([©ÕBTUméӜ٣©.ÙGNŽGÄQâ(w´Ç%w¤Yx°YŽ6}Ù¸¹_ñH LRäÚÖÓçÏ[(qRÃ#bJÊBLùHv8ޤM’í µI©vHÕÞýŽåq†Vâê§¶4dÞ<, E?¢BÆûËüŠ]h›:þJ™)køÒQ¯¿^‘ÿr8ÿ­ß‹#â :š¦¼)õ'33Þ~õÕ·3»ŠII4›º1e'µÙˆŸ¨á«H ™޳q£ªt‹±š(F³‰™$bU¶|—Ú’[kÏÏ­UóÇæÖ:´Â™?¶`;±‘pÏ1}Zú4485E¥74_F¥iŠ¢áØþ¤ C0€]µ;òXä¹#,¦B‚;Au9\žÞXINÍr¤8º9X7ú°x¡ß¸m¢±is]_%ö´º3»•ln¢'(¡ýt™kÅ%d'±’¤°ÔÖÚ T2³CRmj jOq•Aƒ©,«»3;(kI|±âÅ¥ËiZyy™¸ôÝyâýðÇó¢÷©S¢W›>ê ‘ð£ÄD:†ÒnyÛM95€‡€¢aµ©·y­=ã]Õœg.1C´¥zÉ~ý6åú QÆŠSâ{qêúBÍ~á"I ÛdD›´–qj•ˆjÔ¦ÑÒe˜LS‰©Ü$ýº®NN70U%»48¡¬5⻪ë!>l%5òZ›Õ 8d6µ !tS 1=4Ënô8Ý.f¨)Ç‹¦-_úâ qéýøÔ)º÷ü¢ÿ‰bàO¿ð&4#NˆSÃf’ v›_²í7m1(²•U§†‡˜|€±uwÏ1 ºôg.Ÿiipä fÐ6˜j¢»5»õx¡9½0ýÙeÚ.o)uv¸ Ò=îͯ]o‘"Ûœ ü&ç¶Ç÷j<Öy¬ÚIª-û5` ܽÿ‰ÇÞÍŽéï…ݰ ¤‹[)Œ}8»´tö¬yófÑ ,vˆ¯Å_Åv:æ¾Y]ý¦–)ûD3¦}´'uaêÙ¦äTENM ¹á/ÎcQMÄV5n{¼g¨›L+ay|¢ß×ÕÒ¤Çt–;&j€Ò5èâaÚ‹ºhûeÔO¢Hh²tÓb%¯nËmÔ‹¶œâòèb±ÿ5ñœIëéã˨cNéOó¾ýùâ©>SÒႇ† ¡Õt2FWõ̆­?œ®¢³`šÌU(³,E_z‡ýv ÔrÂuØ»\¥[œ2#1v«c(C§ûªLÝW¥ŸÉ¼|¦Í=UÄ_®q zUÄ ªÓ«;,ײi’S«èTfsx†v))×°x×;Ó÷ [ÙÆ’±â|—…³ý)©W°´kUk&^Ð}€ÍïÃóTÑô'x«Éa›£š6î·-§…- qÌaR »Å ¨gÎ šÕ3·¸_ptƒÿcn9næÔ0u‹ ¨A—0]Úë[e¥æÙšOÐ|CÖ¤½(›BBäÕp“L|Ifò™÷¼ÚvØ8˜t ~u ’²…¤x!Öj²šû&ÕÕ;m@íi§ëïr‹î³®\Èi;èðÈôö}}ƒýÛ߸#X( >x08?0?XÒþùÀóÁW¯ß ¼ÜØtg&f$ L 'Ý“˜—4)1’ôdbyÒÒÄÅIk«’êk“ÔÂ[°Ý—†nžDV÷vÁnm®Ñà@Å$¶¦äá±wM~–N/Ûº`Óqj§ÉŸ?õçÿnÆßfÒtj¥WGŽtÇ’éi ¯/XW\x`ÍÞmñ¿Õ¥ uÄ'ü cÞ'†p'âÇN{„ÚˆKf‹l œK q‰%PƵ0;ÎJ IJ)LåDYmü ÈÕn3›ŒEÏ¡ªM¹µJ¾î*rk]º¿ š¿hðêQ·CSZNFî=•Ÿ7¨Æ™ëÙ›c0ž“LÃ5x`LÅ`a–Ç&³™ì)VÎ^b‹YÛÊjõ´S;Ìþ S¬DMÀ9—¹=ÔÍݲÛÐŽ·“Û²Hí =ä†[Ž}0LGÀi0,Oa“å§ØSü)ùiëÓ¶—Y¯WXWØÖ³7à iƒmƒýZÛ¤ZS­òy»u»m;`=`ûÔ~Œe9èÔb‚&äm‹úS-¦Ñm‹´¡;_9T2ö¯"E®ÙÕk=v,;×+7I×;Æ¿\BŸ£ÛÂnØ$±M|‘l2cå8 ±œš‘Ï›Ú"WÔšú\}Á‹4_¬¥céƒ4ÿZ3U`ï0*»–%®èq˜‚Ñs{\Û€5“TI‚,s%Á¨ zLZ%H@1@%Á¤H„‰l2J‹d¦˜ŒcZ!UðeÆJÓ»Æí¦d/ÝËöMÇÉ1zŒ75~eJ×BŽ ©ž@r}ÝxQÊÒè–&J¯¯§+RU\äG£YˆÝÝWC{°Is½nC/”ê>èiò­v&`8kÚoÝ’RíüІT ±ÚÂîÀ¹wÚM~иA h‘ÊÄaGuˆtÐøL³a’ÕhŽ1+¨¹Œimt ·º²u#êf5ŠËÔÒ8«n*ÞõÓ&Ý·ulmMóCóQ2oÞ®‰ãèÀè5zû¸IëZâ’ø&¤ÞY•5 ×TT®®YVQƒ[ƒqÍ×èÏbÉsá4¢³—zb5ËñXb[l7ot®”ÈÛ,Ö͉ÍèqS¿ |êÉÜZ«ææAwó-Mš«päü‚C-ÎÓßvÞÖª¶v*óvðtð›—Çz½ÞØd’ìMŽEÓófÅ #øïX»~$ˆ`Úþ•¹]²ÝÉ–\ûˆ‡Y4ëÞc=.£š6?õ‹³ÔOO˜?hZù#é°Ž›?ìóͺ!Öùý:Ö»a„¥nšº‡ô䯛TdÓYˆk,¤GÈefк°é×XKááÀï0U‹ß®Ý)ÕékÃïÓäF }<e~Îò‚í%ã+–Ŷul¬“6ð׌LëÌoXÞ¥Ûe´dËÇ´öKŸÊGÙqøBúŠŸ5þÝô­ù{ËXgýTûÑ ™‡Ñqõ;iGÌãÄq1gg½˜#EZZ@º.±–kUÀZÚ2Æõ2AlÑš†3ýq,Þ—àñ¢ x½žÏ£Ä áÊ›,‹¼Š'&ÔxŸL$+Fý^Õdð˜!¡-ôGÄ{sm‡£@Ž3ç–óᚌI®‹ÇX/œäõûââüþ¸øîžÁîÁžÑîÑž¼ÄÉîÉžH¢½êFÞ‘ú® ÷‰Ô Ù×s¦M›³F”±‘´=yañ¨ÒðQünöÃ÷Aÿ{§ˆâÊuüRùøØK»:;ˈ:£D'.D+Æ~x·‰Ý‰÷DŒÀÐIi¥÷Ð ôQ:Ÿ¾È>f'©Œ@¯ÀÆ`rk«ö÷REï¦/½1ƒã9¿ŒÿöEñ'i%]IWaªº‘>Æ´îûoæãÇÚ?õÄÏ¿|ƒë—š wò襳÷7Ÿw  ±:!n¢ýEŒërkÆÄDÓš8±c‹Ùù/¥ùza8Ó£-ýë}ѧD!I ÍH1öj÷4Ÿ4ãøDœ¹@z’æã}º„᎗I¸£ÝÈDò0ÖBR ­'øôºˆã÷j³õik]áÑ‹<‡åiºÔWª“Hu8c–T,- µXæ°#ÒJi®tãøM2:RËú×v%ASH%«¤ƒ0 Äɇºüý¥½ù~€%GiÎÜHf3…~B/a\@ëð©+ä MÂr)½@¿C‰—“#PÀRI^ NlÕ“F”û,¹DfH¸*yEÚ"§ÉØOÈýÚ'I€Îü(¦‹d=¹5sã磲Ë”ŠÙUÒLÿÄÖ±«4…2LNš„Ú¼¥ˆô‰ô Ž¢v(ƒn°§ÍàGi%JqZ.¦Èrzš‹ïif±m¸Ç]äî ßÎÆ±¹¬’œ¢›èûTÃò“t“1L”ü¤R®” ÈM7äkD}äéúxŽ<'w%W$™\„‘4"­×4FBüC `ƒ†²“TІ?áNd“¹ºE~J ÿ°-á,£œ@*¤ö° eg¬ô¦ÞèÒÈr`"Y©§¥tYJ¶‘šQCê{†6oH:ÔZ^T¾« °oL°s§jTC –äÕZç¶µ¶æH~>¦–Ç×BÈX+…RNÿÖàéÎró ÛèmƒÝXvpdvÞS€U­…ÝØ?x>¦½µ–‡ð7'~XOjùlR¯ÍÁz \¹e8w έ#UÚ|^L x6ññ%ä;«Þg%»‰„Q©™ŒÁ>m°,Ç>3UH*öi#€eû´ &ýI£^P#Z©=!׽܅ßCe}_\/%}è;bzÕKn-…Ö~ ´\ëÄ[\ëQ?_Ê.…«Cá§(\ð£€Ëþs\ð. ø!.8߬ðóšhKç¾Wø¹Lø^¿GáoK<üo¾‹Â·Q8‹³ÎøFÀ¿ 8-àß|-à¯Q8u2–Ÿ*‚“±p¢*‘Ÿ(‚¯Ž‡øWQ8‚/„ø—Qøâ˜‹ácGU~ÌGUhúÜÌ›ð¹>ßEá®$‡_²ðÃ)pè/.~(þÒèäqA£âðÁ8à‚ýŸîàû|º¯º>-—ö…[? ñ}…°/,}‚ì-‚†Å*oðQ<ìð¡€Ýô⻣ðÁ[~þA/ص3ŽïÊ„õ¾3êwØy½v¼oá;ìð¾¶ã˶ Ø&à=7¼ë„wl°E@6û Öoã:oGaÞ6Eá-œÿ–6âmc)¼)àTØ `½€×¬ðš5Ö®±ñµÖØ`MXªFEUG¡ ©J„Õx[…U¸ùUñ𪀕¯ìà+¼RYÈ_Ù¯”K•/„xe!T†¥–#:– x¹ Tàƒ‰áVX†. ÀKXŠ]KsáE¼½(` êa‰«ðBþ,`‘€ç<'àYÏxzaˆ?-`ažð¤€'2áOð¸€Ê}P¦À|¥æ ˜…Ç¢0GÀì?®ã³üqÌšéç³¢0Ó3¢ðH)<, ä¡Nü¡Nð`¦Gá(üAÀý¦ ˜:ɧfÂÅ™0¹Há“)P–&MTø$ LT`BÄÍ'T@„:xÄ ã¸O@¡@Oçà㌽×ÏÇ ¸[÷úaŒ€‚(ü^Àhl‡[G ø€üD¸ÇwßåãwGá.¸Ëy£|¸Ów$ÂÈ\é†Üžë‚Ãm|„†Û`X†qñ¡nâ‚ÁQ4ÐÆÙa  Üâ¢p;®y{Âýí<, ?ïo‡~6èÛÇÊûz zA/9.è) ;zdÅñ!ÈêîâYqµ[ê®Xywt/—ºeZx7t K™èš±Žwëg¬ƒt t‰ÎzñÎQèäñN½ ct(‚4·¹¡½×ÁÛ'BjB‰Ð.б]"¤8 ™Xyr‚v†¥€ ’HL„„xOA¼=†Çû ~rÆÉo…8_.+¾Ô— ±¼ðàÛ> stream xœ]’Ën„0 E÷ùŠ,§‹oÒ‘R5ݰèC¥ý€˜)R Q`ü}íx4•ºŸ8×7VœäÜ=wnÚdòÓÃ&ÇÉÙër ä—ɉ,—v2ÛmÿfÖ^$XÜïësçÆE4L>psÝÂ.OvàAH)“·`!Lî"_çžSýÕû˜Ám2m+-Œh÷¢ý«žA&±øØYÜŸ¶ýˆeŠÏ݃Ìã:ã–ÌbaõÚ@Ðî¢IÓV6ãØ pöß^^qÉ0šoDS”¦)䊹B.ÇÈ0Ÿs>'ÖÌš¸`.UƒhòÇÈЧfŸšô'ÖŸHÜ+öWÑŸÏ­éÜRq­" ÷¦¨7•2§¤ÏXŸQž=yœ/(_2—Ä5{ÖäYð¹[qÿõ¯X£HS±]æíÖèZiþ÷y™k8ªøHâŒh:“ƒû;ò‹§ªøý%û¨< endstream endobj 17 0 obj 342 endobj 18 0 obj << /Type /FontDescriptor /FontName /XAMBSN+DejaVuSans-Bold /FontFamily (DejaVu Sans) /Flags 32 /FontBBox [ -1069 -415 1975 1174 ] /ItalicAngle 0 /Ascent 928 /Descent -235 /CapHeight 1174 /StemV 80 /StemH 80 /FontFile2 14 0 R >> endobj 7 0 obj << /Type /Font /Subtype /TrueType /BaseFont /XAMBSN+DejaVuSans-Bold /FirstChar 32 /LastChar 121 /FontDescriptor 18 0 R /Encoding /WinAnsiEncoding /Widths [ 0 0 0 0 0 0 0 0 457 457 0 0 0 0 0 0 0 695 695 0 0 0 0 0 0 0 0 0 0 0 0 0 0 773 0 0 0 0 683 820 0 0 0 0 0 0 0 850 0 0 770 0 682 0 0 0 0 0 0 0 0 0 0 0 0 674 715 592 0 678 0 715 0 342 342 0 342 0 0 687 715 0 493 0 478 711 0 923 0 651 ] /ToUnicode 16 0 R >> endobj 19 0 obj << /Length 20 0 R /Filter /FlateDecode /Length1 3288 >> stream xœÝU}tTÅ¿ó~3»Éf?“ÝÀf“͆d£–D !hb€ƒHB1(µEY"~¤"àbj¨Rµˆh° "Ek¶Øj$Q¡X%j”à'm©R¿¥TŸ9“Þ÷ÔÓszNÿìé9}³ï7óûÝ;wîÜ}o "J§6ù›nX÷ì¨4"±È˜Ñ¼àŠ–KŒ«ásöÚrÅ5Kš‹šÖ¿Äãuì#çÏ›“¤†ôIö§1óYðns.bþ6ó¢ù-‹oruÑ*æÌÓ®¹¶iÏ«'R’¹»eÎM “¥ÌýÌc Î[pËœ1Iä|Œ jÖí²Ymâ윔ßMR³£C?-ÒÔ]†¤/èEþýúGf ñ‚@A³¤Eˆ ÑíNï© gðŽ5œãNt9V˜.Kd9ÂéÒCž q«/Ý“5)˜‘ã¨îï/û¼z z ²r%²²ÓLï#Æ!—Z/éÑ ë)¿{Dõ‡eìç?z¬,³²rd"p<²3’Š´q[iŒ$"ib¶à4*æÈ³Fv+ ”WŒSŽwÄ•zMíKuU†ÖkôÕzM*µÿ #¨ßEŸU3íë hà[дg¶qÚ¢Y”‹fê¦Ox<–6’‰|Þ W†U«ß"fP?Ûç²ç2¹\Ìà¾En"ƒí·Êk碜æÒu<ŠËM¢›vО½LÜ«ÎW³,oú¦>T»ÅqUiTRƒl‘cåv¹Lngëe³\F)ÆJ£W®“Kåkr)5X™‰©ÖmåA¢VR‡Ñ!jDXÔ=´Ëμèç¨WÔ+ÔG}üïoáv£á{Ä 1B4ˆí<ë$ùÌ*Œ qL|Ä?D½hP.ê •"“Y7õpÞGè-’•Vª>c¸ê£Ýt˜Þbè*a0æ¡Tõq;NÓU\™ÃÂP}Ž ³@6§¨_Üfl6N‰BapËù\ÍËÐ#åy[¹:Â@9ò1‘ñRËCõ‰Îâ°£Y,a?«-åuúÝF'ïñy:ÄûâÕK¥F[ÅΘh¹Ø*se„:²ŽYµ¡^£‡ëQo×ãnºÛ1ŠNJÇTÑ(·*FqµK(pÖ:2©]Ô:oã΢¥dë>Aj×7½ÒyÔ.OÃÜ»a´~W7±„zŒJÌå÷Ôj«E'­¦Nâ7“ÿâgœ%a*‰ùSF|J2•˜ÞÛ;³ ´äßhÌ¨>åYë¬o53¥rSˆ§¥d¼ðð2.-©«oˆuŠÓ'Õ|vRc ‹5ðÐb,³>©Æ¶Y«¦TœSS±¦ù±þ…U+üóªJÉ:ŸŒ7Ÿ÷Øú'.÷UAùiöãÚsvuáwý— ¶úzÒ籯e¶£³Eçù ¾\øU™¯ç[ý_—ÁoG³:f ö5Õ:Ó¾YÓX½‰Üü\û)aEUGîå„Û6>„4AÄ)ÀX$ ¨Š”("“Y!e3ûVfûYcˆ˜mϧ.Æ(%ólk.ŸFŠ2æØJØÆ¡6±1ÛÆ’—£†lf!²ìq¦>á¥V¶ûlf!<ÂM÷°æ±5íä#Ô-2h&k–Œm¬e³fYÀ˜`ÍR Òí™i6:¹"Z3Ûל©&d ‡½/e£´½`ïȰa#%[18Zcàë5 ñu L¯NMV_µâÔd|iâ¤ÆŸkü£ '4þ®q\ãoQÓ8ÚïRG5ú]èOÈÏ>u©ÏÊð© Ÿ˜øxU¶úXã#5q„É5>Ðø‹Æa?küIã&½?TJâý¡xoCT½—Ä»ïÄÕ»&Þ‰ãíÞ¸zÛÄ[ƒê­lìó«ƒAôùqàÍ u †73ð{¼a¢—ã÷Æñúnõz!^{5¨^+Æ«=™êÕ z2±ŸÍûóðJ/ïëR/kìÛ;[íë¾6¹71¸'®öÎÆÞ„ÜÇKHâÅûüêEݹø½Æ./T©&^x2¢^¨ÂóÏå¨çËð\w@=—ƒî.Ÿê k‡[uù°Ãgy±g5:5ž áéLüNã·Oil‚ß„‘ÊÆ6޳ÍÄVî¶šx’ýŸŒ` w[Zñk'Šñ+Ç5ÓØ¬ñK6i<ºÑ«ÕØèÅÆ„|„ õˆ‰ •Ð?ΫÆû0΋±Õ56Õœ“D•Fegkœ•…19jL£ƒª";åh—Gbt›,/s«ò ʲÌQ#7«Q#9þÈÍáÆ™Y(-©R¥&JBqUR…áI|/‰34Ná´!uZÅ1Ä£(*ä /Š¢0€aäQÃLøP± ò]ˆF‘—Vyqäú²Tn¹|f¬’rÂu*§a^4\‡¡CÈæÕ²M„X ÅL"+€Ló€†? Ÿ×¯|Yðí”^?¼mÒà w2xkÙÈh“.\ ™®‘¦áÔp(—rh(TBJHò2  ͧ—G‰ÈÑ)’ËïÃÿ?.ú_'ð_¼ò蟱2g endstream endobj 20 0 obj 2254 endobj 21 0 obj << /Length 22 0 R /Filter /FlateDecode >> stream xœ]ÏjÃ0 Æï~ ÛCqÓsŒî’ÃþÐlàØrfhd£8‡¼ýd7t0-$}?óYúÚ¿ö2èOŽvÀ >c\âÊaÄ)j.à‚Í{Uo;›¤´Àödœ{òQµ-è› —Ì^\ñ¨@°C4Ááû:> endobj 24 0 obj << /Type /Font /Subtype /CIDFontType2 /BaseFont /LDLMNU+DejaVuSans-Bold /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 23 0 R /W [0 [ 600 741 ]] >> endobj 6 0 obj << /Type /Font /Subtype /Type0 /BaseFont /LDLMNU+DejaVuSans-Bold /Encoding /Identity-H /DescendantFonts [ 24 0 R] /ToUnicode 21 0 R >> endobj 1 0 obj << /Type /Pages /Kids [ 8 0 R ] /Count 1 >> endobj 25 0 obj << /Creator (cairo 1.12.14 (http://cairographics.org)) /Producer (cairo 1.12.14 (http://cairographics.org)) >> endobj 26 0 obj << /Type /Catalog /Pages 1 0 R >> endobj xref 0 27 0000000000 65535 f 0000021259 00000 n 0000001823 00000 n 0000000015 00000 n 0000001800 00000 n 0000010299 00000 n 0000021094 00000 n 0000017430 00000 n 0000001970 00000 n 0000002200 00000 n 0000009446 00000 n 0000009470 00000 n 0000009998 00000 n 0000010021 00000 n 0000010795 00000 n 0000016685 00000 n 0000016709 00000 n 0000017130 00000 n 0000017153 00000 n 0000017879 00000 n 0000020229 00000 n 0000020253 00000 n 0000020555 00000 n 0000020578 00000 n 0000020854 00000 n 0000021324 00000 n 0000021454 00000 n trailer << /Size 27 /Root 26 0 R /Info 25 0 R >> startxref 21507 %%EOF PyTables-v.3.1.1/doc/source/usersguide/images/objecttree.png000066400000000000000000007155501231437614300240510ustar00rootroot00000000000000‰PNG  IHDRüÐÁ$PsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝyXUÕþ?ð÷‘Yå0"2C†8K8 &jh*fšæT9T×R«kל½Š×Êœ-Í4Ms*,•ÄQpVgp™Öïì/Ûs€ÃäQz¿žg?ÏÙköZŸuØrãs÷ÞK!„Õu´Uüˆˆˆˆˆˆˆˆˆjüˆˆˆˆˆˆˆˆˆj]m'@TY …ÂÀ]mçADDDDDDUv\ í$ˆj ÞáGDDDDDDDDT‹°àGDDDDDDDDT‹ð‘^ª5‚‚‚ЪU+m§ADDDDDDX¿~=îܹ£í4ˆj%ü¨Öxã70iÒ$m§ADDDDDD8pà ~D5„ôÕ",øÕ",øÕ",øÕ",øÕ",øÕ",øÕ",øÕ",øÕ"ºÚN€ˆˆˆjŸääd(•J˜™™i;µ233‘’’‚¢¢"XZZÂÜÜ\Û)½ðäɘ››£Q£FÚN§R‘••4nÜXÛéÕÞáGDDDÕ"== €µµ5œœœ`nnL:·o߆§§§´­_¿^vn»ví¤c“'O®‘ü®_¿Ž?üNNNP*•pssƒ‡‡,,,ШQ#Œ7—.]*õü¤¤$Ù6oÞ\¡ñÏŸ?/;çÎURµŠ‰‰)óøéÓ§áíí-Z`Ïž=RûÂ… eóÒd;qâ„tþ¤I“¤öN:ÉÆ\³fì¼”””*ÏsݺuhÑ¢š6mŠ;wîT¹?"""¢—ïð#""¢*+((@Ïž=qôèQY{jj*ôõõ‘ŸŸË—/Kí=’Å]¹r÷ïß´lÙ²Úó[·nÆŒŒ µÇoÞ¼‰eË–aåÊ•˜4iæÍ›YL^^žléééÊ!77Wv~i¹¼hçÏŸÇĉñðáCÄÅÅ©Bàƒ>@aa!”J%† "KMM•ÍK999Òç;wîHçgffÊâ=z$ë»   Bã¨3zôhÌž=YYYøä“O°qãÆ*÷IDDDô²á~DDDTeW¯^•ûÌÍÍñöÛo£_¿~ ÔbfÀÔ©S1lØ0 l………X¸p!Þzë-!^@vÚõŸÿüÍ›7ÇþýûËŒûå—_päÈÀ;#zõ꽈ôj„½½=z÷î Ø´i<¨åŒˆˆˆˆªïð#""¢*KHHí/Y²DvXFFÂÃÃ¥ýçݬ)À¼yódmíÛ·Ç„ ТE àÔ©Søá‡°}ûv)fÛ¶mX°`>ýôÓjËÅÁÁAö´jÕªÚú®¬ˆˆîš[°`ôù½÷Þ+3vþüùðññ)3¦äñáÇ£mÛ¶€úõë—›Kuxï½÷¤Ÿ÷üùóѱcÇ2.ы‚UÙíÛ·eû-Z´í+•JLš4©ÚÆB@¡P”“••…áÇËîÔûøã1þ|ÙãºèÓ§–/_ޱcÇJí_ý5F¥ñÂ#åådmm]áï@“yÖĹ%8p§N899¡Y³feÆûûûW¨€Ö³gOôìÙ³J9ÓtÎ]»v…±±1233¤¤$¸¸¸TKDDDD/>ÒKDDD•¶wï^cÉ’%²ö1cÆ 88£G¤¤¤ 88XÚvìØQá±¢££1pà@¸»»ÃÀÀMš4Á!Cð÷ß«ÿã?œœ,í{zzª}7_Éœ»uë&ígeeaÕªUeætçÎŒ9022B«V­ðÉ'Ÿ ++K%öÊ•+²ï 22RmŸ¿üò BBB`ooºuë¢uëÖ5jT¹ïÉ+,,Ä¢E‹Ð£G888@WWvvvèܹ3¶nÝ*+|þþûï–-R’””$åvöìY©ýÇ”>? [fÏž-ûöÛoWøü'OžàóÏ?G‡ T*amm®]»bÆŒÈÍÍU{޾¾>zôè(**Â÷ß_¥9½t„ܸ½’[¢x DDôb­]»V”ü]üüæéé)„âÚµk²öÅ‹Ëú±²²’Ž………ÉŽ‰ÿüç?BGGGíuêÔÓ¦Mùùù²ó† &‹Û´iS¹ó9xð ìœ®]»JÇ.\¸ ;6tèPacc£6'777qöìYYß±±±²˜õë×ËŽgee‰¡C‡–ú]Ö«WO¬Y³FmÞ ÂÏϯ̟ŀDaa¡Bˆï¿ÿ¾ÌØH}7hÐ@jß½{·ÊØŸ~ú©ìܨ¨¨r¿ç’  kgg';¶`ÁYß·nÝ’žžž¥ÎÃÇÇGœ?^í¸%¯] åLDDÕÃßß¿äïícâ%ø;“·Ú²ñ‘^"""z©ýøã˜>}º¬­E‹¸páž>}Š¢¢"Ìž=øøã¥˜’w©š½3ïù‚Oœ8Qjìúõ뎎ŽpvvF||<ÒÒÒ<»›oذaˆÕø±Ú©S§J}€®®.š5k†S§NAììlŒ17FçÎ¥¸ÂÂB„††ÊækbbKKK$%%Im[¶l‡‡¾þúkò€3gÎàîÝ»Ò~óæÍË=çСC*«0S(èÓ§Æã—%''!!!¸qã†Ôfee$&&xv ôèÑW®\¾¾¾ìü¦M›JŸ/_¾Œ[·nÁÞÞ¾Zr#"""Ò6>ÒKDDD•†´´4,\¸PÖ~âÄ ¤¥¥!&&¦Jýgdd`Ê”)Ò¾¿¿?âããqêÔ)ܸq#GŽ”ŽÍ›7ÙÙÙÒþýû÷¥ÏuêÔƒƒC¹ãÕ­[¶¶¶Ò~zz:òóóKŸ1c®_¿ŽÈÈH\¿~¯¿þºtìÔ©SØ´iSù“pþüyÙc¥ÄÕ«W‹K—.ÉV:þòË/eç.]ºTVì›:u*=z„ÄÄD\ºtI6ï%K– 33C† Áµk×døà\¼xQjS*•x÷ÝwKÍí·ß~C^^ž¬-!!gΜ‘ö5]è¢ä<ÏŸ?œœÙ<ïÝ»‡+VàÈ‘#HOO—æY§N¸ººJçîÚµKåî·µk×¢ÿþðôôD½zõd ‰”\ÈäéÓ§*y•|<¹äâ)Úfoo/[(æèÑ£²ïËÐÐû÷ïǦM›pöìYÊîæ+V²\r®DDDD¯:üˆˆˆè¥†úõëKûãÇ—½kmΜ9˜\*dEGGcÞ¼yÒñ:uê ((HÚ/ù^¾û÷ï###÷ïß—î|óöö–Ž¿L¿:uê`Ô¨QÒþ¶mÛ°yófiÿÎ; Ã»ï¾ ???4oÞ\åñçÜÜ\ÙœZ´hQó‰½ ,øÑKËÂÂãÇ—öOœ8„„„àµ×^Ã×_-srr’¿ 443f̵ݸqS§NE·nÝбcG|ôÑG8}ú´,æÝwß•«ŽvïÞ 4oÞ8zô¨t<88¸ÌG‚K @÷îÝ¥ý7ÂÍÍMºKq×®]Ò±^½zÉÞ-8aÂYanË–-033ƒµµ5Úµk‡ääd騤I“dEÌ’ì>|øÖÖÖ°¶¶Æ@¶øFvv6>|¨Ñ|^„É“'Ëîò8p üýýѳgO¸ººÊîÞûꫯTîð;wî <+ ¶jÕêÅ$NDDDô°àGDDD/µ3f`РAÒþ£G°k×.\¸pAjsppÀÞ½{e¨ûòË/±jÕ*˜˜˜”;Vݺu±`Á¬Y³¦ÜØÍ›7£qãÆÈËËÃéÓ§eÒúúúbÅŠåöQÒÏ?ÿ,+:ݼyÛ·oÇ­[·¤¶€€lذAvž®®.~ýõW4oÞ\j+**Âýû÷eq ÀÌ™3emo¾ù¦l¿ø®Ââ1ÝÜÜdºFEEUhN5©aÆعs§ì.Ř˜DDDÈ/™1c† ¢r~||¼ô¹iÓ¦¯BLDDDô*ÐÕvDDDôê³³³C§N¤ý’w^ÏÜ(yÜÞÞ^v¼]»vÒ»õ¼¼¼dÇtuu±qãF 8«V­ÂáÇñøñcÀÑÑaaa?~<ÌÍÍKÍoäÈ‘ ŪU«S§N!==À³ÇZ_{í5tëÖ ãÆ+õ]nuëÖ•ÍÁ××ÇŽÃÌ™3±oß>$$$ I“&èÞ½;þóŸÿ¨¬Ð›ŸŸ/Û/ù¾A077ÇÑ£G±råJlذqqqxòä êׯ777Œ3ÇW»š¬——bbb°`Áüõ×_8{ö,ÒÒÒ`kk ooo|üñÇèÙ³§Êyo¼ñ–/_ŽE‹!11æææhÑ¢¥˜©x©R$tqq‘}/šVKòöö–η²²’sppõýü܃‚‚pùòe,X°»víÂõë×QPPøùùaòäÉèСƒÚq:$} «PÎDDDD/;…ºÙˆ^ …€´axx8&Mš¤ÅŒˆˆèEyúô)ôõõ¡P(*ÝGAA ÕÐ*ÛŸ®néÿ_ê¾}ûеkWÙ~É÷é©“““##£Jå“——§öŽÇÒäçç«!àÔ©ShÙ²%€gŹóçÏW*ŸA¼¼¼r¦B4hÐ)))ÐÑÑÁ7`gg÷‚²$"¢b%ßK{\ Í|ˆj>ÒKDDD¯ƒ*û€gwVW±¯¸?uîÝ»‡K—.aåÊ•²ö’ïÏ+Me‹}*TìTï8,æëë+=j|áÂܼy³Ò9Õ4…B¡ÑÏ4..NZ°£k×®,öQ­Ã‚Q 3f ¼¼¼d«È€µµµ–2ª¸É“'KŸŸ/\¾ŠV­Z%}þâ‹/´˜ QÍ`Áˆˆˆ¨ÅÆÆª´ 6ì•*ø½õÖ[h×®`ÅŠÈËËÓrF•—žžŽuëÖú÷ï/Í‹ˆˆˆ¨6aÁˆˆˆ¨†Õ(¶U«V044Ô¸ïììl¾ÓÂÂBܹsGÖ–““ƒ¬¬,ÇÒ[[[XYYêׯ###ØØØ”{Þƒ™™Y¡±RSS5ºþgÅÖ­[ÃÀÀ@£øÇ#--­BùÜ»wÙÙÙ:§¦¥¥¥iô=~üééé÷›žž^¡øÊÈÈȨö1lll`mm °´´„B¡€­­mµŽADDDDôOÂÑsœ1iÒ$\¼x055…••p÷î]•ø‡bîܹpwwG½zõàèè###Œ;yyy²Ø®]»bÈ!X¹r%4hggg$''ãÛo¿… Ú´iƒ›7oªŒqüøq4mÚJ¥–––puuEddd™óxôèkänÆåË—ÃÍÍ J¥NNN022‚‡‡222TbSSSDEEáÀ055•¶øøxµý·hÑcÇŽErr2ºté+++(•JôèÑC%öüùó²>MMM±uëÖRs·µµÅÔ©SqúôitèЦ¦¦°´´DÇŽqÿþ}•ø‚‚L›6 J¥ 6„µµ5:tè€F¡^½zeŽUž={ö`Ù²eenG­tÿÅ\\\àää$í;99ÁÅÅ¥Ôøï¿ÿ 6„••LLLàîˆˆ2ÇX¾|9acc¥R‰‘#Gª-jÅÆÆªü¼~ÿý÷2ûþûï¿áéé 333˜››ÃÛÛÇŽ+5>55aaa055Eƒ  T*áîîŽ9sæ¨ÄöìÙ¦¦¦8uêvíÚ%ËëêÕ«*ñ=zô€©©)âââ°sçNYüµk×ÔæãììŒÏ>û —/_F§N`ii ¥R‰·ÞzKmü¾}ûàááSSS˜™™¡I“&e¾ÓnïÞ½pww‡™™™|e;v þþþ077‡™™‚ƒƒÕþ{¯Œ’×§¾¾¾ô»‘ˆˆˆˆˆ*ô=çñãÇøë¯¿°qãF¼ÿþûøê«¯°cÇ„‡‡cúôéX±b…,>)) Û¶mÃøñãáëë‹ääd¬Y³Ë—/‡‹‹ &Ož,ÅfeeáÊ•+HJJÂÿû_Œ;ƒ B:u0þ||ðÁؽ{7Æ'sèÐ!ÁËË ß~û-,--1{öltëÖ ‡B›6mÔÎ#33÷î݃¾¾>„Õö¨ÜªU«0vìX 2 ,€››’’’póæM(•J•øáÇ#00‹/†B¡Àĉ¥cöööjÇÈÈÈÀýû÷Ñ·o_cΜ9HNNFÆ Ub4h -HqûömÌœ9S¥ÐZÒãDZ{÷n¬]»ãÇÇÌ™3±yóf,]º³fÍÂâÅ‹eñ³gÏÆœ9s0wî\„„„`Ë–-˜>}:ú÷ï%K– eË–}oêüúë¯Ø·o_™1£F*õg¬©¶mÛÊ~6~ø!<==ÕÆ~ñŘ5k†ŽaÆ!''áááèÕ«6lØ€Áƒ«œ³~ýzXXX`úôéðððÀÚµk±zõjèééaÙ²e²ØFI?¯k×®aþüùeÞíöûï¿£OŸ>ðõõÅÊ•+a``€Ù³g£cÇŽˆ‹‹Ãk¯½&‹ONNFË–-¡P(0kÖ,´lÙˆG³fÍTú3f BBB0wî\˜››côèÑÒ1 •ø±cÇ¢OŸ>˜3g,,,Êž]ÏwîÜAHH5j„ùóç#11ÞÞÞ*±;vìÀ›o¾‰Ö­[cÍš5ÐÑÑÁìٳѾ}{œ9s²øíÛ·#44~~~˜:u*ttt0kÖ,´oß§OŸV‰¯ŒmÛ¶aÙ²eøè£0þ|¬]»?þø£ô;Qœœܺu 6,÷wO`` ÜÜܤýO>ù„?""""¢ªBpãöJnlˆâ-<<\.»wï^äääÈÚÖ¯__åBTÓ¦MaaaððpŠEîîî°··Gxx8ž)õ¼¾}û¢~ýúð÷÷‡µµ5d/÷Yýù矨[·.Nž<‰›7o"##EEEøã?ðÖ[oáàÁƒÚN±Z­_¿-[¶DÏž=akk sssL˜0#FŒÀ¬Y³Ôžóþûï\\\`aa‘#G¢uëÖX¼xq•óy÷Ýw±dÉDDDÀÚÚ...066†«««Ú…š6m +++899•Y, …žž|||4Š×ÔèÑ£±hÑ"ìܹ–––Ò|ÝÜÜðõ×_«Ä3 .ÄŽ;TâgΜYê8/^ÄåË—±k×®*ç\ÒøñãѦM(•JÔ«WAAAðòòÂæÍ›¡££S­cQé/óc@DeQ(¶î‡cÒ¤IUî÷È‘#°²²Ry þåË—ñðáC´mÛVåœüü|ìܹÙÙÙhß¾=œ‘ššŠ .ÀßßFFF€¸¸8( iÅÓ¸¸8èèè iÓ¦€“'OBOOOÚ/)55ÇŽÃåË—¡¯¯'''tíÚuëÖ-u.‡‚M©/ô¯ŒÜÜ\ìß¿IIIÈÎΆ­­-Ú¶m«Ñ*¤999ؽ{7îܹOOOB___%îøñã011‘VzÕTvv6Nœ8ooïRW&=tè4h ò.º‹/âñãDzGP…èÛ·/ °eËÙw––†† â½÷ÞÃ’%K*”çË®°°GŽÁ™3g`hh___øúúªŠŠ‚ƒƒœœœpêÔ)DGGÃÍÍ ;w–®ûÒDGG£]»vؾ};úôéSfì;wƒ„„ÂÅÅAAA044TŸ––†˜˜\ºt &&&hÕª¼¼¼Ê,:eggcçÎxð༽½Ñ©S'èê–þª[Mã9ß<[}¸x¾FFFpuuEPP 4ŠwssÃ믿^j|QQ6nÜRW5.**ÂÁƒѸqc8::ÊŽ9sùùù*é>|ø{öìÁÝ»wahhˆfÍš¡}ûöÕº‚7Õ%Ÿ’8.„xù_LLôŠ`Á^Y5Uð#*ÄÆÆª6’““Ѹqc,]ºcÇŽÕR†¯¶õë×ãwÞATT:vì¨ítˆˆˆˆècÁ¨æð‘^"¢RØÚÚBWW_}õ•ô¾Ä¼¼ €Ã‡ã÷߇——ú÷ïßÿ½ZÇòõõEÏž=ѦM›jí·2† ‚éÓ§k; """"¢,]m'@ô"= …Ë—/×v*¯¤¢¢"|òÉ'hÔ¨> 333éX›6m0aÂ-fG/»üü|üë_ÿ‚——¢¢¢`ll,k×®3«©©©Xºt)ºwïŽü …À³+Mš4Á¬Y³Ð«W¯jÏÍÍ­Ú‹ˆDDDDDôjbÁþQž‡ &»N6n܈۷ocúôé²b_yV®\ Jó555E»ví¤Ÿ‘‘M›6áÌ™3022’ò×ÑёŠ!ð¿ÿýmÛ¶…ŸŸŸìØÎ;ñøñc :T%Þßß^^^ضmNœ8{{{ 8®®®²>~øáäççcܸq²ßfff}z•ÆB{{{1räH1gÎ@(•J@têÔI%öìÙ³ÂÍÍM‰N: ???¡««+Úµk'îÞ½«6ÞÕÕU¯££Sj|hh¨°±±zzzÂÀÀ@6ׄ„YìèÑ£‘ššZ¡ùÚØØˆ±cÇŠ3fÈæÛ¥K•Øøøxáìì,ŒŒŒDçÎEëÖ­…ŽŽŽèر£HII‘Å bÆŒ*ýôîÝ[x{{ËÚ 1pà@áææ&EPP¨[·®077'NœÅ‡…… …B!rrrTúÿ믿±nÝ:©-//Oƒ ®®®²þK»>£££¥ïÛÜÜ\üñG©ßåýû÷E§Náää$\]]* Tâ Å”)S„ŽŽŽ°³³ÁÁÁ" @Ô­[WLž88XØØØ]]]ahh(»Ôýû%""¢6Qâoºcâ%ø;“·Ú²i=nÜ*»ÕtÁÏÕÕUX[[‹ˆˆQPP >^sçΕڊ ~ …B¬ZµJj?wîœ033Íš5+åzæÀåüÞ}÷]¡¯¯/"""DQQ‘(((ááá€øþûïUâ¿ûî;@|ñŲ‚`aa¡ÚBf1Vf¾DDDD,øqãVs›ÖàÆ­²›&¿Q£F iÓÓÓzzz²¶ &¨œçíí- ˼ƒN!"##±`Á•cÿþ÷¿Ù]?û÷ïÄÿû_•ø/¿üR±±±jǪhÁ¯&tìØ±ÔFY\\\D½zõdE*Sð2dˆJ{ñÝrGŽ)o:Zgoo/ŒeE9u~ûí7@¬X±B娤I“„B¡—.]’Ú¶mÛ&ÈŠYÅ&Nœ( …¬V’&?___µ?“òØØØ•;Ÿ·yóf@¬]»VåØ| êÔ©#+ÚW¶à7fÌ•øÏ?ÿ\§OŸ–Úš4i"ÜÜÜÔæzóæM@Lœ8Qj+.ø7®ÔþË*—Wð»v효S§ŽJá¶°°P888Y{vv¶011ݺu+uÌÒ°àGDDDš`Á·šÛ¸J/Õj#FŒÀ²eˤÍÏÏþþþ²¶·ß~[í¹½zõ‚¯¯o™ýŸ:u зo_•co¾ù¦,¦2ñ/CCC<}úTÖ6mÚ4HÛÎ;ÕžÛ§Oøøø”ÙñÜûôé£r¬:¿KKK•¶âw ^¸p¡Êý¿ýû÷‡§§g™1qqqJÿ>…RLeâ+JÝõóÑGÉ®Ÿ}ûö©=wàÀpss+³ÿ²®Ÿ~ýú¡¨¨ñññ•Ìþÿhzýèéé!??_m ö½ˆêúïÖ­ !!¡â ÿgΜAQQ²³³1eÊi›6mLMM‘˜˜ˆ¼¼<)þÂ… *ï1$""""¢Wí Z­¸ˆPl÷îÝP(4hP¹çÖ©S~=üÚµk€† ª+~i~qLeâ_6öööÈÎÎFff¦ôòý† ÂÓÓ>ÄîÝ»‘ššªö\M¿O…B;;;•c5ýýyûöíJ÷ѧOŸR žÅ>úè#,Z´¨ÒcÓôûÔÓÓƒµµµÊ±âïóúõë²x}}}XYYi_Qööö8vì ¤B—ƒƒÒÒÒ’’‚={öàÁƒjÏÕd¾×¯_‡‘‘‘ÚeœœœªœYÔ]?¶¶¶HLLT_<ϲÝ)©Aƒ€”””J瘜œ àÙ‚ Ï>íìì`gg‡ôôtéz¹téÀÅÅ¥Òc‘v°àGTÅ…‘´´4ÉŽÿA_²xR2¾øø²â_6žZ·n øàƒ'OžÄîÝ»«Ô¿••„HOO‡………ìXM?>ØØØTºéÓ§cܸqeÆ4nܸÒýW”••òóó‘••…úõëËŽŸ%ï&³´´D^^²³³Q¯^½rã+ÊÁÁEEE¸r労"òÇ ˆŠŠÂž={*Ýwqn¹¹¹ÈÍÍ…¡¡¡ìXuä_u× 233ñàÁ•q‹‹oš^oÅù«+†kª¸¸øõ×_£S§NåÆÿ[«J‘‘ˆˆˆˆˆ´ƒôU»»;àøñã*Ç:$‹©Lüó +Ÿl5 üôÓO5Òñ#›ê¾ŸƒPý~tttÔÞU(„(uœŒŒ •¶£G\]]5Oø9¾¾¾.sóðð¨tÿUÑﳬ보¦¢¢¢2ó)~Ô¶¦®www!£rL]þ … …¢Æ®Ÿ®]»vìØ¡¿}ûvèêêâõ×_רÿ#Gލä_QÅEÖ¿þúK£xooo@DDD¥ÆÓöï+""""¢4m¿D·Ên¨Ä*½GG-7ÎÛÛ[ 0 Ü¸¬¬,akk+Z¶l)ž¸¸¸h´:òãÇ…¥¥¥­úðáCáìì,<<V¬XQå9‘´]q䯭²*q‡Ÿ¦† &¾úê+ã<(ºté"¬¬¬„­­­èÑ£‡ˆ‹‹+5>**ªBñB<»[hÖ¬Y¢I“&ÂÔÔTøûû‹… –Ÿ-^ýu1~üxç¡©´´41~üxáçç'êׯ/E¯^½Äßÿ­6>,,LÌš5Kãþ###Å믿.,--Eƒ D¯^½Ä™3gJÍeðàÁÂÌÌL¸»»‹ñãNjNj3fˆªÄЉ'ŠcÇŽ‰Þ½{ 333ѪU+±aÃóÓ¶ˆùóçkÿ矊ÀÀ@aaa!6l(z÷î-.\¸Pjüž={Tâ/^¼Xæ™™™â«¯¾ÂÌÌL´mÛV,]ºTmìýû÷ÅØ±cE«V­D½zõDãÆEŸ>}Ä‘#GÔÆ÷ë×O,Z´HãùþñÇ¢sçÎÂÜÜ\ØÛÛ‹RïtMMM¡¡¡ÂÔÔTxyy‰O>ùDdee‰©S§ŠáÇËb‹ïðûüóÏETT”èÑ£‡ôoqëÖ­¥æó矊ž={ KKKáìì,,®]»¦—ŸŸ/:uê$Ö¬Y#8 ëûöíåÎ[“;ü„xvò‡~(š6m* …™™™ðóó?ýô“Úøœœñå—_ abb"œœœÄ€ÄúõëK#--M|ñÅÂÕÕUXXXˆ:ˆU«V•;"""úgá~ܸÕܦ¢ô÷½Ì …-€»Åûááá˜4i’3¢W‘‘ÆŒƒÅ‹k;zÅBWWŸþ9fΜ©ítàŠ(È IDATTìØ±}ûöÅþýûÕ¾ˆˆˆèePòýÑÇ…ÕûHÑ?í ""ªΟ?àÿVã%""""¢®Úó²*""¢˜¡C‡"##عs'<==¥Õx‰ˆˆˆˆèŸ‹wøÑ?ŠŸŸœœœ´½¢üýýáàà í4$Í›7‡R©Ä£G0jÔ(ìÝ»·Úè!""""¢Wïð#¢”¨¨(m§@¯(;vLÛiÈ|òÉ'ÚNˆˆˆˆˆ^B¼Ãˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨Q!´Q¥( w—‹÷ÍÌÌ`ll¬ÅŒˆˆˆˆˆˆHS÷îÝC^^^ñîm!„½6ó!ªM¸J/½Ê K¥!--M[¹Qå™h;¢Ú„ôÕ"¼Ã^e…%wŒQ·n]måBDDDDDDðèÑ#äççïh3¢Ú†?z•=,¹3cÆ Lš4I[¹QàøñãÅ»—ËŠ%¢Šá#½DDDDDDDDDµ ~DDDDDDDDDµ ~DDDDDDDDDµ ~DDDDDDDDDµ ~DDDDDDDDDµ ~DDDDDDDDDµˆ®¶ ¢WOQQ6oÞŒãÇãܹs011››† ‚&Mšh;½WTT„Y³fá_ÿúŒŒŒªÔ×çŸ޶mÛ¢W¯^Õ”Ý˯°°7nĉ'pþüy˜™™ÁÃÃC‡…‡‡‡¶Ó#$$$`Ú´iøê«¯´ö{íâÅ‹8räÞ{ï=­Œ¯ÎÓ§O±aÜæË$??ƒÆÖ­[‘ŸŸ_åþ,X€ÈÈÈjÈìÕðøñcôìÙC‡EDDÌÌÌžžŽåË—ÃÏϹ¹¹ÚN‘^cÇŽÅ‚ jtŒèèhlݺÿýwŽS–¼¼<|ú駘?¾Ör()%%xï½÷pàÀXXXàþýûX¼x1Ú·oÿÿØ;ï°¨Žïÿ¿—Þ«H* È"*ˆ  ‚ b%klQò1ÖØKT¬Q£QcĒ؈bCKŠ‚`A)‚€H‡ßþv¿¬÷î²Ø2¯ç¹Ï3gΜ¹wîì½çΜÇûØ&R( …B¡Pþ?t†å?Å™3gÀápпÿmJ›sûöíVqH½Ë¤I“pêÔ)lÛ¶ ãǤ×ÕÕ!99êêê­^ç§DTTnݺ…ÄÄD¨©©}ls>;"""pñâEìÝ»#Gޤ×ÖÖ"%% Ñ:ÊçBBB***Ú´ŽQ£FÁÖÖ\.·MëG×®]qêÔ)xzzÂÔÔ¡¡¡Í AJJ bccáïï/H¯¬¬ÄÇ!%E¿#S( …B¡|*Ð'3ÊŠ}ûöaß¾}Í*SXXˆ×¯_7«LYY™D3•Þ¼yƒââb‰õVUU!;;»MyP^^.ÖžçÏŸãÀrö€¬¬,œ%ª§´´555MÊ7ûÜ7—ÂÂB‰Ïç¥K—°qãFlݺºººmbOEE^½z%‘ìëׯ%–mLii)jkk%Ò_RR"±Þ¦f÷ddd 66‘‘‘BÎ>““C÷îÝ%ª§¤¤D"û‹ŠŠP^^.‘Î÷¥  õõõm¦¿¼¼¥¥¥ÉVTT ;; é%„ýߥ¥¥Û4ÝÞ¥¬¬Lbû[«W¯$ºç¥¤¤àìì YYY‰u—••I4˹®®yyyBi¨ªªbÈöèÑ?üð&Mš„üü|‰miíÙv‰‰‰HHHÀÌ™3…œ} ¤¤$±c´¸¸X¢{¦  •••ïe«¤äçç·y£P( …BùXP‡…ÂBii)"##¡­­ ]]]hhhÀÌÌ ÿûßÿXå]\\ðõ×_#77>>>ÐÑѺº:úõë'ôRÍ')) öööPWWG»ví`nn.rYgQQV¬XKKK(++ÃÄÄJJJ˜8q"«Ó#<<ˆÇÕ«W¡¡¡!8þùçÖ:Ñ¥K=¸téCî÷ßG}}="""Äœ=&ŽŽŽ?~<²³³Ñ¯_?èèè@MM d•ÿõ×_Ñ¡C´k×êêê°°°@ll,«¬¾¾>~üñGFzdd$ºtéÂH×ÓÓÃ?ü€Ý»wÃÔÔºººPSSÃ7ß|ÓäKhTT¼½½Eε··Ç´iÓD–_¼x1´µµY¯Û¿ÿþ 777hhh@[[}úôé|½rå lmm²:uµk×DÖkkk‹iÓ¦áñãÇðôôD»ví ¦¦†ÀÀ@VùË—/ÃÆÆÐÒÒ‚ ®_¿.R?äææÂÐÐ!!!"eöìÙ×ìþcee…™3g"33}úôÜ_ÁÁÁ¬ò›7o†@ÎÚÚgÏžeÈñx --U«V!""îîî ÙÙ³gcÊ”)ðõõÅ¥K—pòäItèÐÄüÁj7ÛlÜŠŠ ‘ýgË–-ؾ};-Z„«W¯â«¯¾Â¦M›0oÞ¼fµ‹²²2:t2d.\¸€ˆˆœ?k×®eÈß¹s—/_ÆÜ¹s‘˜˜ˆ;wÂÑÑ«V­bÌf~ýú5þúë/ÄÅÅaÉ’%ÈÈÈÀ AƒpãÆ ,X°qqq ‡ÓÑ£G1pà@hkkc÷îÝØ³gjjjЫW/±×º¤¤EEEbe`ùòåøßÿþœX³fHÝÀÛÙhµµµŒþ#++‹iÓ¦áÙ³gHLL«C‚‚‚ ­­-”6nÜ8”——ãÆ-Ö333Áÿ222ptt”8Þ§ðìÙ3Fž««+!##===ôìÙòòòPTT„¶¶¶Ð˜rëÖ-deeaìØ±¨©©ôMMMôíÛ.\k‡±±1äååÅʘ™™!==ûöík³Ø¤0hÐ Ö¥ÿ­Á°aÃ`ll,ø_QQ]ºta\/AšŒŒ ´´´$º¶†††þo,j Ælâ–PVVƸ…î_Qí ir×ø?þø„DEE ¥+))aòäɸ{÷.^½z%[kÙ²e8p à8sæ k]–––MîàÈ·§sçÎŒ<!™ÖÆÆÆÀ[§,¹¹¹PVVëÀ°°°ÀëׯQ^^Ž“'O"%%/_¾Äï¿ÿBrssY~l¨««ƒálü¶/]º”q½Ž=ŠššV'ð6ž8g_cý‹/fè?v쪫«Í÷A__………BióæÍê?¢ºÖÖÖMêÏÌÌ„””lmmyªÿHêŒi.êêêb­IIIؼy3¦NŠàà`‰6÷‘‘‘û?ÿ\ýïÿcô‡sçÎ –í¶„µk×ÂÒÒ#GŽ„žžFŽÙ*NýwiìPÿ¨©©1î_iiixzzâàÁƒ‚q&66™™™í¯££àíXô1`»§L™"tÿþû↓e%9ÿ™™™ÐÔÔ„¾¾>#¯­ï_¾}¢Æ …B¡P(”Ï™¦E(”ÿü(ýoMø±Â¢££áêêÊ*Óxfߦ!C†àâÅ‹àr¹èÚµ+ìíí‘››Ûbçßž 6ˆÜa×ÄÄDðw‡ÀãñŸŸ/˜mòóÏ?ã§Ÿ~½{÷Ð"{***Àáp   ÀÈSVVÈ´|Gž¨x]ššš¨ªªÇ鸴°°ðv&ßæÍ›1tèPtèÐ[¶lÁ!CPSS#±Ã þõÚ¶m›È8Šâf Jªÿ—_~aÝðÚ·oÿÞú;tè€ŠŠ ¡™B¿ýö*++‘””„°°°÷Ö ¼írrr ÇðáúO[ÎRcãÅ‹ ÄíÛ·áêê ;;;tëÖMl,IIá÷‡ßÿ]зßåÝ¿ÍÅÀÀÉÉɈÇ_ý%ØIÝÛÛÇŽ\·/…íÛ·ÃÓÓ–––èØ±#?~Œ¯¾ú “&Mj²,¿ïjjj¶µ™¬áîÝ» „>œ8q5558}ú´DmGEE”””Xó¾Ôû—B¡P( ¥-¡? ¥üYD÷ïßoSýG¤Ãï]¦L™‚Ë—/#>>žžž‚ôÇ·ØáÇ·GJJJ"{øËœÏŸ?1cÆx;ëDGGGâå†â°°°!Ïž=c8Æø sssF¹¦vוþÌQéÁãñðêÕ+´k׎UÆÜÜçÏŸG\\¡¯¯Õ«W 6Ph‰Ã½ddd$î?Ÿ’~þÌ»óçÏ vóå;EÍLl¨®®F^^c–ÐÇ2ïÒý‡?¶¹´”1cÆ 55wîÜ£££ ýêÕ«"w­•~““k“þÀ‡Ãá oß¾èÛ·/–-[†Ÿ~ú ‹/ÆæÍ›EîŒþ¹RUU555„„„ _¿~ppp@÷îÝ%*ûòåKZ>ü!±³³ÃñãÇqùòeôéÓ÷öaK077ÇùóçQSSØI-îþm 'ü÷ƒ…B¡P(Êç ]ÒKùOa`` ?é]ôôô`gg‡­[·¶Šë],,,СClÚ´I(6ž8®\¹WWW!g_~~>뎾‘••e,¿z+++bãÆíü8lØ0hkkcûöíBËz[‹^½zöîÝËÈÛµkddd;%«¨¨0â.UTTˆátáÂÆL‘˜˜±ŽOþ޶ÉÉÉ"õ*))A__Ë–-ƒ££#\\\`llŒAƒaÅŠ’’jÑ ¥Ú·oõë×·Š“ê]:wî DGG·Éõ3f TUU±uëVÆrÇÖ€¿ë'[ÿÙ½{7äååÑ­[7Aš””ý§¬¬LìÒ¾³gÏ2îߘ˜ÈÉɽW8€–€ 9ûž>^pþ·³wNœ8Ñ"ÛÀÇÇXºt)Î;à­“%::ÇŽôiÓ3ÿ,--‘€;wÄÄD¸¸¸ˆ={÷î]Œ9Ïž=ÇÃÎ;qðàA„……‰\R]UU///|ûí·-ngc:tè ˜MZQQ£GÂÝÝõõõbÇIÐÔÔÄòåËqóæM„‡‡£¤¤ÀÛsš™™)Ö)ZXX777¡©l¤¥¥ {X¸p!*++áææ&²œ‡‡®\¹"£ž>}Š›7o6§yœºº:¼zõ GŽAÿþýáë닾}ûÂÍÍ øûï¿Å–?qâ|}}%ZVŸ››‹ž={6ù{׌ŒŒðõ×_ãÏ?ÿĸqã¼Ü»wOp¿µ„°°0téÒßÿ½àw«¡¡óçÏÇÕ«W1gΓx; ..÷ïßÇÃ… àìì,6¶ä•+W0~üxäåå¡¡¡kÖ¬Á¹sçÙfá<( …B¡P> {›`zÐã}zø¿-ÜItt4i-þúë/¢¯¯O}}}Ò¾}{€¸ºº²ÊÛØØÐÐP‰õïÚµ‹¨ªª‡C:vìHTUU 2`À†ìµk×ˆŽŽ@¤¤¤2oÞ<²~ýz€±Ö‘‘‘!ÐË/onnÎ*»cÇ!{TTTâççÇ*¿iÓ&"##CEEEÁ5ððð >dÈ›››“#FHtn Hß¾} Ò¾}{¢©©I‘#G’ŠŠ †üÉ“'‰¬¬,@455‰œœÙºu+6l166fÈ+((I“&‘ÐÐPÂápú¹\.)((kÛÊ•+‰²²2yþü¹H™±cÇ---RYY)HãñxÄÚÚšøøø0äeeeÉwß}ÇH:t(133c­cË–-DII‰p8bffF”•• Â*ohhH"##Ŷ­1›7ofÕ/®ß¼y“ ¦¦¦Mê_µj‘––fôòôéS†¼®®.™0a‚D¶çææ777€èééuuu€Œ7ŽTWW3äÿøã-šššDQQ‘ìÙ³‡øûû[[[!Ùúúz€Ìš5‹øûû õWWWòêÕ+‘veddÚ)ªÿÔÖÖdþüùŒ<___Ò¥KFúÉ“'‰†††`|àp8dÆ dΜ9DVV–ðx<¬‰‰ ú̘1‚ÿuuu…þ'ämß]³f ‘——'RRRÄÌÌLp;þúk‘í½xñ"@ìììDÊBȰaâ­­Mìí퉴´4‘––&S¦L[.!!(**‡C´µµ Ò»woVY%%%%VgÏž%H||M'''‰ä%¥¡¡üðÂ¾Üøþ "ùùùŒ2RRR¬}šG€ÂápHTT©¯¯gÈoÛ¶p8€hhh555rìØ1âêêJ<<<„dóòò²xñbÒ»wo"---|||È›7oÞëœP( ¥e¸¸¸~KÜ ŸÀ{&=èñ¥B>Ì2 ¥µáp8zòøÿGGGcúôé­¦¿¼¼IIIHOO‡‚‚œœœÐ¹sgÖ nݺeee ê« IDATÖÝAEQXX(Ð/++‹Ž;¢_¿~¬Aêëêê‹ÊÊJ¸»»£cÇŽ(((@ZZ\]]Y7¹ÞÝ?uêrrr`mm ///‘3Ì ””„‡BNNN`¨ êøçŸðàÁhkk£sçÎ"—3&%%A]]]â2y<®]»†»wïBVVŽŽŽb—JÞ¿.\€ŽŽ<==¡§§‡´´4”••1–è***büøñˆŽŽFrr2`nnOOO‘måÓÐЀ^½zÁÐÐPäL¿§OŸ¢¢¢‚±Ópff&çàÊ•+044dÄ&|ðàÊËËK˜ùäååáæÍ›xøð!äååann޾}ûBQQ‘!›˜˜mmmXYY‰mß»ú“’’‘‘˜™™¡_¿~"ûÄÇÇ£cÇŽű{ñâ’““‘––†öíÛÃÞÞ^°ç»\¿~:::Ç?¬¯¯ô%%%8::‚Ë劔OIIÁ¥K— ¯¯>}ú }ûöHMMEUU•Вˆ†ÈÈÈ`îܹX¼x1nß¾ÄÄDX[[£OŸ>bÏ ðvŒZ¹r%òòòX7~!„àòåË055eÌ4½wïjjjXª*=zÒÒÒpssƒ‘‘rrrðèÑ#¸»» v¿qãÔÕÕ; ߸qššš‚X}ׯ_‡¶¶6ëŽÈ999¸yó&233¡¨¨KKKxyy‰Ýµ:..ÖÖÖèСƒH™7oÞ ..YYY¨®®†¾¾><<¶9 …BùÿP‡…ÒvÐ~ …Ò ÌÍÍ‘€3gÎbR(M¡  €íÛ·SgßCCCp8Ì™3G¿²ªª {ö쯯/ ???¡2çÏŸ‡‚‚Î;G} …B¡P(”fA~ …ÒLLMM‘œœ,rƒ å]TUUáååõ±Í |D±k×.œ?PSSl„4eÊ=z”r! 'Ožl2Ü…B¡P( …ò.Ì`d …òó×_‰)&)l±Â(_6RRR8}ú4#Þ"…")cÆŒÁðáÑ‘‘¢¢"ÁÄÄ„56,:Ö´ZZZ8}ú´ †%…B¡P(Ê—uøQ(”ÿÞÞÞÛÊg ‡ÃÁ€>¶”ÏYYY±›PPÚ999zÿR( …BùOA—ôR( …B¡P( …B¡P(_ÔáG¡P( …B¡P( …B¡|AP‡…B¡P( …B¡P( …òA~ …B¡P( …B¡P(ÊuøQ( …B¡P( …B¡P(_ÔáG¡P( …B¡P( …B¡|AP‡…B¡P( …B¡P( …òA~ …B¡P( …B¡P(Ê„ÌÇ6€B¡|ÔÕÕáÂ… Éöïßç½ê9þ<ŒŒŒ`cc#‘|rr2 ÿ[XXÀÂÂâ½ên—/_†‡‡G‹õœ;w&&&°¶¶n«>²³³qçÎÜ¿ššš°¶¶†§§'¤¥¥?¶i  ´´ûöíCXXÚµk÷±Íi7nÜ€´´4ºwïþ±Mi3Š‹‹qàÀ >šššÛáàÁƒ1b444>¶9‘““ƒÔÔTFº²²2ÜÝÝ[¤»¼¼×®]ÈÉÉÁÖÖzzz-Òù9‘ŸŸÃ‡côèÑPUUýØæ //GÅèÑ£¡¢¢òQlÈÉÉAuuõyfGaa!þùçŸ&å444àêêú^uðû¿ƒƒƒÄýþâÅ‹¨©©üß½{whkk¿Wý å? !„ôø,zÿˆŽŽ&”ÿãàÁƒdÿþý­¦¯¨¨ˆ4>ß⎺ºº÷®GII‰L™2EbùÁƒ Õ½`Á‚÷®[R6mÚD´µµI^^^‹uÉÊÊ’ï¾û®¬ú|X¼x1áp8i×®‘““#ˆ©©)©ªªúØæQ>vîÜINœ8ѦuüòË/Y·n]›Öó!èÞ½;ñòòúØf´)6l ÈÖ­[[]÷Ï?ÿLΞ=Û¬2ëÖ­#È/¿üÒêö´Û·ogýM·²²j±îääd†^}}}²qãÆV°üÓgÉ’%‰‰‰iuÝÑÑÑäÒ¥KÍ*óã?äàÁƒ­n¤ìÚµ‹èêê’”””f!„ÄÆÆJôlëììüÞuðûsÎwûöí…êoîô9áââÒ¸­7È'ðžIz|)áGùO‘ 066þÈ–´=»víB]]† Ö*ú´µµQUU%”Ö­[7¨««#>>^(]FæÃ -‡BCCjkk¡®®ÞæõíÞ½³gÏÆùóçÿS³Z‹~ø+W®Ddd$-ZBœœŒÄÄD(((|l)ŸÑÑÑàr¹4hP›Õ1dÈ””” ,,¬Íê ´aaa¨®®Æ!CZ]÷Ê•+áçç‰Ë„‡‡£®®AAA­nO[ÔÕÕÁÊÊ cÆŒÁÂ… !++Ûju,X°#FŒÀÝ»wƒ©S§"..Gý¢gxGDD@NNluÝ‹-Âøñ㛵ê`ìØ±PRRBÿþý[ÝI‰ˆˆÀ£GЧOüûï¿íÙ|РABÏ·¯_¿†®®.¾ûî;,[¶L.%õa#aeggƒ‚Ë—/cÀ€´n …òå@~”ÿsæÌ‡ÃÁï¿ÿþ±Mù,y×#%%))©ê¤‘••…¬¬ìyËÊÊ´iÓ°bÅ ôèÑ£ÍëûÒxõê6mÚ???ìØ±CÎápàèèGGÇh…"Œ¶¶6¾ÿþûmEBÚ·oÿI]/]]ÝOÊIPQQŠŠ jkkjjj055mÕ:´´´`ii KKKcÞ¼yX¶lvíÚ…o¾ù¦Uëú”044ü¤úƒ‘‘¢¢¢>¶X¶l®]»†ˆˆÄÅŽw8˜–ðîs,¿ÿËÈÈ|Ôç[yyyo—ÀS(ÊûB7í PD°oß>ÃÖÖNNNøú믱oß>VÙ)S¦`Íš5¨©©ÁòåËáììŒÞ½{cîܹ „0äKJJ0uêT8;;£K—.;v,òóóEÚ’˜˜ˆo¾ù®®®033CŸ>}pèÐ!VÙåË—#((ÉÉɸwï‚‚‚Gff&k™W¯^aÊ”)èÞ½;ºté‚ÈÈH¡¸x-åÚµkˆŒŒ²ÿðáÃbËÜ»w°³³ƒ§§'~ýõ×V³§¨¨ß~û-œœœ`ooqãÆáåË—M–›1cŒ1iÒ$Öü±cÇbÛ¶m"Ëïß¿ƒ>£GF÷îÝaff†~ýúáôéÓ ÙqãÆaÛ¶mˆ‹‹ƒ››Æ‡úúzœ>}½zõ²eËPWWÇ(÷âÅ Œ7\.\.S¦LAYY™Ø¶”––"$$[·nm²Ý‰‰‰9r$¸\®Àþo¿ý ÙY³f!((ÙÙÙ¸té’Ðø&ª†……á·ß~Cii)æÍ›x{{cÍš5 ÙÿýWHgPPîݻǪ·²²AAA8r䊊Š'''8::bÍš5¬c?>|^^^°¶¶ÆðáÃ1þ| >ÎÎθ|ùr“ç«)Ö­[‡^½z Æ6q¿-ÀÛY$cÇŽE×®]Ñ­[7̘1oÞ¼)õêU„‡‡ÃÁÁæææðööÆ´iÓD¶·¡¡ëׯ‡¿¿?¬¬¬Ð«W/Lœ8gΜa•2d8€¢¢"üðÃèÚµ+ú÷ï72do޼ɸ^>dÕ[ZZŠ   œqqq …½½=,,,пÌž=[¤|mm-V­Z___XZZÂÝÝß~û-.^¼È­®®FPPbcc‘——‡¨¨(téÒ¾¾¾B±ø¼;Fáùóç¬vð¯Ñ… pîÜ9„††ÂÚÚþþþ"Ç…ˆˆ¡¢¢Gª‡øøx†=¹¹¹"Ï œ>}ÁÁÁ°¶¶†››¢¢¢P^^Î{þü9‚‚‚péÒ%dggcÊ”)èÚµ+œ›|~ûù矈•ãóäÉøûûãØ±cÉ·õõõ8räBCCáààKKKøûû7ðäÉ“ µµ5ZeÜçsïÞ= >¶¶¶èÑ£~üñGÖßu …òß:ü(”w¨««ƒ¿¿?FމÚÚZ >¾¾¾(**ÂãÇYË\¸p·nÝ„ °bÅ èèè ¬¬ ©©©Œ¯•Ïž=ƒ££#¶mÛSSSôîÝû÷ï—Ë,9nÌÙ³gáåå…’’øûû£oß¾HIIAXXbccòrrrPPP€´´´à«%ÿ`›÷ôéSp¹\üòË/033ƒ»»;öíÛ.—‹œœœ÷<‹ÿÇ©S§Ð¯_?”••! ^^^ø÷ß1tèPœ:uеLbb"|||P[[‹ððp”——#22?ýôS‹íyôè¸\.vìØKKKôìÙ{÷î—ËE^^žÈrùùù8yò$F%rÙQZZšÈ—XàíÃöµk×_kÓÒÒàää„›7o –m,Z´[¶laèxøð!¸\.víÚkkk¸¸¸`÷îÝàr¹"´gÏžÅ?ÿüƒÑ£G#::(,,DFFC6==\.»wïF§NàììŒ]»vÁÑÑQ¬SôîÝ»øóÏ?±nÝ:‘2<ôéÓG¤ gΜArr2Fމõë×ÃÐÐùùù¬ìC‡¡gÏžHMMÅСCáé鉓'OÂÁÁõ!<66éééŒô¤¤$Æ&5„ÄÆÆbÓ¦M4h8†Ž‚‚„††"&&¦Yíz¾þƒ¢[·n¸{÷.üüüP[[‹¹sçb×®]Œ2ûöíC`` êêêŒ^½z!)) ¾¾¾¸~ýºì¹sç°ÿ~Lš4 :::رcfΜ‰éÓ§CKK óæÍc¼x¤¤¤€Ëåbß¾}èÚµ+°mÛ6tëÖ ¥¥¥"Ûrûöm9r6lÛæ£GÂÍÍ <@¿~ýðÝwßÁÖÖÕÕÕ¬÷™¼¼¼`,“––ßDÍ 9qâRRRŠ­[·ÂØØÙÙÙÈÊÊbÈ6ÖYZZŠØØX±ê­««Cll,öïß'''¤§§cРAxóæ ¢¢¢X_^·lÙ‚¡C‡B[[Ó¦MCVV–.]Š‚‚ôìÙºººbÏWSLŸ>3g΄¼¼<† †—/_ÂÍÍMäØpëÖ-p¹\:tÝ»w‡ 6mÚgggTTT0ä÷ïß>>˜>}:¬­­QWWÇzþ‹‹‹áââ‚Y³fA]]‘‘‘pssÓ'ODÚtüøqܽ{ÁÁÁعs'LMMñäÉVGIãëU\\ŒØØXVgðÖY‹U«V!,, ***6l=z„Aƒáï¿ÿf”á÷7‡#˜í#®¿5¶§¨¨H¬=°yófôë×ÏŸ?ÇÈ‘#áâ₽{÷ÂÁÁOž<’­ªªBll,öîÝ‹îÝ»#++ þþþ‚v'NœYÏÕ«W+öƒÔû°sçNôíÛÏŸ?‡ŸŸ¦OŸ ‹f9deeŽé´´4¡¼Ë—/ÃÉÉ Ç‡››LLL°zõj¸¹¹±~8Û¾};¼½½‘›› ???L›6 æææ"íyñâºuë†ùóç£}ûö7n\\\žžŽW¯^1äëëë‹{÷îÁßß¿ÿþ;ÌÌÌ––ÆêXoÜòóóËê,€7oÞ 66‹/Ƙ1c ­­°°0¤¦¦¢ÿþ¸rå £ _7Fÿd£±=yyyˆëÜ_±büüüðòåKŒ3\.Û·oG·nÝÑÊËË‹_ý®®®ÈÏÏG@@òòò)Ö©ekk GGGüòË/"e‡“'ObçÎÉ· ,Àĉ¡ªªŠððpØÚÚâìÙ³pwwéHݺu+&L˜===„††"99ýû÷gu07—“'OÂÅÅgΜ··7Úµk‡E‹ÁÛÛ›õƒ!…Bùð±ƒÒƒï{@‚M;ž={F’““‡¯¯/ñóóJËÎÎ*³jÕ*€ìÝ»—¡O666DMM8::’—/_ Ò²¡¡¡DNNŽdee Ò?~L¤¥¥Ixx8C¾¶¶– ¥½xñ‚p8âíí-Ò&âééÙ¤íÁÁÁD^^^èM]¯ãÇ$11‘5?// ºººäáÇ‚ôÂÂBb``@Dê666;þ±qäȀܼy“5?''‡(**’Aƒ mtõàÁ¢¬¬LBBB„äŸ?Nmmm¡>™••EÑ£G‹´¥¦¦†ìÞ½[¨Ýâàÿž6Õ/íìì—Ë•hÜãoZ°~ýzFÞáÇ rèÐ!AZ]]éÔ©±°° oÞ¼¤;vŒ kÖ¬aèéÔ©qrr’x&ÊÊÊBã¹8ÊËË÷|XX˜ÐukªîÝ»— ÷ïßgÍøð!@:tè ô|˜““C´´´XŸøhhhÿýïµÏîÝ» ’žžÎšŸ‘‘AdddHXX˜PÛîܹCäääÈ×_-$ÿþ}€èéé‘ëׯ Òù¿GMõ¥9sæ‡Crrrš´½²²’ìܹ“<}ú´IÙ÷¡¬¬Œ ßÿ½X¹’’ÆXxðàA€,]ºT(ßÿÍÌÌ„ž?Ÿ>}JTUU‰›››Èzâãã›Ü´£²²’GGG¡qs×®]ùõ×_ŶåcC7í =ÚîøèЃï{Hâð>|x“»nEFF 䈪ª*4hC—8lllˆ‘‘Ã1÷.üŽáÇ3òüüüˆœœœÄªÎÎÎÄÌÌLd¾$¿œœ€Œ5Š‘7`À¢   ö!V‡Ÿ(YwþSRR"“&Mb¤ïÛ· ‡fÕ'‰ÃÿbÄæØëÛ·/QVV&<µì¤I“R^^.Rÿ¢E‹Hûöíÿÿý÷d„ ‚shkkËx)“••%Ó§OgèêÙ³'ãü@ÒÒÒyݺuc8üDŽ™:u"–––Œt777bjjÊøPÆFvv6áp8bÃlp8âââBjkk›UNR‡ßÚµkyüKÙÎ!mãð[°`ÈHS§N%„œ|‡ß† òúúúdÀ€ͲO’:ü¸\.éÔ©yñâE“:Å9üøV­Z%Hûûï¿Y{ ÄÈȈ8991ôØÛÛ[[[¡b¢à;¨ØÆQð~^^^M:øÞER‡ßŽ;y3fÌ „Ei ‡ßôéÓ‰”””ó‘ÏèÑ£‰”””ÐDþùܳg,ÿ¹ºñoü]ÑE·I~¢ÊÊÊÊ’ˆˆ¡t~ÿgÛ¥yâĉ€È~+‰Ã/&&† »víJ¯ªª"b'| P‡=èÑvÝ´ƒòE³eË¡8M“'O‡ÃŠw£¤¤$øûÉ“'(//oÖN||zöì ---±2üØB‰‰‰puuÊ{ñâjkkñäÉXZZ2ÊÖ××ãÑ£Gxøð!rrrPWWÇC¨9ðíIHH`Ø“››‹êêjdeeÁÌ̬Eõo—¿ñíÏÍÍECCƒHûÙ–ò999ë2óæÍìY³„dù¿§ÊAÑ>Æý›ŸŸvíÚ±æµõxÕZ 4éé騾};bccqøðaL:K–,ÁäÉ“%ÖÃÿgaa!HãÇx†††¸xñ"!"70i ÿú8×cllŒåË—cùòå¸zõ*.\ˆ¨¨(˜˜˜`èС¹Ž;x;s¾sçÎÍl͇åcÜ¿ÆÆÆ¸sçkßÕ’ß»…™™V­Z…U«VáÂ… X¸p!¾ýö[tìØ¾¾¾é8pàäää`cc#Hãtÿ¼yó&k¿¶¦=üMÁ>‡>ÏgÈ!øçŸ°sçN|õÕW§ì“þyü>ù>˜™™‚ðððÏêR(”¶‡îÒK¡4ÂÔÔZZZØ»wo›,éÔ©”””°oß>‰Ë:t\.‹-8ûêëë;ø±ÑÔ’_(**6ËžærðàA899aÁ‚g_}}½X§*)Ocø;víÚõ½m±µµ…¼¼ü{µ—_ï£GDÊhiiASS‹/F»vígggtëÖ óæÍ <ƒ¡¹ØÙÙANN®Í®WçÎ!++ÛfúGŽ )))ìÙ³§MôwéÒXw>~ü¸ ðv©‘ŒŒ ëîÛâf ²-á×ÙXÿ‡à?þ€f̘!pUWW·ÊÛööö––nÓñá]zôèãÇCII‰uWJ>‡uÙO”––âöíÛˆ‰‰ATTT«8û€·ã[mm-®]»&”^SSÃ:ÇÁÁ§E××ÝÝLJ¬¬,ãzÙÛÛCFF†uw鉨ûWZZ¶¶¶¬e8N‹Ch¼K—.]PQQK—.1òŽ?™¶¼¸µèÛ·/Ž;)))±÷ocbccqíÚ5LŸ>ÚÚÚ‚t.— -êŸýúõÑ#GÀápö8::‚Ãá|6ýSAAAäóC[õÏüü|ÖÝí?EEÅ=ϼKFFdeeEÞƒŸùùù¸|ù2¾þúkŒ1Bàì+,,löóÉ' ®®Þ"G££#€–Ý/ åË„:ü(ÿ)¶lÙ"¿ï]¤¤¤°fͤ¦¦b„ ¨©©Êoéò5eeeÌŸ?/^Ä¢E‹@iR¿œœž>}*XÊQZZŠÐÐP\»vMì—ËÅ?ÿü#´ô÷ݘRªªª˜;w.âââ°dɉìi.òòòxúô©à¨¤¤ÁÁÁ¸qã†HûÏ;‡E‹ êOKKÆ àêê*x x4551{ölœ>}+W®lV{CBB ¤¤$p‰ÂÂÂ÷îÝÃøñãK&OžŒ´´4èêê¶(6Y»ví…'NŦ”Ä~IÐÑÑÁ¬Y³‹uëÖ5K}}=,X öü˜››#88{öìÁ–-[„òÄŧ“”ÐÐP˜™™aþüù‚pýúuìØ±C† šA¼•˜˜ˆêêjoï‘aƉ}Y=räÖ­['è?)))ضm<==ammÍZ†‚Å‹ãСC-m¦rrrHOOØÿòåKøúúâÁƒ-~466ÆäÉ“±ÿ~lݺ•‘/®?TWWcÞ¼yøûï¿ÅÖÁ·»1§NBee¥ØÙ°¸råŠÐ‡™wÇ·Oªª*ÄÇÇ£¸¸?ÿü3V¯^%K–`Þ¼yøí·ßÄ.'•äþ3f 1oÞ<¡þþù§Îòâ:Ÿ‰è_ ¸G áÄéE'`”ÞõñÇ‹‘‘‘8::JÏž=¥[·nR£F QÕjÕª•ïdjÒ§O µjÕ’~ýúIÛ¶mÅÑÑQºt颿dÉ ¥K—–€€Q©TÒ´iS™0aBž£ÆªG`´¶¶–°°0qqqGGG¸§OŸjF3®]»¶& Ës_ 2J¯zäÔ2eÊH@@€K‹-dܸq@žQ©Tâåå%iii×[·nÐ\7$ @o¬¹¹¹Œ=:Ï<òÚ‡üFé1c†Î²ñóóÓ™¿dÉ133Ó;b¼™™™Ü½{Wï¶"##€˜ššæy\¾ùæ Õ«W—nݺ‰tîÜYêÕ«§÷û+99YZ´h!ÄÛÛ[  Í›7—*Uªè=f½{÷[[[éÚµ« >\|}}ÅØØXäÑ£G:ñ?Ö\ok×®-}úô‘ÐÐPqtt4ø™V(y޶nHAGé}÷Ýw¥J•*R»vmiÛ¶­˜™™‰½½½\¸pÁ`ÛK—.Õ×N:‰]¾#+ç7J¯:ç2eʈôêÕKZ¶l)*•J‚‚‚$99Y+V=Jï¼yótÚñòò’f͚܎zÏçGî~ÞêÕ«ÅÚÚZ¬­­€˜››‹µµµÎˆÓj;w–ªU«J÷îÝeèСâåå%J¥Rš4i¢3*¶z”Ò²eËŠ¥¥¥æ¼ïÔ©“$%%émÿöíÛ,$ @  M›6•Ê•+ËÔ©SuâCCCÅÞÞ^zôè¡•O³fÍäéÓ§:ñ©©©Ò¡CÍ÷]xx¸´k×NªV­ªw„Võ(½Ï\¥wÈ!bii)õë×—V­Z‰J¥½ß_jšß”¡¡¡bcc#]»vÍ3ŸüFéY»v­˜™™‰ƒƒƒôéÓGš5k&J¥RZ´h¡óÛS=JïªU«tÚquu•Î;Üξ}ûò…6· iÙ²eâ « £ôöêÕKó»ÇÃÃC³N›6m¤AƒZ±êóÿƒ>råʉ———4oÞ\Œ¥nݺˆüß(½ÎÎÎR¿~}ƒŸõÄÄDñöö…B!AAA2`À KKK™?~áÄkÄQz9q*ºI9iÒ¤—,É“'— é%¼U«V:#‹¾¨-Z uëÖ055ÅíÛ·ajjŠÐÐP„‡‡ÌÀ××·@"(•JtêÔ þþþ022Bbb"T*0pà@Á4h€Ž;"''åÊ•ÃСCñå—_ÂÊÊ 666ðõõÕ €›ƒƒZ´hœœ¤¤¤ ((“&MÒé Z©T¢sçÎðõõÕäcbb‚ÀÀ@ 0ÀààÀ³ÇH|||ò¼ëÎÛÛ:t@NN*T¨€aÆáÿû,--Q¥Jøúúj ”¡P(ЦML˜0fff¸zõ*š7oŽo¿ý6ßÇy ¼½½áææ777ëccctíÚÞÞÞP(HLL„©©)5j„þýû|g¯ÍŸ?<À[o½¥7ÆÂÂ7F£F´¶éââ½çHãÆõâR§Nøùùéä¦y)!!¦¦¦ BÿþýuFtT 4x÷Yn*• ݺuÓ¼§‰‰‰033CPPúõëg°}õy5pàÀ<C333C=P»vmT¬XIII¨_¿>úöí‹•+W<¦aÆùÞ1<ëÿçí·ß†J¥Â7P¶lYôêÕ ‹-Òz\L-88VVVÈÊÊ‚­­->ùäŒ3*• îîîšÑ;ÿ»K¯C‡X¼x1Œqýúu´iÓóçÏÏóóoee…Û·oãôéÓ˜0a‚Þ´  BBBtúR(:ùÏÎÉ–-["##¶¶¶1b&OžŒòåËÃÆÆ 6ÔêÔ> @+Ï€€­÷«aÆZ¯MMMñöÛo£nݺ$&&ÂÜÜMš4Aß¾} €¤>OÞ}÷Ý<ïÔ³´´„B¡Àýû÷qûömT«V #GŽDDDDžƒŽÔªU ÁÁÁxüø1ÒÓÓÑ¢E |úé§zïÐR( 28PR^*T¨€&MšèÔB¡P@©T"$$D§/+…BÍcVÀ³óç×_Ejj*¶nÝŠéÓ§cÒ¤I˜8q"<<<°víZØÚÚêý³±±Á•+WpéÒ%Œ7Îà¡~~~ðôôÄ“'O`llŒbêÔ©P©TðòòÒ¹†–*U ½{÷†›››æý-S¦ š5k†^½zéOõgHý~U¯^£GƬY³ôx¤R©ÐµkWøûûC¥Ráúõë°²²B·nÝЭ[7XYYéÝB÷g¥P(`ii‰>}ýõ×èÓ§¾þúkˆnß¾Ž;báÂ…ynÏÝÝHKKCvv6Z·n1cÆè½¦äV©R%4iÒÄàYnnnèÒ¥ ŒŒŒpíÚ5XYYáÝwßÅܹsõŽJnjjŠ&Mšè½{{{üŽtpp€ˆ`ðàÁyîgff&,,,àíí4lØÞÞÞðóóÓŒûüþåää 99÷î݃««+ƇéÓ§ë½Æ•)S ÄàÁƒ1mÚ4 2Äàg½téÒèÛ·/ªW¯Ž¬¬,$&&­[·F=tî€Tç“””„{÷î¡F7n¦M›¦w0SSSôìÙžžž066Ƶk×`kk‹ž={¢S§Nzß_¥R‰&MšäùÛH…Bkkk„„„è}o“’’0oÞ< <_|ñ233qïÞ=„……aáÂ…y.âååÜ¿ …¡¡¡Z}>bccƒ&Mš¼SÏÝÝ¡¡¡P(¸zõ*lmm1dÈ|õÕWz?ïêïõ ¹ùùùé‘:++ ¡¡¡hÞ¼9Fg¾jÕªUƒˆ`èСyþf{ê÷ÙÐoàÙ`þþþxðàêÔ©ƒ‰'bÈ!(S¦ t®åeÊ”AŸ>}0hÐ ¤§§ãþýûèÑ£,Xçݼ …åË—Gýúõáææ†:uê 00P'®\¹ršß±™™™¸rå *T¨ =;¿ó¡8}ÿý÷¹ŸÌ¸>iÒ¤ï‹3¢’D!"ùGý ) 7Õ¯#""0bĈb̈þ vìØŽ;â‡~@ç΋;z²³³allŒñãÇã‹/¾(Ôº™™™h×®œœœ°xñâ"ÊþÍöïß   œ;wN§zãÆ ØÙÙaùòå×Y7==Í›7‡ŸŸŸÞÇù)·nÝB•*U0gÎ|øá‡Å‘–sçΡfÍšX¶lúõëWÜé¼999èÛ·/:„£G-œJ>ÿÜhG‰È«¹ƒƒˆØ‡Qa´hÑëׯǀôv„O¤ÏÍ›7ѰaC,Z´¨¸S¡b¢ddÇŽZó/^¼ˆ®]»¢Zµjh×®ÞuѾ}{ûˆ¨ÄX¸p!bbb°oß>ûˆˆŠ ~DD…Ô¦MÄÄÄ|œ‘èyøì³Ï P(Š;*&Íš5Cxx8† tèÐþþþ¨Q£ÌḬ̀sçNƒuÕ¬YcÆŒyÍÞ½{cïÞ½¨R¥Jq§BDTbévhADDùrtt,îè5S(øä“O\Ü©ÐH©Tbùòå5jöîÝ‹{÷î¡jÕªX´h<<<Š;½¯L™2øä“Oàíí]Ü©é¨X±">ùäÔ«W¯¸SymôõµIDD¯ûð£7ûð#"""""zs±?¢¢ÃGz‰ˆˆˆˆˆˆˆˆJüˆˆˆˆˆˆˆˆˆJüˆˆˆˆˆˆˆˆˆJüˆˆˆˆˆˆˆˆˆJüˆˆˆˆˆˆˆˆˆJüˆˆˆˆˆˆˆˆˆJüˆˆˆˆˆˆˆˆˆJüˆˆˆˆˆˆˆˆˆJüˆˆ¨ØˆHq§@DDDDDTâ°àGD/äÎ;@@@îß¿_Üé¼VwïÞEÓ¦Mqûöí—n+((ß|óÍ+ÈêÍqóæM 8õëׇ©©)¬¬¬Ð¨Q#|÷ÝwÈÌÌ,îôˆ»wïF¥J•°eË–âN¥H¤¥¥ÁßßkÖ¬y¡õ·oߎJ•*aÇŽ¯83*.6lÀûᅵœœâNEË¡C‡àïïvíÚw*DDDoüˆJ¨öíÛ£U«VEÖþo¿ý†ÈÈHDFFbÓ¦ME¶››7o"88vvv¨\¹òK·…ÄÄÄWÙ›!&&¾¾¾X¿~=üüü#F bÅŠ˜1cŒŒøµDùóòòBÿþý‹t™™™xúô)ÒÓÓ‹t;Å%33QQQ¸yóæ ¯ÿo9>nnn>|xq§ñÆ Âž={ðÎ;ïü«Š~kÖ¬ATT¶lÙ‚ãÇw:DDDo ãâN€èuŠˆˆ€B¡Àˆ#Š;•"—‘‘Q¤wK­_¿5kÖDzz:6lØ€¾}ûÙ¶þ-D}ûö… –-[…BQÜ)½qˆŒŒ ìß¿uëÖÕZ–žž¥RYL™Ñ›äéÓ§E~7hË–-‘ššÊ"´íÚµûן×q>üT®\ýõ<<<0{öl|üñÇÅD7nÄ[o½…½{÷býúõðôô,î´ˆˆˆÞ,øÑʱcÇ \¤ÉÌÌD\\Ξ= sssÔ«WzcãââPªT)8::âéӧصkÌÌÌàëë‹2eÊè]çöíÛ8vìžþøc<~ü‹/ƃP¶lY½ñgÏžEéÒ¥áàà€ôôtìÚµ æææðõõEéÒ¥ _ºtiøøøè€«W¯âôéÓ¸sçѰaCk_â.]º„GÁÝÝ]gý””$$$ÀÙÙz·±xñbüóÏ?8yò¤NÛ‹ *ÀÎÎNïú·oßÆÍ›7áîî®·°õðáC8p=Bpp0*Uª¤·àنǎCff&|||PµjUƒ±gΜAÙ²eaoo'Ož`çΰ°°€ÁóáÆ8vì²²²òm?÷vªT©‚ *è]¾{÷n;v S¦LÑ)ö€™™™ÞõNŸ>råÊ¡jÕªxüø1vî܉òåËÃÇÇ¥J•һιsçpòäI”*U ß“˜˜T©RÖÖÖZó/_¾ŒŒŒ Ô¬YS'ÞÆÆ666¸téŽ9‚ªU«ÂÇÇ&&&z·QP"‚'NhÚOKKÃþýû‘‘‘àà`T¬XÑທ/_Æ™3g””gggêbΜ9 ØÚÚb÷îÝpuu…££#²³³±{÷nøøø |ùòzÛ¿víŽ;ðõõE•*UòÝŸS§NÁÁÁåÊ•Ë7öÂ… 8}ú4RSSáììŒ5j轃öܹsxüø1ÒÓÓ‘œœ¬u}«S§ŽÞkâ©S§`ii [[[<|ø;wî„••¼¼¼tιàâÅ‹Zó\\\ô^ÇsrrpòäIØÚÚ¢råÊHMMž}û““ƒàà`ƒÇ2;;Àõë×áíí '''$$$àüùó¨_¿¾Ásµ Dqqq8uê,,,àéé©s~ësîÜ9DGGÃÉÉ ^^^zeZZ.]º¤5ÏÕÕÕàuY-11ÑÑÑP©TðññÉ7Ÿ'OžàÌ™3ˆ‡••êÕ«§w¸¸8¤§§###÷îÝÓ: ]gÕNœ8êÕ«üŽÎíüù󈋋CJJ \]]áïïo06÷5%-- ;wî„­­-4h ÷‘;>55»víÊ3xö^ÅÇÇ#55...zó9{ö,”J%jÔ¨¡³Lý]T«V-˜ššj-sppÀÌ™31lØ0´mÛµjÕÊïðŽ?Žš5küNyQÄÍ›7ñõ×_CD°aÃL›6Mo¬úªþL¦¤¤`×®]°··‡§§§Î9­þ ÛÙÙÁÊÊ*ßxµ¸¸8œ;wiii¨Q£|}}ubΜ9¸¸¸è,»yó&nß¾Úµk¿ô÷QžD„§7r`@ÔSDD„äçwÞ‘^½zåwèÐ!qssbjj*ÆÆÆ@:vì¨7¾V­ZÒ­[7Ù·oŸ”-[V““£££äääèÄÏž=[T*•ccc122’I“&é•-ZˆB¡SSSQ(@ÜÝÝåÎ;:ñ:tÜÇ%÷¥7ÿ¯¾úJ³ê|¦L™¢7‘eË–iÚÛ½{·5kÖ<žÕ«W—^½zÉîÝ»¥téÒš|\\\ôÆ;;;KïÞ½e×®]Zñ®®®:±'Nœ¦M›ŠB¡333Íññðð¤¤$­Ø)S¦‰‹‹ÓigäÈ‘¢T*åÆzsÊÎÎ 7¸Ÿ5kÖ”>}ú\>jÔ(111‘¬¬,Í<•J%£F’… Š©©©˜˜˜±´´”ØØX6rrrdÚ´i¢T*5ï—±±±|ùå—·kgg'”mÛ¶‰¹¹¹æxÖ­[WoûS§N(OKy IDATÕiÖ¬YÛ‰‰‰R³fMƒ1ÇráÂ…<Ûzžµµµ¼ÿþûòÇH©R¥4ù{xxèĦ¤¤H×®]5Ÿ]###Q(2nÜ8­ã.òì= S¦LÑi§}ûöR»vm­yYYY@F%aaa@ÌÌÌ€x{{ËÝ»w µ_ÏËÈÈ2qâD‰ˆˆ•J¥9¬­­åüùó:ëDFFJPPÐ:6òðáC­XGGGy÷Ýw¥uëÖbdd$ÆÆÆræÌyë­·ÄÈÈH¬¬¬äâÅ‹:û<~üxQ(¢P(D©TЉ‰‰|ûí·yîËÁƒ€4hРϸÔÔTiݺµ…B!–––@Ê—/¯ó~‰ˆxyy¼¾]»vMï6ÌÍÍeôèѲnÝ:155ÕÄ7jÔH'vûöí:íîÞ½[o»)))@¦M›&ÿûßÿÄØØXó~ÙÙÙIbb¢Î: R¯^= •+WÖì·z[Û¶mËóxåçîÝ»šã©¾ËôéÓu®ç÷îÝÓœomÚ´Ñ|fˆ¯¯¯ÞóyóæÍ:ÇgÿþýóÉÈÈ‘#G 122###133“¥K—\ç×_ æææšãóé§ŸêĪ¿§õM÷ïß7¸?ÿüSHHHˆÁ‘Ý»w‹Îç«yóæ’‘‘¡Ÿûš²lÙ2Íù@Z·n­¯¾¦L:U–.]ªߦMø;wŠ···N>­Zµ’ÌÌL­Ø^½z‰¹¹¹zôHÈÌ™3eáÂ…šßY¤S§N:ñ>2kÖ,Y°`V|çÎuâÿüóOñôôÔ9þ:tììl­Ø®]»Š………ìØ±CÈï¿ÿn0¦mÛ¶Ò¤IƒËÃÂÂÄÍÍMkžJ¥’òåË‹§§§8p@222äÇòöÛoë´1wî\ ~ø¡ÄÇÇKbb¢ôíÛWÈÆõn×ÎÎN7n,–––òùçŸËÑ£Geݺuz‹ @FŒ!ñññ’ }úôòÛo¿Ü·Û·o‹«««ôïßß`LçÎÅÈȨÐÿɱ¶¶–&MšHÅŠeòäÉrôèQùùçŸeÇŽ:±íÚµSSSY½zµ<}úTÒÒÒdâĉ@&Mš¤û¢?¥R)½{÷–K—.Iff¦,_¾\”J¥„……j¿ž§.ø•/_^üüü$**J2224׌wß}Wgèèhùì³Ï$>>^rrräüùó2dÈ _|ñ…V¬£££”)SFÆŽ+Ж'Mš¤)Ü? úâ‹/€|òÉ'rñâE¹pá‚tïÞ]È_ýep_®\¹"Õ«W×{ÊmÀ€bdd$?ÿü³¤¥¥‰ˆHRR’ÁÿÐ'$$ÈÙ³gÅÕÕUÚ·o¯u}{¾à¡fnn.Í›7— *ÈŒ3䨱c²víZùûï¿ub=z¤iïûï¿/PÁ¯|ùò$G•§OŸÊüùó€|ðÁ:ë´lÙR*V¬¨ù.Y¿~½æ¼ÿ¾Á}(¨FIéÒ¥eÆ ’‘‘!÷ïß—?üPÈœ9s´bÕ?¥R)ƒ–+W®ÈÓ§OeÞ¼y¢P(¤gÏž:í?|øPs|.\˜oÁoìØ±bdd$Ÿ}ö™$&&J\\œ„†† 9tèNü¯¿þª)v?^rrräÁƒràÀIHHЉ¿té’œ={Vììì¤{÷îZçÃó˜ÜâââÄÉÉIo1·½{÷ÊôéÓåâÅ‹’““#§OŸÖ\,X ¯¾¦´nÝZÊ—//³gÏ–èèhY½zµ}¤T©RÿcP½zu ?üðCžù¨9;;*þyR®\9 ÐYöÖ[o‰³³³Ö<õŒ6lØ`°ÍO?ýTÈ­[· Æ|øá‡ZÇìæÍ›ZwùûûëU*•„„„È£G´æ;88èÜÁöàÁ±´´Ô¹k*##C*Uª$îîîzó²³³…B!¿üò‹ÁÜEDÒÒÒ¤bÅŠâíí­5ÿéÓ§bii)õë×Ïsýüøûû‹M¡×³¶¶###ƒMµýû÷ ™/..·nÝBË–-±mÛ6|þøã¤¥¥i£Í›7###ÕªUCdd$ zõêxòä þüóOtíÚUï¶Ú·owÞy§À¹uèСPñ¹©T*4nÜ'NœÐYŽ>}úàСC¬]»•*UBûöí ¶yåÊ(Š<ûÕsqqÁµk×<ë¯fÍšxôèPµjU\½zU³ÍÜ4h Ó‡bccµæÅÆÆ")) ;wÖy¿4h€;w"==]o_x]»v5øÞ¨:u ÉÉÉ Ó{>ìÙ³/ÜçPff¦NŸHË—/G||¼æõÛo¿zõêé¬Û½{wtêÔ)ÏöwïÞ zGo0`6oÞŒ#GŽ uëÖ/’¾†§§§NÿxݺuÃòåË {{û—jßÇÇG§_-ܹs§@ë—-[þþþzG_uvv( T®\YóÚØØ–––HNNÖÄ=z<€‡‡‡ÎùP¿~};v "òRƒ×„„„à믿ÆÀ1dÈ4hÐà…ÛÊK¿~ýвeË"iÛßß_ë¼V*•¨W¯ÒÒÒ´âŒQ±bE­cœ™™‰ÔÔT÷ûEìÙ³ô~ 0ááá8yò¤Î5ÈÛÛ['¾[·nذaN:U þõ‰ŒŒDzz:ÜÝÝuΟºuëâèÑ£ZóNœ8«W¯þëGë¶µµEíÚµóÝxèС *p›Ã† +T|nU«V…›››N> …}ûöÅôéÓqûömMˆkÖ¬···Þ¾lsSÿNºråÊ åõ*¬_¿`mm­ù½áîîŽÕ«W#>>^§Uµ‘#GæÙÏ¢¾x??¿ÊÑÅÅNNN:Ç_©T¢W¯^øæ›oœœ¬éƒuÍš5hÔ¨\]]_h{DDD…Á‚•hýúõÓúÏO¯^½ P(°hÑ"½ñ§OŸ€wR[A:¦?þ<àçŸƺuët–›ššâòåË:¿””¬\¹‡B||<®]»†¤¤¤uÄ^|~üñGüôÓOó©_¿¾fÞ† @ïè}ëׯ7XT*ÈñyÑøû÷ïcÅŠˆŒŒD||<®_¿Ž¤¤$½…—.]º`èСøá‡€§OŸbÆ èׯŸÁºàîÝ»077ϳx¤§§ãîÝ»X¿~=LLLP¥J,Z´“&MÂÍ›7 ü#__@ý~­\¹«V­ÒY®R©påʽ´æü\¾|9V¬X¡³ÜØØW®\ÑÛ yAT­Z'OžÔ*ýóÏ?øûï¿ñôéSܺu z ~ÍßÄÄDï êcrþüù—.øéS­Z5ÏÏ( y„îÞ½‹eË–áÈ‘#8wî®_¿Žääd½Ç1·ç uÏ¿¾pá`Ñ¢E¯™·nÝzá‚L›6 øî»ï°téRØÛÛ£ÿþÿõýÑáyNNNž †ô¢ÔçODD"""ôÆäìée¾‹Ò7°téR?~ñññ¸qãRRRô*£V”ßwׯ_DzeË­ù#bJJŠÞ•úöí‹©S§â§Ÿ~‡~ˆÄÄD8póæÍËw;êÏ̽{÷ ¾#¯˜ú÷FÇŽõ.7nœÞõŠòø_¹rË–-CLLŒæø§¦¦êˆ*<<_}õ~ùå 4çÎÃÑ£G±téÒBåGDDô¢þ½B%*ê‘W322Ф}õH†¿üò ÒÓÓõNÏßáròäI8;;cÊ”)°µµÅˆ#°qãF½?€_4Ÿ7Ì'w±ïáÇضmzôèãÇkMmÛ¶Å–-[žžþÒyFtt4ªU«†éÓ§ÃÞÞ#GŽÄ¯¿þжmÛê777GXXÖ­[‡ÌÌLlÙ²©©©ùÞY¥Jöïßoð|x™bðìý˜;w.îÝ»‡7¢V­Z˜((?üð€gÜ355EÏž=óÝ–úNÔÜOD¼NÇÇ¥K—0qâDß5jÔÐÜý÷:­_¿ÎÎÎXµj<<<0qâDìØ±Ãà÷z:uàãã£9þk×®Eéҥѭ[·×™6ý‡ñ?¢\ÜÜÜQQQù€^„úñ“#GŽ C‡Zç£>Bff&._¾¬õ8髸 ±:Ÿ£Gè?œê‚^Ÿ>}àáᡵ¬W¯^زe ¶oߎÐÐЗέ FŽ àÙãÒ¹ïŒ\°`ÁuÂÃñlÙ2lß¾kÖ¬Aƒ ò½J}×;wôÞA<»+ÆØØ«V­B\\¶nÝŠŠ+âÓO?ÕÜáò2?õùyäÈ4iÒä…Û)Hû7~åíbÞ¼yX¿~=†þÊÛwuuEVVÎ;§s—PLLŒ&æy=*ÔvDDgžúî$õ#²¯Ë°aÃ`aa‹/j=nnnŽ”””—j;÷ù ï±ÏW©téÒèÔ©:uê„]»v¡Y³føþûï1}úô"Ýîë¶uëV>|óçÏGóæÍáììœç]Ã…áêêŠ 6àÖ­[:Eš¼Î}ruwêëÞ‹È}þ¨ÿõg622²Pc¥÷ßNNN8sæŒæ‚@ÞwÜ¥AƒÁÙÙ±±±Î'<< @||<Ö¬YƒŽ;æÛmܾ}ÀË/cÆ P(xï½÷tî^ìÒ¥ f̘K—.½¶k®ˆ`РA¨S§Ž?®õØy^…ôððp 6 X»v-ÂÂÂP¦L™×‘2ïð£ÿ–¦M›â­·Þ2¸ÜÂÂí۷DzeËôöÿö²ˆo¾ùׯ_/Ð:ñññ¨U«–V±oß¾}øõ×_õþGM­\¹r8wî\žmW«V ~~~ˆˆˆÈ³?"µ 6ÀÂÂM›6ÕYÖ¦M˜˜˜¼ö¿ºÇÅÅ¡nݺZÿÙ³g6oÞlðø¡zõê˜?>¶lÙ¢·Ï·çµk×À³GP 166†££#.\ˆ¶mÛÂÑÑeË–EïÞ½±dɘšš¾TÿnnnnðððÀ¬Y³Šä1«Úµk£^½z˜9s&’’’ ½þÇó<'ÃÂÂààà€ùóç¿Pûùi×®ŒŒŒ0wî\­ùÙÙÙ˜;w.lll´ FFF(_¾<Ž;¦üøq>|Øàv–.]ªé«xöÁ… ¢bÅŠy>.ùèÑ£<Ï‹ˆ‡———V±oóæÍسgÞÂda4hÐ5jÔÀ´iÓššZèõžïcñy=µk× U\[³f ¢££_ùg9?iii¸yó&µòYµjbbb æsssŒ;±±±þcæÞ½{amm]àb~dz°Ö¯_ooo½*«ûsU?òû:ܽ{ÉÉÉhذ¡V±oÉ’%8{ö¬Áãß³gO˜˜˜`äÈ‘8þ|~o½*,øÑÊó}úé3þ|”.]ÁÁÁX¼x1Î;‡³gÏâÇÄ?þøRÛW÷˜‘‘___üüóϸrå Ž=Š+V`ãÆ:ëøùùáÈ‘#?~|Ë–-ÃüñÂmçn?%%ÞÞÞØ¸q£¦ý¥K—bË–-×½t鬭­ó¼óÐØØ³fÍÂ¥K—аaC,]ºQQQX¼x1¦NúR¹ÏŸ0`/^ŒqãÆ!>>ÑÑÑèÖ­¢££1}útGn½½½qðàA|ýõ׈‰‰ÁŒ3вeË<ïØ011A‹-°zõj?~}ûöÅþýûñá‡ê ¾¢vçÎT©Ržžž¯´Pàëë‹mÛ¶aÆŒضm>øàôîݵk×.tÑäy&&&X¸p!nݺlÚ´ W¯^Edd$–,Y‚;v\÷ôéÓ¨\¹r¾woÞ¼‹-©S§pÿþ}lß¾}úôAFFFžƒÌ´jÕ ÇŽÃ’%K‹åË—cÉ’%/¼¯¯ƒºàÿí·ßÂÜÜ\Ó'¨J¥BµjÕ0f̃ë&$$ÀÚÚc†™3gbÚ´i¸pá>Œ:àÂ… ˜5k–Þó:''Í›7ÇÏ?ÿŒcÇŽ¡[·nˆÅèÑ£_êîCsssÌ›7—/_†¯¯/¶nÝŠk×®áàÁƒX¼x1þþûo­x•J…%K– 55ÞÞÞX·nƒ+Vè t•[ëÖ­qàÀ¬\¹§NÂÒ¥KõöCª+++ôèÑÃ`LéÒ¥Q§N¬[·غu+Þ{ï=|ðÁ¨Y³æK¾ ËÂÂnnnøé§Ÿ0gÎlÙ²ï¾û.F•g>eË–E—.]ðÛo¿ÁÞÞÍš5Ëw[غu+zõê¥U\4dïÞ½¨T©úöí[èýÒçôéÓˆGçÎõ.÷ññ½½ýkýcåÊ•áää„Õ«WãÛo¿Åü~ýúaüøñpuu5xü+T¨€:à·ß~ƒ‹‹Ë ÎBDDôBŠ{˜`Nœ^t`ƒÿÂ]"""äU¹zõª´mÛV”J¥¦ýòåËKÿþýõÆ×ªUKºuëVàöOŸ>ýüôbdd$ƒ Ò‰½víš„††Š±±±ùóÏ?eÑ¢E@nܸ¡wwïÞ•æÍ›köÁØØXºté¢7666V|||´òQ*•2dÈMÌÆ€¬[·Îà~-Y²DÈ–-[´æW¯^]zõêUC#""ÎÎÎÒ»wïÅ&&&Jûöí5ûéää$ýõ—|óÍ7@îÝ»gp=…B!Ý»w/p^¿ýö›( 9tèÁ˜aƉ«««ääähÍ ‘ÐÐPx•J%£FÒ™&ÎÎÎz·qüøqñôôÔy¿ôµ#"bgg'Ìk×´DGG‹‡‡‡VûÆÆÆòñÇ\çâÅ‹bnn.ÁÁÁù¶¿ÿ~qss…B¡i¿B… 2}útyôè‘N¼µµµ¼ÿþûÊ=++K>ùä111Ñúì®]»Öà¾ÚÚÚjb½¼¼$66VÚ·o/µk×Öi€Œ?^fΜ)šc?bÄ÷<·›7oJéÒ¥€\½zUoLFF†‰'ê,kÓ¦¸»»ëÌ‹‹“-Zˆ‘‘‘WWW9xð L™2E …dddhb¥GZ¯ÃÃÃ5¯­­­µ^«EFFJ:u´Î•J%Ÿ}ö™Áý333iݺµÁ‘‘#GjrWO®®®y^gDD.\¸ 5ÒœCfffZ׫ÜÌÍÍeôèÑy¶§ÏöíÛ€ìÞ½[ïò”” 3fÌÐY"~~~:óããã¥lÙ²*Ç—1cÆÈرc%000Ïm]¾|YLMME¡PHrr²ÁœŸ>}*C‡Õ|_+++ùý÷ßubïÝ»'dæÌ™òÙgŸiÎO•J%Ÿþ¹Ám¨mÞ¼YÈþýûóŒû矤FZﱉ‰‰|ùå—zãOœ8!~~~Z×kkk™:uªÁmœ>}Züýý5ñæææy^¯"##ÅÄÄ$ßëtt´4nÜXÓn:uäĉòᇊ………N|vv¶)S¦äÙ®šúš’×¾åvìØ1 ÖäS·n]9uê” 6L*T¨`p½]»v ™0aB¶óÅ_HÅŠ þ¾xÞ?ÿü#*•JúôéS øüLš4IH\\œÁ˜áÇ‹B¡+W®hæ=zôHsNÄǀ̚5«@ñ‘‘‘Ò°aC …B<<<äìÙ³2pà@©R¥ŠÁõ¶lÙ"ä‹/¾(Ðvˆþkžû?Q¤ü þŸÉ‰SI™Š=Nœ^t*Ê‚ŸZzzºœ:uJΟ?Ÿçæ_Tjjª>|XŽ?.IIIyÆÞ¼yS ½[·nILLŒ¤¥¥8Ÿ˜˜˜<ÿSùotãÆ ƒ…}>œç° éÛ·¯Ô«WOûì3\¸pÕ«W/îtÈ€ììlÔ©Sµk×ÖÛmHngÏžEãÆ1xð`Lž<ù5eX²eff¢FÀÚµk‹;¢%DEE©_F‰(½"¥—ˆþ3~þùg3Ø¿—‹‹ :¤5xSq˜={6”J% ¾ÿþ{ØÙÙÁÁÁ¡Xs"ý¾ûî;(•JÍ CéãõöíÛøôÓO1räÈ×aɶpáBcÉ’%HNNÆŒ3Š;%""úbÁˆþ3~ÿýwlÙ²õë×ÇöíÛòB혚šbþüù¯69*ÑŒŒŒXìûsppÀÞ½{1yòdŒ;ÉÉɰµµ…——öìÙƒàà`ƒëªTªb/öåää`Ïž=8vìîÝ»‡:uê`Μ9P©TÅšé¬\¹111hÞ¼9öîÝ —|× yáïEú?YYYX±bbccѺuk|ÿý÷ptt,î´ˆˆè?ˆôÒ‹ô½¹øH/QÑ1*ˆˆˆˆˆˆˆèÕaÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨1.îˆÞt?ýôbbb4¯CBBЪU«bÌè剦M›†#F L™2ZËnݺ…Õ«WcôèÑÅ”å…?¢—tæÌüý÷߀¨¨(ˆÈ]ðËÊÊBß¾}qòäI :TgyFF¾ýö[\»v sæÌB¡(†,‰ˆˆˆˆˆˆÈüè?eÛ¶mP(hÙ²å+ksÊ”)˜2e ÀÈèÍJ~üøñØ·o"##Q¡Båøë¯¿|ôÑGÅ%òæW'ˆ á‡~À?üP¨uîܹƒ´´´"É'99©©©Žðà‘™™Y ø‡"))©Àí8p_}õæÏŸ[[[ƒq5kÖĬY³0aœ9s¦ÀíQÑcÁH”” 8–––°¶¶FùòåáììŒO>ùä•´¿jÕ*888ÀÒÒRÓö†  Æ/Y²5jÔ€……œœœ`nnŽ5j %%Eo|dd$êÕ« TªT ...سgO¾y3ÁÁÁhß¾}¾±ýúõƒ‹‹ &L˜o,½>,ø=')) îîîøå—_0nÜ8ìß¿;wîİaÃàçç÷ÒíÏœ9}ûöE`` vî܉íÛ·ÃÝÝ]»vÅâÅ‹uâ—/_Ž÷Þ{>>>ظq#bcc±aÃ|ðÁ(_¾¼NüÞ½{ ###ÌŸ??ÿü3Ê”)ƒæÍ›#22Ò`^ñññ8xð ºwï^ ýP(èÔ©þøãܽ{·à€ˆˆˆˆˆˆˆŠûð£mÛ¶mˆ‹‹Ó¼Ž‹‹ƒB¡Àœ9s4óÜÝÝÑ´iSÍëÑ£GãÎ;ˆŽŽF:u4óßzë­—ÎçÎ;˜:u*Úµk‡üQ3àE³fÍФIL˜0={ö„………fM›6¡jÕªX¹r%ŒŸ}dsç•›ˆ`äÈ‘(W®¢¢¢`jj hÛ¶-lll0fÌìÝ»Wﺿÿþ;h‹ü4iÒS§NÅ–-[^àõˆˆˆˆˆˆˆ¨è°àG%ÚîÝ»±iÓ&Íë›7o-Z¤™¦)r=}ú«V­ÂÀ Õ^Æü‡â£>ÒÝÖÈÈ£FBÇŽ±sçNtîÜY³ÌÙÙ›6mÂwß}‡~ýú¡T©RÛ¿pᢣ£1jÔ(ˆÒÓÓJ¥;vÄÚµk‘žž333u/_¾ °±±)ðþ¨ûùKHH(ð:DDDDDDDT´Xð£mæÌ™˜9s¦æu¯^½ P(°zõj½ñqqqÈÎΆ··w‘äsþüy@ݺuu–yxxhŨ}öÙg8zô(†бcÇ¢E‹G›6mtFV¯;{ölÌž=[o/^Ô[̼~ý:T*Ê–-[àý±²²Ò¬KDDDDDDDÿ,øå¢£L™2EÒþ£Gæææ:ËJ—.­£V®\9üóÏ?8tè~ûí7üðÃذa¼½½±mÛ6XZZjbÕwôÍ;¾¾¾zsprrÒ;¿B… ÈÌÌDFFLLL µ?úú$""""""¢âÁ‚Q.5kÖœ>}ºHÚwqqðìñÙçï²;wî zõêz× @@@¦L™‚%K–`øðá˜2e æÎ«‰Qç¯P(àïï_¨Ü÷îÝÓ<ª›õ`ŽŽŽ…ÚŽÒKÿ)¶¶¶y³lllP§N,\¸III¯|û€U«Vé,[¶lŒŒŒg¦¦¦6l4EBµ5j jÕªøæ›o‘‘Q¨Ü‚ƒƒÑÑÑ^GT¨mQÑaÁþSfΜ‰/¿ü2Ϙŋ#99-Z´@||¼fþÍ›7 tçßÓ§O‘’’‚””äääh-óööF§N0wî\lذÀ³‘u—/_Ž+V ÿþ¨Q£†Ö:—.]ÂÉ“'5m¥§§#""W®\Ñ)´©T*Ì;.\@hh(nݺ¥ÙFBB‚ÖˆÅÏkÚ´)ªV­Š7æ»j›6m‚‡‡êׯ_àuˆˆˆˆˆˆˆ¨h±àGôœ† â÷ßÇÍ›7áææ[[[X[[ÃÖÖÌwý¹sç¢B… ¨P¡‚VÁPmÉ’% A×®]Q¹reTªT ýû÷GXX"""tâ,X€úõë£bÅŠðôôD¹råðÑG¡gÏž3fŒN|çÎñý÷ßãÀ°µµ…³³3,,,P­Z5|ôÑGóVüÓO?hÔÝ“'ObëÖ­=zt¾±DDDDDDDôú(D¤¸s z! …ÂÀMõ눈Œ1╵ÿàÁDEE!..fffðööFݺuall¸ëËþù¹?S¾¾¾zèj999hܸ1Ê—/M›6A¡PèËÊÊBHH¬¬¬ð믿晑>þþþˆŠŠR¿Œ‘ÂuDND±àGo¬¢.øýW%$$ qãÆèÔ©æÌ™£³<;;ï¼óNž<‰þùVVVÅ%½éXð#*:|¤—ˆ´899aÿþýøûï¿qíÚ5å111¸|ù2öíÛÇbÑ¿ág‰è?ËÞÞÇŽƒR©ÔYæåå…ƒê]FDDDDDDDÅwø‘^yôXì#""""""ú÷bÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨1.îˆ^•ëׯ#&&¦¸Ó """""¢xüøqq§@Tb)D¤¸s z! …ÂÀÍâ΃ˆˆˆˆˆˆ^Z”ˆøwD%é%""""""""*AXð#""""""""*A؇½ÉRô,î$ˆˆˆŠÐRæÿÿß»|_Œ¹¥{ÅQIÂ>üˆˆˆˆþ¥ E €rÿÿåby¿8ó!"""¢7é%""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""""*AXð#""""""¢ÿÇÞÇ×p/þŸ$ö¥jW*b}ßŠŠ¥Š"-ªÊUj«ZªÜ.®V×Ûö*EK­âÚ—ŠÅ­}¯"¶ØBb ‘X²JÌïßÌ/“sNÂéëùx|÷ÌÌgf>3gŽ^oŸÏ|¸?À…ø.„Àp!~€ !ð\àBüBà¸?À…ø.„Àp!~€ !ð\àBüBà¸?À…ø.„Àp!~€ !ð\àBüBà¸?À…ø.„Àp!~€ !ð\àBüBà¸?À…ø.„Àp!~€ !ð\àBüBà¸?À…ø.„Àp!~€ !ðx<äÈêàñ@àðxx2«€ÇàBüBàðèº–Õ Àã‡ÀàÑŸÕ Àã‡Àp!~€ !ð\àBüBà¸?À…ø.„Àp!~€ !ð\àBüBà¸?À…ø.„Àp!~€ !ð\àBüBà¸?À…ø.„Àp!~€ !ð\àBüBà¸?À…ø.„Àp!~€ !ð\àBüBà¸?À…ø.„Àp!~€ !ð\àBüBà¸?À…ø.„Àp!~€ !ð\àBüBฬnœº!éúÿ}ŽÊʆàña3 #«Û “0¤p! é<²l6Ûg’Údu;Åg†a¬ÎêFà ü² ’fu#E‘¬n®‚!½€ ¡‡à±={vÕ®];«›È$ÑÑÑ:|øpV7—Dàx,”(QB»wïÎêf2‰¿¿?ÿÀÂ^À…ø.„Àp!~€ !ð\àBüBà¸?À…ø.Ä#«ž›7o*_¾|iÖ;zô¨-Z¤]»v)$$D×®]S‰%äéé)ooo 4HeÊ”yð ~Œ†¡Õ«Wë·ß~Óþýûª¸¸8.\XU«VU»víôòË/+wîÜ÷ŽŽÖÈ‘#ÍåŽ;ªC‡é>ÿõë×õÞ{ï™Ë]ºtQ›6mîÿ‚2YZÏ^tt´êÕ«§õë×O&LpX/!!A6lÐÒ¥KuìØ1…„„èöíÛòôô”§§§š4i¢×_Ýé}þ;ذaƒºté"›Í¦Õ«W«I“&YÝ$ð°†A¡P(Ê#Y$-’dH2<== ܿ۷oß~û­Q¹råTë]¿~ÝèÙ³§‘tߣgÏžÆ7Ò<ÚÎ;gøøø¤yߊ/nÌž=Ûá1""",u?þøã{jÃÅ‹-û7.3.-Ãnß¾mLž<9ÍgoôèÑfÛ8à°ÎÁƒ*Uª¤yŸ *dLœ8ñA\Îc!11Ñ(S¦Œ!ɨU«–‘ÕMrèÀ)¿»>Æ#ðß …B¡P\¡0¤·uëVÕªUKÇ×µkלÖ;}ú´jÖ¬©ùóç§yÌ„„ÍŸ?_;wVlllf6÷±³iÓ&Õ¨QC›6mJ³îåË—Õ§OKOñÄFdd¤Y'£CzE¾¾¾æõ+VÌa°°0#W®\æPñˆˆËö;wîÞÞÞ–{Ó­[7ãêÕ«vÇŠŽŽ6Ú¶mk©Û£GrmƒÖ­[›÷aÑ¢EYÝ; é¥P( åÁzøpŸ¦L™¢[·nI’Ú¶m+OOO}ûí·Ú»w¯6l¨Ž;ªvíÚ–}N:¥yóæ) @áááªP¡‚jÔ¨¡çŸ^åÊ•Kóœ‡Ò‚ têÔ)EFFÊÛÛ[5jÔP‡T²dIKÝÀÀ@-[¶L'Ož4×EGG뫯¾’$uîÜYÞÞÞŠ×Ì™3Í:îîîZ¶l™ªW¯î° #FŒÐ–-[äçç§’%KªvíÚ:{ö¬*W®lžãÛo¿5ë÷îÝ[QQQš6mšÕ²eKuîÜY^^^–ãîÙ³GK–,ÑéÓ§¥ªU«ªFêÔ©“ .l׎;wîhܸqærÆ åãããð$éÑ£‡<==%I×®]ÓŒ3Ìm}ûöUÁ‚µnÝ:mܸQªQ£†Z·n­çž{ÎîüGÕÒ¥KÍå9rhÅŠæñ“Ë™3§&Ož,mݺUÒÝI6¦OŸ®wß}×Á]þÿ–-[¦Í›7ëäÉ“òööV“&MôòË/Ëf³YêݺuKS¦L1—}||Ô°aC»ã…„„hÙ²e:pà€.^¼¨*Uª¨V­ZzñÅõÄO¤Ú–£Gjݺu:r䈂‚‚T´hQ•+WNýû÷WÙ²eÍz§NÒòåË-Ï^TT”ݳ'IsæÌQLLŒ$©iÓ¦*P €åœ7nÔ±cÇÌå*UªháÂ…rww·k_®\¹´xñb•.]ZQQQªT©’rçÎ-Ã0ÌûuàÀ­[·NÒÝÞ˜C‡Õš5k4þ|yxx¨mÛ¶êÔ©“òæÍk7>>^~~~Z¿~½ÎŸ?¯\¹r©FªS§Ž:uê$77û·ä?~\~~~ærïÞ½õÔSOYê¬Y³F‡’t÷ùyûí·Ím»wïÖæÍ›%I Ðo¼¡ÐÐP­^½Z7nÔ­[·Ô AuéÒE+V´;¿$ùúúê?þ$}÷ÝwêÞ½»ÃzÀeuâH¡P(г¢G¼‡_ñâÅÍž)“&M2š7oné­2hÐ ³nTT”ñúë¯;d gΜƄ ŒÄÄD‡çºv횥§^Ê’?~cÖ¬Y–}V®\™êÄ ,0 Ã0–/_nY߯_¿4¯ýÊ•+Æ•+Wn»|ù²åx+V¬0<==-ë¦M›fÖ 1Z¶lé´EŠ1–/_nwžøøxK½wÞyǮί¿þj©³aÃsÛ‰',ÛvìØa´k×Îa `ÄÄÄXŽ=nÜ8KQ£F¥yß6oÞlÙ§iӦ涔=üFŽéô;oÛ¶­ÝýOϤ+V¬0 *äð˜^^^Æ®]»¶;!!Áøì³ÏŒlÙ²9Ü×ÝÝÝøðÃÍú~~~©>{ .4ë¶jÕÊ\?~üx»s÷êÕ˲ïªU«Ò¼ÏFTT”ÃmÓ¦M3U¼xqã×_5ÜÜÜÌunnnFHHˆYÆ ÆSO=åôZš4iâ°WçâÅ‹-õvîÜiW§oß¾–ßpr_~ù¥¹­L™2Æ‘#GŒ%JØ?Ož<ÆÜ¹s^ëÙ³g-u“_×£€~ …B¡<¸’å  P( ÅYyœ¿"EŠØýE|óæÍ†aÜ¥ô™gžI5I*¯¼òŠÝynݺeT®\9]û9ÒÜ/½ß»ï¾ëpýýJø¥¼7FXX˜aw‡s–,Y2]×öÕW_YΓٟ——WªçïÓ§åØ)ø?þø#Í{sçÎ#GŽæ>Ù³g7âãã ðül6›åž¥lÏ3Ï>^ááá놡&Mš( @qqqÚ¿¿9 V’vìØ¡•+W¦ëÚbbb4räHsÙËËK3gÎTDD„,X`7!!A|ðeßhúôéær½zõ´cÇ…„„hΜ9–{ôÉ'Ÿ(**JuêÔIõÙKj¼uëVËDކ‘_¸pÁü\¬X1åÊ•+]ל111ŠŠŠRýúõõÉ'Ÿ¨E‹êÙ³§¤»²ôíÛWwîÜ‘t÷»9r¤víÚ¥;wjèСæq¢££Õ·o_%$$dZÛ’»qã†ÂÃÃÕ¯_?;vLçÎÓgŸ}fJüÎ;ï8<µjÕÌÏIÃ{Àß@V'Ž …B¡8+zŒzøIÖaªIâââŒÂ… [z»]»vÍR',,Ì(X° Y§hÑ¢æÐÞ«W¯Zz@U¨PÁnhi`` ¥NõêÕ-ÛÓš´£E‹–ë8sæŒ]ñãÇ9räpZú÷ïoÖMÙÃÏf³9ìÝhéÅÖ°aC#!!ÁRgïÞ½–V­[·6·ev¿üùó›=“lܸÑR§o߾涊+Z¶¥ü^œyî¹ç,û†aßÃ/oÞ¼vS;vÌr?Ú¶mknK­‡_ò^mrÐÛìÈ‘#–íÛ·o7·uíÚÕ\Ÿ3gNãÒ¥K–}¿…ÜU¥ IDATýö[ÃÝÝݨ\¹²Ñ­[7ãðáÃæ¶´&í˜2eJªÏfÊ{R³fM‡÷´yóæ©>ŸÉ{¿¥¼^^^Fll¬Ý1Ço©÷ïÿÛ®Î|`©3uêTs[föð“d¼ð vû:4Í^~ 0·7hÐÀáýË*ôð£P( åÁzø *¤Ø­ß¼y³®^½j.¿ýöÛzòÉ'-u .¬·ÞzË\ ÕéÓ§%I«V­²ô€3fŒræÌiÙ¿\¹rêÓ§¹|äÈݼy3ÝmOÙž»:‰‰‰Š‹‹sZâãã¿yóæjܸ±ÝúåË—Ë0 sù£>²›ˆ¡^½zêܹ³¹¼{÷nË>™©gÏžv“ƒøøøX&D8qâ„ù9{öì–ºÎzê¥fYvÖc­cÇŽ*T¨e]åʕմiSsùÔ©Sé:gÒÄÒÝžv)¿ªU«Z&}Ù³gùùèÑ£æg/^ܲïàÁƒuëÖ-;vLK–,±ô(KË¥K—ÌÏ)'ë¤|ùòYž‰äõ“KíÙŒ‹‹3{é92bÄåÈ‘Ãn}òÉ^ (`é!™dôèÑÊ;·¹¼sçN§çɨáÇۭ{ölº†‘&£¢¢T¼xq»råʳNÒ0ïÄÄDË5§œ Z’<<<ìBèôJàåÏŸßn»»»»%` UTTÔ}Ëg3Ü&¿nooo‡×˜;wnËìÄɃՔ2ú|&“”+WÎ2[sÊß”t74M’ü€k#ð 8 mRþ…>eoº$ÉÿR.I×®]s¸¿£^PŽöOoo3IªQ£†eù¯¿þ²«Ó¿;vÌ,~~~é>~Ê^sI’÷\”dy__rÎîMJ·nݲ[w/÷!ùûМ?yÏÉ”÷mÿþýiž#88X‘‘‘æ²———Ýõ¥%oÞ¼æçÄÄÄt…FÉüÄÄD]¹rÅ®$Ï;'én8˜üøÎîÑýJÞ¥ì1™$=ÏçâÅ‹-Ïç!CÒ݆ô<ŸÎ~w’õùpölJ>“{IräÈa¹oŽÎ‘<¨LLLL÷ùÀãÍ#«€+pV<õÔS–å3gΘ$—r〉U«:Ü?((H¥K—Nsÿ{Vùâ‹/jðàÁfà3cÆ ½÷Þ{*Q¢„Y§`Á‚*X° ¹|/=“²eËæp½£kK>)E’¤ðIºzT©RÅáñ’iIî¥GSpp°ÃõÉò佹|||ôÝwß™ËãÆÓ€,C>>Në&Ÿ¬ÂY{Š+–êù’<ýôÓ:|ø°$©D‰š5kVªõ“ìüùó«páÂæ}L~îäÂÃÆީI>ᇳá¦/¿ü²~ÿýwsù“O>ÑÆ-uRN˜â,Äs$µßî7$¥Þ{3ùó™Úï.3žÏ”×yýúuK0é¨Whò°H‘"é>x¼ÑÀLÞÞI)gàM²|ùró³Íf3g+MÏþ†ahÅŠær¾|ù,Á@ò¡ÆŽ‚ºB… ©C‡ærLLŒ†®ØØX‡m•¤}ûö9Ý–’³{S³fM˲£k‹×êÕ«Íå²eË*Ož<’î‰É‡: TRΤ›š5kÖØ½ë-**Ê23o¹råÌÏ:t°„–!!!zçwœö¢Z¿~½æÌ™cY7hÐ §íùßÿþgw¬¸¸8íÝ»×\N@¦&y»ÃÂÂÔ´iS=ÿüófiݺµŠ)¢æÍ›ëùçŸWýúõÍúɇ¼îÚµËî¹Ø¿¿ .¬"EЍE‹–g1­g/åp]G^|ñEË÷¼iÓ&ýôÓON¯566VN·§ä,NþÛ;uê”° '8p eø_ò!‡111º}û¶];'Mšd©·lÙ25hÐ@k×®5{!]¼xQ«V­R«V­ôúë¯;¹öœÝ›Ž;ZÎùõ×_kÆ æòíÛ·5|øpË;ïRdÉ{û8pÀ2¤òرciödKîÔ©S?~¼eÝØ±ca.·iÓÆüœ={v}ôÑG–úßÿ½Z¶l©­[·êÖ­[JLLÔÑ£Gõþûï«]»v–@±S§NjРÓö\ºtÉn¢ˆÏ>ûÌ0=÷Üs麶®]»šŸôñÇ[¶OžüðCó}t·oß6{-^½zU[¶l±¼ç/ù÷m÷ì%,SöVKòÄOh„ –uýû÷×k¯½¦¿þúK±±±JLLT@@€f̘¡J•*9 Öqö|öêÕËü|çÎõïßß2¡Í¹sç4pà@s9{öì–És’zé&IþlKÒ”)S,½Ó2}útËï<""B~ø¡åüÏ>û¬Ý~—/_vÚ&à²zš` …B¡PœI‹$’ OOOãQS¼xq#©}Ý»wwZoãÆ†»»»YW’Q½zu£GFÕªU-ë‹-j\½zÕ²ÿ¢E‹,u$õë×7zôèa”/_Þ²¾B… Ftt´eÿ#FXêÔ©SǨS§Žñßÿþ×RoîܹvíL*yòäq¸^’a³ÙŒ)S¦˜Ç¹|ù²eû—_~éôÞLž<ÙR×ÍÍÍxæ™gŒîÝ»¥K—¶lkР‘`Ù¿_¿~–:U«V5>úè#£OŸ>Æ“O>i×Ö 6˜ûž8qÂáõ4mÚÔxóÍ7 XÖ—-[Öˆ·»†>}ú8<Ž›››‘+W.‡ÛÊ”)c÷=GDD8¬[«V-cøðáFË–--ë‹/nܼyÓÜÿâÅ‹–íãÆ³¿Y³f–ííÛ·7&Nœhôë×ÏÈ;·å¸ÉŸ¡˜˜£L™2–}«U«f¼üòËF¡B…,ë6lh9ç[o½e÷ìÕ®]Û˜7oža†qþüyËö3gÎ8}Vºvíêðþ¸»»9sætú|*TÈ8|ø°yœiÓ¦Y¶?~ÜáùŒV­ZYêæÏŸßxá…ŒöíÛùòå³lûì³ÏìŽQ°`AK^xÁøüó϶mÛÚýÖòçÏoÙ÷Ë/¿´»–\¹r]ºt1ú÷ïo”,YÒ²møðá¯ÃÛÛÛ¬3uêT§÷7+8p å5ö1ÿöP( …â %Ë@¡P(г" ü Ã0fΜiäÈ‘Ãi(!É(Y²¤±eˇûñÅNø¤R¡BÃßßßnßÕ«W;¬ï( øßÿþg.\8Õó¤ ØÖ­[g9ƽ~†a#GŽLó<µjÕ2íö ° ’J¶lÙŒ)S¦XÖ¥ø5kÖÌÈž=»ÃcåÍ›×X¿~½Ãö'&&o¿ývºïÙ3ÏeÊ”1Fm.§øµnÝÚ鱪U«f„……Ù?**ÊòçÆ¾}ûœ^kV ð£P( åÁ†ôðôïß_{÷îU·nÝ,³fJw'FèÛ·¯Ž9¢æÍ›;ÜôèÑÚºu«:tè ëœ[ Ôˆ#tðàA»÷âIRÛ¶m5tèP˺ìÙ³Û½¯N’Z·n­€€?Þéð¿‚ êå—_ÖŠ+tøðát+uf„ Z·nZµje7¼²hÑ¢;v¬þüóO‡ï'óööÖï¿ÿ®ªU«šÃ˜ÝÜÜT³fM­ZµJ½{÷Nw;^|ñEmÙ²EeÊ”±¬¯_¿¾öîÝkΛœ›››¾ùæíÝ»W]»vu8ë®›››4h ùóçkÛ¶m*V¬XšíiÓ¦¶oß®úõë[îKÕªUµsçNùúúZê§ü>SÞËråÊiß¾}zýõ×Í÷ &±Ùlzá…´}ûv5kÖÌ®--Z´Ð‘#GôÒK/Ù=¿Ù²eÓo¼¡½{÷Z†óJÒóÏ?Ÿæ³—üùÙ´i“ݹ“·ñ“O>‘¿¿¿† æt’Š+jذa:|ø°–/_n¯Š)¢íÛ·küøñ–wJwﯷ··V­Z¥Ÿ~úÉîw)Ý‚>aÂË» sçέ:h×®]ª\¹rºÛ2sæLM˜0Á2IKöìÙõúë¯kÏž='*ù믿Ì÷@–-[Vµk×N÷ùÀãÍfFV·‡l6Û"IÝ¥»³p:›EõqsëÖ-:uJaaaªX±¢]À”–ÈÈH*22R•+WNw ®#Gލ@ªR¥ŠÓÉ Ržëüùóºxñ¢òå˧%J¨L™2Nß{–Qááá Ô­[·TµjUKP’–«W¯êÈ‘#ªY³¦ž|òÉ4ëŸÕ«WOÒÝYfS›7¹;wîèÊ•+:wîœ"""T¤H•*U*]aêý Rpp°rçέjÕªÙ§©9yò¤ÂÃÃU¯^½týö¾úê+=Ú\Nš¥7!!A‡V||¼j×®ítRI3fŒ¾øâ IÒG}d÷îÆ¬æïïŸ2„ìkÆì,j.ÅþŸ"À•7oÞ õ´)P €Ü‹B… 9|©Zç*P €eöÑ©P¡BN{n¥¥páÂjÑ¢E¦´ÃÓÓÓ2Óñ½ðððPµjÕ2¥I ,˜æµíܹӲì,”¤œ9sªN:÷ÕwwwU®\ùžz§¥öÝÔ­[WuëÖÕ¾}û¤   yyy¥yL777•(QB%J”Hw;2ÊËË+]ms$eÁûåáá‘î??Ö®]+énïÈ{éí  éxŒ;V%K–T¿~ý,ëS ü5Ç7?Ï™3' [â:ÌY};wîl™¸>?€ÇضmÛbYW¾|yËðÞG]¯^½Ì^k3fÌÐíÛ·³¸E¿ï¾ûNÒÝw,þç?ÿÉâÖ€‡Àà1e†Ù‹+IÁ‚µbÅ åÍ›7‹ZuïÜÜÜ4iÒ$IÒ¥K—ôË/¿dq‹oš;w®$iðàÁ™6œ<>x‡ø[*[¶¬‚‚‚Ìå{œãQ±~ýzs‡† ªV­ZrwwÏêfݳæÍ›ë§Ÿ~Rhh¨råÊ•ÕÍÉrƒÖ+¯¼b.ßËl×/_Ö‡~(I8p`¦· <ú˜¥ðÈrÕYzÌÒ ÀƒÄ^À…ø.„Àp!~€ !ð\àBüBฬn©È•ô!&&FkÖ¬Éʶ2QPPPÊU¹Õ÷ÎfFV·‡l6ÛYI¥³º€‡â'Ã0úgu#p é\àBx‡àQ¨ÿÒ[¨P!?>‹›È,çÎÓG}”|Õɬj ®†wøY6›m‘¤î’äéé©ààà¬m Óøûû«víÚÉWõ5 cv5—Â^À…ø.„Àp!~€ !ð\àBüîÓ† ôÛo¿eu3)7oÞÔÚµk*I:r䈶lÙrOǘ3gަOŸž®ã>|X[·nÍX£\ p9§NÒW_}¥ðððzž~ýú©GºsçÎ=ÏãäÌ™3j×®vîÜ)Iš0a‚^{íµtï©7ß|S111·ª]»vÚ½{·$iüøñêÓ§O†Û àJü.#&&FþþþJLLÌê¦ ‹hôèÑ { çùþûïõã?ÊÍÿK•Y~þùgI"ÄȬn™% @õêÕSXX˜ .œÕÍÁß@ûöí³º œ H’òçÏoþïO<‘®} ÃÐ÷߯^½z™ÇÉÌãü]ðÏÑ8qéÒ%ùúújáÂ…vÛîܹ£W_}U_~ùe†Î±xñbùúúêöíÛúý÷ßÕ½{wU¬XQíÛ·×áÇî®ñãÇ«mÛ¶ªX±¢jÖ¬©!C†(""ÂRoÛ¶mòõõÕÅ‹õÖ[o©AƒZ¿~½bcc5dȵlÙRûöísxŽyóæ©C‡ªP¡‚Úµk§+V¤y-‹-RçÎuþüù{¿©ˆŠŠÒ¤I“Ô¢E •/_^µjÕÒË/¿¬Õ«WÛÕõ÷÷—¯¯¯ù½ :T¾¾¾òõõÕ·ß~ëðø¿ýö›|}}­õë׫[·nªZµªúöí«C‡ÙÕÿàƒÌcúúúêý÷ßwÚö¹sç꥗^’$ýúë¯êÖ­›*V¬¨Ž;êøñã÷ Ó!CT­Z55mÚTï¼óކ®víÚ©K—.iÞ/g¢££uüøñTˉ'îûøIž~úieË–MeÊ”‘$•)SFåÊ•K×¾ëׯW`` † â´NéÒ¥åááq_Çø» ‡N”(QB§OŸÖ¸qãÔ£G˶-[¶háÂ…zþùç3tŽãÇkåÊ•úç?ÿ©åË—«K—.jÖ¬™æÏŸ¯:èÔ©SÊ‘#‡eŸ¦M›*GŽjÕª•š6mª+Vèûï¿W@@€6mÚdÖ»pá‚V®\©øøxåÍ›WAAAêß¿¿š5k¦;wîÈßß_ƒÖŸþi9þ°aÃ4eÊU©REÝ»wײeËôÒK/iüøñ5j”Ók™1c†6mÚ¤.]º¨wïÞº/Inß¾­¶mÛêÀêÔ©“:v쨘˜=zTöÿ7ÆÍÍM9sæT¶lÙ$IÙ³gWΜ9%É\—ÒéÓ§µråJýñÇêÖ­›ªU«¦R¥JiÅŠúôÓOíê'?æÆÍÉ#9räˆüüü4|øpùùù©k×®Ê;·-Z¤ãÇëØ±c–눊ŠR“&M«Áƒ+""B3gÎT||¼¨§Ÿ~:ý7/…;vè¹çžKµN¶lÙßçî~mÛ¶5ÛZ½zõt³Ÿ:uªš5k¦5j8­ãîî®¶mÛªT©Ræñm6[†Ú àr àP( å‘,’I2$žžžFJÑÑÑÆÌ2þ|C’±iÓ&ËúÄÄD»}Ók„ †$ãÈ‘#–õýúõ3òåËgDEEÝ÷± Ã0>þøcC’ѦMãÚµkæú>øÀdlÛ¶ÍnŸË—/Û­ëÞ½»!É0×-X°Àd 0À0 Ãøðà Iưaà Ã0ŒwÞyÇÈ™3§qûömsŸ­[·’Œ1cƘë ___#wîÜÆ… œ^Ë©S§ŒY³f±±±÷pR·}ûvC’1eÊ”{ÚÏÏÏÏd;v,ͺ'N4$EŠ1üüüÌõéynš7on4nÜØéöwÞyÇd¼ð Æ7Ìõ#GŽ4$ûöí³ÔÿùçŸ IÆêÕ«ÍuIß›£ïý^ÄÄÄAAA©–ààà ##‚‚‚ 777cÑ¢EYÖ<\0’þŒÿ¿ÒÇxþÛC¡P(Š+zø[IïìKÉÇÇDz|óæMåÍ›÷¾ÎÑ«W/½÷Þ{š7ož9L4..NË–-S÷îÝ•;wîû:nJ~~~–c5nÜX’tùòe»ºÅг[×¹sg-^¼Xòöö¶lKYºti»åØØX;wNeË–•$}÷ÝwÊž=»† fîïææ¦AƒÉÏÏO+W®Ô›o¾éðÊ—/¯òå˧ûšÓ#{öì’¤‹/*!!Áa¯¾ÌòÍ7ߨsçÎærfMÄa³Ù´råJËñœ}¿!!!²ÙljÚ´©¹®Q£F’¤'N8üîÓ+gΜæ0ØGÑ?ü bÅŠ™C pÿü­š5kêÒ¥KæòÁƒõüóÏëèÑ£*X° ¹>Ož<÷}Ž¢E‹ª}ûöZ°`¾øâ Ùl6­^½Zׯ_Wß¾}3ÔþäR†K%K–”$%$$8Ý'44TÇŽSPPüýý%Ý}O[JIÓîCÒrRÀ˜üÇ—‡‡‡%ø’î¾³0iûÃT¯^=uíÚU_~ù¥æÏŸ¯nݺ©[·njذa¦Ÿ«cÇŽ™~LéîýNùý>õÔS’d7ÔµI“&2 C¿üò‹9K­ŸŸŸ²gÏ®ªU«f¨:xð`ªuÜÜÜÔ¼yó ç~ÄÅÅé§Ÿ~Ò!Cœ½@úø[*^¼¸¹|ñâEIwCºÌœ¥·oß¾zñŵmÛ65oÞ\óçÏW¥J•Ô¤I“L;ǽصk—Þ{ï=mÛ¶MO>ù¤Ê–-›i³”†††ªL™2òõõµÛöÒK/e8tºW6›MK–,ѯ¿þª… jæÌ™š0a‚êÖ­« ¨bÅŠµ=Z³fÍôÊ+¯¨ÿþZµj•®^½ª­[·jüøñ*T¨P†Žý×_=”wøÝÅ‹ëúõë4hÐC?7€+"ð :tP‘"E4oÞ<Õ¨QC«W¯Ö'Ÿ|’%m ÖóÏ?¯Š+j×®]æpOÕ®];ÃÇ/[¶¬bbbRyöa³Ùlêܹ³:wî¬[·niúôéúâ‹/Ôºuk={Ö¥&lpww×È‘#µbÅ ÅÆÆªzõêúè£ì†©ßV­Z)&&&Õ:Yu/§Nª—^zI%J”È’ó¸?Ò-[6õìÙS³gÏV­Zµ”i³ÐÞ«ßÿ]7nÜЄ ̰O’Nž<™)ǯS§Ž~øá:t(Õ™R³JÞ¼y5jÔ(ÅÅÅi̘1 –———]½¤à***êa71CvíÚ¥6mÚhîܹzùå—3õØI3?jþúë/ýùçŸ?~|V7ÀedÎÛ¨x$½Ó/£CéÓ§"##õ¯ýKÏ?ÿ|–õDÊ‘#‡$éÀ’$Ã0´téRó}‚ŽÞáw/ÆŒ£ÓîÝ»eFV71ÓM:U5jÔP³fͲº).ƒ~—‘ò~™©fÍšª]»¶8©“uÜ«_|Q}ô‘FŒ¡Ù³g+44T7nÜÐâŋթS'ó=†÷«D‰š7ožúôé£êÕ«ËÇÇGO<ñ„Ž9¢€€>}Úœp"¥>ø@[¶l‘···9áDF………©dÉ’ªY³¦5j¤ÐÐPíØ±C!!!š5k–9‹oJ¥K—VÍš5õþûïkóæÍº~ýºöï߯¥K—ªC‡™Ò¶á7ÞÐæÍ›5uêT»mcÇŽÕðáÃ5yòä,hÙƒ®E‹¹Ô5< Ü?þøã¬n}òÉ']%U“¤ hĈYÚžòåË«lÙ²êׯŸÜÝÝ3å˜6›MžžžjÑ¢…e&W›Í¦W®\êׯŸ<==¡Ž;jâĉzæ™g”#GÕ©SGåË—7Q´hQµhÑByóæµ,'ÍÐ[¼xq˲$UªTI½{÷Vîܹ©ððpyzzjðàÁªW¯žÓ­|ùò*R¤ˆú÷ï¯\¹reÊý1 CÅ‹Wtt´.\¸ =ûì³úé§ŸÔºuëT÷õõõÕÓO?­àà`yzzê­·ÞR»víÎ[ªT)ùøøÈÃãÞþ-Ôf³©V­ZªS§ŽÓ:åʕӳÏ>k·>þüòññ±ôH=þ¼æÌ™£·ß~[ ,Ї~¨O?ýTƒÖ¡C‡´fÍ5*Óž¿¬öÇèòåËúòË/™÷oèòåËš>}zòU+?þøcÿ¬j®ÄæŠCC®Áf³-’Ô]’<==œµ °víÚ)_¾|Z²d‰Ý¶¡C‡jÅŠîÅ <*L6Ô×0ŒÙYÔ\ ïðxDDEEiïÞ½–÷ &&&júôéš1c†† –…­Àã‚wøþ6mÚ¤ƒ¦»~Ë–-ïi–Ú7êСCé®ßªU+U¯^=Ýõñ÷0qâDµoß^ÅŠSÆ •'O9rDQQQúüóÏõî»ïfuð ðü-lß¾]óçÏOwý¢E‹ÞSà·mÛ6-\¸0Ý—mb€ IDATõ‹/Nà;uëÖÕÙ³gõÛo¿éÔ©SÊ–-›  çž{NùòåËêæà1Á;ü,Þᮋwøðàð?À…ø.„Àp!~€ !ð\àBüBà¸?¥ ÃÈê&ào‚g ü]xduÀƒ­©S§jΜ9êܹ³þýïgi{~úé'-Y²Dû÷ï×Í›7UºtiùøøhĈòööÎÒ¶ÁµDFFjÔ¨QÚºu«.\¸ Zµj©W¯^2dˆ]Ý &hÕªUÚ´iÓ}«U«V²Ùlúã?2Úì‡êúõëúæ›o´téRõïß_#GŽ|(爈H¹ê9›ÍV࡜ìí6 cwV7È,~¸¸yóæéwÞÑ•+Wd†4hem¹s玆ªiÓ¦©yóæ6l˜ž|òI9sFK—.UãÆ üiÂÂÂÔªU+‘Dà—Á^€Ë8}ú´|}}uóæÍ¬nÊ#eÞ¼yjÚ´©:¤ìÙ³gi[üüü4mÚ41B›7oÖØ±c5lØ0Mœ8QgÏžU¯^½²´}p-“'OÖáÇ5kÖ,͘1C£FÒºuëäëë«/¾øBAAA™z¾íÛ·k×®]™zÌû‘˜˜¨¸¸¸t až={¶:wî¬={ö<„–€‡…~—©•+W*..NùòåKµîåË—µsçNåÉ“G5’a:uê”BCCÕ¡CKÝððp?^ÕªU“‡‡‡Ž?®C‡©^½z*[¶¬Ããß¼ySþþþ:wîœ*Uª¤êÕ«+GŽvõ®\¹¢K—.©V­Z–õ±±±:~ü¸Ê”)£þÿ·Ë—/ëòå˪Y³¦bbbô×_éÊ•+jذ¡J—.í°-óæÍSáÂ…S½ŽDGGëĉª]»ö=ïëÌ×_­ è‹/¾Íf³lswww¸ÏÕ«WuáÂU¯^]îîî ÐÑ£GU¿~}•)SÆá>7nÜ¿¿¿ÎŸ?¯Ê•+«zõêÃÎK—.),,L5jÔ°¬ŽŽÖÉ“'U¶lYåÏŸßR?44T5kÖTTT”öîÝ««W¯ªQ£F*UªÔ=Þ¬sáÂíÞ½[O>ù¤6l¨øøx:uJׯ_×sÏ=g©ªÕ¬YS6›M‡Ö‰'Ô A§ÏÜõë×uàÀ]¼xQUªTQµjÕ”-[6»z/^TDD„ªU«fYëÖ-ª\¹r–ßòÅ‹®5jèÖ­[úóÏ?¡Æë©§ž²#!!A³fÍ’···^}õUs½ÍfÓÇ,???Íœ9S_|ñ…Ãk¸uë–öìÙ£ÈÈH5jÔH%K–tXïäÉ“ŠŽŽ6—óå˧råÊ9¬›$66VЙ3gT©R%Õ®]ÛéóŸäܹs PDD„ªV­*ooo»{©àà`;wN’täÈ]¾|Y’T¤H‡×°víZ.\X‘‘‘©ž<^ü;3fÌЈ#äáá¡„„ÅÆÆš=aÊ—/oøýòË/8p ‚ƒƒ5pà@­_¿Þܶ|ùr½ôÒK–ú«W¯Ök¯½¦ððpåÊ•K111ªP¡‚–,YbìýðÃúøãízâ$m .Ô+¯¼b©ÿÉ'ŸhÍš5êÙ³§¢££'777M›6M´»Þû û$é•W^Ño¿ý¦¥K—ªk×®÷uŒä®\¹¢Ý»w«o߾ʕ+Wº÷[°`Þzë-…„„èÕW_ÕæÍ›Ím«W¯Vûöí-õW¬X¡×_]‘‘‘æý÷ööÖ’%K삥ɓ'kòäÉŠ‰‰±¬?tè7n,???uîÜÙ\?iÒ$}ýõ×Z³fºw﮸¸8ÅÅÅÉÃÃC?þø£^{íµ{¸#VGޱbš’»»»æÏŸßç¤ñãÇk̘1æ½¹}û¶ùüÕ¯_ß.ð›={¶Þ{ï=]½zU:uÒÎ;Ím6lPË–--õ—,Y¢èÆæ9ªW¯®%K–¨råÊ–º_ýµæÌ™c÷.·¿þúK>>>Z»v­Ú¶mk®7nœfΜ©Å‹ëÕW_5¿Ù²eÓܹs-¿•cÇŽéÒ¥KêÓ§Ý=¨Y³¦Ê”)£ 68¼G+W®TÏž=•˜˜¨ØØXyxxhÆŒêÛ·¯]ÝW_}Uûöí3—[·n­ÿýï+IûöíS·nݤlÙ²éöíÛzöÙgµhÑ"/^Ü®~xx¸†® Èf³)wîÜŠŠŠRΜ9uùòe=ñÄfÝuëÖYîA›6mÌÏo½õ–&MšdwüûýóáY,éý¬n€¿’Žgu#€…Àð·rìØ1 2D={öÔ”)S”˜˜¨Þ½{kõêÕ:{ö¬¥7WJ#FŒPDD„V¬X¡%JhÇŽzá…,uöï߯Ž;ªyóæš;w®J—.-uëÖM>>>:}ú´ ,˜áëøÇ?þ¡Ù³g«mÛ¶ºv횺uë¦Aƒ©Q£Fv½ÕîWÆ uðàA•/_>SŽwñâEIJ³÷“3o¾ù¦âââ´råJ-ZT;wî´„A’´k×.½ôÒKjÓ¦fÍš¥’%KšKÒýOí;NÃ0ôÚk¯iáÂ…jݺµBCCÕ¥KõíÛW5R¥J•îë¸îîîÊ™3gªuÜÜ2ö6–={öèÝwßÕСCõŸÿüGÑÑÑêÖ­›þüóO={Öa/Ô$ýû÷—‡‡‡V­Z¥'Ÿ|R{öì‘¥ÎæÍ›Õ½{wµoß^?þø£Š/®={ö¨[·njÙ²¥N:¥¾$3FcƌɔcIRHHˆ¤»í½‘‘‘úã?”;wnIR£FìêŒ=Z ÔŠ+Ì`©^½zZ´h‘5j¤ñãÇëÓO?½Ï+øÿÖ¬Y£ºuëJ’J•*¥yóæ©R¥JúöÛo5uêÔû:¦···fÏžá¶¥fãÆ’¤÷Þ{O¹råR®\¹4`ÀmÚ´IgÏžU:uœî­uëÖ™¿¥&MšØÕyï½÷T¬X1-_¾Ü¬×¨Q#Í›7O-Z´ÐäÉ“õ¯ý+C×àææ¦µkךÁ¶§§§æÎ«êÕ«ë»ï¾Ó„ $ýÿÀÏY¶B… içκsçŽ%Huww׺uëT½zuIR™2eôßÿþW5jÔÐwß}g÷ýzzzšŸÓ l?ûì3ݸqCÓ¦MS½zõ$Ý ROž<©¯¿þZ»wï¶<×?üðƒöïßoד¸lÙ²_'/_>U®\Ùü•/_Þa¯Aàúü­lÙ²Yþ2›ô¬¢E‹Zþâü]qE‹•$]»vÍ\.)í¿¬ÿøãfØçÈ;w´yófõèÑîçM­ZµT·n]3pɨ¤° IéÒ¥Õ¨Q£ Í0ú Ý¾}[’,alpp°~øás¹xñâ1b„ÃýgÍše†}ŽÄÇÇkûöí0`€]/²† ªjÕªÚ¸qc†?›Íf†}I*T¨ Úµk?Ò÷_ºûü†¡ÈÈHó}nIÏj½û¤»C{SûDEEéÏ?ÿÔˆ#ìê=ûì³*W®œ6nܘáÀ/{öìv½X«U«¦*UªXî||¼$ÉÃÃñÿÝÍ–-›”˜˜h üräÈa†}IªW¯.ooo8p Cmß¼y³J•*¥«W¯jíÚµæú¤÷îÛ·Ïø­ZµJåË—·{m€«pÐÛöáM €‹#ð<¶J—.m ‹öíÛ§_~ùEãÇwÚ«ÇÇÇG… Ò˜1c4iÒ$ÅÄÄè믿VåÊ•U¿~ýTÏ—2ÄKéüùóŠ‹‹s:„°bÅŠZ½zuWuÿ¼¼¼´råÊvüŒJšÔ"©§Ÿtwˆã¢E‹$ݤbÅŠN¿ä“—8¤ÄÄD§©T¬XQ;v츟¦§‹———¶lÙrßûïß¿_#GŽLµŽ»»»Ó÷Î¥GÛ¶m•/_>5JãÇWXX˜¾ýö[Õ«WOÞÞÞ©î›Öý ”¤TïÿÑ£Gï¯áéàå奃šËIÿh¦®‚ :œLÄ‘2eÊèðáÃjc`` âããåëëk·-GŽædIŽ=j÷ÞOW⠷h®ˆÀð·R°`AýñÇjÚ´©9|±aÆZºt©ÃY\ïEÒþ ·ß¾};ÃçHMlll†ßö =ýôÓ’d 5êׯ¯àà`IÒ3Ï<£›7oÞ÷ñ“îmRO”ÆýO­hZžx⠇Ô“Ëè;üJ•*¥µk×ÊÇÇÇìÅÖ¢E Íž=;ÃÇ~Ôî±bÅ$ÝYÙ‘Ë—/ßÓpט˜˜4CÏ´äÎ[:u2Cî´¸»»›=îào%66V'N”———fΜ©råÊ©H‘"™rì%J(Ož< øìÝwX××ðï "¨€bTPEcAÀ±Ä£ØÀ’¨±Å–Ø¢K4F%b= ÖXˆØ{°¢ˆRD–zÞ?üí¼Œ», è"žÏóÜçÉΜ¹sæ2¬ñxçŽÒý·nÝB½zõ”î{ýúu‰Šuo¿Õx3¨°ÙUåA5`mmýû÷cùòåÂ#ØeÅÂÂÚÚÚ¸{÷®Â¾üü|„††*Œ¿D"ANN²³³‹]Œ""‘èQq¸sçN¡³;‹ÃÆÆ .|çã‹#-- K—.E“&M°zõjXYY¡zõêeÒ·µµ5444”Ž^^ÂÂÂÖ”H$ÉdÈËË+öý ìÞÏÏÏÇÝ»wѤIa[ýúõ¡§§‡cÇŽaúô颸§OŸâÎ;4hP±úÏËËÃÝ»w•®ÛYvvvVX7°0 4@HH²²²Š|äš1Æc¬ ÒýS.cŒ1VŽ˜˜˜`äÈ‘ÐÕÕ-4fë֭ضmþùç|þùç011!//¯LrèÑ£<(ÌZ“;tè>|¨ð(Ÿ¼Ø,lËËËÃúõëUžgîܹ¢Ï'OžÄ½{÷Þ\Z¯^½*³¾$ &Mš„ØØØ2{±HAšššèÖ­üýý…7ËàÉ“'JÇ?//Oô(hnn.6nܨò\o¯xäÈ?!ËcŒ}²äÿJÍ7nܸ•·`@–––T‚‚‚HSS“¾¾>ikkÒÒÒ¢:ÐñãÇŽñóó#ÿQQQT¹re²±±¡ÀÀ@züø1íØ±ƒ ÈÆÆ†233Eñ—.]"äææFgΜ¡ƒRûöí©Aƒ€vîÜ)ŠŸ3g 3f ]»vöìÙCÆÆÆdddDIII 9mÞ¼™Ö¬YCkÖ¬!MMMjݺµð922²Ðk>|8I¥R:qâD‘×]\äääD‰„&NœHÇŽ£ÀÀ@šûŒ¦NJ!!!´eËÒ××§Úµk“L&SyýÍ›7§Ž;º?//Z¶lIššš4sæL ¥‡Ò¾}ûhÁ‚JéÚµ+ ‘#GÒåË—éÉ“'tôèQZ°`Âý,÷êÕ+ÒÖÖ& ¦£G’¯¯/={öL!Vþ]°téR@Â6eñeéÆ$ÿŽÿ_ó¡rðg7nÜ> Ò[ßA¾êΉ·²ljO€7nܸq+¬½‚Ñ´iÓ¨F4räHš8q"͘1ƒFMÆÆÆdoo¯_’‚Ñ›¢‡½½½ð?‰„<==)!!Aiüرc…X}}}š9s&«,øEDDPË–-…ã,,,èÖ­[Jû755}û/ÕBûçŸ ½Ž÷Qð#"ÊÌ̤#F¾¾¾(—N:ѵk×âKRð#" &[[[ÑøwïÞ]i1”ˆhĈB¬Í;—.^¼XhÁO"‘Ptt45oÞ\8ÎÊÊŠnß¾]òÁPƒ‘#G’¹¹9=Z¸ÿ¿ùæ244¤6mÚ(Ä—¤àGDtåʲ±±ïÞ½)%%Eiü Aƒ„XCCCZ°`PˆUVðÓÕÕ¥‡R“&M„ãêÕ«GíûùçŸáëë‹”””"ßÈ[/^¼À“'OP¯^=¨Œ}þü9`oo-­Â—ØõõõÅܹs!“É ££ƒ¸¸8¼zõ õë×/u¾Ê¼zõªÈÜß!22ééé¨[·n©^x¡L||<ž>} [[Û"ûŽERRìííU®%÷ã?bñâÅÈÏÏðæÃ¯_¿.tmÆòæ—_~ÁÆqõêU…7YOš4 «V­ÂëׯUÞƒÅ‡ØØXØÙÙ¹>åÓ§O‘’’{{{•kÛM˜0ëÖ­CFF†p\VVlllŠÌ'11OŸ>EÆ ‹½^ãÓ§O‘]ìµ15j„Ï>û EÆæææâáÇHKKƒ©©)êÔ©£2žˆðäÉ$''£aÆÅZÓ/;;aaa033C­ZµŠu ÒÍ›7áèèXpÓP"Ú¬¦tcŸ‰DR @fMs‰ÈWMé0Væø¥Œ1Æ>)»wƒƒB±O&“áøñãppp(“bðfMÁâ¾ÄÌÌ fff%>GÍš5Kô¦Ñ’z_Å>àÍš~uëÖ}oýרQCxSkQ>ûì3…{¢8ÊcE•Ý»wÃÙÙY¡Ø—––†Ó§O£U«VeRìJvoš››ÃÜܼÄç(É1ÆÆÆ ×]–ýË xo° ¥¥¥… »‰DKKKXZZûmmm4oÞ¼ØñŒ1Æ«8¸àÇcì“Ò¹sgüôÓO7nºu놪U«",, K—.EBBΞ=«î{o:wK—ÂÂÂÐÓÓÃ7°téRÈd2ìÝ»WÝ)~Tnß¾;w¢^½z8qâ²³³Ñºuku§ÅcŒ1Æ?ÆcŸ–Y³f¡ZµjX»v-Ö¯_‰D+++ôîÝãÇ/öŒ<Æ>F¿ýöÌḬ̀qãF,_¾R©ÖÖÖðññÁwß}‡ªU«ª;Åʽ{÷pøðaܽ{=z4† ¦î´cŒ1Æx ?Æcå×ûXÃ1ÆXùÀkø1ÆÔ‰×ðc]á«"3ÆcŒ1ÆcŒ1Æ>:\ðcŒ1ÆcŒ1Æc¬á‚cŒ1ÆcŒ1ÆcücŒ1ÆcŒ1Æ«@¸àÇcŒ1ÆcŒ1ÆXÂ?ÆcŒ1ÆcŒ1Æ*.ø1ÆcŒ1ÆcŒ1VpÁ1ÆcŒ1ÆcŒ± „ ~Œ1ÆcŒ1ÆcŒU ZêN€1ÆcïOBBvî܉‹/">>5kÖ„››¾ùæhjjª-¯ððp:t!!!HMME:uàîîŽ>}ú@[[[my±Š‡ˆ°mÛ6œ={111pttDŸ>}àää¤{ðàA\¿~?ÿüó;Ë×׉sæÌ)mÚDLL vî܉«W¯"11æææèܹ3 ‰D¢îôcŒ1V <Ã1Æ«À®]»†‰'",, UªTÁáÇ1zôh´k×999jÉéŸþ³³3,X€¤¤$T©R×®]À°|ùrµäÄ*¦ÜÜ\øøø`È!8sæ ¤R)Ö­[:tH!þøñãXºté;ŸÏÏÏýõWiR.AAAðôôDJJJ‘qÓ§OÇÇQ¹reÀÛÛ=zô} lcŒ1ö>ð ?ÆcFBBvìØQ£F¡R¥JêN§\hß¾=Q­Z5@ff&Ƈ 6`åÊ•˜8qâÍçúõëèß¿?Úµk‡ÀÈÈHØwïÞ=˜˜˜|Ð|XÅæçç‡-[¶`„ X¶l 55­[·Fÿþý+ºKëòåËå¢P‹ÀÀ@dee©ŒûꫯЧOÒÒÒàíí}ûöaçÎ0`À‡H—1ÆcïÏðcŒ1VaÄÄÄ`âĉHOO/V|rr2^¿~-Ú–˜˜XäqD„„„„"ãrssû^ ÙÙÙxñâE¡ûuuu…bŸüóÔ©SçÏŸ/²ÿüüüÒ'YÀo¿ý©TŠ={ö(ZìììP½zõbå¤êšårrrÞûøgeeëž)‘™™)|&"$%%y\~~~±®Y>þï“L&S™óÚµkQ£F üþûïÂ6CCC,[¶ غuk©ú›……,--‹›——Wâû3##ÏŸ?/v|QªT©"ûäŸ'Mš8wî\™‡1ÆcücŒ}r®^½ŠfÍš¡zõê022BóæÍÑ´iSèëë£U«V ñÿý7ŒŒŒ‡ à³Ï>C5`aaÿþûO!>&&Ð×ׇ¹¹9 àíí´´4…ØE‹)a###ìÝ»W´}áÂ…022Btt4<==a``SSSÔ«WÁÁÁźþ:uê@*•¹†ß´iÓP½zu\¹r¥XýåÕ«WØ»w/ºtéSSÓb·víZáåË—˜;w.jÕªSSSÔ©S§OŸVˆŽŽF‡„ñ744Ĉ#Š»0wî\Ô¬YSaûõë×add„#GŽˆ¶ûúú¢Zµjˆ‰‰AÇŽa``ØÙÙ!44´Ø×¤Lll,þúë/•ÍÏϯTç€3gÎÀÞÞ&&&044D‹-ààà===|ñÅ ñ+V¬€‘‘2331sæLÔ¬Y&&&°¶¶ÆÅ‹â#""àêê ===˜››ÃÈÈ£Gå¦OŸŽ:uê(l?þ<ŒŒŒpâÄ ÑöiÓ¦ÁÌÌ ÑÑÑpssƒŒÑ¨Q#ܽ{W!ÐÐPôìÙSá^wwwGõêÕ tŒ=zWWW¡ÿ àÖ­[JcÝÝÝadd$´ž={*“KMM…—— „ûsÖ¬YÈËË+ô˜uëÖÁÚÚúúú¨U«ªU«777¤¦¦ŠâöíÛ###|÷Ýw[[[!¯éÓ§«ÌK®^½z Ö5>cŒ1VzüH/cŒ±OJBBºté‚zõêáôéÓÈÊÊÂÔ©SqûömüóÏ?044T8&;;©©©X·n–-[†ÁƒÃÌÌ .\PXø?..ŽŽŽJ¥ðóóƒ.^¼ˆ3fàÆ†T*âe2™Â_Ú7³RSS‘-Ú.oÓ¦ úõë‡iÓ¦áùóç˜4iÜÝÝñðáCÔ¨QCå„„„ ''­[·V‰—/_">>^e\qÅÄÄ //M5Ùäo IDATš4)ÑqYYYHMMÅŸþ‰U«VaРA055ÅÅ‹ѬY3…s8::B__6l@½zõpîÜ9Ìš5 7oÞÄ•+WD…ŒÌÌL¥ãŸ››‹ÔÔT…u333‘’’‚V­ZaÀ€˜={6ž[·nèèèRç¦ÊÁƒ‘žžŽßÿ](ÖEEEa„ hÔ¨êÖ­[è±ÿý7.]º¤ò~˜1crrrpöìYÔ®]À›ñ711Áˆ#Êd]¸üü|,^¼¸¸¸ÀØØݺuúuë0yòd ÕÆÆÆJû©^½:nܸ¡°=//K–,ò”÷ß½{w¬^½?ýô“(¾wïÞ¯_¿^eî+W®ÄÝ»wqàÀ|ùå—€Ž;BCC«W¯Æøñã…û±yófÌ›7Ot_µhÑBiÿ666°±±®®.þþûo 2Dé VUŽ?¸¹¹•è8ÆcŒ•/üH/cŒ±Ö‹/D;ÊÏÛ¼y³h{ÁYZòÿ®U«–°M>#®¨µº¶nݪ²Ø»víB·nÝâz÷î kkkìÚµ«ø¨Âˆ#DŸÛ´i{{{¥ôôéS,^¼cÆŒ½½½ÊX--­2+öÀË—/@4+)%%ÇŽÚ…  =~çÎ*‹}D„Ý»w£wïÞ¢¢ xyyÁÜܼLÆ_"‘( ;tè?~¼Ôý¿O999H$¢¢kqïÿ={ö¨¼rrr€~ýú Å>9ooo˜ššbçÎ¥ÈþJ•* Å>¹.]ºÀÜÜ\tÿËgêëë+íGOO™™™ /¶¨\¹²BQ²k×®077Ç©S§J•ûîÝ»annŽ:@&“ ÍËË ¹¹¹8{ö¬(~ݺu055Å?üPªó×íÛ·áç營~úIôÉcŒ±ÏðcŒ1öѲ··Ç½{÷„ÏÁÁÁprrÂ¥K— ÕÓ¡Chhhà÷ßÇŠ+››‹Õ«WÃÈÈÎÎÎ*Ïgkk«rBB^½z¥´&‘HФI¥kž•• (]Ó® ñãÇ£zõê˜?þ{Ë£0ò"SÁž„‡‡‹Ökܸq¡káÙÙÙ©ì?&&2™Léøkhh qãÆ¸sçλ¤^, 4ÀÍ›7ßùøGáï¿ÿV£¡¡Ù³g¿ó9:vì"Â’%K0þ|dffbíÚµ¨Y³f‘Z5þ=B^^žÒñ×ÒÒ‚½½=>|øÎ¹«"‘H`kk‹¨¨(a›üÏ)))Jyùò%ôõõ¡££S¬þëׯ_ê˜<@JJJ¡EÈû÷ï‹>‡‡‡ÃÁÁÚÚÚ¥:oqäççcÔ¨QhРÁ+02Æcìýá‚cŒ±OJ:u°qãF 6 ûöíCNNòóó±uëV¥ë—•„|í®Ê•++ݯ§§§r}¯ÒÒÑÑQXs®   ((çÎ+´àð>Ég}ÉíÞ<îüèÑ#ÀW_}¥°faI¨{üµµµUŽQ’’’pìØ1•1ššš¥*ø5iÒ+V¬Àøñã±}ûv¤¥¥AGG»wïF¥J•Þ¹_@ýãÿöý/Ÿ¹XØ…Kô¸«T*­?ø.²²²àéé‰9sæ(Ýÿv>/_¾,rVqY™4i9ùùù066ÆÄ‰accWW×½5¶0J¥Bëm÷ïß/t´ÜÜ\hi•îåÈÈÈB¹Ü¹s'|}}ñï¿ÿ–ø¥e¥V­Z044Äÿý‡E‹xS¤‘¿¥UGG§T?+++hhh”xüóòò@D¥.æDEEÁÚÚúwrr^˜ð>I$Ô¨Q“&M‚µµ5\]]^Dñ.äQ6 ®°ñûÅïêíñ·¶¶†††Î;‡ &ˆb_½z…°°0¸»»»ÿÈÈÈRý|7³„“““ñùçŸ+ÞÎηoß.Õ9‹cÅŠX¿~=NŸ>]êkdŒ1ÆXùÀkø1Æ«0*Uª[[[•…³Ã‡cĈ8rä¦M›†¾}û–I±x3ûªeË–Ø»w¯ÂL¦ððp\¿~mÚ´m—Ï´ m Qy®ƒŠ>GDDàÊ•+ ýÀÙ³g1tèP¬[·®Èu¼OR©£FÂÍ›7qéÒ¥2ï¿R¥JhÖ¬öìÙ™L&ÚŒ°°0…ñ©R¥ rrr¥Tö29"¡C‡DÛîܹƒ›7o–û7›nÛ¶ “'OÆ™3g0eÊôîÝ»LŠ}```€Fa×®] 3/\¸€‡*ÿôôt…"¡ªñ—Éd kU†„„àÞ½{¢ñ755Å_|£G"99Y€¬¬,¥/pÉÈÈPX‹ñÊ•+ˆˆˆ€‹‹K¡y‡»»;®^½ª°V_a:t耇b÷îÝ%:üà‚Ïfÿþý˜:u*þùç•/-bŒ1ÆØÇ… ~Œ1Æ* ùš~FFF…ÆÄÅÅ!??ƒF÷îÝááá777¸»»ã—_~ÁãÇK•Ãüùó‘’’‚E¿ØØXxyyAOOOá ®ò—PlذéééHII¯¯/Ƨò<#GŽ„¿¿?rss/¼Ä`üøñ¢¸û÷ï£gÏžðòò‚³³3îÝ»'´·‹\o[³f Z¶lYd\IL˜0FFFèÚµ+<ˆŒŒ ¤§§ãßÿElll©ûŸ?>0xð`dddž[·n%---@•*U"T¿~}züø±BìØ±cIOOöíÛGÚÚÚ¢þ~'“’’⣣£©C‡€ôôôÈÚÚš444H"‘УG =Oûöí U«V455ISSSáw gÏž…~?Ô¬Y³Èë-­7n¼}^*öpãÆíÓh*½õä«îœ¸q+Ë&!¢2/"2ÆceA"‘ìÐ,--Ký†L  Aƒ°mÛ6tìØQ´oÊ”)Xºt)RRR`hh(lþü9î߿֭[ûm™111¸|ù2ž~ü—/_ÆÓ§Oagg‡¶mÛŠ~¦effbïÞ½HJJB«V­Ð¼ys¤§§#$$7=òúã?bñâÅÈÏÏGBBNœ8ôôt´oß^Xî<Û¿?F…}ûö¡U«V¢}#FŒÀÖ­[ñúõkÑ8ÇÄÄ 22íÚµƒ†Fñyôè®\¹‚ØØX4lØmÚ´Qú³ÞÌVÛ·oRRRкuk4kÖ ©©©¸yó&š4i"ú½™0a‚03>>AAAÉdhß¾}¡ëWo^séÒ%ÄÄÄÀÑÑ-Z´Púr‘ˆˆ<þ...ˆ‹‹CPP²³³Ñ¾}{a­IUall¬ðHðÛrrrpéÒ%ܾ}iii055…»»;,-- =æÚµk¸sç’““ѤI4oÞ¼Ð{Zîúõë8sæ ÌÌÌо}{…—‚„‡‡úR…{¤¬Ý¼yŽŽŽ7 %¢Íïõ¤Œ1ö?‰¤€Ì›æ‘¯šÒa¬ÌqÁ1ÆX¹õ> ~;w†žžöíÛ§°oÈ!8yò$ž>}Zêó¼/ ~:::êNç“S°à÷1rvv†½½=6mÚ¤°ïË/¿DTT”Âz’åIÁ‚_ydhh¯¹÷©â‚cL¸àÇ*:^Ã1ÆØ'ÅÜÜGŽA@@€°žUdd$† †;v`Ù²ejα÷ÇÜÜ{öìÁ‘#G™ùæï8wïÞEÿþýˆßÿ]Í~\^¼x   <~ü~~~xõê6l¨î´cŒ1ÆP¼çbcŒ± bÙ²eHOOG¿~ý ‘H •J‘™™‰¶mÛâØ±c ù2V‘¬[·>>>èÖ­¤R)444 “ÉбcGœ!‰‰‰xõꕺÓäææB&“!//OÝ©°O@ll,._¾ŒŒŒŒBcrss‘••õÎ爋‹C||ü;¯n HOOWwŒ1Æ+\ðcŒ1Æ>!!!èÚµ+LLLðý÷ß«;@×®]¡««‹)S¦¨;Vݸq666077G«V­```€¾}ûB&“)ÄNœ8Õ«Wçs999ÁÉÉ©4é–‰ÄÄD\¾|999ÅŠ?wî\]]Q£F üúë¯ï9;ÆcŒ}\ðcŒ1Æ*¸©S§¢yóæ …†Fùø£?%%'Ož„††öîÝ«îtX777dggcûöí¸~ý:|}}€/¿ü²ÌÏ7hÐ ôïß¿Ìû-©Ã‡£U«VHJJ*2ÖÇÇíÚµCttôûOŒ1ÆcLùø¿~Æc¬ ܼyFFFHNNVw*åJrr2V®\‰ˆˆhii©;À››‹±cÇâÉ“'¸zõªºSbÐÒ¥K‘žžŽÿý @óæÍ1kÖ,̘1ÇÇ¥K—Êô| .Äâŋ˴Ï÷-==›7oFpp°ºSaŒ1ÆX*ÿ×ÏcŒ•¼¼<¤¦¦"??¿ÈØü÷ßˆŠŠ‚®®.Z·n~ýú) GPP¾ùæ¼~ý{÷îEhh(œœœàéé‰Zµj‰âÃÂÂpâÄ |ûí·HOOÇÞ½{'''xxx(Ä@pp0þûï?}£GFJJ öî݋۷o£E‹ðôôDÍš5UÆàÎ;…ÆÀ•+WpâÄ  3gÎ(į]»–Ð7ÈÜÜ\öìY…øŽ;’T*UÈg̘1¢¸œœ255¥®]»*ôJhÉ’%*¯][[›†ª2FÎÝÝЦM›Š_\©©©¤­­M&LÎcmm]hüòåË …††RÍš5ISS“ôôô]¹rE!~Ù²e€ÂÂÂâ¯^½ªïââBÚÚÚÔ¼ysrww'---@“'OÅÉd2222¢>}ú(ôqåÊ@+W®,épÎ;G5jÔPÙjÕªõÎýåççÓçŸNÚÚÚäääDnnn¤©©IhÚ´iJY´h ;w iiiQåÊ• ݺuK!~áÂ…B¼±±±(>44T!gggÒÑÑQÈgæÌ™¢ØôôtÒ××§*œóÌ™3€Ö¯_/l &4þ|¥×Õ¨Q#jÒ¤‰hÛØ±cIWW—&MšD•*U¢öíÛ“µµ5 ¥ýtîÜYøùhiiQÇŽ•ÆÉ­Y³†´µµI[[›Ú´iCdjjJ—.]RéÒ%ª[·.éèè³³3uéÒ…j×®Mzzz”‘‘!ŠÝ¿?Õ¨Qƒ ™˜˜¹Íš5Ke^)))*ïƒ÷áÆ$ÿŽÿ_ó¡rðg7nÜ> Ò[ßA¾êΉ·²ljO€7nܸq+¬½¯‚ßÿýGqqqÂçÄÄDrqq!‰DBÑÑÑ ñò‚Ÿ=š)77—‚ƒƒ•ö//øÙÙÙÑwß}Wd|`` ÅÇÇ‹òiݺ5ihhPLLŒ(vâĉ¤¥¥E ¢í?þø#iii‰®K™’ü:DC† ¡gÏž+¾¸¶mÛFèôéÓDôÿ½¥ñòývvv4aÂJNN¦œœœBÇS^ð³³³£ï¿ÿ¾Èø£GŠî™„„rrr"---ÑÏ…ˆhôèѤ££C/_¾mŸ0aéèèPrrr±ÇámÏž=£M›6©lÿý÷;÷/wäÈJLL>ÇÅÅQÓ¦MI*•RRR’B¼¼àgggG?ü𥤤Pvvv¡?/yÁÏÎÎŽ~üñGzùò¥ÊøÃ‡‹ÎG¤­­M)))¢XÒÓÓ£ôôtÑö‘#G’žž½zõJÔ¯ª‚u‡ÈÄÄD´mìØ±€Z·nM©©©DD”——G ¤´/¹æÍ›«,øÝºu‹444hðàÁÂïð½{÷¨^½zdggG999¢ø—/_R5¨Q£Ftÿþ}Ѿ·Ç  Í›7zþü¹Ê| â‚7nÜ>µÆ?n½©=nܸqãÆ­°VTÁïÆdhh(4ù,:ÑvU1–[½z5 #GŽ(ì“üFŽYd?Dÿ_ð+lÆ`qüù矀EÛoݺEèÏ?ÿ¶åçç“……õèÑ£È~KRð{_zõêEÆÆÆ”››KDDÑÑÑJgtÉÉ ~òE‘ü¾ÿþûwÎqñâÅJgpÊgòœI–››K5kÖ¤~ýú½óùÔmþüù€.^¼¨°O^ðûñNj՗¼àWšÂѼyó”Îà”ÏäÛ¶m›°-;;›ªU«FÞÞÞ¢Ø7:xð Òs|ýõ×$‘H(;;[Ø6vìXÒÖÖV(èÆÅÅ‘¶¶v‘?㢠~}ûö%MMMzòä‰hûï¿ÿNèßÿmŸ6mijjÒÍ›7Už÷m\ðãÆ·¢ü¸UôÆkø1ÆûhYZZ⯿þ>GEEaæÌ™X²d‰°nèèèÚGnn.ž>}*Ä«záǬY³J”_Iã‹“ƒƒ±}ûvŒ;pþüy´×¯_ãØ±c°²²½ÜÀÐÐþþþøå—_ =öCŒNNž>} CCCoÞ&\³³34h€mÛ¶aøðဓ'O"..C‡-ñùÔ-''111…^oAjücbb`dd¤4X[[cÛ¶m8p àØ±cHNNVùÚv¹¹¹…žKCCššš¢íR©T¹5j qãÆxðàA‰¯© 7nÀÄÄ«V­mOLLܹs]»v¶_¸p k2ÆcŒ… ~Œ1Æ>ZU«VEÿþý…ÏÁÁÁ˜9s&zõêccãBKJJŠ+àïï‡"''EžOC£d/·/n|bb¢ODDD‘ùøøø`„ ˆŒŒ„ ¶oßSSSQ¡ ¼:|ø0233ƒ…  Ûsrrpÿþ}„‡‡£Q£FJ}»0S”âÆÇÇÇcÅŠØ»w/"""››[äøO›6 OŸ>…¹¹9¶oßssstêÔ©Dù½íĉèØ±£ÊMMÍB XŇeË–aÿþýˆŒŒ,òz ž»$Šÿüùs!Ÿ¨¨(•ùH$x{{ãçŸFBBLMM±}ûvØØØ ]»v¢Ø5jøÿbÚÛ’’’`bbRìßS333ܸq£X±…yüø1jÖ¬‰›7o*ìóððýCÜ¿...¥:'cŒ1Æ>M\ðcŒ1öIIKKƒ››âââ0iÒ$téÒÖÖÖxüø17nüÁóIMM…««+^¼x!äcee…¨¨(4mÚTé1 À”)S°}ûvLŸ>{öìÁСC¡¥UþÿX€‘‘ •J…í÷ï߇ -ø½ÉÉÉpqqAZZ&MšOOOXYY!<<¼Ð··<3fÌÀŽ;0~üxìÝ»ãÇ/qAømŽŽŽ8zô¨Ê‰DRªs$&&¢M›6Éd˜„„„´iÓÙÙÙB>VVV¸víÜÝÝ•3dÈøúúb×®]6l<ˆ3f(ŒümÌ=RÚ¼øV\‰‰‰033+v¼25kÖDÓ¦MqðàÁbÅ#!!¡TçdŒ1ÆØ§©üÿÍ€1Æ+CGExx¸è‘@àÍ£¦êpäÈܹs1Ì- IDAT;wîÍVT•±±1ºuë†íÛ·ÃÑÑQéãŒåQff&>Œ^½z‰Š}`kk‹† ÂßßsæÌù`98p>ľ}ûгgOa{FFF¡Ç˜™™¡sçÎØ¶m¬¬¬––ŸRçR­Z5xzz–ºU…ÿýW4#TÕõ¾OþþþxôèŽ=*ºvUùÔ©SnnnضmªV­Š¬¬,x{{+Ä5hП}ööïßùóç‹öݼy?F¿~ýŽËÍÍEFF*W®,l{õêÂÃÃñå—_¾ËeŠrº|ù2ÒÒÒfó)coo#GŽ ..®DÅI¹¼¼¼wI“1µ‘H$~LÕûd¼ý/uý%‰òmeìýXBDçßWç\ðcŒ1VaX[[cçÎ*OLJJèêê Û¢££1`Ào-ý”å%# ËÇÇÇ=zôÀôéÓÑ¢E ØÛÛ—yn FçÎKü8§2xýú5zõê¥t¯^½ð믿âÁƒ¨_¿~©ÏWòÇ= w?ÿü3vìØ!üŽçççÃ××5j”ÂqYYY6lvíÚ%l[²d ÒÓÓ•KbÊ”)èܹ3¦L™‚5kÖ9+tÊ”)ÀÈ‘#±{÷n•ë‘daa¸zõj¡¿oŒ•SXª; öɲý_cìCÙUtÈ»+ݳ'Œ1ÆX9"_ÓO¾X¿2¨\¹2Fމï¿ÿ @“&MЧOoÖû<==¡««‹o¾ù'N„——š6mо}ûªÌ§K—.055ÅíÛ·…bMaFމþýû£ÿþÈÍÍÅéÓ§…Ï—/_.ô¸Áƒ£K—.¢ÂGiøûûCWW·ÐYl½{÷â>”®]»B[[C† Áäɓѯ_?4oÞ^^^ ÿ=z jÕªÅÿò¤[·nJ¥8p ¦L™‚¾}ûÂÙÙY(†}èû¿{÷îÐÒÒ‚——¦L™‚¯¾ú -[¶ Þ…åÓ§OèëëãÎ;*g·N˜0Íš5ðaÃ0|øpÌŸ?îîî8pà|}}aee¥pŒŽŽž={gggüñÇ>|8æÍ›‡: sçÎ¥ºÞN:aܸqðó󃻻;þüóO¬_¿ßÿ=<==ÖgtvvÆÜ¹sqèÐ!´lÙ‹-ÂÖ­[1mÚ4xzz"==]éy\\\`dd„)S¦`éÒ¥˜6mÜÝÝqçÎ…Xùwü%4¶Ý½{·T×ËcŒ1õá~Œ1Æ>)ÖÖÖ Š+°uëV4mÚË—/‡îÞ½«ô1;333¸ºº{vMIâëÖ­+ä³eË8::båÊ•2dÂÃᯯ¯ô8---ôéÓ›7o= ¬LBB‚ð¶Sù âââ2™¬Ðãzö쉗/_ÂÉÉ©Èë( !-- cÆŒÍæ*¨Y³fèÑ£‡›œ¹¹9\]]‹½FaIâ6lˆãÇãÏ?ÿÄæÍ›Ñ¼ysøùùÁËË 7nÜ(4WôèÑþþþBqöcààà€ÀÀ@¬\¹›6m‚““6lØ€¯¾ú ÁÁÁ¢™¦rpuu-ö…%‰oÚ´©ÏæÍ›áää„M›6¡W¯^F¥J•”§§§‡nݺ!00Pô(öÛªU«†Ó§OcÚ´i8{ö,àèèˆõë× ®‚êÕ«www`úôéXµj²³³ñí·ßbùòåźþ¢¬X±®®®Xµj~ù夥¥ÁÔÔíÛ·GFF†Â åY³f¡]»vX´hþúë/$''£I“&hÑ¢d2™Òï---lÙ²k׮Ŝ9s„ÇЕÍ~.øûæêê*Úö¡g|2VP•*UP«V-u§ÁceF&“áñãÇì|"ú`'cŒ1ÆJB"‘ìÐ,--­Þ„Ê‘¼¼<4hÐ...ذaƒºÓùädgg£~ýúèÚµ+V­Z¥ît>92™ 666ðòòÂ’%KÔŽ nݺ°µµÅáÇÕÊGáæÍ›ptt,¸i(mVS:¬H$Ñøß#½ýúõ+³æŒ1V\¾|ùí—ÂyÑ{û¢ãGzcŒ±Ðúõë‰ &¨;•OÒš5k‹qãÆ©;•OÒ²e˘˜ˆ1cƨ;AFF=zôN/×`Œ1Æ+küH/cŒ1ö‘¸ví/^ŒÄÄDœ:u Ó§O‡ƒƒƒºÓúd\¸p+V¬@\\Ξ=‹¹sçÂÎÎNÝi}2N:???ÄÄÄàÂ… X´h‘Ò5ø>¤ãÇcîܹ¨W¯Î;‡üü|~IcŒ1ÆÊ.ø1Æc --- 77§N‚›››ºSú¤hkkCOO&&&8wîÚ¶m«î”>)•*UB¥J•P«V-\ºt Ÿþ¹ºS‚¾¾>Z´h;wî Y³f˜?>ºuë¦î´cŒ1ƸàÇcŒ},ä/`êÑ¢E ´hÑBÝi|²Zµjõöº7jWsbŒ1Æx ?ÆcŒ1ÆcŒ1Æ*.ø1ÆcŒ1ÆcŒ1VpÁ1ÆcŒ1ÆcŒ± „ ~Œ1ÆcŒ1ÆcŒU üÒÆc…øøxxzzª; Æce$--MÝ)0ÆcücŒ}d2ÕcŒ1ÆcŒ•{\ðcŒ1ÆcŒ1ÆX±¤¦¦" ‡FDD •Jabb‚Ï?ÿ=zô@çÎ =þúõëX¿~½ðyÚ´i¨S§N±ÏñâElÙ²Eø<{ölÔªUë®å}HKKC•*UŠŒ‹ÅرcADÐ××ǶmÛŠ<æË/¿ÄÙ³gáää„   Bãrrr0qâDäææ ÛfÍšssóâ]ÄG*22NNN "¬Zµ TwJjÅ?ÆcåY,€ûêN‚±ȶÀgx¤®DSƒTu'ÀSíÔ©SðööFLLŒÂ¾˜˜„„„`õêÕhÞ¼9üüüЬY3…¸‡bíÚµÂç¡C‡–¨àwïÞ=ÑñcÆŒ)¿ÄÄDÌš5 999ذaC‘ñ³fÍÂþýûFFFEÆûûûãСC€¨Œ=xð V­Z%ÚfjjŠŸþ¹Èó|ÌlllàèèˆS§NaÊ”)èÞ½; Ô–ÚpÁ1ÆX¹ED“LVwŒ}(‰$€äcˆÈNù0Æcr¿þú+~úé'Q‘±ÁÁÁpqqÁîݻѵk×z­\¹³gÏFJJ ¼½½‹ŒÿóÏ?±yóæb÷/“É0qâDoŠƒ^^^*ã Π”Û°aæÌ™MMÍbŸ÷c4zôhœ:u qqq˜3gþøãu§¤6\ðcŒ1ÆcŒ1ÆX¡Nœ8¡Pì«[·.zôè¦M›"##!!!صkRSßLÖÍÈÈ€——®_¿Žúõë—Y.–––èÑ£‡ð¹<Ìàš:u*d2Y‘q999˜7oæÍ›W¢þ·nÝŠ§OŸ† ]]ÝBccbbðßÿ)löì:„ž={–èÜ›ž={¢FˆÇš5k0cÆ ˜˜˜¨;-µà‚cŒ1ÆcŒ1Æ”JMMÅСCEÅ>ooo¬Zµ zzz¢Ø3f`РA8wî€7ëÙM˜0G-³|:tè€:”YáàÁƒ˜3gnݺUâc—.]*|îÓ§ÊøM›6!?? ‘H •J‘ ðóó«ð?©TŠž={bíÚµÈÊÊ‚ŸŸfΜ©î´Ô‚ ~Œ1ÆcŒ1ÆXd2vìØóçÏãùóçxýú5 „gÊ^T±k×.DGGš7o777¬X±§NBÆ ñÅ_ÀÝ݇FXX ~ýúèÝ»7Ö®]‹#GŽ N:ððð€§§'444¼™­µaÃÜ»wÏŸ?¢I“&ðòò‚­íÿ/ {ñâEœ={Vøüõ×_ÃÚÚZ!× .…:6lLMM±~ýzÑš}ŽŽŽØ´i$‰B€••^¿~ 8vìBCCáààPèØ¾zõ [·nÅ¥K—ðòåK8::¢k×®øüóÏbCCCqäÈáóðáÕÎàº~ý:‚œœ4mÚNNNèÞ½»ÒÜ ÄåË—Žääd˜››£iÓ¦1b„è…¸qã†èåaaaX¸p!`âĉÐÑÑÁÙ³gE…6©T SSSÄÆÆªÌNŸ>{÷îªU«†6mÚ›ŸŸ7 Ÿ]\\`jj !ßÇÃÒÒRéñÿþû/ÂÃö¶¶BáìèÑ£¨S§<==ááásçÎáÒ¥K€ZµjaÈ!رcöîÝ‹êÕ«ÃÓÓ]»v…¶¶6 )) 7nDhh(ž?ŽÜÜ\¡Aƒøúë¯áèè(¿Ã‡ Ÿ»té¢ôÞ¹uë–¨\ð¾–ç kÖ¬Á?þ-­O°üEDܸqãÆ7nÜÊA€þ×"Ô7nܸ}È Zþد_?*Onß¾MfffTà;Z¡7ŽòòòDÇuèÐAØ?vìX:t¨è˜¶mÛ‘···°­gÏžôÓO?‰â¬¬¬„>ׯ_OR©´Ð;çÆ7nܸqãVüÆ?nܸ}Ê­¼ü^¿~MµjÕR(FI$…âÅ‚ DÇ,¢™˜˜(į^½šˆÄ?ccc…¸3fÑ›ÂÑÛ…,---¥Å²°°0!F)-ʽxñ‚455…˜É“' û k´µµŠšÊœ8qB”Ï_|!ì{»àWp•]˼yóD}Uðûæ›o”ŽGÁÏ”‘‘!:.++‹lmmUðPåÊ•)88˜ˆJVð«U«ÍŸ?Ÿ^¾|IDT삟ƒƒƒ·téR•ãÞ·o_!VGG‡RRR(''‡LMM…ífff ET¹‚E4e÷á¬Y³ˆH\ð«ZµªèÞ@>>>DDNÚÚÚEÞ¯€¸Ü¹sgQñP&“)ü¬ …e…Ж-[ û}}}UŽÛ‡ò¡ ~oæ3ÆcŒ1ÆcLÁ–-[ðìÙ3áóÒ¥K‘œœŒ¤¤$ìß¿_ôÖÓ   Bûyñâ455áåå…éÓ§ÃÎÎ}ûöUˆKLL|ùå—˜={65j„~ûí7a}6###œ9sˆŠŠÂ?ü ô‘ŸŸ“'O Ÿ‡*ü÷£GpýúuÑ9÷íÛ‡¼¼<áó!CéééÈÌ̶׮][x¬XÑç‚ã÷6"¸qã€ôôtüóÏ?Ð××öÿöÛoxñâE‘çÞ¼xÆ ÂçöíÛã¿ÿþC||<,X ¼àãÉ“'X½zµèØeË–áþýûÂçáÇãîÝ»xøð!æÎ+lÏÈÈ€¯¯/€7knÚ´ R©TØß¦MlÚ´ ›6mBåÊ•NNNˆŽŽÆôéÓahhX¬k€øøx„†† ŸU=˜˜ˆŸ»wï###hiiaàÀÂöçÏŸãàÁƒEž[~öèÑ?ýô5j¤ôíÀ)))ÈËËCûöíáëë‹-Z÷ëü!¬¨­­C‡!==111øí·ßDýüÝ)x¿¾zõ ÇŽÅ?~\x9 ðÿ÷kAöööJûþ¤¼Ïj"7nܸqãÆ·â7ð ?nܸ} åt†_bb"ѲeËhþüù û >òÚ¸qcѾ‚3ü€7ý*Sp†êÕ«—Ò¸ˆˆÚ·oÍ›78 Ú—œœ,zԷࣛñññ¢}S¦LÛ©S'aŸƒƒƒ°=**J”—«««Ê±’ËÍÍÍܳ°°ö½=ÃÏÍÍMáøåÿÇÞ}‡WQæÿÿBI#„ÐA IhBè_j" TÁ‹"JsqñƒR–]QXwÙum«‚¢K“U”Þ1È"DÐBoÒKh ½Ü¿?ØÌ/‡“ IN2>×5מ™¹gæ=“C„×Þ÷Ü|àÔfêÔ©Ö¾¬zø=ñÄÖöÊ•+›˜˜§ó¾ûî»Öþ *XÃNSSSŸŸŸµ¯U«V.5uïÞÝx{{›æÍ››çž{Î$$$Xû<==­c‡ ’£g”“~Û·owº×sçÎez¾÷ßß©múïÇž={œö=öØcž#}?I¦oß¾¶KßÃ/íy¥¦¦º´;sæŒY¹r¥yóÍ7Í¿ÿýo§}III& À:ǰaì}qqq¦lÙ²Ö¾Aƒ9›þÏKåÊ•­áçé½õÖ[V›råÊeúÜ RA÷ðû ¾µ€œ)W®œḚ̈7nÜÐÖ­[®C‡YÛoݺ•å¹&Nœ˜£kfÖ.$$D!!!N@œ—+WNݺu³Ö5j¤FY3oذA'OžTPPP–×Ïé÷õå—_Îp"”êÕ««zõêêÙ³§µíüùóÚ²e‹ÂÃÃomOÿ}õôôÔÀ­^˜+W®T||¼<==•””äÔCqРAN½lÓ¤Vׯ_WJJJ†íìŒÀ€lüøãZ»v­Ö®]«Ÿ~úÉZ›^V³¿úúúªjÕª9ºVúYvï§7jíÚµZ·n5 pvµ 6Ì üNŸ>­üQ-[¶Ô²eˬYf=<<œ†–+WÎé¿üòKŽê?yò¤ÓúÝçI/£ÐÉÛÛ[VÀxâĉl¯™˜˜¨3gÎXë«V­RåÊ•ÚÜý3;{ö¬êÕ«§£G:m¯V­šËù³ -óKúÀÏÃÃC>>>¶Û±c‡§OàÝÒ÷î“î„Ó?þøc–çž7ož¦L™’éyK”(a½ó0;™}·oÞ¼©>}ú8½OR’Ê–-«¶mÛjÇŽÖ»ïþ³óÿþßÿShh¨öïß/éÎ÷µOŸ>Nß׆ ªQ£F^ÛÓÓÓi=ýwã·¢p|‹(„Þxã §°oàÀZ³fnܸ¡ï¾ûNMš4±öeÝ=<ö^ÚõìÙÓ Ó|||ôç?ÿY»wïVTT”¾þúk/þÿ÷é¹»–âÅ‹kðàÁÖú×_­ëׯkóæÍÖ¶ŒzKuìØÑi=mŠÌ$&&êí·ßvÚÖ¡C‡LÛŸ;w.ÃíiÁ¤”q/À»UªTÉ)¼êÕ«—Õ«,³¥sçÎ’\'Ií4 º}ûv¶u䥊+ZŸããã3¼þ­[·´xñâ\Ÿ;**Êi’»åôûšUÛgŸ}Ö û<<<4aÂíØ±C×®]ÓŠ+`µÍèÏNúáÞ+W®ÔÍ›7­^ª’ôÌ3ÏdZÓÝÏ*§»ø‰U«VYŸ›6mª… ª[·nVÈ‘>ʪ‡_V=´rÒ.**ÊivÝ?üáúÛßþfõž‹Í6J œ={V'NTRR’¤;C Ó¿0ÍÈ‘#Ö/^¬o¾ù&Ãócôç?ÿÙi˜qåÊ•õøãgZÓÚµk]¶ýüóÏNïw Îôø4ªQ£†µ~óæMuíÚÕiiÒ¤‰‚‚‚Ô¥KuíÚÕ ïºzw4éÎìÌ~~~ RÏž=†§7\Ú¬´yáî!É—/_vi³xñb§ž€5kÖTË–-3\îîi9kÖ¬L¯ÓïkfmµqãFk}ذaúç?ÿ©V­ZYN²û³3xð`ëÜ111š0a‚ÕóîáçwKÿNFÿ\Ý]ø‰Ã‡[Ÿ1NÁDdd¤¶mÛf­g5iGN‡‰fÖ.}’k@òñÇ;­gTKýúõÕ¼ysk}öìÙÖç~ýúeøžºfÍš©OŸ>Öº1F}ûöÕ˜1c©äädÅÆÆjûöíêÖ­›Þyç§ã_{í5•*U*Ã{’¤E‹9õÚŠÕ¸qãœÚtéÒ%ÓãÓëׯŸõùûï¿w œ¤;Rݺuåãã£&MšXC†K—.­°°0«ÝÒ¥Kµ~ýzkýêÕ«š:uªŒ1:uê”víÚ¥ÀÀ@kú÷ÅEGGç¨Öœ¸;ę̀çaúἇC7nTDDD†ËO?ýäô~ÂÍ›7gú~ÄÜ kΨí™3gëT[z .tzV}_Ó&›I“þûÚ¹sgU©R%ÓšÒ?«úõëgsöDà@&êÖ­k}Þ½{·†ªåË—ëü£}ôQ%$$Xûó2ìɪIúàƒô·¿ýMK–,Ñ Aƒ4iÒ$§ý™Õ2|øð ·g6ù$ÍŸ?ßå½}~ø¡5j$___ùùù),,Ì)$“¤§žzJ£FÊô¼ÒIžzê)uíÚUcÆŒQÓ¦MõŸÿüÇÚß·o_µnÝ:Ës¤7nœÓ̼=zôÐ /¼ ©S§ª{÷îÚ´i“¤;Ãsk×®í4ÌóÃ?´‚«ÔÔTõèÑC;vT¯^½T­Z5]¹rÅj;jÔ(§áÓéß¹¸nÝ:uéÒEAAAöÈËÚµk; ë½pá‚Óþýû÷;½«¯mÛ¶N½ïæáá¡¡C‡Zëi“wä‡5j8½Ÿ}ö™&Nœ¨eË–iĈ.=G3û¾f4‹³”õ÷U’öìÙc}nÙ²eN˶c K!X$¥J2ÿ[Ž»»–‚\$JûØ¿SX¬\¹Ò¤ûÝì²4iÒÄiýÔ©SÖ±:u²¶‡††fz!C†Xíüýý3m7räÈLëðòò2uëÖµÖ6l˜á9~ýõWãééétìƒ>hRSS³|W®\1aaaY>‹ô˘1cLRR’Ëy¾øâ «§§§ùë_ÿšé9jÔ¨aNž<étüܹsÚDFF:íÿú믇‡G–µ5hÐÀDEE¹Ôöæ›of{l÷îÝMrr²Óq£GΰíO?ý”éóìß¿Ž~æ}ûöµÚ?Þiߘ1cœ®÷¯ý+Óó¤9~ü¸q8Ö1+V4‰‰‰Æcžyæk{@@@¦çxå•Wœ®›a»¬~¶Å‹7 4°Ö+Uªäò\1&))ÉTªTÉéØÒ¥K›˜˜˜,ïó°ÚõÕWÙ>—‚°cÇŽ»ŸÃ“¿Séá@&zöì©Ï>ûÌ©§•$=ðÀZºt©¶lÙâ4iÁÂ… ó­–iÓ¦iܸqN½Ë‡~øaíÞ½[S¦L±¶GFFZ3œ¦çïïï4DWº3Ô5«÷JRùòåµuëV}ùå— sª!žxâ íܹSÓ¦M˰ÍÝþò—¿hÁ‚N=Ó<<<Ô»woýüóÏ.=Öîžmõîá¤ýúõÓŽ;Ô±cG—}ÞÞÞz饗´eË—Ÿ§$Mœ8QÛ¶mSãÆ]Ž­P¡‚Þ{ï=-]ºÔé}’ôÊ+¯¨M›6NÛÊ”)“'“|<úè£ÖçððpësBB‚>ÿüsk½T©Rzê©§²=_HHˆÚ·oo­_¾|Yß~ûí}×™‘É“'kÊ”).CÅ7n¬ˆˆ§Ép¢¢¢œ&Is÷d3Ò^Ÿé{rÞíüùóÖd0ÞÞÞêÚµëýÜF‘å0wþŸ¸™ÃáH•”ö/®Ƙšî¬ ’Ãá8%)P’ú÷ï¯E‹¹· »$&&êøñãºpá‚êÖ­«xÀmµDGGëÈ‘#JHHP£Fäçç—«ãûõë§¥K—ZëÔC=”«sܼySÇŽÓÕ«WU¼xqUÑÁ÷ IDAT¨PAuëÖÍÕì®wûå—_táÂ5mÚ4Ó@'-ôLsáÂ…LßåvõêU;vLÑÑѪZµªBBBäãã“£ZbccuàÀݾ}[ÁÁÁzà\‚¾ôŒ1:}ú´Ž?®Í,œ·nÝRÕªUuûöm+VLW®\qšÝ¶(¸}û¶Ž=ªèèh5jÔ(×õ¿ôÒKš>}ºµ¾iÓ&uêÔ)ÓöóæÍÓˆ#$Ý ³?ûì³{+K/Ü‚ž¥·ð÷Ýc~€ø6BàØ`#~€ø6BàØHqw€$´ÑÑÑŠŒŒtg-§Nœ8q÷¦ùy=‡1&?Ï€r8©’ÿ[=aŒ©éÎz  9Ž$Ñ)ÀoÇtcÌKùur†ô6BàØÝ¥…A´¤r’Ô¤I=ûì³n.òÎéÓ§5mÚ´ô›Næçõü…Ámý/ð«]»¶ÆŽëær ïDDDÜø]ÌÏë1¤°?ÀFü!ðl„À°?ÀFü@¡óË/¿èöíÛî.£PIIIQ||¼µž˜˜¨¤¤$7VT8¥¤¤èÒ¥KNÏ*³vv}ž~ ÇÎ;§]»våë5®_¿®:uê¨oß¾ùz¢fþüùòòòÒùóç%IM›6Õ AƒÜ\UáaŒÑâÅ‹U¿~}U©RE+W®Ì²ýÌ™3åå奫W¯J’êׯ¯aÆD©ùŽÀäØÌ™3õÈ#äë5Ê”)£§žzJO<ñD¾^öaŒQãÆ5`ÀÅÅŹ»·+îîÈ­Ù³gkæÌ™úùçŸÝ] ò‡‡‡¾øâ w—QèxzzºüoÚçß:cŒ|||´~ýz/^\:uÊö˜»Ÿg©R¥ló< üENBB‚nÞ¼™e›åË—ëÖ­[¬%K–è×_U›6mÔµkWùøø8µÿøãÞ¡Ö¤Iµoß>ÃÚ—.]ªäädõïß_‘‘‘Z³fNž<©°°0=óÌ3r8.ÇÄÇÇkþüùÚ³g‚‚‚Ô¬Y3]½zUGUHHˆœ«çWЂ‚‚äíí­råÊI’|ðAeÚ>%%Ek֬Ѯ]»tîÜ9UªTIO>ù¤š7ožaûU«V)::ZO?ý´8 %K–èöíÛÖÏËËË+Óöû÷ï×Ò¥K­öݺus Ï’““µfÍýôÓO:wîœ*W®¬¾}ûªY³fNíÒ¾kÏ>û¬œöEEEéË/¿TûöíÕ¤Ik{±bÅ´}ûvIÒwß}—Í“¼#((HeË–•¯¯¯$)00PÁÁÁ9:¶Ð3ư°°°°°°°°‚ERª$ó¿å¸»ëaaaa)ÈEÒ©´ßýû÷7Ù™>}º©Y³f–mºwïnBCCÍ»ï¾k<==MXX˜)_¾¼‘dÆŽëÒþúõë¦FÆ××ׄ……™–-[IÆáp˜O?ýÔ©íØ±cM¥J•̈#LHHˆ‘dFmž~úik}Ò¤I.×xå•WŒÃá0¾¾¾¦U«VF’©Y³¦9vìX¦÷‘’’bJ—.m$™dûlr*66Ö<òÈ#F’ 1O>ù¤iÓ¦ñ÷÷7ÿýï]ÚO:ÕTªTÉøøø‡Ãa*Uªd-óæÍËðƒ 2µjÕ2[¶l1%J”0^^^¦D‰¦téÒæÖ­[.íkÔ¨aS’y饗2­¿C‡¦yóææõ×_7¥J•2?ü°)[¶¬‘d^}õU—öÑÑѦnݺ¦T©RæÉ'Ÿ4AAAF’)S¦ŒiÔ¨‘yýõ×sñôœM›6Íéyd´ôêÕëžÏŸæâÅ‹¦^½zÖú¸qã\¾›iΞ=kªU«füüüÌÃ?lš5kf$™bÅŠ™o¾ù&Ãcúôéc6lhÖ¯_o<<<Œ···)^¼¸ 0qqq.í{÷îm5jdÖ­[çÔ¾\¹r&>>Þ©íéÓ§MÕªUŸŸŸi×®iÚ´©‘d<<<̲eËœÚFDDIfúôé.×|ûí·$³ÿþLŸÓæÍ›$óÕW_eÚÆcN:eš4ib­¿ð fÑ¢EYs¯vìØaÒý=ÏH`òówj~žœ……………………%ç  ËoyɯÀO’iÑ¢…9{ö¬1æNÈÕ¤ISªT)“œœìrÌÒ¥KMLLŒµ~âÄ S®\9óÀ8µ;v¬‘džþy“’’bZµje‡y饗LJJŠiÒ¤‰yôÑGŽY¾|¹‘d&NœhnÞ¼iŒ1fçΦlÙ²æ±ÇËò^¦NjFaÍ÷jÞ¼yF’Y°`A®Ž›4i’ñõõÍQÛAƒ™òåË›*Uª˜Y³f™¸¸8cöíÛ—í±^^^Ù~’LÛ¶mÍÅ‹1ÆÄÄʇzÈøùù¹´ŸýôÓ,Û§¦¦š%K–80žžž¦aÆ.íëÖ­kZµjå²½aƦE‹™Þ³19üŒ1æöíÛÖ縸¸,Ÿçý(èÀ!½€BoöìÙúãÿh­'&&*!!AþþþÖ¶zõêYCúÒêûï¿·†zyy©C‡Ú½{·¢¢¢TµjU§öO>ù¤Ózpp°ºwï®Ï>ûL7nÜpº^ñâÅõþûï«X±bªS§ŽöîÝ«wÞyGÅŠSíÚµõý÷ß;kÊ”) Ðk¯½f lÞ¼¹¨O>ùD‡Vݺu3¼ÿ±cÇæôQåØåË—%Iaaay~îô®^½ª/¾øB´¶…††æÉ¹ëÔ©£Í›7«dÉ’’$oooµk×N³fÍrùyíÝ»Wµk×VãÆ%I‡C?þ¸þö·¿)..Îe¸jn4hÐ@ 4¸¿›É¡´á§ÒwÎeåîYŽëÖ­«Î;kõêÕJNNVñâ®±PTT”¾ùæõéÓǺF½zõ2½Æ¥K—ôí·ßªwïÞY¶w8.õÔ«WO:uÒ† ”’’â4~èСš8q¢Nœ8¡IÒ©3fdyß¹‘~X¹]Þß'ñ?@СCÍœ9ÓZ_¿~½Ö®]ëô¹ôáN???—ħ…jñññY^3..Îzï™$ýúë¯N×ððð···$©lÙ²*Y²¤À”-[Öi¦Ð””íÛ·O!!!š2eŠÓu¢¢¢$IÌ4ðË}ûöÕ›o¾©¦M›jøðáêÛ·¯Ú´i“á» ïGHHˆSØ——üýý­°/Mf?ßÐÐP­[·NW®\Q… $I?þø£Ê•+w_a_Q«³gϪjÕªJIIÑÍ›7]Þ'ÝyNia_N4hÐÀ ûr#&&FçÎSÕªU•””¤Û·o«L™2ÖþgžyF“&MÒÂ… õÚk¯I’.\(OOÏ|û>Ù  Ð«U«–jÕªe­_½zUÛ¶mÓ€r}®Œ&sHsäȽÿþûÚ°aƒNŸ>-ɹGÕ½:þ¼’’’”ššª={ö¸ìì±Ç2œà#?Õ¬YS;vìÐ믿®¹sçjÚ´iªQ£†ÆŒ£1cÆdùœr#¯ÄìdV÷3Ï<£™3gªW¯^5j”"""´qãF½ûî»÷}Í7ÞxC“&MʲM›6môÃ?Ü÷µrcß¾}zÿý÷õÝwßéìÙ³r8VH™Üþ¼rÓ>22Rï½÷žÂÃÃuîܹ,ë©Zµªºté¢Ï?ÿ\¯½öšŒ1úòË/õä“O:ƒÈºÓîM›6ªZµªþüç?+,,L5jÔЂ ôüóÏß×¹+V¬¨bÅŠ©{÷îš:ujU|ÿzè!-\¸P‰‰‰Z±b…Þzë-7N—.]Ò[o½åîòòÔC=¤9sæ¨oß¾:qâ„‚ƒƒ5gÎ :ô¾Ï=`À5mÚ4Ë6eË–½ïë䯮]»Ô¡CÕªUKÿøÇ?Ô²eKêƒ>Пþô§­EºÓ›²cÇŽªS§ŽÞ|óMµhÑB5jÔÐ{ï½§W^y%Ãc†ªhçÎJJJÒ©S§4{ö쮼h"ð@Ò¿þõ/ݺuK+V¬PÍš5­í±±±÷}nOOOióæÍJMM-ð^oÙ)Y²¤úõë§~ýúéá‡Ö矞eà—’’R€Õåððp9R3fÌÐïÿû<=wpp°‚ƒƒóôœ÷ë“O>QJJŠÖ¬Yãô®Ê˜˜·ÔóñÇË£µkתråÊ9ª§wïÞò÷÷×Â… •””¤ÀÀ@uêÔ© Ê-òüEÎc=¦   <=çµk×T¬X1§‰6oެɓ'K’’’’îëü&LШQ£ôÎ;ïhâĉ¹:öðáúvíZ¾O°‘˜˜¨k×®ÉÏÏ/Ó6>ø âââ´ÿþ<›|£ ¼ûî»*]º´jÖ¬©Ÿ~úI JHHŸŸŸš5k–gC˜ ‹k×®ÉÃÃÃéûœÖ‹Sºÿïs^Ô³lÙ2½óÎ;™ÖSªT) 8P‹-Rjjª^xáÛýœò  È¹û~yáÉ'ŸÔ矮Î;«oß¾úïÿ«íÛ·kĈúðÃuñâEÕ®]ûžÏ?räH­^½Z¯¼òŠvìØ¡=z(99YûöíStt´¾øâ‹ KMMUÛ¶muíÚ5=z4Ïî{×®]š4i’ÂÂÂÔ¸qc=zTk֬ѡC‡4oÞ¼LëÙ³§‡† ¢aÆéøñãÚµk—6lØà4ãiaóì³ÏjÈ!êÒ¥‹Ë¾ªU«ê믿V›6mÜPYþèÓ§V®\©öíÛë‰'žPDD„~þùg1BŸ|ò‰.^¼hMHSPõ¬Y³FíÚµÓã?îTÏŒ3tñâE•/_Þ帡C‡jÆŒr8Y¿>yò¤548mêiÓ¦iéÒ¥’¤O?ýÔV³ðf‡À`K 6Ì0ШZµªÚ·oïòÿ>}úhñâÅš5k–æÎ«6mÚhÕªU ÔÞ½{•ššjµ­Y³¦Úµkç´Þ¶m[k½víÚNëÒÉ V¬X¡3fè‹/¾ÐË/¿¬¤¤$U©REÝ»wÏô>Š+¦þýûëèÑ£ª^½z®ŸCf‡Œ1úøãuãÆ ¨aÆZ½zu–õT­ZUsæÌÑ‚ ôÊ+¯¨FêÙ³g†Ã|zè¡{¾Ü®];§¡Õwkܸ±\¶?ðÀjß¾½Ëì½mÚ´Q½zõÔ¦MuèÐAžžžòôôÔÉ“'5nÜ8M:ÕVß°aÃäáá¡Ù³gkÞ¼y ÓÆåãã£(11Ñå˜ÐÐÐ\½k044TåÊ•ËQÛçž{N%J”ÐìÙ³5þ|………iÓ¦MòôôÔÁƒ3¬G’õ®¿   ,{õ&''ëÒ¥KÖzûöí%ÉÚ–þÏïoÃãî Éáp¤JJ§r“ù¿rÀfÇ)I’Ô¿-Z´È½ÁvBCCÕ¡C}ôÑG.û:vì(cŒ¶lÙRð…!K‡Rhh¨/^¬~ýú¹»œ{¡Ö­[§ß4Гo¿è ×[BòXBB‚Ž9¢%J¸ì[±b…¶oß®¾}ûº¡2d%55U“'OÖƒ>¨Þ½{»»œ"…!½ÀÖJ•*¥Q£FéÃ?ÔáÇծ];%%%iëÖ­ ׸qãôâ‹/º»LüÏ'Ÿ|¢;wê‡~ЩS§´~ýz/N„•ôð¶÷ÁhÅŠªT©’6nܨ={ö¨yóæ:tèÞ}÷]w—‡t«Î;ëèÑ£zä‘GÜ]R‘C< lÏáp¨GêÑ£‡»KA6  ¸»Œ"~€ø6BàØ`#LÚ( Ò>¬\¹Rî¬òTBBÂÝ›jåçõü…WÚ‡ØØX9sƵ@~«Ÿ'gH/`#ôð)úß¿Q½½½U¡B¾v~€• K—.¥ßŸŸ×#ð—$JR¯^½´hÑ"7—y'""B­[·N¿é¿ùy=†ô6BàØ`#~€ø6BàØnôÃ?hâĉº}ûvŽÚúé§:xð Ëö””ýå/Q|||^— ˆ!ðÀvíÚ¥·ß~[111Ù¶:uªÆ¯ääd—}IIIÚºu«ºuë¦[·nåG©Š?@‘³wï^}úé§î.£@-[¶L'NÔŠ+Ô°aC—ýžžžZ±b…¢££5lØ07T ° ð9ÿùÏôü#×Ç%$$èÆÙ¶KJJÒ… r|Þääd9sF±±±Ù¶5ÆèÂ… 2ÆäøüW®\ÑóÏ?¯?üájÛ¶m¦íJ—.­ hùòåZ¸paŽÏ/I©©©¹jŸiÏ'...WÇÅÇÇ+:::ÏÛ'%%ÝS=@QAà°­_|Q¡¡¡JJJÒðáÃU¡B¨~ýú:{ö¬KûóçÏë±Ç“¯¯¯ªU«¦råÊéã?ÎðÜ š7ožZµj%///Ê××W>ú¨.^¼˜á1S§NU@@€uîÉ“'ç(ø{çw”œœ¬É“'gÛ¶Aƒ:t¨^~ùe¥¤¤dÛ^’:vì¨\…kىל9sÔ¢E yzz*00P>>>êÞ½»¢¢¢2<æ¹çžSóæÍ¯Áƒ«|ùò Pƒ tùòe—ö#FŒP‹-2låÊ—zþõ¯©yóæÖÏËÇÇG=zôp9÷W_}%mÚ´Éåšßÿ½üýýõÅ_ÜÇÓòÀ¶bccuóæMýßÿýŸþóŸÿhüøñš4i’š6mªêÕ«;µ½zõªš6mªhâĉZ·nZ·n­Ñ£GkÚ´i.çNNNÖ´iÓÔ½{w­]»Vk×®ÕàÁƒµiÓ&=Ú¥ý¬Y³4~üxµhÑB7nÔìÙ³µmÛ6½ýöÛYÞCrr²>ûì3õìÙSÞÞÞ9ºï'Ÿ|Rçϟ׆ ²m›’’¢_~ùE.\ÈñÄ!9‘˜˜¨>ø@?þ¸Ö¯_¯Õ«WkРAZ»v­ÆŽ›á1i?¯ßýîwúé§Ÿô§?ýI'NT‹-T±bÅLÛ?÷ÜsúùçŸÚW¨PÁ©m||¼¦OŸ®Þ½{kýúõZµj•¨5kÖhüøñNm{ôè¡””ýûßÿv¹æçŸ®¤¤$õêÕë>ž¿Š»»²³wï^…‡‡[ëÛ¶mÓ7œ‚¸òåËkðàÁ.Çž;wNûöíÓ®]»äïïŸé5¦L™¢Ë—/kïÞ½Ö;ò{ì1=üðÚ2eІ ¦2eÊXí}||étŽ®]»êðáÃZ¹r¥RRRäáá!éÎÒüãªW¯žV®\©’%KJ’ºwï®Î;gÚãM’vìØ¡¨¨(uêÔ)«Gä¤C‡’î¼÷¯[·nY¶õððо}û«Ê•+çøÙñóóÓ¾}ûœ¶uïÞ]û÷ï×òåËeŒ‘Ãáp9îØ±cª\¹²víÚ¥Ò¥Kg{£GªR¥JÚ¹sg–íýýý]êéÑ£‡"##µ|ùr§í>>>êׯŸ–,Y¢ØØX+hMLLÔ’%KÔ¯_¿Õ¸ =ü…Þþýû5sæLkÙ¶m›¢££¶}õÕWëãã£åË—göIÒâŋժU+Õ®][ñññÖ2hРݸqC»wïÎQ­={öTRR’Ó°ÞÈÈH={V¿ÿýï­°O’¼¼¼ôÔSOey¾S§NIR®Â8///•-[V'OžÌQ{??¿< û²Ò³gOÅÅŹ ¹Mãïï¯o¿ý6ÇZnÛß­Gº}û¶®]»æ´}èСº}û¶S¸fÍݸqCÇ¿§k…~€Bïé§ŸÖÓO?m­ôÑGúàƒtøðál-[¶l†ÃAÓ‹ŽŽÖ•+WtåÊyyyeØæÈ‘#VϹôΜ9£ððp>|X§OŸÖÞ½{%ÝŠ›æÄ‰’¤:uêd[ïÝΟ?/I ÈÕq+V´Žu§Ó§O[ÏçÌ™3Vpšþù¤—öžÅœªX±b®ÚŸ:uJááá:räˆÎœ9£ÿþ÷¿ÖÓ®];káÂ…8p $iáÂ… V»vír|=Àü¿y ’¤áÇëw¿û]†mjԨᴞššª^xAsæÌQõêÕÕªU+K’<èÔ6mòŒâÅsÿÏ𴞉111¹:.&&F¹¾^^IMMÕÈ‘#õé§Ÿ*00P-[¶Tpp°RRRrÔæµ””ýîw¿Ó‚ ¤-Z(88XIII:räˆK{‡Ã¡!C†èïÿ»®\¹¢R¥JiÕªUzõÕW3Š &~€ß¼Š+Êßß_111jÕªUŽŽ™9s¦fÍš¥·ß~[&LP±bwÞš5kÖ,}ùå—NmÓ&9wî\®kK í®^½š«ã®\¹âÖžh~ø¡æÎ«©S§ê¥—^²žÏôéÓ3~Ÿ¦M›¦ùóçëÃ?ÔèÑ£­ÐnÚ´iZ²dI†Ç<ûì³úë_ÿª¯¾úJ>>>JLLÔСC °jàÞð?@‘S¶lY«7]^騱£¾ýö[?~!00P·nÝÒÌ™3§~øAÝ»wמ={²¬gèÐ¡ŠˆˆÐwß}Çd(2ü¤ 6(99YÍš5SÕªUU±bEUªTI;wvi?aÂ=ôÐCš0a‚|}}Õºuk•(QÂz÷„o¼ñ†Zµj¥áÇ«|ùò*W®œÎž=«É“'g[ÛøñãuôèÑ {ÞíöíÛúè£4bĈlµÝ¶4 IDATg&–î¼ÛnñâÅÚ¸qã= 9ÎÌË/¿¬Úµkk̘1òõõUXX˜üüü¬áÎ=¡ÈĉU«V-=Z¾¾¾jÛ¶­´páÂ,ëéÛ·¯|}}åçç§Þ½{dÉÀ=scÜ]$9ŽTIio?aŒ©éÎz  9ŽS’%©ÿþZ´hQžœ÷È‘#úõ×_sü^>éÎd;vìД””¤*Uª¨K—.™Îô»uëV8p@mÚ´Qƒ ”˜˜¨ˆˆ=ôÐCªT©’SÛ¸¸8…‡‡ëøñãjÕª•š5k¦‹/êøñã S‰%2­k„ Z²d‰öíÛ§Ò¥KgÚnܸqZ±b…"##åãã“£{>|ø°®_¿®6mÚä¨}Ncôý÷ßëðáÃÖ󉋋ÓÎ;U¿~}U¨PÁ©ý¡C‡tëÖ-µhÑ"GçÏm{cŒ¶lÙ¢#GŽ(,,L¡¡¡ŠÕ®]»2¬GºˆÖ¬YS½zõÒ‡~˜£ëw‹ˆˆPëÖ­ÓohŒÉ›_t ð($üü–åWàg'ñññêÚµ«Œ1Z·n¼¼¼\Ú¼ÿþûúûßÿ® 6¨yóæn¨Ò~,X aÆéСCªS§Ž»ËAUÐCz(<==µnÝ:ùùùiãÆ.ûcbb4oÞ={V'OžÔÂ… õꫯêÙgŸ%ìC‘RÜÝ€œñôôÔ²eËäááá²ÏÇÇG{÷îÍprç7ÞÐÌ™3U¼xq 4ˆ¡¼(rü(B² ôûòÆG}¤1cÆÈËËKî.È5?€t<<­_ýUÞÞÞª]»¶»Ë¸g+W®Ô¤I“´oß¾ ÷תUKãÇ׈#2ü»ú©S§œBÁéÓ§kôèÑ9¾þ¡C‡Ô°aCk}îܹ>|x.î ÿDGGë³Ï>Ëô~öìÙ£×^{M›6mR\\œ$©T©R Õ믿®®]»fzî”” ©©©š;w®j×®­3fdØæ³Ï>SÓ¦MµråJ+쓤„„ýüóÏêÖ­›^|ñÅL¯1cÆ íÞ½[’4jÔ¨L¯Ñ¨Q#}þùç†}Ò_-Z´HMš4ѬY³rz‹¶ó /H’Ξ=«×_ÝÍÕ…˜1&׋¤Æ’LÚ2þ|¤wâÄ Óºuk“þ{’ÙÒ¨Q#süøñ ÏÓ¶m[«]³fÍr]G³fͬãÛ¶m{¿·•'bccÍ_þòãååe~üñÇlÛïܹÓ+V̺W_}5Ëöqqq&88ØH2!!!&55Õ¥M||¼;v¬q8Ùþ|Š/n&Ož|Ï÷[ÔݼyÓ”.]ÚH2íÛ·ww9æ‰'žHÿ=8lîá¿,,,,,·Hš–þ¿ß@z={öÌðïyUªT1!!!þ0$$ÄܸqÃé<Çwj3}úô\Õétüܹsóò6ïIÿþý­zêÕ«ç²ïÞ½ÆËËË©îÒ¥K›òåË»<³o¾ùÆåø˜˜S®\9#Éøûû›˜˜—6 ,ÈðçãååeBCC3¼V±bÅÌÒ¥Kóå™v©ÿ{çÕñ¾ý{a©‚€ ‚  €¨À®€‚kDCl±F‰Š5FcýÆc•ØÐØÅÞ{ bCEE, +ˆ" ‚Ô}Þ?üíy÷ì9»¬€¢8ŸëšË=3ÏÌ‘‘‘¨U«f̘ÁTELL :uê¤v¤2AAAˆ 6 ‰„—ž——‡Ö­[cÉ’% ¢BËËÏÏÇßÿ¹sçjìCYÂØØ˜›zöìYìØ±£”=b0 ƒÁМ5kÖàðáÃܵD"ÁøñãñòåK¼xñ>Djj*–-[ÆÛW...Nål´¢âèèˆ7npÁ××·DË/ …Íd\³f ¯ß¾xñb¼|ù‰‰‰Ø½{7Ï6((HãÆHIIüøã‚½ûîÞ½ËÍX“ãíí¨¨(dddàöíÛxõê.^¼ˆúõës62™ “&MÒ¨?_ÖH$èß¿? 77+V¬(eŒ/&ø1J”ÜÜ\ôë×ïÞ½ãâLLLˆK—.áÕ«W¸páfÍšÅÛk!%%~~~ÈÎÎ.Q~ûí7,^¼‹/ÆèÑ£K´ì¢póæM}Š'Ož`Ïž=hذ!/ß”)S§±/e îóüùóKуÁ`0bäåå!**ªØȯ^½BTT”ZåÍ›7ˆŒŒÄ¥K—ðöíÛbÕW^¿~+W® ++K¥Mrr2~ýõW^\HH,X .ÎÄÄ#FŒ@DDôôô¸øíÛ·#::Z#’’’péÒ%¤§§«´Ñ×ׇ››*T¨ Ò–ˆ_¤û,Ï{öìYÜ»wO£vU„††rŸ+W®ŒÑ£GÃÀÀR©?üð¯ÏÉ{vˆ‹/æ®»té"(ÿ?þ@ff&w€ÐÐP¸ººòöÔvwwGhh(œœœ¸¸ ,,¬Ð6dddàÚµkÈËËSksûömœ?žw°Êç&##—.]BZZšZ;E±XY”e0ÿGQ¦‚-é%"]*Yê*Ž“'OæM5®]»6=zôHÔöÁƒäààÀ³_¶lÏFÝ’ÞOqOŠS¦&y×­[Çk¯Ø’Þ°°0jÑ¢…Ê%¶ê–ônذ³[~zýúu^YFFFôßÿ‰–•““C;väÙOœ8±Ð6~ _ËsMDdggÇ݇ .|b¯J¶¤—X`áë øF—ô¦¥¥QŸ>}HOOk{¥J•è?þ ""[[[ÒÓÓ#===ÞòÓ»wïrñzzztÿþ}>|8·´ÕÂÂB°\òèÑ£Ô²eKÞòW‰DBvvvHÿ^¾|É«gÁ‚›Í›7óln޼ɥ]¾|™—öäÉÚ·o¹¸¸p~hkkS«V­(!!APöÚµky}9ooïBïé¨Q£xyÆŽË¥‰-é åõé% ¹¹¹Ñõë×eß¹s‡×ž 6lÞ¿O#GŽ$^™ööö…þî|þü9ùúúR¹råx~êééÑðáÃ)11‘³Ý°aééé ¾O¹o§OŸ&"¢M›6ÑìÙ³iĈ¢K˜úé'ÞR_žedd$oynVV/oJJ éêêr6”žž®¶ûöí#Z±b]¾|YP¦âý=tè-Z´ˆttt8ÿ.\ȳ¿zõ*uëÖ³‘+++úí·ßå˱´´äêmÛ¶ÚµkG–––¤¥¥EÕ«W§®]»ÒòåËE;DDÓ¦Mãê2dˆ¨Mûöí9›àà`^ÚàÁƒ¹´ÊËË£… RÛ¶m©\¹räàà@}ûö¥ÊÍÏϧ *ðþ©“äìÞ½›gïààÀKWüd2-Y²„š7oNdkkKݺu£Ë—/‹–?räH®=¿ýö›¨MBB <˜\]]IWW—ªW¯N¾¾¾¢eÂÃéOŸ>äââBºººT¾|yrvv¦©S§Ò«W¯8;™LF>>>T·n]^{›6mJ>>>4wî\ÎöÇäÙøúúj,øy{{svÊ/sùýP,kæÌ™jÛ—@•+W¦6mÚЄ èÈ‘#¼ô… r÷÷¯¿þ¢ŒŒ êÕ«™˜˜Ðwß}G¿ÿþ;åççóò?~œüüü¨jÕª¤­­MVVVäããC3gÎTÙ¡XµjW¨HçïïÏ¥+ߣ3fpisæÌ!¢<___233#êÞ½;…‡‡«¼cÇŽåî[Ÿ>}ÔÞ·²üX`¾®ð- ~?æ È)‡òö>[²d —7&&F`«œ?&&†³ÿõ×_UÖ#žžžôìÙ3žIII<y?DÅ[tãÆ .-<<\ÐwÓÖÖ­¿B… ‚¾Œr¿òرc…ÞWeQÏÕÕUeZãÆUú£««KÛ¶mã•]Ø~?&gggµ÷¹wïÞ”››+ðûرcdff¦6oÍš5)99™ˆ„b¨r8yòd¡÷*77—WgóæÍyésæÌáÒZ¶l)ȯüÝO:µÐ: C±¼þýû ögT²—/_.ú”ƒ““EEE êQlwÿþýéW®\ᕳuëV.MY¼š>žKW¼NNNôý÷ß‹ú#•JE®ÂíØ±coôP9øùùQjjª _~~>Mš4Ieø0º|÷î]®…u\äÈ;f†††´víZJIIáÙªü222x#„·oßæ¥ðY333•›¦ 0€÷@yF`“&M8ÛôôtêÑ£‡ÚûP³fMºté’ åN¶˜à§øµk׎—¦(^õìÙ“~ûí7Ñúuttx?9~ü8ïÞ©ÝË LðcXøº¾AÁ¯wïÞ¼÷x­ZµhÊ”)4f̪\¹²à=¯Nð“y_ÊÍͳ]¾|¹ ßéááA]»v%+++^Z«V­xý”’ü€3ŸÚ·oOÇü¦pqqáõQûÂÊýlUðfLJ¥R®MÊ‚ða†aß¾}iƌԮ];^š¹¹9½}û–+»0ÁO¹¯ïèèHãǧêÕ«óâ•'-¤§§“¥¥%ïuêÔ‰† B666¼¼Ý»w'"¢;wr3@û‚¶¶¶dkkKçÎ+ô^)‹†3fÌà¥+Æ+ÿî""úã?xù5™pPbϵT*%‰DB&&&”MDD§OŸæ €êׯOÝ»wçÍœ>à¢|ØHI ~òàîîNäîî΋¯X±¢èo²   ΦJ•*žw_2Lðc¡(¡h™¾AÁ/66–›^¬øòòòâF#Åu‚Ÿ˜HtâÄ "ú0…]ùÅ |±S±¶¶œœU’‚Ÿ\ÜÔÒÒ"'''0fhhÈÅ\µj/ý÷ß×èÞöíÛ——Oqº·˜ðiddD-Z´ Zµj Ò"""xe«ünÞ¼)ø.œœœ³{õê%ðyÉ’%‚—¨““¯Ã€ªU«Fïß¿ÿ(ÁoôèÑô÷ßÓëׯ‰ˆ4ü<ÈÙèéé fÖ½xñ‚WNÓ¦M5ú~Ô¡(ø‰=׊ë.]ºÒMMMÿ¯ èÞ½{¼zJRðSíkÔ¨!úƒàâÅ‹‚ò•ïŸò³VÖ`‚ ,°ÀÂ×ð ~‘‘‘¼¾qÓ¦My‚ijgÏÈÚÚZe¿DLð›|8'ÎL:•gײeKî˜ñ´´4úå—_xé?ÿü3¯ž’ü³³37(ö‡_qfâÌ™3yiË—/×èÞ*l)îã§,ø5lØ·÷ÆêÕ«yéÊû’¨ü¼¼¼¸´*Uªp¢kff&-X°€×)Pœ-÷òåKžøéêêÊí™òöí[ àù´zõj""zôèÍŸ?Ÿ—¶ÿ~zôèoù¯2š ~Š zõê Ò•_ºbB&Ñ¥K—ÔÅŽ¢¢à|_´h…††ÒÈ‘#¹ï*$$„gW§NniÀû÷ïiöìÙ<ÁÐÃÃ7:]’‚²´´äퟸzõjÞ§*1T±c3oÞ}úðlBCC6ëׯçÙ(‡öíÛóìÓŒ¹%ÌŠœ?žg÷Ë/¿l ¨^½zœ‘‘ïù)IÁ¯víÚ‚Õ3ÙÙÙ¼Y›5ÔÍ+§,ï³Í?Ф`Jnn.ï({///Œ5Š»677Çš5kàââ™L¦Q™sæÌÁ Aƒ:tðát±5kÖp6–––8tèw𭉉 V­Z…¸¸8ü÷߀uëÖaúôé¨V­Zñ)‚D"ÁæÍ›Q½zu€––æÍ›‡£Gr§umß¾ëÖ­ƒD"Ajj*/¿¹¹¹Fõ(ž|8™I+V¬€¥¥%wíïïÝ»wãäÉ“€°°0¤¥¥ÁÔÔTmÑÑÑ8{ö,w=yòd´k×`hhˆñãÇãÀ¸pád0ð­" IDAT2±~ýzÀÎ;y§ýóÏ?°µµcöìÙˆŒŒD5àââ€*UªÄóÃÊÊ vvvj}Õ”§OŸrŸíííéoÞ¼á]‹–Ó¬Y3µõ$%%¡råÊ¢icÆŒáN‚kÙ²%¿råJî³®®.:„5jøpZÛäÉ“ñüùs,_¾pñâEœ:u >>>j})*‹/FãÆ¹kœ8q{öì\¾|=âž}9Õ«Wçžó„„„OâƒÁ`0ŒÂ‰ç]+¾×åxzzj\^«V­ • ]¹r…wÝ»woMùòåѶm[lݺÀ‡~fAAï„Uu‘Æ~z{{ âlllààà€»wïâââ¸4333®˜ÄÄDT©R¥Ðz=zÄ»633µ»ïFFFøî»ïpþüy?ê¸ÿ>¯ ÅßEŠñïÞ½<~ü “ÉxuÈûÞŠ4jÔ5ÒÈÂ8vìüüüx'ÞΟ??þø#Ï.11‘w-Ö÷¶¶¶æ]?zôˆ×‡..õë×ýM¦És­¥¥…îÝ»ãÆ€wïÞáÑ£G\¾0>æ¹nÑ¢´´´xqzzzpwwÇÞ½{ˆ?GåË—ç]¿zõJã:Œo&øi@BBOÈëØ±£ÀÆÙÙŽŽŽ¸wï^¡åI¥Rôë×OëÖ-¼|ù’»t^¼xkkkñlŸ={¦Q=ŠBðAØ£R¥J¢/ênݺq‚<|ø 6T[§b‡B^vll,/®F¸páðÒóêèè yóæÿ/]º¤¶þOb§Bùå@ ÒÉ;I%ÉÏ?ÿ,ˆËÌÌÄÅ‹¹ëÎ;‹vFÍ ~À‡çúS~ÚÚÚðóóÄ÷ìÙ“ü€ eÁOñ¾²ƒÁ`0¥‡âÀ›¾¾¾hßGq¸0Tõ¥_¼xÁ}644T9詘ÿýû÷xôèvŠ"‘Åä°²²·±±á?Å{cmmÍJâãã üRRRðöí[îºråÊÐÑѵUuïÓ“'OÔÖ'çáÇÜçÄÄD >\­½¼Ü§OŸ"77—‹×tÒAQ8qâºuëÆ«oþüù˜0a‚ÀVñw Þ?W~î”…VàÀsÏž=¹ëììl8p@#5y®¨ñ”óGGG‹Ú~ÊçZΛ7o‘‘ÁN•EÔ´´4ëd0¾˜à§Ê#ˆU«Vµ«V­šF‚Ÿè¢²S«V-Ñüʳ·nݺ…ï¿ÿ^`'6Û0++«Pÿä¨zAÈg³Éyøð!¬­­3õ4ÍS¾¿ÊåærG#>>þ£?±Q-EEIżH$jó~.’’’¸Ïb Å& Þ¡„Â`jj*¯S£±ÙŠÊ,,¬Ð%Î999çÅ)—#G>}_±éß…ñþýûBmILLä^(ŠmÊÎÎþ¨r>%ŠÓæÅ¹9jÖ¬É]ÇÇÇãÁƒ»¤¤$^ÐtYT*…¾¾¾ ^±óVŠmP· FìÙÖ´Sñ±Ïµ"Š>}L» ƒÁ`”,ʃŒ‘‘‘åe‹êPõ^W0•Éd¸}û¶¨|»àà ¢\„RîOˆÍ@ú˜þ¹b=Š(°*NPž°f͵޽{‡™3gòâºvíªÒþÎ;…ú£éPE¿+W®ŒË—/« 3f̘ššòV±Aí””Œ5 ÁÁÁ8þ<¯/©ØTõÛåîÝ»èÖ­×÷—H$V)öÂI Ê3þÀÁÁ 4à®ããã±víZ•e~,š<×À‡•5b(>o‰uëÖ実m±çúc?Uϵ℘êÕ« ~÷)÷ë5—Œo&øi€r‡ByvˆŠ'b¨úë<½^¬@øûî»ï¸ÏŠ/¬âþáUžy'Gy? ù˹^½z¼?²ÑÑÑØ´i“Ú:–-[†çÏŸs×µk×¼€ä<{öLtº¸¦SÒQ^ªyåʼÿ^eÈÌÌä^jŠ¢Ù›7ox£|rÎ;‡K—.©‡?ŠŠäädQÅåøã?J¬þâ>× <ÁOÕs Ÿíììlg¯¦¥¥ ö3T?׊(~ŸŸr¹ƒÁ`0 õ(Îx€3fðú‰/_¾Ä_ý¥qyb}À‡­H”ëQ&,,Œ7‹ÎÃÃû\±bEÞÞÒÊ¢O^^ÞG “[·nôcŽ?Î[âääÄ}nÒ¤ oû™üü|´mÛ–·ÝŠœG¡M›6¼~¤ zõê¥ÒŸÃ‡ V);wŽ'*ú£777îs\\œ@L¼té|}}1mÚ4lÛ¶×w«S§÷ùøñソ–ÿùç,[¶ ÇG‹-xÛ)΂Ì÷îºwïΛ|0oÞ< :Tm{*W®ÌëêêŸO™2…wý믿ò¶¹Q$22}úôQ[¯"ªžëöíÛó²çÌ™#XÑ“€7r×nnn¼vŠKÖÅÚ&öŒ©âüùóÑ1>>¡¡¡ÜµØ*!å™…ª~G2ß*LðÓ[[[Þ‹zãÆ‚Ñ„Õ«Wk¼l@ÕÎÎμéÕëÖ­1_‰®®®Üµâr㜜ÁL6M—Ù:$ÇÄË7N>L¥— MÚÚÚ1bÏö÷ßÇ™3gDË?~ü¸`qìØ±*ýÉÊÊ¡C‡ñ»víâ]+‹yb(w<®\¹}}}^ذa:„øøxÞËRQð#"ìܹ“WVZZ:wî www˜˜˜ðFE•E±’œ!¨(ø‰ À Aƒxš;v`ñâÅ*7Õ=vì˜Ú¥µŠ¨z® xK¬:$º rÇFqÿHåeôÊ‚ßÇ<×x9ŠÏ5P¸à§jƒÁ`0Ÿž-ZðŒ ƒ§§'1mÚ4Ô¯__°OtQðôôäÍj:|ø0ºté‚°°0ÄÄÄ ((]ºtáÒË—/¹sçòÊpvvæ>=zÛ·oG^^…——|||0eÊüý÷ßèÖ­\]]y⣖–Ö®]«²|èËzyyáøñãHKKéS§x¡¶¶¶ÚYpŠŒ9’[©#“ÉлwoNô‹G@@8€Y³f¡ÿþ¼ÕUŠ¢Y~~>Ú´iƒƒ"::sçÎEPP—^±bEÞÌGÅÕW‰‰‰8vìöîÝˉ†‹/ìõ777Ñ ÷KOO÷Ý«êŸwïÞ·½Pvv6Fމzõêaذa ÄðáÃѬY34lØPðÛª(Û YZZÂ××—»¾{÷.<<ŒW¯^áÌ™3èС ¸t±çHq¶jÅŠ5žMÊ`|3åh_nP8zýúõTÖ™iÒ$A¦¦¦‚¸±cÇ |ëÖ­›FmÛ¼y3/ßĉEí¶lÙB999¼þŸ&áíÛ·\ÙŠ¿íúõë'úݽ}ûVcÿC½zõèþýû¼² »Or^¿~M¶¶¶Õ3~üxAþ-[¶ìtuu¹ßxööö¼þõÖ­[¹¼©©©õ\wîÜY´ ŠÏK‡T¶µ, |ÏL¢/àÝ׊–éü^¾|IÕªUSùGHWW—*UªÄ]Uð{óæ Õ¨QCP¾••iiiñâªV­Jééé¼üaaa‚¼uëÖ¥&Mšp| ¹´Â?$‘HÈÑÑ‘ÌÌÌxñåÊ•£/^Úpûöí~1ÚØØP\\œ ,EÁO±cdooO666¼2tttèÁƒ¼ü꿽{÷òòëëëSÛ¶mÉÃÃw¯õôô¾-X°@ІJ•* ^V5jÔ ´´4.ߥK—ùôôôT¾Äˆ4üN:ÅÙ”/_ž Dí ¨_¿~Ý¡@  ÜÜ\®,M¿¼¼>ž·zõj.ßÇ~òz”ƒµµ5;vL4o~~¾J1§{÷ît÷î]^œ:Á/00Z·n-ZÖ¸qã(//Oeîܹ#AÅ‚››;wN´ eÁ/""‚ºwï.ZÎ!C(;;›—¿0ÁO&“Ñ„ T @‰„FŒÁ–“••E£GL*Pìc.Z´Hô¾(þ>’‡¹sç î¿&AQðÛ¾};occ£ò»‘·=00P´oªjÖ¬Iëׯý¾íÔ ~D&+´oß^e=ÆÆÆôï¿ÿªÌÿûï¿‹ækܸ1%''“§Nð;v, 8P´¬~øAð›WŽg·`ÁµmýÚa‚ E ì”^ ±°°ÀÅ‹Ѷm[Á”n###ìÝ»³gÏFXX€¢oèoff†'N W¯^¸~ý:¯¼¿X³fͰqãFÁID^^^7n-ZÄÅɧÂK¥R#$$„óS 6D•*UpðàAÁþ„åË—ÇÆEP¯[·.._¾Œ^½záêÕ«…ÖÓ®];lÚ´©Ð%’C‡EVVÖ¬Y#XÂi``€ÿý—·—DatëÖ ³gÏÆ´iÓ “ÉS§N ÊÝ¿¿`zø˜1cðüùsˆ÷G477Ç‘#Gx'‡5nÜ666xöì—““S"KNš5k©TŠüü|¼}û7nÜàm,GKK ›6mB£F0qâD0©W¯¦OŸ®vÓfuH¥Rìٳđ#G¸xåý>±aÃÁrZ+++¬Zµ þþþ\\BB·ÔbÒ¤IHKKCppp¡¾˜››£W¯^Xºt©`i±T*Å‚ D޹qã÷¹råÊõ¬1 ƒÁø4Ô©S ¸}û6ÂÃÃQ±bE4oÞUªTìlmmÍ}677ÇôéÓ¹k±>“r=×®]CXXöìÙƒ‡";;ß}÷\\\УGÞ@ŠhkkcÛ¶mØ»w/Î;‡ØØX4jÔ^^^èС222x¾(ö H¥J•pâÄ ¬]»çÏŸÇÓ§OÑ AtêÔ ­ZµRÛgggü÷߈ŒŒÄ±cÇpýúu¼zõ ùùù¨T©êÔ©ƒvíÚ¡U«V*—‰V¨Pç«­­-víÚ…ƒâôéÓ¸sç\\\Ю];Áþ‡{_+Ÿ"+‘H0þ| 4Û¶mCdd$^½zsss888`È!¼%ÖÊe¡wïÞ8yò$nݺ…ÄÄDØÚÚ¢V­Zøå—_Dï­³³3"""°lÙ2ÄÄÄÀÂÂõêÕC‡ŸŸÏk¯&( Ø©S'###Ïž=Ãàèè(šO"‘`ܸq3f Ž;†3gÎàÉ“'xþü9 `eeøøøð¶tRFÑßfÍš©õÕÒÒÇŽõk×°cÇܽ{iii¨U«\]]áëë+ØZG‘yóæÁÓÓaaa¸~ý:œáííN:ÁÐÐS§NåöÖTÜŸ[}}}¬_¿;vÄ©S§‹ºuë¢U«VðóóÍ“››Ëí¨­­ýQ{2ß EQ ñ Îð““ššJ[¶l¡€€òóó£E‹qÓ¨›7oÎÝ“ï¿ÿž—oذaäååE^^^4bĈBëÉÍÍ¥ 6P›6m¨B… |˜å×¾}{ R9{‹èÃèЂ ÈÛÛ›ôõõ©jÕªÔ·o_ %"¢±cÇr¾,]º”—Wq†_Ë–-I&“Ñܹs©^½z$•J©jÕªÔ£GÁÔqU>|˜úõëGÕªUã¦tëèèPõêÕiРAôßÿ©Í?jÔ(Î×+VѪU«¨M›6d``@–––äëëKQQQ¢ù]\\xí#""‚~úé'ª\¹2I$ÒÖÖ&[[[:t¨è¬CE¨{÷îäàà@ZZZdddD...ô矪‰ºwïµnÝšŒIOOêׯ¯vôííÛ·Ü=ðòòR;ʦ8Ê5þ|µ¾}X‚L­Zµ"[[[î;233£úõëÓ„ (,,Leþ9sæp~©›¥¨È¨[·ndaaAÀ‡åµÞÞÞôÇPff¦Ú¼›6m¢:Pùòå©R¥JÔ½{wÚ¾};-^¼˜óEyÙâl5KKK®,wwwÒ××'sssêܹ3]¸pAeÝŠË= -- °~,°À _WÀ76ÃïÞ½{4qâDZ±b=z”bcc6gÏžå͈QÕ_ü’Qža¶qãÆÒv©Xœ>}š×žÓ§O—¶KŸœ¡C‡rí-l&é·‚òlµI“&}tÿý÷—ßÇÇçxùeÁfø±P”P´Lß àwìØ1Šˆˆ ääd•6Šûo >¼DëWÞÛNSÔMëCYð+NYb&è| 𸣏<ºG…Úçææ¹ûÉd²¹§Š(.S.Ê>E~ÖŠBQë*((]Ê¡ 1ÁOަß➊KmÊ*LðcXøºÂ·&ø={öŒ÷ãWKK‹nݺť'''ó–¯jkkSJJJ)z\4ÊŠàwîÜ9 æõ§ð¾³²Ê7¸öj:@^Ö) Áï×_åòïØ±ãxùeÁ?ŠØ)½ò矢qãÆ°°°@¹rå§|^½z•·ÌTqÉ@IPÔ%ªŽb/­² KÀ“¨ò';;=ÂæÍ›ñäÉ.Þ¢Ð2uttŠÜÎýŽ$I‰~?Ð¥Kn™õùóç5Z®«ˆ––V‘Ÿµ¢PÔº´´´Št"™š|/^¼ÀÍ›7...pss+‘º ƒÁ` kkkÞûX&“ÁÅŵjÕBõêÕaaaÐÐP.}äÈ‘¨P¡Bi¸Ê0{öl 6ŒëO–aŠmTÖpssãNr>zô(·% £èdeeaÆ >l=¤jÙ/ƒñ­Ã? ñððà>geeaÔ¨QèÖ­ÆŽ ///x{{sû°=J‘ÐÐPÔ¨Qýû÷G~~>¯‰à÷µ#•J¹#ëß½{‡­[·–²GeƒuëÖqŸÿ÷¿ÿ•¢' ƒÁ`0äìÝ»666¼¸{÷î wwwüõ×_ŸÑ3†2×®]ÄÍ;•*U*o>?ÐÑÑL&ÃÊ•+KÛ¯žÍ›7#55°hÑ"hi1YƒÁƒÚ¡!þù'Nž<É€‘‘‘ýû÷‹ÚΘ1Õ«Wÿœî1ëP˜››càÀŸß™R`ôèÑF||<–/_ŽÁƒ—¶K_5yyyX±b€Â?Ad0 ãË zõêxøð!6mÚ„={öàÅ‹HJJ‚––ììì`gg??¿¯úÝmaap×Ê›} dddÀÍÍ ÑÑÑÉdpwwG»ví0lذÒví³áè舱cÇâŸþÁ–-[ð×_ñ÷øÖÐÕÕå=×…š£ÌêÕ«¡§§‡N:ÁÛÛ»„½c0ÊLðÓccc\¸p+W®Ä²eËðüùs^z¹råРAÌš5 žžž¥äeñ±¶¶æNÏRwBØ—Œâ‰Â:::hРæÍ›§ö„©²„žž.\ˆîÝ»ãÆ‡»»{i»UªXXXÀÖÖ–ûü1ìÚµ ‰‰‰H$¼Ó¯ ƒÁ`”>zzzð÷÷‡¿¿i»òI°··ç–.~­ãÔ©S¥íF©3þ|ÌŸ?¿´Ýø"044,Ös}ýúõ’s†Á(ÃHˆèã3I$nnȯׯ_ÿÍÌž’óþý{$%%!==ÖÖÖ077/m— ¼xñÉÉÉpttD¹råJÛR!""2™ U«V-ñ=%¿%âã㑜œ }}ýojï>___8p@~yˆj•¦? ƒÁPD"Y`Œüº(}|ƒÁ`|™¤¥¥ÁÌÌL1j2Í--_l†_100`Ëv¿`ªT©‚*Uª”¶¥JãÆKÛ…2A5P£FÒvƒÁ`0 ƒÁ`0 a»[2 ƒÁ`0 ƒÁ`0e&ø1 ƒÁ`0 ƒÁ`0e&ø1 ƒÁ`0 ƒÁ`0e&ø1 ƒÁ`0 ƒÁ`0e&ø1 ƒÁ`0 ƒÁ`0e&ø1 ƒÁ`0 ƒÁ`0e&ø1 ƒÁ`0 ƒÁ`0eii;À`0 ƒÁ`0Š³â…••UiùÁ`0Œ†ˆ”£œÅì E$"Ná™$’Vþ“_×­[ÖÖÖ%éƒÁ`0J‘7n 99Y~™DDì—#ƒÁ`|ÁH$’ÜJÛƒÁ`|NQûÒv‚ñeSÔ~æŠwîÜÁ;wJÀƒÁ`|T(m ƒÁ`0 ƒ¡9lI/ƒÁ`0 ƒñõ“¥xáääTZ~0 £„‘ÉdxðàbTfiùÂøz`‚ƒÁ`0 ƒñõs€»ü"66¶]a0 FI’––333Ũ«¥å ã롨‚ßCÅ‹%K– gÏž%àƒÁ`0¾ „ãÇË/Ÿ”¦/ ƒÁ`0 ƒÁø8Š*ø(^˜˜˜ÀÒÒ²Üa0 Æ—€žžžâe*;ƒÁ`0 ƒÁ`|yh•¶ ƒÁ`0 ƒÁ`0 £ä`‚ƒÁ`0 ƒÁ`0 ƒQ†`‚ƒÁ`0 ƒÁ`0 ƒQ†`‚ƒÁ`0 ƒÁ`0 ƒQ†`‚ƒÁ`0 ƒÁ`0 ƒQ†`‚ƒÁ`0 ƒÁ`0 ƒQ†`‚£LòàÁøùùáÖ­[¥íÊŦM›°téR@jj*&Mš„ÈÈÈRöêË"-- ³fͰaߟ¯ÖvݺuX¹r%àÕ«W˜4i{æ ƒÁ`0ŒD&“•¶ ÆW üŸ€€Ì›7ï“ÖqùòeìÙ³gΜù¤õ|mìÙ³ëׯðAØš7oîܹSÊ^}dffbÖ¬Y¨^½:¦M›†U«V*øíܹ›6m¤¤¤`Þ¼y¸{÷îçp—Á`0 ã«bĈpttDvvvi»òÅ‘‘¦M›bç΀ÿýîîî¥ìÕ—All,üüü`ii T®\~~~xøð¡Ê<)))hÚ´)öïßX¾|9¼½½?“Ç Æ—Çgü8€³gÏ~®ê_0/^ÄÍ›7?i?ýô"""ðIëù‘H$j¯¿UNŸ>éÓ§£k×®èÞ½»ÆùØýd0 ƒñ5}}}œ>}ú“Ö“¬¬,|Òz¾&òóóqåÊ$%%ž={†Ë—/—²W_ï޽ý{÷àëë‹ ÀÍÍ @“&MðâÅ Ñ›à·jÕ*ìÝ»÷sU÷IÈÈÈ@jjªF¶™™™xòä‰F/´ŒŒ wýîÝ»B󤧧#--M#_€O? :==]ãö‡7oÞ //¯P;---4jÔ:::—ýöí[¤§§j—ŸŸÄÄD^\ff&²²²4®«´°´´„¹¹9pÿV®\Ymž¼¼<®"'33ïß¿/´¾”””BgÉÀË—/?ùýKJJRû|:;;#666l@µjÕ4*Sñ~ZXX(ü~2 ƒñ%žž___jYyyyˆŠŠ ŠÊyøð!¢££‹UÇ›7o…ÜÜ\À£G°}ûvœ={–7x®LAA®]»†;v`×®]¢K333…ÌÌL¤¥¥áСCÜÒÓ7oÞàÔ©SjË¿yó&¶nÝŠóçÏk4(š——‡k×®©õ»¨àâŋغu+vïÞ[·n‰ú”““ƒ¨¨(Ü»w‡¨¨(DEE!..N´ìôôtDEEq÷&>>;wîÄýû÷Eퟧ"¯^½ÂpäȤ¦¦âíÛ·¸~ý:8 6ß—@ùòåQ¾|yX[[¬­­QµjUµy’““±ÿ~;v iii\{<(°MMMåýŸyðàvîÜ©vYlff&.^¼ˆDDD¨\‚œœŒ¨¨(Á3,®Þ¼y#j_PP€ììl\¸p;wîü¨ßì}úôÊÕb•*U‚ªT©¨R¥J¡÷“Á(ÓÑGnHÖ¯_O…Ñ¡C=zt¡všrîÜ9@kÖ¬¤9::’§§g±Ê/(( äââB–––4gÎ £Áƒš2eŠ Ï‘#G¨yóæLááá´víZjÖ¬™è=²··'{{{òöö¦ÀÀ@@îîîÔ¶m[š;w. ]»vñòìß¿Ÿ´´´¨I“&´víZÚ¸q#999‘®®.ݽ{We["##IKK‹5jT¬{¢ÌìÙ³ 6Œ:D·oߦ={öˆ~'DD{÷+W’µµ55jÔˆV®\É…ŒŒ Ñ<ÆÆÆ4`ÀªQ£µk׎ièС´bÅ í³gϸòÆŒCèÔ©S¢åfddrss£*UªÐ‚ èÌ™3Ô¯_?@ÿý· ÏÈ‘#IGG‡–.]JÑÑÑ4nÜ8@ƒ¦}ûöQRRÒGÜ=>ݺu#[[[µA¬Í˵k×èøñãÜõ¼yó(//OÔvèС¤§§GË—/§èèhîžþòË/´oß>JNNä‘J¥äïïOU«V¥N:Ñ¢E‹Èßß_ô™2d 1cÆÐùóçéÀÔ¤I’J¥tòäIžmrr2 åË— Êqww§æÍ›óâ^¾|IÈÊÊŠš4iB!!!töìYòóó#´`Áµ÷iìØ±€Þ¿¯ÖîòåËtúôiîzöìÙ$“ÉÔæùZèÚµ+)ü¥"¼+X`Xø|ÀÅþyaÈß•‘‘‘…ÚjBAAÙØØPË–-iÙÙÙdjjJ?üðC±êX»v- K—.QÇŽI"‘ŽŽ ___Ñ<‹-"kkk@€´µµiΜ9<»óçÏZ·nU¬X‘§§'ݹs‡LMM uíÚUPþÓ§O¹þ¾ÜGGGºyó¦Ú¶ >œÐ¢E‹Š~CD8sæ U«V¡¡!1b„À6&&Fñ]Ï bß#Ñž={EEEQ=xyV¯^-°ïÛ·/ÏÆÚÚZ¥ïK—.åžÉ6mÚD"!©TJ¨OŸ>¢yBBBÈØØ˜ ÉÈȈ$ W—¹¹¹†wMÈóçÏ©gÏž…†´´´"×!§^½ztãÆ "":}ú4µmÛV¥í† ¨\¹r\Plo•*Uö!!!€bccéûï¿ç}›7oØŸ–¦M›’››ÛGçsqqQûGZccc’H$4mÚ´ªãôéÓ ~uëÖ¥„„.þýû÷¤««K:tä133ãuôòóóÉÜÜœúöíûQ¾} Èd2266¦ü‘‹ËÍÍ%3334hÊ|R©”$ Íš5Kmù7nÜ ‰DBcÇŽåÅgff’}÷ÝwTPPÀÅUðkÒ¤ OL.(( zõê‘¥¥%åää¨ôOSÁ¯,Ã?X`…¯+”¶àGD4eÊÒÒÒ¢§OŸòâå"ÑáÇ‹U¾\ð333£ßÿž={FIIIôÃ?:wîœ Ï²eËhóæÍôúõk*(( S§NQ½zõH*•ÒÇ9;¹àgccCçÏŸ§€€î·ÀåË—éçŸ&^ÿ!??Ÿœ©Zµj´nÝ:z÷î:tˆÈÎÎŽ²³³U¶eÍš5dmm-ä,ÙÙÙdllLuëÖ¥[·nQ~~>Éd2Š‹‹£çÏŸ ìsrrèîÝ»Ü÷³fͺ{÷.ݽ{—ž[·nÅĉQPP€;vàÇ„‘‘Q‰Õ£Œ‰‰ ^¿~­2=""W¯^Ž{÷ðüùs$$$º‘¿òáÊ×<L:S§N-#)) –––š4¡DX¸p!bbbпŒ;:tÀàÁƒÑ²eË­ÇÉÉ©DË+ ÁÞZZZhÕªvìØqãÆÁÞÞ‡½{÷0|øðb×)ßÄXmÚ´§§g±ëÒ©T ooolÛ¶ £GF5°wï^ÄÅÅaܸqjójò}=x𦦦Ü~%Џ¹¹q6ß}÷]Ñ †Úµkø°çLÆ K¼|ƒÁ`0¾D´´´xýDùžn+VD¥J•¸øâlpïèè„„„à÷ßðaÏ·#GŽ`ìØ±ÜÁÅ¥yóæ¼ëzõêøp›&´mÛDOú¬Q£€ÿ—òµâÛaaaJ¥033ÃñãǹxhkkãúõëùSR8::ÂÚÚýõÞ¿¾}ûr‡Š•4Ë—/GÅŠ?IÙÊ߯¼o¨üýÊÛ¦¸Wœü7š¾¾þ'ñ­4k¯üy,¬½ÁÁÁ011QkŠ.]ºpÒÉqrr‚‡‡BCC‹â¶åþwåÊ•áååUè¾ôQQQX±bæÍ›§ñ{ Æ·ÎW}dMµjÕЪU+„„„`âĉøï¿ÿðòåK 4¨TüIJJB×®]&MšÀÙÙõë×ÇÇ‹}z­|³Ô 6¨TÌÌÌŠUÇÇbee…ÈÈHœ9sDHH¶lÙ‚Ö­[ãÀeî®àà`´lÙ5kÖDõêÕ‡ü#GŽ,vÙ—.]Â;wÔÚØÛÛ6Áþý÷_x{{ÃÑÑ‘koïÞ½1lذb—™™ CCCÑ4ùs“™™YìzÄÐÓÓNf0 £¬`llŒàà`î:99›6mÂÔ©S9Á¬$4h† ‚[·nÁÅÅ{öìANNÎ'íŸëêêªMˆˆÀÖ­[ƒ¸¸8$%%ø°ÒIZZZj¯ƒ“èÓ§ M*•ª<”îS!•JqâÄ à·ß~Ãøñãáéé‰áÇ£G%&¸(T<*Iä}7e<<<`ee…3f bÅŠJ¥˜5klmmÑ¢E‹bÕùôéSôëׯP»½{÷¢B… ŪKSš7oŽÊ•+cúôé055Ì™35jÔ€‡‡‡Ú¼…}_¯^½Bzz:ªW¯.š^³fM\¹r2™LôÿBq©^½:Ξ=«²üœœøûû£qãÆ;vl‰×Ï`”U¾jÁøÐ©øé§ŸpóæMlÙ²Åþ_T~þùgܼyW¯^å\\¾|=*VÙr‘OWWM›6-VY%‰D"A«V­ÐªU+Ìš5 .ÄŸþ‰þù“'O.m÷J”÷ïߣ|ùòèÞ½;ÚµkWWW4nܸDÊ^³fM‰”S’ÈÛûã?¢uëÖ¨W¯^‰ÍˆsppÀÉ“'‘-‘”Ÿ'vÚmIˆtñññ ²CÃ`0 £èôèÑ£GFHHæÏŸ-[¶ÀÝÝý³¯Ø3vìXÁÝÝÝ»w‡½½=ÌÌÌàíí]ì²Ë•+‡š5kª=yösãì쌳gÏ"!!{÷îEPPúôéƒ`ûöí¥í^‰b``€3gΠY³fhÖ¬€³:TìÕ^úúúýæ*Ll.IŒŒŒ www4iÒP¿~}}:·µIY ??AAA¨T©<‡ÛÞÈÜÜsçÎ-‘²Jм¼<ÁÒÒGŽƒƒC‰ý¾611¹¹9bbbDÓ£¢¢ààà š¦nåŽb3k£££UÆO:§NBxxø'[¢Î`”U>›j´jÕ*L™2¥ÄË500@Ïž=ŒÌÌL 0 ÄëДªU«"44áááÈÊʾ}ûмysäææ"55µXe›˜˜`îܹ¸~ý:z÷îÍíÝ “ÉððáCÄÅÅ©ÌûúõkxzzâÏ?ÿ,–ÊÄÆÆ"66–û£™™‰?ÿü™™™‚½7ñòòÂ… ¸ÑЄ„DDD”¨o%M^^RRR°wï^øøø cÇŽhݺ5<==1pà@9r¤´],QäÏìÎ;Ѿ}{^{þùg;v¬Xå÷èÑ...˜8q"ÂÃØ>}:Î;‡)S¦ |ùòœ½lllpúôiDGGC&“!447Æ«W¯TÖsþüyüòË/xñâ ˆãÇcÈ!‚‘ß””î™–ÿÿºÿ>bccñäÉ“bµ—Á`0Œ/  :”·_I1pà@<{ö LJ¾¾>zöìYâuh‚|Õ€âê™L†ñãÇsŸ‹C¯^½ŸŸ¯ríÂPÞ3úS •JQ¿~}ª÷8”/÷T7`ÿ%²oß>¬\¹›7o†§§''ö•Õm[vî܉իWcëÖ­ðððàľân%§k×®8qâ„`Æêÿý‡[·nÁ××—/ßÃQq¯J™L†ÿýWm=3fÌà]_¾|‘‘‘èܹ³ÀößÿÅ’%KpèÐ!8::~T{ FXÒ |XÖ»zõjøøøÀÆÆ¦ÔüX´hz÷î hiiˆˆ´´4Ìš5 Åš‚ìïïÌÌLL™2»w’’••…þýûcãÆ¢ùbccqñâE¤¤¤”¨è÷÷ßcË–-¨X±"¬­­"ˆ#ÔîÓâç燕+W¢N:¨P¡RRRàáá .”˜o%®®.NŸ>-ZàÆ°´´ä¦ïÙ³7nDtt4êÔ©SÚ®–úúú8uê¼½½¹öêëëCKK ;wîĆ ‹š5k©|---ìÛ·~~~ððð€µµ5ÒÓÓñîÝ;Œ?^ô`ÿýï6lêÖ­ SSSÈd2lذ .TYÏÔ©SŠjÕªÁÈÈéééhÛ¶-fΜ)°]¶l™àÿ‡««+€"uXXX‘ÚÊ`0 Æ—ˆòž~%IË–-agg‡#GŽ ÿþ066þ$õFýúõ!•J¹÷{NN6lØ€·oßB___í ¡&4oÞC† ÁÊ•+ñîÝ;Œ9–––ˆ‰‰ÁíÛ·1`À•3’f̘¿þú 7nDß¾}‹å‡"³gÏF­Zµàî¬,\¼xÓ¦Mƒ££#'ü)S£F 8::bÕªUpuuáÊ•+hÙ²å}ÀYµjÕ ££ƒ¦M›ÂÈȹ¹¹ÈÍÍ…––<==1~üxtéÒ¥´Ý,1ììì •JÑ A^{µµµáéé‰ßÿ;v,rù3fÌÀŽ;àãフ+W¢nݺ¸rå †ŠªU«b„ <û øð›PWWÙÙÙXºt)?~¬¶ž}ûö!;;ƒÆ‹/0|øp öE?uêбcGDEE!**Š—îïïÏ–÷2…P&¿¦M›âìÙ³°³³+±2µ´´pæÌÑ¥‚³gÏæN6S¤}ûöHLLÄÞ½{!‘Hàé鉪U«âùóçhÙ²%oú²|Y¡âµâfª»ví, H$øõ×_Ñ£GDDDàþýû000€ƒƒZ·n­²-žžž8uêT‰ŠÃÏÏ ÈÎΆ••¼¼¼ ýš5k†ääd8p©©©¨S§¼¼¼Dm= ++«ö­Aƒ8sæ 'Ø(#ßóC~òš"‚‘²ÜÜÜÿÇÞ}‡Eu|}ÿ.HW)"( "*vÅBQP,¨ìرÝX0±÷Øk,?%šØÅŠ]c¬€ ìAª ‚´óþá»Ö-,KSs>Ï3îÝsïœ;,»—Ù¹3˜2e <<<°k×.±ù:^½z øûû7~YYY˜:u*úöí‹;vˆ­ýìÙ3Ô®]˜5k–Ø~/^D5ªÃÒÒ7oÞÄõë×]]]ØØØ Q£FRãÇŒkkk\¿~prr‚,,, ¤îS©R%\¸p·oßFHH5j'''‰U°Ï£d½‹{K>cŒ1ö_"0lØ0,X° ÜÓ>ϼsçN,\¸^^^PWW‡››ѲeKÄÆÆ»ŽmÛ¶¡~ýúX°`þøãÑöªU«¢}ûöez bff&¶oß.Öá"àîîŽ7J½þòõõżyóD£!ÍÌ̾úëZ;;;,\¸«V­B¯^½ ££---¤§§ãÈ‘#?~<ÜÝݿʩ”áàà€¹sçbÆ èÕ«´µµ¡¥¥…´´49r&L€«««Ìë☘˜àÎ;ðòò‚»»»h»p‘Ì/dlÖ¬¦OŸŽ•+W¢S§NÐÑÑÁرc±zõj™×ópîÜ9Œ=ööö "˜˜˜àÒ¥K·cŸ:u ¹¹¹8~ü8Ž?.qoooîðc¬0DTäÀ ‹ŸŸ1ö=;yò$ /^H<BèÈ‘#åYé8zô( ØØX‰çnÞ¼I(00°2SÌÛ·o ­Y³¦¼SùfyxxP÷ùHRⳂ .\¸”]°®àõ9ûW~~>………ÑÇKµŽgÏžÑ7èÉ“'”››[è>ïÞ½+•\ž={F·nÝ¢»wïRrr²ÂûåççÓ½{÷èùó生Ÿ_*¹•¤={ö=}úTâ¹õë×zõêU9dV:vîÜI†††ôüùs‰çV®\I(..®DêJLL¤àà`…^£ñññtïÞ=ÊÎΖ·bÅ @)))Dôùz=""â›x­} RSS ^›€Yô|öpùºË÷ñuc¥ÌÔÔ?ÿü³h¾ÄÌÌLìÞ½®®®èÒ¥ËwuË€©©)`öìÙ¢Um333±k×.tëÖ nnnpuu-ÏcŒ1Æ"иqchii•jµjÕBëÖ­aee¥ÐÈ£‚w÷”¤ZµjÁÎÎÍ›7‡Âû X[[˽{âkrøðaXYYIܱ“““ƒ3gÎÀÂÂBá;O¾‡Fýúõ%î¦ÊÎÎÆÙ³gQ·nÝ[´ÄÐЭZµRè5jll kkk¹#H¥122Býúõ¿‰×cߪr¿¥÷Ý»wðööV8^__¿H«¤¦¦¦éVìÚµKáxöß`mmßÿ3gÎÝŽžžŽ*Uª`üøñ˜1c†Øm¾ß:ìܹ³gÏÆîÝ»Eç[µjULš4 Ó§O/ò‡:cŒ1Æc%¥sçΘ8q"F OOO!<<ëÖ­ÃóçÏqéÒ¥òN±DuîÜÓ¦Mƒ<<<`hhˆÇcíÚµxýú5._¾\Þ)2ƾ2åÞ᧪ªZ¤o" ®Ü©•"Ÿçêb² : @TT’’’`jjŠš5k~·_#FŒÀàÁƒ¤¤$˜™™¡fÍš¨P¡Üß6 ¥§§‡3gΠAƒå cŒ1öÍY·n]‘z›2e æÍ›§püš5k°páB…ã§M›†9sæ(Ïþ&L˜€Š+bÓ¦M¨» IDATØ»w/ˆèÞ½;&Ož “òN±DM:•+WÆ–-[°gÏÑùzzzbÒ¤I%6º1öýQáQ_î$X¸'|ìççW¤QzŒ1ƾnžžž >|BDõË3Æcò ‚u& +s/(ß°aC4mÚô«9>ûo"¢ÿÔí¡ßÚùæåå!''šššåÊ7éÝ»w_.êéKDËË+ömøú‡ê0ÆcŒ1ÆÊLƒ Ju”|iŸý7}K_%á[;_UUU^U—±2Æ‹v0ÆcŒ1ÆcŒ1öá?ÆcŒ1ÆcŒ1ƾ#ÜáÇcŒ1ÆcŒ1ÆØw„;ücŒ1ÆcŒ1ÆûŽp‡cŒ1ÆcŒ1ÆcßîðcŒ1ÆcŒ1Æcì;Â~Œ1ÆcŒ1ÆcŒ}G*”we)++ *T@… ŠvNNòòòDÕÔÔ ªªZj¹½{÷FFFPQ)Û~X"§OŸ ÐÐÐPºž¬¬¬"µavv6òóóEK³ýÿ ¾Ööÿôé>|CCCÔªUKf\Qó/ˆˆðøñc4jÔ 8é–‹?"-- ÕªU+ïTcŒ1ÆcŒ}¾Ê~?FTTT‰“ˆ ¥¥…¹sç*¼»»;´´´DeÍš5%šdffbåÊ•¨Q£ªW¯Ž¸¸¸¯£0QQQbç)«+]Gff&´´´°dÉ…÷qvv«Ë–-J×ÿ-yðàž>}Z¢ÇLMM…––V¯^­ð>ŽŽŽbí¿k×®Í)???þø#*W® [[[XZZÂØØ.\ˆMLL„––Ö¯_¯T];vì@“&Mðûï¿3ëâ»wï^¼x¡PlZZæÏŸêÕ«£zõêÈÉÉ)ÝäcŒ±2‹   |üøQ¡øOŸ>!((H¬”ôgcTTvìØ `Ó¦M.Ñã%/ÏUZ‰ŒŒTºŽ×¯_#((YYY ÅgffJÔ_pp+š/^ ((ÙÙÙ ÅøðA¢ý‰¨Äózóæ >ŒeË–áĉHNN–÷üùóbýÞ¸q,Nªe.,, [·nÅ‚ ðÛo¿!,,¬¼SbL®¯r„ߨQ£`llŒcÇŽ•kK—.ÅÔ©S‘žžŽ~ýú•øñ£££Ñ®];¼}û¦¦¦%~|E™™™áÌ™3bÛˆúõë‹uª©©•i^«V­BJJ ’““1xðà2­»>Ç/ñã1»wïÆ?üooo$$$`Á‚èÖ­Î;‡víÚ•X]vvvpuuEË–-Kì˜Êòôô„»»{¡ØÁÁÁpqqAZZªV­Š´´´2Ê1Æ—’’‚íÛ·£wïÞ¨[·n‰÷ܹs1bžy-Z$:×ÌÌL?~pèÐ!±í«V­*³œjÕª%ªwÁ‚eVoy{ýú5|}}ñìÙ³rÍ£Aƒ¢ö÷õõ-ñãß¿={ö„¥¥%¢££ñþý{<|øêêêèØ±#RRRJ´¾5kÖ`ÇŽåÚÙ111ðõõÅ?ÿü#7ÎÞÞþþþˆ‹‹Ã»wïpýúu899aݺuØ¿eËXєٿŒŒŒB/(üýýñúõkÄÅÅáÇbßV 6 úúúû¤§§# ?FRR,,,0räH¹#æÞ½{‡€€ÃÔÔ½{÷Fƒ ”?¹òóóqèÐ!ܹsùùùppp@ß¾}¥Æ¶iÓmÚ´ðùEåååaëÖ­°¶¶†ƒƒC‰ä­¬´´4øûû#<<ÉÉɰ´´Ä¨Q£äÎ5–’’‚€€„††¢FèÓ§Bßê*"77DHHÑ«W¯9viÛ·o’’‚ððp±×ÿ˜1c ­­-±Ï»wïàï¤¦¦¢víÚ5j”Üo “’’€»wï¢fÍšèׯj×®]"ç““ƒ $$*T€““zôè!·sçN~üñG±íÕªUC¯^½àçç‡W¯^Iý511GÅÝ»waaaþýûKûïÙ³g¢‹c¡þýûËýæ.;;û÷ïGhh(ÔÕÕáää$ú&O–\¹rQQQ¨Zµ*š5k[[[XXXˆÅíÞ½©©©HKKÃÇÅ~¾ãƃºººX|=¤¶cŒ1VRRRмysܽ{Í›7/ït¦©©  tî<ÑÖÖFÿþýE[´hÕ«Wãøñã8vì&MšTâuÊòå¼ãÂë555Q”5@ ª[Ñ9ÑYÉ)íöß²e rss€:uêš4i‚Ý»wÃÙÙ{öìÁäÉ“K¬¾öíÛ£}ûö%v¼Òfhh(öwe›6m°téR´nÝ0`@9fǘ DTäÀ ‹ŸŸÆÕÕ•&Nœ(7¦W¯^dllLjjj¤¡¡AÆÆÆ¢-æÌªX±"U«VºtéB€*W®LOŸ>‹ÍÏÏ'Ô·o_ªW¯™››S§NH[[›*UªDW®\‘™Wrr2 _ýUnþÉÉÉÔ±cG@5kÖ¤ºuëòôô¤œœ¹û.Z´ˆÐ?ÿü#7ŽˆèÒ¥K€jÕªUh¬²ªU«FrcŽ;F:::dbbB]»v¥5jÒ××§W¯^‰Å~üø‘Ѐ¨víÚdaaA;v$---ÒÓÓ£7nȬ'66–І äæ“@mÛ¶µ¥¥% ~ýúQnn®â'/E·nÝÄ^ÒÊúõë‹UG§NÈØØ˜TUUISSSìØoß¾•ˆ?tèiii‘™™¹¸¸©©) CCCŠ‹‹‹MII!4dȪY³&YZZR‡HSS“ªT©BÁÁÁ2ózúô) íÛ·ËÍÿÍ›7dooO¨N:¢ßÇ!C†P~~¾X¬››éèèP^^žÔó@ÇmKHH 4lØ0ªQ£Õ©S‡:tè@dhhHwïÞ•8NPP¨ýôôô@¡¯3[[[@VVVdnnNÈÛÛ[""¢ììlš0a 277'WWW²±±!MMMZ¼x±D|›6mÈØØ˜TTTHKKKì盞ž.·m'L˜@(;;[n\Iòðð ïó‘¤Äg.\¸p)»`]Áëó¼}û–Hý JHH êÔ© [[[òðð š1c†Ô}ÒÓÓiëÖ­äîîNõë×§† Ò!Cèõë×±»ví"ôäÉúí·ßÈÅÅ…êÕ«G^^^#7ÿÕ«WJHH(ô\OŸ>MžžžT·n]êØ±c¡×4_ªU«5iÒDnÌùóçÉÝÝ{¸|Ýå«újÆßßÀç[ ™Ã¯yóæ8vì:vì@"ÂÁƒ1`À¬X±Û¶m“ØçÈ‘#غu+ÆŒˆˆˆ@Û¶mñã?",,¬X+xúúú⯿þÂÉ“'áêê "Âo¿ý† &ˆþ- ¶¶¶5jKäxÊjÕªNž<)úf†ˆ°gÏx{{cÕªURX8pàüüü0lØ0ÀÇÑ®];Œ?¡¡¡ÅÊç§Ÿ~Â;wpöìYtîÜùùùX·n~úé'8;;côèÑJ{ìØ±èÝ»·Ü˜âÎ'\¬¢Aƒ Íággg‡³gÏ¢mÛ¶>.ݱc|||°víZ¬X±BbŸ}ûöá?þ}ŠöíÛcÒ¤I¸~ýz±òŸ|ˆ‹/¢C‡ÈÍÍÅÊ•+ñóÏ?£sçÎ2dˆ(öíÛ·066–:Žpq˜7oÞH<÷çŸbÿþý¢Q³ÁÁÁèС&OžŒ«W¯ŠÅvíÚñññ€ãÇÃÃÃCnþãÇGxx8._¾ŒöíÛ#''Ë–-üyóÐ¥K‰oíV®\‰7â×_ÅO?ý$zïÈËËCnn®Äñ…í[³fM…æð+o±±± ‚ùå” cìëð7I®ªÄ¾kÂEÂIùÕÕÕE#Œ¾™.Ô·o_DGGÃÕÕŽŽŽ8uêþüóOܺu ?–:2ÏÇÇqqqèÙ³'êׯ;vàÒ¥K¸sçjÖ¬Y¬sX¹r%fΜ sss 8çϟǘ1cðèÑ#…{ÿþ=^¼x#FÈÛ»w/N:{{ûrBÄÃÃoÞ¼A×®]áèèˆ'Nà÷ßGpp0|8"""¤^«*êÅ‹ Ý¡«ô9_ÿÂ׺"¯WWW¼ÿ]ºt££#±sçNÜ»wÁÁÁR¯}ˆ¤¤$xzz"%%ÿûßÿpåÊ„††{~Ê_~ùK–,AݺuÑ¿?~ƒFTT”ØmÙo߾ŇDW|Ùööö8{ö¬Ô:úõ뇔”ôèÑÉÉÉ¢üïÞ½‹*UªˆÅlÿÿþróŸ5kV¬X+++ôïßÇŽÀ9sæHÄ;v C‡…¾¾>z÷î DFFââŋӋÉûù*:‚8&&°³³S(ž±2§L/!á×§OÒÕÕ• *ºººØ¶yóæIìGDdoo_èè2Yòòò¨jÕªÔ¦M±íÂ~£G–Øgþüù€®_¿.õ˜ŠŒð{ýú5©ªªRŸ>}$êµ°° KKK¹ye„_YPd„Ÿ4999¤««K;vÛ.á7a‰}fΜI(44Tê1áC€,¶=77—LLL¨aÆE>—òR¿~}6l˜Rûfff’ŽŽ¹¹¹‰mŽð›>}ºÄ>“&M"ôøñc©ÇTd„ßãÇ 5Jl{vv6R‹-Ķ›™™Iý‘ˆ(,,ŒÐܹsEÛ„#ü|}}%âüñGÑèYåŽð{øð! ±íYYYd``@vvvbÛ“’’HKK‹úöí+³NYÌÍÍiìØ±EÚ§}šÐîÝ»åÆ½yó†vìØAïÞ½+ô˜ÊPt„Ÿ´»B„×z—.]Û.áçââBŸ>}m?qâÒ¯…áNªªª4zôh±×ÄèÑ£IUU•ÂÂÂäž‹<999ôüùóBKqïò!"ºuë Ë—/+­ýøá$¯C…#üzôè!6ñðáÀæÌ™#³EFøÝ¿ŸTTThüøñ¢öÏÏϧ¡C‡’šššØµshh( eË–I=ÖèÑ£%~ÞÂ~={ökçýû÷Z¸p¡Ì܈ˆzöìIVVV2Ÿ !@@“'OmËÏϧ’ºººÄ(àÄÄD200 ¶mÛé÷Pø^xçÎ…÷òóó#tæÌ™"ï[T<‹2¥ÔFøM˜0AlDÔŠ+P­Z5ÑÈ.%6ožPjj*bccQ§N™“ŠJlëܹ3æÏŸèèhÑœzE†¼¼|h}}}DEE!77÷»žo$)) oÞ¼¥¥e‘ÛýúõˆŽŽFÆ •ªûþýû>ÿ~Ùþ†††ˆŒŒ‰FÁ©©©‰F |I8:NÚ7k²òß¼y3¢££•ž òÞ½{>¯þ'-ÿððp‰øÌÌL :T©ú¾µk×dŒ1öíÎÙ÷¥/G°‡‡‡ë]Úˆ$¬X±OŸ>•ºÏ¦M›Äæ¥svv†½½=>Œ 6(ËÖ­[‘©S§Š¬7n6n܈ƒʽöÌÏÏÇ‚ ФI“Bççª^½:F¥t®%EÚ<ÎX¿~=bbb¤.†·e˱kݺuƒµµ5>Œ_ýUé\¶lÙ‚¼¼hР>Œ… *˦M›@¬ýÆŽ‹={öÀßß_´øGRR@OOO걄sé¿}ûVbUæmÛ¶‰íß¿?æÎ‹C‡I…W”üؼ>>>Ø·o0}út±øÔÔTlÛ¶ ºººJ׫¨OŸ>aÙ²eprrB×®]K½>Æ”Qj½_®ì¹gÏÔ­[^^^%ZOPP6n܈;wî )) êêêPQQ)Ò€pÿ·oß*Ç‹/|^DAØù!T­Z5T«V ïÞ½ûî:üNž<‰Í›7#88ÉÉÉÐÐÐP´ÎÜ’lÿ””‰ö733ƒ™™Þ¿/1¬\Q¸qã†Ü˜%K–`öìÙJ_YÇŽÖ-[Š””hjj"??¿H§%Ñþ/_¾ð¹ÃìÇbÏÕ¬Y5kÖćDÕªU“¹Vrr2€oí-LI柔”„´´4±çjÕª…ZµjáãÇ¢…S"##–––J×ùµ“Ö¹ÊcìÛehhˆ¸¸8Ñ㤤$4iÒçÏŸët)î-„B©©©ˆˆˆÀÓ§OE×i?~”+í6ÓV­ZáöíÛÈÊÊRz‘ŠÈÈH¨ªªÂÇÇGjÂÏsY¶oߎàà`ܸq£T )M)))¢ö‰‰Pôöÿý÷ß‘——'õyEDFFBMM Ç—xNEE¥Ðö—'33·oß.4®M›62o¿-MIIIˆŒŒÄÓ§OEçY”öoÙ²¥hº+e ÛàÀ2Ÿª\¹2H\Ç · ã ú2@€–-[âäÉ“Jå]0?555±…t¾|¾ ÀÌ̬Äɲ|ùr¼zõ 'Nœ(Ö´`Œ•¦oz¸ÓŽ;0f̸»»cÏž=hܸ1LMMÑ«W/8pà6ltèП>}Rø8%ÑþÂU™W®\‰V­Z)*Ö‰&$ü£@ÞJÏ•dþkÖ¬µµu¡ñÂ?†”ù‰`[ÞI0ÆÊœü>öMQQQûlŽx«R¥ŠÂŸ¹ŠˆŒŒÄO?ý„Ó§O£bÅŠ°´´„©©i‘£¯¯"BFF†Ò~ 022‚§§§Äsžžž033“¹ï½{÷0}út,]ºô›šŸëÑ£Gøé§ŸpîÜ9TªT –––Jý|õõõ‘››‹¬¬,èèè(•KBBªW¯.³ý‹3BïŸþ‘:ZñKÅ™ÃO÷îÝÃŒ3páÂèêêÂÒÒR©Nt}}}dee!''GéÎæ„„˜ššÊláJ¼À¿×ÂÂ/Þ¿”œœ UUU…¿Ö××Gfff±:ŒP£F ™ùù·×Ë—/‹õ÷|Q\¾|K—.ÅÖ­[Ëüo@ÆŠâ«íðËËË+4fåÊ•hРD¯zFF†Ì}¾½ü;™~q~Y…ß$œ?þ?Óá·råJX[[ãèÑ£bÛË»ýK£Ã¯¬/ôyý¯ZµJt»KA2o].­övz?^¡?ggg=z§OŸFŸ>}Äž;vì´´´¤þËâõ£H‡Ÿð|Ïœ9#Z´¦(òóó‹¼O9Ë'¢wåc¬lñˆVTééépvv†¦¦&‚‚‚Ð¥KŸï€Þ¨¨ØØXèèè(}wðy$~tt´Ät…yýú5ºu놡C‡bæÌ™J×_ÖRSSáìì ===\¸p;vÄÇǹ#$66UªTQº³øÜþ·oß.rû+¢N:ÈÌÌ,4NÙÎbe$$$ K—.¨V­®\¹'''Ÿ¿Ì®U«V‘Ž%ì¨,ÎÈRKKK|(õùG¡zõêR‘&66fffJwöŸóùò¥Â¯ŸZµjáÚµkJ×§¨ððpôêÕ ³gÏ–:z•±¯‰b¿±%`Ö¬Ybó÷ÉcnnŽ‘ܸäädhjjŠ]Λ7.\9?ضmÛÄVóLMMņ РAØÚ*?€¥^½zèÖ­Ö¯_àà`¥£¨‹/ŠnC,D„””‰[”gΜ‰¿ÿþ[fûoܸ7oÞ=NJJÂæÍ›amm]¬Íš5kggg¬Zµ }ÂùóçK¤ýãââpéÒ¥bGá «…•’TØëÿâÅ‹HJJÂ’%KD}@ÑÛ?##—.]*‘öþü¹èËqy´´´àîîŽ .àÝ;ñïx###ñøñc…o­MOOÇ•+Wú]ž-Z &&F¡[·Ï·A'&&âĉEªGØ— oЊP||<ÜÜÜлwoÌ›7¯Hõ0Vʬï]»v Ï+Ö½{wÄÇÇ£o߾ؼy3FŒ!urüž={âÞ½{pvv†¯¯/¬­­±wï^ôêÕKlŽ’‚ìììЧOŒ1ëÖ­ƒ““^½z…%K–úMöñãÇ1yòdLžþù'–.]ŠbÇŽbñ•+WÆÚµkqÿþ}8::báÂ…7nºuësssÌ;Wj¶¶¶pvvƸqã°jÕ*´k×ïß¿/Ö6'£ IDAT„ÆÂü·oßMMM´jÕ 3gÎÄŸþ‰%K–`À€صk—Ä>¿ýöLMMѱcGüøãøý÷ß±víZ 6L4I²4=zôÀýû÷1bÄlذƒÆ”)S$â>,z ð¹£ÔËË ‹/.Öù2ÆcÂ9ýš4iRh¬¥¥%*W®Œ‹/ж‘Äõ˜p®´‚_Î]½z®®®2;LFŒˆˆÑãE‹áÍ›7×?E5nÜ8˜››cܸqxôè‘Øs ÊÉÉAïÞ½Q±bEìß¿_áÑLÀçü~úé'¼~ýºX9‡p.í‚íþüyÑ-‘²ÚÈ!¢yþ`Μ9HLL,vûOš4 &&&3fŒD§´öÿZ5lØêêêb—yyyÈÎ΋¶¿p188}ú4úöí @vû0@4¥ øúúâÝ»wÅnÿiÓ¦¡jÕª9r¤ØÏ~·‰²³³1zôhÑÏ&++ £G†ŠŠ ÆŽ+µžþýûãÕ«W¢Ç3gÎDZZšÌxEMŸ>>|¸Ä‚?ÒòŸ8q"ÌÍÍ1vìX±€ü×[“&M ªª*ñóýrEFFºu놆 bëÖ­ÊžceK™¥}X£À’Ð~~~T’ÒÓÓiþüùT¯^=Ò××'Ú²e‹Ô¸+V••Õ­[—FŽIñññtðàArrr¢ŒŒ Ql~~>999ÑŽ;èÚµkäææFzzzdkkKþþþróIKK#'''±’šš*5öýû÷4qâDjÚ´)ijj’¾¾>ÙØØÐîÝ»%b$Ž+,Ç—™Obb"9::Òܹsåæ]½zõ¢ŸþYnÌû÷ïiéÒ¥T§NªW¯=šé?þ öíÛ‹-/Ÿ••ENNN´{÷nºt鹸¸žžÙÛÛÓñãÇåÖ“””$Ñ>¶¥¦¦Ò¸qã¨I“&¤©©IdggGûöí+z#”“äädš={6YZZR•*UÈÉɉ~ÿýw‰¸ÔÔTZ¸p!ÕªU‹4h@>>>”’’B;wî¤N:‰Å _Ãû÷ï§³gÏR—.]HOOÚ´iCAAAró‰•hÿÜÜ\©±III4fÌjܸ1ihhµnÝš>,5þرcÔµkW200 +++:t(½yóFê¹:99Ñ¡C‡èÌ™3Ô¹sgÒÓÓ#:wîœÜü‰ˆ ݸqCn\bb"=š5j$Ê¿M›6 5þÇ4}útjÕªU®\™êÔ©C^^^täÈ™u$$$ÐÌ™3©fÍšTµjUêØ±£Ô×çÎ;e¾?L:µÐs..*ð>I_Á²ò\¸p)ÛRðZÀòò·K¡?¯uf%­_¿~€ÚµkG:u¢Ê•+ÓêÕ«Åb²³³©yóæ€êÕ«G–––T¡BÚ³gU¯^FŽ)¿k×.@³gÏ&555rvv&;;;@Ý»w§ììl™ù¬^½šPãÆ©Y³f4~üx©q7oÞ¤5j¦¦&¹¸¸ÐðáÃÉÎÎŽ*V¬(q]°nÝ:@ºººdll,Qâââdæ3dÈ@óçÏ/¬)•DèèÑ£2c233©Q£F€6lH¤®®N }}}úñÇÅâ·lÙBèçŸ&555êÒ¥ µjÕŠPïÞ½e^ï-Y²„P“&M¨Y³f4mÚ4©qýõ™˜˜¶¶6¹¹¹‘··7ÙÚÚ’ŽŽݽ{W¹Æ(]ºt!Ô©S'jß¾=U¬X‘vîÜ)“‘‘AVVV¢×¥¹¹9ihhP@@ikkK´Ñš5kýòË/¤¦¦F...¢ßŸR~~¾Ì|æÌ™C¨Y³fÔ¬Y3òõõ•wáÂ266&rww§aÆQ«V­H[[›ÂÃÃ%âçÏŸOÈÄÄ„ºvíJUªTýé×_%4gÎRSS#WWW²¶¶&4dȹùõìÙ“¬¬¬äÆœ;wŽŒŒŒ¨bÅŠÔ­[7±ü£¢¢$âoß¾MµjÕ"uuuêÔ©9’©R¥J,³GGGÔ¥Kj×®ikkÓþýûÅbfÏžMÈÀÀ@êûCÁ¿}KCjj*}ñ¹<‹¾‚Ï._wý›@` @ôÕ…ŸŸ¼½½‹|Æ+ »w·7BCC‹´‚ñ™§§'…ŸQýò̇1VöAÁ‹ÄDTòq±#Ö˜$|¬Ì5¾<ïß¿G@@Nœ8Š+¢M›6èׯ Äâ²²²päÈ;v 4@÷îÝakk‹­[·BOO^^^¢Øû÷ïãØ±c˜0a"##qäÈüóÏ?hÛ¶-Æ'sNb¸yó&Ξ=+zlee%s5Ò÷ïßcÇŽ C||< кuk 4HlŽÀ¿þúKîí§Ó¦MC¥J•¤>†Ý»wcÒ¤I¨Q£†Ìc(ëéÓ§øã?àåå…úõe$üø‡Ɖ'ШQ#ôèÑ-[¶Ä¦M›P­Z5±¹“CBBpòäIL™2<€¿¿?âââàää¹ó¯]»vMlÄgÆ ѯ_?©±©©©Ø±c=z„„„ÀÁÁƒ ‚žžž­QöRRRpäÈœ>}zzzh×®z÷î ]]]±¸ŒŒ ÏÏwâÄ ØÚÚrgcŒ1Æcå ??YYYÐÖÖF||<ÂÃÃѹsçòN‹±ïZ™-ÚÁce%//¶¶¶ÐÒÒBzz:Æ_ä»cŒ1Æc%ãÊ•+¨X±",,,`nnŽOŸ>{aƘ|<Â1öÝQUU…¯¯oy§ÁcŒ1ÆЬY3œ455¡££ƒ–-[âÈ‘#åcŒ1ÆcŒ±owø•£»wïÂÂÂsæÌÁ˜1c””„#F`É’%åš×áÇqûöm\»v çÏóT¬tlÞ¼^^^HKKÃ/¿ü‚Þ½{ãâÅ‹pttÄóçÏÅbsssqûömÄÅÅ)UWNN>}ú„¬¬¬’H½X¬¬¬0iÒ¤BãBBBаaC,Z´C† ÁË—/Ñ·o_ìØ±£ ²dŒ1ÆcŒ1ö-(3šL X¸'|ìççooo¹û,Z´Õ«WǨQ£Š\ßEVVLMM‘žžŽwïÞA[[»\òhÑ¢ÔÔÔOOOøùù•KìûõîÝ;˜˜˜ Aƒ¸víšèµ~óæM899aÈ!عs§(>11FFFX½z5¦NªTùùùPQ)ÿï8jÖ¬ wwwlÙ²¥Hû%''ÃÔÔÚÚÚHII)¥ìþåéé‰ÀÀ@áÃ'DT¿Ô+eŒ}UAÁ‹ÄD4«Ü’aŒ±/üÿ~ÍþÿáY"r)Ï|cìkSf‹vܼyuëÖU(öåË—xüø1’’’`aa¨ªªJ}üø1tuuaff†?âÂ… ÐÓÓƒ ´´´Š/^¼@xx8’’’P«V-888HtÄÄÄ ;; 6”Ø?%%¯^½B:uP±bE™ç­©©‰ÁƒcÆ ˆˆˆ@Ë–-eÆ>zôfffÐÓÓ“£ŒgÏžáÞ½{XµjîÝ»‡ãÇ#77*H©„……¡J•*011AFF.\¸€*Uª U«VÐÔÔ,v¼0§ˆˆ¤¤¤ˆÚ_ ˆÅDGG#77 4Ø?)) ÿüóêÖ­ %Z¥l=}úHMM…¥¥%Ú´i#q¾B>DÕªUQ½zu¤§§ãâÅ‹022BË–-¡¡¡Qhü… `ll,3øüÚŽŒŒDjj*j×®Ö­[KäóäÉ@½zõ$öOLLDll,êÕ«'úÛ³g233áëë+ֱݺuk¸¸¸àÀX³f tuu¥æ%!Û²eK¨©©IĤ¥¥áÙ³gbÛ¬¬¬ íHùò%BCC¡®®[[[ÉÏÌÌÄãÇ…ªU«¢Y³fR÷‰ŒŒDVV²³³‘œœŒû÷kÚ´i¡‘UªTAÏž=qàÀ¼~ý5jÔÏcŒ1Æcì?ŒˆŠ\X añóó£Â¸ººÒĉ寄„„Pûöí ijjŠŽoccCïß¿—º±±1ùøøÐ©S§HKKK´O³fͤÆÑرcéäÉ“bñÖÖÖ±ÁÁÁÔ®]; kggGiiib±3fÌ @@¯^½’8Θ1cHCCƒRRRäž?Ñĉ ½yóFfÌ;w5iÒ¤ÐãÕ¯¿þJèéÓ§tèÐ!@gÏž•¯««K“'O¦€€±Ÿ™½½½ÔøÊ•+Ó”)SÈßß_,¾uëÖ±7nÜ ‰öwpp ŒŒ ±Ø‰'’ªª*ÅÇÇKgèС¤££CéééElÍ;—ú÷ï/·=zTéã]»vZ·n-q¾NNN”™™)u š5kíß¿Ÿ444Dû´oß^j¼ºº:ùúúÒ¾}ûÄâ;tè û×_‘½½½D>:t ¬¬,±Ø1cƺººÔ×xß¾}IWW—>~ü(ÚæééIêêê?G"¢;w mKHH 4oÞÂ"ëýíK^^^¤¦¦&óõP’<<< æIJ|VpáÂåÛ._¼W-/ï|¸páÂ¥`p¿À{TPyçÃ… ._[)ÿûÛ ÐÔÔ„ƒƒ"""‘‘gÏžaÒ¤IÆÊ•+eî÷äÉ :³fÍBHH<(7>22C‡…¯¯¯Üxuuu899!"">|ÀÓ§O1aÂܾ}«W¯‹õööI,*ÇÃÓÓúúú…¶ÁÍ›7aaaêÕ«ËŒ111A:uàèèXèñŠêÈ‘#hÚ´),--áêê øûûËÝçÑ£G9r$æÌ™ƒÐÐPìß¿K—.•†Q£Faîܹ¢xi󪫫£sçÎˆŠŠBFF¢¢¢àããƒëׯcÆ b±ÞÞÞÈËËÃÁƒŶgffâèÑ£èÓ§ÜÑ•…QWW‡¦¦¦Ü"k¤¢ÔÔÔàêêŠèèhdddàÉ“'5j®^½ŠÍ›7ËÜïîÝ»;v,.\ˆÐÐPìÝ» .”?nÜ8,Z´H¿`Á‰¸ *ÀÝÝ111ÈÈÈ@dd$FŒË—/ã·ß~‹õöö½Ö JOOÇÉ“'1`À±´oß¾…±±±ÔÑv5kÖ¼yóFâ¹Å‹£víÚxýú5ÒÒÒ°aÃܾ}[êœx;vDDD"""ä¶ŸÐÏ?ÿŒ7bÞ¼yxñâ=z„Î;cĈ¸sçŽD|@@z÷î Q¥¥¥áúõë3fŒDü™3gêÕ«cÀ€¢Ü"""~mÞ¼y-Z´9–1ÆcŒ1ÆPz#ü.\H¢bddD–––bÛvïÞ-±ß—>~üHÚÚÚRG }á§¢¢B…‹èó?¥Gcedd¦¦&9;;KmÛ»w/jÔ¨ggçBýã?ÂÜÜsçÎU*·âò÷÷¡F¸uënݺ…zõê!11ýõ—Ìý† www…ë:t(ÜÜÜ”ÊQWW6662Û?88QQQ¢mûöíCíڵѶm[¥ê+ohÑ¢…ÔÑnB£FRèõ%4zôè"ÅTµjUX[[KÍÇÛÛÿý7^¾|)Ú¶oß>4jÔ¶¶¶b±999RçÝ )ùéÓ'‰çZµj%±­ÿþÈÍÍÅ£GŠt.ݺu Ÿ>}B£F$*ÁÁÁhܸ1BCCÅâïß¿ØØXüôÓOe²H^^|||вeKL˜0¡ÔëcŒ1ÆcŒ1öm+µE;Æ/öØÍÍ uëÖÅúõëåî—œœ ???ܹsOžoÞ¼AJJŠÔÅ! €)S¦`ïÞ½hÚ´)ÒÓÓqâÄ L:µÐN={öàâÅ‹8þ¼ÌÅCJ›ðÖ]i+)ûûû£C‡R÷+ÍöOHHÀ®]»"Öþ-Z´ˆ4hf̘!ºE5%%AAA˜;w®ÌE/5~üøB;”FAƒ«žøøxìÚµ ¡¡¡ˆŠŠBll,RSSagg'sŸÒlÿøøxìܹwïÞµjj*Ú´i#;dÈüòË/Ø·o|}}ñöí[\¼x+V¬ˆ566Frr²Ô:…Û«U«¦Pހם_+xV’¢££kÖ¬Áš5k$žÈÈÈ-ú"|-H[¤§4¬Y³ááá¸yó¦ÌŒcŒ1ÆcŒ1¡2[¥W·nÝBçΡ­­aÆ¡{÷î°´´Äüùó‹õǼ²nܸ.]º bÅŠ6lzôèKKKÌ;ññññzzzðôôľ}û°|ùr=zYYY>|¸ÜzþþûoüðÃزe‹ÌNµÒ‡7nÀÇÇ?üðƒØsãÇG@@6nÜX쎳¢¸rå ÜÜÜ §§oooxzzÂÒÒ3fÌ@ff¦D¼‘‘ÜÜÜD~GŽAnn.†Zì\5jTè[¤@[[[j‡ >={öÄ­[·°wï^8;;£FÅÎeìØ±Å>†"u#** ¢íZZZ2Ï·´ó©^½:ž 88X¡?aüíÛ·%nW.I!!!0`V­Z…^½z•Z=Œ1ÆcŒ1ƾ/e¶J¯››[¡E‘‘‘hÞ¼¹XgßÙ³gqöìYäçç—vŠRóiÑ¢…Xß™3gpþüy™ùtîÜ&&&X½z5.^¼ˆ#FÈ<~bb"ÜÜÜàîîŽÅ‹)·ôôt©Êò÷÷‡™™™Ô9Ò<==!päÈ‘«¯0ùùùˆŠŠ‚Xg_`` ®^½*³ý…#ä–-[†k×®Émÿ¯Inn.ž>} [[[±Î>\¿~½Ì_ÿÙÙÙxþü9ìììÄ:û:„[·nȨ́gÏžÐÕÕÅüùóqçΙí?jÔ(¨¨¨H¬ž‡cÇŽ¡k×®R;ð6mÚ$v+pNN¶mÛKKK4iÒD™S8::ÂÌÌ óçÏW¨sµeË–¨_¿>-Z„ÄÄÄ"Õ¥««+6Ϥ,/_¾D÷îÝáãã#sT%cŒ1ÆcŒ1&M™ðûrN?iìììpáÂ,Z´¶¶¶ ‚ŸŸ6lˆ˜˜˜2ÈR2ŸóçÏcÉ’%hÕªNŸ>Ý»w£aÆxñâ…Ô}TUU1dȬX±+VDŸ>}¤Æegg£GHJJB«V­°uëV±çmmm¥ÎSüÛéàà€óçÏëˆå7nœÔ[vÍÌÌ`cc¬]»¶Øõ)BEE6668yò$V¬XfÍšáäɓػw/6l(³“EMM ƒ Âúõ롯¯¯ôâ,e­B… hÙ²%±råJ4nÜ'NœÀþýûÑ Aƒ"w*—ºº:š7oŽ£GbÕªUhÔ¨Ž?ŽƒÊÍGSS^^^ضmŒŒŒd.æR³fMŒ9;vìÀ?ü€Aƒ!>>K–,AVV~ùå©ûååå¡sçΘ5k,--±xñb„‡‡cëÖ­ÅZHìcllL>>> ×addDcÇŽU(6::š\\\DùÔ®]›®]»FK—.%”™™)u¿ˆˆ@#FŒyìׯ_SÁ6ü²,^¼X澤¥¥EÎÎÎ Ga¶oßNèÒ¥K2c–-[FèöíÛbÛuuuiòäÉ ×U¹reš2eŠB±áááÔ¹sg€¬¬¬èÖ­[4wî\RUU¥ÜÜ\©ûÝ»wиqãÎëkðèÑ#êÔ©“è|ëׯO!!!äëëKêêꔟŸ/±††Íš5Ká:ÔÕÕÉ××W¡Ø‡RÇŽEù4hЀBCCiÆŒ¤©©)s¿›7oš6mšÜãçççÓÔ©SIGGGôº777§«W¯JÄ&$$Z½z5ýòË/¢}ÔÔÔhÁ‚…žK``  7nÈ»rå Õ­[WìwQCCƒV®\)5þþýûdcc#_­Z5Z²d‰Ì:ÂÂÂÈÎÎN¯­­M3fÌ‹ •ûþ°}ûöBϹ¸<<< ÖI_Á²ò\¸p)ÛòÅ{Ïòò· .\ ÷ ¼G•w>\¸páòµQÑo Öî ûùùÁÛÛ»ÈÇ‘%66&&&%vÌâøçŸ ¢¢¢p>W®\A‡pçÎØØØ”JNééé¨X±b™.¢Q^^½zuuu…Wm ‚««+ }‡ôRÙ´i"##ѽ{w9r®®®zG©T"44QQQHMMEëÖ­±lÙ2ò'ˆˆˆˆˆˆª?þë·‚üõ×_ú᥶oß>}‡ðR Õw2سg¾Ã """"""Ò‹ú€ˆˆˆˆˆˆˆˆˆ*~DDDDDDDDDÕ~DDDDDDDDDÕ~DDDDDDDDDÕH…,ÚSSÓŠ8UÉÉÉúˆˆˆˆˆˆžR…$üV®\‰•+WVÄ¡ˆˆˆˆˆˆˆˆˆèpJ/Q5„Q5ò´Sz¯èT‘UrCªÕûHÕS,D/Z¶¾ """"""Ý=UÂO‘ ¼‚c!ª´ …w±Mg„wô Q)8¥—ˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨1ÔwDDDDDDDÏ ¹B¡˜¯ï ¨ÒÙ.„ˆÖwDú„UeÍÌÓwTéÄ`Â^ZœÒKDDDDDDDDT0áGDDDDDDDDTpJ/U5Ýè;ªTÚ8ªï ˆ* &üˆˆˆˆˆˆ¨JB<Öw T¹( >Dj8¥—ˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aˆˆˆˆˆˆˆˆ¨aÂH7ú€ˆˆˆˆˆˆˆHLøU#LøU#Lø•_.€L}ADDDDDDD¤ ~Då—#„`ˆˆˆˆˆˆˆ*%&üˆˆˆˆˆˆˆˆˆª&üˆˆˆˆˆˆˆˆˆª&üˆˆˆˆˆˆˆˆˆª&üˆˆˆˆˆˆˆˆˆª&üˆˆˆˆˆˆˆˆˆª&üˆˆˆˆˆˆˆˆˆª&üˆˆˆˆˆˆˆˆˆª&üˆˆˆˆˆˆˆˆˆª&üˆˆˆˆˆˆˆˆˆª&üˆˆˆˆˆˆˆˆˆª&üˆˆˆˆˆˆˆˆˆª&üˆˆˆˆˆˆˆˆˆªC}@DDDDDDÕ‹B¡00RßqÐKűXÝG¡P˜ê%zYý-„x¨ï Š0áGDDDDDDÍÀ:}A/µþWˆ^”v*MÂSz‰ˆˆˆˆˆˆˆˆª&üˆˆˆˆˆˆˆˆˆªNé%"""""¢çj̘1˜4i’¾Ã "ª0ÁÁÁ˜9s¦¾Ã(~DDDDDDô\Õ«Wúƒˆ¨ÂÄÆÆê;„RqJ/Q5„Q5„Q5„Q5„Q5„Q5„Q5„Q5„Q5b¨ïˆˆˆˆˆˆˆª›ììl¯]»¶ÜIÞ—]µX¥÷Ã?,s©p"¢ªdãÆ¸}û¶¾Ã """ª¶rss5FÃåççKûŠ>|~~~%NýMNNÆèÑ£qûömÌš5«Äó?'NœÐ8ß®]»Ùj§e9xð † ‚´´4cž:u ]»vEPP¥}·nÝÂ¥K—¤z«V­ÊL:Õ©S#FŒ° “ME-¢R©ðꫯ"""B¶ýÉ“'øá‡päÈ;vL–@ÌÏÏ—î·¶„àòåË(}7E²²²°oß>¸»»cóæÍðóóÓˆeäÈ‘%¾‹/..#GŽDBB>ûì3…ÉÊâÏDAA ¤[¶l‰–-[j=&ìÚµKVo×®ôY!›âûÚk¯•xœòºví|||’’¢uZZ,X€«W¯j$:Õ}ÿý÷ˆˆˆ%U*Nž<‰:àÏ?ÿDŸ>}tŽ+&&¯¿þºÆÂ*• ±±±xûí·1aÂüôÓOP(²6Ý»w‡B¡€÷ïßGTT””x¥²U‹„ß§Ÿ~ ;;;}‡ADTa>Ì„Ñsdoo¬¬,ܹsGÚfkk‹š5k¢aÆ 4R²¯hD^Ó¦MqêÔ)>|XÚ·xñbL™2¥ÄNœ8 6 VVVصk¢££wîÜÁ”)S°oß>bÏÌÌÄèÑ£eÉ>´iÓ›6m£GŸŸ)S¦ wïÞR"ñúõë²ã”5­ˆú´SRÜÅ)•JDDD yóæxçwŸŸß~ûMú{í… ðõ×_cÑ¢E:766V–ì300À€`bb‚mÛ¶!??ééé=z4dV¯^-KöÙÛÛÃ××))) ‘{sçÎÅk¯½†®]»¢aÆHMM•%Ílll`aa{{û2ãÍÉÉÁÏ?ÿ,ÕÍÌÌÐ¥K©~îÜ9ܽ{Wª»»»ëtt1sæL)n¼ùæ›pwwÇÅ‹*=+›7oÆÜ¹sKLZ†‡‡ÃÄÄ#FŒ@£F‚cÇŽ(L²Nœ8±±±SKòþûïË’}mÚ´AïÞ½eÏÅŠ+гgO¼óÎ;²¾æææhÒ¤‰ôÜ:tˆ ¿òBT¹`&QTRRRQuâåå%Ô~çÂE%øíeaay¹Šúßµ|¥ïxXXXªV`ªþ;2cÆ Q=zTý·NlÞ¼Y¶?))Iôë×O899 …B!6nÜ(Û(ëõêUißš5kdûêÕ«'nÞ¼)íÏÊÊݺu“µ9zô¨´Û¶m²}Ç—öÍš5KÚ^£F qäÈißãÇE—.]¤ýƒ ’ömß¾]vÌÉ“'ëtŸöìÙ#ë÷Þ{ïIûdû<==ÅãÇ¥ý·oßÒ~333qïÞ=iÿ˜1c¤}VVV²óöéÓGÚW·n]‘˜˜(íKNNvvvÒþÅ‹KûÒÒÒ„´ÏÃÃC¤¥¥Iû###e1÷ïß_ÚwúôiÙ¾Õ«Wëtrrrdñ ,µY±b…´ÏÚÚZ§ã¿÷AAAmòòòÄ!C„³³³000ÐøóVüYü믿¤}ááá²}"<<\Ú¯R©dß±bÅ iÿåË—eûV­Z%íÛ¼y³lßÊ•+e1=ZÚçêê*”J¥Æµ½ñÆR›~ýúétÏ^”­[·Ê®€‡¨¿¿E…ïð#""""""ÒÂÁÁ»wïFBB2220bÄi_AAš4i"kŸžž^â±>þøc4jÔHª›™™añâŲ6Ç×).õ•[}}}ñꫯJu L›6Mªoß¾]Š«øâ fff:ÏÔÔTV×6õ¶ÈçŸ ©Þ AY<ÙÙÙ8{öl™çÌÎÎFpp°Tÿý÷Ѹqc©Þ°aCŒ7Nª¯Y³FúŽÔÔT©>gÎÔªUKªwìØß|ó –-[†°°0Ùª»O#??Äþýû¥mÍ›7ÇôéÓeíÔgðè2bPWFFFøí·ß‹ÌÌL,\¸PÚ'„€£££¬}iÏé»ï¾ ///©®P(°xñbÙ º®¦¬þœ:::büøñ²˜çÌ™#Õ/]º¤uè¢Ñ¶Jœ®LÚU‹)½DDDDDDDÏ“……Î;‡àà`iõÓâ ´âï™Sçé驱ÍÛÛ[zG\½zµÌ8T*•ljnvv6V®\)k£žBàæÍ›¨U«–ÆâÅß«V’âíêÔ©Sb[m×Y|!Œ«W¯Â××·Ôs^»vMº/@á%ů3))IöY¥R¡F«'k›:;uêÔRϯ«üü| <ÿüó´ÍÑÑÐH”ª/–––rþâLMMqõêU#44GŽ‘%?‹b.‰¶ïÏÎÎMš4‘íÐå9 ûLLL4¾?!jÔ¨!-Æ‘˜˜¨ñ¬¨ß§û÷ïët^*Ä„Q rss1cÆ lÛ¶­ÌF5j”<‰NÛ ´†††¨S§Ž”)kÅÙ¢6Å9|øp©}’’’àææ¦1ªìÚµkežÐ|÷ŸúHEuÆÆÆZWü-~íº\gñ¤ÝƱqãÆÛçççãÎ;hذ¡FBÊÖÖ¶Ìó=‚‚ :Tc$ÛáÇ5F½¿ïy$ü‚‚‚°fÍ{W\yŸÓ¢íE ?]¾?@þÆÇÇc„ ¥¶WOàQ_ॴ‘‰¤‰ ?"""""""-²³³ñúë¯#,,LÚfooþýûãµ×^Cvv6F%í+-‘òàÁƒÏQD—Ä”úÔJ pÚqYÓC Žt³´´ÄãÇQQQˆ‰‰ÑX”£xßí۷˶uíÚUkÛ¼¼m¶È”)S0vìX,]º”¶»ººÊÚ­X±BVÏÎÎF`` ,X€ÁƒÃÏÏOz_`YÏüñÇøî»ï¤zݺuVæHË HŸ+2á§þœÀ‡~(ûŽŠ?§yyy%ë?þÐxWÞÎ;qçΩ®Ës íÚµ“>Ÿ={Vc*ð¾}û0pà@Ì›7Û¶mÓx× E088'Nœ¦iª/º T*¥QTJ¥ÿý7¾ýö[YõäDqQQQxã7pêÔ)<|ø+V¬­¦Ú¸qc ýôSL:U–üyå•W4’~6l€‹‹ F…   Lš4 ;vĸqãdÉÂW^y¥Æ¶yófŒ?±±±HIIÁ´iÓdɺ>}úh$ä´111Á‡~(Õƒƒƒñé§Ÿ"''ùùùøõ×_ñßÿþk×®Å'Ÿ|"Kêµk×}úô‘êüñpöìYÿüs©]ûöí¥Ï?.uµãò(¾8ÈÚµk¥©°‘‘‘IÛÒžÓÔÔTøøø ,, =®]»0vìXÙ¹Ô¿—Ò|üñÇÒ}ÍÉÉÁ!C¤÷FÆÄÄ`âĉøóÏ?ñùçŸã½÷ÞƒR©Ô8F\\œô¹´‘¥¤…¢Ê3ˆ¢’’’"ˆˆª///¡ö;.*Áo/ ËËUÔÿ®à+}ÇÃÂÂRµ Sõß‘3fˆÊèñãÇÂÔÔTûÍMš4B¢±¯iÓ¦¢^½zۈ͛7KÇ^³f´ÝÔÔTÔ¯__kŸ¢²eËYlÛ¶m“í?~ü¸´O¥R‰Î;Ëö+ amm-ÛV£F ±sçNë.((Ç/5žâ¥M›6âæÍ›Ç ÚØÙÙi½ŸEÅÀÀ@\¼xQÖ̘1Ò~+++Ù¾{{{Ù1 …………l›¥¥¥¸té’¬o\\œ066.óºœÅÇ¥~999Ç êׯ/„bíÚµåºocÇŽ•Ž™™) ¥}QQQÚL5{öì‘/((H£Mbb¢033Óˆ·I“&Zcš5k–Ô7<<\¶ÏÉÉ©Ôë™={¶ìÜ—/_–í_µj•lÿرc5ŽQ«V-m‹-Ò¸.¥R)û.öíÛWæýz‘¶nÝZü:–ºÆcÙ²e04ü¿eîÞ½‹„„aÁ‚èß¿¿´¯¤ç(œ"Þ³gO­û¦NŠ ”+¶+V`ܸq²©ðê«íà³Ï>ÃìÙ³5úÆÇÇKßµ©©):wî\®s¿ì¸hÇ  OOÏ ;Þ[o½%-‰íííõë×—«ÿˆ#púôi@›6m4V]ªiii ÂßÿË—/ËöÙÚÚÂ××'NÔx¡k•J%ûÑ3f f̘¡óù³²²dC¦'L˜€É“'—ó*ž]Ÿ‰‚‚¼òÊ+HLLìØ±=zô(µÏ¼yó333$''ãÔ©S9räSÅùþûïkü%GWÓ¦MÃîÝ»®Êtâĉrõ_°`¶nÝ  ð/BÅß=ñ4-Z„-[¶ÀÑÑ111²•²ˆˆˆˆèå³zõj´k×aaaxðàÜÜÜàáá¥R CCC|ûí·èÛ·/¶lÙ‚øøxÔ­[íÛ·ÇÈ‘#áèèœ9s@aB§Hûöí1oÞ<…«³:99áäÉ“øóÏ?‚û÷ïÃÓÓ”½“¯ˆ«««Ô€F²­V­ZØ¿?BCC±cÇ\¹rppp€»»;>øàƒ2“y“'OÆØ±c±wï^„„„ 11>„……ìììеkW¼ñÆ¥&ú^ýué<Íš5C—.]…;vH -oooŒ1BëqÔ§µPø¾¸³gÏbçÎØ»w/‘——‡Æ£K—.3fL‰§å•WpöìYlÞ¼çÏŸÇÅ‹allŒ¦M›¢G8p Ö…:–-[†6mÚàСC¸{÷.Ú¶m !н{wäbiÔ|0dÈDDD(|÷Þ'Ÿ|Rjÿ-ZÈžƒ.]ºhm7vìXxyyaåÊ•ˆ…‰‰ :tè€ÂÝÝÒs¦P(ðäÉÔ¬Y²›žt IDATãÛÛÛcß¾}Ò÷wåÊ´o߯¿þºÖ‡Ö­[WÖ¿øõbÕªUøàƒðûï¿ãÂ… HMMEýúõáââ‚qãÆ¡yóæZ¯IýÝ„ýû÷×Xý™Ê ï!†OSP…¦ô&&&Š › =®›››tý¯½öZ¹ûwëÖMêß¾}û M!<¨1ôº¤2xð`‘™™©q ¥R)k÷É'Ÿ”+†ÇËúÏ™3§¢.ï™ÜºuK 6L˜››ëÔ~É’%²ë.µý¥K—„‘‘‘ ÆŒ#„âðáÃåv®^žeúŰaäãÔ­[·Üý'L˜ õ766~ê8Ô;vL:æ§Ÿ~Z!Ç|8¥—……EߥØ8¥—……¥\UdJ/韟ŸŸôœ´mÛVßᓦvÀ®]»`ll,kÓ´iSÏ_UŒ;“'OF^^¾ÿþ{|ÿý÷ú‰ˆˆˆˆè¥'[ùµˆ£££¢yñfÏž­[·¢  Ë—//ó•M/£7JïïûüóÏa`` çˆª&ü*!•J%-]ý4„ZßCð,t=æ¨Q£d ­·ß~¿þú«ì]¾¾¾ðõõÅ€пäææÖ­[‡ÀÀ@Ù YŸ%&##£2—Œ/ï1ŸW_uk׮ŗ_~)-W^ÿýï¥Ïê/e­]»¶lYú"Å]½{÷ÖXÒ½$J¥ò™~tõùœ0@zŸãúõë±páBXZZVh,DDDDD¤ú ƒ"vvvX¸p¡¢yñ\\\€eË–a×®]¸uëôV¥²lÙ2€‡‡üýýõMÕÄ)½ÏAdd$úôéƒcÇŽIÛ222ЧOôéÓGc%ž'N`ðàÁprr‚©©) aee|ñÅÒôÌÒ¨T*¡sçÎ077G³fÍ0hÐ œ?þ©¯ãÊ•+=z4ÜÜÜ`bb‚æÍ›càÀøý÷ßµ¶?þ¼lQkkküüóϲdŸ:___Œ7Nª !Êi•™™‰O>ùîîî055E›6m0vìX¤¤¤h´ÍÉÉ‘îyŸ>}ð믿j=æáÇ1hÐ 4oÞ&&&pssÃÈ‘#qüøñRc ÿ¯Ã[o½…æÍ›ÃÐÐõêÕƒ——~þùgäååIí.\¸€>}úàСCÒ¶ÜÜ\)6õÑw³gÏ–’}FFFxýõ×ËŒ£èzûí7©þÆoèÔOW±±±øàƒмysXXXÀÐÐ5kÖDË–-([i©47nD¯^½`mm† ¢_¿~ZGêêÁƒ˜4i<==annôë×Ë–-ƒJ¥ÒÚ§Q£FÒËd322°iÓ¦§>?•¡¡!Ú¶m‹ZµjÁÍÍ }ôöï߆ ê;´fþüù°³³ƒÖ­[§ïp*•°°0\»v ¦¦¦X²dÉ3 ˆz©éû%‚OSPÉíøçŸJ]aãÆRÛ+V”¹hB¯^½Ä£GdçP_´ÃÃÃCôêÕKk_±råJËZ´cݺu¢fÍš%Æ4jÔ(ñøñcYŸÅ‹ËÚ,X° Ì{•’’"jÔ¨!õqtt”ö_´£_¿~ÂÅÅEk}dÇÎÎÎÖÚîСCÒ‹eK*­Zµ ²~ê‹vÔ®][Œ;Vk_…B!¦L™¢qÞ²í  4(1¦îÝ»‹[·ni½¦9sæHíüüüʼW/í`aaÑw)ö›ÊE;XXXÊUÀE;ˆ¨šã¢T¢˜˜HuCCCxyyÁÕÕU6-ñСCøñÇK<ιsçpèÐ!XYY¡{÷î²%­sss1qâD\¾|Yç¸Nœ81cÆÈ¦æ¶nÝZ¶ö¯¿þŠ)S¦Èú]¼xQVå•WÊ<—ìíí¥úÍ›7q÷î]­mÿùçÄÆÆ¢~ýúèÑ£4h í»ÿ>†.Íñ×ŪU«°xñbÙ(°öíÛÃÈÈ@á”ÕyóæáçŸÖè;~üxìÞ½[ª›››ÃÙÙYö½EDD`̘1:ÇSÄÝÝëׯG\\¼½½uî§þ²[ww÷ ›.›™™‰"''@áîmÛ¶Å+¯¼"Ý+ ðyž={v‰ÇIKKÚ5k`bb‚Î;ÃÍÍMŠQ%K–`ÇŽ:Çuûömôïß_6º³qãÆ²ÿ+xøða \Ú·hÑ"é=ƒpàÀìܹSª:÷ïßGll,’’’ЩS'ißöíÛ‡N:!!!o¿ý¶´ÏÔÔTz&† &;¾¿¿¿Æâe9|ø°ôY=™õ¬öîÝ+[AyïÞ½¸páN:…ôôtY¶¬ç´Y³f¸xñ"Ž?ŽóçÏc×®]²¤á¬Y³PPP S\3f̼5kÖÄöíÛqíÚ5$&&bÆ ÒŸŸ'N`ÿþýý]]]¥Ï™™™8uê”Nç%""""""ªì˜ð{ÌÌÌàäässsi›œœœàää$½ÓnÞ¼yÈÌÌÄ™3gŠ-ZHíe‰£´´´ÏW£F ¬Zµ 666Ò¶)S¦ÈF‡íÙ³G§L8sæŒTŸ?>ºwï°°°À¼yóàîîÈÏÏÇwß}WbŒuëÖ-ó|ÚÚedd”ØîÇ”a†††øá‡d×­žˆ+Ío¿ý†‡JõåË—K   `ÅŠÒ÷wëÖ-lݺUj»zõjé³µµµ¬­½½=‚‚‚àíí÷ßË–-ƒ‰‰ LMMáää ©¯B¡ž õíO+))Iú\‘+ë<™™™ˆŽŽÆ¡C‡d‹˜››ËÞXÚs AAA²çü7Þ½€õÊ•+:FÍÈÈÀæÍ›¥z@@ÞyçÀÈÈ£FÂ!C¤ýß|óÆ1š4i"«'&&–y^""""""¢ª€«ôê™™™Ú·o pÔYXX>,ª+¢>ª8'''´jÕJcû€ p”_BBZ¶lYj ðÏž¡¡¡4¢°´‘¥DDDDDDDU ~zöðáCüôÓOØ·o"""J\U´´Uiµn·³³“Õ¯_¿^î„_Y+½ª'ëŠ'÷®]»&%3K£>-XÛqЍ'pÔ©_ç“'Op÷î]Ô¯_¿Ôsª_g^^Z·n]jû¢ëB >>^Ú®þA}ºwïžìÙ©è„_~~>–/_Ž={öàèÑ£²)ÎêJ{Nmmmaff¦±]ÛsZ–âÏé'Ÿ|‚O>ù¤ÄöwîÜA~~¾lú0XZZJ£™ð#"""""¢ê‚ ?=JJJ‚¯¯¯4Š (uäããƒW_};vìÀéÓ§ ÔJZ¨¢xòÅÄĤ̘²³³u ]¢žðóôôÄü!ÕCCC1hРRû_ºt ÷îÝ“êÍš5ƒ­­­Ö¶%]§ú»Ý®³¬‘hÅ©'üÔG–•÷~=/ê1i«?‹ÇãwÞÁÁƒ¥mèÔ©|||]»v(ý9U_FÝ‹xNU*’““áää$Û®þìèr^""""""¢ª€ïð{A´%`¦L™"%û ±yófÜ¿ûöíìY³dSdK9•˜˜¨õø·oß–Õuy¯[ñ÷šÅÄÄ ;;»Ä¢¾PGïÞ½e}׬Y£1«¸â‹‰?†º’F~©_gíÚµQ«V­RÏ Èï…µµu©×˜C‡(üÔWAV_!VÝÎ;qáÂ…G›”«W¯ž,ÙVÒJÇOcéÒ¥²dßâÅ‹qçÎ;v .”¼,í9ÍÎÎÖz¿*â9ݾ}{™ß¡¶EdÔßYR¢™ˆˆˆˆˆˆ¨ªaÂï9R1T<ñ#„@hh¨T÷Ýw1lØ0Ù;êÔGÏ•4Õ(œŠ¢±}ûöíÒg###888”³³³³¬SSSYY½z5öîÝ‹7nȦHº¹¹I |…ï•6l˜ÖÅ„øú믱{÷ni›B¡ÀG}TblGÅ;wdÛ222pàÀ©®ëbê×ùèÑ#ÄÇÇË®±  ?þø#BCCq÷î]ÙuªO‹ŽŒŒÔ˜’†·ß~îîî°°°-QÚ3ñ,ŒŒŒd‹—TdÂOýÙj×®fÍš%KŽéúœÞµ¨N}T( ÛwX|jzxx¸ÆsúçŸbÇŽˆ‹‹ƒB¡Ð}˜››‹¼¼<©^|j1QUÅ„ßsdii)}ÎÎÎÆíÛ·ñèÑ#<|ø²ÑEQQQ²¾?þø£lŠ’V®-2bÄœ;wNªÏŸ?§N’í/>õU›ž={ÊF°}öÙg¸zõªTß²e &Mš„wÞy...˜:uª¬ÿ·ß~ SSS©~æÌ´k×sçÎÅîÝ»Ž•+W¢gÏž˜9s¦¬o@@€ÖÅGŠäççÃÏÏO%–››‹±cÇÊVÛU_ñµ4£F’Å [avþüù˜6m^ýu899aãÆÒ¾ÿ÷ÿþŸôY©TbĈHNNDGGËF-ȪPµ)„Àõë×ñøñã yœú½SŸ&ý¬Ôïo||¼ìY •%mŸ²¶Enܸ!«·k×®ÌóU|‡ßs¤¾ø„J¥‚““”J%~øáÀÃÃCJÊ]ºt mÚ´AïÞ½qèÐ!\¼xQv¬ôôôÏcbb‚»wï¢]»vhÙ²%?~,›:innŽ… ê³æÌ™ƒÑ£G(½åîîŽnݺ!--M–D´°°ÀôéÓeý;t耵k×Âßß_Zý4==_|ñE©çíÞ½;–,YRjDEEÁÁÁ®®®HJJ’Ý—-Zàƒ>Ðé:4h€ñãÇãûï¿üûï¿hÙ²%:w¸8ÄÅÅIm]\\0|øp©îçç‡~ýúáŸþœ8q¨_¿¾ÆÈº·ß~mÚ´‘êÅqqqJ¥ÂW_}¥‘<-¯W_}ÇŽ­£*ŸVÇŽ  ð=ŠmÚ´Á€pöìY?~\cjrFFj×®­qäääàÍ7ß„ƒƒLLL4VU^²dI©ïT÷ÙgŸaïÞ½P©TÈÉɺví ;vLJ<Ö¨QóçÏ×诞 ¯_¿~‰‹ßÑS1V¯|÷ÝwXµj•¾b!"ªpê3Æþ§€sZšêGÑ"U©˜ @•””Q;wN¨ÇYTfΜ)„"<<\jmccc#ÆŒ#Õ …¸~ýºtl777ißìÙ³ÅСCµÇÒÒRìØ±C#¶nݺImÚ·o¯±öìÙB¡Ph=fÑq;Vâµïß¿_4lذÄþEÅÐÐPLžúè#Ù¶Ý»wK}‡ &m8p ˜;w®Öãˆo¿ýVãÜ&LÚkì_¿~½066.ñ;0006lÐzí3gΔڽóÎ;eÞ«ÍËËKýZÂE%øíeaay¹J±ßÔ¯ô KÕ*j•õo–jV†èû·W½p„ßsäîîŽ;v`Þ¼yˆEÍš5áááÖ­[¼¼¼púôiÌœ9‘‘‘HOOG‹-еkW,\¸æææHHH(ú&NŸ>--VбcGiUË–-±páBtêÔ ;vìÀÉ“'akk‹N:á‹/¾Ð:EÒÃÃCZ`¡ø{û`Ñ¢EèÛ·¯ô»ÀÀÀxóÍ71uêTÙb ÅùúúâúõëØ°avïÞˆˆ¤¦¦B¥RÁÌÌ -Z´@¯^½0~üx÷±Q(ðññ‘ê­ZµÂáDZhÑ"#** Íš5ƒ¾üòKÙ;ìH# ‹ÊwcccìܹÛ¶múuëpâÄ dddÀÔÔNNN9r$&NœkkkØìììŒÕ«Wcûöí¸xñ"RRR`kk‹-Z`üøñZ§Q·lÙÿüófÏž˜˜˜™™ÁÝÝmÛ¶-ñ^ÚÚÚÊîC:u´¶ëÖ­jÕª…ôôt>^ÏUNkÖ¬P¸È´iÓô QÅbªõ•„‹tëÖ :uÒC4/Þ’%K`hh!–/_®ïp*˜˜>>X¾|¹^béÛ·//^¬sûùóçÃÛÛ[*?üðC…ƳlÙ2´oß055E«V­°lÙ2(•Êû|÷ÝwøÏþ#Õ;uê„5kÖTh\U~DDDDDDD/±Q£FÁËË빞C©T"//YYYÏõ>hÚ´)òóó…B///XYYÉÚ ::Z¶ÍÁÁ¶¶¶Zã¾|ù2,,,àèèˆììl„‡‡ãÁƒèÒ¥K©÷éÌ™3ˆ‹‹ƒ««+Z·nÛ·o#>> 4€««k™÷KŸ5j{{{©nooGGG­moܸ´´4Ò¿¯¯/|||ðæ›obìØ±ÈÍÍE^^,,,°oß>tíÚUÖþÉ“'xûí·qàÀÔ«W÷î݃B¡€@ᨳ§Mø­Zµ aaa¥¶yõÕWðTÇ/Ò¬Y3YÂËÉÉIö\ª›>}:¶mÛ&ÕÕïí‘#GðꫯÊÚÇÅÅ!00žM•J…Ö­[ãСChÐ@–Zäççc„ X¹r%LMM‘““ƒ&Mš`ÿþýhÙ²å3]+|ÿý÷˜6mòóóahh•J…9sæ`þüù éÒ¨'õÔi»ŸÍš5{Ö°«&!D•+ö,,,,/Iy¬ïß]–—¯ûúJßñ°°°T­ÀTýwdÆŒ¢,“'O}úô)µMË–-…µµµhÒ¤‰Ø·oŸÈÍÍæææÂÛÛ[£ýÇŤI“ÄéÓ§E~~¾¸sçŽ Ä!Cdmûõë'jÖ¬)ÞyçqõêUamm-lllÄСCÅ•+W„¹¹¹˜6mš¬Ï®]»1|øpqæÌqÿþ}1mÚ4@,]º´ÄëÈÍÍ;v½zõ*•ªÌ{£«¥K— bñâÅâÞ½{B!?~,Μ9£µýíÛ·ELLŒðóó..."&&F*™™™ZûxzzŠ:ˆF‰ÀÀ@.víÚ%¶oß®Ñ6??_:Þ‘#G±lÙ²ãwppµjÕ-Z´‡yyybß¾}ÂÈÈHôèÑC£ý”)S„B¡ÁÁÁB!¢¢¢„‰‰‰:t¨HKK999eÞ³’¬]»Vøûû—Z~þùç§>~‘G‰Ó§OKõÓ§O‹GAÜLK IDATim›œœ,bbbD=„‡‡‡ìûÊÊÊÒhŸ””$¦L™"Ο?/ Ä­[·Äܹs1~üxö¶¶¶ÂÀÀ@ôë×OÄÄÄ¥R)þúë/aff&ÚµkWêuˆ©S§–Úfýúõ€7nœˆŽŽ·oß&LÄúõëKí[äõ×_–––B©TjÝŸ––&Ξ=+Õ###K|–ŸÕÖ­[‹ÿ»ÍCT‚ßߢ¢÷ž*h&üXXX^®Â„ Ë /Å~‡˜ðcaa)W ¿5kÖˆþýûK¥iÓ¦¢^½z²m_~ù¥¬OË–-…‹‹‹¸ÿ¾l»°¶¶Ö8GIZ¶l)dÛúõë'lmm¥ä@‡Dƒ Dvv¶BWWWñŸÿüGÖÇÙÙYØÙÙ‰¼¼<Ùv777Q»vmQPP sLÁßß_ÔªU«Üç2dˆðððЩ­§§§ ¾ÿþûr#99Ye'üÜÝÝÅÇeÛ;vì(4h ÑÞÍÍMtëÖM¶­ÿþ¢yóæåŠ­ªyóÍ7E§Nžª¯J¥õë×mÚ´ÑØgkk+<==5¥Ÿ~ú© Ž=ZâqËJøååå {{{Ñ¢E Y’[¥R‰Æ‹Æ—ûÝ»w…¥¥¥xï½÷Êlû"Tö„ß–IDDDDDD/œ••ììì¤R³fM˜˜˜È¶ÕªUK£_«V­4Þ×®];dddè|î^½z!%%Ec{ýúõ¥w•ÙÙÙ¡aÆÒë£ìììððáC©mJJ âââàåå…ìß¿_*nnnHKK+׊§áµ×^Czz:ú÷ïÐÐP(•ÊçržÎ;cÒ¤IÏåØmÚ´AíÚò7Ú”ôýÖ¯__ö…Ó¥MLLžKlÕB¡@Ïž=µ>ÿжm[û7xð`@TTÔSŸ7>>ÉÉÉðôôDpp°ôg%88íÚµÃ7ššZê1¦L™333|óÍ7OÇˤª¾Ã/€ô2cÇŽ•øÒO"¢ªèÝwßÅùó狪Wõ Ñó0hÐ 4Hª"..+W®,÷±J[€àÉ“'Ø´iŽ=Џ¸8$%%áÁƒe³øûĊׯ^-ü+ÚÞ½{±ÿ~þ&&&HLLD‹-t¹„ áïï{÷îáË/¿Äž={P§N 6 “&MªÐ8¬­­+ìXº(éû8p ÆE‹aôèÑøûï¿qüøq|ýõ×Ï|Π  ìÝ»·Ô6¾¾¾˜5kÖ3ŸëyÊÈÈÀ† pâÄ ÄÅÅáÖ­[xðàFRµ4EïÄKJJzê8Šþ¼üñÇؾ}»Æ~$$$ÀÆÆFkÿ}ûöaóæÍضmêÔ©óÔq¼LªjÂ/K½Ò¼ysØÙÙé+"¢ Wl!¢\}ÅADDDT•%$$ k×®xüø1Fމ€€4mÚ›6mºuëžéØæææ w3fLE„[!¦M›†ÀÀ@9rëׯNJ+°nÝ:ìÝ»WcQ‡ªnܸqˆÇœ9s0gÎaúôé˜2eÊ3»iÓ¦ðöö.µ.«1ëÓåË—ñꫯB©TÂßß¾¾¾hÚ´)~úé':tHçãdgg€Ö·º*úóòûï¿ã­·Þ*Wßèèh¼ûî»øüóÏ1pàÀ§ŽáeSU~DDDDDDD¥úì³ÏpïÞ=ÄÅÅÉV>-kä–.œ§NªT ?022B¯^½Ð«W/ÌŸ?íڵ÷ß~[í~gΜÁŽ;0mÚ4Œ3M›6­°é¼¬òÉ¥éÓ§#++ ¨_¿¾´}Ó¦M%öQ©TÛ.^¼%®¬ …^Ê“ð»}û6üüü0dÈÌ™3ç©Ïÿ2â;üˆˆˆˆˆþ?{wVcúÿü}Z´RQ²”¶£…ì´IYÉ’A-ëh”&|-1ƒAY²ÇPÖÈ2ÄŒDŠ¡"íi¡´ïuÿþpóë8§E*>¯ëº¯¯ç~>ÏóÜ÷Óq¾ÍǽBœ……†.Ö{FFFBCC:::üºgÏžáÈ‘#•7©EEEŒ1DTTÔg__XXˆÒÒÒ:?¿¶´´´Ð¦MdggW£¬¬Œ˜˜”••}ñöˆÓœ9s ««‹ 6ÀÈÈ222(//¯×ϵ)PRRÂëׯE&ç*‹ŒŒ„¾¾¾@²ïáÇ8uêT•מ>}/_¾¨óòò‚œœ† Rç6khh _¿~عs'’’’juM^^FŒ®]»b÷îÝu~ö÷Š~„B!„Bœ££#.\(Ö{ššš".. .DPPV­Z…AƒËå‚1V«µüª³sçNÈÈÈÀ‡F\\ÂÂÂpôèQüõ×_U^—›› mmmŠ5é÷èÑ#lݺÁÁÁÈÉÉÁ;w0wî\DGGcâĉU^7lØ0äääÀÃÃ/_¾„¿¿?6lØ ¶v});vÄõëס  HIIAJJ êêêpvv®6ÉÙ”ÙÚÚâýû÷X·n^¾|‰'N`óæÍBq¦¦¦‡››‚‚‚àîî[[[ ;;%%%B×())ÁÎÎ{÷îųgϰpáBœ?ÎÎÎU®¯Ç€€ 77Wèüž={PZZ SSSøùù!>>Ož<ÁáÇqæÌXÆÆˆˆXYYaß¾}سg¿Ü»wï3ßÚ÷‡¦ôB!„Bù&­[·™™™Ø¿?¼½½Ñ¦Mìܹrrr=z4’““¡¦¦Vçûkkk#,, NNN˜5kd‡ÃÁ¸qã0eÊqu¥Vž>}Š+V$rÔÕÕ±eËÌ›7¯Êë ;;;üþûïØ°a$%%amm wwwHH4ÞqB{öìÁÇ¡¥¥….]º@VV222ÇЭ[7±'‘[[[ 6 ÿûßÿàáá))) 6 Ë—/ˆÛ²e ±mÛ6lÞ¼ðõõÅû÷oßò7äà:t(,,,àêêŠÌÌLHHH`úôéØºukíâ%û€ëîuîÜYà|çΊY³faÒ¤Iüz 899aìØ±üºÒÒR\¹ràêê*ô¬E‹Á¢Æ6}Ï8Mq¨+‡Ãq°‘wœ’’B›vB¾)fffæ3ƪ_1˜BÄŒÃáTþ%qc̽ÁCir8Ž,€BÞ±››<==¬=ÈÊÊ‚žžÞ{F~~>"##!!! ‰………’’‚´´´XÛ‘——‡¸¸8äää uëÖÐÒÒªõ3222]]ݯ¾ïçbŒ¡{÷î011Á¾}û„Îkhh`À€8~üx´îëx÷î’““¡««‹-ZT—––†¢¢"hiiÕúÞxñâ455ëµYGUrrriiitèСIî¼ëçç'¸Ѓ1ÖPíùð#„B!„òMkÕªUÓëKAA½zõªu¼œœÜi‡¢¢"Œëtí×xOâ‰gÏžaõêÕBçBBB’’‚þýûý†}Ejjjµ¡Zy ¿Ú’@—.]êÒ¬ZiÑ¢úôéóÅîO(áG¾‘‘‘¸}û6œõó¯-((ZZZÐ××GRRþûï?XYY‰mçªÚJMMEXX,,,мyóãËÊÊ„¶‰8p deeÅÞ¶²²2\¹ròòò°¶¶®6öêÕ«ÐÓÓ—ËEBB^¾| kkk±ÿ«,!„B!¤z\.:::X¼x1annŽÜÜ\\¾|»wïÆØ±c1{öì†n&! †2#ä‹*((€§§'ž={öEŸ³fÍÌ™3ç‹?§©qppÀþýûW®\­­-222ªŒÏÉɧ§'"""ÄÚŽ›7oÂÖÖ111µŠÏÍÍ…­­­@IOOk›ÊËËáëë ØÛÛÃÃãÆkìììpäÈÀ… `kk‹œœ±¶‹B!„R3iiiܹsC‡…§§'ÌÌÌ0iÒ$„††Â××~~~””lèfÒ`¾›~áááÐÕÕ­Õè"">ùùùX±bZ·n®]»~±çüòË/011ù¢CŽ¿YYYX±b´´´`ddÔ`íPQQAaáÇ%_|||°xñb±?ÃÝÝüñ¬¬¬PPP öûB!„B¾¬öíÛ‹\¿òðëÙ³'îܹÓÐÍ _H—.]°téRúœO())ñoåýoc_|—GVV²²²_lºl—.]pëÖ-üóÏ?PUU­Õ5Ÿ¾O úGB!„B!Îw“ð«IXXðüùs¼xñ³gÏFçÎaaaóçÏ‹¼¦¨¨GÅèѣѥKÂÑÑQQQqééépppÀÝ»w±sçNôêÕ ;wîðq›lSSSœ9sFä3îß¿ñãÇÃÐÐ ÀüŠŠŠjûòäÉØÙÙáöíÛuxUcŒÁÏÏvvv022‚¡¡!ìíí±cÇ¡ØÂÂB888à‡~xyyÁÁÁ˜?¾Èû¿~ý Å‹/0gÎtêÔ ãÆC`` PüÑ£Gù÷䕪ÃÁÁ¯^½BXXœœœ`dd„þýûó·úþTII 6lØtïÞóçχ››F ccc”––Öæµ aŒ!22²ÆRTTT§ûW¦§§Çßf][[êêêPPPŠËÎΆƒƒ~üñGÀÖ­[ùïtéÒ¥"ï]PP€ÀÞÞ;wF§N0yòdÄÅÅUÛ¦ÀÎΘ0a"##ëÕÇÊ®_¿Ž±cÇB__VVVصkW•±Ó§OÇ€>ëþŸ¾O 4kÖ¬>M&„B!„BÄî»™Ò[“ôôtœ?jjj¸pᆊ#FààÁƒ;v,^¼xk\\\põêUŒ1fff¸qãÎ;‡[·n!::š?¨  çÏŸGII ²³³Q^^Ž¥K—"!!=B^^&NœˆÜÜ\M |}}áääUUUL:?ÆÏ?ÿŒ‡âôéÓUöåܹs¸|ù2:tèðÙ ê,]ºÛ·oÇðáÃ1iÒ$(**âÅ‹"²²²àp8>®¯Àë[UF|øðçÏŸÇÈ‘#ñÛo¿AFFÆÆÆ¸{÷î§[] ÝóÙ³gÕ®;—’’‚óçÏ£M›68wî†;;;|8úöí‹¿ÿþ~~~¸ÿ>"##En°±téR$%%a̘1044ÄþýûqýúuƒËåÖ«¯;wîÄ’%Kо}{Lž<·nÝ‚‹‹ ÂÂÂàããS¯{ó 8†††>&ÿlllÄr_B!„B!D¬cM®pÀx%%%…}*,,Œ=}ú”_$$$ØÎ;êrssùñW¯^e—Ëe/_¾ä×_ºt‰`›7ozÆû÷ïYYY™@ÝöíÛ¶ÿ~~]ll,ÀzõêÅŠŠŠØåË—fnnÎJJJØÙ³g"poeee6dÈVZZʯ߸q#À.^¼(ÔžÌÌLæããÃÒÒÒªŒù\………¬Y³flÊ”)Ÿu]zz:À8Pclpp0ÀZ´hÁV­Z%p®¢¢¢ÚkÝÝÝÙdzhçÎc˜‘‘{ýú5¿þÔ©S óòòˆŠŠbØÊ•+ùuW®\aعsçjìKu***Xlll¥¸¸¸^Ï©‹øøx€?~¼ÆØôôt¡ŸËêÕ«vúôiú'N0Ì‚åççóëïÞ½Ë0ggç*Ÿ³k×.€ÅÇÇW“Àdee™£££ÀßÉåË—3ìÞ½{ÕöÅØØ˜YXXT󵙚š²JßsY#øî¥B…Ê÷U*ÿ®À³¡ÛC… •¦UÈVþqssc„ò-áýwn¥Ò5‚ï_^ùf§ôöìÙ=zôà—ŠŠ ,\¸P îÑ£GB×íÙ³G`ô•™™8RSS…b[µj%´fœ½½=àÍ›7BñNNN‘‘á$ûñÇ!--Í?®<µÑ××YYYX¼x1¤¤þ ¦³³3¤¥¥áççWeßUTTðã?¢uëÖUÆ|.IIIHJJ"33ó‹ïJ:uêT¬]»V Ž7R°¾8 0’ÌÜÜ„~¾ÉÉÉ€~ýúñëLMM ÞSP9´µµk,}ª¨šššÐÏeÔ¨QDþ`ÇŽ——ç[XXÀÊÊ §NªW[öíÛ‡¢¢"¡uçÎ ‡SíßB!„B!ä[óÍNé}ûö-ï_–|ܽÇ××ÖÖÖüº–-[ ]÷é4Í–-[BVVeeeU>+''‘‘‘ˆŽŽFJJ ¤¤¤DîúÉ»7/áñéqyy9?–—TZ½zµPòKJJJ¬ëžÕ†´´4Ö®]‹Ÿþššš7n0|øp±o”1bıޯ²O¾íÛ·¡ŸoŸ>} --³gÏbèС€€€“ÉõQQQQ«õ{õêÕ$6„ÈÊÊâþ“’’ Ê]oE}Vz÷îÿýYYYPVV®S"##ÁápD®7Ø_!„B!„†ôÍ&üÔÕÕ…êTTTЦM±=#11®®®8{ö,š5kèêê $ë*==ŠŠŠ3fŒÐ9‡:'FêÃÕÕ–––8xð qðàAtèлwïÆðáÿz{¾$EEElÚ´ ?ýô^¼xvíÚ! ãÆ«r¼Ú*,,„••UqâXÃïKŠŽŽ†««+!//þ†ŸCEE——WçÏuzz:TUUEnÜâàà Ö¿÷„B!„BHc÷Í&ü¾´ŠŠ :™™™8yò$FÅ=&''WïûëééáüùóX´h‘ÀȆ֧OôéÓ^^^8uêV¯^ DFFBWW·¡›'V“&M‚——chÞ¼9<ˆ)S¦Ôû¾ (,,¬1®ªÍMƒÂÂB <Œ1ÂÖÖÅÅÅ"7ë¨Nrr2¤¥¥Ñ¶mÛ:·GWW!!!pssÛôoB!„B!¤©úf×ðûÒž?Žˆˆ,Y²£Gæ'ûbcc«þ[[=zôc ǯ÷½¾IIILœ8{öìAii)|8¿­¯^½ªöºO§ÖVTTàÒ¥K066®×Ôðž={¢  gÏž­ó=!„B!„oÅw3ÂïíÛ·ü©ƒâÀÛP!,, Œ1p8<}ú#GŽDEEE½\'NÄ–-[àîî###XXXœ¯¨¨ZŽ'66^^^pvvØ€¤¾ …F/žû9™™™˜7ofÏž-°WAcvïÞ=œ9s¯^½BQQ:vìˆÙ³g ü÷ŸïæBÔš~õahhˆ!C†ÀßßÁÁÁ——GDD~ÿýw?~œ¿Ëk]IJJâØ±c7nKKKhkk#** ÿý÷öíÛWå—‚¶nÝŠÜÜ\øøøÔ«•uïÞÀÇU%$$pÿþ}DDD`îܹèÓ§O•×9»w¹9”””ðèÑ#Ì›7ëׯ[ÛÄÍÊÊ sæÌÁÞ½{qá¡ó}úô¹ËsS'!!;;;9r ——GHH\]]’r&&&033ÃÞ½{ñÏ?ÿbbb°{÷nxzzVùùŸ?>úôé áÁƒ:t(¦M›VcÛlmm!-- ¡Ïµ¬¬,üüüàèèsssXZZBCCøï¿ÿ€Aƒ \3jÔ(222Àápøký¹»»cÉ’%µq„B!„|þù'ŠŠŠÐºukTTT )) Œ1èëë# @¬ðððà‹ú; |ÜÕøÙ³gØ·ož={†¤¤$hiiÁÑÑ]ºtŠwpp¨ò_’ê»3!„Bù|·n݇DnÄö=*??………PUU­õ5ééé••E‹-ª1b²³³qçÎÏn×âÅ‹±cÇܸqChVVcççç‡I“&U®êÁ «*þk£M;¡  ©©©¨¿qã–-[†‘#GR²B!„`gg{{{|øð£F‚ªª*”••ajjŠââb¡øóçσËåBII ªªªÐÔÔĄ↎1cÆTù܃BYY±±±B犊Š`dd„>}ú ¼¼¼~¬Þ‹ˆêïÙ³g¡«« eee´jÕ :tÀ‘#G„↠"r攕•‘””T§6Þ¾}{ö쩶ܸq£N÷®ÌÊÊ 'NDzz:†UUU())¡ÿþ"7Ôô÷÷‡ŽŽTTTвeKhii‰Ü,sàÀ˜:uj•Ïõòò‚²²2ÒÓÓ€¿ ¨é¹ŽŽŽ(//¯r„ß©S§Àår¡¦¦†æÍ›ÃÑÑùùùBqñññPVV(‡®²ðøñctïÞ-Z´€šštuuqíÚµ*ã³³³1{öl¨ªªB]]ÊÊÊÐÑÑÁòåË…bøá(++ãêÕ«xðà@»* ^¨—Ë€zmâHD£)½„ˆ°lÙ2\»v ½zõ‚±±1´´´7oÞ`úôéðòòjè&B!„ò]ÊËËc &L@FF<<<‘‘B#£:„Y³faøðáðööF³fÍpàÀ8;;#%%+W®äǪ««W›¤ˆ‰‰A~~>455…Î!)) rrr(--ý¢É‹””9rúúúÐÓÓ8çããƒ9sæ`äȑسg¤¤¤°oß>̘1©©©pssãǶnÝÏŸ?¯ò9oÞ¼AQQ‘кsµõ÷ß‹L¤U6jÔ¨z¯?—››‹fÍšaìØ±())ÁÚµk‘šš ¡M¼½½±`ÁŒ=>>>àp8Ø»w/¦L™‚´´4,]º”ÛºukDEEUùÜ7oÞ ¼¼­[·ðq£Pü +ëܹ3 11QèÜ•+WpçÎ,[¶ ݺuÙ3g°uëV”••áܹs±-[¶ä”ÍÌñÆî IDATÈÈÀÊ•+QRRRe>|KKKèëëcûöíhÓ¦ 6nÜ[[[üûï¿0`€@|ff&ºw¬,üöÛo077Gqq1ÂÂÂDŽ$2e ÌÍÍáííÂÂB¸ººòÏÕv€Lpp0­v"^”ð#D555}Ê?~úô)ÒÓÓñçŸòë¸\.FŒ!tíÝ»wáàà€‹/V¹t^^V¬Xsss\¸pŸ„³²²BAA6n܈Y³fñZ\.§OŸ®²½‰‰‰ÐÑѹ;.o䟴´4deek÷j)..{öìAyy9þý÷_\¾|rrr8zô¨ÀòC999øõ×_Ñ¿ðÏ 4ùùùX·nf̘ÁßÐ’ËåâòåËÕöWOOOh‰£ÚÚ°a6lØP§k?WPP&Mš„#GŽT¹{ñ‡°jÕ* 4§OŸæ÷ËÚÚ¶¶¶X³f ¦OŸÎŸòÍår«¢š˜˜Èiiiàp8"§PóîÉK V&--7nðd}ûöÅû÷ïqôèQDDDlÌÒ¼ysÌ;+°å§Ÿ~‚¼¼<£QàãôÛ¶mÛÂÕÕUh#ÊåË—#55Ož<X½ª©¶C† \¼xÙÙÙü¶ÕVII nݺ…þýû×8›|>šÒKH8lll°lÙ2üú믘2e %û!„B“û÷ï Lï|þü9êþþûo‘×jkkãøñãÕnþvóæMþ¨­OGÜýüóÏ(((À… øu;vD^^óQ£FAGGñññ„<ŸRSSƒ²²r­û_[Ïž=üyó°`Áœ9sNNNuãÆ ¼ÿ?ýô“P’ÎÕÕyyy¸xñ"¿ŽËåâÇü©£vvvÐÕÕåOáMLL¬Ó†Š ÁÐЇ®2Ù×®]ǰlÙ2÷ÃápàêêŠììl(—ËEZZÝ!C §§‡´´4Âï';;rrr"GwÊÈÈ@RRYYYBçlll„FÃÍ™3Œ1ܼy³v/@„„„|xÍ3{{{œ?wïÞÅ€––&r BuýíÞ½»@ ~²*)) ¹¹¹¸|ù2Ú¶m‹½{÷âÿûajjZçvâÉ“'ÕÆtíÚµÚukKOOÒÒÒÕÆ|îûár¹`Œ!99oß¾Åõëס®®Ž}ûöaåÊ•HLL„ ?^MM ())JDçå塼¼¼ÖfðvÞå%›ë‚×ooox{{‹Œyõêzõêàã¦#eeeèÝ»wŸù9Þ¼yƒõë×ÃÝÝÆÆÆ_å™ßJøB!„BùæðF®ñ¦2V&##)))*'üΜ9ƒ`òäÉX¹r%V¯^¤¤¤ñÖ¯_?ÌŸ?ÞÞÞ8uêÎW×_999HHHTÙߣGÂÚÚ£FÂúõëñ믿âíÛ·õêï‹/ª¡ÉÃK¯6ª{?¼ºÊï‡×÷ÄÄDìÞ½Æ ƒµµ5¶mÛ†eË–!--M`Ä'oªôû÷ï…Ö=|ÿþ=€»òÖ/yY×éÔÀÿoèòûï¿£_¿~"côõõùæ>TTT¬ó3k+++ ööö°±±‡‡ÇÞ÷Š~„B!„B¾9¼dL\\´µµÎÅÄÄ ¬¬L aÃÛÅ4<<ÇÇáÇagg777x{{£¨¨¨Á§¸zzz"00...8p ÔÔÔøçxxÄÅÅ¡}ûö×½~ýýmÕª”•• œ8qÖÖÖøå—_àííÒÒÒzõ×ÝÝîîîu¾^Ü*¿ŸÊï ø8Ò €ÀûiÛ¶-ðèÑ#œ9sçÎCß¾}±jÕ*ìÚµ ï‡wí;w„v?¾wïžÐý«óæÍ€ŽŽÎçtQ€ÿÏfff5ÆóFþ÷ßu~fm”––b̘1PTT„ŸŸíÎûÑ~„B!„BœºººX×Ì611””|}}…Î:t`aa!PÏår±uëV(++côèÑPPPÀ?ü€7@ƒ'ü±wï^¼{÷óçÏ8gff ‰ÏîïüÖ­[ÃÞÞ-Z´À´iÓMÅÉÜܧÊ÷Ãápзo_z===lÞ¼°µµ…ŠŠ &Ož,òýL˜0²²²8vì˜Ðýÿúë/4oÞ\hT&ܾ}>|¨;vì8N•#ójC[[ÚÚÚðòòBaaañjjjèÒ¥ öîÝË‘X[Íš5Czzz­b‘€‹/ŠmIć~„B!„BÜŠ+°oß>±ÝOWWNNNðõõÅþýûÁœ;w[·n…½½=ÌÍÍ®ár¹x÷îfϞ͟V9þ|dffBZZZhsžŠŠ Œ5 S§N[û«2lØ0LŸ>§OŸÆÉ“'ùõúúú˜1c<ˆÃ‡óû{êÔ)ìØ±cÇŽZŸ×ß9sæðGZÍŸ?““û¦6-ìܹ3¦L™‚½{÷⯿þâן8q»víÂĉùk;òðÞϼyóøÓk]\\‘‘-Zð§ñ@Ë–-1mÚ4\¼xëÖ­C^^rrr°zõj\¹r³gÏFóæÍ…Ú•””„qãÆ!22€;wbÈ!;ô~.IIIxyy!>>#GŽDrr2€Ó¨ããã!tÍÞ½{‘••…ÁƒóÛ©©©ÕŽü0`^½z…Û·oø¸±¨Ž=<c¬É¤¤0Bù–˜šš²JßsY#øî¥B…Ê÷U*ÿ®À³¡ÛC… •¦UÈVþqsscâ2`ÀfeeU«Øœœ6~üx€©¨¨°Ö­[3ÌÖÖ–eddůY³†III±äädzf``PåsÒÒÒ˜¤¤$“““cyyyŸ×¡j¨¨¨0{{{¡úŒŒ ¦®®ÎTUUYZZ¿>;;›3†`-[¶djjj 9r$ËÌ̺ÏÊ•+Y³fÍîÁc–––ÌØØXlýø’zõêÅFŒQ«Ø>0{{{€µjÕŠ©ªª2lôèÑ,++K(~ùòåLVVVè³bnnÎzöì)_RRÂ&MšÄ0)))&))ɰ3f°²²2¡xuuu6mÚ4æääÄÿ™`:uú ~*&&†`{÷î­6îÈ‘#LII‰`ÚÚÚ¬yóæ ³¶¶ÈÚµkǰ6mÚ0uuu€™˜˜TÛ–-Z0üÏ\‡bJKKÙ'¿[”… VÛÆèĉŸö£;kß¿¼Âa¿Œ›‡ã`#ï8%%¥Ö‹_BHS`ff†àà`Þa0c¬æ…7!DŒ8Nå_71ÆÏBL„FÃáÈàÏ#tssƒ§§§Xî‡#4«:=BXXÊÊÊЭ[7ôíÛG(.%%ÉÉÉB#á™™ÉßÍU”ÈÈÈ k×®µïL îÝ»%%%‘»˜FGGó7ùt;àà`„……¡¼¼Ý»wçOgýTrr2RSSù;µòÄÇÇ#++ë³ÞqCyòä ddd>k§× <<Œ1þû%)) ïÞ½C=êãââ››‹.]º]ÃÃóçÏ ˜˜˜ÀØØXäû¿ÿ>Zµj<{ö ·oßF‡`ee%r4`eÑÑÑèØ±#öî݋ٳgWûîÝ;#""ÒÒÒÐÖÖÆàÁƒ¡   2>77ÁÁÁˆŒŒ„ŒŒ úôécccHIU½ Dqq1.]º„„„`РA‘‘x/·nݪòz Z¯qØXøùùaÒ¤I•«z0ÆÂª=Ÿ¢„!„4B”ð#„44JøBêãK&ü!ÀÕ«W1lØ0œ|ЩS§†mi´hÓŽïÔ‰' ''Ç/Íš5Cûöí1iÒ$þ–ä¤qÉÈÈ€••TTT`aa555ôèÑ B±gΜœœž>}Z§g999A__111õmv½ãáÇÈÌ̬U|LL ~øá¨ªªbäÈ‘_¸u„B!„òõ¸ººÂÔÔúúúðóóÃüùóѹsç†ni¤h„ßwîçŸF§Nÿý—.]•+Wðøñã&7þ[VXXˆþýû#::üñ¬¬¬ŽŸ~ú  …šššØž7räH4øTù·oßÂÜÜþþþ?~|µ±{öìÁÂ… ¡¨¨HÛ»B!„B¾97nÜ@BBâãã¡££óMí¢LÄï»áתU+5t3!C†`ÆŒX¼x1àããƒììlüþûï Ý4R‰¿¿?"""°mÛ6üôÓOèÑ£f̘'N )) ëó&Mš„€€€&•8{ûö-V­Z…¸¸8hkk7ts!„B!Dì:tè€þýûS²Ôè»á—••…ÒÒÒjcÞ¿cÇŽaäÈ‘hÛ¶-.^¼ˆ›7oÂÐÐ666"çÆ¿zõ ˆŒŒ„††¬¬¬0`À˜ÔÔTøùùa̘1èСƒÈg?~222;v¬Ð¹´´4øúúb„ U^/.cÆŒ‚‚¢¢¢DžŠŠB`` ¢¢¢ ©©‰Aƒ¡_¿~1oß¾ÅÉ“'1nܸ*¿„Ž;Œ=Zì}—””øûûc̘1hÙ²%.\¸€;wî sçθ¹¹‰¼¯MÑÑÑèÖ­¦OŸEEE¡¸Û·o#44”,))‰… VûŽRSSqêÔ)ü÷ßhÛ¶-F%´ƒÕ§.^¼ˆÐÐPÄÇǃËå¢k×®èÛ·/TTTø1ùùùØ·o*ïåË—ñöí[@ëÖ­1yòd¡û®]»¶ÚçB!„B!ß ÆX“+Ü0^IIIa5‘`—.]ª6&<<œ`þþþÌÊÊŠ`-Z´`ØæÍ›…â=Êäå噚š6lãr¹ [´h«¨¨àÇÅÇÇ3쯿þªòÙlÊ”)"ÏýòË/ ûñÇkìgm;vŒ`AAAõ·oßfØÌ™3…®9tè“““c­[·fÆ czzz ûé§ŸúͰ“'OVù|uuu6cÆŒ:µ½¸¸˜©««×X?~\§ûóÜ¿Ÿ`.\`æææŸooo¡x&##ÃÚ´iÃlmm™ŽŽãp8ÌÍÍM îåË— ¨òÙ***löìÙücyyyfkk+2vþüù ËÉÉá×}rrr€+VÀÓÓ&&&˜0a@KK RRRUö·¤¤éééuÞVJJ žžž5Ɖk4äòåË¡¥¥…„„hjjâåË—ÐÒÒˆyùò%æÍ›;;;øûûCVVŒ1¸ººbÓ¦M055åfÔÕÕ…„„D•ï'??>|࿟¼¼¼j×ÓSWWðqdå§£=ŠÇÃÈȰk×.¸¸¸àÏ?ÿÄš5kb׬Yïûé§Ÿàíí]å;),,ÄĉÑ®];ܼyÈÌÌĬY³°páB Œ… BWW¯_¿nÐþÖÅòåËѬY³jcBCCaffÆmÇ#%%…‘#Gb÷îÝ(--…´´4€ï‡÷öïßeË–ÁÀÀüzÞˆOÞ³«Zƒ²¬¬ ø÷®LUUU¨nðàÁضmòóóEþ¬k#,, =ÂÓ§OÎÉÊÊâåË—u÷ïßLŸ>½NÏ#„B!„BHí}³ ?ÞôRž)S¦`À€>|x×JHÔ¼yqll¬Ðf<¼$`ll,ºví àcòæÖ­[oooÌš5 ÇG`` Š‹‹4Llúôé°±±AçÎ1~üx¬X±;v슋ÅСCEÞCKK Œ1ÄÅÅñ§Žr¹\„„„øØ_gggøúúâÊ•+øð်÷·¸¸Xh ¨(<€™™YžQYm?={öyNKK åååHHH€žž€ï'""Œ1ìÚµ ³gÏÆÁƒqýúu$&&BBB‚Ûºuk@FF†Èûóê?M6V…7==-- ºººµºæSqqq——Ç‹/„ÎYZZ %Â###¡¦¦&r³B!„òM*«|лwoXXX4T[!Dì^½z…+W®T®úÐPmå›Mø}ijjjÈÊÊyîýû÷üžŽ;âØ±c¸uë^¼xóçÏ£¢¢ÞÞÞ:t(Zµj%°Ké׿èèˆÑ£GÃËË cÇŽ…¥¥¥Ày555~¢îS¢úËårqöìY\¿~¯_¿Æµk×PXXoooôïßmÚ´©sòGZZúÓ¿T"ñ’_C]Þϵk×påÊ$$$`õêÕøðá¼½½Ñ£Ghhhð“š222PQQALLŒÈûÇÅÅ@•küUÕžªv®¶mÛBJJ ÿýw­âyï§ò(GB!„òMHøY[[×jnBi*üüü>ÍM¤7T[D¡„_éëëãîÝ»(((àoØÁsçÎ(** $T¸\.Þ½{‡?þøÇ‡ŽŽ\\\`bbIIÉF1½ÕÛÛÿþû/fÍš…gÏž L÷Ô××ÇãÇQTT$4ºîÎ;PQQ˜>Ú±cG¤¦¦bÛ¶m°··‡¦¦&\\\`aaòòòzõWBBB`Ó‡Æ@__OŸ>EII‰Ðôß;wî@]]-Z´à×q¹\$''cÇŽ3f Ú¶m X[[£  @èýØØØàÌ™3HKKÉWVV†K—.ÁÌÌŒ?}¼²œœ¡º{÷îASS³ÆiëÕ122Â_ý…ôéÓ§ÆøN:¡¬¬ ׯ_‡­­íg?¯¼¼¼.Í$„B!„B¾K5ÏUüF?~=zôÛýæÌ™ƒ>`Ë–-õÿüó‚‚‚0wî\zÞzl/^„‹‹ €ÃÚûô郫W¯V›+,,D`` rssÅÖ~QÚ¶m‹­[·"&&îîîçæÌ™ƒ÷ïßãÏ?ÿ¨¿zõ*nÞ¼)²¿¸|ù2¿¿æææèÞ½;®]»Ö(œâ4gΤ¦¦ÂËËK þâÅ‹¸ÿ¾Ðû騱#JKKqõêUþû±´´„¡¡!‚‚‚„ÞÏœ9sPQQúmÛ¶áÇü{|jÞ¼yI¿ëׯãÞ½{øá‡êÜWpvv†²²2\\\]cüôéÓѶm[,Z´oß¾­õsZ·n þôpB!„B!„Ôì»á÷éš~õegg‡1cÆà·ß~C\\ €èèhlݺ†††øõ×_â;tèiiihii ¬…çââ‚GU›Û¼y3V¯^ ¡„’¸Íœ9'Nœ€··7ÆŽ‹`oo+VàÍ›7èׯ^½z…mÛ¶¡sçÎX±b…À}tttø#­­­ùõ...pvvþæ~ŽŽŽ8tè\]]ñêÕ+ôíÛضmºv튟þY ^OO]ºtAÿþýùõ...˜?¾Ðû4h¦OŸŽ½{÷")) ýúõCxx8üýýagg‡ñãÇ‹l—¦¦&ºv튙3g¢¤¤;v쀦¦&,XP¯þª««ÃËË ³fÍBÏž=áìì DDDà¿ÿþÚ5kìJJJ8pàÆ=z`öìÙ000@ll,°dÉ÷À£  €AƒáÀPPP€œœ‚ƒƒ1nÜ8L›6M vÍš5ˆˆˆ$$$@ZZ'NŒ?cÆŒ©WŸ !„B!„¦â»IøÕ†¢¢",--Eîl*ÊéÓ§±yófœ>}'Ož„††f̘M›6 ­O'))‰qãÆaèСàp8üú &àøñã055­ò9VVV¸páB•›fÔ…ºº:,--E®èããƒY³fÁ××–––üöž;wžžž8{ö,üüü ©© '''xzz íö*--1cÆ`Ô¨Qõ“'O†¿¿?LLLÄÖ—/¥E‹°´´DË–-kŒåp8¸xñ"6lØ€€€;v ÚÚÚ˜;w.6nÜ(4}VNNB‰èiÓ¦áÌ™3BÓd9>ŒN:áüùóذaôõõáææ†µk× ­‹§¦¦KKKxyyáòåËøë¯¿ƒÂËË«Ö|TgÊ”)066†‡‡|||ðöí[¨¨¨ÀÄÄDä:}¶¶¶xþü9V®\‰³gÏ">>úúúèÝ»wµë9nܸþù'víÚ˜™™A___(.##©©©Àß›wœŸŸ_ïþB!„B!M‡1ÖÐmølÇÀFÞqJJJ­7, „ÔlæÌ™8sæŒÈ5É×aff†àà`Þa0c¬þ[NBÈgàp8•IÜÄs¯2˜B>ÁápdòŽÝÜÜhÓBÈ7ÅÏÏ“&Mª\Õƒ1ÖPíùÔw³†!¤ö^¼xAItB!„B!¤‰¢„!™™™èÝ»7¦NŠ~ýú!$$£GnèfB!„Ò$mÙ²ƒ ªUl~~>FŒÁ_º²øøxØØØ 33SÜMl”F…Õ«W×:~Æ 033ã—O7Õ$ä{F ?Bòóó1tèPäææBYY›6mÂÿþ÷¿†n!„B!MRll,BBBjŒËÉÉ JKK¡§§'t^CCÊÊʰ´´Dzzú—hjL›6 ffâ_q&44ÑÑѵŽ700ÀÀ1pà@„††"..Nìm"¤©¢M;!ÐÔÔÄúõ뺄B!ä;vàÀ$''ã·ß~kè¦|5‹-B^^®_¿ŽfÍš —””ÄñãÇaee…Y³fáâÅ‹ ÐJa%%%())ièf`ìØ±;v,ÀËË«[CHãB ?B!„B! îùó爊ŠSLˆ¨ IDATª1.::zzz(--Å­[·P^^333())‰¼&##OžÍ„B!„Bš”ŒŒ Œ5 'NÄýû÷ˆ½{÷ %û.^¼ˆY³faôèÑxüø1Þ¾} 777lß¾Û·oº¯’’´µµñèÑ#äçç#99›6mBtt4~ùå¡x'''<~üûöíCnn.âââ !!ƒVÛþˆˆÖFyöööˆŽŽÆíÛ·kŒURRB×®]ѧO¡wRžžžˆˆˆ€­­-ŒŒŒÁ/BñÍ›7—ËÅ“'OŸŸ¤¤$¬[·QQQXµj•Ègœ:u xñâòóóqùòedffÂÑÑŒ±zµßßßóçÏÇäÉ“ŽÄÄD,Z´žžžØ·o_½îMHcéï_˜†ÀápÜl䧤¤ M›6 Ø"B/333óƒcâ_™BªÁáp*ÿ’¸‰1æÞ`!„49G@!ïØÍÍ žžž1@`` ÿøùóçÈË˃¹¹9¿ÎÌÌ îî‚_?–––¸}û66mÚ„åË—WÛŽN:!##‰‰‰käõèÑqqqx÷îÐ(?QôõõQTT„„„~]TTŒŒŒ0oÞø8ZŽËåbõêÕððð¨Õ5_Ê„ ðúõk„††Öéz---4kÖ ¯_¿¨×ÔÔ„ªª*îß¿/0…zݺuXµj®]»†Áƒ‹¼§¢¢"fΜ‰;wŠ<_^^ŽŽ;¢¼¼111””äŸ300@NNRRRêÔò}òóóäI“*Wõ`Œ…Uÿµ5Õ)½†•ÌÍÍþ²BHS—””Tù°}CµƒB!äKiÑ¢…ÀÀ˜˜”–– Ô)++‹¼¶gÏž5&ûÒÒÒ‘#GâŸþ8×µkW„……áÍ›7µZsÏÆÆFhØÃ‡ÁÃÔ©Sꥤ¤Ð¥K—j“a¼Ä¡ššZÏæá½—ÊIÇ¦ÊÆÆþþþ"ÏuîÜYh½ÄñãÇcÕªUxúôi• ¿šÄÇÇ#66ŽŽŽ 8×½{wœƒ†®®.öïß³gÏÖú>EEEªñY¼Ïƒ¯¯/&NœXçûÒT4Õ„ŸÀ7ªšší¦Cù¦dff¢´´”wXÖm!„BiŠ ÀápRë„ßÊ•+‘žžŽ¨¨(èêêòë/]º$Û¡CGŽq¹\s5­•¯­­ HOOçÿ¹&iii ´Spc¶bÅ dee!::Z`GÞ3gÎTyMEE…PÝóçÏ@àgò¹ ?® B ?ò]hª ¿ìyÏž=£M;!ß”O6íˆjȶB!„| ÐÓÓÛýäåå1räH:tK–,á'|ª MMM¤Zxx8Ž9"”ij¶¶†¬¬,>ŒaÆñëß¾}[mB lmm!!!›7oÂÄĤVý¹uë`ĈµŠ/,,„””¤¥¥kÿ9”””ƒ²²²j7=‰ŒŒ„ŽŽŽ@²ïÉ“'8~ü¸ÈÄœ?áááèÖ­¿ÎËË Íš5ƒ­­mÛ¬ªª kkkìÙ³ ,hR‰SBꂆÅB!„BipŽŽŽX¸p¡Xï¹sçNÈÊÊ¢_¿~8|ø0bcc†cÇŽáØ±cBñ¦¦¦ˆÅ¢E‹„U«VÁÚÚ;vDEEí=àãL³™3gÂßß¿üò ^¾|‰“'OÂÔÔT`G`QÚ·oÁƒãÔ©Sµî˹sçЫW/×›““---¡¬Lü“Elmm‘ ¼|ùþþþذaƒPœ©©)¢¢¢°téRá—_~ÁàÁƒù»‹ÚÅXEEöööصkž?ŽeË–Áßß?üðC}bbbðìÝw\Våÿ?ð×Í ‚€ ‚¸Á=·fŽJMMÍš_KÍÌO&æJ³Ä2WËYš¸2G©¹æÈ=Š‚ˆ€ˆ€ìyýþ0ÎÃ=¸Qô†Û×óñ¸Ýçœë\ç}‡;}{½Ø»w/RSSUŽ÷ÝwP(èС‚‚‚ƒóçÏã§Ÿ~Ò¸ˆQ•%„¨r@Q\âãã‘!ñöö%¾çN‰JðÝËÂÂòb•’Ö¨ïxXXXªV`Vò{dæÌ™¢¢téÒEtïÞ]çú·oß=zô …BŠG¡PˆÁƒ«Ô}øð¡6l˜055„³³³Ø¶m›Ø½{· .^¼(«Ÿ››+† "µmgg'Ö¯_/&Ož,,--µÆuúôi¡T*Å®]»Ê¼‡¿þúK( qðàAî9--M888ˆ ˆüü|Î)ŒŒ ѯ_?abb"¥R)^yåQXX(«—””$†*ªU«&ñË/¿ˆmÛ¶ ",,LVßÕÕUŒ9RlÙ²EØÛÛK?«áÇ‹¼¼<­1YXX”üÿ–ø÷ßÕÖ‹ŒŒ:u’½FFFbôèÑO÷Pè…Sü—(­E%øþ-. !´Ï-P)Š‹‹·ããã9¤—ˆ J©!½§…ô½xþû‹P±%Bˆ½CDUŽB¡0]¼=sæLê1¢Ç«ï†‡‡ÃÈÈ®®®pppÐX799©©©:1~ðàÑ´iS•Õ|µ™3g~úé'œ={µjÕR['-- ;vD§N°fÍÛ~–Cz‹%''ãÎ;¨_¿¾Ö•““’’‘‘Q®a´B\»v µk׆­­mE„+“žžŽˆˆÃÕÕööö~ 2lÛ·oLj#Jîj#„¸¨¯xJ«ªsøéÌÂÂíÚµÓ©®ìììtnÛÞÞþ‰FóçÏÇÍ›7Ñ»wo„††ª$¶222зo_¸¹¹aÅŠåj»zõê厧¼t}NZ¬ê( †/?©5j }ûöϬ}"}ã~DDDDDDDz`bb‚­[·¢W¯^jç³ËÊÊB›6mðÛo¿=—öð#""""""Ò###|õÕWj9::âÛo¿}Α!`¿*( ‡Ö©î½{÷4výÞµkNžSdd$–,Y‚{÷îéT¿¨¨¡¡¡ ÅÎ;±dÉ<|ø°Bc""""""""z^˜„ߪU«­ï0ž›ììlôïß¾¾¾Xºt©Ú:#GŽÄâÅ‹1hРܸqã9G¨^^^>Œ¸¸8½ÆajjŠS§NáÔ©S˜3gŽ^c!"""""""*&á÷Áàúõëå>ïþýûB”Y/!!¹¹¹:·›œœŒ„„ꦤ¤ ++Kç¶`Ö¬YHKK÷ß~«uYø)S¦ S§N3f un¿¨¨¨\ñùäXYY•Yÿ«¯¾Â©S§°k×®2ëÀŽ;`ccƒõë×ëT_ééé°±±A£F“'O† lllðÒK/©=çìÙ³ðó󃵵5àää'''ìÞ½[ãu²²²àïï[[[¸¸¸ÀÁÁ›7o®{B`Ñ¢E°··‡‹‹ ,--1hÐ &""""""¢çŠ ¿ ‘––†“'ObìØ±ðòò²eËлwoxyy©ÔŸ>}:йsgìß¿sçÎÅ?ÿüƒÎ;«íÝõóÏ?#++ ßÿ=N:…Ï>û Õ«Wǘ1cpëÖ-YÝ´´4têÔ ·nÝš5kpâÄ ôèÑ]ºt)ó>6mÚ…BÁƒëtßÍ›7G£FðÃ?èTÿÎ;HOO¯Ð!Òfff ”†Ï: Äÿþ÷?µçlÛ¶ X·nN:…… ÂÄÄ£FÒ8oáÈ‘#‘••…mÛ¶aÿþýpssÃØ±cqèС§¾‡‰'báÂ…èÙ³'~ÿýwÌœ9ÁÁÁèÞ½{¹zO= c}ð¬¬ZµJ6ìT_ý‘‘‘Ò¾ÁƒÃÍÍMåÜåË—cÇŽxýõ×5¶ޝ¿þ~~~RO·þýû£iÓ¦:t(Ö¬Yƒ÷Þ{OvN`` lÛÛÛîîî5j~ÿýwL™2E:öý÷ß#11û÷ïGÿþý¾¾¾hРüýýµÞûÞ½{ѪU+ØÛÛk­WR÷îݱ~ýzdffÂÂÂBkÝ3f`àÀRo¼Š`bb‚wÞy÷ïßÇG}„nݺ•yŸ¥{]z{{£víÚ?~<:„ &¨œóꫯbíÚµÒ¶——êÕ«‡%K– OŸ>Oÿ… °~ýz¼÷Þ{X¹r% oß¾hذ!üüüðÓO?•y?DDDDD†êøñã˜6mš¾Ã "ª0%óK•‘Á&üÖ¬Y#ëU%„Àï¿ÿ.[ݶC‡j~óæÍÓšì€]»v¡  ãÆCNNŽ´¿wïÞ°±±App°JÂO¾}ûÂÈÈ111²ýû÷ïGÆ ¥d_±±cÇ–™8ŠŽŽ†O™×.ÉÅÅEEEˆ…§§g™õ+2ÙW‘ŠŸWéçYlüøñ²m¼ñÆغu+ `lüd¿;wî„þþþ²÷áÕW_…¹¹9Ž?΄½°þý÷_üûï¿úƒˆè…a° ¿k׮ɶ•J%Ö¬Yƒ~ýú•y®. ¯âUm}}}ÕˆˆP»???GÅÕ«WqóæMÄÇÇC‚‚Y½¨¨(´mÛ¶Ì8JËÊÊBJJ jÖ¬Y®óqqq:Ýe‘——§ò<¨|~~~HMME÷îÝÑ AôìÙ(W;ÅÏÙÈèɧ´ÌÉɽ½=öíÛ§ö¸¹¹ù·MDDDDT ¹ú‚^( ÕJlç(ÒP—èY¨Tï~OÈÃÃÛ·oG½zõP«V­2ëgggcРA¨_¿>Ο?/;çã?V©ïææ¦qá m êÔ©ƒ”ë¼âúîîî徦>ž…âÞv÷ïß×ZïüùóÈÌÌÄÛo¿-KöíÙ³Gëy¿þú«l;''¿üò |}}Ÿª‡_= „ÀÒ¥KŸ¸ """""""¢ŠðÂ$ü®]»†.]ºTX{]»vŰaðbÅ JɵÜÜ\\ºt ÉÉɲúnnnP( ÂíÛ·‘˜˜ˆåË—cäÈ‘°°°@JJЬ~ñ‚cÇŽEZZ„Ø·o† Vfl~~~HIIAHHˆN÷’––†?þø~~~:Õ A»víÊ=tV¶¶¶hÑ¢víÚ…””âÒ¥K¸yó¦¬^:u?ýôbbb/¿üþþþ077WyžÅ–-[†eË–!##™™™ðóóCrr2Þÿý2cKOOGjjª4ü¹¤~ýúáÕW_ÅâÅ‹±bÅ iÁœœ\¸p©©©å}DDDDDDDDOä…Iø= ë֭Ø1c0kÖ,XYY¡aư´´DëÖ­±ÿ~Y]WWWÌ;çÏŸGýúõáââ‚… bÿþýhÑ¢âââdõ=<<ðý÷ßã?þ€½½=ìíí1bÄlÞ¼¹Ì¸:wî oooêtË–-C50nÜ8ꇄ„àüùó8räˆNõËkÔ¨Qø÷ßáêê kkk´nÝ[¶l‘Õ©_¿>púôiÔ«Wµk×F`` > OOO•çYlýúõX¹r%œœœ`gg‡;v ,3®öíÛÃÖÖ½{÷V{|óæÍ2d>øàXYY¡Aƒ°´´DÛ¶mqìØ±ò?"""""""¢' Bè;†rS(oÇÇÇÃÉÉé©ÛÍÌÌÄÙ³gѼyór m½zõ*.^¼ˆØØXØØØ mÛ¶ðòò‚B¡P©›˜˜ˆÝ»w£nÝºèØ±#¬­­qåÊäåå¡]»v*õ¯]»†'NÀÉÉ ]»v…BCCáîîŽzõêiŒ)""mÚ´ÁÚµk1jÔ(õÂÃÃѺuklÚ´I§ÞƒÀãù;†îÝ»Wè0é’îÞ½‹½{÷ÂÌÌ ^^^hÙ²¥Úzñññسg4hXYYáòåË(,,”­˜ûàÁ\½zíÚµƒ‘‘Nœ8¨¨(øøø }ûöZcIHH@xx¸´mmm­q5^¸té.]º„¸¸8ØÚÚ⥗^Rû³%Ò¦C‡8}útñæi!D}ÆCD/…BQò‰K„z †ˆˆ¨ jæðÁ9üèEÆ„Ÿ Â;#}ûöIs–.]º`À€øúë¯õ!i„é~DDT•0áG$ÇUz بQ£`ff†/¾øBmÂoõêÕ9r$/^¬æl""""""""ªŠ˜ð3pC† Á AƒÔûì³Ï T*ŸsDDDDDDDDDô,qÑŽ€¦¤“}DDDDDDDD†‡ ?"""""""""„‘aˆˆˆˆˆˆˆˆÈ€0áGDDDDDDDDd@˜ð#""""""""2 Æú "\»v úƒˆ¨Âdeeé;""""""ª¢ "á׫W/}‡@DDDDDDDDT)pH/‘aˆˆˆˆˆˆˆˆÈ€TÕ!½¿ˆÖwôBy@—ÿ>g¯ÇXèÅó@ßQÕQ%~Bˆ0aúŽƒ^ …¢þÂ/_±]ŸñiÂ!½DDDDDDDDD„ ?"""""""""„‘aˆˆˆˆˆˆˆˆÈ€0áGDDDDDDDDd@˜ð#""""""""2 Lø&üˆˆˆˆˆˆˆˆˆ ~DDDDDDDDD„ ?"""""""""„‘aˆˆˆˆˆˆˆˆÈ€0áGDDDDDDDDd@˜ð#""""""""2 Lø&üˆˆˆˆˆˆˆˆˆ ~DDDDDDDDD„ ?"""""""""„‘aˆˆˆˆˆˆˆˆÈ€0áGDDDDDDDDd@˜ð#""""""""2 Lø&üˆˆˆˆˆˆˆˆˆ ~DDDDDDDDD„ ?"""""""""„‘1ÖwDDDT6…B1€¾ã V…B ï è…r@qEßAUULøU ÿà¡ï è…Õõ¿Bô¼$`ˆˆè qH/‘n„¾ """""""Ò~DDDDDDDDD„Cz‰ˆˆª˜~ýúaË–-úƒˆ¨Âܸq^^^úƒˆ Ë…BÑAßA”rB±GßAЋ ?""¢*ÆÄÄ666úƒˆ¨ÂÔ¨QCß!‘áéû_©l˜ð£ç‚Cz‰ˆˆˆˆˆˆˆˆ {øé&Wß‘FE¨œo3Õwôbbˆˆˆˆˆˆˆª4!ÄefúŽ£4…B‘&ýH8¤—ˆˆˆˆˆˆˆˆÈ€0áGDDDDDDDDd@˜ð#""""""""2 Lø&üˆˆˆˆˆˆˆˆˆ ~DDDDDDDDDÄXß’èèh8p7nÜ@bb"Œáààoooôîݶ¶¶Ï‹‹ÃŸþ)m÷ìÙ:_;&&ÿüó´Ý»woÔ¬YóÉnDRSSqåÊܺu ÆÆÆððð@“&M`aaQæ¹+W®DHHœœœ°zõj­uwïÞ¼¼>þøc­q\¸pƒ–í[¶l>øàƒrÞQÕâêêŠèèh\ºt QQQ¸páŒùW"""CÅ!½DDDDO)44-Z´Ðšì€ÜÜ\¬Zµ ^^^ˆˆˆxNÑé×Õ«WѳgO 2ÑÑÑ*Ç 1dÈ,^¼X%Ù<îõ7uêTL˜0Aã5&OžŒüü|aâĉZãY¿~½Ê¾µk×–}#àÝwßðøgòõ×_ë9"""z–˜ð#"""z ±±±4hâãã¥}ÆÆÆhÓ¦ Ƈ‘#G¢I“&²s¢¢¢Ð§O¤¤¤Th,ÕªUƒµµµT4õˆ{žºvíŠàà`Ç—,Y‚£GJÛ¦¦¦­ôëס¡¡hÖ¬ AAAøä“Oà»Fx} IDATîî®õÙ†‡‡ãäÉ“HNNFëÖ­áåå¥6ÎäädܸqCÚnÑ¢…Ú/²³³qáÂ\¸p999hÕªZ·n {{{­q@ZZ._¾Œ«W¯âÁƒpuuE‹-о}{Y½˜˜ÄÇÇK÷Zß©S§íڵãGpéÒ%éø«¯¾*‹¡[·n¨S§îܹ¸xñ¢ìQQQ8pà€Ç½*ûöí«5ö’ÃyëÖ­‹ H?¿]»vaÕªU°³³S{nÉ÷ÐÖÖÇ¡C‡àææ†ž={ÂÆÆFöNYXX E‹ˆ‰‰ÁþýûaccƒÞ½{«,Ärþüy\¼xñññÈÏχµµ5<<<УG˜™™Iõîß¿[·nIÛ 4P»¨KJJŠlÈxÆ ¥ç:pà@©—ã7ß|ƒwÞyGë3#""¢*JÁÂÂRF°€ø¯¤ê;–¯/þ0`€¨L222Ä›o¾)ŒŒŒD‰ïJ©˜ššŠ-[¶¨œ×¿©ÎÛo¿-6nÜ(;¯Q£FB!&Nœ(íëÓ§8tè066–öY[[‹ÜÜ\!„—.]-[¶TQ¿~}.ÅpôèQÙñE‹©½G???Ùý<|øP!ÄèÑ£eç¯X±¢Ìçåïï/;géÒ¥Ò±={öÈŽìííž}ûTÚÞ²e‹¬Þ™3gTê„„„WWW•6•J¥øä“ODAAÆØW¯^-,--Õ>Û®]»ŠÈÈH©î´iÓ4þˆøøxQPP nÞ¼)Nœ8!¶nÝ*._¾¬rMwwwéœ^½zÉŽ-_¾\:Ö­[7­Ï=22RvýÙ³g‹Í›7küY”6a©^¿~ýÄÁƒ…R©”öÙÚÚŠ¼¼<±`Ái_Ë–-Å¿ÿþ+ÌÍÍ¥}ÆÆÆ"))I!ÄíÛ·EÇŽ5>#GGGqúôi)†k׮ɎOš4Im¬³gÏ–Õ»qã†t,==]˜ššJÇþý÷_­Ïíy +}ÿ~¢|÷²°°°’ïÓ§tìã?ÖÚÖÌ™3em………‰ÌÌLQ£F i_ãÆ5ž_2á׸qcÙyÄèÑ£…B–ðsqqÎÎβzÝ»wBñðáCQ¯^½2ŸiõêÕÅùóç¥8¼¼¼¤cµjÕR› mܸ±TÇÇÇGåxûöí¥ãZŸÛó„ ‹¡&üXôU8‡=±ãÇËæY0`öîÝ‹_ý3fÌö !´ÎvùòeäççÃÙÙ¾¾¾P(xë­·Têݸq=BÍš5ѵkWKõ–-[†¤¤$€B¡ÀܹsŒüýû÷—ÚHII‘022ÂØ±ceq”^=÷øñã²Å5ÆŒàñÜt Ò~;;;Ô¨QCËÓz¬Aƒ²í’C4K+»cÇcêÔ©²ã³fÍBvvv™×€àÿûŸ4§ž­­-fΜ‰={ö _¿~P(€àà`lÛ¶Mvî¡C‡$m{zzâ»ï¾Ã† ðÊ+¯Hûcbb0wî\€——ÆŽ‹jÕªIÇëÖ­‹±cÇbìØ±¨^½ºÖxsss1gÎi[¡P`èС²ã'Nœ¶[¶l©±­‚‚lÚ´IÚnß¾=<==annŽ!C†Hû###¥@´‰ŒŒDzzºì=1b„J½{÷î!>>–––èÞ½;LMM¥÷uíÚµ²aâS§NÅÑ£G±eËÙ\„ÙÙÙ²¹ ýýý¥Ï‰‰‰²gW®\Add¤´]ü¾–Ô¼yséséy‰ˆˆÈ@è;ãÈÂR ØÃ……EÏ•´‡ßÙ³gżyóÄo¼!zôè!²³³eÇ_zé%©ÇN“&MdÇJöðÉ,îYvïÞ=©^É~„§§§ÈÉÉB‘””$òóó…Blß¾]L›6MôêÕKÈ®'ëÙVÜ{P!¢¢¢„B¡Ž-X°@vîøñã¥cööö"//Oºvɸڶm«Ó3+((õftrr’Ž•îáW§N‘••%;ÿ£>’ÕY½zµtL[¿©S§Jû«U«&nݺ%k÷Ã?”Ž7jÔHÖs¬iÓ¦²xÓÓÓ¥cEEE¢]»vxÜ“ÓÓÓSdffJÇmll¤s‡ ¦Ó3ÊË˯½öšì^Æ/«sõêUÙñ«W¯jl¯ôs]¹r¥t,44TvløðájÛ(ÙÀhÚ´©4”üþýûÒ{X²‡þë…—’’"„"55Uz6û÷ï3fÌ}ûöãÆ“]+33SXXX¨ô,nÃÌÌL:6qâDÙ¹sçΕýœ‹‡Ÿ—´dɩޯçö<±‡ ‹¡°‡‹ž í ""¢'Ö¾}{•Å€Ç ;üñÇR¯±â}ÚÂÈèñàgggõ-ZSSS-ð0lØ0 6L¥~tt4BCCannŽŒŒ •Xêׯ®]»J=»~þùgÌ›7Àãža{÷î•ê>\Zô£äb——§õþŠ   @Ú.¾gu&Ož¬ÒnÆŒXºt©ÔSïòåË:]÷ï¿ÿ–>wîÜõêÕ“=z4–.] àqOÊððp4kÖ ééé¸~ýºToüøñ°´´”¶ ‚‚‚’’‚æÍ›ëÔËQ›‚‚ 6 ûöí“öÕ®]‹/–Õ+Ù»€ÖGJ.Öall,ëA×¥KÔ«WOêm·{÷n$%%©] £¤E‹I½µÕ={¶´ˆKÉ…Vú÷ï/ëyZ,>>!!!°¶¶Fff&ùûjmm7Þx[·n•âýöÛo¡T*ÈW2~õÕWÕ.˜SòY¥¦¦"??_¶˜ U}LøÑS+((ÀÉ“'qðàA;v .\PIˆ'¨Ô122B›6mtºV»ví´OKKÃÑ£GqðàA#::Z¥NéXüýý¥„ßµk×pýúu4mÚ!!!HNN–ê•Y³fMYÚ†æ– !„´­iUXàñÐÙÒìííagg' _ŽŠŠÒéº%W’%½Õ„å;wЬY3ÙyÀã©.q>‰‚‚¼õÖ[سg´¯víÚ UIªÅÇÇ˶­¬¬Ô¶‡C‡IÛ:t@VV–ì½èÓ§¾ÿþ{ŸÃÆeCÒÕ)ë=Ôµ^ff&‚ƒƒqðàA?~\6·˜º÷µ8á—””„ôêÕ ×®]CXX˜TOÝp^@õY=xð@k’ˆˆˆª&üˆˆˆè©ìÙ³ãÆCjjªl¿©©)ÌÍÍ¥ùïJöö+­fÍš²¹Þ´Ñ”˜(**ÂôéÓñí·ßÊzГj=B~~>Õ^uC† Á”)Sžžàq/¿O>ù;wî”êxzz⥗^’¶MLLP§Nܹs••…»wïÂÕÕUkü¥h 6ÔXWSËÞÞ^JøÝ¿_ëõ€Çó>zôHÚŽŽŽV›-)66VößbOÛƒO“ÂÂBŒ5JöÌ]]]¢ö•LÄ* snܸQ–|þ믿Tz7–¶víZ|øá‡ZßY]dÚê}öÙgøôÓO‘››+Ûoee…‚‚deeP}_{ôè!{÷~þùgôêÕKÖ»ÏÎÎýúõS{] ÙvqOB"""2\´ƒˆˆˆžØÞ½{1dÈ)ÙW·n]|øá‡ Ejjªl‘mCW‹‡èêBSbpâĉX¹r¥”ìëØ±#¾úê+\½zIII²žt¥c177Ç›o¾)mÿüóÏ(,,” çU×[ªk×®²íåË——ÿÊ•+µ¶QRé^lÅ>|(}vss+óšVVV²!›mÚ´ÁÌ™3µ–â$[íÚµemé’`,/!ÆŒƒ;vHûêÖ­‹'NhLˆ–ìa)„P;d\ 6”;ž›7o"88Xãq…B¡óXMïöüùó1gÎ)ÙצM,Z´çÎCJJЬ×déÄcéÅfvïÞ‚‚Y²´äðóÒJ/ô¢­—)UMìáGDDDOlÓ¦MÒpC{{{\ºtIÖ+íÁƒÒgm ?]“'&&&j{]åææbË–-ÒöàÁƒe½„*=ÂJó÷÷—’Caaaøæ›od«þŽ5J圷ÞzK¶úð·ß~‹ &hâºÿ~Ùª¨ÕªU“%EK;yò¤Êê¯IIIR\€ú!¶¥)•J¸»»ãæÍ›GGG–y4nÜX¶­nÎÀ½{÷bÑ¢EhÖ¬š7oŽ·ÞzKJ–ü¹—îyYì³Ï>“†¨_óøñãZ{K:99ɶïß¿/›#x¼âpÉ•pËcíÚµèÙ³§Úcå™ïNSÝ~øAúܱcGÙ‹@Ù¿;~~~X´h‘ônóÍ7¸víšt\Óp^²ÞžÆÆÆÒƒDDDd8ØÃˆˆˆžØ©S§¤ÏÍš5“%ûRSSeÇ‹‡'ªcl¬Û¿A/LPÚåË—e½–:vì(;, çT{8€¯¯¯,¹õñÇKŸ»u릶']Ÿ>}ds´åææ¢C‡²žjÀãáª_}õÞxã Ù~???¸¸¸¨½' ’ÍÉŸ~ú©lN7]ç’ëÖ­›ô988Xe®¸?üNNNèÙ³'¦N*õä³²²‚‡‡‡ToóæÍ*I´âži?ýôfÏž-ëÕVrøhNNŽJ\!!!˜?¾´mff†   XZZ"55UVJ&ªJ'UÚ.¹X;v ÙÙÙjKjjª,ñµgϽu}_5Õ‹‹ÃÝ»w¥mÙñ+W®àÞ½{Ò¶ºßâÅfŠÍž=[úìáá///1•ì!Z¯^=­C—‰ˆˆ¨jbˆˆˆžXÉáž'NœÀºuëššŠC‡á7Þ%-ÊZ¥·¢â€¯¿þZZpcÆ *½JÏ7XÌßß_ú\2É¢­·ÔöíÛe‰¢´´4 >¶¶¶èÑ£:uê+++|øá‡²¤cãÆ¥Uq5IMME×®]ñùçŸcïÞ½?~<¾þúkéx³fÍTzj2sæL)ašŸŸ^½zaÛ¶m¸xñ"–-[†U«V!11ÁÁÁøã?d‹d|ùå—Òçììlxyyá“O>Á²eËàíísçÎIÇ  [¶äœ¡¡¡X´hÆ/õ`›5k–,™““///ØÚÚª”–-[Jõ<<{{{—;~"""ª„,,,e«ˆÿJª¾ãaaayñ €ðâÊbåÊ•¢Ä÷£J177—m'&&Jçöïß_Úß°aCט8q¢TÏÌÌLc½ž={ê‹···Ú6âââ„R©T9ïÑ£GZŸÃÙ³g…“““Öë—,mÚ´111*íìÙ³GV¯ä3*]ŒÅ‘#GdçoÙ²EVçÌ™3²ãóæÍ+36KKKñ÷ß«Ä6tèÐ2Ïuww ²óÆŒ£¶îÙ³gÅ™3gt~fÅí—ôꫯJÇ>þøcÙ±ÒïæìÙ³µþ …âܹs²s4h ŠŠŠ„BL˜0AöNh²`ÁY>T[oôèÑ:¿¯nnnjÛÈÌÌ5jÔ§P(Ô¾[%µnÝZª¿jÕª2ŸËóVúø‰JðÝËÂÂÂò´@N‰ï¶úއåÅ)ìáGDDDOì½÷ÞÃäÉ“ÕÎ16qâD\¾|YvìçŸ~f±üôÓOèСƒÊþ5j`ýúõXµj•´ïÌ™3ˆ‰‰Q©ëââ‚Þ½{Ëö 8°Ì•iÛ·oóçÏcòäÉ033ÓXÏÖÖŸþ9þþûoÔ©S§¬[¯¿þŠwß}We¿³³3BBBðòË/Ëö !´¶·`Á|óÍ7*sÝkÖ¬Ž9¢2$vìØ+Vh\ ·oß¾8zô(jÕª%ÛÿÁÀÑÑQ¥~ll,Nœ8¡5Þ²tïÞ]ú\z‘ÒÃyÕÍÁXZÛ¶mѺuki;**J6çbEúæ›oTÞ5àñ"_|ñ…ìw%66V6<¾XéÅf€Ç‹Àh{·²³³qýúui[ÓJ¾DDDTµ)Êúƒ! …b5€‰ÿm¦ !8»5=W …"€ðxÈdÉÕc+ƒË—/ãï¿ÿF\\œ4Xñ¼o§N’æn«U«š4iàñ}ºlµÝƒ¢OŸ>:?‹¬¬,üù矸ví’’’ T*áàà€¶mÛ¢C‡Z|xðà4ÜR¡PHs´ÅÆÆâøñãˆÅK/½„Ž;ÊæK,¶~ýzL˜0AÚ—Í¿W2Æ£G",, ©©©pvvFóæÍÑ£G2çsKJJÂ… pùòe„­­­#zö†*­Èüûï¿KɬA~~>6lˆ;wîxœMLLT› .6mÚ4¬\¹°nÝ:¼ýöÛÏ%Ö²0áGD†Š ?ÒÝ—#"""2@ÅÃO:…ï¾ûN¶ìĉ«D²oÞ¼y¸|ù2<(í3111ød¼ûî»RÂoÇŽŸð»qãòòòpåʬ^½ZJö‡-kKöIÿX`kk«2˜ˆˆˆ ~DDDôB[¾|¹Ê|o`mm>ø@•«V­RYɵU«VzŠèùêÑ£^~ùe=z;vìÀÒ¥Ke«š   Y/Îb&&&˜={¶Ösûí7iîÊyóæiMQÕÆE;ˆˆˆè…¦n^4sssìÚµ 66•‡7n¨$ûlll°mÛ6=Eôü-_¾J¥999ذaƒ¾Ãy¦Ô½¯ÆÆÆØ¼y3êÖ­«õÜ⡼ 6ÄäÉ“ŸExDDDTI°‡½Ðúöí‹øøx$$$ÀÎÎM›6Ř1cÔ.èQ™››ãã?Æ7P½zutéÒ/¿ü²N«ŠfÍšaáÂ…øã?d f¢Î;cÒ¤I¸wשּׂ­Ñ¸qcŒ3Fm"°¤û÷ïÃÌÌ ¯¼ò ¦M›¦uñ"""ªú¸h‘¸héí "CÆE;ˆÈPqÑÒé%""""""""2 Lø&üˆˆˆˆˆˆˆˆˆ ~DDDDDDDDD„ ?"""""""""„‘aˆˆˆˆˆˆˆˆÈ€0áGDDDDDDDDd@˜ð#""""""""2 Æú€ˆˆˆtâVüá·ß~ƒ‰‰‰>c!"ªPBˆÒ»ÚØøü#!""2 LøU Òÿ³…(((Ðg,DDÏš™¾ ""ªÊ8¤—ˆˆˆˆˆˆˆˆÈ€°‡QÕ 899ÁÛÛ[ÏáUœŒŒ ?~¼ä®d}ÅBDDd˜ð#""ªØ€··7öîÝ«çpˆˆ*Nxx8š4iRrW˜¾b!""2ÒKDDDDDDDDd@˜ð#""""""""2 Lø&üˆˆˆˆˆˆˆˆˆ ~DDDDDDDDD„ ?"""""""""b¬ïˆˆˆˆˆèù)**‚‘ÿÝŸèI)ŠZbôU¦%>OQ(ïè-ªJ~B¼ù4 0áGDDDTÅäåå¡eË–èÒ¥ Ö®]«ïp*Çcþüùøå—_àêê ØÙÙaéÒ¥úMïvïÞ+VàòåËHOO‡»»;Þ|óMÌ™3–––jÏÙ·o>ûì3üöÛopttĨQ£àææ†Å‹?çè‰*äI"])ÿ+De©ö´ ðŸöˆˆˆˆ*P`` ÆJE)**Bnn.222žéuªš¤¤$œ>}¹¹¹€+W® ""BÏQU7nܼû?>,--±dÉ 8BµçÜ¿§OŸF^^àÒ¥KR;DDDT¹±‡‘:vì¶oߎõë×ë;”Naa!rrržé5ÌÌÌÅa™¥?þ£­R©”þ[üùE7sæLÌœ9SÚž7o^ýuìÛ·GŽÁ+¯¼¢rŸ'‘n¼½½Ñºuk}‡ADUÜŽ;ššZaí1áGDDd€bbbpèÐ!­u¢££‘&Mš °°.\@dd$Ú´iƒ&Mšh><@‹-‰{÷î¡[·n€ððpääähüËQFFΟ?»wï¢Y³fhÕªU™÷ûàÁ$''ÃÃã̺啖–†Ó§O#>>ŽŽŽhܸ1êÖ­«’øHJJB\\âãã/^”ŽÕ®]*mÇÆÆ"==M›6…§NÂýû÷áããGGG•úW¯^EAA´mooWWWµqߺu ùùùððð@AAΟ?¨¨(´k×7Öx¿8þ<ÜÝÝÑ®];¤¤¤ 22&&&ðññÑþ°ôÌÍÍ …ÎÎÎ?÷Úµkk=çáǸtébccáèèX[[«­ƒììlxzz¢¨¨'OžDJJ |||`ooÿÔõKÇS«V-øøøÀÊÊJV'!! hÒ¤ LMå£ óòòpýúuÔªUKzš¼ýöÛØ·o.]º¤6áçææcccé]¬]»6ÜÜÜ´¶Iô"8p ôUq¡¡¡šðƒ‚……¥Œ`5ñ_IÕw<,,,/^^ü=4`ÀQ–õë׋ڵkk­3xð`ѰaCqöìYQ¯^=abb"###ñý÷ß«ÔÏÈÈþþþ¢zõêÂÈÈH˜šš ÂÎÎNœ8qBVwÖ¬YÂÊÊJüðÃB©T âóÏ?ßÿ½P*•¯qìØ1áèè(Hñ 8P¤¦¦j½www@„‡‡—ùlÊ#00PXXXÂÖÖVº—¨Ô]ºt©(ñÿ YùòË/Õ¶?~üxáêê*âããEƒ ¤úFFFâÎ;*õmmmeíN˜0Acì}úô­Zµ'Ožnnn¢Zµj€P*•bãÆ*õ‹ŠŠÄĉ…B¡öööÂÈÈH( éZ~~~åxrrÆ ÓZž¸ýbñññÂÅÅEÚþàƒÄòåËÕÖMNN#Gަ¦¦B©TJÏÇÉÉIœ={Ví9#FŒ7wîÜuêÔ‘ž±±±HLLT©?|øpááá¡Sýˆ#FˆjÕª ¥R)½ÿÎÎÎâܹs²º‡ÄO?ý¤rÍíÛ· bÿþýe>¯½{÷ "((HíñèèhQ¯^=i{Ò¤Iâ»ï¾+³Ý'Vú÷ÆOT‚ï^u€SÉ÷uñâÅ‚ˆèiyxx”üÿà^ñ”ßUBDDô‹‹‹Cÿþý±`Á¤¤¤àüùó¨[·.fÍš%ëI°¶¶Æ‘‘¤¤$lÚ´ 999˜4i’JÛYYYøá‡‰fÍš!00Û·oÇ7РAüöÛo²úèÓ§š7oŽ£G"%%Ë–-áC‡0}út­÷áëë‹&Mš¨í÷¤Ž;†€€ >111xøð!233qéÒ%ôèÑC¥¾¿¿?ÂÂÂðþûïC©T",,L*ãÆÓxüü| 4mÛ¶Epp0Ž;†Õ«W«íIuúôi©MM½ÐJºyó& „/¾ø)))8sæ \\\dC;‹mÚ´ kÖ¬ÁŠ+€[·n¡^½zhÕªRRRðÝwß•y=M”J%ÌÌÌ´–Ò=Õž„““‚‚‚¤íwß}C‡U[×ÊÊ ¶¶¶8vì233‘˜˜ˆuëÖ!99ï¿ÿ¾ÆkdggcàÀèÔ©Nœ8#GŽ`íڵ߽ììl 0;wÖZßÊÊ vvv–âY»v-’’’0mÚ4YÝ—_~µk×–Ýk±-[¶ÀÙÙ}úôÑxÅN:èØ±£ÚãnnnøñÇ¥í÷Þ{,³]"""ªž6cÈÂò"°‡ ‹ž ÊèáwôèQ1`À©´nÝZ˜™™Éö½óÎ;²s ìè IDAT,¬­­EXX˜lÿ”)S«ruÞxã @Ö oÖ¬Y€¸yó¦B???Y¯µáÇ ggg•x”J¥J϶‘#G ###qûömâ©(Å=öbbbÊuÞ¢E‹„R©Ô©îøñãñÿ÷¢¨¨¨\×±µµ-³‡Ÿƒƒƒô3(æïï/ˆäädÙþ‘#G ‘ŸŸ/í›={¶P*•"##£\±Ue={öÆÆÆ²çPlĈ€˜:uªNm >\ï¿ÿþÇÓ­[7abb" dûgÍš%”J¥ˆ—ö%'' ñÑG•Ùn^^žðôô^^^O[Eb?–ªTÀ~Dô °‡©¨^½:œœœ¤bmm ¥R)Û§n9xzzÊöÏ­—––¦Óµ_~ùeÀ½{÷TŽÕ¯_P«V-K½ÖjÕª…äädYÝÐÐP4oÞ×®]áC‡¤R¯^=áÂ… :ÅSQ:wî ### 2»vízf qØÙÙá›o¾B¡¨ð¶]\\РAÙ¾6mÚ=z$Ûïè般¬,Ù}&''ÃÈÈ&&&[eõòË/£  IIIj»¸¸`Ù²e:·W»vm|õÕWOO¯^½ŸŸ¯òû2vìXbûöíÒ¾;w"??þþþe¶»lÙ2DEEaݺuOU^\´ƒˆˆÈøúúÂ××WÚÞ°anÞ¼‰Õ«W—»-mC+ °{÷n:tááሉ‰Áýû÷ ¸×ƒZ¥W“-½’’‚ääd¤¥¥©2hjjŠ;wî”ç6žš——¶mÛ†>úC† ¹¹9ˆ©S§ÂÛۻ®S½zõçšP«V­šÚýƒ ªU«0mÚ4Ì™3çÎÃöíÛ1|øpçèjçÎøöÛoµÖiܸ1Ö®]ûT×)¯¼¼<üüóÏ8vìÂÃÃqçÎ2ßgKKËr­T[žúyyyرcŽ;†ˆˆ­ñxxxÀÇÇAAAÒß­[·ÂÇÇG%‰_Zdd$.\ˆ?þ-[¶Ôù^ˆˆˆ¨ê`ˆˆˆt’žžŽîÝ»ãòåË:t(ÆŒƒúõëãüùó˜5kÖSµmff###L:õ©zCU´7ß|C‡ÅÙ³g±eˬ_¿Û·odž àçç§ïð*TçÎ¥ûÚ°a †Žo¾ùæ©ÛvvvF‡´Ö)k5ÝŠ–œœŒÎ;ãÖ­[1bƇúõë#$$Ÿþùsx¼ÒtçÎ-‹çøñã T{ŽŸŸ&Nœˆˆˆ˜››ãÏ?ÿÄš5k´^')) ýúõÃ믿Žùóç?‹[!""¢Jà…Nø)ŠQê;ªÚ—øl¦P(>ÑW Tå !žÍ8@¢çìÛo¿Å¹sçpàÀôë×OÚŸðÔmW¯^uêÔÁÙ³gŸº­Š¦P(àåå///Ì;]ºtÁÂ… .á‹Õ«Wã7ÞÀ‚ P·n]XZZVHÛ:uB§N*¤­Š²téR„……᯿þ’õŽ ÓK<_|ñÂÃÃñÏ?ÿÈ’£W¯^ÕxΰaÃðþûï#((5jÔ@õêÕ1lØ0õ³³³ñúë¯ÃÕÕ›6mz&Ãȉˆˆ¨rx¡~FxEßAP•c €ÿ$NºZ€ ?zî<==1zôè m3<<ÆÆÆðññ‘ö%$$àË/¿=UûÇG`` öïßW_}µ\ç"''OCYìííQ¿~}iuSu¬­­QXXˆ[·nIsVsçÎŽ{÷pìØ1é9ÿLKÁ6ááá°°°@Û¶m¥}111X¹r%€§ŸŸ$ž5jHsh@tt4¾þúkñX[[cРAغu+,,,0xð`XYY©m¿¨¨£GFzz:<øÔô‰ˆˆ¨r3¼?½|}}±xñâ mÓÛÛ7n>Œ/¿ü;v„ h\ä@WsçÎEÆ 1dÈ|ùå—ˆˆˆ@XXvî܉U«Vi=·cÇŽpvvÆÝ»wŸ*†’îÞ½‹E‹!$$iii8wî,X€àÍ7ßÔx^Ÿ>}¤û¹rå ~ûí7Ìž=û™-úQQ5j„;wîÀÚÚ–––011R©„µµ5Þ|óMܼySß!V(ooodffbüøñ8rä>ÿüsøúúÂÙÙ¤¹óžg<éééxûí·qäÈ|öÙgðõõ…‹‹‹ÖxüüüpëÖ-\¹rãÆÓØ~@@víÚ…`ûöíX½zµTöíÛ÷LˆˆôçEïáGDDD:ú¿ÿû?„‡‡cÓ¦MØ»w/lll0}útŒ15B\\ÜSµonnŽ3gÎ`Ê”)˜5k>úè#éX‹-0eÊ”çÚÓ,22_|ñÒÓÓ¥}–––øàƒ°dÉç5lØ&LÀÖ­[±uëVÀK/½„É“'KÉ›Ê( ÇŽÃеkW˜™™ÁÌÌ 111ضmlmmËœ®*™>}:nß¾­[·bÛ¶m°³³Ãœ9sйsg´oßqqq²ÞÏÚ‡~ˆèèhlÛ¶ [¶læÍ›‡:ÀÛÛqqqhÕª•Êy½zõ‚««+LLLеkWíïÝ»ÔÎOØ£G¼öÚkw3DDO);;‰‰‰ÈÎΆ½½=ììì ²·ye‘››+MiQ¿~}½Å Å¥K— „€‹‹ õÎ3£Ð¶¢ž¡S(‡ðßÞÆãÒ¥KzŽˆˆªº¥K—bîܹ%wÙ !Rõ…BÀ  ýå]²³³qûömxzz>³?|çææ"""yyyprr‚«««ÖúÏjHo^^¢¢¢’’‚š5kÂÝÝÕ«W×éܬ¬,\¿~îîîppp¨Ð¸ž…#FàÞ½{8zô¨ÊpÏW^y÷îÝÕ+Wôݳ“‘‘»wïÂÃãRÌi—žžŽ¸¸8ãIKKC½zõðÑG! à9Døl„‡‡£I“&%wù !6ê)"­ …€øâíÅ‹Wéß¿Ê"55K—.ů¿þª2©z÷îI“&i¶Y³f(,,Œ5 sæÌÑùúùùùhÑ¢…´=aÂüïÿ+ç]<;gΜ———Úc……… ÂîÝ»…ÔÔT4hÐM›6ÅôéÓѨQ#­mÏ™3Ÿ}öLMMq÷î]ØÛÛxüB÷îÝÓx^µjÕP£F X[[£U«VèÛ·/ºtéòä7ùœtëÖ 'Nœ@›6mðï¿ÿVšd²§§'"""Š7B |šöØÃï? …fffúƒˆª8cc~­’á«^½:š6múL¯ajjŠ–-[ê\_©T>“ùûªU«V: ¡3sss´o߾슕@aa!vïÞÙ³g«$ûbccqñâE 2DOÑ=[–––ðôôÔw’5j”+žåË—#;;&Lx†Q=[¡¡¡3f bccÕONNƶmÛ°mÛ6 <7nT»°Txx¸4çiy§fB”L¶<÷©4‰ÇÌ™3±mÛ6äçç«‹‹C¯^½®²ÿ?þÀ?ü€ hLJGFFJó12DJöÀÍ›75þLJ;xð 1|øpA©Têz‹ÏݤI“pâÄ \¸p«W¯Æ¤I“ôÒ3Á¿™½à”J%zôè/¾ø&&&èÒ¥ ŠŠŠð×_aùòåpww¯ð9!éÉ8q·nÝÂ?ÿüƒM›6aÑ¢E°³³ÓwXDDOdóæÍðóóÓy±¤]»v!** þùg…­&_Ymݺ'NDFF†ÚZnn.¬’ì+)//³fÍBóæÍÕ.Š6cÆ äåå@…$¾¶oߎ† âÓO?}ê¶ž•AƒÁÉÉ ˜3g†Žš5kê;¬ W9ú-‘^íܹï½÷Ö­[‡®]»âõ×_Çž={ðé§ŸâÏ?ÿ4øù|ª’þù³fÍBhh(V¯^]©†œ•GLL ¦L™"Kö5jÔ7nÄåË—‘€Ã‡cÒ¤I²i.^¼ˆwß}·Bc166ÆòåË¥Ræ6 AFF†Æã¿üò NŸ>-m÷èÑ@DD>ýôSY’pÆŒ*燇‡K 75iÒ;vÔx-'''çá~ÎyŸóyŸÏ=}ÜÏûó>ïw[téÒß}÷²²²;wîÄøñãÕN°TØ<5440nܸ"éÿ)×®8®û™3gÇ¿ýölllÄ勌ŒDxx8 >>‚x+V¬€$¯ƒ¯¯¯Ês•+WíÚµSØ6hÐ Ô¨QƒÇû{óæ nܸfÍš©³4×^—.]x¶µk×bâĉ_ô6ä<ü‚ ‚ ‚ ¢Œ†víÚ¡]»vèß¿?c˜4i,,,P«V-Œ1)))‚>×®]ÃðáÃQ»vmèêêÂÄÄÍ›7Ç„ ðìÙ3¹sôîÝ›ŸC‘7Ô˜1cx»"Ï» 6ðö.]º ??·o߯ßÿÍe ±qãF¥Ût[µj%7öªU«T^›wïÞaÒ¤Ipvv†®®.ìíí1xð`×Mâ•W½zutèÐ 4@óæÍ¹±OB‹-øgƘ .á‡Â;wî\¨þÊÐÔÔ„ .=]qÞÂ[·naôèѰ¶¶†žž*T¨OOOŒ;ÉÉÉròC† áó^½zµ\ûäÉ“yûÀåÚÿýwÞþÝwßñíË5‚…… )) GŽ)ê´¿|cßlpÀlmmAħÌ$÷•ÿ• ì ¸ßQ)û@œd]ùúú2‚ ˆ¯‰;wîÈþÿ9}÷^*TæÒë588˜•&«V­âºÔ¨QƒM:Uð÷dhhȲ²²cŒåææ²éÓ§3 Ù¿9^*V¬ÈöìÙ#8Gÿþýy{Æ moÞ¼aZZZ¼]SS“½~ýZ ãääÄÛýüücŒ-]ºTpÞéÓ§:×çÏŸ3±XÌûT©REÐ.=¯öíÛ3{{{¥s<~ü¸ ovv¶@fòäÉ‚ö¼¼<Ä455Ž©¡¡ÁfΜÉrssåô~ñâó÷÷WzÍ0WWW–‘‘Ácìüùó*e׬YSèµbŒ1ÞÇÀÀ€åååñ¶ˆˆÞfdd$h“P½zu.cee¥ò\nnnãããíùùùlþüùJ¯ŸDmÛ¶ ú5Š·ËÚm²³³™¾¾¾`Œ§OŸ d<<}ðúõëBÏ)aóæÍ˜;w.òòòx‹‹ ÏnŸŸŸyóæaíÚµr}G…ƒòc===Ô­[W°õÊ•+èß¿¿ÚúÆÓ§O[~7n ÿ78q‚vtt´c HJJÂõë×ñÃ?b Ö®]µk×ôY¾|9f̘!¸~åÊ•C¹råøñëׯ1pà@ìÚµ‹×I¯½øøx|(8Ÿ"^¼x¶mÛ ±sæÌÈHo§vtt,ô¿(iAA” ø/ðèèhøûû—¦.AÅŠ‚,”Šä‚(œV­Zaùòå///^¿aÃwÕæÍ›Ñ¡CÆ=z 33ƒ¤¤¤`ݺu˜6mÊ•+:tpüøqôë×€b£KDDÿ,­U«V022nL”P©R%µæ'+§,¬‰‰ Ö®] YxW¯^ÇscãÁƒ±`Á‚BϹoß>aqÍš5Ü;¬råÊX»v-<ˆŒŒ ¤¤¤`çÎÜÐ*óÐÀÀëÖ­ƒ¾¾>ÀÂÂK–,Á¸qãàèè”+W:::°´´„¡¡¡@KKËBuMKKCëÖ­”}úô‡‡‡@.))‰–5¢~,æææøý÷ßѦMAý¦M›ÆÇ_ýUð[Ö××ëׯG@@ŸÃêÕ«±`Áhii¡S§Nعs'€ƒŸ$–cQÖ^Ó¦Mann.•½ž=Rë—ÈàGAeƒŠ’OŸ>å?¸ ‚ ¾R¬J[‚(« 4HaýÉ“'ùgKKK…I €qãÆñ̹Ò[qýýýùï'N ??ÏŸ?熥š5k";;)))¸~ý:ÒÒÒ``` 0ÊHo©”5f%''£jÕª…ÎïñãÇ‚c‰Q¹sèééÁÇÇ;vìPày–ŸŸ_èvÖ»wïòÏšššÐÑÑA\\œ@¦fÍš¸yó&Ú¤û:;;ÃÄÄDÐÏÝÝ/^Ty~uIOOGÛ¶mqýúu^çêêŠ_~ùE Ç$g‘öìT†H$‚––Oz!MýúõñÛo¿ÁÝÝb±¼™IzíUªT ½zõ’“éÞ½;ÌÍÍy‚Ùµ'1ø9s999ÈÊÊÂåË—ù˜†††xðàœœ AÒÙí¼ŠæýâÅ •× ¬A?¢ØaŒ!::§NBrr2^¾| CCCXXX E‹ðôô„–––ÒþÑÑÑxðà€—oE˜ªˆŠŠBbb"@WWWÎm·¬ÏÝ•­­­Ñ±cG•ò¹¹¹øþûžŽN:qwõ[·nÉÅÇFSSúúú000€••ªU«V|“(AvíÚ…}ûöA__[¶láoî‚ ‚ â[¦V­Z ë%ÏHP·n]…2°´´Ä7€ÿ :u‚X,Fnn.^¾|‰Ë—/#!!··nÝ999رcc8{ö,ŒŒŒ¸ž¦¦&|}}¹¼¬§Þýû÷áææVèü$ÏŠÌÌÌÊU¯^]a½´—Wvv6ž­ÆÆÆðööƹsçРAž4å÷ßGjj*Ž=*×G]ç ekO__mÚ´ÁáǬ=éµÜªU+˜ššrƒ_DDîß¿ÏÛ]]]É^£(z–ÈàG‹-BPP`o¾"žLœ8IIIü?@U,_¾\]¨0ž>}ŠY³f(0„¹»»¬ªxüø1ñäɵâY”öööðòòBdd$‚ƒƒÑ¯_¿¯*ÞA( @h×®¶nÝZÊêA ‚Àõ¢KK‚(ëHÙ¤±°°àYi¥·™J“ŸŸ/ðÆ’MæàïïÏ ~ÇŽÓ'Ol÷lÕª• F`DD„À‹LÖéA6ÎÛöíÛñã?¢^½zJçöÓO? ŒSmÛ¶U*+ë (AÚ`h`` Vì@+«ÿ2 ¯¯ÔÔT•ÎÒ†$\½z@Ás›"ÂÂÂ`ii‰ºuë*5:)3ÊåææÂ××W%×ËË ýõ—RãoÅŠ¡©©É¿/Ù*ê`bb‚´hÑ‚sìØ1¬Y³cÆŒÈZXXðµ"íñ(‹´YÑÚ“üŽ;&Xk­[·†‰‰ ŸSDDLMMy»2‡ÙøÒ}¾ÈàG »wïÆ´iÓuåË—‡‹‹ êÕ«‡¤¤$\¹r…»çÀ0tèP¾¿¸(W®ã ,žÃç$**Š- Æ/^,w- cÙ²eüf5jÔ(•²õêÕã7~Ærrr““ƒøøxÁMSbDSæîÿ%0räHDFF"++ sæÌ!ñµÃÿ@uttJtKAÄç&==]¶J>HAj¡,|’§§'îܹ ÀvôèQž´CÂŽ;F'''A»ŸŸ7æH˜êׯϷÖ:88àæÍ›ˆåí"‘HÎèbgg‡6mÚ <<@ÁVÙ>}ú`ÿþýPðܲbÅ 8p@PÿÃ?(¹ À… œœ,Yôöí[A\7Ùó(ÃÆÆ†~÷înß¾† òºÌÌL¬]»vvv°··d–6ø]»v wïÞŒwþüy¾+MSSsçÎåm¥·ùùùÈÍÍ•Û2;mÚ4³H“&Mpüøq•Ž5033ãHéx~EÁÓÓAAAÜù&OžŒ-Z¶={zzrïÃgÏž!44Tîyÿþý{ìÚûî»ï¸AOzmÕªU‹ocoܸ1ñ%m¸V¶kP«RÂW÷ûš1öÍÇ0ÌÖÖ–Grr2366f’k €uéÒ…¥¥¥ ärssÙœ9s˜X,Èîß¿_ 7xð`Þfhhø9§R"œ?^0ß;v(”‹‰‰a^^^YIéÖ­›Òñ_¿~ÍÊ—/Ï0±X,wÝgΜ)+22Rá8·oßfÎÎÎÙ©S§~üÄ?™™™¬\¹r ÓÑÑaÏŸ?/m•Xpp°ì÷W}÷;*e¿ˆ“¬+___Fñ5qçÎÙÿ?²/àÞK…Š¢À\z½³ÒdÕªU‚¿Ÿ(”»pá‚@®råÊìØ±c,??Ÿåææ²}ûö± *ðöªU«²ôôt¹q5j$÷¼2~üxÞ>a¹vwww…:ÅÄÄ0===lùòåÙŒ3XXX»xñ"[·nkݺµÜ˜C‡•OCCC ãààÀ’““cŒegg³^½z Ú—/_Îûfgg Ú&OžÌÛž?Ο;$óyñâoŸ6mš ï¦M›xÛÉ“'mnnn,))‰1ÆXll,kÖ¬™ ýêÕ«¼ï”)Smqqq,##ƒ?÷DEEÉ]—.]º°~øAa‘œ—1ÆZµjÅû|ÿý÷ ¿ŸêÕ«s+++…2¹¹¹ÌÕÕU ƒ³³3ËÍÍ|Ï"‘ˆ·W¬X‘:tˆååå±¼¼ÛµhÑBå: ’k¯_¿¾B½c,""‚ˉÅb–™™©Tös`kk+­û!ö‰÷*òðûBÈËËì·¯W¯tttŽøøxx{{ÃÅÅEÎm8;;§NBBBÞ¼yƒzõêÁÑѵk×–“}ðàwÕÕÕÕ•³˜¿xñBtÕÙÙYàNœ““ÃßL–ôÊ•+cúô邔꾾¾Ø¿¿Ü555„òåËcüøñ¼~Á‚^Ó¼P IDAT…Æ´ËÉÉÁ•+WpñâEèëëÃÙÙ... ‚&$$ð`›ZZZ‚7/Ò¤¦¦âßÿÅõë×abb‚ ÀÁÁA­-Æ?ÆÍ›7y2ŒêÕ«ÃÓÓS.áELLŒàíPŒöâÅ‹000ào=V¯^-x;¥¡¡‘H$ð¸SƶmÛø›‰fÍšÁØØ¸Ð>а³³Ã„ Я_?®ÊøðáNŸ>»wïâõë×°³³ƒ££#êÔ©# !11‘¿=ÒÖÖ†‹‹‹ ýÕ«WˆçÇŽŽŽ(W®?–ýû¨Y³&ªT©Â³l£>{öŒµoß^e߆ Ê=£?xð@áw5räH–™™É´´´ ½fÒåÊ•+|liï6}}}–““#§·:~dŸƒôõõÙ£Gx{nn.›2eŠœ¦t)_¾<Û¼y³Òs$&& äeŸ-?|øÀ 2ׯ_W:^ÿþý¹Ü˜1cTÎïs@~ßÓ¦MÕ+Wuþþþ öîûúú⯿þRÚ_’1'$$;wP` =z4cÜ;K:ƒí©S§cDDDb:9r„®Zµ*ÜÜ܃—/_òúfÍšÁÖÖVåÜtttЭ[7¬\¹’×ýý÷ß ûeeeÁÕÕ•—•&::ÎÎθuëV¡iÔ¥éÞ½»BD  XiÇŽ±dÉLœ8QÐöêÕ+4lØ>TØ7''ëׯGjj*öíÛ'çåVºººèÛ·/æÏŸ/÷&G™™™8þ]å³}50`ÀžÜ£F‚ïQ,cøðáÜÕÔÔT.ù‡4çÎ㟠Ë]&ùT‹aY.ø‚=ü$ÅÕÕ•ÕªU‹ÙÛÛsÙyóæÉ½™œ×¿zõŠijj ÆuqqèX«V-Þ6zôhÆcýõ— Ï€Ôšïš5ký¤cÅI{øIJ¿~ý؉'Ø¡C‡˜···Êsªòð;xð  ¯[·n[·n«W¯¯×ÓÓcOŸ>ô5j” ¯ŸŸÛ±c[¸p!«[·® íÀŒ1Æ&Ož,÷&ÇÓÓ“ 0@à±ùï¿ÿÊy½©ãáwäÈÁÛ é7kd=ü ÄfÍšÅf͚ŦNʆΘ½½½@ÎÌÌL.fÇŠ+2FFFìÇd«W¯f ¼­144T#¢wïÞ¼>33Sî  à¼¼màÀ‚¶ÜÜ\¦««ËÛ·nݪðZ}.ÈÃJI‡A_1äáG¥,|a~ŸÊ‡X^^^i«!G^^^±ÆTûðáƒÊö””Á}håÊ•…Ž™••Åòóó?Je^ŠÊøðáÃGŸK'Näó7o^±Ž­¥¹öâãã•>–ÅíáWê7ËÒ,_ºÁï·ß~ãíãÓ›7o˜¾¾>—©\¹2KLLŒµcÇÁ8={öämÒÆˆ5jðúÈ_CCƒ'€uÛŽˆˆ`Œ1¶uëVA}PPZó•6T­£²¿Aƒ úæää¶øjhh Kª ~VVV¼­aÆ‚fnn®Àè%íÒ{ïÞ=A´W¯^‚q>|È·kkk æ£nÒYÔ1ø-Z´ˆËXZZ*”‘5ø©SLLLØÉ“'ã¼ÿž™˜˜pcccvïÞ=Œ¬AµS§N¼MÚÈkjjÊoì²Al%åÉ“'Œ1Æ’’’õþù§Ü¥×C`` Z×·¤ ƒ•’*ß²Á/))‰;vLíý·nÝb©©© ÛΜ9SœªQLÁJY*_›Áï[çåË—,!!M:UpRµ­ùkáîÝ»ÜaÈÃã´Õù¬¬\¹’× ,(mucÅoð+ÚžCâ³!që• I}àÀ¼{÷Ž×Ïœ95jÔôíÛ·/Z´hÁ=*ù‰o €¤¤$ܾ}€pK¥¾¾>€‚­Ã‘‘‘| •*U‚§§'€‚m ÒȺA+CÌS‚l‚id·ÖjiiñàŸ=ec("%%<àÇýúõœWSS½zõâÇþù'ÿ|ùòeA Ùôï–––Ç­[·ðöí[lذ¡P}ŠI" ƒš;;;üðȉ‰A›6mmGŽAZZ?ž2e êÔ©#ñóóÃwß}ÇOž<ÉׄŸŸ¿Þ/^¼à[Ö­=8sæ áÚ344”Ó (pÕ– ILCÄ×ÃÑ£GѾ}{¹ÀÖŠˆŽŽFóæÍqá¹¶÷ïßcðàÁ?~<ÿ‘ ‚ ˆo›ŸþuêÔá %$˜™™•’FŸkkkøúúΟ?Ïíß›6mT¬X£F*emJ2ø}¡899)Œ'ûزeK…ý%9  ã$뎭­-ìììxÛñãÇ.ãÆãŸ%Féj¾¾¾<¶@ÅŠçU•ÕUiÛ¢q$hjjÂÆÆF®^6nƒ:ç½{÷®à844={öi#ß“'OŸŸ¯°¯$Ž4^^^°··—‹õP’Hü$Ž cøðá˜6múôé#È„+‹1jÔ(¬\¹RaÜ„Y{999<ûnµjÕàêêÊÛ [{’ìKÒk¯cÇŽ‚ÌѤç.ÉÐLß:iiißÔ6 à^ݪU+Lž<™ÿx•FWWáááØ³g¦OŸ^ Añ¥!;ÜÝÝÏ5_3K—.…¶¶6à×_-em>gΜá1&ƒ‚‚P¡B…RÖ¨d ƒßв€˜Ò 2€‚äŠMú hBÚËïØ±cHJJâ-kkk|ÿý÷¼=""‚Ä]»våŸeƒ¯ÊÆ”!+gmm­PNOOb±|nY¡:^]’ÀžþùçìÝ»WP.]ºÄÛ?|øÀ½I?~,è+”¶4‘ö¸“6Þ©¢OŸ>X¸p!vî܉˜˜nÜËÍÍŘ1c0`À…ž/%±ö^¾|ÉS¡W©RcÇŽå^€ÈÎÎæFg@¸ö¤‘ö ”ö€%ˆo™ƒ¢mÛ¶¥­Æg#// €——&Ož¬T®N:غu+/^¬Ð ‚ ˆo‹{÷îñÏðññÁþýû¹ìk§N:;v,`ÇŽxóæM)kTò¬Zµ `ccƒ‘#G–²6%ü¾P”Ý\$[{%¦N oooXYYÁÍÍ ,P"ã—_~ÁСCÛ·o‡\\\¨pËóš5kxˆíÛ·£mÛ¶*å0eÊ>ÁÁÁrúœ9s~~~ˆ‰‰‘ãáÇðóóáC‡Š|=‚ âs“€û÷ïãÆxõêŽ?^hFÚ¯9sæ ** 'Ožü&ž̘1QQQ8uêÔgÝ¡÷¹!ƒßŠ2ƒŸ¬GÝßÿ­PNÚkÁÈÈHçÏÕÕÕªUPKoáÂ…¼­uëÖÀ½Bc˜3goÿî»ïº‰D"ôìÙSpî‰'âýû÷Jç6sæLÁ[777…[d%üóÏ?ru²ÛÔTõ— {í¼¼¼°hÑ"•E’ŽÞÖÖVÐ÷ÆrãÿðÃhÑ¢Fõë×ó¥l|BÙ¸‡Ÿ‚´NØVŠhÖ¬™à;€iÓ¦áúõ낺Y{:::‚-ÙuëÖå×2??_pÞV­Z€ FßìÙ³ùg'Ÿ4Òë©8bÄ·Ä7pôèQìØ± ÀÛ·oadd„Ç˽(ÉËËC»ví0iÒ$dgg£[·n¸xñ"<==!$¼yóööö¸páš4i‚ž={âÅ‹˜5k&Mš$'…–-["::ݺuƒ¥¥%Fމ;v:7ÂÓÓSî^¥Œ¾}û"66QQQ…Ê2ÆðóÏ?#$$qqqj¯ééé¨W¯¢££Ñ¬Y3àÙ³g˜1c~úé'…}®^½Š'N`Æ øþûï‘ }}}9rDaxŒ«W¯âäÉ“X¿~=†Šœœ¥òiii°··Ç•+Wàáက¤¤¤à§Ÿ~ÂÌ™3²ŽŽŽ8zô(6oÞ,wÎ]»v!,, õë×ÿ„«CAŸ‘H+++888(Ü]ö-P®\9¸»»ÃÝÝ]íPQe™FÁÝÝ]-Ç¡2ͧfý(Ë_p–^eÙnÓÒÒx6XÌ‚%'' döîÝ+«oß¾rãŒ=ZaVÞ—/_2Æä³­JÊäÆºÿ>ÓÕÕÈ9::²ØØXÜË—/Y·nÝäÆ”Íº*›¥×ÓÓS™1++‹ÙÙÙñvöìÙ3Þ®,Kï‡XµjÕ:æææ ÚX:u˜¯¯¯ ÛV\\ÓÐÐà}›5k&H£ž˜˜È´´´:Kˆ‰‰ÌG:û²*ÔÉÒ»xñb.S¡B…2²Yz###ådòòò˜§§§@®^½zìýû÷\æíÛ·ÌÀÀ€·W¬X‘=|øP0Î_ý%ÃÏÏOî\²Ù¯$åñãÇŒ1ÆÂÃöÿþûïJ¯•‹‹ —›={¶R¹Ïeé¥RR…dé}ùò%»ví/AAAÌÌÌLPwûöm¹~L[[›YXX°K—.ñzI&miÖ­[ǰ]»vñº¬¬,fooÏ,--÷ )))ruõêÕc:::‚{0cŒyyy± *°ðºÇó{·ì=GB||<À–-[¦°]L$±Ñ£G«%ñâEªöøê"{}òóóY:u˜¾¾¾ “¼„Aƒ1===VµjUvíÚ5^¯èûbŒ±rù˜˜˜BåéS«V-fhh('ëïïÏ*UªÄ>|ø ¨¯[·.óòòR8>A(ƒ²ôR)K”¥— ˆ€²ô~ã ²÷þ÷ßhР~úé'¬_¿ýúõxÜUªT ?ÿü³Ü8Ò[+%899ñí-Z´{»¡¯¯víÚÉõ³²²ÂÚµku7nÜ€½½=jÖ¬‰Ž;¢~ýú033Ãü!6l:uê¤rÎç΃··76n܈x{{ãÎ;¼}̘1jeP‹Å‚¸N7nÜ@»vípæÌ\¸pC† ÁÍ›7‘€°°0ט­­­ ¶á… àáá+V`êÔ©hÖ¬™`»“´¬¡¡¡@µk×báÂ…‚-ÃK“&MøçôôôÞj¦¡¡­[· âÞ¾} ,àÇúúú3f ?~ùò%œœœ0yòdlذƒFçÎy»‘‘~ùå¹s)Z{666ÜëÔÃÃzzz‚v---Aö_iòòòëÁÍÍ­°éÄWÉÁƒáììÌËܹsñüùsAª¿£}ûö¡qãÆ¼NQâ¨Õ«W£zõêèÑ£¯ÓÕÕÅàÁƒñèÑ#\¼xQ®l\Oèܹ3²³³[†‰¡C‡¢V­Z¼¾Zµj˜6mšÊ¹K’Õ¬YS¥œ4†††¨X±¢Ú{nnnèÞ½»Úã«‹ìõ‰Døî»ïðîÝ;¹ rssÆC Š¿/iùÇ£Aƒ…Ê+ÓçÍ›7rñrˆÔÔTœÒ˜››£aÆjoaa—Ò'11‘__é{-tèÐfffعs':tè `;¯A‰G ‚ ‚ õ!¿2ˆ¶¶6NŸ>Y³fÁÈÈH¡LÓ¦M£Ô›C,ËyÖIb¨IŽ¥(Ï*aôèѸpá‚\?YìííqèÐ!¬_¿¾Ð™–––¸xñ¢Âooo\¹rEÎÄØÿ•Ÿ§««‹cÇŽaÈ!rmy???DDDÈkjjŠ›7o¢OŸ> uÕÕÕŸqãðûï¿ Æ‹Å˜={¶ÜëóçÏ•­WÁCºtFÛa̘1ðòòâÇ999>|8¿¦ššš8zô(‚ƒƒ•&ÇpuuÅÕ«W@ÒH®±4Ÿ²ö®\¹Â?»»»+ŒaEßzzz077çÅÈÈ‚:Ù˜|E!==999ptt„ŸŸŸ ôíÛÁÁÁptt”ë÷ÇpïþýûcëÖ­rYÓàÕ«WðQqc$†Ì¢ÆHÍËË+õX={öì,--1pà@lÛ¶M鋲ÏAHHêÖ­ËõÙ¾}»R}Äb1úô郰°0¼}ûùùùسg”Æ\%‚ ‚ >äá÷… ¯¯ˆˆ~lii©R^bDš:u*Nž<‰¸¸8¼yóõêÕƒ££#ìììTnï 2Jo¹‘õî „··7?Vg«¤››Nž<‰””DDD 11iii000@•*Uàé鉺uëªcòäÉÜCÎÔÔUªTÁ•+Wƒ3gÎ@,ÃÝÝ®®® ½>¤="=8–/_›6m¼yópüøq$&&"775kÖ„‡‡ììì”êV¡Bìܹ³fÍÂõë×qëÖ-èêêÂÊÊ J³95 ˆŒŒDjj*àìì¬2Õ{XXß&¬jËò€øÚ‰ˆˆ ô>xð`AfeEäD"þøãÄÆÆ ê³²²øv_ L:&L@xx8îܹƒôôtØÙÙÁÑÑõêÕ+ÔgÆŒèÖ­?–ÞF(ÑÙÕÕ•K–%<<œîß¿¿ÊóññT¬XFFF°°°ÀÔ©SÕêsæÌôèÑ;wÆ¡C‡`mm   ¸$3°‰G°ì¶Qulå}ñâE‘ú¥¦¦–ªWðñãÇÑ»wotíÚGŽá G6mÚÄ3ñ~NŽ=о}û¢{÷î8vìOˆµnÝ:Œ9RaŸbÅŠ8xð ªU«†'OžÐv^‚ ‚ ˆ/2ø}!hjj Œkꢫ«‹Î; b§©KÕªUn¹”P©R¥Ò (ØnÔ«W¯êkkk+—W$ñTª`Œ bB©2”U©Rå£J¬­­amm-0Z†£££Jc›,²[攀ñãÇãÕ«W¸párrr†DKKËB ÈÒ¨û½kkk£cÇŽ‚-„ê"ñ6R†‰‰‰Z:0Æøöb¹ŒÑA/ÎÎÎ8vìÒÒÒ”zùJ³oß>hhh`Æ ‚í¶’í»ÒØØØ@,#""B€B@i ¡¡„„õ&‚‚piii‚8xŸ›}ûöA[[6l€±±1¯Wt}>¡¡¡<$ƒ´—û½{÷”öqtt„³³3vî܉êÕ«ÃÚÚŸC]‚ ‚ B´¥—ø*xûö-úõë‡&MšâU©“Ì£¬£§§‡(ðÄûóÏ?KW¡ÏÈùóç¹7P=Ìñ­Ó»wo\»v­XÇ Æ«W¯Ð¿dddÚòóóåäutt››‹[·n>|ø€Å‹cÑ¢E„Ù•*UB×®]±wï^ÁVýÝ»w+L>%MÕªUѲeK:tHí¹;v ùùùrÆEeìÝ»óæÍS8ÏEƒõöíÛ Â(ÌŸ?Ë—/ <†_I¡££ƒ÷ïßóD&999˜3gV®\ x÷îÂ~ÄéÓ§JÞ}AA_dð#¾ ®^½Š;wâÒ¥K‚úÒôÜøœL:•o_.,Ê×ÄêÕ«xºÎ›7¯”µ!ˆ/ ==½"'±( www¬^½§N‚­­-zö쉞={ÂÞÞUªT‘“:t(tuuѺuk4iÒ•*U¯¿þŠmÛ¶€ yÌŸ?*T@‹-àïï&Mš`„ 2dH¡º1W®\ U±aÃ4oÞ\e Œ1Œ9AAA¸~ýºZã«Ãðáá­­æÍ›óë³yóflÙ²€üõ)iFŒmmmxxx iÓ¦055ÅöíÛ±iÓ&•úôîÝÈÌ̤РAA_´¥—ø*Pô€çéé‰iÓ¦•‚6Ÿ333Ìœ9“&MÂÙ³g {{ûÒV«Dyüø1<?~Ø¿‘ÂJA("%%Eúðã³íý‘tFÓo ‘Ht€P7N²……({DFFbß¾}HNN†¥¥%š7oŽV­ZÁÐа´Uûläää oß¾ÈÈÈ@=¾úmU¡¡¡Ø²e ´µµ±k×®/æ»^´h‘¬¡Ù˜1–^Zú_"‘(€-P`ô)ÊöÕo…çÏŸÃÕÕ:uÂÚµkÊ\¼xm۶ŲeË0lذϬá×É¥K—àîG¢}ûö¥­QF‰‹‹“õ¸ÄÛVJê„JD"‘#€âsù&‚'…1¦øm«š‡ñUàåå//¯ÒV£TÑÖÖFhhhi«ñÙ@@@@i«AÄ„™™Ο?6mÚ`÷îÝrÉ£Þ¿,Y²„Œ}ÅDrr2~üñG899¡]»v¥­AAñ?ÈàGAÄWC5pîÜ9…^¿ºººˆŠŠ*RærB13gÎÄï¿ÿާOŸÂÂÂýõD"Qi«EAAü2øAñU¡*C;ûŠ‡Þ½{£Zµj(W®zõ걘~RñM‘&}0vìØ¯>œA%¿¿?=z$9¼ù©ãѯ3‚ ‚ ¢HØÙÙ©•ᘠâ+%Wú J•*prr*-]‚øJÐÑÑ‘>ÌüÔñ”§°#‚ ‚ ‚ ‚ ¢ÌA?‚ ‚ ‚ ‚ ‚øŠ ƒAAAAA|EÁ ‚ ‚ ‚ ‚ ¾"ÈàGAAAA_”¥— ‚ Êaaa‰D¥­AAA_(ߺÁÏQò!>>ššš¥© A_Œ1Ù*C饠 AAAñò­ü´¤òóóKK‚ ¾^(tAAAñYùÖ ~AQVØÀ¬´• ‚øLÜ,m¢4‰ˆˆ@vv6@GG-Z´P) ‘H„F©5~NN6mÚ„:ÀÒÒ²HºÝ¸qiiiðöö.R¿Ò$;;›7oF§NP£FÒVQQQÐÑÑ‹‹‹R™gÏžáÚµkü¸fÍš°³³ûê•9233±uëVøùù¡jÕª¥­Î÷nðK` †††èСC)«CDYçÎ;¸qã†tUNiéB|]0Æ~.m‚ ‚ø<ôêÕ Ïž=˜››ãéÓ§*å'MšMMMœ9sF­ñÏŸ?ÀÀ@Œ5 ¿þúk‘t[ºt)"""œœ\¤~Åž}ûðáÃôîÝ[í>gÏžE`` îÞ½‹•+Wª” E^^zõêõ©ª*å‡~@¥J•päÈ¥2çÏŸG·nÝøq`` ~ùå—Ó©¤Ø½{7455Pbç8qâFÿþû ,(±ó”5¾uƒßC6`aa={ö”²:A”u-Z$kðË*-]‚ ‚ ˆ/ƒÇ#??5kÖT»OïÞ½ñ믿BC£ø#Ä4mÚË—/GÇŽ‹}ì’fëÖ­x÷î]‘ ~^^^X¶l:wî\¨ì–-[]¢?uð÷÷GVVÁ£„©©i©êò)lذ:::%jðkÓ¦ –.]Š®]»–Ø9Ê"ߺÁ ‚ ‚ ‚ J”   deeÉÉD[[*TPKÖÈȨH†A]]]L˜0Amù²Žžž~üñÇÒVƒcdd„òåË«”ÑÐЀ®®.@$}µÊ,˜8qbi«ñÅA?‚ ‚ ‚ ‚(ÃÔ®]Œ1•2Ïž=ÃðáÃuhÓ¦Ò>!!!زe ž„““lmm‘˜˜ˆ{÷îaÊ”)‚9}‰PöH‚ ‚ ‚ ‚(Ã4jÔ 6T)#‰ «« ]]]äåå!,, =R*ÿ믿¢OŸ>HKKCŸ>} ‰àããƒëׯ+”OJJ‚‹‹ Ö®]‹š5kÂÛÛ{öì³³³Âó=z€››Ξ=‹6mÚ@GG‹/Æ’%Kääµµµ¡«« MMMîý&)м¥çûáÄ……!))Ié|µ´´øX²ãËzØYYYáàÁƒ “gÍš5ˆŽŽ†Òs5nÜÎÎÎJÛ?…¸¸8ì޽˗//Ô\Μ9777\ºt žžž˜8q"œœœžž.gìþÿûRt=•y£ž|ÈÄb1ëÙ³§œ¼‰‰ ÓÖÖf›6mâuùùù¬B… ÌÙÙY©îíÛ·g^^^jÍSB\\À¶nÝZ¨¬óööV)“——ǪV­ÊÚ·o/¨OMMeb±˜Mž<¹Hú† T[~ïÞ½ìÒ¥KŪC×®]™™™KKK+R?oooæã㣖lÍš5Yùòå™»»;{õê¯ÏËËSÙïêÕ« Û»w¯R±XÌôôôXHH¯ËÎÎf::: ד£££ÀN”““ÃLLLXïÞ½ÕšËÇ`kk+ýyˆ}⽊<ü‚ ‚ ‚ ¢ ‚³³3/‡Ɖ'uAAA¥­¦R>Œôôt̘1åÊ•ãõ-[¶T˜!55{öìA×®][5---Ñ¡CüñÇøðáƒ\¿~ýúaÈ!üX$ÁÍÍ )))Å<£âECCýúõCxx8ž?ÎëCCC‘››‹Aƒ•¢v@@@7n\¬cjkkãýû÷<{tIajjŠ“'O âWWâšáÇ ’±hkk£aÆ ×Ûÿý~¬¥¥…Þ†_*dð#‚ ‚ ‚ ˆbdÕªUxúô)/=zô€¯¯¯ nÕªU¥­¦RîÞ½  `«°,šššruñññ`ŒáÒ¥Kpww”k×®!77÷ïß—ë§ÈSµjUäææÃ,J–"77{÷îåu»víB“&MP·nÝRÔ¬d˜6mD"êÕ«¬]»iiiÅ~///û¸€âõfaa¡p½5mÚýõrrriiiˆŒŒ,±­Ø%%í ‚ ‚ ‚ ˆb¤|ùò‚,¬zzzssóÒR©H¼zõ Í$+AâåÖ¢E Ar iJʈSZØÚÚÂÝÝ;wîĘ1cðèÑ#DEEaýúõ¥­Z‰ààà€{÷îaýúõ C`` &Nœˆ1cÆ`Ñ¢E_]&á¹sç¢U«V°²²BûöíqøðaT¬XsçÎ-mÕÔ† ~AAAApªV­  À§(›ª,VVVwww :´Duû’8p FŒ{÷îaß¾}ÐÓÓC=J[­£R¥J˜1cf̘˜˜Ì;K–,AåÊ•å2æ–uàçç‡ððp¼}ûcÇŽÅÈ‘#abbRÚª© mé%‚ ‚ ‚ ‚coo  3«,Š2îÚÚÚB__;wî,iÕ™™™_Äø={ö„®®.víÚ…tíÚUm¯È²Ž““öíۇʕ+#22R¥lI_%AÿþýqãÆ \¾|»wïÆôéÓË”± ƒ_‰²víZAì‚K—.©”ÿ믿àîî^¤ ˜:uB³fÍŠ¬ÛåË—áîîŽË—/¹oiâããooïÒV°ÿ~¸»»#==]¥\Ë–-ùhÙ²ågÒ®lâéé‰:”¶AAQ¬¬ZµªLmõlß¾=jÖ¬‰%K–àÅ‹€üü|Lœ8çÏŸ—“×ÓÓìY³‰   äçç Úe?'''ÄÄÄâÇeeeëøW¯^<ç)ßÈÈ~~~X½z5bcc1xðàbÓãcÉÏÏÇܹsZ¬ã¾ÿ^®.22Ïž=Sº(¸žÑÑÑxûö-¯+Îï«$`ŒáèÑ£xýú56n܈%K–`þüù˜9s&6nÜXâ‰KŠ 2ø• III‚ ¥FFF*åSSSqéÒ%dgg«}Ž÷ïßãÝ»w’ôðj“‘‘K—.áõë×EêW\üñÇÐÕÕUøvHïß¿Ü(” ]]]$%%}¤†…óìÙ3\ºtIa¶)i<==áíí>”9«„;v@WW·ÄolÙÙÙj}¿AAQ–(_¾|¡Ïƒ_šššøå—_ðèÑ#8::¢G¨[·.Ο?öíÛ+ì3~üx 8óæÍCýúõ1hÐ |÷Ýw°´´T˜Ù÷céܹ3rssagg‡€€ØØØÀÖÖ¶ØÇ¯[·.`mm ;;;¥òƒ «W¯P«V-4oÞ¼ØôøX®_¿ŽY³faĈE¶¨bÞ¼y¨T©|}}1~üxî¬akk‹)S¦(í×¹sgdggÃÆÆÝ»wG:uРAƒbÓ«$‰DX¿~=RRR0cÆ L™23gÎÄüùó1lØ0T­ZUaš/ ŠáWbcc1}útìÛ·ZZZj÷[¹r¥Zr’lGв)ãÔ©S`Œ•¹™ùùùÈÎÎ.ò èìÙ³%:~Q|Ob±ê?£9sæÞ¾}˳]•5òòòŠdˆþX.]ºTæÖ2AAA”5Œ1kÖ,4lØP©L§Npúôi„„„ -- ãÇG¿~ýއÊÉ‹Åblݺ={öÄÉ“'---øùù¡[·nròS¦LQhHóõõU™åÖÝÝÿüóvïÞ¤¤$téÒ¥Pƒ¢©©)fÍš'''•r@AvÖ .`÷îÝHNNF×®]ê/¡Y³f‹Åñññ¸ví,--ÕJ#˜˜$$$ÀÆÆÎÎÎ [ÿý÷^¼xGGG0Æ‹7n nݺpqq‘“ÏÈÈÀƒ¸gß;w¸—¡©©)ªU«&×çÞ½{x÷îZó}ýú5>|ÈÇ¿}û6Ï0%;¾DÖÒÒ*TëæÍ›000Pzé^½:ôôô`ll¬°ýSyóæ >|GGÇb;++ QQQHNN†±±1lll`ee%·®ÓÓÓñèÑ#î)yëÖ-<}ú`ff ¹±SRRðìÙ3þÖ&&&÷ï߇›››Âï7>>^àÒ]¾|yôW–Çãõëר_¿>òóóqóæMÄÆÆ¢~ýú*¯Ó£G 3334nÜ™™™¸wï²²²h›5AAñE’––†˜˜hii©4d| ÆÆÆ˜={v¡rðððÔùûû«ìãããŸBÇžyò„ijj²üQnœ;wî0lþüùJÏÇêÖ­«´]–ÀÀ@f`` ¶|³fÍvúôiµû¨Ã–-[˜±±1ÀŒŒŒøw¼~ýz9ÙÝ»w+½žãÆS8~PP‰D,##ƒ5lØPÐçâÅ‹rò™:(Õýûï¿g+Vd±±±ÌÎÎŽiii1‘Hİàà`…}fÍšÅÄb1366fb±˜Ë`­[·Vóª©Opp°ìµªÀ¾€û*T¨P¡B… •â)Ì¥ï)ûú)T®\™onn^ìãÅÿÿþËÌÍÍåž5‹ƒ?þøCð\Xìç såʀ͞=[PÿøñcÖ¶m[V¹reöèÑ£b?¯­­­ôw~ˆ}⽊bø}AT«V Û·o/Tnçθsçîܹ£Ò¶oߎ¥K—¢wïÞxñâ^¾|‰ñãÇcìØ± å_½z…–-[B__Àëׯ±uëVÜ»w}úôQØ'77M›6Å AƒðüùsÄÅÅ¡Q£FX¼x±\Ì·–-[âÎ;øùçŸlI–Ìeîܹ Çß³g—)l¯ëÖ­qçÎ,[¶ púôiÞWÖâoaa6mÚ`÷îÝrAdCBB ¡¡(=—••6nܨRŸO¡I“&¨U«jÔ¨QlcÞ¾}C† §§'žžŽÌÌLÄÆÆ*Lß±cGܹs .ðìÝyXMkûðï.Õn4JТɔ¡Ìd&㉣ƒ™2Æ1$ §â  ÉœÌs™NˆÄ1‹(*J!J*4hnýþè·×kÛ{׎Âý¹®u]Ö³ïõ¬{­öñ¾Ýž¸víû>-Z$ò> ÃÀÑÑêêêø÷ßqíÚ5øùù B}êÔ)¶ÏfÍšUù 999èÑ£ÜÜÜ™™‰ØØX´hÑ^^^ÈÉÉá‹ ÇòåË1þ|¤¥¥!55íÛ·‡––Þ¿*ïG!„BÈ÷v÷î]$''#99ù‡]ügÖ¯_?4nÜmÛ¶EãÆ±zõj‰ßcРAHKKcooo‰ßƒ×¶m[Ì™3Ë—/‡žžŒÎ;£I“&øøñ#.^¼CCÃÚN³J4¥·¼5ûx²²²TÌñ–’ú_­TRE)))±ùü¼¤  PéðÞÕ«W£Q£Fسg;%×ÕÕyyyB‡Pûøø ;;¡¡¡ì[ggg$''ÃËË W®\Ø%—Ãá 44”R­®®Ž‘#GâîÝ»HLL䛢¬¤¤333B§ òÈÈȈ5,ùk­Y³kÖ¬‘hŸ111` .„±±1€Šµ6,,,„Æ+++ÃÌÌ zzz€¦M›V9åüsçÎc§ ‹zWFFFìŸåääªìSJJ çÏŸg§¦[XX`ذaX±b»¨0Oxx8dee1þ|ÈÉÉAGGãLJ««+²²²Øw@!„BH]b``PÛ)J,]ºwîÜAóæÍѧOŸ¹‡œœtuuk¤oRµuëÖaêÔ©¸té222 §§‡uëÖ ÄRWQÁ¯²²²|ÿ1÷¡££S­5jËLJùóç ¬¿'jîý•+W ­­¼¼<„……±í¼"Ͻ{÷ ~ÒÒÒë'ðŠ1µµ °¸† 555ìß¿Ÿ-øÝºu III쨶Ÿ‰ äåå1qâD¸»»cøðáPVV®‘{×ÈZ—JJJëPòοá§­­’’äææ²k-ò ÷â !„B!äK;wFçÎk; RÚ5k&Ö,´ºŠ ~•hÚ´)ÙóÈÈHœ:u 7n¬tšºâùóçª÷¯CÏž=CNN† &𙜜^½z%V??J1…ËåÂÁÁû÷ïG@@äååqàÀ¨««cèСµžÄ!44³gφ““&Mš„þýûcæÌ™|#¿‡ÃŠŠŠÄú«Š¬¬¬Ðv;;;,^¼nnnðööFrr2¶nÝ [[ÛJGoB!„B!?2ZÃï'¦¤¤ bÇ]q)((`РA(,,zøûû×TºµÆÙÙ¹¹¹8}ú4ÊÊÊpøðaŒ=ú‡)ZVWŸ>}ðäÉ<|øóæÍõk×ЧOŸŸrD£©©)BCC 333 0Í›7Çj;5B!„B!¤ÆPÁï'ÆÁôìÙ3Ͼܤ‚ÇÌÌ ÷ïֶ߯ß?:˜™™aß¾}GFF&L˜PÛiÕ¸–-[bÕªUxñâºuë†U«V¡¤¤¤¶Ó’¨ììløùù¡K—.xðàÞ¿sçÎÑZ„B!„B~jT𫆠`Ê”)ëáÕUòòòèÕ«Ž?Žüü|¶½¬¬ B¯qppÀ«W¯°iÓ¦ÍMUUðôéÓ:Ñ¿³³3°aôlÙRä‡ßÃ0kÒÕeee˜››£¨¨HähP555@BBBç#Ië֭õk×pìØ1´nÝjjj`æ—*hB!„BùõPÁ¯xkúý(?˜?>²²²0dÈܹs×®]ƒîß¿/4ÞÅÅݺuÜ9s°páB}úàÒ¥K;vì7ß¿®JIIA@@V¬XÁ¶ÉÉÉÁÙÙ~~~"¯SQQÁÂ… ±yóf´mÛ`ii {{{èééÕxÞ_ËÙÙ.\ÀíÛ·Ñ¿p¹\ÈËË#-- ‡Fyy9Ξ=[ÛiB!„B!Åa¦¶s¨5' @? bqÿøøx‰öïîî___Ô…w\TT„¸¸8˜ššB^^^¬kJKK‘˜˜ˆ?BKK Mš4‘x^%%%xôètuu¡¯¯_«ý·iÓ¦¦¦8xð Äó€3f`Ïž=ÈÍÍ+žaäææJ|·Û²²2$&&";;ÊÊÊ0229²ïKÅÅň‰‰žž4h ѼjÂüùóñï¿ÿâÆïÑÅÅ'OžDvv¶DïéããƒE‹}Þ¤Î0̉ބB!„Ô‡£ €êâíí ww÷Z̈ò3033û|­P†a†}K4Âï;6¬âgäéé‰Ö­[×JrrrÕ¾w½zõ`ffVCU‘‘aGŒÕfÿ!!!xðàA¬]8|øp ..®Z×q8‰û@ZZ¦¦¦_u­¬¬lþ¼$íÈ‘#èß¿¿À{ÌÎÎÆõë×ѵk×ZÊŒB!„B©9Tð«AmÛ¶…““{þ#­ý÷+ˆ‹‹ÃÕ«WñòåK`̘1èÔ©“ÄïchhˆÂÂBËåJ¼"Zß¾}±sçNhkk£W¯^••Ž{÷°víZÈÈÈ 00°¶S$„B!D@QQ;SŒÃá@NN®Òøââbp8ÈÈȈ}'Ož iÓ¦Õº¨˜EU^^^eNµ­Á=À« IDAT¨¨ÒÒÒuò÷pq~^ååå(..fÏëÕ«W§žåñãÇ033ƒ´´tm§‚¢¢"¼ÿZZZbåSZZŠèèh())ÁÄĤN½WI¢M;jÐÈ‘#ÄÍ›7¯í”ÈgžèׯŸØý߸q–––_5y„ 066®öuß›®®.f̘QÛiÕ¥Kv& ('Ožd¿òòòpssûNÙUíüùóhÞ¼9¼¼¼$Þ÷“'OÄ^n­¨¨þþþ044„žž’’’ª¼fÁ‚PSSCûöíannEEEøûûkÚuÒÏYÆ$D ööö°··¯í4H ’‘‘‡‡<<ˆˆÌ;·ÒøèèhøûûÃÃëV­bÛ{öìYÓ©Öšºõ­!„B!„B~2Ÿ>}BAAAõ߸qã*c>~üˆÝ»wóµõîݻҥ§âããqìØ1¼~ýmÛ¶…ƒƒƒÈØ­[·ÂÄĽzõBdd$ÂÃÃñþý{ØÙÙaÀ€ñ077%vñâE$''còäÉlÛ‘#G //Ž;bË–-ÐÖÖÆ¤I“ðîÝ;¢}ûö8p Ð¼òóóqüøqܾ}ZZZ>|8Zµj%4–aœÍWðóó󃲲2¼¼¼çÏŸƒÃá`Ë–-øøñ#<==qêÔ) <˜¯¯×¯_£K—.xûö-š7o޽{÷Â××ÇŽƒ_l^^ÆŒƒS§NAOOØ´i‚‚‚ EEEgþûï¿Ñ¶m[<}ú³gφ²²2òòòЮ];vtž0ÆÆÆPUUùù·ˆŽŽÆ”)S ¡¡ÌÌL‘EÇêÊÈÈ`¿?¥¥¥ÈÊÊ‚ŽŽŽÈ‚ßüùó1fÌlݺ§OŸF»vípãÆ lÚ´ !!!2d_üáÇììlp8ö^@ÅfŒ_üªëĉèÛ·o½÷ºˆ6í „B!„B~`³gÏ®rJ£¡¡!ÒÓÓ‘žžŽÛ·oW[VV())!::áááxùò%~ÿýw„‡‡‹¼nÿþý(..Æ»wï/^@NN›6múªçúÜ… 0uêT<~üòòò˜:u*æÍ›‡˜˜Ô«WwïÞ¸æÌ™3øã?ðúõk\¸p111¨_¿>\]]QZZÊ»jÕ*œ:u ÄË—/ƒ}ûöáÒ¥KX³fȼîÞ½‹U«VáüùóøðáÞ½{‡ƒVú,îîî˜6mÚ×½ˆ*4oÞ®®®ðòò’X±,,,ØïÏå˗źfçÎPTTd¿‰‰‰’’ÂæÍ›b<ˆôôttêÔ ={ödž ‹oÊýÇÈË˃¡¡!222päÈ 22’Ý ûgD#ü!„B!„ š>}:öïßÏž€a¨©©±m£GF@@€Dî'+++‘~x˜˜ˆ€€˜˜˜°í .Ddd$=z$ôº¡C‡bï޽칶¶6Ú¶m‹/^|sNVVVpqqP±¾¡††ÆŒÐ××GLLŒÀ5ööö|S8 ±`ÁÌž=aaa4h€ŠéÎ~~~°µµå‰8f̬[·þþþðôôš×›7opãÆ všpýúõQ¿~ýJŸEÒ?¯ÏÕ«WObß«o5zôhlß¾=700@óæÍñòåËïšGjj*àúõë011Ann.¤¥¥QVV†N:áðáÃboüñ#¡‚!„B!„"ANNNèÚµ+{ˆââbÌš5‹mgݽÚÂ+ž [{¯²b––––@›©©)ž>}úÍ9)++³ÖÐÐ8ÿôé“À5¦öíÛðìÙ3¶->>………’’‚»»;_¼¬¬,>|ø€7oÞ Aƒý G…K—. |ç’’’à§\à ~5(,, îîîìñùÂDEEÁÝÝ>|ûÞÞÞððð¨vn‰‰‰pwwGbbbµ¯ýžÎœ9ƒ¿þú«¶ÓêÖ­[pwwG~~~¥qË–-c¿Ë–-ûNى篿þŠ+j; ÀåË—1mÚ4„††V[RR‚Ý»wÃÙÙ Àܹsù&„B!¤.quuÅìÙ³k; ± :ÊÊÊðóóCYYÛŠS§NIä:::;ë>|‡’HÿpèÐ!¾|óóó±fÍèëë£W¯^l»––œœœ°{÷n±w ­Ë®^½Zç×ÅÀÀQQQ/úÍš5 ÅÅŘ?>ÛV\\Œ‹/BSS–––½_]@¿tåÊøúú",, aaaìÐaQ=z___¡ÿ !ÊŽ;Xíyñâ|}}‘’’R­ë¾·ª¶@¯M÷ï߇¯¯¯ÐÅa?wíÚ5\¹rAAAX·nÝwÊN<Û·oÇÖ­[%Þïŋѿ±Š×7oÞDÏž=Ñ«W/lÙ²wîÜ©4¾¤¤£F‚‹‹ ¢££!++‹£GÂßß_RéB!„"QÖÖÖèØ±cm§!6,\¸ááá°³³Ã–-[0qâDLž<­[·–È=ú÷ïçÏŸ£wïÞð÷÷ÇСCááá+++‰ôí۷ǤI“0fÌlذ=zôÀãDZbÅ ÈÈÈðÅ®Y³&&&èÛ·/\]]ŒþùÎÎÎðòò’XN5íñãÇèÞ½;ºté"ÑÑ’ßË!C••…ß~û ›7o†‹‹ þøã¸+W®ÀÁÁÌ™3B‹ëX¿~=‚ƒƒ1pà@,]ºÆÆÆHHHÀòå˺©ë­áW-¯_¿ÆÑ£G1sæLHKK‹}Ý—‹~V¥:;Ã\½zEEE?Ýn2?’ªÞ=ï_ˆf̘={ö|”ÄVUqík¥¦¦âüùó(..®2vÊ”)(((€¿¿¿Xÿê9oÞ<„††âСCøý÷ßÙöª ¯„B!„ \.Ý»wºë,ÏâÅ‹!##ƒ   ,_¾Ý»wÇ•+WpòäI¡S};wî SSSvSSStîÜY ý¯¿þBFFΜ9ƒ/^ OŸ> ÂöíÛqóæM¾X+++¾••Ùó¶mÛB]]ïš.]ºÀÖÖ=zô€——–/_###aܸqùhii!::žžž¸xñ"‚ƒƒ!++ ###¾ X>gmm-ô™k“‘‘lmmѪU««(**¢{÷îÐÑÑÓ­[7˜˜˜´[XXTZˆ´³³ƒ——<ˆ«W¯ÂÌÌLhÁ¯  ééé*ÖèëÞ½;òóó‘ŸŸ/rc777hii!((6l@ãÆ±bÅ 8;;WñÄ?(†a~Ù@cjjÊTåÊ•+ ¦   ÊX†a˜… 2¯X<çÎc0Ÿ>}ûšêHKKcJKK†a˜ððpsñâÅJ¯)//g^¿~Í”••I<Ÿ?òÝ'77W ÆÍÍ‘““cÏ ˜wïÞ‰ÕYY“ššÊ”——W+¯ÒÒR±îqüøq¦^½zì;­ÊôéÓ%%¥jåRïý[ˆû}صk€yûöm•}^¼x‘)--e²²²Œ‡‡‡Èج¬,F^^ž™2eJµsÿ^¼½½Þß+ÿ¨1uàï;:è ƒ:è ƒ:$sÐýüÿïy{{3’¦££Ã8;;K¼_òcRRRb¦OŸ^Ûifjjúùï‘!Ì7þ]ESzëcccèèè@^^¾Ò¸BMM=ú÷ï_iü©S§ §§===¨¨¨`ܸq(((»dÉhii¡¨¨nnnÐÖÖ†¾¾>444pâÄ øÅ‹³[\îÆPSSã[ô4((jjjˆŽŽ†……´´´°eË<{ö ¦¦¦hÔ¨‘ÈõRRR`kk UUUhiiÁÌÌLè¿* ³Ž;ÊÊÊhذ!TTT°hÑ"¾µ'>·nÝ:¨©©¡¸¸îîîÐÕÕ…––Œqûöm¡×@“&MаaÃjö¬Ž)S¦@GGqqqí·[·n|ß{{{‘±sçÎ…‘‘>}ú„iÓ¦ASSúúúÐÖÖÆ¹sçâ?555LŸ>дiSö>‹/zÞ½{‹ýwíÚ…‚‚Lš4I¬xB!„B~T€ššÌÌÌj;R Nž< .— .—+±Rȯ…¦ôÖ!FFF˜7o^•qS¦LÁ!Ck×®Enn®ÈØ«W¯â·ß~ƒ¹¹9Áår±{÷nLœ8Qh|aa!²³³Ñ©S'b×®]x÷î–-[†‰'¢oß¾PRRâ‹¶N[YY>~üˆ’’¶­¸¸?~„““ÜÜÜàçç‡%K–`÷îÝX¸p!|||°nÝ:888ðõUZZŠN:ÁÉÉ ^^^HNNÆœ9sУG$&&²[Æë»uéÒ©©©˜4i „àà`¬Y³999ؼy³@®EEEøøñ#|}}±gÏŒ?ªªª¸sçZµj%òÝ6mÚnnn"?ÿV‰‰‰ÈÊʪríÇêš5kÛçßÿ]é¦#Ÿ>}ÂÛ·oaccKKKìÝ»¯^½‚§§'&L˜€””p¹\6¾U«VðññÁÍ›7Œ%K–@YYвeËoÎýÑ£G¨W¯Ú´ióÍ}B!„RWùûû³ƒ4~ƵÅHÕÚ·o   öüó]… ü*Á[³‡·]ó¦M›P¯Þÿ^¤Š>²²²bü†ÊþùÀ"G®ÀŠ+ //Ë—/³…±¾}ûÂÙÙ™]ØòKååå=z4þüóO¶-%%+V¬À“'OD®_ ®Ù³gc„ HNN†6n܈ѣG#66[·nEYY߈¯²²2øúúÂÑÑеkWhiiaàÀؼy3<==ÙØ-[¶ &&ÇŽcG®õîÝ222غu+fÍš%r…½{÷âÖ­[044ë9kt§­sçÎ!==]ì|Ä5bÄöÏâlØQ\\ŒiÓ¦ÁÕÕ•m{úô)Ö­[‡¤¤$¾ÝŒLLL`bb999ÃÉÉ ÚÚÚË=55ººº8qâÖ¯_øøxèëëcذaððð€œœœÄîE!„BHm5jTm§@jYÆ ÃR4¥·oÞ¼A`` {ð¶óÞ¾};_{]UXXˆK—.aôèÑ|£à8ŽÐEJyêÕ«ÇWìÈh3[[[}y^PP€wïÞñÅËÉɱÅ>žÀÐÐP`Ëô#GŽ@OOýúõCaa!{888 ¬¬ ‘‘‘"ó:~ü¸Ä‹kßBNN®N䣢¢ÂWìþ÷}ÈÊÊú®¹¼{÷©©©pttDãÆ1cÆ ÈÊÊÂËË ½{÷®´øM!„B!„ü*h„_%Ú·oøøxö<22=zôÀÇù¦1ÖUÏŸ?Ã0ú«ªª ¼Ej%âËmÐyçâÞÃÔÔ |mÏž=CFF;ôKOŸ>­´?"’ý>ˆCKK ŠŠŠxòä »v¤§§'<<<àíí]étuB!„B!äWA¿Ÿo´Ó—…µŸ…ŒŒŒÀ6ã………èÓ§¼¼¼„^SÙ¶á¤î300@dd$ôôôøÚGooo\½z• ~„B!„B~yTðû‰¨X÷¬&ÕÖ4ʤ¤$4iÒ„¯ÍÔÔ™™™èСC­äDjV»víŒÛ·o£K—.l»¾¾>€Š X!„B!„_­áW 055…”ÔñÚx[¸‡„„|våʉÜCII Ÿ>}ÂóçÏùÚïß¿/‘þŠM#ÂÂÂøÚîÝ»‡øøxtíÚ•¯ÝÖÖ<XÛ|_²²²€·oßJ´_'''(++Ãßߟ¯÷}¶°°èý!„B!„ÑQ¹ª#xkúñŠ?‚Ù³gãÙ³gpwwGii)ŠŠŠ°råJüóÏ?é¿iÓ¦€mÛ¶¡  ™™™X°`<<<$Ò?ϸqãpæÌ”••áÕ«Wprr‚œœœÀfK–,¾¾>þøã\¸pmÿðánÞ¼)Ñœjš¯¯/:uêTã#4kB·nÝAAA(--E~~>"##‘——'øøx<{ö @ÅF ¼¶‚‚¾XÌ;ÇŽƒ··7 ñßÿÁ××\.&L¨ù‡#„B!„Bê8*øýä&Mš„1cÆÀ××PWWǾ}û ‘þGŽ øúúBWW:::xôèöïß/‘þŠÑb;vìÀˆ# ¥¥CCC$%%áðáÃkò)++ãâÅ‹000@¿~ýP¿~}@]]:uBFF†ÄòªiÇŽÃÍ›7SÛ©T›zôèuëÖAGGªªªèÕ«bccùâÊËËannsssvöÖ­[Ù¶{÷î ôíé鉹sçÂÃꪪèÚµ+bbb°mÛ6v;!„B!„ò+£5ü~p6l¨t§TiiicÒ¤IxðàZµj…Ž;¢°°7F«V­øâ]]]1dÈ~Z´hˆˆ´lÙ’¯]NNwîÜÁÉ“'‘Î;£M›6ÈÉɈÀÞÞ-[¶dÏ`ÆŒ>|8ºuë†/^ <<………èÙ³'7n,ô™ÍÍÍqëÖ-ܼy±±±ÈÉɶ¶6lmm¡­­-?fÌtìØ±Îmn‚Ç£OŸ>5v;vTúÜsçÎ…£££@»µµ5"""кuk‘×FDDàîÝ»¸zõ*ôôôЫW/­””"""Döñå÷gíÚµ˜6m""" %%…îÝ»ÃØØXd?„B!„Bȯ„SY±ègÇápÂô*6{ˆ—hÿîîîðõõÅúõëLJ¡¡¡DïAê¾uëÖ¡¸¸§NBLL rssk;%Rƒ|||°hѢϛÔ†ùP[ùB!„Éâp8ºÒxçÞÞÞpww¯ÅŒ!?333$$$ðNC†ö-ýÑ¿¤®®CCCøùùÚ´iC¿_Ðþýû‘ŸŸhÖ¬Y-gC!„B!„|=†aÀápj,žH­áWƒ.\ˆ””öàmd@~-QQQì&QQQµ!„B!¤Ž³³³C‡СC 4¨ÊxWWW̘1ã;dV÷,]ºÇ¯4fâĉ˜3gN¥1‘‘‘ì;ïСV¯^-É4¿ILL ´µµ±{÷îZËáôéÓ6lttt   €-Z ((HäckÖ¬AëÖ­¡¨¨.— KKK¢¼¼ü;gþë¢~„B!„BH…úõë£OŸ>PQQ©2þÉ“'––þ™Õ=‰‰‰¸ÿ~¥1?†ªªj¥1ZZZèÑ£ÀÏÏVVV’Jñ›•––¢¨¨Ÿ>}’xß €´´4Μ9Si\bb" 1sæL",, ãÇG||<|||⣣£Ñ AŒ9YYY8zô(\]]‘’’"4žHü!„B!„´qãFãÏ?ÿûkkkvy¨ªHKKÿ²?qˆó~,,,ØBT``à÷HKlVVVxÿþ=¤¤$?I³¸¸X¬ïΜ9søFI®\¹-[¶„¯¯/æÍ›MMM¾øýû÷ó¯Y³†††X³fÐx"yTð#„B!„BjÐýû÷QPPPcýˆ½FZjj*¢££‘••…F¡Y³fÐ×׈‹…ªª* ðêÕ+ܾ}ššš°±±¼¼¼Èþ322pïÞ=|úô íÚµƒ‘‘Q¥ùðâ Ю]»J×½/,,ÄÝ»w‘––†¶mÛÂØØX¬g600€²²²X±_#!!ÐÒÒ’XŸ………‹B]]]h|LL 444 ¯¯üü|ܸq999èÚµ+´µµâŸ?ŽœœäææBZZÑÑÑìg¦¦¦•þŒyF…˜˜¤¤¤TYÀ“––†³³3V­Z…ØØXv4%©9Tð#„B!„B~`ÆÆÆU®VRR‚‰'bÿþý(++ƒ¦¦&233Ááp‘‘!P°±µµ…äåå.—‹ÂÂB4kÖ .\Z˜ó÷÷ÇüùóQRR‚zõ꡼¼‹/ÆòåË…$ýüü°`Á¾ø%K–ÀÓÓS þõë×èß¿?bccÙ\¦NŠÒÒR±Þ‚‚B•q_#)) æææhР^½z%±Í)’’’¦ïÝ»cÇŽß±cGŒ?­[·ÆÌ™3QVV†ââbhhhàòåËhÕª_üÌ™3qîÜ9öüó{EEE¡M›6Uæxýúu¡mÛ¶b=o½¿ ˆO¾ mÚA!„B!„üÀÆ''§Jc|}}Œµk×"33ïÞ½CNNîß¿/rtÖ¾}ûššŠ¸¸8äçç#$$¯^½‚£££@ìž={àæægggÄÄÄàåË—˜2e V¬X={öÄaΜ9?~lذ.\@^^Ž=Šììl¬\¹R vëÖ­ˆ‹‹ƒ :uêÄÞ+..–––"ïQ^^Ž'OžÀÑÑ×®]ƒØÏ}ëÖ-hjj¢Y³fbÅ“oC#ü!„B!„ Ú¸q#.]ºÄž?xðeee6lÛÖ³gOÌš5K"÷«jÚ,<|øzzz˜={6Û¦¬¬ŒÖ­[‹¼¦mÛ¶8vìäääC‡…››¼½½qÿþ}vXII –,Ylݺ•-mÞ¼çÎòeËàììÌö[\\Œ%K– iÓ¦ ÿyóæÍ›øï¿ÿ°dÉŒ5 `bb‚#GŽ C‡xÿþ}¥ÏÞ¤I“*ßÏ×RQQÁãÇ%Þ¯¬¬,ÌÌÌeeeb]cii‰óçÏCQQ0bÄ#..N ¶aÆEEEHKK³÷ªŠºº:rrr   €[·n¡E‹b]—ššŠ›7oÂÅÅE¬xòíh„!„B!„"AªªªÐÕÕeyyyp¹\¾¶ªv•´=z -- ööö¸zõj•S€ E‹l±ç÷ßܽ{—mKLLDjj*¬­­qþüy„……!,, çÏŸG›6mðòåKdffòÅ¿~ýZh¼••^¼x¬¬,6þæÍ› 0•Ëå¢ú/ã'eeeÅû>oûøñ£Äîáççhkk£ÿþ ëºÙ³gCCC«V­’X.¤r4ÂB!„B‘ qãÆaܸqìùøñãQPPP«»¿NŸ>ÙÙÙX»v-Nœ8MMM8::bæÌ™hܸ±Øýðb“““Ù¶gÏžŽ;†ãÇ \#DðK IDAT''Ç·±/þèÑ£8vì˜ÈøúõëóÝËÀÀ@ìŒÂÂB¡G»víØxÞGŽÿùF¼þy÷&µOVV+V¬@qq1<(2.** ãÆÃš5k`gg÷3$Tð#„B!„B~²²²èׯŽ9‚˜˜býúõBcy»ª~.66ÿºx¦¦¦ø§ùV†·^œ¸ñ¼‘}¼‘Ÿgj2©ÊÊÊ€œœ¡Ÿ¿xñƒ ‚‹‹ þüóÏï™Mé­QOŸ>ÅóçÏÙóvíÚ‰Üý¨Øf<&&=zô—Ëý)ÖYYY¸{÷.¬­­¡¡¡!4æÕ«Wxüø1zöì YYY‘}]ºt %%%iiiôéÓ§FrþQQQ¸rå 8š7oŽŽ;²IÀ»wïÅžËËË£{÷êW{ýú5nÞ¼‰¸¸84hÐ}ûö9ôžaܹswïÞEff&6l[[[ç¬ !„B©9¶¶¶ìï(uI“&M ¥¥%²`sôèQüùçŸ|ëä@NN½{÷fÛ6lˆ.]º`ãÆpuue7„ÅÀÀ;wƆ 0uêÔ*ã )))ìÙ³ÖÖÖl{bb"þý÷_¨©©‰ó¸5&??\.ÒÒÒµšÇ×PUUÅýû÷¿êÚ]»v¨ø~éãǰ³³CçÎE”IÍ¢‚_ Úµk|}}Ùó°°0ôë×Odü… 0a¼xñ5ú)ÖQQQ0`._¾,ô/ 8wî¦NŠ´´4èêêŠìËÞÞž]””Ë墠  Fr®®ƒb̘1——‡††RSSË·åù­[·0dÈöÜÀÀ/_¾¬tùÄÄÄàìÙ³˜9s¦À"°Ÿ{øð!Z·n ‡eeeäää€ËåbõêÕ˜9s¦@|@@f̘‡%%%äææBVV«V­Â¼yójò‘!„Bùn>_ϯ¶ü÷ßˆŠŠBçÎÑ´iS·²²‚M?Û/a˜_ö€À˜šš2UÉËËcC‡ÁÇÇáááÈÎÎF×®]áãã#²x¶k×.=z‰‰‰hÖ¬¦M›VéŽ:;wîıcÇØøéÓ§càÀBc“’’°xñb}ÐÖÖ–HŸGÅþýûT õ–’’°aÃØÏOœ8ñÍ…½‚‚¬Zµ ×®]CZZ,,,àèè(0dž'-- ;vì@dd$RRR ªªŠž={ÂÓÓS`ºîÿý‡þù‡VìììÌT‡ ‚ &T™ï¬E­ã÷¥^½z233ÅŠ'„B!ägtùòe 6 êêêØ½{·DúTQQA‹-ª}””Tµ®SQQAûöík$^GGG`:±$ܸq^^^*Ö䫎ʖ=úQèêêVºt4hÐ@¬ßÝIÝðã !ú‰5iÒ¤Ê &ŠŠŠÐ·o_,[¶ úúú˜>}:ìííÁáp ###ÿøñc„„„ OŸ>8{ö,zõêkkklݺ={öºp¬‹‹ \\\ðæÍŒ5 ?—Ä¿egg³k^XXX`„ ÈÏÏLj#°páBø²²2´jÕ gΜA›6m0fÌàŸþZ¼“––—Ëe¿[rrrlî⌠,..Æ¢E‹ ''‡±cÇŠõL>ZÓB!„ü²F[[[¨©©±3HÍRPP€‘‘ŒŒŒàââ‚nݺÕvJ„|›oü#¨b ?Þš}¼cÇŽ æöíÛ|í¢ðÖ𓤈ˆ³uëV±âçÍ›ÇX3Áßß_è¼µôV®\ɶ•––2ýúõcTTT˜·oßòŇ……1˜U«VñÅ÷íÛ—QQQa222øâœœiiiæÎ;lÛÇ™V­ZU¹†_uÍž=»Zkø=yò„Ù³gÈõ(¾Uýúõ…®© ÊèÑ£«\Ãoîܹ ‡Ãa®\¹Â×>eʆÃá0QQQ×[‡ÏÖÖ–À¼{÷Nè}Ä]Ã',,Œ™1cÓ A¦]»vLtt´X×1 Ãxzz2˜'Ožˆ}M]BkøÑAtÐAtüܾÃ~uo ?BÈ÷!é5üh„_%îÝ»+++ö˜8q"€Š‘GŸ·O¼é”©©©(++ëiiilݺ•¯mÊ”)¨_¿><È×¾aÃÈËËcÚ´i|×O™29998wîœ@¼‚‚‚Xñ¥¥¥8xð †Î7\[EEÞÞÞb=KM277Ǹqㄎ”¬‹JKK€!C† {÷î|Ÿ­\¹rrrذaƒÀu†¿óÖÝKJJ’Hn‘‘‘عs'Þ¼yƒ @SSS¬ë²²²°qãFŒ;æææÉ…B!„R}©©© ¬í4!_‰Öð«D§N––Æžß¼y“ÝZšWxûÞlll0lØ0¬X±ÁÁÁøý÷ß1räÈ*×;ør8999´lÙR Àiii 0€¯7õ7>>^ ^JJJ`í@añ)))(..š«¨Å>‰h)))(,,º±…¦¦&ÌÍÍ~^Ÿ{óæ âã㑜œŒ˜˜ëJÂßÿU«VáÎ;˜6mÌÍÍqåÊ´iÓ¦ÒëæÏŸ‡ƒõë×K$B!„BÈש­ßy !’A¿JÈÈÈð-ZÉÛ|@GG§Fwੌ””Nœ8¹¹¹Ø²e ¼½½Ñ·o_<þ\ì~Þ¼yƒFñµ5iÒŠŠŠpww«êÄëëëÞ¾}+vŽD4ÞÏîóQ¨Ÿ{õê ùÚ>|ˆÁƒ£S§Nxøð!Z¶l 8þ¼À(MIÑÖÖFË–-¦ƒ.22...ضmúöí[#yB!„B!¿ ZÃ溺¬Œ ÀÍÍ ÉÉÉHMMˆ)//dzgÏøÚÞ¾}‹¨¨(­ÕÛ´iƒ»wï"!!A¬ûóâŸ>}Ze¬®®.444!ðYJJŠX÷#ÿc``555œ={Vೄ„ÄÅÅ ü|CBBPTT„€€¶Ø@àûñ%‡ úÛÓÓ»Ÿ?333¡ŸÇÇÇcøðáððð€³³sµû'„B!„B?*øUoM¿ÚšÎËóå:k¥¥¥8yò$”””ø¦ ó0 ƒ¡C‡âÝ»w* €sçÎEqq1\]]ùb—-[8::âÍ›7|Ÿ•—— ô½lÙ2Ô«WŽŽŽ#;Œ—’’ÂäÉ“‰3gΰí—/_ÆÂ… ÅxòšäååÕv*báp8pwwÇ7°cǶ½°°3fÌ€ŒŒ æÍ›Çw oŽÊÊʰ}ûvÌ;€è‚ojöåË—Ù¶ââbcRSSÙ©ÛðúõkLž<˜>}º@¿o߾ŀ`oo¥K—Šýì„B!„B ~Õðåš~µ!-- ªªª°¶¶Æ¬Y³ðûï¿ÃÐÐ111زe êÕœ¥---áÇ£aÆ2d,--qàÀÌœ9ÖÖÖ|±†††ÆÓ§Oaii‰ß~û ŽŽŽ°²²‚¢¢"_1ŒŒŒ°gÏ$$$ÿðá_üŸþ ØÛÛcàÀèÙ³'ìíí1iÒ$É¿¬jZ°`¼½½qáÂ…ÚN…•žžŽÖ­[£uëÖø|öìÙ°³³ÃäÉ“akk ggg4oÞ×®]C`` ŒŒŒøâÇŽ uuuŒ3íÛ·‡®®.-Z„C‡¨(Ð ÓªU+4jÔ®®®2d:tèܾ}›/nÉ’%¨_¿>š7oŽ®]»¢Q£FØ·oþþûo >\ ß?ÿü))) ®®.ßñåz‘„B!„B­á÷ƒQTTĺuëpãÆ $$$@II ŽŽŽpqqAÓ¦ME^·jÕ*ØØØàÌ™3PQQ‡‡…ÆÚÛÛÃÚÚAAAxüø1233Ñ¢E Ìš5 òòòñ#FŒ€ _|Ë–-1{ölÑšššˆˆˆÀæÍ›öíÛcÓ¦M““ƒ‚‚7nüm/è¬^½—/_®±5ä,X 0Ŷ2ööö|?Ssss.—‹Ó§O#88ׯ_Gzz:FŒ¡³† âéÓ§Ø»w/®_¿Ž#Fà÷ßG£Fàéé)ô<ׯ_Ç¡C‡‰–-[bÞ¼y»îþõ×_000ÀãÇQZZйsçbܸq"ŸÛÎÎNäF3***"s!„B!„Bˆh†aj;‡ZÃápÂôSSSÄÇÇK´wwwøúú²Ó'¡¬¬,Ñ{TeþüùX¿~=JKK¿ë}kÓ½{÷PZZŠõë×ãÔ©SS  ©I>>>X´hÑçMê Ã|O!„B~,G»ž‘···Ø›Bˆ(fffŸï§Ê0̰oéFø}VVV€°°0ôëׯ–³ùùõîÝ?~€Z_o‘B!„B!ä{£‚_ Z¸p!¦NÊž×öú¿Š§OŸ²†ðv—%„B!„òs(**ÂÎ;aggCCÃZÍ¥´´÷ïßǵk× //KKK´lÙêêê•^—””„gÏž¡~ýúhß¾ýwÊ–üJ¨àWƒÔÕÕ«ü¼¦ÙÚÚBVV¶VsøÞ´µµk;B!„B!5$22Ó§OGBBüýý%Ú÷‘#GPVV†?þø£ÊØOŸ>ÁÎÎW®\¢¢"JJJP\\Œ¥K—bùòå"¯+))ÁÀñôéSôìÙ—.]’ä#€ ~?½bàÀµ!„B!„ü²^½z…òòòZö³èÚµ+þùç ••…þùçxp¹\HIIAJJн—˺ddd$zôè999±Ÿáþýûؽ{7Ö®]ûË­µO¾?šÒK!„B!„HЫW¯••Åžggg£¨¨ÑÑÑl›††5jÄwÝõë×±qãFL™2°lÙ2hiiáĉ"GÌ]ºt 7ÆóçÏÙ ¿ÝWRRWWWàúõëìæ’sçÎEÇŽáêêŠØØXÔ«WQ"011Áùóç+}>ccc¡…0SSS;v zzz––ÙGuééé±S¢Ÿ>}ŠsçÎUyÍÕ«W±eËL˜0ð×_A]]'Nœ€‡‡_ì¼yóýû÷GQQQ¥Ó¯srr›› }}}„‡‡#$$oß¾………&Ož }}}¡×Íš5 ={öÄ!CÄzfB¾ð#„B!„B$héÒ¥°²²bS§Náüùó|mK—.¸î?þ`‹}<:t@zzºÈ{)**"<<œ-öÝwöìY$%%añâÅl±êׯE‹!!!¯Àgbb‚ÔÔTöÜÓÓ'Nd§§¦¦¢iÓ¦"s²··G§ND~þ½899±Å> â½ØØØTú>ÅñæÍÀÁƒ1`À\½zÉÉÉðòò‚………ЩÆÄ­[·àçç÷M÷&D\Tð#„B!„B$Èßßiiiì1jÔ( :”¯Íßß_àº/ u ¯¯ÒÒR‘÷êСC•»½ÆÅÅ:wî,ðY·nÝñññl›‰‰ ‘™™‰§OŸÂËË {÷îÅÑ£GTŒð«¬àWW|Íû‡’’ÀÀÀ±±±xôèîÝ»‡‡‚ËåbܸqøôéÿéÓ',X°S§N…¥¥å7Ý›qQÁB!„B‘ èê겇¼¼<¸\._›°õïj oD›°Â ®®._ PQð* {›7oFÛ¶m1yòdlÞ¼¥¥¥HKKû! ~5EOOõêÕƒ¹¹9LMMÙö–-[bèСxýú5îݻǶûøøàýû÷˜6mÒÓÓÙ£´´ÅÅÅHOOç+" Tð#„B!„B~b†††€´´4Ï^¾| |ë 6iÒRRRˆ‹‹CPP¦OŸŽiÓ¦áÖ­[8}ú4ÊËË邟´´4LLLðøñcϬ­­¯_¿fÛ:„üü|XZZBOO=’““ñßÿAOO;wîünù“_mÚAȆºø,!„B!„üˆZ´h b-?777¾ÏBBBTŒNã‘••…||| ##p¹\ôìÙ‹/€Ÿºà'Îh»‰'âÿÚ»÷°œïÿàÏ»tQ¨-ÑQ‡uºé„–È)Æ¢d62çóá;‡E¾3ŠÚev-™¯Óæ›/±œB,fæ4‰Q*I,5šHÑáþýáwßëvßÝÝQnåù¸®®Ëçýy½ßŸ×çs]³Ë«÷aþüùHNN†«««¤ý÷ß ý}Ö­[‡ÇËŒ1}út´k×Ë—/—úþD 3üH¥ˆ^½z©: aüøñ°´´„®®.„B!&MšTg¿ .ÀÓÓžžž(,,| ™QS… 6¨ìù}ûö…««+"##‘™™)iOMMÅ×_ ///™—YYYáêÕ«?~<´µµ3fÌ@zz:tuuñî»ïÖú¼+V`ûöíó2ÌÅÅ)))øûï¿%meee2q&L€¾¾>ÆG233ñÓO?ÁÊÊ ÎÎΒؾ}ûÂßß_æGOOíÛ·‡¿¿?,,,ÿåè­Â‚©Tyy¹Üßt¼ª¸¸8hkkK¦§+RTT„Þ½{cÿþýièС¨¬¬„­­-‚‚‚`mm ;;;™¸6mÚ )) ¹¹¹033C¯^½`oo{÷îaíÚµÐÐÐPAöDÿà’ÞzHKKChh(víÚÅÿxȉ'eÜêêj<}ú"‘¨ÎØ3f 77'Ož„‹‹‹ÒÏØ¶mRRR0fÌÄÄļJºDDDDDô–›?¾Üe²|ðÌÍÍåö™6mÚ¶m«ÔøÎÎθté6lØ€?þø‹/ÆÔ©Så#G©g«««cýúõxøða­Ï±³³Cxx8ÌÌÌ ®®®Tnõehhˆ¥K—B(Ö"÷D\…'åöèѧOŸÆŽ;pçÎ 00Pn¬»»;~ÿýwìÞ½çÏŸÇÌ™31nÜ8¥ÿ]9kÖ,•‚©y(Si®A"€`cc#u ¹<'Ož„ÊÊÊ$Sš_ô×_¡°°ÎÎΉD¸víþøãtéÒEj]MEEEÈË˃““ÔÔÔ––†k×®ÁÝÝ]²¹ê‹JJJpéÒ%ܹsvvvptt”)BÞ½{RS‰kª®®Æ•+W$§D½èñãÇÈÎήµÿËÊÊÊBii©äZOO¯ÖßåççãÞ½{prr‚H$Bjj*®^½ ;;;¹¹?|ø999HLLÄ¢E‹pøða¼óÎ;žŸHÕ±cG©øÛ·oÃÒÒ ,@DD„ÒïðøñcØØØ`Ô¨Q000@hh(þüóO˜šš*=5O‘‘‘X´hQͦ¶"‘èïÚ≈ˆˆ¨iÆ$§_DDD $$D…Qs`kk[sÉý~‘Häÿ*ãqIo‹ŠŠ‚‹‹ nÞ¼ 7778;;c̘1pssÃçŸ.·Ï¶mÛ  qïÞ=x{{ÃÁÁAAA033CRR’Lüž={ЩS'øøø`Ò¤IèÖ­„B!®]»&çââRëo_rss! ±wï^¹÷ýýýáââ‚Ç×ó+(6vìX…BÉÏ„ j]µjÜÝÝ‘••¡Pˆ®]»bôèÑèÚµ+¾øâ ™ø'N@(J .ƒ ’ýôÓz½ÃÊ•+QYY)7""""""""UbÁ¯‘tïÞŸ|ò qýúutïÞ«W¯–:šûES¦LH$ÂpæÌ¬^½}ûö•Šùí·ßOOOäåå¡´´çÏŸÇ£G[“Ù'•IDATлwoÉf¡À?§åææÊ}ž¸½¶Ó•<==affVëôñ—µcǤ§§#==]©©ÎèÙ³'&L˜€ÂÂBdffJ6œ-**’ŠíÛ·/ÒÓÓñõ×_Ž;&yÖ—_~)3vvv¶äHõ§OŸ"-- ÷ïßW˜ÏÍ›7ñÍ7ß <<­[·Vþʼnˆˆˆˆˆˆˆ^îá§€xÏ>1q!(((jjÿÔJÅǘ״gÏxyy 0räHœ={YYY011‘û¼G!)) :::ž _‚víÚaïÞ½ÐÕÕð|ß€ØØXxyyá›o¾ÁÒ¥K@²L677xòä add: ~áááWð…^NÍeÊâwUD àÀðôô´mÛHNNÆ7о}{I¬žžlmmqùòe€¥¥e­Ë¢àÎ;hݺ5,X€µk×¢¢¢Àóo¿iÓ&¹{;Ì›7vvv g&© gø) ©©)ÙßÎØØ###©vyÄÅ>1ñL6E››nÙ²Ea¬¼¼gΜÁG}$)ö‰õìÙ666R‡`tîÜ’Â^hh(ÌÍͱ|ùrÏ šššèÔ©S­Ï|´hÑBRìïß§è{*ãÑ£G(..Æ‘#GðÝwßá—_~AHH²²²Ð§OÜ»wO*þçŸF||<¢¢¢¤Š¾DDDDDDDDo ÎðSÀÚÚëׯ—\ŸdȨ©©!%%EÒ¶mÛ6¤¥¥ÁÜÜS§N•´_ºt °xñbØÛÛ¿xB+ÑkÅ‚_bnnŽ-ZÈœÆ ÕÕÕ¸zõ*ìíí¥Ú­¬¬––†äädŒ?íÛ·ÇØ±cñÝwß¡¢¢>>>¯)û7Sÿþý…„„Œ;VÒ^QQêêj´jÕJÒVUUgggäææJ„RPPHIIAUUÕëKžˆˆˆˆˆTÁ°æÅâÅ‹¥ö~'"zÕÕÕ5/…¯: ~õðî»ïbÊ”)hÑB5Ÿ­E‹4hâââwÞyGrïÇD^^/^,ÕÇÊÊ 6l@ee%öïߘ>}:¢££¡¡¡I“&)|fIII“<‰V¼<úúõë íðó󃵵5V¯^   ÉŠÇðÏ^0a¹u¬\¹¡¡¡HLL„©©iC¾½yÔk^ˆD"ˆD"UåBDÍ“fÝ!Š5½ÍÛTH¼§Ÿª ~ªª*øúúâøñã¸}û6bbb0qâD888`òäÉRñVVVxöìüüü`aa°µµ…¯¯/***.éýøãѾ}{œ9s¦Qß©1ôêÕ :::øê«¯pñâE$%%!,,Ljfðüào¿ý™™™>|8~þùgDFFbÞ¼y044”ZºKDDDDDDDÔp†_cooÓ§Oãã?†¯¯/€çE«¡C‡bË–-2ÅHqAoÆŒRí3fÌÀ±cÇší~ººº Áš5kàêê °³³Ã!Cdfá 4ûöíÃôéÓѯ_?Ï—OïÚµ«ÎƒTˆˆˆˆˆè­S^ó¢k×®pttTU.DÔLìÛ·%%%âË¢WOð6O=‰€ 222TœQýܽ{yyy°±±žž^£<£©.髨¨À•+W`ll “:ãsrrPUUKKK‚×!57‘‘‘/ÜÒV$ý­ª|ˆˆˆˆ¨a c‰¯#""¢ÂŒˆ¨9°µµEff¦ør¿H$ò•ñ8ï 366†±±q£>£)û@CCݺuS:ÞÜܼ³!"""""""j|ÜȈˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨aÁˆˆˆˆˆˆˆˆ¨yÛOéí þC^^† ¦Ê\ˆ¨ÈÊÊz±IKy5EK—.Åüøøø:c‹‹‹„~ø¦¦¦R÷ÒÒÒ°`ÁÄÅÅAOO¯±Ò%zc½í?Éß?Vê/"¢zÒVuDDDDDMÅ7’’Rg\QQ|}}acc™ûVVV¨¬¬„¯¯/’’’ ¯¯ßé½±¸¤—ˆˆˆˆˆˆ¨EGGcõêÕªN£Y™8q"ôôô°}ûv¨©É–6´´´°oß>”••aæÌ™*ÈHµÞö~DDDDDDD*%%eeeJÅæççãêÕ«¸{÷.LMMáååMMM™¸´´4´nݦ¦¦ÈÍÍÅùóçÑ®];¸»»CWWW&>55úúúJÇ‹ââÅ‹(--…««+ÌÌÌäÆÕÿÉ“'8{ö,Š‹‹áååccc¹}ÊËËqáÂäççÃÕÕ–––J}£­[·âСCHII‘ûmÄZ¶l‰7¢gÏž€¿¿¿Rã5o{Áï €>Àóé¾§NRq:DÔÔ­]»+V¬¨ÙT¢ª\ˆˆˆˆ¨éÈÌÌÄ¿þõ/9r¨¨¨@uu5ìííqôèQ¼ûî»Rñ}úôÁ€ЪU+¬[·ÚÚÚ(//‡µµ5’’’d s}úôŸŸZ¶l‰ÿüç?’ø.]º )) ;w–ÉiÍš5˜?>***ТE TUUaÉ’%X¶lT¬† oooL›6 xöìôõõqôèQ¸¹¹IÅçååÁÏÏW¯^•ä2eÊTVVÖù­V¬X€€899Õëéé‰~ýúaÅŠ,øÑ[åm/øUˆÿ ®®^ëoˆˆ”%gC`‘*ò """¢¦EOOvvvˆˆˆ€ƒƒîÞ½‹M›6aÙ²eøòË/±qãF™>±±±8p ÒÓÓÑ¥K$$$à£>ÂèÑ£ñÛo¿ÉÄoß¾½Öø'ÀlݺsæÌÁ¤I“0{öl"<<aaa077ǸqãdÆß½{7NŸ>„„ôìÙ?ýô† †åË—ãàÁƒR±£FÂõë×±sçN"''óæÍáC‡dŠ›5>}ׯ_DzeË”ý´2dfΜ‰ÔÔT888(ݨ)ˆDoï¿EA"€`ccƒŒŒ gDDM]dd$-ZT³©­H$ú[UùQÃÆþ_GDD $$D*&::ÇŽ“\_ºt UUUpuu•´õéÓ³gÏVø,‘H 55Uêž‘‘:wîŒS§NAKKKÒ¾dɬX±.\z^‡`ff&Š•+W"99ݺuTTTÀÂÂZZZÈÊÊ’Ìæ‰D°°°@UUþüóO©|ÚµkKKK=z­[·–´;::âÉ“'ÈÎΖ´;wÝ»wGhh(ÂÃÃ%íåååèÞ½;îß¿/3¾Xxx8þýïãæÍ›077WøýÄ.\¸wwwDEEÕù͉TÅÖÖ™™™âËý"‘蕦¤òÐ""""""¢¤¯¯cccÉŽŽ´µµ¥Ú”95V À××ùùùrï;88Hï ((Àó"׋•ŠÏÎÎÆ;wàáá#GŽ 11‰‰‰8rä„B!rssQTT$3¾“““T±\\\PR"½ËÍÙ³g£G–j×ÖÖ†½½½Üw;tè 0®&ñj¾ÚŠˆDÍÑÛ¾¤—ˆˆˆˆˆˆ¨A#88Xr=nÜ8”••aýúõ û•”” &&gΜAff&îܹƒ{÷î)UÏzËÉÉyéø¬¬,Ï—èîÙ³G¦––nݺ…öíÛ×9þ‹ÆšÏ255U*ÇšŠŠŠ ¦¦†–-[*ݧM›6€{÷îÕûyDM ~DDDDDDD*–žžoooTVVbìØ±0`,,,°nÝ:9rDéqž>} JÄäÅ‹ÿ¼sçN 6Lég+K<þÓ§OëU¸€wÞyÕÕÕxøð¡Ò…ÐââbI_¢· ~DDDDDDD*¶páB<~ü999RJÆÆÆÖÚGÞžüâ½þ,,,^:ÞÆÆÀóe¾QðÏìËÊÊ‚‡‡‡Ô½êêj…}Å3‹ŠŠ”.ø€ÌÉÅDÍ÷ð£Fqÿþ}$&&âÁƒJÅçççK¦¿è×_EUUUC¦GDDDDDôÚôîÝýúõS“™™ kkk©bßùóçñã?ÖZÛ½{7ÒÒÒ¤ÚÖ­[MMM¹ÏÛµk®]»&¯¥¥%obb‚÷ßÑÑÑÈÍÍ­óýêËÏÏjjjˆ‰‰‘jÏÎÎÆáÇöÀ† påÊÌ;»ví¸q㤠‡5ã,‰Ÿ3gvïÞqãÆÁÈÈH*výúõ¨ªª‚‡‡vìØÛ·oãâÅ‹ˆ‰‰ÁîÝ»_é{˜››cĈX¿~=V®\‰ôôtüïÿCÏž=ѪU+…}íííáîîŽ]»v)ý¼}ûö¡OŸ>/µg QSÅ%½õPZZЬ¬,8;;KŽ%§Wsÿþ}x{{cÈ!˜;w®Ü˜ÀÛÛÁÁÁعsçkΈˆˆˆˆ¨ñ}ýõ×xòä ¾ýö[¬Zµ ;vDLL Š‹‹qîÜ9äççK–³Šõë×ÞÞÞ˜?>îß¿@€Ñ£G#::Zî3ú÷ï÷ßóæÍদ†Ñ£GcÍš52±öööHIIÁøñãññÇKÚÕÔÔ0nÜ8¾ÒûþðÃxøð!BCC ###DGG#>>§NRØ7<< À‰'лwo…± 8þ|c57,øÕCrr2|||PVVmmmU§Ó,LŸ>mÛ¶­õH`hhˆ¸¸8…BÄÆÆJýφˆˆˆˆˆ¨9022ž={PXXˆ²²2tîÜYroìØ±µöûôÓOŒ´´4˜ššJN¤mˆx[[[œ9s%%%ÈÌÌ„††LMMahh([Û ¸›7oÆæÍ›eÚuuuñÓO?¡  ÅÅ۵µŒ1BaþÀóBçÔ©S1mÚ4œ;w®Öw(,,ĬY³°`ÁtïÞ½Îq‰š.émEEEøê«¯Ð¿X[[C(bΜ9xøð¡LìÖ­[1|øpTTT <<>>>pppÀ´iÓPRR"#‰ C¯^½Æ‹mÙ²~~~°¶¶Æ Aƒ 7î¿ÿý¯ä75ñññ D—.]0tèPdffÊí“Q£FÁÆÆÄ–-[”ùL8zô(âââ°fÍ´h¡¸öüÞ{ïaÖ¬Y˜5kÊÊÊ”Ÿˆˆˆˆˆ¨©éСƒT±Ojjjptt¬³Ø÷²ñ­[·†››\\\äû^…‘‘‘¤ØW«W¯†¥¥%ŒÒÒR™û<@ß¾}áææ†åË—7DªDM ~ÀÃÃ?þø#œœœ0fÌTWWcÍš5øè£dbÓÒÒþýûãàÁƒèÓ§<==±qãFôîÝrãûõë‡C‡Á××Wa<Lœ8ãÇG^^Fމ¬¬, :ëÖ­“‰MMME||ÃÓ§O1mÚ4™X‘HIÅ?{öL&þ‹/¾€††ÆŒƒüü|©{ÕÕÕʼv­&OžŒ_ý”´Ÿ8q .TØWSS#GŽÄÁƒQUU¥ÔóÎ;‡û÷ïKBLDDDDDDDDÒ¸¤·^ÜÓOž   „……aêԩظq#òóóñäÉÄÅÅaèСÈËË“é#SSS 07nÜ@zz:f̘OOO™x555 >;v”ŠŸ9s&<<<¤b;w[·b„ xï½÷àãã===¤¦¦"##yyy’½ _ÆgŸ}† 00¾¾¾(//GJJ &OžŒU«V)ì;yòdlÞ¼{÷îň#ê|Ö† `nn޾t¾DDDDDD éäÉ“ªNˆš4èx,ø5°víÚ!==Û·oÇñãÇ€€€ØØØ ,,¬Öå¨aaapwwGBBZµj…×úœððpxxxààÁƒhݺ5-ZTëÌ·€€¸»»#&&©©©¸wï1{ölèèèHÅöïß_î!:uÂÒ¥KÑ¥K™÷ýå—_°víZ\¾|nnnˆŽŽ†ŽŽtuuann^ë;tëÖ sçÎżyó0hÐ ´lÙ²ÖØÓ§OcÛ¶m8vìX'½.‰‰‰HLLTuDDR"‘HÕ9¨Œ@ H0lll‘‘ñÚsøüóϱjÕ*¥—×.\¸«W¯Vzì›®¼¼=zô€‰‰ öíÛ'9½·¦7nÀÛÛÁÁÁˆŒŒTA–DÊ‹ŒŒÄ¢E‹j6µ‰D«*""""jXÀÀ_ªÎƒˆšµý"‘ÈÿUà~¤RÚÚÚ8~ü8 image/svg+xml +name: string = "objecttree.h5" +root: Group = rootGroupObject +create_group(where:Group,name:string): Group +create_table(where:Group,name:string,description:IsDescription): Table +create_array(where:Group,name:string,object:array): Array +close() +_v_name: string = "/" +group1: Group = groupObject1 +group2: Group = groupObject2 +array1: Array = arrayObject1 +_v_name: string = "group1" +table1: Table = tableObject1 +array2: Array = arrayObject2 +_v_name: string = "group2" +table2: Table = tableObject2 +['identity']: string +['idnumber']: int16 +['speed']: int32 +nrow: int64 +append() +name: string = "table2" +row: Row = rowObject2 +read(): table +name: string = "array1" +read(): array +name: string = "table1" +row: Row = rowObject1 +read(): table +['identity']: string +['idnumber']: int16 +['speed']: float32 +nrow: int64 +append() +name: string = "array2" +read(): array fileObject(File) rootGroupObject(Group) arrayObject1(Array) groupObject2(Group) groupObject1(Group) tableObject1(Table) rowObject1(Row) arrayObject2(Array) tableObject2(Table) rowObject2(Row) PyTables-v.3.1.1/doc/source/usersguide/images/pytables-front-logo.pdf000066400000000000000000002261741231437614300256160ustar00rootroot00000000000000%PDF-1.5 %µí®û 3 0 obj << /Length 4 0 R /Filter /FlateDecode >> stream xœ•X»Žd·ÍùŒ —,¾C`'ÎÀá@ºÖHA…•¾Ï)¾zºG6ŒÅà.OóÖóT±x¿o%E×{³É{b´/¡t×r¶¿ýdÿjÿi¾™ñK¨©[ã÷2–5¹¢ä`¯wóåoþ—ýòïlÿô«ùj¾Úo&Xïj•¬»ûÙŽ=¦xçc¶ÁGK±ï6ïbŠ“ìÍ ®ønC .·¤E—ƒØÀgÊLÄK05ÔæD:·?¨ÆìC/ahäžuO쪲¸ƒ ¹¸^"^z0æf4Ç àiÈC¬Wç§¼;ïÏf=[N•>Ý©üÄÎ*.Ô‚4×E#’\†ØXâ)6•!VRû]±K^r©Cž$ä7S ÜÛ£“ÿ/CK§¥CðÞw+°8#±;¡YÝ•Ò 4#ÎëϲäaTñµAv+R7•ýËw[AŸ/zqàm®®1-ChûéŽÁùØ\ |üÊÔgÖ;TÕø½Ô̘¥'eq¡¡vk Bë‹Aæ¬#3ÑJ ® =^\Cq‰G,<µÖmIµ¶Hó1OR>„gÆaŠC÷ 9жeú‡gË6‰ëp34äÜcÙTðæ /—ÍIƒhBGðZÉ0êÝUPÌSÇ{rÔ»lÞ@¤6J¦˜Ïê:R`×R  ©ÉÀ/æ( `¡D|³o# ¡€® iž¼.B±ûcT·›p2x¼A"º¥i<½CiÈÅèuçñƒ`cîšÄâ¢$¨•Ì$š˜O Ö%ÍAîÁ ø³žÿ™éà‘·€$¡ö  KÐ7á¾S¡ŠRѤö&¥+N)µh•?Jú/IH\¯Ã½•Æ €nYØÃÐ:B/à{HˆˆÐ„F¡ô ñj e7ØöºCâ±.ZžÌKª$­WF^J*ÒE$#ì"q&XБ lã P$kŒLÀ+àp`½õ®Á`‹) Cæz¢ìb°=6‹,|$yˆw£zçµúC€Ñh\b¾!ENKFù€p®Õ —㺖º )÷‘ò'ÑŸtàBQ5]]Ël#8"êHi‡B¾òè#1 hNQw†nÏûн¯ƒT6Ê¢2&ÒD[a<{—m k‰}d­¯'[oAÂGÏ3bRk­íŸ!½(…nGÙF )¡-~B4]ÍAðÁ­µä4€P\@€»z8X”qº%å˜:8]aËëÉ››67ðÔ€b³â×é‹×ó,tD`ÓåÁ+-kízOÎ1XAã[yÔ«û kà… Ï LMJ:²vèRu6añF°NýAñ)„̦º ÈûD(ó6PQhØêTÎ*$¢sÃ(-4ÐŽõ¤¥‰„€ièŒo³vÑ­¢¹¼VÈfÄnZ6N®qt'”°÷¹ÞÐf– é¤Ï€M"6AMçF̆Î[Kû3²µ ºSbîP¯<©@bɣ 1Œ\G?Ö0Sr¥PQLL2”¾"èo‚1…•o¸öˆ ÁCÃcù]ŠdßiaVɀ왨°Ÿµ ´H­é `z‹­«Œ…°¿•ÉÔö4žLj{å\_ê™¶½cc‹k³´\Oñºi×Ë8"‚pÒ9^H× ‰ÐN…ˆðÔ3qÿ¡?PlÊãÌŒœá` Ú7™Ùðá>Š’íÝ¬Š !~=±e!ÞDÓGl‹†#ðD€1%ÈŒzPo`OíA×x€9M=}ƒ1Mâ½·xFÌOHд_İæÑOšŠŒ%®åQºÏã.0ß6˜®³VĪ…½‰•þŒPºo£Š&„‰þ2d<êV3Ø–Í5áhŒùDŒpxÕ“eŠXë©å:È)Ä¡»BÜPÕÌÜF © mH8 ÀÅÊ\Í—D/GÈEw SަÍ)ž§ýn-A;þõÄCZÒ9%ä\Ps!+{Ú²ûNOgQŸìA‡ö“>œÚ?Öëk}²·]—æ@³ª–ÌUuSé)ËeÔDŽ[ÂZï²|ð1ÇôŒàܵÞcÈé~ áDÞ´Á¸2'Äk×äM’΄´;EgGÄéÇyo±h¯7‹Ì†fòngÓL/úí8Wúž÷ i¼4œÉBpÑê:{d*8‹ºÒ°©'Ì o-íWW”¡âÛ0à{ßî"9¨2ÚçÛд_{FŽèĴɘ䕠±FNˆ›&²s»‘IC®ù)e²2À‘Ãcêi§Kmµûý¹Þ46–ÝÙŠ]3ç¡l®dÕlÅ“ÊÛ²MömûÞ1};"°È¾‘}E8â[ù Ù3Z#!ûIÙ¶ms,ÆÙ!'b"Gfp‰XëÝ…6²;óFf=˜-uÌÒ{*jY¶v,Ë·ˆìš:ÙyÎÜ«U1Ò\FKØ^Ï%ëe?ˆÂkSäÍ\O™×¨X›^'b"¯é¾k=µ\wHÔ·;Äë…vð?Aé†ÌÃŽ<>²àt¯‘GqDcÒÃ:&}ã«€p:ØsÜÂÚxe"üÔæà3ÿ#·s§©D^ KT*!aØXK¨hZ'ÀáºC3åàö€¾>×Hѯ ;¨\ÈÔ¬1ëOjÚü³¶ Mµ%¬åPq-Àð[Üø–·¶À,^Q©C×Èo&X¦ñÔômð©é]ÇpžŠÚòÃ:¿U¬%NòqÕY@©zb Ç'ynPcêXÞø…¬–#]ãÃͱÖ[‡ÙPâ Cî9ýŒLþÝì.þÉ®?Ü»¿5$‰~`žÄã o_8Û`„‚fVBÃàùún¾¼½øoƒ}}3û£÷>|÷ø”ùŒßé#ÍežÏµ.óY?¾föë…6Ÿ?<üöãÝÿ¯z\ò®ùî²ëKÞØ÷–ÿy»“9 ~¿ó÷׿˜?¿š¯æ?«@£! endstream endobj 4 0 obj 2239 endobj 2 0 obj << /ExtGState << /a0 << /CA 1 /ca 1 >> >> /XObject << /x5 5 0 R >> /Font << /f-0-0 6 0 R >> >> endobj 7 0 obj << /Type /Page /Parent 1 0 R /MediaBox [ 0 0 400.131744 244.131226 ] /Contents 3 0 R /Group << /Type /Group /S /Transparency /CS /DeviceRGB >> /Resources 2 0 R >> endobj 8 0 obj << /Length 9 0 R /Filter /FlateDecode /Type /XObject /Subtype /Image /Width 500 /Height 212 /ColorSpace /DeviceGray /BitsPerComponent 8 >> stream xœì½u@UÛÖ>üó”‰"J·4*`!H*- ‚HŠ " Ò ¥t©”€ "Ý ÝÝÝ!ö©oεö&Ï9÷¾W}ïû1þ¸Wk¯µö~æÈ9Æ3ÿßÿÛ’-ù¾² ‘ïý[ò­dÛùÞo³%__ ò#üŸ-Ðÿ à?þøÓO?ýüÓO?þQÿÞ¯´%_W ä?þôó/¿lß±cû/¿Ø·@ÿ?.ˆ’ÿôËö»vïÙ³{×Îí¿lþ] –ÿ¼}çûñðöïÃÙ½c ôÿã ÿéç»pö8HDL|ˆßžèßûŶäk ¢å;vïÃ?DJICKKENL€ AßÂüÿ¬` Ç% ¡:Ì|„•š”wÏŽŸÿJÑ·mÛJçÿ‹eÛ6àË䤴̜<§Ïœæ=ÊBKF°o×ö/):&“ÿqUЄ~ ÷ÿÎ|û®}dtlÜgEÄ%ÅEÏr³Ò’àãìüùÇMpD‰üÏ ³ûe;Hì~ù¦ô[°ÿ÷´ì¿ìÜ{€”îÈ !©K*ª*—$Ïñ°PÚ¿{EÇþ3ÈãwݞݻwíÚ rúŸ1°—/±%›Ëæ®uæ8ø$´GNŠÊ©éÜ42о"-pŒìÎŽŸøôbñí;víÁÙò:| xûqAr·kÇv êßò;mÉ—[KÇ8Þmëþ&æ{öQ³ž½¤edmï`g¡§"qæ î®_6Fqh±î$Ç'8HDBJ„”„è îÞÝØJÎêß_ÖJé?aK髸 Î÷ %3ˆ‚¶¹ƒ§€‹•®¢7#TôõÆSžÝ¾sÏ><"R j:zF&ff&Fz:j R"<Ô#º¾ú÷*Äoß¾cû'~Bœ99ÃqA¹k.á1±®š2|G¨âîZÅa”¤ñx„$´Œ¬G¹yOž:u’—û(+- !ÞÞ­úÝÿÁ†\;`%ÖÒwü²¦‹ˆeß œùaN~ 3— ØäŒôä˜{7•DŽ3âïY‹â¶¡^`Î~BƳ€¤N@ø¼„‰ó§y8YS’îÇÙµýç-п£l[Ý:Ù³\àïß·gת.¢ñÛ<"š#§%Uƒâ3r sžDy[iIŸf¥$Ü»š®mC˳»÷áQf=vêÜy™KÊjš×´µ¯iª)+Èœ?wê(ëa "ü}»wlþÝ5ê@ÅwíÙ é$ Þ"!"ÄÛ‡5ÀŽÛwã¢b9q^ÙÀþ^|fQUÍ˲I¡N7/ ¥#Þ¿ÅÅìú\Rf®SB’—Tµ L,­oÛÙݶ¶41ÐV½$)tŠ‹‰†„w ôï%Ø ñ»ppñ ‰É©éA¸ECké;`-ü—]û)¹…/éÚúÆ>-®mio­/ËŠõ¶Ô8ÉLަkÛP-ï?H~øï9IE }óÛξAÁÁA¾ηÍõ5%Îñ°Ñ‘a«¶[ cÁæÑÀ¨ã8D ëè\ܼ¼<ÇØ™hÖPA‹1dô\¯ÝòŒL+ªkïèk¯-HqГà 9„¦kÛ0N`ÿA ÎÓ¢òêú·=Ãb&&§¤$'&Ä„Ýót´ÔW“=ÅAO~ðïJõ[òdÍïÝO@DFÃÀvôÄY!Ñ Ä„ÎpCUÜ·{;ŒÞgNLËÁ'¥næö %¿¦cpl|´¿µ"+ÚÃìª7#&ŠC÷ݸr.>q¥ëæÞ!1‰éÙ¹Å%%ŹÙé‰1!^f×/‹óq1âÂÛo)ú7”57²*b Z&vî3Â/«¨ª^¹$%t‚ŽôÀ^¸=q܈šíäCÇàÇ/ªÛ'ff§G»ëòïݾ.s† ‰â~€Î$ñ„ä Gù%UômîǤ<+(«®klniin¬«.Ë–än­§"y‚¾oâ<¾÷/ñÿÙàÆ’PÒ1sòò‹É\V×½ibff¤ ìïI6"<`~éΜ’™Gô²žü³Š–‰¹¥¥ù©ÁÖ²ŒpW#%ác‡a÷ÜBCçY©«†v>á‰Y…•õ­]½ýƒCCƒý½]­õ•…Yüín^•äãE]ò é¡C$TÌH1Æ/6³¬¹baåÝÇß~ûíã»W3ÃmåO#ݯˆ‡»sÇ.À1r‹*:‡&å×tO/®¼ýð+¸ø÷ßÿ ~æÃÛ•Å©¡Žš¼¤gCeQn2ü›r[òŸò{p ˆ)³"n\M×ô¶«ßƒ8UUÔµtõ  ô´Tç&;è+ g¢¡  a:v)Ƥ7ôŒÍ½ }ÿza¼§6÷‘¿Í5i®Äݳg?TsYm¿øì*Ä" ×þüG ¶UfÇûÙh#›rû`­þ{ÿ,ÿ—ÝÅÙŒ–åèi!Œ¿þðÉ󢪆¶žÁ±ÉééÉѾ–ò¬h/ )>.fzÖcüÒH1¦ ¶ Äoo?üQüíãÛåéÁ–Òô0C¸»FF€»—€”Ḉ²‘kXï'€€×þ‰Bþ€þ~ea¢¯©$-ÌÕHYä= Þî-ãþUeµ6JÏËᚨÏxQò²±£‰±—–fF{ê ’ƒ .‹>ÊÁÉÃ/¥jäüøˆß¦—Þ|øQ\ è+óc]/s|­µdøØiI𠈨YOKiYû%ä¼ìE-Âþ‰Åüj:þ:áǬ4¥N!YÞ–qÿвmÛpƒŒœžó´¨œš¾¥£7êÆk›;ûG&f–_¿y \îôP[eVŒ×--9Q¾Sg¥T îÄgW´Àø … ÷áÍÒÔ@SÉ“.FWÄxYhH‰ˆ)è ^Òwºÿ¤¤y`j ±®ÇüÏÕÁ,OOA€1î[˜=AZ`ë*ÇéóŠÚ¦ö^!±ˆoíYÕ›wï?€èXß^ è!NFjrâbdUôl}¢3J{ÇçWÕEíô¯ïVPõ·Õ‘<ÎBKEEÇvâÂU3ϘgU£s+ÈòøsÄ>Ì€,/ÚÃäŠ,áÈ} ó¯&ЛÃ}pÖ¢Š×-\îE%?+¬lhƒˆø‚ì_?¾…yEV¬ïmõË —Õô¬<ÂRòk»Ff–eÿ “A]œêo.I¸k®!#ÀÍÆÄ\¿¬¶mÀãüúÞÉÅ7~C^‡9ø#üØDo]ÞC?+ q^FRèз0ÿj{öà“æÕ2w ŠÏȯhhGÜøÒ D‰±1y}Aʃ»Ö7¯k]¿iåòèyEKÿÄ<’zaqÔ5wÕä%;«I ž8ÆuüŒ˜’S(0íƒ3Ëï>þ¾)æß-O4%ÙiKŸb!?°ç‹ƒ0[ò?—m?ü¼ç9˜òMû€¸§E5°8 Ýø[`ÔÅ€ù;exôì‡Áî¶æ¦æ¶®÷bÒ k;‡¦^ë~ý ƒúï¿×<=ØZ‘à`¨vQT€_DVÃìnTfEûÆ´¯Fí(ê˜àov¸­,í¾ã‹|l”[AÜW€ùν„”,§$ÔÍæ×´õÍ,¼zýî=4ê¿cckDÑ{Š3bƒ<œìÝýó˺†&ç 9¨£öÆà+óã}¥Ocýí5e$$.^ѳõ‹QûØÂkhÚWe]–n??ÚQñ4ÜY_ÿáÞ-Ì¿¢`0‡ù”•OlVysߨìÒkÄÿþûzëûvyf¸ãeþ“˜?oï€ûqO^”×wôLÎÎ/½BlÆ |x»<;Ú]_”àd~CýÊUs—¤‚úÞ Ô¯}õ¿}x=?ÖY•ézó’;õæ_U mßK€`níÿ¼ª}h–ʰaÙŸ¨ Š>Ñ×\ñ"5.,$ø~äÃ'Ï‹«Û{G&@°=úï¿Aë>3ÜUW”änkj g`vÇ'*½¤©dj7`þÇæßE@ ý9óI [>qÏ«;Ff_aÊj0‡Š>;ÒÝPö<5!:"":!ùiNQEMckgï šÔaAÿøþõâôpg]qÖ£¾®wlo;z†>z^Ù64³ü—˜vVfFlaþ ÛIyÎ_5óˆÎªlž…µ²õIô˜–ÅÛk‹³S¢##£ã§>ÍÎ+®¨ihë›^x…æl˜úôpX)1¡þ>Þþ¡±iHöÕZؾ1nGôvÄŸËŸÝòç_Y`ëîýÄtG….ßt K+mœFÕq昲øÛWsc}-UùOcÂï‡Þ‹ŒIHJËÊ-®¬oë™^Xy÷a-Ž[œéiª,ÈLŠ‹ŠŒNxò¢¢¹o| Ûÿü sÄs¼šj-M uнx†•‚g+WûŠ™"vî;Hu„OöúíÀÄ|L¨µ®&¾¦èo›]ò,)æþ=//o߀ð˜Çi9Å ¿…m˜²;¢é3£}mµeyÏÒŸ¤eæ–Öu M-"uù?ÿ\‡:s¸93ÐT˜h{Mê$39þží[ͯ_O¶¡Œ‰÷‚ª9R!ŦÑë’hŒ¢¿{57ÞÛTž“èé|ç¶íí;Nw}C¢³ŠjÚ&V‹°¨O_šìj®)/.,,®¨àÜ~[ƒüÏuàÿöáÍÂDOí‹_Kµ < [u¸¯+èDÊ~":Îs—ôCS‹‘øz-ˆûDѧÛªsS"ý]lÌ ôošXÚ¹ù‡?~VÚÐÝRý³?úæÕüÔhgKC]m}SGß(’lŽù￾{5 Üyf¤»±’ðQ:d|} ó¯&Ûã¾—€‚唤¦•/R;™ÇÖËÖaŽ†îš®Ú‚ÔHs½kêªjZ7Œm\¢Ÿä× {ªXŸS¶·+‹³“#½]]½£Só ²ÿu3ÌÑ*ýdcQJˆý 9Âmí«}eA§ ñHV2\Åýþ)æhµ ãÒ£|ï˜\WURTTV×5½ã–ô¢äõ«˜c@÷zyavr|tddtbz~ùõ;t3æsËþªyGUvœ¥ºÄ 8ë¶Â}]A£8B*¶3Ò×lüæÖv/¼þDÑWõqij ¥<+.ÀÑôúUEyyÅ«ÚÆ·=Ã’rÌW7ÇÿÄ8õ·¯—çggffç–WÞbŠ{aÇ–k{ Sî#3­È4ÄV÷uIÑñHé‹^1v‹È(o‚`£¼ˆBv¾ÌM u»¥¯qåòå+ê7Ì|#ŸÔvŽÌ®åxXU‡¨¯,//--¿zýæí»÷ï‘~×Õ¢.\¿ÿ _˜„+)Æû–¦Ôi6*­~¸¯.˜tI×îÁÝéÏõ˜c °½%Ñ~Žæú×44®é™Ü¾’UÚ„4OüºN‡‘þF€ú»·oÞ¼~ý#oß d ²,^-L‚ÈðÅã }EaØà¾µ“úõeµ'vÕônT&Z‹Ãà·¾t‚î´ µWç&‡û8Z™™Þ²÷ŠM/¨é¶ŭϿP?¼‡¸¿^yô}hüÊk÷°¿ý0Ksí/óR¸™©Iœb¥Údùý—]¸‡h8øåt탓 úÖu´l¨ºÃ&Çñ¾¦òìĈGGÿñiyU-ýãók%ÛÏQûúÕÒÂìôÔäÔôÌÜÂÒò Tx8Ʋ²4?=Ú×ZŸáyKû¢×a8þ²ùW$]C'Nˆ«YxÅfWub:×6ɪ€¢£;-qá¡ÁÁ¡qÉY…Õ-½c³ËŸ¨9v½ ü›W ‚è_^Z\˜ìjªÌM‰ð¶4cprç–šEÙ†==ΓãÓq bê2è¾çgiºÓ2ÜÕX‘ÿ,-%))%ýY~Ym[ßèÌl\ÿ,[«ÏLt·5766µ´w÷ ܧ¦Ú t·Ô”d'…yÙê)?ÉJá ÝÂükÈ:†åŸàÔùn܃Tl§¥ºLMרüëÍFÙSÃ=-µ%…E¥•µÍc3èxÊç´‹âÕü$øH]eiqQqYeMckGw_ÿÀ@_w{smy^FBè]ëÊN³C~à‹]äS~ðMäߺÛúâï)X‚GȰ ÿvîÚ³Ÿ‚‰GTÙÈ5<­Ë|¶‹ŽEpQÚ¦†úú†¦Öξ¡‰díóz ¦Lr±¡Î†Š‚ìŒ'©O2žå•U×645775ÔV•äe&F¸Xê*]8Ãq¥±ø—Ô|ÕVýü#\>½Û_~hÓ‹ÿù›[A‡!áîÎ]»wïÙƒƒƒ³ÿ 9ÃÑsò:v÷çÕvÔ ímü}C‡-ªŽõ÷õôôö NÎ. ›ÔØí–…©¡Žº’ì”øÈ÷Ã"c¥>}žWXRZZR˜‡òÉØ›j+^8ÃIOŽ¡«ùç?fíbØIÞ\Ö¨Kÿ†RvÛ®ÓMøN?{ô?½ø{ˉÈnœ}¸xøø€"¡dàä“Ò°ðŒÊ,oœZXy‹¤ThOëˆHH¶87=91eb i‹~ÿq=G7f–àlQæÃ°O7W7Oß °è‡I©éé©I Q!¾.Ö†š çÏp1RÚÿ¯‘…­’“[µcÇŽ_ðOºôo9ñìòèÝÐaùå¿pñÏŸ\üãÿN¦bì¸ùn\<ȰLFN„œœ‚ŠŽ™ëŒ¸Š‘Sh2œV˜^€)ZBY× ‰)¯Ük~v„a“S3HeuEÇ´DÎŒtÕ?öt°¶0³°²s¾ët?"*::*â~ ›¹žšüy¾£ÌÔÄöþKüŸXvŒœtïÞ½û¾${÷âàìÁr†¬*?‡o?´kçzÂÓÍ.Þ½=hìçÿ…¤ä({×/DäÀ!ȰÌÀÄÌÂÊÂÂÌÄÄÌÊÁ}V\Iï¶_\VYSÏðäÌ<šJ¿ÿ¸¶9²Z^{³™A¦&ÆÇ'@îG^ÐnøO,;l®éi,ÉŒr³1¾q]ûºž‘¹ƒ«‡Ÿ¿¿Ÿ·‡Ë[Æ:ªòâÌÈÂÆÎÁɵ¹pr°³±0ÒQ‘ÄÃ’Ío‡¡^  ¥gf=ÂÎ~„•CxúY.®HTMIË€¹˜‰ž†œè–õ?†Ú¿-Ødü'”)èÀ!rà»OˆI+(«iiëèê\¿¦¡zEIQQIESßÂÑ7ìQÆ‹¢²ª—u­]ý#æý¯ëÒo46[ž›ìj©Yý²®©½wxrn6Æÿ¾¶ ‡ 2 wÔä%‡yÚh\–“½(ùª¦îM +[Èñkceab £©¢ -&pò5Ù!üÍÔjóï„T’ö ¦b`;Êsú¬À9!!!áÏEHHð?ß).ÖÔˆ÷ØÌ>!-ÇñgÎòóŸ=}%<ý,²Äð4à$§Šs{ñFjR‚ÿ ô¥ÛÖr xü ð@øÉh˜8Oœ—W¹¦obicgï`ç6øù õnèÞÐ7¾ež™“WT^ÓÜ581÷IeQa‚u5Vççæ”V7uŒÃ,è …Zd¤%-Òû¶¡æe9Yy%ÕkzÆæÖ·íœœàc t4®`P§§úçDîHS×Þ$4ÌGO ž—º¨ ¨¤¼¹()*\”;w’“‰ŠÿKœƒ˜9|²ÃG¸Ï ‹ËÈÉËËI‹ ó!„§Ÿ2’"ÅêÝOÏY xñEé BgŽ±Ò’ L–ÿYÿÙ¶š¾þŒw´¦á¤T ì<â j7Lm<|ï‡Þ ô÷¾ëbÛúÖ-+[7ï{ °ŽKxœš™[V×>01ÿêíú:²Ã6;ÚÓT™Ÿõ$9ùIV^Y][ZŽ[% @{!šJŸÆúÙk)]”–‘WÖÐ5¶¼íèz×ÓÛˆ—ç]W'{Û[¦×Õ•åÄ…NCˆÜÿYô¾ %§fáæ«WK×ÀÐØÄt3116Ô×Ѽ"r&ʃ¸› ;‡䤰ôe5m]==ÝkjŠRB'Ž „§ æˆ ²Ã§Dd”ÔµoÀ‹Uá¡Sl4ÄH%ñ»`¾¦Þ¸wïÙ bLB"2*z¶ã|brª7Ìíï<ˆNHJ}’––šü8>*,ÈßËÝÅÉÉÁÄ××Ï?ð~tbfaMûà$¦uf5:û€ôC¾,Èx•š]\Ó @_zí¶@ù@kEvB “É5%YI©‹Jšwܼ‚G p?$(À×ÓÕÑÖÆrR§¸˜¨Iöíþ{в„}äŒÇ¤®è[Û;»yxzz!≠ö/wÝœïÜ2ÒV’8ËIOzdÛê„=Lvˆ!ç ‰È©ë™ÛØ;:ÚÛ˜é©]æe¡:„»a`ÚeBLs䤘‚†¹-¸øŽµé«²BÜÌ߃Ôpmß[^€Ùë>\¼„‡ˆÉ(i?#"sEÇÌÒÆ¤=Ë-,)-++)Ê‘•–u?ÐüF®ÎÀîÚݶspõ ‰Ë(¬í™Y^ë|úÛ×Tö,12Øß/ 4:1«èekÿØìâù!‰Ì̇;_æ&…ºšk_–¿ }IÝÀÊÅ742þqRJê ©)ɉâ¢ÂC¼\í,ô5"whw)ÔZûž,a? ûI}+„­2&6.>>>n½À¿ÇFG>ôr²ÔU:’ Ýžßó‘–o†cç.jÛÝõ òs¿m¤.{î(ÊS¸þ|¸ –Ûq!ùk¦öwýƒÁžn¶7Õ¤ù9éHð¾eÏÇjù…µæ8{aöz¤T4ôLl\<|"ÒJZF¶îÑI™y¥Õu-­m­­Í µU¥ÏÓ“âƒ|=\ìm,Lnêëß4µu Œ{ZÒˆ0>ýºsH"Ò]W”âãîæî™ø¬¸¶q¶Ë­(-ÂÓJGIJLDLFY×Òõ^äãôg/ò ‹ äçåædg¥§>Ž ôr¼¥¯&'r’ý0züæ_÷Ä!ºv€”[DQ×únpLbˆ?r^ ’ƒ ö/Ù™OGºYjËCºí¶ÿ´æÈhë)ñ«Fö>aq‰))‰q|îÜT?ÉB‰¶k­ÃüØXÄvFRÝÄÑ/,>)%åql¨×m}e1^&òßnz~­XŒ{7€{?r˜% óÎã'ø„.È*iÝ´rñxœ™_Q×ÒÙ;04<<<4ØßÓÑR_U’›‘êëvÇÒPGãªÊU-ý[®ÁrÑŵöt´Uu´«¶àIt ‡³ƒ£«wpLJNyc÷È4¤„üí·ß°äÏâünë_‘•U5´÷N}^Tñ²®¾¡±±±¡¡¾¾®¶¦º²¬8ÿyFRL¨·ƒ™¶âùÓÈ^Ëßqz#µ„°E[ÝÌ5$!#·¤âeMm"µ¨`þRS]Qü"-.ÈÉHåÌ%¨pã¶oTò³¼‚‚O?¸k©%s†²˜Ì·­V‹Q¸aˆð ,µ1³qã9uVPLòâeÕëFVÎ>ÒóÊëÛzG'¦gfçfgg¦&F{Û«‹²SbC¼,ô4”@nuIM߯+2£´yÃŽ:–C¤®0=6ÈÓÙÁÁÙ# üpümýãsˆ¢ÃÎÆù±îºüäPgc5¡3§øD.jš»‡%¿(¯oíìîíƒÒÛÛÓÓÝÕÙÞÚ\ÿ²¼¹»Yé*ý³=UlßÙ‹:¶~±…ÕmÝ=½ˆô ‚ùKwGkCe~ZÒbɺž‰ìÌ÷“ÐV2r}ü¼¤º®®º8ûq°ÓÍËÂ(«ÑOë1‡Ýá Üb*&n’sJ_ÖÕUg= ´×»É¿Uç6 ßþv$8G75-=+ÇQž“|çD.HËÁÜØÐÂÎÕ÷~Ü“œÒÚVH–W€¼ZZœ›îm«+ËI¾çj¥¯~IZü‚Ô%MSçÐä5Æ€Ṳ=¥YÂü=\]ܼî…UTÙ܃̭Aî×·ËÓC­Y1ÞVÚò"§yxN Ëk[ûÆf•5tŒŒMLB™À¹?ð´½lÔD×;Ø×C4œ‚— C’r«[ºÁmÇ'&à'PAÿ4>6ÜßÕTñüÑ=„†–’pïjïÕzÌAt€ÇsU-¼¢Ÿ–Ôµvv¶ÖgDy˜]=ÏÍ@‚·>ÚGûɘOHh@ž†Òú¶ÎΖšÂ´p7cH{E„.÷•ñþëN*܃‹Opˆ8ïÃŒ¬ì\Ü'Î_º¨xE]VºíݼÃa”] ÒîñÈþöîÝû÷H›âÊòÂÌø`gCYNr„È®.KŸ¿¨zÓ>ðÚô¾Ö¾ŽtÀN´Vç¥Å‡úùø„F=ÊÈ«hì†3J>~D:d SBT$Îrs=!¬ kø8·¦cò BYX˜Ÿ›ƒ6f™¡ÞöúòÉ^°GŠçï{¤ æÀsÅt K+®_gzvnaaaq½€¿ÏÍN tÔ¤„“bØ©Ö™Þ ˜oßOÆtBBÓÚø²¶ÞÁÁÞÖjHS¨!~‚ic/.ñÛs€œå”´öíÀÇy/Ûû{Z*³c½,TŸ¾Õò]{à äÔ‡™Ø8ñžæ:/){IYíÚ C³[·Ü½@М”‘S\ÝØÙ?†Ì–Àm3DNÅåùI88žýøþ]k}UyÉ ró{›`þá lŒk¬ÈMÉZXXDÌÃÔg…UÍXÌ?À¯«æÅCÿÛº Â'8pð )è9„¤5öC h+ìÍüìôøPOKU^j¸§¶ò/uÀtG…•Ý#2J›zF¦æ–€Íz îŒôU¿~ó<6_‚/U_ô侓>Â8ø%Ì÷à“3Ÿ’ºf”\ÐÐ=<>>”ŸxÏöšäIf²ŸbŽs€‚õŒ¬Ž}hjqcÏÈøøPgm.¤:»ÀÃH²ÿÛŒX®UËÅŒGŽòž9'".£ ¤ª¥{Óä–­ƒ«§_Ѓ¨øÄ'™9…å5Mfné5ºcöûº^sØøÔÕPš•äj¡{UAV‰Spb^]ÏøÂ:ÛŽí~éj¬,ÌNOIJLJÍÈ.(¯ƒqóH¢Ö™#-5eŽae纤ïô ½¬uhòÕ ÐèÀÃÒÒ2쇜›ëo«ÎK~àfúOzž1AÔs·ð´b€Ò$À Íx¨ñ‚›¾3Cuð, ç9PÝÓ2×íCž7÷OO÷5§ ìeÌäŸcN@ÁÆwñüF-ÓÓc½ ‰6š¼Œ$ßh¬ËÔKHJ ðÓçÎK+(«kë[ØØ»xøÞŒ{”r¤‚’ÊšÆ6àù&gW°{¤˜acÌŒ¤ˆ¨/~wÏ„qW¯jê[Ý {´srñÍZ ‡öHÍMu·ÔVçç¾x‘ n]×Ò=81·ô" £ö–²ŒÈ»fj’|GY™Ì œÃ2ʰw ]A¶cæççà Ë«åÅÙñ¶ªœGp¶Ahu¶á‹I¨mçT¼ 7ükÀr›œ…[þïÞ#äðÙ矟îm©zñ8ðŽîE”ÄâK˜S°ž–Ñq¸Ÿ^Ú:89;;9ÐRò$äÎu™Ó,›aNy„ONÏ9üiEÛÐÔììDSQr ­–pß sd —€„š‰ëä9q¹+š7Œ-5÷¹ ÌnrZfN~qyUmcKGOÿðøJñøÉÈ –-` Ø©¢ôØ{®ÖÆúúF·œïÅeU´bè'Öm“¾‡lû#ý]­u/«««kê›Ú{’‘·ï!æðœHÕlrUü4' 3¢KxfeÇ(l¯ü N³­,ÍÏL@kz‰$_-Í÷¯Î0m8s³oŒÄpÔìürºvñY%um=CcÈ{4÷ü†¬*Øc=6ØÝZS”ãcuM!±ø ÌÏÈê:>È(0ÎÍÁ…ÒBíu¾ˆùYy}—82=779Ð ŒÂík’ßs¸ÑCDÉÈyJDFYËÀÂÎÅëÞýH`ÍŸ>Ï+*«ªihnë„[£ãS³ ÅãçC¢ÐaûZmaF\ˆ§ÓmÛ;®þQO ÌÜFf´…˜Í‘Á¾îÎŽööŽ.¸š¦ÑS€½X˜ìk,J}àb¤r`Î1¿ ¡Î18…i«›!{ÿÀÐ(¤„ ÏŽõÔƒ¸ÏÉ@qãù››}ãa~ ¯4,Ü$f£,V±Í{ópU‚ø­§­¡ª0óaˆ‹ÉÕ '6”L¾„y9€q~~z¨íŸ`^Õ123??5ØRòÍ1‡gYÒ9!$£¢cjçæý(53§ ¤âe}€»opé][XOçöç'‚mfì¨-Ê|äçãò¢²u`r•qƒ92w BýÙ鉱ёááax{È £ö°Ù¿C ¯œ?ÅÁ¼Šy‚ùGd2qz´¿«­¥¹¥­ /èÀ±´WeÇyÿƒ™d”À’Œ‘GTIï¶WØ£ôÅUõm½Ã“óÈn?ŒNànïPOk]eÑó´øPkA„~ÓšÌæ˜#kÑÓ /k›ÞANRáÚËúæöî¾áQØñ-èë7oßo$üû\Ó‘ÞäúÒœ´Çq1qÒ^”7aÎbØ8Œ5 :ÌÅù¹Ù8p îºSøsÏŒt¾|ñ(Ð^ÿ²èIv&&6îu˜¿ÿˆŠô´ÔV–•–UÖ6w @`eey~¢Ã=ÀÏþלÞèØ<1-ÇYiUÃÛA‘ÒžV6vM.ÀŽ`ÙAÀ11ÐÑPQð,5!âž»þ‰Ó{ùoÆ{(é!*æãÒWõ­Ý£“² *êš;zFƇ ÔÆ´>`;Y7£ùXDïMUÅ/žefåU5uL£ An3w 3"zà ­²ðŒ•…‰þæò¬X_y!6FVnÔŸÌ?ÿ€,Šî¦Ê‚çO3žfç—×w NÎ/¯¬,!Ñ~Ô]ce‘ct™è"Ð}„LÜ‚²ªú–÷ÂâSŸ—ÖÃ#_^¿GLÍÂ$ÊmûÀÿîó*RGÈ ÖE ÿµ˜oÃ4¹íÄÁ‡›É‚²j7o{ÞO@š{‡€û†ê÷ú fôÓŽõ/>=Ú×Þð²¢¼¢º¾¤¾È);›N …‚y"È€9ü¹VÀšüw©K=ÊLOÏr\ðÃAÌß½GXa›+^ÉKê6a)õ=þàÝ[„H¡Ä¸¡*/!t†›¶cìÚ|Pæ¿óÕ‰”={q$¥fàa;ðçwC2‹ë Äò°þ#¸×ƒcò¹™ééuÍÎù™?°MÐoQÈËž= v1Õ=wœ™†œŒ’žS@Aó7𬞚ƒ]­ŒôõMl= X!ƽûåóXeH†ng­ôK£?¢•GBJ:äg°Øíü㳫;G€qG,IkEf´Í eI¡SÇŽ0Ò’؇éEþä–‰¹,&©_]*ÿ"æÛ>Ú—Ðü×a;ëqöá"¡ a<‚t=é[»Ç?-ª˜CFÎ/hè_ƒŽÆäËKË ÀìþÙÇÞ¾‚`=Ðò`s-9!2""2:Žu˜¿yƒðÁeÅøØhªª^3²ó‹Ë¿Ý,0îÀÔç=ôµT;ÏÃûSàÉÛˆ¬ ®µ…lÇÎfÐ2±ó Êh˜º‡g”A>œ7o_£ÇúÜw1º*ÉÏ}„šœ¶ŸïDŒÀÝЮ˜} óûºðBäp ÿsìÛ"M‰ë·)ÞŸMT~r¤³ž˜œŠŽ‰‹÷,Ús7$þ)ÚÆ¶²ùŒðߢ‡ þîí[HƒIíþÁ§Ð¹ã¡ŽºâÌø g3-9a^VÒƒ„$´üòë0‡JXšæf¦uYNNéš™†šéÚBzÏF ž­Lˆ»{'œ4G Ñß Qƒ}@û`_ åá#'D.ë;„¤7€,ãíëE°xàiì:ò‚Ü,@Ç ðöá ÓE¦±s…›bÞ^žþÀQOžŸƒ†hÿ°Z°ÿ3Ì×5±lxÜçs:Ÿ]ùåyGL¸Ž´á3²qqŸâ‘SÖ2°tò {”U 9V7‰·ÿ蘘üã*ÑÓ?Y(äƒí5…±÷œL5–³Òâí?@Dþó×Ë Ty¸ÃMYqqYHiÒØ?µ³µ¡–$ˆƒ´ÀÝ»gÏn »vîØþ fÄÛÔ‰LŠíÛ‡‹»€ˆ‚žó,<ì'±Ügéõ›•ʼnžÚÜß[’§Ð’îß ‡ÏvÀÁk8̸ ÈNäžèâ›ÄíÃiÚ妢ÐQz2\œ];ЋáÏþ÷˜o[ µÀËb·c³9ÆWîܵs'æ»n6Q ST؆£îÓçD%/*^ÕÒ3±vò‰M}QÞØ…¦ªÿæÔ‘°ì÷/&óŸ|yÛËü´h?cuY `4¶½x‡¨lÀÕæ@;]Eqaá —®Ûø?Ê«ï›\\y ‚¸6 ^Nú—1RÀÇÇÇÃÛ¿Ä Qy¤-dß~<<|DðöãâââS³ž”P¿åû0¿¡îoV&ºkrâ<ÍTÄx)îG߬œ½`Ùî¹{':¹ø#Ü߈ùÌHGUV¤»É•ó'• çÝv ®áç¿Ãü§‰Yd~yÜ~ð–ûöâ ßaã¤äºIKd£ï…™¨üd“ =ÍVΈHÊ_Ѹ®obyÛÉ#àA¦÷mCýw0_ÇêüÏÂ?d†BÞZ—écwSUZà8351þ¾=»÷ं¿Ð:̧óØhË‹ ð‹\Ô´ô‰QÛ3±°òž§]™áf¤,vxa 22R $D ðq÷u‡_¸H—)ø7âC„À’À?DÁ›\¼ráÚ˜ƒ`0;殉’ðÑÃðÐv dÈô5p„‡ˆˆˆ‰‰$8€‡N.ã·G×c¾0;Ú¢Io¸|Œ…–‚ä ô»!Hwû_asƒ1±5ìY‚Ï#$Àß =®gm[„W—‚÷_ }¯OmLVpð‰iX¹$.©#Ón^þ!qIðäËæîá)¤ õ_ áþ-ÁmZ«rS½lõU$ù2Qáºc×>ÂÏ0ïoÈäg­uQ˜OPZÍÜ3öy Š9ˆè«ŸE{˜«I ps°01Ba §£?úýû€9ßà 5=###ÃaZ*2"B|üÉ‹ª˜yÅçÖõb0ïz™íntYqÇ@Çáj!$"¥ ¢¡;|ø0-5%9É¡ûAd·XxDÉæ ³c=uyîÝÑS?ËÍÎt˜š¾X<àâ]{ ÿó=Ⱥ±õúǦ£¡¢ %&Äh®KÖÎ!G®¤¥£££¥¡D÷ìütL[}d<Æ/©¬czÛÅë^hDìÃd„G¿®µ–°±#_r ½,´¤„{‚üHœ‹âúsR²}†9³°lVcñ“0w‹ëJ’Â|¼GÙ™Á“à"0»1«x3ÌÉð÷ìø™ Ä?HJIË€yÜñ£œì¬ŒtTd‡°ó±?`•|7¸’½’ëØ±£è{Q¢c®ëí;:zqˆšíäùË:N¾p#íiv.ÜIk€£…“³«óÁ_ó?0¶ÀöçÔOkÝËNsГ/ íb 7ÃX2‚§OñK¨˜ÜÆb™ÜsúÛé«\=/..~ATèì .:JR"" :N^>AÑ âÄÐÿNABDLÁx\äŠéç˜_€˜ãìÞ³”šž•‹ç4¿ ˆ¨˜˜¨ð9¾“Ç9à<,@a$Q⻈Å|qiaj¨­òY| “ù ÕKÒ„ÏžI> ÙA «ÞjsÌq€QÙ…ƒwpÃãÄDùÏð…·€“˜ˆªÿ°:ßJEÏÊ [“…EDE„Ï=É Þ‹3»®†„ÙY¢ã8+­nâè™øôEQyU-²Q>€ ¿ùžéWÀáío*}íc{Céü)öÃp¶oÇÏØˆÅü駘KŸ;uò¬øc÷¨g«˜÷Öå=r4¹vå’‚Âeå«jêêj*вçxÙi©¨hÙyøÅdUÔ44ÔT.Ëžççf;LIFFõeÌiˆð÷íÅ=@DIÏvü´àiùËW®ª^UQ¾tQR”ÿ3-ÙA|õá»x‹ùòÒìhw]aZL€«­©þ5U%9I‘³<ì ”@ÕAyˆš}SÌaóˆ;öìÃ>î<ú8Õ«W.+Ȉ óñ°3R“ 3Ë?"e%á’f=vê¸RQYEEEYQNJLà$ x¯ý8¡±$Ü\çä´­<ßU5´ttCZŸñÉ™ùEÌÁxßÈ´cø_< t¸yEü4;v “ÙüCÌß`Nã u»e ­©u]ÏÐÔâÖ- ƒkWd„Oq±22²p–Q†µVV–¦×”¥yØSQÒ0s‹nŠù9Zb<|Þ0=-"­¨ª­odjnaanrSWóŠ¼ø¹H ÓÁb>2»ôjyòÐæ¥Å…ú¹;ØXÝÐT–ã;ÆL ³O|"j˜}~†9Ù×P˜æqÛÌЦ!î^>¾^îŽV7ÕåEÏçä<~ZTNÝà–ƒ»·¯¯·»ƒ¥ÞUicÌtÔt,ŸbÞýõçœtd‡‘Ó±ñœ“º¬e`î çb½=\¬Mt2&jRX,d?+‡ÅnìÎÂ3‡ 2“ã#B¼Ý¬Œ´ác¥]­2 Ÿ`Nqo?!ÝtÃyœ¯¯—»³¥¡öY‘Sœ /i'&Ü‹OLÍrœ_BQCßÜÖÑÍÃËÛ ¼—é 5ø^T ÞPöE[?9øåtl!Lc÷rDý+ìÖé—Z#¾æð¬…Þ†‚ä`ÈÎÌÍKhèœá¿ƒyÑ“kK+;ï °è¸ø˜ˆ Ï;Ær¢|'OòÈì<ƒ"bããcÂï¹[ëAOÂ@w˜•gsÌÒS’RÒ³Ÿ‹ÅÊÉ+ðAT\ÂÃp×Pÿ»wÌt¯Hãf¥£$§¤çä_ÃüÍ›•Å™ÑÞÖÚ²‚çOSÇE†øºÚjÈ ó²Ñ’ƒÂ) €b>:³€`ž 1g¡Lˆ‹ òqµ5¾vYüìQX2ÀÙµk79¬ªž¥£ç½û‘àKÅ!ïeoÞ d»T(™Ö'sulg¤×νK”þÛ¿¸·ò?Äžœ ƒ/mÙ³ì4DxX¾…ó ˆy¤ŸË;wÿ°ø”§ÙÙYiÂ}îªÊŠž;'*£zÓÎ'ìQÚ³çÏ3Sã‚Ý,4eøá=˜Êæ¯1˜]:ÆHCIÍÀqJì’¶™£Ïýؤô¬œÜ¼ÜÙ™©#ïÚªÉ÷@OKËtT@^ÅnÓ@ö”±þÎæºêòâüœÌ”ø0_G³kò¼¬´d”ô\›b~Š•šä€üôùË×ÍáãÓàãòrsž¥'LJù»Zé©Hñ# ïÅÙ ; °œ¦É¯èÇi™Ï_¼Èïõ(2èîmð^çŽ3íÙµ¶©‡9‘‰÷ÂUS÷ˆôÒ¦¾ ¸§P‚ôo„ø*æÝ5/âa±ó+%ájwúßbþYÜÞ 1÷wsttó {‡+Ë‹²“ý@(sá‚´²®µgXRvQEUei^z¬¿ÝKBܬ l¼Ìû&—^¿Å`òsan–ÇäŠ:·Ü‚bSž”UÕÔÕ×Õ¾¬(Î}ú8ÂÏÁD㢠Ï&FÖã‚ zNXÌß¿GÛ4û{:Ûšëk*Šž?‰ rµ¸&'ÄÍBBI¸;üæÒ§ÙhÉI)9u^醕{pljvA)|\}]MUYáó´„^v†ªRg¹@Zƒ‡ˆ’鸠œ–™ó½¨ä¬üÒÊ—µÈL%æ½ÔeŽÒ“ÀY×Úƒuè‚—ô‚“ êѶµµãÈ¿ àbËï†ÇeÝÒ>`/Ö ýæ0?¹ÈÏæ5æ¯` W1wrñ Š}’[Q×ÜÚ\Wöüqˆ‹©–âEYEMçàGÙeu-m­ •¹É÷ •DyÙ¿€¹’ïfŽ"—t¬<î?Ê*¬ª‘nOooOd›ÍMƒ[@Ó9Žpð ÃÁ óØ¥´cOŒ!umÕEYC\Í4 Y9|˜v|й¶ôöÃTTtl¼¢Šº6ža×=®»³µñeÉó”(?{ëgØ‘(ƒ‚ž‹_FÓÌ5ž<_×ÜÞÕÝÝÝÙÖTS†¼—±šÔ`3÷¯oÛDNRý¾š·¼c³«@Š9>ç[½ úo¡?¯Ï{äo­%uŠåŸ`žÿÈÏJKVèÌ™sRªfž1«u¸ñžºü”_€¹wèì’ºöž¾ÞŽú’Œho=µË—UoX{E¥×wô ôu6”dD¸›\;q„ñ zn¬,v’‹“Wð¢–ÅÝI9eõí½ƒ#cã@F‡û»ZjŠ2ãao‡”ï±ã§D œÂPÌA΃ EÀe éåP_G}Yö£ 'Cؽͼ®«s=æ2|Œt‡Y€îjßò ‡S¸«ìëlªÊOñ»£¯$vz à$®;?|VRÛ Bðѱ±Ñ‘¡¾®æ—…Oãî9ÜT>Ï h£¢ïÁ‡´—o:ÝOE§M>üë{§ÿÌáQˆSp–#èŽÎE>ô¼¬¿ÄÖÛým®É‰ðŸ’ѰðŽË©íEëíc]5yI¼]]¼ï?~^ÑÜ34:ÒßV ²@Gm5Umc‡À‡9Õmý£ã£}­YѦ*b¼lÀ¶cc¸u˜G»_¹p†û¯€”ª‰KHbNeS÷ÐøÔÌ윎œžè¨+ÉŒó·ÓWç?y‚ï¼ÒMç5ÌQЗ‘iÊÙé‰¡îÆ²¬8?[!^6`oºnÄÜN[æ,3H'ù¤ÔÍÜ$çV·_}ÜÌÔøp_[Maz”·õuyÁã,t´ ðQÔ»ÿ¬´¡s`ä\ÈÈ&¸®½¶øiŒ¯­Žü9.:âõýÙ? „ Š“Ò´òAÛS5¾濾{53ÔZ–ærSQ‹Žhu aşš àæö¥ó‚‚bò׬üн‘•×Ës£Õ9B=ïÀ•s*ÛƦ&GzŠRÃÜoé_»¦oéö µ°¡gtjzr¨£*;ÆÃäŠ(+ýjÜÞ7µ†ù]æÙó—õì²+š{á,ãâ"‹óp”­¾8=Òó–¶¼?ß9 eØÃ«eÌ@ÝlòK w7U“<{œ“ë¤èåÏ0¿.ËŒ•ü“¾CУœªÖ¾Ñ©Ùy8+»'1ç¦Ç:j À71EGºØŽ ÈhYzEe”4t¤ ÎÕÂi]t”¶ðIø]35ñÌëgæ7*zhÊ÷St”af´«:;ÖË\õ/Ùê¸È¦˜#ûçÉAözÊRbbRJ7ì‘}od/u¸V<Ýìlï¸?|^Ù681331ÐZžícg¬§g|Û;êiYËÀäìÜôHg5Te%¦Ñ‚9²Ç07U•äã–Õ0÷ˆL/i잇.#“‘Ë‹s“C5ùI!ÎFª2¢‚"R*F.ë0Gfê–á12¯^-/ÀfÞÜG÷à¨íÉãÇO‹)~†ùÅsÜœœÜ2ÀÙÆd–L &ÏKÈP4¸ÍâÂìÄ`;4YwôEOå™ÁÖ²§Ñ~NÖ–6ÎqÏ*Ú'çæ¦‡;ªŸ'Üs²42²t ˆ^Õw¾fF:«²"]a9Æ œ(÷¦˜«I‹œ’PÒ¿‡ß;ÁÇ–^¬!ÉexPkųXkË’bçeU]ñ12ð¶4?;;‹N9/ÏO4—¦‡»«HðŸàå;¯üæ:r‚'ŽÁÕ`à’\Pß= · §>Cæ ú³ã}ÍeO£<,4dO8-|ñšX³‰YÄÌÂÇÍÐGÑëÌ®žça„óš?nPt<’ÃG….é;„ | ›°ðõA‡Å×™¡¶òŒ7ce‘ãô«ôI0Çö@¾šn+éqKWUIIMÏÚ+:³¬WØì ¢‚â'ážvæ¦ö>Ñ@£æó3£Ýµy‰!î¶æ¶n!‰¹µÝc³‹óŽŠ§aNzò\Œ´tÌÜ"Ÿbžía¦.{^äüE 3÷ˆ 86 µ|iaéîx,/¹‚ä'#59IIyu·UÌ‘“ g&ÇÇÆ'§ç‘yºÑ®—9ñ¾VZ…Ïœâ¿bä¹ó;:ò§xNHkXxÆ ‡¦ÀãÀÓ¦&ÆÆà14tðÒ]µy¢+‰ ò I^¹ VG!H¹fäó³iòÔ ðþ“Ãàº{·¯Ëœa¥$Xw"zZæã›an®)/~^ê²®­_BÎË®‘™EhЧ'FGFÆàdäòòÂÔ`[Ef´ç­ëJe/iš¹E<­@k2ò¹ÉÑÁ¾Þ¾‘‰ÙÅeXïo,J¶×S=>2Ø?0821=;;=ÞŒ ¡5?lÜhEw…öA‰ùu=ãóëéû¾æ ÷æ’'œ±c¥¨¢o‚ùÛ×ÈNJj¸—ý-3skgÿ˜ŒÒæþÉL÷<ÞßÞ䆎¾… 0]ÞÙ…¹ ðdD¸ÚÛ»úGg”4õOÎ/´Z`©ÓltÔ4L›`ãi¡¥ %yQÕÐ1†;sÀ¼ z»»{Ç o_œ6$÷Q€ê%y¥kæî泯޼}Ì8¶775·uŽÏÂÕ2 -Y¸«‘Ф€äUãO1¿qIìì™sW ]ÂÒ…j èjkjj逜¸‹PÑ;¡±°ÑU–•º¨BKHãî±®ìéhkë웚™jõó8/ó«bÜô$ëǸ~º½Ì'ÄUÍ<¢ž–·M¯§ïûv ÃÃ×À±RLYæ ˜¿{³~¤ŠìG|Ý]\=£Sók:Gf—ÈÖòŒHOk}- mc”Ödlv~nòŽ%{º¸x'<ƒçHÌ/B­+xìo¥.~‚…–’šñ¸ˆò*æo°˜[^S”•¹¤M;øm¦ äýíÍ-ý0 [멱(5ÔÙDKYñжÅÝHóåׯa oWSMEyÅËÆÎ¨é`}tVƒ›škÈ‚€ObžµóûÀð ˨›{ÆÀFû™ñX{CuyyemK÷ðÐæ9`³ ’‚Ô.kš8#I6ðæó3ãÍu55õ­`‰MÏL§“ˆtÃ>Œu=ó>DZNyÈу¥ò==B9Ò’õpÐ" À¸¯uc1G§Lºë‹3GÝ ‡ÇòµL.@Ó>Ù×X˜âlzM劚žO,òÓÍÏÁœœÄ0?OO¿°ÄœjؼÀê­ÏMðŠÀDMAÅxL`¿s/Km%99e(¡ :ÚÛZWU^QÝÐ10ÿòÌîZê\U¾ªcéÁ|åœÀj¬,|ñ<§ ¢¡úæ¥98N—àk­­pAXTFÕÄýÌõ.‹ÃÜSÛÚï!äcO ´×•åç<Ï-®nî1Ý"4î t·¸~UYU÷xX f™êj¬.-*.¯i鞘†Æ½äI¨ƒîEì×Z'ä?¯«ºƒ8esBñ7ý×w+³#í•YÑkÁæO?¬ïwÆðLÀi²Å)84ú<-91éɳÂêذ‰˜v%ùÙê]½$¯ •Æv°ñx¤³6?5:ØÏ/…Ú.ˆÞìXw yQ>Æ@ENÅ€bòüõ˜ßº~EáÒUýÛȉÏc³ ³ã„‚ܼ¢ O°`¤£ Dî6ú*jƒ(øÈ¸>ÑßR•Ÿ™š”ò4·¢©|xq~¤&‰ [S”=/«†b>²s% aaqE{&6öƒ›ƒïj(ËIO†ßò%ˆÎç`ueVŒ·¦ºÖÍÛ¾qÙÕª¬Å–ê¢ϲsKkÛúǦ¦'` ŽqAF”ŸÖ·Ë [ª4üunßPÓõI×€êå?¾g«-s† Ãʵæàp8øíë+Šòsó‹*Pò¡•× jDtÀLó’´”¼š±Ëx û Ü{ꋟ&„‡%<-nèAІ¦j—Îq¦ £D0÷Úˆy¬·•ŽŠâeuCû ¤‚†>ÌÃÁªò¼¬ôŒgU`¥?:®üí µÔ4ô¬Í-oìŠ"wàHî+ qÒÚ8º·Ê¸i ÃÓQÎÆï¢èß-ÏÌÒœo* q!ãf?n†ù{„•`v|°«¥¡¶¦¶¡µ Ë÷êõÊ2P]OÁ^SX«QÖ·N.ñ ÜûšÊ²“¢Ã¢“²Ëš!z 3Ãí U“;Ë÷²è7Ç\÷êeeMc§Ð$X‘A{MaVÊãÄ'Ù%õ]HS30Ø {r0ÖV×Ô·öDN Fúá[+s’¢Bƒ#?+k†Ÿ†Ø–> uº©"sAü¢º©{Ô'˜Ã÷–¹ŠvÚ†à½ÇzJ2††Æ¤æ½ì‚ëw¬daANæúº0LM‚þÌÜÌxkU^Fb|BRVQMûàøÔäX‹ñ^f*"Géˆp?Á=â–Îqø?|QÉ_Þ}üÆFqÕÏ@£ºfÜAfñæ¿~|ÿzinrÄÏ] €Ff¦1E2¤Õ梨À91…ë¶ó¡zgõåIBttBZ.tþÐ&µ–¢ÄNl4¤$äôG?Ãüy¬·õ U¥+×Ì\¤•´€O€ûZ*^Á1iE0óVÖ—¸©ÊŠKÈ}޹Ái¨ÿ&nà›Âþà“jòS¢½½üî?|V_>v¹Þ2Ô7´rB¸Þq€ùXoSùóä¸È¨ø'¹•-}£“#@úZ¨Š?LŒ»kæXEç½9A"3µôæÛ+:†á¾§fs ©¿n‚9Bû)xŒŽŽO!B+ËÅŽ—/Øð÷ì©Ó‚2è¬Ãâ@æRS˜™ôðarfamÆÎ6C~.É,T$Ä_Ä\Mùêus`Ë`°•¬±4+1:,<69§¢¥®'Hhêb~CKû¦WtV%¶¿¤“‘þî.n¾aIy5]†Ãm Y3V““”×0½û9æÄåÔÍÀ¯F{¦†Ú«rÝ÷vuñ¸“QÜ„®_„^ÉÝÚø¦ Z‚è˜OB‹ð(òþƒ¨Äg¥(+a]Þ#¿[jç¹é?åÜÀ(:õ>™kÖ~ñ9/á4î¿5§ö?A™`^AvBo_—åcÃP±}Ž9< rü¬,-Ì!•ÍEL=8ÐÂÔ0WUI~ÞcÜ|âhß{×( Üá°+ù’ÓrJºG°ÄSÁOr‡‘‚˜ˆìðf˜ûXë©]QÕ±X9,‹ÁŠ^}QFBxHh4Ø“°ô÷ÀÍRïÚÌGÑ >'{GÏG9°Ü‹ ±U>p3U‡E; ³˜‡Ìe.HÈk𣝠rLàʳâƒî:Ø»øF<õ6ˆ9Rjô°552½í•tn / "–Œ¢ÚŽ‘‘ÁΠ?·Ô/ðlLÐQEÿiÇ|R†ã"J†Î`é nµ|Ë0{Òâ|£.p²gÀ?l†ùïØ£ùÐ tßjr¸»¡äi´·•¶œ /Ç΢È&0’ðÇí…'¤§gåW6÷ÂJ%’7Å{›«ˆ£'':´)æq>6zê*ªØh™Bëª+H‹½‚Á’Æ^Ć Tvî·ôµ¯Úx£˜Ï£µ“G!wímï¸Æ?«„A°Õ•™‘îfòRRŸcî0—PвðŠ}biXWh){àbwÛÁëAr~]7xXcàyž·ÍŒÍì¼"ÒKšW1O»ï^hljnUs÷À@okUN‚Ëþ sÌD ¶3Æþºõo„ùØã_{ÛVõÄÜÊÑ^¨¨Ã/óR¢‚üüCbÓ‹º±˜§GxXlÀ\Y•ÏÕö–“_ r†,T~ˆ¹‡9Ä\a3Ìeæ×nÁ,æ%Q¾N6Öw!,Á6O`º¤€þvŠŽQrÈ 9>ÐQ_úì!<á LSÜm;X$¯€CŸ™š„]JÝm yé±N&êÒü\ Tä´l§$5mîÁâÆ´’ÃÝMU%%ÕMÝ# „[€óáo\äc£&" $¥ãÚs s¸‘0zµW¿HŽ ôõ˜Ö# Es£õ˜Ãü{.6–ÖN~ÑOËPÌG;1˜KKo޹„óñ¹ÍõÁ<bndjë†p]#q{cIfÂ}ß ˆG¹Åå¥yq o½tŽkÃúzEGöTõáÞ\vOõ_œ(þw!ÿðv¤^Ã=­5EYBÝ-G†G×ìqû¦˜ÿ†@¾4ô{d6–6ÕVd%EÂTÙsǘ(Iˆˆ)™x/¨Yú>Ì«ï,SµÖþí}yŽf{•Ö ®X‚3kc0¿<â«|¯h}7Ö?¨¹{ëzIÞùÄH?W+ýòb|Ë—.f`båݪ},V Ž^u·5Þ¿{ûvÕàÃÂak Rv> *ÅÇÆÄ°x"̱ gÍ:äó¾xÞòðVAÆÙp¼Î¯ç9`y¿¹ÿW0w¤`>v{þh»½gÜWâ\¬-­NFfâÕÿrð¨Œf ó÷ñ 9—œššáë|hŸ¢$?Û(fâ1 çT‘ÝãŽëž{† 5L߆+Þ§tXåÀ¬ )¨Ê²¼Œ¸O;Ó}*±–¢‡»L‰É`F±çmMïUV”•äf§ÆGœ°ƒÞ Q;\´ˆž™SxóÞ#> ùÈÓE ½ùaMUUMý8α«v•ð^yXé39øjÈ?÷ŽÉ¾VÓ„üŸö¦ÚWÒ΄‡Fa®{pš0Ç)8€*‹2b‚¼½ý"°N æ]Šów>rÈâ˜GXj< :u€Fñ¹p¿“ž^§üƒBÂý<,t·Ë ¯WÀ /ô…‹ÙÖ«èÛújý3àëVF`¯idšrÈ Íž×Tf'žöq9 öÂ<ì¤dá—˜¿ûãOLÙõ´¾º¢4ÿbVj™È`w; ƒÝÊ2ëx9–Ò-¤¦†ÎOÌGrù6ö’_tµ4<¨©­{ÜÇù«Áž§÷ŠS!2¹jÃø˜CLfŸ¶‘µÇéô¢Û›ZZšÞ½v)%6,8,&åò:œœŸF̰›p."0046½âpC¯HÌÑYbfvÔ5(ñÊmÂ3ÁF\Òi?ãÎÎXOÖÉÎÊD[]„ú£È9ƒÒà ¼IÓÜ-ö‹î—o>|üý#ð±¾.È¿¾dïžÌ1¥ÈKp¤®å¦Æ¸3ÕVß²~-7æÕ£8óß™¤7o—^ÎLŒöõ:îpÔÌ@Km³ñ—óçÐ>œˆ¹xRàãÐô¨®çæ©€ ÄÞÍœÌô´ Ìœ`®»wŸ¥KPÂ¥kwï×××Uß(ÊNŒ ?“1bÝMæœïièÚÙÈÓ1I9W« Œ×zT\ßÐÄÄÒÑ÷,”ð2”DûŸp´9rØâ¹©1(ŠnœH“¨’ZÌÆ/©¬g㟋s¯ß¾ÅÎÐð«Êö)†ü¿þó×ï9ž a'éíR”äbc¢4Ñ’q¸Õ£1ÿãwhK«¿]”p:Ð븽µLÝ,½N€tˆçÍ™3šývc×ÓdryèÏ Š¤±åYï Ô"@t$ÎÛr·ÜšKèhéÇÅòjÚ»wk£Í=2õòÕŠ;•·Ë‹s3â£ÂÂ"㳊€¤¯»iÁ¼wèõk̶Rz)-1!å|A%è¿õãD”ùCSÏH¨nê†8-ÚÜK³Ï°³=¯¼™íðùLæÃoß½~ÑÑX{ûZqqiEÕ£–n²þ«þFNìIcí½:í|ÐæŽµDì 3ÚÇÁ\w§Ò&iqáÕ|«V°.!K'PmŸ9’SuNÊ»qïQSsÓãú÷kàCðõûï&›s\ùˆü‘¼Ä`×CÚjr°­<;åú/1ûñã;œË8íqÔPSi“”(f×acÆßÊlLN1kÞ¢%œ" û­ý’ !Ë‚ŽÁÁÏŸµ·cÕ•7¯‡^´a¾HP:be˜s¨ÔTÞºMMËÐÒÁÃ?42öìÙ3±ÑQQ1q8cÙ†Öâ›WóoñÏÿæÐŠõáý›ÁžöÆUwîÜ­}ÔÒÕÿ šx(Åý†{4÷è“Zf„;Úò-„ ã–z;¤×­æYÉ΂y­&¡/!§,tÈ©î?ìq:íÊõ;ÕÕUwn–—•ݸSû¸­gpÊ‹#)=È5¥™§ÑóªÊˆð.g¦P&þö+¥å Ì?`¸ü„cÍ­Ò"Ü+ØY–ØÈ·•L+ÖÊkYŠ¿r›>Ðiô¼«ëù‹è@ÆQmizˆ=$—ÑÓ,ü„yMë'®°D?[C­rr›•wé<âèvÒ/0$,â4@ž %]¯Þ¢½öÙô`þ v´×=O××?jn{Þ7üöÃG¢¡ãB´§•Þ.u½&öþ/‚Ü>,ôªą̂“ÇìRZó#¾€Â7æŸrªÕ mOE§]*,))ÎÏ͹s¹èFuCû‹áwSÜî€ÛÔ;nçAc†º,¦[HAœø2ÇÁüîi€Þ¶#ÚJÖðpßÞ¢…ÔTø/ -²Xv×!O\rÒ7 EÊýÈ6ÖtN¾ì~rŠH´¡ˆ„náBÚ 0?¦¿]^Zr½Œ‚š–ž©•‹Ç)ÿˆ˜ø”ì+¸N%æd×y=Qy›Qç‘~ä×0GÂÕô`,ÇŠg¿}&Q€Œ8ÀÝ ®E8¨ø i­§¶QˆäßšTÓƒàz¦[ÊÁ³VBF^Aaó&YÙÍ*{:%Þmîzÿçzè˜E¦§:lu)è}.]6 æAvºÛ@#~¬P1Å ˜»qù9àfº|¶ýNQô¢J-µäG³Š™vÁ|*À\a<Ì÷æ+8y1W˜­Wx"òË4AÏ!fÃý÷4bŽÏó?Ab:šŸ£³ýI}²Ó¼j+I­áãX'£¦oã“}KÃöw·‘Gú~%)È®Lª N¼+PZ[ÂÎÅ¿f¨è:áµkE$äTuq+}Cçà”èÈ„{?ÔÝt÷ аmÓl1˜ÏBu™æ8Ë+XâßQ`ä·ÑVXǹdÕ(ÌK¾ÜÛ¹¹x„$¶ì4´öK¼xµòá“g½˜ù¯ùÛÁi¶Û¡£¹íé<š›êªÊ¯¤†cGø+ùEåw9øÇ],«izÖÛ?Ð×ÕRwóJRˆ«º€‡uñÂyãÆ]Ç,ô™s€Sxç*nîU\œ\<«Å7kš¹>½®½ÿÍTÒÍà&†Nö>´‹™þRŠôû0ÿå×ßzu“ã‘Ùå;ˆÌð¿@+˱Ë;¬bùµÈŠ š7 æÛ$…@»s“ºþ‘¡ò»Èz{ñò5©>­˜cõÀW=O?¼_[ƒ¦¥++È:ëïh¢!'ÌÍÁʶR@\a·™sPBnùýfèM„ú_Ìqc„®àb¦›TüRvlÎ<êEô d°l3ó2à£R3tN-©Aúzè„ ×ñ°üüi×Ï”ágü0æØYcáÅYBcåÖ‰Ãn¿¿{ g½,4å„8ΟGE3æ˜+Lh­¸œšÎa·ÄKתµvf6ðáN/æ”8ÑŒ\^†ÆõòëeW‹óÎ'„{Zë©nâd]º”e¥ ”â>‹ã¡Éyžv¾è‡ `ä¥Çx[ë© ÿwÜÜù— Ïž "tôth0,fóýÖ¾Pp2¥:‘!ªU ”%x°øÌ˜ÿòݘϧeæSÔ³J+©Å‡ã¿þMˆ÷L°ˆŸ1Pƒĸ˜o”²Œ¸¨„¬ÊþC®Á —ʪÚ /Gª㆚i܈·w·>ª¾Q’Ÿ›“sñRîåË—/]ÈHˆò#jžp §´ŠŽÕ‰ˆÔü[[º^ô÷u·7TÀð´»3PÕŒ#äæ‚0Ô4ôÌœk7iZxÆ^º9µ:nDí~`t®nåZJ6œO æÈpGÎÚ:mÐíxҲ鸪0ÇáK‚Gúñ'ÁÜFWm“´´œÒS§€ø‹eÕÛ»û1› NèvO“ GÉ«µ7T_¿’šxî\BRJZzzZò¹è`O[cÍ-b¼lô*aÙíúÖ^‘EШÖ×ß÷¼åáÍ¼Ä G㲘ò« Kþ€t ‘à@Ç¢ky ÿ5…˜¿Š9Þ‡÷ȯ]IÔfN!æ4L+×Êï±:Í_ÝC”:^ÌNxˆN†*àßÏs‚CH‡Â&y%M#[ŸXd?nïé944ôô`Ÿ¶˜ ÂüÍÛ׃]͵åyéñQa¡aQ±qçÎÅE‡ù?b¨\®Œ´44tLì¼¢òlOÅd•Ü>à8ºÓØ5uáW0áÞ@º +ÂŇÃÌ™RÌI•;S÷蜊GÏß’7Žƒü½Ä‘²_A„s Íü9b`o¬¥®¦¡mîL°#Èû{»:ÚžiàÕ;PÙœ&Ì{2’û×/%„rwuu÷‹:æçvÔp§¼(; Í"äZs¯Ý¨¢mq<$é2²ãð‘Þú°"7Þ÷,²1PÏ|sÿôòð6\bh¹ì2sºº’SF%†ë%^´ê¦8†:{ŒšüaFÜzV¾õjÆ.èTBÎ…Õ”Œ T^!E•˜Bù ®°øó¤@‡ƒû±:§wl6t0À*ï#號âÀÌ´åR毀t¨ª$3ÆßÝÑÞÁÕ+ ’ÏÅ„xÙÔÚ*!°‚y12³c‰-; m¼"Ó‹î:Ë–ˆ5)¸Ö'ÆG‚®Ÿ?íB¸jTsfM„yJ°ëa##K瀲±t¨¯³ùþ­ÒüË— ­ íîÓTYCö.ÕWäž r·³¶qô >›™WzãfEYaö¹`Wó½ŠÒ"¼<¼ü‚kÖ‰¯—UPÝ­oéìw&ûê=Xè=˜/ÒÛj/Pó|ûæÎÀÊ+©¤‹îðò­úŽ)b”A#ÂÕX>p„‡vj0Ÿ %qk6i‘ŒÙ8œ˜£yÑŸc6@eäªÑNŒyQJˆÛ‘ƒF¦Ö'ÂÒŠp%åð`O[}einVzÆ…‚5Í@X1˜¿ìkxãR| ›­õ1çSI—®Þ¾W[[u£ #Ê먾ºüzQ‘ub’Ò2›¶lSÞ¾k¯Á!;¯ˆ”ü[õèDìëjª.J ²7P‘$I~ÿ>æ„:ÁTbÏ1ñÈïî¤ËTƒ]&(D¤¥úÄ7=E˜ÃßÉ€Ò¥[ àqæ8úGÌkKüùüÙ`^]”ênmv³3]«ÅUòý˜,=!.>%§ônCÁ!:m˜mÉŸ€ã¶Ö¶Çýb2 oÕ>jl|XUv(ãw+ËËÈÈmQTݱKkï>m]}ãCÇ܃À_{ÒÕ78Ð47‘n°‰Žöƒÿæ9(¿pâ÷Þ)1ã×Zc—i|è¾ó_È’8iu‚xàÙ8kD²j…”yiæÍž9k"ÌÃNØ2·rò=ƒWÐfû¢£áNÑy ‹EFÁü‹þó©ÂÜÎÆÞ=(>çZuCK[Û“ºÛ)¡®:*J*êšû ŒÍYX27?duÌÕ7:£¨Y›ƒÐ¨zûò˜x×߯|¤¿eçAg°^ {ñÇ[ZÀe¢˜p»‘ Ç8†nz 0'³,Rª˜x`$'HDÿ€bâÔa-äªy3 æÇ,-ŽºÄ瘽ìi­«ÈK‰ ŽJÊ-kÚ1?àfwÌÁ#$9ßOž=ïjo¬ÆµýöjîÚ£grØÖ鸻»›‹£Ý±cv.ÞáÉ@öß30ˆ¶$d…?Þ;Šú¯ÄBç“Ø¶ÿ°gTÖÕ{#»ûŽÍgÌÐF˜p Ôs¦óùP§D´";HïHWíVn,°âèß„˜§…{ض´9”€i&††qÏ͹0¿S~añ$ÓÃ:w³·uò OÉ¿ý¨­xßj˲c¼mMõut-íÜ}‚ÃOG„ùx¸:9ºz…Äç\¯…â¸n Õ$­:ªo1â0 0.{ßnhë‡{ï§`w'£pÊ2ÃqÛ?6áfŒÌ9%˜ã’¸u$ñÀÓÞWæ#ß° àî$˜{ÚY>æ’4B Ô\]’u&ÈÛÓ;øìù«DÿÈ7cþ5ž‰Îó@w{;gÂÊÇÏÀõn­+Ï9ãëhaltÐÊédHlbZFzr|d·»ëqÏÀ3çK«›ºú^"¯rt"é0'û[èY¸×mÖ4u IÉ¿ÓÐÑ÷ûû(SÊNol]ÔTaNdY(ÄÍÝØYÃI{JÄ+ÌSÍžùÛ¤˜[ÙžMοCò»5Þ-ʈö÷t÷ ˆÍ*½×üí˜ÿm>Ì!Dbî`ïr*2¨ûú–ÀÆçç|ÄÜÜÊñdX|æåÂbä´'Ç{»»yÄdÝ}ü¬˜'È„ñvø´¿s\öÎÈÎ/©¸ßÊ#2¾¢Á·?(ÄFD@›ÑGÙºÆ( ̘"Ì?•Ä‘$æoF%í1‰ÖßÁ÷~¾އ´(ßÇ=ücЛø æ¡ïæj«C¾ZÐ GWŸ¨Œb<ߟŒµ%Ú‚Ïfæ_¿uçvyQNR¤¿× Oÿ¨4dt!#îe_çc¬=² '°F‘yÿ-б·Rh£š­/²Ý‘é2ôîpØþ‹ÒµtóR¬'«,ÿÒ„›ÌqII<ÐÐ ÎÚˆý…¨»ÔÙF7sLUYI`žõÌ+!lêêèâ üpŸñù%æ‡3¶G‡Qõh®0g'7ßhàúŒCÈádDòåëUÖUWfÅ…œòôQ‚Û:°®T#¥oƒpÖþ>æd5,=˪uò»:C‚©¾½ïÕ‡ï<Ò‰ö¿þø@öc¸š¨Ã±:ê;œ̉’8VþõÛM€xàavÖþƒ]5‚b‚²¿LˆyqžÛ¢óøÓHÌa{¸yÆN~ž'`QZ¤›óqo0¿>ñ@úNÆ©¶›àl&0¯¯¸œzÒÕå„?ð@>%È!aoG˜±vöA®ÙíºÆ'MõUe—’Nû{{D¥0¦ $ð W‡ñ[1§ìîÈvßgyâtFqU#4¯}{8ãý‚­õ !,d¯O©‹÷8ÿÌÇ:k#%qØYûϨB -ÒEœ slà ‡éáðyàål¸ìk5O&´Û1­;¤Ä<ŽŸð‹L+Í÷êcc 9ßk…ïÕΟà{&ø^“#|Üݼ‚â.”´g˜ûó¬?ð½:û¡/¡úqk{[óƒ›ùé1Á¾¾AÑi·Q0ÏǬ‡k¾sÊî¾dÅê ªú6§Î\'eèÝ·9lŸðþëÏß?‚"|˃—ÎzÙG$Õ¦s²$ˆj[_¼äß"W ˜9ç&Ã|´¯ö^cuÉù³!>Þ~á ËïO쟷(·¢íÖ;(6³¤ ³W‘¼ÎÖ$¯ó8\ÞÊ*»Ð·Å‡Ï ^ç;…iÑ'½|Â.•? yë®_8ãë|ÔÊâëe5Í]­ wK²ã#BÏdç9H unù=뜴ÝÐ/ã–·äõ·¡7CTž|#ÞMëë¡þç­õwÐKjW¡åL G‹AMæ¿bâ q…’¸§¸ çߨU#D Õ¥Øp–ñ×™ÄÛAÍËÒK/‚ †…ÚjÊ.&ž ‰IÍ#âpÃãa>ŒéÊÑvèq.m  »2Âß¾“àoÿ\§a?Áßîÿ‰¿½ªä|\˜¿_Ptêè>%…ÃÎGŸr°²<âèƒóúÝÝM5×s“cÂÃ"Ïe—`»”á<'3ÆßdÃïèdãWØsÈ-< dlÞü-‡mï‘x¿y54Ð×Ýñäaeiö=e©Ï*9¦óO%q•Mà¬ýëß„«FPLHñµ¶“ancq舳Ü¥ ’/¿µ®âJÆÙȈ¨„óÅ•`—‘ñö¸Ï1­œ[q§C#ΤåUô)«è=ƒ²¯‡ã¼ÿ3‚÷»7Xëïù³Öæúš›ÅâƒHå^,‰ûi²ïÅü g(‰âÛ ÖPȪµÖ–¦‘¸TlbÌCÝ­™v„xûCˆ·¿|ÑÑp·ôb깸ȼëГOÉ¥ŽÅÿçÎÆêk—ÒâÏ ¤c \O´ŽO¢Ç²mÛvíÞ1#z,moe'=s.Ï×õX *ìnmjbfãó^½/ºÛª®åf$%¦dÞ¬#8Æ;U€f zú†˜ åeÂîNÇ̹Vv‡‘½ÿ¹ÜŠº¶ÉÃqcñþHàÝðn{ÚX¿êfi^æÙ ·ÃÚÊë8±ôõÔc>‡š]`â$ µÿú×_¡E®ˆˆCc’I0O u;jvÐÂÞ;:»ìæ‚ÕÄuƒìæG\„ª’„«6óâ‘uõíAX_ÍÈÚ3G+ H|uwuvu÷íÀ|9„oú_¿ÿðáÝë!d³v>ëêNº·oñÖ^˜èp@cËi9åýG>Ó¼Çúç’ë7ï8`ú¬àòaŠ3ô#]@úçÏŸÖ\ÍŠ-!öt°¯§+ÂÛXoï.5ÅÍ2ëÅÖ òrr°Œfvs™‡<8t¨‚ülKMqJ€-¯OŒ9β%qQÀYUrÂUÓ†")Ib.¼3…V?AÇ4È5V^Ið·7Ù»sç^£c§‡•P;àî®aÌÝr½XkËkð8ûÇ#7¶q_ú@ƒƒ/‡€ýçõЋg閭8ܵEJTLzÛÞÃ^gs]~p»U®&›Ä……%äw;¡ìö#ðÍÐöŽ~g8„€¿ôYcui&Zæû”6ˆ‰ˆmTÖ¶òŠ>í²ï^BëäË—èÊa¥xt;?9ØÑH}ƒ ãÂï;Î!w÷y4‹ÙøÄ·îµpH+ªlè ê@óþÞ·ïì´Ä3oû£‡ŒõöíÚ®´Yv=&Ÿ]Éβ”‘óNÈLù svA ó©hïzýjðy3a’*¬ãZ21æ£Xâ"ÎCfí Úp){«ÆD3w&‰9TWì;â›Î¨>ƒcÓçØÝjªÈT M-¬·w˜%Hù½ûðáý›¡ž–û khoarÐÊ%¯×4uôô  Çp½Ä”N¸YTe£èZaI- ÏØ‹7ëÑYLh•‡9«ËŠ®^-,­´ß yF%U¸'ÿõ0!Ùû¬¹¶üR<°fʉ®X-¶IÃÈ! áòûÍÏ`¥ÁÇn¯¿»­¡ª4+Êóð¾­bÜ,ãÉ4| ès PJPZEÏÚ;æ<×¼ ßÃ`_ÏsÀ»þ~õíW /g§#¼Cü¼Ž;X[˜èïÓÜ®´á-²šŸ{%+ó’Åô‹. y>'ëˆ'š !²ây¾ìþÓç°,:îäóù¢Úb<ÌYx$pf Ê _¿ÿ–(¼ô"²öÔÌâ -:÷±· Tvv?ïÑÏGCµÂü¼¢r;ØùÅå\«jhíêEPâÞçmµ7ò’B\͵$¹¹¸WKÂâ OÍ¿y¿©ýy/š n¯¯·«µ¡êZNœŸýõ«ÁjŸ=9ëÀ¤ ÿŠÃqlÑ0²âš{Ûž÷öàÕ3Ä!ñ¾žt6rïýšêÊ[ä¤ÅE„ø¹9—³-CxÓÒP#À猰u~s6~i5cçд⻠m]ÝÏŸ5ß/Ïùd\Æ;º/ç ÌiG9è}¯Þ!Ï ÚŸÉÞ¢bˆbëÉiòŒ¹PVÛÔñ¼ûyûcbݯºYn³ÊS§ÀsÀ2ñ´ù™J`,!<ûûz;Ÿ>¼]îa}ÐÀÐô˜GxJÞõª ›ššŸ¶¶#®§ÝrËãšWRÂÝ-Aò—‡_DNÃÄ9Zwž¶·=©»…i[=åõBܜ܂ Zf.Açr®VÖ5µu>G΋ޞn Œª¾ž—áa¥£,-ÄÅÁƱjÍU=ë“Qéù5èÞ:ÑT=€DS]åÕ‹ A.fZ[DI«ï…œH¥Ï[¸˜•Wl º­àÄܲªúæ–¶Ö–'îWßÁxg$ õ?éæhc |úêÊ rÒoŒ7#Â-ðy$à¿Î˜r„94˜òJ*ØœË-¿×ÐÜò´©îNQz„›ù.Y¡ŸÉ¹±áÈuŽÙ ¯ÞGùÛw¯ú;°4äHûƒÆA×°´‚[Ÿ¶n@¡]ÿÈþmü+9–¯Z¯´ßâxp|vQEuÝc4è ?¬¹EhÈé«Ë ƒà'3;Ï:´»ÛžŠJË+»Sû¨éiKkk 2«+Š.œ >nIá ûjÿùW@'’-RJû-ÝC/–Þª®}p¿¦úNŵ¢<„wÜ'¼µA?AnƒÄº5$Þ €7‰÷ß0ÇÑ4n±­È˜LÏ/¿[óàþ½[¥9ñFÛ‘}²xÌgœÅ¬|’*†Ž¡sä_¿U GD$ñu å¡j`çw6»øfõýµU7 3cN3Pß$¹n”i{PgÏ^]S[¯ðs™—ò ‹¯–߬¬ª©­½Wu«¬ ;!ÜÓÆpÇ&Q¾lì+$µœŒÊÈ/¿Su÷ÖÕÜä°ã滲˜…ÚÉUu,]bÓr‹Ëï ÷[W‡º²¢4/3>ÄkÈñ²/a cXÊÁ'¾EÓØÎ;"ñº·»5÷ï߯EK¯$7ýLàñúª×p2ÓýÐ2ÿ…ØÝç@²eÍF5=ôÎeå_-»VR˜—“™Làítì°©¡¶ÖB?áÍ…ð^ÊÈ@·ho‚÷뀘ÍS»:ùŸÉÈ-¾vyÃ<¬´%xG±MÁhp8CpÃvcçðL`ú~ƒ,®§÷ŠG2,Xœ”åߺÏÒ=äÜù+¥×¯—•äeƹګ(-,(¸Vr³†Á·ÀØ”ìÜü¢‚|4 ‹KËÊoܸ~µðbj´Ÿ³¥þn Pñp:›œš’œ””œšy!÷JQIii 2o’cÝîÜ,Æ¿‚e)¬PyMSçôD…%Åù9)Q§l ·oâdabZÊÎ#,£ª}ÈÑ;,.í•¢«e×/*ÿbƹÓ~®V›Å€´™f! ýÒ’[5mNÅ$Ÿ¿\XrõÚµÒ¢+9éñá§œ,tÔdE¸YIþÏÁ|$'"‡>~·À脴̬Œ´¤¸è°owŒ·Ž–Â{£¤è^.àÓÿ^¼ñ„¸3–Cp½ŠÎa×€¨„ôó²3SΆzÙíÜ$ÂÕú3g|Žù L…ô†Ë@JÊ KåÛû†ß‰we>IBH‰Hâs)¤ Øx„œIÎȾp>#AtD™ÇÜ\<«%6ïгt:—”œ”˜€FRJzVöt3©ñ¾®V†»Õ””¶kX:zø…„‡G9—œšž–šäåh©·c³8ÿŠeŒ‹—­Ú ªku=QjFzJ\„“ùž­Kàêâ‘QÙkb}Ü'4ú\rzfÖù¬Œ”„Øp?w[3uy1þåK騩æS-¤g^)(©°Íê—”–‘™™žr.&Ì×Íæà>Y6FR³ì0 DZñŠÊ£÷àèåìïM °i¨n“Gx#œ‹ÐOÀxÏÿ¼ñ|xçeAËBCÿ°ÓÉ€°È¨¨ˆ`_·c÷(J ,g¢™?Ú&% ÷¿“#ŽîÞ¾þþ~¾¾¾~þA!á§£¢£"Bü<- v«l–“Û¢¢©gvÔÞÉÙÙÑÞÖÖÎÁÅÝË'}AþÞîVÆ{Õ6¡Uμ˜v¼? +ç“¡áaè'ìé¨Ê¬åbaX´ý+÷Zé­;uM­<|CÂ#"ÂC}=]ŽÂr|Ë1ùœ¹ó2,[)(.¿}Ÿ±•ƒ›·phXXH §³™Þ.Å „LÝø<ß :TÇqð‰mÚ¾ßÄÊÎÉÅÅÙÞæ°ÙÝ=o)À{ÕÞÔ#xÿ½üKÌq-ž€ä Ó£ö.nîîÇŽYj)aµÅ/ØÏ0ê¿Îœ…^ýÒå|b›w™8§ÞÅ~7è°àèÇv‚„ð7sjâZ#­¸Û࣫»»›«ƒ5zm[× ­bc^²”u%ÿºÛ4´˜[YÛ³±¶>zÔú˜£óq7w·ãN¶‡Mtv*ÊJ‰‹KÉnSߣwÀÄÄø€žžž¡±™åÑcvv6Vf†ûvlÝ Â»|)= 5P°­@ TC=‘³« ú ãýÛ!ÇÈDKMEEMËÄʵZ\Ny—¶‘ù['ggG»£ƺ»U7K­åáõ¹9³fÍšƒ@g^îm뎽†fV6öŽNN¶èBM•M’èÞ™hLäÄîŽ6ô>EewîÓ726> ¯½g§š¢¼ŒØ àÂ<¢—ðþÛÛ¸˜ƒ§À°Œsµ¤¼Š¦¶‰©™ÙA#½=êèލ-޽?r*†e+øÅ6©ë[ŸŒÉ&R8ƒ]’JÛŽ4qa»í\hm)lߣ{à ™™©‰¡ÎnµÍRkV±¡Í–~13Ï ÙmÛwíÙ¯­£½ÿ¾}ûuô ŒLLMMéïq7Q!AÁ5¢ëå¶©ªkh¨«©())©lß¹{ŸŽž¾¾z=ª 2â«WúÕ¼ù h³p IÊ«¢'221A?±SIíKéÑú;oÁ¢ÅËVð­•”C3îÕ58`dtÀ@wßnuEùõ"œ¬èØ7½Óßtú¥ìÜBâTwîÑÖ304Ô×ݧ©®¸IJ˜% ã¢i.};è³çôÏ’r[U¶«oWSÞ xã+Âp¬—ñCx“³Aˆ‘…SPTZ^½Ð;Ô”d$Öàïý‹ÏxñQR-ZŒþD\^]ÏÊ= —xô á‚¥;WÈhÎHR™øˆi—À»“Ù¢¤¶M¡®ª¸yƒØj9=ÍBd,1±®ä[#&-»YaëÖ­ [¶lQئ¤¢¦¾CCcÇvd¼ Äy¹V®\Å»ZDBz£ŒÌFiI q L°UQIIqëfÙõ¢B<+XiaSžƒPedåÝ€žh»º:zrR"|+–1ЀæÄlôÁÒ/aãÄ3nÙ¦¬ªª¦ª¬¸Enƒ¸°À*æÅ‹ä¿’-#Ë žÕë¤d6oURVQQÞ±µü\ìKAfkÖT@N‰ÁRÓ-aãâ_+&%---%!ºv5÷¼ ½Œ›˜ #²?˜ˆ¸Ô†27¬Gö!?'Ž|ñÏ üI¼¤ycå’˜{ãAK7VCn«+¿}œäž5"ôÏÃÄÊÉ'´Nb=1…„ÈjÞ•,LhYΟOE½ˆa)ÛJ!a »^'""²NT\RJzÃÆ¤%Å„y8ÙY–-ca_ÉÍ'($$$(ÀÇËÃÃÇ¿zˆ¨˜¸¸˜¨°?ˆÑÓPÍCë`Öìy h)O$½=ØZ@“iæL´~©i£¹ù…DD%$¥¤$%ÄDÀåegf¤ÃêY8–… Õèû`Y¾Šoµ°(úÌ$Äñ…+Ù‘Ù@¦¯¦r ¼$ÚÅÌìœÜ|ü|<«È€*°}“þÕÉ0" ÐKg]ÎÅÃ' ¸ZPÎæñsr3FN¡õÛvY#¿x®;û‡(!ކ €=ŠS˜ ³FÏöb/?L!ÀÏõ‚m)Vþš3{Î<ª…´‹—°°/_ÉÉ…'''×*n^>t)º’›s9|ì ô‹™–²°s¬X±œƒ•………•c'7\+‘¿ÊùP¾úí·1O„^ /7‘q"ЄŸ‹fdX²Œm9ç*^4xVq"iÉb:2+EˆÖsç'33+ÇJ.nnòBÚ…TXÏhj ÿ…üÀ¨hè—.cecgc]F®ï)ÆûÓdø01³°²³spÀ„Ÿž~ƘVŸ Ô6L³Ÿt© Òpš¿b8¬þ²Šù› Ig½€†nñf6˜ Lxosˆ…7­uºÅŒK–ÂX²dÉRæeSv4ÐÍÖ*‹hé321112ÐÓÓÑÑ3,f‚ —1/Åñ(2£D-Ž<ñŸ2NøßñŒ4t ŒKÐDh,c^‚M$*Š^Ú âÆ1'3²héñTËÈ ©©&VûnæÌ_°–Ž zºEÓ‚÷§ÉˆW@ ¯qñb0d. †ç›&¼?M†Þš ÍFM™püœæ´#H lNF¦]¹QÓˆ«‡±Šì¥8]% ^Ö1Dy#¯ø¬‰)¨¨æäˆ•‡î`.‚u>î|L}=r3sæÌ†nÆÜ¹$Gò\¸þ„òzȾ üú€>›jÔ ¤$ ÒÝßfâ)ÑP¡?1*G1êq)¤Ìä•]88Ì o Ù£.S8ËèÉÐsQf5á—Ó͘1"ô«DöX¸‡¥äݨmê *Äz@},5ÔÕt„stЖ|Éc§ømDííWâßGO—Î÷ï#WÌ„ÿ?ÿ_øf}ŠOŒÿDø’Ñ(°ãÄX~ã/©Ï¯œðÂ)Áa„ÿù·ß¾=Àö“³‘tÓøýN0!ÑËÀÆ'©¢oë—Sv¯×aI¢Òó1'`†k挶þÈÅ7ÑÜáò>]ûEÞò/¿Žý¿ä“çýì‰FÞàMþÀ—?1öÊŸ®œøÂŸ^¹¦aŽÏf™\|ã^H¤F7¨;CïÐØ¾|ÙßÓÞx¯,'ÎßÞh"&ûñfø"Â7vŒ¹ôkc²'ÿ’/¦üú úú…?8¦†qçœüß R‘Õ²;ÍÜNg–ââ–þþ]­ Õe—pÁW+þ›ŸèçøÑ1ã×YPö"$§yèDtä·uuwwµ7?„Ò‘\Gðw”i~ŽÿCãææî‘™%•›[ÛZšÕÜ,ÊŽ>~X‡RGðašŸãÿРìí2;M]ÃR¯Ü¨~P_ÿàÞ­«—Ócý]-uÔd„W±üxÁÏñ¿i6œÀ†íüqqÚ­›å%y™ç½Ì÷«ÊŠp³1Ò|UQòçø¿4HÁ{>I%#aç2/æåå^H;wÚÏÍÚxÒFa¢täçÎþO3fÌœ1™u›w™Øy…DÇ+úï®ìø9þ'Æß¯*úÿeü?H¹‘ endstream endobj 9 0 obj 36751 endobj 5 0 obj << /Length 10 0 R /Filter /FlateDecode /Type /XObject /Subtype /Image /Width 500 /Height 212 /ColorSpace /DeviceRGB /Interpolate true /BitsPerComponent 8 /SMask 8 0 R >> stream xœí½Án9²5ìwºËos·½˜Ù 0zú¶Õ]ÉÈË@¸Fß­ž§_r~"xþÃÌT©ªdK6c!¤²2“d0â0 ß½›4iÒ¤I“&Mš4iÒ¤I·§ÿlô­k1iÒ¤I“®¥ æBwwwú﷮ݤI“&MzÌÏ#"ßÿÖ54iÒ¤IOlõ Ýÿýßÿ]ÁþÓO?ÝoT/hÀ×p1}5“&MšôšI}/õoEòú·"ü—/_`«× >~âÃߺâ“&Mš4iL´Ø+zº+’Ãbÿ÷¿ÿ l¯õ×þóŸÿó?ÿSïOxŸ4iÒ¤×Lvøa*hÀä˲Կï7ª?UTÿõ×_a½Óñ>á}Ò¤I“^!Ñbz׋ àõú_ÿú×üQÿ=N¿üò ~ª _†ÝªƒÂÄöI“&MzU¤ÀþåË€v…ôŠäe£ßÿ±Qµç+æÓ-;ÿ+˜îÿIô¢ÅMš4iÒÛ¥Šô±®ûí· ÝÕJ˜W$¯7×uÅßJ§êcxïÞ<$R£ëï‰M´Ÿ4iÒ$P±‚3PºBeí ãp³Wë]M÷ ìpÑTªà_¯ýõ×þóŸùú÷úúÒéêaÅÉPL.“9A~Ò¤I?8©I\mõ ’õoöŠäá—`¨Ãå^¯ëO°áë¿°ó«a_/®7ÝÒi ·¼–ÂÚ~ùòÎ<‰Ø{<© 3NMš4iÒ!u³WÛ»ZàÕ¯ÖxEïzQ¼;luDˬð/Üì?~DP þ½ N5®úðë—QCÄaþ*„˜LFõ ö šL„Ÿ4iÒ[!Ëñr±«ÙBÙÕbW ‡s†Ö{ý%®ðâkh ½4×tÁ¤I“&ÝŠhÖÒ_¼ÁL¥Ÿù|/„ŽéЮvr5ÅáŠÁz„'¶ÓžÀÖçº0ÝQÃsÆ5×T_¯ëEe*VÿòË/°Ø1M@Ñ\É…×H'XÞ­/"zM›ûg'MšôªÈ\Ðõ/Wás@t À¶7ž|2…½1øÜì æqlÛaƃèÇÃÛŠ¢ðŸœ³¢jÉ ¢íª#¨@‚2 <†®ðÂq„Ÿê5ï˜Pü±QýT½= šþ™I“&}+RT§ß¸Â0ŠKŠ´´á$çªè±ªë§Có›þszc€í¸ ,=5ÂM” åàÀt·äÜ«~ ´.æbôÑQ†ÿÆ9¿àÌ‚>~Âû¤I“¾2©û…q}¸¨˜©©]*”UKë‰0›á‚€I ït³×W>þ tÅF$Š!„ÂHV[]a“A’õ;ïß¿‡í}<²ÐÄ=­ ¿ä8Rÿ"Ò@M¢‹†¥ë¯x>%ü §}e sâ«l¡4iÒ$K™÷ W'«ÙY³"'¬küàÂb"à9^*\0óÒ*põ±íªc%”æ.±õWÅv8ºëß>ðÉã`Hv8êñA€9ÀYÑ›CÖsíZûøj1–Ý}Å “&MúaÉœêê~©@ï¹9`ì‚;kšp; ØcÚ¦7u%,‰êR)­biÈ**Íuº»Õ=;ÁæðÒ·ƒ„6ˆheX"XÁêqt°±‰Ô"8ë™ð>iÒ¤¥¡SýË—/p¿iQÛ2"þÝL6¹ž‘”ÝìŸ>}úå—_èÑͧ4Ú‡F²óõáß7BVTuñ¾;#µ±¨†ºýyÁj¨ã°}(>£%ñ¼îÊœéxŸ4iÒMhÏ©-ðýû÷H¨KÄSg2dZïÀ.< ;?¶ÀÏ,nv gþ^El#Cx:@xŸ›Xÿg#]Q…Sè±Ãé™.MƒF‹#hëJnnÔÈG•84à]Œ>µ† ï¸`­¾µDLš4é Ó“Nu m%¸Y÷MÄ1 à+ÄÿðáÃ"ðœ®ïhA€7nSâÐÀ G‚¶¹hW£_lÅT»N¸¢ª{¬²;¨^ÑÖ…~cx®ø¯ë¼Šÿ{C’:í1&â]T@ó›MxŸ4iÒt¾S¾tnÌ•ÎUK‚yô.e2ô><<Ð:…óÀ‹®$£Å ¢·úF¢·ØCÜ2µÎ€S„Í«OÛÜAub‚ºx½?Ïd”Ÿ!—8ôpuµ²·>V‹Æº³úަgfÒ¤IçÓ9Nub`àˆ6‹Ô ×H Øð4ÂëG˜}7šÃÎpÌ ¢%ïÕÁ"Yíä!®š÷;¶¡º¢ªÿhÉ rÔ¥îN Y5H×k«Ÿäwòò+VkÑê¢.ûNš4iÒ†)°Žê¸ Éps†˜uªV÷Òöò ûîŸþYÑ쯿þÂW`g e½o14t ú«BèÒoSUd®ŸEÔ=VT5 RMbˆ É`b®Ó!“‹+â„»¾¥¯ ¤c­#Ý•é+'MšôC‘úÕ3ªs¥:"Ý­Ž’Fz‡¸ø0Wa‘âS€Y&<Ç“ð~0âQÃÍæcÙÂÇóº£tF­‡x„ \¥÷Û|û‘f ¹†™4<¯èð¤\åTò8bsÒ¤I“”Ô\G@¼ÜL–‚5Duª«á­†«¡“»^Ø ÀˆÄhmÀEƒ ùü¢B¨bfÆy»Vlç S R«˜Ö;Ìõ:Üh£hok‰zͦÙpi6Q$Ï|鉣ƒ66¶‰M÷03iÒ¤R`a³Ì§OŸ˜Kꚤ+Ä\'­}n.³Tù/3šïñ'!á"8)vôÎsÑìYéÙÌ6d^[0äÒ6 }ùò©ÀÔhg3u¹Ö`ÜÊ*;4‰ôÝ"ÆdHéƒ<‹Þ`êÄeß»v¸É·–£I“&½.¢Ñ~×ÎqÃ:fæNÉ>풜꼯ÿÆŽ]ª˜FÐÆQš#K«kÛ†¯«“Õ‡?eÀŒ~…WyqS·2!Q!'®Ú(ckÄCô>Æv»Ãûê;¢‡i‘š8Z´ÌÜÄ4iÒ$%n±g°ìv¬ª„P“ãúÌŒ7\5 >£ŒRÔ]ö#Rì­!ìë÷ÕÒV„Çàv‘´3H5râGV-wèá?ÆvVϸÁz¢h&7ÅôåË—™:lÒ¤I™˜ ën;‰»>í!Iä nÅv½™ÛBbì×U¼óÀ¡ l¥ëØaP¯d¡,†Æêi)ÍzGpN5Œ𩓈ÒÇ^.ýnÜ=è~Û3ÓôûkËÞ€Ÿ8æb¶…Ñù[‹Ò¤I“^qO=v_F‹9d^tƒhŽBSöºë"`Æ4Ÿ h‹œ­«–D~3˜‡Hh¥h¹Q£ÏheJ;nã·X[ zI9{`|ˆêCn ?ú,Z$=@¨Ì\N4iÒ€íˆôÃ6%ÝÑC¯ˆ!¡a©BP†5C*{Ý%qŸv©V&*àÁøÐÑa•ÔUKý3(P¿#Ä””@ rõ!iÇt[ëY+üÄÛ'Mš”)c{H¼Õè³Á™ýCøÍ )ªpé¹À“tñ¯Bk$‹]á:GõØ¢'ÝA\Òåñv|R·ÍšO&ž²Ï Þ²¨Qã&¨ÏÄöI“&í}2%*h¶+”í™ëêîVŠe`WØ?xrè¨W„q}ì+k¹ ›ê“§g[< Nï<•¸’†Ø¾öA8¬êÄöI“&`Aýí7>|ø°¶ ƒ/CQ¢eFªl·_÷Ü×âÌ1n€¿ŽB‡ðžÓŠßYú”Œú ®ÍŒ¿’X¨•¥¬žvû¤I“Î!‹“©X±lédé Øƒhâük®!ðclJ‚zµÏíË }éCs]kžÃrïEÌx­ù±/(×|È=~ßÞ!†¬•ð·×Ž›Ø>iÒ¤LL#ƒã“Þ¿Ͻ¨ˆ¢k$cõÊt,àEŒ,vÛª£–¹=ÀZcgà“Šö†ùúMó„ä&ÇðùÛí]cQÈlB·Ç"N&Ÿß=iÒ¤IÈ9m÷ú²åBdÎCƒµHö°¢ÐŽ •Ä®¯«oßJ ‰B‡ËZY<Ì?u›’â§ù@¬ž‹œ©¤1™{¯ }Ä™á¯ÇwÖ-$¢ñ2Þ`Î5÷.Mš4I‰I érÿøñ£;°f|$SöÞ ñïÚg­á"fHRÅ|DÑhõ¢wûGoýZõ²ï}Ø¢üc µä‡î3©¤%ÔLÀöhûd™]îK4iÒvÀrcýˆ½†xqª+pñÚV*‡E€¸ãžÏë3Ävµ«cäE§7 `×~ÏÔÐæçõ1€P®˜63$àUB!1\jßZˆ&MšôêHÝ2u‚_ö‡‡€ N‚Ð D9¼0ã“Ø;ƒEörã-îåÁ}˜¯„»=$·ºÅÉyÓVZymÀ~М‹±½Èà)P§ÈŽ]8d°ºÌ<“&MzŠ`º#Ío… xty(†Çèt>¶ë¿ÙX$þpéÏÂДY|w¯JVÖ1ækØØ¼V›ŸÏø|>•Þø×",,Ä#È+Í…ÔI“&“­¨"¾Îv …8(†h9ŒrßÃö!òzˆÛ“ÉÄ¿¬˜=¶ó's¿+´Z%mh³Êï ÑösKKð;í“&Mz’4Ð)‘{T ‰{j[*4‰íÕ‰ÕKŸ_1Z1dÊ‚«)b»¨Q”9?×Äê°‡çy¾ûÇNñNò$’G:mGr`äy^ê¤I“Î# †„³]cf2ñzû}ŒíüˆFÆö }ú$ccÊ~Šz],ºFM÷c£=Wàás³ÅiO«"öp,0Kžn%æl¯7+ÎÃhGïL`Ÿ4iÒ“ÄU;ª±nÙ`x&éÐ`Pi„a\ýÁWEÈ'v{KÞjh–¶Õœ~òØYØÑxA¢Ñ®«Æx §`ÓÓ>öI“&·¦ò\ìŸ~úéË—/õéeà{îc2Ì|„_±ÅGÃpCQö=~–Ñ>¬Ø°eDV™HgjÄ÷†q€@<ÿ¿ÿýoøÛ‘"lzÚ'Mš¤dHN™›±Ù„C*¶ÕáàµÌÜþ9Äç‹M÷Œù›ð«+°gëÚ\ô{ºª¿¥ô³ ºMl±! v|2¶ìy½S9_/~ùåxcpˆ^lÝ4±}Ò¤I„tÎåB¸þ©QÅ FËÀD¬`ŽT×–«ŠÐš÷1©™}`Öî¡hFã½× ªöVŽ‹>¨ÒÞè U%bë(À%à½ojJ[¦ïÅÆÜÿ׿þ-‡ âo-SNÿÙèñkŠ{µ-ý Õ;³/Z‡IßÐûj™+†Wô†û cA8ñ¡Z‰[H8% ÿf˜}ˆÖô¯·fñ*Àæoæë °Ý€]‹Ó$Z+[VÉ26”tdÁÚNEÁÃÈð7;½1¯Çh7›b¦ô,æï‘}ÀÐ×olné^õn °,åÛVcÒ7$¢:ôëqKöX/*˜c©ÿëFªGH0Xýc#ÜD¨†å–ÉË…AÚ±´ÙNÎÈœ§ únþ,ã/Àö½°œÈL~_áÝlou©…ÏêðQyûûï¿Ûä¨Þ„›±1¯Ø)]0Þ7º»Ý ¡ÜM°_¾ˆ¥êº´–æê¡nØ› @Z ˆÍùÕ`OM„ë¤2€.®p o-z™»pÇIbƒ¨ ø{+¶ˆxrèæ-9 ì‘VKõa³ÛCŒvû;ŽÔ‡ _Ñù‚~'£·p¸f‡¿µH?‹çùÊ²Š‚ÇpZJlÞ˜Øú1^¶dðØÛÌâ®ò¯Îdû9„bj yŽÍ>[XŸ›c—ªF™»æ·DeØØzÁêÙÃDøËê0´ÓÀVã_Pz¿ZqÑðŸUújÓœI7'Šk2o½®x‚¸‹Ø òÒ‚î¸ë“¡ÑBÜy$„Å· -íŒlÇð#ïŠ>“ ûUÒؘ’?¾GöîÚ'=°‚òý«>zçÒÛÕåÅOau£ðµƒÀyì#‹Û¿¡õÅù~ŒÌ„ß3%â¯'|ª~¦H-ò\˾½st»oó&hÑò„xóóçÏX‰6A~¼Â™6¬Fl.;Lº÷ª}‡9gƒà­X4é+îèV¢zÕX€ M¥eeÀ‡1ëß>À!C,ÒüºÙc¬8©{E 9àÝޞ̡&ö¯.Ãõ=ÊÑïÛ")òÛD SD>sUEŠuW×=\.œEÃv¸eϧöÍ?C°o¢žô9GËó_«˜­uþôéÓÇ1ãû÷Fhõ/×>‚Å}ð¹Juå ˜yß¶cÜÞÕÛC§ pk}TáÃÄßÚ•x…KZ—á*M5õDUH?¨ÆÃÃnRrêc˜†_9ÊLúʤsd yøj<ÜmaêpÛCê5 }Ùb×!tq0‡ ÂÚVõzéOÇÎ@š¡R±1ÛÀ{`›s\/) ^=ÿCÓßÙƒ÷2ŠcäwÉŠIÏg4z_YDô¦ã£*ÿesÔiÏ‚j7Õ'ïÙÁ¾•zg`9dêu…ô*!µÎÀv´K]IÑ ðÓ˜.õØ7u[åebÕ¦:âÔx áeËq„ŽÃ Æ¾»—u‡ç® ØøRe€c\% |ÃjT-æ°B5ð÷u†ZMz—ÂÔÑSt¬Aû¢YV@õesäV¢ÒÍ¢a0„)þ«P¶gŸ‡øj8Fd·ù7~¶Çf6ñ“¢_ÎßÑÕÕ½gö^/)z‡ø¼ni¢wÈ„0³ˆ+?Ù’4@¡†Ât¿o81íºß"—âj#ðRÀ^©Úê¸+0õ[ÚªñäÂëÜ=›„ÌGﹺɺªŽbw-$¸Ž\0±G˜Ó[5<ØY°‘b;¤³˜»ç¯†CÍî/ºŽt´.Övä"ªã Î=Ô žÂû³ª1é¥IÁü.-Ù«á‹-þ¥§béÙB6S3Õˆo-ÍÝÍ¿¶bÛ/½™½§×ú++ÆÚ²E,ÝÆ}÷Þõ›ú˜ [l,ZÀaMä2ƒ0@SSvmVåODøesõÄhª%² â«I ¥Ž®ö« (†ó$ü^Cd;ŒR†Òám€Øë:æ•l¹ka?peØi«4BÅ¢éjËy1`–aë9uÐ4 uLG’gÌ0;s,¢5Œ6ÚÖjÇæ£ÿ?ZÏÄöoEyVø({Žl«þªSÚ!LCu[[uôÀ^’sƒè´ô~fC?EZRžð•¡Ï<“‚­¾¢DËÐìjCNÃí¥ß]•ùqÞœNœþ„ÌtBÆ/ŽqÌ3¯ßTޱªdÝÅD-e¸ºúRíX®å™ )”×õ,úk@0Z¸HñùóçŠ7ÉÉ šÛ3XÍcJ‡r€-çt˜&ÿüóÏì/~öœ:¨Ñ^¿P¿†ñEÍhÚÁB•W˜ï`P¨wÞ¿o»›¯aѤó)'Øs ÀZ9ÀºsF˜R!¸M+1«…62SmK ¹Ên¦¬È{˜¬6­ "z?úáC E‚u¼ˆ™÷>¥/.#7»½mPS­YÚjÅ"áî! ¾ÈýìÒÇÆ¹æ`,¦Y¬€µF1øjðNþš]ªó&Î\ÈÒ!¼¯#>Æ^ v-²B;Õ.Å…×enª!Œv8Ïñ—îºB@ù,͇‚Àn¯C¦:®žVh€pŒ/œ±8t(‡*cx†u&Â\(“)ƒùpsDHäp4à 7ë5‚ÐÑ@› õ«¸¸WÙꨤ©j•ULíü“¬´µ-È]7E"dbäÁæÅð{,z3›uPÍZålSEÎ=£ÑJ\SعQZ8KÑg”Ã!&“ý ÒqV å— õü üx.Š]¿¼sç[·¶õ‚1TeLay†CÒç³å ]3fÙ"$ÿüóOÜ|¼â´AxB·rhÜ÷[æ ºËT¿²®Qü@p‰˜Íü$¶s¢ô×_ášž}zÊ“=uDÁjªØL·Ì­È’?èº9uÓ<-Ìp×"‡Ñ­˜âÁV‡×Š@ŒêÈÚ{KVYèTC´È“Oªa©Ìʘñ9?f;x÷XtÕô¥šk`§Bý";‰TÚíûúŒ6w4â KˆÞ¶Ì,ZzŠÞ8_“vc•æ¥!ªT5§û”^Ü—‹{l¦ßµ“¹X1¢™ddËžÀ$›}<³+õ³ú¸eª.ÔgªÃT™µ¶×`{Èd\8.mÎÈUí}k5eòÇT³ÃÏÁvÐÿýßÿ-íPEŽ/”"v‡J8«‡ªÂ9Ã9 †˜‰íWÒ0 EŒÀÙæð´ÜÉR ÙÑ¢_J›ôÁ÷xQ‡ÆdIî‹=˜5{i£â´»4_…ÓX¡Ô&‡Jùð´¡ŠU]v–MymSOEÜ1D®|Æ"–¥ÕίÛp³¶Ðw. ÿ;õ9Äb§UÕÇÓŽÕœ‡‡Hƒc×~6Ç ücß{l(']ð¤* ÜÑ×8h‡ÔÄæðÑ~çðªâG“cí§{E–!¸trŽ?¤þú÷ßs]ãýû÷e[>ƒQ¡1WyøS1V\Úº<×U'¶_LDuBúcÛ/›á 0ÙbÆ\.UP«q)%™·‰ˆÊÙ½ÙT]íB_ç}ªoD¡o‘S›×¶‚cŸ-â¨1%NÕ‡ØNéUa6‚eUúõ…u´|i<±›ù#D`B3,„m4Éȯ _ÄØÓ‰-ÒBÕ-ÆÝ |­Ü¿fÊd Þµq³$Ê0¾ö &6hÔ(Æ»z]õ¥v ¸q%¶C[»X±ßWéÒÊè0ÄŸT*Ð_ŸáÎ:'LØŸ?ƒ‚B|t!+ËæRÍ…G¥Hcêærê¤É”"eu §%ÄmޏYô¼+ÑÆeÝ.ZÄ`Й~išlÚ!à˜öéÑOºM¿V1û×Þ™¯¯+Ø*F•~È(;Çgdpà}m/•Ëê2HÙŒ5dìâ[êç)›ÕÄ•åæÊ3{Ö©Á»¡Ÿ5‡qÄ q2s¥OãC^Â3Cÿ@4k6Úî6e²Ê†vÙçß]eÄï30rÙ|eºX—¥•{\Y€ªÒ’«—{Š IâëFOƒír~1"dê_hƒ¯âpÈêA0ÄhNl¿€h®GJ»Á#/13^K6¥ík³zá²Ú:Ú¢¦Bˆ~)®*ôÅñ²”ŸY“»#$v‚éág‡h6Äö|]zK›õª¼¿k>ü~é3T׿·´šý?{u0ä×OÙÃÄL­0ˆ•ÓW9k¯øýF¶|,—UØZú$©Hë‡lYÅÅMG^¼ÞálÇécpõÀÛ³JlLI¬7YÊaýyÑet&¶Ó ™¸™©fÚ:^CâÞ9}@‡^&?,°GËÛƒøó?Ú)iètë°Ï£ß‹·ŠïÔˆ} ÊêÚ;LõØéª}$}Ò¾É>äTaèWž,°zzQ±]!¢ôsÿ%¹ÙùMu¬²®ª…+¢X«|´‘‹Ã+-g›žÄHÝìû{Xg\- '׿_wÎÈ~¹ãöh´Û!®Ñ¶/Äòh«m^dþä® ±ÛC¤‹31ÆÁº¾¶Ã`V‘øC²©è £ÍG×ÔžúðáCiÆÛs±½„ÿû¿ÿ‹1ÇE« …,4gU«¯ûÄöç’fbdj&쟑CÕt\‰ò|Èð üiOû²¢e€R&ú­½›/2t§HƒÒ[Ý”Ì=ŸŒýÑXSd 3‹…ó¾²ÈJ„r;³ÂÆìŽá~%èÈÚ˜jÄÚ¤ioìÐaôIæë  ëã|IqýB¦»a;€T›œí kà°+I YëÈ$άÓ.ƒÝ~slÇÇÑX9Æ"îu=:kí¥¸Ûkë*¶/-l"§"dþ"J/û¢¾…á!w-?ðÄög‘Æ0½R2.mG6XM`¶®=}75)b ¦©¾˜\©Zåçí¦ê”§ÕàåüÔ¶ä ¹æ¥0;|wØF}xOëù}¶NǯÜ|kã°hü¥;—ùÓð/±½ô‹­¹Õ>ƒµlh… ž1ŸèW<6ƒ„!/jºÓ'ƒ©%ƒdl#›²bÈ^k¬Ú$LÐwõaBê h×ûÛ‰íh)¶Œ m¤aëhE`ÒÐ&üt¶sÙz ÑÍÌmÃX\ðMl¿Œû\yð˜akBRdƒ› mö¯âèS£eéW'µsÕXÍ1¢¡ R~øïÒ{• qEœŸªwC<e jJÆÌlš®4M+c×%yz©5ÌìWZ€¥ùáC”Èzm¯u×64ç†ó‚JÊ…•¥…ˆ Û MÖ»[ǺÛZ*íXúF´ 8}3ùɰÇ.k²~Dû…–Â8™›c» Ú¢Ò»VaÜ¿¶3Z&Dø—4”C˜(Û¯¡GÉÃäEºÛ—lÁ7¦]¢ü(T1Œ‡š¢½9D~|8ÄïQF1ã¥ë•¯‡5Ü+]ÁAVîÅS`˜5ÝÔs„ ‰í¤±ŠÑ™Ønõ´Ê³±Ú¬UO«‘>dKé#Þ×m¥kdUg_ E$QñíÜj±È~d–Ü ¹¯ Û³<+¨T îʉÍ'cs®‰<Û­QY+)¢å%±ÝaOžËÄö[У¤Ë ‰xÔÅ/š|ê‹Vy0û<ëEVöŒ]±kÏ¢!Úä S– Ó²° ql¯]Çm·JßÇmµ‚¨/ôjÒ4-mí{ˆíC‡•.Êëq«€~Á~Ò)'º§¼ÝÜ-óØö.q>øÀ™KHZHu•èE$ $°ïuE+›Íœ3ÂK·Ií”+Ó…MlŸ´G L…C† À„4b9uUû6ú9þÞ_sóòŽêš¢Jî÷óÉ@Ø`×*í†ê¦¼C]8(:scï#ÖÌÒ[Ô{eñ~ŒÐYˆsÐý¿Ob»ÒÁ¯kï×â3Ö"¶‹Ë¸¥­$FÛ–rslgd`ýþ§OŸ~ùåÝSCnäáŒÿf——>oÍTÁÎyñ.vàBäãŠëÎ.™Ø>iH)¥?¼îE’°é߬ÂKŠâ0tÚ|ólßÕËUŽdPEïÉØ»?,ÚÆ2-]kUÄ 9ä¯ÍEOæçÏÒP·Õó·§{^£áý\z.­Â,÷r¡Ë²/¶ÈœSËQ©ñí:ƒÓ:ï îúo¶ÞUt æc\ã2wlž™ëW&¶O2²ãJálG|,åÙfü=Å'š Lô`5Ô—ëQ=X4[´Hœá°™ú…óÿŠð‘`3ÛuÖv+eOðWMÐEÖ¾q|gýµv+CX#™åÖwʺEÖÍ3\¨An/jmñ"W7¸yöãLj–Sâ%–S£¹eîZÊÙØòÉ7´ùºÑ¦ì¯G,)|H9Ϲ ožä”ZÿeÞ¼ëó·OlŸDÒˆÇhÛ”˜;Ž›;"a{Œ6‘e|z$Lß©D¹oHVÉ’<{•Ô¨j<·žl].wè³=øÈÒµE;vWÜ@^žVF@b»v´"ØåˆüŽ }¾ˆ[¯VFÅ ¹ÜóBw±ÉjHç /ëG$¾eL`0 %‡­&fò &†1\ƒíw’!dbûIDulA¥H0­r¾ZS˜ÑJïu AHöTF¼¬×Cù 4p³¸2˜G/êö©kjBÚÃL«Áúëð5./[F².m»â4†Ø®¬°á›ÏëäK‡ Çy K×¶œúÒ.wõ7Þ·Ó¨cs†œZ¤Ò¼˜$”Þ]–¡É¢¯u×~âÑYÄtÇÛcbûE¤˜hÙC¢b€õš[xÜN{T/£€2\ظðMhïJê!‰„±ÑCÓKÔ”kB/еÅð™}¿þú+ë ƒC›u€íö¯Þ§{ÿ*Øp l¤É‚<[ ‚G—;ä?=]&ð:3ÕÝÖˆ‡Ä^<{½£xRúÉlo$1 à[O’,6x¯×8–ZùüåË :8‡îñÒœi7Çö¥§ÂYU­çý×ãêÄöoBæZ‡B!6 ìB0ÆH£Sô„»@ EB½I{ôóúoN{(m¢5t¼¼°?Yɵ_§0œ±#––)=Z2™Øbÿp“a‡ÇØž¯9:¯Í¥ó¿8šJ2 ‡z¾®þ= ‘±Á89Å8‚¸w$u¯µ>pÂɈh㗶΀Q™õaž+\ Ð÷ïß×^€5¤¢¡e-ÍM>pXÝ ÛÙÎþt+¶7½l7IPyÈü¼yöÑaé™n[.]ZŠ&D?r:okF!¡¹YÂcgõõ;(›(ŠK¸ŸgåùÝ­áp}ZÄ7®¦xH/ Ë+Ô¡*–SYÊ9v»}0doÔ²EzXÝÔí0ô5åk Yâ‡`EœÌhЉWå¨å09G&Ÿ íèúïŸþ žHR6÷{ôÛ”²äèd¤A4H¯­£­Î†Ð¦bjß')7\ñáJlIÒbÂQfž#Ñ^5¾!¶[Ö\0ó1ž× ëù¥çÇ®ÄyöÊùÏŸ?ßmaÌ–•]{ý®ÑÑÑ´¸$ÏŒþô¤Ð~eêi¦úØŸ[7\/ûñ“lÁ#2aÁÖºÛÂYí]~|ÛòhNS¶¤¼R¬0ÐkYèr¯èÇM£Dòmt:ŽâëOb{Œ?BÂ?ò3ʨUv´é„t‚“˜ÕjWK0þk#í¿7ŠRbñR%õ¸Öijþš߬,¾!|ø ¨h^Ò¬ çcô´s`ýûóÏ?ªÌCN‡Jâ§ÏÝ·)èS">„lwúûï¿+Jðƒçc»aìh¼ÈÙYˆµÛ¤ƒÕxìJµÑ"N_ÛkKßõ áˆrÐgÈ Î‡ 1³ÊÌwžµê¡¨¾W:+û1š%£ýø\„çy[Øvm»¬”*êkÛMáÌLŽ~ã {dϹúʱT-²ìû5Q=Fþ[Eïèá×,ÌRR Û`€©–=‰í¥ßY©ø¶Ê1[¢0¢ŸŽSÑpyÂ鑯Aï$T.¦Ú:hDÕ /ê¾"³´|õõß¶S*ðMŒ ÷rÄ3´Ÿ… Žý&G™X‹òˆ¦î2àJKS' ÍESxýýFÑÂlPCW"ãdW¨÷ÏÄv^p,.’i¿l¾)°t·¹°ÈsTƒÅ *öBØŽp"j´á2ZLëhNfe&ù îUžëdŠC¿€¨iíò.L·´å$5‘uÎýøÜ¹™€¯ÕqG´“’L ¢§ìr ñFjé¯Û_)벜¯²»*Zaâ l¡AwÍŸL‘‹MÌ.Àv«'qÑ’\Á–F Uî}¾kš«ö0ZD— 8¼l¸(¥ü ÙBép¶pytiGQð|‡YÊæÿ©ÜÀ‘õ–ü?þñN4`íœú<Ï:™:p]žÚt:K‚ÑN‹ý®gVQ ^±Y’§vœÿ z]+ŒXVp›aEõ.OpæÂ™Z<…í!ƒ5»ƒCÿe¾¬†d¬ÿrØb5b³N±Ðs[l' b¹“˜ ëÇÌ?£íP®aªX_¬}Á¡Š =„ÙÇt8‡˜\z½ Ý[ZûïZÓžïrø8ÛÅ1ÌßSÉ’ì·²cLò-ÞT•Ÿt@.ÈyÜÉNo<@È‚ݵžªìÔ²¥žÏÁö"™iaËUu®Õ€¢1T«¬íæ¯ñ¾Ù‡¼ƒ´x E€î·ð-¤çÁˆ·€ÌÑP§}¡b½ÐÀ¦„:Àvö‚öTéçéÑB7£Y€ÈCR¶ñ~x>v×וX ³(â l¯Äc=‰„d)Ά3é<4f‚Ÿæˆ@ïC bódb¨R˜¥ŸÍuº)~p–ÎÓŠ)QÚõ_ôã]‹›âÈr¦AÇ՞ɇ‰ï©6²çÉò¯wðÊ$#­Í)ói[ƦP§vв-¤RòC\ œ_ƒí¸VO2DuÐ& ±]‰åªqh¬²®4Coa,Ã@ ¾Y¯î[ðçÚ¶åšÜð1éÀwN-uâ `·—~ŠÏÄvT@±½"O©¾“PLv]²ä–½Ôô8µcg è…¿þúë®ÅH`âvqú±ÝP½ô6X¡ϱ­DÔ Ô¿÷-\?ZvM‚-š9ôÆÕØÓ…¼Â¿KÛ’ó$3—3‘i<Ð>A¨0L¬®u>¦ˆ ußÎ&ø½y¶ÊêÕý?¡[:q8ÛÁvø)Ò:¸G°${Vÿ¬¼&31éT­"ñ“ÝQZ\JôFWHÈ!-O5é€b»UC/ö°õ„šS O-±ð¶«gƒ-²¨K'ïÃ^Ûê­}xW`;áŠâ-¾Ž;¿õgµ/íäV  ñ†ØŽgpã <”•—¹u?µÄ; õ¿µ´Ûõ#€VXøõû°€W¿µ“è#a;9?ü÷ —vj< ÷)WXÁq£•æÕQv= ÛõØV®ÞIô w^ãÂöãXwgËF;Zßîli&ÝÉ&8 d7|‡~0ÕÊ!E’†1«6mi1*èGø¸ïO:g Û+9>i×"Õ3CºöM¬Ü5k¿R6±ýIR¬0ŽÃùwm›Áñ—Ë@Xc ™EÞI4Ú7Äö\„ªvôó‘賓Ԥç‹0Òàœ‡‘IÏ0+ Cƒ‰q´ã8QžÜÁi0kr•lG¡{?}ú¥ƒ7Ї·xÔig? ~V¯ñ:w¢Õš×R&Q¯£™¬{Ø®•W,µ®/rj{Ga#P‘‘·ÎôÀV-‘…f‰Û€øõ®­?b1škLúMe&…\eR¥‹]ÌÉ%“aB:Z†­v±éSõðMµOJKC·Êßt’¨@'<áýÌ$ôÉÀnW™¡P±]Y†µë ²TL`?ŸŒuzS…Ÿ §k[”:víô¯Œíj=Ùd~“ØKíS÷`ˆ÷&D×–µ šå¶6¿:Ÿá§øY%h¡ FæÏ?ÿ|ê#LÎÁöÒÖ¤È âaý,S3áy\£¶¬Ká»ìwEfÅöhûÐj8Þñ”XÚ|äÛ‡´6Ò‘ZkrjÇfÿ"ëEÈvÌ‘{ÊÛ3±{‡ñe@.@õ¾¥¿³uŸÌL«—r«E[À"¼3d=°#1µz?(l ­*À*W!«fÚœ•`u8z¥b»NÒÑ §¶-]¹­âª’¬ú>càöóÉøªºO ¥JWLöW‹í†kŸõ7Fñó%rƒØB4Uc•5PÊ¡‚0J-:6–†û¶Ñ#®ÀöŠ60¥*.1’³ê —@mj"»žm Á%2“ÏÃsÍáÃìa»©|î8“,¨9뉲ªpbØ d2¶È ËBB鎦'„|ˆÑþ ö,ð*!!–6µãè$Á+Ð5v5’ya`¨˜š+ë–É ½æòY<•ÕŸ ôø üŠy ±ÞY7«^X¿,½#kÒ9¤âm|VTt•ó<¸¿flWØÔé*ŸÉ*OðQ°RbÓ†ê=”‘ 4ºJ‹ý€:«+Ck¥0«Ó[ÅvØK;%Ú´‚ ü½eÑ×zê79üͼ/ÍæDp–Qs˜mº¬ÀwùA~vOØXDô¡qZ+Š¿ C[vºÁ Óž…í¬Ü&ŒeÂ+÷ù¯Ã´VÒl J ¶·´?Uà*ö:E[:A¨á}K®HÉŒ~@´‘”ÏX«Ùë–åS-.Ÿ=™~S5e1*LEtÏg$åÒJZ_¡ÜG“hˆí¼Öñ—+nplâšË.=þØ~Aœ á+ìþj* º£5±ÀÝüñ"SïèC4cGàù…!¶GŠCl×IAôón»z°Ù×:S0&K‹8¨A0ïp€ÃŒ8"Î^ý¬™ñÚ.½£Àz²JŠÒl5@ Üyùä³°=š 3„Ž—ëÂõSÆkféÇ&vS4„6è/-‰Ç²-Ußµ}s\ñ§#H%Íl í2•ëÇÒÌÀ2òNG ŠCÓý±¥›P—;„aígv6î(Èÿ‹ØçÒ¯¤!f~B=h§m‡ €Ã~¿9¶ŸÚ"ý«CT¼CjØ–¨E«Ní ¨k†ïB€é¥QŠÞ–³†/½ý¿]íÑÔ–Y%O- çÚ–wíªïÆ—~ðR>ŸÚNR8 tø -±Ý>¢•~ÐÌ LãV[¤@A¢Kül×Á®lÎ Ý×¹ž¾8dæž`[­(Tk‹[ГÓ×Ê.Fÿ–!Uz3‡òÃÁ…a`x÷œ3¹v š[†Q÷mŠÖaØäã^˜t )WMT (] Åöã™Ú5ؽp®â!_[ò®QÚ‹‘„Ê”:z7©Ù<¹ª¥AYˆ…¯¶SÈ0#lÑ&< [—ƒÔõØÍf¦ 7Z0g^E `méoü‚ÉüWÅÈÀ íµ…À)æðSÙ|5XfKù¤6œ•d)§––*LÙ0 RŽQ…¦ì©íµD)´™!¹ªä6*—òÍÖýÞN?„›«"üÃÃ>½HÊüÜ}±¯V% Ü|`ikLPósÎÒe´ &ð†Ñe¤å.âÔ5æO` "WÕ¶‰^Þ…°\m¤~ÒnßÛ»4¬ƒÊX~æ·>|‘TçÑC_)2 Ö1B£îÇŽÊç ÅpÕëldyÖr9u%g¸+6ž‰íüI¾ocm‚³ô¦o~ PCã» ×·‰Ò¶KÙã¿VƒwøA¶‹îkÃ!Âp¶s®Á]ÉKs€£uIécGƒ¹ýÄk‚ªÂ;Vrï·Œx •a¡EB샑ä6WIF;cö41‹Îžénø‹¬¨¢’§þ…ÜöHª1é&d¬VýUtqDÆž¸½ýkWb;‹^ÓrK;x‚©Et>ŽwWqØgÕ8ÔRˆoÑk½V’uæX€8™èáTe¼Uå%"q…Žgvìa»ÖjÛ‹8(>|øð[;Zf/ZK‡*¶ôË‘|’ á_M°nNË(P!×Pë¯?åñÎ Z$ŠFgUZîšÂ÷Ðõ@­Õ| ¼Ê#,7°†ÏÂö"“Dl1Ó‘BÅ&Dú+"¹ÕœjLó‰q}œÊ¥ï jöär)“hf+Ø*…؉¿ÿþûLÓKl&kŸvÀª‘¯'Ý„”ÏJC3®ú—›æ²é~¶Ûð=¬Ì±©âU„(¬Ù±±/TÊ ~¡È^øE¼¬{rËô>J§Ï¤ÚHܾTd˜`ë")£¶H‘pWÚ4‡fÞžjï dZgFú$ ¢´,rZ«ReÛjulñ²æ,…Ævôƒ‚!Ì9Ø®cS4û“æzR±7‡ßÉ‚½H^#-+Ã>?ÎKGX •uÆ«½–f›Ÿ¿2j«'œ8vËhÀ ²JrÛWn+i]¼7›˜t1QMLOñë"+øKKÎü‡RiEõlÓ—Óº,*‘|ÂÃ&h#iGVºHÚÁ'-J<ž¢Ø%&lŒm \ھы±}‘À3å³~3³BÈÝ‘QKÙbU Ùµ4×^͈lì8xŘ©²´¤Ï5ù£ÎÄvm wãI‹b2¶XåµÊg›ÙºP#¿:ÙÅûnöd/WXeCøÔö³Àäˆ8q€íïúX÷»–’j¥½ýø>ÔI7¡áˆ©ÝA/j¯}úô +ªyýblW£TGBNýÂÒG¤Sx–äOˆ-O)ÃÀr8ÅVÅYzŸC.(zôÐ;¼¹´™ï½$*¿ÛµJ`8íg šFd>cLÈÀEVða«vi^÷=´9¾iìÚ{€ÍgqLD©L 'ÏÄvÆíÓòäHÁgr_+9|,z¥ÐšÉQÄ;t’í«„DƒÇ¡ZK‡7µV´0C‡íqìrWÓ‰Ñõ\ÿÕ¨6*ofNîŽI×О¾ë¿ïá&¦ë±=›m!r¨‚Q’»fOˆüÄ´uäž^G¬V¸þC=–Šf*Ï_¹´»‡qã¼ ðVÃoâ9ؾŠË¨ˆ3JȖ̺¬ûvߢӑ‘®¡ÌL¶ÈXd=®ˆ?’o.mv©{JšREo1ê‰-:õû|Ñ×V±=Z‰ׯj½Ð'iPÙÅ ˜•ÔL÷ußÇn½¼ôóVä7Fà\îX0}2ë/Ìú€=iåpcˆç¬€±T•ˆ÷ïÛšü{/.¬²`—ße3gX±’œ¨Ês«-«§¼5SèÖ‰Àü,”—Ý}RDkˆ5'ú<úÕê\a¶W¿¯ Êd…a{6!¬¹2¥’Z ~S%GQ}é×z¢‡PûŽÖST3„2µÐ2Š1+ý¬Êƶn‘¹íÚRô# mŸ‰í0òc;-…=-ÈG«4¬Ì°S&]CäöžêQ‘99e¿ÛRËÅØ®}—Þz§Îê“VsS½¥2C97¬ÓïÛw —ôyåÏcõ_3°ðÆDí1²÷°È³¶Ì½‹$"PÕèEé­Ç"f|n‹=Ï›,î$›C‡øš&D:‡xòÉÞ9“èñ‚—{yønˆ¤…œ‹Ç¥> ýb¥G?îkó­CBÌ’è—tµòE6Ž™@²t~AßÍÊ5Ü&¸89uhºã,*qñ… õk¯_&]CÊáµ÷ìáb‘]ºG5ú¥–˰ý JŠºv‡’ S~]Q¢ô{ L;ö>s)” ㌠äX$mr ’¥ª_çc»â›2 ¶–› LÚ¢¦ûkï!‰Æy€ÐÏf^^ñ³f»jshŸsQ†¼ú]®Õø–s°]å¤ôösBVá5Í)f¸ZøM‹ÔU.Y…Ùm]éWØÉyÖ'ª¼Â“È{9 ùl×3;(Ò\•ÎÊ’åaÙq"MºŒ´£Ÿ<®’z:Z0äÒïn`´ 72< Û‡ j51]سOLmùîÒâ(Övšç‰E&³Z“:ÕV%3‰ YkïÞçfvœÛ(ivõ™ØÎ6.â§]Û®ÿÒ{lŒu¹!2ÿå7£“쥡N ÉæAŒF@ýi¯>ö¤qcéçbœwè¶}°'±]§ÑìãU®›ðkªDö<+¯ ¬aNt å]ÿæ^Ó×ú=D$ôÚdœÞûþý{zWÎÁö¡é®Zq{MÓºa×Lº˜TU©Ú†i*64&¸Û©’¹2 ïöbˆ *d•~êzj´´ ÌK ,Q†¨Ø+7´!V¨5ªˆÆªbÃ#–PÿØÎš-¨à&Ø^6Éð‡¬ÈÜÓ¶³8ÖyM3}ƒ·EÒ ›=GŸaGhåí_³1pGŠ­z.¶ã󃙨X–Î!ù±3¯bÔ§¥ŸmYïGZ/Î_ÓŠñš ª¦+IÕS…BÑ8uWçægb»™îÌðOF•6úgÉT^ŤëHYª7•í:¦¯mC·.µÄ6¬ÿç?ÿ¹ ÛËh~ªd:¢ù-ÕMý—÷ræZ4û¬i6$ÒÒ-¹Ç'WYG‹»ýááÇÀ•~\‹‹°#—"˜êxÙ¡4à(Æù7×! £ÉŒ††“ö`ÈÁ²üU‡eEmæÒ‡÷+&? Ûə軻ô#uMRÈíµ?™ËôhŸCÕSÄΤ=8|Ìd;D˜‹W¼L¶÷s°ý8f‘@ 'êrîc±úÖ‰eÇq4éYDLˆ^sMêì‚KKÌ-à ª.Ãv+N+9ìn{EíüüúÒ6nsççìfºg1­ŒxíÉèÁ&“®0Bqð.Na(W`»–»JXh¨DÄLVoØã¹uZOå€Å¢œÒUúµ1‘ö$+YÚ^û‘1zÑžb‰ôZh–}þlf p+ÙôNFiVÛüü¬ð°ÚR6ŠMSžgÆÚèœ9Æve½ÓÑ™}q%¶ÓtG•†§©f~šÆ ågÒ1iÁ0pŸBE÷ã a{$‹N…ÐZÁëliYÑvd9»¡Z\)‹ÞÈ kòÞØÃV6AµÂ>^dˆ¸ÛËæ–ѨeK$H±ÏªÝ®OhcM É¥’ÆÄèåÊx… ¨(É-f W ?µ#ºÌÌ.ÏÄö±é÷tÂW~‘ò¦bP$årn¦¶%ÄÎ×NW4lÏ\ÍØ^ÒÔÛÔGÿ]eMmi[S¹±.ž‰íïRîß*áççþÍÆÆ¤3É6Dm£—áUH¿°¶0ÈÅvítíë22°UXaÆäãÛåø«}J+½t™îDoUfV< Ý‘ Û¡D,ñl¦ãܸdúk}MÖᎺ/–æ|ȥخץ¹ ÎÁvm#ë ’VÄ%«ÍÔ²ŒÉxË úÙr¶ã1¬qk,¥Š™2Vy¥V·Þ~ ò“ÐGl§ñoˆ—…ÓX¤¢Âªje²ðð È_ǵÔ'Óì™îzlG´Ükº•)l¬ÿ2M÷çPîzÞ^…£_1¤K!š§úáá™ß{CŸŒÕ$W~(Ì*'Ùü {!`€Ö¥EA˜´g±W">ìM42cUÙ±¨DÓ¨^sÍó UÉ °Ë©¥a5}2%Åòe€âO‹8–­;VqSìaû’ÎÊHZSî2 ‡¾Ë‹!¶[+Ê¥ØÎ† ±]™WØí†í”^®þ[ï,â¼RÁ࿹¸U\16˜êGTß—vøeØþî0÷ïI6ÞšâGRÀs(~xÊÂ`ƒ¦>©2¬¢¯u"Æ@rÛÚ•Ønš« ë©Â`ê*ÉUÖͿʠ&Ÿ>}b~?}]Ól0­† a¾©òF¾©ÆaÊÅ&DËp·¯¾xÝ®eE?)Ÿ×Þß®å@{J¹]RtÏÂöèeÖ ò»Æáüµ¸¶súcbf=e<цX¹V!¶“hü[?E±È>Ó\,)t“¥k'ÏÐD°Ä³°ý.–¥1 9Ír˜ié½[?&™æò¦]D2J5°v‘cË`jZ6Ès°]ë“13ChôQ|`éÉê\ÚŽ À8,vÎ?~üˆ&”ѶôÒS†PÞ×dž|^{¿Íºùd@‰Šs+l_úµT2MQ×êYd¦£áÒûdôyÃ}%¾¶Û¿ÚŠr lßÛ,+ö—6‘Ñ0ÈÌ·µŸñ™5åLÈ•7öj—Q)¬±Æ[­¥ÍdùVοþúKáÎÇvEƒ/_¾0÷/B"© Ê’¬¼5SV¨èÚ?e|ÐñÑ„?z©Ó;rÄ5aÂÍ!óî¼}©¬Oî2í¬èÕG+¼7XSõ #8³†À^gŸ7ªUÌ0å„OfIçëY}øÙ"~`3wCàÑ>Â'ù}Ô Ÿ:¸ÛÕ`SóŒÐNÌö!’ýCQÛb]¦Mû°]…ÓúÈ:Å Ô,N¹-ú¢eK‡¼63úÞ±/+i”Í4:8…繦{EŒ*ó§íðY´‘µ"åZY×çÎÒ‹Þ ò}Õ}*&AŒÉýðÜ€Ž`´£ßsÎX¤Ob»™Ê‰'{ý>4làra¶H×?7‚ÅNcÝlú@ŠdÏgû͞ת®-Í ñ¡4· $Ÿg\‰íø>·¸j•ŠüÔ*'Yd 1üÑšðS?,¶çžbT~ÍØ#*3Ñ»w†¬Ó4n”âTJs„ªZ2º2EfBåi±_†íjº#÷/D]‹ÎvˆÉ†6„œÉïõéwOÖpãÌ"s7’ sjO[Ú“úŽb wˆÑ~1¶h™j‡ZA¥ßeÏxt®-cêEfÓU©VÒY€]äªFÌî*½ÓƒÑeUȫюØL"®Ävžà`¶roÈdZûVyEm¾u–=üCa»<ÍE$5™BpÉjÂ;Y Ö¶x¤ü4]°¾PåZ›W‡ï²Q jB‰Ìµ›LVùÄ®óØtü`ן¸Çi~Ù±Ž¢rë£<…ÉR‘¿ð}ÓPÅb4©ÑÞ†Ep­DK WoØ“‡ibû²s6Å/ö;¥ô~¡SŸ`Ïà&Üæ“-«ÑkSKHq¾4û„8Ÿõ=ze±ó,#z!ä7U‘1q¨5©t]ŒíEò@®rnšfÕ#ó£G×üpæƒÕíGÃö¡X;ˆ7Un­ùvÍ]K2d+³Êm ÿÍC qÿBN˜T¿2¿k\g´M÷ØlFÜ ãmîwà¥wv Uò{¥¡$[ˆTøõÔŽÄ¢"ÀÐÅqÀíÚYÑí{ØN5èvZdM*úc¹Ö¶m-bÝ¢í¹¨€Iƒ¹ìb[#ÀÊ– I-^f† lýšÙýEòÖ22ÚÉw@øZÔùl×zòÐRÜ+b)Q‘ù‘Ò|DŠjgæžÊÒõã`{n{ˆØ°9úk™Ù–` µ÷íp@–¢¥œQž,mn‹Ð¬“lÁ愚þ@„FÀÍ«†7_ŒízâžÛ±ÈÁ‹¹ƒ¢ÈØ×Í,¥ñcОƒ(rTFô8þ‹Ì (D8F~¶3 XUâœ%l.-a È À\·"ÚT÷¶ØN E¡¼>|`¿ëòP´ ˰ɥŸ¡èýØcÇ66A:µ”æ±ÅÃy°˜ÏÁ/ÆöawàW=)‰^šô¢ãâxf¿_ýµ6³^üÖÍ?ÆçáO%¹Uµ!K²ši:u1¶—>ûeÓ.$í-+€ûˆüÏßῥش›Û•!ÚF4Í0ÉW»4C=§WL.G[=ú)•®*œ:;Ëͯ_FDMh ¸9°+>@øïZ¶ rÛ˜Æk.:,W“{3KK|×d3š,9fãg°x~øÓõØn˜†¡¼´öhÞ Tì¿þë¿4Ìþœªây„Ú2=u4—¾²H«Ç kÍC€ÅSˆÃµº9Bˆû¶G¯¹ØNk‡ˆ„U¾ÑáÆš«šè<7K‘ÞœØ=žïµ±ì{ öøÀn-2RëäK2™Ôqd¾õˆ~¦û"{ÌãêˆÇ'ñÞ¢‡2R€ÙS‹œu¾¶ð€¡\)O† ùÎȰHÅI‡~DFÁ*ÓîÒ†ÓóûîlW”`U5­‚o뜹wô][N¦‚eÒgdÍpa•´j[‹¬9§vH4¤÷ãÇ!aö±s¶¯’.€%b¤®„;À|Ÿ1ŸVÊšœ<¹ùY&¶›èa<’•½zòƒ¬—æaC ,3eÐʨ~e£] ]·¨ Hˆ:dh)=K§žE„ØWЋª4Ý)Œ/Í«¹Ê¢ƒ¢Š±zعßeYâÄG9Ã3‰ððÝuÙ®ÇöH~¼Ž­ú?ÿüs<ÿ ­aH, Н¢™±YŠtÆÁÑÄäã'äFÆJ±¯ëÞx>¶G;˜²òª~Ÿ9s°®Ê¨HýÎ*9:²ðƒ¹Ü‰íÙnW!a¿°nÆ4rÞÈçiºƒ Ÿ˜âéOü&¯M¡´>ÑË3„ÎX(gë/ä“y' ÄÒmÖŒ^nVvpü²Eøpo Ä2zS/øw$GßéOK[3òÀ;qÁ±æ¹ãnˆí Ø-¥™ t°_\C„Aâ8om(1d¦‘þj`XÒðÔ­ÃÖ©£íÑìI×`;¿ù¯¥`:]à:ž,ýü×ä„ `(Ûþ#c»6œʨ½)ñSßâ+kÛ QZÚUF)sÈÖ¶+·3Ûõ¾Ž XÑÌ`ÛuÏÕA€ bfàu‡Ž—N†‰Ñ³¬!‡$µê‡j»§ËßíQô~iüFH*þ‹{íõc;kˆ00è‹nó‰äXqdù¯Ö|ˆ'Ñ\ܵþŸ?®¨måzÛ—óÖR×䓉-¨Ÿ¥«M¨_Ö¶˜üè7 ËÄö™KZ,2z–DZ¢É;hi~~ü¨>ð¯àu‡ÆíVÿEc+UÑ-[Î%xá±TaÛÉ¥Üp»ø.Éæ}Ä%Åv(>ŒØÊíûvŽÒe½öú±“j….J±n2â±ÚÜ3‚k®hÀ!s=¶k)Ñܪ˜ÉòÔo¬Äq,ˆ».»X–þØn Ìü±1‘åêD‰¡¯³7ùX´uF,Ó(sôô@mΰùŒv“^T!Ä^Åk\²ÏUÃh~~$œ¬úH-¨£  þýFH†ƒ6"dN…déO!1iùþÈÚ¨–†Îûìá+{öõcû»m9•aØ º¶µx¢e*EéÉ"TÖ^¸ep@*XÍ‚ãÛó "c;Þå‰6*ü¼V;Gq†Fæ°Q ÝÛÙ;ёƖ\i‘g|RÙK݈U«•` 9!Û­í¹8«ÿeïóWFçÆ¦\>FFl®ëpj' ë ƒd¿/mº¿x¿“´~?%ºoÉõ@míkS„áÅ÷D‹¬ «>*PPn£‰Äõ°ù†°­&¸N99ÄU‚†ý¢¿ ¨œñùOãRl§zâ¢ð/ƒ) 'I#Æ¿‘¬8Úœì…\™Û÷ŠÎ¥PVÉ÷EfiY":Q˜ÁDZìȸÖSYMž˜Zi%£ŸN.í öèÀ¾ØÌ{–>jäÌc¢»vCÎB“;1DüöxÓôdsðÃGaUb.vÍ2Ê[ÁöÇ–³+Èvð_Œ?WÛ$G›`€ƒøüÒ ±„Š ¸Ûù—Qúk `.½ƒE?•«Û«8`¯Ll¾¯—~UTÇVe>Ëå÷Y+„B1Û+¬ÑqÁpe<ÌøpŽ£‡yÖy•ÔÓ8#^c¡_Ôt'Á†;.¢9Né$åíÞÜê»!Ó\^¨©mÓqi›%¯_"CØÎŒa´ l⬠±§ †“Ñã~zJ[“M\_€íKŸ¿=1BBÙ½KZZ2e„xCŸØž[§³$UvhH/èÖAÑò;Aæ£AÙì§f@8“Tqï°«L*KKX¢^úô®·ônBrì÷¤ÀcUÍP·>Íbÿ¦iOnKïG¥@ÂWÀUŒ‹PÞ¶¿k¡2˜åÕq­ ɇt÷¨ ¿ñsˆímèÞ!8 kÖ<¼-â)!`»êfI ¬«äц¬½‹Æš£H5ì‰íú}m¾öZô–dæ†=©VhÙüxiq#Ñ\º!3Ÿ òS­_e>}†WÂMH1*,ãŠU¼£‡¸Ø±XÞ.™ÔE¯òl8³cqI_ãa{CØÎ±L™Zû-ù†¬›5AÖ›:tÂÒ`(¯wÛuþ~¶³¸¥ù`aÌpÞ1l‚5„KOöï”Þùó}c»rF;bÈUÙ|³ô«6Z÷iÖ+¼ÉëßzMG¸Ð*Yݬ_Àp–ŽC÷5‡Eÿ¯ëþ$!}+cÛ>}ú¤ÌÉr•q&vTàÍ‘XÑ$jßZ4ÝÇŽfKvM?¾!lGAДҶû¡†Œ-Qü4öý!¼††_¹0t1¶“9Y€ulÊö$\12mÚ²×|–bØ®Ž‚½âXP¼elW¸^$ò0Äѧ`«mÉýK!Ä[`kÑ?ÿüsErبHBKL¿ u³ùÂÒ{$”‡K¿FÀ /Û6b0Üýb]» Ñ-ƒÁ-³´Œôœ°”ÞE©»|ž™¬‰ ’Š9kóО¼rŒ~+ØþØg cÆèu6ƒj–Þçë«xÂ˶(öáÃ@"‰e´Ùn\í Y¤|“X#x¤$ ¡˜_ g¬¸ÛµíüøšÝ ÁZÄ™–+¬ãòð0DEc¼ˆÍ›,§Kçé%MêU––”,$ ÉWˆ–yRauìãFˆ]Z(šº¤¬ƒözüÍ‘6Ê.ôš;Ót̓W®ï‚×í-c„¹È–%j¨âaV c©B%îsS  $sCl§§¼¥ª²Q„£¡$è÷Mf†H>lû‹íd>…<$–Cq>“õ¦6’VÃÞ˜ñxç³J<ÂC«a‚1DJ‹^TLP·ÌW‹–9PXÆÀ#Zñ<\NU94H"Ì%Ãc2¤uø—GTp!õÊ.x[Ø®õÄHGBm³úGLÞQ>Sû´ò!éñ¯ÄöÒ¯î‡(xŒ ,Äh7 šëŒÿÑëTü¨Øž¹4¾´ôM$òŠ* Œb)ä<$3Úö4òvWjW–Þíú-½ý¯ŠFãD—‡^‰Ë=ÚAö¸.Ûì&d<Íã¦ý›5úm‘bfô^³ß–˜wÀ|÷¦°=œÊŠ"?íþð1S¨E 3É)—b»Úo˜þ+2X•ŒÏZ–Ý7Ûk5«â°ØnÛ'U˜)*6†–~°ÖøFSgøaÈ^¶t‘SçTó¸o#>/n¾ŸñVDláN.äÉ ©¬Î‚z`ä¼"Œ«Š©‚£u°Q‘‹&×ÎoÛß2†évÎ _ù¢$Š^Ó£©"Ùlq*nŠí1B¼ÜÜšj¥L†`C_ÌÌ)Ûå ¸Ã”/|Œ,Röª†š p3O¹U^ñˋ䋣c_G=ÄS¶°á¢7 Ÿ» =Jn1šî•3ˆ‚(2WRѲVïuý%EHö5‡òSË^u“`§·…팪âÁ©K¿F™‘Ðjž\ArÒn}¬ÎâØ2ÍTÌá¯j%ZIkÏOí!óÄöa‰Ñ‹±šXzu0Kšò[™Š˜ô'ÉDÍ‚È4Sœa%z‹$Øy…èîÛ^nE-ÅM­ÒçÏŸï·´öpRaøc×dÁÓ;ËÛôÌd825Öé¿5Ò“+»ïma;ÊÒŒaElש­Öœº£¬ÉýeCoˆíD€ S1®HଈÁ¶2h{_-¶Û׌K·Åv{xˆí4žÇB–† F”·Üü‚V‰¥ã‚õ…â¿v¥uº¾b\*£ÔvM|sR¯;lª|ÕUNbR ÔA{)oÞ‡ê¼&WV^pé•çè)½!lLâ͂Í6ffxÔÊóNÌ1€j åGÙF·Æv.ái5Ôl39W¨¹Ð׃í Úœå _ƒíksnk‰Š¢ØN†ðWµÕ­u¨¿BýÒVO4„2¿«¯„È¡²Ñ†é\s}·ÞG6|IµF¾9¶¿kžfU¡ÀX`šrÞäÄF·,¯œJO¼oØAÒ¾¸ÅrÉÛÂöǃS ¥ËñW­yˆâÛôVÇЯ‰í6%WÖ9{é³Z¹åŰ]ûš•ͩ£ï1z#a;Gç+±½lÛúž…íѯ}gÔ¬¥—~9ÕŒIg yeMJŸ”¿žd žukô#‚‰ÖÒ"*éH| »SIyQ5ähoæHÊ›å6Þ™ú[Ç™I7€i·ZH}÷±UÕ¨Ø÷ÆäªfZ›³Ê5\–úVØN$Ñw™ì«úG§7Çv³´‰cZ%URÃö"~é+±ÝG É¥÷oD?²ü••Q±Ñæ`{ìLa´Vbé]+Vmýˆ¾¢°ªÌÇõJ Iœöªg†œ4„ Žsg¶¼b…Í,'õ“›ŽËo ÛïäàTœw »¶‰ÇPÀëU<í_6?º!¶Ô‘ÂOÎÄÛY¹¬ýrSlWq]Åa¸H ²¢´•N8 Ñ\úxPì©°ù\l‡­^6lÏò9T(­Œ¶z².¦Ø°GØ^>@0ç¹ÕŸåªThe–ÞÿlÕP©‹–°Ž¶ßkÃöw#ÏÌi;Œé^ÎÙ!¬ÓU‡bð ‰nª—Ãѵˆa†¹Œ£w½?í aû»3Nå3r¬þÚ³'™±GçG·ÅvEEøamÍJŒ^Ç rõù=ã‡w>¶GÚ;£5шÞRÔ5D9¯BÖÏÇvb™ ÕXûiŽö¯ù5޳ÙÔ׿óCöÅh˜ˆ^M†\^8U~´ÚúW»€rûj±ÉÞé™AÌÌÒÎ+9€kí¾×ïOâùprÁgS*„/‘2âÍa;à íàÔˆ#Ç´æÑÿU «¨ËÈoùØnB«­¬Ú6ŠiY¹ôë±]Âo*¶®×”K!פô&=6)ÛÙ„x&¶k -æÄJ7ÅÔ"¢÷á,É9cÐÇf?3(Qïë½o؉ôum£JÝÚÖ^³Ýþ®™ÁÔ»V›nOL¸J?èïÉp|_š²Øä*EêÖ,3K¿˜7:niéÄãFûÎÞ¶?ÊÁ©pR© XÝb_6"©’þ éb )tá¯É'CënM ®°vøü’VÜ"iİu|åLl²—ÍQ£}:r e1 Ÿ›ó$¶GsJðƒHÀ¥(Ay¶h…u”ßÃ\“l{/-|ýÔçá×B#mhÒbˆb{$ãвR¿ªµT§À˜w[Ìgߪ)ƽÒOH‡}t+¢ ïṉwÖV}K1Ae˜ž"n}ºÊÅöh"Á`fun˜™Tĵeõ×>ZÚ& Ü2Äv‘†\€íø•L^Å©b¼ Q<1agEBÂacUbÏÄvýŽ¡¬æL3ˆ"¡É'ÙneP}ñ|lGÍ[ÚtŒFgóX?µö~¶QÇnvßðE­03?© Ô±FŸÉýµÇv¢X|xxxxÍØþN'ÚI%85›©u¬sUZ2ø£®'Ó­‰öÅž„+–á¨ÈÌî)žÝùØr¬ÝÕoÛwNUHÑ UÛaµ‰¨|R÷½¾¶ë+¸&Ê™ ™eOë¯Õ^úÌQå¨Ëyؾ$Ï!ßUÙ(’âÒªguVUe%ÕÞί`»r‰çò¦ER ¿@E3cä×UÀˆüü7+r桲Q[gŸ~™ÜËŒ]7ŸLÞp÷ö.e2ÏL%8WéÄìÇDõ,z{Y·òæ™x®ÿšü«`˜ŠáÉ> áàèø½IǽElGm£91ê÷™»$ì°+yÍÇ8ƒ†·G«}[l_z„ÖäØrS9± 2ÉÌx&¶ë§tнΆ‡W®¾À*Ùž£2Úe|Œí¹’«„?­bQÛ@£#HIv»6!z9ÏmQT_Ûâ>›f¬³rcî mrÃÕ˜©ãš8ðø:ö¥é±?P5Zâ_&F6qd¨”[xf®ÇóÜ5Ú¹”:Ó)ú]y~\½€ø¶«$o Û1èÛ9Œ885F€³§ %Ù™h)—emƒWù¯Çö8%2p'£J8J,‚=•»×v}13m(·%y$xsí×OsY‘²è£k¢p½ça;Áœn­Œ½ÎÊðBaÖŠ3 )ýPU„ìuíMö£ŠÁ^Ûáó*:žj>™xÇs¥hŽehÍioP‚96LÇy¤•ÅÃ>Xú‰RþÕG{M»L?Å:ëš^ÿ…Ñâ%¸!“ß ¶¿;<8UYm|ÎÈÆ;Y£‡ÄnŽí9(=úUÔ¬ò&„ÖL“X{>sÀ°=öÕÄì^>64Dµ¬¶š¯¼Xé&-7ãªÕ û¡€k:©ph–[3Ùkª˜ÃâBÄfi¹°È]Ã/g`Qƒ?zSaÐBY 6oâ­×Œí0–j%a§A­ªN•-nj‘ƒ«(É{ÂyðSŒô=’:X_?‰çŠr¹£µÚ«,¬„lA7j £=nçy÷f±=¶Ã\ôàTÝX=ÎÄ(@"÷&¤†Á7Áöl¾F?¸dˆS´±š "©ZeXP<Û#ŒÖßcï ¼ÀÐ3ê\¯mSç“ØÎO­-Š’•êl§¢÷2ÓøŠrÛ~ˆ¥%H‰Mˆödõ4åµ,> ã£ÊM…£üH=3p±Vk )#˜l™ÍW !wA c]$0ÎôŽ>œçÔì†ïj8‡ÎÝÐ.âjT˜ë¿þú+°÷¶Fû»7ˆíû§j5Öä@XÄka½¬1ɸ“3m>‰í¨ÆÛÉ“ ’À7=@S¿ Rrs†fÆð“ÇØÎ®Œdôê3ÑTߎ‹_ 9@óás°ÏcÁC3Î-5H7>h U$2þÛuž€ŽKÛË·ò¬ÐºfȺI6ÔkÛÀ ™WÜ>¤Ç-½êÝN1ëe‹ <»Ò;w_f ~MŽq•Ÿ¥'í\…Aݯ½6ר- g°â3ò¶K$oÛ%cTIC£K®PFV“jÚ¨Q©åc{ôºlÌYû#ÉûÇÊóù?´|g(½ÖqÖ‰{Ø®CŒV&—~ /YÑHô7‚!LD/²¨z&¶ÛðÁHï¥%à¾ÈzÚ0£¡ÙØhã+LÝasŒ\2¼ÒI\é ^Øc‹ì‚ܾžóRÏ!ݬúåËH¼ão‘ýŠEú¯ÝÜëbí8e»>c¢ø$¤S°a]RxR9Z/àÇ|Û.;ÆvÕ)¶‘íU¦)[¾¶[…ÁL†•Åú·$˜ÿéÓ'뼇í4¹UfX 6•4Î×V9;I…phç™âëE~ؘ£ØŽï0ÜÈÌx>YŒk3õ§a´hµgTw,¥ ¥N/T É:¸ÔtñËs6ÎP”iYN¢·Ò£ŽæË¶:¦psl§e‚*©“–•‚­©µ’¿bHÖ9c;õWENù£`¸$i‘àºú‡r¨ÊnÿZõ_BŠ^ë\òÎü¤~?c£–hwÖ4Ïe¿ í@cˆú* Oª5†½ú:¿iÑm yÎ5´‹"£¼•k…8èhU ­ Áæþzj'áj…“*«ªV¬ƒøÖFÈTSzñúv=3t¿G‹ŠÌ «jµöö³u“ò3³1zX0RHÞý¾ Ò— e÷ñüÃÃ/µƒª„`‹2·Û¬”«Ø/P­ÃÚ²I«™äkcUðø÷¾Ñmåí®e CÐÈ YÏ¡Dþ¯²K^yl¤ŒBCþüóO<‰W–Þ¥ÃB—äo?µ ·œ»éÖZ•Ò!Z[²I3Ä(J¬.:GÃUJ»ÑÒ¯S(åú kK¢VžZB0î¸ÑÊœRJý>ke6-öïXÍi9³álŽÒ†½Öüè‡}’ýŶ¥‚d&¥ÒÒû 3W­sKïóÔ( (p$ÞµcÂÞ¶[1 'ΊÀÑy™«MêØQ »c&:ùluPá~+ $§ó?~üôé“çÀLãë†Ô^¨³ÈUª»J3Ým3QW¸°¶ßü$†Ab@¬ü,’ 2Û6ª8¦&ª§Ì¶ô×_Ù‚±½–X±ÊÔ„àEì£\ÙÙšÊ'©›ñcæNµuYÔµïhSt—æQ¶(…@´~v8M>P=Dw4@óo\Yá»>‹{ˆË1ë©Æ:Z•~ Æ3kl×¶1 ¾²ú&È*¬B“AbwÖ-r¯]´XEu›Vè‹&ÌŠ?‘ðŸ½Ãñ«´a…¯hH¶"U`X«*QTURaÖ,fvѦ]•·l¾µTÿêÆÎ]×ÿížgö)Û¢~ ë  ¶Õ"y´ãbÅ÷ïßë^*5{t´×§:1Éãå â ·ÞØø5I=3!iÄNm³ªbYa=¢&„J ;ôØD7…ækË##¢‘œKïYRp¦RP=¢Ç%“gìh–§nôV<1¸6j•l¢ªð¨ D\"¡Ê¬rzf’+ùer›ã~ºU%ìåa0’y¸…ú¤,?ˆ1(¡îG‡°Ãwr»´ÇU¢È=ðŠ 4õ~ÕÖ:”@V߮Ѣg†›U‘F¬ÈôVíå˜*‚¢ºŽÚ $Jœú¤eó¡a/¤â91ȼ^t¡ñœ^—oÕ)Àvºƒbó†˜aœ€¨º±íj`D´oD}¿Õú¾€lêøre)}2V­­ÂQFx¨L‘]ÛŸ?ÆOZau^qÇ•bB¥¨kVÁÒ"¦¬&Ä7EZÆZP™à,(8hÚ• ÖYS ëC°È>˶Xõb„“ðŠÃ–.›Q VýÅ ¾².×Y²Uæ5§vÚ,l~¬†èL$doÔ"ÛI´¶«ŒÎ6Nñå0þå\&”ÂÌ—ƒ‹vÊ*󫀩•½ÈFÁ»¾l‘9>¸úFvÒãh³*ÏJˆ¶big¯›¬RÒ)'ÑeƒtЦ~(Î"âÏÇóo»êÁ”›w-)ºú ÕL1£^›–q,À5Üwí@õ]_PIºb À …sÅJûwI~KÕ¯%í5PuË‹¿`&8˜‚AHˆT†”¢_(Ä3á¢3…Šaó”êljjY¼“MÐ\%ÖœEñ^Åñ®:BIÐå×èMM³”J[‹ç¿àREr,”@;8@ÃeM”¶†XO-º‰ã216dÛ²vq‡¹Nñtf oOéÄ@¢ºžZ¼Ó±áàuØÒOi@”ö¦2Ó¦ Ëhkª@,QÏçM`á[=3j¿Q/Vqœäy:W™ëQN²DI…â9¡@ñ<ú%Ñ׆çJd&½ ȑŶ+@)=I—o*¼Rjº_¼vÏå€h“5®;£#èRPíÈÓ^«¹öûÒŽ$¶ƒ¢Mʶ^V¶#¹‰<ê×ÕB92ªc„®Á16`iI)uâwBLw5QXãØ,Ñò Èî£ÞÄÀ*¦Í´!äÜدW/h¨àÙò´¶TÙ²n¾,¸D ’uÚ… ~rm|!«պK›¶Ið†B¦¡‘ ­G'¢š:lmY($K¿8«3µÒ[V JªTûåËŽhoÛß휬ÊùžK¿m<\ûÉæÒáuÁ»?~„@îá9ÇkÃóoåB.eNêŠÞ*ñxª!¶ACŸ¯€“qi~3tt£¹Œ`ņ®ènj+!BµI6dŽ¦æ™‚ÈÑÛ£ß@Çõ; gÞ€Hv ¡>ŒÔÄx½bZW…-úµHòœ6jˆéÈo®}xü©E®.ÍkAWßç;àäÚO.8 èúSÖLz} ć]]?X[ÇüH @"L\Íá@YR¾­²€ÏBÓëÇ÷¸ln žÛR/ି¢Ú)ùÍEv¢ÙŒ•æ4M—hc1 ,‚pM§ UUq(¨Z­¥Ê(Ê {'ù¿`=ö!‘´¦Ð×ÜþI dÀ67âq(À«ZŠçÑ–fjïÔQ ¬‚Ú›u¸Œ†çœA¼ÅNÏÿòæI¢ñõHŽØÙi•å 8 Ä@}xÜ*<¿o;ÁLd £8%¡·?z:µ”øýÒø Êâ(LÇ3¼¯X+Œ9)6Ms(GCÀ[Î|¹¨A%âx±´EsüÄ%ƒß[ªÛß[ / aU› 5¬ á1 ¥.ÚCÞ0¿V¤5¸c7E³Ÿ°w-@1ZP=œ,åp¦C09S/>}úÄ´7û” yéårã9ÂïÉR4¹c]‹Fp‘"'|ëø3$.9DV¦Õ.8mᜈAtj£[þ‡[Dï^GúW z?(Š„øHï[8 ±ÄI"ªJþõ¹+U/ðÄÞcK£yŠÕàÉnsb`~‡ yB v¤îa;š£ˆYáBµ’*ZéœéÀXU#ö±ÅXâîښΩ혀[ƒ!G˜ \œ*,@ò17yL3&Z²zŽ8§B°…ƒ®¾ò¯F˜S`üBÑP4þÍaŒÒüHñÕâå¿RG®?¡ ?ú¬ziÕ¢6s…ãQ½‰jtÖÇhECްЦ¾_K6aOTØ.ŒÚ˜—UþׂÁ«‰Ž!E¹hÌ}tpüŽd]I3›ÒQ¤#=TàGÆs#E³ *ǠԕسóGK_ÉØ³z±'ùðF;;5Zîß"xÛÕO«8Ï'ŒXó¢³}XgšdÑFhª£Ö,NíÜöÊ.0JnTOm‚²Á h€ë”ò±­Ùî(ð$J~¤H8F Èdˆ²èí¡O?Ým³Úh.-š&ô}¿_o˜õNg@OJ]ˆ 9Ϊ§ßÇ’r5šŸ\%ëHhËý¶~ÄÎ…4j—‹7+@†³\°š}ZÅæ¡\4ˆsÑÏU«7G*!„Z>ª OlN™xnD6Rª$CÀIM;LÆ2‘…f9#c/c FÝ£&L†@ßòð¯bxŒ¼1º@̓‡ A/Ïš´7Vá7FÒuP`4Î]Oì~¼ö³+()ÂW©f7±&ùÙ’ÑJ¢»¹- „È<óe#\k•þ-tß™îdÇÝN~$cæÔí©°"f[†xZ´õÈ3le‘Ø–8}V3Õ ã Û¨Õ&ŸUô…K6Dªô"Ú ×ºÃh⹑™R€*>9IÊ,¥]# &'pa½r•Ð8mïUâ“õ'uÅ,’{?ÚbÙýA‡42wçm¡%\˜ÂÆfªÈ)£*s¬¬n|°õ£Ç6ïF/¨OÀÆ½É h†’Ï!†’ÏîVBÑ?%b•"‹Pa8S^jµÂ:”ì5ä°UžµRñFq$mÈ^=Kž• ÚÌ\úm‹þ>H5NhD]x|[þ_!e9Üã$‰Ši,½¦èDf¹Aœ@HàñÒïû1†DWJÚ²æÈõ_˜‚lø“•ÌpA„Ü#•=4,KA^Ó,¾ùd7iO‘JAMø<Íc+WGVþî<{éYRg 9³ˆw²@¯ËMІäκr{õ9¥ß¼èïƒT)TM˜'ž“Ùd¦ò3ãí$ßùi;1B΄`EŽ&bÓDß •a8 Ÿg?WQ ò,©0…ÍRw%£´#22ä>²ÎRÊ¿Gç?9,Z}–0KÝAC.^ÐÙk• ¹¬¥_§èRLF]LÿÙhO»ñë ‹Ãgï%¡nô‰|AC׺¾â9‡]u帊zM´ð9\ziñc)_¡›^𾛆Lš4 D-þi‹ýf؆æùÔ­IßH„·˜Ò'cÁ00Œ+s#Lš4iÒ¤'éQV»°™Ñ²Zåˆs¿¬’+•†ýI‡+£]1ßå¿I“&Mz%t€ísXòSúL­ë(¹wâÛ¹ÓhŸ4iÒ¤¥¡O†›ú÷<í!±Ž õš‹‰ù JËTùÝœk0iÒ¤I¯œòZ*öÚk^¬"YÕÁN{^Ý2Ø%Ê­¬ØÂÃX⟾‹s &Mš4é•ö»ë‹HÄ.êŠ*=0ša†Y@yÿ$§´€ôÀšé™4iÒ¤¯@Ø»‡Ì]Û»„ü«4Ý9QCÕzWHÇGöSÓ_Ç÷u®Á¤I“&½Zâᤱåú¸oÙq5†É£ˆçL –}ò $°­ÀþýD6iÒ¤I¯Ÿ4WÖ:‘Ù¹ÖïÛ¹Z´áy<ÏZÚ!hÈ‹|†Ÿ>}R‡ }zc&Mš4éë]îÌñk&–þt鵑f§¹ÑsߎmŠà\ƒI“&MzTQ÷ï¿ÿFhb…b—ÌFÏÀý‚ãgé¯q†¦h~'' M`Ÿ4iÒ¤oEt¹ÿ$ÇÇã …"G&1½Ì½å†c€pÿnKˆýS; ö.€6iÒ¤I“¾&=ögaG;'ÇâÀß ç1dz”[ÈÌ\™½»:ÿð¤I“&Mº†xŠ+ÍòþóŸŸ?Æad!G¹ñ2žÚ£Cy¦fÒ¤I“^ÁÆžw‰ÀH¸Ðq Yœ}”Û¤I“&Mú¶x§Ÿ\O"³CÐîöÏIœ¨>iÒ¤I¯ö\3ŠÑQnߺî“&Mš4éˆÎ?†lZé“&MšôFé?Bߺ.“&½RúÿtòtF endstream endobj 10 0 obj 30764 endobj 11 0 obj << /Length 12 0 R /Filter /FlateDecode /Length1 5904 >> stream xœÝW{xE¶?Õ§kÞ=3ÌäyDBHdä! ‰n" Ñ`ä!¢F‚T|²hÄ B•G ¨DÊS@>Ù•+¬®Èu#â*÷t'è~ûÝûí_÷»ßw§¦Oõ9U]uίίªX |åUÓ«¡üü?¬d„Pùó}Ãw4‘J*«o®š"Ýz@ÙAí›o¾mQe¼ûƒÚ= R2kæôŠ ï;ß°SÈ™Eû6ãý¤¯$½÷¬ªùwM|Òø é­¤ºíöòéi,‡6ߨªéwUn0 },é¾ê¹3«ï¾î[Òg_ *E\É7·Fð¶‚ÌúPGëó&3ñG%ÒÛÚÃÁÙngD«~5àWý•2tÎÃÄÎ3¢Îh¿øÓ\C?m6ä¿ÀS°ÃƒÑÒ^Óž°ŒÃÍ,Ðæì g¶‡3BúÐŽ¡ጠÛÙÁ‡Ña*VBJµ²F±°²hšärieK¤!l‰X|iŸXÌ_úPÊô—]zX›SÒp’¢ .Ýÿ~A»á$l—×Kœ)28MÎΡútam2s±9d®6ךå?&ØÌ*E½vÉ¡ßÖ\â+}ÌÒ®o¸E¾\#ƒ~pÆÌ‡Lí|}·&ê€meR¢[2¹%)Žü$Š«#ܦFåP§;§¢ãœó\nÆŽôä‚d‰•1·ßò2s«v–âÕ ~ðA}RzŒÜÒÙ¢¼ðøgk‹Åâ¨ØÆŠX/fbÃÄÓ C³îwJY•÷Ý7r”g dÙ,–E±<ñîŠÊšsº1§ø1L¾ª08£ÚÍ#à^ë3öæíF‹A“3ÊÙYÔ]RºL]{‡LnÓ9Ýqº³MÍ%Ÿ3X#GÜîáäfL,†ÓËÒ—=«!3z{MÔ•W`zŒûõ—.uÊ¡æ93‘÷`$¿OóZ «ƒWš ïµÂDZG kìGUß!ïÁ¤5)Ô•6H‰Å8ŬX‡yQqå÷!°Ú:™™jnn®îHgGØù·sÎåFéîǧ÷ææ/è{Ì?Ç7ÇŸï>ußÇ}ûWûVû·ø¶øwûvûÝ™ž ïHOÐ;ÉSì-÷„¼yj½+}ÿHUO%V*N÷Ø´êZ-O&¾9çÝýÒ«—n¸=¿bNBJß-Ï_úÜິiFÙ¹Ë~ôðÔó¬—˜N™¦ÕÈ©Q“‡x5¯å=ÔÔiipýîæøT⣞uB &Æ6ÀQ»ÚÀšVÚ°õè’A‘‚ #¬ùÉ:3uÊS†‡§ Ú ž§;žÕ×¥03§;mÜÈf‰•…M Žˆ Ìrdþöuó-š7wÑ"l•J ¯+ŸÊÆ1¤2®¬sÿ¦µk7i—W"ño¢¡*˜`ç&6¨l»©,&«Y2“kÎ({‰Kã £¤¨ÑY2¥¨QÕDTÉ¢¤‚C&mëÚÖ¥ó¡-ÜA{-A Æ@A´D ÃéP‹¥b,v‡¤šË ;Õ“O7”ÏjŠš¥JYìñÔð©ÍâpûëMMüEñnˆÀµƒ»àõvö%6\÷y6åé ÊÓ¾ðU0¨Ø$»5ÇãõpƒÑdæ²%Çëõ,VWvë9ì:än[£Êk”ÃWx,Vo¢&%^gw‹{]s…Æçöði¢®Nèî¬þYËê¨ØÜŒ¢I¥F§ý¬›ÛSMîÕd²2V¼Él6[ÌV«Íª˜<%Á– $Øã©¦4sš%ÍšfKSúùrMùæ|K¾5Ï–§™ -…ÖBÛXe¡m¡²Ë´Ë¼Ë²ËºË¶K Ø v£Ýd7Û-Šu°RÐoZ¿nˆ.“ÉËä·K& úªúNL ŸNû^ö J9vÞ‰i•åEÓ XôqQDn?[së©ù·ÌWUðãÞŽÎò/ˆcç32²²û§YÍ)k_Û¾#%…9 ÊËÍHWLžu/5möüÎ5Âõ_¹¶ý¿çÚéÏ5÷¿áڪ嗹&‡6tSÒE;‡¥sØBY90cÛj´n…'ÌÑŠ‘™œd«‰cW÷a¬v¯Zz‡v¾7›iôÉX)ªŸ©ÝX¥´J‹Y2(Žˆo„XÌ–¯¾ûîj:Œ¿ÿáÒ¥ˆ¼GL«ª¨¸MÏ­xq5aË Žt$ƒƒËV›‚ödÎeL–¹,%3‰kJê•Lï"»Err°¬1}°Ýé°[Í&Ž#ö8íE–.E.3 q¦-VO6‚RÕŽžLãgNSÏÅõ‹Ïtvr/Ü@ÀHN*>)(K3¥ùÒR©VzFzZZ+íõ²—J»tTúšJœÌÌÈ97p#7Å07wÜÆÞ¼·¡·1²ÙÌ1äsí¹ŽÑ0šb¡<š6Ü,Í4,•–ò¥†G”GìÏIu¼Îð¼ò¼}“ô*¾*¿bÅñkÅf¹ÑÜhyÛºSÙiß'TÚ÷;NHg¤\ÊÚh¿™ù™õgÙ~‹g•lQ·Rô³{Õ‘ê)“b-rè’Iºø[N˳?äygéK¹g¥ìŒ64DAƒí@ÔÊ8óÇxáÎׯóò±s.ãÍÁÑñc¢ñŸÎGZh¼üvBRzgaMÍÂ÷Þ»€ü-ZÄ_Å_ÄN6ïy­¡á5íb ö‰0•}lsQШ×.`z=dðQ-S!ËygWIèêrBO¤êµªçö¼ œ¬F0™òØ 6Ph³t€“àÈb•”ã§®Ã`DЫ--T’U«7³Sû ê¹D~ˆ•P]%ÓòSûbù°æË‚pÝä ¬vÁzz {‚å7j½{¢àç¹R.”ÊUò0¹I^"7Qr¥¼IæJÇää{ä#„}©æ¯]ú›i=+d)P/Õ³Q´Œ£¤Ãðù\É X=ËçùA8ÇY1õÜ % ûýD/¥¬‰žº˜—´l)›cß’Ç+á–r ÔÃS,Š´V8L~ŸŸ`ÁZ OñãRbý{p >%;Àl&‘LÆü8•ó° f2§ˆÇ .£_®”.B˜= m”.²&Q‰b^Bó&<,‡äåG©•Ðaf¡GœªõàÇY=yqÊPÉQ?­ÜCó„¥÷¤fŠqœ¤¸hviªtT'ÙV¶‹<xˆm•CÆr"ÔêåR8§aǤÄG±ŽÇcð˜a \ pdz¼IC ü"€ßXhˆ‚:Vh|€" Ú:À~üîB½L†d¨“ûâ‹ä»$Õ\Æ-‚ÃR.΀ô²‚5à h†yZ’bŸ·ŒÚ$©>g£WÑœXêÛ7Ù? õ_TŸÓèk„âFe‘¯¹««¸TNä“yR#Lr åÔÿÔxj@jQq©¯™]1zTϰ£C£È8©”n5Ìd=JoÓfmäú 5úÊgù–9—¥ä-sÎÌ 3Fš0uñƒfÛ4ÇПÁkÒÓu_¿üÞ—ë_ævnu6Ϥ¾&¸Ì1’Æ*Aû­ÓÿËÜ_3‡{ìüTbG¥¡ Z¥=°™WA)þDu%åþ(åM°Y³KûétÙSñH”߃ٚŸ„V>âùb(ík<]{iRZ!FµD[’ ¿¦m€¾=‰úŃtugÔfl0˜÷iß¾ºw*^ ýá.b¿DoA-~VòS-_õ TK«hß@Ö›ù!8ë ÒR€9ú’ê¶õÒûi÷È|z»ZHz ‚d²ÞšDïíÈÁC2A·Äë2N—±ºŒÑ¥›¢²Ó¨n]Óî‘Eë÷Qºt0;ÔP»C×´{¤Ï&p™ÀG>òp€?"ðá.øÀ3ñ:¼_àµñ¸Ø‚÷ ¬x¯À{"xw \xçF¾PàqÁüD¾ ‚óq^çÖà«oOå·§âœVEð¶Þ*p¶À[Î*·ñY™x³ÀÊLœYaá3VX°"(—ϰðrΰàô›O¯ÃSyÈÓ,x“À2A'”ʧ œrc"Ÿ"ðFÒnLÄÉK#ø'7ìºAàõK<8É…×MŒç×Ep"5LŒÇâ ñ¼8‚®Uù„x¼VÅk<8¾ÈÅÇ»±¨PåE.,gç…*޳ãØ޹ÚÅǸñjŽŽà¨‘v>Ê#í8âªÁ«hÌ«,pð À‚áv^àÀáv6TáÃbp¨‚ù˜'0×…CŽÆœìžÀìA.ž€Ù{åA…rá Z9+ÓÆ³\˜”3m80c#(0ƒÆÏ؈é6L‹Æ©y|@SÝžš‡ý+ðÊ ì'ð 7öUy_öñaÀƒ½S€þ½=˜¢b/Px¯úèÊ>z-èñ`rR> stream xœ]‘ËjÃ0E÷úŠY¦‹àG^ CI7^ôAÝ~€,SA- YYøï+ëš*°àxîIw²KóÜX({÷£j9Po¬ö<7¯˜:¾+Š’´Qa¥´«A:‘Es;O‡Æö£¨*Ê>bq ~¦Í“;~D”½yÍÞØ+m¾.-~µ7ç~x`(uMšûØîEºW90eɼmt¬›0o£íOñ9;¦2q+©Qóä¤b/í•E•ÇUSÕÇU ¶ú_½8ÂÖõê[ú$/¢<Ï÷u¢2Ññ Ú }¢S : V€Ž èZ{>‚蜨ÌAµ=¨Ã +)ÐÚSCÉ NtX»ôPâÖE%X_ºD±Ìíž³ºy#NÃMÙ.©Ë÷ù»Ñ-®ôýšl endstream endobj 14 0 obj 294 endobj 15 0 obj << /Type /FontDescriptor /FontName /HALMKU+DejaVuSans-Bold /FontFamily (DejaVu Sans Bold) /Flags 4 /FontBBox [ -1069 -415 1975 1174 ] /ItalicAngle 0 /Ascent 928 /Descent -235 /CapHeight 1174 /StemV 80 /StemH 80 /FontFile2 11 0 R >> endobj 16 0 obj << /Type /Font /Subtype /CIDFontType2 /BaseFont /HALMKU+DejaVuSans-Bold /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 15 0 R /W [0 [ 600 836 342 678 493 674 592 711 342 348 715 478 595 711 732 651 687 ]] >> endobj 6 0 obj << /Type /Font /Subtype /Type0 /BaseFont /HALMKU+DejaVuSans-Bold /Encoding /Identity-H /DescendantFonts [ 16 0 R] /ToUnicode 13 0 R >> endobj 1 0 obj << /Type /Pages /Kids [ 7 0 R ] /Count 1 >> endobj 17 0 obj << /Creator (cairo 1.10.2 (http://cairographics.org)) /Producer (cairo 1.10.2 (http://cairographics.org)) >> endobj 18 0 obj << /Type /Catalog /Pages 1 0 R >> endobj xref 0 19 0000000000 65535 f 0000076211 00000 n 0000002354 00000 n 0000000015 00000 n 0000002331 00000 n 0000039673 00000 n 0000076046 00000 n 0000002491 00000 n 0000002705 00000 n 0000039649 00000 n 0000070667 00000 n 0000070692 00000 n 0000075045 00000 n 0000075069 00000 n 0000075442 00000 n 0000075465 00000 n 0000075746 00000 n 0000076276 00000 n 0000076404 00000 n trailer << /Size 19 /Root 18 0 R /Info 17 0 R >> startxref 76457 %%EOF PyTables-v.3.1.1/doc/source/usersguide/images/pytables-front-logo.svg000066400000000000000000000434461231437614300256430ustar00rootroot00000000000000 image/svg+xml Hierarchical datasets in Python PyTables-v.3.1.1/doc/source/usersguide/images/query-time-nhits-cold-cache-float64.svg000066400000000000000000001420261231437614300304070ustar00rootroot00000000000000 10 0 10 1 10 2 10 3 10 4 10 5 10 6 10 7 Number of hits 10 -2 10 -1 10 0 10 1 10 2 10 3 10 4 10 5 Time (s) Query time for an indexed table with 1 gigarow (cold cache) PyTables Pro Postgres PyTables-v.3.1.1/doc/source/usersguide/images/query-time-repeated-query-float64.svg000066400000000000000000001521261231437614300302400ustar00rootroot00000000000000 10 3 10 4 10 5 10 6 10 7 10 8 10 9 10 10 Number of rows 1 2 3 4 5 Time (s) x1e-4 Query time for Float64 column (repeated query) PyTables Pro O0 PyTables Pro O3 PyTables Pro O6 PyTables Pro O9 Postgres PyTables-v.3.1.1/doc/source/usersguide/images/random-chunksize-15GB.png000066400000000000000000001710731231437614300256340ustar00rootroot00000000000000‰PNG  IHDRÐß}™SsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwTTW÷7ðïÐ¥J¯‚  ذ;؈]Ñ<¶¨QÔÄjTŒšX¢b7£Ñ$?cL¢&,,øÚPT”&EŠ€4©3œ÷œ‘ë Ã41û³–kÁ¹gî93\‡Í™½ïá1Æ!„B!õ¢¡î B!„Ò˜PM!„Bˆ (€&„B!D@B!„"   !„B‘ЄB!„È€hB!„Bd@4!„B!2 šB!„PM!„Bˆ (€&„B!D@B!„"   !„B‘ЄB!„È€hB!„Bd@4!„B!2 šB!„PM!„Bˆ (€&„B!D@B!„"   !„B‘ЄB!„È€hB!„Bd@4!„B!2 šB!„PM!„Bˆ (€&„B!D@B!„"   !„B‘Є¼Ξ=‹ùóç#//OÝSyçýðÃX¶l™º§ñÎKOOÇüùóqùòeuOå½’M¯+!ï   ‘bÛ¶màñx¢ššš°··GŸ>}pöìYuOpãÆ lß¾………êžÊ;ïï¿ÿÆÞ½{Õ=w“'O°téRÄÄĈ{ñâ¶oߎ{÷î©afﯼ¼¼÷úu-..ÆÒ¥Kß™÷FB”EKÝ ¤±;v,Ú´iƒòòr\¹rW¯^EXXþøãŒ9RÝÓ#Df‰‰‰Ø¸q#Úµk‡6mÚpŽÙØØ 88>>>jšiŒŠ‹‹±qãF€¿¿¿šgCˆòPMH= :cÆŒ}ÿÇ`Ô¨QX»v-Ðä½cccƒÕ«W«{„òN¢BhÈ!066ÆãÇÁµgeeaÕªUèÖ­lmmagg___œø-Z´ÀâÅ‹QYY)6^yy9V¯^nݺÁÂÂÞÞÞX¹r%***8ý:äååaýúõèÒ¥ lllðÑGqÒîÞ½‹qãÆ¨¾¾„×Ô´iÓêýÒh0BHBBBvôèQNû³gÏÇc­[·æ´‡††2[[[6}út¶iÓ&6cÆ fooϰíÛ·K<÷ÇÌtttØØ±cÙ'Ÿ|Âtuu™––{ôè§ÿúõëÖ¥K¶yóf6þ|fnnÎúôéð¤¤$Îülmm™žž›5k[¿~=ëÞ½;ÀfÏžÍ9ïŒ36jÔ(fjjÊæÌ™Ã>üðC€=š9s†éèè°Aƒ±©S§2===¦­­Í¤¾~GeX@@³²²b3gÎdË—/g§NbŒ16sæL€988°E‹±?þ˜ikk3SSSÎù322˜‘‘?~<[»v- b­[·fؤI“ÄÆ6lÀ†ζmÛÆ&L˜ÀìííYûö홉‰‰Ôygdd0¬_¿~ÌÄÄ„õèу͞=›3mmmvíÚ5Ö¯_?fkkË,XÀ<==6nÜ8ÎyJKKY×®]æééÉV®\ÉzõêÅ0///VVV&ê{âÄ fooÏf̘!víìÙ³‡sޚ׎®®.?~<;v,ÓÖÖ–xí¼íÒ¥Kìã?fØÐ¡CYPP bGŽaŒ1vïÞ=€…„„ˆ.꯫«ËFŽÉ&NœÈttt˜µµ5»ÿ>suueláÂ…¬Y³f Û´igì””æììÌx<ëÙ³' f^^^¢çS&&&¬}ûöÌÁÁM˜0mÛ¶ >œ`Æ ãô­¨¨`½{÷fX«V­ØÊ•+Yß¾}ÇcìÕ«Wb¯ë¨Q£˜““›7o[²d »téRóY°`ÀìììØ_|ÁÆŽËttt˜‰‰ {üø±¨ß£GÄ^WÆ[³f ãñxÌ‚͞=›}úé§L__Ÿéëë³Û·o‹=~àÀÌÀÀ€ 0€MŸ>éëë3É:wîÌš7oÎ.\ÈÜÜܶ`ÁÎxyyy¢kÖÛÛ›3€uíÚ• Qß   Ñkb``ÀÙ°aئ¦&300`ÙÙÙŒ1ÆâââØìÙ³æãã#º¦¶lÙR¯Ÿ)! ЄH!)€~øð!?~<À.\Èé_\\Ìø|>§M 0777Ö´iSNÀ$<·³³3{ñâ…¨ýßÿeجY³Dmééé¬I“&ÌÇLJUTTˆÚÏŸ?ψÐÂùýóÏ?¢6>ŸÏúôéÃx<»uë–¨]@÷îÝ›LŒ7Ž`ºººìþýûbc.Z´Hêë'  õõõYZZçØÙ³g6sæLÎ/츸8Æãñ8ÁTee%gnBcÇŽeXTT”¨íŸþ·¦µk×22Ðo¿†OžçÜsæÌaØÉ“'9ÏeÇŽ ;xð ¨M@{{{³‚‚Qû?ü ö’ðÿMPPP½^;B+   ‘Bø Õ‚9991Ñ/ª5kÖ°ÊÊÊZ+Xvv6ËÈÈR<;÷’%K8+++czzz¬_¿~¢6á/¶·Wó355åÐLKK‹™››s~¡3ÆØ6þ|Q›0€®ùK³fßž={rÚKJJ˜¶¶62dH¯\5a=xð`±cƒ bššš,99YìXÏž=™¡¡a­çÍÍÍeì÷ßgØ?þ(:& üÃÃÃ9¿²ÐNNNbÇÆi_±bÀùãÄÜÜœÙÙÙq‚ƪW¦Å^[¡š×Ž0°¬¹’YÛµSRRÂtuu9×Nm@8Ó·  €ihh0 –™™É96jÔ(@ôÇOVVÀúöí+6fBBÀæÎ+uîÂúÉ“'œö+W®ˆ}àììÌ,,,Xii)§¯@ `¶¶¶¬sç΢6áë(uBŒÇã‰Í…1Æú÷ïÏtuuEI  ?ÿüs€EDDˆ=^ø¢0˜>¾C‡bÏÅØØ˜`111œcÂUaá':|>Ÿéèè0OOO±ñ^¾|ÉtuuÙ¨Q£DmÂzëÖ­œ¾ééé ›6mš¨hò_AE„„ÔS§Nàææ†—/_"<<éé騬¬„–÷¿c ;wîÄÞ½{>ŸÏ9žœœŒ>ø€Ó6xð`Î÷ºººpssË/Dm‰‰‰€¾}ûrújhh W¯^œ\Õäädðù|ôéÓÜR‡’’’ÄžãÛóhݺµÄö&MšÀÙÙ™3?i $ÖÌ›7OìXff&Š‹‹‘žž{{{À‰'ðí·ß"**J,×8--Môubb"ôôôðá‡rú¸ººÂÉÉ ùùùrÍ»uëÖ(((@Ïž=ÅÚˆòÆsss‘››‹fÍšaôèÑbçÑÑÑABB‚è{i×NZZÜÝÝ9m’~6îîî2ýldõÑGq¾766†ƒƒ,--ammÍ9&|M^¼x'''<|ø@õÏwøðábçÖÓÓã¼&uqrr‚««+§ÍÇdž††¢ÿ/¥¥¥HNN†­­-§¸&IãIú¹×&..úúúX¼x±Ø±”””——#99-Z´øø‡BKK ß~û-¾ýö[Î1áóHHH@ÇŽEíoÿ 444àááôôtxyyqŽÕü¸¸¸ 99(++«õgðvî5 ~­ÙÙÙÁÊÊJ©×!ï*   ©§‰'r~6 k×®…»»;Æ/j_½z5Ö¬Yƒ#F`Ö¬Yðôô„ NŸ>]k1‘‘‘XÇã_ ‹„ÁdMvvvœï…÷ƒ–Ô×ÒÒºººbÅS’æÁãñê=?iš7o.Ö–ŸŸ}}}ØØØˆ³±±¯¯¯è”Ó§O# ;wÆêÕ«áéé ggg$$$`È!œ×µ¨¨fffÐÕÕ;¯LtmÏ]__šššbíD¯‹p333‰Ï1 úúú¢ïk»vþøã¬X±¢Á׎¢J³¶vàÍk’›› °¶¶†­­­XÿÉ“'ÃÍÍ­^óxûºmmmXZZŠ®ï¼¼<0Æ`ff&q¼aƉý H¾^k#í:îÕ«—ÄkQ(77çgkk‹nݺÁÄÄ„Ó.ÏÏ@Úu9f̘››‹µ«ãZ#ä]E4! tðàA´iÓ ,ÀСCE¿\<üúë¯ÐÑÑõÞµ¡œaaab+ioßC¸Ò%éW®\Ayy¹L‚²¸»»ãéÓ§øî»ï¤ö=|ø0444ðÓO?qVÏŸ?/Ö×ÙÙ±±±ˆGË–-EíEEE¸}û6 ó¤hÞ¼9tttàääT¯çXÛµóàÁ¥ÌOX©’p½[·nøê«¯ä:×íÛ·QTTÄ ì’““‘””$Z-µ³³ƒ¡¡!ììì”¶Ž»»;îÞ½‹½{÷6è5uwwÇ“'O°mÛ¶:mEþâååU¯ëR긦Qº! dnnŽ+V ;;;wîP½Â“““ƒ–-[r >Ÿ/ñVd²¦#\¸pÓž••…ØØXN[Ó¦MѪU+ÄÄÄ ;;›s,44Ð¥K¹æ£¾¾¾ÈÎΖx‹¿·eeeÁÈÈÍš5ã´ÿùçŸb}…¯ÕÛ@\ºtI,-B™455ѳgOüûï¿¢âkÃË/Ä®òòrœ9sF)óóôô 9…AYÜÜÜ`gg‡#GŽ ¸¸X®sñù|\ºt‰Ó&üÿ!¼x<z÷îË—/‹ý?Q___äççãØ±c ~|EE:¤à™Ifdd„Ž;âäÉ“ÈÊÊRè¹­¬¬`ee…ääd…ž—w ЄÈaÊ”)°··ÇÖ­[Q\\ ‡nݺáÒ¥Køî»ïðòåK$%%aâĉråç燞={âçŸÆþýûQ\\Œ¸¸8Œ7¦¦¦býׯ_ÆÆŽ‹‡"??GŽÁÞ½{áêêŠO?ýTî9ÉkÙ²epvvƧŸ~Š}ûö!-- %%%ˆ‹‹Ã¾}û0sæLQß.]º ??AAAHMMÝo[˜S[Óœ9s`nnŽàà`œ;weee¸ví¾øâ ‰¯•2íܹ À©S§››‹‚‚DGGcíڵغu+€ê@¯sçθtéöíÛ‡üü|$$$`üøñÐÖÖVÊÜìííѦMüüóÏ8xð .]º¤´ SHGG»víBrr2úõë‡+W® °°999¸yó&,XP¯{.€©©)¾øâ \»v eee8wîV­ZsssÌ™3GÔoûöíÐÐÐÀÀñûï¿ãÅ‹(**ƒ°aù?Z´hÜÜÜ0cÆ ìÞ½©©©(--Å“'OpàÀ©÷Aþì³ÏСC,X°›6mBrr2JKK#GŽ`„ rÍO’½{÷"??}ûöÅùóçñòåK¼|ù‘‘‘X¹r%öíÛ× óòx<øûû#,, ;wîDxx8nß¾­àÙ¢~@"]]],Y²¹¹¹Øµk€ê€ÉÙÙŸþ9ÌÌÌàâ₼¼<Ñ*µ<Ž?ŽöíÛcÆŒ066†‡‡0{öl±¾Ã† ÃpãÆ |ðÁ055ŤI“СCœ?^iA™,ôõõqåÊôìÙŸ}öš5kxxx`îܹœƒW¬XìØ±ŽŽŽ°±±ÁñãÇE›šÔddd„óçÏCCCþþþÐ××G=0cÆ ±Â?eóððÀÕ«Wa``€áÇÃÂÂM›6E»ví°eËNÎêÞ½{Ѽys|öÙg055EË–-Q^^Ž-[¶(m~ß|ó Z¶l‰¹sçÂ××_~ù¥ÒÆ1bþüóO¤¤¤ W¯^011¥¥%|||pìØ1×ë<={öÄìٳѳgOèëëÃßß8þ<'­ÃÅÅ×®]ƒ¥¥%F +++ÃËË ëׯçä¡7„®®..]º„~ýúaöìÙptt„¾¾>ÜÝÝ1sæL©9ššš8wîF…   8;;C__®®®˜2eŠØÆ&ŠÐ±cG„……¡²² €™™ÌÌÌàíí½{÷Öûg ɼyóзo_|õÕWðóóSÈ!ï£ìBêTXXˆ¼¼Ä ¢ qÿþ}deeÁÓÓ®®®byй¹¹(**‚““çXyy9222`nn.V@”žž ‰…O5½zõ /^¼€µµ5š4iRk¿ôôtÑJ¹­­-Ú´i#öKœ1†Û·o#55žžžpssƒ@ @ZZš6mЦM›rú—––âîÝ»(,,ć~SSSdgg£¬¬ ŽŽŽuÎ[  55&&&b«ÖYYY¨¨¨K'©ë¹VUU!11>DUUìííÑ®];±?dJKKqóæMˆ~¾’ÎÛk§.ÈÈÈ@“&M`ee…ŠŠ <þfff¢ŸCYY233aaa!V¬–––---±¢´º®ÍŠŠ ®_¿###x{{×z1Ƙ˜ˆG¡¢¢öööhß¾=çõ«ëu­çÏŸãáÇÈËË]Ç5 +++‘žžÎy]kÊÊÊ£G••xyyÁÌÌLìñ¦¦¦b……¢k«¦¢¢"äææÂÖÖV,ÇZ  >>=‚††жm[NìË—/QPP€fÍš‰Φ¦¦BWWVVVbÏ¥ªª iiiÐÔÔ”XÐLHcF4!„F§fM!ªF)„B!„È€hB!Ž£££Ä´BQJáSUU~ÿýwDDD ??­[·Æ¤I“¤æ…B!„ƉV åPPP€AƒaúôéHOO‡––~ùå±M-!„BÈûƒv"”ÃÊ•+qïÞ=DFFÂÅÅEÔ.i»]B!„ò~ èÊÈÈÀ¾}û0bÄNð ฿.!„BQ  èîÝ»¨¨¨À‡~ˆ%K– ÿþ Ddd¤º§F!„B”ˆR8(11DÛÆ~ôÑGø¿ÿû?>|ç΃ŸŸŸ¨ï¤I“pëÖ-±ÂBi9BÈ_Zn >Ÿ z›Þ»wïbݺuˆ‡ƒƒ–.]Êé{óæM¤¤¤ˆåF§¤¤ ¼¼üiKHHàü'RÆW®\‘û|)))¢²<6""¢ÞcûÖÕ/../^¼¨µß;wœœ,ñ±yyyxøð¡Ädž‡‡¿s׆²~–uµÅÄÄàñãÇrŸ¯>?Ë·Ûdù¿™™‰øøx©ý¤]çΓù± ¢¹¾KׯÛm‘‘‘HHHhÐc»]kÑ7?˜;ƒ1<º_­ýêzýêÛ&|_‘õ±²ü_¨ÏûPll¬è½ER¿¤¤$DEEI|lff¦è½åídž‡‡7èuQT[xt8ß=ŒŸ¢mo®ðŸåÉ“(?|8¾Ö~™™™¸uë–ÜÏM–ß)B²ü_HMM½·ÔÕOÚõQïÇæää ²²¸yó&Hi£G2ìûï¿ç´Ï™3‡éèè0>Ÿ/jóõõe^^^ªž¢ÌNœ8ÁNœ8¡Ô1&Mš$÷9:OYÆ®Oß½{÷²ëׯ×züèÑ£,44Tâ±{÷ÇZ´hQ¯9ª›"~–u g‡’û< ™§,×X}ç)mu¯íØÒ¥KÙÒ¥K¥Ž­nr½·¬,flZaõ¿…Eµv£÷–jïò{ËУCVƒyìò¨³Ÿ\?KWWÆÆþ÷¿Z»Ð{‹ôc^^^Ì××WêØÿe”ÂÑ@îîî€ÒÒRN{ii)455Áãñ8í*›[Cyxx(}ŒáÇË}ކÎS–±ëÓ·cÇŽ°±±©õ¸§§'ôôô$³±±ÄcÝ»w¯ß$ÕL?˺4oÞM›6•û< ™§,×X}ç)mu¯íX—.]¤Žû. ÷Ùú¾¯ï-q9qws÷:ûÉõ³®Ü[ZÖÚ…Þ[¤k 1‹ºÑF*rðöö†¾¾>ÂÃᥥ…’’´lÙ­ZµÂÅ‹Eý„ùÐaaaêš*iD&OžŒüQÝÓ ÀÉ“'(ÿµZõ ȬªþÚ˜l6Tï|1u¾·ð«øÐÿZ•U•XÜu16õÛ¤øA*+꯿ú XµJñcüGPÜ"å@ËaçΈE‹-”——cÛ¶mêž!„òÎHÊOBeUu»EÝ+Ð –“óæë:V  QJáC×®]qëÖ-;v qqqX´h¦N kkkN?GGG±ä}Bjóv*!µQ÷Gò¤qQç{‹0}žÂÑ`5 /)€–µµ5tuuÕ=w­@Ë©eË–øòË/qäÈ,_¾\,xÊÊÊRñÌHcµaÃuO4œ;Ru¾·ÄåÖ •µM´ÂPÌ"­@«Ýó™Ôå?“úz¯sŸ‰Â©ó½E¸mªg K}%·”¡0³HGô;"22;vìP÷4H#d``€o¾ù&&&êž !„Hô$÷ ÀÍÜMyƒÔ\¦»H%£ŽwÄŽ;è£XÒ Dhh¨º§A!µ¦p(-}x@khææÊ‡Ð ´ÊÔ§ˆ°{÷îôñ=‘ÙÙ³gÕ=¢&9¯?²¦{¶’úxüø±JîÉý¶ÂòBdgPb!ð&€6545•7Î@yy9JA+Ð*B ù„E£"B" uª¤€¨×&*¤~(f‘ŽV U„ò !ŠFE„Dêú„³æ-ìT’M´Ü(f‘ŽV  !„¢4ÂB ž\Í\•7ÐD…(€&„BˆÒS8M¡§¥§¼(€&*D´ŠÐN„„EËÉÉ"ÍãÇÕ2®0…C©„UU@nnõ×@Ëbé(€VJÈ'„(Y¨£ˆáiÞSJÎÎË«¢  €b騈PEäMÈ/((ÀË—/¥ö300€eß<ÊÊÊ™™)µŸ––4!Dq¨ˆÈBE„©©(©, ¢;p@+JGt#1hÐgˆ—þ棯‰‰á cÙ²-øùçÐÔlZg?Æ.ãîÝŸ(ˆ&„R'a! äÚÆ›¨Є@ ‹¬¬ÕRû¹¹Mn𥥕ÈÉ™ yý¬­WƒÏç7xø÷ßQZZŠ!C† ::.\@QQzõê???‰³â¨Ÿ IDATyòä ÂÂÂøúú¢U«VûâÂ… ˆŽŽFyy9ÜÜÜ0dÈXYYqúÅÄÄàÊ•+HNN†››úõëggg±q###áïïââbœ;wÉÉÉèß¿?ºuë TTTàêÕ«‡¥¥%ÆŒkkkÎyN:…¦M›¢{÷î õk×`dd„>ú­[·æô}ô袣£1hÐ äææ"<<÷ïßÇçŸWW%V±Bˆ©üÐÐD%(€VJÈçZ±b233ñìÙ3Ì;æææ¢b¨+V`íÚµœþ;wîÄâÅ‹ÁCË–-‘˜˜@€õë×cáÂ…œ¾çÏŸÇ”)Sžž'''èéé!99»ví½{÷Dý‚ƒƒñõ×_CGGÎÎÎØ±c´µµ±k×.|úé§¢~ÿüó¾øâ üðÃX´hªªªPRR‚uëÖaÁ‚X¿~= „‹/ÂÌÌ ¹¹¹Ø¼y3®_¿ÎY¥Ÿ1cÚ´i[[[;v ...HHHÀ²e˰oß>Θ§N²eËpðàAÌ›7UUUÐÑÑAÿþý)€&"´!‘…:v"êkëÃÁX‰ŸZR­P´¡tTD¨"ªJȯªòóö¯¢B%SIOOÇþýûñ×_!++ ÷ïßG‡°qãF¤¤¤ˆúEFFbÞ¼yðööFVVbcc‘••…nݺaÑ¢Eœ"ªgÏžaĈ066Fll,’““ñøñc¼xñÁÁÁ¢~¡¡¡X³f üýý‘››‹ØØX¤¥¥ÁÍÍ Ó¦M“X­¾hÑ"lذÏŸ?GRRˆmÛ¶¡_¿~°··G\\222°{÷n¤¥¥aóæÍbç Crr2233ñðáC<þÞÞÞ˜>}:Åú/X°?ýô ‘——___y_vò¡"B" u W ]Í\ÁOyÕ  éJ¹Q¡t@«ˆªòããSÓ†ý;tH%SáóùX¾|9 xzzbòäɨ¬¬Ä7Dý6mÚÆV®\‰¦M«ó³Eñ×_-ê»zõj”””àðáÜ´###NÁÕÆëÖ­C“&MVVV ‚@ ¯iРA˜>}:ôõõaoo)S¦ ªª ·nÝ®]»àêê mmm|öÙg066Æ•+WÄÎ!ðå—_Šž‡¹¹9‚‚‚Àçó"Ö¿_¿~6l44ªÿ«ÒŠ©iøðáTHHêME„¢[Ø)3}x@::Êë?€Š¥£š¨žž ÄiëÝ»7 55UÔ ôìÙ“Ó·k×®000ÀƒDmwîܳ³3:uêTçØÑÑѰ´´DÛ¶m9í}úôÇãœSèí@ÅÇÇзo_ŠÚ544йsg¤¥¥‰CWW=zôà´õêÕ ˆŽŽëïïï_çó „wU)¿)ÕŸ&*µ€ MTˆÊQô{ÆÒX¾¼a=~øÿO±ó©‹µµ5ŒŒŒ8mÂï+++EmÏž=C»ví ¯¯Ïé«££oooDDD€1‡ÄÄDtìØ±Îq ‘ŸŸ¡C‡‚Çã~¤hii WWW‰Áo‹-8ßHl«Ó¶m[ÑŠ·©©)\]]94uéÒ¥ÎçB!ïªø¼x00*\¦š¨Ð*¢ª"BSS`þü†=öñcÕÐõeccƒGÏçCKëÍ%[UU…ØØXXXXˆa{{{dgg×y>###èëëãþýûbÇŠ‹‹‘””TëÝ=ä%éy””” )) íÛ·믩©©”y÷Y¨ºˆP˜¾(y€h£"Bé(…CE(!¿á<<ž>}ŠÀÀ@Œ5JÔ×ÛÛ!!!X¼x1\]]ѹsgèééáÁƒ044DTT`âĉ ÅîÝ»†V­ZáîÝ»HIIÁòåËÅ ý¥[·nxöìÚ´iƒ¶mÛ"22qqqX°`:w1 !D„+ÐJ_}.(xsV  ‰ŠPÝHL˜0 & Sê]»z£kWo¥Ž!ˆââb±ö¦M›"88Ý»wç´ïÚµ C† Ahh(âããáïï±»xÕ«Ë~~~8vì˜èn#FŒÀèÑ£E}444pìØ1|üñÇGrr2Fމ¡C‡Šî"äããƒàà`±?ôôô,ºGMcÆŒ‘Ønhhˆß~û ßÿ=nÞ¼‰®]»bݺuœ? {÷î¦ÜVBH£%ÜÆ[eùÏÐDe(€VÚ‰+00Pb{Ó¦M±zõj‰Ç €Ôëüžžžðôô”Ú/ uöñññ‘ ëééÕ:×1cÆÔz>ccc±ÝßÖ½{w±?"yY¨²ˆ0ûU6ò˪Óý”~aú@´‚P¡t”­"”OQ4*"$²Pe¡Z   „béhZE(!Ÿ¢hT@Hd¡Ê"š·°SÙ=   „bé(€&DEþüóOÎŽ…„ò¾®@kkhù©³r£š¨Є¨H×®]Õ=BQ a¡‹© ´4”jh}ýꄨå@«B-''GTHHˆ4?VÙXÂ¥§ooh*¦UŠY¤£ZE(!Ÿ¢hTDHd¡ª"B~‰/«wƒUz!@»*Å,ÒQ ‡ŠPB>!DѨˆÈBUE„‰/QYU @Å+Ð@+ Å,ÒÑ 4!„B¦æ-씾‰ @4Q   !„¢0ÂB€R8Èû‹h¡„|Bˆ¢Q!‘…ªŠ…„MõšÂÊÀJ¹ƒ•”Tÿ(€V ŠY¤£ZE(!_vùùù¸té233ëlËÉÉÁ¥K—( ÿ9TDHd¡ª"Ba ‡JVŸio¥ ˜E:*"TJÈ—]TT|}}qèÐ!Lž<¹Ö¶ˆˆŒ1'Nœ¨WQÕwß}‡³gÏJ<6wî\øùù)ê)¢TTDHd¡ª"Bá ´JóŸ  ˆbé(€n¤áèè]]]¥“œœ SSS˜˜˜(m Y¢mÛ¶033kð9¢¢¢pêÔ)´iÓ<s¬¸¸XÞ)BÈVAy²^U¯^ªôÐD¥(€ndRRRðÉÜOVœ†Wú¯ W¢sMsìX½Ý>ì¦1ŠŠŠ0eþD?‹F‘^´*´`Ì7ÆâÀŘĕ+Wj=>hÐ 4kÖLâ±çϟ㯿þ‚¯¯/ÜÝÅßœ“’’ð÷ß#66mÚ´Á˜1c`nn.×| yW óþ-h6R?†‡‡‡RÇà܃V ­òòr¥~ºý> ZEäMÈ/))ÁôåÓ‘ê—*^ú© d}˜…§v"`PZ´hÑàqþ7ãˆ÷‰Œß:À Û"42¿þñ+ÆŒið÷îÝ“XÌ’——‡¢¢"„††Ö@?yòŸþ9:$@ÇÄÄ`Ú´i°°°€®®.~øálݺÿüóÄ`;##™™™ptt„½½}ƒŸ!ê", ¤\hR6lPz´°€\Í\•:€7´¶6ðޤ¾²²²(Z  UDÞ ñû#ß#Þ%¾Îû¦¤uLÃŒå3p`ßñðþC<ÄCñๆüöùøöûoå  ÇñãÇsÚâããáããøøø4輫W¯ÆñãÇ :¸0`fΜ‰‹/Šõwqq}mccƒ7bâĉ ›u À™ÈBE„ÂGGèi© EŽî­Ÿ--Å\>yyy8p 444ðÏ?ÿ iÓ¦ :OëÖ­EÁ3tïÞ¾¾¾8sæ ¢¢¢Ð®];Õo K–,··7cøóÏ?qúôiLš4 ˜3gŽBž!„ü׈na§Šô €h¢6tèF¢´¬´~îð¤w©U9€z¤< 4¨¨¨c 7***0|øp¤¦¦â¯¿þ‚³sƒ¾¾¾µ¶=xð@Ô¶|ùrlܸü1þ÷¿ÿáØ±cHHH€¥¥%‚‚‚PTTÔà9BÈÃÓܧTT@¼  ©@ª’’lÙ»WÝÓxoÐ ´ŠÈ[Dø¡÷‡K «{…X´²n…%ÃVHø¤Ùl=½åÖuÏÕTÃúúú ãmS¦LADDŽ;ÖàÔ !ooïZÛÒÓÓë|¬ >ùälݺ±±±rÏ…U "B" e¦¤¢”_ €V ßEëvìÀîßÇ`??‰uA5Q¡t@«ˆ¼E„ãñãä‘n]{ hþÀßÌýÃÛ5,/²Ì£ úqü¸Z¯ Ýgºðm/¾ÒÛÁÁÁøå—_°~ýzŒ=ZîóIº½°ÍÖÖVêã…AÈË—/åž !ª@E„DÊ."¦o*ÚD ºžJJJðëåË(\¶ s׭ùŸ~ª³?JG)*"ï…Ø¼ysÌ6 &‘&€„Tè&ÉMÐ]¿;†nø/R===ìY»V—¬¾øqpOpÇ·_}Ûà1„~úé'¬Y³Xºt©Ü瀰°°ZÛZµj%õñ.\¥ßæ‰E>|8Ϥޔ~Uߺ²((¨þšè:­Û±©þþ€¹9¢C\\\ý)x–ŽèFdÙ˰ýÛárÑÆ1ÆÀ3 Il8†;bnó¹øãðrá×Û'¾=">€Ù=3 Ð~¬ Ûk¶( @Ä鹋/_¾ŒÀÀ@ôíÛ{˜õàÁ„††Š¾¿sçÂÂÂеkWtêÔ @õGÞ{öìá¬2ß¾}3gÎDxx8üüüмys…͉Bþ+„+ÐM´š ™‰ä[‘*Ôë&@×A¸úÌoÛ5bæ®[§æY5~”ÂÑÈL7 £‡Fdd$îDßAk·ÖèÔ±LMM6FWŸ®ˆº…˜˜ܸ{ÖÖèÒ© rþC‡¡¢¢qqqðôô;~àÀtïÞ]æóa„ pssƒ®®.nܸ333ìÙ³GÔ§¸¸³fͬY³`mm MMM<þжm[üøãbÛ{B‘N¸ífîž\íõD›¨Ô˺;:`À›ssDWU!..Nj.4©Ð*¢ˆ…ôõõѽ{÷™õ¥¥¥…: C‡ ?÷¸qãD·”“D¸‰JË–-‚Î;‹ŽIjóòòBHH† ‚O?ý'OžDll,üýý1qâDØÙÙ‰ú:88 44HKKCyy9š7oŽ: ô¡ i<¨ˆÈBÙE„Â]Užÿ P] ÑêsP§=käÈ:s¡©ˆP:  UDÞ"Â÷IÿþýÑ¿©ý0þ|©m-Z´à´-YRû]H´´´àïïgMÈ»‡Š‰,”YDXÊ/EJA 5Ü ºb«ÏBRV¡©ˆP:ZnSº !ŠFE„DÊ,"|šûìu…»Êï P-AII ކ‡ƒ_Ë'¾Y#GbÎÚµQÌ"ЄB‘KÍ[Ø©|ZC03S͘Ⱥ;V×§­ææˆy½ MdG)„B‘KÍ[Ø©<Úܼ:ˆ&ç.\€‹±1på JK!` ÆZZ°ÑÑõ)/(ÀÏ'Obí[9ÒD:  UD‘E„„P!‘2‹…+ÐÖÖ0Ñ5QÊbh•:ݽxTV—7_µl‰ùõ¸£JG²©B-""BTHHˆ46lPÚ¹…wàPYú@t=Ý)*}ÝÑȨ^¡˜E:ZVJÈ'„(Y(uï×)*+ (€®'a­É㡽¡a½C1‹t´M!„Ëz•…‚òê-µUº-܉R˜êt»°ÐJ_ššjžÍûƒhB!„4˜Z «ª€ÜÜê¯iºV @dq1€ú§oú¡ZE¨ˆ¢h999¢BBB¤yüø±RÎ˹…ªR8òòªƒh€è:<-)AŸ@¶šbé(Z/^Äßÿ-Ö>iÒ$´oßžÓF ù„E£‰,”µ¡°€P[C.¦. ?¿D´‰J½4¤€ ëƒh9ܾ}û÷ïÛ–úÕ«Wb}éBäº~ý: hKmBä@3‘…²Š…).¦.ÐÒPQXAt½hmmëY@PÌR@ËÉÊÊ 'OžT÷4¯¾ú QQQÈÌÌTê8½{÷–Ø®££ƒóçÏ+ulBù/¦p¨,ÿ ºžn¿ =  G›Í(ÐDJJ ¥ö³²²BëÖ­4F~~>¢¢¢¤öÓÓÓƒOƒÆPµË—/ÃÚÚZi›BÈ¿ŠÄ—Õ¿›Ôrh€èZT1†{T@¨4@ËéÅ‹èܹ3ÊÊÊàáá H .åMÈß÷õ×> :þ‚,p®U+ü}÷nƒÆ¸zñ"ŽLžŒAAý¶áNz:´´Çåãïï¯ÔûŸ¢.´!‘…2v"Lx™~Uu‘šZî Ðmìjñ¨¤¯^ÿ>—5€¦¥£õ|9XZZbòäɘ4i|}}qãÆ tíÚ'Nœë{ýúu9r'Ožäü«oý¼µkñÂÚ“KKký÷J_A[·6øù 9''L¨cŒæ••˜0mšÂƒç¨¨(lذ¡ÖIIIœþX¾|9F…E‹áܹs OcÂã|/麢¶÷³M¸á»0eµÅ=ŽCYYÙ;1—ÆÞ&܉P‘c<É}<PÂ]Vús{ñ'䘘ÚÚª·µÕ, ,¾|¹ÎÇ c’eË–áÈ‘#ˆ©[ãXB|GM:S§N}‚–-[bÞ¼y1b§¯±±±\cYYYÁÂ׆¤bQ-["¨W¯Áãñ0néRüüùç˜üúcŸ·íur¡åË‘‘¸YXˆÎÆÆ¸Ù¡ƒL­OLò_G+Ð –šš …Ÿ»¶UèbQ ”{ €q~0ù­cÊZ}–$##ƒ ‚‰‰ NŸ>-z=·¾NQY°`š4iÐÔÔDPP~ùålÙ²…HŽ5 Æ ƒ³³3bccñÛo¿áÒ¥KèÝ»7bbbàä䤒çC!ï#á ´J 7+ÐT@(Ÿ1DS¡RQ´bbb8ßß¹sGE·nÝÄhEíê³pÓ&lyëþŒ»ÌRÈÙ«ø ¿FÛ%--t3úúú I²’’ 28sæ lmmEÇ=z]]]ôèуó///ØÙÙ‰í´uüøqL˜0ݺuÃôéÓñï¿ÿâàÁƒ(,,ĪU«”þ\Q&Ú‰ÈB; oa§ÒB^÷@KôàÕ+”½Þ©±!4íD(­@Ë¡gÏž077‡‹‹ ž>}ŠgÏžÁÙÙ‡뫨ß^….忆 r~àõ*ôåËøyß>L.-ìutTÉêsUUƇèèhœ>}^^^œãÏŸ?‡»»»Ä@ÞÛÛÿý7 ëÌ9ÿä“O0sæLDFF*|þ„¨íDHd¡èóËò‘ý*€W )WW¢š„@ÓN„ÒQ-‡‹/âêÕ«ÈÌÌD=вeKH¼õ‹"/Ä…›6aYx8~HIÁnssÌÚ¿£xP’€aÃpá&ÄÆ"BK ÆŽUÉêó¢E‹pêÔ)ìÛ· ;Þ¬Y3ïÙ³!!!X²d ¦OŸ.±§§'bbbpéÒ% }àåå…]»vaþüù°³³ƒ‡‡âããQ\\Œ•+WbРA¢ÇܸqÛ·o˜››ãåË—¨ªªB“&M°yófN_B#Ú‰ÈBÑ; W MÑD«‰ÂÎ+ÐuŠyõ •¯ïPÜКv"”ŽhQTaMÊ ž…š5k¦”ó.\¸yyy¢ïÑ»wïZû×|ÓÿüóÏÑ»wo\¸pOž<Áرcáçç‡oÝçrÕªUðóóCtt4222```ggg 2DiÏ‹U¢"B" E W Ušÿ P-…¼„ÖÐ*B"W¿~ý8ßwïÞÝ»w¯÷ã[µj…V­ZÕÙÇÌÌ C‡ÅСC4GBÞu8Y(2xf`ˆÏ‹ Æ;p@Kp»°`¬¥·¦_RÌ"å@B!D&))(åWßæTå÷€Ð@¦Ž4Â膆 òA塚B!2¦o´ ỤD ÀÃ’T@¨l@«íêCQ4Ú‰ÈB‘;Ö¼…Úr )€U\ Áë†æ?³ÔÐ*¢Œ"BBÈ[DD„¨iêºE¨¬„+ÐM´šÀÑDÅù²´w­n×( ”gšb騈PE(!Ÿ¢hTDHd¡È"Bá.„®æ®à©:Ó–V k%Ì6ÓÖ†‹ùá³HG+ЄB‘‰0…Cå„ÐuÐÞ††jžÉûhB!„Ô[Ie R R¨¡€°¤¤ú@ô[Š<)­¾3 *Ð*B ù„E£"B" E>Í{ †êB5µn¢B;prD¡JX@hl,×¹(f‘Žh¡„|Bˆ¢Q!‘…¢Š9·°S×= Z~‹¢ ŠYꃊU„ò !ŠFE„DŠ*"´ á»D˜ÿl¥£ƒfººr‹béhš¨Å™3gðûï¿×«ozz:~üñG$%%)yV„B¤ZXÃD×DµƒS]+aMùϪA+ÐD-Ö®]‹ÌÌLŒ5Jjßû÷ïãÓO?ÅÑ£Gáì쬂ÙÕ-00wîÜ‘xì§Ÿ~‚———ŠgD!ª#LáPyþ3@t-^òùHx]@(Ï*¤þ(€Ve$äçääÀBÉEùùù044„–]*Bñññxòä ú÷ï/vLWÎÍ‘…°€PÙïäýðøñcxxxÈ}Ñ-ìT¾¼  ut9 åÞ'w˜ÿ TÇ,ôû¬n©ˆ¢òóóóÑÂË agÎÀ»C…ž»&ŸÁƒñé˜1š=[ic4FVVV8yò¤iÑå IDATº§Aþã„„” MêcÆ rçAgg¢°¼Ýú]¢ÈB :f¡<èºQ­"оWlÞŒ¢ ðņ ¸òÛo =·Ð_¡¡ÈèÒNŸÆÓ§CGGGácðù|ìß¿—/_†@ €¯¯/>ÿüshhÔ/=ÿÌ™38}ú4ž>}  0býž>}Š .àâÅ‹(((€ z÷îÀÀ@N¿’’ìÛ·÷îÝCjj*lllЧOŒ?MäØÕ‰e À™ÈBE„j- hïZóŸíuua£€ßÕNGGƒuê„GFF¸©”qVíÞB¤øù!dÿ~…ŸŸÏçcìØ±Ø¼y3š4i‚äädÌž=£G{}/˺̚5 ƒFxx8¬­­qýúuŒ5 Ÿ|ò çñÇŽƒ››¾ù怓“RRRðÙgŸqÎOOO,^¼ÉÉÉpttDVVfÍš…¸¸8N_>Ÿ‹/âØ±c¸sçJ„7ö'„÷˜0}Ps4ÐT@¨z´Ý­Ø¼©C†r†Ç”uë°ýàA…ŽñÿþýOÝÝmmTtîŒï7lPø*tzz:rss+ZÝ]°`BBBpìØ1Œ3¦Ödž††bÏž=7nŽ9MMM0Æ0}út8pÄØ±cÛ¶mƒµµ5âââ``` :ÇóçÏE_WUUaêÔ©ÈÍÍÅåË—Ñ­[7ѱììlèéé‰Í½oß¾¢ïÍḬ̀aÃL›6M¾…BÞaÂB- -´0m¡ú P-æEe%RÊÊP¡*Q­"Š*"ÌÏÏÇé¨(T ƒ7##ÄÀ÷øqÀÕU!cví¾üRômêëUhEçBO™2…“1g΄„„`÷îÝuÐ;wîÌž=ššš‡/¾øÀŽ;D´–– Å‚;;;Ñ×ÿþû/"##Ì žê|çšúöí‹iÓ¦¡uëÖÈÏÏÇñãÇñûï¿cúôéÐ××ÇøñãðJ";*"$²PD¡pÚÅÔZj!(€s»°Pôµ¢V ©ˆP:JáPEÖ\}=¨ç=•ëåöm }{@[[ÔTѹ3~øçTTT(n~~~œïÑ¢E ÃÄΛx<¸µm+÷8Ó6mBü‚bíÏ|}º mbbÂYjݺ5Μ9Æx<žÄǾxñnnn¢Õç·áÂTTT@GGpwwGHHöïßÝ»wÃÝÝ‹/ÆÔ©S¼Iç°là›²³³3z÷îÓ§O#;;[lÕše "B" y‹+«*‘”_½¡•Z   kÐÍõô`^cáKTD(ÐÈŠÍ›‘ööêók#Fà§]»põøq¹Æø+4Ù|H¸ïsE§NøaÓ&…åBàÑ£GhÕª•¨1†ëׯÃÁÁ¡Öàš5k†èèh¼zõŠ“×ÌçóqóæMØÚÚræØ£GôèÑ8uêBBB0}útôë×ŽŽŽhÑ¢:—/55•3Yó¤Ë^ç¢BÈû$ñe"øU|´‰Ê»D@Sþ³jQ G#‘ŸŸ¿##QU[þš±1Ê}GŽà={P8`@­ÇŸ)øŽaaaœï£¢¢““ƒ6mÚÔù8///TTTàòåËœö[·n¡°°íÛ·—ø8Lœ8[·nEUU•h;ñN:Ž7ð‚‚\½zFFFhÖ¬YƒÎA!ï2aú æ{@åýž——#ãuj%ÝCµ(€Vy‹WmÝŠT "¢Ö9˜¹jUƒÇ8}î——7nÔ:FEY¶8@ ×óMMMìܹIIÕ aÅŠ€¥K—ÖùØåË—CSS«W¯åjåååaùò倕+Wrú>}úTô}II Ž= ¢`·S§NÀ¡C‡°}ûvÑócŒáìÙ³ÈÈÈܹs_ý5âââPYY‰œœìß¿}úôAVV.\XçÊ9!Š”““#*$$Dššµ! QóvjÝ… è×½Š2vO~ßP ‡ŠÈ›?sÜ8øÕãÍÏvԨѩ];9Sj?]oo‰¹Ç²rpp@`` Z´hæÍ›#==|>›6m»ÆÛ>øà8p³gφ½½½èÞΠÜÇÇGÔwÆ X¿~=LLL`ee…ÄÄDhiiaÊ”)œMW¾ûî;TTT`þüùXºt)‘žžŽW¯^!::¶¶¶ÈÉÉÁŠ+°bÅ hhh€ÇãA @KK sæÌý@ˆ*ÐN„DòîD(\6Ñ5µµ‚f%  ÅÓ7x¼@ÓN„ÒQ­"ò^ˆrß~Hkkk•ý"Þ´iÊÊÊпøùùáòåË`Œ¡oß¾bé:tÀÿgï¾Ã£*Óÿ¿'½iô„š!@è  "F$Š_„ßʪ«¬»‹~×uíŠ"ûuE”^¥H± ¥) ´„–PBHBH/3¿?&'$„0'dfÎLÎýº®\ÎÎLæÜ¸xøð侟gõêÕemЏ¸8ú÷ïÏÖ­[ËN"ìÓ§OY?³âÊ•+lß¾'N““CÏž=‰‰‰ÁÇǧÂû‚‚‚øöÛoÙ±c$%%…F1`ÀÚ¶m ÀàÁƒùý÷ßÙ·o.\Àh4ÒºukzöìI‹-lðoJˆªIpÕQÓ!BeZ“þg¸ ]]¡~}mjp0J€níãCÝ[Ì.Ý) Ï–I€šˆ‰‰){ܹsg:wî\å{CBBª Íš5c„ ·½V`` =ôPµj+__y...tèÐÁb¶BÔ6Ê1Þš´oÀ]8AZå9PKÒ-„BˆÛÊÌÏ$5'Ðh€ä•›œÍÏ'­¨­ Ðv" ùBk“!BQ5"Ô|€$@ßÄV„ ™E Ðv"§ú!¬MN"ÕQ““Ëoa§y´hàFû†‹Á@g??«~¶dˤÚN¤!_am2D(ª£F;p”®@0H€vJ€ŽòñÁ× ;c•'™Å2YB!Äm)„Më6ÅÛÍÛþžn~,ð› jJV „—— .dÛ¶mZ—"œÌ•+WÊŽB[PZ84 ¼zÕ¢A4p:/Ìbó±ê µ!ÚN,5ä¿õÖ[ÿú•œœLÆ U¿ß××—#Fذ"ᨔ 9ÖX¨Gç MFN¦›OsÕ|€$@cÛB0gOOO«nm"ÚN,5äׯ_Ÿ¸¸8û#Z\\Ó§O׺ áä$BQwzá¹kçÈ/Î`€$@s£ÿÙÍ` “•AN"TC´ÈoD¡VMO ú!ÁYTÇÞ[*la§õÐò—²ÝÞ×/ë³If±L†…BQ%e€¤…ÃM&È¡æ$@ !„¢JÊ¡·›7Íêj´2©d0è~:>7—ì’@´–$@Û‰œê#ÔªÉiaB_ä$BQwzoQZ8ZÕo…ƒ5KROY®WÜôÝ}ú«A2‹ íDNõjÕä´0¡/r¡¨Ž;½·”ma§UûÈ!*å(ÚÓÅ…6 É,jèû¯qv$ ùB-"§N¢¨¨ˆ¨¨¨Û¾O†EuÜɽ%·(— Y At9J€îàë‹»Á6?Ìb™¬@ !ìæÕW_%..Ž;wZåó233IJJ¢¤´Й\¼x‘”””[¾öôÓO3tèP;W$De'®žÀ„ hGPl2q(;þg­É ´Â.Ž;Æ;ï¼@zz:½{÷®ñg~üñǼñÆ$%%Vãϳ§þýûÈ/¿üRéµ 8å_ DíS~Íö€ Ð¥ŽæäWz"£hmÉ ´HC¾P«¶Î;ƒÁÀ!Cøî»ïª\}°téRU«ô2D(ªãNî-±4ÜØ…CçºüaW›]G2‹e²m'Ò/ÔºÓÓÂYqq1 , W¯^̘1ƒ~ø ðòË/WzßÊ•+iÓ¦ ;w®ðÚÉ“'ùí·ß2dìÚµ‹#Gްnݺ²ã¬;vìX¡wø?þ`ûöíœ={–ˆˆHóæÍ+|v||<‡bèСdffòã?röìY† Btt4...°cǶoßNhh(cÇŽ­t„vaa!;wîä×_%%%…Fqï½÷Söž’’V¬XÁõë×1™L,]º´ìµ|¶lÙBnn.<ð@¥—{÷îeß¾}œ9s†ÔÔT¢¢¢øë_ÿŠ‹ SµËÜ[”ÂßêyÕ³AU*dfBQ‘ù±h|\]iëãc³ëÈI„*˜„ÍM˜0Á4a­ËB3«W¯6¦/¾øÂd2™L­[·6EEEUz_VV– 0M:µÒkŸ|ò‰ 0ýöÛo&“ÉdŠ‹‹3ùúúšSpp°)44ÔjúôÓO˾gÆŒ&WWW“)22Òäââbòõõ5-X° Âg¿óÎ;&Àôõ×_›êÖ­kjܸ±ÉÝÝݘ^zé%S~~¾©OŸ>&???SPP 0………™’““+|Î}÷ÝgL 40EFFš`7n\Ù{òòòL¡¡¡&WWW“»»{YÝ¡¡¡¦ .˜L&“©OŸ>¦ððð Ÿ““czòÉ'M€ÉÛÛÛt×]w™BCCM€iùòåÕù¿Ã¹ü=ÛdúŸ,ó׋׵®Fwºü·‹‰×1õžÛ[»"Nœ0™Àüµp¡vu8€.¿þjbëVSô6½ŽäËdÉBassçÎÅËË‹1cÆ0~üxŽ?Ξ={îø3¿úê+^zé%öïßOJJ )))L™2€õë×óÆopÿý÷“––ÆñãÇIJJ¢uëÖ<õÔSœ>}ºÒg¾üòˬ_¿ž .pæÌÌ|@ß¾}éÞ½;™™™\¼x‘>úˆ³gÏòÑGUøþ‰'’””Drr2Ççüùó<óÌ3,\¸°l¥ÙËË‹””Z¶lI—.]ÊêNII¡qãÆUþzÿö·¿ñÕW_ñÚk¯‘••ÅüAJJ ¤C‡wüïQˆÛQz b€t½]h4òGN ýÏŽ@´¦RRRøî»ï9r$uëÖÌÚ`0ðÕW_Ùìºÿú׿ðóó㫯¾ÂÛÛ€¦M›òùçŸSTTÄ{ï½Wé{^zé%zõê@“&M˜0a&“‰ëׯóïÿWWW<<<˜2e >>>ìØ±£Â÷?üðÆ›4iÂ[o½…··7«V­ºã_Ëùóçùä“O4ho¾ù&nå’èÔ©†Qk¥d§U8È!è:@ÿ‘“C¡ : é¶iÈjÅÇÇ©uV3þ|Š‹‹yâ‰'Êž '&&†¥K—òñÇ—\k:zô(ÑÑÑ•ú”»uëFHH¿ÿþ{¥ï¹ygîÝ»гgO åö[ussãž{îáÌ™3•>cóæÍlܸ‘óçÏ“•ežžžœ:uêŽ-GŽ¡¤¤„ØØØ Ï+„7ÿ…¸•êÞ[f€P4pÓ¡tAAžžž6½†³“h;‘!B¡Vm;‰pîܹxxxžžÎÒ¥K˾š6mJVVß|óÕ¯™••Åõë×iذá-_oÔ¨gÏž­ô|½z‡¤üJOùºùyåµ"e°©Ô¸qã8p ‹/&--æÍ›‰OY˜¾J­M›6­ð¼œD(ª£º÷e€4ná(¿ÓŒŽÿ²¨è:®®´±Á¢Cy’Y,“h;‘iV¡VmÚãçŸ&!Áü‡ð¸qãnùž¹sç–½¦ì$QXXXé}9¥½jÔ©S//¯*·xKKK£Aƒª?O;v°hÑ"~øaV¬XQáµ7b,ýÑëPj½ù59‰PTGµwà(]vsq£E@ T¤’²íç^^ÚÕ¡1%@w®S@¨Ìb™¬@ !lfîܹüðÃ$&&VúŠeÛ¶m$&&àëë‹¿¿?{÷î­ôY‡ªôœÒú‘™™YáyƒÁ@Ë–-Ù¿?ùùù^;}ú4/^´z›Ìñãǘ4iR…ç/^¼xËVooïJuW¥mÛ¶lÚ´©†U ¡ž²ݼ^sÜ]ܵ+DQ!Ïhä¨ : ÐB›ÈÉÉaùòå´mÛ–Áƒ^éëÉ'ŸÄd2UXëÔ©üñ({nýúõ•Vu²½ü“ÉTáµ^xÔÔTþþ÷¿—½–ŸŸÏ /¼Pöº5);hlܸ±ì¹¼¼<&Ož|ËSÃÂÂHHH¸e+ÉÍÚ´iã>Ê¢E‹X¶lY…×222¸téR «¢2‡Ø$@‡²³).½ÙºÿY¨#ÚNdˆP¨U[N"\¾|9ÙÙÙŒ?¾Ê÷ 8æÍ›Wr_ýu\]]¹çž{ˆŽŽ¦K—.Lš4‰çž{®Ò÷>œfÍšñ—¿ü…àà`"##™3gO=õ±±±¼ÿþû´k׎‡zˆ6mÚ°aÃf̘A·nݬúë2d;väƒ> mÛ¶ÄÆÆÉd¢k×®•ÞÿÜsÏa0/ë•NNN®òógΜI÷îÝyôÑGéС<ò={ö¤Q£FüüóÏVýµˆÚ©:÷–"c‰™æŸ i:@ ©8@hhÉ,–I´HC¾P«¶œDèççÇŒ3˜0aB•ïquueÖ¬Y=z”+W®B¿~ýصk_~ù%)))tëÖñãÇ“’’Bppp…Á@___Ž?ÎúõëIJJ"77·ìB–.]ʘ1cزe gÏže̘1<øàƒôéÓ§B={ödÆŒßô´¯¯/3fÌ(ÛÚ®¼Çœþýû—ýowwwvîÜÉ'Ÿ|Âþýûñóóã¯ý+'NdáÂ…\/÷ Àý÷ßOBBÛ¶m#99™ââb|JO‹‹‹ãÚµkÞÂŽ;˜7oûöíãìÙ³¸»»ÇàÁƒo÷…@õî-§ÓOSl,dÚ(:ÀÍ–6 9‰P ƒéæŸ{ «‹‹‹j×p˜BØÅ?r ¥tÓßïûi[N¬MXËÈ¥æ!ÕmqÛèÖÇÂwØäåÁË/ûïjW‡†Úíßϱœ°©cG›_Or‹e²m%sæÌ!--§žzŠ­ËB!îX…-ì´láÈÉ1‡gÐí tvI ñ¹¹€ô?; ÐVðÍ7ßðì³Ïb4¹ï¾û$@ !„pjÊ¡¿§? ü¬»åcµÈ!*¸~ci³€ìÀá8dˆ°†ÒÓÓ™2e S§N½íû¤!_¨U[†…í¥¥¥U¹×µ7«Î½EÙÚaA·ÚÞ„ ™E Ð5ô§?ý‰¡C‡Ò·oßÛ¾O†…Zµí$Ba;r¡¨ŽêÜ[”‡ Ýè`wwšÙé É,–I G ¬_¿žM›6qüøq¶oß~Û÷Ê4«PK†6„Zr¡¨µ÷–Œü ®äšƒk›À66¬H…ò?aÑy€¶gû†dËdú]»vçž{Ž™3g`ñý`þüù¬Y³¦Â×Í?~•çä9yNž“çnHˆO¨tš¤#ÕWŸ+ Œ‡F.´­¯tz æâR½ï­Ïesªtˆ²Îž=6»®’I¦OŸÎüùó9wîâö$@ß¡_|‘N:«u)B!„Õ(ýÏ­[iX 7Z8ÜÝÁ×WÛZ4ðÛõë({ ·²ÃþÏB=ÙúüñÇtìØ‘wÞy§ìÇ¿þú+|ðo¿ý6Ý»w¯pPC\\,Y²D«’…‰'22Rë2ìjçÎ|ðÁ¼þúëtêÔI³:²³³7n#FŒàÉ'Ÿ¬ñç%%%ñ /0qâD†n… +RV‚‚‚¬þÙCö¶µ÷–×¶¼Æ[;߀ì¿fããîc‡êª0q"Ì MšÀùóÚÕ¡‘wÎãÕ3g¸MC»\wìØ±xzzJKámÈ ôÈÍÍ%,,ŒÙ³góꫯòꫯ2þ|À|Üîܹs+}4ä µô8DxáÂÖ®]Kjjª¦u²víZŽ9b•ÏËÊÊbíÚµœ:uÊ*Ÿw3"Õ¡öÞ¢´p4ño¢mxÝŸB¨ô?7òô´[xÉ,jÈáèÞ½;IIIž[³f £FbãÆ·\A“†|¡–ü¿öhÚ´)_}õݺu³ÉçË¡¨µ÷–²-ì´Þt ÷—h{ "™Å2YBØMQQ999ªÞ›[zò–-Xû³322())©ô|@@qqq´mÛ¶Æ×PûïMˆš0šŒœJ7ÿÄDó= A×:­¨ˆ³¥´r€Šã‘m%íÚµãßÿþ75²üf!tÄd2ñÙgŸÑ®];|}}ñóó#44”_|ñ–ï÷ÝwiÕª¾¾¾4jÔˆÙ³gWx½¤¤„ÈÈHþþ÷¿WúÞ•+WÉÊžûì³ÏˆŒŒäܹs¼ð 4nÜ___ZµjÅÚµkUýNŸ>Mtt4ƒ *ûÑæÙ³gyøá‡ñöö¦~ýúxxxТE Þyç²ïKHH 22’yóæ•=7`À"##où5jÔ¨ ×=sæ #GŽ$((???ÂÂÂxíµ×(,,TU·ÕuöÚYò‹Í¡MV µ¥Å*B=iá°’Ö­[óꫯVùºœê#ÔªmC„?þ8K–,áþûïgÚ´iÔ­[—cÇŽ±eË–Jï}óÍ7ÉÊÊâOú×®]cöìÙ<ÿü󄇇3tèPÀÈHII©ôý™™™$$$Wºí˜í7nîîî̘1ƒ“'OòÅ_ðÐCqìØ1""ª {÷îeøðá4hЀåË—ŠÑhäþûïçܹsL™2…{î¹£ÑÈ‘#GHLL,ûÞ‚‚HOO¯ðï#++«Â5ø¿ÿû?6lXöÜîÝ»4h¡¡¡üãÿ €¯¿þš·Þz‹ßÿõë×ëcˆPXš{‹r„78ÀÐ…… ü·"Ú®×.((ÀÓÓÓ®×t6 íDò…Zo¿ýv­éƒþöÛoY²d “&Mª´’}:ãÇgöìÙ¬]»–iÓ¦ñÚk¯ñÖ[oñ§?ýÉb­3fÌ`ñâżùæ›<úè£^Svܸ÷Þ{i×®]¥ï¥U+Oˆµ’Cnaº Ð2@èø$@Û‰  µjÓaëÖ­Y¿~=ùùùxyyYå3ÝÜÜðòòâÂ… •^»xñ¢U®Ñ¡C>úè#HLL ›7o¾åÿ'Mš4á­·Þ*ì‹åÍ7ßdÒ¤I·ý‹ÃÂ… ùßÿý_ž|òI^{íµJ¯GEEæ ÿÊ+¯Tù92D(ªÃÒ½%§(‡‹Yæÿ†´ \øÙg\(=u€ƒo<ž9Jï!уsß}v®Î¾”mîÑ @Ë¡eÒm'2D(ÔªM':”¢¢">ýôS«~nXX»ví¢¸¸¸ì¹ââbV¬Xaµk´oßžíÛ·c0èÓ§‡¾íû;vìȰaÃHKK+kÁ¸•;w2qâDú÷ïÏçŸ^åµ›6mÊþó®—[‰º™œD(ªÃÒ½åäÕ“˜0ÿGbBXP IDAT‹ŸV¯&úé|mÝÊH0ÍžÍÈ?ÄkÖ,N?n÷ÚìM Э¼½©w‹ŸžÙšdËdÚN¤!_¨U›†'NœÈW_}ÅôéÓIIIaÔ¨QeÛØýð÷<ö^Ç{Œ3f0räH¦L™‚Ñhä½÷Þ«°}5DDD°cÇ @¿~ýøñÇéܹ3?üð3gÎä‰'ž ** 777vïÞÍ_|Axxx•«|ÙÙÙŒ5 wwwþçþ‡Í›7WxÝßߟèèh¼¼¼øüóÏ6lÝ»wçÿøíÚµ#77—“'O²dÉ&NœXißh!nÇâWËíÀ¡A ÇôÏ>cnß¾¼s‹-*ÿgÑ”)v¬JûK·ïÓª}C2‹e …6c0Ø´i/¾ø"Ÿ~ú)~ø!... >üŽ?÷Å_äÈ‘#¬X±‚ 6àííÍÔ©Syì±Çxæ™g¬U>-Z´( Ñýû÷çûï¿'44”sçÎ1vìØ ïŽŽæ‹/¾ÀÕÕõ–Ÿ•ÍÕ«W*}/@§N8Xúcëû￟]»v1yòä =Ò...DGGß²/[ˆšÐz »ˆˆ2;uâò÷ßz‹××yz2ø¹çp·ó–növ.?Ÿ+EE€ô?;2ƒévS7Â*âââ€Úµ²(DuqâÄ £~ýú5þÌË—/séÒ%ÚµkW­aEkIOOçüùóдiÓ ¡XÓµk×8qâ¾¾¾4iÒ¤ÒÖvµÚ?r Åh~ìo€÷ý´­§{|Õã,>¼˜`Ÿ`R_NÕ¤†„„¾ìÛ—wo± =¦M9Rëôª+W}ô(Û;u"¦;ûX‹äËdÚNdˆP¨U›†Ësww¿åŽ5Jhè­Öªì£~ýúVù‹€%uëÖ¥k×®•ž—!BQ–î-Ê)„ZîÀAfÇŽ\NI©° ½ÎÓ“!:X}†ýÏ.5Z–!BËdˆÐN¤!_¨U›†…mÉ¡¨K÷¥…Cë_œ9“÷CB*<7¯Y3&è ÷n éãƒ_í`¶&™Å2Y¶iÈjÉÌ„Zr ¡¨ŽÛÝ[’³“¹^hnZïÁµ† IIM¥ð­‡÷Mš¤‹Õg€ßJ´–ýÏ’Y,“h!„BçÊj±ôÍ^ŒˆàýÒÇóÂÂt³ú|:/ŒÒí9e€Ð±I€B!t®Âv·pDüú+YÀÿ¹¸p¿ŽVŸåBç!ÚNdˆP¨¯u º3{öl~øaŽ–N¾;‹´´´²AB!,¹Ý½EYvsq£e}·HLJ‚3gxøÔß_7«Ïp#@» tòÓnÇÉ,–I€¶iÈjÉ¡ýíÙ³‡o¾ùÆéþò"C„¢:nwoQvàh^¯9î.¯ö–0ýùgݬ>ÃÂv¾¾x»hÑ$³X&C„v" ùB-"´¿&Mšpþüy+©"Õq»{‹Ò¡õ!P iØÚ¶Õ¶;2`€$³¨!+ÐBÝkÚ´)à|Zk(,)$1#pŒB¶l1ÿ³më°³„Ü\®—”Úha™h!„î9ë ´Öp:ã4%&spÓ|€ðÈPÚ ж;+?@ØU´Ã“m'Ò/Ôr¶>ÜÚ@Y¾pá‚Æ•T Šê¨êÞR~ ;Í[8”ö Ðm€öpqá.__Mk‘Ìb™h;‘†|¡– ÚŸ³®@Ë¡¨Žªî-Ê!8À ôO?™ÿÙªè¬W ìà닇†„ ™E "´iÈjÉ¡ýáååErr2%%%¸jt|nuÉ¡¨Žªî-Ê¡¿§? üر¢›ÃŽæÇ:[}.1™8” 8Fÿ³dËdZ!0¯B—””œœ¬u)Bؕҡùáþý•e~¬³},7—\ t* …çm㢦ʶ°Óº}Cé6t·‡ : Ðv" ùB-"ÔFxx8§OŸÖ¶j!BQ·º·¤ç¥“–kþ=ä0„;B` ¶µØÙþÒ•woÚj<@’YÔ ‘ŸŸoÓkHC¾PK†µÑ±cG<¨q%êÉ¡¨Ž[Ý[ÊjÚ‘—»w›ë¬}n¬@wòóÃÍ`иÉ,jèrˆ0??Ÿ¹sç²yófŽ;Æ©S§04oÞœ¨¨(zôèÁ¤I“°Ú5¥!_¨%C„Ú¸ûî»ç Ð2D(ªãV÷¥}4náØµ ”UOè"“‰?rrÇé–Ìb™®V sssùè£hÑ¢o¾ù&EEE<ðÀÌž=›/¿ü’GyOOO¾üòKÂÃÃyíµ×¸zõªÖe !ì S§N:tHãJ„°e€Ð€AÛh¥}ÃÝbb´«C‡³³)0èêï¯q5B-]­@¿üòËúˆÑ£GãæVõ/ÿÇdæÌ™ÄÆÆò“²/¥¢Öª[·.Í›7'11‘¤¤¤²žh!j3eº‰|Ü}´+D Ð÷Þ ÐlOåeZX¦«ýÏþ“úõë«zïàÁƒ|8ëÖ­Ó¸Ëä$BQ7ß[”h/7/šÕÕèGö[¶Üx¬Ã­ Öss£¥ƒ ‚d5t û÷ïÏÖ­[IMMeΜ9„‡‡Ó«W/®^½Š———U¯' ùB-"Ô–3h"Õqó½EYnU¿.â€Ò¾¥{±ë‰²}O:8Æø ™dËtµ]y111xzzÒ¸qcŠ‹‹™={vY?ôO?ýTv¬¯µHC¾PK†µÕ£G9tèçÏŸ§iÓ¦Z—T% Õqó½E9ÆÛ!ûõÙÂ^rJJ8ž› 8^ÿ³dËt»íççÇ¡C‡Ø´i'Nœ`Ò¤Ie¯µhÑ‚¿ýíoV'„Њ««+Æ `ýúõW#„mäåp!ë áaB\0× ÇöƒÙÙK¥ÿÙùè6@+úöíKëÖ­+<÷ä“OÒ§O*BhÍ™Ú8„¸Êê3h¸´²ú º Ðûe€Ð©é¶…C‘’’Â?þÈ¥K—*<߸qcÆoµëHC¾PKN"ÔÞ!Cððð`Ë–-deeáïï¯uI·$'Šê(o©°…V-J€nÚnZÈÒ¥ÿ9ØÝ0+Ï]Õ”œDh™nW 322ˆŽŽ¦Q£F<óÌ3|üñǾ–.]jÕëIC¾PK†µW§N† BAA_~ù¥ÖåTI†Eu”¿·TØÂN‹£Ñ|!èrõ*:É,–évzöìÙ:tˆíÛ·sï½÷âááaÓëIC¾PK†ôiÓX·nü1S§NÅÍÍñn—2D(ª£ü½Eiáö &À+ÀþÅ<ééæÇ: Ð׊‹9é „ ™E Ý®@_»v¡C‡Ò»wo›‡g!„óéׯ;wæÜ¹s¬X±Bër„°*¥…C³B÷?ÈÎF9ëTúŸ“nô€عs'………Z—"„pP/½ôï¿ÿ¾Æ•a]Ê ´æ„QQа¡65hhVVÙc ÐÎI·zÈ!Lž<™Q£F±páBâãã+|={֪ד!B¡–œDè8ÆŒCXX`ëÖ­Z—S‰œD(ª#>>žK—.1gñ®ï¼‰æfÿB AéÝ×áê3ÜènèáA#Ö“Ìb™n´ÑhÄ××—ü‘ñãÇUáK9ÖÛZ¤!_¨%C„ŽÃÍÍ^xpÌÿ_dˆP¨UPPÀƒ=H·qݘ´~„YðÙß>ã?_þǾÅìÞ ¥ý¿zÐ]t‡É,–9ÞTŒ¬[·Ž—_~™?ÿùÏôéÓ‡ÐÐÐ ¯ûùùYõzÒ/Ô’!BÇòôÓOóÖ[oñã?²lÙ2«ÿåº&dˆP¨5â‰$õL¢¨IÑ'C!µc*¯|û ™Y™LŸ6Ý>Å(í®®Ð·¯}®é@®‘˜Ÿ8nû†dËt ·oßN¿~ýøàƒ´.EáÀüüü˜5k±±±L™2…þýû¬uYB¨6oÉ<ö°§bx.çzçëÌZ=‹ÇzŒ°0;´t(ºsg¨WÏö×s0¿É*µ‚n[8ÚµkGFF†Öe!œÀ#<ÂèÑ£IKKãùçŸ×º!ªå¿‹þ˵¨k·}Ï¥¨K|µä+Ûsý:ìÛg~¬Óö 9°vÐm€Ž-[Y²G–†|¡– :¦Ù³gÈŠ+øæ›o´.!B¡NF~€ÛýV …_~ûÅöÅìØÅÅæÇ: ÐJÿs˜—ÁîîWsk’Y,Óm€^µjçÎcêÔ©„„„YáëÑGµêõ¤!_¨åˆÃjBBBøôÓO˜}̇¨è3 ‚d5÷¯?6ЫW/öîÝË?üÀ¿þõ/fÏžÑhÀ`0`*ýÑÒ]wÝÅ|@ll,®Vú\ò…Z2Dè<‚ƒƒÙ±cO?ý4kÖ¬áÁdòäÉ|ðÁxyyÙüú2D(Ô:˜r¬¡Y͵ÿa@‰‰æ/ÐmûÆÙü|Ҋ̇Ù8z€–Ìb™®´bÈ! 2„üü|8~ü8EEEDEEiõc¼…µW`` «W¯æóÏ?gÚ´iÌž=›íÛ·³xñb:tè uyB°ìÈ2\ .<Üöaû ¬>ƒn´ R»è2@+¼¼¼èر#;vÔº!„“{öÙg‰‰‰aìØ±üþûïtîÜ™G}”¿ýíoDFFj]žÐ¹åG—ЫY/Õidÿ” íÛÛÿú@ißp1è,ÚééªÚÚöíÛÇ¿þõ/žzê)žyæV¯^Mvvö-ß+ ùB-"t^QQQìÝ»—éÓ§ãååÅ¢E‹h×®=öÇ·úõdˆP¨±ïâ>3! bÛÅÚ¿“ɼ@ÿþö¿¾ƒPt„·7u¼\2‹e kà7Þ`ÕªU‘™™ÉÓO?MPP[”E9Ò/Ô’!BçæééÉ[o½ERRÓ§OÇÏÏ%K–о}{bccùþûï)*탬)"j,;jnß0ì20ºíhûpäˆyhÐmû† øÍIA2‹“29'ª­  OOϲÿŸŸOxx8­[·fçÎeÏÇÅÅ2&„edd0sæL>ùä222ó#FŒàá‡fРA¶ß×™ý#RÌÃÞøà}™Q©&Â> ã|Öyú7ïÏæ'6[þ&kûøc˜6Íü81ÂÃí_ƒÆNæåÑfï^f¶jÅÿkÒDãŠnOr‹e²]åÃ3˜{ª»uëFqq±F !M@@¯¿þ:IIIüç?ÿ¡oß¾\»v¯¿þšx€&L˜À¢E‹8sæŒÖåŠZf÷ùÝœÏ:hÔ¾7úŸ[´Ðex9°6Òõ¡5¥¦¦òý÷ßóý÷ßóùçŸk]ŽÂÁøûûóÜsÏñÜsÏ‘’’ÂÊ•+Y¶l?ÿü3óçÏgþüù„„„УGºwïN=èÚµ+>>>W/œ•Ò¾áæâ¦MûFq1lßn~¬Óö ¸ ] î–]+è~zåÊ•ü¿ÿ÷ÿ=úÆeíÚµª~âãã1 „††ÇüùóyòÉ'+½oß¾}ÌŸ?Ÿ5kÖTøºyHž“çâãã¦yÎ6ÏíÙ³‡G}”;wrþüyæÎË AƒˆˆˆàÊ•+¬]»–éÓ§Ó·o_üüühܸ1½zõbüøñÄÆÆ2kÖ,¶oßΡC‡HMMu¨_Ûš5kHLLä•W^aíÚµÕèóâÈÏÏw˜_›3=g4Y¸|!䀿¸röŠýkÙ¿JÃã‡ø÷¢ÅsJ€nº?9ééWŸ’I¦OŸÎüùó9}ú4âöt½=bľýö[Ú¶m˱cÇʞߺu+K—.eÉ’%?#<<œÝ»wsþüyÖ¯_ÏĉÉÈÈ`Ò¤IÞ—››kõúEíôöÛoË:Ò¸qcž|òIèÕ«®®®ìÞ½›_~ù…µk×’˜˜È¥K—¸té?ÿü3Ë—//û~WWW‚ƒƒ‰ˆˆ ""‚æÍ›Ó¼ysÎ;GTTAAAvÿ5Õ©S‡§Ÿ~š.]º`2™ˆŽŽÆßߟþ:ÞA »Îí"=ÏÖbÛÇòöÛoÛ¿§Uiß0@§û¢M&”è–ÞÞW£Îµk×´.ÁáévˆpÕªU<òÈ#ìÙ³‡6mÚP·nݲ“7nÜÈsÏ=ǹsçªý¹cÆŒaÏž=œ;wƒÁH3¾¢f®\¹Bbb"III$&&Vø:{ö,………U~¯——õë×§~ýúÜöqùçêÖ­‹‹KÍ~H¹cÇ €ÉdÂ××—€€&OžÌO<¡þøh"¼cÏo|žÙûgãîâÎå—/à`ÿ"úõƒmÛÌáù÷ßí}p,'‡vû÷0»M&5Ò`îj’Üb™nW ùå† F—.]*íÝLª²åN5uïÞ•+W’ššJhh¨5JBè\pp0ÁÁÁtëÖ­ÒkF£‘K—.U°•ÕëK—.Uëš...Ô­[·Ê€}»î]ºÊÃÿû_¦NJVVYYY¼ñƼñÆtîÜ™)S¦0bÄ»{®7%¦V[ Àà–ƒµ ÏyyðË/æÇÒÿ @Wé®5t }}}ÉÊÊ([)VlÚ´‰ÆßöûF#ÉÉÉÞ—››ËgŸ}FãÆ%< !ìÂÅÅ…&MšÐ¤Iz÷î}Ë÷\¿~ôôtÒÓÓÉÈȨðÏÛ=§|U———W…€Ý°aC’’’(...kgÛµkä©§žbÈ!Lš4‰AƒÕèß…¸a[Ò6RsÌ A±í5Ú}cçNP~:":øúj\°Ý蘘Þ|óM8@TTTÙó/^dÁ‚ôéÓç¶ß_XXHxx8111´hÑ‚3gÎðóÏ?ãêêÊÚµk+½_NõjÅÇÇËÑÏBeÈRŸs:u¨S§aaaÕúüââbUAûVÏ%''“œœ|ÛÏÏÉÉ`õêÕ¬_¿ž   ž}öYâââª]«¨hÙóîž®žŒˆhpoQúŸÝÜ &Æ~×u0J€¾Ë×¶EÙËÍç\ˆÊt  À¸qã¸çž{èÛ·/>ø 7n¤Aƒ¼óÎ;·ý~OOO¾ûî;¶mÛFRR­ZµbذaŒ?žàààJï—S}„Zš ú§¤œBh«¡S77·²ö‘êÊÎÎ&##ƒ… òæ›oV9HíZz¤±Éd"00+W®°{÷n4h €ß¡bc1«Ž¯à¾V÷áïéhpoQt·n ÓÖ…b“‰C¥m¢Î´ÿóåË—iÖ¬™Öe84Ýh€yóæ1dÈ-ZD«V­¸|ù2S¦LaÆŒܾ_Ì`00pà@¨êZòQ¨%áY¨åÈ»µøùùñÎ;ïðÑG•…goooÜÝÝÉÏϧyóæDGGÓ§OºtéBTTT‡…ÙæÄÍ\Í» Tlß°ë½%#4?ÖqûÆÑœòŒæ!Xg Ð’Y,Óu€xì±Çxì±Ç´.C!jÂÂBžzê)-Z„‹‹ Í›7§GôíÛ—.]ºÐ¾}{ÜÝݵ.³ÖRÚ7¼Ý¼Þf¸6ElÝ ¥ÁQÏZk/Ýh!„Öc2™X¼x1=zô`êÔ©têÔIZ1쨰¤Õñ«Öf~mû§´oøø@ÚÔà”íåâB; ¬Utýó²+V0~üx6lˆÁ`¨ðÕÃÊÿÁË¡P+>>^ë„“HKK«tº˜Ö qqq<ÿüóÜ{~ílÓ™MdægÛ®âîv½·(ºW/ðð°ßuÌþÒÝÉÏ·›vürd’Y,Óí ôÚµkyä‘G:t(Ó¦M#$$¤Âëw24s;2D(Ô’!B¡–­‡…óQÚ7|Ý}ÖfX…×ìvo¹xÌuܾQh4r¸t§gê"TC·zË–-Ü}÷ÝlذÁ.ד߈B- ÏB- ΢¼‚’Ö&˜·Q1o·ŠÇFÛíÞ¢¬>ƒ®ô99–ö;[ÿ³dËtÛÂI½zõ´.C!„°ŠïN~GVù€°›Û7ìJ Ðp÷ÝÚÕ¡±ò„ζ-,Óm€5jçÎã§Ÿ~Òº!„¢Æ–5·oø{ús_«û´+D ÐýúŽ·&TúŸý\]‰ôñѸamºmáhР«W¯¦sçÎ4hЀ{î¹§ÂëS©iÈjÉI„B-µ'ŠÚ/¯8u ëx0âA¼Ü¼*½Ç.÷–„s4èº}n¬@ßí燋 ‚œD¨†nÿjxæÌˆ››-[¶ÄË˫—‡•§†eˆP¨õöÛok]‚p»ví*$ú¶áÄrŠÌkUµoØåÞ"ýÏä+ ìêï¯q5Õ'™Å2Ý®@/_¾“ÉÄ•+Wðó³ý>™Ò/Ô’!B¡–  …Ò¾QÏ«ƒ[¾å{ìroQtãÆaûë9¨CÙÙ›L€sö?Kf±L·+ÐùùùÄÄÄØ%< !„¶’S”Ææ¥FEŽÂÃU£}—Fó „ ëÕg€ýYYe1@ Ët |ðA~úé'222´.E!„¸cëÖ‘WœÀ#íÑ®ƒAù3UçZé®ëæF+oo ïÎH·-^^^ 4ˆ:0bĺtéRáõàà`† VÅwWŸ  µdˆP¨%C„n´oz2°ÅÀ*ßgó{Kù]­$@pO:8×ø ™ Z¦Û½eËÖ­3O,Ï™3‡9sæTxýÞ{ïµj€–†|¡–œD(Ô’“EVAßü€‡¢ÂÍ¥ê?Öm~oQúŸ#"Ì=Ð:•]RBBžù'Îv€ŠBN"´L·zÊ”)L™2Ånד߈B- ÏB- ÎbmÂZ JÌ?áŒmûÃSlzo)(eG¯>¸~£‚d5tÛ-„B8»eGÌí!¾!ô ï«]!»wC骫Þô~9PtµOJJ ÑÑѤ¦¦rêÔ©*ßëïïOçÎíXB¡^f~&›Îl`tÔh\ ®Ú£´o¸¸˜O Ô1¥ÿ9ÈÝp¯ÊÚˆÚAWúí·ßfÞ¼y$''³råJ¦M›Vå{ï½÷^öìÙcµkË¡PK†…Z2D¨o«ãWSXRXnßß[”}÷Ý`›k8 %@;óê³ Z¦«Ž™3g’œœLHHÏ<ó ÉÉÉU~mܸѪז!B¡–œD(Ô’“õMißhè×ÞÍz[|¿Íî-ׯÃþýæÇ:oßÈ,.æti+‹3hÉ,–éj:99™¨¨(’““iРҞý~ IDAT>>>v»¶4ä µdˆP¨%C„úu5ï*›Í«¾cÚÁÅ`y=Ìf÷–íÛ¡¸ØüXçú×ë×1•>væ-™Å2]­@ !„µÁ7Ǿ¡Øh­šž7Ú7<< W/mkÑØ¯2@¨ …B'³üèršú7%ºi´¶Å(ºG°ãOv‘ xxÐXzˆk5]µp(Ο?O~~þmßãééIÆ ­vM"jÉ¡PK†õ)5'•mIÛsû†AåYw6¹·¤¦Â‘#æÇ:o߀ÚYPQÈ¡eº\îÖ­Í›7¿íרQ£¬zMiÈjÉ¡PK†õi展”˜Jˆmgy÷ …Mî-[¶@é¡!zÐWŠŠ8[º8çìí’Y,Óå ôÇLݺuoûžàà`«^Sò…Z2D(Ô’!B}ZvÔ¼ûFx½pº5î¦úûlroQÚ7êÔnêk©jSÿ³dËt ccciРÖe!„Õréú%v3ÿÔAóáA¸ cbÀM—‘¢Lm ÐÂ2]¶p!„Îh展MF zí6‘˜hþÝ·oÀÝÌË‹«¶&ÚNdˆP¨¯u ÂI¤¥¥•  }PÚ7ZÕoE熫õ½V¿·üôÓÇ kÅ „ É,–é*@7kÖŒ­[·R¿~}»_[ò…Z2D(Ô’!B}9ŸužÝçww¶úlõ{‹Ò¾ wÝeÝÏv2— ¸T:kC€–Ìb™®–|||èÛ·¯&×–†|¡–  µdˆP_–]Ž©ôœ»;é¶ê½Åd‚­[Íû÷ƒº­ôj«ÚÖÿ,™Å2]­@ !„ÎJ9<%2(’¡´-æðaóÐ íÔ¾-,“-„B8¸ÄÌDö]Ü8Àð Ühß ÐÜÐ-½½ Ðùn$z!ÚN¤!_¨%C„B-"Ôeõ ¶ýh«Þ[”-ZXïsPVVÛ>ü¨=«Ï’Y,“m'Ò/Ô’!B¡– êDz#æÝ7î ¹‹¨ ¨;ú «Ý[Š‹aÇócY}fÚ»ï’wé$$Ôš-™Å2ù9ƒHC¾PK†…Z2D¨'ÓOr0å P³ÃS¬voÙ·”ž_謬,6üþ;¼ø"|ò ]b ½Æ $³X&+ÐB!„³Fû†U•ïîß_»:ÀŒ?$õÀÓš5Ø uIÂN$@ !„Li߸»ÁÝ´®ßZãj¸ Û·‡ÐPmkÑPVVß:„)"ÂüÄȑ̘9SÛ¢„ÝH€¶iÈjÉ¡PK†k¿ãiÇ9œz¨ùê³Uî-¹¹°Û|˜‹ÞÛ7f|ø!I÷ßã OOޱgÿ~튲É,–I€¶iÈjÉ¡PK†k?eõjÖÿ Vº·ìÚ……æÇ:ÐÊê³QY}.•>l/ô‘FUYdËdˆÐN¤!_¨%C„B-"¬ý”þ箺Ҽ^ó}–Uî-Jû†«+ôéSóÏsR•VŸåV¡»wíjÿ¬D2‹e²-„B8 Ã©‡9žvpáA¸ »vmkÑHU«ÏŠÚ² -nO´Bနö †·oXEz:4o§§çö*WŸµ¨ZTMZ8ìDò…ZñññDFFj]†pÊaPPÆ•[PÚ7z4íASÿ¦5þ¼ß[¶m£ÑüXÇútRŽåø·ß’WR‚—‹ m}}+¼§¤°­{÷:mGAAžžžZ—áÐ$@Û‰4ä µÞ~ûm郪(„Ò ]ûL9ÈÉô“@͇5¾·üô“ùŸÞÞm•šœÑ·_Íáœ:”®0ÿ½ysþ¦qUÖuùòe郶@´ÈoD¡–„g¡–çÚKißp1¸0¦í«|fï-JÿsÏžæƒCtl~J `\-Ü [2‹eÒ-„B8¥}£W³^4ªÓHãj€ àÄ óc·o”˜L,NM O½z4óòÒ¸"¡ ÐB!„Ùwq‰™‰Ä¶s°Ý7@÷zsF—JçšžhÐ@ãj„V$@Û‰  µä$B¡–œDX;-;jnßp5¸2ºíh«}nî-J€®W:w¶NANj~éL“«+k\mHf±L´È¡PKN"jÉI„µ +Ž® OxB}­×_[£{Ë–-æöík>DE§²KJX]ú—Ö‘AAÔ©¥ÿ.$³X&C„v" ùB-"jÉaí³ûünÎg¬ß¾qÇ÷–øx¸xÑüXçí+¯\!·¤€'jáð B2‹e²-„B8¥}ÃÍŇ¢Ò¸šRÒÿ\FÙ}£¡‡4®FhI´BáŒ&cYûÆ€æòqr”ݰ!DEi[‹†Î°-3€ÇCCq54®HhI´HC¾PK†…Z2DX»ì<·“äìdÀz‡§”wG÷£Ñ|!è~õyáå˘J×öÝ7$³X&ÚN¤!_¨%C„B-"¬]”½ŸÝ]Ü5ÊêŸG÷– #ÃüXçZißèèçÇ]7Ý]ÛHf±L†íDò…Z2D(Ô’!ÂÚ£ÄTÂÊc+Ür0^Öﯽ£{‹ô?°ÿúuâssÚ=<¨Ìb™¬@ !„Û–´Ôóév±íäð¸ [·†¦Mµ­ECÊ곫ÁÀc:ÐÂ2 ÐB!„Æ–1ï¾áéêɈˆWSª ”!¯>™L,-=º{p@ <<4®H8 Ðv" ùB-"jÉaíPl,fÕñUÜ×ê>ü=ýmrjß[v<ócèï®^%­¨¨ýà É,–I€¶iÈjÉ¡PK†k‡Í‰›¹šw°mûFµï-Jû†ÁýúY¿ '¡ÝíïæÆˆ ÙZÐÆ$³X&C„5PRRÂÎ;ùõ×_ÉÈÈ E‹Œ=šzõêUz¯4ä µdˆP¨%C„µƒÒ¾áíæÍð6Ãmvjß[~úÉüÏN 0Ðêõ8ƒŒâbÖ_5ÿåfLp0Þ.úXw”Ìb™>~'ØÈ AƒvßêI€® &pöìY:Ä®]»¸xñ"®®®Lžg¤¤À®]t1iíííPõYû9%“LŸ>ùóç#,“m%‡büøñ<óÌ3ÄÆ:P›B‡´ëœù§u<êp_«û4®¦œ-[n<Ž‰Ñ® íºvÄÒ¿¸=¢q5ÂL77ëŠj;|ø0ýúõcÈ!,X°—›¶¹‰‹‹d{2!„¨¶ä@Šyü ð¾ƒìRQCyÅy¿LNQã:ŒcÁ¨Z—tÃÓO×_š[722Ì}Ð:ó? ÌINÆÃÅ…ä=¨¯³’[,“è:vì  o߾̟?¿RxVHC¾PK†…Z2Dè¼6œØ@NQp£}ÃÖTß[”þç˜]†ç|£‘W®ð@` îÂ3HfQCt œ8q‚pï½÷²dÉ\]]«|¯4ä µdˆP¨%C„ÎkÙQóá)õ¼ê1¸å`»\SÕ½åÌHJ2?ÖéöukÓÒ¸Vº…Ÿ^÷~–Ìb™œDX?þ8)))3f̘ ¯}ýõ×N$”S}„Zò#3¡–œD蜲 ³Ùpb##Gâáêa—몺·(«Ï Û­ì¾èîÎPžÀ(™Å2 Ð5ðÐCѳgÏ[¾v»Õh!„úµîÄ:òŠóûµo¨¦èÀ@èØQÛZ4ZXÈéé<‚»AO»?‹ê]Ó§O׺!„NF9<%Ð;-j\M9&Ó8ú÷†ÇÅ©©—î­ ×ö ¡Žô@Û‰4ä µdˆP¨%C„Î'« ‹ïN~ÀCQáæb¿u,‹÷–Ç¡txN¯íÊÑÝ>>tó÷׸íHf±L´HC¾PK†…Z2Dè|Ö&¬¥ ÄNbÛÛ·}Ãâ½EçýÏGrr8˜ Èê³dˤ…ÃN¤!_¨%C„B-"t>ËŽ˜wßñ ¡ox_»^Ûâ½E ÐÍšA«V6¯ÇÑ(Ã`œÎ´dËdZ!„°ƒŒü 6ÙÀè¨Ñ¸hؼ¸¶o7?Öáê³Ñdbai€î[¯ͼ¼4®H8: ÐB!„¬‰_CaI!`ÿö ‹öî…Òö=èÍ™™\*íû}¢A«Î@´HC¾PK†…Z2Dè\”ö†~ éݬ·Ý¯Û{Kùþçþým_ŒƒQ†}\]¬q5Ú“Ìb™h;‘†|¡–  µdˆÐy\Í»ÊæDsH}¸íøìÿÇïmï-J€nÛ6´OA"»¤„Õ¥D9ÇA2‹ 2Dh'Ò/Ô’!B¡– :oŽ}C±Ñ|<´VíUÞ[rsaÏóc¶o|så 9%%€ì¾¡Ìb™¬@ !„6¦žÒÄ¿ ÑM£5®æ&;wB¡¹7[ZÙ}£¡‡4®F8 ÐB!„ ¥æ¤²-i´{vŸҾáê }úh[‹]((`kf&‡†âªÃÓÅ‘m'Ò/Ô’!B¡– :‡•ÇVRb2·ĶÓn÷*ï-J€¾ç¨WÏ~9€…—/cTŽî–Ý7ÊHf±L´HC¾PK†…Z2Dè–5ï¾^/œn»iVÇ-ï-éépèù±Û7”Ý7:ùùq—¯¯ÆÕ8É,–É¡HC¾PK†…Z2Dèø.]¿Ä®sæ¿ä<ÒîMk¹å½eëV0Íu ½~ã¹¹€¬>ßL2‹e²-„BØÈÊc+1šÌUËö*)힞г§¶µØ™²úìj0ðXHˆÆÕg#Z!„°¥}£UýVtnØYãjnA ÐÑÑ £ã«‹L&–¦¦08 €P+ÎF´HC¾PK†…Z2DèØÎgg÷ùÝ€öíp‹{Ë… pâ„ù±ÎÚ7¾OOçJQ í·"™Å2 Ðv" ùB-"jÉ¡c[~t9&Ì;<8BûF¥{Kùã»u •ö 77Fi\ã‘Ìb™ Ú‰4ä µdˆP¨%C„ŽM9<%2(’¡4®æ÷%@ûûC×®v¯G+™ÅŬ»z€1ÁÁx»ÈZâÍ$³X&¿k„B+KÌLdßÅ}€c¬>ß’ ûô1¢¢ËRS)(ÝyDŽîwJ´BaeÊê38Fÿs%ÇÃ¥KæÇzkß(mO÷ò¢·ÎŽÖ#-v" ùB­øøx"##µ.C8e€0Hz8β#æÝ7Ú‡´§mp[Mk¹víyyyœ:uŠV­Z™Ÿ\½úÆ:v„”êÖ­‹···6EÚÉé¼<~¹v €ñ¡¡Žv¨ºÃ(((ÀÓÓSë2šh;‘†|¡ÖÛo¿-}ÐBe€Pz¡ËÉô“L98Fûƨž=¹'=ï¯]c˜²âšžnþ§‹ <þ8ç iËÿΚ¥]¡v° ÜŸÅãe÷*]¾|Yú -m'òQ¨%áY¨%ÁÙ1•o߈m¯}€žüÊ+äOžÌ{¹¹Pzò^£.]bJ“&<;}º6Ú‘ »ûûÓº–¯¶×„dˤZ!„°"¥}ãîwÓº~k«ÑãÆ±&,Œ’*^OÜ££iܸ±=˲»]×®q&/½ŸEÍI€B!jè÷ßç½OÞcÔ³£8¼å0\sœáAƒÁÀ£¯¼Âb_ß[¾þ~“&¼ôá‡v®Êþ”½Ÿ=\\ˆ Ö¸áì$@Û‰  µä$B¡–œD¨½ÌÌL†?6œA¯ â/GÿšºkÀØg7žÅXº]šÖFdž +­BŸ xê`õn´oº»340PãjŸdË$@ !„w`ù†åäFåÞö=É-’™·|ž*ºìl <ÂX“‰…¥O½ß¸±.VŸS ù¾tÛ¾±!!¸d÷gQs …Bˆ;–­¢ÿ<öÚgûb,yî9HHàÿ³wÞñMUï'i:é¤-»Œ2Ë”-*2E@6‚ã'TŠàÄ Šƒ¡âWÜ \€ÈT”%`¥ì²Z e´¥t¯¬ß'éLšt%i{Þ¯×}åæÜ“{ÒpòÉsŸ1ØLà~Ç5Âû¼2>­AÜ%Õ7$…ÐvBäKlÅYn÷Jœ™DèX<]=­OÊ„:A޹ýê+øñGΊ{ïå¡÷ßg„·wð>C~ëîÖžžtóöv°5U©Y¬#´ù[‘I„[‘I„Ž%´~(¤–<Ç-ÎÁw¶Aæ8v ž~€…žžðý÷Œyä>_·®FxŸOfd‘–ˆÖÝÛšÅ:²¡ù[‘I„[‘ „Žeñ[‹Ù6b©÷YPÑÙÐæFÿ} 3‘žãÆAV¸¸°|Û6 DÜ=`€cl²3¦Îƒ àÿ¤€¶©Y¬#=ЉD"‘”Ý7w“š ë"¹„ÊJBw‡òã²Q©T±Ç‡¨(±ÿöÛp玱ÃAè ~0 è>~~„¸»;Ø"IuBz %‰D")%‡¯fÒ¦IÐ|ûÒ.ªÉ™Éd²ðVyÓ)´K·/ÅÛQ1·_~ ?ý$ö†—_vŒdWr2qÆX^™<(©h¤€¶2 _b+gΜ¡uëÖŽ6CR0%:Ø’šÅÕ´«Œ\5’lm6.JÖ>±–þMû ×ëQ*|s·@Ü3 ÂwßBQãÖ–ÆÚÏž*cdëîR‘““ƒ[‘¦;’ÂÈ;!ò%¶"“%¶"“íO¶6›‘«Fr5í*ú0O<ŽÏéépÿý ..°r%`Õ¤µ%]§c½ñæ¨À@¼FSE‘šÅ:Òm'd@¾ÄVd¡ÄVd¡ý™´i‡¯`j—©Ìè>ÃÁaêT8{Vì¿óN¡¸ç𴶬MH C'z.ÊÖÝ¥GjëH´D"‘H$6°`ï~:.âŠïn|7ˆ,s°EEøâ áq÷üÒKŽµÇ˜ªoÔws£¿¿¿ƒ­‘TG¤€–H$‰Ä ›¢6ñÚÎ×hê×”_Æý‚Z©v°UˆŒ„gžû Â÷ßC mY}%'‡?““x(8U }$•‹ÐvB&JlEv"”ØŠìDhNÄŸàáucÀ@-×ZlzpžN”¸™–&ê=›âžW­‚Úµ‹M«)kË7n —­»Ë…Ô,Ö‘ÚNÈ€|‰­Ô¤DIùI„•Obf"ÃW'=7 ~ý#í‚Û9Ú¬Â<þx~Üó¼ypÇf§Õ”µå;cõNµjÑÞËËÁÖTM¤f±ŽL"´2 _b+5)ÑGR>daå¢ÑkûóX¢“£˜×Ã[ w°UEøüóü¸ç!CàÅ-N­ kË‘´4NgŠ®6Òû\v¤f±Žô@K$‰Db†[f°çÒjÿ¯ÜùŠƒ-*Bd$<û¬ØoÔ(¯ÞsMÆä}V)<ì`k$Õ) %‰D")²CËøâß/èV¿_ÿÚÁ!-­p½g qÏ5 ÁÀªøxPÇÕÕÁIª3R@Û /±•š’è#)?2‰°rؽ“ç¶=@}ïúlx`î.S§Â¹sbÞ<èÕËêKªûÚ²5)‰µŸË‹Ô,Ö‘ÚNÈ€|‰­Ô”DIù‘I„Ïù¤óŒ[3­^‹»‹;Ø@}ïúŽ6«0Ÿ.<ÎC‡–÷\ê¾¶˜Â7|\\!ÛÛ— ©Y¬#“í„ È—ØJMHô‘T 2‰°bIÍIeøÊá$e%ðõð¯éV¿›ƒ­*ÂÑ£…ãžW¬°9î¹:¯-ÉZ-¿Þ¼ ÀýAA¸;º¥zGjëÈO˜D"‘HjÈéÄÓ̺sµÈÁVÁÆzÏ5‘Õññäèõ€ ߨ) %‰DRãyyÇËl9·€á­†3¯ß<[d†)SòãžçÏ·)ð1ä ‰»;wùù9ØIM@ h;!ò%¶RÝ}$‡L"¬¾‹üŽEûÐ.¸?Œþ¥Âɾ?û V¯û÷Ý/¼PêST×µåBVûSRx¤Njv!¿ŠAjë8Ù Q}‘ù[©î‰>’ŠC&–ŸW0õשÔö¨Í¦7áíêí`«ŠPޏç‚T×µåûß¯Èæ)‚Ô,Ö‘I„vBäKl¥:'úH*™DX>®¤^aÔªQäèrP+Õ¬¿–¦~MmVaLqÏ99"îyõj(Ó©ªëÚbзûøÐÂÃÃÁÖT¤f±Žô@K$‰¤Æ‘©ÉdĪÜÈâké¥ÜÝøn[e†‚qÏ Àí·;Ö'coJ ³²Ùº[b_¤€–H$I€ð áD\‹`z·é<Þåq[e†O?-÷’ŠG&–·÷¼ÍšSkè×´‹ï]ì`‹Ìðßðœè†HHH™ãž RÝÖ–l½žŸ¸¯vmü]dTjE!5‹u¤€¶2 _b+Õ5ÑGRñÈ$ÂÒ³îô:æîž @¨(kî_ƒ‹ÒÉ„Wjj~ܳZ]®¸ç‚T·µeSb")Z- k?W4R³XÇÉVê‹ È—ØJuMô‘T<2‰°tDÞˆdÂú 0àãæÃ¯ýJ€Gù…i…3e œ?/ö,€ž=+ä´Õmm1%ªÕ ‘ e*©Y¬#=ЉD"©öÄgÄ3|åp24(JVŽYI›À6Ž6«8Ÿ~ ?ÿ,ö‡ “qψÏÍek’h¹þ@p0êr†·H$¥Ez ËIvv6‘‘‘DDD’’„ ¨_¿¾£Í’H$‰‘\].£W&6%€…2¤Å[esqϳ¬ŒGk0²ú†Ä1H]NÚ¶mËÅ‹ñðð ++‹>}ú˜Ð2 _b+gΜ¡uëÖŽ6CR0%:ØçfÚæi컼€ 'ðb¯l‘ÌÅ=ûûWè%ªÓÚbjÝÝÚÓ“nÞNÖø¦““ƒ›››£ÍpjdG9ùä“OˆŽŽfË–-%Γù[©n‰>’ÊC&ZgñÁÅ|óß7ôlØ“/†}á`‹,0yr~ÜóÂ…÷\ê²¶œÌÈ "- ÞçÊBjëHt94h111%Γù[©n‰>’ÊC&–̶ Ûxaû 4ôiÈúñëqS9¡Wí“O`(«Çðáðüó•r™ê²¶˜¼Ï àÿdõJAjëH´D"‘HªQ7£xà—Ðtx¸x°ñÔ­å„Þʈˆ|Áܸ1T‘[Yè ~4 è>~~4’a!´ˆˆˆà»ï¾cÆ …¶¢M䘓crLŽåu&ŠìììR½69;™á+‡“|42aùÈåt®×Ùéþm~ú‰Ä1c Å=oسÇyìs±]ÉÉÄsŠÚ=êtöUÕ1“&yå•Wøî»ïˆER22„ÃNhÅÞ%kT·na’ÊCv!,ŽÎ cü/ã9{ó,3oŸÉ¸¶ãl•>þLáï¾ =zÀ† •v9gO"|õÕ¸r%˗ϱr埸¹yä3Øš7ÀS¥¢—­­Þh4G›àô( cI¹Ø½{7}ûöåÀô,’üNll,»víru’ªDxxxµ‰U”T.Œb«ZÇB¿ž×õbßG‹j•8ýÙ­ÏòÑ?0ªõ(ÖŽ_‹'¬üñÇ0c†Ø1¢R…³ g_[š5{èè-OðÐúóà®çá:uø¡Öñ®&ôë×§þ¼8é¶2 _b+rÁ’ØJµÎeàëÿ¾ÎÏëtäûÑß;§xŽˆÈoÒ¸1|û­].ëìk‹Ré”§~×up?¦dëîÊEjëÈh‰D"‘TyöÆîåÉÍOäÄÆ7â¥ör°Uf(ZïùçŸ+¼ÞsµeHtMËb€|Ï$Fz ËÉœ9søã?HMM`òÑtøö IDATäÉÔªU‹¡C‡2gÎ['‘H$ÕŸK)—½z4¹º\\U®¬¿ŽÆ¾m–y&M‚ Äþ{ïA÷§ª”·%‹Ý£PsÂ; ’…Ðå¤[·nxy÷r´oß¾ÐsÙ‰Pb+Ξè#qd'BÈÐd0bå2ødè'Ür§ƒ­²À²eðË/bäHxöY»^¾J¯-n€B¤l½à`cª?²¡u¤€.'ÇgøðáVçÉ®>[Y¸p¡ÓÇ*JœSš mÀÀ#ë!òF$Ïôx†I·Mr°Uø÷ßü¸ç&Mì÷\*½¶Ã78_ ë·kK àÆ2Ú R@Û ùA”ØJ•ý‚“Øš*œM¼ñç¬?³€{BïáƒA8Ø" ¤¤ˆ¸çÜܼzÏøùÙÝ g_[,Ök™3Äþöºüû/üð<ü0(d$G¥ 5‹ud¡D"‘Hª?Ÿü™·ÿz€–µ[²zìjT •ƒ­²À¤Ipñ¢Ø—qÏf¹rââ,4yŸu ØLN<òÜvlÝj7%’BH´HHHààÁƒÅÆëׯ/éI$I)ˆ¸Aø†püÜýØôà&üÜíïѵ‰eË`íZ±ï€¸çªÀ0z´(LR Uô3 è#p˵4ˆŒ„Áƒ¡o_ч¦[7»š-©áHm'þø#ž½{oÑâŽYã‹$ÎJ•Nô‘Ø•šD˵ãq$§&ã×ÀŸœ#jþûa²´Y¨*V]E«Ú­m¦yœ î¹ Î¸¶¬X?nÏçñòš…ÍÈÕ^ãfÎf nßøÚ‰GýiÑBOŸ>ðÁžþ)š8Ž óæA‹Žý7Ud¡u¤€¶7ÍÅÆ•Êâ^iIͦJ'úHìJuN"LLLdü”ñÜ{ê.ü\}ÀN$Ÿâ•gßA£ÒÀݰhÈ"…r˜W¯^Í+aZŒ´43rs©ëêŠßÏ?;$î¹ Î´¶èõðÒKB¸¸À‚¿ñÿ—7gøÔ©Ü¼ãEX¿ŸGá¿eãqS(ðññÁÓž|Þz ¾øBx¤×¬õëaÊxã ½VÊŽL"´ŽÐvC~%¶á,_pç§: gƒÁÀ AŸ[Ý!ÝxÀ 45pZoɳo96âÿ¤o|ù ®X×µk[|pXÅ^\bgLô‘8'Õ5‰ðfÖMë“‚àØ©c•oL ( yí5VL™Âä´´bÇÕ©Ã[‹;À2ó8rmYºT„ZètâùË/Ãüù 4ãÆ{úÝwIš2¥ÐXìàÁ¼½x1ïΞmõZ÷Ü Á<{6DG‹*‚=ï¿/*v Xÿªê‹L"´ŽÐvöN„·nÁ‘#b+Š»;4kV\X7o.¼ÕêÒ[µxñ&Î{Ëê¼Îßv¨€ž9s!gÎÄXw×]]˜5kŠÕyÎŒ3%úHœ›êšDèåêe}RÔ ¬[ùÆXaĸqŒ7‰ÇSp Ž|t*ï³#ÖF$û}õ•xîî.ö~ØüüŸvïæ¿ÀÀ|ï³m»vüòþûÌIO§V­ZV¯«PÁ—Ô@¤€¶;ÆóÐCÅ;vè0µÌç¬WOlwÞYüXJJaA]P`ÇÅ•>ùÜ9Ñ2µ,(•–E¶­"üêÕ²]»*"“%¶RÕ“£nFñäæ'Ù½+oìîÆwóé}ŸÒ&° 7½Á߃¶¢¼ ZƒOµOÜõïï|ß1 Néé¢6ÚâÅBuX„-bă2¶cGz?î´Þg{¬-O¿.¾c”J‘(øòË–çÿÏÿ>Æ`@9n“ÝܸÓ××ò 5â¶ÊmgË–¢öô¡C¾ݻşwî\!°çÌ©SË–[TI„ÖQ ²¨KeNll,»ví²>Ùdg‹¬äóçaÊ”pnÜXnëÂ[æU¶]_¡§qãå4l(b >šöëÔ©øj&&:vBF†õ{€aaîlÚô™Ùcááá2Zb6lª^,t¶6›yÏã½}ï‘«zƒ½‚yàûLè8¡ðä×3àºÑ í£€Er ®YÏ?WŒU‹T*xê)àãÀ†Õ«yiêTöœ9㔺2×–¬,ÍL@äÝüô“(Ug‰¯®]ãñ³gÑ ¸)•¬ c¤ƒ~ þþ;Ìš%BOL4o.~ŒWyßÎJ¿~ý ‘ßE% =Ðv™òÝÝEMÌ6mD¨6 ƒ¯¿N—Ü\ñXpßÜXyö‹ŽEGç;|JÂ`È·„Z-âð, ì† …È.KèIvv0.,·:/((Üâ1¹`Il¥ª g€­ç·2}Ët.Þº€R¡dj—©Ìï?w[g†¨(!”ÿø#ìÎ;á㡈'tĸqÔmÔÈ)Å3TÞÚrå Œâyh(lÚ$¾7,ñÁå˼pá^*Ûµ£¿¿ãþþƒàAðÃ~é’p2=ð,Z$*vmöRq&Íâ¬H-± //Ñ=ÊQ„…ÁéÓÖçÕ«}ûÂåËbQ‹+ÞJ£‹£)¹Å&‘mI`›<Ù2¾["±¸´8žÝú,¿œú%o춺·ñÙ}ŸÑ½AwZfÌLá~üàƒüE$8Þ{&L0ë’T(ôìÕËΆ:–`ôh¸~]<ï×O8ëJ¨ 2':šwŒ °¿‹ [:t §Ñ‹ïH”Jñ§?^ü>š?_ä9": ÂĉCÈζ~§±sçV®üŸ,—8) %ÕŠ† áÇóŸ "yÒ$¨M÷Ë*²]\ {²6u¼%I>:ƒŽ¥ÿ,åõ?_'-W4ñqóáí¾o3½ûtT •ƒ-4Ãúõðì³+ž+•0mšÔ~~ŽµÍ‰X±BT¯È1æÂOŸ.ÂÃ],( ð̹s,‹ ®«+Û;v¤½— u¿íˆ››ˆÖ™4Ixž?úHüžÚ¶ ¶o/¯`ÒÓ—[=OIwíAHHw „ÛF:wvgÃó!…ËHm'rr,WÛp$ÍU‚‚Ú03Ãú'D¡žâ:uDÍOsXÙã⊇hµâûÕô[Ò҄Ǧ®™>2‰Pb+ΞDxðÊA¦mžÆÑëù ÔãÛŽçÃ{?¤^-' s8^„klÝš?Ö£|òIµè¸QQk‹^/ï-ÏÕjQÅbj 9ñ:ƒÇ¢¢øÎèªnìîÎŽŽiîáQn{* __á…ž1C$~ó(k¥£¸Ó R…³Ü꼦MËÉ$BëHm'nØhìþúk)))VçN¶:§r‰£Q£'¬ÎrsË.õ™mÙ7nXØ&O¶-qÚ'OŠpOOhÚ4¿\a³f°qãB–,YNӦˡ–›\O\œõÏäm·…1p`ïÊ1BRnœµá­ì[¼²ã¾ø÷ ˆ<õ-øxèÇ læ„=”³²`ÁžarvÔ®-î×OšTm2È*¢aJ <ø Hº u”{—°LäèõÃÃOô1U)*¬Mb»hÄD±>\ü:¶†~¤¦Â޽³®Vç?Ü·4VMÂFG'n>·™[f“€J¡bF÷¼Ýïm¼]K¸â(~ÿ]$ ë£PÀĉ"ö9(ȱ¶ÙÒ&j4¢²Æ—_Šçðí·¢Ì[I\ÊÎf`d$ç²²ÌmÚ ®a‹Bûöbý´ƒÁ²À¶&¾-=¾ùfÙ+CÉ$BëHm'œ5‰PR±tî`Sé¢Þ½;YoÙe Ï¥E¡±Ë®®wÎÅ‹Ë. e¡u¤€¶òƒX3¨ˆ¢ùeMôñõ…Ûn[Q !FMÞê™3Eƒ€ÊD§›=n¾œ<)ò¾Ú¶Íß4¨üë:{ g­^ËGÿ|ÄÜÝsIÏ·æýÝýY0`S»LE“‰¤Ü\QkmÞ¼ü0c†ø•Wƒ°um9v †ϯƒûí"yÎ\ÙÍ‚ü—žÎ ÈHŒåˆf6jÄ¢ÐÐrX,qR³XG h‰¤ PAÙ ð(›:lY#, –,Éo«®Õš¬¬c'OÚ&ÀÓÒD«ù‚øúÔmÛŠOýúe{k:û/ïgÚæi»q,olBÇ ,ºgAžNþðÇB$Ÿ=›?öðÃBP[S‚5˜õëá‘G ÃXúâDøüs‘´\ûRRzü8)Æ`Þ·›6evãÆ•l­c s/÷FIÕG h‰DbÑÂÖQ´jUXÿXÂÛ[„|¼]™’"ª—­`âï/„tQq]M5eÊl.\¸buÞÀ½x啺M8!IYI¼¼ãe¾Žø:¯¦sXPŸý”Þ°Fø•+¢`ïš5ùcaa¢7sŸ>3«*°`HÔëE.å{UÖØž”Ĩ“'ÉÔéPKZ´`F5¾´iSÕèÚ׫W€Ù&)E5j@åS ‘ÚNÈ$B‰­ÈN„¥§m[8p®]˯«}êTþ~rrþÜ[·`ß>±$ ßK]PXשcýúýuŦr{YYá* +3‰Ð€GWðâ/’˜)®ã©öäõ»_çùÛŸG­t²:¾ |ø¡Ï0¹OkÕ‚7ÞñÏU°îpEѲe4š&h4)¨Õ¾ÅŽ "á8#Ãø __Xµ î½×ú¹×%$ðàéÓäêõ¨ ¾iÕŠ ÒÃïüøcÙC e¡u¤€¶2‰Pb+Ñ-Ì99Wðñ±îE1JßÙёԫ'¶E*W¯æ‹é‚âº`Τ$‘0eJš2Q»¶ùPààÊÿ÷X£¬I„YYY¼6ï5üw€ MþþŒºwÏ<ñ …‚“ '™öÛ4þŽÍ3†·Î’ÁKhìë„·åÿüS”‹8}:ìþû… ®ÆžP[Ñë›[:‡ËK˜N‹¢·L«VÖÏ»üúu&GE¡3pS*YÆH'm+/)2‰Ð:R@Û ùA”ØJe‹g€mÛ>æ– éÙ ÞWé¶”„‹KMš„[ççWr2XýúbX¤‹t\\aamׯ¾€ˆÿë/±$00_PôpÛ“Ò ç~ý&påj±™“30 º˱èaïŽ}¼6ÿ<»‘Ôþ,z…(:ÞØ·1K/ax«ám¾EÂï½WEF_ÇWë@–2“U;> [«¥çÀ<{ã¬\™ÿâ–-aÙ²âl %‹gQûÐ!ñh%W®ðìùó/•ŠíÚÑßß¿B¬”8©Y¬#´DRie‹{É 8yòJ=¿)±òž{ _¹b^X§¥åÏIL„={Äf+iiâ<þþb³µ£YEqù²Žó)gàñÔ«¿ôtd†Ü$sÇMèj¥šçož×ï~Oµ§]ílFÿ?þ §^ÜL„Ò¹7!€w½¼èúÅùÕ5<=áµ×à…*¶X ",Ì6ñüö¥K¼nl}îïâ–èéãSÉÖI$΅ЉDR„† Å6hPáñË—Í kc³5«œ<)<Ö&<<òÅ´i °m¬,11ó_myå÷š€ç~þraAa¥¿HðÔܹLÛ¸‘ž/š=žÍÈàeÓÀÈ‘¢èm5­úP^ v0- [J5ϼpÿ]¾ @]WW¶wìH{/¯rX'‘TM¤€¶2‰Pb+2‰ÐyiÔHl“«   kJKV–ØÊÒ0ÆË |}ñõ…  @‹B»àx¶" XQS­¡Öú ‡‰gÂFŒààGÑÓŒ¹ÓA¼ñK–À!v¶Ð9ÉÌ?ÒNœÈ€Öžžüѱ# ¥Àª¶È$BëHm'äQb+R²´YÅ'+à"Ь7å?µ!³ ¼ÜgΈÍ%‹ì ¬xè ˆˆ€Í›Åvø0‹ŒnÔ»€À(ãT ð¡Ñ7wÀM /áÄ•Ïùó:.\Xnuž«k¸Õ9—/ʧO‹Ÿ’P© ys!” n-Zäw*oÞܶΣEIÕjvâËÌÜåëËoíÛãã"¥ƒD"ÿH$I91bhÔ(Üê¿|œ­é¿¢AÍ[$qu !•³ø¡ãîr¿—öæâEèß_ˆecq‰Ô©S\(·m+ZÖ—…îÝhÐ œÌÌ<=‹w"41hP/®æä0ðØ1NñÇóC›6¨m)Ó!©È$BëHm'd¡ÄVd¡ %'…=1{ò¼Ì§NŸ ¾n¾ 6˜þMû3 Ùšø5)ùĆæpëüú xü .ñ i™€®äÞÍ.. ZRxdV–Òqѹäþ±¿}›izj3Á©ç‹ÍÍÆÝôAÈê¡Dg6…Ó :æ™â>ÜH¥1;8C*½ ½þŸD§H…BxºM[IÏËzÌÜ\cA «\»f~®ŸŸÆEÅrE7óûé'ÑÒ9<<ÜêÚr1+‹‘‘Dg‹.¤“ëÕãó–-QJñ\£I„Ö‘ÚNÈ¢ÄV¤x®ºäääðî’wÙµ©Y©ù1vèX¦L˜býµºö_ÞÏ΋ÂÃ|äêt†â jÞ®ÞônÜ›ÍÐZÚ·Cmâ¦A=áFüÀ*ãaa m:_1®^ÅcËZnÞLË;ÌÈnÔˆœC¹Öy(§ëõ#&ÞïËÐ+=Ù—.®&‘MâÑa °]Z­qv”J¸í¶âB¹aßæ²bmm9™‘ÁÀÈH®åæ0³Q#…†ÚÁ2‰³!5‹u¤€–H$’ àøÉã<ðäœ =‹¶£€ömÞÇŠ5+Ø´byóõ=ÿ]ÿOx˜/îdoì^³‰®*Wz6ì)sÓþtoÐeÙ–î]»¾+ë?ÏŒ#Ì0Ä£i+ø¼²ŽED€ÑQ["·ß{÷ZŸçH§¥1øØ1njDhÍÛM›2[6¦‘H,"´D"‘”“¬¬,Æ=1Ž3wŸÉ<PAFXûRöqÿäûùôËOÙ½“wògÌŸ$ewŸ*PЩn'!˜›õç®»*¬•öòÅ‹‰)©¤†‘žpïØ±æ&'ömB0ÿþ{ñ gqƒ ÁÊ…ŽÐ÷4z" ðß©ÿX7`¿}ÿ ­ÔÍÒfq&ñL1¡|!é‚ÙZËQ  ±_c‚ÂhÔ–° 0‚ÂhÔoW!.ïM䎡wpÖë,˜Ë»I‚.Š.ôêÙ«,oE!+‹À¤¤ÂBùÌ8{Œm˜‹ò"°ø´ÀØò€VmÙÝ»‹.!§%Ç$” ˆd“PÖ,ÜIKƒèhxôÑBÃéC†0{éRK-)€L"´ŽÐvB&JlE&擞žÎ¤Y“8×ït4+ »I6‘‘Œ›:Žý[ö‹ù¹éœN<]L(Ç$Ç 7èK¼–R¡¤™³<œ'”ÛX-#÷ðÀÜs%‰;#ÕhUZ *£ˆ1€R«Dš Ï£Ì}ê)ÞûúëÒ¿\¹RX GE±÷èQHLd¤%ÑT//‚Ѫ!­Zá²e чÓøÕÕ•{^z ×=Jo›$äädž™=›Ë–UÈùrõzÎfeå‹d£`>Ÿ•eY(qQ(õð ­—m==9ðÑGìЙù±èêÊ…æÍùý?¤ˆ–ä!“­#´¦E§Nœøçù«NR"öÏ7oÞäà?ùïäÜÙýNºu톗——]m°ÄKo¾Ä¹öEÄsAjÁ#tz¦·šÞârÊe V’]”.„ú‡ÉmƒÛÒªv+Ü]ÜËdçS³gséÑG™žc.ìCäð¤ÓfÏ.ùDééÅB.ˆŠÞäÌÌbÓ‹E+¢Ïv«V…·Ö­¡AƒBžå{Œ…·ßÎ'W¯²<$„•Ï=WÚ¶¤³-bí¹s<òçŸ èÛ׿×i Ιñ(ŸËÊBkE(«LBÙÓSˆeãÖÊÃWc)Á¤¤$¾…yóÌž#eð`f/[&´$)ž­#´8rü8±}ûòÁgŸñê3Ï8Ú‰­VËKs_b;5Ä×'×;ÏÞÅñÊäWxüÑÇíjOzn: ™ $d$ä=þvð7ôJökB5Dî„€Âãj¥š–µ[ó(·¬ÝWUÅV¸oÌFÍËä'ÌFpÄtíJÓ¦ME»K—Ì å¸8Û.èãS\$·j-Z€‡‡M§ AÕ£Kû“'ãZI5j ÉÉÉüIÆóÏ3ëÃ9bF@k ÎeeqªˆGùlf&„r3w÷B"¹­§'­<=q³RsûÕ÷ßçÒ}÷Yž Vs±E é…–HJÐvâzz:š±cùváBf>ñ„ôBW¹¹¹Nû¥ëÖ-ž›ó§cO“š“Š¿‡?½:ôbÞkóœæo?~Êx6+7“Ó'?>?³A&—Ú^â¥__"îZo½úV g(™”œ”BbØÒc|F< ™ dk‹×÷ úî:%Ƀt\qs¡ï̱…„r‹€eny]&ÍË×>Ê“fâŽß¯U‹µZèÐγ­ÿ³J%Zí™ÊÔèâÅÅ‹Áa'÷>gff²óï¿6ö^G›b‘Ù‹qyøppsãbÓ¦|»u+¾]ºò*ŸÍÊ"Wo-”¨ˆPöô$Ìˋ֞ž¸—±9MJj*=¶n-qŽ^§ãäÅ‹ .Ó$’š‡Ðv"£^=b pZ/tdd$[vn!òt$=;õdè=CiÑ¢…£Í*ÄåË—™òâboÆ’¡ËÀGíCÛÆmùüýÏñõõu´yüù÷Ÿ<öÒc\êz C—|¯Ò‘+Gø­ßolþ~3¡Í,÷‚³Gá;oÌ᯷¢ ÉÁÓLx¾–T¾þn)ÝZwbØèÑ0”•d“ NÈL 13‘\]n¹íÌ € 7`²™Æw&õ€&#†³rÌÊr_Ï,:$%ÁÍ›%n÷ݼÉèÜ\&Ö] éé4ýýwóç(nѪ„†Z­³\ÚN„EiРO½õ–Óþ5ñÏÑ£$ººræüyZÓÉî×OÕj¹eÜ’4ñ¨Õr˸=)‰5舺Է† á±÷Þƒ9s,žS©PÐÔ$”"¹­—mÊ!”-±òã™ ,±™Dh) íE®¹]»2wÎvµoO=WWÚøûÓ¡n]BÜÜhì‹}ÿ$§OŸ&::š÷–¾Gdr$ɵ“ÁVÿ¶šw¾}‡»ZÜÅôIÓ ·ŸÄîíÛY¿n ¿ìü…ä&Éy•‹gOp óN&?0™Á÷ §ëí·;ÌÎécÆp`ûf<}rhs¹èQ è£שžÉ3o™÷îVd¡V¯%S“I–6‹,MVÞãËóiR&Ç-¿ö "™'_ú?¦\ô!13Ñjµ [qwq'È3ˆ ¯ «ßª¿åï.ä±TCžúPϸüíá—“gØvñ¬¬ââ71±dqœœ,’ølà1àk`0ßÓ“¹™™¼¼èâ"š˜ó&—£ó[i;åëä…wߥûm·Ñ¡}û2ÛQ™¤§¥s-3m›0û‡V†Ž(ÊP%$K¯Ï¼&ñ›dÆöMÙ´Ÿ¬ÕZCfÅŠÂåáÜÜÄ¡£GQtêDScŒrX¯r//<ìÜî\&(KlE&ZGa0ØøÍ )3ááᬸ~f͇‰ú¬f¾ô|\\hìæFˆ»{ž¨qwÏ«ïꊲKL¿ë.ºìß·R HÞwõäÜLÔz¸„ ýÿýï9pá}°wo†ÿýwžpJžõòâËŒŒ¼_ÿ‰S¦0ï‹/c$пk'ÞŽˆ¤—ñUзV-N¦§‹÷xO ÑScÖü×…¸- l >Z:fN[š¯Õ›wÝ*V£qðuºe;o÷‡ƒ 1%ÿ›½]½-Šà@ÏÀbcµ\kÙü~êõzÚ·kÊóçb™¤…C =<ˆÎÌ$˜â†îÝøjÖ\Û„±¹îzåA¥äÚµó¶ÑÿM×Ü\懄°"*Š}úðɶm`çÇÖÐét´2„3Ó¦ÑïçŸÙùÓOŽ6É,뻯#±VSðò";þ"n/¤ÑgèP‹‚×ÒX¶•Љ²â‘•EîG¡3­ï&rr[¶ŒÃ6àYŽZå‰#dgÜ’p®½¦Ð½;ê×^C?p º"ÕRµZŽkµÏÈ0ûR…‚†F1mNh‡¸¹áUŠÅºí þ\>¼¥Æ0ØGH{ðAn-_ÎF†»x|üýeû·VÙ!µùÍ ~4¾-Süü¸:r$ÿ­^Í‹FQ4·–‚;šM®.N#õšŸÛ2ÇÖçÇ•ç™én ;Ÿ $fäH–ýôÏef’ |éçÿû†Ïã˜70xÀŽ HL‡@àùÀ@.Ý?ËV¬à¹ÌLö+à\ ¨ëQ—a‡•è%vS¹Íg0ˆßÌÌü-! .]ƒÌ âyVVþ±‚ûEž+339â]—1ú+<Šž™uêöÒK¼ö曼•’‚&–ÿ}þZþ7ÅË«¶ióõ-V3yÂêÕL3‡ŒE‹˜>m¾úÊéÄ3ïsl¯^àçÇIwwŽ?^*/t®^O¶^OV‘ÇBc:]ñ1çeëõ¤ÅÇóhJ2ÁuÄ:™Û8„§—M^û t$¸(ø»¸à¯Vàâ‚¿‹ juáG Çg¾þ:ŸŽ[ü¤nn\ eÿ_•ª"‡D"©8ߪ^S:ï)±ô›ŸhuGob³³¹”MlNN¡Ç›šÂe±´1ÙÙÄ”„ä«RPGÁ.Uz•Z”ZüÈÁ<ÈA«Ó ÑkøqÏJ4u ¼ YÀžÐP4cÇòÃöí¼tîqÀ‘&z.|ó›t›Ðéuè :ô}Þ~ÑÇÊ8–v*zõàÚyP4mJîĉ|ù×_Ë¿›Ï¢0€ÂøÅ©0€ ã÷¨Ò ö òÆÆ15b¯)p¼À%O…ŒÖ0;”n~ÄNŸ@ìĉ|´p!*³¸»¸Ÿe$¨  4n*}þ¾Ò`û1T¨*\ J\J\ *T .(Q”¨*T(pA‰ %.·â’H Ìa¡¸Ö nÆ P(ˆ}ä}ø!» 9üzüs}¨·)•Þ ®«3 ÔéQé œÏΡ•J…R§G¡Õ¡ÐéD"›NÚlЦ‹ýrÐ W¦Ó‚ý¸b|?/M˜À³|@†Î)0œÛKòßû1(½Riy_¥ÂàíÞ××¶ùE÷]]Ñ{zbpwÇàæVh?ÄÃéþIê¤IÂÎéÓYûÉ'ÜûÚk\ööFçãƒÎÛ}­Zè¼¼Ðzƒ 3JÞ×ëÑÅÇÛ>¿„}Mn.¿ÿò ú¹sÈyè!†ÎK§wß-•Àµsk"%E<–6vÇ!@M^\__®×ªÅšC‡DåJF¥Pà®Tâ¡TztW*ñP©pW*QܼÉß¹¹àí· ¼xÐ ¾û.ŸO›Fí"ØÏÅ;wPœ½h±%•‡ssãBÓ¦ì(e]èÊB&JlE&ZG èrÁÚµk¹qã½zõbâĉ¨Ìy3E“„‚ ÏÈ>âÕCg¬\%ÉâRÉÕÀ@bƒƒ¹T§±uê~ &ͳp5ƒRMŽwCr¼B°Ûz¾ý5@Ó£ïtî,â¶ÝÜàÑ¢W.;Šâ@anÌ̾Âxý=¦`øê+ ¡¢ŠENïÞ¼¼cÊW_Ee¼Mnzµ¡À—©¹ý‚ÅPÑ_¼‰‰"±¨AagŸ>¼ºk¼ñ;¬ÝÎ_¸0?^¾²IL„c•˜œÞ½ùd×.˜=›·œ)ì *J$âÖñÓ†–-¹R§ƒÝÝ…ç877?ÎÙ‘lÚ÷Ü“ÿÜÓ“” س?„…Uüõ޳@Íxc-¢×Þ=P4‰õþûñúòKZ½ùf¾˜-(lK»[˜g‹Ð}øé§É;¶9 R‘ܵ+©{ö0dôhÛÿÝ•Àµ7èÛŠ™N«åðÉ“N! e¡ÄVd¡uœè²êñ믿rÿý÷s÷ÝwÓ¬Y3^zé%6nÜÈÚµkq) >bcc >}šÞ=ˆd·Vp`9…›J¯§Q|<âã¹ãÄ ³s’kÕ*&ª/Õ­›'º¯`X³Æ/üÂûîƒ_sñ}å ˜5íØú>¬_÷ŽÇ6 ŒnÛ6t#FTŒÁêÕ…ßO…B«mÛ`hÄëV«Wøq…Ç r>;×®…§Ÿ.<6v,¬YÆÿW‰R¡@…ð”Úº¯Ôh¸ôï¿hÞ|³ðÉÆŒÁgÉzÜq®J%j…µBQ!ûG._ÆE¡ O§N6¿ö»~`æ€dý?çëK­à`¾uuuxEŽk×®ñ÷õëy?@‹’Þ¿?óþ÷?ÆU¦ŠÅÚ/¿tص%’Ê&66ÖÑ&85R@—½^ÏÓO?ͨQ£X¹RÔŸ §gÏžlÚ´‰ÑE<#­4Ö$$ <мo×8WZu™wõ)~s_ ¶Ž÷3nÍÌMKOgÚkÓY[¯)ÙFïs]ºàúËOtRi>x$ cÉ¥<¬P”ìÙ5Þ¢·4/o®i^‘×=篛ÖrúZ,ºfÍ Ûyûí¨^~žî!õèÓ»¿°S¡_¬Æ­Ð¾é¸ñ±àñ‚_Æß-Óx¡13û …‚ë/òIj Ú† Ûy績ü¯LžŒ{ ÝâÖx{sÑc%zý:Ÿ)õê>pûíÍŸÏ‹O>‰‹Zc(Báý³G2»qcRj©ÞÑ¢õ6mâûfÍð÷÷/•Ø-i¿¬UnÞ]¶Œ×ï5ÓèÃË —Æy#3“;*¸Ä¢ÁÛ€»ýülš¯Óéøè§ŸÈ´p‡ãƈ<·`Ã+r¼°`—KúA¬RÓ©«×¯ç{¡%IÍD è2²{÷nbbbX°`AÞX= bùòåÅtŒ ÐÁ³>phRA·ÔŽô^òq…f“—oàÊÆõd¦_›nÛhF§sB<¯™e`Æ e®Y{mÍN<4Ѭ Õ;/wéR!×¶eî‘#G¨[·. ‹åq ¢}dDG‹F&/šBbÄH|wïæÁ#ˆ‰‰¡gÏžÅÎ{ÆË‹Yv¸u6þÝwI± >Rû÷Çeûvž{Ür;ïòü-m!&&†äädÞþôSR&N4;çÆˆüòÉ'|ºp¡Åó”ÅÎ3gDX•-ñ¢111$$$ðņ äΞmvNÒˆ<öÜsDo^j°˜ŠxJkgY®]žµÅĉ'pww§yóæÅŽ]¿~ÝâÚRU°×ÚÒ©SùšòØcm±ÅNkvØcm©‰Ø·Š{5âìYQF¡}‘/™°°°¼c‰wHàHpOr§ý¡ö¬ýj­Co?‚¸UzÑ ±Ï±±ùâÔˆ¡{wvœ§-”æýøxÞ¥úìFDD¢•·£×’’Æ"""ðõõ%44´Ô¯»t…:ÁuctJ}©ß?[ÇnÜ}íCBB*m]³eJKK#88˜   ³ó¢££III¡M›6Å^ëêêJRRaFï|Á×®[·ŽÑ£G;Õg£²þ–%]¿~ØØX:vìX®ó•æ;ÅDiþ/( rrròî4”õó·wï^BBBJ|­)Þ911___Ž=JÆ 9}ú4óH]FJ# 'NœÈ¡C‡¨WÄÛ'3\%‰D"‘8š¢ ƒW¯^¥G¬X±ÂA9?2ºŒÔ¯_ŸÜÜ\¢¢¢hÛ¶mÞøÉ“'©_¿~¡¹ò(‘H$‰DR}1Ðe¤eË–7Õa5rêÔ©¼c‰D"‘H$’ê‡Ðe¤OŸ>4iÒ„7æýóÏ?$$$î8Ã$‰D"‘H$•ŠŒ.E©¬Y³†;X#‰D"‘H$IõAz ËÁ°aÃØ¿?]»vE£ÑðÞ{ï±nÝ:›Åó˜1ch֬͛7'<<œ„„„J¶XRÕ‰‹‹Ãßߟ'žxÂѦHœ˜ž={Ò Aš4iB“&Mسg£M’81ôêÕ‹úõëÓºuk’““m’Ä IMMÍ[Sš4iB```¹kiWe¤ÚÄÆÆ‚F£áÙgŸÅ××—ùóç;Ú,‰3räH|||ðôôä³Ï>s´9'¥gÏž,_¾¼Ô A$5ÄÄD:tèÀ’%K5jiiixyy¡.XÞR"1äI“hÙ²%/¿ü²£MqÒí@LeìÔj5 6$##ÃÁIœ™ï¿ÿž–-[Ò¹sgG›"©lÙ²…Ý»w£×ëmŠÄ‰ùúë¯éÞ½;]»våÒ¥KøùùIñ,±Jzz:ëÖ­c¢….±5) ÌsÏ=G§Nؾ};o¾ù¦£Í‘8)7nÜ`éÒ¥ò3"±‰^½z‘””ÄÇL«V­d3‰EΜ9Ãùóç¹ÿþû™4i÷ÜsO‰]%€U«VÑ»woêÖ­ëhS†jîܹsmDU$&&†C‡qóæM4h`vNFF»víbçΨTªbT:vìH=øçŸ¸rå }úô©dË%öÄ`0pîÜ9¶oßξ}ÿßÞGEuÝqÿþ9 Ȱ Xö­ Q49D£¬Ç¡%­¸$RKMQbkMhcB†¤©¦1I•œ5X ‰FQÁF£‚"˜€5™Ê& ¢°È̯ä̫ψ¤0@ü}ÎáèÜwç¾ßÅ{îûùæÎ}ŸÃÃÃFë:t»víBee%d2™ð2X±bþð‡?`úôé(--ÅÕ«Wgªn0éèè@II Ž;†¦¦&øùù­WWW‡={öà£>B{{;üüü •J…ãóæÍCLL âããÑÙÙ‰“'OâñÇ7U7˜‰h4œ={J¥r¹666FëÕÖÖâã?FCC …¨ÞÉ“'ÑÜÜŒ3gÎ`ùò娷od2Ù€cOÃ5·è¥¤¤`íÚµ÷ö21bCrìØ1rqq!€¢££Ö«¯¯'rrr¢ûR)ýæ7¿!Ng´þ_|A#9 €ÌÌÌ}úé§ut:­\¹’ììì(>>žfÍšE666TPP ÔyðÁÉÏÏüüüÈÕÕ•d2¥¤¤˜°'l¤íß¿Ÿ$‰0^š[ÊËËÉÅÅ…ÂÂÂèÉ'Ÿ$¹\N?þ8uww­ÿÁЂ F0r6bccI*• ×"cs QVV™™™Qhh() rww§ÊÊJáøG}D+W®^oÚ´‰6oÞ<Òá3î¹E©T’››ݺuËÑ]œ@ѹsçhÛ¶mtòäIŠŽŽp ÆÅÅQ@@©Õj"":räI$ÊÏÏ'"¢ŽŽ*//'­VK---´jÕ*zî¹çLÕ f"~ø!•——ÓgŸ}6àE®¨¨ˆÐáÇ…²„„òöö&­VkPÿ/ù %''dØl\¸p:DÍÍÍ4oÞ¼ç–|fÏž-\¼*++ÉÜÜœ²³³‰èÛ¹åƒ> ††***¢ððpÚ·oŸ©ºÁLdÛ¶m”——G¹¹¹Î-gÏž%´cÇ""êë룇~˜‚ƒƒ…:mmm4iÒ$º¸ÇXådIDATtéµ¶¶Rpp0UWW›ªÌ†knÑ[¿~=mذa¤Ãó8þ? 4ÈÌÌŒ222DåÓ¦M£Ç{ŒˆˆÚÛÛiîܹäëëKQQQô /P{{»)Âf£ ¤¤dÀ‹\bb"ÙÙÙ‰>ÈÏÏ'tìØ1ƒú‡6˜ÐØË@sË… eee‰ÊƒƒƒiÖ¬YDDÔÙÙI«V­¢Y³fQRR8pàž¿SôC6ØÜ²zõj²²²¢þþ~¡ìý÷ß'T\\,*‹ˆˆ ˜˜Úµk—)Âf£äÿ™[ôÖ®]K/^É0Ç~ÚÇøòË/¡Óé(* Á©S§r¹ÇŽðØSSSƒÀÀ@H$¡,$$D86wî\Q}^Ëz節­‰ÊCBBpäÈ€²³³M{*** Ö°êç–ŠŠ DEE‘˜˜8*1²±ánæ½7ÞxÃdqe¼ Çhjj`| ^½z•¿áÌDššš þ³5uêTØØØc‰1`ð¹åúõëèîî°ØÕÔÔd0VüýýaaaÁs Ñc7þxn1Žè Ñh2™LT.—ËAD<™ˆV«…¥¥¥¨L"‘ÀÜÜZ­v”¢bc‘~<ܹO¯~üðxa·Óh4×! ØÛÛ ×)Æ€ÿÍw^‹xn'Ð#@¿]þ#=¥R L˜0a4Âbc”‡‡”J¥¨¬®®7oÞ„‡‡Ç(EÅÆ"ýx¸s¼TUUÁÁÁööö££ÜÝÝ ®CÍÍ͸~ýºÑmUÙ½‹ç–¡ãzøøø0>õÇÓ›:u*¾úê+QYUU•pŒ1=ýx06^x¬°;ùøø½é1¦§Ÿ?Œž[ŒãzDFFbòäÉ())Ê:::PUU…„„„QŒŒEË–-Ã7PVV&”`âĉ˜?þ(FÆÆšÐÐP„……‰¾€\__¥R‰eË–bdl,JHHÀ•+W R©„²Ó§OÃÞÞ ,ÅÈØX£Ÿ[ …2ž['!"í Æ“k×®áÅ_8pýýýxâ‰'›6m‚««+ ''+V¬ÀºuëwÞy—.]™3gø£³{ÈŸÿügB­V£´´‘‘‘Ëåxøá‡±qãF@?âââpþüy$%%¡±±yyyx÷Ýw‘””4Ê=`¦ÒÚÚ*ü{———C«ÕâdggÃËË PXXˆE‹!&&áááØ»w/d2>ýôS8::ŽZüÌ´rssQVV†––äååaÉ’%ðòòÂüc!áéîîFdd$ -- x饗žžŽôôôÑ Ÿ™Ï-#ƒ·±"­V‹––ÀÌ™3@xÝßß/ÔKJJ‚þþ÷¿ãøñ㈈ˆÀûï¿ÏÉó=ÆÛÛááá€èèh¡üöOÍÍÍqèÐ!¼õÖ[8}ú4\\\pôèQ<òÈ#&—KKKa¬èÿÔ³²²þ>wî\|öÙgسg”J%žzê)¤¤¤ðî£V«…kþ“Í––ܸqC¨cccƒS§NáùçŸÇ›o¾ gggüõ¯Errò¨ÄÌFÏ-#ƒï@3ÆcŒ16¼š1ÆcŒ±!àš1ÆcŒ±!àš1ÆcŒ±!àš1ÆcŒ±!àš1ÆcŒ±!àš1ÆcŒ±!àš1öƒwöìYÑÓØFÚÕ«WqâÄ ÑÞðCuþüyüûßÿƨwáÂ455 ¯‹‹‹ÑØØh²ówÆÌc¦Â 4cl\khhÀêÕ«…§<bÙ²eøòË/…:O=õvìØa²˜NŸ>9sæ ³³ó{·‘ššŠ×_}£˜Z­ÆŒ3DÉèOúSäççÿßmWUU K;Æ=zK–,‘¶cl0œ@3ÆÆ­?þAAA())Á“O>‰Ý»w#%%jµÚà‰[ãÍêÕ«±xñb“œëÍ7ßDpp°ðxßáôÉ'ŸàÙgŸöv`åÊ•¨­­ÅñãÇG¤}Æ?Ê›16.©Õj¬^½³gÏÆD¤MIIÁ¡C‡Œ¾¯­­ MMM½§¯¯ÝÝݘ0a‚¨~GG¬¬¬„º7oÞ„……¬­­lk°˜ÍÍÍagg'”555áòåËðöö†B¡Êò“Ÿ@*•t::::Œ¶éàà Ô€žžÔÔÔ@¡PÀÃÃã;cêêêÂÖ­[ñÎ;ï X§¡¡===˜6mšPÖßßÎÎN8::ÂÌL|/¦³³fffJ¥èééN§1mii [[[¡î­[·PSSGGGøúú=¿Z­Fmm-\]]1yòd¡ÜÞÞO?ý4222ðÈ#|g_clØcŒC[¶l!ôÕW_}gݰ°0JMM¥Å‹“T*%äááAUUUB·ß~›&L˜`ð^ÊÌ̵µnÝ:ŠÚrww§ŠŠ ¡Îþýû ]¿~ˆˆt:¥¦¦’³³3ÑÑ£G) €ÌÍÍÉÅŅК5k„6¢££)99™ˆˆjjj€ÑŸ’’""êéé¡õëדT*% @=ôÕ××ú»ÉËË#©TJ}}}¢r…BAééé4sæL²²²"F*•Šˆˆ._¾Læææ”››+zŸF£!™LFYYY”™™ioBBiµZÚ²e Y[[ ñ‹þMT*EEE‘¹¹9¹ºº’™™EDDˆÎW\\L¨®®nÐ~2ÆØpâ%Œ±q©ººnnn¸«úo½õQ__sçÎÁÉÉ ÙÙÙßëÜÛ·o‡.]º„óçÏC.—¸Æº··?ûÙÏpðàA#** žžŽyóæ¡££mmmhooÇ/~ñ £møùù¡»»[øÑh4X¸p!~ô£ÁÏÏðüóÏ#''¹¹¹¸qãJKKÑÓÓƒU«V Ú—êêjøøøÀÂÂÂàXff&{ì1´´´à_ÿúnݺ…ÄÄD€»»;-ZdÐï}ûöA£Ñ`ùò娏q#233áãã"áÿøàí·ßFzz:¶oߎ+W®@©TÂËË ñññ—/_}õUXYYáÊ•+hmmEWW²²²Dç›:uªÐÆ3N cã’J¥‚··÷]×÷ôôDFF<<<'žx‡þ^çvssÃ+¯¼///„……áç?ÿ9Ž=jPïÆ˜7oT*JJJ0}útß.[¨­­ÅÔ©SaccËå˜5k–ÑóI$X[[ ?úÓŸpêÔ)|òÉ'prrÂÍ›7ñÆo 99ñññ°µµÅŒ3°qãF¢®®nÀ¾TWW Iè&OžŒÍ›7C&“aΜ9HKKCii)Î;X³f Nœ8!Ú-dÇŽX²d &Nœ8èïðå—_ÆÒ¥K±råJÈd2âå—_FMM >ÿüs@ee%¼½½…e5ÖÖÖxôÑGEíLœ8ŽŽŽœ@3ÆLŠhÆØ¸äââ‚k×®ÝuýØØXÑëÐÐP\¼xqÀµÅƒY¸p!$‰ð:""*•Ê ­G}Z­'Nœ€«««Pnaaµk×â™gžApp06oÞ|×Û±íܹ[·nŇ~À×_¾¾>|ñňÇÒ¥K±dÉáû`ÛáÕ××ÃÍÍÍè±;“Uýëšš@LL ¦OŸŽ;w”J%Š‹‹‘œœa¬Zµ »wïÆÁƒ…;¼½½½Ø·oß ñ…††¢±±===Ǫ««ñÞ{ï¡¿¿/^ÄöíÛˆ3fuär9ðÌ3Ï »»III¢6zè!´´´ ¬¬LTž––†þóŸÈÍÍ>EÐét(((¶¼ËÏÏ%ßjµÀÿî`ÀÅ‹!!!ƒö“1ƆÕ(ïÂcß›V«¥W_}•¬­­I"‘——™››“½½=¥¥¥ õÂÂÂè÷¿ÿ½è½wn5GD”––FÈÙÙ™ÌÌÌè…^0ºÝmˆÚ2Öö+¯¼B–––´ÿ~úæ›oÈÒÒ’\\\èþûï'rww§={öõo߯nïÞ½B\ …BôS^^NDDjµš Éd2ò÷÷'rwwôwØÛÛKžžž”““#*W(ôÛßþ–|||ÈÓÓ“¤R)yzzÒÙ³g Ú(--%´lÙ2ƒc:ŽH&“‘¥¥%ýò—¿$"¢¾¾>Z·nI¥R²µµ¥   rpp ¹\NÍÍÍDDäììLöööN>>>äèèhð»_³f ÅÅÅ ÚGÆn¢Ûn0ÆØ8¤ÑhPSS•J///„„„ˆÖ)_ºt vvv¢!:;;ÑØØˆéÓ§‹RQQÿüç?¸ÿþûáíío¾ùNNNËå¶ÕÕÕ…††¡­ÚV©T "L™2]]]8sæ Z[[‚ÀÀ@QŸêëëaee…B›7oø%C___ÑZe•J¥R‰[·naòäÉÿλè[·nENNŽh­ö×_ gggØÚÚ¢¬¬ ===ˆŠŠ2Xÿ |»Ô%<<\´Mß´Z-T*ìììDx¹|ù2ªªªÐÑÑoooÜwß}–þþ~”——£¾¾^^^ˆŒŒ-o¹ví&Mš„ãÇcæÌ™ƒö‘1Ɔ'ÐŒ1vÓh4ðõõE^^æÌ™3¤÷jµZ,Z´ýýý8räÈEh\ff&ŠŠŠøQÞŒ1“ãš1ÆÚÚÚ`aa™Lv×ïÉÌÌÄk¯½­V‹ââbƒ»è#­µµ666ptt4éycŒhÆcßKmm-ÚÚÚ0sæL£O2dŒ±*N cŒ1ÆÞÆŽ1ÆcŒ±!àš1ÆcŒ±!àš1ÆcŒ±!àš1ÆcŒ±!àš1ÆcŒ±!àš1ÆcŒ±!àš1ÆcŒ±!àš1ÆcŒ±!àš1ÆcŒ±!ø/îNå±o‰IEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/random-chunksize-15GB.svg000066400000000000000000003522001231437614300256400ustar00rootroot00000000000000 image/svg+xml Automaticchunksize PyTables-v.3.1.1/doc/source/usersguide/images/read-medium-psyco-nopsyco-comparison.png000066400000000000000000001775051231437614300311110ustar00rootroot00000000000000‰PNG  IHDRÜÐbåxÒsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î<tEXtTitleSVG drawingLÓ§ IDATxœìÝwxÕþÇñ÷„4JC ½+"(MÊ.‚Eš r/*J•+*‚E,ˆŠŠŠ(ø³aTš"$ ” (QDé=”’óûãìnv“MH ›MàózžyvvæÌÌ™ÍòsÎ÷8ÆD2ˆÖ»"""""…H`m°+!KH°+ """"""r.RÀ-"""""" ¸EDDDDDD@·ˆˆˆˆˆˆH(à Ü""""""" €[DDDDDD$p‹ˆˆˆˆˆˆ€n‘PÀ-"""""" ¸EDDDDDD@·ˆˆˆˆˆˆH(à Ü""""""" €[DDDDDD$p‹ˆˆˆˆˆˆ€n‘PÀ-"""""" ¸EDDDDDD@·ˆˆˆˆˆˆH(à Ü""""""" €[DDDDDD$p‹ˆˆˆˆˆˆ€n‘PÀ-"""""" ¸EDDDDDD@·ˆˆˆˆˆˆH(à Ü""""""" €[DDDDDD$p‹ˆˆˆˆˆˆ€n‘PÀ-"""""" ¸EDDDDDD@·ˆˆˆˆˆˆH(à Ü""""""" €[DDDDDD$p‹ˆˆˆˆˆˆ€n‘PÀ-"""""" ¸EDDDDDD@·ˆˆˆˆˆˆH(à Ü""""""" €[DDDDDD$p‹ˆˆˆˆˆˆ€n‘PÀ-"""""" ¸EDDDDDD@·ˆˆˆˆˆˆH(à Ü""""""" €[DDDDDD$p‹ˆä³M›61gÎ&OžÌÂ… Ù»wïŸkÅŠ¬^½:k—µß~ûùóçcŒÉ—ë‰ 6°hÑ¢lË|÷Ýw¬]»6ŸjtöŽ?Îüùóù믿8zô(óçÏçŸþ rÍ Ÿ;w2þ|Ž9rVç‰'...j•s³fÍâ§Ÿ~ò¼?|ø0;wîÌ´>|8Ó±ÉÉÉüøã,Y²$GÿæìÞ½›ùóçsèС<½‡¼¶jÕ*Þ}÷Ý`WCD$kÆ-Z2.±FDòÜÑ£GÍM7ÝdŸ%44ÔLž<ùŒÎy饗šN:åqMIMM5/¼ð‚yýõ×=Û|ðA˜”””<¿^^4h)W®œçýîݻ̀ÌŽ;<ÛjÕªezõêŒê‘ÄÄD˜—_~ÙcÌÆ `Þzë­ ×¬ð™;w®ÌO?ýtVç¹âŠ+Ìå—_žGµÊ™ï¿ÿÞDGG›x¶uëÖ-Ó¿'€¹õÖ[}Ž}÷ÝwM‰%Œã8ÆqS´hQ3mÚ´l¯÷É'ŸÀ¬X±â¬ëþ矚=zœõyüÙ·oŸ‰ŽŽ6ñññ9¿H.Åšàÿ¯¥€-¡ÁòEDÎG·ß~;sæÌáÑG¥[·nÔ¯_ŸåË—óúë¯3jÔ(FŽäZZ{÷îåî»ïfòäÉžmW^y%EŠ!$¤àvŽêÙ³'õë×÷¼÷Ýw™5k“&M b­òV¹rå7n—\rI°«rÞ4hÆä_OS§N1xð`FMéÒ¥=Û׬YC—.]èÓ§Où:uêxÖ/^Lß¾}éß¿?“&MâСCŒ3†»ï¾›¦M›rÙe—¼þÓ§OçÓO? ȹ£££=z4ƒ bíÚµ)R$ ×9S ¸EDòÁÉ“'™7oÝ»wçÁôlïܹ3­Zµâ믿fîܹYÜÆÇÉÕ5szLNËuèÐ:œÕyÎä>r£G¹>æLë”ÕqþÊ–-Ë#}ú°k×®Lå¦NJ›6mˆŒŒ¤J•*Œ;–“'O²aî¼òJžyæZ´hÀK/½Dll,©©©Œ?žAƒ±téR.¿ürŠ+F:uxòÉ'IKKó¹Ö«¯¾J»ví(V¬Mš4á›o¾¡{÷î<÷Üs~ïaÍš5ÄÆÆ²`Á϶””Z´h‘©5Ï}iiiŒ?Þ§îO?ý4:uâá‡öcŒá¥—^¢~ýú+VŒÖ­[û\ËŸU«VKBB7ÜpeÊ”¡zõêžóN›6† I»víøý÷ß}Žß²e Ý»w§L™2”.]šŽ;úKþ /кukŠ+FÛ¶mÙ°aƒÏþÄÄDbccùüóÏ;&=666Ó¹^{í5bcc9~ü¸§~=zô ..ŽvíÚQ¼xqš4iÂG}ÄÉ“':t(111”,Y’zŽógß¾}ž:tèÐèèhÏçššÊc=Fýúõ‰ˆˆ Aƒ~¿{öìáŽ;î ^½zDFFÒ´iSæÍ›çSfÑ¢E´mÛ–¨¨(¢££¹êª«X·nO™&MšðÞ{ïqíµ×RªT)OPšœœÌý÷ßOÆ )Uª½zõbß¾}YÞ“[JJ ÷Þ{/UªT!,,Œ:uê0lØ0Ž=ê)3hÐ OÐýõ×_›åâ=zÚ´i4jÔˆˆˆj׮͘1c8yòäië4yòdºuëF©R¥<ÛÖ¬Y@Ó¦MÛ žÑîÝ»Y°`ýû÷'22Ò³½bÅŠlÙ²…áÇŸöÚüñ;w¦D‰Ô®]›GyÄóoÀòåˉå‹/¾ÈtÜM7ÝÄСC3f o¼ñ±±±<ÿüóž2Ÿ|ò Í›7§X±bT©R…Ûn»ƒúœgΜ94nܘˆˆ*V¬H÷îÝùõ×_}Ê”,Y’Î;ûôÈ)0‚ݧ]K\4†[$n¿ýv˜ºuëšGyÄüòË/Y–Ý»w¯©X±¢©P¡‚¹ÿþû͇~húôécÇ1|ð§\Æ1ÜsæÌ1Žã˜-Z˜W^yÅÌœ9ÓÔ«WÏÄÄĘýû÷{Ê7Î8ŽcºtébfÏžmFŽiÇ1wÞy§Ù³gyî¹ç `úôécÞ~ûmcLæ1ÜîñÒeË–543fÌ0W^y¥Ìk¯½æ¹Ö[o½eÓªU+óæ›ošž={š°°0aî»ï>¿÷òäISºti3dÈ϶%K–xƼ>|س½I“&æ†nð©“1ÆÄÇÇ›¾}ûÀL:Õ|÷ÝwÆ;†;""ÂÔ¨QÃ<óÌ3fòäɦV­Z&22Òüúë¯YþL-Zdcz÷îm^}õUÓ©S'˜®]»šZµj™‰'šñãÇ›ÐÐPÓ¾}{ϱ7n4Å‹75kÖ4?þ¸™;w®éÔ©“ ÷{úòË/À´mÛÖ¼ù曦cÇŽ&"""Û1ÜŸþ¹<÷çöØcÀ9rÄcÌØ±cMTT”‰ŽŽ6÷ÝwŸyî¹çL½zõLñâÅMÛ¶mM›6mÌK/½dú÷ïoóè£fùYìܹӦjÕª¦qãÆ¦W¯^æÅ_4ÆsÝu×™ˆˆÓ§OóÁ˜ñãÇ›ˆˆŸqÅGŽ1Íš53%J”0wÝu—yçwL‡L‘"EÌâÅ‹=Ÿ·ã8æÂ /4“&M2&L0åÊ•3aaafùòåžs¹ëQ·n]sÓM7™GyÄó] 1}úô13fÌ0 64ááá§Ã}Ï=÷˜°°03nÜ8óñÇ›qãÆ™°°0sÝu×yÊxáÞ¼y³™2eŠgyî¹çÌC=dÇ1­Zµòü¾Ü}÷ݦH‘"¦gÏžfΜ9æ™gž1%K–4]ºtɲ.Ƥáwÿº=ù䓞ŸSóæÍM‘"ELƒ ÌG}ä)oóñÇ›×^{Í\uÕU¦iÓ¦fذa>cÁýqá 3—]v™™9s¦gÌø€Œ1Æ$''›²eËfŸ½e˘7ÞxÃ,]ºÔüûßÿ6€™9s¦Y½zµ1Ƙ_|Ѧ}ûöfæÌ™æ•W^1^x¡©W¯ž9~ü¸1ÆþÎ;Žcz÷îmæÌ™c¦M›fêÔ©cJ–,i’’’|®ùꫯÀüý÷ßÙÞ—H€i ·–LKÐ+ ¥@. ¸E --Í<øàƒ¦téÒžäFµk×6Ç7 >eï¾ûn˜•+WúlïСƒ©R¥ŠINN6ÆøÜÇ7•*U2uêÔ1iiižc¶oßnBBB̈#Œ1ÆlذÁžÀÄ­OŸ>¦B… æØ±cf×®]ðIææ/àÌ'Ÿ|â)sìØ1S²dIsíµ×cŒIJJ2‘‘‘ž?ÐݦNj€,nw}jÔ¨áy?vìXS½zu˜Ï?ÿÜcÌ?ÿücÇ1ï¾û®§NÞIÓÜ×ɘ4­xñâfëÖ­žmîàbæÌ™YÖÇp÷ìÙÓ³íÀ&$$Ä>ç4h 7©©©ÆcºvíjÂÂÂ|ꑚšjªW¯nZ·nmŒ1æÐ¡C&<<ÜÜvÛm>×=ztžÜ€yþùç=e>úè#ø|ÎÆS½zuó¯ý+ËÏÂp×®]Ûœ8q³}áÂ…0cÇŽõ)?yòdŸïóC=dBBBÌÏ?ÿìóY–)SÆ <ØcL:uLÆ ÍÉ“'=e’’’L•*UL³fÍ<ÛS®\9ŸrÙ²e>Ÿ™1öónß¾ýiî‹/¾8Ó½?ýôÓfèСžŸgvIÓ:d5jdjÔ¨avïÞmŒ1fýúõ&$$Ä 8Ч¬;‰›wœÑo¼aŸ‡ ÆÓ»wo˜6mÚ˜_|ÑŒ=Ú”/_ÞföìÙÆ˜ôŸo·nÝLxx¸éß¿¿iÛ¶­çg—1hõæþÈø»ûÀÇqÌÆ1Æ 6Ì„……™½{÷zÊ<ôÐC¦X±bžccÆŒ1¶ÇÚ·oŸ)Uª”iÕª•Ϲ׮]kóÄOcìw644Ô§žK—.5ýû÷ÏôÀÒý@Î}ï"A¢€[K¦E]ÊEDò‰ã8<ú裞nžC‡ÅÃsÏ=G‹-˜5k–§ì7ß|Cݺu)Uª¿þú«giÕªÛ·oÏÔ]à—_~áŸþ¡GlÚ´ÉsÌ‘#G¸è¢‹Xºt)€gZ¡Œã>gÍšÅÎ;)Z´hŽï)$$„®]»zÞ-Z”ZµjqàÀÖ®]ˉ'4hÏqƒ>í¹{ôèAbb"[¶ll×Ýo¼‘5j°xñb¾üòKBCCéÖ­[Žë аaCjÔ¨áyïyóæÓû¯ý˳^ºtiªW¯ÎÅ_ìs¾jÕªqòäI’’’HKKcÑ¢E´k׎ƒz~.›7oæòË/gåÊ•$''³nÝ:Nž<É€|®÷ßÿþ7W÷–›ú7iÒÈ<ö½jÕª9ê~Ý®];"""<ïÝC:tèàó½mÔ¨€ç;øã?Ò¢E 6lè9¶téÒìÚµ‹3f°g϶lÙÂàÁƒ ó”)^¼8 `õêÕ>S{µhÑÂ'™X||<€Ïg’£ï]óæÍY¸p!}ûöåã?æèÑ£Œ=š_|ñ´ SRRèÕ«Û¶mã‹/¾ \¹r€M\–––F—.]|>—ÚµkÊ’%K²<çü@åÊ•}¶ßtÓML™2…E‹1tèPž~úi~ÿýwŠ/Έ#0Æx~—-[Fbb"³fÍbéÒ¥¼þúëüöÛo9J&xÛm·ù¼0`ƾûî;ÀŽgOIIaΜ9€mÈyçw¸þú뉊Šò{ÎU«VqèÐ!O÷p÷AåÊ•=ß“fÍšqêÔ)Ú·oÏôéÓÙ¾};mÛ¶eÖ¬Y>ÉÁ~gÁ¹)H”4MD$Ÿ………Ñ¥Kºtéع´È AƒèÑ£¥J•"11‘'NРA¿çغuk¦}î?4Ÿ}öYž}öÙLǸÇnذÇq¨R¥ŠÏþ3ÉîMxx¸Ï¶¢E‹zÆx®_¿°cF½/^œ .¸ ÛswíÚ•°°0.\HÙ²eY½z5=ö{÷îõ̵ýÅ_СCŸ±­9qá…fª3Ø€étªU«æó>44Ô'+4à$þóÏ?$''óÍ7ßdùóüã?HHH2V­³åLÀÖȶþÙ©^½ºÏ{÷w°S§N~Ëoݺ°ßAɺÜßA÷C–ŒŸµ÷¶ÄÄD7nì·Ü† (Y²$ÅŠóÙžñ;ïÏ /¼Àûï¿Ï{ï½GÑ¢Eéܹ3&L8mfø!C†°xñb¾úê+ŸŸµû¾3æȸߟýû÷ã³ýšk®ÉT¶D‰\{íµ¼óÎ;üù矞czõêE¥J•<åúõëǨQ£X¶lY¶÷™WÜçt?pUçw:t(ßÿ=‰‰‰Ì˜1#Ësº¿'cÇŽeìØ±™ö»n×\s /¼ðO=õwÞy'C‡¥Y³fÜ}÷Ý™L¹¶îÏKD¤ PÀ-"’Þ|óM&L˜ÀâÅ‹3­Zµâ±Çã†nà»ï¾£{÷î”,Y’nݺ1mÚ4¿çó¬–,Y€Ù³g{Zl½¹3üFEEaŒáÈ‘#>­‚iii8Ž“«Lħ+ dþ#8%%Å'™”?¥J•¢}ûö,X°€Š+ÆW\Áž={xóÍ7Ù±cß|óÍMùu6S›¹ƒÔœr? ¸çž{=z´ß2eË–å—_~È”4êĉ9ºNƤYîÖÍŒrLçDÆÏ¢dÉ’DEEeÙSÀý`#**ÊïÏÿÔ©S„††z¾—? °÷å8ŽO°Ÿñž*UªDRR)))>û²KçV¼xqÞxã ¦M›ÆÂ… yÿý÷™7o_ý5[¶lÉòÈ„ ˜9s&3fÌÈôÀ¡T©R8ŽÃæÍ›)Q¢D¦c½{ dän%NNNöy¸åîP¦LŸò]t`ŸÝAhÆÔ©S'GŸÇ¶mÛ¨Y³¦ç½û{åÀ8áÇóûï¿óÎ;ïPµjÕlg4pÿ[µ`ÁÏCoÞÿîºë.†ʪU«øàƒ˜;w.7ß|3©©©>½tÜÉç²jU u)É—\r ‰‰‰¼üòË~÷ïÞ½€ºuëP¿~}V¯^M™2e¨X±¢gùöÛoyì±Çü¶â¸»X®Y³Æç˜òåË3qâDO—õzõêçsüÔ©S)Z´(¿üò‹' 5æìæ¾ôÒK É”]ýÛo¿Í”µÚŸ=z°dÉ.\HëÖ­)Z´(;v$--‡~˜¤¤¤l§sÿá~¶÷q6¢¢¢¨T©ñññ>?—Š+òî»ï2iÒ$RSSiÖ¬`3?{ûñdz=ñâÅÌö¦M›òð.r¦~ýú9r„¿ÿþÛç>:Ä<àÎP¯^=Ö®]Krr²Ïñ­[·¦eË–Ô¬Y“°°0¿-°ß}÷µjÕòmþ\z饤¥¥eúŽŸî³LNNfäÈ‘¼ùæ›-Z”ž={2{ölf̘ÁñãÇùþûïý÷öÛo3nÜ8î½÷^¿ÝÖëׯ1†7ú|.Žãð¿ÿýÏÓ…Úw‹òÞ½{=ÛRSS©S§Žß vùòåž¡uêÔ¡|ùò¬X±Â§ÌöíÛùñǹâŠ+²ý<€LÙïÝ]É/¾øb϶¾}ûÎG}ħŸ~Ê€|jeü÷ÄýoÕºuë|> .¸€qãÆñÑGðÊ+¯0bÄÇ¡E‹Lš4‰Ÿ~ú‰ÈÈH.\èS/÷ {ˆˆ›n‘|ФIÚ¶mË“O>É!CX¶l{öìaãÆLž<™ûî»V­Zyî1cưmÛ6ìé^»råJ† Â×_M… 2]£J•*ôë×_|‘W^y…¤¤$’““™0aÏ?ÿ¼gÌkÏž=©Y³&£GötÍNHH`Ê”)Ô©S‡ Mhh( ìܹóŒï»víÚôíÛ—§žzŠ1cưyóf¦L™’iLwVzôèÁ‘#G˜5k–§Õ°B… 4jÔˆ7Þxƒ-Zø´´eäCÚõ@3f ß}÷cÆŒaÇŽ¤¥¥ññÇ3zôh6nÜHDDUªT¡GLœ8‘Ù³g“––ƲeË5jT¶ç®_¿>¡¡¡L™2…ýû÷“’’Âo¼Á—_~™Ow—î¶Ûn£\¹rüç?ÿáÛo¿Åî]»¸å–[xûí·)[¶,#GŽd×®]Ü~ûíüù矤¤¤ðÚk¯±jÕ*®¾új"##6l³fÍb„ ìß¿ŸÝ»w3vìX¾üòËL݉3êÔ©uëÖåŽ;î`É’%¤¥¥1wî\¦L™’íq,[¶Œ‘#Gzºø=zÔ3ƺeË–™ŽùöÛo>Þ3%[Ÿ>}Œ1ö»çΘ~Á˜ÈÈHbîºë.Ï, ÇŽ3:tðü{æþžôîÝÛ;vÌç#FŒ0111>34ˆ²”kÉ´8Ư›X±Àš`WBä\µÿ~ظq#1114oÞ<Ëq¡ýõqqqìܹ“ºuëÒ±cGŸñ?þø#aaa™ÆA®ZµŠõë×ã8-[¶ôŒëôvôèQ¾ÿþ{¶lÙB•*UèÚµ«ÏXÒ'N°bÅ :D§NØ»w/üñíÚµÃq6mÚľ}ûhݺµÏy׬Yƒã8ÄÆÆúl?|ø0¿ýö 4àðáÃT¬X‘?üë®».ÛÏkÓ¦MìØ±ƒ6mÚxî}ÇŽlÚ´‰K.¹Äg<»¿:íØ±ƒ¸¸8Ê”)C›6mˆ‹‹#**Ê'C¶1†¥K—RµjUŸñªÞ<ÈÚµk3]3>>žâÅ‹û|ÆÛ¶mcëÖ­>u›}õêÕ:tˆFeÙ¥7!!+VpÉ%—мys–-[F½zõˆ‰‰áرcÄÇÇÓ AŸž ¬\¹’J•*Ѷm[’““Ù°aƒ§‰‰‰üùçŸ>-“ÉÉɬX±‚ºuëúôX·n©©©4mÚÔoýRRRX¾|95kÖôIÂæ–””Ä?üÀæÍ›©X±"W^y¥g<¿·mÛ¶±bÅ öïßOÓ¦Mý¶ ¯[·ŽŸ~ú‰%JСCO+¹ÛÒ¥K©\¹2µk×Îtljj*Ë—/gË–-´iÓ† *°fÍš5k–m«ò‰'X°`Û¶m£råÊ´hÑÂ'áÚÚµk1ÆÐ¤I~þùgOwf6lèiáMNNæ‡~`ãÆ”)S†víÚå¨ ô°aÃøì³Ïøý÷ß3åXµjkÖ¬¡xñâ´hÑ"S<·„„V­ZÅ\@ûöíO›´pß¾}$$$кuk6nÜH\\µjÕ¢M›6~Çœÿý÷ßT©R…×^{-Sfs°?ëU«VQ­Z5O+tZZ+W®$!!¢E‹Òºuk¿?ÇåË—³aÃÂÃÃiÚ´i¦1éiiiT¯^NøXW IDATÞ½{óôÓOg{_"ÖX{ÚRr^QÀ-þ(à‘|¸gûÔ©S9r$ëׯ÷L%"þýõ×_Ô®]›O?ýÔgZ·‚d„ L:•íÛ·gÊh ,àšk®aëÖ­Ã-Á¦€[2Q–r ˜Ò¥KÓ¸qc|ðA~þùgš4iB\\sçÎåÖ[oõÛò."¾.¼ðB†Îã?^àîÇœŸþ™ÿû¿ÿcܸqùl<öØcÜsÏ= ¶E¤@RÒ4 ¨ÿû¿ÿcøðáÄÅÅñÄOpàÀžzê)^ýõ³šžKä|2aÂŽ=ÊâÅ‹ƒ]$>>žQ£FùS;Ð.\HJJ ?üp¾_[D$'Ô¥\üQ—r‘ÜQ—rÉDM """"""" €[DDDDDD$p‹ˆˆˆˆˆˆ€n‘PÀ-"""""" ¸EDDDDDD@·ˆˆˆˆˆˆH(à ÜùÈqœ¢ŽãDœ¦LxÎã8Žšƒr¡Žã8¹©£ˆˆˆˆˆˆä ÜùÄqœª@"p‡Ÿ}åÇyÕqœ=@²ã8»Ç™á8Nù åJ9Žóp Hrç{ÇqZû9_#Çq~NûÇùÄqœŠ¹1ñKw>p'ø¨àg_ðpðpðp#ðe†–ìg€k—€žØÀûkÇq.õ:_80¨ †—ß;ŽS"ÏoNDDDDDDü:m·d9;Žã žÍ¦H{à `œ1f‚kÛÇqãNÀ|ÇqÚƒ~Ƙw]ç^Œm5¿èí:ö~ PͳÝUîW`=Ðx9ïîNDDDDDD²¢îr§1ð:°è˜U1`¶UÛÛ·®×®×£ÀÇîƘ“ÀÀµŽãumî ,qÛ®r @Ð÷ŒoFDDDDDDrE-ܵheŒYé8N5Œ1ß’\{»ÁõºÞõz °Ýs›ý’Š– WÖñ¯€À ¯]áØ$i¹·w•!›raŽã„cRNWgžy†ñãÇç¸Þ"""""ç¹÷ƒ]<– v% ;܈ã8íÏ¿€ÎÞã°±-åø9̽íOlv“M¹9 ¶öîÝ À+¯¼’e™òåË~Úià ´M›61jÔ(>þøãB/ùåÕW_e×®]<ôÐCÁ®J&Û·ogÈ!¼óÎ;\p¿_ƒœ›9s&‰‰‰L˜0áô… ±Ù³g“À“O>ìªC† ¡gÏžtëÖ-ØUÉdÞ¼y|óÍ7¼ð g}®»ï¾›N:ѳgÏ<¨YÁ5jÔ(ZµjÅ 7ÜpúÂÂîÝ»8p 3gΤ|ùò§? Ÿ=üðÃÔ¨QƒžÕy8@ÿþýyå•W¨R¥JÕ®à9zô(7Þx#/¾ø"5jÔ8ý—_~ɼyóüþùä“Ä TËÛÀlþ¦Ó¹ÛhçÏh N^Uê|¦€»€pçߨh€®Æ˜½Šü´ôshìÓ§?1)®¹¼ËdQnknëõßÿþ7·‡*ÑÑÑ\uÕUDFf×£FÜ.\P ƒ_ý€Ž;R±âÙM=¿lÙ2Ž9R ï3/ÅÇÇó÷ߟó÷™—Š/ÎÅ_\ ?³Í›7—'u{àhРA¼Ï¼4aÂêÕ«wÎßg^ùã?hß¾=Õ«Wj]üyþùç©U«ÖYÿ}:^˜±=G²Ò¶mÛ³¶`w`g'’Â1Æ»ç×´`#1S½¶¿ R°]Ã3kŒyÖUöàiàì˜íîÀ/@kcLš«Œ|‚ Ä?Ç&Rë ËŸk­\ ÚåÈ8~žx~ýÔó>3Ç¡:°xÏql§ãp%ð‹ã0&˜u“ü§€[DDDD¤©RªW·­Øû÷çìclWôᅦ½{Ïîú‘‘ð¿ÿÁ%—ØÖvcÒ÷%%A|<üô“ ̽¯ð ï6·”»/5Õwû?ÿÀ?À¾}þë‘”«WÃúõö+Ø^Tz»‡_€¯bÀDÝçÜ"""""…H±bðúë°s' vúò+WÚà¸V-hÓÊ•ƒ«¯¶íÙˆŠ‚£GᔫÓô+¯@L ´l —^j÷?ð€ ¤±u¸êªÌçyøa¸ðB8q¾‹ƒ  re¸ür{ÎûîK/oŒ=¦\9hÞÜž·L˜>=÷÷’b¯{ò¤ïöÔT»Ý]'ïk»·§eH)öþû”–bƒm·h >¾q×DÇávÇ¡ˆãé8DzŸÇqpÜÛÇ7^s"\Û54¸PÀ-""""RÈ\y¥íÎ={6|òIÖåöíƒk®cÇl@üçŸ0nœméîÙ3sИS«WÛsÄÆÚDj¿ÿwÞ }úÀo¿ÁöíöaÀĉ¶ë{HÜ|3|÷üñGúyÒÒàwàúë¡xqØ´ þýo(Y>ü0ýœ^oW_ ýuöcÔó@òiöïÉ—ZH €[DDDD¤rw-ï=øè£Ìû·mó?mWT”M~¶{÷鯱t©íV½s§ x¿ÿ^{ êÕó=_|¼íÖ½i“í^µ*tè`ƒ`·íþU«lkùGÙiwkïo¿e?͘û\*dÞW®œ}ÍÉ=XÙÓìVc°Ï ¸EDDDD ±I“l+ñwØn×Þ*Wöƒìvà€MüuñÅyWJ•àùçmP¾r%Œaƒõ!CÒËôêe[æß{>ÿÜf¿å–ôý11þë»aƒm­¯\پ߱#s™; <ÜvcÏ©áÃm]&OöÝÞ¹³ÝþÞ{Pºtúöʕӷ·ní{ÌWð,p 8]gñ÷`3—÷q-½öÿíµý‡ Çqm-·'€ž¬ˆˆˆˆˆbî®å:Ù1ÐÞš6…/¾€5k I“ôí|¾?/¼õŒ_~i»¯·li—¸8;_·w]{õ²]Äwî´ÉÎÜc¾Ýõyï=Û-¼¬«8%ºv…jÕìXôèh˜;×wð´4ÛZ~ñÅ6‰[Nµje—ŒjÕò­—[T”í6ïÏ¢E|å8ü üžÍeÏà à÷Œ;á0Ç߯0/›óJ¤n‘BÎݵ<ã4V÷Üc»h_u<ñ„³zÄ9Ò&»ì²¼¹~óæ6ùÙ!0¾M¢öÒK¶›ù5×ø–8mrw²4·±cí4c:Á»ïÂ?ÂþcÏ=b„ÏýÄ6ðîÔ ,€O?µcÃ7m²؃É>nN‰@S xÖUdp+Ø–ó€Z¸EDDDDÎî¬åÞÓn•, Ë—Û$gcÇÚ%:n¼ÑÎ[›Öàì4lhä‘#mk4Ø1âC†ÀSOù–mÓÆ¶ÿóÜpƒï¾êÕáÛom}ûõ³ÛJ•‚W_µ-ã`Ï÷ßo$8\t‘¦Ë_2µüf Ÿ:=€Ÿa»kóhÇa-ð‚íó‹cŒ v¤à‰:uêš‘#G¢ï‡ˆˆˆÈ¹áðaäÖ­kÖ@HMµ­ÑÇŽÙë)â¿ÜEÙVñ7ßÌú\»vÁþý68÷ÓAÛÛR^¢Dö‰ÖòQ`m°+‘Çy ¸Å“GdÎ_já9”,i—@*RÄŽµÎμy°q£,;*øÏFîæ8és|‹TÃ-"""""÷Ö[6ƒøõ×Cß¾pé¥Á®‘Hà©…[DDDDD®Y3¸ür;}ÙСÁ®HþPÀ-"""""™¥)¶…YŒË>‹.Ê~̶ȹH·ˆˆˆˆˆdö8àoš­@cà. O¾Ö¨`kÎM¬æª\1Ã/« ¸EDDDD$kcwò²SÀ^àm /v¶é[‚T¯‚fƒ2¹8âPÀ}®SÀ-"""""Y»haÛh ð4 ¸E²¡€[DDDDDr§,p0ÛÊì^â±c½/Æv;ÁŽ¿¸á\ßã’ØVôo±mÀ€á@U¯cöÏk\×néºVñ¼¼I‘³§iÁDDDDD$wN+±w8pèLÇvª.¼ –ÿÄ&Zû¸P{{X‚ ¶þÜÄÕ€w€¦@¢kÿï@]ìs€ƒØnïí¹¼÷€O\ëó™®õ8`²k}3ðk}ëZI®ûüåÚ—JÑ g?ìw½ösõÚ8ŒÀ¡.ºÖËáð$%pu­_èÚ7‡Æ®õ;qh—Ë;–|¦€[DDDDD²vÔþæý±¡d?W™å@ð0 x À¹Ê Ĭó½Î½ø¸Õõ~ð&6È^Ì–‡€I®2ÿqw#ð¹ëü+Ø®î¹1Ï«>K€¹®õ5À«®õ­Ø®ó`[ñŸ"=à~ û™¤™áìÑ@QÀø¹zuà¿®õšÀ½®õ2Øþ%°=’ï*»öÝ 4t­ß ´:Ý-Jp©K¹ˆˆˆˆˆd­¥Ÿm¶køx×{wRµç(lx °Ë똫±¡ä, »kÛll`_×ûÅØ®èý¼Ž«üàºÆaW™G€úêx3¶Uý%ʉñZ/´ZHPŒ¯¢ˆˆˆˆˆL《®õàBl—nï1Õ°Að£ÀWØ.å=°-áí]e±õ là\|ws•X®Ó¾š»^ã]¯ ý”iˆ7üwl|°ÒPïâó’~è"""""’µÞÀí®e Ð ß`Ûm¶[÷ àRl÷òØVo·Øñß`ƒëuøf9?‰ MOÇ_™×ë98Þ­2éʃk¤´=GM×z Òø×z(6’ªž‘ÛN¦ZìÈõÃÀo~®~Ûal'õM®õd×ú)ìnÂŽØBúxð?°Ü¥S ·ˆˆˆˆˆœÍØqÔ}€Á®e6ÙÙKÀ0W¹&Ø®æÛ°I׺{§66DÍh6Âõ>û À[6€.Ÿ‹z?ëµ>Êk½·×ù¯À>¨‘¡~Þë¡"Ågîj^ë%Él†á¾Ç~2`HÄ·Ã|úº¡×úM~Î)ŒZ¸EDDDDäìÄa[ªgzm+mÞ«’¡ì@ì”_s±z˜×¾¾À/À뤧Û¼èz üÛõþ{¯ã`3Ž_yö·"’—p‹ˆˆˆˆÈÙ¹¸ ¸ÃõÚÛ¾{x8CÙ~ØŽÒ¿’žÜíVl‹÷` 6ÑZS 0ÑUæì˜ï6@k×þ«°‰Ú^γ;ÉêR.""""yk6qUNE`»KÁÒ;.»Üé b†‹±ã¶W`Gß… Äkg([”ÄËÞB€Ï°S-ÅN=ö˜ë\Å\ej>eØØÎÚ]!ÓLØù)Š_ØïÓ^:{V)0p‹ˆˆˆHÞúÛm8§ªaÓ?IÁÒεäT$0ĵdg° ˜’M™›\KVŠkôsÁ±»µÁ®†, ¸EDDD¤pI!÷¹™+ž¾ˆØzlb³·€âØy¼EÎq ¸EDDD¤pù;^77LÛß–åâ<ÍÛrym±Öcƒì €±A·È9N·ˆˆˆˆœ¿–¯ä¢üApŸ©¾Ø)¶.й."ùD·ˆˆˆÈ¹"¸9—Çø›ó8Úa“`…b®Ä|º®!@õ`WB$)à9W6»YXêz=üÌŠˆˆäÜ""""’·øÙæ>Žº"ðO†ýÛ]KNlð³­äZï‚#|&J`[a÷QÀnàОKDÎ{ ¸EDDD$ol*cƒÖŒB°w(ЀÌ÷PàÓ³¸önÒúïÏâö7í° ¶¢€•@/ vj)oÍ€Õ®õ2®ã¥à™ | ü$a»•OjD¤PÀ-"""ç¾MÀÿrQ>ÿ÷vlÀÕø è‹m•.|„mÑ®‹:j6éYKÒîZøÜi¤ۿ梎’¿R€+€QØÖîŠÁ­ŽˆÃ-"""âOŠëõkà×ú* ?6P¾;Oö/@m <6ÈvH².óZ¿8ൖ@¹x è”Öó9ÕEä¼¥€[DDD$£T kýð2p <'€JÀ…¤Ó­€\ë—¹Ö‘ž,Ms8^û°­Û—%¦Ø¡""ÙP—ò|ä8Î(àGcÌR?ûÊý°ÏÇ×ßc2å*u§!¶#[(ö9úbcLªŸr]°ÿ%–cÖä彈ˆˆze°Ás6Öa¯}öãØ@ú°;Þº*6ÈnLzëõm@oÒÇ_?Dz³F5×"…[$vhBg`,¶[yÙ ÖHD ÜùÄqœ‘À³ØK3ì‹Á>#-ÄÀ¿ÇébŒùË«\Wà3l¦õÀÃÀ§Žãô5Æ${•›ŒÁŽ4; Lqç^cÌ3¼E‘Â¥4éYÁ;cb¹…`»”ÿ´Æfÿp·ÂŽÕxq­÷Ép¼?ÿÆŽýΩ¹(+U›}Þ-›Œ¯Fpª#"…ƒîs'›·4»\§/b'ù—1æ'Çqªßc;°ýÛuž®÷_ÿ5Æìt§3ö¿í»g\åš÷€‰Æ˜Žã< <í8Îbc̸O‘BÇ{Š®L}Ï€ØÖëÖÀ`l4€YØq°ã¸sãB×"…S<0Ûõº;¬`kPk$"œÆpã8€uØ?Ó²(s)pðœ1æ'cÌ6lp}•ã8î禷c;±1Æìt•ûX Üî8Žã*÷(° oŒ9áÚö¶ÓÜy{‡"""…ÄF?ÛJ{­Wð³p­k}4p“k=ÌOY9?$bìnÀ<|3Ì‹ˆø¡€;°*bÿi¾ ÛÜŸË]¯ 3lÿûóiîUn»1&㟠_cS°¸Gµ¾5Ƥ¹ cŽc[Ì[žÁ=ˆˆˆ^Ç\¯»üì ªåHïî­ê.,¾zc÷=ü ÛÍüp¶GˆÈyNw`­7Æ\ušnÜîŽe{2lw¿ñ*—±ŒO9Çq¢°Ïë³*ãg»ˆˆÈ¹i6YÙ?@{?û÷`³ìÁNí%’ïC±ãù£HÏ@/"â‡Æp1ÆËg.¹ IDATä X)ìä#Ÿîw½Vð*·ÍÏñÞåÜë²(í8N˜1&ÅÏ~¿n¿ýö,÷5ŠºuëæôT"""—¼ \ \ŠmÁþ¾ ÍDÎÆû®×ë§Hï‹(RÈ8Žs 6 ¤?—cçk³¤€;ø’°ÙB±ù.Ý"\¯‡½Ê…û9Þ»\’k=«rÇrlÎðþxPj!’Šá›Í›¿xB΀îàûÇõz°Ûk{´ëu«W¹ê~Ž÷”3Ætç¸×¶Œås[¹9sæäö‘üw?ö±ó4`2Лn´-Ð XBî2™Dœ¾ˆœ§öc[¹Wa“¦ýŒ&®zë$rŒ1Óéþö9ŽópKþÖèܤ€;øþv½VÃ7àv§iIô*w¹ã8¡Æ˜SÊ%cöx•«æç:58ƒ€[DD¤À: |\…mž;¶öJ줚#€EÀØäVê)yáð8ÐèçzU–É‚’¦ßþŸ½;—r~ÿ8þú´I”$•"¥Ò&!"K©d+[‹„”} YãûµoÙR_’/áG–’5”HYŠ•B+m´j¡’:ç|~\3ß™s:gÎ953÷Ìœ÷óñ˜ÇÌÜsÏ=×á4ç¾îÏçs]vÊÐ5ßö³°k¨á2.obë¸;äÛïL`JÔó7ŽÎ¹Êá ι:XiÉñ [DD$`§c£‹'c£Ú7„^{[˜ TBɶÄÏÀRìŒëfìbZʼnH!”pÌ{¿x¸Ö9w‰s®®sîZ /Ðß{¿1´ßx`ðŒs®ƒs®sî9¬zù5Q‡ºÓ9w°sîpìOÂàñ$ýX"""‰±h‡•=k†ycèµAÀ'Àt !p/šË'‰ñ3ð$p!Лe!"RýJ ·»b+ÏÊcUËŸòÞ¿”o¿S±f‡ž¯.óÞÏ ïà½_îœk¼Ì mžôôÞoMÜ ""’@‹ @u¬·öMÀ(ì2ó¡ØÔñ°ËËõŠQJW€W±éäa³)DD  „;I¼÷‹)dB[(¾Æ`ÕI¾Â §‰-ë?8«V®ò´"’n †6eñD’ìÁX{¯±X{¯7(FÉP‘$û kQ÷Øò‡«°‹G""Q4¥\DD$S­>,á{Î&yÓd/Çb ·÷:k|Ù¸ø›RÞ%Iñˆ”ıÀŸ@Å ‘T¦„[DD$S-ÂÖ<—Ä: JüCùŸåØÈõE@¬ÚÕXòÒ+Š6x ÍÓÔVë ÿ V=ÿk ðHA‰HªÑŸ2I¼ÜÐý/À¥ÀwXUçs±$;[Ç}-vv¢3Iý€“°YÕ°‚""QôçLDDDk-И´ºa‰5À@, Ÿ†C»Húx ›V>Žýn‹ˆDÑŸ4IŒï@U 1ÖÞ ¬½×wب`m`1”ô´7°ø¸«7ðU ‰HŠÑn‘Ò¤– 4f'ð³þº‹µ÷j¼œ…U"¯z}Æ ’HÛ°ßã\ 9Vøo·@#‘£„[DD¤4Ùõx~Žÿ*ðð 6Ýö`¬RúIXq´G°„û²|¶H²•Ç–C4v 8IIšR.""RšD&ïÇãN Ý7FB¯ÄúkgwSò6e"©®ð6“ã.à‚@£‘£„[DD¤49Ø8ë3°ö^S€–X»¯þ@–€´6` ~å8}¦Hªx[ËÝë#¿6Õ\DM))]&Æé8¹ØèõeX’Ý«<þ ÖÞ«ð2Ðx-NŸ)’Š:b›£3kÙŽ¾DDD$bP)Æë¹ÀþÀ¾X»¯k€/°Ê ‹€IØúm‘L·wè¶»àô Ð8?È D$U(ᑈŽE¼ž´>ÆÖiz·ËCû–¨ERPàu¬Bù¡@Ý@£‘¢„[DD$S•ÇÖk¯þ =ßH5eü­µ.®]°iáö^ý›€3€q‰Z$ýÜü h ” 8I)*š&""’©š+°V`s°êÉ›÷BÛWb£Ó%Q[«}°¸ ŽZ"Iév¶„bð6p+ð{ ‰HŠPÂ-""’©V§`-ºa‰ño@«¨}vd4îߨhù,l½÷‰;¦HÚ[…Õ5¨ \ˆ­ã^d@"’*”p‹ˆˆdªmX‹¢“±5Õ¯‡ž¥^Ôãüm¼¶b½¼Bë´EªuÀ'Xn)õ´†[DD$üŠUú.‰')ü/¹ê#€û€AÀã@·b÷—¨Çæ{-<"®Kö"y…«’/ÁF¸WWŽˆ¤%Ü"""©`%ðß¾g0…ÿ%|Üt†`I¸+ÆqË`í¿ÀFîVF½¦‚P"‰\TŽG ·ˆèú´ˆˆHF:Øè ´Ä ¥'Ù&ôž]€X‹#)Zs¬UÞlýö;Á†#"©A ·ˆˆH¦YƒõÇþ?`!ÐŽ’UL~k#6ïàD2TSlZy#l‰ÈHlj¹ˆ”jšR.""’ªšB=°¬˜ï;¨ŠM'ïŒM/‘Ä»»`µ+PxÖ2LDJ-%Ü"""©ê/`ièqAk§Gcí¹ò»›B~6P[GzÖ¶HD§ ÐK²ë‹ˆ¤%Ü"""©jqÔc_ÀëãË€Ø4W%Ü"‰Õ'tï¹À·Ø…¯]‹HD¦„[DD$UÕ–‡7ÆzüÆ[àïì_ÜÂk"¥Ñ 7ð=ÖRoo¬_}ã ƒ‘ )áIU«B÷•H\¢[¨ c‹”6{a öØ´òºF#")@ ·ˆˆHªÊ ÝofˆˆËnÀ£¡Ç¹Ø¬”µX!C)•ÔLDD$D€7 , ìuøw°áˆH°”p‹ˆˆd‚èiá?…ˆì  ¼„uø,ØpD$XšR.""’ ²€@¬ØflùÁ¼Æú-`[Ôó}¡Çå€ì„E*"±t ÝÀ–…Ìöj‘ˆH ·ˆˆH*Øü¬ç±`CÔó:ØÉý¬ð ˆQDŠç&àK`:Öà1àú@#‘€hJ¹ˆˆH*8¡î\º/‰IÀ¯X1%Û"ÁÊ:£(Ù)Å4Â-""´‹°Bgý€‡€ËeF$";㱨ÇÛ€°å!"Rêh„[DD$h-¦À»À¨H:û¸hU+o¬ 4" ˆF¸EDD‚’ ü…˜ŸÜt¾Ç’ïXö¡àbj…)ɾ"²sr±Y*§w‡Õ‚ HD‚¢„[DD$(£€k°òË€AÀ-X2]”¹‰ KDvÒÀ;QÏ7c-ÂꎈGSÊEDD‚r60ø¶¾s<ÅK¶E$õ.Á¦•÷ 6 †n‘ |üÜ€U'?ø&ЈD$ž~Ö`ËDÆb3ZD¤ÔÑ”r‘dËÆ¦’¯îŪ” 4"‰·B·°ØÔòŠÁ„#"ÁзˆˆH²•f`I÷ X•òßHDâ-x¸[2Rx;ЈD$ቷ5À1…¼¶骊]öÞXüœÐD$IÊÏc…Òz­B7)U”p‹ˆˆÄ[6EWÿ#ßóm ŠED‚35ßs]X)u”p‹ˆˆˆˆ$Â2` Vñk` °(dP"’LZÃ-""""’³€Ç±,—ÐÙ·H)£n‘d8˜¬ÆF·¶Žˆ$ÁÉØ¿ûh¹A""AQÂ-""’ _ak»v 2Iª™ÀçØ´òo°‹oÃHD’H ·ˆˆH2äD=Vá$‘Òã`p$Ö ðø`ÑäRÂ-""’ Ç_„·ÃÖrŠHæ{ ­Û)Å”p‹ˆˆÄÓ@Ù¶OÅNº6½TDJ‡\lIIxJù×À+ÀQA%"É¢„[DD¤ °}±£XÖ'u°i£ùE÷ÙÎ)àuÉLÿ`³ZG·õHD’H ·ˆˆHA>z–`ÿ2À à"¬ Ò׉JDÒÎnÀT,Q¤”ÒŠ‘x(ôǦŒ?p,"’Z<¶”äQ ;p\°áˆHòh„[DD$ö^.ÀÚþüØ·﯑ˆ D$%¼ \4Ū•Ÿl8"’à¼Ðãg±a‘xz¨t& ¡/‘R@ÿÌED¤ôš¼܆U>zþ.ð)п˜ÇRK0)JO¬=ØÁè,\¤”Ð?uÉ,ý(þèôJà= pð4pЫB®Jä"O„nˈL+?èdP"’HJ¸ED$³< üS‚ý/.ÃÖVö6—ˆØLšé@¥ÐãCƒ GDK ·ˆˆ”n½€…Xâý)pq ÑˆH¦ì4BÕ”DJ%Ü""Rº9àe௠‘R¡mè~ ð-ðÖ†°|P‰H")á‘ÌV( lÖ9ù^ŸMäXD$Ñfbk¶W•ð連A%"‰¢„[DD2Ûf`Uèq-à÷|¯7Kn8"RÊÕ‡ P‡‘ §„;Å8ç²¼÷[‹ØÇe½÷ÙEìWÈñÞûxÆ("’Vþˆzœ?ÙI¶ÝsCÁ*•; [`‰H©TC pÎíæœäœ[ üãœ[Ã9W>ß~{:çF9ç¾pε.àx-œs_[€?œso9çª'å‡I5Ñß’•‹BD$â!`/ >p9ðq°áˆHâ(áN ÿ®^NÆbå3Ì·ß   ðp&Öøf‚s®exçÜ.À(lÕb?ìkü0`²s®bB ‘T´;©­Ô8€c€¡À|`-ðL°áˆHâhJyÀB òùÀïýu¡Íãœs+œs·xïsœsí€ Þû×Cï,nÄÛÜÔñÞ/í7ø›À4<9?™ˆHŠ˜õx}`QˆˆD´ Ý{àgàl6N£À"‘Ñwð²B÷ËómŸ‡ýÿ ¿ÞkZónxïý?À›@Wç\x¿À'ád;´ßtìë<œ”‹ˆd¦ß˜U0DDRÄ)ØÌ›&ØÐÉÁ†#"‰¡„;`Þû¿€q@/ç\[ç\ç\¬tÆïýß¡][KBIv´yXR^Ã9WhHÞñœèýöKÈ!"’*ª£¿l"’Nž~Å–¾¨hšHFÒ”òÔÐ x˜„5°©Œ.ŠÚ§*°²€÷†ëïÖ¶au.ÿ(d¿¯^\±bE¡¯U­Z•¬¬¬B_Iš\¬Æ•Øâ™éإʢ¦hª¤¤ˆ!¼0˜º[øî"ñ末 ìZÈË…m—RÂ0ç\lÝuà3`X†M7Ÿ<uÈw±Àι÷±ñž“Þû™Éú¹DDj3ð3p¶^»0»´("’.úF=Þ†ÕHÁÕxD$-i„;xïoÚŸbSÉÿ8Ú{G¾ýÂF´g`Kî:yïs£öÉ:WbíÀÎþ•àCD$9¾öÁú:ƒ•ž|hˆŠ‰Hzù¸h]0lL 4"‰3p§ïýgØécQûM&±O.ð|è&"’9í€K€ÁÀ«ÀýØ‘t³+¶4æTàn,á®hD"gJ¸ED$=äõO€3ùغGÍß‘t6*êñ?ÀO@“€b‘¸Ó”rI}‹æÀרò¯±J…uI3«±š{„îsc¾CDÒˆnI}µ€£°j¯_w’ˆH\¬–g¿¡3t‘ ¢)å""’ºr€'€«€á@cà¬}ÎuÆ%"%²d ì·_äyv6¬^ 5S¬ÐáÖ­°v-Ô¨Ù¶f T¬h·„hº…ý ¬ª%èóD$©týLDDR×à! #Ösáàm }A‰HI Ãĉö<;zö„£Ž‚E‹ -­[¡[78æX¶ fÌ€>€ví sgؼ9þp9ЛV~g?KD’J#Ü""’šÖ`SÇ¿ºé¹Ý9È D¤$„[oµÇ;Bưj•"´m Ÿ~ uë`H8Ù3Æž7heÊØöìlÛÖ¹3¼÷^‚Fº'aßy=±ïºÃð"p‹ˆHêù ¨ ¼Ô&cEÓÆ“ˆ”ÈÓOG’m€Ü\˜3'’l,^lI÷úõI/Þ½#É6À–-6¢N¶ÁFèÏ>;A ÅF¹oƦ—; ;ÖD$](á‘ÔÓë¯}1p Ö«ömlJ¹ˆ¤…ÓNƒ:uŠÞï ¡J•¼ÛfÍ‚Àûȶ7Þ€^ˆkˆÿs饕{Ÿ²eáÊ+óùlž.Z•±Ù="’ö4¥\DD’g%Ð;ÆëÛ€9@# 8ø/P¸/áщHÕ® Ï> 'Tø>ÁgÀ5ר”ó`ÁhßÞŠªÍž W]S¦ØôôÜ\KÂûöÃ{5 zôçlÛÆ0i’M/Žvíìs¼ð}Î;¯øÇÛ!ðÂG‡&ð³D$i”p‹ˆHòü Œ/Æ~¿G=ž4KL8"’XÕŠ¨´]­š­‹~â »o‰íêÕöúØ-ÚÅÛ}ß¾–l_u f öàÁ°iœ~:|õ ž79%º2ù޼¾SvæÛ¶-Ÿ'"I£)å""’Úê¢ö8";hÉ’¼Ï½‡¥K“óÙÛ¶Ù´ðX¦Nµ©â`£Ý'F’íÂxC‡ÚñÃÉ6À3ÏXA³½÷†/¿´ý.¾ž¾èXW¬ˆ=º>þâÅEk‡Í†ça3|D$í)áÉ@Ï?oÕ¶Ã mx4ø°Ãlt"½ü²µ›0!ö~99ðÃöøÄáÁcOA¨T ¦O·Ññp²]ïm}ö‚±÷ëÓÇ’îXÖ­ƒsΉ½ÏN™< äWÏ%ð³D$i”p‹ˆH°Z šh¡“Hœ<ÿ¼înÝ Ý»[;®š5-A]½ÚÖH'*é~ë-8ÿ|X¸jÕŠ½oùòк5T® :ÙìK/ýž ,‘þóÏâMæ8ðÀØûÜv[dýwajÔH\Ñ6®æ¯×bß"’ö”p‹ˆH°¾Ç¦R.ö8‘ ðÖ[–l‡+|{óçÃÊ•‘}ÂIw"¦—ŸrŠ%Ñ¿ÿIvo¸–/‡ï¿‡°ªào¼ak­ÿøÃn°ç±,Zd÷ÕªÁ“OÆÞ·Fâ%åC†ä­ˆÞ§üö¼óì¶›ç½÷à ƒŠ>ÖN™ ÎÅ.D>àÏ‘„SÂ-""ÁÚõø×À¢ÉmÛBóæEï×µ«U· àÝw-©Øc~ôQK\[¶„Ï?·¾×]ºØ>eËB¹r–€ûøëÖÙ}:V -–-[ò&Ò…<8oí´XëÖ…³Î²‘ø,ú8;í¬3CYà Q}¿E$i”p‹ˆH°ÚG=Þ/°(D2FÕªðæ›±÷©TÉ 5º8.´u×Ñ#èÕ«[uñ .€/¾°^ÛÑj׎ŒhGûë/kýU”#´õè§Ÿ{¿õëm¿œœØûÕ®mÉþر6eýé§áÎ;áC`Ä› 0v¬McO¨/€¹Ààj¬E¢ˆ¤5%Ü""¬Ÿ€ê@eàŸ€cÉŸ|9.Ìž{F’í­[¡woK87o.xÿÜÜí_Û¼Ù¦wy¤µóêÒþþ;òzãÆðâ‹V9¼¸úô±V^áØªU³d½I“È>½zY_îêÕ‹ž~ðÜs¶Ž½8‰üÉ'Ü96 ß¡ƒmsڴdz‘ø„Ê>zõ"й‰HjSÂ-""ÉSÐÉür`°!ôXDv˜÷pË-ðóϱ÷[¶,R½ü£à¥— [·í{^ƒ%ª_l#Ò7Ú¶íù=÷DF™¿ùÆŠ¥g w,}ûZÒ}Øa0w®MGŸ=®¸Â’í_´Ä·sç¼É9Ø4ð*U"ϳ²ì‚Â[oÙˆu´iÓlúýìÙy·— {¬µ [´&O†+¯´õÜ µè¼ ìÜ ìšàÏ‘„RÂ-""É31èD2Ot‚ë\ÑmµÂïéÕËútmÛ*V´‚gaë×ÛÈpûö–ôNžlë²Ï8Ãî'O¶í99б£½ç£l„xgõík=º«Vü\C‡Ú…èQæpr^¶¬d/_k×ZrN¶š6µQësαm99V ý³Ï E ¸é¦í׃—)×]û%s©K#à/`:ð,p6ûGDÒ–nI¼\`pZЈdïm´·iSX³&²ýî»aÿ"*þ;g-ÂjÔ°âe`Ó©£Gpß}×’ñO?l[³Æ¶GÞ‹/Zû¯“O¶$¼qãþÑ+¢–?æ2œ¹öíkI~¸y89¿øb{Þ¸±ÄŸ>Œ­ZÁODFëss­ [¿~EÇ´m›U/OlÁzrÄŠ¦õIàç‰HÂeLÇSçÜž@;¬kau °XLðÞÏ 0<‘Òí¬ÐhଠØ{ÀùØ7v,Ý)ÐçŸGŠ‘Ý~»%Ð`£½Ÿ~jk.´õÙ>6Ø´ñ´Qá¾}-q>ã K¢»uË{üð”ó¢8Ço믃’¿Ïv8énÜØúŸ~d ú?@ÿþÛc= >vNLš#GÂÛoÛHù„ ñ?Xk°æÀáÀq ü,I8çwv¡MÀœscMzåC›·`c){b‰7Xâýð´÷~c²ãL3‡ t&µaƒ%ÛºÕ’Ù ¡gOxõÕ¸ýHq÷è£6e¼(:Áûïo?²~ç6BV¶¬rï³O|ãüŸ ØÙë. :¾$RK`FÐAăsn8ÐÛ{_¾È%¦´RîœÛÝ97[åÒ¸hìã½ßÕ{_Ë{¿+P 8ƒ«üêœëTÜ""¥Ê€;*ÀÇÀÁØhÍR”l‹”À–-pß}y?°žÕåË[rýÀy_+[ÖF·£‹ŠU­º}² vŒòQ§Õ›6Ù¨ð^{Ùèu,ÑI~*ºñÆ¢GßëÔwÞÙ>Ù+ζ&¼sg›B_Øhx\T6〻±‹•ï$ðóD$¡ÒyJùWÀ ‰÷~na;yïÿÀ’í1ι€nÀýιêÞû'’ªˆH)Õ[ƒ¸›cô.V}·NA‰ØúÝJ•ŠÞ–*úô±)ÍYYVì¬~}ÛÞ¨‘Þ:·Þ¿Ï«U˦?ü0zhì}Ÿ~Ú*x'4 ÝIÝ»[·Âtì*üÚá‡Ãk¯ÙõÊÉZâÒX‹]¤·}{ IDATlÔJÒçŠHÜ¥í7ÐÓ{ß5V²Ÿ÷~«÷þ 16©QDDa,0 è„ÿ ô ½Ö;¨ D̘1P¯|ýudÛ]wY1­Iîy¼dIÞç[·Ã7ZR½u+ÜpCÞ×î¿ßF¿ѲªgOX¼8ö>‹o¿þ;Õ¼SÄñĉ‘Šæ9çœ$&Û“±jåßC#’øÙ"Wi›p{ïgíÄ{·•$Q‘ |‰•²üÈTB6fŒ%‡«W[ŸåŽ¡Y3«ì=g´k—¼¤û®»¬ÂøäÉö|ëV‹­M¸ürX¹2²ïa‡E ¤½û®LK†{î):Ѭ\yû©î©ä?ÿÿþ7ö>‹ÙûœœâsÃ+>—0õ°î÷€Û±Ù ü<I˜´M¸ósΕqÎàœ«z~sn„sî}­ÙI’_±`ÿt:bë›/bI·H@ÆŽµ¤*<’¹m›U›ŽNœÂIwtÛ«D¸ë.Kò7n„¶m¡I“H‹®_~±ñÚkó¾ç¬'ô“OZRž G ãÇ[R]¦Œûê+»/Sƶoû¥ª† a—b kÒ$oïü6o†×_·5ðûìcëãæc Ð›%t¶®[DÒN:¯áÎï9àB µsnV^¢° 8Å9—ë½`|""™-…iŒ­Ó~+[y6µ8ï>õêA‹РAÞíeÊÀ®»Zá­X#Ü[·Â¿þ;ŽÉ“í‚‚¤¶%K"Uæã® ö=º x¸[@)"i%#F¸½÷ß:çÞF…6Í^ U,ÿ ›^~jPñ‰ˆd¤uÀûÀ‡Àx PHf¯ZÉ([¶X¯épEé lun®«ºóÎȾ‡n½ó'¥‡n÷ÞÃ÷ßGF$ëÕƒ~ý¬HÖ¼y±ã0jÖ„—^*øõ§ž*xûÍ7Û-¿#Ž€¥K­`W­Z¶^ø‹/bÇ gž ¿þ •´,#¥¬Zekñ_{ ¾üÒ~·,HÀmÆ~ª­€3ð9"’P™2 p¶z°7p´÷~½÷~#V4íïý'F'"’I>öÁúkï ,zaÓËEJ`Þ<8äØ}wkÝV¹²­Å†íëðñÅ‹ó¶ðjÕÊ^»ì2KÖ£ýç?VYú˜cìù 'ØèäèѶf,.ɺÞâÈÊ²ŠæõëÛøM7ÙgDzË.6z¯d;õ¼ò \}µ³óÞ*ÇÏHD™¬òÀP¬åZà#¬z¹ˆ¤•´M¸s79çŽvΕðÞçxïß÷Þ¿ä½ÿ#¼Ÿ÷þfïý…IDDJd:иўÔÃúÆŠÄ0{6\|±B/\Ù^³¦µÔÊζQéháëo¿Ý~{Ù²Vý{eT½€Úµ-9úéí§‚ƒ%°ãÆÙˆùøñV«kWKžž|2þÉvA*V„÷Þ‹$Ý·ÝfSØ¿ùÆâßekc•¿À—¤†îÝm&F:Öî»ïì‚QBœu}XŒÍã¼7AŸ#" “ÎSÊî6;ç>Æ&4Ž÷Þ/ 6,‘ ö7Öôä l^Ñ`00Ž4¾„+ÑÂÃÂ6o¶é²%¯…Y¼>ú¦O·VYeËÚöM›¬‡5X’R¿¾=Þ}wëEòÞoô޿罿Ê{ ÐëXØxXëœ뜻Æ9×0Ð`EDÒÝ À!ØzíïvÀ4àü ƒ’xéß¶5©]{ì{î 'Zñ²Nl v´›n²)Ý=zXòNž!ob]¡4kfó'Öç?o¾™w{íÚ6â®öX’Ê~ü>ø ®œ ˆ%ݯ å;"i&­îü¼÷ ¼÷C½÷±‰7YX[°¹Î¹Ý PD$Mn²°©ä§aÉöw@C lp¡I|<ù¤%ÛÑþú+oÿè àÔSó( ·ÚÚ¼~ú)²½iÓÈèsþÄú´Ól ìYgåÝ~â‰pÝu¶¦Z$üú+Üsý¾7kfª¢/<ÅM쬶!p=ðM>CD&×pÿsîà,àïý÷Þû­ÀÄÐíç\Mlõ¡ˆˆ”D.ð_à7l ùóÀ¡Ø\"É^/¾Xtßgçlä;,\Ð ì½Í›Ûãr嬈ÔêÕ°×^yq¯Š>I†øôÓ¼µV­²Gç:«N~Ö&LDÒJ¦Œpga×ÿ¾sÎ-vÎýÇ9w‚sî¼÷˽÷¹Á…("’fæßb%*?ê‘w6WjW”1*U‚^(z¿ìlø!ªïG£FVð `þü¼û~ö™m»ýöøÅ)’JÎ<Ój ´l =‹% Ù«™Ñؘ ¼¬JÀçˆHBdDÂí½ÿ+ Ö x›ðø °Ò9÷’sî,M')¡غí @ `2°ØdP²£²³-)ز%²mõj2Äçó’tô(¸sV mÕ*xà¼û•Wa'ÉpUªX’ýý÷póͰß~ ú  @G`Oà l‰Ïü˜ï‘’SÊáSÈ? Ý®qÎ5Núb%}f`ÍlDD$– @Eà 8x8˜`\²Ã²³­öoXEñ'Ÿ´]}úÀ¼y°t)|õUÑÇé׺uË»-º…˜HiS³f>¤2p$p96­¼N>SDâ&cî0çÜÞ@[¬¤O{¬ÄD66 GDDŠÒ«xñp?6è2 vâ'i%:Ù˜<ÙÖWG{ì±âëÅáŽ;âŸH¦È͵¥‹[]„¸º/tŸü,Æ•D$åeÄ”rç\íкíYÀJàuà8àClÅá^Þûs‚ŒQD$m<ŠMWì¬ÃªâÎAÉvšúðÃH²K™|gåÊåÝV©’µ=ªV-¾ñ‰¤»„«¯†}÷…N°jûÑþãâ  °;¶Äç–8_D&#nlEK? )ðÐÒ{ßÌ{m¨O÷ŸÁ†'"’*q+”K°¢<í±%Yië´Ó¬¿u,ÎÁ[oÁWØã§ž‚mÛ¬Ø1ÇX²=nœ=‘¼æÍƒ¡CaÅ {¾y3̘ç©‚Íß| økz+"iÁya§9çözaã1í°ë³±Âi€Ï¼÷aÚ9dðàÁÓû÷ïO&ü~ˆHˆ+áþï‡îOw ãÿ¹r–`{_|ÇymãF˜;;,ñ1Ф£þZµìßHÖc~Ï=øÛ€±Z ø9²#Zbu£Òžsn8ÐÛ{¯˜;)#Öp{ï×OO†Z‰%ßí+€2ι75­\D¤vÃFT$í­^ûõrå¬zy… y“m°Ñm%Û"…Ûe+Ç’Á&O†¦Má®»"Û† ƒ† a̘Äþ¼y6%¼(3gÂï¿'>‘L÷÷ßðúëðè£q>ðíÀ½ÀiØšîiØôòt3˜ZÀí>ìì=ÿö_ƒ S$^2bJ¹sî8àSl…âà9` 0Õ{Ÿ`h""©k þ†Ô7eBLž 'Ÿlë ï¾†‡òåaÑ"{½[7=ºtI\ mÚXÛ¯k¯µ¶E`ë¹ëÕƒ… íy0i’m‘3s¦%Ùï¼cÿæwÝ.¿Ü–eÄÅ àn,Ñþغo§ã'ËÍÀ»1^?*ßóË€§ŽH¢eÊ÷Zà& ¡÷¾±÷~€÷þK%Û""1D—AÙXköìH²öÛo‘d¬uP·nðùç‰åê«­Êx… •eÏ,°êåádû ƒƒH¦ûãxùåÈ¿ù¬,ûˆ› X÷ˆ³°¢–kI¿d[¤ʈnïýl`¶s®œsî  V»qð½÷~A Šˆ¤¢Ó×C«bëç$n:N:©èØÇ‡žøxN<–/‡uë"½´¯».¼ªTIüç‹dº¶m¡AhÕʪ•Ÿt’%ÝqSx3êù`P7ŽŸ!"q— 7€s® 0 K¶ó¿v§÷þžäG%"’Â^z¬d;îÊ•ƒ×^ƒ÷ß]ŒlàÀ‚ .M ­[GžçäÀ÷ßÛÉ|qääÀý÷C¿~‘EUªlŸ\+Ù‰2e¬^B¬|;m"0ø«RtêÉ-’â2bJ¹s®<ð*6FÓ¨p7w;ç. .B)Ê•³[,Í£¦„nÙ›7ÃàÁpÔQ‘Bk99л·†·ÐÚOÀwÚHû+¯ìPø"RB M¶~Öç`½x¾Hðç%ÃùžW $ ‘„Ɉ„huíà½é½_é½ÿÛ{ÿ³÷¾?VH=¸ED$©}4ïî‚ yüÒKPµ*ôïoÏï¾Û .UªdIsxÍwq’î/¿´ûU«à¼ó[˜MD¶·j  7Üǃž‡µ»hɯãñƒðO¾çª)"&Sî¦Ø×Ík…¼þ4p”s.S~^IqÓ§ÃM7½ßÝwÃøñöxÐ ø'ßÉç¦My§¤‡“îŸ~Š}Ü×_‡Q£ fM{~ôÑÅ]DvÜ´iЩÔªe ‡ ±ä;n†}°³ß=CÏÓYþ‹’å ÜK$meJº +%Ñ¢×ÛkTµ\DJ­l`vY²ÖD±_h[a·ƒ‰4c´l ÷Þ[ô~×]g'ç?ÿ\¼~Ù×_MšØãìl8ÿ|øðÃÈë«W[…ôCµãÞ}wœGÙD¤PeÊÀGÙR€Úµ#-øâb,ð7Ð[Ó}y„¿êX¢Ý¨l8"ñ–)EÓ¾À®Ýëœëé½ß~Á9×è¼Tp""Z´þ\‚-°ù¨ªÛ&Ø¿ÿmk¸o½ÕÖv>ÿ0a"I˜I‰LWb]&¾¾+°QbI9™2¥ïýpàà] Öl-pÐÜ{¿.¸èDD2 ø¸ø[|Ó;æ;d'Ì §žj­»f̈lïÙ3’lƒz÷Î÷ÿ¡|y+l¶dIìÏøãKÎW¯¶Q좬ZóæûG‘T·xÈ®>þÛE$%eÄ·sn¬nãëÞû GD$u„íÁXÓÄ÷±–2’#FÀ† vkÓÆªŽŸ}vñß?v¬M /Êý÷C:[ŒÊ$C‡Â)§?‰¯ ¬€áܹö°ÓZùë=l‹ÃqE$!2e„»V£±mÀqˆˆ¤–qÀeÀ…À)ØÈÈžA”Ùî»yÄŠ&mÞ ·ÝfUÅ‹«sçÈúì°²eíxaåÊÁk¯ÁÌ™ö¼¨µ¡:ÿóE$~¦Nµe X=‡#à—_âtðÙØ…Ôs±>Ö§Çé¸É0›ƒúwè¶ 8 êu‡µ= ¿þd²‰¯LI¸Ã§%8­É`Ó€±êä÷3€]±7’P7Þï½g#Ðï¼YY%{ÿ€‘¤{À[›ýÏ?еk$ÙîÚÕ* fSÑcùì³û9Ddçì½7|÷]äyË–ql6xØû®(NÇM†òX‘“°µè»ƒ€1ØŒ¬ÍÀ@ìïU2d>®”fÎÇ*…š&œså°µÚ½±¯œ9À ú‡Ûâ½/fÕRïÁƒOïß¿?™ðû!R*}‚j—†§MF{ùehØŽ8"ïö­[KžlGûì38þøÈóìløúk›ª°v­UBž5+öq²²¬"zÇŽ;‹ˆì˜= qc«ãШQÐѤÀóÀ+ØtøW€Î¡×VbÝ5 &´ÔûéÒžsn8ÐÛ{¯Îè;)S®µÅÞ³æI‰FD$H›±ÑƒŸÛ±©†±Q‰«O?…¾}mÚ÷óÏÛIuØÎ$Û7ÙÝ'Û•*ÁpW­j£í"’|£F%èÀkŠû ‘j僀ÔŒÌJ¬‡Ð€G°‘íeØÚô…À& û[V6 Eâ(Sî>Eì£2A"’ù~ÃNdn®.ö 2¨Ì®(¾e œ{.üôÜsOœ{î"+ F†nÝ`ÌKú{õ‚ÚµáÙg­ŠyÖž(ºBºˆdtja=znZQñlõ±3÷snÀ,,Ñ®ºÕCɶdŒ´M¸sÕ¼÷k¼÷Ëÿ+b•‘Ì·/ð0pð2ð4Ð&æ;¤fÍ‚[oµÞÙ•+Û¶1c,™7ºw·)Þ_|996 ᤻W/8ÿ|k+ЯŸ¶¦d[$xoEÔF‚~€‰ãpÐß°õÐé$ kcö¶ô`"pyç¡®þvOr|" ¶ 7ð„snˆ÷~j¬œs{ÿÅúrkJ¹ˆd®¯°Q>ØZ¸›€~ÀwDJKJ‰ÍšíÛÛˆñ!‡À5×X_ëgŸµäzÛ6˜2®»ÎÖs'+Ù 'ÝÑÂ#Û"¼¯¾² `‹G¶Í˜aß';e0™È´òÙÀO¤öÈð|ìl|Vué}lÕ3X%¦¹¡}ÖÏ—£Hœ¥sÂ]çœkï½ÿ¾ œsG¯a§ ï&38‘¤»˜‚­×¾AØŒ’íl,Z×_ŸwŸW^±û>°iÝ""ÑêׇeËìq™2pÜqÖy`§}ŠÕèhŽM+äº ÷*l:yS /ppfÔë5ˆL7¯¨ÈœdˆtN¸o¾j>rεõÞÏ¿àœsØ Æû±SÍ;€‚RD$iÞÅŠÐܼ¼ Ô 2 ô÷óÏ6U¼(?üþ {ªÇ¹ˆäS½:\{-Ô­k-ýjÖŒÓ;b-ÓAu`¶ô1àfl4{¿Ðëù[›å$-2‘„JÛ>ÜÞûyXuò­Àç\#ç\u`¶ŠñWàhïý½Þ{ý³‘Ìô5p°èM)<PòGnnñ¶dÅ ›.Þ¶mìý*U²õ˜J¶E¤0=fµâ–lƒ­‡þøb:bJE›þ~p°k_¹_Ô>ã°‘ïã±z$G&7D‘DIÛ„ò$ݹÀ'ιÞÀL 0 hé½ÿ6¸ED’À“°©x¯u°þ¦•ƒ *x6À±ÇF¦|̙͛Ã÷,DÊ?ÅsØ08ï<øä“ØŸÓ²%T«¶óñŠHéâ}rpÖZk?àè83f`gì °‹Ëcòí³KÌ;bk¸E2@:O),évεŦ—ÿ°8Õ{?6À°JÌ9· pÖÐço`lô4ù¨ýšaåÊb%’&yï·¯qÎu¾vןzïg&0| Ê:líÞ líöXÿí;ƒ *x6@§Nðõ×V°hÔ(Øgxí5Ø´ :t€ àÐC-!2ÄÖiÿúkä:X‹¯¢Ìœ ëÖi„[DŠöÏ?ðá‡ö4mš],³3Ã_OaÕŠ²â`¢´–`gêÏ«É;¾gè&’aÒz„;,j¤{9ÖÅoF •s®66ÑæMàdìTy†s®o¾ýNÃFðÿ…]ÏüéœËÊ·ßá×ú—Ós×%úç‘$[…­Ñ¾ÈƦÎ. .¤Tlƒ"½÷ nÉ6X‚Ü¡ƒt¯Yß~kÑæÍ‹§uk¨P¡èÏûóO;Ö† qÿQD$ƒ|þ¹­ç>ã »ø7¾mÛ)û`ˉ>ĦjŸ†u¦H%€°Xo`K¡ò¯=߀]8¾8؈ًH$=dD y’îŠÀDçÜ>ÁFT"ƒ€&À!ÞûÖ@mlÄþ)ç\Uø_{³aؤ¡ú¡ý:gW†äœk…u5¼hà½o‚•¡xÜ9×"i?‘ˆ$^u`0k|ø!VÕuÿ ƒJáéœ'œÙ=}¼|y8üðâ>Õ«»íßøD$³|0lÙb+V„îÝa=vò ›±¿gbk ë•vò˜ñ6‹oàvlývÓösØYîà8à>¬ÏHšs>. H’Ï97 [«_`ìŸkôмŸ¼÷)·²Å9W«ÙØÏ{ÿTÔöVÀÅÀ ïý\çÜ-Øu¿F¡‹ áý&µ¼÷á¢qbÉ{ÝðTsçÜnØXØËÞûËŠÖ!ƒžÞ¿Òõ÷C$ã}Š]š;Ýø7ð6²‘1—Rw܆ P»6lÜXø>};Zâ]£¬Zgo¾ÙgãFõîÐÁFÅkÕ‚‹.‚… aäH+ÀÖµ«V%»ÿ¶ˆ¤ŸíÝi§YÒ³±‹­åãt¼DX ¼ŒM'ÿ Kº3³eeKÒl¦maœsÃÞÞûTþÍJ é|z0‡’•úµè]Ñ ;=þÀ9W Ø*ö]ð­ °(:Ùù¸?4¾[·ýFôºnïý&çÜ—@ëÄý"’TñE(ÿ¦”Æz6(Ù reØøñÇÂ÷iÞÜîƒë®³ûNù.ãVªdë¼'L€›o†×_‡ªUíµ3Ï´ä|Ä%Û"R<$à Í€eÀ´Ðmö7",~NźhôVRx²ý9vö»0t;x$ñaŠ$RÚž"xï3¥$Pml$~WçÜW@+ Ç9÷>p¹÷~uh¿ý€5¼?üz ¬¶ãî1ö;8ž‹H€^ÆÊ,^õÛ~ k&  ?ý{Ÿ·Þ‚+C rn½5ö¾á¤;Z×®vÙ›7ïäh÷³À¥Ø¢ÊÃH­á•o°5Ù•€ó±¶_Mbìÿ6½>vFÜ&ÑŠ$^Ú&Üι Þû-A½?ŽÂ ÷D¬êxO  ÐØ×9wth´º26õ<¿µ¡ûØJžèmù÷«æœ+[’žä—_~y¡¯]ýõ4lذ¸‡‘xø«ú085¸¨dP©eüx¸úê¢÷»æ›vÞ¥Kâc‰¶a¼ý¶-G™6 ~ÿvÙeÖëVÑ ëa“JÎÅ*ÂÚU~‡µ±,Ì’”„…Z*UÈËmÈÔ‰ÿI–¶ 7ð­sî)àYï}vqßäœÛ¸›¨rw¢‚+]€=€ÑÞû‹CÛF;ç–·鮸‘‚›>„¿ž7„ö!Æ~›J’l¬_¿¾Ðײ³‹ýŸ]Dâ¥"ð 6šýpðX ¥œŽ¡qcøùçØû5iGvš!"’ S¦@ûöÖ",lìX[¦²Cö Ý–™VÞœà[l}†Mu? «Jt1Öø6–õXß°Y IDATž…Øtô…Àë¤^!¸ÌQ¨RÈk©Þl.m¤sÂÝ»Vv“sîUlÝrE œs{aÌ{cm·†“:§¨ËB÷/æÛþ*–pŠ%Ü¿5 xh5!¿xï×9ç¶{²ß/% näÈ‘%}‹ˆ$Ê*ì$êk`(Ö^åWàÑ ƒJ=eÊÀ¬YШ‘7+Sn¹<|,°õÛŸ|{ït´"RÚz(deYÂ]£œ}6ìô„ÁS°*å•°iå©°ÄèìïÔUÀ9ØÔòÊxÏO@w¬ èØß¼m Œ±”óÞú m'\4-¹e¦´M¸½÷߇*y_Ž•`ø—sn#v}o vlolísƒÐÛ>ŽõÞ§RW¿¥¡ûüµtÃÓÝÃCÌ¿‡0%¼.°Á{¿.ôüwìgί.©[8NDв;ùè‰õ+è‡5T#rrlzx¯^pt¨EÙ²¶†»MxähÛÖ¶Ÿ}¶H{øa%Û"Œ]w…!C n]8þøâµ,ÒÀÃØúèT)žÙ[³="NÑ wk`šÈ,%UþIîïý6ïýXB}ðöÏ´1ÐK¸ç×zïOK±dlô:[á­GèþóÐý›Ø(u»ðÎ9‡MÔ™õ¾7Nιݣö;)ÞODÒɞطÅçØÈÅ+غí}ƒ *5ôèO=emvfÍŠlÏÊ‚o¿$Û`ÕË_xAɶˆ«Oh×.NÉ6X¢Ú XŒMÁ¾™HYÝ ¼Bdîæ…Øß®'‹ñ¾2Ø|Ìq¡ýûcEAEÒXZ'ÜaÞûïýÞûk¼÷Gxïëzïkxï[xï;{ï‡xïgA¼÷¿#€;s×;ç9çnÅj¿O¤—ßX¬ÓâpçÜ Î¹†Ø†À5Q‡|+™ñ¦s®yhÀèÐ{‡$凑øšÌÂÆÌ®Æ ˨Œ;[K¯u묭ׯšË#"ihMA=fŠk¶ °6÷s—ÐM–¥À Øbȋ鿠ÛYÀéØß¹9Dæ|Ф©ŒH¸3À¥Øu¼±¯–Û±kç„ûi{ï=¶þ|)ð 6r߸$úb‚÷þ7 #Ð[=ó ¡f Þ{­‚IGocsTnÁ’ìÛ±ži»((¾z÷†GCëØ—/·ª¿""é`éR4Ž<êÔ±êå;¤¶÷ÿÙ»ï0)ª¬ãßKN%ƒ #D‚b€.âšQt1¾*`X «°*FTÌŠY  $]@’² Š*È‚ˆ€$ Ã0÷ýãôØ3ã„î™î®îžßçyê™îêêª3>ÒÓ§î½ç,Çí±o‚A¹ [äøtèçÌ(Þ;+®ö=6Ò}^Ì£I(}]K¡DxHh+ì¸ÿ]#8ßg@³ØD'"{»Ýv%6Uð m jÕ*k£“³¢ïu×Á† ¶6ò–[‚‹MD$RsçZ§Ÿ£ÇÛoÃ…ãde°bcX†U*ߊ é$ÚXÝëósB[4jaÕ˜~më°Í")J#Ü""Éj6_eVö¬÷vóc ØwßY!´sÏ…‰s¿v÷Ýpë­ÁÄ%"­ŽÃõ$Z·†áíˆZ±=Uûi,›Vâ‹o6êÞðâÈH=‹Mï…‚›… „JJÓ·ˆH²ª‡Uj=¸+‚sg nÃÛ23-éž<ºu :*‘è•- O<ÍšAûö18áÑÀ“@G`ßœ¯¸nÃÚ}€M)_åûÏÂj–4ÊÇ42‘@(áIF?awø?^ÄÊ(®Áúo—bG ¯¿§Ÿ;wB¿~ðãP©RБ‰ˆDïôÓcx²CB[V*w^èù1¼FQúí°îͧ†¶hÕj` q—cÓÊ»ìšt‘Дr‘d³ûÂr&VyöoÀlÄ úožéˆ'ãÇCíÚðÆJ¶E$½|÷] Þ|.–¬¶nÆ:[$Êl:û£@Sàtl)T´<–tïÍìZ£E² ·sîmçÜÎ(¶AÇ,"‘½°>«1XšRØs{Ü88ì0¸3ÏTúó΃~€£Ž &.‘XZ¸n¾Z´€ƒ†5kŠy¢>ÀóXѱ_KcbÑÊ#±Z•ŠÅ8ʃ~lÇz‹Ÿ£EÊSÊ?Äþ f;8k–3 Ø€}==k‹õT¢‰Ú$  Ð û4{x +€SÊlÛ#GBVÜ~;ì½7 ~½fÍàb‰•ÿþ:tȽoÂ:´'» ô3+V6¸˜È{`W&6íû$l:ù ¡­¸z`#Ý¿`SÊwa£Ý")(eG¸½÷Oyï‡zï‡wxïòÞ_ソÇ{5ÐK¾KqIŸa_4.~c•fãýe) U«fEÑj×¶çƒÃ“ˆH¬µkg#ÛÎYŠG±Y<ŲèTÚñÑîxÛ œ ¼Šlë ¬(ÁùîªbCgÝ€% O$H)›pçÑ›„9*ï ÞûÀ¿€ss.Áq‰ˆDçŸÀ ¬%Ø¡Ÿi*#Ã’èÿý/¼oõjÅÞ¹Óžt|ø!T­jÓÇ»t &V‘x?V¬€™3᪫ ^½bž¨>ö­ø¬È/$¦by%àØšíY@+¬ÓFqŒý‹€?€™% P$8©<¥<'‡­©†fçÕØî½W?INó€!ÀcØ´¼ÿaswÒPFœs¼÷¼ý6Ü{/ìÙcÉöŽVyüÝw­ Z§N0m´i•+¹ˆHìÅìfbMltlÔù ¬g÷??kÃó€¿G†¶’hÚÀÚŠ}…-Ý«„ç @ºŒp‚­ô圫’óç\gàràÝ ‰H Ö?õ@0Œ´ür‘3Ù+4`\x¡%ÛnýpÄJ¶E¤ôÈÈ€Ï>+æ›_ŽÂ¦•wˆYXù«‚ý½š„ú< ØXÂsþ‚u먎Ú,,á9E’ ·÷~ pV m•sn¦sn’sn 0;`ˆ""[Š%ÛobÒ^ŠS,'E,^ S¦}Ü”)m &"’®23ífãEAýúpÌ16ÍXðžs®0¸Ô9×Ý{ÿC1Šˆäòp 6üalAÌ€@#Jˆµkáæ"n*deA0}º’n)=ŽUgŠGr<Þ ,ÀF‹céelÔ¹/–p ìÃóÌó|k Ï-’@i1¥Ü9Wx øp(ð€÷~Öɯ<á& ""ÁˮǾ­Âz—¦©õëÃSÄwí W#/ÌÎv¬ˆHiµy3¼öZ1Þø=08 ›VÞ ØËÈ€þX¥¤±*âÇÇøüK°6c]ØïñkŒ¯!’i‘pc+Fjç{ïå|Á{ÿ!ðo¬f¢ˆHðfaýµ§m±)x£BÓÐçŸÃ¡‡ZÛ¯©S¡Y3˜1Ã~”)'}ú„×u7ib£Û-ZµˆH0ví‚×_‡3Ï´jåýúÁW_Ey’L,9=˜ lJ:M=§õØÈy`*°»‰Ke±›Ó'`­Í>Çæ±Š¤˜t™RÞØüQÀë+ι²ÞûX•r)ž±Û„ÇÿÂF"ÒT:°e‹U$¿àøúkhÞÜ’î^½`Ìû 0m dk¼[j½žˆ”B¿ÿnIvVVxßÛoCÛhnÊ„µ™Ìö°h“a –Ÿ \еìÚ/FçÎÖx.Æç @ºŒp‡}„ô(àõ‹€¥J¶E$p_`£ ã€Oùän}’†Z·†Ñ£íñš5pÅö¸ysëÉl{,|ó’m)½4€îÝ¡vm¸øbøä¸í¶bœè=,n‹ýݹ:†AÞŽù܈µ²;2N׉£´øZã½ÏpÎõ^îÏñÒY€Ç¾Ú>Dl""ªŒpÁ¾ü\ƒ­ÝŽG»–€Œ ×]g~®¹&üÚ¸q6­¼N`bIc1õ;ï¨øVlÝu¥ž÷lª÷©XQ³F%°Šâß×^›ûµ#àŸÿT4‘âª^N: š6…aÃìÆæwFy’±)Ùý±¿AM°êFÅõ>öwlo` ðïœ+YÀÀCØBÑ>ÀÇq¾¦HŒ¥EÂíœk Ü‹•‡h„ýÓÄ{¿ [µ²X€"RzµÎÅÖ¹eWsí T 2¨Ø¸þúðHËSOÁ;ZÌ#"SãÆÁÊ•pÿý6=´å”¸|Ž-Ê=X#Þã°éägPòŠç‘X€kˆÄQº$ÜÙcEÉ¿tCEJ6FD$:»°év•±Ñ…nØ•­AU|·ÝwÝ5j@ÇŽáJäM›Â„ V4m¿ý QD$í-Zo¾ ·Ü*Dø¦¹À€À|¬žÈ#Ÿø¬ÐûŸÁê‘TÄfoÅÛ¬xÚ¡­ íÔz‘¥Å”r`!V²¡_Þœ wit[D¦"ð_ ;p,p)v;°F€1•@§Nösóf0ÀF´³p‚’m‘xÙºÕ P|°Í(9&E35ü,I®ü¸²A<,擟ZŒóÇC¡k=|ÝÈI!i‘p{ïoO;çÞ'´ÂÃ9w V²èl}·ˆHü½Š’Ù ûb2+嘈©w1’•™™áç}úÀ AöxæLxøá`â)m*V„Ñ£áÛoí¹sV@-b¯ßb;¯Š2€ÀK¡÷uÆZe’¸y²·cÕÕ¿ÃÖ‘ß’ ëŠÄHºL)ë0ø6ŽT6´ïìÜùÞû)A&"¥LV¦ñyl:ù‘¡-E¬Yc£ØG£F…÷?ð̘‡_Xx""¥J… pưp!œwôí ûìÅ 2±)åó±iå €·ˆ¼'welIÔ·Xñ²Û€.ÀQÄP•°bm?䨮Áú‹¤€´I¸½÷›€Aι[ÖX ÃÀRïý®ÂÞ+"3Û °¥C±DûMà´ ƒú«±cᬳ A{ž‘>h=_„iÓ,¹îÕ+\‰¼re˜=jÖ ,l‘RéñÇ£X³×vàd¬oOGàV i„ïÝÜ„u×èÜO+[Ø›bl°/–µì‹µ7ÛŒnIi1¥<ßõÀ¯Àr%Û"’0ŸÍç€Ø4¾€ãŒ)#FÀàÁpôÑ6=|Ñ"èÝn¾Î9ÇF²+W¶iåÀ¦Má÷*ÙI¼b'Û5±‚ €Ç±9¡‘¶ÖúûÛv8Öær,V4‘š˰©íßcÍ£/ ´I¸såsc)ßs€-ι/œsm‚NDJ…ŽØ¨ö ¬OéRà jAå6b„ÜøñGèÚÕŠðL›fûÞ{Ï*’ßs=ÿåøôÓ@B‘|ìÙcŸÙW\[¶Dø¦mÀdàn¬W¤7‚auH–½€1بw"•Z`‹D?Å À½ŸàDJ mn`p1Vb pp=P˜åœë\h"’ö¦`k·ocUʇÑ_,_N¤ óÞ{Ш\y¥€Ÿ–dÓáEDJ£­[áê«¡qc8î8xâ x÷ÝßüVéû] !ð·Þ³«Œ4 [¬9 iÞ+úØKìTl¤»ö·õ?Ä RLi±†Û9×k¼s¾÷þå<¯=ŒÕ5†þyŠH€E$ÙyìA>Ánç½€%Ûh½v­µú2 ï¿÷^[ËwÙeá`"""¹´Æ¦•wÂþ¶U.¸¸ûÖ|NÁá7¬/ø1ØÍõá–² ·÷~¾sîlÇÀÀ¥Þû/œsÇ^Ë}«÷~b0‘ŠHRÛtŽò=³âHd¼·uÚ_~ _ ñÇÚkÎÁСÁÅ&""ÁY¶ &L°íé§¡cÇ<ŒžË³/Ø 8¬Yul­ôÍÀ§Ø ædrqhI!)›pxï_tνŒÝßú>»2yÈj`$ð^v."–_tF¶Ê´»vÁ™gZeÚC .& NF†­ß^° ¼ï•WòI¸–FqâÿÅ ¸Xû‰_Ú–ï`ÅÞD’TÊWíöÞgyïçI¶ñÞ/ñÞP²-"é¦[7xî9K¾wí‚ :" J… PµjøyûöpàÁÅW»—€ XÃß[FF$R¤”á) îº þøF ïëÛ×ÚÁ´o]º›ˆˆoð`èÞݪ•ï¿ÐÑÄQ=¬ªºH QÂ-"’Ww¬Ú  °(¸P·ÇÍšÁW„_»úê`b‘äröÙ¶E¥6bœÝÛ§°.¦aÅÇ,¬WÑ¡í0`x ‰*å§”‹ˆÄܧX² ðmÀ…BÆöxð`xÿý@Ñá½mªF8Ù«ž Æa½ŠVyת‹$%Ü""yÕÉñ8ày@ûìcIvÕª°g¼új°ñˆˆHòòf϶ŽMšÀŒ…\>ÏóTY ý,ððppr°áˆE ·ˆH^]€Øt»Æ‰½ô¢E0p ìÞÞסƒ%Ú×\Ï?ŸØxDD$5ìÙcë·>þýoX³^½7쵨ìÿcb3Öø÷q`p&65^$Ii ·ˆH^ïäxœÀ à ÀqÇÁ–-P¦ <ûløµ>}lÉOÙ²pÐAÖ»lY8æëjQ o±Qîr@&¶6:|œ†Ý hÚvð×{‘$¡„[D$Ix NÌŸãÇþûÂí·•ˆˆ¤Šk®N°jõëq°2Š8&uþ\ÀqˆDHSÊE¤tÛQô!‰Rµª­×nÑžÿûß°~}°1‰ˆHê8öX¸êª’íTVXLʧÝ|hD"…Ò·ˆ¤·®\yµ°ê­ ™ #FXq›½÷¶}õêÁGÁ€ðÜsáý"""ű};T¨f_úOVͱ)å‡þ„sY IDATŒHáÒêßžˆÈ_,~âøvÀçES±øádÛ¹Î8&M‚iÓl«TÉ^kÕ æÌ)ù5DD¤tÚ½>þ^yÞ}×~ö9k£©}ã] Ìj£¹º’”p‹ˆätP)þ—©T êֵǟçŸoÕdËè˃ˆˆ”€÷Vä§ŸÂû^{ ú¼XX±Uøø!´m® 4"‘髈HN ,ÂòôÓ¶Þà­·`âÄÄ][DDÒ“sе«=®ZúõƒóÎ 6¦˜{kÛÙ L4‘Bi„[DJ—#%ÀV #0/q—ž1jÖ„víìyùò–hwëÿ;œvZâb‘ô5dœ|²µ“¬R%èhâ /p<Ö,˼DâI ·ˆ”.»€Í¡Ç L¶Ç‡Ë.³iäsæ@Ó¦¶¿F kV¡Bâb‘ôÖ¡ƒmi«^hû•ð´ò.$÷ºs)µ4¥\DJ—oƒ¹ìï¿[›5k woؼ9üš’m‰·uë`GµÂ,‘L ÐK´ÿø&ЈD ¤„[DJ—ò9”¸Ë^{- l¿ùÆZ‰ˆˆÄÓæÍ6ÃêÄ¡Q#xóÍ #Š‘rÀ+À"ààg O ‰HSÊE¤tÙ‰ÝjôØ´8Ù²,E3V®´Q†ûî‹ßµEDDÀj†¬X~þÚkð·¿Nlú¹K¼3°Ñn‘$£n)]2,,áÞŸK¬YcbO: fÍ ï/[^}¦O·‰—ìbœ ÀÕWÃ-·OLj{€túÝ$­h„[DÒ×,±N°ï¿‡%KlÍöi§YŸíV­ìµ´¬+""I骫ìïÐ1Ç@™tf;8hÚjŽHAÒíŸ^ZpΕuÎXFÉ™"o–8çÊ9çØUX$É\lLüe»w·Û6X[–ŒŒÄÇ!""¥[«VУG&Û`m>Ïð%.Eá$­h„;É„éÿÕ€6y^«<œxçÜàzïýœ<ǵ w8°Å97¸Â{¿.þ¿H‚<ü·ˆc°Ös»pfç=¼xá,\íÛ‡Ÿ`ëµÿùOÛT‰\DD’Á?B:Ö–2¥ý œƒÕcÙÚ÷9–ˆ‹$%ÜÉçVì£bq>¯=œ < L†Sœs]½÷ œs×°f WcA÷3sí¼÷Ûãÿ+ˆ$À$àÝ(ŽŸ <4‹}(#GˆðÔSpÉ%áý·Þ ýúAË–±¿¦ˆˆH¤V­‚×_·:"óçÃØ±på•AGUB{c•É[-C?ë‘H¾”p'çÜ‘Xɇ5ù¼Ö¸è뽟Ú7X‰%Þ燽ûØiê½ÿ%tÜ2l¢ÍyÀ¸øþ"Iê\â’l¯] Éçýß“gM¸HZ©…}¢m¿cw»ã`Ý:[ÿV¶¬=ïÜÙîÈŸsŽ}yÈ®H.""’ .¿N?:t:“loÞlϯº*÷ëk×ZÒ=}z#ݵ€+°)å­ã®H‰(áNι3€ €ÎÞû¯ üšÏþì2 €ÝX™¨ × ÚØÖ®][àkµkצ‚*AI²hÌ =^4$. ÷¢E6e¼O»#ŸíÌ3í‹A—.iZ VDDRVãÆ¶%‹aÃÂÉvAÖ®µZ(oä7 •mPžç;KYé✫T.àå‚öK””pÌ9×x¸Ó{ÿE!‡V ÿfÙEЪ†Ž¡ã*9çÊzï÷D_Æ |múôétïÞ=ÒS‰Ä×7yž|¯¨D†µâ3=͛à 7„_ëÖ->׉µyó`Ÿ}liT¢½õrˆÍ+HË–0~|'ZŒÆ*•ÿ€UAú¸-#KCÿòzÄ9ƒL w€B=²ŸÅ>"þYÄákšùì¯ú¹’pŠQÐqÿ‹&ÙX²dI¯í³Ï>ÑœJ$¾2ó<߈dýW”^~:u²–_7Ýd_Vúõ‹ýuDDDbí¿ÿµJå&ÀO?Ùšé[n‰ÿu33aöløðC8ï<8ôP8î¿¿à÷  Õªqb 3uÅJ ·@ã²Ñ¹¸§€×îNO`,iK w°öNÄŠ¡­Î1•¼&PÖ9·xÔ{vÏ®m>稃Ý}úÙ{ŸéœÛHþMê?EàE–ˆI{aŸh;€ƒ°iåQÚ¾Î?ßéNlߊpÙeðüó6 P¿¾}a8úh[ÃÝ,•ÏEDDâáòËmd;Û„ ‰I¸×­ƒcŽ±Ç•+[³jz~Šz€CWJ]é*²üK~¯9犘ô/‘ÒJÃ`ýŒÄúk?žcûØzüyèØEÀ~ιFyÎÑ•P²ã¸.9pΕ:a#é"éi=ð…u±òÏÄöíÖ–äw¬PË7ÂwX±–O>îÝm=ÀAYeòÙ³­`šˆˆH*Èž‘uä‘0z´Ý@Ž¥_…뮳éâS¦„÷7jdI6À¤IðÌ30qbáçzê)û[[¤‡Ë€ã€æ@!£æ"AÐw€¼÷y÷;ç:ͼ÷9_{›ö1 ¸.tÜ¡@Or7Aø7ð–sîï}öGÙ%ØøßC1þD‚ãcwªìd{Ú4{¾cÜw_îc–. WLmкvÝõEDDá ­Èg¬fgýø#Ô«žú]¹2<òìÞ |Ç>¶W/k›9ožýõEüÏÌ„©SáÔS‹bÖççp 6%’D4Â"¼÷káÀPçÜgι—™À G­dÞ>Þpνéœûëí}¿÷þ«DÇ-oŸÆîtëÖÁ÷ß}Ü?Ús‘TT«V8ÙÎÊÊýZÞç…ùé'ØhÑ"÷(tõê¶ä ,áΩW/¨PÁfŒÝ}7œ}vuƒªUs¿çàƒ#æ%àul%ò¥€VCJ’Ñwrz™|ê+zïïuÎ}œŠµøº [ã•ã˜,çÜ)X鈮Ø$Û¡sФ‡@}l½öQ¡ÇY~ ñð‚_jÞÜF®<Ðî¨ä”SlžˆˆH*3&O†×^ƒÏ>ƒV­àÎ;¡iS5*÷±?ý}d-1³“õ&MÂˬ&M²BhÙN: fÌ€eË`ùr«663lÆðhø+¡µ×k×Úù½·B¤ÙêÖ…Úµ#øe–ËBÛ`Fäÿ-DâM wòÞ˜{ï§S z=tLðLhIë€Wk€o±EÞ%;u˖а¡µü*È‹/–ì"""A3®½Öתe#ÛuêX2œ-;éþþ{É»!}Í5ö¸|y8î8xûmKܽ‡ìÚ¿'Ÿl7±{÷¶óg+W.wÕñrå,éÞµ+<²}Í5V?eà@xà‹«Hs§° å­¢úÏ!wJ¸E$y\G¸³|~þLƪt®ýìQòK¯X«W~ÌüùZ»-""©+g² áiä9“í{BM¢F‚Ö­mÔ{Õ*ÉÎN¸Á¦ˆ¿ý¶-ËZ´(\í ƒ"/ÆV®œmÙn¹Ž=6\Ñ<"„6‘$¥„[D’Ç3DV]ü‡ÐÖ [¯UB«VÙ÷¢Ö¯rŠU]íØ±ä×I½zYÅð3`çN¨TÉö÷é?l#Ù-ZÄæZ+þ5ÙÞ½zÈZšåÛ—{0ð”òeÀõ@4I»H©hšˆ¤®õ±9M­Z¹×¤n]«¬*""’І…þý ?¦zõÜë¸{õ²ŸÃÊ•áýÁàÁ±K¶óóùçо= ·ÞZÈgË–äS I$8J¸E$u›ÓT«f[º„:Ø—-kS↠ƒ¡?Ú-[Úš´&MbsM‘ älÕ•Ÿ­[aÓ¦ðó^½¬Ú²eáõ܉0|¸ý]þæ{>}:ddäs`YlvÜ·XM—1@»DE)R4%Ü"’¼š`w©`…PJè믭çv¶5k gÏpÕÔ>²/3fÀâÅpÿý°`µ0Q²-""©nÏ7®ðcjÔ€mÛÂÏ«T WO¤ƒ²¥^+Z!µ ¬­X¾~Å*“? Ü‚µIZÃ-"É«>V(m3àJvª¹sáÄá°Ã`âD«¦zôÑV,­eK›¶vÀ–tç”=²-""’ꮸÂþÞfÓ&4ÈþV©o_+Æ6`@#룀GæXµr8•$¢nI^9ÖŠñKñO“loÞ Ó¦Ùšíúõ-ÙûrÑ®½&""’®ú÷·ë”+gm¹’Á]wý5ÙÞ¸1Ÿ®"wcý·¿>ÂF¹E’„nI^9‹ž4¬ë×Û4ñÍ9ªŸgdصœv튼‰ˆˆH*:öX¹ÎNºO;Íza_j«•Ýû쳃‹±0/¿l³Ñþþ÷€Þ½aÛ6Ûç”-kwų ƒÇæš"""?Ÿ—^ ß|cÏï¸F ½ø;°+¼zn0ñ‰ä¤U‹"œÉÀþÀ‡@{`^hÿüØœþ¥— eKX³>úÈŠ¢ué[¶X"þ裖|cÇæ?UMDDD’ÇŽÖÎ,;Ù>ø`8õT`*P;´ 6t‘ÄÑ·ˆÄÇdì.safÛ6Kº?úÎ:Ëzng«U Ú´… aëVÛ7hõç‘ôV»6[€ƒ¶Yn;•”vJ¸E$öž–ÇïôÞÛÏ„5`Ý:Ø´É’ðjÕ¬5ÈqÇA—.0v,8¿XDDD$94jo\uæBûêÕ6Ôƒ: ÂÇ,\•+ÃÄ9Gg YïXŽgAчIªQÂ-"±“‰M¿k†ùP+˜V”ªP¥ Lœ[]ñã-™xúi›6¾kT­jûêÖµµÞ{í¥d[DD¤49¡°¶7„¾ƒ©_Ãà7 S'X½N: *V„éÓãžtúFqü „;)á‘عøxøx¸[¿½Od§¨RZµ‚ùó >¦cÇÜÏË•³-§êÕ#ŒYDDDÒÇ5ð[›é¶hµíúôœ¿Ö£GB’nM‘º K´~.~ âd¬ù¢E…óâ‹ÅŒODDDÒÛz˜Ô®X“€eÀþù¶v-ôï^¦&/J¸E¤äž^Z³ @g`=Ð òÓlØÝ»ÃÎ*˜g»ýv%Ý"""’íдª ßc€ßó9¬|y¸ä[–‰—^‚ï¾ ?÷þõ/ؼ91KZSÂ-"%· øð P˜ü°wt§©SÎ8£èãZ´°Ä\DDD$—} ÜwðÓ¸ ¬Ëç°Ý»aذܵ^~ùÅ °nܘûØgž kWøðCX²Î?®¿ÞÚ—F˜t/ÎñØ_G÷‹IªÒnÉß uÔ,ê‡#€›ÕÀ¿€K‹wÙÑ£aÇxâ‰Üû6´?„-[Úš«&MŠw~IsŸÁ¾ïÛèvöä»QùÖ®]î0}_l—.…Ö­-Ù¾äÑ^¿N>9÷9æÎµ¤{òdëœRõÐ4Ç8„Û - 9\ÒˆnÉßVlZx4žÞ2€ŠÅ¿ôM7Y•ñuë¬ÅÀÛoÓOÂÈ‘J¶EDD¤`›î€æÓa7Ö¥tqÇÕ©“ûù¼yö³Z5»Á¿iÜpCÑë¼çεžßÆ|Ìz¨±wîçUó$Ü5qœ„õw‰Ôb|¿ž$ %Ü";G`…ÒJ¨ysXúó±|9üç?pÄÖÒCDDD¤0×ï ã¦}ܲe–LgO+Ÿ;×~¶oeÊ@ÍšðñÇödÏž‚ÏS¾<|pžsCÓV9žï ¬êßûýõ4½€.@ã¢#ÿÓH ¾Ÿ IB ·ˆÄNVìOÙ²¥m""""‘xì^h¼ Ö| ­€þÑ~ùví²c:u‚>'Û»wÃO?Ùãß«C _—ßBpÂï­U+üÜ9Ê¿ s&ܦÙçÌÿ4•±ùhnIJ¸E$vT†QDDDVîs1 ÖVƒïvCÛ3á§§à·íÖŸ»J•¿®¹._Þº¥,^lSʳy¿çWæ<}r·@m\\‡ä¿X15I3J¸E$r­•À. 0=ö—xÿ}xè!«BÞ½»Ýe._>öב4u"°öö°ïj¨ÕÌv×­ Ó¦Ù÷Šü œ•- mÛ†Ÿ{W]e#ØEÙ´ 5e{B›ÿEõ"àÐèß&ÉN ·ˆDnV à?ñ¹Ä¤IðÉ'¶H[µ*>ב4TX å~€f??ݰw-K·nµ"®Eq.„ƒ`Ø›ð÷_ʰ§”Ý„qË«dÔ²Vá€y^ÎÀFÊËcKÀ!<3]R„&€ŠHäjæx§Qçºus¯ÙιŽJDDD$"‡Ý€Û™X:¥êÕaêThÓ&¼¯iSèÜ9<ûÎ97Îúr‡<\ æ÷… ÃäP¶V,­A>[m¨àà%<7¿å ¡,áo\MúÑÿ4%Ü"¹#@ y|.1|¸U ]³^}† ‰ÏuDDD$Ív`³ó¦gï4u놓îAƒ`åJ˜=æÌ±bjãÆÁEã€\ºêiÞz´¿­ðKxà›Ð㼃àkó<Ï@Rަ”‹HäÞËñxI|/Õ°!ôíßkˆˆˆHšj ¬Ç¦“ÿl./Þ©êÖµ©å{í®jÞ¡ƒµ.­™=û¯°’3±üêB<–w¼³c±©ás€‰À™ÀaX²}‰÷ôòŠNñ+{aÓÌ«Ä j‰3%Ü"b^Àì;€ÉÀI@O¬ßv¯ø_¾MøòKØ´ >û fÍ‚CÿuEDD$ UÆcÅÌöª&àšÍgEV{Ï-ù¿ÀÉ…¾Ñ1<\g}1¢”SÂ-"¦pvGø>à#à®ÐþªYN=Õ6‘bëú¹ ø kºÏ5ò-|ç( Tð…ŒŽó€ x>Åáp\Šgpq¯+ñ§>Ü"¥ÝûÀ·ØˆöûÀ£À5Ø$¥Q¿ÐˆˆˆˆH†õ± Øí€›Kx¾N@ÙÐã¦%ØNb¦_S¦ÀoXuòîÝ¡AƒÄ\WDDDÒÌ*`uèq}àJ,9nŠ%Þsò_(¨fÌ–<Ïa±³¯SBÎqp=¶.à ç¸Ïû?¯’ϛؘ¼œƒç8ªxv–<*‰5%Ü"¥Õ"¬\Ç3ÀßÀÇXq´Ê;ïÀOØpä‘ðùç‰ADDDÒÀXàÞ(Ž?˜”ãùÕÀ¡À¥ù›3uðg©³â[K8Ùþ‹¾ ’p{~ÂÑÏW¡8úc#Ý£ûK‘Äœ¦”‹”FXòþ@VTäLŠšÈ7;v@ÅÎë×&)…®Æ„—^ =묮 6jÞ8+&W~+[ÛÕ{Úzϓ޳»Èwy¾ÂqŽ©Xíõ'‡c‘Äœn‘ts*ö¢°­:ð ð6ʉ}TPðiþ9²}ÅñôÓV|út>ŒÍyEDDDŠTp’}0“ÜcÏÙ2±¦]k±ïP%ä=[¼çï™YŒ·ß tÇÆÚo6áØcg[‚ç/J6%Ü"é&ØÁ–M…Z \‹}\WÏÿ”3gÂÁÈá}=­[Ã{ïÅ&ìJ•lýöˆpƱ9§ˆˆˆH‘ξÀÊ–…`¿L(ÎQÅ9:GpèB,—«T mår<λíãȶ&qùÅJ1­á)íÎÁî‹`æLèݶmƒ‘#aìX¨PÖ¬ ½ýxýuµñ‘$TköGèyeÈÕxë =е¾øÐ21Ί¡] \ ”qŽÆÞÿu,´"©3Ãë–zJ¸EJ»êØÚbñâp²mýúÜÇddXÒýÉ'Э[ô—6ÌFÊ»w·Ÿ""""1³r¥­y»\¯ ý|hzÜ?ô³^”תT¼>ÜÎÑ ˜NîÙÇ}±Ò¶’â”p‹¤»ƒ±uÛ_bm¾ªk"{ë@¯^Ö²«0]º@ǎчöÛoðàƒàCU>5²õܽzE.‘¿¨lÍñ<ï÷Þ¡Ÿ§åóÞ¯£¾ÚŒ¨ßa>ÇV‡7¦`m¿"¶ vU´UæU€Í»¡byûm%`ZÃ-’î~ÅÖ%y`°¾ðÃs*W^yªT)ü¸‰‹>&?Ë—CÍšáçkÖ@ÆџGDDD$_[—]8Ø'Ïë5Ñ_„*“_ä=ÇyÏÛ…öâÎGE[§]ûkz%ÛIC#Ü"énCžç.º·—+gͶo/ø˜â$Û;Ûõ¯¿†3`î\hÓ¦xçù‹5„göÍ 2ÂyŸ«xÔ¶»öí.þZd=ûRØpL`9–¤k¸#Ž4Â-’îÌñ¸Ð<º·?ölÜXø19«—G«Lh׆µÑô2úT‘RÎ9j8ÇaÕð­À¡Ç»uºK¶ÁJ×K¬è«­&$žA IDATHºû6Ç〥‘¿uáB¸êª¢9&OŽ60ÉÉ9Ú8ÇãÀj` 0(’÷5ö =.± oa·Ì¨ƒ”¨(áIxÈŠÝ)Û·‡#Ž(ú¸îÝáÀ‹<,—Ù³á¾û`Þ<ØÕ*%‘´5¸›ê pt,Ož±!˜]Àä ­õŽ;%Ü"ébÖ9qQìN9gŽ%ÄÙöß~üV­‚>}l_Ó¦¶þzàÀpµñHL˜7Þ:A­Zpþù±‹[DDDJ©»°*ä‘nƒ ³c±á“S¼§{ö ÎÑÔ¹h«ñäVšaà+ä^ú-q „[$ÕmÇšPÔ>"ª*äE©_zö ?_ºÔ’í&Mà’K¬XÚªUöÚŠáÇ‘X¾<üxëVøã‚‰H9¬j¤[ù`Â,Ä+ÀaX!³£ØTs¬ïÌç˜ô[x\’œª”‹¤º÷€ €÷žØ}ÑÁÀÍ@çÞß¼à—öÝ&M‚W_…k¯…ÇnÝìµÆ!#Ã=Õ£¸Gúþû–¤OŸn#äÇù{EDDDÒTeà }èùçøhÝ8 l9™WQHšû5€ –n‘TµûØí‡M#?˜\t%ê`é×z÷†;Âû:v„ý˦”ŸyfñÎÛ¼9üýﶉˆˆˆ”fÎQø˜p² °ppÞcçÁÙóàp¬<îXì[ß:l*zg`€›â³MSÊERÕ` 7Öân /ÐØ ´¥DÉö]wåžò P£4h{ß!M¶ÿøÞx£ø×)¼'x4ÂÃpŸ÷<†rÏZ{Ï߃€gg/±d½ò^0¡26|žßV. =¼+v¿‘€n‘Ôósèç“X²}:VgòIàe FÉN?q"Üv´ic‰wö´ñHÌk•ÍÏ=צ‰䡇à“O`ûö’Å*"""’N¼ç`J‡MÁ^zÏc@WïÙzž\\²Ìó-žÛ ÆN  í7¨†g'^mÂbM ·H*Ù„M¨‹M:Z‚M#/ Ä`ôÂ…P¦ ìÜi‰wûöE'ÆYY0|8}4,[fÕÊŸ}6ÿc7n´õà={B͚Х‹UC~-âõg¼ç4ïùs>¢Ï“({OV(ñÆ9ÎÇ*ýæ.ç8 XÑJ¡”p‹¤ŠÍ@Ml¢ÑµX ËæÀdàÊØ]æöÛ­GvÛ¶öü¨£¬yaʔŋ­Ÿv… pÿý0~|þÇ~ñE¸}ØîÝ0kTVH‘ìjäEUǹ-»zyç+ £èņ5‹#9§DGEÓDRÁDà `6pvßs 6ʇêÞ:Á‚0v, Ù{žx¶l±BjÙÉz~N8~û >ýÔ¦ù¥M_)ÍBÉñ[ØZêÂÜ *êœÞ“é=©„ –‡f‘Ó›# ¶æûÆb„-EPÂ-’̲ôÂ>"Ofb#Ü­bw©¯¿†C ?/WΊ¢Ejï½m]v^?ý+B£Fá}uêX±µâV7I7¡äøoØüŪò,þ/ŠóþæÇý{÷U™·qü{HHH @„€R!HG^tQ^EÖ¾‚eUt_QV]”µeÕem,b¬‹ ,ňéEz!@L!¿÷3™d’ ÈdÜŸëšk2çüæœg†“0÷œç<nèþèaFŠãpîõàŸwfwA—¢¥.å"%Ù¸C^”&ÑÀ€TÜÐ]³hv3n$&ÂàÁî5ÖEåÝwݳÝýûçt#ÿÌø ÷K tâ‡pOÃ,ºšQ¨Olfì÷l«G®ÁÕ–ÍQØ(á.!Ç© \ƒÛµc0Ý̶ø©k\‡Ûýc0ÇÌòý‚8ŽÓh æšÙò6_ ãœrüGÃ½ÒæzÜ«xªÏÿFP¤_•edÀã»øí·á³Ïàå—á¶ÛÎ|›û÷Ã]wÁ矻gÍ‚9s 6Ön¬Cˆ‹+šö‹ˆˆˆœOÌøÉÓ <ÍŒ•žÅ¯8Û€¹… Û¹¶{ÀϲÍgÑT9 :Ã]8Žó¿¸cM¿ t^Öz–ç®»XŽÛ…äZ`:0Áqœ°ÂÂà•W Ô)þÊ\|1üå/E»o‘’@£”—0Žã„â^±Ûèé^î\|yÏnÞªâvϽ,o]%ÇqBÌìÄé¶gÈ!®{衇¨[·îénJ ’JÎW$ q+ÜÅ:6Ö…üßÿvÏ8?þ8Ô¬yv#“ûÓ«tì³g\óÊ+îÀj""""R|Çé´)`õ•¸§ƒä,)p— žÑÆ'7ÃÍìÝ\«a~žî¹OñÔp’ºÔ„m€C‡¸.SÞ¼g€‹áO[l,¼ó C†@Ù²pë­ÙשÎpŸj½ˆˆˆˆDܲþøËr¸KÇq"É@Oà3{#OÉn܉¢òŠõÜo6³dÇq2€ŠÔzž½ &ö)r*y+KöGpÿäÍ÷Œ€iÓ–,À @П>Ýûd~®¼Rór‹ˆˆˆ'OÞÈ›9pg,пx[t~Ò¹¥À3å×t  ÐÏOØwäñ‹Ç ɳ¼bfɞǻq¯÷Ϋ á±ä´=ìȳl&îÕú$Ç‹nw™™pìXÎã_„Y³|kBCÓ¥;#î¾Nœ¢OÅæÍðüóE¿‘`Sà2ÏÈâ_M®f6©€Ò)¸g©;åyîÀyêz8Ž•«.¸"O§mžû;(Ò@}2™™î”[×_ééðóÏ0lt뉉”Øý‡…Á7ßø†yÇÒ¥}ëzõ‚çž l[DDDDD‚A]ʃ¯?î\ÛÿÚ9ŽÓ.ÏúÙf¶˜¬Æ:Ž3Ø Ç,­[®úW€?SÇywŽï7<Ï}5/D pw¤ñW€¸ì<é3ÎZvØž<Ù}œññ`æ>^¾jÔ€  zõÀµ£~}·Ky§NP¡‚ûsÕªðå—îœß={§Ÿºá\DDDDä|£À|·{î»xnyýXdfæ8ÎÕÀ'¸áÜÈÿkf›²‹Íl—ã8Ý€€žÅë[̬˜Î­ à^“½wÚ¯‘¸_ƒ´Êv·f¾a`ï^÷–[Z¼ù&üýïmOýú0w.”/Ÿs¶»W/wY³f Û""""rþRà23ó² ªÝ ´?ºï€šgÓ.)/ceÀý¸3ªßŒÛé?ÄíFýþûn7ë뮃Fà“O`ËˆŠ‚±c¡qcNköôÝ»á·ßÜP[»¶;YîÀíOH´lyv/ñtÕ«—Y›‚&¡9Oèn‘¢6È*ãöa0`Ј¡oÁð÷a°Ö`Ô—pdzðÍwÙÏG ýÿ‚d¼“5ìÝ <âΕ½p¡ï.áŠ+àÿp?ø ôèqòfÞ{¯{¦YDDDDDC[¤(†â^LàŽN^Þ}œ ‹ŸzS‡ÃwßåùnÊ–…-μ™Ù¡;5Õ ø­[»!»C…m‘@Ón‘Ó•å¹OÞ>Æí|ܶ ^{-gµã¸óg¯Z•3:¹ˆˆˆˆˆH6n¹`¥ïÅqü°RÒý× <ól?û,$%嬫VÍòKDDDDD$/n¹pä Ô‘™¾£³ü?-$FvGïÕ 23ý׉ˆˆˆˆˆä¦QÊåü· ¨ –N®Å¿ÙScR ¹Ö;æö*èÙ֯ϙc[DDDDDäTt†[ÎoI@}`:8y®Ñ.ç¹O¶çzœí»ï|+l‹ˆˆˆˆHa(pËùé.à "Ðø[þϴٔ꿻G•*ˆˆˆˆˆˆœ1n9¬Ç ܾãÙ!ûqÏòý…Û\ãÆEÕ0¹)p˹/{³»=??,¾e)0·&dý”Ö‰ˆˆˆˆÈJƒ¦É¹í}àŸÀ"à¯@]`иŽü®Xã™R»7$‡}ûN½ÙÄDèÑ.p͑󟷜{¶ÿž:w_½€ÀÓ¸û (÷+$>¿üýçÂØ±p÷­îä©^æ| h49 êR.玞{xø ¸øð4¬^ ÷ì‚-{q‡¯N7xúi÷i‡ÁªUðùçaaðÊ+°`ÜíéŠ^½:Ì™£ÉEDDDDäìé ·œR€&ÀDàZ`0¸Ô…×o†·ÖÀ'`‹åÌ©}Ýuв%T¬×^ Íš¹¡ÜùµZ·†† ¡G…m):Ã-%ÛmÀg¸swý‰œ‘LJ+p§þª¬†^£ÝU‡ÁK/ùnföl˜6Í Ûàíì°íÞ{¶EDDDD¤è(pKɳøÉósyÜk² xXûÆÂ#à‘Òp$ÂSw)\s{&àÕW!%%g“eË""""""R¬¸¥äHõÜÿ BNÈ^ƒ{½v ` ¤~ÿþ7Œ> £fûnâ©§ kW÷lvùòÅ×t‘¼¸¥d ´Æ ÙÃqCög@ 8ú?p"»+ùëð´há>|õUHJÊÙLÏž0s&´Ó”^"""""d Ü<[€qCö5À¯ÀÜ3Ùw‚€ûî…&ŸÂøDÏs<Ãüe<~äLšT¬­9- ÜRüVY@ð&0¨†;ŸöH¼g¹š°ñgØ|†NÉ&{Ș1#gJ/‘’D[ŠW Ð ø¸¸ x È‚}!k ð£gÝWðÐßݧ9£Gûnê7 {÷bk¹ˆˆˆˆˆH¡(pKñ¸˜„;êø`rÎd?l‚oÿj^ Âà@ýœ§uïmÛº?ÿð˜s»EDDDDDηÎ/À<ÏÏ‘²ÿ؄ە<Ñ-!=Öþ£FùnæÅáóÏÝÀí8ÅÖz‘³¢À-E/{þëqÏfgáNïµ ø EÂO!}ާn ´ íÛ»ÿõ/Ø·/gsmÚÀõ×SÛEDDDDDŠˆ·­·q§÷ÊÂ=“½˜TîŒ' V-h»žÊ3OöÈ‘î}BìÙS|M n9{[!¸!û`îõÚU<ËGzÖ ƒ° qc÷áë¯ûžÉîÐfÍ‚+ I“â} """"""EM[ÎÜàP·ûøD ¸ø;N„ÁšR¸ÝÌã€+੧ܧ§¦Â /øn²K]§-"""""ç‡Ð`7@J‡÷–ÞÇ„÷îÏÕ\ õýÔßÜtþܲŸúâv%þ~ ŒžeÊÀæ ÷„7@§NîÙìíÛ!11€¯KDDDDD$ˆÓÝ»ÃÌ™›6ÁEåÔgfB¨F ‘󛺔K>:Ã-ù$g^>©Pà>9ŽÓÔqœŸ€tà€ã8“Ç©ìv‰ˆˆˆˆˆ\H¸Ï3ŽãDŸÕ{<·VÀ÷ŽãD³m""""""îóÏÀÅ@S3{ËÌ&}€ú@ß ¶¬JJJb„ dee»)çŒeË–ñÃ?»~9r„ &žž즜3V­ZÅìÙ³ƒÝŒsÊ´iÓØ´iS°›á׆ ˜1cF°›qN™5kk×® v3Ω©©L˜0ÔÔÔ`7ůyóæ±bÅŠ`7ãœqüøq&L˜À¡C‡‚Ý”sƦM›Jôå6ŽãÜâ8Nå`·Cr(pŸúßšÙ¾ìf¶ØÜ´V•P¿þú+ýúõ####ØM9g¼÷Þ{¼øâ‹Án†_»ví¢_¿~úàP“'Oæé§Ÿv3Î)=ô3gÎ v3üš6mÆ v3Î)?þ8_|ñE°›qÎØ¿?ýúõcÿþýÁnŠ_Ï=÷üq°›qÎHMM¥_¿~lݺ5ØM9gÌœ9“‡z(ØÍ8™O€Ë‚ÝÉ¡À}q§Pð7¬ìzÜ3ß""""""R BƒÝ)R1žûƒ~Öâ »ÁÉ“'¸.66–°°°Ân²DY½z5óçÏ?ç_KqÙ½{7III%²[ù¶mÛX´h±±±gµ­;w’œœ\"_gQÚ¾};)))çýë,Jiiilܸ±D¾g›7o&55µHÚ–ššÊæÍ›Käë,JGŽaëÖ­çýë,*Ù³˜,^¼˜;w¹5ù%''³sçγþ÷ÿLf_8ŽóPè Œ4³UAm™ˆˆˆˆˆÈDûº†[DDDDDD$¸EDDDDDD@[DDDDDD$4hÚÄqœV@3{¶€õ½€-Z´?~<—]vY¾š´´4>úè#¶lÙBƒ èÚµ+U«VÍW·k×.&L˜@JJ -Z´ K—.”)S¦¨_’H¾þúköîÝËwÞ™oÝñãÇ™8q"k×®¥N:téÒ…š5kæ«KJJâ£>bß¾}$&&Ò­[7Ê—/Ÿ¯nÍš5|ñÅdffÒºuk:wîLHHH¾º™3gòÃ?C‡hÚ´iѼX¹à½ûî»ÄÆÆrýõ×ç[—ššÊ‡~ÈöíÛiذ!ݺu#..._ÝöíÛ™8q"G¥eË–téÒ…ˆˆˆ|u ,`Ö¬Y„‡‡Ó®];Ú¶m›¯&++‹©S§òË/¿P½zu:wîLݺuóÕ=z”?ü;vШQ#ºuëF¥J•Îð]ɱ|ùr¦L™ÂÓO?],ûûúë¯Y´hqqqtêÔ‰Fù­›={6?ÿü3´oßžŽ;KûäÂã8N_ ÄÌ>.†}EâÒœ¬f™ÙÞ\ëÛõN²‰ef¶<°­ "3Óí¸¿ë X?0`SDDÄö7nœå–––f—]v™vÅWX\\œÅÅÅÙ’%K|êÖ¬YceÊ”±ÐÐPkÛ¶­EFFZË–--))ÉDŠÃÂ… -""®»îº|ë²²²¬C‡X£F좋.²råÊÙ¬Y³|êöìÙc•*U2Çq¬uëÖeõêÕ³mÛ¶ùÔ}óÍ7beÊ”±Ö­[[hh¨Ýpà –žžîS7|øp¬zõêÖ°aClÔ¨QEÿâå‚óé§Ÿšã86tèÐ|ëŽ=juêÔ1Àš7on+V´øøx[¹r¥OݲQ QIDATeË,""ÂJ—.mmÛ¶µˆˆkß¾½:tȧn̘1XÅŠíŠ+®0ÇqìÁ´¬¬,Ÿº›nºÉ«[·®%$$XXX˜Mš4ɧæðáÖ`€µhÑÂbccí¢‹.²Õ«WÑ;#ª½{÷Z5¬jժŲ¿{î¹ÇKHH°ºuëZ©R¥ìÍ7ßô©IMMõþ^4nÜØâââ °x XÚ(Å&ÑJÆçþn@&0¾ö¬öäˆ%À>ÏíŠ\5oyÖt{<ØïY@ߣ`7@·bøGvé¶zè|Û³Þ€€P3K2dˆ•*UʶlÙbÙ†n±±±öí·ßš™YJJŠ5oÞÜ.¹ä¬¬,»òÊ+­yóæ¶nÝ:33[¹r¥ÅÄÄØwÞi"öÞ{ïYLLŒ~÷˜1c,,,Ì>ýôS33KOO·=zXll¬ýþûïÞºÿùŸÿ±K/½Ôû…Ò¶mÛ¬FvõÕW{kŽ9b5kÖ´k¯½ÖvïÞmff3gδŸ0½téR µ'žxÂÒÒÒÌÌì‰'ž0À~þùç¢ä‚™™i/¼ð‚…‡‡à7p?øàƒV¥J›3gŽ™™%%%YãÆíòË/÷ÙN³fͬmÛ¶¶qãF33[²d‰•+WÎî¿ÿ~oݶmÛ¬\¹r6xð`KII13³·ÞzËûì³Ï¼uS¦L1ÀÞyç;qâ„8qÂn½õV ³ß~ûÍ[wß}÷Y||¼Í›7ÏÌÌ8` 6´¦M›Ý›$œï¾ûÎ.½ôRŠ,pÿý÷¶`Á¿ëæÎkŽãبQ£,##ÃÌÌî¿ÿ~sÇ6lØà­{þùç ðù¿çÞ{ï5À¾ÿþû"i§”A Ü€Üñ|¶_Û zµ Xÿ t÷<Ž~~%gF¬Š@-?·Ÿm@\0ß·€ÿ»»ºø^ðüÂ͸+s-Küí·ß,$$Ä}ôQ33KNN¶ÐÐPûóŸÿl¹Mž<Ù›1c†™™}õÕWØÔ©S}êî½÷^+S¦Œ%''›H ôîÝÛëÛ·¯U«V-_à>qâ„ÅÄÄØ7Þè³|Á‚x{u,]ºÔ{ýõ×}êž}öYsÇ6mÚdff/½ô’¶jÕ*ŸºnݺYíÚµ½_D]{íµo™™™ÞšßÿÝ¢¢¢lÀ€Eóâå‚’––fÍ›7÷žÙö¸÷ìÙc¥J•²aÆù,ÿý÷}>äOœ8Ñ›>}ºOÝ€¬|ùò–ššjffC† ±ˆˆˆ|g½¬[·nÞÇõêÕ³¶mÛúÔlÞ¼Ù{öÙgÍÌlçÎVªT)ûë_ÿêS7nÜ8 7"'3räHsÇZ¶liW^yåI÷±cÇlÙ²eöË/¿xƒrAÚ·oo×\sßuíÚµ³ºuëú,;xð ………y'ÓÓÓ­jÕª6hÐ Ÿº}ûöÙÀmæÌ™§óòäÜìÀ=Ïó¹ÿmOè’Úò@ vv0. .Ò³Íûý¬«d¯åY~£ç9ÝO²ÝÁž³ð-ƒùžÇMƒ¦ÿj÷q»wøð\sј‘{yåÊ•iÒ¤ .`Ñ¢EdffÒ£GŸçwëÖ À[÷ã?ø­ûý÷ßY¹re¼$ÿöïßÏĉ™0a‚ßkO×®]Krrr¾ã³uëÖDEEÖqlf,Z´È[wÑEѰaÃ|u›7ofß¾}Þº.]ºø\×I»ví¼û)Œ´´4J—.ͬY³x饗üÖ,\¸¬¬¬Óú»]ªT)ºv횯.%%…µk×zëZ·noƒ®]»z·uàÀÖ¯_ŸoŸ \rÉ%Þº œVÛD ã×_eôèÑüðÃT¯^½Àº·ß~›ØØXš6mJbb"•*UâÃ?,ôþ222øùçŸéÞ½»Ïò˜˜š5kæ=Ž—-[ÆÞ½{½Ç{FF©©©ÄÅÅñïÿ;ßïžÈYˆ®5³»pÃl>Žã”qçMà0جv'ñ ö×( |›gùLÏ}ëÚ¼Œ1³Eg°ßsŠ÷ùïf3cf' X;xÞ¼+âââØ³g;vìÈ7˜Mtt4aaa>u‘‘‘ùHË '»N$¾ÿþ{n¾ùæ×oß¾Èg/Û»wïIë²ãÜuþ¶•»îèÑ£$''X—½-‘ˆ‰‰aþüùtîܹÀš‚þng?Î>övìØALLL¾þòï;vì(ð8>rä©©©ìܹÓï>³ëroË_]Þ}ŠÆøñãyøá‡)]ºt5~ø!ƒ¦OŸ>,^¼˜Ý»ws×]wqûí·3cÆŒŸçϾ}û8vìØ)÷ìß‹¸¸8z÷îM… ˆŽŽ¦mÛ¶Þ/´DŠH33ûÏ)jÞúãŽñÔ÷¤Ü,Çq*r5<÷ûs/4³#À1ÜœáÏ¿€ãÀ…Üß9Iû'‘}ªâ`Þ±±±üöÛo¤¤¤x—ª® À['Žãœtý©ŽãìG)))”*UŠ *ä«|êNv¼ïÝ»÷”ûLNN&##ã¤í9‡ò{¡¡¡DGG{ãC‡ò863>|Ê¿ïí3{Yî}ú« §L™2 ÜrFNõÀc=F57nÍš5#>>žÑ£GS§N†À† ¨Zµª÷¶páBfÍšå³l÷îݧýJvà4hûöíãí·ßfèС¬^½š:pð`¾`"gäTŸûÇiˆ;šø'föŒ™í0³5À¸×Y?ì©ì8Î^Çqö[<Œ ¢££(W®ÇŽË·¼uÕÞ:‘`È>ŽýÜŒŒ ïwåÊ•#++‹ãÇæSx»Ô–+W®Àme×jŸ‘‘‘'=#r¦¢¢¢€‚½ìã8**ê”DZã8”-[ö”u…ÙçéÔ‰¥ýû÷³sçNºtéÂ;ï¼ã³.>>žE‹‘••E… øÓŸþä]÷ÑGÆM7Ýä]V¶lYŽ?œú8Îý;2þ|n»í6Z·nÍM7ÝÄk¯½Æˆ#Šò¥Š¤)îÀjGÇ’g]2ÐÄóój`¼ççPÜ þî(äàÎ|99"çÃRŽpÀ_ ~÷ì÷k…lû9K[v{îó}=›””DíÚµ¨V­@¾oa³²²8tèO]rr2fæóMsRR€·N$.ºè" çxÌ-))‰6mÚøÔQQQ>ËÀ‹ÃqœSïÙד÷ïßß§æÚk¯%::š¥K—žÝ 9}yçY·8`f??€w¼§‡¯Ì,oHö›#Ç)…; Úæ<ËÃqG<ŸffþΊŸ—Ô¥ügf©@ P3ﺭ[·zrvÙ¶m›OÍöíÛÉÊÊò©;qâ„÷?µÜÛn ®ìùyãÔÔTöïßŸï ¦¼uyãjÕª±{÷n233óÕ•-[–*Uªxëòn+»N¿(ýÝÎ{_tÑE=z4ߪþêNuW®\™ÐÐÐ|uYYYlß¾ý”ÿ§èÿ ¤ì¿íþóŸY·nß[åÊ•O{{aaaTªT锿Ù;»ÇS¶BCCó]¾$@Ù¹¯™Õ÷sëUÈíeàÏ›#.ÆÍ™[ò,ïˆ;mؤBî眦À-S€^Žãxûµ.[¶Œ­[·rå•WФIêԩÔ)S|ž8uêTBCCiÙ²%½{÷&$$Äo]BB‚÷?;‘`¨R¥ W^ye¾ã󫯾âøñãÞã½G”+W._Ý”)Sˆ¥AƒôéӇÇ3sæLŸº©S§Ò¶m[ïã>}úðßÿþ×{½¸×ô-Z´È»O‘¢Ö²eKªW¯î÷8§yóæÜpà 8Žã·®AƒÄÄÄpã7²bÅ ~ýõWoÍï¿ÿÎôéÓ½ÇqHH×_=Ÿ}ö¹/%üî»ï8pà€·®M›6ÄÇÇûÝgDDW\qE½ "9*W®L5˜2eŠÏñyâÄ ºtéB—.]ÈÊÊÊ÷¼°°0ŸË‹rëÓ§ÿùÏ|.§[½z5ëׯ÷ï­Zµ¢Zµj|ýõ×>Ï5k䪫®*Š—'r:–âN×uSî…ŽãÔrg£ã8# xÞ1üz¾ØüOžå7zêʳürÏý.$Áž—L·â»Óð?wÜ.$S€üSƒ ¬mÛ¶Þy„ÍÌÞ|óMì©§ž²Í›7Û'Ÿ|bQQQöØcYn}ûöµèèh›8q¢mÞ¼Ùþö·¿™ã8öí·ßšHq©]»v¾y¸Í̾üòKìþûï·7Ú×_m•+W¶;î¸Ã§nèСfo¿ý¶mÙ²Å^}õU µwß}×§®iÓ¦V½zu›9s¦mذÁ dááá¶nÝ:oÍž={,""Â:wîlË—/·Å‹[Ë–-­~ýú–žž×/ެ¬,¿óp›™½üòËVªT){þùçmË–-öþûï[™2eläÈ‘>u×]wÅÄÄØÔ©SmÓ¦Mö裚ã8Þ¹ºÍܹããââ,11Ñ~úé'[¹r¥]}õÕV¥J;xð ·nÑ¢EØm·ÝfëÖ­³9sæXBB‚õèÑÃgŸ£F²R¥JÙèÑ£mË–-6~üx‹ŒŒ´çž{®ˆß!¹õíÛ×ï<ÜŸ|ò‰Ö­[7ûòË/mÙ²e6hÐ ïç›ÂÚ¸q£…„„Øu×]g«V­² X£F¬E‹–™™é­{íµ× °{ï½×V­Zeÿþ÷¿­N:V¿~}KNN>«×*%JPçáÎ}ý&{¼Ÿåãqô[@3 °wÔðvg°ŸÁ¸!þI ¸÷ÚígýÔŽ~Jûý)Ö‹`7@·büÇ. p{Ö] ìõüÂXãÆmýúõ–[VV–=óÌ3iÙu½{÷¶´´4ŸºÃ‡ÛÍ7ßl!!!Xxx¸=ýôÓ&Rœ Üffo¿ý¶EEEyãN:YRR’OͱcÇlÈ!Vºti,$$Äî»ï¾|ÛÚµk—uèÐÁ»­òåËÛøñãóÕýøãV«V-oÝ¥—^j‹/.š+´“î'NØO"üÔþ,ö{SÜ7ÇóâErK–»"""""ç¦À/Án„”,º†[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDä4¬\¹’éÓ§“ššêwýÞ½{™>}:ÉÉÉÅÖ¦ùóç³dÉ’bÛߙرcï¾û./¿ü26lvsDDDŠ•·ˆˆÈix饗¸úê«yä‘Gü®Ÿ;w.W_}5+V¬(¶6ÝsÏ=<þøãŶ¿ÂÚºu+‰‰‰ 8#F(p‹ˆÈG[DD¤Þxã æÌ™ìfœ¦M›ÆÁƒùì³Ï8|ø0×^{m°›$""R¬¸EDDNSéÒ¥©Zµ*äèÑ£Ù‡™jy j « m8p€®]»l%u»""" À-""rÚBBBxóÍ7Ùºu+ÿ÷ÿwÒÚuëÖ‘˜˜È·ß~ë³ü‹/¾ 11‘mÛ¶0iÒ$Ú´iúuëèÞ½;QQQ\vÙeŒ;–¬¬,†NÍš5)W®}úôáàÁƒùöõÒK/‘@TT]»võÛ­}ìØ±4iÒ„ˆˆj×®ÍC=Dzzºwý7ß|Cbb" , víÚÔªU‹/¾ø¢À×·hÑ"ºwïN… (_¾<;vä‡~ð®ÿÃþÀo¼À•W^ÉwÜqÒ÷iöìÙ4k֌ʕ+óÚk¯°oß>† BõêÕ §qãÆ¼þúëÞüꫯҬY3Ÿëê¿ûî;½ûÎvà 70|øpÀ½¿GDEEM«V­˜:uj¯UDDäL)p‹ˆˆB¯^½¸í¶Ûxë­·˜5kVuiii,_¾œC‡ù,?xð Ë—/çØ±c€{xñâÅtìØ‘zõê1zôh"""¸ûî»éÙ³'3fÌà/ù  `êÔ©ù®Ùþïÿˈ#¸å–[xâ‰'X±b­ZµbÍš5ÞšG}”ÁƒS£F ÆÏ<À{ï½G÷îݽá599™åË—Ó¯_?êׯÏ%—\B5ü¾¶+VЮ];–/_΃>Èßþö76mÚÄUW]å é¤uëÖÜwß}ôíÛ÷¤ïÓ€ £mÛ¶T­Z•ŒŒ ºté¸qãèÔ©cÇŽ%::šû￟»îº €Ë/¿œ¥K—2wî\ïö¾üòK–/_Δ)S¼ËvíÚÅçŸNƒ øý÷ßéÞ½;ûöíãÅ_äwÞ¡bÅŠôéÓ‡éÓ§øï)""rFÌL7ÝòÞMDD|ôïßß"""ÌÌìàÁƒo5kִÇ›™Ù'Ÿ|b€Í;×ÌÌ–.]j€Mœ8Ñg;ãÆ3ÀÖ¯_offcÆŒ1À† æ­Y¸p¡eÇŽó.oÓ¦]~ùåÞÇMš4±ÐÐPï¶ÌÌöïßoQQQvË-·˜™Ù† ,44Ôn¾ùfŸv|õÕWØ|`ff}ô‘Ö¿ÿS¾íÚµ³øøx;räˆwYFF†5nÜØjÖ¬i'Nœ03³#F`iiin+û}jݺµeeey—=Ú›={¶Oý°aÃÌq[²d‰?~Übbbìþûï÷®oܸ±ÕªUË"##-==ÝÌÌÞ~ûm µƒÚ‚ °)S¦xŸsøða»å–[lÒ¤I§|í""'‘hÁÿ¯[ »é ·ˆˆH!ÅÄÄðæ›o²mÛ6~øá"ÙfÏž=½?7mÚÔ»,,,Ì»¼V­Z$%%ù<¯}ûöÔ­[×û¸R¥J\}õÕ|÷ÝwÌ›7ÌÌLzöìɺuë¼·‹/¾˜ˆˆæÍ›ç³½=zœ´'Nœ`Ñ¢EÜ~ûí”+Wλ¼téÒ 4ˆmÛ¶±yóæB¾zèÖ­ŽãxÿøãÔ¯_ŸN:ùÔÝ}÷ݘsçÎ%44”k®¹†3fðÛo¿±råJ}ôQÒÒÒX¸p!ÿùÏèС1114lØÈÈHî¾ûnFŒÁÒ¥K‰ŠŠâ“O>ᦛn*t»EDDNF[DDä dw-çwò]§}&.¾øbïÏ¡¡¡\zé¥>5ÙËsó×í;>>žÝ»w“‘‘Á–-[·‹wƒ ¼·&Mšžžž/תUë¤íܶmÔ¬Y3ߺìeg¸ónï×_õyO²Õ¨QÇq¼ûèÕ«ëׯgÛ¶mÌš5‹²eË2`À*W®ÌìÙ³9vì³fÍ¢wïÞDEE1{öljÔ¨ÁSO=E³fͨY³&>ú¨Ï5í"""EA[DDä ½öÚkÄÇÇsçw’’’â·&33Óçqrr²ßºÒ¥KŸQ¶oßžoYrr2•*U",,ŒèèhÀ(lÏž=ùnŸ~ú©Ïsý…úÜ*T¨ïÚôìýÔ®]»Ð¯#ïë¯P¡‚ß÷ôСC˜™wÙ½¾ýö[fΜI‡ £sçÎÌž=›yóæ‘ššê Ü­[·fñâÅlݺ•—_~™øøx^xán¿ýöB·[DDäd¸EDDÎPv×ò;v0räHŸuÙÝ­óìõë×i–/_îó8++‹ùóçÓ¨Q#êׯÀªU«¨Zµª÷Æc=vÒßü‰¥R¥JÞ.ë¹}ÿý÷DGGS§N3|59êÕ«ÇêÕ«óu¡ÿþûïœn÷ÑÑÑtìØ‘™3g2gκté¸S‘ýôÓO|üñÇ4oÞœêÕ«ðÃ?0hÐ vìØAÍš5:t( .¤cÇŽEÒSADD$7n‘³Ýµ|×®]>Ë«W¯Ntt4cÆŒaïÞ½œ8q‚/¾ø‚÷Þ{¯H÷ŸœœÌˆ#03öìÙÃ!Cظq#Æ Üé¹5jİaØ:u*™™™¤¤¤0xð`ÆODDD¡÷9lØ0ïèé{öìáСC<ÿüó¼ÿþûÜzë­>×bŸ©‡~˜ôôtn¾ùf-ZDVV“'OfèСԫW-Zxk¯¿þz¾þúk¶oßîó»k×®?~œ>øÀçìvdd$ãÆãÑGõ~²yófÖ®]K«V­ÎºÝ"""¹)p‹ˆˆœ¥ì®å¹EFFòÜsϱaêU«F… 4h/¼ðB‘î»G|øá‡TªT‰Úµk3nÜ8žyæï l¡¡¡Lš4‰ *ЧOâã㉋‹ã‹/¾à™gž¡W¯^…Þç<À}÷ÝÇË/¿L­Zµ¨Q£=öüãùç?ÿY$¯ë²Ë.ãý÷ßgÉ’%´jÕŠÊ•+sÓM7ÎìÙ³}lëÕ«iiiT©R…Ë/¿p¯ ¿ä’KÈÊÊò ÜÍš5cĈLœ8‘¸¸8jÔ¨A:uˆ‰‰a̘1EÒv‘lŽ™» Rò$˂ݑ’dݺuìß¿ŸöíÛû]¿iÓ&vìØAÓ¦M)_¾¼wù¯¿þÊ÷ßOLL ;v$$$„¥K—ÒªU+"##Ù½{76l M›6„‡‡{Ÿ7wî\jÖ¬IBB‚O’““iÓ¦ ‹/¦|ùòÞÂRRR¸êª«ü^C‘‘ÁüùóY³f *Tફ®òv³Ø·okÖ¬¡Y³fDEEö{òóÏ?SªT):uêDµjÕ|ÖoÛ¶-[¶pÕUWQª”ÿïø=ÊâÅ‹iРUªTÉ·~ÿþýÌŸ?Ÿ½{÷Ò¶m[5jä÷ úüùó)[¶,Mš4ñ.[µj䪫®ÊW¿iÓ&æÏŸÏáǹüòËiÑ¢‘‘‘§õºED Ðø%Ø’E[üQà)nÉG]ÊEDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$¸EDDDDDD@[DDDDDD$þu!¨#aøßIEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/read-medium-psyco-nopsyco-comparison.svg000066400000000000000000001357311231437614300311170ustar00rootroot00000000000000 SVG drawing This was produced by version 4.1 of GNU libplot, a free library for exporting 2-D vector graphics. 0 200 400 600 800 1000 1200 1000 10000 100000 1e+06 1e+07 Speed (Krow/s) Number of rows Selecting with medium record size (56 bytes) No Psyco Psyco PyTables-v.3.1.1/doc/source/usersguide/images/seq-chunksize-15GB.png000066400000000000000000002113751231437614300251440ustar00rootroot00000000000000‰PNG  IHDRÐß}™SsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwXTGàß"½#UP°ÒA Fņ]cà ‰-hb0v£“¨QQ£1Fcš-cc!"‰ÞC”.½·ùþ€½ë.,K<ïóì“0wî¹»ër˜=3ÃcŒ1B!„BEª½;@!„BHGB4!„B! šB!„ PM!„Bˆ(€&„B!D@B!„"   !„B‘ЄB!„H€hB!„B$@4!„B! šB!„ PM!„Bˆ(€&„B!D@B!„"   !„B‘ЄB!„H€hB!„B$@4!„B! šB!„ PM!„Bˆ(€&„B!D@B!„"   !„B‘ЄB!„H€hB!„B$@4!„B! šB!„ PM!„Bˆ(€&„B!D@BšŒ1†´´4>>íÝBH šÒ$çÏŸ‡±±1ºuë{{{XZZB]]ÖÖÖ¸téR{w¯M?›7oyìîÝ»8tèÊÊÊštm:tEEEÍéb›KMMÅ¡C‡ÒÞ]iùùù8tè&L˜€»wïŠ<ÿôéÓxë­· §§‡I“&áÚµk¸t霜œÏÕÛ½{7¦M›&t~EEœœœðõ×_ »ÿ>fÍš…Þ½{ÃÐÐS§NE`` @¢¢"899á»ï¾C`` fÏž XYYáË/¿DUUWwõêÕ¸yó&€ÿËNNN¸ÿ>à»ï¾ƒ“““@ FLL Ö­[‡B[[FFF˜8q"þùç‘ÏGc-_¾k×®Ezz:–.] sss˜ššrÇÓÓÓñÞ{ïÁÆÆÚÚÚxë­·ðÓO? ]çŸþÁòåËaee…®]»ÂØØóæÍCLLŒÈv=z„É“'COO#FŒ€———DýÎÎÎÆ‘#G0kÖ,èèè@__Æ Ã?üÀÕ‰ŠŠ‚““®_¿ŽK—.a„ ÐÓÓƒ££c½)C999øðÃaggmmm >GY·  6l€½½=´´´0dÈ8pÕÕÕBucbb0kÖ,èëëÃÞÞ»vícL¢{&„tl@B$Ö³gOäççãøñã"ŒWýúë¯ësïÞ=x{{c̘1HIIÁ¬Y³0aÂ@@@¬¬¬ðÃ?@__+W®Drr2\\\°iÓ&ë?~¡¡¡prrÂÖ­[annŽ«W¯¢ÿþˆŽŽ¨ëëë‹‘#G"44+W®Ä¸qãàéé‰Ï?ÿ¼Ñýž5kÜÝÝ¡¨¨ˆ­[·ÂÝݽzõx.rssqãÆ |ýõ×X±b €µk×"33o¿ý6Ž9"pͨ¨(XZZâðáÃÐÔÔ„››²²²àææ†÷Þ{O nrr2ú÷ïýû÷C^^ï¿ÿ>÷þ›?¾@Ýàà` <~~~puuÅÌ™3qöìY¬Y³¦Ñ÷Ké!„H诿þb222 300`|ð»p᫨¨ª›••Å444˜……+,,äÊKJJ˜……ëÖ­+**bŒ1–‘‘Á”••™­­-+--åêÞ»w`X`` W>oÞ<&'''ÔfII ÀV®\É•ÅÅÅ19996räHk0]]]fffƪªªcŒåääpíýþûï\ÝŠŠ ¦¯¯ÏTTTXuu5WîââÂêû8ýðÖ““Ã•åææ Õ+((`ªªªÌÌÌL |Ó¦M KMMyýºLLL¶qãFòªª*fggÇäååYbb¢@ùœ9sX—.]XXXXƒý e˜³³3WV]]Ílll˜’’’@ÿ222˜ºº:ÀŽ9Ò`ŸSSS¶zõj¡cü׃1Æî߿Ͻ&!!!\yaa!366fêêê,;;›+=z4“‘‘aÿý÷Ÿ@—.]Êx<{úô)W>g΀=xð@ ý-[¶0ÌÇLJ+?~<ãñx,::Z ýúõc˜‡‡Gƒ÷Kéhš"±qãÆáÙ³gؾ};ÊËËñõ×_siûöí%þí·ß““ƒµk×BII‰+———ÇÂ… ‘ššÊ¥P\ºt ………˜1cää为000hVŸøá”••aÆ ×VVVƼy󉄄sôôô0sæLîgiiiŒ;ÈÊÊjr_ø£ÓPYY‰ŒŒ búô鈊Šjvù‚ ~þ÷ßñôéS,Z´FFF\¹””ÞyçTUU ŒÀ×í_YYÒÓÓ¡¥¥{{{qÇÂÂÂèééqåÚÚÚpttlT_ÕÔÔ ªªŠøøx¡•F¤¤„E™˜˜ÀÊÊŠûYII S¦LAnn.®^½ÊõË××óæÍCß¾}¹º<K—.c ÞÞÞ€ŒŒ œ?cÇŽ…½½½@[®®®À„gff⯿þ‚µµ5Œú0iÒ¤FÝ/!¤s I„„&éÖ­>ýôSlÛ¶ Ož<ÁùóçqòäIlذ·oßæ~ÞìùóçqåÊk¼|ù@Í×íÇçò›ÇŽ+ÔÞ˜1cDæë6VLL x<Ž;†ï¿ÿ^àXJJ ×>}úpå“&M âlllðÓO?!;;ÚÚÚMêKqq1öîÝ‹S§N!))I( æÅ‹ýD·nÝп2~ÚEHHf̘!p¬²²Ç•¥§§c×®]8þ<222ò{•••¹ÿç¿^£GêÇØ±cqñâE±ýUPPÀÊ•+ñÕW_AWWS¦LÁ´iÓ0gÎÈÈÈÕ¯¯­pýáßodd¤Ðý5tTT ""––&²®ŒŒ ÷Ü4t¿£GÆ¡C‡ÄÞ/!¤s šÒ,²²²6l† †;v GðññA\\úôéƒÜÜ\@÷îÝ!--ø‘£§§ ôêÕ ¸ÍBD6K2ÍDLèÊÍÍ…´´4ºuë&tLOO¶¶¶ÐÕÕ(WQQªËãñêm£±V®\‰_~ùï¼ó·†¶¦¦&¼¼¼päÈ|iI •ñ_}}}¡{ä÷gРAjò¿§L™‚¬Y³–––°°°€²²2ÜÝÝñ÷ßsçñ_/}}}¡kJòzíÝ»óçχ§§'®_¿Ž³gÏbÆ øê«¯„FÓj‹ßþýêè茌ó­X±666þÿGœ®®®È÷ÆÒ¥K¹Ñæ†îWT!¤ó¢šÒb444àää„ . !!}úô‰‰ ÀÝÝ––– žÏ¤ïܹÃ}}ÎwçΡúŠŠŠ(//Gnn.ÔÕÕ¹rQ›Y˜˜˜àÖ­[صk´´´$½µñƒêÆ(++Ù3g0xð`œ8qBàØ««´þk0mÚ4¼óÎ; Ö Á“'O°|ùr|õÕW\yUUž>}*P—ÿzݽ{‹/8&êõjÈ€ðÓO?¡²²·o߯ªU«°qãF8;; | p÷î]lß¾]d[üþðïwܸqX»vmƒíòë>;wîl°nÝûݰaƒÀ±úV”!„tN”M‘XzzºÈòÂÂBøúú—{Êÿº»¾åÃê:t(pËÂñåææâÉ“'Bõ ÁƒŸŸŸ@ù™3g„êJÒIñsrÅÖ}ùò%*++ann.PþâÅ <|ø°Åûƒ†’’¾ÿþ{±«¦ð_[ r???¡¼oKKK¨¨¨ˆ –_} KZZ&LÀܹsñâÅ „…… ÿ矄vuä·Åÿôïß]»vʼn'ÄŽæó—UüùçŸQ\\Ü`]###èëëÃÏÏK}á“ôBHÇF4!DbÆ ÃôéÓqæÌ„„„ >>§N¼yó±cÇ¢gÏžjFö–,Y‚o¿ýîîŽFii)’’’pùòeÌœ9“›t8lØ0Œ?çÏŸÇáÇ‘ŸŸØØXÌŸ?ªªªBý˜:u*à³Ï>õkׄ÷Þ{>>>BuùKº}úé§Ø±câââPZZŠ„„œ?Ó§Ooòó1qâDÀ¶mÛpùòeܽ{—K x•¾¾>ºwgÏâܹs(,,DXX,X &÷¡!:::øì³Ïpÿþ}̘1OŸ>Eqq1RSSñ÷ßcÙ²exô耚o999xyyáæÍ›())ŸŸÜÜÜ ©©)p]555¬]» øàƒššŠŒŒ ¬[·©©©êÛ½{÷°bŠܽ{ÙÙÙ(,,Ä­[·pòäI¨«« Ljr¦.\ˆÄÄDäææbïÞ½ðññÁ´iÓ`kk  &õfß¾} ÃäÉ“ñèÑ#!==þþþXµjnß¾  &é›o¾ABBÆ{÷î!??YYYxðàÜÝÝñ矨 î·mÛ†‚‚¸¸¸ )) 999Ø»w/?~ܬ׈ÒÁ´ë „éý÷ßgÚÚÚܲbü‡ŒŒ sqqXNŒ1ÆJKKÙÇÌ-}W÷aggÇ-cÇXͲw ãñx ›9s&Û»w¯Ð2vŒ1¶sçN®Ö£G,´ŒcŒåçç³÷ߟIII ôAJJŠ9’«Ç_ÆîÃ?ºwOOO€EFF ”ïØ±ƒÙÙÙq}¹técLô2v·nÝbZZZ}X±bûüóÏ…®-é2vööö"UWW³cÇŽqËËÕ}ôéÓ‡…‡‡su?Îäå垟0¡e+**ØòåË®gnnÎþüóÏF-cwÿþ}¦¦¦ÆËãñ˜¬¬,sttdþþþõ°C‡±É“' ¼?Æ'ðüòýòË/"ß§FFFìÑ£Gu/^¼Èôõõ…êêëë³;wî<Û·o诡¡!»sç-cGÈ„ÇmŸD‘\uu5"##‘””„üü|tïÞÆÆÆ ®L‘——‡ˆˆ$%%AKK ¦¦¦"'›UUU!<<qqq°³³ƒ¡¡!<ˆµk×"00›Æàà`ÂÆÆRRRxöìTTT„FMš4Šˆˆ¤¤¤@GGÐÑѸ·¤¤$¨ªªr›¤ðåçç#;;"W‰ÈÎÎF~~>ttt ¨¨Èýlhh(Ë›——‡G¡¢¢ƒ†––òòò““#p휜äåå¡GèÒ¥K½Ï-P“"%%%r2_II "## UUUôêÕ‹Ë®+-- Ož<¢¢" dee¡¨¨H`)<¾¸¸8„„„ÀØØæææ(//Gjj*455ENƬ«ªª !!!HJJ‚œœÞzë-(** Ôyðà†Š#GŽà½÷ÞCBB‚‚‚`ll 333‘KÞ59çQQQˆ‰‰’’zõê333‘uËË˃èèhÈÉÉÁÈÈ–––"sÜŸ?ާOŸÂÐÐÖÖÖ`ŒáùóçPWWÈÇ'„tN@B:„†hÒù½@BH{¢hB!„B$@4!¤CPUU…‘‘deeÛ»+¤ðS*Ä¥ƒBH[èT)%%% B@@ ðî»ï ä5ŠrøðaÀÍÍM`ûZ f;ØóçÏ#99öööpuuبÙLá×_…ŸŸ4440cÆ n)%B!„ÒùtªÚÀÀ)))PPP@II‰Ø\É“'ObÙ²e¨®®FBB·ìøúúbêÔ©4hÌÍÍqáÂXZZâêÕ«——P‡ÂÃùÏQõø»0Š:7--ûlyõ\__ß&=/m]Ö¯eCeiiixôèQ³¯'Éï>Iþ-$''sŸ- Õ÷þó÷÷oô¹YYY¨¨¨@jj*>|Ò€vÙ@¼•y{{3,00P üÑ£GŒÇã1///vúôivúôi¶fÍ€yyy±{÷î1Æ;}ú4ÀþþûoógÏžÍtuu¹ŸmmmÙÀê$$$0ìË/¿äÊFͬ¬¬Zú6[ÜüÁþøãVmÃÅÅ¥Ù×hj?%i»1u9Âîß¿_ïñÓ§O3ooo‘Ç™§§§Èc}úôiTÛ[K¼– ñõõe'OžlöušÒOIÞcí§¸~4t¼¾c›7of›7oÛv{kÖg‹wck þÿø¯Rd5úl©AŸ-âÑg‹øcVVVlôèÑbÛ~“½Q)ÅÅÅ044Äþýû¹²¢¢"Àž={0iÒ$888ÀØØŽ‘#GruCCC¹cзo_\¹rÕÕÕ\4ÿ/ÿºõ@KK«unª™šš¶z3fÌhö5šÚOIÚnLÝBOO¯Þã–––Ü’‡¯ÒÓÓÃ!CDspph\'ÛYK¼– éÙ³'ÔÕÕ›}¦ôS’÷Xcû)® ¯ï˜½½½Øv_Íúl©PRg±¨*ÑÕè³¥}¶ˆGŸ-âu„˜¥½uªeìø|||Ä.cÇwæÌÌŸ?¿U—±sttܹs§î–t6®®®«½RŸK—.hý€£]])®Ôùêy"`Ò¥ýúÓÑg i,Š[ÄëT#Ðk×®Åýû÷‘——X²d ñöÛocãÆ]ëСC˜:u*¹TF… puÆŽ‹… bîܹB©ÐЄB!S§  ‡ ]]]¡òþýû×{Nÿþýñå—_BCCC |ôèÑxðà·•÷Î;…¶òæñxøù矹­¼ÍḬ̀eË¡µ¥ …’÷ ©ÏæÍ›Û» ¤ƒè(_É“×}¶ÆÒÕÕˆwˆ°N™ÂñºquuERR}B…¾f%E)DôÙBËÑц††ô~i@§~њϤ±è‹4V§œI‹£ÏÒX³ˆGôk" ^^^íÝ Ò)))á‹/¾€ššZ{w…By#¼q©¼®¼¼¼e'¤±Nœ8ooïöî!„òÆ è6Ò˜I„ô‘˜O{w´“¬¬,´f+iœ¨¨¨6YïŸt|eee4‰P n#éééíÝBH'ãïïOß\‘FÛ½{w{wt³ˆG#Ðm„ò !-&IÐ7œ¤±(fF  !„B‘ЄB!„H€è6B;BZZVV7‘q¢¢¢Ú» ¤ƒ ˜E<  Û%äBZM"$’ I„¤±(f&¶‘æ&äçåå!''Gl=%%%hkk7©ÒÒR¤¥¥‰­'--îÝ»7© BHË¡I„D4‰4M"èbòä÷k"¶ž¢â]ÄÇû6©-[öã—_2Ñ¥‹zƒõ»‹§O¦ šB!o$  ;ˆª*9¤§"¶ž±±k“Û()©@V–;€ž ÖÓÕý•••Mnnݺ…’’L:ÁÁÁ¸yó& 0räH8::Š<'&&wîÜAll,z÷îÑ£GÃÌÌLdÝüü|ܼyÁÁÁ(++ƒ±±1¦N z!!!ðóóCbb"Œ1nÜ8ôêÕK¨Ý€€899¡°°7nÜ@bb"ÆáÇCJJ ååå¸wï|}}¡­­ gggèêê \çÏ?ÿ„ºº:pçÎüóÏ?PQQÁĉann.P722ÁÁÁ˜dˆ ˜Ø‡±qý×gåJ$ˆmCW׃%$$4¹Æ³··gFFFì믿f<iii1 Û¶m›P}///&''Çdee™¹¹9“——g222lß¾}BuoܸÁ fddÄLLL˜œœ³±±¨·cÇÖ¥K¦  ÀÌÍÍ™ŒŒ STTd'Nœ¨çééɰ~øihh0555&##ð>úˆ•••±±cÇ2Ç455Ö½{w–œœ,p]]]6nÜ8¶dÉ&''ÇÌÌ̘¬¬,“‘‘jóË/¿d؉'˜ŠŠ SRRbÌÛÛ[è~uuuÙéÓ§ýÜ“Î#33“eff¶w7Z×å2Æ–çÿÿUÙÞ=ê°"##Û» ¤ƒpvvn0&!ŒÑ$Â6ÒV ùÕÕ@nnÓååmÒE΋/pìØ1\¾|ééé …­­-öìÙƒ¤¤$®^@@>üðCØÙÙ!==áááHOOÇðáñ~ýzITÏž=ÃÌ™3¡ªªŠððp$&&"** ™™™ðððàêy{{ãÓO?…““^¾|‰ððp<þÆÆÆX¾|¹ÈÙêëׯÇîÝ»‘’’‚„„Lš4 ĸqã```€èèh¤¦¦âðáÃxþü9öíÛ't;wî 11iiiˆˆˆ@JJ ììì°bÅ ÄÇÇ Õÿè£ðóÏ?#??ÙÙÙ=ztsŸvÒ‰Ð$B" šDH‹&ŠGti«„üØX@C£i“'Û¤‹œÊÊJlݺS¦L””,--áêêŠŠŠ |ø0àÃ?l¥»¾ùæTUU¨™HyôèQÀêÕ«[­MÒ9Ñ$B" šDH‹&ŠG)Ä̆¿ÿbëõì)z’ÆX²d*€+bë)(hCOO¯ÉíHÊÉÉ «W¯ÆáÇñäÉØØØ 44ÿý÷–-[†Ù³gsuíììàéé‰ 6 _¿~|wîÜ™™ž>}Ф¤$lݺUh¢_K>|8ž={kkkôïßˆŽŽÆG}„Áƒ·J›„BiY@w‹MÇ¢EÓ[µaÃì0l˜]«¶Á·lÙ2 •«««ÃÃÃåß|ó ¦N oooÄÆÆÂÉÉ žžžB«x5£ËŽŽŽ8{ö,·šÆÌ™31gÎ®Ž””Ξ=‹¹sçÂ×׉‰‰xûí·1mÚ4n%¾!C†ÀÃÃCèyyyxxxp«qÔåìì,²\YYçÎÃ÷߇bذaصk—ÀàààÊm%„B^C<Æÿþš´WWW”••áôéÓ Öè+¶ÎLOO666ðññiñëhïnÔ‹&’Æ¢˜E\øï?,ôõÅØÑ£Û»;Bh4‘4Å,âQMH¹xñ¢ÀŽ…„Î#77WƒƒQ°v-¶xz¾–4!¤åP !mdذa°¶¶nïnBZÁö}û4e '‡x##Üòõmï.BZÐm¤­v"$„¼9²²²¸‰„¤ýdåäà÷À@T×n†”=e ¶xz¶s¯„EEEµwHA1‹x@·JÈ'„´4šDØ~²+*ðkz:œ#"`°~=Ò¦NýÿA99DõèñÚBÓ$BÒX³ˆG9Ðm„ò !-&¶­èâb\yùW^¾Ä?yy¨b (,’’€Å‹êN›†wöìAòk” M“IcQÌ"ЄBˆ•ŒÁ?/¯&hÎÊÂ%%Buþø¥³gChC99<ïÙS~þ—-‚4×&}&„´   !„Z9••ðÉÎÆ•¬,xgg#·²R¨Ž¹’¦ijb´´4Vfd ±6÷YÈÌ™¸¶{7¦öïsPéBÀÒYPÝF(!ŸÒÒÞˆÛÀ%%¸’•…+/_Â?/•¯lÐ+Ããá-uuLÕÔÄTMMôVP|°m[ÍÊõ‘“LLàsçF”—ãš• ääZóVD;’Æ*++ƒ\;¾W;  Û%äK.77AAA055…žž^½eYYY ƒ¥¥%äŸ@H¹Ð’©ª›šñò%bŠ‹…êt•‘ÁÄ®]1USN]»BMZø×å³çÏa ܺ%² !?¹rr¶±}@®YY¡;­¿{÷nʃ&’žžNyÐbPÝFè(¹   Œ='Ož„««k½eþþþ˜9s&þøãFG…ÈckÖ¬££cKÝ!­ŠçÆËå§f¼| ï—/‘#"5ÃXQÓjG™‡«©¡‹˜¼åËF€-ññØ“”„eeˆspêÚµ‰wÒt<“Æ¢˜E<  ;¨ªª*ÄÇÇÃÐаU¿fILL„††ÔÔÔZ­ I(++£ÿþèÚŒ_>AAAøóÏ?amm Þ+¿ ›ÛEBH+¨®f’’l"^lI 7ðžˆÔŒ.<ÔÔ¸Ô cEÅ–ì2€`wïÞè-/Õÿý‡‚ª*L Åá~ý°R_¿ÅÛ#„´  ;˜¤¤$,^³Ï Ÿ£H±òÅòÐì¢ ¯O¼0|èði£  ïº¿‹àgÁ(/€t¹4T+U±aÙ¸.pm‘6šjàÀ j‘k=|øòòò-r-BHëÉÎÎÆµ7°`î<44 ¯Š1ü›ŸÏå3G‰HÍP“–†SmjÆ$MMhˆHÍh +ôõa(/¹áá(¨ªÂ{11ˆ/-ÅîÞ½AësÒñPÝFZbáß÷þÆ’íK<(¨“B÷¬âf휅&}„k66«/^`ÔÜQˆ³‰Qg´†^øùý…ߎþÖ¬6"""àççWïñÉ“'£G"¥¤¤àòåË=z4LDÌ|OHHÀ•+Wkkk8;;CSS³Yý%äuõ¦L"ü7(寯‰ŒÀ 8–WY‰µ©׳³‘]Q!t~oLÕÔÄ4MMŒPW‡L;-)çÔµ+î €É¡¡xQV†½IIH()ÁOff—jý}Íh!i,šD(Ðm¤¹“‹‹‹±bë $;& ï)¤MÇ×~Y“g¡OŸ>MngÞÊyˆ ¨¾r€ä[çÃ;Àg.œó,ç&·(rG¬ììlÀÛÛ»Þ:&&nnn8yò¤P‚åË—CKK rrrøá‡pàÀ\¿~]d°ššŠ´´4ÂÀÀ É÷CH{y&&&&"·‹4ªõõ ‹r$—Wk'ú忢â•Ô )CUU¹Ô s%¥vê½°þÊÊx`k‹É¡¡),ÄùÌL¼(/ÇŸ––Ð’‘iÕ¶i!i,šD(Ðm¤¹oÄïú±½cÜ|ýùÀçX¹u%Žw¼ImD„F ÂÁs¹rñÕ÷_5+€^¸p!.\(P‹!C†ÀÔÔC† iÒu?ùäœ?³fÍP\L˜0«V­ÂíÛ·…ê÷îÝ›û===ìÙ³K–,iRÛ„´‡Î8ó}þ6nÜ;;;0ÆpñâE\½z...ÈËËÃ|Ð"÷Ei:à×##ƒž²²5eÚÚÈ *220’—Ç”ÚÔŒQêêmƒ4ˆ–¢Ò¥ ®ZYaUL ¾OMElI †à’¥%^“IÛ„úQÝA””–4îÕjNj_€F¤„í“'Ø“”ÄÏR<Fª«ãp¿~¸¥¢mm žTª [[ütëV‹,ú:qPSÿ OíDÂã©©˜Š|»&6Ϥ±(xèdËÚ-84ïzßî ÕUà ®C_C¬é¹N]hvŽ£ñÇWÀÂß]» €L” ºýÓ ³JfÁÿª³sŸïÞ½‹eË–aìØ±8räH³ûÌoooîç'OžàÎ;6l  æ+ïo¿ýV`”ùñãÇXµj|}}áè舞={¶XŸyÓ…a{BL=‚Í“'øâÙ3ÄÖÍ<Ô^ýúáùСøÛÆ« °}×.d@r2“óÿGl,ì;z´}o®+*â­-†ªÖ,‡ôWv6‘ÜÉþX ¤££ŽÆe æÌ˜ƒ€€< ~scs 8-ÖÆ°!Ãt3!!!xðôtµta?ÈÝ»wo‘ëŸW·{÷îðöö†¿¿?ž?޲²2ôìÙ¶¶¶˜5k¤:Ð2X„¼N“£Š‹q.#ç23^T$tÜ^Uótt0[[=ÄLNrž9óÿ?\)ªÿÿùÙÁ@À¤¡ ½;y))œ³°ÀƸ8ìKNFJYÞ ÄYssLjâîª4‰4M"dz¸pÇqùòe<|øÁÁÁ(,,ÄñãÇÑ·o_:.\Ào¿ý†ÀÀ@äçç£_¿~pwwǼyó„®wôèQœƒ¥¥%<ˆáÇ#00[Ñ¡¤¤#FŒ€‚‚<ˆ´´4lذ™™™ðòòh—ò !-­=çØ’n¤9XÄg¬­Š æikcŽŽz55h&Üôõa$'‡y(¬ªÂª˜Ä—”`oŸ>-ýOÁi,ŠYÄëTtll,àãド'Ьãç燮]»r?»ººÂÌÌ ^^^\]QQ={öÀÍÍ [¶lP³!‡ŽŽŽ;Ào¿ý†èèhÄÆÆr#Ó%%%غu+¶oßmmíÖ¼]Bi–ÔÔTŒŸ5 ~~ NŽ/)ÁùÌLœÍÈ@ ˆ ÙFYsut0W[›[A‚´¬Iššð0SBC‘RV†}ÉÉH(-ÅÏffP Ô3BÚ\§úW§ÐˆîºÁ3ðx< >ùùù\Ù½{÷‘‘Á­ÜÊÊʰ°°À… ÿ_éâܹsÐÒÒHë4hÊËËqåÊ•æÜ !„´ºu_| =uJèXbi)¾JNÆ §OÑçáClŽž­””°«W/ÄØÛ#pà@l14¤à¹• PVÆ[[X)).dfÂ1(™íÜ3BÞ<*€nм¼<øøø`̘1\YRRÀÂÂB ®••’““¹ŸSRR`ee%PÇÜÜê-;‰B€šI„ü‰„’JMMŽ””/X¯³gQYY‰¤ÒRìON†}@z=x€qqxRPÀc¡¤„={"rð`„ „ŒÐ‚æ6ÕCNþ`\íÊKòó1$ ѯ¬v"JTTTkwt³ˆ÷FÐŒ1¼ûî»(--ÅÞ½{¹ò´´4€‰‰‰@}333äææ¢´v«ÙŒŒ ¡ :´´´ ©©‰ÔÔTò+W®`þüùxï½÷111­qkä òê<`Qï+*ëœeþþþð÷÷oÒ¹ë¾øÏgÌ<=ojŠ~»v¡çƒX‡GŸ|<0UT„GÏž˜ûË/¸¨¦†={´v{í¶¸ßo¿ýyyy­ÚFG+S•–FÏ#G0«6ȉ/)Á°À@L÷ÝÏݽ{÷kuTöú”ñc ÌŸ?~~~ b°NÈÛÛ›` Ö[µjSRRbþþþåß|ó ÀâããÊׯ_ÏdddXuu5cŒ±^½z±Ù³g Ô)++cÒÒÒlýúõ\™‹‹ ›?>+**b%%%ªª*®Ž‹‹KSo™¼ÁtuuÙéÓ§¹Ÿë¾¯¨ŒÊD•¥¤¤0ý™3|}nÜ`¸}›ÁÁáÖ-__ÖïÞ=öql, ),l÷>—ý^Àª—å3¶¼öUÙîÏßëT¶+1‘ñ|}|}™ì͛엔”תTÖ1Êø1INN+**b‹-¢˜DŒN5‰P›6m‰'põêU >\àÝàˆˆôêÕ‹+ E·nݸ%êtuu*pnLL *++ÖYYY(ÖŽÚÒšäE¬|@eTV·ìí;Â_ÁCVÀ3ãž>ÅÞU«Ð_ÄfíÕgYYY€W&¶Þ›Zö±‘zÉËãèh”KKcqt4ËËñ±‘ÑkÑ?*ëeüÿçÿ·K—7k©È¦x#S8¶mÛ†C‡áÒ¥K¹Ï|vvv’’Bxx¸@yhh¨ÀƒFll,—ÒÔÝüc„ò:‰/)Á[·oãÁ‹À+ËT±‘#‘pë,hé¹g®.nZ[CCZ À¶„,ŽFEçÙæ×N§  ‹ŠŠ››‹¢Úý ››‹’’®Ž‡‡vïÞ~øöööÈÍÍå|†††˜8q"Nœ8ììl5KÖ¥¤¤`åÊ•\=777TWWãÀj’î¿ùæXXXjSB>!¤¥5va%cØ›”ËÇqïØ1@ÄÆQàñìà rEòú{K]÷mmÑ»vRç‰ÔTL A^e%W‡&’Æ¢˜E¼N@Ïš5‹Û Þzë-hhh`íÚµ\C‡¡ªª ‹-‚†††À£îHò÷ßYYY OŸ>X¸p!víÚ…±cÇruLMMqòäIxxxÀÐÐÚÚÚHLLÄï¿ÿ.Ô·ôôôV¼sBÈ›ˆ?‰°!O 0èéSlŠGIf&ŸÔ~½ÿªÒ#pèôiTÖ ºHÇa¢¨ˆ¶¶°WUÜÊÉC` ’j·ñ'"Å,âuªèo¿ýV`=g>---îÿïÝ»‡ªª*‘ç×Ý÷½[·nxòä ‚ƒƒ‘’’[[[ôèÑCèLœ8=‚ºº:ìììD®GM»úºÿ>òòòàääÔÞ]!¤Ãjh'¢ª*ìHLÄ¡çÏQUûU¾Ú… È›2± _ò€8zêÞ_º´ÅûKZŸ¶Œ |ml°(233VT„!¸je%°á“€ ´µm¿Ž’×Å,âuªºwïÞb뼺nsCdee6S©ŽŽ¦L™Òèë`çÎ â– l-£FY.++‹¿þú«UÛ&¤½ødgÃ-&‰µ#rRRØfd„ò„…5x¾’ŠJ[t“´))œ77džøxHNFjy9Þ ÂssLÑÔÄ…Ë—ñÎûï#öÉèèè´ww é:UÝ™%%%!>>^l=n3Iåææ"((Hl=yyy 2¤Im´µ»wïBWW¦¦¦íÝBZ]fEÖÆÆâ×:_¿ŽTWÇwÆÆ0QT>ú¨{GÚ’‡ý}ú ·¼<>ŒEQUf„…áPß¾øîèQìØ _~‰SžžíÝUB:$  ÛHsò¿ûüsT:S©úÓÖ Ü03Õ§O›Ôƽ۷ñ“«+&דâÂwPEO^¼€´tÇxû899 |uIHgÁŸ@¨¥¥…SiiX‡—µÛ:«KKã«>}°´[7ðÚ³“¤]­60€¡¼<æGD (!ïÿû/dÍ;}á{ý:222hš)++Hk%Â:FÔ 47!ÿÃÏ>Ãì®Ýf\”=ššØT»"HSLyûmœôðÀ¢ððzßKKcÑòå-<ÁÇǧÞãóæÍX“Ûßßׯ_GLL zöì‰qãÆa„ -Ú'B^wþþþH+/ÇÜÊÉáÊçhkë_?èÕ®ñLÞlS55qׯ#¾ü%EE(ß¾<}:B·€„„L_´ÁþþÜ>]zz:åA‹AtiîQGGZ£G#âÔ)ˆJÐ(Ô·/6Ùä6x<lÞŒ_ÜÜàZÏ$£#FF8¹uk“Û¨Oll,Μ9#TžœœŒììlØØØpôÎ;±sçNèééÁÆÆ.\Àþýû±|ùr|÷ÝwB`¥¥¥¸wï***`jj*´É !Q%cˆ±µÅ'‰‰(© ž{ÈÉá°±1¦jj¶sïÈëÆNEžK—bÕýû¨æ€áÖŸÒ(t3}ôÅøoà@ütî\D-ÙQð,ÐȺ½{±Å×?ˆ…>¬¦†ÕË—ÈanÈ, ÌÒÕÅ¢ÂB¡7ÇßÒÒäìÜ*;*Ξ=›[~/44°µµÅˆ#Ô¬ÞñÉ'Ÿ`üøñ¸téPUU|ÿý÷ptt„³³³ÀuΞ=‹³gÏr?ÛÛÛãÈ‘#0`@‹ß!máIA–GG#¨ö])«õõñyïÞP¡ÄH=¾=yÕîîe)3f`égŸáÊ×_·S¯:¶„„<)-E邨è–ÌÛiF¡IÃ(€î@ê….”—‡MË–5» €~àúʱÖ}%55“'O†šš®^½ %%%à6­ù裸å»té‚M›6á×_ÅþýûèÙ³gcúôéèÕ«ÂÃÃqîÜ9üý÷ß5jBBB`TÏz¸„¼ŽŠªª°=!^/^pKÓõWVÆ÷&&D+g\¸| ––À«éw††¸vþëöîÅþW¾Z9 `u‹\½Æ,—ÔÝJ¡5GŸ_U\\Œ©S§"//×®]C·nݸc‘‘‘““ãF¤ù¬¬¬ ¯¯/´ÓÖùóç±hÑ" >+V¬À­[·pâÄ äççcÇŽ­~/„´ŸìlX>~ ÏÚu¤¤°]]>FF<±>=v &&"±¹sáºs'ö40džKHHÀÃ’ 6eªØÁû~û ¬l¡N;ŠG#Ðm¤¥võyuº@±16íÙÓ"×jG¡ïÞÅ/ß}×ÚmжÉèsuu5,X€àà`\½zUhÝî””˜˜˜ˆ äíììpåÊäççCµv'.Q/^ŒU«V!  ÅûOHKˬ¨€{l,~«ó2FCß#ôÆ v hÆäAQfMŸŽY7obQx8ü¥¥1hþü6}^¿~=þüóO|÷Ýw"WÕèÑ£"##QXXeee®œ1†‡BUUµÁਙ,©¬¬Œ‚‚‚ï?!-éÕ¥éºÊÈ`Ÿ>pÕÓô¡À™4Â'û÷CÎÌ ò}û¿ÿ.t¼’1ä1†êK—pÊÕñ%%¸hi -™vèmǰûáCøq£Ï|eo½…O÷ïïð¹Ð<‹GtÄ…~têT³WÞ¨OÝ9¼uuÛdôùÛo¿…§§'6n܈+Vˆ¬cii‰üý÷ß»? ##Æ ÛNHHÒÓÓ1qâÄë;!-)®¤+cbp»ÎÒtóutp°o_èÐÒtDB>¿ý&vÀàeEÖ¾|‰ÇîååÁ¾vûo³68éHŠ«ªàößøiÏ`áBá <Äç?ý„m..mßAÒf|YL IDAT(€î ÖíÝ »‹qºS7^5káBŒþäLiƒÜgooo¬Y³sæÌÁîݻ뭷yófœ9s{÷îň# ¦¦†’’xxx¶Ö ôÏœ9999Œ?JJJÈÈÈÀüÁMD\½º%3Ç i¾Jư?9;QR] 0’—ÇccLìÚµ{G:*ƒFÕ»W]¥ÑÑø5=ñ%%€³ææ˜@ï=@Tq1f‡‡#<6“}æc#GbççŸcÁܹè];Ùt>@·‘–NÈ×ÑÑÁ§Oѯ_¿½n]<?ûúB[[»ÕÚà;{ö,ªªªàíí ¡ã.\À˜1c`ee…o¾ùîîîÐ×ׇ©©)bccQXXˆíÛ·còäÉÜ9<À¡C‡šššÈÉÉAuu5°oß>º„´·W—¦ëÂãa>ëÕ Jõ,MWw'BBÄ‰ŠŠ‚i+mÈIIá33˜**bGBò*+194ûöÅû Â;«ÓXªªš\ò™3zöK€J õôDÈúõÐí€ßÑN„âQÝFZja]­<óõèÑ£U®»nÝ:dggs?/[¶ £Fª·~Ý}777Œ5 7oÞDLL æÏŸGGGØÚÚ œ³cÇ8::"88©©©PRRB¯^½0uêÔV»/B$%ji:›Ú¥éŠY]Ãßßh!i”Ý»wãÇ[o›‘Lቒêj|ð߈*.Æ¡¾}Ñ¥çõ6EYu5ÖÆÆâHJ €šIöCÍÍÑíáCÔ÷L„!²¸ÚÚp Á]¨¶ðî½­&Š×±^ьވ‚Æ'𳃃}¾™™ÌÌ̬ӵkWL›6 Ó¦MkR imÞÙÙp‹‰Á³ÒR€‚”yÂÏCTU8p`£ç"|Û¯fצ?ÞÍÍ…sD÷ é(€&„Všš*²üÇ´4˜=~ŒÓjFµ~45Å­þýч&‘׌œî €™µyö‘ÅÅ€{yyíܳÖQÉ6ÆÅaFXr+k¶sïÞ~ ‡9ÁR<~13ƒc휞?³²°"&¦UúLÚÐm„võ!äÍñèñc˜ÚÚ äùÇ•”`lp0Þ‰ŠBvíºÎ tu9x0\j×u–TVV7‘q^Ý©µ±”ºtÁKKl®ýZÿeEÆãÇ´´–ì^»K)+ƒcP¾JN &- ðìÛ2MøVHNJ —,-aW;—áDj*6ÇÇ·p¯[Å,âQÝFZc!!äõ´nï^lÚ„÷îE%cØ“”«Ç¹u{ÊËÃÛÚ¿š™A»›Uøûûs §¡%BÅáø²woühj Y))”WWã¨(lŠGu'HM¸“ƒOŸr#딕ñÔÎo7s*•.]àmm ãÚ¥`÷$%arr³ûÛÚ(fï͘ ð „|BÞ ?F´†˜ þÜ¿þ¾¾«<Ô˜¥éþÇÞ‡Çx®ÿf•U{±D"±/¥–hmEm­j„’ªjÑåwôPôT[E‹ª¶Ô¡Å)µW{Gí"Di‚MÈ6óþþL3f“Ì$¹?×5Wå™wÞ÷7wž¹ïç) i"EQ”&Â'U³&ž¶¶ :žÄìlæÆÆréÁÖ4mjïéÒ¦Vf_¿Î§×¯k~¬]›E ¬é¯º•{üüètö,q™™|‹•U±?y* ’³è&3ÐBa@ÌËÜ•_â à|nÒÒÂÁc­ZñMÆe2Ñ"ÏóNNoÕ o{{¶&&òüÙ³Ü,cû'fgÓ'<œ™×®¡Vì-,XÝ´)K76øŠn66ìö󣪥% 06*Š?îÞ5è5Dé’Z! äÄÉ“DV­ ¹«а!f·n1³Z5N¶n­s]g!ÊŠ¶¶mÙR³Kaèýû´;}š“:¶ 7GSRhyê{rû¼íí9Ùº5ÿpu-±kúØÛ³ÃÏ; r…×""8RN›1+I K‰ä QþMúê+[wÜìÕW¹õë¯%²44Š¢(ná“8YZ²Ã×W³Ka|V]ÏžeÃ;½Ž¡}sã]CC53æÿpuåD«V4Í­S.IÏU®ÌF¬ÌÌx¨VÓ?<œ°§ìhh,’³è& t)‘‚|!Ê·ƒÇsºrå¿gŸs©6d×Å‹Z+rŠ4Š¢x–&Â'±03cq£F,iÔËܤðµˆ>»~Ýà×zV)99 >ž¢£ÉQlÌÍYÖ¤ «K¹~»Oµj¬ôò HÎÉ¡WX˜É­­-9‹n’@—)È¢üJW©0kÙƒú|lÿþL›;×à×8p 4 ½¢‰ðI&Ô©ÃN??œrk|gÄÄ0üâE2Õê»fQœ½ŸÖ§O³%÷O[[þlÕŠqµj%žá®®,lØ€„¬,z†…q++Ë(±FrÝ$F±cÇ6nܨױqqqüüóÏÄÄÄ”pTBÝ•Š.ëÖ‘Z³fÙç<%9 -„©èQµ*ÇZµÒlôë­[ø‡†rÛȉᲿþ¢ã™3DçÎò©^3­[Óâ ÿ^KË{uë2ÍÍ x´N|ï°0Rs7o¦O–±FñÙgŸ‘À+¯¼¢óØððpFÍÚµkñðð(…èžnìØ±œ:uªÐçV¯^¯¯o)G$Œå¡ZÍËçÏsféRèØžRNqÝÙ™OæÏgÉ_”b„B”./;;Ž·jÅàˆ%'ógj*íΜá__|sWí(-é*ã/]bMn9‚•™ó<=™T·n©Æñ4³=<¸“Ͳ¿þ"ôþ}^>ž]~~_Dž$Ð¥¤$ òqÉÝ^µ¤$''ãàà€¥¥|«ä¹rå —.]¢gÏžž«T„­^EÙ–©V3ðüùG›£¼þ:-33ùØÍíÉ;–5k†———AcÈk ,éû€("## þ=Xg++öúù1þÒ%V&$p=#ƒNgΰÖÛ›¾ÎÎ%~}x´åø+\HO ¾ ¿y{Ó¡råR¹~QüШw³³Ùtç!ÉÉ »pM>>X”@ã±¾233åç™’•Cä'''ãéëË;hݪ•AÏ_‡~ý=lÿœ8±Ä®QÕ¨Qƒ­[·; a$Yj5ƒ#"4K`õèØ‘m¾¾¥>k”×@(uÐBsæÌ)Ñ:èü¬ÍÍYáåES{{¦^½JšJÅËçÏ3ßÓ“)%<üë­[^ºDºJÀKÎάòòÂùvý,Iæffü·iS’rr8”Ä^ºÄš41ZL·nÝ’:h$.%†þFœ>>iï¿Ï”9s8´~½AÏg[PñíÛ³|ûv¦bmmmðkäää°lÙ2BBBP©TøûûóöÛoc®g"²cǶoßÎåË—iР½zõbÈ!Ž»|ù2{÷îeÿþý¤¤¤P³fMºuëÆØ±cµŽ{ðàK—.åìٳܸqƒš5kò /0|øplsëúDÅ–­(¼zá;s7Að¯R…ß<ƒ$΢hJ+yÎï£zõhlkËð‹IW©xÿÊ.¦§³¤qã'ZSL™j5“¯\áÇ¿þ­ò™‡Së×Çxs¹ú©dnÎÖfÍð åtZ+âãq±²â« Œ$ϺI‘M”œœÌösçPڶ墣#§Ïœ)‘ëÌX²„ÔÞ½‰íÞË–üü999¼þúëÌŸ?[[[®]»ÆÄ‰yõÕWQr·T}šwÞy‡~ýúŒ««+þù'¯¼ò #FŒÐzýo¿ýFãÆù"·öÔÍÍØØXƯu¾ÐÐPš5kÆG}ĵkר_¿>·nÝâwÞ!**ª@ìû÷ïç·ß~ãÔ©SIMkk¶hA@î¿¥ËÒáÌ6úøÐ½jÕ"Ÿ/GQøøêUÍ,­0ÍÍYîîFmÀ{Vn66ìöó£óÙ³$åä06*ŠjVVô/¥L&B}È玥ÄPM„ùgŸ5^}ô\SY/'OBË–¯á"«];þ³s'Y^ϳ{÷îZ_{xxàééÉ¥K—žúº+W®àääD»ví´Æ½½½©S§ŽÖ–µ£F"::šzõê1qâDöìÙS D$<<¼Ðx 3}út†NË–-ñ÷÷çûï¿çøñã888ðÑGé|½([ÔŠÂè¨(~Íý7ÜÆÑ‘Ý~~T6•id'BQ%±aQÙš›³ÎÛ›îî$åîÄ·,ß'‚úˆË̤[h¨&yv±²b§ŸŸyx”éä9½=;üü°³° GQx-"‚Ã))¥v}Ù‰P7ãÿ¨ QŸœœÌgÏþ=ûœ§reœj×f®™›7æëŒ›;—+ï¿_`üº¿¿Ag¡œœ´fóx{{³cÇEÁì 7Â;wîиqcÍìóã¯ß»w/YYYX[[3vìXš4i‚ X¶lK–,¡I“&|ôÑG¼ùæ›ÀßåÕ«W/Ö{ñðð [·nlß¾Û·o˜µe“Œ»t‰U ´tp`Oóæ8™@ò ÒD(ŠÆM„…1>uwÇËÎŽ1‘‘d¨Õ¼ué‘0ßÓs ðÞ¤$†_¸Àìlž«\™õ>>Ô-g3¦ÏU®ÌF„‡óP­¦x8‡Z´À¯6€‘&BÝdº ™>>7Ÿ}Ε2h«¿ûŽnUª<Ó#õÏ?¹íã…$YmÛt:%%…‹/j)ŠÂŸþIݺuŸ˜<Ô«WsçΑž»ÆgžœœŽ?N­Zµ´J6:wîÌæÍ›¹sç¿üò ¶¶¶ €§§'7ž¡a#¯N:##£Øç¦CÆ_ºÄŠøxüØÛ¼9UM$y¢¬{½F ‚[´ÐÔ*/¸y“—ÏŸ'-wù¹¨¨(ê5iBvn¢¬Vf]»Fï°0Mòü~½z„´lYî’ç<}ªUc¥—f@JîlýÕÜ…qI]F$''óÇ™3¨ŸT¿V¹2‘ϼ"ÇÌï¿'µW¯'>ÝÀ+r8p@ëëÐÐPñóó{êë|}}ÉÊÊ"$$Dküĉ¤¦¦Ò²eËB_çääÄÈ‘#ùæ›oP«ÕšíÄÛ¶m À† Šõ>RRR8|ø0ŽŽŽÔ«W¯Xç¦åÝË—5+ûØÛ³¯ys“]GVˆ²ªCåÊœhÕJ3«ºãî]:9ÃõŒ &þ9wúõcÉŠÜÉΦwXŸ^»†ZQp²´dK³f|íéiðåðLÍpWW4l@BV=øeäíÑ…$Ð¥æY›g|ó 7\\mü„G¢‹ fÌ(ö5¶ïÞMdf&;öÄkded°pùrT¹3Ï‚ŋ@ZZÓ§O`êÔ©O}í¿þõ/,,,˜5k–¦VëÞ½{üë_ÿà“O>Ñ:öòåËš¯wòÔ©S|þùçDEE‘Mbb"Ë–-ã…^àÖ­[|ðÁO9eÃä+WX<ÚšxóæT7Áä911QÓH(„.ù{CLI}þײ%ýr›äÂÓÓi½u+'Õj2ûõã›M›hqì{“’håèș֭Xšg'Õ­Ë477¢>¤wX)\ðq%±{ry#ŸE–’g-ÈŸðÆt×ãæWë•WŠ}¶-Z°vÂÇUjݺÐÚ㢪[·.cÇŽÅÓÓwwwâââÈÉÉaîܹVÂxœË—/gâĉԩSG³¶s^RÞ¡CͱsæÌáË/¿ÄÉɉ5jpõêU,--3fŒÖ¦+?þø#YYYLž<™©S§R¿~}âââHOOçܹsÔªU‹ÄÄD¦OŸÎôéÓ177ÇÌÌ •J…¥¥%ï¾û®æQv}Í¢›7hdkË|1›Ù‰PEiîDXTüÞ¬ÿwõ*_߸ÁÝ5k`ôh03ãF—.°môíËøÚµYذ!•*àÚë³=<¸“Ͳ¿þ"ôþ}^>žÝ~~%²‰“ìD¨›$Ð¥äY¿½¼¼J|ù!WW×RûAk?OšÄˆÞÜÔýШw³³Ùt燒“vá›|| ¾òˆ$ϺI-Œ¢K—.š?·jÕŠV­Z=ñØ5j<1Q¨_¿>£Fz굜=+ÉYt“èR"ùB˜–ooÞäÃèh\­­9ТÊPò ÒD(ŠÆÔ›'ΚEüóÏÃS’Àë^^,Y±‚ɼ:› »ýüè|ö,I99Œ‹ŠÂÙÊŠþOø%D_’³è& ´¢Âù>.ŽIW®fqö7oŽ×c«²!JW¯®]ixãän`T({{xx”^Pe€½=;üüxñÜ9¨T ˆ`Oóætvr2vhåš$Ð&ÂÆÆ†5kÖpðàAc‡"ʘ;wîh¶ºýÏÄÜuœ­¬Ø×¼9>ööFŽJñáøñÆ¡Ìz®re6úø0 <œ µšþáájÑB³Ã£0|ø[=jÙ‰P·r¹ Gff&ÉÉɨTª'£R©×¹ÔÝ»w {ê¹EáÂ… ÄÇÇ?ñùFú’p†·öömFGF¢V-,ØíçG›Ü5T˲Jò,ô&÷–ŠaRݺLss úáCz‡…‘’“Ãÿþü7oo²²²tžCrÝÊU=yòdš7o޽½=U«V%<<¼Ðã¶nÝJµjÕhÙ²%Õ«W祗^âþýûZÇ}ZsÌ_ýEïÞ½éß¿?IIIDGG“˜˜È+¯¼RbïQ¡¿-‰‰¼qá*EÁ΂¾¾t’Ît!DðC£F ©^€CGrÎÅ…ìîÝY¾}»^³ÐâéÊU½nÝ:æÍ›GÏž=ŸxÌ?þˆsæÌÁÎÎ___¦L™Â¦M›4%wïÞeݺuLœ8‘–-[bkkË¿ÿýoªV­Ê?ü u®ÌÌL.\ˆ££# 4`úôé„„„pþüy­ëÊ®>B_ea·°²à»wy-"‚EÁÖÜœí¾¾t©RÅØa”ìD(ŠBî-‹¹™ÿmÚ”îU«Â¦Md @l·n:g¡%gÑ­\%ÐúÆÇÇ333͘999š†œ£G’™™‰Ök}}} Ö:—››Žùj)ó^“ÿ8]}„þL}·°² èÞ=^‰ˆ [Q°17çw__üËYò ²¡(¹·T<•ÌÍ™zÿ>–îî»Ö}V‡ü´mÛSg¡%gÑ­Â%Ðýõ¾¾¾ZcÞÞÞšçòÿ·°:£à­[· œËËË sssÍ9òHA¾Ð—4ú<›½II >ž,µšJæælnÖŒU«;¬!M„¢(äÞR1}2>9ƒkÝð÷ê,´ä,ºU¨Z­V“’’‚[nwjžºuëbffƽ{÷4ÿ­W¯žÖqnnn<|ø‡œœ\à\vvv8;;kΑgóæÍ 4ˆ€€­Çã©É˜ŒÉXñÇ$%=ÚHà‹/°¼qƒ>>ô©VÍd⓱¢-Z¸ääd“ˆEÆd¬¬ýïÏ?¹\½:,^ ±±šc²ž{ŽÏ¿üR³ØB@nNÒ°aC $ŸléÁLQÅØAÚ®]»èÓ§gÏž¥E‹ZϹ¸¸Ð¿V®\©»|ù27fñâÅLœ8‘åË—3nÜ8´f˜ß~ûmV¯^­Y±£iÓ¦¸»»¤9&55'''¦M›ÆìÙ³Gߘ ¿ý Q’%'Ó'<œ*–fflðña l0Röý‘ä«ÇüÀšX/!ʃq|ìX(d·Uë?ÿäߎŽüsâÄÏIÞ¢[…š¨S§Nåí"""4Ïåÿï… ´Ž ×<P³fÍçÊ{Mþã@ ò…þ¤Ñ§èþ—’BßÜäÙÂÌŒµÞÞ"y–&BQro©X4³Ï…$ÏðhzÙÖ­…ÖBK΢[…K _zé%.\¸ õ Ž­­-þþþtîÜGGGÂÂÂ4Çäm¼òÒK/iÆúöíK\\wîÜÑ:—™™½{÷Öº®ä }I£OÑKM¥OX÷s“ç5M›òJîÒMå4Š¢{KÅòÎŒÜssƒÐÐ'>bkÖdáO?x­ä,º•«­¼CBBˆçܹsìÞ½›ÈÈH6lH›6m dñâÅŒ3†3fÅüùó=z4Ur»ô7n‹-¢M›64kÖŒ/¿ü’ììlƯ¹Þ˜1c˜3gÌ›7„„f̘Á AƒðððЊM ò…¾ä#³§;rô(ÏwìÀ©´4z‡…‘¦RanfÆJ//†Õ¨aäK4Š¢{KÅ2sÒ$âu%Âþþtl×®À°ä,º•«zÅŠ„„„þòÖl>|¸&öðð`ß¾}Ò´iSœœœøÇ?þÁ¢E‹´Î5oÞ2+h[P×¼¼ÀÊêÑ@­ZT63c ZmÜÀŒLšEQȽEèKrÝdZaòòÏ>çI}å&çnV$„B”&I …&m[PуµµöµjqìÞ=bómO+„B”I K‰ä }I£¶iß~Kz¾ Œò‹<¸BÏBK¡( ¹·}I΢›$Ð¥D ò…¾¤ÑçoÛ‚‚¸TØìsžÚµ+ô,´4Š¢{‹Ð—ä,ºUèu K‹¬§(Dñ4éÚ•Kuë‚•VææT³,Ø÷œÀ † YþÍ7FˆP”8YZˆR'y‹n² ‡Â$e¨Õ˜Ï›`knNh›64¶³3vXB”ºˆˆNž]ùè£333PÌÌÌ”×^{MQE9tèR½zuÍyj×®­œ={Vë<{öìQ\]] Ä4bÄ%55UsÜÀ ýË/¿TEQ–.]ªÊÑ£GµÎáÂ¥M›6^÷î»ïêýÿ¢¢‘¼E7“œ¾uë'OžÔü–,„¨ާ¦òuîÌPG''¦H醨ÀBBBˆŽŽfäÈ‘Œ9•JŪU«Š}Î>}úÀ”)S¸xñ"/^ä÷ßÍÔ¾ôÒKìß¿Ÿo¿ý–¿þú‹ãÇÓ¢E ÆŽ˦M› œsùò儆†rüøqN:Å!Cøí·ß˜4icÇŽeñâÅDGGóÕW_‘’’„ ´^Ÿ••Åøñã5?÷ƒƒƒyçwX½z53gÎÔ÷Ýwß1qâDöíÛ§‰=00ð‰ï7..ŽöíÛsýúuÖ¬YÃÍ›7¹qã[·n¥AƒÅþ{Â$f ³³³•+V(Ï?ÿ¼RµjU­ß•.]º(«V­RrrÊæÌƒü&'„n*•Òôøq…à`Å6$D‰JO7vHÂTàè#F(æææZ3ΞžžJ£F «ï ´¢(JTT”(_|ñEãóf¨?ùä­ñ´´4¥jÕªZ×Λ®S§ŽòàÁÍø½{÷4³Ñ¿ÿþ»Öyòf‘ãããu¾ÿ6mÚ(666ŠJ¥ÒŒÍš5K”˜˜˜Ç6ýÆo(€rìØ1ד¼E7£Î@ggg³lÙ25jÄ¿þõ/š6mÊ´iÓØ¶mgΜaëÖ­üóŸÿ¤aƼÿþû4iÒ„+V”É[)Èúª¨M„3®]ãbnåg²ê†^¤‰°üJMMeÓ¦M¼ð ԩSG3>räH._¾\¬Úw}î-ÿûßÿ7nœÖ¸ƒƒ¯¼ò —/_&!!Aë¹Þ½{ckk«ùºjÕªøøø`mmMß¾}µŽ}þùç¸víšÖ¸¢(>|˜Ÿþ™¹sç2gΜÉÈÈx¦zåЮ];Ú·o_ìsTD’³èfÔu #""øé§Ÿøì³Ï:t(Öí6Ö²eKÍŸ322X»v-ß~û-;vÄËË«´Ã}&R/ô5gΜ ·x½”nO^5pà@#G" mݺuÀÞÞ^ï÷¡áÇsåÊvíÚEjj*7nÜ 44TSï\ØûÒG¥J•°´´,tñt’³èfÔè–š)É× !LOþÒ Ù0EˆGŸÎ?~œ=zðÆox>44”E‹±aÃF €³³3÷îÝ#>>^kF8::ºÀëóf… û˜ÞÃÃEQ§M›6ZÏ………аaÃ⿹Çøá‡x|öÙgØÚÚ²råJÍkò>™Ý¿¿fL¥RñÕW_8íÚµ8{ölç†й¹9_|ñjµZ3~îÜ9¶oßNÿþýqtt4̬­­±²²"&&Fk<,,Œ78>¯´¤°Ø 3nÜ8bbbX¼xqçÊb?•0FÎïþýûüóŸÿÔÔ@¥¦¦òÖ[oñÒK/‘M````e‰ä }EFF–¹ÿâx¼tãg//)Ý(¢¼B#G" %;;›5kÖP«V­'Öî:::Ò¯_?6lØÀåË—iÔ¨o¾ù&Ÿþ9cÆŒaçÎx{{³mÛ6jÔ¨¡y]Þ½ÅÖÖ–_|‘;wÒ¹sg|||¨[·.Ó§O§E‹¼ûî»,Z´ˆ^x—_~™„„~üñGœœœ˜7ožA߯¥¥%C† aíÚµôë×>}úpãÆ –-[F·nÝ LžõèÑ&L˜À† ¨^½:ƒ *0{gÊ”)l߾ɓ'³ÿ~ºv튢(œ>}€µk×ôý”™™™TªTÉØa˜4“I ccc©T©’f]Æ5kÖ`kk˯¿þŠ……...šEY$M„B_¥‰PJ7ž4–?aaa4mÚ”—_~¹@^~ܾ}›'NШQ#\]] aÒ¤Ilß¾ .0pà@&MšÄ AƒðôôÔº·¬[·Ž%K–FTT”ÖlìÂ… iѢ˗/gÖ¬Y¸¸¸ðÒK/1wî\êÕ«§9®R¥JtíÚUÓ\˜_óæÍ©V­ZqWWWºvíJÕªU5c?þø#ÎÎÎlݺ•£GÒªU+ÍìzVV•+WÖ[·n]Ž9Âþó®]»Fdd¤¦Ù°V­ZtíÚ'''Íñ–––8p€ùóç³yóf>ýôS¬­­ñööæÝwßÕõ¿£Â’&BÝÌ”âVçØÍ›7ñðð %%;;;ºuëFãÆY¶l;w&00#F9Ò¢ *^s˜Or"5•ŽgÏ¢Rž«\™#-[Êì³(ÜYðG¾Oð>°ƒ&ON,…ÏNòÝL¦ºnݺxzz²hÑ"8ÀáÇ3fŒæùk×®iýV)„(›2Õjò¯º!¥B!Ê“)á˜6m}ô·oßæ•W^¡C‡À£Žã›7oÒ¶m[#G(„xV3+Ýh"¥B!Ê“™1bׯ_'&&†õë×kÆÙ¹sg¡‹µ—ÒD(ôUžw"<‘šÊ|YuÃ`d'BQåùÞ" KrÝŒž@Oš4‰Ù³gÿüsΟ?ÏçŸnìŠE ò…¾ÊS£”n”,i"“'Oæ×_ÕëØ’¼·üøã̘1Ã`çûꫯøúë¯ v>Q4’³èfë@geeqúôiœiܸ1'OždÍš5DGGÓ·o_Þ~ûmìÊèì•ä }•—FŸLµšÑQQRºQ‚¤PEYº· 0€J•*;Œ KrÝŒž@ß¿ŸÎ; @ß¾}Y´h;wÆÎÎŽœœvìØAvv6ï½÷ž‘£Bèkæµk\Èm–Ò !DQ”ÕO›EÅaôúàÁƒ\¹r…yóæQ«V->ùäúõëÇäÉ“™5k*•ŠÉ“'óûï¿K-D!¥B<»´´4¾ùæΜ9Cll,5jÔÀßߟñãÇS¥J­cïܹ 8räööö <˜qãÆióÓO?qþüy-Z¤5ž••Å{ï½G·nÝ6l·oßfÆŒ 8OOO–,YÂñãÇ©Y³& 0@¯÷°víZBBB4h½zõ $$„~øèèhÌÍÍ©S§;vdÒ¤IXYYðïÿkkk¦N <ª±Þµkׯ3kÖ,jÖ¬©ùzïÞ½¬_¿žððpjÔ¨A×®]yï½÷4çâY½zÿþýôìÙ“?üáÇóöÛoI`` 666ØÛÛÈ‘#G¸ÿ¾±Ã-6)Èú*ëM„RºQz¤‰°ü:þ<¾¾¾Ìž=›œœºté‚££#_}õaaaZÇÞºu‹nݺqøðaêÕ«ÇáÇ äßÿþ·Öq›7ofùò宕““ÃÒ¥K9|ø°f,%%…¥K—²fͺuëFTTuêÔaÇŽ 8µk×ê|Ÿ~ú)o¼ñvvvôèÑ€ 6Э[7Ž=Jƒ hß¾=jµšY³fi-Y»~ýz6mÚ¤ùúþýû$$$x¬]»–¥K—’’’¢9vâĉôìÙ“ÐÐPZµjÅ•+WøðÃiß¾=ršÅÓI΢ÅÈüýý•Ù³gk¾Þ·oŸR©R%­c>|¨Jhhhi‡g£FRüýý†(#FeìžÉÔèh…à`…à`e^l¬±Ã)×¶lÙ¢lÙ²ÅØa”¬m™Š2.õïGdޱ#*q*•JiÓ¦âàà ?~\ë¹ôôt%%%Eó5 Ê’%K4cIIIJƒ ”Ê•++šqwwwÅÎήÀõÒÓÓ@™0a‚fìÒ¥K  ˜™™)»wï֌߸qC±±±Qš5k¦uŽ^½z)5kÖTEQ²³³•Ñ£G+æææÊ‚ ´Žëܹ³R­Z5%))Ik<--MÉÉùûÿ­Ò¦M›'ÿ%)вbÅ P4c›7oVeæÌ™ZÇ®\¹R´ò ñdþþþeþgQI3ú ´Z­ÆÂÂBóuþ??NQ”Ò©DHA¾ÐWYjôyÜÉ´4æå+Ýx_J7J”ìDX>sêÔ)&OžL»ví´ž³³³£råÊZcÞÞÞL˜0Aóu•*UèÑ£©©©\¿~]3Þ¶mÛ"ÇòòË/Ó³gOÍ×uëÖå¹çžãâÅ‹äää8>--¾}û²víZÖ¯_ÏäÉ“µžwttÄ¢ÀÏz‡§þü\pp0o½õþþþ,[¶L3>wî\êÔ©S`E‘#GR»vmYOO’³èfôh€}ûö‘‘‘Àµk×ÈÉÉaÖ¬Yšç ûG*„0-²aІ@·nÝô:Þ××·ÀX›6mXºt) šÕ­ŠÃÏϯÐs“˜˜¨UwüðáC:wîÌ7Ø·o:u*ðÚ±cDzsçNj׮͠Aƒ8p /¿ü2––ú§#QQQ 2OOO6oÞ¬U׉³³3ü1Š¢ ( jµEQpttäòå˨Tª"%ëBÆ$èýû÷³ÿ~­±O?ýÔHÑ!ŠcV¾U7>“U7„(¶¼w]\\ô:ÞÆÆ¦ÀX^R©V«Ÿ)–¢œ[¥RqïÞ=ÌÍͱ··/ô|ƒ "44”¯¿þš-[¶°zõjjÕªÅ;ï¼Ã´iÓtÆ“˜˜Hß¾}±´´dçÎZÍ”$''ckk˹sç ¼ÖÝÝwww233Ëì’¸Ât=>xð ±C(R/ô‰———±Ã()Ý0޼B}-Q6xxxpýúuš7on°ófdd••EFF†Vb|÷î]ƒœßÁÁC‡ñ /н{wvíÚU  yóæ¬ZµŠ¬¬,‚‚‚˜;w.Ó§O§OŸ>´jÕê‰çÏÌÌdàÀÄÅŬù{ÊcccCíÚµñððxêŠB·ÌÌLY‡[£×@W²«ÐWYÛ‰PJ7ŒGv",ŸÚ¶m‹¹¹9«W¯6èyóJ$=ª5~àÀƒ]ÃÝÝC‡Q£F zôèñÔïOkkk ÀâÅ‹زeËSÏ=fÌŽ=ʪU«èСC¡ÇtèÐS§Nqéҥ⿠!9‹Œž@ß¿Ÿääd½*•ÊØá›ä }•µ&B)Ý0i",Ÿš6mJ`` 7ndÚ´i¤¦¦ÖkÞ¼y3ÑÑÑÅ:ï7ß|À¼y󈈈@¥R±k×.ƒnÁ P§NBBB¨_¿>½{÷ÖJÐßÿ}N:¥Y ))‰Ÿ~ú xô¾ŸdöìÙüúë¯Ìœ9“þýû“‘‘¡õÈ;ßܹs177gàÀ=zT3žžžÎ–-[X¸p¡Aßky%9‹nFO ûõëGÕªUõz„‡‡;\!D>Rº!DÉøúë¯yë­·øâ‹/¨R¥ ®®®ØÚÚ2dÈMtQ½øâ‹Œ5Š]»vѬY36lX¡kC?+WWWµ3V~#ÂtHé†%ÇÎÎŽü‘÷Þ{3gÎGÍš5éÚµ+îîîšã<ˆ««k×÷îÝ›àà`Z´h¡5þóÏ?3räHÎ;‡››]ºt¡ZµjS§NÍquëÖ%88˜ 8÷رcéÑ£‡Víýܹsµ6Bpvv&88˜3gÎhêiccc9vì/^äÞ½{T¯^öíÛXIdåÊ•ZùÀ‚ HJJzâß—›››æÏ]ºt!,,ŒcÇŽqáÂ}šeË–ñÛo¿aooÏ?þñžúQNY@ff¦^;7 QVšÿuõ*_ÆÆ0ÏÓ“ëÕ3rDO…h"ü# þÈׄý4‘%ÈŠ£¬Ü[„ñ½þúëTªT©Ì•–&£—p´nÝš¥K—ϼyó8sæ Íš5£C‡üøã$'';Dƒ‚|¡¯²ÐDx2-¹RºatÒD(Š¢,Ü[„iœE7£Ï@&66–_~ù…+VO\\ÎÎÎÆ«Ø€²×&Da2ÕjZŸ>MDz:6ææ„¶i#ƒ¢äÈ ´¥NòÝŒ>ý8EQ¸ví111$&&âìì,; aB>½vYuC¤¤¤°aÃBCCŠ¥Ê$šáÑú”¿üò «V­"..ް~ýz…Å‘””Ä®]»HJJ¢OŸ>c‡G‰|pp0'OžÄÃÞ={jíx”'!!½{÷rëÖ-:vìHÇŽ «¦$éF)ݢ»páC‡å…^`ß¾}ÆGˆRcôè­[·âïïOƒ  â£>"!!ß~û>}úÉYt3ú ôÂ… 9v쯾ú*M›6%!!በ?žš5kûZy+~|úé§š…ã:D×®]Y¶l™&ù]·nëÖ­ãÈ‘#´k׎ЩS'Þ}÷]¶oß®9ߘ1cðõõe÷îÝXZZ²eËLß¾}éÞ½»Öµ¥ _èkΜ9&Yw&¥¦'¯P6Sú(‰{ËÍ›7GËÞ‰òãÖ­[²t°FO °±±a÷îÝìÞ½û©Ç2ä™èÛ·oпÍXçÎqrrÒ<ðÃ?дiSÚµkë×¯ãææÆ‰' ãçŸÆÒòÑ_ã AƒprrbùòåhùFú2Åäù””n˜$IœEQ”ĽEèòIrÝŒž@çŸÑ-iþþþ899ñË/¿àëë‹¥¥%›6m"%%EëÑùóçéÛ·¯Ök}||P«Õ\¸p777 ,þîíí­yNˆòàñ S¤tC‘GJ8DEeôèÒdccÙ3g8yò$®®®xxx0aÂöïßÏsÏ=ÀÇIMMÅÇÇGëµÞÞÞÀߥýõšñüÇýõ×_®}óæMBCC‰ŒŒÔzÆ cذa8;;³fÍîß¿ÏG}¤9îí·ßfÍš5ôìÙ“€€NŸ>ÍÖ­[Ù¸q£æ˜J•*ñÙgŸñÎ;ï~øþûïiÕªÆ +Ñ÷!DIÊzlÕ•²ê†¢ÇŽ$“Ñg K“‹‹ gÏžeäÈ‘œ?žÝ»wóüóÏsúôiºté¢9®zõê=zb¼[§ IDATMÒ¼oß>­õ£áÑÆ.yEøüñ¯½ö!!!ØòQ·ìê#ôe¬ÿ÷矀véÆ¿¥täÉN„¢( }oÉ›Î[ÅJ”’³èf¦(Šbì Ê»€€bcc9pà€±Ce@@@@©×AÿÄð·Þâ×Ý»tû69ŠB‡Ê•ù_Ë–2ûl¶nÝ ”óZè?²à|?Ì?°ƒ&Æ‹§ 3ä½%--*UªàèèHRR’Ô@—3Ý»w§~ýú&Ó“cŠL¦„£¼“‚|¡/cܰf|÷i³g3|æLr&LÒ2¢\'ÎÂà yo9yò$jµšvíÚIò\I΢[…*áBôGPך6…úõIÍΆ۷¥tCñTRÿ,*:I …¨àf|÷ɽz=úbèPªmÚIJê†â)vïÞ @§NŒ‰Æa2 tvv6dggúü„ мk¡)‘‚|¡¯Òl"ÔÌ>[Y=¨U kàæ¥ƒ(>i"Ea¨{K\\GŽ¡jÕªøûûäœÂ´H΢›É$Ð*•Š_~ù•JUèókÖ¬)Ó{³ßºuËØ!ˆ2"oCŸÒ 5ûœ+aà@ÞÿüóR‹Aß‘#G´Öµâi uoY¿~=jµšÁƒcmmms Ó"9‹ne¢‰022µZM“&MŒJ±IA¾ÐWi5þDLþÙç<µjq<%…ØØXù¾5qÒD(ŠÂP÷–uëÖðúë¯ä|ÂôȽ_7£Ï@ïܹ///š7o@óæÍñòòÒ<<<<ðööæÅ_”ßt…0 ß}GÊc³Ïyn ³ÐBˆbbb8qâ5kÖ¤[·nÆG£1ú týúõ6l999|þùç¼úê«XZþ–½½=]»v¥M›6FŒRˆòå  ®zyœ}Î#³ÐBˆBäÍ>¿úê«XXÈzÜ¢â2zݬY3š5kFNN–––LŸ>]+./¤ _è+22//¯½ÆŒyóÈðð€µk¨ji‰åck¹ÞÏÎæÓ øÏ‚%‹(¾¼B#G"ÊCÜ[¤|£bÈÌ̤R¥JÆä™L¦jiiɬY³ŒF‰‘‚|¡¯9sæ”xô´~àÕÜmx‡T¯ÎlB«Q£F‰Æ!žM^¡ÔB }<ë½åðáÄ……áîî.Ûw—s·nÝ’Ou0™:++‹ž={>õ˜åË—Ó°aÃRŠÈ°äQ諤“gµ¢0;- ê×Ç΂…íÚQWfÊ$IœEQ<ë½åóܾˆÉ“' aÊ$gÑÍdh333ÜÝݵƲ³³9pà 2Dš…0€ œ»€êÕ“äY¡Ó©S§Ø½{7Õ«WgܸqÆG£3™ÚÊʪÐߎÕj5‹-bÛ¶mÔ®]»ô¢IS©˜@J•ø¿zõŒ‘¢,øâ‹/˜2e vvvFŽFã3ú2vº˜››3~üxÎ;Ç©S§ŒN±I¡ÐWIîDøÅõëÜÊÊàË °“.ú2Mv"EQÜ{KDD[·n¥J•*¼óÎ;ŽJ˜"ÉYt3ùíR˜““CLîÌYY$M„B_%µᵌ ܼ @[GGþáêZ"×¥Gv"EQÜ{Ë—_~‰¢(Lœ8‘Ê•+8*aŠ$gÑÍdJ8Eáxîªù]ºt‰7’‘‘A»v팙aHA¾ÐWI5þ_t4™j5 6ÄLÇñÂôI¡(ŠâÜ[Î;Ǻuë°··—æÁ DrÝL&ÎÌÌ,tYGGGüýýY¿~=žžžFˆLˆ²ïHJ îÜàµ5èääd䈄¦N¥R1vìXT*S¦LÁÙÙÙØ! a2L&®T©R 333j×®Õ“vKBè¤S®\ÀÆÜœ¯40n@Bˆ2áÛo¿åÔ©S4iÒ„éÓ§;!LŠÉ$Ð…-cWžHA¾Ð—¡w"\À©´4Þ¯W7ƒ[—ìD(Š¢(÷–ëׯóÉ'Ÿ`ffÆO?ý$»ÒU0²¡n&×DÃÔ©Syùå—éÝ»7ï¾ûn™^}#ä }²‰0]¥âãÜOvjZ[ó±Ôµ•+ÒD(Š¢(÷–·ß~›ôôtÆOçÎK0*aŠ$gÑͤèßÿ&Mš0þ|nß¾MNNË—/§mÛ¶šÊ*)Èú2dáÜ7ø+÷ÓÏ=þøc>ùä®äÖq !t»‘™É¼ØXZ:8P³¦‘#B˜ºøøxÍjßÿ½,['ĘL}ùòeRSSùòË/©R¥ŠfÜÖÖ–O>ù„š5k–‹R!JËÇW¯ò0ß²uæf²pâÉ2224h·oßføðáôïßߨ! a²L&vssÃÜÜœ{÷îx.==û÷ï´±ª´I¡Ð—!v"<žšÊ¯¹5lƒ«W§k¾_JEù!;Š¢Ðuo äøñã´hÑ‚eË–•RTÂI΢›É$ж¶¶L:•>ø€S§N¡Î9»xñ"“&M¢W¯^øùù9Êâ“‚|¡/CÔN¹r°67gž,[WnI¡(ЧÝ[æÍ›ÇêÕ«©^½:[·nÅÎή#¦FrÝLf»¬¬,öìÙÙ3ghÛ¶-ÕªUÃÖÖ–¸¸8Z·nMÇŽ5ÇÏ;—.]º+Ü"“‚|¡¯gm"\{û6¦¦0©NØÚ *aФPÅ“î-;wîdêÔ©XYY±iÓ&ÜÜÜJ70ar$gÑÍdhsss:tè@‡ô:¾jÕª%‘eÏCµš©W¯PÃÚšéòƒPñ/^äõ×_G­Vóý÷ßË’uBèÉdhKKK.\hì0„(Ó¾¾qƒØŒ þíîNeK“ù'.„01—.]¢G¤¦¦òöÛoóÖ[o;$!Ê “©.ï¤ _諸M„ñYY|•»l¯½=ckÕ2dXÂI¡(Šü÷–ÈÈHºuëF\\ `Ñ¢EFŒL˜ÉYt3©úÁƒlß¾‰'Ò±cGMIGÞãòåËÆ±Ø¤ _諸M„Ó®^å¾J|À§îîT‘ZÆ %¯Pj¡EaÒÒÒ1b¿ÿþ;ÖÖÖ´nÝš~øÁØa‰2àÖ­[R­ƒQÚ^¼x‘>}ú_µj«V­*0~öìYZ´hQ¡œ|# }é›<ßÎÊâóÜeë¼ííy«víŒJ˜"IœÅ“\ºt‰rñâEj֬ɦM›´vóâi$gÑͨ tÛ¶mIJJÒûø²:û,DIøäÚ5RsrøÚÓKY¶NlÛ¶‘#G’’’B‡Ø´iµål! ʨ ´……UªT1fB”Iáééü'>€>ժѻZ5#G$„0¶¤¤$&Ož¬ùwìØ±,Y²kkk#G&Dùc2“Š¢pîܹ'>oooO½zõ°±±)Ũ G ò…¾"##ñòòzê1S®\A¥(Xš™ñuÆ¥™05y „...FŽDÛï¿ÿÎÛoÿ?{wUÙ>pü;ì à†;*ˆûnjj.å¾—¹d*¾n-šùs×rÍĥĴÒ2—RS[ÕÒ·ÌJ_Ë}WDÅ…MQ\fÎïÃ6²Ì 03Àý¹®s)Ïæ< ž¹y澟ûM¢¢¢(^¼8AAAœcʽEPcGGGKOêYMnbb" 6Ìö¨^½:...ôèуððpKO7פˆP˜ÊX'·o³'%õérå¨)ÛÖYÒ‰P¤îéÜ»wo¢¢¢èÚµ+çÎË<ÃÓw9EÄ,ÆYÍ ´££#7ndÚ´iôë×öíÛðÇðÝwß1kÖ,nܸÁ7ß|Àø÷ß Ô6<’/L•Sa’¢01, O;;f˶uEšmßÿ=o¿ý67oÞÄÓÓ“   † ’íùOÓåTM³g5´F£aòäÉ,\¸Aƒ¥wéÒ…Zµj±`ÁBCCiݺ5/¾ø"¡¡¡T¯^Ý‚3Âü>ˆàb|<3}|(aooá !ÌíìÙ³Lž<™]»vгgOV®\IÙ²e-<3!Š«Y #&&&Ë•—_~™+W®AÛ¶m)]º4W®\±À,…°œÛIIÌMI_ªîâÂÛåË[vBB³ŠŠŠbäÈ‘4hЀ]»váååņ ضm›ÏB˜™ÕОžž$''³~ýúL­]»€âÅ‹cccƒF£ÉÔdÅÚI¡0Uvg‡‡—²mÝ’jÕ°—mëŠ<éDX4×®]»`ccCrr23fÌ`Ĉ|úé§€Úð¥råÊ|óÍ7¼õÖ[Ï4OQ´]zü˜å7nÐÆÃƒ>ò½ÎíÛ·Y·n_}õçÏŸÀÖÖ–nݺ1bĺwïŽÕ¿ !²P –´lmm±µµåâÅ‹Ïô<'Nœà¿ÿý/ýû÷Ï6xØ´i5jÔH žZ·nÍÕ«Wù÷ßøûï¿‰ŠŠ¢uëÖiçT¨P___6mÚôLóbRXIŠ‚FÃRÙ¶Nˆ#,,Œ   Ú·o··7'NäüùóT«VùóçsíÚ5~ùåz÷î-Á³˜ÕüïU… .d¿xñ"[¶láþýû4jÔè™®qòäI|||xùå—9sæ U«Veòäɼøâ‹iç…‡‡Ó©S'ƒÏ­U«VÚc­Zµ"<%75u|8Å‹çùçŸ'00àà`*V¬H‹-ذa±±±lذvíÚ±páB‹OS‹-ýý“1ëKI:wîÌÔ©S9räÂÅJèõzåĉÇÉ“'•û÷ïçÙ5fÏž­ÊÈ‘# Æ+T¨ ´lÙRQEINNVlmm•3fœ¡JPP¢(ЍÊíÛ· Λ0a‚âììl06tèPåÅ_TNœ8¡œ?Þàxôè‘Á¹2V´Ç¾ˆŒTøë/…õë•9!!V7?“1s]ÿ,LIvGQFÞWägz¾àà`%444ן©Ì™3Géܹ³âää¤iGýúõ•Ù³g+Ç7Û÷EÆd,/ÇRc’_ýU9qâ„Ò»woeèСŠÈžFQÅ’¼9­^½š‘#G²cǺwïž6>jÔ(¶lÙ’¶]… hܸ1Û¶mK;g÷îÝtìØ‘ï¾û޾}û²aÃ̾}û ò ;uêDXXXZûU€€€@* EÎètø:DŒV‹“çŸ{'ÙyCu;´°#C Üð·ÍõÓ(ŠÂ?üÀòåËÙ´iåMØòôéÓi©G%õåÒÁÁ¶mÛÒ³gOzöì™';D aM$n1Îjr Sݼy“/¿ü’´Z-~~~ :??¿g~îúõëa0iÐù°^½zœ>}Úàœ³gϦ=–ñϳgÏЧOŸ¦E‹Ï},=%Q¤v!”†*éôz=›7ofêԩܽ{—êŽ;wæ7ÞààÁƒœ={Ngð¹~~~4oÞœæÍ›ÓºukêÖ­k‰/!ßÊ[òÂ$111ÒLūɾví•+WæÐ¡C<÷Üsݾ}6nÜHÏž=ŸùZwîÜa÷îÝ„††R½zuz÷î½½}¦óöîÝË¡C‡¨X±":tÈr•çæÍ›ìÞ½›ÈÈHZ´h‘e VÉ%9Ùï­Oœ`ˆ·7ëe…Hˆt&æ@ët:6oÞÌ”)S¸wïžÁΆ'_êÜÝÝyî¹çhÞ¼9-Z´ Y³f”(Q"ß¾ ! ‰[Œ³š褤$4 ®Yìyëää„““I)mŸ•——ýû÷7z^›6mhÓ¦MŽç”.]šAƒåɼDѣ列³µåà ÛÖ !ŒÓét,\¸E‹Ÿåë„¢(T«VvíÚ¥­0׬Y©3B<%«¹{T­Z•R¥J1}úô´Ý0@ ¬.\H\\Ü3·ñÂÚ|ͱ”·˜'W¬HyÉ9Â$ÉÉɬ[·Ž *0sæLîÝ»—ã"Ktt4^^^ 2„ÚµkKð,„x&VsÑh4¬Y³†;wR²dIZ·nM÷îÝñòòbÞ¼y|øá‡T®\ÙÒÓ|jRD(žôH§cú•+TptdRJ¾YHHˆ%§% ØØØ´B¢D«Õ²lÙ2¶lÙBñâÅqrrÂÆÆÜÝÝñôôÄÍÍ ‡´ÏyôèË–-£N:Ý‹¹·SIÌbœÕ¤ptëÖsçÎñÉ'Ÿƒèß¿?Ç/ð[ÃI¡xÒÂk׈L¹IV­ŠsÊŠ˜úSÕ"B&L˜À„  ÆïܹCTTÑÑÑi^¿~+W®ÁÍ›7¹qã5"00qãÆY¼½¶9ɽE˜Jгš:<<œwß}—/¾ø‚åË—[z:yN~EF×Yrý:ÍÜÝy­L™´ÇäN˜ª¨ÎÆxyyáååEíÚµs|ˆ›››™fgyro¦’˜Å8«Iá°··gûöíYî†!Da3õòeëõ,õõ¥è¬ ayŽŽŽøøø©àY‘·¬&€._¾<5kÖ”ßE¡wèþ}¾MIéXº4-¤£™BQ XM ‡^¯gÒ¤IL:•S§NѱcÇLMSš4i’å6w$ä‹T¢Î66V­šéqé&L%EnȽE˜J:g5+ÐZ­–aÆÃúõë4híÚµ38BSöË-ˆ¤ˆP|{ó&ïß`BÅŠTrrÊtN`` ¹§% ¨ýû÷§ aŒÜ[„©$f1ÎjV ¹’²¥Wvžµ·%IB¾x¬×3õòeÊ9:25›Ÿ Ic¦’"B‘ro¦’˜Å8«  5 >>>–ž†ùæ£ë×¹–Àü*U(f›¹±B!¬ŸÕÐݽ{—Ó§O£OÙ¥ UAÎE“^¯gãwßñRŸ>^»@c77†z{[xfB!„xZV@ïØ±ƒ… rðàAt:]¦ÇOžÂTRD(rCî-ÂT³g5+З/_æÁƒ¬X±???KO'ÏIB~ѳnóf®5k%J ¸»ãp틚77þyRè#L$E„"7äÞ"L%1‹qV³]µjU¹w§"Ä3K]}ŽoÛVèߟr?ÿL•,¶­B!DÁb5´““³fÍâƒ> 22ÒÒÓ♬ۼ™kÏ=6)ÿÅ<=yäêʹà`ËNL!„ÏÌ¢)ÁÁÁ¼õÖ[c‡¢B… 4lØ777ƒÇV¯^¯¯¯9§˜g¤ˆ°èÐëõ,Z·Žø©S ÆoõéøùóùcãÆ?_ }„©¤ˆPä†Ü[„©¤ˆÐ8‹ÐŽŽŽ™ö~Îi/h‡üP>’„ü¢cÝæÍ„g\}NåéÉÎS»V­l??00Pr…IR %Z˜Bî-ÂT111’m„FQÅ’xøð!vvv8âÜЀ€@ 8н^OÍN¸8mZæàÎ^üᣫÐBˆ;´°#Ã;x\À_š ‘Ÿ$n1Îâ9ÐÝ»w—­uD¡±nóf®4mšuð àåÅi\h!„¢³šmì„( >ZµŠd''8~{ï,ÒŽ´wïòÑ_°&(È3B!ij’ÚL¤ˆ°hðþä‚ãâ°ÑhØß°!Ϲ»çú9¤ÐG˜JŠEnȽE˜JгŠ:::š“'O=Ïßßggg3Ì(ïIaá·6:š?ãâ[¾üSÏ …>ÂtRD(rCî-ÂTRDhœUЫV­bÕªUFÏ;qâ 40ÃŒòžü n7µZ&†…PÉɉªTyêç’8a* œEnȽE˜Jbã¬"€îß¿? ÓÈÑÖ IDAT0z^•gJ„ÈOr') €Ïýüpµ•]„BˆÂÊ*è5jÈJŠ(°~½}›-7o0°tiº–(aá !„"?Y|»¢BŠ §‡:o^¼€—½=AyÐ)3$$䙟C ±±±i…„B#÷a*‰YŒ“ÚL¤ˆ°pš~ù2×Sn4U«Fé<è–)û¢ Síß¿?­PcäÞ"L%1‹qOáX½z5®®®–žF¾“„üÂçÐýû| À‹žžx{çÉóJ¡0•¤¾‰Ü{‹0•Ä,ÆY<€ö̓·¼…0·$EaÄ… ègVU¯né) !„ÂL$…Cˆ§°èÚ5Î>zÀlªÐýÉ…B‘{@›‰$äâã™wõ* \]ù¿Šóôù¥ÐG˜JŠEnȽE˜Jbã$€6IÈ/`ÔÅ‹$êõØj4¬ö÷ÇN£ÉÓkH¡0•ŠÜ{‹0•Ä,ÆY<º¨„üÂauTûîÞàÝ hìæ–ç×Ba*)"¹!÷a*‰YŒ“h!L¥Õ29¥]w''æúøXvBB!„°  …0ÑØK—¸›œ ÀÊêÕq‘vÝB!D‘$´™HB~Á¶-6–nÝàõ2eèèå•o×’Ba*)"¹!÷a*‰YŒ“ÚL$!¿àºŸœÌÛ—.PÒÞž¥ù¼w¹úSI¡È ¹·SIÌbœš‰$ä\S/_&"å·ñ¥¾¾”´·Ï×ëI¡0•ŠÜ{‹0•Ä,ÆÉ ´9øçÞ=V¦´ëîèåÅëeÊXxFB!„°4  …ȆV¯gä… (€‹­-+¥]·B!Úl$!¿àùðÚ5ÎÇÇ0×LJ*NNf¹®úSI¡È ¹·SIÌbœÐf" ùKð£G,¸v €Ænn¼[¡‚Ù®-…>ÂTRD(rCî-ÂT³'E„f" ù‡Œ¼x­^]J»nÛÂTRD(rCî-ÂT³'E„FÄÇÇãââ’ã9Z­[[[lmm³=Gò­ÛÝädƦ´ë.íàÀGÕªYl.Rè#L%E„"7äÞ"L%1‹qEvúñãÇ4iÒoooŽ?žéñ P®\9Š+†¯¯/ßÿ}¦s®_¿N›6mpssÃÝÝž={r÷î]sL_ä±ÉaaDkµùúâ•Ïíº…BQpÙúý÷ß'66–˜˜´)SªåË—óþûï3wî\ÂÂÂèׯýû÷çï¿ÿN;G«Õò /””ÄáÇٳg!!!tëÖÍÌ_‰xV{ïÞeuT]K”``éÒž‘B!¬Y‘Lá8tèëÖ­cÅŠ 8Ðà1EQ bÀ€Œ1€?ümÛ¶±lÙ2Ú¶m ÀO?ýDxx8Û·o§nݺ|üñÇôèуC‡ѬY3ƒç•„|딨×3êâEÀÕÖ–Ïýü,=%BBB¨Q£†¥§!r!66–‡=ÏËË ww÷<½.@É’%óì9…éFõì‰6""Çs´:-ûöåí÷Þ3Ó¬²'÷aªÄÄD-= «VäèÄÄD† Æ‚ ðööÎôxHH—/_æÍ7ß4¯[·.?ÿüsÚÇ;wîÄÞÞÞàfÔ Aƒ´Çž  %!ß:Í»z•‹)íº?¨R…JfjדÀÀ@ÉU,`^ïБ‘8åÐpçjR¥ûôaáêÕyvÝÔ– ýÛŽD]¿@Å3å¨\6í±s›.r¯Ì6kFýÆ-2G€ÊÕªÑé×_i¢×g{Î//ê·jeÆYeOî-ÂT111’mD‘  çÎK©R¥1b{÷îÍôxDÊjBêªrªÚµk³eËbcc)Y²$øùùaŸ!W¶B… xxx¤=GFòƒh}ÎuêÕ{Úi=³Õóç3èÐ!<€tÂŽi•ÿç3< c¿­-÷Þ{Ï¢ôØ9s³m_§ìÿ¤xà„ŸSÞ¹´4¹·SIÌb\‘Ê>~ü8Ë–-ã‹/¾@“ÍJÑ­[·ðõõ5¯–²+Cêã·nÝJËÈ××7휌~üñGúôéC@@€Áñ侜2fž±¡¼þÛo$) v _V¯Î† ³šùÉXÁëܳ'{+Wf0ðäN»Àï€Cóæi/J–šóà~ý¸1dޝ† áA†/0xÀ‹~O§ú)§J•¢-ð'à ”M9žÖÊ—gÔ”)™_*wwwÎ:;óÝûÅ þ|êåÅ›)[ÇYÃÏ©ŒÉXvc)1‰¯¯/}úô‘í1M QE±ô$Ì!))‰&MšÐ·o_Þÿ}þþûoÚµkÇhÞ¼9¿ýö;wfÏž=´oß>íó™6mÑÑÑ”)S†víÚq÷î]Nœ8apÒ¥KÓ­[7Ö®]›6ÈoÿÖä“7 À´J•ø°jU ÏHtÿݾ‹ƒóN«Ðo”-Ëôƒ-¾ªsâøq~ìÒ…y7of{Îë>>¬8q3Î,³€6mÚ·:A†høŒ¿ì®rjútÞ3çÙ.’” ðø±z¤þýÉ?sxìþ½{¼½u+ß<‘ÿY³&›þþ¤0Y0·WdR8‚ƒƒ9}ú4vvvüúë¯ÜOy¡1bÍ›7gõêÕ”+W€3gÎÐÁÁÁØÙÙQªT)Ê•+ÇÁƒÑétiû?ÇÄÄpëÖ­´çÈHŠ­Çµ„f¤¼åêçìÌL+k×-…>€N‘‘põ*„‡ÃÕ«t¾z•/õzF3é/ö±±TjÙŠõÏìŽ\<ûèh4&6lÔˆ š5¹só&^Y<~ÔÆ¿îÝÍ<'%V«‰‰™þ>î?ÿ!èôifg±3è*Ö:;Ãä*ØÍ4¦Ó=ó—áÔ£®Ž§úxëüy(S¼¼ fMãF ðñòæóšÜ[„©¤ˆÐ¸"@—+WŽ¥K—Œ…††rþüy:tè¶]³fM¼½½9sæŒÁ¹çΣM›6ؤ¼U×®];6mÚÄåË—ñKÙ¹!õsÚµk—éúRDh=Þ¼t‰‡)/œ_øûãdvÝ9‘BŸt5¢Ll,Ù·(‚X­–]»²dÍš¼»°V ׯÈiGx8ܸ)-ß3 | ŒÍ0¶˜–”¤~Nۯр£#½ÝÝM À]\x·Q#–?μ2=_P‰¬¨QV­Ê2¨Í)àÍÕyZ-yó³!° x¸f?4ÅyÚ´<üN>{{pv''Æ::òVt4’’uõù$09õÜ;wàŸÔ##ggð÷Ï\ûùƒCžM5>>­VËܹsùì³Ï²=ÏÅŇ<¼®(¸¤ˆÐ¸"“‘•¬R8fÏžÍâŋٶm/¼ðk×®å7Þà§Ÿ~J+Úyôè•*U¢uëÖ¬Y³†ÄÄDz÷îÍýû÷ 6ȱ–·B¬Çæ›7 Àð²eYíïoቜl^³†Ä1cúøq¶çL(UŠáýE­ÚµMâøxÀøÉ9:rØY!F]i¬\™W.\`ÓÝ»8W€«Wgy§Nðè‘zÝG²>R3Ã-y(° V¡¿³òýê¦;œ§¯eHáÏg|HΩvvàä”ÌüiêØÓœokøkÝüñãyé“Oh¦×³ÈË‹ç?ÿœVpþ¼á‘EL&vvPµjÖ«Önn¹þ>¶«S‡ºwïæXô©Õâÿê«ÌûôÓ\?¿(|$n1®È¬@çÆûï¿OTT;wÀÎÎŽ?þØ â½X±büùçŸôìÙ“R¥J¡×ë©[·.¿ýö[¶ŠÂ²î$%¥å=—qp`±Ûu Óô  Ï¢E¼~áB–«ÐÑ@\½z™ƒç»w3Åÿž²²Élm¡|y¨\9ýðñIÿ{¥JjPŒÜ¾/fÌýû,.WŽ©»w«›êñãìƒëœoSKI%{5€ž—á²AÀŠìæ¤Ñ¨+¢©‡£cÖÏãÇ:8°wÄw$^G RcCƒÁCq^òFz0kg/ccçÌáÍŸfUx8'ªUcr¿~ê;žxûvæ úüy¸v-ý—§äd¸xQ=¶m3üü 2Ö5kæ˜g=úÿþÆŽe@Ê–Yy§|yFf(ÊBä¬H¯@ëõz´Z-ŽŽŽY½< ,,Œš5kæ˜ téÒ%³}»C~“³ÃBBX ÀÖÚµy5%Ÿ]X·ÍkÖ 3†!Y¬BO,^œÿŒI­„ÀùÞ½Ü]ÄÑ*V̧*ä*PëÛ œ:ŧ}ú°üÇs7—ü¤Ó¥ÓC{÷féáÃx¡æ>ÿ:`³æÌÉ: µ`kûËKƒ‰›¾—Æ ÙãæNËýqªWÌbóÉÉüñã9¹z5ãvì Un·®{ô.\ÈX‡†ªùâÆxy©+ÔOÖ>>è…~uê°õüù,W¡¯½ü2Ë~ø!ws…–Ä-ÆYǯîbccƒS3ÜÜÜÒš£äÄÏ„îuRDhY{ââÒ‚ç%JXuðlŽBŸ¡;£1ÒA->9™]»2ý£òu.îܨ(õˆŒ„¨(úEFò²ƒÀ`:¸sïµ–,1þ¼®®ê*ð“ÁqêÇÞÞyZÌ5bî\º Ä_AAyöœYÉu'B[[pwwwÞýüs–¦ìÈT©+>ý,¼óFVªúúò·ç).Ç$RÆÏ'GË7;ÊÎØ9sXðøqîƒgPsÔ5RŒ’’ ,,s`¢Ý©îÜÿUŒœ±ñ÷§¯»;Kíì˜Eîþâòå™ôD(Ú¤ˆÐ¸"@›“ZÎc½žÑ/àfkËgÕ«[xF93GaÓV­¨2w.ÝrXÙšïéÉ =z<ûÅE}Û:CPœöç“ÏâM`° œa| 01õ/¯Ì«Æƒå%žýëÈ…Î={²a×®|/Ây–N„©;rük¾7žRÝæÍÙ½s'}ë_а$www¬\™·Ojo¯®,ר}ú¤+Ššö‘U:ÈíÛéç=~ 'OÒ¨ ŒÇ°ÄU@ãáA¥3gÔ_"+VÌÛù‹IŠ+Ò)æ"o…X֔˗Ó:.÷ócŒ•t´¤ÄÄDÔªÅO—/gùø`D“&l9r$û'ÑëÕ‚¨¬áŒŠ2í-è츸 /[–W"#ùþñclQWŸ§Õ¯Ïš Ôù) «„º/töí9n½ô-ìH$9Y‡-Lpÿœöeܺ•e`½ùÆ ôŠÂkN LÒB%¨WÏð¨SG]!E†Ä-ÆÉ ´(ÔN>|ÈÇ)­‹[¸»óV{tEŽŽŽ¼8l;çÎ¥kÁíòâÅ;jüòKöÁqLL–[¹™ÌÍ Ê–Urå²þ{Ù²P¼86@ÿ5køvìX^ç£Ò¥™¸q#äfç ‘IÃF8j½Ásvv4›¬T)õxáƒá~÷ïójƒ ¸r Ð8;S)c}ÁÝ»°oŸz¤²±Qw©Wê×O¬«T1ë>ÖBX  E¡¥SF\¸@²¢`¯Ñð¥¿?6r³O3rÒ$®]K×'V¡§îÝcú¨QO÷ÄYÂOɹ\ÑêÀ+‹óbH·ëÖÍݶu"[&çO‹ÏÆÝWß{oÇŽeP|­ØááéÛí=|¨GF•+g^­öõÍ´wö“Žÿû/oîÚENIv?99ýÜs9œ!ò‚'´™H¡y]IH`fx8þ..̨\ٲʅ|-"LJ‚ᅦeËàÐ!F¯‘@?N•+Ç´3 ƒboï<íŒö´ú`ko/«Ï)ž¥ˆP=©÷–~“&ÑóÝwÙ•ÓÎ66jGD??xå•ôñÔÕêŒõ™3pÿ~ú9©ÛIîØ‘>æì¬¦\=™_¡Àwê'Ÿ°¢M‚"#³œ’ì®\™­£G?åw@˜JŠ“ÚLäѼ޸x‘x ð¥¿?ŽVÖ®;'ù<ߺ_|Ÿ}¦æ0§p´³£]Íšì:ž.ÉɬðôdÌÆð4Ûp™ ¯lüÄ"Bg‘©÷–~”(Sæé^—ÜÜàùçÕ#•¢¨+ÓƒêÓ§Õ”ÔŽžÃÑ£ê‘Q¹ri+Õ¾õê‘àçGDdd–«Ð?8;ÓsìX쬤yNa&1‹qòS( 11ü~ç#Ë•£õSæã §N©«Íß~ éã%K¨QðÖ[Œ,Y’AµkÓ:,Œ“¾¾LµÒàY‘7lllèЭ[Þ=¡F£V©½z¥ÇÇùséé©«Õ)÷g@ý…>2víÔ­)k4=±A˜lªTIVŸ…ÕZ*±IIŒOi×]ÖÁEU«ZxF Ó©í?ùöî5|¬~}xçxíµ´\eG m@}yoÑ"óÏWQ8¹¸@Ó¦ê‘Ñ™W«/\€äd|E! V¡ÐhèY¾r„ð„Üíì8ß´)å óoÏŠ¢æ .[»w§o+P«–ºÚý¾üÒ°Ñ€‹ ¢Ì5kZn~BX‚мr÷.ìÜ©¦zü÷¿jMIF 4i’^„X»v–OóÕ’%Ü 3z¹6Ý»Ó6/Ö˜‘Ä-ÆI´(pV®_ϵV­ÀÉ }hNžäËaì"xžúH=J•JkÞ2yÑ"¾>wŽùÙ>ý?U«²aÔ(3}1Â,¿dWDHB~ÞX¹~=á­Z©oµ8:¢õ÷ç¿þ²ìÄRŒ›7 J•²}< ØåìL¯ Ôí—Rƒg''u7S§Y¹úô‘àY%E„"7 Z¡Ùh4ТBHˆÚœ*0š7O­¹uKÝ©W/üŸžÛÉÉd÷?o»“ÝÆŽÅ¶ßÃ%f1Nh3‘„üg—œœÌÒï¾CÛ²¥ÁøÝ®]™¶l™…feÈÃÿîÝ9šM:Éz`ðÍ›hnÞTÊ—W·Xº~¾ú êÕ+:…>â™I¡È ¹·˜¨F ˜2PwïX¹ºtQW­âãÃÇÙ|úæªUXÀWŸ%f1Nh3©”ê¤0Í’5k¸Ò²eúŠ@*GGÂ||¬~: Øô5Ÿnóf‡éÓ¡dz6mSõîݛ޽{[z¢€{ËS([VmLµs§º ½e ¼öþÅ‹s¸õÄéÛé:fL^}‰YL!´(î'&2góf”V­²|<®KëZ…nÙ’£OúëÁ­[£9rþùú÷cû› !„°nnЯlÜ·n1~Í>.VÌà”-Z-å¾^$H-¬žNQh¹h íÛg^}NåèHX•*–_…¾&NdÜÖ­eØ!2 ØU³&½öîU·IBQpÙÛã?lqmÚ¤­Bïº( ¶£FÁë¯ÃÇ–œ¡Ègòk’™HBþÓ{óâEÎþû/( nP!5í :­–ïvíâ¥víÌ !„(Xüýý9~ùrúκ»Ò_©‹* îºÔ¸1œýí³.]àìYõ-6 ì"…>ÂTRD(rCî-VÈÓSÝÉéãÓS:Ƈ޽!.ÎbÓ’˜Å8  ÍDò måíK—µQÊÍÝ(åÊèÓ:t€à`uÌ×vì€;Õ&)"…>ÂTRD(rCî-VlüxØ¿||Ô·mSS:²Èt$f1Nhavîßg@J£7[[vÖ«‡¹¥ÄÇÃûïC­ZðóÏꘫ«ºGç¹séÕÑB!„9=÷œºKGŸ>êÇW¯ªÍX>úȲóY’Z˜ÕÅøxzœ9Ãc½^m”R§ ]]ÍsñÍ›Õmé>øÔ¼æ×_Wóœ§LóÌC!„ÈŠ‡‡ZP¸l™ú𔔤Öãôì )ÅöÂ:Hm&’Ñ)Rn§4JùªF :xzæÿ…O‚6m`à@¸qCkÜþù¾ùÊ–Íÿ9ä‚úSI¡È ¹· ï¼£îUµªúñŽРAúQùLbã$€6“¢žÿ@§£ëéÓ\IH`AÕª Îïý”o߆7ßTƒå}ûÔ±R¥àË/áðahÑ"¯ÿ”¤ÐG˜JŠEnȽ¥€iÜŽ‡¾}Õ¯_Wƒ-R›}壢³˜ÂÎÒ(*ŠrB~’¢Ð÷Ü9N<|À[åË35—ßÛ·oó ¥ÑJN<==)îê +WÂÌ™éoyÙÙÁ˜10{6/žÛ/Á¬¤ÐG˜J EnȽ¥*^\mûýé§jÜÄÄô¶à_­6fÉE9f1•Ð"ß  á÷”@¶OÉ’,÷õÍýstïŽÿåË8ç°str2Ž °ìæM8s&ýÔ}5kÕÊõu…B‹{ûmµíw¿~ªîÕ |û­ºw´0;  E¾švù2ߤ¼Ôò¥¼;o'ûõãÝöÅœèäÄ{ö¤T©¢î­)«tBd+11GGGKOCaLÆpìŒ[¶¨5=íÚÁܹ0uªY~ É6›¢˜ÿiD×®P#¥QŠÓS6JiûÒKªVÇÙ<N§³ô”2‘{K!àî®î(õùçàä¤vÒ>]mvëVž]¦(Æ,¹%´™µ„üŸbcy'4€²)R¼ž±QÊè X™Í®K€ À… ðÞ{êÍ¥’Baª§)"üzË×´ÚŠ?«ýÉ….p­Í5‚_f‹ÝvnÈé3§ói¶¹£×ë™þÁtÆÌ{›ßB~ãÀ­ì<µ“®Cº°ù‡Í–ž^½^ÏÌgR£S :ufÄž´›ÝÿÖþlß¹ÝÒÓ3 ÷–Bä7ààÁô¦_¿ý¦¦t¤Ì?£¢³< ¢äs)§ (:ÿÜ»ÇK§N‘ ×ãfk˾† iG{=hÚ”µGâœa,˜çîÎç¿ü¢n:/„ÈÒõë×iõZ+®½t-ë¡öÿjsâÏØ›³3hú éÃ.Í.:&µ¥ÇÅŽiãµøŒ˜+·™Õmï¾ù®g¨ê;¬/;u;y\í‰÷Çôàu؋ٽg3vôXËLN~ÂèѰi“ú±­­Z,?}:<å;¾Pôâ–§!+Ð"O§çÙ³$¤4Jù±N< žÞèуϟÈóZR¼8Ž‘àY#&͙ĵ¦ÙÏŽp©Ò%6nÙh¾Ieáç_~foÜ^«fñ6²î6½KÐwADFFšrüüËÏüuç¯ÌÁ3€ Üi~‡6}dñyŠBÌÕ6nT·guvNí¶Û¹37ƒƒ 7zõëó¢›69tÙ Õj©9|83/6ãd ‡"@ïÞ½›þýûGÙ²e‰ŽŽÆÕÕ•+V0dȃs—,YÂäÉ“ñññ!&&†Ê•+óûï¿S¡B…´s¢¢¢èÔ©—.]¢|ùò„……ñÁ0cÆŒL×.ì ù÷““ézæ ×24Jy=¯¥ìÝ Àîî|®Ó1áÑ#—-Ë„  ¼¹Ž• ¡F–ž†(R K–,™é±D]"§¢Oq4òhÚq6ú¬ñ'um¼–˜GÌŒÅøû£eáÇ]?ò£ûæ˜QÖ¢0>Ooøì—ÏÐ7Õãë勯—/Õ¼ªáéd†.¬O(H÷–û÷ïsøÈaÎ]8G‹&-hРA–‹X"ƒºuáèQµØ7ßðfr2vqq ÈáS†WªÄÈI“2Ëî<Æ©Z§Ó1sæLðððàêÕ«Œ=š‘#Gò /àã㨅9“&M⫯¾â?ÿù÷îÝ£U«VðǤ=ßÈ‘#ÑjµDDDàååÅ?üÀ«¯¾JÓ¦M騱£Áµ sB¾V¯çåsç8•ò6Иòå™’›°'&ÂŒ°t)èõêX×®´ýê+>ïÞKÇŽñ¸aC|Ÿb_ik(ygÂ$©„ÝzvãLÌÃ`ùæY’ôI†Ÿ zrúî@ãºy®És$ë“3:E—yLŸy,·ç*d(Ç1e7®dÀ6wß/‹ÐAD|ïÿõ¾Á°—³—L{V3¬}½|)S,º´2uêTöþ³—s¡çhݤ5­[¶ÆÛÛ;_®÷4t:SçNeËÞ-Ü*}‹×Ü÷¸S"ª‹¦,¢o¯¾–ž¢u+VLm°Ò®ÇŒáÕøxú‘õù` d»v”.]:Óc111ÒLň"_Dxøðaš5kÆ·ß~Ë€êïiƒæ×_åÎô·æ–/_θqã¸xñ"¾¾¾\»v*Uª°`Á&Ožœv^ÕªU©[·.Û¶mK+ÌÉø ðúùólJùáåR¥øî)÷z6pò$  gSVÌŠƒ>R‹%€¿ÿøƒa½{³ûäÉB@ ‘“d}2Á·‚ ‚åÓ1§IÔeÿ.W ç4.×›Ó6ì½²—Çu²ÛÊþ¯,»—í¦víÚù1ýl)(iÁt߀¾ì¬¸œ Ç…N†E„ÏÆ…aØ„Ù0»Él^òzæçÊâeÍ @Ïá¼Üœ;ò‘ìóÛ9-Ô…‚‡Öƒ{µîeù¼YqupÍ2°öõò¥‚{…,SLŒyðàÿyç?ìÚOLÙO»;ÊÝ(Ljî#xoâ{Ù¦´˜S×z°Ûi7‰•ŸøyVÀó˜'s{Íë1–™\Asî:tÀ.**ËUèá•*±àÈ‘,è·ä•"µ•ÔÔŠŒ¿>|8Ó‹GݺuQ…#GŽàëë˱cÇÐëõ™Î«U«‡Îÿ‰[‰ÉaaiÁsËâÅÙX³æ³Ï:,Z³fARÊêY‹ðÍ7P­ZÚim_z‰Mÿý¯ÏÂê\½z•ÿýû?N]8EËF-iõ|«,S,L¡Wô„ĆË'£Oò89û¸¸cq•mDÓòMiR® MÊ5¡ŠG•´Ç[viÉÁ›Ñ—Ögú\çg^iüŠÙƒgPóŽímì±·±gá´…œxûQí¢²>Y~á~LX9óN4ƒ At›Ô¨VÙϳzxuŽí:†­“-—ã.z'”°;a„Þ UÿÆÕ»WÑ)éûF?Ô>äTÌ)NÅœÊô”޶ŽTõ¬š)°®æY ìl²~Yï1¸ÿ+÷?ô-ÒÿÝ“‹'s­ú5òxþc>|ïÃgû†<£-?náŸÄHôϺx4®I‹Ö/¢O—>”/_ÞüÌÂåË—Ù³wg.ž¡]óv´iÝ///KOKU»6/\àÕŠéwïžÁ*tN«ÏÂ4E:€NNNfÚ´iÔ­[—VZaFFFÒ¶m[ƒsëÖ­›öXÆ?k=ѺvíÚü÷¿ÿEQƒßæµZ-ñññØ<±­ŒƒƒƒÁXBBB[vãK®_­–šîîRžêaa0d ÿþ‹`co¯nÇ3e II8èõŸÛ°IôOŒYÃ÷EÆŠæXBB£ÿo4{B÷U6 }q=˶,Ã{¡7o½úSßšãó=NxÌÕW9}<-X>~ý8ô SRS4P̾ Ë6¤aɆ4«ÔŒ¦šâW šlç¼sóNFþßHþ>ô7·ÊÜ7ÐÜÑP!ºƒÛfÞôyÿžÖ©]‡7º½ÁŠ=+À õ­®ÔïÁc¨p¬+æ­0ž-1ç† 2èùA¬;¼ŽØ&±éï“'Z(ÿoy>™õ ®®®$$$P³DMj—ªéùtè¿NØ0ÎGŸçêë„Å©Aö•¸+êBOÊ¿y¢.‘ó±ç9}>m,•ÞŽÊ^•©V"=°®èR‘“žä˜Ë1ôeôéóËð¹ñuâYóû†¾:ÿ<û^ÙÙÛ‘¬$“¤OB«ÓòðÑC[:´:-Iº$Æ?Do£G‡Ž>ÿ€¸çã2Í/㜯׸Îú­ë™>~ºEN“““5a{®ìáVÙ[(® Ÿoþœ²KÊ2aÐƽ1Îâÿlll°usãåE‹Ø8v,ƒ´Ú´Ñ*Ubö¼yi¯¡ )õKi?“:¶¶!GÊ‚”"ìÍ7ßT\\\”£G¦ét:ÅÆÆF™1c†Á¹:N±··WfÍš¥(Š¢,X°@”œ·xñbPâããÓÆ†ª¸»»+ PFmp\¸pÁàó ÊØÖ›7›¿ÿVøë/Å¥woå¯Ó§Ÿí+W*J±bŠÊhP.øù)ÊñãVóõšsìüùóV33}¬Ã+›6 cQ˜áh‚âÜÉY™<{²Áçî>²[Ù|v³2ñ÷‰J»uí‡fY~nê˜ÓNJ³/›)uºÖQ·*gožUbnÆ(·nÝzª9Ÿ9sFùxÅÇJ:5”ùó•ëׯ[Ý÷ôï}+ƒJôU6”^©|Sé3åÛ²_(ƒ;¼¦¼öÚkV1¿Ô±U«W)µÛÔV*¾TQ)Ù«¤âVÎMiض¡réÒ¥g¾†N¯S ¤¬ß³^ùâØÊäÝ“•—·¼¬”h]Bqù?—l^ ÆüQ˜…˜Ϋ…Rçå:ʨ£”a?Sý0H©Ö¡šòÒÒ—”n»)¾î ´]×V)Ó¦ŒRg^¥þçõ•š+j*¾Ÿø*®Ï»*¥¦”RJ,,¡¸/pWœ>pR4M49þ>ÇÜçTíÛ·§R¥JòZ”ƒ"™Â1wî\–.]Ê?þ˜)x¨T©çÎ3;sæ @Ú>ϩթçÎ3 ƒƒƒ³ÌÍ*,Õ¬ç=¢×Ù³$¦6J©]ûéƒçï¿WÛ‘Þ¾­~\¹2¬[O¤Ï5rÃÊÚ®ßvñÓo?z%”FõÑ¿Gš6ijéi°jã*î6Ì>x¸[ÿ._lúž7·ÕØR«T-ƒœåzeêáhk<+Ôsvvv/înéiåáᇇ‡Y¯éíê·«7-+¶ÌôX\B¡wByuÏ«\åjöÁ3@8sÄËÕ [ìmíÕ?mÔ?³3öqn?gêñ©Ò‚v«³¹aC¯{ñÿíÝwxTeúðñoz% ’@B@ ($€Š(mA•u!ŠŠ,¯.ˆQv­‹bC±ƒ?Á‚iŠô&ZH% „ô>ÏûÇ!“L23Ì$p®k®™9ç™sîgîyÎSÚôkCVQ–Þ#»8›¬¢,ŠË‹ký|¹¦œK…—¸TxÉŒ¿²V3Çfx»x“ùM&# ê,›Ú9••?®ä¹YÏ™½Ÿ†pÿÔ©Œ÷]ºÆÅ™ÔöùFÉYÒM—@¿öÚk¼úꫬ^½š#F,sï½÷ò /pùòeÝÅpÏž=x{{3tèPn»í6üüüØ¿¿n éÂÂBbbbxâ‰',s0–re¢”ËååØ_têÄí×2QÊåËðøãðõ×W—M™ï¿ÿKRXVFF™À!»Cä‡åCwØ–¹¥ó–2,hŸ¿÷9...Æ7d¦Ò íXÈéùédägè½NÏO×{Ÿ“”·Ù` `tnÑY—(÷ èIdËH\¬×Nܸ¼½éЋnÁÝH,H·ÚË:$9ðécŸ2åS,`5Ÿ¾ò)#žAjÿTîtýæ³oêü?_T^¤ŸXe×H¶«&ܕܒÜ:ãË+Í#¯4O;¤±¾ò~°óÀN#…,ÇÎÎŽq³f1ñÙgÙ¶`µÃ¹!ÜT ôŠ+xá…èÛ·/‡æðáúuwÜq‡®#áC=Ä¢E‹=z4O=õ'OždáÂ…¼ôÒK8;;àààÀ³Ï>Ë3ÏÉÉÚ÷¾¾ðé§0vl=F+n†Œà`·ƒPõ·U Èl‘ÉÊ”•”ÿ¿rV}¾Ê¤í•iʸPp¡ÖD¸ê뺚XÔ`JC¸R>‚Ÿfüdúv…¨Îÿ#“<8Ùpè”щþ^sH@KêÞ­;у¢Y¼k1z^м¸‚vñÙüÏŒþ`v±w!°Y ÍÌ©£\SNvq¶Ñ„{ùêh(¯{c%°1a3~™Á¸Î㈠‰ªu„”†Ò¯ß}]½C¬”¢¼y† {F¯\TTÞ{OfÊ4×M•@k4¢¢¢Ø¾}»ÞºU¦‚öõõåÏ?ÿdöìÙLŸ>€€Þ~ûí5ËO>ù$®®®|ñÅ,Z´ˆ>}úðçŸPcßMy&†¿?Îñí-«ÇyÆÜÛ;EE0g,Z¤íÆ0j,^ õ5cá ¢)ÍÖм¿€'ô“ç*4­5lݽ•µ›×j´¶8»(Ûä±xkÓ̱þîþ´to‰¿›öyÛmœÈ;¡1¢vÉvŒ:òºö]]]3 Q)((ˆyÎã…E/páö P%ÿ´¹hCèÁP¾ýüÛF1ê«ϿÊmC˜ýÚl²U6%®%¸ä¹êʲo–5hÓ{[{|]}ñuõ­³Üšm§°(EïïXCT•óÑþøhÿGx;{3ªã(ÆvË]íîžþïšU—žîNBÂR£å<=£k,“™»i;ZRSîD¨€¿Ÿ8Áª ¸××—UæN”rà€vR”¸8í{wwX¸PÛyPÔ ¯ê{O_ööÞ[÷íÒlà0äÚ÷ãæàV#)®ú¾êkCÍ-’““øÀ@’†&ÞA9tÞљÛ×ë—ÒÍÒ‰õU* f»6ÚN„ÝØ±cɱÍ!-?BUˆ—½áá|üæÇo»mŠÂÂBRSSiÓ¦M£Hî+…„Œ#Éq?,,Ú¤zРhvìÐ/'»©j ­©)5È_¿a£î¾€'ÏœÑ%Ï==ùÚœ‰RÊ˵£i¼òŠö5ÀÀÚiFÃÂêþìMÌÒ¬S§N±iû&Ç&ªOw ¾Ãà]”†S’CâåD.'˜“¨÷:ár—R/okè6‰®ÆÅÞÅ`Rl(Avw¼ÆŽ°WñÂÔxyÙˤôIÑŸ™.Âö‡ñå¢/ë½Fç†NœE½[»v­îuS¨atuu¥}ûöÖ£H™ +ÀØ4ýN™6°:2~ ËáwX·ùÖŸZÏÚØµl:»‰¢ò" Ë Y»†5±kp°u`pè`ÆvËß:ýVî­¬uXzšRÎb-’@ =ë6l`Òôéìýå~mÖŒ…)Ú_Ø]]Y¡›(ŨS§´µÎ•³2::jé§žS·!Taa!Ï|˜­‰[¹x¼à‹_¿ ೦Ü1…Wæ¾RcâseejâZ’äËÅu\aÚÀ-íná¹ñÏé%ÅN–íúÈäGÔgÓæL#-/‡\J]èØ‘%?.‘f¢QiìÉs£W<ân‡O× p+…\W(蹟^<}ºù`c3›)ÚPð+ù­×Rø3—)Ó”±9~3›ã7ó¯ŸfàšÕÏ´±xž‹s¡v¦]›«cï«.KO·üŸåf" ´ÐóŸ?¤`Áþþßÿ3cNNüÚ­Þö&œ.JÁ‡Â3ÏhÛ=t릊»[·Œ\˜kÌä1üæ÷šþW§öUžŠÔ¶©¼÷.9srø`Áµ~^¡ÈÈÏÐÕW¯=N¼œHAYÝC=UçdçD°g0!^!„z…r$ö/DùÖÞÒÌåœ ONx’ûÂï3k_ ¡cÇŽl_»€¼¼<š5«£Q´¢I(,„ƒµõAûöAbâ•*²þ„,€<ªw‚(.†3gª.qƒ÷÷‚]„nƒÎk Óàž6ŠÂæ»)l¾›´®ÏÀ…®;bÇBz¤%U˜Ah i ×mØÀ¹ŽÁÏTb"mÛ²!"‚à+£Ô)5U;ÂÆæÍÚ÷¶¶ÚçW^ÑÖ@ “X¢áß|Á>ö]Ú·š¢öE¬Ù½†Q»Gáèf0INÊIªs¼UCš96#Ä+„ÏÝs¨W¨îµ¿»?6UÚläFåÒsdON9­ß#¿R!t½Ü•I&™‡%X"y–N„ÂÒAÙ¸Š 8~üj²¼oÄÄh—×­æÿw__6ìÊôWê*_+å C©a¨Ä¹ä¼›Tµœ÷XKÓYma¿ãÚGÔ˸–†Ò2{,-/Å+w([ÛÔßÏÎÚ$þZ4…&>Ö& ´…dddX;£þóá‡äþûߨ °]¼˜µ_M7S&Jùö[˜1²¯4F ƒeËà¶Û0âÓ믿Þàí ¿üþKr#êó4­]Ã_ƒLßns—æz qÕçP¯P¼Í7ÜÃÃO_ý”Gæ>Bü-ñ¨æWk¢“épº«¿\9Zo ;wjÇ™•¶Ð–¸¶45 úÉòÁƒÚçÚAV˜ps­Cýéjgƒv†¥þÀ›Í8ÊÚ¸µ¬]Ë_Pè˜@¼ÿ»Äû¿‹Ÿ›c:Žalç±Üv;Žv†+¨Â´ÇwUphÔÝ)3##CÚA! ´…4öqÅO?Û¡8h§*ÅË‹f-ZàŸ–uM–’•ÿú¬\yuÙÔ©ðî» ·¯¯IC~Á啿qàüb3b¡»‘Â-€Ì«om°¡U³Vz qõ$ÙÍ¡Ž™®ÑACØ¿~?³æÍâØ®c”àéìÉоCyùã—q¨´'îiã:ãîvwîm÷+x? ޹мÒ\!'. ÎM{ÎÒH-ø:#ƒ‡Þy‡²¹ú©çŒËÌÿý͵ý„Þ¸zΟ׾÷óƒ%K´ã; ««P¿pœ½){Ù›º—}©û8qñ¥:jXtJ GHÞšü!^!yÕZÓÑм½½YúÁR«ì[ˆ¦.66–œœ£å‚‚‚ 4oò‘ú~'å彌–ëÔ)›-[–ÕY¦¸ÒO–ëJz!2R?YîÐáj§õ3kb×ðë™_)(+ ¤¢„ŸNýÄO§~ÂÎÆçþØÆå£¹§¬æÆÜ)©8/IDÄ“õçÍBh iLsÊËùoB¤¦R¡üò <ÿ¼Á²û›~-ôž=0y2œ>­}ïáï½ÑÑ– þ&`JGŸÊ¦•ÉòÞ”½¤å§ÕZÞËÙ‹^½èÓº}ûò@£MÂÐÃ(ƒŽI™:Y&»iÌnÔN„#GÎ&+럌¨ðçž*}]nIá´ö‡a‹ùóOë%|M…“S¹¹q@]×–=Šèú””À÷ß_M˜€üüÚË·j¥Ÿ,÷ê¥mžq­¾ûnѵ¸žx:y2)b“"&Q\^̦³›X·–u'בU”E…ª àT6 ¯c#öPv[>=ûÔl®!“ÚBC'B|•žÎ3ññd\igf·?v=zPZÛužž±³cïŸÒç×_aþü«Ý‘£¢´CB,sMLll,ÛÈÑ“GéK† fR»²ê}êlŠa€ƒ­Ýü»Ñ§uzö¦O`:¶è¨7ºÀ+3^ṟ#¥o T™\Ï&ˆ6ÚðÅ¢/®©¡°s;>òÈ œ=[ËìiUÜygž{îÑëŠíz8;QT @ ¥”sµ¢¤<’¢+ ll¶[!º«ºw¿‡âb?£ån¹Å‡o¿}Çó:°ÔÚA\·à¾ZF­lÖ n½U?a ²l|–ælïÌ莣Ýq4åšr~Oüµqkùô§O)s4Pû\EEë 6ÿ¹™èFë-—N„ÆIm!Ö>ÿÊÏgÆéÓüY¥ÜPoo|ü1šÖ­q:r¤ÖÏäçóϱc9UÙÛÂÉ ^{ fÍ’IQ (((à‘'akÒV.´¼Þ°tëR¿ äþ÷3Þü:§¥}åýWø>ö{Óšb¡^¡ô 죫]¾¥Õ-8Ûvð пg}úQR²S(¢wwº†u哟>Á»®Î£BϨQ‘‘a|¼¨~ý:ðÞ{s–3•¹ÿ=…S§–-WTmÕº©((ðãìÙ¥FËùúF7x,¦YZçÚœm×–²2(-Õ>WÔ¶üZ>S}yewSÙÛk§¨š,wî|s-ÙÛÚ34l(CƲíýmÄS÷  °æ÷‹µs–¦@èÜåòrþsî?¯m®:9ñvÛ¶Lôóãç… 9:e Ó/×>#Ü<­\22R;)J×®–¿I9i$¿·üM_ý JRÚ¥°èô"²gg³xábàú›bô쟛ñ°Ú´iÓ†-ßoç-»;wêš)Ô¥mÛ¶DDDX "ÃN*6)1µ³‹nèPn*ªöùun( äæÖþÈÉ©{½nâ#އáuÝòo$´ÃÃÝr ˜2EÁÍÊ×ÕW{ë¹®Ž©0¸ï` Etc‘ú¥€eéé<Ï…+Í5ll˜ļܯԀŽ=šemÚàtè.¶“+E„vvÁ—^º:Ô¨aé7K9ìpM€áæÅí‹Yµc^ Þ)Þ¤¦½{ëfCM1êKcKž&MúÉÉÆkB{öüû÷kˆDq±¶qa¡v²ÑÊׯæ–5Å®]`g§M¢ª?\\Œ/3¥L]Ÿ3UI =zíIp]í{­ÍÞ^ÛùÎÁÁð£¶u†–ÿ½öx €þýþØšºGþþ‡¿>LÎ-µÀz.”Io4¾‰¨šI -Ä’\i®±«Js;¼½YÔ¾=\]k”ôµ×øtÒ$fVN‚RÅÛÀSÁÁÚ‰RäŠeÔòuËÉéZ÷pQ¹]sY·~] J*›b„U„1ªÿ(“›b\‹'žx…³gS–»ýö¾<ùdtƒÄ` ?Àx3{û> Rªö[ÝU__¸p‰òrpumQk™ª¯³²LÛL ôìi8±Õþ½g5ÍÕø«´5$mû_š!/Œu"ŒˆÐ¦dN"\Ÿ~ÿÝ´Z˜fÒøI|ýý×l=³•ÒvÕÆ×Ö€÷nož‹~?¿šw1ãÉÆFh ±D'ÂËåå¼pîŸTi®ÑÚɉwÚµc|ÃËÝ1|8Ÿ1-;[¯: È ¢ã‰àVÿdÜh.\àtæéºo—4û{†´¢×ѯ²)Ftt4ý'6ì•OsêÔgFËee=fÕÚ’4míjQÑÕ‡©ïMheh‡ÖŠŒ¬=Á­|]Ûð[5í¼ò\¿ªäåigd«/ÎÎàêjÚÃÅE;œ¼ßó5„„À”)WÿŠ‹¯>ª¿¯m™ñ)š–]í‰oåÃÓ³îõÚmÔ6fq»v• tÝ=< oß8Ha5ë¿Yϳ/>Ëêm«¹Ôò…®…øäùàêÍ›Ï½ÉØQc ~N:' ´…4䉨€/ÒÒ˜ÏÅ2m[G[[žlÝšBBp«­ÃZy¹v»eËxôÄ >fVYý¶OmÞ,És-ÒòÓØ‘°ƒ‰;Ø‘°ƒØK±`—>%0ªë(ÖüsÁÕ–™-ÌhÜ+*L¯íLO‡7ß¼¶ä·òaÂh×­ þú«>·Ø03zxÀ AúI­© põdØÕÕüÉ(Ö¬1-nÝZÛªìz”•™–hJÐ,0-Îöíáõ× 'Æ–½¼.µäÎ̦TÚê–lèPnvvv¼õÊ[Ì˙ǡÇ8’>=úÐ¥K—:GY’äÙ8I ›¸CyyÌ8}š=Uî{Ýy¥¹FGÍ5í7øÒ¥°|9\¸ÀÀgÀ4À…+µÏ½{Ó±cdž=€&$97Y/a>uºf!W´ItX8$;pûm·7T˜SR¢½m^P`ø¹®u¦<›Óê)!AÛDßZllLëÐæåƒ×¼%n©×½z]½.áá°~ýuÿYš„Ê¿Q³fÆËVgjM¹ŸŒgþöëKii6¦Œñ¬ÑX·:~ôè.$'¿h´Ü°a#>˜Œ§§'CaÈà!Öå†! t•]^μjÍ5‚y·m[Æj®qñ"|óvÜæêCÖ¹¹Á¸q<Ú¥ Ÿ¾ñ3³³y»U+žZh|F¨YÂå]²¼#qñÙñ˹ػзu_¢B£Øp|/‡×ýNéäÃM9JÁ~›+§›[¯¡ŸFcúmë'´·t %¹Ö¾õ]GÇ«¼\\ôÕ—{oêgºtS§ŒÇÖ©¬]ÛðƒÚX{ZbS•”\¶_y×ìÊ£R&\Z£1¹­ËMí•W #£ö¡J+õìù˜¢©Ý»ïÖßB44I -¤¾:ÖÖ\ã©  žƵjsÒRøùgmmó/¿è7¬´±ÑÞ£ŽÖŽHïî®­…^µŠÑÙÙätï~ÓÕ>ŸÉ:£—0'å$,çæàÆ€àD…D1(d½{ãh§½¶üÑhJSÿ¾š ãR´Ó¥Vº|߆¢Œ•ìÝûA­q˜2¡!YYÚ¦ úÏÕ__¼hzò››«·>ØÙi«¹ºšþüÁÚxéÙ¶o×&´7Ó°7êL„ï¾; =æO豫ëÜnOᲿvÜÚÈÈ[!º¦gÊ”ñÀµ_[ÄÍG:' ´…ÔG'BCÍ5†ûøð~ûö´w©ÒýïàAmMóò噩¿‘6m´SqOž¬L³šG_{aãÇós#¯}ÎÍÍ%!!öíÛãRõØÍw)N/a>Ÿgx'$*$ЍÐ(nmu+ö¶uü×)ñý`ÉTpM—"(lỨ{Ùª3ææÖ W¾¾p¡aÚð6kýú™Ÿøz¾–I ¿ùÆ´ÚÞþælªoîL„J%m´œ››u‡Ú¸ï¾QW߬/…Š«á“"¡cíYRx¸³I“¤ ÙðÁ˜ ú,§BÔF:' ´…\ω˜UVÆóçÎñYZš+Í5Bœy·];ÆVÖ<¥§kG–_¶L;~UÍšÁøñÚîê·ÝVç}Ü;†gÉ?4ºÚçòòrÊËËÙ²m sÞ˜C®C.……¸f¹âkç˧o|J×.]qpp08ËŸBs!F—0ÿžø;†Ôx;{s[Èmº„9²e$v6æ~aÁåMWúÔú¿äKJ´cØJ†32–¦}oêx¸µqpí£eKíÃß_ÛvóJó÷:ué¢KÔÍÛ;ŸÎ£– ñ©×ýš;á©SÛëuÿ7»uë>±vf‘äY˜J’gã$nÄðyZÏÅÇséJs '[[ž bnH.ee°zµ¶‰ÆÆú÷åmmaèPmÒ±±[(7aº;‡úž«WÔ+s;Š››t"¦’N„ÆIm!ùYY<óØcT›s>²o_\dÆéÓÐε Àˆóçyïå—i{ò¤þ†Âï6ÑhÕÊ¡[MÌÅ~9± q¿Qä“iü¶@’—_Š7i.«Jnn5âÚžkkS\éÿ3cǵ°Äœ½½=ööòß¿©“ÄY˜C’ga*Iž“oP q^;šîÚD9ì7èôô?kp{vÅ~øæ¡“ÓPúµJïîí˜ýc4ñ* ùèUX-`“'¤ÿ·ˆ«%ÏþxÝÛŽ>ÂTÒ‰P˜C®-ÂTÒ‰Ð8I -¤ÔÛ€KÓ¦qiõjèӇд4-ZÄÈ3g´ ó”)Ù8f¬ªN££G 'É µÌ‚ç’¡Û¡÷•¤Ù÷„Ám»ÚzÑÃ{0w¶ʸC‰ðïR£Ìܹ@þW°µù³¡U$6‡8/Èšeãèú;h+‘Ž>ÂTÒ‰P˜C®-ÂTÒ‰Ð8I -¥²Ã_Ë–ØTT0àãÙ¬Ñà §NiçÏ(pyü>‡®—À¿RšÁ1Èü/”>Xc;nnھϮRôÜ~#¹â ªT­v²s¢_P?]ÂÜ;°7¶æý`¸çžpÚ´™i`Í>½w·Ý&ã !„¢aIm þþìÊÏgÕªUÜrË-´k×®^7_\¬mbQ™$WM˜ Gí~? [ݪÌCœýòà÷™°ç>ðêüR }öpŽmì»ø{RöPZq%U®2¤°½­==zêæAp¶¿¾qªß~{Îu}^!„¢¾Hm)W&G©téïgÒ¿¿ [«Õ:´ÚìÍUThÛJ’““MŸEÏÎñ<]Žè'Ï•l€¨\¸ø9‘ÎÌHö¦0¾Ð@1º·ì®K˜… ¢™£Œ2q­¤£0•t"æk‹0•t"4NhKÉÎÖïçGEo49e†ËJi›XJ’ããkääµòö† }{ísåëöí¡Uç î¸X÷†ä’› U*Ê;·è¬K˜‡ÆÇÅÇ´`„QÒÑG˜J: sȵE˜J:' ´¥äb³ô ”ÚšÝrð(&%5‘ÌLÃIò™3PP`Úæ]½ò íœEëöÙø…dá…[‹,=³(±É&«(‹¬¢,¶eñ}vY;²ÈÞ˜MAóüšã.Wç¶v<Ø#š¡aC6„Vî7ö,ˆÖ$_pÂT’8 sȵE˜J’gã$¶›³(—*ã;'šs9éVtw_m4à’ ÎÙÚ!àZeiŸ]²À%;÷,Ü}³pñÉÂÖ- å”M±MyåYª2Nºâ2®<êƒÜsZ°dô’zÚ B!DÓ% ´¥¸€nïJ£2©Øõ;ôo«M’rÀ¦öaØ*€œ+@¯ã^]œíñqñ1øX·{§òNAM–mÎÛ0mÊdÓv&„Bqƒ“ÚR %»Þ@i1xÇ›´‰fŽÍjM„«>¼]¼õ޻ػԺÍém§3xê`’'.  ,6Œ¹oÍ5)Fqý¤£0•t"æk‹0•t"4NhK1Ô–¹ì+œ˜q¯ñ¤ØÙ{ÛúÿçjÓ¦ ³ÆÏbÁ Hï®FAë½­yÿ?ïãååUïû†IGa*éD(Ì!×a*éDhœ$Жâi`Y’--‹ºð͸o,NU³þ5‹}2}ît.•]¢È­×|WÝùòó/iÓ¦Uã»ÙÈœ0•$ÎÂrm¦’äÙ8I ­¥øµ-þA­ ½níÅ(..&))‰°°0ÅôâB!„‹­µ¸)åKý ó_h4&ö´ggg:tè ɳB!D-¤ÚBœpÙâŠc¡#>-˜<ýa<=½ÜÚ¡‰FF:úSI'Ba¹¶SI'Bã$¶þ‘ýY±b~~~ÖE4rÒÑG˜J: sȵE˜J:' ´…Kò,L"_pÂT’8 sȵE˜J’g㤠´B!„fZ!„B3Hm!%%%ÖA4qqqÖA4—.]Òu$¹¶SIÎbœ$Ð×éСC<ÿüó<üðÃüßÿýËeddX82ÑT½þúëÖA4;wîÔu$¹¶SIÎbœ$Ð×aýúõôïߟàààÀ3Ï<øqã(/×Û9))ÉJ !„B˜Or—ºÉ(×H£ÑðÄO0vìX¾ýö[¢££éÛ·/ëÖ­cܸqVŽP!„B4©¾FÛ·o'!!1cÆè–õéÓ__ß&;TP\\\ƒ·‘ûᇮ{×§9û6¥ìHII©uýñãÇ9sæŒÁuéééìÙ³Çäx£úø·¬KBBG޹îí\Kœæœc¦Æi,ŽºÖ7ôߺ¡ÉµÅ¼²rm‘k ȵ¥±“ú:u €ˆˆ½åáááºuU•••Y$®ë!_ræ•m¨/¹œœÓ‚´2ù’Ó²æ—\nn.¹¹¹F÷mmrm1¯¬\[äÚÖ½¶4…œÅÚl”RÊÚA4EÿùÏxã7(((ÀÞþjK˜3fðÕW_é}©uêÔ‰ÄÄDzö쉃ƒƒÞvüýýõ¦ËLJJ²Ú²²²2t¨7Ä>4hÐum¯j»,s>»ÿ~hÒ>vîÜÉÀë,—€¾¾¾Ë8p€-ZZ㳤§§^ã³kÖ¬¡gÏžêÜh¨˺–=zGGG½©‡¯e{¦ü[^Ïÿ…ììlòóói×®Ýu7ndðàÁf}öСC€v*ïÆtnT_vèÐ!<==iÛ¶­ÙŸm}È—¡…±³³`C—ì»|Ь¿Ÿ©Ë*;N7ØÿS®Cyyyøùùáëëk°Ü¹sçÈÉÉ¡sçÎ5>ëèèHVVááá5>»fÍÆרΠsÿ/ÔDzôôt’’’èÞ½ûumÏœï”Jæü_°±±¡¤¤„víÚÕYÎØù·sçN‚ƒƒëülå÷ê¥K—ðôôäÈ‘#´nÝšØØX„a’@_#sè)S¦°oß>Zµj¥· ™éG!êæVê‚k‰‹îýe×<Êì¤vLˆúT½ÃàùóçéÓ§Ë–-³RDŸt"¼F”––ròäIºté¢[C@@€^Y9…B!nÒúuèЀcÇŽé-?qâ„nB!„¸ñH}Lhh(?þø£nÙÞ½{¹xñ"ÑÑÑÖ L!„B4(i}Ö¯_Ïøñã‰ŠŠ¢M›6¬^½šðý÷ß뵋B!„7©¾£Fb×®]ôìÙ“²²2,XÀš5kLNžï½÷^Ú´iC»víˆŽŽæâÅ‹ ±hêRSSñööæ±Ç³v(¢ëÛ·/„††ÊŽ;¬’hÄ:Dÿþý  S§N\¾|ÙÚ!‰F(77WwM ¥E‹DFFZ;,«‘h+JJJ"88˜²²2fΜ‰§§'ÿûßÿ¬–hÄþö·¿ááá««+Ÿ|ò‰µÃTß¾}Yºt©Þ€BréÒ%ºuëÆûï¿ÏرcÉËËÃÍÍ­Æ«BT7uêT:tèÀ³Ï>kíP¬Bj ­¨r;Z·nMAA•#ÙW_}E‡¸å–[¬Šh~ùå¶oߎF£±v(¢ûüóÏéÝ»7={ö$11///Iž…Qùùù¬Y³†)S¦X;«‘ÚÊfÍšEdd$›6m⥗^²v8¢‘ÊÈÈ`Ñ¢ErŽ“ôïߟ¬¬,>üðC:vì(“!ˆZÅÅÅqæÌÆÏÔ©S6l¥¥¥ÖK4r+V¬`РA´lÙÒÚ¡XÝ‹/¾ø¢µƒhŠØ·o™™™,SPPÀo¿ýÆÖ­[±³³«1‘ @÷îÝéÓ§{÷î%%%…Áƒ7päÂ’”Rœ>}šM›6ñçŸ@³fÍ –]¿~=_~ù%ýõ^^^ºYÈ|ðAž{î9:tèÀž={¸té#GŽ´Ôa ÉÍÍe÷îÝlÚ´‰ÔÔT:vìh°\bb"_ý5kÖ¬!33“Ž;êfê¸ë®»:t(&L ??Ÿ;vp÷Ýw[ê0„…ràÀbbbðññÁÅÅÅ`¹¸¸8~üñG’““ñ÷÷×+·cÇÒÒÒØ¿?ÑÑѬZµ //¯ZÏ=Ñ4Õ×µ¥ÒŒ3xüñÇoîfbJ˜eÓ¦MªE‹ P€ŠŠŠ2X.))IuêÔIy{{«[o½UÙÙÙ©ÿ÷ÿþŸÒh4ËïÝ»Wuîܹ#ÖУG([[[¨mÛ¶Õ(£ÑhÔC=¤ÜÜÜÔ„ Tÿþý•‹‹‹Ú°aƒ®ÌÀUÇŽUÇŽ•ŸŸŸòòòR3fÌ°à‘ˆ†¶víZecc£;_j»¶>>êî»ïVEEE˯^½ZÝsÏ= ¹°†#F(;;;Ýw‘¡k‹RJ-X°@ÙÚÚªnݺ)ÕªU+õ×_éÖ¯Y³F=ôÐCº÷sçÎU/½ôRC‡/,¨¾¯-111ªeË–ª¬¬ÌÑ7^’@›éСCê½÷ÞS;vìPQQQµžˆ#GŽT;wV999J)¥~ýõWecc£¾ûî;¥”R¹¹¹êàÁƒª¢¢B¥§§«©S§ªçŸÞR‡!,äûï¿WT¿ÿþ{­_r[¶lQ€úå—_tË&Nœ¨‚ƒƒUEEEòï¾û®š6mZC†-¬àäDµ[øIDATÉ“jýúõ*--MÝu×]µ^[¨  ûòú믿”½½½Z²d‰RJ{mY½zµJNNV[¶lQ‘‘‘jÕªU–: a!ï½÷žZ¹r¥Z¾|y­×–(@}öÙgJ)¥JKKÕàÁƒU×®]ue.^¼¨‚‚‚TBB‚ÊÈÈP]»vUÇ·Ôa ¨¯kK¥'Ÿ|R=ýôÓ v£' ôu¨íDLNNV¶¶¶êÕW_Õ[Þ¾}{5lØ0¥”R™™™êÎ;ïT¡¡¡ª_¿~êÅ_T™™™–[XÁîÝ»ký’›4i’rssÓ»;ñÝwß)@mÚ´©Fù_~ù¥ÆMÜXj»¶œªœœœTyy¹nÙ7ß|£µk×.½e=zôPC‡U_~ù¥%ÂVr=×–J?þ¸:}útC†Ù$Èl àèÑ£h4ÂÃÃõ–GDDðÇàããæM›¬žhdbcc ÇÆÆF·,""B·îÎ;ïÔ+/mYo^qqqtéÒEoyDD¿þú+nnn,Y²ÄⱉÆçðáÃ5Ú°V^[>L¿~ý˜4i“&M²JŒ¢q0åÚRéý÷ß·X\™ŒÂÑRSSÃ'â¥K—¤‡³Ð“ššZãÇV»vípqqÑKB@Ý×–ììlŠŠŠ¬–h¤RSSkœ+:uÂÁÁA®-BOåù`¨âO®-†IÝ ðòòÒ[îããƒRJND¡§¢¢GGG½e666ØÛÛSQQa¥¨DcTy>T§·òü‘óETUXXXã{ÈÁÁwwwÝ÷”põÚQý»H®-µ“ºTWWyK¤RLL ...xzzZ#,ÑH£·,11‘¼¼<¬•hŒ*χêç˱cÇhÖ¬îîîÖK4R­Zµªñ=”––Fvv¶ÁaUÅÍK®-擺„„„†OÄÊuBTj×®'NœÐ[vìØ1Ý:!*Už†Î9WDu!!!¿‡*× Q©òúaè|‘k‹a’@7€^½zÆîÝ»uËrss9vì'N´bd¢1šø ³fÍ¢G|òÉ'$$$°ÿ~¹uvyûí·Ù¼y3999ìÙ³‡^½záããÃàÁƒ™3gåååŒ9’#GŽ0eÊRRRX¹r%Ÿþ9S¦L±òKÉÈÈÐý{bçδhÑ‚7rûí·[<^a=ŽŽŽºs¥ò¹’“““îõwÞÉï¿ÿÎ×_MLL ?ü03fÌ/¸›LNNŽî»§òÎfzz:—/_Ö•qqqá?þà…^àƒ> yóæ,Z´ˆiÓ¦Y%farmiR-„B!„¤ ´B!„fZ!„B3H-„B!„$B!„ ’@ !„BaI …B!„0ƒ$ÐBˆÞôfckh—.]bûöízcÛëÈ‘#œ:uª£ªÛÉ“'IMMսߵk)))Ûÿµ¨³BXŠ$ÐBˆ&-99™G}T7Ëcxx8“'OæèÑ£º2?ü0Ÿ}ö™ÅbÚ¹s'C† !??ÿš·1sæLÞyçzŒªv999ôéÓG/7nß}÷Ýuoûرcõ²C6nÜȽ÷ÞÛ ÛBˆºH-„h²~üñGºtéÂîÝ»ùÇ?þÁW_}ÅŒ3ÈÉÉ©1ãVSóè£2vìX‹ìëƒ> k×®ºé}ëÓÏ?ÿÌSO=UïÛx衇ˆ‹‹cëÖ­ ²}!„¨Lå-„h’rrrxôÑG0`?üðƒÞ”´3fÌ`ýúõ?wñâERSSéܹ³ÞgJKK)**ÂÓÓS¯|nn.NNNº²yyy888àìì\ë¶êŠÙÞÞ777ݲÔÔTΟ?Opp0þþþºå£GÆÎÎFCnn®Ám6kÖLW ¸¸˜ØØXüýý 0SAA .ä“O>©µLrr2ÅÅÅ´oß^·¬¼¼œüü|<<<°µÕ¯‹ÉÏÏÇÖÖ;;;Š‹‹Ñh4º)¦quuÕ•-++#66BCC î?''‡¸¸8üüü Ó-wwwç‘GáÕW_åöÛo7z¬BQo”B4A¯¿þºÔ‰'Œ–íÞ½»š9s¦;v¬²³³S€ PÇŽÓ•ùøã•§§gφ„„¨ùóçëmkÖ¬Yj„ ºmµjÕJ>|XWfíÚµ PÙÙÙJ)¥4š9s¦jÞ¼¹Úµk—RJ©7ªÎ;+{{{Õ¢E ¨éÓ§ë¶¥¦M›¦”R*66V»wïVJ)U\\¬ž|òIegg§ n»í6•””TçßfåÊ•ÊÎÎN•––ê-÷÷÷WóæÍS}ûöUNNN PÝ»wWñññJ)¥ÎŸ?¯ìííÕòåËõ>WXX¨¼¼¼Ô‚ ÔüùókÄ;qâD¥”Rêõ×_WÎÎκx»víª÷o¯úõë§ìí핟ŸŸ²µµU=zôÐÛß®]» ë>>µ¶±.))áïÿ;ëÖ­c×®]ôë×€yóæq×]w‘››ËÅ‹ÉÌÌä0¸Ž;RTT¤{2jÔ(Ú´iCÇŽxá…X¶lË—/çòåËìÙ³‡ââb¦NZç±?~œj¬›?>Æ #==ß~û²²2&Mš@«V­3fLã^µj………DGG3gÎæÏŸOHHJ)”R¬X±€?þ˜yóæñá‡ráÂbbbhݺ5&LÐu¾|óÍ7qrrâÂ… dddPPPÀ‚ ôö×®];Ýq!„¥H-„h’âãã 6¹|`` ¯¾ú*ôèуûî»_~ùåšöݲeKÞxã Z·nM÷îݹÿþûÙ¸qcr—/_æ®»î">>žÝ»wÓ¡C@Ûl!..ŽvíÚáââ€ýû÷7¸?œuÿþ÷¿üñÇüüóÏx{{“——Çûï¿Ï´iÓ˜0a®®®ôéÓ‡9sæ°yófk=–ãÇë’ÐêÂÂÂx饗ðòòbÈ!Ìž=›={öpèÐ!¦OŸÎöíÛõF ùì³Ï¸÷Þ{ñõõ­óoøÚk¯1~üxzè!¼¼¼çµ×^#66–?ÿü€¿þú‹àà`]³gggî¸ã½íøúúâáá! ´¢$B4I-Z´ ++Ëäò#FŒÐ{ß­[7NŸ>]kÛ⺌5 Ýû=z_c[wÜqlß¾???Ýrüqžxâ ºvíÊK/½dòpl‹/fáÂ…|ÿý÷têÔ €3gÎPZZÊÞ½{™0aãÇçÞ{ïÕÕ°×5^RR-[¶4¸®z²Zù>66€¡C‡Ò¡C/^ @LL »víbÚ´iuCvv6iiiÄÇÇëÅûÊ+¯`gg§‹wæÌ™|÷Ýwñøãë¬RUË–-IJJªsŸBQŸ$B4IíÛ·'11‘‚‚“ÊWí¸è:¾i4½„¸ª’’’ËÜÝÝëÜV¥ð×_qøðáÛxõÕW9yò$#GŽä«¯¾"44´Fó„ê6oÞÌ¿þõ/>ýôS†ª[^ZZ @¯^½¸çž{1b£Fb„ |ñźf†´mÛ¶Öñž ¾¯lbaccÃc=ƲeË(--eñâÅtêÔ‰¨¨¨:£2Þ®]»êÅ;fÌ–,Y€?~‰½}ÍKsLL ãÇç™gžáÁÔ[שS'lll())!::Ú¬»téÂçŸnpÝ–-[ ¾¯lŠ0eÊæÎË·ß~Ë—_~Éþó½Ï¸ººÖøâïï··7™™™FãmÞ¼9O?ý4O?ý4?üðcÇŽ%..NWûž––FAA]ºt1éx…¢>H ´¢IêÝ»7<ò/½ô , °°ÐÖoذnݺ™µ½~ýúѬY3/^Lyy9‰‰‰¼üò˵ÖL›jÁ‚<õÔSŒ1‚ 6™™É–-[PJéÊQ\\lpŒ1‚áÇóꫯÖXïééÉÔ©SùꫯX·n®†·¤¤„U«VÕ_·nÝHII¡¸¸¸ÆºãÇóÅ_P^^ÎéÓ§ùðà §OŸ>º2>>>Lœ8‘'žx‚¢¢"¦L™¢·Ûn»ôôtöíÛ§·|öìÙüôÓO,_¾\wA£Ñ°aÃÝwß}÷^ò““\­Á8}ú4u§BÔ++"„׬¢¢B½ùæ›ÊÙÙYÙØØ¨Ö­[+{{{åîî®fÏž­+×½{wõì³Ïê}¶úPsJ)5{öl¨æÍ›+[[[õâ‹/Æ®ú¶6lØ ·-CÛ~ã7”£££Z»v­:{ö¬rttT-Z´P={öT-Z´P­ZµR_ýµ®|Õaì¾ýö[]\þþþzƒ*¥”ÊÉÉQ“&MR€òòòR:uR...ªU«Vuþ KJJT`` Z¶l™Þrõïÿ[…„„¨ÀÀ@egg§ÕjlcÏž= P“'O®±N£Ñ¨‰'*///åèè¨þùÏ*¥”*--U³fÍRvvvÊÕÕUuéÒE5kÖLùøø¨´´4¥”RÍ›7Wîîî*22R…„„(ûéÓ§«‘#GÖyŒBQßl”ªR"„MPaa!±±±ÄÇÇÓºuk"""ôÚ)'$$àææ¦72D~~>)))tèÐAo"ÇsîÜ9zöìIpp0gÏžÅÛÛŸZ·UPP@rr²n[µm;>>¥mÛ¶¥  €ýû÷“‘‘ADDááázÇ”””„““þþþäååÕÚÉ044T¯­r||<111”••Fdd¤ÑZô… ²lÙ2½¶ÚgΜ¡yóæ¸ºº²oß>Š‹‹éׯ_öß mê©7L_uÄÇÇãææ¦7ÁËùóç9vì¹¹¹së­·êš°”——sðàA’’’hݺ5½zõÒkÞ’••EPP[·n¥oß¾u£BÔ'I …â&WXXHhh(+W®dÈ!f}¶¢¢‚1cÆP^^ί¿þÚ@6þ|¶lÙ"Sy !,Nh!„\¼x¼¼¼LþÌüùóyë­·¨¨¨`×®]5jÑZFF...xxxXt¿B! ´BˆkÇÅ‹éÛ·¯Á™ …âF% ´B!„faì„B!„0ƒ$ÐB!„B˜Ah!„B!Ì ´B!„fZ!„B3H-„B!„$B!„ ’@ !„BaI …B!„0Ãÿ)­q`NlIEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/seq-chunksize-15GB.svg000066400000000000000000003737431231437614300251670ustar00rootroot00000000000000 image/svg+xml Automaticchunksize PyTables-v.3.1.1/doc/source/usersguide/images/tutorial1-1-tableview.png000066400000000000000000002571161231437614300257640ustar00rootroot00000000000000‰PNG  IHDR•Ê­zû pHYsaa‰f΀ IDATxœì½w`\ŵ?~ænUYuÉ’lɲܛ\$ÛtŒ&@ ônZh/„Gò ¤ç^ $B€„Žy”x@! Íl‚ÁØ’mŒqC–Ü$Y–¬²«]iwïÞùý1sÎÌ]«™p?k÷–™3gΜó™3s¯XãÞÚD²,+aY/¿ñËì`8pàÀô ÀM#õ¼¯Ÿl†a.—‹í®Û–H$^|kcJzVaY…ÛåúWKéÀ8pðo€XÜ<°gS$¼à”#Ýn7«ÝþÉŸÞÞ”V0%/¿¨­ËŒÄ-3±„õ¯–ó‹ Àè`xÄ–åKú øµÏÏL+~€Bz¸dY¸v§üȸ½œ« ˜&7çÀ˜ü÷ÐHJt¢ZT¡€ ‰8ãÀfÀäq-à²^º‰gœ ð@Ñ&y»øÎTuEb¶6S?r&u$Z*Oªz÷¤Ô‰*ŠÀÚ“ÔŒ1ιØ­óç @5ó^VÒ;—ÌαíJEŒ ¡ì¢l Óì4õs*]Ù†êP½Z¤I&¢÷é½·FzÙNàì[Y'éN“A }©RÊŤ¾Èv„†±VÑAʦ¥1piÚ\o%éC\Æ4ÛBïnE‘mcGVÍ@ $ú°12»Î¨ 6Fö]–¤ .5IŠä\ ;åU 4ë!—¢ô†Å’/£ŠÐ¶eë¹”D>ÆéIºƵjÈIÛZ£)x`$;@­OÈ6À®š$ ¤!»NðÞƒTà!£Ì@…ôÔô¯Ëp» Åkä¦zZZÂ-;Ï=y;‹ùR³Yc¶5uwÇ.5hpÝ•‘s‘AG™,£À„®\„VÍS(6£y"^™v#0àœ1¦‡"Žá/;ÕUYj„ãó˜ ÝV(Ê;Ã2ìqà¯b4ì…Ðù¸Æ«4v"%m‘lHÒ-üÚ<5ÓcæqÀŠVHݨš’úO%-Š—cÀ-éç¤Ããøêæ30¨HñÈKrÅô¦G$p\ö‰ªA jLùsnÉŽ”°@NËF­d0VQ ÜæÕ˜êÔczàQv)ZI¶h+ãȼ´îÂP –% íŽJµ”™’<ÒTˆµi!ÒæÆÑìÔŽ6N‘H ýÅ»~.£Šô’õ8˜t²â>(ç6é^,nåH'CÒ‰”¸ÇÒ­ÇÆ ˆ$q)h¡Üâjt1M4w<*­H‡RPŸtŠ#ËN‘Î8p°5ˆI§ ­±ŒƒE¡˜«± ÈÓD 2¢Ü\k“¢0(–°Hnã(z‰Lë5œ¤aÚ—jçÊY¡xšUعº¡?ìHâ‚4Ã1#)³æMˆQÉK•o•“ ИyRoƒ”ƒké5he}ºUTÞ¯M&{)ÆA?CB|h ÅËs‹áýÑhÔU„Ò f—D$†pj0ÆÔ0µ‰¯b¬VS1š¢'¢ò5‡Gˆ$µEl¦9hÆ4¸NATD`ZÀc¶fk²Ë²T ´¹V§è¤VŒ"ê+9UÔõälé#ÇTÔFÒPGi±ÍnK¨©YƘL-TmÑrmšA©eŠ. ´L5‡i,–zYRô˺UëA @ifTüwRQhcB%¢EtŽƒ•é|Eœf…bRqt«ƒ¤$1dðUæ© e¨¥¤*µ È4´nÝ$ƒ´FÌ‘3rÕ;ÌFw4‚£ÆqF¡((ÎÁ ÅÓôÅ­È”ŠH~mð0¡8‡Ñ Ò•”‰ÊX“ä}8 ­:®>fÁWË0¬ Öeà´d¹šp@ŒŠrÝ4lÄÕ‡‘Qé]§²eä`TŒ„ £âV²¬D"n™q‹[¢DÆŒÁÞ?z…Œ†!Œ™à xjƘ kÞqÇ»Z€±žàý@zWašœr!LåÒAÍ»M–qqš»p€âeÌrIJÌ×´1Ôq´üŒ–›f|,ÝÏ F¬`7ÛÅ9¤ú ?à*Í6г ÔclÝŸöèBÉI%W’àÃù+hÓX¼BT•§æ«Õ$âÎ,\·å\™%IQ@[ äX¨9§æ¾Ôõúì@”}J¢N }¶FºfÙbKÄÁ^1Osu@-çZ^ŒkÁL^ŠI%@N¬2™r6NÁ›4+tÄ´@ Œc¶V ÌC8GÀI°PõŒãD_Y”žÄ’-g”•$Aee8‡6D‹I/äô™ ¡œ+¶¥níǃ÷çØûóVDš´ø.Å—) ýâ#Š<Ñ9•)”Mf´`¥“32ÐÕ(n“q”v TÒ–qœœp9¥Úxžž_Á»„&©K‰TPj Û„4¤Ù—ç¶&óC’ÿsua²þÅ9KFâë²lÌcÚ´ŠAÙ˜ÖâU”–brpÊ$&]ôYI®…½ÇÈqfa‚”‰3ôzR;š»–#‡’>[Ò%Ó5FEœ®£Â¥LwéŒ }Yï'†zîŨx¿ŒJ D€[‰„ZèŸX”™™îs»\f"Ñê©k moޏÜ>fz‹ö¨2Z‰0=&Oó¹â]ÜBw kȤêÅ?>y&¦v.œ=¾dÒ¬®¼æ¤q Sà‰wv4†é8øÁÙå™in¸ëåú¯ÎË«( À¯_©¿á”ÒT_¿Ò7wÄ~û×]Ôxñ±ETÔÁP| 2ØSV“+œO—>¿<06Ç—™æqŠ$>k ¯øø`gÄ\Þ!^%=‡TŸkÉ윙¥é)îH4±£1²âãÖ`Ä´ñ*Eƒ€3àíaÞI•n‡1ˆDyC‚·†M+ã²ÊR×®ƒVýAKãUzÒ×còša”dÊ¡¨¸"‡v€"OÖçèJ9SóNN.ƒ‰h²LôPžð,CW']—l ¨MªqÈ” (Њ y<úšD­0Æb«ñ:ì΀Y4GÅå7ê2†ú"_¤·×ˆ#^¨±+É¡Pµè›¥\Yy] 4‹ù$$X˜©0ŽZïEl¾—zù¶bZ ÅLzfªk¿@£_IªWÒ¨ÞÍíË$¶r•Þl­@þ„ùÞJL’ȨmuJÅ_"R )À~%š-/–ÁET%&rL[õÅ¥(_¨¬ ƒ–Ô°ñbDZ8ÔäLF¦îÈ2lÆ›D§”Ju:Řê<]}¨R©~û1”™dÁʘ¹&³bZ\%ƒµAÊ•ÉÑB8 ;"™˜&»œ=p%/ ÁÕæµh'È÷” ý2*°q?¢®´·5I-MôͨúYõÓUÿ9*QÑÐ@·¢Q¿H$ÌLOlÉü"æò„{ͦÅãc^·wVyÁ̲Ø;›ƒ¦ÇårîBF #ÆÄÍS†èŸ„ngƒÀs¿\¶¯~'ìd¾úQÇš•+¸xC}H|˜;! /+HŒjwKw[×p™Í¿ó4G΃#§dÎ) äex=.f0–™æ®š”ù­SK)nûнŒy=ÆuKKŽ™žæq,⮜˜qÃ)¥™iJKaÚTL`” ׈![˜å:f¢kV‘kQ¹»9È7íKŒÏ1²R .œò`ŒÒää¸qÚÄÕZ †?ô_äæi6Ò³†oÇ0)+àÒÑYKŒ{šËéo(¯%» åL2ZÍ‹“³&ýµ-s¢DÒÛD Ð>Ú§mR%– e4ç |§ÊI2!3¢2\é˜jAH<ú3<D‹ËÒD×K÷ÎP8=¹Zä’û€ÅIF¹‡ˆi¾˜˜7¥E™ë’Å3Æ…çLZ†œepY1Àœ‚:ÄíúôóÓÇ•X‹•“üXĘcQœ(:þÁyÙ2ÉÏqà©Q ˜ÏÃå%Z‚d.Øõ ƒ#ÇX&ʤ<}ÊшÑôÐft“]ÃdPŒ>²yÆ Ù»¢.:w"[ÔLø1'¥€> ’S¶–D^eoc{í\@Ù#ÒB6‹³âbœ&Tê6@– IÇ1;,¶ÚE"Ù9}''&µŠŠáT PI~å0%Ó8èŒ z1*΀ȫÈÐ)¯£çÁ4FÅ{3*†ˆQq6TF…#ÐÁ ‚QYV"ËÛsJUi4ájéŒ茵…â]f[(~ 3ÖÒ‹šîSªÆeºc–Õï¶¢Q)d´0ra(3åZ¼xqúع¡hbðüíݶöý—?oJ×ϯÚY˜Ùñɾ’† ¯(5˜™SÐçõCñc§gËI÷¼¿µ’b‹gåŒËõÀÊÍíûöìŠok¼+ÔÔÝÓÒóñ®Ð†úÐæ=]sÊBâ'ÿѸ¡>´¡>´yoWgØ@ÂÙãc²|ðÁ¶ŽîX ;äC€ôWdŠ €W”öìù[uËëë[¶5„'¦ù½†Ïc˜ kçþn9ýÁè'Ì|éܼ™¥é°z[ûV5uFÌ)Åi>‘•æÙ´+$#70PS99(µ7Ê 3€T›;ÎU½;±¿#ÑÜ 3ЦNK°Ò\£¡]yMl×f¸‚«ámŸ¸tŽÒ¥Iad¤ôqä¤yº4š/K7Ká–Ã¥®;sP-Óx.z ÝÜ8ÔS«$¢âœè‹tÀhIF kâNb'ê µQå,3•)2]Ll’̤yºBÈØGG)0p .@ÝjÊWtM;O]ˆ=$ƒ‘Ô `qQKH‚/>m€a‘açÉÔ E}m¢n—]ûB„‰iTIûHöû)ízýKráÉú³ÉFúb”C¢þÆØ­u#jD19¤õš¹s±qŠÐr¨ª\šˆúÌeDS&nhh¢~d÷c&ÉÆ"¡§<™6–¥Ц" Äa¥Æ¤R`O`&V¸;І-àr5ö£šbЗ$ Gi„nqÁZ+ëdžؽ‰t€ƒ‘Ó';W {íªK©×´Ï  £ ½²D&J¦ÓW‚J¹F-! 0ÈMT2“¯©¹ŽNR±MDc™œ¥ åèædgTŠî!£BŸAÄõPàÜJô„O©,îIáxwÌb ƒ‰Æ žà–Å].câ˜Ô-»nëUì¨2ZÐ…ù¬¶îÏø}AQi #Ks i×_Ÿ(wLI 3{aÜ ø]]‡œUÛ»mí³w,€æ]»—¹{Ù‘®;Å÷õ»¦7ìëîo0·¶ìëªðyŒéãÒ?Ùƒ±Ùã%[Ú´;‹gæÐ’ÜÎýq¯×m`³–+Æþë«%¹oªÏà‚ÝæÎ¦.®ÙæsX‘;½$lk¿ºî@¸§ošYœã[<+gBAJªÏÕ³j›"o|°5gó3¼'ÏË++HIõ¹ Þ‰ïo¾º®%Ô5ªÌ-`/~°¿;&ÿa]=Ý«¶´± r^Ê¡H©ÆæOÌÚxcÃAÓ´Vok?fzvVš{FIZŠ×Õ3iÜPJ‰ãÒ‘ðÒ¶!Ê¡(“µ„¬®(?qº>ÚiN-tmlHL*pg¤@°#=Àµ1DÑ=ˆ ðbps\Yà žÛž'qäJmnLÍ9€6o•bƒô –`mJˆ¤R(Ýä]DZ‚G¾¢…%Ô a“G@9•aRù mߘ\Xá²ÝͤCç”kBvEîý—H81ô”\e°ìM:h®Ü_ÁnQM¢¤òÑÙ[<Ñ“2º˜ºQ†¼ßàœ‹b©I6Ç}@ĤlŒ+ª&#™´'’’L‚ƒ"‘Ôs:½H¦P‡Èž÷¦\¼WኆÛıAV‡QX¥i1Òéld>PU`qe…ŒÔá²_ñ>l¾-ÛkcQ*;='ôjÙÃÃĨ:§—ñÐÊ0KghTj™Oj W´“¹”æoäPÜI ™Dކ&€ÓM@¾D*“ä…‘üÉ×Sf4ÏQýÊ”†…R,À :!æ»D#íÆ"-‰¹JP©”sì9d°Øx• â˜é§>Ñ8%>¢ÃÕ®GÐTº£ìgÉg7Z€¡.UÊå*Ae7oê\À{¥|¨fé×ìÎz ˜f|z¡ÛãõC±x‚»]}ÜOðH4‘ðMãÙÖ÷x|ƒ)dû¦þúì}_¿ô;“g-L!£]˜Uo¼ðÑ?þ´uýª~úHAñø õÿâú®Îƒ)~ߨknŒ0CÞ]_2m¡ø°· ê÷@dú8vï%+jR¸kC]P|i'˜T”šæsÀö†p$:´ä^Iž|~ŠÇÅ:æ™àÙižªI™×µÄãNîÝK/žWžá÷>1§,ð¯Œs}XÀÔ±i7œRZ1>æw‡£‰TŸkî„À§•fùÀ`ìê“ÆÍ*MO÷»ººÍžx"?Ã;{| bßõÅ7ïŽYè/9ø=òʶ®8Gw8ø°¬Twºßí]q3!ýzsgTÔ^œë“^C¬ûɸÚÏ!†`ª‡üL 9ô‘Õ%Öì49cn$LÞÚeå¦á”„Ré¸ð©©mø¡d¼Ji0庵Õ@¿±…G%ŸpQ¨2šNq¢VqÀV€EËR‡Z΀b¡òêÀeB^Ü—Á0Ž2 ̉¤îydÓˆî茼?ÍFõÀÀµ$Ô¤FŽ@)®Z“ÀE©IéÓ¨'0Ä% ”zã8·0 bc™º¸ÈTb ¤"œsnak8€Eªàb=˜¦ô¸”Yöâ€ë„€‡’Zo?ÑçO_P'åª ZÓú›–WejSÈŒW[²§8X-G ô–jÔ*üމ%Ž–(´%åä„éô]³AűŽR ñ™bսص–LKT¤W¤LNݬRœBÊvÕ¤6ãÈ˜Ž¶Ì“Ýô3 å5Òþ¥=©”Ö¬-#ÐR$‹,状¡ÃaºÄ¸æ"#‘¤Ò %3œ&fޤŸ‘’‰&+6ªT€ÏÛJ÷.4¢\\1*¥• RŒŠã’W×qû’ŸƶäG.–Ë/¶‡U±¯¨;)ï>8pv—eEãVÜ´:6½ùâ­n?-ûÞ|ñ`[cÜ´¢qk|QF<Úm€ì4ϬÒ@Ò•íáø/×ÿêÏu»t@qŽ/i_ŒsÄ—Á‚óׯÔßñRÝ}Û7¹Ïc|­*rÒ=™©nøËÚwü©î/Õýü…Ïþ³©«÷K(¤‹`4ãÀ³ÓGÞüÄK°u'€;þþج¶n4-¾iWÜ.6«4Ýåb3JÒ ;fmÙ×E—õ) ô:^ ;'à9µ2ÿÂcŠ.>¶¨4ß/.ÈËð&ݵâãƒm]ñö°ùö'mâȤÂÔ¤ºŠr|©n` Î9rÌÕ';}A°¼òÂÆX¨Ç{ÐÏÊ9çÈ1ÇÍÌ›ëÛ¼§«³Û죱Œq–j¼ ÓwÝÉ%i>—™àϬll$„ëæ–Ø'ÌÀ>‹‰eûê¶tyrhY\,XqΙ…—Æ-¾ó 5.Ë5¹ÀU`&‡•ÛãsK]‹&¸™èêŽóúƒ:¢‘Š®ŠÉ€F8_—Ñ×bôC°,’úg ¤¯àè5@ŠÇP"ÔqŒmÊÔ)éB)ÊɰÈ,à4íç —¦¤¨w“x©!6 Š@áò H\hʆìÊRªàÀ|c€z&—º!‚$Ù35MdÒ,Š52üÙm[:rÎ[H°„È‚T+‘¾JÁ”åÌBÏ Øj‘Õ’Æ&ôŒ»x©– $`‘ФÉid‹sÜÐÎ8³(Äs.LWŸ¦SÏ‚j¯Ê`0Yñ/ޱÜöcñ~ú¼…ë%ÊJ L<ÐædY¾^h^¼ÑR6LZ>ç ‘' ó\dNphØy†Ðµ`H²xaœ*Å-É,7$Å&îÌ1É€L™“Ý24<²7®Z§ÌOö¿%Z§&8˜$N¤špn!—â8Õ!‰!*¥ŠS$ˆ4lå“{yܾÈq…”+AÑ£’Ç`Ò5i.‚äúQã]G®NXp5Œü ׆†rÎÌni\Yæh±÷•Î1d1ùH‹¼—KncѨ¹8†O/3&»R¨(ÙD…<Œ¨Å¸…ôÐ"Óe2²pŒÔYœ5PêIýþÇâ–iÆãq–°¸ÛÅ->cì¸R73_{æW u›^{æWÃ;®lÑ⯹],añxœ›fÜâÖ…tµï§/½ñçT`WGó…ŒÖO’0n+(wæåßÉÎÎÊÌdgeuÙwó ÇŠS #0„=U¡Ýkk«×Â|¸ý xiDM°,Xzüý1(/íÌòg |ûúºà‘S³`hÜò{ ødOÈLðoL¸â„±ÓƦõ>å鵸ÛûÙƒ¸Ý*ÕŸü¦†t<HqRl 1ó{H4ñ꺧Uæg¦ºLʧBÝæòwšÚ£ýJ 0!?õò%Å)^WOÜzê݆úænàr.D©‘ë G¥x~¯Ápœ @¨'îT$„UÐbLM¹°Žlm6'绳S\E™Æ¦óÝqËb‚Yg¤²±YF"!ÿŒí^)(–âLLõ±`OÕPÞ ÷kˆi©ÈMƒ–+šÿÊc3<®åµ96§ÕR èq—‡€¥š/N¾Eþ #”èÜ]£jC=3=i¡v:Ș ZSa6aPOØZuHHuL~•sVÜ‹2”mqÌp´Ê¯Èé*½¨‡ãjŠÍ0gÅ€Éç¥^JY/¡%ÁpÖ.ÓjnÀ0Ý'mE³~•HÕ KkˆmÎ0ZÐÊ$}*EÙD1€þ§<rgÔ‘®r¬Isfb÷Ÿª” ´Y¯¦Y+mÅB““¾‚¤I¶ßh÷@,$¡—‰/õxEo/ª%+€ºŸq›áQ7‚nÆwÏ ƒÐ“†ƒE G9N0Å+k™<&¯'z @TÖJ—Ÿ«2V´úsB*™scÔn†þQ]£ùaÍ(å¨ZC] ™´0™\ÂI޾¾¦›6Xß…²#u™VWê´‰/{…£î”NežX—èh¦ü '•aÊT—¶“sˆV»§¥+ê÷ºÜü¢ó®ýÙ›ÏÝÓî\ýêc^ÏWzò…ÿ/#»zbVkGpí¦¿Bê6­ÖoÌÅwmþ ê„sû+d´$ tlªYñ\f ] Øuo?›“ÿÿ2sÆ Ð"ƒ%U¡ÝkÌ]vM*ÔìÊR¸õ4øá_öí‡9§CÙŒGž3vàö´ö Åsž‰…©nÜØD{­±¹~Á¨öìyfUcGØœ1.ýòŠû¼8+ÍÝŒ€ÈE@¤×Fõ.<²±>ôÜûM}–³fGgÍÎ`Iž??Ã;.׿`rf ŽdvÎßëûz˜]š~á1En댘O¼³oG @þsÀ€%,ËâÐÞevõ$Òý®ì4ÛÃÌ8ñ|¢ÅyS[TÄU\ÇЇ4–Éâ «=Ì>êŠÉ0J²§z„wIXÀ€h±„ts6j’£{ÅDÞ4Óî’5È™•$}°+ô*äÅÑn©+ IDAT1ã¡0g ŸdDEË,’¢ÊŠ.Î,U›pȸ<ª1Ní`´àIñŒb!þ9y-›ôű0\sEM0–ôC³(ÀC¿L ’ÈHâCË{òš$Ê%Ed:Âz”öqž¯Y€"‡Ê-Ù"ïã˜B_w؈Ó ÕV©ÕS¢àoK¶…aõÉ´IkŸè?davòýð'®1r—^ŠÈ‡dQ¸sšÑËWˆc‘ìê •Æ8P—4j´V3e§šã#(œc­I"ÁR4µ‹œí¾WÐÈÉ y’—â'RÅ¥´~“KÙµÁŠ# KdT 9't´¨‹)%û6ytnøV8ݤQaýÒ)5Ò{Ñ)j8Ñ)òֲõíSL{eŒîÞ†¨yÍ’xŠB+ bKÓN ©ÃcÌ8ØÖévyü^Ã`,7¿ðŒËnyû…{fÌíñ}åüo§gæ€Åyw4ÑÚÞɘѫJ[!sŽ81==u⬣Ò2s 7¿ðŒË¿W·ùɳ6 è¿Ñ‚M˜Hg뇯=æó@VqIÕ’ó×½ó=‘àš¿?vüYÿ•š‘;aKªNŒý®?îxþt|)4tÀïW}ÖGŸõ­Á²¾.xÒœ\ƒÁ„1)ÐŽ×7wR½´¡-*Q´ù½7Nš“×ÖÕÄ9œX‘+Ž|¶?yïWc[4Ømf¤¸g•¦O.Jý¬)é~ל²@Nºç¯Õ-.ƒ95kóžP]sw]s÷æ½]ó&dxÜ,ÅÛïëIMÎsÑÑêaÀÁ`C}ð¤9¹ôuc}h~sOKww,‘âu-˜”™™êNó¹Ædyû»8+Íý½3'Ðׯ¶èÆ^ûâ-Îÿôaóe‹‹Ý.võ‰ãÄ;Òü.ødO0§WåŸ^•ß·¢q+ÝïnkC?¨( ˆá”âu]z¼Ê¢Õ5w?òÖl‡Ü?6·O›>&Ë{Ì´ìc¦e‹S¡nóµêô ¸X¯Q+q³ÏQñòT;µ2ì@ˆË—ƒsîv±1Fi®±¹!¡8S åbø‹âŽÊ,‰«´5A ¯ääÈênR'ò³ž¸†¾’–e¾HÆŽ~Ù•àB}箤ïŒ š·×èŠð‚˜—âxq7A‘`ážVÒÎÕò$p òlËdsm @Ƥ$Ž…:PšL¢Yz6«¦EbpŒÓŒò Š-’ᄜ³<†+]jÿ3¥/UWz™ƒnþ}{ùÁx†Þ÷i‚jÅÁÓ⩊Q6Ë¥%-Ë–J~%ðaz¦Ò„@qP¦i9ƒ¤PÄ¢¸²ðþX)Ä#Þ ¥24^ˆÈ ãf\] ^ÕD†ˆìƒvX“¨ZªÆ‚´9K£·úÎ~¸’üÍ´V¢{ ÐWjJ;K-ZXÃWtJ¥¯ä9w®Då$/hyxÀ©"ö}±¯/:e#„It 8zeÒ&³‰Ä§A.í‘kÚ:%g¸<¾Ô {}l¯'Ëãaâ)«‚qåãÊé²Py0lvCµûÚ3 J’þºK…$aàBF IÂŒ™\å÷ûÆNš›’ž ™ÙyGzeãÎY%s)Ì¡I•{óï„ #Y©¿<NÿóÂy&ý&ˆúÁÁP|wKÏxÜW>Œµ?è‰[Ëßi8cAÁØ_ižë¾ðêí]ØçÅXÕxÔÔ¬é%郭ûÂYw Ï?ȳ­!üû×÷,ž™3aLJºß•H@sglwKwÍÎ Xÿp{GIž?7àÉHqÇÖþöèºÁñQ¾¡Aøä9VOÌzèͽ'ÎÉY’F¦æ­[;Â&€z9E rÉÀ ‡ìTÃç†ön‹AF1“{\,ÍÃrÒYY®+åÕõ‰` ¹‘Œ%\x g\@—ä=ZâJÖ¤˜:VJ§Qâ S+ø×%0Ž#ÏЖ™]1¢6v%"˜t?èœÔö ù J™3 ²YŒžY¦%Q`â½ïƒ"XÒWSõøW[Sý£q,9kÖc@Çfè–ÑShÝBîVDUÚšŽkšxƒ-§ÊGcÏ&ó-РR.щ2†YœŒD™G¯Í(\¨RÅý:ÉÖ¹ôÆ`öOð¾n´Q7½FEST˵¤-'i„Išr&à½iS’øš2"O ø‘4hLqêÍà@ûö$ Ó2* œ:‹RG%ýÒRUDžP‚«ˆÔ =7F XJH"R4 µ¼ ZWR©ÝçÂL¹¤«Ú¦F¤>ŒÜG\Y8C‹Á‘δq´Ì'ë¡»1‡¬“«nj*¨¸”(ÛžšJ26tB*a89“t O¨ÉgòÞ)ü¨Ñ)-;¥ y°tŠ£I+:ÅH'£B§_Š;%sw}ɧÅþ1™“î¦ Íñoë2›;£ÍmÝ»?ó¦ez|}¼niT 9,-ÊJ3éXº›Þ¿•ÈΞtlsg´¹-<aØ­·ÞZ¸àІŽXÔìÃE…v¯}0oÙº]ð|5üæ\øsã·½7Æ!;å opÝÐË$[H1À>}QC‹áç Ÿ›e§°€Ÿ¥ú ÍË|n0 æbÀÂ1ˆÆyýA«­ËœYb¾‰øÖ¢+ù9Žõ÷’J~Ãû•ˆ:‰‚Épä"Ä>´Ì¿ÜÀ¤˜GÒÜMû|ŒRW6}"ÏA-ëÒÙû@Ò Z›ST’šJûeEzQÔ>›‚PoŒáÊ!ЭX•¬ ÐáK9¯"ÕQeýêQ ¯B’蘟f½€AUi«C‚ôKã.h•cÓÙ ½øQDr ð»FšˆWÙØ¥ôz«5UØÁ¨uúàµ÷HN`+B;‚¼Y¼*œ¾“õj#~9*’ˆG+ ªCéI-_ÅTaHÖÉ)‘)£¢hÜ€aéŠ@&;8Ý¿ábŒªqÀ•Ð*ëÉm%Ñb¢·ÔYèR4.Eó&™e¦ •¦(ê8U™ÞyzŸU~R]¤­ÿÑÆ)@¯ uV¦JÂvÉp ñþ¤RNÉWl=0ʈöD‚mM)¬{ìø©.oj~†'Ío¸ ô¬pÕŒ[±ðžúϢ̟‘Säó§¾B¾8-ò¹±Yžýëž(SÕüÙÚÇÇ,€eðôGp]Ë3Y“5;å o(FGÈó©¡Åm®^±ú Fáž8ì[ûCê:½6ŽWÚâ'ºQ‡ô.º7£€‹RqE•¸ÂF Ç;9Ñr襳aÄ4´Ü0®|†t^à”’\°|TN|â1¢8Ú"‚ÞŸk\‰ÚƒdGH¨=Ó„ 4"|jŸG'¹&-“=¨æàêf\í`@å4Gf*-Éäy”îlË”´2´'Ùv¯Q ¦l•V/GeÛ‰¢Æk™¢z’:SVÇé=fbÿi)P\Dë…¾ÓPvô“βgÁ°Q$I«'ˆ0uÚj# Ž9M!ꪈëGÕÆÄpÅä-¥U´Ñ“LgìňÎÓºF¡@²[7#£¢4"îÂ^Ò–Z>Äâ„x4¬ii‰ñ°Û *R›8øÛÒŒVñÅÄG†Øº'G¹<ÊKˆ±È„ä?ä¦d2Påæl Éî?mË|d*¨AÌûJ¿/tÅðz-5E)cY2ÍN•àÊÚQ~©LyjSäÿ8#[&SÃÞe\YWïÅ>ì¦Ã¯ÏÈ*èê8°ùãõËÇB,ç€'˜ xb]áÎÖº=MþŒ¼@V×ç?¬…|¡Z$0©â+–¹/Îá'»oL½ê[‡—(~9!#6]£ì %‰•_%£] ¶Ø)¼!Î o—b¶h†žC+YÆJEæ”\IìŠÆ© bý±+Š=¸ x(vE«bÔtA(¥Ÿ> çð”aÈC@Déð™Å8-c k–DĤ¢ÁÒý¹XyQ[¤”\×"Ǥ=œÍ¹FOžl7Q!q+>¦G4‹2g (=¦Mê·0Gj¤î¬@¼…ŽTÏb3ˆÓõC¹ÐĸºM›%c?“Þ0\iJTÐùÄÀðÑñ²µÕ›|%Ñ;{R ÙŽ*KÑÔïã¼ZòåT R§uÌ5x8uw?ü 4Т.“†A½Ât Å”Ùpi6(¥–¨Œ25ô˜£d_H"Ƚèá\L¯LFtAkÄ•Ê.T„DŠfHgh‘޼B•Vó\YF\ŠnèKqäR4/êKqrtÒÛ1$X4u#׊Ó\àM²rºt’NIç‰ 4®7A­_ª’eÒËF§F±¯O0føSÒ Ãíõ¥íÝßÞ³³Ñ4£V"a¸\n·ÏŸÈÈïOËôúü,äJ!_¨ DªâKž¹àñeé_{&o¦³Þw8¡9 À¡ië}ðÉZÛRÆfY\9 =^ȹÍ—qnŠKŠE')Òž}Ê?xv%ÄB~ \‚Ø£)êÀÔ¼¬ŸÈLÑ´)¶E‘†ö7Iâ!f¾œÆ¦ªÒÃY5‰ªúKK,Ñ>ǽ":Çt€4…唾Jê“^Ù,ÜèƒÍ!‡,2µœ–-k&/ÕP2ß¹/G³8Ù_¶†a‚€ ¢Žeöõ/6æ•|, ýù§^ë }Þ;RÕß56ÉûúJ)H˜€™¾T§7’öà ĜôFȸªçŸc,4•lCÃ@Ó‘¥}Ro3åôƒLššG¡§L‘¤ 0)ƒY6{’©Œ,·©^)‘"g$gK4û’I)|1ê,@fÆž4”U(¶"u‡cY’ MTUÚà¸Ãá…^‘ÒL¸©O€‘ìÈÔõ3+Q»pIZæÔh–¥$;|ýFε'qP-ŸR`ÌëOq{½þôŒx4š0£Ü²˜a¸Ü>ÏçöøŒÁì+•BF £$Œ$UÄ‹uŒ›¶¦m‡¾N9}0d=è&qB3G›[–|ˆÞÆŽ™gE , žÊý«lˆÎ®$²€Æ.:&tà–Æ®¸-˜ôfW²AI+ƒônbœyQ6‹àÂÔÓÚ 2w¤/¯;ÊiOŒrî ,9½eô¢$‹‰øÄ-¹J*\1•žRo°Â°q,Ò¶­é!àˆ!' ˜Åi¡MÞÂäßál1.1Ê.ÁæÈfÁ´Çú¥µX@@RþNã[H—©;8 ªSsÔ¸ Ð@£ÑZ(%6¢w0EûÝÙ믠WN÷Æ¡½NW0û ÞmÒI*]‡÷ /*ކRy-ÐN½”b“1IÌ 0wÉò$«fÒˆEòE\žì\D5q…b\³OÁ<Ôòš¨f®XºÎ¢À΢ Uù0*Óì@é@’%ñÞ‹Ø¡dR¯Ô’Ùb’ÍXâê$F;Ü1C$’ò¨ÊàûYãÓ(.%Ã!“}šÄ¥°ãKõ³Ì§«UœèBÍ®,À-zj ȶ‰žËǃN§Há‡?—Ó'Ä£soŠLç mè{+>¯BF ÆÎáꃇŒ 4Ÿâä÷mõk9ŒdÙp6gõE/š ê›9ÈŸb¨SÕ2û•ÊÑÓnbP7£àjJ(îHžpËy˜ý^q%5„÷}¯zy¦ƒ¹0¾Šl nѤSºUƸ¥µÓ9,%`¾™ð˜·±µÑ0×< °@:Sƒæ®´{ô&èÄ ûÅàöεåkpO‚ø-'Ó D Õè88€dEZÄ£ú„[S;@€©·…-‚XÄ<Àà ˜eAI,¦8ÒFl£"MÙf`×/ÅÍu¨ä…‹qîKé-¬›¤¶J÷a-8»•K»¨W9%×:)\0[Æ mhèÛBÀ>u7°¤‘HK:*OJ®_Th({–¢(.HÙ4šãÕi&«H2°$ж>Â^“mÑÏð^½¦gRÉ£ Ôv–TŽQ´?Ò;ÃIfeµ*•,´'Ôe0´WåŽ,` Ä´˜¶‘×l+©ÀäHçÀŒþm9 ¤·äf¡uÆ+bºöR°zµQ(,ù†¥Ò(‰'JÝáƒ*Øù†¤äÍ­ZeÅ@³ ì`HJ ÊH é{½¼‚¡ÿSNHdZ¿“¥1 ;–ª·™ rÈ’°b—Ðbç` ­ÑS¯Ibk1¥|%´¬?锊6çÈ °dý:Ðn¤SöáÃt oÄ#¶BìáÀíö@ŸÏ‚׉µ‘"ÿäV_C8©Hr_ªi šßo}ƒFÚ»†dR•džÃƒÛHþ;ͺèvDÛ}ûžzXd0€24EÓ0²‡Ç^beÒ²¸ÉûdÉmQ_…K²dZ-#yµØÀ•¦Å72w)ôn5µ Ÿt€¶S) rã\?ª=ÄäÎtÕé:·… ôS*Âè1â¿n»”iÒâ›t¯t×¼+^¤H”ÝU*o ÷Š dšbð:Í££Î™ ôʱ(‘U˰šŒzÄç²NÚ«b' tZW°fÉFµ[´ÆjÁIS‚6Ø+%‹B¹AŽNš·ØzMéY©BsÌdú Y ôz꾬5?$Ÿ¤ÆÚ‹"Íâ…zSð8×ÅãºîÁv©‡÷?Ÿƒ^EéC”N¡Z@”½E\÷$\¯×¦`5@Òäråj´FÚ$äŒdÔíÜÖX-€âq;…‘b­ì‰JÛÌÏ6¸5pHJSäÊ”‘ ³f·;ÀÚP׆¼ÒÌè£ñOë³t…r› ç4Ì^Jr[43s-5‹Æ}\X™:Lõ<êYù»B›0¡®5gΨMÒin-)ú¨Òòܦä裆Œ6¨´[lO*Çô¿R;¤ú==ñ‘âw»Á6Ø“‡²2oû´_»E¢ÏW, Œz12ô&U?:#o„u8pàÀ|ð‹W[A#jɤ*Iþ+.8pàÀ‰äu:8pàÀÀCª8pàÀF©ràÀ88¤Ê8pà`@Õ9ôz.Ñ8pàÀAÿàÚ¿N¦Ê8pà`40П©qàÀ8pÐly*•©âƒ%VÏ=÷|EÅœ¤ŸÕï¯~ôÑÇ-:B\£Eˆª/>¡»»›664VTÌyáņWæ5×\û­n%“±vݺoÝpãqÇ_YYuê©§Ýy×]mmm‡©®QÄ믿^Q1§¦¦†Ž<Ø&4¯_öÜóÿWQ1ç³Ïjad=~X»`ˆD"½-œ~Î>ûìá;êƒB ‡úúzý`sssEÅœåË—ÿKDràÀƒ/ìôi˜ûïßÿ~~A>}6}zÓþ¦‰ËG.Þ!ÑÖÖöÇ?>{õÕßøê ž~ú™»ï¾»böìoÝxCNVÎŽÏv<ûìso½ùÖòå—••ý«¥•••PS³^|€ššj¿ßßÖÖV__?aÂqp}MMVVÖ¤I ''ûóéýÏ>Ÿï7¿¹›¾ÞzëÿŒ_zå•WНéé‘\8pàà‹Ža’ª#:’‚«À¹çž{î¹çކH‡ªúÈ#žzê© .¼ žþ9T7<|¼qã=÷ܳtéÒ»îºÓ0 8ñ¤O?ãô‹/ºä替÷ Ï½þ<âÇ<ï¨UPPP2®¤¦¦àq¤¦¦fÑÂEuuu555:©š7o®øKXŸ[ïp¹\'t}½ãŽ;òóóõ#8pàÀAŸµÐ>ÀRÂg;vÜxãMGutUÕ‚e—]¾~ýú‘Ttýu×G"‘gž~º¿ >øàÃeË.«ªZpÔQGÝø­ÿÞ¹³N?ûÖ[o}ýëgVV.8óÌ3W¬Xq˜Dýß'Ÿt»Ý?ùÉuòT2®äÚk¯Ù±cûêV‹#?üá.¼ðBýÆ«®úÆM7Ý4y~s÷oN8á„>úè’‹/©ªZp÷¯ï~÷Ý•s¶lÙªxÍ5×&U1̯šÿñÇ›LÓ_«kÖϯœ7þ¼š)Àž={ZZ[«ªªÄW½÷…`ëׯ¿ô’K++,Y²äñÇm«QwAÝ·eËÖŠŠ9Öo_Ÿ{ö¹ŠŠ9¿ûÝïHžŠŠ9ïýó=Ø»oïM7ÝtôÑÇÌ›7ñâ¾yà ápx¨è[×Àg ‡UÂ$ Ò¤E¯ýóýž{îù•• –.=ùé§Ÿ9L"9pàÀÁ*†IªÂáH1ðŸ ܱcû¥Ë. …‚·ÿâ<ðûÂ1c®¹æÚ¤À?$->û¬³þð‡?tvvö>ûá‡kn¸á†¬Ì¬ûï¿ÿ¶Ûnolj¸ì²ËÅÙ¾ÿÏ[nùÞ”©S~ä¡o~ó›÷Þ{_ÝΣ.*ç|íÚµUU•YYYI§¾²ä+°vÍÚÁ”sHy‚Á®»îºë¿¿}Óëo¼~Ùe—wܱ………/¾ø"]°wïÞµkמÞùCmBUeU$Ùºm„B¡µµóçÏŸ?>…äšê¨¬¬êóö`°ëî»ï¾åûßûÇ?Þ¾þ¿®¿ÿþû‰< Ütß´iSÀGk¥ê>Zû‘Ïç[»v}u»Ý•ó+à¦ÿ¾©±qÿí·ßþÔÓOÝzë­%%%ñx|¨è[×ÀguŒ¢„úH …B]]6r6$“C÷þö¾;î¸íÃWûÛ7Ýwß}Ï<ãð*8†¹üwñÅÓçŠÙ³ÿðÇ?ôwå=÷ü6//ïÑGñz½°pá‹/¾øÑG¹÷Þ{‡W5\{ݵ/¿òÊOüïw¾óí¤Süþ÷eeã{ï=n·**fŸzêiÿûÄÿþø'?€‡|xêÔiwÝy§X´?¾ô¼ó.˜>Ú¢v…Ã]]áqcKzŸ*,*dŒ5íß?˜r)O<ûéO~2wÞ<ºåÜsÏ}â‰'n¾ù»iiiðÒ‹/¥¥¥žrê)C’ªä¶ªšÙ³f­__ãñxf̘‘••ÕÔÔÔØÐX<¶¸¦¦&==mêÔ)}ÞÇþçÖ['O™çŸwþË~ù­·Vˆ´»`€î3 £²rþºµk¯¿þ:˲ª«k.¸àügŸ}.‰¤¦¦®]»nÆôéiii===µµ;o»í¶NX,Ê\¼øø¡6¿? l]Ÿ%Œ®„úHì!™t<ÿá0eÊT8õÔS?ýtË£>váEyÜÃô8pðeÃ03U·ÿâöåO,?I1CG<_·vÝÉ'/>cÇ¿xý†Ã«W ??ÿ‚ .xî¹ç´=L‹Å6úéI'äÆ0ŸŸ¿`á‚êšjˆF£ŸnÙrÒI'ŠpS§N?~üaµOXVâ× F¿ß?gî\ý®sÎ9'‹ýýµ¿‹^ùË«_;ík)))C•p츱cÆŒ©©^5Õë+fÏöx<ãÇÏÉÉ©^_ÕëkæÎ›çr¹ú¼=F%K;®ùÀ8T Ü}°há¢oŠÅbÛ·o…BW^y•ÇãY_³Ö­]·`áB¡“‰Ëzø¡çž{þ³Ï>ã|Ô^2°x‡ž0ºê#qùËï¾û×tj¨&íñxçÏŸO_:òÈÎÎεŸD<8øRa˜sÐÙ³f%mTïÁ`(nšO>ùÔSO©uέDâЬb`\}õU/½ôÒc?vÙ¥Ëôê,ËÊÎÉÕ¯ÌÍÎÙ¶u+„B]–eååÚÎæç纨éiiiiiû÷õ>µ¿i?ç¼°¨è… Fž´´Tb'¹¹9KNüÊ‹/üéüóÎûí·ÛÛÛÎ;ÿ¼¡Ê/PYYùþûïsÎkjjŽ:ú(qpþüy555‹.llh<¯ÿé^¯GÿÊ f™& ¢ è>X¸pA<Û°qã¶mÛ¦L™š››3oþ¼uëÖµµµ-\¸P\öÀ<ôàC?üH{{[^^^zå•W&)jX¼C ¯c%L‰ÍÍͺÀC2é´ô4} ` 3:;ƒÃʾœ8¼‰ý@ Ýår]zÉ%g'óßv IDAT}Öè–œsÉ%?õÔ3'Ÿ´”fd Ãho;¨_y°½-33ScF¨«K? u¥¦¤Ž®¨Œ±… |ðÁ‡¢jÂ»ï¾ G±H|u»\IyŠX4 ô‘ÈsÁùç_uÕ76}òÉ‹/¾4gΜɓ'¯UU•¯½öÚ¦M›¶nÛö­å{¤æÏ›ÿüÿý_uu5àá0p Ü}0iòä¬ììµ}´mëöE‹À¢… ß|óÍÂÂBÇ3ožLÚßvûmPWWÿòŸÿ|ï½÷åçœ~ú׆*mïÂë8L&a¨&Ô Åb1Jkµh€Ì̌ѕÊþƒqxÿL×ë­ªªZ»nmIIÉ;F^øW\á÷yyô½ºÙ³f­X±‚æâ-­­ëÖ®[Pµ|>ßÌ3Ö|´†®oimݹ³öpˆzÅåWÄb±;îø¥¾¸Ó°¯á‘Ǜټ¹§§gþ¼J!OZjêÚuëáÚ_ccã5W_ó‹/¬«®^³fÍm?¿=qÄè¼7|`ñ)üç a†dB)))÷ÿîw/¾ôbuuõ¯î¼óÍ7ß¼öÚkœ]ê8p0’¸Óa÷˜S¦L}î¹g|è¡;ïüu0Ø™‘‘9kæÌ‹.ò›“úIJe—>ûì³täÈ#xðÁzè¡ï|ûÿy<žªÊª»îúuqq±8{ì1ÇÞu×<ðà½÷ÞW8¦ðò+/;|¢^yå•3fÌxêé§sÏ=âíS§N{ôч²³sèšiÓ¦ßvûm=ôÐËŸ()-¹îºëbÑØÈå9yéI¿¼ãŽ3¾~-å •••+V¬˜?_-ó¹\®9s*>üpÍü¡¯ý Üw,\°fΘ‘žž&䩬ª\¹rÕ$UŒŒ1cÆ<õäÓû›š˜aLž<é׿¾ëÈ#G‡² ,Þ!…ÿ$LÂL(55åÎ;ï¼ó—¿Ú¶}GNNöwoþî²eËú¼Òô vë­·.¸bO{4jrøîIiÿj‘þ3ñ?ÿóó—_~ùž{îY²ä„C_=2üåÕWú“Ÿþå/¯ŒÊ2«ƒ/~s÷oþö÷¿‰=8pà`øÍŠ0øÝFI¶wÿº'ïž*„ÿøGG±è?øÁ§Ÿn9|µÔÕÕ¯zoÕ¿àøã;ŒÊ8ø<1´å¿ŠŠ9ýÚ´éã 3̪?O1ƒE½è¢‹èó¨KûË_þr}MÍ슊ŸüäGÃ.dªÖñEPû(¢? |qšùo48pààËgùÏ8pà`8p–ÿ8pàÀF¸üw˜^¨àÀ8pðŸ ;wr2U8pàÀ£‡T9pàÀŒRåÀ8p0 P¯Tp¶T9pàÀ \ûœL•8pàÀÁ¨ ùåŸ55ëÿ%r8pàÀü»aªþ%™T{ì1Ÿ£(8pàÀÿ®x÷•ýk2©ŠD"Ÿ£08pàÀÿ!pöT9pàÀŒôLUßÏÿÕÕÕ@yù„ÏEÿÞp¬ÅÁàñ%´–/a“8øO‡íù¿AeªŠŠŠŠŠÇ6üGÁ±ƒÇ—ÐZ¾„Mvàà˃ä=U½Q^>Áq ÇZ _Bkù6Ùƒ/Mª ©q8‰kƒƒc-/¡µ| ›ìÀÁ—’TñÁ½Q]$®…SHBSkÌ0XÒÁXÜŒÇ≄Õ @UÅØŠûfÅ=»ne=¥Áffe•z&V¹3ò†Zçܲ,—Ë5Ô‰„aŒ%÷ãð0€µüË‘HXZ"==愲¬aÒÖíóø† Ÿ|eIÕ°‹í{÷íëwÓ×”´”’q£óø‚ŒÜZK]}ýòÇ—÷y꿸}rF|‘ˆ‰¤7ª*S%0pâÚ0ØÜY¶Y—ÅyOÔ4MË4­=ûögeª7ítxÕ!Þ™š½6%‹eϾºcWÓÁOW¹*Oc^ÿP‹q¹\{6Tï|ù…`ÛA·ÇË-6s®ÿf Ìx<+LùYç•Ϋä|tþ^Ñ|™c÷ž¶£Žš»zõ'#,gæÌ‰âƒ°óp8lÀìwþQ=º¼jçÎÑhlñâãéÈÊ•«jkk'Mš4Â’¿#wÖòÞ{ïµ´¨×ÃtuuÀÙgŸ¤‹#Ñhtg]ݺµëFKHÎy4M$,:âr>Ÿox“a½7nûÛß³ £ àˆ‹/Ê/+F!88L@RÕªJOSuRå2˜e0—a€ÏëY0úºõ[“w¾ëî;33ó¦™,ƒÇ½¡ºæá‡^±+W®|w媖–6Ëâ–•0M3#¶ø„Ågœþ5Ãò )ZZÂ-m‘„i¹]F0‰ÅÍ„™ð¸]¦iÆb ÓLL÷?ºè¨°·–…V¤¶VÔ¶MsÅ-¿wUpÎckº?oÕßfp\ÊìqV¼Ç²8€Å€0p‹'ܲ8·8çiní¾í;k–žwÄõ7µQ:Fb-Ÿ'\.cwdÓÁƒmá¶¶ÈäIùÃ*qÎÈ$ªª*FΫ,Ë:ÐÒíîé † óä“—öôôÐÙªªÊÕ«?Ø´isfFÀ—â/3f$u%áó¹#±–Ïv|ÖÒÚ’Ÿ';«»§æÌ©ÈÊÊ2Mzzzz¢Ñu0j¤*‰ÔíÜÉ ‡#†Á|>0cê”É~ÿæ<ÃnrëΟ¼úׂhlþ„ ưînËíÞ÷Úï×Õ…Š ¼ø¢¬¢¢¡¶È£{ªjP™ªá¥© Æ\.ƒs° 5ÕŸ–ê™1mBõ¦úáyç·6ý—–Þǯ>Ê8õÈü ¦ü÷7švrÈÜÇ»pÕíÞ½ëÞû²?þ¤Ù³gx<ÑÒÒöÉ'›V®|çÙgŸûþ÷n®¬¬R™­í=“§”.c\aö÷ï|13ÇägüàúÓÄYïŠm¾Þ°ôtZm{ö¿{ *òÇ÷¯»véà«`ŒíY_·êo©ç}=ܼ»uÓ6œ¹]†Ûm%âVÜÆ Ç’êòx9fÌ0<¥FvFö/îYxTéü¡5* £¸¨Q[[ #OÆô†`?]]j5­¡±Ý0ü¥¥Åñá•És¹\‰DbÙ!¯ª­­ÍÏ/ÈËÍ=°q㤉“Z[[“.(++«ÝY;vÜ´`0¸cÇŽ)S¦ ».£>rÀH¬eòäÉçœsŽø,–ÿº»»£ÑhggP â‡Á ×ïªà‚x4ž’šrÄGìÝ»×ãñäåå­^½:‘H Uì!59Ôܼñ/¯¦¶µUN›~QI)‹D ‚DŒîîÒX¬4H®Ý/¼ôîÞ=æÄ‰G]xAZvö!‹%-ilØÛÏ)ÛrjcÃ7_¬_Zö§ÿ(5î÷¹Ü.£¸(;-Õ?¼Yï÷ïâožštpùŠ–«ºú`[[ªeÐÙ! EÙÆV9Ö®[÷øãO^zéåçž{¶Ø›‰Äzz¢™™Y&LXºô”×_ý§ÿßmW\~ÉùçŸ7øb£Ñx¤'æv°ºæ³’œŒMs¹Yÿ¤7¯ ¦BÜk…[#cE³Ö¼]=$Ru¯¼8sñ±áæÝíë«Ýó¸¡'‹u§–fL(w§ÌH¨kÿîžöoz&¸ÝÜ0bí)ÙYñÙåu¯¼4R5*«~½¹Ô¨³«®®H,–…Ââk]}³iºŽ?~òƵc †¼ØJ0 #‘H,]z\0Ø …?ýtûH„4ÍDQQ!$Ì<ØÖûš„™HOOOMMÝ¿¿y$uõÆ(ŽÜþ0 kù`õ|ø„B]ûëëëà«§|555 |ð!°p ;‹ ¾äæÞzëí™3gôwa9<§µµµ¤¤öïßö½©n ©É,ÂØôÉÙ§œâ.*†X D^tŒö¯+,(d„ºÂ<óìI÷Þ3sÞ¼Á‹Ô'¯"FÕØð ñ¹xìr‡W9ppHö§ÿc†ÁÞùÇêwþa;¾cÇgŸ»xHE@]à@»üKÀå'å¤qÆwÖüàd¶s?Ä»Áåιa0Î!nB€•¼$xìÚµûÑGÿ÷替w Ç|üñÖ’’"Ç Ezzb¦i¶·wîÛ×´hÑÑ@ÖÝwÿlüøÒE‹ ²äXÜŒÅÂåçe§O›X4¡T®_XáFà¯1žQô¤t¶zw¤,ê Fâ½4‰PG{ÊìâÖMÛÝ€á÷Yñhzá¸Ig^˜:/¥ „3V"²whÛºú·_L˜1—dž‘ˆÆSòÁ†Z]o-o¼ñ&}>î¸c}>ŸË媭­-//omm ¶¶6###//oT¶ tuuG"Ý]]زu/c¾%KæƒÝ-Œ)(FŒ1—KX»wwwG‡±:Üb%«¼|Âöí}ó³òò ¦iZ–•H ÙNÆèŽÜþ0TßRX\4}úŒ>øÌX,ú•%Krsrýþ”¯,Y2BaŠŠŠ¾~Æ^¯×0 ·ÛmY–iš±XLhøàÁ¶Úµñ¸LdÆãqËÎÄÁ7yÿëo\]\¼áùç#99EååSòòz3*`,ÁyC[[OFFÊ1Ç@kkGW×PEJâU:£‡W9p0 -…3Ô\½a0ιÁØU—Ÿ ‰Db_ÃXÜ䜿ôÒKC ³ ¯°v7L›TÐñæÉ?¿û‘æ6ˆtÇ h#¶Ì¡¸¾D"qÿï~ÙeWüÑ~Xãñ¸×¯ß\^^æñxâq³½=ØØØ4yòØmÛjKKË/¹äÚþèÇo¼þš×;¨}O±˜‹›Â~mÉÜÒâÜ‚ÜçVûJÃW]9`¹]æØy-û ;;ÛMsÈë †ËeÅ£8ó¸­xOî”Ù³¾õK_^1˜±ÐîmáÖ¦@ñ„´âò´âòÀ´ÛžþE´«Ó0<ÌåÎGëÑ?8”µ¼ñÆ›³gÏ¢¯O=ýÌ…\‘˜4iRmmí´iÓÊÊÊÜn7TTTÄb±mÛ¶b¦*ê‡{‚Áð†;}¾´ªªiûöµ¬\YsܱÓGR,cܲ¬H¤Ç0 Ã0æÍ›­?!8Ô§ãñ˜eYPXXXXX¨ŸÒÿF§Ø£=yòäžžž={öŒÊ"à¨ÜCb¾¥|„¼Üœ­[·„B]†áJKKYòÉ¥èÃ𙕕•ùá‡k*+çûý~Á‰MÓŒD"ÝÝݱX<ì ¤§555¹\®¢¢¢ººº‘ÔxÈ& Ã7®dAg'tv†¶nû Jä啎?ÞçÆ,Îtv¶3–¿dÉØ³Îred€ç·'Nž< azç«tþD¼ÊcÔžþëŒ1îr±XÌì w[œ»\†5ÜGÌZÃï¬o|cW±vØH™?æÔÕ»¡»tRp|d§Ú…÷W¯ÎÈÈ9OoÞ¼=%Å_^^ÜÞܲegaaQyíµ×¦ðX,‹› ËjFÎþj%çœsÞÒ‚DwfèoÞìthsAÔ {w¦Íî'º#=úÓFƒ·¸eqævñn3³dÒÌoþ—WÚ±~×ߟîj¨s¥¤efNš=þô«ÒŠË§^úý­OÞžàs€¿\œ|jŒŸ=ÝÄzx´;‘–é~øo{æM+QX¹rUIÉŒxœO˜PRS³9ŒdggVTLª©Ù',˜ž•ˆÅâ6lcÌÓÚÚVV6åoÿû I•'‚]=ë6Õ›fÂ4qÓ2‰|kýq³ ;€C<íÓ†ø«Q³-ê‰F‡±€3° —‹'âåç\í/ܲö“GÖÝÑ:õÜÿ*ùÚU[¿µá£ÝmM3®¾5}Ü”±G²kÕË®”Tñ0àЫS’µ˜ ÓL˜Ùö†O›6uÚ´i^¯WäiÊÊÊD¦j„ ÃŽaIˆDzV¯þxêÔñ3f”íÚµÕªêK.^ìõyï€x1X4åœ'Ѳ‘<Å…†a–e‰ ¬¬lø¼ê`#dæÛ«\cãûVÉ´ŽÜ>1kéïíSMM-­-}žM¤_pÁCÆ0Œ‚‚‚ùóç­[WíñxçÏŸ'T*´Ê¹ÍÍÍÝÝ=Gu„?%<ÏçR-ƒiòöÝ ÷¾¶~ê¢SZÊŽÜòæëÓfÌ4²² ¤:; ³3¿³3­¥eßùçž}¶~—·ýõ¯|õêmÇ[8v˜ûÞ¯ê—rÒT ‡ñé?‹s3a%,nš–eÉu%·Ëÿ?{çÅÕð;3Û+½Šô. ØP±aÅD‰`‹%1Q“˜bI4‰ÝXc+šh4FËgï-*±‹b¯ˆ J¯ l›™ïÑÍJY–&ˆïw8œ™Wï»ûfçî}Ê„WE…ÜÊUÀ¹§@` ¥¢¢¤¡0 J•€q 0‡ÊÏÐ8zs8VÍeÂêU——Wèê*Ù¿ÿTTT;ww—;wxÊdÒ°0šfär1IR§O_ËÊ*æry7o^Ä0¢°ÐÔI ¬§J¥Ö’:Š$)RG‘:Š!Kƒì.pdKÅQP§²ìÓ1J«ÕªTÿU:EÕØ™“"µæ®>æ^!@SÉ{×i”%|s+‘¥ -¬–6%YÏžÙì;Aîοz’b¨‹W¦é½…¡i†ft:]@@‹â—A|¾››[zzºB¡`çT¥¤¤Èd277·”””šù«JRtù ¸2{Goxþ8¹¨È®gÏ6Oždž:uyÜØ>|>·%³PÃ:¨Ø‰8ebk¼*$µF?zôØõë7F)‘Hàåp§N§cÕNÓ4EQ$Iæææ?~<(00ºoŸÚˆ„a˜……å;ï¾³}ûÿ0 kÖ¬YJJJbbâСCårymJf©²É6"†´6§µ$¥ÖòårJ£M+-ÑȈԮ^7ù<ÇgoŸ”oÞTâêêÜ·o·)Sœœœæþþ·§§gXT,¬]…,*¢Jj~L ˜à¸Öû„ÔM38ޱ ñ8`ãr‚fh€~?—h€ÝzF¡À€¢AGIƒNÂéêÉäÜgÉÀÜTMöc•[k³„;ðûø©þþÕÚ‚Òh48Îp8Øóç`ذwe2±V«£iF"¼óN§õë3®]K y ƒ“¤ŽÔ™º]$Iê´$Å×Q:¥TªÕj’RʱÇfP´0¯ ·Xä¤VkKKU¥¥J•RUCßCc\.èÔ@ðù0#Vñ†cpœÃrx| iŒàà—¡k;§Ê*{‹Þ–*.)®0A~~¾……{mnn^Ý U·÷þÜÑ3óêy?t†EûÞŠœ$îƒÏ%þ<½l®·TŸ×z0ô“ݾ”çÔãJªU¸A+^˜Ô¬åT¡…Í® $IŠÃÁiºŸ]û©Ñ¨¬Þ×ÿnaM v´±zm`Ÿ\™%t‰Ã6.Â…DêÑÅ3¬E¥øl®:j‡fjóäVIe½E.—?O¾k×îèè¾fffìšPýê<š¦U*UffÖÍ[·ÂÃÂÜÜÝ%iíå‘J¥¾÷ë¯kGþàСCãÇV'•!•59ÀÉìnòm¡µ3!àñäJ­á©ÅssJ£õ—%­ûñGÐjµE  ÃòK¨{y6ã-ž:æãvµ MTG jI¯þÓhÿ›‚c@Ó ‡ÀÙPÐÑ@ãxMg«Ô©0нœPÅŽâ–f Jq%QP$÷³WÁ—-“:uêhzr™4''ÓÖÖN­Vø-óu:fëÖ}¥¥ê>, ÆŒÁçóÞ'‰ ÄbS j4$IRJ¥æñãÜììb’ÔºK; -ãQ:üfšê‰W©Ÿ•J­Õji£ 7ÙÉÇÐÁãk ²i•ÂÂ;DîÞ¢èé=®X‚\À‚àót¤FâèÎ3³Qf>ÑrB-]ž*SzKii©••UVV¥»VjIm~~>MÓ~þ~5™¢žò?®\›oñ±¿oÀ䞪'”P©£s™ÇI÷h‘湎àýFi™ÔËøa˸–5™ÜMÓ:†Á†f§«—‰5\Èçs†£ÓUÃJfZ­Ö°³¤+©ýjµZ§Ó±³ûM™Þ^†O®Ì‹Ï_:+T°•òÃ)ÚÞ‰Z?¹Æ1Ò[ø|¾«« ‡Ëù'ùDrr²³³³µµµ¡+N£ÑäææåææCûùû[[[ñù5Ì5D*•r88{Êx[TFš<²O—´ßþ>ú¼)„ IDATš°m-siÕÈ!Ÿ+—h sF¸°ix<^‰šþëß¼¿_NHüŸÍµ€¾‚9ã‡?é=¸_tu…Ižæàèå"Q]êxõörCÀ ì»àå ¾5žîZ¢J §dËŠ ¤´Ô‹k @© ¥dM,„ÈÎöí;nmí Kú÷ï) vì8xêÔI’ÔòùÜ‘#ã8|ĈÁwïÞÉÍ-xðàZï^¦îx®%ujV§Õâ8#qH’á”(hÚxÞ™ñøÚ?™üÀh.gšÔ’¤–d'8Æçóù|“Œ`††¢8"IaêƒÂGw,Z´qï3üöŸ 4% RS :J[ª;{7ë:Ô™tZ5!”Ö~?…ê®­r§%(e2™LZ߃%_“ŸËàv/Å{~òç f:òܸîêØü$ß æH0Ï.𛇧ZõÛQÝ*Ø( c0 +?X~U SÀhšV«5$Yé¶àz•V«…—Îà ÓÑ?¹ø“ûXFkQAQ)ÿ¯ ªî© j÷äVˆ‰½E$¹¹ºšÇÄüþûú°°°îÝ»é÷‘¢i¦H¡¸yó¦NGÆÄÄXZYu±çjýaJ“ͤ’%>ú|ån›£çwþ‘ï'²4å’ :3¯ý}âÆÑ³»ÍÍÎ6÷£Ûpôpèç×ûÈþ³V&ÿÀÓcÄ;…†ÿˆêR_«ÿ0 0 †|1HÁ¾Qp¬†_|JÝ‹íÑZÀh½QE޽¦ƒØ50ÕÿaÝ«gÏ-[¶df¦š›[¼Ã! zgçÎ=yyù Eivvê¸qM,\£!‹K”</)Q)IRÇ/Ì)±0Ôأ /f¸r¤º•NGq¹¸VKju$—!¤!ŸÏÃqÌô÷1ÃÐŽá<ÁÓƒäÎÞÏ¿aSžœØ’öï¾ìgI¥Â¦E[çž#xrUÖÓÌ+'¹"Ã` E3LmßI¦÷™\Ö®][vÚ‡Ã!‚Ï糓‘{±iÓf?¿šM)Q‚­T›pvebA¡¸l´ø…ë¾n1+t—¢ „N¿,¡\¾ÐÝù×Ñ.ÁÑ)#÷Q‚Ô½Nͤ¤›ú}†°åWêttµæ?ÇqNÇ*°N6p'ÎᮞÃZT ÍÁ •Dê³~½ ÷¢%R¨Å“[&ö‡cie5hР›·nƯ^=nìXÐjµùù{öìmæãçkieeº+×tT*UÕ‰ªƒ‰MVª˜>­Ûv/-Ö(®Ýu~×o¥Ê–[,ì–o=b.?çÚBÓq ¥­ETÿ QNm88ŽÀYOw÷j £·¨*sV!ˆjQïgÿÕ-ªŠ)¥@“š@Ó@3@Ó  é£8¥„RCä]”YV¯ ‚ &Ošôý´Y‘‘ïÞ½ûè§Ÿ~MKK!ˆóay<âÀ]wïÞÎÊÊQ«©³gþðÃì2³†É¯Ò(KU4—C’:Š¢hš*)Ñž?}ED\KV˜©DŽ4©cg¾cCQ:šÒa8!Uï4: 0Œà K²RíXîû¥Ô+į¹wiÆR!2·Û6‚«)ÈJ;ñEQ„@Àθ†Ú½•ªÕ[Zøû³[,ß8@&•VwŠ:Ë3a§¢œƒ[>á°ã&?—ËÎvqq©+i‘ƒ ¨.õ~ö_òóû²¯vÜXï>UùÏG.Y*x¦y|öY´|§X$æq«·QPPЈáC×®ÛЦM7™ÌœÇÐ4E’†Ñá8!ð32²‹‹KÏž=6hÐ{Ñ}ûš^²J¥Q©´@±k—hš¦i­NýüiŽ];­ÔŠÑQìëÃ!x<EÓ¤ŽâÕ~AÒ4Í0 †ã Nsø’œûWÕëf4ëøŽÔ5@æêÏ0 †¶0·ôé­Ìk§uZ Îà.¥%u¥*†©á|m=&ö–ÇOž<~ò¤ÊÒ¾Sƒ)ê,Ç.9ô·£ôÖ!>—šÙ·âÚêź°Ò"J•¯ƒf/ôÈT8øÂ£êdŒò«1 Ãq†¦«a·77kf‰”ÏÒ´Ž$uÉÉIÙÙ©3gLïßÿÝj«RªK Z^Zª IIê´bGw†sIEÓÀNG©TU±B£,Šªý¶YX+3s¹Î6Ú‚"ÀqŽ@\š—yç*‰­“ØÞãpq`Ty™ê’"ŽPJðÅ8‡K%êÜ'¥éÙfÞup ‹ñÞ2bøû©i©U"‰j¼‹:O$éôát€éå£ ÕöBêá“ ›‡ªNÉ-eš®:;oT)«£R©H’$ˆJRC£J¿°ÆS–q?Ó}4B×'h¨À ž#²è¥?,¬‹Ê¿[„B¡¹™9¼ØäŒKËjú¢«C``@ýn¼ÉïtìèþÐþï‡w)­ZŠ:4·²2s%„ZÐÊÙŸM“­Êÿ߃Ÿ8üäòÓH»³Û ô«Õá•¢–¼ü¾6¾Q˜æ¸þo›Mh†y±œ`Ý'ìæ’ »6»ÚˆÅ"w[-iê<.W*•™¾:϶mÛìÙ½kÓ¦MûÌÏ/¢)†¢hRGJ¥Â^={|üñ33³ê–Ùµ£ß–­ ¤N—r÷ M3 CSMÓ¬ÏêÅæ… ‹)Í×àoò¤AÕ­ÅíÝ÷Ts¿Â-äBs3JCAFJE²0ãpp‚lj¡ghšÒ’êÜ'é§NñòKÝÞ­›ÃÔªì-|>ßÑ¡êÃ4j6ðW% ~8SrDZð響±Â»ýüž?LXõ­ÆØ_•”_XÝ_11vìØY¡XGzªh†‘„Z{ éÿž\* 0/å9»n·ÆOn•Ô`ŽAIIq©²´>„y=˜Òäžž?xz@â­[Û~=~JwÉ.DØÑyÿ½“O±Œ¿Ž\ÿ÷n;3Ïá‚'ޱ²²2ñXw# Qm^ݨªŽWÿñ^|Æ~)S4Í0@Ó ÃvçO `0]Mfîà8.•J¤ÒêOÞÞ¸qÝô¼`ôèÑ£G®VFèÛ7´oßZ"Q% Ã4iu!j ùáíd »ÐJ 4ÍP4ÃÐ8À¼°vIšÒR CÑÀ0:¥º4=›—WRóp«ÚÔnzo!BZ£5}u‚W÷Ñ»â lñ¸çohÅÅØ}l@‡:óÒU¸*°Â‚Ɖ‰Pa¸Z­.cQÕÒÀª×'·2js®è£”ÇÆgñ7NjÖäÐ-B[´`æÂõÛ–½Uxûî‘û½BBç|þ¾u=ýð@ 5 ÎVÿ>yê¼:’ª.¹qãºÞ®ª–Eõ†Â¾¹ÛŒŸÚº]Ê®í…w3‚ËÐÌæ4£ÿ ƒÅ·ðnçöÎ{þÁ­ªµæ¿BjvRäkFliï7bíÆõ_yr¨¸¥nƒÚ ›PW…W¶*°IMMÕóWKöÉ­qo¹u³~5\ԸɆµmÔ¶eTsoñÚÀfΜi6òq®F­£à›žøX7u#M'00èm°¨M · ^oaoy ›Œ@4a.wµäg^Þð&­þ« È¢zm4Þ‚xm¼…½å-l2ñöP½]XÇu=‰‚hb Þ‚0·°·¼…MF šÜÞ¡´k×îµK„@ Ä Î;g<éÇŸWèb-*oÓ³ pïÞ}¨Ð®j×®]pp0²«Ñ œ;w®]»vUÚU&R £Šµ¨0 ¿š”T'u#ÞB‚ƒûôé}àÀÁòFkQÙØØ$''7ˆlx˱±±aÛ׉]U½á?¸š”ôå_`VûºM†a~YºÔÍ͵²OŸ>}"!aEQuUTMŽ©Á0Œ¦éº’Ñ„IKK«2 2Ðâ ??¿f-,,s9µáÕªï©zQ ƒvµB âm!??ßÝݽfy=z¤·c[9uK *¢®@ž*hääååÕØ‚ww÷GYZZ6¶rj\Be O¢AF@4~jÿ]Í–ÐØÊ©[Q…h\ðšƒc[š1˜6È`ÿ]«4´VG+T”¹„/̽hCU=m ñ¦0`À»n®®°zÍo%%%µ/ÐÖÖÖÝÝ RRR23³Œ¤ìØ!"<žž°uÛöà`WWµZxåêÕ«I-ÂÂBeRivvÎ?'Oê[§ïl;vî lÞÜ RRŸt¸OŸÞA}ûô9røcЬ÷ï?¾cÇŽíÛ·ŸúíÔ‚ü|6jÞ¼ykV¯)..öõõõõõŒŒ4žžaX ®]»Ô¥sçõë×ëcçMM}úé§Ÿ¶nݺE‹íÛ·ÿxÌÇ%%%UêÊH®×Œ)} {¾Xnž¹:Û;wG{æv,s6"uc$‚3O~kNߪ;ѶäŸhÍ™þÙ"¹%†¨gôŸT×.]<==ø|^qqñÞ=»ÝÜ\‡äíå% òó¾÷ ¸X‘PÈf´³·spp€¬¬,¥R%“Éüýýú¿Û$I6A``@T÷nfffAXYYEG÷±²´2ìîînƒâb===D"ŽãB¡ÐÃÃ=.v µµuÙ6oþ“-V²éÏöÀcÇŽ¦?n¼¥B¡ÈÒÒJ*‘°·E……¥¥¥†5±&oÎóçé'Oww7Œ¡¥RidçNpÿþ½>úøÒ¥‹U6V/@—.===8ŽD"‰ìÔ±Kd§îݺšÉå8ŽÛÙÙöêÙC¡P”ÉÒ§w/;;[¥RÉãñ||¼{öèN’Z¬ÜK´ÊOQW”ïW5 ²r*´¨Œ„‘'+=…µ¢X*3×ôåÔaëXêx¢º>œF¡(ž?þÔ©SÝÜÜ´ Ã0÷ïß:t˜Ÿ¯ß¼yóD"ÑöíÛ?øàƒÍýåïçÆcËpîܹ±cÇFDtX±b¥J¥Z±bù°¡CvìØéàà0yÒ䈈ˆI“&ǯŽ÷psK$å¥Mø7aÂÄ =zöœ>}F~~þO?ÿ¤R©ü|ýØ”™™^Þ^bÈ$²gÏŸýöÛoŸŽÿlÓŸ›`ܸq4EíÞ³g÷®]€s8 ÃIϪB©T~ýõ7cÆ|ìâìòÏ?ÿ,Z´løˆáÆë€O?ýŒÃáüðÃVVVyyyçÏŸ×jµ"‘ȸ®*ËU³Ïº¾)Ó¡¹80\)¥VjT !sŸ3üf¤2[÷<…æZa´Z““IÛ:2ªbÆŒGàuü< Œ@’ÚOÆ}rûÎ];‚ ºuí‚ãxvvöð#2Ò3üýý~ÿýw‘Htààas ‹”””-ÿ}äÈ.—+‰>úè£Ãß·¶¶V«•Aðx}:¡S§Ž?þø#A={t_´øGGÇfúÄ4ͰƒÅ†oÞü7ŽÁĉ`m1ý®GžFºH$23“;ØÛ³çœ*•Êã'þq÷ðlbMÆq¼ÊæÜºu»yófî£?ÃçñÔjõ×_O‘J¥áááÆËáü÷rÉÎÊþê‹/[}=y2´ n¹mÛ¶?ÿÜ4}Æô°ÐPsssEaT*%BŸ%!!á»ï¾çp8³çÌîÞ­›««+Ðt™M§MùD*û”5 ö_¶XåÃv#÷(øÇßG*L\Y9=;y>ÍÚUìõìô[·ž<ÈS·¼ì÷ÕÚRÝ4HR;cúŒà`}È’%K¬­­×®[ËÎNhݺõ ¸¸5«ã—-[^el–/_îââ²lÙRöÑ èÙ³çºuë¦M›ffnnee 6Ö6ŽŽʶjå*Ÿ/fuêìì¾/b;tèСCö:8$Ø××÷wÞyøà§——\.K%8Ž–l$=¨R©&NœØ½{76A^~îê5«ÌåräU«ÕÉÉÉ?üðC—.]Ø;w®R“Fr½p8R‘óðÆ=‚˽wê9ES"iqa^%±¤ ÎÜ:q‰À1<˜ ‰Æ98²¨^K—.Kºv­c§Hss³N‘f‰0Ÿ;g¶••5A8Ž@H«V³fÏéÕ#==£E@@ÿ,-,p‹^LÁ¶·³¿qófÛ¶íXC?))éàÁƒ"# .?33Óîåè•­­X$€Ë—/Ÿ8ñOç.]›9%''{yyY[[çædÛÛ;T)sPË–úëvíÛûøøIÌòÁ诟¥¥M›>£°°ÐÅÅÅÚÚª)5Ù”æÐ4pÆÞÎA*•²SÂ.\”––Öïwqœ0ÞXÿ^c«V®JÏÈhÈÞΛ7¿E@@Nv"3“gd¤7kæ¤Ï²ió_ö;w),,bCü[ø>|Ô° vv¶U6¡Ju!L§^ýU•n¤Ò £zEz:Yé)¬ueëàÖ+Ò«Æ¢šÄ«[ªsÊ„›ZHÅž*ƒAPË }I’—.]9r—ËÕvŠŒÜü×_ Ã-SV«½uëÖ˜1c‚`c­­­ÃÂÃ/_¾ÌÞ¾ø@i4šÛ·oöÙgú”ÞÞÞ.ÎÎÌË[’$×®]{ôèÑôôt­–ds=~ü˜TQ~öU•é¹\n§NõYºwÚ³gïÃ䇾>¾Fòòù|ww÷•«V•–*ÃB[yxz²}ȸ®*ËÕh)#Ofû^úí¬fÝ™œGw/¹õ²Âžä'åš…î£}x³H)“9dÑ9A4ÒUµM ½†ï޽Ύø8¼|»[[YY¿:šÃá”””@¿~}]]\ÊÈð …P(doÓÓÓmmíØw°F£Õ'Ó¯ƒËÊÊvpppuuÝË#ºDBqAAA9ËxòË÷ê~AËär[;Û(ûb±Xo44&WÙ†ap/..I~”………ûöíkÓ¶­¹¹9†aÑÑ}Œ4ÖP°Ô´ÔV­$/Wê=žÎãñBCÃøÁ‹q޲Ti˜¥°  E‹Ç5 bnfžžžn؉XbJ*l>¢Ô«§ªZF•‘r wgïƒ'_XT½;{W)O-©Ïcj˜W†ÿØ/}HQQI’ë×oذa£A94EQ Ã-SOQQMÓææ†Q–æ÷îÞ}iT½”²"9 MÓ–––†±VÖ6z£pîܹ‡þì³Ï‚‚ZŠÅ"…Bñþûï«5j½­¯škU¦K$GŸÅÌÌ ‹†1žwÕªU«V­ŒŸWoee5ìýaŒú J]U˜«Ñe‡ÿœ¡¨ÒGJžóTÉO´wÓ€ô)¹~ŒLºÇ–ªûÇK®¤hí‚”¹×…ö=8høï5B’:++kVá*õ‹YÜ2ejùÄ––ì÷Ö­Û'NÌÈȈŒì´lÙ26–ÒQjµš½–›É¥R)[¬àå[”*%{akk#‹Ù²—/æü‚<‘D̼ü*ãñxì+Y&ûoÝqY{Ý´É+V¬X¿~C÷îÝçÎ#“ÉfÏšõã’Ÿ1 kbM®²9$I GG‡À€6ÄÌÌlòäIéY†ÙÚÚo¬a½4MËåfúЦ$ Žã8öŸÅÃc˜¥™S3¡H„a˜\&gC Ôµ¡„zuoB…ÍGÔ€7Ũ:ðÏ=Ö¢ÊJO9xútñ1.OÝ¢7ªª7þW¡±Â†—sçH$‚ † Ö@ÿò¥-"•JqÏÏÏ3ŒÊËÏ—ËåvT6Wš}ž‹‹‹ c‹‹‹EB!²ÿþ‘#G:”zøð!0z“‘yÅ|4%}iq±V«år_lîPPr™”aãyíííçÌ™ )))»vîúåç_¬­lzôˆ2®« sEG÷-¯ŠFAà::»È©Óï[ê´V ™™~ÇO§bÌ£ôûÍI\ÂUp ÜÝÐÏÐ×^á99¹ÅÅÅR©´[·ný$²›!‰D"OO¹L–ðï™LƦ¼wï®N§4xHÌ«Ý5??¿´´T,‡‡…[[[€­­µ>AVV6[ExxxhH+‡ãêââèè999÷ï?ðôò)-)e·k׎¢¨  À2é·~pwwËÌ6u'Ì  –¶vögΜŒìÄãñZ‡‡¿p‘ýÒh2M®ò>Ÿß#ª;†aÏŸ?OJJêÛ·ollì¢Å‹…B±ÞX©¬±e(?£ËHbødÜ'4“Ëõ;²^¼pQÀÅBÊÌ̪² ˆ:äþÛâ.kQõíê»ÿd¥§øúv­zÜ¿¼bf¼¾³ÿx<^hhè¥Ë—>ÿâsÃ9Œ¦Ä–OТűcÇØ@ÈÉͽ|érÿw=Õzø|¾¿Ÿß…‹†Άääæ>z”lkc4Mët:±ä¿-øŽ?^¦vÒ`“ž*Ó©Ó:uª{÷îìí‘#Går¹»‡§)yYÜÜÜ&Nš¸eë–äääèè¾&êÊ0—‘d K™ƒÀ€IœÚKž_§\¢ÌtÏR$Ú¸ÆXÑïÝ=#v{×\yãž’q°Îd3yª^+zÏÃ0'OžîݧÇ›5szii)»T ®^Mºpá¼€ÏW©TB¡°ÿþnnnŽÍšYë—è IDAT˜›–Ã0Ì…‹—ºvéÌçóW¬XVT¤°°0×étúß Ü<•лW‡3õÛ)ú¼:nöì¹<ÏÒÒòáÃdv:ùüùó”J¥X,&IR_†aÙYÙ Ã`6xÈàÁCÀê5¿‘$YeK9ÎÃäGá­ÃEBa×®]:(—Ë›R“«üÛ¶m×µKg‰DBÓôwß}ÿìùó°Ð0[;ÛF^¹rUVU­ÒgV~àÒðÖÚÚê›o&ëoïß¿wðСví#ªÛ„ª>gD5¨ðËvßñ;å—×Ù:¸Ew«`…Y}{ªXaôµGwóÛw²ÒSöŸ#òÔ-u8ü÷Ê Xù12˜4iâˆ#?ýall¬M±¢äÖ­› Í|þÅçUÆ–aÜ'Ÿ~òɸñã?<8N­R¯^½šÇã9â¥sȘ§ ÆŽûÙgãÿܸ1nP\~~Á´iÓ¸\.+1†a­[·Þñ¿#;;88;vlû¶m`0CËÍÝ]­VoݺÕÏߟÏåzzyOÏ# þéç"E‘³³óÉ“§öïß?yÒ$A€‘¼ééé3f̈Šêîêê¦ÓéŽ;®ÑhZ·nÍ0Œ]ÉU³Ïº¾)kTqmQFaâe:&%3i*2§Pq%•yX Íƒ¢+Ç5wŠKEçéô©])ŽæTÕ?•½ùRÓÒV¬XåëíÒ*ÄÜÜ\§Ó=Nyœ””´{ïÞÒÒR-IîÚµ' ÐßÇÛÇÝÝ=11ñòåÄ)S¾1,êîÝ{YY™:DØÙÙ+Ö­[×¶]ÛŽ/×m`–ššº*>ÞßÏ/$$X*•+’’®¯[»öö;Ý£¢‚xššºwß¾ÖááÏŸ?[ÿû†˜÷bBCCõ%(Š‹>`goÇzG6¬ÿ=nÐ`½b¤™æÊ•«"ÚÀÀ:‚aXSj²ñOÐßßÏÃÃ6þùgRRRt¿~§þøÞ ‹®]»ž;¡ÊƱ™ ¢÷™aš‰&{XûˆÇæÏ_(“É=<<Ê”i¼ èË¡±QÙ'bëà¶vcG°Ø:¸U«(gϱvUuå© ugT•+7Fžž^›7ÿµzuü¢…‹Š"™Lîïï?xP›ÌxlÚ´i½båÊ5«WõÕ.—ÛªU«‹ÙÛÛ¿2Q½r£ª}ûˆ… ®Š_õËÒ¥¶¶v#F 74 gÏž={ö옘—üãKFŽ©Ÿ¡Ù©SLLÌÊ•+‹ŠŠ¬­­=j<=0  ç/˜¿páÂû÷XX˜O˜8aè°aUÖ%•JmmmþøccFFŽã‹.jÓ¦5Ã0Fte$WÍ>ë׌L@ šw¶T/· JCƒ-ÇšÐé4 ´æ†·£ujëÌÁ’Òp%¾aÒCõ¾¯7âð‘£'¾>þ¨"g'‡Ã¹péÒê5¿fd¤«ÕjÇ¥R©­]`@ älß¾óü¹³¹¹¹\.·yóæ_|ùÕÉþ€ÀÀ ¶„üüÂ… _»v­¤¤X&“={ö|ÑÂÅ©©OõUà8'á߳˖-ÏÈÈÔj5<ßÎζß;ï򯯡 RSŸ|püÄ?údïôë'—Ë6þ¹©ÂôpòäÉŋ޸qƒÇãµmÛnúŒzG; ·zͯ?ÌsûöíaÆ-X¸ÈpPïî; .HLLÌËË333ëÖ½û¬™³,,-+l¬é*e¹{çÎ7ß|sõê¹\þÑÇc¾ü²¬ƒM©T …Â&9ÐŽù4¼uk±X|ãÆõåË–õêÙóÔé[ÛŠ]zo" eTM›6mûöí#FŒ˜:uªaø¦M›çÎÃ^ KKK??ß¾}££¢¢Êx ¯_¿±aÃúÄÄÄÂÂB‰DÜ¢EÀÀ£¢¢êµ!F¸páÂ7>þøãZ–Ã0ŒR© ©,§§Ç¾}û‘¢½üªCmtÿ믿&U²~~Ô¨‘<ž`ÍšÕ5.¡±te"z£ÊÓÓSèææöðákZÎÕ0FÕï—6|=9À9àbé±"­B©U©µ¤†$•Z­š$Õ¤tjµZäæ#4bÞú5åªÚ£Ñh’’’&Lœ¨wƒÙÙÙµˆ8wî¬>P( ¯¬­V»léÒ½{÷¤¥¥iµ$˜œœ\{£J,wèÐAëã뛑‘Á^¿Ü£<222"¢CÏQ¿®Y3múôZVú–£Ñh:Äçó÷íÛ7yòäòÎÑ™3gÚÙÙjµdzzúÉ“'¿øâ‹ˆˆö«VÅóx/ lÚ´yÞ¼ÜÝÝFŒáèèXRR|þü…¯¾újåÊ‘‘_{ƒ.\¸°yóæ:±ø|þÒ¥Kõ·Ó¦Msvv=z4{+‘HõQo¹¢¯KK †-áMá­ÒkTùøø” ÷ôô¼w¯â5õuKÝUó^þfìØÇŒ ñòò’H$&f¬[t:ª‰MWgê #µìÚ½ÛÍÍuð!666»vïz%׋/W² ŠÔ¶mÛm[·ªT*†aÖ­]ËårgÍšE„a2O//???öúþ½{Ÿ}öYÛ6m¢¢z¬X±BŸìÌ™3ƒ 7vÜÃäd}Ôד¿~ï½Ã2‡þé§Ÿ²×‹.ŠˆˆHLLŒ‹‹ êСÚ5¿²QóæÍ[³zMqq±¯¯¯¯¯oddd]éŠeÊ…¿Šb*×Õý{÷>÷IxxxPPÐà!CõQFä11ïùóçãbcƒ‚‚,XÀ†:x¨OŸÞAÑÑÑÇŽ5jÔØ±c†9qℯ¯ï­[· Ë5jTu½*ÓUeÂ3•´7>>>$$DŸýð¡Ãlö¾}ú9|„©¸Û¾B™!HW¦£Weµ}E¥a†ÿ„|¡ÐVH08Cb@ Uq‰F1ªÓû‚óûÉ?‹J|ˆhƒQNöŽATXŸÇ'IÒ0ÄÎÞþè±c4M³«í Å;wlll*LÏçóCBBöíÝ;iÒd¶Š¬¬¬³gÎ 2ÔÄV“$)•þ7ê±oÿ¾jª¡&èt:Ñ©„„„§OŸ xu;Ð7æµ{ª²³³/œ??fì Ãzôì±åï­…EErÙ é 70e‹ŒŒ<þüÍ[7C[…^¼x14,T.“U&üí;w†¿ÿ¾§§ç´éÓ­m¬Ÿ>yúàá6ñ¹sçÆŽÑaÅŠ•*•jÅŠåÆÙ±c§ƒƒÃ‹Ú™WubðË’F¡(^¸pá·ß~ëâârøðáÙ³g;;7ŠŠ7nMQ»÷ìÙ½kàN[YÆ2áHQ†:¹ÿÞСÃü|ýæÍ›'‰¶oßþÁlþë/??ãò˜˜wþüùS§NussÓj4 ÜN8=qÒÄèè¾ÓgÌ(,(Zºt©B¡ðóõc¦cÇŽvvv[·n™5k+[jjêÅ‹gÍœùzžµÊÐ×nDx¨¤½†n¿ &Nèѳçôé3òóóúù'•J¥Ï^yõ¯ÈÐÈAº2‘ƲOUm¨ñN¯8רµŠ¢zõê ½{õþs㟇Š‹‹3ž‹}—çfç—””4slf$ñ‹[YYÿñÇ|>Z…´ÒG-_¾ÜÅÅeÙ²¥lw èÙ³çºuë¦M«`]myHR;göl/ooˆ‹‹Û¹cÇ‘#G¢¢¢är¹X*ÁqÜÁ±÷{{;µdÉkkëµëÖ²“ÉZ·n=(.nÍêøeË–—Çļ3¦Ï ÖW¿*>00pþüì­§§gß¾}Øk‚ bÆ®]·ö믿‹Å°}Ûv±XÜ»OŸ:lom0"C³fYYYúX¨ }VRBY… Eé !IòÒ¥Ë#GŽàr¹úb;EFnþë¯*å1%¯@ j¤Õh4·oßþâ‹/ô!ÎÎÍÝÝÝõ ‰y/&~uü¾ýûâbãH’ܵ{w߾Ѡ1xªª¾|{Á`›ý³Ï>ÓèíííâìÌTõÖáDº×Ò•‰¼ñFUßè~†“¥LßQ¬Í¬Õu«<àÁËIA\·»]­bäc'ձ䧟–üô“a`\\œ¡ƒaÀ€ãé;wîܹsÅ ŽfÍž=köì2&Nœ0q"{mkk»ù¯¿ csró*LÉáp £ÊÐ¥kWÃØòõþöÛZýõøñŸÿyeE5 ^³QuûöíG}ôñG …‚ é¹iÓ¦'Ož8;;ƒÁJÁ2ÒÓÀÊÚJ"‘H$’´çÏ*“¼¨HAQ”¥•Uùì‚ ss Ã(K ó{wï¾ôç¿"íKéÿÕâ¾jàFQ:}l¦Oõ©¸„r¶R”^€¢¢"’$ׯ߰aÃFƒ(š¢¨*å1%/ëpÒgW(4M›™™hff®o”……E×®]·mÝ;0öè±£ù±6ø;’ JáË·à?;˜ÍniiikemSõúæ iÒ•i<~ü¸¡EÐSSî{°f˜nQ€·“÷™gÛiÚž,>­e´€FatÂÝ3ÍäŽgŸ^toî^{‘o¯Ù¨Ú³güöëo¿ýú›a‚={öŒgÇ‚™Šm…S§Nñx<__?†aÂÃÃÏž=[XX(—ËË×%•J8NnNNù¦I¥RÇóóó £òòóår9Â!ð2Uk5jF*©ÌÅòâ›U[¹[Èt*,¡‚_·o½¢ôUH$‚ † Ö@ÿ ‘§y% Žã†EE"¡PûÁ\¿qcû¶íAAAž þŽÔëª á+z£ë-x6{qq±a‚ââböW\û›ã}¤+ Þ´y³é‰ëªÞ—š¡œ:XýgÈþ}{MOÜ3¨g?§h+ʪ…ÈTØH¡@ÈårG´lhõêáÑ).²IÍ¿F4BH’xpçÎ.—×*$äà¡Cêrûòóx¼€-Ž;¦÷Zçäæ^¾t9,4Œ½µ¶µÉÈÌÔ—”<0y;GV²ÇkãíTÇ ½tù’“““ë«ÔG^>ŸïïçwòŸÿNèzúôirò#Ã4¡¡¡î?ÿôSbbâ@ýÐv#Àá«Ì~áâ}HNnî£GÉu,eãéªñSÕ ©–§J.“wjÛ©L —›§—›g…éo ¯ÓSu:!¡°°pâĉ¡­ZÆ3wî—/_ cŹ|ùRjêS­–ÌÌÌ<}úô¥K—ÚµkûÅç/&7~ýõ× .Œ‹ííèà ()¾tñÒ±cÇ–.ý…a˜¯&L9r䨑#ß>ÜÚÚ:-5íþƒûS¦L€qŸ|úÉ'ãÆÿ|ðà8µJ½zõj7bä¶äîݺ­[»nUüªáïÏËË[¸p!‡Czû˹î½ÉÍÝ]­VoݺÕÏߟÏåzzyÕFWeÂÊ EêjÒ¤‰#FŒüpô‡±±±666ÅŠ’[·n24óùŸW)Oõó˜±cÆÿü»ï¾ë? aaѪ+,-,07L6p`Üüùód2Y¨¨ÆàuÐË`\øŠg \2cÇýì³ñnÜ7(.?¿`Ú´i\.·OjÙê_‘¡‘ƒtU%W“’¦}ÿ½‰‰çÌëæV/ǻձQU­9UD…¼N£jßž=b±¸{÷îe*íÙ³×âÅ?îÝ»'44”u}Ïûðù|KK ßÿص[W Ãô äçç÷ç¦?7nÜXTT$‘Hü[´øqÉ’ˆˆ ÃøøøüñdžU«âçΫV«íííûõëÇæmÓ¦õŠ•+׬^ýÕW¸\n«V­,ZdooÏÆz{ûÌž=gõêøß×ýÞÜÉé£1k4ZcãV_‘:ÅÄĬ\¹²¨¨ÈÚÚúèÑ£µÑÕ«A•Ωz›e¨+OO¯Í›ÿZ½:~ÑÂÅ E‘L&÷÷÷<(Îyª "¢Ã‚ âãã>ìÔ¬Ù'Ÿ|ºé¯M‰Ä0Y÷îÝçÏŸÝ/Úp |¢—¡ á+ïll`ûö ,\¿ê—¥KmmíFŒ^¡Ñ_¶ö7aHKOƒëÊðn̘²ä«^XÆfΜi6òA–REÒ0­oX6mÚìããÝïÝú“Ñ$¹sûæ‡ +»˜ƒãû§NNóòò‚—½+éÚõ¯¾ü²¨¨¨ž$),,ܹkÔk-M¤+Ói„º***êÕ«×'Ÿ|2lØ0}àÞ}ûfLŸ¾s×.W—†Ì]U(ü[HãÑÕòeËwïÙ}âĉú«¢–°ºšöý÷&U›6o~/f€þÅtùrâ;ï.e˜“•eÉHVYÔœýE âž6ÂÌË‘§ y¹¢f(•ÊøøøðÖáfr³ŒŒŒ ë× ‚¾}û²±?N{–¿jU§ŽТª ãÂ# yͺR(·nÞ<ñω€õTEÝÒàî´†œSU‡yM‰×9ü‡0¤+Ói(]ñäÉÓ°#ª¡­Zý0¾~iä‚ ®^½8eê”Æóiê%1.<NW×®%Mœ8ÉÏßÂÄ oÄÇÑàB"O¢ÑŒªÆÒ•é4”®¸\îÒ¥¿T&L|||ùÀG/‰qákÌÓÔԸ؊ÏEغm«sóæµ,ÿuÒ€ºÚ¶};««ÆÓs3MÇS¥ÓénÝ~ü0ùYÿw:žNS·ü´dÉÒ¥¿|øàø‰ÿ6·}§_?¹\¶ñÏM0cúôíÛ·mݶmÊ”)ׯ]³·w˜1cFßèèµk×®ŽÏÈÈ ^±b…‹Ë‹ýÇØ‚-Þ IDATô¿¯_?sÆŒ›7o™™É?úxÌ—_~ÉÆ>yòxú´içÎ+))177jÙríÚuÿgï¾ÃšHúÿR 4)Ò»€HQ@PlñD ôì§g=Ôô¬¨`äTûYÎ.XÐÓ» U¹OÅ‚ž…& €JHB’ýþXÍEJØ!ÌûøäÉÎÎÌÎc2™UQQi¡Š;'Ô©’6¨®ˆCuEª+âP]u’©z’‘±aÃÆÆá[·† ÉG½ÅÌ„VÍàU~¨ÕÓd îÞ]]㑘L 8\ #SY\òƒ'¥Cœê@WG«q&Ïž=7vŒMxx„®žî»·ï^f¿ÄwÝ¿ÚTÿáÃ=?}†Á`ìÚ¹cÌhï{÷Eúb0K/þqÁ‚À•ÇŽûñÇù?.XðâùóÐP‹¹)8xá‚ý}›ŸN¯ Þ¸qûŽ×®]]dan>ÎÇfΘA¥ÊEEï×ÑÑ)--ML¼Ïf³E-O§PZZÊ`0Ä”¹’’R¥k@uEª+âP]‡êŠ8~]I¼÷)á9UDÖéj”€ª‚}ŒÕß]—&Oæñ€Ãàp±+i¯>rä“ßÖ¤¼f}¨>·«‰NÕæMÁ::º×â¯ãÏ#4h0×ÎÛ{öìyòÔ)|¶{¿~ýœû÷‹Ú·/,<\Ô¢ÖÖÖîÜæââýú÷·êeyãÆ‡ÊËÓ ¶¦öçŸW}½’Íf³öìÙkÛ»7Ì™3÷ÌéÓ×â¯óñ©««ËÉÉ‰ŠŠöööÆczyy‰Z˜N¡ŸS?âOÄlõ!0Œ'î£t ø3GQ]êŠ8TWÄ¡ºUWëT‰:§ª*.€A’»ýœþ¦ª†Çãq9<êÙ¼·ê>×SU ´ kÉ<YåÔÊ&†sØlÖƒ–-_Ž÷¨±X¬ŒŒŒ•üûõôô\\]ÓÒRE-'(++ã=*PSSÓÕÕ:t(Þ£|%ñ’’~§JMM ïQáLLM‹‹‹@QQÑÊÊ*,<¬¦¶ÖeÈk‰ÔŠòH? ¹=´âcÇù Å;ˆHNJtsŠêŠTWÄ¡º"Õq!![$]i©Ú¾5T0d݆ü|_ù8\ þþ_µ›y73—F©``Ÿ« ¢Š§j¢Ê‹<^] «ò=½àiÁêÚJ§Wq¹\ݦvÑy<ž–Ö7©´µ´^<Nü¤øø’8 …"B¦P€ËáðCäååã“ÉdþÞsç/„íÚ¾¶¼LGGgá¢E˺^×êIFÆŠåËÅw^†íÙ»}B!‚tIh¤Š·fÝú!ü|_ù¸<(¯äÖU³G»j¬¨Ç”)Y¤5¼7…ðá#÷ÓGFYaUeAEwàè«‘œ­™ª¦¦J¥R?}úØÔ.52™\VV*XZV¦¡¡Ñ82•Jið÷c±˜ªÏ]$ÆÆÆQÑûàõë×gNŸ ÑÓÓ÷óóDZ$‹D"µbü’ ‚´À‚ H—%ñNU;/ætãz¼HñyMž„ËÅžåÔ²éVM"½gR¼ä½ÊÅò yùõ¹ï™o³éÅ5µµ•=Œ©=M›¸?N^ž6xðุ¸ººº»h4š““Óõøx.—‹‡|üø15%ÅÅŵq>zúú…>ð \UUõòåK‘N¿zõêµ%$DAA!';[ÜÇ’Llš<\YYÙ’Å‹-{Z˜šûùMyýúuŸo'‚êŠ8‘êJÆ+VÆOŸ¸’’’µk×x{224ÐÖÒ,ÈÏoÕ$Ü¿ÿþýûÂß‹O;wªZ1R%j§ª¦–û>¯Î¹·B!˜r-㱸rÀ£òêÍTU&éhµÔ0•ÛÛ‚id¨Ód&›·„”~úä3nìåËq©©©gNŸ^»v ¾ë—5kÿý÷ßÓ§'$ܾ?e²/Fû)  q&>>>Ÿ?‡…í¢ÓéoÞ¼™?ž˜Vr/((˜4q‰ÇSSSƒ™L¦ûСâ8–¬©¯¯Ÿìë›””¸eKȾ}Q?~ï3®´´´å”²Õq"Õ•ŒW¬ŒŸ¾Hòrs¯^¹¢¡ÑÝÙÙ¹ñ^T“ðu¤j̘1üÒß‹µ’œSåäè¸gϯM† IUô‰Ý§‡’±*UE˜ŠP¥Dª­åÔÖrÊ9ÕÕ$f0k)Ì:ù‡™ì>=9 ´&3±··¿yëÖÎ;W1 ccã©S§á»<<<Î_¸¶kÎìÙrrrC†¸9z¬Éõìì죢÷‡…íÚ·w¯™™Y`P‹É"xî"QSS300ˆÙ¿¿°°L&ÛØØ=zÌÃÃCÇ’9~—••yåê5WWWp0À¹¿è¨¨-!!V†ÎÕq"Õ•ŒW¬ŒŸ¾Hœ ÈÎy‡LIIi°Õ$`æîî^]]o6÷^|$9§ªu7a}*eZéM»“É$àɃ¦™¡Hbª€ ¹® ƒAb²¨÷Ÿ²]™ ɧO»æîS6lذaÚܵ20pe` Óßßßßÿ¿‡PNšäË¿%$¤AS~’ñTp³ÿþ¥eåBâ9r£ªª½?Fȹt1Ù©úëÏ[úúúøg¸¹»ß¼yS¦>†BuEœHu%ã+ã§/á_C5 R0§ªó=û˨Ï+aMøŒaÆfóêõuL6£–YWWϬc3ëØL6W‘D·²4l]IÉêÈÿ999ÖÖÖ‚!6Ö6wïÜa±X4ZÓÜ2 Õq"Õ•ŒW¬ŒŸ~;B5‰KNN777!ïŧó=ûï\‡Bþ$ 0LñËýv`€`€l¨oÓê’ Ô§OŸ>œ™ùâåËl6›}çÎCÃ:Ö•}ììCÔÔÕ1 «¬¬ÔÕmbÑ Y†êŠ8‘êJÆ+VÆO¿¡š9Ut:it¾‘*O­Ëé,Z=RUŸóæM;;{GǾééÿ@ówü!‚ ]†a...•••øfsïŧóT!]^«{B}ûâ“7O:•žþeøÔÕÕüp¡WV’H$55aOã–M¨®ˆ©®d¼beüôÛªIiÀŸõ†}ý×&¢®SÕ^iZšÅÙ$kkëW99‚!Ù9Ù¦¦=?ÂAuEœHu%ã+ã§ßŽPM†a©©©©©©øê&ß·û1»O"T‘H->^'Cô2µKZDJeeN)~;4ú/³ëZ©òòòþã?ÒÒR‡ q€¢¢¢ä¤¤yóç·µ]ª+âDª+¯X?ýv„jæT}þüi´óâŸ×ã…-1%iiÓK§cMæÓøX¾“'ÛØÚ.\°àÜÙ³×®^6mj·nªK—þÔá'Ý  º"Nx]Ý»wOOW'..–Hä.OÆO_$†]¿Ÿ™• w®ÇÇÿ“žŽïE5‰_/ÿµÏ%CA¤Ûÿþ÷?pvvò^|dëî?h§‰ê4eþüøçÏ?ÀÁƒæÍsœ2ÅïTݺõæÖ­7x´ÔÔ‚ŒŒâÌÌ%vv:/^|ÂUTäúéæýû¹ððaaYÙêI“l¬­£Y,.¾÷È‘q&&jùùt Q\\=räïL&îÞ}ÿÏ??nØà>qâ‚çÛµºSE¡P2š—BþAdöuª?â!ͽYœSÕö‰ê••L¼G…{ÿ¾ÒÐP//O úâÅ⪪µLæ†Ç€••?rM ïQáùW'$¼Ã{T] Ý𬆠3»té%Þ£ ƒë×_»ºvñ+€í0§J£¾‚ ˆdaÖ¯_¿’’ü3¿É÷b-@×™S…÷«š{m_ü>ŽÇèÔ/5¹ÿè  !G>ñô<åèxð»ïN€‚Â#‚µµõ‚i¹\L0„ËÅÏMCCAN޼jÕ&sÿßÆîššJ€ ‚ ˆ”éjsªš{Ô`ºðMQMŸn‘¶wï—%níìtZÎâr±={;&[S­Ñ‚ Ò ÿú¹9Uí2Q½9d2I^žRUÅâ‡øúÚ¶:7&“sÿ~î°afk×Þápxm)Xç"ñÿ‚ Hg$ñ¯®6RÕ"±®¨Îãa ï~üÑéÚµœÜÜÊÉ“m.ì'j&‚ÿJIùáÞ½Ù<úð¡J]]aÀC2™´ví¶d+å$þ¿AéŒ$þõÑuæT,ÖÕçνúúuùóç‹ËÊVÏœé0yòÅVgÏž}tv>òáCõž=^wîÌ>rħo_½¤¤¼¶ä‰ ‚ ˆ8ÈÜHUÛýô·`ÈÔ©±ü÷ÅÅ5ãÆÜK"m’¶G=‚› Æ€œœ2ÁüeÄj ‚ ‘Ä¿>dnNˆy¢:ÒF¥¥¥ CL™+)¡'Aº¬®Ö©’þ‘*±NTGÚÈÉÑñô™3â>DrR¢XÑ• º"Õq¨®ˆCuÕ¹ÈÖHÕ¸q/Z}¤Ëps*é"tÉI‰¨®BuEª+âP]wïþ}@#UÒ‘O22V,_N"‘Ä”?†a{öî;ÎGLù#‚ ÔÕ:UR>R…H?‰Äã‰kQ®‚‚1åŒ ‚ "wª0l“½7®Ç·z@Riq{™%ÚU׎ü©‘˜˜xîìÙ‡|üøQCCc¸§çúõôôôø{'ûNŒß½{÷W¯ÿ IH¸½wÏÞçÏŸafeeµyKˆ‹‹K‡•¿#•””ìÝ»çiFÆ‹/X,Ö“'Æ&]ü1”­&j]•••oÜxûößl6{à A[·nëÕ«W‡•V²P»ÉǶoÛž‘ñ„F£ ûî»P}}}IJŠtµ‘ª¶ôl$•‘e‘»wWUUÍœ9ËØÄäíÛ7‡LKKKLLRQQáÇ Ýjhhˆ¿—§É &?yòDP` ··÷ÖmÛ¨jfVfyYY‡ž@ÊËͽz劣£“³³sJJФ‹#ÕDª«úúúɾ¾ee¥[¶„(++Gþ9Þg\Rrж¶vÇ”V²P»"îñãÇ“&Nìׯÿuua»v÷wï~¢²²²¤‹&Þ¿/é"´w§JêGªD½Í”€Žü©±;2²gÏžüM»>vsçΉ¿víûéÓùC=Ü“¿Þ^¯^½ÌÌÌn\¿Ž:U o`<Χ¸¨PßÀˆÈ«¨óRšƒ÷ø=¨vnÍ7®ÇKÚ{v‚&á›HÇÃĦÅC?HKÛoƒ=ÚÛËÔÄØÔÄxÎìY¹¹ÿ /§?|hkkë`o§«£íäØ÷@LŒÄ¯è#NNN޵µµ`ˆµM^^.‹Åj. "›Ølvƒ4-;;[Rå‘*ýúõ#Þ£züø±˜ŠæT‰›Í’—§Ér:RZڃ˗/?~ü¨´´TMMÍÝÝýçŸÖÑÑ!˜¼¢¢bóæMvvö£FÂCTUU—,Y:`à@eeåçÏŸEíÛçíåu?1IWWJJJJJJ"wGoÚlddtõÊ•àà,6{ÅŠâ:C¤+ª¨¨ìc÷Í95uu Ã*++ñ–† 8KKËŒ'O0 ÃGÍ+**Þ¾}Ëf³™L¦‚‚‚¤K'a?&Þ¯êׯ_{T5 ‹#UÃv¿¼Ì'|³±MÁÁ¶6Ö ·=†540èë` &¦ÁÞ¤¤¤Q#Go܈‡¿ÌÊš1}º…¹™‘¡ÁèÑÞ>à'ÉÍ}?k挞æzº:6ÖVS§ú×ÔÔ _¼h‘çðï‹4ÞÇgÖÌ­.€´iõ@ÔÞ¼y3eò”­Û¶Mñ›ò×_Mûþûšš"#UL&sÎìÙµ Ʊß~£P(x ££ã–1cÆxxx,[¶üÂÅKååå‡Â÷òx¼ššš}û¢üýý]\\Â#"¾><:j—Ë{!"{æÍ›ÿêÕ«Í›6UTT|øðá§¥KñO2Y\kûu"h¤J2i\Ú¾Ù:ràÀž=-oܸ¾, €D"-Z¼øëÞª ë×mÛ¾£W¯^,&²²2G{{ÛÛ;DïQVV>uê¤ï¤I7oýéàà3gÌ R墢÷ëèè”––&&Þg³ÙB O´H›V_AÛ¼y³™Yþ¦•õò+þ¼õç¤o—EhŒÍfÍš533óÅ•«×ÌÌÌš‹æäädnnž‘ñßìÞ½û»wïÜÜÝù<†zܽs§°°ÀÔ´GÓY H#êêjt:]0„^YI"‘ÔÔÔ$U$D:}?}zqqñž=¿ÆÄìOÏã||îß»';—#„’‘*Y»û¯}&ª³Ùì;wöîÝ|}'?}ú42r÷¼ùóåää€ÍfEDì0p ?þ–Í›uuucãâh4¸¹¹á¹;âä©ßëêêrrr¢¢¢½½½ñÈ^^^Ð\8"€`ž‚™YÁM§~ýàã§ÂS±Ùì¹sæþ“ž~)6ÎÞÞ^xd‡ËŸ®nemýèÑ#¸Ãƒ‡ñ€LBoX[[gd|³|qvN¶©itAi,0(héO?½÷N]CC__ßÝÍuàÀA’.”Tu¤JQA¾åLE×ΟþÒ?Rí1Q]^ž6hÐ`þæ0a99_f ***:ÀßËf³SRRÆwh€D"åõ0=leevôèÑì—/ùƒ4Í…!R¤PÛ&£ÿçÿûXZörùÃáü8^RRâÙsçœïÜLJJÊËËí׿?¾9f̸sç.?„---C#£ö­¤kóòò.**JKKÅ7‹ŠŠ’“’¼G{K¶TˆÔRPP°±µÕ×׿võjvvöܹs%]"©ðøñc‚=*|¤JLÅèR#Uͽ¶.O!Tº©Þ¬¦®•_öª¨Þ{O§Óëë룣£cbðy<.æÍ¹óÂvíŒ_[^¦££³pÑ¢€€e$©¹ð–‹'b¤M«/ÿ ¢Óé6Ö6C‡º É0((ðæÍ›³fÍ./+¿ÿefž™¹YŸ>v0kæL}}}‡¾ Š/^thsáàãã³wÏž°°]‹/)--]·nm‹]%! Ri¬-#Uõõõ+^ù4#ãСCÖÖÖír%A‘r©Â^ep¤ ¾ö–øý'á›MRVV>|øÈºµk23³´´4·„„ð×ShRïÞ}n'Ü Ûµ~ÝúÊÊ uu GGÇùó瀚ššAÌþý………d2ÙÆÆæèÑcUUUM†€}Tôþ°°]ûöî533 b1[XyYH¤P«{BgÕª ‡écÄØÛÛ£‚ ˆŒ¾‘ªö ý#Uí¥_¿~ý}»qø–&ŸØeiiyäÈÑÆáªªªÑûcˆ‡ãüýýçîð/D¶¢]ÉÖm[ïÞ½7Ùwrå犄Û_þ:Æ&ÆVVÖÂ"‚ šSÕžiEÒö‰êˆXµz„éù³çËœêï¿fíÚö)‚ "•ÐHU{¦%®]&ª#bÕêNUlll“áè: ‚ H׆FªÚ3-aCÛžEs××ö‚ú@‚ ˆH¤d¤ª‹¬¨Ž ‚ ˆÌ’’ÕÛ¹Suãz¼DÒ"]I{=¦¦1IŸ‚ "¢ŽT‰©h¤ ‘:¨S… ‚ˆTµgZ¤+A*AD$h¤ª=Ó"‚ "³ÐHU{¦‡óôÙ¿—âîq8œV$÷4ñûiÓˆÄLNNÞ»wO+!Ö¬º4R… ‚ˆDJFªºÂ:U†ÕÔÔ¤>ü$UCc»;÷^Œî€?lX’““Ž=º|ù ©ÊªË(--e0bÊ\IIIL9#‚ „Ö©jeÚK×iòd  F¸Ù¿þ÷cq©‚µ¥±¶¦“ÉM.ÒxõúƒµqëŠÑ‰°Ù,yyš¤KÑžœOŸ9#îC$'%Šõ] ª+âP]‡êŠ8TW¹¹•†uª$9Ruãzü“Œ Á|ÓÉÑQH>çs,¦ŒÖfr Ž½Ç(¼ð¿ÁNƒÕiòÔúz©—…Æ?ÏÊ5ºÓõtÔ„=þÚµ;wäåå÷èaºfMÃǘ¼ÌÊÚ¾}ûƒi,ËÞÁ!88xРÁ°~ýºÃ‡€¶–&èëë?‘)$>?·]»v>x𠺺ÚÈÈØÏÏoÕêÕÍeuïÞ½ð°]ÏŸ?———›‚ƒ/]ºxðÐám[C³²²f̘±sWáúF¾pso‡5`eArR"ª+‚P]‡êŠ8TWÄIáHU;Ì8i¤êIFƆ ‡oÝ*$õn3Z5ƒWù¡VO“5d¸{wu9ŒGb21àp1ŒLeqÉž”qª]­Æ™$$Üž?Þø Â#v——•…††0 {{|oVVæhoo{{‡èý1ÊÊʧNô4éæ­?‚‚Vñ¸ÜóçÏ'%%…Jž={6nì›ðð]=Ýwoß½Ì~ MfuÿþýiSý‡÷üýôƒ±kçŽ1£½ïÝO46þ2ðF§WmX¿nÛö½zõb1™«º³x’‘±bùr‰$¦ü1 Û³w/º%A¤‹‘ÜŠê˜À«¤çTñxy»|ÇùâG­X1èS)ÓJ‡lÚL&O4åÈ ES TÈuuÀ` “E½ÿ”í:È\H>ã'L?asΜ¹‚{---9ÚdB …²;2rwd$ÁøÐ§]“ …7™Õ°aÆ Öd>[BB:ÑT‰V~NˆÂÂB55õùóæw×ì^^V~þÂù9sfŸ?ÞÆ¦Ù_½ŽŽŽŽ_;ñ®®n^£F>thcp0ñã2™Ì9³g×2q—¯P(”Ö¾‘µóm‹&ëJx«ãñx555Gîé ...ùùÑQûºvm£vÕ¢gÏžMõ÷sv0oþ•ž?aÑ¢EêëàÐ\äÁC_Ž»Ëb±tuuýüüY³FSóËó"¹\î?üwÃêU«`Ĉ‘gÏ€ÇÀ©S'O:É3oÞ¼»ÂÚR~©%kçÛÂëJx«#‘H§~?½mkhDx8N·°°ˆÞãïïïÅx<.—Ë¿¨-''wyㆠ7n`³Ùƒ :|øHçºïµ+â&NœDÒ1K–,æñxfff¡¡[|íŽË84RÕŽ‹H!˯Q>ÜsœÏ¸=¿îÁo?nR@À²€€eÍí¥R©¥eåÍíMNiöql]’¬o[¯+á­ºuë¶sWX“½Šï†oÐ&µµµ:ÔºrJÔ®D2aâDt¯_“¤d¤êëÌGìë¿¶‘TÏõ¨º’öZê“J¥ô´èùáCa‹‹"‚ šÄVTÿ¶û$áÕ;ûqqhu/ŠÃánÒéôçÏŸ›šš¢N‚ H׆VT—Àqo=)º”VH¦Ü0þ{_Çâ±9¼ª:n7ʬa&>Î’YéÊwÒDMŸ¸ÓÉÉÉOž<^¾¼‰…˜»¤eË–ëëë[ÛX)**(¾|õrMMõ‚… $].AD¼¤dN•lTýz5O[ÎD‰««ÂÕSæê)sõT°¯o¸zT¦ŽÍÀÕ¼§¶œÁÆ NÜù îb‹[rrÒ¾½{%] ‘µz¤ÊÅÕåÕ«œÈÝ‘7l<{öŒµ•õ‰'ú9õC#U‚ ]©’ÀqK*˜”¨d.‡C&“I$—Ç¥)`<€¢®LɧSʪyañ¯‹>×É“ÍfÉË7ý„A¤uZÝû™êï?õëMRmÏ Aé,¤d¤J¶îþ³5S<0¯ÃÄ0 € $Æe“(r$ŒË&Sä0ŒÇ‰òËOÒ›ÌdSpð¥K:¼mkhVVÖŒ3ð›t^femß¾ýÁƒ4‹eïà`4£zÆ'·w<9-É*-áébuÕ˜º<…ÜìàØlVDÄn|YmÜ–Í›uuucãâh4¸¹¹á¹;âä©ß`¸§'þÀy0p ½›«KöË—6¶¶Ö§ÝáÃGð‡™[˜óðàç¼sÇöž={ž::ÚÐÀ€ÿo÷ϟñ¿ü²zÿþè3fÆ]¾rïþý«W¯“Å€ªª*§££#˜¿®®?g§¥¥-¸W[K«¢¢¢µUÕ  N‚ "i»ûÃÚ¾žº¤Gªš{å“£1.·öù_5äëÞä²³  ÞºæÙíúŒ¬^³îUBÍãwl=FÙ3EýQBFªPUU¥P( .š>cF“b/]ZúÓOü'4e¿|)˜–L&WUU ƯªªRVV5552™\VV*¸·´¬LCCO¥RôX,&zb9‚ "C$5§ ï;ñ¿†»ÈHA ™Ãb~¢[”´‹^™”°16½è¥a1Ç‘„Õ½2)'Û3«¨Eù@ÂðgÂA£Ñ\\\RR’ÍÌÌ,¿<¯¾¾¾[·nüø×o\ç¿WPPèÛ·obR"?äãÇ99Ùüœœœ®ÇÇs¹\þÞÔ”W|SO_¿ðÃþÓU«ªª^ ôØhò´úúzQkIâÐH‚ ")©ê"sª¢€¢¤bì¢"¯*g>NÝ|p (é˜ùjY þòÊæ4Œû|R5×5[‹ñ0*…èH„„†¾yóf„ñqq±ii©·nÝÚ¶mëÖÐP “ÉîC‡þ~êÔ»wïêëë/_Ž;yâ„`ÚU«Wß½sçàl6«¨¨hÉâEòòÿM ûeÍÚÿýwÆôé ·¯ÇÇO™ìK£Ñ~ À÷úøøT|þ¶‹N§¿yófþüyT»ü¬¬­ëêêŽÿ-##ãeVV[ª®Ã”––ÖŠ’’’¤ÏAiR2§ª‹ÜýG…JaÓ‹+¥0Š 2ã VPW_ZYõ8û·‚]ôÇ ¬—ÕµJxEÝôjÉÍÏ©j¬wï>·î„…íZ¿n}ee…ºº†££ãüùóñ½QQÑ+þÙÝÍU^^~ÀÀÇŸ3f4?­§çˆ#GŽîܹ#44ÄÀÀpéOKsöðð8ábxØ®9³gËÉÉ âräè1þz vvöQÑûÃÂvíÛ»×ÌÌ,0(ˆÅdñÓz{{Ïš5{çŽzzzÒ¿N•“£ãé3gÄ}ˆdqAD8TWÄ¡º"Õq¨®rs* wÿu‘uªR–«W0¦ÉŒÒ\t©Ú‡…Á@¹Cx&4ŒJÂê¹,9g¥»¬&3Ù²%$¤q¸¥¥å‘#G›L¢««{æìYÁÒ²rÁÍñ&ŒŸ0¿9gÎ\Á½Ã† 6lXs'åïïï/°Œø¤I¾ü÷ ewdäîÈÈæÒv¼wïÞ€¹¹™Ëàæ>T‚GïD’“Q]„êŠ8TWÄ¡º"­S%Yyæ^Rç’‡ ¿°‡Pžq̽:¨d²D__oÖw=ÉÈX±|¹à•í ð={÷JsEAZAJÖ©’­‘ª +šïA:†¹¹™¾‘$‰?ï¾Ýˆ)gAD‚¤d¤J¶îþC¤AqQa“ÃT|y÷߬™3´µ4ü9ÂS0NBÂíqcÇšš›ðžšúßÊ«÷îÞ;vŒ©‰±¡¾ÇСgNŸn¯Z’Bÿ¤§ûM™ÜÛÖFOWÇÂÜÌoÊä¾.o‹4©¹–“˜˜Ø ÉYõ²$˜¶¤¤díÚ5Þ^£Œ ´µ4 òó…—¡¬¬lÉâÅ–=-LMŒýü¦¼~ýºÝO³P»"ŽÈç•Ì’’»ÿdk¤ AX¶|Ê?þæûÜ÷¡!!ž#FðCNž<èíí½uÛ6*…š™•Y^V†ïJøpÚ´©NNNûöEÑ.œ?·bÅr ÃfÌœÙѧÑ!ò ò54º,ÓÒÖ*ýTzâÄñ‰'üù×_vvö’.š4Òrp¡¡[ ñ÷ò4y‚iórs¯^¹âèèäì윒’"¼ õõõ“}}ËÊJ·l QVVŽü5r¼Ï¸¤ämmmá ;jWĵøy%ˤd¤J¶æT!Gd¢zG.(åìì,¸¹}û6‰4Õ*¾YŸ¿~ݺ%K–6ykÂåË—àÌÙsøZ¬#GŽtvîñâŮکšQ]H„޼ü'ˆÇã]ºxqРÁ&¦¦xÈ™³g0 [ˆïmŸL!S(•/›d²j7U ¥ÿOI-MMM*UN|à:5á-‡Á`4n–-´:‹À_ÞÒ××Ç{T```àæî~óæMâ9t<Ô®jüy%ã¤d*4§ éPææfæ=…Ç‘T§*%%¥°°pÚ´iüô‡mmmcccìítu´ûˆ‰áç3kæ,¶níÚ>”••íß““½pÑâ6V‘”c2™µµµ¹¹ïW¯^E£ÉϘÑ5‡åÚHxËÁöö251651ž3{Vnî{‘Ò”““cmm-bcm“——Ëb5½^Œ¡v%ªÆŸW2Í©Bd”ðYêíeÙ²€¿ÿ¾=qâ„;vLrþÜ9%%%Ÿñãù!%%%%%%‘»#‚7m622ºzåJpðF›½bÅ °±µ½x)vÖÌ'N…˜¼¼¼Äq:ÒÃ×w>XSSëüù M^ÀB„·UUÕ%K–8PYYùùógQûöy{yÝOLÒÕÕm1­H***ûØÙ †¨©«cVYY‰Kz v%ªÆŸW2NÖçT_‹HøO´”””˜ýû?~TSS£¯o0ÊkÔÏ?¯ÔÒÒ"˜y×¹{÷Þ½{òò 99ùÉ“ÇË—‹üù+=Z÷£\ÐÝ»÷þùçyy9ÀˆæVSSsãÆõqãÆá²Æñx¼ššš£G ÷ô—ü‚üè¨} åÙ³gSýýœÌ›?O^žvíêÕ¥K–€4aâÄ6–_šE„GЫè%Å%¿ýö›¿¿ßÅK± æy ÐRËqtttttÄczxx¸ººyyøÐ¡ÁÁ-¦•àI‰jW"iòóJÆ¡9UýUzzz¶þfzK7Öˆ‰™8aüçÏåk×­;tèð¿)/\æ1ôÍ›7m;N,99ißÞ½’.E³Þ½{ÿîÝ{áqÚx¯¶¶vkhÈÊ• û63!¿v­®®nê´ï»wïnîîü¡t:½°°B¶lVSWÿýôiOÏîîî»##=<†­]»¦kO±±µ4hð„‰ã._*MSž¥‡ð–Ó€“““¹¹yFÆ“V¤N]]N§ †Ð++I$’ššš¨Y‰jW"iòóJÆ¡9U666666´Àß8p 4?¦õ¿þÙ¼y“Ïøñ7oý9gÎܱãÆýòËšÛ ,kþ¼yâøbc³Û4¡É»Œ'ª·ÑÞ½{uõõ}}}[Ž*àܹsÆÆÆü)½8«/óQþëñ0IdxóæMo[[Á'X;ôu(++kð5ÖUÉÉÉÙXÛäççIº ÒHxËiŒÃáò?èDM+„µµõ«œÁìœlSÓ ¢fÕaP»"¢ÉÏ+'%sª$÷ß«W¯ÊËË Ç«¨¨xûöíÓ§OÓÒÒnÞ¼yéÒ¥°°°æFGGÉÉÉ…‡GÞÓ£‡ÙÊ•YY™wïÞÁC/Zä9ü;Á„ã}|fÍœÁß|™•5cút s3#CƒÑ£½>|Àßµ)8ØÖÆ:))iÔÈF†Á7ÞºuK[KóÙ³g‚úNšØàÍ%oñˆ¹¹ïgÍœÑÓÂ\OWÇÆÚjêTÿšš"g[¿~ݯ‘‘UUUø¢pöv}„ä)➨þ"3óüù ›¾V5|;TÕÜóòrÓÓNñókЃ3f ܹs—r'!AKKËÐÈ 333ëëëù{?~¬¢¢¢ªª*r½t\.Wp³²²òÑ£G’*4Þr8Ž`䤤¤¼¼Ü~ýûI+//¢´´/ ‡%'%yöù|Ä µ+Q5÷y%ã¤d¤JÂëT]¸pÁÊÊêíÛ·T*> þqÃår9NnnîêÕ«333'Ä0,99yÈ!øP¹ 1cÆlܸ!9)ÙÓ³å%Ѳ²2G{{ÛÛ;DïQVV>uê¤ï¤I7oýéàà€G Ó«6¬_·mûŽ^½z±˜LC##CCÓ'NDþú+áýû÷ÉÉÉ‘‘¿6™ƒä-qæŒTª\Tô~ÒÒÒÄÄûl6›hm­âq¹çÏŸOJJ •Úö<Û]‹Õ…_§‚ËånÞ4mÚTË^½ðL0b¹?ðÆ÷ÑxzŽpuu]±|Yqñccã«W®$''‡G|éÇÿ¸`Á‚œêï7gÎ\9yùëñ×îݽûóÊ•]uâËŒéÓŒŒììí””” ò NŸ9]]]$érI#á-gÖÌ™úúú}_¼x~âøqCCÃ… I‹aØë× 3+î$hijéêê8îÝ»7mªÿþ˜_ßÉà;yr̘… ¬[·^II)ò×ÈnÝT—.ýIRÕÒ$Ô®DÕÜ界“’9U’¼ûïÝ»w %22ÒÂÂ"??_]]ÿ6ª¯¯¯¯¯ÏËË[µjôîÝ»qÚêêêêêjSÓw‘H¤Â„n1Û²y³®®nl\F77·‘#<#wGœ<õ;ÍfEDìÆ?°p³fÍÞ·ooHh(¾@Ñ©“'UTT&5s±©qr!G¬««ËÉÉ‰ŠŠööþòSRÔ[É444º©ª’Édc<¤íyv¼VwªN:Uþ¹|É’%9´0• qáÂ…ƒõèÑpIR‰tê÷ÓÛ¶†F„‡Óét Û? IDAT ‹èý1þþþøÞ‰'‘€tà@Ì’%‹y<ž™™YhèÖ_¿»žážžq±—bc/ÕÕÕuïÞ}àÀGãO¸F o9ƒ‡ ¾w9..–Åbéêêúùùÿ²f¦¦‘´\.÷‡æò´zÕ*1bäÙsçãñ¸\.òƒœœ\\Üå6lܸÍf4èðá#ÒvßjW"òy%ãdýî?=ztuu5¸¸¸˜˜˜ª¨¨P(÷þýû€€€ .ð?JDÅûvH¹Il6;%%eéÒ¥xÿH$Ò¨Q^GŽáÇQTTt0@0ÕÌY³""Âcc/Í™3—ÍfŸ;wnÊ?%%¥&Ñ ¹ð#***ZYY……‡ÕÔÖº bmcÓöÑ]qäÙﬨÞ:¥¥eû÷ï_»v-ÇÃÛÔ³9ÕÕÕŠŠŠ‚3Ÿ ‘HOžd4··[·n;w…íÜÕôeè 'ví{ýÍŸ?þüù’.E§!¤å, XÖº´T*µ´¬¼¹„ß Þ`¯¶¶öÁC‡—ZP»‰ðÏ+Y&%#U_g#a_ÿµÍØq>ø\õæ^ó÷ ²±±qužlddTSS#''÷îÝ»y󿥧§7X¶NP·nÝTTTòššÌø¡°Ã0"óètz}}}tt´¡ÿßîÝŸ?ó㨨¨4è…hkk;ö䉓pãÆõòò²9sæ4wˆÉ[<â¹ó\† ‰wwwëÓÛvß¾½­¶áGžm!¦Õ?}`2™›6mòU]]ÝÍ[7‡ òðÁCásªANMbsª¾í;µóH|½ØÜkcâ#àæß²³W½zõjÚ´iééé666¹¹¹Í…D"¹¹¹Ý»w¯¢¢òß­[·`èСø&•JiðmÊb1T@UU•B¡,\¸húŒ†3¾…›;wîxŸÇŸAøëæ_Žþü|9]áó©—.ý‰Åb­ùåÁïËü¼¼Ý‘»mlm‡÷ÄCôôõ ?|àO2¨ªªzùò%þžF£¹¸¸¤¤$›™™Y~Kx™‡ q±¶¶Þ²ysZZêì9s…GDüˆ½zõÚ¢  “-ü,Bž&x?š<%¥¸¨P‹ª«¨(÷ÿ…BÑÒÒêß¿¿®Êƒ ‚´£®y÷_+à#Uø«ªêáׯ_L8pРÁÁ![¶~(œþýôîšš/³²:(''wìØoüu|||öîÙ¶kñâ%¥¥¥ëÖ­œ^:fôè ÆÏ;W__ŸN¯zòä1ÆÃ6ðïÉoÆœ¹?¬ùeµººú„ D:_!G,((X¾,ÀgüxKË^çz|<“Ét:´Å³dem]WWwüøo}û:Òä廩ª6—§Ôj×Q%tÕA¤ë“’‘*Éwª˜X[[»æ—ÕÁÁ›Ö¯_×ç#~]ïŒÄ¡ ?ýºuK–,Å›J“†zxØØØˆºwòä)“'OáoNœ4ÉÁÞîâÅ‹x§ŠÈqù„7ï†Ú"ý}eÐû÷ï%]Ô©B:œ‰ê¸ŽìT‘É®€ÿõç-}}}ü+ ÜÜÝoÞ¼‰¢ ö¨`à AP\\,¸}û6CÃé3ft™¯Š®wFâpæì ÃVÇk®™1 EEÅæî̾§©©I¥Êñ§!<.Nxóî`¨]!ÒßWÖˆ´"“££˜Šþ$H‡2773·è)}çÎ]ooo<äNB‚–––¡‘p8*õ¿ÏÞ¤¤¤¼¼ÜI¾“ðMá{¹Üÿ³wßqMœÀŸ öTÁ°‘½‘!‚€ X먶"ÚjÕj]Å.GݨuÕUµÚ*Ί¶U©`uPhý)VPPA•Lòûã4FBŽp‘ÏûÅë^¹çîž{î¹ûÞsÏÝ $/ô”••]¼xÑËËS‘õÖ!óVlWJQêÿÛ2Q1“½½½ô¤{÷î©¡ª@Ý쨮rPehh8rÄH¿}=ý7r7oÞüÑGÃÒÒÒä<ÌP$ýzø0!äzÎuBÈñÇÍÚ˜ñx¼ŽÁÁ„ƒ­]·vì˜1Ó§ÏÐ××OYžbdd?$$dÆâ£.]¼HIMÝ–šºMœÃ¨Q£.Z¬ZùáÁb±R·ï˜?oîÒ%KÊËËœœV¯Y;dÈjj§ÐNûÒö¥¥í­©©áñx|0äëo¾iÓÆL‘©‘‘i{÷ìÝ»§ªªªuëÖÁÁÁ›6mwl—¿^‘P(ĵ’¿yÉÿÿ‘x¢úÿþ÷?ñgµ=iA¨UžOT§‹·Ýõœkræár¹Å%OäÌ`nnþÃúõ2'eVâ•[÷òßµž¤ïÞ7¢—‘‘ÑÂE‹eFØ'Nš8qR} ÊŸ:zôèÑr¯ÐÉYoˆˆ:[»œÍ»¹`»’OÎÿÈÛ¯©¡žÄvóæMu¾»A¨›:Ÿ¨.aááx-Aø)''GÍoDPjÕ¤Õëôð=þüýû÷ûDõAPÐà…ÊÐâ4ÝÕ§L™ÊkÛÖÃËCGG÷æÍ›{~ùÅÂÂ"NâõÆðCPP—Ê KþÇŽþ–~4Ïç›™™ÅÄÄŒ?ÞÔ´ZªZUв(ÒQ]åh䈑#GŒ¤+7Ð,ÍTáá n >Q]å×Ô4üxøû+øšä;w¼~ íÐRj¥HGu¥BvìÜÙàœþþ¥¥¥MQ U nr:ª:zÔ©S§.gg+’uª¡àÌ’æÌIVv‘+ãÏ?›»ƒ–ß*ÀßßÒÊ:00ðÒ¥Kïê°s—®Í^M¾K¿••%ïÒ¥ËM±{§‚$BH``õ¡9%%%ýpä¿©¯Ž]“=vMvჂúþ/Zxèàþ¦»4煮ëW/Z(½EÒíÏŒ’[×áC“““„‚—Šü%'')>3þð§ÉÉI"‘¨ðA†¾cæ۽'''>t°°þèEæŸäéàý„tÕON>Tì4uÓµŽü—””„–*P«<¹Õ/gg'Μ©H>wïÞ®­­¥¥lÍëþýû„¢Âû–V6bøŽ UÛ½+"nذ;w6!—ÿ@ý|¢º²·oˆßetüøqBHdd¤ Ÿ.L8þaˆ!íCjóVv÷Þìwó) A¨•"Õ¥k‘ u/‚*õíÛ·ªªJµÏ@ &ÿ0Äö!µy+»{Ï/6æ@PêVÔÐÕëÔ:{{{éDIªèÒ­[·ŠŠ Õ>]˜püÃCÚ‡Ôæ­ìî]r’ͽ{÷š¦ÚÑA0Žd-rtt”N”3?€¦cÂñC iR›w#wïöööyyyMPíhƒ  ÔJ~GuЏ¹¸¸H'JCK]þúë/BHXX˜ Ÿ.L8þaˆ!íCjóVv÷.=ÉÑÑñÿûÝÕŽ6ª@Ýï¨~óæMww÷:‰ÒTѨoß¾OŸ>Uí3Ђ Ç? 1¤}HmÞÊîÞ¥'ݼy“ÉW'TZ)ÛQ=''ÇËË‹ ¨R‹ààà’’Õ>]˜püÃCÚ‡Ôæ­ìî½Î¤œœ>A¨_‘’Õ¯^½êëë‹  Z&ÿ0Äö!µy+»{—œtõêU†GTA0tµÉÎÎFGu5¸|ù2!$ @…Ï@&ÿ0Äö!µy«¼{—?s ¨µRª£º‚ÐRE£¾}û>xð@µÏ@ &ÿ0Äö!µyãáŸ4ÃÕË××·  @µÏ@&ÿ0Äö!µy#¨ “üŽêþþsçÍ‹6Lñ •}KÃ1áø‡!†´›n÷¾cçÎU+ÍTºÕßFE½sÇΊäCÕ"gÐþþ–V6—.] ÄÃwiHišÝ{€¿?CÞ¦La%%%Yü·ðY%_@™Ý߬¾YwìØéîîÛ¯¿‹ï‚ÜœkGޤÇÅÕ=A±²þgF¢««+!äÌÙ³ywòúönŽ@ËräHº£“ch§N„ .¾×¥H”QßÌrÚ’”Bôu¸n–F/lEK¨U}Õ©-ûÈ‘ôf(´0∊^ª@Ýêë¨Ú©SSlâMç%¿ŠèQŸÙÍ[hiœ›»4xɯzüßõ—üj-U n’mTååÏ.\¸ØŒ…PW[¯m;®¶Î«Ñæ- ´d"Q‹Õ½¹K-œ^êŠ,-þ„  ÔªNGu‘(#44´YKÀgΜF½ HÖ ¢.ÿé»bDTúIvT õ÷÷ÇñàÌ™3¡¡¡Ôñ#44´µK¯ˆÐ°æ.@3;qæ/q½`&\þƒæ$ùDu*¢jÛ¶ííÛ·›·TÍ®mÛ¶âŒÖ.½ lÎ_¹×Ü…hf†6Ä¥Ãã*\þƒæ$ÙQÝÙÙùÞ=9‘xÍ™s;Ë×ñREBÑa1úí®¸üÌÂb±š» až>}ªÚ‚­[·fr>’P/@Y/ž«¶ ¡±9“óa8\þƒæTßÕôôéS'''Õ–½sçŽ8ŽaZ>u ¦¥¨’„  ÔJ²£:¥ÎÁcÙ²~Je˜pˆ†bæh|´AåÀ´|ê`¿xâç¥òŒr¹QeMÓøíMÓöLo>A¨›œ×}Sº/»&þœ‘à#”öâÃÑՄô|¤²­›¢T½ÐÌã‘:Œþ(|h¿ BHm­ ~Ê–â'Ï©ô}?36Ò}ö¼zÀ§ëšµ€*j|BeÀ´|4‚*hf2IT´$>NÈÅU’–†i-LMÔR…zÑDXäÕ/£¥Å‰ÑÝ´¯P[טš@yY[™“™âÝ5¡ù ¨"¦mÏMT/˜A¨•tGõFÖ•çr¹/_¾l̪›3KÅ(LÛé«3¨jdž@!?̇MÝý K/°î,øë±Ùôl‡LˇhX—*U vu:ªË$y-£ÁQBHLßhWWBHÚ¾ýí}}íìl !yyw3þþ¼¸¤äÏŒS/**œúÁàA66Ö„5kÖÕðù’)«×¬ãóù*—ª%kºËv¿eÞ~T˜'='Ïʱwg¥ò!„ÔYDf¢2¯ž(^/ÔßåÛ‰}º†¸B¾ùn_tŸ@_{.—}é꽕›O–?«"„t qéßÛÏ’gbl¨+‰ž–Ufçlß{®äé *‡™“£Ã;:Bæì}¿__»ç/ªÓŽ\Þ쟨îÞÄš·1ÌË/Y½åÏ[yÄëu²7Ò¯ƒ»µ‘‘îóÕÿäìH;ÿàa½5EÜR%¸\î˜1ëvßÐÖk%9›Åj°´LÓt}¡òÿͪ¯^عuV*BHEd&JæC(V~†@PjÕ`Gu"ÕSJþhÅûFG½|ù²²²ÒÔÔÔÝÝMGGû—={µ_?A„Ñ£G›6­ !>?tð@´iýbcØl¶P(|úôi«V­ÜÝÝìììRS·WUW³ÙìÁƒBŠ‹‹Y,¶™Y›V­Z­Y³–dz0003UOOO¼Ò;·¿×€¶¶¶8eÇöÔ©V*~f§¾ÃÌ#ÇëtÁœÃأ¼ß2ß„PT¸Æ³rT¼<ÒiJÖ‹úŠÖTÄ+œ5¥––¶H$ÒÖæ†vpª|V¸hýi®–›ÏÓÕòÅ‹ŠÂÂBSS“¶fF½»zú¸¶=m«ˆ¥'™ÅWc;óxŠïԩüyó !Ý;wøaCêÖÍ«—¥, 33kc¡_P,l­¥¥=et—Ë)**êÛ7ú~Á}/ÿô# âß÷žò›ž¯þb’ßÿÍÃÓ·½¯ç¸QWl½ôÖ,,Vƒ¥m²ŸQEMZ/B"FJ'ž;±U©záàÑ…¼Ž«<ºÜ½‘IÕ *½¾|4‚*`"Å;äÖ±cçO–VVÝ»÷(++§R¼¼½Žû]<ÃÊ•ßgÿóO—®ÝZµ2íÚÍ”ŠŠXlö¼¹sÌÌÌ9›Í&„&Ï™kaa! 9lö¨Ñ£»tí&‰ŠKJnß¾ãíí£««WUY)gªRßW©Rõì%ãÀßr4é]{õe.g¥2'Eus=ú'yT˜GEW<+Ǩn®/£;ª¿^áŠå),®©Cà°{…„Ãáh‰ÊE"Aî­"ŸÆǵ57ãrÙ­L^u´³³:ý†™u{qN‹.¼ó_¡¡•5úôéÓÙ3xa—rËÃÂ!ÄÌÌôîÍ»AÁámZBØlö÷߯60±b±µX,!$4´Ság_:ÆÉ*æ›_æÇÇWúzÆÆÆnN=Àz3a±XŠ—–!š´/T}™ËL—““WWò:®¢ê•"§<A¨UuTgRVZêííÃf³kjj¨”V¦­ Å3ܸ‘Ô±£••!ÄÊÒŠJ473373“Ì–Ëå¾xQQ]SsòDFXX§¶mÛ¶mûêItÏž??xððÓ§Oùµµr¦J—“R'E…R‰D"*Æj™šôŒ\© J~Gõèîn鯎ÑÝÝ,T¢œ%ÖŒ—ÿòóó­\ºp¸Ú/_7s8l!ÿÅœqAíÛI/¨«§[]YÂb½ÉâîÝ<·îVâ 9\][·6÷Õ¹ —í­~bjü*ÐáñxÔåB1.—K^–"d±¤jŠÄ/s!ûæ•ëwÚ{;ÿtˆHô¦=›Í&³¦öm ´ CG½¨7%ë…¼Eœ½»‘×õY~y4‚*P7Ež¨®x‡Ü:‹ÛØÚèéë³X,9—Y IDATc*¥´¬´ºæM?¤ÚÚ—ffæÔRUÕUTbzzú7ß|+]ŒÚÚÚ7oþõpuU¥­——瀌Œ,-ÛÞ¹s»uë6ò§Š^ß¶¢­­M…PÆÆF2K®l©tuuë$rØlj'D½”W|µQ$QW?9!„ˆD!£ßNÚ M ªŽœ¼I9æ¥g¾=Üå—§™'úJtTo|e½^¡H$Ò3hÍ~»«¼«“%£dggòñÈû÷ï÷éÓgûޝ& ’ó …B}C3ñW:ú¦›Ã‘ˆX¢g/^Õ”´´½ãÆŽ•.‘PP£¥­_'Q²XlÙ}øj{o§Þ½{ oê…«#¯ÁÒr8l›Eá×R5ŽÅå° !/B¡PDÑÖâBB‘dÎM§ñ·&P?¸Ì|êË\öÌõçC¹u-C\/!®>Ýå—Gã ¨µ’î¨.M©¹uŒÿlü‘#é¦&&;Q)çÏ×Õy+7ó—<þÜÈÈ(22òý©·…èë뻸8›gfæ°Ù>¾>FFFeee?:ÿ÷…˜˜CÃììì^={É™Yñ¢‚ZQhh¨@ hßÞ×ÈȈÔCñRÉ\ü½÷bmll!?¬ßÈçó½<=»wïJ9q"ãzN޾¾þ§£?!„¤í; Ñ ]qùï×7¨#GL„ǯ'ȣ¼#'IL„GcŠ×˜8Ô@ò²Ú«VX‰"½ n®]»ZöBØ3áÓÏ¿µðÛó³XoõA¬3J¹›ÿäÉÓgmZÇÄÄöx¾¤Öbb¬×¹£3ÏÜxó®¿ê-©D¾¹ÿ+ºøÏ­~®’›‘¡^ƒ¥Mú"¶½§ !$nâÆçµ½»z}ß•²n[ÆÑ?sLŒô·ÿ !äJnÁÌÅØlN=…¡Fœlü{õ$U/Ü|{×שÏН”áTº5iGuss³¯¿þR<úï¿7Ó ¯³Fq+NFÆ©è¾QÚÚÚÉI³***Øl6u×ÞåËÙçÎ ïÜ¥sx—Îá55ü—/kuõô8l6!$3+‹_SÃb³åMe±þ÷¿Ûîîn„ ¾«¬¬400¨­­ÕÒÒ’ùÅ/U§N¡r~ÞŸ~ÚÙ/¶Ÿ8ó³gÎ>, xusSaaáoÇŽF÷‘“ÃÉÜÕ>ž+}ÏÊ16Ò³¾šîàAF¼öØHÏÃÇɣ¼_O9åi0‘ñÕßx}©ûMnÞ~ðüE•‘¡Þ°aqí=,­ÛÙZ½yŠ‹Ô¿Îµré‹%‰Ö¦f~ûyÍë—”•¿`³9ÆFz„ŒÌ‹ÿåuðŽ–U¶ºyþüëµ~oõx»•÷¨áÒ¾ÎçÂïK\‚ƈÇó®yœÿÄÔûUçëò’»7ϧz…~¢Ð×2ÛunüsBf½ðð‹ž¹©ûTQ…¯RÅ“Sƒ  š-rÅ™$|‘7<.,<œÃfgff.X°ÈØØÄÙÙ¹¾ÎLù«W¯õps  hÕªÕË—/ïæÝÍÎÎ>pèPEE!äÊÕk\ÇÖÖÆÐদæîÝÿØ¿k÷ÏTd#g*‹Åº—ŸèðáàŽ[·nýàÁý-?n8h`‡¤‹¡T©è:%m±êûú<+ÇM©¿ÊLW*"ÏQq•Rå‘™·2õ¢ÞÕ5•·‚•º¨¬ªIZvà£7o/—?3Ï®[s:eéwâeëÌ/ÙÅJv ‹°Xäòõ‚q +bº9…têdffV[[{ëÖÿþ>n×î]5U/üX„°XäöÏ^¼Ù©Ã›ë³UÕüF•Væ ML©UÈœùu_¨ºé<+Ç?Ó7JÏϳrT*jOÿ7ñ“ø³œ|4‚*P+éŽêô*-+;söüÆ›ïÝûO$ÙÙÙ‡…‡q8œc¿ý~â«ãwnß–^„ËåžûûïÖo(**¬®®f³ÙFFF< __¡P˜™™u毿?~ôìÙ3>ŸÏápLMMÃÂ;{yyÉŸJežŸ?ýÈÑ+Wþ©®®611å×¾œ1#±¨¨šªZ©d~ñùõðƒ¨Ñë99¿ìùåtV!$¼sçêêê¥KSRS·B¬­­ý37'™!H¿ž^„x522©?Ÿºd&*•­:WAʆãc>ý´øþUBˆ™ ‹Åb-ùá÷Ü¿zò ‡J¹{¿ü›ûò®~ò¢¬ˆÃÕnmáÞgÜ¿÷BlœÃ¥çùRØÔê¿Î&„µ¶e±X¿ŸÊݶuËíBœý^5Ç>­4\»+{zò÷å%wkù,GW¿•q;ç0™?ÂŽ}ççÍSp+“âÑñCjž%ëÿÌ9úéÄ-m}«áÒ&¥üzíôeÅw!Òe{^QÓ/~ÙÙôï!¦æNjøwÈìÃäé­ð›¸_µ0Iå£T&òó‘YBùùhU nMÚQ¢§§×#BFcrddÏÈÈž2‹dffÙSö$‹ÞYÆî™Jññõõñ} 9:95¾T2ÅÄö“õòòöòòêéë÷™â¹1V“öiÆ|êи'ªB<;%‡J¦x“5imëßí­ÐÂÎ_ÎülŽV×ßI¦X;†X;†ÔY¯±©µçÛë•ÏÑ»£wŸ:‰>¡Ãë¤4XÚöGÉ)›¶®aÂ7©Æo‡õ= ½yóÑ8ª@­šú‰êráÝÀ´`¨‰‚*é4†?QšÏ©bb>A¨›ü·þ%&Öß÷¤~¿ÿqü˯¾’y Þ1tEÌLËG~¶Œû·1‹Ã;i' -¶ë'‚*hftÕœ^½z“–ý¨ñ‚i;ý&k©ÒÈ# 4—Æ_,«¯/Tóæ£qTZÑþDuhQÚµkwéÒ%ñ”ʺxñb»ví˜4 íPÍÂÕÙùÔ™KÝÃUÜ3N_tuvf`>šA¨[ŽêJi×®ÝÅ‹U^–±ùÔ˜ ”âæâœqZÅíÐÍÅYò3£òÑ8ª@­é¨ Ÿƒ=ä`Z>’P/@Yî®.ïd>šA¨[ƒOTÔ M„  šÒ4ôm²-‚*P+ÉŽêgÏê,ø®½«ë«·n9{6ïNÝ×T¼ÛC;u¢>_¸pqýú !¡ô}F%.[vùãùÍV>€æÐ3Ò.!áÕsÜg&žù1-«yË£8U n2;ªSõúa€–ãæM*Šê$s*Q]½zMæT€w›8®Ò ª@­¤;ªS¨ˆŠÍæ^º|©1ùŠDÂËÙÙÉ@=üýûö>r$½¾ ŠŠ¨~=‹z-G€¿ÿæËÿ8ž   ar¦péò¥)“'«ÜËJ$­X¹’Ò˜LÔƒÚ\y¹8ê´¯7WÇæ.ˆŠT³°X,¡P¨Ú²Ï@=Ä›«"P/ …Pª^0‚*P+é'ªK‰D_-™0êó±›»ÐâPÕ›»4CK¨U}Õ%áŒ@êó!¨ukð­8xHC½`>UÀ8ô<?~¼aÆëׯåæÞàóù'Nœ°¶¶n|þj†zÀ|èSj•—w—ê«®6ééé&&­üýýÕ¹^&C½h h©u“ùDuIôž‘ûùùýõ×_„Ôm©çÏŸ'"®€€&B½`>U Vêï¨Îb±¨Ï""¢ÒqðM„zÀ|ª@Ýš­£ºèU: ‰P/˜}ªh€–*P«f|¢:.s€FC½`>U njî¨.™Dpð…zÀ|ª@­šñ‰ê"ôM†zÀ|ª@Ý쨠‰TãÐ{F.‰N?N¹yë&!$ëôéÖ­Z™™™ùᙇ QP/˜A¨•ú;ª¿|ù2aÚ4qúüùó !;w^µjUã× 6¨̇  ÔMÍÕ9Nvvv­@mP/˜A¨•"Õ4‚*P·f{¢:€&C½`>UÀ88xHC½`>U VÍøDu†zÀ|x÷¨ÕQ½¹K@3´TZ5ãÕ4êó!¨uCGu ^0‚*`–âââÊÊJÕ–Õ××o|&ê!Þ\z-„Rõ‚TZÉ器cçÎÆä( ™ €z(öNÔ hQüý³³Ë›»*BPê&ç‰ê"‘°‘™‹D˜Ø~1±ý™€dežRd6ºêŘq XYÛbˆ!“‡?ïÚAˆ¦U¬¤¤$‹ ‘ÿ>«ä !³û›Õ7ëŽ;ÝÝÝbûõWcñà]›síÈ‘ô¸¸aÔ(ÕQ½¨ð¾•õð?3]]]Éë­+ûŸ+S&Of±Xª­H$­X¹rÖ¬Ùt• IeežjÓ¦µ¸v\¸pñ½þ+ ! _LMH „ô‰:põêµäd«Æ×‹Öo*|P@cášHVæ©”åùyyåÇŽö'„ÌL<óãëD¢Œf,’œ®ÀÉJ!ú:\7K£‡¶¢¥ ÔM~Gu‹%ªx^^P€c¼›_/QFxÝR¥©ðœ*`‘ª¤³:uêÔ¸±cýÚûZZð<=Ü'NüüáÇ’SÍÍÚHþ¹¹ºÈ)XIIÉøÏ>sqv²·³ýàƒÁ·nÝ¢ÿËÔ£‘õÂÊÚVœÕš5«üý¨Ï›6moÿÖV–¡BV­ú^ ¨VȬ¬¬•+WH¦¤,[fog[ßüÊΦNò÷’¿!ÐHÓ£´TZ)òDu¥,[öìÙ³áÃãmíìîܹ½þ‡Μ9sêT¦¡¡¡xž¹sçY[[SŸµu´ë˪¶¶vÐÀ%%ÅÉÉs R–§¼×/63ë´¹¹y“ €F“¬Z²t©••UuUõ¾}is’“ËJËgÍRa-YY™›7mšÞ><òÐÁƒ &NìÚ­›‡‡GƒY¥¥¥åä\ßà`xx8!$¨cÇ «W­Jž3‡®ÒÈÑÈzAõ®ojXX¸‹‹ !$¶_¿ž‘›7oš>c‡ÃQ<>¿F[[G:=>~D|ü Ì î€.… ¼ƒ™pùÔÊÑÑÁÑÉYþ<4^þ“Œ¨!Á!!„¢¢¢:³UVV6xÄúíØQKKK*¢"„XYYuîÒ%==]þRtid½P𪠋ŠìPQQQZZz#7wäˆxo/OK ž‡»ÛĉŸ?}òD<çìY³<=Ü333{÷êicm5+1qÆŒéËSRž={F],óõñ&R×õrsrFÄwuq¶´àuè°dñb™ÅÈÍɉ6ÌÉÑÁÆÚ*::êܹ³ªÿp¦Èþè"yZ¡¥ ÔM=OT—éì™3„OOÉÄè¨>/^¼ÐÓÓëÑ£GRrr»v²/MÞ¼yÓÝÝ]2ÅÃÝãä‰555::2NÐèEcKUïÞ}êœoHÊÏÏçr¹FFFWþùÇÓËkX\œ‰±É½ü{+W¬?üÈ‘7'ååÏfΘ>ÿ»®®®5ÕÕFÆÆB`÷îÝ™™Y„·îñåÊ•+±1}=<<–,YʳàåÝÉ˽‘+]€œœëÑQQ¾¾íW¯Yk``šºmà€éGµoßžRŸà?iÒdÕ.P*«¾ýƒüßT¦é-Uª@c<~üxÆ ׯ_ËͽÁçóOœ8!îë ˆÒÒÒ¤¤Ù>>¾½{÷¦RŒÇŸÐ18ØÀÀàêÕ+«¾ÿ>ªOŸ?OeòxU=º‹?GFö\¾|!„Ïç¿rå¡C øüZjêíÛ·ÅA•žž^PÇŽ –ŸÏ¯9{öì¤É“©ˆªþÙø§OŸž0a‚¸˜ÅbõîÝgã¦Ô¨­­íÃG\ic`ÿÐ,ÐR •;ª·÷ó;}ú4!$55õüù¿ëëG%ϯ‰~ýúµý:8ÔÏ8::fg_–9ÕÔÔ¤¼ü­‡ü–—•±X,EÊÐH¬òûTý°~½µ–¶¶½½]›6¯ýõ×_Ø¿ÿÛo§w 244,/+‹ŽŽª®©/ehh¨ø#IËËŸ ‚¶mhÖ-//¯­­]½zõÚµëĉB¡@å§<ÐEþþè‚–*%8::POTW›­Jƒ?ŸÏÿxäÇŸ?¿go𝝝ü™_¾ÔwpwwÏÎΖL¹qó†½};ù§ÝMMÁz!¿¥Ê×·=u÷Ÿ¤½{öLøüó1cÇR£7reôRœ‰‰1—Ë}üø‘üÙŒ9ÎØ±ã†ÅÅ5fuMAÎþè¢é-U¸ûÔ­¨ð¾ü¾ê ßÍ$’=›tV/_¾ütô¨ÌÌS?íÚ$=Ur433óÞ½ÿ;tYª>}¢ Ïœù‹-,,ÌÊÌŒŠŽRø{4J#ë…²ÏT …µµµFFFâ”ÿnp)mÚÚZ™“´µu:uê”––VUU%/°°°Ó§³\Þ¦TùO©ýÐwÿЬÁ‹zÔÑC‘ËÓ¦%¤§§ÇÇxRòäð¡CT¢ƒ£ƒ··!$~øpKKËö~íuuõ®]»ºuËkk뱯ÏË322>:dÍÚµ"„ 4híºµcÇŒ™>}†¾¾~Êò##ã >oä—PP#ë…ü–*il6»K×®ÛSS£¢¢mmm>´mëÖ—rsw¯ªªÚ²åG??mmO//É©IÉsbúF÷‹ùlüxÏâ¿»w¯ç\_°`aLæÌÛ7:ºÿ÷>þøcKKËòòg—/_ E3 !A'Nš4cÆLÅ¿Ž äï ‰hzK‚*`œ†{J‰ ª.]¼HIMÝ–šºMœ8jÔ¨…‹B:…vÚ—¶/-moMM Çûàƒ!_ó¸C‰H(â®iii¥¥íKœ931q&ŸÏ Ù°a#îûµid½Páí«V­þbêÔ.õµµ;oÙ²µoßhù‹DEEÅÇX¸`Aii©……ÅÕk×%§úúú¦=ºpÁÂ/§M«¬¬´µµ:ôCéL¼¼¼ÿ8~bñâE3¦Ï(++55måïï?zôhñ×BŠoBTœüý4ô©`®¬ÓÉ™:q⤉'Õ7µGDDqÉÉssóÖ¯§­pjT_KÕèÑ£ßÄ+oãñx;úI2E²F$Ï™#ý:‡³,%eYJŠ8å‹„„/Ä£ÞÞ>;vî”^WÙ\\\6nÜ$³T¶vvu*f‘¿€&¢é-UèSŒÓ`ß‘Â}ªÞ¬šþžZh!4½O‚*`ŽÔeŽºs6wÁšP#ë…¦« …Ðôè—ÿ@cˆD¢ÇBþ½õ/!äô_§[·jeffæ÷ú}-‚õBÓUÐB OÍêkszùòe´iâÑùóçB:wî¼jÕ*5•  ù4²^({÷@³Ðô>Uª€qê;xp8œ:ß”??À»¤‘õhMo©BŸ*`œ†ûŽÔ×¥àÝÕÈz>U 4=úGPðîÓôc´šý#¨ÆAK€4´TAK éÑ?‚*`UÒY/4ýX-„¦Gÿª€qTHkd½Ðôc´šý#¨x÷iú± ZMþTã ¥ @Zª %ÐôèA0Kqqq…ªôõõ›»øM¢ñõ¢ðAWaˆ!󇚋•””d4òßÂg•|!dv³úfݱc§»»[l¿þj,¼ rs®9’7¬Nº•õð?3]]]Éë­‹Åb_–õCÅøû›˜˜4&ujÓ¦µ¸v\¸pñ½þ+ ! _LMH „ô‰:põêµÃ‡ú¢^@‹’²~mU#¥ö\þÆ¡ñ2GmÚ´árµÄ}Svþ´S$}‘@i°ÃÊoÇŽZZZR!ÄÊʪs—.éééû®Šjºz!‰Åbv¨¨¨(--½‘›;rD¼·—§¥ÏÃÝmâÄÏŸ>y"žsö¬Yžî™™™½{õ´±¶š•˜8cÆôå))Ïž=£.&úúx©ëz¹99#⇻º8[Zð‚:tX²x±ÌbäæäÄ æäè`cmuîÜY•~³FQjÿ@AK0ígäÕÕÕ ¸øñÊ•+ut´ãâ†SéçÏóôôÜ»wï÷+WÚÚÚ~úé˜qŸ}&ó6«›7oº»»K¦x¸{œ—Ë522ºòÏ?ž^^ÃââLŒMîåß[¹bÅðøáG޼9‘(/6sÆôùß-puu­©®626 »wïÎÌÌ"„p¸u/W®\‰éëáá±dÉRž/ïN^î\éää\ŽŠòõm¿zÍZƒÔÔm H?z¬}ûö„‚üü€ÿI“&«vRqò÷òCh±TÆ8sæì¾}û.]ºX\\lbbÒ¥K—©S§¶mÛ¶Á@õOoÓÆl÷îŸ=<<¨ô‡>|ø0eÙÒY³“lllìß?kVb Ÿ?eÊéLJK˼}|$SLLME"QYYÇ£ãû¨B…záìì\' ¨ªªª¨¨¨®®JÛ›vâÄñ¨¨(ˆÈȈÈHj†ŽÁÁ>>¾ÃÃnäæzxzR‰|~ÍÒ¥Ë:‹ó126f³Ù¶vv2×›4{VÛ¶¼ƒ‡ëêêBBB:Éœ-9)‰ÇãíMK£NW:wîÜ«gdʲ¥ÛR·B‹ÅápØœ&¿Ì"ÿ ýUÀ@õ‘¯[·îùóçƒ ¶²±þï¿»©ÛRÿ¾páàò3\ºdiù³ò‡EüñÇ!C>øeÏÞ   BˆP(|ñâŦM›©#GXXX~AþêUßOœ8Q©Þ$j@{½Ñ£»øsddÏåËWBø|þ÷+W:t°  €Ï¯¥¦Þ¾}[Téééuì¨à*øüš³gÏNš<™Š¨êŸúôé &ˆ€Y,VïÞ}6nÚHÚÚÚ>|ôXñ¯¦2ì@ª€qê;x$%%98´z¸¹Ož2åØÑcŸ¡øÐ7&&$$xîœ9‡&„´nÝ://¯s—.â9»uívòĉû÷ ìíÛÕÉÄÔÔ¤¼¼\2¥¼¬ŒÅbáí ´× I?¬_ocm£¥­mooצͫ—•}ýõWöïÿöÛé‚‚ ËËÊ¢££ªkªÅK*þHÒòòg mÛšuËËËkkkW¯^½ví:q¢P(Pù)*Sjÿ@APÃÁ¡äh@` !äÑãGŠç ¥¥åáîqýú5jÔÍÝýâÅ‹„¼9V EBB›%ãÊ‚»»{öÛï_»qó†½};ù§ÝMÍÁ¡ä¨ õ‚âëÛžºûOÒÞ={&|þù˜±c©Ñ¹2ú?)ÎÄĘËå>n¨`ÆÆÆgìØqÃâ⳺ÆSjÿ@ÁÆŒ£à=M.\ „¸¸¸Ê¹Ë©ÎÙmYYÙÅ‹œœ¨Ñ¾}ûBNœ8)žáÄñãfffÖ66ÒYõéUXXxæÌ«GÿfefFEGÑô¥@c½P„P(¬­­522§þõpƒKéhëÔÖÖÊœ¤­­Ó©S§´´´ªª*y9èè„……>åàààò6e¿B#)µ  ¥ G‘Ã@yyùÒ¥K=Ü=ºví"gþ¸aÃlll||}ôõõ ò vìÜñüù³„iÓ¨©‘‘=ÃÃçLžTTô­­íý³Ó4_ IDATû³²²–,]Êf³ !²fíÚB´vÝÚ±cÆLŸ>C__?eyŠ‘‘ñ„ ŸÓô¥@c½P›ÍîÒµëöÔÔ¨¨h[[ÛÇmÛºµÁ¥ÜÜÝ«ªª¶lùÑÏÏ_G[ÛÓËKrjRòœ˜¾Ñýbc>?žÇ³øïîÝë9×,XX'“9sçöŽîßÿ½?þØÒÒ²¼üÙåË—DBÑÌÄDBHAAAP‡À‰“&͘1³1_°Aò÷2!¨Æið`PSS3yò”ʪª›6±Ùl9óGDF¦íݳwªªÖ­[oÚ´ÙßߟšÊb±R·ï˜?oîÒ%KÊËËœœV¯Y;dÈWÅ øù4ZZZiiûgÎLLœÉçóCBB6l؈ûþ@mh¬ ZµjõS§vé®­­Ý18xË–­}ûFË_$***>~Ä JKK-,,®^».9Õ××7ýèÑ… ~9mZee¥­­íСJgâååýÇñ‹/š1}FYY©©i+ÿÑ£G¿š, ¡ É%ÿ ‚*Ð0µµüI“'ÿûïÍ›´³màea£G~³/–ÅÈÈhá¢Å Éxaˆˆâ’'’)æææ?¬_¯B™ššRõB’œ:Âãñvþô“dŠdHž3GúQãgYJʲ”qÊ Ôó3)ÞÞ>;vî”^WÙ\\\6nÜ$³T¶vvu*fÓ‘³ A0Žœ3ìÚÚÚ/¦~ñOvöúõëÝÝÝiL(c¡^0‚*`œú /_¾üòËiçΟ[»n­¯¯/ŽТ ^0‚*ÐóæÏ;y2cÐÀAeOKÿñ•hkgëææ.A€wês ¨Æ©ïTûê•«„½i{÷¦í'2ä›o¿USÉšêó!¨Æ©ïà±wï^™é¸Þ-êó!¨ÆÁÁ@êóá!f4@K0ÎȤ¡^0‚*`<¤¡^0‚*`<¤¡^0úTÐ-UÀ88#†zÀ|ª€YŠ‹‹+++U[V__ŸÞÂ0ê€F@P àï/ó öJå•yŠ®ò0ꀦ@PïšÎ]º6w¢Î@õ4…FŸ ¨¹œ=eòd‹¥Úâ"‘hÅÊ•1±ýè-@óB½Ðª€YX,–P(TmÙ‚‚z À¨TÆ©J~¶anÖfâç$Ï;Û/6ÖÖÆÚÙÉñÓOGÉÉ¡¤¤dügŸ¹8;ÙÛÙ~ðÁà[·nÑðmCc½X³fu€¿õyÓ¦Mæfm¨?k+ËÐN!«V}/T+dVVÖÊ•+$SR–-³·³mpAgS§øáqâ_Fü׫g$5Uò7CK´ÇŽû믿´µµ%/]º4àý÷;¬[÷CeUÕâE‹Þë›ñç)éjkk XRRœœ<ÇÀÀ eyÊ{ýb3³N›››«ëK4•%K—ZYYUWUïÛ—6'9¹¬´,qÖ,òÉÊÊܼiÓäÉSÄ)fæfnnnô•T}&NšUÀ8´?§¢¢â›¯¿š5köŒÓ%Ó/^djjúó/¿P÷œûùù…‡…nÞ¼iÒ¤ÉÒ™¤¥¥åä\ßà`xx8!$¨cÇ «W­Jž3‡ÞÒÈԤϩ wqq!„Äöë×32bóæMÓgÌàp8ŠçÀç×hkëH§ÇLjA[AÕ(((Hrô»ïæ³X¬¡C†6Wy@#àò0í—ÿ¾ûn¾•µõ°¸¸:é—.^ìܹ‹ø)>®®®¿>,3“ߎµ´´¤"*Bˆ••Uç.]ÒÓÓéøÆ £½^ÈÄb±;TTT”––ÞÈÍ9"ÞÛËÓÒ‚çáî6qâçOŸ<Ï9{Ö,O÷ÌÌÌÞ½zÚX[ÍJLœ1cúò””gÏžQWÊ|}¼‰Ôu½ÜœœñÃ]]œ--xA:,Y¼Xf1rsr↠srt°±¶ŠŽŽ:wî¬J¿m„Báž_~ édgoß¼%†CK0½gäÿüóÏÖ-[ÿøãé›§ø|¾¶Î[uttnܸ!3Ÿ›7oº»»K¦x¸{œ—Ë522ºòÏ?ž^^ÃââLŒMîåß[¹bÅðøáG޼9‘(/6sÆôùß-puu­©®626 »wïÎÌÌ"„p¸u/W®\‰éëáá±dÉRž/ïN^î\éää\ŽŠòõm¿zÍZƒÔÔm H?z¬}ûö„‚üü€ÿI“&«vR5§OŸ¾ÿþW_}-N‘ÿB‹…  4ÆåË—×®]sóæ¿¥¥¥úúúíÛûŽ?! @Î"à‹©S?õ‰§——ôT—ìË—E"o•––Þ¹s‡ÏçWWWëêêÖ™¹´´ÌÛÇG2ÅÄÔT$•••ñx¼F9©P/œëUUUÕÕUi{ÓNœ8¥££ùª_vÇà`ßÎáa7rs=<=©D>¿féÒeƒƒÅù³Ùl[;;™ëMš=«m[ÞÁC‡©úÒIælÉII<ooZuºÒ¹sç^=#S–-Ý–ºBX,‡Ãæ¨õ2Ëî]»ôõõû½÷ž8Eú7 ª€ê;#¿ÿ¾‰‰éèQ£[·iý¤äÉîŸw9b÷îÝžõeõÃëJJŠ¿þú™SG=yò¤¤Ù³§LZYYùÕ—_R7=±Ù*> éÐX/¤Eôè.þÙsùò„>ŸÿýÊ•‡,((àók©©·oßUzzzA;*¸ >¿æìÙ³“&O–>cy{6þéÓ§'L˜ nf±X½{÷Ù¸i#5jkkûðÑcÅ¿Zã½xñâ×_ÇÆÆÊ¼…@‚*`œú111111âѨè¨=z?žÇ³øïîÝë9×,XX'“9sçöŽîßÿ½?þØÒÒ²¼üÙåË—DBÑÌÄDBHAAAP‡À‰“&͘1³1_PA»wï‰D~ø¡ÖïUÀ8ò‰3Ÿ¿xöøqñîÝ?7î‡õëýÚ·WmEFFF·nÝJKÛËç×:»8Ïÿ#?~S ¡P ˆ/£hii¥¥íKœ931q&ŸÏ Ù°a#îûµQ[½[µjõS§vé®­­Ý18xË–­}ûFË_$***>~Ä JKK-,,®^».9Õ××7ýèÑ… ~9mZee¥­­íС2‚//ï?ŽŸX¼xÑŒé3ÊÊJMM[ùûû· ‹D@(PñMˆJ‰D?ÿüspHH»vjX¼T†qq}Õó#""2¶_ìŠå+¶nÝ¢à²÷òßz³¬½}»Cõ<ê“Ò#"¢¸ä‰dй¹ùë×+Y^uP¹^Œ=zt=W±y<ÞΟ~’L‘¬ÉsæH¿N€Ãá,KIY–’"Nù"!á‹„ñ¨··ÏŽ;¥×Ug6—7É,•­]ŠÙtX,ÖåËÙ Ïð‚*`/[p¹g'çÿ½IïÃB˜ õ€ùTãÔw0’/#{öìÙÕ«W]]]pð€–õ€ùTƘ4i²¥¥¥»‡›žž^у¢}ö½xñ|ÌØ1Í].€æ„zÀª€qê;à ;šžžž~¤ººÚÔÄÄÏßñ¢Å^^^8#‡–õ€ùTãÔw0:dÈÐ!CœàƒzÀ|ª€qp<†zÀ|j}Ñ7À» -UÀ88#†zÀ|ª€qpð†zÀ|ª€qpð†zÀ|èS@´TãàŒ@êó!¨f)..®¬¬TmY}}}z À¨A0H€¿¿Ì7Ø+•CVæ)ºÊÀ¨šA0H``@``@#3±´²¡¥0j›s­ÁyP/ ¥IY¾½¹‹ "tT ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚* ¨ ‚*p›»„âèäxóæ¿NNNϪ´´´ñ™¨Ç‘#éŽNŽõMíiG?!C%h~yyåÔÆ¯qT#„vêD9r$½¹  VŽNŽÔÆ/SBB!äãùj,@óëiGmüA0Eh§NrŽ.-SBB€†]Z ô© Zª 9•—?»pábs—€YJ˪g&žiîR€ÒTA³‰2X¬îÍ] ‰2!¨’¨z¡”ª^ߟu­éJï$9÷7iPUPÔ ¥DP…û³@5òïox7(wù÷gŠ ï7whƒ»ÿh€   ayywóòîb£-p‡  @!––––V6Åh á‘ stt<Æ`£-gÇJJJ²ùoá³J¾€2»¿Ys @$(!„èëpÝ,^تDK•ä¹Kaa¡ÝÌ™3“’’¦Nºnݺ¢¢¢V­ZQSçÍ›·páÂ/^Ôù\Ç´iÓV¯^íââ²råJêêjÉù97jÔ¨'Ož$&&–––üúë¯Ôâëׯ·³³?~¼‹‹KZZÚ† öìÙ3hР§OŸÎž={Û¶mW¯^%„p¹\ooo--­¤¤$ ‹G?~|öìÙmÚ´Q¤TW®\ HHH044ܰaÃΜ9H©/gùKI¯ÈÒÒÒÂÂb̘1‹/¦ óèÑ#kkëE‹%$$ÈÏ-===&&æƒ>7n\qqñôéÓ+**Ä¿PŠ ï_ºtùrvvƒsøû*ñòM/ÿíØ±C  :”2tèÐ+VüüóÏãÆS6Ÿššš~ø!,,LæÔ9s槦¦R£ÞÞÞîîî’3¼xñâû￈ˆ „ôêÕëÔ©STPÕºuk6›Ý®];jÎÊÊÊœœœ-[¶¼÷Þ{TJ¿~ý/ÕW_}eiiùûï¿ëêêBzôèѱcÇyóæíß¿_NÎr–ªoEƒþé§Ÿ.\Èf³ !»ví"„|ôÑG æ–œœìçç·k×.‹Eqqqñ÷÷oà×hy¨ˆ*qæÌçœ;o!Dñ¸JÅŽêÛ¶móõõ¥Bœàà`‡mÛ¶©¾¾~hh¨ÌIUUU/^ìß¿¿8ÅÕÕÕÓÓSrCCÃ=zˆG½½½-'gùKÕ÷õãããyòÄÐÐPµ ííí·nÝJ¹qãÆæÍ›¿ùæ++«áÇ+R‡3eÊ”Q£F)žóàÁƒå/%‹ÅŠ‹‹[±bźuë¶oßnhhøþûï7Xê·*//—L,//Wù·x·©35Hé–*>Ÿ¿k×®àààŒ·ùùùmß¾]þe5¥èééuèÐáÀâ”[·nåää(¸¸ŽŽNmm­ÌIK—.ÕÓÓ»~ýº"YéêêvëÖ-##ÃÙÙÙýmrrV|©:†þâÅ‹}ûöíܹsÀ€úúú –ú­Ž?.Τ¨¨HÁoÐÕi”²±±i†–ª#GŽ//¯1y*ÝRµmÛ6##£Áƒ×IÿðÃõôôTë®^Ÿ¾}ûîÚµëüùó={öœ1cFRR’“““‰‰‰"˾÷Þ{cÆŒILL ŽŽŽ655µ±±Y¶lY¯^½bcc/]ºôóÏ?÷ìÙSÁ’´oßþÂ… ÖÖÖS¦L‰ˆˆøôÓOÿùçŸ.]ºBää,g)ù†þàÁkkkÉU~nÑÑÑ»wï^¿~½±±q·nÝ "^V( ¡P¨à÷x·‰›£¨ˆŠâèèØ˜–*%þÙìÏØ}úô©Ýܹs§NÚ¼% 5gNrâÌ™OŸ>%„Èì“sóæMêCyyùŽ;?]oßhÕþ©~/^¼˜5kVDD„™™Ù½{÷-Zd`` yE @TC”̾Ú*÷agtPÅåroݺµcÇŽ'Ož˜˜˜tëÖí§Ÿ~’~:€Ršâî?FUºººxË Ð®ÅUMA ñðOv÷îݦÈ-UЂøûïØ¹Sñ™ÏAh¶çÏŸ?|øÅb9;;WTThiiikk7w¡ %R=¨:þ|JJJVVVII‰±±qPPÐèÑ£HcáÄNž<ù÷ßóÍ7â”yóæ-\¸ðÅ‹M½¢iÓ¦íØ±ãáÇt­"##cÙ²eçÎ{þü¹MllìôéÓÛ¶mKWþAòß'ý›+îÛo¿]ºtéË—/y<ÞÇíííÇG½Õ»‰¶Ðt—³³gÎTpæ¹óæ(8³Š}ªV¯^š““3eÊ”;v,X°ÀÄÄdÈ!Mô„“'O.\¸P2¥mÛ¶žžžjX½RRRzôèQRR2oÞ¼Ÿ~ú)...55Õßßÿßÿmº•2œÊ¿ù½{÷.\8oÞ¼çÏŸß»wbooOwàT©e»^©ÒRuöìÙÉ“'¿ÿþû»wïær_åðé§Ÿ^½zµ¶¶VzþššÉ—ûÒb̘1cÆŒ¡7ϦvæÌ™/¿ürðàÁ»wï¦^ =pàÀøøøŽ;2äòåË ¾+Zqüå›âG#*êÕ«—¡¡!•‚§š€‚dF,¤ÊQ|ñâÅ:::6lGT__ßÀÀ@BÈ´iÓ,,,Nœ8¬§§—@ÍpõêÕ~ýú™ššêéé………eee‰—½víÚ€¬¬¬´´´x<ÞÇ\RRBMš2eÊüùóËËËY,‹Å²±±!„Ì›7O|(¥V—••¢««kii¹`ÁÉ‚ýòË/ºººÞÞÞû÷Œ‰‰‘þ^2W$.^×®]õôô¤3—ó¥êX²d‰¶¶öºuë$ƒ'''§™3g^¹r娱cTJ\\\‡$ìÖ­[ÿþýY£ô/ðàA‹uéÒ%É ###묢¾Å\ã;wú÷ïߪU+.—Ëãñ¢££Ÿ?®È· ÈüÍëËSÒ‡~Ø»woBHXX˜¡„¹sçÊøé•ù7@KðRÊæ©JKÕÉ“'»téÒºuk9ó”••M™2eåÊ•ÕÕÕ„+W®„……ü¿½;kâè>!! à<@9T”;  TÄ*‡x ""àEUðÁå”Ç*—¢xPäµZÔ¢ÚúØzŸG« T-ˆm=Aä×ûǶḵ٠Q߸›Ù™ùÍìn†ÙÍîTTT²³³'Ož|õêUlVSScbb²xñâ~ýú=~ü8!!aÆŒW®\Amذ¡³³óÀ·oßF ŒäðâÂÃÃwîÜ9räÈãÇ/_¾ÜÀÀÀÓÓ!tæÌooo__ßÌÌÌÚÚÚèèèúúz ×GÅôæÍooï+V¬_¿¾  `ݺuxæÄAñëîîÆÚMø5;3gÎ\µjUII‰‹‹‹ÄÆ—X¢@Ëkii ><+++;;KðÛo¿•””à‹=ì8yyùÜÜÜ!C†<{ö¬¸¸˜ÇãIŒ'²ÍÉä™’’2mÚ4ÿo¾ù¦M[[›àsó>fª(ªuuu‰“µµµeffÚÚÚâk"##‡zþüyEEE„££ã¸qãâããOž<‰š6mÚ´iÓ°”¶¶¶æææFFFwîÜ1660`€šššœœœŽŽAq{÷î511A-[¶,'''??÷lÞ¼yüøñÄR‰|%5BH\AMMM;wîtrrBM™2åòåËxæÄAñklllll1b„p¹ZZZ4­ººZ|s¾%±Dá– LJJJMMe³Ù¡ììl6›=oÞ<‘ùSê¸æææ»wïîß¿ßÃÃK<}út2Qà„ÛœdžšššZZZ!¼CétºÈRÈw€ÏÁ'öœ*%%¥ &à‹<¯´´4""ûVCÑh4ww÷¯¿þO˜˜˜ŸŸ_UU…OKÜ¿ßØØ˜LqýúõÃFT]]ݧOŸ"„ZZZnÞ¼¹uëVü£‘#GR½Ã]EEÅÑÑ_422Â2—%díJ¦D–G-]ºtóæÍyyyË–-ãñx¹¹¹¾¾¾ÊÊÊ"‹ ÔqJJJcƌٴiSSS“½½½‘‘F£·ˆ È6OÙv€OÝû{Nå{ªÔÔÔTUU%ñØl6ÿwa}}}{{û¶mÛùÄÅÅÕÖÖb ‚‚‚RRR–,YR\\|ëÖ­’’„vù‰ û©åää°K¡ ]]]‡ÿSE‰”••ùca0Xæƒâ§ªªÊf³E¶[uuuww76ïBŒL‰-v½B(==½ªªjþüù—/_ùˆs™(//øðaLLŒ»»;Œ¨ôîLöImmíGõ$Ͼ?¨b0<8|øpmm­šššƒƒÃ7ß|#üXó‚Á`½ïRV®\ùÃ?X[[§§§¿ï²€O>¨200ÀWŽ1¢²²Rê<ûþ JQQñ3ÏnqqñÇ®л`ƒ*á 8ÒåÙ÷U°A•ȧ,I}; ªðÙéETøtÁ  @`P ïcP%Í»ÿ0¥¥¥nnnGAAAOO/44ôùóçü víÚE£Ñøo¡/++8p ©©é‹/°5ÁÁÁ4Mê:Põøñã÷‘­”3UÛ·o?~|||¼ººúíÛ·¿þúëüüü’’’Q£F‰ÜäÚµkS§NÕ××/**0`@ê % sóÃyyä“ÏYš™ª«W¯®^½zΜ9W¯^]¶lÙìÙ³7mÚtãÆ¶¶6///‘oIûñÇÇŒsñâEQàc)»uë=%–f¦jÛ¶mL&3##CNîí˜LOO/:::,,ìܹs¯U.))qww?~üéÓ§UTT¤(@Vb¢£I¦Œ‹'Ÿ-噪îîî’’{{{á7½`o.))á_ùý÷ß»ºº~ñÅgΜ>To½¢|8ÿÊmÛ¶ihhlÚ´‰ÅbEEE l2hРaÆQ­@O`c&mmm᪪ª¤Ë“òLFsttüá‡êêê>:uêBÈÑÑQ }VV–ŸŸßš5kÒÒÒ6 úïÿKµ=ÍE=zôH`ý£G¤ž©’æò_DDDkkkPPÿÓ=zoll1@ïÄåZP-‘—ÿdU2ƒ*€A€ À  @`P 0¨TÈ ªdU2ïþë³~ù¥¬ìÖ­]  ·£úŽ?q`PÕg•ݺµtÉâ]  ·Û³7G&ƒ*¸ü 0¨TÈ ª>/^¾455»Uv+5%uÒ¤I»:oáC;~|êÔiffæ¶l%n˜½güxkìÿ×®_ÏÉÉ!_¨¸ôxeŽ9jbbŠýãr-=®^»ôù¸ÚÛyòòÌ^R™¾äcEu¨§§§§§'ÕmMÍL/]*E>tX&ƒ*kk}2)_Õ×Ó 6›Ô_Y}uê«q!¸ü÷™hjzsãú GÇ·3Ø••• .²´´rttÜ»÷ø«WZ°ÀÏÒÒj„ +ƒÿõðá#ü#l¼¢¢ÜÏ?ÀÒÒÊÕÅ­øB1BèÈ‘£..®\®•@À“š'ü¹U>x°reˆí[KK«~þeee"+³pá"„ÐܹÞ&&¦IÉÉX%Ü,,¸“bbb^Õ× G—”œ¼gÏÞ×MMØÜûäÉÎÄE‹K/ÜJ8fblÒÜÜÜÐÐPZzÉÄÄôÞ½rþK—z{{‹k2üïöíù>ó¹\«)S¾>>íØš«W255;rô˜Ä&¥zôõ‡é‚ÖwJ<ÿt(ÿå-’g „_ççLLKKKww·Ä|þw綃ƒãªððÒÒKø> ’ðúÅ-,¸®.n™xJ‚FãGf秺w‰;X(ŽÄe"ÛÃIú>’ ˜©ú,\¹ò£¼¼¼µ ¶ØÒÒé5×{éÒ¥ÅÅÅ;wîÔÖÖÂæT~úéç   ;[»;w¶´´¤§ïöóóËÏ?®¡¡o»nÝzŸ¯–.=zôXdT”ÏÿæU”WDDDðxm))©k"×ä}“‡%~ðàþ‚þ†††ñ[¶()±Nœ(Xº4ðСCcÆ TÌÔØÄÖÎ622jwú.=]=ee„г¿ž9r欙ª*ìš§Osrrþvð@®@t˾úª«³óôéï Nœ@Ñtâ¢E¦n%OŸþÁ`0TTT&NübÈ!ùùù7nÀ>zòäÉõë×7nØ ®ý%ÒÜܲnͺÀÀ¥ÚÚZ¥¥—RRRh4´`Á|ˆÛ3ä_!tº|||ü@ÎÀºÚºŸ~þ©½½]dh¯Óvü;!a‹ŽŽnqqqL̬8‚М&; 7Zï‰ËÌÔLdŸ666%''GFE1‚×Ú&®ƒø566¥¤¤D­Y££­]t¾(>n ~tðÛ»7'===&:z欙ÂL‹ÅJNNœ?Áî]»CCCjkëÖ¯_go?qž÷\2MJé$èëÖâˆ< %žˆ;”äC çLŒŸŸß›7omm'¬ _5|ØpqY³·!&º°°0,,LUUuê—_ºMw716NÉ¿GÝ»W` ¯½^]]½ºªúÁo•X2‰F)LŠ{—؃…ÊéHl&²=$~É ª> KJ&ØN`2ÿ¾öÑÜܼfÍšñãÇ#„&L°¹yóæùó°x÷®]::Ú;Ò¶3 „‰‰±‹‹ëþ}û×G¯Ç·]»n•¥%BÈÄÔdâDûâ‹¿;} »°Òü¦9vÓ¦?ÿüsèС¡íÛwp8œìì,¬èqãÆùøødgg¥¥¥ TŒÉdäpBƒÔÕ54ÿ>Ø}ag÷…ö3sóÑ£ gÍšUYYi``ÀšššŠŠŠ†oH\´Èô­„jmmmnnnkk={æÜ•ÿ\™4ÉûÔÓÓsß¾}áÊÊÊ¡ù'”••¦¹L×þiii [6y²– ¶¶6;{÷¼yòŒwŽP‚ Z[[ûía\\ܤIXb‡·wË „ÖÞÞ¾víš‘#G!„\\\îÞ½‡'.4‹%Üh½'.&“)²OÛÛy1ÑÑfææâºFX{;oSl¬ÁÈ‘!¯9^'ÿï$~t`º»»“’“ Nlß¾]\#ïù£G†††¤¦n·¶Ÿ{à€œ}ó¦M$›”ÒHÜצ ÄyJ<ÿw(É3†Ι**l??3ss%%¥òò{ûöí_àë—"_Ù‹Åò˜áá1ÃãÅ‹gΞ=Sxöè±cZZZîîîn®®šÃ4ñ”ïìQ©©gßþ} ! ‹·©”Øh”¤¸w‰>X(žŽÄq²=$~É\þëûÚÛÛÿsåŠ#߯W”””Ƈ/êè?{þ!Äãñ~½{×ÙÙ™ñÏ W]]ÝjœÕÍ_nòo‹íÁ!6›Íáp¬­­ñ[UFŒÐE=ñ+÷Æõ_~9?xh4š½½CÙ­ÿŠ«˜pÍ33³fÏžmc3˵š;×!ôûï¿“ ™¸h2­„š;×ÛÚÚÆÞ~RRr²­]ìÆØúÙ³g·óxgΞÁ6üöÔi7W7‹%u ò †=ßIÇyÊ䆆†‡ÿü%J&(EEE=½™GŽ­¬¬ä¿ !š¼<“ÿ¤<ÁÆ/ŽRh½-.aŠŠŠ¦ff „±ÙlìüŽÑÔ†˜Î®î¨¨5…ßfgeñOðW†Ìîçëë;ÁvBPÐÊŸ®þ´uë–~ýûãù7)ùCÐ×ü•]@™óq‡J}ÆHÜ9!dd46<"ÜÉÉÑÆÆzÑ¢E™õõõy‡óB¯ùä©®®àìäÉ“S¦8ŸCpÄÉðpâKA:0SÕ÷]¿v½µµÍÞþí‹¥H£ÑðE:ÞÕÑjl|ÝÕÕÕÀ@þÍöPQ^ο-ÿ§t:ÿ—F§#„º::±ÜÚ;:rs8ðö6Žîî.üÁ°eë֢ƒ‚LÌL••”_76,ðóok“|GbÑÂDV&11aè¡ ¦ü0Mþýàëà8Ù)ÿxׯâââúúº9^sê#1e6›æ _ÿ¡††FJAíÞ½;#=#33«¾¾ŽÃèëë»páB&š²Š2ÿ=%l5U¼8J¡õ¶¸„)++ñïêdüt‹&Gëâ»ñ¥ù͛˗/s-¹Æ&ï\¯á¯ ™ÝF£¹»¹]ùñʨQ£±ùŒÄ&%þÝâû÷^»€<2çâ•úŒ!‘¸s¦0c##--­»¿þŠúõîݾo¯´Þ¾ý?‘›4½nlzÝÔÜÒÂß›ïîQMŽºð¶d<ª{ÁÁB~Ÿ!ÈD†‡ƒ_ d Õ†ÐKþ50¨êûJJJ¹\.™›¨ª²åääêëjùWÖÖש©©IQ.›­B§Ó}çÏÇo:¡Z±3…güçûÎÇ+++Å¥¤Z4ÉÊb¿þ6×ËkѢŷïÜÉÏ?ajjJ|}Ab o^¿noo——ÿûäòª¾!¤¦¦J)( ¸ø8„УGOþßÿ¥¥ý[]}»»›phM¯_óx<ü/¶Ï_ðG>´Þ :]àfb^[b«ÜœÍVIII ŽŒŠJNJ§ø+Cf÷{ù²611ÙÐа¢¢"ïpÞ†Rïóâ÷5Ô{êJ­ÝóóÌ[O:ˆFCéëéÈÍ—ì÷ß/,,<{ö\MMÍØ±c–Ns™ŠÿñÆß¶ªª* ãåË™Pj´îü”Èä°•a‡Jñ¥ ÑPa…ßFH‹%\þë㺻»/]*utt$“˜Éd]¸p¼¿xùòÆõV–VRÍd2---¯ß¸>|øpÝw‘©XWWWGG‡²ÊÛ¾¸¸X|Y ü¿£!.Z8=¥VÂXZZêëëíØ¾ãæÍ›ÄÓTdiïè¸té¾XTtAMMMOÿьĠp#Fè†G„+((üöÛo"Cëèè¸xñ"¾øý÷ßó'.4Fëmq TOõÁƒþü믮®.lñuSÓŠgj.—›‘ž~õ?W###±*#1¢îîîèèõL¦üž=Ù¾¾¾Ûw¤U>x€(îó$÷5É ã¨vqk ¶=9ÿ¼Ö#£ãݯ]»VSScbbŒRVV6·0Çÿa ^75>tØÛÛ{útÂÂ3S§M=uêÛ#GŽøÌ÷ÁGTm+/ÏäZXœ=w®µµU tJÖó_ OGâȶCÉïä$ý3¢3U}Üí;w^¼|)ò1"­ Z¾|ù¿V†xûÌmmiÍÌÌd2™ ¤+}õêp?¿€ÅK–Ìõò4hÐë×Mw~½ÓÝÕò/‰“““o=¾àDã¤IC54Š/\ÈÏÏ—XOO¯µµõØñãcÇŒU`ÊŒIP´púæÖVJ­„™ã57aëVì÷;ÉÈÂb±vìHkllÔÖÖ.-½TXX¸zu„À­Äˆ°=ÿøã6:鬫;¢³£ãÂùâ¶¶6kkk‘íÌb±v~ýuÓ›&]Ýâ‹‹ŠŠŠšp#÷ª¸„«'²;¦8OÎÙ›“‘‘áççW[[›˜”Äô;5aææY™Ë–¯X±-%åÞ½{•!Þý:ôóÏ×öîÝ£ªªrãÆÍȨ¨£G*((ßçI’Ø×+Ü“. nmáô=9ÿP:ctwwcßÐ÷ÜGýøŸ+ú÷Wçp°[à¯^ý)((hË–x‰å††„ O’ľ–Xážtqk §ïáù‡|ëuvv†‡Gà‹ØíῘ¸k÷סÎÎN|F‡×ÒâìÙïÏž=Ëãñ8Ž»»ûŠ+øoÁ`anqéÒ%ⓆðéÑÐÐðС»veÄÇÅ·´¶:ÔÃÃûˆ|£Éjç—ˆÒ鈀lâ/òøGTÂ÷TÑbcc‡XÜÿ£±™×‰Ú8Cô¯@Á'gÏÞœ¥KOŸîáææ¸ôcWGP¯ª˜t•9uútLtÌ©SßJ=ü‡–š’Zx¦°´´”`«ÞZØ>2}-µ^uÐWÛ¶7Ç…}cJL&0¢*üîtá-„’cÔPö_7ra¦ª;}úÔÇ®‚h½ªbT+óèÑã'5Õ»wí¶·wèµÃL_ í“Þú†Ï3꣯¶í§—ðˆJ8 ª ,!!¡ì—_ŒML¢£×}ìºÈX ¤&rD÷T {ödì*H/<"<<"\ܧŸth@q_H7¢¾§ © Áˆ û¯O ƒ*ÈŒ¨žÿþkïïç¼3:H<.|Š,ÌÍ÷ìÍùصz; Qoé–8¢ÂcÌ`* „:::=|ñ 赸\ .×Br:¼‹Ìˆj¨Æ°ÞóïvvvÒ¢6lí7Êåijïè&È „:x- & »ö7HׄÅbiªtÕWœ£+ÍTm}ت¨(¾Ëàsƒ¥XªS‘ÅÈ`*vuuuV—º:Ž—C!ƒõúꆙ*" &kŽ1C^!Ä`*"ÔÍj¸ÓMg!ì×7Ÿ´Êñ^±_^–oyJël‘ÀgŒÁTDÑ:[ä[ž²Ÿ—Ò;ÞL²ÐB1hrr49úõÇ mÍVÃ^ÑšžÃU@‰äTÔMŒõ•”” NÿÝápƒÇáIEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/tutorial1-2-tableview.png000066400000000000000000002540451231437614300257630ustar00rootroot00000000000000‰PNG  IHDR•Ê­zû pHYsaa‰f΀ IDATxœì}w`\ŵ÷™»UeÕ%K²%Ëror‘lcÓ   ¦cZh/„GBz¾ð!¡„ ó( ð€BÀ‚`llÉ6Æ€mT\%K²Uvµ+íîÝ;ß3çÌÜU±$Ë„„ûCX»·Ìœ9sæœßœ™{Å÷Ô&–eY Ëzù÷\fààÀ8pà o0n©ç~õÃ0 Ãp¹\lWý¶D"ñâ››SÒ³ Ë*Ü.׿ZJ8pàÀƒÄâfËî-‘pðüSºÝnV»ýÃ?¾µ%­`R^~Q[—‰[fb ë_-çç€Ñ/Àðˆ-Ë—ôðkŸŸ™Vü…ôþ0À5Ø·²NÒ&ƒ úR¥”‹I}‘í c­¢ƒ”MKcàÒ´¹ÞJÒ‡¸Œi:·„ÞÝŠ"ÛÆŽ¬šH!ôacd vQlŒ†m»,I\j’ɹvÊ/ªhÖC.Eé ‹%_ F¡mËÖs)‰}Œ3Ò“t)ŒkÕ“¶µFSðÀHv€ZŸm€]5IHCvཨÀCF™ é?¨9è^—ávAŠ×ÈMõ´¶î ·Ö-=ež;‹ùR³Y£¶5uwÇ.5hpÝ•‘s‘AG™,£À„®\„VÍS(6£y"^™v#0àœ1¦‡"Žá/;ÕUYj„ãó˜ ÝV(Ê;Ã2ìqà¯b4ì…Ðù¸Æ«4v"%m‘lHÒ-üÚ<5ÓcæqÀŠVHݨš’úO%-Š—cÀ-éç¤Ããøêæ30¨HñÈKrÅô¦G$p\ö‰ªA jLùsnÉŽ”°@NËF­d0VQ ÜæÕ˜êÔczàQv)ZI¶h+ãȼ´îÂP –% íŽJµ”™’<ÒTˆµi!ÒæÆÑìÔŽ6N‘H ýÅ»~.£Šô’õ8˜t²â>(ç6é^,nåH'CÒ‰”¸ÇÒ­ÇÆ ˆ$q)h¡Üâjt1M4w<*­H‡RPŸtŠ#ËN‘Î8p°5ˆI§ ­±ŒƒE¡˜«± ÈÓD 2¢Ü\k“¢0(–°Hnã(z‰Lë5œ¤aÚ—jçÊY¡xšUعº¡?ìHâ‚4Ã1#)³æMˆQÉK•o•“ ИyRoƒ”ƒké5he}ºUTÞ¯M&{)ÆA?CB|h ÅËs‹áýÑhÔU<~VÆØE=‰ÎîÄ¿V¾30eýrhËÑ+ŒRÙ£í¼ÂøE í6G¶qø©] |Hó1¯‹Õì6CQp›j”ç¹Æå¹Fe)Ì`¬+ÊYBi…³K"’? C85 cj˜ÚÄW1V+‹©MÑ“QùšÃ£D’Ú"6Ó4cš \§ *"0-à1[³5ÙeYªÚ¿\«SôR+F õÆ•œªêzr¶ô‘Šcª j#i¨£´Øf·%TÔ,cL&Ȉª¶h¹6Í T2EPZ¦šÃ4K½,© úeݪõ N 43"þ;©(´1¡Ñ":ÇA‰Êt¾"N3B1©8ºUAÒF’2ø*óT…2ÔRR•ÚdZ7‹n’AZ#æÈ¹êf£;ÁQc„8£ÎPç`Œ†‹âiú¿âVä JE$¿6x˜Ð œÃhiŒJJ‚Då¬Ir€>œ†VWŸ³àŒ+‹e˜ V뎈2pZ²\M8 ?FE¹n6âê#Ȩô®SÙ2r0*F£âV²¬D"n™q‹[¢DÆŒÁÞ?r…Œ†!Œ™à xjƨMëÞvÇ»Z±žàý@zWašœr!LåÒAÍ»M–qqš»p€âeÌrIJÌ×´1Ôq´üŒ–›f|,ÝÏ F¬`7ÛÉ9¤ú ?à*Í6г Ôc|²?ìÑ…’“J®$Á†óWЦ±x… ¨*%NÍW«HÄ/œY¸nʹ2K4’¢€¶ȱ:PsNÍ}©ëõÙ-€ (û”D@ú6ltͲŖˆƒ½bžæê€Zε¼ׂ™¼“J€œXe2ålœ‚:6iVèˆ1h€8Çl­™‡pŽ8€’`¡êlj¾²(=‰%[Î(ÿ*I‚ÊÊp2m4ˆ“^Èé3B9WlKÝÚïϱ÷ç­ˆ4iñ]Š/SúÄGy¢s*S(›ÌhÁJ'gd: «QÜ&ã0(í0¨¤-ã89àrJ3´ñ<=¿‚w MR—© Ô¶ iH³%.ÏmMæ‡$ÿçêÂdý‹s–Œ8Ä×e٘Ǵi=‚²1­‡E«(-Åäà”I>$Lºé³’\2 {‘âÌÂ!(gèõ¤v4w-G4%}¶¤1*J¦kŒŠ8]/F…9J™îÒú²ÞÃdTœ<ê¹£âý2*- n%ftr¡|QffºÏír™‰DG¨§¾)´½9ârû˜qè-Ú#RÈHáp„é1yšÏïjq Ý%¬!“ªÿðd L(Líœ?slÉ„\yõÉcƦÀoïÛѦã àç”g¦¹àΗ¾<'¯¢,w½Òpý©¥©¾~¥oîˆýæ/;¨ñ¢c‹¨¨ƒ¡ø0.d°§¬&W8Ÿ<&}ny`tŽ/3Íã2 I|Ú^ùÁÁΈ ¸¼C¼Jz:©>׉3s¦—¦g¤¸#ÑÄŽÆÈÊ#¦W)œhóŽH‚|¨t;Œ@$Ê÷%ø°i%`L¶QYêÚyÐj8hi¼J/PúzL^3Œ’L9WDàÐÐ@äÉZâ])gjÞÉÉåb0 @–‰ Þeèê¤ â’­µI5¹ƒÅZ1!@_“¨ÆXl5^‡Á0‹æ¨¸üF]ÆP_äë‘ÔáöÑcÄ 5v%9ª}³”‚Ë +o£ ”f1Ÿ„ 3ÆQë½ÈÍ÷Ro ßVL ´Ø€É@ÏLõ2píhô+ CõJU»¹}™ÄV®Ò›­ÈŸ0¿Ó›B‰ÉAµ­N©ø @D 4"دD³åÅ2r ˆ  ÄD€i«¾¸å •U£aÐ’6^ŒH ‡šœÉÈÔY†Íx“è”R©N§S§«U*Õoÿ †2“,X3×dVL‹«d°6H¹29š@dçA$Ód—³®ä$¸Ú¼íùž’¡_F6îGÔ•¶s £â¶&©¥‰¾U?«~:£ê?G¥3*èVÔ ê‰„™é‰8·ˆ¹<ážDs‡iñ¸Á˜×íQ^0½,ööææ éq¹ÜGº‘Âa câæ)CôOB·³Aà¹_.ÛÛPû#™¯¾ß±nÕÊ.ÞÔf èÇË R£ÚÕÚÝÖ5\fó¯Æ<Í‘sÆ`á¤ÌYe¼ ¯ÇÅ Æ2ÓÜU2¿yZi ÅmZ¸W€1¯Ç¸vIÉ1S³³Ó<.ƒRÜ•ã3®?µ43ÍCi)L{à₊ Œ²áâ1d ³\ÇŒwÍ(r-(w7ù–½‰±9FVŠÁ…S €Qšœ7N›¸Z+Áð‡þ‹Ü<ÍÆ@zVÀð­ñ"e\º":k‰qÏ@s ý 嵄c×"¡üIF«yqrÖÀ¤¿¶eN”Hz›¨ÚGû´MªÄ’¡Œfã”ïTù1I&ÄaFR†+=SM #‰Gÿb†€È`qYšèzéÞ §'÷@ \r°8É(÷1ÍÓ â¦´(s]²xƸpãœI˳ .ë#˜3BPg€¸]ÿ~~ú¸Ë b±r’ëâ‚s,ŠEgØ78 [&ù9<5Šóy¸¼ÄÁBKÌ»žapäËD™”çá¢O91šÚŒn k˜ ŠÑ'P6Ï’A {WÔEçNd‹š©?æ¤ЧA²qÊÖ’È«ìml¯ (›`DZÈfqÖC\ŒÓ„JÝÈRÁ"é8f‡Å¶B»H$;§ïäĤVQ1œ $¢*ɯ¦¤c‡QA/FÅy:åuô<˜Æ¨xoFÅpÃ1*Άʨp„:˜A0*ËJdy{N­*&\­±–ÎX[(ÞÑe¶…â-±ÖÎXÔtŸZ5&Ó³¬~·H!#…Æ2S®Å‹§žŠ&ÿÀßžmëß}ùws&uýüʺÂÌŽ÷–ì úŠRƒ™9}^0?vj¶a°œtÏ»Ÿ´SRlñŒœ1¹~Xµµ}ïÁžƒ]ñmûÂì 5uDw·ö|°3´©!´uw׬²€øÉ¿7njmjmÝÓÕ6pæØÀ¨,¼·­£;ÖGÃyÁ ý™"àe={þZÝúúÆÖmû Óü^Ãç1Ì„U·¿[N0ú 3_2;ozi:¬ÙÖþûÕMsRqšÏcd¥y¶ì ÉÈ ÔTNJmÇrà ÕÃfqUïJìïH4w´b£©“Ǭ4ר׮¼&¶Èk3\ÁÕpŒƒ¶O\:GéÒ¤02Òú8rÒ€<]Í—¥›¥pË€áR×9¨–i< =nnœ@ê©ÕNQñ@Î ôE:`´$£…5q'±uÚ¨rŠ™Ê™. &6IfÒ<]!äì#Š£8P n5å+º¦§.Ä’ÁHj°ˆ¸¨%$ÁŸ6À°È°ódê…¢¾6Q·Ë®}!ÂÄ4ª¤}$û}ƒ‰‹v½þ%¹ðdýÙd#}1Ê!QcìÖº5¢˜ÒzÍܹØ8Åh9TU.MD}æ2 ¢©744Q?²û1„¿dc‘ÐSžLËÒES‘â°Rc R©°'0+ÜhÃp¹ûQM1èK’…£4B·¸`­•ŠõcCìÞDº ÀÁÈiŒ“+…½vÕ¥ÔkÚg† …Q†^Y"“ %Óé+A¥\£–ä&*™É×Ô‚\G'©Ø&¢±LÎÒ…rts²3*E÷Q¡Ï âz(pn%z§V÷$ŒÎp¼;f1†ÁÄcOpËâ.—1~TêÇ;nëUìˆ2RÐ…ù´¶þO¿ÿ]AQi #KÓÒ´ó/Ï?”;ª$™=€0nƒü®®ÆÍCΪíÙ¶þÙÛ—@óÎ]ËîZ¶pÓµ§ú¾zçÔ}{»û[ŒÆ­÷vUŒ ø<ÆÔ1éî €ÁØÌ±’-mÙ€ÅÓshI®nDÜëuØl ¥ÃбÿúrInÀ›ê38‡`·Y×ÁÅ5Ò|®“*r§–¤3€mû¯nh ÷ôM3‹s|‹gäŒ+HIõ¹ºcVmSä­ÅÄÙü ï)sòÊ RR}®D‚wDâûÛ£¯nh uc*sËØ‹ïíÇXWO÷êÛΜW¹/åP¤ ‹TcsÇgm¼±é iZk¶µ35;+Í=­$-Åëꎙ4n(¥ÄqéHxiÛåP”ÉZCVW”Ÿ4Õï×™“ ]›÷%&¸3R Ø‘žàÚ¢èÄ x1¸9®,pPÏíÏ“8r¥67¦æˆ@›·J±AzPK°6%DR)”nò."-A„€#_ÑÂê °É# ƒœÊ0©|†¶oL.¬pÙnb[Ò¡‹sÊ5!»"wˆþK$œzJ®2XvŠ&4Wî/‰`·¨& QRùèì- žèI]LÝ(ÃÞopÎE ±Ô$›ã> bR6ÆU“‘LÚII&ÁA‘Hê9^$S¨CdÏ{S.Þ«pEC„mâØ «Ã(¬Ò´ét6 2¨*°¸²BÆ êpÙ¯x6ß–íµ±(•Àžzµìáab TÓËxhe˜%34*µÌ'5…+ÚÉ\Jó7r( €Ì"GCÀé& _"•IòÂH~Ždë)3šç¨~eJÃB)àŒó]¢‘vc‘Š–Ä\%¨TÊ ‡9ö2Xl¼JPqÌôSŸhœÑáj×#h *ÝQö³ä‡³-ÀP—*år• ²›7u.à½R>T³ôkvg=L3>µÐíñúƒ¡X<ÁÝ®>îŒ'x$šÈ ø¦Œòlk‹{<¾Á²}Ëûyö¾¯^òí‰3æ ¦‘‚.Ìê7^xÿïüdãêëúHAñØ–} ÿ⺮΃)~ßè«oŒ0CÞ]_2e¾ø°§ ö@dêvïÅ+kö¥ pצú ø ÒN0¡(5Íç€íû‘èÐ’{%yþ±ù)ë ›f‚g§yª&d^÷å;¹w/9¾xNy†ßcø<Ƭ²À׿4Æeôa“G§]jiÅØ@šßŽ&R}®Ùã7œ^Z˜åƒ±«N3£4=Ýïêê6{â‰ü ï̱@Š}×CÞ¼;f¡¿äà÷È+ÛºâÝààgÀ²RÝé~´wÅÍ„ôëÍQQ{q®Oz ±î'Sàj?‡‚©ð35äÐG¾_ŸXWgrÆÜ.H˜ü@—•›†SJ¥ãÂ@¦¦r´á‡’ñ*¥Á”ëÖVmüÆ•|ÂE¡Êh:ʼnZ`Ä[-Hj9Š…Ê«— yAJTp\Ã8Ê00s$’ºç‘M#º£36òþ4Õ×BP“9¤@ºjM}¤&¥O žÀ<—4PêsàÜÂ(ˆezèâz S‰0Špι…­á©‚‹õ`˜ÒãRfÙCtˆ®Jj½ýDŸ?}A”«‚hLëoZ^•©M!3^mÉžâ`q´1$Ð[ªyP«ð;&–8Z¢Ð–\”“¦ÓwÍwÆz8J-ÄgŠU÷bÔZ2-Q‘fx\‘298u?h°Jqb)ÛeT“ÚŒ#c:Ú2OvÐÏ0”×Hû—ö¤RBX³¶Œ@wH‘,°œ+Ú†‡éNKàš‹@ŽD’J7 ”Ìpš˜9’~FJ&𬨍JP>o+ݻЈJpqŨ”zT‚J1*ŽK~\]ÇìK~BÛ’¹X.¿ØVž¢î¤¼ûàÀy<Ú]V”[qÓê<Ø´âÅ:4º]Lüt´î]ñâÁ¶Æ¸iEãÖØ¢Œx´;y†d/DÜØÞ²ûOËÿ'ÜÑüÒc·t´î*d¤`æ„Ó/,)-7xü÷÷Ý\ÿñúgî»Ù fiÙøN¿` ™TUn_–oB/™ãÉðG¸kGc$MÀ”Ñi>Úþ*Úq5x|¼·ëžWwþìùÚ;^n¸åÿjÿñQ;d§yf”’®lÇïx¹áWªßÕÒ Å9¾¤}]`0öµ£F¹ Œ˜w½ÒpûKõ÷ýugÜä>ñ•ª|ÈI÷d¦ºàÏë[nÿcý/^ªÿù uÏþ³©«÷K(¤‹`4ãÀ³ÓlG&¦&ÕU”ãËHucðµ…£®:yÌó „啦0ÆB=¦Øƒ¶xFÎ׎:nzöè\ßÖÝ]ÝfeŒ3°äP㙾kO)Ió¹ÌfUc{$!\7°Ä>aöéXL,ÛØW·¥Ë“CËâbÁŠsÎ,¼4nñºƒÖ˜,×ÄWA€™VmÏ.u-ç>f¼«;ÎrèˆBF*º*&C Nà|]F_‹Ñ{ Á°pHZèŸ-¾‚£×)C=ˆPÇ1¶Q(S§¤ ¥('Ã"³€Ó´Ÿƒ\š’¢jÜILâ¥þ…Ø€*y„Ë7 q¡)²+K©‚s ðê5š\ꆒdÏÔ4‘I³(ÖÈðg·méÈ9n!Áf R­Dú*S P6–3 =3`«EVK›Ð3îâ¥6Z2€E*’&§‘-ÎpC;àÌ¢Ϲ0]}šN= ª½*ƒÀ dQÄ¿8ÆrÛÅúéó®—(+50ñ@?d4š“adùBx¡yñFKÙ0iùœƒFž€Îs‘9Á¡açBׂ!Éâ…qª\·$³ÜL›¸3Ç$2eNvËÐðÈÞ¸j2?Ùÿ–hšà`’d8‘jÀ¹…\Šsà oT†$†¨”*NE Ò°•OîYäpû"ÇR®EJƒIפ¹’CêGw5¹:`qÀPÔ0ò,\Ê93 ¸¥qe™£ÅÞW:Ç|Åä#-ò^.¹E£Fäâ>½Ì˜ìJ¡¢dò0" ãÒC‹L—ÉÈÂ1jPgqÔ@©&õ?ø‹[¦ÇYÂân[°øÌÑcJÝÌ|í™_í«ßòÚ3¿òÖè1e Åíb ‹ÇãÜ4ã·(¤«}?ÝxÉ ?§»:š(d¤~’„q»XAј³.ûvvvVfF ;+ëìKoÊ/-N ,ŒÀöT…v­¯­^sá¶3á¥M5Á²`Éåð·Ç ¼´3ËŸ1ðíëƒ 'gÀìqѸå÷ðáî™àߘpù £§ŒNë}ÊÓkq·÷³q»Uª?ùM éx$â¤Øb0æ÷‘hâÕ -§Wæg¦ºçMȧBÝæò·÷5µGû•`\~êe'§x]=që©wö54w—s!J…ˆ\o8*Åó{ †ã\¨B= t§"!¬‚cjÊÅ€uDà“fsb¾;;ÅU”ilÙg¾³#îb`YL0ëŒT6:ËH$dâŸq ÝK åÅR<‰©>ì©zÊ[à~ 1-¹iÐrE@ó_à@yl†Çµ¼6Çæâ´Z*"îò‚c°4B“ãÅÉ·È¿a„=‚»kTm¨g¦'-ÔN30¡Ck*Ì& ê [«Ž ©ŽÉ¯rΊ{1€B†²-ŽÙŽ@ù9]¥õp\M@±æ¬0ù¼ÃK)ë%´$XÎÚeúBÍí¦û¤­hÖ¯©ºai ±ÍF Z™¤OE£(›H#†Ðÿ”§BîŒ:RÃUŽ5iÎLìþA•26ëÕ4 Ò`¥­XhròÁW4ɶãíˆå$ô2ñ¥¯èíEµdP÷3n3<êFÐ͘ãî9aÚCbÒðq°ˆá(Ç ¦xe 2“ÇäõD/h`‚ÊZéòsU&ЊVAH%snŒÚÍÐ?ªk4ÿ!¬™¥Uk¨ 4“#“K8ÉÑ××tóÃë[£P q¤.ÓªàJ6ñe¯pÔÒ©ÌKãÍ”á¤2ìQ™êÒvrÂjw·vRý^·‘›_tî5?[ñÜ=ÝáÎ5¯>æuñ¬1¥§\ðŒìè‰Y:¢€k7ýR¿e~c.¸së{U',í¯‘B’0Ðy°©fås™t1`7¼õlNþw2sF Ð"ƒ%U¡]ëÌ]vu*Ôì†ÊR¸åtøáŸöî‡Yg@Ù´y ¿6zàvè9Šç<ã Sݸ±‰öZ £sý‚Qí=ØóÌêÆŽ°9mLúe'÷yqVš»5‘‹€H¯ê]xdsCè¹w›ú,gݎΚº`Iž??Ã;&×?obf Å}âÌœ?ü£ïë`fiúǹ]¬3b>ñöÞý1møÌm–°,‹C{—ÙÕ“H÷»²Ó*Ã(É6NšìÞ%an !ÄÒÍÙ¨`ð´Ua§V¢ eÓ“^ D#eô¡xœc¾]úHŽîx;Ðetú¨,ï1S²™’-N…ºÍת[Ñ'àb½F­ÄÍ>DÅËSíÔÊL°–—/çÜíb£2ŒÒ\c뾄â@L1”‹á/Š;*³$®ÒÖ1¼’“#ÿ©»IÈÏzâ úHZ”ù";úeW‚ õ»’¾06hÞ^£+ b^ŠãmÄÝdE‚…{ZI8 TË“Àʳ@l,“͵q,“’8ê@i2‰féÙ¬~˜‰Á1N3Ê3`(V´H†rÎò®t©ýÏ”¾PT]fèeºù÷íåãzß§iPªGO‹s¤*FÙ,—–h´,X*ù”À‡é™JÅAM˜^ü ¤å ’BA‹âÊÂûcQ@¤GŒxƒ–Ê|Ðx!"ƒŒ›quzU"²ÚaM¢j©6 Òæ,Þêk|4:ûáRHbð7ÓZ‰î h4@_©)í,µha {\Ñ)•¾’çlܸ•“¼ åá§ŠØöž¾è”&Ñ)àè•I›Ì&cœ¹´G®i{è”TœáòøRëöíñ²½ž,‡‰§¬ Æ”Œ)§ËB=æÁ°Ù ÕîmÏ((Iúë.ý’„ )$ 3jb•ßï=avJz6dfç-:íŠÆºÍY%³)Ì¡I•{ëïZBЬT€_žgüiþÚcž ¤C¿ ¢~p0ßÕÚ3÷•cízâÖò·÷9¯`tޝ4ÏÿÉÞðší]ØçÅ¿_ݸhrÖÔ’tƒÁ'{ÃÞÐÒçäÙ¶/ü»×w/žž3nTJºß•H@sglWkwM],‹¯ÝÞQ’çÏ x2RÜñ„µ¿=º¡.¸åä€OžcõĬ‡Vì9iVîô’4ú35o~p #l¨—SQ¤ — ¢qÈN5|nhïæÑ¸d3¹ÇÅÒ<,'•åºÂQ^ÝÆÉXÂ…×rÈtAÞ£%®dMŠY c¥t%®0µ‚]ã8ò mYÙØ#j`cW"‚I÷ƒÎIm¿Ò`¡”9ÚQ!›Åè™eZ&Þû>(‚%}5Uµ•1Õ?Ç’³f=$q,Ðh–n=5€Ö-änET¥­é¸¦‰7ØrZ |4öl2ß-œ å(c˜ÅÉH”ypôڌ…*Uܯ“lû@o fÿïëFuÓkT4EµPKÚr’F˜¤Ù!gÞ›6%‰¯)s òŠIƒÆ§Þ ´oOR:m £ÂÀ©³(uTÒ/-UEä õ ¸ŠH­Ðsc´€¥D$"EZËÛ Õq%•Ú}.Ì”KºªmjDêÃȈqÄ•…3´éL×IË|²ºsÈÊ0¹ê¦¦‚ŠK‰²í©©$cC'¤ö€“3I§ð„š|&ïÂÒ²SÊK§8š´¢SŒt2"tŠàñ¥¸S2w5Ô›|J<á• 9énÚÐOð¶.³¹3ÚÜÖݸëSoZ¦Ç×Çë–F¤#Ò¢¬”QŽõ§»éý[)ìì Ç6wF›Ûƒ†ÝrË-…ó.ß׋š}¸¨Ð®õæ-Û°ž¯†_/…?5ÎË{C`ì²Sú× ]° @²…ìÓ5´¾qN± ð¹Yv øYªÒ¼ÌçÃ`. ƒhœ7´Úº,À™%曈_`-*°’ŸãX/©ä7ð¸_‰¨“ˆ ˜ G.BìCËüË LŠy$ÍÝ´ßÈÇ(ueÓ'òÔ².½$Í µ9E%©©´ÿXV¤Eí³)õƮ݊Uɺ¾”Sñ*R-UÖ¯Þµú*$ùˆN€‰ðiæÐ T•ö±:$øH¿4î‚V©16 Ú‹A$·¿k¤‰x•íQê@a ·ZS…ŒZ§^{oä¶"´#È;Å«Âé;P¯6"á—£"‰q´¢:”žÔòUL†dœ™2*ŠÆ –®d²ƒÓý~!ƨ\ ­²žÜV-&ªqK….EãR4o’YfÊPiŠ¢ŽS•é§÷)Xå'•áÐEÚúmœô Zg`eª$l—üïO*å”lqÅÖ#ŒhO$ØÖ”ºGìò¦ægxÒü†Û0LË ÷X­Á¸ ïnø4Êü9E>ê‘+äóÓ"ŸÛåÙ¿áÉ2UÍŸ®|Ô2˜WO¿×¶>“5qþP³Sú†r`t„<ŸZÜæêõ«Ï`äî‰Ãþ¸µ?¤®Ókãx¥-~¢{uHï¢{3 ¸8!wQ„Q‰+lz\°“-÷€þW:FLCË]ãÊgHç…a‘N)ÉËGåÄw #Š£-"èý¹Æ•¨=Hv„„Ú3M˜@#§ö‰qtqÒ‘kÒ2Ùƒj®nÆÕÔXNsd¦Ò’L¾‘Géζü@I+CËqpRm÷jÊViõbqT¶(j¼–)º¡ ©3euœþ×c&öŸ–ÅE´^è; eG?é,{ û•Aò‘´z‚SG ] 6Âà˜Ó¢n Š¸NqTmL WLÞRZE=ÉtÆ^Œè< k ${°u32*J#âž!\á%m©åC,NˆGà &&Hÿ» ª"µ‰ˆ¿-Íh_Lb9-žt`.à ˆu…;Ôïnògä² ¼>ÿ-äsÕ"H_¹Ì}p?ÙuCê•ß<²Dñ‹ ´éeo(I¬ü*‘íZ°ÅNá pf€|»³E3ôZÉ2V*2§äJbW4N \ë]QìÁeÀC±+Z£¦ B)ýŒðQ8‡§üC"ÂH‡Ï,Æi]³$" Õ–îÈÅÊ{ˆÚ"¥äº9&ÕèálÎ5z Èðd»‰ ‰[ñ1=¢Y”9@éé0mRϸ…iZ #¬?-Óëó°?"…|®Z$0©ŠŸøÌù/KÿÊ3yÓõ¾# Ía~¤M^íëè€OÖÚ–*0&0ËâÊQèñBÎh¾ŒsS¬XR,:I!öèSþÁ³+!òáÄMQ¦æe-øD`ºˆ¦}L±-Š4´¿I1óåD°0v0U•F°ΪITÕ8XZb‰ö‘Ø8îÑ9 ¤),§ôURŸôÊfáFl9tà`©©å´lY3y©Æ€’ùÈ}9šÅÉþ²5 Tu,³ß¨±1¯äcIèÏ?õZíóÞAªþ¾Ð¨±¹HÞ×/PJAÂDÈô¥:½‘´n æ¤7BÆU=ÿÄã`¡¨dšŽü(í“z›)÷ odÒ Ô< = `"ˆ$e€I†̲ٓ¬Hed¹HõÊHÙˆ9#9[¢Ù—LJá‹ Tg23ô¤¡¬B±©;Ë’i¢ªÒÇ¥/ôŠ”fÂ…H}ŒdG¦¦¨ŸqX‰Ú…KÒ2¿ F³,%Ùáë7r®=‰ƒjù,è”c^ŠÛëõ§gģфå–Å Ãåöy|>·Çg f_ùˆ2R!a$©"^¬cÌ”ù0e;ôuÊÁȃ!ëA7‰³š9ØÜ²äCô6vÌ<+Z`ñ$Pî_eCtv%é”4vÑ1¡·4vÅmÁ¤7»’ JZ¤wãÌ‹²YœèǦžÖ•¹ã }‘x­°ØQN{b”sg`Éé-£%YLÄ'nÉPRá*ˆ©ô”zƒ®p€c‘†´m HG 9i`À,N mò&ÿ`‹q‰Qv 6G6›x ¦%8Ö/­Åz’òwßBºLÝÁIPš£Æe€æÖB)±½ƒ)ØÏèÎ^}½rº7íuú»‚ÙOð¾h“NRé:¼h¬}Qq0”Ê£hv꥘ŒIbN€¹KÆ@'Y5“F,’Ø(âòdçB ª‰Û(ãš} æ¡–ÐD5sÅÒuv¥H¨Ê‡Q œfJ’,‰÷–XÄ%£z¥æÌ“lÆP'1ÚáΈ"¡”G PßÏŸæx@q)™ìÓ$.…‡\ªŸe>]­ÒàDjvenáÐSShD¶Mô\Ž8t:E ?ò¹œ>!óxSd:_hCß[ñY2R¶0tvoTD骤AÙ[ÄuOÂõzm V„!ýHn W®Fk¤MBÎÁHAÝÎmÕ(·S™!ÖzÁž¨´Íülƒ[‡¤4E®LÙ0{av»Ã¬ uÝiÈ+íÁŒ¾1ÿ´>KW(·ÉpNÃì¥$·E39×R³hÜÇ…•©ãÀTÏs ž•ß±+´ êZsæŒÚ$}æÖ’¢ê!-oÀmÚHŽ>jÈhƒJ»Åfð¤‚pLÿ+µÃAªßÓ?ÜBün7Ø{òPVæmŸök·HôùŠ¥!1P/F€Þ¤êGgæf8pàÀ_üâÕ µdR‰$ÿ8pàÀ‡Dò:8pàÀƒaÀ!U8pàÀ#‡T9pàÀŒRåÀ8p0 êz=—èÀ8pà pí_'SåÀ8p0èÏÔ8pàÀ8è¶<•ÊTñÁ«çž{¾¢bVÒÏšw×<úèc %®Ñ? DÕ‹ŸÐÝÝM÷5VTÌzáņWæÕW_óÍëo!“±~Æo^ÃqÇ_YYuÚi§ßqçmmmG¨®Ä믿^Q1«¦¦†Ž<Ø&4¯_öÜóÿWQ1ëÓOkáðzüˆvÁ0‰Dz[8ýœsÎ9Ã+vÄ… úÁæææŠŠYË—/ÿ—ˆäÀ_8ØéÓ0ÿöß¾ÿýü‚|ú:eêÔ¦ýMãÇ—¾x‡D[[ÛþðìUW}ý3¨ëpðôÓÏÜ}÷Ý3g~ó†ës²rv|ºãÙgŸ{sś˗?^VVö¯–n TVV@MÍFñjjªý~[[[CCøqãÄÁ55YYY&Œ€œœìϦ÷?ø|¾_ÿúnúzË-ÿ3vléW\!¾¦§þEr9pàÀƒÏ;†Iª.ZHÁU`éÒ¥K—. ‘Uõ£žzê©ó/8?žþT7<|°yó=÷ܳdÉ’;ï¼Ã0 8éä“Î8óŒ‹.¼ø»ßýÞ /ݱã†nWÌœùû?ü¾¿+ï¹ç7yyy>úˆ×ë€ùóç_tÑE>úȽ÷Þ;¼ªàšk¯yù•Wžxâ¿ýío%zàw¿++û›{ïq»ÝPQ1ó´ÓNÿß'þ÷Ç?ù1<üàÓ'O¹óŽ;Ä¢ÕØ±¥çž{þÔ‘µ+îê ]ÒûTaQ!c¬iÿþÁ”sHyâñØOò“ÙsæÐ-K—.}â‰'¾ûÝ›ÒÒÒà¥_JKK=õ´S‡$?TÉmU53gÌØ¸±ÆãñL›6-++«©©©q_cñèâšššôô´É“'õy{<ûŸ[n™8iœwîy/ÿéå7ß\)VÐÏ0ŒÊʹÖ¯¿îºk-˪®®9ÿüóž}ö¹H$’ššº~ý†iS§¦¥¥õôôÔÖÖÝzë­'œ°X”¹xññCm~غ>KY õ‘ØC2éx<þÃþ`Ò¤ÉpÚi§}ôÑÇ>úØ^èqÓK8pàÀÁ ÃÌTÝö‹Û–?±\ü$Å ñx|Ãú §œ²Døt`Œüâ›6¯^üüüóÏ?ÿ¹çž;xÐö0],ÛúÑG'Ÿ|²Ã@~~þ¼ùóªkª ~ôñÇ'Ÿ|’ç0yò”±cÇQQû„e%yÍ`äñûý³fÏÖïúÚ×¾ÅþöÚßD ¯üùÕ¯œþ•”””¡J8zÌèQ£FÕTo€šê3gz<ž±cÇæääTo¬€ê5³çÌq¹\}Þ£’¥ÓÜ҇ꂻÌ_°ùƒ-±Xlûöí¡PèŠ+®ôx<k6À†õæÍŸ/t2~|ùC?ôÜsÏú駜ثBïÂFVB}$.bùÝwßE§†jÒwîܹôuÑÂ…uµŸŽx8pð…Â0ç 3gÌHÚ¨Þ'‚ÁPÜ4Ÿ|ò©§žRëœ[‰Ä¡YÅÀ¸êª+_zé¥ÇìÒK–éÕY–•“«_™›³í“O ê²,+/×v6??ÄEMOKKKKÛÛ¸·÷©ýMû9ç…EE‡,d0ò¤¥¥;ÈÍÍ9ñ¤/½øÂÏ;÷¼·Þz«½½íÜóΪü•••ï¾û.缦¦fÑÑ‹ÄÁ¹sçÔÔÔ,˜?¿q_ã¹ýïL÷z=úWf0Ë4a]0@÷Àüùóâñئ͛·mÛ6iÒäÜÜœ9sçlذ¡°¨¨­­mþüùâ²xà¡zøáGÚÛÛòòr/¹ä’+®¸"IQÃÀÀâRx#(aÒHlnnÖ’I§¥§é[™ÐÙ†T8pðÅÄ‘Mìé.—ë’‹/>7:`r IDATûœ³G¶äì윋/¾è©§ž9åä%t0##`F{ÛAýʃím™™™BÃ0B]]úÙP¨+5%udEeŒÍŸ?ï½÷ÖvvvŠª ï¼óuÔñÕír%å)bÑ(ÒGžóÏ;ïÊ+¿¾åÃ_|ñ¥Y³fMœ8qx­¨ªª|íµ×¶lÙòɶmß¼A¾GjÏÿßÿUWW. wÁÀÝ&NÌÊÎ^ÿþûÛ>Ù¾`Á<X0þŠ+ =Ïœ92iW\\|ëm·@}}ÃËúÓ½÷Þ—Ÿ_pÆ_ª´IX¼C ¯ãI˜„¡šPW(‹Å(­ÕÚÒ ™™#+•üãÈþ™¯×[UUµ~Ãú’’’qv~á—_~¹ßç}äÑGôêfΘ±råJš‹·8°aý†yUóÀçóMŸ6mÝûëèúÖêêj„¨—_vy,»ýö_ê‹;ûöî{ä±Ç&NœxÌÑLj#ù£ šöï·,K| uuíøôÓÔ§ªªj„ñ¿¹ç7ÕÕÕÃNS¾­jùòåœóY³*ÄÁ9sçîÞ½{ÅŠ~¿ÚŒC-ó]0@÷cl^UÕºµë6nÚ8Á|˜?Á¶mÛß~ûí3gøýþ¤êÊËÇÝôÝ›|>_mmíPEíÅ;¤ð}bd%ì-ðLÈ4Í·ß~›¾¾ñÆ™™™ã' “”;pàÀÁ’T ú…êCÆÍ7ß´s箯_uÕk¯½V]]ýÎ;«îÿíoï»ïþÃ/9##cÙ¥—¾÷ÞZýà7®¿¾¡açßpã?ßýçÊ•+¯»öZ¯×{ù—‹³×}ãº5ï®yæ™gâñXssó~ø#G-T ¨sæÎùÖ·n|ýõ×/»üŠW^~eÕªÕ>úØ…]äq¹î¾û.ZdYròI=ôP(Ú¹sçÍ7ßìv«]JÖçÜóί©©ÉÈÈøò)§ CxqãÆåää¬^ý)S&§¦¦ŠƒâóêÕÿ˜UQ1¼ýËwÁÀÝóæÏûpëÖžžž¹s*…ôÐwÜ vfddΘ>ý ‡üæ¤>±lÙ%Ï>ûlGGY¸ð¨|ð¡‡úö·¾ãñxª*«î¼ó®ââbqöØc޽óÎ;xàÁ{ッpTáeW\zäD½âŠ+¦M›öÔÓOÿúž{ÄÛ&Ožòè£egçÐ5S¦L½õ¶[zè¡'–?QRZríµ×Æ¢±Ã—ç”%'ÿòöÛÏüê™´”3Îq ÇZ _@kù6Ùƒ/Mª ©q/8‰kƒƒc-/ µ|›ìÀÁ’TñÁ½Q]$®…SHBÓ˜a°¤ƒ±¸Å «3€ªŠÑ‡)î<˜÷ì¼…õ”›™•Uê_åÎÈj!œs˲\.×PoL$†a0–ÜÃÃÖò/G"aµ´FzzÌqeYÃ.¤­=ÚçñM›>üÒ‰UÃ.¶?ìÙ»·;ÜM_SÒRJÆŒ@Îãs2rÓZÞ[»véÒóú<Õ¸oÏaÈuñy 8$’Þ¨>¨L•ÀÀ‰kÃ`³gØf]ç=QÓ4-Ó´vïÝŸ•¨ÞRçðªC \—š½>%‹eϼªcgÓÁV»*Og^ÿP‹q¹\»7U×½üB°í Ûãå–F›9×3f<ž•?ªüìsKçTr>2¯ès¾Ì±kwÛ¢E³×¬ùð0Ë™>}¼ø ì<†0óí¿W,¯ª««‹Fc‹OGV­Z][[;a„Ã,ùó0r‡a-<ð`mm-}mim€»îº+/ONB"áÐ{ï­ýóϔœóh4šHXtÄå2|>ßð&!à {6oÞö׿eFÀQ]˜_V6ŒB8pp„€¤ªÿT•ž¦ê¤Êe0Ë`.ß×3oîÔ ?9BÞùλïÈÌÌK˜f² ÷¦êš‡~pxÅ®ZµêU«[[Û,‹[VÂ4ÍŒ@ÚâŸyÆW cÈ/¤hm ·¶E¦åvÁP$7fÂãv™¦‹%L31Õÿè‚E;`O- ­L=PQÛ6Å·üÞ!TÁ9gŒ­{èþ¼ÕqÂq)3ÇXñËâÀ8XÀ-žHpËâÜàœC¤ù@÷­ß^·äÜ£®»a¨Òq8ÖòYÂåò0áp7@6<Ønk‹Lœ?¬çŒL¢ªªâðy•eY-­­ÑîžÎ`(‘0O9eIOO­ªª\³æ½-[¶ff|)þÂQ£§®$|6#÷p¬åwVÕÖÕN/9eg0gžyfqQa,€®PG8úó#&m8©¯«cŒ…ÃÃ`>Ÿ˜1yÒD¿sža7ù@]݇¯þ¥ ›;nܼ‚Q¬»Ûr»÷¾öÆ»õõ¡¢Â…]˜UT4Ô9pà``OU *S5¼4µÁ˜Ëep¤¦úÓR=Ó¦Œ«ÞÒ0<ïüæ¦à]ní}üªEÆi óÇ›ôß7Ü`ÚÉ!p{ìüaT·k×Î{ï{ È>þø“gΜâñDkkÛ‡nYµêígŸ}îûßûneeåÊ<ÐÞ3qR©á2ÆfÿŽc1sT~Æ®;]œåñ®ØÖë ‹A@§Õ¶{ÿ;-U‘?¼{í5K_cl÷Æê¼ÕM=÷«áæ]¶lãÀ™Ûe¸ÝV"nÅM`ÜðxÜ)©.—3`†Á ÃSZ`dgd¿ñâîù‹Jç­QIÁE ‘Š8üdLoöÓÕ¥VÓö5¶†¿´´ >¼2c.—+‘HlÚ$`‡É«jkkóó òrs[6ož0~Â’.(++«­«=fJ0ܱcǤI“†]—޹àp¬å„Å‹ó›{Äg±ü l·ío’¥µìo|iáp¸agÃÄ£ñ”Ô”£Ž:jÏž='//oÍš5‰Db¨b©É¡ææÍ~5µ­­rÊÔ KJY$¡$`tw—Æb¥@Âpízá¥wöì6Ç_tÁùiÙÙ‡,–P<º¤¿åÑâÑËõ¯û¾>øb8øÂâˆ?ýG©q¿ÏåvÅEÙi©þáÍz/¿'_qZÒÁå+[¯ú隃mm©–L@g‡7eCXåX¿aÃã?yÉ%—-]zŽØ›‰Äzz¢™™YãÆ[²äÔ×_ý§ÿïÖË/»ø¼óÎ|±Ñh<Òs» XSóiIaNF€¦¹ÜlxÒ›×S!îµBO"ãcE3Ö½U=$Rõ¯¼8}ñ±áæ]í«Ýó¸¡'‹u§–fŒ+w§ÌH¨kÿ®žöVoz&¸ÝÜ0bí)ÙYñ™åõ¯¼t8¤jDVýzs©gW]]‘X, …Å×ú†fÓtüÄÍ›kG y±•`F"‘X²ä¸`°+ ôÑöÃÒ4EE…0pð`[ïkf"===55uÿþæÃ©«7Fpäö‡aXËc=þøòåÐÒÒúÁ–-ï­] ?ýéOrrràŒ3Ïa ˆètw÷ PTš[ZÞ|ó­éÓ§õwa9<çÀ%%%°ÿþh´ïMu`HM~oùÆ–Ï9õTwQ1Äb Òð¢c´]Á`9@y #ÔþÃYçL¸÷žésæ ^¤>y1ªÆ}_Ÿ‹G/wx•‡Äú1fìí¿¯yûï¶ã;v|zÑÒÅC* êƒ-íòk,—œ_~Ô™ß^÷ƒSXÝ~ˆwƒËœsÃ`œCÜ„+yIðعs×£þïw¿û½N8æƒ>)))òx¼¡P¤§'fšf{{çÞ½M dÝ}÷ÏÆŽ-]°`Á KŽÅÍX@mmmFFF^^Þˆl!èêêŽDº»º"ðñ'{óxâì`°»µ¥eTAé0 dŒ¹\ÂÚp¸»»;:ŒÕáÞ0MÊËÇmßÞ7?+/gš¦eY‰Äíd`ŒìÈíCõ-ÓgL?å”S|9D£Ñp8|Ów¾=®¬,##ó¦ï|û0…)**úê™gz½^Ã0Ün·eY¦iÆb1¡áƒÛjëjãq™ÈŒÇã–5œ ˆƒoòþ×߸ª¸xÓóÏGrrŠÊË'ååõfTÀX‚ó}mm=)Çttu U¤$^¥3*px•CÁÐR8CÍÕ㜌]yÙRH${÷µÄâ&çü¥—^²°Ý‘xÝÀ0€õ»`Ê„‚ާüüîGšÛ Ò70 Ø^0‡âú‰Äý¿ýÝ¥—^qüñG¯][ãñ¸7nÜZ^^æñxâq³½=ØØØ4qâèmÛjKKË/¾øšþèÇo¼þš×;¨}O±˜‹›Â~åÄ٥ʹέöU†¯ºrÀr%º2ÌÑsZ÷vv¶›æ× —ËŠG9pæq[ñžÜI3g|ó—¾¼b0c¡]ÛšÅãÒŠËÓŠËSæm{úÑ®NÃð0—8©GÿàPÖòÆ+fΜA_Ÿzú™ Î??##0a„ÚÚÚ)S¦”••¹Ýn¨¨¨ˆÅbÛ¶mÁLU(Ô÷ƒáM›ë|¾´ªª){÷¶®ZUsܱS§XƸeY‘Ha†aÌ™3SBp¨OÆã1˲ °°°°°P?¥ÿή®.˜8qbOOÏîÝ»GdpÄGî!1Hß²háÂqee+V¬hiiu»Ý99Ù7ÝôqŠ> Ù™YYY™k×®«¬œë÷û'6M3‰twwÇbñ`°3ž^TTÔÔÔär¹ŠŠŠêëë§ÆC69`¾1%ó:;¡³3ôɶ÷B¡D^^騱c}^`Ìâ¼¥³³±üO}öÙ®Œ ð¼ýÖø‰‡!Lï|•ΟˆW9pà``ŒØÓý1À].‹™]án‹s—˰†ûˆÙ0ÀÛߨY¬6RæŽ:mÍ.èî_Ù©öÁCáÝ5k22rÎ=÷«[·nOIñ——··?þ¸®°°(7çÌ™œ››‘‘xãu F~ýÉ'Ÿ¼æškSx,ÅÍ„eµ#ç|¹’sÎ9om A¢;3ôWov:´¹ ê …½ui3;‰îHþ´Ñ Á-nYœ¹]¼ÛÌ,™0ý¿ðå‡vlÜù·§»öÕ»RÒÀ23'Ì{Æ•iÅå“/ùþ'OÞ–àsR›«FväŒ!ù–¢¢¢÷×­]ºô¼’’1´§êð‘™•9göìM›6mÚ´iÞ¼y¦iºÝn–ÅÃáȺuëB]]3RRR8À0^\"pÈ&×ïÛ_‰Â”©ÐÕ`Ç¢ÎNèìlÝöÉ?Âa#˜pÒIy?üaa¾í¹ 3¡oó`•‰C“ªÃyB‡[œsnq‰D{¢1ÓLøý^nÁ°ŸÛß° ‚ïx¾…¿\œ|jŒ=ÝÄzx´;‘–é~ø¯»çL)QXµjuIÉ´xœWRS³5ŒdggVTL¨©Ù'æÍ›š•ˆÅâ›6mcÌsà@[YÙ¤¿þíoƒ&Uf<žvõlØÒ`š ÓLÄMËL$ò­ÇÍØÝÙâií‹¿5Û¡žht;a8Ëp¹x"^þµ«üc‚¯ÿðÑŸuw˜¼ô¿J¾råÇß²ïý•ÝmMÓ®º%}̤ыNݹúeWJªxpèÕ) ÉZÌ„i&LˆÇl{çL™ýgqn&¬„ÅMÓ²þ?{ç×ÔõðóÞË$ aO‘½÷ª{¶÷ª{Ôª]ZµîY[7ÚjµV[­uo­?÷DEq£‚² ëå½÷ûãA +$€¸î÷ÃGóî8÷Ü“›¼“sÇ£‹ç•8PFÜ**ä^:(ó.?   $ ù9ùP¤ŒùYTnºÚٗñ©o.4­¹œœ|wwñáÃg;ujîééöàÁ“  ossIxx M3R©ˆ$©sçn¿zUÀåòîÞ½†aD~¾±‹ØH•R¥!µIR¤–"µC…8\å˜;A6 G‘G}嘆QF©|íTiµA˜p2'Ej,Ýý,}€¦žܬVò-m̬í@he#°¶+|õâʼnQS¥~ü[g(†Ú¸e?ZšfhF«Õ5((Y"àó=<<ÒÒÒär9»¦*11ÑÜÜÜÃÃ#11±zñªÂÄóÚÜ'\sGW7x™ôT&sèÒ¥irrÆÙ³±ãÆvçó¹ÕÌBQ  bâ”É­ö®@’ÔˆTéÃFShšNIIaSªãW½J†»—#è–Ÿj1.ûÉÜøÏbÛ2©Ø.uòO´™¤ÚŸÜʨr´\¾r¥a£pW7w>—Ã!pÇ1 4$eï\_«¥´MR4©¥´­¥hy‘*óERê‹j†[p·³³ˆˆ¸tñÇ ÝʪÔ©YÙÙ:v°··¯É‚¹*»üçñó×rBî¿æÚ퇗­lGå4 ððö K¨çò,öºo×®laZ¥’;vçγ§O“ÍšyNþ2°qòÐùUz)¥–X!¼ÙÝj5¥!µ 08Žq9‚À)Šf¦SZ,-€LÔhiÐR ¡€¤0ºd¢O­a ‹h’œ1&ÍYh4$Ÿ/HHxVTTЪUD½zõnÝzܸ±¿H$dÐh¨Ó§¯çæ* ‚xôèFffªHdCSÆÊ×”†¤hš yZ.­¥(µZkwœ]Ј4 '¦¦ËÌýÄ÷U‘B­ÖÐ4Cj)•RC’$—Ë•H„Fµ„­%EÎî ›Üû×åIxRK†Tßò†!„bù‹'š¼W"[g‘“<ó0PÃH•I£E«¥X§!;;G—¨V©Ù¥*2™œ}!‹år¹\.wr*ž¬u~FêÑïü}lüy å&Aú©ø^Q¡þ¸w£æû÷ŸS©T'ô˜tXi†!I+¡ÂÛmõvjµTe‘¡Ê|ë °k€ÒÓÓïB1cAžEr|ßM‡ájœË=e±mäËùÉ/¤n5ý¤ÚŸÜ 1r´|Ö§dT?©HÈçñyÇò ¹re¡R­Pi ê…ºP©Q¨4OR³®Ûùêñ•j«„a˜­­m›¶­9Æçó}}}q×j©”””÷~þyo[[›š¬;4¦Ëw2´æ)•&Y¡| iåüÈœ·åÙ]÷=Ç:óTÍ‚øº¸<ù÷ßzÖÖOwî¼ôßò† J¬;ÿüS‹æÍÍÌ̪­›ýy@äQ!&aÔcjª±û=”™¢ݬŽcƒëBVÕ£@j€‚$ZH 4ð9 AÈ @$©ñA, €"Mp(ЦiŠ ð„„¤W¯^yyyr¹< à ‚`opZ-óêÕËôôD¹<Ç1š¦ŒŸ¡S«I IJÄ‚N-)ŠV©4ÊB™@vIde `DEyéÒ¡gx+ IÅüyT¡PªÕêÜ\9[ÔRF9U Ã0 †1˜P ©¡i'J‹c@à€ã8—CÓ”–Ôðpçp0‚]3Ÿ À”Ñ GGGÝŽ*} ŠŠG7mÚÄÔ©­ªðÙÎí?ÉËÊâ=?OZ:Òæ!Ï9áRðý3ÒÛ ÝÝ]{ôèðý÷... ~ÿÛÛÛ»V<*Ö¯BQ%ÕL ¸ÖÅ„Tj-M38ޱñ8`ãr‚fh€j~?ªA£+0 hÐÒ@Ò Õ‚šp¹uæiö‹§Àìuf’Ò£‰Åùðû¤é&A©Õjg8ìåËt@0hÐgææ"FKÓŒX,øôÓ6[¶¤ß¾&ò'I-©5ö¸H’ÔjHН¥´ZJ¡P©T$¥Ê—bIæP´0'/»ÀÌE¥Ò)‹ŠJ…²š±†Æ¸\Ъ€àó93ÀpŒ XÃcŽÀq_Èáñ¦1‚ƒ\†®éš*}ª-:_ª ° Â¹¹¹ìùC`iiiꂪûiíq늿ñV-ºÉ³â¸ _Šy $ºÚÝi¨º¢ñbèäýj¾„çÒãŠM®×‹b—šõœ*ô°Ù]$Iq88M›0øÙ½ŸjµÊÀî}Ýïv¢Šm4­ì'×Ü>‰Æ¶-ÃäùDJ²Ùµ‹¬G%Ÿ¸@ÕésÍÔä“[%•GG‡¾}úܹ{oæ·_Íž»ÀÙÑžÏå°³à ÃZš¤è\¹òÙ“„ÿükØà-š5±µµ«¹>‰¤oß>¿þºiäÈÇŽ›4ib­xTúTÖå ‹‡Oï m] 'S*5O%XZRjM yÜæŸ~FCQ”@ À0,·z”c7iÙòécF7®‰Jh¡:QCjy÷ŸZóz Ža@M3gS`@KãÕ]m\ R S` -YPÅÎâÖ^V^ Šp‘'“8*eðUø6mZß„Ô\’••aoï R)ƒ‚ûõë)ñµZf×®CEEªQ£ú›™ ÆŒÊçó?df&ÊÏωŒý¨V“$I)ꤤìÌÌ’ÔÚG Ôе©b1f´ÙØØ¼zUé©•R“››KÓt@`@u–¨'þË•jr­FvLê­L¦„ -Í$]#={š©_j Þo”†I9½š¾šk]ÅÝ4­eŒahv¹z™\ý]|>—a8Z­ ^2ÃÐ*•š]%]IëŨT*­VË®î7fy{Š?¹ævXä$þªo°|9ëQ)¾ø^Ó­/QãO®a Œ‰DÒ¬YS¾€áüÏ—/_ óòôÀq ŒaЦæˆù IDAT ŠÔɉOSžÞjóºuéäáá.‹jE+‰DÂáàìSÆkÝ£2ÐåaÝ?Iýíï“ÿSöMÌÝšrÅæ„Ï•ŠÕùY}[º±ex<^¡ŠþëBÎßÿÅž¿ñ¯Ýí ‚ù×b†$wëß«§©Ê¤½LurvrQ€ 0•ZÞý‡•hÀ”½”<Á·ÚË] Õ@ir¬ ÐÒ@jAC¿ PSPDVÇChۮ͡CÿÙÚ:‰DâÞ½»ˆD†={Žž={†$5|>wذh:´ÿDz³ónwëjì‰çR«Rk´ Ž3ff’d8…ršöž/E¦'Ýþ_?0šËņ&5$©!ÙÅãŽñù|>ß('˜a€¡(Ž™8?%!ÿÙ«M=»¹ÿçu¡œT€V­ÔÉ¥®¾õÚ÷UÆ3­FE%5?OÁÔ½¢Užô£…¹¹¹¹DR e¬ùêÜlw(~(ÞË3¿„ÔÓ’/ þŽ»sýg@’|;€š#Ƽ?Qß=>ݦ×S›`'þ0ŒÁ0¬ü$`ù]Œ)'Ñ4­R©IRc û¯F£’gïè¿0Ý'O~Œ¥§³ÈŠømUvüœ‘˜CÍ>¹bäh±´´lÞ¬™Ë/¿D÷ë5`ÈWÓ¾3ñqÃ1œaàyFî™Cƒ"så/¿¸{¸s9ÕÜÂY7Óe ‰xÅÔQ_®[ÁmzòÊÞ?2sÌ[Šë7æWŸæµ eŽÞ’ý}:þä¥ý–—êÐÍ>wörêÐíÄáK6FÿÀÓa :…¦ÿSyS»ÿ0 0 †,ž¤`ï(8VÍI…¶øxt¹0ZçTQ e_S€Añv5Œé?¬»vé²sçÎŒŒKK›£GOGFv=qâü±cG¸\ǹÇâpˆ~ý>Ý»÷@NN®\^”™™2nÜ6#…«ÕdA¡‚Gà……*™LA’Z~~V¡•¦Àž]Mº–î–Ï‘h •Z-Ååâ ©Ñ’\†ˆ…|>Ç1ãïÇ Cc8†óÏn•ºúнÃ}Ÿ|zgê…C™ñ—H…Ü®A3×.CyR;å«ç7ÏpÍÌc(šaj:¿cüh1—š7oÞŒ]6Äáp‚àóù‡ÃáöÅöí;ª·X¤PöÍùKënä½"äC¯€¿zÇß#r­öz'(¤ÓbÅ”Ûdíƒ Îç]Ò³Ÿ—xšÔŒ‹»«ÛЧOhhû¢ü®@­–6iM¡á¡à8®ÕjYÖÊîÄ•Ü óYŠ¡9X¾‚H¹iÑ«kþc´X5øäV†‘£E xxzlˆYðÐá¨ÞÝÿÙw+T©§d®ünè°Áý;ušìáéAԆʠT*«.d FvYA(#»7iÖ±¨@-¿yrß•}¿)î´rX³ë„¥ô²{uë¾ÖöVz‡tîäÒ”ƒsà\òöô4IGUY° @˜Äö_í¢,¤˜" TY)94 44 Zš.ž Äù /"j"+hY&fmZA|óõ×3gÍmÛö³‡Ÿýü󯩩‰4MSÅãGŽì{øðþ«WY*uéÒÉ… ç•Y5lH¥ZQ¤¤¹’ÔREÓTa¡æÊ¹›fÄí§r ¥™3MjÙ•ïÆP”–¦´N˜™™ö4: 0Œà _¥<Û³Æ+ê+‰OX@}ߢôdµBnfi'²¯W÷*õô_E»âª¿‹ÀÄÑÒ 0=bÑðÁ扩KÔY^ÛȲŽömø„+Àûô¥8‰ùÜ=r*ΫŠ(*•)°aîÖãÖíÙáÞ~¾/’ÒLrª*;=E½ü®@ö4)ã·5PZÊðÃgØET wT•.Ýø^èÐ÷¨ÔÓ–hÝ|Í>í„)4ÄÝxé§]s÷aŒçFbühÁ0ŒC¾¾>M³"0 [¹bi÷Ïú¼LÏ<¼ûßÏztnÚ$ÂÃÝSKg˜•áâ¥Kµ(Íø.[céùi3s‰CçÞ¶-»åå'ß½õ"!v£z ëéÖZÈ)õÍ òEf¦››[mi‹T„©¼ñgÿÕ"¿ 6Ÿ²ç.€ÀvÿÙÊ>rqx¥„ê¤K/zJ÷ŠÌD<®iG…„„ 2pÓæ­M›v07·äñ4M‘¤ša´8NüôôÌ‚‚¢K—Nõë×§gÆKV*ÕJ¥(Z«Õ2 MÓ4­Ñª^>Ïrh®‘Ø0Zнr8Ç¡hšÔR<Âä$MÓ Ã`8Îà4‡/Îz|KµùÇz­?•¸™»2 ƒa ÉÏ.z~/ãö9­Fó8‡KiHm‘’aª¹^[‡‘£%)99)9¹Ji}ûDVc‰:Kë±+Žýí,¹wŒE!ŽM¸7êÎfÉ(e®ê?Ð#CîäÏL !Êï Ä0 Çš6Áo5ìnVÏyª âèNÖ£Ò|½”ìÖ—¦Å¿'̺uÂd2ÎÝxÞå ª.Ýk±9“¾[,,,ZµlimmݧOTƒ†Ó^¾8¹çKÏÛÚÚÕÖ:ª2´k÷‰B©ð®ÕÓüìrsÿà-[w5êæfgo)âŠE<‘—­§CKû‚ÆÄE»(¯RKR 3v=8µ/ù¬5õIˈˆê)†‚UDÍy“ÏþÃ*ØIV“™»…ÛøX=}öôY‘BpÌPÑ’¸X$òðôHLþ…ݧO¤³³óœ¹s%;ww?±XÂç iZK’Ú¼¼¼§Oã23Sæü8»wïÏL«T¨ óå^T$'I-Ij5";µWG†qI-EÓÀVK)•je\­(š™|„’¹•­"#›ëj§É“Žs¢¢œŒÇ{׋í]DŽn‡‹£ÌÉPÊ8B Áá.!«²“‹Ò2-|ká ',†GËÐ!ƒSRSªbffVíSÔyfâ6_̘]>+_å(¤ž$_=\?ŒTr ï)Rµ¦œ¼Q5Æì 4ŒR©$I’ *ýê;Uº=€Õ†ü~5÷Ëhí¨¡ÚîQ@3@‡È0ïÙ¹háÒ7áQécäw‹………K=л5ÒÝý þÌ zs wùÓÖ­=Ÿ8þ½íø>Å ›†fA­êÛØXˆ¸bB%häÈ–ÉTæþ›púïÓÇ“cŸ·uh0¯é€à€=¼@Ô’ïkÃU€që×Çl2@3Lñ>p€ Ÿ°‡K2ìÞl“‰Ì¼<½ìíì4¤±Gð¸\‰ÄÜøÝyú4kÖôÀþ}Û·o?|ähn®Œ¦Š¢I-)‘»véŸïìTõÃ4ª7ñW%r~SøÀ9ÿïÓë °ü‡½^>y*°éaÂÜ_•”ßhê/ŠÈÈÏ÷ìÙ[¡ØG-Fªh†31µé0@Ó¯?¹TPpNâKvßnµ?¹UR5/³ò³s+>‰ã½À˜.7ðö^èí 7îÝûç×ÿÎj¯;„‰ƒ[»~tæ9–þ÷ùw.ÖÝ(@…@˜L郪jy÷¯äÁgì—2EÓ 4Íp0œaOþŠÓVgåŽã‰X"1m~*88Dÿ2>þŽñuÁÈ‘#GŽiR‹èÑ£q5zˆD•0 S?¬ÑÕN}-ï&ƒ=…6 i†¢†Æ¦ØÛ%iJC1 EÃhª¢´L^NaaäˆÀÐF5iÝøÑB„¤Z{újŸŽ#÷ÅœêÛ ©ço¨ØcìóVNµ¥«pW`…; ùy…é*•ªŒGUCë~r+£&ϽyíRaA~-*S7T¯Ë4hÜ Ã0WïÄÿ³êä½üûO<îÖxþ—ƒíìlßÐQ jm÷AàßL_TKZÕ&ññwt~•IÕ{ {çn:vRJ“æ‰ûvç?L'.C3¯ÝiF÷/0 PßÊ·¹Ç§}C™´ç¿Bª÷¤È:Fdí0tÓ¶-S¼¹ yJn‘G¿¦ƒ¦Ö–ðÊvÖ")))ºÇüÕ·ûÉ­öh9µǛЧ¨v—1 kÖ0¤YÃ0ñlQg`sæÌq–”­Viiø®Kq 6LýŽß& ò1xTïÀhAÔáhù»Œ@|À,=^.înÍψÝú>íþ« È£ª3>€Ñ‚¨3>ÂÑòvøx0í¬<6pý†TA|` Ñ‚0žp´|„]F >xjùÙ -ãùGËGØeâc@çTq¦Z€04ZÆóŽ–°ËćH©3jÿQY@ !e§ÿnÞ¼õVô@ xßðÖ¿(ëTµjÕ²UA x_ùoï+ý˲N•B¡¨Ce@ >К*@ ˆZ 8ReÔÞ?@ D e|'©B ¨S…@ Q § @ ¢ÐÛý‡U!@O©ÕQ¤ @ ¢6@N@ D-PöðOÃ\¾r%ñYâRñ¡âáéѼY³òéŽNõ yóæu®@ †¸|ù²îµñ?7Á©b=*??_ÓôB|ôB†Y¹j•‡‡{e¼ÜüoÝK­K•¨>FW£VuSƒaMW§1ÄÇFjjÕrÐâý¥PžU½ŠbsÛwYŽ‘”>QÁôHU±j…¨O…@ ï)²,OOÏêÕ}öì™DjûnʨæáÕtªˆÚEªâ}DžŸéåUM¼¼<Ÿ>}fna÷®É)¾®K§ EªµŽœ*x?©ù¯böð®É©6È©B¼eÊ `ÌÒÚ%ÐÒ_´Ç`¯_+Õ´FKË•”•„—ý4#Ï­3UuÆÂï>kêýÇÿ–•[XsÞîvÍyÀÕ[‰ ‰¯ ”9 e¿^áºK­–ÊÍWܾŸºmÏÕŒLYÍ5©Œ·Øå÷‘™“»µmæ C¿Úò2#ÿm«óñRó_Ŭ€wMNµ©¾S¥P(ÂÂÂ*+àííuèÐáêjU üý÷ß›6ý–žž!‰bcc«,?|ø0O°qã†*K^½z5>>~ôèÑFj²t鲃\ºtÉÈòe~X`¶Þ¶ÜúJRƒÆÖ‡ÇTÚJŒ»Ù b ¾ùÌuî^¢àŽÉçˆ Þ#\\méÙ—+0¯¡owû!}šÀóg·ïd˜™;VVƒR’Ã!ìl$Ú„Øü+Ék¨I•Ô}—ßOŠß¦‡—Ö œ»ó…–oW›–šG†°ZŠ0Õ®œjSý5U|>ÕªUºËY³f¹ººŽ9’½‹%5Q«†dddÌŸ?À€þ={~ÊçskWøÕ«WwìØa¼S…0L™ü8%/-‹ƒqpJ«ÅqÃ0Цœ`€¡i€°)2"»€^v0ááó\4{øöàó8jö7ƒaµøE)Ïzª,x%’:U^´øÿÍ›7oÿóÏúõ]Vüü‹­­µ¯}NüK;‚ï¡2F©[—]~ÁJ½®s!ªŽ×Î@}×ä@5—TÕ R…ãx§Nt)óæÍ³³µÕOÑŸ"$I —Ë«^[Õ 55…¦éÞ½?#'+0²$Œ±2õD£ ÓÊ(­ ô0Û1Ù‡Öª†ÀÃJƒ\Œ¡48Áeš £7|Ý+Z޾Lëˆé“º´iêßÌßóiç° ú9yE_LÛ‚ãOWÛè^ƒüœ%AA¡êöýÔí{®é&eZ7õþ¬sCG{©¹XÀ0Ln¾"î~êŸÿ^ÍÖ›äêÜ60ªg#;kÉ‹ô¼­ÿ\Ñá†c˜‡}t¯Æ¾N¿°H}?!mçºY­å3#ƒü rÔ†"…F?¥÷1‹§÷öór(.ùÓOì‹™ËܨèÔY]Ó™™¯2å:ßåÄ™Ûûv›‹q¬À‡ÔeÃÝ òs^6ós îÞJœ³â0tlå?mlGØwüöÆ?ÏWÙÙ™“»µŒð€ióþíÝ¥axC·‚BÕž#·ö¿Ýµ]ƒ¨žl­Å‰)Ùk·œÕõN7Ø~X²¿{‡ Ð.píVrÌŸçdr%û>•1—áwñ†xsk¡R_x•VÁcñì<êû¶2I”©Ra¢¾ ŒÓ¿lõjÕ¦ÀvU²¥ËZ¶lyåʕ訨%K–°é=?n|DDDHHHÿnܸ¡/Äpn.^¼Ø¿ÿðððqcÇ=yú”MŸ>ýûÁƒ‡@dd¤¿¿ÿ¢E‹*¬~üØñîÝ»‡ôèÞýÄñŒ^·?ž4iRëÖ­ƒ‚‚Z´h1}Æô¼Ü\6kÑ¢E7l,((ð÷÷÷÷÷oÛ¶­áò Ãú`pûöí訨ààOÚµÛ²e‹.×pÝ””ç&LhÒ¤Iƒ Z´h1zÌèÂÂÂ*me VcÌX°R|‘Ô2c+u©[öžÌý(æRË”m!ðt8\l™ü[}úÑ@íéf…ÿ멾ÆŸg&µ.Sý½Á¿’·ìÛ±­[Fx™ yeîÃËú®šÕº©·¹¹ ';Ë\Ìo×ÜwÕÜÈzv\¶¢¯§}€#—`ÒÒÒ”J…¤s›€e3ºã d tý¤Á”Qí,¸\½¾Íì)Ý<ݬôGH³F¿ÌéÛ"ÜÓÂ\H¸Ô\ؼ±ç/sú„úIY :âþ[BSªÒ)‹+üÍùäæŽ‚짺 \¾ØÌÜ+°`/órs5*†ÁÖeÃݹ÷øå¶N@Ó0àúù¶Öâ±CÚÀ½{w'Žèöâñ‰*;«³é·cZµŒðâó86Vâ1ƒ[7¶ù䑟8ÚK9ÂÇÃ~öä”*«Ì»0srg/;…¢H(àµmî3gr; TeÞ&cÞô÷&þÇjôP©œ =*x•–h’¶JÊã º»V™]ïªAm:U¥Ò‘Ë /^üÕ”)'Ož6tÃ0=ì?`€\._´hQLLŒƒ½ýˆ#îÝ¿ÏÖ0œ[†K—.;V*µX»vÝÂ…‹^¦½4pÀË—/†ùæëo~úi9Älˆ9uòä¸qãÊW?wþÜÔiS}|ý~ûí× 'þüËÏÏž=¦XÿôŒ _Ÿ¹óænݲõÛo¿½w¤‰lÖ¸qã ‰Nl(MÓALÕ¹0÷)¦w¥iÃpýøjö_ßϘþþæë¯mm¬mm¬ÿwútÙÁÇv³¤¢H$²±¶ô÷vl×ÜŠŠŠŽ9Âá‰8âCê²1Ýù÷Øãø{à‡ï'OÕJdÆS*•£Gâð-]|;MÓÓÓÛ·ï>R©T$ã8®/Ù@y6Q©TN›6­cÇlœÜì 7ôПË娫R©ž>}ºpáÂO>ù„-Ю]»*-i Ö»I™±ËåpHyÖ“øG—ûèìKЦÌ$ù9a”ؚλxïôuÇðœ«ê2¬'ÎÅ«?ô&Sbéóç_¹ëò9.¶íÜPbm)ÇW¯^+’:a8ÃhÞ¼YÚ¸o¼ zÔxààAv¶6n)5cå¸ÕwºvïaD³öæB¸víêž={<~~;Õ.9%Ý­~ñÂjG+ \¼xñÈ‘£ž¡QZŸ›·ÂÃü$ ९&»¯”âæäÕZwéÖ ‡]ýp¨ðå—“¿ü²81))yò—“2³rƒý½Ül?¤.ÓšfVn¹ºzn=©TÚ0H ?̘ñ,19°ÅXœàî¬sˆ®­¥K–—îª{õhr!æ$`Ýub½=«ìBeo7¢†¼ÑµP• ¯0Ý€ÏÀ6Å¡/Ö£bS èc¥Tç”I7VHÅA½à ¤aˆ.…$Éë×c‡ Êåru‰mÚ¶Ýñ×_ ÃÎ-ÓŒF£¹wïÞ˜1c‚`smmmÃ#"bccÙËâ¡â`‰Z­¾ÿþĉu%}}}Ý\]™’K’$7mÚtòäÉ´´4†dk%%%yy{ë,Å”Z1VEy.—Û¦Mk]•Ž;8pðÉÓ'þ~þêòù|OOÏuë×)Â7òòöfßlöª¬Ö;Kí0 xæ¶aQ}Ò×Ú“ÉzöðªÌ£« –œ—m1²»æÉ]™ÂÜÜéEDõƒ´SÑY:>þŽ‹o©µ8:ÕgííííííõËs8ÐæaýãÔ^á!nå „•"[j^¼Ÿ.55UbY߯9rd¤®‚EÉM:=-ÍÜÚÍÚÑäŠâ\©¹YFAFi‡0¬ôZ½Ò³E`8¶_Q–¥¥…­C=¿_¾@banö!u¹ÊîІgf\¾ñ´SÛÈÍÍýçŸ]®]Í$6³§t7ÐY½¨$%%ÖómGð‹wꥤ¤‹o{œSlÁ!U9úA·ÜœG·68Ž©Ùk+kyN¢~o,¤"cºPqÿ5£æ÷ «TNeÂ+)l¨ŠWƒ¶PâQ±¯ ëc,qªJ´¬HO¹\NÓ´µµµ~®­Î)\°`ÁñãÇ'NœÒP$2“ËåƒV©U:_ J»kU–‰ÅGWÅÂÂòóe î»~ýúõë×ÅÄÄ,Ê˵±±4xЈá#ª´U…µÞYתÌ .3U¢ð%Où4Yó0H¿Â;§È¸G i­|ü_áÍDCˆ"ûŽÐ±3Çjþ; a,%–&IRjéÄZ^^¨d÷ìùwì˜1å+yÖ·dï¸qqq#†{ñâE—.]þܾ£8›¢ ‹TìKKKKÈ‚k.è$ÈJ¼ G''žPʰ±³‰ÙÙYßž.ùpñù|Ãp³µy½/½D‚-Pi/Kr/^´vÍšž={­]·ÎÂÂbýÚ•CÇ/UÐØÖå*»CSj.Ϭ¯SÇ6ÁlŠ••ÕÜy öŸ/ÀpÌÛÝÎpgq=_¦i3±NŠ¢øf„žÇƒ18þÚ«rusË¡D8Ž9ÚIÙ”œÜ-©Ð÷å%æ2Ü… »¨!5?ŠÊ©LxÅ…+— wϰ¯ò ªt§†÷SeÚü_…Î £Ÿ^.œ#‹ ‚4pPïÏ{——f8·LŠD"Áq<77G?+'7W*•êù=PÙ²±XŒãxAA~nAA™PȦ>|xذad³ž·|á  N:ÅÎ@VvvìõØÞŸ•u2*„Ïç\½vuÈ!lJVvö³gOíí쀦i­V+‹tåÿûï¿2­“Ú×góTYH­öìÙ³;vd/Oœ8)•J=½¼©Ëâáá1íëi;wí|úôiÏž=Œ´•~-ÅÞ.e* 3±K ñË;”[' í‹Ä„vî‘6ô“G/Š<>³TÄ?RÐaNŽ/™L†C 5UoÝBNšaÖo;?}b>Ÿ¿yãò|Y!Žæ!œ9#ùþ1‘Ùg…J‰X8pà 7Gg7§×ç4b€Ñ ³cßõ ÃÚ ‚ƒ{·edÉ­4¤–Ï+þBÓLÌŸ¾ŸÐ™Ãá¬úy‘®®V«ýzÚ4œ#K/Æ>e—“ÇlØ /PHÍEjµ†Ïçé´}’œÉ0 †a#G9jô¿I¥&+êšÞk‚à>›Ò½c‘X,êÞ½ûÖHÆúCêr•ï {ƒn‡µ³¶Ó4=aü„„Ä”fÍZÕ«ç8o椑_­NHäî,VÚ«*¿¸LJ™K{{û¿~›©»¼wïîÞ½{]{–ê]uʿшZ¡Â¸ÎÃÛ§Ëïݳwòðoؾ|á7½¦ŠUF×:û/«ž}ªM-îþcJ%—¹d†a¾þzZrrò#¿8rôHìØÿýïÌêÕ«W­\eLnÆŸ””4iÒ—.œ?uòäØÑ£y<ÞÐaCKg*TRÇØqc/]¼ôç¶m:##cÆôé\.—Õð&MšìùwÏóçÏI’lïÞýr¹¼¨¨hß¾½}"ûp…Ö¶.AeF£á.¼õÏ·úå¾ÀuéR™„ ³ì<Îý­üŸ½“‡IrÀÞÉ# ´½.% ´½½“‡} ÷Â0µ·¦ªÌ¤X¹92ðööٱ㯠b–-].—ËÌÍ¥ýûE³Å ç–¡iÓ&k×­Û¸aÔ)S¹\n£F–,[æèèÈè/T¯ü¾Þ¢EË¥K–®Y¿rÕ*{{‡¡C‡€ÞJ©yóæÍ›7/22’Ç冄†þôÓŠaÇéVhµmÓ&22rݺu2™ÌÖÖöäÉ“†ËfBáâ%‹—.]úøq‚••åÔiSTe[‰ÄÞÞî?¶¥§§ã8îååµl鲦M›0 cÀVjUï½~Ó”ùY`. õÛY«Ö8D¥¦ÁžcKhµjšp#šÓZµã` I©¹bÿpÉEªêŒŸýoô¨QY/âÀÆ»T8!W!^ÿwÜŒ¹«eÙI¤¦Ã™¥¹uýz^-0 Kz!û~ñÞÄø…ùé‡gåàÛ²ËØÇ7v@=¯–¬œØ‡ÊC£¦&œ×(ó"«Ëq™¿ú1÷Õc¨ †eŠVn¹öbú2Yv²V«äp„æÖõCZ‘XÖcu8›¹cçôO.ê"¡ØöÑ NZâdYv”?Î\Kùk׿© ç5Š|öãPáÆéí{¯-˜?/5á<¸ùwd 8yoíÚuÉ÷O€…­gPËR— ¿ƒÿ]|´}ûö'qû žW+©û“äœN}~ˆ¿ðÃ0–>\þÃ]¾áäƒë弼ώ­–þ싵—ü+ ÃNž{ðÇÖ-Oo¯†½ôƒ[¹¹¹1;bgÍ_“›ñˆah+¿ Á-? ¿#5ûˆJ©p Sƒ° *}ˆ]YŠ#Låä˜$İœ 54,§Ú`sæÌqö,K©$i˜Ñ­ÒNmß¾ÃÏÏ7îö)_}%“½Á‹">òóó÷îÛ×'òó#GŽ4°LndŸ¨ˆŽ“_¤—:òø¥ÔÆÇÝ]U¬dx@'$%9˲k[kñ–™2ªC‹pOˆolë)YUYQ÷$%'5hX —¯ßvws×ä°¯e¹/‹Òþ»|ù2¤§½¨¬Ê¢£rr O[AFìÖº[S…@TH™_õäÙŠ;FùIZ€zPñxñ^ƒ•žÝC‘¦w–Ú8§ê]”SmjõHÂtPd@”aå¦Ó£FÌL{_ô-ñîòFwÿ½E9Õ9Uˆ· úºD åiÐt4ð¶µ@TAÍÏ©ªl-ÔÛ•SmS…xËÔü3€@ ˆºÇÇËëÜå›íZ6®^õ3oøxy½ƒrjB±Sõý‡@ò©â=Å×ÛëÌÅÕ®ûÎÊ1ž7ù˜ÂtÐô@¼¿øùxrªrªoäT!âà:NUVV–B¡¨ºâ£Ç̬ꇘ¢'"#âàĩ2zQUXhèö;ª.‡@@XhheYW®ð›7.ý®{¹bÅ­Sÿ¥Ô•^@”¢c‡úÓ¦Ÿã>sÖåß÷\¨ºNißÉäHUž½zôìej-ÄÇ̃ûw«,ÃzTññU—D âÍ¡ó«ªA-Ÿ¨~øÐAär!ªëQ>Ôóæ­›o´¡Fa†¾÷F[ù0`£ŒÈVÆ€le<ÈVƃle<ÚjÔÈ/nÞ¼eŒÃBC5 ?áÌ©ÿRÞ!§ªÚ•F£¹ÿðMÓ~þB¡°vµB¼/ܼuó«É“ßÜÒu†aV®Zo´•d+ãA¶2d+ãA¶2žÊluîܹ[qq³fάRÂü jE“·©R©UOSµåêX_Èçï:¶ïfÒcp}Æþ,ʘÎ.\¸uëæäÉ_©›©å+äç+V­Zù<%Õ¤’«V­¼w÷î]µZ}ëVœKýú•UüqöìÝ»ÿyððQ…¹çÎëù¹~Š••Õã„'&öãÝÃ0š¦ßðÔÔÔ:håÃÙÊx­ŒÙÊx­Œ§2[éQÀ ·ïØàQCMÞZ¤êö£»_n›“+—-05#-#§°`Bÿ~í>øßŠÍ1Ó¾W¥_uáÂùÍ›6™âT™V¾Bllm|}}M­õ<9yÿ¾}¡¡aááá/^¬‰,óç/pvvf_óø¼š |§¨›;б ƃle<ÈVƃle<ÈVÆSÞVEÕYëµ|¢ºñ‘ª"eQjîˬìÌ5{7öo×w@¯Î{ò×*Q}ÆÞ8›8{å²)ÃG9;:׆RµƒF£æñøC† 2d¨©uÃ#">z 7l¨§ªMÛ¶þþþ5—ƒ@ ćÍuªjçDu‡,³Œ½ 5ì]á8Žé$•ë: i+ÿ¿r×xðÝwßݺuS*•Ž=櫯ÊØ …P(ü 'ÚQ¤ê]ÙÊx­ŒÙÊx­Œ§ÊH•««ëóçÏßPë5uªnÅÅÍœ9«|ú‚ó 8UEåæzØØ}ýµ½psö’WÊW$I‘$õLñl¡ì»ÑÓ>³íþÃÚŸ&ôâÈá”Õó믿¡)jçÎçÏ_¢\cÊŸ={¶¿èöí;ü¹}‡B¡Xºdq÷n]Ïœ=çââÂÖ’Éä3˜±pÑbµJUFfZZZ@`àÀAƒ¤æÒç)ÏW­\9xÈà#GŽVa2#P(£F>â‹)S§>thá‚ùž={½¶g·®] …Bá'Ÿ|2gî\77÷š7úªw d+ãA¶2d+ãA¶2ÃN•‡‡¸ºº&&&¾‰ÖkaMU5кvêäã“ †Í¸j¶g®ÊTe‘$­&II’$¯ÉZüxÎ$Ïo¾ˆì÷Ûþ]]^¶ü¬K·2,--%ææ8ŽXñ]eù%‹yyyý±më´5jÔ(¼q£5«W/[¾œ- Ñ¨úiED“&Êlß¡CûØ×Mš·jÙâáƒþ¦¤ EEE‹/iݺ5´k×îòåK`*ssóñã'D4i"‰âãï¬Y½ºk—.gÏ··¯8¤÷>ò¶œªY³fíÞ½{èСӧO×Oß¾}Ç‚óÙ×ÀÚÚ: À¿Gž:u*)¼s'~ëÖ-7nÜÈÏÏ‹E õíÛ·S§No´#¸zõj||üèÑ£k(‡a…BVé6coo¯C‡#Cºù™BMló믿ÆU²U~øða<ž`ãÆ Õ–ð®le<:[ÅÄİ/tN•·÷ëÇzxx†Ç+Þ(°}ûŽE‹zzz :ÔÙÙ¹°°àÊ•«S¦LY·nmÛ¶íê¼CW¯^ݱcG­ø |>ÕªUºËY³f¹ººŽ9’½‹%º¬ÜPˆºÁÚÚÊËËëíJx_øhmemm••%N•ŸŸ_™ÞÞÞU¼Ñ¾ÚÔŽSµ¨äç)ËŒ’ Avݺþ¿lº¥Ôrõ‘5B®Ùèþƒ¬0‡/œ¿ŠÐR4ES ÍhhÍ/W8Š\fÿºüUN¦™Ø¼æJ–G&“Ñ4mcc«Ÿhkcs7>^w)‹ ,Zúî»o÷ïÛ7}úŒÆááb±X–Ÿß­[W•ºì,a5033Óo—ÃáPZm…%ÃÂÂ<<<âânÕ¼Ñw‡·©:uêTaaá3f,\´èÂ… mÛ¶}]gãÆ0tèпwþ=oî¼eË—ÿ0cÄݾ½hÑÂö:ü¼b…Î!ëÓ§ï˜Ñ£I­ömE/XÍkÞ:Ã08ŽëG’æÍ›ggk«ŸÂ0 2Tm 1 ’Ôp¹ïåàêÙŠíoß¾Q}ûFU*©Z~Þ1­ŒG§gß¾Qëcb Ä©ºÿ~ùµ¾†ÝäÔå¡iúû?èÿébW¬¥ÿ/ËÊ?/‰^¤(* ç1fõ0¯³§¯9ræÄÑKçOÝòæJp ‚ææå/ÿýš¯ŒÁp8D™w]]¹‹#•JqÏÎÎÒOÌÊζ´´4²¹wï3vìè1cÂÂÂ|||Äb±‘k­–úÀ–«3o ­ìÛ¿ßÃýÿ€vvvûöï+U«øK§T•~Ñýš5köÏ®]J¥’a˜Í›6q¹Ü¹sç¡_ÌÛÇ' €}ýøÑ£‰'6kÚ4((¨S§Îk×®Õ»xñbÿþýCBBÂÃÃÇ÷äéS]Ö·ß|Û§O¤¾Ì!C†L˜0}½lé²–-[Þ¸q#:::88¤U«V7þÊf-Z´hã†þþþþþþmÛ¶­-[±¹L¹ôÓPLå¶züèÑøqã#""BBBúpãÆ ]–}Œ¬{åʕ訨%K–°éÇŽëÞ½[ppHÏž=O:5|øð±cÇ2 súôiÿ{÷îëË>|xsÕ •Ùª2å™Jú¦«~üØq¶zîÝO?ÁTI’ú)ŽŽ'O¢išÝm'—ËŸvèàÁ¯¿þ†mâÕ«W—.^0` ‘½&IR"y=ëqèð!ÍP´Z­þÌÔùóçŸ?Oþ¼ôq ï;LGª233¯^¹2fì Ã:wé¼óï]ù2™Ô¼8DZÆ€²ݶmÛ^¹råî½»5¾víZãðÆRsóÊ”¿ÿàÁÁƒ½½½gÍžmkgû<ùy“¶ðåË—ÇŽÛ²e«µk×)•ʵk× 8`Ïž½NNNÅ­3¥m¢÷Ë’F./XºtéŒ3ÜÜÜŽ?>oÞŸ?qÒ$£”ÇñÖmÚü¹m[×®Ý\\\:øÇÖ­•fæð¡Cpïþ=øïô6Ö6öööìø3gÎôï½nýúÈÈ>U¶;dð`GGdž!ðîÝø­[¶8;;3Æ•qèð!Š¢ºvíݺvûsÛŸÇ‹ŽŽ6\‹½—ggfÖs®g ðOË—ÛØØþñÇ|>…5Òe­Y³ÆÍÍmõêUìp êÒ¥ËæÍ›gͪ`_myHR3Þ<__ˆŽŽÞ»gω':uê$•JE1ŽãNÎoù¼·ÓP+V¬°µµÝ´y»˜¬I“&ý¢£7nˆY½za}Œ¬ûãìCÃBuÍŬ ^¼x {éííÝ£Gwö5AQ}£6mÞôí·ßŠD"ØýÏn‘HÔ­{÷ZìoM0 ÆÔ! Ct¹jµúþýû“'OÖ¥¸ºÖ÷ôôÔ$²Od̆˜C‡EGE“$¹oÿþ=z ‚w!RU¥òåû zËàØê'NÔ ôõõusueªzCkq!]€le<:=™’PÕûäTõèÙK±”ñ'ª€­…­J­jjÓŠ<(Y„aÀåp;:t¢•ŒTlèI5A¬øùç?ÿ¬Ÿ­`øüóHÃåÛµk×®]ÅŽæÎ›7wÞ¼2‰S§M›:mûÚÞÞ~Ç_éç² Ê—äp8úYeø¤}{ýÜòíþöÛ&ÝëI“¾œ4éËÊD}Ô±SuÿþýgÏž=J.—³)íڶݾ}{rr²««+è}¡”Q,=- llmÄb±X,N}ù¢2Íe29EQÖ66å °&,-­ô³¬­,=|XÏ/¥m‰ö¯gµ¸¥ýÃ(J«Ë­Àõ1Š%”ó¡t Èd2’$·lÙºuë6½,š¢¨*õ1¦.pÒU—Ëå4M[XXè ´°°ÔuÊÊʪ}ûöÿìÚÕ7êä©“yy¹Q}û¾õ{$«@•Ê—ï/Àk?˜­nmm­ŸkckWõúNi![C™é¿¤¤¤ºl½ä15徫‡ñøºø^L¸Ô\ÝìLÁ9 £ 0 £Ï?¼XOê|éù5Ïúž5W ñÞQÇNÕà·_ûí×ßô 8p`;ÌTì+œ={–Çãùû0 qéÒ¥üü|©TZ¾-‰DÌáp²³²ÊwM"‘à8ž››£Ÿ•“›+•JÙ—iZ£V1qe!–âoV]nåa!ã©PB¿n?zCéš‹ÅA 8¨÷ç½+,`@ŸjÔ‹Å8Žçååé'ÊdyfB¡.%**jĈwâãwÿ³;$$ÄËÛë­ß#u¶ªBùŠîè:ž­^PP _   @¿ï·þF_­ŒA§'L£Ð°í;vY1,44.NVÍæJ­…Ýú>tÐøÂ]BºôréiCÙ40 >L(r¹Ü¡M†ÚÛqm:{µ‰nûA­¿F¼ƒ$yìØ±à  Í¿ÿ®ÿçëëwøða_"»þÙuíÚµ¨è(@Ç'Irî¼¹ÚÒ‡_NCñx¼Æ_½îâââ^š7Q—Ïçœùßë't=þüéÓgúe7nìååùËÏ?߸q£¯njûÀ嫬~õÚU]JVvö³gOkYËwd«wŸZX¨®I‘*©¹´M³6e}<¼}<¼+,øH¨ËHÕ¹óçóóó§M›Ö¸Q#ýܾ}#,XΪ{=%å¹FCfddœ;wîúõëÍ›7›üeñâ†ààào¿ývéÒ¥ÑQQ=zötvr’\¿výÔ©S«V­dfÊԩÆ >lØà!ClmmSSR'<þþûï`Üø ãÇ›4éËþý£UJÕ† x<ÞÐaCYÉ;tؼióú˜õCÉÉÉYºt)‡CèGûË…î] ÉÃÓS¥RíÚµ+ 0ÏåzûøÔÄVeÒÊO CéÛê믧 :ì‹‘_DEEÙÙÙÈ ïÝ»ËÐÌ—“¿¬RÓ똱c&Múò‡~èýyïü|Ùúµk­­¬0×/Ö·oôâÅ‹ÌÍÍ;wêô.Dt:V¾âÉY½ÌØqc'Nœôç¶mÑý¢ssófÍšÅår+ˆ¤–m¾”ï8ÈVÆ£?ýw+.nÖÌ™FVœ¿`€G [¯e§Ê¤5UD…Ô¥SuèÀ‘HÔ±cÇ2véÒuùòŸ<иqc6ô½`ÁBàóùÖÖV~~þ?-ÿ©}‡ö†é*öë×/ àÏínÛ¶M&“‰ÅâÀ ~Z±¢eËV ÃøùùýñÇÖõëc,X R©{õêÅÖmÚ´ÉÚuë6nØ0eÊT.—Û¨Q£%Ë–9::²¹¾¾~óæÍß°!æ÷Í¿×wq5f´Z­14o¥÷õ×¶M›ÈÈÈuëÖÉd2[[Û“'OÖÄV¥“*˜/C†Ò·•··ÏŽmسlér¹\fn. ìß/Ú}L® вe«%K–ÄÄÄ?~Ü¥^½ñã'lÿk»X,Ö/Ö±cÇÅ‹õìÕS ü[D§CÊW>ØØÄ-Z.]²t}Ìú•«VÙÛ; :¤B§¿lëïç”ÖÛ²•þGøGú]­mÌ>°ÚzÄ26g·ða ¯J’€Y=*XèÀ²}û??ßž½>«•†îß=räè Aesrx£­| [Ï;h+™LÖµk×ñãÇ4H—xðСgÏÞ»oŸ»›ÛÛRÌ[U¨üGÈ»c«5«×ì?°ÿôéÓo®‰R™­~ß²eÖÌ™F:UÛw숋óHL”?öÌœuù÷ßcæ ¤§½¨¬âüÃ20ãÞvŒحïP¤ E¹¢z(Š˜˜˜ˆ&R‹ôôô­[¶‚=z°¹III©/RcÖ¯oÓºÍ[ô¨*ðò}êØVr¹üÞÝ»§ÿw:¨AÐj¢¨ËÛÛ\SU‹uu9ý‡0d+ãy[¶""9ùù‘#GØÕÆ-\¼X·5rÉ’%·nÝ þ~ú÷ïλ©Óİòx{¶º};nÚ´¯§N›ú¾¼åõ|*©BÔäT½k [ÏÛ®T¶ï IDAT²—ûöî;,Š£ðï MŠÔ£ˆ HQ@Pl±ƒ ú‰¨Ñˆ%ؤXQ‰±ÇŠ`ƒ&Vª’D±€¢1 Ò„£^ßïÕËI9¸7ïÃsÏíìÌìì0Ü ³³³ ìo©0¼‡ ,ý6y%\øvËûøÑ×§ùç"\޹lÚ£Gó—$)ÖUÌ•+x]ÉNËLºåì:#Ul6;+ûÃ?ï ¦Nvå:hEîÛwàÀþ¼ùbÊÔ©’=¨®„‡êJxâ®+C*5æJL³»¨ë7…êJxh¤ª£i1 «­­M{\u#Û{^Že?SA‘Od2ÙÄXЃ&ž®]W¸S%‘ª+ IE"††›c\íÞþSZ\¦dea¢«­D§sRŠ´Þ¼-´¶ö~ˆ¬A#U²Õ•ðP] Õ•ðP] O~Gªn&Ä?ÍÌlîèà “K9½fŒ×¥³¡‰}x__pù¯¡ŽöC4)Šd‹ @èÓKëÏçZÝiz-.¯²³wïÞõèÑ£ššccŸu?þˆïzðàÁžˆÝ/^¼PTT:tXðÖ­–––MsX²xñ?ÿ¼½{ï¿Åm'{yih¨Ÿûå<l ¾r%ærL̆ ž?{F¥nݺuâ¤I'OžšÝHf=(5õܪÂ:mưQnÝ50.NÇ€ÍÁ0"™Á!>zZ6Ì‘úz:M3yþüù¤‰¬­­÷ìÙ«o ÿþß÷¯^¿Âw=|øpÖLßQ£FÿrþB}}ýî];'Œ÷|ð0ÉĤÍC_õõõK—,ù~Ñ¢5«×œ:uêûï~¿hÑË/ÂÂà úÖàà€E‹~ÿã/>V´cç®^½zݸq}ÝÚµ½ÌÍ'yyÀ??2YáPÔa==½²²²¤¤‡L&³­åéÊÊÊêëëÅ”¹ŠŠŠŽÒ5 ºª+᡺ª+á ®+y©áÖ9m’€êüZ*Æè¦OQ$r¹Àæ°9صôÊ7¥%Eå”kSßÒ‹ k.în¦S²5XOOÿF|þ<²!C†òvíÚ¹£wïÞgÏÃg»0Àià€CFìÙÓÖ¢ÖÕÕíÚáìì ´ìcqóæÍÇ+*R ®¶nÕª•Æ_¯d3™ŒýûØôí óæÍ¿pþüø“¼¼rrrŠòôôÄczxx´µ0ÂÇÂ?ü²Ý‡À0®¸Ò58:8ª+a ºª+᡺ž€ºêĪ¶Î©jG§ŠÃ¨'(ÜyA{W]Ëår9l.› ,&÷߆Ï,²š¡nA‘«§õ(»‚\ÕÌp“ÉxôèÑò+ð?ƒ‘™™¹zÍÞýƒÎ..ééim-'¨ªªâ=*ÐÐÐÐ××>|8Þ£€>}ú@II ¯S¥¡¡÷¨p=LM‹‹‹@YYÙÒÒ2bODm]ó°aVÖÖ¡å‘}ÖæöÐŽCLœäµhñ²¢Â|C#ô*à5%9ÉÕm8ZèD¨®„‡êJx¨®„×l]………J¸Ò©Ú±-œ?dÓ– ^>ü¯¼lüUãjÞÍÌ¡*ë±ÏÕPYÍUï¡lÒËÐÉ^qßäbyÜù¬Üô_ÓŠókëê02zš{›6sœ¢"eèСqqq vQ(GGÇ„øx‡ƒ‡”––¦¥¦:;»4ÍÇ€J-(,为ºúÕ«Wm:ývèÓ§OhX˜’’RÎë×â>–´`bƒçohôÍ=åååK—,±èÝË´‡‰ÏŒ·oßJã¤eN£Z€’’’7xzŒ362ÔÕÑÎÿøQ*ë’’’ô··£èÛX[þPRR" ¾œ7B9?}áµú7ˆj’_yy9ïýÇ>|(ø½Hˆ¸SÕŽ‘ª¶vªjë8òœú*ЀÎ&ÖÕc\G¸d. ˜tuU‚žQG#9}{ÑôšÍ$$4¬ìÓ'¯I¯^KKK»pþüÆð]ë7lüçŸüfϾ{÷NB|üŒéÞ å‡ÀÀ¦™xyyU~þ±›F£½{÷náÂbZÉ=??ÚÔ)gΜNKKKJJZ»f Nw>\Ç’ü#U,kº·wrrRhhØÁƒ‡JKK'{M*++\N4ÏËËͽ~íš–Vw'''©©‰Ü·ïÍ›7sæø8xhŽ¿B|ü„ ãkkk›,çPÎO¿Mÿ ¢šä‡×þÿz„ ¼ÿ®›¾Õq¥9§ÊÑÁaÿþŸš ªè³_Ou²š*Е¡Z…PWÇ®«cW˜°kjô ×‘è Š³˜ýz³•”(Ífbgg—xëÖ®»Ö­][__obb2sæ,|—»»û¥Ë1{"vÏ›;WAAaØ0ç'O5»ž‚­­Ý¡¨Ã»8`ff¶fíZ!ä¹·‰†††¡¡aôáÃD"ÑÚÚúäÉSîîîâ8–,ûŒLx=†¸¸¸ìì¬k×o¸¸¸€Ó AND:&Ö2Ⱦ¦sªœ zóŽ=ššš*¥ruû"#{÷îÍÛ´íg;þ¼ø7þ7{vÓÈrÞåüôÛDðß ªI~xmŒ50 sss«©©Áwµô^$¤9§jâ$¯vÜÔð©Œn©G4íN$€«Ú Äze] Õˆ P_O¨¯'Ðä‡Ï˜.CÌäÓ¯ŸmK÷©Ž1bĈÍîZ½fÍê5kx›¾¾¾¾¾ÿ=„rÚ4oÞûа°FMùiæ3þÍ–•WˆâÄIüººzÔáhçÒň»SÅ?óûí[T*ÿ CCCW7·ÄÄDùüâ×tNzô“ðø{T0xÈÀoæmJΡœŸ~›þD5ɯ ü}'ž¨.gÿqêYy%Œ_ï~Æ0 c2¹ õ¬:³¾ŽÞÐÀ¢70é L:“£L YZµ¯$ˆtIr¤*''ÇÊÊŠ¯µ•õý{÷ …Òü0§œ@wÿ‰Ð£ôt°¶±iv¯œ7B9?}B5ɯQm¤¤¤€«««€÷"Ñùžýwq-›D,¦üå~; 0À0Œ@4¢Z·»$H'õéÓ§ãÇge½|õê5“ɼwïž‘Q3kþ‘ªÊʪ~¶¶ü{5451 «ªªÒ×ofÑ ùîþ•ÊÊÊ­¶¶vãÆk!‚\7B9?}B5É¿6xs§h4ÒÒûŽë|#U£Ýµ/¤³h÷HUþlj‰‰¶¶vý32þ¾;þøñT!-A#U"A§ÓçÍ[W_wõ‰D’vqDaæìì\UU…o¶ô^$¤|÷Ÿ¨Ò"]I»WL°ïß?55õÈ‘è#F6›ž?JSS£Ñÿ(´ª* ¡!èiÜòõ;;ŽÉdøûÏÉÊzyåJ¬™™YKÑä¼Êù鋪I~MkC2x*ìëO‡´u*Q¥Ez&5ÿ LVVVorrø÷¾ÎymjÚ³é#ŒäMÓuª6a2™óçÍÿ3#ãÒå;;;1å¼Êù鋪I~üµaXZZZZZþßu³ï;p¨oºOh¤ ‘9í©úoD k><þ1Ï¢¢"Þ³‹ŠŠR’“=Ç{Jü¤e©ê6›ýýÂÉÉI¿^¼Øê²^rÞåüôEÕ$?¼6ð÷˜ש’òŠê²‘5ïTa_îbh¾SÅ?ã=}ºµMÀ¢EýõÆõë³fÍìÖM}Ù²¤s沤éH†a ññ ññYÙYp÷ÞÝ„øø?32¤Q:Y·víšÄÄDߊò ¼Òâã³²^â{~ù¯qVxþè¾6a ZB¤kÀ0lРAeeeøA³ïEu¬.r÷_›ÒŽØ÷’ÿ2Ÿàͦ¶ÛX[=~üÈcÜX#Cþ6Öû÷ïçí}ýêÕ¼¹þýúÚP ô­­,ø\QÑ(íË—/&Loldè4pàÍ„8yòäÀŒ 'Nœ›ûÿp¯²³ýfÏîenfld8~¼çãÇ„?SyƒaØÝ;wîÞ¹óæíHMK½{çγÌÌFÑÐŒ0P-!Ò5õŸ¸ˆÈÝœªF—öo¶„F« Ú±sW¯^½nܸ¾níÚ^ææ“¼¼ ¨¨È¦oßÙ~~êyóìß?ÇÎo¿%òÒÖ××/]²äûE‹Ö¬^sêÔ©ï¿_øý¢E/_¼ g0è[ƒƒ-úý;xäìì¬ñžžvvöQ‡£UUUÏ;ë=mZâ­Ûööö”³“jwûf³ÙkÖ®åmnß¾\]]:Ä ­¨. ´¢:‚ ]Æ_ýøú&-½ y»ûO4Õ™LÆþýlúö€yóæ_8þFü ¼S5jôèQ£GãÑ lkkçêâüúÕ+Þ½uuu»vG8;;À€-ûXܼyóñãÇŠŠ¨«­[µjeAA±±1„†„èëëÇÆÅáÃtuu;ftä¾½gÏý"äùvFíîT‘H¤Ì&ãRM3D=*a gÿ!Ò5`_×£*--ÅCZzßqò8§ªãÕ544𮇩iqq1þžÉdîݳÇÍÕŬ§©‘¡á¨‘#àÝ»w¼Èªªªx ÏG__øðáx úôé%%%xV©©©“'Oæ=`œ@ Œçñ¸«¯ $‚9UxÑl!a ZB¤kÀ0lÀ€%%%øA³ïEu¬®3§ ïWµô*ZŠŠŠü›D"‘Ãfãïׯÿñðá(?¿9qW¯=xøðúõë@gÐy‘UTTøÓ’H$þ"‰xn4ÅbEEEò~öíÛ[)Ò•Ê䩪%A¶êjsªZzå×hºàÍ¶Š½reÙ?, À7_¿zÕî¬ÔÕÕI$R@ÀâÙ~~)R§#š…æT Í©B¤k÷w ?¹›S%’‰ê-ár¹,«[·n¼„› íÎB¡8;;§¦¦l "“Eü›’eâþ@=*a 9U‚t ¸S%õ‘ªV‰uEu"‘è6|ø/çÎyzŽ711IHˆ?{æL[3á>aüø)S&ÏŸ?ŸJ¥ÒhÕOŸ>Á¸Ø–  Žd+ãÐH•,@#U‚t ’ìTu9UÂGëŠê‡EõêÕÛÍÕŲELLÌéÓgÚôíÛïÎÝ{T*uó¦ÍÓ¦N]µreÖˬ¡C‡v$Oõ¨„j A¤­än¤ªãBÃÂBÃÂøCNœ8É{¯¯¯á×_ù÷ò?ª©iÚ§™Ïø7ØèÑNüùË4R% ÐH‚ ]©oÚ®¨ŽˆUYYYØà7Zæãë W¯¨ß‰ H× ü‚;G 1pš÷¶¤®Å€ Iš-E=þ‚••å$¯)²“ù‘ª¶N¼ÞžÒ ßz•ýò·ßýüf7 74š³fõª5kÀÃóú‹/â'|ø ɃÊÛœ*t÷_' ÉëßNƒ½ÎyÇŽE*ÁöEFöîÝ›·iÛÏvþüyñ7nüovãû:\]]݆õ?oݼy“€hqqqÙÙY×®ßpqq§Aƒœˆ:t¨Ñšv]›u…Àpwwkkki—BÖåü¸yÓ¦£Gž¿pAÈ$Ž™™´÷K§  |“ÉþH•XSƒˆ„$;UD¢ˆ¯€waü=*¦é8IΩBÚíQz:XÛØH» 2êÙ³ggNŸÙ»go«ß|999VVVü!ÖVÖyy¹ Cœ”!Â×ã==L{˜˜ö0™7×?7W¢×¶:‘ŒÇmll  |êÉ“'?.šS%^L&CQ‘"ϤôôGW¯^}òäï²²2 77·U«VéééI»\]PeeeHÈV[[»qãÆI»,²ˆÃá¬^µê»ßÙôíÛjäÊʪ~¶ßü#§¡©‰aXUU•¾¾¾ØÊ(+ÚTWrN]]}éÒeƒVUU}ñâù¡ƒ==<&%ËC;i«’’’’’’'Ož߯0`À«WGª:ø˜š­ÁÁ6ÖVwïÞq>ÜÈа¿½Ý‘èèF{“““Çcld„‡¿ÊÎö›=»—¹™±‘áøñž?â%ÉÍýà?ǯw/s}=k+Ë™3}kkk„/Y¼xô¨‘üEšìåå?ǯÝ5íˆ:räÈ»wïfLŸ±mûö>3~ÿý÷Yÿû_mm-©-:>oîܺúúS?ÿL"‘¤]Ytôè‘òò²õë7H» ª+á988„†…M˜0ÁÝÝ}ùò—c®TTT?vLÚå’E\.·¶¶T‰=m£K{‚7[B£ÑÂÂÂŽ9Ò»·ÅÍ› Ë Ââ%K¾î­Þ²yÓö;ûôéàÓ ;;k¼§§}ÔáhUUÕsçÎzO›–xë¶½½=Ìñó#“EÖÓÓ+++KJzÈd2„ Q¼¶@Ö´»÷bfÖ“·imiµbåÊÛ·nOóž&Šr!L&ÃßNVÖËk×o˜™™I»8²¨´´t÷®];wîâp84Ú—y¯L&‹F£©ªª’É?u555xÑp´ª* ÏSjk]!üÍÍÍ33ŸJ» ²¨{÷îïß¿—üH•¼Ýý'š‰êL&s×®]}ûöoïéÏž=‹ŒÜ·`áB`2{÷î4x0/~hHˆ¾¾~l\…BWW×±cFGîÛ{öÜ/ 999‡Eyzzâ‘=<< ¥pa´©BæÙ)˜™õäßt0J?•J£,]“Éœ?oþŸWbãììì¤]•ŸŸßÐаr劕+Wð¯^»z5îò嘑£F5Šoee•ùí#/_ç¼65í©¤¤$‰âJU[ë i„Íæ ‰hͲ´²úûï¿%?R%â˲?R¢˜¨®¨H2d(os„ûˆÊÊÊœœ×ø¦²²²Ó A¼½L&355uòäÉx‡¸q32ðÈ–––{"Nž<ùúÕ+Þ MKáÂhSdPG§£õ×_€…EtùO$Ølö÷ $''ýzñ¢“““´‹#»,--¯ßˆçÿ¡P(#F޼~#ÞÁѱi|Ï¢¢¢ôô4|³¨¨(%9Ùs¼§dK-m­+9Çf³ù7“““óòr (­òȲ &À“'O„ìQ}©ê¨.5RÕÒkûò@­›ÿ­øššPYYõe¯šÿ¿4ÅbEEEEGár¹‡ƒ¿¿xérÄî]{÷ìÙXQ®§§°xq`àrÐRxëÅkcdHz?4mïÞ½ÖVÖÇ» Èð› •wïÝÕÑÖÑ××ççCpk×®ILLô÷Ÿ[Q^‘ÿe£™¹Y¿~ènÙotëÖÍÙÙ™?„D"éëéñK½YaááÆŸ2eòüùó©T*Výô錋m ÊÏÏ_±<Ðkòd ‹>l6;!>žN§» ÞR8xyyØ¿?"b÷’%KËÊÊ6mÚØjWI@„©4 ëÈH‹ÅZ½jõ³ÌÌcÇŽYYY¡yT‚ rK#Uß«ŽTÁ×Þ¯ÿ$x³YªªªÇŸØ´qCVV¶ŽŽvhXo=…fõíÛïÎÝ{»7oÚ\UU©©©åàà°páBÐÐÐ044Œ>|¸  €H$Z[[Ÿmgg‡zT‚ ò Í©ê4 ðûwš†‡†…5ûÄ. ‹'N6 WWW:-|8Î×××××—·É»ÙŽt%Û¶o»ÿÁtïéUŸ+ïÞùòÛ1éabii%8!‚ Òõ 9U’¾Øê&"yíazñüÄÆÅÆÆÅògúúnظQ4%CA:4R%v"™¨ŽˆU»;U±±±Í†£ë€‚ rT‰»_5¼ãY´t} ÔBA:­¨Ž ‚ "’_Q]Ī› ñRI‹t%¢zLMSÒ>3ADrÐH‚ N‚ "h¤ AP§ A4R… ‚ "h¤ªýØlö³çÿ\‰{Àf³Û‘Ü{ÚÔÿÍš%LÌ”””ö·ãbͪ+A#U‚ HÇ¡uªÚðÚÚÚ´Ç…@P72±½÷àå˜QöøÃ†Å!%%ùÔÉ“+V¬”©¬ºŒ²²²úúz1e®¢¢"¦œAYƒÖ©j=í•„$Š"ÃÀÍ1®voÿ)-.S²²0ÑÕV¢Ó9)EZoÞZ[™´¯“ÉPT¤H»¢äèàpþÂq"%9I¬‡èJP] Õ•ðP] Õ•ðšÖ•«Ûp9©º™ÿ43“?ßttpÏ¥œ^3ÆëÒÙÐÀÄ>¼¯/¸ü×P¿¿°÷ IDATGû¡NšE2‹Å ôé¥õçó ­î4= G¿qc×®yy{ö4ݰ¡ñcL^egïØ±ãÑ£tƒagoAII x#XÍfµkçŽÞ½{Ÿ=wŽL&À€œ8tð`Äž=x&“±wï¾Aƒ·µÞ: ÐŽV!¤üü|1åŒ ‚ÈÉTIùî?ns'ápê w^Ò/¥Ôž¾SõóïŸþýóÙ;;/ÿÏÅtt H:t=“GïH…Å̦9ÐéôgÏžMš4‰7"Ò¯Ÿm¯^½ð÷L&355uòäÉx ¸q32š-àøL&ãÑ£GÓ¼§á=*Á Fffæ$//¼GÎ..ééi¼8ÊÊÊNƒµšU§&É»ÿ’’’tu´ù,ûXðö–””lܸÁÓcœ±‘¡®ŽvþÇÒ.èooG5з±¶ ü¡¤¤Dìµ#=òv¾¢2Þ\]íÀ– ˆS^^¾tɋ޽L{˜øøÌxûö­ÄŠ'u¨]µÉƒû÷'Nœ`ÚÃÄÈê>|ø…óç¥]"õgF†ÏŒérw÷—Ëݱ-œ?dÓ× ‚ø•DþW<œÍÁà¿j\Í»™R8Re=ö¹*«¹ê=4€Mz™Çm¨eT} å?Ëÿq‘nÓƒVWWs¹\===þ@}}ü Fc±XQQQÑÑGøÊÉáp8Íž‚àø4Z5‡ÃÑÓÓ¦6h4—ËÕÑù¦Ìº::/_¼àmª©©‰ïê˜Ü ßfdd„¿W¤(òÂórs¯_»æààèä䔚šÚ(Uä¾}ÕÕÕsæø›ôèñï¿ïŽ=šžžž””¬¦¦&¹¢K¼¯Hܾ};--MQQQ@‹5ÝÛ»¼¼,44LUU5ò§ÈÉ^“’SRuu›ùøêzP»^ÆãdzfÍttt,<üÝ»wS¦LŽ‹‹MOO»uëÖöíÛ¶…¹LiieÕÐÐpúôÏ™™™¯²³[Vöé“פ‰W¯Æ¥¥¥]8~ãÆ -eµ~ÃÆþùÇoöì»wï$ÄÇϘîM¡P~ lS­vv’ïT÷ô0íabÚÃdÞ\ÿÜÜí+ö£ôt°¶±içiw6òv¾mõìÙ³3§Ïìݳ·Õëõ999VVVü!ÖVÖyy¹ Cœ”Q¨] à?ÇŸB¡lÚ¸±°°°¼¼üð᨜œ×‹—H»\²K¾æT9:8ìßÿS³áR}böë©b¢NVSº2T«êêØuuì vM Þô:½Añq³_o¶’RókcŽ=æÄ‰“»ví 344ZöíÒ¾}ûݹ{/"b÷æM›«ª*55µ.\ˆïõôôô÷Ÿ»kçÎÊÊJƒ/³Ç·³³K¼uk×Î]ëÖ®­¯¯711™9sVKY¹»»_º³'b÷¼¹s† s>qòo=D°§OŸFGÎÉySYY©¢¢boo·té2GGGIÔÕÕ—.]6hð`UUÕ/ž:xÐÓÃãaR²¾¾PÓàx*++CB¶ÚÚÚ7®c'Ñ9ÈÛù¶‡ÃY½jÕw ¾³éÛ·ÕÈ••UýlmùC4451 «ªªjk;ììP»ÌÚÆ&æJ¬ÿ¿3gN€’’Rô‘#Ò.—ì’¯9U'yµcÅ OetK=¢iw"‘\EÐV Ö+èj`¨Flh€úzB}=Î ?|Ætb. ŸÉS¦Lž2…·9oÞ|þ½'Nœl6!‰DÚ¹/2RÈøÐ¯Ÿm³ …7›Õˆ#FŒÑl>¡aaò0ÍBðu: 444.XØ]»{Eyť˗æÍ›{éÒ%këÿëupppøÚ‰wwwwqqõ7öø±cAÁÁ—N§Ï›;·®¾>îê5‰Ô¾Âw"òv¾ípôè‘òò²õë7H» jW­zþüùL_'§A .PT¤Ü¸~}ÙÒ¥ L™:UÚE“Qò5RÕ>œzV^ ã×»Ÿ1 ØLnC=«Î¬¯£74°è Lz“Îä(h–Fâ. "íîTMœ8qâĉ¼MÏñž#G޼q#Þʪ™IèÍrtt477ÏÌ|*üA™L†¿ÿœ¬¬—×®ß033k[‰;!y;ßv(--ݽk×λ8FÙLFSUUå-˜Â£©©Á‹†£UU AÏ„èbP»FXhˆ†¦æ/çÏã­ÈÍÍ­¸¸xãÆ ^“'‹ïq·š|TµÏŵl±øú¥¦  €†¯hŠa$¢UØïQ¤«ÒÒÒ"‘Èm]J”Íæ¿h“Éœ?oþŸWbãìììÚ^ÆNFÞη}òóóV®\±rå ^àÕ«qW¯Æ]¾3rÔ¨Fñ­¬¬2¿}`×ëœ×¦¦=…YÜ®k@íJHïÞ½³··çï—Û÷·¿sç&o7‰ Tµn´{_úi÷HŽÁ`p8œŠŠò'OQ(ŠÞÞÞ2d³ÙüŸPÉÉÉyy¹Ó¼§ s 6›ýýÂÉÉI—cbœœœ:RæNAÞηÝ,--¯ßøæ–_ŸÜW¬XiÓÜükÏß~û-==mØ0g(**JIN^ðuRf—‡Ú•ðŒŒŒ²²²X,–‚‚òäÉ555uuuéLq8‰„Fª¤£ª àÿ÷kiu?räHïÞ½dè?g•Jµïo¯¤¤üòå‹3§OðJr3!²²³àî½»:Ú:úúúøc‚Ö®]“˜˜èï?·¢¼"!þË—¨™¹Y¿~¶-­s“·óm·nݺ9;;ó‡H$}==^àƒfÍô=íí=¼§O>°hѦM›UTT"ŠìÖM}Ù²¤Pti@íJxß/Z´èûïgúúÌ›7_AQ1!þƃû÷W­^¦ 5å7{¶±±ñž½{ÑH"ï:Ø© ÚTS[ýéSÙ¥K—/^|ôرþöö-E:lèÕ¸«qq± C__ßÇÇwý† ÚÚ_žÉáp¾ûî¿;~\·ÆŒûëÅ‹ðäï¿àܹ³çÎåÅY°`Á®Ý)¿Ì’·óŒËåp8¼ Ó qqWƒ¶l ÚÂd2‡ rüø ù¹ïµ+áM:„#G¢—.]ÂårÍÌÌÂ÷-úúO ÂoÔèÑq±W:ÁH*`oB¼Ã$¯LÄDZÇE„‘%ÔÕ4Q±øúð¾Q£FOòš´ÿ§ýøíÇÍ \¸¼¥½d2¹¬¼¢¥½)©i-íê’äí|E(ïã7Oò9jT£v¥««{ôØ1ÉJV vÕ&S¦NE÷ú cáÂ…øÂFRZQûúÓ1ÒêÙ UW"ª¥>ÉdRï^½ Z]üAéz$±¢ú·Ý'ß„™/hÝNñ‘Öqqhw/ŠÍfóoÒh´/^˜šš¢N‚ ˆêôwÿÉøH•No­þ£Ì9œ¿\,.›ƒÕ3¹ê*äWɪÞW‰®Œmp÷®?Ξ8ñ׿3r¤Ù AF»v5~pWµ|ù *•jem©¬¬\\X|õúÕÚÚšE‹¤].AD :Áœ*Ád|N•ñÀžd]&° @À;Vàã&›«®BÔÓTø§°aÚ½‹ñÑ{ô[¥häH³~Ôé:UíRrvq¾•˜˜˜øN×ÔÐèïà±;¢oß¾hŒ AD¡‘*ñ÷cQÍg‰@"p9H ã D øDU ©¬ŽXÝÀû³0·°F˜<)ƒÁéXñ‘o´»4Ó×w¦¯¯¨rCA:54R%Þãö0PZ?Íã²0  0.›@$Æe‰d ãr@À€¼ïzUqs™ìÝ;ÖÏÏnöì¸;FÙÙéŸ:•ùÉ`g§¿mÛH77S …ôôiñ† wSR>âIlmõBCG b¬««òùsCbâ?ëÖÝ)/¯çå9c†MXØ33­÷ï+ƒƒ4:âØ±½BBÜ ˜LNrrÞúõw_½*Ãw??ÍÊJgàÀã¼ÈΫª¢O™riÿ~+†m€ÂÂcãHè P7Aé84R%Þã*PTÔªîÑ î×TÖu3ֺʲ¢j=K¨*þ”W¤gkÏ­(abÝH Ê*®•Ñ-壩©´¿ÇŠ·_¿.SR"€½½~ZÚ‚§O‹çνV[Ë\´hÀÝ»þÆzò¤ŒÕ_¼(=uêiUÝÌLkãF—ë×gº¸üŒç6~¼ÅåË3bb²—,ùMWWeçÎQªªŠOŸ~éÑcž˜8ûÖ­&O¾¤ªª6"-í»þýæåÑZ*.,,‰D"ÌÛßÎî°Ùm{Z ‚ ‚tjh¤J¼Ç%#)sX Xœê Œ¬Ífи%\’:ËdÑ*¹šÚ$f¦J&[|…BZ¼øfZÚkÏDDŒ).®;ö: ÷ïøóÏï·lq›:õ2ܺõîÖ­wxÌ´´üÌÌ⬬¥¶¶z/_~€­[‡?{V2kV,>@óÏ?Ÿ33ÿ[Ì-<|ä›7åS§^Æ{E…ïÞ-_¿ÞeéÒߟìçÏ 4ƒËÅrs¥3ݾÝÐH‚ Òq’©ñ’ 2>RE"‘8õ´¢ôúò‚—ï ?±0vC] µNÉã0òž¿-«áVå¿)z×@ H-?T·¾ž•žþ_JQ‘4b„Ù•+¯ð`$$¼uqéÁ‹<üåË%ÕÕéô-Ož€¥¥(+“4Œ‹{ÅëH<{Vòöí—…•”ÈNN†±±¯xãLEE5ænÚ†ÚélÚ½¤B«¤}f‚ ˆäHbªou©uªZzå!—¬¢ÑÛÕYÝȲ÷X7‹¡V%uƒF½ ¨Úgüèžv½´-íÌG™s¹\bËuSSÃäÿ‚ÖÒRRP ®[7ŒNßÂû rÓÖVÁ#><~íÚa'O>=úœƒÃÑ‘#Ï~ÝPCC‰H$””Ôòç_\üeSSS‰H$|úTÇ¿÷Ó§ºîÝ•ÛYSêT!‚ 'Å9UÖñõÔ¥=RÕÒ+‰HÀ¸\ú‡§ô 2³{)+¿ØÆô™ì÷ÀîÆ,xÖðo [ËŒQýAQË‘Ôòå¿Fh4‡ƒíßÿøÔ©æOöl»½{ÓÈÀ7mmõøÒÒ¹\LCC‰?¾†¥¶– UUt.ÓÓSåß«§§úùsþžÍæ¾-&ÞWCA9'9Ux߉×ê"#UB"‰³ªžJ hTèV±Í]_‘¯]É1àTêÖz2ëIŸË¨@Àa;Ut:ûáÃÜ#ÌÞ½ûœ“SÎÿD"AQ‘T]ÍàÅ÷ö¶á½oh`ÿýwÑèÑæ¼*U­_?=^ÎþY8}º éëÅH*UÍݽçǹøfaau¼é_þƒÁVPñ¯XÐH‚ ÒqhN•xHe]k%² Ù`š¾UP4 †©XÑ€¬d0DMÇ”¦b iàD ~¤ Ö¬ùÝÒRûÁƒ¹ÿûŸíðᦓ'[nß>rçÎQÀåbwï¾ÿþ{G ‹î ÄY³ú|sá644Éã÷ªUC(’±±ú/¿Lã_ø*8ø••N|ü¬ñã-¦O·ùã9 ;"âË#Hcc_ik«„„¸kj*YZj_¾<ƒÿ.¿ìì2…%K:9ÚÙuŽ‡Þ—••Õ‰ŠŠŠ´ÏA‘ÉÏ©ê"wÿ ‰H"²ê?×ýóŠñêÞaåLvumý»2¬¨–]õïž±ò蔬¢VY‹NhK‡óùóR'§!!îû÷{tï®üùsÃ_FEý‰ï?ÿúñã“^¼XÂdrÒÒò§OIMýŽ—61ñŸ™3cÃÂFìÚ5:?¿zïÞtþœïÜyïéy!$Ä=.·Åâ&%åúúÆòÖSÈÌ,™7ïzHˆû† .ïÞ}Oâ¿üwãFÎñãOÂÃGvï®\TÔ Ö©rtp8Ⴘ‘’œ$ÖCt%¨®„‡êJx¨®„‡êJxMëÊÕm8Z§JŒÔ(E]»nÌÍ>Àea IÒ r8, ,É}ˆ\#Ø‘›Ë"+™X¨V²›ÍdíÚ?Ö®ý£ixNNùÌ™±Í&).®4é"Ê¿““ÍÛüÂùó¢ª%ôgF†ÏŒé}m¬ ôõz™›ù̘þgF†´ %ÓZj9IIIšœe !Ó–””lܸÁÓcœ±‘¡®ŽvþÇ‚ËP^^¾tɋ޽L{˜øøÌxûö­ÈO³ƒP»ž0ŸWÿ+C#U"QËW̘áÃÛüû!<,lô˜1¼³gϬ]³ÆÓÓsÛöíd9+;«¢¼ß•ñøñ¬Y3}osê´iöv¶111è˯)Á-7ÜÝÝÚÚº­i zóŽ=Új§*...;;ëÚõ...xZ§¢P*ÉCíJx‚?¯¦:ÁH†mm-šŒ|ãUöKþMÕqÒZ¥“Ëå^‰‰2dhÓ/V¼ðë ÃV¯Yƒï%~ûè""‰H"‘ÔÔÔ¾l‰êÝÔI¤Î·Újûhkk“É â›×© n9<õõõÊÊÊîÌh¥Õ x~V¿ß¾E¥Rñºº¹%&&ÊT§ªÔ®„Ôôó iªÓÏ©º™/Ú ‘.ÆÜÜ̼WoÁq$9§Š_jjjAAÁ¬Y³x!ÛØØÄÆÆÚÛÙêëé::ô?ÍËÇŽ?…BÙ´qcaaayyùáÃQ99¯/é`É8:^WW—›ûáÇ×Q(Š~~]sX®ƒ·ÜxOÓ&¦=LæÍõÏÍýЦ´BÊÉɱ²²â±¶²ÎËËe0-%‘Ô®ÚªéçÒT'© Ý¬Ž´Jð,uQY¾<ð?îL:eçÎ]B&¹tñ¢ŠŠŠ×äɼ’’’’’’È}{ƒ·†_¿v-88ˆÁd®\¹¬mlb®ÄúÏñ;sæ4())E9âáá!ŽÓ‘ÞÞÓðyÄÚÚ:—.]nö"¸å¨««/]ºlÐàÁªªª/^üäÉßµµµTªá8q«V­ÖÑÑ2ó®!rß¾öç}Ì€”””§OŸ¬XÑæÏ_ÙÑñË÷ï?øóÏ?6·ÚÚÚ›7&Mš¤ªúß㫹\nmmíÉ“§F ÎÎÎó?F:H"‘ž?>Ó×ÇÉiЂ… )7®__¶t)S¦Ní`ùeÙÞ={iÕ´’â’ŸþÙ××'æJl£y´Ör¾Ü(íîîîââê1nìñcÇ‚‚ƒ[M+Å“+Ô®Ú¤ÙÏ+„ﯬs?û¯M#U¯¿ÊÈÈx͇·™ÑÚµG¢£§N™üùsÅÆM›Ž;>ÃgFÌåË#܇¿{÷®cçщ¥¤$bãÆ ]{:ˆµÍ!C§Lwõjwmípž#E‚[N#ŽŽŽæææ™™OÛ‘V0MM ÆB«ª"mÍJÜP»j“f?¯¦$ÿì?)Ï©²¶¶¶¶¶èÚšV++«799ü!¯s^›šöTRRjkVƒÚ•0šý¼Bš’üœ*iŽTáÞ¼ySQQQ__Ïår+++ÿý÷ßgÏž¥§§'&&^¹r%""¢¥„QQ‡öìÙË;LÏžf«W¯ÉÎκÿ²dñâÑ£Fò'œìåå?Ç·ù*;Ûoöì^æfÆF†ãÇ{>~üˆ·kkp°µUrrò¸±cŒ ƒƒ‚nݺ¥«£ýüùsþ ½§Mmtˆ–’·zÄÜÜþsüz÷27Ð׳¶²œ9Ó·¶¶V˜³ÀmÞ¼é§ÈÈêêj|Q8;Û~ò” qOT™•uéÒå­_«¾ªjéˆyy¹gøø4êÁO˜0îÝ»Ï ¹w÷®ŽŽŽ‘±1eee±X,ÞÞ'Ož¨©©©««·¹^:‡Ã¿YUUõ÷ß÷êÕKZå‘e‚[›ýÍsE“““óòr (LÚ6ñðð,**JOÿ²phQQQJr²çxÏ6Ÿ8¡vÕV-}^!üð¿2ÉTImNîòåË–––ÿþû/™LÆŸO‚W‡Ãa³Ù¹¹¹?þøcVVVÓ„†¥¤¤ 6 *ç7a„  -)É)£G·¾$ZvvÖxOO;;û¨ÃѪªªçÎõž6-ñÖm{{{<V½eó¦í;vöéÓ‡A§=s&ò§Ÿð>|HII‰Œü©Ùü%oõˆsüüÈd…CQ‡õôôÊÊÊ’’2™Lak`íÚu\çÒ¥KÉÉ)@"“;ž§Èµ:Q]ðu:8NHðÖY³fZôàYšâ IDATéƒg‚ —Û¥K—0 kzÍèÑc\\\V®X^\¼ÁÄÄäúµk))){ö~éÇ¿hѢ￟éë3oÞ|EÅ„øîß_µzuWøâ7{¶±±±­­ŠŠJþÇüóÎ×ÔT¯Y»VÚå’E‚[Žÿœ9T*Õ¾¿½’’òË—/Μ>mdd LZ Ãn&$@VvܽwWG[G__ÐàÁðàÁƒY3}GG{{OïéÓ£D,Z´iÓf•ÈŸ"»uS_¶ìiUK³P»j«–>¯~ø_Ù¾ÈH9ºûïýû÷$)22²W¯^?~ÔÔÔÄ¿X,‹ÅÊËË[·nôíÛ·iÚšššššSÓžMw„‚B¡n1 Ñ×׋£P(àêê:vÌèÈ}{ÏžûÀd2öî݇`áüýçï@?®[cÆŒýõâEÀ¸\‡Ã›ü   w5hË–  -L&sÈ!ÇŸµûþP»jŸW?ü¯LŽîþ€ñãÇ×ÔÔ€³³s= ÔÔÔH$—ËýðáC``àåË—y%mÅývH¹YL&355uÙ²exÿ¸q'NžàÅQVVv4ˆ?Õÿ½{÷ÄÆ^™7o>“ɼxñâŒ>***Í¢QrÁGTVV¶´´ŒØQ[WçR…¿ª«ûö­ ZPX0û³»kk¿ÊÎ>v쨂‚©S?óÖYðòò:°DÄî%K––••mÚ´‘zMXxø„ñã§L™<þ|*•J£U?}úãb[x÷ä·`Þüï6¬ÿQSSsÊ”)m:_GÌÏÏ_±<Ðkòd ‹>l6;!>žN§» ÞêYð³´²jhh8}úçþý(ŠŠÝÔÕ[ÊSf‰tT ]õC‘SþÙ7}útá#.··ï}8$dkee%ôëgË»lmíEŽˆØ}ðÀ33³5k×2èÿ-ÂÙ·o¿;wïEDìÞ¼isUU¥¦¦–ƒƒƒ0³&'Ož¼aý¾3gò¦œ IÀ544 £.(( ‰ÖÖÖ'Ožrwwoõ,øyzzúûÏݵsgee¥AjZzKyJ…0ÕEÕ JOOan‚ Hç"Å»ÿD1M½JJJ>~üÈ‚?—†Á`¼~ýÊÊÊZÍÄÍÍÍíëóV¯ZuáÂùŒŒ?ÇÏÇ××—ÿ.ÂiÓ¾YþÀÂÂâĉ“ÍfÚÂÓîÞ½ sçÎP¶–’·tDuuõ¨ÃÑ-å&à,V¯Y³zÍü=‰DÚ¹/2’·W@žROTpuƒAŽ3¤êßLˆšÙúý’Ží½ø‡ñ½Ju¤ŠJ¥¶‡µôVEìÙSP¿8`Q|ÂÍþýûw h‚¼}û67÷î;ÆóhuöÒˆ0ÕA¤ãžá€y(ssªd_ÜÕkÒ.B‡:U‚ ˆ¨p„X \TP§ ‘(INTGAIvª¤¿N"oZ]QAAD…ó-cccN¢:©B$J|+ª#‚ HSü}&sss055}ÿþ½8Ž…:Uˆ¤ ž¨^VVV__/¦C·ôÜkA¤«âuªøoØ777ÿçŸD~,Ô©Bdˆ£ƒÃù Ä}ˆ”ä$±¢+Au%rÄÃÃCÚåê|ä¼!µ$hË!c†oÛÖñáN"sÚ}ù 6¬ß@¥à! ŠŠBæF§ÓçÍ[W_wõ‰Dj_º¶£G”——­_¿AÚé¸\nmmíÉ“§F ÎÎÎó?F:ˆZW#ÏŸ?Ÿéëãä4hÁÂŠŠ”ׯ/[º”„)S§J»hˆÕÔ+Ô©B$J¼Õ1 †Êÿ€'arc2þþs²²^^»~ÃÌLš«½Ë¬ÒÒÒÝ»víܹ‹ÃáÐh_¦0™,¦ªªJ&£O’otïÞýýû÷®nn¼÷áî÷ïÝ+(È75íÙ(²¦¦¯Jq´ª* ¡¡!¢J]XhˆÆÿÙ»ó¨&®6À7 »¬Š²£ D"*¸ãˆ»ÕVE­Öݪu©UQ@­+Ô}·*®Uq­ØEå,ÖªP­Å * ‹¬$ßÓ¦iBC˜Èï9œœÌ;wn†y3ofn&¦¦GŽ¥ö¢=zäää|õÕ’ÁC†(BRšøŽ¤îS2j zƒ®¢¼¼\ùÌŒÏçOš8é·Û·O~wŠËå6hÇ4WVVVyyù¼ysÚ:ReeegÏÆ:µuL¸ß&“Öîï¡-ÿî„B‘ÂfÉyËuuu}òß {œþ¸uë6zzz ÚI†xöì™{ûö’yy‡Žòòò¤ò¨Uß‘À}ªàƒÕÐÕ !Ÿ|ò‰·····÷çŸÏÉÌ|¡x zuuõgS&'$Ü8~â„Ü[¿¥]»vç/\”üÓÕÕíݧÏù y^^Ý;Æ &„\»v]\ríêUsss[9' ÌÎÎNJúûÖ ÙÙÙ‰ Aêéj£³±±IMM­ªª—Ü»wÏÐÐÐØØ¸{¥‰šøŽ¤•6ÙʃûTÆ«u ºÊ—ÿ 'N˜ØÑ‹g oðøñ£|òÉØØØØ–-[Ö´ÈÂ… âââBC'äçå_ºx‘*tptððð¬i‘¦ÉÈÈÈÏÏO²„ÃáX´j%U”€€~þþþóæ~ž“³ÄÎÎîü¹s‰‰‰7m¢®gÅÇÇ›:uêgŸýÑĉ“´ut.]¼ýúü/¾Àà3Y"‘èûK—!©i©„«×®š·0·°°èìëKšüŽ$E¼­È?—ÿ222%ëdddà•áçrRåîîîþÏÓºvíâãÓyìØOŽ9:o¾œ¯\QîݽK‰‰9sX\8yòäuë7¨ÖB‹ÅŠ9rtÍêU›6n,**jÛ¶íö;GMÍ …@ü—ÚÚÚ±±gÖ/ [Îçó»té²wᆭóu­aƳk×®3gÎ …«V­ž:mZc÷‹‰äœ/ZDéׯÿñ'H“ß‘¤PÛjÎ矉1Uüñ‡xÄíüÑc­TZ)3P.žžööö©iÔI¼Yãϱb/2ñëÔŠ­[¿AnvÞ§oßܼ|É’–-[îÞ³G]]cœ¡Ã†á»~ÊÐÒÒ’Ús¤4ñIµ­"##ȪSwóJOOo ÑëHª@ÝÔyGu@@‹Æ@³HåOiii ÷}@$U VŽŽµ~õO娺ºZò;D·oß~ùòåÀÀHªš,uÞRI¨[ÃÝQ}Þ¼ù­Z¹¹»éêꥧ§Ÿ>uÊÒÒr~  CRMšÊ'–¼¼y?\ù1îJŸÏ7774hÐÌ™3MMÍp¦  ÉBR¬½£úÄ 'N˜HWkðÀÕáCVëÕësóÏZo M„§äÏ$=v̋ǫÿq¦ ÔJ™êõçííE9zìX­5½xRûy]ßó¥f¥¥¥1$£"Hª@ýrê8PýÁƒ\.I4A~ÌÃ#ô‘ÚÏëúž/9ëÁƒÌɨ’*` ÙIIIÁ@uµINN&„xyy©ðèÕèÇ<<â±A©ý\å÷|ÅÕ’*P«: TWÎTÑ.88øÕ«Wª=5ú1xlÐGj?ÇÍ?T‡;ª3—ËÍÊÊRí9ЫÑyxÄcƒ>Rû9’*)¨îÅã­Z½zÜØ±Ê7XׄР~ÌÃ#ô±áÞó;æÅã©yªCRê–Só9*êÇ/;¦L;TÀ(Y@ãxñxVÖ¶÷îÝóööÆ#?ÐdžzÏ÷âñÔÿkÊ„Vxx¸¥ÏÄ'ÙÅe|!dåPóšª=zÌÕµ]Èà¡jì|¥=¼|9nÜ8éÏ"Ö6ã|1Á/BHTTòÏW3M£ƒ$#£¨_€=uTZ–ôí·»D¢x¢ð\@ÄùŸ¯r¯š²†»<ñc³7Ù²5-¬ôpªS;„©EäÖU]ãBÄ;ó™3g=¹mÚ´á°Ùýõâç«×¨lçâÂãu011Õ××#„”””de½ü%éVII ÕBÈ `gg'BÈw§N{ñxm***îÞKNNNñôôðñédldôömîõøø×¯ßˆ×«r¤--­ÀÀÉ)¿IÍ­µ·LÓ”ã‚QTZÕ:PÈŒQ<)µxpP`uuuYY™©©©«k;]]S§ÏèüsBHß>}Z´hNyýúýÅ ç,\88d›Í …ïÞ½333sumgoos¤¼¢‚Íf9ÜÐÐ’››Ëb±ÍÍ[˜™™íرÓ²Y³f æêëë‹WzìØ‘!C‡ëèèˆKމ>b¤j½Rb3ƒ"5~ä9þ)—ó¦¯à0ö&;ãÇ„ÔaÉÂÚQùþÐꨣ£# µ´´œœÚæçå]ÿßÿôôô-­,­­­KKËÞ¼ycddlllìîÞ¾U«–ß<,„>}z·47'„öêÙÃÔØ¸#¯#5ËÒÒ"pà€-[·›˜˜BÚÔ5RÄ[æûË—‡ øã?KÕQ¾· ˆ æCRê&uŽJ6rê4 Wjñ„„„eË–kiiE®Šìààà@„B¡P(®PUÅŸ9cfÚ£ÇÖÖ–' o6›ýöíÛÐ r²sÜÝÛûí·ú^¼—ã~ppt¤r¦µ_}âäwFFFyS>411±¶¶V0×ÀÀ@¼R¡PÄf³¥º*9Y§^™iÔ\h× —'&|<@¶ðð‰ä=²³ötþáÆßÇ=¸ñuäØSú2“d;µÖ'.Ô)5-mþ¼ùZZÚ§OײeËN>6lÜØ½GÏŒŒŒ“'NüøãÚÚÚúŸ}öÙ„Ðñ-[¶¬¨(ãp8ZZÿŒÞ¾y;îD$ú÷|6›Í VÜÛÚ·ˆz5‘¸`>$U nÊÜQ]ù¹R‹ÛÚÙê°X,cª¤ ° ¢òßÑUUÕææ-©¥Ê+ʩ¸¸¸%K¾’íFUUÕãôôKß_ª(/³µµswo?|øpc##+«Vþù¬yóŠçŠþ9ªèèèPÇ cãÇÃþ÷ò_Ýz¥§§'UÈa³ ‹EþùñQñÕF‘HD]ýäp8„" $.†j"M9x\¾žN9ÞdgÄÅ“à>®Šû£L¡Êq¡N"‘ÈØØXª–V–TŽ’šš¶`Á‚œœœ^½znݺ•š+¨HÖ …&&¦âP`hhÈf³Ù¬3$©)R’SRzöì%9HÀ¢U­½eZÄ5‘¸`>$U V²ÕeՌà IDATi@®”™3f^¾gjbBÝßrû×ÛzºÿycxÍÍÍ{ÿþ½‘‘Q@@À°áw©_E000pvv216NH¼Éa³=¹žFFF………oß¾¹ýÛAƒéêê63LIIé߯¿‚¹}û”–”R+êÖ­›@ èÐkd$ý%#z%wñ!CBlmm !»÷ìãóùîíÛ÷îÝ“ríZ|jZšÁgS>%„deeÅž=¯Ñ'º4â2Ç÷×SGŽA}ݾ¿FÞdg\¾NõuS¹·¤~qÑèÄÉMzúãêêê12bø0õ¥¶ªÜ=VµH‘’óÇÏœ$W¡Lo™qM6.˜I¨[ƒToÙÒüË/‰'Ÿÿܹ ž\w×v®mÛ¶½{÷î;w—,ùRj‘š&‰¼WÄb±TˆÙµ¼ÍÍ}òäI»vÿŽïáWUÕÚ[å·Œ õ…FÄó!©µ’¨N¯‚¤[·÷í;ðâÅ_"‘ÈÞ¾µŸ¿‡ÃùáÇŸ®-¾úç³g²‹hiiýúÛo»÷ìÍÉÉ®¨¨`³ÙFFF––\O®P(LHHLúå—·oßóù|‡cjjêçßÝÝÝ]ñ\ªñÌÌ—q—¯Ü¿ÿ{EE…‰‰)¿ªzÙ²°œœlj®j½’ûÂÏ_¸tùûK¯^½¢&SÓÒN>u31‘âß½{EEŦMÑ11‡ !666õÞÌIî[íà~׳©¹ir ?$²;ó•~üù矞gü}¦ä]AÁéÓgo%ý’——§­­moo?wÞüøë× !\nÙú`ËÖíöï#„P_‘Ú«©ju”¤[¿nÞ¼å÷ßS!ýúõ§ ¾z}áÂÅ/^üEþ¹öWko™qˆ †@RêÖ Õ !úúú}úö•m3  _@@?¹]27oÐOþ,‹%~û®ë\Š'—ëÉý÷ýݱmÛú÷J®A!ƒ%'ÝÝ=ÜÝ=Ä“úÓ¦ÏP¾5Æ¢ëDÓÚQ¦áÕewæþÿ½©……ÅÐaÃ%K\\Ú)¨¯¥¥%µÓJíÕ”ºFŠo—.¾]ºH ”*©µ·ŒŠ8¦íÏMö„:’*P«†¾£º‚Bø00íM_mI•¦ÜQÓög$Uj¢øWÿÂÂj¾Æ^³Ÿ~¾ºhñb¹×ÑàC×[-ÓÚQLµ¸€¦ƒiû³†¦Dõ‡¤ ]±×¿ÿÒ´o5ÞD0í“´:/ÿÔ„iû3ÎT¨íwT‡&¥M›6÷î݃²®îÞ½Û¦M¶# qÊcÚþÜpqÁ|Hª@ݤªÔI›6mîÞ½«ò²Œm >˜¶?7Ù¸@Rj¥Ì@uÅè¹%ÓÚ‘„¸€ºbÚþÜqÁ|Hª@Ýj½£: .4’*hd8xÈB\h"$U V’ÕoÝÒ]ûujVÒ­[JÿLÀ‡Í±­c·®]©çwîÜݳç !qMœÜ¸ÐHª@ÝäT§ŽÔÏ4ééO!âã‡Ä4MŠã‚ÉTZÉT§PG6[ë^ò½ú´ïíå- “SRêÓ€zxñxÁÁA—/ÇÕtð@\@Tk\0’*P77S¸—|oÞܹ*&‰D›·l!„Ô§õ vWe~\qM‡òqÁLHª€YX,–P(TmÙ¬¬¬ú7 âÝUˆ h"ê „¤ ÔJöŽê²D"QýWDK#̸`>vcwšj zc÷€f8SjUÓ@uIøD qÀ|Hª@ÝjýÕ?<d!.˜I0½·oßîÝ»75õá£Gù|þµk×lllêß>€š!.˜cª@­22žScÕÕ&+++..ÎÄÄŒÇã©s½L†¸h8Sê&÷Žê’èýDÞ±cÇ_~ù…s8æöíÛD$ÂÐDˆ æCRj¥þê,‹z.""ªÐDˆ æCRêÖhÕE—ãàšqÀ|S@œ©µjÄ;ªã2h4Äó!©uSó@uÉ"‚ƒh,Äó!©µjÄ;ª‹0v4â€ùTºÕ:P@!©Æ¡÷¹H$ºvõ*!$ýi:!$ñæÍæffæææqÏCÐ(ˆ æCRj¥þêÕÕÕ .—¯Y³†Ò½{÷mÛ¶Õ-jƒ¸`>$U nj¨ÎápRRRh-jƒ¸`>$U VÊ TÐDHª@ÝíŽêš qÀ|Hª€qpð…¸`>$U VxGu†¸`>üö¨5P½±{@3œ©µjÄ;ªh4Äó!©uÃ@u .˜I0KnnnYY™jËÔ¿õï®Ê@\@Q§¸` $U VŠª{{y=v¬>í{{y‹DÂz6 ^Êý& âš%ã‚™Tº)¸£ºH$¬gã"‘pPÈàA!ƒëÙ€$&ÜP¦âš%ã‚™TZ)¨žœ’2oî\‹¥Zã"‘hó–-8rÀq )Tº)¨Îb±„B?—gee©¶ Ã!.4îSŒ#R•lS¯_¿þê«%ØÚX·4o‘•™©xÕyyy3gÌpvjÛÚÞî£F=}ú´a^"@Ñ;vl÷âu¤žïß¿¿¥y êÏÆÚª[×.Û¶mªu211qË–Í’%ÑQQ­ííj]PÉjjöÛíÛéÞÞÍÒ¢U[G‡Füíömñ\ÉÍ@ARj•‘ñœ«®/þúëü¹sffÍ}||j­\UU5rĈ„„‘[·n{óæÍÁ!¹¹¹jè'@ãÚ¸iÓ±ãÇwíÚíââñõš5ªµ“˜˜°uËÉó–æíÚµ££ 3+Ó̬ùœ9Ÿoß±cáÂE/^¼6lèÇ»_À\¸üê¦` :…Æ[éøtîü8ý !dÏîÝ7oÞT\9666--õÜù þþþÔ²>¼·oÛIWTÖ ·˜òóówvv&„„ Ü/ ïû—.[Æáp”oϯÔÑÑ•- :¶Žª×È‘£FŽ%ž6|x®ç©S§<=¹Ø+`2œ©µrttpl뤸—9Øì:ìá?þpÅÊʊʨ!ÖÖÖÝ{ôˆ‹‹S¾€†Cc\(Àb±¼½;•––<~ôhâ„P÷öV–n®íæÌ™ý.?_\såŠíÝ\ôïgkc½",lÙ²¥ßDGS¹žDæºÞ£´´ ¡ã]œ¬,-|:uÚ¸aƒÜn««+WW×ï¿¿¬¸Ž¶¶¶›«[jêCõt 4’*P7ª‹þ®F/_¾œ”ôK·n~„ìììÄ„„ÉS¦Ð¸ •©9.„BaUU•‘‘‘¸äÒ÷µŸžÑÕÑ­ªª’;KGG·k×®±±±ó硯¯_c ºº~~~7o&. Ó’êÞp }»t‘*’÷•(,,¼{÷®»{{µõ 4’*`œZ """Ê‘+{ðŸ~:I<¹xÑ"BH¿~ýŸ8A …@|×mmíØØ³aË—‡…-çóù]ºtÙ»w¾÷ Ac\(iÛ¶í_̟ߣ»¿ŽŽNg_߃)^$0004tºµk ,--$UÀ85<8ŽÔÍ9×ø .˜I0²̧¾úø€áL0>‘ÈB\0’*`<d!.˜I0²̇1U4À™*`|"…¸`>$UÀ,¹¹¹eeeª-k``@ogq TƒxñxrÁ¾N-$&Ü «?L€¸ÐHªàCÓ½GÏÆî€RÔ™è .@Shô$UÀ É))óæÎe±Xª-.‰6oÙ2(d0½½h\ˆ M¤ ˜…Åb …BÕ–ÍÊÊ¢·3 ¸Ð¸¥0ŽHU²Mݸqcú´i;p­,-Ú»¹Î™3ûõë×5­÷õë×_}µ$pà[ë–æ-²23¥*äååÍœ1ÃÙ©mk{»>õôéSÌeÔÜZÿƒƸرc»¯#õ|ÿþý-Í[P6ÖVݺvÙ¶m«@ P­“‰‰‰[¶l–,‰ŽŠjmoWë‚JVS3;›ä6CR²è¨¨'OžŒºeë¶ñ¡¡—.^ *))‘[ùÅ_?wÎ̬¹ìܪªª‘#F$$܈ˆˆÜºuÛ›7o† ÉÍÍÅ\æÌUü6nÚtìøñ]»v»¸¸DFD|½fjí$&&lݲE²Ä¼¥y»víè裺)ÞÙäÂå?`ïÇíää$žôôðœ4iâÅ >;V¶²OçÎÓŸBöìÞ}óæM©¹±±±ii©çÎ_ð÷÷§*ûtòÞ¾m[Dd$æ2d®âÿ ¦kÐûTùùù;;;BBîÐ÷ÀýK—-ãp8Ê·ÀçWêèèÊ–‡†N @[GÕHñÎ ÎTãÐx™C2£"„øvéBÉÉÉ‘»^6[Q8üøÃ+++êí•bmmݽG¸¸8ÌeÎ\ÅÿAMGc\(Àb±¼½;•––<~ôhâ„P÷öV–n®íæÌ™ý.?_\såŠíÝ\ôïgkc½",lÙ²¥ßDGS¹žDæºÞ£´´ ¡ã]œ¬,-|:uÚ¸aƒÜnŸ;—ʨj®Æ¿yóæ¬Y³¨ŒŠÂb± ¸oÿ>jÒÎÎîõ›·Ê¿4uBRŒSÓÁ#<<ÜÁ¡xÒ­ëÜyó~¸òÃðÃ7ÈçW††ŽOM}xîüÕzejjRTôŸKE……,ËÄÄs2÷ÃF{\HÚ½g­­¶ŽNëÖö-Z˜S…_~¹øü¹s_}µ´“¡¡aQaaPP`Ee…x)CCCåoIZTT,Zµªå„bQQQUUÕöíÛwîÜ%. *ßåAeMyg•!©áàÐFrÒËÛ›òæíÅKñùüI'ývûöé3±\.Wåµ»ºº¦¤¤H–úht~^þ¥‹©¿ÔÔ‡ÔÜøøxK‹V±±gÄëý»BZ*!ä굫—.^üíömj#ÝÚ·Ÿ6uê‰ãÇ/œ?ÿñÇcŒŒŒgÍš¹Ì™«ø?¨éhŒ e°Ùì={‰‰ÉÈȨªª:{6öð¡Cµ.ÕÎÕµ¼¼üàÁoSRR¥¥IÍ ˆÌ}ûvpÈ ³gcùå—cG~õÕÙF"W­zöìÙСCbcÏ$%ýråÊ•5kV¯^µŠš›••eiÑjÍšÕª½.å)ÞÙäÂå?`œZ•••sçÎ++/ß·?›ÍVPÿÞÝ»„˜˜Ã11‡Å…“'O^·~!D$ ñOª ‚O?$®¶xÑ"BH¿~ýŸ8AÑÖÖŽ=¶|yXØr>Ÿß¥K—½{÷‰¿q†¹L˜«ø?¨éhŒ %mÛ¶ý‹ùó{t÷×ÑÑéìë{ðà¡àà Å‹††NX·vmAA¥¥åƒ‡©’s¹\nÜ•+ëÖ®[´paYY™Ý˜1Ë6âîîñóÕk6¬_¶tYaa©©Ç›2eÊß³E"@ ¨øKˆÊS¼³ÈÅ ·ô™ø$»¸Œ/ „¬j^SÕ£G¹º¶ ‘ÈB\0’*`–ÜÜܲ²2Õ–500 ·3 ¸ÐHª€A¼x¼£ÇŽÕ³…Ä„tõ€ šI|hº÷èÙØ]PŠ:Äh þ€¤ $9%eÞܹ,KµÅE"Ñæ-[… ¦·W q )T³°X,¡P¨Ú²YYYôv€!·TÆ©J¶©7nLŸ6­c®•¥E{7×9sf¿~ýZÁªóòòfΘáìÔ¶µ½ÝGzúôiƒ½J€º¡1.vìØîÅëH=ß¿KóÔŸµU·®]¶mÛ*Tëdbbâ–-›%K¢£¢ZÛÛÕº ’ÕÔìÆâCýµsqϕ܌œ©‚YtTTqqñøñ¡vööþùlÏîÝIII7n$ÊV®ªª9bD^^nDDd³fÍ¢¿‰28$!ñfË–-ÕßsuÚ¸i“µµuEyÅÙ³±‘……a+V¨ÐNbbÂýûçÎ'.1oiÞ®];úzÚV­ZmccC=×ÑÕiÜÎÃ!©Æ¡ñ~}Â#"Ú´q ÊoFhšTÆHNNÞ¹sGzú“‚‚ƒ¸3gÎòòòRrñ‚‚‚ð𕞞ÜÔP¡ÐÃÓS²ÄÄÔT$ZXXÔ·÷ C…¸prr’ÊÊËËKKK+*ÊcÏÄ^»v500PWW·o@@߀ªBg__OOnw¿Ç‰?–ðù•›6Euöõ·cdlÌf³íìíå®7|åŠV­,.\¼¤§§GéÒ¥«Üjáágbc©3Ý»wïß/ :jÓá˜#„Âbq86G—YŒgΜÕÙ×·Y³fÜß¶ukàÀÿ»‘@½!ÈnF$UÀ85}"ùò¥‰‰é”ÉSš·hžŸ—ò»“'N8yò¤››ü3O’***&N˜PZV{ö\FŠ0DCÄ…Xß>½ÅÏú}óÍfBŸÏߺeËÅ‹²²²øü*jî³gÏÄI•¾¾¾OçÎJ®‚ϯ¼uëÖçsçRUÍÕø7oÞœ5k–øô0‹Å0`à¾ýû¨I;;»×oÞ*ÿÒ”$JJJÄ“&&&„Çãñ¨’^½zùûw8 ÿÞ={Ôv’ 4’*`œšƒ 4hx20(°OŸ>.\tu­e )Ÿ_:>5õá¹ójªfjjRTT$YRTXÈb±¨·W€ÆE{\HÚ½g­­¶ŽNëÖö-Z˜S…_~¹øü¹s_}µ´“¡¡aQaaPP`Ee…x)CCCåoIZTT,Zµªå¤oQQQUUÕöíÛwîÜ%. *ßåAI)))ÿ=‡›—/[ÇËËËÑÑ1%%¹A{ Ih*333G«Ö;"òùüI'ývûöé3±\.WAMWW×””É’Çé[·n£øƒ5£(R¸ÜÔ·ÿ$9}zÖìÙS§M£&?’3þIy&&ÆZZZoß¾Q\ÍØØ˜ÃáL›6}ì¸qõY]]¹ºº~ÿýåZ«UW T¾µ=4Hª€qÈ­¬¬ùùyûöÐÕÕ1b„‚úÕÕÕŸM™œpã»S§|||¯wàÀÀË—/'%ýÒ­›!$;;;1!aò”)ª½ zÑÊ …UUUFFFâ’Kß_ªu)]ݪª*¹³ttt»ví;þúúú5¶ «ëççwófâò°0-™¡î ÇÐÐúv°¤êêjÉ>$$$¼xñ×ðÃÕÖ+Ð8Hª€q &OžLO23k¾k×.'''õ.\:!?/ÿÒÅ‹T¡ƒ£ƒ‡‡'!$>>þã1£wìÜ9bÄHBȈ‘#wîÚ9mêÔ¥K—Dmdd–mÄÝÝãç«×6lX¿lé²ÂÂSS37E|ÎX$BŠ¿„X']»u={66öLee¥……ÅGþrÉñ˜3YHª@Ã8ÿóÛ[}û„ ÙüÍæC‡ÖT9ñæ/ šêÓ·¯ÔpÔ–-[îÞ³‡–~¨SâBÒ”)S¦ÔpÛÂÂâØñã’%’ñ)ûc'*::*:Z\òÅ‚_,X žôððgÎçêY|Tã(yÙBK‹ãÔÖéÉ“tÜéšÄó!©Æ©é` $o1U\\üàÁg< )@\0’*П>×ÊÊÊÕ­¾¾~Ϋœ³çÏ–”¼Ÿ:mjc÷  1!.˜I0NMŸ°ýüý®ÄÅÅÅ]®¨¨051éÈãmX¿ÁÝÝŸÈ¡)@\0’*`œšcF3z´’•>0ˆ æCRŒƒã€,Äó©ã‡¾>x8SŒƒOä²̇¤ Yˆ æCRŒƒƒ€,ÄóaL p¦ ŸÈd!.˜I0KnnnYY™jËÐÛ†@\h$UÀ ^<žÜ_°¯S ‰ 7èê .4’*`oo/oo¯z6bemKKgÔàQÚÃZë . ©Q&.˜ Õh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €Hªh€¤ €ZÝBql똞þ¤mÛ¶õoª   þ¨ÇåËqŽmkš‹¸€¦Iq\0’*`„n]»B._Žk쎨•c[Gjç— qM“â¸`2$UÀݺvÕÐ(h8ˆ ‚1U4À™*hLEEÅwîÜmì^0 â@C!©‚F#ųX½» "ÅB’¨¸ÐuHªþùÊÆë ||CƒB@mªI¾‡ªÑÜïq(¯n—ÿð= QNöËÆîmðí? ©¨]FÆóŒŒç˜Ädœå!©PŠ••••µ-&1Ù'@I¸¥@í$1˜ÄdÓ™å±ÂÃÃ-}&>É..ã !+‡š7v—4@Äù=räÈwïÞ­\¹òðáÃ< „hiiÙÚÚzxxhkk‡‡‡[ZZ¾yóæêÕ«+W®lÑ¢…2½ºÿ¾ŸŸŸ——ׂ ÷îÝ{þüù¤¤$oooBHM-+^JvEVVV–––S§NݰaÕ™7oÞØØØ¬_¿~Á‚Š[‹‹‹4hÐG}4}úôÜÜÜ¥K—–––Š·Pr²_Þ»—œœ’RkM/ÏÛÛKù–U¼üwôèQ@0fÌBȘ1c6oÞüÝwßMŸ>½®íTVVîÞ½ÛÏÏOîÜÈÈH__ߘ˜jÒÃÃÃÕÕU²BIIÉÖ­[ûöíKéß¿ÿ7¨¤ªyóæ&&&l6»M›6TͲ²²´´´ƒ2„*ùä“Z[‹ˆˆèرã‰'X,!ÄÙÙ™ÇãÕ²õš*£ [¾¼Öš«V¯&„(ŸW©8PýðáÃ\.—Jq|}}>¬B;ݺu“;«¼¼üîÝ»C‡—¸¸¸´oß^²Ž¡¡aŸ>}Ä“¯^½ªiEíÛ·ˆˆØ¾}ûÇE"‘ò½âóùñññ£F¢²B‹Å ¹y󦂖/UÓË }õêÕõëשÉ#GŽôíÛ×ÊÊJqkÔ¶1b•QB:vìèââ¢à54MJfT„qcÇ*sBKL•¤êîÝ»= )üÇàÁƒýõ×§OŸÖµ)###q ¥¨¨H(š›ÿgŒ—Ôd³fÍ$×ÒÒª®®®i]qqq={öŒŒŒär¹ÖÖÖëׯ¯)µ’êUAAAUUÕÆõ$¬Zµ*??_A˵.%÷åûûû·iÓæÈ‘#„Ç'''‡††ÖÚj[YZZJ6eeeUÓ¦hâJ¨k›ª\þ£NJ­Y³fÍš5’å111«W¯V¡A¹¨ëwyyy’…ùùù†††ª5غuëC‡B?~|àÀ%K–X[[?^™žp8œyóæMžPÛª¨¨H²°¨¨HåmðaS!gªUÏTñùü'NøúúÆÿWÇŽ9¢ø²ZèëëwêÔéüùóâ’§OŸ¦¥¥)¹¸®®nUU•ÜYnnn›6mÒ××OMMU¦)==½^½zÅÇÇ;99¹þ—‚–•_JÊøñãKJJΞ={ìØ±áÇÔÚj[]½zUÜHNNŽ’¯  ’:)ekkÛgª._¾œŸŸÕ«W/ÉòiӦ͘1ãÿû_ïÞ½ëÚfMV¬X2aÂñ·ÿ,,,¨ܵrww/++ÛµkW§NtuuMLL>ýôÓQ£F¹ººVWWŸ9s¦¼¼< @ÉžDEEùûû÷îÝ{ÆŒ666………¿ýö›P(\»ví‹/jjYÁR Öåâââëë»dÉ’W¯^Q×þjí!dåÊ•ƒ úæ›ofΜ™››;qâD]]]ñ²?ýôSPPPLL 5æ ‰“Ì™ !­[·ÎÈȨO›u>Suøða##£Q£FI•üñÇúúúª W¯Ippð‰'n߾ݯ_¿eË–…‡‡·mÛÖÄÄD™e‡ 2uêÔ°°0__ß   SSS[[Û¨¨¨þýû‡„„Ü»wï»ï¾ëׯŸ’=éСÃ;wlllæÍ›×·oßÏ>ûì÷ßïÑ£!DAË –Rlüøñ¯^½²±±‘ÌP·tòäÉ={ö÷êÕkäÈ‘]ºt/+ P(Tòõ|ØÄ§£¨ŒŠâèèXŸ3Uu¸ùg£ßc÷Ý»wööö«V­š?~ãö4TddDØòåïÞ½#„È““žžN=)**:zìØgSj­úÍ?Õ¯¤¤dÅŠ}ûö577ñâÅúõë›5k&yE @Ô‰(¹cµUÃÎè¤JKKëéÓ§GÍÏÏ711éÕ«×ñãÇeïP' ñí?F'Uzzzø• ]“Kª’*0âæŸíùóç Ñ,ÎT@âÅã=vLùÊÊ·Œ¤ 4Ûû÷ï_¿~Íb±œœœJKKµµµutt»SЩžTݾ};:::111//ÏØØØÇÇgÊ”)#FŒ ±sbׯ_ÿí·ß–,Y".Y½zõºuëJJJzE .|øpøðáÖÖÖÚÚÚ“&MÊËË£fÍ›7oÍš5EEE,‹ÅbÙÚÚBV¯^->”R«KLLìÒ¥‹žžž••ÕÚµk%;vêÔ)777===sçÎ 4HöuÉ]‘¸{={öÔ××—m\Á‹’²qãF]»vI&OmÛ¶]¾|ùýû÷øáªdܸq:u’\°W¯^C‡Uf²[þÂ… ,ëÞ½{’ H­¢¦Åk]ãŸþ9tèP333--- ‹   ÷ïß+ó*(r·yMmJúøã @ñóó3”°jÕ*9›¾.ÿ&h ª•P×6U9Suýúõ=z4oÞ\AÂÂÂyóæmÙ²ÅÍÍ­¢¢‚rÿþ}???//¯Ã‡îÝ»7 ))‰ÊÃ^¾|Éår'Ožljjúüùóµk×:ôæÍ›„+V‚Ç?xð€"•ɉW·`Á‚­[·º¸¸œ:ujÆŒÎÎÎ#GŽ$„\¾|y̘1ãÆÛ½{w~~þòåË ¼¼ä\­iE¥¥¥cÆŒ™9sæ²eËbcc—.]*n\ñ‹’$‰¨í&û3;Æ ûâ‹/®_¿TëÆ¯uR[ÞÞÞÞÎÎnÏž={÷î¥*<{öìúõëâÉzþㆠ¢­­}èÐ!KKË7oÞ\½z•Ïç×ú*ÄänseÚÜ´iS``à„ Ž?.þ9Ì®]»VVVª°Ñ ©iˆ3UuNªŠŠŠŠ‹‹W«¬¬Ü½{·ŸŸŸ¸dñâÅVVV?ýô“žž!¤OŸ>;w^½zõ¹sç!TM???çááñðáCOOÏæÍ››˜˜°Ùì6mÚ(XÝþýû¹\.!dúôé8}ú4•÷DFFúúúÆÄÄP5=<<äþ$5!¤¦•””lݺµoß¾„þýû߸qCܸâ%©¸¸¸¸¸ØÑÑQv½ööö,+33³æÍù¯Z×(»å§Nº~ýú¨¨(###BÈÞ½{ŒŒ>þøc¹í×éWVV–––vðàÁ!C†P•¬Ì«“ÝæJ¶icccooOiÓ¦øÊápä®Eù4vŸ*ƒnݺ‰'ù|~||üÂ… ©£!„Åb…„„lÛ¶M\aݺu§OŸ~ñâ…ø´Ä“'O<==•Y©©)•QQ^½zE)//¿{÷î×_-žåââR×†}úôOzxxP×ú¢êD™[»*³F©-Oùì³Ï"##;6}út>ŸèСqãÆ5kÖLî*êô300hß¾}DDDIIIÏž==<|øž={!±±±¹¹¹Ó§O¯iuýÇÅÅÅõìÙ322’ËåZ[[¯_¿^$ÕúB£·Í:ý›àƒçíí¥dªäÅã)?¢Ú™ª>}úüøãïÞ½S<¬J’‰‰ ‡Ã™7oÞäÉ“åV8vìØÂ… çÎKM>|øP…ŽÉ]/›Íy§äç狹׳qÅ/J‹ÅêÓ§ÏO?ý$»Ý.\¸@  &µ´´¤rqrY§5Jš1cF¯^½nß¾½gÏž®]»*yþO™5¶nÝúСC„Ç8p`É’%ÖÖÖãÇWð*jUS›J.^×—Mʽ{ÉJÞªjÕêÕ„†½OÕâÅ‹+++§N*50þÁƒRß2ÓÓÓëÕ«W||¼“““ëB„B!ŸÏ766ו\\WWWµeúúú:u:þ¼¸äéÓ§iii5Õ¯ÓŠ¿(Y .¬¨¨˜={¶d¶ñüùóÕ«W{zzЇ”ÙØØdff …Bj²¨¨HœbÖub={ötww_¼xñ7œ¦ªÏktssÛ´i“¾¾~jjªâW!EÁ6—jS5*o4ø )óÏqcÇ&§¤(ß²*gªºvíºyóæ¹sçòx¼ÐÐÐ6mÚ^¿~ýôéÓÔI¹¢¢¢üýý{÷î=cÆ ›ÂÂÂß~ûM(®]»–ÍfìÛ·oÈ!mÚ´9sæ u­JÌÝݽ¬¬l×®]:uÒÕÕ•>U«+V„„„L˜0aòäÉùùùaaa5ݪ®+Rð¢d+ûûû¯[·îË/¿ÌÌÌüôÓOÍÍÍqâÄÊ•+Ož<Ù¶mÛÈÈÈÍ›7›˜˜È­\×)~Q²/^ìííµhÑ¢wïÞB:vìøÓÿÛ»ï¸&Î7àoHH¨š(¢‚¨8XE,q0Ô:T¬,P XQA©e9«Uê@‹E°Õº°Øj¥¥Pke•!;ðûãì3.—dø|?þA.ïÝû<ï{÷æõîr¹r…ÃáàeLLLŽ9¥§§ÎáLÚq‹-òóó[¶l~¿6I5~ôÑGC‡‹‹+))¡R©ãÆ;uꔽ½½Ä,ø ´ùãÇÅmS‹Åâr¹ÊÊÊøccã!C†H•B¨½½Çãá'Õˆ_èºbRE‰ˆˆÐ0÷zú¢¶¡…‡Ú2OìM܃5‡Š{«©ªªÒÒÒÚ¶m[```wÇ‚V­ZuèС³gÏ ?SîŽ=êå啟Ÿ—½|ȾøbkxXXcc#ÿBmmmì‡:ø½|ùòxjªÏJ±·äný®!¤Ì Ìz™{¤«©ÐsÔ××oÞ¼ÙÎÎŽÍf—””DGG«¨¨xzzvw\!”””TRR²dÉ’~øAä#Îå"??ÿùóçáááÎÎÎ0£лgª°§Hjkkwf›}RE£Ñ ?^YY©®®nmmýí·ß ?Ö¼[Ðh´ÌÌÌ®®eíÚµ?þø£……ERRRW×ô ø¤J___8bĈ¢¢"™·Ù÷'UJJJøïìfeeuw@Ï‚Mª„/àèëëȶ;?©€MªD>eIæ{ØaR€Nz¤@ï“*9€I€tŤJ–ßþÃäää899±Ùlƒ¡««ðêÕ+þ  …ÿú¼¼¼•——cKüüü(ŠÌ1Hë÷ßïŠÍÊx¦j÷îÝAAA'NŒŒŒäp8>üꫯÒÒÒ²³³G%r•»wïΚ5KOO/33³ÿþˆ@F¦&&ÇSSÉ&¿eYÎTݺukݺu‹-ºuëÖêÕ«,X°uëÖÜÜÜææf‘¿’vãÆ {{û1cÆ\»v fTè.ytQaYÎTíÚµ‹N§'''+(¼“éêꆅ…^ºtIàg•³³³'NœxþüyUUUj—ð°0’%·EF’߬Ôgª:::²³³­¬¬„éûUàììlþ…—/_vttœ:uê… `F€ÞBÚ[¯¤>SU[[[[[«££#ü–––…B)--å_¨««›žžÎ`0„W¡Ó銊ŠÒÆÐ=ëÛ$9::>þ|çÎ"ße±Xjjj]? ÒnSê3Ujjj,Kä ±?ÿü³££cذaü wíÚ¥©©¹uëV&“*°ÊÀ‡*m Í™´µµ…ß*))‘m›RŸ©@=ò”_IDAT¢P(¶¶¶?þøcUU•À[ééé![[[òûöíóôô\¿~}||¼À*¾¾¾¿üò‹´1tv.ª¸¸X`yqq±Ìgªd¹üÜÔÔäëëËÿô„âââÈÈÈñãÇÏž=[ <…B9|ø°‹‹K```JJŠ 5È>s***½×Ë¡)S¦DEE…††–””xyy±ÙlìáŸt:ýÔ©SüÏYÀQ©ÔÔÔÔ–––5kÖ0 oool¹¿¿bbb[[› aȆÎTPP0zôè‚‚‚NÞ½.ãÕCBB¸\n\\܆ êëë‡ âáá±aà ±5Ñh§NúøãW®\É`0ÜÝÝÑóDc‰ÀôãñãÇŸÈþƒÊvvvvvvüüüüüüø—Ðéô .ð/IHHHHH9tÅ9Ù'U½Lªä&UrÐ+Ÿ¨Ðs˜š˜ü™äã©©¦&&ä· gªðárMBÇSS%–451Á “$Ťêïeä ôL\®©T³%’àò€À¤ @`R 0©˜TÈLªä&Ur“*9€I€À¤ @à·ÿzŸÎË{𠻣ú&iãO˜Tõyø¬\ÑÝQ}Óƒ‡ä2©‚Ër“*9€I€À¤ª÷)¯¨022~÷ .6ÎÆÆ¦»Ãy  !têôéY³f›L¶œ,qÅýûLœhý}÷Þ½C‡‘¯T\y<˜'Naÿ¸\³¹sç}ýõ×<`› ø*ÂÿæÏŸ/nEâáO³3øÛY6Ò6ò{ÎKÚðd ñØé|#¿OÝÞré2òYð—,//ßµÔc©™™¹¡¡Ñ‹¿^¬HÜïwîÜ8اM³’*{”Ï*?ßµd6"ÇCCŽ›zŸ ù¶"Öuƒ ܨÞûääd÷ë×ÏÈØ(;;»»cyØ?ÿü³sÇNWWG'']QªÜ»{÷䉓+V½1_\y<˜‚§OBaá› ÔÜÔ|éÒ¥={âkjjüÅm“Á`ÄÅÅâ/#"¶jkky{{c/UUYRe$wxj2oAÚF~?ð¼»=¼Î7ro$sÈeêß¿Ÿ®îi×*+-˼œ9nì8c#£{¹¹ ³n]°††ö7]Êá‹€\ö(9¹rÜTozhmmûyPPNÎu|I`ºråÊܹó¸\óyóæ]½zU0<)‡;⑹¨°0 `êÔi¦¦\G§ää‚MIÛeHÔˆ$\† B‰MÜV˰ÇÊœ©êenÞ¼¡¨¨8Ébö²±±)$$ÄÅÕÍÇÇ'++kïÞ½ÚÚZØ9•Û·ïøúúN™d}ê·©Xá§K—.300ˆÜ¾]Y™yæÌYŸUÇŽ3Æ@ 0£ñ†“§L MLJÐÕÑUQUAýóòŸ‘#G~<ÿc5UVÙ_:tè³€ÀoŽÈnõ'Ÿ´óxçÏöÌ„•F%®ZdyáVð×_/h4šªªê´iS544ÒÒÒ¶lÙŒ½UZZzïÞ½-›7‹k‰‰444n\¿qÕ*mm­œœë±±± Zºt©ÀvˆÛÓÿ3*U122r{@UeÕí;·[[[E¦V[[¿çË;·®“••¾«Ž 5»évÂÖsò262Ù§µµõ111!¡¡#FŒhij×AüjkëcccCׯ®­y%3rÛvüèàwðࡤ¤¤ð°°ç,ÜÈ1™Ì˜˜¨%K–&&$øWVVmÚ´ÑÊjÚb7W2M*ÕHÐ×ï­ ÄyJˆ;”äˆ!‚1ãééùúõk%%¥É“-?ú|ØÐaâ65Á|Âæð°ŒŒŒÀÀ@55µY3g:Íq6?^¸$ÿuãæuëBfÌœV]UÿeSc£Á¬¤´Ãqw?y’ïå奯§¶‰ÃáüYògá³"$×.‘ËGHÜÑÄm%1`qÄ}pÈLªz™kÙÙ–“-éô7çTÖ¯_?qâD„¥å¤û÷ï_¹r †×Þ¿›F£!„ Ç;88~}øëMa›ðu7lÜhnf†242œ6Í*ëÚµïϧc'l^7DlÝú÷ß<!´{÷6›½ÿ>¬ê &¸»»ïß¿/>>^ 0:>€ÍF äp4‡¼Ù¹§L2eêìoc“Ñ£ æÏŸ_TT¤¯¯ÏŸºººªªª…‚¯H\µÈò­„jjjjhhhnnºxáÒÍŸnÚØXcï.\¸ððáÃÁÁA***¡3igTT”g;Ì×þill ümmm  361×5ÂZ[[¶FDè‰rYärîçð£ÓÑÑsöÌÙÝ»w‹kdâ=ôhƒ€ÿ¸¸Ý=ª @ýbëV’M*ÕHÜ×ï§ ÄyJˆ;”äˆ!‚1SU•µÌÓÓØÄDYY9?ÿÉáÃ_/õðL;“Æa³EnŠÉdÎ7wî¼¹ååå.^¼qñä©SZZZÎÎÎNŽŽC†ÁKòïQ)I)£FމަP(!mm­E‹\ñû$¤îˆ÷ÏÝqql6ûðׇ BÈÔôÍÃ-åØeB#’`â‰;š¸­$,ޏ¹€Ë½IkkëO7oÚò}{EYYy„ øK=}½^½Bµ´´üöø±½½=í¿—Ãá˜O0¿ÿó}þu±!Äb±Øl¶……~ |Ä„Ыòr¬ÞÜ{¹3gÎÀ …bee÷àq Gž’²oÁ‚“&Yr¹æ®®n¡?þøƒLÊÄU“i%„««›…Å$++›è˜˜)“§DlÙ‚-_°`AkKË…‹°¿K?ïäèÄd2eND‘F³âû ²Ÿ1½¦¦æù³"òI)))éêŽHNI>qâdQQÿeáÔéø@‰²œ4 ¯NªÔzZ^”””ŒŒ c±XØŒ 3dÈPìèÀðÚ;BC×g|Ÿ±ß>þ¹0dv?ËÉ–¾¾koߺ½cÇöúõ÷Cܤä@ A_óßu]@™ñ‡¸Ce1$7f"„Ædgg;i’ÅòåË“S’«««S§"„x<^mr8¯eËÒÒN;wnÆ ûsçÎ98:nÜŽç‚·mssóã'Oìí§c³„ШQ£µµµñ’R wÄå[[[~ÎË›={6£"&[— ï6eÈ|vˆëhâ¶"p·€3U½É½»÷ššš­¬Þ~0™Jø‡¢R©ímm¡ÚÚºööö~ýð¯> _ÿ‚ü|þuùߥR©üŸ¸*!ÔÞÆÃ¶ÖÚÖväÈÑ£GßÞÆÑÑÑŽ?˜@80Ûwìȼœéçëkhl¤¢¬RW[³ÔsYs³ä‹8«&2˜¨¨ƒ5ÓèŠC‡höë×_>`@Ûévi§Ïº,rÉÊʪ®®Z䲈 ‰‰¨°Xüg>ê×!TSS+UR‰‰‰ÉIÉ))ûª««ØìÞÞÞ E85Uþ{JXêjxuR¥ÖÓò¦¢¢Ì¿«“!ðÕ-Š¥ïÆ—†×¯øá®w¼á;×køƒ!³ûQ(g'§›7nŽ5;ÿ‘ؤäÀ7- ¾¯q]Ú䑈;TæC"qc¦°ñãÆiii=þí7„Ðo/õx{¥õáÃ_E®R_W[_WßÐØÈß›üm[WWßÞÞÎðNËp8ìi‡;âòµµõ<Íæˆk MÉÐe"F¤wËḦ £%¶•Ā߃ÁšCªà_“ªÞ$;;‡Ëå’ù²‰šKAA¡ºª’aeu•ººº õ²XªT*ÕcÉü¦i»qÁËkÙ%ØË¢¢"q%¥­šd0Ø·ÿ„¹º¸,_¾âá£GiigŒŒŒˆ¯/HLäu]]kk«¢â›ó««BêêjR%¥©©¹-rB¨¸ø÷sÿû_|ü—Î@gg'áÔêëêZZZðÿ–¿*篎|j=-/2hTªÀÍÄ-Í͈¥JruK566Ö××/$44&:ÿÿ.0dv¿ŠŠÊ¨¨ƒ‚‚‚Ôã©xʼϋCÜ×xR]ÔRµvçǹ·žlx<¢PBzººGWì?þÈÈȸxñRYYÙØ±cV¯Z5ÛaþŸ7=JAA¡®¾žõººze¦2’~¸#.¯¦¦J£Ñ**Ê…ßUX–.“¸ÛḦ £‰ÛJbÀȬ94ãûóiñ/„˽FGGÇõë9¶¶¶d ÓéôñãÆ]½zÿAyEEî½\s3sª¦Óéfff÷rï 6Lç]dkoookkSQ}»7gee‰¯‹Áÿ=⪅ËKÕJ333==Ý=»÷Ü¿Ÿø4™DZÛÚ®_¿Ž¿Ì̼ª®®®«÷ÎlFbR¸#t‚‚ƒ ƳgÏD¦ÖÖÖvíÚ5üååË—ù«—š@£õ´¼Â‡3hàß/_¶··c/ëêë ¥üèår¹ÉII·~º‚Õ(ŒÄŒ:::ÂÂ6ÑéŠì÷ððؽ'¾¨°I¹Ï“DÜ×$ÆIÛÄ­-|ØvfüéŠÖ#£íÝ”ïÞ½[VVfh8!¤¢¢bbj‚ÿà ÔÕ×?vÜÍÍmΜ¹fÍž•žþ݉'Ü—¸ã3*¶e0cÇŒ¹s÷^KyEÅóçϰ¿¥îˆË+*Ò¹¦¦/]jjjN¶ó]Ff°%ޏ£%¶qÀRí±2øoF%ÎTõ=*¯¨°õ˜‘Öøú~úé§Ÿ­õwswmjlJII¡Óé^Þ^²Õ¾n]§§×Š•+]]\XWWÿè·GíþþŸI LAAa¢ÅijgÎÚÚØ ÖÔ̺z5--M\a]]ݦ¦¦S§O3–AWÔ9’ jáò MMRµf‘‹ëÎ;°ïï#““Éܳ'¾¶¶V[[;'çzFFƺuÁ·#Âö|ñâÅ–Í[ìgÚëèŒàµµ]½’ÕÜÜlaa!²™LæÞ¯¾ª]¯3\'ëÚµÌÌLêD¦&ÜÈ=*/áðDvÇ ûé‡JNNöôô¬¬¬ŒŠŽ¦Iÿ-S“})É«?]³.8xWlì“'O‚!Þý¾9vìλPSS ðÏͽzòäIƒA~Ÿ'Ib_K ¸3]@ÜÚÂå;3þH5bttt`ŸÄO Ÿ"„nüt³¿~6»WúÖ­Û¾¾¾Û·G:88H¬7À?pà cÆ0J§OŸÖÐÐðøï$а¼¼Ÿ“’“¦ÛÛ›q¹"¯f ·íê5«ý|×;vÌÍ͵ªª:,,?Œ$íoÒŸ}¾l™—···§§'›Ã)û³´ ðé†õëEnJÚ.#ù‘D¡ÄŽ&n+‥Ýc‰³€Ï¨¸\ÓŒR¸ü×;ådçŒc0hÐ ’å'M²HJJJNN ø\QQÑŒk³KâwMÅ9rÔ‰ß&%'GG節­QSS7vìâÅn$Û¶mÛ_ÌŸ¿€NW461Ùç¹ÌKdIë… &&$ÖÔÔp8œ¬¬«U —wrr’ª•03gØïܱcÎÜ9ü_”-eefTtTôΨ‚§…ýû÷ þÒ;"lO–šÚ AƒŽùæåßSôõõv튙4É">þKáÔ”•™ÑÑÑÕ‰LM¸‘{T^Âá‰ì‹Ñ£ ¶EnKNN>|èð0­aŸ|òIKs‹È’ÄŒŒSö¥|ºzupP¶öp`2ÊÏÏßûåÞ+–›™™!„cb¢\\\wÅĆ…o"¿Ï“$±¯%Ü™. nmáòÈ·Ç Æ_b·‡O›:-!ñ+„PG{;ÇÃOWãš™^¼xùâÅ‹---l6ÛÙÙyÍš5ü·` 051½~ý:ñ !<þKA˼=ùËK5ÜI Ž;š¹-²±©iðàÁsçη)i»ŒäGq„ÄMÜVÄK»ÇgÁF%|O%""BÃÜëé‹Ú†BhË<ÑßÝèÀÁC>+WÌ™3×ÉÉiÕ*ŸîGP L¶`ÒÏŸ OOÿNÜ}W=pjq±q2rrrÖêù©õýç= Ó×2ë±Y÷}¸mûXjØç¬Äb3ªŒïÏg”j!„”´QƒY/sÀ™ª^ãüùôîA´˜´Áÿ^ZögbB¢••uv`újj½zÿé>̬ß>ܶ}85q„gTÂe`R>h;wîÌûùçñ††aa»;9ëéÀ{&rF÷TðŽöww²  ÷n¯N  îk@—7£¾§ © ÁŒ û»­¥/ “*Ę̀^ýñ[[Ë›ÇýÓÚ:÷ü+𘚘8x¨»£ú&SQ¿í-qF…ÝS5pøxjkk£ü†è!¸\S.×Tr9È™Õ`Í¡m-¥4ú›ßyäñx”ÐÍ;>åðW=jmë Ø:@µµ4ÒèLìÚß@C&“9Dµ½ºà’¼Ù6E9­­¤8 ð!{sUk3Î8|`4ºBˆÂkTlü‹õ*‡ÚöÚÆT !D£R©4ÍÚDkΜ9k×m¡Ô¿ñCÛà] ªÃñzÊÊÊ4J¥þ'û«¦qÒÒIEND®B`‚PyTables-v.3.1.1/doc/source/usersguide/images/tutorial1-general.png000066400000000000000000001371201231437614300252510ustar00rootroot00000000000000‰PNG  IHDRjyí pHYsaa‰f΀ IDATxœì]w|Eûf¯·ÒC ¡'@ éÕ." -‘òŠ ˆXà ¨T¥* ‚‚"HôB@¥C©!”´»Ë%×v~ìÎìÞ%¹$g0¼¾óýh¸yæ™gf¾óLÙ]túôiÞívó<Ïó{9ªpY »8}·)Ç)8Žã8¥Ëét»Ý»žÑƒê¶ì­T(jZK†û§ëÀ‰ã¶"sÏ6M”J%:vôhú¡³†ð&¡aQyV—ÍÉ»Üàpó€`—rH„Kúd?JG(_Š,—‹aÏhᲄÊ3#‘©¢ƈê& C#,–‹&Ô'ш‰=2Àœ,IC§ÊzHxHF ‰j…B!À˜š!ŒÅ˜ÉòD€‹™`RNA>BT}$J+­5¢v@XV.¹±Ë*©BŠ'[®› 4ã‘<…›%¼K‡1±BÆä–ÜDX24‰ ˜—äcáñR–¯¨-ÂäºTIïYÕ°GI©ra„±wÆ R¨z)oyÆ^}FÖþ½;…W(«s•îw^Y•“§W*Á¬Ô N©š Ñ«îܹQtçb÷Ô†J‡Ã¡Ñ™‚"ÎÞ,.vòP@€Ä¶€Ø:1„Ѐˆ¥’ ‘%HãFbû:„ð‡4zbX±ñérÊ,vuÒ§=(ˆ€<_ ­Il@’e‘ y1ˆ¬MÐî(Ë—]ó€in¼¬K‹ V’'&Å”D"žd/äÎKùP&¯ä„ „_Aê|b.´‰z„¬ ^b’×" Š`wc— ™í¸ÈÎ[ЉjB‘ïÑ–É?Ëj–c¤†0¸ÅzñèðcL‰@T –ˆË(Žæë]q²Ò! nÀÈÉ󤯋$Nl„0`Ò¾‘Üz²¾ €@›§ìŒcLLI•LläHd\„…ÁCÒMO깉t"$”MHI스>ˆ¡4eU Eà~ܱ8„D¹‹rìv»"6.) ¦}A‰»°ØýW¤Ë!4C„¨¸|¤z¨È HbLâKž‚T+²Þ!Æ ùHy":Ê „H˜P{È+6Á•èðMþÇDȲCBöȘdޤæ@z*M*”B´@‚‚b;‹F› ¢ cZ:ƒHHüÓÜ1ÉEŒMK…dBh«“Ê^i jÒ©AÅ¡ÓÙî"péP`T'…Á¨FA±K¬Lʺ˜ºRÇE”G°ÌÚXL‰ ÉD!Ò-(£ŠE#c ¾PG‰ZàÅŒ“aƒ=%Úx„þÉó¼Ûåv¹cѵ"VGÛ³‚C ¥¤ž¬Àµ<Æ„4ÄÒ STi-Ÿ˜’dØír¨ì]£BUTâ¾UàⱓCH­T·ˆ oãØ÷û­B·J¡PRORlA”ê¸Ü® ¥£l! ›×wüøû-³K¥T*#,έ¥i ¦J7Jr2d2ieIpðY>ò¬N·Û¨rtKª@…BYfp‘E¥Pn죵”ï¦å¸ë«c娷.H¾žÞ¶KOyÑ•}ú UÈ|y/£2¥âÈ$‰ÞŒA@Ö‰Ý síêT­‘ꉯ@µÐLäSÊy2M„R¶nJQ&BH< ‰ ™0Ù  Ò]2ÆF©:Ä€µÄ“oÇÄù•‹ƒØ”éüÓ¡žæDJ!Sß T2¢n¢d,A3Á•7¢a¨Ä…µJtê_`ÃM#Qa g.ÆdZNˆÃ“5„9%5Y›ÀÄå ɰ$•8àÊœ©Jë"Bޤšeõ&ŠÌc>@]Ü;©¾¹˜·Xùæ¢Cû¾Ë¾’Ó°Ej—Ç “NÙ;¥ÎG¯™ÝHÁ)j"ë$b3äÝ®@µ½wRŒ ÄZâæÉ€ÍqÈhW˜tʇSêìʼfá9qÆ<ÂÒ¬®‹PÃfDiÂcÚ"÷G¼üJžw©KJ®_¡2f7âÊòA¨·Q6»øÆµ³WÏd¤´»õÒG¯Ä®ýµãѺÚNzø „Î1Ÿÿç¦B…¥u޲_÷S¶Ð¥K\<ÈW¦1eÀdyJ¬YŒ©o ›vxµtqÊTšA€z²Åk:cy×!K]¤ÕÈi¨ÈÉ‹2ÈPš s$v ‘Fd¦¡ýœzéâ¨H‡¡Ç‹> uF(Èí¦²(ŸºÏœ¸jA,65›D`ÔBdX”ó‡Ì^•.£XêhÐTÄ3ÆX£‚a܉뼋ÇJ×8¿…¯çCýtüOÓ SÑX¤4@”Èn &EîÒø^u‡ÉE&g¶rú‘¼ÈR›,t] cì*¶vn[ÛjçÍ6§Íîþ`úÈ+NÀ¯é_þq8}Ìë‹1ƧêÒ2ò›ƒ×9½!ެ™ˆÌÊón·ÝÖ%1ZRìà9œBÊÔfw Bº&D~}èºJkDÀÑ‘KœŽ“Î@ÛY¹ÓB$eN[0æÝ%E]k[íü…‹—~øn]燇EÕîÞʾüõ®MõÚ¸Qî ‘_¼ŽtFK!U¦kg3>' n]¾’ÖîJZ»ßÆ>¬yrnÜëÅ”>hm 3…‹·lB·Ð(9R8—]$LUêÇö®bRë5Æ`)v]Ì)þñÜ‚b—°|BË®×($„4«cDçnmϼc³»°WÝ è`MÇæµb#t:5Wìà/åØöžÈ˵8¤9e Ü¿}d‹|¶ïF›Æ±z8Ÿ]´óèÝ"»ô{0²E=#|úãõš5ŒÔ[Š]K¿¿ŠÔ®¥é_«^¨V§áJüÕ;%ûOçÝÌwõ÷t»ÈæõŒ°áçìäF "tp>Ûöñ»6»èóE©Œ®¦Ó«¹bÿçíâÿœÌͳ:+>Õ."¾®ÖíËNm¡³–¸íNwt-­PÚ‡’ÂJ €Mûo^ʱµm$Ä_±ëZžÅ‰0Ž Ò´‹ ª*Zãòíâ§ós-yCLÊÎ-Bê†iuj…‹Çf›ëN¡=ý·»ÖÏ¥tºš#Ÿ@ˆ½—îD@DʵòV'îÐP¿]ЏÓ9|L2hÁZ‚yâø!²ÑI=ÙTƒÇ€¢ya¹c'zï䯌×äÄ!ó!鎈+‚_À «ž"sÑÍ.Œ°Óéh¡R©µf‹ÃéÆJ²šóUjMʃ½?¼÷ôoûÿu×]·ÙÝ!&MÓÕ¹|§Z©¡ì,äër¹šE(äBàÜñÃÛ>_üäÐI[¤€Ó!ÍÂUgóœjµF.8„$ ì ŽžÜ!í¶ˆö²ƒ#Õ¸\θH¥ ÌÏ»¾<¼÷«3Ç~ÿÖÊðè˜Û7þ\1{œµ0W§ÕÔ=%Ĥi¡:›çT©4¥º˜_K¬e¢n³6Âk7áÏk`‹«ƒ I?zCGãÈ'`¢W%4/¹oMê³N˜6&L§R Â"—˃ ªä†cz×U+È(ÁàNQ­c´*N£âê›Fv¯Íq\éq²I´~ìCu[Æõ…ÍîÖk õMÿz¨nD Fl³¢=‰{)0¨ctÝPË5*®eŒiX×h'î ú?_רQqB56ŒêY'®ŽÁ Upé5Šfu £zÖimÀ èU7Dëtaµ’kQÏ8¤KÇnex¶gøºFƒFQdwë5ŠõŒ£zÖ Ô€4@ômѬŽA£â{Z4¼ü‚ÇÇFê†w¯WG°¯×(š×3Žè^;

    šQpçº( ±cqv ã [ò˜¸erÆdåŒîþ—0vÚ‹©2]T·^;×/žrétƺÅS”àªW¿a×GÊ•)¯µÞGò¹4£¬€wVÀG³,÷J|,@k“70² …°´Œê± B¦+øìµ¢Ì æ;vŒ€x()¬c|pA_×øÇŸ,KTPäZ¹û`Ð!2&L¬iUßxì¢E®B¨Ï ™m®v_Ë·9Ã5ÿê]O£âN]óã a&Lý/Çõ|vÑæ_sÀÓí#[Ô3Fkê›~»TH#¸y¼æÇw F%Ç¡ÇRÃ8„x [~É9Ÿ]Ô8Ú0àÁH¡ÇRBe[åëÑYÙE_¼žn_פic:~ÙòXj˜‚C–bךo¹Â‚Ô£zÔѨ¸ž­C6ütS¾ÝìæñúŸ²o:‚ Êì|{b¬éÑ”pØyôα‹ÙÑ ¹5¸GSÂùŸí»QXä PìQG£âº· ýü?7‚ j“N ?üv÷è3èÔ\l„Þæp{Í ËhBcÀ Mù;V¸eÁ.7\ËãÛÄ*ÌØ\ jr‹0`((ÂaFtÈÌNÜ 9N¢uDû.ˆô+ 3¢#"ãzUèÒÇ «DtbixäˆÑ°¸éF—ƒxÌ;íÅAAÆ¢Ìq¢o p:ì‹ÿoDö• AAMâo‚ǸVP€Ó~Õ­àœÐ©17%6¹Ü[׿Z5]¯Õt:Œù¯VM4~V­ðÚ’’+¼>@aò+ÍPš;ÈF&ñ;È."A¾S¦LDTíqo,Þ¸tšÕœ·ý³÷”×­WÐøÙÁaÑ^ÊpÞK"Dú(5” ÓÒú'e|× 6ød ¼ü,Ä5¥ó@í T*.P!Ì‹6!,xð.ݱÅÕ6¦4 0i•Jµ¢J¡j^<­#"ýxn~‘cØ{"od·ÚÐ0Rì¢E–/Žª¥Ð+!èÓ6‚† ØâïéÌaàeKipð\›ÇPÆùa¶¡ûí’™²Ìž?rÿ¼] V»»NˆVè{—oÙN_³À™ëÖ?oÛDèM:eD°æfžö…Cç „ª#Y…Âä"6Bw»À.H@¥†0Š ×!ɶ½Çsÿ¼]Œl%.zx„]ê@PdJ”У)áÔ'@1áZqV;Ïó˜ãPûfA‘Aš»ÇíÇÙÖ vâÄ®ŽÀíÆ×òqýPN§†‚"¸kŇþt·U”8±V… løZ>`@V;ĆŠÝè(*GœØ#ž:…²mLEè¨C¯1Yz–܉0Ù𖥓x²¤$+/º4#Ìïr9œNäæy¥l©â·#?Ü ¨]¿Ùn¹yìt‚Ëéà1Ï'ð»\N*¤àîÍï×½§âøÚuêwëûÜÞ¯WY ¾_÷^ŸQÓ‚B"E!.'yaEŒ—M!x„O¸ƒ@$¦¸Xª@Ù®y¹2U§ÏðI;Ö/À<÷xÚä°ÈÚB:Oe<¦)Tv¼Ë•Œ ™³ž€-¿Ý<½FÀŽ A½Â m‰d 2‘דÀ¡‚a]j7«m(J‰À³r ‹\Â4¶P\½FéYûÈ@ØÇ¤S =‡‚CH«âl7`^l¡´¿Ž*²»…Ë"‡8ó7hòùõÍ<;Hw£ÍÅ.q0Ä`¶‘„Z–1¦­Dl™T²N­ ÚuJc)mÕJ®Øé¦åË)pˆûFÒé2(P¾¸JÃyF-Ê×*ŒZìpi”œÍéþá÷»ÝBL:e«X“pËZâÞ´?û–°\&d>ÂPP ïð1µP Ž 3á³9üÁKn¼[8LŠMZȹy:A<&SwâAЮ,8"ÂÈÉÓr¾‘ ˆåv¸Èc$œ^J"è.®=z¶H‡?&ÜWïXMz­Z)õkþ­ €€Öízõî7––8ø»v qòMp„8 ¹tüµÕ©×{àËÁá!c¦ÿ°qaqQáå“¿¦tí'! 0Ò ýEVêR~™©!¹•d>õÏä%*̽y4}c É(LuŽìù¼VØËµ"h‰@¾h^ •¥Ë•Œe!i£õpô*$׃Âß\ÏVCýøÔvO×–ª€LØ<„ޤš0D‡j[²á?7ó‹\ñµ úFÓhrÕ Ê»fƨÕ.²»¼ô," ~\¶|ùK5"Ý…›¤Šô”Ô2ªîš€!Ø "Y¸åÓ¥ç6ŠJÄÜtJêUPÝJÜòÞlTæZp°^”ls¸­DÂÉ+Ö¯åxLÂA:&À%œ€–†#2œ"Éñ¹‰²ÙEkœ¼jýöÐ-© Šò18vÑ|ü²%º–6Ä¨Šª¥Il`Ô*lüõÁ[rÃz4mØ€Ë ùE8¿ÂŒ\Tz°¡Èª. 0'n¾#‡[ìéÂ|ÓNà! ñbfâm²xAXB\À@˜¦ e–Ü *è®2Ð…YÒ8©¥1YDAt„Ìr8¤ÈÍ+T*TZ5Gø„¤öæÜk‰t¡. q±Ý}7¿!N\OÏò`„BÒªm£Qß°E{C`„„E>1üÕK'6lù Ç$¤T—ý©ÒÜA§jÒ±&R29wˆvñPÆVx÷à÷iT]7¥Û€#?~Qb3ÚñQç§þ¥‘•¨\þ¨ìÒiÇ0®#¼³àµ^ðBgñÖƒO½8àõuu·¢‘=NmI«žd1FÈ ›Zvž=¿È‰ê›<¬&[›è™Rˤ 1ª»· B.Ü´y” ãì¼s± š×56ŒÒ Ì Q´môHrm6d1 N!E·–µj™TÁFU×µ„‹96ùÄ_ÒÃÍ|»¥Ø±úæõŒ*j^Ï®K±+'ßcôîÒ²V°QU˨îD$_ʱåä; Íê„ëùz­"µqPïÖ!žŽ•8_Ž­ðN—x7,@͉} K^=æá&•_Û®VôEjã ^‰¡â¸ÔÆ:µâòíâc—ÌûNä9Ý´j&SZ\jbËcñ?q^€D£Ü±âã×܇.¹Nçð—sñÍ|-.ÜæýÉ_ÉãK2%iX  Ñù&UNÈĉ ö ’¢ú¨c)­ô/Æ€y’µ˜X: *¤$ ¸€‰»‚@©Ñ˜ÍÅê©T œ«g]E·®ž¥!%¾ Ø_˜§Ôh×ì8w£H«Vè4 ƒFÁʵ8Ï\·fd™ÅEzn›øá-êÑ,n(•\AÅc|úšuó/9vÍëŠf”Ø%öÉ5;²nÚ …^«P*ÍÁ_ʱ}søöŸ·Š…<š×3…ª`ݾJhPñŸ¹^ôÕÁ[v—åZ\糋´*…NÃé5 „PžÕyîzQæÅBs±0Ä×5 2ΛmN7à”8xk‰;,@eÐrJŽsñ|Q ÇìäEWÿÌr ­©V”Fkò¼¼‚ØÇ€‘½¤ÈœŸ£Cŵc| ±#m@­(V/o Ò3øÄ*5a¡¼".›çDâÙóþ`/±™ónú¡ …FÉÕRåYãkòr++ããˆ4H­Ÿ†±wÖ5ncò‘@VŸXæ"Zÿä´9qD¥IŽGå‘EoqPl#{¼…ZIb_ºJ+Û—÷¾d3Ú2±œ\ä K-O:«(9Ô„­°lÉRÞJÈŠ´ÊÈ<]¨ZR™ÈC-™õpÂq5"‹ž —vÃ%S“®)ª&™ˆôWi#“®!c¯üË„LIj4‡ ß±à;A ì©GBÚ¨÷A4iTñX%™ â<×:8*b F¡gGdÅ.¯jÄpº€KÙ+kWk4¦ pKÁí“kØ 68jÝV)»Áa-*¼{éêM­)Ô®Öhx ç IDAT¦%µVg ·ú@…x™Ž¼¿C  êi?Œú$Xt«0mÄÖU¢ÖhýT¦|ÑNOSŒaÚ•õϾP •i3I  ²Ü…è1eiˆÇòáJ!óbñYXÚþe’¥…"i‘s Æ ’¾è-P"Io ’Äu>© Ò4A`Ú‰&´¿ËŽ& é@j˜Òa8ºYàM%Ôy“õ ™k!w¤m B$'ÙˆO™EL&fâIo¾!£™ éŽtVÔ®¬éFädØÓ¿é!÷?„_^”! ÉWA‘¸Z"múI¬AØDJ.½{d¶–X !itFÄ)Õõ[%³].;ïvsœB¥Òh Æ€°­!P­Ñ€°g+ÒH»ÎqZ‘„ääKB ¥R£5˜ˆ­×ž‹àaˆå¢mFQ= &9ûÒ`0öª‘¿¤Liøš¼\;›áþ!ÍøØºÐ†ÎV<…ÊZ-!Mâf¢È6ýE”d_J²´AvØNRÉŒ‚’ˆ4%ÎñRÊmÈ£çz8ÓR øSvH{ÞÊ,ªB4“œd)Gõ¦C™²"Òúe¾‹'!Ú®‘|½¤òÐ]./î’«¥Â½ÃJ§¤éeqÊ% ¯ûR¡$5=\±8ž&õHH¼:á‰dƒuð=ŒO„áÒ•%™”V„±¸Í,ÅE¼ÛårÚ»ÛeÇ<8N©Ô(Õ¥JÍ•õ`;‡ ôá;žw»œv§]¢PjTR¥)óÙV„hË'—׬XN¹;dJ+Ÿ° /{û§ …÷ä—Õ ê4kÍÎAåF&9—‹Q<›À“~ÎÓ¹<3/4$¼Hxҵȡ:Ò8r˜N¢"˜ td\ú+/V†@1„ÏÄ{ô2Dmá©wà…wyð ;5,k|Äõ:ŽŠïüÀèkl<ÊJg§˜DFÒX @KO\‘l„‬¹Wp‚‹#n/ѵ4Áîr•)Á‘FÅ{0†/rêR”(ú>ò™E©Í!a‰žŒÐâ<x„8ù (‘rc0¦ o%ý“Y `,Û°Ý;á¼-/Xžü‘¯b©ÝKvƒØv°LS±!z¶@z ‘´5qW hÅ‘ 0ÐÓˆb÷LJ0Obžê„Éòäý#ññÉ%L|5’mð”~°øl­\Ò}<ADé‡öj¢„¬Re#<'6bHxñâˆq%²Bd4¤ì€.sÊæ2ž>À¦bÝx¾ãI ÄÑ鿸p. ^.® Sb<’¢e!µ%æŒ1ViôRÕÈÎ. c[ezˆÃaW–÷¬*B*%Åét`bƒ§ÝFhM<¹–Z’UAèQ„ˆ€ól5þ@«V"Òl0ÂJ̇ÉYAÉ¡ÄÀ!2Ö“#‰%˜Ä‘žˆ‡yŒ$Ú¡ÞbùIO¥®&ñ€Iš7“z"žíy¤SŠº@{'µœÔºd»*Äùݡ͑¤&Ò0bï¦Bdêí<²FCR‘2Ié°¬O1‡LÑ}ò  U [~• lb©h,É; ³skÈäˆ#1–$aAâ«Qf”ˆ\HC: "Çé$eE¥¤¦,¥£ë1ˆäC=²%…á¦ÙIt—V A†Ü"ïSÑUEIO,JßüEÕ^•ÌÀÀð¿ ßCš¼0þ```¨žüÁ&/ ~ŸoÜþzðो—ª]•û 6hß®]MkÁð?cÇ~?zìh…Ñ’“’“’Zû¿J¨2}Üñè£T5á?;v|ŒAj Lš8±Â˜ï/Z$ü¨Rüª2H•éCàŽàààª&ü Y³¦gÏžcôÁPS¸ãŠŸøÌ3›¾øªÿžÓÃúõjZ†¿ 6¸ß¦ê²/2Ü“ø•£ªA£ÕŽ~î9y}xU ½U^¸×-ú.пžD^•$ {ƒDÅ·* ‡Ò͵2Iä—žÉq9I¼³)Oò’TNügÿ7/¼ðB™ÕZ•@(§øÞ‘}®XùÜO`ôQ5`;åüµê¿å©Ro|ªHž_·ÊÅÏ?WCÓšý U,¥dÞÇ+0à{W ÷!îÃÿ_¡S§N¯[·îرc¹¹¹jµº~ý˜N: øLpp­{š¯Xµê£Õ«W>|Èw4Œ1ÏÿÕ—¾1üÕ¸ˆÇÆð=8wºqã¦!C†œ={f@ÿþïÌžýú¯·lÑrýú &¾\­ùü½ÀUÀ®]» ”˜˜Ø¼yó.]ºŒ÷¯ýûTE€<¸råJ¿“ûiÓ¦5mÚôwÞ©PŸJj¸lÙ²Ö­[ ¿ß{oNûöíýSìï1H%küÍ7ߌ÷ üå—_›6mºk×®jS¦Òm°ªm¶²Íß“%îÕ©Ó?~ÿ}Μ9=zôزeËs£ŸëýPï'Ÿxbê›SwíÚÙ£{÷{”iyp:«mµ‚ǘ¯¶lÞ2a­V;íÍi .>|¸Ãé8p`%“—ÆÁƒ?úè#¿“û‡âââ;wj4šmÛ¶9ßúTRÃàZÁ6~ «‚þéö7¤r5ŽË*ˆ°cŠùʶ™ á£e~ýõ7¶Þ¦M›US?q¯&/Ÿ|ú©Z­ž>ýÿ”J,L&Ó°aiô2ëüù%,=v4ÓîpÄÅÇOš8!))I¸µ`þ‚í;¶/X°`á‚…gΞ 4 <ä¹çFU>í{ï½·dñ’sçÏ?Ý÷©7¦NÍÊÊZ¶lÙñ?ŽçtìØaòË/Uõ ®ìLrÍÚ5ñqq«V­¢ïÀOKKs:•gúÒ9ýœÇ–‰={Ò­Vëo¼ñî»ï8°¿sç.>ô©PC§Ó¡R©û÷ëß¿_,n'ø_¨¿É •«qñY²6ª}õ¤´´o¾ùj…„åÞ½í;~Ó¦ÍΞ=S]šÜïcœ‘q$9%ÙdòõIÜóçÏ Mf±˜gÍž½t金£G9}Z*›Ùl?þ”×^Ý»wϸ[²dIzzzåÓÎ;÷¥‰vîÚ9lØp¸•s«I“&ÓßžþÉê_yeòÉ“'_š8©ÊE«ôì%77/,<<]G¥R‰1þñǽ͛7?uê”üÖ¨QÏöïßc|õê•_|¡]Û¶ ;v;nœÕj}÷½wW­\e±Xš7oÞ¼yón]» ©Î=7~üø¶mÛ&&&:äèÑL*pÞÜy:u:}æôСCzèáÝ»wcŒ7lø¼wï‡Z·NLKK»zõŠïR|ûíw±±±ƒ ûæÛïhxi}ÊÔPÐáàÁCLLLœ3g.ÆxÅŠ)))‚¡ÛýþûïlÝ:±{÷îkÖ¬¡¹¼öêk‚M(†ñâ‹/”©@…)Ó°¾‹/¨XÐÏ)xËB}(Pa= 6y"îðºåÕå͚ŕ¥¿?¸'Þ‡µ¨¨¨¨¨vTmy Íf£¿u:BháÂ÷CCCW­Z©V« M›6ƒ^µjå"rÞÖét¼=cFã&M`@ÿß|ýÍîÝé={ö€Ê¤}kÚ´Ö‰‰4Ó;tèØAøÝ:1±Y³¸¾}ûfee5nܸòEÑÊÄlÙ¢Å/¿ølíg½ê&¿Õ±c‡ÈÈÈ/¾øbúôéBȵk×ΘþÓyžñÅ ÕÌY³BBCórs:h·ÛÇŽëv¹·nÝúÕ–- P*yž?þ\ZÚ°¸¸øY³féõú-[¶Œõܺuëâããc\\\üÆë¯fÌ蘘˜}û~š7o¤¥¥ñ/< Ž1žçý0H™†Õé*ø2I%k\è€^1…É O_ËS Âz¤6<~ü¸W¾ß~û]…º•F\\ü™3§ýHè…¿iãÖf³µm+õÞ»wo``À‘Œ#ÃG ú? „:wîòùÆ4šÉd¸C@íÚunݾ N§³Â´Z­¶Uk¸N§sõêOÒÓwggßt8„ÁåË—«F•vD§¾9õ•É“çΛ;wÞÜðððĤÄ>Oöiß¾=p×ïé~Ÿ|úÉäÉ“ lÞ¼Ù`Ð?ôðCÅÅÅ.\œùï™]ºtätîÜYøa0BQÑÑ¢&/\¸044tåÊ‚RSS‡ ¼jÕÊ÷ß_$¨j³ÙÞxcjJJ ´LhÙ¹sç=?îÙúÝw*•ŠŠlo¿ývvvvTTT™Eضm›ÛíîÝ»7Æø¡Þ­_·~×®ýû€€€/}ÊÔv:ÓÞ”xǾ¸¸xâË“„±V­[ßͽ»jÕª*•J:¢Ë+@)­€oƒ””””iØ k³Ò5^ÆLJ¼ÄcìC ë‘ÚP šËwßmõV¢,UË Œ‹‹?}úT%Êå ÷„>ŒƒÁ`ÈÎɦ!Z­víš5°cÇ÷_nþÌf‹ÓåZ³fíÚµëh4Œy·ÛM/ÕjOõ ñ.W%Ó z¯ïÍ~çvýðÂøñ ­[ô‹¹0mØp»Ý^¥¢áJoãEDD¬[¿þäÉ“G2œ:yòÀþ?ìúaÔsϽ0~<<Õ÷©+Wlß±½¿þN§ó»ï¶>òÈc4h°lÅrkQQJrRÃFh)¼Æ7§Ó™‘qdø°aJ¥’vêÔyã›èÀ®×ë“’’„KƒÁúÀ(büúõëÀ­[·#""Ê,ÂÖ­[7iÃó|óæÍ£k×ÞºuëÓO÷+SŸrB°V«m™à2ïC©TvêØ‘FèÞ£û¶mÛÎgoÖ´&¾†”€†TÉ jµº<ÃúF%k\^( Ợ‚÷Qž•©ÇÒ6ðä“O”fJâ¯sÜ£×!„RSS:l±X„åŽã“àØoÇ„8&“Q¡P 2䩾OUU¾iwlß1bÄð!C‡—YYYUÍ@I*]˜™€Åbyá…ñk>ýô™BCCƒƒƒ»uïöÕæ¯ú=ÝoÏž=ùùyýžî+HþàƒV¬X¾jÕÊ9ùù!!!C† >|ùœ­4’˜Íf—˵ö³Ï>[·^¦ïv»É Z­V®-ÇqZ­Ž† Ž·«ìÕÜÓ§O_ºtiÔs£Ìf³Ò¹sçŸ~ùò•˜˜zBnà=²• Á ×ë½=–<1M&…BA#@aA¡—Ÿ"ËBZT¨’AÊ7¬OT®ÆœBØ‘ t;]Àq  T¦½l(×ç‰'ߺu›\Ù2KàrêÔI?×;<Ý«ÉËÈ#þù?ÿþ÷Ìwß}ÇkóE€Z­NIIÉ8’ñÒ„—ÊŒà~¤åyÞårŒF²gÏž*e*WzçÅ F£ñ±Ç?qâäÕ«WCBB _¿~cF9~âÄW[¾JHHhب‘ 922rÆŒ·àÏ?ÿüöÛï–,ù 44üÑGñêKƒA¡P 4¨OŸ>¥””œ(¥­¬3`ì£DÛ¶m€Õ¯^ýñjyøöíÛžþy ”šYx1EY:xL^Àj±8•Jô4óóò À„1V(8¯…=‡ÝF#íWU2H¹†õ‰JÖxp­`ŒñíÛ·ÃÃÃi`έ ©åCž={Tµ½ôyüñǶmÛ^æ­2Ož<á÷Z©î}$&%¾:eʼùó³²Î?öØãuëÖ —¯\ÙúÝV“ѨѨ`Ê”ÉÆõÜsÏ n±XOœ\"9~üú{̘ÑcÆŒö¡ª€ÊoÜ>?þùŸöý´iÓ¦»wï*•Ê:uëŒ3fÈ!òäݺu7oî#}V¬Xi6›CCC·mÛÖ°a£O׬ùøã,xßb1›Lññqýûõ§KnàÓû·Ë*ÑŽíÛõz}·®]½nõìÑsÑû‹vlß–”˜XZŸÒ!åé [þÄ:þß3ÿ½pÁ‚óY‚ƒƒ^zé¥g>#Ümܨñ[oýßG´víÚ:uê<;êY{‰U2ˆÃVWÏ™óÞÚµŸ¥ïÞ½eó…BѸqãÙ³fuïѣš­j=–G7³¯—]„ê£ ,û hÆŒ‘©#®æÙK\^éeð~ýú ÿ³oËÌÌüãø‰ÏªõÛ>þ7OÍ1ܮҧ$+?33sǎçÑ˃שÓ{õªd†<îÉû>þ™ðt?˜÷ÁÀÀà'}000ø F ~‚уŸ`ôÁÀÀà'}000ø F ~¢fècãÆM ­„ÿÚ´yࡇž4iRzzzåŸí9œ‘±zõêŠã,˜¿ k×®~)ËÀÀP6jòÐú´·ÞŒˆˆpÚ7snþüÓÏ“'¿Ò¾}»%K–ÐOOú@ÆáÛ6n5ª‚W3200T#¼­×ä#s©)©±±±Âï´´´/¾ürö¬Ù ßÿõ×^«A­*‰ûè‰Ûg øqÏ[6o™8a‚V«ÍÊÊZ¶lÙñ?ŽçtìØaòË/Àœ¹s7¬ßäÌááá{ö¤ûˆOñÇñãsß›söÜùZC‡6,M÷öÚõkóçÍÏÌXzìh¦ÝላŸ4qBRRÒßi4†Ä}DÐ¥sçC‡:u*99ùVέ&Mš<Õ÷©£éú«W¯~iâ¤ÏÖ®€qcÇòn÷Ö­Û¾Ú²Jøˆ/Àf+žúúÔ1cFÇÄÔÛ·ï§ùóç#iii¦ðÒ…B5k֬мܼƒ‡:N8þ\ZÚ𸸸Y³gëõº-[¾=z̺uëâããþf»10Ôî/úˆŠŽ€»wî@‡Ž:t_`Ý:1±Y³¸¾}ûfee5nÜ800Ðh4rE׎¦i}Ä‹‹‹'½<©GîB„ÜÜÜU«>8hJ©ô‘¶¤¤äÂ…‹3gÎìÚµ‹¡K—ÎÂ… ß ]µj¥°XÓ¦M›Áƒ¯ZµrÑ¢E÷ÖL ÷î/úßI‹œNçêÕŸ¤§ïÎξép8…—/_¦tà… ã«”ÊΤç@Ï^=¶nÛvñBV³fq>ÒjµÚ† ,_±Üf³¥¤$7jÔHø„ºÓé<’qdøˆát¡!Ô¹s—Ï7n¬f£00ܯ¸'ôñëÁƒ—.^ò ¬ðM$só&„……ÀìwÞùa×/ŒŸÐº•Ao°˜ Ó† ·Ûí奭0¾ÁdR)¥ò×€ÂBs…i—.]º|Ùò+Væçç…†† :täÈ‘f³Åér­Y³víÚuT&ƼÛí¦—ë×o¨°È 4l ¼g³zQýô!pGeÈ¢4~úùgµZ;¶ï1bø"'++ËwÚ ãY,N§S¥R —ùyPaÚèè虳fÀ¥K~óõ׋- ïÝ»—B¡:dÈS}Ÿ*O%ÿŒÀÀPíF²jgrl WÛÙ¿¹ã‹/¿<|øð€ghµZžç].—Áh¤w÷ìÙ#¬Vkœ.½¬0>8]®Ÿ~ú‰^þðCz```ÃF+“V@ƒ±“_™¬Ñh.\¸ V«SRR2ŽdÔ­[7Ö~”ážbèÐ!¥'þÀ“(jríãHæ‘«×®:íΜœœŸþùpFFûöí&N˜Ç=Ðö¯¶|Õ­kרèè=éé›7o–§mذaIIÉ_~Ù<¾¹F­jܤ‰ïø ÓéÞ‘Ùlމ‰Ù·ï§íÛ·O™òŠ0ñ‘6;;{úÿMïÙ»gll·Ë•¾{ÝnoÛ¶-L™2yذ£ž{î™ÂÃÃ-뉓'0'LxéžÛŽá>@MÒǬ™³@£Ñ„„„ÄÅ5[°`~=„…I˜9sæ¿gü»oß§ÕjUëÄÄ…  >‚¦íÚµK¿~ý–~¸´°°0,,lÏžtßñ@¯×½7ç½9ï¾wöÜùZµ‚'¿2Yصõ—) ""bíšÏrnÞD׸q£yóæ¶kך4iºqãçË–/Ÿ3gžÙ\Ø¢yóAƒÞk»10Ü'@3f̈Lñç]{‰‹€×2úN°~ý†G}ÄÇ«Æ×¯ßÀæü ÷|wÌÌÌ̳gÏUØsçì²€VÅņhrެaOÜ200ø F ~‚уŸ K§ìC/ Âã‘}æ}000ø F ~‚уŸ`ôÁÀÀà'}000ø‰š|U2üX‚y ~‚уŸ`ôÁÀÀà'dì³ÅßðüNó>ü£?ÁèƒÁO0ú```ðŒ>ü£?!ÒGõ}æ¥RظqSBB«.]ºÓÀìÙ ­¾ÜüåߥÅýˆÑ£Ç¼0þÅ¿.çpFÆêÕ«ÿº9¼ˆ¢&½¼¼¼ >¯AþÁÈ8|ø“ÕŸÔ´ ÿpÔ$}´k×víÚµ«µuø+p:5­CM¢&écÜØq6›mÝgŸ•y7++kÒ¤IÝ»uOJJîÒ¥ë[o½UŸOï.˜¿ k×®gÏž6|DJJê£<¶'}lܸé‘GMNN>bĵë×<ž?ÿâ‹lÿ`JJjÚ°áÇŽ«ªÂB¦‡2xHJJêüyó+”ì»°{÷î'Ÿì“œœÚ§OŸôôt¯ýõ`ZÚ°””ÔöíÛ¿øÂKeß|ã©z|’êÙgGM˜0æÌûÑG[¬Ö„„V ­zôèYÕ’20T5Iѵ£û>õÔúõë Kß½•s«I“&ÓßžþÉê_yeòÉ“'_š8I¡¸¸dêÔ7Ÿxü±Å‹5hÐàÕ×u3³A IDAT^›?þ{ö¼òÊ+³gϼqýÆë¯¾N#Ÿ?nhÚ0‹Å߸Q¸ŒŽŽþí·JÍe´Zm«Ö­åJú–ì£v»ýÔéÓãǧŸõmÚ´YLLŒðÛápœ¡u+ƒÞ`1¦ n·ÛiN+¯P(t:½D ð.7˜Í§ËµfÍÚµk×Ñón·»ªÚ zÚÛ+#ÙG),+Ïó¡!!rùaaaT2ÏóÁµ<î†×:{æLUuf`¸G¨yú®5dÈàµk×õîÙK¾cûŽ#†!ßìÍÊÊò; “ɨP(†òTß§þ’®U—ì£&“‘ã8¯'‹Åª×é ÀÄq\~^®ünn~^`` ð[©P`σ:»L|ᜡq_œ:1b„V£^¹j% áyÞårŒRgسgßòÕjuJJJÆ‘ŒºuëÆzâ/é]‘dߥÐh4Íãã>DCîܽ{ñâ*¹e‹éééÔ‘¹s÷#©)©ÂeXDøÍœžç…K‹Õz^ÆMjµÆérýÅÒ10ø¡¿ùØ©'Ò† ûõ׃’Z÷@Û¾ÚòÕÕ«W.×Î;7oÞüW²˜2eòåËWF=÷Ü÷ߟ™™¹oßOK>ø`ñâ%ÂÝìììÄĤ%|P½’+,ŸçÇýrà—uëÖ9Ž[·nM}cªJ¥¢wŸ?þÏ?/¿ôâ„ýö§§§;V­V9B¸Û«g‚‚åË—[,–Ë—/O™2E©Tд 6,))ùâË/Ož<•uþ¼åb`(žDÁy…×ÒÒ†ÉCfΜY?¦~ß¾OwîÔiÛöí ,ø+ò›4iºqãçááásæÌ=zÌŒoŸ;s.9)I¼Áívón¾Ú%û.EÇçγyó–¶mÛ?;rTÏ^=èÝvíÚ.[¶Ìl1Ošøòÿýßôè¨ÚŸ}öYt´¸™Ò¬YÜÌY3·ïØÑ¥s—‰'>ùÄÍšÑMèÚµK¿~ý–~¸tÈ!ÿz~¼åb`( /7͘1#2uÄÅ;%ÅN¦>bò~ýú >úHpp°CÉTŸá>™™yöì¹ {î;ß[@§â†isެ¡K§ìK/ ƒ%î‹¥S†ÿF0ú```ðŒ>ü£?ÁèƒÁO0ú```ðŒ>ü£?!c‡Æ*„ç²™÷ÁÀÀà/}000ø F ~‚<2Ç?*„'K0ïƒÁO0ú```ðŒ>üDÍÐÇÆ›„ï'&$´JNNyòÉ>Ÿ~úie>›PúÃñ‡#eàÎÝ»­ZµþíØo¥o=æ…ñ/VFH邸jå£hÕŽÊÛÊ7ª±ø UEMzÓÞzóƒ—¼ûî; ľÿþ¢>ø°Â$÷Çã÷íÛܪu«¿"¤ R¢ª¥h3î‡&ñ? ‘>jäEë©)©;uîÕ«×Â… ããã6mÚäÇw›þ~ìÝ»¯s—Î÷™÷9ú»¼¢Éã0ü/Ë(î‹>€Jh™`³Ù }|’ÞLJ㳲²FŽ|6%%µ[·n|]Y«µèHÆ‘nÝÄéÒîÝ»Ÿ|²OrrjŸ>}ÒÓÓ½"g?ÿâ‹lÿ`JJjÚ°áÇŽó]òâÓ»'NìØ±SRRò£<¶|ù ¢~ýõ`ZÚ°””ÔöíÛ¿øÂK/^¢r„éÞáLJ ’’’:ÞüÒE+/Ž }T\elåCá7Þ˜:pà@yägŸ5aÂÅgø{Pó_™pãF¶R©4§OnÒ¤ÉS}Ÿ 0š®ß¸±zõê—&Núlí7v,ïvoݺí«-[@A>kR\\òꫯxfàèÑ£÷ìÙ³dÉ’˜˜z={ö€ìÙ=üð³Ï>;qâ„jÑóÀý*•ª]Ûv°ÿÀþ)S^íÕ»×´·¦åçå-Z´¸¤¸8..^ˆyþü¹´´áqqq³fÏÖëu[¶|5zô˜uëÖÅÇÇ•Yñàôé3#FŒhܨѴio†……]½rõü…¬òlrðà¡ñãÇwx°Ã’%KŠ‹‹—-[:lذ͛¿¤y0›­sçÎ}õµ×4hà(±—.Z™q|kx+çVyW¡­*T¸<”×$þÔ$}”””Øl6»½äû;ür k×.jµÚÇ'éËûp¼Íf{ýõ×xàhß¾]ffæîÝé}…BÁ)ªÍÉúqïÞö¶>ˆ½bÙŠ¦M›Í3GøêmLL½þýŸ¡ŸZY¸ðýÐÐÐU«V ‘Û´i3xðàU«V.Z´¨Ì‚øˆ , ýäÓO4 $‘ïÈ”)jé‡Ö¯óþ¢…ÂçµZ>òÈ£Ÿ~òé›ÓÞ"8Ž·¦Mk˜X^ÑÊŒã[CW¡­*T¸<”×$þÔ$}<óŒä‘vìÐqÆôéàó“ôåÉÑëõmÚ´¡—7ºuû¶ð;::ú·ßŽ•“®Êp:¿80uêT°Ûí§NŸ?~<ýbvÓ¦ÍbbbhÌ#G†N{#B¨sç.ŸoÜXždñNÇÑcÇž}v¤À¾áp8Nž:5fÌh¡+@XXXj›ÔÌ£™4ŽV«mÕºuyE+3N…%òQq¾mU…îOÔ$}¼÷Þ»Q‘QJµªNíèààZB OÒ—N+ÿê½B¡àïÍç]3g””Ø;wî ‹•çùÐy„°°0á‡Ùlqº\kÖ¬]»v½‹1_ÞÚ°ïøf³Õív‡††UFI³ÙÂó|p-ÅB‚k=s†^ z¹Å¼ŠVfœ Kä£â*´U… 3ÜŸ¨Iúˆ‹‹+ý‘jŸ¤¯qìÝ»/99Ùd2€Édä8ÎbµÊ#X,V½N/ÜU(C‡ yªïS•‘ì;~@€Q©TÞ½{§2¢LÇåçåÊsóó}¤’Í ÁgÅù¶U… + ì¹)è°ÛÁd†šÆ}±óBáû“ôP£ŽÇÿôÓ¾nݺ —¦y|ü¡Ã‡h„;wï^¼xAø­V«SRR2ŽdÔ­[7Ö$‚GA|ÇW©ÔÉIIßïÜYRRRZ±Ò¢Z¶h‘žžNý‚;wïÉ8’š’ZÉ¢• ßú®¸ må[á°ˆð›99*ü$}U?Ÿ˜˜´äƒþºnÇOœ¸s÷.ݲ€qÏûåÀ/ëÖ­s:·nÝšúÆT•JEïN™2ùòå+£ž{îûï¿ÏÌÌÜ·ï§%|°xñ’ò â;þË“_ν›;räÈ;wÉÌüæëoÞ}ï½òD=?~üŸ^~éÅ ûìOOO7v¬Z­1rDå‹V&|hXaÅù¶•o…{õìQXP°|ùr‹Årùòå)S¦(e;,Um Õˆûeã–bæÌ™ÿžñï¾}ŸV«U­.X0løz—~8¾°°0,,lÏïãÞÀàv»y7ÿ×Û·w_||\DD 騡ãܹs–.]¶hÑâȈÈá#‡Éã7iÒtãÆÏ—-_>gÎ<³¹0 °Eóæƒ ,¯ ¾ãÇÅÅ­[·ö×Ϛ9«¸¤$**êÉ'Ÿ,OT»vm—-[¶|ùòI_V©T)É)sçÎó± Zºhe·†¾+η­|+ܬYÜÌY3—/_þÉêOêÖ«;vìX‡]:ÆVå&ÁP}@3f̈L‘u»¸ØÉÀ´G|'X¿~ã>ì#B…ßéþoÄO<ùØc3º¦©~üƒ‹Æ@á»cfffž={®Âž;k‡tjEã0mΑ5äUÉcÌÞä [·~WÓ*Ü+üƒ‹ÆP½Y‚pÅýµöÁÀÀð_F ~‚уŸ`ôÁÀÀà'}000ø F ~‚уŸ`ôÁÀÀà'}000ø F ~‚уŸ`ôÁÀÀà'}000ø F ~‚уŸ`ôÁÀÀà'jàe… å~¹Q£†_ýõß©L•p8#ã䉣Fªdüólß±}ß¾}eÞ]µê£Õ«W–½@˜á¿ 5@ ̧¿gÌx;&¦ÞÈ‘#…K£±ÜÜÈ8|xÓÆM•§†6j€>ÄÏGÀ;ï¼&a``øoÁ}´ö±oßO ­NŸöø¶ØèÑc„¯« ß|ß`¿~’“S{õêýÙgëä1}žþ¯£Ì¹WøYyøãøñ!ƒ‡”©óß©?Cµã>¢N:FFFÊ¿ríÚµŒŒŒý—f³eÑû‹ßygæÁƒ¿Lœ8añâÅëÖ‰½ñüùsCÓ†Y,æY³g/]úadDÄèÑc(eßÈNHhµhÑâ¿¢Þ¸±c h0víܹkçÎ Öù¬üô·§²úãW^™|òäÉ—&N’§²ÙЧ¾>uÀ€«?^õðCÍŸ?Ÿê,Çÿ³wßqMœoÀŸ$VÁZe*ˆì- ´¢Ö… ÊtU¥ ¸7CDÄ‚ (­`‡Õ*‚u†øSÁ*8p”!(’ +!ùýqôŒ†qQßïÇO›»{ïî¹7äá½»pàøD4‰P …âââ’œœ(##™™22Ò“œ'a X,Öºukuu‡€³³sqñƒÄÄCsÜÝÅÅÄP(2¥G¹²Ãbî‚ËÊ@ccãÊU+LJ5xóæ 3ïÆ»ˆAD’>`Ö¬Y¬––3‹õû§¦Lž"%%…-§šššâmmlêêêž<.ÅŠ¿Oœ8¯ø{áÿa“êêêwî®X¾¼×f±X gÍšecckffáæ6ÊÊÊðâbb£?Ôvš0‹™o#‚ãGÑ$B£PRR;~\Ɖ“®³]srrjkßÎv/•¡ÉÉò¬¼ÔÕ1>µœ}/PV¾-fYYÞÆE,fÞ?‚ôþ“͉2Qn®®óç/¸wÿ~FF¦‘‘~õLfKK þ+ºúu5ÈËË}j9û^$ ¬<æ=“Éb±ðz®ïj߀¼üG¥üŒA>—ç¿"vòæææÚÚZÑ{¢ x‡Àf³/\¸€Ož;wN^^^K[§Ërö½…¯˜»à²ò›}éÒ%|2++‹ùãÍöSüÒ»Dëä3ÛÕmGx¸œœÜw'òΗ’’ŠÝ·¯þ}½ÆPœ ²²²‚ƒƒ°Sƒàà@//Ÿ º¹ºª¨¨0™õ÷‹îs9\ÿP^^>yòßù¾=¼üs¡?B‚*®£«‹••;fŒšºzNv6_Yy,æèèƒ1dÈ‹/>}™—àøD4‰bú˜8ÁiGxø÷Ó¾ÇÏS0ÒÒR;v>|T¢¨¨èéé‰-\ü¸ÐÚÚÊiåô0°öÅÜ—•ÇbÞ±³Ã˜yu?‚ˆ$‚ÓG‡rõÚ5˜íâÒ~‘áȑǎëpSQ‘‘.R¤~ïÞ݄نB¡lÞ¼ióæMøº²ò¾ý±¼mxwƼxñ"Þ¢öâGÑô_ú+§OŸ>{ùêEÜþ¸Ñ£Ñ™?‚ˆœ®œ=úà³cÇŽÂÛ·GnܸžèXé‚h¥C‡;[„Ÿ ""DîÆ-‚ Ÿ ”>J‚ ¥A„„Ò‚ BBéA!¡ô ˆPú@DH(} "$”>J‚ ¥A„ÔûéCSK3-­ãGr Bˆ´´cšZš½¾ÙÞÿ‹[[@AD‡¦–&öÁì]}òû¶66}+‚ "]û@DH(} "¤¶“.p¹¢ð°SAD–%¸"[& AÏJ‚ ¥A„„Ò‚ BBéA!¡ô ˆPú@DH(} "$bŠT¦§ÿ¼cÇ쵤¤¤¢’¢ÞðáÎÎÎãÇ'‘HÝÙBn^^Ñýû ,èË0„'}ôû—N7nÚ ªªÊjfUTV\¾t900ÈÖÖ&66–J¥v¹n^nîÏé?£ô ýŠËó_bKd[˜[hhh`¯===9q",4lOtôÚ5kŒ An"2}ðqsu½s!3#3Àß_RR²´´ôÀ÷îÞ«}÷NNNÎÞÞ.pÕª y,퀊ŠJNN¶€öðòÕË]Q» n744ÈËËëЊŒ”‘‘Ù½k÷é3§·‡lß³ïÙ³gJJŠ^^žØZ‚· ¥%%qܾ]øþý{µjS¦NYºt ¾(v_\áí‚æ–=}ý•þ¦¦¦ýÙŸÒ×D(}€ãèÑ·nÝ*..633«ª¬ÒÕÕ1s†MöÕ¿ÿ&%%­X™rô,ùáNkë©SžÌÌŠ´ÿþŠxhh¨’²ÒÛ7ooÞºÉb±°E 3&zïŽaC‡jääälÚ´™DOOÏ.·ùàÁ?>>>:ÚÚ7n Óé/ž¿(y\Š-*)yäéé­§§&--•™yrѢũ©©úúzýØÒ·D+}¨©«@Mu ØÙÛÙÙÛaóML†×›9sfii©ŽŽŽ¼¼œ,!!¼ƒ‹={¢•••b×q,--çΛ˜x0&&¦ozA ZéƒÃáX,VRRrvöùòòŠ––¶‘BYYöÑmO@{III--Íø„ø††ss3mmmÞû;ââTÞO¾­Mjjê“Ç¥Ã‡ë Ø&‹Õr»°pþ|_,wðE’Ÿ—ïíã_&‘H£G;OOïi!ˆ(é“ôqãæÍ§OžòÍôð˜×劕@§Ó ,<<ë\Ö~~†ÆF2Ò2LF§—wsssgë n >!á`mí[ee%___,‰ÈÐdÈäß‘•—€º:†àm2õ­­­ÊÊôö‘0L›}äÈÑ£GSñ™\.§µµŸDÏ‚EúÓgó¬S,wt'Y´wéòe*•ª¯¯gNŸñññž÷ßvJKK¯+¸½ººzHh<}úì·_‰ÙK§«L:ê™Ì––|¤PýºäååoSNŽ&&&VSSÝ>YY…Bñ˜7oÆÌE+\ÿ ˆp°_W½žAzÿ[§BçŽ_NœÈÍÍuus•””äp8l6[†F׿ääð6¦R%Xl6>Ùe{œ¦¦F`P „„ÄãDZ9l6ûÂ… xƒsçÎÉËËkiëÞ¦¸8ÕÌÔô¯³g›ššøvA¥RÍÍÍóòó¬ñ±î÷‚ô"yíOzŽÈkùù/^¾`5³*++/_¾œ›—gkkàïd2ÙÊÚêdæÉ±cƨ©«çdggdd𮫥¥ÕÔÔôˉ#ôGHPÅutu´///ß²y‹ÓD' ÍV6;û|Nss³µµ5¶TJJ*vß¾ú÷õC5r.\ÈÊÊ Á1¬ \åííãëëëåå¥L§¿zñòaÉ£uk×@pp ——Ï‚… Ý\]UTT˜ÌúûE÷¹®¿ÿоîUé7D¦Ð0PRRÒÓ¾{÷.Þ/­‡„„lߺ}æÌYTª¸±‰ÉžÝ»½¼}ðuÇŒqtqq‰ÛWWWG§Ósr²´—•“SUU=z$¥²¢‚D&ëèhGEEÚØ´¥ii©ˆˆˆˆ;>*QTT ÄîÚvƒžž^jêÑýûãCCB›šÔÔÔ¦M›†-ÒÕ–ž~ü@||DDƒQ'''o0b„»ûœ¾ìNéo¤­[·´ðyTYߨ€Íß+^!-íØäÉÎ 6KK;öØc_»xñ"Ñ HßüÁ,((xøðQ—ŸÜí§j@šJÑ(S™ýÅ-‚ BBéA!}íé#0(¹ ˆpx/¢2Q‚öÑ_ìí£A„†Ò‚ BBéA!¡ô ˆPú@DH(} "$”>J‚ ¥A„„Ò‚ BBéA!¡ô ˆPú@DH(} "¤ÿþ`Ÿ‹þ^A®|ô÷úhô ˆ°ˆIéé?aÿ,-­¾›4iåÊ•ÙÙÙ\î‡!Pbâ!++kBÂë¨Å#@uuõŽ;==<ÍÍ- Êÿ-':"äËDd¡†›6¨ªª²šY•—/] ²µµ‰Å ¾)**hiiQ‹G€W/_eË2a`ld”—ŸOt8È‹Èôaan^óôôüåĉ°Ð°=ÑÑk׬Ãã#jñ`dltéÒEHKMCéé;"tíÃÍÕÕÚÚ:3#+ûÈ{²°{×î1cÆ<|ø—·¹¹Ådç)9Ù9žþ³³ód33 oŸ—¯^òn­´¤dùrÿQ¶£ÌÍ-<½¼ ñEØÖ =æy˜™YŒ;ö§Ÿ’ð¥/_½ô÷÷5ÊÎÄÄÔÑqÌ2?¿÷ïßC»“—7nzzz™›[ØÚÚ.ÿqÅž €Âm¿ñVüF¾#Z?gŽ£G·´´·_ÔØØ´~ý†ï§NÙ£©©¹zÍš]»v]ÈÉ ù÷Õ¿kW¯Å—”<òðôb2¡aaqqûªª.Z´øÁƒð Fý®]»‚׬þûïœ%K—ÄÆÆfggc‹üWø——W†††M9ºuëÖÁƒ³X,¾`nÞ¼åçç7@~@lllHHhyÅ¿^^^ååå=Ù~ù¿å††F11{{©/¤ÏyòÒžšº:ÔT×´_ÔÐаnýz ss042tps៧þ§@Ãû†­Û¶UTT¨©©Àž=ÑÊÊʉ‰±Ë(–––sçÎML<ƒmÅjÙ¶u«Ž®.¸Îvýí×ßΟÏvrrjjjzüøIHHȘ1ŽXKGÇÑ탉ۿèÐ!Ñ1{ÄÄÄÀÐp¤³óäÃɇ7lÜ üöI@¡PÈÑJè"€h¥‡@ê`‘´´4–;@VVVYYÙÚÚË ©©¯««ÕÔÔX,V~^¾·7–;€D"íx<=ßš¬¬,öÙÆ ôMÕë× ))©¥¥ŸßÐÐ`nn¦­­×Üŵ´´/^¼Ë@§Ó-,- nôdûêêêwî‚|>D+}TVTNo¿HJJ’w’B¡HIIá“$ 8ìV`0˜,6ûÈ‘£G¦â ¸\Nkk+>I¥ŠónD&qØlìu\\\üø„„ƒµµo•••<<<|}}y“ƒÁäp8 ŠJ¼[PRP|øÏ‡“£žlA>xúàrEàk§—._¦R©úúú=Ùˆ¬,B¡xÌ›7cæ !VWWW €§OŸýöë¯11{ét•©S§à äädÉdríÛ7¼k½©}+//ß+ÛG‘…e Dž¼ää¿xù‚Õ̪¬¬¼|ùrn^ž­­M€¿Ï·èåå³`áB7WW&³þ~Ñ}.‡ëï¿BðŠååå[6oqšè¤¡¡ÙÊfgŸÏinn¶¶æÿ²é2?¿¥K—®Xî?g®[ScSBB•Jõñõé20Û///Ÿêò“»í7 M¦F«Ì?"B×>ù¼ ô *½k- IDATˆPú@DH(} "$”>J‚ ¥A„„Ò‚ BBéA!¡ô ˆPú@DH(} "$”>J‚ ¥A„ôßジ :ED´}œ%ÐèA!–>²³³½¼}¬­mLMÍ&L˜¸|¹ÿ7±EX‘G¢ûb„‡…/_ÞöàØêêê;wzzxš›[•ÿ[Î×øíÛ·ë×o°³³·²²^²téÓ§Ï:ÛìÑ”WÞªÈW‹˜ôñûo¿IIJ¬[·.**ÊËÓ“Ån¹qã:!Á|‘ÊÊÊ223—-[‚M¾zù*ë\–¼üc#£öYlöâÅKroÝ Ü¾}[uuÍüùóß¼yÛá–縹UWWŸ>}¦£G>Ä<*9%5EOO/!!0²‡§GûR²ˆÐ9b`` §§‡M]ºtÒRÓòòóùÿõ×_%%~JúÉÒÂŒ'9|80(°ý–%$$¦OŸ–œœ% bN^F\¿q=-5­ºººÃM«W¯ž0qbll¬£ãÞ õU•Uººº[¶mINú)((°¨¨hEÀJÞu fLôÞðð›7¯øïÝ»75µ-ƒ””<òðôb2¡aaqqûªª.Z´øÁƒ¶â’_Lûü‚üÆÆFK«î–­züø‰¶¶ï-íWÿ¾jiié°½žÞpYíò•+= ùÌsò²aãúÀÀÀȨ¨È¨(S“Ó¦ÛŽ²Å444¬]»ÖÊÊ lmm ° õ`goggo‡5361>\oæÌ™¥¥¥:::ØL‹µnÝZ]Ýaàìì\\ü 1ñÐwwq1±={¢•••bÕ³---çΛ˜x0&&àË©qÿ ø‰¤¡¡ÙÍöuuŒaÇñΑ•“ãr¹u ]Y¹}{2™¬©¥UTTÜ ±"Ÿ3bÒ‡ššÚñãÇïåçå]¿v-ë\Ö¢E —ÿW`MZZÚÒÒo¯­£U¨‹•””œ}¾¼¼¢¥¥írIYYž>ÄÅ©¦¦¦øº¶66©©©O—jiiççå{ûxc¹H$ÒèÑŽÇÓÓ±É/¦Æýëª×²²²|'ƒ½KIIñÙ³²¾Û>òY ²ÊÜHƒ‘Àd2—.Yšœ|xŽ»;öëNJJ’÷Ê…BÁ+Ô‡…‡gËúÑÏÏÐØHFZ†É¨óôònnnÆËÐdxOõeåå ®ŽÁ`0Ylö‘#Gýp5„Ëå|y÷ Ù6™Bé~{99Y&“É;‡É`H$9YÙÎV!S(ìÿÞä«EdúÀÉÊÊ~?ýû{÷ï¿|ñ¢ÃÑ2¯3§ÏøøxÏû¯Vii)_ƒz&³¥¥bT¿®yy9YY…Bñ˜7oÆÌ½}¢EQAñ=“Éår»YñS[›ÿL¤ôÉão}#!!ÑÙ*Ì:†¢¢bOE>sÄœç¿|ù’oΓÒÇ ¢¢"xE‡Ãf³eh4|V š›Í¾pá>yîÜ9yyy-m*•jnnž—Ÿ7xð`õè`D––&‹Í®(¯èf{GÇ1UUUØdUUUî­Ü1c¬Röü9~¶ˆ|µˆ},X°pÈ·ßÚ9ØRÄbµÜ¸qãÔ©?ÇŒqüæ›o¯H&“­¬­Nfž;fŒšºzNvvFF_))©Ø}ûêß×k Õȹp!+++88H\L ‚ƒ½¼|,\èæêª¢¢ÂdÖß/ºÏåpýýW@¯Ö¸'–••‰Dº{ï®ú ¶ªÝ\.˳JÀÕë×èÊÊÆ&&à<Ù9%%eÍšµ+–/—”’L<ô&ëíロûúõë… ¹¸¸xyybs*++«ªªÚWG¾6Ĥ€ÿœ Ž¥«©©¡P(ß~û­ŸŸŸwwÖ Ù¾uûÌ™³¨Tqc“=»w{yûð6––ŠˆˆˆØ±óá£EE…À @O϶Ÿ{]ÝaééÇÄÇGDD1urrò#F¸»Ïi[³÷jÜ‹N§[ZXü}ñâ¤I“°9­­­Axƒ°Ð0p°wØ·ÄÅÄ£¢¢"£¢X,–©©IdD~ùþýû²²26û×ú.^¼$K£988ôß!!"‰˜ôáìììììÜÙÒÀ @¾ï;FEFâ¯éÊÊûöÇò.½wï.ß GŽJ‚ ¥A„„Ò‚ BBéA!¡ô ˆPú@DH(} "$”>J‚ ¥A„„Ò‚ B"æöÓÓÞ±£íÙV’’’ŠJŠzÇ;;;?¾›ÏÍË+ºÁ‚ÂÐÃÕ‘OµhÑb ªölDä‹Aäèc㦠ûödž‡…Í›;—É`-]º´³Âˆ|òrs““’…ÞuWGˆ}\…¹^$ÁÓÓó—'ÂBÃöDG¯]³†À¨¾<,V‹¸8õËÛB8ºöáæêjmm™‘ÙÔÔ¥¥¥+W®7vœ©©™£ã˜M›6½«­ÅZFDF:ô³¾ÞÐÐÈÐÐhüx'Áíù´_ýâÅK††Fx­lÌ¢E‹çÌ™»wí3fÌÕkW]\\ÍÌ,&L˜˜’òÑ3DKKJ–/÷e;ÊÜÜÂÓË»°àJ—XÀ¹¹¹óæÎ37·Øµ«;q––”ØÛ;˜ššMvžŸ€/ºq㦧§—¹¹…­­íòW`0ê###W¯Y£©©ÙÒÔÜeœüãã㣣­½qã:þâù‹’ÇmåûnÞ¼åççg7Ê.66¶±±ñÀ8//¯ŒŒêêêíë굫ÁÁ«'Lœ°qÓÆÚ·ocbö656êéé÷g ý@´Ò‡šº:ÔT×€½½6ߨÄdøp½™3g–––êèèÈËËÓh42‰„—AÜžo/®îââ’œœ(##™™22Ò“œÛꤰX¬uëÖêêggçâ≉‡æ¸»‹‹‰íÙ­¬¬œ˜x+‹iii9wîÜÄă111$ P(dJòX¬–M7bU 0‚ãܳ{·²²ròád¬üoñ¸ýû‡³GLL G:;O>œ|xÃÆ í+á@°aÃ##"° áC†|;{¶›^7Ò¯Dèä8 €Åb%$œ5k–­™™…›ÛðèðOmÏgÖ¬Y¬––3Á6õû§¦Lž"%%…-§ò~¢lmlêêêž<.e±Xùyù'NÀKê’H¤Ñ£ ïü›TWW¿s§°ÿËÖIJJã“‚ãd±ZnNšô]ûÒ----EÅÅNNNXî:naiQp» ³}577?xàäôá&Ú°aÇ ÒG‰L´F•@§Ó ,<<ë\Ö~~†ÆF2Ò2LF§—wsssgë~j{>JJŠcÇË8qÒu¶kNNNmíÛÙ®³ñ¥242ùCª••—€º:ƒÁd±ÙGŽå­¨ÂårZ[[?ñÐ{™ŒŒ4ï-pÁq2õ­­­ÊÊôöÛa0˜GAQ‰w¦’‚âÃ>\'âÛ“YÏáp”•>Z{O‘/Œh¥K—/S©T}}}8súŒ÷sæ,*UÜØÄdÏîÝ^Þ>øºcÆ8º¸¸Äí«««£Óé99Ù‚Ûói¿:6â§ááßOû?OÁHKKEDDDìØùðQ‰¢¢B`P v×tu‡¥§?Å`ÔÉÉÉŒáî>§mM.´¶¶rZ9½Ôg§žž^jêÑýûãCCB›šÔÔÔ¦M›†-²±±>pà@||üÊ€Uâââæfæ‘‘Qø]ÛÙÛÙGFFÄň‰Ù;Pu ·¯WŸÒ/ĨR*CGŠQÛ.±“¶nÝ:ÐÂçQ9£¡¥¶LW¼~Zڱɓ4ðøïÄgçS§6mÜôÇ¿óž}ìÞµûô™Ó/^$00é!ÁÌ‚‚‚‡uùÉÝö{ »¥QNVv˜šleþѺtJ §OŸ½|õ"nÜèÑŽ¢våADvò"=Ì@DíÎ vìØQxûöHCÃ× ‚ˆ(¾“”>Ú:”ØÙ¢À ÀÀ Àþ AD%B7nq|w^Pú@¤»øN^Pú@äˆQ%ñ×(} "$”>J‚ ¥A„„Ò‚ BBéA!¡ô ˆPú@DH(} "$”>J‚‰ÈôqïþýààÕãÆŽ335sp½tÙ²œlAÏ7þ|%&²²²î·Ý¥§ÿlhhäè8¦±ñÃSmËÿ-744:‘q¢ßÂ@¾x„¥ôôŸ½<½žòòòÌÌÍT*êºZ^ñ¯——Wyy9Þ ±±iýú ßO²7&FSSsõš5»víº“òï«×®^Ë»Aƒ½7<<äæÍëþ{÷îMMMåYZ¹"Àÿì¹³^^ÞPRòÈÃÓ‹Éd„†…ÅÅí¨ªºhÑâÚ*3ú¯ð//¯ =šrtëÖ­ƒf±XæÒ¡1õ»ví ^³úï¿s–,]›ÝVG»~³·;¬>H}æŒiiiuuuBô°€0wòõ àä…Y___ÿþõo´é²ª{CCúõë-ÌÍÀÐÈÐÁatÎ… žú;ïhxß°uÛ¶ŠŠ 555¬=‹ÅZ·n­®î0pvv..~˜xhŽ»;Vêé“êÑ755=~ü$$$dÌG¬±£ãhèlþ§‹Õ²mëV]]píúÛ¯¿?Ÿíää@ …B¦t7é/þaño¿ÿžœ|xåÊ€^ C@çt30äË Š7n»SÕ]ZZË ++«¬¬lmm_³ÐÔÔ€×ÕжЋSMMMñI[›ººº'Ûêà~R=zIII--Íø„øôôŸKKK¹\.¾‘çê¡ÉÊÊbZÌ AßT½~½VWW¿s§pÅòåÝëH ÓénnnéééoÞ¼í­0wòU!`ô!K£Ñh2¯Ê_uÖ ;UÝ¥¤$y—R()))|’D¡‡ç*¬ M†Lþ+eåå ®ŽÑ¶ôSêÑ@\\\üø„„ƒµµo•••<<<|}}I$Rgó?éШÔê9’È$N»r¼Ý·páüÌÌÌC?òòðì•0ºìäëAÌKKËë×oÔÕÕuxùCˆªî]ªg2[ZZð_˜Õ¯«@^^®ÃÆ‚ëÑ€ººzHh<}úì·_‰ÙK§«L:¥³ù}zh‚)((Λ7÷èÑÔ‰Nz%Œ.;ùzsòâëëËb±¶mßÎþø÷jiIɃÿQÕ½Kl6ûÂ… øä¹sçäååµ´u:l,¸=/MMÀ @ ‰Çwg~_Z—|||$%¨öJÝïä‹GÌèÃÈÈhÍêÕ;#"ÜÜܦL:H]ÉdæææeggïÐ[æç·téÒËýçÌukjlJHH貪»`RRR±ûöÕ¿¯×ª‘sáBVVVpp¸X§‡èåå³`áB7WW&³þ~Ñ}.‡ëï¿¢¼¼|Ëæ-N444[Ùììó9ÍÍÍÖÖÖÍçÛrO­¼¼|òä)¾ó}»ùäää<½¼8Ð[aèœîG…|«2ç>×]ß`DjJjjJÊ»wu4Í`¤Áž={ì@¨ªî‚IKKEDDDìØùðQ‰¢¢B`P §§§€öêÑËÊÉ©ªª=’RYQA"“ut´£¢"ml¬™õõÎçÛr ­­­œVΧ¾§§ÇñãÇß½{×+aèä«BÚºuë@ ŸG匆–VØ2]Yð iiÇ&OvVPPРË:Ýýl÷®Ý§Ïœ¾xñ"Ñ a0 >|Ôå'wÛï5 -!6LM¶2ÿˆ(Þ¸Eä³€Ò‚ Bú*ÒG`P :sA^÷U¤AúJ‚ ¥A„„Ò‚ BBéA!¡ô ˆPú@DH(} "$”>J‚‰˜?Øß½»0;ç!»F/ÓøoM»n×ÛHXî¸wï~ÿïA¾`ýŸAHXîøóÔäÂ;wúï¢ÆÔÄPWêŠÿ˜š˜˜™™Þ¾]Ø®À/ó»˜óâ«H˜Â;wV,ÿ‘÷)ä_!.—»o? ®@]Áû¡ðÎM7vÙ>$4´ïƒêaéH$‡óÉÏÝû’¼|ÙVIuê ÖÝÌà1o^Ú±cý]€Cdú€k)}PWàPW`D¿tÁéAΠôÑô{‡º‡º×>† òüùs¢‚éJ¢uu†7}hjjÀ!Cž>}J\DüOúúú¼3,*.>tèÐíÛ·»ÜBBB‚€–óçÏ—ˆï`ûV¯|dÌÿ«Þž¶¶ö©S§za7}Lè®8ŸuþhjÊ£‡[ZZèÊÊÇëÍ7wÔ¨QuêÏSW¯^íÍ@ûž>tt>”CÔÔÔ,--%("~„_ûàÀúuëTTTðYzúzå•ÚZZØÒîoGØ¥"¢‚” ÆDGã“›·lòí `“4YÚÜ¿žüuã¦M¶¶¶7l ÉÊ–——_½võúõk£FÙòló³8ü6Xú>|8ß|‡?ÂG\°e«ùq…ÔÙ..³]\º5ˆårAÐp— ÜÏc0Ü+A’Éd§ NødHh(]…Î;ç îŠ#Gêëé%&Ä¿6âééÁb±°­q{°e¢`飸¸¸³E„#øOæ¸\.—Ëmû„„ƒææøä£‡üü~´¶¶611çáqûvÁ‡¦<Ûár¹Y粦L™jll:uê÷ç³Î@[çr¹\nTd”ƒÃèÛ· ÜÝÝMGvLL<„/-yTâ¿ÂßÑÑÑÈÈØÞÞaý† µoßò­ûàŸž&&¦ß}7éüùó\.÷رã'N266õôôzñâïîø'öº¢wñnùÂ…¿GŒ0(..æm°`Á‚Ù³]ñ#ºråÊÌ™³ŒMÇäÈQñôô^¨=éŠ7oÞÒUTøVkÛ>— %J¼¼|LL„y»û§sxû¡µúî³Ù„§àr¡¾þ=ƒÁÄþ½ßÀå¶ý²À–>|ôhž‡ƒÁ ;p@UUuÁ‚EÅÅÚ~$xZ^¹r50(hذaô[æýäÉ|)ÿ?ƒ¹kõêµ/þ½téÒ½{÷žÏÊÆ–VVVê ÓݺuÛáääàà ¢ûE?®ðç]·±±qÝÚõß?5vï^MMÍààÕ‘QQ99ÙÁÁAaᡯ^½Z½z5Þ¾ý!ü÷óÌß½ûKŸÀ傃ƒÃÀù%_úüÅËÜÜDâÚ‡»»;>mh8òرc¼gª{vï¡++:tJ¥€•¥…»»ûÁƒ÷îÁÛ`ÿ?0lØðÈÈlø:dè`W==è쌗ÅjÙ¶m«®®.¸ºÎþõדYÙYNÆ€½½ÖÌØÄxøp½3f”–”èè¶]ÄjhhX¿~½……9ÚÛ;ää\8ýçââThxß°uëÖŠŠr55µ¡£ún\Í …<ÛÅ%)9988PFF232dddœ'a X,Öúuë†éêÀdgçââ⃉ç¸Ïëê-è“€?ÕÆV­Z©¢¢bjj:cútÛ¶  k×­µ¶²[[›‚‚üî¿Ý„táÙ¡K"qí#4,û˜MFÃq¹\‹•Ÿ—çíã-..ŽíFv<žžŽMâ-›››‹‹øùùá›ÕÕ6dÈN¯}p¹²²²:::øÒAƒ¾©ªzM²X¬¤¤äììóåå--,¬Á³²gÚ:ÚØºÒÒÒææfXc¦¬¬lmm%&Ö¤¦ÆP¨zýzàÀBû:²§¸Ø¹61sÖ¬„„„ÓgN»Îve±X¿ÿþǔɓ%%%±±½¸8ÕÄÄolkm“š’úøq‰––¶à· ‚f³<~üøý¢¢ü¼¼¢¢¢k×®ž;wnÑ¢…Ë—/Ç6*--miao\[G»ûo7!ƒÒG°6a Ásé”ËsJRWÇd±ÙGŽ=z4•§§µµœc-Ìz‡£¤¨Äû®Ñ•éÜNnr¨TqÞE$2©•ÍÆæ„……geeùùùIKË0u^ÞÞMÍÍØR.€””$ïº EJR ŸC"S •ÕÚÙ!äwØ}ß²¢¢âØñã2NœœíâšS[ûvöìÙøÉÐdH$2ÞXVNÞ½ctýôYÀB0a`0˜LæÒ¥K““ϙ㮬¬Œ½e$|ã ¥ûowÿwγgÏz´~¿ <}`÷Møáp¡í~ &C¡PæÍ7}ÆŒŽ×Å[ÊÈÉdf=“wSÌúz)i©NFÀ?0á~Øì™3g¼½½ç΋-yŒÝiÇ/nµ_÷ãßóxlB?>ÚÅé:{ö‚ ïÝ»—‘‘idd¤¥­Q=“ÙÜÜŒÀàõë× ''×å[ÐûQ÷Æfi4Ú÷ßÿþýç/ž+))õðíîçÎ151I;v¬ûïÜ©z_=AøÃ ¹ük[J¥Š›››åçç  %%µo_\}ý{¡C/\øû|ÖùÀÀ@ …Âår»x z=l¡ºbÑ¢EßþÖÎÞN]}‹ÕrãæÍÓžvt­®>낞¼ÝýÜ9Ýÿƒ}h{äÇWúûØÿÚݺãYª££›–––p0!*jƒÁ““¡?ÂmŽ~·5ÊnçŽñññ±±±ªª½¼<ÿû¡épß<´ iÛÖm!!¡³g»Š‹‹ò?ŸË0ߺ|;£êìF¾ÐqWô¾-;99íܹsÊÔ)ââÔ‹¸ %%µ#|GTTÄ£G¥ŠŠ +W­œ7o^wÞ‚¾¸›–¯Xñ÷…¿?^Só†B¡ þöÛ¥K—zyyuö–ñî«Ë·›ÎéN-++ëénz€´uëÖ>Ê -­°eº²àÒÒŽMžì¬    ‡Ç<[ønÒï÷îÝß¶MÝÅòº:†pqÞ½«ýýSП]ñçéÓ[·l9yòäСCñ™ÑÑ1ýu&;;»bh®è¦þì¼6mÜØÇ&=þ<íØ±;w4Ÿ>­;wvº€–‚?˜>üÉ€m¿×€´„Ø05ÙÊü#"ñ½ú¡+ž={öêÕ«„†òñy~GODÂÀ‰TçˆÑ8yAú¥+"## ïŒ4¹fíÚN¾ Óç1t‡ˆ„ÁG4£"áé½'mú¡+âât¶;y;D$ Q#jýÐJ¢uuÅç]û¨+p¨+>„>ˆÝ¿A]C]ýQáéCÔ;¨ß ®À¡®ø\™>ª«kÞᤥe°¨+PW`ð~€Ï!–>LMLާ§µwÑvE]¨+þcjbò¹ú%,}ÈËË'üévþM3 ›¯ù¿¿¤§ikk™™õwqcôøñÔðøñ¢Cè.¾´~åò\mm-á‚þ’`Ÿ¢£ ¨+0?¹xéÒ¦Zºlüï¿ÿø¥uÂÿ`ÿ«ffaCt"<”>ˆt;ÿ&Ñ! ˆðPú } tXɱ¢ôA$4ú@>k(} >ÐèãÓlݶÍÐÐ(2*Šo~zúφ†FØ?KK«ï&MZ¹revvvûî»wÿ~pðêqcÇ™™š98Œ^ºlYNv'7//))©³¥Ÿ:úHL•™òË#vFz{ybs¦ ·›^uøÈ===lRBBbúôiÉÉÉÓ¦}Ol`ýƒðs“.‰ÐèãS§444æ¸Ï¡Óé§þø³Ëön®®ÖÖÖ™™MMMpøðaqqñ-›7ã¹££««¯ßöóWZR`oï`jj6ÙyJ||ÞìÆ›žž^ææ¶¶¶Ë\ñäÉS|Ѻuëç̙ûÍùóøû·¨wïÚ=f̘ÂÂByffcÇŽýé§¶³†ˆÈÈC‡~bÖ×c§]ãÇ;ÁÇÚ>ï‹÷äåS÷K& z¯ûý7.—»ø‡ÅÐGlöƒ¦¦¦sgÏMœ0wæ„ ž={v÷ÿ#**„—¨Œ>ª««soå.^¼ˆL&Oünâ/?ŸÀZ-x-ÇÑ£oݺU\\lff–——gfn&//ßYãþñññÑÑÖÞ¸qNñüEÉãRlÑÍ›·üüüìFÙÅÆÆ6668çåå•‘qB]]½;Á3õ»víZ³víÐ!C²Îg…†„ ò­““Ó’~à´¶ž:õçÉÌL ˆQøVüÔÑGoí·½;…wtuuNŸù+9)©ªªJ]]}îܹžžXµ`Bää766ZZYðÎÔÓ.K£]¾rÅÈØ˜¨Àú }t×éÓ§[[[¿ûî;øî»ïX¬–sYçº\KM]jªk˜õõõõï¿QÿF@ã=»w+++'Nž8q¢©©éôÓWc‹âöï:tHtÌ[[›qãÆ&$$´´´N>ÜÍàY¬–m[·ÊËË»Îv1Bÿüùl——§ÑhdI}ºú uUUU¾{xçEèý¶÷ººº¬ìù¡ÄƒËW,OJN²e·k×®¤Î¯ÚôƒÅÿH$ ®õÉdM-­¢¢b¢¢Bx‰Jú8uꔎ®.viÃpäÈAß ú³ç/mÃìnü‚d±ZnNšô„„ߢ–––¢âb'''ü¬‡N§[XZÜîî}YYY]]|rРoª^¿îΊ=üÞ‡ÐûmÃá¼ÿ~û¶íßOjan¾qÓ†Qv£>LàmŽ×U¯eeeÅÅÅùæ+))VUUÂG$ÒGqñƒ'Ož:Žv`þÇÑÑñî½{ÏŸ?¼beEÐétYF“yUþª³– F}kk«²2½£EL‡£ ¨Ä;SIA±®®»uC©Ô~ÄId§£;Gíõpô!ô~ÛS0¬¬­ð96Ö6L&³¢¢¢'ö›Ã&S:8í"S(Þ˜ûò ï}tË©SÀ¡C?e‡ý;–v N:%xÅK—/S©T}}}°´´¼]p»³Ï¼œMLL¬¦¦º£E²d2¹öíÞ™ojßâ—QÄ(¾·©¥¹¹›‡&XûÑGßíK0Ͷ?uý°o—da?!Š Šï™ÌöŸfCQQ‘>ħ‹uöì9Ñ#“’“xÿ 6üôé3òë/'Näææºº¹JJJ€¯¯/‹ÅÚ¶};߯¦Ò’’þ§š™šþuö,v›†•Ji`Ô«kjòóò-ÌÛ.ÚÑUU**+ñûÌúú’ÒÒn•*ÁêüWeûÑGOöÕýý¶7nÜX¸ví>çúÕkŠŠŠÕ ±÷^¡¥¥Éb³+Êù‡?eÏŸëèè‡ø;/W®\}÷î]`P …¹9ïüÙ®³BCÂò ,-Ú>Æùù/^¾`5³*++/_¾œ›—gkkðßMM##£5«Wpss›2uê uu&“™››—½76@oUà*oo___///e:ýÕ‹—K­[»–ùù-]ºtÅrÿ9sÝš›¨Tª¯¶å Nã“~JŠ÷òòzóæÍΈ±nÜËÀhii555ýrâÄýTqÞKÐÑ—žìKð~¹\nNN<*yW¯_STP ++›˜€½½¥…Å–-[ª^/SWWË:—•›—·qÓÁ·{û”••‰Dº{ï®ú ÷¿*++«ªªllºõÕÛÏáç&]">}œ:õ‡ŒŒ ßí}˜4É9*rןœÂÓGhHHHH())éé ß½{×øñãyï,ºÏu×7‘š’šš’òî]F3i°gÏ{ÐÓÓKM=º|hHhcS“ššÚ´imß²±±>pà@||üÊ€Uâââæfæ‘‘Qø]ÛáÃõBBCâãã““’;ø‡~hiîú9.˜1c]\\âöÇÕÕÕÑéôœœê¤bOãÓ“} Þokkk``Þ ,4 ìöÇ퉳wï¾Ø½ æÐ¡CBÃB¿Ÿ:Uˆ]÷:niañ÷Å‹“&MÂg^¼xI–Fspp 00‡ž6F0ôˆ-\û®¸zíj€ÿªœœ,…¶‹³g»eàOD€ýÚ“Éì²qMM zÚØW ýÅ­`övö††#MÅ&¯]½VYYáããMlT¥"¡ç}tiçÎø•ºŠrRÒ¡‚CéƒHhôÑ%UUU+«¶o£ 6\Ww±ñô'.—{åÊ•+W®`_ñèð5±¢ôA$4ú@º4yòä._¥"¡Ñ"˜½½=ƒÁüš@(} >ЗÖAÐèìÚµk×®]üš@(} >°ÁÅäÉ“ñFû×ÄFHdúÀ~÷¢ÿ"HgFõîÝ;Á¯ Dä·N…‹A¾xØ·N»“ jkk üÖ)‘󂾬 èKë^QYµoß^8š’òç©Ó¿ü’N騀ÒÿN»wïÂ'·nÝ6dÈ·¾¾¾Ø$&‹/Ú¸iƒªª*«™UQYqùÒåÀÀ [[›ØØX*•Š5HOÿ9""BSSÃÃÓC]]½¾¾þVnnðêÕ1{£ÛWÛîy¹¹?§ÿÜ‹éCQQAKK³ëvíúNá¿400ؾ}›„„ä©SlÙº•ËåΜ5³·Âëueee™™ÇÃ&縹%'>}úÌ×S";77°'žtöš@§ …âäô¡ptxx8N烳0·ÀjЀ§§ç/'N„…†í‰Ž^»f ܽ{7""b츱‘x±¸Y³f•–”°Ø„ÕIëu......­{öìYØ¿V¿ÆÁÁ~ò”)ž>-Êéãð‘#zzmÎ%$$¦OŸ–œœü•¤ÌäÉ“kjj¿&ÊçzíÃÍÕÕÚÚ:3#«Ûrøðaqqñ-›7ã¹££««¯ßöÃWZR`oï`jj6ÙyJ||ÞìÆ›žž^ææ¶¶¶Ë\ñäÉS|‘à’÷ŸZæ¾K‚w—˜xÈÊÊZ¸]“(d2™,-#ƒµ!“É4Y quºÔÔÔtîì9¾GðO˜0áÙ³gwÿ÷?¢¢êgVVVxŽèì5>ã¯9Ž}ëÖ­ââb33³¼¼<3s3¼.\{üãã㣣­½qã:þâù‹’Çmå—nÞ¼åççg7Ê.66¶±±ñÀ8//¯ŒŒx¡Áz±Ìý§ú¤]»Ìšõç©S;".Z@¥ROýùç“'÷ìÙÓëQõ–ü‚üÆÆFK+ Þ™zzÃei´ËW®X¿!üÆJ—>ãô¡¦®5Õ5Ìúúúú÷ߨ# ñžÝ»•••“'c%²MMMñEqû÷:$:f6r14éì<ùpòá 7t' ¬Ì=VÿÉu¶ëo¿þvþ|¶““o™ûžfoíZGG'>!!ÀßÿDÆ st$æªPw<(þ‡D"ih|t­‡L&kjiÂë3Nm•I]µ`±ZnΟï‹å^---EÅÅ‹/ÂÏzètº…¥EÁí‚n†Ñ‹eî?Õ'íúÁƒ–-]fldеA„„Ò‚ BB'/"¢ÐÉ ‚ _,”>‘'/x)à¯êê ^¢òBdú@_Öô¥u¨+0ŸQE'/‚ ÝyA%ú'/hô ˆÐèAD} òÅB£QÔÐÐPSSƒ?~UaÀ€?Ÿ Aˆ‚F"'&f¯ý”)S§M›ŽýKMKëùfsóò’’’ÚÏ¿zíª¯••µ¥¥•»»{~AnV­Zehh´i㦞‡ñ©Â×/o{¶ØÑ”W<«~ñУ’‘OS^^žœœüã~·nÝ,(È/(Èï­§ãäåæ&'%óÍÌÈÌð[ö£¼¼ÜêÕÁÖ¯761©}û–¯Í¥K—óó Ú—kêeee™™Ë–-Á&縹UWWŸ>}¦ÿ#A:ôu¼°X-ââT¢£¤¢¢lmm¤¥¥ûz_åÿ–GìŒôöò ÄæLkצ¡¡!<<|e@@Ddd_ÇÓÞá#G ôôÚŠœKHHLŸ>-99yÚ´ïû?˜þGøà¢K¢2ú(-)Y¾Ü”í(ss O/ïÂÂB|‘€RòÝ\777wÞÜyææ»¢vaó³²²¦M›nff1sæÌ þ^´hñ~ËàâÅK††F|TbnÑ¢Å|UïûHÈöÐ~XžžÞVVÖÑÑ1íÛܸqÓÓÓËÜÜÂÖÖvù+žGFFæÜÙ³çΞ=v, îÞÑÕÕ9}æ/'§ ÆÆ&ß}7)%%•÷7^qñƒ¿dlÞ¸· F¿É/Èoll´´²à©§7\–F»|åJÿǃ´''/{öD+++'&¤R©`ii9wîÜÄă11m¿~;+%ßÍu7mÜhlb‚ïî`üAÑ#ðIíï¿o¶S(—äää  @ÈÌÈ”‘‘žäü¡ÔHß¡++«©©À AjíÄíß?tèè˜=X­ICÑÎΓ'Þ°qØÙÛÙÙÛa-ML†×›9sfii©ŽŽŽ¼¼”x0`åJ55µ¬³Y»víjiiY¸p´¶¶nÛ¶}Î7Þ*ÜýéAñ?$ICC“w&™LÖÔÒ***&$¤~†N^ºÆb±òóò'Nœ€}þ€D"íXxçõ³RòÝYWRRÒÈØŸlnn.~ð`ÌØ1øœ!C†hi}ø5k«¥åÌ_g°íÿþÇ©)“§HIIõîQ ¡¥¥¥¨¸ØÉÉ ¯SK§Ó-,- n·Ý+a±X gÍšecckffáæ6ðÇ”·ÇápÞ¿¿}Ûöï§Nµ07߸iÃ(»Q‡Ænm¤¦¥½}ûfÙ²¥}~`x]õZVV¶ý%[%%Ūª*BBêOÏž=#:„®?ú`0˜,6ûÈ‘£x9àr9¼÷ç:+%ßued¤yÇÞLf=‡ÃQðÑ×(ðL*))Ž?.ãÄI×Ù®999µµog»Îî…ãì1ƒÉáp•xg*)(>ü§íL-,<<ë\Ö~~†ÆF2Ò2LF§—wsç¶ xñâ…•µ>ÇÆÚæúµë’’â¬[»–Ãáà5´[X,&“)%%Å[g»ï°9lrG4É û+vLMLÒŽë~ã;w:.ß׈O²²4 …â1ož×ç„XWV–F&“kßÕòά«{'-õáN‡›«ëüù îÝ¿Ÿ‘‘idd$"™åädÉdríÛ7¼3ßÔ¾•——Ç^Ÿ9}ÆÇÇ{Þ…ÂJ»ª­­©­u÷Þ=€#d—d¹¼¼üÿíÝ{ï×¾vgžyΙϙu>óœçœ9Ÿ¶¶¶Ý»#"ø¯^¾|ùòåËÇmíl¥±A}ÐÔÐ|ÓÔÄãñf^šXlMMM9 @º£F‡/\TWûDwÔQþœr®ï•Ê€âÓ•Jµ²²*¾Q¼%x‹¸_k,K£Ñ¦Lžœw5ï‹/¾ÀZjjjó|AÁç`-ÿ—_ ©©9RwäPuõä”Nom ØhŰZ¿~ý$y%S}—[W[‡Ÿ¯AU×ÔØÛÛË'E±´´=w”””(*Nŧ„Ðöí¡ÞÞ>ë}}WxxŒ1¢©©ùÎßwx¼àà-²XÖ£ÿæ -Ì]Ì%K—46²Ž®I"0 ´ÜcÅþ¨¨¡C‡Îwq‘ÂJɦÀÀ7nÙ¼rõжֶãÇS©TŸu>!2™l=Óú׌_ç::ꎕ›“ƒ?„200hkkûé矧LžB£*Nšdog?ƒÁؽ{÷³ç›FÒͺ’UT\Ì ÛE&“étU†•~q%%%íáZ2emmM"‘ÊÊËð飾¾þÙ³g663å†B”””ˆžA,--º§8?uŠš4É臾1bDttì† ~{*ÿ©´´°Ñ²ö1ÑÑåwîøùù9òÝÆÇŒ§¦FÇ÷qqvB-Z¼ˆ?)K663ÙMìm[CÂÃwÒ}öìÙQ£ºö®½{÷Ž×¿té²Ù¿]¼x0>¿¬£ãww÷„£ žžž7"„H$ҡÇÌw9qüø¶­!‘û"åsŠZÚÚÚ3Œ«yyøÆ¼¼kjtºƒƒƒ¢¢’}ˆg„ ±½\×úe(ÿ²HŒ@O±–ŸÌwq™ß5¬`±XLf˜Ó¼Oñò BËÝÝEÞé`XY•——á[®_ÿÿtÖ,›Y³lz\V[KëÈÑïð-øU)))…‡‡…‡ð»:]õ›;¿Ù¹³ÏÀŠŠ ûì#uÞ>Þ[ƒC^khtMvdfž_îáA£ÑäŒ<õ—ÑQÒ‡<µ´´$$$ZÏœ1l˜F]mmJÊi•… b¯>zôøOþM8š0{öœ/¾rcogof655õÜÖ­Á¡‚ü‚úú:ŸµŠŽKæýgÆIDAT`ôA\ ¥¦ºæâÅK,V#Ng0¬¢£ð™¶ÿþÒ’’©ffLfßßÉ@ÖØÏ¿tE{„Vrr’´~CHd0ú .*•z4áHo¯&%”g0@8챑‘±bƒ‘›þ2ú ÄÔ)¯¤¤DÄÜñnô¡> }$£€„`ôŒ>ê/£Ež¸íG¥€e >>ø(0pÝGß œ:‚²ò8ðQ`&*WWWWWWþÓ æ;99ÿöÛ%cc„PxxØøñãù¯‡„†ædç¸-qû¸eèô™3¦¦¦&&&ØSææ¶8%%eñâEŠ LÖúËU§D}ôVò!ïèèXTTä¹ÚÓÊŠ‡º ò³³³±ÅÝÜÜøÕ³…#þÁ ¶á¥¥¥^ž^––Œ¹sçž:Õu´“”tª©¹ÙÌlš™Ù´y󜺯j˜††’«ü„Â焹…B«õILmmmW._qqvÆ7:;;?~ü¸ìöíÞ–`ô!¬äý矻†…1YGa³Ø&&“ùØì昘˜_}¥¯¯ßÞ&Xu1¿ ûöÎ.ÎÌ0fÃëׇnkmÅ/Þ±ÙÍqqq_}ýõx=½¬ì¬È½ûôôÆ999øûwvtüç?¿ýš‘R¢¼/æøöíÛŽŽŽW¯_%'§ÐhÊK—,íqÍ%%7B) ×£7o´¶¶Î°fàMLŒÕèô?¯_Ç×-xúËèƒéCHÉ{ ‡ÓÆdN77ïqñã‰ÇŒŒc¢£±j†zzã–/_a"ë å‚Ãiß•÷Xîq>ó|vvŽ“““ºº:N'“HØBüü±S ÍÄ„CÉÝWËb±üÖØØdölâL¹w÷‰4aÂ3Ad2YßÀàï¿ï***ùè/£Å¼ôYò!ôÉ'Ÿôömƒ-îä4_ ÕÈÈXOOOvË“šš–;0£Géóp#Œ¹ë̙ӱ11úú6n ,++èðöíÛmÛBZZ[ããc•zªAMÏŸ=WSSSVVh>\óÙ³g InúË™Å>ú,yRU"P'Y`q­áÔ×ÖÖ–zœ A¥~°óȤξŠËóGæÎûtÑ¢E‡>}úý §=xëÖÊŠŠSɧƎ+õ€¥ˆÛÉ%÷”ÝÈJJܾ>„þF¢ê­ä½X‹757㛚š{ë?x(S( Ÿ>}Êoáp8!Û¾¼}ëvâ±Dþé ÂÒÔÐ|ÓÔÄãñÚ›XlMMM…„$7ýeô¡øôÁ/yÏoÁJÞ‹µx!®„â‹—/>| å(‰‡J¥q>ü¸žŠÍf———óã¸\îöíÛ ‹ =2mÚ4ù*)}—[W['Ð^]SCä_©è/£Å¼ ÑJÞ °) (pó¹sçV®\ñúu“Öý€yà100hkkûé矧LžB£*Nš´es°®®®±‰‘ŠŠJmm]æùÌææ&?¬ÿÞÈÈ«WóÜÝÝøç¶ÇKØÚKÖÖÖ$©¬¼ ?=\__ÿìÙ3Âq—.8ó"¬ä}Bbâå+WÆŽx.-] ä½övö11Ñ ‰‡©3rí:o™FKŽŽsÜÝÝŽ&°X,mmíÜÜ;{»ß/]ºôû¥¶¶¶aÃÔÍÍÍccbMM§`ýï”—#„222222ø+Yµr…(õ±B[[{ƒq5/oÁ‚üƼ¼kjtºƒqOIE}"""F2|*kÙ-í¡ÝnZÂHKKwuýLCCCH//O!k˜¿àByùë®îíÎt,ËÙÙ%((pÍš5"lBÿwèãëþQääo ÉÍÍÒÐèšìX¾|…­í,¬bö@õàÁÃ!ªt±2È’%ç=b]¹,ìbá;æÍ›7+**…﹡=^"„†Ð(Fºjõ7Î(~î!ÔÒÒw=ÿzù;YYY~~þø’÷`в·³73›ššz{Z_P__çã³V±QÉA}âàExÉ{0˜8°¿ºº{¬=B+99i0üaÀ܇„—¼—XMMÍÒ¥=ÿü,33cÀ\Z6°éèèèèè` ;Ë+u0úP¼Q£G_¸ÙãK#uuå ¢ƒÑ‡â)S(¿°€õ—Ñ!¦NxpÕ)@B0úHF õ—ч"§N¡œ:>>ø(0pæ¥op±6‚‹Öqà£Ààเ„`ôŒ>‚Ñ@B0úHF²%¼Ä$¾Æ¥dkPÑ£**.NNNƷܾu+ `îܹææ¶³l°zQ˜Â¯¿þÆÙÙÅÂÂÒÑÑ1,,ìÅ‹RŽ^¢öEmÞÜuo±Ô³gÝÝ=î= ÁèC¶D¯D/»5È‚èQ¥$§à[žÖÖª«[·n]dä^ÿ€€'Ož®÷ÝPQñöêÉ“I>Z¶lÙž={–-[–““ë½ví›7o¤¿ ÒS]]ýKFƦMØÓ•+V¼xñââÅKŠJúËè£ÿý`ŸÃiWV¦~L%zÌǯA>&*WWWWWWþÓ æ;99ÿöÛ%cc„Pxx¾J¶±‘qHhhNvŽÛa7ÈT¬ÓgΘššòKÒÐh47·Å)))‹/Rl`²VWû¤¤¤´ôTrŸ=-ÌÍ---ãU§xYYY‰‰Çž>þ`üÁ**ÕÕÕV¯öôõ]ŠŽ‰IOKG™™MC1"77G`UÃ44””(¼Nì)>w „Ì-,B}–¼T ¶¶¶+—¯l Â7:;;Ÿ>}ºìöí^"»´ôÖ­0&³Ïž{##åOo‘>®ç_ß±ã«Ï?w c66²Ž=Âf±ML&ó;°ÙÍ111;¾úJ__¿½í­ÀâùùÛ·ïpvqf†1^¿>tèp[k+~ñþ‹ÍnŽ‹‹ûêë¯ÇëéeegEîݧ§7ÎÉÉ)Àß¿³£ã?ÿùí׌ „å}1Ç·oßvtt¼zý*99…FS^ºdik.)¹‰p- èÆÍ­­­3¬øFc5:ýÏë×vú1w „¼<=ÓÒÓRÌa8!Òljc'̦NÝ·oöÔÐpâ¢E‹ñ8œö0&sº¹y‹OéíÛ[ÜÉi¿†¶‘‘ñ€¹ ²šš–;0£Géóp#Œ¹ë̙ӱ11úú6n ,++èðöíÛmÛBZZ[ããc•zªAMÏŸ=WSSë^0pøpÍgÏž)$$9ëb#T|úhjjîììÔöAÝ©a>UUÂÏ=.®5|8¾Q[[[êq*•úÁÎC"“:û*.ohhhaaá2ßådÒÉaÃ:Œ•ÃiÞºµ²¢âÄñc¿,·“Kî)»‘•”¸}}ƒ@¦3f ¤Ajjt2™ÜÐØ€od±ÅZ¼©¹ߨÔÔÜ[ÿÁC™B™h`øôéS~ ‡Ã Ùöåí[·%òOg–¦†æ›¦&'ÐÞÄbkjj*$$9ç l@­§§éã4mÊäÉyWóø-555¢ß6[¼wåÅË—>r”ÄC¥Ò8~ ü1±Ùìòòrþq—Ëݾ}{aQáÑ£G¦M›&¿@%e` ÏárëjëÚ«kjˆ<ã+Eü¡¯ÿþX^__Ÿ8éƒS§þý7maîb.Yº¤±‘•ptøpMYÔÔ°) (pó¹sçV®\ñúu“Öý€yà100hkkûé矧LžB£*Nš´es°®®®±‰‘ŠŠJmm]æùÌææ&?¬ÿÞÈÈ«WóÜÝÝrrºÎòŽ7–°µ—¬­­I$RYy~z¸¾¾þÙ³g66„»\X°ì`l,ø?ÈÐа¢¢B "Dúp°wˆ‰ŽNHL¼|åÊØ±cÏ¥¥«©ÑE\ÜÞÎ>&&:!!ñСÃ#uF®]ç-₽ͧô ŽŽsÜÝÝŽ&°X,mmíÜÜ;{»ß/]ºôû¥¶¶¶aÃÔÍÍÍccbMM§`ýï”—#„222222ø+YµrÅ7;w*fú¢­­=ƒÁ¸š—·`Á~c^Þ55:ÝÁ¸'Œ¤KwïöpšIáã !ÒBÈe¾‹Ë|ì1‹Åb2Üæ}Š= ý24ôËPþ~~üü6¼_ÜÅÅÅÅ…ÿÔc¹GŸïØÚÚª¢òÉÇÆ-møíê¾á±11üÇJJJááaááaü–U«V®Zµ²·5gfö\pȼ}¼·‡44¼ÖÐèšìÈÌ<¿ÜÃF£)60ù HŽ‚飥¥%!!ÑzæŒaÃ4êjkSRN«¨¨,\¸PFoÇf³ï”ßÉÍÍ555“Ñ[©°·³73›ššznëÖ`„PA~A}}ÏZEÇ%'>DB¡Pjªk.^¼Äb5ÒétÃ*:ú€ì ©ßº}+dkÈdSÓ;¾”Ñ[i9p`uu5öX{„Vrr’ìþ0ˆÒ‡H¨T*öó骩©Yº´çŸŸežÏ0—– l::::::ØcÂÎòʤE5zô… =ðÔÕ•s0ˆåñãÇŠ¡o9}(S(¿°€Y˜›§¥§‹ÞùÖ-–Lãéâ/(ÅÝ&Nº¥k >è¿DüÁ>Rè-?`ô@?¦Ø)}@PpæE(§ŽÏ> z¼@©¦¦F®1õæ> (ì'ù=hôèA~°é‚âçˆû÷ïóïß¿Oœû}@ú€ ði»ÁGEE¡î6g^ (ìp÷î]…ç > (¢%‹î }@PÄO„˜û^V~ÿ ÀÍòŒïèèØw?©þQà''÷P5¿ ßÇgµõÌ3¬W­ZuãæÍî}BBBÌ̦…1ú¿D4Qû¢6oƧž=ëîîAüJZ‹2ôD±"}³Ø½BˆþQ¥$§4þ’ñKঠuõ¡;vlßµsçtsó†×¯ú\»öç7ûÅݤ«««ÉÈØ´){ºrÅŠ/^\¼xI±QÉ ñÓ!^ˆYì^Š8œveeª(=?棨}Z} f­·7ÿ©‹»õiii‰ŠŠÚ¶uk4î¶©„uúÌSSS~Iææ¶8%%eñâEŠ LÖ,ÌÍ÷FFzyzöÙ3-=}°ÿ`_`Äž½x±›¥%ÃÍÍ_RƒP”––zyzYZ2æÎ{êÔûü7ßì\¹òƒ{ñÅúàà`ü²ÿx¯õ±²b¸~öynN.Bè‡~üì3WKKÆZŸÿ=ùŸ@leååž«=--ÎÎ.gϞÿt¿ªjóæ`ÛY¶VVŒ5ÞkKKKâ,**ò\íieň‹“ࣲ±Ñ11II§šš›Í̦™™M›7Ï !tþÂyççï‡êìììqýGŽÕ9rÉÒ%"Æ£@mmmW._qqvÆ7:;;?~ü¸ìömEE%––Ø-?úüÇÂÜÜÒÒBQq’"""F2|*kÙ-í¡ÝnZÂHKKwuýLCCCH//aY3>¾4'÷_}}uÉ"xôˆå4o\h¨°<"|ǼyófEE¥ð=!´çÂK„ÐÅHW­þƼ`™“û¯ü߀©ÏÜ!#Š™û µPÈÖ¤ˆs€þÒ@B>‚ô¤€„ }$é !H I?}èè§¥‰Zž iiéú2øQ»ô¯:ecƒ‚ qèèc;¦tÉä¢õY66²ˆ@(0÷¤€„ }$é !H AúHÒ@B>û²1}ýK—~766’E4…¨¨¨”àªv±Óv9iEE¥¸ K²«Ú%¹h®I ˜ûH Ò@B>‚ô¤€„ }$é !H AúHÒ@B2¹Ó:ÀNJÎ/(PtÒgogç뻾Ïnÿýë¯GÉ!9“ßo^À …åŽ+—/+:雿`BHxÁrGhh¨¼‚’Ÿøøxôî±¢ƒôÄ_Pùë¯MMMŠDúRÏœYëã#<}€Øäþ#úF òÍǃôÄ6È÷ŸA¾ùx>€xêè]Ää›/ÒÛ€ÜD7È7.âáÉ@aQQ@@€Í433''稨¨W¯^á;¤¥¥›˜˜<|øˆßr÷î=›™3»¹½~ýªÛú$$ÅÍ/++Û²ÍÁÁÁÌÌÌÆÆfƒŸ_vV–´â”À±cÇ,,,„÷‘àF@l’ý©õ&õLjLlŒ™™Ù–-[454+ïW~Ÿ–ž•••rúô„ñã»Þñ°co]^^îïï7n츓IIêêêR‰Gô•ôÙ3ýûïì߯¯¯¿Æ{ÍèQ£›››ÿ*, =rä»Ù³ç|l ’á!$4rH@N¤˜>nߺçìâK&“Bóœæ-Z¸pÕªÕ!!Û2~ÉÀ±¿~ì«¿´´dãÆM†“ ;N§Ó¥›ËD!üoß.;°ÿ§Ÿ~K¡tí_Ë–-«òÝÀíàÊ?ZLWþ•ö»ÃÁ tÕ)§S(J“I"‘øcÆŒõ÷󫪬ÊÏÏ7°îúë/,,òóóŸ2eò‰ã'TUU¥ŒT6?%%YYYy÷îÝJJJøvÃI†&&&<¯ª²*88ØÑqÎôéÓv1w5¼~Íï;{öì’’›«W¯27·˜3gÎɤ$üz*++·lÙbkk;}úô $&$â_ ²±™iaaáååURróý¦ñú^‚?}±Iö§ÖãzŠ‹‹­VÝ@£cbŠŠŠìíí±®¡‚ü‚C‡Y1¬:L£Ñ¤?©ô,..¶´²:thoÝêëë'Mš´dé’¡tµ'OŸ&''o>›šúníˆÍnŠýêëoÆëéeegEîÔ7ÎÉÉ !tïÞ=É™L¦¶¶Ö¿5ÿV=¸½QUUåš5Þ&&“##÷ 2$##Ã×wùsç&OžŒD}@úò ñ7UwÍÍÍÍÍÍ£Gé¾B‘#I$R]}=öözLl̘1c¿;|XY™*ÝÜ!:á›/d‹ølílmíl±ÇÓ¦O726^¶téýªª‰††!âq8í»# 'MB-w_~þ×̬ììyóæ!„âã㵆k%§¤Ðh4„¹¹z·çyòÄ·ßB¨ï¹É@úb“âèãÝW(ðöåiooŸŸŸêTr@@€Tèé?ª§-âãp8)))999uuuíí¬ññãjƒ‰Bˆ‡ÔÔÔ&ò×0jÌèçÏŸñx<§½´´tݺuTª`öäp8ÅÅ7Ö®õVVVæ¿ä0{ö?þˆÿaôOZécÈ!ªªCž<}Ú}…õuu<OGgd×K<„Ú¶m›¶¶öñãÇ©4ê:ŸuR‰O*éCUUUUUµÇ-â‹Ú••µiS ™™™ªª*›Íöññi{ÛÆßÏñ)!D"‘;¸<Ínêèè>\«ûÊÙl6—ËMM={öl.ÎÎŽŽüé(˜^B 㯿 ÕÕÕñíyyy¡ +ü—'Bh×®]íííßþŽJ¡®ö\-­0D×çæÏ˜Áøïÿê¾E|¿_úÝÛÛ{ÕªUØÓ÷ï#Ü9éžösö¶tº*…ByùêE÷TUU•””V¯Zí¶Ä­{Àμâ>/–5^ÞíííöÀ¾'1Ožü/)ùÔDÉ66³ºvWÜNîäì÷KÆ/RŒ„'ÎèCoïµgod$‡ÃÅ·WUUÝ»w¯££ƒËåâÏå^ý¡wùƒ‡;øáGÅëz•BQ677¿|ùJkk«À›*++[ZZÞ¸ycôè1zz¿8óˆ@²?µM›>-hsБïŽÔÕ×}¾ðóaêÜÿé§Ÿ(Êþ¨ýØÙ\„ûòäñxd2yïÞHN;çÀþT uᢅR‰D¬ô!äÕ©S§†††ÆÅÅy®^õÙ箣tG557Ý(¾ùǹñññÆÆÆŒ3233fÏ¥«ûÇüšñ+Â>º xàà`__ß ¾¾ž^^ZZZOþ÷¤ê~ÕöíÛBÛ¶m]ÿÅz?ww÷#´›ššÿ¾û7¯“„`î„ÄßT½ñ^ãmll’žž–p4¡¥¥E{Ĉù æ¯óY7|øð÷oÄ{Ÿ>BJdrTTÔö/·ïÜK¡Ræ»Ì—b<‰²ù&&&éߟž–ÞØØ¨¦FŸ`ôäaÞùæ €ôÄ6 ÷Ñ òÍǃôÄ3È¿~ùæ €ôÄ6È÷ŸA¾ùx>€Øäþ#ºA¾ùx>€ìíì¼Ö¬Ù©è@¤o“iog'¼¾~jjêœ9sä‘\]»vMß@_Ü¥ }1`%`w1™ŠDúìí세Eï*H_»vMÉ—¾¾¸n¤ ._ßõ}îfØ, v³ ~°¤€„ }$é !H AúHÒ@ ÜöVþcHQqÛ[ŸWÿÍm‹=%s¹\Åè/(T•ã§R¨4„—Ë%wtt(:$@ÿÀmo¥P?ÁwttP:•Õ9]/cwR>n{+…ª‚¹Œ˜`Æ¡:(jdû¦/ïçqøùpºæ;8o»Ž\”inÇ«ª«3§O"#„‰¢ÒtÁ] Ýð³BˆBý!ž ëOIag^fk‘ÛÕ^þ©Üú”ÔÑÚÇʃ 6ßAêhUn}ªö0À5Ø·²NÒ&ƒ úR¥”‹I}‘í c­¢ƒ”MKcàÒ´¹ÞJÒ‡¸Œi:·„ÞÝŠ"ÛÆŽ¬šH!ôacd vQlŒ†m»,I\j’ɹvÊ/ªhÖC.Eé ‹%_ F¡mËÖs)‰}Œ3Ò“t)ŒkÕ“¶µFSðÀHv€ZŸm€]5IHCvཨÀCF™ é?¨9è^—ávAŠ×ÈMõ´¶î ·Ö-=ež;‹ùR³Y£¶5uwÇ.5hpÝ•‘s‘AG™,£À„®\„VÍS(6£y"^™v#0àœ1¦‡"Žá/;ÕUYj„ãó˜ ÝV(Ê;Ã2ìqà¯b4ì…Ðù¸Æ«4v"%m‘lHÒ-üÚ<5ÓcæqÀŠVHݨš’úO%-Š—cÀ-éç¤Ããøêæ30¨HñÈKrÅô¦G$p\ö‰ªA jLùsnÉŽ”°@NËF­d0VQ ÜæÕ˜êÔczàQv)ZI¶h+ãȼ´îÂP –% íŽJµ”™’<ÒTˆµi!ÒæÆÑìÔŽ6N‘H ýÅ»~.£Šô’õ8˜t²â>(ç6é^,nåH'CÒ‰”¸ÇÒ­ÇÆ ˆ$q)h¡Üâjt1M4w<*­H‡RPŸtŠ#ËN‘Î8p°5ˆI§ ­±ŒƒE¡˜«± ÈÓD 2¢Ü\k“¢0(–°Hnã(z‰Lë5œ¤aÚ—jçÊY¡xšUعº¡?ìHâ‚4Ã1#)³æMˆQÉK•o•“ ИyRoƒ”ƒké5he}ºUTÞ¯M&{)ÆA?CB|h ÅËs‹áýÑhÔU<~VÆØE=‰ÎîÄ¿V¾30eýrhËÑ+ŒRÙ£í¼ÂøE í6G¶qø©] |Hó1¯‹Õì6CQp›j”ç¹Æå¹Fe)Ì`¬+ÊYBi…³K"’? C85 cj˜ÚÄW1V+‹©MÑ“QùšÃ£D’Ú"6Ó4cš \§ *"0-à1[³5ÙeYªÚ¿\«SôR+F õÆ•œªêzr¶ô‘Šcª j#i¨£´Øf·%TÔ,cL&Ȉª¶h¹6Í T2EPZ¦šÃ4K½,© úeݪõ N 43"þ;©(´1¡Ñ":ÇA‰Êt¾"N3B1©8ºUAÒF’2ø*óT…2ÔRR•ÚdZ7‹n’AZ#æÈ¹êf£;ÁQc„8£ÎPç`Œ†‹âiú¿âVä JE$¿6x˜Ð œÃhiŒJJ‚Då¬Ir€>œ†VWŸ³àŒ+‹e˜ V뎈2pZ²\M8 ?FE¹n6âê#Ȩô®SÙ2r0*F£âV²¬D"n™q‹[¢DÆŒÁÞ?r…Œ†!Œ™à xjƨMëÞvÇ»Z±žàý@zWašœr!LåÒAÍ»M–qqš»p€âeÌrIJÌ×´1Ôq´üŒ–›f|,ÝÏ F¬`7ÛÉ9¤ú ?à*Í6г Ôc|²?ìÑ…’“J®$Á†óWЦ±x… ¨*%NÍW«HÄ/œY¸nʹ2K4’¢€¶ȱ:PsNÍ}©ëõÙ-€ (û”D@ú6ltͲŖˆƒ½bžæê€Zε¼ׂ™¼“J€œXe2ålœ‚:6iVèˆ1h€8Çl­™‡pŽ8€’`¡êlj¾²(=‰%[Î(ÿ*I‚ÊÊp2m4ˆ“^Èé3B9WlKÝÚïϱ÷ç­ˆ4iñ]Š/SúÄGy¢s*S(›ÌhÁJ'gd: «QÜ&ã0(í0¨¤-ã89àrJ3´ñ<=¿‚w MR—© Ô¶ iH³%.ÏmMæ‡$ÿçêÂdý‹s–Œ8Ä×e٘Ǵi=‚²1­‡E«(-Åäà”I>$Lºé³’\2 {‘âÌÂ!(gèõ¤v4w-G4%}¶¤1*J¦kŒŠ8]/F…9J™îÒú²ÞÃdTœ<ê¹£âý2*- n%ftr¡|QffºÏír™‰DG¨§¾)´½9ârû˜qè-Ú#RÈHáp„é1yšÏïjq Ý%¬!“ªÿðd L(Líœ?slÉ„\yõÉcƦÀoïÛѦã àç”g¦¹àΗ¾<'¯¢,w½Òpý©¥©¾~¥oîˆýæ/;¨ñ¢c‹¨¨ƒ¡ø0.d°§¬&W8Ÿ<&}ny`tŽ/3Íã2 I|Ú^ùÁÁΈ ¸¼C¼Jz:©>׉3s¦—¦g¤¸#ÑÄŽÆÈÊ#¦W)œhóŽH‚|¨t;Œ@$Ê÷%ø°i%`L¶QYêÚyÐj8hi¼J/PúzL^3Œ’L9WDàÐÐ@äÉZâ])gjÞÉÉåb0 @–‰ Þeèê¤ â’­µI5¹ƒÅZ1!@_“¨ÆXl5^‡Á0‹æ¨¸üF]ÆP_äë‘ÔáöÑcÄ 5v%9ª}³”‚Ë +o£ ”f1Ÿ„ 3ÆQë½ÈÍ÷Ro ßVL ´Ø€É@ÏLõ2píhô+ CõJU»¹}™ÄV®Ò›­ÈŸ0¿Ó›B‰ÉAµ­N©ø @D 4"دD³åÅ2r ˆ  ÄD€i«¾¸å •U£aÐ’6^ŒH ‡šœÉÈÔY†Íx“è”R©N§S§«U*Õoÿ †2“,X3×dVL‹«d°6H¹29š@dçA$Ód—³®ä$¸Ú¼íùž’¡_F6îGÔ•¶s £â¶&©¥‰¾U?«~:£ê?G¥3*èVÔ ê‰„™é‰8·ˆ¹<ážDs‡iñ¸Á˜×íQ^0½,ööææ éq¹ÜGº‘Âa câæ)CôOB·³Aà¹_.ÛÛPû#™¯¾ß±nÕÊ.ÞÔf èÇË R£ÚÕÚÝÖ5\fó¯Æ<Í‘sÆ`á¤ÌYe¼ ¯ÇÅ Æ2ÓÜU2¿yZi ÅmZ¸W€1¯Ç¸vIÉ1S³³Ó<.ƒRÜ•ã3®?µ43ÍCi)L{à₊ Œ²áâ1d ³\ÇŒwÍ(r-(w7ù–½‰±9FVŠÁ…S €Qšœ7N›¸Z+Áð‡þ‹Ü<ÍÆ@zVÀð­ñ"e\º":k‰qÏ@s ý 嵄c×"¡üIF«yqrÖÀ¤¿¶eN”Hz›¨ÚGû´MªÄ’¡Œfã”ïTù1I&ÄaFR†+=SM #‰Gÿb†€È`qYšèzéÞ §'÷@ \r°8É(÷1ÍÓ â¦´(s]²xƸpãœI˳ .ë#˜3BPg€¸]ÿ~~ú¸Ë b±r’ëâ‚s,ŠEgØ78 [&ù9<5Šóy¸¼ÄÁBKÌ»žapäËD™”çá¢O91šÚŒn k˜ ŠÑ'P6Ï’A {WÔEçNd‹š©?æ¤ЧA²qÊÖ’È«ìml¯ (›`DZÈfqÖC\ŒÓ„JÝÈRÁ"é8f‡Å¶B»H$;§ïäĤVQ1œ $¢*ɯ¦¤c‡QA/FÅy:åuô<˜Æ¨xoFÅpÃ1*Άʨp„:˜A0*ËJdy{N­*&\­±–ÎX[(ÞÑe¶…â-±ÖÎXÔtŸZ5&Ó³¬~·H!#…Æ2S®Å‹§žŠ&ÿÀßžmëß}ùws&uýüʺÂÌŽ÷–ì úŠRƒ™9}^0?vj¶a°œtÏ»Ÿ´SRlñŒœ1¹~Xµµ}ïÁžƒ]ñmûÂì 5uDw·ö|°3´©!´uw׬²€øÉ¿7njmjmÝÓÕ6pæØÀ¨,¼·­£;ÖGÃyÁ ý™"àe={þZÝúúÆÖmû Óü^Ãç1Ì„U·¿[N0ú 3_2;ozi:¬ÙÖþûÕMsRqšÏcd¥y¶ì ÉÈ ÔTNJmÇrà ÕÃfqUïJìïH4w´b£©“Ǭ4ר׮¼&¶Èk3\ÁÕpŒƒ¶O\:GéÒ¤02Òú8rÒ€<]Í—¥›¥pË€áR×9¨–i< =nnœ@ê©ÕNQñ@Î ôE:`´$£…5q'±uÚ¨rŠ™Ê™. &6IfÒ<]!äì#Š£8P n5å+º¦§.Ä’ÁHj°ˆ¸¨%$ÁŸ6À°È°ódê…¢¾6Q·Ë®}!ÂÄ4ª¤}$û}ƒ‰‹v½þ%¹ðdýÙd#}1Ê!QcìÖº5¢˜ÒzÍܹØ8Åh9TU.MD}æ2 ¢©744Q?²û1„¿dc‘ÐSžLËÒES‘â°Rc R©°'0+ÜhÃp¹ûQM1èK’…£4B·¸`­•ŠõcCìÞDº ÀÁÈiŒ“+…½vÕ¥ÔkÚg† …Q†^Y"“ %Óé+A¥\£–ä&*™É×Ô‚\G'©Ø&¢±LÎÒ…rts²3*E÷Q¡Ï âz(pn%z§V÷$ŒÎp¼;f1†ÁÄcOpËâ.—1~TêÇ;nëUìˆ2RÐ…ù´¶þO¿ÿ]AQi #KÓÒ´ó/Ï?”;ª$™=€0nƒü®®ÆÍCΪíÙ¶þÙÛ—@óÎ]ËîZ¶pÓµ§ú¾zçÔ}{»û[ŒÆ­÷vUŒ ø<ÆÔ1éî €ÁØÌ±’-mÙ€ÅÓshI®nDÜëuØl ¥ÃбÿúrInÀ›ê38‡`·Y×ÁÅ5Ò|®“*r§–¤3€mû¯nh ÷ôM3‹s|‹gäŒ+HIõ¹ºcVmSä­ÅÄÙü ï)sòÊ RR}®D‚wDâûÛ£¯nh uc*sËØ‹ïíÇXWO÷êÛΜW¹/åP¤ ‹TcsÇgm¼±é iZk¶µ35;+Í=­$-Åëꎙ4n(¥ÄqéHxiÛåP”ÉZCVW”Ÿ4Õï×™“ ]›÷%&¸3R Ø‘žàÚ¢èÄ x1¸9®,pPÏíÏ“8r¥67¦æˆ@›·J±AzPK°6%DR)”nò."-A„€#_ÑÂê °É# ƒœÊ0©|†¶oL.¬pÙnb[Ò¡‹sÊ5!»"wˆþK$œzJ®2XvŠ&4Wî/‰`·¨& QRùèì- žèI]LÝ(ÃÞopÎE ±Ô$›ã> bR6ÆU“‘LÚII&ÁA‘Hê9^$S¨CdÏ{S.Þ«pEC„mâØ «Ã(¬Ò´ét6 2¨*°¸²BÆ êpÙ¯x6ß–íµ±(•Àžzµìáab TÓËxhe˜%34*µÌ'5…+ÚÉ\Jó7r( €Ì"GCÀé& _"•IòÂH~Ždë)3šç¨~eJÃB)àŒó]¢‘vc‘Š–Ä\%¨TÊ ‡9ö2Xl¼JPqÌôSŸhœÑáj×#h *ÝQö³ä‡³-ÀP—*år• ²›7u.à½R>T³ôkvg=L3>µÐíñúƒ¡X<ÁÝ®>îŒ'x$šÈ ø¦Œòlk‹{<¾Á²}Ëûyö¾¯^òí‰3æ ¦‘‚.Ìê7^xÿïüdãêëúHAñØ–} ÿ⺮΃)~ßè«oŒ0CÞ]_2e¾ø°§ ö@dêvïÅ+kö¥ pצú ø ÒN0¡(5Íç€íû‘èÐ’{%yþ±ù)ë ›f‚g§yª&d^÷å;¹w/9¾xNy†ßcø<Ƭ²À׿4Æeôa“G§]jiÅØ@šßŽ&R}®Ùã7œ^Z˜åƒ±«N3£4=Ýïêê6{â‰ü ï̱@Š}×CÞ¼;f¡¿äà÷È+ÛºâÝààgÀ²RÝé~´wÅÍ„ôëÍQQ{q®Oz ±î'Sàj?‡‚©ð35äÐG¾_ŸXWgrÆÜ.H˜ü@—•›†SJ¥ãÂ@¦¦r´á‡’ñ*¥Á”ëÖVmüÆ•|ÂE¡Êh:ʼnZ`Ä[-Hj9Š…Ê«— yAJTp\Ã8Ê00s$’ºç‘M#º£36òþ4Õ×BP“9¤@ºjM}¤&¥O žÀ<—4PêsàÜÂ(ˆezèâz S‰0Špι…­á©‚‹õ`˜ÒãRfÙCtˆ®Jj½ýDŸ?}A”«‚hLëoZ^•©M!3^mÉžâ`q´1$Ð[ªyP«ð;&–8Z¢Ð–\”“¦ÓwÍwÆz8J-ÄgŠU÷bÔZ2-Q‘fx\‘298u?h°Jqb)ÛeT“ÚŒ#c:Ú2OvÐÏ0”×Hû—ö¤RBX³¶Œ@wH‘,°œ+Ú†‡éNKàš‹@ŽD’J7 ”Ìpš˜9’~FJ&𬨍JP>o+ݻЈJpqŨ”zT‚J1*ŽK~\]ÇìK~BÛ’¹X.¿ØVž¢î¤¼ûàÀy<Ú]V”[qÓê<Ø´âÅ:4º]Lüt´î]ñâÁ¶Æ¸iEãÖØ¢Œx´;y†d/DÜØÞ²ûOËÿ'ÜÑüÒc·t´î*d¤`æ„Ó/,)-7xü÷÷Ý\ÿñúgî»Ù fiÙøN¿` ™TUn_–oB/™ãÉðG¸kGc$MÀ”Ñi>Úþ*Úq5x|¼·ëžWwþìùÚ;^n¸åÿjÿñQ;d§yf”’®lÇïx¹áWªßÕÒ Å9¾¤}]`0öµ£F¹ Œ˜w½ÒpûKõ÷ýugÜä>ñ•ª|ÈI÷d¦ºàÏë[nÿcý/^ªÿù uÏþ³©«÷K(¤‹`4ãÀ³ÓlG&¦&ÕU”ãËHucðµ…£®:yÌó „啦0ÆB=¦Øƒ¶xFÎ׎:nzöè\ßÖÝ]ÝfeŒ3°äP㙾kO)Ió¹ÌfUc{$!\7°Ä>aöéXL,ÛØW·¥Ë“CËâbÁŠsÎ,¼4nñºƒÖ˜,×ÄWA€™VmÏ.u-ç>f¼«;ÎrèˆBF*º*&C Nà|]F_‹Ñ{ Á°pHZèŸ-¾‚£×)C=ˆPÇ1¶Q(S§¤ ¥('Ã"³€Ó´Ÿƒ\š’¢jÜILâ¥þ…Ø€*y„Ë7 q¡)²+K©‚s ðê5š\ꆒdÏÔ4‘I³(ÖÈðg·méÈ9n!Áf R­Dú*S P6–3 =3`«EVK›Ð3îâ¥6Z2€E*’&§‘-ÎpC;àÌ¢Ϲ0]}šN= ª½*ƒÀ dQÄ¿8ÆrÛÅúéó®—(+50ñ@?d4š“adùBx¡yñFKÙ0iùœƒFž€Îs‘9Á¡açBׂ!Éâ…qª\·$³ÜL›¸3Ç$2eNvËÐðÈÞ¸j2?Ùÿ–hšà`’d8‘jÀ¹…\Šsà oT†$†¨”*NE Ò°•OîYäpû"ÇR®EJƒIפ¹’CêGw5¹:`qÀPÔ0ò,\Ê93 ¸¥qe™£ÅÞW:Ç|Åä#-ò^.¹E£Fäâ>½Ì˜ìJ¡¢dò0" ãÒC‹L—ÉÈÂ1jPgqÔ@©&õ?ø‹[¦ÇYÂân[°øÌÑcJÝÌ|í™_í«ßòÚ3¿òÖè1e Åíb ‹ÇãÜ4ã·(¤«}?ÝxÉ ?§»:š(d¤~’„q»XAј³.ûvvvVfF ;+ëìKoÊ/-N ,ŒÀöT…v­¯­^sá¶3á¥M5Á²`Éåð·Ç ¼´3ËŸ1ðíëƒ 'gÀìqѸå÷ðáî™àߘpù £§ŒNë}ÊÓkq·÷³q»Uª?ùM éx$â¤Øb0æ÷‘hâÕ -§Wæg¦ºçMȧBÝæò·÷5µGû•`\~êe'§x]=që©wö54w—s!J…ˆ\o8*Åó{ †ã\¨B= t§"!¬‚cjÊÅ€uDà“fsb¾;;ÅU”ilÙg¾³#îb`YL0ëŒT6:ËH$dâŸq ÝK åÅR<‰©>ì©zÊ[à~ 1-¹iÐrE@ó_à@yl†Çµ¼6Çæâ´Z*"îò‚c°4B“ãÅÉ·È¿a„=‚»kTm¨g¦'-ÔN30¡Ck*Ì& ê [«Ž ©ŽÉ¯rΊ{1€B†²-ŽÙŽ@ù9]¥õp\M@±æ¬0ù¼ÃK)ë%´$XÎÚeúBÍí¦û¤­hÖ¯©ºai ±ÍF Z™¤OE£(›H#†Ðÿ”§BîŒ:RÃUŽ5iÎLìþA•26ëÕ4 Ò`¥­XhròÁW4ɶãíˆå$ô2ñ¥¯èíEµdP÷3n3<êFÐ͘ãî9aÚCbÒðq°ˆá(Ç ¦xe 2“ÇäõD/h`‚ÊZéòsU&ЊVAH%snŒÚÍÐ?ªk4ÿ!¬™¥Uk¨ 4“#“K8ÉÑ××tóÃë[£P q¤.ÓªàJ6ñe¯pÔÒ©ÌKãÍ”á¤2ìQ™êÒvrÂjw·vRý^·‘›_tî5?[ñÜ=ÝáÎ5¯>æuñ¬1¥§\ðŒìè‰Y:¢€k7ýR¿e~c.¸së{U',í¯‘B’0Ðy°©fås™t1`7¼õlNþw2sF Ð"ƒ%U¡]ëÌ]vu*Ôì†ÊR¸åtøáŸöî‡Yg@Ù´y ¿6zàvè9Šç<ã Sݸ±‰öZ £sý‚Qí=ØóÌêÆŽ°9mLúe'÷yqVš»5‘‹€H¯ê]xdsCè¹w›ú,gݎΚº`Iž??Ã;&×?obf Å}âÌœ?ü£ïë`fiúǹ]¬3b>ñöÞý1møÌm–°,‹C{—ÙÕ“H÷»²Ó*Ã(É6NšìÞ%an !ÄÒÍÙ¨`ð´Ua§V¢ eÓ“^ D#eô¡xœc¾]úHŽîx;Ðetú¨,ï1S²™’-N…ºÍת[Ñ'àb½F­ÄÍ>DÅËSíÔÊL°–—/çÜíb£2ŒÒ\c뾄â@L1”‹á/Š;*³$®ÒÖ1¼’“#ÿ©»IÈÏzâ úHZ”ù";úeW‚ õ»’¾06hÞ^£+ b^ŠãmÄÝdE‚…{ZI8 TË“Àʳ@l,“͵q,“’8ê@i2‰féÙ¬~˜‰Á1N3Ê3`(V´H†rÎò®t©ýÏ”¾PT]fèeºù÷íåãzß§iPªGO‹s¤*FÙ,—–h´,X*ù”À‡é™JÅAM˜^ü ¤å ’BA‹âÊÂûcQ@¤GŒxƒ–Ê|Ðx!"ƒŒ›quzU"²ÚaM¢j©6 Òæ,Þêk|4:ûáRHbð7ÓZ‰î h4@_©)í,µha {\Ñ)•¾’çlܸ•“¼ åá§ŠØöž¾è”&Ñ)àè•I›Ì&cœ¹´G®i{è”TœáòøRëöíñ²½ž,‡‰§¬ Æ”Œ)§ËB=æÁ°Ù ÕîmÏ((Iúë.ý’„ )$ 3jb•ßï=avJz6dfç-:íŠÆºÍY%³)Ì¡I•{ëïZBЬT€_žgüiþÚcž ¤C¿ ¢~p0ßÕÚ3÷•cízâÖò·÷9¯`tޝ4ÏÿÉÞðší]ØçÅ¿_ݸhrÖÔ’tƒÁ'{ÃÞÐÒçäÙ¶/ü»×w/žž3nTJºß•H@sglWkwM],‹¯ÝÞQ’çÏ x2RÜñ„µ¿=º¡.¸åä€OžcõĬ‡Vì9iVîô’4ú35o~p #l¨—SQ¤ — ¢qÈN5|nhïæÑ¸d3¹ÇÅÒ<,'•åºÂQ^ÝÆÉXÂ…×rÈtAÞ£%®dMŠY c¥t%®0µ‚]ã8ò mYÙØ#j`cW"‚I÷ƒÎIm¿Ò`¡”9ÚQ!›Åè™eZ&Þû>(‚%}5Uµ•1Õ?Ç’³f=$q,Ðh–n=5€Ö-änET¥­é¸¦‰7ØrZ |4öl2ß-œ å(c˜ÅÉH”ypôڌ…*Uܯ“lû@o fÿïëFuÓkT4EµPKÚr’F˜¤Ù!gÞ›6%‰¯)s òŠIƒÆ§Þ ´oOR:m £ÂÀ©³(uTÒ/-UEä õ ¸ŠH­Ðsc´€¥D$"EZËÛ Õq%•Ú}.Ì”KºªmjDêÃȈqÄ•…3´éL×IË|²ºsÈÊ0¹ê¦¦‚ŠK‰²í©©$cC'¤ö€“3I§ð„š|&ïÂÒ²SÊK§8š´¢SŒt2"tŠàñ¥¸S2w5Ô›|J<á• 9énÚÐOð¶.³¹3ÚÜÖݸëSoZ¦Ç×Çë–F¤#Ò¢¬”QŽõ§»éý[)ìì Ç6wF›Ûƒ†ÝrË-…ó.ß׋š}¸¨Ð®õæ-Û°ž¯†_/…?5ÎË{C`ì²Sú× ]° @²…ìÓ5´¾qN± ð¹Yv øYªÒ¼ÌçÃ`. ƒhœ7´Úº,À™%曈_`-*°’ŸãX/©ä7ð¸_‰¨“ˆ ˜ G.BìCËüË LŠy$ÍÝ´ßÈÇ(ueÓ'òÔ².½$Í µ9E%©©´ÿXV¤Eí³)õƮ݊Uɺ¾”Sñ*R-UÖ¯Þµú*$ùˆN€‰ðiæÐ T•ö±:$øH¿4î‚V©16 Ú‹A$·¿k¤‰x•íQê@a ·ZS…ŒZ§^{oä¶"´#È;Å«Âé;P¯6"á—£"‰q´¢:”žÔòUL†dœ™2*ŠÆ –®d²ƒÓý~!ƨ\ ­²žÜV-&ªqK….EãR4o’YfÊPiŠ¢ŽS•é§÷)Xå'•áÐEÚúmœô Zg`eª$l—üïO*å”lqÅÖ#ŒhO$ØÖ”ºGìò¦ægxÒü†Û0LË ÷X­Á¸ ïnø4Êü9E>ê‘+äóÓ"ŸÛåÙ¿áÉ2UÍŸ®|Ô2˜WO¿×¶>“5qþP³Sú†r`t„<ŸZÜæêõ«Ï`äî‰Ãþ¸µ?¤®Ókãx¥-~¢{uHï¢{3 ¸8!wQ„Q‰+lz\°“-÷€þW:FLCË]ãÊgHç…a‘N)ÉËGåÄw #Š£-"èý¹Æ•¨=Hv„„Ú3M˜@#§ö‰qtqÒ‘kÒ2Ùƒj®nÆÕÔXNsd¦Ò’L¾‘Géζü@I+CËqpRm÷jÊViõbqT¶(j¼–)º¡ ©3euœþ×c&öŸ–ÅE´^è; eG?é,{ û•Aò‘´z‚SG ] 6Âà˜Ó¢n Š¸NqTmL WLÞRZE=ÉtÆ^Œè< k ${°u32*J#âž!\á%m©åC,NˆGà &&Hÿ» ª"µ‰ˆ¿-Íh_Lb9-žt`.à ˆu…;Ôïnògä² ¼>ÿ-äsÕ"H_¹Ì}p?ÙuCê•ß<²Dñ‹ ´éeo(I¬ü*‘íZ°ÅNá pf€|»³E3ôZÉ2V*2§äJbW4N \ë]QìÁeÀC±+Z£¦ B)ýŒðQ8‡§üC"ÂH‡Ï,Æi]³$" Õ–îÈÅÊ{ˆÚ"¥äº9&ÕèálÎ5z Èðd»‰ ‰[ñ1=¢Y”9@éé0mRϸ…iZ #¬?-Óëó°?"…|®Z$0©ŠŸøÌù/KÿÊ3yÓõ¾# Ía~¤M^íëè€OÖÚ–*0&0ËâÊQèñBÎh¾ŒsS¬XR,:I!öèSþÁ³+!òáÄMQ¦æe-øD`ºˆ¦}L±-Š4´¿I1óåD°0v0U•F°ΪITÕ8XZb‰ö‘Ø8îÑ9 ¤),§ôURŸôÊfáFl9tà`©©å´lY3y©Æ€’ùÈ}9šÅÉþ²5 Tu,³ß¨±1¯äcIèÏ?õZíóÞAªþ¾Ð¨±¹HÞ×/PJAÂDÈô¥:½‘´n æ¤7BÆU=ÿÄã`¡¨dšŽü(í“z›)÷ odÒ Ô< = `"ˆ$e€I†̲ٓ¬Hed¹HõÊHÙˆ9#9[¢Ù—LJá‹ Tg23ô¤¡¬B±©;Ë’i¢ªÒÇ¥/ôŠ”fÂ…H}ŒdG¦¦¨ŸqX‰Ú…KÒ2¿ F³,%Ùáë7r®=‰ƒjù,è”c^ŠÛëõ§gģфå–Å Ãåöy|>·Çg f_ùˆ2R!a$©"^¬cÌ”ù0e;ôuÊÁȃ!ëA7‰³š9ØÜ²äCô6vÌ<+Z`ñ$Pî_eCtv%é”4vÑ1¡·4vÅmÁ¤7»’ JZ¤wãÌ‹²YœèǦžÖ•¹ã }‘x­°ØQN{b”sg`Éé-£%YLÄ'nÉPRá*ˆ©ô”zƒ®p€c‘†´m HG 9i`À,N mò&ÿ`‹q‰Qv 6G6›x ¦%8Ö/­Åz’òwßBºLÝÁIPš£Æe€æÖB)±½ƒ)ØÏèÎ^}½rº7íuú»‚ÙOð¾h“NRé:¼h¬}Qq0”Ê£hv꥘ŒIbN€¹KÆ@'Y5“F,’Ø(âòdçB ª‰Û(ãš} æ¡–ÐD5sÅÒuv¥H¨Ê‡Q œfJ’,‰÷–XÄ%£z¥æÌ“lÆP'1ÚáΈ"¡”G PßÏŸæx@q)™ìÓ$.…‡\ªŸe>]­ÒàDjvenáÐSShD¶Mô\Ž8t:E ?ò¹œ>!óxSd:_hCß[ñY2R¶0tvoTD骤AÙ[ÄuOÂõzm V„!ýHn W®Fk¤MBÎÁHAÝÎmÕ(·S™!ÖzÁž¨´Íülƒ[‡¤4E®LÙ0{av»Ã¬ uÝiÈ+íÁŒ¾1ÿ´>KW(·ÉpNÃì¥$·E39×R³hÜÇ…•©ãÀTÏs ž•ß±+´ êZsæŒÚ$}æÖ’¢ê!-oÀmÚHŽ>jÈhƒJ»Åfð¤‚pLÿ+µÃAªßÓ?ÜBün7Ø{òPVæmŸök·HôùŠ¥!1P/F€Þ¤êGgæf8pàÀ_üâÕ µdR‰$ÿ8pàÀ‡Dò:8pàÀƒaÀ!U8pàÀ#‡T9pàÀŒRåÀ8p0 êz=—èÀ8pà pí_'SåÀ8p0èÏÔ8pàÀ8è¶<•ÊTñÁ«çž{¾¢bVÒÏšw×<úèc %®Ñ? DÕ‹ŸÐÝÝM÷5VTÌzáņWæÕW_óÍëo!“±~Æo^ÃqÇ_YYuÚi§ßqçmmmG¨®Ä믿^Q1«¦¦†Ž<Ø&4¯_öÜóÿWQ1ëÓOkáðzüˆvÁ0‰Dz[8ýœsÎ9Ã+vÄ… úÁæææŠŠYË—/ÿ—ˆäÀ_8ØéÓ0ÿöß¾ÿýü‚|ú:eêÔ¦ýMãÇ—¾x‡D[[ÛþðìUW}ý3¨ëpðôÓÏÜ}÷Ý3g~ó†ës²rv|ºãÙgŸ{sś˗?^VVö¯–n TVV@MÍFñjjªý~[[[CCøqãÄÁ55YYY&Œ€œœìϦ÷?ø|¾_ÿúnúzË-ÿ3vléW\!¾¦§þEr9pàÀƒÏ;†Iª.ZHÁU`éÒ¥K—. ‘Uõ£žzê©ó/8?žþT7<|°yó=÷ܳdÉ’;ï¼Ã0 8éä“Î8óŒ‹.¼ø»ßýÞ /ݱã†nWÌœùû?ü¾¿+ï¹ç7yyy>úˆ×ë€ùóç_tÑE>úȽ÷Þ;¼ªàšk¯yù•Wžxâ¿ýío%zàw¿++û›{ïq»ÝPQ1ó´ÓNÿß'þ÷Ç?ù1<üàÓ'O¹óŽ;Ä¢ÕØ±¥çž{þÔ‘µ+îê ]ÒûTaQ!c¬iÿþÁ”sHyâñØOò“ÙsæÐ-K—.}â‰'¾ûÝ›ÒÒÒà¥_JKK=õ´S‡$?TÉmU53gÌØ¸±ÆãñL›6-++«©©©q_cñèâšššôô´É“'õy{<ûŸ[n™8iœwîy/ÿéå7ß\)VÐÏ0ŒÊʹÖ¯¿îºk-˪®®9ÿüóž}ö¹H$’ššº~ý†iS§¦¥¥õôôÔÖÖÝzë­'œ°X”¹xññCm~غ>KY õ‘ØC2éx<þÃþ`Ò¤ÉpÚi§}ôÑÇ>úØ^èqÓK8pàÀÁ ÃÌTÝö‹Û–?±\ü$Å ñx|Ãú §œ²Døt`Œüâ›6¯^üüüóÏ?ÿ¹çž;xÐö0],ÛúÑG'Ÿ|²Ã@~~þ¼ùóªkª ~ôñÇ'Ÿ|’ç0yò”±cÇQQû„e%yÍ`äñûý³fÏÖïúÚ×¾ÅþöÚßD ¯üùÕ¯œþ•”””¡J8zÌèQ£FÕTo€šê3gz<ž±cÇæääTo¬€ê5³çÌq¹\}Þ£’¥ÓÜ҇ꂻÌ_°ùƒ-±Xlûöí¡PèŠ+®ôx<k6À†õæÍŸ/t2~|ùC?ôÜsÏú駜ثBïÂFVB}$.bùÝwßE§†jÒwîܹôuÑÂ…uµŸŽx8pð…Â0ç 3gÌHÚ¨Þ'‚ÁPÜ4Ÿ|ò©§žRëœ[‰Ä¡YÅÀ¸êª+_zé¥ÇìÒK–éÕY–•“«_™›³í“O ê²,+/×v6??ÄEMOKKKKÛÛ¸·÷©ýMû9ç…EE‡,d0ò¤¥¥;ÈÍÍ9ñ¤/½øÂÏ;÷¼·Þz«½½íÜóΪü•••ï¾û.缦¦fÑÑ‹ÄÁ¹sçÔÔÔ,˜?¿q_ã¹ýïL÷z=úWf0Ë4a]0@÷Àüùóâñئ͛·mÛ6iÒäÜÜœ9sçlذ¡°¨¨­­mþüùâ²xà¡zøáGÚÛÛòòr/¹ä’+®¸"IQÃÀÀâRx#(aÒHlnnÖ’I§¥§é[™ÐÙ†T8pðÅÄ‘Mìé.—ë’‹/>7:`r IDATûœ³G¶äì윋/¾è©§ž9åä%t0##`F{ÛAýʃím™™™BÃ0B]]úÙP¨+5%udEeŒÍŸ?ï½÷ÖvvvŠª ï¼óuÔñÕír%å)bÑ(ÒGžóÏ;ïÊ+¿¾åÃ_|ñ¥Y³fMœ8qx­¨ªª|íµ×¶lÙòɶmß¼A¾GjÏÿßÿUWW. wÁÀÝ&NÌÊÎ^ÿþûÛ>Ù¾`Á<X0þŠ+ =Ïœ92iW\\|ëm·@}}ÃËúÓ½÷Þ—Ÿ_pÆ_ª´IX¼C ¯ãI˜„¡šPW(‹Å(­ÕÚÒ ™™#+•üãÈþ™¯×[UUµ~Ãú’’’qv~á—_~¹ßç}äÑGôêfΘ±råJš‹·8°aý†yUóÀçóMŸ6mÝûëèúÖêêj„¨—_vy,»ýö_ê‹;ûöî{ä±Ç&NœxÌÑLj#ù£ šöï·,K| uuíøôÓÔ§ªªj„ñ¿¹ç7ÕÕÕÃNS¾­jùòåœóY³*ÄÁ9sçîÞ½{ÅŠ~¿ÚŒC-ó]0@÷cl^UÕºµë6nÚ8Á|˜?Á¶mÛß~ûí3gøýþ¤êÊËÇÝôÝ›|>_mmíPEíÅ;¤ð}bd%ì-ðLÈ4Í·ß~›¾¾ñÆ™™™ã' “”;pàÀÁ’T ú…êCÆÍ7ß´s箯_uÕk¯½V]]ýÎ;«îÿíoï»ïþÃ/9##cÙ¥—¾÷ÞZýà7®¿¾¡açßpã?ßýçÊ•+¯»öZ¯×{ù—‹³×}ãº5ï®yæ™gâñXssó~ø#G-T ¨sæÎùÖ·n|ýõ×/»üŠW^~eÕªÕ>úØ…]äq¹î¾û.ZdYròI=ôP(Ú¹sçÍ7ßìv«]JÖçÜóί©©ÉÈÈøò)§ CxqãÆåää¬^ý)S&§¦¦ŠƒâóêÕÿ˜UQ1¼ýËwÁÀÝóæÏûpëÖžžž¹s*…ôÐwÜ vfddΘ>ý ‡üæ¤>±lÙ%Ï>ûlGGY¸ð¨|ð¡‡úö·¾ãñxª*«î¼ó®ââbqöØc޽óÎ;xàÁ{ッpTáeW\zäD½âŠ+¦M›öÔÓOÿúž{ÄÛ&Ožòè£egçÐ5S¦L½õ¶[zè¡'–?QRZríµ×Æ¢±Ã—ç”%'ÿòöÛÏüê™´”3Îq ÇZ _@kù6Ùƒ/Mª ©q/8‰kƒƒc-/ µ|›ìÀÁ’TñÁ½Q]$®…SHBÓ˜a°¤ƒ±¸Å «3€ªŠÑ‡)î<˜÷ì¼…õ”›™•Uê_åÎÈj!œs˲\.×PoL$†a0–ÜÃÃÖò/G"aµ´FzzÌqeYÃ.¤­=ÚçñM›>üÒ‰UÃ.¶?ìÙ»·;ÜM_SÒRJÆŒ@Îãs2rÓZÞ[»véÒóú<Õ¸oÏaÈuñy 8$’Þ¨>¨L•ÀÀ‰kÃ`³gØf]ç=QÓ4-Ó´vïÝŸ•¨ÞRçðªC \—š½>%‹eϼªcgÓÁV»*Og^ÿP‹q¹\»7U×½üB°í Ûãå–F›9×3f<ž•?ªüìsKçTr>2¯ès¾Ì±kwÛ¢E³×¬ùð0Ë™>}¼ø ì<†0óí¿W,¯ª««‹Fc‹OGV­Z][[;a„Ã,ùó0r‡a-<ð`mm-}mim€»îº+/ONB"áÐ{ï­ýóϔœóh4šHXtÄå2|>ßð&!à {6oÞö׿eFÀQ]˜_V6ŒB8pp„€¤ªÿT•ž¦ê¤Êe0Ë`.ß×3oîÔ ?9BÞùλïÈÌÌK˜f² ÷¦êš‡~pxÅ®ZµêU«[[Û,‹[VÂ4ÍŒ@ÚâŸyÆW cÈ/¤hm ·¶E¦åvÁP$7fÂãv™¦‹%L31Õÿè‚E;`O- ­L=PQÛ6Å·üÞ!TÁ9gŒ­{èþ¼ÕqÂq)3ÇXñËâÀ8XÀ-žHpËâÜàœC¤ù@÷­ß^·äÜ£®»a¨Òq8ÖòYÂåò0áp7@6<Ønk‹Lœ?¬çŒL¢ªªâðy•eY-­­ÑîžÎ`(‘0O9eIOO­ªª\³æ½-[¶ff|)þÂQ£§®$|6#÷p¬åwVÕÖÕN/9eg0gžyfqQa,€®PG8úó#&m8©¯«cŒ…ÃÃ`>Ÿ˜1yÒD¿sža7ù@]݇¯þ¥ ›;nܼ‚Q¬»Ûr»÷¾öÆ»õõ¡¢Â…]˜UT4Ô9pà``OU *S5¼4µÁ˜Ëep¤¦úÓR=Ó¦Œ«ÞÒ0<ïüæ¦à]ní}üªEÆi óÇ›ôß7Ü`ÚÉ!p{ìüaT·k×Î{ï{ È>þø“gΜâñDkkÛ‡nYµêígŸ}îûßûneeåÊ<ÐÞ3qR©á2ÆfÿŽc1sT~Æ®;]œåñ®ØÖë ‹A@§Õ¶{ÿ;-U‘?¼{í5K_cl÷Æê¼ÕM=÷«áæ]¶lãÀ™Ûe¸ÝV"nÅM`ÜðxÜ)©.—3`†Á ÃSZ`dgd¿ñâîù‹Jç­QIÁE ‘Š8üdLoöÓÕ¥VÓö5¶†¿´´ >¼2c.—+‘HlÚ$`‡É«jkkóó òrs[6ož0~Â’.(++«­«=fJ0ܱcǤI“†]—޹àp¬å„Å‹ó›{Äg±ü l·ío’¥µìo|iáp¸agÃÄ£ñ”Ô”£Ž:jÏž='//oÍš5‰Db¨b©É¡ææÍ~5µ­­rÊÔ KJY$¡$`tw—Æb¥@Âpízá¥wöì6Ç_tÁùiÙÙ‡,–P<º¤¿åÑâÑËõ¯û¾>øb8øÂâˆ?ýG©q¿ÏåvÅEÙi©þáÍz/¿'_qZÒÁå+[¯ú隃mm©–L@g‡7eCXåX¿aÃã?yÉ%—-]zŽØ›‰Äzz¢™™YãÆ[²äÔ×_ý§ÿïÖË/»ø¼óÎ|±Ñh<Òs» XSóiIaNF€¦¹ÜlxÒ›×S!îµBO"ãcE3Ö½U=$Rõ¯¼8}ñ±áæ]í«Ýó¸¡'‹u§–fŒ+w§ÌH¨kÿ®žöVoz&¸ÝÜ0bí)ÙYñ™åõ¯¼t8¤jDVýzs©gW]]‘X, …Å×ú†fÓtüÄÍ›kG y±•`F"‘X²ä¸`°+ ôÑöÃÒ4EE…0pð`[ïkf"===55uÿþæÃ©«7Fpäö‡aXËc=þøòåÐÒÒúÁ–-ï­] ?ýéOrrràŒ3Ïa ˆètw÷ PTš[ZÞ|ó­éÓ§õwa9<çÀ%%%°ÿþh´ïMu`HM~oùÆ–Ï9õTwQ1Äb Òð¢c´]Á`9@y #ÔþÃYçL¸÷žésæ ^¤>y1ªÆ}_Ÿ‹G/wx•‡Äú1fìí¿¯yûï¶ã;v|zÑÒÅC* êƒ-íòk,—œ_~Ô™ß^÷ƒSXÝ~ˆwƒËœsÃ`œCÜ„+yIðعs×£þïw¿û½N8æƒ>)))òx¼¡P¤§'fšf{{çÞ½M dÝ}÷ÏÆŽ-]°`Á KŽÅÍX@mmmFFF^^Þˆl!èêêŽDº»º"ðñ'{óxâì`°»µ¥eTAé0 dŒ¹\ÂÚp¸»»;:ŒÕáÞ0MÊËÇmßÞ7?+/gš¦eY‰Äíd`ŒìÈíCõ-ÓgL?å”S|9D£Ñp8|Ów¾=®¬,##ó¦ï|û0…)**úê™gz½^Ã0Ün·eY¦iÆb1¡áƒÛjëjãq™ÈŒÇã–5œ ˆƒoòþ×߸ª¸xÓóÏGrrŠÊË'ååõfTÀX‚ó}mm=)Çttu U¤$^¥3*px•CÁÐR8CÍÕ㜌]yÙRH${÷µÄâ&çü¥—^²°Ý‘xÝÀ0€õ»`Ê„‚ާüüîGšÛ Ò70 Ø^0‡âú‰Äý¿ýÝ¥—^qüñG¯][ãñ¸7nÜZ^^æñxâq³½=ØØØ4qâèmÛjKKË/¾øšþèÇo¼þš×;¨}O±˜‹›Â~åÄ٥ʹέöU†¯ºrÀr%º2ÌÑsZ÷vv¶›æ× —ËŠG9pæq[ñžÜI3g|ó—¾¼b0c¡]ÛšÅãÒŠËÓŠËSæm{úÑ®NÃð0—8©GÿàPÖòÆ+fΜA_Ÿzú™ Î??##0a„ÚÚÚ)S¦”••¹Ýn¨¨¨ˆÅbÛ¶mÁLU(Ô÷ƒáM›ë|¾´ªª){÷¶®ZUsܱS§XƸeY‘Ha†aÌ™3SBp¨OÆã1˲ °°°°°P?¥ÿή®.˜8qbOOÏîÝ»GdpÄGî!1Hß²háÂqee+V¬hiiu»Ý99Ù7ÝôqŠ> Ù™YYY™k×®«¬œë÷û'6M3‰twwÇbñ`°3ž^TTÔÔÔär¹ŠŠŠêëë§ÆC69`¾1%ó:;¡³3ôɶ÷B¡D^^騱c}^`Ìâ¼¥³³±üO}öÙ®Œ ð¼ýÖø‰‡!Lï|•ΟˆW9pà``ŒØÓý1À].‹™]án‹s—˰†ûˆÙ0ÀÛߨY¬6RæŽ:mÍ.èî_Ù©öÁCáÝ5k22rÎ=÷«[·nOIñ——··?þ¸®°°(7çÌ™œ››‘‘xãu F~ýÉ'Ÿ¼æškSx,ÅÍ„eµ#ç|¹’sÎ9om A¢;3ôWov:´¹ ê …½ui3;‰îHþ´Ñ Á-nYœ¹]¼ÛÌ,™0ý¿ðå‡vlÜù·§»öÕ»RÒÀ23'Ì{Æ•iÅå“/ùþ'OÞ–àsR›«FväŒ!ù–¢¢¢÷×­]ºô¼’’1´§êð‘™•9göìM›6mÚ´iÞ¼y¦iºÝn–ÅÃáȺuëB]]3RRR8À0^\"pÈ&×ïÛ_‰Â”©ÐÕ`Ç¢ÎNèìlÝöÉ?Âa#˜pÒIy?üaa¾í¹ 3¡oó`•‰C“ªÃyB‡[œsnq‰D{¢1ÓLøý^nÁ°ŸÛß° ‚ïx¾…¿\œ|jŒ=ÝÄzx´;‘–é~ø¯»çL)QXµjuIÉ´xœWRS³5ŒdggVTL¨©Ù'æÍ›š•ˆÅâ›6mcÌsà@[YÙ¤¿þíoƒ&Uf<žvõlØÒ`š ÓLÄMËL$ò­ÇÍØÝÙâií‹¿5Û¡žht;a8Ëp¹x"^þµ«üc‚¯ÿðÑŸuw˜¼ô¿J¾råÇß²ïý•ÝmMÓ®º%}̤ыNݹúeWJªxpèÕ) ÉZÌ„i&LˆÇl{çL™ýgqn&¬„ÅMÓ²þ?{çÅÕð;3[ØewaéE”Ž‚‚ l±÷Þ[>“¨I즘آÆ]“h4Æ£Ækì%6boˆ(J¯ Û¦~ ®+eÙ]@ßïp8»¯Þ¹ûfæÎ}÷½a æ•0fÜ*ŠåN2h³.>’††Š…œL€Pk@N:“•¬÷ œj*$–u—™™ãã#;xðt»vMýü¼ïÝ{\¯^€B!oØ0„e9;;[ŠbΜù/55O(ݾý/†99æ1ðž*­Ž¤h†¢Šf(šá(u˜ÛeÂ2(`šlætª{Æ$©Õ¾6ªhš! væd(RéS[,·ƒ^“/V:I]@âàdãè’ŸúâÅÑ­¾ý&ÙÕn$¾qŠá8([¦ù£…cYŽåhš®W¯nÞ«@±Ø××7))I¥Rñ1Uñññ …Â××7>>Þ:U~üY:ë1ƒPánãß ^>ËÍuëСIBBÊéÓWÇŒî, ­h™‡a8ÞAÅâʵzU E‘&Ù)9J_pËç8ŽÈT/“Ù©¶Îž¶Nª´ÀA=Ušfx£!##Ó¨×éùP•Ü\ÿA&“©T*•JåáQØ=Y*úœ”ÄÃ_Õ zäTGv‚ü§|ü±¿ð:x@ƒ¦ýuF§Ó}þYW‹¶{Žã(ŠÂ^QìíÖºU4Í”ä*ɶ®[·.”œœlþ!pÿ*¨²A­Â÷®#Û|¨Ç…â«Çí7/‚•8á…݃ÿÔMZ[}æ‹™£¥GŸ½û p²³•ˆEb‘€À±œ"<ÂßÏÇ1 0Žã–ÍSëâãžÇÝ•ÐÙ:®O IDAT´óõõ‘ÉlËE*¹\.àü[ÆËÝ¢2qÈ#:·Nüeû±t„kc…w¡LAHÄB;™>'½o´7_F$åëØmç2·Ÿ¸zöÚŸ..ÿÕëb3çßµÃ: ìÖÕRa’^&zxzA‘€ÈA…@XJ9¯þÃ^mhÀ¾¼zƒ¯Õá®ùz`H€L ß0Ð,P4LÁg`@C‚ž5e…вU‹N8;{ØÚÊzöì`kkÃq°{÷áÓ§OQ) GŒè/àǼÿ^FFö£GÿuêhîŽç$Eëô$M’8ÎI¥Šâù*–õQC%?ýïŸq=ÀX¡ç8–")ФøàqÇÄb±Xl–ÌqÀ1Œ@*Ëyþ(çÉ=‡ºMü:»ûû}¾ŠÒ«€ÖkIµÊ®VP€.å Mꉼìû)XºV´Ô~4 Q( ¹Ü aÅú¬ w+x)ÞËS?†Õ ©pë¦gÍ'@Qb€LV ÃZëo™êÔm·¥]ðÆaVt°èª@Î’ÀX–ÕéôEš(Àÿ'I^½{ÇøƒùÎ\<á!–œÌ[T«oÛ¤mÛ‹“+ lgn±˜9Z”JeÓ¨(¯ì?``¿AÃ&LþJa+Æ ÇpŽƒg)Y§lMÚ²ôñõ ¬\Âùv0çíå²%“>·z‰°É±K{~KË –ºGËjF $6—ã²[…s‡oän?yëØ…¿”öj³Q½<ý=zöîtôà'³ð ˜ðN¡é?ÂR*jõ†Ç†Çñ€“üǬœDÐÐÛ£«HÀXƒQÅÍfƒ‚v=œåÖ;tرcGJÊs¥Òéðᓽ{wbèÀvíÆûúùå¡„BhµÚÒ Y‚™‡¬!´½;7Žj«ÎÓ«®Û{iï/jMýn+wUÚ]ô©«oÞ×ÑÕ¡]ϰöí¼šp… ~~ c°¨JrV!‹¨ðwÿ•/Ú|†S3 Kž À²ÀrÀ²@°lÁl .•šÐè‰ô`sÓ0G˺ â‹)S¦Mÿ®eË÷ï?YºôçÄÄx‚–e†‰ˆC‡öÞ¿755]§c.\8öý÷³ E ›’_«×¨µ¬P@Q4Ã0,Ëäç“—Î\—ÿÅ©ìµRO–¢ùÈw ã†fà ©Ô²·Ñap€a„X’ŸúüÉî•þý&È#‚k©“ô•TébëZ¡>;5ñä6†a>â¬_E`áh©Âo±hzã…\niˆ:Ï I‹ÜôÃ}ë?Épƽ”=åzùôž„‹e:5Ã$ryÿSxwxwzr°gí O“YdT•´-¿‹zÑUünRæ/k`hÆôËgø *0ÚªÊnþQ0¶¨ô“ÐÞAÒîí0 Iܾe×½cÖžCœÙãÜLÌ-† "((°Iz# Ö-YعGŸ—ÉiwýÙ£Kû&ùúøÊi³Bœ¿p¡[3ÿ1ûäœ$¹Tá&wkßÓ9º³:[“pûÆ‹G) qù¸Ûˆ®ÞÍ%‚7® $P/ÒÒ¼½½ËKZä B ,¥ÂßýWŽü8T1q÷mç¿N—üø(Ä!U /ôO/¼èj·ÇVj+Z¶QXXØðaƒ×oØÔ¤I…B)Ù°,CQzŽ£qœ°±''§åå©/\8>`@Ÿ®]º˜ß²V«×jI`Xš¦9ŽeY–%iÝËgénMI¹G3üíP D"ò͈‹o,Ër‡á8‡³±,ýá ݆™5šw—ûÔSø„p‡a@æd¨ŸÝIùï Mêq‘ .2$E«µge¼¶3GËÓ„„§ ¥¶Ö·Oo+BÔyš^ò÷vOù¿Å§¶so<ȧAg>KËh³h¨QðB•Gxbù LQtU †a8α¬v«isÓ:ã©$ˆÃ;x‹Šœ²êÔ—e9ÍŸG¥Úa¹¹‚Û·DÏé:t.Çîx,º¶ØÛÛÇDG;::öéÓ¯nýȤ—/ŽíþíÂù³ÎÎ.åGUˆV­Zk´š€rÝÍßÌCnZ't㦠:y»¸*m…2[‘­¿³Ÿ[´k^dqÞ¥Ÿÿ!‰ù);ïß›pÚ‘iݨ‘u‚!gQv*òÝX1+Éʲ#s§†NqO➨5€¿M}å—ÙÚúúùÊå?a÷éÓÛÓÓsÖwßÉå.>>µe2¹X,aYš¢èìì츸ش´ç³fÎèÙ³‡EÍj5ºü)ÀÕjEÑE“¶.zÿ¶a+¤h†eŽšf´Z½6O¥×¨%R‹·PR88kR2„µ\Èì\Àq­:3åáž52W/[woL ÄÓf¦èòs9!¶ÅBB"Óe$¨“ÒìƒÊá '<¦GËðaCŸ'>/µ©Tjõ.ê"©¬ÅÈ3ŠfåèÜ%Ìã„ˇBkFPÚ|AþM"mÉÎ¥cΪ@ÓhµZŠ¢¢Ä“ÔØ¨2¬´êëÂqýé‡Óû˦:pTѵ½úû…aQcæµÅÞÞÞ«†Ã/ôñ©ÀǼÐÐz׸éCîÞ¼¹ßc÷í›ìÕ\sª/­SÓÉÉÞV(#t6 j…ðeÒ´Y>:¹ýä‘„«ÏZºÕÝdPhp™^^‰@ ÊÈ«ëµéªÀ<Çõëm69`9®`8À»OøÍ%9~m¶ÅØÚJýýü]]\HÊÜ- DB¡\®0už1QQMöýµwË–-ÎÊÊeŽaXЦärIÇí?ùd“½½½¥m~Ðd âê‡EÕ ÷æ@ o lÖ¬Yn G<ÍÐëh¾êPŒˆwSWòÛ¤iBCÃÞ‹ª2P F â­ñŽ–÷ðˆjÌÂ#ù`#Ä}Å)W7U¥ÕeYToj0Zo÷p´¼‡‡Œ@¼?X¶W︮ QÕ 4ZæóŽ–÷ðˆjO9¿ûàA£a>ïáhyx0Uf쩀▀F Â|ÞÃÑò2QycO…òU@ Ä{Háé¿ë×o¼9@ ªÆ_ U11ÑoQ@ ˆªÊ‰=©Æ_ Uæ- ƒ@ QM@1U@ å@§Ê¬µ@ ˆW²§ @ ¢@F@ D9€Œ*@ ˆrÀhõ ªB 0Ÿ76TGž*@ ˆòU@ å@áÍ?MsñÒ¥ø'ñ$ ¢ºâëçÛ4*ªhº»G hÚ´é[—@ ï/»ÿüãúõ7bcK-Þ A„ù-[`TñUíÚAæWA àÁƒ‡P¬]Õ´iÓððpdW!â­Á[TÓ§M+µäœ¹sÀ|»Ê£Š·¨0 7ǸC x"ÂÃ;wîtèÐá¢FoQ¹¸¸ÄÅŽÙñ‚a`ŽECÞ²uk…U<7bc'Œa˜¥ï!Ç-[¾Ü××§¤þþþÏž={›"!â=ÇÁA Ô{ËÖ¼¦Ã0–eË]Dõ#11±Ô2È@G ˆjCVV–uÞZ;åhT½¹£‚垪‚V8´«@ ˆ×deeùùùYW÷É“'{¨âÛá ˆQU«V­²ÏœXiT!åòT!D5 33ÓjKüüüžût´D"))7##sÓo››ÇD7jÔÐÈqœV«MJJþ÷ß+É))eÀÕÕÕÏÏâããSRR é]»t €õ6æää˜hÁü’<¾>>ÁÁu\]]är9Žãjµ:!áÙ…‹—òóóKªRÍ4PFИ1gÌ ”×CrE·Ã›0¼QU»víB¹<°®Gë*FQâ"ÀÿZ×x¹°}ûöõëINN±µµ½zõj©å?üp„HdóÓOëJ-yùòå[·n}òÉ'fJ²pá¢ýû÷]¸pÁÌòï…ý a£paM-Eâ€ñ–;†œ€Žb•¶¸·«ÍÕGy_ô¨õÝËøã]51}ÊÉÉÙ¿ï¯Íc U‘J¥þþ~>>Þ¿mþ=;»LwqWW—¦QMàÑÇYYYŽŽŽ… ìݳ»a£Ær¹¼Ô¦Ì,Y?<Ì×çõâS¹\^¯^Ý5jlÜ´©¤ÃBZªê(#h̘3fªŠ§Š‡7ªîÞ½[R–XS%‹—/_nø:}úôZµj}ôÑGüW™¬bOuÓ¤¤¤Ì™3gР]»v‹…åÛøåË—·nÝj¾Q…0M¡AÿäEfRºà Mã8ŽaÃ2NpÀ±, @ØÛÏs‰Œ^› ªw-qÕáyb¢Hl#ÅbqAÇ^ºxéƒÚ€Tbëáîa(œ™‘Žã˜ggΞmÚ4 üýoÞºéáéfgg/‘Ø@~~~bâ ãù‘.;À»þ ¯_¿fÍšju¾N¯÷pwç L›þ-ÿaÏÞ¿ž5j9dð@ø~Þ|þBáääØ02ÒË«†D*eh:#3óüù /^¼,zPÎÎÎEÖð¬a#±Ñiu‰‰‰/]6Ìò9zìïÇ„BaÃF•Jå¿W®vïÖ<ÜÝOŸ>njC¿ê¡¥Rݬ©§§‡ ˲yyyé§OÉW«ÀÕÕeð pçÎÝcÇOm‹Æ ¢â¦íŽžKM*&„ÜÕ÷}s‹Ú€ÖQ5¡²UÅ$¥/^´xÿý‹/^öã>ìÓ§÷´iÓàÑÇ˗¯¸výš^¯ ™ßÿ½““Sffæ¥K—H’”J¥¦uUR-ë~늦ÐÐâÀ åŒN£×€žÊxɉkPš4úe<+tÂX>=…uõä´yœ½ˆÀ+|ú£ZR4l¨hG'çZµjÕ¨áÙ¨aA òãGH’Ä0,>>~ÇöíG …©ôã?>l¨³³³N§!B x}U¡(òÓ1ŸÞ½wßÃÃíÅ‹—=zô˜°}ÑÄß¶5ñtT4«C‹€#g >W¤QeÑ–êæAQäÌ3Ã# )K–,qvv^¿a=ÿhÒ¸qãýûÿ´níŠ+KÍ-ÄÊ•+½½½W¬XΟ¡¡õ:tè°aÆéÓ§Û+•NNÎàâìRÒ\þšÕkj×®ýÃâżÆkÕªÕ»wo¨SSðÄ^§NîÝ»?~ô( 0ÐÎÎÎV.ÃqܸeåùD­V;yòä¶mÛð2³2Öý´nà B¡ÐD]N÷ý÷ß·nÝš/ЪU«R5i¢V•@ PªôÇ·BáƒÓ/–‘Êór2#™#›}þÎÉ+Žá™—õyTDW\€£ \E1p@㯠Ã,]¶Léà@DRRrÝzõzöêåèà€¸­´À êîæ~ëömÿ׋“—/_ûßÍ[´T*íýüÃê×7d5mÖ¬ví:P77WÞ¸yëÖÚµëÂÃ#‚CB‚HINÆ_ù! •Äp|îœÙNNÎAà8 |7{NÛv¯/ÁŽŽ=ºw“H$$INš8);;»e«ÖÕ^nnn Ë8>ò£š·hÉq\zFF\Ü“ºuëÙØH --}ÖwsØÏ²¬ovª6+ã˜AT誽’7ÿa€§cË@½^÷ôéS«Å{ƒ7·TJ7·‘â=UÜëtlllÂê‡R(ŠºråêˆÃ…B¡!±EË–[·mã8Îtn¡nH’¼sçΨQ£‚às6jtõêUþkÁcyŒÐëõwïÞýüóÏ %ƒ‚‚¼kÕ2L_Rµ~ýúcÇŽ%%%‘$Å×zúô©?¿•E‘è«RË …Â-šª´mÛnß¾ýãש]ÇD]±Xìçç·z͵ZÓ0²@?>L몤Z•–ÂOÀÀˆÎýú$ÝM­ÑÜKrÿr®oG',!+6þÑGÉÇ·s5 …G*›ÎÚæÊLx e±,«Êͽyëæú oݼٮ]{Ç»víìãí]´Y±X¥R;<î߿װQ#~þH")ì+53îTf[0³“àââñjRÛ«fÍ’J:;9æhxA~¾šýOO®]:‹Åâ|µzÜØq±±±íÚwào®¥*§Jk@§×ÿsòT³fQ......|–*/oß¾†ww(•ÊAƒ‡@ÿüó°°ú¶¶R•J5tèP^g°ÕàMs­Ôò¶2™@ 0T±·W@NN.Çq¦ë®Y³fÍšÕk×®—åää4dèÿ}ø¿RuUl­Jk|žþ#pŽaÔ·Žæ¿iãÈû‰@ÕοyœŠ}ÀQŽÚ‡'ò¯Ç“našŒ›÷ö4ýgæLå¬ZµêçŸásEb±«‹k×®Ý\\]]]]ø»ã;w'OžœœœÜ²e‹+Vðµš1n¢h''ç’.yŦk´þƒ½½½‰ß×PòðáÃ_]LÜ$EQ666þþ~íÛµ%"--m̘1Ïž=ïб£»QxuIT ÜðàÀÁ:­¦F ¯à^½z)ärww—'Oâ1 Ãqœ¿:™~£+3æŒDU1ª’’“Í))9¹Äàî"Œ*ËæÿJ T/êΑÉdA <¤g¯žE[3[(…ß“-++Ó8+3+ËÎÎÎÈîámªbä”Éd8ŽçååçæååI%>åàÁƒ#FŒ>~ïž½Ë~\æìäÒ¾};Óº*¶V×®]Šª¢B8­×¥åúaR]ÒCGšt⨔¤{Á4-Á¸'IkR¸L¨ädûùymš™ÂÌ™;×dþfÆÛ{÷ŸH$ŠŒŒ¼rõʸñãŒÃÍÉ-Z¸^ݺÇçg =#ãê•«={˜:Q ˆÅâààËÿ^6lŸ’ž‘ñäIœ«‹ °,KÓ´­ìõZ¹'ÞXi,‰¨Wç¹9倢éÓ§O·mÛ–ÿzôè1;;;?ÿsêòøúúNž2yÇÎqqq]»v1SWƵL{·ô„TæÕLöò&ãÝΞ~ÿèš‹Oo'öñƒûçm}{(5·hØ÷—\' §ÊÌñ:””•š’ªÕj%IÏž=}}}=kÔp0,t-Η`œ’–šÆq†a 8h ¬û銢ŒËswêô™N;…Â!CÒ÷íÛŸ””T¸ä©3:w‰DßÍš¡V«qç·ºq#öòåKQQMüùÞå ÅÒ%K uŸ>Mø}ËV¥‘äÕRÑÑ1Íc¢›ÇDëõ$MS6 ãpöÜ9R¯7–“_ý×¼y 3õ€Æ ¢6¢Ï IDATXŠNÜ+ºvÏÕ÷k›bV¡U´§ê•05Á¼­ŽJ-cL9Nÿ½1)VtŽ ¦L™<|øˆ‘ìׯŸ‹‹Kž*ÿÎÛË?®ÔÜBŒùô³O?3vì¸ûë´ºuëÖ‰D¢á#†¿r™òTÀè1£?ÿ|ìï›7÷Ð?++{úôéB¡—ðÆïþsw«–­<<<Ž?¾ë?À(BË×ÏO§Óíܹ38$D,š.Ï'‘H~\úc®*·V­Z§N>xðàS¦LÔMJJš9sf»vm}||iš>~ü„^¯oܸ1Çq&te¢–u¿uESبdnrεóš$ȉãµTzŽêúsîq6™ ¹×Oèïå©¥—ؤl¹›G1UVaf|L±³-$EíÝ»¯^hHí Ú~~~×®]»zõÚ×_UR-㯪¼¼#GŽ……Össwã½›6þÚÀÀBå_¬^½& 2²££#EQOžÄïÛ·O$¶)TòybâªUkêF4ˆP*•4M?û×þýjµÚÄØÈÌ̸pþ|×nÝL+§ªknÞº- /¯2™L¯×?}š°ï¯½Ûw쌊jZ¬5S’ÆÐ˜11fåHI?«‡ïúÍż¦ÅÕÃ×¢vLT)ÊϨ*4)VdŽ ·nݶnÝÚE «T¹ …]HHÈÀýùb¦s ѤIãU«Wÿ´nÝĉ“„Baƒ ,ZäîîþF zÉFU³fÑ ,\³vͲåË]]݆flΞ={öìÙ½{÷ …aáá?ü°dć# Z-[´èÝ»÷êÕ«sss;fº

    ȨB¼cP¤9@T*‡ yªï)ȨB ˆj€··÷õë×###­«~íÚ5ooïÊÓŽuUðê?@ ïÞÞÞ×®]³ºî[kÇϯÜvU¨È×Ô –ƒHWæƒte>]݈>mÚœ¹s+¨£wæ©ÒéuqÏãi†©å^S"ïü{ïõ§ ºkh~æ8Ÿ;wîÆëãÇO0S6KËËÒ%K–/_öìy¢E%Ïœ9³}Û¶Ë—/¥¦¦*•ÊÚ´ùöÛinnnÅVœ9cÆ®]Ü»ÿ ØÜ3gÎôéÝË8ÅÁÁáá£Ç–J%Ã0–eßU‰•AŒ*Ò•ù ]™Ò•ù ]™¯+Þ¢ªÐŽÞ™§ê¿·Çmž•¥Ê;hRJRJf~Þg`¶kÿ‰%ÖN9¦T»êܹ³Ö¯·Ä¨²¬|±89;Y÷³tÉ•J5tè0¯š5Ÿ<‰ûiݺ‹/ž9sV&“Y'Æœ9s===ùÏ"±ÈºF*-•dÃŽJ"F•éÊ|®ÌéÊ|®Ì‡a˜Šk¼œwT7ßS¥Öª³^¦g¤­ÜóÓÀV}uk¿;gU¾VÓ¯Ïèk§ãg,[4ñÃ=Ý=ËC¨ò$õ"‘xذáÆ ·´î’¥Kýýý _ëÕ­÷á‡#öïÛ7hð`ë„iѲe:u¬«‹@ Ä{KùUå³£ºƒöŠùâ¿F„‡›¶®pÇ€ò°³ÑqP“˜:Û²V>Ñ<ÐèÉUê¹Ã£&؈$s7-›1rŠ»‹k±Õ¿ýö›Ÿú œÀÝÝýÖí;cF~üøÑ‰“ÿŠuïÖÍÎN±ù÷-Å–€S§N-^´ðÖ­["‘(*ªéŒ™3 Ž(~nÝO??wÎÝ»w‡ ²`á"ãI½û÷î-\¸àÚµk™™™ööömÚ¶ýnÖwŽŽE¥5¶¨ q“&œœlB?÷ïÝûꫯnܸngg÷ñ'£&L(ì`Óh4‰¤ZΣW’G®J"F•éÊ|®ÌéÊ|®Ìçmxª¬æFlì´iÓ‹¦Ï;Ç„QÅ0Lv~–¯“ËWý§¸J6d,HÕ¦RCQÌÍ“ïs¿ú$xrçÎß®úá³¾CÂê„…åœ2å –avìØqöì9 Š0§üéÓ§èÿÁm~ß²U£Ñ,\0¿s§Ž§NŸñòòâkåæª¦}ûÍ÷óæê‹lk‘””2xÈ;…ݳçÏ–/[6tØÐC‡—¢2€K/@àà’ h4š?þèÃÿœ8iÒÁ¾Ÿ;ÇÏ×·k·×úìÔ±C~~¾D"iݺõ¬ï¾óö~«ÛðW4•äêPIĨ ]™Ò•ù ]™Ò•ùTj£ ¬ˆ;þïñcÍ1˦¹%myš.¢X=Eé(Š¢Ø2}þÃYcý¾Ù{À/íìð2ºG‡N…ZP*•r…Çq¯š5Í鱨ò æÏó÷÷ÿmófÞhkРAÃÈ+W¬X´x1_€$õ?ü°¤QãÆÅ¶ùA›6´iÃnÔ¸q½z¡1ÑÍîß»gÂZ€ìììY³fÖ«Ú¾}û’ʨÕêyó4oÞZµjuñâ…}û÷ñF•B¡øôÓÏ5nlkk{ëÖÍ•+VtìÐáô™³®®Å»ôª"•äê`cíÚµ?ÿüs¬«p/_¾|ëÖ­O>ùÄb~Ý*º* UZ«•dxW ®ÌÇ «¢W¡*MEN…UxÙ›`‹Ãt•_¯lú¨ÝȨ (’!sI•†Ôæéóóôù*}~®>/ŸÔäèrt:]˜oíO ßqöDÙ…,Š^¯íÚ­›Á æææÖ,:úâÅ †2‰¤a£F%µ@’ä‹7‰öñ®åéáñAëÖg¢SN7bøpµF³á×_ ‚(©˜­­mLLŒákí:u s…áááß͞ݹsç–-[Ž7~ç»233ù™MÄ;çòåË¿üò‹qŠ££C¡É_DÙAZE J¢èU¨JS‡c0ª83°´ñòñTÍ›;Ç8å›W‚|ܺñ>]i§\qh¥D(ýdàÌm¤ç×À4Ã2,ñÉ’?>\ânë5ãçÅ©™iR™¢ìB%77—eY''gãDg'§Û·n¾Êd2AK_}õå_{÷NúMdÆ2™,7'§S§Ž:}‰›ß“¤~ذ¡wîÜÞû×>ÓïÍ–J¥Æý †¦‹-áëë{ÃDkUŽJòxúZ îͯ&ªW¨dß¾ýúöíWIލâ°BWe¡JkµŠŠýN(‹®ôz½X,.GaÊ…Š“Ê «¢W¡J E‘Ba)«×Ëx8ÅvQ|0xðà:ÁóæÍ“J¥»víúßÿþ·uÛ¶“ ï#]0vìX©T*‹³²²>ô$þÉŽ;„B¡¡Àĉ333ÀÙɉã¸sçÎ;–¢(‚ 333:xúÌé-¿ÿHÓôÈÿ}˜’š ®®. Ã>}šðôiÂ'âääd:÷ ± ¢r¯¿ßÉ-’ª,º*ö*dú×·t,qÀ©Tª%K—.\0ßÇÇ÷øñcß~; lØðaPÚHãÇüüùó§NêëëKêõÇYzQå€îÍÓßè‚Pl…tU *\€ëu¤ˆ“"‘—·€ˆÅ"ŽåD  ãhÆDàQPíÚZ­vãÆ_ë׋DÁ!!ݺu[¾lÙ¢E ÇŒù4==ý›o¦ÛEËõõÔþýú¾>uëÖ€S§N Ðõš5½{÷)µßaC‡º»»‡Õ³±‘ܾ}kÓÆžžž£F2GfDÙ‰‰‰1„»…G„שS§{÷î=  ´³³³•Ëp÷ð¬Dû«½CV®\éíí½bÅrþ4 ­×¡C‡ 6LŸ^@QäœÙ³ƒ‚ ÿþ{vï>zô(oR¬]³644tþü|É€€€.]:¿£ã¨@LhÀÄHã5Í´iÓø‡´Ð°°èèfÇŸ8|è ?Ù¡QkfÌœ™œœìîîK–,qvv^¿a½H$€Æèßÿ§ukW¬Xù.ŽÛzZ´h±xñbøòË/9òàÁƒC‡õèÑÃP@(nÚ´ÉßßïeR2MÓ3gΠ(ÊÕÕeËï[<<=ãââú÷Ÿ¿pÑ¢õë׿|ù’·™¾6mÐÀ››{éâE0k ÿo‹¤*‹~н •úë[4–€¢¨o¿ý&(¨6tîÜåÎÝ»ë~Z7pÐ@¡PXj_EΜ13<"Ü ^¹_T‹vQˆJmTE„‡/[öc±é¥ÖµKUÚ|` Ž}eHb8p‹¦Ñë‚-*èØ±ã°aÃÌŸŸíæævëözõBW®Z½hÑÂË—ûøøLž2E¯Ó›(ß²eË;ÿX¼háˆáÃ…BaÓ¦Í~Y¿Á°ŸB©¬\¹jÒĉÍc¢E"Q£Æ7nÜÔ¹sáUŠ<ׯ]€Í›Û¼ù7CâÈ‘#,\Ë2 c¦mÕ4jÏî=»wÿ©×ë]]]ûõëÿÕ×_;::™)s• ’x ¸âb(ŠZ¿~ý±cÇ’’’H²ÀñùôéSÿ€¾(¼)Šo( EuE’ä;wFE“òÎÎÎ 5ºzõªá™^!—êzÔ¨‘ššÊqœ^¯¿{÷îøñã YµjÕôóóãŒF¡ÊjÕØ«W’ÀŒ‘&•J###ùÂr¹ÌÉÉ)*ª‰@ äS||} 55ÕÍÍ¢¨+W®Ž1\(újѲåÖmÛ*¹Ä+ø0dÈÞ‚0 ÿ‘#Gàò¥ËÝ»w7˜0qBdd$ØÛ+ïܹ“šš,ËMŸQ`ÊóÁW¯^¥(J©T …BŠ¢Öÿüó½{w}}|ƒj·þà^Q¦s_ËôÊ/U\Š5RÝBÈ2]½y*ý×·d,ñå…BQDx„¡µ¦QM7ÿ¶ùqÜc?ÿRû²±± «f<ð,½¨òçÿ›—Ù•—ÔE!*µQÕ¥k7ã`)ówTg{g^×Ä)F"x„a ÛºµcµœÌÔ›j‚X²té’¥Kû÷ïß¿Ã×^½z›.ߪU«V­ZÛþw³g7{v¡ÄI“'Oš<™ÿìêêºuÛ6ãÜôŒÌbKž;J¦õW,Úï/¿¼~v;vÜØ±ãL´V ¨$úb/RsçÎ=räÈçŸVßÖVªR©†ªÓëŒnö\¡ó*ÍUEuÅ/Q*ŒÝÑAùàþ}ƒ®„"‘q.Ža Cs§R©X–µ··7ε·W_7¡ÊjÕØ-I`ÆH“H$Æu ‚ؼNÁ0šæ Š¢6nÜ´iÓf#1X†a*¹ +0Œ ;;{>%+'Û0Ïuj×1ÔâgÜ ====ýðYš¦óòòìíí¿þúë%K–¤¦¥íݳ—ÏrvvZ»n]P`L&3‘[HN¾SóqAеRY¦¦W=|xó*Tê¯oÑXâËÛÊl1 3Ë““kN_¶¶¶ðæÉkéE•{óx ©&º0æéÓ§f)ÔZ^½¦æù_ë1ߢ€ ¯ ó.4ÕGÊ;Cr$`€1{öþùvžžýëWÓ¯ì"!ª•äB_¬§êàÁƒ#FŒüêýB?Î0ÁϽ>·‹Ö­ÆÕ•\.Çqœ#6ËÌʲ³³+É0*¸"rœL&Ãq<;;Û8777[úêÒ_¥µZ’GŒ4挴"àŒî=Æ+d2ACéÙ«g‰ÂTJŒ§€Ä牵jÕ€/_ð)J{{ã»—€eä³”ót:uZ°`A±í÷íÛ·[·n·oßNHH¸{÷îž={ÒÓ3~ùù~’Ñt.8ôz½D"€””dCËe‘Ê%©õæU¨ô_ß’±ÄÏÏËÓëõü¤¥¥€Bn]_–^T^Èb!õ:N.3ç‰+"<|ËÖ­ÅfYÍ«~ z,‡ÕÆ<°ßüÂÂ:tóêêÄ8Õ•†|Øp„ÄF" ‡7îê"tjïߢËj¨ê°,KÓ´­ÌÖrâÄ›¨‰D"ª„Í/Þ7D"Q½ºu?nð´§gd\½rµadé 5ÄbqHpð©^¿oêÙ³gqqO*JÖÊG©#Í"D"Qddä•«W¼¼¼|Þ¤<„}«¬Y»:111ñEâOëÖñ)ü[¿Š¥ví ggg8qâÄåË—ùĬ¬¬íÛ¶/Z¼(šÞòû–¬¬¬ÈÈÈ>}úŒ7–ßï 77·Ô\pq-XuñâEøkïÞ¤¤¤R¡T©ÊH¡«PEüú4MŸ<ùz@9ò·Ÿ€}YqQuvuINI18óòó=~lõ±”;å¨nŒEž*;…]‹¨…}}ÊU(D£’<=õ(`Ö¸qãÝînÕ²•‡‡ÇñãÇwýñ=Õùúùétº;w‡„ˆ…€ÀÀ*=Qe>Åz_Æ|úÙ§ŸŽ;vÜÀýuZݺuëD"ÑðÃKœ*Ž{UwÔèQcÇŽûöÛo{öꙓ“»fÕ*G Ç«Ùô_I(u¤W÷Íz¾Ø«”)S&>bäG#ûõëçââ’§Ê¿sç6ÇrãÆWêX£,øžžÑ¹óë% AAµ;vèÀE/û0‚˜9sÆ„ I’üä“OxQvv´iÓ†ã8–a-^´hñ"™ÌV*µÍÊÊ¢ibbbJÍ€:8p¦Nº`Á‚œœ±X¬×ëİFª2êªèUÈô¯oéXâ§ W¬\™ŸŸïíãsòÄÉ£G~1eŠ€ 8޳´/+.ªmÛ´Ù°~Úµk† –™™¹páB€Ó§Æ+nÄÆNŸ6 æÌkžK¥œ*‹bªˆb©$7Ëb§ÿfÏž={öìÞ½{‹„°ðð~X2âÃðêŠÙ²E‹Þ½{¯^½:77×ÙÙùرcUz¢Ê|ŠÕU“&W­^ýÓºu'N … 4X°h‘»»{I^}cS)::fÁ‚k×®=räˆWŸ~úÙ–m[d2Yu›þ+Y¦GZ1u îzœq/†”€€À­[·­[·vÑÂÅ*U®Ba2p@ÿJ®ÃBÆ:üðÃ;vìÑëI(yº°(¨ŽÍš5Ë­áˆG©-ÅÀô.v%ݲekíÚA]»õ(©Q,÷îÞ>tèð!…7óðzúÔôÀÀ@x5ºbÿ»9qƒwýí“““³gï^x·bT Þ¦®rss;vìøé§Ÿ2¤B;ª и2ŸbuõÕ×_;zöïßoþíjÏ;WK—.=tèÐÉ“'ßNwå…AWÓ§M£izþ‚Ó§M3Ǻzþüù–­[?þhdIæÌ©ˆp‘¤\Ýô.cªÊ±.¨Nh4š%K–œ;îöíÛÇŽ3z´ÄƦK—.ïZ.Qå)´HÓ4–6þ.cªÊ±.¢:QI¦$*‰U‚r×A Ï:”››+“É"4ø~þü×+«2ÕàÞFº*&8 aÌÛSKUŽhä©Pá+QLŠÇBðT’Óµ’ˆQ%(w] …ÂåË—Ut/ï„êqoƒ®æÏ›?ÞüB‰cÞšZ&Lœ0aâ„*ý+T%£êzªhš¾s÷éã¸=»Ç”º#íúõë§~ýU¡D©TúìybYd0͹sçnܸ>~ü„Šë¢zPIN×J"F•éÊ|®ÌéÊ|®ÌÇêwë™CuðTq—ŸŸáòKÀž^õNžºÝöƒ°R_ÆóæÍ/Øw«^`>çÎݰ~=2ªxW OÕì:pF,Â98hÚÆ„>zœšœnS;ÀËÙÑF§cÎ%)>zY§vé+DZ¶j€öĪtT’G®J"F•éÊ|®ÌéÊ|®Ì§*Uy›Ø#6¶hzDx¸‰Fv<ðëÛÉYGƒ–äžÆk^ì¼ÕÐ^,P €ú)¯ÜÌT:亹”¸=„ öïÛ7räÿΜ9bH0 rrò™3gàÞÝ»óæÍ»té¢^¯ ›1cF“&Q|±™3fìÚõǯ7Κ9óöí;ööv2j„ ðí·ßüüÓOàìäîîî·nßIHx:cúô‹/æçç+•ʰúõ×¯ß “ɬ»:QI®•DŒ*Ò•ù ]™Ò•ù ]™OUšþ³ÈSu#6vÚ´éEÓçÎc¢{9áSSœ§as^ªÝõM?hî`/äXL§ã€f8èüÒô¦¸º8•e–ri IDATÔT~~¾ñÞB¡P*•¶ïÐA¡PìÚµkæ+£*==ýô©S3f΀»wïtêØñÿìw\×Àg÷íŽÎÑ{U(X± ˆMT0Q‰[bvÅ{‰Æ®£±F1Ñü¢FÔØ@TŠˆ ¤ƒp”ë»ûûãô<Žã8Ž£ˆûýð¹Ïíì{³³sËÞܼ·ó¼¼¼wíÞ£««{ôè‘Ñ_|qñÒßÞÞÞ’ÆNUôòåëÖoprr:þÜü¨('GÇ¡¡QQóq ;uêTbb¼jœ8a•JÛ¹k·™™Yiié×…B¡êì”––r¹Ü¶:ºŽŽN{0㣀ô•ê¾RÒWªCúJu¤¾‚œ©µF' *¯Æ‚|Ö—Í £8b@ŒܪxZŒiѵ“ž×$gó ò«Onl0¨<(HvsРÁ'Nžd0¡#GÆÇÇ-Ž–LÌ:£G€U+W²Ùì¸øxÉ"P}úô<(hÛÖ-GŽþ.Q" ¶oß!ÉrMš4ùø±cçÎ 544d²X(ŠÚØÚJZòx¼¬¬¬;w…„„H$C† iª7:~¾~_íR os3> |}|€ô•*¾RÒWªCúJu|}|$ƒcSPÕÔ9UjUÀEh—qrªjpÇĸX "!þ<Ÿ÷VDÕ³4}S‹âf†·ÓË©•Ê?»vï±¶¶–nJÞ„‡‡ûý÷¤¤¤ÀÀ@8sætß¾}Ùl¶P(LNNž9s¦$¢A‚ƒ‡Œ9(U¢¯¯/;nhkgWXX¨ðèÚÚÚnnn›6oª©­íÕ³§»‡‚ MõFƒ Z0+«º ÃG„’Õ=T!)ñFŸ¾¤¯Tô•ê¾RÒWª“”xCòæcþS#Sµî§5²’%Ë–KõȾJˆ1þ¹WÝÇ‘é`ÉÀ” .ñ¶ *ªp–­>ˆ)_ã¼AåKNÞüÓL•Ý××WáDuÿ[ÛØ3§³³³=z´wß>àp8"‘h×®]{öì•9L¶Ô=N—U…¢(Vw…mYNž:½iã†-›7/./333›>cÆ?üø)‡V)©©³gÍjC±}ÇòEBBBÒQéà™ªEK–ÊI¤zd_¥`8”Wb¼jáÐÞ¦Å"B—R,@Škðœ7_Œ•sËÞTUæUØBéæÎWã,;vìþ}û6oÙ{æ´®®î°aÀÅbQ(”éÓgŒ×Ðd666;wí€ìììãÇŽ­Y½ÚÜÜ",,L#Ê?RiÑŸÊÉËkÁBe$$$$$mN‹Um¼ö®å]0ŒH˪ ð`V#ÈK>õvþôñú ž›'zõ’ÿ<“S˜WS[KP°·¡:Û©ù$]XXxmmíŸ^ˆ‹‹>|¸¶¶60Œ^½z%''988¸ÔE :C$)Üåêêºjõj--­¬ÌLõ îH¨²S ¡Ðž²²²ï¾ýÖÅÙÉÎÖ&,llvvv+;äc¡¨¨hñâE!C‚­­,MMŒórsåž”²{÷._Ÿ®’÷111¦&Æ’?+K‹ž=vîüE•¥^’””$WŒ~ÛÖ­v¶—˜Q±YëóÃ÷3%Ή˜8¡!I}4x±}D×-é+UèÈkÿ©‘“¨©Å^¾æMhð†|1ZËãŒ8ÏÒEôTL%ëì$¶¶2S¢êúµkrALð`:NNN~~~kV¯.,, 'm°zÍšaC‡Ž5ròäÉNUJÊ'–-Wð£nîî<ïðá_»võaÐéLkÖ?„Žéââ*‹/$$ðùü¾MuI‹"‰ÆŒ]VVºjÕj]]Ým?o:"1)ÙÔTÙÈò§ÉëW¯Îýñ‡o·nÝ’““åö’žTÎæ-[,--ù<þÙ³ñ«W­ª¬¨\­†žúE†MLMÜÜÜ4giàêêzêÔim™¸êKdÑàÅöÑ]·¤¯EPIuÚrN•¯Ïöí?+”+éUP"ìb¯câêé_ªtÚZqm­¸ÜF\]ðyÀ¯¥ðyôÿž»8‹µ´JT-Y²XN’ž‘ifö.¾há ‹Þ½{KtîÜåò•«›6m\ºdiee…¡Ï”)ST9߈ˆ¯7¬__QQannž|ó–¥¥åžÝ»ß¼yƒ¢¨‡‡GLÌ¡~ýú©¢ªcÓ¢W|S‰OOòǹó’Ë [÷îÝ>óÛµsçªÕ«ÛÚ´vG·îÝ3³žÀþ}ûêU¤'•Ó«WoIÎ{Dhè  ‡Å,Yº”B¡¨®A(H~ÊñuDÄ×3´- ÑèÒ禒ȢÁ‹í£»nI_5ÊÇT5)S¥Þ3V%¥|73ÔÎEÀé`LC¹Ú_,õP¸\„ËEøêõ‡ÂÞŽ )™2eJ£‘Pdddddd}¹‹‹ËÁƒ1 »¬Z½Zîê‘mI¡P¶nÛ¶uÛ6©d×î=Êmø4iWAÕÿþ¾$X[ZZöéÛ÷âÅ‹íí6ÑP¾6éIAÄÏï³´´´ŠŠŠÒ’’7Ü¿¿¼¼ÜÀÀ hРU+WKZJª ïÛ`íOkÒÓÓ'L˜@¡RëÞ¶uëŽÛ¥ ›f¤§oܸáöíÛÕÕÕÖÖ6aaaó,¨o†’*Çí ^lþºý}Õ‘ŸþS£/ƽ.œ¸ò– B(Äy\/äÖòy<Ÿ'äó„|!¦pÜ\¬Ô³„¤miWAUVV–»»»¬ÄÃÝãß«W´²‰*ž”%8xˆ³³sC{sss©T*“ÉL{ø°SçÎã'LÐgé¿Î}½cûö‰ÿú뢴%‡Sµlé’µëÖ»ºº ø|&‹U¿È°,iii#†óððؼy Ûœýâù‹ŒÌŒú(¯rœ—›ëëëóã³Ô l4x±uøëöôU‡ÍT©×÷d”˜‚æ@Ah° €  jeá¡¶%$•’’’Ÿõꕃ10(h`лÅÝýý==½úôî•™‘áÑ©“D( ¶lÙÚÝß_ªG®È°+WD›™±Ï'\ÐÒÒ€†’OT9F …‚R4üœ“ÑàÅÖá¯ÛOÐWSPÕ ™ª ~ÝÕÓOò±ÐrW|^nîÅ‹==½||ºÞ¹sÔz¸ƒ„¤%8 ¿ô}PРŸÞB¡ð—;Îçåå …ïžÎÉÉ‘UÚÚÚݺ«zK ·oßþqÖ,IDÕp³FªÛØØ—4åäHHÚÓð_+dªH:<-èxwí*™@}ôèÑ;wîªòĬ¾ì‘À©¬DD__åº?eHO*gßþýÖVÖ4:ÝÎÎÖØøÝâZ .8÷Ç‹/ù¬[7===NeåС!|Á‡ò{zzzªËåpª0 33k$g J•ãvŽ/¶Ý~‚¾jL!Nk&­©"!Qå3©âîîžšš*+ÉÌÊ´³³Wþ[Ÿ¤>¤'•ãåå]¿â]\lìÌ￟6}ºd33CÁü'ÕÑ×gQ©Ô’’båÍ4^å¸õÑàÅÖá¯ÛOÍW/_¾Ô´JBæUÓÅ?ÉLIóÑX)O%õÜŪoÌ!!·nÝ”l$%&† iM‡t HO6ÇE"“É”J.üy¡Ñ^JŠ ÓéŒ=zÄÇÇóx ª´´´~ýõ°tsÎœÙŽŽŽ?üð£d“ÉbIw­[·ÞÜÜ\ºY¿Ÿf©¿Ú—òUØÚ9­ðC™SÕÒÇ"!!i&™™v¶6ÁÁC<ØDm’““§Ï˜ahhØl35©JmH_)¡E2Uuæ©b™ª¢ þ_¥yÅÄbEÁpŒ‚R p èRr9”²j|SBvÁ[ù¹œ eD臣,\¸ÀÜÜ\V"¥_ÿþm;©Sù*líœÖªÞÍ©j_ÕÛIHHê³b媹ó¢@WW·!IsسwoãZ]•z¾RN+fª4D;ÏTurÐÞ銋ùA € &D(4„À„(…F8‚}ÖNã›BÂùó‘‘ßܸ‘Ø©sg©pܸðÂÂÂ7Aéb[’E¾~=|xåŠ?10П:múìÙ³`éÒ%õWûR£vÀ§AW¯\€§ÙO ùf²‘¡¡‰‰IW¥kx“´!&&&&&&Ê%$H_)‡œS¥á¾ ]}âßJž}[XiìnœÂÜìr»>þP”óêIŽ}¿Xþ aDcè±Âtô¿SϨ©©‘­F£Ñttt‚‡ a±X±±±+ÞU¥¥¥×¯]‹^±[l 8œªèåË×­ßàäätþü¹ùQQNŽŽ#BC£¢æ+_í룣å²Gb±x^T”tsíڵЧOŸ;w¶ÐIHHHHÚ d¦JÃ}i(4&Æç x •å k·Dœÿ§™ 8_PZ„³­^5a@§ ªÖ)®ÏàAA²›’  FèÈ‘ññqË££%™¤³ññ0zôht±-¡P°}ûI–kÒ¤ÉÇ;Ÿp~Dh¨¡¡¡òÕ¾>:Z.¨¢P(rµéZúˆ$$$$$í2S¥á¾T*UTUúìQ…F˺žá˜³º²ÜÓ3Æ+’Ÿ\½KA´ü?AµÈwJmFPµk÷kë›Ñû¹xáááÇ~ÿ=)))00Μ9Ý·o_6›Ýèb[ ¯¯/;nhkgWXX¨¶…í2Ä!!!!!Ñ8d¦ª }%qUC¯’f`t–©oؘ‚ôbë¾NDéóÌÿ8Ž!&È«·©eݧ >{Ìá²X–Åx)F¡¨møúú*œ¨îï`ck{æt```vvö£GöîÛª-¶E§ÓeU¡(ЉëÔí !!!!!!i2SÕä¾ ½J QPÃjý¯&ŸÎËy%ÌÌ‘{MÚeQj!2æ=½Róà…ÐÜ›[–¦mÜœLUC 2vìØýûömÞ²5öÌi]]ÝaÆC‡XlKƒ™*Cfª4Ü—BAÅ~ Ç Ñá<5 MQQAF'±X!ž<µ¡z´*je…““%ÑBÐ………oÛºõÏ?/ÄÅÅ >\[[dÛZ¶|9µé3Í•¬öõ1BU$$$$$‡ÌTi¸/ŠŽžM/½ü4Ì~°øÍ‹ìûf£MðgY™ÉºŽ£ ¹²¸¸¯¥E>QBP)êgª®_»–•™)+ L§3ÀÉÉÉÏÏoÍêÕ………aá㤠V¯Y3lèÐQ£FNž<Ù‚éJIy@àIJåË=œtµ¯®]}tz§Î ‚øó®ÂfblÂf³»ûû«}R­T‘h¹LUK|×|b™**EÈ)¬¼ŸÌ-€ÊÔ"'*­¬zK<«–çÁAFu­Îm¼ ‚i^‹6cNÕ’%‹å$é™fff’÷cÃÂ-\`aaÑ»woiƒFÛRJýÕ¾”¯ÂFBBBBBò©Afª4ÜW—&Ò²íoÌßiÞ0lª)E,àOëÞóq¤?!D˜€¦çÑMç_rm™Yõ…J–ñ’Y_®d±­ú‹|ɶ¬¿ÚW£«°µgÈL ‰Æ!çTi¸oúëüɱ¨|` ¤‰¹ü|WµM"QÒÒR.—ÛVG×ÑÑi«C“´(d¦JÃ}OÏ~’ÖÁ×ÇçØñãmnCRâ¶µá#‚ô•ê¾RÒWªCúJuÈLUkô%!‘£OßÀ¶6áã )ñé+!}¥:¤¯T‡ô•ê$%Þ 3U­Ñ—¤ý’š:{Ö,Ñ|…0!bûŽäµDBBBÒñ 3U­Ñ—¤] Žãmuô¼¼¼¶:4 I‹Ò ™* ×·üóBÂG×—¤½A´õ)**Z¼xQÈ`k+KSã¼ÜÜÖwÈGÄÿÝ1ÂÆÚÊÙÉqêÔ)uyÊæ³{÷._Ÿ®’÷111¦&Æ’?+K‹ž=vîüEv‰ª&‘””´cÇvYɶ­[ílmí¨b³Öç‡ïgJœ1qBC’ú”••}÷í·.ÎNv¶6aac³³³Õ;úÇuPÃW<Áöï«XXZ·h¦JÃA™©"é`¼~õêÜuëÖ­­miïh.ÿûû’ì’–––}úö½xñ¢\ eUèðw ž`û÷UKdª$±“4‚ú´žþûùÜk+žHˆ"q‚ |!n¦…ÚÛjÝË®ž?ÊnÕÙrA•––Ö¯¿–nΙ3ÛÑÑñ‡~”l2Y,é®uëÖËF0”¦¯‘Ü$’’ÅÄÈUÛ¶n­ªªš81ÂÆÖöùóœýûöݺuëÆD==½µD#´« ŠDu„B!A—•0ŒÌº‹`’HâììÜÐÞÜÜ\*•Êd2Ó>ìÔ¹óø ôYú¯s_ïØ¾}bÄÄ¿þº(mÉáT-[ºdíºõ®®®>ŸÉbávêÔ©ÄÄ$PtçIKK1|˜‡‡ÇæÍ[ØæìÏ_ddfÔ7 =ýÉÐ//ï]»÷èêê=zdô_\¼ô···7äåæúúúüøã¬åÑÑšq‡¦ÉÊÊrww—•x¸{ü{õª@ `0meI›C®ý§á¾Eü¿Jó*ЉÅ(Š"‚á¥@à8@1Ð¥är(eÕø¦„ì‚·<¹î eDè‡-\¸ÀÜÜ\V"¥_ÿþ...jŸNóÙºm›ì-Û³‹çäÉ“Οÿjüø6´ªÍ¹uëöÙ³g<¸_ZZª¯¯ß·oß9sæH×d$i&...©))AHŠbTTT<þ\(òù|--­¶¶®}áìì,Tñx¼ÚÚZ>Ÿõꕃ10(h`P¤AwOO¯>½{efdxtê$ …‚-[¶Ê.”Îd±Pµ±UœS_¹"ÚÌŒ}>á‚äi(ù´jåJ6›/ Aúôé3xPж­[ŽýA( Ji¿i‰ŠŠÊ.žž²}‚ *++Ùlv[YEÒæuª4Ü·“ƒöÞHW\Ì'!0!B¡ &D)4€Àq@賎pÔ3¦!ΟŒüæÆÄN;K…ãÆ…Þ¸‘Jóí’<ÿ¯‡¯\±âñã'úS§MŸ={6,]º¤~Â_î~íË£X-—©Ú»wouuõØ1c-­­^½zyôÈÑ»÷î?wNWW·…ŽøI9eÖ¬W®X1{Î.—»`þ|ɬ m³Âc|Xï!(hÐÏ?o¡PøËŽ çóòò„B‘doNNŽ4¨ÒÖÖîÖ½»Š‡ ·oßþqÖ,å1®P(LNNž9s¦4©ƒ Hpðƒ1%›666EÅ%M99’vY§JÃ}ºú†E¿•<=ú¶°ÒØÝ8…¹Ùåv}ü¡(çÕ“û~°ü<ˆÆ(Ðc…éè§¶=555·˜ŒF£éèèÂb±bccW¼ªJKK¯_»½b4–o§*zùòuë7899?n~T”“£ãˆÐШ¨ùÊþpûÖ-ÞˆÛ9-T­\¹ÒÁÁ^ºéáæ>köì¿/ýýÅè/Z舟__XX¸}ûÏ{ö쀠 A#BC¯_»F§“.³oÿ~k+knggklünNñÂ… ÎýñÇâÅK>ëÖMOOSY9th_À—öÒÓÓS½X.‡S…a˜™Y#©‡#‰víÚµgÏ^©Ç1µ«<´>ú²w`àTV"¢¯¯ßV&‘´ÈL•†ûÒP hLŒÏð@ *Ë'Ö"n‰8ÿN3Ap¾ ´g[¼j€NiÆÏëÁƒ‚d7 |âäIƒ:rd||ÜòèhÉ„¾³ññ0zôh4ß  ¶oß!ÉrMš4ùø±cçÎ 544Tžð¯¨¨X¹r…§§Wpp°ÚgÔ1pp°—Ýôõó€â’â¶°¥c2/*jæ÷ß¿|ñÂÀÐТoŸÞþþmmÔÇ——wý9q±±3¿ÿ~Úôé’ÍÌ óŸTG_ŸE¥RK»àY,…B™>}Æø Vj縻»§¦¦ÊJ2³2íììÉaèO2S¥á¾T*UTUúìQ…F˺žá˜³º²ÜÓ3Æ+’Ÿ\½KA´ü?AµÈwJmFPµk÷kë±°‘¡¡äMxxø±ßOJJ €3gN÷íÛ—Íf7šo}}}ÙqC[;;U†óø|þ¤¯¿®årãÏþA¡PÔ>£Ö¤Õ&ªß»w\\\É©ñDKKK’=î\ffæŠ+ÛÚ¢ÇE"“É”J.üy¡Ñ^ :C$)ÜE§3zôè?gÎ\mmí50½zõJNNZ¶|9µ…²i!† ù믿nݺٳg/(((HJLŒœ2¥­í"icÈ:UMëÛЫ0:ËÔ7lŒ©k€ß”/»}ÞÕ3q 1qõTÛ û·“<øZ ðùº+Ža͉@|}}{É wó÷°±µ=s²³³=z2ùv+KKéßÖ­[*Þ¾•ê¤Óë>N(9»8¯]·nÒ¤Éw#i˜;wÍ3§oŸÞt:½»¿ÿáÿ 6Ty—ˆˆ¯7¬__QQannþèñÙ½^^^/]Ú°~Ãü¨(.—kcc3nÜ—õ•tîÜåò•«›6m\ºdiee…¡Ïi¦‡ 0 Ã1Åktau&`aXŸ8cÖ¢ÿæ4->þìòeË–/_& 8(}îÀq äkŒÖÔÔäääˆÄŠÓ{ÊïMRÕÍaÂDµÏ¨u¨—f™÷ÞßÙÖÀÏ‘iD…ZüÝËmçžãDi™¶ž& ={ð[S«¡½¯Þ”O‰::å«ÞãB»I…ATUóÓ³ Nž»›™SÔL\Ìzø9À)/²_KåËf í×à ¾ž}8¿¨R‰Õ[Jð÷qØÛÃÕÑÌÔ˜I¥ o+kï?z}4övéÛšfžKfÁ·Áƒ;I7g¯<ó$+¿³›å„/üímL õu ‚ÃÍxZpâüÝç¯ÞÍ:ŠÝ?]² j¹‚‘ßìiêqþähíM I«Ñ2sª™×O,SEC 11>WÀ¨,Ÿ`X‹¸%âü8ÍÁù‚Ò"œmEðª :Uÿ?~ð  ÙÍAƒŸ8y’Á`„Ž·<:EQ8£G€U+W²Ùì¸øxƒ}úô<(hÛÖ-GŽþ.Q" ¶oß!ÉrMš4ùø±cçÎ 544d²X(ŠÚØÊO¢=ú‹»w±É©S§=<<Ô>£ÖAî·/CO·×°À‘}lìt0`À‚ƒA]mÌÖ,øfê|'!—ÆÐ‘yÖ ™‡W¶“_Súô¿ýÈW}ä Ögi÷üÌÉßÇ>jåá'9ÍŠE\Øcàõó‡iÕE:, 9Ë2oîÖ²ÆÐ6lX‡ê-B{ûû8H7M™!ý»tó´œ6ÿ·j¾†—[è8¼¿NîݽKDÊ¿;Å:Ý\ºvó¶—615Ò ìáàk?eÎî¢ ²rЍø[WW}}}L,xr}³gÿM;¬‚Íd˜4«‡„¤Õ ×þÓp_*•*ª*}ö(«†“—u=5ûµTW–»pôâ¢Ú'Wïæ–cEÿ=K©FP”ÚŒ j×î=çÎ'Hÿ–/_.‘‡‡‡&%%I6Ïœ9Ý·o_6›- “““GŽ)‰¨Aàà!ÿݹ#Õ©¯¯/;nhkgWXX¨ÜŒ-›·\øóσc\]]ÃÃÃîÝ»§öµH] -Í;;Yé€.<1ÄÀ¯Kqm*Ú½PqI¢!~Úqñ«ñÃÃþŽx—Ò‰Dááa’¿yóæ"ðá õСCýûõ 1âÚµk@¡P¦}Õ«¦ü…z‡ÖbÐ$o$Ê«JsxÕÒ½g.ÜŸ>sÑø¯¾***BäTYg*o‰ ‚`8ñÇùKŸéáî:bD~~>˜˜õñ¢b¢ZõÎE¤çÛ®PÕª÷ÁçŸ ?=·°šJÓÎ/ª\³éPÏînnÆ }úô)0t?W”_[† ÈŠ­¾Œø!íQzCW{#Ç@Q¤9ð>ˆiWz¤g§„ß~E+bŹsã’4Š™™îï¿þöíšš%ÿ=ÁÃC3Iî  ÇãÇ¿ÈÍ#-/*Š:|x¤¥e[NKøáû™¦&Ʀ&Æ'4$QÂäI_›šÿðýÌæ[¢AU¤Öþë8™*É`C¯’f`t–©oؘ‚ôbë¾NDéóÌÿ8Ž!&È«·©eݧ >{Ìá²X–Åx)F¡¨møúú*œ¨îï`ck{æt```vvö£GöîÛG$íÚµkÏž½ÒÆ8Ža&ݤÓ鲪PÅÄbåfxtz7H1løð€ÿ5«W'\¸ öIµr >n.LÀ€'1Aˆ1aPÉ%Jj* aÆL?ÖBÈ£kiæ.ö(ãMõ[Ç´µß…¶_»vÝÙçÝÛ®S'éjIIqI•ÎòÜõÛÍþýû€‡‡Gy^²¾©sß—QÁ]-Øú,=-‚ ÞVrSÓó~û¯ìý˜Ú↸Àü5ñ#ƒ½}=mË+jk¹|wçw©©Í[¶HÞ,Ûtþ~ÚëðÐϾ€±“Vqøz(‚Ø[‡ðóêdm ¯#‰_¿y{èäÍÇYù²É6APq²3 ýÌÓÝŠÉÔª®á?LÏ;G:2¸mÿå´ä#*ÃØaWË4öÂÝÙ3>;;«²SÇ,û»:šý²füïzúϯ*ô›ôt–n87,ÈÓ§‹ ÜIyµ÷÷œ*^Cç9ï0ŠR•›gma8)¬Gw+–ž–HŒ•–W¿Ì-Û{4ñmem£{7/íéa£§î«å e%ŸGîåñEj[%ëd·nJ€ÔÇyUeÏ…tws[]ûjò3777 Ñ(•Eu]€³ï—ºúÒŠ‘¦Ÿ5|4JCs¡rŸ&¼¨ßžméhëÖ§¾\‰ë¢P(«°úûê’™Y6tèq.W¤D¢ ý矉ffºQQÿÔÔ—.ísýú$/¯½ÅŵªtW²e}õõ>xõªÒÕÕxöì€À@{oï½ÕÕÂfjVWW×S§Nkëè(‘(äï¿ÿ¾yó¦ÜzhP•f!Ÿþk’¾ ½J QPÃjý¯&ŸÎËy%ÌÌ‘{MÚeQj!2æ=½Róà…ÐÜ›[–¦mÜœLUC 2vìØýûömÞ²5öÌi]]ÝaƋŢP(Ó§Ï?¡ñj@£Ñ<Ü=ž|¸¶¶60Œ^½z%''988¸ÔE :C$ªó‹M6Å•••÷ïßW»¨L«!7äÁ%ÐWUð´ ñ–xY†?+ÆŠ+ðÚZœ[×TbB>@(*r5›êè—~£š™±Ý]<=¬üf€D’‘ž.P¹“újäØïìm»uëÞÉÓwÏž=`iÁv6)!p1‚ Òïe¡P8fL¸»›ÛŒéÓBG]´d¥D>?*J’±ÿ÷êUi 2gj$¢Ú´i£‹³“­õ¸qáþÇ«.‘mI¥¢³§ ¤R)………Ý>óóìÒ%hPÇÓÓÓø¼ ¿¦DrF4ºŽä Û”õù®PUUuøð¯ ƒz®hÈWïÚ\þçŸÎܽ½}þwçΖ‚Àžo£æ™›éKb¦E xvéìãÛ½Sgï©S§¼Èº#Õ*ß+ûIá8Ž ¨¬ÞeYÔ± ©ȹâô¾iÿáÀ¦‰.f¥¥ecÇŒNM}hdÙYæúAêÛèÙ¤ö iP¨Nªÿ×Pã†ô8xôe[:¼x••„ ˆ4¢rðè«üŒº!hŠÐP·üüjIDoÞT]½úrÔ(÷æk–¨ 99¬¬>¾“×­[kie¥‘_õT¥qÈL•†¡ @Ñѳ饗Ÿ†Ù6¿y‘}ßÌa´ þ,+3Y×q”!÷Q÷µ´È'J*Eýõë×®eefÊJ‚‡Óé prròóó[³zuaaaXø‡ «×¬6tè¨Q#'OžlaaÁáT¥¤< pbÙûùXJpswçñx‡ÿÚµ«ƒNïÔ¹ó„ñã­­­=½Üõl­ŒVÏe1µÁäI_—¼õìû%‚ Ï_—q+Ã0C /Å®~/Û`?ÓØÉ©ë˜›’òmþݽ“öþȰúçÜ•©ÜEÑêZDbld\UþBáùZXÚ*7¯–Ëßwäêäðž¡ïFËßV­ØzáÕ› .O¨d¯œ+$άc}ŸB£VÔ)öV7÷Sæ}xmK—¾lÙ’¯Æwppˆø¢ëÎ#)tƒú^kêuÛüo)‰…zRÞ@ce]œ»ôƒ÷•ä½r{Z##í‡ëT<©¨à!jj¬zˆ‘‘öæÍƒSS‹.\xª)­†asçÌù&òÙ¡Ú\U Afª4Œ.M¤eÛߘ¿Ó¼;`ØTSŠX, ÀŸÖ½'.æãH*Bˆ0MÏ£›Î¿åÚ22³ê §L™Òh$)—åx‹‹ËÁƒ1 »¬Z½zÕêÕ²Ù– eë¶m[·mk’í¹ß¾L¦QÉÝäuÕB_ÿîNÎ|.Á«%*+×3oÛ ÏÙC0ŠCfGþþ”1EK[_“fÈç9d3ïÞ¬_¿nÛÖ­€ …®Ã4°éÜ󦑭‹ƒ™$¢JMMýfò¤7oÞ 2ä÷cÇßuÃ0ýð}.‰ô -?è—9.Š 2òÆTUq%o\\\tôL䳊ZÆÇÇ͘>½þi☀F×éù™ÓÜiƒè4JaaaxØØg9/;L”Ì-“XD£RhF` }P;{ûrLE ³wGùÛr±ˆ«ð|«jxš÷ORö™ØXc­2Gû®>]'L˜hlÄ Ðßñ[–Ë\ù^ü}­dƒ!q¦©É‡AQü)¨b•ìÕ¢R÷#ð8n>~n®Î¹O7ºú…Éù izæ©ùu¡$GT¨§!åŠ7¬²_“DT’|•«gåöt ´µ©gφëêÒ‚‚ŽbØÇ´ìξ}{ËÊJ.\Ô®Tµd¦Jä¿ÎŸk€¡Êÿ• Jš˜ËÏpmi“Hd©ÿÛ—måö6çaÂý.ØùŠ|œ@q®@·èò†þ©…Ç,ûYK®ß|(™*ͬhÜ ¤T•­{µ«üÐSïݤïÇUÖ~ƒæMý~¬loDvä E©RýBố77·›iR¹lûœWE¥åSc}??¿SÂï>¥ŠÄ˜‡‹†áiod[>]\þ¶Ê؈5|øˆÐÑwÊD ÏÒîÓÝ™mÊ:tò&vú6"AÊÊÊ%K™üµ–®—'Ì+¨g{³­ÑcàrbúÎ_¯!Š&Jºpá¢Í{þbŒùnà2ñÆ ]Wáù¾Ì-Wn…‚ày;…Y\Z™ò#"¿ìùe/.OÀã‹ XÚ —/ÿƒ yT ªd/‚ É÷rú÷t€½ûöUUsõYºÁ KÍPÏ*‡.Cå&¼K:vïj?°·üÄg.—»uË†Ž‘ÌáêdJœY…ẏWë?»Ç¶tôè:°~ã–žS%1FztÉ«Ä<%ö´éé¥ÝºYÊJºt1{ñ¢‚ÇÓÀ„*:Ö«—ÍàÁ¿§¤4R;°½‘——ÇãñfÏž5{ö,©ðìÙø³gãOŸ>3` ‚O­TµäÚ$Ÿ Ýcit-[‚x÷H#¨¾‘eÂS„šò?€_N‰šõÅö=ŸëÌk 3¼ó+šÃã Wn=÷Õp7OÏ.ž]®'ÞÞ»;yÛ–uuºÔùVþ ¤¸´jסK£w±²¶–<|úß_«½úÏ“kŸ–ñfÆÜCû9ôìÙÓÌÌL(>}úôß‹¿ñj åZ¦<É›1oûð~N=z˜˜˜ˆD¢ììgwïüwòÔI¯FÉ7Z-§àEÚÏ>‘*¹â½ð›É“¦Mÿ.(h…BùçŸÿ-^´˜¦mljãÙÐù*7Ãñ‹ÿ>¶4Âìíí õY|>ÿÙ³g'O?tè£çPå{Rçîœ]»î“lFGFGvî¼[®pƒa2™½zõ’•P(¶™™TxíÚµ/Ç…ïÞ³G²œZaa៊ˆøúÛïäׯՠ*ÈÉÉéݫ眹s¥3´úÀõ7$›[6oÞ²esòÍ[ÎÎÎõ»79§ŠäÓ¢©Sq-Ý^ÈF°¤K,=½”CÔYc÷h”ÆübÂ]Ž]†8v¢¼»¾‘O¿oe%æ¶u½ìÔ}tW¼Â†•ƒ¿•ƒ¿œ°~{¦¾¹G·0¨Gý–,«N  ¼û(xfB}Cë†\Qº–žŠV©dBqéÚðmAù^°vîiíÜSºifíÙ|«¤¡@@@(zv™'¶ÖÕ7wè4ȡӠ†,qs27`iËJÚOEõ¶ÕÓ ˆDø AG·m Þ¶-˜N§$'ç~ùe¼ô¹?E(”Ï…0™ 77cM¥:…Ö0mšß´i~RáîÝ÷¾ÿþb£š¡Pɰµ*µÎA%ÝÛ¶6ã†áïR©©©ÉÉɉU*dßUA`X5E0¬ÎX-Nà†5uüŽÌT‘|Z¨q÷°tpñ…ZV(Ö²°î8Ï}LÔ™gÖôJ»»öÃ÷3‹^§H%>Ó=ë©óÞúsPðJS\f] š¨SÕõ´ÅŵãÇŸU¸ëï¿sd•t³gO›ª*ÁÁƒ) ËÑ¥Ë%{•kÎÊ*“Ý[_ÛÊ•×W®¼®Šäunžìæ€KË>äÉîݽËd2'NŒhiU...² )ù¦ìæ‚ ,X¨Š²´b¦ŠÐÄ„-2SEÒÔûMfå$Ÿ×!iM¶Ç\:uJIÞ#`»µÞ˜NâÑm¬G·±·«K¯K›sÐ}ú¯ õ¨‚§§YMÍ’ ž~ùe|CMÑ¿¿ýöíÿ½}ËÓ¬ÚÕ,Kff†­Mpð6$Q›äääé3f6Þ´U©N‹dª™W2SEÒ®ø¾;$]¾‚€¯ÚÚŠNóËš Õ¶zeÁ‚Ë?ý”55†$dâÄ?4®³¥5KY±rÕÜyQ )ë¯PÒöìÝÛ|%W¥:䜪& ‹Ÿ¤¿|–óæó‘}-‰³xÑ»Ì!N·³³ûò«¯¾ûn&…¢Nõ¤¤¤””³fÍ–J¶mݺcÇv¹äg}TlöéÐjs,HH>.\oÜzп÷gêu¿–|ßÕÙ¹êQ…’’Ú’’Zå &&&&&&Ê%Ÿ2äœ*• ¢¦¦ææù€°¬l<¯^{^ÙÏŠ KµÜ]lLµø|,©Àðiv¾‡»M£ªzõêíââ#BC KÿuîëÛ·OŒ˜ø×_¥-9œªeK—¬]·ÞÕÕUÀç3Y,ÃN:•˜˜”zÓ¹ÒÒÒF æáá±yó¶9ûÅó™õ HO24$ÄËË{×î=ºººGýÅ/ýííí 'L Ri;wí633+--½qãºP¨ùšmNGZ`•„„„„¤]!—©ú–©iê¼(¼¡uï•u! *¯Æ‚|Ö—Í £8b@ŒܪxZŒiѵ“ž×$gó ò«Onl0¨âñxµµµ|>/>.þêÕ+!!! c`PÐÀ w%€»ûû{zzõéÝ+3#Ãã}¡}¡P°eËÖîþãg²X(ŠÚØÚ*<ÊÊÑffìó ´´´à}ÁÀú¬Z¹’ÍfÇÅÇ3 èÓ§ÏàAAÛ¶n9rôw—••µsç®Iã!C)>ùÑqû6£gí ßÕÞݺ5åò•ܶ5‰„„¤åd;oÞ»:îË–ßú5>©mí!ùèà™*P+¨Âp.B»üˆ“SUƒã8&ÆÅb ñçù¼·"ªž¥é›Z73¼^N­T–Î8àÃúÊAAƒ~þy;…Â_vìHH8Ÿ——'¾+öš““# ª´µµ»uﮢµB¡àöíÛ?Κ%‰¨n&LNNž9s¦$¢A‚ƒ‡Œ9(9¢››Û¦Í›jjk{õìéîáѱ‡É$Õ£GÛÚ’–EW‘´|Nà8¾î§5²’%Ë–KõȾJˆ1þ¹WÝÇ‘é`ÉÀ” .ñ¶ *ªp–­>ˆ)_ã¼AåKNÞüÓL•}ßþýÖVÖ4:ÝÎÎÖØø]BkáÂçþøcñâ%Ÿu릧§Ç©¬:4„/àK{ééé©Óp8U†™™±kƉD»víÚ³çCéÿP¤ÿä©Ó›6nزyóâò233³é3füðÃ5´’DT^ñ åASûúùú®p®‰¾>>@úJH_©Ž¯Ÿ_ã¡Òw3¯]¾’KU$­IkfªB%ÕÕÈT-Z²TN"Õ#û*á¼ãU ‡ö6-®º”bR\ƒç¼üb¬¤˜[ö¦ª2¯ÂÄúH7w>4Œ——·äé?Yâbcg~ÿý´éÓ%›™ æ?©Ž¾>‹J¥–”+oÆb±(Êôé3ÆO˜ °ÍÎ]» ;;ûø±ckV¯67· S°ÈZ‡áAʃٳf5)p$bûŽÐÔŽŸ ¤¯T‡ô•êH|¥JPEBÒú´D¦J;Iã§¶ÏT5õF¤eýŸ½óŽj*éø¤Ð‹ô&R•ª`¡ˆ‚¬+l«k]{WPÁ.ÈZ{]ë 6¬Ÿb²*M‘& E-=ïûã¹1›FIf~‡“ó2oæÎ}—ð¸¹ïΖþMÌg ¾²„UGÂ~©d•–Ñ˾Ð*K[ZX-ˆ:èaŽïi!Þú8‹E§Ó544Ø-É·’ÛE$étþ»BÄ$%%-[¶\EE…o‘HôòòJOO‹ˆŒ^¹ÔÖÖ6*:úäÉùùm*ÖÕÁ`0b}BÊÊÊÚ7P¶h+ÑaÛ ‘C`Nš[˜ŸKÈáþÚå$@a`[Z,*S °ð,: Q4Õ0ê,`0Ì^Ö 3S±„c±XßAƒþ<{600ÈÜÜ<9ùæ™Ó§ÛegoO&“O:éêêF${õâ<»9*zÔÈ 1£GÍ_°ÀÐШøóçœÜœ;vr ‰Þ²edPиqcgΜillL"5¾y“…°ˆÈȲ²²%‹;ÖÆÆ–Á`$ß¼I¡P| ëÒº(í^!e?+ÐV¢mt]~òœ*w7·½{ÿàÛ.dTE5­wUsM¼º ¨€FULK £¥…QgÎhjÂPÈ€Ò‚£ Ïsh½{2”•ùTæÎñË—-óõñ&žýú:uzäÈ áC§M›¾sÇŽúúz##£wïs8Ï:;;ß¹{w玫V®lmm577Ÿ8q¯^½z?x˜²{÷® ë744Ôkk븹¹Íž= ¥¥ebb’pð`yy9‹upp8~ü„ŸŸŸ¸—@ rKZêSöqêS¿ÎS¤‹Ái7ˆp~òHÕ¨ÑcÚQ½º†bg€µè†Åb‹t•°­*Š:0QÇ’É µÓÚŠ¡PñOþ¦y÷·$döìÙ¨¿Â‹¡¡áù 8[jjëØÇQÑÑQÑÑ\Cp8Üž¸¸=qqì–å+V,_±‚ý¶wo§sçÏóÎÅÕÍÆÆæØ±ã¼Ý455ã&º–®EQÑg€••¥ˆýa¤J@[‰´•ôðñý}OK}Ê>†ÚJtÒRŸþ䑪öÁl¥—TQ/<ü† Æ"·ÒÉZk …L¦SÈ4 ™F¡1U0$;S©jiÆÆÆÆ&f"~W€N• €¶h+¤ëò“GªÚÇÅ• ö À Ùö¢€&h>‚kjì mM íÀÊÊR¬o ]Ú©ª®®>zôhNÎû¼¼|–’’bj*޾<Ø*##óêÕ«YY¯kjj´´´|}}—-[f` ^N¤ [½yó&!á`AÁ‡úúzUUUç º»ÃwHÈ R…•¬¸[É7%+—?ÏÁ¾ƒ}<ûz öõâë9dçAžþƒúùûõó÷ë0¸_ÀàþöÖÒÖÒ>*+Ê¥úEA~(++»s玖–Ž›Ð4AàСC………&Lضm{hhèýû÷'OžÜÒÒÒÙzÉ#_¾”kiéÌž={çÎ ,(++Ÿ1cF>¿Í¯~ý¾P_OW_OwÚÔpA-mR[[»`þ|›žÖÝÍCC'|üøQ"º½|ñ"tBH/G#Ck+ËÐ !/_¼ˆäöÑ>[=ž9fôhs3ÓžÖV¿ý6»²²²Ý HP”4ÈÊÊý9Iûèz‘*ˆBÑ¥#U®®®Ïž=œ=söÅ‹AäA+^äA«¨¨Í––?2íìí/YrïîÝñÁÁ¨/ò`«Q£F5šý6((hðàÁ×oܰ·ÿiÃó¶¶¶—.]VQUÒ":\[[­¦¦÷GÜØ1£SÓÒõõ…U‡…Ò²Rn‹-ÖÓ׫©®9}úÔ/¿Œ»wÿ¾““s%·qm•••5þ—_úôé{èÐáV2y÷®]cÇŒ~ü䩚šš¸SKP””øù÷þƒ( •¨ŽÁ`P5¾W‡ƒN•`zôèÁ©†›»; êëWyÐyÓ ­­ÃáYL–ê&)””\›«ò¶!)))77çÚõÞÞÞOO¾}âà]u$.!!BB&°ßþ2~¼‹³Ó_ýÕ‰N•¸¶Ú½{—¶¶öå¿þRUU¸ººz{ /D\¬¬,­¬{v¶¹†D"ÅÆÆ:88øùÁµâ™5{–‡‡ÇˆR>t˜wÇ-›‚‚{{{Î{‡’’b*•*ù¨ƒ[\üyõêUD"!<|ªDÄÊF 8[ˆDb~»vì ()sªä}^H;ëÝîï¾rõ¥>þ*•ºxñ’V2ùøñX,V®tòd«‘MÍMÕ_k.^º4wÞÜ#GŽºººt¶RrJ}}Co''Î-mmA ÛØí^‚ƒÇ£ù麺z—.]vpèJÉm666ÙoÞ ‚îkY__ÿéÓ'F¡P”••;K””€uª$<ïÝ7W2ʱ8Ž-Q0?ŽÉTÁj$35”qÓwãñŸð---=,>™¶··OKvüøñuk× -ÁÂÂbÒäÉ ,Äápb_iiioÞd-Y²”Ý·gϾ}{KJÛØ`KÄn€—/^ÄÆÆäææÖÕÕ©©©õéÓgåÊUžýúµC[iðs8UN•HÐé´E‹øâÄIss3ùQŒü¨ÔóßÐÔÿÑcFÿñǧOŸê\•–ؘXR#©ª²êäÉ“aa¡]Iôððèl¥DeÖ¬ÙK–,Þ¼iÓÒeËZ[[W¯ZÅd2X¬Ø{‡KP””€uª$<ï×KÜL-Ét`Ð[#` ÐXÊØÝ•_}lZ5Î"êj—S¥¬¬|òäÛÖ²eK­¬¬-ZŒ¾ÕÐÔdŸŠ‰511¡)W¯&EGE5Ô7DnÜØŽëJKK=qü8§S¥§¯ggg×Q‚ñê•"EJTgƒpäTu¶.|­ètúòeËÿÎÎ>r䈽½½œhÅ…j…ÇãzZ÷üð¡@u“´µµH$g ©¡ƒÁhiiID¾ƒ£#z0rÔ¨þýûm‰Ž¾)O‰D™Æ“Éä¼™466¾{÷ÎÖæT ÄÞÞ>;;›³%¿ ߢ‡ÄŸI)))9Ø;ää¼—¬Xi³båÊ…¿ÿþ¹¨H[GÇØØØ×Ç»_¿þ.JÀH•„çu´T94Ë–Å  `0“†Á)€A˜4,N a±Ã„%gHmKl Ó§Oß·oßÖ×××TWïÚµóõë×uuuÚÚÚC‡FmŽê¦«‹öÜ´qã•+>rtÛÖ-¹¹¹ááá8<þè‘#}=]€±±ñ»÷9\ÏõòrswíÚ™™™ÙÔÔdffºjõj^5òrs·oßž™™A¥R]\6nÜØ¿ÿ¾ K{õŠBUTG$åáC@ÁÇ@Zzz7===W9«*¶Ú²uË£GB‚Cê¿}{ð¿ÿ¡æÝÍíìì…”1ò`«E‹Û;Ø©¨¨T~©¼zýjssÓœ¹säA7ùdĈÀÛ·ogd<8Ð PQQ‘–š:KÀö¯bÁåà644¼~ýºW/ÇŽK–1ÊÊÊh¼íÆõëùùù›6m–QFª$='å%‹ÁÖ=§6ÑÝGcñpªü‡ f ýã½¶ß¾›7o”••Ñhß?I………l§JEEÅÃÓSÄ)h4jffæâ%K„§Ðh´ôôô… ¢ƒÁ >âØñcœÝd¶zEÜDué8úúú‡á{jˆ¿Mmûí«—/544¦N&ŠØÙ³gÏüQ¸dγ€´ôgœoW¯^³zõQÔè=„¤Õ‹e Š’’qd©B„ÇÖ‰‘*Ô¯ôŠvÃ&ASß=4¤"÷«™¯5Ró)ÿ9É*PSü-»VÛsöHÚ?ïI­šš&_Y5ÌvÕ@øÎá#GÌLÍ” ‹îººzhãš5«¯_»¶nÝú¾êê꤆†  @ •Â¥®®Ž–÷©‘Éd´Qd…D"Ñéôøøø„„CìFë?߀lW¯ˆ•¨Þ¥#U]h+Ѷêòóó,º›>âè±c‚Z$EzzúÜyóttt$+Vª’9‘ª­$x ²±RÉ©úï-ág‹T zEQÂa&³åÝýæ/ra1-¿ Ðí›ß> g t]ò‡‡ÍYE4#—ÖÚ·*ÆÃ;©rvvá-pœxåÊÂߟ3w.ú6?¯C»Êkiiâñøêê¯Â»ijjâp¸¹sçM i;wi¯^Q¨Dõ®´•è@[ÉžM›£–¯X `ïËËÛ"Aj»“œIf#m[Iðd` ^`N•„çÅá° *¥šdQ¥T|ÐeÐôzUEž#ƒ¡‚A>U|èNǪ+5âê­­M,V’{ø°X,:®¡¡ÁnI¾Õv(ˆH Òéü9Ä$%%-[¶\EEE "ÑËË+==-"2Ï“ê:cõŠâ$ªw ­DÚJöèééééé o @[ ®þ“ð¼8 À©ª›{©yËì1L›Q^ôñµe°럂üt5«q:­ï ZYî&Æ_j“dX,ë;hПgÏ™››''ßÞÁ³_¿S§N$|H``à´iÓwîØQ__oddôî}çYggç;wïîܱsÕÊ•­­­æææ'NâÒ«WïSvïÞµaý†††zmm777vr¥ðÕ+G1+ªË9ÐV¢mt]d©’L¦ºœGªÔ”èÊÝëRy&• ñú8ƒŠ€~JžY 3AèLª’ºƒ‡ê£660ÏË/àm²ÄÐÐðü… œ-œ«¢¢££¢£¹†àp¸=qq{ââØ-ËW¬X¾bûmïÞNçΟ狫›Í±cÇùj%|õŠ4€‰êò´•è@[A ]éDªŽW‹Tå–|™yE›‰$üÁî-£•ò[‰¨a#V¢zMMMkk«èÂUUUÛ7Pô}æ IDAT¶h+ÑaÛ ‘C`N•„ç½¼tp›} ÒFÄo }Üûð µ9 AXí¨€¸»¹ ­DÚJtÜål&„ Ì©’÷y!ÒAÚ³ÿ ‚°F3gÞŠ/e&¦æðUÈkZêSßAðoG ­D'-õ©T#H»‘*yŸ".¢'ª¿ÉÎ^ºd‰èÕP‚ìÝ·õ¨ðUÈ«‰©ùå‹çD·-tud©’d)&#U@ÕEé‰Á`XâPZZ þõ ÂV‚@ ŠïÞm"îbGª0˜(!g“oºÃgYiÓYóBD!7g<ûXÕÑg[âŽR4`¤ (]/RÕYž ô¨º•åRýLCJ • ˆ¢Ñõrª`¤ "Y`¤JJÀHDÞHK}Ê÷"h+Ñéz«ÿä~ü(d:±:wi¸¬xúôé¼¹s]]œ ì-ú½ªªJˆű•pfΘ®¯§»è÷…Bú@[A ò@VV–èûy´ÅŠT•V4}#á08 ‹ÉÄ`° `!,,‹|¯Š„U#âjZ°dVÒË/Å_¸£5L&’˜˜Ç~TQÑÄÙÂæÉ“â‚‚Z@bbÞ«Ws.ôذ!E¬`‘ˆ£R™¼íGf=š%ºœ6™;7ùÇ»åüýwUbbè„ ½Nž”ʯRÜDu6ÕÕÕGÍÉyŸ——O£ÑRRRLMMùöäŒTÑéôààÚÚš¨¨h55µ¸?âÆŽ𖮝¯Ï;P¬Î]ÞHUÜž=S§N3ïÞýÓ§Â#‡gdd<}šª®®Î;\¡l%„{÷î={öŒ@ö%Ú ‘`¤JÂóv7RÞ5ÝdÇd½á;¦lŸb°c²Þö);¦올·+Ü r‚î‚aZkÇu›í¯c¨#h ‚€/ÊÕÕ ººªNNW¯†UT¬ Ó#¿~]yêÔX=½[:ÄÆ«ªZéïoùâÅl2yÞ=Ã÷î±aƒ–A6!Ȧòò倈ßææõìQÎΆ׮…ÕÕ­¦Ó# oÚÄ?ììlx󿤆†µdò†gÏ~õñéÎ>ÅéQÒÓK¦¦¿vAˆ•¨ÎŽB••–Þ¹sGKKÇÍÍõ?'„Fª’’’rss9:yÊ”±ãÆ]¼x©±±1þÀ¾s‰Õ¹«Ã©Ú÷øÉ“+W†††®[·>>þ`iIÉÍ7øW([ ¢¥¥eíšÕ7nÂã…};…¶ê ‹~_¨¯§«¯§;mj¸ –6‘A°P”˜¥´i‡­ªªªÖ­[8b¸™©‰¾žnYi)oŸ‡Œ5Ê¢»yws³¡þÏž=k‡n2›H0R%áy•ˆªê )¤òGMõ-fz ¥¾¦¢ÑÀÑ4TV—T8¹°êªhˆN©NEÕ‡¨ ÝzôÐf0XÔ>}Œß½ûzâÄ›†Š¥¥ÎºuÞׯOôö>Éî©­­¼wïˆ%Kîåç×(+ãI$*‡™>ÝÕÙù€Áà®0Þ§qjêÌœœêùóoWV6ÙØè:9ð*àâbøìÙ¬7o*§O¿ÖÜL›3§ÏÇÓ<‘•UÉÛÙ××ðþ}µD®½ã°]%W×ôôtÀÙ³g_¼x)¤‚g¤êþ½»ÆÆÆÞÞß¡š˜˜øøúÞ¹s‡wïjq;wux#U={öä|Û¯@e%Ÿ P0[ bûöm&¦¦SÂÃ7lX/¤´UDZµµ½té² ÇÆ‚¼-BA°P”˜¥l×V%ÅÅׯ]sss÷ðð@ï±\œ9szåŠ[·mÃãð9¹9uµµíPLf ®þ“ð¼8,@p*L:•NЙu^—A%±êªX8M ‹F'Õ³´uq42¢†ÇbŨåÍ…ªª’º:AE?y²S` Í ãîÝ»w ÑÏž•egWæä,pr2`{0D"nÞ¼[Ïžýð H$*‹…7𙀘˜aUUÍ~~§Éd -ãؽ{heeÓ°aR( À£GŸ_¾ü-"Â÷—_.sõìÖM%&fXvvUrò‡v_»pÄMTgƒÅŠUå\ýWPP`ooÏyÖÁÞáQJ •J%‰\ÅêÜÕisõ_fFÀÁÑ‘ïY…²_þþûïÓ§N?xð Í¢ÿÐVGI‰`Þ½»ð! ÁÂk×o ®­‡§§Gß>ñHʯeÇ,…»×²A\[yxzæ|9|˜××)+-ݰ~ý‚ ;n+™M$X§JÂóâp8f+©âs9¥µ¶ü}Ñ—j: ·4·(» LjÉÛ5M¬†²…d ƒk¿O²²æ45­«®^µwwÿùí·d€Û¸qÐû÷ó×Q(YYsvvzìQ­­ôŒ QW®‰8__‹‹sPJnð`Ë+WòP € 9ù£·7÷ß˜Š þêÕ055¥ÐÐ+í[«("LTÿvvàŠ3RU_ß ¥­Í)PK[A†>~ªX»:Â×HÖ××oÞ¼ÉÉÉyøðá:(­xa2™Ë—-ûu֝޽zµÙYÁm% JJ>;f))²Dø÷ÕóÎ#²|Å ‹ÕžýXe?‘`N•xó zeƒ,¼ªVO/MS»žÃ|mØc”5úh™ö‚šmP@gk];g++‹%rd„S¦\õñ9Õ¯ßqƒ˜‘#/ÔÖ¶ Z¹ràñãoκ¹2ä @YùG°°©‰&zmmeSUÕ,¼›ŽŽ²’vÕªJû'2ÒWW÷?¡`"wíÚDWW£áÃÏ~ëbÅÂÊÊÒʺgÛýþ…Ÿë„ðo°úÂ!V¢P(3¦Ooim=qò$‡“¥V]…ÇÕÖÖ¬Y³¶³ˆß`aII1•Jí¸p4f+ÖF¥]…ÏŸ;::&&&º8;è»»¹JH”z!ÿUV”ge½‰Ž¶1Lù©rª½²Áa1‹Eùü†R‡§uûJ/« 3ÊçlFQ9`hÐÊÿ&ªbèXR?tÜqxü÷æM%ºú“)Sœcc3öí{¾å›ÿ$: ƒedÄge'$•ÉDöî}~â„À_ €KL õò26ìÏ7oøçÐH±B¯|þ¨Nß!œ1mm-‰Äy–ÔЀÁ`´´´xŠÕ¹«#¨NF6mjNÎûk×oXZ |J«P¶ââëׯ»vîܱc'“ÉdF£“H$555Þ¤uE¶•œP_ßÐÛɉ³…,444ìˆd±b–]‘ªªªªªª¸=±7m633»~íÚÆ‘TméÒ¥]q¢¬¬7o²³##"¶lÝ ÚU±M~ªHU›`±X&ÖÐjŒ!jÕ•ë70¬£µ®L·ži³î‹~¦­÷­Æ`É~íÀb1®±ñÇ£à`þÙ*œP© %%þ¿#*•™šZ2y²“ªª’  ãÉ“âÁƒ- ¿Ôrþ ðxìåË!þþV£F]ÈÌ”â“f™Áƒ±··ÿPPÀy6¿ ߢ‡²²2ï@±:wuøFªh4ÚÌ3_¾xqéò_ÎÎÎB†+”­¸(++#“ÉK—.éim…þ´¶¶^½šÔÓÚ*õ)?U‘mõÓóÓÇ,Y,Vssóþý¼¼¼bbc‡øûÇØÏdò©ø#ÿ¡•ò¢X9UXpD}e¼*ÞÈSÝоµŒjÙ“^Ù¨¿ºžIÕHÛȃ¤#‘*^X,äáâß~s·±é¦¤„4©÷ܹ}Ú•›[£ªª4~_ggîoT«V=04T{útƤI½ýüzÌšå¶ ¯+îÛÙé>~<}òd§Aƒ,ÆŽµÛ¶mÈŽßë‘>V·sW‡7§ŠÁ`ü6{VjêÓ /zxx®P¶âÂÎÎîú›œ?D"qð!×oÜtswçí¯È¶’¤,Dc–kÖ¬Ec–èhÌ’Á–êÚ…èÖ­ÀÇ×—Ýâ7ÈD"•—Kxã ™M¸Gȉ‚EªpXzë·–ò¨ÕÅ-EoÉŸ¾1¿´f“ ëMßZ ÿ¦|®'W´Ö# F¶3g^ÿø±îÝ»ùµµ«§Nu ù«Í!7n=šµeË/~»sg ×Ù7o*½¼NVV6:4êÁƒ©k×zWW·ð yûö«‡Ç±/_šöî‘’2ýر1®®F©©%èÙþýÍsæô¹reûgöl>ÿ$EG+ªÏ©â—Ãøo &8$ÄÁÑqîœ9/\¸qýú¤I544.ü=ûøñc#Cƒ¤¤DQ:ÿdðFªV®\qçÎÐаºÚºä›7ÑŸœœ÷èYE¶^ÿ‡Ãxyyéèèh+ùCJÁBqc–]»ï¹h?“±+é2›HÙ©ú©rªÚDÈ$è;kÐæjÛÚ8-,“IG€ÞËbÒŒ3ƒ0Xt¼²¹Z}_5ŒŒbyãã_ÆÇ¿äÛ¿²²yô苜-Ìt¹•+ÿ·råÿ¸†0™Èܹ·æÎ½ÅnÙº5uëÖTöÛ¿ÿ®3æ"à«[AAíĉ‰|µêÝ;o»”hwEuAR>|øøþ,½›ŽŽžžž«·?̓QRRJJºA£Ñú÷ïôè1vÂb1™LöJá2xsª²^¿œ={æìÙ3ìÆY³fíܵ(¶­ÄÚJÞ1"ðöíÛÏôÿ gÍžÝA±hÌ’³%,tÂ@/¯%K–: ¨EÒå9räùsçRR~­¦<|¨§§gj&át2›t-§JÎëT½Î-k ˜ÐYŽÂì!(}B ?Á}y¥BûÕ ÆŠ•+ÙíÛ¶møøøà©LÍY§  ¯¯øÈ¾Â‡øû×Ôþ§ ¼Î?¼uªÒÒ…/Vd[µIIéîÐVòFpHH¡„¹sæ¬_¿AUU5î8®ˆõ¤‰a‚ƒC•••ã7mÚôù ‹Æ,9[Ø1KQ$z{ \¶|9;%ËoÐ À“£\±11±±1éÏ2¸ óJAn%'rrsSêéêzöëêíí½tÉâÊÊµæææ×¯]KKK‹‰Eë#ˆeºŽL$Y[u%§JÎ#UeÊËü ¹ØŠÛ©ÂápÙÙ|~˼Ë7„W`‚ Zýü|ˆ±nnn.,,¤3èŸW¸dA˜L&ç?x&ó?GX‹ÉdJc…'L&ó×_g²ß®^µ 0tè° /0ÌÙ?ÏmÛº%6&†D"Y[[ÇL û~ ☮CIÔV]É©’óH¤Ó)³¢z;n(\‘*_Ú¬¨üLˆ±~õò¥††ÆÔ©ÓÚ1‹ð˜%—d®ˆ&W´xõê5«W¯i‡bÇã¹ÔàBCCcç®Ýhb™®#IÖVœNÕ•ÄDÀ„!Çb¡X«ÿ ò€X‰êízT¢­éBäççYt7ŸóÛoBZ$EzzúÜyóÐ5]E2'²´²¹ÀŽÃ©ú#»NбèÀHD¦ˆ›¨#URFª ]…M›£–¯X PSSÔ"A’¸LiKf#c[q!ƒ ”œNÕ„ö†Q‚ŽÅBl§ A6µÕaL;ôè85/¤Mòrßs¾íhEõ¶€•(Àœ*HWAOOOOOOx ÚJºRª[É7Ûî$:k^ˆ´á[äSè¸÷Ÿ(@+A Å„+§ MŸr,vªFîœpQgÍ —"1+ª·©h%¢˜°*ô«øo³g³¿–ó‹+\Â9U·’ovŠ#â¼wßT\É(Çâ8ÊT!`~“©,ƒÕHfj(㦠î>ÆÃ”sxKKK‹î‚„ÛÛÛ§¥?;~üøºµß×  ‹I“'/X°‡Ã‰}U¤¥¥½y“µdÉ%ãöìÙ·o/×^D쨪ªÚ·oïßÙÙïß¿§R©oÞd›wxMTñ! Ì©’0§ ( Ÿ?ÿç[}Hpp}}½ðc±P¬HÕ×Kô•º«2 Õ™FjL#5¦‘:òïÓO10 šx[õÔW2‰çy:å ×peeå“'O±´´´ÜÜÜØo·lÝÆî{þÂ…C‡ÛÚÚFGEmß¶ ´‹´´Ôýûöq¶èéëÙÙÙµO_JŠ‹¯_»¦£Ó­ÍíÞ$‚••¥•µ¨…ìjjjZÄAUUPñ¥ }¶_…¼B¿(înnçΟ?wþ¼ô¦ÀlÞ¼ÙÈcÆ‡ŠÆV°iœÀŒ¶sçÎÛÛÛ3Nˆ89T9/zÌb¨cðX&ƒÅb1 “ÅÄaq@X,8m5œq7\mˬñSuõó݃„Hst°ïÛ·ïÙ?ÿóuTed>·±± 24À¿°°ðSÑg±‚U4•@ nß¾íÄñãŸÄ^&z¤ŠÅb¡%k>±A‘ª¼Ü÷·oß çÞ»ÐÄtê“Ç‘¶¶¶èñŠåËV¬pŒ¼þîÝûä›#ßð«ö)w7·n’ @:ˆ¯°;'Ê/¿üYTDºww "2ãäÉCò=•–úT m%i©O?yÂ~Q__Ÿ˜” ð=nhh8wþüo³g u½ JÄÛkT½:-áÇr©r´T94Ë–Å  `0“†Á)€A˜4,N a±Ã„%gHmKl Ó§Oß·oßÖ×××TWïÚµóõë×uuuÚÚÚC‡FmŽê¦«‹öÜ´qã•+>rtÛÖ-¹¹¹ááá8<þè‘#}=]€±±ñ»÷9\ÞR^nî®];333›ššÌÌÌCCCW­^Í«F^nîöíÛ333¨Tª³‹ËÆû÷€žB=ªŸ x‹x;h+Ñ«J!òLdD`ËÖ­ì–ßfÏf?ét,:Š•SETÓÒ©:]ýáì·Ê]{s@ª,ýXgáÓTçöðÂüRDFº)+Ô5CUµÚØøIDJKKñx¼††ÆÛ¿ÿvìÕkJx¸–¦VIiɾ½{§N›zûövO©1bÃúmÛwØÚÚR) MM“yéÒ¥ÔÔ4ÏýËzûöíèQ#bbb ‹>ååçñ*››èìì0AMMíìÙ3ÁãÇß¹{ÏÅÅE"(¢WT“½tÉ Fø>ÿA½ûöÁU ‰çT)V¤J % &¥•JPéµ_¢½µšñ¥ˆ¥¤‡aQ¨5U,CS„Ü„hpX1þsA&“[ZZ(rRbRJÊÃÀÀ@"‘èà€vðì×ÏÉÉÙÇÛ+?/ÏáßýÌi4jlìtwI MM,+èyÜæM oÜLVVV°ƒO\DmÞlhh˜˜”D$>>>ÆÄí‰=söÏv_`G=QƒÁ°w•…²2˜'@ at¥½ÿä>>!áÇ,Sª®ºp΍@ ˆáÝûæTñWÐ+Š‹0™-ïî7! ‹iùe€nßüö=»¡ë’?ÞÁ³_¿S§N$|H``à´iÓwîØQ__oddôî}çYggç;wïîܱsÕÊ•­­­æææ'NâÒ«WïSvïÞµaý†††zmm77·Ù³g£g™L毿Îdw^½j`èÐa.^ìèóCÜDu6™W¯^ÍÊz]SS£¥¥åëë»lÙ2ƒ6ž9szåŠ[·mÃãð9¹9uµµ|{ÒéôààÚÚš¨¨h55µ¸?âÆŽ𖮝¯ß…»/ž?Ÿ4i¢»»ûþýˆÊÊ—/]\ºt ‚ áS§òvVp[¡û¸¹¹{xx¤§§sË8 nIDöðæT}ûöMø±èü<9U¢ ¦DWî>X—rÀÈ0©,`ˆ×Ç1TôSòÈbPX˜Áx BgR•ÔÚa«§OŸ^¼páùó̯_¿êèèølØaddÄ>+© ”ÙDmÂéTÏöœ‹…bEªrK¾Ì¼¢ÍÄþ`÷–ÑJù€þê íTmÞ¼ÙÒ²»ÝÁÎ~ÉÒ¥÷îÞÿß¿C.Î_8 ê_²7äÄý{wÑÿm_ß;wî(È¿7,‹ÃáÔÕÕ¿¿Åb554q8þSt[Ià–[[ÛK—.«¨ª i‚´Ã[¶l555E D‚Dd¶qm·gOccãÔ©ÓÌ»wÿô©ðÈáÃOŸ¦²o@B(³‰ÚÖ©’——n»Dšˆ›¨ÎÆÒ²ç[÷>}_ÛZüøâùsGGÇÄÄÄýûöVTT˜››ÿöÛœyóçó-]QPP`ooÏÙâ`ïð(%…J¥²«|ýÄL›:íò¥Kë×­[ºl‘H¼|ùRAAþ©ÓgøvVp[ G,ã@KŠ‚’«2o‹¤äççàà QG\[퉋ëÙóÇ&÷N½fΜqóÆÉS~ìÖ*‘ ”ÙDm"U§J‰êòìQAä‰$ª¿zõ `cc+P¤q2›ˆw77έ”¥båTAäö%ª³¡R©K–,m%“?ŽÅb…§éÖ­[QQ‘¯/»Åoߣ””òò2 ‹\µµµH$g ©¡ƒÁhii‰®p×%:j³–¶öŸçΡõÌ|}}+++×­[;fìXÞ"·•pÄ2´¤ ¨¯oèíäÄÙ¶±)…p455,XèÙ¯ŸššÚ»woìß8bÄ“§©ÛYÔ××oÞ¼ÉÉÉyøðáh‹”.Pfqѧ;€ïê.I¡X9U®N[¼dɇù'NœìnnÞf;{ûׯ_ðÃñb!,Ã'FkooŸÍÙ’_oaÑCøîŠ? ………...œb]\]<ø‰DB×r¢à¶ŽXÆ–d“—û=ÐÕíÆ>ÌŸ?ó,ßá\½šÈÕØPÿaCýëj«ëj«;¢3‘€ ›€ø ðè¸XÑ‘¸­Îûð¡àû†Ò»@™MÄFW·[}}½••¥••ebR{jP‰ŒTAdJG*ªÓéôåË–ÿ}äÈ{{{Qž»9òü¹s))Ñ–”‡õôôLÍø$uxûö파gz***ÒRSg (ñóajjš““C§Ó•””Ж¬¬,uuuMMMÞÎ n+áˆehI6·oK2É é`¤ "kÐDu²='ƒ±jÕÊç/ž'Jpvv1“1 `¨··÷Ò%‹++ך››_¿v----&6}žõøñãIÃ&$‡‚CB%Ì3gýú ªªªqÄihh.\ø{{/´‹ñÛœ9s~ûmbXèŒ3•„ä›7?z´lùr ­þ ‚ ·’“ì}ôtõ =ûõmZRááSÚîH‚cÇOHPƒFÄï{ÐuZ¤Šïšv¾ÿ÷™žžžpð`VÖëææfcc“á#†/[¶\OOODá?q{öìÛ··¤´ ––öæMÖ’%K;[)þ´»¢úÖm[=zÒð­þáƒh£yws;;{!£0ÌÙ?ÏmÛº%6&†D"Y[[ÇL CÏ",“Éd±Xè[%%¥¤¤«‘‘‘4­ÿþG뢹íà—_ÆcæÐ¡„ æ³X,KKË-[¶²÷ÿ†¶âDø>Â- È YfÈ ‘«‹sTíÜЩÊÏÏG91°ß666öë×Oˆ„C 7FöéÓgÝúõzºz¹y¹Ç»yãÆµë7¸Vo*ii©'Ž—[§ ü7QDj|õêµÎìý»·ï‰I‰‰I‰ì³ÃÂÖ®['|: »vïܵ›÷Ô®jòúúú‡ië ~ZÆýò˸_~á{ ÚŠ“6÷!bhIA¿@ ò ž bÐà Oø^U®“sªD)ó…Á`øº“¯^¾Ü¼yÓ˜±c;Ž>Í5ztXXذ¡CgÏšõèñcáUÛF%Ú_ޝƒÃ>ä1ÓF9Vö¯>11QxÒ¥;Ž9DâDEùITÞCŸSõáÃ===eee‰ôíÛ·¦¦¦ÖÖÖ†††–––ÏŸ?¯^½šïÀøøJJJ11±œÎS–Ë—¯ˆŒŒxô(% `(`þ¼yÿüóñaÊ#vŸ±cÆhii²wAÊËÍݾ}{ff•JuvqÙ¸qcÿþÐS›6n¼rå¯ÃGŽnÛº%777<<|ßàiSæpà@77·ffòßï( .ìl ŠÂ§O…’%ÝÇbEªŠŠŠp8\\\œµµuii©¶¶6ºÎˆN§Óéô’’’U«VzõêÅ;¶©©©©©‰·~#ÀÔÌ ƒÁ”iqYÔæÍ†††‰IIè6[>>>ÆÄí‰=söO´FÝãÉ‘Ú5mÚôýû÷EoÙ‚{Ξ9£®®>>8˜¯|ÞáBf$“Éij×ÿ Ú$D:::ššX,–½ÓSÇeJÎDuÔ£200(,øù†‘*DAr€@$‹È+åDDjÿÄŠT555¼¼¼ºwï^^^®®®ŽÃáX,ÖçÏŸ-ZtùòeöB-qa‰° FKOO_¸p!{ãR 3|øˆcDZû¨¨¨xxzrŽš:mZllLbâ•3fÒh´‹/N˜ª*`p®áÂgTQQ±³³Û³»¹¥Åkà@{Ñ×H B2;g¢zÏž=KJJ„t†N¢ Ô××w¶ E¡[7î‚ÆíF«ÿ½ò6fåå›±Þ!fffåååJJJEEE³fÍzñâ…šÂêêê%¥|þ)/G„ouG.H$NOH8Änd±˜œé««sy!úúú#G:súÌŒ3oÝJ®««1c† )¸†·9ãÅK—wïÚ³®®ÖÀÀ`î¼y‹-î $ ™D¸&Щ‚@ù¹)AÚäÛ·oíÈ•±ÓYr$èTÉbõŸ W¾\¾ ¸“r2?•™™Ù‡&MšôâÅ ‡ââbA£0ŒÏãÇëëë¹öи{÷.`РAè[<Çõ™J¥  ÐÔÔÄápsçΛ.ÖeΜ9sì˜1YYYgNŸñððàÚRmÎhnn~ þ àãÇçÏÛmdd*ä*ÚDLu–,âVT‡@ ˆ\ñíÛ7kkëöýôéÛêT9’üºŽ'üI¸è@;³}ݼæÝ6-µ <ŸzáÂß©TêÚ5k8½Ò’’=q{ýýÐ#cãò/_Ø¥öóòþÝlˆHôòòJOO³´´´ù/Âu8ÐËÞÞ>jóæŒŒgÓgÌÞ™Ñg´µµŠŽVVV.ÈÏ~ÜSˆt:ï).™š¨Î~‹Á`„|CEÄA&êC ©€t:â ¬­­¿}û&?r$Ž„#UíT¡¯ššG?~ü(âÀ~ýûGnÜUþ¥|Êä)Ýtuórs9¬¤¤tâÄIv…1cÆìÛ»w÷î]óç/¨©©Y¿~ç–±Ñ[¶Œ 7nìÌ™3I¤Æ7o²)|ö3]»fµ¶¶ö¸qãĺ^!3–••-Y¼hÌØ±66¶ #ùæM …â;hP›WÁ‰½=™L>u꤫«‘@ÐÐÔ$³Sà­¨ŽìQÕÔÔ´¶¶Š.\Pf‘„Ü rEÇS¨„N”#½/áïTq"zçE‹»¸¸&$ܼyšäØ»·SbR¢®îmjœœœÄܽ{×þ}û,--W¬\I¥ü¨†Ò«WïSvïÞµaý†††zmm77·Ù"ìf:vìØµkV‡MœÈN9!3jii™˜˜$aaaœ«ÇÿOù›cÇøIŠŠŽŽŠŽæ{êáÇ€éÓgÑMÐpA3jjjÆL$MÈU,_±bùŠè1‡Û·'.Ž}VˆLy@ÐßCŸ>î}ú¸·C`ûö„@ ï­ÀÐÐ`à€þ€>|ûöMWWmwus±²ü‘—©¡¡áäÔÛÌÌìÔéÓ0 @ü‘*iÄ«:Ó©266n³X[Í쎉)//›7wÎÍä[®®®PM?~,.þ¼sÇöáÃG´™}á‚7QÆü!àw+`·”—•×ÕÕêé}°G).\¸Pøé“¥¥åŽÛŒŒtt´µ´4«ªªUTTdª·âÑéÎÄåHNsª¤â!âñ]á¿CœY·vMfffŸ>}wÇÄH{®ŸΊê‚²pá|[\\òàa ™Lž4)ÌØÈ=¹=¸zízqqɽûÿ»{ç¶’’’‡g?/_3`blüäÉS7·öD¸!¢#½Çv÷S ¿Vñö44±îÛS,9®!|%ŽÜåTÉ?IW¯u¶ ]±Õ!ˆâ€Å`ðx|ÏžÖuµµž<áÛçÁƒÿ©©ª›š™yûø²óYõÿ`})ÿÒÔØo)ò ß_êßv>Îßõ׊¢û©?\(Ô]34±_Yñ€NDÖpŨà†~åÊe}}ý¾}wÇĨ¨¨2ô+–¶nÙöו¿ÐnÇÀ`0ìÒКššnn®€¦ææËýehdo)ÒFªí¦OÎÛxæâ}¾É1ÈæÞÓï~ÕˆA6÷žþƒzT#I=c:Ué|FŽEjlÔ××Çáp­­­L&Ó…#5v ——½½×]ÝnãÆŽQQQ¡ÑhË—-¯¯¯÷Þ]ù·ÕÌ™3å#Gw¶"ÐVܰ*v£••Õ?ÿü#Õy¡S‘)¢$ªË'T*õîÝ»D"199yÕªUx<÷ßÎæÍ›Œ i4zEEÅãÇ—,Yâíí•pˆ@  Î;¿}û6kk«éÓ§›šš677ef>_¶lÙÁƒñ~~ƒe~AðüùóóçÏKÄW ‰ûöíc¿ŒŒ´°°˜={6úV]]ƒ}JÁ @dêTÙÛÛsµÛØØHo^èTAdM›‰êrò•‹K477oX¿~Ûöíiii~~~?zàáá™Ó§O¿xébtTô ëײÿþ{ûömþq{ö°² sçÌ¡3uɨæŸA,ËIŠŽŽ6Ð×çlAJRBù±NSR"ð9 zʃ(rb+Ô©ÊÍÍtJJ`¥'áÅÊÊÒʺ§ð>Hç!Dkׯ[YYNš<ÙÀÀàÚõkÿõýfñŸ!Ã&0à¯Ë—Éd2‚ 'ŽWRRŠŠŠÂápœÝllmÑã¿ÿþû€þýœœ† Ïî–žž>iÒ$ùóæÿSXÈ>µzÕê`N™Ó¦M[¸p!z¼{×nooïׯ_‡……9;»øøø9r=µ}ûö#‡455988888øùùIÊVèY„§]1 …°•ùøáâE‹|}}œœ¼¼¼Ö­_Wÿíר¼¼ÜÉS¦¸¸¸ 6ü÷ï#rîÜù¡C‡9;»L /--áÔáCAÁ‚ù <==]\\&Mžüúõkq¯B6ðÚªµµ5((0t†¶¤§§;::ž?}۾σ𳨅333ÃBC]\\vî܉¶ß»{oäÈ gg—Q#GÞ¿wáÿñ—¼¶JIIqppÈÉÉåì6sæLö'_l…„)ÿaµ©‚Èš®˜¨^]]ý<3sî¼¹ føˆá—.^n ‘´45¿÷DÃáÒÜÏÏ/33ó}Îû¾}ú¾xñ¢¯G_-MMAW—›—7mêT›Èõ ôKŠK>þó휑‘1oÞþ@ø”ÉIIWMLL¾ÏŽü×h߀466íÚµkýúõ=zô¸wï^tt´…E÷aÆ͟?ŸÅd^¿qãúµk,ßnË ÈÕ Åi!3*«ªlílÇ×T×,ÿR~ìØ±…‹~?÷ç9öX2™¼fÍÚðððysæ^¸xqÅÊ•S§äçå¯^½šF£ÄÄĬ\¹êÒ¥Khÿ ¦L wtpܾ}»ªªê•+W~ýõ×ó.ôrtl÷…ÈÔVÊÊÊ11±“&M:°ÿÀ²åËêêêÖ®]ëçç7iÒDAÚýyhóÓÒØØ´cÇŽuëÖYYYѨTARÓR—¯X>|Ĉ7}ûö-î82™ìèà(·,T___##£Ë—/EEE¡í¥¥¥/^¼ˆÚ¼YÑl%Šç$ e S´Mò­d&“ úóìŸ÷îÞ > ½ãÔV×455577›™ «%£§§æÌ"‘èãÞ‡}êÀ=zôØ¿ú8ÌÙÙiĈ'NœˆŒŒEy:¶%:ÚÖÎv5)éþýûÆ ÓÒÒRÓPÇb±&¦¦¢È‘ k(A3||||||ÐnnîncÇŽýçãG[[´±µµ5""ÂÃÃàìââííõàÁÃ;·o¡O^Z[Z7nÚTYYill سg¾¾þñÇÑĵ~ýúM ;røÐþý${ERÂÁÁaù²å1±1ýô?uú‡ÛºåûЇvÚü´Ðé´M7¹¹»±&L°··‰A×UXXXAp8\è„Ðã'ޝ^½ZMM på¯+jjjA#G³UgÕ©‚ÿ 2¥¨èsQÑgá}¤ˆ 5n\¿akgkÙ£‚ NNN¦ff7nÜø1 á¯7óß#¾KF^FÍÊÊ $\§¨TjNNΰaÃØÃôõõ=<=_½zÅžûA‡6š6¶¶ì“&ff_¿~åÔ\°=„!üWÆ·Y1 …°•ði4ZBBÂÿÙ»óx(ó?àß1¹¹YW JÎô[…¶EÇʱ›¤¥{K§Ê•P©°µ+´i+Ñ!*¶Ý­äH×b‘m»TìRŒD†™ùýñ´³ãcæ™áó~yõšç;ßyžÏ|zf||Ÿï|gÞ¼y'N455[°À !ôìÙ3öc%%%---±MiEEÅÿýo’¨¨Ö¢£«‹ÂöF§ÓïÝ»ïà0SLLŒ½}aQq¿Ÿïôt^y-òúÜÆfåÊU· víÚ-'/ÏÀù€çl‘˜`:½Ã?–••͘1ƒØØ±c?ÓÖîz™›oºÍ•ë×¶¶¶K—/±X,:~þÂgg ‰á“+,!pù B7Q½¬¬ì¯¿þZºlicc#Ö2ÕÞ>%%åùóçÚÚÚˆcs§È«ß¼A)*)JKKKKKW½~ÕÓS£Ñ Æ(EÅ®h4“ÉTPÉyר‘ å嬧.¡®Iû÷: ±ÄÄÅ9ï!‘Œvö½¨ËŸ~è~¬Îí(θ122òêÕ«ß~ûí„ ¦RR’‹-úØú‘ψ#8K&“GHü×B"‰ „íí,‹F£µµµ=zìØ±dŽ0˜ C@^k=éž‹‹s~^ÞØ±Tkë‰Ø]ý>pœ-,l¤‡Ý¡±±‘ÉdŽ5Šó!ŠJ£ëÄ v #GŽœ6mZÚ™³înî¿þök}ý;w77‹5¬rõìY/ºóU€¯„qEõÌÌL„Ðáþñ0g‡ÌÌÌ5kÖ`]QwµÂÍ›7ÅÅÅ X,ÖĉoݺÕÐÐ ''×õX22Ò¢¢¢uµµ]Ÿ»ŒŒŒˆˆÈ»wo9ïzûÖ"JéthzëG–Œ4ëßQÔ1¥ìiMŸ6º”>ýÐíXÝT0Ã=QÁõˆ—/_öññY¸p!v¶¸‹3žî¦¬qVlèßTKKK“Éd¯…^ó¿œßc0‰3¼ºº·Q»÷VTT¤œHYèµ à|èõléšaiii‘÷ïßs6¾ÿ^²cuKÎÜÝÝ¿ùæ›%%gÓÎN˜0Aß@ŸÅb Ÿ\™›™¥¤¦òú(=ˀߪ߼¢/þkkkûùçŸMÆ?òÓOœ?cÇR/_¾Ìå âLÚ™»wïº{¸KHH „–,YÒÖÖ¶#|G{{;g·?++=z$&&nanžõóÏ?~ì´qqññãÆýöÛoì!ëÚººû÷î[YZa›JÊ£«kjØßQÿ¾©©÷êvâââmãá?HTWL&³½½]JZŠÝríÚµ~ïM\\ÜÒÒòÞý{ššš: F°üÀb±‚ƒƒÄÅÅ>ìååûÝwVV"„ú}>ôz¶tE¡PŒŒîܽÃn©­«ûë¯'ƒûL…¥¥¥¾¾Þw±±¿ÿþ»›»;Ö¹â©GþìCÿ†‘“›ÛÐаqãFK Î{ÝÜ\##wÞ¿ßÊÊ ‹÷þý{/_¾ ÓÛjjjrrrîÝ»÷ùçÿó_ëíÇÄÄ$ `Ïž=îîÎ..êjjMïïݽ÷Ûo¿8°ŸÅb­ß°ÁÇÇg‰Ï"oo%%¥ª—U+oݺ!´rÕêU«V®Y³ö«¯<>¶|LLL_ì³ÛóŒéÓ$‰Oˆ÷^äýöíÛ={öˆŠ’ÙÃìÝ]·b±Ç«²¸R IDATtõô>~üxæÌ#ccŠ˜{tÿrÕ©­ëP?$Š3W\ŽH"‘¬­­3Ò3¦ÚOUSSûí·ßΦ¥!ޱ¨î/GrŽ¥}üÔ²iÓÆÅ‹}|ý|ÝÝÝGý¾±©´ô!‹ÉZë¿¶ßO„ØO'99ùÎ;‡“ËÈÈøûûß¿ÿ{À–€S§NS(”~ŸÜïí6Ã+V®øöÛ5'’“=<=Þ½« ëfD–bpsóؽ{—¬¬¬Ã̙컆I®ŠŠ‹C‚ƒ¹÷‰ˆŒäÑÑ¡¨|õôé3ÔÛŠê‚ð…þ ãRf¦””ÔŒ3:Eåèè´o_ôÅ‹™–––Ø¥–ÈÈ! …2jÔH*Õ0z_ô´éÓH$ûžžžFFF'RN$''Óh4iiiãqã¢cb&OžÂb±¨TêñãÇâã"##?~ü¨ªª:gÎ챓&YÇO±§®))©TêX—9óø •=¼r%ËËk!BèéÓgªªªºzúÕo^©©/º™2fÌôïÙUüǃõëÖÑh4¢Bmhh8wþQ½¨¸x¿?‰DâgHœX,Öþà „ŒTá‚ûDu‰DÔ_¡ªª*¢ `°UTÁ:U@àp__„§º§®®nÕÊ•úzÚZšîîn•••|Nˆ\átð`œ¹™)v;))IIqT§m-Mž——wàÀ~žbõ鼿'!ä ÃäŠwÇ…‘*ÀWÜ'ª  ¶¶¶®®uuµ;v„KIIÅ~;wŽKn^¾’’Ñ¡ ÈÕ@ìÚµ[EE…½Iåí›s^^$ÿu<=Ê èÓy5ÌOBÈ\þ׉ê¢^ ÝÊÈÈ(++=!sòäÉ!«‰­,-â~øaGx8Ñ¡ ÈÕ@ØOj``@t‚¨OçÕ0? !Wlx~ðâw \þ|¥««£«§Ï½@]þûåêϪªªØ›BHMMmŠ­mVVÖà§FøA®ÝÅÌL%ÅQÊÊ8===ììl±ÛÊʼ.ÔÓÕÑPW›5ËéÎÛìnÛCC ©wîÜvt˜©®¦fld¸ÿ§ë}AAßÅÆ666b—MÆãÛ3ê‡>WÃü$„\±uùŠ*ÀoÕo^qŸ«.PEUEE•Jål1¤¾xñ¼µµuó"ü Wø988âlijj¢qÀÖrtpt”••={ö,»[mmíÍìl„PYY©““#F‹;òÔi555×/¿|ðà»3Ö¹sWiYÙæ€€‘—.^DmÚ´ÙÏÏOZZº¨¨¸¨¨øê/¿"„ª^¾TR!x£}:¯†ùI¹bëë[ý`¢ #ÿüóOdd¤§§‡‰‰ •J}ýúu¯©¯o“—çl‘“—g±X < SXA®ðÓ××wppäl™9cº¾ž.ûÇÏ×!D¡PæÌ›‘‘ÎþÛú\FBÈÕuBhGX˜²²rzF†“““­­íáÃIFFF±1Ñì}Òé­û÷°´´TPPðñYbjjšy1!¤   #++""¢©¥¥©¥¥¦¦†B$™L! Ü/…>WÃü$„\±UTÁœ*ÀWOqLTçÝ_õòeVVÖøñ&ff¦wïÞC<~u€_ÜÁx ÿ–p© €ÝðððH9q"//ÏÎÎ!”–vÆÖÖVYY™N§ççç¯^½šB¡`=I$’ƒƒãá¤ÃìÈÉɳ7µ´µ«««{ @SS³æï÷I@Xü NTŸ`jšŸŸJNN¾{÷ž?Yäååh4g ­¡D"ÉÉÉñ(Há¹ssón'ª[[OÒÔÒ:›vÆÎή²²²¤¤$!1!D£ÑÚÚÚââââãØ™LƒÁ`oŠ‹‹sîJDD„ÑÞγgÀ+}:¯†ùI¹b#êf(ª_q_Q×DDú|iƒJ¥s¶”W”kk&!!1xq +^ ‘Hnnn‡÷EÇœM;#%%5{¶3BHVV–L&/_¾b¡—Ñ1òVŸÎ«a~B®0Ïž=#êÐPT~ã>Kñç/ Ö§õz,GG§+W®Üúüs„Л7oòrs}ýüx¡‚\ñˆ»»GlLÌåË—ÒÓÓGŒ¢P(666ùùyÁ!!¢}_ÔŠ"NikkãA°ƒ¯OçÕ0? !W!s33¿aŠ* pøPT± á+ª\,ˆOˆ_¾lY``¤¤dìw±22²«WËë…äj nfgW”—s¶88:ˆ‹SBzzzááÕÕÕîžìá³gÍš7oî’%KTUUi´Æ¢¢B“ÒëáÆR©---GþdjjF726®ªª²²´X³vmPPðŸË“'O&Û|¾~Æ-[¶b-övv¡›99Øfô¾}ÑÑûòoèë÷²À êí¼ÊÎÎþÊÓã`|<6y˜Ÿ„+„ªšz¨Ëœê7¯°y&Ýþ‹}é2/@Q_FªðUbbbçB‚ƒCB‚étú¤I“~üñ°²²2Ï#B« ÜÖ©¥ìQùèÑ£±Ûnî[·p®*„26÷Ûµë{÷î jh¨——W033óÃ7Òàäääí½8j÷îúúz•’‡¥ˆÅb0LÆ Lïe±X F‡Ù] F‡¹\L“Á`à|¥s?¯XL&ƒÁ`ÏJæ'!ä !daaÁ½¢êõjÉ@@Q@/”””": á¹ê??¿^+!_____ß®í‡'uûáá–ÆæìI&“cbccbcÙ-šZZµuoûwÏ :í*/ÿçf@À–€€-øwÈå¼úbÚ´NÇæ'!䪰°°×º ëÉ‹?ࡨ‡—ÿpÏ© D`¤ €ø8§J°¾gÀá©â(ªÀ0Âb±®_»†z\ù!”+¤‚‚¢¢¢©™Ñ¡0R@¼=joo߸i{sçΡ)S¦üðÃ<:"~‚‘*:à]QE&“;-vÇë#à'© (qô±#U÷…äý#Ut#UúæTÐUúFªèŠ*ýsªŒTÐŒTèøî?þS[[ÛÜÜLÔÑ%%%‰:4€ƒ‘*>173KIM%<†¼Übc"+ü WøA®ðƒ\u")% Ÿþ@€L±µ#:á—›¹Â r…ä ?ÈU·`¤ „**.^çïO"‘ˆ €Åbí?pÀÙeQX§ €ÿH$&“IÔÑ«ªªˆ:4€#vN,©‹8]ƒÉÉÉY±|¹éUe#Cêš5ßÖÔÔð?'B¡¦¦fÛ¶­NŽêjJŠ£ª^¾$:"Áuð`œ¹™)v;))IIqT§m-Mž——wàÀ~žb°ôõ¼ª««[µr¥¾ž¶–¦»»[ee%â+„Paa!÷Š Fª LlLLccã¢EÞšZZýõäPbbAAANN®´´4Ñ¡ œÏŸ_8ÞÌÌÜÊÊ*??Ÿèp„Ì®]»UTTØ›dQÞ¾9çååIJò÷_ÇÓ£ Š>Wmmm \]ëêjwì—’’Šý.vî—ܼ|%%%þDK,È"z¤ Š* pjª˜ØX}}}öæøqã—,ñ¹˜™ùõÂ…F%˜¬&N,¯xŒ:”˜EU_ÙOj``@t‚¨OçUFFFYYéù ™“'OÆkei÷Ã;ÂÃù+Ñ Wˆè9UpùºüÇYQ!„¬'MBUWWó)BEDÞOÙÅÌL%ÅQÊÊ8===ììl±ÛÊʼ.ÔÓÕÑPW›5ËéÎÛìnÛCC ©wîÜvt˜©®¦fld¸ÿ§ë}AAßÅÆ666b—MÆãÛ3ê‡>W¿\ýYUU«BjjjSlm³²²xšÀ\!¢GªàM*ª:¹]P€242â}ÀPæàà¸;*г¥©©‰Æ[×ÁÑQVVöìÙ³ìnµµµ7³³=<<Bee¥NNŽ4-î`üÉS§ÕÔÔ\¿üòÁƒìÎ4ZchHHäÎ]¥ee›vFF\ºx!´iÓf???ii颢⢢⫿üŠªzùRIqT„RTTTP©TÎCªá‹Ï[[[‰ I` Õ\;§ Š*0ŒÜÞ´ióÔ©SÇgcc³mÛ¶þùÿÃëëëöoâààÀ» Áp ¯¯ïààÈÙ2sÆt}=]öŸ¯/BˆB¡Ì™;7##ý‘Øs!WסaaÊÊÊéNNN¶¶¶‡'ÅÆD³÷I§·îßÀÒÒRAAÁÇg‰©©iæÅL„‚‚‚Œ¬¬ˆˆˆ¦––¦––ššB‘Hd2Y„,Ü¿êëääå9[ääåY,VCCQ! ¬¡š+˜S@xFŒú'!!áýû÷n ÜÔ4ÔŸ?–|<ùÞýû™.HIIõúØ?ú,^ü¡¹9ãÜy2™Ì£Á°w0^Cã¿? G*(`7<<<!SGG‡GááÌÜܼۉêÖÖ“4µ´Î¦±³³«¬¬,))IHLDÑh´¶¶¶¸¸¸øøvg&“Á`0Ø›ââ✻a´·óìyy9ÆÙBkh ‘HrrrD…$°†j®`¤ >ÑÑùŒsÓÜÂ!ô÷?sN_â³äÞÝ»gÓ3LLLxÝ!‘Hnnn‡÷EÇœM;#%%5{¶3BHVV–L&/_¾b¡—Ñ1 *•Z\\ÌÙR^Q®­ý™„„Q! ¬¡š+øôðmZúýû÷Bc¸LTooo_êç›››sòÔ)+++¾'äîîñáÇ˗/¥§§;;;1!D¡Plllòóóttt :³OŠ8¥­­ÇÀÑÑéÍ›7·°Í7oÞäåæ:Ír"6*Á4TsŸþ þTT ÑÑцTC;;[.EÕ¦M³²²ÜÝ=ÞÖ½½tñ"öSZúÿi|,ëS~ÊJB×®_»tñâ½»w‰ŽK8ÜÌÎfŸ`ØþéCXzzzáá¯^½r÷ðd?$<"âÉ“'óæÍÍÈH/(¸õóÏ?ïÜçpc©Ô–––£G*..Æ–l¨ªªRQ½sgäÀŸË“'OT”GïÙóßÇííììíþûÞßè}ûT”G?yòÏÞ¸ŸWÙÙÙ*Ê£32Ò±M× Œ–/[vêäÉÌ ¾úÊSFFvõêoþ¤„ä ýé?¸üN·ÅÍàjmmõ÷_×ÜÒr8)IDD„Ë ÿ!”œ|<9ù8»Ñ××7jÏ^^)t Æ7ß,aolÞŒš1cæÉS§ˆ JhnëÔRö¨|ôèÑØm7w­[8WBûíÚõ½{÷54ÔËË+˜™™ùùùá9œ“““·÷â¨Ý»ëëëUTTJ–"‹Á`0ƒðÍ›,‹Áè0»‹Áè0—‹Éb2 œ¯tîç‹Éd0ìOGЉ‰edœ  ¦Óé“&MúñÇÃÊÊÊRBr…ˆ©‚¢ ;mmôµþþ—9ò“–f/ß°–—‹?Q ¢¢¢µuo‰ŽBøøùùõZ ùúúúúúvm7008|8©Û‡ìï´46gO2™ËnÑÔÒ¬ÿ>ƒN»êô: ذçÞ¸ŸW_L›Öé^%%¥ÄC‡p;¤@®Ñsª ¨‡§#UmmmÖoø£¸øÐ¡CT*•£bøFªè€w…N{{ûæÍ›îܽŸobb 10RŸDq#{낆wõ×~û kÔÔÒ;–Êý„ŒTÐïJ” „Ò3ÒÓÿýü BÈÓÃcë¶Îs„#© ÞUéééݶÃu@`¤ € ÄÐ?Ä~÷,þ €!FªèFªôÌ© (ªôŒTÐUú‡Ø‘*˜S€!FªèFªôÌ©à?µµµÍÍÍD]RR’¨C8©às3³”ÔTÂcÈËÍ!6!¹Âr…ä ?ÈU'’RÒ0R€™bkGtÂ!/7r…ä ?È~«nÁH!TT\¼ÎߟD"‹ÅÚà€³Ë¢0Ä®¨E,$‰Éduôªª*¢ `àˆSK*Ã"÷À–ø,VRµæÛÕüɃÐÉÉÉY±|¹éUe#Cêš5ßÖÔÔ”€:x0ÎÜÌ»””¤¤8ªÓ¶–&OÈËË;p`?OÁ x^ƒuuu«V®4Ð×ÓÖÒtww«¬¬ä[x„«©©Ù¶m«“£ƒ†ºš’⨪—/»ö¹ví7ggm-M-Mӧݺu«§½ i& ¹WT0§ â]½zõÖ­[âââD"¸bcb-òÖÔÒúë¯'‡ rrr¥¥¥‰MìÚµ[EE…½Iåí›s^^$ÿu<=ÊàÂólkk[àêZWW»cG¸””Tìw±sç¸äæå+))ñ-N½xþüÂùóffæVVVùùù];?~lÓÆNNN‘;wŠ’EKËJßÖÕu»+áÍ$|ú€pª>lݺ=((èXWLl¬¾¾>{sü¸ñK–ø\ÌÌüzáB£öS§…àÂùÌÈÈ(++=!sòäÉ!«‰­,-â~øaGx8¿"%’ÕĉåB‡»UU/_®ZµO6„7“°¢:àå¿]»vª©«/ôòâg„gE…²ž4 !T]]MP8CÁÅÌL%ÅQÊÊ8===ììl±ÛÊʼ.ÔÓÕÑPW›5ËéÎÛìnÛCC ©wîÜvt˜©®¦fld¸ÿ§ë}AAßÅÆ666b—MÆãÛ3ê7œ¯Á_®þ¬ªªŠÕ!55µ)¶¶YYY¼P ˆˆpûžz2•Åbmظ!ÔëÔUáÍ$Ì© A+ªþøãcGEï‹&ðc‰ÂèvABÈÐȈè@‘ƒƒãî¨(Ζ¦¦&l \GGYYÙ³gϲ»ÕÖÖÞÌÎöðð@•••:99Òh´¸ƒñ'OVSSsýò˰;Óh¡!!‘;w•–•mØqéâE„ЦM›ýüü¤¥¥‹ŠŠ‹ŠŠ¯þò+B¨êåK%ÅQ9ÿ5XQQA¥R9[ ©†/^>‹OŸ>mhØãhJbbB]]í–-[yÒôñãGŸÅ‹?47gœ;O&“‰G8ÄŒ×Ðøïè‘ Ø ”'òòòìììBiiglmm•••étz~~þêÕ«) Ö“D"988N:ÌÞ‰œœg!¢¥­Íåj¬¦¦fÍßÿ î“ð,L&³©©))éÈ´éÓB666/«^Æýðýš5k†Òë¾û€xWT9;;;;;³7f9}ñÅ™™©TÃnûÿý÷ß{¢¢vïŽb04 k¤ÓÛh4š”””(?Ÿ%¤èôVoïE¥¥Ï_ÈÔÑÑ!:¡annÞíDukëIšZZgÓÎØÙÙUVV–””$$&"„h4Z[[[\\\||»3“É`0ìÍN”a´·óìðD__ƒòòrìnZC‰D’““ãSÄläÈ‘OŸ>bkËn±·³¿qýú«WUÚÚŸuê,¼™„9UCAALå2a³ªªª¥¥eÝ:öE™æææsç2ôõtssàû¶ºA§Ó—ø,¹w÷îé3i&&&D‡3H$77·Ë—/·´´œM;#%%5{¶3BHVV–L&/_¾âfNû'7/ÿVÁí^÷)Dúú¤R©+*8[Ê+ʵµ?“àWÈ‚kì§9RÿýÕÊd1B"¤n*áÍ$¬S@¼©Â´¶¶2Œ·oë'¡PÄ]]]{:âØ±c/d^älñpwûÜÆÆß̿½}©ŸonnΙ´4+++¢Ã:ÜÝ=bcb._¾”žžîììe†ÒÑÕ7n<± …›ÙÙååœ-Žâ℞žž……EDxxuuµ»‡'»CxDÄìY³æÍ›»dÉUUU­±¨¨Åd‡„ôz¸±TjKKËÑ£?™ššQÄÅŒ«ªª¬,-Ö¬]<ÀçòäÉ“É6Ÿ¯ß°=ÊÞÎ!tóß±¥è}û¢£÷åß*è´ GW½¾³³³¿òô8MÞw]° >!~ù²eA’’’±ßÅÊÈÈ®^ýퟑ°`±X—/]B•–•"„®]¿¦8JQYYy¢µ5Bhúô“'O^ç¿¶ºz«¦¦æ…óçóòòöEGc 1 ™LÂwÿЯ‹ªà÷MÿüS{úô™+V$:d:aO8Lþþ;B(9ùxròqv£¯¯oÔž½Ä%4·uj){T>zôhì¶›»ÇÖ-œë!„ŒÇývíúÞ½{‚ƒêååÌÌÌüð%899y{/ŽÚ½»¾¾^EE¥äa)b± “1ß¼Éb±Œ³»Œs¹˜,&ƒÁ”W:‹Éd0ì‹øbbbçB‚ƒCB‚étú¤I“~üñ°²²òÀ$ Æ7ß,aolÞŒš1cæÉS§B$)ùDÊÎȈè}ûh4šžž^ÜÁxly4„2 #Uð•Á˜OÓ§M›î2Çeÿwû;Šó±/^Â7.÷(/¿Ç/\øùùõZ ùúúúúúvm7008|8©Û‡ìï´ø5gO2™ËnÑÔÒª­{Û‡¸{f``ÐiW΀€-[ú·óN¯Á/¦Mët,%%¥ÄC‡ú·sa'**Êý?QFF&jÏÞnÿÎ2™„OÿЯGªØDEÉúzúWðíˆx Fªè€w%ƒÁà\Ž¥±±±¤¤dÌ(ª`h€‘*ødíZUUUªáØ#FT¿®>wá\SÓûeË—€Á#Ut鯮u IDATÀ»q#›É6?geee]ùøñ£¼œœ©™ÙÞ={a¤ †© Þ•8žžÿ~Ô…‡Àg0R@PåèbGªàkj0DÀHÀH€þ9UtE€þ‘*:€¢ @ÿûÝ0§ CŒTÐŒTè˜SÀjkk›››‰:º¤¤$Q‡0p0RÀ'æff)©©„Ç—›Cl Br…ä ?È~«N$¥¤a¤ 2ÅÖŽè„C^nä 'È~+ü WÝ‚‘*B¨¨¸x¿?‰D"*‹µÿÀg—9D` `Nÿ!‘HL&“¨£WUUuhGìœ*XRqz éÚµß\œµµ4µ45fLŸvëÖ-~&DXÜ»{×Ým±‘¡Šòh=]w·÷îÞ%:(uð`œ¹™)v;))IIqT§m-Mž——wàÀ~žbݹs{Ž‹‹¦†º¾žîÒ¥~ÕÕÕ\:×ÕÕ­Z¹Ò@_O[KÓÝÝ­²²’oq ‚ì7œgkkiª«©ÚÛÙ¥¦¤°ïª©©Ù¶m«“£ƒ†ºš’⨪—/¹ïJH3YXXȽ¢‚‘*ˆtüø±M7:99EîÜ)J--+}[WGtP‚èeÕK…‘kÖ¬UTR¬ý§öر£óçÏ»úË/ãÇ›šصk·ŠŠ {“,ÊÛ7ç¼¼Ü#IIþþëxz”AQXXøåüù– ‰Í--{÷ì™;Ç%ûfŽ””T×Îmmm \]ëêjwì—’’Šý.vî—ܼ|%%%þGÎwïÜùê+Ossóï¿ÿ"!qæô©uëüY,–×¢E¡ÏŸ_8ÞÌÌÜÊÊ*??Ÿû®„7“ðé?:¨uªª^¾ \µjõŽðp¢ct ¸-XàÆÞœÿå—LƧ¥¥AQ…‡ýÔ©DG!ˆöîÝ#//&- [ñÄÔÔt²ÍçGŽ$­]ëßµsFFFYYéù ™“'OFYMœhei÷ÃÃäõ{îÜ9„PêÉS ¡™3gZYY¦¥¥aE•ÕĉåB‡{-ª„7“ÄΩ‚Ë@àÔå¿Ô“©,kÃÆ!'{ £Q£F‰ŠŠAÒâbf¦’â¨Geeœžžvv¶ØíGee^ êéêh¨«ÍšåtçÎmv·í¡¡F†Ô;wn;:ÌTWS362Ü¿ÿÓõ¾  Àïbc±K&ãÇñíõCáï¿O™bË^Cn̘1:::—/]ê¶ó/WVUUÅꄚšÚ[Û¬¬,>ÅJ4²™L–––þ´)""+#K&‹°7ñïJx3‰¤ª¿¸ƒ¢ N}{ýܽsÇÈÈ(==}‚ÉxåÑJæf¦ ññýx¥ ?~üðáÃóçÏ6S(â^^‹ˆŽH988âlijj¢qÀ–Àupt”••={ö,»[mmíÍìl„PYY©““#F‹;òÔi555×/¿|ðà»3Ö¹sWiYÙæ€€‘—.^DmÚ´ÙÏÏOZZº¨¨¸¨¨øê/¿"„ª^¾TR!xãt:]œ"ÎÙB¡PÊËË»í\QQA¥R9[ ©†/^~üøñãÇ®ëëääå9[ääåY,VCC?b%š¡‘QÚÙô+W.›N01¤ŽÚ½;>!ÁÑѱ÷Gv!¼™„9UtÀ‡q 7²ïÝ»'..†X½ŽÉd655%%™6}:BÈÆÆæeÕ˸¾_³f ™LæuœÂ(z_4­‘VS]óÓO?yx¸§M·²²":(!w0^Cã¿? G*(`7<<>Ý™Éd`F\¼ÃU3F{;Ïž¯|½pauuõþýßÅÇDMŸ>ÃeΜ›ÙÙââ”®åååh4g ­¡D"ÉÉÉñ)\B…ï““—?‘’"**вµµ­®®Þ¶m뜹sû4¡ s&a*øêÀʪª®®®x:ý4«à¿Â‹Éb"„DHðÚé…˜˜˜!ÕðåËD"ÜH$’››ÛåË—[ZZΦ‘’’š=Û!$++K&“—/_q3'‡ý“›—«àv¯û:7múóÉ_¹¹y%KO>ý¸¢ÂÚzR·=©Tê㊠ΖòŠrmíÏ$$$ø)Áž-ÏûæÍ›¼Ü\§YN|Œ”Hêêꥥ¥œÿ³………ÒÒÒ²²²}Ý•ðfæTÐï.ÿ1Œ°Ðí_}åi0f vVo‡›>}ÆäÉ“×ù¯­®Þª©©yáüù¼¼¼}ÑÑ}K¼.ÔÐÐo2^RR²êeUJjÊû÷7m":.áp3;»¢ã'Ú°+\zzzááÕÕÕîžìá³gÍš7oî’%KTUUi´Æ¢¢B“Ìþ›¡gc©Ô–––£G255£ˆ‹WUUYYZ¬Y»6((x€ÏåÉ“'“m>_¿aÖ-Ÿ>bog‡º™“ƒmFïÛ½/ÿV¾¾~¯{+-}xìè1SSSQ1±û÷î:uÒÛ{16Ç!”ý•§ÇÁøxlò¾ë‚ñ ñË—- ’””Œý.VFFvõêoøŒ„ÅÒeË–-]êéáîã³DL\üÒÅÌì7ÖoØ€Me±XØR¥e¥¡kׯ)ŽRTVVžhm†P&aNð®¨JNN~ûîíªU«8ÑËœ*‰”|"egdDô¾}4MOO/î`<övÐÉ´éÓ3ÒϦ§Ÿmii9r¤µµuRÒ333¢ãÛ:µ”=*=z4vÛÍÝcë–ÎuƒBÆÆã~»v}ïÞ=AA õòò fff~~~xçäääí½8j÷îúúz•’‡¥ˆÅb0LÆ ¬+Æb±Œ³»Œs¹˜,&ƒÁÀùJ—‘‘©¬¬ÌÈH§ÓÛô ôwîÚåãóß0‹Éd0ìOGЉ‰edœ  ¦Óé“&MúñÇÃÊÊÊRBaþü/Iˆ”¿jÕJ&“©££¹lùrì^ƒñÍ7ÿ¥.`óf„ÐŒ3Ož:…†P&a¤ ~¨­­;xðà¶mÛ˜Læû÷ï±Æ6zûû÷ïGŒ!Úó·‚ÈÈÈDíÙµg/¿"V~~~8£NxòæëëëëëÛµÝÀÀàðá¤n²#<¼Óâל=ÉdrLllLl,»ESK«¶îmâî™A§]ååwøºÌ€€-[pîM[û3.ŸuøbÚ´NÇRRRJýŽ }ú/ûÆ gçÙÚZšêjªövv©))|Nˆ0Zâ³XIqÔšoWˆ€:x0ÎÜÌ»””¤¤8ªÓ¶–&OÈËË;p`?O1Xjjj¶mÛêäè ¡®¦¤8ªêåË®}®]ûÍÅÙY[KSKScÆôi·nÝêiouuu«V®4Ð×ÓÖÒtww«¬¬äeìü–““³bùrÓ &ª*ÊF†Ô5k¾­©©á¼·Ói6vŒA§= ™L2¸ÂÓ‡³'~0R7wïÜùê+Ossóï¿ÿ"!qæô©uëüY,–×¢ED‡&¸®^½zëÖ-qqq¢&»víVQQao’Eyû朗—{$)ÉßO2(^<~áüy33s++«üüü®Ž?¶iãF''§È;EÉ¢¥e¥oëêºÝU[[ÛW׺ºÚ;Â¥¤¤b¿‹;Ç%7/_II‰ÇO‚Obcb-òÖÔÒúë¯'‡ rrr¥¥¥Ù}"""ÕÕÕ±Ûâ”/Ò¡”É~ŒTikk¿xñb€Ç…¢ Z/êܹs¡Ô“§B3gδ²²LKKƒ¢ª'>|غ% 4t{PP ѱû©S9?û ج&N,¯xŒ:”˜Øµ¨ªzù2(0pÕªÕ;ÂÃ{ÝUFFFYYéù ™“'OÆölei÷Ãx+bbcõõõÙ›ãÇ_²Äçbfæ× ²íìí »>v(eòÙ³gxºqUººº!mmí§OŸäÐPT#PE•Y„L&³ÿΑ•‘%“áºyvíÚ©¦®¾ÐË Šª»˜™éëûMNN®‘±1»ÑÓÓ£ºº:''!ô¨¬l×®]·o´¶¶šL˜:iÒÿ°nÛCCÏžMûéèѰíÛ>,•——[ºlùºuëBAA?:„RR…RUU-yXJÀÓÃGD„ÛË-õd*‹ÅÚ°q#BˆÉdrïüËÕŸUUU±:!¤¦¦6ÅÖ6++K@J㬨BÖ“&!„ª««;uknn1bD§ ™Lâ_š‡ûZVý¿€À¨9UÞ‹¼)Jà¶m¯_¿®««;x0®¢¢|ùŠ•üO‹Pøã?Ž=½/šÀp ÇÝQQœ-MMM4Ø¸ŽŽ²²²gÏžew«­­½™íáá*++urr¤ÑhqãOž:­¦¦æúå—<`w¦ÑCCB"wî*-+Û°32âÒÅ‹¡M›6ûùùIKK_ýåW„PÕË—JŠ£"à—bŸÜ½sÇÈÈ(==}‚ÉxåÑJæf¦ ññ=ýaVQQÑiá"Cªá‹Ï[[[ù,¿Ý.(@q6ÎrrÔÖÒÔÖÒôYìýüù#:C&“EÅÅ8{b³¦ºŽÀœ*zWPpû›o–p¶((Èß¾}‡ËC ŒÒΦ{/ò:vì(BHBB">!ÁÑÑ‘· 'ƒ±aýúo|¿áVÝÒ××ï4¨0sÆtÎÍ3fž>KRSR2/fºÌ™£   #++""¢©¥õß!I$2™,"l£°555555±1Ñ¡ÛÃ444.œ?ÒJ§ccrÔ×7Œ?ž³EN^žÅb544(+ã]2[XÔ×ׇ…m?ÞÄÁÁk‘••]µjõDkk))©’’?|ÿ½“£ãÍœ\ì¹¥Löº\¶ú÷µ¬úwh(ª€ÀáÝå?b!„¶nÙªªúiF°˜¸8÷Ã=xðÀÓÃÝÊj¢¯Ÿ¯¸8%óÂ…Õ«V‘iÞüù< Rx%&&ÔÕÕnÙ²•è@„RÜÁx öæH솇‡Gʉyyyvvv¡´´3¶¶¶ÊÊÊt:=??õêÕXE…"‘Hއ“³w"''ÇYàjikw½Ħ©©Yó÷?ƒû¤ø€Éd655%%™6}:BÈÆÆæeÕ˸¾_³f ™L&::Â|üøÑgñâÍÍçγó`fffff†Ý¶··Ÿ}útŠ­-»ÅÞÎþÆõë¯^UikÖ©³¼¼ûäÄÐH$’œœBå:½ÕÛ{QiéÃó2uttzêfnn®««[\\„m±LÂ:UðOKK ÎÒíÉ“'ÆFFœ5ÁÓ uuuÞP@UUUKK˺uþúzºØOssó¹súzº¹9ðÝdýG"‘ÜÜÜ._¾ÜÒÒr6팔”ÔìÙÎ!YYY2™¼|ùŠ›99ìŸÜ¼ü[·‰™¯Æ~šÙóßË™Éb"„DHÝüv£R©+*8[Ê+ʵµ?“àiüD§Ó—ø,¹w÷îé3i&&&Ü;··3ØÓ‡X&‰Z§ Š* px=ý믿¶°°°°°X»vÍË—/¸OTWWW/--mkkc·JKKËÊÊò/#Â`ìØ±2/rþP(”©_|q!ó¢™y¾ŽtåîîñáÇ˗/¥§§;;;1!D¡Plllòóóttt :³OŠ8…ó¬^³gÏF]¿~ƒÝrýÚ5EEEuŽk©lŽŽNoÞ¼)(ø´ å›7oòrsf9ñ'T>hoo_êç›››sòÔ)++«®÷rnæææ¾xñÜÂÒÛb™ÄJ"îÀâŸ`xáÝå?iiiŸÅ>¦æf’#$ËË9räë¯fddpY°né²eË–.õôp÷ñY"&.~ébföë7lª“ úMFFÆÆÆ†³…L&+Ý©ôäfvvEy9g‹ƒ£ƒ¸8!¤§§gaa^]]íîáÉî1{Ö¬yóæ.Y²DUU•Fk,**d1YÁ!!½n,•ÚÒÒrôèO¦¦fqq#c㪪*+K‹5k×áúN4.žé‰ÒÑÕ7nÅî¹–U_AQïŠ*cccãçíþ¬&.\øuʉ”uë{\Wzþü/Iˆ”¿jÕJ&“©££¹ìß·!K`à¶N-eÊGÝvs÷غ%€se „±ñ¸ß®]ß»wOP`PCC½¼¼‚™™™ŸŸžÃ999y{/ŽÚ½»¾¾^EE¥äa)b± “1ß¼Éb±:ý•Ï`t#a²˜ ç+Á`p~h7`ófôï§#B$)ùDÊÎȈè}ûh4šžž^ÜÁxlÉ „‹Éd0ì¯ËÈ8L§Ó'Mšôã‡áÓjƒ¥ð÷ßBÉÉÇ““³}}}£öìEýïóÿË8—‘‘ÞÚÚª¬¬ìîî±eëÖQ£±nC,“ìÓïÏ?ÿdßþùçŸX;þµ¬°ÎøK S±òyü¦±™Î@mŸ§ØS×””T*u¬Ëœyø÷BèQÙÃ+W²¼¼vjWS_t3;d̘1èß³«øë×­ûðáßbsvvVQQNJ:‚mÖÕÕ;>4t;ßjy¹9Sl툎B8@®ðƒ\á¹ê*<|GHpð»wï8©TjÇ$°ƒññ}ÚçR?ßžîÚq¡!$I«*SsÿŒTÃÃOÿuÁ`0"ñóˆx­Ót¨²²²N-½®eņ-j…U@àð®ÄiooçüßÝ»w_½zåèäE %ƒµ\B_§^AQ†‘uëÖ+mhlH¡HTTTœMKSQQñZØùº$¡FÔ:UPTûq#s ³«?ÿ’õsNWTTtvv^µj•¼¼ŒTÀPEŸð®ÄñYìã³Ø‡o‡@üE•¶¶v×»^¼xÑ¿ãÂâŸ@àðtñOîˆ~êÄÜÌ çÔrlá§OŸvjÇÖ²‚Å?0¬YX˜#„ð,CÅ}-«þ¢ ¥~¾999EÅÅx:c ²áìÜ'áá;}ŸCUöÍ›D‡ 4 Wø c®ÌÍÌTÕÔ-,, ùö¯¤”4BˆŸGø¿Õo^ñâ}ÞÜÌ «¨,,̱=9œtu¼DXQQ­e5À‰VPT‚½Òp.‚ ðâ_kx'"22ÔeNõ›WXÝÿöô/ïÞç±ÎÜË)N½®eÕPT‚ÿ•ƹvÈÐø^X€ðzõêB¨úÍ+U5 ø—û¿EIGúñ>‡×Â…)©©ý.ªU@àôõDg\\»v !4}úô~ÜÂë¡øËU_ßçyQAQ†…®'º††ö‡`· ¨8Íž=»¥¥¥·èáõŠPü‹åª¯ïóìþÜ»õ U`Xèt¢c‹ˆp9û¡¨xØÛÛ³¿«»¯·ÀƒðzE(þÅrÕ×÷yλ444ú½ŽTOû,PTÃy¢ëêêvmäÒˆBx½"ÿb¹àû¼¶¶v×õ¥ðÃÖ²Âóe)©©Ø'q‚¢ ö‹‡½p‚‘*0`·nÝBÙØØôã6x^¯Å¿X®úú>ßõ.]]Ý?ÿü³ÿSø×²b/Ó€),,LÅÊçñ›Æf:!´}žbO]SRR©Ô±.sæáß;¡Ge¯\Éòòêæo‚>­Vò¦ºº¦¦f°£À+***–}]ÎJMU™×kY –êB’ѱª25÷ÁH ¬JC[Ddd?–³º|颀¬eÕWPTÂÀªT0„õ{ù®þývÀ£¯kYõU€`°*>’Oœ@y/ZÔÛ©A™Ìއ |h Š*@0X• +KýüØk_õõ6Âhà“ÙÙø¿–U_AQ«Ráã믾b¯}Õ×Û©Á©"d-«¾‚¢  V¥€!lPFªø¿–Uÿ@Q«Ráãlz:BÈmÁ‚~Ü@H |¤Šµ¬úŠ*@0öË ¢¢‚J¥vjì Š* Ô–úù½{÷®·F©ÂùÛ¡ë]|¾¸E ç_VVfllŒ ¨CÔüyóêêêúw!5(sªðüvètWYYÿ§‹@QÖé¤/))111¢ ††Áúô_¯¿8ï*))!d.U€`]Ïûââb˜¨†¤ËW® „œgÏîÇm„Ô ®S…ó·÷n<E ,þ †•¥~~¯_¿îßm„Ñ ®SÅ üÉ E U`øptp¨ªªêßm„¬¨Þ=]=ÝŠŠÇzzy ’®\ÉÒÕÓíÚnnféµp!þ]õõkž«#U¼ûí’šjnfÖß§Ò»>UŸÿï¡+W²x štõt±“§ì+-SRSñìÄÜÌìMu5ÎÎŠŠŠªæ´%Å IDATšFaa¡……þ-B}ùíЧμû6e„),,LÅÊçñ›Æf:!´}ž"ï0dì¸P‡’¤ˆŽU•©¹L„èx†(ªèÝÓ§Ïž>}›°9 7øAQ.ªªªìϤÀ&l«MN°¤½ÓÕÕáü›°9|6øÁDu€þè4Q½#Uœ»¼yóFKK+888,,lýúõ ÕÕÕ Ø½‘‘‘QQQMMMnw²iÓ¦¸¸8ƒ~üøQGG‡³ÿ•+W\\\¼¼¼|}}ß¾}R__onn~ùòeìá‡ÒÒÒZµj•AFFÆ?þxöìÙ ¼{÷nûöíÇ/))A‰ŠŠjhhŒ7NLL,,,LEEåï¿ÿ¾víÚöíÛG…'ªØØØ˜››oܸQZZúǼpáBAAöÁÏžöÌýQ]¤ªªª¢¢²lÙ²½{÷bÁüý÷ßêêê{öìÙ¸q#÷½eee9;;»»»¯X±¢¶¶600ðÇì\SýæUaaQQqq¯=ûºC?/ÿ¥¤¤0 OOO„§§çþýûÏœ9³bÅŠ¾î§µµ511ÑÆÆ¦Û{ÃÃí­­“““±ÍqãÆQ©TÎMMMßÿý´iÓB3gÎÌÉÉÁŠª‘#GÊÉɉˆˆ|öÙgXÏæææ²²²£GÎ;k™3gþ¨TUUýõW „Ð_|1qâÄÈÈÈóçÏsÙ3—Gõt 77·“'OFEE‰ˆˆ „N:…úúë¯{ÝÛŽ;LMMO:E"‘Bf¼\ß RXEÜkψÈHô慠xôs¢úñãÇMLL°ÇÚÚZGGçøñãýؤ¤äçŸÞí]---¿ÿþû¼yóØ-cÆŒ122âì#--ýÅ_°7Ç×Ó—dIJJíØ±#..îáÇ, Tt:=;;ÛÍÍ «fB$ÉÅÅ%??ŸËž¹?ª§§ïííýúõë7n`›'Nœ˜6mšªª*÷½a¹ruuÅ**„©©é˜1c¸ûsù”Ú¹sçÎ;9Û“““###û±Ãna×ïêêê8ß¾}+--Ý¿jkk;v !T^^~äÈ‘­[·ª©©-Z´O$d2yݺu¾¾¾ø÷ìææÆýQÝ"‘H^^^û÷ïOHH8qâ„´´ôüùó{ËFãl¤ÑhýÎ0´ñâ ˜û;`ê4(¥¡¡AÀHÕ•+WÞ¾}cooÏÙ¾|ùò•+WÞ¼ysêÔ©}ÝgOBCC]\\/^ÌþôŸ²²26ƒ»WÆÆÆÍÍÍ ––– ENNî›o¾qss£R©íííééé---Ó§OÇILLÌäÉ“§NºråJuuõ†††{÷î1™ÌÝ»w¿xñ¢§=sy—c3ÆÚÚzëÖ­¯_¿Æ®ýõBhûöíÎÎÎß}÷ݪU«jkk}||( û±¿þúë¬Y³’““±9ïÀ0ÇY3éêê"„´µµŸ>}:}öy¤êøñã222nnnÚ¿úê«#FôoºzOfÏž}êÔ©»wïΘ1#(((,,LOOONNÏcçλlÙ²kkëY³fÉËËkhhÄÄÄÌœ9ÓÅÅ¥°°ðÌ™33fÌÀÉ„ îß¿¯®®¾nݺiÓ¦-]ºô?þ°µµEqÙ3—Gq·hѢׯ_«««sV¨Ü÷6kÖ¬Ó§O:tHVVÖÞÞ~Á‚“&Mb?–Éd2 &“‰óùC{8 «¨0ººº©êÃ⟄¯±ûîÝ;--­ˆˆˆõë× „TxøŽààwïÞ!„º“SQQÝ Ñh)©©KýzœÝÿÅ?ù¯©©)44tÚ´iŠŠŠ/^¼Ø³g””ç1€~À¢º«Ýï9ì]T‰ŠŠVVV¦¤¤¼}ûVNNÎÞÞþäÉ“]×@è^|úO ‹* ø– ºaWTðUƒ@ ÿjÏž=ãÅna¤ ȹ™YJj*þÎø÷ Enïß¿¯©©!‘Húúú>|':(ÃQÿ‹ª»wïÆÆÆæååÕÕÕÉÊÊZYYùùù¹ººbpl7nܸwïÞÖ­[Ù-‘‘‘QQQMMM¼>ЦM›RRRjjjëÙÙÙ111wîÜyÿþ½†††‹‹K``àèÑ£kÿBó¿¯kÎñÛ¶m[ttt{{»²²rMM¶¶öŠ+°oõæÑ@؇ãìiaa޳s?çTÅÅÅ}þùçeeeëÖ­KIIÙ½{·œœœ‡‡V@¸qãFTTgËèÑ£ŒŒøp ÁûÅ_ÔÕÕEFFž}-´«««··÷ĉ=<<ŠŠŠp~W4~Ì\°\]]] …òôéSÑÍù–Ø[>000)))55•Åb!„rrrX,Ö¼yó„æ/Qǵ´´Ü½{wïÞ½XâéÓ§“‰'Øæ$óÔÑÑÑÕÕEéëëãJ¥R…–B¾›| >°çT)**Ž?ÉårËÊÊ"""°O5„…Bqwwÿæ›oð‰‰‰555ø²ÄƒÌÌÌÈ7`ÀlF…100øý÷ßB­­­7oÞܲe þÖÈ‘#%½Ã]YYÙÙÙijjŠe.6(‰y´+™ùZ!´dÉ’M›6åçç/]º”Ëåæååùøø()) -B¢ŽSTT=zôÆ›››MMM)Š„q ©€ló”m7øÐ½»çTI|O•ªªªŠŠŠØ)‹Åâý,lhhèèèØºu«¸¸¸ºº:,APPPJJÊâÅ‹KJJnß¾]ZZŠÂ.?‘Áw?µœœv)´±±±»»[MM÷]¾—b)))ñÆB£Ñ°ÌÅÅKEE…Åb m·§OŸöôô`ë.ÄÈ”È×ò! Y³fegg#„ŠŠŠ^¼x±téRQEHÚq§NrttÜ´i“¹¹¹¶¶vRRROOØ@ˆÉ6O‰º ÀGͶ&9U²¶²"ÿ<$ÝJ•³³óÙ³gëëë‰o«â¥ªªJ¥RCCC-Z$4A~~~DDDHHöòÎ;RTLh¹rrrø=º:ü&÷^fN/ …âìì|îÜ9Áv+..F¹¸¸`/i4ߟ\JT"¯eË–999]»v-;;ÛÎÎŽäú™õôôòòòB÷ïßÏÍÍ]½zµ¶¶ö‚ ¢KTž$w—4Ÿ”[·*H>ª*.>!ônŸSÙÞÞÈwc|ee%ß·Ìp NNNeeeFFFÆÿ†êîîær¹***xú¢¢"ÞÝ †t7”1™L‡óÝwßá[>|x÷î]Qé%*ˆ8(AmmmÁÁÁ¼³'OžÄÇÇ›™™á·”éèè<}ú´»»{ÙØØˆO1%-çèè8f̘ÈÈÈK—.,Sõ&F“””&“ùË/¿GÁ‡ Íùò”ŽÔà£DþáŸ>óçWܾM>giVªìììÒÓÓCBB¬¬¬|}}õõõ_½zUZZZPP€-º•ššêàà0qâÄeË–éèè¼zõêúõëÝÝÝ rrr...»víòððÐ××/,,Ä®UáÆŒÓÒÒ’™™Éáp ïíSb­_¿ÞÝÝÝÏÏoÑ¢Euuu111¢ž%iAA &vppHLLŒŠŠzúôéÂ… ÕÔÔ*++ÓÓÓétúÑ£Gñ*yzz&$$ÄÆÆ®\¹òùóç!!!¼_x”¨D^Ë–- 8p ——qJò1ÖÔÔ,\¸pöìÙÆÆÆ………­­­Ø’q¼øÚ\UUUTžR#n´sçιººîß¿ßÛÛ[ìK2·2KJÊÕW¬Xacc“–––––öòåKUUÕ±cÇM›6MÔ.7n܈ Å.ÙØØcïîÝ»700ÐÜÜœN§ÛÛÛ:88àûzxxÆÄÄÔ××kkk×ÖÖ’¯ê´iÓ:´aÆÇnÚ´)==]UUUhbI "JPdd$›ÍNMM]µjU}}=BÈÒÒòܹsêêêx++«¼¼¼ØØØÄÄD##£˜˜Þ g’–ˆ›={vpp°ŸŸ~¿6I%0`èС©©©555T*ÕÔÔôÈ‘#“'O/¾6¿{÷®¨<ù°X,6›­¨¨ˆo±´´ÔÑÑ‘(„PwwwWW¾¨FüÀÇá]Lª(±±±š6þþhjáv!„6Ìy·–öPQo}@êëëuuuãââÂÂÂúº.(00077·¨¨Hð©˜2·oß>ÿû÷ïÃe/Ÿ²M›6ÆDG·¶¶ònÔÓÓÃ~¨ƒ×_ýu0?Éb‘·änüî%BH‘A¥ÅúëFÞ»z¤BÿÑÜܼ~ýúI“&©©©ÕÔÔ$%%)))ùúúöu½B(##£¦¦fþüù—.]úˆs™¸ÿþ£GbbbÜÝÝaF ¯TaO‘ÔÓÓ{üøqoòüø'U4íáǬ««SUUurrúöÛokÞ'h4ÚÙ³gßu)+V¬øá‡lmm322ÞuYÀŸT1ß8|øðêêj©óüø'U Ÿøïì–””ôu€þ›T ^À1bDUU•ty~ü“*>ؤJèS–¤¾‡&Uøäô£G*|¸`R 0©w1©’æ·ÿ0eeennnjjj ÃÐÐ044ôï¿ÿæM°cÇ …Â{ }EEÅàÁƒ-,,^¼xm ¦P(R×@ROžnܸøøxuuõÊÊÊo¾ù¦   ´´tÔ¨QBw¹víÚ_|addtöìÙAƒõ¢ÎR²¶²:˜ŸO>1ùœ¥Y©*//_µjÕìÙ³ËËË—.]úå—_nܸñÆííí^^^B%íòåË“'O=zô… `F€¾Rqûö;J,ÍJÕÖ­[étzff¦œÜÛ9™¡¡atttXXØéÓ§ù~V¹´´ÔÝÝ}ܸqÇWVV–¢DY‰‰Ž&™2.>ž|¶¯Tõôô”––::: þÒ ö«À¥¥¥¼Ïœ93mÚ´Ï>ûìäÉ“0£À‡BÒ[¯$^©jjjjjj200|KWW—B¡<{öŒwcXX˜¡¡aqq1ƒÁÜ…N§ËËËKZ€Þè_ßþ#iÚ´i=JHHú.‹ÅRQQy×uàÕE‚¤yJ¼R¥¢¢Âb±„.ˆ=}ú´§§gذa¼·nݪ­­½qãF&“Å·Ë!C†*iz›3ééé ¾USS#]ž¯TQ(ggç~ø¡¾¾žï­ââb„³³3_úììl__ßÕ«W§§§óíôÓO?IZ€ÞÀÖ¢?~Ì·ýñãÇR¯TIsù/""¢­­-((ˆ÷é ?Ž733›:u*_z …²gÏ//¯°°°¬¬,)J!|æT]]o¬®®~¯—ÿB‰‰‰QQQ555þþþjjjØÃ?étú‘#GxŸ³€£R©ùùù\.wùòå # Û²sçÎÎÎN)ª Þ9SUU•±±qUUU/ï^—ò‰ê‘‘‘l6;55uÍš5ÍÍÍ:::>>>kÖ¬ÑÔÔYväÈ‘™3g.^¼˜Á`x{{£ÿÍ¥¬;€Tø¦wïÞíý„DúTž4iÒ¤I“ón¡Óé'OžäݲcÇŽ;vH])¼‹5é'U(˜TÈLªdàƒ|¢:@ÿameEòg’æç[[Y‘ÏVªð a³­BóóŦ´¶²Â“$Á¤êÏ?jÉ'èŸØlk‰fK$Áå?€I€ À¤ @`R 0©˜TÈLªd&U2“*€I€ Àoÿõ[·**nßîëZ@âßø&U}£âöí%‹õu-€víΕɤ .ÿÈLªd&U2“ª~áÅË—–·+n§¦¤Nœ8±¯«ó^1„Б£G¿øbª¥¥•ýx{±;æäì7ÎûûÚõë¹¹¹ä •¯Ì¡C‡ÍÍ-°l6ÇÃcÆÞ½{»ººòliiÁwü7kÖ,Q;÷o˜½ÁÛÎÒ‘´‘ßs\’VO bÏÞ7òûÔç] “.#oÊ«W¯®^½fʔϭ­Ù'NŒ‰‰yñâ…¨‰ûýêÕ«|'û„ Ž’F!JoF$‚-O²e{̈0?¸¨%:®¤7ª÷ ee¥´°´(--íëºü ^±çÏŸ'lI˜3Çkš›ƒ./Q&ׯ];|èð¢EdoÌ•¯LÕƒ¡è˜uímí§OŸÞ¶-½±±)44DTž #55»QOO7 {©¬Ì’("™ÃC“:IùýÀãÚ¹sgŸW¯÷ü!’º drD 4ÐÐp¸¤{åäìzýºùË/¿ÔÖÖ®©ùíàÁü›·n())IWU«"455±¿é_z3"lyéšQjX\k׬¡ÈQ°-æµÌ+¡`RÕ/”––9:9ÊÉõ»…C¼bµµµÝÝÝ3fÌ011éóÊ`/m86¡É“'Ï›7ïðáÃ+VS©T¡ûR©ÔÉ“'ã/·lÙ¢®®Î»¥oõÛ —ÈÇÕÑÁ•—§÷“Ê|Lú*j¬C======%Ýwýú}}}ü¥ñ(ã•ááçÏŸ1s†t•±µµ1ˆLÊW TÅ"õ¿¬ÞŒHB‰: ¤kF©aqMù| ¾…`Àü€¢–ùq%Ô§5¾ôOÍÍon\¿áìüv»ºº: `!‡cãìì¼{÷¿VDËË\°À—ñ?~üŠàÿ\±"Ä~¼=‡c³Àׯ¢¢BhÅb¢c"„æÌ™knn‘”œŒU2,,l’ó$kk¶“ÓĘ˜˜W ‚Ñ%%'ïÚµûus3¶Jìâ2™¸hQé[ G¡PÌÍÌ[ZZËÊ.š›[Ü»wŸ7Á’%sçÎÕþdù¹²r¾÷|6ÛfÊ”Ï÷ï? 2+Ñíù¬öYHHˆ½½ƒ••µ“ÓÄåAAoÞ¼ ëÄËW.{zzñGšÐFë?q ­éµk׿{ÏçplR¶¦ „Ö¬YË×S . ùç?¾Ø.>ó}Øl!g¯â­Ù»víld‚ˆZ[[§O÷ðööîèìĶ”—ÿhaayèð±M*é HÐ×ï§ Z[Ôi(vüáëPÞK3$G „ï'BÈÊÚ!ôüï¿E5"31­­­===9`~¾Séää¼2<¼¬ì"~ ErD"sÌð6šÐ–ç»ÂUýðahhègŸM°¶fOsuËÌÌÞ&½Ž$Òÿ£–⸒¬Tõ½+W.ËËËÛÙÚa/[[Û"##½æÌ]²dIIIÉöíÛõôt±ÿ"üøãÕ   {‡íÛ··¶¶fdìôõõ-(8ª­­ï»ví:ooﯖ,9|øHdT”÷ÏóªîWEDDp¹í))©«#Wç›%~øðÁ‚~&&&ñ›7+*2 ‹–, XM !4D]][çŸÔá3‡Ï°¿-­¬ŒMfÍšU]]ÍwŒªªª*++ËQ(øŽÄE M/ØJ¡¶¶¶–––öö¶S'O_ù¿+':aïzzzîÙ³'""»R^XP¨¤¤8Õuª¨öHkkkØÊ0—IX‚ºººœœ]sçÍ“§ýëô!ª­­í×_ÅÅÅMœè„%vrz{·,_hkÖ¬9rBÈÕÕõîÝ{xq¢Bc2™‚Öâ¢ÓéBû´£ƒmie%ªkutp7ÆÆŽ9!ä5ÛëØág¦§§')9¹¨°(--MT#ùÆÆ&¡¡!©©i¶¶ãòöí““£nÚ¸‘d“JÉŽW šIDATt÷õûéQ„ž†bÇâ%9bðillLKÛfllâè8AT‚1SY™åçëkie¥¨¨xÿþ½={ö.ðñ-(,PWSš“Éô˜áá1ÃãÅ‹'O:yâÔá#GtuuÝÝÝݦMÓªƒ§$9"‰Z°Ñˆ{*-5UMMmÏÞ= !dm-üy•²ŽÄúP¢æKI渒LªúXGGÇÿ]¹²víZ|‹¢¢âرcñ—F#Œ°õI.—ûËÝ»KhÿpÕÕÕmÆÚܼu“w_l@G±X,555[[[ürõðá¡¿_¼ÐÒÒêèè¸qý†Ÿ¿~òP(GG§oU1Ášçæî9þÜüÉåþó¿œß~ûMìÄ_lÑdZ !4gÎÛ+Ÿ9|»aö÷—_~™•uòÔI¯Ù^ßw›æÆd2¥DžFsät&Oq9þý÷~­666áÍ„ (CÃá™Y™---ÛÈȈB¡ˆ M^žÎ;dŒ·³;pàVœD¡õ·¸)((XXZ$Äb±°FGg(ïê}WwOTÔêòòòœìl+ë·ã5oeÈ~>>>?^½´¢³³#''{ÀÀx>ÄMJþĶô5oåß]Gfü!îP)FŒööö°°•-­­»våܦ#jÌD™šŽ15ƒýmggk3v¬Ï|Ÿüƒù¡¡!]]]---ø^|÷Q©««ûûùùûù=zôøäÉÇŽËÈÈðš=›A’‘ÄF-ÑYÐÑÁ½UQ±pa6·ìý3JÔ¼HWÒIU»~íz[[»£ãÛÏ6&S?ÄBT*µ»³!ÔÔôº»»{à Á¼»8¨êþ}Þ}yߥR©¼Ÿ¸*!ÔÝÙ…åÖÑÙ™—·oß¾··qôôtã_ˆ¬ŸÍ[¶œ=s68(ÈÜÒBIQéuSã_¿övñqÄ-Hhe´4µhtù¡:Ú·<ÈÙeRÁÑ"¯Ù^%%% õ³½fÔGl J,ïÊÁ€ƒBMµsçÎ̬̌¬ì††z5µÁ>>> E04%e%ÞÛŠYª*xq…Öß⤤¤È{¨“Á÷Õ-Š¥›çÆ—–7o.]ºÄæ°ÍÌÿu½†·2d? …âîævåò•Q£Œ±õŒØ&%þÓ¢û÷N»€<2ãq‡J:bttpCBCTUíÎÝ=l˜È vHô˜)ÈÌÔTWW÷î/¿ „~¹{wÏÛ+­••? Ý¥ùuSóëæ–ÖVÞÞ$?"‰? %9 ššš»ººÔÔÔÅ%{ÇÌ5†üqE†–öP„^ònIU+--c³Ùd¾l¢¢Â’““k¨¯ãÝX×P¯ªª*E¹,–2•Jõ™?¿éDÒŠOÛ(j$ëãJK{è‰ï#¤Ë»¾ý×—zzz.^,svv&“˜N§›™šž?Ÿw¿xùòÆõ6)ЦÓéçúëÆ 3ø72ëîîîììTR~;þ–””ˆ.‹Áû=â¢ÓKÔJ‡cdd¸-mÛÍ›7‰—©ÈÒÑÙyñâEüåÙ³çUUU þ5›nøpƒðˆpƒñ믿 ­³³óÂ… øË3gÎð'*4¾FëoqñUOu!þõWww7öòusóC ‡c6›™‘Qþå‘‘‘X‰|•QOOOtô::]~×®Ÿ´méÕ" y’ˆûšd…q’vqk ž¶½$j½ÎÎÎU«V]½vuÇŽo,,zõ\±Î‡|íÚµÚÚZss3„’’’•µþKðº¹ùàƒsçÎ>Ýãĉ“_Lý¢¸ø»C‡yÏ÷ÆgTäG$鎂“E^žÎ¶¶>uút[[a2ޤÐo£–áq…ÞΨøÁJU_ª¼sçÅ˗俼º<(hÙ²eÿY2×{N[k[VVN÷ð—®ôU«Â}}ý-^<ÇËkÈ!¯_7ßùåNOwOHÈÄVLNNnœí¸¢Â"牵´µKΟ/((•ØÐа­­íÈÑ£cFaÐåGŒIP´`ú–¶6‰Z 3ÛkN–-Ø÷w’‘ „ÉdnÛ–ÞÔÔ¤§§WVvñĉ«VEðÝJŒÛó?þذ~ÃäÏ' ïêì<®¤½½ÝÖÖVh;3™Ìíß|Óü¦Ù@ß äÂ…³gÏò'44ÁFîWq VOhwL™ì’»;733Ó××·®®.1)‰&î{j‚¬¬­²³2—.[¾*"bkJʽ{÷ø*C|øí?pàêÕk»wïRQQ ¹qãfdTÔáÇ ùcž$±}-¶Â½éâÖLß›ñG¢#.>¾´´ÌÓÓ³¡¡áüùóØF]Ýa£F#„ÊË Ú¼9ÞÕÕUl¹¡!aC4†ŒmÂ`(TUU=zTSSÓç+(‚**nedf¸LžÁa³…^¢"?nK5ŽødY¾ÒÏÏ? À××WM]½ö鳪‡Ö¬^Í—‰¬†#)ôϨ‘¸ãJ"øŒŠÍ¶>ñ .ÿõe¥e£G›hhhLogg›‘‘‘™™ºR^^žÃæ$'oÅ¿Ï,©‘#G:ômFffRÒÖ¦¦FUÓ1cæÍ›K²bqqq›b7Íšõ%.oie•–šêëç/4åĉNžžž;wìlllTWW/)9OP´`z777‰Z óù”É [¶L÷˜.ök,bQTd&&%&%$V=x8hÐÀðˆpÁ/½#Âöd©¨hhhìËÛÿןRääFŒ0Úº5ÙÎÎ6=ýkÁЙIIIÅ M°‘ûU\‚ÕÚÆÆ&qñq™™™{r÷ ÓöÕW_qÛ¹BS³°´ÌÊÎZ¶tiDx¸žž>_e"ºÿþö¯·/Z´Ãá „äåå““½¼ælMN‰ŽYGþ˜'Il_‹­poº€¸µÓ÷rü!ßzw*+B………………øÆysç¬Y»!ÔÓÝÝÕÕ…/°cs¬O:sêÔ).—«¦¦æîî¾|ùrÞ[0ùX[Y_¼x‘xÐhÜ–â˜!>YLLLØ·cGf|\|k[›–––‡‡‡`&²ޤÓ£FâŽ+òxgT‚÷TQbcc5müüÑÔÂíBm˜!ü‹¦@¶víÎ]²xÑôénnnKúº:üúUŤ«Lññã1Ñ1ÅÅ߉ºïª? -5%õÄÉeee{õÿÐ>‚ãç= Ó×Rë·Q>Ö¶ýXã" ûP›ŒoFuâûã'žé"„´QZ¬¿näÁJU_:~¼¸¯« \¿ª˜¤•yüøÉ³Ú§;wìtttê·ÓÌÇÚ}ü|>ͨߵm?Ö¸dHpF%˜&Uàc“Pqë–™¹yt´lžÐÓ|Ä¡@&tF÷Tß®]9}]é…G„‡G„‹z÷ƒ ð!îk@ÿ!jF%xOÅÅÅ1cÆ 6lØb_||<3fÌ`æÌ™cRì_¹r%?ÿüsªÇ>{ö,3fÌ`×®]©¶Ù»w/3fÌàôéÓ—vÙ`ûöí̘1ƒøøøTÛ¬\¹’E‹ecT—oÖ¬YlÚ´ c 3fÌ`Ë–-~Ž*ðœ8q‚3fpàÀË:Îúõëùõ×_3)ªô›>}:³gÏö¼>}ú4û÷ïO±9r$Å{/\¸Àúõë™={6{öì¹è¹N:ÅŒ3Ø·o_¦^CfÛºu+~ø¡¿Ã¹ª)‰!"">…††Ò»woyä‘û-ZDëÖ­‰eõêÕ)öwéÒ…Aƒ¥zìƒÒºuk¦OŸîÙöÍ7ß0tèPÏëÙ³gÓºukþúë¯Ë¼’¬óÕW_ѺukO¢åôéÓ<óÌ3,^¼ØÓæùçŸ÷ys²6mÚ0fÌÀ&¬Z·nÍĉýUàÙ²e ­[·fþüù—uœ·Þz‹»ï¾;“¢JŸíÛ·Ó½{wÊ”)ãÙ6pà@J”(‘béСƒ×{ýõWJ•*EÍš5iÙ²%åÊ•càÀižoÇŽ´nÝš9sæ\vìGŽ¡OŸ>lß¾ý²•\Ù²eyçwøæ›o2ýØ""’>JbˆˆHªš7oÎòåˉ‹‹óÚ>cÆ Ê•+G®\¹˜1c†×¾Ý»w³}ûvZ¶l™êq###¦OŸNtt4Û¶m`ܸq´hÑ‚7rë­·’?~Ê”)C¿~ý8{ö¬WÜß}÷mÚ´!oÞ¼T«V &ðÈ#Я_?Ÿ×yøðaêÔ©ÃèÑ£½¶·k׎† z}£;mÚ4êԩáC‡7nÑÑÑœ:uН¿þšÇ€ûî»/ŃÞ÷ßOtt4¹sç¦nݺŒ?>Í{àÀ¢££™7o}ûö¥dÉ’/^œx€ .ðí·ßrýõד+W.êԩòe˼ÞðàAºuëFÉ’%‰ˆˆ ~ýú>ç)ùꫯhÑ¢yòäá†nH1÷B||<ÑÑÑ|ôÑGüõ×_DGGóÃ?xµ›5kÑÑÑž¹4~øáêÔ©ÃÖ­[iÓ¦ ùóç§Zµj|ðÁcx饗¨X±"´k׎ƒ¦y?n¹åÆÇ=÷ÜCþüù½îï'Ÿ|BíÚµ §bÅŠ<õÔS)>gÏž¥ÿþDGG“+W.ªV­šâ÷½fÍÚ¶mK¡B…ÈŸ??5òšìgbĈ<öØcäÏŸŸvíÚ‘À°aèW¯Üzë­lݺ5Íkr{óÍ7©T©¡¡¡”+WŽ=zxÝÁƒ{þ7nÜHtttªËúõë=ï›:u*7Üpyòä¡L™2<ðÀéê=0bÄjÕªÅu×]çÙ¶uëVNžø€W^y€Ûn»Í+ɰpáB7nLþüù)V¬:uòêývÛÍ7ßLž|Ø”(QÂDEE™˜©S§š»ï¾Û8Žc¦L™â9ÇÌ™3MPP©U«–3fŒéÒ¥‹ 7€yúé§1Æœ?Þæ…^0ƳqãF˜qãÆyÅûå—_À¬^½Úc̸qã `J–,iúöíkFŽin¼ñFdZµjejÕªeÞ~ûmÏ=»÷Þ{S½Æ“7o^S¶lYS±bEs÷Ýw{~çýû÷7AAA¦mÛ¶æ‹/¾0o¿ý¶)X° iÔ¨‘IHH0ÆoÚ·ooÂÂÂL=ÌäÉ“MçÎ `ÆŽkŒ1fóæÍ&wîܦpáÂfРAæÝwß5åË—7€ùâ‹/>ÞT¬XÑ”*UÊÄÅÅyÞwøðajúôécŒ± ZÀüþûïž6›6m2]»võú›6ÆþΓ^»Hoüÿox-Zrä¢á$""’¦-ZpðàAþüóO.\¸ÀÏ?ÿLll,`»U·lÙÒ3Á]…!ù|7Þx# È”xÚ¶mëYw‡êÕ«sôèQÀ¡Ø¶m½zõ"((ñqmÚ´¡dÉ’i·}ûöœ?ž¹sçðóÏ?Cýúõ=×·hÑ"Ž;FÇŽ3s¸å–[<¯¯»î:¢¢¢Ø¼yóEßCXX˜çu:uÈŸ??M›6õl+_¾<€§ÔåìÙ³©R¥ ‘‘‘lܸѳԯ_Ÿ={ö°eËöïßÏîÝ»éÞ½»×Øÿ^½zeê\îÏ @5 ¥yóæDDDxÅøðá‹+::Úk˜Â¼yóˆ'66Öë:Ë–-Kxx¸g~–•+WR´hQ¯ÏNpp0ëÖ­óTÃX´h]»v¥`Á‚^mxàöïßÏüáÙ^±bE*W®ìy½råJèÙ³§W¼÷ÝwßE¯é†nðT1ùì³Ï8zô(Ý»wgìØ±éú›y衇˜3gS§N¥Zµj,[¶Œ“'OÒ¶m[¯ûATT”ç3îËŽ;(Uª”×ö-Z0lØ0-ZÄSO=Å /¼À¶mÛ¨T©¯¾ú*‡òüΜ9“åË—óÝwßñý÷ßó믿räÈ‘‹V(èÝ»·×ß®ûóéþ]vëÖ°°0&Mšäi3qâD4hÀ5×\ãó˜Û·ogÛ¶mÜ~ûílÙ²Ås?<Èõ×_ï¹uëÖÅq:tèÀðáÃùóÏ?©R¥ “'O¦Q£F^Ç,Q¢!!!žû%""ÙGI ISLL AAA,\¸%K–pôèQZµjåÙß²eK¶lÙÂáÇ™?>åÊ•óªh‰Ù™¡D‰^¯sçÎí—¾nÝ:Š/žâ}¥K—Nó¸uëÖ¥téÒÌœ9c ¿üò -Z´ iӦ̛7 .ðã?R¡B…4'õ%i™Ê¤q'¯úâK¹rå¼^S®\9¯ÄFHˆ÷<ÝÛ¶mcóæÍ\{íµ^Ë¿ÿýoÏ~÷½òu?£¢¢ÒwaéP¶lYϺã8¥xØLj’ß w ÍÞ½{{]gíÚµ9{ö¬gNŠõë×ûü6ùuâĉÇOzN÷Ü,òóœÚç®B… ½¦gŸ}–þýû³lÙ2zôèA±bÅhÑ¢Eй8|:t(Ÿ~ú)ü1Íš5ólwÇ:`À€Ÿx]KrîDXòÏEÓ¦M0`yóæõls‡îÝ»sþüyÖ¬YãyOÆ ©S§Ž§]“&M¸îºëÒUj6éçì|… ò$ .Ìí·ßÎ_|Á… صksçÎåÞ{ïMõ˜îëýøãSÜ%K–°cǨ]»6S¦LìUªT¡zõêŒ1c¼+S¢D Ïý‘ì£ê$""’¦‚ R·n]Ï?ö .Ìõ×_ïÙß²eKÇaéÒ¥,X°ÀgU’ô>¤¦GZ½Üߢûz°8tèPŠo—“k×®³fÍbݺu8p€-ZpâÄ ȪU«øñÇéСC†cNúÍrF…††fø=‘‘‘´iÓ†?üÐçþ‚ ²eËÏ·çI;wî¢çHž€ñu¸´øS“üXùóçl’¢páÂ)Ú»=ùòå㯿þJ±?>>žòçÏã8>'½t_WÅŠ=Û’žÝ½|Ž;æU8=÷188˜×_¡C‡2wî\¦L™Â7ß|CË–-Y¸p!õë×÷ù¾Ï>ûŒçŸž¤èñ À/¿üBõêÕS¼7­¿Ç|ùòùŒýرcœ;w.E‚Ë}ü„„O¢°víÚ)Ž[³fÍt%f’ÿž.\¸À‰'¼zRõîÝ›©S§2gÎV¯^MXXwÝuWªÇtßQ£FÑ®];ŸmÜ£:u¢S§Nlذ©S§2yòdžzê)Ž;ÆË/¿ìõžsçÎyî—ˆˆdõÄ‘‹jѢ˗/gþüù´lÙÒ롼X±bÔ®]Ûó🴗FF\΃¾[ÕªUÉŸ??¿üò‹×öÍ›7§«Ûw»víØ¸q#“&M¢téÒT©R…ºuëR°`AF͆ ÒJâþf?ù·¶Ù­Zµj¬X±‚Â… S¼xqÏ2gÎ^}õUŽ9BµjÕˆˆˆ`Ñ¢E^ïݾ}{ª ÀóMü±cǼ¶»«’d'÷ð‰õë×{]ghh(ô|ªV­ÊÎ;Ù¿¿×û{öìIñâÅ=½[ÜC’š?>aaa>“n7Üp`«_$µråÊ‹^ÃàÁƒyã7 ¡E‹Œ5ŠŸ~ú‰„„„TúçÎËý÷ßO§Nxíµ×Rìwß—ß~ûÍë¾(P€_|‘o¿ý6Õxܽ):äµ=&&†êÕ«{*…¸-\¸àà`jÖ¬IžÈ¬Y³ˆŠŠ"88˜¾}û2vìXÞÿ}Ξ=˺uë.:CTTEŠáã?fÏž=$$$ðÓO?¥(Wšn»í6jÔ¨Aÿþý™6mñññ;vŒ¾}û2~üxrçÎ À#IóoÁ=/ÄÚµk½¶ß{ï½>|˜!C†pòäINž<É{ï½ÇÈ‘#éÓ§ça~РA¬X±‚AƒqþüyÏ\'Nœ G½“&MbÅŠ€M =üðÔ)S†»ï¾ÛÓ&88˜=z0eÊ–.]š¢œ±ûïpùòå;vŒB… ñàƒòé§ŸòþûïsüøqâââxóÍ7>|¸'iwæÌ>ýôS^}õUO²fÉ’%ìÛ·/ÅïbýúõÄÇÇ{X""’ü=³¨–«bQu‘wæÌOéͽ{÷¦Øï.7X¿~ýûp•LÊWu’¾}ûzJšþïÿKµ:IÒ*'Ƥ¬Üožzê)bOÕˆ"EŠ˜ûî»ï¢×ê.Ûê®laLbÅŽäïO^äøñã¦téÒ0¥K—6ÆØªµk×NqžòåË›öíÛ§‡»:IÒ–ÆÓ¦MS³fM¯m_ýµÌÊ•+=Û†nråÊeÇ1Å‹7€©T©’Ù¸q£§Í… L§N ` ,hÇ1;w6QQQ©V'1ÆVq 5Žã˜|ùò™ÈÈHóÁø¬N’ô|Æ“+W.ó¯ýËk[ß¾}MR½ÆØê$÷ߊí6l0µjÕ2€)R¤ˆ 1¡¡¡fèС^í¾üòKé©–˜&Mš˜'NxîÅÓO?m‚‚‚Lhh¨ÉŸ?¿Lçν*ZT®\Ù´mÛ6E‡65jÔ0€)T¨Ìc=vÑê${öì1U«VõÄ•'OOåwÕ˜¤Ÿñþýû{þN|-C† 1ÆØÏOÆ =¿Ûððp”âÞûR¯^=sÇwxmKHH0}ûö5Žã˜Ü¹s›|ùòÀtïÞÝœ9sÆ«Ý!CLPPÉ—/Ÿ‰ˆˆ0@ŠßGrîê$}ûö5!!!¦dÉ’žòÌ‹/NÑþ÷ß÷”ðu—vÛ¹s§)X° ÌM7ÝdŒ1æØ±cž²ºžßïwÜá©<“`ºwïî© åùœ:tÈëï½÷ž õüí‹dñÆÿÿ†×¢%G.Ž1þíò*W…À¥õ/‘cùòåÄÅÅÑ AƒûâââX¸p!%K–L1©ç¼yó(Uª”W5‡sçαxñbªT©âë~á–-[ÆÞ½{iܸ1 üñÇÜxãäÉ“‡]»v±uëV6lè5¦Ó¦M>|8E\gΜaãÆT¬X‘ÈÈHÂÃÃy饗xöÙgÓ¼Î;w²mÛ6n¸áÏЉãdzzõj¯x“¶mÔ¨‘§ ûÑ£GY´hAAA´nÝšuëÖqîܹߨ.]º”ˆˆjÔ¨á3_÷ðy¼¿ÿþ› 6xÅ °k×.–.]Êþýû©R¥ Í›7÷ęԶmÛ˜={6×\s 5bÙ²eDEEQ±bEŒ1Ì›7òåË{Mh¹mÛ6æÏŸOÞ¼y‰‰‰!<<œåË—{bØ¿?7n䦛nòôˆûíz©R¥¨T©’gÛæÍ›ùûï¿Óìå²`ÁŠ-JÕªUSì;þ<‹-â÷ß§@4nÜØçD®ÿý7 .d÷îÝ\{íµ4kÖ,Å+þù'Ë–-#!!˜˜˜ÇY¶l¹sçöÞÔªU«X±bõêÕ£zõê,Z´ˆêÕ«{Í•‘Ü… øå—_زe‹gΙ¤“Ÿ&ýŒoÛ¶;w¦z¬ *x&#MHH`ñâŬ_¿žÜ¹ssË-·xÝ÷Ô¼ûî» 4ˆ½{÷z ÓÛdÙ²ec¸þúëSävË–-,]ºÇqhÖ¬™ÏÉv“:}ú4Ë—/§nݺ8p€yóæQ¼xqš4iâsÞ‰¸¸8 *Äã?î™°6©ƒ²xñb"##½*ù¬ZµŠµk×ráÂêÕ«çsþŽ5kÖ°jÕ*âãã‰ŽŽ¦nݺ)æiܸ1EŠaÚ´ii^—Èe˜ôòw"9‘’’”Ä‘lS­Z5Ú´iÈ#<Û¾ûî;:vìÈÔ©S¹ãŽ;üHÎ÷Ï?ÿP¡B^|ñE}ôQ‡ãÓçŸν÷Þ˶mÛRT4Éj›7o¦Zµj,_¾Ük’c‘L¦$†H*TDDD®(-Z´à½÷ÞãàÁƒ4hЀõë×óå—_Ò¬Y3Z·níïðDr¼ø ÛÏ-""–†“HvÐp‘ôÓp‘T¨'†ˆˆˆˆˆˆˆ%1DDDDDDD$ (‰!"""""""AI  JbˆˆˆˆˆˆˆH@PCDDDDDDD‚’"""""""”Ä‘€ $†xq',â8Ž“YÇ‘«›’‚ã8EÇí8ÎßÀ9Çq8Žó‰ã8Å’µ»ßqœ>–éÉÚÕtgp8Ü´iÓ:ÈÆ+‘+Qˆ¿ÿr'˜ÔÆ?->@´ã87câ]Í›E€éɳ'Éñ€¯€BÀÿ7mÚ4¡aƬY³†ˆˆˆ¬¼¹‚)‰!M†À`cÌË®m3Ç9 b€Ÿ]ÛësŒ1½Ò8Þ³@U ¬1fÀ‚ þÕ¨Q£Ÿ}ö}ûöÍ‚K‘«†“ˆ| |–lûl×Ï2Žã„Õ€U9Þ]À¯î@Æ OÖ¨QƒÏ?ÿï¿ÿþekôèÑ8p€^x!"ËÙ2ó¿!W“¶mÛ2lØ0jÖ¬éïPRÈÌÿÆmÚ´‰~ýúñí·ß^ÿJË÷ßÏ/¿ü’)ÿ ${÷îýÀ\Î?§2)¤Ì6X LIGÛ xûGa+(ÊUBI Àqœ¶À7Àz µ1æP:ßú 6‰QÄç8Îßøèîuøða*V¬˜á¸rêjfúí·ßøã?hÓ¦¿C (£G¦|ùò9ö¾½üòËT­Z5SâË•+ÑÑÑ9öZ3ËŽ;hÚ´)åË—÷k,äĉäÊ•+Ç~>~ûí7V¯^)ñÍš5 Ç^kfÊÌÿ†\mnºé&š6mêï0RÈÌÿÆ*dÿ©KxxøeF–³mÞ¼™¥K—^ »ûôéóßK}³ã8=cƘK>FVrçE`SfÄç8NêßzÊI{ ŽãÜ |‹­HÒÔWÃqœ Žã¬q'yºÿ×ÏM®Ÿë±%[=Ο?ï,Y²„J•*erä""""""r5QOŒ«œã8¥O± ŒöƘ ©4Ýôzc»lá8NU`°pOúðƒã8Œ1ß >¼Ô±cÇxâ‰'²îBXll,U«Võwçá‡&_¾|þ#U  B… þ# .\˜‘#GR¸pa‡PêÕ«Ço¼áï0RKåÊ•ýFÀÑC.ÍÈ‘#©R¥Š¿ÃðIÿ»4Í›7§téÒþ#Îù;ˆ4¼l÷w˜cŒ¿c?ru¿zˆ|4yÎó–ã8¹°‰Ž[€åÀQàVà/ ­1æw×ñà{ ˜„9ŽÓö™gžáõ×_Ow\=ö~ø!ú|Š@ñâÅyçwèÚµ«¿Cñ«ýë_ìØ±ƒï¾ûÎß¡ˆøÕ’%K¸ùæ›9sæÌ?œä*6èåï ã8˜oŒiâïX${¨'†,ö¥± €1æœã8M°½1áÀÓÀĤÃOŒ1ÆqœöÀ½@ dĈëžx≜7Öˆˆˆˆˆˆ%1®rƘ±h{çZÒjg€ñ®lÕ%1DDDDDDä²(‰!9’Ê+Š$zã7¨W¯ž¿Ãñ»;3“'Oú; ¿«X±"#GŽLwéz‘+\pÐßAHöQCr¤üùóû;‘£gÏžþA$GhР¿CÉŠ+ÆC=äï0DrŠ฿ƒì£$†ˆˆˆˆˆdªmÛ`ôh¸ñF¸ãßmž¢£¡sçË?ß³Ïz¿vÈ›ŠƒÛo‡âÅ/ÿ"’3(‰!"""""™ê¯¿àõ×!<V¯†jÕR¶yóMèÖ-s’¯¿¡¡'Oâ¶Ó§!>BB`øpø¿ÿ»üó\‰‡²Æ°3Éëp ¿1¢!9S¿‘+Óٳл7\¸õçêÔ ŽK\Ο‡µk¡jUè×Ö­Ëúãðð›ãp“ëu8ð0ÏqPÿÉ‘”Ä‘,Ѱ!,Yo½•þ÷œ8K—† ¶'Å¥r¨U † „øõWïý[·Âÿþ»w{o?sÆ&A|9yþùÇ{[\œ÷÷ßíy’3vì€ à`êÛàJ`< D ‡ À~ P øU‰ ɉ”Ä‘,ñè£Ð¨¼ø"üñGÚmãâà©§ H¨_jÔ€¨(˜4éòbÈ—Ïþ<îšúñàA{üÊ•¡qc(SÆžkíZ»âD(XæÏ÷>ÎÑ£P´(Œa_'$ÀƒÚ¶õëCõêP³¦wÕ«¡^=¨PÁÞ‡¨(hÙÒ·ÉÇÁqÂ]KP²}¹\ÛC’mumK¶=Øqx›Àp ®Ã&4Ü܉ŒÜ—xŽpWÏ‘L¥$†ˆˆˆˆˆd‰  7‚ƒ¡W¯´‡•¼ö¼ótïn 3gÚž={Â?^ÚùãâàÃíú7ÚŸýúÁæÍ0{¶MLÌ ‡Á}÷Ùý]»BîÜ)“'_}e‡¨ôèa_?ö|þ¹ý¹};Ì™c{‘tìh'OB‡öØï¿;wÂС6±Ñ¦M†{™Ôθ–vÉöýåÚþA²í“\ÛK¶ýà•tžw”1œ¹ÄsœΨ7‡d6Mì)"""""Y¦R%› ø¿ÿ³“y&¯$pê”}ÀïÖ ÆŽµÛjÕ‚fÍl‡çžƒ¶mÓ>ϪUð¯%¾Þ´É9}Úö~hÕ*q{åÊv¨KX4i_~ {÷Ú$Kd¤MDL™b“ᮾ'BL ”+g{sŒi{˜ b÷—/o'8ÑQ™<ví‚+ n]ÛfÐ {=:ØȽ÷^òmÍ«á!’œzbˆˆˆˆˆH–züq;œâ¥—ì\ÉmÚd{9´oï½=$Z·†õëíþ´ìÜi“_~i{MìÞm“#GÂÿ›Øî®»`ùr›ÈxüqÛ#£aC¸çÛcìd¤ÇÃþc_oÙ‹'&ÜCObc½c¸ë.Ûk¤lY;¬¤téĆ[Û¶ö<«V¥}=9Àa â‹’"""""’¥Ç{XIò¡;]>‹KùÞbÅl‰#GÒ>G‡°¿]öí³I„ÿüz(19ðôÓðÙgP¥ |ü1´haÏñÑG‰mš5³‰÷’‰!o^[àÏ?S7é5ùÚbçÑÈI“|¦¢‰»j‰HN¢$†ˆˆˆˆˆd9÷°’+àõ×½÷•.mîߟò}{÷BáÂP<gVèÖ ~ù€1c dI; éÊ•vP‹cÆ [©äË/¡sgˆˆ°ûK–ôï™3vBУGí5ùºžóçáða;™hìîv-Ë“íëëÚ>&Ùöw]Ûû%Ûþ3ð}:Î |ë8ä¾Äs¸ãM¥Ö‹È¥QCDDDDD²…{XÉË/ÛI7ݪV…þŽE²‡zbˆˆˆˆˆHŽdËŸfµÈH¨Y3õýçÎÙá"·ßžz , ªWOû\ùòÁµ×^Zœ"âMI Éz§\Kz…Yø-¼HZ®¿vì°½,>ýÔßшHRJbˆˆˆˆHÖ{xý¢­5æfM("Ó©“­,Ò«ÔªåïhD$)%1DDDDD$°%/âé¹.ýp™U ED2Ÿ’"""""âH™ŒHËàvÀ¹}ìŠw’—àÂ!ˆÍÀ;cø$«ÂÉ(%1DDDDDÄÿÎç2ÐþB²×uI^ŸVóE@ÄåxŨ¼–ö›@I É9”Ä‘ìW¨,¢€k°š9Uµ ¶ŸܘHªªÿò±ýYì|,³€ŽÙ‘ˆd%1DDDD$ûºÖ÷ùýKzlÊ`û²$ ¹°IŒ5$&1~¾ö°ƒ+:'Ù7x(’ìXƒ°=>Ümÿ¾þä:wbçäpû˜ ìªÝ€[2åÊD®JAþ@DDDD®Bù’½>î—(²NOlïô,ýcN²ËǶV$Îuák΋ôúÕõÓ='Æ  ðPØ t¸öÞ¾LvœEØAá®×«›€¡ØÞD€»“àQ =ð PÎuÌFÀ° ^ƒC †áŽCˆk½Œkßs8Ôr­?‚C×úÝ8tp­ÇâÐÛu´ >Îp;ç,¾Rp=‡ÃM8ôs­WÁá• ^±Hª”đ엨îZw°€W’]ØÞéYöù)Æìööa>Û§=ö>%•]?“Ú œq­ŸáâÎÇ’,Kcù€v®v °Ã—ÞÁ&9º¸b»€ML\ LJvü‰ØdE¬ë\­±I]Àx`Ðý<Ï>ú;€O]×tðð{:®)Q6=ŽíM?(åÚ÷8pk½'p³k½=‰x6îr­—ñqüì] '±TR=GàA×zEà™Ô/M$c”Ä‘ì÷°Áµn\KR{²7œ «Š}¤+‰}˜•DÛ±Ã'À&ÜsƒìnÅþnóÿÅ&q‚°¹É?{3xÞ©@Á$K}à9ìcù—ØyXÀ¦Ö#¿]Û¾VÁ®×½€eÀf×ës®6ݰû`ý±=7ÜÞÁÎó |ä†`+¥àz= ˆ¦eðú²VÒ«¨á·(DÒAI Ézñl_û­øÑ,ˆ%3lÃ~“¾;Ìàja€Ó®õ_o\ë-]ë«€û]ëyåÀ~ì †`ìð×ë­®v×2‰‘tÞˆæéˆ­611ÛÛâ`6QÒ&I»7°Ÿ¯G€âØá#ðÒÔë»7ưŸÅ^®×k]?«&‹¡˜+Žp`#öÓµ)fc:®)û$­ “Ñô‘H¶ÒÄž""""’õ–d°}ðö!ty&Æqûí|F õ±-éW›Ï£0vØÁ"ìm>ÒßÓä}{¬¤G,¸f(¸<;±åH c{TnÃ>ÜçÃ& æs±“\Áön›8*aïÕVl ¬k½©ë˜î$FàÇd1ü7ÉúìtÄ\x(íª`{R,Âö†øx ›øXÀ&9bÏW°ÉŒ:Øß#@œëgò!0É¥¶?Û[$ýÎbÓ1\GÝDâ ›?®õØO9ØOÙI×úAgI>ç…`{`Db{%—ÑsŦúN‘ñ©qER¥$†ˆˆˆˆd½úøiŸš]Ø¡?»^ŸÂ~«¹N£2øžâ>¶ÝFâ‰2øž˜Ò­öaì\ ¾Só#¶RFz ýIŒxlràflÏ€ÎÀÓØßU[lBàQìÃþVì5»Ëâ‚MVŒI²¾{+cç9ÙŠ_¢´kýW;wââ6ŸFþ›°9Ÿk½qØê#Q@C×òvÂÎç°Ÿ·.®¶½±÷äØò¬¯'9Ne×Ï?Hœ)l/”§±‰JØÔ@òÏîVlú¡nâ6lÄ»Ðoµ$û%Yïšdý©$ë#’¼w‰}eÀþ¶’öu¹ÖÇù3vÃWÀW®õ@íǹDN"""""Yg%¶,åÓØ¾!@öî?RY†cçÌø û¹(ïÚçïûØö=v¸A0ï|.ÙëPŸ­²ÎA`´k}/ö¡û ëõmØo°ÃcÖ¸Ö¯Á~÷~±õ=ØïõÝõ[°Iš2$NXú¤«-Ø /¹Ö!qêÇ2x—%Í*¡Ø„ÉÝ$ö¦p°ýÀ&\ÜnÇöDyÛÿáž$ûêc‡Š¼MÞ€íÏðolÒ£¶×Êìõ»û0ÂöúCeVE.‘’""""’uNc«2tÅöxû|©—>:a¿µn Üý~8«¿©÷Å׿˜ã±¶î%-%’#/‰ÌYa¥ëç kWü ôÅ(‚íØ¿Û ¢¾URÙ~ ¶×É9×z9ì—î!&î^+ÛHìò/kYÃ>ÀûÓp쀈RØdÆ-ØÄFlrÂ- û9]ƒý I²/vøÉØ{Ð ÛKeðvÂ×›±UH>Å&i:a‡Óü;„¥J\›ÈU@ÃIDDDD$óìƒlcì7ý]°s |ObµŠÔ4w-cç%¸û€|Þõº-$éÄ~ùªbKrþ÷d“n}°%.Õ ì¿ºƒ°ÿë/ãX…±=ÎcÂÿÄ{ÒT÷PØ^ǰ×6qq3¶·ÁŸØ^.•°½'À&%6'YwWϨEâà…šØß¡ƒMJmOrî¤óQ“qÿÁwB¨öþ5bH¬2âŽáƒIœ¯âb:« Ø?×cç_éJÊÞ ·c{âôòqœ–ÀoØòª°Ujz˜°ÛóèVì=Û ¼Šý '‚’ý`k¥×Ž,ŠCä’(‰!""""™ï[ìÝH '0û­´¯ù%Rsû-ùGØ@l/‚ ®ý d^¿â®ŸÉ‡~tçò’†Ä¡ ¾d¤rK>)Ïc cL²ÿ4v^ŠŠ®×b«e”p­ßŒM\¸{V$MbÄ8Ùä@×z]'ÝŒÀAÉ ·¦²} ‰ŸƒP´*¶W‡ÛKØÉ ß#㉀RØo¹úÓ±ó ÇNÄ8;ãÅæR(A⹯$EÒº »}ìÀ&cÒk«ÆrøâM<’&<Î’²|'ØE]ìCþfl£*v>€°½(ÀÞG÷D£m] ØdFƪgdâØÊ'Ÿ_`çáMje÷y>ÂNÒy)=KD$K(‰!""""™gð,¶ þc؇é.Ø.û}/ñ˜ ØÉ¿Á~ƒÞ;é'xO¶˜šVØ!"õñ]$„Ää@0¾‡4¤§t§Û2–ĈÂN™;mKãhÉ2âV[ ì¼"u\ës’´¹;ÉzVÎÏ‘™z`“UO`ã/•vóËöv¨I _ŸKD2DI É\›±É‹¯€Øy ]Æñ‚€‰®õÕÀ×—pŒñÀ2l‚%¹ (ö>7ö[ÿËq/ÞD&·[¶´6ar;dáMR·<–ìõZW›CØIB&Ûß; $– ½ÌÇ&lÀþ¾~Åå¨Eç[®Sìb E$»)‰!""""™c%¶ÇÅ"l/Œ¦ØÒž½2ñk±C#2šhx[Îóqû°“zþ}y¡yĸ–´ 7‰ó{Ü—ŽcŸ!±ÔlfÅòc÷Ÿ“±×>ЬKb¸xñ•X‘Ë÷;¶÷A?ì×dc°%(keòyzaçÛȈxlEŠÜäœé lI¿'±55^Ä/êå×hDÄOÔCDDDD.ßuÀ/À]Øá_“¾ž™¡4¶"ÇqlrÀÁö®pÛ „a'õ5±§¿TÁ[ô™ý=‚­öò¶ K ¿E$"~ $†ˆˆˆˆ\ºØÄÅëØ «;7H_ ËÌpŒÄÉ, )ûŸtý|; !£CQ".=´4ùª,’–¢@³‹´©}‰±‚0ìP¥‰Àl9Ù·QCä*£$†ˆˆˆˆ\º`ìüõ±ÃGzb']ôUá#«œOöºp ÉëêIÖ‹_fyD—¦6¶Üênlo’0¼{Ž4#çÆž]ÞÁö¸y èHàTW‘L£91DDDDäÒÌÇö&˜Œ­¬ñvÈF+ãȃD´ p —W ÅŸNX>ՠʾ|ÌÄ·ŠMê,òg@"’ݔđŒ;…-áÙ ;DãQ`ÐܱæG…$Vï4‡“½Îë—(r6ÛÓ§'…­8³Æ¯‰H6Ópɸ¼ØJºØ9 êû5¢ÀW [B5ÛÃE_7úö6q5h‡­:#"W ý§QDDDDÒ/Ûûâ  <¶çCàe?Æt¥Ø‚½¿g ~Œ%'û[™ä.ì}ú[¹DD® ê‰!""""éÄ`»ó/ÂV EÊÉ5³R^ìP‚ô Ô92$u?£ÿù€Á@¬_#‘l¢$†ˆˆˆˆ¤Ï\ &ð,vèÈÝÀo®íaÙÇÓ®E®^‹±“Ç~ƒM^„ú7É>JbˆˆˆˆÈÅ` °˜Š­²X‚(KöK:|éol2£v²Y¹¢)‰!""""08žÊ>ƒ­ Q8Ü„Mh¼tÈ–è®LŸ`çwH¯à¬ $@ý|ü”Àö‘+ž’""""€h? xìFr¥ Eà .ÇV 46‰Ñ›h‘+ž’""""’qCQCüë‰$ë»É@~ ¯Â‘졌""""’qÚ 9ÁtlµœrØá9üŽˆd=%1DDDD$¥’@lýÒ~ŽE$5ÇÚØ f7ø7ÉzN""""")ý ĹÖwû3‘4ts-[€Ï±óŒ ò[D"’ÅÔCDDDäjæî~>ÙöøìDäMÇV̹øÛƒHD®XJbˆˆˆˆ\­Ve€S¤¬’Q4Ézžl‹H$ã°ÕIVБ+œ’""""W“mÀuÀ! :p˜JÊò”!®me€ºÙ HµÞ¢±‰Œ§'ý‘ˆd!%1DDDD®t€[€=Ø*G€/±%R»|¼g/`€]À‚ì Sä’ý€MÎÕæÕüŽˆd%1DDDD®D'ÎÀv 6ñ¶4ê=ÀDW»žÀ\TšR[!  ° Xôõo8"’u”ĹRœž6ù€õÀ$ì°î®u°‰‹å®v7?¢J`k ÁNî¹x›¬‘+ŽJ¬Šˆˆˆ2¼ÜŽãâ@ðмˆM\ Öb瘔p£5Ð8–ó–»üÐE2ÕÀcÀN )p¯_£‘,¢$†ˆˆˆˆ¿üŒM>¤W8‰‰†/€@-à?Øy/ÞzoÿÆö¾xX Ü |q½¿{²cœáèEr–ÒÀ£ÀÝ@)?Ç""YFEDDDüå¶:Hz—°CDÀö¤øÈµ~/0ˆÇv¡ßç¢6™QÐÕîaôp'W®ÚØÊ$%±“{>Äú5"ÉJbˆˆˆˆŠ Àp×zOàklrãNà80 (Ší}‘ËÕîYT©A®s°É»[±É¼^~FD²€’"""""˜ œ:`{^üž\í^ø#@?« öa‡YuÅÎ#"W ͉!"""¾ýè—öE±“J^ÌÀ‰ w ¶òÀÕ¢#ð“k½ö÷à„ßbç´xÛì°‘«]yìß…;Á÷9¶ Ï*?Æ$"™JI ñí°)íÓ[Ùb3‰Þéq*mÍFÛ~ÀøÃÇþ{Ý®õ׳"(‘7è‚ýoXà9lo Ç1‰H¦QCDDD$;} üŽ}°Úæcÿ…$ëG|ì‘A‰\Aª¯bçŠqOj{Û‹Ig¬©C IDATDžæÄÉ*î±øcÇ\ëGw]ûnðñž*®Ÿ!@ž,NäÊTè ä¦`穈b""OI I¿ÒØíú™tÌ@- Ð:“Žé/øÛµþÐÙµž[5{‡€•@1ÇØìúœPD2n ôòbçÖÑ“ÈAÃIDDD$ý–%Y¯‘IÇüƒÄ!§3é˜Ùé86ÙP xûð4›èy[µöÚh`¾{bˆÈ嫌ڮm'€ü~‹HD2‰’"""Wƒ%ØÉîÒ«z‘6;’½> KDZÏ%{œq­g¤j‰?m`«‡ ~1®×'æØqøó[›±UGqñû+"—./p6É8ø Xìuí‘€¥$†ˆˆÈÕ`.00í‹/úØ^ ØŠí–ýO²}§3x·ÖÀ4×zÒ_å$»ÍÃö¸è |ƒ s  ¼†»ö_W¿`K¥6À–H½èOâÄ‚º~ÎȦØE®F+[°Ã¹º/‘Ø+CD–’"""’Ò?ØRŸÉíÆ–)4>ö]ª€`ì’Œ”^Í_a¿¹};”f 6‰Ñû@´;7H>lâ¢3¶7ƱIŒWHüÖ÷vÇ¿Û;#½‚3z"W±ÚÀרD£;x(ì·ˆDäÿÙ»óx™ëöã¯Ç…THE„Bº[)QJ*´ïîö”¶[Û}ׯ»¤í›¥Tºï’²¤„$ZHZ(KÙ÷õ8Îç÷Ç5sÏÌÙÏ13ß9gÞÏÇcgæûýÎÌ5œe¾×\ŸëŠ%1DDD$·zä½ü$çRh%MnDO ˆgr¤¸<öžÃ–Ê Äšt>…%1Nê)bɇ}ñÀeÀÉØ’’žÀ`"c/ä9kc'X"@w¬Jjð&ð–˜T"C¤ÔRCDD$‘ÆP¼>í°%‰V;Þ€¸—#Ò— VèöÒÝxŽêXŸ €Ê9?d뱇€¹ØIN%àu¬¿GWàÆÐ¾Ã€±% ×aIù¡Çz[ pHr‘"˜‡M?ª œ%•À)Õ”ÄI¤[°7ÑEõÉIbÔÀ–Œd†nŸ€5 Œv ðÐn>Ç_¡ëÛwãqf¿j…õXuES /ð0K< Á]+™@[ìÿc 6}äb`gè±^Á@ “"‘TÕø«Š XýhP<"²Û”ÄIG»tã»<޹8 y#±KC–D]Ïk™È XuC´µX߇ ¡ËÇÀ¿6XeG~ZaÍûò2«†9ëcñ6µ–¨Ùœz¾¯±­°ÄEÛPœµCõ@Ôã:D¤4è€õ¯U[Ía•U"Rê(‰!""’²sÜ.ÚN.äœ4VPœª›‰MbìId)MerWct#ï ‹ DzJ„RÈsÏŽˆºý)Vrvâò6]ä4àl™Kg¬e*6µ¶TähàlIMøu‰Héµ8h€-)y %0DJ±r…""""¥^ÎBøcŒšØI{Ãáæì5A¤’%ÚïXE}lyH9l’HxÍ{XŸ ˆŒB‘ôQKrÎîÅ–¹M!w¿ Iyjì)""’L{a åi|Èhº=›¤Aèz l ÈŽb<×ëXÆËDzSì[Äû–dYÅß(^|ÅI&|Ddl^“N¦=°±©á†œGí×$iŠUÀ½ ¼,f`•["Rj(‰!""’(£ÈÝPó«¨ëó¸O-lZÆ*l²ÆN,‰ñD žÿàUàrì$ÿ0û—r{gÔõ*Øä‘hÍB_oO` "Rºý…%[›ý€ó€zF$"% å$"""‰ò_`Y î÷6E£66*t м+ Òx>t¿Ã)Ý]œU±ÔÆšvæÔ:¹áˆH)Tkî9¸¨|Fîd³ˆ¤4%1DDDâ)›x±x‘¼û7欗X/‡ÎØ ü–ǕÚWKî$ʼnXuDYXJ1 «\YKéïï!"Á©‹-!¹Ø8 ›d$"¥FiþLFDD$õ¬ÀzY|‹õ¢8›8RëQ˜ªD`ÝóÏ ]Ϲ„ÂëB×§‘;aâ°FŸ""bvݰ¾?ÿôDŠDJ%1DDDâa%öi^7àC¬ â`¬Æ"ìMrI¦bD÷x¨”c_eb«3rŽk‘X°%%á¤r&VéÕM.)%´œDb8ç*áçœ+4æœËpΕ…"f‘ÂÎÆFöý ë~/0› 7ǵrÜnKd©H |žCD¤¬«Žý®¾ë‹q!ZR"RŠ(‰!8çöqνèœ[ìpέrνäœÛ7Çq5œs#€­ÀfçÜTçܱy<^KçÜ4¬MÒºŽ;¶^µjUR^‹ˆHÒ} ¬®®Ã&Šüú:8:Ï=…H³Ï_‰àQšÕ¶órx ‘ŠHiäË€%ÀSØ4¨“‚ HDŠCIŒ4çœ+‡õÏ?t†½sT\ ÂÞ–?‹­ÐÞ Lpεz¼ŠX_ýFÀMÀÕóæÍ«Þ¾}{¶lÉÙ‘ND¤”óXÿ‹3±´íc@{¬"#›Ä&0ʺÊż¨îODŠª0« »ûý1XdP"RTJbHGì-÷ ï}_ïýxïý­Ø[ñ#±UÝ8çN®.÷Þß꽜ŽõŠ^±Ýh ´õÞ?ã½9räÈYóçÏgøðáÉ{U""‰äñØßÿbý0. Ý~ xý…IeqÀÅÀ~ØÒ’ïHDŠHo±ÄocÕÑ> }= ôµ7Ö>îƒðÞûLløßÙιpÿûsϽ÷ËÂǵoßþ¯-Zðæ›o& |‘ÌÎ^ö>>öÚšˆˆÑ]À&l’Ô*lܪˆ¤ÏusKfÀäœÖ´iS¾ýöÛø-"”ÕÀZàlaݵ@  V™Ñ$ÁÏ?Ø\ŒãKT ""eÀ7D"¯^:‡‘ˆ’’‹s®Ö¢n‚÷~zhs-ì×{NëB_ëbo­]^ÇÕªU‹•+W;–‚îS«V-*V,t˜ŠˆHü Þf}€ùXÊ÷{l"I¢µOÂsˆˆ¤‹òXMñp`,6võ%”ÄH1ιòÀ>AÇ!©CI ‰š62KD\µ«"ÖÈ3§peÆ¡cÈ븪U«’™™ÉÎ;©P¡B‘ã©[·n¾û>ÿüs:vìXäÇÉe50±Ç-ZßÝ€/€ØTŒƒ ˆˆ$ÔËØD¤ÿ`UuE›*ÉsðKû}û¤ RCþÇ9w"08ÕÙ{¿4j÷J fw oû;ðy÷çŸR·nÝb%0~ù%ÿßW 4(Öc‰ˆäò3p~1ï³8ëh?ª÷‘Ô16êú ¬SÜñX{{I ±%ëùù1YHjPCpÎuà ê~NõÞ¯ÍqÈrò.–Þ$ø»÷~§sn ¶ô$ƺuëhÔ¨Q±ã:ôÐC‹}‘„ª‰½é]t ""o¯Ÿõ€†(‰‘BBÃææ·ß9§JŒ4£é$‚s®7ð>Öà³c °äÆÎ¹ú9¶, ýr ³j;33ÓMŸ>ÆÇ9r‘ŒÂ–œt ""°ßë±ÚⳂ GD ¦$Fš %%^Ægzï·äsè Àvàö¨û¶ºbCÆMœs= ´ÿ† èÛ·o¼ÃI¾cƒ@DDâêUàEàlIÉc­_’ˆBËIä^ ö™âç\Îýw{ïóÞ¯qÎýxÄ9×ë›Ñ ëÉÿTÔña}5ÞuÎ}TtÎvûí·Ó¦M›D¿‘Ýw(‘¢Õs°foÑÔ]D¤ìy›L2 8x0ÐhD¤JbÈ ,真ðˆU¼÷ƒœs33±‘ªÏzï³£Žñι3K@Æã?>§oß¾-½ˆH¼E¯ºX""’L³VØ(mõÃIiJb¤9ïý+Å<~"…Øyï=ðZèv $†ˆ¤žyyl« üº^Èo‘ˆˆ”¢®ÿ¼‰Õ>3˜pD$ê‰!""éÉû¦5¬ ¶d¤&p@R#‘ Žš#Ì‚‘`(‰!""ée0Ö ÈWå±ÿ3` V13‰q‰ˆH°VbÍ=g?½‚ GDò¦å$""Röe_c î®D“FDD$âQ×gæ@Ÿ`‘¼©CDDʾqÀÉÀb '¶d¤ ‰ˆH*z hLö 4Ƀ*1DD¤lZ œ ¼tǴݼ<œ ìT€""’r²°d÷ûÀ!Ç""yRCDDÊ–_ZXsÎMXÿ‹WG€ŽÀ-À1ÀçØÚçK‰RDDRQx鈾À¦”DìR ”–“ˆˆHÙr.ð/¬qç ¬òb6pZ&4:‘´§$†ˆˆ$ßÀÕÀmÀœÐ×ûÈ?‰!""’,ÿºd°8 ø8 è 4Klh"¢å$""’lŸaŸXµ¦¯bk‰•À‘Ò¢.–À˜tΆcU…}€‚ M¤¬SCDD’ë=àHà 쓬ÞÀ@#)™ý€€W€, 0e‰$~¼DD$¹ž&`ÕM€Oƒ GDD¤ÄÄšzž--™´>~ ,2‘2KI IŽuÀyÀ\ ðð4Ð&È DDDâ`°K`4ê1º£ŒI¤ŒRCDD’c3ð'Öµý:,©q)P+È DDDâ 0«0|è Ü Üü…5‘¸PCDDop 0ûtê  [ ‰ˆˆÄßfààaàn`Öøóª ƒ)[4bUDD¯7–6ˆ ëŒ>•‘²§ðPX‹ý½Û |ÚŸ T &4‘²B•""’x÷›°Fg}±O¦ˆˆH‚Ô}톟ԞŽv–HY¡$†ˆˆ$N6ð pð¶Nx"ªÂ‘²ï ìo^màQàVà |A‰”~JbˆˆHâLÁ>uê ,Â>•šƒ%5DDDʲ£À¬?ÆÛØ”®EØ8ÖÁ…&Rš)‰!""‰Ó˜Žk†ujwA$""’d­€‘À9À| ÖT½1DJD=ED$1Þš퀩ظåF$""’|ái\ó€NXRã.`6ð^h•÷YÅ>ôàè¨-ßàÕMC”Ä‘D™\œ<ô 6‘@ÍŽþ%øÃËóèÓB—°šX[l‘´§å$""’/c󱊌o‚ GDD$P=±ªD‰ìý‰ˆH|Í:³€c€¯€@› ƒIJê‹ì%1DD$¾ª•#˰>=ÐH9€¿€Hé¦$†ˆˆÄÏz 10øø¸1ЈDDDR_"Ý ›ˆHêScO‰ì N¾6”ˆˆH)ð1º¾8È@DRŸ*1DD$>ð.Ph ôÖ5‚ JDD¤ðQ×õwS¤@JbˆˆÈîÛ|†}š4ø È2(‘R¢9P¨U4ŠH¾”Ä‘Ý788˜t&û”ˆˆH)ñ°X |›çj ¢$†ˆˆì¾s™À6 ðP°áˆˆˆ”1ƒ@$U(‰!""»çßÀ4àpl"Éû@Ë@#)k¶âè…SE†ˆ’""RrÙØò‘ãÞÀBàtlY‰ˆˆˆÄKà ì#‘´¦$†ˆˆ”\9ì-Õ `Ö˜lI ‰ˆˆ¤¶Ã±å—]61¨ TnzàùÇÁ8-/‘ô¥$†ˆˆ”Ì»XÆ×À‘X#ÏIÀÆ$""’êP¹KuváÙ ܆%0Æáh‰-â¼.ˆÐER’""R2GacàŽ.þÀÞj‰ˆˆH|x&-¢Ÿ….OáÈÀQ)ÐøD $†ˆˆßB !ð6ð%–Àx&È€DDDÊ(Ï|µ±äÅçÀE¡=o/—H@ÔÝVDDŠgð7l”êãXõÅ—@fA‰ˆˆ”ažµ8®F…¶¼ tN ,&‘€¨CDDЧðPë…q°M°I$Ï€]Àë@'à$ÖÈóI j€1‰ˆˆ¤ Çñ5ðp(qðð!ÐW¤Gùž ŒR$¡”Ä‘¢›\ œ€-%麈ˆˆHrxžÀÑëNU8't)ŠšX ¥H©¤å$""Rt}€9ØÔú6X"CDDD‚Ð x-è D’MI )œ.Åú¢ |ŒCíÄDDD‚â : ‘dSCDD ·(œt~:‡”ˆˆˆˆ¤%1DD¤`;=€W€™Ø(Õ£MA%"""ùðQ×…H‚(‰!""€R„U^Lf{“ˆˆˆä':‰Ñ8°(DDI )Ø@K¬ÿÅ™À<ô–HDD$u­º¾9°(DDI Éßt >Öûü;`+09È€DDD¤_ìÐõœÚá„Ã%7,‘øÈ:IQ«€NØ’ÁXŒ Ä©ŠˆˆHª9=êz§<ö?ü†Çã…Í+ê_÷Çð¼TäHí¡E>vàÕ6\ ¦$†ˆˆäm?l ÉÀ±À¹XEF¥c‘ÝÕ èºÞ‘âu¹Ú»˜ÏU hZŒãwóñ% i9‰ˆˆä6 4FÓ€ýQCDD¤ô{Ï8NªŒHq©CDDr› ü8x [Jrt ‰ˆˆHnYØТª ÂQx$tß:‰L$QT‰!""¹õÖ’ÔÚ GDDDòà™§N1.5ð,šcõ•Ër<âÀ/XŒ©À–D=X ̾HÀãK§$†ˆˆDlÂVÇŽþƒ--é`L"""_ž9À!X%G´L à°zÌœûãád öNãà<¾”qJbˆˆH„ZgÙ@{¬É§ˆˆˆ”žì<¶uÝ{$à™«D]×R)6%1DDÄlÄÚ{=…“V΂<ß∈ˆHY´5êú& |ŽýÍŠùxyoFWwŒ)æã‰¨±§ˆˆ„ÜÌÂyž ŒÖ£t·ˆˆHú¨ƒ-))GÞ£WÿÄÑ8 Ø\„ÇkǶ `WèÒ¥„qJSCDDÌ#À¿°þ]€Á@“@#‘ä*Oîê‹h+›ü<"ùÒçk""ïµç€°U°+HDDDROpdÐAHzS%†ˆHºû¸ëþ8Ð ø(ЈDDD$5u"±ç.-e„’""é®ð+p76‘ä,`$z!""Rö ¤è3Èp0¿¶k^;¿Ì&¶â¶h5¯Çk tǦ Œ&#vISJbˆˆ¤³a@%à\àUàFàk”À)ÀóÏÃé§Cýúv;+ { nº *W66‘bñ¼_¬ã/oåØ: KF”Ç&älY0 Ïóy<–&/ñlÀQG]¼µJþÔCD$-.޾Ú׈Hj{øa¸öZ8î8˜4 ~úÉýûC°}{Њ$ÏsðúþDštfåØŸ™Ïcy ž;-8®wÕ†Èÿ(‰!"’®<¶„d.pp<0:ЈDDRÚÃÃwÚõÅ‹áÄ¡E ?Þ¶Ÿ;‘±t)L™û8Ó¦Áï¿'%d‘d¨’ãöÊ"ßÓãqtÂ:t ž®_hR)‰!"’ŽæÍ÷€†ÀlI×cIa‹Ã~ÜøñðÞ{v}éRKtœz*Œgɉ¡KèØQ‰ )3¶?ËϚżÿ&ìI#<ÿ²p\ߥ,QCD$ít.ÂZsMǦU2(‘ÔÕ Œ å y÷üÀEY£cG˜?¶nµDF•*pÒI°y3üñGl"cãFøå—ØÇúõWX¿>/F$¾öê'ûëÞžoñô6Ḡ˜ Áq@¼•²AI ‘t³ë>+Þlô4"‘”—™ ¿ý{îYðqýC¿Oûô >ö?à²Ë,Ñ¥ tèsæØ¾¹síöÉ'+‘!iÀQ ˜ƒ½;y«ÊXlP’ª”ÄI7—-±þaKIÆ‘ˆH`/.Ú¶~ýàšk`Æ‚oùrû:tháUUªÀSOYcÆ X³Ž8jքÕ+aÖ,%2$-œ4Æf¦Ý¬Å±G&Žíy\ ùI”²LI ‘tóààlàd¬x¥@# ĸqд) Ù6x°m£Ññ5EœÜtƶ”¤bEÈÎkŽC”ªU­Q茑mÙÙ–(‰¾ï¬YлwÑž_$ ~fã²°Y[ÔZ{WR ›tR!êvôEÃŒÓXFЈˆH’lÃÚf]< \}Ö¡¿"’†Æ³I";vÀ-·À!¶=\…ѽ;<ÿ<\}µÝ®]Û’[·ü¸K—ÚØUç ¡vm0&O¶>ù©RÑÐIIž{{ƒCÒ—*1DDÒÅl /И4Ū2 2(‘ä›1#’À[¼8÷2’뮋TIÔ­ ï¾»D¤qch×Ê—·Û+ÂÈ‘¶+<Ž[o…®] >FD¤,P#Áœsgou¼÷Wxï?ôÞÿæ½ß>Î{¿Ë{ÿƒ÷þiïý‰À!À`¶sn7 ‹cçÜcÀT »€C[½÷—å¸ÜuLìsß¶Þûg¼÷#GŽ9kþüù ><¯BDr™…u>z«¾8x-È€D$?óçÛòŠqãìùAƒàÆáŒ3`øp8ÿü²“ÈÒ²eVõ1c IdœrŠ% /¶ë`û H^lµjA«V±Ûš7‡:uì{£e®ÙX‡–¸ØDDR‰óÉZx˜¦œs€UÞû•!8çš¿zïwÅ7²˜ç¸ x$tùx 8Â{]aQø ¸×{ÿpõ°Â{rÔæq-[¶ìR³fM¦L™R¤˜† B¿~ýÐ÷§H ylQØf` pnÔvuªI)áÆÒ¥׳'ŒQøDTwÍ5ð ùïoÕʦÄ»«Vf¦%0~üöØÞ:w¶žçž™’™ ï¼c0©båJhÑÖ­Ëÿ˜¶maÚ´Øé'Rª . :ˆ¤qtÆã;ðTpÎí†yïû$$6I9ªÄH0ïýÂ’&0B÷ÿ%‘ Œ/€C¼÷÷ù}ÎÓûLw£s®¿sn¢sîuçÜqáB#ͰÏ|c4mڔŋ' tɓÖŽô.ŽÃR”J`ˆ¤œ_…Õ« ?îÇaÆÄÇ“h…-‹Øg:Ô¦qÄSÅŠpóÍP¾öóÏ­ÄÇÛdxºòJkžyÁVé±ï¾ñ}üD(WΖµôè;vä\ݺÖCã¿ÿ…?þ€{ïM^Œ"»Í³–È4’ιòäõ.ÆQ'ÁQIŠR%F’9ç÷‡nŸ <~_ ŒvÎ¥âäÍÀ'@ï}WïýiÀÀRàIçÜ>XsR€­9ï\µjU233ÙYÐ,³<Ô­[7ßË´iš´$’§9ØHÕðdF#à4a]$EmÞl} rðÁ‘c¦O‡­¹þÒ¦® ¬ŸÃÀwØí‚d‡ºs]tÑî?·÷¶4%ÚÙgØ1ðî»püñ»ÿÉе«ýûUªd•"ƒÃW_Áµ×Úþzõ ZµHSØý’Ö^$)Vp)\hUb$ßýÀ2àÎÐí>ÀZl^ÀØÖ“€ÑD—ïýÛÀÛ9¶-sν.eO=P3çýÿüóOêÖ­K…b[ÿå—_òÝ× Aƒb=–HÚ8 X€ý¶é\<hD"’÷Þƒ¾}aÛ¶‚[°ÀNP÷ØÃOfgÃÂM7%'Î’úë/8ïü°dûÞ{v"Þ©“Ulßn¥ðç—^Z¶;vÀ]wA³fÖÏ Ì9«.pÎ8‚Mç¸ë.«(Zéݽ»Mø1T4}þù±5FŒ°Šƒ¯¿Î=b33“¤øïmòÈ»ïZD˜sðÜs–°™=ªVµ$ţƞp_q…]öÜ3öqï¿?R}2p`ì}žz Ž<Ò¾FŒ°ï¡o¿µª•“N²ÆäÉV¡rå•VRV9gU,a¿üçœcß[¶D¾¿DDJ=ï½.I¼í€XïÜÚþ@èöDÀße¡8ϱ½05´ïk`ÄN©þ |äœówÜq‡/ŽÁƒ{ûö‘"yÂ{_×{ÿ²÷~WhÛºà‘²cçNï{öôÞ>ËÎûR±¢÷£FïqGŽô>#£àǽðBï³²óº’-+Ëû–-íuU¯îýбûß{Ïû™3½ïØ1vߨQöï¿c‡ÝÞ¹Ó¶ýòKìý:Ê»Q£Øíóç{_£†÷W]åý‚ñ]Ѷm³çï÷Ü3÷k,©)S¼o×Îû.]b·ÏŸï}ùòí³÷?üàý²eeç{©(²³½?âˆÈ¿Ão‘”Àk>Î]JÃØ :]’wQN6ɼ÷ß` WÇ{ïŸ íú¸èêC?ùk3ÛÔ{¿è\Í>X Ü´óÞÿuœÎÄ^ßFàÏÇ|΀’½HºêƒRí ´¦Gw‘â{úéȆüdfZÉúÚµE{Ì+àâ‹c—äåÍ7­J¡´Š^&S¾¼õu«*¸ë®ØcÏ9Z·¶¥ Ñ“Jºw·eáꊌ ÛÝ2ê?¬±#äžè1|8lØ`Ë8¶Ç¹?ÎŒ¶\#¬råHƦMVµÇoÏ5bDìöîÝ ¯ÖY³Æ*{êÔ‰Tµ¤çà7¬ ê½÷æþ¾x÷]«Ð)”ÄH0çÜqιKsÿ{Kâ½ÿÝ{ÿ’÷~jÔ¶qÞûg½5® Œ÷þ{ïý}ÞûUyìÛå½Õ{¥÷þBïýãÞû\oY½yÍ{¹÷þâ›o¾yy9Õ0Š$ÎgØp±¿m€oH’`åÊÜË/ŽÿóÜpCá%øåÊY¡ví¢=fݺv‚½, /=zX?…ÒfÑ"[zÓ¼¹Ì‡|²-¿ëÙÝcwx üö›¬^rIì¾pƒÌ6m,ž°ï¾ƒã޳$Ñ–-¹sÅŠÜI¦èï¯;î€cޱå›7G¶Ÿz*œu–%eî¿÷^W4ç fÔì³ß·Æ…©RÞ~;=—R´h3gæþxþyë[Ò¥ üùg0±‰ˆìŽ4ü•žtհʆåιYι‡ssÅ›5*"’—Ø$’¦À ð2pKAI¢-]jŸN÷êIdLj£ï»/¾Ï•‘=Vð1Û§âÅѳ'4mZð1ƒ•ÎOÏ¿ÿÞª'V­‚ˆÝ7h¼ü²UD÷/Ø]ÃÿýŸ} ûê«È‰~ÎäÆoÀ´ipÍ5V©í÷ß-Aqþù‘DÆÄ‰ÖÏãá‡íöa‡ÙB…eËrŸ$¿ý¶Uïì¿Ü^^.{íe±g2g¯[7k$š®öÞ;6Y8nœ%&~þ96É&"RZ(‰‘`ÞûñÞû†@s`¶”d °Þ97Ê9ws®ø³GEDÀºÕüˆµÜí ¬/ðRÊ-] 'žh'§~heòÍ›CÇŽö‰øý÷Ç?‘±qcÁû³ó˜©ýé|ÎmK–ØÉåÂ…?n¸™cªó>¶4ÿ¬³ìÿàÉ'­J"¬iS«^HFe@›66é¥[7[R–•YšqÒI±É†wßµi)üa‰ˆýö³äEçÎ6æõÎ;-‘qÉ%–Hxâ ;!ËÙX4jÖ´çý׿ >nëÖÄÇRZ¬XaB»vY…ʇZˆHi£$F’xïçzï‡xïOÅVªŸƒÍ¸XàœûÍ9÷”s®›sn@ƒ‘Òa*ð¶”ä_À¯À¨F¶ys$öçŸ6… º7Àý÷Ã!ñyÎ5kàòË >fÁ‚HâäÃmŠF»v±É”3 Q£Èþ1c ŸšqÑE¹Ç¯¦š/¿´I=»}ð`KTdf–|zËîªTÉNZ?úÈ’aŸ|bcK!¶BcÃK°D'Ö¯‡¹scUwÞ /¼Ï>k•]»Bµj‰}-y™3Ç&œdܸÂ{º¤‹ºumºK… V‰{ –ØúôÓàb)%1à½ßî½ÿÄ{‹÷þ0 0¨ ¼ ü;ÐE¤tXƒýæ8x¨Ä©‘ž¤¦jÕlLdaêÔ±~ñðá‡ð믅÷ V%rÍ56uî\K¦´haŸâ¼%Dzõ²“©¢,ùé'Kv¤*ïáúë­©æ£Âòå‘}‡À”)pë­ÁŘ—fÍ «à9ë¬Èöòå‹6޵AëíÑ¢…%>üж%Û‹/¾";ÛþoòªJG×^kIÐsΉÝ~ÝuösúÿL\""Å¡$F’9çîtÎ]蜫Þæ½_â½ê½ï ì–ˆˆäÏgós±å$J`”i»vÁgŸY‚¢U«‚}öYhØ0>Ï{å•v2­B…Ø%uêØTgŸµ’õh?ýdŸð†—[dfÚ‰ÔÍ7ÇW¹²U D¯ß<8÷T… EÏs.Rñ²eKî‰#wÜYr‘J:ÈþOú öˆªý9²h+† ƒúõízô””d2Ħℕ/oÉ”è^#­[ÃØ±éÙØ3?9N<™4ztü§ØˆˆÄ›~¥'_ `8°Æ97Á9w½s®~xghÈÊüï."ioÖ]gPx˜‹-N“”–×§ÁEý„øÊ+mºÅÍ7[³¾‚uJHQõïMšDnïÜi{öŒ$0jÖ„§žÊÿ1¢í½·UlÜr‹Ýï m©Ìʕ֓!#Ã9AòÞNÞ[´€uë"Û;v´°ØWåšíUzÔ«:ÞÓ¢F‚÷'Kùòörá…°Ï>0k–õòX¾Ü’­[[­––ØåküxûùK|~ô‘%EDR™’I潿hÜ da+Ú—8ç¾qÎÝíœ;,ÈøD¤ÈÀšxþhŒÄ–“HÊÊÌ´Òýðt°“ö¶mmÝ~XV–õ,xòÉØûŸvš}ݸі(ä¶, 8æÎ…ÿüÇ)y¤5‹1¦O·Oã+T°e$°çž?ÖˆpðÁ6õdæL; //éÙÓz|\yel³È Mž —]f1ýóŸ±û}.¸ÀöE÷ž(mºvµïèþ*yéß?÷øÕ „_™BR«–%/”À(ÜñÇÃgXbj̘Øïßµk‹6ÆVD$Ùœ®‹”¤ 5ñ<è\ TzyïËRªq@—âÜaÈ!ôë×}Šä°K?×6ï?ÕŒK ”™i½  ;t°®#ìSýJ•àƒì$ò¦›,Q±¢}ªþÔ{Ëû´yÛ¶¢=ç}÷>¹!–,±×QPbÑ¢¼—»ìÜi¥ÿo¾ieíM›Ú§ë©¢KK0•/ocT[´:¢øÚ°Á–šä¹š—‡†ü#ñ1Iâeg[ß›è¥AÛ·[UÎo¿Á¨Qpì±ÁŗƆ—DiàœÛ óÞ÷ :IUbÄ9WÎ9׸¸[Õ^X¬ 26IaOÕ€GßP#–.µ“ï¥K#Û¦NµäÃî¬Ï™ÀûTÿé§#Ëvì°FœãÆA÷î‘ûEßg=¬£bÅ›bvê·ß^ò˜wÇòå0o^ÁÇä79eð`8óL[–±m›% ‚¨ÆØ²Å’@9›>ö˜ýÛïÚÿ.ƒ-¸kÔ°ª†Â–“tébÉ6)Ê•‹M`x_ _}eÕÇ›ˆH^”ÄH2çÜιQÀ:à[àŸ@6Ö’¯¥÷¾Ž÷~R€!ŠH*û? ?¡ßÀ$ RAw¢XºÔF—ŽmÚXÓÃÛn³ï½g †’&2–/‡Å‹ ?.\xÖ±c¤§ÅÈ‘±Ç l‰>²êúõ­Œ~ÅŠHSÉN, PµjÉâÍ);Û*CŠ’LX±ÂNp [ŽðÄð꫹·Ÿ{®5ˬVÍú|ôrHÉâÞ]d“UzȪFÂZ´°i$wÜa“1Ê¢îÝíû®bE«Ê˜9Óþ_ÃVºt±ª!õM(»î¹'2–öÄíçUD$¥xïuIâ;åðÀ2¬/F• cJÂeœ/¦Áƒ{ûö‘ÿæ½ßº¾Ú{­÷þãàÂ)+–,ñþàƒ½·4Bþ—.]¼ß¶Íûo¾ñþÝw½3&öq òþÀ½ßwߨíãÇþØàýàÁ‘ûüýïÞW­êýä÷„ Þ/Z¹½p¡÷½zy߸±÷ýúíî¿JÄ×_Gb<à‹mÉ’ü0 ð×Ú¾½÷ý•÷ý?ùÄû-[âI̘á½skÏžÁÆ”qãrÿ?¿ñ†ý HÙ6s¦÷õêyß´©÷ë×Çî[´Èû¬¬@ÂJG¯ùàßÃ—Š °tº$ï¢JŒä»ŒUb V9çF;çnrΕ±Õµ"R, €·ó¹¼ôêc Ð>NN $Ò2eáB«–(ÌܹVZ}Ûmл7<ø`ìþÌL›Œ°zulߊ¢6z<úèÈõ°ÇyóÍü?ùäH_‰ÛnƒÆíô ì~ñj¼øÅ‘ëK–ÀË/\åÑ¿d$ë¾ûZLS§Z…@ûöÖ÷¢Zµ¼ïß¹süªHŠbÉ¸âŠØ©"GíúÞ{0cFòâI]ºDƨ†]t‘*0ÒAëÖÖ°wÌ›¶l™ýüvë›6ŸˆHFФïýÀι:XSÏÎÀ­ÀçÜ*à<¯%%"égÖ%§0/†.MP#N8ÁlžyfþÇT¬“&ÙIݾûÚ¶Õ«c©S'r}ÍhÐÀ®‡/̰a‘DFq'\~xd9J›6¶ž=+Ë&…ì®[nÓO·~ãÆYÿŽÂ&>ôïÕ«Û‰ðÁÛ¶ñã-ñsë­ù'0ò²b…%N<±ä¯!? Ú‘mÛìß*z‰ÈÃ×_Ú2víâÿÜ"©ì€booÞl¿–-³ËÿkÓzDD‚ $F€¼÷+sc±¨, '°P;ÐÀD¤tpAPvœpBÁû<0RõN0”ÄX½:’ĨRÅú<„“ ùyþùHÏ…â:ë,;Ù¾ðBhÞ¼ø÷/L“&véÛ·è÷Éù:22Š>-eÃçúÖ[–<ª]Ûªe khZ\Y’±c­Â䆠U+ÛW¯žMf(§šUIsÙÙpÞyÖhàºë”À‘`éOs’9çÊ;çŽqÎÝ¬Þš€Ã|Ù¯*"’òÆŽ-xÿâÅ‘©%áÊŠ¬, Ö¬™œ|üq;«QþùÏÂc¨_ß*J¢Z5«rHD#cÇBŸ>0q¢@­^»¬¥¤¦Mƒgž‰Ý6x0T¨`Ïn^¦†ˆýœt’}=í4kômÅŠ`â‘ô¥?ÏÉ7˜ÜŽS½ ¨ç½?Ö{ÿ°÷>€ar""éë“O"ýò³c‡-gX±úõ³ÒêÍ›íä7¬aC[‚Я_ll\g¸ ¡B[nñÆж­m«_>ÿ<²ô"Â=:vÇ;ïÀ”)±ÉšdèÞÝúbì·Üx£zìØq÷sà@8î8ûÿùí·Èö¦Mí9Z·†»ïÞ½ç)«úõ³ådo¿[5mšõãyôÑàb‘ô£å$É÷1ð 0Á{¿­°ƒE$Uva‹ÍÎþ\8eÕØ±ö)|a4€½ö*yÓÉûî³%G ]»Ú¶Þ½íúöÛã—Àøî;xí5ëóÑ´©õu(‰ìl[^±v-ì¹§}Û·ïî'Šb=¬±`óæñ[BrÊ)¶äfçN«¸øðÃȾ´¾'ª¼É_çα·,°^BÛ¶Y°[·²S &"©M®“Ì{ÿ¤÷þC`çÜUιGC“INpΣՙˆ”yÙD`š%î ‚sÏÜ.WÎNrûô‰TZtê£GïþÔŒ{î‰$0ÀNœ_x!~ ŒÇ³$ÉÓOúuö)itÕAqüü3¬_o×7m‚÷ßà‘h-[–<á½}b^Viqé¥v}ôh˜0!²¯re%0DŠãÏ?-i±v­Ý~î9%0D$yô';ιû€Ÿ±¡‰·C€ÉÀιF†&"©&ú·´j·¢|y;á½þzkÀùüó–Øxé%+Ÿ>ýôø$0’áÔSíkùò–,1"÷”¢jÑÂ’o¾iÓNêÕËýIl2Ík¯§0[·Â1ÇÀùçÛ²h>hýCÎ<ÓJàE¤d*Tˆ$_û÷‡+¯ŒÝ¿aCòc‘ô¡$F’9çÎþŒZ{u€îØ)Êdç\•à"‘”±£Zhl8eÉ“OÚxÍhO? ?þÿ{d[¼*0’¥ys›²±d‰-“9ï<«2(©Úµá‚ àõ×m¬ba£UãmåJxä«¢hÖ .¿Ü>.HÕª‘É0o½3fDöÕ­ ¿ü|`“ID¤dªUƒQ£àÕWᡇb÷½ü2z(|ûm0±‰HÙ§$Fò],.ñÞÏñÞoõÞ¯òÞ®êÇ¡ˆ¤Ž¹ÀŸÀ7¡‹ì¶§ž‚›n²&³gÇî+ åÐW\a'ëeÁ¼y6ñ%<ÚqÇ›X61l=~Îé2Û ‹ˆÄ›’É×xÑ{¿+çïý$`p\²ƒI«WÛו+¡C›¾Q–mÜS§ï>³fYÊ–-‰‰©¨N8ÁFÚ–+g Eçχsαõ÷MšØ'À‡ gœ?ý¹_ÆpË-Ösä„’?]E$Í›={ÚÏ[¥JV U±bÐQ‰HY¤$Fò­:äµÃ9w Ð8tŒˆˆ$ÀÀ!öéáÆ0lXÐÅßΖ„èÝêÔ±¾Û·ýþÏ>kcN÷ÞÛú` š¸X òå—–HÉΆI“,étÐApÝuV•qß}¶Ìe×.íÎ;-±1p N¤D’᯿¬W†s6!©}ûȾìlû9‰%1’o,ÐÅ9wzôFç\UàaÀƒLD´˜ìº„gÕŠÚ–ó²OòÃ,­²²boßt n'êÏ?LL‰4j”½¶‘#-y±q£%5ŠêóÏíëŽðé§‘ÛÉôãÖ¨4ºdÙ2øý÷Èíè ‹ b_cµjñ›ú""…;òHüâ‹Ö'Z¿~ö;ióæ`b‘²%#èÒÐ@àT`´sîg`Öܳ%vJr»÷þ×ã‘D˜ ô)`ÿ&`%PH’bÐ0á‘•yóæÙ(À¡CmIBØØ¥,:ã ¨YÓNòÏ:˦‹œtRÑï?{¶%.ƳË)§$.Öüz¨MXyb;öX˜322‚_þ"’î:ÈÆSG{â k¦ pà V¥!"²;”ÄH2ïý_ι¿7€C-À$àIï}1W.‹H©°ëxS˜M¡KE”ÀˆƒíÛíDø÷ßíë›oZO…²®R%K>vì±Gñï¿Ç¶åôPÍ`tcÌdÉȰ‘ªcÆØØÔüŒ3gBË–°×^É‹OD ÷ÁÖŸ¬©nÎI&""%¡å$Iæœë´öÞöÞŸá½oâ½oí½ï­†ˆH|U® XO„;¬GDº| xÔQ%K`ä%zú@2ed>"¶jU[{¯†HêY°À’ Õ«ÃÇC½z‘}ÙÙÁÅ%"¥›’É÷ ðrÐAˆHŠ è¤±,:ï<{ó\½º]Ú´ :¢`,[¯¼Rð1›6Á«¯Âòåɉ©0Ï=ë×|Ì}÷%%hÿÜ· IDAT)[o…wÞ±K«V‘í;v@§N6òZD¤¸´œ$ù ©Ù"m5ØÒ‘JÀO.E³s'|ñ…½A;ùd›n±ukìé²nëVë)ñÆ0q¢}òyôÑмyÞÇO˜W\a×[µ²%8÷ÜcÉŸd›5 ®¿¾ðã9ºtI|L"R|½zåÞvÅ0y²]jÔ°¾=""E¥JŒä»¨êœ{Ó9×Û9w”sA)"Ið°˜RÊqâ=\~¹}æ™Ø}mÚÄŽüK+VÀ¥—Ú„‘péöرù=…ä‡ॗl¹FZ·.Úúù›oVC¤4ùç?á­·ìzûö¶ÌOD¤8ô¶9ùžš….ùõÅ?x'i‰HÉ-¢h?­yÍŠÉÂø„“îæÎµFrÙÙÖõjû¤>]5n Ç¿þ 矗\bÉü<ù¤}"žJrÐAP¾|òâÍ©ûzçv²óúëP¡\{-|ô‘%0.>)ž¬,øê+»~ðÁöûºR¥`c‘ÒÇù Zާ1ç\W N!‡MòÞÿž„p’eP¬ÏɆ B¿~ýÐ÷§¤¼qØÐä’ø0#tý`IÔ¾JÀö݈+}óT]³ÆnkË"ÒÕ²e°ß~Ö$³¸¼®©g´#àÌ3#U!YY6iæÒKƒKDŠ/+ ¹š4‰l_±Î>ž?<¸øRÈ0ಠƒ( œs;aÞû‚†ÙK¢JŒsÎí lóÞïðÞ+äø:ÀÞɈMD6¨º¾"È@Ê–ví`êT[bУGz'0öß¿ä÷M…XI´Œ %0DJ«Œ xüñØm[·B÷îðí·V=6s¦UjˆˆäE=1¯ð–s®Ð‚\ç\`6Ð"áQ‰HðvF]²Ž¥ûøc¸ûîØmMšØ›áœo”~þÙþ½6oŽÝþÞ{ðÛoÁÄ$"éË{[Æöí·v»W/K`  ¿ÿ9n×.8ÐÉ4w. »mÜ8K–‹H0T‰‘x»€žÀ0çÜ%Þû\S±s€‡€[±É%K“¢ˆÄÕÞXb"Ø\ȱ²[¦M³7¼Û¶Yÿ‹çŸôpØ[5mÿ³};¼ð‚õ”˜9Ó¶zhd"ÀæÍpÁ6Ù¥Q#«^¹áhÖ,¸˜E$=,ZdSJÀš2¿ð ýúA½zö{«Në3z4|ò‰}MFÓá¹sáÄaÕ*øã8ç˜2n¼*V´åŠéÖ0Z$¨#Á¼÷s€À…À‹ÎÅç:ç_·?Gyï¿Lz "?€MX£AÀ±”qõêE–K  çž ;vS*ÊȰIá†s0{vdÿ´i–ÀX¸ž}Ö­K~œ"’~5²fŸ=zXEØÓO[`ùr‘Ý¢…%.ÀÆEŸqFâ+2 Œ•+­Zäž{,±{õÕ™iÉßSOUE†HT‰‘Þûþ¡ÜÅ?€mÀιó€€êÀãÀ]Þ{½ý)ívE]ßc_ðTC¿‹¤aC{Ùµ+|ÿ½-•زEÝîsÊÈ€ /´“€K.‹.²É#a§œóçÛÒœ1c,ÁqÌ1ÁÅ+"éåCàý÷­¹ç½÷~üÿ³wßaRV×Ç¿—"(Š€ŒĆX# ±aKì¢þ¬±GcÁ‚AchÄhl(6,؂ذ`Gƒ lQ,QA^ïï;ëì.ËØÙòý<Ï<óÎûÞ™9«[Μ{Î /À¹ç¥=a¼ñF:>þølUÞÔ©ðÈ#éx¯½àW¿Ê>ç_ÿJF·ÙºuËžÿ÷¿SS¦”Çœ9pöÙðÖ[eÇ,©òøkrŽNdd:è¶Ž%Í#èc›Ïø$U¢5È&/~ ¼_èZ}à¤\T»Ìœ «¯žýµMxñÅ4vóšk U«¼†Wm]qEé=B6ÚN?=Ý–,ÉïhUIuS»vi‹Æ®»¦QÙ¥y碟~:m90 û=ì‹/ÒÏH¯]8‰qê©©zï¼óŠ&1þþ÷T¹Ö¤IÚŽ·"k¯ ?^®/MR%2‰‘CÅ*2îN‰1ÿ¬VRM¶0 ˜Kê‹¡J3gNªhÛxš6Mç[¶„{ïÍolÕ]Á«òX™q¬’TzôH[¿­âÚµK=;VdÀ€ôóHRnùkJŽKd|dCª…ž+tì¿ðJuÑEðöÛéx=Òöˆ5ÖÈoL5QŒiÎðái$mçΰãŽ&/$åßÒ¥¥W?äÒüù¥_ÿøãÜÄ!©({æAŒñ|R³ÏËCçä;I«`v¾¨[NÖ ýÞ§OÙ%Ç*ê²ËÒÖ‘]vIûÁO<zöLeÑ£Få;BIuÕÒ¥pôÑð㥯 !%c kÐ õCjÜ8]/P¯^ö|ñmrMš¤ó =ߨQ:÷Ã¥ÇñÔSi*–¤Ü2‰QÅB[†f¿‘Ým ×ÈgÌ’*ÀJ‹œZ}õ´§yÿýÓ/©çœ“~AUù½ür¶<ºaÃì¿Y³àá‡í´/)fÌ€7ß,{]ýú0p`Ñs眓*8,(ÚÜy‡²çûô)úœ™3ÓùÁƒ‹žüñÔG£x¢¤¸S/Ž'ŸL½„$内£Uo.ðFŸã`;©&ø ØØø/©*c#`ÍRžãÔŒ >öÝ7»m¤qc9ÆŽM£÷T1G “&¥¤ÅSOÁæ›§_À/¼0}ú¹ÓNùŽPR]ÕºuúÞÞ»wš˜i’RëÖ©‘çÒ¥©ââ¾ûÒÏ…ª4th==lXzܼ9tí ~˜’-bØ}÷4òµ¸ÿ~§cI¹`£ŠÅ¿úæ;I•ì#`àr`<©çe@ O)ÏS…üóŸpÒI°ÅéÓ®õÖKçë×7±²Þ{/1„ô©ä¦›¦z?ÿœÎ ”¶—´n·%ÕaíÛgW^™Î—’·ÞšÎWµ²#\_y%umÝæÍƒ½÷N•Ç“ïÛoÓv=IUË$F !4‰1®t{¢B#`qŒe´IÊ©Í;€€7;II Uª_L÷~˜*ÆŒNòR6hPút±À’%0qbÑ5|Do¿ö…KR®µoãǧÉSzôH[árÙ̹ ‘1{6´h‘Î5k–ªØ>ý¶Û.õejÑ"ý|2!å†;‰«ÞQ!„ÇB+ò¤| øk¤”K§Vpë¬AÚ62Xx¸0ÖË–¥O¶ ›;7í51N9%ûúëì'rZ9‡k–¶Ý)㤓L`Hʯ Œù˜FB6Q iSØzk¸ôÒ”ÔxòÉ´Õ¤@ŒÙê6I•Ï$FÕ»xBB8"„Т¤…!„z!„­B—_W'Ææ0^Iߟ¬àö)0 ø"óx2°)¡¡ [¶ þïÿ`Ï=aΜtnÖ,øÍoàÈ~ 6hPªÄ° üªéÔ ®¾ºô5={¦†v’¤Ò]tQê1Ô³gÑó§ž Ý»§í%’*ŸÛIªXŒq1pYa$p)©Q/„ð=ðMæÖhtZÓ€ëÄà(UwHÕª‚Æw¤Çl>Ùz饴͡ Cý?þ‘>íºà«*Ã:ë¬ÚuIRV‡EŸwÜtS:>þø4Q«¦z1²¬¬sR¾Y‰‘#1Ɖ1ÆÃ€ ³IÕ-€]€­HmG{bŒW™ÀT›pB6i2ÆóÏS7lœvZ:6±êæÍK ¡Ò<ù$¼ûnn⑤Úä/k®IÇn·Ý–ßx*"êÃCàÊBçZ¯‡@Z©Jåg%FŽÅ¿þ–¹Iª ~ ´&’†&/œ¿ÊvÚ n¿=í^‘RI®*ÇÀi:Ii.„ý÷‡Ï>3q$I±í¶©GÆkÀsÏÁºëæ;¢òÉ$0îÏ<îü4s­ p_#å-H©+1$©,SqÀ `!Ð0¿áÔÇ Púš~ýàðÃsO]0htëVúšæÍSCU’T1{î Ï>›nl=¿h|pÕZÝO`dìHJ`@úàû>+2T]˜Ä¤²oÅkÿ‹Jq×]ðôÓ¥¯Ùe—ÜÄRW´l™Ævë–ª\.º4HU1묓£G§Q†’¤ŠÛi'è\h&áÒ¥i2ÔCAß¾ðÖ[ù‹m…| 0‰!IeÙ¨Ÿ9n”0öM÷ùçi”jin¹%;µD•£ ‘ñàƒ0xpú¥ú°ÃRCÕ§Ÿ6!I•%ÆTuøè£éñvÛÁ–[æ7¦âbd)p4ðYK¯r;‰ª “’TØ4Ò–‘ÂÆ’’õ2×>ÉuPµCñQso¼Qös¾øÂí$U¡eK8(S|ôÑ)‘Ñ©ì¼s~ã’¤ÚdêÔ” †Ô/ã‰' iÓüÆT’L"ãµ2–ËE,Ry˜Ä¤ÂNÞ.vn°(s¿(¥¥–7{6œ{.l¼qúô¿Àå—§h¥YsͲ§ihÕì¿?l´Q¾£¤Ú§M7ú÷Op- mO6­úL/ÉL!Ù«ŒeÇTÂû4 óBÈ—ö!pÒª¾¶ê§“HªÝÆ?•cݧÀ¦ÀéÀcUQ# ž¦^üéO°ï¾Ð¤IêËð лwºvÉ%©)Ú“O¦1uÝÝ»vÍ÷WP»Õ¯_öIÒÊiß>»¤À̙ЧOšõÍ7©ér¾„@#àY`í2–þ.Þ‘+Vò}š£€=Þ!ðG 5©©h‡X#F®Z™×VÝc£Š…¶*ÚÂçÈãȪˆGªsÎÊ)YÄ`{ v¯I~þ9û‰S‹©êâ„૯àúëá ӵnÝ`ìXøòËÔø `ÇaÓMa‹-L`H’j—9sÒä­‚q×_ W_ §–­Nœ1¦ó!UOŒ, EåXú#ðøÊ¼G±™û=‹-»23ÆõªÌú3€¿Æ˜†Ú‡@{`ï¹yebPíb£êM‡åþ±Cêðû0ði`ã6¤2®æ2@I…| îÖ6)Çs6¯Òˆj”É“áœs`Ò¤ô ZƒÌO™ãŽƒü#{é¥lR"£øØÏ£ŽÊYÈ’$åÌWd{Bx`ªD<ÿ|5 † K9&Mbû¡C#gUeÖ'5²¹ Ø5ÆxWÓj¿O_“ú\ïŠmtËaÙ2¸ãØuWXThÛ‘G™í9Æ¥&e’$©l3fÀî»—¹¬ pðfô©¤·n|ž9ÞøOìRÖ“B QœCúMê`pyß0FÆC‘Q®‹€/ =þŠônY#T[÷™È¨ÛLbäAŒqIŒñï1ÆCbŒ›Å{ÄÏŒ1¾‘ïØ¤Zo!0˜¼Ù¢I­Èƒ±ÇÂË/ÃÐBEž!¤Ç ï¾›o/I’J÷ùçðÙgå^þ0®2Þ7F¾z’¶±GÒ4’òôÜx$óœ™Çë†Pþ¿%cäqR"c&°°}Œl\FJ`ôZë•ã嶇 U‚¨–1‰‘'!„߆þBx*„°Q¡Oa¿|Ç%Õ:V¬²ƒ†-¶HÇ—_?ü½Ö½{Jr¬¿~~b“$©&Úa=š7OC€.] óò›)–‡ÇÈü‚!Ð13þt¥ÄÈâùÐÔ8ôçr<ퟙû€=cdßâ[MJ7TzdÄÈ“1ò~æÜ  KŒL.¡bc)ð6ðßB/ùÐ+FÜ~_‡™ÄȱB½ƒ¤Ÿ'‘Æ©6vF…®Ég|R­²Œå{`¨TsæÀ…ÂðáÙsõë§³gg{aH’¤•×£GJd¬±Fú@à?ÿ>‚›n‚æÍY¼ü=F^-xN´žÞ íËó>!°V 6…ÏÇȘy«<¯#Oû[ÇÈ3å|ߣS±!p}4‰‘™%¼öÌBÇ…+6öˆ‘bdc UlÄÈäò¼¿j/÷åÞ™À¾ÀÑÀXøe<Е¤†7ç„îŽ1~§ø¤Úap i„jR.iwii‰ÕJ¹Vì±¼þ:´iûï«gFÐöé:Áùçç7FI’j‹=àË/S"£ÀÉ'Cÿþ<²îºÎò¯] ü*sëBªT(ËÀÞÀ!pTy“ÅÅȨâç2A¿Š‘Å%<¥©&¶p°glW¸ªdïóxlP,¹1(†–”QÝc%Fî 1fœŒ1þ ü ”ÝæGRé6 í˜ü¸žÔc6)‘1¥”Ûñù¶ú8ãŒtÿÃiëHa#GÂàÁ°ZOôH’T™ '0 ´kÇ¢ÌØÓ_’!°ÙßTž‰‘[‹?¯xŸŠhÌÍ'5=V=y"ÛIòaiTÒ°âB;›eÖHZŸ—zh?@ÚÀµi˜±~ñöÛг',\ë® —\’½6thÚJ2x0tè¿%IRVŒÜ¯cüeK:!°!©î´iæÔK!ð ií@ýÌñÀæ1òÜ*†².Ùé 〳cL­ÔC` /psŒÄLÜË(áoieY‰‘{ÿv !< ìš9×<„иøøw¾‚“j´™¤ÏÆ?’¦Ÿ@j›»Q㪆¶Ý6õ·¸æøî»ìµí¶KU@=JH’TmÄÈ—1òTÁãÀýdjO$5ȬŸ9× ¸ x¢¸xøppH¡XnnÞö•ð^Òrüõ4ÇbŒ“ºëî ¿4ÇŒ&Í]>$Æ8+OáI5×ç¤j‹á™ûW`•?k¨%^}y$û¸^=2$Ï ]ßÀ:=I’j‚¡|SJ*KŒ,Œ‘ÝIíÓÿœÏÝ3˶> ßæ26Õ þšš1ÆA!„á@ÒçÃóOGcŒóòœTSm Žþ\Gú\Bœ>\}5´j½z¥{€Þ½Ó¶‘É“á¸ãò¡$IZ1òPœHêO± +îþµè#S*ã}CàXR£àývËÜ kFjìi•¹*•IŒ<‰1~AÚZ"©43§K¹¾¸4¹|à<²».‡Tmh5EÏž)‰1c\z)ÜpCöÚw¤1ªn‘$©fŠ‘[Bà_ÀBVü÷ÝœÊJ`dìDùÚ¥¯W‰ï)&1ò"„°:p1°'éóãH…ï cŒå=$Õ “C˱nt¡ãwɶ›ªcb„{ïMÍ2}Â÷Ú úôgŸ…aÃÒD’Í7O×Z¶Ì_¬’$©ÒìAéÛµ n1òf%½ß @`ÛRÖ¼ œVIï'ýÂÏÞr,„Ð’Tì~)[Z0ŽhMÒË­¤UµÔÅVRÓ¦A·npÄ©Ygaý+Ô¯-Zä'>I’TùB`g²½öV¸ +ã=3G)cÙýJ¤Êd#÷“:oHʘc¼†4ï¹W¡Ožb“Tƒ­½6„Laç5×À7ßd¯m¹%|ò <ø üêWù‰O’$U‰÷€·Ë±î%R+ôU–™D²O˪Œ÷’Š3‰‘{»CcŒ“‹_ˆ1þ øš´µD’J5g¼üröqð·¿¥ûùóá¼óŠ®ßÈ1³’$Õ:12èGšxðp2p0#sîqààYTIo{#°ckv ¿VÒûI¿0‰‘{ÍI•Ë !4 íe³ìJ*Í:…Žûæ-Š*5oŒ*V:y2¼öZ:~ì1ØtSØ{o˜:5»fÇÓv’Æ¡C‡Ô#C’$Õn…wÛÆÈÍ12èÜBå&0>¤ì¿Y"ðQ%¾§˜ÄȇÿLJ–pm_`]RI˜¤™Vèøù¼EQeæÍƒ}öIý+þþwX° mÙuWØsO75‚ᅦٳáâ‹‹>ÿšk`Ò$¸êªìöI’T»ÅÈœ9¦ð’ù}à‡RUÆ–[ºëÂ>ûÀOÀË/›À$IRÝe#÷Ž%5õ¼˜Bø „ð9ðp ©`þ!„™™Û˹ .„ð[`&°e ×Ö!ÜÌæ„Æ…v*aÝ–!„×€Àô^½zmóÃ?TuèªéGï»o_Gå3¨ò{óMØsOøè#èÚN<~û[8xèׯb‰Œµ×†Ë/‡÷߇wß…úõÓøÕvíªîk$I’ª;{æÞD`IÖSUBèNê˱¢äÖuÀþÀ?H­ÿ<BØ%ÆøNæ5­€3€©Ÿ|òÉ]=zô`„ ¬¶ÚjUýe¨¦Š@}Rÿ‹»þÀëÀù ª| ³f¥Ç³gÃ-·]3n\JdŒ Í›WìõÛ´©œ8%I’¤šÎ$FŽÅ¯Ïw Å…š—g’Š÷KZ³ ppXŒñ¾Ì¹±¤F¤ç‡d–žt:Äÿ0nܸ3{öì¹Ó=÷ÜÉ'žX•_Šjª‚žwƒ€ÁÀ@‹ü…U1®úk,^ Ç {í ü-I’$áv’j „°i¡eC88ø )!Q’ßs)Ô¯#ƸˆÔ½à€B“Ìéßc =zô˜½Å[0bĈªˆ]5Ýà` /0ƒÔÐó`Í|U1ݺÁ}÷•¾fõÕË®ÂxòÉ4bµXo=8ç˜1£rc•$I’j2“9·_5 IDATBØ=„p{a¿Bç¶ !|EjæùcáñB³<„÷ °IŒñbV¼Õe+à›ãübç?íB Í€O‹?¹cÇŽ|ýõו²j…e¤Fž¯S€nÀ$RCÏ“ò×JèÚµôë¿þuÙÛH¾ý6»fêÔ´%¥qãJ O’$Iª,VÎÂnÀÓ¤ÿ/fε&Ë_4!õ¸+„ð»+£8½|bŒï–cY+ÒçäÅMÏÜ·#}¦JZתU+¦L™RáØJ{N«V­hÔ¨Q…_SÕÄ\ '©öçPRÿ‹Ã2ŸÌc\+©¬JŒO>ISK6ÞxÅkN; Ž=FŽLSM6Û l##I’ê²B} u¾ãPõa£ŠeF•Þ|cœ¹t°>ptŒqxfí÷À ÀæÀ‡y·4HSIŠ+¨ÌX-³†’Ö5kÖŒE‹±xñb6lXî7mWÊ(†±cÇÒ«W¯r¿–rh&)mW–NÀáÀƒ¤v°‘Ra5ÌÈ‘ðÇ?–¾fÑ"ØqÇ4½¤´F«­¤ÛÒ¥•¥$IR´ ©VwEröᯪ“Uo`à’B €½H®þüö!Rc{ª_c %w)(8÷0•ôMd¹u?ýôíÚµ«P`Ò¤¿êСC…^K94™T]Q^£Hi½©1< ̘Ûn ;ÂÇ—¾völ˜;·ü¯]¿þªÅ&I’T |AÚ²¾"Õíï&U1“U¯Kæþ…‚!„æÀ®Às1ÆÅ…Öþ,6Ì]xåö©cAqk‘:LŽ1.!L#m=)búôél¸aÅ¿¬N:Uø9ª¡*’ô¨&æÍKSD–.…‡†L‰ŒŽáüóS#ÏÌ®?óLXýå_gÜ880m%9è ·H’$È XáGE!+1ê{V½Ÿ2÷ë:·ÐxªØÚHM2—kŒY |¬Bh_ì|OàÛÌ7—‚u= /X´hQxã7Øh£r¦j¬Þù ân¸ÞxÞ~ö߆OÛ@ÆO÷·Þ :@Æ)™qõÕ%WWÜy'¼ürzN»vpÊ)¹ý:$I’¤šÂ$FÕ+ØBRøO´ÃHÛ.F[[0¹äƒªj%üX@¡¬!„-Hƒ1¯/´n(°i¡Á‰ë®»n½™3grúé§ç*V)'Î9N8!úiªÄ¸é¦ì„‘-`̘0>¸èsçêýÑ¡C¶OÆìÙ°=p%I’¤:Áí$U,Æ85„08?„Іôß|Oà‰ã÷™Ñ¤ûÆå+Þ‰1N !\\Bè |Cêë1ÔÇ£À“ÀÀƒ!„'F!„ÍÏ=÷\º–5ƒRµ[àÙÌq;àû<ÆRÓ¦ÁÏ?CáB¢>JÛBš7‡þÚ¶…Áƒáφ¦M‹>¿¤Q£GÃa‡¥êŒ>}à’Kàì³S寨±ÙĈ$I’¤¢¬ÄÈc€±ÀñÀq¤DÅQ…®?CšÍð#°_±>¹6¸ŒÔȳˆãuÀîÀ»¤dÌ_€>1Æe…ÖDREÉ À,à§ë¯¿þƒ+¯¼2¡«Z{®Ðq ©4˜6 ~óèÕ+G4vÞúõËVS\v¼óNÚRšS²c¯½`æÌ´eæLX° õ¸äøÓŸ oß*ü¢$I’¤ÌJŒˆ1Îú†Z«Å'[ò8ð/à©ãÌ\ÇWXf‚Ê„R®¿@¡&¥+X;37HÃ6·¬œUcn¹´!ðß|R> Œ2›»:u‚ÆS3OHÍ8ûõKUÍ›Ã6Û”ýšƒÃ¥—fÏ›*:,H#X!ûš=z”ü’$IR]f%Fŧ•À Æ8$Æ8"ß ©Ju©ÅmÇ<ÇRM`@š@RÀ(0n\ÙÕ…µ(a|ìÏ?gª;úõƒïkÈvI’$)—LbHÊIÕÿþçXÊáºëRåEi4€Ë//ÿkžp”§5ÌàÁiJ‰$I’¤¢LbHª\‹WòĪëÖ Ž<²ô5çž[rãÎiÖ z¨ô5ë¯gžYþ×”$I’ê“’*×+@-ø#|áBx¥ŒdLñI$åQ¯Œïº!Tü5%I’¤ºÂ$†¤Ê± ˜ ì\9·:Цœ·jöÝèõ×áÓOK_3dHvjIy,XPvÉ“aذò¿¦$I’T—T³?$ÕX×;“s€QÀ¢Ìý”rÜJhz™O¦‘¨¥ùé'8ï¼ò¿æÝwË/–½îÜsmì)I’$•Ä$†¤Êq*°° ð9°ð!)±QCÜsÌŸŸŽ~xù~ Š ¥îÖ n¿½ü¯üñË÷»hРè“fÍàñÇmì)I’$•Ä$†¤Usp:Ð xØØ•T]±q㪀… áè£S#Ïÿû¿t®m[;6%2ÖZ &M‚Å‹á7 eË”À3&WÄ!ÙDÆ!é5gφÝvK Œ'žHÇ’$I’–× ì%’TŠ=€½%À?€G€{¶ù ªd\:ÀÉ'§Ç ¤ÄÅ©§Â×_§s÷Þ Ûn gŸMdÌš;¦ëݺ¥-!lPñF!C Øu×ô¸ yñÑG°ýö«ô%J’$IµšI I+ç:RÅÅ.À³@_ÒxÕ[€£ó× \p\uUšþñÊ+н;Ü|3LœO? ÷ß_~ _}úôì™ mÛ¦[a]º¬z< ŒÍš™À$I’ÊâvI+çC`w`Ð KÚRRF3Ì|(H`@jÖyß}púé)0gr\ziJ&œvl³Mþâ•$I’T2+1$UÌhà7À@SRÆc™sËc\+°h¼õVÙëæÌ¹sS>.I’$Ig%†¤ò›œ@š<²œÜ–Ï JרQù¦}üá©7† I’$©ú2‰!©|¾š/“€½€¹À_»óVif΄«¯†­¶J·ÒtPnb’$I’´òLbH*ÛD`C`$°ð2ð5ðçÌõú¹ cÉ’””X° {nÆŒ4í£$óæÁEÁ_¤fž¥yì±Ê‹S’$IRÕ°'†¤²u.æG¯«ç.„%KàÐCᡇàÉ'áÆS2ã˜cR‹Ï>ƒM7M>vÞ9=gÝuaï½aÔ¨”Ð(Íßþ›m'œPõ_‹$I’¤•cCÒŠ½ü x"sß8X 80waN`Œ·ü˜ÓaÃÒýf“ú\ŒUö{4m o\9ñJ’$Iª&1¤ºèg ŒÊÖ&õ½èIê…q°.iI=ýt6Q–Q£`ÊhÛ6=Þm7Øw_˜=ÆŽMçöÞvØîº þûß4Võ‰'ÒZI’$IÕ—=1¤ºèO@»rÜ:Ó€]ÿ‘*0ÖÈm¸{ï C‡–oíÒ¥pÇÙÇ!¤~/¼gž™úg<ñ\|1¼ÿ~zm’$IRÍ`%†¤²õZ óÂg¤$ÄŠ¬½6´o³fÁ:딼¦xЂ I’$I5ƒI IekÜ–ß~ü±ôësæ¤-'n˜ª/$I’$Õ>n'‘T¶Eù{ë©Sá‡ÊÞî±`Aš,2~nâ’$I’”{&1$ÁÀ@š±|V£œGÀK/ÁÖ[Ã9礜eùàøöÛªK’$IR~˜Ä”v¾DÒÔ’&ù `Ù28ýô4idÄ80õ°€´]d›m`³Í²ë[·†çŸ‡M7ÍO¼’$I’ªžI IЪØã6y‰¢ˆzõàÁaõÕ!F¸î:¸õVXc4FõÝwaâD¸é¦ÔÈóùçaË-óµ$I’¤ªdCRJb´Ï¯´ÈO}”¦‹èØn¿=7k–ù%ì»ovÍÉ'Ã'Ÿ˜À$I’ê“’`"PÐKb&ðŸÜ‡pï½Ð­}tª¼(pÐApË-©òbÛmS%Fq%“$I’Tû˜ÄêšÙÀkùby#GÂܹðØcpå•E¯|Ú2"I’$©n3‰!Õ5³€¯òÄòîº+Û”óâ‹á¹çò$I’¤êÇ$†TWÌž&õ¾Ø·Œµ•hÊè×&OΞ{óMØyç´E¤@‹ðÈ#°Új°ÕV°á†¹‹Q’$IRÍÐ ßHÊ‘1ÀAÀ0à:àdàÿ€3€]ËxîJ6úœ2z÷†?Ný,Î>~þ† Å‹¡gÏÔ¨³`«Èæ›Ã³Ï¦ñ©MªÁ˜WI’$IÕ‹I ©¶[BjÖÙŸ”À8™T•q:ðP¿jÞ¶p`Æ ¸è¢¢kæÍKSE¾ü2MèÞ½jâ‘$I’Tó¹Dªíþì|Ü ü˜@•%0 mùöÛ2—1ujªÌ$I’¤²˜Äj»³€v@Oà à`Ð¥jßvÇaôè²×õè'Tµ±H’$IªLbHµÕYÀ@`u`4Б”ÈøØ87!ôèQúõ֭ᥗ`­µr$I’¤šÍžRmµ7i É<`ð80X;w!¼ùfé×ú ¾þ~ý뜄#I’$©†³Cªm®Þ~CšHr;p"Ј´•$GƇ=÷,}Í’%Ы|óMNB’$I’TÙÄj“eÀûÀnÀx ðй ¥mÛ´]¤,¿ú¬¹fÕÇ#I’$©æ3‰!ÕÏìì¼l\“ÛPÞ~b„±caãLÿ–-á„àÄ¡Q£t®GÔü³yóÜÆ'I’$©f2‰!Õ³€£€ßKÛ€Ã[rÊäɰ×^°ýöi›ÈرiËÈøñðÏÂÍ7ÃóÏCß¾&0$I’$UŒ=¥šîGR³ÎI}0úÿ %4rìÑGaÚ´tÜ»wJ\Œ[tMA†$I’$U„•RM6Ø€ìÕW2çþœ¹^?÷!uÜr 4l ÂÅÜ9¹C’$IRícCªÉ:’ª/%%4^.ÊgPpüñ0fLjÚùøãn‘$I’T9LbH5ÑK¤­#³€ €«€ß‘*2Öšå6œ€W_-z®woøüsèÒ%·±H’$Iª½LbH5Ñ&À÷¤DÆtà,à.`Ç܇òÚkpôѰÛnpûíE¯L!‘$I’¤Ê`CªIÆ7“ª-^–½€€Ã€5sÒ7ߤûE‹à¸ãROŒ¥yh(*I’$©ö3‰!Õ$Ÿ§×­±ÀÀ²ü…ôûßà /À:ë¤ÇãÆ¥„†$I’$U6G¬J5Á$ 9p8Ð8˜ îË_XvÚ Þ~N8n» š6ÍwD’$I’j#+1¤šà/@Oà Rχ€áÀù gÐ ¸õÖ¢ç:t€§Ÿ†õÖËKH’$I’ê+1¤êl:°p+p)‘ñ<°/ÐÈCãÌ#à²ËÒñÀõ×C¿“H’$IÊ+1¤êjÐ8h < ì¹–§É›líÚ¥ãn€~ý`îÜüÄ"I’$©n1‰!UWõ€›€À±™Ç÷‘×¹;ìo½]»¦Ç-[B³fù‹G’$IRÝaCªn~vÆ=€çH‰‹€úÀ&¹©ø´‘öíá•Wà¼óà®» „ÜÇ$I’$©îq'»”K#¥e¬‰¤í#»w“F©Î¬ÚÐVN„ßýZ·†›n‚† ÓùfÍફò“$I’¤ºÉ$†”KG +°þ °WÕ„SW_ =–Ž?û ~ÖZ+ñH’$Iª»ÜN"Ug‹ó€°ãŽéø¥— [7˜3'¯!I’$Iª£LbHÕÙ‘@ÿü†Ð¶-¼ø"ydz|è¡Ð¼y^C’$I’TG¹DÒr¦MƒÕW‡&MÒãÆaøpØèŸç¤Š$I’¤ºËJ )Ÿö'MÈÓ„)S`ìØìã  wï´…dÊ”¢k÷ßßI$’$I’òÇ$†”OO‘VsÿöS¦¤„E¿~ðøã)qÆðÑGðÞ{еkº—$I’¤êÀí$RuÑX»·+H`|üqz¼ß~˯ùþû´æë¯íƒ!I’$)ÿ¬Äò©;i„jvÊí[ŸtR6QšµÖ2!I’$©z0‰!åÓKÀ<ÒV’rûÖ7ßœ&”fõÕaôèÜÄ#I’$Ie1‰!ÕQmÛÂYg•¾æÄaãs$I’$•Å$†”+ÿåïí§N…+®€ÎaòätnúôÒŸSÖuI’$IÊ%{J¹²#Ùæ-ÆåxNyÖ”Ó?ÂE¥ã{î>}à†JÎÝwÃï~}ûV^’$I’´²¬ÄªÚ<àV 0¸˜ \L)ãÖ»âo·t)üûß)ùðî»Ùó;§‘©Ã‡Ã7Âüù¥¿Ö’%ð׿V<I’$Iª VbHUí=à,Òv’»HIŒÀvUóvS¦À>ûÀ²e©ïEAâàÈ#Sbã³Ïà¶ÛàçŸáñÇÓµ `ÝuÓóe¶½ôè>Z5qJ’$IREY‰!U¥ù¤1ªoI‰‹€ß¶”¬¢ï¾ƒk®×_Ïž[o=Øm·t|ß}°xqöÚ¡‡¦¤Æõ×§ÊŒ‘#aß}¡}{˜4 ¾ú*Ý:uJ ŒÑ£¯*I’$©ú°Cª*ï{·¿ÞŽ&%1¶\õ—Ÿ?6ÝæÎ…#Ž€îݳ׎: ž{.õÁxê)èß?oÓÞy§èëŒ ?ü¿úUzܶ-¼ø"¬¶š I’$IÕ‹•RUÙœ”´Ø4«øKÅ/½¯½–=×´iÚ6ðÈ#0{vöÚ¤dÄGd“+Ò¨ÑòkÚ´1!I’$©ú1‰!U¶¥À•¤)$WC€Ù%‹Á!‡À /dÏM™ýúeÇŸÖ½;ôêƒ=?`@ºŸ7z({~µÕàÛoÓt‘m·]Õ/H’$I’ª“Re›BÚBÒ øØœš./Z <’g•úZl³ <ý4ì² ŒSô%wÜ1Ý?ÿ|êƒQ OŸÔ£cGh\lkýúUñÅI’$IRþ˜Ä*ÓL`=RÒbR#ÏG€MÒù‚FÁDE‹`èP8ï¼T‰ðÍ7©Ùfኌ£ŽJ÷Ë–ÁˆÙóõêÁøñðñÇpØJlS‘$I’¤šÄ$†TY~6"m!i <üø"»dÆ ˜8±ì—Z´¨èV“®]aóÍSÿŠ©S‹®mÛvU—$I’¤šÁé$ReY¸8†4RõNàÒ¢KÚ¶…±ca“M`Á‚Ò_îóÏ‹>¾çèÐZµª¬€%I’$©f±Cª ƒ€ÉÀ¤QªŸG–¼´}{Øpò_ò•WŠ>îÒņ$I’¤ºÍ$†´ªæ“*/¶F3ÿVòò)SàÓOKÉË.ƒ—_®Ì %I’$©æ3‰!­Šï¦ÀS¤é#û¶¬üzùåS§BïÞ°dIé/{ÕU&1$I’$©8“ÒÊZìÌ.ŠŸÒ¢¬¿~Ù/½ÖZitª$I’$)Ë$†´²ï;{‘úc³h _|£FÁž{f^¢ì·œ~J\@ê™1v,l¼qÕ ’$I’T“˜ÄVÆ•ÀsÀVÀxRŒ½IÕÅ,[¿ÿ=ÜuWÚJòÙg)‘±ß~ðÄéøÊ+aÂèÕˆ$I’$­ˆI ieÌú’’-€G(qhq½z°Ã鸠'ÆÇ§äEß¾ÙuV`H’$IRéLbHñ-°¸ x(s¿æ\‡?í‚ `Èt<}úòãS%I’$Ie+áscI+t )‘ñП´•ä6 ÉòK'M‚Í6Ë>>óLhÖ ¦MƒÓNËE°’$I’T»X‰!UÄ]@;`[àI`R5F1gÛn Ï.áöx±u[…^Ó{÷îÝeêÔ©ù ze=DÚB²€4}ä!àÀË/Ýl3hÑb„“O†nÈe ’$I’Tw˜ÄPEôÖÞ(v{¿`A¡1ð ðkà4àĉ'¶ìÑ£óæÍËu¼+oÒWÖ øØŸÔcëå—vï/¼k­•?ñ,[–£8%I’$©1‰¡Šè<cPìvQ¡5]cŒÃbŒ#GŽùîgŸ}ƈ#òuEÌÆ‘ú`¼t¶'5õlž]6iRѧuí /¾ £FA=ÿeI’$IR¥óO-•K¡)Ðx·Œ¥¿^ˆ1~_pb—]v™Ý¹sgî½÷Þª ±rŒ"Õ›\ ¬ <\Njê™1f l³ œ}vѧn±<ø 4k–£X%I’$©Ž1‰¡òÚ¨üB¸ „06„pW¡{Á‚ÌV’ޤ Elºé¦|õÕW¹‹veDààn`0pð3iSL¦ céR8ã X¸† ?ü!õÂ$I’$U=§“¨¼¶ÉÜ^$µ¼<8<„pLŒñnRíB¦òZk­Å”)S*ü¦¥=§U«V4jԨ¯Y¢qÀÙÀ¤†ž[/“&“dÔ¯Ÿ*1~óøïᦛ`ÝuaàÀÊ C’$IRV¡>Ð:ßq¨ú0‰¡òš < Œ1Ž!´^n !Œ 2 ËuðlÖ¬ .déÒ¥Ô¯_¿ÜoÚ®]»^;v,½zõ*÷k•j`5R/Œ@?Ò$’r$ë¯/¿œ!À‰'VN’$I’–³ 0©”ëÖE×1&1T.1Æ{{‹û6„pp!°ð|æÒšÅŸÿÓO?±ÞzëU(0©xÍB:tèP¡×*ÑàaR'çH_É>À¤:àý÷aèP¸ùf((üXw]xé%X²Ö^{ÕÃ$I’T¢/€ÍJ¹þa®Qõ`C«êyÒŸþ­cŒ CÓIcX‹˜>}:n¸a…_¼S§N«aiÞ·’zîLJÉŸ|»ïÓ¦Á”)ðÈ#ФIº¶Î:Uš$I’T×ůèzÁJŒ:ÆÆž*—Â=!„wCÅ7Xl’¹ÿ$sÿ!Ð£ð‚… †7Þxc¥’Uj )Yñ*ð:ÐôUì ´MKÚ´‚°G†½ö‚¹só«$I’$É$†ÊísRsÏ£ N„:çïofN:…~i‡yÍ5×´Ÿ={6§Ÿ~zÃ-ÃgÀ¦¤:’®¤¯àWÀ³E—­±<û,ì²KzüÁðÝw¹ T’$I’TÀí$*¯«€=€[CÇ3>ÀdàÈ4úððpá  I¡óyçG—.]òö üØ Øø3p)êz)I±ÚjвeZºúê© ãØcá a“MVðš’$I’¤*e%†Ê%Ƹè GÚ22 8Ø>Æ8©ÐºHÚq"ð30}È!ï_qŹzEþIÚJr0ø pP~øvÛ-ݦÛ¬Ü?lµU>–$I’$•ª€ãRàöÌ­´uË€;27€§êñçÿ÷ÀåÀ0àà0`k 34åÚkS3O€]w…瞃¶mó¨$I’$©8+1TwÌÚ‘&’¬ lGÚB²9‚rÕUpÈ!éø£ o_X¶,±J’$I’–cCuÃR¢âo@k` p20¶è² `Ä07†k®zþ+‘$I’¤jÁ?ÏT74®¿'Ue\s/ƒÃ‡ÿþ7»´^=¸ývxóMØc¼D+I’$I*I Õ~7߇ÁNúâE°÷Þpï½i„ê¤IÙ§„[o—h%I’$I+`CµÛ"RÏ®Àó¤þoC¡Q#èÑ#-ûî»ÔÈó½÷ò¨$I’$©,&1T{ÍÏ’Ãö®Z[¤%ƒÃå—§ã„ÿü'qJ’$I’ÊÅ«ª"°Ð¸‰”¼èñßðé'бcvéÀФIjä9`@>‚•$I’$•‡•ªpð"Ðø–íÀöÛÃË/]þÇ?©§æp lÙ:oWLƒ)Âȑиqžc”$I’$U+1TýM~| ^ƒFïÁ3'A÷îiÉ{ïÁ´iy‹P’$I’”Vb(ÿŽÞ*zê蟎¦ý i ÉwÀ¯aY[¨w>ð4kÏ'žÿßÞÇWQŸ}ÿNö@ $BXRKØwPˆ¢ jE¬ˆ (ÅG´´w)XÑJ_h½ŸÚÖ}©r›¢DET  ( AH€ ,!²Èz=„œ›““°™p2ðyûšfæ:ó»æ0srΗ™9=&5kv¡\H„ð¿LI›¼gÕ?ù_ùù»¤¢¤ ÿSúsD„4gÎéàg\N×™·F:xÐß].4B ¸Nú>éºëüÝàB#Ä@³EÒ1Ç”«|ý'D:RnyHˆ4k–?:ø÷Ä@sTR-sTK¡ê] í•yÊò ¤¨Þ~jà7œ‰'ªÜÏuË//_¸$b ÆI;åÿs%òW#€…ËIPã ‘T É‘TKR}ÿ¶¨!1PãH ñw€‡ËIàwfþîà„ð;Çñw7àrøßI7K_|!¥¤”ÎjÕ*K?þø±Ú¶½S›7KµjI÷ß/5o.©›?›ø !üoDé&J? "#¥ýûßÑs¿ý­Š7Þ©)S¤_ß&5ïâß6þEˆeâÄÒ?Ÿy¦ôÏ€€ÿýÀ¥{bW Ä®@ˆ\¸!pB à „À1€+bW Ä®@ˆ\¸!pB à „À1€+bW Ä®@ˆ\¸!pB à „À1€+bW Ä®@ˆ\¸!pB à „À1€+bW Ä®@ˆ\¸!pB à „À1€+bW Ä®@ˆ\¸!pB à „À1€+bW Ä®@ˆéèÑ£þn¨1æÏŸ¯ÌÌL·øÝÚµkµ|ùr·ø]NNŽRRRTRRâïV€šÀ‘éï&páb Ú8Žä8Žs>ÍÎήêv׺çž{´råJ·øÝ믿®Ù³gû» Àï¶lÙ¢1cƨ  Àß­5A ¤h7 ‡UÎqœ$ÇqVKÊ—t`РAIû÷ï÷w[—#Ä@•r'LÒ\Iq’î•4iÆ uû÷ï¯ãÇû·9€«b ªM—ÔRRg3{ÎÌþý?ÿó?k7mÚ¤””÷V#mܸQŸ|ò‰¿ÛpåË—kíÚµþn£RŸ|ò‰6nÜèï6\%//O)))ÊËËów+®’™™©ùóçû»JmܸQ‹/öw®ÃkÈùIIIѾ}ûüÝF…x;?›7oæ5ä<8ŽÓÛqœnþî£2Žã$;ŽÓÎß}À1PÕFKZbf{Ëf <øpÛ¶m5gÎ?¶UsÍŸ?_Ó§O÷w®3{öl½þúëþn£RÓ§O¯Ñ,k¢ýû÷k̘1âò³s³råJÝsÏ=þn£RóçÏ×Ô©Sý݆ëðr~ÆŒ£~øÁßmTˆ×¸óóá‡òr~î—4ÞßMœÆLI#ýÝ܉UÆqœZ’ZKÚ\~YÛ¶mùvÀÏäïpQipòÏœò 6lx^ß8òÎ;ïT>Xƒ 9çuÖ4Û·oב#GøÚÀs”““£û¼9rDÛ·o¯’þ ´iÓ¦»­U¥ì5bÍš5ÊÊÊòs7î±iÓ&ÔØýcûöíÊËË«’þvïÞ­œœœ»­U©*_C.5ßÿ½‚‚jÞ[ܪ|Û°aƒ¤Ò3±.†÷B§“‘‘Qe¯!n²wïÞÆ7Þxc¿Ÿ±ŠF’ÂÇù9ë¨N‘’âϲ¿@•n IrÌÌß=à"á8N‚¤­’þËÌNý¼E“&Mòâ‹/ª  @gó­«úÓŸôØcUW«.ï™Ù(7 £æÅÔp³²S-ê•_››«¸¸¸³ 0$iòäÉJLL<íͯ7n|Ñÿë.=»wïþt„ Oû»"HRôi–çKZzzA @ˆ*cfÇÇ9$©aùe999jÙ²åY¯+**J¿þõ¯«²=À-²Æÿ¡¿›j"n쉪–&ÉëÚ¶cÇŽ|õÕWjÕª•ŸZ\ 1PÕž‘ÔÑqœ«ËfÌš5«Ù±cÇtß}÷ù±-€Ûq9 ªZª¤E’Þug¡¤ZŽã´{ðÁuÙe—ù¹5€›q&ª”™•H.i’¤£’ü÷ÿ÷w3fÌðoc×ãL T¹“AÆË''©ôÌŒ_ú¯#ÀÅ€31€+bW Ä®À=1pÁ|õÕWZ²d‰|ðÁ —/X°@_ýµš4i¢Aƒ©}ûö>5Çל9s´mÛ6ýâ¿Ð•W^©èèhŸºÝ»wë­·ÞÒ¡C‡Ô­[7%''«V­ZU¾MÀ™”””è±ÇÓu×]§N:ù,ÏÊÊRJJŠŽ9âÙWÃÃÃ}ê¾úê+}üñÇ Rß¾}Õ¿Ÿ3Ó{ï½§o¿ýV1114h}êòòò4gÎíØ±C:tPrr²7n\5 œôÅ_è믿Öï~÷;Ÿef¦ÔÔT­]»VÍš5Ó AƒÔ¶m[Ÿº£GjΜ9ÊÌÌT‡tå•WªQ£F>u;vìÐܹs•——§îÝ»+99Y¡¡¡>u+W®Ô§Ÿ~ªÐÐPõë×O}úôñ©)))QjjªÖ­[§¸¸8 4HmÚ´9Ïg¨ØÒ¥K•––¦É“'WûXÅÅÅš;w®ÒÒÒ¯Áƒ+!!Á§®¨¨H‹-ÒÚµk¤!C†¨K—.ÕÞ.mŽã4”ô ¤GÍìЯ»¤!’Š$­0³/OYV[Ò¯Nóðb3{£š[ÄÙ03&¦êžeffZ“&M¬uëÖV‘ &˜$KHH°6mÚX`` ½ôÒK^5'Nœ°:˜$ëÒ¥‹5jÔÈ¢¢¢lÍš5^u7n´Úµk[`` õéÓÇÂÃí{÷îvàÀ ǪӔ)SL’½óÎ;>ËÒÒÒ,<<Ü‚‚‚<ûjïÞ½íàÁƒ^u/½ô’I² X×®]Íq›4i’{Õýú׿6IÖºukKHH°   {óÍ7½jòòò¬M›6&ɺuëf 6´èèhûî»ïª~ãqÉÚ¼y³Õ¯_ߺtéRáòÑ£G›$kÛ¶­µlÙÒ‚ƒƒ-%%Å«æÈ‘#–àÙW4h`111–––æU÷Í7ßXhh¨[Ÿ>},44Ô `‡òª{öÙgM’EEEY—.]Ìq›2eŠ•””xÕÝpà &É->>ÞBBBìí·ß®‚g(µaëS§ŽõéÓ炌7tèP“díÛ··¸¸8 ·÷ßß«fïÞ½6`À °îÝ»[dd¤9ŽcÏ<óÌézÍüÿ¾Z'IÁ’–I2IÍ.Àxwžë ¤¯%•Hú‡¤€“ËãN.¯l:îïçŒéäߥ¿`ºø§¹sç~Ý¢E χ«ò>ùä“dO?ý´˜™Ùo~ó °ŒŒ OÝôéÓ­~ýú¶xñb33;|ø°uïÞÝZµjåyZRRbýúõ³®]»Zzzº™•~P¬_¿¾Ýyç>cÕå§Ÿ~²»îºËó‹¯|ˆQ\\l½zõ²=zØæÍ›ÍÌlýúõV·n]»çž{Q¸$Í›7Ïš4ib’ª,ÄX´hQ¥Aô«¯¾j6gÎ+))±ÂÂB9r¤EDDx}&L°[µj•™™:tÈFŽi–™™Y%}✽f5à}|uM’Z`TIˆ!©£¤a•,k*)WÒ+’êœ7öäØ7Ÿü9PR|Óí'ë¦øûyc:ù÷éï˜.îIÒŸ%Yÿþý­[·n†={ö´öíÛ{ÍÛ¿¿Ùþð3+ý@l÷Þ{¯W]jjªI²>úÈÌÌ>øàƒ ?0Þwß}îùT§7ZLLŒÕªUËî¹çž ÷É÷Þ{Ïç”™Ùĉ­víÚž7—÷ß¿…„„øœI”˜˜h ðü|Ùe—Y·nݼj²²²Ìq{ä‘G̬ô_Úí¿þ뿼êÞ|óM“dŸþùÏÛp\òî½÷^“dW^y¥uìØ±Â£]»vÖ«W/¯yÛ·o7ÇqlæÌ™ff¶{÷n °iÓ¦yÕ•x+V¬03³·ÞzË$ÙÇìU7nÜ8«W¯ž;vÌÌÌî¾ûn ·Ã‡{jJJJ¬E‹6dÈϼ6mÚX¿~ý¼Öõã?š${òÉ'Ïõé¼”ÛÇ·ÄÄÄÓ†yyy¶zõjûá‡|κ+/&&Æî»ï¾ —5mÚÔ†ê5oýúõ&Éž}öY33Û³g…††ÚO<áU—––fãÇ·µk×žÍæ¡ê½f5à½|uL’FJÊ“´SÒÜÓ…*½‡c[I=%Õ>ÃzŸ–´·’e•T )ªÜü’¾8Í:#$m“”êïçé'nì‰êÖꡇÚøÙgŸ©I“&> Oœ8¡o¿ýVC† ñ𥤤$­ZµJ’ôõ×_«°°Ð§îÊ+¯”$OÝŠ+$©ÂºãÇkýúõU³UÀiddd¨_¿~Z»v­n½õÖ kN·¯æåå)--ÍS×µkW5lØÐ§nõêÕ’¤Ã‡+--MW]u•WMll¬Ú·oï9>V­Z¥ââbŸº²ŸËê€óµcǽð Z¼x±êׯï³üàÁƒÚ¸q£Ï>Ø¢E µiÓÆ³þç?ÿQIIÉY½æiðàÁ>u?ýô“ÒÓÓ=u½{÷Vdd¤§Æq%''{Öµÿ~mÙ²ÅgÌV­Z)!!ã?[VV–^}õU-\¸PuêÔ©°¦¸¸X3fÌPݺuÕ³gOµoß^qqqZ²dÉ9·mÛ6íÙ³ÇgŸþå/©Æ{öé¥K—*??ßSwìØ1åçç«C‡zñÅ•””tÎcgÐAÒó’:IZ]Y‘ã8ƒ%í´IÒ*I‡Ç™á8NàyŒÙWÒ7fv Üü%* H*3[RIÿç<ÆD5!Ä@u=cÆŒÌÀÀŠ_k²³³UXX¨¨¨(Ÿe5Òž={$I;wî”$Ÿºˆˆ………);;[’”™™©ÐÐPEDDø¬«l< º 6Lÿþ÷¿+¼Qa™ÌÌLEDDøÜ|°ü¾š™™Yéñ‘ŸŸ¯ÜÜ\eeeÉÌ*¼áaEÇQùº† ÊqOp¾,X  &Èqœ —geeIòÝËæé5¿ìq§ÖÕ¯__g¬«ì8:tèŽ?^é˜euüþÀÏõÑGéöÛo¯ôø¤Ç\?ü°î¿ÿ~¥§§kÛ¶m8p †®~øáœÆ;Ó>]þx ÑÀU¯^=EDDhøðáÚ½{÷9 œ¥?›Ù3;XYã8$-’ô£¤a’JºWÒTI<1ã$•0$i¿¤“7-ßCI÷œì—ƒ¡!Ä@µ²“çaUæ§Ÿ~’$5hÐÀgYƒ ´wïÞ³ª+{syèСJk$B \§{ƒZæl÷Õ³©;t¨ôfÞýË÷©ÇQY]ùõ¨^½zž:à|i߯l,›w¦}588X^¿Nw|ìÝ»W%%%:räÈëÎö÷ p¾ÎæøxòÉ'Õ¯_?Íž=[‰‰‰Š×K/½¤àà`=üðÃ’¤%K–(::Ú3eggëå—_öüçYŸtæã-++Kaaa:t¨jÕª¥7ß|SãÆÓ§Ÿ~ª«®ºJ………Uù4gü|pÒc’I·™Ù"3;hfÏKzWÒÿu'B’ÇÙæ8N¶ã8Ù* ¢Ê~>9]}r}uUzCÏòÊæùž2.M;¹üïg¿u¸øŠUøUÙ©½>Ë <§[ž©®nݺ’JĮ̈¬F’§ð·ÊöÕüü|I:ã>}j]QQ‘¤Šüü|ÏqTv†RÙc+«ªËéöÁS_ó϶.22R™™™ÖHR:u ZµjñwCÙ˜gú=T—ôôt?~\±±±zî¹ç¼–ÅÅÅy.‰‹‹Óí·ßîYö÷¿ÿ]‰‰‰JNN–$•ýz¶ÇQAANœ8¡Þ½{ëí·ß–$ÝtÓMjÓ¦þøÇ?*%%EcÇŽ­Úά‹JïE1¼\X$©–¤6’ÖJzCRÈÉeƒTz©Êk§Ôï8ùçQI¾ß½ý¿=|êLÇqUzÈŸÍìÈùnª!ü*&&F’”““ã³,''Çó=æ•Õ™™rssÕ²eKI¥÷ÈÍÍ•™yý‹GÙãÊꋉ‰ÑÁƒ¾ÿ P6ïÔ}¿²º°°05mÚÔsIJeuå£òu'NœÐ±cÇ˶oßîyZöæ²|ÝÎ;U\\ìõFµ¤¤Äs}ç©ë’ć4Ô±±±*((ð9E½l_=5˜«ìøˆ—ã8jذ¡ÂÂÂ|êÌL;vìðZ—ä{q|àBiÔ¨‘‚ƒƒ+ÜW333Ïøš_~_ÕáÇ•››{ƺ3ýžiÒ¤‰}êÊ~§p| º•í÷³fÍRzzºÏt®7'/ 1ÊïÓÚ³ggŸ. 1N½ñ­Tz Çq8 þ²[Ò23ûE%ÓÒs\ß.I-*˜/i{—¸Œ”´ÁÌÎíf4¸ 1àw7Üpƒ.\èu ïºu딑‘¡¾}ûJ’.»ì2µiÓFóæÍózì¼yó¤ž=Ko*|Ýu×)00°ÂºøøxÏÀßF¥€€½óÎ;^óçÍ›§V­Z)::ZRéñ‘žž® 6xjòóóõÁxŽIºþúëõÞ{喝¸Ø3oÕªUÚ½{·úõë'IêÞ½»š7oî3fjjªBBBÔ­[·*ßNàT5j”Þ}÷]•””xæ/_¾\ûöíóìÓ={öTll¬ÏkyjjªÂÂÂÔµkWI¥û}ÙüSÍ›7O:tð|øºþúëµnÝ:mݺÕS“——§Å‹{Æ Òˆ#”ššªSßË.[¶L999^ÇPÚµk§Zµjy.é(søða%%%ùœ)Q&,,LAA¾'WGFFꪫ®ò9Ž/^¬¼¼<Ï>}õÕW+,,L .ôª›7ožJJJ4`À€Ÿ³YÀùúFÒåo¸é8Îÿsç{Çqb*xL¡JϪ¨ÈjŽãØ¢E‹|ƪۗ_~i’ìwÞñYvà 7Xݺumîܹ–‘‘a=ô9ŽcŸ~ú©§æÄ‰Ö´iSëØ±£­\¹ÒÒÒÒìÚk¯µ¨¨(Û¿¿§nݺuæ8Ž=Ú6nÜhŸþ¹µiÓÆ®¸â ¯1ŸyæsÇžxâ ËÈȰ7ÞxÃj×®m>úhõ= ¸$õïßߺtéâ3Íš5æ8ŽÝrË-–žžnË–-³„„KNNöª›={¶جY³lÛ¶möúë¯[xx¸=þøã^uÇ· Ø»ï¾k[·nµ©S§Z@@€-_¾ÜS“——gQQQÖ¹sg[½zµ}ÿý÷6dÈ‹ŽŽ¶ÜÜ\OݪU«L’;Ö6mÚdK—.µøøx6lX?;¸ÔuïÞÝúôéã3æÌ™&ÉFmË–-³+Vص×^k’ì7Þ8çq>ýôS“dwß}·mÞ¼Ù/^l±±±vã7zÕ=ðÀ&É{ì1KOO·§žzÊ7nl—_~¹œ÷vâgyÍüÿ¾Ú'I¿“d’š•›ßNR¤õ’î’”¨Òmš¤…ç1NˆJÏÆH“Ô[¥!Å|•~cI£rµ-OŽó{??L•ü}ú»¦Kb:mˆaföÑGYtt´|Á°Ë.»ÌÒÓÓ}êžxâ ÷Ô9ÒŽ?îUsäÈ=z´š$ åüæt!Æ¡C‡ìÆo´€€Ï¾ZþšYiÐשS'Ï~m|ðOÝüùó­Q£Fžº.]ºØÖ­[½jJJJìᇶ°°0Oݯ~õ+ËÏϯº¬òÃÌ,55Õ¢¢¢<û`×®]mÛ¶m^5ÅÅÅöàƒZhh¨§îæ›o¶ÂÂB¯ºÜÜ\1b„ç8 ·Ù³gûŒùý÷ß[‡<늵%K–øÔ½ýöÛÖ°aCO]=,33óüŸ •…ÅÅÅöÔSOYdd¤glÒ¤‰=ÿüóç=Ö¿þõ/«W¯žg}}ûöµììl¯š¢¢"›6mšçwChh¨ :Ô+äÃ÷šùÿ=|µO•…'—õ‘ôCÙ¾«Ò3-^•Tû<ÇJTéÍ@ËÖ—-ixuÃN.êï燩âÉ9ùT§E’†ø» À%^—t»¿›j"î‰\¸!pB à „À1€+bW Ä®@ˆ\¸!pB à „¸Ô®]»´hÑ"eeeU¸|X©©©úË_þ¢eË–ù»p1p©?þXÆ Óµ×^«ÂÂBŸåÔ°aÃ4gΜ ÖÓ?ÿùO9ò‚w® uå•Wê†nÐŒ3ôÑGù»%p1p¹uëÖéñÇ÷w®°yóf}õÕWúÝï~§Ã‡kÖ¬Yþn œB \®yóæzâ‰'´nݺ :®™]°šsUÙ:8 IºâŠ+ðóÞUGßÕ¹^.„¸ÜSO=¥ððp7®ÂËJNÕ¯_?ýùÏöš·k×.%%%)55U’´uëV%%%iÕªU7nœš4i¢ØØXMžÝDZ5kÖxÍŸ8q¢Ù¦M›ÌÌì³Ï>3I¶hÑé5EFFZ:ulïÞ½žy«W¯6IöðÃ{Õ~øá‡&Éþú׿š™ÙðáíS§Nžå÷ÝwŸÅÇÇ›$ûòË/ÍÌ,==Ý$Ùûï¿o%%%j“'OöZï”)SlÆŒ§íÀEë5óÿ{x&¦9q&‰_|QµjÕÒ¸qã*½Lâ\$''+00P’¤Ë.»LÑÑÑêÙ³§§&>>^Ré7¡” ÖØ±c=?;Ž£[o½U;wîTFF†¶oß®ŒŒ ]sÍ5úñÇ•žž®ôôtíÛ·O]ºtÑçŸîÕÇå—_®ÓöºbÅ õèÑC]»võš?qâDéË/¿<çíOJJRãÆ=?¯\¹R’4iÒ$¯ºaÆ©yóæúì³Ï$I#FŒÐwß}§ììlI¥ß"3~üxEEEiéÒ¥’¤?üPJNN–ã8êÒ¥‹ž{î9Mš4IK–,Qaa¡žzê)ÎÄ B .qqqzúé§•––vÚ{Gœ­-Zxý¨V­ZÉqϼŠ.Lj‰‰ñ™_v9ÆŽ;”‘‘!Izî¹çÔ®];¯iõêÕÚ¾}»JJJ*í£"[¶l©°®l^Ù˜ç¢üú¶lÙ¢°°0¯`ãÔÚ²1®½öZI¥÷Ù¹s§6mÚ¤ääd 8Ðb|ðÁ:t¨BCC%I©©©2dˆ^zé%]uÕUjÔ¨‘ÆŽësé—:B ."wÝu—† ¢Y³fiÍš5>ËÇñ¹ùçO?ýTẂƒƒÏ«‡={öøŒ‘››+©4à¨S§Ž$é…^О={|¦ÌÌL¯o9›>êÕ«Wáv”›pÎÛQ~Üzõê)??_'Nœ¨pœ²1š6mªîÝ»ëã?Ö'Ÿ|¢ºuëª[·nJNNÖªU«´wï^}ùå—5j”çñÑÑÑZ°`öíÛ§7ÞxC пÿýoõèÑCEEEçÜ;+B .2/¾ø¢j×®­ßüæ7>Ëj×®íóa?==½JÇ/((ÐÆ½æ-_¾\aaajÕª•Ú¶m«€€}óÍ7ŠŽŽöšžzê)½üòËçÿ :e 3ó¹4%;;[[¶lñcäÈ‘Z²d‰–.]ª+®¸BJNNV~~¾¦OŸ.3ÓðáÃ%•~«Ê½÷Þ«Å‹«^½zºõÖ[µ`ÁM›6M{öìQZZÚÏ!™²ËJvíÚ峬cÇŽZ¸p¡V¯^-IÚ°aƒ¦M›Vå=üáÐñãÇuüøq=óÌ3zóÍ7õÀ(44T 4ÐÝwß­W^yEûÛßtøðaÏW—Ξ=Ûëþç2^NNŽF­uëÖ©¨¨HsæÌÑ´iÓ”””¤Ž;þìmºé¦›Ô²eKMœ8QóæÍSII‰V¯^­1cÆ(88ØëÌŠ#FhïÞ½š7ož’““%I­[·V‹-ôÊ+¯èòË/Wݺu%I5RJJЦL™¢;vH’8 åË—«N:j×®ÝÏ!¡²ËJÊ{ä‘GT«V-õêÕKuëÖU§NôÐC÷¥#iÙ²¥Õ A5kÖL¿ýío5jÔ(MŸ>ÝSóä“OjĈš9ãAÏFXX˜æÍ›§àà`Ýxãjܸ±zõê¥o¾ùFï¿ÿ¾:uêä©íر£tüøq <Ø3ðàÁ*))ñ SVG drawing This was produced by version 4.1 of GNU libplot, a free library for exporting 2-D vector graphics. 0 50 100 150 200 250 1000 10000 100000 1e+06 1e+07 Speed (Krow/s) Number of rows Writing with medium record size (56 bytes) No Psyco Psyco PyTables-v.3.1.1/doc/source/usersguide/index.rst000066400000000000000000000045741231437614300216060ustar00rootroot00000000000000===================== PyTables User's Guide ===================== ------------------------------- Hierarchical datasets in Python ------------------------------- :Authors: **Francesc Alted, Ivan Vilata, Scott Prater, Vicent Mas, Tom Hedley, Antonio Valentino, Jeffrey Whitaker, Anthony Scopatz, Josh Moore** :Copyright: |copy| 2002, 2003, 2004 - Francesc Alted |copy| 2005, 2006, 2007 - Cárabos Coop. V. |copy| 2008, 2009, 2010 - Francesc Alted |copy| 2011-2014 - PyTables maintainers -------- Contents -------- .. toctree:: :maxdepth: 1 introduction installation tutorials libref optimization filenode datatypes condition_syntax parameter_files utilities file_format bibliography -------------------------------------------------------- Copyright Notice and Statement for PyTables User's Guide -------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: a. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c. Neither the name of Francesc Alted nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .. |copy| unicode:: U+000A9 .. COPYRIGHT SIGN PyTables-v.3.1.1/doc/source/usersguide/installation.rst000066400000000000000000000435321231437614300231750ustar00rootroot00000000000000Installation ============ .. epigraph:: Make things as simple as possible, but not any simpler. -- Albert Einstein The Python Distutils are used to build and install PyTables, so it is fairly simple to get the application up and running. If you want to install the package from sources you can go on reading to the next section. However, if you want to go straight to binaries that 'just work' for the main platforms (Linux, Mac OSX and Windows), you might want to use the excellent Anaconda_ or Canopy_ distributions. PyTables usually distributes its own Windows binaries too; go :ref:`binaryInstallationDescr` for instructions. Finally `Christoph Gohlke`_ also maintains an excellent suite of a variety of binary packages for Windows at his site. .. _Anaconda: https://store.continuum.io/cshop/anaconda/ .. _Canopy: https://www.enthought.com/products/canopy/ .. _`Christoph Gohlke`: http://www.lfd.uci.edu/~gohlke/pythonlibs/ Installation from source ------------------------ These instructions are for both Unix/MacOS X and Windows systems. If you are using Windows, it is assumed that you have a recent version of MS Visual C++ compiler installed. A GCC compiler is assumed for Unix, but other compilers should work as well. Extensions in PyTables have been developed in Cython (see :ref:`[CYTHON] `) and the C language. You can rebuild everything from scratch if you have Cython installed, but this is not necessary, as the Cython compiled source is included in the source distribution. To compile PyTables you will need a recent version of Python, the HDF5 (C flavor) library from http://www.hdfgroup.org, and the NumPy (see :ref:`[NUMPY] `) and Numexpr (see :ref:`[NUMEXPR] `) packages. Prerequisites ~~~~~~~~~~~~~ First, make sure that you have * Python_ >= 2.6 including Python 3.x * HDF5_ >= 1.8.4 (>=1.8.7 is strongly recommended), * NumPy_ >= 1.4.1, * Numexpr_ >= 2.0 and * Cython_ >= 0.13 * argparse_ (only Python 2.6, it is used by the :program:`pt2to3` utility) installed (for testing purposes, we are using HDF5_ 1.8.12, NumPy_ 1.8.0 and Numexpr_ 2.2.2 currently). If you don't, fetch and install them before proceeding. .. _Python: http://www.python.org .. _HDF5: http://www.hdfgroup.org/HDF5 .. _NumPy: http://www.numpy.org .. _Numexpr: http://code.google.com/p/numexpr .. _Cython: http://www.cython.org .. _argparse: http://code.google.com/p/argparse .. note:: HDF5 versions < 1.8.7 are supported with some limitations. It is not possible to open the same file multiple times (simultaneously), even in read-only mode. .. note:: Currently PyTables does not use setuptools_ by default so do not expect that the setup.py script automatically install all packages PyTables depends on. .. _setuptools: https://pypi.python.org/pypi/setuptools .. _ctypes: https://pypi.python.org/pypi/ctypes Compile and install these packages (but see :ref:`prerequisitesBinInst` for instructions on how to install pre-compiled binaries if you are not willing to compile the prerequisites on Windows systems). For compression (and possibly improved performance), you will need to install the Zlib (see :ref:`[ZLIB] `), which is also required by HDF5 as well. You may also optionally install the excellent LZO compression library (see :ref:`[LZO] ` and :ref:`compressionIssues`). The high-performance bzip2 compression library can also be used with PyTables (see :ref:`[BZIP2] `). The Blosc (see :ref:`[BLOSC] `) compression library is embedded in PyTables, so this will be used in case it is not found in the system. So, in case the installer warns about not finding it, do not worry too much ;) **Unix** setup.py will detect HDF5, LZO, or bzip2 libraries and include files under :file:`/usr` or :file:`/usr/local`; this will cover most manual installations as well as installations from packages. If setup.py can not find libhdf5, libhdf5 (or liblzo, or libbz2 that you may wish to use) or if you have several versions of a library installed and want to use a particular one, then you can set the path to the resource in the environment, by setting the values of the :envvar:`HDF5_DIR`, :envvar:`LZO_DIR`, :envvar:`BZIP2_DIR` or :envvar:`BLOSC_DIR` environment variables to the path to the particular resource. You may also specify the locations of the resource root directories on the setup.py command line. For example:: --hdf5=/stuff/hdf5-1.8.12 --lzo=/stuff/lzo-2.02 --bzip2=/stuff/bzip2-1.0.5 --blosc=/stuff/blosc-1.3.2 If your HDF5 library was built as a shared library not in the runtime load path, then you can specify the additional linker flags needed to find the shared library on the command line as well. For example:: --lflags="-Xlinker -rpath -Xlinker /stuff/hdf5-1.8.12/lib" You may also want to try setting the :envvar:`LD_LIBRARY_PATH` environment variable to point to the directory where the shared libraries can be found. Check your compiler and linker documentation as well as the Python Distutils documentation for the correct syntax or environment variable names. It is also possible to link with specific libraries by setting the :envvar:`LIBS` environment variable:: LIBS="hdf5-1.8.12 nsl" Finally, you can give additional flags to your compiler by passing them to the :option:`--cflags` flag:: --cflags="-w -O3 -msse2" In the above case, a gcc compiler is used and you instructed it to suppress all the warnings and set the level 3 of optimization. Finally, if you are running Linux in 32-bit mode, and you know that your CPU has support for SSE2 vector instructions, you may want to pass the :option:`-msse2` flag that will accelerate Blosc operation. .. hint:: some GNU/Linux distributions provide a packaged version of the HDF5 libraries with MPI support. In this case you may need to specify the path of the MPH headers as additional include directory. On Ubuntu 12.04 the following command has been reported to work:: $ C_INCLUDE_PATH=/usr/lib/openmpi/include pip install --upgrade tables **Windows** You can get ready-to-use Windows binaries and other development files for most of the following libraries from the GnuWin32 project (see :ref:`[GNUWIN32] `). In case you cannot find the LZO binaries in the GnuWin32 repository, you can find them at http://sourceforge.net/projects/pytables/files/lzo-win. Once you have installed the prerequisites, setup.py needs to know where the necessary library *stub* (.lib) and *header* (.h) files are installed. You can set the path to the include and dll directories for the HDF5 (mandatory) and LZO, BZIP2, BLOSC (optional) libraries in the environment, by setting the values of the :envvar:`HDF5_DIR`, :envvar:`LZO_DIR`, :envvar:`BZIP2_DIR` or :envvar:`BLOSC_DIR` environment variables to the path to the particular resource. For example:: set HDF5_DIR=c:\\stuff\\hdf5-1.8.5-32bit-VS2008-IVF101\\release set LZO_DIR=c:\\Program Files (x86)\\GnuWin32 set BZIP2_DIR=c:\\Program Files (x86)\\GnuWin32 set BLOSC_DIR=c:\\Program Files (x86)\\Blosc You may also specify the locations of the resource root directories on the setup.py command line. For example:: --hdf5=c:\\stuff\\hdf5-1.8.5-32bit-VS2008-IVF101\\release --lzo=c:\\Program Files (x86)\\GnuWin32 --bzip2=c:\\Program Files (x86)\\GnuWin32 --blosc=c:\\Program Files (x86)\\Blosc **Development version (Unix)** Installation of the development version is very similar to installation from a source package (described above). There are two main differences: #. sources have to be downloaded from the `PyTables source repository`_ hosted on GitHub_. Git (see :ref:`[GIT] `) is used as VCS. The following command create a local copy of latest development version sources:: $ git clone https://github.com/PyTables/PyTables.git #. sources in the git repository do not include pre-built documentation and pre-generated C code of Cython extension modules. To be able to generate them, both Cython (see :ref:`[CYTHON] `) and sphinx >= 1.0.7 (see :ref:`[SPHINX] `) are mandatory prerequisites. .. _`PyTables source repository`: https://github.com/PyTables/PyTables .. _GitHub: https://github.com PyTables package installation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Once you have installed the HDF5 library and the NumPy and Numexpr packages, you can proceed with the PyTables package itself. #. Run this command from the main PyTables distribution directory, including any extra command line arguments as discussed above:: $ python setup.py build #. To run the test suite, execute any of these commands. **Unix** In the sh shell and its variants:: $ cd build/lib.linux-x86_64-3.3 $ env PYTHONPATH=. python tables/tests/test_all.py or, if you prefer:: $ cd build/lib.linux-x86_64-3.3 $ env PYTHONPATH=. python -c "import tables; tables.test()" .. note:: the syntax used above overrides original contents of the :envvar:`PYTHONPATH` environment variable. If this is not the desired behaviour and the user just wants to add some path before existing ones, then the safest syntax to use is the following:: $ env PYTHONPATH=.${PYTHONPATH:+:$PYTHONPATH} python tables/tests/test_all.py Please refer to your :program:`sh` documentation for details. **Windows** Open the command prompt (cmd.exe or command.com) and type:: > cd build\\lib.linux-x86_64-2.7 > set PYTHONPATH=.;%PYTHONPATH% > python tables\\tests\\test_all.py or:: > cd build\\lib.linux-x86_64-2.7 > set PYTHONPATH=.;%PYTHONPATH% > python -c "import tables; tables.test()" Both commands do the same thing, but the latter still works on an already installed PyTables (so, there is no need to set the :envvar:`PYTHONPATH` variable for this case). However, before installation, the former is recommended because it is more flexible, as you can see below. If you would like to see verbose output from the tests simply add the :option:`-v` flag and/or the word verbose to the first of the command lines above. You can also run only the tests in a particular test module. For example, to execute just the test_types test suite, you only have to specify it:: # change to backslashes for win $ python tables/tests/test_types.py -v You have other options to pass to the :file:`test_all.py` driver:: # change to backslashes for win $ python tables/tests/test_all.py --heavy The command above runs every test in the test unit. Beware, it can take a lot of time, CPU and memory resources to complete:: # change to backslashes for win $ python tables/tests/test_all.py --print-versions The command above shows the versions for all the packages that PyTables relies on. Please be sure to include this when reporting bugs:: # only under Linux 2.6.x $ python tables/tests/test_all.py --show-memory The command above prints out the evolution of the memory consumption after each test module completion. It's useful for locating memory leaks in PyTables (or packages behind it). Only valid for Linux 2.6.x kernels. And last, but not least, in case a test fails, please run the failing test module again and enable the verbose output:: $ python tables/tests/test_.py -v verbose and, very important, obtain your PyTables version information by using the :option:`--print-versions` flag (see above) and send back both outputs to developers so that we may continue improving PyTables. If you run into problems because Python can not load the HDF5 library or other shared libraries. **Unix** Try setting the LD_LIBRARY_PATH or equivalent environment variable to point to the directory where the missing libraries can be found. **Windows** Put the DLL libraries (hdf5dll.dll and, optionally, lzo1.dll, bzip2.dll or blosc.dll) in a directory listed in your :envvar:`PATH` environment variable. The setup.py installation program will print out a warning to that effect if the libraries can not be found. #. To install the entire PyTables Python package, change back to the root distribution directory and run the following command (make sure you have sufficient permissions to write to the directories where the PyTables files will be installed):: $ python setup.py install Of course, you will need super-user privileges if you want to install PyTables on a system-protected area. You can select, though, a different place to install the package using the :option:`--prefix` flag:: $ python setup.py install --prefix="/home/myuser/mystuff" Have in mind, however, that if you use the :option:`--prefix` flag to install in a non-standard place, you should properly setup your :envvar:`PYTHONPATH` environment variable, so that the Python interpreter would be able to find your new PyTables installation. You have more installation options available in the Distutils package. Issue a:: $ python setup.py install --help for more information on that subject. That's it! Now you can skip to the next chapter to learn how to use PyTables. Installation with :program:`pip` -------------------------------- Many users find it useful to use the :program:`pip` program (or similar ones) to install python packages. As explained in previous sections the user should in any case ensure that all dependencies listed in the `Prerequisites`_ section are correctly installed. The simplest way to install PyTables using :program:`pip` is the following:: $ pip install tables The following example shows how to install the latest stable version of PyTables in the user folder when a older version of the package is already installed at system level:: $ pip install --user --upgrade tables The `--user` option tels to the :program:`pip` tool to install the package in the user folder (``$HOME/.local`` on GNU/Linux and Unix systems), while the `--upgrade` option forces the installation of the latest version even if an older version of the package is already installed. The :program:`pip` tool can also be used to install packages from a source tar-ball:: $ pip install tables-3.0.0.tar.gz To install the development version of PyTables from the *develop* branch of the main :program:`git` :ref:`[GIT] ` repository the command is the following:: $ pip install git+https://github.com/PyTables/PyTables.git@develop#egg=tables A similar command can be used to install a specific tagged fersion:: $ pip install git+https://github.com/PyTables/PyTables.git@v.2.4.0#egg=tables Finally, PyTables developers provide a :file:`requirements.txt` file that can be used by :program:`pip` to install the PyTables dependencies:: $ wget https://raw.github.com/PyTables/PyTables/develop/requirements.txt $ pip install -r requirements.txt Of course the :file:`requirements.txt` file can be used to install only python packages. Other dependencies like the HDF5 library of compression libraries have to be installed by the user. .. _binaryInstallationDescr: Binary installation (Windows) ----------------------------- This section is intended for installing precompiled binaries on Windows platforms. You may also find it useful for instructions on how to install *binary prerequisites* even if you want to compile PyTables itself on Windows. .. _prerequisitesBinInst: Windows prerequisites ~~~~~~~~~~~~~~~~~~~~~ First, make sure that you have Python 2.6, NumPy 1.4.1 and Numexpr 2.0 or higher installed (PyTables binaries have been built using NumPy 1.7 and Numexpr 2.1). The binaries already include DLLs for HDF5 (1.8.4, 1.8.9), zlib1 (1.2.3), szlib (2.0, uncompression support only) and bzip2 (1.0.5) for Windows (2.8.0). The LZO DLL can't be included because of license issues (but read below for directives to install it if you want so). To enable compression with the optional LZO library (see the :ref:`compressionIssues` for hints about how it may be used to improve performance), fetch and install the LZO from http://sourceforge.net/projects/pytables/files/lzo-win (choose v1.x for Windows 32-bit and v2.x for Windows 64-bit). Normally, you will only need to fetch that package and copy the included lzo1.dll/lzo2.dll file in a directory in the PATH environment variable (for example C:\\WINDOWS\\SYSTEM) or python_installation_path\\Lib\\site-packages\\tables (the last directory may not exist yet, so if you want to install the DLL there, you should do so *after* installing the PyTables package), so that it can be found by the PyTables extensions. Please note that PyTables has internal machinery for dealing with uninstalled optional compression libraries, so, you don't need to install the LZO dynamic library if you don't want to. PyTables package installation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Download the tables-.win32-py.exe file and execute it. Binary packahes can be found e.g. at the `Unofficial Windows Binaries for Python Extension Packages `_ page. You can (and *you should*) test your installation by running the next commands:: >>> import tables >>> tables.test() on your favorite python shell. If all the tests pass (possibly with a few warnings, related to the potential unavailability of LZO lib) you already have a working, well-tested copy of PyTables installed! If any test fails, please copy the output of the error messages as well as the output of:: >>> tables.print_versions() and mail them to the developers so that the problem can be fixed in future releases. You can proceed now to the next chapter to see how to use PyTables. PyTables-v.3.1.1/doc/source/usersguide/introduction.rst000066400000000000000000000355051231437614300232160ustar00rootroot00000000000000Introduction ============ .. epigraph:: La sabiduría no vale la pena si no es posible servirse de ella para inventar una nueva manera de preparar los garbanzos. [Wisdom isn't worth anything if you can't use it to come up with a new way to cook garbanzos.] -- Gabriel García Márquez, A wise Catalan in *"Cien años de soledad"* The goal of PyTables is to enable the end user to manipulate easily data *tables* and *array* objects in a hierarchical structure. The foundation of the underlying hierarchical data organization is the excellent HDF5 library (see :ref:`[HDGF1] `). It should be noted that this package is not intended to serve as a complete wrapper for the entire HDF5 API, but only to provide a flexible, *very pythonic* tool to deal with (arbitrarily) large amounts of data (typically bigger than available memory) in tables and arrays organized in a hierarchical and persistent disk storage structure. A table is defined as a collection of records whose values are stored in *fixed-length* fields. All records have the same structure and all values in each field have the same *data type*. The terms *fixed-length* and strict *data types* may seem to be a strange requirement for an interpreted language like Python, but they serve a useful function if the goal is to save very large quantities of data (such as is generated by many data acquisition systems, Internet services or scientific applications, for example) in an efficient manner that reduces demand on CPU time and I/O. In order to emulate in Python records mapped to HDF5 C structs PyTables implements a special class so as to easily define all its fields and other properties. PyTables also provides a powerful interface to mine data in tables. Records in tables are also known in the HDF5 naming scheme as *compound* data types. For example, you can define arbitrary tables in Python simply by declaring a class with named fields and type information, such as in the following example:: class Particle(IsDescription): name = StringCol(16) # 16-character String idnumber = Int64Col() # signed 64-bit integer ADCcount = UInt16Col() # unsigned short integer TDCcount = UInt8Col() # unsigned byte grid_i = Int32Col() # integer grid_j = Int32Col() # integer # A sub-structure (nested data-type) class Properties(IsDescription): pressure = Float32Col(shape=(2,3)) # 2-D float array (single-precision) energy = Float64Col(shape=(2,3,4)) # 3-D float array (double-precision) You then pass this class to the table constructor, fill its rows with your values, and save (arbitrarily large) collections of them to a file for persistent storage. After that, the data can be retrieved and post-processed quite easily with PyTables or even with another HDF5 application (in C, Fortran, Java or whatever language that provides a library to interface with HDF5). Other important entities in PyTables are *array* objects, which are analogous to tables with the difference that all of their components are homogeneous. They come in different flavors, like *generic* (they provide a quick and fast way to deal with for numerical arrays), *enlargeable* (arrays can be extended along a single dimension) and *variable length* (each row in the array can have a different number of elements). The next section describes the most interesting capabilities of PyTables. Main Features ------------- PyTables takes advantage of the object orientation and introspection capabilities offered by Python, the powerful data management features of HDF5, and NumPy's flexibility and Numexpr's high-performance manipulation of large sets of objects organized in a grid-like fashion to provide these features: - *Support for table entities:* You can tailor your data adding or deleting records in your tables. Large numbers of rows (up to 2**63, much more than will fit into memory) are supported as well. - *Multidimensional and nested table cells:* You can declare a column to consist of values having any number of dimensions besides scalars, which is the only dimensionality allowed by the majority of relational databases. You can even declare columns that are made of other columns (of different types). - *Indexing support for columns of tables:* Very useful if you have large tables and you want to quickly look up for values in columns satisfying some criteria. - *Support for numerical arrays:* NumPy (see :ref:`[NUMPY] `) arrays can be used as a useful complement of tables to store homogeneous data. - *Enlargeable arrays:* You can add new elements to existing arrays on disk in any dimension you want (but only one). Besides, you are able to access just a slice of your datasets by using the powerful extended slicing mechanism, without need to load all your complete dataset in memory. - *Variable length arrays:* The number of elements in these arrays can vary from row to row. This provides a lot of flexibility when dealing with complex data. - *Supports a hierarchical data model:* Allows the user to clearly structure all data. PyTables builds up an *object tree* in memory that replicates the underlying file data structure. Access to objects in the file is achieved by walking through and manipulating this object tree. Besides, this object tree is built in a lazy way, for efficiency purposes. - *User defined metadata:* Besides supporting system metadata (like the number of rows of a table, shape, flavor, etc.) the user may specify arbitrary metadata (as for example, room temperature, or protocol for IP traffic that was collected) that complement the meaning of actual data. - *Ability to read/modify generic HDF5 files:* PyTables can access a wide range of objects in generic HDF5 files, like compound type datasets (that can be mapped to Table objects), homogeneous datasets (that can be mapped to Array objects) or variable length record datasets (that can be mapped to VLArray objects). Besides, if a dataset is not supported, it will be mapped to a special UnImplemented class (see :ref:`UnImplementedClassDescr`), that will let the user see that the data is there, although it will be unreachable (still, you will be able to access the attributes and some metadata in the dataset). With that, PyTables probably can access and *modify* most of the HDF5 files out there. - *Data compression:* Supports data compression (using the *Zlib*, *LZO*, *bzip2* and *Blosc* compression libraries) out of the box. This is important when you have repetitive data patterns and don't want to spend time searching for an optimized way to store them (saving you time spent analyzing your data organization). - *High performance I/O:* On modern systems storing large amounts of data, tables and array objects can be read and written at a speed only limited by the performance of the underlying I/O subsystem. Moreover, if your data is compressible, even that limit is surmountable! - *Support of files bigger than 2 GB:* PyTables automatically inherits this capability from the underlying HDF5 library (assuming your platform supports the C long long integer, or, on Windows, __int64). - *Architecture-independent:* PyTables has been carefully coded (as HDF5 itself) with little-endian/big-endian byte ordering issues in mind. So, you can write a file on a big-endian machine (like a Sparc or MIPS) and read it on other little-endian machine (like an Intel or Alpha) without problems. In addition, it has been tested successfully with 64 bit platforms (Intel-64, AMD-64, PowerPC-G5, MIPS, UltraSparc) using code generated with 64 bit aware compilers. .. _ObjectTreeSection: The Object Tree --------------- The hierarchical model of the underlying HDF5 library allows PyTables to manage tables and arrays in a tree-like structure. In order to achieve this, an *object tree* entity is *dynamically* created imitating the HDF5 structure on disk. The HDF5 objects are read by walking through this object tree. You can get a good picture of what kind of data is kept in the object by examining the *metadata* nodes. The different nodes in the object tree are instances of PyTables classes. There are several types of classes, but the most important ones are the Node, Group and Leaf classes. All nodes in a PyTables tree are instances of the Node class. The Group and Leaf classes are descendants of Node. Group instances (referred to as *groups* from now on) are a grouping structure containing instances of zero or more groups or leaves, together with supplementary metadata. Leaf instances (referred to as *leaves*) are containers for actual data and can not contain further groups or leaves. The Table, Array, CArray, EArray, VLArray and UnImplemented classes are descendants of Leaf, and inherit all its properties. Working with groups and leaves is similar in many ways to working with directories and files on a Unix filesystem, i.e. a node (file or directory) is always a *child* of one and only one group (directory), its *parent group* [1]_. Inside of that group, the node is accessed by its *name*. As is the case with Unix directories and files, objects in the object tree are often referenced by giving their full (absolute) path names. In PyTables this full path can be specified either as string (such as '/subgroup2/table3', using / as a parent/child separator) or as a complete object path written in a format known as the *natural name* schema (such as file.root.subgroup2.table3). Support for *natural naming* is a key aspect of PyTables. It means that the names of instance variables of the node objects are the same as the names of its children [2]_. This is very *Pythonic* and intuitive in many cases. Check the tutorial :ref:`readingAndSelectingUsage` for usage examples. You should also be aware that not all the data present in a file is loaded into the object tree. The *metadata* (i.e. special data that describes the structure of the actual data) is loaded only when the user want to access to it (see later). Moreover, the actual data is not read until she request it (by calling a method on a particular node). Using the object tree (the metadata) you can retrieve information about the objects on disk such as table names, titles, column names, data types in columns, numbers of rows, or, in the case of arrays, their shapes, typecodes, etc. You can also search through the tree for specific kinds of data then read it and process it. In a certain sense, you can think of PyTables as a tool that applies the same introspection capabilities of Python objects to large amounts of data in persistent storage. It is worth noting that PyTables sports a *metadata cache system* that loads nodes *lazily* (i.e. on-demand), and unloads nodes that have not been used for some time (following a *Least Recently Used* schema). It is important to stress out that the nodes enter the cache after they have been unreferenced (in the sense of Python reference counting), and that they can be revived (by referencing them again) directly from the cache without performing the de-serialization process from disk. This feature allows dealing with files with large hierarchies very quickly and with low memory consumption, while retaining all the powerful browsing capabilities of the previous implementation of the object tree. See :ref:`[OPTIM] ` for more facts about the advantages introduced by this new metadata cache system. To better understand the dynamic nature of this object tree entity, let's start with a sample PyTables script (which you can find in examples/objecttree.py) to create an HDF5 file:: from tables import * class Particle(IsDescription): identity = StringCol(itemsize=22, dflt=" ", pos=0) # character String idnumber = Int16Col(dflt=1, pos = 1) # short integer speed = Float32Col(dflt=1, pos = 2) # single-precision # Open a file in "w"rite mode fileh = open_file("objecttree.h5", mode = "w") # Get the HDF5 root group root = fileh.root # Create the groups group1 = fileh.create_group(root, "group1") group2 = fileh.create_group(root, "group2") # Now, create an array in root group array1 = fileh.create_array(root, "array1", ["string", "array"], "String array") # Create 2 new tables in group1 table1 = fileh.create_table(group1, "table1", Particle) table2 = fileh.create_table("/group2", "table2", Particle) # Create the last table in group2 array2 = fileh.create_array("/group1", "array2", [1,2,3,4]) # Now, fill the tables for table in (table1, table2): # Get the record object associated with the table: row = table.row # Fill the table with 10 records for i in xrange(10): # First, assign the values to the Particle record row['identity'] = 'This is particle: %2d' % (i) row['idnumber'] = i row['speed'] = i * 2. # This injects the Record values row.append() # Flush the table buffers table.flush() # Finally, close the file (this also will flush all the remaining buffers!) fileh.close() This small program creates a simple HDF5 file called objecttree.h5 with the structure that appears in :ref:`Figure 1 ` [3]_. When the file is created, the metadata in the object tree is updated in memory while the actual data is saved to disk. When you close the file the object tree is no longer available. However, when you reopen this file the object tree will be reconstructed in memory from the metadata on disk (this is done in a lazy way, in order to load only the objects that are required by the user), allowing you to work with it in exactly the same way as when you originally created it. .. _objecttree-h5: .. figure:: images/objecttree-h5.png :align: center **Figure 1: An HDF5 example with 2 subgroups, 2 tables and 1 array.** In :ref:`Figure2 `, you can see an example of the object tree created when the above objecttree.h5 file is read (in fact, such an object tree is always created when reading any supported generic HDF5 file). It is worthwhile to take your time to understand it [4]_. It will help you understand the relationships of in-memory PyTables objects. .. _objecttree: .. figure:: images/objecttree.* :width: 100% :align: center **Figure 2: A PyTables object tree example.** --------------------------- .. [1] PyTables does not support hard links - for the moment. .. [2] I got this simple but powerful idea from the excellent Objectify module by David Mertz (see :ref:`[MERTZ] `). .. [3] We have used ViTables (see :ref:`[VITABLES] `) in order to create this snapshot. .. [4] Bear in mind, however, that this diagram is *not* a standard UML class diagram; it is rather meant to show the connections between the PyTables objects and some of its most important attributes and methods. PyTables-v.3.1.1/doc/source/usersguide/libref.rst000066400000000000000000000031351231437614300217320ustar00rootroot00000000000000.. _library_reference: Library Reference ================= PyTables implements several classes to represent the different nodes in the object tree. They are named File, Group, Leaf, Table, Array, CArray, EArray, VLArray and UnImplemented. Another one allows the user to complement the information on these different objects; its name is AttributeSet. Finally, another important class called IsDescription allows to build a Table record description by declaring a subclass of it. Many other classes are defined in PyTables, but they can be regarded as helpers whose goal is mainly to declare the *data type properties* of the different first class objects and will be described at the end of this chapter as well. An important function, called open_file is responsible to create, open or append to files. In addition, a few utility functions are defined to guess if the user supplied file is a *PyTables* or *HDF5* file. These are called is_pytables_file() and is_hdf5_file(), respectively. There exists also a function called which_lib_version() that informs about the versions of the underlying C libraries (for example, HDF5 or Zlib) and another called print_versions() that prints all the versions of the software that PyTables relies on. Finally, test() lets you run the complete test suite from a Python console interactively. .. toctree:: :maxdepth: 2 libref/top_level libref/file_class libref/hierarchy_classes libref/structured_storage libref/homogenous_storage libref/link_classes libref/declarative_classes libref/helper_classes libref/expr_class libref/filenode_classes PyTables-v.3.1.1/doc/source/usersguide/libref/000077500000000000000000000000001231437614300211765ustar00rootroot00000000000000PyTables-v.3.1.1/doc/source/usersguide/libref/declarative_classes.rst000066400000000000000000000122271231437614300257340ustar00rootroot00000000000000.. currentmodule:: tables Declarative classes =================== In this section a series of classes that are meant to *declare* datatypes that are required for creating primary PyTables datasets are described. .. _AtomClassDescr: The Atom class and its descendants ---------------------------------- .. autoclass:: Atom .. These are defined in the class docstring Atom instance variables ^^^^^^^^^^^^^^^^^^^^^^^ .. autoattribute:: Atom.dflt .. autoattribute:: Atom.dtype .. autoattribute:: Atom.itemsize .. autoattribute:: Atom.kind .. autoattribute:: Atom.shape .. autoattribute:: Atom.type Atom properties ~~~~~~~~~~~~~~~ .. autoattribute:: Atom.ndim .. autoattribute:: Atom.recarrtype .. autoattribute:: Atom.size Atom methods ~~~~~~~~~~~~ .. automethod:: Atom.copy Atom factory methods ~~~~~~~~~~~~~~~~~~~~ .. automethod:: Atom.from_dtype .. automethod:: Atom.from_kind .. automethod:: Atom.from_sctype .. automethod:: Atom.from_type Atom Sub-classes ~~~~~~~~~~~~~~~~ .. autoclass:: StringAtom :members: .. autoclass:: BoolAtom :members: .. autoclass:: IntAtom :members: .. autoclass:: Int8Atom :members: .. autoclass:: Int16Atom :members: .. autoclass:: Int32Atom :members: .. autoclass:: Int64Atom :members: .. autoclass:: UIntAtom :members: .. autoclass:: UInt8Atom :members: .. autoclass:: UInt16Atom :members: .. autoclass:: UInt32Atom :members: .. autoclass:: UInt64Atom :members: .. autoclass:: FloatAtom :members: .. autoclass:: Float32Atom :members: .. autoclass:: Float64Atom :members: .. autoclass:: ComplexAtom :members: .. autoclass:: Time32Atom :members: .. autoclass:: Time64Atom :members: .. autoclass:: EnumAtom :members: Pseudo atoms ~~~~~~~~~~~~ Now, there come three special classes, ObjectAtom, VLStringAtom and VLUnicodeAtom, that actually do not descend from Atom, but which goal is so similar that they should be described here. Pseudo-atoms can only be used with VLArray datasets (see :ref:`VLArrayClassDescr`), and they do not support multidimensional values, nor multiple values per row. They can be recognised because they also have kind, type and shape attributes, but no size, itemsize or dflt ones. Instead, they have a base atom which defines the elements used for storage. See :file:`examples/vlarray1.py` and :file:`examples/vlarray2.py` for further examples on VLArray datasets, including object serialization and string management. ObjectAtom ^^^^^^^^^^ .. autoclass:: ObjectAtom :members: .. _VLStringAtom: VLStringAtom ^^^^^^^^^^^^ .. autoclass:: VLStringAtom :members: .. _VLUnicodeAtom: VLUnicodeAtom ^^^^^^^^^^^^^ .. autoclass:: VLUnicodeAtom :members: .. _ColClassDescr: The Col class and its descendants --------------------------------- .. autoclass:: Col .. Col instance variables ^^^^^^^^^^^^^^^^^^^^^^ .. autoattribute:: _v_pos Col instance variables ~~~~~~~~~~~~~~~~~~~~~~ In addition to the variables that they inherit from the Atom class, Col instances have the following attributes. .. attribute:: Col._v_pos The *relative* position of this column with regard to its column siblings. Col factory methods ~~~~~~~~~~~~~~~~~~~ .. automethod:: Col.from_atom Col sub-classes ~~~~~~~~~~~~~~~ .. autoclass:: StringCol :members: .. autoclass:: BoolCol :members: .. autoclass:: IntCol :members: .. autoclass:: Int8Col :members: .. autoclass:: Int16Col :members: .. autoclass:: Int32Col :members: .. autoclass:: Int64Col :members: .. autoclass:: UIntCol :members: .. autoclass:: UInt8Col :members: .. autoclass:: UInt16Col :members: .. autoclass:: UInt32Col :members: .. autoclass:: UInt64Col :members: .. autoclass:: Float32Col :members: .. autoclass:: Float64Col :members: .. autoclass:: ComplexCol :members: .. autoclass:: TimeCol :members: .. autoclass:: Time32Col :members: .. autoclass:: Time64Col :members: .. autoclass:: EnumCol :members: .. _IsDescriptionClassDescr: The IsDescription class ----------------------- .. autoclass:: IsDescription Description helper functions ---------------------------- .. autofunction:: tables.description.descr_from_dtype .. autofunction:: tables.description.dtype_from_descr .. _AttributeSetClassDescr: The AttributeSet class ---------------------- .. autoclass:: tables.attributeset.AttributeSet .. These are defined in the class docstring AttributeSet attributes ~~~~~~~~~~~~~~~~~~~~~~~ .. autoattribute:: tables.attributeset.AttributeSet._v_attrnames .. autoattribute:: tables.attributeset.AttributeSet._v_attrnamessys .. autoattribute:: tables.attributeset.AttributeSet._v_attrnamesuser .. autoattribute:: tables.attributeset.AttributeSet._v_unimplemented AttributeSet properties ~~~~~~~~~~~~~~~~~~~~~~~ .. autoattribute:: tables.attributeset.AttributeSet._v_node AttributeSet methods ~~~~~~~~~~~~~~~~~~~~ .. automethod:: tables.attributeset.AttributeSet._f_copy .. automethod:: tables.attributeset.AttributeSet._f_list .. automethod:: tables.attributeset.AttributeSet._f_rename .. automethod:: tables.attributeset.AttributeSet.__contains__ PyTables-v.3.1.1/doc/source/usersguide/libref/expr_class.rst000066400000000000000000000014671231437614300241030ustar00rootroot00000000000000.. currentmodule:: tables General purpose expression evaluator class ========================================== The Expr class -------------- .. autoclass:: Expr .. These are defined in the class docstring. Expr instance variables ~~~~~~~~~~~~~~~~~~~~~~~ .. autoattribute:: Expr.append_mode .. autoattribute:: Expr.maindim .. autoattribute:: Expr.names .. autoattribute:: Expr.out .. autoattribute:: Expr.o_start .. autoattribute:: Expr.o_stop .. autoattribute:: Expr.o_step .. autoattribute:: Expr.shape .. autoattribute:: Expr.values Expr methods ~~~~~~~~~~~~ .. automethod:: Expr.eval .. automethod:: Expr.set_inputs_range .. automethod:: Expr.set_output .. automethod:: Expr.set_output_range Expr special methods ~~~~~~~~~~~~~~~~~~~~ .. automethod:: Expr.__iter__ PyTables-v.3.1.1/doc/source/usersguide/libref/file_class.rst000066400000000000000000000051131231437614300240340ustar00rootroot00000000000000.. currentmodule:: tables File manipulation class ======================= .. _FileClassDescr: The File Class -------------- .. autoclass:: File .. These are defined in the class docstring. This is necessary because attributes created in a class's __init__ method can't be documented with autoattribute. See Sphinx bug #904. https://bitbucket.org/birkenfeld/sphinx/issue/904 Attributes ~~~~~~~~~~ .. autoattribute:: File.filename .. autoattribute:: File.format_version .. autoattribute:: File.isopen .. autoattribute:: File.mode .. autoattribute:: File.root .. autoattribute:: File.root_uep File properties ~~~~~~~~~~~~~~~ .. autoattribute:: File.title .. autoattribute:: File.filters .. autoattribute:: File.open_count File methods - file handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automethod:: File.close .. automethod:: File.copy_file .. automethod:: File.flush .. automethod:: File.fileno .. automethod:: File.__enter__ .. automethod:: File.__exit__ .. automethod:: File.__str__ .. automethod:: File.__repr__ .. automethod:: File.get_file_image .. automethod:: File.get_filesize .. automethod:: File.get_userblock_size File methods - hierarchy manipulation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automethod:: File.copy_children .. automethod:: File.copy_node .. automethod:: File.create_array .. automethod:: File.create_carray .. automethod:: File.create_earray .. automethod:: File.create_external_link .. automethod:: File.create_group .. automethod:: File.create_hard_link .. automethod:: File.create_soft_link .. automethod:: File.create_table .. automethod:: File.create_vlarray .. automethod:: File.move_node .. automethod:: File.remove_node .. automethod:: File.rename_node File methods - tree traversal ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automethod:: File.get_node .. automethod:: File.is_visible_node .. automethod:: File.iter_nodes .. automethod:: File.list_nodes .. automethod:: File.walk_groups .. automethod:: File.walk_nodes .. automethod:: File.__contains__ .. automethod:: File.__iter__ File methods - Undo/Redo support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automethod:: File.disable_undo .. automethod:: File.enable_undo .. automethod:: File.get_current_mark .. automethod:: File.goto .. automethod:: File.is_undo_enabled .. automethod:: File.mark .. automethod:: File.redo .. automethod:: File.undo File methods - attribute handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automethod:: File.copy_node_attrs .. automethod:: File.del_node_attr .. automethod:: File.get_node_attr .. automethod:: File.set_node_attr PyTables-v.3.1.1/doc/source/usersguide/libref/filenode_classes.rst000066400000000000000000000044661231437614300252440ustar00rootroot00000000000000.. currentmodule:: tables.nodes.filenode .. _filenode_classes: Filenode Module =============== .. automodule:: tables.nodes.filenode Module constants ---------------- .. autodata:: NodeType .. autodata:: NodeTypeVersions Module functions ---------------- .. autofunction:: new_node .. autofunction:: open_node .. autofunction:: read_from_filenode .. autofunction:: save_to_filenode The RawPyTablesIO base class ---------------------------- .. autoclass:: RawPyTablesIO RawPyTablesIO attributes ~~~~~~~~~~~~~~~~~~~~~~~~ .. autoattribute:: RawPyTablesIO.mode RawPyTablesIO methods ~~~~~~~~~~~~~~~~~~~~~ .. automethod:: RawPyTablesIO.tell .. automethod:: RawPyTablesIO.seek .. automethod:: RawPyTablesIO.seekable .. automethod:: RawPyTablesIO.fileno .. automethod:: RawPyTablesIO.close .. automethod:: RawPyTablesIO.flush .. automethod:: RawPyTablesIO.truncate .. automethod:: RawPyTablesIO.readable .. automethod:: RawPyTablesIO.writable .. automethod:: RawPyTablesIO.readinto .. automethod:: RawPyTablesIO.readline .. automethod:: RawPyTablesIO.write The ROFileNode class -------------------- .. autoclass:: ROFileNode ROFileNode attributes ~~~~~~~~~~~~~~~~~~~~~ .. autoattribute:: ROFileNode.attrs ROFileNode methods ~~~~~~~~~~~~~~~~~~ .. automethod:: ROFileNode.flush .. automethod:: ROFileNode.read .. automethod:: ROFileNode.readline .. automethod:: ROFileNode.readlines .. automethod:: ROFileNode.close .. automethod:: ROFileNode.seek .. automethod:: ROFileNode.tell .. automethod:: ROFileNode.readable .. automethod:: ROFileNode.writable .. automethod:: ROFileNode.seekable .. automethod:: ROFileNode.fileno The RAFileNode class -------------------- .. autoclass:: RAFileNode RAFileNode attributes ~~~~~~~~~~~~~~~~~~~~~ .. autoattribute:: RAFileNode.attrs RAFileNode methods ~~~~~~~~~~~~~~~~~~ .. automethod:: RAFileNode.flush .. automethod:: RAFileNode.read .. automethod:: RAFileNode.readline .. automethod:: RAFileNode.readlines .. automethod:: RAFileNode.truncate .. automethod:: RAFileNode.write .. automethod:: RAFileNode.writelines .. automethod:: RAFileNode.close .. automethod:: RAFileNode.seek .. automethod:: RAFileNode.tell .. automethod:: RAFileNode.readable .. automethod:: RAFileNode.writable .. automethod:: RAFileNode.seekable .. automethod:: RAFileNode.fileno PyTables-v.3.1.1/doc/source/usersguide/libref/helper_classes.rst000066400000000000000000000053231231437614300247270ustar00rootroot00000000000000.. currentmodule:: tables Helper classes ============== This section describes some classes that do not fit in any other section and that mainly serve for ancillary purposes. .. _FiltersClassDescr: The Filters class ----------------- .. autoclass:: Filters .. These are defined in the class docstring. Filters instance variables ^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autoattribute:: Filters.fletcher32 .. autoattribute:: Filters.complevel .. autoattribute:: Filters.complib .. autoattribute:: Filters.shuffle Filters methods ~~~~~~~~~~~~~~~ .. automethod:: Filters.copy .. _IndexClassDescr: The Index class --------------- .. autoclass:: tables.index.Index .. This is defined in the class docstring .. autoattribute:: tables.index.Index.nelements Index instance variables ~~~~~~~~~~~~~~~~~~~~~~~~ .. autoattribute:: tables.index.Index.column .. autoattribute:: tables.index.Index.dirty .. autoattribute:: tables.index.Index.filters .. autoattribute:: tables.index.Index.is_csi .. attribute:: tables.index.Index.nelements The number of currently indexed rows for this column. Index methods ~~~~~~~~~~~~~ .. automethod:: tables.index.Index.read_sorted .. automethod:: tables.index.Index.read_indices Index special methods ~~~~~~~~~~~~~~~~~~~~~ .. automethod:: tables.index.Index.__getitem__ The IndexArray class -------------------- .. autoclass:: tables.indexes.IndexArray :members: .. _EnumClassDescr: The Enum class -------------- .. autoclass:: tables.misc.enum.Enum Enum special methods ~~~~~~~~~~~~~~~~~~~~ .. automethod:: Enum.__call__ .. automethod:: Enum.__contains__ .. automethod:: Enum.__eq__ .. automethod:: Enum.__getattr__ .. automethod:: Enum.__getitem__ .. automethod:: Enum.__iter__ .. automethod:: Enum.__len__ .. automethod:: Enum.__repr__ .. _UnImplementedClassDescr: The UnImplemented class ----------------------- .. autoclass:: UnImplemented :members: The Unknown class ----------------- .. autoclass:: Unknown :members: .. _ExceptionsDescr: Exceptions module ----------------- In the :mod:`exceptions` module exceptions and warnings that are specific to PyTables are declared. .. autoexception:: HDF5ExtError :members: .. autoexception:: ClosedNodeError .. autoexception:: ClosedFileError .. autoexception:: FileModeError .. autoexception:: NodeError .. autoexception:: NoSuchNodeError .. autoexception:: UndoRedoError .. autoexception:: UndoRedoWarning .. autoexception:: NaturalNameWarning .. autoexception:: PerformanceWarning .. autoexception:: FlavorError .. autoexception:: FlavorWarning .. autoexception:: FiltersWarning .. autoexception:: OldIndexWarning .. autoexception:: DataTypeWarning .. autoexception:: ExperimentalFeatureWarning PyTables-v.3.1.1/doc/source/usersguide/libref/hierarchy_classes.rst000066400000000000000000000131641231437614300254300ustar00rootroot00000000000000.. currentmodule:: tables Hierarchy definition classes ============================ .. _NodeClassDescr: The Node class -------------- .. autoclass:: Node .. These are defined in class docstring .. autoattribute:: Node._v_depth .. autoattribute:: Node._v_file .. autoattribute:: Node._v_name .. autoattribute:: Node._v_pathname .. autoattribute:: Node._v_objectid (location independent) Node instance variables - location dependent ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoattribute:: Node._v_parent Node instance variables - location independent ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoattribute:: Node._v_attrs .. autoattribute:: Node._v_isopen Node instance variables - attribute shorthands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoattribute:: Node._v_title Node methods - hierarchy manipulation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automethod:: Node._f_close .. automethod:: Node._f_copy .. automethod:: Node._f_isvisible .. automethod:: Node._f_move .. automethod:: Node._f_remove .. automethod:: Node._f_rename Node methods - attribute handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automethod:: Node._f_delattr .. automethod:: Node._f_getattr .. automethod:: Node._f_setattr .. _GroupClassDescr: The Group class --------------- .. autoclass:: Group .. These are defined in the class docstring Group instance variables ~~~~~~~~~~~~~~~~~~~~~~~~ The following instance variables are provided in addition to those in Node (see :ref:`NodeClassDescr`): .. autoattribute:: Group._v_children .. autoattribute:: Group._v_groups .. autoattribute:: Group._v_hidden .. autoattribute:: Group._v_leaves .. autoattribute:: Group._v_links .. autoattribute:: Group._v_unknown Group properties ~~~~~~~~~~~~~~~~ .. autoattribute:: Group._v_nchildren .. autoattribute:: Group._v_filters Group methods ~~~~~~~~~~~~~ .. important:: *Caveat:* The following methods are documented for completeness, and they can be used without any problem. However, you should use the high-level counterpart methods in the File class (see :ref:`FileClassDescr`, because they are most used in documentation and examples, and are a bit more powerful than those exposed here. The following methods are provided in addition to those in Node (see :ref:`NodeClassDescr`): .. automethod:: Group._f_close .. automethod:: Group._f_copy .. automethod:: Group._f_copy_children .. automethod:: Group._f_get_child .. automethod:: Group._f_iter_nodes .. automethod:: Group._f_list_nodes .. automethod:: Group._f_walk_groups .. automethod:: Group._f_walknodes Group special methods ~~~~~~~~~~~~~~~~~~~~~ Following are described the methods that automatically trigger actions when a Group instance is accessed in a special way. This class defines the :meth:`__setattr__`, :meth:`__getattr__` and :meth:`__delattr__` methods, and they set, get and delete *ordinary Python attributes* as normally intended. In addition to that, :meth:`__getattr__` allows getting *child nodes* by their name for the sake of easy interaction on the command line, as long as there is no Python attribute with the same name. Groups also allow the interactive completion (when using readline) of the names of child nodes. For instance:: # get a Python attribute nchild = group._v_nchildren # Add a Table child called 'table' under 'group'. h5file.create_table(group, 'table', my_description) table = group.table # get the table child instance group.table = 'foo' # set a Python attribute # (PyTables warns you here about using the name of a child node.) foo = group.table # get a Python attribute del group.table # delete a Python attribute table = group.table # get the table child instance again .. automethod:: Group.__contains__ .. automethod:: Group.__delattr__ .. automethod:: Group.__getattr__ .. automethod:: Group.__iter__ .. automethod:: Group.__repr__ .. automethod:: Group.__setattr__ .. automethod:: Group.__str__ .. _LeafClassDescr: The Leaf class -------------- .. autoclass:: Leaf .. These are defined in the class docstring .. _LeafInstanceVariables: Leaf instance variables ~~~~~~~~~~~~~~~~~~~~~~~ These instance variables are provided in addition to those in Node (see :ref:`NodeClassDescr`): .. autoattribute:: Leaf.byteorder .. autoattribute:: Leaf.dtype .. autoattribute:: Leaf.extdim .. autoattribute:: Leaf.nrows .. autoattribute:: Leaf.nrowsinbuf .. autoattribute:: Leaf.shape Leaf properties ~~~~~~~~~~~~~~~ .. autoattribute:: Leaf.chunkshape .. autoattribute:: Leaf.ndim .. autoattribute:: Leaf.filters .. autoattribute:: Leaf.maindim .. autoattribute:: Leaf.flavor .. attribute:: Leaf.size_in_memory The size of this leaf's data in bytes when it is fully loaded into memory. .. autoattribute:: Leaf.size_on_disk Leaf instance variables - aliases ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following are just easier-to-write aliases to their Node (see :ref:`NodeClassDescr`) counterparts (indicated between parentheses): .. autoattribute:: Leaf.attrs .. autoattribute:: Leaf.name .. autoattribute:: Leaf.object_id .. autoattribute:: Leaf.title Leaf methods ~~~~~~~~~~~~ .. automethod:: Leaf.close .. automethod:: Leaf.copy .. automethod:: Leaf.flush .. automethod:: Leaf.isvisible .. automethod:: Leaf.move .. automethod:: Leaf.rename .. automethod:: Leaf.remove .. automethod:: Leaf.get_attr .. automethod:: Leaf.set_attr .. automethod:: Leaf.del_attr .. automethod:: Leaf.truncate .. automethod:: Leaf.__len__ .. automethod:: Leaf._f_close PyTables-v.3.1.1/doc/source/usersguide/libref/homogenous_storage.rst000066400000000000000000000046111231437614300256410ustar00rootroot00000000000000.. currentmodule:: tables Homogenous storage classes ========================== .. _ArrayClassDescr: The Array class --------------- .. autoclass:: Array Array instance variables ~~~~~~~~~~~~~~~~~~~~~~~~ .. attribute:: Array.atom An Atom (see :ref:`AtomClassDescr`) instance representing the *type* and *shape* of the atomic objects to be saved. .. autoattribute:: Array.rowsize .. attribute:: Array.nrow On iterators, this is the index of the current row. .. autoattribute:: Array.nrows Array methods ~~~~~~~~~~~~~ .. automethod:: Array.get_enum .. automethod:: Array.iterrows .. automethod:: Array.next .. automethod:: Array.read Array special methods ~~~~~~~~~~~~~~~~~~~~~ The following methods automatically trigger actions when an :class:`Array` instance is accessed in a special way (e.g. ``array[2:3,...,::2]`` will be equivalent to a call to ``array.__getitem__((slice(2, 3, None), Ellipsis, slice(None, None, 2))))``. .. automethod:: Array.__getitem__ .. automethod:: Array.__iter__ .. automethod:: Array.__setitem__ .. _CArrayClassDescr: The CArray class ---------------- .. autoclass:: CArray .. _EArrayClassDescr: The EArray class ---------------- .. autoclass:: EArray .. _EArrayMethodsDescr: EArray methods ~~~~~~~~~~~~~~ .. automethod:: EArray.append .. _VLArrayClassDescr: The VLArray class ----------------- .. autoclass:: VLArray .. These are defined in the class docstring VLArray instance variables ~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoattribute:: VLArray.atom .. autoattribute:: VLArray.flavor .. autoattribute:: VLArray.nrow .. autoattribute:: VLArray.nrows .. autoattribute:: VLArray.extdim .. autoattribute:: VLArray.nrows VLArray properties ~~~~~~~~~~~~~~~~~~ .. autoattribute:: VLArray.size_on_disk .. autoattribute:: VLArray.size_in_memory VLArray methods ~~~~~~~~~~~~~~~ .. automethod:: VLArray.append .. automethod:: VLArray.get_enum .. automethod:: VLArray.iterrows .. automethod:: VLArray.next .. automethod:: VLArray.read .. automethod:: VLArray.get_row_size VLArray special methods ~~~~~~~~~~~~~~~~~~~~~~~ The following methods automatically trigger actions when a :class:`VLArray` instance is accessed in a special way (e.g., vlarray[2:5] will be equivalent to a call to vlarray.__getitem__(slice(2, 5, None)). .. automethod:: VLArray.__getitem__ .. automethod:: VLArray.__iter__ .. automethod:: VLArray.__setitem__ PyTables-v.3.1.1/doc/source/usersguide/libref/link_classes.rst000066400000000000000000000027641231437614300244130ustar00rootroot00000000000000.. currentmodule:: tables Link classes ============ .. _LinkClassDescr: The Link class -------------- .. autoclass:: tables.link.Link .. These are defined in the class docstring .. autoattribute:: tables.link.Link.target Link instance variables ~~~~~~~~~~~~~~~~~~~~~~~ .. autoattribute:: tables.link.Link._v_attrs Link methods ~~~~~~~~~~~~ The following methods are useful for copying, moving, renaming and removing links. .. automethod:: tables.link.Link.copy .. automethod:: tables.link.Link.move .. automethod:: tables.link.Link.remove .. automethod:: tables.link.Link.rename .. _SoftLinkClassDescr: The SoftLink class ------------------ .. autoclass:: tables.link.SoftLink SoftLink special methods ~~~~~~~~~~~~~~~~~~~~~~~~ The following methods are specific for dereferrencing and representing soft links. .. automethod:: tables.link.SoftLink.__call__ .. automethod:: tables.link.SoftLink.__str__ The ExternalLink class ---------------------- .. autoclass:: tables.link.ExternalLink .. This is defined in the class docstring ExternalLink instance variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoattribute:: tables.link.ExternalLink.extfile ExternalLink methods ~~~~~~~~~~~~~~~~~~~~ .. automethod:: tables.link.ExternalLink.umount ExternalLink special methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following methods are specific for dereferrencing and representing external links. .. automethod:: tables.link.ExternalLink.__call__ .. automethod:: tables.link.ExternalLink.__str__ PyTables-v.3.1.1/doc/source/usersguide/libref/structured_storage.rst000066400000000000000000000127061231437614300256660ustar00rootroot00000000000000.. currentmodule:: tables Structured storage classes ========================== .. _TableClassDescr: The Table class --------------- .. autoclass:: Table .. These are defined in the class docstring .. _TableInstanceVariablesDescr: Table instance variables ~~~~~~~~~~~~~~~~~~~~~~~~ The following instance variables are provided in addition to those in Leaf (see :ref:`LeafClassDescr`). Please note that there are several col* dictionaries to ease retrieving information about a column directly by its path name, avoiding the need to walk through Table.description or :attr:`Table.cols`. .. autoattribute:: Table.coldescrs .. autoattribute:: Table.coldflts .. autoattribute:: Table.coldtypes .. autoattribute:: Table.colindexed .. autoattribute:: Table.colinstances .. autoattribute:: Table.colnames .. autoattribute:: Table.colpathnames .. autoattribute:: Table.cols .. autoattribute:: Table.coltypes .. autoattribute:: Table.description .. autoattribute:: Table.extdim .. autoattribute:: Table.indexed .. autoattribute:: Table.nrows Table properties ~~~~~~~~~~~~~~~~ .. autoattribute:: Table.autoindex .. autoattribute:: Table.colindexes .. autoattribute:: Table.indexedcolpathnames .. autoattribute:: Table.row .. autoattribute:: Table.rowsize Table methods - reading ~~~~~~~~~~~~~~~~~~~~~~~ .. automethod:: Table.col .. automethod:: Table.iterrows .. automethod:: Table.itersequence .. automethod:: Table.itersorted .. automethod:: Table.read .. automethod:: Table.read_coordinates .. automethod:: Table.read_sorted .. automethod:: Table.__getitem__ .. automethod:: Table.__iter__ Table methods - writing ~~~~~~~~~~~~~~~~~~~~~~~ .. automethod:: Table.append .. automethod:: Table.modify_column .. automethod:: Table.modify_columns .. automethod:: Table.modify_coordinates .. automethod:: Table.modify_rows .. automethod:: Table.remove_rows .. automethod:: Table.remove_row .. automethod:: Table.__setitem__ .. _TableMethods_querying: Table methods - querying ~~~~~~~~~~~~~~~~~~~~~~~~ .. automethod:: Table.get_where_list .. automethod:: Table.read_where .. automethod:: Table.where .. automethod:: Table.append_where .. automethod:: Table.will_query_use_indexing Table methods - other ~~~~~~~~~~~~~~~~~~~~~ .. automethod:: Table.copy .. automethod:: Table.flush_rows_to_index .. automethod:: Table.get_enum .. automethod:: Table.reindex .. automethod:: Table.reindex_dirty .. _DescriptionClassDescr: The Description class ~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: Description .. These are defined in the class docstring Description instance variables ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autoattribute:: Description._v_col_objects .. autoattribute:: Description._v_dflts .. autoattribute:: Description._v_dtype .. autoattribute:: Description._v_dtypes .. autoattribute:: Description._v_is_nested .. autoattribute:: Description._v_itemsize .. autoattribute:: Description._v_name .. autoattribute:: Description._v_names .. autoattribute:: Description._v_nested_descr .. autoattribute:: Description._v_nested_formats .. autoattribute:: Description._v_nestedlvl .. autoattribute:: Description._v_nested_names .. autoattribute:: Description._v_pathname .. autoattribute:: Description._v_pathnames .. autoattribute:: Description._v_types Description methods ^^^^^^^^^^^^^^^^^^^ .. automethod:: Description._f_walk .. _RowClassDescr: The Row class ~~~~~~~~~~~~~ .. autoclass:: tables.tableextension.Row .. These are defined in the class docstring Row instance variables ^^^^^^^^^^^^^^^^^^^^^^ .. autoattribute:: tables.tableextension.Row.nrow Row methods ^^^^^^^^^^^ .. automethod:: tables.tableextension.Row.append .. automethod:: tables.tableextension.Row.fetch_all_fields .. automethod:: tables.tableextension.Row.update .. _RowSpecialMethods: Row special methods ^^^^^^^^^^^^^^^^^^^ .. automethod:: tables.tableextension.Row.__contains__ .. automethod:: tables.tableextension.Row.__getitem__ .. automethod:: tables.tableextension.Row.__setitem__ .. _ColsClassDescr: The Cols class ~~~~~~~~~~~~~~ .. autoclass:: Cols .. These are defined in the class docstring Cols instance variables ^^^^^^^^^^^^^^^^^^^^^^^ .. autoattribute:: Cols._v_colnames .. autoattribute:: Cols._v_colpathnames .. autoattribute:: Cols._v_desc Cols properties ^^^^^^^^^^^^^^^ .. autoattribute:: Cols._v_table Cols methods ^^^^^^^^^^^^ .. automethod:: Cols._f_col .. automethod:: Cols.__getitem__ .. automethod:: Cols.__len__ .. automethod:: Cols.__setitem__ .. _ColumnClassDescr: The Column class ~~~~~~~~~~~~~~~~ .. autoclass:: Column .. These are defined in the class docstring .. autoattribute:: Column.descr .. autoattribute:: Column.name .. autoattribute:: Column.pathname Column instance variables ^^^^^^^^^^^^^^^^^^^^^^^^^ .. autoattribute:: Column.dtype .. autoattribute:: Column.index .. autoattribute:: Column.is_indexed .. autoattribute:: Column.maindim .. autoattribute:: Column.shape .. autoattribute:: Column.table .. autoattribute:: Column.type Column methods ^^^^^^^^^^^^^^ .. automethod:: Column.create_index .. automethod:: Column.create_csindex .. automethod:: Column.reindex .. automethod:: Column.reindex_dirty .. automethod:: Column.remove_index Column special methods ^^^^^^^^^^^^^^^^^^^^^^ .. automethod:: Column.__getitem__ .. automethod:: Column.__len__ .. automethod:: Column.__setitem__ PyTables-v.3.1.1/doc/source/usersguide/libref/top_level.rst000066400000000000000000000011511231437614300237170ustar00rootroot00000000000000.. currentmodule:: tables Top-level variables and functions ================================= Global variables ---------------- .. autodata:: __version__ .. autodata:: hdf5_version .. autodata:: hdf5_version Global functions ---------------- .. autofunction:: copy_file .. autofunction:: is_hdf5_file .. autofunction:: is_pytables_file .. autofunction:: open_file .. autofunction:: set_blosc_max_threads .. autofunction:: print_versions .. autofunction:: restrict_flavors .. autofunction:: split_type .. autofunction:: test .. autofunction:: which_lib_version .. autofunction:: silence_hdf5_messages PyTables-v.3.1.1/doc/source/usersguide/optimization.rst000066400000000000000000001510411231437614300232150ustar00rootroot00000000000000Optimization tips ================= .. epigraph:: ... durch planmässiges Tattonieren. [... through systematic, palpable experimentation.] -- Johann Karl Friedrich Gauss [asked how he came upon his theorems] .. currentmodule:: tables On this chapter, you will get deeper knowledge of PyTables internals. PyTables has many tunable features so that you can improve the performance of your application. If you are planning to deal with really large data, you should read carefully this section in order to learn how to get an important efficiency boost for your code. But if your datasets are small (say, up to 10 MB) or your number of nodes is contained (up to 1000), you should not worry about that as the default parameters in PyTables are already tuned for those sizes (although you may want to adjust them further anyway). At any rate, reading this chapter will help you in your life with PyTables. Understanding chunking ---------------------- The underlying HDF5 library that is used by PyTables allows for certain datasets (the so-called *chunked* datasets) to take the data in bunches of a certain length, named *chunks*, and write them on disk as a whole, i.e. the HDF5 library treats chunks as atomic objects and disk I/O is always made in terms of complete chunks. This allows data filters to be defined by the application to perform tasks such as compression, encryption, check-summing, etc. on entire chunks. HDF5 keeps a B-tree in memory that is used to map chunk structures on disk. The more chunks that are allocated for a dataset the larger the B-tree. Large B-trees take memory and cause file storage overhead as well as more disk I/O and higher contention forthe metadata cache. Consequently, it's important to balance between memory and I/O overhead (small B-trees) and time to access data (big B-trees). In the next couple of sections, you will discover how to inform PyTables about the expected size of your datasets for allowing a sensible computation of the chunk sizes. Also, you will be presented some experiments so that you can get a feeling on the consequences of manually specifying the chunk size. Although doing this latter is only reserved to experienced people, these benchmarks may allow you to understand more deeply the chunk size implications and let you quickly start with the fine-tuning of this important parameter. .. _expectedRowsOptim: Informing PyTables about expected number of rows in tables or arrays ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PyTables can determine a sensible chunk size to your dataset size if you helps it by providing an estimation of the final number of rows for an extensible leaf [1]_. You should provide this information at leaf creation time by passing this value to the expectedrows argument of the :meth:`File.create_table` method or :meth:`File.create_earray` method (see :ref:`EArrayClassDescr`). For VLArray leaves, you must pass the expected size in MBytes by using the argument expectedsizein MB of :meth:`File.create_vlarray` (see :ref:`VLArrayClassDescr`) instead. When your leaf size is bigger than 10 MB (take this figure only as a reference, not strictly), by providing this guess you will be optimizing the access to your data. When the table or array size is larger than, say 100MB, you are *strongly* suggested to provide such a guess; failing to do that may cause your application to do very slow I/O operations and to demand *huge* amounts of memory. You have been warned! .. _chunksizeFineTune: Fine-tuning the chunksize ~~~~~~~~~~~~~~~~~~~~~~~~~ .. warning:: This section is mostly meant for experts. If you are a beginner, you must know that setting manually the chunksize is a potentially dangerous action. Most of the time, informing PyTables about the extent of your dataset is enough. However, for more sophisticated applications, when one has special requirements for doing the I/O or when dealing with really large datasets, you should really understand the implications of the chunk size in order to be able to find the best value for your own application. You can specify the chunksize for every chunked dataset in PyTables by passing the chunkshape argument to the corresponding constructors. It is important to point out that chunkshape is not exactly the same thing than a chunksize; in fact, the chunksize of a dataset can be computed multiplying all the dimensions of the chunkshape among them and multiplying the outcome by the size of the atom. We are going to describe a series of experiments where an EArray of 15 GB is written with different chunksizes, and then it is accessed in both sequential (i.e. first element 0, then element 1 and so on and so forth until the data is exhausted) and random mode (i.e. single elements are read randomly all through the dataset). These benchmarks have been carried out with PyTables 2.1 on a machine with an Intel Core2 processor @ 3 GHz and a RAID-0 made of two SATA disks spinning at 7200 RPM, and using GNU/Linux with an XFS filesystem. The script used for the benchmarks is available in bench/optimal-chunksize.py. In figures :ref:`Figure 1 `, :ref:`Figure 2 `, :ref:`Figure 3 ` and :ref:`Figure 4 `, you can see how the chunksize affects different aspects, like creation time, file sizes, sequential read time and random read time. So, if you properly inform PyTables about the extent of your datasets, you will get an automatic chunksize value (256 KB in this case) that is pretty optimal for most of uses. However, if what you want is, for example, optimize the creation time when using the Zlib compressor, you may want to reduce the chunksize to 32 KB (see :ref:`Figure 1 `). Or, if your goal is to optimize the sequential access time for an dataset compressed with Blosc, you may want to increase the chunksize to 512 KB (see :ref:`Figure 3 `). You will notice that, by manually specifying the chunksize of a leave you will not normally get a drastic increase in performance, but at least, you have the opportunity to fine-tune such an important parameter for improve performance. .. _createTime-chunksize: .. figure:: images/create-chunksize-15GB.png :align: center **Figure 1. Creation time per element for a 15 GB EArray and different chunksizes.** .. _fileSizes-chunksize: .. figure:: images/filesizes-chunksize-15GB.png :align: center **Figure 2. File sizes for a 15 GB EArray and different chunksizes.** .. _seqTime-chunksize: .. figure:: images/seq-chunksize-15GB.png :align: center **Figure 3. Sequential access time per element for a 15 GB EArray and different chunksizes.** .. _randomTime-chunksize: .. figure:: images/random-chunksize-15GB.png :align: center **Figure 4. Random access time per element for a 15 GB EArray and different chunksizes.** Finally, it is worth noting that adjusting the chunksize can be specially important if you want to access your dataset by blocks of certain dimensions. In this case, it is normally a good idea to set your chunkshape to be the same than these dimensions; you only have to be careful to not end with a too small or too large chunksize. As always, experimenting prior to pass your application into production is your best ally. .. _searchOptim: Accelerating your searches -------------------------- .. note:: Many of the explanations and plots in this section and the forthcoming ones still need to be updated to include Blosc (see :ref:`[BLOSC] `), the new and powerful compressor added in PyTables 2.2 series. You should expect it to be the fastest compressor among all the described here, and its use is strongly recommended whenever you need extreme speed and not a very high compression ratio. Searching in tables is one of the most common and time consuming operations that a typical user faces in the process of mining through his data. Being able to perform queries as fast as possible will allow more opportunities for finding the desired information quicker and also allows to deal with larger datasets. PyTables offers many sort of techniques so as to speed-up the search process as much as possible and, in order to give you hints to use them based, a series of benchmarks have been designed and carried out. All the results presented in this section have been obtained with synthetic, random data and using PyTables 2.1. Also, the tests have been conducted on a machine with an Intel Core2 (64-bit) @ 3 GHz processor with RAID-0 disk storage (made of four spinning disks @ 7200 RPM), using GNU/Linux with an XFS filesystem. The script used for the benchmarks is available in bench/indexed_search.py. As your data, queries and platform may be totally different for your case, take this just as a guide because your mileage may vary (and will vary). In order to be able to play with tables with a number of rows as large as possible, the record size has been chosen to be rather small (24 bytes). Here it is its definition:: class Record(tables.IsDescription): col1 = tables.Int32Col() col2 = tables.Int32Col() col3 = tables.Float64Col() col4 = tables.Float64Col() In the next sections, we will be optimizing the times for a relatively complex query like this:: result = [row['col2'] for row in table if ( ((row['col4'] >= lim1 and row['col4'] < lim2) or ((row['col2'] > lim3 and row['col2'] < lim4])) and ((row['col1']+3.1*row['col2']+row['col3']*row['col4']) > lim5) )] (for future reference, we will call this sort of queries *regular* queries). So, if you want to see how to greatly improve the time taken to run queries like this, keep reading. .. _inkernelSearch: In-kernel searches ~~~~~~~~~~~~~~~~~~ PyTables provides a way to accelerate data selections inside of a single table, through the use of the :ref:`TableMethods_querying` iterator and related query methods. This mode of selecting data is called *in-kernel*. Let's see an example of an *in-kernel* query based on the *regular* one mentioned above:: result = [row['col2'] for row in table.where( '''(((col4 >= lim1) & (col4 < lim2)) | ((col2 > lim3) & (col2 < lim4)) & ((col1+3.1*col2+col3*col4) > lim5))''')] This simple change of mode selection can improve search times quite a lot and actually make PyTables very competitive when compared against typical relational databases as you can see in :ref:`Figure 5 ` and :ref:`Figure 6 `. .. _sequentialTimes-10m: .. figure:: images/Q7-10m-noidx.png :align: center **Figure 5. Times for non-indexed complex queries in a small table with 10 millions of rows: the data fits in memory.** By looking at :ref:`Figure 5 ` you can see how in the case that table data fits easily in memory, in-kernel searches on uncompressed tables are generally much faster (10x) than standard queries as well as PostgreSQL (5x). Regarding compression, we can see how Zlib compressor actually slows down the performance of in-kernel queries by a factor 3.5x; however, it remains faster than PostgreSQL (40%). On his hand, LZO compressor only decreases the performance by a 75% with respect to uncompressed in-kernel queries and is still a lot faster than PostgreSQL (3x). Finally, one can observe that, for low selectivity queries (large number of hits), PostgreSQL performance degrades quite steadily, while in PyTables this slow down rate is significantly smaller. The reason of this behaviour is not entirely clear to the authors, but the fact is clearly reproducible in our benchmarks. But, why in-kernel queries are so fast when compared with regular ones?. The answer is that in regular selection mode the data for all the rows in table has to be brought into Python space so as to evaluate the condition and decide if the corresponding field should be added to the result list. On the contrary, in the in-kernel mode, the condition is passed to the PyTables kernel (hence the name), written in C, and evaluated there at full C speed (with the help of the integrated Numexpr package, see :ref:`[NUMEXPR] `), so that the only values that are brought to Python space are the rows that fulfilled the condition. Hence, for selections that only have a relatively small number of hits (compared with the total amount of rows), the savings are very large. It is also interesting to note the fact that, although for queries with a large number of hits the speed-up is not as high, it is still very important. On the other hand, when the table is too large to fit in memory (see :ref:`Figure 6 `), the difference in speed between regular and in-kernel is not so important, but still significant (2x). Also, and curiously enough, large tables compressed with Zlib offers slightly better performance (around 20%) than uncompressed ones; this is because the additional CPU spent by the uncompressor is compensated by the savings in terms of net I/O (one has to read less actual data from disk). However, when using the extremely fast LZO compressor, it gives a clear advantage over Zlib, and is up to 2.5x faster than not using compression at all. The reason is that LZO decompression speed is much faster than Zlib, and that allows PyTables to read the data at full disk speed (i.e. the bottleneck is in the I/O subsystem, not in the CPU). In this case the compression rate is around 2.5x, and this is why the data can be read 2.5x faster. So, in general, using the LZO compressor is the best way to ensure best reading/querying performance for out-of-core datasets (more about how compression affects performance in :ref:`compressionIssues`). .. _sequentialTimes-1g: .. figure:: images/Q8-1g-noidx.png :align: center **Figure 6. Times for non-indexed complex queries in a large table with 1 billion of rows: the data does not fit in memory.** Furthermore, you can mix the *in-kernel* and *regular* selection modes for evaluating arbitrarily complex conditions making use of external functions. Look at this example:: result = [ row['var2'] for row in table.where('(var3 == "foo") & (var1 <= 20)') if your_function(row['var2']) ] Here, we use an *in-kernel* selection to choose rows according to the values of the var3 and var1 fields. Then, we apply a *regular* selection to complete the query. Of course, when you mix the *in-kernel* and *regular* selection modes you should pass the most restrictive condition to the *in-kernel* part, i.e. to the where() iterator. In situations where it is not clear which is the most restrictive condition, you might want to experiment a bit in order to find the best combination. However, since in-kernel condition strings allow rich expressions allowing the coexistence of multiple columns, variables, arithmetic operations and many typical functions, it is unlikely that you will be forced to use external regular selections in conditions of small to medium complexity. See :ref:`condition_syntax` for more information on in-kernel condition syntax. Indexed searches ~~~~~~~~~~~~~~~~ When you need more speed than *in-kernel* selections can offer you, PyTables offers a third selection method, the so-called *indexed* mode (based on the highly efficient OPSI indexing engine ). In this mode, you have to decide which column(s) you are going to apply your selections over, and index them. Indexing is just a kind of sorting operation over a column, so that searches along such a column (or columns) will look at this sorted information by using a *binary search* which is much faster than the *sequential search* described in the previous section. You can index the columns you want by calling the :meth:`Column.create_index` method on an already created table. For example:: indexrows = table.cols.var1.create_index() indexrows = table.cols.var2.create_index() indexrows = table.cols.var3.create_index() will create indexes for all var1, var2 and var3 columns. After you have indexed a series of columns, the PyTables query optimizer will try hard to discover the usable indexes in a potentially complex expression. However, there are still places where it cannot determine that an index can be used. See below for examples where the optimizer can safely determine if an index, or series of indexes, can be used or not. Example conditions where an index can be used: - var1 >= "foo" (var1 is used) - var1 >= mystr (var1 is used) - (var1 >= "foo") & (var4 > 0.0) (var1 is used) - ("bar" <= var1) & (var1 < "foo") (var1 is used) - (("bar" <= var1) & (var1 < "foo")) & (var4 > 0.0) (var1 is used) - (var1 >= "foo") & (var3 > 10) (var1 and var3 are used) - (var1 >= "foo") | (var3 > 10) (var1 and var3 are used) - ~(var1 >= "foo") | ~(var3 > 10) (var1 and var3 are used) Example conditions where an index can *not* be used: - var4 > 0.0 (var4 is not indexed) - var1 != 0.0 (range has two pieces) - ~(("bar" <= var1) & (var1 < "foo")) & (var4 > 0.0) (negation of a complex boolean expression) .. note:: From PyTables 2.3 on, several indexes can be used in a single query. .. note:: If you want to know for sure whether a particular query will use indexing or not (without actually running it), you are advised to use the :meth:`Table.will_query_use_indexing` method. One important aspect of the new indexing in PyTables (>= 2.3) is that it has been designed from the ground up with the goal of being capable to effectively manage very large tables. To this goal, it sports a wide spectrum of different quality levels (also called optimization levels) for its indexes so that the user can choose the best one that suits her needs (more or less size, more or less performance). In :ref:`Figure 7 `, you can see that the times to index columns in tables can be really short. In particular, the time to index a column with 1 billion rows (1 Gigarow) with the lowest optimization level is less than 4 minutes while indexing the same column with full optimization (so as to get a completely sorted index or CSI) requires around 1 hour. These are rather competitive figures compared with a relational database (in this case, PostgreSQL 8.3.1, which takes around 1.5 hours for getting the index done). This is because PyTables is geared towards read-only or append-only tables and takes advantage of this fact to optimize the indexes properly. On the contrary, most relational databases have to deliver decent performance in other scenarios as well (specially updates and deletions), and this fact leads not only to slower index creation times, but also to indexes taking much more space on disk, as you can see in :ref:`Figure 8 `. .. _createIndexTimes: .. figure:: images/create-index-time-int32-float64.png :align: center **Figure 7. Times for indexing an Int32 and Float64 column.** .. _indexSizes: .. figure:: images/indexes-sizes2.png :align: center **Figure 8. Sizes for an index of a Float64 column with 1 billion of rows.** The user can select the index quality by passing the desired optlevel and kind arguments to the :meth:`Column.create_index` method. We can see in figures :ref:`Figure 7 ` and :ref:`Figure 8 ` how the different optimization levels affects index time creation and index sizes. So, which is the effect of the different optimization levels in terms of query times? You can see that in :ref:`Figure 9 `. .. _queryTimes-indexed-optlevels: .. figure:: images/Q8-1g-idx-optlevels.png :align: center **Figure 9. Times for complex queries with a cold cache (mean of 5 first random queries) for different optimization levels. Benchmark made on a machine with Intel Core2 (64-bit) @ 3 GHz processor with RAID-0 disk storage.** Of course, compression also has an effect when doing indexed queries, although not very noticeable, as can be seen in :ref:`Figure 10 `. As you can see, the difference between using no compression and using Zlib or LZO is very little, although LZO achieves relatively better performance generally speaking. .. _queryTimes-indexed-compress: .. figure:: images/Q8-1g-idx-compress.png :align: center **Figure 10. Times for complex queries with a cold cache (mean of 5 first random queries) for different compressors.** You can find a more complete description and benchmarks about OPSI, the indexing system of PyTables (>= 2.3) in :ref:`[OPSI] `. Indexing and Solid State Disks (SSD) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Lately, the long promised Solid State Disks (SSD for brevity) with decent capacities and affordable prices have finally hit the market and will probably stay in coexistence with the traditional spinning disks for the foreseeable future (separately or forming *hybrid* systems). SSD have many advantages over spinning disks, like much less power consumption and better throughput. But of paramount importance, specially in the context of accelerating indexed queries, is its very reduced latency during disk seeks, which is typically 100x better than traditional disks. Such a huge improvement has to have a clear impact in reducing the query times, specially when the selectivity is high (i.e. the number of hits is small). In order to offer an estimate on the performance improvement we can expect when using a low-latency SSD instead of traditional spinning disks, the benchmark in the previous section has been repeated, but this time using a single SSD disk instead of the four spinning disks in RAID-0. The result can be seen in :ref:`Figure 11 `. There one can see how a query in a table of 1 billion of rows with 100 hits took just 1 tenth of second when using a SSD, instead of 1 second that needed the RAID made of spinning disks. This factor of 10x of speed-up for high-selectivity queries is nothing to sneeze at, and should be kept in mind when really high performance in queries is needed. It is also interesting that using compression with LZO does have a clear advantage over when no compression is done. .. _queryTimes-indexed-SSD: .. figure:: images/Q8-1g-idx-SSD.png :align: center **Figure 11. Times for complex queries with a cold cache (mean of 5 first random queries) for different disk storage (SSD vs spinning disks).** Finally, we should remark that SSD can't compete with traditional spinning disks in terms of capacity as they can only provide, for a similar cost, between 1/10th and 1/50th of the size of traditional disks. It is here where the compression capabilities of PyTables can be very helpful because both tables and indexes can be compressed and the final space can be reduced by typically 2x to 5x (4x to 10x when compared with traditional relational databases). Best of all, as already mentioned, performance is not degraded when compression is used, but actually *improved*. So, by using PyTables and SSD you can query larger datasets that otherwise would require spinning disks when using other databases In fact, we were unable to run the PostgreSQL benchmark in this case because the space needed exceeded the capacity of our SSD., while allowing improvements in the speed of indexed queries between 2x (for medium to low selectivity queries) and 10x (for high selectivity queries). Achieving ultimate speed: sorted tables and beyond ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. warning:: Sorting a large table is a costly operation. The next procedure should only be performed when your dataset is mainly read-only and meant to be queried many times. When querying large tables, most of the query time is spent in locating the interesting rows to be read from disk. In some occasions, you may have queries whose result depends *mainly* of one single column (a query with only one single condition is the trivial example), so we can guess that sorting the table by this column would lead to locate the interesting rows in a much more efficient way (because they would be mostly *contiguous*). We are going to confirm this guess. For the case of the query that we have been using in the previous sections:: result = [row['col2'] for row in table.where( '''(((col4 >= lim1) & (col4 < lim2)) | ((col2 > lim3) & (col2 < lim4)) & ((col1+3.1*col2+col3*col4) > lim5))''')] it is possible to determine, by analysing the data distribution and the query limits, that col4 is such a *main column*. So, by ordering the table by the col4 column (for example, by specifying setting the column to sort by in the sortby parameter in the :meth:`Table.copy` method and re-indexing col2 and col4 afterwards, we should get much faster performance for our query. This is effectively demonstrated in :ref:`Figure 12 `, where one can see how queries with a low to medium (up to 10000) number of hits can be done in around 1 tenth of second for a RAID-0 setup and in around 1 hundredth of second for a SSD disk. This represents up to more that 100x improvement in speed with respect to the times with unsorted tables. On the other hand, when the number of hits is large (> 1 million), the query times grow almost linearly, showing a near-perfect scalability for both RAID-0 and SSD setups (the sequential access to disk becomes the bottleneck in this case). .. _queryTimes-indexed-sorted: .. figure:: images/Q8-1g-idx-sorted.png :align: center **Figure 12. Times for complex queries with a cold cache (mean of 5 first random queries) for unsorted and sorted tables.** Even though we have shown many ways to improve query times that should fulfill the needs of most of people, for those needing more, you can for sure discover new optimization opportunities. For example, querying against sorted tables is limited mainly by sequential access to data on disk and data compression capability, so you may want to read :ref:`chunksizeFineTune`, for ways on improving this aspect. Reading the other sections of this chapter will help in finding new roads for increasing the performance as well. You know, the limit for stopping the optimization process is basically your imagination (but, most plausibly, your available time ;-). .. _compressionIssues: Compression issues ------------------ One of the beauties of PyTables is that it supports compression on tables and arrays [2]_, although it is not used by default. Compression of big amounts of data might be a bit controversial feature, because it has a legend of being a very big consumer of CPU time resources. However, if you are willing to check if compression can help not only by reducing your dataset file size but *also* by improving I/O efficiency, specially when dealing with very large datasets, keep reading. A study on supported compression libraries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The compression library used by default is the *Zlib* (see :ref:`[ZLIB] `). Since HDF5 *requires* it, you can safely use it and expect that your HDF5 files will be readable on any other platform that has HDF5 libraries installed. Zlib provides good compression ratio, although somewhat slow, and reasonably fast decompression. Because of that, it is a good candidate to be used for compressing you data. However, in some situations it is critical to have a *very good decompression speed* (at the expense of lower compression ratios or more CPU wasted on compression, as we will see soon). In others, the emphasis is put in achieving the *maximum compression ratios*, no matter which reading speed will result. This is why support for two additional compressors has been added to PyTables: LZO (see :ref:`[LZO] `) and bzip2 (see :ref:`[BZIP2] `). Following the author of LZO (and checked by the author of this section, as you will see soon), LZO offers pretty fast compression and extremely fast decompression. In fact, LZO is so fast when compressing/decompressing that it may well happen (that depends on your data, of course) that writing or reading a compressed dataset is sometimes faster than if it is not compressed at all (specially when dealing with extremely large datasets). This fact is very important, specially if you have to deal with very large amounts of data. Regarding bzip2, it has a reputation of achieving excellent compression ratios, but at the price of spending much more CPU time, which results in very low compression/decompression speeds. Be aware that the LZO and bzip2 support in PyTables is not standard on HDF5, so if you are going to use your PyTables files in other contexts different from PyTables you will not be able to read them. Still, see the :ref:`ptrepackDescr` (where the ptrepack utility is described) to find a way to free your files from LZO or bzip2 dependencies, so that you can use these compressors locally with the warranty that you can replace them with Zlib (or even remove compression completely) if you want to use these files with other HDF5 tools or platforms afterwards. In order to allow you to grasp what amount of compression can be achieved, and how this affects performance, a series of experiments has been carried out. All the results presented in this section (and in the next one) have been obtained with synthetic data and using PyTables 1.3. Also, the tests have been conducted on a IBM OpenPower 720 (e-series) with a PowerPC G5 at 1.65 GHz and a hard disk spinning at 15K RPM. As your data and platform may be totally different for your case, take this just as a guide because your mileage may vary. Finally, and to be able to play with tables with a number of rows as large as possible, the record size has been chosen to be small (16 bytes). Here is its definition:: class Bench(IsDescription): var1 = StringCol(length=4) var2 = IntCol() var3 = FloatCol() With this setup, you can look at the compression ratios that can be achieved in :ref:`Figure 13 `. As you can see, LZO is the compressor that performs worse in this sense, but, curiously enough, there is not much difference between Zlib and bzip2. .. _comprTblComparison: .. figure:: images/compressed-recordsize.png :align: center **Figure 13. Comparison between different compression libraries.** Also, PyTables lets you select different compression levels for Zlib and bzip2, although you may get a bit disappointed by the small improvement that these compressors show when dealing with a combination of numbers and strings as in our example. As a reference, see plot :ref:`Figure 14 ` for a comparison of the compression achieved by selecting different levels of Zlib. Very oddly, the best compression ratio corresponds to level 1 (!). See later for an explanation and more figures on this subject. .. _comprZlibComparison: .. figure:: images/compressed-recordsize-zlib.png :align: center **Figure 14. Comparison between different compression levels of Zlib.** Have also a look at :ref:`Figure 15 `. It shows how the speed of writing rows evolves as the size (number of rows) of the table grows. Even though in these graphs the size of one single row is 16 bytes, you can most probably extrapolate these figures to other row sizes. .. _comprWriteComparison: .. figure:: images/compressed-writing.png :align: center **Figure 15. Writing tables with several compressors.** In :ref:`Figure 16 ` you can see how compression affects the reading performance. In fact, what you see in the plot is an *in-kernel selection* speed, but provided that this operation is very fast (see :ref:`inkernelSearch`), we can accept it as an actual read test. Compared with the reference line without compression, the general trend here is that LZO does not affect too much the reading performance (and in some points it is actually better), Zlib makes speed drop to a half, while bzip2 is performing very slow (up to 8x slower). Also, in the same :ref:`Figure 16 ` you can notice some strange peaks in the speed that we might be tempted to attribute to libraries on which PyTables relies (HDF5, compressors...), or to PyTables itself. However, :ref:`Figure 17 ` reveals that, if we put the file in the filesystem cache (by reading it several times before, for example), the evolution of the performance is much smoother. So, the most probable explanation would be that such peaks are a consequence of the underlying OS filesystem, rather than a flaw in PyTables (or any other library behind it). Another consequence that can be derived from the aforementioned plot is that LZO decompression performance is much better than Zlib, allowing an improvement in overall speed of more than 2x, and perhaps more important, the read performance for really large datasets (i.e. when they do not fit in the OS filesystem cache) can be actually *better* than not using compression at all. Finally, one can see that reading performance is very badly affected when bzip2 is used (it is 10x slower than LZO and 4x than Zlib), but this was somewhat expected anyway. .. _comprReadNoCacheComparison: .. figure:: images/compressed-select-nocache.png :align: center **Figure 16. Selecting values in tables with several compressors. The file is not in the OS cache.** .. _comprReadCacheComparison: .. figure:: images/compressed-select-cache.png :align: center **Figure 17. Selecting values in tables with several compressors. The file is in the OS cache.** So, generally speaking and looking at the experiments above, you can expect that LZO will be the fastest in both compressing and decompressing, but the one that achieves the worse compression ratio (although that may be just OK for many situations, specially when used with shuffling - see :ref:`ShufflingOptim`). bzip2 is the slowest, by large, in both compressing and decompressing, and besides, it does not achieve any better compression ratio than Zlib. Zlib represents a balance between them: it's somewhat slow compressing (2x) and decompressing (3x) than LZO, but it normally achieves better compression ratios. Finally, by looking at the plots :ref:`Figure 18 `, :ref:`Figure 19 `, and the aforementioned :ref:`Figure 14 ` you can see why the recommended compression level to use for all compression libraries is 1. This is the lowest level of compression, but as the size of the underlying HDF5 chunk size is normally rather small compared with the size of compression buffers, there is not much point in increasing the latter (i.e. increasing the compression level). Nonetheless, in some situations (like for example, in extremely large tables or arrays, where the computed chunk size can be rather large) you may want to check, on your own, how the different compression levels do actually affect your application. You can select the compression library and level by setting the complib and complevel keywords in the Filters class (see :ref:`FiltersClassDescr`). A compression level of 0 will completely disable compression (the default), 1 is the less memory and CPU time demanding level, while 9 is the maximum level and the most memory demanding and CPU intensive. Finally, have in mind that LZO is not accepting a compression level right now, so, when using LZO, 0 means that compression is not active, and any other value means that LZO is active. So, in conclusion, if your ultimate goal is writing and reading as fast as possible, choose LZO. If you want to reduce as much as possible your data, while retaining acceptable read speed, choose Zlib. Finally, if portability is important for you, Zlib is your best bet. So, when you want to use bzip2? Well, looking at the results, it is difficult to recommend its use in general, but you may want to experiment with it in those cases where you know that it is well suited for your data pattern (for example, for dealing with repetitive string datasets). .. _comprWriteZlibComparison: .. figure:: images/compressed-writing-zlib.png :align: center **Figure 18. Writing in tables with different levels of compression.** .. _comprReadZlibComparison: .. figure:: images/compressed-select-cache-zlib.png :align: center **Figure 19. Selecting values in tables with different levels of compression. The file is in the OS cache.** .. _ShufflingOptim: Shuffling (or how to make the compression process more effective) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The HDF5 library provides an interesting filter that can leverage the results of your favorite compressor. Its name is *shuffle*, and because it can greatly benefit compression and it does not take many CPU resources (see below for a justification), it is active *by default* in PyTables whenever compression is activated (independently of the chosen compressor). It is deactivated when compression is off (which is the default, as you already should know). Of course, you can deactivate it if you want, but this is not recommended. So, how does this mysterious filter exactly work? From the HDF5 reference manual:: "The shuffle filter de-interlaces a block of data by reordering the bytes. All the bytes from one consistent byte position of each data element are placed together in one block; all bytes from a second consistent byte position of each data element are placed together a second block; etc. For example, given three data elements of a 4-byte datatype stored as 012301230123, shuffling will re-order data as 000111222333. This can be a valuable step in an effective compression algorithm because the bytes in each byte position are often closely related to each other and putting them together can increase the compression ratio." In :ref:`Figure 20 ` you can see a benchmark that shows how the *shuffle* filter can help the different libraries in compressing data. In this experiment, shuffle has made LZO compress almost 3x more (!), while Zlib and bzip2 are seeing improvements of 2x. Once again, the data for this experiment is synthetic, and *shuffle* seems to do a great work with it, but in general, the results will vary in each case [3]_. .. _comprShuffleComparison: .. figure:: images/compressed-recordsize-shuffle.png :align: center **Figure 20. Comparison between different compression libraries with and without the shuffle filter.** At any rate, the most remarkable fact about the *shuffle* filter is the relatively high level of compression that compressor filters can achieve when used in combination with it. A curious thing to note is that the Bzip2 compression rate does not seem very much improved (less than a 40%), and what is more striking, Bzip2+shuffle does compress quite *less* than Zlib+shuffle or LZO+shuffle combinations, which is kind of unexpected. The thing that seems clear is that Bzip2 is not very good at compressing patterns that result of shuffle application. As always, you may want to experiment with your own data before widely applying the Bzip2+shuffle combination in order to avoid surprises. Now, how does shuffling affect performance? Well, if you look at plots :ref:`Figure 21 `, :ref:`Figure 22 ` and :ref:`Figure 23 `, you will get a somewhat unexpected (but pleasant) surprise. Roughly, *shuffle* makes the writing process (shuffling+compressing) faster (approximately a 15% for LZO, 30% for Bzip2 and a 80% for Zlib), which is an interesting result by itself. But perhaps more exciting is the fact that the reading process (unshuffling+decompressing) is also accelerated by a similar extent (a 20% for LZO, 60% for Zlib and a 75% for Bzip2, roughly). .. _comprWriteShuffleComparison: .. figure:: images/compressed-writing-shuffle.png :align: center **Figure 21. Writing with different compression libraries with and without the shuffle filter.** .. _comprReadNoCacheShuffleComparison: .. figure:: images/compressed-select-nocache-shuffle-only.png :align: center **Figure 22. Reading with different compression libraries with the shuffle filter. The file is not in OS cache.** .. _comprReadCacheShuffleComparison: .. figure:: images/compressed-select-cache-shuffle.png :align: center **Figure 23. Reading with different compression libraries with and without the shuffle filter. The file is in OS cache.** You may wonder why introducing another filter in the write/read pipelines does effectively accelerate the throughput. Well, maybe data elements are more similar or related column-wise than row-wise, i.e. contiguous elements in the same column are more alike, so shuffling makes the job of the compressor easier (faster) and more effective (greater ratios). As a side effect, compressed chunks do fit better in the CPU cache (at least, the chunks are smaller!) so that the process of unshuffle/decompress can make a better use of the cache (i.e. reducing the number of CPU cache faults). So, given the potential gains (faster writing and reading, but specially much improved compression level), it is a good thing to have such a filter enabled by default in the battle for discovering redundancy when you want to compress your data, just as PyTables does. Using Psyco ----------- Psyco (see :ref:`[PSYCO] `) is a kind of specialized compiler for Python that typically accelerates Python applications with no change in source code. You can think of Psyco as a kind of just-in-time (JIT) compiler, a little bit like Java's, that emits machine code on the fly instead of interpreting your Python program step by step. The result is that your unmodified Python programs run faster. Psyco is very easy to install and use, so in most scenarios it is worth to give it a try. However, it only runs on Intel 386 architectures, so if you are using other architectures, you are out of luck (and, moreover, it seems that there are no plans to support other platforms). Besides, with the addition of flexible (and very fast) in-kernel queries (by the way, they cannot be optimized at all by Psyco), the use of Psyco will only help in rather few scenarios. In fact, the only important situation that you might benefit right now from using Psyco (I mean, in PyTables contexts) is for speeding-up the write speed in tables when using the Row interface (see :ref:`RowClassDescr`). But again, this latter case can also be accelerated by using the :meth:`Table.append` method and building your own buffers [4]_. As an example, imagine that you have a small script that reads and selects data over a series of datasets, like this:: def read_file(filename): "Select data from all the tables in filename" fileh = open_file(filename, mode = "r") result = [] for table in fileh("/", 'Table'): result = [p['var3'] for p in table if p['var2'] <= 20] fileh.close() return result if __name__=="__main__": print(read_file("myfile.h5")) In order to accelerate this piece of code, you can rewrite your main program to look like:: if __name__=="__main__": import psyco psyco.bind(read_file) print(read_file("myfile.h5")) That's all! From now on, each time that you execute your Python script, Psyco will deploy its sophisticated algorithms so as to accelerate your calculations. You can see in the graphs :ref:`Figure 24 ` and :ref:`Figure 25 ` how much I/O speed improvement you can get by using Psyco. By looking at this figures you can get an idea if these improvements are of your interest or not. In general, if you are not going to use compression you will take advantage of Psyco if your tables are medium sized (from a thousand to a million rows), and this advantage will disappear progressively when the number of rows grows well over one million. However if you use compression, you will probably see improvements even beyond this limit (see :ref:`compressionIssues`). As always, there is no substitute for experimentation with your own dataset. .. _psycoWriteComparison: .. figure:: images/write-medium-psyco-nopsyco-comparison.png :align: center **Figure 24. Writing tables with/without Psyco.** .. _psycoReadComparison: .. figure:: images/read-medium-psyco-nopsyco-comparison.png :align: center **Figure 25. Reading tables with/without Psyco.** .. _LRUOptim: Getting the most from the node LRU cache ---------------------------------------- One limitation of the initial versions of PyTables was that they needed to load all nodes in a file completely before being ready to deal with them, making the opening times for files with a lot of nodes very high and unacceptable in many cases. Starting from PyTables 1.2 on, a new lazy node loading schema was setup that avoids loading all the nodes of the *object tree* in memory. In addition, a new LRU cache was introduced in order to accelerate the access to already visited nodes. This cache (one per file) is responsible for keeping up the most recently visited nodes in memory and discard the least recent used ones. This represents a big advantage over the old schema, not only in terms of memory usage (as there is no need to load *every* node in memory), but it also adds very convenient optimizations for working interactively like, for example, speeding-up the opening times of files with lots of nodes, allowing to open almost any kind of file in typically less than one tenth of second (compare this with the more than 10 seconds for files with more than 10000 nodes in PyTables pre-1.2 era) as well as optimizing the access to frequently visited nodes. See for more info on the advantages (and also drawbacks) of this approach. One thing that deserves some discussion is the election of the parameter that sets the maximum amount of nodes to be kept in memory at any time. As PyTables is meant to be deployed in machines that can have potentially low memory, the default for it is quite conservative (you can look at its actual value in the :data:`parameters.NODE_CACHE_SLOTS` parameter in module :file:`tables/parameters.py`). However, if you usually need to deal with files that have many more nodes than the maximum default, and you have a lot of free memory in your system, then you may want to experiment in order to see which is the appropriate value of :data:`parameters.NODE_CACHE_SLOTS` that fits better your needs. As an example, look at the next code:: def browse_tables(filename): fileh = open_file(filename,'a') group = fileh.root.newgroup for j in range(10): for tt in fileh.walk_nodes(group, "Table"): title = tt.attrs.TITLE for row in tt: pass fileh.close() We will be running the code above against a couple of files having a ``/newgroup`` containing 100 tables and 1000 tables respectively. In addition, this benchmark is run twice for two different values of the LRU cache size, specifically 256 and 1024. You can see the results in :ref:`table `. .. _optimization_table_1: .. only:: not latex .. table:: **Retrieval speed and memory consumption depending on the number of nodes in LRU cache.** ====================== =========== === ======= ==== ==== === ======= ==== ==== Number: 100 nodes 1000 nodes ---------------------------------- --------------------- --------------------- Mem & Speed Memory (MB) Time (ms) Memory (MB) Time (ms) ---------------------------------- ----------- --------- ----------- --------- Node is coming from... Cache size 256 1024 256 1024 256 1024 256 1024 ====================== =========== === ======= ==== ==== === ======= ==== ==== Disk 14 14 1.24 1.24 51 66 1.33 1.31 Cache 14 14 0.53 0.52 65 73 1.35 0.68 ====================== =========== === ======= ==== ==== === ======= ==== ==== .. raw:: latex \\ \\ \begin{threeparttable} \capstart\caption{Retrieval speed and memory consumption depending on the number of nodes in LRU cache.} \begin{tabulary}{\linewidth}{|l|l|r|r|r|r|r|r|r|r|} \hline \multicolumn{2}{|l|}{\textbf{Number:}} & \multicolumn{4}{|c|}{\textbf{100 nodes}} & \multicolumn{4}{|c|}{\textbf{1000 nodes}} \\ \hline \multicolumn{2}{|l|}{\textbf{Mem and Speed}} & \multicolumn{2}{|c|}{\textbf{Memory (MB)}} & \multicolumn{2}{|c|}{\textbf{Time (ms)}} & \multicolumn{2}{|c|}{\textbf{Memory (MB)}} & \multicolumn{2}{|c|}{\textbf{Time (ms)}}\\ \hline \textbf{Node is coming from...} & \textbf{Cache size} & \textbf{256} & \textbf{1024} & \textbf{256} & \textbf{1024} & \textbf{256} & \textbf{1024} & \textbf{256} & \textbf{1024}\\ \hline Disk & & 14 & 14 & 1.24 & 1.24 & 51 & 66 & 1.33 & 1.31 \\ Cache & & 14 & 14 & 0.53 & 0.52 & 65 & 73 & 1.35 & 0.68 \\ \hline \end{tabulary} \end{threeparttable} \\ \\ From the data in :ref:`table `, one can see that when the number of objects that you are dealing with does fit in cache, you will get better access times to them. Also, incrementing the node cache size effectively consumes more memory *only* if the total nodes exceeds the slots in cache; otherwise the memory consumption remains the same. It is also worth noting that incrementing the node cache size in the case you want to fit all your nodes in cache does not take much more memory than being too conservative. On the other hand, it might happen that the speed-up that you can achieve by allocating more slots in your cache is not worth the amount of memory used. Also worth noting is that if you have a lot of memory available and performance is absolutely critical, you may want to try out a negative value for :data:`parameters.NODE_CACHE_SLOTS`. This will cause that all the touched nodes will be kept in an internal dictionary and this is the faster way to load/retrieve nodes. However, and in order to avoid a large memory consumption, the user will be warned when the number of loaded nodes will reach the ``-NODE_CACHE_SLOTS`` value. Finally, a value of zero in :data:`parameters.NODE_CACHE_SLOTS` means that any cache mechanism is disabled. At any rate, if you feel that this issue is important for you, there is no replacement for setting your own experiments up in order to proceed to fine-tune the :data:`parameters.NODE_CACHE_SLOTS` parameter. .. note:: PyTables >= 2.3 sports an optimized LRU cache node written in C, so you should expect significantly faster LRU cache operations when working with it. .. note:: Numerical results reported in :ref:`table ` have been obtained with PyTables < 3.1. In PyTables 3.1 the node cache mechanism has been completely redesigned so while all comments above are still valid, numerical values could be a little bit different from the ones reported in :ref:`table `. Compacting your PyTables files ------------------------------ Let's suppose that you have a file where you have made a lot of row deletions on one or more tables, or deleted many leaves or even entire subtrees. These operations might leave *holes* (i.e. space that is not used anymore) in your files that may potentially affect not only the size of the files but, more importantly, the performance of I/O. This is because when you delete a lot of rows in a table, the space is not automatically recovered on the fly. In addition, if you add many more rows to a table than specified in the expectedrows keyword at creation time this may affect performance as well, as explained in :ref:`expectedRowsOptim`. In order to cope with these issues, you should be aware that PyTables includes a handy utility called ptrepack which can be very useful not only to compact *fragmented* files, but also to adjust some internal parameters in order to use better buffer and chunk sizes for optimum I/O speed. Please check the :ref:`ptrepackDescr` for a brief tutorial on its use. Another thing that you might want to use ptrepack for is changing the compression filters or compression levels on your existing data for different goals, like checking how this can affect both final size and I/O performance, or getting rid of the optional compressors like LZO or bzip2 in your existing files, in case you want to use them with generic HDF5 tools that do not have support for these filters. -------------- .. [1] CArray nodes, though not extensible, are chunked and have their optimum chunk size automatically computed at creation time, since their final shape is known. .. [2] Except for Array objects. .. [3] Some users reported that the typical improvement with real data is between a factor 1.5x and 2.5x over the already compressed datasets. .. [4] So, there is not much point in using Psyco with recent versions of PyTables anymore. PyTables-v.3.1.1/doc/source/usersguide/parameter_files.rst000066400000000000000000000076561231437614300236450ustar00rootroot00000000000000.. _parameter_files: PyTables parameter files ======================== .. currentmodule:: tables.parameters PyTables issues warnings when certain limits are exceeded. Those limits are not intrinsic limitations of the underlying software, but rather are proactive measures to avoid large resource consumptions. The default limits should be enough for most of cases, and users should try to respect them. However, in some situations, it can be convenient to increase (or decrease) these limits. Also, and in order to get maximum performance, PyTables implements a series of sophisticated features, like I/O buffers or different kind of caches (for nodes, chunks and other internal metadata). These features comes with a default set of parameters that ensures a decent performance in most of situations. But, as there is always a need for every case, it is handy to have the possibility to fine-tune some of these parameters. Because of these reasons, PyTables implements a couple of ways to change the values of these parameters. All the *tunable* parameters live in the :file:`tables/parameters.py`. The user can choose to change them in the parameter files themselves for a global and persistent change. Moreover, if he wants a finer control, he can pass any of these parameters directly to the :func:`tables.open_file` function, and the new parameters will only take effect in the corresponding file (the defaults will continue to be in the parameter files). A description of all of the tunable parameters follows. As the defaults stated here may change from release to release, please check with your actual parameter files so as to know your actual default values. .. warning:: Changing the next parameters may have a very bad effect in the resource consumption and performance of your PyTables scripts. Please be careful when touching these! Tunable parameters in parameters.py ----------------------------------- Recommended maximum values ~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autodata:: MAX_COLUMNS .. autodata:: MAX_NODE_ATTRS .. autodata:: MAX_GROUP_WIDTH .. autodata:: MAX_TREE_DEPTH .. autodata:: MAX_UNDO_PATH_LENGTH Cache limits ~~~~~~~~~~~~ .. autodata:: CHUNK_CACHE_NELMTS .. autodata:: CHUNK_CACHE_PREEMPT .. autodata:: CHUNK_CACHE_SIZE .. autodata:: COND_CACHE_SLOTS .. autodata:: METADATA_CACHE_SIZE .. autodata:: NODE_CACHE_SLOTS Parameters for the different internal caches ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autodata:: BOUNDS_MAX_SIZE .. autodata:: BOUNDS_MAX_SLOTS .. autodata:: ITERSEQ_MAX_ELEMENTS .. autodata:: ITERSEQ_MAX_SIZE .. autodata:: ITERSEQ_MAX_SLOTS .. autodata:: LIMBOUNDS_MAX_SIZE .. autodata:: LIMBOUNDS_MAX_SLOTS .. autodata:: TABLE_MAX_SIZE .. autodata:: SORTED_MAX_SIZE .. autodata:: SORTEDLR_MAX_SIZE .. autodata:: SORTEDLR_MAX_SLOTS Parameters for general cache behaviour ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. warning:: The next parameters will not take any effect if passed to the open_file() function, so they can only be changed in a *global* way. You can change them in the file, but this is strongly discouraged unless you know well what you are doing. .. autodata:: DISABLE_EVERY_CYCLES .. autodata:: ENABLE_EVERY_CYCLES .. autodata:: LOWEST_HIT_RATIO Parameters for the I/O buffer in Leaf objects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autodata:: IO_BUFFER_SIZE .. autodata:: BUFFER_TIMES Miscellaneous ~~~~~~~~~~~~~ .. autodata:: EXPECTED_ROWS_EARRAY .. autodata:: EXPECTED_ROWS_TABLE .. autodata:: PYTABLES_SYS_ATTRS .. autodata:: MAX_NUMEXPR_THREADS .. autodata:: MAX_BLOSC_THREADS HDF5 driver management ~~~~~~~~~~~~~~~~~~~~~~ .. autodata:: DRIVER .. autodata:: DRIVER_DIRECT_ALIGNMENT .. autodata:: DRIVER_DIRECT_BLOCK_SIZE .. autodata:: DRIVER_DIRECT_CBUF_SIZE .. autodata:: DRIVER_CORE_INCREMENT .. autodata:: DRIVER_CORE_BACKING_STORE .. autodata:: DRIVER_CORE_IMAGE .. autodata:: DRIVER_SPLIT_META_EXT .. autodata:: DRIVER_SPLIT_RAW_EXT PyTables-v.3.1.1/doc/source/usersguide/tutorials.rst000066400000000000000000002764071231437614300225330ustar00rootroot00000000000000Tutorials ========= .. epigraph:: Seràs la clau que obre tots els panys, seràs la llum, la llum il.limitada, seràs confí on l'aurora comença, seràs forment, escala il.luminada! -- Lyrics: Vicent Andrés i Estellés. Music: Ovidi Montllor, Toti Soler, M'aclame a tu This chapter consists of a series of simple yet comprehensive tutorials that will enable you to understand PyTables' main features. If you would like more information about some particular instance variable, global function, or method, look at the doc strings or go to the library reference in :ref:`library_reference`. If you are reading this in PDF or HTML formats, follow the corresponding hyperlink near each newly introduced entity. Please note that throughout this document the terms *column* and *field* will be used interchangeably, as will the terms *row* and *record*. .. currentmodule:: tables Getting started --------------- In this section, we will see how to define our own records in Python and save collections of them (i.e. a *table*) into a file. Then we will select some of the data in the table using Python cuts and create NumPy arrays to store this selection as separate objects in a tree. In *examples/tutorial1-1.py* you will find the working version of all the code in this section. Nonetheless, this tutorial series has been written to allow you reproduce it in a Python interactive console. I encourage you to do parallel testing and inspect the created objects (variables, docs, children objects, etc.) during the course of the tutorial! Importing tables objects ~~~~~~~~~~~~~~~~~~~~~~~~ Before starting you need to import the public objects in the tables package. You normally do that by executing:: >>> import tables This is the recommended way to import tables if you don't want to pollute your namespace. However, PyTables has a contained set of first-level primitives, so you may consider using the alternative:: >>> from tables import * If you are going to work with NumPy arrays (and normally, you will) you will also need to import functions from the numpy package. So most PyTables programs begin with:: >>> import tables # but in this tutorial we use "from tables import \*" >>> import numpy Declaring a Column Descriptor ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Now, imagine that we have a particle detector and we want to create a table object in order to save data retrieved from it. You need first to define the table, the number of columns it has, what kind of object is contained in each column, and so on. Our particle detector has a TDC (Time to Digital Converter) counter with a dynamic range of 8 bits and an ADC (Analogical to Digital Converter) with a range of 16 bits. For these values, we will define 2 fields in our record object called TDCcount and ADCcount. We also want to save the grid position in which the particle has been detected, so we will add two new fields called grid_i and grid_j. Our instrumentation also can obtain the pressure and energy of the particle. The resolution of the pressure-gauge allows us to use a single-precision float to store pressure readings, while the energy value will need a double-precision float. Finally, to track the particle we want to assign it a name to identify the kind of the particle it is and a unique numeric identifier. So we will add two more fields: name will be a string of up to 16 characters, and idnumber will be an integer of 64 bits (to allow us to store records for extremely large numbers of particles). Having determined our columns and their types, we can now declare a new Particle class that will contain all this information:: >>> from tables import * >>> class Particle(IsDescription): ... name = StringCol(16) # 16-character String ... idnumber = Int64Col() # Signed 64-bit integer ... ADCcount = UInt16Col() # Unsigned short integer ... TDCcount = UInt8Col() # unsigned byte ... grid_i = Int32Col() # 32-bit integer ... grid_j = Int32Col() # 32-bit integer ... pressure = Float32Col() # float (single-precision) ... energy = Float64Col() # double (double-precision) >>> This definition class is self-explanatory. Basically, you declare a class variable for each field you need. As its value you assign an instance of the appropriate Col subclass, according to the kind of column defined (the data type, the length, the shape, etc). See the :ref:`ColClassDescr` for a complete description of these subclasses. See also :ref:`datatypes` for a list of data types supported by the Col constructor. From now on, we can use Particle instances as a descriptor for our detector data table. We will see later on how to pass this object to construct the table. But first, we must create a file where all the actual data pushed into our table will be saved. Creating a PyTables file from scratch ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Use the top-level :func:`open_file` function to create a PyTables file:: >>> h5file = open_file("tutorial1.h5", mode = "w", title = "Test file") :func:`open_file` is one of the objects imported by the ```from tables import *``` statement. Here, we are saying that we want to create a new file in the current working directory called "tutorial1.h5" in "w"rite mode and with an descriptive title string ("Test file"). This function attempts to open the file, and if successful, returns the File (see :ref:`FileClassDescr`) object instance h5file. The root of the object tree is specified in the instance's root attribute. Creating a new group ~~~~~~~~~~~~~~~~~~~~ Now, to better organize our data, we will create a group called *detector* that branches from the root node. We will save our particle data table in this group:: >>> group = h5file.create_group("/", 'detector', 'Detector information') Here, we have taken the File instance h5file and invoked its :meth:`File.create_group` method to create a new group called *detector* branching from "*/*" (another way to refer to the h5file.root object we mentioned above). This will create a new Group (see :ref:`GroupClassDescr`) object instance that will be assigned to the variable group. Creating a new table ~~~~~~~~~~~~~~~~~~~~ Let's now create a Table (see :ref:`TableClassDescr`) object as a branch off the newly-created group. We do that by calling the :meth:`File.create_table` method of the h5file object:: >>> table = h5file.create_table(group, 'readout', Particle, "Readout example") We create the Table instance under group. We assign this table the node name "*readout*". The Particle class declared before is the *description* parameter (to define the columns of the table) and finally we set "*Readout example*" as the Table title. With all this information, a new Table instance is created and assigned to the variable *table*. If you are curious about how the object tree looks right now, simply print the File instance variable *h5file*, and examine the output:: >>> print(h5file) tutorial1.h5 (File) 'Test file' Last modif.: 'Wed Mar 7 11:06:12 2007' Object Tree: / (RootGroup) 'Test file' /detector (Group) 'Detector information' /detector/readout (Table(0,)) 'Readout example' As you can see, a dump of the object tree is displayed. It's easy to see the Group and Table objects we have just created. If you want more information, just type the variable containing the File instance:: >>> h5file File(filename='tutorial1.h5', title='Test file', mode='w', root_uep='/', filters=Filters(complevel=0, shuffle=False, fletcher32=False)) / (RootGroup) 'Test file' /detector (Group) 'Detector information' /detector/readout (Table(0,)) 'Readout example' description := { "ADCcount": UInt16Col(shape=(), dflt=0, pos=0), "TDCcount": UInt8Col(shape=(), dflt=0, pos=1), "energy": Float64Col(shape=(), dflt=0.0, pos=2), "grid_i": Int32Col(shape=(), dflt=0, pos=3), "grid_j": Int32Col(shape=(), dflt=0, pos=4), "idnumber": Int64Col(shape=(), dflt=0, pos=5), "name": StringCol(itemsize=16, shape=(), dflt='', pos=6), "pressure": Float32Col(shape=(), dflt=0.0, pos=7)} byteorder := 'little' chunkshape := (87,) More detailed information is displayed about each object in the tree. Note how Particle, our table descriptor class, is printed as part of the *readout* table description information. In general, you can obtain much more information about the objects and their children by just printing them. That introspection capability is very useful, and I recommend that you use it extensively. The time has come to fill this table with some values. First we will get a pointer to the Row (see :ref:`RowClassDescr`) instance of this table instance:: >>> particle = table.row The row attribute of table points to the Row instance that will be used to write data rows into the table. We write data simply by assigning the Row instance the values for each row as if it were a dictionary (although it is actually an *extension class*), using the column names as keys. Below is an example of how to write rows:: >>> for i in xrange(10): ... particle['name'] = 'Particle: %6d' % (i) ... particle['TDCcount'] = i % 256 ... particle['ADCcount'] = (i * 256) % (1 << 16) ... particle['grid_i'] = i ... particle['grid_j'] = 10 - i ... particle['pressure'] = float(i*i) ... particle['energy'] = float(particle['pressure'] ** 4) ... particle['idnumber'] = i * (2 ** 34) ... # Insert a new particle record ... particle.append() >>> This code should be easy to understand. The lines inside the loop just assign values to the different columns in the Row instance particle (see :ref:`RowClassDescr`). A call to its append() method writes this information to the table I/O buffer. After we have processed all our data, we should flush the table's I/O buffer if we want to write all this data to disk. We achieve that by calling the table.flush() method:: >>> table.flush() Remember, flushing a table is a *very important* step as it will not only help to maintain the integrity of your file, but also will free valuable memory resources (i.e. internal buffers) that your program may need for other things. .. _readingAndSelectingUsage: Reading (and selecting) data in a table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Ok. We have our data on disk, and now we need to access it and select from specific columns the values we are interested in. See the example below:: >>> table = h5file.root.detector.readout >>> pressure = [x['pressure'] for x in table.iterrows() if x['TDCcount'] > 3 and 20 <= x['pressure'] < 50] >>> pressure [25.0, 36.0, 49.0] The first line creates a "shortcut" to the *readout* table deeper on the object tree. As you can see, we use the *natural naming* schema to access it. We also could have used the h5file.get_node() method, as we will do later on. You will recognize the last two lines as a Python list comprehension. It loops over the rows in *table* as they are provided by the :meth:`Table.iterrows` iterator. The iterator returns values until all the data in table is exhausted. These rows are filtered using the expression:: x['TDCcount'] > 3 and 20 <= x['pressure'] < 50 So, we are selecting the values of the pressure column from filtered records to create the final list and assign it to pressure variable. We could have used a normal for loop to accomplish the same purpose, but I find comprehension syntax to be more compact and elegant. PyTables do offer other, more powerful ways of performing selections which may be more suitable if you have very large tables or if you need very high query speeds. They are called *in-kernel* and *indexed* queries, and you can use them through :meth:`Table.where` and other related methods. Let's use an in-kernel selection to query the name column for the same set of cuts:: >>> names = [ x['name'] for x in table.where("""(TDCcount > 3) & (20 <= pressure) & (pressure < 50)""") ] >>> names ['Particle: 5', 'Particle: 6', 'Particle: 7'] In-kernel and indexed queries are not only much faster, but as you can see, they also look more compact, and are among the greatests features for PyTables, so be sure that you use them a lot. See :ref:`condition_syntax` and :ref:`searchOptim` for more information on in-kernel and indexed selections. .. note:: A special care should be taken when the query condition includes string literals. Indeed Python 2 string literals are string of bytes while Python 3 strings are unicode objects. With reference to the above definition of :class:`Particle` it has to be noted that the type of the "name" column do not change depending on the Python version used (of course). It always corresponds to strings of bytes. Any condition involving the "name" column should be written using the appropriate type for string literals in order to avoid :exc:`TypeError`\ s. Suppose one wants to get rows corresponding to specific particle names. The code below will work fine in Python 2 but will fail with a :exc:`TypeError` in Python 3:: >>> condition = '(name == "Particle: 5") | (name == "Particle: 7")' >>> for record in table.where(condition): # TypeError in Python3 ... # do something with "record" The reason is that in Python 3 "condition" implies a comparison between a string of bytes ("name" column contents) and an unicode literals. The correct way to write the condition is:: >>> condition = '(name == b"Particle: 5") | (name == b"Particle: 7")' That's enough about selections for now. The next section will show you how to save these selected results to a file. Creating new array objects ~~~~~~~~~~~~~~~~~~~~~~~~~~ In order to separate the selected data from the mass of detector data, we will create a new group columns branching off the root group. Afterwards, under this group, we will create two arrays that will contain the selected data. First, we create the group:: >>> gcolumns = h5file.create_group(h5file.root, "columns", "Pressure and Name") Note that this time we have specified the first parameter using *natural naming* (h5file.root) instead of with an absolute path string ("/"). Now, create the first of the two Array objects we've just mentioned:: >>> h5file.create_array(gcolumns, 'pressure', array(pressure), "Pressure column selection") /columns/pressure (Array(3,)) 'Pressure column selection' atom := Float64Atom(shape=(), dflt=0.0) maindim := 0 flavor := 'numpy' byteorder := 'little' chunkshape := None We already know the first two parameters of the :meth:`File.create_array` methods (these are the same as the first two in create_table): they are the parent group *where* Array will be created and the Array instance *name*. The third parameter is the *object* we want to save to disk. In this case, it is a NumPy array that is built from the selection list we created before. The fourth parameter is the *title*. Now, we will save the second array. It contains the list of strings we selected before: we save this object as-is, with no further conversion:: >>> h5file.create_array(gcolumns, 'name', names, "Name column selection") /columns/name (Array(3,)) 'Name column selection' atom := StringAtom(itemsize=16, shape=(), dflt='') maindim := 0 flavor := 'python' byteorder := 'irrelevant' chunkshape := None As you can see, :meth:`File.create_array` accepts *names* (which is a regular Python list) as an *object* parameter. Actually, it accepts a variety of different regular objects (see :func:`create_array`) as parameters. The flavor attribute (see the output above) saves the original kind of object that was saved. Based on this *flavor*, PyTables will be able to retrieve exactly the same object from disk later on. Note that in these examples, the create_array method returns an Array instance that is not assigned to any variable. Don't worry, this is intentional to show the kind of object we have created by displaying its representation. The Array objects have been attached to the object tree and saved to disk, as you can see if you print the complete object tree:: >>> print(h5file) tutorial1.h5 (File) 'Test file' Last modif.: 'Wed Mar 7 19:40:44 2007' Object Tree: / (RootGroup) 'Test file' /columns (Group) 'Pressure and Name' /columns/name (Array(3,)) 'Name column selection' /columns/pressure (Array(3,)) 'Pressure column selection' /detector (Group) 'Detector information' /detector/readout (Table(10,)) 'Readout example' Closing the file and looking at its content ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To finish this first tutorial, we use the close method of the h5file File object to close the file before exiting Python:: >>> h5file.close() >>> ^D $ You have now created your first PyTables file with a table and two arrays. You can examine it with any generic HDF5 tool, such as h5dump or h5ls. Here is what the tutorial1.h5 looks like when read with the h5ls program. .. code-block:: bash $ h5ls -rd tutorial1.h5 /columns Group /columns/name Dataset {3} Data: (0) "Particle: 5", "Particle: 6", "Particle: 7" /columns/pressure Dataset {3} Data: (0) 25, 36, 49 /detector Group /detector/readout Dataset {10/Inf} Data: (0) {0, 0, 0, 0, 10, 0, "Particle: 0", 0}, (1) {256, 1, 1, 1, 9, 17179869184, "Particle: 1", 1}, (2) {512, 2, 256, 2, 8, 34359738368, "Particle: 2", 4}, (3) {768, 3, 6561, 3, 7, 51539607552, "Particle: 3", 9}, (4) {1024, 4, 65536, 4, 6, 68719476736, "Particle: 4", 16}, (5) {1280, 5, 390625, 5, 5, 85899345920, "Particle: 5", 25}, (6) {1536, 6, 1679616, 6, 4, 103079215104, "Particle: 6", 36}, (7) {1792, 7, 5764801, 7, 3, 120259084288, "Particle: 7", 49}, (8) {2048, 8, 16777216, 8, 2, 137438953472, "Particle: 8", 64}, (9) {2304, 9, 43046721, 9, 1, 154618822656, "Particle: 9", 81} Here's the output as displayed by the "ptdump" PyTables utility (located in utils/ directory). .. code-block:: bash $ ptdump tutorial1.h5 / (RootGroup) 'Test file' /columns (Group) 'Pressure and Name' /columns/name (Array(3,)) 'Name column selection' /columns/pressure (Array(3,)) 'Pressure column selection' /detector (Group) 'Detector information' /detector/readout (Table(10,)) 'Readout example' You can pass the :option:`-v` or :option:`-d` options to ptdump if you want more verbosity. Try them out! Also, in :ref:`Figure 1 `, you can admire how the tutorial1.h5 looks like using the `ViTables `_ graphical interface. .. _tutorial1-1-tableview: .. figure:: images/tutorial1-1-tableview.png :align: center **Figure 1. The initial version of the data file for tutorial 1, with a view of the data objects.** Browsing the *object tree* -------------------------- In this section, we will learn how to browse the tree and retrieve data and also meta-information about the actual data. In *examples/tutorial1-2.py* you will find the working version of all the code in this section. As before, you are encouraged to use a python shell and inspect the object tree during the course of the tutorial. Traversing the object tree ~~~~~~~~~~~~~~~~~~~~~~~~~~ Let's start by opening the file we created in last tutorial section:: >>> h5file = open_file("tutorial1.h5", "a") This time, we have opened the file in "a"ppend mode. We use this mode to add more information to the file. PyTables, following the Python tradition, offers powerful introspection capabilities, i.e. you can easily ask information about any component of the object tree as well as search the tree. To start with, you can get a preliminary overview of the object tree by simply printing the existing File instance:: >>> print(h5file) tutorial1.h5 (File) 'Test file' Last modif.: 'Wed Mar 7 19:50:57 2007' Object Tree: / (RootGroup) 'Test file' /columns (Group) 'Pressure and Name' /columns/name (Array(3,)) 'Name column selection' /columns/pressure (Array(3,)) 'Pressure column selection' /detector (Group) 'Detector information' /detector/readout (Table(10,)) 'Readout example' It looks like all of our objects are there. Now let's make use of the File iterator to see how to list all the nodes in the object tree:: >>> for node in h5file: ... print(node) / (RootGroup) 'Test file' /columns (Group) 'Pressure and Name' /detector (Group) 'Detector information' /columns/name (Array(3,)) 'Name column selection' /columns/pressure (Array(3,)) 'Pressure column selection' /detector/readout (Table(10,)) 'Readout example' We can use the :meth:`File.walk_groups` method of the File class to list only the *groups* on tree:: >>> for group in h5file.walk_groups(): ... print(group) / (RootGroup) 'Test file' /columns (Group) 'Pressure and Name' /detector (Group) 'Detector information' Note that :meth:`File.walk_groups` actually returns an *iterator*, not a list of objects. Using this iterator with the list_nodes() method is a powerful combination. Let's see an example listing of all the arrays in the tree:: >>> for group in h5file.walk_groups("/"): ... for array in h5file.list_nodes(group, classname='Array'): ... print(array) /columns/name (Array(3,)) 'Name column selection' /columns/pressure (Array(3,)) 'Pressure column selection' :meth:`File.list_nodes` returns a list containing all the nodes hanging off a specific Group. If the *classname* keyword is specified, the method will filter out all instances which are not descendants of the class. We have asked for only Array instances. There exist also an iterator counterpart called :meth:`File.iter_nodes` that might be handy is some situations, like for example when dealing with groups with a large number of nodes behind it. We can combine both calls by using the :meth:`File.walk_nodes` special method of the File object. For example:: >>> for array in h5file.walk_nodes("/", "Array"): ... print(array) /columns/name (Array(3,)) 'Name column selection' /columns/pressure (Array(3,)) 'Pressure column selection' This is a nice shortcut when working interactively. Finally, we will list all the Leaf, i.e. Table and Array instances (see :ref:`LeafClassDescr` for detailed information on Leaf class), in the /detector group. Note that only one instance of the Table class (i.e. readout) will be selected in this group (as should be the case):: >>> for leaf in h5file.root.detector._f_walknodes('Leaf'): ... print(leaf) /detector/readout (Table(10,)) 'Readout example' We have used a call to the :meth:`Group._f_walknodes` method, using the *natural naming* path specification. Of course you can do more sophisticated node selections using these powerful methods. But first, let's take a look at some important PyTables object instance variables. Setting and getting user attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PyTables provides an easy and concise way to complement the meaning of your node objects on the tree by using the AttributeSet class (see :ref:`AttributeSetClassDescr`). You can access this object through the standard attribute attrs in Leaf nodes and _v_attrs in Group nodes. For example, let's imagine that we want to save the date indicating when the data in /detector/readout table has been acquired, as well as the temperature during the gathering process:: >>> table = h5file.root.detector.readout >>> table.attrs.gath_date = "Wed, 06/12/2003 18:33" >>> table.attrs.temperature = 18.4 >>> table.attrs.temp_scale = "Celsius" Now, let's set a somewhat more complex attribute in the /detector group:: >>> detector = h5file.root.detector >>> detector._v_attrs.stuff = [5, (2.3, 4.5), "Integer and tuple"] Note how the AttributeSet instance is accessed with the _v_attrs attribute because detector is a Group node. In general, you can save any standard Python data structure as an attribute node. See :ref:`AttributeSetClassDescr` for a more detailed explanation of how they are serialized for export to disk. Retrieving the attributes is equally simple:: >>> table.attrs.gath_date 'Wed, 06/12/2003 18:33' >>> table.attrs.temperature 18.399999999999999 >>> table.attrs.temp_scale 'Celsius' >>> detector._v_attrs.stuff [5, (2.2999999999999998, 4.5), 'Integer and tuple'] You can probably guess how to delete attributes:: >>> del table.attrs.gath_date If you want to examine the current user attribute set of /detector/table, you can print its representation (try hitting the TAB key twice if you are on a Unix Python console with the rlcompleter module active):: >>> table.attrs /detector/readout._v_attrs (AttributeSet), 23 attributes: [CLASS := 'TABLE', FIELD_0_FILL := 0, FIELD_0_NAME := 'ADCcount', FIELD_1_FILL := 0, FIELD_1_NAME := 'TDCcount', FIELD_2_FILL := 0.0, FIELD_2_NAME := 'energy', FIELD_3_FILL := 0, FIELD_3_NAME := 'grid_i', FIELD_4_FILL := 0, FIELD_4_NAME := 'grid_j', FIELD_5_FILL := 0, FIELD_5_NAME := 'idnumber', FIELD_6_FILL := '', FIELD_6_NAME := 'name', FIELD_7_FILL := 0.0, FIELD_7_NAME := 'pressure', FLAVOR := 'numpy', NROWS := 10, TITLE := 'Readout example', VERSION := '2.6', temp_scale := 'Celsius', temperature := 18.399999999999999] We've got all the attributes (including the *system* attributes). You can get a list of *all* attributes or only the *user* or *system* attributes with the _f_list() method:: >>> print(table.attrs._f_list("all")) ['CLASS', 'FIELD_0_FILL', 'FIELD_0_NAME', 'FIELD_1_FILL', 'FIELD_1_NAME', 'FIELD_2_FILL', 'FIELD_2_NAME', 'FIELD_3_FILL', 'FIELD_3_NAME', 'FIELD_4_FILL', 'FIELD_4_NAME', 'FIELD_5_FILL', 'FIELD_5_NAME', 'FIELD_6_FILL', 'FIELD_6_NAME', 'FIELD_7_FILL', 'FIELD_7_NAME', 'FLAVOR', 'NROWS', 'TITLE', 'VERSION', 'temp_scale', 'temperature'] >>> print(table.attrs._f_list("user")) ['temp_scale', 'temperature'] >>> print(table.attrs._f_list("sys")) ['CLASS', 'FIELD_0_FILL', 'FIELD_0_NAME', 'FIELD_1_FILL', 'FIELD_1_NAME', 'FIELD_2_FILL', 'FIELD_2_NAME', 'FIELD_3_FILL', 'FIELD_3_NAME', 'FIELD_4_FILL', 'FIELD_4_NAME', 'FIELD_5_FILL', 'FIELD_5_NAME', 'FIELD_6_FILL', 'FIELD_6_NAME', 'FIELD_7_FILL', 'FIELD_7_NAME', 'FLAVOR', 'NROWS', 'TITLE', 'VERSION'] You can also rename attributes:: >>> table.attrs._f_rename("temp_scale","tempScale") >>> print(table.attrs._f_list()) ['tempScale', 'temperature'] And, from PyTables 2.0 on, you are allowed also to set, delete or rename system attributes:: >>> table.attrs._f_rename("VERSION", "version") >>> table.attrs.VERSION Traceback (most recent call last): File "", line 1, in File "tables/attributeset.py", line 222, in __getattr__ (name, self._v__nodepath) AttributeError: Attribute 'VERSION' does not exist in node: '/detector/readout' >>> table.attrs.version '2.6' *Caveat emptor:* you must be careful when modifying system attributes because you may end fooling PyTables and ultimately getting unwanted behaviour. Use this only if you know what are you doing. So, given the caveat above, we will proceed to restore the original name of VERSION attribute:: >>> table.attrs._f_rename("version", "VERSION") >>> table.attrs.VERSION '2.6' Ok. that's better. If you would terminate your session now, you would be able to use the h5ls command to read the /detector/readout attributes from the file written to disk. .. code-block:: bash $ h5ls -vr tutorial1.h5/detector/readout Opened "tutorial1.h5" with sec2 driver. /detector/readout Dataset {10/Inf} Attribute: CLASS scalar Type: 6-byte null-terminated ASCII string Data: "TABLE" Attribute: VERSION scalar Type: 4-byte null-terminated ASCII string Data: "2.6" Attribute: TITLE scalar Type: 16-byte null-terminated ASCII string Data: "Readout example" Attribute: NROWS scalar Type: native long long Data: 10 Attribute: FIELD_0_NAME scalar Type: 9-byte null-terminated ASCII string Data: "ADCcount" Attribute: FIELD_1_NAME scalar Type: 9-byte null-terminated ASCII string Data: "TDCcount" Attribute: FIELD_2_NAME scalar Type: 7-byte null-terminated ASCII string Data: "energy" Attribute: FIELD_3_NAME scalar Type: 7-byte null-terminated ASCII string Data: "grid_i" Attribute: FIELD_4_NAME scalar Type: 7-byte null-terminated ASCII string Data: "grid_j" Attribute: FIELD_5_NAME scalar Type: 9-byte null-terminated ASCII string Data: "idnumber" Attribute: FIELD_6_NAME scalar Type: 5-byte null-terminated ASCII string Data: "name" Attribute: FIELD_7_NAME scalar Type: 9-byte null-terminated ASCII string Data: "pressure" Attribute: FLAVOR scalar Type: 5-byte null-terminated ASCII string Data: "numpy" Attribute: tempScale scalar Type: 7-byte null-terminated ASCII string Data: "Celsius" Attribute: temperature scalar Type: native double Data: 18.4 Location: 0:1:0:1952 Links: 1 Modified: 2006-12-11 10:35:13 CET Chunks: {85} 3995 bytes Storage: 470 logical bytes, 3995 allocated bytes, 11.76% utilization Type: struct { "ADCcount" +0 native unsigned short "TDCcount" +2 native unsigned char "energy" +3 native double "grid_i" +11 native int "grid_j" +15 native int "idnumber" +19 native long long "name" +27 16-byte null-terminated ASCII string "pressure" +43 native float } 47 bytes Attributes are a useful mechanism to add persistent (meta) information to your data. Getting object metadata ~~~~~~~~~~~~~~~~~~~~~~~ Each object in PyTables has *metadata* information about the data in the file. Normally this *meta-information* is accessible through the node instance variables. Let's take a look at some examples:: >>> print("Object:", table) Object: /detector/readout (Table(10,)) 'Readout example' >>> print("Table name:", table.name) Table name: readout >>> print("Table title:", table.title) Table title: Readout example >>> print("Number of rows in table:", table.nrows) Number of rows in table: 10 >>> print("Table variable names with their type and shape:") Table variable names with their type and shape: >>> for name in table.colnames: ... print(name, ':= %s, %s' % (table.coldtypes[name], table.coldtypes[name].shape)) ADCcount := uint16, () TDCcount := uint8, () energy := float64, () grid_i := int32, () grid_j := int32, () idnumber := int64, () name := |S16, () pressure := float32, () Here, the name, title, nrows, colnames and coldtypes attributes (see :class:`Table` for a complete attribute list) of the Table object gives us quite a bit of information about the table data. You can interactively retrieve general information about the public objects in PyTables by asking for help:: >>> help(table) Help on Table in module tables.table: class Table(tableextension.Table, tables.leaf.Leaf) | This class represents heterogeneous datasets in an HDF5 file. | | Tables are leaves (see the `Leaf` class) whose data consists of a | unidimensional sequence of *rows*, where each row contains one or | more *fields*. Fields have an associated unique *name* and | *position*, with the first field having position 0. All rows have | the same fields, which are arranged in *columns*. [snip] | | Instance variables | ------------------ | | The following instance variables are provided in addition to those | in `Leaf`. Please note that there are several `col` dictionaries | to ease retrieving information about a column directly by its path | name, avoiding the need to walk through `Table.description` or | `Table.cols`. | | autoindex | Automatically keep column indexes up to date? | | Setting this value states whether existing indexes should be | automatically updated after an append operation or recomputed | after an index-invalidating operation (i.e. removal and | modification of rows). The default is true. [snip] | rowsize | The size in bytes of each row in the table. | | Public methods -- reading | ------------------------- | | * col(name) | * iterrows([start][, stop][, step]) | * itersequence(sequence) * itersorted(sortby[, checkCSI][, start][, stop][, step]) | * read([start][, stop][, step][, field][, coords]) | * read_coordinates(coords[, field]) * read_sorted(sortby[, checkCSI][, field,][, start][, stop][, step]) | * __getitem__(key) | * __iter__() | | Public methods -- writing | ------------------------- | | * append(rows) | * modify_column([start][, stop][, step][, column][, colname]) [snip] Try getting help with other object docs by yourself:: >>> help(h5file) >>> help(table.remove_rows) To examine metadata in the */columns/pressure* Array object:: >>> pressureObject = h5file.get_node("/columns", "pressure") >>> print("Info on the object:", repr(pressureObject)) Info on the object: /columns/pressure (Array(3,)) 'Pressure column selection' atom := Float64Atom(shape=(), dflt=0.0) maindim := 0 flavor := 'numpy' byteorder := 'little' chunkshape := None >>> print(" shape: ==>", pressureObject.shape) shape: ==> (3,) >>> print(" title: ==>", pressureObject.title) title: ==> Pressure column selection >>> print(" atom: ==>", pressureObject.atom) atom: ==> Float64Atom(shape=(), dflt=0.0) Observe that we have used the :meth:`File.get_node` method of the File class to access a node in the tree, instead of the natural naming method. Both are useful, and depending on the context you will prefer one or the other. :meth:`File.get_node` has the advantage that it can get a node from the pathname string (as in this example) and can also act as a filter to show only nodes in a particular location that are instances of class *classname*. In general, however, I consider natural naming to be more elegant and easier to use, especially if you are using the name completion capability present in interactive console. Try this powerful combination of natural naming and completion capabilities present in most Python consoles, and see how pleasant it is to browse the object tree (well, as pleasant as such an activity can be). If you look at the type attribute of the pressureObject object, you can verify that it is a "*float64*" array. By looking at its shape attribute, you can deduce that the array on disk is unidimensional and has 3 elements. See :class:`Array` or the internal doc strings for the complete Array attribute list. Reading data from Array objects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Once you have found the desired Array, use the read() method of the Array object to retrieve its data:: >>> pressureArray = pressureObject.read() >>> pressureArray array([ 25., 36., 49.]) >>> print("pressureArray is an object of type:", type(pressureArray)) pressureArray is an object of type: >>> nameArray = h5file.root.columns.name.read() >>> print("nameArray is an object of type:", type(nameArray)) nameArray is an object of type: >>> >>> print("Data on arrays nameArray and pressureArray:") Data on arrays nameArray and pressureArray: >>> for i in range(pressureObject.shape[0]): ... print(nameArray[i], "-->", pressureArray[i]) Particle: 5 --> 25.0 Particle: 6 --> 36.0 Particle: 7 --> 49.0 You can see that the :meth:`Array.read` method returns an authentic NumPy object for the pressureObject instance by looking at the output of the type() call. A read() of the nameArray object instance returns a native Python list (of strings). The type of the object saved is stored as an HDF5 attribute (named FLAVOR) for objects on disk. This attribute is then read as Array meta-information (accessible through in the Array.attrs.FLAVOR variable), enabling the read array to be converted into the original object. This provides a means to save a large variety of objects as arrays with the guarantee that you will be able to later recover them in their original form. See :meth:`File.create_array` for a complete list of supported objects for the Array object class. Commiting data to tables and arrays ----------------------------------- We have seen how to create tables and arrays and how to browse both data and metadata in the object tree. Let's examine more closely now one of the most powerful capabilities of PyTables, namely, how to modify already created tables and arrays [1]_ Appending data to an existing table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Now, let's have a look at how we can add records to an existing table on disk. Let's use our well-known *readout* Table object and append some new values to it:: >>> table = h5file.root.detector.readout >>> particle = table.row >>> for i in xrange(10, 15): ... particle['name'] = 'Particle: %6d' % (i) ... particle['TDCcount'] = i % 256 ... particle['ADCcount'] = (i * 256) % (1 << 16) ... particle['grid_i'] = i ... particle['grid_j'] = 10 - i ... particle['pressure'] = float(i*i) ... particle['energy'] = float(particle['pressure'] ** 4) ... particle['idnumber'] = i * (2 ** 34) ... particle.append() >>> table.flush() It's the same method we used to fill a new table. PyTables knows that this table is on disk, and when you add new records, they are appended to the end of the table [2]_. If you look carefully at the code you will see that we have used the table.row attribute to create a table row and fill it with the new values. Each time that its append() method is called, the actual row is committed to the output buffer and the row pointer is incremented to point to the next table record. When the buffer is full, the data is saved on disk, and the buffer is reused again for the next cycle. *Caveat emptor*: Do not forget to always call the flush() method after a write operation, or else your tables will not be updated! Let's have a look at some rows in the modified table and verify that our new data has been appended:: >>> for r in table.iterrows(): ... print("%-16s | %11.1f | %11.4g | %6d | %6d | %8d \|" % \\ ... (r['name'], r['pressure'], r['energy'], r['grid_i'], r['grid_j'], ... r['TDCcount'])) Particle: 0 | 0.0 | 0 | 0 | 10 | 0 | Particle: 1 | 1.0 | 1 | 1 | 9 | 1 | Particle: 2 | 4.0 | 256 | 2 | 8 | 2 | Particle: 3 | 9.0 | 6561 | 3 | 7 | 3 | Particle: 4 | 16.0 | 6.554e+04 | 4 | 6 | 4 | Particle: 5 | 25.0 | 3.906e+05 | 5 | 5 | 5 | Particle: 6 | 36.0 | 1.68e+06 | 6 | 4 | 6 | Particle: 7 | 49.0 | 5.765e+06 | 7 | 3 | 7 | Particle: 8 | 64.0 | 1.678e+07 | 8 | 2 | 8 | Particle: 9 | 81.0 | 4.305e+07 | 9 | 1 | 9 | Particle: 10 | 100.0 | 1e+08 | 10 | 0 | 10 | Particle: 11 | 121.0 | 2.144e+08 | 11 | -1 | 11 | Particle: 12 | 144.0 | 4.3e+08 | 12 | -2 | 12 | Particle: 13 | 169.0 | 8.157e+08 | 13 | -3 | 13 | Particle: 14 | 196.0 | 1.476e+09 | 14 | -4 | 14 | Modifying data in tables ~~~~~~~~~~~~~~~~~~~~~~~~ Ok, until now, we've been only reading and writing (appending) values to our tables. But there are times that you need to modify your data once you have saved it on disk (this is specially true when you need to modify the real world data to adapt your goals ;). Let's see how we can modify the values that were saved in our existing tables. We will start modifying single cells in the first row of the Particle table:: >>> print("Before modif-->", table[0]) Before modif--> (0, 0, 0.0, 0, 10, 0L, 'Particle: 0', 0.0) >>> table.cols.TDCcount[0] = 1 >>> print("After modifying first row of ADCcount-->", table[0]) After modifying first row of ADCcount--> (0, 1, 0.0, 0, 10, 0L, 'Particle: 0', 0.0) >>> table.cols.energy[0] = 2 >>> print("After modifying first row of energy-->", table[0]) After modifying first row of energy--> (0, 1, 2.0, 0, 10, 0L, 'Particle: 0', 0.0) We can modify complete ranges of columns as well:: >>> table.cols.TDCcount[2:5] = [2,3,4] >>> print("After modifying slice [2:5] of TDCcount-->", table[0:5]) After modifying slice [2:5] of TDCcount--> [(0, 1, 2.0, 0, 10, 0L, 'Particle: 0', 0.0) (256, 1, 1.0, 1, 9, 17179869184L, 'Particle: 1', 1.0) (512, 2, 256.0, 2, 8, 34359738368L, 'Particle: 2', 4.0) (768, 3, 6561.0, 3, 7, 51539607552L, 'Particle: 3', 9.0) (1024, 4, 65536.0, 4, 6, 68719476736L, 'Particle: 4', 16.0)] >>> table.cols.energy[1:9:3] = [2,3,4] >>> print("After modifying slice [1:9:3] of energy-->", table[0:9]) After modifying slice [1:9:3] of energy--> [(0, 1, 2.0, 0, 10, 0L, 'Particle: 0', 0.0) (256, 1, 2.0, 1, 9, 17179869184L, 'Particle: 1', 1.0) (512, 2, 256.0, 2, 8, 34359738368L, 'Particle: 2', 4.0) (768, 3, 6561.0, 3, 7, 51539607552L, 'Particle: 3', 9.0) (1024, 4, 3.0, 4, 6, 68719476736L, 'Particle: 4', 16.0) (1280, 5, 390625.0, 5, 5, 85899345920L, 'Particle: 5', 25.0) (1536, 6, 1679616.0, 6, 4, 103079215104L, 'Particle: 6', 36.0) (1792, 7, 4.0, 7, 3, 120259084288L, 'Particle: 7', 49.0) (2048, 8, 16777216.0, 8, 2, 137438953472L, 'Particle: 8', 64.0)] Check that the values have been correctly modified! .. hint:: remember that column TDCcount is the second one, and that energy is the third. Look for more info on modifying columns in :meth:`Column.__setitem__`. PyTables also lets you modify complete sets of rows at the same time. As a demonstration of these capability, see the next example:: >>> table.modify_rows(start=1, step=3, ... rows=[(1, 2, 3.0, 4, 5, 6L, 'Particle: None', 8.0), ... (2, 4, 6.0, 8, 10, 12L, 'Particle: None*2', 16.0)]) 2 >>> print("After modifying the complete third row-->", table[0:5]) After modifying the complete third row--> [(0, 1, 2.0, 0, 10, 0L, 'Particle: 0', 0.0) (1, 2, 3.0, 4, 5, 6L, 'Particle: None', 8.0) (512, 2, 256.0, 2, 8, 34359738368L, 'Particle: 2', 4.0) (768, 3, 6561.0, 3, 7, 51539607552L, 'Particle: 3', 9.0) (2, 4, 6.0, 8, 10, 12L, 'Particle: None*2', 16.0)] As you can see, the modify_rows() call has modified the rows second and fifth, and it returned the number of modified rows. Apart of :meth:`Table.modify_rows`, there exists another method, called :meth:`Table.modify_column` to modify specific columns as well. Finally, it exists another way of modifying tables that is generally more handy than the described above. This new way uses the method :meth:`Row.update` of the Row instance that is attached to every table, so it is meant to be used in table iterators. Look at the next example:: >>> for row in table.where('TDCcount <= 2'): ... row['energy'] = row['TDCcount']*2 ... row.update() >>> print("After modifying energy column (where TDCcount <=2)-->", table[0:4]) After modifying energy column (where TDCcount <=2)--> [(0, 1, 2.0, 0, 10, 0L, 'Particle: 0', 0.0) (1, 2, 4.0, 4, 5, 6L, 'Particle: None', 8.0) (512, 2, 4.0, 2, 8, 34359738368L, 'Particle: 2', 4.0) (768, 3, 6561.0, 3, 7, 51539607552L, 'Particle: 3', 9.0)] .. note:: The authors find this way of updating tables (i.e. using Row.update()) to be both convenient and efficient. Please make sure to use it extensively. Modifying data in arrays ~~~~~~~~~~~~~~~~~~~~~~~~ We are going now to see how to modify data in array objects. The basic way to do this is through the use of :meth:`Array.__setitem__` special method. Let's see at how modify data on the pressureObject array:: >>> pressureObject = h5file.root.columns.pressure >>> print("Before modif-->", pressureObject[:]) Before modif--> [ 25. 36. 49.] >>> pressureObject[0] = 2 >>> print("First modif-->", pressureObject[:]) First modif--> [ 2. 36. 49.] >>> pressureObject[1:3] = [2.1, 3.5] >>> print("Second modif-->", pressureObject[:]) Second modif--> [ 2. 2.1 3.5] >>> pressureObject[::2] = [1,2] >>> print("Third modif-->", pressureObject[:]) Third modif--> [ 1. 2.1 2. ] So, in general, you can use any combination of (multidimensional) extended slicing. With the sole exception that you cannot use negative values for step to refer to indexes that you want to modify. See :meth:`Array.__getitem__` for more examples on how to use extended slicing in PyTables objects. Similarly, with an array of strings:: >>> nameObject = h5file.root.columns.name >>> print("Before modif-->", nameObject[:]) Before modif--> ['Particle: 5', 'Particle: 6', 'Particle: 7'] >>> nameObject[0] = 'Particle: None' >>> print("First modif-->", nameObject[:]) First modif--> ['Particle: None', 'Particle: 6', 'Particle: 7'] >>> nameObject[1:3] = ['Particle: 0', 'Particle: 1'] >>> print("Second modif-->", nameObject[:]) Second modif--> ['Particle: None', 'Particle: 0', 'Particle: 1'] >>> nameObject[::2] = ['Particle: -3', 'Particle: -5'] >>> print("Third modif-->", nameObject[:]) Third modif--> ['Particle: -3', 'Particle: 0', 'Particle: -5'] And finally... how to delete rows from a table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We'll finish this tutorial by deleting some rows from the table we have. Suppose that we want to delete the 5th to 9th rows (inclusive):: >>> table.remove_rows(5,10) 5 :meth:`Table.remove_rows` deletes the rows in the range (start, stop). It returns the number of rows effectively removed. We have reached the end of this first tutorial. Don't forget to close the file when you finish:: >>> h5file.close() >>> ^D $ In :ref:`Figure 2 ` you can see a graphical view of the PyTables file with the datasets we have just created. In :ref:`tutorial1-general` are displayed the general properties of the table /detector/readout. .. _tutorial1-2-tableview: .. figure:: images/tutorial1-2-tableview.png :align: center **Figure 2. The final version of the data file for tutorial 1.** .. _tutorial1-general: .. figure:: images/tutorial1-general.png :align: center **Figure 3. General properties of the /detector/readout table.** .. _secondExample: Multidimensional table cells and automatic sanity checks -------------------------------------------------------- Now it's time for a more real-life example (i.e. with errors in the code). We will create two groups that branch directly from the root node, Particles and Events. Then, we will put three tables in each group. In Particles we will put tables based on the Particle descriptor and in Events, the tables based the Event descriptor. Afterwards, we will provision the tables with a number of records. Finally, we will read the newly-created table /Events/TEvent3 and select some values from it, using a comprehension list. Look at the next script (you can find it in :file:`examples/tutorial2.py`). It appears to do all of the above, but it contains some small bugs. Note that this Particle class is not directly related to the one defined in last tutorial; this class is simpler (note, however, the *multidimensional* columns called pressure and temperature). We also introduce a new manner to describe a Table as a structured NumPy dtype (or even as a dictionary), as you can see in the Event description. See :meth:`File.create_table` about the different kinds of descriptor objects that can be passed to this method:: from tables import * from numpy import * # Describe a particle record class Particle(IsDescription): name = StringCol(itemsize=16) # 16-character string lati = Int32Col() # integer longi = Int32Col() # integer pressure = Float32Col(shape=(2,3)) # array of floats (single-precision) temperature = Float64Col(shape=(2,3)) # array of doubles (double-precision) # Native NumPy dtype instances are also accepted Event = dtype([ ("name" , "S16"), ("TDCcount" , uint8), ("ADCcount" , uint16), ("xcoord" , float32), ("ycoord" , float32) ]) # And dictionaries too (this defines the same structure as above) # Event = { # "name" : StringCol(itemsize=16), # "TDCcount" : UInt8Col(), # "ADCcount" : UInt16Col(), # "xcoord" : Float32Col(), # "ycoord" : Float32Col(), # } # Open a file in "w"rite mode fileh = open_file("tutorial2.h5", mode = "w") # Get the HDF5 root group root = fileh.root # Create the groups: for groupname in ("Particles", "Events"): group = fileh.create_group(root, groupname) # Now, create and fill the tables in Particles group gparticles = root.Particles # Create 3 new tables for tablename in ("TParticle1", "TParticle2", "TParticle3"): # Create a table table = fileh.create_table("/Particles", tablename, Particle, "Particles: "+tablename) # Get the record object associated with the table: particle = table.row # Fill the table with 257 particles for i in xrange(257): # First, assign the values to the Particle record particle['name'] = 'Particle: %6d' % (i) particle['lati'] = i particle['longi'] = 10 - i ########### Detectable errors start here. Play with them! particle['pressure'] = array(i*arange(2*3)).reshape((2,4)) # Incorrect #particle['pressure'] = array(i*arange(2*3)).reshape((2,3)) # Correct ########### End of errors particle['temperature'] = (i**2) # Broadcasting # This injects the Record values particle.append() # Flush the table buffers table.flush() # Now, go for Events: for tablename in ("TEvent1", "TEvent2", "TEvent3"): # Create a table in Events group table = fileh.create_table(root.Events, tablename, Event, "Events: "+tablename) # Get the record object associated with the table: event = table.row # Fill the table with 257 events for i in xrange(257): # First, assign the values to the Event record event['name'] = 'Event: %6d' % (i) event['TDCcount'] = i % (1<<8) # Correct range ########### Detectable errors start here. Play with them! event['xcoor'] = float(i**2) # Wrong spelling #event['xcoord'] = float(i**2) # Correct spelling event['ADCcount'] = "sss" # Wrong type #event['ADCcount'] = i * 2 # Correct type ########### End of errors event['ycoord'] = float(i)**4 # This injects the Record values event.append() # Flush the buffers table.flush() # Read the records from table "/Events/TEvent3" and select some table = root.Events.TEvent3 e = [ p['TDCcount'] for p in table if p['ADCcount'] < 20 and 4 <= p['TDCcount'] < 15 ] print("Last record ==>", p) print("Selected values ==>", e) print("Total selected records ==> ", len(e)) # Finally, close the file (this also will flush all the remaining buffers!) fileh.close() Shape checking ~~~~~~~~~~~~~~ If you look at the code carefully, you'll see that it won't work. You will get the following error. .. code-block:: bash $ python tutorial2.py Traceback (most recent call last): File "tutorial2.py", line 60, in particle['pressure'] = array(i*arange(2*3)).reshape((2,4)) # Incorrect ValueError: total size of new array must be unchanged Closing remaining open files: tutorial2.h5... done This error indicates that you are trying to assign an array with an incompatible shape to a table cell. Looking at the source, we see that we were trying to assign an array of shape (2,4) to a pressure element, which was defined with the shape (2,3). In general, these kinds of operations are forbidden, with one valid exception: when you assign a *scalar* value to a multidimensional column cell, all the cell elements are populated with the value of the scalar. For example:: particle['temperature'] = (i**2) # Broadcasting The value i**2 is assigned to all the elements of the temperature table cell. This capability is provided by the NumPy package and is known as *broadcasting*. Field name checking ~~~~~~~~~~~~~~~~~~~ After fixing the previous error and rerunning the program, we encounter another error. .. code-block:: bash $ python tutorial2.py Traceback (most recent call last): File "tutorial2.py", line 73, in ? event['xcoor'] = float(i**2) # Wrong spelling File "tableextension.pyx", line 1094, in tableextension.Row.__setitem__ File "tableextension.pyx", line 127, in tableextension.get_nested_field_cache File "utilsextension.pyx", line 331, in utilsextension.get_nested_field KeyError: 'no such column: xcoor' This error indicates that we are attempting to assign a value to a non-existent field in the *event* table object. By looking carefully at the Event class attributes, we see that we misspelled the xcoord field (we wrote xcoor instead). This is unusual behavior for Python, as normally when you assign a value to a non-existent instance variable, Python creates a new variable with that name. Such a feature can be dangerous when dealing with an object that contains a fixed list of field names. PyTables checks that the field exists and raises a KeyError if the check fails. Data type checking ~~~~~~~~~~~~~~~~~~ Finally, the last issue which we will find here is a TypeError exception. .. code-block:: bash $ python tutorial2.py Traceback (most recent call last): File "tutorial2.py", line 75, in ? event['ADCcount'] = "sss" # Wrong type File "tableextension.pyx", line 1111, in tableextension.Row.__setitem__ TypeError: invalid type () for column ``ADCcount`` And, if we change the affected line to read:: event.ADCcount = i * 2 # Correct type we will see that the script ends well. You can see the structure created with this (corrected) script in :ref:`Figure 4 `. In particular, note the multidimensional column cells in table /Particles/TParticle2. .. _tutorial2-tableview: .. figure:: images/tutorial2-tableview.png :align: center **Figure 4. Table hierarchy for tutorial 2.** .. _LinksTutorial: Using links for more convenient access to nodes ----------------------------------------------- Links are special nodes that can be used to create additional paths to your existing nodes. PyTables supports three kinds of links: hard links, soft links (aka symbolic links) and external links. Hard links let the user create additional paths to access another node in the same file, and once created, they are indistinguishable from the referred node object, except that they have different paths in the object tree. For example, if the referred node is, say, a Table object, then the new hard link will become a Table object itself. From this point on, you will be able to access the same Table object from two different paths: the original one and the new hard link path. If you delete one path to the table, you will be able to reach it via the other path. Soft links are similar to hard links, but they keep their own personality. When you create a soft link to another node, you will get a new SoftLink object that *refers* to that node. However, in order to access the referred node, you need to *dereference* it. Finally, external links are like soft links, with the difference that these are meant to point to nodes in *external* files instead of nodes in the same file. They are represented by the ExternalLink class and, like soft links, you need to dereference them in order to get access to the pointed node. Interactive example ~~~~~~~~~~~~~~~~~~~ Now we are going to learn how to deal with links. You can find the code used in this section in :file:`examples/links.py`. First, let's create a file with some group structure:: >>> import tables as tb >>> f1 = tb.open_file('links1.h5', 'w') >>> g1 = f1.create_group('/', 'g1') >>> g2 = f1.create_group(g1, 'g2') Now, we will put some datasets on the /g1 and /g1/g2 groups:: >>> a1 = f1.create_carray(g1, 'a1', tb.Int64Atom(), shape=(10000,)) >>> t1 = f1.create_table(g2, 't1', {'f1': tb.IntCol(), 'f2': tb.FloatCol()}) We can start the party now. We are going to create a new group, say /gl, where we will put our links and will start creating one hard link too:: >>> gl = f1.create_group('/', 'gl') >>> ht = f1.create_hard_link(gl, 'ht', '/g1/g2/t1') # ht points to t1 >>> print("``%s`` is a hard link to: ``%s``" % (ht, t1)) ``/gl/ht (Table(0,)) `` is a hard link to: ``/g1/g2/t1 (Table(0,)) `` You can see how we've created a hard link in /gl/ht which is pointing to the existing table in /g1/g2/t1. Have look at how the hard link is represented; it looks like a table, and actually, it is an *real* table. We have two different paths to access that table, the original /g1/g2/t1 and the new one /gl/ht. If we remove the original path we still can reach the table by using the new path:: >>> t1.remove() >>> print("table continues to be accessible in: ``%s``" % f1.get_node('/gl/ht')) table continues to be accessible in: ``/gl/ht (Table(0,)) `` So far so good. Now, let's create a couple of soft links:: >>> la1 = f1.create_soft_link(gl, 'la1', '/g1/a1') # la1 points to a1 >>> print("``%s`` is a soft link to: ``%s``" % (la1, la1.target)) ``/gl/la1 (SoftLink) -> /g1/a1`` is a soft link to: ``/g1/a1`` >>> lt = f1.create_soft_link(gl, 'lt', '/g1/g2/t1') # lt points to t1 >>> print("``%s`` is a soft link to: ``%s``" % (lt, lt.target)) ``/gl/lt (SoftLink) -> /g1/g2/t1 (dangling)`` is a soft link to: ``/g1/g2/t1`` Okay, we see how the first link /gl/la1 points to the array /g1/a1. Notice how the link prints as a SoftLink, and how the referred node is stored in the target instance attribute. The second link (/gt/lt) pointing to /g1/g2/t1 also has been created successfully, but by better inspecting the string representation of it, we see that is labeled as '(dangling)'. Why is this? Well, you should remember that we recently removed the /g1/g2/t1 path to access table t1. When printing it, the object knows that it points to *nowhere* and reports this. This is a nice way to quickly know whether a soft link points to an exiting node or not. So, let's re-create the removed path to t1 table:: >>> t1 = f1.create_hard_link('/g1/g2', 't1', '/gl/ht') >>> print("``%s`` is not dangling anymore" % (lt,)) ``/gl/lt (SoftLink) -> /g1/g2/t1`` is not dangling anymore and the soft link is pointing to an existing node now. Of course, for soft links to serve any actual purpose we need a way to get the pointed node. It happens that soft links are callable, and that's the way to get the referred nodes back:: >>> plt = lt() >>> print("dereferred lt node: ``%s``" % plt) dereferred lt node: ``/g1/g2/t1 (Table(0,)) `` >>> pla1 = la1() >>> print("dereferred la1 node: ``%s``" % pla1) dereferred la1 node: ``/g1/a1 (CArray(10000,)) `` Now, plt is a Python reference to the t1 table while pla1 refers to the a1 array. Easy, uh? Let's suppose now that a1 is an array whose access speed is critical for our application. One possible solution is to move the entire file into a faster disk, say, a solid state disk so that access latencies can be reduced quite a lot. However, it happens that our file is too big to fit into our shiny new (although small in capacity) SSD disk. A solution is to copy just the a1 array into a separate file that would fit into our SSD disk. However, our application would be able to handle two files instead of only one, adding significantly more complexity, which is not a good thing. External links to the rescue! As we've already said, external links are like soft links, but they are designed to link objects in external files. Back to our problem, let's copy the a1 array into a different file:: >>> f2 = tb.open_file('links2.h5', 'w') >>> new_a1 = a1.copy(f2.root, 'a1') >>> f2.close() # close the other file And now, we can remove the existing soft link and create the external link in its place:: >>> la1.remove() >>> la1 = f1.create_external_link(gl, 'la1', 'links2.h5:/a1') >>> print("``%s`` is an external link to: ``%s``" % (la1, la1.target)) ``/gl/la1 (ExternalLink) -> links2.h5:/a1`` is an external link to: ``links2.h5:/a1`` Let's try dereferring it:: >>> new_a1 = la1() # dereferrencing la1 returns a1 in links2.h5 >>> print("dereferred la1 node: ``%s``" % new_a1) dereferred la1 node: ``/a1 (CArray(10000,)) `` Well, it seems like we can access the external node. But just to make sure that the node is in the other file:: >>> print("new_a1 file:", new_a1._v_file.filename) new_a1 file: links2.h5 Okay, the node is definitely in the external file. So, you won't have to worry about your application: it will work exactly the same no matter the link is internal (soft) or external. Finally, here it is a dump of the objects in the final file, just to get a better idea of what we ended with:: >>> f1.close() >>> exit() $ ptdump links1.h5 / (RootGroup) '' /g1 (Group) '' /g1/a1 (CArray(10000,)) '' /gl (Group) '' /gl/ht (Table(0,)) '' /gl/la1 (ExternalLink) -> links2.h5:/a1 /gl/lt (SoftLink) -> /g1/g2/t1 /g1/g2 (Group) '' /g1/g2/t1 (Table(0,)) '' This ends this tutorial. I hope it helped you to appreciate how useful links can be. I'm sure you will find other ways in which you can use links that better fit your own needs. Exercising the Undo/Redo feature -------------------------------- PyTables has integrated support for undoing and/or redoing actions. This functionality lets you put marks in specific places of your hierarchy manipulation operations, so that you can make your HDF5 file pop back (*undo*) to a specific mark (for example for inspecting how your hierarchy looked at that point). You can also go forward to a more recent marker (*redo*). You can even do jumps to the marker you want using just one instruction as we will see shortly. You can undo/redo all the operations that are related to object tree management, like creating, deleting, moving or renaming nodes (or complete sub-hierarchies) inside a given object tree. You can also undo/redo operations (i.e. creation, deletion or modification) of persistent node attributes. However, when actions include *internal* modifications of datasets (that includes Table.append, Table.modify_rows or Table.remove_rows among others), they cannot be undone/redone currently. This capability can be useful in many situations, like for example when doing simulations with multiple branches. When you have to choose a path to follow in such a situation, you can put a mark there and, if the simulation is not going well, you can go back to that mark and start another path. Other possible application is defining coarse-grained operations which operate in a transactional-like way, i.e. which return the database to its previous state if the operation finds some kind of problem while running. You can probably devise many other scenarios where the Undo/Redo feature can be useful to you [3]_. A basic example ~~~~~~~~~~~~~~~ In this section, we are going to show the basic behavior of the Undo/Redo feature. You can find the code used in this example in :file:`examples/tutorial3-1.py`. A somewhat more complex example will be explained in the next section. First, let's create a file:: >>> import tables >>> fileh = tables.open_file("tutorial3-1.h5", "w", title="Undo/Redo demo 1") And now, activate the Undo/Redo feature with the method :meth:`File.enable_undo` of File:: >>> fileh.enable_undo() From now on, all our actions will be logged internally by PyTables. Now, we are going to create a node (in this case an Array object):: >>> one = fileh.create_array('/', 'anarray', [3,4], "An array") Now, mark this point:: >>> fileh.mark() 1 We have marked the current point in the sequence of actions. In addition, the mark() method has returned the identifier assigned to this new mark, that is 1 (mark #0 is reserved for the implicit mark at the beginning of the action log). In the next section we will see that you can also assign a *name* to a mark (see :meth:`File.mark` for more info on mark()). Now, we are going to create another array:: >>> another = fileh.create_array('/', 'anotherarray', [4,5], "Another array") Right. Now, we can start doing funny things. Let's say that we want to pop back to the previous mark (that whose value was 1, do you remember?). Let's introduce the undo() method (see :meth:`File.undo`):: >>> fileh.undo() Fine, what do you think it happened? Well, let's have a look at the object tree:: >>> print(fileh) tutorial3-1.h5 (File) 'Undo/Redo demo 1' Last modif.: 'Tue Mar 13 11:43:55 2007' Object Tree: / (RootGroup) 'Undo/Redo demo 1' /anarray (Array(2,)) 'An array' What happened with the /anotherarray node we've just created? You guess it, it has disappeared because it was created *after* the mark 1. If you are curious enough you may well ask where it has gone. Well, it has not been deleted completely; it has been just moved into a special, hidden, group of PyTables that renders it invisible and waiting for a chance to be reborn. Now, unwind once more, and look at the object tree:: >>> fileh.undo() >>> print(fileh) tutorial3-1.h5 (File) 'Undo/Redo demo 1' Last modif.: 'Tue Mar 13 11:43:55 2007' Object Tree: / (RootGroup) 'Undo/Redo demo 1' Oops, /anarray has disappeared as well!. Don't worry, it will revisit us very shortly. So, you might be somewhat lost right now; in which mark are we?. Let's ask the :meth:`File.get_current_mark` method in the file handler:: >>> print(fileh.get_current_mark()) 0 So we are at mark #0, remember? Mark #0 is an implicit mark that is created when you start the log of actions when calling File.enable_undo(). Fine, but you are missing your too-young-to-die arrays. What can we do about that? :meth:`File.redo` to the rescue:: >>> fileh.redo() >>> print(fileh) tutorial3-1.h5 (File) 'Undo/Redo demo 1' Last modif.: 'Tue Mar 13 11:43:55 2007' Object Tree: / (RootGroup) 'Undo/Redo demo 1' /anarray (Array(2,)) 'An array' Great! The /anarray array has come into life again. Just check that it is alive and well:: >>> fileh.root.anarray.read() [3, 4] >>> fileh.root.anarray.title 'An array' Well, it looks pretty similar than in its previous life; what's more, it is exactly the same object!:: >>> fileh.root.anarray is one True It just was moved to the the hidden group and back again, but that's all! That's kind of fun, so we are going to do the same with /anotherarray:: >>> fileh.redo() >>> print(fileh) tutorial3-1.h5 (File) 'Undo/Redo demo 1' Last modif.: 'Tue Mar 13 11:43:55 2007' Object Tree: / (RootGroup) 'Undo/Redo demo 1' /anarray (Array(2,)) 'An array' /anotherarray (Array(2,)) 'Another array' Welcome back, /anotherarray! Just a couple of sanity checks:: >>> assert fileh.root.anotherarray.read() == [4,5] >>> assert fileh.root.anotherarray.title == "Another array" >>> fileh.root.anotherarray is another True Nice, you managed to turn your data back into life. Congratulations! But wait, do not forget to close your action log when you don't need this feature anymore:: >>> fileh.disable_undo() That will allow you to continue working with your data without actually requiring PyTables to keep track of all your actions, and more importantly, allowing your objects to die completely if they have to, not requiring to keep them anywhere, and hence saving process time and space in your database file. A more complete example ~~~~~~~~~~~~~~~~~~~~~~~ Now, time for a somewhat more sophisticated demonstration of the Undo/Redo feature. In it, several marks will be set in different parts of the code flow and we will see how to jump between these marks with just one method call. You can find the code used in this example in :file:`examples/tutorial3-2.py` Let's introduce the first part of the code:: import tables # Create an HDF5 file fileh = tables.open_file('tutorial3-2.h5', 'w', title='Undo/Redo demo 2') #'-**-**-**-**-**-**- enable undo/redo log -**-**-**-**-**-**-**-' fileh.enable_undo() # Start undoable operations fileh.create_array('/', 'otherarray1', [3,4], 'Another array 1') fileh.create_group('/', 'agroup', 'Group 1') # Create a 'first' mark fileh.mark('first') fileh.create_array('/agroup', 'otherarray2', [4,5], 'Another array 2') fileh.create_group('/agroup', 'agroup2', 'Group 2') # Create a 'second' mark fileh.mark('second') fileh.create_array('/agroup/agroup2', 'otherarray3', [5,6], 'Another array 3') # Create a 'third' mark fileh.mark('third') fileh.create_array('/', 'otherarray4', [6,7], 'Another array 4') fileh.create_array('/agroup', 'otherarray5', [7,8], 'Another array 5') You can see how we have set several marks interspersed in the code flow, representing different states of the database. Also, note that we have assigned *names* to these marks, namely 'first', 'second' and 'third'. Now, start doing some jumps back and forth in the states of the database:: # Now go to mark 'first' fileh.goto('first') assert '/otherarray1' in fileh assert '/agroup' in fileh assert '/agroup/agroup2' not in fileh assert '/agroup/otherarray2' not in fileh assert '/agroup/agroup2/otherarray3' not in fileh assert '/otherarray4' not in fileh assert '/agroup/otherarray5' not in fileh # Go to mark 'third' fileh.goto('third') assert '/otherarray1' in fileh assert '/agroup' in fileh assert '/agroup/agroup2' in fileh assert '/agroup/otherarray2' in fileh assert '/agroup/agroup2/otherarray3' in fileh assert '/otherarray4' not in fileh assert '/agroup/otherarray5' not in fileh # Now go to mark 'second' fileh.goto('second') assert '/otherarray1' in fileh assert '/agroup' in fileh assert '/agroup/agroup2' in fileh assert '/agroup/otherarray2' in fileh assert '/agroup/agroup2/otherarray3' not in fileh assert '/otherarray4' not in fileh assert '/agroup/otherarray5' not in fileh Well, the code above shows how easy is to jump to a certain mark in the database by using the :meth:`File.goto` method. There are also a couple of implicit marks for going to the beginning or the end of the saved states: 0 and -1. Going to mark #0 means go to the beginning of the saved actions, that is, when method fileh.enable_undo() was called. Going to mark #-1 means go to the last recorded action, that is the last action in the code flow. Let's see what happens when going to the end of the action log:: # Go to the end fileh.goto(-1) assert '/otherarray1' in fileh assert '/agroup' in fileh assert '/agroup/agroup2' in fileh assert '/agroup/otherarray2' in fileh assert '/agroup/agroup2/otherarray3' in fileh assert '/otherarray4' in fileh assert '/agroup/otherarray5' in fileh # Check that objects have come back to life in a sane state assert fileh.root.otherarray1.read() == [3,4] assert fileh.root.agroup.otherarray2.read() == [4,5] assert fileh.root.agroup.agroup2.otherarray3.read() == [5,6] assert fileh.root.otherarray4.read() == [6,7] assert fileh.root.agroup.otherarray5.read() == [7,8] Try yourself going to the beginning of the action log (remember, the mark #0) and check the contents of the object tree. We have nearly finished this demonstration. As always, do not forget to close the action log as well as the database:: #'-**-**-**-**-**-**- disable undo/redo log -**-**-**-**-**-**-**-' fileh.disable_undo() # Close the file fileh.close() You might want to check other examples on Undo/Redo feature that appear in :file:`examples/undo-redo.py`. Using enumerated types ---------------------- PyTables includes support for handling enumerated types. Those types are defined by providing an exhaustive *set* or *list* of possible, named values for a variable of that type. Enumerated variables of the same type are usually compared between them for equality and sometimes for order, but are not usually operated upon. Enumerated values have an associated *name* and *concrete value*. Every name is unique and so are concrete values. An enumerated variable always takes the concrete value, not its name. Usually, the concrete value is not used directly, and frequently it is entirely irrelevant. For the same reason, an enumerated variable is not usually compared with concrete values out of its enumerated type. For that kind of use, standard variables and constants are more adequate. PyTables provides the Enum (see :ref:`EnumClassDescr`) class to provide support for enumerated types. Each instance of Enum is an enumerated type (or *enumeration*). For example, let us create an enumeration of colors All these examples can be found in :file:`examples/enum.py`:: >>> import tables >>> colorList = ['red', 'green', 'blue', 'white', 'black'] >>> colors = tables.Enum(colorList) Here we used a simple list giving the names of enumerated values, but we left the choice of concrete values up to the Enum class. Let us see the enumerated pairs to check those values:: >>> print("Colors:", [v for v in colors]) Colors: [('blue', 2), ('black', 4), ('white', 3), ('green', 1), ('red', 0)] Names have been given automatic integer concrete values. We can iterate over the values in an enumeration, but we will usually be more interested in accessing single values. We can get the concrete value associated with a name by accessing it as an attribute or as an item (the later can be useful for names not resembling Python identifiers):: >>> print("Value of 'red' and 'white':", (colors.red, colors.white)) Value of 'red' and 'white': (0, 3) >>> print("Value of 'yellow':", colors.yellow) Value of 'yellow': Traceback (most recent call last): File "", line 1, in ? File ".../tables/misc/enum.py", line 230, in __getattr__ raise AttributeError(\*ke.args) AttributeError: no enumerated value with that name: 'yellow' >>> >>> print("Value of 'red' and 'white':", (colors['red'], colors['white'])) Value of 'red' and 'white': (0, 3) >>> print("Value of 'yellow':", colors['yellow']) Value of 'yellow': Traceback (most recent call last): File "", line 1, in ? File ".../tables/misc/enum.py", line 189, in __getitem__ raise KeyError("no enumerated value with that name: %r" % (name,)) KeyError: "no enumerated value with that name: 'yellow'" See how accessing a value that is not in the enumeration raises the appropriate exception. We can also do the opposite action and get the name that matches a concrete value by using the __call__() method of Enum:: >>> print("Name of value %s:" % colors.red, colors(colors.red)) Name of value 0: red >>> print("Name of value 1234:", colors(1234)) Name of value 1234: Traceback (most recent call last): File "", line 1, in ? File ".../tables/misc/enum.py", line 320, in __call__ raise ValueError( ValueError: no enumerated value with that concrete value: 1234 You can see what we made as using the enumerated type to *convert* a concrete value into a name in the enumeration. Of course, values out of the enumeration can not be converted. Enumerated columns ~~~~~~~~~~~~~~~~~~ Columns of an enumerated type can be declared by using the EnumCol (see :ref:`ColClassDescr`) class. To see how this works, let us open a new PyTables file and create a table to collect the simulated results of a probabilistic experiment. In it, we have a bag full of colored balls; we take a ball out and annotate the time of extraction and the color of the ball:: >>> h5f = tables.open_file('enum.h5', 'w') >>> class BallExt(tables.IsDescription): ... ballTime = tables.Time32Col() ... ballColor = tables.EnumCol(colors, 'black', base='uint8') >>> tbl = h5f.create_table('/', 'extractions', BallExt, title="Random ball extractions") >>> We declared the ballColor column to be of the enumerated type colors, with a default value of black. We also stated that we are going to store concrete values as unsigned 8-bit integer values [4]_. Let us use some random values to fill the table:: >>> import time >>> import random >>> now = time.time() >>> row = tbl.row >>> for i in range(10): ... row['ballTime'] = now + i ... row['ballColor'] = colors[random.choice(colorList)] # notice this ... row.append() >>> Notice how we used the __getitem__() call of colors to get the concrete value to store in ballColor. You should know that this way of appending values to a table does automatically check for the validity on enumerated values. For instance:: >>> row['ballTime'] = now + 42 >>> row['ballColor'] = 1234 Traceback (most recent call last): File "", line 1, in File "tableextension.pyx", line 1086, in tableextension.Row.__setitem__ File ".../tables/misc/enum.py", line 320, in __call__ "no enumerated value with that concrete value: %r" % (value,)) ValueError: no enumerated value with that concrete value: 1234 But take care that this check is *only* performed here and not in other methods such as tbl.append() or tbl.modify_rows(). Now, after flushing the table we can see the results of the insertions:: >>> tbl.flush() >>> for r in tbl: ... ballTime = r['ballTime'] ... ballColor = colors(r['ballColor']) # notice this ... print("Ball extracted on %d is of color %s." % (ballTime, ballColor)) Ball extracted on 1173785568 is of color green. Ball extracted on 1173785569 is of color black. Ball extracted on 1173785570 is of color white. Ball extracted on 1173785571 is of color black. Ball extracted on 1173785572 is of color black. Ball extracted on 1173785573 is of color red. Ball extracted on 1173785574 is of color green. Ball extracted on 1173785575 is of color red. Ball extracted on 1173785576 is of color white. Ball extracted on 1173785577 is of color white. As a last note, you may be wondering how to have access to the enumeration associated with ballColor once the file is closed and reopened. You can call tbl.get_enum('ballColor') (see :meth:`Table.get_enum`) to get the enumeration back. Enumerated arrays ~~~~~~~~~~~~~~~~~ EArray and VLArray leaves can also be declared to store enumerated values by means of the EnumAtom (see :ref:`AtomClassDescr`) class, which works very much like EnumCol for tables. Also, Array leaves can be used to open native HDF enumerated arrays. Let us create a sample EArray containing ranges of working days as bidimensional values:: >>> workingDays = {'Mon': 1, 'Tue': 2, 'Wed': 3, 'Thu': 4, 'Fri': 5} >>> dayRange = tables.EnumAtom(workingDays, 'Mon', base='uint16') >>> earr = h5f.create_earray('/', 'days', dayRange, (0, 2), title="Working day ranges") >>> earr.flavor = 'python' Nothing surprising, except for a pair of details. In the first place, we use a *dictionary* instead of a list to explicitly set concrete values in the enumeration. In the second place, there is no explicit Enum instance created! Instead, the dictionary is passed as the first argument to the constructor of EnumAtom. If the constructor gets a list or a dictionary instead of an enumeration, it automatically builds the enumeration from it. Now let us feed some data to the array:: >>> wdays = earr.get_enum() >>> earr.append([(wdays.Mon, wdays.Fri), (wdays.Wed, wdays.Fri)]) >>> earr.append([(wdays.Mon, 1234)]) Please note that, since we had no explicit Enum instance, we were forced to use get_enum() (see :ref:`EArrayMethodsDescr`) to get it from the array (we could also have used dayRange.enum). Also note that we were able to append an invalid value (1234). Array methods do not check the validity of enumerated values. Finally, we will print the contents of the array:: >>> for (d1, d2) in earr: ... print("From %s to %s (%d days)." % (wdays(d1), wdays(d2), d2-d1+1)) From Mon to Fri (5 days). From Wed to Fri (3 days). Traceback (most recent call last): File "", line 2, in File ".../tables/misc/enum.py", line 320, in __call__ "no enumerated value with that concrete value: %r" % (value,)) ValueError: no enumerated value with that concrete value: 1234 That was an example of operating on concrete values. It also showed how the value-to-name conversion failed because of the value not belonging to the enumeration. Now we will close the file, and this little tutorial on enumerated types is done:: >>> h5f.close() Dealing with nested structures in tables ---------------------------------------- PyTables supports the handling of nested structures (or nested datatypes, as you prefer) in table objects, allowing you to define arbitrarily nested columns. An example will clarify what this means. Let's suppose that you want to group your data in pieces of information that are more related than others pieces in your table, So you may want to tie them up together in order to have your table better structured but also be able to retrieve and deal with these groups more easily. You can create such a nested substructures by just nesting subclasses of IsDescription. Let's see one example (okay, it's a bit silly, but will serve for demonstration purposes):: from tables import * class Info(IsDescription): """A sub-structure of Test""" _v_pos = 2 # The position in the whole structure name = StringCol(10) value = Float64Col(pos=0) colors = Enum(['red', 'green', 'blue']) class NestedDescr(IsDescription): """A description that has several nested columns""" color = EnumCol(colors, 'red', base='uint32') info1 = Info() class info2(IsDescription): _v_pos = 1 name = StringCol(10) value = Float64Col(pos=0) class info3(IsDescription): x = Float64Col(dflt=1) y = UInt8Col(dflt=1) The root class is NestedDescr and both info1 and info2 are *substructures* of it. Note how info1 is actually an instance of the class Info that was defined prior to NestedDescr. Also, there is a third substructure, namely info3 that hangs from the substructure info2. You can also define positions of substructures in the containing object by declaring the special class attribute _v_pos. Nested table creation ~~~~~~~~~~~~~~~~~~~~~ Now that we have defined our nested structure, let's create a *nested* table, that is a table with columns that contain other subcolumns:: >>> fileh = open_file("nested-tut.h5", "w") >>> table = fileh.create_table(fileh.root, 'table', NestedDescr) Done! Now, we have to feed the table with some values. The problem is how we are going to reference to the nested fields. That's easy, just use a '/' character to separate names in different nested levels. Look at this:: >>> row = table.row >>> for i in range(10): ... row['color'] = colors[['red', 'green', 'blue'][i%3]] ... row['info1/name'] = "name1-%s" % i ... row['info2/name'] = "name2-%s" % i ... row['info2/info3/y'] = i ... # All the rest will be filled with defaults ... row.append() >>> table.flush() >>> table.nrows 10 You see? In order to fill the fields located in the substructures, we just need to specify its full path in the table hierarchy. Reading nested tables ~~~~~~~~~~~~~~~~~~~~~ Now, what happens if we want to read the table? What kind oft data container will we get? Well, it's worth trying it:: >>> nra = table[::4] >>> nra array([(((1.0, 0), 'name2-0', 0.0), ('name1-0', 0.0), 0L), (((1.0, 4), 'name2-4', 0.0), ('name1-4', 0.0), 1L), (((1.0, 8), 'name2-8', 0.0), ('name1-8', 0.0), 2L)], dtype=[('info2', [('info3', [('x', '>f8'), ('y', '\|u1')]), ('name', '\|S10'), ('value', '>f8')]), ('info1', [('name', '\|S10'), ('value', '>f8')]), ('color', '>u4')]) What we got is a NumPy array with a *compound, nested datatype* (its dtype is a list of name-datatype tuples). We read one row for each four in the table, giving a result of three rows. You can make use of the above object in many different ways. For example, you can use it to append new data to the existing table object:: >>> table.append(nra) >>> table.nrows 13 Or, to create new tables:: >>> table2 = fileh.create_table(fileh.root, 'table2', nra) >>> table2[:] array([(((1.0, 0), 'name2-0', 0.0), ('name1-0', 0.0), 0L), (((1.0, 4), 'name2-4', 0.0), ('name1-4', 0.0), 1L), (((1.0, 8), 'name2-8', 0.0), ('name1-8', 0.0), 2L)], dtype=[('info2', [('info3', [('x', '>> names = [ x['info2/name'] for x in table if x['color'] == colors.red ] >>> names ['name2-0', 'name2-3', 'name2-6', 'name2-9', 'name2-0'] Note that the row accessor does not provide the natural naming feature, so you have to completely specify the path of your desired columns in order to reach them. Using Cols accessor ~~~~~~~~~~~~~~~~~~~ We can use the cols attribute object (see :ref:`ColsClassDescr`) of the table so as to quickly access the info located in the interesting substructures:: >>> table.cols.info2[1:5] array([((1.0, 1), 'name2-1', 0.0), ((1.0, 2), 'name2-2', 0.0), ((1.0, 3), 'name2-3', 0.0), ((1.0, 4), 'name2-4', 0.0)], dtype=[('info3', [('x', '>> table.cols.info2.info3[1:5] array([(1.0, 1), (1.0, 2), (1.0, 3), (1.0, 4)], dtype=[('x', '>> table.cols._f_col('info2') /table.cols.info2 (Cols), 3 columns info3 (Cols(), Description) name (Column(), \|S10) value (Column(), float64) Here, you've got another Cols object handler because *info2* was a nested column. If you select a non-nested column, you will get a regular Column instance:: >>> table.cols._f_col('info2/info3/y') /table.cols.info2.info3.y (Column(), uint8, idx=None) To sum up, the cols accessor is a very handy and powerful way to access data in your nested tables. Don't be afraid of using it, specially when doing interactive work. Accessing meta-information of nested tables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tables have an attribute called description which points to an instance of the Description class (see :ref:`DescriptionClassDescr`) and is useful to discover different meta-information about table data. Let's see how it looks like:: >>> table.description { "info2": { "info3": { "x": Float64Col(shape=(), dflt=1.0, pos=0), "y": UInt8Col(shape=(), dflt=1, pos=1)}, "name": StringCol(itemsize=10, shape=(), dflt='', pos=1), "value": Float64Col(shape=(), dflt=0.0, pos=2)}, "info1": { "name": StringCol(itemsize=10, shape=(), dflt='', pos=0), "value": Float64Col(shape=(), dflt=0.0, pos=1)}, "color": EnumCol(enum=Enum({'blue': 2, 'green': 1, 'red': 0}), dflt='red', base=UInt32Atom(shape=(), dflt=0), shape=(), pos=2)} As you can see, it provides very useful information on both the formats and the structure of the columns in your table. This object also provides a natural naming approach to access to subcolumns metadata:: >>> table.description.info1 {"name": StringCol(itemsize=10, shape=(), dflt='', pos=0), "value": Float64Col(shape=(), dflt=0.0, pos=1)} >>> table.description.info2.info3 {"x": Float64Col(shape=(), dflt=1.0, pos=0), "y": UInt8Col(shape=(), dflt=1, pos=1)} There are other variables that can be interesting for you:: >>> table.description._v_nested_names [('info2', [('info3', ['x', 'y']), 'name', 'value']), ('info1', ['name', 'value']), 'color'] >>> table.description.info1._v_nested_names ['name', 'value'] _v_nested_names provides the names of the columns as well as its structure. You can see that there are the same attributes for the different levels of the Description object, because the levels are *also* Description objects themselves. There is a special attribute, called _v_nested_descr, that can be useful to create nested structured arrays that imitate the structure of the table (or a subtable thereof):: >>> import numpy >>> table.description._v_nested_descr [('info2', [('info3', [('x', '()f8'), ('y', '()u1')]), ('name', '()S10'), ('value', '()f8')]), ('info1', [('name', '()S10'), ('value', '()f8')]), ('color', '()u4')] >>> numpy.rec.array(None, shape=0, dtype=table.description._v_nested_descr) recarray([], dtype=[('info2', [('info3', [('x', '>f8'), ('y', '|u1')]), ('name', '|S10'), ('value', '>f8')]), ('info1', [('name', '|S10'), ('value', '>f8')]), ('color', '>u4')]) >>> numpy.rec.array(None, shape=0, dtype=table.description.info2._v_nested_descr) recarray([], dtype=[('info3', [('x', '>f8'), ('y', '|u1')]), ('name', '|S10'), ('value', '>f8')]) You can see a simple example on how to create an array with NumPy. Finally, there is a special iterator of the Description class, called _f_walk that is able to return you the different columns of the table:: >>> for coldescr in table.description._f_walk(): ... print("column-->",coldescr) column--> Description([('info2', [('info3', [('x', '()f8'), ('y', '()u1')]), ('name', '()S10'), ('value', '()f8')]), ('info1', [('name', '()S10'), ('value', '()f8')]), ('color', '()u4')]) column--> EnumCol(enum=Enum({'blue': 2, 'green': 1, 'red': 0}), dflt='red', base=UInt32Atom(shape=(), dflt=0), shape=(), pos=2) column--> Description([('info3', [('x', '()f8'), ('y', '()u1')]), ('name', '()S10'), ('value', '()f8')]) column--> StringCol(itemsize=10, shape=(), dflt='', pos=1) column--> Float64Col(shape=(), dflt=0.0, pos=2) column--> Description([('name', '()S10'), ('value', '()f8')]) column--> StringCol(itemsize=10, shape=(), dflt='', pos=0) column--> Float64Col(shape=(), dflt=0.0, pos=1) column--> Description([('x', '()f8'), ('y', '()u1')]) column--> Float64Col(shape=(), dflt=1.0, pos=0) column--> UInt8Col(shape=(), dflt=1, pos=1) See the :ref:`DescriptionClassDescr` for the complete listing of attributes and methods of Description. Well, this is the end of this tutorial. As always, do not forget to close your files:: >>> fileh.close() Finally, you may want to have a look at your resulting data file. .. code-block:: bash $ ptdump -d nested-tut.h5 / (RootGroup) '' /table (Table(13,)) '' Data dump: [0] (((1.0, 0), 'name2-0', 0.0), ('name1-0', 0.0), 0L) [1] (((1.0, 1), 'name2-1', 0.0), ('name1-1', 0.0), 1L) [2] (((1.0, 2), 'name2-2', 0.0), ('name1-2', 0.0), 2L) [3] (((1.0, 3), 'name2-3', 0.0), ('name1-3', 0.0), 0L) [4] (((1.0, 4), 'name2-4', 0.0), ('name1-4', 0.0), 1L) [5] (((1.0, 5), 'name2-5', 0.0), ('name1-5', 0.0), 2L) [6] (((1.0, 6), 'name2-6', 0.0), ('name1-6', 0.0), 0L) [7] (((1.0, 7), 'name2-7', 0.0), ('name1-7', 0.0), 1L) [8] (((1.0, 8), 'name2-8', 0.0), ('name1-8', 0.0), 2L) [9] (((1.0, 9), 'name2-9', 0.0), ('name1-9', 0.0), 0L) [10] (((1.0, 0), 'name2-0', 0.0), ('name1-0', 0.0), 0L) [11] (((1.0, 4), 'name2-4', 0.0), ('name1-4', 0.0), 1L) [12] (((1.0, 8), 'name2-8', 0.0), ('name1-8', 0.0), 2L) /table2 (Table(3,)) '' Data dump: [0] (((1.0, 0), 'name2-0', 0.0), ('name1-0', 0.0), 0L) [1] (((1.0, 4), 'name2-4', 0.0), ('name1-4', 0.0), 1L) [2] (((1.0, 8), 'name2-8', 0.0), ('name1-8', 0.0), 2L) Most of the code in this section is also available in :file:`examples/nested-tut.py`. All in all, PyTables provides a quite comprehensive toolset to cope with nested structures and address your classification needs. However, caveat emptor, be sure to not nest your data too deeply or you will get inevitably messed interpreting too intertwined lists, tuples and description objects. Other examples in PyTables distribution --------------------------------------- Feel free to examine the rest of examples in directory :file:`examples/`, and try to understand them. We have written several practical sample scripts to give you an idea of the PyTables capabilities, its way of dealing with HDF5 objects, and how it can be used in the real world. ------------ .. [1] Appending data to arrays is also supported, but you need to create special objects called EArray (see :ref:`EArrayClassDescr` for more info). .. [2] Note that you can append not only scalar values to tables, but also fully multidimensional array objects. .. [3] You can even *hide* nodes temporarily. Will you be able to find out how? .. [4] In fact, only integer values are supported right now, but this may change in the future. PyTables-v.3.1.1/doc/source/usersguide/usersguide.rst000066400000000000000000000055001231437614300226440ustar00rootroot00000000000000:orphan: ===================== PyTables User's Guide ===================== .. raw:: latex \listoffigures \listoftables \clearpage :Authors: Francesc Alted, Ivan Vilata, Scott Prater, Vicent Mas, Tom Hedley, Antonio Valentino, Jeffrey Whitaker, Anthony Scopatz, Josh Moore :Copyright: |copy| 2002, 2003, 2004 - Francesc Alted |copy| 2005, 2006, 2007 - Cárabos Coop. V. |copy| 2008, 2009, 2010 - Francesc Alted |copy| 2011-2014 - PyTables maintainers :Date: |today| :Version: |version| :Home Page: http://www.pytables.org .. raw:: latex \clearpage .. rubric:: Copyright Notice and Statement for PyTables User's Guide Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: a. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c. Neither the name of Francesc Alted nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .. |copy| unicode:: U+000A9 .. COPYRIGHT SIGN ------------------------- The PyTables Core Library ------------------------- .. toctree:: :maxdepth: 1 introduction installation tutorials libref optimization --------------------- Complementary modules --------------------- .. toctree:: :maxdepth: 1 filenode ---------- Appendixes ---------- .. todo:: check why "latex_appendices" config option doesn't work with parts .. todo:: try to use raw latex \appendix .. toctree:: :maxdepth: 1 datatypes condition_syntax parameter_files utilities file_format .. raw:: latex \bookmarksetup{startatroot} \addtocontents{toc}{\bigskip} .. toctree:: :maxdepth: 1 bibliography PyTables-v.3.1.1/doc/source/usersguide/utilities.rst000066400000000000000000000426131231437614300225060ustar00rootroot00000000000000Utilities ========= PyTables comes with a couple of utilities that make the life easier to the user. One is called ptdump and lets you see the contents of a PyTables file (or generic HDF5 file, if supported). The other one is named ptrepack that allows to (recursively) copy sub-hierarchies of objects present in a file into another one, changing, if desired, some of the filters applied to the leaves during the copy process. Normally, these utilities will be installed somewhere in your PATH during the process of installation of the PyTables package, so that you can invoke them from any place in your file system after the installation has successfully finished. ptdump ------ As has been said before, ptdump utility allows you look into the contents of your PyTables files. It lets you see not only the data but also the metadata (that is, the *structure* and additional information in the form of *attributes*). Usage ~~~~~ For instructions on how to use it, just pass the -h flag to the command: .. code-block:: bash $ ptdump -h to see the message usage: .. code-block:: bash usage: ptdump [-d] [-v] [-a] [-c] [-i] [-R start,stop,step] [-h] file[:nodepath] -d -- Dump data information on leaves -v -- Dump more metainformation on nodes -a -- Show attributes in nodes (only useful when -v or -d are active) -c -- Show info of columns in tables (only useful when -v or -d are active) -i -- Show info of indexed columns (only useful when -v or -d are active) -R RANGE -- Select a RANGE of rows in the form "start,stop,step" -h -- Print help on usage Read on for a brief introduction to this utility. A small tutorial on ptdump ~~~~~~~~~~~~~~~~~~~~~~~~~~ Let's suppose that we want to know only the *structure* of a file. In order to do that, just don't pass any flag, just the file as parameter. .. code-block:: bash $ ptdump vlarray1.h5 / (RootGroup) '' /vlarray1 (VLArray(3,), shuffle, zlib(1)) 'ragged array of ints' /vlarray2 (VLArray(3,), shuffle, zlib(1)) 'ragged array of strings' we can see that the file contains just a leaf object called vlarray1, that is an instance of VLArray, has 4 rows, and two filters has been used in order to create it: shuffle and zlib (with a compression level of 1). Let's say we want more meta-information. Just add the -v (verbose) flag: .. code-block:: bash $ ptdump -v vlarray1.h5 / (RootGroup) '' /vlarray1 (VLArray(3,), shuffle, zlib(1)) 'ragged array of ints' atom = Int32Atom(shape=(), dflt=0) byteorder = 'little' nrows = 3 flavor = 'numpy' /vlarray2 (VLArray(3,), shuffle, zlib(1)) 'ragged array of strings' atom = StringAtom(itemsize=2, shape=(), dflt='') byteorder = 'irrelevant' nrows = 3 flavor = 'python' so we can see more info about the atoms that are the components of the vlarray1 dataset, i.e. they are scalars of type Int32 and with NumPy *flavor*. If we want information about the attributes on the nodes, we must add the -a flag: .. code-block:: bash $ ptdump -va vlarray1.h5 / (RootGroup) '' /._v_attrs (AttributeSet), 4 attributes: [CLASS := 'GROUP', PYTABLES_FORMAT_VERSION := '2.0', TITLE := '', VERSION := '1.0'] /vlarray1 (VLArray(3,), shuffle, zlib(1)) 'ragged array of ints' atom = Int32Atom(shape=(), dflt=0) byteorder = 'little' nrows = 3 flavor = 'numpy' /vlarray1._v_attrs (AttributeSet), 3 attributes: [CLASS := 'VLARRAY', TITLE := 'ragged array of ints', VERSION := '1.3'] /vlarray2 (VLArray(3,), shuffle, zlib(1)) 'ragged array of strings' atom = StringAtom(itemsize=2, shape=(), dflt='') byteorder = 'irrelevant' nrows = 3 flavor = 'python' /vlarray2._v_attrs (AttributeSet), 4 attributes: [CLASS := 'VLARRAY', FLAVOR := 'python', TITLE := 'ragged array of strings', VERSION := '1.3'] Let's have a look at the real data: .. code-block:: bash $ ptdump -d vlarray1.h5 / (RootGroup) '' /vlarray1 (VLArray(3,), shuffle, zlib(1)) 'ragged array of ints' Data dump: [0] [5 6] [1] [5 6 7] [2] [5 6 9 8] /vlarray2 (VLArray(3,), shuffle, zlib(1)) 'ragged array of strings' Data dump: [0] ['5', '66'] [1] ['5', '6', '77'] [2] ['5', '6', '9', '88'] We see here a data dump of the 4 rows in vlarray1 object, in the form of a list. Because the object is a VLA, we see a different number of integers on each row. Say that we are interested only on a specific *row range* of the /vlarray1 object: .. code-block:: bash ptdump -R2,3 -d vlarray1.h5:/vlarray1 /vlarray1 (VLArray(3,), shuffle, zlib(1)) 'ragged array of ints' Data dump: [2] [5 6 9 8] Here, we have specified the range of rows between 2 and 4 (the upper limit excluded, as usual in Python). See how we have selected only the /vlarray1 object for doing the dump (vlarray1.h5:/vlarray1). Finally, you can mix several information at once: .. code-block:: bash $ ptdump -R2,3 -vad vlarray1.h5:/vlarray1 /vlarray1 (VLArray(3,), shuffle, zlib(1)) 'ragged array of ints' atom = Int32Atom(shape=(), dflt=0) byteorder = 'little' nrows = 3 flavor = 'numpy' /vlarray1._v_attrs (AttributeSet), 3 attributes: [CLASS := 'VLARRAY', TITLE := 'ragged array of ints', VERSION := '1.3'] Data dump: [2] [5 6 9 8] .. _ptrepackDescr: ptrepack -------- This utility is a very powerful one and lets you copy any leaf, group or complete subtree into another file. During the copy process you are allowed to change the filter properties if you want so. Also, in the case of duplicated pathnames, you can decide if you want to overwrite already existing nodes on the destination file. Generally speaking, ptrepack can be useful in may situations, like replicating a subtree in another file, change the filters in objects and see how affect this to the compression degree or I/O performance, consolidating specific data in repositories or even *importing* generic HDF5 files and create true PyTables counterparts. Usage ~~~~~ For instructions on how to use it, just pass the -h flag to the command: .. code-block:: bash $ ptrepack -h to see the message usage: .. code-block:: bash usage: ptrepack [-h] [-v] [-o] [-R start,stop,step] [--non-recursive] [--dest-title=title] [--dont-create-sysattrs] [--dont-copy-userattrs] [--overwrite-nodes] [--complevel=(0-9)] [--complib=lib] [--shuffle=(0|1)] [--fletcher32=(0|1)] [--keep-source-filters] [--chunkshape=value] [--upgrade-flavors] [--dont-regenerate-old-indexes] [--sortby=column] [--checkCSI] [--propindexes] sourcefile:sourcegroup destfile:destgroup -h -- Print usage message. -v -- Show more information. -o -- Overwrite destination file. -R RANGE -- Select a RANGE of rows (in the form "start,stop,step") during the copy of \*all* the leaves. Default values are "None,None,1", which means a copy of all the rows. --non-recursive -- Do not do a recursive copy. Default is to do it. --dest-title=title -- Title for the new file (if not specified, the source is copied). --dont-create-sysattrs -- Do not create sys attrs (default is to do it). --dont-copy-userattrs -- Do not copy the user attrs (default is to do it). --overwrite-nodes -- Overwrite destination nodes if they exist. Default is to not overwrite them. --complevel=(0-9) -- Set a compression level (0 for no compression, which is the default). --complib=lib -- Set the compression library to be used during the copy. lib can be set to "zlib", "lzo", "bzip2" or "blosc". Additional compressors for Blosc like "blosc:blosclz", "blosc:lz4", "blosc:lz4hc", "blosc:snappy" and "blosc:zlib", are supported too. Defaults to "zlib". --shuffle=(0|1) -- Activate or not the shuffling filter (default is active if complevel>0). --fletcher32=(0|1) -- Whether to activate or not the fletcher32 filter (not active by default). --keep-source-filters -- Use the original filters in source files. The default is not doing that if any of --complevel, --complib, --shuffle or --fletcher32 option is specified. --chunkshape=("keep"\|"auto"\|int|tuple) -- Set a chunkshape. A value of "auto" computes a sensible value for the chunkshape of the leaves copied. The default is to "keep" the original value. --upgrade-flavors -- When repacking PyTables 1.x files, the flavor of leaves will be unset. With this, such a leaves will be serialized as objects with the internal flavor ('numpy' for 2.x series). --dont-regenerate-old-indexes -- Disable regenerating old indexes. The default is to regenerate old indexes as they are found. --sortby=column -- Do a table copy sorted by the index in "column". For reversing the order, use a negative value in the "step" part of "RANGE" (see "-R" flag). Only applies to table objects. --checkCSI -- Force the check for a CSI index for the --sortby column. --propindexes -- Propagate the indexes existing in original tables. The default is to not propagate them. Only applies to table objects. Read on for a brief introduction to this utility. A small tutorial on ptrepack ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Imagine that we have ended the tutorial 1 (see the output of examples/tutorial1-1.py), and we want to copy our reduced data (i.e. those datasets that hangs from the /column group) to another file. First, let's remember the content of the examples/tutorial1.h5: .. code-block:: bash $ ptdump tutorial1.h5 / (RootGroup) 'Test file' /columns (Group) 'Pressure and Name' /columns/name (Array(3,)) 'Name column selection' /columns/pressure (Array(3,)) 'Pressure column selection' /detector (Group) 'Detector information' /detector/readout (Table(10,)) 'Readout example' Now, copy the /columns to other non-existing file. That's easy: .. code-block:: bash $ ptrepack tutorial1.h5:/columns reduced.h5 That's all. Let's see the contents of the newly created reduced.h5 file: .. code-block:: bash $ ptdump reduced.h5 / (RootGroup) '' /name (Array(3,)) 'Name column selection' /pressure (Array(3,)) 'Pressure column selection' so, you have copied the children of /columns group into the *root* of the reduced.h5 file. Now, you suddenly realized that what you intended to do was to copy all the hierarchy, the group /columns itself included. You can do that by just specifying the destination group: .. code-block:: bash $ ptrepack tutorial1.h5:/columns reduced.h5:/columns $ ptdump reduced.h5 / (RootGroup) '' /name (Array(3,)) 'Name column selection' /pressure (Array(3,)) 'Pressure column selection' /columns (Group) '' /columns/name (Array(3,)) 'Name column selection' /columns/pressure (Array(3,)) 'Pressure column selection' OK. Much better. But you want to get rid of the existing nodes on the new file. You can achieve this by adding the -o flag: .. code-block:: bash $ ptrepack -o tutorial1.h5:/columns reduced.h5:/columns $ ptdump reduced.h5 / (RootGroup) '' /columns (Group) '' /columns/name (Array(3,)) 'Name column selection' /columns/pressure (Array(3,)) 'Pressure column selection' where you can see how the old contents of the reduced.h5 file has been overwritten. You can copy just one single node in the repacking operation and change its name in destination: .. code-block:: bash $ ptrepack tutorial1.h5:/detector/readout reduced.h5:/rawdata $ ptdump reduced.h5 / (RootGroup) '' /rawdata (Table(10,)) 'Readout example' /columns (Group) '' /columns/name (Array(3,)) 'Name column selection' /columns/pressure (Array(3,)) 'Pressure column selection' where the /detector/readout has been copied to /rawdata in destination. We can change the filter properties as well: .. code-block:: bash $ ptrepack --complevel=1 tutorial1.h5:/detector/readout reduced.h5:/rawdata Problems doing the copy from 'tutorial1.h5:/detector/readout' to 'reduced.h5:/rawdata' The error was --> tables.exceptions.NodeError: destination group \``/\`` already has a node named \``rawdata``; you may want to use the \``overwrite`` argument The destination file looks like: / (RootGroup) '' /rawdata (Table(10,)) 'Readout example' /columns (Group) '' /columns/name (Array(3,)) 'Name column selection' /columns/pressure (Array(3,)) 'Pressure column selection' Traceback (most recent call last): File "utils/ptrepack", line 3, in ? main() File ".../tables/scripts/ptrepack.py", line 349, in main stats = stats, start = start, stop = stop, step = step) File ".../tables/scripts/ptrepack.py", line 107, in copy_leaf raise RuntimeError, "Please check that the node names are not duplicated in destination, and if so, add the --overwrite-nodes flag if desired." RuntimeError: Please check that the node names are not duplicated in destination, and if so, add the --overwrite-nodes flag if desired. Ooops! We ran into problems: we forgot that the /rawdata pathname already existed in destination file. Let's add the --overwrite-nodes, as the verbose error suggested: .. code-block:: bash $ ptrepack --overwrite-nodes --complevel=1 tutorial1.h5:/detector/readout reduced.h5:/rawdata $ ptdump reduced.h5 / (RootGroup) '' /rawdata (Table(10,), shuffle, zlib(1)) 'Readout example' /columns (Group) '' /columns/name (Array(3,)) 'Name column selection' /columns/pressure (Array(3,)) 'Pressure column selection' you can check how the filter properties has been changed for the /rawdata table. Check as the other nodes still exists. Finally, let's copy a *slice* of the readout table in origin to destination, under a new group called /slices and with the name, for example, aslice: .. code-block:: bash $ ptrepack -R1,8,3 tutorial1.h5:/detector/readout reduced.h5:/slices/aslice $ ptdump reduced.h5 / (RootGroup) '' /rawdata (Table(10,), shuffle, zlib(1)) 'Readout example' /columns (Group) '' /columns/name (Array(3,)) 'Name column selection' /columns/pressure (Array(3,)) 'Pressure column selection' /slices (Group) '' /slices/aslice (Table(3,)) 'Readout example' note how only 3 rows of the original readout table has been copied to the new aslice destination. Note as well how the previously nonexistent slices group has been created in the same operation. pt2to3 ------ The PyTables 3.x series now follows `PEP 8`_ coding standard. This makes using PyTables more idiomatic with surrounding Python code that also adheres to this standard. The primary way that the 2.x series was *not* PEP 8 compliant was with respect to variable naming conventions. Approximately 450 API variables were identified and updated for PyTables 3.x. To ease migration, PyTables ships with a new ``pt2to3`` command line tool. This tool will run over a file and replace any instances of the old variable names with the 3.x version of the name. This tool covers the overwhelming majority of cases was used to transition the PyTables code base itself! However, it may also accidentally also pick up variable names in 3rd party codes that have *exactly* the same name as a PyTables' variable. This is because ``pt2to3`` was implemented using regular expressions rather than a fancier AST-based method. By using regexes, ``pt2to3`` works on Python and Cython code. ``pt2to3`` **help:** .. code-block:: bash usage: pt2to3 [-h] [-r] [-p] [-o OUTPUT] [-i] filename PyTables 2.x -> 3.x API transition tool This tool displays to standard out, so it is common to pipe this to another file: $ pt2to3 oldfile.py > newfile.py positional arguments: filename path to input file. optional arguments: -h, --help show this help message and exit -r, --reverse reverts changes, going from 3.x -> 2.x. -p, --no-ignore-previous ignores previous_api() calls. -o OUTPUT output file to write to. -i, --inplace overwrites the file in-place. Note that ``pt2to3`` only works on a single file, not a a directory. However, a simple BASH script may be written to run ``pt2to3`` over an entire directory and all sub-directories: .. code-block:: bash #!/bin/bash for f in $(find .) do echo $f pt2to3 $f > temp.txt mv temp.txt $f done .. note:: :program:`pt2to3` uses the :mod:`argparse` module that is part of the Python standard library since Python 2.7. Users of Python 2.6 should install :mod:`argparse` separately (e.g. via :program:`pip`). The old APIs and variable names will continue to be supported for the short term, where possible. (The major backwards incompatible changes come from the renaming of some function and method arguments and keyword arguments.) Using the 2.x APIs in the 3.x series, however, will issue warnings. The following is the release plan for the warning types: * 3.0 - PendingDeprecationWarning * 3.1 - DeprecationWarning * >=3.2 - Remove warnings, previous_api(), and _past.py; keep pt2to3, The current plan is to maintain the old APIs for at least 2 years, though this is subject to change. .. _PEP 8: http://www.python.org/dev/peps/pep-0008/ PyTables-v.3.1.1/doc/sphinxext/000077500000000000000000000000001231437614300163065ustar00rootroot00000000000000PyTables-v.3.1.1/doc/sphinxext/LICENSE.txt000066400000000000000000000175661231437614300201500ustar00rootroot00000000000000------------------------------------------------------------------------------- The files - ipython_console_highlighting.py - ipython_directive.py have the following license: The IPython licensing terms IPython is licensed under the terms of the Modified BSD License (also known as New or Revised BSD), as follows: Copyright (c) 2008-2010, IPython Development Team Copyright (c) 2001-2007, Fernando Perez. Copyright (c) 2001, Janko Hauser Copyright (c) 2001, Nathaniel Gray All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the IPython Development Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------- The files - numpydoc.py - autosummary.py - autosummary_generate.py - docscrape.py - docscrape_sphinx.py - phantom_import.py have the following license: Copyright (C) 2008 Stefan van der Walt , Pauli Virtanen Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------- The files - compiler_unparse.py - comment_eater.py - traitsdoc.py have the following license: This software is OSI Certified Open Source Software. OSI Certified is a certification mark of the Open Source Initiative. Copyright (c) 2006, Enthought, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Enthought, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------- The files - only_directives.py - plot_directive.py originate from Matplotlib (http://matplotlib.sf.net/) which has the following license: Copyright (c) 2002-2008 John D. Hunter; All Rights Reserved. 1. This LICENSE AGREEMENT is between John D. Hunter (“JDHâ€), and the Individual or Organization (“Licenseeâ€) accessing and otherwise using matplotlib software in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, JDH hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use matplotlib 0.98.3 alone or in any derivative version, provided, however, that JDH’s License Agreement and JDH’s notice of copyright, i.e., “Copyright (c) 2002-2008 John D. Hunter; All Rights Reserved†are retained in matplotlib 0.98.3 alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates matplotlib 0.98.3 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to matplotlib 0.98.3. 4. JDH is making matplotlib 0.98.3 available to Licensee on an “AS IS†basis. JDH MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, JDH MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB 0.98.3 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. JDH SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB 0.98.3 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING MATPLOTLIB 0.98.3, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between JDH and Licensee. This License Agreement does not grant permission to use JDH trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using matplotlib 0.98.3, Licensee agrees to be bound by the terms and conditions of this License Agreement. PyTables-v.3.1.1/doc/sphinxext/__init__.py000066400000000000000000000000331231437614300204130ustar00rootroot00000000000000from numpydoc import setup PyTables-v.3.1.1/doc/sphinxext/apigen.py000066400000000000000000000364631231437614300201370ustar00rootroot00000000000000"""Attempt to generate templates for module reference with Sphinx XXX - we exclude extension modules To include extension modules, first identify them as valid in the ``_uri2path`` method, then handle them in the ``_parse_module`` script. We get functions and classes by parsing the text of .py files. Alternatively we could import the modules for discovery, and we'd have to do that for extension modules. This would involve changing the ``_parse_module`` method to work via import and introspection, and might involve changing ``discover_modules`` (which determines which files are modules, and therefore which module URIs will be passed to ``_parse_module``). NOTE: this is a modified version of a script originally shipped with the PyMVPA project, which we've adapted for NIPY use. PyMVPA is an MIT-licensed project.""" # Stdlib imports import os import re # Functions and classes class ApiDocWriter(object): ''' Class for automatic detection and parsing of API docs to Sphinx-parsable reST format''' # only separating first two levels rst_section_levels = ['*', '=', '-', '~', '^'] def __init__(self, package_name, rst_extension='.rst', package_skip_patterns=None, module_skip_patterns=None, ): ''' Initialize package for parsing Parameters ---------- package_name : string Name of the top-level package. *package_name* must be the name of an importable package rst_extension : string, optional Extension for reST files, default '.rst' package_skip_patterns : None or sequence of {strings, regexps} Sequence of strings giving URIs of packages to be excluded Operates on the package path, starting at (including) the first dot in the package path, after *package_name* - so, if *package_name* is ``sphinx``, then ``sphinx.util`` will result in ``.util`` being passed for earching by these regexps. If is None, gives default. Default is: ['\.tests$'] module_skip_patterns : None or sequence Sequence of strings giving URIs of modules to be excluded Operates on the module name including preceding URI path, back to the first dot after *package_name*. For example ``sphinx.util.console`` results in the string to search of ``.util.console`` If is None, gives default. Default is: ['\.setup$', '\._'] ''' if package_skip_patterns is None: package_skip_patterns = ['\\.tests$'] if module_skip_patterns is None: module_skip_patterns = ['\\.setup$', '\\._'] self.package_name = package_name self.rst_extension = rst_extension self.package_skip_patterns = package_skip_patterns self.module_skip_patterns = module_skip_patterns def get_package_name(self): return self._package_name def set_package_name(self, package_name): ''' Set package_name >>> docwriter = ApiDocWriter('sphinx') >>> import sphinx >>> docwriter.root_path == sphinx.__path__[0] True >>> docwriter.package_name = 'docutils' >>> import docutils >>> docwriter.root_path == docutils.__path__[0] True ''' # It's also possible to imagine caching the module parsing here self._package_name = package_name self.root_module = __import__(package_name) self.root_path = self.root_module.__path__[0] self.written_modules = None package_name = property(get_package_name, set_package_name, None, 'get/set package_name') def _get_object_name(self, line): ''' Get second token in line >>> docwriter = ApiDocWriter('sphinx') >>> docwriter._get_object_name(" def func(): ") 'func' >>> docwriter._get_object_name(" class Klass(object): ") 'Klass' >>> docwriter._get_object_name(" class Klass: ") 'Klass' ''' name = line.split()[1].split('(')[0].strip() # in case we have classes which are not derived from object # ie. old style classes return name.rstrip(':') def _uri2path(self, uri): ''' Convert uri to absolute filepath Parameters ---------- uri : string URI of python module to return path for Returns ------- path : None or string Returns None if there is no valid path for this URI Otherwise returns absolute file system path for URI Examples -------- >>> docwriter = ApiDocWriter('sphinx') >>> import sphinx >>> modpath = sphinx.__path__[0] >>> res = docwriter._uri2path('sphinx.builder') >>> res == os.path.join(modpath, 'builder.py') True >>> res = docwriter._uri2path('sphinx') >>> res == os.path.join(modpath, '__init__.py') True >>> docwriter._uri2path('sphinx.does_not_exist') ''' if uri == self.package_name: return os.path.join(self.root_path, '__init__.py') path = uri.replace('.', os.path.sep) path = path.replace(self.package_name + os.path.sep, '') path = os.path.join(self.root_path, path) # XXX maybe check for extensions as well? if os.path.exists(path + '.py'): # file path += '.py' elif os.path.exists(os.path.join(path, '__init__.py')): path = os.path.join(path, '__init__.py') else: return None return path def _path2uri(self, dirpath): ''' Convert directory path to uri ''' relpath = dirpath.replace(self.root_path, self.package_name) if relpath.startswith(os.path.sep): relpath = relpath[1:] return relpath.replace(os.path.sep, '.') def _parse_module(self, uri): ''' Parse module defined in *uri* ''' filename = self._uri2path(uri) if filename is None: # nothing that we could handle here. return ([], []) f = open(filename, 'rt') functions, classes = self._parse_lines(f) f.close() return functions, classes def _parse_lines(self, linesource): ''' Parse lines of text for functions and classes ''' functions = [] classes = [] for line in linesource: if line.startswith('def ') and line.count('('): # exclude private stuff name = self._get_object_name(line) if not name.startswith('_'): functions.append(name) elif line.startswith('class '): # exclude private stuff name = self._get_object_name(line) if not name.startswith('_'): classes.append(name) else: pass functions.sort() classes.sort() return functions, classes def generate_api_doc(self, uri): '''Make autodoc documentation template string for a module Parameters ---------- uri : string python location of module - e.g 'sphinx.builder' Returns ------- S : string Contents of API doc ''' # get the names of all classes and functions functions, classes = self._parse_module(uri) if not len(functions) and not len(classes): print 'WARNING: Empty -', uri # dbg return '' # Make a shorter version of the uri that omits the package name for # titles uri_short = re.sub(r'^%s\.' % self.package_name, '', uri) ad = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n' chap_title = uri_short ad += (chap_title+'\n'+ self.rst_section_levels[1] * len(chap_title) + '\n\n') # Set the chapter title to read 'module' for all modules except for the # main packages if '.' in uri: title = 'Module: :mod:`' + uri_short + '`' else: title = ':mod:`' + uri_short + '`' ad += title + '\n' + self.rst_section_levels[2] * len(title) if len(classes): ad += '\nInheritance diagram for ``%s``:\n\n' % uri ad += '.. inheritance-diagram:: %s \n' % uri ad += ' :parts: 3\n' ad += '\n.. automodule:: ' + uri + '\n' ad += '\n.. currentmodule:: ' + uri + '\n' multi_class = len(classes) > 1 multi_fx = len(functions) > 1 if multi_class: ad += '\n' + 'Classes' + '\n' + \ self.rst_section_levels[2] * 7 + '\n' elif len(classes) and multi_fx: ad += '\n' + 'Class' + '\n' + \ self.rst_section_levels[2] * 5 + '\n' for c in classes: ad += '\n:class:`' + c + '`\n' \ + self.rst_section_levels[multi_class + 2 ] * \ (len(c)+9) + '\n\n' ad += '\n.. autoclass:: ' + c + '\n' # must NOT exclude from index to keep cross-refs working ad += ' :members:\n' \ ' :undoc-members:\n' \ ' :show-inheritance:\n' \ ' :inherited-members:\n' \ '\n' \ ' .. automethod:: __init__\n' if multi_fx: ad += '\n' + 'Functions' + '\n' + \ self.rst_section_levels[2] * 9 + '\n\n' elif len(functions) and multi_class: ad += '\n' + 'Function' + '\n' + \ self.rst_section_levels[2] * 8 + '\n\n' for f in functions: # must NOT exclude from index to keep cross-refs working ad += '\n.. autofunction:: ' + uri + '.' + f + '\n\n' return ad def _survives_exclude(self, matchstr, match_type): ''' Returns True if *matchstr* does not match patterns ``self.package_name`` removed from front of string if present Examples -------- >>> dw = ApiDocWriter('sphinx') >>> dw._survives_exclude('sphinx.okpkg', 'package') True >>> dw.package_skip_patterns.append('^\\.badpkg$') >>> dw._survives_exclude('sphinx.badpkg', 'package') False >>> dw._survives_exclude('sphinx.badpkg', 'module') True >>> dw._survives_exclude('sphinx.badmod', 'module') True >>> dw.module_skip_patterns.append('^\\.badmod$') >>> dw._survives_exclude('sphinx.badmod', 'module') False ''' if match_type == 'module': patterns = self.module_skip_patterns elif match_type == 'package': patterns = self.package_skip_patterns else: raise ValueError('Cannot interpret match type "%s"' % match_type) # Match to URI without package name L = len(self.package_name) if matchstr[:L] == self.package_name: matchstr = matchstr[L:] for pat in patterns: try: pat.search except AttributeError: pat = re.compile(pat) if pat.search(matchstr): return False return True def discover_modules(self): ''' Return module sequence discovered from ``self.package_name`` Parameters ---------- None Returns ------- mods : sequence Sequence of module names within ``self.package_name`` Examples -------- >>> dw = ApiDocWriter('sphinx') >>> mods = dw.discover_modules() >>> 'sphinx.util' in mods True >>> dw.package_skip_patterns.append('\.util$') >>> 'sphinx.util' in dw.discover_modules() False >>> ''' modules = [self.package_name] # raw directory parsing for dirpath, dirnames, filenames in os.walk(self.root_path): # Check directory names for packages root_uri = self._path2uri(os.path.join(self.root_path, dirpath)) for dirname in dirnames[:]: # copy list - we modify inplace package_uri = '.'.join((root_uri, dirname)) if (self._uri2path(package_uri) and self._survives_exclude(package_uri, 'package')): modules.append(package_uri) else: dirnames.remove(dirname) # Check filenames for modules for filename in filenames: module_name = filename[:-3] module_uri = '.'.join((root_uri, module_name)) if (self._uri2path(module_uri) and self._survives_exclude(module_uri, 'module')): modules.append(module_uri) return sorted(modules) def write_modules_api(self, modules, outdir): # write the list written_modules = [] for m in modules: api_str = self.generate_api_doc(m) if not api_str: continue # write out to file outfile = os.path.join(outdir, m + self.rst_extension) fileobj = open(outfile, 'wt') fileobj.write(api_str) fileobj.close() written_modules.append(m) self.written_modules = written_modules def write_api_docs(self, outdir): """Generate API reST files. Parameters ---------- outdir : string Directory name in which to store files We create automatic filenames for each module Returns ------- None Notes ----- Sets self.written_modules to list of written modules """ if not os.path.exists(outdir): os.mkdir(outdir) # compose list of modules modules = self.discover_modules() self.write_modules_api(modules, outdir) def write_index(self, outdir, froot='gen', relative_to=None): """Make a reST API index file from written files Parameters ---------- path : string Filename to write index to outdir : string Directory to which to write generated index file froot : string, optional root (filename without extension) of filename to write to Defaults to 'gen'. We add ``self.rst_extension``. relative_to : string path to which written filenames are relative. This component of the written file path will be removed from outdir, in the generated index. Default is None, meaning, leave path as it is. """ if self.written_modules is None: raise ValueError('No modules written') # Get full filename path path = os.path.join(outdir, froot+self.rst_extension) # Path written into index is relative to rootpath if relative_to is not None: relpath = outdir.replace(relative_to + os.path.sep, '') else: relpath = outdir idx = open(path, 'wt') w = idx.write w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n') w('.. toctree::\n\n') for f in self.written_modules: w(' %s\n' % os.path.join(relpath, f)) idx.close() PyTables-v.3.1.1/doc/sphinxext/comment_eater.py000066400000000000000000000120751231437614300215070ustar00rootroot00000000000000from cStringIO import StringIO import compiler import inspect import textwrap import tokenize from compiler_unparse import unparse class Comment(object): """ A comment block. """ is_comment = True def __init__(self, start_lineno, end_lineno, text): # int : The first line number in the block. 1-indexed. self.start_lineno = start_lineno # int : The last line number. Inclusive! self.end_lineno = end_lineno # str : The text block including '#' character but not any leading spaces. self.text = text def add(self, string, start, end, line): """ Add a new comment line. """ self.start_lineno = min(self.start_lineno, start[0]) self.end_lineno = max(self.end_lineno, end[0]) self.text += string def __repr__(self): return '%s(%r, %r, %r)' % (self.__class__.__name__, self.start_lineno, self.end_lineno, self.text) class NonComment(object): """ A non-comment block of code. """ is_comment = False def __init__(self, start_lineno, end_lineno): self.start_lineno = start_lineno self.end_lineno = end_lineno def add(self, string, start, end, line): """ Add lines to the block. """ if string.strip(): # Only add if not entirely whitespace. self.start_lineno = min(self.start_lineno, start[0]) self.end_lineno = max(self.end_lineno, end[0]) def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.start_lineno, self.end_lineno) class CommentBlocker(object): """ Pull out contiguous comment blocks. """ def __init__(self): # Start with a dummy. self.current_block = NonComment(0, 0) # All of the blocks seen so far. self.blocks = [] # The index mapping lines of code to their associated comment blocks. self.index = {} def process_file(self, file): """ Process a file object. """ for token in tokenize.generate_tokens(file.next): self.process_token(*token) self.make_index() def process_token(self, kind, string, start, end, line): """ Process a single token. """ if self.current_block.is_comment: if kind == tokenize.COMMENT: self.current_block.add(string, start, end, line) else: self.new_noncomment(start[0], end[0]) else: if kind == tokenize.COMMENT: self.new_comment(string, start, end, line) else: self.current_block.add(string, start, end, line) def new_noncomment(self, start_lineno, end_lineno): """ We are transitioning from a noncomment to a comment. """ block = NonComment(start_lineno, end_lineno) self.blocks.append(block) self.current_block = block def new_comment(self, string, start, end, line): """ Possibly add a new comment. Only adds a new comment if this comment is the only thing on the line. Otherwise, it extends the noncomment block. """ prefix = line[:start[1]] if prefix.strip(): # Oops! Trailing comment, not a comment block. self.current_block.add(string, start, end, line) else: # A comment block. block = Comment(start[0], end[0], string) self.blocks.append(block) self.current_block = block def make_index(self): """ Make the index mapping lines of actual code to their associated prefix comments. """ for prev, block in zip(self.blocks[:-1], self.blocks[1:]): if not block.is_comment: self.index[block.start_lineno] = prev def search_for_comment(self, lineno, default=None): """ Find the comment block just before the given line number. Returns None (or the specified default) if there is no such block. """ if not self.index: self.make_index() block = self.index.get(lineno, None) text = getattr(block, 'text', default) return text def strip_comment_marker(text): """ Strip # markers at the front of a block of comment text. """ lines = [] for line in text.splitlines(): lines.append(line.lstrip('#')) text = textwrap.dedent('\n'.join(lines)) return text def get_class_traits(klass): """ Yield all of the documentation for trait definitions on a class object. """ # FIXME: gracefully handle errors here or in the caller? source = inspect.getsource(klass) cb = CommentBlocker() cb.process_file(StringIO(source)) mod_ast = compiler.parse(source) class_ast = mod_ast.node.nodes[0] for node in class_ast.code.nodes: # FIXME: handle other kinds of assignments? if isinstance(node, compiler.ast.Assign): name = node.nodes[0].name rhs = unparse(node.expr).strip() doc = strip_comment_marker(cb.search_for_comment(node.lineno, default='')) yield name, rhs, doc PyTables-v.3.1.1/doc/sphinxext/compiler_unparse.py000066400000000000000000000602041231437614300222310ustar00rootroot00000000000000""" Turn compiler.ast structures back into executable python code. The unparse method takes a compiler.ast tree and transforms it back into valid python code. It is incomplete and currently only works for import statements, function calls, function definitions, assignments, and basic expressions. Inspired by python-2.5-svn/Demo/parser/unparse.py fixme: We may want to move to using _ast trees because the compiler for them is about 6 times faster than compiler.compile. """ import sys import cStringIO from compiler.ast import Const, Name, Tuple, Div, Mul, Sub, Add def unparse(ast, single_line_functions=False): s = cStringIO.StringIO() UnparseCompilerAst(ast, s, single_line_functions) return s.getvalue().lstrip() op_precedence = { 'compiler.ast.Power':3, 'compiler.ast.Mul':2, 'compiler.ast.Div':2, 'compiler.ast.Add':1, 'compiler.ast.Sub':1 } class UnparseCompilerAst: """ Methods in this class recursively traverse an AST and output source code for the abstract syntax; original formatting is disregarged. """ ######################################################################### # object interface. ######################################################################### def __init__(self, tree, file = sys.stdout, single_line_functions=False): """ Unparser(tree, file=sys.stdout) -> None. Print the source for tree to file. """ self.f = file self._single_func = single_line_functions self._do_indent = True self._indent = 0 self._dispatch(tree) self._write("\n") self.f.flush() ######################################################################### # Unparser private interface. ######################################################################### ### format, output, and dispatch methods ################################ def _fill(self, text = ""): "Indent a piece of text, according to the current indentation level" if self._do_indent: self._write("\n"+" "*self._indent + text) else: self._write(text) def _write(self, text): "Append a piece of text to the current line." self.f.write(text) def _enter(self): "Print ':', and increase the indentation." self._write(": ") self._indent += 1 def _leave(self): "Decrease the indentation level." self._indent -= 1 def _dispatch(self, tree): "_dispatcher function, _dispatching tree type T to method _T." if isinstance(tree, list): for t in tree: self._dispatch(t) return meth = getattr(self, "_"+tree.__class__.__name__) if tree.__class__.__name__ == 'NoneType' and not self._do_indent: return meth(tree) ######################################################################### # compiler.ast unparsing methods. # # There should be one method per concrete grammar type. They are # organized in alphabetical order. ######################################################################### def _Add(self, t): self.__binary_op(t, '+') def _And(self, t): self._write(" (") for i, node in enumerate(t.nodes): self._dispatch(node) if i != len(t.nodes)-1: self._write(") and (") self._write(")") def _AssAttr(self, t): """ Handle assigning an attribute of an object """ self._dispatch(t.expr) self._write('.'+t.attrname) def _Assign(self, t): """ Expression Assignment such as "a = 1". This only handles assignment in expressions. Keyword assignment is handled separately. """ self._fill() for target in t.nodes: self._dispatch(target) self._write(" = ") self._dispatch(t.expr) if not self._do_indent: self._write('; ') def _AssName(self, t): """ Name on left hand side of expression. Treat just like a name on the right side of an expression. """ self._Name(t) def _AssTuple(self, t): """ Tuple on left hand side of an expression. """ # _write each elements, separated by a comma. for element in t.nodes[:-1]: self._dispatch(element) self._write(", ") # Handle the last one without writing comma last_element = t.nodes[-1] self._dispatch(last_element) def _AugAssign(self, t): """ +=,-=,*=,/=,**=, etc. operations """ self._fill() self._dispatch(t.node) self._write(' '+t.op+' ') self._dispatch(t.expr) if not self._do_indent: self._write(';') def _Bitand(self, t): """ Bit and operation. """ for i, node in enumerate(t.nodes): self._write("(") self._dispatch(node) self._write(")") if i != len(t.nodes)-1: self._write(" & ") def _Bitor(self, t): """ Bit or operation """ for i, node in enumerate(t.nodes): self._write("(") self._dispatch(node) self._write(")") if i != len(t.nodes)-1: self._write(" | ") def _CallFunc(self, t): """ Function call. """ self._dispatch(t.node) self._write("(") comma = False for e in t.args: if comma: self._write(", ") else: comma = True self._dispatch(e) if t.star_args: if comma: self._write(", ") else: comma = True self._write("*") self._dispatch(t.star_args) if t.dstar_args: if comma: self._write(", ") else: comma = True self._write("**") self._dispatch(t.dstar_args) self._write(")") def _Compare(self, t): self._dispatch(t.expr) for op, expr in t.ops: self._write(" " + op + " ") self._dispatch(expr) def _Const(self, t): """ A constant value such as an integer value, 3, or a string, "hello". """ self._dispatch(t.value) def _Decorators(self, t): """ Handle function decorators (eg. @has_units) """ for node in t.nodes: self._dispatch(node) def _Dict(self, t): self._write("{") for i, (k, v) in enumerate(t.items): self._dispatch(k) self._write(": ") self._dispatch(v) if i < len(t.items)-1: self._write(", ") self._write("}") def _Discard(self, t): """ Node for when return value is ignored such as in "foo(a)". """ self._fill() self._dispatch(t.expr) def _Div(self, t): self.__binary_op(t, '/') def _Ellipsis(self, t): self._write("...") def _From(self, t): """ Handle "from xyz import foo, bar as baz". """ # fixme: Are From and ImportFrom handled differently? self._fill("from ") self._write(t.modname) self._write(" import ") for i, (name, asname) in enumerate(t.names): if i != 0: self._write(", ") self._write(name) if asname is not None: self._write(" as "+asname) def _Function(self, t): """ Handle function definitions """ if t.decorators is not None: self._fill("@") self._dispatch(t.decorators) self._fill("def "+t.name + "(") defaults = [None] * (len(t.argnames) - len(t.defaults)) + list(t.defaults) for i, arg in enumerate(zip(t.argnames, defaults)): self._write(arg[0]) if arg[1] is not None: self._write('=') self._dispatch(arg[1]) if i < len(t.argnames)-1: self._write(', ') self._write(")") if self._single_func: self._do_indent = False self._enter() self._dispatch(t.code) self._leave() self._do_indent = True def _Getattr(self, t): """ Handle getting an attribute of an object """ if isinstance(t.expr, (Div, Mul, Sub, Add)): self._write('(') self._dispatch(t.expr) self._write(')') else: self._dispatch(t.expr) self._write('.'+t.attrname) def _If(self, t): self._fill() for i, (compare, code) in enumerate(t.tests): if i == 0: self._write("if ") else: self._write("elif ") self._dispatch(compare) self._enter() self._fill() self._dispatch(code) self._leave() self._write("\n") if t.else_ is not None: self._write("else") self._enter() self._fill() self._dispatch(t.else_) self._leave() self._write("\n") def _IfExp(self, t): self._dispatch(t.then) self._write(" if ") self._dispatch(t.test) if t.else_ is not None: self._write(" else (") self._dispatch(t.else_) self._write(")") def _Import(self, t): """ Handle "import xyz.foo". """ self._fill("import ") for i, (name, asname) in enumerate(t.names): if i != 0: self._write(", ") self._write(name) if asname is not None: self._write(" as "+asname) def _Keyword(self, t): """ Keyword value assignment within function calls and definitions. """ self._write(t.name) self._write("=") self._dispatch(t.expr) def _List(self, t): self._write("[") for i, node in enumerate(t.nodes): self._dispatch(node) if i < len(t.nodes)-1: self._write(", ") self._write("]") def _Module(self, t): if t.doc is not None: self._dispatch(t.doc) self._dispatch(t.node) def _Mul(self, t): self.__binary_op(t, '*') def _Name(self, t): self._write(t.name) def _NoneType(self, t): self._write("None") def _Not(self, t): self._write('not (') self._dispatch(t.expr) self._write(')') def _Or(self, t): self._write(" (") for i, node in enumerate(t.nodes): self._dispatch(node) if i != len(t.nodes)-1: self._write(") or (") self._write(")") def _Pass(self, t): self._write("pass\n") def _Printnl(self, t): self._fill("print ") if t.dest: self._write(">> ") self._dispatch(t.dest) self._write(", ") comma = False for node in t.nodes: if comma: self._write(', ') else: comma = True self._dispatch(node) def _Power(self, t): self.__binary_op(t, '**') def _Return(self, t): self._fill("return ") if t.value: if isinstance(t.value, Tuple): text = ', '.join([ name.name for name in t.value.asList() ]) self._write(text) else: self._dispatch(t.value) if not self._do_indent: self._write('; ') def _Slice(self, t): self._dispatch(t.expr) self._write("[") if t.lower: self._dispatch(t.lower) self._write(":") if t.upper: self._dispatch(t.upper) #if t.step: # self._write(":") # self._dispatch(t.step) self._write("]") def _Sliceobj(self, t): for i, node in enumerate(t.nodes): if i != 0: self._write(":") if not (isinstance(node, Const) and node.value is None): self._dispatch(node) def _Stmt(self, tree): for node in tree.nodes: self._dispatch(node) def _Sub(self, t): self.__binary_op(t, '-') def _Subscript(self, t): self._dispatch(t.expr) self._write("[") for i, value in enumerate(t.subs): if i != 0: self._write(",") self._dispatch(value) self._write("]") def _TryExcept(self, t): self._fill("try") self._enter() self._dispatch(t.body) self._leave() for handler in t.handlers: self._fill('except ') self._dispatch(handler[0]) if handler[1] is not None: self._write(', ') self._dispatch(handler[1]) self._enter() self._dispatch(handler[2]) self._leave() if t.else_: self._fill("else") self._enter() self._dispatch(t.else_) self._leave() def _Tuple(self, t): if not t.nodes: # Empty tuple. self._write("()") else: self._write("(") # _write each elements, separated by a comma. for element in t.nodes[:-1]: self._dispatch(element) self._write(", ") # Handle the last one without writing comma last_element = t.nodes[-1] self._dispatch(last_element) self._write(")") def _UnaryAdd(self, t): self._write("+") self._dispatch(t.expr) def _UnarySub(self, t): self._write("-") self._dispatch(t.expr) def _With(self, t): self._fill('with ') self._dispatch(t.expr) if t.vars: self._write(' as ') self._dispatch(t.vars.name) self._enter() self._dispatch(t.body) self._leave() self._write('\n') def _int(self, t): self._write(repr(t)) def __binary_op(self, t, symbol): # Check if parenthesis are needed on left side and then dispatch has_paren = False left_class = str(t.left.__class__) if (left_class in op_precedence.keys() and op_precedence[left_class] < op_precedence[str(t.__class__)]): has_paren = True if has_paren: self._write('(') self._dispatch(t.left) if has_paren: self._write(')') # Write the appropriate symbol for operator self._write(symbol) # Check if parenthesis are needed on the right side and then dispatch has_paren = False right_class = str(t.right.__class__) if (right_class in op_precedence.keys() and op_precedence[right_class] < op_precedence[str(t.__class__)]): has_paren = True if has_paren: self._write('(') self._dispatch(t.right) if has_paren: self._write(')') def _float(self, t): # if t is 0.1, str(t)->'0.1' while repr(t)->'0.1000000000001' # We prefer str here. self._write(str(t)) def _str(self, t): self._write(repr(t)) def _tuple(self, t): self._write(str(t)) ######################################################################### # These are the methods from the _ast modules unparse. # # As our needs to handle more advanced code increase, we may want to # modify some of the methods below so that they work for compiler.ast. ######################################################################### # # stmt # def _Expr(self, tree): # self._fill() # self._dispatch(tree.value) # # def _Import(self, t): # self._fill("import ") # first = True # for a in t.names: # if first: # first = False # else: # self._write(", ") # self._write(a.name) # if a.asname: # self._write(" as "+a.asname) # ## def _ImportFrom(self, t): ## self._fill("from ") ## self._write(t.module) ## self._write(" import ") ## for i, a in enumerate(t.names): ## if i == 0: ## self._write(", ") ## self._write(a.name) ## if a.asname: ## self._write(" as "+a.asname) ## # XXX(jpe) what is level for? ## # # def _Break(self, t): # self._fill("break") # # def _Continue(self, t): # self._fill("continue") # # def _Delete(self, t): # self._fill("del ") # self._dispatch(t.targets) # # def _Assert(self, t): # self._fill("assert ") # self._dispatch(t.test) # if t.msg: # self._write(", ") # self._dispatch(t.msg) # # def _Exec(self, t): # self._fill("exec ") # self._dispatch(t.body) # if t.globals: # self._write(" in ") # self._dispatch(t.globals) # if t.locals: # self._write(", ") # self._dispatch(t.locals) # # def _Print(self, t): # self._fill("print ") # do_comma = False # if t.dest: # self._write(">>") # self._dispatch(t.dest) # do_comma = True # for e in t.values: # if do_comma:self._write(", ") # else:do_comma=True # self._dispatch(e) # if not t.nl: # self._write(",") # # def _Global(self, t): # self._fill("global") # for i, n in enumerate(t.names): # if i != 0: # self._write(",") # self._write(" " + n) # # def _Yield(self, t): # self._fill("yield") # if t.value: # self._write(" (") # self._dispatch(t.value) # self._write(")") # # def _Raise(self, t): # self._fill('raise ') # if t.type: # self._dispatch(t.type) # if t.inst: # self._write(", ") # self._dispatch(t.inst) # if t.tback: # self._write(", ") # self._dispatch(t.tback) # # # def _TryFinally(self, t): # self._fill("try") # self._enter() # self._dispatch(t.body) # self._leave() # # self._fill("finally") # self._enter() # self._dispatch(t.finalbody) # self._leave() # # def _excepthandler(self, t): # self._fill("except ") # if t.type: # self._dispatch(t.type) # if t.name: # self._write(", ") # self._dispatch(t.name) # self._enter() # self._dispatch(t.body) # self._leave() # # def _ClassDef(self, t): # self._write("\n") # self._fill("class "+t.name) # if t.bases: # self._write("(") # for a in t.bases: # self._dispatch(a) # self._write(", ") # self._write(")") # self._enter() # self._dispatch(t.body) # self._leave() # # def _FunctionDef(self, t): # self._write("\n") # for deco in t.decorators: # self._fill("@") # self._dispatch(deco) # self._fill("def "+t.name + "(") # self._dispatch(t.args) # self._write(")") # self._enter() # self._dispatch(t.body) # self._leave() # # def _For(self, t): # self._fill("for ") # self._dispatch(t.target) # self._write(" in ") # self._dispatch(t.iter) # self._enter() # self._dispatch(t.body) # self._leave() # if t.orelse: # self._fill("else") # self._enter() # self._dispatch(t.orelse) # self._leave # # def _While(self, t): # self._fill("while ") # self._dispatch(t.test) # self._enter() # self._dispatch(t.body) # self._leave() # if t.orelse: # self._fill("else") # self._enter() # self._dispatch(t.orelse) # self._leave # # # expr # def _Str(self, tree): # self._write(repr(tree.s)) ## # def _Repr(self, t): # self._write("`") # self._dispatch(t.value) # self._write("`") # # def _Num(self, t): # self._write(repr(t.n)) # # def _ListComp(self, t): # self._write("[") # self._dispatch(t.elt) # for gen in t.generators: # self._dispatch(gen) # self._write("]") # # def _GeneratorExp(self, t): # self._write("(") # self._dispatch(t.elt) # for gen in t.generators: # self._dispatch(gen) # self._write(")") # # def _comprehension(self, t): # self._write(" for ") # self._dispatch(t.target) # self._write(" in ") # self._dispatch(t.iter) # for if_clause in t.ifs: # self._write(" if ") # self._dispatch(if_clause) # # def _IfExp(self, t): # self._dispatch(t.body) # self._write(" if ") # self._dispatch(t.test) # if t.orelse: # self._write(" else ") # self._dispatch(t.orelse) # # unop = {"Invert":"~", "Not": "not", "UAdd":"+", "USub":"-"} # def _UnaryOp(self, t): # self._write(self.unop[t.op.__class__.__name__]) # self._write("(") # self._dispatch(t.operand) # self._write(")") # # binop = { "Add":"+", "Sub":"-", "Mult":"*", "Div":"/", "Mod":"%", # "LShift":">>", "RShift":"<<", "BitOr":"|", "BitXor":"^", "BitAnd":"&", # "FloorDiv":"//", "Pow": "**"} # def _BinOp(self, t): # self._write("(") # self._dispatch(t.left) # self._write(")" + self.binop[t.op.__class__.__name__] + "(") # self._dispatch(t.right) # self._write(")") # # boolops = {_ast.And: 'and', _ast.Or: 'or'} # def _BoolOp(self, t): # self._write("(") # self._dispatch(t.values[0]) # for v in t.values[1:]: # self._write(" %s " % self.boolops[t.op.__class__]) # self._dispatch(v) # self._write(")") # # def _Attribute(self,t): # self._dispatch(t.value) # self._write(".") # self._write(t.attr) # ## def _Call(self, t): ## self._dispatch(t.func) ## self._write("(") ## comma = False ## for e in t.args: ## if comma: self._write(", ") ## else: comma = True ## self._dispatch(e) ## for e in t.keywords: ## if comma: self._write(", ") ## else: comma = True ## self._dispatch(e) ## if t.starargs: ## if comma: self._write(", ") ## else: comma = True ## self._write("*") ## self._dispatch(t.starargs) ## if t.kwargs: ## if comma: self._write(", ") ## else: comma = True ## self._write("**") ## self._dispatch(t.kwargs) ## self._write(")") # # # slice # def _Index(self, t): # self._dispatch(t.value) # # def _ExtSlice(self, t): # for i, d in enumerate(t.dims): # if i != 0: # self._write(': ') # self._dispatch(d) # # # others # def _arguments(self, t): # first = True # nonDef = len(t.args)-len(t.defaults) # for a in t.args[0:nonDef]: # if first:first = False # else: self._write(", ") # self._dispatch(a) # for a,d in zip(t.args[nonDef:], t.defaults): # if first:first = False # else: self._write(", ") # self._dispatch(a), # self._write("=") # self._dispatch(d) # if t.vararg: # if first:first = False # else: self._write(", ") # self._write("*"+t.vararg) # if t.kwarg: # if first:first = False # else: self._write(", ") # self._write("**"+t.kwarg) # ## def _keyword(self, t): ## self._write(t.arg) ## self._write("=") ## self._dispatch(t.value) # # def _Lambda(self, t): # self._write("lambda ") # self._dispatch(t.args) # self._write(": ") # self._dispatch(t.body) PyTables-v.3.1.1/doc/sphinxext/docscrape.py000066400000000000000000000357321231437614300206350ustar00rootroot00000000000000"""Extract reference documentation from the NumPy source tree. """ import inspect import textwrap import re import pydoc from StringIO import StringIO from warnings import warn class Reader(object): """A line-based string reader. """ def __init__(self, data): """ Parameters ---------- data : str String with lines separated by '\n'. """ if isinstance(data, list): self._str = data else: self._str = data.split('\n') # store string as list of lines self.reset() def __getitem__(self, n): return self._str[n] def reset(self): self._l = 0 # current line nr def read(self): if not self.eof(): out = self[self._l] self._l += 1 return out else: return '' def seek_next_non_empty_line(self): for l in self[self._l:]: if l.strip(): break else: self._l += 1 def eof(self): return self._l >= len(self._str) def read_to_condition(self, condition_func): start = self._l for line in self[start:]: if condition_func(line): return self[start:self._l] self._l += 1 if self.eof(): return self[start:self._l+1] return [] def read_to_next_empty_line(self): self.seek_next_non_empty_line() def is_empty(line): return not line.strip() return self.read_to_condition(is_empty) def read_to_next_unindented_line(self): def is_unindented(line): return (line.strip() and (len(line.lstrip()) == len(line))) return self.read_to_condition(is_unindented) def peek(self,n=0): if self._l + n < len(self._str): return self[self._l + n] else: return '' def is_empty(self): return not ''.join(self._str).strip() class NumpyDocString(object): def __init__(self, docstring, config={}): docstring = textwrap.dedent(docstring).split('\n') self._doc = Reader(docstring) self._parsed_data = { 'Signature': '', 'Summary': [''], 'Extended Summary': [], 'Parameters': [], 'Returns': [], 'Raises': [], 'Warns': [], 'Other Parameters': [], 'Attributes': [], 'Methods': [], 'See Also': [], 'Notes': [], 'Warnings': [], 'References': '', 'Examples': '', 'index': {} } self._parse() def __getitem__(self, key): return self._parsed_data[key] def __setitem__(self, key, val): if not self._parsed_data.has_key(key): warn("Unknown section %s" % key) else: self._parsed_data[key] = val def _is_at_section(self): self._doc.seek_next_non_empty_line() if self._doc.eof(): return False l1 = self._doc.peek().strip() # e.g. Parameters if l1.startswith('.. index::'): return True l2 = self._doc.peek(1).strip() # ---------- or ========== return l2.startswith('-'*len(l1)) or l2.startswith('='*len(l1)) def _strip(self, doc): i = 0 j = 0 for i, line in enumerate(doc): if line.strip(): break for j, line in enumerate(doc[::-1]): if line.strip(): break return doc[i:len(doc)-j] def _read_to_next_section(self): section = self._doc.read_to_next_empty_line() while not self._is_at_section() and not self._doc.eof(): if not self._doc.peek(-1).strip(): # previous line was empty section += [''] section += self._doc.read_to_next_empty_line() return section def _read_sections(self): while not self._doc.eof(): data = self._read_to_next_section() name = data[0].strip() if name.startswith('..'): # index section yield name, data[1:] elif len(data) < 2: yield StopIteration else: yield name, self._strip(data[2:]) def _parse_param_list(self, content): r = Reader(content) params = [] while not r.eof(): header = r.read().strip() if ' : ' in header: arg_name, arg_type = header.split(' : ')[:2] else: arg_name, arg_type = header, '' desc = r.read_to_next_unindented_line() desc = dedent_lines(desc) params.append((arg_name, arg_type, desc)) return params _name_rgx = re.compile(r"^\s*(:(?P\w+):`(?P[a-zA-Z0-9_.-]+)`|" r" (?P[a-zA-Z0-9_.-]+))\s*", re.X) def _parse_see_also(self, content): """ func_name : Descriptive text continued text another_func_name : Descriptive text func_name1, func_name2, :meth:`func_name`, func_name3 """ items = [] def parse_item_name(text): """Match ':role:`name`' or 'name'""" m = self._name_rgx.match(text) if m: g = m.groups() if g[1] is None: return g[3], None else: return g[2], g[1] raise ValueError("%s is not a item name" % text) def push_item(name, rest): if not name: return name, role = parse_item_name(name) items.append((name, list(rest), role)) del rest[:] current_func = None rest = [] for line in content: if not line.strip(): continue m = self._name_rgx.match(line) if m and line[m.end():].strip().startswith(':'): push_item(current_func, rest) current_func, line = line[:m.end()], line[m.end():] rest = [line.split(':', 1)[1].strip()] if not rest[0]: rest = [] elif not line.startswith(' '): push_item(current_func, rest) current_func = None if ',' in line: for func in line.split(','): if func.strip(): push_item(func, []) elif line.strip(): current_func = line elif current_func is not None: rest.append(line.strip()) push_item(current_func, rest) return items def _parse_index(self, section, content): """ .. index: default :refguide: something, else, and more """ def strip_each_in(lst): return [s.strip() for s in lst] out = {} section = section.split('::') if len(section) > 1: out['default'] = strip_each_in(section[1].split(','))[0] for line in content: line = line.split(':') if len(line) > 2: out[line[1]] = strip_each_in(line[2].split(',')) return out def _parse_summary(self): """Grab signature (if given) and summary""" if self._is_at_section(): return summary = self._doc.read_to_next_empty_line() summary_str = " ".join([s.strip() for s in summary]).strip() if re.compile('^([\w., ]+=)?\s*[\w\.]+\(.*\)$').match(summary_str): self['Signature'] = summary_str if not self._is_at_section(): self['Summary'] = self._doc.read_to_next_empty_line() else: self['Summary'] = summary if not self._is_at_section(): self['Extended Summary'] = self._read_to_next_section() def _parse(self): self._doc.reset() self._parse_summary() for (section, content) in self._read_sections(): if not section.startswith('..'): section = ' '.join([s.capitalize() for s in section.split(' ')]) if section in ('Parameters', 'Returns', 'Raises', 'Warns', 'Other Parameters', 'Attributes', 'Methods'): self[section] = self._parse_param_list(content) elif section.startswith('.. index::'): self['index'] = self._parse_index(section, content) elif section == 'See Also': self['See Also'] = self._parse_see_also(content) else: self[section] = content # string conversion routines def _str_header(self, name, symbol='-'): return [name, len(name)*symbol] def _str_indent(self, doc, indent=4): out = [] for line in doc: out += [' '*indent + line] return out def _str_signature(self): if self['Signature']: return [self['Signature'].replace('*', '\*')] + [''] else: return [''] def _str_summary(self): if self['Summary']: return self['Summary'] + [''] else: return [] def _str_extended_summary(self): if self['Extended Summary']: return self['Extended Summary'] + [''] else: return [] def _str_param_list(self, name): out = [] if self[name]: out += self._str_header(name) for param, param_type, desc in self[name]: out += ['%s : %s' % (param, param_type)] out += self._str_indent(desc) out += [''] return out def _str_section(self, name): out = [] if self[name]: out += self._str_header(name) out += self[name] out += [''] return out def _str_see_also(self, func_role): if not self['See Also']: return [] out = [] out += self._str_header("See Also") last_had_desc = True for func, desc, role in self['See Also']: if role: link = ':%s:`%s`' % (role, func) elif func_role: link = ':%s:`%s`' % (func_role, func) else: link = "`%s`_" % func if desc or last_had_desc: out += [''] out += [link] else: out[-1] += ", %s" % link if desc: out += self._str_indent([' '.join(desc)]) last_had_desc = True else: last_had_desc = False out += [''] return out def _str_index(self): idx = self['index'] out = [] out += ['.. index:: %s' % idx.get('default', '')] for section, references in idx.iteritems(): if section == 'default': continue out += [' :%s: %s' % (section, ', '.join(references))] return out def __str__(self, func_role=''): out = [] out += self._str_signature() out += self._str_summary() out += self._str_extended_summary() for param_list in ('Parameters', 'Returns', 'Other Parameters', 'Raises', 'Warns'): out += self._str_param_list(param_list) out += self._str_section('Warnings') out += self._str_see_also(func_role) for s in ('Notes', 'References', 'Examples'): out += self._str_section(s) for param_list in ('Attributes', 'Methods'): out += self._str_param_list(param_list) out += self._str_index() return '\n'.join(out) def indent(str,indent=4): indent_str = ' '*indent if str is None: return indent_str lines = str.split('\n') return '\n'.join(indent_str + l for l in lines) def dedent_lines(lines): """Deindent a list of lines maximally""" return textwrap.dedent("\n".join(lines)).split("\n") def header(text, style='-'): return text + '\n' + style*len(text) + '\n' class FunctionDoc(NumpyDocString): def __init__(self, func, role='func', doc=None, config={}): self._f = func self._role = role # e.g. "func" or "meth" if doc is None: if func is None: raise ValueError("No function or docstring given") doc = inspect.getdoc(func) or '' NumpyDocString.__init__(self, doc) if not self['Signature'] and func is not None: func, func_name = self.get_func() try: # try to read signature argspec = inspect.getargspec(func) argspec = inspect.formatargspec(*argspec) argspec = argspec.replace('*', '\*') signature = '%s%s' % (func_name, argspec) except TypeError, e: signature = '%s()' % func_name self['Signature'] = signature def get_func(self): func_name = getattr(self._f, '__name__', self.__class__.__name__) if inspect.isclass(self._f): func = getattr(self._f, '__call__', self._f.__init__) else: func = self._f return func, func_name def __str__(self): out = '' func, func_name = self.get_func() signature = self['Signature'].replace('*', '\*') roles = {'func': 'function', 'meth': 'method'} if self._role: if not roles.has_key(self._role): print "Warning: invalid role %s" % self._role out += '.. %s:: %s\n \n\n' % (roles.get(self._role, ''), func_name) out += super(FunctionDoc, self).__str__(func_role=self._role) return out class ClassDoc(NumpyDocString): def __init__(self, cls, doc=None, modulename='', func_doc=FunctionDoc, config={}): if not inspect.isclass(cls) and cls is not None: raise ValueError("Expected a class or None, but got %r" % cls) self._cls = cls if modulename and not modulename.endswith('.'): modulename += '.' self._mod = modulename if doc is None: if cls is None: raise ValueError("No class or documentation string given") doc = pydoc.getdoc(cls) NumpyDocString.__init__(self, doc) if config.get('show_class_members', True): if not self['Methods']: self['Methods'] = [(name, '', '') for name in sorted(self.methods)] if not self['Attributes']: self['Attributes'] = [(name, '', '') for name in sorted(self.properties)] @property def methods(self): if self._cls is None: return [] return [name for name, func in inspect.getmembers(self._cls) if not name.startswith('_') and callable(func)] @property def properties(self): if self._cls is None: return [] return [name for name, func in inspect.getmembers(self._cls) if not name.startswith('_') and func is None] PyTables-v.3.1.1/doc/sphinxext/docscrape_sphinx.py000066400000000000000000000171301231437614300222160ustar00rootroot00000000000000import re, inspect, textwrap, pydoc import sphinx from docscrape import NumpyDocString, FunctionDoc, ClassDoc class SphinxDocString(NumpyDocString): def __init__(self, docstring, config={}): self.use_plots = config.get('use_plots', False) NumpyDocString.__init__(self, docstring, config=config) # string conversion routines def _str_header(self, name, symbol='`'): return ['.. rubric:: ' + name, ''] def _str_field_list(self, name): return [':' + name + ':'] def _str_indent(self, doc, indent=4): out = [] for line in doc: out += [' '*indent + line] return out def _str_signature(self): return [''] if self['Signature']: return ['``%s``' % self['Signature']] + [''] else: return [''] def _str_summary(self): return self['Summary'] + [''] def _str_extended_summary(self): return self['Extended Summary'] + [''] def _str_param_list(self, name): out = [] if self[name]: out += self._str_field_list(name) out += [''] for param, param_type, desc in self[name]: out += self._str_indent(['**%s** : %s' % (param.strip(), param_type)]) out += [''] out += self._str_indent(desc, 8) out += [''] return out @property def _obj(self): if hasattr(self, '_cls'): return self._cls elif hasattr(self, '_f'): return self._f return None def _str_member_list(self, name): """ Generate a member listing, autosummary:: table where possible, and a table where not. """ out = [] if self[name]: out += ['.. rubric:: %s' % name, ''] prefix = getattr(self, '_name', '') if prefix: prefix = '~%s.' % prefix autosum = [] others = [] for param, param_type, desc in self[name]: param = param.strip() if not self._obj or hasattr(self._obj, param): autosum += [" %s%s" % (prefix, param)] else: others.append((param, param_type, desc)) if autosum: out += ['.. autosummary::', ' :toctree:', ''] out += autosum if others: maxlen_0 = max([len(x[0]) for x in others]) maxlen_1 = max([len(x[1]) for x in others]) hdr = "="*maxlen_0 + " " + "="*maxlen_1 + " " + "="*10 fmt = '%%%ds %%%ds ' % (maxlen_0, maxlen_1) n_indent = maxlen_0 + maxlen_1 + 4 out += [hdr] for param, param_type, desc in others: out += [fmt % (param.strip(), param_type)] out += self._str_indent(desc, n_indent) out += [hdr] out += [''] return out def _str_section(self, name): out = [] if self[name]: out += self._str_header(name) out += [''] content = textwrap.dedent("\n".join(self[name])).split("\n") out += content out += [''] return out def _str_see_also(self, func_role): out = [] if self['See Also']: see_also = super(SphinxDocString, self)._str_see_also(func_role) out = ['.. seealso::', ''] out += self._str_indent(see_also[2:]) return out def _str_warnings(self): out = [] if self['Warnings']: out = ['.. warning::', ''] out += self._str_indent(self['Warnings']) return out def _str_index(self): idx = self['index'] out = [] if len(idx) == 0: return out out += ['.. index:: %s' % idx.get('default', '')] for section, references in idx.iteritems(): if section == 'default': continue elif section == 'refguide': out += [' single: %s' % (', '.join(references))] else: out += [' %s: %s' % (section, ','.join(references))] return out def _str_references(self): out = [] if self['References']: out += self._str_header('References') if isinstance(self['References'], str): self['References'] = [self['References']] out.extend(self['References']) out += [''] # Latex collects all references to a separate bibliography, # so we need to insert links to it if sphinx.__version__ >= "0.6": out += ['.. only:: latex', ''] else: out += ['.. latexonly::', ''] items = [] for line in self['References']: m = re.match(r'.. \[([a-z0-9._-]+)\]', line, re.I) if m: items.append(m.group(1)) out += [' ' + ", ".join(["[%s]_" % item for item in items]), ''] return out def _str_examples(self): examples_str = "\n".join(self['Examples']) if (self.use_plots and 'import matplotlib' in examples_str and 'plot::' not in examples_str): out = [] out += self._str_header('Examples') out += ['.. plot::', ''] out += self._str_indent(self['Examples']) out += [''] return out else: return self._str_section('Examples') def __str__(self, indent=0, func_role="obj"): out = [] out += self._str_signature() out += self._str_index() + [''] out += self._str_summary() out += self._str_extended_summary() for param_list in ('Parameters', 'Returns', 'Other Parameters', 'Raises', 'Warns'): out += self._str_param_list(param_list) out += self._str_warnings() out += self._str_see_also(func_role) out += self._str_section('Notes') out += self._str_references() out += self._str_examples() #for param_list in ('Attributes', 'Methods'): # out += self._str_member_list(param_list) out = self._str_indent(out, indent) return '\n'.join(out) class SphinxFunctionDoc(SphinxDocString, FunctionDoc): def __init__(self, obj, doc=None, config={}): self.use_plots = config.get('use_plots', False) FunctionDoc.__init__(self, obj, doc=doc, config=config) class SphinxClassDoc(SphinxDocString, ClassDoc): def __init__(self, obj, doc=None, func_doc=None, config={}): self.use_plots = config.get('use_plots', False) ClassDoc.__init__(self, obj, doc=doc, func_doc=None, config=config) class SphinxObjDoc(SphinxDocString): def __init__(self, obj, doc=None, config={}): self._f = obj SphinxDocString.__init__(self, doc, config=config) def get_doc_object(obj, what=None, doc=None, config={}): if what is None: if inspect.isclass(obj): what = 'class' elif inspect.ismodule(obj): what = 'module' elif callable(obj): what = 'function' else: what = 'object' if what == 'class': return SphinxClassDoc(obj, func_doc=SphinxFunctionDoc, doc=doc, config=config) elif what in ('function', 'method'): return SphinxFunctionDoc(obj, doc=doc, config=config) else: if doc is None: doc = pydoc.getdoc(obj) return SphinxObjDoc(obj, doc, config=config) PyTables-v.3.1.1/doc/sphinxext/inheritance_diagram.py000066400000000000000000000325201231437614300226370ustar00rootroot00000000000000""" Defines a docutils directive for inserting inheritance diagrams. Provide the directive with one or more classes or modules (separated by whitespace). For modules, all of the classes in that module will be used. Example:: Given the following classes: class A: pass class B(A): pass class C(A): pass class D(B, C): pass class E(B): pass .. inheritance-diagram: D E Produces a graph like the following: A / \ B C / \ / E D The graph is inserted as a PNG+image map into HTML and a PDF in LaTeX. """ import inspect import os import re import subprocess try: from hashlib import md5 except ImportError: from md5 import md5 from docutils.nodes import Body, Element from docutils.parsers.rst import directives from sphinx.roles import xfileref_role def my_import(name): """Module importer - taken from the python documentation. This function allows importing names with dots in them.""" mod = __import__(name) components = name.split('.') for comp in components[1:]: mod = getattr(mod, comp) return mod class DotException(Exception): pass class InheritanceGraph(object): """ Given a list of classes, determines the set of classes that they inherit from all the way to the root "object", and then is able to generate a graphviz dot graph from them. """ def __init__(self, class_names, show_builtins=False): """ *class_names* is a list of child classes to show bases from. If *show_builtins* is True, then Python builtins will be shown in the graph. """ self.class_names = class_names self.classes = self._import_classes(class_names) self.all_classes = self._all_classes(self.classes) if len(self.all_classes) == 0: raise ValueError("No classes found for inheritance diagram") self.show_builtins = show_builtins py_sig_re = re.compile(r'''^([\w.]*\.)? # class names (\w+) \s* $ # optionally arguments ''', re.VERBOSE) def _import_class_or_module(self, name): """ Import a class using its fully-qualified *name*. """ try: path, base = self.py_sig_re.match(name).groups() except: raise ValueError( "Invalid class or module '%s' specified for inheritance diagram" % name) fullname = (path or '') + base path = (path and path.rstrip('.')) if not path: path = base try: module = __import__(path, None, None, []) # We must do an import of the fully qualified name. Otherwise if a # subpackage 'a.b' is requested where 'import a' does NOT provide # 'a.b' automatically, then 'a.b' will not be found below. This # second call will force the equivalent of 'import a.b' to happen # after the top-level import above. my_import(fullname) except ImportError: raise ValueError( "Could not import class or module '%s' specified for inheritance diagram" % name) try: todoc = module for comp in fullname.split('.')[1:]: todoc = getattr(todoc, comp) except AttributeError: raise ValueError( "Could not find class or module '%s' specified for inheritance diagram" % name) # If a class, just return it if inspect.isclass(todoc): return [todoc] elif inspect.ismodule(todoc): classes = [] for cls in todoc.__dict__.values(): if inspect.isclass(cls) and cls.__module__ == todoc.__name__: classes.append(cls) return classes raise ValueError( "'%s' does not resolve to a class or module" % name) def _import_classes(self, class_names): """ Import a list of classes. """ classes = [] for name in class_names: classes.extend(self._import_class_or_module(name)) return classes def _all_classes(self, classes): """ Return a list of all classes that are ancestors of *classes*. """ all_classes = {} def recurse(cls): all_classes[cls] = None for c in cls.__bases__: if c not in all_classes: recurse(c) for cls in classes: recurse(cls) return all_classes.keys() def class_name(self, cls, parts=0): """ Given a class object, return a fully-qualified name. This works for things I've tested in matplotlib so far, but may not be completely general. """ module = cls.__module__ if module == '__builtin__': fullname = cls.__name__ else: fullname = "%s.%s" % (module, cls.__name__) if parts == 0: return fullname name_parts = fullname.split('.') return '.'.join(name_parts[-parts:]) def get_all_class_names(self): """ Get all of the class names involved in the graph. """ return [self.class_name(x) for x in self.all_classes] # These are the default options for graphviz default_graph_options = { "rankdir": "LR", "size": '"8.0, 12.0"' } default_node_options = { "shape": "box", "fontsize": 10, "height": 0.25, "fontname": "Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans", "style": '"setlinewidth(0.5)"' } default_edge_options = { "arrowsize": 0.5, "style": '"setlinewidth(0.5)"' } def _format_node_options(self, options): return ','.join(["%s=%s" % x for x in options.items()]) def _format_graph_options(self, options): return ''.join(["%s=%s;\n" % x for x in options.items()]) def generate_dot(self, fd, name, parts=0, urls={}, graph_options={}, node_options={}, edge_options={}): """ Generate a graphviz dot graph from the classes that were passed in to __init__. *fd* is a Python file-like object to write to. *name* is the name of the graph *urls* is a dictionary mapping class names to http urls *graph_options*, *node_options*, *edge_options* are dictionaries containing key/value pairs to pass on as graphviz properties. """ g_options = self.default_graph_options.copy() g_options.update(graph_options) n_options = self.default_node_options.copy() n_options.update(node_options) e_options = self.default_edge_options.copy() e_options.update(edge_options) fd.write('digraph %s {\n' % name) fd.write(self._format_graph_options(g_options)) for cls in self.all_classes: if not self.show_builtins and cls in __builtins__.values(): continue name = self.class_name(cls, parts) # Write the node this_node_options = n_options.copy() url = urls.get(self.class_name(cls)) if url is not None: this_node_options['URL'] = '"%s"' % url fd.write(' "%s" [%s];\n' % (name, self._format_node_options(this_node_options))) # Write the edges for base in cls.__bases__: if not self.show_builtins and base in __builtins__.values(): continue base_name = self.class_name(base, parts) fd.write(' "%s" -> "%s" [%s];\n' % (base_name, name, self._format_node_options(e_options))) fd.write('}\n') def run_dot(self, args, name, parts=0, urls={}, graph_options={}, node_options={}, edge_options={}): """ Run graphviz 'dot' over this graph, returning whatever 'dot' writes to stdout. *args* will be passed along as commandline arguments. *name* is the name of the graph *urls* is a dictionary mapping class names to http urls Raises DotException for any of the many os and installation-related errors that may occur. """ try: dot = subprocess.Popen(['dot'] + list(args), stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True) except OSError: raise DotException("Could not execute 'dot'. Are you sure you have 'graphviz' installed?") except ValueError: raise DotException("'dot' called with invalid arguments") except: raise DotException("Unexpected error calling 'dot'") self.generate_dot(dot.stdin, name, parts, urls, graph_options, node_options, edge_options) dot.stdin.close() result = dot.stdout.read() returncode = dot.wait() if returncode != 0: raise DotException("'dot' returned the errorcode %d" % returncode) return result class inheritance_diagram(Body, Element): """ A docutils node to use as a placeholder for the inheritance diagram. """ pass def inheritance_diagram_directive(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine): """ Run when the inheritance_diagram directive is first encountered. """ node = inheritance_diagram() class_names = arguments # Create a graph starting with the list of classes graph = InheritanceGraph(class_names) # Create xref nodes for each target of the graph's image map and # add them to the doc tree so that Sphinx can resolve the # references to real URLs later. These nodes will eventually be # removed from the doctree after we're done with them. for name in graph.get_all_class_names(): refnodes, x = xfileref_role( 'class', ':class:`%s`' % name, name, 0, state) node.extend(refnodes) # Store the graph object so we can use it to generate the # dot file later node['graph'] = graph # Store the original content for use as a hash node['parts'] = options.get('parts', 0) node['content'] = " ".join(class_names) return [node] def get_graph_hash(node): return md5(node['content'] + str(node['parts'])).hexdigest()[-10:] def html_output_graph(self, node): """ Output the graph for HTML. This will insert a PNG with clickable image map. """ graph = node['graph'] parts = node['parts'] graph_hash = get_graph_hash(node) name = "inheritance%s" % graph_hash path = '_images' dest_path = os.path.join(setup.app.builder.outdir, path) if not os.path.exists(dest_path): os.makedirs(dest_path) png_path = os.path.join(dest_path, name + ".png") path = setup.app.builder.imgpath # Create a mapping from fully-qualified class names to URLs. urls = {} for child in node: if child.get('refuri') is not None: urls[child['reftitle']] = child.get('refuri') elif child.get('refid') is not None: urls[child['reftitle']] = '#' + child.get('refid') # These arguments to dot will save a PNG file to disk and write # an HTML image map to stdout. image_map = graph.run_dot(['-Tpng', '-o%s' % png_path, '-Tcmapx'], name, parts, urls) return ('%s' % (path, name, name, image_map)) def latex_output_graph(self, node): """ Output the graph for LaTeX. This will insert a PDF. """ graph = node['graph'] parts = node['parts'] graph_hash = get_graph_hash(node) name = "inheritance%s" % graph_hash dest_path = os.path.abspath(os.path.join(setup.app.builder.outdir, '_images')) if not os.path.exists(dest_path): os.makedirs(dest_path) pdf_path = os.path.abspath(os.path.join(dest_path, name + ".pdf")) graph.run_dot(['-Tpdf', '-o%s' % pdf_path], name, parts, graph_options={'size': '"6.0,6.0"'}) return '\n\\includegraphics{%s}\n\n' % pdf_path def visit_inheritance_diagram(inner_func): """ This is just a wrapper around html/latex_output_graph to make it easier to handle errors and insert warnings. """ def visitor(self, node): try: content = inner_func(self, node) except DotException, e: # Insert the exception as a warning in the document warning = self.document.reporter.warning(str(e), line=node.line) warning.parent = node node.children = [warning] else: source = self.document.attributes['source'] self.body.append(content) node.children = [] return visitor def do_nothing(self, node): pass def setup(app): setup.app = app setup.confdir = app.confdir app.add_node( inheritance_diagram, latex=(visit_inheritance_diagram(latex_output_graph), do_nothing), html=(visit_inheritance_diagram(html_output_graph), do_nothing)) app.add_directive( 'inheritance-diagram', inheritance_diagram_directive, False, (1, 100, 0), parts = directives.nonnegative_int) PyTables-v.3.1.1/doc/sphinxext/ipython_console_highlighting.py000066400000000000000000000101271231437614300246220ustar00rootroot00000000000000"""reST directive for syntax-highlighting ipython interactive sessions. XXX - See what improvements can be made based on the new (as of Sept 2009) 'pycon' lexer for the python console. At the very least it will give better highlighted tracebacks. """ #----------------------------------------------------------------------------- # Needed modules # Standard library import re # Third party from pygments.lexer import Lexer, do_insertions from pygments.lexers.agile import (PythonConsoleLexer, PythonLexer, PythonTracebackLexer) from pygments.token import Comment, Generic from sphinx import highlighting #----------------------------------------------------------------------------- # Global constants line_re = re.compile('.*?\n') #----------------------------------------------------------------------------- # Code begins - classes and functions class IPythonConsoleLexer(Lexer): """ For IPython console output or doctests, such as: .. sourcecode:: ipython In [1]: a = 'foo' In [2]: a Out[2]: 'foo' In [3]: print a foo In [4]: 1 / 0 Notes: - Tracebacks are not currently supported. - It assumes the default IPython prompts, not customized ones. """ name = 'IPython console session' aliases = ['ipython'] mimetypes = ['text/x-ipython-console'] input_prompt = re.compile("(In \[[0-9]+\]: )|( \.\.\.+:)") output_prompt = re.compile("(Out\[[0-9]+\]: )|( \.\.\.+:)") continue_prompt = re.compile(" \.\.\.+:") tb_start = re.compile("\-+") def get_tokens_unprocessed(self, text): pylexer = PythonLexer(**self.options) tblexer = PythonTracebackLexer(**self.options) curcode = '' insertions = [] for match in line_re.finditer(text): line = match.group() input_prompt = self.input_prompt.match(line) continue_prompt = self.continue_prompt.match(line.rstrip()) output_prompt = self.output_prompt.match(line) if line.startswith("#"): insertions.append((len(curcode), [(0, Comment, line)])) elif input_prompt is not None: insertions.append((len(curcode), [(0, Generic.Prompt, input_prompt.group())])) curcode += line[input_prompt.end():] elif continue_prompt is not None: insertions.append((len(curcode), [(0, Generic.Prompt, continue_prompt.group())])) curcode += line[continue_prompt.end():] elif output_prompt is not None: # Use the 'error' token for output. We should probably make # our own token, but error is typicaly in a bright color like # red, so it works fine for our output prompts. insertions.append((len(curcode), [(0, Generic.Error, output_prompt.group())])) curcode += line[output_prompt.end():] else: if curcode: for item in do_insertions(insertions, pylexer.get_tokens_unprocessed(curcode)): yield item curcode = '' insertions = [] yield match.start(), Generic.Output, line if curcode: for item in do_insertions(insertions, pylexer.get_tokens_unprocessed(curcode)): yield item def setup(app): """Setup as a sphinx extension.""" # This is only a lexer, so adding it below to pygments appears sufficient. # But if somebody knows that the right API usage should be to do that via # sphinx, by all means fix it here. At least having this setup.py # suppresses the sphinx warning we'd get without it. pass #----------------------------------------------------------------------------- # Register the extension as a valid pygments lexer highlighting.lexers['ipython'] = IPythonConsoleLexer() PyTables-v.3.1.1/doc/sphinxext/ipython_directive.py000066400000000000000000000460631231437614300224210ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Sphinx directive to support embedded IPython code. This directive allows pasting of entire interactive IPython sessions, prompts and all, and their code will actually get re-executed at doc build time, with all prompts renumbered sequentially. To enable this directive, simply list it in your Sphinx ``conf.py`` file (making sure the directory where you placed it is visible to sphinx, as is needed for all Sphinx directives). By default this directive assumes that your prompts are unchanged IPython ones, but this can be customized. For example, the following code in your Sphinx config file will configure this directive for the following input/output prompts ``Yade [1]:`` and ``-> [1]:``:: import ipython_directive as id id.rgxin =re.compile(r'(?:In |Yade )\[(\d+)\]:\s?(.*)\s*') id.rgxout=re.compile(r'(?:Out| -> )\[(\d+)\]:\s?(.*)\s*') id.fmtin ='Yade [%d]:' id.fmtout=' -> [%d]:' from IPython import Config id.CONFIG = Config( prompt_in1="Yade [\#]:", prompt_in2=" .\D..", prompt_out=" -> [\#]:" ) id.reconfig_shell() import ipython_console_highlighting as ich ich.IPythonConsoleLexer.input_prompt= re.compile("(Yade \[[0-9]+\]: )|( \.\.\.+:)") ich.IPythonConsoleLexer.output_prompt= re.compile("(( -> )|(Out)\[[0-9]+\]: )|( \.\.\.+:)") ich.IPythonConsoleLexer.continue_prompt=re.compile(" \.\.\.+:") ToDo ---- - Turn the ad-hoc test() function into a real test suite. - Break up ipython-specific functionality from matplotlib stuff into better separated code. - Make sure %bookmarks used internally are removed on exit. Authors ------- - John D Hunter: orignal author. - Fernando Perez: refactoring, documentation, cleanups, port to 0.11. - VáclavÅ milauer : Prompt generalizations. """ #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- # Stdlib import cStringIO import os import re import sys # To keep compatibility with various python versions try: from hashlib import md5 except ImportError: from md5 import md5 # Third-party import matplotlib import sphinx from docutils.parsers.rst import directives matplotlib.use('Agg') # Our own from IPython import Config, InteractiveShell from IPython.utils.io import Term #----------------------------------------------------------------------------- # Globals #----------------------------------------------------------------------------- sphinx_version = sphinx.__version__.split(".") # The split is necessary for sphinx beta versions where the string is # '6b1' sphinx_version = tuple([int(re.split('[a-z]', x)[0]) for x in sphinx_version[:2]]) COMMENT, INPUT, OUTPUT = range(3) CONFIG = Config() rgxin = re.compile('In \[(\d+)\]:\s?(.*)\s*') rgxout = re.compile('Out\[(\d+)\]:\s?(.*)\s*') fmtin = 'In [%d]:' fmtout = 'Out[%d]:' #----------------------------------------------------------------------------- # Functions and class declarations #----------------------------------------------------------------------------- def block_parser(part): """ part is a string of ipython text, comprised of at most one input, one ouput, comments, and blank lines. The block parser parses the text into a list of:: blocks = [ (TOKEN0, data0), (TOKEN1, data1), ...] where TOKEN is one of [COMMENT | INPUT | OUTPUT ] and data is, depending on the type of token:: COMMENT : the comment string INPUT: the (DECORATOR, INPUT_LINE, REST) where DECORATOR: the input decorator (or None) INPUT_LINE: the input as string (possibly multi-line) REST : any stdout generated by the input line (not OUTPUT) OUTPUT: the output string, possibly multi-line """ block = [] lines = part.split('\n') N = len(lines) i = 0 decorator = None while True: if i==N: # nothing left to parse -- the last line break line = lines[i] i += 1 line_stripped = line.strip() if line_stripped.startswith('#'): block.append((COMMENT, line)) continue if line_stripped.startswith('@'): # we're assuming at most one decorator -- may need to # rethink decorator = line_stripped continue # does this look like an input line? matchin = rgxin.match(line) if matchin: lineno, inputline = int(matchin.group(1)), matchin.group(2) # the ....: continuation string continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2)) Nc = len(continuation) # input lines can continue on for more than one line, if # we have a '\' line continuation char or a function call # echo line 'print'. The input line can only be # terminated by the end of the block or an output line, so # we parse out the rest of the input line if it is # multiline as well as any echo text rest = [] while i2: if debug: print '\n'.join(lines) else: #print 'INSERTING %d lines'%len(lines) state_machine.insert_input( lines, state_machine.input_lines.source(0)) return [] ipython_directive.DEBUG = False ipython_directive.DEBUG = True # dbg # Enable as a proper Sphinx directive def setup(app): setup.app = app options = {'suppress': directives.flag, 'doctest': directives.flag, 'verbatim': directives.flag, } app.add_directive('ipython', ipython_directive, True, (0, 2, 0), **options) # Simple smoke test, needs to be converted to a proper automatic test. def test(): examples = [ r""" In [9]: pwd Out[9]: '/home/jdhunter/py4science/book' In [10]: cd bookdata/ /home/jdhunter/py4science/book/bookdata In [2]: from pylab import * In [2]: ion() In [3]: im = imread('stinkbug.png') @savefig mystinkbug.png width=4in In [4]: imshow(im) Out[4]: """, r""" In [1]: x = 'hello world' # string methods can be # used to alter the string @doctest In [2]: x.upper() Out[2]: 'HELLO WORLD' @verbatim In [3]: x.st x.startswith x.strip """, r""" In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\ .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv' In [131]: print url.split('&') ['http://ichart.finance.yahoo.com/table.csv?s=CROX', 'd=9', 'e=22', 'f=2009', 'g=d', 'a=1', 'b=8', 'c=2006', 'ignore=.csv'] In [60]: import urllib """, r"""\ In [133]: import numpy.random @suppress In [134]: numpy.random.seed(2358) @doctest In [135]: np.random.rand(10,2) Out[135]: array([[ 0.64524308, 0.59943846], [ 0.47102322, 0.8715456 ], [ 0.29370834, 0.74776844], [ 0.99539577, 0.1313423 ], [ 0.16250302, 0.21103583], [ 0.81626524, 0.1312433 ], [ 0.67338089, 0.72302393], [ 0.7566368 , 0.07033696], [ 0.22591016, 0.77731835], [ 0.0072729 , 0.34273127]]) """, r""" In [106]: print x jdh In [109]: for i in range(10): .....: print i .....: .....: 0 1 2 3 4 5 6 7 8 9 """, r""" In [144]: from pylab import * In [145]: ion() # use a semicolon to suppress the output @savefig test_hist.png width=4in In [151]: hist(np.random.randn(10000), 100); @savefig test_plot.png width=4in In [151]: plot(np.random.randn(10000), 'o'); """, r""" # use a semicolon to suppress the output In [151]: plt.clf() @savefig plot_simple.png width=4in In [151]: plot([1,2,3]) @savefig hist_simple.png width=4in In [151]: hist(np.random.randn(10000), 100); """, r""" # update the current fig In [151]: ylabel('number') In [152]: title('normal distribution') @savefig hist_with_text.png In [153]: grid(True) """, ] #ipython_directive.DEBUG = True # dbg #options = dict(suppress=True) # dbg options = dict() for example in examples: content = example.split('\n') ipython_directive('debug', arguments=None, options=options, content=content, lineno=0, content_offset=None, block_text=None, state=None, state_machine=None, ) # Run test suite as a script if __name__=='__main__': if not os.path.isdir('_static'): os.mkdir('_static') test() print 'All OK? Check figures in _static/' PyTables-v.3.1.1/doc/sphinxext/numpydoc.py000066400000000000000000000130701231437614300205170ustar00rootroot00000000000000""" ======== numpydoc ======== Sphinx extension that handles docstrings in the Numpy standard format. [1] It will: - Convert Parameters etc. sections to field lists. - Convert See Also section to a See also entry. - Renumber references. - Extract the signature from the docstring, if it can't be determined otherwise. .. [1] http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines#docstring-standard """ import sphinx if sphinx.__version__ < '1.0.1': raise RuntimeError("Sphinx 1.0.1 or newer is required") import os, re, pydoc from docscrape_sphinx import get_doc_object, SphinxDocString from sphinx.util.compat import Directive import inspect def mangle_docstrings(app, what, name, obj, options, lines, reference_offset=[0]): cfg = dict(use_plots=app.config.numpydoc_use_plots, show_class_members=app.config.numpydoc_show_class_members) if what == 'module': # Strip top title title_re = re.compile(ur'^\s*[#*=]{4,}\n[a-z0-9 -]+\n[#*=]{4,}\s*', re.I|re.S) lines[:] = title_re.sub(u'', u"\n".join(lines)).split(u"\n") else: doc = get_doc_object(obj, what, u"\n".join(lines), config=cfg) lines[:] = unicode(doc).split(u"\n") if app.config.numpydoc_edit_link and hasattr(obj, '__name__') and \ obj.__name__: if hasattr(obj, '__module__'): v = dict(full_name=u"%s.%s" % (obj.__module__, obj.__name__)) else: v = dict(full_name=obj.__name__) lines += [u'', u'.. htmlonly::', ''] lines += [u' %s' % x for x in (app.config.numpydoc_edit_link % v).split("\n")] # replace reference numbers so that there are no duplicates references = [] for line in lines: line = line.strip() m = re.match(ur'^.. \[([a-z0-9_.-])\]', line, re.I) if m: references.append(m.group(1)) # start renaming from the longest string, to avoid overwriting parts references.sort(key=lambda x: -len(x)) if references: for i, line in enumerate(lines): for r in references: if re.match(ur'^\d+$', r): new_r = u"R%d" % (reference_offset[0] + int(r)) else: new_r = u"%s%d" % (r, reference_offset[0]) lines[i] = lines[i].replace(u'[%s]_' % r, u'[%s]_' % new_r) lines[i] = lines[i].replace(u'.. [%s]' % r, u'.. [%s]' % new_r) reference_offset[0] += len(references) def mangle_signature(app, what, name, obj, options, sig, retann): # Do not try to inspect classes that don't define `__init__` if (inspect.isclass(obj) and (not hasattr(obj, '__init__') or 'initializes x; see ' in pydoc.getdoc(obj.__init__))): return '', '' if not (callable(obj) or hasattr(obj, '__argspec_is_invalid_')): return if not hasattr(obj, '__doc__'): return doc = SphinxDocString(pydoc.getdoc(obj)) if doc['Signature']: sig = re.sub(u"^[^(]*", u"", doc['Signature']) return sig, u'' def setup(app, get_doc_object_=get_doc_object): global get_doc_object get_doc_object = get_doc_object_ app.connect('autodoc-process-docstring', mangle_docstrings) app.connect('autodoc-process-signature', mangle_signature) app.add_config_value('numpydoc_edit_link', None, False) app.add_config_value('numpydoc_use_plots', None, False) app.add_config_value('numpydoc_show_class_members', True, True) # Extra mangling domains app.add_domain(NumpyPythonDomain) app.add_domain(NumpyCDomain) #------------------------------------------------------------------------------ # Docstring-mangling domains #------------------------------------------------------------------------------ from docutils.statemachine import ViewList from sphinx.domains.c import CDomain from sphinx.domains.python import PythonDomain class ManglingDomainBase(object): directive_mangling_map = {} def __init__(self, *a, **kw): super(ManglingDomainBase, self).__init__(*a, **kw) self.wrap_mangling_directives() def wrap_mangling_directives(self): for name, objtype in self.directive_mangling_map.items(): self.directives[name] = wrap_mangling_directive( self.directives[name], objtype) class NumpyPythonDomain(ManglingDomainBase, PythonDomain): name = 'np' directive_mangling_map = { 'function': 'function', 'class': 'class', 'exception': 'class', 'method': 'function', 'classmethod': 'function', 'staticmethod': 'function', 'attribute': 'attribute', } class NumpyCDomain(ManglingDomainBase, CDomain): name = 'np-c' directive_mangling_map = { 'function': 'function', 'member': 'attribute', 'macro': 'function', 'type': 'class', 'var': 'object', } def wrap_mangling_directive(base_directive, objtype): class directive(base_directive): def run(self): env = self.state.document.settings.env name = None if self.arguments: m = re.match(r'^(.*\s+)?(.*?)(\(.*)?', self.arguments[0]) name = m.group(2).strip() if not name: name = self.arguments[0] lines = list(self.content) mangle_docstrings(env.app, objtype, name, None, None, lines) self.content = ViewList(lines, self.content.parent) return base_directive.run(self) return directive PyTables-v.3.1.1/doc/sphinxext/phantom_import.py000066400000000000000000000130641231437614300217240ustar00rootroot00000000000000""" ============== phantom_import ============== Sphinx extension to make directives from ``sphinx.ext.autodoc`` and similar extensions to use docstrings loaded from an XML file. This extension loads an XML file in the Pydocweb format [1] and creates a dummy module that contains the specified docstrings. This can be used to get the current docstrings from a Pydocweb instance without needing to rebuild the documented module. .. [1] http://code.google.com/p/pydocweb """ import imp, sys, compiler, types, os, inspect, re def setup(app): app.connect('builder-inited', initialize) app.add_config_value('phantom_import_file', None, True) def initialize(app): fn = app.config.phantom_import_file if (fn and os.path.isfile(fn)): print "[numpydoc] Phantom importing modules from", fn, "..." import_phantom_module(fn) #------------------------------------------------------------------------------ # Creating 'phantom' modules from an XML description #------------------------------------------------------------------------------ def import_phantom_module(xml_file): """ Insert a fake Python module to sys.modules, based on a XML file. The XML file is expected to conform to Pydocweb DTD. The fake module will contain dummy objects, which guarantee the following: - Docstrings are correct. - Class inheritance relationships are correct (if present in XML). - Function argspec is *NOT* correct (even if present in XML). Instead, the function signature is prepended to the function docstring. - Class attributes are *NOT* correct; instead, they are dummy objects. Parameters ---------- xml_file : str Name of an XML file to read """ import lxml.etree as etree object_cache = {} tree = etree.parse(xml_file) root = tree.getroot() # Sort items so that # - Base classes come before classes inherited from them # - Modules come before their contents all_nodes = dict([(n.attrib['id'], n) for n in root]) def _get_bases(node, recurse=False): bases = [x.attrib['ref'] for x in node.findall('base')] if recurse: j = 0 while True: try: b = bases[j] except IndexError: break if b in all_nodes: bases.extend(_get_bases(all_nodes[b])) j += 1 return bases type_index = ['module', 'class', 'callable', 'object'] def base_cmp(a, b): x = cmp(type_index.index(a.tag), type_index.index(b.tag)) if x != 0: return x if a.tag == 'class' and b.tag == 'class': a_bases = _get_bases(a, recurse=True) b_bases = _get_bases(b, recurse=True) x = cmp(len(a_bases), len(b_bases)) if x != 0: return x if a.attrib['id'] in b_bases: return -1 if b.attrib['id'] in a_bases: return 1 return cmp(a.attrib['id'].count('.'), b.attrib['id'].count('.')) nodes = root.getchildren() nodes.sort(base_cmp) # Create phantom items for node in nodes: name = node.attrib['id'] doc = (node.text or '').decode('string-escape') + "\n" if doc == "\n": doc = "" # create parent, if missing parent = name while True: parent = '.'.join(parent.split('.')[:-1]) if not parent: break if parent in object_cache: break obj = imp.new_module(parent) object_cache[parent] = obj sys.modules[parent] = obj # create object if node.tag == 'module': obj = imp.new_module(name) obj.__doc__ = doc sys.modules[name] = obj elif node.tag == 'class': bases = [object_cache[b] for b in _get_bases(node) if b in object_cache] bases.append(object) init = lambda self: None init.__doc__ = doc obj = type(name, tuple(bases), {'__doc__': doc, '__init__': init}) obj.__name__ = name.split('.')[-1] elif node.tag == 'callable': funcname = node.attrib['id'].split('.')[-1] argspec = node.attrib.get('argspec') if argspec: argspec = re.sub('^[^(]*', '', argspec) doc = "%s%s\n\n%s" % (funcname, argspec, doc) obj = lambda: 0 obj.__argspec_is_invalid_ = True obj.func_name = funcname obj.__name__ = name obj.__doc__ = doc if inspect.isclass(object_cache[parent]): obj.__objclass__ = object_cache[parent] else: class Dummy(object): pass obj = Dummy() obj.__name__ = name obj.__doc__ = doc if inspect.isclass(object_cache[parent]): obj.__get__ = lambda: None object_cache[name] = obj if parent: if inspect.ismodule(object_cache[parent]): obj.__module__ = parent setattr(object_cache[parent], name.split('.')[-1], obj) # Populate items for node in root: obj = object_cache.get(node.attrib['id']) if obj is None: continue for ref in node.findall('ref'): if node.tag == 'class': if ref.attrib['ref'].startswith(node.attrib['id'] + '.'): setattr(obj, ref.attrib['name'], object_cache.get(ref.attrib['ref'])) else: setattr(obj, ref.attrib['name'], object_cache.get(ref.attrib['ref'])) PyTables-v.3.1.1/doc/sphinxext/plot_directive.py000066400000000000000000000476571231437614300217170ustar00rootroot00000000000000""" A special directive for generating a matplotlib plot. .. warning:: This is a hacked version of plot_directive.py from Matplotlib. It's very much subject to change! Usage ----- Can be used like this:: .. plot:: examples/example.py .. plot:: import matplotlib.pyplot as plt plt.plot([1,2,3], [4,5,6]) .. plot:: A plotting example: >>> import matplotlib.pyplot as plt >>> plt.plot([1,2,3], [4,5,6]) The content is interpreted as doctest formatted if it has a line starting with ``>>>``. The ``plot`` directive supports the options format : {'python', 'doctest'} Specify the format of the input include-source : bool Whether to display the source code. Default can be changed in conf.py and the ``image`` directive options ``alt``, ``height``, ``width``, ``scale``, ``align``, ``class``. Configuration options --------------------- The plot directive has the following configuration options: plot_include_source Default value for the include-source option plot_pre_code Code that should be executed before each plot. plot_basedir Base directory, to which plot:: file names are relative to. (If None or empty, file names are relative to the directoly where the file containing the directive is.) plot_formats File formats to generate. List of tuples or strings:: [(suffix, dpi), suffix, ...] that determine the file format and the DPI. For entries whose DPI was omitted, sensible defaults are chosen. plot_html_show_formats Whether to show links to the files in HTML. TODO ---- * Refactor Latex output; now it's plain images, but it would be nice to make them appear side-by-side, or in floats. """ import sys, os, glob, shutil, imp, warnings, cStringIO, re, textwrap, traceback import sphinx import warnings warnings.warn("A plot_directive module is also available under " "matplotlib.sphinxext; expect this numpydoc.plot_directive " "module to be deprecated after relevant features have been " "integrated there.", FutureWarning, stacklevel=2) #------------------------------------------------------------------------------ # Registration hook #------------------------------------------------------------------------------ def setup(app): setup.app = app setup.config = app.config setup.confdir = app.confdir app.add_config_value('plot_pre_code', '', True) app.add_config_value('plot_include_source', False, True) app.add_config_value('plot_formats', ['png', 'hires.png', 'pdf'], True) app.add_config_value('plot_basedir', None, True) app.add_config_value('plot_html_show_formats', True, True) app.add_directive('plot', plot_directive, True, (0, 1, False), **plot_directive_options) #------------------------------------------------------------------------------ # plot:: directive #------------------------------------------------------------------------------ from docutils.parsers.rst import directives from docutils import nodes def plot_directive(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine): return run(arguments, content, options, state_machine, state, lineno) plot_directive.__doc__ = __doc__ def _option_boolean(arg): if not arg or not arg.strip(): # no argument given, assume used as a flag return True elif arg.strip().lower() in ('no', '0', 'false'): return False elif arg.strip().lower() in ('yes', '1', 'true'): return True else: raise ValueError('"%s" unknown boolean' % arg) def _option_format(arg): return directives.choice(arg, ('python', 'lisp')) def _option_align(arg): return directives.choice(arg, ("top", "middle", "bottom", "left", "center", "right")) plot_directive_options = {'alt': directives.unchanged, 'height': directives.length_or_unitless, 'width': directives.length_or_percentage_or_unitless, 'scale': directives.nonnegative_int, 'align': _option_align, 'class': directives.class_option, 'include-source': _option_boolean, 'format': _option_format, } #------------------------------------------------------------------------------ # Generating output #------------------------------------------------------------------------------ from docutils import nodes, utils try: # Sphinx depends on either Jinja or Jinja2 import jinja2 def format_template(template, **kw): return jinja2.Template(template).render(**kw) except ImportError: import jinja def format_template(template, **kw): return jinja.from_string(template, **kw) TEMPLATE = """ {{ source_code }} {{ only_html }} {% if source_link or (html_show_formats and not multi_image) %} ( {%- if source_link -%} `Source code <{{ source_link }}>`__ {%- endif -%} {%- if html_show_formats and not multi_image -%} {%- for img in images -%} {%- for fmt in img.formats -%} {%- if source_link or not loop.first -%}, {% endif -%} `{{ fmt }} <{{ dest_dir }}/{{ img.basename }}.{{ fmt }}>`__ {%- endfor -%} {%- endfor -%} {%- endif -%} ) {% endif %} {% for img in images %} .. figure:: {{ build_dir }}/{{ img.basename }}.png {%- for option in options %} {{ option }} {% endfor %} {% if html_show_formats and multi_image -%} ( {%- for fmt in img.formats -%} {%- if not loop.first -%}, {% endif -%} `{{ fmt }} <{{ dest_dir }}/{{ img.basename }}.{{ fmt }}>`__ {%- endfor -%} ) {%- endif -%} {% endfor %} {{ only_latex }} {% for img in images %} .. image:: {{ build_dir }}/{{ img.basename }}.pdf {% endfor %} """ class ImageFile(object): def __init__(self, basename, dirname): self.basename = basename self.dirname = dirname self.formats = [] def filename(self, format): return os.path.join(self.dirname, "%s.%s" % (self.basename, format)) def filenames(self): return [self.filename(fmt) for fmt in self.formats] def run(arguments, content, options, state_machine, state, lineno): if arguments and content: raise RuntimeError("plot:: directive can't have both args and content") document = state_machine.document config = document.settings.env.config options.setdefault('include-source', config.plot_include_source) # determine input rst_file = document.attributes['source'] rst_dir = os.path.dirname(rst_file) if arguments: if not config.plot_basedir: source_file_name = os.path.join(rst_dir, directives.uri(arguments[0])) else: source_file_name = os.path.join(setup.confdir, config.plot_basedir, directives.uri(arguments[0])) code = open(source_file_name, 'r').read() output_base = os.path.basename(source_file_name) else: source_file_name = rst_file code = textwrap.dedent("\n".join(map(str, content))) counter = document.attributes.get('_plot_counter', 0) + 1 document.attributes['_plot_counter'] = counter base, ext = os.path.splitext(os.path.basename(source_file_name)) output_base = '%s-%d.py' % (base, counter) base, source_ext = os.path.splitext(output_base) if source_ext in ('.py', '.rst', '.txt'): output_base = base else: source_ext = '' # ensure that LaTeX includegraphics doesn't choke in foo.bar.pdf filenames output_base = output_base.replace('.', '-') # is it in doctest format? is_doctest = contains_doctest(code) if options.has_key('format'): if options['format'] == 'python': is_doctest = False else: is_doctest = True # determine output directory name fragment source_rel_name = relpath(source_file_name, setup.confdir) source_rel_dir = os.path.dirname(source_rel_name) while source_rel_dir.startswith(os.path.sep): source_rel_dir = source_rel_dir[1:] # build_dir: where to place output files (temporarily) build_dir = os.path.join(os.path.dirname(setup.app.doctreedir), 'plot_directive', source_rel_dir) if not os.path.exists(build_dir): os.makedirs(build_dir) # output_dir: final location in the builder's directory dest_dir = os.path.abspath(os.path.join(setup.app.builder.outdir, source_rel_dir)) # how to link to files from the RST file dest_dir_link = os.path.join(relpath(setup.confdir, rst_dir), source_rel_dir).replace(os.path.sep, '/') build_dir_link = relpath(build_dir, rst_dir).replace(os.path.sep, '/') source_link = dest_dir_link + '/' + output_base + source_ext # make figures try: results = makefig(code, source_file_name, build_dir, output_base, config) errors = [] except PlotError, err: reporter = state.memo.reporter sm = reporter.system_message( 2, "Exception occurred in plotting %s: %s" % (output_base, err), line=lineno) results = [(code, [])] errors = [sm] # generate output restructuredtext total_lines = [] for j, (code_piece, images) in enumerate(results): if options['include-source']: if is_doctest: lines = [''] lines += [row.rstrip() for row in code_piece.split('\n')] else: lines = ['.. code-block:: python', ''] lines += [' %s' % row.rstrip() for row in code_piece.split('\n')] source_code = "\n".join(lines) else: source_code = "" opts = [':%s: %s' % (key, val) for key, val in options.items() if key in ('alt', 'height', 'width', 'scale', 'align', 'class')] only_html = ".. only:: html" only_latex = ".. only:: latex" if j == 0: src_link = source_link else: src_link = None result = format_template( TEMPLATE, dest_dir=dest_dir_link, build_dir=build_dir_link, source_link=src_link, multi_image=len(images) > 1, only_html=only_html, only_latex=only_latex, options=opts, images=images, source_code=source_code, html_show_formats=config.plot_html_show_formats) total_lines.extend(result.split("\n")) total_lines.extend("\n") if total_lines: state_machine.insert_input(total_lines, source=source_file_name) # copy image files to builder's output directory if not os.path.exists(dest_dir): os.makedirs(dest_dir) for code_piece, images in results: for img in images: for fn in img.filenames(): shutil.copyfile(fn, os.path.join(dest_dir, os.path.basename(fn))) # copy script (if necessary) if source_file_name == rst_file: target_name = os.path.join(dest_dir, output_base + source_ext) f = open(target_name, 'w') f.write(unescape_doctest(code)) f.close() return errors #------------------------------------------------------------------------------ # Run code and capture figures #------------------------------------------------------------------------------ import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import matplotlib.image as image from matplotlib import _pylab_helpers import exceptions def contains_doctest(text): try: # check if it's valid Python as-is compile(text, '', 'exec') return False except SyntaxError: pass r = re.compile(r'^\s*>>>', re.M) m = r.search(text) return bool(m) def unescape_doctest(text): """ Extract code from a piece of text, which contains either Python code or doctests. """ if not contains_doctest(text): return text code = "" for line in text.split("\n"): m = re.match(r'^\s*(>>>|\.\.\.) (.*)$', line) if m: code += m.group(2) + "\n" elif line.strip(): code += "# " + line.strip() + "\n" else: code += "\n" return code def split_code_at_show(text): """ Split code at plt.show() """ parts = [] is_doctest = contains_doctest(text) part = [] for line in text.split("\n"): if (not is_doctest and line.strip() == 'plt.show()') or \ (is_doctest and line.strip() == '>>> plt.show()'): part.append(line) parts.append("\n".join(part)) part = [] else: part.append(line) if "\n".join(part).strip(): parts.append("\n".join(part)) return parts class PlotError(RuntimeError): pass def run_code(code, code_path, ns=None): # Change the working directory to the directory of the example, so # it can get at its data files, if any. pwd = os.getcwd() old_sys_path = list(sys.path) if code_path is not None: dirname = os.path.abspath(os.path.dirname(code_path)) os.chdir(dirname) sys.path.insert(0, dirname) # Redirect stdout stdout = sys.stdout sys.stdout = cStringIO.StringIO() # Reset sys.argv old_sys_argv = sys.argv sys.argv = [code_path] try: try: code = unescape_doctest(code) if ns is None: ns = {} if not ns: exec setup.config.plot_pre_code in ns exec code in ns except (Exception, SystemExit), err: raise PlotError(traceback.format_exc()) finally: os.chdir(pwd) sys.argv = old_sys_argv sys.path[:] = old_sys_path sys.stdout = stdout return ns #------------------------------------------------------------------------------ # Generating figures #------------------------------------------------------------------------------ def out_of_date(original, derived): """ Returns True if derivative is out-of-date wrt original, both of which are full file paths. """ return (not os.path.exists(derived) or os.stat(derived).st_mtime < os.stat(original).st_mtime) def makefig(code, code_path, output_dir, output_base, config): """ Run a pyplot script *code* and save the images under *output_dir* with file names derived from *output_base* """ # -- Parse format list default_dpi = {'png': 80, 'hires.png': 200, 'pdf': 50} formats = [] for fmt in config.plot_formats: if isinstance(fmt, str): formats.append((fmt, default_dpi.get(fmt, 80))) elif type(fmt) in (tuple, list) and len(fmt)==2: formats.append((str(fmt[0]), int(fmt[1]))) else: raise PlotError('invalid image format "%r" in plot_formats' % fmt) # -- Try to determine if all images already exist code_pieces = split_code_at_show(code) # Look for single-figure output files first all_exists = True img = ImageFile(output_base, output_dir) for format, dpi in formats: if out_of_date(code_path, img.filename(format)): all_exists = False break img.formats.append(format) if all_exists: return [(code, [img])] # Then look for multi-figure output files results = [] all_exists = True for i, code_piece in enumerate(code_pieces): images = [] for j in xrange(1000): img = ImageFile('%s_%02d_%02d' % (output_base, i, j), output_dir) for format, dpi in formats: if out_of_date(code_path, img.filename(format)): all_exists = False break img.formats.append(format) # assume that if we have one, we have them all if not all_exists: all_exists = (j > 0) break images.append(img) if not all_exists: break results.append((code_piece, images)) if all_exists: return results # -- We didn't find the files, so build them results = [] ns = {} for i, code_piece in enumerate(code_pieces): # Clear between runs plt.close('all') # Run code run_code(code_piece, code_path, ns) # Collect images images = [] fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() for j, figman in enumerate(fig_managers): if len(fig_managers) == 1 and len(code_pieces) == 1: img = ImageFile(output_base, output_dir) else: img = ImageFile("%s_%02d_%02d" % (output_base, i, j), output_dir) images.append(img) for format, dpi in formats: try: figman.canvas.figure.savefig(img.filename(format), dpi=dpi) except exceptions.BaseException, err: raise PlotError(traceback.format_exc()) img.formats.append(format) # Results results.append((code_piece, images)) return results #------------------------------------------------------------------------------ # Relative pathnames #------------------------------------------------------------------------------ try: from os.path import relpath except ImportError: # Copied from Python 2.7 if 'posix' in sys.builtin_module_names: def relpath(path, start=os.path.curdir): """Return a relative version of a path""" from os.path import sep, curdir, join, abspath, commonprefix, \ pardir if not path: raise ValueError("no path specified") start_list = abspath(start).split(sep) path_list = abspath(path).split(sep) # Work out how much of the filepath is shared by start and path. i = len(commonprefix([start_list, path_list])) rel_list = [pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: return curdir return join(*rel_list) elif 'nt' in sys.builtin_module_names: def relpath(path, start=os.path.curdir): """Return a relative version of a path""" from os.path import sep, curdir, join, abspath, commonprefix, \ pardir, splitunc if not path: raise ValueError("no path specified") start_list = abspath(start).split(sep) path_list = abspath(path).split(sep) if start_list[0].lower() != path_list[0].lower(): unc_path, rest = splitunc(path) unc_start, rest = splitunc(start) if bool(unc_path) ^ bool(unc_start): raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" % (path, start)) else: raise ValueError("path is on drive %s, start on drive %s" % (path_list[0], start_list[0])) # Work out how much of the filepath is shared by start and path. for i in range(min(len(start_list), len(path_list))): if start_list[i].lower() != path_list[i].lower(): break else: i += 1 rel_list = [pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: return curdir return join(*rel_list) else: raise RuntimeError("Unsupported platform (no relpath available!)") PyTables-v.3.1.1/doc/sphinxext/setup.py000066400000000000000000000017251231437614300200250ustar00rootroot00000000000000from distutils.core import setup import setuptools import sys, os version = "0.4" setup( name="numpydoc", packages=["numpydoc"], package_dir={"numpydoc": ""}, version=version, description="Sphinx extension to support docstrings in Numpy format", # classifiers from http://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers=["Development Status :: 3 - Alpha", "Environment :: Plugins", "License :: OSI Approved :: BSD License", "Topic :: Documentation"], keywords="sphinx numpy", author="Pauli Virtanen and others", author_email="pav@iki.fi", url="http://github.com/numpy/numpy/tree/master/doc/sphinxext", license="BSD", zip_safe=False, install_requires=["Sphinx >= 1.0.1"], package_data={'numpydoc': 'tests', '': ''}, entry_points={ "console_scripts": [ "autosummary_generate = numpydoc.autosummary_generate:main", ], }, ) PyTables-v.3.1.1/doc/sphinxext/traitsdoc.py000066400000000000000000000100531231437614300206530ustar00rootroot00000000000000""" ========= traitsdoc ========= Sphinx extension that handles docstrings in the Numpy standard format, [1] and support Traits [2]. This extension can be used as a replacement for ``numpydoc`` when support for Traits is required. .. [1] http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines#docstring-standard .. [2] http://code.enthought.com/projects/traits/ """ import inspect import os import pydoc import docscrape import docscrape_sphinx from docscrape_sphinx import SphinxClassDoc, SphinxFunctionDoc, SphinxDocString import numpydoc import comment_eater class SphinxTraitsDoc(SphinxClassDoc): def __init__(self, cls, modulename='', func_doc=SphinxFunctionDoc): if not inspect.isclass(cls): raise ValueError("Initialise using a class. Got %r" % cls) self._cls = cls if modulename and not modulename.endswith('.'): modulename += '.' self._mod = modulename self._name = cls.__name__ self._func_doc = func_doc docstring = pydoc.getdoc(cls) docstring = docstring.split('\n') # De-indent paragraph try: indent = min(len(s) - len(s.lstrip()) for s in docstring if s.strip()) except ValueError: indent = 0 for n, line in enumerate(docstring): docstring[n] = docstring[n][indent:] self._doc = docscrape.Reader(docstring) self._parsed_data = { 'Signature': '', 'Summary': '', 'Description': [], 'Extended Summary': [], 'Parameters': [], 'Returns': [], 'Raises': [], 'Warns': [], 'Other Parameters': [], 'Traits': [], 'Methods': [], 'See Also': [], 'Notes': [], 'References': '', 'Example': '', 'Examples': '', 'index': {} } self._parse() def _str_summary(self): return self['Summary'] + [''] def _str_extended_summary(self): return self['Description'] + self['Extended Summary'] + [''] def __str__(self, indent=0, func_role="func"): out = [] out += self._str_signature() out += self._str_index() + [''] out += self._str_summary() out += self._str_extended_summary() for param_list in ('Parameters', 'Traits', 'Methods', 'Returns', 'Raises'): out += self._str_param_list(param_list) out += self._str_see_also("obj") out += self._str_section('Notes') out += self._str_references() out += self._str_section('Example') out += self._str_section('Examples') out = self._str_indent(out, indent) return '\n'.join(out) def looks_like_issubclass(obj, classname): """ Return True if the object has a class or superclass with the given class name. Ignores old-style classes. """ t = obj if t.__name__ == classname: return True for klass in t.__mro__: if klass.__name__ == classname: return True return False def get_doc_object(obj, what=None, config=None): if what is None: if inspect.isclass(obj): what = 'class' elif inspect.ismodule(obj): what = 'module' elif callable(obj): what = 'function' else: what = 'object' if what == 'class': doc = SphinxTraitsDoc(obj, '', func_doc=SphinxFunctionDoc, config=config) if looks_like_issubclass(obj, 'HasTraits'): for name, trait, comment in comment_eater.get_class_traits(obj): # Exclude private traits. if not name.startswith('_'): doc['Traits'].append((name, trait, comment.splitlines())) return doc elif what in ('function', 'method'): return SphinxFunctionDoc(obj, '', config=config) else: return SphinxDocString(pydoc.getdoc(obj), config=config) def setup(app): # init numpydoc numpydoc.setup(app, get_doc_object) PyTables-v.3.1.1/examples/000077500000000000000000000000001231437614300153255ustar00rootroot00000000000000PyTables-v.3.1.1/examples/add-column.py000066400000000000000000000042651231437614300177310ustar00rootroot00000000000000"Example showing how to add a column on a existing column" from __future__ import print_function import tables class Particle(tables.IsDescription): name = tables.StringCol(16, pos=1) # 16-character String lati = tables.Int32Col(pos=2) # integer longi = tables.Int32Col(pos=3) # integer pressure = tables.Float32Col(pos=4) # float (single-precision) temperature = tables.Float64Col(pos=5) # double (double-precision) # Open a file in "w"rite mode fileh = tables.open_file("add-column.h5", mode="w") # Create a new group group = fileh.create_group(fileh.root, "newgroup") # Create a new table in newgroup group table = fileh.create_table(group, 'table', Particle, "A table", tables.Filters(1)) # Append several rows table.append([("Particle: 10", 10, 0, 10 * 10, 10 ** 2), ("Particle: 11", 11, -1, 11 * 11, 11 ** 2), ("Particle: 12", 12, -2, 12 * 12, 12 ** 2)]) print("Contents of the original table:", fileh.root.newgroup.table[:]) # close the file fileh.close() # Open it again in append mode fileh = tables.open_file("add-column.h5", "a") group = fileh.root.newgroup table = group.table # Get a description of table in dictionary format descr = table.description._v_colobjects descr2 = descr.copy() # Add a column to description descr2["hot"] = tables.BoolCol(dflt=False) # Create a new table with the new description table2 = fileh.create_table(group, 'table2', descr2, "A table", tables.Filters(1)) # Copy the user attributes table.attrs._f_copy(table2) # Fill the rows of new table with default values for i in range(table.nrows): table2.row.append() # Flush the rows to disk table2.flush() # Copy the columns of source table to destination for col in descr: getattr(table2.cols, col)[:] = getattr(table.cols, col)[:] # Fill the new column table2.cols.hot[:] = [row["temperature"] > 11 ** 2 for row in table] # Remove the original table table.remove() # Move table2 to table table2.move('/newgroup', 'table') # Print the new table print("Contents of the table with column added:", fileh.root.newgroup.table[:]) # Finally, close the file fileh.close() PyTables-v.3.1.1/examples/array1.py000066400000000000000000000025461231437614300171050ustar00rootroot00000000000000from __future__ import print_function import numpy as np import tables # Open a new empty HDF5 file fileh = tables.open_file("array1.h5", mode="w") # Get the root group root = fileh.root # Create an Array a = np.array([-1, 2, 4], np.int16) # Save it on the HDF5 file hdfarray = fileh.create_array(root, 'array_1', a, "Signed short array") # Create a scalar Array a = np.array(4, np.int16) # Save it on the HDF5 file hdfarray = fileh.create_array(root, 'array_s', a, "Scalar signed short array") # Create a 3-d array of floats a = np.arange(120, dtype=np.float64).reshape(20, 3, 2) # Save it on the HDF5 file hdfarray = fileh.create_array(root, 'array_f', a, "3-D float array") # Close the file fileh.close() # Open the file for reading fileh = tables.open_file("array1.h5", mode="r") # Get the root group root = fileh.root a = root.array_1.read() print("Signed byte array -->", repr(a), a.shape) print("Testing iterator (works even over scalar arrays):", end=' ') arr = root.array_s for x in arr: print("nrow-->", arr.nrow) print("Element-->", repr(x)) # print "Testing getitem:" # for i in range(root.array_1.nrows): # print "array_1["+str(i)+"]", "-->", root.array_1[i] print("array_f[:,2:3,2::2]", repr(root.array_f[:, 2:3, 2::2])) print("array_f[1,2:]", repr(root.array_f[1, 2:])) print("array_f[1]", repr(root.array_f[1])) # Close the file fileh.close() PyTables-v.3.1.1/examples/array2.py000066400000000000000000000020051231437614300170740ustar00rootroot00000000000000from __future__ import print_function import numpy as np import tables # Open a new empty HDF5 file fileh = tables.open_file("array2.h5", mode="w") # Shortcut to the root group root = fileh.root # Create an array a = np.array([1, 2.7182818284590451, 3.141592], float) print("About to write array:", a) print(" with shape: ==>", a.shape) print(" and dtype ==>", a.dtype) # Save it on the HDF5 file hdfarray = fileh.create_array(root, 'carray', a, "Float array") # Get metadata on the previously saved array print() print("Info on the object:", repr(root.carray)) # Close the file fileh.close() # Open the previous HDF5 file in read-only mode fileh = tables.open_file("array2.h5", mode="r") # Get the root group root = fileh.root # Get metadata on the previously saved array print() print("Info on the object:", repr(root.carray)) # Get the actual array b = root.carray.read() print() print("Array read from file:", b) print(" with shape: ==>", b.shape) print(" and dtype ==>", b.dtype) # Close the file fileh.close() PyTables-v.3.1.1/examples/array3.py000066400000000000000000000022231231437614300170770ustar00rootroot00000000000000from __future__ import print_function import numpy as np import tables # Open a new empty HDF5 file fileh = tables.open_file("array3.h5", mode="w") # Get the root group root = fileh.root # Create a large array # a = reshape(array(range(2**16), "s"), (2,) * 16) a = np.ones((2,) * 8, np.int8) print("About to write array a") print(" with shape: ==>", a.shape) print(" and dtype: ==>", a.dtype) # Save it on the HDF5 file hdfarray = fileh.create_array(root, 'carray', a, "Large array") # Get metadata on the previously saved array print() print("Info on the object:", repr(root.carray)) # Close the file fileh.close() # Open the previous HDF5 file in read-only mode fileh = tables.open_file("array3.h5", mode="r") # Get the root group root = fileh.root # Get metadata on the previously saved array print() print("Getting info on retrieved /carray object:", repr(root.carray)) # Get the actual array # b = fileh.readArray("/carray") # You can obtain the same result with: b = root.carray.read() print() print("Array b read from file") print(" with shape: ==>", b.shape) print(" with dtype: ==>", b.dtype) # print " contents:", b # Close the file fileh.close() PyTables-v.3.1.1/examples/array4.py000066400000000000000000000032611231437614300171030ustar00rootroot00000000000000from __future__ import print_function import numpy as np import tables basedim = 4 file = "array4.h5" # Open a new empty HDF5 file fileh = tables.open_file(file, mode="w") # Get the root group group = fileh.root # Set the type codes to test dtypes = [np.int8, np.uint8, np.int16, np.int, np.float32, np.float] i = 1 for dtype in dtypes: # Create an array of dtype, with incrementally bigger ranges a = np.ones((basedim,) * i, dtype) # Save it on the HDF5 file dsetname = 'array_' + a.dtype.char hdfarray = fileh.create_array(group, dsetname, a, "Large array") print("Created dataset:", hdfarray) # Create a new group group = fileh.create_group(group, 'group' + str(i)) # increment the range for next iteration i += 1 # Close the file fileh.close() # Open the previous HDF5 file in read-only mode fileh = tables.open_file(file, mode="r") # Get the root group group = fileh.root # Get the metadata on the previosly saved arrays for i in range(len(dtypes)): # Create an array for later comparison a = np.ones((basedim,) * (i + 1), dtypes[i]) # Get the dset object hangin from group dset = getattr(group, 'array_' + a.dtype.char) print("Info from dataset:", repr(dset)) # Read the actual data in array b = dset.read() print("Array b read from file. Shape ==>", b.shape, end=' ') print(". Dtype ==> %s" % b.dtype) # Test if the original and read arrays are equal if np.allclose(a, b): print("Good: Read array is equal to the original") else: print("Error: Read array and the original differs!") # Iterate over the next group group = getattr(group, 'group' + str(i + 1)) # Close the file fileh.close() PyTables-v.3.1.1/examples/attributes1.py000066400000000000000000000012231231437614300201440ustar00rootroot00000000000000import numpy as np import tables # Open a new empty HDF5 file fileh = tables.open_file("attributes1.h5", mode="w", title="Testing attributes") # Get the root group root = fileh.root # Create an array a = np.array([1, 2, 4], np.int32) # Save it on the HDF5 file hdfarray = fileh.create_array(root, 'array', a, "Integer array") # Assign user attributes # A string hdfarray.attrs.string = "This is an example" # A Char hdfarray.attrs.char = "1" # An integer hdfarray.attrs.int = 12 # A float hdfarray.attrs.float = 12.32 # A generic object hdfarray.attrs.object = {"a": 32.1, "b": 1, "c": [1, 2]} # Close the file fileh.close() PyTables-v.3.1.1/examples/carray1.py000066400000000000000000000010011231437614300172310ustar00rootroot00000000000000from __future__ import print_function import numpy import tables fileName = 'carray1.h5' shape = (200, 300) atom = tables.UInt8Atom() filters = tables.Filters(complevel=5, complib='zlib') h5f = tables.open_file(fileName, 'w') ca = h5f.create_carray(h5f.root, 'carray', atom, shape, filters=filters) # Fill a hyperslab in ``ca``. ca[10:60, 20:70] = numpy.ones((50, 50)) h5f.close() # Re-open and read another hyperslab h5f = tables.open_file(fileName) print(h5f) print(h5f.root.carray[8:12, 18:22]) h5f.close() PyTables-v.3.1.1/examples/check_examples.sh000077500000000000000000000017741231437614300206500ustar00rootroot00000000000000#!/bin/sh set -e PYTHON=python # Small script to check the example repository quickly $PYTHON add-column.py $PYTHON array1.py $PYTHON array2.py $PYTHON array3.py $PYTHON array4.py $PYTHON attributes1.py $PYTHON carray1.py $PYTHON earray1.py $PYTHON earray2.py #$PYTHON enum.py # This should always fail $PYTHON filenodes1.py $PYTHON index.py $PYTHON inmemory.py $PYTHON links.py $PYTHON multiprocess_access_benchmarks.py $PYTHON multiprocess_access_queues.py $PYTHON nested1.py #$PYTHON nested-iter.py # Run this after "tutorial1-1.py" $PYTHON nested-tut.py $PYTHON objecttree.py $PYTHON particles.py $PYTHON read_array_out_arg.py $PYTHON split.py $PYTHON table1.py $PYTHON table2.py $PYTHON table3.py $PYTHON table-tree.py $PYTHON tutorial1-1.py $PYTHON tutorial1-2.py #$PYTHON tutorial2.py # This should always fail at the beginning $PYTHON tutorial3-1.py $PYTHON tutorial3-2.py $PYTHON undo-redo.py $PYTHON vlarray1.py $PYTHON vlarray2.py $PYTHON vlarray3.py $PYTHON vlarray4.py $PYTHON nested-iter.py PyTables-v.3.1.1/examples/earray1.py000066400000000000000000000010641231437614300172440ustar00rootroot00000000000000from __future__ import print_function import tables import numpy fileh = tables.open_file('earray1.h5', mode='w') a = tables.StringAtom(itemsize=8) # Use ``a`` as the object type for the enlargeable array. array_c = fileh.create_earray(fileh.root, 'array_c', a, (0,), "Chars") array_c.append(numpy.array(['a' * 2, 'b' * 4], dtype='S8')) array_c.append(numpy.array(['a' * 6, 'b' * 8, 'c' * 10], dtype='S8')) # Read the string ``EArray`` we have created on disk. for s in array_c: print('array_c[%s] => %r' % (array_c.nrow, s)) # Close the file. fileh.close() PyTables-v.3.1.1/examples/earray2.py000066400000000000000000000050451231437614300172500ustar00rootroot00000000000000#!/usr/bin/env python """Small example that shows how to work with extendeable arrays of different types, strings included.""" from __future__ import print_function import numpy as np import tables # Open a new empty HDF5 file filename = "earray2.h5" fileh = tables.open_file(filename, mode="w") # Get the root group root = fileh.root # Create an string atom a = tables.StringAtom(itemsize=1) # Use it as a type for the enlargeable array hdfarray = fileh.create_earray(root, 'array_c', a, (0,), "Character array") hdfarray.append(np.array(['a', 'b', 'c'])) # The next is legal: hdfarray.append(np.array(['c', 'b', 'c', 'd'])) # but these are not: # hdfarray.append(array([['c', 'b'], ['c', 'd']])) # hdfarray.append(array([[1,2,3],[3,2,1]], dtype=uint8).reshape(2,1,3)) # Create an atom a = tables.UInt16Atom() hdfarray = fileh.create_earray(root, 'array_e', a, (2, 0, 3), "Unsigned short array") # Create an enlargeable array a = tables.UInt8Atom() hdfarray = fileh.create_earray(root, 'array_b', a, (2, 0, 3), "Unsigned byte array", tables.Filters(complevel=1)) # Append an array to this table hdfarray.append( np.array([[1, 2, 3], [3, 2, 1]], dtype=np.uint8).reshape(2, 1, 3)) hdfarray.append( np.array([[1, 2, 3], [3, 2, 1], [2, 4, 6], [6, 4, 2]], dtype=np.uint8).reshape(2, 2, 3) * 2) # The next should give a type error: # hdfarray.append(array([[1,0,1],[0,0,1]], dtype=Bool).reshape(2,1,3)) # Close the file fileh.close() # Open the file for reading fileh = tables.open_file(filename, mode="r") # Get the root group root = fileh.root a = root.array_c.read() print("Character array -->", repr(a), a.shape) a = root.array_e.read() print("Empty array (yes, this is suported) -->", repr(a), a.shape) a = root.array_b.read(step=2) print("Int8 array, even rows (step = 2) -->", repr(a), a.shape) print("Testing iterator:", end=' ') # for x in root.array_b.iterrows(step=2): for x in root.array_b: print("nrow-->", root.array_b.nrow) print("Element-->", x) print("Testing getitem:") for i in range(root.array_b.shape[0]): print("array_b[" + str(i) + "]", "-->", root.array_b[i]) # The nrows counts the growing dimension, which is different from the # first index for i in range(root.array_b.nrows): print("array_b[:," + str(i) + ",:]", "-->", root.array_b[:, i, :]) print("array_c[1:2]", repr(root.array_c[1:2])) print("array_c[1:3]", repr(root.array_c[1:3])) print("array_b[:]", root.array_b[:]) print(repr(root.array_c)) # Close the file fileh.close() PyTables-v.3.1.1/examples/enum.py000066400000000000000000000054531231437614300166520ustar00rootroot00000000000000# Example on using enumerated types under PyTables. # This file is intended to be run in an interactive Python session, # since it contains some statements that raise exceptions. # To run it, paste it as the input of ``python``. from __future__ import print_function def COMMENT(string): pass COMMENT("**** Usage of the ``Enum`` class. ****") COMMENT("Create an enumeration of colors with automatic concrete values.") import tables colorList = ['red', 'green', 'blue', 'white', 'black'] colors = tables.Enum(colorList) COMMENT("Take a look at the name-value pairs.") print("Colors:", [v for v in colors]) COMMENT("Access values as attributes.") print("Value of 'red' and 'white':", (colors.red, colors.white)) print("Value of 'yellow':", colors.yellow) COMMENT("Access values as items.") print("Value of 'red' and 'white':", (colors['red'], colors['white'])) print("Value of 'yellow':", colors['yellow']) COMMENT("Access names.") print("Name of value %s:" % colors.red, colors(colors.red)) print("Name of value 1234:", colors(1234)) COMMENT("**** Enumerated columns. ****") COMMENT("Create a new PyTables file.") h5f = tables.open_file('enum.h5', 'w') COMMENT("This describes a ball extraction.") class BallExt(tables.IsDescription): ballTime = tables.Time32Col() ballColor = tables.EnumCol(colors, 'black', base='uint8') COMMENT("Create a table of ball extractions.") tbl = h5f.create_table( '/', 'extractions', BallExt, title="Random ball extractions") COMMENT("Simulate some ball extractions.") import time import random now = time.time() row = tbl.row for i in range(10): row['ballTime'] = now + i row['ballColor'] = colors[random.choice(colorList)] # notice this row.append() COMMENT("Try to append an invalid value.") row['ballTime'] = now + 42 row['ballColor'] = 1234 tbl.flush() COMMENT("Now print them!") for r in tbl: ballTime = r['ballTime'] ballColor = colors(r['ballColor']) # notice this print("Ball extracted on %d is of color %s." % (ballTime, ballColor)) COMMENT("**** Enumerated arrays. ****") COMMENT("This describes a range of working days.") workingDays = {'Mon': 1, 'Tue': 2, 'Wed': 3, 'Thu': 4, 'Fri': 5} dayRange = tables.EnumAtom(workingDays, 'Mon', base='uint16', shape=(0, 2)) COMMENT("Create an EArray of day ranges within a week.") earr = h5f.create_earray('/', 'days', dayRange, title="Working day ranges") earr.flavor = 'python' COMMENT("Throw some day ranges in.") wdays = earr.get_enum() earr.append([(wdays.Mon, wdays.Fri), (wdays.Wed, wdays.Fri)]) COMMENT("The append method does not check values!") earr.append([(wdays.Mon, 1234)]) COMMENT("Print the values.") for (d1, d2) in earr: print("From %s to %s (%d days)." % (wdays(d1), wdays(d2), d2 - d1 + 1)) COMMENT("Close the PyTables file and remove it.") import os h5f.close() os.remove('enum.h5') PyTables-v.3.1.1/examples/filenodes1.py000066400000000000000000000022501231437614300177270ustar00rootroot00000000000000from __future__ import print_function from tables.nodes import filenode import tables h5file = tables.open_file('fnode.h5', 'w') fnode = filenode.new_node(h5file, where='/', name='fnode_test') print(h5file.get_node_attr('/fnode_test', 'NODE_TYPE')) print("This is a test text line.", file=fnode) print("And this is another one.", file=fnode) print(file=fnode) fnode.write("Of course, file methods can also be used.") fnode.seek(0) # Go back to the beginning of file. for line in fnode: print(repr(line)) fnode.close() print(fnode.closed) node = h5file.root.fnode_test fnode = filenode.open_node(node, 'a+') print(repr(fnode.readline())) print(fnode.tell()) print("This is a new line.", file=fnode) print(repr(fnode.readline())) fnode.seek(0) for line in fnode: print(repr(line)) fnode.attrs.content_type = 'text/plain; charset=us-ascii' fnode.attrs.author = "Ivan Vilata i Balaguer" fnode.attrs.creation_date = '2004-10-20T13:25:25+0200' fnode.attrs.keywords_en = ["FileNode", "test", "metadata"] fnode.attrs.keywords_ca = ["FileNode", "prova", "metadades"] fnode.attrs.owner = 'ivan' fnode.attrs.acl = {'ivan': 'rw', '@users': 'r'} fnode.close() h5file.close() PyTables-v.3.1.1/examples/index.py000066400000000000000000000021761231437614300170140ustar00rootroot00000000000000from __future__ import print_function import random import tables print('tables.__version__', tables.__version__) nrows = 10000 - 1 class Distance(tables.IsDescription): frame = tables.Int32Col(pos=0) distance = tables.Float64Col(pos=1) h5file = tables.open_file('index.h5', mode='w') table = h5file.create_table(h5file.root, 'distance_table', Distance, 'distance table', expectedrows=nrows) row = table.row for i in range(nrows): # r['frame'] = nrows-i row['frame'] = random.randint(0, nrows) row['distance'] = float(i ** 2) row.append() table.flush() table.cols.frame.create_index(optlevel=9, _testmode=True, _verbose=True) # table.cols.frame.optimizeIndex(level=5, verbose=1) results = [r.nrow for r in table.where('frame < 2')] print("frame<2 -->", table.read_coordinates(results)) # print("frame<2 -->", table.get_where_list('frame < 2')) results = [r.nrow for r in table.where('(1 < frame) & (frame <= 5)')] print("rows-->", results) print("1", table.read_coordinates(results)) # print("1", table.get_where_list('(1 < frame) & (frame <= 5)')) h5file.close() PyTables-v.3.1.1/examples/inmemory.py000077500000000000000000000027021231437614300175420ustar00rootroot00000000000000#!/usr/bin/env python # encoding: utf-8 """inmemory.py. Example usage of creating in-memory HDF5 file with a specified chunksize using PyTables 3.0.0+ See also Cookbook page http://pytables.github.io/cookbook/inmemory_hdf5_files.html and available drivers http://pytables.github.io/usersguide/parameter_files.html#hdf5-driver-management """ import numpy as np import tables CHUNKY = 30 CHUNKX = 4320 if __name__ == '__main__': # create dataset and add global attrs file_path = 'demofile_chunk%sx%d.h5' % (CHUNKY, CHUNKX) with tables.open_file(file_path, 'w', title='PyTables HDF5 In-memory example', driver='H5FD_CORE') as h5f: # dummy some data lats = np.empty([2160]) lons = np.empty([4320]) # create some simple arrays lat_node = h5f.create_array('/', 'lat', lats, title='latitude') lon_node = h5f.create_array('/', 'lon', lons, title='longitude') # create a 365 x 4320 x 8640 CArray of 32bit float shape = (5, 2160, 4320) atom = tables.Float32Atom(dflt=np.nan) # chunk into daily slices and then further chunk days sst_node = h5f.create_carray( h5f.root, 'sst', atom, shape, chunkshape=(1, CHUNKY, CHUNKX)) # dummy up an ndarray sst = np.empty([2160, 4320], dtype=np.float32) sst.fill(30.0) # write ndarray to a 2D plane in the HDF5 sst_node[0] = sst PyTables-v.3.1.1/examples/links.py000066400000000000000000000033421231437614300170210ustar00rootroot00000000000000from __future__ import print_function import tables as tb # Create a new file with some structural groups f1 = tb.open_file('links1.h5', 'w') g1 = f1.create_group('/', 'g1') g2 = f1.create_group(g1, 'g2') # Create some datasets a1 = f1.create_carray(g1, 'a1', tb.Int64Atom(), shape=(10000,)) t1 = f1.create_table(g2, 't1', {'f1': tb.IntCol(), 'f2': tb.FloatCol()}) # Create new group and a first hard link gl = f1.create_group('/', 'gl') ht = f1.create_hard_link(gl, 'ht', '/g1/g2/t1') # ht points to t1 print("``%s`` is a hard link to: ``%s``" % (ht, t1)) # Remove the orginal link to the t1 table t1.remove() print("table continues to be accessible in: ``%s``" % f1.get_node('/gl/ht')) # Let's continue with soft links la1 = f1.create_soft_link(gl, 'la1', '/g1/a1') # la1 points to a1 print("``%s`` is a soft link to: ``%s``" % (la1, la1.target)) lt = f1.create_soft_link(gl, 'lt', '/g1/g2/t1') # lt points to t1 (dangling) print("``%s`` is a soft link to: ``%s``" % (lt, lt.target)) # Recreate the '/g1/g2/t1' path t1 = f1.create_hard_link('/g1/g2', 't1', '/gl/ht') print("``%s`` is not dangling anymore" % (lt,)) # Dereferrencing plt = lt() print("dereferred lt node: ``%s``" % plt) pla1 = la1() print("dereferred la1 node: ``%s``" % pla1) # Copy the array a1 into another file f2 = tb.open_file('links2.h5', 'w') new_a1 = a1.copy(f2.root, 'a1') f2.close() # close the other file # Remove the original soft link and create an external link la1.remove() la1 = f1.create_external_link(gl, 'la1', 'links2.h5:/a1') print("``%s`` is an external link to: ``%s``" % (la1, la1.target)) new_a1 = la1() # dereferrencing la1 returns a1 in links2.h5 print("dereferred la1 node: ``%s``" % new_a1) print("new_a1 file:", new_a1._v_file.filename) f1.close() PyTables-v.3.1.1/examples/multiprocess_access_benchmarks.py000066400000000000000000000222141231437614300241470ustar00rootroot00000000000000# Benchmark three methods of using PyTables with multiple processes, where data # is read from a PyTables file in one process and then sent to another # # 1. using multiprocessing.Pipe # 2. using a memory mapped file that's shared between two processes, passed as # out argument to tables.Array.read. # 3. using a Unix domain socket (this uses the "abstract namespace" and will # work only on Linux). # 4. using an IPv4 socket # # In all three cases, an array is loaded from a file in one process, sent to # another, and then modified by incrementing each array element. This is meant # to simulate retrieving data and then modifying it. from __future__ import division from __future__ import print_function from __future__ import unicode_literals import multiprocessing import os import random import select import socket import time import numpy as np import tables # create a PyTables file with a single int64 array with the specified number of # elements def create_file(array_size): array = np.ones(array_size, dtype='i8') with tables.open_file('test.h5', 'w') as fobj: array = fobj.create_array('/', 'test', array) print('file created, size: {0} MB'.format(array.size_on_disk / 1e6)) # process to receive an array using a multiprocessing.Pipe connection class PipeReceive(multiprocessing.Process): def __init__(self, receiver_pipe, result_send): super(PipeReceive, self).__init__() self.receiver_pipe = receiver_pipe self.result_send = result_send def run(self): # block until something is received on the pipe array = self.receiver_pipe.recv() recv_timestamp = time.time() # perform an operation on the received array array += 1 finish_timestamp = time.time() assert(np.all(array == 2)) # send the measured timestamps back to the originating process self.result_send.send((recv_timestamp, finish_timestamp)) def read_and_send_pipe(send_type, array_size): # set up Pipe objects to send the actual array to the other process # and receive the timing results from the other process array_recv, array_send = multiprocessing.Pipe(False) result_recv, result_send = multiprocessing.Pipe(False) # start the other process and pause to allow it to start up recv_process = PipeReceive(array_recv, result_send) recv_process.start() time.sleep(0.15) with tables.open_file('test.h5', 'r') as fobj: array = fobj.get_node('/', 'test') start_timestamp = time.time() # read an array from the PyTables file and send it to the other process output = array.read(0, array_size, 1) array_send.send(output) assert(np.all(output + 1 == 2)) # receive the timestamps from the other process recv_timestamp, finish_timestamp = result_recv.recv() print_results(send_type, start_timestamp, recv_timestamp, finish_timestamp) recv_process.join() # process to receive an array using a shared memory mapped file # for real use, this would require creating some protocol to specify the # array's data type and shape class MemmapReceive(multiprocessing.Process): def __init__(self, path_recv, result_send): super(MemmapReceive, self).__init__() self.path_recv = path_recv self.result_send = result_send def run(self): # block until the memmap file path is received from the other process path = self.path_recv.recv() # create a memmap array using the received file path array = np.memmap(path, 'i8', 'r+') recv_timestamp = time.time() # perform an operation on the array array += 1 finish_timestamp = time.time() assert(np.all(array == 2)) # send the timing results back to the other process self.result_send.send((recv_timestamp, finish_timestamp)) def read_and_send_memmap(send_type, array_size): # create a multiprocessing Pipe that will be used to send the memmap # file path to the receiving process path_recv, path_send = multiprocessing.Pipe(False) result_recv, result_send = multiprocessing.Pipe(False) # start the receiving process and pause to allow it to start up recv_process = MemmapReceive(path_recv, result_send) recv_process.start() time.sleep(0.15) with tables.open_file('test.h5', 'r') as fobj: array = fobj.get_node('/', 'test') start_timestamp = time.time() # memmap a file as a NumPy array in 'overwrite' mode output = np.memmap('/tmp/array1', 'i8', 'w+', shape=(array_size, )) # read an array from a PyTables file into the memmory mapped array array.read(0, array_size, 1, out=output) # use a multiprocessing.Pipe to send the file's path to the receiving # process path_send.send('/tmp/array1') # receive the timestamps from the other process recv_timestamp, finish_timestamp = result_recv.recv() # because 'output' is shared between processes, all elements should now # be equal to 2 assert(np.all(output == 2)) print_results(send_type, start_timestamp, recv_timestamp, finish_timestamp) recv_process.join() # process to receive an array using a socket # for real use, this would require creating some protocol to specify the # array's data type and shape class SocketReceive(multiprocessing.Process): def __init__(self, socket_family, address, result_send, array_nbytes): super(SocketReceive, self).__init__() self.socket_family = socket_family self.address = address self.result_send = result_send self.array_nbytes = array_nbytes def run(self): # create the socket, listen for a connection and use select to block # until a connection is made sock = socket.socket(self.socket_family, socket.SOCK_STREAM) sock.bind(self.address) sock.listen(1) readable, _, _ = select.select([sock], [], []) # accept the connection and read the sent data into a bytearray connection = sock.accept()[0] recv_buffer = bytearray(self.array_nbytes) view = memoryview(recv_buffer) bytes_recv = 0 while bytes_recv < self.array_nbytes: bytes_recv += connection.recv_into(view[bytes_recv:]) # convert the bytearray into a NumPy array array = np.frombuffer(recv_buffer, dtype='i8') recv_timestamp = time.time() # perform an operation on the received array array += 1 finish_timestamp = time.time() assert(np.all(array == 2)) # send the timestamps back to the originating process self.result_send.send((recv_timestamp, finish_timestamp)) connection.close() sock.close() def unix_socket_address(): # create a Unix domain address in the abstract namespace # this will only work on Linux return b'\x00' + os.urandom(5) def ipv4_socket_address(): # create an IPv4 socket address return ('127.0.0.1', random.randint(9000, 10000)) def read_and_send_socket(send_type, array_size, array_bytes, address_func, socket_family): address = address_func() # start the receiving process and pause to allow it to start up result_recv, result_send = multiprocessing.Pipe(False) recv_process = SocketReceive(socket_family, address, result_send, array_bytes) recv_process.start() time.sleep(0.15) with tables.open_file('test.h5', 'r') as fobj: array = fobj.get_node('/', 'test') start_timestamp = time.time() # connect to the receiving process' socket sock = socket.socket(socket_family, socket.SOCK_STREAM) sock.connect(address) # read the array from the PyTables file and send its # data buffer to the receiving process output = array.read(0, array_size, 1) sock.send(output.data) assert(np.all(output + 1 == 2)) # receive the timestamps from the other process recv_timestamp, finish_timestamp = result_recv.recv() sock.close() print_results(send_type, start_timestamp, recv_timestamp, finish_timestamp) recv_process.join() def print_results(send_type, start_timestamp, recv_timestamp, finish_timestamp): msg = 'type: {0}\t receive: {1:5.5f}, add:{2:5.5f}, total: {3:5.5f}' print(msg.format(send_type, recv_timestamp - start_timestamp, finish_timestamp - recv_timestamp, finish_timestamp - start_timestamp)) if __name__ == '__main__': random.seed(os.urandom(2)) array_num_bytes = [int(x) for x in [1e5, 1e6, 1e7, 1e8]] for array_bytes in array_num_bytes: array_size = int(array_bytes // 8) create_file(array_size) read_and_send_pipe('multiproc.Pipe', array_size) read_and_send_memmap('memmap ', array_size) # comment out this line to run on an OS other than Linux read_and_send_socket('Unix socket', array_size, array_bytes, unix_socket_address, socket.AF_UNIX) read_and_send_socket('IPv4 socket', array_size, array_bytes, ipv4_socket_address, socket.AF_INET) print() PyTables-v.3.1.1/examples/multiprocess_access_queues.py000066400000000000000000000141611231437614300233430ustar00rootroot00000000000000"""Example showing how to access a PyTables file from multiple processes using queues.""" from __future__ import print_function import Queue as queue import multiprocessing import os import random import time import numpy import tables # this creates an HDF5 file with one array containing n rows def make_file(file_path, n): with tables.open_file(file_path, 'w') as fobj: array = fobj.create_carray('/', 'array', tables.Int64Atom(), (n, n)) for i in range(n): array[i, :] = i # All access to the file goes through a single instance of this class. # It contains several queues that are used to communicate with other # processes. # The read_queue is used for requests to read data from the HDF5 file. # A list of result_queues is used to send data back to client processes. # The write_queue is used for requests to modify the HDF5 file. # One end of a pipe (shutdown) is used to signal the process to terminate. class FileAccess(multiprocessing.Process): def __init__(self, h5_path, read_queue, result_queues, write_queue, shutdown): self.h5_path = h5_path self.read_queue = read_queue self.result_queues = result_queues self.write_queue = write_queue self.shutdown = shutdown self.block_period = .01 super(FileAccess, self).__init__() def run(self): self.h5_file = tables.open_file(self.h5_path, 'r+') self.array = self.h5_file.get_node('/array') another_loop = True while another_loop: # Check if the process has received the shutdown signal. if self.shutdown.poll(): another_loop = False # Check for any data requests in the read_queue. try: row_num, proc_num = self.read_queue.get( True, self.block_period) # look up the appropriate result_queue for this data processor # instance result_queue = self.result_queues[proc_num] print('processor {0} reading from row {1}'.format(proc_num, row_num)) result_queue.put(self.read_data(row_num)) another_loop = True except queue.Empty: pass # Check for any write requests in the write_queue. try: row_num, data = self.write_queue.get(True, self.block_period) print('writing row', row_num) self.write_data(row_num, data) another_loop = True except queue.Empty: pass # close the HDF5 file before shutting down self.h5_file.close() def read_data(self, row_num): return self.array[row_num, :] def write_data(self, row_num, data): self.array[row_num, :] = data # This class represents a process that does work by reading and writing to the # HDF5 file. It does this by sending requests to the FileAccess class instance # through its read and write queues. The data results are sent back through # the result_queue. # Its actions are logged to a text file. class DataProcessor(multiprocessing.Process): def __init__(self, read_queue, result_queue, write_queue, proc_num, array_size, output_file): self.read_queue = read_queue self.result_queue = result_queue self.write_queue = write_queue self.proc_num = proc_num self.array_size = array_size self.output_file = output_file super(DataProcessor, self).__init__() def run(self): self.output_file = open(self.output_file, 'w') # read a random row from the file row_num = random.randint(0, self.array_size - 1) self.read_queue.put((row_num, self.proc_num)) self.output_file.write(str(row_num) + '\n') self.output_file.write(str(self.result_queue.get()) + '\n') # modify a random row to equal 11 * (self.proc_num + 1) row_num = random.randint(0, self.array_size - 1) new_data = (numpy.zeros((1, self.array_size), 'i8') + 11 * (self.proc_num + 1)) self.write_queue.put((row_num, new_data)) # pause, then read the modified row time.sleep(0.015) self.read_queue.put((row_num, self.proc_num)) self.output_file.write(str(row_num) + '\n') self.output_file.write(str(self.result_queue.get()) + '\n') self.output_file.close() # this function starts the FileAccess class instance and # sets up all the queues used to communicate with it def make_queues(num_processors): read_queue = multiprocessing.Queue() write_queue = multiprocessing.Queue() shutdown_recv, shutdown_send = multiprocessing.Pipe(False) result_queues = [multiprocessing.Queue() for i in range(num_processors)] file_access = FileAccess(file_path, read_queue, result_queues, write_queue, shutdown_recv) file_access.start() return read_queue, result_queues, write_queue, shutdown_send if __name__ == '__main__': file_path = 'test.h5' n = 10 make_file(file_path, n) num_processors = 3 (read_queue, result_queues, write_queue, shutdown_send) = make_queues(num_processors) processors = [] output_files = [] for i in range(num_processors): result_queue = result_queues[i] output_file = str(i) processor = DataProcessor(read_queue, result_queue, write_queue, i, n, output_file) processors.append(processor) output_files.append(output_file) # start all DataProcessor instances for processor in processors: processor.start() # wait for all DataProcessor instances to finish for processor in processors: processor.join() # shut down the FileAccess instance shutdown_send.send(0) # print out contents of log files and delete them print() for output_file in output_files: print() print('contents of log file {0}'.format(output_file)) print(open(output_file, 'r').read()) os.remove(output_file) os.remove('test.h5') PyTables-v.3.1.1/examples/nested-tut.py000066400000000000000000000071661231437614300200050ustar00rootroot00000000000000"""Small example showing the use of nested types in PyTables. The program creates an output file, 'nested-tut.h5'. You can view it with ptdump or any HDF5 generic utility. :Author: F. Alted :Date: 2005/06/10 """ from __future__ import print_function import numpy import tables #'-**-**-**-**- The sample nested class description -**-**-**-**-**-' class Info(tables.IsDescription): """A sub-structure of Test""" _v_pos = 2 # The position in the whole structure name = tables.StringCol(10) value = tables.Float64Col(pos=0) colors = tables.Enum(['red', 'green', 'blue']) class NestedDescr(tables.IsDescription): """A description that has several nested columns.""" color = tables.EnumCol(colors, 'red', base='uint32') info1 = Info() class info2(tables.IsDescription): _v_pos = 1 name = tables.StringCol(10) value = tables.Float64Col(pos=0) class info3(tables.IsDescription): x = tables.Float64Col(dflt=1) y = tables.UInt8Col(dflt=1) print() print('-**-**-**-**-**-**- file creation -**-**-**-**-**-**-**-') filename = "nested-tut.h5" print("Creating file:", filename) fileh = tables.open_file(filename, "w") print() print('-**-**-**-**-**- nested table creation -**-**-**-**-**-') table = fileh.create_table(fileh.root, 'table', NestedDescr) # Fill the table with some rows row = table.row for i in range(10): row['color'] = colors[['red', 'green', 'blue'][i % 3]] row['info1/name'] = "name1-%s" % i row['info2/name'] = "name2-%s" % i row['info2/info3/y'] = i # All the rest will be filled with defaults row.append() table.flush() # flush the row buffer to disk print(repr(table.nrows)) nra = table[::4] print(repr(nra)) # Append some additional rows table.append(nra) print(repr(table.nrows)) # Create a new table table2 = fileh.create_table(fileh.root, 'table2', nra) print(repr(table2[:])) # Read also the info2/name values with color == colors.red names = [x['info2/name'] for x in table if x['color'] == colors.red] print() print("**** info2/name elements satisfying color == 'red':", repr(names)) print() print('-**-**-**-**-**-**- table data reading & selection -**-**-**-**-**-') # Read the data print() print("**** table data contents:\n", table[:]) print() print("**** table.info2 data contents:\n", repr(table.cols.info2[1:5])) print() print("**** table.info2.info3 data contents:\n", repr(table.cols.info2.info3[1:5])) print("**** _f_col() ****") print(repr(table.cols._f_col('info2'))) print(repr(table.cols._f_col('info2/info3/y'))) print() print('-**-**-**-**-**-**- table metadata -**-**-**-**-**-') # Read description metadata print() print("**** table description (short):\n", repr(table.description)) print() print("**** more from manual, period ***") print(repr(table.description.info1)) print(repr(table.description.info2.info3)) print(repr(table.description._v_nested_names)) print(repr(table.description.info1._v_nested_names)) print() print("**** now some for nested records, take that ****") print(repr(table.description._v_nested_descr)) print(repr(numpy.rec.array(None, shape=0, dtype=table.description._v_nested_descr))) print(repr(numpy.rec.array(None, shape=0, dtype=table.description.info2._v_nested_descr))) print() print("**** and some iteration over descriptions, too ****") for coldescr in table.description._f_walk(): print("column-->", coldescr) print() print("**** info2 sub-structure description:\n", table.description.info2) print() print("**** table representation (long form):\n", repr(table)) # Remember to always close the file fileh.close() PyTables-v.3.1.1/examples/nested1.py000066400000000000000000000045451231437614300172520ustar00rootroot00000000000000# Example to show how nested types can be dealed with PyTables # F. Alted 2005/05/27 from __future__ import print_function import random import tables fileout = "nested1.h5" # An example of enumerated structure colors = tables.Enum(['red', 'green', 'blue']) def read(file): fileh = tables.open_file(file, "r") print("table (short)-->", fileh.root.table) print("table (long)-->", repr(fileh.root.table)) print("table (contents)-->", repr(fileh.root.table[:])) fileh.close() def write(file, desc, indexed): fileh = tables.open_file(file, "w") table = fileh.create_table(fileh.root, 'table', desc) for colname in indexed: table.colinstances[colname].create_index() row = table.row for i in range(10): row['x'] = i row['y'] = 10.2 - i row['z'] = i row['color'] = colors[random.choice(['red', 'green', 'blue'])] row['info/name'] = "name%s" % i row['info/info2/info3/z4'] = i # All the rest will be filled with defaults row.append() fileh.close() # The sample nested class description class Info(tables.IsDescription): _v_pos = 2 Name = tables.UInt32Col() Value = tables.Float64Col() class Test(tables.IsDescription): """A description that has several columns.""" x = tables.Int32Col(shape=2, dflt=0, pos=0) y = tables.Float64Col(dflt=1.2, shape=(2, 3)) z = tables.UInt8Col(dflt=1) color = tables.EnumCol(colors, 'red', base='uint32', shape=(2,)) Info = Info() class info(tables.IsDescription): _v_pos = 1 name = tables.StringCol(10) value = tables.Float64Col(pos=0) y2 = tables.Float64Col(dflt=1, shape=(2, 3), pos=1) z2 = tables.UInt8Col(dflt=1) class info2(tables.IsDescription): y3 = tables.Float64Col(dflt=1, shape=(2, 3)) z3 = tables.UInt8Col(dflt=1) name = tables.StringCol(10) value = tables.EnumCol(colors, 'blue', base='uint32', shape=(1,)) class info3(tables.IsDescription): name = tables.StringCol(10) value = tables.Time64Col() y4 = tables.Float64Col(dflt=1, shape=(2, 3)) z4 = tables.UInt8Col(dflt=1) # Write the file and read it write(fileout, Test, ['info/info2/z3']) read(fileout) print("You can have a look at '%s' output file now." % fileout) PyTables-v.3.1.1/examples/objecttree.py000066400000000000000000000027631231437614300200350ustar00rootroot00000000000000from __future__ import print_function import tables class Particle(tables.IsDescription): identity = tables.StringCol(itemsize=22, dflt=" ", pos=0) # character String idnumber = tables.Int16Col(dflt=1, pos=1) # short integer speed = tables.Float32Col(dflt=1, pos=1) # single-precision # Open a file in "w"rite mode fileh = tables.open_file("objecttree.h5", mode="w") # Get the HDF5 root group root = fileh.root # Create the groups: group1 = fileh.create_group(root, "group1") group2 = fileh.create_group(root, "group2") # Now, create an array in root group array1 = fileh.create_array( root, "array1", ["string", "array"], "String array") # Create 2 new tables in group1 table1 = fileh.create_table(group1, "table1", Particle) table2 = fileh.create_table("/group2", "table2", Particle) # Create the last table in group2 array2 = fileh.create_array("/group1", "array2", [1, 2, 3, 4]) # Now, fill the tables: for table in (table1, table2): # Get the record object associated with the table: row = table.row # Fill the table with 10 records for i in range(10): # First, assign the values to the Particle record row['identity'] = 'This is particle: %2d' % (i) row['idnumber'] = i row['speed'] = i * 2. # This injects the Record values row.append() # Flush the table buffers table.flush() # Finally, close the file (this also will flush all the remaining buffers!) fileh.close() PyTables-v.3.1.1/examples/particles.py000066400000000000000000000101701231437614300176640ustar00rootroot00000000000000"""Beware! you need PyTables >= 2.3 to run this script!""" from __future__ import print_function from time import time # use clock for Win import numpy as np import tables # NEVENTS = 10000 NEVENTS = 20000 MAX_PARTICLES_PER_EVENT = 100 # Particle description class Particle(tables.IsDescription): # event_id = tables.Int32Col(pos=1, indexed=True) # event id (indexed) event_id = tables.Int32Col(pos=1) # event id (not indexed) particle_id = tables.Int32Col(pos=2) # particle id in the event parent_id = tables.Int32Col(pos=3) # the id of the parent # particle (negative # values means no parent) momentum = tables.Float64Col(shape=3, pos=4) # momentum of the particle mass = tables.Float64Col(pos=5) # mass of the particle # Create a new table for events t1 = time() print("Creating a table with %s entries aprox.. Wait please..." % (int(NEVENTS * (MAX_PARTICLES_PER_EVENT / 2.)))) fileh = tables.open_file("particles-pro.h5", mode="w") group = fileh.create_group(fileh.root, "events") table = fileh.create_table(group, 'table', Particle, "A table", tables.Filters(0)) # Choose this line if you want data compression # table = fileh.create_table(group, 'table', Particle, "A table", Filters(1)) # Fill the table with events np.random.seed(1) # In order to have reproducible results particle = table.row for i in range(NEVENTS): for j in range(np.random.randint(0, MAX_PARTICLES_PER_EVENT)): particle['event_id'] = i particle['particle_id'] = j particle['parent_id'] = j - 10 # 10 root particles (max) particle['momentum'] = np.random.normal(5.0, 2.0, size=3) particle['mass'] = np.random.normal(500.0, 10.0) # This injects the row values. particle.append() table.flush() print("Added %s entries --- Time: %s sec" % (table.nrows, round((time() - t1), 3))) t1 = time() print("Creating index...") table.cols.event_id.create_index(optlevel=0, _verbose=True) print("Index created --- Time: %s sec" % (round((time() - t1), 3))) # Add the number of events as an attribute table.attrs.nevents = NEVENTS fileh.close() # Open the file en read only mode and start selections print("Selecting events...") fileh = tables.open_file("particles-pro.h5", mode="r") table = fileh.root.events.table print("Particles in event 34:", end=' ') nrows = 0 t1 = time() for row in table.where("event_id == 34"): nrows += 1 print(nrows) print("Done --- Time:", round((time() - t1), 3), "sec") print("Root particles in event 34:", end=' ') nrows = 0 t1 = time() for row in table.where("event_id == 34"): if row['parent_id'] < 0: nrows += 1 print(nrows) print("Done --- Time:", round((time() - t1), 3), "sec") print("Sum of masses of root particles in event 34:", end=' ') smass = 0.0 t1 = time() for row in table.where("event_id == 34"): if row['parent_id'] < 0: smass += row['mass'] print(smass) print("Done --- Time:", round((time() - t1), 3), "sec") print( "Sum of masses of daughter particles for particle 3 in event 34:", end=' ') smass = 0.0 t1 = time() for row in table.where("event_id == 34"): if row['parent_id'] == 3: smass += row['mass'] print(smass) print("Done --- Time:", round((time() - t1), 3), "sec") print("Sum of module of momentum for particle 3 in event 34:", end=' ') smomentum = 0.0 t1 = time() # for row in table.where("(event_id == 34) & ((parent_id) == 3)"): for row in table.where("event_id == 34"): if row['parent_id'] == 3: smomentum += np.sqrt(np.add.reduce(row['momentum'] ** 2)) print(smomentum) print("Done --- Time:", round((time() - t1), 3), "sec") # This is the same than above, but using generator expressions # Python >= 2.4 needed here! print("Sum of module of momentum for particle 3 in event 34 (2):", end=' ') t1 = time() print(sum(np.sqrt(np.add.reduce(row['momentum'] ** 2)) for row in table.where("event_id == 34") if row['parent_id'] == 3)) print("Done --- Time:", round((time() - t1), 3), "sec") fileh.close() PyTables-v.3.1.1/examples/read_array_out_arg.py000066400000000000000000000034401231437614300215310ustar00rootroot00000000000000# This script compares reading from an array in a loop using the # tables.Array.read method. In the first case, read is used without supplying # an 'out' argument, which causes a new output buffer to be pre-allocated # with each call. In the second case, the buffer is created once, and then # reused. from __future__ import division from __future__ import print_function from __future__ import unicode_literals import time import numpy as np import tables def create_file(array_size): array = np.ones(array_size, dtype='i8') with tables.open_file('test.h5', 'w') as fobj: array = fobj.create_array('/', 'test', array) print('file created, size: {0} MB'.format(array.size_on_disk / 1e6)) def standard_read(array_size): N = 10 with tables.open_file('test.h5', 'r') as fobj: array = fobj.get_node('/', 'test') start = time.time() for i in range(N): output = array.read(0, array_size, 1) end = time.time() assert(np.all(output == 1)) print('standard read \t {0:5.5f}'.format((end - start) / N)) def pre_allocated_read(array_size): N = 10 with tables.open_file('test.h5', 'r') as fobj: array = fobj.get_node('/', 'test') start = time.time() output = np.empty(array_size, 'i8') for i in range(N): array.read(0, array_size, 1, out=output) end = time.time() assert(np.all(output == 1)) print('pre-allocated read\t {0:5.5f}'.format((end - start) / N)) if __name__ == '__main__': array_num_bytes = [int(x) for x in [1e5, 1e6, 1e7, 1e8]] for array_bytes in array_num_bytes: array_size = int(array_bytes // 8) create_file(array_size) standard_read(array_size) pre_allocated_read(array_size) print() PyTables-v.3.1.1/examples/split.py000066400000000000000000000017141231437614300170350ustar00rootroot00000000000000"""Use the H5FD_SPLIT driver to store metadata and raw data in separate files. In this example, we store the metadata file in the current directory and the raw data file in a subdirectory. """ import os import errno import numpy import tables FNAME = "split" DRIVER = "H5FD_SPLIT" RAW_DIR = "raw" DRIVER_PROPS = { "driver_split_raw_ext": os.path.join(RAW_DIR, "%s-r.h5") } DATA_SHAPE = (2, 10) class FooBar(tables.IsDescription): tag = tables.StringCol(16) data = tables.Float32Col(shape=DATA_SHAPE) try: os.mkdir(RAW_DIR) except OSError as e: if e.errno == errno.EEXIST: pass with tables.open_file(FNAME, mode="w", driver=DRIVER, **DRIVER_PROPS) as f: group = f.create_group("/", "foo", "foo desc") table = f.create_table(group, "bar", FooBar, "bar desc") for i in range(5): table.row["tag"] = "t%d" % i table.row["data"] = numpy.random.random_sample(DATA_SHAPE) table.row.append() table.flush() PyTables-v.3.1.1/examples/table-tree.py000066400000000000000000000230131231437614300177220ustar00rootroot00000000000000from __future__ import print_function import numpy as np import tables class Particle(tables.IsDescription): ADCcount = tables.Int16Col() # signed short integer TDCcount = tables.UInt8Col() # unsigned byte grid_i = tables.Int32Col() # integer grid_j = tables.Int32Col() # integer idnumber = tables.Int64Col() # signed long long name = tables.StringCol(16, dflt="") # 16-character String pressure = tables.Float32Col(shape=2) # float (single-precision) temperature = tables.Float64Col() # double (double-precision) Particle2 = { # You can also use any of the atom factories, i.e. the one which # accepts a PyTables type. "ADCcount": tables.Col.from_type("int16"), # signed short integer "TDCcount": tables.Col.from_type("uint8"), # unsigned byte "grid_i": tables.Col.from_type("int32"), # integer "grid_j": tables.Col.from_type("int32"), # integer "idnumber": tables.Col.from_type("int64"), # signed long long "name": tables.Col.from_kind("string", 16), # 16-character String "pressure": tables.Col.from_type("float32", (2,)), # float # (single-precision) "temperature": tables.Col.from_type("float64"), # double # (double-precision) } # The name of our HDF5 filename filename = "table-tree.h5" # Open a file in "w"rite mode h5file = tables.open_file(filename, mode="w") # Create a new group under "/" (root) group = h5file.create_group("/", 'detector') # Create one table on it # table = h5file.create_table(group, 'table', Particle, "Title example") # You can choose creating a Table from a description dictionary if you wish table = h5file.create_table(group, 'table', Particle2, "Title example") # Create a shortcut to the table record object particle = table.row # Fill the table with 10 particles for i in range(10): # First, assign the values to the Particle record particle['name'] = 'Particle: %6d' % (i) particle['TDCcount'] = i % 256 particle['ADCcount'] = (i * 256) % (1 << 16) particle['grid_i'] = i particle['grid_j'] = 10 - i particle['pressure'] = [float(i * i), float(i * 2)] particle['temperature'] = float(i ** 2) particle['idnumber'] = i * (2 ** 34) # This exceeds integer range # This injects the Record values. particle.append() # Flush the buffers for table table.flush() # Get actual data from table. We are interested in column pressure. pressure = [p['pressure'] for p in table.iterrows()] print("Last record ==>", p) print("Column pressure ==>", np.array(pressure)) print("Total records in table ==> ", len(pressure)) print() # Create a new group to hold new arrays gcolumns = h5file.create_group("/", "columns") print("columns ==>", gcolumns, pressure) # Create an array with this info under '/columns' having a 'list' flavor h5file.create_array(gcolumns, 'pressure', pressure, "Pressure column") print("gcolumns.pressure type ==> ", gcolumns.pressure.atom.dtype) # Do the same with TDCcount, but with a numpy object TDC = [p['TDCcount'] for p in table.iterrows()] print("TDC ==>", TDC) print("TDC shape ==>", np.array(TDC).shape) h5file.create_array('/columns', 'TDC', np.array(TDC), "TDCcount column") # Do the same with name column names = [p['name'] for p in table.iterrows()] print("names ==>", names) h5file.create_array('/columns', 'name', names, "Name column") # This works even with homogeneous tuples or lists (!) print("gcolumns.name shape ==>", gcolumns.name.shape) print("gcolumns.name type ==> ", gcolumns.name.atom.dtype) print("Table dump:") for p in table.iterrows(): print(p) # Save a recarray object under detector r = np.rec.array("a" * 300, formats='f4,3i4,a5,i2', shape=3) recarrt = h5file.create_table("/detector", 'recarray', r, "RecArray example") r2 = r[0:3:2] # Change the byteorder property recarrt = h5file.create_table("/detector", 'recarray2', r2, "Non-contiguous recarray") print(recarrt) print() print(h5file.root.detector.table.description) # Close the file h5file.close() # sys.exit() # Reopen it in append mode h5file = tables.open_file(filename, "a") # Ok. let's start browsing the tree from this filename print("Reading info from filename:", h5file.filename) print() # Firstly, list all the groups on tree print("Groups in file:") for group in h5file.walk_groups("/"): print(group) print() # List all the nodes (Group and Leaf objects) on tree print("List of all nodes in file:") print(h5file) # And finally, only the Arrays (Array objects) print("Arrays in file:") for array in h5file.walk_nodes("/", classname="Array"): print(array) print() # Get group /detector and print some info on it detector = h5file.get_node("/detector") print("detector object ==>", detector) # List only leaves on detector print("Leaves in group", detector, ":") for leaf in h5file.list_nodes("/detector", 'Leaf'): print(leaf) print() # List only tables on detector print("Tables in group", detector, ":") for leaf in h5file.list_nodes("/detector", 'Table'): print(leaf) print() # List only arrays on detector (there should be none!) print("Arrays in group", detector, ":") for leaf in h5file.list_nodes("/detector", 'Array'): print(leaf) print() # Get "/detector" Group object group = h5file.root.detector print("/detector ==>", group) # Get the "/detector/table table = h5file.get_node("/detector/table") print("/detector/table ==>", table) # Get metadata from table print("Object:", table) print("Table name:", table.name) print("Table title:", table.title) print("Rows saved on table: %d" % (table.nrows)) print("Variable names on table with their type:") for name in table.colnames: print(" ", name, ':=', table.coldtypes[name]) print() # Read arrays in /columns/names and /columns/pressure # Get the object in "/columns pressure" pressureObject = h5file.get_node("/columns", "pressure") # Get some metadata on this object print("Info on the object:", pressureObject) print(" shape ==>", pressureObject.shape) print(" title ==>", pressureObject.title) print(" type ==> ", pressureObject.atom.dtype) print(" byteorder ==> ", pressureObject.byteorder) # Read the pressure actual data pressureArray = pressureObject.read() print(" data type ==>", type(pressureArray)) print(" data ==>", pressureArray) print() # Get the object in "/columns/names" nameObject = h5file.root.columns.name # Get some metadata on this object print("Info on the object:", nameObject) print(" shape ==>", nameObject.shape) print(" title ==>", nameObject.title) print(" type ==> " % nameObject.atom.dtype) # Read the 'name' actual data nameArray = nameObject.read() print(" data type ==>", type(nameArray)) print(" data ==>", nameArray) # Print the data for both arrays print("Data on arrays name and pressure:") for i in range(pressureObject.shape[0]): print("".join(nameArray[i]), "-->", pressureArray[i]) print() # Finally, append some new records to table table = h5file.root.detector.table # Append 5 new particles to table (yes, tables can be enlarged!) particle = table.row for i in range(10, 15): # First, assign the values to the Particle record particle['name'] = 'Particle: %6d' % (i) particle['TDCcount'] = i % 256 particle['ADCcount'] = (i * 256) % (1 << 16) particle['grid_i'] = i particle['grid_j'] = 10 - i particle['pressure'] = [float(i * i), float(i * 2)] particle['temperature'] = float(i ** 2) particle['idnumber'] = i * (2 ** 34) # This exceeds integer range # This injects the Row values. particle.append() # Flush this table table.flush() print("Columns name and pressure on expanded table:") # Print some table columns, for comparison with array data for p in table: print(p['name'], '-->', p['pressure']) print() # Put several flavors oldflavor = table.flavor print(table.read(field="ADCcount")) table.flavor = "numpy" print(table.read(field="ADCcount")) table.flavor = oldflavor print(table.read(0, 0, 1, "name")) table.flavor = "python" print(table.read(0, 0, 1, "name")) table.flavor = oldflavor print(table.read(0, 0, 2, "pressure")) table.flavor = "python" print(table.read(0, 0, 2, "pressure")) table.flavor = oldflavor # Several range selections print("Extended slice in selection: [0:7:6]") print(table.read(0, 7, 6)) print("Single record in selection: [1]") print(table.read(1)) print("Last record in selection: [-1]") print(table.read(-1)) print("Two records before the last in selection: [-3:-1]") print(table.read(-3, -1)) # Print a recarray in table form table = h5file.root.detector.recarray2 print("recarray2:", table) print(" nrows:", table.nrows) print(" byteorder:", table.byteorder) print(" coldtypes:", table.coldtypes) print(" colnames:", table.colnames) print(table.read()) for p in table.iterrows(): print(p['f1'], '-->', p['f2']) print() result = [rec['f1'] for rec in table if rec.nrow < 2] print(result) # Test the File.rename_node() method # h5file.rename_node(h5file.root.detector.recarray2, "recarray3") h5file.rename_node(table, "recarray3") # Delete a Leaf from the HDF5 tree h5file.remove_node(h5file.root.detector.recarray3) # Delete the detector group and its leaves recursively # h5file.remove_node(h5file.root.detector, recursive=1) # Create a Group and then remove it h5file.create_group(h5file.root, "newgroup") h5file.remove_node(h5file.root, "newgroup") h5file.rename_node(h5file.root.columns, "newcolumns") print(h5file) # Close this file h5file.close() PyTables-v.3.1.1/examples/table1.py000066400000000000000000000043171231437614300170540ustar00rootroot00000000000000from __future__ import print_function import tables class Particle(tables.IsDescription): name = tables.StringCol(16, pos=1) # 16-character String lati = tables.Int32Col(pos=2) # integer longi = tables.Int32Col(pos=3) # integer pressure = tables.Float32Col(pos=4) # float (single-precision) temperature = tables.Float64Col(pos=5) # double (double-precision) # Open a file in "w"rite mode fileh = tables.open_file("table1.h5", mode="w") # Create a new group group = fileh.create_group(fileh.root, "newgroup") # Create a new table in newgroup group table = fileh.create_table(group, 'table', Particle, "A table", tables.Filters(1)) particle = table.row # Fill the table with 10 particles for i in range(10): # First, assign the values to the Particle record particle['name'] = 'Particle: %6d' % (i) particle['lati'] = i particle['longi'] = 10 - i particle['pressure'] = float(i * i) particle['temperature'] = float(i ** 2) # This injects the row values. particle.append() # We need to flush the buffers in table in order to get an # accurate number of records on it. table.flush() # Add a couple of user attrs table.attrs.user_attr1 = 1.023 table.attrs.user_attr2 = "This is the second user attr" # Append several rows in only one call table.append([("Particle: 10", 10, 0, 10 * 10, 10 ** 2), ("Particle: 11", 11, -1, 11 * 11, 11 ** 2), ("Particle: 12", 12, -2, 12 * 12, 12 ** 2)]) group = fileh.root.newgroup print("Nodes under group", group, ":") for node in fileh.list_nodes(group): print(node) print() print("Leaves everywhere in file", fileh.filename, ":") for leaf in fileh.walk_nodes(classname="Leaf"): print(leaf) print() table = fileh.root.newgroup.table print("Object:", table) print("Table name: %s. Table title: %s" % (table.name, table.title)) print("Rows saved on table: %d" % (table.nrows)) print("Variable names on table with their type:") for name in table.colnames: print(" ", name, ':=', table.coldtypes[name]) print("Table contents:") for row in table: print(row[:]) print("Associated recarray:") print(table.read()) # Finally, close the file fileh.close() PyTables-v.3.1.1/examples/table2.py000066400000000000000000000034111231437614300170470ustar00rootroot00000000000000# This shows how to use the cols accessors for table columns from __future__ import print_function import tables class Particle(tables.IsDescription): name = tables.StringCol(16, pos=1) # 16-character String lati = tables.Int32Col(pos=2) # integer longi = tables.Int32Col(pos=3) # integer vector = tables.Int32Col(shape=(2,), pos=4) # Integer matrix2D = tables.Float64Col(shape=(2, 2), pos=5) # double (double-precision) # Open a file in "w"rite mode fileh = tables.open_file("table2.h5", mode="w") table = fileh.create_table(fileh.root, 'table', Particle, "A table") # Append several rows in only one call table.append( [("Particle: 10", 10, 0, (10 * 9, 1), [[10 ** 2, 11 * 3]] * 2), ("Particle: 11", 11, -1, (11 * 10, 2), [[11 ** 2, 10 * 3]] * 2), ("Particle: 12", 12, -2, (12 * 11, 3), [[12 ** 2, 9 * 3]] * 2), ("Particle: 13", 13, -3, (13 * 11, 4), [[13 ** 2, 8 * 3]] * 2), ("Particle: 14", 14, -4, (14 * 11, 5), [[14 ** 2, 7 * 3]] * 2)]) print("str(Cols)-->", table.cols) print("repr(Cols)-->", repr(table.cols)) print("Column handlers:") for name in table.colnames: print(table.cols._f_col(name)) print("Select table.cols.name[1]-->", table.cols.name[1]) print("Select table.cols.name[1:2]-->", table.cols.name[1:2]) print("Select table.cols.name[:]-->", table.cols.name[:]) print("Select table.cols._f_col('name')[:]-->", table.cols._f_col('name')[:]) print("Select table.cols.lati[1]-->", table.cols.lati[1]) print("Select table.cols.lati[1:2]-->", table.cols.lati[1:2]) print("Select table.cols.vector[:]-->", table.cols.vector[:]) print("Select table.cols['matrix2D'][:]-->", table.cols.matrix2D[:]) fileh.close() PyTables-v.3.1.1/examples/table3.py000066400000000000000000000032701231437614300170530ustar00rootroot00000000000000# This is an example on how to use complex columns from __future__ import print_function import tables class Particle(tables.IsDescription): name = tables.StringCol(16, pos=1) # 16-character String lati = tables.ComplexCol(itemsize=16, pos=2) longi = tables.ComplexCol(itemsize=8, pos=3) vector = tables.ComplexCol(itemsize=8, shape=(2,), pos=4) matrix2D = tables.ComplexCol(itemsize=16, shape=(2, 2), pos=5) # Open a file in "w"rite mode fileh = tables.open_file("table3.h5", mode="w") table = fileh.create_table(fileh.root, 'table', Particle, "A table") # Append several rows in only one call table.append([ ("Particle: 10", 10j, 0, (10 * 9 + 1j, 1), [[10 ** 2j, 11 * 3]] * 2), ("Particle: 11", 11j, -1, (11 * 10 + 2j, 2), [[11 ** 2j, 10 * 3]] * 2), ("Particle: 12", 12j, -2, (12 * 11 + 3j, 3), [[12 ** 2j, 9 * 3]] * 2), ("Particle: 13", 13j, -3, (13 * 11 + 4j, 4), [[13 ** 2j, 8 * 3]] * 2), ("Particle: 14", 14j, -4, (14 * 11 + 5j, 5), [[14 ** 2j, 7 * 3]] * 2) ]) print("str(Cols)-->", table.cols) print("repr(Cols)-->", repr(table.cols)) print("Column handlers:") for name in table.colnames: print(table.cols._f_col(name)) print("Select table.cols.name[1]-->", table.cols.name[1]) print("Select table.cols.name[1:2]-->", table.cols.name[1:2]) print("Select table.cols.name[:]-->", table.cols.name[:]) print("Select table.cols._f_col('name')[:]-->", table.cols._f_col('name')[:]) print("Select table.cols.lati[1]-->", table.cols.lati[1]) print("Select table.cols.lati[1:2]-->", table.cols.lati[1:2]) print("Select table.cols.vector[:]-->", table.cols.vector[:]) print("Select table.cols['matrix2D'][:]-->", table.cols.matrix2D[:]) fileh.close() PyTables-v.3.1.1/examples/tutorial1-1.py000066400000000000000000000067221231437614300177700ustar00rootroot00000000000000"""Small but quite comprehensive example showing the use of PyTables. The program creates an output file, 'tutorial1.h5'. You can view it with any HDF5 generic utility. """ from __future__ import print_function import numpy as np import tables #'-**-**-**-**-**-**- user record definition -**-**-**-**-**-**-**-' # Define a user record to characterize some kind of particles class Particle(tables.IsDescription): name = tables.StringCol(16) # 16-character String idnumber = tables.Int64Col() # Signed 64-bit integer ADCcount = tables.UInt16Col() # Unsigned short integer TDCcount = tables.UInt8Col() # unsigned byte grid_i = tables.Int32Col() # integer grid_j = tables.Int32Col() # integer pressure = tables.Float32Col() # float (single-precision) energy = tables.Float64Col() # double (double-precision) print() print('-**-**-**-**-**-**- file creation -**-**-**-**-**-**-**-') # The name of our HDF5 filename filename = "tutorial1.h5" print("Creating file:", filename) # Open a file in "w"rite mode h5file = tables.open_file(filename, mode="w", title="Test file") print() print('-**-**-**-**-**- group and table creation -**-**-**-**-**-**-**-') # Create a new group under "/" (root) group = h5file.create_group("/", 'detector', 'Detector information') print("Group '/detector' created") # Create one table on it table = h5file.create_table(group, 'readout', Particle, "Readout example") print("Table '/detector/readout' created") # Print the file print(h5file) print() print(repr(h5file)) # Get a shortcut to the record object in table particle = table.row # Fill the table with 10 particles for i in range(10): particle['name'] = 'Particle: %6d' % (i) particle['TDCcount'] = i % 256 particle['ADCcount'] = (i * 256) % (1 << 16) particle['grid_i'] = i particle['grid_j'] = 10 - i particle['pressure'] = float(i * i) particle['energy'] = float(particle['pressure'] ** 4) particle['idnumber'] = i * (2 ** 34) particle.append() # Flush the buffers for table table.flush() print() print('-**-**-**-**-**-**- table data reading & selection -**-**-**-**-**-') # Read actual data from table. We are interested in collecting pressure values # on entries where TDCcount field is greater than 3 and pressure less than 50 pressure = [x['pressure'] for x in table.iterrows() if x['TDCcount'] > 3 and 20 <= x['pressure'] < 50] print("Last record read:") print(repr(x)) print("Field pressure elements satisfying the cuts:") print(repr(pressure)) # Read also the names with the same cuts names = [ x['name'] for x in table.where( """(TDCcount > 3) & (20 <= pressure) & (pressure < 50)""") ] print("Field names elements satisfying the cuts:") print(repr(names)) print() print('-**-**-**-**-**-**- array object creation -**-**-**-**-**-**-**-') print("Creating a new group called '/columns' to hold new arrays") gcolumns = h5file.create_group(h5file.root, "columns", "Pressure and Name") print("Creating an array called 'pressure' under '/columns' group") h5file.create_array(gcolumns, 'pressure', np.array(pressure), "Pressure column selection") print(repr(h5file.root.columns.pressure)) print("Creating another array called 'name' under '/columns' group") h5file.create_array(gcolumns, 'name', names, "Name column selection") print(repr(h5file.root.columns.name)) print("HDF5 file:") print(h5file) # Close the file h5file.close() print("File '" + filename + "' created") PyTables-v.3.1.1/examples/tutorial1-2.py000066400000000000000000000215341231437614300177670ustar00rootroot00000000000000"""This example shows how to browse the object tree and enlarge tables. Before to run this program you need to execute first tutorial1-1.py that create the tutorial1.h5 file needed here. """ from __future__ import print_function import tables print() print('-**-**-**-**- open the previous tutorial file -**-**-**-**-**-') # Reopen the file in append mode h5file = tables.open_file("tutorial1.h5", "a") # Print the object tree created from this filename print("Object tree from filename:", h5file.filename) print(h5file) print() print('-**-**-**-**-**-**- traverse tree methods -**-**-**-**-**-**-**-') # List all the nodes (Group and Leaf objects) on tree print(h5file) # List all the nodes (using File iterator) on tree print("Nodes in file:") for node in h5file: print(node) print() # Now, only list all the groups on tree print("Groups in file:") for group in h5file.walk_groups(): print(group) print() # List only the arrays hanging from / print("Arrays in file (I):") for group in h5file.walk_groups("/"): for array in h5file.list_nodes(group, classname='Array'): print(array) # This do the same result print("Arrays in file (II):") for array in h5file.walk_nodes("/", "Array"): print(array) print() # And finally, list only leafs on /detector group (there should be one!) print("Leafs in group '/detector' (I):") for leaf in h5file.list_nodes("/detector", 'Leaf'): print(leaf) # Other way using iterators and natural naming print("Leafs in group '/detector' (II):") for leaf in h5file.root.detector._f_walknodes('Leaf'): print(leaf) print() print('-**-**-**-**-**-**- setting/getting object attributes -**-**--**-**-') # Get a pointer to '/detector/readout' node table = h5file.root.detector.readout # Attach it a string (date) attribute table.attrs.gath_date = "Wed, 06/12/2003 18:33" # Attach a floating point attribute table.attrs.temperature = 18.4 table.attrs.temp_scale = "Celsius" # Get a pointer to '/detector' node detector = h5file.root.detector # Attach a general object to the parent (/detector) group detector._v_attrs.stuff = [5, (2.3, 4.5), "Integer and tuple"] # Now, get the attributes print("gath_date attribute of /detector/readout:", table.attrs.gath_date) print("temperature attribute of /detector/readout:", table.attrs.temperature) print("temp_scale attribute of /detector/readout:", table.attrs.temp_scale) print("stuff attribute in /detector:", detector._v_attrs.stuff) print() # Delete permanently the attribute gath_date of /detector/readout print("Deleting /detector/readout gath_date attribute") del table.attrs.gath_date # Print a representation of all attributes in /detector/table print("AttributeSet instance in /detector/table:", repr(table.attrs)) # Get the (user) attributes of /detector/table print("List of user attributes in /detector/table:", table.attrs._f_list()) # Get the (sys) attributes of /detector/table print("List of user attributes in /detector/table:", table.attrs._f_list("sys")) print() # Rename an attribute print("renaming 'temp_scale' attribute to 'tempScale'") table.attrs._f_rename("temp_scale", "tempScale") print(table.attrs._f_list()) # Try to rename a system attribute: try: table.attrs._f_rename("VERSION", "version") except: print("You can not rename a VERSION attribute: it is read only!.") print() print('-**-**-**-**-**-**- getting object metadata -**-**-**-**-**-**-') # Get a pointer to '/detector/readout' data table = h5file.root.detector.readout # Get metadata from table print("Object:", table) print("Table name:", table.name) print("Table title:", table.title) print("Number of rows in table:", table.nrows) print("Table variable names with their type and shape:") for name in table.colnames: print(name, ':= %s, %s' % (table.coldtypes[name], table.coldtypes[name].shape)) print() # Get the object in "/columns pressure" pressureObject = h5file.get_node("/columns", "pressure") # Get some metadata on this object print("Info on the object:", repr(pressureObject)) print(" shape: ==>", pressureObject.shape) print(" title: ==>", pressureObject.title) print(" atom: ==>", pressureObject.atom) print() print('-**-**-**-**-**- reading actual data from arrays -**-**-**-**-**-**-') # Read the 'pressure' actual data pressureArray = pressureObject.read() print(repr(pressureArray)) # Check the kind of object we have created (it should be a numpy array) print("pressureArray is an object of type:", type(pressureArray)) # Read the 'name' Array actual data nameArray = h5file.root.columns.name.read() # Check the kind of object we have created (it should be a numpy array) print("nameArray is an object of type:", type(nameArray)) print() # Print the data for both arrays print("Data on arrays nameArray and pressureArray:") for i in range(pressureObject.shape[0]): print(nameArray[i], "-->", pressureArray[i]) print() print('-**-**-**-**-**- reading actual data from tables -**-**-**-**-**-**-') # Create a shortcut to table object table = h5file.root.detector.readout # Read the 'energy' column of '/detector/readout' print("Column 'energy' of '/detector/readout':\n", table.cols.energy) print() # Read the 3rd row of '/detector/readout' print("Third row of '/detector/readout':\n", table[2]) print() # Read the rows from 3 to 9 of row of '/detector/readout' print("Rows from 3 to 9 of '/detector/readout':\n", table[2:9]) print() print('-**-**-**-**- append records to existing table -**-**-**-**-**-') # Get the object row from table table = h5file.root.detector.readout particle = table.row # Append 5 new particles to table for i in range(10, 15): particle['name'] = 'Particle: %6d' % (i) particle['TDCcount'] = i % 256 particle['ADCcount'] = (i * 256) % (1 << 16) particle['grid_i'] = i particle['grid_j'] = 10 - i particle['pressure'] = float(i * i) particle['energy'] = float(particle['pressure'] ** 4) particle['idnumber'] = i * (2 ** 34) # This exceeds long integer range particle.append() # Flush this table table.flush() # Print the data using the table iterator: for r in table: print("%-16s | %11.1f | %11.4g | %6d | %6d | %8d |" % (r['name'], r['pressure'], r['energy'], r['grid_i'], r['grid_j'], r['TDCcount'])) print() print("Total number of entries in resulting table:", table.nrows) print() print('-**-**-**-**- modify records of a table -**-**-**-**-**-') # Single cells print("First row of readout table.") print("Before modif-->", table[0]) table.cols.TDCcount[0] = 1 print("After modifying first row of TDCcount-->", table[0]) table.cols.energy[0] = 2 print("After modifying first row of energy-->", table[0]) # Column slices table.cols.TDCcount[2:5] = [2, 3, 4] print("After modifying slice [2:5] of ADCcount-->", table[0:5]) table.cols.energy[1:9:3] = [2, 3, 4] print("After modifying slice [1:9:3] of energy-->", table[0:9]) # Modifying complete Rows table.modify_rows(start=1, step=3, rows=[(1, 2, 3.0, 4, 5, 6, 'Particle: None', 8.0), (2, 4, 6.0, 8, 10, 12, 'Particle: None*2', 16.0)]) print("After modifying the complete third row-->", table[0:5]) # Modifying columns inside table iterators for row in table.where('TDCcount <= 2'): row['energy'] = row['TDCcount'] * 2 row.update() print("After modifying energy column (where TDCcount <=2)-->", table[0:4]) print() print('-**-**-**-**- modify elements of an array -**-**-**-**-**-') print("pressure array") pressureObject = h5file.root.columns.pressure print("Before modif-->", pressureObject[:]) pressureObject[0] = 2 print("First modif-->", pressureObject[:]) pressureObject[1:3] = [2.1, 3.5] print("Second modif-->", pressureObject[:]) pressureObject[::2] = [1, 2] print("Third modif-->", pressureObject[:]) print("name array") nameObject = h5file.root.columns.name print("Before modif-->", nameObject[:]) nameObject[0] = 'Particle: None' print("First modif-->", nameObject[:]) nameObject[1:3] = ['Particle: 0', 'Particle: 1'] print("Second modif-->", nameObject[:]) nameObject[::2] = ['Particle: -3', 'Particle: -5'] print("Third modif-->", nameObject[:]) print() print('-**-**-**-**- remove records from a table -**-**-**-**-**-') # Delete some rows on the Table (yes, rows can be removed!) table.remove_rows(5, 10) # Print some table columns, for comparison with array data print("Some columns in final table:") print() # Print the headers print("%-16s | %11s | %11s | %6s | %6s | %8s |" % ('name', 'pressure', 'energy', 'grid_i', 'grid_j', 'TDCcount')) print("%-16s + %11s + %11s + %6s + %6s + %8s +" % ('-' * 16, '-' * 11, '-' * 11, '-' * 6, '-' * 6, '-' * 8)) # Print the data using the table iterator: for r in table.iterrows(): print("%-16s | %11.1f | %11.4g | %6d | %6d | %8d |" % (r['name'], r['pressure'], r['energy'], r['grid_i'], r['grid_j'], r['TDCcount'])) print() print("Total number of entries in final table:", table.nrows) # Close the file h5file.close() PyTables-v.3.1.1/examples/tutorial2.py000066400000000000000000000075421231437614300176340ustar00rootroot00000000000000"""This program shows the different protections that PyTables offer to the user in order to insure a correct data injection in tables. Example to be used in the second tutorial in the User's Guide. """ from __future__ import print_function import tables import numpy as np # Describe a particle record class Particle(tables.IsDescription): name = tables.StringCol(itemsize=16) # 16-character string lati = tables.Int32Col() # integer longi = tables.Int32Col() # integer pressure = tables.Float32Col(shape=(2, 3)) # array of floats # (single-precision) temperature = tables.Float64Col(shape=(2, 3)) # array of doubles # (double-precision) # Native NumPy dtype instances are also accepted Event = np.dtype([ ("name", "S16"), ("TDCcount", np.uint8), ("ADCcount", np.uint16), ("xcoord", np.float32), ("ycoord", np.float32) ]) # And dictionaries too (this defines the same structure as above) # Event = { # "name" : StringCol(itemsize=16), # "TDCcount" : UInt8Col(), # "ADCcount" : UInt16Col(), # "xcoord" : Float32Col(), # "ycoord" : Float32Col(), # } # Open a file in "w"rite mode fileh = tables.open_file("tutorial2.h5", mode="w") # Get the HDF5 root group root = fileh.root # Create the groups: for groupname in ("Particles", "Events"): group = fileh.create_group(root, groupname) # Now, create and fill the tables in Particles group gparticles = root.Particles # Create 3 new tables for tablename in ("TParticle1", "TParticle2", "TParticle3"): # Create a table table = fileh.create_table("/Particles", tablename, Particle, "Particles: " + tablename) # Get the record object associated with the table: particle = table.row # Fill the table with 257 particles for i in range(257): # First, assign the values to the Particle record particle['name'] = 'Particle: %6d' % (i) particle['lati'] = i particle['longi'] = 10 - i # Detectable errors start here. Play with them! particle['pressure'] = np.array( i * np.arange(2 * 3)).reshape((2, 4)) # Incorrect # particle['pressure'] = array(i*arange(2*3)).reshape((2,3)) # Correct # End of errors particle['temperature'] = (i ** 2) # Broadcasting # This injects the Record values particle.append() # Flush the table buffers table.flush() # Now, go for Events: for tablename in ("TEvent1", "TEvent2", "TEvent3"): # Create a table in Events group table = fileh.create_table(root.Events, tablename, Event, "Events: " + tablename) # Get the record object associated with the table: event = table.row # Fill the table with 257 events for i in range(257): # First, assign the values to the Event record event['name'] = 'Event: %6d' % (i) event['TDCcount'] = i % (1 << 8) # Correct range # Detectable errors start here. Play with them! event['xcoor'] = float(i ** 2) # Wrong spelling # event['xcoord'] = float(i**2) # Correct spelling event['ADCcount'] = "sss" # Wrong type # event['ADCcount'] = i * 2 # Correct type # End of errors event['ycoord'] = float(i) ** 4 # This injects the Record values event.append() # Flush the buffers table.flush() # Read the records from table "/Events/TEvent3" and select some table = root.Events.TEvent3 e = [p['TDCcount'] for p in table if p['ADCcount'] < 20 and 4 <= p['TDCcount'] < 15] print("Last record ==>", p) print("Selected values ==>", e) print("Total selected records ==> ", len(e)) # Finally, close the file (this also will flush all the remaining buffers!) fileh.close() PyTables-v.3.1.1/examples/tutorial3-1.py000066400000000000000000000030621231437614300177640ustar00rootroot00000000000000"""Small example of do/undo capability with PyTables.""" import tables # Create an HDF5 file fileh = tables.open_file("tutorial3-1.h5", "w", title="Undo/Redo demo 1") #'-**-**-**-**-**-**- enable undo/redo log -**-**-**-**-**-**-**-' fileh.enable_undo() # Create a new array one = fileh.create_array('/', 'anarray', [3, 4], "An array") # Mark this point fileh.mark() # Create a new array another = fileh.create_array('/', 'anotherarray', [4, 5], "Another array") # Now undo the past operation fileh.undo() # Check that anotherarray does not exist in the object tree but anarray does assert "/anarray" in fileh assert "/anotherarray" not in fileh # Unwind once more fileh.undo() # Check that anarray does not exist in the object tree assert "/anarray" not in fileh assert "/anotherarray" not in fileh # Go forward up to the next marker fileh.redo() # Check that anarray has come back to life in a sane state assert "/anarray" in fileh assert fileh.root.anarray.read() == [3, 4] assert fileh.root.anarray.title == "An array" assert fileh.root.anarray == one # But anotherarray is not here yet assert "/anotherarray" not in fileh # Now, go rewind up to the end fileh.redo() assert "/anarray" in fileh # Check that anotherarray has come back to life in a sane state assert "/anotherarray" in fileh assert fileh.root.anotherarray.read() == [4, 5] assert fileh.root.anotherarray.title == "Another array" assert fileh.root.anotherarray == another #'-**-**-**-**-**-**- disable undo/redo log -**-**-**-**-**-**-**-' fileh.disable_undo() # Close the file fileh.close() PyTables-v.3.1.1/examples/tutorial3-2.py000066400000000000000000000050631231437614300177700ustar00rootroot00000000000000"""A more complex example of do/undo capability with PyTables. Here, names has been assigned to the marks, and jumps are done between marks. """ import tables # Create an HDF5 file fileh = tables.open_file('tutorial3-2.h5', 'w', title='Undo/Redo demo 2') #'-**-**-**-**-**-**- enable undo/redo log -**-**-**-**-**-**-**-' fileh.enable_undo() # Start undoable operations fileh.create_array('/', 'otherarray1', [3, 4], 'Another array 1') fileh.create_group('/', 'agroup', 'Group 1') # Create a 'first' mark fileh.mark('first') fileh.create_array('/agroup', 'otherarray2', [4, 5], 'Another array 2') fileh.create_group('/agroup', 'agroup2', 'Group 2') # Create a 'second' mark fileh.mark('second') fileh.create_array('/agroup/agroup2', 'otherarray3', [5, 6], 'Another array 3') # Create a 'third' mark fileh.mark('third') fileh.create_array('/', 'otherarray4', [6, 7], 'Another array 4') fileh.create_array('/agroup', 'otherarray5', [7, 8], 'Another array 5') # Now go to mark 'first' fileh.goto('first') assert '/otherarray1' in fileh assert '/agroup' in fileh assert '/agroup/agroup2' not in fileh assert '/agroup/otherarray2' not in fileh assert '/agroup/agroup2/otherarray3' not in fileh assert '/otherarray4' not in fileh assert '/agroup/otherarray5' not in fileh # Go to mark 'third' fileh.goto('third') assert '/otherarray1' in fileh assert '/agroup' in fileh assert '/agroup/agroup2' in fileh assert '/agroup/otherarray2' in fileh assert '/agroup/agroup2/otherarray3' in fileh assert '/otherarray4' not in fileh assert '/agroup/otherarray5' not in fileh # Now go to mark 'second' fileh.goto('second') assert '/otherarray1' in fileh assert '/agroup' in fileh assert '/agroup/agroup2' in fileh assert '/agroup/otherarray2' in fileh assert '/agroup/agroup2/otherarray3' not in fileh assert '/otherarray4' not in fileh assert '/agroup/otherarray5' not in fileh # Go to the end fileh.goto(-1) assert '/otherarray1' in fileh assert '/agroup' in fileh assert '/agroup/agroup2' in fileh assert '/agroup/otherarray2' in fileh assert '/agroup/agroup2/otherarray3' in fileh assert '/otherarray4' in fileh assert '/agroup/otherarray5' in fileh # Check that objects have come back to life in a sane state assert fileh.root.otherarray1.read() == [3, 4] assert fileh.root.agroup.otherarray2.read() == [4, 5] assert fileh.root.agroup.agroup2.otherarray3.read() == [5, 6] assert fileh.root.otherarray4.read() == [6, 7] assert fileh.root.agroup.otherarray5.read() == [7, 8] #'-**-**-**-**-**-**- disable undo/redo log -**-**-**-**-**-**-**-' fileh.disable_undo() # Close the file fileh.close() PyTables-v.3.1.1/examples/undo-redo.py000066400000000000000000000104621231437614300175760ustar00rootroot00000000000000"""Yet another couple of examples on do/undo feauture.""" import tables def setUp(filename): # Create an HDF5 file fileh = tables.open_file(filename, mode="w", title="Undo/Redo demo") # Create some nodes in there fileh.create_group("/", "agroup", "Group 1") fileh.create_group("/agroup", "agroup2", "Group 2") fileh.create_array("/", "anarray", [1, 2], "Array 1") # Enable undo/redo. fileh.enable_undo() return fileh def tearDown(fileh): # Disable undo/redo. fileh.disable_undo() # Close the file fileh.close() def demo_6times3marks(): """Checking with six ops and three marks.""" # Initialize the data base with some nodes fileh = setUp("undo-redo-6times3marks.h5") # Create a new array fileh.create_array('/', 'otherarray1', [3, 4], "Another array 1") fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") # Put a mark fileh.mark() fileh.create_array('/', 'otherarray3', [5, 6], "Another array 3") fileh.create_array('/', 'otherarray4', [6, 7], "Another array 4") # Put a mark fileh.mark() fileh.create_array('/', 'otherarray5', [7, 8], "Another array 5") fileh.create_array('/', 'otherarray6', [8, 9], "Another array 6") # Unwind just one mark fileh.undo() assert "/otherarray1" in fileh assert "/otherarray2" in fileh assert "/otherarray3" in fileh assert "/otherarray4" in fileh assert "/otherarray5" not in fileh assert "/otherarray6" not in fileh # Unwind another mark fileh.undo() assert "/otherarray1" in fileh assert "/otherarray2" in fileh assert "/otherarray3" not in fileh assert "/otherarray4" not in fileh assert "/otherarray5" not in fileh assert "/otherarray6" not in fileh # Unwind all marks fileh.undo() assert "/otherarray1" not in fileh assert "/otherarray2" not in fileh assert "/otherarray3" not in fileh assert "/otherarray4" not in fileh assert "/otherarray5" not in fileh assert "/otherarray6" not in fileh # Redo until the next mark fileh.redo() assert "/otherarray1" in fileh assert "/otherarray2" in fileh assert "/otherarray3" not in fileh assert "/otherarray4" not in fileh assert "/otherarray5" not in fileh assert "/otherarray6" not in fileh # Redo until the next mark fileh.redo() assert "/otherarray1" in fileh assert "/otherarray2" in fileh assert "/otherarray3" in fileh assert "/otherarray4" in fileh assert "/otherarray5" not in fileh assert "/otherarray6" not in fileh # Redo until the end fileh.redo() assert "/otherarray1" in fileh assert "/otherarray2" in fileh assert "/otherarray3" in fileh assert "/otherarray4" in fileh assert "/otherarray5" in fileh assert "/otherarray6" in fileh # Tear down the file tearDown(fileh) def demo_manyops(): """Checking many operations together.""" # Initialize the data base with some nodes fileh = setUp("undo-redo-manyops.h5") # Create an array fileh.create_array(fileh.root, 'anarray3', [3], "Array title 3") # Create a group fileh.create_group(fileh.root, 'agroup3', "Group title 3") # /anarray => /agroup/agroup3/ new_node = fileh.copy_node('/anarray3', '/agroup/agroup2') new_node = fileh.copy_children('/agroup', '/agroup3', recursive=1) # rename anarray fileh.rename_node('/anarray', 'anarray4') # Move anarray new_node = fileh.copy_node('/anarray3', '/agroup') # Remove anarray4 fileh.remove_node('/anarray4') # Undo the actions fileh.undo() assert '/anarray4' not in fileh assert '/anarray3' not in fileh assert '/agroup/agroup2/anarray3' not in fileh assert '/agroup3' not in fileh assert '/anarray4' not in fileh assert '/anarray' in fileh # Redo the actions fileh.redo() # Check that the copied node exists again in the object tree. assert '/agroup/agroup2/anarray3' in fileh assert '/agroup/anarray3' in fileh assert '/agroup3/agroup2/anarray3' in fileh assert '/agroup3/anarray3' not in fileh assert fileh.root.agroup.anarray3 is new_node assert '/anarray' not in fileh assert '/anarray4' not in fileh # Tear down the file tearDown(fileh) if __name__ == '__main__': # run demos demo_6times3marks() demo_manyops() PyTables-v.3.1.1/examples/vlarray1.py000066400000000000000000000024001231437614300174340ustar00rootroot00000000000000from __future__ import print_function import tables import numpy as np # Create a VLArray: fileh = tables.open_file('vlarray1.h5', mode='w') vlarray = fileh.create_vlarray(fileh.root, 'vlarray1', tables.Int32Atom(shape=()), "ragged array of ints", filters=tables.Filters(1)) # Append some (variable length) rows: vlarray.append(np.array([5, 6])) vlarray.append(np.array([5, 6, 7])) vlarray.append([5, 6, 9, 8]) # Now, read it through an iterator: print('-->', vlarray.title) for x in vlarray: print('%s[%d]--> %s' % (vlarray.name, vlarray.nrow, x)) # Now, do the same with native Python strings. vlarray2 = fileh.create_vlarray(fileh.root, 'vlarray2', tables.StringAtom(itemsize=2), "ragged array of strings", filters=tables.Filters(1)) vlarray2.flavor = 'python' # Append some (variable length) rows: print('-->', vlarray2.title) vlarray2.append(['5', '66']) vlarray2.append(['5', '6', '77']) vlarray2.append(['5', '6', '9', '88']) # Now, read it through an iterator: for x in vlarray2: print('%s[%d]--> %s' % (vlarray2.name, vlarray2.nrow, x)) # Close the file. fileh.close() PyTables-v.3.1.1/examples/vlarray2.py000066400000000000000000000065501231437614300174470ustar00rootroot00000000000000#!/usr/bin/env python """Small example that shows how to work with variable length arrays of different types, UNICODE strings and general Python objects included.""" from __future__ import print_function import numpy as np import tables import pickle # Open a new empty HDF5 file fileh = tables.open_file("vlarray2.h5", mode="w") # Get the root group root = fileh.root # A test with VL length arrays: vlarray = fileh.create_vlarray(root, 'vlarray1', tables.Int32Atom(), "ragged array of ints") vlarray.append(np.array([5, 6])) vlarray.append(np.array([5, 6, 7])) vlarray.append([5, 6, 9, 8]) # Test with lists of bidimensional vectors vlarray = fileh.create_vlarray(root, 'vlarray2', tables.Int64Atom(shape=(2,)), "Ragged array of vectors") a = np.array([[1, 2], [1, 2]], dtype=np.int64) vlarray.append(a) vlarray.append(np.array([[1, 2], [3, 4]], dtype=np.int64)) vlarray.append(np.zeros(dtype=np.int64, shape=(0, 2))) vlarray.append(np.array([[5, 6]], dtype=np.int64)) # This makes an error (shape) # vlarray.append(array([[5], [6]], dtype=int64)) # This makes an error (type) # vlarray.append(array([[5, 6]], dtype=uint64)) # Test with strings vlarray = fileh.create_vlarray(root, 'vlarray3', tables.StringAtom(itemsize=3), "Ragged array of strings") vlarray.append(["123", "456", "3"]) vlarray.append(["456", "3"]) # This makes an error because of different string sizes than declared # vlarray.append(["1234", "456", "3"]) # Python flavor vlarray = fileh.create_vlarray(root, 'vlarray3b', tables.StringAtom(itemsize=3), "Ragged array of strings") vlarray.flavor = "python" vlarray.append(["123", "456", "3"]) vlarray.append(["456", "3"]) # Binary strings vlarray = fileh.create_vlarray(root, 'vlarray4', tables.UInt8Atom(), "pickled bytes") data = pickle.dumps((["123", "456"], "3")) vlarray.append(np.ndarray(buffer=data, dtype=np.uint8, shape=len(data))) # The next is a way of doing the same than before vlarray = fileh.create_vlarray(root, 'vlarray5', tables.ObjectAtom(), "pickled object") vlarray.append([["123", "456"], "3"]) # Boolean arrays are supported as well vlarray = fileh.create_vlarray(root, 'vlarray6', tables.BoolAtom(), "Boolean atoms") # The next lines are equivalent... vlarray.append([1, 0]) vlarray.append([1, 0, 3, 0]) # This will be converted to a boolean # This gives a TypeError # vlarray.append([1,0,1]) # Variable length strings vlarray = fileh.create_vlarray(root, 'vlarray7', tables.VLStringAtom(), "Variable Length String") vlarray.append("asd") vlarray.append("aaana") # Unicode variable length strings vlarray = fileh.create_vlarray(root, 'vlarray8', tables.VLUnicodeAtom(), "Variable Length Unicode String") vlarray.append("aaana") vlarray.append("") # The empty string vlarray.append("asd") vlarray.append("para\u0140lel") # Close the file fileh.close() # Open the file for reading fileh = tables.open_file("vlarray2.h5", mode="r") # Get the root group root = fileh.root for object in fileh.list_nodes(root, "Leaf"): arr = object.read() print(object.name, "-->", arr) print("number of objects in this row:", len(arr)) # Close the file fileh.close() PyTables-v.3.1.1/examples/vlarray3.py000066400000000000000000000012341231437614300174420ustar00rootroot00000000000000#!/usr/bin/env python """Example that shows how to easily save a variable number of atoms with a VLArray.""" from __future__ import print_function import numpy import tables N = 100 shape = (3, 3) numpy.random.seed(10) # For reproductible results f = tables.open_file("vlarray3.h5", mode="w") vlarray = f.create_vlarray(f.root, 'vlarray1', tables.Float64Atom(shape=shape), "ragged array of arrays") k = 0 for i in range(N): l = [] for j in range(numpy.random.randint(N)): l.append(numpy.random.randn(*shape)) k += 1 vlarray.append(l) print("Total number of atoms:", k) f.close() PyTables-v.3.1.1/examples/vlarray4.py000066400000000000000000000012341231437614300174430ustar00rootroot00000000000000#!/usr/bin/env python """Example that shows how to easily save a variable number of atoms with a VLArray.""" from __future__ import print_function import numpy import tables N = 100 shape = (3, 3) numpy.random.seed(10) # For reproductible results f = tables.open_file("vlarray4.h5", mode="w") vlarray = f.create_vlarray(f.root, 'vlarray1', tables.Float64Atom(shape=shape), "ragged array of arrays") k = 0 for i in range(N): l = [] for j in range(numpy.random.randint(N)): l.append(numpy.random.randn(*shape)) k += 1 vlarray.append(l) print("Total number of atoms:", k) f.close() PyTables-v.3.1.1/requirements.txt000066400000000000000000000000471231437614300167740ustar00rootroot00000000000000numpy>=1.4.1 numexpr>=2.0 cython>=0.13 PyTables-v.3.1.1/setup.cfg000066400000000000000000000002341231437614300153270ustar00rootroot00000000000000[sdist] formats=gztar [bdist_rpm] release = 1 group = Development/Languages/Python packager = Francesc Alted doc_files = *.txt doc/ PyTables-v.3.1.1/setup.py000077500000000000000000001002021231437614300152170ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Setup script for the tables package""" import os import re import sys import ctypes import tempfile import textwrap import subprocess from os.path import exists, expanduser import glob # Using ``setuptools`` enables lots of goodies, such as building eggs. if 'FORCE_SETUPTOOLS' in os.environ: from setuptools import setup, find_packages has_setuptools = True else: from distutils.core import setup has_setuptools = False from distutils.core import Extension from distutils.dep_util import newer from distutils.util import convert_path from distutils.ccompiler import new_compiler cmdclass = {} setuptools_kwargs = {} if sys.version_info >= (3,): fixer_names = [ 'lib2to3.fixes.fix_basestring', 'lib2to3.fixes.fix_dict', 'lib2to3.fixes.fix_imports', 'lib2to3.fixes.fix_long', 'lib2to3.fixes.fix_metaclass', 'lib2to3.fixes.fix_next', 'lib2to3.fixes.fix_numliterals', 'lib2to3.fixes.fix_print', 'lib2to3.fixes.fix_unicode', 'lib2to3.fixes.fix_xrange', ] if has_setuptools: from lib2to3.refactor import get_fixers_from_package all_fixers = set(get_fixers_from_package('lib2to3.fixes')) exclude_fixers = sorted(all_fixers.difference(fixer_names)) setuptools_kwargs['use_2to3'] = True setuptools_kwargs['use_2to3_fixers'] = [] setuptools_kwargs['use_2to3_exclude_fixers'] = exclude_fixers else: from distutils.command.build_py import build_py_2to3 as build_py from distutils.command.build_scripts \ import build_scripts_2to3 as build_scripts build_py.fixer_names = fixer_names build_scripts.fixer_names = fixer_names cmdclass['build_py'] = build_py cmdclass['build_scripts'] = build_scripts # The minimum required versions min_numpy_version = None min_numexpr_version = None min_cython_version = None min_hdf5_version = None min_python_version = (2, 6) exec(open(os.path.join('tables', 'req_versions.py')).read()) # Some functions for showing errors and warnings. def _print_admonition(kind, head, body): tw = textwrap.TextWrapper(initial_indent=' ', subsequent_indent=' ') print(".. %s:: %s" % (kind.upper(), head)) for line in tw.wrap(body): print(line) def exit_with_error(head, body=''): _print_admonition('error', head, body) sys.exit(1) def print_warning(head, body=''): _print_admonition('warning', head, body) # Check for Python if sys.version_info < min_python_version: exit_with_error("You need Python 2.6 or greater to install PyTables!") print("* Using Python %s" % sys.version.splitlines()[0]) # Check for required Python packages def check_import(pkgname, pkgver): try: mod = __import__(pkgname) except ImportError: exit_with_error( "You need %(pkgname)s %(pkgver)s or greater to run PyTables!" % {'pkgname': pkgname, 'pkgver': pkgver}) else: if mod.__version__ < pkgver: exit_with_error( "You need %(pkgname)s %(pkgver)s or greater to run PyTables!" % {'pkgname': pkgname, 'pkgver': pkgver}) print("* Found %(pkgname)s %(pkgver)s package installed." % {'pkgname': pkgname, 'pkgver': mod.__version__}) globals()[pkgname] = mod check_import('numpy', min_numpy_version) # Check for numexpr only if not using setuptools (see #298) if not has_setuptools: check_import('numexpr', min_numexpr_version) # Check if Cython is installed or not (requisite) try: from Cython.Distutils import build_ext from Cython.Compiler.Main import Version cmdclass['build_ext'] = build_ext except ImportError: exit_with_error( "You need %(pkgname)s %(pkgver)s or greater to compile PyTables!" % {'pkgname': 'Cython', 'pkgver': min_cython_version}) if Version.version < min_cython_version: exit_with_error( "At least Cython %s is needed so as to generate extensions!" % (min_cython_version)) else: print("* Found %(pkgname)s %(pkgver)s package installed." % {'pkgname': 'Cython', 'pkgver': Version.version}) VERSION = open('VERSION').read().strip() #---------------------------------------------------------------------- debug = '--debug' in sys.argv # Global variables lib_dirs = [] inc_dirs = ['c-blosc/hdf5'] optional_libs = [] data_files = [] # list of data files to add to packages (mainly for DLL's) default_header_dirs = None default_library_dirs = None default_runtime_dirs = None def add_from_path(envname, dirs): try: dirs.extend(os.environ[envname].split(os.pathsep)) except KeyError: pass def add_from_flags(envname, flag_key, dirs): for flag in os.environ.get(envname, "").split(): if flag.startswith(flag_key): dirs.append(flag[len(flag_key):]) if os.name == 'posix': prefixes = ('/usr/local', '/sw', '/opt', '/opt/local', '/usr', '/') default_header_dirs = [] add_from_path("CPATH", default_header_dirs) add_from_path("C_INCLUDE_PATH", default_header_dirs) add_from_flags("CPPFLAGS", "-I", default_header_dirs) default_header_dirs.extend( os.path.join(_tree, 'include') for _tree in prefixes ) default_library_dirs = [] add_from_flags("LDFLAGS", "-L", default_library_dirs) default_library_dirs.extend( os.path.join(_tree, _arch) for _tree in prefixes for _arch in ('lib64', 'lib')) default_runtime_dirs = default_library_dirs elif os.name == 'nt': default_header_dirs = [] # no default, must be given explicitly default_library_dirs = [] # no default, must be given explicitly default_runtime_dirs = [ # look for DLL files in ``%PATH%`` _path for _path in os.environ['PATH'].split(';')] # Add the \Windows\system to the runtime list (necessary for Vista) default_runtime_dirs.append('\\windows\\system') # Add the \path_to_python\DLLs and tables package to the list default_runtime_dirs.extend( [os.path.join(sys.prefix, 'Lib\\site-packages\\tables')]) from numpy.distutils.misc_util import get_numpy_include_dirs inc_dirs.extend(get_numpy_include_dirs()) # Gcc 4.0.1 on Mac OS X 10.4 does not seem to include the default # header and library paths. See ticket #18. if sys.platform.lower().startswith('darwin'): inc_dirs.extend(default_header_dirs) lib_dirs.extend(default_library_dirs) def _find_file_path(name, locations, prefixes=[''], suffixes=['']): for prefix in prefixes: for suffix in suffixes: for location in locations: path = os.path.join(location, prefix + name + suffix) if os.path.isfile(path): return path return None class Package(object): def __init__(self, name, tag, header_name, library_name, target_function=None): self.name = name self.tag = tag self.header_name = header_name self.library_name = library_name self.runtime_name = library_name self.target_function = target_function def find_header_path(self, locations=default_header_dirs): return _find_file_path(self.header_name, locations, suffixes=['.h']) def find_library_path(self, locations=default_library_dirs): return _find_file_path( self.library_name, locations, self._library_prefixes, self._library_suffixes) def find_runtime_path(self, locations=default_runtime_dirs): """ returns True if the runtime can be found returns None otherwise """ # An explicit path can not be provided for runtime libraries. # (The argument is accepted for compatibility with previous methods.) # dlopen() won't tell us where the file is, just whether # success occurred, so this returns True instead of a filename for prefix in self._runtime_prefixes: for suffix in self._runtime_suffixes: try: ctypes.CDLL(prefix + self.runtime_name + suffix) return True except OSError: pass def find_directories(self, location): dirdata = [ (self.header_name, self.find_header_path, default_header_dirs), (self.library_name, self.find_library_path, default_library_dirs), (self.runtime_name, self.find_runtime_path, default_runtime_dirs), ] locations = [] if location: # The path of a custom install of the package has been # provided, so the directories where the components # (headers, libraries, runtime) are going to be searched # are constructed by appending platform-dependent # component directories to the given path. # Remove leading and trailing '"' chars that can mislead # the finding routines on Windows machines locations = [ os.path.join(location.strip('"'), compdir) for compdir in self._component_dirs ] directories = [None, None, None] # headers, libraries, runtime for idx, (name, find_path, default_dirs) in enumerate(dirdata): path = find_path(locations or default_dirs) if path: if path is True: directories[idx] = True continue # Take care of not returning a directory component # included in the name. For instance, if name is # 'foo/bar' and path is '/path/foo/bar.h', do *not* # take '/path/foo', but just '/path'. This also works # for name 'libfoo.so' and path '/path/libfoo.so'. # This has been modified to just work over include files. # For libraries, its names can be something like 'bzip2' # and if they are located in places like: # \stuff\bzip2-1.0.3\lib\bzip2.lib # then, the directory will be returned as '\stuff' (!!) # F. Alted 2006-02-16 if idx == 0: directories[idx] = os.path.dirname(path[:path.rfind(name)]) else: directories[idx] = os.path.dirname(path) return tuple(directories) class PosixPackage(Package): _library_prefixes = ['lib'] _library_suffixes = ['.so', '.dylib', '.a'] _runtime_prefixes = _library_prefixes _runtime_suffixes = ['.so', '.dylib'] _component_dirs = ['include', 'lib'] class WindowsPackage(Package): _library_prefixes = [''] _library_suffixes = ['.lib'] _runtime_prefixes = [''] _runtime_suffixes = ['.dll'] # lookup in '.' seems necessary for LZO2 _component_dirs = ['include', 'lib', 'dll', '.'] def find_runtime_path(self, locations=default_runtime_dirs): # An explicit path can not be provided for runtime libraries. # (The argument is accepted for compatibility with previous methods.) return _find_file_path( self.runtime_name, default_runtime_dirs, self._runtime_prefixes, self._runtime_suffixes) # Get the HDF5 version provided the 'H5public.h' header def get_hdf5_version(headername): major_version = -1 minor_version = -1 release_version = -1 for line in open(headername): if 'H5_VERS_MAJOR' in line: major_version = int(re.split("\s*", line)[2]) if 'H5_VERS_MINOR' in line: minor_version = int(re.split("\s*", line)[2]) if 'H5_VERS_RELEASE' in line: release_version = int(re.split("\s*", line)[2]) if (major_version != -1 and minor_version != -1 and release_version != -1): break if (major_version == -1 or minor_version == -1 or release_version == -1): exit_with_error("Unable to detect HDF5 library version!") return (major_version, minor_version, release_version) _cp = convert_path if os.name == 'posix': _Package = PosixPackage _platdep = { # package tag -> platform-dependent components 'HDF5': ['hdf5'], 'LZO2': ['lzo2'], 'LZO': ['lzo'], 'BZ2': ['bz2'], 'BLOSC': ['blosc'], } elif os.name == 'nt': _Package = WindowsPackage _platdep = { # package tag -> platform-dependent components 'HDF5': ['hdf5dll', 'hdf5dll'], 'LZO2': ['lzo2', 'lzo2'], 'LZO': ['liblzo', 'lzo1'], 'BZ2': ['bzip2', 'bzip2'], 'BLOSC': ['blosc', 'blosc'], } # Copy the next DLL's to binaries by default. # Update these paths for your own system! dll_files = ['\\windows\\system\\zlib1.dll', '\\windows\\system\\szip.dll', ] if '--debug' in sys.argv: _platdep['HDF5'] = ['hdf5ddll', 'hdf5ddll'] hdf5_package = _Package("HDF5", 'HDF5', 'H5public', *_platdep['HDF5']) hdf5_package.target_function = 'H5close' lzo2_package = _Package("LZO 2", 'LZO2', _cp('lzo/lzo1x'), *_platdep['LZO2']) lzo2_package.target_function = 'lzo_version_date' lzo1_package = _Package("LZO 1", 'LZO', 'lzo1x', *_platdep['LZO']) lzo1_package.target_function = 'lzo_version_date' bzip2_package = _Package("bzip2", 'BZ2', 'bzlib', *_platdep['BZ2']) bzip2_package.target_function = 'BZ2_bzlibVersion' blosc_package = _Package("blosc", 'BLOSC', 'blosc', *_platdep['BLOSC']) blosc_package.target_function = 'blosc_list_compressors' # Blosc >= 1.3 #----------------------------------------------------------------- def_macros = [('NDEBUG', 1)] # Define macros for Windows platform if os.name == 'nt': def_macros.append(('WIN32', 1)) def_macros.append(('_HDF5USEDLL_', 1)) # Allow setting the HDF5 dir and additional link flags either in # the environment or on the command line. # First check the environment... HDF5_DIR = os.environ.get('HDF5_DIR', '') LZO_DIR = os.environ.get('LZO_DIR', '') BZIP2_DIR = os.environ.get('BZIP2_DIR', '') BLOSC_DIR = os.environ.get('BLOSC_DIR', '') LFLAGS = os.environ.get('LFLAGS', '').split() # in GCC-style compilers, -w in extra flags will get rid of copious # 'uninitialized variable' Cython warnings. However, this shouldn't be # the default as it will suppress *all* the warnings, which definitely # is not a good idea. CFLAGS = os.environ.get('CFLAGS', '').split() LIBS = os.environ.get('LIBS', '').split() # ...then the command line. # Handle --hdf5=[PATH] --lzo=[PATH] --bzip2=[PATH] --blosc=[PATH] # --lflags=[FLAGS] --cflags=[FLAGS] and --debug args = sys.argv[:] for arg in args: if arg.find('--hdf5=') == 0: HDF5_DIR = expanduser(arg.split('=')[1]) sys.argv.remove(arg) elif arg.find('--lzo=') == 0: LZO_DIR = expanduser(arg.split('=')[1]) sys.argv.remove(arg) elif arg.find('--bzip2=') == 0: BZIP2_DIR = expanduser(arg.split('=')[1]) sys.argv.remove(arg) elif arg.find('--blosc=') == 0: BLOSC_DIR = expanduser(arg.split('=')[1]) sys.argv.remove(arg) elif arg.find('--lflags=') == 0: LFLAGS = arg.split('=')[1].split() sys.argv.remove(arg) elif arg.find('--cflags=') == 0: CFLAGS = arg.split('=')[1].split() sys.argv.remove(arg) elif arg.find('--debug') == 0: # For debugging (mainly compression filters) if os.name != 'nt': # to prevent including dlfcn.h by utils.c!!! def_macros = [('DEBUG', 1)] # Don't delete this argument. It maybe useful for distutils # when adding more flags later on #sys.argv.remove(arg) # For windows, search for the hdf5 dll in the path and use it if found. # This is much more convenient than having to manually set an environment # variable to rebuild pytables if not HDF5_DIR and os.name == 'nt': import ctypes.util libdir = ctypes.util.find_library('hdf5dll.dll') # Like 'C:\\Program Files\\HDF Group\\HDF5\\1.8.8\\bin\\hdf5dll.dll' if libdir: # Strip off the filename libdir = os.path.dirname(libdir) # Strip off the 'bin' directory HDF5_DIR = os.path.dirname(libdir) print("* Found HDF5 using system PATH ('%s')" % libdir) # The next flag for the C compiler is needed for finding the C headers for # the Cython extensions CFLAGS.append("-Isrc") # Force the 1.8.x HDF5 API even if the library as been compiled to use the # 1.6.x API by default CFLAGS.extend([ "-DH5Acreate_vers=2", "-DH5Aiterate_vers=2", "-DH5Dcreate_vers=2", "-DH5Dopen_vers=2", "-DH5Eclear_vers=2", "-DH5Eprint_vers=2", "-DH5Epush_vers=2", "-DH5Eset_auto_vers=2", "-DH5Eget_auto_vers=2", "-DH5Ewalk_vers=2", "-DH5E_auto_t_vers=2", "-DH5Gcreate_vers=2", "-DH5Gopen_vers=2", "-DH5Pget_filter_vers=2", "-DH5Pget_filter_by_id_vers=2", #"-DH5Pinsert_vers=2", #"-DH5Pregister_vers=2", #"-DH5Rget_obj_type_vers=2", "-DH5Tarray_create_vers=2", #"-DH5Tcommit_vers=2", "-DH5Tget_array_dims_vers=2", #"-DH5Topen_vers=2", "-DH5Z_class_t_vers=2", ]) CFLAGS.append("-DH5_NO_DEPRECATED_SYMBOLS") # Do not use numpy deprecated API #CFLAGS.append("-DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION") # Try to locate the compulsory and optional libraries. lzo2_enabled = False compiler = new_compiler() for (package, location) in [(hdf5_package, HDF5_DIR), (lzo2_package, LZO_DIR), (lzo1_package, LZO_DIR), (bzip2_package, BZIP2_DIR), (blosc_package, BLOSC_DIR)]: if package.tag == 'LZO' and lzo2_enabled: print("* Skipping detection of %s since %s has already been found." % (lzo1_package.name, lzo2_package.name)) continue # do not use LZO 1 if LZO 2 is available (hdrdir, libdir, rundir) = package.find_directories(location) # check if the library is in the standard compiler paths if not libdir and package.target_function: libdir = compiler.has_function(package.target_function, libraries=(package.library_name,)) if not (hdrdir and libdir): if package.tag in ['HDF5']: # these are compulsory! pname, ptag = package.name, package.tag exit_with_error( "Could not find a local %s installation." % pname, "You may need to explicitly state " "where your local %(name)s headers and library can be found " "by setting the ``%(tag)s_DIR`` environment variable " "or by using the ``--%(ltag)s`` command-line option." % dict(name=pname, tag=ptag, ltag=ptag.lower())) if package.tag == 'BLOSC': # this is optional, but comes with sources print("* Could not find %s headers and library; " "using internal sources." % package.name) else: print("* Could not find %s headers and library; " "disabling support for it." % package.name) continue # look for the next library if libdir in ("", True): print("* Found %s headers at ``%s``, the library is located in the " "standard system search dirs." % (package.name, hdrdir)) else: print("* Found %s headers at ``%s``, library at ``%s``." % (package.name, hdrdir, libdir)) if package.tag in ['HDF5']: hdf5_header = os.path.join(hdrdir, "H5public.h") hdf5_version = get_hdf5_version(hdf5_header) if hdf5_version < min_hdf5_version: exit_with_error( "Unsupported HDF5 version! HDF5 v%s+ required. " "Found version v%s" % ( '.'.join(map(str, min_hdf5_version)), '.'.join(map(str, hdf5_version)))) if hdrdir not in default_header_dirs: inc_dirs.append(hdrdir) # save header directory if needed if libdir not in default_library_dirs and libdir not in ("", True): # save library directory if needed if os.name == "nt": # Important to quote the libdir for Windows (Vista) systems lib_dirs.append('"%s"' % libdir) else: lib_dirs.append(libdir) if package.tag not in ['HDF5']: # Keep record of the optional libraries found. optional_libs.append(package.tag) def_macros.append(('HAVE_%s_LIB' % package.tag, 1)) if not rundir: loc = { 'posix': "the default library paths.", 'nt': "any of the directories in %%PATH%%.", }[os.name] print_warning( "Could not find the %s runtime." % package.name, "The %(name)s shared library was *not* found in %(loc)s " "In case of runtime problems, please remember to install it." % dict(name=package.name, loc=loc) ) if os.name == "nt": # LZO DLLs cannot be copied to the binary package for license reasons if package.tag not in ['LZO', 'LZO2']: dll_file = _platdep[package.tag][1] + '.dll' # If DLL is not in rundir, do nothing. This can be useful # for BZIP2, that can be linked either statically (.LIB) # or dinamically (.DLL) if rundir is not None: dll_files.append(os.path.join(rundir, dll_file)) if package.tag == 'LZO2': lzo2_enabled = True if lzo2_enabled: lzo_package = lzo2_package else: lzo_package = lzo1_package #------------------------------------------------------------------------------ cython_extnames = [ 'utilsextension', 'hdf5extension', 'tableextension', 'linkextension', '_comp_lzo', '_comp_bzip2', 'lrucacheextension', 'indexesextension', ] def get_cython_extfiles(extnames): extdir = 'tables' extfiles = {} for extname in extnames: extfile = os.path.join(extdir, extname) extpfile = '%s.pyx' % extfile extcfile = '%s.c' % extfile if not exists(extcfile) or newer(extpfile, extcfile): # For some reason, setup in setuptools does not compile # Cython files (!) Do that manually... # 2013/08/24: the issue should be fixed in distribute 0.6.15 # see also https://bitbucket.org/tarek/distribute/issue/195 print("cythoning %s to %s" % (extpfile, extcfile)) retcode = subprocess.call( [sys.executable, "-m", "cython", extpfile] ) if retcode > 0: print("cython aborted compilation with retcode:", retcode) sys.exit() extfiles[extname] = extcfile return extfiles cython_extfiles = get_cython_extfiles(cython_extnames) # Update the version.h file if this file is newer if newer('VERSION', 'src/version.h'): open('src/version.h', 'w').write( '#define PYTABLES_VERSION "%s"\n' % VERSION) #-------------------------------------------------------------------- # Package information for ``setuptools``. if has_setuptools: # PyTables contains data files for tests. setuptools_kwargs['zip_safe'] = False # ``NumPy`` headers are needed for building the extensions, as # well as Cython. setuptools_kwargs['setup_requires'] = [ 'numpy>=%s' % min_numpy_version, 'cython>=%s' % min_cython_version, ] # ``NumPy`` and ``Numexpr`` are absolutely required for running PyTables. setuptools_kwargs['install_requires'] = [ 'numpy>=%s' % min_numpy_version, 'numexpr>=%s' % min_numexpr_version, ] setuptools_kwargs['extras_require'] = {} # Detect packages automatically. setuptools_kwargs['packages'] = find_packages(exclude=['*.bench']) # Entry points for automatic creation of scripts. setuptools_kwargs['entry_points'] = { 'console_scripts': [ 'ptdump = tables.scripts.ptdump:main', 'ptrepack = tables.scripts.ptrepack:main', 'pt2to3 = tables.scripts.pt2to3:main', ], } # Test suites. setuptools_kwargs['test_suite'] = 'tables.tests.test_all.suite' setuptools_kwargs['scripts'] = [] else: # The next should work with stock distutils, but it does not! # It is better to rely on check_import # setuptools_kwargs['requires'] = ['numpy (>= %s)' % min_numpy_version, # 'numexpr (>= %s)' % min_numexpr_version] # There is no other chance, these values must be hardwired. setuptools_kwargs['packages'] = [ 'tables', 'tables.nodes', 'tables.scripts', 'tables.misc', # Test suites. 'tables.tests', 'tables.nodes.tests', ] setuptools_kwargs['scripts'] = [ 'utils/ptdump', 'utils/ptrepack', 'utils/pt2to3'] # Copy additional data for packages that need it. setuptools_kwargs['package_data'] = { 'tables.tests': ['*.h5', '*.mat'], 'tables.nodes.tests': ['*.dat', '*.xbm', '*.h5']} #Having the Python version included in the package name makes managing a #system with multiple versions of Python much easier. def find_name(base='tables'): '''If "--name-with-python-version" is on the command line then append "-pyX.Y" to the base name''' name = base if '--name-with-python-version' in sys.argv: name += '-py%i.%i' % (sys.version_info[0], sys.version_info[1]) sys.argv.remove('--name-with-python-version') return name name = find_name() if os.name == "nt": # Add DLL's to the final package for windows data_files.extend([ ('Lib/site-packages/%s' % name, dll_files), ]) ADDLIBS = [hdf5_package.library_name] # List of Blosc file dependencies blosc_files = ["c-blosc/hdf5/blosc_filter.c"] if 'BLOSC' not in optional_libs: # Compiling everything from sources # Blosc + BloscLZ sources blosc_files += glob.glob('c-blosc/blosc/*.c') # LZ4 sources blosc_files += glob.glob('c-blosc/internal-complibs/lz4*/*.c') # Snappy sources blosc_files += glob.glob('c-blosc/internal-complibs/snappy*/*.cc') # Zlib sources blosc_files += glob.glob('c-blosc/internal-complibs/zlib*/*.c') # Finally, add all the include dirs... inc_dirs += [os.path.join('c-blosc', 'blosc')] inc_dirs += glob.glob('c-blosc/internal-complibs/*') # ...and the macros for all the compressors supported def_macros += [('HAVE_LZ4', 1), ('HAVE_SNAPPY', 1), ('HAVE_ZLIB', 1)] # Add -msse2 flag for optimizing shuffle in include Blosc def compiler_has_flags(compiler, flags): with tempfile.NamedTemporaryFile(mode='w', suffix='.c', delete=False) as fd: fd.write('int main() {return 0;}') try: compiler.compile([fd.name], extra_preargs=flags) except Exception: return False else: return True finally: os.remove(fd.name) if compiler_has_flags(compiler, ["-msse2"]): print("Setting compiler flag '-msse2'") CFLAGS.append("-msse2") else: ADDLIBS += ['blosc'] utilsExtension_libs = LIBS + ADDLIBS hdf5Extension_libs = LIBS + ADDLIBS tableExtension_libs = LIBS + ADDLIBS linkExtension_libs = LIBS + ADDLIBS indexesExtension_libs = LIBS + ADDLIBS lrucacheExtension_libs = [] # Doesn't need external libraries # Compressor modules only need other libraries if they are enabled. _comp_lzo_libs = LIBS[:] _comp_bzip2_libs = LIBS[:] for (package, complibs) in [(lzo_package, _comp_lzo_libs), (bzip2_package, _comp_bzip2_libs)]: if package.tag in optional_libs: complibs.extend([hdf5_package.library_name, package.library_name]) extensions = [ Extension("tables.utilsextension", include_dirs=inc_dirs, define_macros=def_macros, sources=[cython_extfiles['utilsextension'], "src/utils.c", "src/H5ARRAY.c", "src/H5ATTR.c", ] + blosc_files, library_dirs=lib_dirs, libraries=utilsExtension_libs, extra_link_args=LFLAGS, extra_compile_args=CFLAGS), Extension("tables.hdf5extension", include_dirs=inc_dirs, define_macros=def_macros, sources=[cython_extfiles['hdf5extension'], "src/utils.c", "src/typeconv.c", "src/H5ARRAY.c", "src/H5ARRAY-opt.c", "src/H5VLARRAY.c", "src/H5ATTR.c", ] + blosc_files, library_dirs=lib_dirs, libraries=hdf5Extension_libs, extra_link_args=LFLAGS, extra_compile_args=CFLAGS), Extension("tables.tableextension", include_dirs=inc_dirs, define_macros=def_macros, sources=[cython_extfiles['tableextension'], "src/utils.c", "src/typeconv.c", "src/H5TB-opt.c", "src/H5ATTR.c", ] + blosc_files, library_dirs=lib_dirs, libraries=tableExtension_libs, extra_link_args=LFLAGS, extra_compile_args=CFLAGS), Extension("tables._comp_lzo", include_dirs=inc_dirs, define_macros=def_macros, sources=[cython_extfiles['_comp_lzo'], "src/H5Zlzo.c"], library_dirs=lib_dirs, libraries=_comp_lzo_libs, extra_link_args=LFLAGS, extra_compile_args=CFLAGS), Extension("tables._comp_bzip2", include_dirs=inc_dirs, define_macros=def_macros, sources=[cython_extfiles['_comp_bzip2'], "src/H5Zbzip2.c"], library_dirs=lib_dirs, libraries=_comp_bzip2_libs, extra_link_args=LFLAGS, extra_compile_args=CFLAGS), Extension("tables.linkextension", include_dirs=inc_dirs, define_macros=def_macros, sources=[cython_extfiles['linkextension']], library_dirs=lib_dirs, libraries=tableExtension_libs, extra_link_args=LFLAGS, extra_compile_args=CFLAGS), Extension("tables.lrucacheextension", include_dirs=inc_dirs, define_macros=def_macros, sources=[cython_extfiles['lrucacheextension']], library_dirs=lib_dirs, libraries=lrucacheExtension_libs, extra_link_args=LFLAGS, extra_compile_args=CFLAGS), Extension("tables.indexesextension", include_dirs=inc_dirs, define_macros=def_macros, sources=[cython_extfiles['indexesextension'], "src/H5ARRAY-opt.c", "src/idx-opt.c"], library_dirs=lib_dirs, libraries=indexesExtension_libs, extra_link_args=LFLAGS, extra_compile_args=CFLAGS), ] classifiers = """\ Development Status :: 5 - Production/Stable Intended Audience :: Developers Intended Audience :: Information Technology Intended Audience :: Science/Research License :: OSI Approved :: BSD License Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 3 Topic :: Database Topic :: Software Development :: Libraries :: Python Modules Operating System :: Microsoft :: Windows Operating System :: Unix """ setup( name=name, version=VERSION, description='Hierarchical datasets for Python', long_description="""\ PyTables is a package for managing hierarchical datasets and designed to efficently cope with extremely large amounts of data. PyTables is built on top of the HDF5 library and the NumPy package and features an object-oriented interface that, combined with C-code generated from Cython sources, makes of it a fast, yet extremely easy to use tool for interactively save and retrieve large amounts of data. """, classifiers=[c for c in classifiers.split("\n") if c], author='Francesc Alted, Ivan Vilata, et al.', author_email='pytables@pytables.org', maintainer='PyTables maintainers', maintainer_email='pytables@pytables.org', url='http://www.pytables.org/', license='http://www.opensource.org/licenses/bsd-license.php', download_url="http://sourceforge.net/projects/pytables/files/pytables/", platforms=['any'], ext_modules=extensions, cmdclass=cmdclass, data_files=data_files, **setuptools_kwargs ) PyTables-v.3.1.1/src/000077500000000000000000000000001231437614300142765ustar00rootroot00000000000000PyTables-v.3.1.1/src/H5ARRAY-opt.c000066400000000000000000000163231231437614300163220ustar00rootroot00000000000000#include "H5ARRAY-opt.h" #include #include /*------------------------------------------------------------------------- * Function: H5ARRAYOread_readSlice * * Purpose: Read records from an opened Array * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted, faltet@pytables.com * * Date: May 27, 2004 * * Comments: * * Modifications: * * *------------------------------------------------------------------------- */ herr_t H5ARRAYOread_readSlice( hid_t dataset_id, hid_t type_id, hsize_t irow, hsize_t start, hsize_t stop, void *data ) { hid_t space_id; hid_t mem_space_id; hsize_t count[2]; int rank = 2; hsize_t offset[2]; hsize_t stride[2] = {1, 1}; count[0] = 1; count[1] = stop - start; offset[0] = irow; offset[1] = start; /* Get the dataspace handle */ if ( (space_id = H5Dget_space( dataset_id )) < 0 ) goto out; /* Create a memory dataspace handle */ if ( (mem_space_id = H5Screate_simple( rank, count, NULL )) < 0 ) goto out; /* Define a hyperslab in the dataset of the size of the records */ if ( H5Sselect_hyperslab(space_id, H5S_SELECT_SET, offset, stride, count, NULL) < 0 ) goto out; /* Read */ if ( H5Dread( dataset_id, type_id, mem_space_id, space_id, H5P_DEFAULT, data ) < 0 ) goto out; /* Terminate access to the memory dataspace */ if ( H5Sclose( mem_space_id ) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; return 0; out: H5Dclose( dataset_id ); return -1; } /*------------------------------------------------------------------------- * Function: H5ARRAYOinit_readSlice * * Purpose: Prepare structures to read specifics arrays faster * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted, faltet@pytables.com * * Date: May 18, 2006 * * Comments: * - The H5ARRAYOinit_readSlice and H5ARRAYOread_readSlice * are intended to read indexes slices only! * F. Alted 2006-05-18 * * Modifications: * * *------------------------------------------------------------------------- */ herr_t H5ARRAYOinit_readSlice( hid_t dataset_id, hid_t *mem_space_id, hsize_t count) { hid_t space_id; int rank = 2; hsize_t count2[2] = {1, count}; /* Get the dataspace handle */ if ( (space_id = H5Dget_space(dataset_id )) < 0 ) goto out; /* Create a memory dataspace handle */ if ( (*mem_space_id = H5Screate_simple(rank, count2, NULL)) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; return 0; out: H5Dclose(dataset_id); return -1; } /*------------------------------------------------------------------------- * Function: H5ARRAYOread_readSortedSlice * * Purpose: Read records from an opened Array * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted, faltet@pytables.com * * Date: Aug 11, 2005 * * Comments: * * Modifications: * - Modified to cache the mem_space_id as well. * F. Alted 2005-08-11 * * *------------------------------------------------------------------------- */ herr_t H5ARRAYOread_readSortedSlice( hid_t dataset_id, hid_t mem_space_id, hid_t type_id, hsize_t irow, hsize_t start, hsize_t stop, void *data ) { hid_t space_id; hsize_t count[2] = {1, stop-start}; hsize_t offset[2] = {irow, start}; hsize_t stride[2] = {1, 1}; /* Get the dataspace handle */ if ( (space_id = H5Dget_space(dataset_id)) < 0 ) goto out; /* Define a hyperslab in the dataset of the size of the records */ if ( H5Sselect_hyperslab(space_id, H5S_SELECT_SET, offset, stride, count, NULL) < 0 ) goto out; /* Read */ if ( H5Dread( dataset_id, type_id, mem_space_id, space_id, H5P_DEFAULT, data ) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; return 0; out: H5Dclose( dataset_id ); return -1; } /*------------------------------------------------------------------------- * Function: H5ARRAYOread_readBoundsSlice * * Purpose: Read records from an opened Array * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted, faltet@pytables.com * * Date: Aug 19, 2005 * * Comments: This is exactly the same as H5ARRAYOread_readSortedSlice, * but I just want to distinguish the calls in profiles. * * Modifications: * * *------------------------------------------------------------------------- */ herr_t H5ARRAYOread_readBoundsSlice( hid_t dataset_id, hid_t mem_space_id, hid_t type_id, hsize_t irow, hsize_t start, hsize_t stop, void *data ) { hid_t space_id; hsize_t count[2] = {1, stop-start}; hsize_t offset[2] = {irow, start}; hsize_t stride[2] = {1, 1}; /* Get the dataspace handle */ if ( (space_id = H5Dget_space(dataset_id)) < 0 ) goto out; /* Define a hyperslab in the dataset of the size of the records */ if ( H5Sselect_hyperslab(space_id, H5S_SELECT_SET, offset, stride, count, NULL) < 0 ) goto out; /* Read */ if ( H5Dread( dataset_id, type_id, mem_space_id, space_id, H5P_DEFAULT, data ) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; return 0; out: H5Dclose( dataset_id ); return -1; } /*------------------------------------------------------------------------- * Function: H5ARRAYreadSliceLR * * Purpose: Reads a slice of LR index cache from disk. * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted, faltet@pytables.com * * Date: August 17, 2005 * *------------------------------------------------------------------------- */ herr_t H5ARRAYOreadSliceLR(hid_t dataset_id, hid_t type_id, hsize_t start, hsize_t stop, void *data) { hid_t space_id; hid_t mem_space_id; hsize_t count[1] = {stop - start}; hsize_t stride[1] = {1}; hsize_t offset[1] = {start}; /* Get the dataspace handle */ if ( (space_id = H5Dget_space(dataset_id)) < 0 ) goto out; /* Define a hyperslab in the dataset of the size of the records */ if ( H5Sselect_hyperslab(space_id, H5S_SELECT_SET, offset, stride, count, NULL) < 0 ) goto out; /* Create a memory dataspace handle */ if ( (mem_space_id = H5Screate_simple(1, count, NULL)) < 0 ) goto out; /* Read */ if ( H5Dread( dataset_id, type_id, mem_space_id, space_id, H5P_DEFAULT, data ) < 0 ) goto out; /* Release resources */ /* Terminate access to the memory dataspace */ if ( H5Sclose( mem_space_id ) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; return 0; out: H5Dclose( dataset_id ); return -1; } PyTables-v.3.1.1/src/H5ARRAY-opt.h000066400000000000000000000025741231437614300163320ustar00rootroot00000000000000#include herr_t H5ARRAYOinit_readSlice( hid_t dataset_id, hid_t *mem_space_id, hsize_t count ); herr_t H5ARRAYOread_readSlice( hid_t dataset_id, hid_t type_id, hsize_t irow, hsize_t start, hsize_t stop, void *data ); herr_t H5ARRAYOread_readSortedSlice( hid_t dataset_id, hid_t mem_space_id, hid_t type_id, hsize_t irow, hsize_t start, hsize_t stop, void *data ); herr_t H5ARRAYOread_readBoundsSlice( hid_t dataset_id, hid_t mem_space_id, hid_t type_id, hsize_t irow, hsize_t start, hsize_t stop, void *data ); herr_t H5ARRAYOreadSliceLR( hid_t dataset_id, hid_t type_id, hsize_t start, hsize_t stop, void *data ); PyTables-v.3.1.1/src/H5ARRAY.c000066400000000000000000000535201231437614300155220ustar00rootroot00000000000000#include "H5ATTR.h" #include "tables.h" #include "utils.h" #include "H5Zlzo.h" /* Import FILTER_LZO */ #include "H5Zbzip2.h" /* Import FILTER_BZIP2 */ #include "blosc_filter.h" /* Import FILTER_BLOSC */ #include #include /*------------------------------------------------------------------------- * * Public functions * *------------------------------------------------------------------------- */ /*------------------------------------------------------------------------- * Function: H5ARRAYmake * * Purpose: Creates and writes a dataset of a type type_id * * Return: Success: 0, Failure: -1 * * Programmer: F. Alted. October 21, 2002 * * Date: March 19, 2001 * * Comments: Modified by F. Alted. November 07, 2003 * *------------------------------------------------------------------------- */ herr_t H5ARRAYmake( hid_t loc_id, const char *dset_name, const char *obversion, const int rank, const hsize_t *dims, int extdim, hid_t type_id, hsize_t *dims_chunk, void *fill_data, int compress, char *complib, int shuffle, int fletcher32, const void *data) { hid_t dataset_id, space_id; hsize_t *maxdims = NULL; hid_t plist_id = 0; unsigned int cd_values[7]; int blosc_compcode; char *blosc_compname = NULL; int chunked = 0; int i; /* Check whether the array has to be chunked or not */ if (dims_chunk) { chunked = 1; } if(chunked) { maxdims = malloc(rank*sizeof(hsize_t)); if(!maxdims) return -1; for(i=0;i dims[_extdim] ) { printf("Asking for a range of rows exceeding the available ones!.\n"); goto out; } /* Define a hyperslab in the dataset of the size of the records */ for (i=0; i dims[i] ) { printf("Asking for a range of rows exceeding the available ones!.\n"); goto out; } } /* Define a hyperslab in the dataset of the size of the records */ if ( H5Sselect_hyperslab( space_id, H5S_SELECT_SET, offset, stride, count, NULL) < 0 ) goto out; /* Create a memory dataspace handle */ if ( (mem_space_id = H5Screate_simple( rank, count, NULL )) < 0 ) goto out; /* Read */ if ( H5Dread( dataset_id, type_id, mem_space_id, space_id, H5P_DEFAULT, data ) < 0 ) goto out; /* Release resources */ free(dims); free(count); /* Terminate access to the memory dataspace */ if ( H5Sclose( mem_space_id ) < 0 ) goto out; } else { /* Scalar case */ /* Read all the dataset */ if (H5Dread(dataset_id, type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data) < 0) goto out; } /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; return 0; out: /* H5Dclose( dataset_id ); */ if (dims) free(dims); if (count) free(count); return -1; } /* The next represents a try to implement getCoords for != operator */ /* but it turned out to be too difficult, well, at least to me :( */ /* 2004-06-22 */ /*------------------------------------------------------------------------- * Function: H5ARRAYreadIndex * * Purpose: Reads a slice of array from disk for indexing purposes. * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted, faltet@pytables.com * * Date: June 21, 2004 * *------------------------------------------------------------------------- */ herr_t H5ARRAYreadIndex( hid_t dataset_id, hid_t type_id, int notequal, hsize_t *start, hsize_t *stop, hsize_t *step, void *data ) { hid_t mem_space_id; hid_t space_id; hsize_t *dims = NULL; hsize_t *count = NULL; hsize_t *count2 = NULL; hsize_t *offset2 = NULL; hsize_t *stride = (hsize_t *)step; hsize_t *offset = (hsize_t *)start; int rank; int i; /* Get the dataspace handle */ if ( (space_id = H5Dget_space( dataset_id )) < 0 ) goto out; /* Get the rank */ if ( (rank = H5Sget_simple_extent_ndims(space_id)) < 0 ) goto out; if (rank) { /* Array case */ /* Book some memory for the selections */ dims = (hsize_t *)malloc(rank*sizeof(hsize_t)); count = (hsize_t *)malloc(rank*sizeof(hsize_t)); count2 = (hsize_t *)malloc(rank*sizeof(hsize_t)); offset2 = (hsize_t *)malloc(rank*sizeof(hsize_t)); /* Get dataset dimensionality */ if ( H5Sget_simple_extent_dims( space_id, dims, NULL) < 0 ) goto out; for(i=0;i dims[i] ) { printf("Asking for a range of rows exceeding the available ones!.\n"); goto out; } } /* Define a hyperslab in the dataset of the size of the records */ if ( H5Sselect_hyperslab( space_id, H5S_SELECT_SET, offset, stride, count, NULL) < 0 ) goto out; /* If we want the complementary, do a NOTA against all the row */ if (notequal) { offset2[0] = offset[0]; count2[0] = count[0]; offset2[1] = 0; count2[1] = dims[1]; /* All the row */ count[0] = 1; count[1] = dims[1] - count[1]; /* For memory dataspace */ if ( H5Sselect_hyperslab( space_id, H5S_SELECT_NOTA, offset2, stride, count2, NULL) < 0 ) goto out; } /* Create a memory dataspace handle */ if ( (mem_space_id = H5Screate_simple( rank, count, NULL )) < 0 ) goto out; /* Read */ if ( H5Dread( dataset_id, type_id, mem_space_id, space_id, H5P_DEFAULT, data ) < 0 ) goto out; /* Release resources */ free(dims); free(count); free(offset2); free(count2); /* Terminate access to the memory dataspace */ if ( H5Sclose( mem_space_id ) < 0 ) goto out; } else { /* Scalar case */ /* Read all the dataset */ if (H5Dread(dataset_id, type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data) < 0) goto out; } /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; return 0; out: if (dims) free(dims); if (count) free(count); return -1; } /*------------------------------------------------------------------------- * Function: H5ARRAYget_ndims * * Purpose: Gets the dimensionality of an array. * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted * * Date: October 22, 2002 * * Modification: October 13, 2008 * This routine not longer returns the dimensionality of data types * in case they are H5T_ARRAY. * *------------------------------------------------------------------------- */ herr_t H5ARRAYget_ndims( hid_t dataset_id, int *rank ) { hid_t space_id; /* Get the dataspace handle */ if ( (space_id = H5Dget_space( dataset_id )) < 0 ) goto out; /* Get rank */ if ( (*rank = H5Sget_simple_extent_ndims( space_id )) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; return 0; out: return -1; } /* Modified version of H5LTget_dataset_info. */ herr_t H5ARRAYget_info( hid_t dataset_id, hid_t type_id, hsize_t *dims, hsize_t *maxdims, H5T_class_t *class_id, char *byteorder) { hid_t space_id; /* Get the class. */ *class_id = H5Tget_class( type_id ); /* Get the dataspace handle */ if ( (space_id = H5Dget_space( dataset_id )) < 0 ) goto out; /* Get dimensions */ if ( H5Sget_simple_extent_dims( space_id, dims, maxdims) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; /* Get the byteorder */ /* Only integer, float, time, enumerate and array classes can be byteordered */ if ((*class_id == H5T_INTEGER) || (*class_id == H5T_FLOAT) || (*class_id == H5T_BITFIELD) || (*class_id == H5T_COMPOUND) || (*class_id == H5T_TIME) || (*class_id == H5T_ENUM) || (*class_id == H5T_ARRAY)) { get_order(type_id, byteorder); } else { strcpy(byteorder, "irrelevant"); } return 0; out: return -1; } /*------------------------------------------------------------------------- * Function: H5ARRAYget_chunkshape * * Purpose: Gets the chunkshape of a dataset. * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted * * Date: May 20, 2004 * *------------------------------------------------------------------------- */ herr_t H5ARRAYget_chunkshape( hid_t dataset_id, int rank, hsize_t *dims_chunk) { hid_t plist_id; H5D_layout_t layout; /* Get creation properties list */ if ( (plist_id = H5Dget_create_plist( dataset_id )) < 0 ) goto out; /* Get the dataset layout */ layout = H5Pget_layout(plist_id); if (layout != H5D_CHUNKED) { H5Pclose( plist_id ); return -1; } /* Get the chunkshape for all dimensions */ if (H5Pget_chunk(plist_id, rank, dims_chunk ) < 0 ) goto out; /* Terminate access to the datatype */ if ( H5Pclose( plist_id ) < 0 ) goto out; return 0; out: if (dims_chunk) free(dims_chunk); return -1; } /*------------------------------------------------------------------------- * Function: H5ARRAYget_fill_value * * Purpose: Gets the fill value of a dataset. * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted * * Date: Mar 03, 2009 * *------------------------------------------------------------------------- */ herr_t H5ARRAYget_fill_value( hid_t dataset_id, hid_t type_id, int *status, void *value) { hid_t plist_id; /* Get creation properties list */ if ( (plist_id = H5Dget_create_plist(dataset_id)) < 0 ) goto out; /* How the fill value is defined? */ if ( (H5Pfill_value_defined(plist_id, status)) < 0 ) goto out; if ( *status == H5D_FILL_VALUE_USER_DEFINED ) { if ( H5Pget_fill_value(plist_id, type_id, value) < 0 ) goto out; } /* Terminate access to the datatype */ if ( H5Pclose( plist_id ) < 0 ) goto out; return 0; out: return -1; } PyTables-v.3.1.1/src/H5ARRAY.h000066400000000000000000000056661231437614300155370ustar00rootroot00000000000000#ifndef _H5ARRAY_H #define _H5ARRAY_H #include #define TESTING(WHAT) {printf("%-70s", "Testing " WHAT); fflush(stdout);} #define PASSED() {puts(" PASSED");fflush(stdout);} #define H5_FAILED() {puts("*FAILED*");fflush(stdout);} #define SKIPPED() {puts(" -SKIP-");fflush(stdout);} #ifdef __cplusplus extern "C" { #endif herr_t H5ARRAYmake( hid_t loc_id, const char *dset_name, const char *obversion, const int rank, const hsize_t *dims, int extdim, hid_t type_id, hsize_t *dims_chunk, void *fill_data, int compress, char *complib, int shuffle, int fletcher32, const void *data); herr_t H5ARRAYappend_records( hid_t dataset_id, hid_t type_id, const int rank, hsize_t *dims_orig, hsize_t *dims_new, int extdim, const void *data ); herr_t H5ARRAYwrite_records( hid_t dataset_id, hid_t type_id, const int rank, hsize_t *start, hsize_t *step, hsize_t *count, const void *data ); herr_t H5ARRAYread( hid_t dataset_id, hid_t type_id, hsize_t start, hsize_t nrows, hsize_t step, int extdim, void *data ); herr_t H5ARRAYreadSlice( hid_t dataset_id, hid_t type_id, hsize_t *start, hsize_t *stop, hsize_t *step, void *data ); herr_t H5ARRAYreadIndex( hid_t dataset_id, hid_t type_id, int notequal, hsize_t *start, hsize_t *stop, hsize_t *step, void *data ); herr_t H5ARRAYget_ndims( hid_t dataset_id, int *rank ); herr_t H5ARRAYget_info( hid_t dataset_id, hid_t type_id, hsize_t *dims, hsize_t *maxdims, H5T_class_t *class_id, char *byteorder); herr_t H5ARRAYget_chunkshape( hid_t dataset_id, int rank, hsize_t *dims_chunk); herr_t H5ARRAYget_fill_value( hid_t dataset_id, hid_t type_id, int *status, void *value); #ifdef __cplusplus } #endif #endif PyTables-v.3.1.1/src/H5ATTR.c000066400000000000000000000347721231437614300154260ustar00rootroot00000000000000/**************************************************************************** * NCSA HDF * * Scientific Data Technologies * * National Center for Supercomputing Applications * * University of Illinois at Urbana-Champaign * * 605 E. Springfield, Champaign IL 61820 * * * * For conditions of distribution and use, see the accompanying * * hdf/COPYING file. * * * * Modified versions of H5LT for getting and setting attributes for open * groups and leaves. * F. Alted 2005/09/29 * * ****************************************************************************/ #include #include #include "H5ATTR.h" /*------------------------------------------------------------------------- * * Set & get attribute functions * *------------------------------------------------------------------------- */ /*------------------------------------------------------------------------- * Function: H5ATTRset_attribute * * Purpose: Create an attribute named attr_name and attach it to the * object specified by the name obj_name. This supports general * n-dimensional types (rank > 0), but if rank == 0, an H5T_SCALAR is * chosen. * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted * * Date: October 18, 2006 * * Comments: * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5ATTRset_attribute( hid_t obj_id, const char *attr_name, hid_t type_id, size_t rank, hsize_t *dims, const char *attr_data ) { hid_t space_id; hid_t attr_id; int has_attr; /* Create the data space for the attribute. */ if (rank == 0) space_id = H5Screate( H5S_SCALAR ); else space_id = H5Screate_simple( rank, dims, NULL ); /* Verify whether the attribute already exists */ has_attr = H5ATTRfind_attribute( obj_id, attr_name ); /* The attribute already exists, delete it */ if ( has_attr == 1 ) { if ( H5Adelete( obj_id, attr_name ) < 0 ) goto out; } /* Create and write the attribute */ attr_id = H5Acreate( obj_id, attr_name, type_id, space_id, H5P_DEFAULT, H5P_DEFAULT ); if ( H5Awrite( attr_id, type_id, attr_data ) < 0 ) goto out; H5Aclose( attr_id ); H5Sclose( space_id ); return 0; out: return -1; } /*------------------------------------------------------------------------- * Function: H5ATTRset_attribute_string * * Purpose: Creates and writes a string attribute named attr_name and attaches * it to the object specified by the name obj_name. * * Return: Success: 0, Failure: -1 * * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu * * Date: July 23, 2001 * * Comments: If the attribute already exists, it is overwritten * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5ATTRset_attribute_string( hid_t obj_id, const char *attr_name, const char *attr_data, hsize_t attr_size, int cset ) { hid_t attr_type; /*size_t attr_size;*/ hid_t attr_space_id; hid_t attr_id; int has_attr; /* Create the attribute */ if ( (attr_type = H5Tcopy( H5T_C_S1 )) < 0 ) goto out; if ( ( ( cset == H5T_CSET_ASCII ) || ( cset == H5T_CSET_UTF8 ) ) && ( H5Tset_cset( attr_type, cset ) < 0 ) ) goto out; if ( cset == H5T_CSET_ASCII ) attr_size += 1; /* extra null term */ if ( ( attr_size > 0 ) && ( H5Tset_size( attr_type, attr_size) < 0 ) ) goto out; if ( H5Tset_strpad( attr_type, H5T_STR_NULLTERM ) < 0 ) goto out; if ( (attr_space_id = H5Screate( H5S_SCALAR )) < 0 ) goto out; /* Verify if the attribute already exists */ has_attr = H5ATTRfind_attribute( obj_id, attr_name ); /* The attribute already exists, delete it */ if ( has_attr == 1 ) { if ( H5Adelete( obj_id, attr_name ) < 0 ) goto out; } /* Create and write the attribute */ if ( (attr_id = H5Acreate( obj_id, attr_name, attr_type, attr_space_id, H5P_DEFAULT, H5P_DEFAULT )) < 0 ) goto out; if ( H5Awrite( attr_id, attr_type, attr_data ) < 0 ) goto out; if ( H5Aclose( attr_id ) < 0 ) goto out; if ( H5Sclose( attr_space_id ) < 0 ) goto out; if ( H5Tclose(attr_type) < 0 ) goto out; return 0; out: return -1; } /*------------------------------------------------------------------------- * Function: H5ATTRget_attribute * * Purpose: Reads an attribute named attr_name with the memory type type_id * * Return: Success: 0, Failure: -1 * * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu * * Date: September 19, 2002 * * Comments: * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5ATTRget_attribute( hid_t obj_id, const char *attr_name, hid_t type_id, void *data ) { /* identifiers */ hid_t attr_id; if ( ( attr_id = H5Aopen_by_name(obj_id, ".", attr_name, H5P_DEFAULT, H5P_DEFAULT) ) < 0 ) return -1; if ( H5Aread( attr_id, type_id, data ) < 0 ) goto out; if ( H5Aclose( attr_id ) < 0 ) return -1; return 0; out: H5Aclose( attr_id ); return -1; } /*------------------------------------------------------------------------- * Function: H5ATTRget_attribute_string * * Purpose: Reads an string attribute named attr_name. * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted, faltet@pytables.com * * Date: February 23, 2005 * * Comments: * * Modifications: * *------------------------------------------------------------------------- */ hsize_t H5ATTRget_attribute_string( hid_t obj_id, const char *attr_name, char **data, int *cset ) { /* identifiers */ hid_t attr_id; hid_t attr_type; hsize_t type_size = 0; htri_t is_vlstr = 0; *data = NULL; if ( ( attr_id = H5Aopen_by_name(obj_id, ".", attr_name, H5P_DEFAULT, H5P_DEFAULT) ) < 0 ) return -1; if ( (attr_type = H5Aget_type( attr_id )) < 0 ) goto out; if ( ( cset != NULL ) && ( ( *cset = H5Tget_cset( attr_type ) ) < 0 ) ) goto out; is_vlstr = H5Tis_variable_str( attr_type ); if ( is_vlstr == 0 ) { /* Get the size */ if ( (type_size = H5Tget_size( attr_type )) < 0 ) goto out; /* Malloc space enough for the string, plus 1 for the trailing '\0' */ *data = (char *)malloc(type_size+1); if ( H5Aread( attr_id, attr_type, *data ) < 0 ) goto out; /* Set the last character to \0 in case we are dealing with space padded strings */ (*data)[type_size] = '\0'; } else { if ( H5Aread( attr_id, attr_type, data ) < 0 ) goto out; type_size = strlen( *data ); } if ( H5Tclose( attr_type ) < 0 ) goto out; if ( H5Aclose( attr_id ) < 0 ) return -1; return type_size; out: H5Tclose( attr_type ); H5Aclose( attr_id ); if ( (is_vlstr == 0) && (*data != NULL) ) free(*data); *data = NULL; return -1; } /*------------------------------------------------------------------------- * Function: H5ATTRget_attribute_vlen_string_array * * Purpose: Reads a variable length string attribute named attr_name. * * Return: Success: number of elements of the array, Failure: -1 * * Programmer: Antonio Valentino * * Date: November 27, 2011 * * Comments: only rank 1 attributes of 8bit strings are supported * * Modifications: * *------------------------------------------------------------------------- */ hsize_t H5ATTRget_attribute_vlen_string_array( hid_t obj_id, const char *attr_name, char ***data, int *cset ) { /* identifiers */ hid_t attr_id = -1, attr_type = -1, space_id = -1; hsize_t nelements = 0, *dims = NULL; int ndims = 0, i; *data = NULL; if ( ( attr_id = H5Aopen_by_name( obj_id, ".", attr_name, H5P_DEFAULT, H5P_DEFAULT ) ) < 0 ) return -1; if ( (attr_type = H5Aget_type( attr_id )) < 0 ) goto out; if ( ( cset != NULL ) && ( ( *cset = H5Tget_cset( attr_type ) ) < 0 ) ) goto out; if ( (space_id = H5Aget_space( attr_id )) < 0 ) goto out; if ( (ndims = H5Sget_simple_extent_ndims( space_id )) < 1 ) goto out; if ( (dims = (hsize_t *)malloc(ndims * sizeof(hsize_t))) == NULL ) goto out; if ( H5Sget_simple_extent_dims( space_id, dims, NULL ) < 0 ) goto out; nelements = 1; for ( i = 0; i < ndims; ++i ) nelements *= dims[i]; free( dims ); dims = NULL; if ((*data = (char **)malloc( nelements * sizeof(char*))) == NULL ) goto out; if ( H5Aread( attr_id, attr_type, *data ) < 0 ) goto out; if ( H5Tclose( attr_type ) < 0 ) goto out; if ( H5Sclose( space_id ) < 0 ) goto out; if ( H5Aclose( attr_id ) < 0 ) return -1; return nelements; out: if ( *data != NULL ) free( *data ); *data = NULL; if ( dims != NULL ) free( dims ); H5Tclose( attr_type ); H5Sclose( space_id ); H5Aclose( attr_id ); return -1; } /*------------------------------------------------------------------------- * * Helper functions * *------------------------------------------------------------------------- */ /*------------------------------------------------------------------------- * Function: find_attr * * Purpose: operator function used by H5ATTRfind_attribute * * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu * * Date: June 21, 2001 * * Comments: * * Modifications: * *------------------------------------------------------------------------- */ static herr_t find_attr( hid_t loc_id, const char *name, const H5A_info_t *ainfo, void *op_data) { /* Define a default zero value for return. This will cause the * iterator to continue if the palette attribute is not found yet. */ int ret = 0; char *attr_name = (char*)op_data; /* Shut the compiler up */ loc_id=loc_id; /* Define a positive value for return value if the attribute was * found. This will cause the iterator to immediately return that * positive value, indicating short-circuit success */ if( strcmp( name, attr_name ) == 0 ) ret = 1; return ret; } /*------------------------------------------------------------------------- * Function: H5ATTRfind_attribute * * Purpose: Inquires if an attribute named attr_name exists attached * to the object loc_id. * * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu * * Date: June 21, 2001 * * Comments: * The function uses H5Aiterate with the operator function find_attr * * Return: * Success: The return value of the first operator that * returns non-zero, or zero if all members were * processed with no operator returning non-zero. * * Failure: Negative if something goes wrong within the * library, or the negative value returned by one * of the operators. * *------------------------------------------------------------------------- */ herr_t H5ATTRfind_attribute( hid_t loc_id, const char* attr_name ) { hsize_t attr_num; herr_t ret; attr_num = 0; ret = H5Aiterate( loc_id, H5_INDEX_CRT_ORDER, H5_ITER_NATIVE, &attr_num, find_attr, (void *)attr_name ); return ret; } /*------------------------------------------------------------------------- * Function: H5ATTRget_attribute_ndims * * Purpose: Gets the dimensionality of an attribute. * * Return: Success: 0, Failure: -1 * * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu * * Date: September 4, 2001 * *------------------------------------------------------------------------- */ herr_t H5ATTRget_type_ndims( hid_t obj_id, const char *attr_name, hid_t *type_id, H5T_class_t *class_id, size_t *type_size, int *rank ) { hid_t attr_id; hid_t space_id; /* Open the attribute. */ if ( ( attr_id = H5Aopen_by_name(obj_id, ".", attr_name, H5P_DEFAULT, H5P_DEFAULT) ) < 0 ) { return -1; } /* Get an identifier for the datatype. */ *type_id = H5Aget_type( attr_id ); /* Get the class. */ *class_id = H5Tget_class( *type_id ); /* Get the size. */ *type_size = H5Tget_size( *type_id ); /* Get the dataspace handle */ if ( (space_id = H5Aget_space( attr_id )) < 0 ) goto out; /* Get rank */ if ( (*rank = H5Sget_simple_extent_ndims( space_id )) < 0 ) goto out; /* Terminate access to the attribute */ if ( H5Sclose( space_id ) < 0 ) goto out; /* End access to the attribute */ if ( H5Aclose( attr_id ) ) goto out;; return 0; out: H5Tclose( *type_id ); H5Aclose( attr_id ); return -1; } /*------------------------------------------------------------------------- * Function: H5ATTRget_dims * * Purpose: Gets information about an attribute. * * Return: Success: 0, Failure: -1 * * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu * * Date: September 4, 2001 * *------------------------------------------------------------------------- */ herr_t H5ATTRget_dims( hid_t obj_id, const char *attr_name, hsize_t *dims) { hid_t attr_id; hid_t space_id; /* Open the attribute. */ if ( ( attr_id = H5Aopen_by_name(obj_id, ".", attr_name, H5P_DEFAULT, H5P_DEFAULT) ) < 0 ) { return -1; } /* Get the dataspace handle */ if ( (space_id = H5Aget_space( attr_id )) < 0 ) goto out; /* Get dimensions */ if ( H5Sget_simple_extent_dims( space_id, dims, NULL) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; /* End access to the attribute */ if ( H5Aclose( attr_id ) ) goto out; return 0; out: H5Aclose( attr_id ); return -1; } PyTables-v.3.1.1/src/H5ATTR.h000066400000000000000000000062261231437614300154240ustar00rootroot00000000000000 /**************************************************************************** * NCSA HDF * * Scientific Data Technologies * * National Center for Supercomputing Applications * * University of Illinois at Urbana-Champaign * * 605 E. Springfield, Champaign IL 61820 * * * * For conditions of distribution and use, see the accompanying * * hdf/COPYING file. * * * * Modified versions of H5LT for getting and setting attributes for open * groups and leaves. * F. Alted 2005/09/29 * * ****************************************************************************/ #include #ifdef __cplusplus extern "C" { #endif /*------------------------------------------------------------------------- * * Set & get attribute functions * *------------------------------------------------------------------------- */ herr_t H5ATTRset_attribute( hid_t obj_id, const char *attr_name, hid_t type_id, size_t rank, hsize_t *dims, const char *attr_data ); herr_t H5ATTRset_attribute_string( hid_t obj_id, const char *attr_name, const char *attr_data, hsize_t attr_size, int cset ); herr_t H5ATTRget_attribute( hid_t loc_id, const char *attr_name, hid_t type_id, void *data ); hsize_t H5ATTRget_attribute_string( hid_t obj_id, const char *attr_name, char **data, int *cset ); hsize_t H5ATTRget_attribute_vlen_string_array( hid_t obj_id, const char *attr_name, char ***data, int *cset ); /*------------------------------------------------------------------------- * * Query attribute functions * *------------------------------------------------------------------------- */ herr_t H5ATTRfind_attribute( hid_t loc_id, const char* attr_name ); herr_t H5ATTRget_type_ndims( hid_t loc_id, const char *attr_name, hid_t *type_id, H5T_class_t *class_id, size_t *type_size, int *rank ); herr_t H5ATTRget_dims( hid_t loc_id, const char *attr_name, hsize_t *dims ); #ifdef __cplusplus } #endif PyTables-v.3.1.1/src/H5TB-opt.c000066400000000000000000000436401231437614300157530ustar00rootroot00000000000000/**************************************************************************** * NCSA HDF * * Scientific Data Technologies * * National Center for Supercomputing Applications * * University of Illinois at Urbana-Champaign * * 605 E. Springfield, Champaign IL 61820 * * * * For conditions of distribution and use, see the accompanying * * hdf/COPYING file. * * * ****************************************************************************/ /* WARNING: This is a highly stripped down and modified version of the original H5TB.c that comes with the HDF5 library. These modifications has been done in order to serve the needs of PyTables, and specially for supporting nested datatypes. In particular, the VERSION attribute is out of sync so it is not guaranteed that the resulting PyTables objects will be identical with those generated with HDF5_HL, although they should remain largely compatibles. F. Alted 2005/06/09 Other modifications are that these routines are meant for opened nodes, and do not spend time opening and closing datasets. F. Alted 2005/09/29 */ #include #include #include "H5TB-opt.h" #include "tables.h" #include "H5Zlzo.h" /* Import FILTER_LZO */ #include "H5Zbzip2.h" /* Import FILTER_BZIP2 */ #include "blosc_filter.h" /* Import FILTER_BLOSC */ /* Define this in order to shrink datasets after deleting */ #if 1 #define SHRINK #endif /*------------------------------------------------------------------------- * * Create functions * *------------------------------------------------------------------------- */ /*------------------------------------------------------------------------- * Function: H5TBmake_table * * Purpose: Make a table * * Return: Success: 0, Failure: -1 * * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu * Quincey Koziol * * Date: January 17, 2001 * * Comments: The data is packed * * Heavily modified and not compliant with attributes * May 20, 2005 * F. Alted * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5TBOmake_table( const char *table_title, hid_t loc_id, const char *dset_name, char *version, const char *class_, hid_t type_id, hsize_t nrecords, hsize_t chunk_size, void *fill_data, int compress, char *complib, int shuffle, int fletcher32, const void *data ) { hid_t dataset_id; hid_t space_id; hid_t plist_id; hsize_t dims[1]; hsize_t dims_chunk[1]; hsize_t maxdims[1] = { H5S_UNLIMITED }; unsigned int cd_values[7]; int blosc_compcode; char *blosc_compname = NULL; dims[0] = nrecords; dims_chunk[0] = chunk_size; /* Create a simple data space with unlimited size */ if ( (space_id = H5Screate_simple( 1, dims, maxdims )) < 0 ) return -1; /* Modify dataset creation properties, i.e. enable chunking */ plist_id = H5Pcreate (H5P_DATASET_CREATE); if ( H5Pset_chunk ( plist_id, 1, dims_chunk ) < 0 ) return -1; /* Set the fill value using a struct as the data type. */ if ( fill_data) { if ( H5Pset_fill_value( plist_id, type_id, fill_data ) < 0 ) return -1; } else { if ( H5Pset_fill_time(plist_id, H5D_FILL_TIME_ALLOC) < 0 ) return -1; } /* Dataset creation property list is modified to use filters */ /* Fletcher must be first */ if (fletcher32) { if ( H5Pset_fletcher32( plist_id) < 0 ) return -1; } /* Then shuffle (blosc shuffles inplace) */ if (shuffle && (strncmp(complib, "blosc", 5) != 0)) { if ( H5Pset_shuffle( plist_id) < 0 ) return -1; } /* Finally compression */ if ( compress ) { cd_values[0] = compress; cd_values[1] = (int)(atof(version) * 10); cd_values[2] = Table; /* The default compressor in HDF5 (zlib) */ if (strcmp(complib, "zlib") == 0) { if ( H5Pset_deflate( plist_id, compress) < 0 ) return -1; } /* The Blosc compressor does accept parameters */ else if (strcmp(complib, "blosc") == 0) { cd_values[4] = compress; cd_values[5] = shuffle; if ( H5Pset_filter( plist_id, FILTER_BLOSC, H5Z_FLAG_OPTIONAL, 6, cd_values) < 0 ) return -1; } /* The Blosc compressor can use other compressors */ else if (strncmp(complib, "blosc:", 6) == 0) { cd_values[4] = compress; cd_values[5] = shuffle; blosc_compname = complib + 6; blosc_compcode = blosc_compname_to_compcode(blosc_compname); cd_values[6] = blosc_compcode; if ( H5Pset_filter( plist_id, FILTER_BLOSC, H5Z_FLAG_OPTIONAL, 7, cd_values) < 0 ) return -1; } /* The LZO compressor does accept parameters */ else if (strcmp(complib, "lzo") == 0) { if ( H5Pset_filter( plist_id, FILTER_LZO, H5Z_FLAG_OPTIONAL, 3, cd_values) < 0 ) return -1; } /* The bzip2 compress does accept parameters */ else if (strcmp(complib, "bzip2") == 0) { if ( H5Pset_filter( plist_id, FILTER_BZIP2, H5Z_FLAG_OPTIONAL, 3, cd_values) < 0 ) return -1; } else { /* Compression library not supported */ return -1; } } /* Create the dataset. */ if ( (dataset_id = H5Dcreate( loc_id, dset_name, type_id, space_id, H5P_DEFAULT, plist_id, H5P_DEFAULT )) < 0 ) goto out; /* Only write if there is something to write */ if ( data ) { /* Write data to the dataset. */ if ( H5Dwrite( dataset_id, type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data ) < 0 ) goto out; } /* Terminate access to the data space. */ if ( H5Sclose( space_id ) < 0 ) goto out; /* End access to the property list */ if ( H5Pclose( plist_id ) < 0 ) goto out; /* Return the object unique ID for future references */ return dataset_id; /* error zone, gracefully close */ out: H5E_BEGIN_TRY { H5Dclose(dataset_id); H5Sclose(space_id); H5Pclose(plist_id); } H5E_END_TRY; return -1; } /*------------------------------------------------------------------------- * Function: H5TBOread_records * * Purpose: Read records from an opened table * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted, faltet@pytables.com * * Date: April 19, 2003 * * Comments: * * Modifications: * * *------------------------------------------------------------------------- */ herr_t H5TBOread_records( hid_t dataset_id, hid_t mem_type_id, hsize_t start, hsize_t nrecords, void *data ) { hid_t space_id; hid_t mem_space_id; hsize_t count[1]; hsize_t offset[1]; /* Get the dataspace handle */ if ( (space_id = H5Dget_space( dataset_id )) < 0 ) goto out; /* Define a hyperslab in the dataset of the size of the records */ offset[0] = start; count[0] = nrecords; if ( H5Sselect_hyperslab(space_id, H5S_SELECT_SET, offset, NULL, count, NULL) < 0 ) goto out; /* Create a memory dataspace handle */ if ( (mem_space_id = H5Screate_simple( 1, count, NULL )) < 0 ) goto out; if ( H5Dread(dataset_id, mem_type_id, mem_space_id, space_id, H5P_DEFAULT, data ) < 0 ) goto out; /* Terminate access to the memory dataspace */ if ( H5Sclose( mem_space_id ) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; return 0; out: return -1; } /*------------------------------------------------------------------------- * Function: H5TBOread_elements * * Purpose: Read selected records from an opened table * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted, faltet@pytables.com * * Date: April 19, 2003 * * Comments: * * Modifications: * * *------------------------------------------------------------------------- */ herr_t H5TBOread_elements( hid_t dataset_id, hid_t mem_type_id, hsize_t nrecords, void *coords, void *data ) { hid_t space_id; hid_t mem_space_id; hsize_t count[1]; /* Get the dataspace handle */ if ( (space_id = H5Dget_space( dataset_id )) < 0 ) goto out; /* Define a selection of points in the dataset */ if ( H5Sselect_elements(space_id, H5S_SELECT_SET, (size_t)nrecords, (const hsize_t *)coords) < 0 ) goto out; /* Create a memory dataspace handle */ count[0] = nrecords; if ( (mem_space_id = H5Screate_simple( 1, count, NULL )) < 0 ) goto out; if ( H5Dread( dataset_id, mem_type_id, mem_space_id, space_id, H5P_DEFAULT, data ) < 0 ) goto out; /* Terminate access to the memory dataspace */ if ( H5Sclose( mem_space_id ) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; return 0; out: return -1; } /*------------------------------------------------------------------------- * Function: H5TBOappend_records * * Purpose: Appends records to a table * * Return: Success: 0, Failure: -1 * * Programmers: * Francesc Alted, faltet@pytables.com * * Date: April 20, 2003 * * Comments: Uses memory offsets * * Modifications: * * *------------------------------------------------------------------------- */ herr_t H5TBOappend_records( hid_t dataset_id, hid_t mem_type_id, hsize_t nrecords, hsize_t nrecords_orig, const void *data ) { hid_t space_id = -1; /* Shut up the compiler */ hsize_t count[1]; hsize_t offset[1]; hid_t mem_space_id = -1; /* Shut up the compiler */ hsize_t dims[1]; /* Extend the dataset */ dims[0] = nrecords_orig; dims[0] += nrecords; if ( H5Dset_extent(dataset_id, dims) < 0 ) goto out; /* Create a simple memory data space */ count[0]=nrecords; if ( (mem_space_id = H5Screate_simple( 1, count, NULL )) < 0 ) return -1; /* Get the file data space */ if ( (space_id = H5Dget_space(dataset_id)) < 0 ) return -1; /* Define a hyperslab in the dataset */ offset[0] = nrecords_orig; if ( H5Sselect_hyperslab( space_id, H5S_SELECT_SET, offset, NULL, count, NULL) < 0 ) goto out; if ( H5Dwrite( dataset_id, mem_type_id, mem_space_id, space_id, H5P_DEFAULT, data ) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( mem_space_id ) < 0 ) goto out; if ( H5Sclose( space_id ) < 0 ) goto out; return 0; out: return -1; } /*------------------------------------------------------------------------- * Function: H5TBOwrite_records * * Purpose: Writes records * * Return: Success: 0, Failure: -1 * * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu * * Date: November 19, 2001 * * Comments: Uses memory offsets * * Modifications: * - Added a step parameter in order to support strided writing. * Francesc Alted, faltet@pytables.com. 2004-08-12 * * - Removed the type_size which was unnecessary * Francesc Alted, 2005-10-25 * *------------------------------------------------------------------------- */ herr_t H5TBOwrite_records( hid_t dataset_id, hid_t mem_type_id, hsize_t start, hsize_t nrecords, hsize_t step, const void *data ) { hsize_t count[1]; hsize_t stride[1]; hsize_t offset[1]; hid_t space_id; hid_t mem_space_id; hsize_t dims[1]; /* Get the dataspace handle */ if ( (space_id = H5Dget_space( dataset_id )) < 0 ) goto out; /* Get records */ if ( H5Sget_simple_extent_dims( space_id, dims, NULL) < 0 ) goto out; /* if ( start + nrecords > dims[0] ) */ if ( start + (nrecords-1) * step + 1 > dims[0] ) goto out; /* Define a hyperslab in the dataset of the size of the records */ offset[0] = start; stride[0] = step; count[0] = nrecords; if ( H5Sselect_hyperslab( space_id, H5S_SELECT_SET, offset, stride, count, NULL) < 0 ) goto out; /* Create a memory dataspace handle */ if ( (mem_space_id = H5Screate_simple( 1, count, NULL )) < 0 ) goto out; if ( H5Dwrite( dataset_id, mem_type_id, mem_space_id, space_id, H5P_DEFAULT, data ) < 0 ) goto out; /* Terminate access to the memory dataspace */ if ( H5Sclose( mem_space_id ) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; return 0; out: return -1; } /*------------------------------------------------------------------------- * Function: H5TBOwrite_elements * * Purpose: Writes records on a list of coordinates * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted, * * Date: October 25, 2005 * * Comments: * * *------------------------------------------------------------------------- */ herr_t H5TBOwrite_elements( hid_t dataset_id, hid_t mem_type_id, hsize_t nrecords, const void *coords, const void *data ) { hsize_t count[1]; hid_t space_id; hid_t mem_space_id; /* Get the dataspace handle */ if ( (space_id = H5Dget_space( dataset_id )) < 0 ) goto out; /* Define a selection of points in the dataset */ if ( H5Sselect_elements(space_id, H5S_SELECT_SET, (size_t)nrecords, (const hsize_t *)coords) < 0 ) goto out; /* Create a memory dataspace handle */ count[0] = nrecords; if ( (mem_space_id = H5Screate_simple( 1, count, NULL )) < 0 ) goto out; if ( H5Dwrite( dataset_id, mem_type_id, mem_space_id, space_id, H5P_DEFAULT, data ) < 0 ) goto out; /* Terminate access to the memory dataspace */ if ( H5Sclose( mem_space_id ) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; return 0; out: return -1; } /*------------------------------------------------------------------------- * Function: H5TBOdelete_records * * Purpose: Delete records from middle of table ("pulling up" all the records after it) * * Return: Success: 0, Failure: -1 * * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu * Modified by: F. Alted * * Date: November 26, 2001 * * Modifications: April 29, 2003 * Modifications: February 19, 2004 (buffered rewriting of trailing rows) * Modifications: September 28, 2005 (adapted to opened tables) * * *------------------------------------------------------------------------- */ herr_t H5TBOdelete_records( hid_t dataset_id, hid_t mem_type_id, hsize_t ntotal_records, size_t src_size, hsize_t start, hsize_t nrecords, hsize_t maxtuples) { hsize_t nrowsread; hsize_t read_start; hsize_t write_start; hsize_t read_nrecords; hsize_t count[1]; hsize_t offset[1]; hid_t space_id; hid_t mem_space_id; hsize_t mem_size[1]; unsigned char *tmp_buf; hsize_t dims[1]; size_t read_nbuf; /* Shut the compiler up */ tmp_buf = NULL; /*------------------------------------------------------------------------- * Read the records after the deleted one(s) *------------------------------------------------------------------------- */ read_start = start + nrecords; write_start = start; read_nrecords = ntotal_records - read_start; /* This check added for the case that there are no records to be read */ /* F. Alted 2003/07/16 */ if (read_nrecords > 0) { nrowsread = 0; while (nrowsread < read_nrecords) { if (nrowsread + maxtuples < read_nrecords) read_nbuf = (size_t)maxtuples; else read_nbuf = (size_t)(read_nrecords - nrowsread); tmp_buf = (unsigned char *)malloc(read_nbuf * src_size ); if ( tmp_buf == NULL ) return -1; /* Read the records after the deleted one(s) */ if ( H5TBOread_records(dataset_id, mem_type_id, read_start, read_nbuf, tmp_buf ) < 0 ) return -1; /*------------------------------------------------------------------------- * Write the records in another position *------------------------------------------------------------------------- */ /* Get the dataspace handle */ if ( (space_id = H5Dget_space( dataset_id )) < 0 ) goto out; /* Define a hyperslab in the dataset of the size of the records */ offset[0] = write_start; count[0] = read_nbuf; if ( H5Sselect_hyperslab( space_id, H5S_SELECT_SET, offset, NULL, count, NULL) < 0 ) goto out; /* Create a memory dataspace handle */ mem_size[0] = count[0]; if ( (mem_space_id = H5Screate_simple( 1, mem_size, NULL )) < 0 ) goto out; if ( H5Dwrite( dataset_id, mem_type_id, mem_space_id, space_id, H5P_DEFAULT, tmp_buf ) < 0 ) goto out; /* Terminate access to the memory dataspace */ if ( H5Sclose( mem_space_id ) < 0 ) goto out; /* Release the reading buffer */ free( tmp_buf ); /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; /* Update the counters */ read_start += read_nbuf; write_start += read_nbuf; nrowsread += read_nbuf; } /* while (nrowsread < read_nrecords) */ } /* if (nread_nrecords > 0) */ /*------------------------------------------------------------------------- * Change the table dimension *------------------------------------------------------------------------- */ #if defined (SHRINK) dims[0] = (int)ntotal_records - (int)nrecords; if ( H5Dset_extent( dataset_id, dims ) < 0 ) goto out; #endif return 0; out: return -1; } PyTables-v.3.1.1/src/H5TB-opt.h000066400000000000000000000041471231437614300157570ustar00rootroot00000000000000#include #ifdef __cplusplus extern "C" { #endif herr_t H5TBOmake_table( const char *table_title, hid_t loc_id, const char *dset_name, char *version, const char *class_, hid_t type_id, hsize_t nrecords, hsize_t chunk_size, void *fill_data, int compress, char *complib, int shuffle, int fletcher32, const void *data ); herr_t H5TBOread_records( hid_t dataset_id, hid_t mem_type_id, hsize_t start, hsize_t nrecords, void *data ); herr_t H5TBOread_elements( hid_t dataset_id, hid_t mem_type_id, hsize_t nrecords, void *coords, void *data ); herr_t H5TBOappend_records( hid_t dataset_id, hid_t mem_type_id, hsize_t nrecords, hsize_t nrecords_orig, const void *data ); herr_t H5TBOwrite_records( hid_t dataset_id, hid_t mem_type_id, hsize_t start, hsize_t nrecords, hsize_t step, const void *data ); herr_t H5TBOwrite_elements( hid_t dataset_id, hid_t mem_type_id, hsize_t nrecords, const void *coords, const void *data ); herr_t H5TBOdelete_records( hid_t dataset_id, hid_t mem_type_id, hsize_t ntotal_records, size_t src_size, hsize_t start, hsize_t nrecords, hsize_t maxtuples ); PyTables-v.3.1.1/src/H5VLARRAY.c000066400000000000000000000244211231437614300157620ustar00rootroot00000000000000#include "H5ATTR.h" #include "tables.h" #include "utils.h" /* get_order */ #include "H5Zlzo.h" /* Import FILTER_LZO */ #include "H5Zbzip2.h" /* Import FILTER_BZIP2 */ #include "blosc_filter.h" /* Import FILTER_BLOSC */ #include #include /*------------------------------------------------------------------------- * * Public functions * *------------------------------------------------------------------------- */ /*------------------------------------------------------------------------- * Function: H5VLARRAYmake * * Purpose: Creates and writes a dataset of a variable length type type_id * * Return: Success: 0, Failure: -1 * * Programmer: F. Alted * * Date: November 08, 2003 *------------------------------------------------------------------------- */ herr_t H5VLARRAYmake( hid_t loc_id, const char *dset_name, const char *obversion, const int rank, const hsize_t *dims, hid_t type_id, hsize_t chunk_size, void *fill_data, int compress, char *complib, int shuffle, int fletcher32, const void *data) { hvl_t vldata; hid_t dataset_id, space_id, datatype, tid1; hsize_t dataset_dims[1]; hsize_t maxdims[1] = { H5S_UNLIMITED }; hsize_t dims_chunk[1]; hid_t plist_id; unsigned int cd_values[7]; int blosc_compcode; char *blosc_compname = NULL; if (data) /* if data, one row will be filled initially */ dataset_dims[0] = 1; else /* no data, so no rows on dataset initally */ dataset_dims[0] = 0; dims_chunk[0] = chunk_size; /* Fill the vldata estructure with the data to write */ /* This is currectly not used */ vldata.p = (void *)data; vldata.len = 1; /* Only one array type to save */ /* Create a VL datatype */ if (rank == 0) { datatype = H5Tvlen_create(type_id); } else { tid1 = H5Tarray_create(type_id, rank, dims); datatype = H5Tvlen_create(tid1); H5Tclose( tid1 ); /* Release resources */ } /* The dataspace */ space_id = H5Screate_simple( 1, dataset_dims, maxdims ); /* Modify dataset creation properties, i.e. enable chunking */ plist_id = H5Pcreate (H5P_DATASET_CREATE); if ( H5Pset_chunk ( plist_id, 1, dims_chunk ) < 0 ) return -1; /* Dataset creation property list is modified to use */ /* Fletcher must be first */ if (fletcher32) { if ( H5Pset_fletcher32( plist_id) < 0 ) return -1; } /* Then shuffle (blosc shuffles inplace) */ if (shuffle && (strncmp(complib, "blosc", 5) != 0)) { if ( H5Pset_shuffle( plist_id) < 0 ) return -1; } /* Finally compression */ if (compress) { cd_values[0] = compress; cd_values[1] = (int)(atof(obversion) * 10); cd_values[2] = VLArray; /* The default compressor in HDF5 (zlib) */ if (strcmp(complib, "zlib") == 0) { if ( H5Pset_deflate( plist_id, compress) < 0 ) return -1; } /* The Blosc compressor does accept parameters */ else if (strcmp(complib, "blosc") == 0) { cd_values[4] = compress; cd_values[5] = shuffle; if ( H5Pset_filter( plist_id, FILTER_BLOSC, H5Z_FLAG_OPTIONAL, 6, cd_values) < 0 ) return -1; } /* The Blosc compressor can use other compressors */ else if (strncmp(complib, "blosc:", 6) == 0) { cd_values[4] = compress; cd_values[5] = shuffle; blosc_compname = complib + 6; blosc_compcode = blosc_compname_to_compcode(blosc_compname); cd_values[6] = blosc_compcode; if ( H5Pset_filter( plist_id, FILTER_BLOSC, H5Z_FLAG_OPTIONAL, 7, cd_values) < 0 ) return -1; } /* The LZO compressor does accept parameters */ else if (strcmp(complib, "lzo") == 0) { if ( H5Pset_filter( plist_id, FILTER_LZO, H5Z_FLAG_OPTIONAL, 3, cd_values) < 0 ) return -1; } /* The bzip2 compress does accept parameters */ else if (strcmp(complib, "bzip2") == 0) { if ( H5Pset_filter( plist_id, FILTER_BZIP2, H5Z_FLAG_OPTIONAL, 3, cd_values) < 0 ) return -1; } else { /* Compression library not supported */ fprintf(stderr, "Compression library not supported\n"); return -1; } } /* Create the dataset. */ if ((dataset_id = H5Dcreate(loc_id, dset_name, datatype, space_id, H5P_DEFAULT, plist_id, H5P_DEFAULT )) < 0 ) goto out; /* Write the dataset only if there is data to write */ if (data) if ( H5Dwrite( dataset_id, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &vldata ) < 0 ) goto out; /* Terminate access to the data space. */ if ( H5Sclose( space_id ) < 0 ) return -1; /* Release the datatype in the case that it is not an atomic type */ if ( H5Tclose( datatype ) < 0 ) return -1; /* End access to the property list */ if ( H5Pclose( plist_id ) < 0 ) goto out; return dataset_id; out: return -1; } /*------------------------------------------------------------------------- * Function: H5ARRAYappend_records * * Purpose: Appends records to an array * * Return: Success: 0, Failure: -1 * * Programmers: * Francesc Alted * * Date: October 30, 2003 * * Comments: Uses memory offsets * * Modifications: * * *------------------------------------------------------------------------- */ herr_t H5VLARRAYappend_records( hid_t dataset_id, hid_t type_id, int nobjects, hsize_t nrecords, const void *data ) { hid_t space_id; hid_t mem_space_id; hsize_t start[1]; hsize_t dataset_dims[1]; hsize_t dims_new[1] = {1}; /* Only a record on each append */ hvl_t wdata; /* Information to write */ /* Initialize VL data to write */ wdata.p=(void *)data; wdata.len=nobjects; /* Dimension for the new dataset */ dataset_dims[0] = nrecords + 1; /* Extend the dataset */ if ( H5Dset_extent( dataset_id, dataset_dims ) < 0 ) goto out; /* Create a simple memory data space */ if ( (mem_space_id = H5Screate_simple( 1, dims_new, NULL )) < 0 ) return -1; /* Get the file data space */ if ( (space_id = H5Dget_space( dataset_id )) < 0 ) return -1; /* Define a hyperslab in the dataset */ start[0] = nrecords; if ( H5Sselect_hyperslab( space_id, H5S_SELECT_SET, start, NULL, dims_new, NULL) < 0 ) goto out; if ( H5Dwrite( dataset_id, type_id, mem_space_id, space_id, H5P_DEFAULT, &wdata ) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; if ( H5Sclose( mem_space_id ) < 0 ) goto out; return 1; out: return -1; } /*------------------------------------------------------------------------- * Function: H5ARRAYmodify_records * * Purpose: Modify records of an array * * Return: Success: 0, Failure: -1 * * Programmers: * Francesc Alted * * Date: October 28, 2004 * * Comments: Uses memory offsets * * Modifications: * * *------------------------------------------------------------------------- */ herr_t H5VLARRAYmodify_records( hid_t dataset_id, hid_t type_id, hsize_t nrow, int nobjects, const void *data ) { hid_t space_id; hid_t mem_space_id; hsize_t start[1]; hsize_t dims_new[1] = {1}; /* Only a record on each update */ hvl_t wdata; /* Information to write */ /* Initialize VL data to write */ wdata.p=(void *)data; wdata.len=nobjects; /* Create a simple memory data space */ if ( (mem_space_id = H5Screate_simple( 1, dims_new, NULL )) < 0 ) return -1; /* Get the file data space */ if ( (space_id = H5Dget_space( dataset_id )) < 0 ) return -1; /* Define a hyperslab in the dataset */ start[0] = nrow; if ( H5Sselect_hyperslab( space_id, H5S_SELECT_SET, start, NULL, dims_new, NULL) < 0 ) goto out; if ( H5Dwrite( dataset_id, type_id, mem_space_id, space_id, H5P_DEFAULT, &wdata ) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; if ( H5Sclose( mem_space_id ) < 0 ) goto out; return 1; out: return -1; } /*------------------------------------------------------------------------- * Function: H5VLARRAYget_info * * Purpose: Gathers info about the VLEN type and other. * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted * * Date: November 19, 2003 * *------------------------------------------------------------------------- */ herr_t H5VLARRAYget_info( hid_t dataset_id, hid_t type_id, hsize_t *nrecords, char *base_byteorder ) { hid_t space_id; H5T_class_t base_class_id; H5T_class_t atom_class_id; hid_t atom_type_id; hid_t base_type_id; /* Get the dataspace handle */ if ( (space_id = H5Dget_space( dataset_id )) < 0 ) goto out; /* Get number of records (it should be rank-1) */ if ( H5Sget_simple_extent_dims( space_id, nrecords, NULL) < 0 ) goto out; /* Terminate access to the dataspace */ if ( H5Sclose( space_id ) < 0 ) goto out; /* Get the type of the atomic component */ atom_type_id = H5Tget_super( type_id ); /* Get the class of the atomic component. */ atom_class_id = H5Tget_class( atom_type_id ); /* Check whether the atom is an array class object or not */ if ( atom_class_id == H5T_ARRAY) { /* Get the array base component */ base_type_id = H5Tget_super( atom_type_id ); /* Get the class of base component */ base_class_id = H5Tget_class( base_type_id ); /* Release the datatypes */ if ( H5Tclose(atom_type_id ) ) goto out; } else { base_class_id = atom_class_id; base_type_id = atom_type_id; } /* Get the byteorder */ /* Only integer, float and time classes can be byteordered */ if ((base_class_id == H5T_INTEGER) || (base_class_id == H5T_FLOAT) || (base_class_id == H5T_BITFIELD) || (base_class_id == H5T_COMPOUND) || (base_class_id == H5T_TIME)) { get_order(base_type_id, base_byteorder); } else { strcpy(base_byteorder, "irrelevant"); } /* Release the datatypes */ if ( H5Tclose(base_type_id ) ) goto out; return 0; out: return -1; } PyTables-v.3.1.1/src/H5VLARRAY.h000066400000000000000000000024671231437614300157750ustar00rootroot00000000000000#ifndef _H5VLARRAY_H #define _H5VLARRAY_H #include #ifdef __cplusplus extern "C" { #endif herr_t H5VLARRAYmake( hid_t loc_id, const char *dset_name, const char *obversion, const int rank, const hsize_t *dims, hid_t type_id, hsize_t chunk_size, void *fill_data, int compress, char *complib, int shuffle, int fletcher32, const void *data); herr_t H5VLARRAYappend_records( hid_t dataset_id, hid_t type_id, int nobjects, hsize_t nrecords, const void *data ); herr_t H5VLARRAYmodify_records( hid_t dataset_id, hid_t type_id, hsize_t nrow, int nobjects, const void *data ); herr_t H5VLARRAYget_info( hid_t dataset_id, hid_t type_id, hsize_t *nrecords, char *base_byteorder); #ifdef __cplusplus } #endif #endif PyTables-v.3.1.1/src/H5Zbzip2.c000066400000000000000000000131711231437614300160220ustar00rootroot00000000000000#include "H5Zbzip2.h" #include #include #include #include #include #include #ifdef HAVE_BZ2_LIB #include "bzlib.h" #endif /* defined HAVE_BZ2_LIB */ size_t bzip2_deflate(unsigned int flags, size_t cd_nelmts, const unsigned int cd_values[], size_t nbytes, size_t *buf_size, void **buf); int register_bzip2(char **version, char **date) { #ifdef HAVE_BZ2_LIB char *libver, *versionp, *datep, *sep; H5Z_class_t filter_class = { H5Z_CLASS_T_VERS, /* H5Z_class_t version */ (H5Z_filter_t)(FILTER_BZIP2), /* filter_id */ 1, 1, /* Encoding and decoding enabled */ "bzip2", /* comment */ NULL, /* can_apply_func */ NULL, /* set_local_func */ (H5Z_func_t)(bzip2_deflate) /* filter_func */ }; /* Register the filter class for the bzip2 compressor. */ H5Zregister(&filter_class); /* Get the library major version from the version string. */ libver = strdup(BZ2_bzlibVersion()); sep = strchr(libver, ','); assert(sep != NULL); assert(*(sep + 1) == ' '); *sep = '\0'; versionp = libver; datep = sep + 2; /* after the comma and a space */ *version = strdup(versionp); *date = strdup(datep); free(libver); return 1; /* library is available */ #else return 0; /* library is not available */ #endif /* defined HAVE_BZ2_LIB */ } size_t bzip2_deflate(unsigned int flags, size_t cd_nelmts, const unsigned int cd_values[], size_t nbytes, size_t *buf_size, void **buf) { #ifdef HAVE_BZ2_LIB char *outbuf = NULL; size_t outbuflen, outdatalen; int ret; if (flags & H5Z_FLAG_REVERSE) { /** Decompress data. ** ** This process is troublesome since the size of uncompressed data ** is unknown, so the low-level interface must be used. ** Data is decompressed to the output buffer (which is sized ** for the average case); if it gets full, its size is doubled ** and decompression continues. This avoids repeatedly trying to ** decompress the whole block, which could be really inefficient. **/ bz_stream stream; char *newbuf = NULL; size_t newbuflen; /* Prepare the output buffer. */ outbuflen = nbytes * 3 + 1; /* average bzip2 compression ratio is 3:1 */ outbuf = malloc(outbuflen); if (outbuf == NULL) { fprintf(stderr, "memory allocation failed for bzip2 decompression\n"); goto cleanupAndFail; } /* Use standard malloc()/free() for internal memory handling. */ stream.bzalloc = NULL; stream.bzfree = NULL; stream.opaque = NULL; /* Start decompression. */ ret = BZ2_bzDecompressInit(&stream, 0, 0); if (ret != BZ_OK) { fprintf(stderr, "bzip2 decompression start failed with error %d\n", ret); goto cleanupAndFail; } /* Feed data to the decompression process and get decompressed data. */ stream.next_out = outbuf; stream.avail_out = outbuflen; stream.next_in = *buf; stream.avail_in = nbytes; do { ret = BZ2_bzDecompress(&stream); if (ret < 0) { fprintf(stderr, "BUG: bzip2 decompression failed with error %d\n", ret); goto cleanupAndFail; } if (ret != BZ_STREAM_END && stream.avail_out == 0) { /* Grow the output buffer. */ newbuflen = outbuflen * 2; newbuf = realloc(outbuf, newbuflen); if (newbuf == NULL) { fprintf(stderr, "memory allocation failed for bzip2 decompression\n"); goto cleanupAndFail; } stream.next_out = newbuf + outbuflen; /* half the new buffer behind */ stream.avail_out = outbuflen; /* half the new buffer ahead */ outbuf = newbuf; outbuflen = newbuflen; } } while (ret != BZ_STREAM_END); /* End compression. */ outdatalen = stream.total_out_lo32; ret = BZ2_bzDecompressEnd(&stream); if (ret != BZ_OK) { fprintf(stderr, "bzip2 compression end failed with error %d\n", ret); goto cleanupAndFail; } } else { /** Compress data. ** ** This is quite simple, since the size of compressed data in the worst ** case is known and it is not much bigger than the size of uncompressed ** data. This allows us to use the simplified one-shot interface to ** compression. **/ unsigned int odatalen; /* maybe not the same size as outdatalen */ int blockSize100k = 9; /* Get compression block size if present. */ if (cd_nelmts > 0) { blockSize100k = cd_values[0]; if (blockSize100k < 1 || blockSize100k > 9) { fprintf(stderr, "invalid compression block size: %d\n", blockSize100k); goto cleanupAndFail; } } /* Prepare the output buffer. */ outbuflen = nbytes + nbytes / 100 + 600; /* worst case (bzip2 docs) */ outbuf = malloc(outbuflen); if (outbuf == NULL) { fprintf(stderr, "memory allocation failed for bzip2 compression\n"); goto cleanupAndFail; } /* Compress data. */ odatalen = outbuflen; ret = BZ2_bzBuffToBuffCompress(outbuf, &odatalen, *buf, nbytes, blockSize100k, 0, 0); outdatalen = odatalen; if (ret != BZ_OK) { fprintf(stderr, "bzip2 compression failed with error %d\n", ret); goto cleanupAndFail; } } /* Always replace the input buffer with the output buffer. */ free(*buf); *buf = outbuf; *buf_size = outbuflen; return outdatalen; cleanupAndFail: if (outbuf) free(outbuf); return 0; #else return 0; #endif /* defined HAVE_BZ2_LIB */ } PyTables-v.3.1.1/src/H5Zbzip2.h000066400000000000000000000002421231437614300160220ustar00rootroot00000000000000#ifndef __H5ZBZIP2_H__ #define __H5ZBZIP2_H__ 1 #define FILTER_BZIP2 307 int register_bzip2(char **version, char **date); #endif /* ! defined __H5ZBZIP2_H__ */ PyTables-v.3.1.1/src/H5Zlzo.c000066400000000000000000000221131231437614300155740ustar00rootroot00000000000000#include #include #include #include "H5Zlzo.h" #include "tables.h" #ifdef HAVE_LZO_LIB # include "lzo1x.h" #endif #ifdef HAVE_LZO2_LIB # include "lzo/lzo1x.h" # define HAVE_LZO_LIB /* The API for LZO and LZO2 is mostly identical */ #endif /* #undef DEBUG */ /* Activate the checksum. It is safer and takes only a 1% more of space and a 2% more of CPU (but sometimes is faster than without checksum, which is almost negligible. F. Alted 2003/07/22 Added code for pytables 0.5 backward compatibility. F. Alted 2003/07/28 Added code for saving the uncompressed length buffer as well. F. Alted 2003/07/29 */ /* From pytables 0.8 on I decided to let the user select the fletcher32 checksum provided in HDF5 1.6 or higher. So, even though the CHECKSUM support here seems pretty stable it will be disabled. F. Alted 2004/01/02 */ #undef CHECKSUM size_t lzo_deflate (unsigned flags, size_t cd_nelmts, const unsigned cd_values[], size_t nbytes, size_t *buf_size, void **buf); int register_lzo(char **version, char **date) { #ifdef HAVE_LZO_LIB H5Z_class_t filter_class = { H5Z_CLASS_T_VERS, /* H5Z_class_t version */ (H5Z_filter_t)(FILTER_LZO), /* filter_id */ 1, 1, /* Encoding and decoding enabled */ "lzo", /* comment */ NULL, /* can_apply_func */ NULL, /* set_local_func */ (H5Z_func_t)(lzo_deflate) /* filter_func */ }; /* Init the LZO library */ if (lzo_init()!=LZO_E_OK) { fprintf(stderr, "Problems initializing LZO library\n"); *version = NULL; *date = NULL; return 0; /* lib is not available */ } /* Register the lzo compressor */ H5Zregister(&filter_class); *version = strdup(LZO_VERSION_STRING); *date = strdup(LZO_VERSION_DATE); return 1; /* lib is available */ #else *version = NULL; *date = NULL; return 0; /* lib is not available */ #endif /* HAVE_LZO_LIB */ } size_t lzo_deflate (unsigned flags, size_t cd_nelmts, const unsigned cd_values[], size_t nbytes, size_t *buf_size, void **buf) { size_t ret_value = 0; #ifdef HAVE_LZO_LIB void *outbuf = NULL, *wrkmem = NULL; int status; size_t nalloc = *buf_size; lzo_uint out_len = (lzo_uint) nalloc; /* max_len_buffer will keep the likely output buffer size after processing the first chunk */ static unsigned int max_len_buffer = 0; /* int complevel = 1; */ #if (defined CHECKSUM || defined DEBUG) int object_version = 10; /* Default version 1.0 */ int object_type = Table; /* Default object type */ #endif #ifdef CHECKSUM lzo_uint32 checksum; #endif /* Check arguments */ /* For Table versions < 20, there were no parameters */ if (cd_nelmts==1 ) { /* complevel = cd_values[0]; */ /* This do nothing right now */ } else if (cd_nelmts==2 ) { /* complevel = cd_values[0]; */ /* This do nothing right now */ #if (defined CHECKSUM || defined DEBUG) object_version = cd_values[1]; /* The table VERSION attribute */ #endif } else if (cd_nelmts==3 ) { /* complevel = cd_values[0]; */ /* This do nothing right now */ #if (defined CHECKSUM || defined DEBUG) object_version = cd_values[1]; /* The table VERSION attribute */ object_type = cd_values[2]; /* A tag for identifying the object (see tables.h) */ #endif } #ifdef DEBUG printf("Object type: %d. ", object_type); printf("object_version:%d\n", object_version); #endif if (flags & H5Z_FLAG_REVERSE) { /* Input */ /* printf("Decompressing chunk with LZO\n"); */ #ifdef CHECKSUM if ((object_type == Table && object_version >= 20) || object_type != Table) { nbytes -= 4; /* Point to uncompressed buffer length */ memcpy(&nalloc, ((unsigned char *)(*buf)+nbytes), 4); out_len = nalloc; nbytes -= 4; /* Point to the checksum */ #ifdef DEBUG printf("Compressed bytes: %d. Uncompressed bytes: %d\n", nbytes, nalloc); #endif } #endif /* Only allocate the bytes for the outbuf */ if (max_len_buffer == 0) { if (NULL==(outbuf = (void *)malloc(nalloc))) fprintf(stderr, "Memory allocation failed for lzo uncompression.\n"); } else { if (NULL==(outbuf = (void *)malloc(max_len_buffer))) fprintf(stderr, "Memory allocation failed for lzo uncompression.\n"); out_len = max_len_buffer; nalloc = max_len_buffer; } while(1) { #ifdef DEBUG printf("nbytes -->%d\n", nbytes); printf("nalloc -->%d\n", nalloc); printf("max_len_buffer -->%d\n", max_len_buffer); #endif /* DEBUG */ /* The assembler version is a 10% slower than the C version with gcc 3.2.2 and gcc 3.3.3 */ /* status = lzo1x_decompress_asm_safe(*buf, (lzo_uint)nbytes, outbuf, */ /* &out_len, NULL); */ /* The safe and unsafe versions have the same speed more or less */ status = lzo1x_decompress_safe(*buf, (lzo_uint)nbytes, outbuf, &out_len, NULL); if (status == LZO_E_OK) { #ifdef DEBUG printf("decompressed %lu bytes back into %lu bytes\n", (long) nbytes, (long) out_len); #endif max_len_buffer = out_len; break; /* done */ } else if (status == LZO_E_OUTPUT_OVERRUN) { nalloc *= 2; out_len = (lzo_uint) nalloc; if (NULL==(outbuf = realloc(outbuf, nalloc))) { fprintf(stderr, "Memory allocation failed for lzo uncompression\n"); } } else { /* this should NEVER happen */ fprintf(stderr, "internal error - decompression failed: %d\n", status); ret_value = 0; /* fail */ goto done; } } #ifdef CHECKSUM if ((object_type == Table && object_version >= 20) || object_type != Table) { #ifdef DEBUG printf("Checksum uncompressing..."); #endif /* Compute the checksum */ checksum=lzo_adler32(lzo_adler32(0,NULL,0), outbuf, out_len); /* Compare */ if (memcmp(&checksum, (unsigned char*)(*buf)+nbytes, 4)) { ret_value = 0; /*fail*/ fprintf(stderr,"Checksum failed!.\n"); goto done; } } #endif /* CHECKSUM */ free(*buf); *buf = outbuf; outbuf = NULL; *buf_size = nalloc; ret_value = out_len; } else { /* * Output; compress but fail if the result would be larger than the * input. The library doesn't provide in-place compression, so we * must allocate a separate buffer for the result. */ lzo_byte *z_src = (lzo_byte*)(*buf); lzo_byte *z_dst; /*destination buffer */ lzo_uint z_src_nbytes = (lzo_uint)(nbytes); /* The next was the original computation for worst-case expansion */ /* I don't know why the difference with LZO1*. Perhaps some wrong docs in LZO package? */ /* lzo_uint z_dst_nbytes = (lzo_uint)(nbytes + (nbytes / 64) + 16 + 3); */ /* The next is for LZO1* algorithms */ /* lzo_uint z_dst_nbytes = (lzo_uint)(nbytes + (nbytes / 16) + 64 + 3); */ /* The next is for LZO2* algorithms. This will be the default */ lzo_uint z_dst_nbytes = (lzo_uint)(nbytes + (nbytes / 8) + 128 + 3); #ifdef CHECKSUM if ((object_type == Table && object_version >= 20) || object_type != Table) { z_dst_nbytes += 4+4; /* Checksum + buffer size */ } #endif if (NULL==(z_dst=outbuf=(void *)malloc(z_dst_nbytes))) { fprintf(stderr, "Unable to allocate lzo destination buffer.\n"); ret_value = 0; /* fail */ goto done; } /* Compress this buffer */ wrkmem = malloc(LZO1X_1_MEM_COMPRESS); if (wrkmem == NULL) { fprintf(stderr, "Memory allocation failed for lzo compression\n"); ret_value = 0; goto done; } status = lzo1x_1_compress (z_src, z_src_nbytes, z_dst, &z_dst_nbytes, wrkmem); free(wrkmem); wrkmem = NULL; #ifdef CHECKSUM if ((object_type == Table && object_version >= 20) || object_type != Table) { #ifdef DEBUG printf("Checksum compressing ..."); printf("src_nbytes: %d, dst_nbytes: %d\n", z_src_nbytes, z_dst_nbytes); #endif /* Append checksum of *uncompressed* data at the end */ checksum = lzo_adler32(lzo_adler32(0,NULL,0), *buf, nbytes); memcpy((unsigned char*)(z_dst)+z_dst_nbytes, &checksum, 4); memcpy((unsigned char*)(z_dst)+z_dst_nbytes+4, &nbytes, 4); z_dst_nbytes += (lzo_uint)4+4; nbytes += 4+4; } #endif if (z_dst_nbytes >= nbytes) { #ifdef DEBUG printf("The compressed buffer takes more space than uncompressed!.\n"); #endif ret_value = 0; /* fail */ goto done; } else if (LZO_E_OK != status) { fprintf(stderr,"lzo library error in compression\n"); ret_value = 0; /* fail */ goto done; } else { free(*buf); *buf = outbuf; outbuf = NULL; *buf_size = z_dst_nbytes; ret_value = z_dst_nbytes; } } done: if(outbuf) free(outbuf); #endif /* HAVE_LZO_LIB */ return ret_value; } PyTables-v.3.1.1/src/H5Zlzo.h000066400000000000000000000002301231437614300155750ustar00rootroot00000000000000#ifndef __H5ZLZO_H__ #define __H5ZLZO_H__ 1 #define FILTER_LZO 305 int register_lzo(char **version, char **date); #endif /* ! defined __H5ZLZO_H__ */ PyTables-v.3.1.1/src/Makefile000066400000000000000000000004461231437614300157420ustar00rootroot00000000000000VERSION = $(shell cat ../VERSION) # All the generated files GENERATED = version.h .PHONY: all clean distclean all: $(GENERATED) clean: rm -f $(GENERATED) distclean: clean version.h: version.h.in ../VERSION cat "$<" | sed -e 's/@VERSION@/$(VERSION)/g' > "$@" PyTables-v.3.1.1/src/idx-opt.c000066400000000000000000001201761231437614300160350ustar00rootroot00000000000000#include "stdio.h" #include "idx-opt.h" /*------------------------------------------------------------------------- * * Binary search functions * *------------------------------------------------------------------------- */ #define NAN_AWARE_LT(a, b) (a < b || (b != b && a == a)) /*------------------------------------------------------------------------- * Function: bisect_{left,right}_optim_* * * Purpose: Look-up for a value in sorted arrays * * Return: The index of the value in array * * Programmer: Francesc Alted * * Date: August, 2005 * * Comments: * * Modifications: * *------------------------------------------------------------------------- */ /* Optimised version for left/int8 */ int bisect_left_b(npy_int8 *a, long x, int hi, int offset) { int lo = 0; int mid; if (x <= a[offset]) return 0; if (a[hi-1+offset] < x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (a[mid+offset] < x) lo = mid+1; else hi = mid; } return lo; } /* Optimised version for left/uint8 */ int bisect_left_ub(npy_uint8 *a, long x, int hi, int offset) { int lo = 0; int mid; if (x <= a[offset]) return 0; if (a[hi-1+offset] < x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (a[mid+offset] < x) lo = mid+1; else hi = mid; } return lo; } /* Optimised version for right/int8 */ int bisect_right_b(npy_int8 *a, long x, int hi, int offset) { int lo = 0; int mid; if (x < a[offset]) return 0; if (a[hi-1+offset] <= x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (x < a[mid+offset]) hi = mid; else lo = mid+1; } return lo; } /* Optimised version for right/uint8 */ int bisect_right_ub(npy_uint8 *a, long x, int hi, int offset) { int lo = 0; int mid; if (x < a[offset]) return 0; if (a[hi-1+offset] <= x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (x < a[mid+offset]) hi = mid; else lo = mid+1; } return lo; } /* Optimised version for left/int16 */ int bisect_left_s(npy_int16 *a, long x, int hi, int offset) { int lo = 0; int mid; if (x <= a[offset]) return 0; if (a[hi-1+offset] < x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (a[mid+offset] < x) lo = mid+1; else hi = mid; } return lo; } /* Optimised version for left/uint16 */ int bisect_left_us(npy_uint16 *a, long x, int hi, int offset) { int lo = 0; int mid; if (x <= a[offset]) return 0; if (a[hi-1+offset] < x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (a[mid+offset] < x) lo = mid+1; else hi = mid; } return lo; } /* Optimised version for right/int16 */ int bisect_right_s(npy_int16 *a, long x, int hi, int offset) { int lo = 0; int mid; if (x < a[offset]) return 0; if (a[hi-1+offset] <= x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (x < a[mid+offset]) hi = mid; else lo = mid+1; } return lo; } /* Optimised version for right/uint16 */ int bisect_right_us(npy_uint16 *a, long x, int hi, int offset) { int lo = 0; int mid; if (x < a[offset]) return 0; if (a[hi-1+offset] <= x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (x < a[mid+offset]) hi = mid; else lo = mid+1; } return lo; } /* Optimised version for left/int32 */ int bisect_left_i(npy_int32 *a, long x, int hi, int offset) { int lo = 0; int mid; if (x <= a[offset]) return 0; if (a[hi-1+offset] < x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (a[mid+offset] < x) lo = mid+1; else hi = mid; } return lo; } /* Optimised version for left/uint32 */ int bisect_left_ui(npy_uint32 *a, npy_uint32 x, int hi, int offset) { int lo = 0; int mid; if (x <= a[offset]) return 0; if (a[hi-1+offset] < x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (a[mid+offset] < x) lo = mid+1; else hi = mid; } return lo; } /* Optimised version for right/int32 */ int bisect_right_i(npy_int32 *a, long x, int hi, int offset) { int lo = 0; int mid; if (x < a[offset]) return 0; if (a[hi-1+offset] <= x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (x < a[mid+offset]) hi = mid; else lo = mid+1; } return lo; } /* Optimised version for right/uint32 */ int bisect_right_ui(npy_uint32 *a, npy_uint32 x, int hi, int offset) { int lo = 0; int mid; if (x < a[offset]) return 0; if (a[hi-1+offset] <= x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (x < a[mid+offset]) hi = mid; else lo = mid+1; } return lo; } /* Optimised version for left/int64 */ int bisect_left_ll(npy_int64 *a, npy_int64 x, int hi, int offset) { int lo = 0; int mid; if (x <= a[offset]) return 0; if (a[hi-1+offset] < x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (a[mid+offset] < x) lo = mid+1; else hi = mid; } return lo; } /* Optimised version for left/uint64 */ int bisect_left_ull(npy_uint64 *a, npy_uint64 x, int hi, int offset) { int lo = 0; int mid; if (x <= a[offset]) return 0; if (a[hi-1+offset] < x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (a[mid+offset] < x) lo = mid+1; else hi = mid; } return lo; } /* Optimised version for right/int64 */ int bisect_right_ll(npy_int64 *a, npy_int64 x, int hi, int offset) { int lo = 0; int mid; if (x < a[offset]) return 0; if (a[hi-1+offset] <= x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (x < a[mid+offset]) hi = mid; else lo = mid+1; } return lo; } /* Optimised version for right/uint64 */ int bisect_right_ull(npy_uint64 *a, npy_uint64 x, int hi, int offset) { int lo = 0; int mid; if (x < a[offset]) return 0; if (a[hi-1+offset] <= x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (x < a[mid+offset]) hi = mid; else lo = mid+1; } return lo; } /* Optimised version for left/float16 */ int bisect_left_e(npy_float16 *a, npy_float64 x, int hi, int offset) { int lo = 0; int mid; if (x <= a[offset]) return 0; if (a[hi-1+offset] < x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (a[mid+offset] < x) lo = mid+1; else hi = mid; } return lo; } /* Optimised version for right/float16 */ int bisect_right_e(npy_float16 *a, npy_float64 x, int hi, int offset) { int lo = 0; int mid; if (x < a[offset]) return 0; if (a[hi-1+offset] <= x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (x < a[mid+offset]) hi = mid; else lo = mid+1; } return lo; } /* Optimised version for left/float32 */ int bisect_left_f(npy_float32 *a, npy_float64 x, int hi, int offset) { int lo = 0; int mid; if (x <= a[offset]) return 0; if (a[hi-1+offset] < x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (a[mid+offset] < x) lo = mid+1; else hi = mid; } return lo; } /* Optimised version for right/float32 */ int bisect_right_f(npy_float32 *a, npy_float64 x, int hi, int offset) { int lo = 0; int mid; if (x < a[offset]) return 0; if (a[hi-1+offset] <= x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (x < a[mid+offset]) hi = mid; else lo = mid+1; } return lo; } /* Optimised version for left/float64 */ int bisect_left_d(npy_float64 *a, npy_float64 x, int hi, int offset) { int lo = 0; int mid; if (x <= a[offset]) return 0; if (a[hi-1+offset] < x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (a[mid+offset] < x) lo = mid+1; else hi = mid; } return lo; } /* Optimised version for right/float64 */ int bisect_right_d(npy_float64 *a, npy_float64 x, int hi, int offset) { int lo = 0; int mid; if (x < a[offset]) return 0; if (a[hi-1+offset] <= x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (x < a[mid+offset]) hi = mid; else lo = mid+1; } return lo; } /* Optimised version for left/longdouble */ int bisect_left_g(npy_longdouble *a, npy_longdouble x, int hi, int offset) { int lo = 0; int mid; if (x <= a[offset]) return 0; if (a[hi-1+offset] < x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (a[mid+offset] < x) lo = mid+1; else hi = mid; } return lo; } /* Optimised version for right/longdouble */ int bisect_right_g(npy_longdouble *a, npy_longdouble x, int hi, int offset) { int lo = 0; int mid; if (x < a[offset]) return 0; if (a[hi-1+offset] <= x) return hi; while (lo < hi) { mid = lo + (hi-lo)/2; if (x < a[mid+offset]) hi = mid; else lo = mid+1; } return lo; } /* Now, it follows a series of functions for doing in-place sorting. The array that starts at start1 is sorted in-place. array2 is also sorted in-place, but following the array1 order. */ #define PYA_QS_STACK 100 #define SMALL_QUICKSORT 15 #define SMALL_STRING 16 #define SWAP(a,b) {SWAP_temp = (b); (b) = (a); (a) = SWAP_temp;} /* This is for swaping strings */ static void sSWAP(char *s1, char *s2, size_t len) { while(len--) { const char t = *s1; *s1++ = *s2; *s2++ = t; } } /* The iSWAP macro is safe because it will always work on aligned ints */ #define iSWAP(a,b) { \ switch(ts) { \ case 8: \ *(npy_int64 *)iSWAP_temp = *(npy_int64 *)(b); \ *(npy_int64 *)(b) = *(npy_int64 *)(a); \ *(npy_int64 *)(a) = *(npy_int64 *)iSWAP_temp; \ break; \ case 4: \ *(npy_int32 *)iSWAP_temp = *(npy_int32 *)(b); \ *(npy_int32 *)(b) = *(npy_int32 *)(a); \ *(npy_int32 *)(a) = *(npy_int32 *)iSWAP_temp; \ break; \ case 1: \ *(npy_int8 *)iSWAP_temp = *(npy_int8 *)(b); \ *(npy_int8 *)(b) = *(npy_int8 *)(a); \ *(npy_int8 *)(a) = *(npy_int8 *)iSWAP_temp; \ break; \ case 2: \ *(npy_int16 *)iSWAP_temp = *(npy_int16 *)(b); \ *(npy_int16 *)(b) = *(npy_int16 *)(a); \ *(npy_int16 *)(a) = *(npy_int16 *)iSWAP_temp; \ break; \ default: \ for (i=0; i SMALL_QUICKSORT) { /* quicksort partition */ pm = pl + ((pr - pl) >> 1); ipm = ipl + (((ipr - ipl)/ts) >> 1)*ts; if (NAN_AWARE_LT(*pm, *pl)) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } if (NAN_AWARE_LT(*pr, *pm)) { SWAP(*pr, *pm); iSWAP(ipr, ipm); } if (NAN_AWARE_LT(*pm, *pl)) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } vp = *pm; pi = pl; ipi = ipl; pj = pr - 1; ipj = ipr - ts; SWAP(*pm, *pj); iSWAP(ipm, ipj); for(;;) { do { ++pi; ipi += ts; } while (NAN_AWARE_LT(*pi, vp)); do { --pj; ipj -= ts; } while (NAN_AWARE_LT(vp, *pj)); if (pi >= pj) break; SWAP(*pi, *pj); iSWAP(ipi, ipj); } SWAP(*pi, *(pr-1)); iSWAP(ipi, ipr-ts); /* push largest partition on stack */ if (pi - pl < pr - pi) { *sptr++ = pi + 1; *isptr++ = ipi + ts; *sptr++ = pr; *isptr++ = ipr; pr = pi - 1; ipr = ipi - ts; }else{ *sptr++ = pl; *isptr++ = ipl; *sptr++ = pi - 1; *isptr++ = ipi - ts; pl = pi + 1; ipl = ipi + ts; } } /* insertion sort */ for(pi = pl + 1, ipi = ipl + ts; pi <= pr; ++pi, ipi += ts) { vp = *pi; opt_memcpy(ivp, ipi, ts); for(pj = pi, pt = pi - 1, ipj = ipi, ipt = ipi - ts; \ pj > pl && NAN_AWARE_LT(vp, *pt);) { *pj-- = *pt--; opt_memcpy(ipj, ipt, ts); ipj -= ts; ipt -= ts; } *pj = vp; opt_memcpy(ipj, ivp, ts); } if (sptr == stack) break; pr = *(--sptr); ipr = *(--isptr); pl = *(--sptr); ipl = *(--isptr); } free(ivp); free(iSWAP_temp); return 0; } int keysort_f128(npy_float128 *start1, char *start2, npy_intp num, int ts) { npy_float128 *pl = start1; char *ipl = start2; npy_float128 *pr = start1 + num - 1; char *ipr = start2 + (num - 1) * ts; npy_float128 vp; char *ivp; npy_float128 SWAP_temp; char *iSWAP_temp; npy_float128 *stack[PYA_QS_STACK], **sptr = stack; char *istack[PYA_QS_STACK], **isptr = istack; npy_float128 *pm, *pi, *pj, *pt; char *ipm, *ipi, *ipj, *ipt; npy_intp i; ivp = malloc(ts); iSWAP_temp = malloc(ts); for(;;) { while ((pr - pl) > SMALL_QUICKSORT) { /* quicksort partition */ pm = pl + ((pr - pl) >> 1); ipm = ipl + (((ipr - ipl)/ts) >> 1)*ts; if (NAN_AWARE_LT(*pm, *pl)) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } if (NAN_AWARE_LT(*pr, *pm)) { SWAP(*pr, *pm); iSWAP(ipr, ipm); } if (NAN_AWARE_LT(*pm, *pl)) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } vp = *pm; pi = pl; ipi = ipl; pj = pr - 1; ipj = ipr - ts; SWAP(*pm, *pj); iSWAP(ipm, ipj); for(;;) { do { ++pi; ipi += ts; } while (NAN_AWARE_LT(*pi, vp)); do { --pj; ipj -= ts; } while (NAN_AWARE_LT(vp, *pj)); if (pi >= pj) break; SWAP(*pi, *pj); iSWAP(ipi, ipj); } SWAP(*pi, *(pr-1)); iSWAP(ipi, ipr-ts); /* push largest partition on stack */ if (pi - pl < pr - pi) { *sptr++ = pi + 1; *isptr++ = ipi + ts; *sptr++ = pr; *isptr++ = ipr; pr = pi - 1; ipr = ipi - ts; }else{ *sptr++ = pl; *isptr++ = ipl; *sptr++ = pi - 1; *isptr++ = ipi - ts; pl = pi + 1; ipl = ipi + ts; } } /* insertion sort */ for(pi = pl + 1, ipi = ipl + ts; pi <= pr; ++pi, ipi += ts) { vp = *pi; opt_memcpy(ivp, ipi, ts); for(pj = pi, pt = pi - 1, ipj = ipi, ipt = ipi - ts; \ pj > pl && NAN_AWARE_LT(vp, *pt);) { *pj-- = *pt--; opt_memcpy(ipj, ipt, ts); ipj -= ts; ipt -= ts; } *pj = vp; opt_memcpy(ipj, ivp, ts); } if (sptr == stack) break; pr = *(--sptr); ipr = *(--isptr); pl = *(--sptr); ipl = *(--isptr); } free(ivp); free(iSWAP_temp); return 0; } int keysort_f64(npy_float64 *start1, char *start2, npy_intp num, int ts) { npy_float64 *pl = start1; char *ipl = start2; npy_float64 *pr = start1 + num - 1; char *ipr = start2 + (num - 1) * ts; npy_float64 vp; char *ivp; npy_float64 SWAP_temp; char *iSWAP_temp; npy_float64 *stack[PYA_QS_STACK], **sptr = stack; char *istack[PYA_QS_STACK], **isptr = istack; npy_float64 *pm, *pi, *pj, *pt; char *ipm, *ipi, *ipj, *ipt; npy_intp i; ivp = malloc(ts); iSWAP_temp = malloc(ts); for(;;) { while ((pr - pl) > SMALL_QUICKSORT) { /* quicksort partition */ pm = pl + ((pr - pl) >> 1); ipm = ipl + (((ipr - ipl)/ts) >> 1)*ts; if (NAN_AWARE_LT(*pm, *pl)) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } if (NAN_AWARE_LT(*pr, *pm)) { SWAP(*pr, *pm); iSWAP(ipr, ipm); } if (NAN_AWARE_LT(*pm, *pl)) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } vp = *pm; pi = pl; ipi = ipl; pj = pr - 1; ipj = ipr - ts; SWAP(*pm, *pj); iSWAP(ipm, ipj); for(;;) { do { ++pi; ipi += ts; } while (NAN_AWARE_LT(*pi, vp)); do { --pj; ipj -= ts; } while (NAN_AWARE_LT(vp, *pj)); if (pi >= pj) break; SWAP(*pi, *pj); iSWAP(ipi, ipj); } SWAP(*pi, *(pr-1)); iSWAP(ipi, ipr-ts); /* push largest partition on stack */ if (pi - pl < pr - pi) { *sptr++ = pi + 1; *isptr++ = ipi + ts; *sptr++ = pr; *isptr++ = ipr; pr = pi - 1; ipr = ipi - ts; }else{ *sptr++ = pl; *isptr++ = ipl; *sptr++ = pi - 1; *isptr++ = ipi - ts; pl = pi + 1; ipl = ipi + ts; } } /* insertion sort */ for(pi = pl + 1, ipi = ipl + ts; pi <= pr; ++pi, ipi += ts) { vp = *pi; opt_memcpy(ivp, ipi, ts); for(pj = pi, pt = pi - 1, ipj = ipi, ipt = ipi - ts; \ pj > pl && NAN_AWARE_LT(vp, *pt);) { *pj-- = *pt--; opt_memcpy(ipj, ipt, ts); ipj -= ts; ipt -= ts; } *pj = vp; opt_memcpy(ipj, ivp, ts); } if (sptr == stack) break; pr = *(--sptr); ipr = *(--isptr); pl = *(--sptr); ipl = *(--isptr); } free(ivp); free(iSWAP_temp); return 0; } int keysort_f32(npy_float32 *start1, char *start2, npy_intp num, int ts) { npy_float32 *pl = start1; char *ipl = start2; npy_float32 *pr = start1 + num - 1; char *ipr = start2 + (num - 1) * ts; npy_float32 vp; char *ivp; npy_float32 SWAP_temp; char *iSWAP_temp; npy_float32 *stack[PYA_QS_STACK], **sptr = stack; char *istack[PYA_QS_STACK], **isptr = istack; npy_float32 *pm, *pi, *pj, *pt; char *ipm, *ipi, *ipj, *ipt; npy_intp i; ivp = malloc(ts); iSWAP_temp = malloc(ts); for(;;) { while ((pr - pl) > SMALL_QUICKSORT) { /* quicksort partition */ pm = pl + ((pr - pl) >> 1); ipm = ipl + (((ipr - ipl)/ts) >> 1)*ts; if (NAN_AWARE_LT(*pm, *pl)) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } if (NAN_AWARE_LT(*pr, *pm)) { SWAP(*pr, *pm); iSWAP(ipr, ipm); } if (NAN_AWARE_LT(*pm, *pl)) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } vp = *pm; pi = pl; ipi = ipl; pj = pr - 1; ipj = ipr - ts; SWAP(*pm, *pj); iSWAP(ipm, ipj); for(;;) { do { ++pi; ipi += ts; } while (NAN_AWARE_LT(*pi, vp)); do { --pj; ipj -= ts; } while (NAN_AWARE_LT(vp, *pj)); if (pi >= pj) break; SWAP(*pi, *pj); iSWAP(ipi, ipj); } SWAP(*pi, *(pr-1)); iSWAP(ipi, ipr-ts); /* push largest partition on stack */ if (pi - pl < pr - pi) { *sptr++ = pi + 1; *isptr++ = ipi + ts; *sptr++ = pr; *isptr++ = ipr; pr = pi - 1; ipr = ipi - ts; }else{ *sptr++ = pl; *isptr++ = ipl; *sptr++ = pi - 1; *isptr++ = ipi - ts; pl = pi + 1; ipl = ipi + ts; } } /* insertion sort */ for(pi = pl + 1, ipi = ipl + ts; pi <= pr; ++pi, ipi += ts) { vp = *pi; opt_memcpy(ivp, ipi, ts); for(pj = pi, pt = pi - 1, ipj = ipi, ipt = ipi - ts; \ pj > pl && NAN_AWARE_LT(vp, *pt);) { *pj-- = *pt--; opt_memcpy(ipj, ipt, ts); ipj -= ts; ipt -= ts; } *pj = vp; opt_memcpy(ipj, ivp, ts); } if (sptr == stack) break; pr = *(--sptr); ipr = *(--isptr); pl = *(--sptr); ipl = *(--isptr); } free(ivp); free(iSWAP_temp); return 0; } int keysort_f16(npy_float16 *start1, char *start2, npy_intp num, int ts) { npy_float16 *pl = start1; char *ipl = start2; npy_float16 *pr = start1 + num - 1; char *ipr = start2 + (num - 1) * ts; npy_float16 vp; char *ivp; npy_float16 SWAP_temp; char *iSWAP_temp; npy_float16 *stack[PYA_QS_STACK], **sptr = stack; char *istack[PYA_QS_STACK], **isptr = istack; npy_float16 *pm, *pi, *pj, *pt; char *ipm, *ipi, *ipj, *ipt; npy_intp i; ivp = malloc(ts); iSWAP_temp = malloc(ts); for(;;) { while ((pr - pl) > SMALL_QUICKSORT) { /* quicksort partition */ pm = pl + ((pr - pl) >> 1); ipm = ipl + (((ipr - ipl)/ts) >> 1)*ts; if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } if (*pr < *pm) { SWAP(*pr, *pm); iSWAP(ipr, ipm); } if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } vp = *pm; pi = pl; ipi = ipl; pj = pr - 1; ipj = ipr - ts; SWAP(*pm, *pj); iSWAP(ipm, ipj); for(;;) { do { ++pi; ipi += ts; } while (*pi < vp); do { --pj; ipj -= ts; } while (vp < *pj); if (pi >= pj) break; SWAP(*pi, *pj); iSWAP(ipi, ipj); } SWAP(*pi, *(pr-1)); iSWAP(ipi, ipr-ts); /* push largest partition on stack */ if (pi - pl < pr - pi) { *sptr++ = pi + 1; *isptr++ = ipi + ts; *sptr++ = pr; *isptr++ = ipr; pr = pi - 1; ipr = ipi - ts; }else{ *sptr++ = pl; *isptr++ = ipl; *sptr++ = pi - 1; *isptr++ = ipi - ts; pl = pi + 1; ipl = ipi + ts; } } /* insertion sort */ for(pi = pl + 1, ipi = ipl + ts; pi <= pr; ++pi, ipi += ts) { vp = *pi; opt_memcpy(ivp, ipi, ts); for(pj = pi, pt = pi - 1, ipj = ipi, ipt = ipi - ts; \ pj > pl && vp < *pt;) { *pj-- = *pt--; opt_memcpy(ipj, ipt, ts); ipj -= ts; ipt -= ts; } *pj = vp; opt_memcpy(ipj, ivp, ts); } if (sptr == stack) break; pr = *(--sptr); ipr = *(--isptr); pl = *(--sptr); ipl = *(--isptr); } free(ivp); free(iSWAP_temp); return 0; } int keysort_i64(npy_int64 *start1, char *start2, npy_intp num, int ts) { npy_int64 *pl = start1; char *ipl = start2; npy_int64 *pr = start1 + num - 1; char *ipr = start2 + (num - 1) * ts; npy_int64 vp; char *ivp; npy_int64 SWAP_temp; char *iSWAP_temp; npy_int64 *stack[PYA_QS_STACK], **sptr = stack; char *istack[PYA_QS_STACK], **isptr = istack; npy_int64 *pm, *pi, *pj, *pt; char *ipm, *ipi, *ipj, *ipt; npy_intp i; ivp = malloc(ts); iSWAP_temp = malloc(ts); for(;;) { while ((pr - pl) > SMALL_QUICKSORT) { /* quicksort partition */ pm = pl + ((pr - pl) >> 1); ipm = ipl + (((ipr - ipl)/ts) >> 1)*ts; if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } if (*pr < *pm) { SWAP(*pr, *pm); iSWAP(ipr, ipm); } if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } vp = *pm; pi = pl; ipi = ipl; pj = pr - 1; ipj = ipr - ts; SWAP(*pm, *pj); iSWAP(ipm, ipj); for(;;) { do { ++pi; ipi += ts; } while (*pi < vp); do { --pj; ipj -= ts; } while (vp < *pj); if (pi >= pj) break; SWAP(*pi, *pj); iSWAP(ipi, ipj); } SWAP(*pi, *(pr-1)); iSWAP(ipi, ipr-ts); /* push largest partition on stack */ if (pi - pl < pr - pi) { *sptr++ = pi + 1; *isptr++ = ipi + ts; *sptr++ = pr; *isptr++ = ipr; pr = pi - 1; ipr = ipi - ts; }else{ *sptr++ = pl; *isptr++ = ipl; *sptr++ = pi - 1; *isptr++ = ipi - ts; pl = pi + 1; ipl = ipi + ts; } } /* insertion sort */ for(pi = pl + 1, ipi = ipl + ts; pi <= pr; ++pi, ipi += ts) { vp = *pi; opt_memcpy(ivp, ipi, ts); for(pj = pi, pt = pi - 1, ipj = ipi, ipt = ipi - ts; \ pj > pl && vp < *pt;) { *pj-- = *pt--; opt_memcpy(ipj, ipt, ts); ipj -= ts; ipt -= ts; } *pj = vp; opt_memcpy(ipj, ivp, ts); } if (sptr == stack) break; pr = *(--sptr); ipr = *(--isptr); pl = *(--sptr); ipl = *(--isptr); } free(ivp); free(iSWAP_temp); return 0; } int keysort_u64(npy_uint64 *start1, char *start2, npy_intp num, int ts) { npy_uint64 *pl = start1; char *ipl = start2; npy_uint64 *pr = start1 + num - 1; char *ipr = start2 + (num - 1) * ts; npy_uint64 vp; char *ivp; npy_uint64 SWAP_temp; char *iSWAP_temp; npy_uint64 *stack[PYA_QS_STACK], **sptr = stack; char *istack[PYA_QS_STACK], **isptr = istack; npy_uint64 *pm, *pi, *pj, *pt; char *ipm, *ipi, *ipj, *ipt; npy_intp i; ivp = malloc(ts); iSWAP_temp = malloc(ts); for(;;) { while ((pr - pl) > SMALL_QUICKSORT) { /* quicksort partition */ pm = pl + ((pr - pl) >> 1); ipm = ipl + (((ipr - ipl)/ts) >> 1)*ts; if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } if (*pr < *pm) { SWAP(*pr, *pm); iSWAP(ipr, ipm); } if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } vp = *pm; pi = pl; ipi = ipl; pj = pr - 1; ipj = ipr - ts; SWAP(*pm, *pj); iSWAP(ipm, ipj); for(;;) { do { ++pi; ipi += ts; } while (*pi < vp); do { --pj; ipj -= ts; } while (vp < *pj); if (pi >= pj) break; SWAP(*pi, *pj); iSWAP(ipi, ipj); } SWAP(*pi, *(pr-1)); iSWAP(ipi, ipr-ts); /* push largest partition on stack */ if (pi - pl < pr - pi) { *sptr++ = pi + 1; *isptr++ = ipi + ts; *sptr++ = pr; *isptr++ = ipr; pr = pi - 1; ipr = ipi - ts; }else{ *sptr++ = pl; *isptr++ = ipl; *sptr++ = pi - 1; *isptr++ = ipi - ts; pl = pi + 1; ipl = ipi + ts; } } /* insertion sort */ for(pi = pl + 1, ipi = ipl + ts; pi <= pr; ++pi, ipi += ts) { vp = *pi; opt_memcpy(ivp, ipi, ts); for(pj = pi, pt = pi - 1, ipj = ipi, ipt = ipi - ts; \ pj > pl && vp < *pt;) { *pj-- = *pt--; opt_memcpy(ipj, ipt, ts); ipj -= ts; ipt -= ts; } *pj = vp; opt_memcpy(ipj, ivp, ts); } if (sptr == stack) break; pr = *(--sptr); ipr = *(--isptr); pl = *(--sptr); ipl = *(--isptr); } free(ivp); free(iSWAP_temp); return 0; } int keysort_i32(npy_int32 *start1, char *start2, npy_intp num, int ts) { npy_int32 *pl = start1; char *ipl = start2; npy_int32 *pr = start1 + num - 1; char *ipr = start2 + (num - 1) * ts; npy_int32 vp; char *ivp; npy_int32 SWAP_temp; char *iSWAP_temp; npy_int32 *stack[PYA_QS_STACK], **sptr = stack; char *istack[PYA_QS_STACK], **isptr = istack; npy_int32 *pm, *pi, *pj, *pt; char *ipm, *ipi, *ipj, *ipt; npy_intp i; ivp = malloc(ts); iSWAP_temp = malloc(ts); for(;;) { while ((pr - pl) > SMALL_QUICKSORT) { /* quicksort partition */ pm = pl + ((pr - pl) >> 1); ipm = ipl + (((ipr - ipl)/ts) >> 1)*ts; if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } if (*pr < *pm) { SWAP(*pr, *pm); iSWAP(ipr, ipm); } if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } vp = *pm; pi = pl; ipi = ipl; pj = pr - 1; ipj = ipr - ts; SWAP(*pm, *pj); iSWAP(ipm, ipj); for(;;) { do { ++pi; ipi += ts; } while (*pi < vp); do { --pj; ipj -= ts; } while (vp < *pj); if (pi >= pj) break; SWAP(*pi, *pj); iSWAP(ipi, ipj); } SWAP(*pi, *(pr-1)); iSWAP(ipi, ipr-ts); /* push largest partition on stack */ if (pi - pl < pr - pi) { *sptr++ = pi + 1; *isptr++ = ipi + ts; *sptr++ = pr; *isptr++ = ipr; pr = pi - 1; ipr = ipi - ts; }else{ *sptr++ = pl; *isptr++ = ipl; *sptr++ = pi - 1; *isptr++ = ipi - ts; pl = pi + 1; ipl = ipi + ts; } } /* insertion sort */ for(pi = pl + 1, ipi = ipl + ts; pi <= pr; ++pi, ipi += ts) { vp = *pi; opt_memcpy(ivp, ipi, ts); for(pj = pi, pt = pi - 1, ipj = ipi, ipt = ipi - ts; \ pj > pl && vp < *pt;) { *pj-- = *pt--; opt_memcpy(ipj, ipt, ts); ipj -= ts; ipt -= ts; } *pj = vp; opt_memcpy(ipj, ivp, ts); } if (sptr == stack) break; pr = *(--sptr); ipr = *(--isptr); pl = *(--sptr); ipl = *(--isptr); } free(ivp); free(iSWAP_temp); return 0; } int keysort_u32(npy_uint32 *start1, char *start2, npy_intp num, int ts) { npy_uint32 *pl = start1; char *ipl = start2; npy_uint32 *pr = start1 + num - 1; char *ipr = start2 + (num - 1) * ts; npy_uint32 vp; char *ivp; npy_uint32 SWAP_temp; char *iSWAP_temp; npy_uint32 *stack[PYA_QS_STACK], **sptr = stack; char *istack[PYA_QS_STACK], **isptr = istack; npy_uint32 *pm, *pi, *pj, *pt; char *ipm, *ipi, *ipj, *ipt; npy_intp i; ivp = malloc(ts); iSWAP_temp = malloc(ts); for(;;) { while ((pr - pl) > SMALL_QUICKSORT) { /* quicksort partition */ pm = pl + ((pr - pl) >> 1); ipm = ipl + (((ipr - ipl)/ts) >> 1)*ts; if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } if (*pr < *pm) { SWAP(*pr, *pm); iSWAP(ipr, ipm); } if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } vp = *pm; pi = pl; ipi = ipl; pj = pr - 1; ipj = ipr - ts; SWAP(*pm, *pj); iSWAP(ipm, ipj); for(;;) { do { ++pi; ipi += ts; } while (*pi < vp); do { --pj; ipj -= ts; } while (vp < *pj); if (pi >= pj) break; SWAP(*pi, *pj); iSWAP(ipi, ipj); } SWAP(*pi, *(pr-1)); iSWAP(ipi, ipr-ts); /* push largest partition on stack */ if (pi - pl < pr - pi) { *sptr++ = pi + 1; *isptr++ = ipi + ts; *sptr++ = pr; *isptr++ = ipr; pr = pi - 1; ipr = ipi - ts; }else{ *sptr++ = pl; *isptr++ = ipl; *sptr++ = pi - 1; *isptr++ = ipi - ts; pl = pi + 1; ipl = ipi + ts; } } /* insertion sort */ for(pi = pl + 1, ipi = ipl + ts; pi <= pr; ++pi, ipi += ts) { vp = *pi; opt_memcpy(ivp, ipi, ts); for(pj = pi, pt = pi - 1, ipj = ipi, ipt = ipi - ts; \ pj > pl && vp < *pt;) { *pj-- = *pt--; opt_memcpy(ipj, ipt, ts); ipj -= ts; ipt -= ts; } *pj = vp; opt_memcpy(ipj, ivp, ts); } if (sptr == stack) break; pr = *(--sptr); ipr = *(--isptr); pl = *(--sptr); ipl = *(--isptr); } free(ivp); free(iSWAP_temp); return 0; } int keysort_i16(npy_int16 *start1, char *start2, npy_intp num, int ts) { npy_int16 *pl = start1; char *ipl = start2; npy_int16 *pr = start1 + num - 1; char *ipr = start2 + (num - 1) * ts; npy_int16 vp; char *ivp; npy_int16 SWAP_temp; char *iSWAP_temp; npy_int16 *stack[PYA_QS_STACK], **sptr = stack; char *istack[PYA_QS_STACK], **isptr = istack; npy_int16 *pm, *pi, *pj, *pt; char *ipm, *ipi, *ipj, *ipt; npy_intp i; ivp = malloc(ts); iSWAP_temp = malloc(ts); for(;;) { while ((pr - pl) > SMALL_QUICKSORT) { /* quicksort partition */ pm = pl + ((pr - pl) >> 1); ipm = ipl + (((ipr - ipl)/ts) >> 1)*ts; if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } if (*pr < *pm) { SWAP(*pr, *pm); iSWAP(ipr, ipm); } if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } vp = *pm; pi = pl; ipi = ipl; pj = pr - 1; ipj = ipr - ts; SWAP(*pm, *pj); iSWAP(ipm, ipj); for(;;) { do { ++pi; ipi += ts; } while (*pi < vp); do { --pj; ipj -= ts; } while (vp < *pj); if (pi >= pj) break; SWAP(*pi, *pj); iSWAP(ipi, ipj); } SWAP(*pi, *(pr-1)); iSWAP(ipi, ipr-ts); /* push largest partition on stack */ if (pi - pl < pr - pi) { *sptr++ = pi + 1; *isptr++ = ipi + ts; *sptr++ = pr; *isptr++ = ipr; pr = pi - 1; ipr = ipi - ts; }else{ *sptr++ = pl; *isptr++ = ipl; *sptr++ = pi - 1; *isptr++ = ipi - ts; pl = pi + 1; ipl = ipi + ts; } } /* insertion sort */ for(pi = pl + 1, ipi = ipl + ts; pi <= pr; ++pi, ipi += ts) { vp = *pi; opt_memcpy(ivp, ipi, ts); for(pj = pi, pt = pi - 1, ipj = ipi, ipt = ipi - ts; \ pj > pl && vp < *pt;) { *pj-- = *pt--; opt_memcpy(ipj, ipt, ts); ipj -= ts; ipt -= ts; } *pj = vp; opt_memcpy(ipj, ivp, ts); } if (sptr == stack) break; pr = *(--sptr); ipr = *(--isptr); pl = *(--sptr); ipl = *(--isptr); } free(ivp); free(iSWAP_temp); return 0; } int keysort_u16(npy_uint16 *start1, char *start2, npy_intp num, int ts) { npy_uint16 *pl = start1; char *ipl = start2; npy_uint16 *pr = start1 + num - 1; char *ipr = start2 + (num - 1) * ts; npy_uint16 vp; char *ivp; npy_uint16 SWAP_temp; char *iSWAP_temp; npy_uint16 *stack[PYA_QS_STACK], **sptr = stack; char *istack[PYA_QS_STACK], **isptr = istack; npy_uint16 *pm, *pi, *pj, *pt; char *ipm, *ipi, *ipj, *ipt; npy_intp i; ivp = malloc(ts); iSWAP_temp = malloc(ts); for(;;) { while ((pr - pl) > SMALL_QUICKSORT) { /* quicksort partition */ pm = pl + ((pr - pl) >> 1); ipm = ipl + (((ipr - ipl)/ts) >> 1)*ts; if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } if (*pr < *pm) { SWAP(*pr, *pm); iSWAP(ipr, ipm); } if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } vp = *pm; pi = pl; ipi = ipl; pj = pr - 1; ipj = ipr - ts; SWAP(*pm, *pj); iSWAP(ipm, ipj); for(;;) { do { ++pi; ipi += ts; } while (*pi < vp); do { --pj; ipj -= ts; } while (vp < *pj); if (pi >= pj) break; SWAP(*pi, *pj); iSWAP(ipi, ipj); } SWAP(*pi, *(pr-1)); iSWAP(ipi, ipr-ts); /* push largest partition on stack */ if (pi - pl < pr - pi) { *sptr++ = pi + 1; *isptr++ = ipi + ts; *sptr++ = pr; *isptr++ = ipr; pr = pi - 1; ipr = ipi - ts; }else{ *sptr++ = pl; *isptr++ = ipl; *sptr++ = pi - 1; *isptr++ = ipi - ts; pl = pi + 1; ipl = ipi + ts; } } /* insertion sort */ for(pi = pl + 1, ipi = ipl + ts; pi <= pr; ++pi, ipi += ts) { vp = *pi; opt_memcpy(ivp, ipi, ts); for(pj = pi, pt = pi - 1, ipj = ipi, ipt = ipi - ts; \ pj > pl && vp < *pt;) { *pj-- = *pt--; opt_memcpy(ipj, ipt, ts); ipj -= ts; ipt -= ts; } *pj = vp; opt_memcpy(ipj, ivp, ts); } if (sptr == stack) break; pr = *(--sptr); ipr = *(--isptr); pl = *(--sptr); ipl = *(--isptr); } free(ivp); free(iSWAP_temp); return 0; } int keysort_i8(npy_int8 *start1, char *start2, npy_intp num, int ts) { npy_int8 *pl = start1; char *ipl = start2; npy_int8 *pr = start1 + num - 1; char *ipr = start2 + (num - 1) * ts; npy_int8 vp; char *ivp; npy_int8 SWAP_temp; char *iSWAP_temp; npy_int8 *stack[PYA_QS_STACK], **sptr = stack; char *istack[PYA_QS_STACK], **isptr = istack; npy_int8 *pm, *pi, *pj, *pt; char *ipm, *ipi, *ipj, *ipt; npy_intp i; ivp = malloc(ts); iSWAP_temp = malloc(ts); for(;;) { while ((pr - pl) > SMALL_QUICKSORT) { /* quicksort partition */ pm = pl + ((pr - pl) >> 1); ipm = ipl + (((ipr - ipl)/ts) >> 1)*ts; if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } if (*pr < *pm) { SWAP(*pr, *pm); iSWAP(ipr, ipm); } if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } vp = *pm; pi = pl; ipi = ipl; pj = pr - 1; ipj = ipr - ts; SWAP(*pm, *pj); iSWAP(ipm, ipj); for(;;) { do { ++pi; ipi += ts; } while (*pi < vp); do { --pj; ipj -= ts; } while (vp < *pj); if (pi >= pj) break; SWAP(*pi, *pj); iSWAP(ipi, ipj); } SWAP(*pi, *(pr-1)); iSWAP(ipi, ipr-ts); /* push largest partition on stack */ if (pi - pl < pr - pi) { *sptr++ = pi + 1; *isptr++ = ipi + ts; *sptr++ = pr; *isptr++ = ipr; pr = pi - 1; ipr = ipi - ts; }else{ *sptr++ = pl; *isptr++ = ipl; *sptr++ = pi - 1; *isptr++ = ipi - ts; pl = pi + 1; ipl = ipi + ts; } } /* insertion sort */ for(pi = pl + 1, ipi = ipl + ts; pi <= pr; ++pi, ipi += ts) { vp = *pi; opt_memcpy(ivp, ipi, ts); for(pj = pi, pt = pi - 1, ipj = ipi, ipt = ipi - ts; \ pj > pl && vp < *pt;) { *pj-- = *pt--; opt_memcpy(ipj, ipt, ts); ipj -= ts; ipt -= ts; } *pj = vp; opt_memcpy(ipj, ivp, ts); } if (sptr == stack) break; pr = *(--sptr); ipr = *(--isptr); pl = *(--sptr); ipl = *(--isptr); } free(ivp); free(iSWAP_temp); return 0; } int keysort_u8(npy_uint8 *start1, char *start2, npy_intp num, int ts) { npy_uint8 *pl = start1; char *ipl = start2; npy_uint8 *pr = start1 + num - 1; char *ipr = start2 + (num - 1) * ts; npy_uint8 vp; char *ivp; npy_uint8 SWAP_temp; char *iSWAP_temp; npy_uint8 *stack[PYA_QS_STACK], **sptr = stack; char *istack[PYA_QS_STACK], **isptr = istack; npy_uint8 *pm, *pi, *pj, *pt; char *ipm, *ipi, *ipj, *ipt; npy_intp i; ivp = malloc(ts); iSWAP_temp = malloc(ts); for(;;) { while ((pr - pl) > SMALL_QUICKSORT) { /* quicksort partition */ pm = pl + ((pr - pl) >> 1); ipm = ipl + (((ipr - ipl)/ts) >> 1)*ts; if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } if (*pr < *pm) { SWAP(*pr, *pm); iSWAP(ipr, ipm); } if (*pm < *pl) { SWAP(*pm, *pl); iSWAP(ipm, ipl); } vp = *pm; pi = pl; ipi = ipl; pj = pr - 1; ipj = ipr - ts; SWAP(*pm, *pj); iSWAP(ipm, ipj); for(;;) { do { ++pi; ipi += ts; } while (*pi < vp); do { --pj; ipj -= ts; } while (vp < *pj); if (pi >= pj) break; SWAP(*pi, *pj); iSWAP(ipi, ipj); } SWAP(*pi, *(pr-1)); iSWAP(ipi, ipr-ts); /* push largest partition on stack */ if (pi - pl < pr - pi) { *sptr++ = pi + 1; *isptr++ = ipi + ts; *sptr++ = pr; *isptr++ = ipr; pr = pi - 1; ipr = ipi - ts; }else{ *sptr++ = pl; *isptr++ = ipl; *sptr++ = pi - 1; *isptr++ = ipi - ts; pl = pi + 1; ipl = ipi + ts; } } /* insertion sort */ for(pi = pl + 1, ipi = ipl + ts; pi <= pr; ++pi, ipi += ts) { vp = *pi; opt_memcpy(ivp, ipi, ts); for(pj = pi, pt = pi - 1, ipj = ipi, ipt = ipi - ts; \ pj > pl && vp < *pt;) { *pj-- = *pt--; opt_memcpy(ipj, ipt, ts); ipj -= ts; ipt -= ts; } *pj = vp; opt_memcpy(ipj, ivp, ts); } if (sptr == stack) break; pr = *(--sptr); ipr = *(--isptr); pl = *(--sptr); ipl = *(--isptr); } free(ivp); free(iSWAP_temp); return 0; } int keysort_S(char *start1, int ss, char *start2, npy_intp num, int ts) { char *pl = start1; char *ipl = start2; char *pr = start1 + (num - 1) * ss; char *ipr = start2 + (num - 1) * ts; char *vp; char *ivp; char *iSWAP_temp; char *stack[PYA_QS_STACK], **sptr = stack; char *istack[PYA_QS_STACK], **isptr = istack; char *pm, *pi, *pj, *pt; char *ipm, *ipi, *ipj, *ipt; npy_intp i; vp = malloc(ss); ivp = malloc(ts); iSWAP_temp = malloc(ts); for(;;) { while ((pr - pl) > SMALL_QUICKSORT*ss) { /* quicksort partition */ pm = pl + (((pr-pl)/ss) >> 1)*ss; ipm = ipl + (((ipr-ipl)/ts) >> 1)*ts; if (opt_strncmp(pm, pl, ss) < 0) { sSWAP(pm, pl, ss); iSWAP(ipm, ipl); } if (opt_strncmp(pr, pm, ss) < 0) { sSWAP(pr, pm, ss); iSWAP(ipr, ipm); } if (opt_strncmp(pm, pl, ss) < 0) { sSWAP(pm, pl, ss); iSWAP(ipm, ipl); } opt_memcpy(vp, pm, ss); pi = pl; ipi = ipl; pj = pr - ss; ipj = ipr - ts; sSWAP(pm, pj, ss); iSWAP(ipm, ipj); for(;;) { do { pi += ss; ipi += ts; } while (opt_strncmp(pi, vp, ss) < 0); do { pj -= ss; ipj -= ts; } while (opt_strncmp(vp, pj, ss) < 0); if (pi >= pj) break; sSWAP(pi, pj, ss); iSWAP(ipi, ipj); } sSWAP(pi, pr-ss, ss); iSWAP(ipi, ipr-ts); /* push largest partition on stack */ if (pi - pl < pr - pi) { *sptr++ = pi + ss; *isptr++ = ipi + ts; *sptr++ = pr; *isptr++ = ipr; pr = pi - ss; ipr = ipi - ts; }else{ *sptr++ = pl; *isptr++ = ipl; *sptr++ = pi - ss; *isptr++ = ipi - ts; pl = pi + ss; ipl = ipi + ts; } } /* insertion sort */ for(pi = pl + ss, ipi = ipl + ts; pi <= pr; pi += ss, ipi += ts) { opt_memcpy(vp, pi, ss); opt_memcpy(ivp, ipi, ts); for(pj = pi, pt = pi - ss, ipj = ipi, ipt = ipi - ts; \ pj > pl && opt_strncmp(vp, pt, ss) < 0;) { opt_memcpy(pj, pt, ss); opt_memcpy(ipj, ipt, ts); pj -= ss; pt -= ss; ipj -= ts; ipt -= ts; } opt_memcpy(pj, vp, ss); opt_memcpy(ipj, ivp, ts); } if (sptr == stack) break; pr = *(--sptr); ipr = *(--isptr); pl = *(--sptr); ipl = *(--isptr); } free(vp); free(ivp); free(iSWAP_temp); return 0; } PyTables-v.3.1.1/src/idx-opt.h000066400000000000000000000054751231437614300160460ustar00rootroot00000000000000#include "Python.h" #include "numpy/arrayobject.h" #ifndef NPY_FLOAT16 typedef npy_uint16 npy_float16; #endif #ifndef NPY_FLOAT96 typedef long double npy_float96; #endif #ifndef NPY_FLOAT128 typedef long double npy_float128; #endif int bisect_left_b(npy_int8 *a, long x, int hi, int offset); int bisect_left_ub(npy_uint8 *a, long x, int hi, int offset); int bisect_right_b(npy_int8 *a, long x, int hi, int offset); int bisect_right_ub(npy_uint8 *a, long x, int hi, int offset); int bisect_left_s(npy_int16 *a, long x, int hi, int offset); int bisect_left_us(npy_uint16 *a, long x, int hi, int offset); int bisect_right_s(npy_int16 *a, long x, int hi, int offset); int bisect_right_us(npy_uint16 *a, long x, int hi, int offset); int bisect_left_i(npy_int32 *a, long x, int hi, int offset); int bisect_left_ui(npy_uint32 *a, npy_uint32 x, int hi, int offset); int bisect_right_i(npy_int32 *a, long x, int hi, int offset); int bisect_right_ui(npy_uint32 *a, npy_uint32 x, int hi, int offset); int bisect_left_ll(npy_int64 *a, npy_int64 x, int hi, int offset); int bisect_left_ull(npy_uint64 *a, npy_uint64 x, int hi, int offset); int bisect_right_ll(npy_int64 *a, npy_int64 x, int hi, int offset); int bisect_right_ull(npy_uint64 *a, npy_uint64 x, int hi, int offset); int bisect_left_e(npy_float16 *a, npy_float64 x, int hi, int offset); int bisect_right_e(npy_float16 *a, npy_float64 x, int hi, int offset); int bisect_left_f(npy_float32 *a, npy_float64 x, int hi, int offset); int bisect_right_f(npy_float32 *a, npy_float64 x, int hi, int offset); int bisect_left_d(npy_float64 *a, npy_float64 x, int hi, int offset); int bisect_right_d(npy_float64 *a, npy_float64 x, int hi, int offset); int bisect_left_g(npy_longdouble *a, npy_longdouble x, int hi, int offset); int bisect_right_g(npy_longdouble *a, npy_longdouble x, int hi, int offset); int keysort_f96(npy_float96 *start1, char *start2, npy_intp num, int ts); int keysort_f128(npy_float128 *start1, char *start2, npy_intp num, int ts); int keysort_f64(npy_float64 *start1, char *start2, npy_intp num, int ts); int keysort_f32(npy_float32 *start1, char *start2, npy_intp num, int ts); int keysort_f16(npy_float16 *start1, char *start2, npy_intp num, int ts); int keysort_i64(npy_int64 *start1, char *start2, npy_intp num, int ts); int keysort_u64(npy_uint64 *start1, char *start2, npy_intp num, int ts); int keysort_i32(npy_int32 *start1, char *start2, npy_intp num, int ts); int keysort_u32(npy_uint32 *start1, char *start2, npy_intp num, int ts); int keysort_i16(npy_int16 *start1, char *start2, npy_intp num, int ts); int keysort_u16(npy_uint16 *start1, char *start2, npy_intp num, int ts); int keysort_i8(npy_int8 *start1, char *start2, npy_intp num, int ts); int keysort_u8(npy_uint8 *start1, char *start2, npy_intp num, int ts); int keysort_S(char *start1, int ss, char *start2, npy_intp num, int ts); PyTables-v.3.1.1/src/tables.h000066400000000000000000000001151231437614300157160ustar00rootroot00000000000000typedef enum { Table, Array, EArray, VLArray, CArray } TablesType; PyTables-v.3.1.1/src/typeconv.c000066400000000000000000000051541231437614300163160ustar00rootroot00000000000000/*********************************************************************** * * License: BSD * Created: December 21, 2004 * Author: Ivan Vilata i Balaguer - reverse:net.selidor@ivan * Modified: * Function inlining and some castings for 64-bit adressing * Francesc Alted 2004-12-27 * * $Source: /cvsroot/pytables/pytables/src/typeconv.c,v $ * $Id$ * ***********************************************************************/ /* Type conversion functions for PyTables types which are stored * with a different representation between numpy and HDF5. */ #include "typeconv.h" #include #include #if (!defined _ISOC99_SOURCE && !defined __USE_ISOC99) long int lround(double x) { double trunx; if (x > 0.0) { trunx = floor(x); if (x - trunx >= 0.5) trunx += 1; } else { trunx = ceil(x); if (trunx - x >= 0.5) trunx -= 1; } return (long int)(trunx); } #endif /* !_ISOC99_SOURCE && !__USE_ISOC99 */ void conv_float64_timeval32(void *base, unsigned long byteoffset, unsigned long bytestride, PY_LONG_LONG nrecords, unsigned long nelements, int sense) { PY_LONG_LONG record; unsigned long element, gapsize; double *fieldbase; union { PY_LONG_LONG i64; double f64; } tv; assert(bytestride > 0); assert(nelements > 0); /* Byte distance from end of field to beginning of next field. */ gapsize = bytestride - nelements * sizeof(double); fieldbase = (double *)((unsigned char *)(base) + byteoffset); for (record = 0; record < nrecords; record++) { for (element = 0; element < nelements; element++) { /* Perform an explicit copy of data to avoid errors related to unaligned memory access on platforms like AMR, etc. Patch submitted by Julian Taylor */ double fb; memcpy(&fb, fieldbase, sizeof(*fieldbase)); if (sense == 0) { /* Convert from float64 to timeval32. */ tv.i64 = (((PY_LONG_LONG)(fb) << 32) | (lround((fb - (int)(fb)) * 1e+6) & 0x0ffffffff)); fb = tv.f64; } else { /* Convert from timeval32 to float64. */ tv.f64 = fb; /* the next computation is 64 bit-platforms aware */ fb = 1e-6 * (int)tv.i64 + (tv.i64 >> 32); } memcpy(fieldbase, &fb, sizeof(*fieldbase)); fieldbase++; } fieldbase = (double *)((unsigned char *)(fieldbase) + gapsize); } assert(fieldbase == (base + byteoffset + bytestride * nrecords)); } PyTables-v.3.1.1/src/typeconv.h000066400000000000000000000024121231437614300163150ustar00rootroot00000000000000/*********************************************************************** * * License: BSD * Created: December 21, 2004 * Author: Ivan Vilata i Balaguer - reverse:net.selidor@ivan * * $Source: /home/ivan/_/programari/pytables/svn/cvs/pytables/pytables/src/typeconv.h,v $ * $Id$ * ***********************************************************************/ /* Type conversion functions for PyTables types which are stored * with a different representation between numpy and HDF5. */ #ifndef __TYPECONV_H__ #define __TYPECONV_H__ 1 #include "Python.h" /* Meaning for common arguments: * * base: pointer to data * * byteoffset: offset of first field/element into the data * * bytestride: distance in bytes from a field/record to the next one * * nrecords: number of fields/records to translate * * nelements: number of elements in a field/record * * sense: 0 for Numarray -> HDF5, otherwise HDF5 -> Numarray */ void conv_float64_timeval32(void *base, unsigned long byteoffset, unsigned long bytestride, PY_LONG_LONG nrecords, unsigned long nelements, int sense); #endif /* def __TYPECONV_H__ */ PyTables-v.3.1.1/src/utils.c000066400000000000000000000744031231437614300156120ustar00rootroot00000000000000#include #include "utils.h" #include "version.h" #include "H5Zlzo.h" /* Import FILTER_LZO */ #include "H5Zbzip2.h" /* Import FILTER_BZIP2 */ #if PY_MAJOR_VERSION > 2 #define PyString_FromString PyUnicode_FromString #endif #ifndef NPY_COMPLEX192 typedef npy_cdouble npy_complex192; #endif #ifndef NPY_COMPLEX256 typedef npy_cdouble npy_complex256; #endif /* ---------------------------------------------------------------- */ #ifdef WIN32 #include /* This routine is meant to detect whether a dynamic library can be loaded on Windows. This is only way to detect its presence without harming the user. */ int getLibrary(char *libname) { HINSTANCE hinstLib; /* Load the dynamic library */ hinstLib = LoadLibrary(TEXT(libname)); if (hinstLib != NULL) { /* Free the dynamic library */ FreeLibrary(hinstLib); return 0; } else { return -1; } } #else /* Unix platforms */ #include /* Routine to detect the existance of shared libraries in UNIX. This has to be checked in MacOSX. However, this is not used right now in utilsExtension.pyx because UNIX does not complain when trying to load an extension library that depends on a shared library that it is not in the system (python raises just the ImportError). */ int getLibrary(char *libname) { void *hinstLib; /* Load the dynamic library */ hinstLib = dlopen(libname, RTLD_LAZY); if (hinstLib != NULL) { /* Free the dynamic library */ dlclose(hinstLib); return 0; } else { return -1; } } #endif /* Win32 */ herr_t set_cache_size(hid_t file_id, size_t cache_size) { #if H5_VERS_MAJOR == 1 && H5_VERS_MINOR >= 7 /* MSVS2005 chokes on declarations after statements */ H5AC_cache_config_t config; #endif /* if H5_VERSION < "1.7" */ herr_t code; code = 0; #if H5_VERS_MAJOR == 1 && H5_VERS_MINOR >= 7 config.version = H5AC__CURR_CACHE_CONFIG_VERSION; code = H5Fget_mdc_config(file_id, &config); config.set_initial_size = TRUE; config.initial_size = cache_size; /* config.incr_mode = H5C_incr__off; */ /* config.decr_mode = H5C_decr__off; */ /* printf("Setting cache size to: %d\n", cache_size); */ code = H5Fset_mdc_config(file_id, &config); /* printf("Return code for H5Fset_mdc_config: %d\n", code); */ #endif /* if H5_VERSION < "1.7" */ return code; } PyObject *_getTablesVersion() { return PyString_FromString(PYTABLES_VERSION); } PyObject *getHDF5VersionInfo(void) { long binver; unsigned majnum, minnum, relnum; char strver[16]; PyObject *t; /* H5get_libversion(&majnum, &minnum, &relnum); */ majnum = H5_VERS_MAJOR; minnum = H5_VERS_MINOR; relnum = H5_VERS_RELEASE; /* Get a binary number */ binver = majnum << 16 | minnum << 8 | relnum; /* A string number */ if (strcmp(H5_VERS_SUBRELEASE, "")) { snprintf(strver, 16, "%d.%d.%d-%s", majnum, minnum, relnum, H5_VERS_SUBRELEASE); } else { snprintf(strver, 16, "%d.%d.%d", majnum, minnum, relnum); } t = PyTuple_New(2); PyTuple_SetItem(t, 0, PyLong_FromLong(binver)); PyTuple_SetItem(t, 1, PyString_FromString(strver)); return t; } /**************************************************************** ** ** createNamesTuple(): Create Python tuple from a string of *char. ** ****************************************************************/ PyObject *createNamesTuple(char *buffer[], int nelements) { int i; PyObject *t; PyObject *str; t = PyTuple_New(nelements); for (i = 0; i < nelements; i++) { str = PyString_FromString(buffer[i]); PyTuple_SetItem(t, i, str); /* PyTuple_SetItem does not need a decref, because it already do this */ /* Py_DECREF(str); */ } return t; } PyObject *createNamesList(char *buffer[], int nelements) { int i; PyObject *t; PyObject *str; t = PyList_New(nelements); for (i = 0; i < nelements; i++) { str = PyString_FromString(buffer[i]); PyList_SetItem(t, i, str); /* PyList_SetItem does not need a decref, because it already do this */ /* Py_DECREF(str); */ } return t; } /*------------------------------------------------------------------------- * Function: get_filter_names * * Purpose: Get the filter names for the chunks in a dataset * * Return: Success: 0, Failure: -1 * * Programmer: Francesc Alted, faltet@pytables.com * * Date: December 19, 2003 * * Comments: * * Modifications: * * *------------------------------------------------------------------------- */ PyObject *get_filter_names( hid_t loc_id, const char *dset_name) { hid_t dset; hid_t dcpl; /* dataset creation property list */ /* hsize_t chsize[64]; /\* chunk size in elements *\/ */ int i, j; int nf; /* number of filters */ unsigned filt_flags; /* filter flags */ size_t cd_nelmts; /* filter client number of values */ unsigned cd_values[20]; /* filter client data values */ char f_name[256]; /* filter name */ PyObject *filters; PyObject *filter_values; /* Open the dataset. */ if ( (dset = H5Dopen( loc_id, dset_name, H5P_DEFAULT )) < 0 ) { goto out; } /* Get the properties container */ dcpl = H5Dget_create_plist(dset); /* Collect information about filters on chunked storage */ if (H5D_CHUNKED==H5Pget_layout(dcpl)) { filters = PyDict_New(); if ((nf = H5Pget_nfilters(dcpl))>0) { for (i=0; itype) { case H5L_TYPE_SOFT: case H5L_TYPE_EXTERNAL: PyList_Append(out_info[2], strname); break; case H5L_TYPE_ERROR: /* XXX: check */ PyList_Append(out_info[3], strname); break; case H5L_TYPE_HARD: /* Get type of the object and check it */ ret = H5Oget_info_by_name(loc_id, name, &oinfo, H5P_DEFAULT); if (ret < 0) return -1; switch(oinfo.type) { case H5O_TYPE_GROUP: PyList_Append(out_info[0], strname); break; case H5O_TYPE_DATASET: PyList_Append(out_info[1], strname); break; case H5O_TYPE_NAMED_DATATYPE: ++namedtypes; break; case H5O_TYPE_UNKNOWN: PyList_Append(out_info[3], strname); break; default: /* should not happen */ PyList_Append(out_info[3], strname); } break; default: /* should not happen */ PyList_Append(out_info[3], strname); } Py_DECREF(strname); return 0 ; /* Loop until no more objects remain in directory */ } /**************************************************************** ** ** Giterate(): Group iteration routine. ** ****************************************************************/ PyObject *Giterate(hid_t parent_id, hid_t loc_id, const char *name) { hsize_t i=0; PyObject *t, *tgroup, *tleave, *tlink, *tunknown; PyObject *info[4]; info[0] = tgroup = PyList_New(0); info[1] = tleave = PyList_New(0); info[2] = tlink = PyList_New(0); info[3] = tunknown = PyList_New(0); /* Iterate over all the childs behind loc_id (parent_id+loc_id). * NOTE: using H5_INDEX_CRT_ORDER instead of H5_INDEX_NAME causes failures * in the test suite */ H5Literate_by_name(parent_id, name, H5_INDEX_NAME, H5_ITER_NATIVE, &i, litercb, info, H5P_DEFAULT); /* Create the tuple with the list of Groups and Datasets */ t = PyTuple_New(4); PyTuple_SetItem(t, 0, tgroup); PyTuple_SetItem(t, 1, tleave); PyTuple_SetItem(t, 2, tlink); PyTuple_SetItem(t, 3, tunknown); return t; } /**************************************************************** ** ** aitercb(): Custom attribute iteration callback routine. ** ****************************************************************/ static herr_t aitercb( hid_t loc_id, const char *name, const H5A_info_t *ainfo, void *op_data) { PyObject *strname; strname = PyString_FromString(name); /* Return the name of the attribute on op_data */ PyList_Append(op_data, strname); Py_DECREF(strname); return(0); /* Loop until no more attrs remain in object */ } /**************************************************************** ** ** Aiterate(): Attribute set iteration routine. ** ****************************************************************/ PyObject *Aiterate(hid_t loc_id) { hsize_t i = 0; PyObject *attrlist; /* List where the attrnames are put */ attrlist = PyList_New(0); H5Aiterate(loc_id, H5_INDEX_CRT_ORDER, H5_ITER_NATIVE, &i, (H5A_operator_t)aitercb, (void *)attrlist); return attrlist; } /**************************************************************** ** ** getHDF5ClassID(): Returns class ID for loc_id.name. -1 if error. ** ****************************************************************/ H5T_class_t getHDF5ClassID(hid_t loc_id, const char *name, H5D_layout_t *layout, hid_t *type_id, hid_t *dataset_id) { H5T_class_t class_id; hid_t plist; /* Open the dataset. */ if ( (*dataset_id = H5Dopen( loc_id, name, H5P_DEFAULT )) < 0 ) return -1; /* Get an identifier for the datatype. */ *type_id = H5Dget_type( *dataset_id ); /* Get the class. */ class_id = H5Tget_class( *type_id ); /* Get the layout of the datatype */ plist = H5Dget_create_plist(*dataset_id); *layout = H5Pget_layout(plist); H5Pclose(plist); return class_id; } /* Helper routine that returns the rank, dims and byteorder for UnImplemented objects. 2004 */ PyObject *H5UIget_info( hid_t loc_id, const char *dset_name, char *byteorder) { hid_t dataset_id; int rank; hsize_t *dims; hid_t space_id; H5T_class_t class_id; H5T_order_t order; hid_t type_id; PyObject *t; int i; /* Open the dataset. */ if ( (dataset_id = H5Dopen( loc_id, dset_name, H5P_DEFAULT )) < 0 ) { Py_INCREF(Py_None); return Py_None; /* Not chunked, so return None */ } /* Get an identifier for the datatype. */ type_id = H5Dget_type( dataset_id ); /* Get the class. */ class_id = H5Tget_class( type_id ); /* Get the dataspace handle */ if ( (space_id = H5Dget_space( dataset_id )) < 0 ) goto out; /* Get rank */ if ( (rank = H5Sget_simple_extent_ndims( space_id )) < 0 ) goto out; /* Book resources for dims */ dims = (hsize_t *)malloc(rank * sizeof(hsize_t)); /* Get dimensions */ if ( H5Sget_simple_extent_dims( space_id, dims, NULL) < 0 ) goto out; /* Assign the dimensions to a tuple */ t = PyTuple_New(rank); for(i=0;i ll_max) x = ll_max; else if (x < -ll_max) x = -ll_max; *pi = x; } return 1; } /* This has been copied from the Python 2.3 sources in order to get a function similar to the method slice.indices(length) but that works with 64-bit ints and not only with ints. */ /* F. Alted 2005-05-08 */ hsize_t getIndicesExt(PyObject *s, hsize_t length, hssize_t *start, hssize_t *stop, hssize_t *step, hsize_t *slicelength) { /* this is harder to get right than you might think */ hssize_t defstart, defstop; PySliceObject *r = (PySliceObject *) s; if (r->step == Py_None) { *step = 1; } else { if (!_PyEval_SliceIndex_modif(r->step, step)) return -1; if ((PY_LONG_LONG)*step == 0) { PyErr_SetString(PyExc_ValueError, "slice step cannot be zero"); return -1; } } defstart = (PY_LONG_LONG)*step < 0 ? length-1 : 0; defstop = (PY_LONG_LONG)*step < 0 ? -1 : length; if (r->start == Py_None) { *start = defstart; } else { if (!_PyEval_SliceIndex_modif(r->start, start)) return -1; if ((PY_LONG_LONG)*start < 0L) *start += length; if ((PY_LONG_LONG)*start < 0) *start = ((PY_LONG_LONG)*step < 0) ? -1 : 0; if ((PY_LONG_LONG)*start >= (PY_LONG_LONG)length) *start = ((PY_LONG_LONG)*step < 0) ? length - 1 : length; } if (r->stop == Py_None) { *stop = defstop; } else { if (!_PyEval_SliceIndex_modif(r->stop, stop)) return -1; if ((PY_LONG_LONG)*stop < 0) *stop += length; if ((PY_LONG_LONG)*stop < 0) *stop = -1; //if ((PY_LONG_LONG)*stop < 0) *stop = (*step < 0) ? -1 : 0; if ((PY_LONG_LONG)*stop > (PY_LONG_LONG)length) *stop = length; //if ((PY_LONG_LONG)*stop >= (PY_LONG_LONG)length)// *stop = length; // *stop = (*step < 0) ? length - 1 : length; } if (((PY_LONG_LONG)*step < 0 && (PY_LONG_LONG)*stop >= (PY_LONG_LONG)*start) || ((PY_LONG_LONG)*step > 0 && (PY_LONG_LONG)*start >= (PY_LONG_LONG)*stop)) { *slicelength = 0; } else if ((PY_LONG_LONG)*step < 0) { *slicelength = (*stop-*start+1)/(*step)+1; } else { *slicelength = (*stop-*start-1)/(*step)+1; } return 0; } /* The next provides functions to support a complex datatype. HDF5 does not provide an atomic type class for complex numbers so we make one from a HDF5 compound type class. Added by Tom Hedley April 2004. Adapted to support Tables by F. Alted September 2004. */ /* Test whether the datatype is of class complex return 1 if it corresponds to our complex class, otherwise 0 */ /* This may be ultimately confused with nested types with 2 components called 'r' and 'i' and being floats, but in that case, the user most probably wanted to keep a complex type, so getting a complex instead of a nested type should not be a big issue (I hope!) :-/ F. Alted 2005-05-23 */ int is_complex(hid_t type_id) { hid_t class_id, base_type_id; hid_t class1, class2; char *colname1, *colname2; int result = 0; hsize_t nfields; class_id = H5Tget_class(type_id); if (class_id == H5T_COMPOUND) { nfields = H5Tget_nmembers(type_id); if (nfields == 2) { colname1 = H5Tget_member_name(type_id, 0); colname2 = H5Tget_member_name(type_id, 1); if ((strcmp(colname1, "r") == 0) && (strcmp(colname2, "i") == 0)) { class1 = H5Tget_member_class(type_id, 0); class2 = H5Tget_member_class(type_id, 1); if (class1 == H5T_FLOAT && class2 == H5T_FLOAT) result = 1; } free(colname1); free(colname2); } } /* Is an Array of Complex? */ else if (class_id == H5T_ARRAY) { /* Get the array base component */ base_type_id = H5Tget_super(type_id); /* Call is_complex again */ result = is_complex(base_type_id); H5Tclose(base_type_id); } return result; } /* Return the byteorder of a complex datatype. It is obtained from the real part, which is the first member. */ static H5T_order_t get_complex_order(hid_t type_id) { hid_t class_id, base_type_id; hid_t real_type = 0; H5T_order_t result = 0; class_id = H5Tget_class(type_id); if (class_id == H5T_COMPOUND) { real_type = H5Tget_member_type(type_id, 0); } else if (class_id == H5T_ARRAY) { /* Get the array base component */ base_type_id = H5Tget_super(type_id); /* Get the type of real component. */ real_type = H5Tget_member_type(base_type_id, 0); H5Tclose(base_type_id); } if ((class_id == H5T_COMPOUND) || (class_id == H5T_ARRAY)) { result = H5Tget_order(real_type); H5Tclose(real_type); } return result; } /* Return the byteorder of a HDF5 data type */ /* This is actually an extension of H5Tget_order to handle complex types */ herr_t get_order(hid_t type_id, char *byteorder) { H5T_order_t h5byteorder; /* hid_t class_id; class_id = H5Tget_class(type_id); */ if (is_complex(type_id)) { h5byteorder = get_complex_order(type_id); } else { h5byteorder = H5Tget_order(type_id); } if (h5byteorder == H5T_ORDER_LE) { strcpy(byteorder, "little"); return h5byteorder; } else if (h5byteorder == H5T_ORDER_BE ) { strcpy(byteorder, "big"); return h5byteorder; } else if (h5byteorder == H5T_ORDER_NONE ) { strcpy(byteorder, "irrelevant"); return h5byteorder; } else { /* This should never happen! */ fprintf(stderr, "Error: unsupported byteorder <%d>\n", h5byteorder); strcpy(byteorder, "unsupported"); return -1; } } /* Set the byteorder of type_id. */ /* This only works for datatypes that are not Complex. However, these types should already been created with correct byteorder */ herr_t set_order(hid_t type_id, const char *byteorder) { herr_t status=0; if (! is_complex(type_id)) { if (strcmp(byteorder, "little") == 0) status = H5Tset_order(type_id, H5T_ORDER_LE); else if (strcmp(byteorder, "big") == 0) status = H5Tset_order(type_id, H5T_ORDER_BE); else if (strcmp(byteorder, "irrelevant") == 0) { /* Do nothing because 'irrelevant' doesn't require setting the byteorder explicitely */ /* status = H5Tset_order(type_id, H5T_ORDER_NONE ); */ } else { fprintf(stderr, "Error: unsupported byteorder <%s>\n", byteorder); status = -1; } } return status; } /* Create a HDF5 atomic datatype that represents half precision floatting point numbers defined by numpy as float16. */ hid_t create_ieee_float16(const char *byteorder) { hid_t float_id; if (byteorder == NULL) float_id = H5Tcopy(H5T_NATIVE_FLOAT); else if (strcmp(byteorder, "little") == 0) float_id = H5Tcopy(H5T_IEEE_F32LE); else float_id = H5Tcopy(H5T_IEEE_F32BE); if (float_id < 0) return float_id; if (H5Tset_fields(float_id, 15, 10, 5, 0, 10) < 0) return -1; if (H5Tset_size(float_id, 2) < 0) return -1; if (H5Tset_ebias(float_id, 15) < 0) return -1; return float_id; } /* Create a HDF5 atomic datatype that represents quad precision floatting point numbers. */ hid_t create_ieee_quadprecision_float(const char *byteorder) { hid_t float_id; if (byteorder == NULL) float_id = H5Tcopy(H5T_NATIVE_DOUBLE); else if (strcmp(byteorder, "little") == 0) float_id = H5Tcopy(H5T_IEEE_F64LE); else float_id = H5Tcopy(H5T_IEEE_F64BE); if (float_id < 0) return float_id; if (H5Tset_size(float_id, 16) < 0) return -1; if ((H5Tset_precision(float_id, 128)) < 0) return -1; if (H5Tset_fields(float_id , 127, 112, 15, 0, 112) < 0) return -1; if (H5Tset_ebias(float_id, 16383) < 0) return -1; return float_id; } /* Create a HDF5 compound datatype that represents complex numbers defined by numpy as complex64. */ hid_t create_ieee_complex64(const char *byteorder) { hid_t float_id, complex_id; complex_id = H5Tcreate(H5T_COMPOUND, sizeof(npy_complex64)); if (byteorder == NULL) float_id = H5Tcopy(H5T_NATIVE_FLOAT); else if (strcmp(byteorder, "little") == 0) float_id = H5Tcopy(H5T_IEEE_F32LE); else float_id = H5Tcopy(H5T_IEEE_F32BE); if (float_id < 0) { H5Tclose(complex_id); return float_id; } H5Tinsert(complex_id, "r", HOFFSET(npy_complex64, real), float_id); H5Tinsert(complex_id, "i", HOFFSET(npy_complex64, imag), float_id); H5Tclose(float_id); return complex_id; } /* Counterpart for complex128 */ hid_t create_ieee_complex128(const char *byteorder) { hid_t float_id, complex_id; complex_id = H5Tcreate(H5T_COMPOUND, sizeof(npy_complex128)); if (byteorder == NULL) float_id = H5Tcopy(H5T_NATIVE_DOUBLE); else if (strcmp(byteorder, "little") == 0) float_id = H5Tcopy(H5T_IEEE_F64LE); else float_id = H5Tcopy(H5T_IEEE_F64BE); if (float_id < 0) { H5Tclose(complex_id); return float_id; } H5Tinsert(complex_id, "r", HOFFSET(npy_complex128, real), float_id); H5Tinsert(complex_id, "i", HOFFSET(npy_complex128, imag), float_id); H5Tclose(float_id); return complex_id; } /* Counterpart for complex192 */ hid_t create_ieee_complex192(const char *byteorder) { herr_t err = 0; hid_t float_id, complex_id; H5T_order_t h5order = H5Tget_order(H5T_NATIVE_LDOUBLE); complex_id = H5Tcreate(H5T_COMPOUND, sizeof(npy_complex192)); float_id = H5Tcopy(H5T_NATIVE_LDOUBLE); if (float_id < 0) { H5Tclose(complex_id); return float_id; } if ((strcmp(byteorder, "little") == 0) && (h5order != H5T_ORDER_LE)) err = H5Tset_order(float_id, H5T_ORDER_LE); else if ((strcmp(byteorder, "big") == 0) && (h5order != H5T_ORDER_BE)) err = H5Tset_order(float_id, H5T_ORDER_BE); if (err < 0) { H5Tclose(complex_id); return err; } H5Tinsert(complex_id, "r", HOFFSET(npy_complex192, real), float_id); H5Tinsert(complex_id, "i", HOFFSET(npy_complex192, imag), float_id); H5Tclose(float_id); return complex_id; } /* Counterpart for complex256 */ hid_t create_ieee_complex256(const char *byteorder) { herr_t err = 0; hid_t float_id, complex_id; H5T_order_t h5order = H5Tget_order(H5T_NATIVE_LDOUBLE); complex_id = H5Tcreate(H5T_COMPOUND, sizeof(npy_complex256)); float_id = H5Tcopy(H5T_NATIVE_LDOUBLE); if (float_id < 0) { H5Tclose(complex_id); return float_id; } if ((strcmp(byteorder, "little") == 0) && (h5order != H5T_ORDER_LE)) err = H5Tset_order(float_id, H5T_ORDER_LE); else if ((strcmp(byteorder, "big") == 0) && (h5order != H5T_ORDER_BE)) err = H5Tset_order(float_id, H5T_ORDER_BE); if (err < 0) { H5Tclose(complex_id); return err; } H5Tinsert(complex_id, "r", HOFFSET(npy_complex256, real), float_id); H5Tinsert(complex_id, "i", HOFFSET(npy_complex256, imag), float_id); H5Tclose(float_id); return complex_id; } /* Return the number of significant bits in the real and imaginary parts */ /* This is actually an extension of H5Tget_precision to handle complex types */ size_t get_complex_precision(hid_t type_id) { hid_t real_type; size_t result; real_type = H5Tget_member_type(type_id, 0); result = H5Tget_precision(real_type); H5Tclose(real_type); return result; } /* End of complex additions */ /* The get_len_of_range has been taken from Python interpreter */ /* Return number of items in range/xrange (lo, hi, step). step > 0 * required. Return a value < 0 if & only if the true value is too * large to fit in a signed long. */ hsize_t get_len_of_range(hsize_t lo, hsize_t hi, hsize_t step) { /* ------------------------------------------------------------- If lo >= hi, the range is empty. Else if n values are in the range, the last one is lo + (n-1)*step, which must be <= hi-1. Rearranging, n <= (hi - lo - 1)/step + 1, so taking the floor of the RHS gives the proper value. Since lo < hi in this case, hi-lo-1 >= 0, so the RHS is non-negative and so truncation is the same as the floor. Letting M be the largest positive long, the worst case for the RHS numerator is hi=M, lo=-M-1, and then hi-lo-1 = M-(-M-1)-1 = 2*M. Therefore unsigned long has enough precision to compute the RHS exactly. Note: We are using here 64 bit ints because PyTables can deal with 64-bit addresses even on 32-bit platforms. F. Alted 2006-09-25 ---------------------------------------------------------------*/ hsize_t n = 0; if (lo < hi) { hsize_t diff = hi - lo - 1; n = (hsize_t)(diff / step + 1); } return n; } /* Truncate the dataset to at most size rows */ herr_t truncate_dset( hid_t dataset_id, const int maindim, const hsize_t size) { hid_t space_id; hsize_t *dims = NULL; int rank; /* Get the dataspace handle */ if ( (space_id = H5Dget_space(dataset_id)) < 0 ) goto out; /* Get the rank */ if ( (rank = H5Sget_simple_extent_ndims(space_id)) < 0 ) goto out; if (rank) { /* multidimensional case */ /* Book some memory for the selections */ dims = (hsize_t *)malloc(rank*sizeof(hsize_t)); /* Get dataset dimensionality */ if ( H5Sget_simple_extent_dims(space_id, dims, NULL) < 0 ) goto out; /* Truncate the EArray */ dims[maindim] = size; if ( H5Dset_extent(dataset_id, dims) < 0 ) goto out; /* Release resources */ free(dims); } else { /* scalar case (should never enter here) */ printf("A scalar Array cannot be truncated!.\n"); goto out; } /* Free resources */ if ( H5Sclose(space_id) < 0 ) return -1; return 0; out: if (dims) free(dims); return -1; } /* * Helpers for management of HDF5 drivers */ /* DIRECT driver */ #ifdef H5_HAVE_DIRECT herr_t pt_H5Pset_fapl_direct(hid_t fapl_id, size_t alignment, size_t block_size, size_t cbuf_size) { return H5Pset_fapl_direct(fapl_id, alignment, block_size, cbuf_size); } #else /* H5_HAVE_DIRECT */ herr_t pt_H5Pset_fapl_direct(hid_t fapl_id, size_t alignment, size_t block_size, size_t cbuf_size) { return -1; } #endif /* H5_HAVE_DIRECT */ /* WINDOWS driver */ #ifdef H5_HAVE_WINDOWS herr_t pt_H5Pset_fapl_windows(hid_t fapl_id) { return H5Pset_fapl_windows(fapl_id); } #else /* H5_HAVE_WINDOWS */ herr_t pt_H5Pset_fapl_windows(hid_t fapl_id) { return -1; } #endif /* H5_HAVE_WINDOWS */ #if (H5_HAVE_IMAGE_FILE == 1) /* HDF5 version >= 1.8.9 */ herr_t pt_H5Pset_file_image(hid_t fapl_id, void *buf_ptr, size_t buf_len) { return H5Pset_file_image(fapl_id, buf_ptr, buf_len); } ssize_t pt_H5Fget_file_image(hid_t file_id, void *buf_ptr, size_t buf_len) { return H5Fget_file_image(file_id, buf_ptr, buf_len); } #else /* (H5_HAVE_IMAGE_FILE == 1) */ /* HDF5 version < 1.8.9 */ herr_t pt_H5Pset_file_image(hid_t fapl_id, void *buf_ptr, size_t buf_len) { return -1; } ssize_t pt_H5Fget_file_image(hid_t file_id, void *buf_ptr, size_t buf_len) { return -1; } #endif /* (H5_HAVE_IMAGE_FILE == 1) */ PyTables-v.3.1.1/src/utils.h000066400000000000000000000100571231437614300156120ustar00rootroot00000000000000#include "Python.h" #include "numpy/arrayobject.h" #include "hdf5.h" /* Define this variable for error printings */ /*#define DEBUG 1 */ /* Define this variable for debugging printings */ /*#define PRINT 1 */ /* Define this for compile the main() function */ /* #define MAIN 1 */ /* * Status return values for the herr_t' type. * Since some unix/c routines use 0 and -1 (or more precisely, non-negative * vs. negative) as their return code, and some assumption had been made in * the code about that, it is important to keep these constants the same * values. When checking the success or failure of an integer-valued * function, remember to compare against zero and not one of these two * values. */ #define SUCCEED 0 #define FAIL (-1) #define UFAIL (unsigned)(-1) /* * HDF Boolean type. */ #ifndef FALSE # define FALSE 0 #endif #ifndef TRUE # define TRUE (!FALSE) #endif #ifdef H5_HAVE_WINDOWS #define H5_HAVE_WINDOWS_DRIVER 1 #else #define H5_HAVE_WINDOWS_DRIVER 0 #endif #ifdef H5_HAVE_DIRECT #define H5_HAVE_DIRECT_DRIVER 1 #else #define H5_HAVE_DIRECT_DRIVER 0 #endif #if (H5_VERS_MAJOR == 1 && H5_VERS_MINOR == 8 && H5_VERS_RELEASE >= 9) || (H5_VERS_MAJOR == 1 && H5_VERS_MINOR > 8) /* HDF5 version >= 1.8.9 */ #define H5_HAVE_IMAGE_FILE 1 #else /* HDF5 version < 1.8.9 */ #define H5_HAVE_IMAGE_FILE 0 #endif /* Use %ld to print the value because long should cover most cases. */ /* Used to make certain a return value _is_not_ a value */ #define CHECK(ret, val, where) do { \ if (ret == val) { \ printf("*** UNEXPECTED RETURN from %s is %ld at line %4d " \ "in %s\n", where, (long)ret, (int)__LINE__, __FILE__); \ H5Eprint(H5E_DEFAULT, stdout); \ } \ H5Eclear(H5E_DEFAULT); \ } while(0) int getLibrary(char *libname); herr_t set_cache_size(hid_t file_id, size_t cache_size); PyObject *_getTablesVersion(void); /* PyObject *getZLIBVersionInfo(void); */ PyObject *getHDF5VersionInfo(void); PyObject *createNamesTuple(char *buffer[], int nelements); PyObject *get_filter_names( hid_t loc_id, const char *dset_name); int get_objinfo(hid_t loc_id, const char *name); int get_linkinfo(hid_t loc_id, const char *name); PyObject *Giterate(hid_t parent_id, hid_t loc_id, const char *name); PyObject *Aiterate(hid_t loc_id); H5T_class_t getHDF5ClassID(hid_t loc_id, const char *name, H5D_layout_t *layout, hid_t *type_id, hid_t *dataset_id); PyObject *H5UIget_info( hid_t loc_id, const char *dset_name, char *byteorder); hsize_t getIndicesExt(PyObject *s, hsize_t length, hssize_t *start, hssize_t *stop, hssize_t *step, hsize_t *slicelength); herr_t set_order(hid_t type_id, const char *byteorder); int is_complex(hid_t type_id); size_t get_complex_precision(hid_t type_id); herr_t get_order(hid_t type_id, char *byteorder); hid_t create_ieee_float16(const char *byteorder); hid_t create_ieee_quadprecision_float(const char *byteorder); hid_t create_ieee_complex64(const char *byteorder); hid_t create_ieee_complex128(const char *byteorder); hid_t create_ieee_complex192(const char *byteorder); hid_t create_ieee_complex256(const char *byteorder); hsize_t get_len_of_range(hsize_t lo, hsize_t hi, hsize_t step); herr_t truncate_dset( hid_t dataset_id, const int maindim, const hsize_t size); herr_t pt_H5Pset_fapl_direct(hid_t fapl_id, size_t alignment, size_t block_size, size_t cbuf_size); herr_t pt_H5Pset_fapl_windows(hid_t fapl_id); herr_t pt_H5Pset_file_image(hid_t fapl_id, void *buf_ptr, size_t buf_len); ssize_t pt_H5Fget_file_image(hid_t file_id, void *buf_ptr, size_t buf_len); PyTables-v.3.1.1/src/version.h.in000066400000000000000000000000451231437614300165400ustar00rootroot00000000000000#define PYTABLES_VERSION "@VERSION@" PyTables-v.3.1.1/subtree-merge-blosc.sh000077500000000000000000000017571231437614300177260ustar00rootroot00000000000000#!/bin/sh # Script to automatically subtree merge a specifc version of blosc to # python-blosc. # TODO # ---- # # * Should probably check working tree and index are clean. # configure remote remote="git://github.com/FrancescAlted/blosc.git" # check argument if [ -z "$1" ] ; then echo "usage: subtree-merge-blosc.sh " exit 1 fi # extract the blosc tag the user has requested blosc_tag="$1" blosc_tag_long="refs/tags/$1" # check that it exists on the remote side remote_ans=$( git ls-remote $remote $blosc_tag_long ) if [ -z "$remote_ans" ] ; then echo "no remote tag '$1' found" exit 1 else echo "found remote tag: '$remote_ans'" fi # fetch the contents of this tag git fetch $remote $blosc_tag_long || exit 1 # subtree merge it git merge --squash -s subtree FETCH_HEAD || exit 1 if git diff --staged --quiet ; then echo "nothing new to be committed" exit 1 else # set a custom commit message git commit -m "subtree merge blosc $blosc_tag" || exit 1 fi PyTables-v.3.1.1/tables/000077500000000000000000000000001231437614300147615ustar00rootroot00000000000000PyTables-v.3.1.1/tables/__init__.py000066400000000000000000000165611231437614300171030ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: October 1, 2002 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """PyTables, hierarchical datasets in Python. :URL: http://www.pytables.org/ PyTables is a package for managing hierarchical datasets and designed to efficiently cope with extremely large amounts of data. """ import os # On Windows, pre-load the HDF5 DLLs into the process via Ctypes # to improve diagnostics and avoid issues when loading DLLs during runtime. if os.name == 'nt': import ctypes def _load_library(dllname, loadfunction, dllpaths=('', )): """Load a DLL via ctypes load function. Return None on failure. By default, try to load the DLL from the current package directory first, then from the Windows DLL search path. """ try: dllpaths = (os.path.abspath( os.path.dirname(__file__)), ) + dllpaths except NameError: pass # PyPy and frozen distributions have no __file__ attribute for path in dllpaths: if path: # Temporarily add the path to the PATH environment variable # so Windows can find additional DLL dependencies. try: oldenv = os.environ['PATH'] os.environ['PATH'] = path + ';' + oldenv except KeyError: oldenv = None try: return loadfunction(os.path.join(path, dllname)) except WindowsError: pass finally: if path and oldenv is not None: os.environ['PATH'] = oldenv return None # In order to improve diagnosis of a common Windows dependency # issue, we explicitly test that we can load the HDF5 dll before # loading tables.utilsextensions. if not _load_library('hdf5dll.dll', ctypes.cdll.LoadLibrary): raise ImportError( 'Could not load "hdf5dll.dll", please ensure' ' that it can be found in the system path') # Some PyTables binary distributions place the dependency DLLs in the # tables package directory. # The lzo2 and libbz2 DLLs are loaded dynamically at runtime but can't be # found because the package directory is not in the Windows DLL search # path. # This pre-loads lzo2 and libbz2 DLLs from the tables package directory. if not _load_library('lzo2.dll', ctypes.cdll.LoadLibrary): pass if not _load_library('libbz2.dll', ctypes.cdll.LoadLibrary): pass # Necessary imports to get versions stored on the cython extension from tables.utilsextension import ( get_pytables_version, get_hdf5_version, blosc_compressor_list, blosc_compcode_to_compname_ as blosc_compcode_to_compname, getPyTablesVersion, getHDF5Version) # Pending Deprecation! __version__ = get_pytables_version() """The PyTables version number.""" hdf5_version = get_hdf5_version() """The underlying HDF5 library version number. .. versionadded:: 3.0 """ hdf5Version = hdf5_version """The underlying HDF5 library version number. .. deprecated:: 3.0 hdf5Version is pending deprecation, use :data:`hdf5_version` instead. """ from tables.utilsextension import (is_hdf5_file, is_pytables_file, which_lib_version, set_blosc_max_threads, silence_hdf5_messages, # Pending Deprecation! isHDF5File, isPyTablesFile, whichLibVersion, setBloscMaxThreads, silenceHDF5Messages) from tables.misc.enum import Enum from tables.atom import * from tables.flavor import restrict_flavors from tables.description import * from tables.filters import Filters # Import the user classes from the proper modules from tables.exceptions import * from tables.file import File, open_file, copy_file, openFile, copyFile from tables.node import Node from tables.group import Group from tables.leaf import Leaf from tables.table import Table, Cols, Column from tables.array import Array from tables.carray import CArray from tables.earray import EArray from tables.vlarray import VLArray from tables.unimplemented import UnImplemented, Unknown from tables.expression import Expr from tables.tests import print_versions, test # List here only the objects we want to be publicly available __all__ = [ # Exceptions and warnings: 'HDF5ExtError', 'ClosedNodeError', 'ClosedFileError', 'FileModeError', 'NaturalNameWarning', 'NodeError', 'NoSuchNodeError', 'UndoRedoError', 'UndoRedoWarning', 'PerformanceWarning', 'FlavorError', 'FlavorWarning', 'FiltersWarning', 'DataTypeWarning', # Functions: 'is_hdf5_file', 'is_pytables_file', 'which_lib_version', 'copy_file', 'open_file', 'print_versions', 'test', 'split_type', 'restrict_flavors', 'set_blosc_max_threads', 'silence_hdf5_messages', # Helper classes: 'IsDescription', 'Description', 'Filters', 'Cols', 'Column', # Types: 'Enum', # Atom types: 'Atom', 'StringAtom', 'BoolAtom', 'IntAtom', 'UIntAtom', 'Int8Atom', 'UInt8Atom', 'Int16Atom', 'UInt16Atom', 'Int32Atom', 'UInt32Atom', 'Int64Atom', 'UInt64Atom', 'FloatAtom', 'Float32Atom', 'Float64Atom', 'ComplexAtom', 'Complex32Atom', 'Complex64Atom', 'Complex128Atom', 'TimeAtom', 'Time32Atom', 'Time64Atom', 'EnumAtom', 'PseudoAtom', 'ObjectAtom', 'VLStringAtom', 'VLUnicodeAtom', # Column types: 'Col', 'StringCol', 'BoolCol', 'IntCol', 'UIntCol', 'Int8Col', 'UInt8Col', 'Int16Col', 'UInt16Col', 'Int32Col', 'UInt32Col', 'Int64Col', 'UInt64Col', 'FloatCol', 'Float32Col', 'Float64Col', 'ComplexCol', 'Complex32Col', 'Complex64Col', 'Complex128Col', 'TimeCol', 'Time32Col', 'Time64Col', 'EnumCol', # Node classes: 'Node', 'Group', 'Leaf', 'Table', 'Array', 'CArray', 'EArray', 'VLArray', 'UnImplemented', 'Unknown', # The File class: 'File', # Expr class 'Expr', # # Pending deprecation!!! # 'isHDF5File', 'isPyTablesFile', 'whichLibVersion', 'copyFile', 'openFile', 'print_versions', 'test', 'split_type', 'restrict_flavors', 'setBloscMaxThreads', 'silenceHDF5Messages', ] if 'Float16Atom' in locals(): # float16 is new in numpy 1.6.0 __all__.extend(('Float16Atom', 'Float16Col')) from tables.utilsextension import _broken_hdf5_long_double if not _broken_hdf5_long_double(): if 'Float96Atom' in locals(): __all__.extend(('Float96Atom', 'Float96Col')) __all__.extend(('Complex192Atom', 'Complex192Col')) # XXX check if 'Float128Atom' in locals(): __all__.extend(('Float128Atom', 'Float128Col')) __all__.extend(('Complex256Atom', 'Complex256Col')) # XXX check else: from tables import atom as _atom from tables import description as _description try: del _atom.Float96Atom, _atom.Complex192Col del _description.Float96Col, _description.Complex192Col _atom.all_types.discard('complex192') _atom.ComplexAtom._isizes.remove(24) except AttributeError: try: del _atom.Float128Atom, _atom.Complex256Atom del _description.Float128Col, _description.Complex256Col _atom.all_types.discard('complex256') _atom.ComplexAtom._isizes.remove(32) except AttributeError: pass del _atom, _description del _broken_hdf5_long_double PyTables-v.3.1.1/tables/_comp_bzip2.pyx000066400000000000000000000007261231437614300177330ustar00rootroot00000000000000# -*- coding: utf-8 -*- import sys from libc.stdlib cimport free cdef extern from "H5Zbzip2.h": int register_bzip2(char **, char **) def register_(): cdef char *version cdef char *date if not register_bzip2(&version, &date): return None compinfo = (version, date) free(version) free(date) if sys.version_info[0] > 2: return compinfo[0].decode('ascii'), compinfo[1].decode('ascii') else: return compinfo PyTables-v.3.1.1/tables/_comp_lzo.pyx000066400000000000000000000007201231437614300175030ustar00rootroot00000000000000# -*- coding: utf-8 -*- import sys from libc.stdlib cimport free cdef extern from "H5Zlzo.h": int register_lzo(char **, char **) def register_(): cdef char *version cdef char *date if not register_lzo(&version, &date): return None compinfo = (version, date) free(version) free(date) if sys.version_info[0] > 2: return compinfo[0].decode('ascii'), compinfo[1].decode('ascii') else: return compinfo PyTables-v.3.1.1/tables/_past.py000066400000000000000000000543141231437614300164500ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: April 9, 2013 # Author: Anthony Scopatz - scopatz@gmail.com # # $Id$ # ######################################################################## """A module with no PyTables dependencies that helps with deprecation warnings.""" from inspect import getmembers, ismethod, isfunction from warnings import warn def previous_api(obj): """A decorator-like function for dealing with deprecations.""" if not (ismethod(obj) or isfunction(obj)): # punt if not a function or method return obj for key, value in getmembers(obj): if key == '__name__': newname = value break oldname = new2oldnames[newname] warnmsg = ("{0}() is pending deprecation, use {1}() instead. " "You may use the pt2to3 tool to update your source code.") warnmsg = warnmsg.format(oldname, newname) def oldfunc(*args, **kwargs): warn(warnmsg, DeprecationWarning, stacklevel=2) return obj(*args, **kwargs) oldfunc.__doc__ = ( obj.__doc__ or '') + "\n\n.. warning::\n\n " + warnmsg + "\n" return oldfunc def previous_api_property(newname): oldname = new2oldnames[newname] warnmsg = ("{0} is pending deprecation, use {1} instead. " "You may use the pt2to3 tool to update your source code.") warnmsg = warnmsg.format(oldname, newname) def _getter(self): warn(warnmsg, DeprecationWarning, stacklevel=1) return getattr(self, newname) def _setter(self, value): warn(warnmsg, DeprecationWarning, stacklevel=1) return setattr(self, newname, value) _getter.__name__ = _setter.__name__ = oldname doc = '.. deprecated:: 3.0\n\n' + warnmsg return property(_getter, _setter, None, doc=doc) # old name, new name old2newnames = dict([ # from __init__.py ('hdf5Version', 'hdf5_version'), # data # from array.py ('parentNode', 'parentnode'), # kwarg ('getEnum', 'get_enum'), ('_initLoop', '_init_loop'), ('_fancySelection', '_fancy_selection'), ('_checkShape', '_check_shape'), ('_readSlice', '_read_slice'), ('_readCoords', '_read_coords'), ('_readSelection', '_read_selection'), ('_writeSlice', '_write_slice'), ('_writeCoords', '_write_coords'), ('_writeSelection', '_write_selection'), ('_g_copyWithStats', '_g_copy_with_stats'), ('_c_classId', '_c_classid'), # attr # from atom.py ('_checkBase', '_checkbase'), # from attributeset.py ('newSet', 'newset'), # kwarg ('copyClass', 'copyclass'), # kwarg ('_g_updateNodeLocation', '_g_update_node_location'), ('_g_logAdd', '_g_log_add'), ('_g_delAndLog', '_g_del_and_log'), ('_v__nodeFile', '_v__nodefile'), # attr (private) ('_v__nodePath', '_v__nodepath'), # attr (private) # from carray.py #('parentNode', 'parentnode'), # kwarg # from description.py ('_g_setNestedNamesDescr', '_g_set_nested_names_descr'), ('_g_setPathNames', '_g_set_path_names'), ('_v_colObjects', '_v_colobjects'), # attr ('_v_nestedFormats', '_v_nested_formats'), # attr ('_v_nestedNames', '_v_nested_names'), # attr ('_v_nestedDescr', '_v_nested_descr'), # attr ('getColsInOrder', 'get_cols_in_order'), ('joinPaths', 'join_paths'), ('metaIsDescription', 'MetaIsDescription'), # from earray.py #('parentNode', 'parentnode'), # kwarg ('_checkShapeAppend', '_check_shape_append'), # from expression.py ('_exprvarsCache', '_exprvars_cache'), # attr (private) ('_requiredExprVars', '_required_expr_vars'), ('setInputsRange', 'set_inputs_range'), ('setOutput', 'set_output'), ('setOutputRange', 'set_output_range'), # from file.py ('_opToCode', '_op_to_code'), # data (private) ('_codeToOp', '_code_to_op'), # data (private) ('_transVersion', '_trans_version'), # data (private) ('_transGroupParent', '_trans_group_parent'), # data (private) ('_transGroupName', '_trans_group_name'), # data (private) ('_transGroupPath', '_trans_group_path'), # data (private) ('_actionLogParent', '_action_log_parent'), # data (private) ('_actionLogName', '_action_log_name'), # data (private) ('_actionLogPath', '_action_log_path'), # data (private) ('_transParent', '_trans_parent'), # data (private) ('_transName', '_trans_name'), # data (private) ('_transPath', '_trans_path'), # data (private) ('_shadowParent', '_shadow_parent'), # data (private) ('_shadowName', '_shadow_name'), # data (private) ('_shadowPath', '_shadow_path'), # data (private) ('copyFile', 'copy_file'), ('openFile', 'open_file'), ('_getValueFromContainer', '_get_value_from_container'), ('__getRootGroup', '__get_root_group'), ('rootUEP', 'root_uep'), # attr ('_getOrCreatePath', '_get_or_create_path'), ('_createPath', '_create_path'), ('createGroup', 'create_group'), ('createTable', 'create_table'), ('createArray', 'create_array'), ('createCArray', 'create_carray'), ('createEArray', 'create_earray'), ('createVLArray', 'create_vlarray'), ('createHardLink', 'create_hard_link'), ('createSoftLink', 'create_soft_link'), ('createExternalLink', 'create_external_link'), ('_getNode', '_get_node'), ('getNode', 'get_node'), ('isVisibleNode', 'is_visible_node'), ('renameNode', 'rename_node'), ('moveNode', 'move_node'), ('copyNode', 'copy_node'), ('removeNode', 'remove_node'), ('getNodeAttr', 'get_node_attr'), ('setNodeAttr', 'set_node_attr'), ('delNodeAttr', 'del_node_attr'), ('copyNodeAttrs', 'copy_node_attrs'), ('copyChildren', 'copy_children'), ('listNodes', 'list_nodes'), ('iterNodes', 'iter_nodes'), ('walkNodes', 'walk_nodes'), ('walkGroups', 'walk_groups'), ('_checkOpen', '_check_open'), ('_isWritable', '_iswritable'), ('_checkWritable', '_check_writable'), ('_checkGroup', '_check_group'), ('isUndoEnabled', 'is_undo_enabled'), ('_checkUndoEnabled', '_check_undo_enabled'), ('_createTransactionGroup', '_create_transaction_group'), ('_createTransaction', '_create_transaction'), ('_createMark', '_create_mark'), ('enableUndo', 'enable_undo'), ('disableUndo', 'disable_undo'), ('_getMarkID', '_get_mark_id'), ('_getFinalAction', '_get_final_action'), ('getCurrentMark', 'get_current_mark'), ('_updateNodeLocations', '_update_node_locations'), # from group.py #('parentNode', 'parentnode'), # kwarg #('ptFile', 'ptfile'), # kwarg ('_getValueFromContainer', '_get_value_from_container'), ('_g_postInitHook', '_g_post_init_hook'), ('_g_getChildGroupClass', '_g_get_child_group_class'), ('_g_getChildLeafClass', '_g_get_child_leaf_class'), ('_g_addChildrenNames', '_g_add_children_names'), ('_g_checkHasChild', '_g_check_has_child'), ('_f_walkNodes', '_f_walknodes'), ('_g_widthWarning', '_g_width_warning'), ('_g_refNode', '_g_refnode'), ('_g_unrefNode', '_g_unrefnode'), ('_g_copyChildren', '_g_copy_children'), ('_f_getChild', '_f_get_child'), ('_f_listNodes', '_f_list_nodes'), ('_f_iterNodes', '_f_iter_nodes'), ('_f_walkGroups', '_f_walk_groups'), ('_g_closeDescendents', '_g_close_descendents'), ('_f_copyChildren', '_f_copy_children'), ('_v_maxGroupWidth', '_v_max_group_width'), # attr ('_v_objectID', '_v_objectid'), # attr ('_g_loadChild', '_g_load_child'), ('childName', 'childname'), # ??? ('_c_shadowNameRE', '_c_shadow_name_re'), # attr (private) # from hdf5extension.p{yx,xd} ('hdf5Extension', 'hdf5extension'), ('_getFileId', '_get_file_id'), ('_flushFile', '_flush_file'), ('_closeFile', '_close_file'), ('_g_listAttr', '_g_list_attr'), ('_g_setAttr', '_g_setattr'), ('_g_getAttr', '_g_getattr'), ('_g_listGroup', '_g_list_group'), ('_g_getGChildAttr', '_g_get_gchild_attr'), ('_g_getLChildAttr', '_g_get_lchild_attr'), ('_g_flushGroup', '_g_flush_group'), ('_g_closeGroup', '_g_close_group'), ('_g_moveNode', '_g_move_node'), ('_convertTime64', '_convert_time64'), ('_createArray', '_create_array'), ('_createCArray', '_create_carray'), ('_openArray', '_open_array'), ('_readArray', '_read_array'), ('_g_readSlice', '_g_read_slice'), ('_g_readCoords', '_g_read_coords'), ('_g_readSelection', '_g_read_selection'), ('_g_writeSlice', '_g_write_slice'), ('_g_writeCoords', '_g_write_coords'), ('_g_writeSelection', '_g_write_selection'), # from idxutils.py ('calcChunksize', 'calc_chunksize'), ('infinityF', 'infinityf'), # data ('infinityMap', 'infinitymap'), # data ('infType', 'inftype'), ('StringNextAfter', 'string_next_after'), ('IntTypeNextAfter', 'int_type_next_after'), ('BoolTypeNextAfter', 'bool_type_next_after'), # from index.py #('parentNode', 'parentnode'), # kwarg ('defaultAutoIndex', 'default_auto_index'), # data ('defaultIndexFilters', 'default_index_filters'), # data ('_tableColumnPathnameOfIndex', '_table_column_pathname_of_index'), ('_is_CSI', '_is_csi'), ('is_CSI', 'is_csi'), # property ('appendLastRow', 'append_last_row'), ('read_sliceLR', 'read_slice_lr'), ('readSorted', 'read_sorted'), ('readIndices', 'read_indices'), ('_processRange', '_process_range'), ('searchLastRow', 'search_last_row'), ('getLookupRange', 'get_lookup_range'), ('_g_checkName', '_g_check_name'), # from indexes.py #('parentNode', 'parentnode'), # kwarg ('_searchBin', '_search_bin'), # from indexesextension ('indexesExtension', 'indexesextension'), ('initRead', 'initread'), ('readSlice', 'read_slice'), ('_readIndexSlice', '_read_index_slice'), ('_initSortedSlice', '_init_sorted_slice'), ('_g_readSortedSlice', '_g_read_sorted_slice'), ('_readSortedSlice', '_read_sorted_slice'), ('getLRUbounds', 'get_lru_bounds'), ('getLRUsorted', 'get_lru_sorted'), ('_searchBinNA_b', '_search_bin_na_b'), ('_searchBinNA_ub', '_search_bin_na_ub'), ('_searchBinNA_s', '_search_bin_na_s'), ('_searchBinNA_us', '_search_bin_na_us'), ('_searchBinNA_i', '_search_bin_na_i'), ('_searchBinNA_ui', '_search_bin_na_ui'), ('_searchBinNA_ll', '_search_bin_na_ll'), ('_searchBinNA_ull', '_search_bin_na_ull'), ('_searchBinNA_e', '_search_bin_na_e'), ('_searchBinNA_f', '_search_bin_na_f'), ('_searchBinNA_d', '_search_bin_na_d'), ('_searchBinNA_g', '_search_bin_na_g'), # from leaf.py #('parentNode', 'parentnode'), # kwarg ('objectID', 'object_id'), # property ('_processRangeRead', '_process_range_read'), ('_pointSelection', '_point_selection'), ('isVisible', 'isvisible'), ('getAttr', 'get_attr'), ('setAttr', 'set_attr'), ('delAttr', 'del_attr'), # from link.py #('parentNode', 'parentnode'), # kwarg ('_g_getLinkClass', '_g_get_link_class'), # from linkextension ('linkExtension', 'linkextension'), ('_getLinkClass', '_get_link_class'), ('_g_createHardLink', '_g_create_hard_link'), # from lrucacheextension ('lrucacheExtension', 'lrucacheextension'), # from misc/enum.py ('_checkAndSetPair', '_check_and_set_pair'), ('_getContainer', '_get_container'), # from misc/proxydict.py ('containerRef', 'containerref'), # attr # from node.py #('parentNode', 'parentnode'), # kwarg ('_g_logCreate', '_g_log_create'), ('_g_preKillHook', '_g_pre_kill_hook'), ('_g_checkOpen', '_g_check_open'), ('_g_setLocation', '_g_set_location'), ('_g_updateLocation', '_g_update_location'), ('_g_delLocation', '_g_del_location'), ('_g_updateDependent', '_g_update_dependent'), ('_g_removeAndLog', '_g_remove_and_log'), ('_g_logMove', '_g_log_move'), ('oldPathname', 'oldpathname'), # ?? ('_g_copyAsChild', '_g_copy_as_child'), ('_f_isVisible', '_f_isvisible'), ('_g_checkGroup', '_g_check_group'), ('_g_checkNotContains', '_g_check_not_contains'), ('_g_maybeRemove', '_g_maybe_remove'), ('_f_getAttr', '_f_getattr'), ('_f_setAttr', '_f_setattr'), ('_f_delAttr', '_f_delattr'), ('_v_maxTreeDepth', '_v_maxtreedepth'), # attr # from nodes/filenode.py ('newNode', 'new_node'), ('openNode', 'open_node'), ('_lineChunkSize', '_line_chunksize'), # attr (private) ('_lineSeparator', '_line_separator'), # attr (private) #('getLineSeparator', 'get_line_separator'), # dropped #('setLineSeparator', 'set_line_separator'), # dropped #('delLineSeparator', 'del_line_separator'), # dropped #('lineSeparator', 'line_separator'), # property -- dropped ('_notReadableError', '_not_readable_error'), ('_appendZeros', '_append_zeros'), ('getAttrs', '_get_attrs'), ('setAttrs', '_set_attrs'), ('delAttrs', '_del_attrs'), ('_setAttributes', '_set_attributes'), ('_checkAttributes', '_check_attributes'), ('_checkNotClosed', '_check_not_closed'), ('__allowedInitKwArgs', '__allowed_init_kwargs'), # attr (private) ('_byteShape', '_byte_shape'), # attr (private) ('_sizeToShape', '_size_to_shape'), # attr (private) ('_vType', '_vtype'), # attr (private) ('_vShape', '_vshape'), # attr (private) # from path.py ('parentPath', 'parentpath'), # kwarg ('_pythonIdRE', '_python_id_re'), # attr (private) ('_reservedIdRE', '_reserved_id_re'), # attr (private) ('_hiddenNameRE', '_hidden_name_re'), # attr (private) ('_hiddenPathRE', '_hidden_path_re'), # attr (private) ('checkNameValidity', 'check_name_validity'), ('joinPath', 'join_path'), ('splitPath', 'split_path'), ('isVisibleName', 'isvisiblename'), ('isVisiblePath', 'isvisiblepath'), # from registry.py ('className', 'classname'), # kwarg ('classNameDict', 'class_name_dict'), # data ('classIdDict', 'class_id_dict'), # data ('getClassByName', 'get_class_by_name'), # from scripts/ptdump.py ('dumpLeaf', 'dump_leaf'), ('dumpGroup', 'dump_group'), # from scripts/ptrepack.py ('newdstGroup', 'newdst_group'), ('recreateIndexes', 'recreate_indexes'), ('copyLeaf', 'copy_leaf'), # from table.py #('parentNode', 'parentnode'), # kwarg ('_nxTypeFromNPType', '_nxtype_from_nptype'), # data (private) ('_npSizeType', '_npsizetype'), # data (private) ('_indexNameOf', '_index_name_of'), ('_indexPathnameOf', '_index_pathname_of'), ('_indexPathnameOfColumn', '_index_pathname_of_column'), ('_indexNameOf_', '_index_name_of_'), ('_indexPathnameOf_', '_index_pathname_of_'), ('_indexPathnameOfColumn_', '_index_pathname_of_column_'), ('_table__setautoIndex', '_table__setautoindex'), ('_table__getautoIndex', '_table__getautoindex'), ('_table__autoIndex', '_table__autoindex'), # data (private) ('_table__whereIndexed', '_table__where_indexed'), ('createIndexesTable', 'create_indexes_table'), ('createIndexesDescr', 'create_indexes_descr'), ('_column__createIndex', '_column__create_index'), ('_autoIndex', '_autoindex'), # attr ('autoIndex', 'autoindex'), # attr ('_useIndex', '_use_index'), ('_whereCondition', '_where_condition'), # attr (private) ('_conditionCache', '_condition_cache'), # attr (private) #('_exprvarsCache', '_exprvars_cache'), ('_enabledIndexingInQueries', '_enabled_indexing_in_queries'), # attr (private) ('_emptyArrayCache', '_empty_array_cache'), # attr (private) ('_getTypeColNames', '_get_type_col_names'), ('_getEnumMap', '_get_enum_map'), ('_cacheDescriptionData', '_cache_description_data'), ('_getColumnInstance', '_get_column_instance'), ('_checkColumn', '_check_column'), ('_disableIndexingInQueries', '_disable_indexing_in_queries'), ('_enableIndexingInQueries', '_enable_indexing_in_queries'), #('_requiredExprVars', '_required_expr_vars'), ('_getConditionKey', '_get_condition_key'), ('_compileCondition', '_compile_condition'), ('willQueryUseIndexing', 'will_query_use_indexing'), ('readWhere', 'read_where'), ('whereAppend', 'append_where'), ('getWhereList', 'get_where_list'), ('_check_sortby_CSI', '_check_sortby_csi'), ('_readCoordinates', '_read_coordinates'), ('readCoordinates', 'read_coordinates'), ('_saveBufferedRows', '_save_buffered_rows'), ('modifyCoordinates', 'modify_coordinates'), ('modifyRows', 'modify_rows'), ('modifyColumn', 'modify_column'), ('modifyColumns', 'modify_columns'), ('flushRowsToIndex', 'flush_rows_to_index'), ('_addRowsToIndex', '_add_rows_to_index'), ('removeRows', 'remove_rows'), ('_setColumnIndexing', '_set_column_indexing'), ('_markColumnsAsDirty', '_mark_columns_as_dirty'), ('_reIndex', '_reindex'), ('_doReIndex', '_do_reindex'), ('reIndex', 'reindex'), ('reIndexDirty', 'reindex_dirty'), ('_g_copyRows', '_g_copy_rows'), ('_g_copyRows_optim', '_g_copy_rows_optim'), ('_g_propIndexes', '_g_prop_indexes'), ('_g_updateTableLocation', '_g_update_table_location'), ('_tableFile', '_table_file'), # attr (private) ('_tablePath', '_table_path'), # attr (private) ('createIndex', 'create_index'), ('createCSIndex', 'create_csindex'), ('removeIndex', 'remove_index'), # from tableextension ('tableExtension', 'tableextension'), ('getNestedFieldCache', 'get_nested_field_cache'), ('getNestedType', 'get_nested_type'), ('_createTable', '_create_table'), ('_getInfo', '_get_info'), ('indexChunk', 'indexchunk'), # attr ('indexValid', 'indexvalid'), # attr ('indexValues', 'indexvalues'), # attr ('bufcoordsData', 'bufcoords_data'), # attr ('indexValuesData', 'index_values_data'), # attr ('chunkmapData', 'chunkmap_data'), # attr ('indexValidData', 'index_valid_data'), # attr ('whereCond', 'wherecond'), # attr ('iterseqMaxElements', 'iterseq_max_elements'), # attr ('IObuf', 'iobuf'), # attr ('IObufcpy', 'iobufcpy'), # attr ('_convertTime64_', '_convert_time64_'), ('_convertTypes', '_convert_types'), ('_newBuffer', '_new_buffer'), ('__next__inKernel', '__next__inkernel'), ('_fillCol', '_fill_col'), ('_flushBufferedRows', '_flush_buffered_rows'), ('_getUnsavedNrows', '_get_unsaved_nrows'), ('_flushModRows', '_flush_mod_rows'), # from undoredo.py ('moveToShadow', 'move_to_shadow'), ('moveFromShadow', 'move_from_shadow'), ('undoCreate', 'undo_create'), ('redoCreate', 'redo_create'), ('undoRemove', 'undo_remove'), ('redoRemove', 'redo_remove'), ('undoMove', 'undo_move'), ('redoMove', 'redo_move'), ('attrToShadow', 'attr_to_shadow'), ('attrFromShadow', 'attr_from_shadow'), ('undoAddAttr', 'undo_add_attr'), ('redoAddAttr', 'redo_add_attr'), ('undoDelAttr', 'undo_del_attr'), ('redoDelAttr', 'redo_del_attr'), # from utils.py ('convertToNPAtom', 'convert_to_np_atom'), ('convertToNPAtom2', 'convert_to_np_atom2'), ('checkFileAccess', 'check_file_access'), ('logInstanceCreation', 'log_instance_creation'), ('fetchLoggedInstances', 'fetch_logged_instances'), ('countLoggedInstances', 'count_logged_instances'), ('listLoggedInstances', 'list_logged_instances'), ('dumpLoggedInstances', 'dump_logged_instances'), ('detectNumberOfCores', 'detect_number_of_cores'), # from utilsextension ('utilsExtension', 'utilsextension'), ('PTTypeToHDF5', 'pttype_to_hdf5'), # data ('PTSpecialKinds', 'pt_special_kinds'), # data ('NPExtPrefixesToPTKinds', 'npext_prefixes_to_ptkinds'), # data ('HDF5ClassToString', 'hdf5_class_to_string'), # data ('setBloscMaxThreads', 'set_blosc_max_threads'), ('silenceHDF5Messages', 'silence_hdf5_messages'), ('isHDF5File', 'is_hdf5_file'), ('isPyTablesFile', 'is_pytables_file'), ('getHDF5Version', 'get_hdf5_version'), ('getPyTablesVersion', 'get_pytables_version'), ('whichLibVersion', 'which_lib_version'), ('whichClass', 'which_class'), ('getNestedField', 'get_nested_field'), ('getIndices', 'get_indices'), ('getFilters', 'get_filters'), ('getTypeEnum', 'get_type_enum'), ('enumFromHDF5', 'enum_from_hdf5'), ('enumToHDF5', 'enum_to_hdf5'), ('AtomToHDF5Type', 'atom_to_hdf5_type'), ('loadEnum', 'load_enum'), ('HDF5ToNPNestedType', 'hdf5_to_np_nested_type'), ('HDF5ToNPExtType', 'hdf5_to_np_ext_type'), ('AtomFromHDF5Type', 'atom_from_hdf5_type'), ('createNestedType', 'create_nested_type'), # from unimlemented.py ('_openUnImplemented', '_open_unimplemented'), # from vlarray.py #('parentNode', 'parentnode'), # kwarg #('expectedsizeinMB', 'expected_mb'), # --> expectedrows #('_v_expectedsizeinMB', '_v_expected_mb'), # --> expectedrows ]) new2oldnames = dict([(v, k) for k, v in old2newnames.iteritems()]) PyTables-v.3.1.1/tables/array.py000066400000000000000000001067071231437614300164640ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: October 10, 2002 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Here is defined the Array class.""" import sys import numpy from tables import hdf5extension from tables.filters import Filters from tables.flavor import flavor_of, array_as_internal, internal_to_flavor from tables.utils import (is_idx, convert_to_np_atom2, SizeType, lazyattr, byteorders, quantize) from tables.leaf import Leaf from tables._past import previous_api, previous_api_property # default version for ARRAY objects # obversion = "1.0" # initial version # obversion = "2.0" # Added an optional EXTDIM attribute # obversion = "2.1" # Added support for complex datatypes # obversion = "2.2" # This adds support for time datatypes. # obversion = "2.3" # This adds support for enumerated datatypes. obversion = "2.4" # Numeric and numarray flavors are gone. class Array(hdf5extension.Array, Leaf): """This class represents homogeneous datasets in an HDF5 file. This class provides methods to write or read data to or from array objects in the file. This class does not allow you neither to enlarge nor compress the datasets on disk; use the EArray class (see :ref:`EArrayClassDescr`) if you want enlargeable dataset support or compression features, or CArray (see :ref:`CArrayClassDescr`) if you just want compression. An interesting property of the Array class is that it remembers the *flavor* of the object that has been saved so that if you saved, for example, a list, you will get a list during readings afterwards; if you saved a NumPy array, you will get a NumPy object, and so forth. Note that this class inherits all the public attributes and methods that Leaf (see :ref:`LeafClassDescr`) already provides. However, as Array instances have no internal I/O buffers, it is not necessary to use the flush() method they inherit from Leaf in order to save their internal state to disk. When a writing method call returns, all the data is already on disk. Parameters ---------- parentnode The parent :class:`Group` object. .. versionchanged:: 3.0 Renamed from *parentNode* to *parentnode* name : str The name of this node in its parent group. obj The array or scalar to be saved. Accepted types are NumPy arrays and scalars as well as native Python sequences and scalars, provided that values are regular (i.e. they are not like ``[[1,2],2]``) and homogeneous (i.e. all the elements are of the same type). .. versionchanged:: 3.0 Renamed form *object* into *obj*. title A description for this node (it sets the ``TITLE`` HDF5 attribute on disk). byteorder The byteorder of the data *on disk*, specified as 'little' or 'big'. If this is not specified, the byteorder is that of the given `object`. """ # Class identifier. _c_classid = 'ARRAY' _c_classId = previous_api_property('_c_classid') _v_objectId = previous_api_property('_v_objectid') # Lazy read-only attributes # ````````````````````````` @lazyattr def dtype(self): """The NumPy ``dtype`` that most closely matches this array.""" return self.atom.dtype # Properties # ~~~~~~~~~~ def _getnrows(self): if self.shape == (): return SizeType(1) # scalar case else: return self.shape[self.maindim] nrows = property( _getnrows, None, None, "The number of rows in the array.") def _getrowsize(self): maindim = self.maindim rowsize = self.atom.size for i, dim in enumerate(self.shape): if i != maindim: rowsize *= dim return rowsize rowsize = property( _getrowsize, None, None, "The size of the rows in bytes in dimensions orthogonal to *maindim*.") size_in_memory = property( lambda self: self.nrows * self.rowsize, None, None, """The size of this array's data in bytes when it is fully loaded into memory.""") # Other methods # ~~~~~~~~~~~~~ def __init__(self, parentnode, name, obj=None, title="", byteorder=None, _log=True, _atom=None): self._v_version = None """The object version of this array.""" self._v_new = new = obj is not None """Is this the first time the node has been created?""" self._v_new_title = title """New title for this node.""" self._obj = obj """The object to be stored in the array. It can be any of numpy, list, tuple, string, integer of floating point types, provided that they are regular (i.e. they are not like ``[[1, 2], 2]``). .. versionchanged:: 3.0 Renamed form *_object* into *_obj*. """ self._v_convert = True """Whether the ``Array`` object must be converted or not.""" # Miscellaneous iteration rubbish. self._start = None """Starting row for the current iteration.""" self._stop = None """Stopping row for the current iteration.""" self._step = None """Step size for the current iteration.""" self._nrowsread = None """Number of rows read up to the current state of iteration.""" self._startb = None """Starting row for current buffer.""" self._stopb = None """Stopping row for current buffer. """ self._row = None """Current row in iterators (sentinel).""" self._init = False """Whether we are in the middle of an iteration or not (sentinel).""" self.listarr = None """Current buffer in iterators.""" # Documented (*public*) attributes. self.atom = _atom """An Atom (see :ref:`AtomClassDescr`) instance representing the *type* and *shape* of the atomic objects to be saved. """ self.shape = None """The shape of the stored array.""" self.nrow = None """On iterators, this is the index of the current row.""" self.extdim = -1 # ordinary arrays are not enlargeable """The index of the enlargeable dimension.""" # Ordinary arrays have no filters: leaf is created with default ones. super(Array, self).__init__(parentnode, name, new, Filters(), byteorder, _log) def _g_create(self): """Save a new array in file.""" self._v_version = obversion try: # `Leaf._g_post_init_hook()` should be setting the flavor on disk. self._flavor = flavor = flavor_of(self._obj) nparr = array_as_internal(self._obj, flavor) except: # XXX # Problems converting data. Close the node and re-raise exception. self.close(flush=0) raise # Raise an error in case of unsupported object if nparr.dtype.kind in ['V', 'U', 'O']: # in void, unicode, object raise TypeError("Array objects cannot currently deal with void, " "unicode or object arrays") # Decrease the number of references to the object self._obj = None # Fix the byteorder of data nparr = self._g_fix_byteorder_data(nparr, nparr.dtype.byteorder) # Create the array on-disk try: # ``self._v_objectid`` needs to be set because would be # needed for setting attributes in some descendants later # on (self._v_objectid, self.shape, self.atom) = self._create_array( nparr, self._v_new_title, self.atom) except: # XXX # Problems creating the Array on disk. Close node and re-raise. self.close(flush=0) raise # Compute the optimal buffer size self.nrowsinbuf = self._calc_nrowsinbuf() # Arrays don't have chunkshapes (so, set it to None) self._v_chunkshape = None return self._v_objectid def _g_open(self): """Get the metadata info for an array in file.""" (oid, self.atom, self.shape, self._v_chunkshape) = self._open_array() self.nrowsinbuf = self._calc_nrowsinbuf() return oid def get_enum(self): """Get the enumerated type associated with this array. If this array is of an enumerated type, the corresponding Enum instance (see :ref:`EnumClassDescr`) is returned. If it is not of an enumerated type, a TypeError is raised. """ if self.atom.kind != 'enum': raise TypeError("array ``%s`` is not of an enumerated type" % self._v_pathname) return self.atom.enum getEnum = previous_api(get_enum) def iterrows(self, start=None, stop=None, step=None): """Iterate over the rows of the array. This method returns an iterator yielding an object of the current flavor for each selected row in the array. The returned rows are taken from the *main dimension*. If a range is not supplied, *all the rows* in the array are iterated upon - you can also use the :meth:`Array.__iter__` special method for that purpose. If you only want to iterate over a given *range of rows* in the array, you may use the start, stop and step parameters. Examples -------- :: result = [row for row in arrayInstance.iterrows(step=4)] .. versionchanged:: 3.0 If the *start* parameter is provided and *stop* is None then the array is iterated from *start* to the last line. In PyTables < 3.0 only one element was returned. """ try: (self._start, self._stop, self._step) = self._process_range( start, stop, step) except IndexError: # If problems with indexes, silently return the null tuple return () self._init_loop() return self def __iter__(self): """Iterate over the rows of the array. This is equivalent to calling :meth:`Array.iterrows` with default arguments, i.e. it iterates over *all the rows* in the array. Examples -------- :: result = [row[2] for row in array] Which is equivalent to:: result = [row[2] for row in array.iterrows()] """ if not self._init: # If the iterator is called directly, assign default variables self._start = 0 self._stop = self.nrows self._step = 1 # and initialize the loop self._init_loop() return self def _init_loop(self): """Initialization for the __iter__ iterator.""" self._nrowsread = self._start self._startb = self._start self._row = -1 # Sentinel self._init = True # Sentinel self.nrow = SizeType(self._start - self._step) # row number _initLoop = previous_api(_init_loop) def next(self): """Get the next element of the array during an iteration. The element is returned as an object of the current flavor. """ # this could probably be sped up for long iterations by reusing the # listarr buffer if self._nrowsread >= self._stop: self._init = False self.listarr = None # fixes issue #308 raise StopIteration # end of iteration else: # Read a chunk of rows if self._row + 1 >= self.nrowsinbuf or self._row < 0: self._stopb = self._startb + self._step * self.nrowsinbuf # Protection for reading more elements than needed if self._stopb > self._stop: self._stopb = self._stop listarr = self._read(self._startb, self._stopb, self._step) # Swap the axes to easy the return of elements if self.extdim > 0: listarr = listarr.swapaxes(self.extdim, 0) self.listarr = internal_to_flavor(listarr, self.flavor) self._row = -1 self._startb = self._stopb self._row += 1 self.nrow += self._step self._nrowsread += self._step # Fixes bug #968132 # if self.listarr.shape: if self.shape: return self.listarr[self._row] else: return self.listarr # Scalar case def _interpret_indexing(self, keys): """Internal routine used by __getitem__ and __setitem__""" maxlen = len(self.shape) shape = (maxlen,) startl = numpy.empty(shape=shape, dtype=SizeType) stopl = numpy.empty(shape=shape, dtype=SizeType) stepl = numpy.empty(shape=shape, dtype=SizeType) stop_None = numpy.zeros(shape=shape, dtype=SizeType) if not isinstance(keys, tuple): keys = (keys,) nkeys = len(keys) dim = 0 # Here is some problem when dealing with [...,...] params # but this is a bit weird way to pass parameters anyway for key in keys: ellipsis = 0 # Sentinel if isinstance(key, type(Ellipsis)): ellipsis = 1 for diml in xrange(dim, len(self.shape) - (nkeys - dim) + 1): startl[dim] = 0 stopl[dim] = self.shape[diml] stepl[dim] = 1 dim += 1 elif dim >= maxlen: raise IndexError("Too many indices for object '%s'" % self._v_pathname) elif is_idx(key): # Protection for index out of range if key >= self.shape[dim]: raise IndexError("Index out of range") if key < 0: # To support negative values (Fixes bug #968149) key += self.shape[dim] start, stop, step = self._process_range( key, key + 1, 1, dim=dim) stop_None[dim] = 1 elif isinstance(key, slice): start, stop, step = self._process_range( key.start, key.stop, key.step, dim=dim) else: raise TypeError("Non-valid index or slice: %s" % key) if not ellipsis: startl[dim] = start stopl[dim] = stop stepl[dim] = step dim += 1 # Complete the other dimensions, if needed if dim < len(self.shape): for diml in xrange(dim, len(self.shape)): startl[dim] = 0 stopl[dim] = self.shape[diml] stepl[dim] = 1 dim += 1 # Compute the shape for the container properly. Fixes #1288792 shape = [] for dim in xrange(len(self.shape)): # The negative division operates differently with python scalars # and numpy scalars (which are similar to C conventions). See: # http://www.python.org/doc/faq/programming.html#why-does-22-10-return-3 # and # http://www.peterbe.com/Integer-division-in-programming-languages # for more info on this issue. # I've finally decided to rely on the len(xrange) function. # F. Alted 2006-09-25 # Switch to `lrange` to allow long ranges (see #99). # use xrange, since it supports large integers as of Python 2.6 # see github #181 new_dim = len(xrange(startl[dim], stopl[dim], stepl[dim])) if not (new_dim == 1 and stop_None[dim]): shape.append(new_dim) return startl, stopl, stepl, shape def _fancy_selection(self, args): """Performs a NumPy-style fancy selection in `self`. Implements advanced NumPy-style selection operations in addition to the standard slice-and-int behavior. Indexing arguments may be ints, slices or lists of indices. Note: This is a backport from the h5py project. """ # Internal functions def validate_number(num, length): """Validate a list member for the given axis length.""" try: num = long(num) except TypeError: raise TypeError("Illegal index: %r" % num) if num > length - 1: raise IndexError("Index out of bounds: %d" % num) def expand_ellipsis(args, rank): """Expand ellipsis objects and fill in missing axes.""" n_el = sum(1 for arg in args if arg is Ellipsis) if n_el > 1: raise IndexError("Only one ellipsis may be used.") elif n_el == 0 and len(args) != rank: args = args + (Ellipsis,) final_args = [] n_args = len(args) for idx, arg in enumerate(args): if arg is Ellipsis: final_args.extend((slice(None),) * (rank - n_args + 1)) else: final_args.append(arg) if len(final_args) > rank: raise IndexError("Too many indices.") return final_args def translate_slice(exp, length): """Given a slice object, return a 3-tuple (start, count, step) This is for for use with the hyperslab selection routines. """ start, stop, step = exp.start, exp.stop, exp.step if start is None: start = 0 else: start = long(start) if stop is None: stop = length else: stop = long(stop) if step is None: step = 1 else: step = long(step) if step < 1: raise IndexError("Step must be >= 1 (got %d)" % step) if stop == start: raise IndexError("Zero-length selections are not allowed") if stop < start: raise IndexError("Reverse-order selections are not allowed") if start < 0: start = length + start if stop < 0: stop = length + stop if not 0 <= start <= (length - 1): raise IndexError( "Start index %s out of range (0-%d)" % (start, length - 1)) if not 1 <= stop <= length: raise IndexError( "Stop index %s out of range (1-%d)" % (stop, length)) count = (stop - start) // step if (stop - start) % step != 0: count += 1 if start + count > length: raise IndexError( "Selection out of bounds (%d; axis has %d)" % (start + count, length)) return start, count, step # Main code for _fancy_selection mshape = [] selection = [] if not isinstance(args, tuple): args = (args,) args = expand_ellipsis(args, len(self.shape)) list_seen = False reorder = None for idx, (exp, length) in enumerate(zip(args, self.shape)): if isinstance(exp, slice): start, count, step = translate_slice(exp, length) selection.append((start, count, step, idx, "AND")) mshape.append(count) else: try: exp = list(exp) except TypeError: exp = [exp] # Handle scalar index as a list of length 1 mshape.append(0) # Keep track of scalar index for NumPy else: mshape.append(len(exp)) if len(exp) == 0: raise IndexError( "Empty selections are not allowed (axis %d)" % idx) elif len(exp) > 1: if list_seen: raise IndexError( "Only one selection list is allowed") else: list_seen = True nexp = numpy.asarray(exp, dtype="i8") # Convert negative values nexp = numpy.where(nexp < 0, length + nexp, nexp) # Check whether the list is ordered or not # (only one unordered list is allowed) if not len(nexp) == len(numpy.unique(nexp)): raise IndexError( "Selection lists cannot have repeated values") neworder = nexp.argsort() if not numpy.alltrue(neworder == numpy.arange(len(exp))): if reorder is not None: raise IndexError( "Only one selection list can be unordered") corrected_idx = sum(1 for x in mshape if x != 0) - 1 reorder = (corrected_idx, neworder) nexp = nexp[neworder] for select_idx in xrange(len(nexp) + 1): # This crazy piece of code performs a list selection # using HDF5 hyperslabs. # For each index, perform a "NOTB" selection on every # portion of *this axis* which falls *outside* the list # selection. For this to work, the input array MUST be # monotonically increasing. if select_idx < len(nexp): validate_number(nexp[select_idx], length) if select_idx == 0: start = 0 count = nexp[0] elif select_idx == len(nexp): start = nexp[-1] + 1 count = length - start else: start = nexp[select_idx - 1] + 1 count = nexp[select_idx] - start if count > 0: selection.append((start, count, 1, idx, "NOTB")) mshape = tuple(x for x in mshape if x != 0) return selection, reorder, mshape _fancySelection = previous_api(_fancy_selection) def __getitem__(self, key): """Get a row, a range of rows or a slice from the array. The set of tokens allowed for the key is the same as that for extended slicing in Python (including the Ellipsis or ... token). The result is an object of the current flavor; its shape depends on the kind of slice used as key and the shape of the array itself. Furthermore, NumPy-style fancy indexing, where a list of indices in a certain axis is specified, is also supported. Note that only one list per selection is supported right now. Finally, NumPy-style point and boolean selections are supported as well. Examples -------- :: array1 = array[4] # simple selection array2 = array[4:1000:2] # slice selection array3 = array[1, ..., ::2, 1:4, 4:] # general slice selection array4 = array[1, [1,5,10], ..., -1] # fancy selection array5 = array[np.where(array[:] > 4)] # point selection array6 = array[array[:] > 4] # boolean selection """ self._g_check_open() try: # First, try with a regular selection startl, stopl, stepl, shape = self._interpret_indexing(key) arr = self._read_slice(startl, stopl, stepl, shape) except TypeError: # Then, try with a point-wise selection try: coords = self._point_selection(key) arr = self._read_coords(coords) except TypeError: # Finally, try with a fancy selection selection, reorder, shape = self._fancy_selection(key) arr = self._read_selection(selection, reorder, shape) if self.flavor == "numpy" or not self._v_convert: return arr return internal_to_flavor(arr, self.flavor) def __setitem__(self, key, value): """Set a row, a range of rows or a slice in the array. It takes different actions depending on the type of the key parameter: if it is an integer, the corresponding array row is set to value (the value is broadcast when needed). If key is a slice, the row slice determined by it is set to value (as usual, if the slice to be updated exceeds the actual shape of the array, only the values in the existing range are updated). If value is a multidimensional object, then its shape must be compatible with the shape determined by key, otherwise, a ValueError will be raised. Furthermore, NumPy-style fancy indexing, where a list of indices in a certain axis is specified, is also supported. Note that only one list per selection is supported right now. Finally, NumPy-style point and boolean selections are supported as well. Examples -------- :: a1[0] = 333 # assign an integer to a Integer Array row a2[0] = 'b' # assign a string to a string Array row a3[1:4] = 5 # broadcast 5 to slice 1:4 a4[1:4:2] = 'xXx' # broadcast 'xXx' to slice 1:4:2 # General slice update (a5.shape = (4,3,2,8,5,10). a5[1, ..., ::2, 1:4, 4:] = numpy.arange(1728, shape=(4,3,2,4,3,6)) a6[1, [1,5,10], ..., -1] = arr # fancy selection a7[np.where(a6[:] > 4)] = 4 # point selection + broadcast a8[arr > 4] = arr2 # boolean selection """ self._g_check_open() # Create an array compliant with the specified slice nparr = convert_to_np_atom2(value, self.atom) if nparr.size == 0: return # truncate data if least_significant_digit filter is set # TODO: add the least_significant_digit attribute to the array on disk if (self.filters.least_significant_digit is not None and not numpy.issubdtype(nparr.dtype, int)): nparr = quantize(nparr, self.filters.least_significant_digit) try: startl, stopl, stepl, shape = self._interpret_indexing(key) self._write_slice(startl, stopl, stepl, shape, nparr) except TypeError: # Then, try with a point-wise selection try: coords = self._point_selection(key) self._write_coords(coords, nparr) except TypeError: selection, reorder, shape = self._fancy_selection(key) self._write_selection(selection, reorder, shape, nparr) def _check_shape(self, nparr, slice_shape): """Test that nparr shape is consistent with underlying object. If not, try creating a new nparr object, using broadcasting if necessary. """ if nparr.shape != (slice_shape + self.atom.dtype.shape): # Create an array compliant with the specified shape narr = numpy.empty(shape=slice_shape, dtype=self.atom.dtype) # Assign the value to it. It will raise a ValueError exception # if the objects cannot be broadcast to a single shape. narr[...] = nparr return narr else: return nparr _checkShape = previous_api(_check_shape) def _read_slice(self, startl, stopl, stepl, shape): """Read a slice based on `startl`, `stopl` and `stepl`.""" nparr = numpy.empty(dtype=self.atom.dtype, shape=shape) # Protection against reading empty arrays if 0 not in shape: # Arrays that have non-zero dimensionality self._g_read_slice(startl, stopl, stepl, nparr) # For zero-shaped arrays, return the scalar if nparr.shape == (): nparr = nparr[()] return nparr _readSlice = previous_api(_read_slice) def _read_coords(self, coords): """Read a set of points defined by `coords`.""" nparr = numpy.empty(dtype=self.atom.dtype, shape=len(coords)) if len(coords) > 0: self._g_read_coords(coords, nparr) # For zero-shaped arrays, return the scalar if nparr.shape == (): nparr = nparr[()] return nparr _readCoords = previous_api(_read_coords) def _read_selection(self, selection, reorder, shape): """Read a `selection`. Reorder if necessary. """ # Create the container for the slice nparr = numpy.empty(dtype=self.atom.dtype, shape=shape) # Arrays that have non-zero dimensionality self._g_read_selection(selection, nparr) # For zero-shaped arrays, return the scalar if nparr.shape == (): nparr = nparr[()] elif reorder is not None: # We need to reorder the array idx, neworder = reorder k = [slice(None)] * len(shape) k[idx] = neworder.argsort() # Apparently, a copy is not needed here, but doing it # for symmetry with the `_write_selection()` method. nparr = nparr[k].copy() return nparr _readSelection = previous_api(_read_selection) def _write_slice(self, startl, stopl, stepl, shape, nparr): """Write `nparr` in a slice based on `startl`, `stopl` and `stepl`.""" nparr = self._check_shape(nparr, tuple(shape)) countl = ((stopl - startl - 1) // stepl) + 1 self._g_write_slice(startl, stepl, countl, nparr) _writeSlice = previous_api(_write_slice) def _write_coords(self, coords, nparr): """Write `nparr` values in points defined by `coords` coordinates.""" if len(coords) > 0: nparr = self._check_shape(nparr, (len(coords),)) self._g_write_coords(coords, nparr) _writeCoords = previous_api(_write_coords) def _write_selection(self, selection, reorder, shape, nparr): """Write `nparr` in `selection`. Reorder if necessary. """ nparr = self._check_shape(nparr, tuple(shape)) # Check whether we should reorder the array if reorder is not None: idx, neworder = reorder k = [slice(None)] * len(shape) k[idx] = neworder # For a reason a don't understand well, we need a copy of # the reordered array nparr = nparr[k].copy() self._g_write_selection(selection, nparr) _writeSelection = previous_api(_write_selection) def _read(self, start, stop, step, out=None): """Read the array from disk without slice or flavor processing.""" nrowstoread = len(xrange(start, stop, step)) shape = list(self.shape) if shape: shape[self.maindim] = nrowstoread if out is None: arr = numpy.empty(dtype=self.atom.dtype, shape=shape) else: bytes_required = self.rowsize * nrowstoread # if buffer is too small, it will segfault if bytes_required != out.nbytes: raise ValueError(('output array size invalid, got {0} bytes, ' 'need {1} bytes').format(out.nbytes, bytes_required)) if not out.flags['C_CONTIGUOUS']: raise ValueError('output array not C contiguous') arr = out # Protection against reading empty arrays if 0 not in shape: # Arrays that have non-zero dimensionality self._read_array(start, stop, step, arr) # data is always read in the system byteorder # if the out array's byteorder is different, do a byteswap if (out is not None and byteorders[arr.dtype.byteorder] != sys.byteorder): arr.byteswap(True) return arr def read(self, start=None, stop=None, step=None, out=None): """Get data in the array as an object of the current flavor. The start, stop and step parameters can be used to select only a *range of rows* in the array. Their meanings are the same as in the built-in range() Python function, except that negative values of step are not allowed yet. Moreover, if only start is specified, then stop will be set to start + 1. If you do not specify neither start nor stop, then *all the rows* in the array are selected. The out parameter may be used to specify a NumPy array to receive the output data. Note that the array must have the same size as the data selected with the other parameters. Note that the array's datatype is not checked and no type casting is performed, so if it does not match the datatype on disk, the output will not be correct. Also, this parameter is only valid when the array's flavor is set to 'numpy'. Otherwise, a TypeError will be raised. When data is read from disk in NumPy format, the output will be in the current system's byteorder, regardless of how it is stored on disk. The exception is when an output buffer is supplied, in which case the output will be in the byteorder of that output buffer. .. versionchanged:: 3.0 Added the *out* parameter. """ self._g_check_open() if out is not None and self.flavor != 'numpy': msg = ("Optional 'out' argument may only be supplied if array " "flavor is 'numpy', currently is {0}").format(self.flavor) raise TypeError(msg) (start, stop, step) = self._process_range_read(start, stop, step) arr = self._read(start, stop, step, out) return internal_to_flavor(arr, self.flavor) def _g_copy_with_stats(self, group, name, start, stop, step, title, filters, chunkshape, _log, **kwargs): """Private part of Leaf.copy() for each kind of leaf.""" # Compute the correct indices. (start, stop, step) = self._process_range_read(start, stop, step) # Get the slice of the array # (non-buffered version) if self.shape: arr = self[start:stop:step] else: arr = self[()] # Build the new Array object. Use the _atom reserved keyword # just in case the array is being copied from a native HDF5 # with atomic types different from scalars. # For details, see #275 of trac. object_ = Array(group, name, arr, title=title, _log=_log, _atom=self.atom) nbytes = numpy.prod(self.shape, dtype=SizeType) * self.atom.size return (object_, nbytes) _g_copyWithStats = previous_api(_g_copy_with_stats) def __repr__(self): """This provides more metainfo in addition to standard __str__""" return """%s atom := %r maindim := %r flavor := %r byteorder := %r chunkshape := %r""" % (self, self.atom, self.maindim, self.flavor, self.byteorder, self.chunkshape) class ImageArray(Array): """Array containing an image. This class has no additional behaviour or functionality compared to that of an ordinary array. It simply enables the user to open an ``IMAGE`` HDF5 node as a normal `Array` node in PyTables. """ # Class identifier. _c_classid = 'IMAGE' _c_classId = previous_api_property('_c_classid') PyTables-v.3.1.1/tables/atom.py000066400000000000000000001176221231437614300163040ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: December 16, 2004 # Author: Ivan Vilata i Balaguer - ivan at selidor dot net # # $Id$ # ######################################################################## """Atom classes for describing dataset contents.""" # Imports # ======= import re import sys import inspect import cPickle import numpy from tables.utils import SizeType from tables.misc.enum import Enum from tables._past import previous_api # Public variables # ================ __docformat__ = 'reStructuredText' """The format of documentation strings in this module.""" all_types = set() # filled as atom classes are created """Set of all PyTables types.""" atom_map = {} # filled as atom classes are created """Maps atom kinds to item sizes and atom classes. If there is a fixed set of possible item sizes for a given kind, the kind maps to another mapping from item size in bytes to atom class. Otherwise, the kind maps directly to the atom class. """ deftype_from_kind = {} # filled as atom classes are created """Maps atom kinds to their default atom type (if any).""" # Public functions # ================ _type_re = re.compile(r'^([a-z]+)([0-9]*)$') def split_type(type): """Split a PyTables type into a PyTables kind and an item size. Returns a tuple of (kind, itemsize). If no item size is present in the type (in the form of a precision), the returned item size is None:: >>> split_type('int32') ('int', 4) >>> split_type('string') ('string', None) >>> split_type('int20') Traceback (most recent call last): ... ValueError: precision must be a multiple of 8: 20 >>> split_type('foo bar') Traceback (most recent call last): ... ValueError: malformed type: 'foo bar' """ match = _type_re.match(type) if not match: raise ValueError("malformed type: %r" % type) kind, precision = match.groups() itemsize = None if precision: precision = int(precision) itemsize, remainder = divmod(precision, 8) if remainder: # 0 could be a valid item size raise ValueError("precision must be a multiple of 8: %d" % precision) return (kind, itemsize) # Private functions # ================= def _invalid_itemsize_error(kind, itemsize, itemsizes): isizes = sorted(itemsizes) return ValueError("invalid item size for kind ``%s``: %r; " "it must be one of ``%r``" % (kind, itemsize, isizes)) def _abstract_atom_init(deftype, defvalue): """Return a constructor for an abstract `Atom` class.""" defitemsize = split_type(deftype)[1] def __init__(self, itemsize=defitemsize, shape=(), dflt=defvalue): assert self.kind in atom_map try: atomclass = atom_map[self.kind][itemsize] except KeyError: raise _invalid_itemsize_error(self.kind, itemsize, atom_map[self.kind]) self.__class__ = atomclass atomclass.__init__(self, shape, dflt) return __init__ def _normalize_shape(shape): """Check that the `shape` is safe to be used and return it as a tuple.""" if isinstance(shape, (int, numpy.integer, long)): if shape < 1: raise ValueError("shape value must be greater than 0: %d" % shape) shape = (shape,) # N is a shorthand for (N,) try: shape = tuple(shape) except TypeError: raise TypeError("shape must be an integer or sequence: %r" % (shape,)) ## XXX Get from HDF5 library if possible. # HDF5 does not support ranks greater than 32 if len(shape) > 32: raise ValueError( "shapes with rank > 32 are not supported: %r" % (shape,)) return tuple(SizeType(s) for s in shape) def _normalize_default(value, dtype): """Return `value` as a valid default of NumPy type `dtype`.""" # Create NumPy objects as defaults # This is better in order to serialize them as attributes if value is None: value = 0 basedtype = dtype.base try: default = numpy.array(value, dtype=basedtype) except ValueError: array = numpy.array(value) if array.shape != basedtype.shape: raise # Maybe nested dtype with "scalar" value. default = numpy.array(value, dtype=basedtype.base) # 0-dim arrays will be representented as NumPy scalars # (PyTables attribute convention) if default.shape == (): default = default[()] return default def _cmp_dispatcher(other_method_name): """Dispatch comparisons to a method of the *other* object. Returns a new *rich comparison* method which dispatches calls to the method `other_method_name` of the *other* object. If there is no such method in the object, ``False`` is returned. This is part of the implementation of a double dispatch pattern. """ def dispatched_cmp(self, other): try: other_method = getattr(other, other_method_name) except AttributeError: return False return other_method(self) return dispatched_cmp # Helper classes # ============== class MetaAtom(type): """Atom metaclass. This metaclass ensures that data about atom classes gets inserted into the suitable registries. """ def __init__(class_, name, bases, dict_): super(MetaAtom, class_).__init__(name, bases, dict_) kind = dict_.get('kind') itemsize = dict_.get('itemsize') type_ = dict_.get('type') deftype = dict_.get('_deftype') if kind and deftype: deftype_from_kind[kind] = deftype if type_: all_types.add(type_) if kind and itemsize and not hasattr(itemsize, '__int__'): # Atom classes with a non-fixed item size do have an # ``itemsize``, but it's not a number (e.g. property). atom_map[kind] = class_ return if kind: # first definition of kind, make new entry atom_map[kind] = {} if itemsize and hasattr(itemsize, '__int__'): # fixed kind = class_.kind # maybe from superclasses atom_map[kind][int(itemsize)] = class_ # Atom classes # ============ class Atom(object): """Defines the type of atomic cells stored in a dataset. The meaning of *atomic* is that individual elements of a cell can not be extracted directly by indexing (i.e. __getitem__()) the dataset; e.g. if a dataset has shape (2, 2) and its atoms have shape (3,), to get the third element of the cell at (1, 0) one should use dataset[1,0][2] instead of dataset[1,0,2]. The Atom class is meant to declare the different properties of the *base element* (also known as *atom*) of CArray, EArray and VLArray datasets, although they are also used to describe the base elements of Array datasets. Atoms have the property that their length is always the same. However, you can grow datasets along the extensible dimension in the case of EArray or put a variable number of them on a VLArray row. Moreover, they are not restricted to scalar values, and they can be *fully multidimensional objects*. Parameters ---------- itemsize : int For types with a non-fixed size, this sets the size in bytes of individual items in the atom. shape : tuple Sets the shape of the atom. An integer shape of N is equivalent to the tuple (N,). dflt Sets the default value for the atom. The following are the public methods and attributes of the Atom class. Notes ----- A series of descendant classes are offered in order to make the use of these element descriptions easier. You should use a particular Atom descendant class whenever you know the exact type you will need when writing your code. Otherwise, you may use one of the Atom.from_*() factory Methods. .. rubric:: Arom attributes .. attribute:: dflt The default value of the atom. If the user does not supply a value for an element while filling a dataset, this default value will be written to disk. If the user supplies a scalar value for a multidimensional atom, this value is automatically *broadcast* to all the items in the atom cell. If dflt is not supplied, an appropriate zero value (or *null* string) will be chosen by default. Please note that default values are kept internally as NumPy objects. .. attribute:: dtype The NumPy dtype that most closely matches this atom. .. attribute:: itemsize Size in bytes of a single item in the atom. Specially useful for atoms of the string kind. .. attribute:: kind The PyTables kind of the atom (a string). .. attribute:: shape The shape of the atom (a tuple for scalar atoms). .. attribute:: type The PyTables type of the atom (a string). Atoms can be compared with atoms and other objects for strict (in)equality without having to compare individual attributes:: >>> atom1 = StringAtom(itemsize=10) # same as ``atom2`` >>> atom2 = Atom.from_kind('string', 10) # same as ``atom1`` >>> atom3 = IntAtom() >>> atom1 == 'foo' False >>> atom1 == atom2 True >>> atom2 != atom1 False >>> atom1 == atom3 False >>> atom3 != atom2 True """ # Register data for all subclasses. __metaclass__ = MetaAtom # Class methods # ~~~~~~~~~~~~~ @classmethod def prefix(class_): """Return the atom class prefix.""" cname = class_.__name__ return cname[:cname.rfind('Atom')] @classmethod def from_sctype(class_, sctype, shape=(), dflt=None): """Create an Atom from a NumPy scalar type sctype. Optional shape and default value may be specified as the shape and dflt arguments, respectively. Information in the sctype not represented in an Atom is ignored:: >>> import numpy >>> Atom.from_sctype(numpy.int16, shape=(2, 2)) Int16Atom(shape=(2, 2), dflt=0) >>> Atom.from_sctype('S5', dflt='hello') Traceback (most recent call last): ... ValueError: unknown NumPy scalar type: 'S5' >>> Atom.from_sctype('Float64') Float64Atom(shape=(), dflt=0.0) """ if (not isinstance(sctype, type) or not issubclass(sctype, numpy.generic)): if sctype not in numpy.sctypeDict: raise ValueError("unknown NumPy scalar type: %r" % (sctype,)) sctype = numpy.sctypeDict[sctype] return class_.from_dtype(numpy.dtype((sctype, shape)), dflt) @classmethod def from_dtype(class_, dtype, dflt=None): """Create an Atom from a NumPy dtype. An optional default value may be specified as the dflt argument. Information in the dtype not represented in an Atom is ignored:: >>> import numpy >>> Atom.from_dtype(numpy.dtype((numpy.int16, (2, 2)))) Int16Atom(shape=(2, 2), dflt=0) >>> Atom.from_dtype(numpy.dtype('Float64')) Float64Atom(shape=(), dflt=0.0) """ basedtype = dtype.base if basedtype.names: raise ValueError("compound data types are not supported: %r" % dtype) if basedtype.shape != (): raise ValueError("nested data types are not supported: %r" % dtype) if basedtype.kind == 'S': # can not reuse something like 'string80' itemsize = basedtype.itemsize return class_.from_kind('string', itemsize, dtype.shape, dflt) # Most NumPy types have direct correspondence with PyTables types. return class_.from_type(basedtype.name, dtype.shape, dflt) @classmethod def from_type(class_, type, shape=(), dflt=None): """Create an Atom from a PyTables type. Optional shape and default value may be specified as the shape and dflt arguments, respectively:: >>> Atom.from_type('bool') BoolAtom(shape=(), dflt=False) >>> Atom.from_type('int16', shape=(2, 2)) Int16Atom(shape=(2, 2), dflt=0) >>> Atom.from_type('string40', dflt='hello') Traceback (most recent call last): ... ValueError: unknown type: 'string40' >>> Atom.from_type('Float64') Traceback (most recent call last): ... ValueError: unknown type: 'Float64' """ if type not in all_types: raise ValueError("unknown type: %r" % (type,)) kind, itemsize = split_type(type) return class_.from_kind(kind, itemsize, shape, dflt) @classmethod def from_kind(class_, kind, itemsize=None, shape=(), dflt=None): """Create an Atom from a PyTables kind. Optional item size, shape and default value may be specified as the itemsize, shape and dflt arguments, respectively. Bear in mind that not all atoms support a default item size:: >>> Atom.from_kind('int', itemsize=2, shape=(2, 2)) Int16Atom(shape=(2, 2), dflt=0) >>> Atom.from_kind('int', shape=(2, 2)) Int32Atom(shape=(2, 2), dflt=0) >>> Atom.from_kind('int', shape=1) Int32Atom(shape=(1,), dflt=0) >>> Atom.from_kind('string', dflt=b'hello') Traceback (most recent call last): ... ValueError: no default item size for kind ``string`` >>> Atom.from_kind('Float') Traceback (most recent call last): ... ValueError: unknown kind: 'Float' Moreover, some kinds with atypical constructor signatures are not supported; you need to use the proper constructor:: >>> Atom.from_kind('enum') #doctest: +ELLIPSIS Traceback (most recent call last): ... ValueError: the ``enum`` kind is not supported... """ kwargs = {'shape': shape} if kind not in atom_map: raise ValueError("unknown kind: %r" % (kind,)) # This incompatibility detection may get out-of-date and is # too hard-wired, but I couldn't come up with something # smarter. -- Ivan (2007-02-08) if kind in ['enum']: raise ValueError("the ``%s`` kind is not supported; " "please use the appropriate constructor" % kind) # If no `itemsize` is given, try to get the default type of the # kind (which has a fixed item size). if itemsize is None: if kind not in deftype_from_kind: raise ValueError("no default item size for kind ``%s``" % kind) type_ = deftype_from_kind[kind] kind, itemsize = split_type(type_) kdata = atom_map[kind] # Look up the class and set a possible item size. if hasattr(kdata, 'kind'): # atom class: non-fixed item size atomclass = kdata kwargs['itemsize'] = itemsize else: # dictionary: fixed item size if itemsize not in kdata: raise _invalid_itemsize_error(kind, itemsize, kdata) atomclass = kdata[itemsize] # Only set a `dflt` argument if given (`None` may not be understood). if dflt is not None: kwargs['dflt'] = dflt return atomclass(**kwargs) # Properties # ~~~~~~~~~~ size = property( lambda self: self.dtype.itemsize, None, None, "Total size in bytes of the atom.") recarrtype = property( lambda self: str(self.dtype.shape) + self.dtype.base.str[1:], None, None, "String type to be used in numpy.rec.array().") ndim = property( lambda self: len(self.shape), None, None, """The number of dimensions of the atom. .. versionadded:: 2.4""") # Special methods # ~~~~~~~~~~~~~~~ def __init__(self, nptype, shape, dflt): if not hasattr(self, 'type'): raise NotImplementedError("``%s`` is an abstract class; " "please use one of its subclasses" % self.__class__.__name__) self.shape = shape = _normalize_shape(shape) """The shape of the atom (a tuple for scalar atoms).""" # Curiously enough, NumPy isn't generally able to accept NumPy # integers in a shape. ;( npshape = tuple(int(s) for s in shape) self.dtype = dtype = numpy.dtype((nptype, npshape)) """The NumPy dtype that most closely matches this atom.""" self.dflt = _normalize_default(dflt, dtype) """The default value of the atom. If the user does not supply a value for an element while filling a dataset, this default value will be written to disk. If the user supplies a scalar value for a multidimensional atom, this value is automatically *broadcast* to all the items in the atom cell. If dflt is not supplied, an appropriate zero value (or *null* string) will be chosen by default. Please note that default values are kept internally as NumPy objects.""" def __repr__(self): args = 'shape=%s, dflt=%r' % (self.shape, self.dflt) if not hasattr(self.__class__.itemsize, '__int__'): # non-fixed args = 'itemsize=%s, %s' % (self.itemsize, args) return '%s(%s)' % (self.__class__.__name__, args) __eq__ = _cmp_dispatcher('_is_equal_to_atom') def __ne__(self, other): return not self.__eq__(other) # XXX: API incompatible change for PyTables 3 line # Overriding __eq__ blocks inheritance of __hash__ in 3.x # def __hash__(self): # return hash((self.__class__, self.type, self.shape, self.itemsize, # self.dflt)) # Public methods # ~~~~~~~~~~~~~~ def copy(self, **override): """Get a copy of the atom, possibly overriding some arguments. Constructor arguments to be overridden must be passed as keyword arguments:: >>> atom1 = Int32Atom(shape=12) >>> atom2 = atom1.copy() >>> print(atom1) Int32Atom(shape=(12,), dflt=0) >>> print(atom2) Int32Atom(shape=(12,), dflt=0) >>> atom1 is atom2 False >>> atom3 = atom1.copy(shape=(2, 2)) >>> print(atom3) Int32Atom(shape=(2, 2), dflt=0) >>> atom1.copy(foobar=42) Traceback (most recent call last): ... TypeError: __init__() got an unexpected keyword argument 'foobar' """ newargs = self._get_init_args() newargs.update(override) return self.__class__(**newargs) # Private methods # ~~~~~~~~~~~~~~~ def _get_init_args(self): """Get a dictionary of instance constructor arguments. This implementation works on classes which use the same names for both constructor arguments and instance attributes. """ return dict((arg, getattr(self, arg)) for arg in inspect.getargspec(self.__init__)[0] if arg != 'self') def _is_equal_to_atom(self, atom): """Is this object equal to the given `atom`?""" return (self.type == atom.type and self.shape == atom.shape and self.itemsize == atom.itemsize and numpy.all(self.dflt == atom.dflt)) class StringAtom(Atom): """Defines an atom of type string. The item size is the *maximum* length in characters of strings. """ kind = 'string' itemsize = property( lambda self: self.dtype.base.itemsize, None, None, "Size in bytes of a sigle item in the atom.") type = 'string' _defvalue = b'' def __init__(self, itemsize, shape=(), dflt=_defvalue): if not hasattr(itemsize, '__int__') or int(itemsize) < 0: raise ValueError("invalid item size for kind ``%s``: %r; " "it must be a positive integer" % ('string', itemsize)) Atom.__init__(self, 'S%d' % itemsize, shape, dflt) class BoolAtom(Atom): """Defines an atom of type bool.""" kind = 'bool' itemsize = 1 type = 'bool' _deftype = 'bool8' _defvalue = False def __init__(self, shape=(), dflt=_defvalue): Atom.__init__(self, self.type, shape, dflt) class IntAtom(Atom): """Defines an atom of a signed integral type (int kind).""" kind = 'int' signed = True _deftype = 'int32' _defvalue = 0 __init__ = _abstract_atom_init(_deftype, _defvalue) class UIntAtom(Atom): """Defines an atom of an unsigned integral type (uint kind).""" kind = 'uint' signed = False _deftype = 'uint32' _defvalue = 0 __init__ = _abstract_atom_init(_deftype, _defvalue) class FloatAtom(Atom): """Defines an atom of a floating point type (float kind).""" kind = 'float' _deftype = 'float64' _defvalue = 0.0 __init__ = _abstract_atom_init(_deftype, _defvalue) def _create_numeric_class(baseclass, itemsize): """Create a numeric atom class with the given `baseclass` and an `itemsize`.""" prefix = '%s%d' % (baseclass.prefix(), itemsize * 8) type_ = prefix.lower() classdict = {'itemsize': itemsize, 'type': type_, '__doc__': "Defines an atom of type ``%s``." % type_} def __init__(self, shape=(), dflt=baseclass._defvalue): Atom.__init__(self, self.type, shape, dflt) classdict['__init__'] = __init__ return type('%sAtom' % prefix, (baseclass,), classdict) def _generate_integral_classes(): """Generate all integral classes.""" for baseclass in [IntAtom, UIntAtom]: for itemsize in [1, 2, 4, 8]: newclass = _create_numeric_class(baseclass, itemsize) yield newclass def _generate_floating_classes(): """Generate all floating classes.""" itemsizes = [4, 8] # numpy >= 1.6 if hasattr(numpy, 'float16'): itemsizes.insert(0, 2) if hasattr(numpy, 'float96'): itemsizes.append(12) if hasattr(numpy, 'float128'): itemsizes.append(16) for itemsize in itemsizes: newclass = _create_numeric_class(FloatAtom, itemsize) yield newclass # Create all numeric atom classes. for _classgen in [_generate_integral_classes, _generate_floating_classes]: for _newclass in _classgen(): exec('%s = _newclass' % _newclass.__name__) del _classgen, _newclass class ComplexAtom(Atom): """Defines an atom of kind complex. Allowed item sizes are 8 (single precision) and 16 (double precision). This class must be used instead of more concrete ones to avoid confusions with numarray-like precision specifications used in PyTables 1.X. """ # This definition is a little more complex (no pun intended) # because, although the complex kind is a normal numerical one, # the usage of bottom-level classes is artificially forbidden. # Everything will be back to normality when people has stopped # using the old bottom-level complex classes. kind = 'complex' itemsize = property( lambda self: self.dtype.base.itemsize, None, None, "Size in bytes of a sigle item in the atom.") _deftype = 'complex128' _defvalue = 0j _isizes = [8, 16] # Only instances have a `type` attribute, so complex types must be # registered by hand. all_types.add('complex64') all_types.add('complex128') if hasattr(numpy, 'complex192'): all_types.add('complex192') _isizes.append(24) if hasattr(numpy, 'complex256'): all_types.add('complex256') _isizes.append(32) def __init__(self, itemsize, shape=(), dflt=_defvalue): if itemsize not in self._isizes: raise _invalid_itemsize_error('complex', itemsize, self._isizes) self.type = '%s%d' % (self.kind, itemsize * 8) Atom.__init__(self, self.type, shape, dflt) class _ComplexErrorAtom(ComplexAtom): """Reminds the user to stop using the old complex atom names.""" __metaclass__ = type # do not register anything about this class def __init__(self, shape=(), dflt=ComplexAtom._defvalue): raise TypeError( "to avoid confusions with PyTables 1.X complex atom names, " "please use ``ComplexAtom(itemsize=N)``, " "where N=8 for single precision complex atoms, " "and N=16 for double precision complex atoms") Complex32Atom = Complex64Atom = Complex128Atom = _ComplexErrorAtom if hasattr(numpy, 'complex192'): Complex192Atom = _ComplexErrorAtom if hasattr(numpy, 'complex256'): Complex256Atom = _ComplexErrorAtom class TimeAtom(Atom): """Defines an atom of time type (time kind). There are two distinct supported types of time: a 32 bit integer value and a 64 bit floating point value. Both of them reflect the number of seconds since the Unix epoch. This atom has the property of being stored using the HDF5 time datatypes. """ kind = 'time' _deftype = 'time32' _defvalue = 0 __init__ = _abstract_atom_init(_deftype, _defvalue) class Time32Atom(TimeAtom): """Defines an atom of type time32.""" itemsize = 4 type = 'time32' _defvalue = 0 def __init__(self, shape=(), dflt=_defvalue): Atom.__init__(self, 'int32', shape, dflt) class Time64Atom(TimeAtom): """Defines an atom of type time64.""" itemsize = 8 type = 'time64' _defvalue = 0.0 def __init__(self, shape=(), dflt=_defvalue): Atom.__init__(self, 'float64', shape, dflt) class EnumAtom(Atom): """Description of an atom of an enumerated type. Instances of this class describe the atom type used to store enumerated values. Those values belong to an enumerated type, defined by the first argument (enum) in the constructor of the atom, which accepts the same kinds of arguments as the Enum class (see :ref:`EnumClassDescr`). The enumerated type is stored in the enum attribute of the atom. A default value must be specified as the second argument (dflt) in the constructor; it must be the *name* (a string) of one of the enumerated values in the enumerated type. When the atom is created, the corresponding concrete value is broadcast and stored in the dflt attribute (setting different default values for items in a multidimensional atom is not supported yet). If the name does not match any value in the enumerated type, a KeyError is raised. Another atom must be specified as the base argument in order to determine the base type used for storing the values of enumerated values in memory and disk. This *storage atom* is kept in the base attribute of the created atom. As a shorthand, you may specify a PyTables type instead of the storage atom, implying that this has a scalar shape. The storage atom should be able to represent each and every concrete value in the enumeration. If it is not, a TypeError is raised. The default value of the storage atom is ignored. The type attribute of enumerated atoms is always enum. Enumerated atoms also support comparisons with other objects:: >>> enum = ['T0', 'T1', 'T2'] >>> atom1 = EnumAtom(enum, 'T0', 'int8') # same as ``atom2`` >>> atom2 = EnumAtom(enum, 'T0', Int8Atom()) # same as ``atom1`` >>> atom3 = EnumAtom(enum, 'T0', 'int16') >>> atom4 = Int8Atom() >>> atom1 == enum False >>> atom1 == atom2 True >>> atom2 != atom1 False >>> atom1 == atom3 False >>> atom1 == atom4 False >>> atom4 != atom1 True Examples -------- The next C enum construction:: enum myEnum { T0, T1, T2 }; would correspond to the following PyTables declaration:: >>> my_enum_atom = EnumAtom(['T0', 'T1', 'T2'], 'T0', 'int32') Please note the dflt argument with a value of 'T0'. Since the concrete value matching T0 is unknown right now (we have not used explicit concrete values), using the name is the only option left for defining a default value for the atom. The chosen representation of values for this enumerated atom uses unsigned 32-bit integers, which surely wastes quite a lot of memory. Another size could be selected by using the base argument (this time with a full-blown storage atom):: >>> my_enum_atom = EnumAtom(['T0', 'T1', 'T2'], 'T0', UInt8Atom()) You can also define multidimensional arrays for data elements:: >>> my_enum_atom = EnumAtom( ... ['T0', 'T1', 'T2'], 'T0', base='uint32', shape=(3,2)) for 3x2 arrays of uint32. """ # Registering this class in the class map may be a little wrong, # since the ``Atom.from_kind()`` method fails miserably with # enumerations, as they don't support an ``itemsize`` argument. # However, resetting ``__metaclass__`` to ``type`` doesn't seem to # work and I don't feel like creating a subclass of ``MetaAtom``. kind = 'enum' type = 'enum' # Properties # ~~~~~~~~~~ itemsize = property( lambda self: self.dtype.base.itemsize, None, None, "Size in bytes of a sigle item in the atom.") # Private methods # ~~~~~~~~~~~~~~~ def _checkbase(self, base): """Check the `base` storage atom.""" if base.kind == 'enum': raise TypeError("can not use an enumerated atom " "as a storage atom: %r" % base) # Check whether the storage atom can represent concrete values # in the enumeration... basedtype = base.dtype pyvalues = [value for (name, value) in self.enum] try: npgenvalues = numpy.array(pyvalues) except ValueError: raise TypeError("concrete values are not uniformly-shaped") try: npvalues = numpy.array(npgenvalues, dtype=basedtype.base) except ValueError: raise TypeError("storage atom type is incompatible with " "concrete values in the enumeration") if npvalues.shape[1:] != basedtype.shape: raise TypeError("storage atom shape does not match that of " "concrete values in the enumeration") if npvalues.tolist() != npgenvalues.tolist(): raise TypeError("storage atom type lacks precision for " "concrete values in the enumeration") # ...with some implementation limitations. if not npvalues.dtype.kind in ['i', 'u']: raise NotImplementedError("only integer concrete values " "are supported for the moment, sorry") if len(npvalues.shape) > 1: raise NotImplementedError("only scalar concrete values " "are supported for the moment, sorry") _checkBase = previous_api(_checkbase) def _get_init_args(self): """Get a dictionary of instance constructor arguments.""" return dict(enum=self.enum, dflt=self._defname, base=self.base, shape=self.shape) def _is_equal_to_atom(self, atom): """Is this object equal to the given `atom`?""" return False def _is_equal_to_enumatom(self, enumatom): """Is this object equal to the given `enumatom`?""" return (self.enum == enumatom.enum and self.shape == enumatom.shape and numpy.all(self.dflt == enumatom.dflt) and self.base == enumatom.base) # Special methods # ~~~~~~~~~~~~~~~ def __init__(self, enum, dflt, base, shape=()): if not isinstance(enum, Enum): enum = Enum(enum) self.enum = enum if isinstance(base, str): base = Atom.from_type(base) self._checkbase(base) self.base = base default = enum[dflt] # check default value self._defname = dflt # kept for representation purposes # These are kept to ease dumping this particular # representation of the enumeration to storage. names, values = [], [] for (name, value) in enum: names.append(name) values.append(value) basedtype = self.base.dtype self._names = names self._values = numpy.array(values, dtype=basedtype.base) Atom.__init__(self, basedtype, shape, default) def __repr__(self): return ('EnumAtom(enum=%r, dflt=%r, base=%r, shape=%r)' % (self.enum, self._defname, self.base, self.shape)) __eq__ = _cmp_dispatcher('_is_equal_to_enumatom') # XXX: API incompatible change for PyTables 3 line # Overriding __eq__ blocks inheritance of __hash__ in 3.x # def __hash__(self): # return hash((self.__class__, self.enum, self.shape, self.dflt, # self.base)) # Pseudo-atom classes # =================== # # Now, there come three special classes, `ObjectAtom`, `VLStringAtom` # and `VLUnicodeAtom`, that actually do not descend from `Atom`, but # which goal is so similar that they should be described here. # Pseudo-atoms can only be used with `VLArray` datasets, and they do # not support multidimensional values, nor multiple values per row. # # They can be recognised because they also have ``kind``, ``type`` and # ``shape`` attributes, but no ``size``, ``itemsize`` or ``dflt`` # ones. Instead, they have a ``base`` atom which defines the elements # used for storage. # # See ``examples/vlarray1.py`` and ``examples/vlarray2.py`` for # further examples on `VLArray` datasets, including object # serialization and string management. class PseudoAtom(object): """Pseudo-atoms can only be used in ``VLArray`` nodes. They can be recognised because they also have `kind`, `type` and `shape` attributes, but no `size`, `itemsize` or `dflt` ones. Instead, they have a `base` atom which defines the elements used for storage. """ def __repr__(self): return '%s()' % self.__class__.__name__ def toarray(self, object_): """Convert an `object_` into an array of base atoms.""" raise NotImplementedError def fromarray(self, array): """Convert an `array` of base atoms into an object.""" raise NotImplementedError class _BufferedAtom(PseudoAtom): """Pseudo-atom which stores data as a buffer (flat array of uints).""" shape = () def toarray(self, object_): buffer_ = self._tobuffer(object_) array = numpy.ndarray(buffer=buffer_, dtype=self.base.dtype, shape=len(buffer_)) return array def _tobuffer(self, object_): """Convert an `object_` into a buffer.""" raise NotImplementedError class VLStringAtom(_BufferedAtom): """Defines an atom of type ``vlstring``. This class describes a *row* of the VLArray class, rather than an atom. It differs from the StringAtom class in that you can only add *one instance of it to one specific row*, i.e. the :meth:`VLArray.append` method only accepts one object when the base atom is of this type. Like StringAtom, this class does not make assumptions on the encoding of the string, and raw bytes are stored as is. Unicode strings are supported as long as no character is out of the ASCII set; otherwise, you will need to *explicitly* convert them to strings before you can save them. For full Unicode support, using VLUnicodeAtom (see :ref:`VLUnicodeAtom`) is recommended. Variable-length string atoms do not accept parameters and they cause the reads of rows to always return Python strings. You can regard vlstring atoms as an easy way to save generic variable length strings. """ kind = 'vlstring' type = 'vlstring' base = UInt8Atom() def _tobuffer(self, object_): if not isinstance(object_, basestring): raise TypeError("object is not a string: %r" % (object_,)) return numpy.string_(object_) def fromarray(self, array): return array.tostring() class VLUnicodeAtom(_BufferedAtom): """Defines an atom of type vlunicode. This class describes a *row* of the VLArray class, rather than an atom. It is very similar to VLStringAtom (see :ref:`VLStringAtom`), but it stores Unicode strings (using 32-bit characters a la UCS-4, so all strings of the same length also take up the same space). This class does not make assumptions on the encoding of plain input strings. Plain strings are supported as long as no character is out of the ASCII set; otherwise, you will need to *explicitly* convert them to Unicode before you can save them. Variable-length Unicode atoms do not accept parameters and they cause the reads of rows to always return Python Unicode strings. You can regard vlunicode atoms as an easy way to save variable length Unicode strings. """ kind = 'vlunicode' type = 'vlunicode' base = UInt32Atom() if sys.version_info[0] > 2 or sys.maxunicode <= 0xffff: # numpy.unicode_ no more implements the buffer interface in Python 3 # # When the Python build is UCS-2, we need to promote the # Unicode string to UCS-4. We *must* use a 0-d array since # NumPy scalars inherit the UCS-2 encoding from Python (see # NumPy ticket #525). Since ``_tobuffer()`` can't return an # array, we must override ``toarray()`` itself. def toarray(self, object_): if not isinstance(object_, basestring): raise TypeError("object is not a string: %r" % (object_,)) ustr = unicode(object_) uarr = numpy.array(ustr, dtype='U') return numpy.ndarray( buffer=uarr, dtype=self.base.dtype, shape=len(ustr)) def _tobuffer(self, object_): # This works (and is used) only with UCS-4 builds of Python, # where the width of the internal representation of a # character matches that of the base atoms. if not isinstance(object_, basestring): raise TypeError("object is not a string: %r" % (object_,)) return numpy.unicode_(object_) def fromarray(self, array): length = len(array) if length == 0: return u'' # ``array.view('U0')`` raises a `TypeError` return array.view('U%d' % length).item() class ObjectAtom(_BufferedAtom): """Defines an atom of type object. This class is meant to fit *any* kind of Python object in a row of a VLArray dataset by using pickle behind the scenes. Due to the fact that you can not foresee how long will be the output of the pickle serialization (i.e. the atom already has a *variable* length), you can only fit *one object per row*. However, you can still group several objects in a single tuple or list and pass it to the :meth:`VLArray.append` method. Object atoms do not accept parameters and they cause the reads of rows to always return Python objects. You can regard object atoms as an easy way to save an arbitrary number of generic Python objects in a VLArray dataset. """ kind = 'object' type = 'object' base = UInt8Atom() def _tobuffer(self, object_): return cPickle.dumps(object_, cPickle.HIGHEST_PROTOCOL) def fromarray(self, array): # We have to check for an empty array because of a possible # bug in HDF5 which makes it claim that a dataset has one # record when in fact it is empty. if array.size == 0: return None return cPickle.loads(array.tostring()) # Main part # ========= def _test(): """Run ``doctest`` on this module.""" import doctest doctest.testmod() if __name__ == '__main__': _test() PyTables-v.3.1.1/tables/attributeset.py000066400000000000000000000612571231437614300200650ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: May 26, 2003 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Here is defined the AttributeSet class.""" import re import sys import warnings import cPickle import numpy from tables import hdf5extension from tables.utils import SizeType from tables.registry import class_name_dict from tables.exceptions import ClosedNodeError, PerformanceWarning from tables.path import check_name_validity from tables.undoredo import attr_to_shadow from tables.filters import Filters from tables._past import previous_api # System attributes SYS_ATTRS = ["CLASS", "VERSION", "TITLE", "NROWS", "EXTDIM", "ENCODING", "PYTABLES_FORMAT_VERSION", "FLAVOR", "FILTERS", "AUTO_INDEX", "DIRTY", "NODE_TYPE", "NODE_TYPE_VERSION", "PSEUDOATOM"] # Prefixes of other system attributes SYS_ATTRS_PREFIXES = ["FIELD_"] # RO_ATTRS will be disabled and let the user modify them if they # want to. The user is still not allowed to remove or rename # system attributes. Francesc Alted 2004-12-19 # Read-only attributes: # RO_ATTRS = ["CLASS", "FLAVOR", "VERSION", "NROWS", "EXTDIM", # "PYTABLES_FORMAT_VERSION", "FILTERS", # "NODE_TYPE", "NODE_TYPE_VERSION"] # RO_ATTRS = [] # The next attributes are not meant to be copied during a Node copy process SYS_ATTRS_NOTTOBECOPIED = ["CLASS", "VERSION", "TITLE", "NROWS", "EXTDIM", "PYTABLES_FORMAT_VERSION", "FILTERS", "ENCODING"] # Attributes forced to be copied during node copies FORCE_COPY_CLASS = ['CLASS', 'VERSION'] # Regular expression for column default values. _field_fill_re = re.compile('^FIELD_[0-9]+_FILL$') # Regular expression for fixing old pickled filters. _old_filters_re = re.compile(br'\(([ic])tables\.Leaf\n') # Fixed version of the previous string. _new_filters_sub = br'(\1tables.filters\n' def issysattrname(name): "Check if a name is a system attribute or not" if (name in SYS_ATTRS or numpy.prod([name.startswith(prefix) for prefix in SYS_ATTRS_PREFIXES])): return True else: return False class AttributeSet(hdf5extension.AttributeSet, object): """Container for the HDF5 attributes of a Node. This class provides methods to create new HDF5 node attributes, and to get, rename or delete existing ones. Like in Group instances (see :ref:`GroupClassDescr`), AttributeSet instances make use of the *natural naming* convention, i.e. you can access the attributes on disk as if they were normal Python attributes of the AttributeSet instance. This offers the user a very convenient way to access HDF5 node attributes. However, for this reason and in order not to pollute the object namespace, one can not assign *normal* attributes to AttributeSet instances, and their members use names which start by special prefixes as happens with Group objects. .. rubric:: Notes on native and pickled attributes The values of most basic types are saved as HDF5 native data in the HDF5 file. This includes Python bool, int, float, complex and str (but not long nor unicode) values, as well as their NumPy scalar versions and homogeneous or *structured* NumPy arrays of them. When read, these values are always loaded as NumPy scalar or array objects, as needed. For that reason, attributes in native HDF5 files will be always mapped into NumPy objects. Specifically, a multidimensional attribute will be mapped into a multidimensional ndarray and a scalar will be mapped into a NumPy scalar object (for example, a scalar H5T_NATIVE_LLONG will be read and returned as a numpy.int64 scalar). However, other kinds of values are serialized using pickle, so you only will be able to correctly retrieve them using a Python-aware HDF5 library. Thus, if you want to save Python scalar values and make sure you are able to read them with generic HDF5 tools, you should make use of *scalar or homogeneous/structured array NumPy objects* (for example, numpy.int64(1) or numpy.array([1, 2, 3], dtype='int16')). One more advice: because of the various potential difficulties in restoring a Python object stored in an attribute, you may end up getting a pickle string where a Python object is expected. If this is the case, you may wish to run pickle.loads() on that string to get an idea of where things went wrong, as shown in this example:: >>> import os, tempfile >>> import tables >>> >>> class MyClass(object): ... foo = 'bar' ... >>> myObject = MyClass() # save object of custom class in HDF5 attr >>> h5fname = tempfile.mktemp(suffix='.h5') >>> h5f = tables.open_file(h5fname, 'w') >>> h5f.root._v_attrs.obj = myObject # store the object >>> print(h5f.root._v_attrs.obj.foo) # retrieve it bar >>> h5f.close() >>> >>> del MyClass, myObject # delete class of object and reopen file >>> h5f = tables.open_file(h5fname, 'r') >>> print(repr(h5f.root._v_attrs.obj)) 'ccopy_reg\\n_reconstructor... >>> import pickle # let's unpickle that to see what went wrong >>> pickle.loads(h5f.root._v_attrs.obj) Traceback (most recent call last): ... AttributeError: 'module' object has no attribute 'MyClass' >>> # So the problem was not in the stored object, ... # but in the *environment* where it was restored. ... h5f.close() >>> os.remove(h5fname) .. rubric:: Notes on AttributeSet methods Note that this class overrides the __getattr__(), __setattr__() and __delattr__() special methods. This allows you to read, assign or delete attributes on disk by just using the next constructs:: leaf.attrs.myattr = 'str attr' # set a string (native support) leaf.attrs.myattr2 = 3 # set an integer (native support) leaf.attrs.myattr3 = [3, (1, 2)] # a generic object (Pickled) attrib = leaf.attrs.myattr # get the attribute ``myattr`` del leaf.attrs.myattr # delete the attribute ``myattr`` In addition, the dictionary-like __getitem__(), __setitem__() and __delitem__() methods are available, so you may write things like this:: for name in :attr:`Node._v_attrs`._f_list(): print("name: %s, value: %s" % (name, :attr:`Node._v_attrs`[name])) Use whatever idiom you prefer to access the attributes. If an attribute is set on a target node that already has a large number of attributes, a PerformanceWarning will be issued. .. rubric:: AttributeSet attributes .. attribute:: _v_attrnames A list with all attribute names. .. attribute:: _v_attrnamessys A list with system attribute names. .. attribute:: _v_attrnamesuser A list with user attribute names. .. attribute:: _v_unimplemented A list of attribute names with unimplemented native HDF5 types. """ def _g_getnode(self): return self._v__nodefile._get_node(self._v__nodepath) _v_node = property(_g_getnode, None, None, "The :class:`Node` instance this attribute set is " "associated with.") def __init__(self, node): """Create the basic structures to keep the attribute information. Reads all the HDF5 attributes (if any) on disk for the node "node". Parameters ---------- node The parent node """ # Refuse to create an instance of an already closed node if not node._v_isopen: raise ClosedNodeError("the node for attribute set is closed") mydict = self.__dict__ self._g_new(node) mydict["_v__nodefile"] = node._v_file mydict["_v__nodepath"] = node._v_pathname mydict["_v_attrnames"] = self._g_list_attr(node) # The list of unimplemented attribute names mydict["_v_unimplemented"] = [] # Get the file version format. This is an optimization # in order to avoid accessing it too much. try: format_version = node._v_file.format_version except AttributeError: parsed_version = None else: if format_version == 'unknown': parsed_version = None else: parsed_version = tuple(map(int, format_version.split('.'))) mydict["_v__format_version"] = parsed_version # Split the attribute list in system and user lists mydict["_v_attrnamessys"] = [] mydict["_v_attrnamesuser"] = [] for attr in self._v_attrnames: # put the attributes on the local dictionary to allow # tab-completion self.__getattr__(attr) if issysattrname(attr): self._v_attrnamessys.append(attr) else: self._v_attrnamesuser.append(attr) # Sort the attributes self._v_attrnames.sort() self._v_attrnamessys.sort() self._v_attrnamesuser.sort() def _g_update_node_location(self, node): """Updates the location information about the associated `node`.""" myDict = self.__dict__ myDict['_v__nodefile'] = node._v_file myDict['_v__nodepath'] = node._v_pathname # hdf5extension operations: self._g_new(node) _g_updateNodeLocation = previous_api(_g_update_node_location) def _f_list(self, attrset='user'): """Get a list of attribute names. The attrset string selects the attribute set to be used. A 'user' value returns only user attributes (this is the default). A 'sys' value returns only system attributes. Finally, 'all' returns both system and user attributes. """ if attrset == "user": return self._v_attrnamesuser[:] elif attrset == "sys": return self._v_attrnamessys[:] elif attrset == "all": return self._v_attrnames[:] def __getattr__(self, name): """Get the attribute named "name".""" # If attribute does not exist, raise AttributeError if not name in self._v_attrnames: raise AttributeError("Attribute '%s' does not exist in node: " "'%s'" % (name, self._v__nodepath)) # Read the attribute from disk. This is an optimization to read # quickly system attributes that are _string_ values, but it # takes care of other types as well as for example NROWS for # Tables and EXTDIM for EArrays format_version = self._v__format_version value = self._g_getattr(self._v_node, name) # Check whether the value is pickled # Pickled values always seems to end with a "." maybe_pickled = ( isinstance(value, numpy.generic) and # NumPy scalar? value.dtype.type == numpy.bytes_ and # string type? value.itemsize > 0 and value.endswith(b'.')) if (maybe_pickled and value in [b"0", b"0."]): # Workaround for a bug in many versions of Python (starting # somewhere after Python 2.6.1). See ticket #253. retval = value elif (maybe_pickled and _field_fill_re.match(name) and format_version == (1, 5)): # This format was used during the first 1.2 releases, just # for string defaults. try: retval = cPickle.loads(value) retval = numpy.array(retval) except ImportError: retval = None # signal error avoiding exception elif maybe_pickled and name == 'FILTERS' and format_version < (2, 0): # This is a big hack, but we don't have other way to recognize # pickled filters of PyTables 1.x files. value = _old_filters_re.sub(_new_filters_sub, value, 1) retval = cPickle.loads(value) # pass unpickling errors through elif maybe_pickled: try: retval = cPickle.loads(value) # except cPickle.UnpicklingError: # It seems that pickle may raise other errors than UnpicklingError # Perhaps it would be better just an "except:" clause? # except (cPickle.UnpicklingError, ImportError): # Definitely (see SF bug #1254636) except: # ivb (2005-09-07): It is too hard to tell # whether the unpickling failed # because of the string not being a pickle one at all, # because of a malformed pickle string, # or because of some other problem in object reconstruction, # thus making inconvenient even the issuing of a warning here. # The documentation contains a note on this issue, # explaining how the user can tell where the problem was. retval = value # Additional check for allowing a workaround for #307 if isinstance(retval, unicode) and retval == u'': retval = numpy.array(retval)[()] elif name == 'FILTERS' and format_version >= (2, 0): retval = Filters._unpack(value) elif (issysattrname(name) and isinstance(value, (bytes, unicode)) and not isinstance(value, str) and not _field_fill_re.match(name)): # system attributes should always be str if sys.version_info[0] < 3: retval = value.encode() else: # python 3, bytes and not "FIELD_[0-9]+_FILL" retval = value.decode('utf-8') else: retval = value # Put this value in local directory self.__dict__[name] = retval return retval def _g__setattr(self, name, value): """Set a PyTables attribute. Sets a (maybe new) PyTables attribute with the specified `name` and `value`. If the attribute already exists, it is simply replaced. It does not log the change. """ # Save this attribute to disk # (overwriting an existing one if needed) stvalue = value if issysattrname(name): if name in ["EXTDIM", "AUTO_INDEX", "DIRTY", "NODE_TYPE_VERSION"]: stvalue = numpy.array(value, dtype=numpy.int32) value = stvalue[()] elif name == "NROWS": stvalue = numpy.array(value, dtype=SizeType) value = stvalue[()] elif name == "FILTERS" and self._v__format_version >= (2, 0): stvalue = value._pack() # value will remain as a Filters instance here # Convert value from a Python scalar into a NumPy scalar # (only in case it has not been converted yet) # Fixes ticket #59 if (stvalue is value and type(value) in (bool, bytes, int, float, complex, unicode, numpy.unicode_)): # Additional check for allowing a workaround for #307 if value == u'': stvalue = numpy.array(u'') else: stvalue = numpy.array(value) value = stvalue[()] self._g_setattr(self._v_node, name, stvalue) # New attribute or value. Introduce it into the local # directory self.__dict__[name] = value # Finally, add this attribute to the list if not present attrnames = self._v_attrnames if not name in attrnames: attrnames.append(name) attrnames.sort() if issysattrname(name): attrnamessys = self._v_attrnamessys attrnamessys.append(name) attrnamessys.sort() else: attrnamesuser = self._v_attrnamesuser attrnamesuser.append(name) attrnamesuser.sort() def __setattr__(self, name, value): """Set a PyTables attribute. Sets a (maybe new) PyTables attribute with the specified `name` and `value`. If the attribute already exists, it is simply replaced. A ``ValueError`` is raised when the name starts with a reserved prefix or contains a ``/``. A `NaturalNameWarning` is issued if the name is not a valid Python identifier. A `PerformanceWarning` is issued when the recommended maximum number of attributes in a node is going to be exceeded. """ nodeFile = self._v__nodefile attrnames = self._v_attrnames # Check for name validity check_name_validity(name) nodeFile._check_writable() # Check if there are too many attributes. maxNodeAttrs = nodeFile.params['MAX_NODE_ATTRS'] if len(attrnames) >= maxNodeAttrs: warnings.warn("""\ node ``%s`` is exceeding the recommended maximum number of attributes (%d);\ be ready to see PyTables asking for *lots* of memory and possibly slow I/O""" % (self._v__nodepath, maxNodeAttrs), PerformanceWarning) undoEnabled = nodeFile.is_undo_enabled() # Log old attribute removal (if any). if undoEnabled and (name in attrnames): self._g_del_and_log(name) # Set the attribute. self._g__setattr(name, value) # Log new attribute addition. if undoEnabled: self._g_log_add(name) def _g_log_add(self, name): self._v__nodefile._log('ADDATTR', self._v__nodepath, name) _g_logAdd = previous_api(_g_log_add) def _g_del_and_log(self, name): nodeFile = self._v__nodefile nodePathname = self._v__nodepath # Log *before* moving to use the right shadow name. nodeFile._log('DELATTR', nodePathname, name) attr_to_shadow(nodeFile, nodePathname, name) _g_delAndLog = previous_api(_g_del_and_log) def _g__delattr(self, name): """Delete a PyTables attribute. Deletes the specified existing PyTables attribute. It does not log the change. """ # Delete the attribute from disk self._g_remove(self._v_node, name) # Delete the attribute from local lists self._v_attrnames.remove(name) if name in self._v_attrnamessys: self._v_attrnamessys.remove(name) else: self._v_attrnamesuser.remove(name) # Delete the attribute from the local directory # closes (#1049285) del self.__dict__[name] def __delattr__(self, name): """Delete a PyTables attribute. Deletes the specified existing PyTables attribute from the attribute set. If a nonexistent or system attribute is specified, an ``AttributeError`` is raised. """ nodeFile = self._v__nodefile # Check if attribute exists if name not in self._v_attrnames: raise AttributeError( "Attribute ('%s') does not exist in node '%s'" % (name, self._v__nodepath)) nodeFile._check_writable() # Remove the PyTables attribute or move it to shadow. if nodeFile.is_undo_enabled(): self._g_del_and_log(name) else: self._g__delattr(name) def __getitem__(self, name): """The dictionary like interface for __getattr__().""" try: return self.__getattr__(name) except AttributeError: # Capture the AttributeError an re-raise a KeyError one raise KeyError( "Attribute ('%s') does not exist in node '%s'" % (name, self._v__nodepath)) def __setitem__(self, name, value): """The dictionary like interface for __setattr__().""" self.__setattr__(name, value) def __delitem__(self, name): """The dictionary like interface for __delattr__().""" try: self.__delattr__(name) except AttributeError: # Capture the AttributeError an re-raise a KeyError one raise KeyError( "Attribute ('%s') does not exist in node '%s'" % (name, self._v__nodepath)) def __contains__(self, name): """Is there an attribute with that name? A true value is returned if the attribute set has an attribute with the given name, false otherwise. """ return name in self._v_attrnames def _f_rename(self, oldattrname, newattrname): """Rename an attribute from oldattrname to newattrname.""" if oldattrname == newattrname: # Do nothing return # First, fetch the value of the oldattrname attrvalue = getattr(self, oldattrname) # Now, create the new attribute setattr(self, newattrname, attrvalue) # Finally, remove the old attribute delattr(self, oldattrname) def _g_copy(self, newset, set_attr=None, copyclass=False): """Copy set attributes. Copies all user and allowed system PyTables attributes to the given attribute set, replacing the existing ones. You can specify a *bound* method of the destination set that will be used to set its attributes. Else, its `_g__setattr` method will be used. Changes are logged depending on the chosen setting method. The default setting method does not log anything. .. versionchanged:: 3.0 The *newSet* parameter has been renamed into *newset*. .. versionchanged:: 3.0 The *copyClass* parameter has been renamed into *copyclass*. """ copysysattrs = newset._v__nodefile.params['PYTABLES_SYS_ATTRS'] if set_attr is None: set_attr = newset._g__setattr for attrname in self._v_attrnamesuser: # Do not copy the unimplemented attributes. if attrname not in self._v_unimplemented: set_attr(attrname, getattr(self, attrname)) # Copy the system attributes that we are allowed to. if copysysattrs: for attrname in self._v_attrnamessys: if ((attrname not in SYS_ATTRS_NOTTOBECOPIED) and # Do not copy the FIELD_ attributes in tables as this can # be really *slow* (don't know exactly the reason). # See #304. not attrname.startswith("FIELD_")): set_attr(attrname, getattr(self, attrname)) # Copy CLASS and VERSION attributes if requested if copyclass: for attrname in FORCE_COPY_CLASS: if attrname in self._v_attrnamessys: set_attr(attrname, getattr(self, attrname)) def _f_copy(self, where): """Copy attributes to the where node. Copies all user and certain system attributes to the given where node (a Node instance - see :ref:`NodeClassDescr`), replacing the existing ones. """ # AttributeSet must be defined in order to define a Node. # However, we need to know Node here. # Using class_name_dict avoids a circular import. if not isinstance(where, class_name_dict['Node']): raise TypeError("destination object is not a node: %r" % (where,)) self._g_copy(where._v_attrs, where._v_attrs.__setattr__) def _g_close(self): # Nothing will be done here, as the existing instance is completely # operative now. pass def __str__(self): """The string representation for this object.""" # The pathname pathname = self._v__nodepath # Get this class name classname = self.__class__.__name__ # The attribute names attrnumber = len([n for n in self._v_attrnames]) return "%s._v_attrs (%s), %s attributes" % \ (pathname, classname, attrnumber) def __repr__(self): """A detailed string representation for this object.""" # print additional info only if there are attributes to show attrnames = [n for n in self._v_attrnames] if len(attrnames): rep = ['%s := %r' % (attr, getattr(self, attr)) for attr in attrnames] attrlist = '[%s]' % (',\n '.join(rep)) return "%s:\n %s" % (str(self), attrlist) else: return str(self) class NotLoggedAttributeSet(AttributeSet): def _g_log_add(self, name): pass _g_logAdd = previous_api(_g_log_add) def _g_del_and_log(self, name): self._g__delattr(name) _g_delAndLog = previous_api(_g_del_and_log) ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/carray.py000066400000000000000000000242731231437614300166240ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: June 15, 2005 # Author: Antonio Valentino # Modified by: Francesc Alted # # $Id$ # ######################################################################## """Here is defined the CArray class.""" import sys import numpy from tables.atom import Atom from tables.array import Array from tables.utils import correct_byteorder, SizeType from tables._past import previous_api, previous_api_property # default version for CARRAY objects # obversion = "1.0" # Support for time & enumerated datatypes. obversion = "1.1" # Numeric and numarray flavors are gone. class CArray(Array): """This class represents homogeneous datasets in an HDF5 file. The difference between a CArray and a normal Array (see :ref:`ArrayClassDescr`), from which it inherits, is that a CArray has a chunked layout and, as a consequence, it supports compression. You can use datasets of this class to easily save or load arrays to or from disk, with compression support included. CArray includes all the instance variables and methods of Array. Only those with different behavior are mentioned here. Parameters ---------- parentnode The parent :class:`Group` object. .. versionchanged:: 3.0 Renamed from *parentNode* to *parentnode*. name : str The name of this node in its parent group. atom An `Atom` instance representing the *type* and *shape* of the atomic objects to be saved. shape The shape of the new array. title A description for this node (it sets the ``TITLE`` HDF5 attribute on disk). filters An instance of the `Filters` class that provides information about the desired I/O filters to be applied during the life of this object. chunkshape The shape of the data chunk to be read or written in a single HDF5 I/O operation. Filters are applied to those chunks of data. The dimensionality of `chunkshape` must be the same as that of `shape`. If ``None``, a sensible value is calculated (which is recommended). byteorder The byteorder of the data *on disk*, specified as 'little' or 'big'. If this is not specified, the byteorder is that of the platform. Examples -------- See below a small example of the use of the `CArray` class. The code is available in ``examples/carray1.py``:: import numpy import tables fileName = 'carray1.h5' shape = (200, 300) atom = tables.UInt8Atom() filters = tables.Filters(complevel=5, complib='zlib') h5f = tables.open_file(fileName, 'w') ca = h5f.create_carray(h5f.root, 'carray', atom, shape, filters=filters) # Fill a hyperslab in ``ca``. ca[10:60, 20:70] = numpy.ones((50, 50)) h5f.close() # Re-open a read another hyperslab h5f = tables.open_file(fileName) print(h5f) print(h5f.root.carray[8:12, 18:22]) h5f.close() The output for the previous script is something like:: carray1.h5 (File) '' Last modif.: 'Thu Apr 12 10:15:38 2007' Object Tree: / (RootGroup) '' /carray (CArray(200, 300), shuffle, zlib(5)) '' [[0 0 0 0] [0 0 0 0] [0 0 1 1] [0 0 1 1]] """ # Class identifier. _c_classid = 'CARRAY' _c_classId = previous_api_property('_c_classid') # Properties # ~~~~~~~~~~ # Special methods # ~~~~~~~~~~~~~~~ def __init__(self, parentnode, name, atom=None, shape=None, title="", filters=None, chunkshape=None, byteorder=None, _log=True): self.atom = atom """An `Atom` instance representing the shape, type of the atomic objects to be saved. """ self.shape = None """The shape of the stored array.""" self.extdim = -1 # `CArray` objects are not enlargeable by default """The index of the enlargeable dimension.""" # Other private attributes self._v_version = None """The object version of this array.""" self._v_new = new = atom is not None """Is this the first time the node has been created?""" self._v_new_title = title """New title for this node.""" self._v_convert = True """Whether the ``Array`` object must be converted or not.""" self._v_chunkshape = chunkshape """Private storage for the `chunkshape` property of the leaf.""" # Miscellaneous iteration rubbish. self._start = None """Starting row for the current iteration.""" self._stop = None """Stopping row for the current iteration.""" self._step = None """Step size for the current iteration.""" self._nrowsread = None """Number of rows read up to the current state of iteration.""" self._startb = None """Starting row for current buffer.""" self._stopb = None """Stopping row for current buffer. """ self._row = None """Current row in iterators (sentinel).""" self._init = False """Whether we are in the middle of an iteration or not (sentinel).""" self.listarr = None """Current buffer in iterators.""" if new: if not isinstance(atom, Atom): raise ValueError("atom parameter should be an instance of " "tables.Atom and you passed a %s." % type(atom)) if shape is None: raise ValueError("you must specify a non-empty shape") try: shape = tuple(shape) except TypeError: raise TypeError("`shape` parameter must be a sequence " "and you passed a %s" % type(shape)) self.shape = tuple(SizeType(s) for s in shape) if chunkshape is not None: try: chunkshape = tuple(chunkshape) except TypeError: raise TypeError( "`chunkshape` parameter must be a sequence " "and you passed a %s" % type(chunkshape)) if len(shape) != len(chunkshape): raise ValueError("the shape (%s) and chunkshape (%s) " "ranks must be equal." % (shape, chunkshape)) elif min(chunkshape) < 1: raise ValueError("chunkshape parameter cannot have " "zero-dimensions.") self._v_chunkshape = tuple(SizeType(s) for s in chunkshape) # The `Array` class is not abstract enough! :( super(Array, self).__init__(parentnode, name, new, filters, byteorder, _log) def _g_create(self): """Create a new array in file (specific part).""" if min(self.shape) < 1: raise ValueError( "shape parameter cannot have zero-dimensions.") # Finish the common part of creation process return self._g_create_common(self.nrows) def _g_create_common(self, expectedrows): """Create a new array in file (common part).""" self._v_version = obversion if self._v_chunkshape is None: # Compute the optimal chunk size self._v_chunkshape = self._calc_chunkshape( expectedrows, self.rowsize, self.atom.size) # Compute the optimal nrowsinbuf self.nrowsinbuf = self._calc_nrowsinbuf() # Correct the byteorder if needed if self.byteorder is None: self.byteorder = correct_byteorder(self.atom.type, sys.byteorder) try: # ``self._v_objectid`` needs to be set because would be # needed for setting attributes in some descendants later # on self._v_objectid = self._create_carray(self._v_new_title) except: # XXX # Problems creating the Array on disk. Close node and re-raise. self.close(flush=0) raise return self._v_objectid def _g_copy_with_stats(self, group, name, start, stop, step, title, filters, chunkshape, _log, **kwargs): """Private part of Leaf.copy() for each kind of leaf.""" (start, stop, step) = self._process_range_read(start, stop, step) maindim = self.maindim shape = list(self.shape) shape[maindim] = len(xrange(start, stop, step)) # Now, fill the new carray with values from source nrowsinbuf = self.nrowsinbuf # The slices parameter for self.__getitem__ slices = [slice(0, dim, 1) for dim in self.shape] # This is a hack to prevent doing unnecessary conversions # when copying buffers self._v_convert = False # Build the new CArray object object = CArray(group, name, atom=self.atom, shape=shape, title=title, filters=filters, chunkshape=chunkshape, _log=_log) # Start the copy itself for start2 in xrange(start, stop, step * nrowsinbuf): # Save the records on disk stop2 = start2 + step * nrowsinbuf if stop2 > stop: stop2 = stop # Set the proper slice in the main dimension slices[maindim] = slice(start2, stop2, step) start3 = (start2 - start) // step stop3 = start3 + nrowsinbuf if stop3 > shape[maindim]: stop3 = shape[maindim] # The next line should be generalised if, in the future, # maindim is designed to be different from 0 in CArrays. # See ticket #199. object[start3:stop3] = self.__getitem__(tuple(slices)) # Activate the conversion again (default) self._v_convert = True nbytes = numpy.prod(self.shape, dtype=SizeType) * self.atom.size return (object, nbytes) _g_copyWithStats = previous_api(_g_copy_with_stats) PyTables-v.3.1.1/tables/conditions.py000066400000000000000000000373041231437614300175130ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: 2006-09-19 # Author: Ivan Vilata i Balaguer -- ivan@selidor.net # :Notes: Heavily modified by Francesc Alted for multi-index support. # 2008-04-09 # Combined common & pro version. # 2011-06-04 # # $Id$ # ######################################################################## """Utility functions and classes for supporting query conditions. Classes: `CompileCondition` Container for a compiled condition. Functions: `compile_condition` Compile a condition and extract usable index conditions. `call_on_recarr` Evaluate a function over a structured array. """ import re from numexpr.necompiler import typecode_to_kind from numexpr.necompiler import expressionToAST, typeCompileAst from numexpr.necompiler import stringToExpression, NumExpr from numexpr.expressions import ExpressionNode from tables.utilsextension import get_nested_field from tables.utils import lazyattr _no_matching_opcode = re.compile(r"[^a-z]([a-z]+)_([a-z]+)[^a-z]") # E.g. "gt" and "bfc" from "couldn't find matching opcode for 'gt_bfc'". def _unsupported_operation_error(exception): """Make the \"no matching opcode\" Numexpr `exception` more clear. A new exception of the same kind is returned. """ message = exception.args[0] op, types = _no_matching_opcode.search(message).groups() newmessage = "unsupported operand types for *%s*: " % op newmessage += ', '.join([typecode_to_kind[t] for t in types[1:]]) return exception.__class__(newmessage) def _check_indexable_cmp(getidxcmp): """Decorate `getidxcmp` to check the returned indexable comparison. This does some extra checking that Numexpr would perform later on the comparison if it was compiled within a complete condition. """ def newfunc(exprnode, indexedcols): result = getidxcmp(exprnode, indexedcols) if result[0] is not None: try: typeCompileAst(expressionToAST(exprnode)) except NotImplementedError as nie: # Try to make this Numexpr error less cryptic. raise _unsupported_operation_error(nie) return result newfunc.__name__ = getidxcmp.__name__ newfunc.__doc__ = getidxcmp.__doc__ return newfunc @_check_indexable_cmp def _get_indexable_cmp(exprnode, indexedcols): """Get the indexable variable-constant comparison in `exprnode`. A tuple of (variable, operation, constant) is returned if `exprnode` is a variable-constant (or constant-variable) comparison, and the variable is in `indexedcols`. A normal variable can also be used instead of a constant: a tuple with its name will appear instead of its value. Otherwise, the values in the tuple are ``None``. """ not_indexable = (None, None, None) turncmp = {'lt': 'gt', 'le': 'ge', 'eq': 'eq', 'ge': 'le', 'gt': 'lt', } def get_cmp(var, const, op): var_value, const_value = var.value, const.value if (var.astType == 'variable' and var_value in indexedcols and const.astType in ['constant', 'variable']): if const.astType == 'variable': const_value = (const_value, ) return (var_value, op, const_value) return None def is_indexed_boolean(node): return (node.astType == 'variable' and node.astKind == 'bool' and node.value in indexedcols) # Boolean variables are indexable by themselves. if is_indexed_boolean(exprnode): return (exprnode.value, 'eq', True) # And so are negations of boolean variables. if exprnode.astType == 'op' and exprnode.value == 'invert': child = exprnode.children[0] if is_indexed_boolean(child): return (child.value, 'eq', False) # A negation of an expression will be returned as ``~child``. # The indexability of the negated expression will be decided later on. if child.astKind == "bool": return (child, 'invert', None) # Check node type. Only comparisons are indexable from now on. if exprnode.astType != 'op': return not_indexable cmpop = exprnode.value if cmpop not in turncmp: return not_indexable # Look for a variable-constant comparison in both directions. left, right = exprnode.children cmp_ = get_cmp(left, right, cmpop) if cmp_: return cmp_ cmp_ = get_cmp(right, left, turncmp[cmpop]) if cmp_: return cmp_ return not_indexable def _equiv_expr_node(x, y): """Returns whether two ExpressionNodes are equivalent. This is needed because '==' is overridden on ExpressionNode to return a new ExpressionNode. """ if not isinstance(x, ExpressionNode) and not isinstance(y, ExpressionNode): return x == y elif (type(x) is not type(y) or not isinstance(x, ExpressionNode) or not isinstance(y, ExpressionNode) or x.value != y.value or x.astKind != y.astKind or len(x.children) != len(y.children)): return False for xchild, ychild in zip(x.children, y.children): if not _equiv_expr_node(xchild, ychild): return False return True def _get_idx_expr_recurse(exprnode, indexedcols, idxexprs, strexpr): """Here lives the actual implementation of the get_idx_expr() wrapper. 'idxexprs' is a list of expressions in the form ``(var, (ops), (limits))``. 'strexpr' is the indexable expression in string format. These parameters will be received empty (i.e. [], ['']) for the first time and populated during the different recursive calls. Finally, they are returned in the last level to the original wrapper. If 'exprnode' is not indexable, it will return the tuple ([], ['']) so as to signal this. """ not_indexable = ([], ['']) op_conv = { 'and': '&', 'or': '|', 'not': '~', } negcmp = { 'lt': 'ge', 'le': 'gt', 'ge': 'lt', 'gt': 'le', } def fix_invert(idxcmp, exprnode, indexedcols): invert = False # Loop until all leading negations have been dealt with while idxcmp[1] == "invert": invert ^= True # The information about the negated node is in first position exprnode = idxcmp[0] idxcmp = _get_indexable_cmp(exprnode, indexedcols) return idxcmp, exprnode, invert # Indexable variable-constant comparison. idxcmp = _get_indexable_cmp(exprnode, indexedcols) idxcmp, exprnode, invert = fix_invert(idxcmp, exprnode, indexedcols) if idxcmp[0]: if invert: var, op, value = idxcmp if op == 'eq' and value in [True, False]: # ``var`` must be a boolean index. Flip its value. value ^= True else: op = negcmp[op] expr = (var, (op,), (value,)) invert = False else: expr = (idxcmp[0], (idxcmp[1],), (idxcmp[2],)) return [expr] # For now negations of complex expressions will be not supported as # forming part of an indexable condition. This might be supported in # the future. if invert: return not_indexable # Only conjunctions and disjunctions of comparisons are considered # for the moment. if exprnode.astType != 'op' or exprnode.value not in ['and', 'or']: return not_indexable left, right = exprnode.children # Get the expression at left lcolvar, lop, llim = _get_indexable_cmp(left, indexedcols) # Get the expression at right rcolvar, rop, rlim = _get_indexable_cmp(right, indexedcols) # Use conjunction of indexable VC comparisons like # ``(a <[=] x) & (x <[=] b)`` or ``(a >[=] x) & (x >[=] b)`` # as ``a <[=] x <[=] b``, for the moment. op = exprnode.value if (lcolvar is not None and rcolvar is not None and _equiv_expr_node(lcolvar, rcolvar) and op == 'and'): if lop in ['gt', 'ge'] and rop in ['lt', 'le']: # l <= x <= r expr = (lcolvar, (lop, rop), (llim, rlim)) return [expr] if lop in ['lt', 'le'] and rop in ['gt', 'ge']: # l >= x >= r expr = (rcolvar, (rop, lop), (rlim, llim)) return [expr] # Recursively get the expressions at the left and the right lexpr = _get_idx_expr_recurse(left, indexedcols, idxexprs, strexpr) rexpr = _get_idx_expr_recurse(right, indexedcols, idxexprs, strexpr) def add_expr(expr, idxexprs, strexpr): """Add a single expression to the list.""" if isinstance(expr, list): # expr is a single expression idxexprs.append(expr[0]) lenexprs = len(idxexprs) # Mutate the strexpr string if lenexprs == 1: strexpr[:] = ["e0"] else: strexpr[:] = [ "(%s %s e%d)" % (strexpr[0], op_conv[op], lenexprs - 1)] # Add expressions to the indexable list when they are and'ed, or # they are both indexable. if lexpr != not_indexable and (op == "and" or rexpr != not_indexable): add_expr(lexpr, idxexprs, strexpr) if rexpr != not_indexable: add_expr(rexpr, idxexprs, strexpr) return (idxexprs, strexpr) if rexpr != not_indexable and op == "and": add_expr(rexpr, idxexprs, strexpr) return (idxexprs, strexpr) # Can not use indexed column. return not_indexable def _get_idx_expr(expr, indexedcols): """Extract an indexable expression out of `exprnode`. Looks for variable-constant comparisons in the expression node `exprnode` involving variables in `indexedcols`. It returns a tuple of (idxexprs, strexpr) where 'idxexprs' is a list of expressions in the form ``(var, (ops), (limits))`` and 'strexpr' is the indexable expression in string format. Expressions such as ``0 < c1 <= 1`` do not work as expected. Right now only some of the *indexable comparisons* are considered: * ``a <[=] x``, ``a == x`` and ``a >[=] x`` * ``(a <[=] x) & (y <[=] b)`` and ``(a == x) | (b == y)`` * ``~(~c_bool)``, ``~~c_bool`` and ``~(~c_bool) & (c_extra != 2)`` (where ``a``, ``b`` and ``c_bool`` are indexed columns, but ``c_extra`` is not) Particularly, the ``!=`` operator and negations of complex boolean expressions are *not considered* as valid candidates: * ``a != 1`` and ``c_bool != False`` * ``~((a > 0) & (c_bool))`` """ return _get_idx_expr_recurse(expr, indexedcols, [], ['']) class CompiledCondition(object): """Container for a compiled condition.""" # Lazy attributes # ``````````````` @lazyattr def index_variables(self): """The columns participating in the index expression.""" idxexprs = self.index_expressions idxvars = [] for expr in idxexprs: idxvar = expr[0] if idxvar not in idxvars: idxvars.append(idxvar) return frozenset(idxvars) def __init__(self, func, params, idxexprs, strexpr): self.function = func """The compiled function object corresponding to this condition.""" self.parameters = params """A list of parameter names for this condition.""" self.index_expressions = idxexprs """A list of expressions in the form ``(var, (ops), (limits))``.""" self.string_expression = strexpr """The indexable expression in string format.""" def __repr__(self): return ("idxexprs: %s\nstrexpr: %s\nidxvars: %s" % (self.index_expressions, self.string_expression, self.index_variables)) def with_replaced_vars(self, condvars): """Replace index limit variables with their values in-place. A new compiled condition is returned. Values are taken from the `condvars` mapping and converted to Python scalars. """ exprs = self.index_expressions exprs2 = [] for expr in exprs: idxlims = expr[2] # the limits are in third place limit_values = [] for idxlim in idxlims: if isinstance(idxlim, tuple): # variable idxlim = condvars[idxlim[0]] # look up value idxlim = idxlim.tolist() # convert back to Python limit_values.append(idxlim) # Add this replaced entry to the new exprs2 var, ops, _ = expr exprs2.append((var, ops, tuple(limit_values))) # Create a new container for the converted values newcc = CompiledCondition( self.function, self.parameters, exprs2, self.string_expression) return newcc def _get_variable_names(expression): """Return the list of variable names in the Numexpr `expression`.""" names = [] stack = [expression] while stack: node = stack.pop() if node.astType == 'variable': names.append(node.value) elif hasattr(node, 'children'): stack.extend(node.children) return list(set(names)) # remove repeated names def compile_condition(condition, typemap, indexedcols): """Compile a condition and extract usable index conditions. Looks for variable-constant comparisons in the `condition` string involving the indexed columns whose variable names appear in `indexedcols`. The part of `condition` having usable indexes is returned as a compiled condition in a `CompiledCondition` container. Expressions such as '0 < c1 <= 1' do not work as expected. The Numexpr types of *all* variables must be given in the `typemap` mapping. The ``function`` of the resulting `CompiledCondition` instance is a Numexpr function object, and the ``parameters`` list indicates the order of its parameters. """ # Get the expression tree and extract index conditions. expr = stringToExpression(condition, typemap, {}) if expr.astKind != 'bool': raise TypeError("condition ``%s`` does not have a boolean type" % condition) idxexprs = _get_idx_expr(expr, indexedcols) # Post-process the answer if isinstance(idxexprs, list): # Simple expression strexpr = ['e0'] else: # Complex expression idxexprs, strexpr = idxexprs # Get rid of the unneccessary list wrapper for strexpr strexpr = strexpr[0] # Get the variable names used in the condition. # At the same time, build its signature. varnames = _get_variable_names(expr) signature = [(var, typemap[var]) for var in varnames] try: # See the comments in `numexpr.evaluate()` for the # reasons of inserting copy operators for unaligned, # *unidimensional* arrays. func = NumExpr(expr, signature) except NotImplementedError as nie: # Try to make this Numexpr error less cryptic. raise _unsupported_operation_error(nie) params = varnames # This is more comfortable to handle about than a tuple. return CompiledCondition(func, params, idxexprs, strexpr) def call_on_recarr(func, params, recarr, param2arg=None): """Call `func` with `params` over `recarr`. The `param2arg` function, when specified, is used to get an argument given a parameter name; otherwise, the parameter itself is used as an argument. When the argument is a `Column` object, the proper column from `recarr` is used as its value. """ args = [] for param in params: if param2arg: arg = param2arg(param) else: arg = param if hasattr(arg, 'pathname'): # looks like a column arg = get_nested_field(recarr, arg.pathname) args.append(arg) return func(*args) PyTables-v.3.1.1/tables/definitions.pxd000066400000000000000000000452331231437614300200200ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: June 20, 2005 # Author: Francesc Alted - faltet@pytables.com # # $Id: definitions.pyd 1018 2005-06-20 09:43:34Z faltet $ # ######################################################################## """Here are some definitions for sharing between extensions.""" import sys cdef extern from *: ctypedef char const_char 'const char' #ctypedef long size_t ctypedef long uintptr_t # Standard C functions. cdef extern from "time.h": ctypedef int time_t from libc.stdio cimport FILE #----------------------------------------------------------------------------- # API for NumPy objects from numpy cimport dtype cdef extern from "numpy/arrayobject.h": object PyArray_Scalar(void *data, dtype descr, object itemsize) #----------------------------------------------------------------------------- # Structs and types from HDF5 cdef extern from "hdf5.h" nogil: ctypedef int hid_t # In H5Ipublic.h ctypedef int hbool_t ctypedef int herr_t ctypedef int htri_t # hsize_t should be unsigned, but Windows platform does not support # such an unsigned long long type. ctypedef long long hsize_t ctypedef signed long long hssize_t ctypedef long long int64_t ctypedef long long haddr_t ctypedef struct hvl_t: size_t len # Length of VL data (in base type units) void *p # Pointer to VL data int H5F_ACC_TRUNC, H5F_ACC_RDONLY, H5F_ACC_RDWR, H5F_ACC_EXCL int H5F_ACC_DEBUG, H5F_ACC_CREAT int H5P_DEFAULT, H5P_DATASET_XFER, H5S_ALL int H5P_FILE_CREATE, H5P_FILE_ACCESS int H5FD_LOG_LOC_WRITE, H5FD_LOG_ALL int H5I_INVALID_HID int H5E_DEFAULT # The difference between a single file and a set of mounted files cdef enum H5F_scope_t: H5F_SCOPE_LOCAL = 0 # specified file handle only H5F_SCOPE_GLOBAL = 1 # entire virtual file H5F_SCOPE_DOWN = 2 # for internal use only cdef enum H5FD_mem_t: H5FD_MEM_NOLIST = -1, # Data should not appear in the free list. # Must be negative. H5FD_MEM_DEFAULT = 0, # Value not yet set. Can also be the # datatype set in a larger allocation # that will be suballocated by the library. # Must be zero. H5FD_MEM_SUPER = 1, # Superblock data H5FD_MEM_BTREE = 2, # B-tree data H5FD_MEM_DRAW = 3, # Raw data (content of datasets, etc.) H5FD_MEM_GHEAP = 4, # Global heap data H5FD_MEM_LHEAP = 5, # Local heap data H5FD_MEM_OHDR = 6, # Object header data H5FD_MEM_NTYPES # Sentinel value - must be last cdef enum H5O_type_t: H5O_TYPE_UNKNOWN = -1 # Unknown object type H5O_TYPE_GROUP # Object is a group H5O_TYPE_DATASET # Object is a dataset H5O_TYPE_NAMED_DATATYPE # Object is a named data type cdef enum H5L_type_t: H5L_TYPE_ERROR = -1 # Invalid link type id H5L_TYPE_HARD = 0 # Hard link id H5L_TYPE_SOFT = 1 # Soft link id H5L_TYPE_EXTERNAL = 64, # External link id # Values for fill value status cdef enum H5D_fill_value_t: H5D_FILL_VALUE_ERROR = -1 H5D_FILL_VALUE_UNDEFINED = 0 H5D_FILL_VALUE_DEFAULT = 1 H5D_FILL_VALUE_USER_DEFINED = 2 # HDF5 layouts cdef enum H5D_layout_t: H5D_LAYOUT_ERROR = -1 H5D_COMPACT = 0 # raw data is very small H5D_CONTIGUOUS = 1 # the default H5D_CHUNKED = 2 # slow and fancy H5D_NLAYOUTS = 3 # this one must be last! # Byte orders cdef enum H5T_order_t: H5T_ORDER_ERROR = -1 # error H5T_ORDER_LE = 0 # little endian H5T_ORDER_BE = 1 # bit endian H5T_ORDER_VAX = 2 # VAX mixed endian H5T_ORDER_NONE = 3 # no particular order (strings, bits,..) # HDF5 signed enums cdef enum H5T_sign_t: H5T_SGN_ERROR = -1 # error H5T_SGN_NONE = 0 # this is an unsigned type H5T_SGN_2 = 1 # two's complement H5T_NSGN = 2 # this must be last! # HDF5 type classes cdef enum H5T_class_t: H5T_NO_CLASS = -1 # error H5T_INTEGER = 0 # integer types H5T_FLOAT = 1 # floating-point types H5T_TIME = 2 # date and time types H5T_STRING = 3 # character string types H5T_BITFIELD = 4 # bit field types H5T_OPAQUE = 5 # opaque types H5T_COMPOUND = 6 # compound types H5T_REFERENCE = 7 # reference types H5T_ENUM = 8 # enumeration types H5T_VLEN = 9 # variable-length types H5T_ARRAY = 10 # array types H5T_NCLASSES # this must be last # Native types cdef enum: H5T_C_S1 H5T_NATIVE_B8 H5T_NATIVE_CHAR H5T_NATIVE_SCHAR H5T_NATIVE_UCHAR H5T_NATIVE_SHORT H5T_NATIVE_USHORT H5T_NATIVE_INT H5T_NATIVE_UINT H5T_NATIVE_LONG H5T_NATIVE_ULONG H5T_NATIVE_LLONG H5T_NATIVE_ULLONG H5T_NATIVE_FLOAT H5T_NATIVE_DOUBLE H5T_NATIVE_LDOUBLE # "Standard" types cdef enum: H5T_STD_I8LE H5T_STD_I16LE H5T_STD_I32LE H5T_STD_I64LE H5T_STD_U8LE H5T_STD_U16LE H5T_STD_U32LE H5T_STD_U64LE H5T_STD_B8LE H5T_STD_B16LE H5T_STD_B32LE H5T_STD_B64LE H5T_IEEE_F32LE H5T_IEEE_F64LE H5T_STD_I8BE H5T_STD_I16BE H5T_STD_I32BE H5T_STD_I64BE H5T_STD_U8BE H5T_STD_U16BE H5T_STD_U32BE H5T_STD_U64BE H5T_STD_B8BE H5T_STD_B16BE H5T_STD_B32BE H5T_STD_B64BE H5T_IEEE_F32BE H5T_IEEE_F64BE # Types which are particular to UNIX (for Time types) cdef enum: H5T_UNIX_D32LE H5T_UNIX_D64LE H5T_UNIX_D32BE H5T_UNIX_D64BE # The order to retrieve atomic native datatype cdef enum H5T_direction_t: H5T_DIR_DEFAULT = 0 # default direction is inscendent H5T_DIR_ASCEND = 1 # in inscendent order H5T_DIR_DESCEND = 2 # in descendent order # Codes for defining selections cdef enum H5S_seloper_t: H5S_SELECT_NOOP = -1 H5S_SELECT_SET = 0 H5S_SELECT_OR H5S_SELECT_AND H5S_SELECT_XOR H5S_SELECT_NOTB H5S_SELECT_NOTA H5S_SELECT_APPEND H5S_SELECT_PREPEND H5S_SELECT_INVALID # Must be the last one # Character set to use for text strings cdef enum H5T_cset_t: H5T_CSET_ERROR = -1 # error H5T_CSET_ASCII = 0 # US ASCII H5T_CSET_UTF8 = 1 # UTF-8 Unicode encoding H5T_CSET_RESERVED_2 = 2 H5T_CSET_RESERVED_3 = 3 H5T_CSET_RESERVED_4 = 4 H5T_CSET_RESERVED_5 = 5 H5T_CSET_RESERVED_6 = 6 H5T_CSET_RESERVED_7 = 7 H5T_CSET_RESERVED_8 = 8 H5T_CSET_RESERVED_9 = 9 H5T_CSET_RESERVED_10 = 10 H5T_CSET_RESERVED_11 = 11 H5T_CSET_RESERVED_12 = 12 H5T_CSET_RESERVED_13 = 13 H5T_CSET_RESERVED_14 = 14 H5T_CSET_RESERVED_15 = 15 # Error stack traversal direction cdef enum H5E_direction_t: H5E_WALK_UPWARD = 0 # begin deep, end at API function H5E_WALK_DOWNWARD = 1 # begin at API function, end deep cdef enum H5E_type_t: H5E_MAJOR H5E_MINOR ctypedef struct H5E_error_t: hid_t cls_id # class ID hid_t maj_num # major error ID hid_t min_num # minor error number unsigned line # line in file where error occurs const_char *func_name # function in which error occurred const_char *file_name # file in which error occurred const_char *desc # optional supplied description ctypedef herr_t (*H5E_walk_t)(unsigned n, H5E_error_t *err, void *data) ctypedef herr_t (*H5E_auto_t)(hid_t estack, void *data) #------------------------------------------------------------------ # HDF5 API # Version functions herr_t H5get_libversion(unsigned *majnum, unsigned *minnum, unsigned *relnum ) herr_t H5check_version(unsigned majnum, unsigned minnum, unsigned relnum ) # Operations with files hid_t H5Fcreate(char *filename, unsigned int flags, hid_t create_plist, hid_t access_plist) hid_t H5Fopen(char *name, unsigned flags, hid_t access_id) herr_t H5Fclose (hid_t file_id) htri_t H5Fis_hdf5(char *name) herr_t H5Fflush(hid_t object_id, H5F_scope_t scope) herr_t H5Fget_vfd_handle(hid_t file_id, hid_t fapl_id, void **file_handle) ssize_t H5Fget_file_image(hid_t file_id, void *buf_ptr, size_t buf_len) herr_t H5Fget_filesize(hid_t file_id, hsize_t *size) hid_t H5Fget_create_plist(hid_t file_id) # Operations with groups hid_t H5Gcreate(hid_t loc_id, char *name, hid_t lcpl_id, hid_t gcpl_id, hid_t gapl_id) hid_t H5Gopen(hid_t loc_id, char *name, hid_t gapl_id) herr_t H5Gclose(hid_t group_id) # Operations with links herr_t H5Ldelete(hid_t file_id, char *name, hid_t lapl_id) herr_t H5Lmove(hid_t src_loc_id, char *src_name, hid_t dst_loc_id, char *dst_name, hid_t lcpl, hid_t lap) # For dealing with datasets hid_t H5Dopen(hid_t file_id, char *name, hid_t dapl_id) herr_t H5Dclose(hid_t dset_id) herr_t H5Dread(hid_t dset_id, hid_t mem_type_id, hid_t mem_space_id, hid_t file_space_id, hid_t plist_id, void *buf) herr_t H5Dwrite(hid_t dset_id, hid_t mem_type_id, hid_t mem_space_id, hid_t file_space_id, hid_t plist_id, void *buf) hid_t H5Dget_type(hid_t dset_id) hid_t H5Dget_space(hid_t dset_id) herr_t H5Dvlen_reclaim(hid_t type_id, hid_t space_id, hid_t plist_id, void *buf) hid_t H5Dget_create_plist(hid_t dataset_id) hsize_t H5Dget_storage_size(hid_t dataset_id) herr_t H5Dvlen_get_buf_size(hid_t dataset_id, hid_t type_id, hid_t space_id, hsize_t *size) # Functions for dealing with dataspaces hid_t H5Screate_simple(int rank, hsize_t dims[], hsize_t maxdims[]) int H5Sget_simple_extent_ndims(hid_t space_id) int H5Sget_simple_extent_dims(hid_t space_id, hsize_t dims[], hsize_t maxdims[]) herr_t H5Sselect_all(hid_t spaceid) herr_t H5Sselect_hyperslab(hid_t space_id, H5S_seloper_t op, hsize_t start[], hsize_t _stride[], hsize_t count[], hsize_t _block[]) herr_t H5Sselect_elements(hid_t space_id, H5S_seloper_t op, size_t num_elements, hsize_t *coord) herr_t H5Sclose(hid_t space_id) # Functions for dealing with datatypes H5T_class_t H5Tget_class(hid_t type_id) hid_t H5Tget_super(hid_t type) H5T_sign_t H5Tget_sign(hid_t type_id) H5T_order_t H5Tget_order(hid_t type_id) size_t H5Tget_size(hid_t type_id) herr_t H5Tset_size(hid_t type_id, size_t size) size_t H5Tget_precision(hid_t dtype_id) herr_t H5Tset_precision(hid_t type_id, size_t prec) hid_t H5Tcreate(H5T_class_t type, size_t size) hid_t H5Tvlen_create(hid_t base_type_id) hid_t H5Tcopy(hid_t type_id) herr_t H5Tclose(hid_t type_id) # Operations defined on string data types htri_t H5Tis_variable_str(hid_t dtype_id) # Operations for compound data types int H5Tget_nmembers(hid_t type_id) char *H5Tget_member_name(hid_t type_id, unsigned membno) hid_t H5Tget_member_type(hid_t type_id, unsigned membno) hid_t H5Tget_native_type(hid_t type_id, H5T_direction_t direction) herr_t H5Tget_member_value(hid_t type_id, int membno, void *value) int H5Tget_offset(hid_t type_id) herr_t H5Tinsert(hid_t parent_id, char *name, size_t offset, hid_t member_id) herr_t H5Tpack(hid_t type_id) # Operations for enumerated data types hid_t H5Tenum_create(hid_t base_id) herr_t H5Tenum_insert(hid_t type, char *name, void *value) # Operations for array data types hid_t H5Tarray_create(hid_t base_id, int ndims, hsize_t dims[]) int H5Tget_array_ndims(hid_t type_id) int H5Tget_array_dims(hid_t type_id, hsize_t dims[]) # Operations with attributes herr_t H5Adelete(hid_t loc_id, char *name) int H5Aget_num_attrs(hid_t loc_id) size_t H5Aget_name(hid_t attr_id, size_t buf_size, char *buf) hid_t H5Aopen_idx(hid_t loc_id, unsigned int idx) herr_t H5Aread(hid_t attr_id, hid_t mem_type_id, void *buf) herr_t H5Aclose(hid_t attr_id) # Operations with properties hid_t H5Pcreate(hid_t plist_id) herr_t H5Pclose(hid_t plist_id) herr_t H5Pset_cache(hid_t plist_id, int mdc_nelmts, int rdcc_nelmts, size_t rdcc_nbytes, double rdcc_w0) herr_t H5Pset_sieve_buf_size(hid_t fapl_id, hsize_t size) H5D_layout_t H5Pget_layout(hid_t plist) int H5Pget_chunk(hid_t plist, int max_ndims, hsize_t *dims) hid_t H5Pget_driver(hid_t plist_id) herr_t H5Pset_fapl_sec2(hid_t fapl_id) #herr_t H5Pget_fapl_direct(hid_t fapl_id, size_t *alignment, # size_t *block_size, size_t *cbuf_size) #herr_t H5Pset_fapl_direct(hid_t fapl_id, size_t alignment, # size_t block_size, size_t cbuf_size) herr_t H5Pset_fapl_log(hid_t fapl_id, const_char *logfile, unsigned long long flags, size_t buf_size) #herr_t H5Pset_fapl_windows(hid_t fapl_id) herr_t H5Pset_fapl_stdio(hid_t fapl_id) #herr_t H5Pget_fapl_core(hid_t fapl_id, size_t *increment, # hbool_t *backing_store) herr_t H5Pset_fapl_core(hid_t fapl_id, size_t increment, hbool_t backing_store) #herr_t H5Pget_fapl_family(hid_t fapl_id, hsize_t *memb_size, # hid_t *memb_fapl_id) herr_t H5Pset_fapl_family(hid_t fapl_id, hsize_t memb_size, hid_t memb_fapl_id) #herr_t H5Pget_fapl_multi(hid_t fapl_id, H5FD_mem_t *memb_map, # hid_t *memb_fapl, const_char **memb_name, # haddr_t *memb_addr, hbool_t *relax) herr_t H5Pset_fapl_multi(hid_t fapl_id, H5FD_mem_t *memb_map, hid_t *memb_fapl, char **memb_name, haddr_t *memb_addr, hbool_t relax) herr_t H5Pset_fapl_split(hid_t fapl_id, char *meta_ext, hid_t meta_plist_id, char *raw_ext, hid_t raw_plist_id) #herr_t H5Pget_fapl_mpio(hid_t fapl_id, MPI_Comm *comm, MPI_Info *info) #herr_t H5Pset_fapl_mpio(hid_t fapl_id, MPI_Comm comm, MPI_Info info) #herr_t H5Pget_fapl_mpiposix(hid_t fapl_id, MPI_Comm *comm, # hbool_t *use_gpfs_hints) #herr_t H5Pset_fapl_mpiposix(hid_t fapl_id, MPI_Comm comm, # hbool_t use_gpfs_hints) herr_t H5Pset_file_image(hid_t fapl_id, void *buf_ptr, size_t buf_len) herr_t H5Pget_userblock(hid_t plist, hsize_t *size) herr_t H5Pset_userblock(hid_t plist, hsize_t size) # Error Handling Interface #herr_t H5Eget_auto(hid_t estack_id, H5E_auto_t *func, void** data) herr_t H5Eset_auto(hid_t estack_id, H5E_auto_t func, void *data) herr_t H5Eprint(hid_t estack_id, FILE *stream) herr_t H5Ewalk(hid_t estack_id, H5E_direction_t dir, H5E_walk_t func, void *data) #hid_t H5Eget_current_stack(void) #herr_t H5Eclose_stack(hid_t estack_id) #ssize_t H5Eget_num(hid_t estack_id) ssize_t H5Eget_msg(hid_t mesg_id, H5E_type_t* mesg_type, char* mesg, size_t size) #herr_t H5Eclose_msg(hid_t mesg_id) #ssize_t H5Eget_class_name(hid_t class_id, char* name, size_t size) # Specific HDF5 functions for PyTables cdef extern from "H5ATTR.h" nogil: herr_t H5ATTRget_attribute(hid_t loc_id, char *attr_name, hid_t type_id, void *data) hsize_t H5ATTRget_attribute_string(hid_t loc_id, char *attr_name, char **attr_value, int *cset) hsize_t H5ATTRget_attribute_vlen_string_array(hid_t loc_id, char *attr_name, char ***attr_value, int *cset) herr_t H5ATTRset_attribute(hid_t obj_id, char *attr_name, hid_t type_id, size_t rank, hsize_t *dims, char *attr_data) herr_t H5ATTRset_attribute_string(hid_t loc_id, char *attr_name, char *attr_data, hsize_t attr_size, int cset) herr_t H5ATTRfind_attribute(hid_t loc_id, char *attr_name) herr_t H5ATTRget_type_ndims(hid_t loc_id, char *attr_name, hid_t *type_id, H5T_class_t *class_id, size_t *type_size, int *rank) herr_t H5ATTRget_dims(hid_t loc_id, char *attr_name, hsize_t *dims) # Functions for operations with ARRAY cdef extern from "H5ARRAY.h" nogil: herr_t H5ARRAYget_ndims(hid_t dataset_id, int *rank) herr_t H5ARRAYget_info(hid_t dataset_id, hid_t type_id, hsize_t *dims, hsize_t *maxdims, H5T_class_t *super_class_id, char *byteorder) # Some utilities cdef extern from "utils.h" nogil: herr_t set_cache_size(hid_t file_id, size_t cache_size) int get_objinfo(hid_t loc_id, char *name) int get_linkinfo(hid_t loc_id, char *name) hsize_t get_len_of_range(hsize_t lo, hsize_t hi, hsize_t step) hid_t create_ieee_float16(char *byteorder) hid_t create_ieee_complex64(char *byteorder) hid_t create_ieee_complex128(char *byteorder) hid_t create_ieee_complex192(char *byteorder) hid_t create_ieee_complex256(char *byteorder) herr_t set_order(hid_t type_id, char *byteorder) herr_t get_order(hid_t type_id, char *byteorder) int is_complex(hid_t type_id) herr_t truncate_dset(hid_t dataset_id, int maindim, hsize_t size) herr_t pt_H5Pset_fapl_direct(hid_t fapl_id, size_t alignment, size_t block_size, size_t cbuf_size) herr_t pt_H5Pset_fapl_windows(hid_t fapl_id) herr_t pt_H5Pset_file_image(hid_t fapl_id, void *buf_ptr, size_t buf_len) ssize_t pt_H5Fget_file_image(hid_t file_id, void *buf_ptr, size_t buf_len) int H5_HAVE_DIRECT_DRIVER, H5_HAVE_WINDOWS_DRIVER, H5_HAVE_IMAGE_FILE cdef extern from "utils.h": object Giterate(hid_t parent_id, hid_t loc_id, char *name) object Aiterate(hid_t loc_id) object H5UIget_info(hid_t loc_id, char *name, char *byteorder) # Type conversion routines cdef extern from "typeconv.h" nogil: void conv_float64_timeval32(void *base, unsigned long byteoffset, unsigned long bytestride, long long nrecords, unsigned long nelements, int sense) # Blosc registration cdef extern from "blosc_filter.h" nogil: int register_blosc(char **version, char **date) PyTables-v.3.1.1/tables/description.py000066400000000000000000001034521231437614300176630ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: September 21, 2002 # Author: Francesc Alted # # $Id$ # ######################################################################## """Classes for describing columns for ``Table`` objects.""" # Imports # ======= from __future__ import print_function import sys import copy import warnings import numpy from tables import atom from tables.path import check_name_validity from tables._past import previous_api, previous_api_property # Public variables # ================ __docformat__ = 'reStructuredText' """The format of documentation strings in this module.""" # Private functions # ================= def same_position(oldmethod): """Decorate `oldmethod` to also compare the `_v_pos` attribute.""" def newmethod(self, other): try: other._v_pos except AttributeError: return False # not a column definition return self._v_pos == other._v_pos and oldmethod(self, other) newmethod.__name__ = oldmethod.__name__ newmethod.__doc__ = oldmethod.__doc__ return newmethod # Column classes # ============== class Col(atom.Atom): """Defines a non-nested column. Col instances are used as a means to declare the different properties of a non-nested column in a table or nested column. Col classes are descendants of their equivalent Atom classes (see :ref:`AtomClassDescr`), but their instances have an additional _v_pos attribute that is used to decide the position of the column inside its parent table or nested column (see the IsDescription class in :ref:`IsDescriptionClassDescr` for more information on column positions). In the same fashion as Atom, you should use a particular Col descendant class whenever you know the exact type you will need when writing your code. Otherwise, you may use one of the Col.from_*() factory methods. Each factory method inherited from the Atom class is available with the same signature, plus an additional pos parameter (placed in last position) which defaults to None and that may take an integer value. This parameter might be used to specify the position of the column in the table. Besides, there are the next additional factory methods, available only for Col objects. The following parameters are available for most Col-derived constructors. Parameters ---------- itemsize : int For types with a non-fixed size, this sets the size in bytes of individual items in the column. shape : tuple Sets the shape of the column. An integer shape of N is equivalent to the tuple (N,). dflt Sets the default value for the column. pos : int Sets the position of column in table. If unspecified, the position will be randomly selected. """ # Avoid mangling atom class data. __metaclass__ = type _class_from_prefix = {} # filled as column classes are created """Maps column prefixes to column classes.""" # Class methods # ~~~~~~~~~~~~~ @classmethod def prefix(class_): """Return the column class prefix.""" cname = class_.__name__ return cname[:cname.rfind('Col')] @classmethod def from_atom(class_, atom, pos=None): """Create a Col definition from a PyTables atom. An optional position may be specified as the pos argument. """ prefix = atom.prefix() kwargs = atom._get_init_args() colclass = class_._class_from_prefix[prefix] return colclass(pos=pos, **kwargs) @classmethod def from_sctype(class_, sctype, shape=(), dflt=None, pos=None): """Create a `Col` definition from a NumPy scalar type `sctype`. Optional shape, default value and position may be specified as the `shape`, `dflt` and `pos` arguments, respectively. Information in the `sctype` not represented in a `Col` is ignored. """ newatom = atom.Atom.from_sctype(sctype, shape, dflt) return class_.from_atom(newatom, pos=pos) @classmethod def from_dtype(class_, dtype, dflt=None, pos=None): """Create a `Col` definition from a NumPy `dtype`. Optional default value and position may be specified as the `dflt` and `pos` arguments, respectively. The `dtype` must have a byte order which is irrelevant or compatible with that of the system. Information in the `dtype` not represented in a `Col` is ignored. """ newatom = atom.Atom.from_dtype(dtype, dflt) return class_.from_atom(newatom, pos=pos) @classmethod def from_type(class_, type, shape=(), dflt=None, pos=None): """Create a `Col` definition from a PyTables `type`. Optional shape, default value and position may be specified as the `shape`, `dflt` and `pos` arguments, respectively. """ newatom = atom.Atom.from_type(type, shape, dflt) return class_.from_atom(newatom, pos=pos) @classmethod def from_kind(class_, kind, itemsize=None, shape=(), dflt=None, pos=None): """Create a `Col` definition from a PyTables `kind`. Optional item size, shape, default value and position may be specified as the `itemsize`, `shape`, `dflt` and `pos` arguments, respectively. Bear in mind that not all columns support a default item size. """ newatom = atom.Atom.from_kind(kind, itemsize, shape, dflt) return class_.from_atom(newatom, pos=pos) @classmethod def _subclass_from_prefix(class_, prefix): """Get a column subclass for the given `prefix`.""" cname = '%sCol' % prefix class_from_prefix = class_._class_from_prefix if cname in class_from_prefix: return class_from_prefix[cname] atombase = getattr(atom, '%sAtom' % prefix) class NewCol(class_, atombase): """Defines a non-nested column of a particular type. The constructor accepts the same arguments as the equivalent `Atom` class, plus an additional ``pos`` argument for position information, which is assigned to the `_v_pos` attribute. """ def __init__(self, *args, **kwargs): pos = kwargs.pop('pos', None) class_from_prefix = self._class_from_prefix atombase.__init__(self, *args, **kwargs) # The constructor of an abstract atom may have changed # the class of `self` to something different of `NewCol` # and `atombase` (that's why the prefix map is saved). if self.__class__ is not NewCol: colclass = class_from_prefix[self.prefix()] self.__class__ = colclass self._v_pos = pos __eq__ = same_position(atombase.__eq__) _is_equal_to_atom = same_position(atombase._is_equal_to_atom) # XXX: API incompatible change for PyTables 3 line # Overriding __eq__ blocks inheritance of __hash__ in 3.x # def __hash__(self): # return hash((self._v_pos, self.atombase)) if prefix == 'Enum': _is_equal_to_enumatom = same_position( atombase._is_equal_to_enumatom) NewCol.__name__ = cname class_from_prefix[prefix] = NewCol return NewCol # Special methods # ~~~~~~~~~~~~~~~ def __repr__(self): # Reuse the atom representation. atomrepr = super(Col, self).__repr__() lpar = atomrepr.index('(') rpar = atomrepr.rindex(')') atomargs = atomrepr[lpar + 1:rpar] classname = self.__class__.__name__ return '%s(%s, pos=%s)' % (classname, atomargs, self._v_pos) # Private methods # ~~~~~~~~~~~~~~~ def _get_init_args(self): """Get a dictionary of instance constructor arguments.""" kwargs = dict((arg, getattr(self, arg)) for arg in ('shape', 'dflt')) kwargs['pos'] = getattr(self, '_v_pos', None) return kwargs def _generate_col_classes(): """Generate all column classes.""" # Abstract classes are not in the class map. cprefixes = ['Int', 'UInt', 'Float', 'Time'] for (kind, kdata) in atom.atom_map.iteritems(): if hasattr(kdata, 'kind'): # atom class: non-fixed item size atomclass = kdata cprefixes.append(atomclass.prefix()) else: # dictionary: fixed item size for atomclass in kdata.itervalues(): cprefixes.append(atomclass.prefix()) # Bottom-level complex classes are not in the type map, of course. # We still want the user to get the compatibility warning, though. cprefixes.extend(['Complex32', 'Complex64', 'Complex128']) if hasattr(atom, 'Complex192Atom'): cprefixes.append('Complex192') if hasattr(atom, 'Complex256Atom'): cprefixes.append('Complex256') for cprefix in cprefixes: newclass = Col._subclass_from_prefix(cprefix) yield newclass # Create all column classes. for _newclass in _generate_col_classes(): exec('%s = _newclass' % _newclass.__name__) del _newclass # Table description classes # ========================= class Description(object): """This class represents descriptions of the structure of tables. An instance of this class is automatically bound to Table (see :ref:`TableClassDescr`) objects when they are created. It provides a browseable representation of the structure of the table, made of non-nested (Col - see :ref:`ColClassDescr`) and nested (Description) columns. Column definitions under a description can be accessed as attributes of it (*natural naming*). For instance, if table.description is a Description instance with a column named col1 under it, the later can be accessed as table.description.col1. If col1 is nested and contains a col2 column, this can be accessed as table.description.col1.col2. Because of natural naming, the names of members start with special prefixes, like in the Group class (see :ref:`GroupClassDescr`). .. rubric:: Description attributes .. attribute:: _v_colobjects A dictionary mapping the names of the columns hanging directly from the associated table or nested column to their respective descriptions (Col - see :ref:`ColClassDescr` or Description - see :ref:`DescriptionClassDescr` instances). .. versionchanged:: 3.0 The *_v_colObjects* attobute has been renamed into *_v_colobjects*. .. attribute:: _v_dflts A dictionary mapping the names of non-nested columns hanging directly from the associated table or nested column to their respective default values. .. attribute:: _v_dtype The NumPy type which reflects the structure of this table or nested column. You can use this as the dtype argument of NumPy array factories. .. attribute:: _v_dtypes A dictionary mapping the names of non-nested columns hanging directly from the associated table or nested column to their respective NumPy types. .. attribute:: _v_is_nested Whether the associated table or nested column contains further nested columns or not. .. attribute:: _v_itemsize The size in bytes of an item in this table or nested column. .. attribute:: _v_name The name of this description group. The name of the root group is '/'. .. attribute:: _v_names A list of the names of the columns hanging directly from the associated table or nested column. The order of the names matches the order of their respective columns in the containing table. .. attribute:: _v_nested_descr A nested list of pairs of (name, format) tuples for all the columns under this table or nested column. You can use this as the dtype and descr arguments of NumPy array factories. .. versionchanged:: 3.0 The *_v_nestedDescr* attribute has been renamed into *_v_nested_descr*. .. attribute:: _v_nested_formats A nested list of the NumPy string formats (and shapes) of all the columns under this table or nested column. You can use this as the formats argument of NumPy array factories. .. versionchanged:: 3.0 The *_v_nestedFormats* attribute has been renamed into *_v_nested_formats*. .. attribute:: _v_nestedlvl The level of the associated table or nested column in the nested datatype. .. attribute:: _v_nested_names A nested list of the names of all the columns under this table or nested column. You can use this as the names argument of NumPy array factories. .. versionchanged:: 3.0 The *_v_nestedNames* attribute has been renamed into *_v_nested_names*. .. attribute:: _v_pathname Pathname of the table or nested column. .. attribute:: _v_pathnames A list of the pathnames of all the columns under this table or nested column (in preorder). If it does not contain nested columns, this is exactly the same as the :attr:`Description._v_names` attribute. .. attribute:: _v_types A dictionary mapping the names of non-nested columns hanging directly from the associated table or nested column to their respective PyTables types. """ _v_colObjects = previous_api_property('_v_colobjects') _v_nestedFormats = previous_api_property('_v_nested_formats') _v_nestedNames = previous_api_property('_v_nested_names') _v_nestedDesct = previous_api_property('_v_nested_descr') def __init__(self, classdict, nestedlvl=-1, validate=True): if not classdict: raise ValueError("cannot create an empty data type") # Do a shallow copy of classdict just in case this is going to # be shared by other instances newdict = self.__dict__ newdict["_v_name"] = "/" # The name for root descriptor newdict["_v_names"] = [] newdict["_v_dtypes"] = {} newdict["_v_types"] = {} newdict["_v_dflts"] = {} newdict["_v_colobjects"] = {} newdict["_v_is_nested"] = False nestedFormats = [] nestedDType = [] if not hasattr(newdict, "_v_nestedlvl"): newdict["_v_nestedlvl"] = nestedlvl + 1 cols_with_pos = [] # colum (position, name) pairs cols_no_pos = [] # just column names # Check for special variables and convert column descriptions for (name, descr) in classdict.iteritems(): if name.startswith('_v_'): if name in newdict: # print("Warning!") # special methods &c: copy to newdict, warn about conflicts warnings.warn("Can't set attr %r in description class %r" % (name, self)) else: # print("Special variable!-->", name, classdict[name]) newdict[name] = descr continue # This variable is not needed anymore columns = None if (type(descr) == type(IsDescription) and issubclass(descr, IsDescription)): # print("Nested object (type I)-->", name) columns = descr().columns elif (type(descr.__class__) == type(IsDescription) and issubclass(descr.__class__, IsDescription)): # print("Nested object (type II)-->", name) columns = descr.columns elif isinstance(descr, dict): # print("Nested object (type III)-->", name) columns = descr else: # print("Nested object (type IV)-->", name) descr = copy.copy(descr) # The copies above and below ensure that the structures # provided by the user will remain unchanged even if we # tamper with the values of ``_v_pos`` here. if columns is not None: descr = Description(copy.copy(columns), self._v_nestedlvl) classdict[name] = descr pos = getattr(descr, '_v_pos', None) if pos is None: cols_no_pos.append(name) else: cols_with_pos.append((pos, name)) # Sort field names: # # 1. Fields with explicit positions, according to their # positions (and their names if coincident). # 2. Fields with no position, in alfabetical order. cols_with_pos.sort() cols_no_pos.sort() keys = [name for (pos, name) in cols_with_pos] + cols_no_pos pos = 0 # Get properties for compound types for k in keys: if validate: # Check for key name validity check_name_validity(k) # Class variables object = classdict[k] newdict[k] = object # To allow natural naming if not (isinstance(object, Col) or isinstance(object, Description)): raise TypeError('Passing an incorrect value to a table column.' ' Expected a Col (or subclass) instance and ' 'got: "%s". Please make use of the Col(), or ' 'descendant, constructor to properly ' 'initialize columns.' % object) object._v_pos = pos # Set the position of this object object._v_parent = self # The parent description pos += 1 newdict['_v_colobjects'][k] = object newdict['_v_names'].append(k) object.__dict__['_v_name'] = k if not isinstance(k, str): # numpy only accepts "str" for field names if sys.version_info[0] < 3: # Python 2.x: unicode --> str kk = k.encode() # use the default encoding else: # Python 3.x: bytes --> str (unicode) kk = k.decode() else: kk = k if isinstance(object, Col): dtype = object.dtype newdict['_v_dtypes'][k] = dtype newdict['_v_types'][k] = object.type newdict['_v_dflts'][k] = object.dflt nestedFormats.append(object.recarrtype) baserecarrtype = dtype.base.str[1:] nestedDType.append((kk, baserecarrtype, dtype.shape)) else: # A description nestedFormats.append(object._v_nested_formats) nestedDType.append((kk, object._v_dtype)) # Assign the format list to _v_nested_formats newdict['_v_nested_formats'] = nestedFormats newdict['_v_dtype'] = numpy.dtype(nestedDType) # _v_itemsize is derived from the _v_dtype that already computes this newdict['_v_itemsize'] = newdict['_v_dtype'].itemsize if self._v_nestedlvl == 0: # Get recursively nested _v_nested_names and _v_nested_descr attrs self._g_set_nested_names_descr() # Get pathnames for nested groups self._g_set_path_names() # Check the _v_byteorder has been used an issue an Error if hasattr(self, "_v_byteorder"): raise ValueError( "Using a ``_v_byteorder`` in the description is obsolete. " "Use the byteorder parameter in the constructor instead.") def _g_set_nested_names_descr(self): """Computes the nested names and descriptions for nested datatypes.""" names = self._v_names fmts = self._v_nested_formats self._v_nested_names = names[:] # Important to do a copy! self._v_nested_descr = [(names[i], fmts[i]) for i in range(len(names))] for i in range(len(names)): name = names[i] new_object = self._v_colobjects[name] if isinstance(new_object, Description): new_object._g_set_nested_names_descr() # replace the column nested name by a correct tuple self._v_nested_names[i] = (name, new_object._v_nested_names) self._v_nested_descr[i] = (name, new_object._v_nested_descr) # set the _v_is_nested flag self._v_is_nested = True _g_setNestedNamesDescr = previous_api(_g_set_nested_names_descr) def _g_set_path_names(self): """Compute the pathnames for arbitrary nested descriptions. This method sets the ``_v_pathname`` and ``_v_pathnames`` attributes of all the elements (both descriptions and columns) in this nested description. """ def get_cols_in_order(description): return [description._v_colobjects[colname] for colname in description._v_names] def join_paths(path1, path2): if not path1: return path2 return '%s/%s' % (path1, path2) # The top of the stack always has a nested description # and a list of its child columns # (be they nested ``Description`` or non-nested ``Col`` objects). # In the end, the list contains only a list of column paths # under this one. # # For instance, given this top of the stack:: # # (, [, ]) # # After computing the rest of the stack, the top is:: # # (, ['a', 'a/m', 'a/n', ... , 'b', ...]) stack = [] # We start by pushing the top-level description # and its child columns. self._v_pathname = '' stack.append((self, get_cols_in_order(self))) while stack: desc, cols = stack.pop() head = cols[0] # What's the first child in the list? if isinstance(head, Description): # A nested description. We remove it from the list and # push it with its child columns. This will be the next # handled description. head._v_pathname = join_paths(desc._v_pathname, head._v_name) stack.append((desc, cols[1:])) # alter the top stack.append((head, get_cols_in_order(head))) # new top elif isinstance(head, Col): # A non-nested column. We simply remove it from the # list and append its name to it. head._v_pathname = join_paths(desc._v_pathname, head._v_name) cols.append(head._v_name) # alter the top stack.append((desc, cols[1:])) # alter the top else: # Since paths and names are appended *to the end* of # children lists, a string signals that no more children # remain to be processed, so we are done with the # description at the top of the stack. assert isinstance(head, basestring) # Assign the computed set of descendent column paths. desc._v_pathnames = cols if len(stack) > 0: # Compute the paths with respect to the parent node # (including the path of the current description) # and append them to its list. descName = desc._v_name colPaths = [join_paths(descName, path) for path in cols] colPaths.insert(0, descName) parentCols = stack[-1][1] parentCols.extend(colPaths) # (Nothing is pushed, we are done with this description.) _g_setPathNames = previous_api(_g_set_path_names) def _f_walk(self, type='All'): """Iterate over nested columns. If type is 'All' (the default), all column description objects (Col and Description instances) are yielded in top-to-bottom order (preorder). If type is 'Col' or 'Description', only column descriptions of that type are yielded. """ if type not in ["All", "Col", "Description"]: raise ValueError("""\ type can only take the parameters 'All', 'Col' or 'Description'.""") stack = [self] while stack: object = stack.pop(0) # pop at the front so as to ensure the order if type in ["All", "Description"]: yield object # yield description names = object._v_names for i in range(len(names)): new_object = object._v_colobjects[names[i]] if isinstance(new_object, Description): stack.append(new_object) else: if type in ["All", "Col"]: yield new_object # yield column def __repr__(self): """Gives a detailed Description column representation.""" rep = ['%s\"%s\": %r' % (" " * self._v_nestedlvl, k, self._v_colobjects[k]) for k in self._v_names] return '{\n %s}' % (',\n '.join(rep)) def __str__(self): """Gives a brief Description representation.""" return 'Description(%s)' % self._v_nested_descr class MetaIsDescription(type): """Helper metaclass to return the class variables as a dictionary.""" def __new__(cls, classname, bases, classdict): """Return a new class with a "columns" attribute filled.""" newdict = {"columns": {}, } if '__doc__' in classdict: newdict['__doc__'] = classdict['__doc__'] for b in bases: if "columns" in b.__dict__: newdict["columns"].update(b.__dict__["columns"]) for k in classdict: # if not (k.startswith('__') or k.startswith('_v_')): # We let pass _v_ variables to configure class behaviour if not (k.startswith('__')): newdict["columns"][k] = classdict[k] # Return a new class with the "columns" attribute filled return type.__new__(cls, classname, bases, newdict) metaIsDescription = previous_api(MetaIsDescription) class IsDescription(object): """Description of the structure of a table or nested column. This class is designed to be used as an easy, yet meaningful way to describe the structure of new Table (see :ref:`TableClassDescr`) datasets or nested columns through the definition of *derived classes*. In order to define such a class, you must declare it as descendant of IsDescription, with as many attributes as columns you want in your table. The name of each attribute will become the name of a column, and its value will hold a description of it. Ordinary columns can be described using instances of the Col class (see :ref:`ColClassDescr`). Nested columns can be described by using classes derived from IsDescription, instances of it, or name-description dictionaries. Derived classes can be declared in place (in which case the column takes the name of the class) or referenced by name. Nested columns can have a _v_pos special attribute which sets the *relative* position of the column among sibling columns *also having explicit positions*. The pos constructor argument of Col instances is used for the same purpose. Columns with no explicit position will be placed afterwards in alphanumeric order. Once you have created a description object, you can pass it to the Table constructor, where all the information it contains will be used to define the table structure. .. rubric:: IsDescription attributes .. attribute:: _v_pos Sets the position of a possible nested column description among its sibling columns. This attribute can be specified *when declaring* an IsDescription subclass to complement its *metadata*. .. attribute:: columns Maps the name of each column in the description to its own descriptive object. This attribute is *automatically created* when an IsDescription subclass is declared. Please note that declared columns can no longer be accessed as normal class variables after its creation. """ __metaclass__ = MetaIsDescription def descr_from_dtype(dtype_): """Get a description instance and byteorder from a (nested) NumPy dtype.""" fields = {} fbyteorder = '|' for name in dtype_.names: dtype, pos = dtype_.fields[name][:2] kind = dtype.base.kind byteorder = dtype.base.byteorder if byteorder in '><=': if fbyteorder not in ['|', byteorder]: raise NotImplementedError( "structured arrays with mixed byteorders " "are not supported yet, sorry") fbyteorder = byteorder # Non-nested column if kind in 'biufSc': col = Col.from_dtype(dtype, pos=pos) # Nested column elif kind == 'V' and dtype.shape in [(), (1,)]: col, _ = descr_from_dtype(dtype) col._v_pos = pos else: raise NotImplementedError( "structured arrays with columns with type description ``%s`` " "are not supported yet, sorry" % dtype) fields[name] = col return Description(fields), fbyteorder def dtype_from_descr(descr, byteorder=None): """Get a (nested) NumPy dtype from a description instance and byteorder. The descr parameter can be a Description or IsDescription instance, sub-class of IsDescription or a dictionary. """ if isinstance(descr, dict): descr = Description(descr) elif (type(descr) == type(IsDescription) and issubclass(descr, IsDescription)): descr = Description(descr().columns) elif isinstance(descr, IsDescription): descr = Description(descr.columns) elif not isinstance(descr, Description): raise ValueError('invalid description: %r' % descr) dtype_ = descr._v_dtype if byteorder and byteorder != '|': dtype_ = dtype_.newbyteorder(byteorder) return dtype_ if __name__ == "__main__": """Test code.""" class Info(IsDescription): _v_pos = 2 Name = UInt32Col() Value = Float64Col() class Test(IsDescription): """A description that has several columns.""" x = Col.from_type("int32", 2, 0, pos=0) y = Col.from_kind('float', dflt=1, shape=(2, 3)) z = UInt8Col(dflt=1) color = StringCol(2, dflt=" ") # color = UInt32Col(2) Info = Info() class info(IsDescription): _v_pos = 1 name = UInt32Col() value = Float64Col(pos=0) y2 = Col.from_kind('float', dflt=1, shape=(2, 3), pos=1) z2 = UInt8Col(dflt=1) class info2(IsDescription): y3 = Col.from_kind('float', dflt=1, shape=(2, 3)) z3 = UInt8Col(dflt=1) name = UInt32Col() value = Float64Col() class info3(IsDescription): name = UInt32Col() value = Float64Col() y4 = Col.from_kind('float', dflt=1, shape=(2, 3)) z4 = UInt8Col(dflt=1) # class Info(IsDescription): # _v_pos = 2 # Name = StringCol(itemsize=2) # Value = ComplexCol(itemsize=16) # class Test(IsDescription): # """A description that has several columns""" # x = Col.from_type("int32", 2, 0, pos=0) # y = Col.from_kind('float', dflt=1, shape=(2,3)) # z = UInt8Col(dflt=1) # color = StringCol(2, dflt=" ") # Info = Info() # class info(IsDescription): # _v_pos = 1 # name = StringCol(itemsize=2) # value = ComplexCol(itemsize=16, pos=0) # y2 = Col.from_kind('float', dflt=1, shape=(2,3), pos=1) # z2 = UInt8Col(dflt=1) # class info2(IsDescription): # y3 = Col.from_kind('float', dflt=1, shape=(2,3)) # z3 = UInt8Col(dflt=1) # name = StringCol(itemsize=2) # value = ComplexCol(itemsize=16) # class info3(IsDescription): # name = StringCol(itemsize=2) # value = ComplexCol(itemsize=16) # y4 = Col.from_kind('float', dflt=1, shape=(2,3)) # z4 = UInt8Col(dflt=1) # example cases of class Test klass = Test() # klass = Info() desc = Description(klass.columns) print("Description representation (short) ==>", desc) print("Description representation (long) ==>", repr(desc)) print("Column names ==>", desc._v_names) print("Column x ==>", desc.x) print("Column Info ==>", desc.Info) print("Column Info.value ==>", desc.Info.Value) print("Nested column names ==>", desc._v_nested_names) print("Defaults ==>", desc._v_dflts) print("Nested Formats ==>", desc._v_nested_formats) print("Nested Descriptions ==>", desc._v_nested_descr) print("Nested Descriptions (info) ==>", desc.info._v_nested_descr) print("Total size ==>", desc._v_dtype.itemsize) # check _f_walk for object in desc._f_walk(): if isinstance(object, Description): print("******begin object*************", end=' ') print("name -->", object._v_name) # print("name -->", object._v_dtype.name) # print("object childs-->", object._v_names) # print("object nested childs-->", object._v_nested_names) print("totalsize-->", object._v_dtype.itemsize) else: # pass print("leaf -->", object._v_name, object.dtype) class testDescParent(IsDescription): c = Int32Col() class testDesc(testDescParent): pass assert 'c' in testDesc.columns ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/earray.py000066400000000000000000000227751231437614300166330ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: December 15, 2003 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Here is defined the EArray class.""" import numpy from tables.utils import convert_to_np_atom2, SizeType from tables.carray import CArray from tables._past import previous_api, previous_api_property # default version for EARRAY objects # obversion = "1.0" # initial version # obversion = "1.1" # support for complex datatypes # obversion = "1.2" # This adds support for time datatypes. # obversion = "1.3" # This adds support for enumerated datatypes. obversion = "1.4" # Numeric and numarray flavors are gone. class EArray(CArray): """This class represents extendable, homogeneous datasets in an HDF5 file. The main difference between an EArray and a CArray (see :ref:`CArrayClassDescr`), from which it inherits, is that the former can be enlarged along one of its dimensions, the *enlargeable dimension*. That means that the :attr:`Leaf.extdim` attribute (see :class:`Leaf`) of any EArray instance will always be non-negative. Multiple enlargeable dimensions might be supported in the future. New rows can be added to the end of an enlargeable array by using the :meth:`EArray.append` method. Parameters ---------- parentnode The parent :class:`Group` object. .. versionchanged:: 3.0 Renamed from *parentNode* to *parentnode*. name : str The name of this node in its parent group. atom An `Atom` instance representing the *type* and *shape* of the atomic objects to be saved. shape The shape of the new array. One (and only one) of the shape dimensions *must* be 0. The dimension being 0 means that the resulting `EArray` object can be extended along it. Multiple enlargeable dimensions are not supported right now. title A description for this node (it sets the ``TITLE`` HDF5 attribute on disk). filters An instance of the `Filters` class that provides information about the desired I/O filters to be applied during the life of this object. expectedrows A user estimate about the number of row elements that will be added to the growable dimension in the `EArray` node. If not provided, the default value is ``EXPECTED_ROWS_EARRAY`` (see ``tables/parameters.py``). If you plan to create either a much smaller or a much bigger `EArray` try providing a guess; this will optimize the HDF5 B-Tree creation and management process time and the amount of memory used. chunkshape The shape of the data chunk to be read or written in a single HDF5 I/O operation. Filters are applied to those chunks of data. The dimensionality of `chunkshape` must be the same as that of `shape` (beware: no dimension should be 0 this time!). If ``None``, a sensible value is calculated based on the `expectedrows` parameter (which is recommended). byteorder The byteorder of the data *on disk*, specified as 'little' or 'big'. If this is not specified, the byteorder is that of the platform. Examples -------- See below a small example of the use of the `EArray` class. The code is available in ``examples/earray1.py``:: import tables import numpy fileh = tables.open_file('earray1.h5', mode='w') a = tables.StringAtom(itemsize=8) # Use ``a`` as the object type for the enlargeable array. array_c = fileh.create_earray(fileh.root, 'array_c', a, (0,), \"Chars\") array_c.append(numpy.array(['a'*2, 'b'*4], dtype='S8')) array_c.append(numpy.array(['a'*6, 'b'*8, 'c'*10], dtype='S8')) # Read the string ``EArray`` we have created on disk. for s in array_c: print('array_c[%s] => %r' % (array_c.nrow, s)) # Close the file. fileh.close() The output for the previous script is something like:: array_c[0] => 'aa' array_c[1] => 'bbbb' array_c[2] => 'aaaaaa' array_c[3] => 'bbbbbbbb' array_c[4] => 'cccccccc' """ # Class identifier. _c_classid = 'EARRAY' _c_classId = previous_api_property('_c_classid') # Special methods # ~~~~~~~~~~~~~~~ def __init__(self, parentnode, name, atom=None, shape=None, title="", filters=None, expectedrows=None, chunkshape=None, byteorder=None, _log=True): # Specific of EArray if expectedrows is None: expectedrows = parentnode._v_file.params['EXPECTED_ROWS_EARRAY'] self._v_expectedrows = expectedrows """The expected number of rows to be stored in the array.""" # Call the parent (CArray) init code super(EArray, self).__init__(parentnode, name, atom, shape, title, filters, chunkshape, byteorder, _log) # Public and private methods # ~~~~~~~~~~~~~~~~~~~~~~~~~~ def _g_create(self): """Create a new array in file (specific part).""" # Pre-conditions and extdim computation zerodims = numpy.sum(numpy.array(self.shape) == 0) if zerodims > 0: if zerodims == 1: self.extdim = list(self.shape).index(0) else: raise NotImplementedError( "Multiple enlargeable (0-)dimensions are not " "supported.") else: raise ValueError( "When creating EArrays, you need to set one of " "the dimensions of the Atom instance to zero.") # Finish the common part of the creation process return self._g_create_common(self._v_expectedrows) def _check_shape_append(self, nparr): "Test that nparr shape is consistent with underlying EArray." # The arrays conforms self expandibility? myrank = len(self.shape) narank = len(nparr.shape) - len(self.atom.shape) if myrank != narank: raise ValueError(("the ranks of the appended object (%d) and the " "``%s`` EArray (%d) differ") % (narank, self._v_pathname, myrank)) for i in range(myrank): if i != self.extdim and self.shape[i] != nparr.shape[i]: raise ValueError(("the shapes of the appended object and the " "``%s`` EArray differ in non-enlargeable " "dimension %d") % (self._v_pathname, i)) _checkShapeAppend = previous_api(_check_shape_append) def append(self, sequence): """Add a sequence of data to the end of the dataset. The sequence must have the same type as the array; otherwise a TypeError is raised. In the same way, the dimensions of the sequence must conform to the shape of the array, that is, all dimensions must match, with the exception of the enlargeable dimension, which can be of any length (even 0!). If the shape of the sequence is invalid, a ValueError is raised. """ self._g_check_open() self._v_file._check_writable() # Convert the sequence into a NumPy object nparr = convert_to_np_atom2(sequence, self.atom) # Check if it has a consistent shape with underlying EArray self._check_shape_append(nparr) # If the size of the nparr is zero, don't do anything else if nparr.size > 0: self._append(nparr) def _g_copy_with_stats(self, group, name, start, stop, step, title, filters, chunkshape, _log, **kwargs): """Private part of Leaf.copy() for each kind of leaf.""" (start, stop, step) = self._process_range_read(start, stop, step) # Build the new EArray object maindim = self.maindim shape = list(self.shape) shape[maindim] = 0 # The number of final rows nrows = len(xrange(start, stop, step)) # Build the new EArray object object = EArray( group, name, atom=self.atom, shape=shape, title=title, filters=filters, expectedrows=nrows, chunkshape=chunkshape, _log=_log) # Now, fill the new earray with values from source nrowsinbuf = self.nrowsinbuf # The slices parameter for self.__getitem__ slices = [slice(0, dim, 1) for dim in self.shape] # This is a hack to prevent doing unnecessary conversions # when copying buffers self._v_convert = False # Start the copy itself for start2 in xrange(start, stop, step * nrowsinbuf): # Save the records on disk stop2 = start2 + step * nrowsinbuf if stop2 > stop: stop2 = stop # Set the proper slice in the extensible dimension slices[maindim] = slice(start2, stop2, step) object._append(self.__getitem__(tuple(slices))) # Active the conversion again (default) self._v_convert = True nbytes = numpy.prod(self.shape, dtype=SizeType) * self.atom.itemsize return (object, nbytes) _g_copyWithStats = previous_api(_g_copy_with_stats) ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/exceptions.py000066400000000000000000000257651231437614300175330ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: December 17, 2004 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Declare exceptions and warnings that are specific to PyTables.""" __docformat__ = 'reStructuredText' """The format of documentation strings in this module.""" import os import warnings import traceback class HDF5ExtError(RuntimeError): """A low level HDF5 operation failed. This exception is raised the low level PyTables components used for accessing HDF5 files. It usually signals that something is not going well in the HDF5 library or even at the Input/Output level. Errors in the HDF5 C library may be accompanied by an extensive HDF5 back trace on standard error (see also :func:`tables.silence_hdf5_messages`). .. versionchanged:: 2.4 Parameters ---------- message error message h5bt This parameter (keyword only) controls the HDF5 back trace handling. Any keyword arguments other than h5bt is ignored. * if set to False the HDF5 back trace is ignored and the :attr:`HDF5ExtError.h5backtrace` attribute is set to None * if set to True the back trace is retrieved from the HDF5 library and stored in the :attr:`HDF5ExtError.h5backtrace` attribute as a list of tuples * if set to "VERBOSE" (default) the HDF5 back trace is stored in the :attr:`HDF5ExtError.h5backtrace` attribute and also included in the string representation of the exception * if not set (or set to None) the default policy is used (see :attr:`HDF5ExtError.DEFAULT_H5_BACKTRACE_POLICY`) """ # NOTE: in order to avoid circular dependencies between modules the # _dump_h5_backtrace method is set at initialization time in # the utilsExtenion. _dump_h5_backtrace = None DEFAULT_H5_BACKTRACE_POLICY = "VERBOSE" """Default policy for HDF5 backtrace handling * if set to False the HDF5 back trace is ignored and the :attr:`HDF5ExtError.h5backtrace` attribute is set to None * if set to True the back trace is retrieved from the HDF5 library and stored in the :attr:`HDF5ExtError.h5backtrace` attribute as a list of tuples * if set to "VERBOSE" (default) the HDF5 back trace is stored in the :attr:`HDF5ExtError.h5backtrace` attribute and also included in the string representation of the exception This parameter can be set using the :envvar:`PT_DEFAULT_H5_BACKTRACE_POLICY` environment variable. Allowed values are "IGNORE" (or "FALSE"), "SAVE" (or "TRUE") and "VERBOSE" to set the policy to False, True and "VERBOSE" respectively. The special value "DEFAULT" can be used to reset the policy to the default value .. versionadded:: 2.4 """ @classmethod def set_policy_from_env(cls): envmap = { "IGNORE": False, "FALSE": False, "SAVE": True, "TRUE": True, "VERBOSE": "VERBOSE", "DEFAULT": "VERBOSE", } oldvalue = cls.DEFAULT_H5_BACKTRACE_POLICY envvalue = os.environ.get("PT_DEFAULT_H5_BACKTRACE_POLICY", "DEFAULT") try: newvalue = envmap[envvalue.upper()] except KeyError: warnings.warn("Invalid value for the environment variable " "'PT_DEFAULT_H5_BACKTRACE_POLICY'. The default " "policy for HDF5 back trace management in PyTables " "will be: '%s'" % oldvalue) else: cls.DEFAULT_H5_BACKTRACE_POLICY = newvalue return oldvalue def __init__(self, *args, **kargs): super(HDF5ExtError, self).__init__(*args) self._h5bt_policy = kargs.get('h5bt', self.DEFAULT_H5_BACKTRACE_POLICY) if self._h5bt_policy and self._dump_h5_backtrace is not None: self.h5backtrace = self._dump_h5_backtrace() """HDF5 back trace. Contains the HDF5 back trace as a (possibly empty) list of tuples. Each tuple has the following format:: (filename, line number, function name, text) Depending on the value of the *h5bt* parameter passed to the initializer the h5backtrace attribute can be set to None. This means that the HDF5 back trace has been simply ignored (not retrieved from the HDF5 C library error stack) or that there has been an error (silently ignored) during the HDF5 back trace retrieval. .. versionadded:: 2.4 See Also -------- traceback.format_list : :func:`traceback.format_list` """ # XXX: check _dump_h5_backtrace failures else: self.h5backtrace = None def __str__(self): """Returns a sting representation of the exception. The actual result depends on policy set in the initializer :meth:`HDF5ExtError.__init__`. .. versionadded:: 2.4 """ verbose = bool(self._h5bt_policy in ('VERBOSE', 'verbose')) if verbose and self.h5backtrace: bt = "\n".join([ "HDF5 error back trace\n", self.format_h5_backtrace(), "End of HDF5 error back trace" ]) if len(self.args) == 1 and isinstance(self.args[0], basestring): msg = super(HDF5ExtError, self).__str__() msg = "%s\n\n%s" % (bt, msg) elif self.h5backtrace[-1][-1]: msg = "%s\n\n%s" % (bt, self.h5backtrace[-1][-1]) else: msg = bt else: msg = super(HDF5ExtError, self).__str__() return msg def format_h5_backtrace(self, backtrace=None): """Convert the HDF5 trace back represented as a list of tuples. (see :attr:`HDF5ExtError.h5backtrace`) into a string. .. versionadded:: 2.4 """ if backtrace is None: backtrace = self.h5backtrace if backtrace is None: return 'No HDF5 back trace available' else: return ''.join(traceback.format_list(backtrace)) # Initialize the policy for HDF5 back trace handling HDF5ExtError.set_policy_from_env() # The following exceptions are concretions of the ``ValueError`` exceptions # raised by ``file`` objects on certain operations. class ClosedNodeError(ValueError): """The operation can not be completed because the node is closed. For instance, listing the children of a closed group is not allowed. """ pass class ClosedFileError(ValueError): """The operation can not be completed because the hosting file is closed. For instance, getting an existing node from a closed file is not allowed. """ pass class FileModeError(ValueError): """The operation can not be carried out because the mode in which the hosting file is opened is not adequate. For instance, removing an existing leaf from a read-only file is not allowed. """ pass class NodeError(AttributeError, LookupError): """Invalid hierarchy manipulation operation requested. This exception is raised when the user requests an operation on the hierarchy which can not be run because of the current layout of the tree. This includes accessing nonexistent nodes, moving or copying or creating over an existing node, non-recursively removing groups with children, and other similarly invalid operations. A node in a PyTables database cannot be simply overwritten by replacing it. Instead, the old node must be removed explicitely before another one can take its place. This is done to protect interactive users from inadvertedly deleting whole trees of data by a single erroneous command. """ pass class NoSuchNodeError(NodeError): """An operation was requested on a node that does not exist. This exception is raised when an operation gets a path name or a ``(where, name)`` pair leading to a nonexistent node. """ pass class UndoRedoError(Exception): """Problems with doing/redoing actions with Undo/Redo feature. This exception indicates a problem related to the Undo/Redo mechanism, such as trying to undo or redo actions with this mechanism disabled, or going to a nonexistent mark. """ pass class UndoRedoWarning(Warning): """Issued when an action not supporting Undo/Redo is run. This warning is only shown when the Undo/Redo mechanism is enabled. """ pass class NaturalNameWarning(Warning): """Issued when a non-pythonic name is given for a node. This is not an error and may even be very useful in certain contexts, but one should be aware that such nodes cannot be accessed using natural naming (instead, ``getattr()`` must be used explicitly). """ pass class PerformanceWarning(Warning): """Warning for operations which may cause a performance drop. This warning is issued when an operation is made on the database which may cause it to slow down on future operations (i.e. making the node tree grow too much). """ pass class FlavorError(ValueError): """Unsupported or unavailable flavor or flavor conversion. This exception is raised when an unsupported or unavailable flavor is given to a dataset, or when a conversion of data between two given flavors is not supported nor available. """ pass class FlavorWarning(Warning): """Unsupported or unavailable flavor conversion. This warning is issued when a conversion of data between two given flavors is not supported nor available, and raising an error would render the data inaccessible (e.g. on a dataset of an unavailable flavor in a read-only file). See the `FlavorError` class for more information. """ pass class FiltersWarning(Warning): """Unavailable filters. This warning is issued when a valid filter is specified but it is not available in the system. It may mean that an available default filter is to be used instead. """ pass class OldIndexWarning(Warning): """Unsupported index format. This warning is issued when an index in an unsupported format is found. The index will be marked as invalid and will behave as if doesn't exist. """ pass class DataTypeWarning(Warning): """Unsupported data type. This warning is issued when an unsupported HDF5 data type is found (normally in a file created with other tool than PyTables). """ pass class ExperimentalFeatureWarning(Warning): """Generic warning for experimental features. This warning is issued when using a functionality that is still experimental and that users have to use with care. """ pass ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/expression.py000066400000000000000000000673241231437614300175460ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: June 12, 2009 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Here is defined the Expr class.""" from __future__ import print_function import sys import warnings import numpy as np import tables as tb from numexpr.necompiler import getContext, getExprNames, getType, NumExpr from numexpr.expressions import functions as numexpr_functions from tables.utilsextension import get_indices from tables.exceptions import PerformanceWarning from tables.parameters import IO_BUFFER_SIZE, BUFFER_TIMES from tables._past import previous_api class Expr(object): """A class for evaluating expressions with arbitrary array-like objects. Expr is a class for evaluating expressions containing array-like objects. With it, you can evaluate expressions (like "3 * a + 4 * b") that operate on arbitrary large arrays while optimizing the resources required to perform them (basically main memory and CPU cache memory). It is similar to the Numexpr package (see :ref:`[NUMEXPR] `), but in addition to NumPy objects, it also accepts disk-based homogeneous arrays, like the Array, CArray, EArray and Column PyTables objects. All the internal computations are performed via the Numexpr package, so all the broadcast and upcasting rules of Numexpr applies here too. These rules are very similar to the NumPy ones, but with some exceptions due to the particularities of having to deal with potentially very large disk-based arrays. Be sure to read the documentation of the Expr constructor and methods as well as that of Numexpr, if you want to fully grasp these particularities. Parameters ---------- expr : str This specifies the expression to be evaluated, such as "2 * a + 3 * b". uservars : dict This can be used to define the variable names appearing in *expr*. This mapping should consist of identifier-like strings pointing to any `Array`, `CArray`, `EArray`, `Column` or NumPy ndarray instances (or even others which will tried to be converted to ndarrays). When `uservars` is not provided or `None`, the current local and global namespace is sought instead of `uservars`. It is also possible to pass just some of the variables in expression via the `uservars` mapping, and the rest will be retrieved from the current local and global namespaces. kwargs : dict This is meant to pass additional parameters to the Numexpr kernel. This is basically the same as the kwargs argument in Numexpr.evaluate(), and is mainly meant for advanced use. Examples -------- The following shows an example of using Expr. >>> a = f.create_array('/', 'a', np.array([1,2,3])) >>> b = f.create_array('/', 'b', np.array([3,4,5])) >>> c = np.array([4,5,6]) >>> expr = tb.Expr("2 * a + b * c") # initialize the expression >>> expr.eval() # evaluate it array([14, 24, 36]) >>> sum(expr) # use as an iterator 74 where you can see that you can mix different containers in the expression (whenever shapes are consistent). You can also work with multidimensional arrays:: >>> a2 = f.create_array('/', 'a2', np.array([[1,2],[3,4]])) >>> b2 = f.create_array('/', 'b2', np.array([[3,4],[5,6]])) >>> c2 = np.array([4,5]) # This will be broadcasted >>> expr = tb.Expr("2 * a2 + b2-c2") >>> expr.eval() array([[1, 3], [7, 9]]) >>> sum(expr) array([ 8, 12]) .. rubric:: Expr attributes .. attribute:: append_mode The append mode for user-provided output containers. .. attribute:: maindim Common main dimension for inputs in expression. .. attribute:: names The names of variables in expression (list). .. attribute:: out The user-provided container (if any) for the expression outcome. .. attribute:: o_start The start range selection for the user-provided output. .. attribute:: o_stop The stop range selection for the user-provided output. .. attribute:: o_step The step range selection for the user-provided output. .. attribute:: shape Common shape for the arrays in expression. .. attribute:: values The values of variables in expression (list). """ _exprvars_cache = {} """Cache of variables participating in expressions. .. versionadded:: 3.0 """ def __init__(self, expr, uservars=None, **kwargs): self.append_mode = False """The append mode for user-provided output containers.""" self.maindim = 0 """Common main dimension for inputs in expression.""" self.names = [] """The names of variables in expression (list).""" self.out = None """The user-provided container (if any) for the expression outcome.""" self.o_start = None """The start range selection for the user-provided output.""" self.o_stop = None """The stop range selection for the user-provided output.""" self.o_step = None """The step range selection for the user-provided output.""" self.shape = None """Common shape for the arrays in expression.""" self.start, self.stop, self.step = (None,) * 3 self.start = None """The start range selection for the input.""" self.stop = None """The stop range selection for the input.""" self.step = None """The step range selection for the input.""" self.values = [] """The values of variables in expression (list).""" self._compiled_expr = None """The compiled expression.""" self._single_row_out = None """A sample of the output with just a single row.""" # First, get the signature for the arrays in expression vars_ = self._required_expr_vars(expr, uservars) context = getContext(kwargs) self.names, _ = getExprNames(expr, context) # Raise a ValueError in case we have unsupported objects for name, var in vars_.iteritems(): if type(var) in (int, long, float, str): continue if not isinstance(var, (tb.Leaf, tb.Column)): if hasattr(var, "dtype"): # Quacks like a NumPy object continue raise TypeError("Unsupported variable type: %r" % var) objname = var.__class__.__name__ if objname not in ("Array", "CArray", "EArray", "Column"): raise TypeError("Unsupported variable type: %r" % var) # NumPy arrays to be copied? (we don't need to worry about # PyTables objects, as the reads always return contiguous and # aligned objects, or at least I think so). for name, var in vars_.iteritems(): if isinstance(var, np.ndarray): # See numexpr.necompiler.evaluate for a rational # of the code below if not var.flags.aligned: if var.ndim != 1: # Do a copy of this variable var = var.copy() # Update the vars_ dictionary vars_[name] = var # Get the variables and types values = self.values types_ = [] for name in self.names: value = vars_[name] if hasattr(value, 'atom'): types_.append(value.atom) elif hasattr(value, 'dtype'): types_.append(value) else: # try to convert into a NumPy array value = np.array(value) types_.append(value) values.append(value) # Create a signature for the expression signature = [(name, getType(type_)) for (name, type_) in zip(self.names, types_)] # Compile the expression self._compiled_expr = NumExpr(expr, signature, **kwargs) # Guess the shape for the outcome and the maindim of inputs self.shape, self.maindim = self._guess_shape() # The next method is similar to their counterpart in `Table`, but # adapted to the `Expr` own requirements. def _required_expr_vars(self, expression, uservars, depth=2): """Get the variables required by the `expression`. A new dictionary defining the variables used in the `expression` is returned. Required variables are first looked up in the `uservars` mapping, then in the set of top-level columns of the table. Unknown variables cause a `NameError` to be raised. When `uservars` is `None`, the local and global namespace where the API callable which uses this method is called is sought instead. To disable this mechanism, just specify a mapping as `uservars`. Nested columns and variables with an ``uint64`` type are not allowed (`TypeError` and `NotImplementedError` are raised, respectively). `depth` specifies the depth of the frame in order to reach local or global variables. """ # Get the names of variables used in the expression. exprvars_cache = self._exprvars_cache if not expression in exprvars_cache: # Protection against growing the cache too much if len(exprvars_cache) > 256: # Remove 10 (arbitrary) elements from the cache for k in exprvars_cache.keys()[:10]: del exprvars_cache[k] cexpr = compile(expression, '', 'eval') exprvars = [var for var in cexpr.co_names if var not in ['None', 'False', 'True'] and var not in numexpr_functions] exprvars_cache[expression] = exprvars else: exprvars = exprvars_cache[expression] # Get the local and global variable mappings of the user frame # if no mapping has been explicitly given for user variables. user_locals, user_globals = {}, {} if uservars is None: user_frame = sys._getframe(depth) user_locals = user_frame.f_locals user_globals = user_frame.f_globals # Look for the required variables first among the ones # explicitly provided by the user. reqvars = {} for var in exprvars: # Get the value. if uservars is not None and var in uservars: val = uservars[var] elif uservars is None and var in user_locals: val = user_locals[var] elif uservars is None and var in user_globals: val = user_globals[var] else: raise NameError("name ``%s`` is not defined" % var) # Check the value. if hasattr(val, 'dtype') and val.dtype.str[1:] == 'u8': raise NotImplementedError( "variable ``%s`` refers to " "a 64-bit unsigned integer object, that is " "not yet supported in expressions, sorry; " % var) elif hasattr(val, '_v_colpathnames'): # nested column # This branch is never reached because the compile step # above already raise a ``TypeError`` for nested # columns, but that could change in the future. So it # is best to let this here. raise TypeError( "variable ``%s`` refers to a nested column, " "not allowed in expressions" % var) reqvars[var] = val return reqvars _requiredExprVars = previous_api(_required_expr_vars) def set_inputs_range(self, start=None, stop=None, step=None): """Define a range for all inputs in expression. The computation will only take place for the range defined by the start, stop and step parameters in the main dimension of inputs (or the leading one, if the object lacks the concept of main dimension, like a NumPy container). If not a common main dimension exists for all inputs, the leading dimension will be used instead. """ self.start = start self.stop = stop self.step = step setInputsRange = previous_api(set_inputs_range) def set_output(self, out, append_mode=False): """Set out as container for output as well as the append_mode. The out must be a container that is meant to keep the outcome of the expression. It should be an homogeneous type container and can typically be an Array, CArray, EArray, Column or a NumPy ndarray. The append_mode specifies the way of which the output is filled. If true, the rows of the outcome are *appended* to the out container. Of course, for doing this it is necessary that out would have an append() method (like an EArray, for example). If append_mode is false, the output is set via the __setitem__() method (see the Expr.set_output_range() for info on how to select the rows to be updated). If out is smaller than what is required by the expression, only the computations that are needed to fill up the container are carried out. If it is larger, the excess elements are unaffected. """ if not (hasattr(out, "shape") and hasattr(out, "__setitem__")): raise ValueError( "You need to pass a settable multidimensional container " "as output") self.out = out if append_mode and not hasattr(out, "append"): raise ValueError( "For activating the ``append`` mode, you need a container " "with an `append()` method (like the `EArray`)") self.append_mode = append_mode setOutput = previous_api(set_output) def set_output_range(self, start=None, stop=None, step=None): """Define a range for user-provided output object. The output object will only be modified in the range specified by the start, stop and step parameters in the main dimension of output (or the leading one, if the object does not have the concept of main dimension, like a NumPy container). """ if self.out is None: raise IndexError( "You need to pass an output object to `setOut()` first") self.o_start = start self.o_stop = stop self.o_step = step setOutputRange = previous_api(set_output_range) # Although the next code is similar to the method in `Leaf`, it # allows the use of pure NumPy objects. def _calc_nrowsinbuf(self, object_): """Calculate the number of rows that will fit in a buffer.""" # Compute the rowsize for the *leading* dimension shape_ = list(object_.shape) if shape_: shape_[0] = 1 rowsize = np.prod(shape_) * object_.dtype.itemsize # Compute the nrowsinbuf # Multiplying the I/O buffer size by 4 gives optimal results # in my benchmarks with `tables.Expr` (see ``bench/poly.py``) buffersize = IO_BUFFER_SIZE * 4 nrowsinbuf = buffersize // rowsize # Safeguard against row sizes being extremely large if nrowsinbuf == 0: nrowsinbuf = 1 # If rowsize is too large, issue a Performance warning maxrowsize = BUFFER_TIMES * buffersize if rowsize > maxrowsize: warnings.warn("""\ The object ``%s`` is exceeding the maximum recommended rowsize (%d bytes); be ready to see PyTables asking for *lots* of memory and possibly slow I/O. You may want to reduce the rowsize by trimming the value of dimensions that are orthogonal (and preferably close) to the *leading* dimension of this object.""" % (object, maxrowsize), PerformanceWarning) return nrowsinbuf def _guess_shape(self): """Guess the shape of the output of the expression.""" # First, compute the maximum dimension of inputs and maindim # (if it exists) maxndim = 0 maindims = [] for val in self.values: # Get the minimum of the lengths if len(val.shape) > maxndim: maxndim = len(val.shape) if hasattr(val, "maindim"): maindims.append(val.maindim) if maxndim == 0: self._single_row_out = out = self._compiled_expr(*self.values) return (), None if maindims and [maindims[0]] * len(maindims) == maindims: # If all maindims detected are the same, use this as maindim maindim = maindims[0] else: # If not, the main dimension will be the default one maindim = 0 # The slices parameter for inputs slices = (slice(None),) * maindim + (0,) # Now, collect the values in first row of arrays with maximum dims vals = [] lens = [] for val in self.values: shape = val.shape # Warning: don't use len(val) below or it will raise an # `Overflow` error on 32-bit platforms for large enough arrays. if shape != () and shape[maindim] == 0: vals.append(val[:]) lens.append(0) elif len(shape) < maxndim: vals.append(val) else: vals.append(val.__getitem__(slices)) lens.append(shape[maindim]) minlen = min(lens) self._single_row_out = out = self._compiled_expr(*vals) shape = list(out.shape) if minlen > 0: shape.insert(maindim, minlen) return shape, maindim def _get_info(self, shape, maindim, itermode=False): """Return various info needed for evaluating the computation loop.""" # Compute the shape of the resulting container having # in account new possible values of start, stop and step in # the inputs range if maindim is not None: (start, stop, step) = get_indices( self.start, self.stop, self.step, shape[maindim]) shape[maindim] = min( shape[maindim], len(xrange(start, stop, step))) i_nrows = shape[maindim] else: start, stop, step = 0, 0, None i_nrows = 0 if not itermode: # Create a container for output if not defined yet o_maindim = 0 # Default maindim if self.out is None: out = np.empty(shape, dtype=self._single_row_out.dtype) # Get the trivial values for start, stop and step if maindim is not None: (o_start, o_stop, o_step) = (0, shape[maindim], 1) else: (o_start, o_stop, o_step) = (0, 0, 1) else: out = self.out # Out container already provided. Do some sanity checks. if hasattr(out, "maindim"): o_maindim = out.maindim # Refine the shape of the resulting container having in # account new possible values of start, stop and step in # the output range o_shape = list(out.shape) (o_start, o_stop, o_step) = get_indices( self.o_start, self.o_stop, self.o_step, o_shape[o_maindim]) o_shape[o_maindim] = min(o_shape[o_maindim], len(xrange(o_start, o_stop, o_step))) # Check that the shape of output is consistent with inputs tr_oshape = list(o_shape) # this implies a copy olen_ = tr_oshape.pop(o_maindim) tr_shape = list(shape) # do a copy if maindim is not None: len_ = tr_shape.pop(o_maindim) else: len_ = 1 if tr_oshape != tr_shape: raise ValueError( "Shape for out container does not match expression") # Force the input length to fit in `out` if not self.append_mode and olen_ < len_: shape[o_maindim] = olen_ stop = start + olen_ # Get the positions of inputs that should be sliced (the others # will be broadcasted) ndim = len(shape) slice_pos = [i for i, val in enumerate(self.values) if len(val.shape) == ndim] # The size of the I/O buffer nrowsinbuf = 1 for i, val in enumerate(self.values): # Skip scalar values in variables if i in slice_pos: nrows = self._calc_nrowsinbuf(val) if nrows > nrowsinbuf: nrowsinbuf = nrows if not itermode: return (i_nrows, slice_pos, start, stop, step, nrowsinbuf, out, o_maindim, o_start, o_stop, o_step) else: # For itermode, we don't need the out info return (i_nrows, slice_pos, start, stop, step, nrowsinbuf) def eval(self): """Evaluate the expression and return the outcome. Because of performance reasons, the computation order tries to go along the common main dimension of all inputs. If not such a common main dimension is found, the iteration will go along the leading dimension instead. For non-consistent shapes in inputs (i.e. shapes having a different number of dimensions), the regular NumPy broadcast rules applies. There is one exception to this rule though: when the dimensions orthogonal to the main dimension of the expression are consistent, but the main dimension itself differs among the inputs, then the shortest one is chosen for doing the computations. This is so because trying to expand very large on-disk arrays could be too expensive or simply not possible. Also, the regular Numexpr casting rules (which are similar to those of NumPy, although you should check the Numexpr manual for the exceptions) are applied to determine the output type. Finally, if the setOuput() method specifying a user container has already been called, the output is sent to this user-provided container. If not, a fresh NumPy container is returned instead. .. warning:: When dealing with large on-disk inputs, failing to specify an on-disk container may consume all your available memory. """ values, shape, maindim = self.values, self.shape, self.maindim # Get different info we need for the main computation loop (i_nrows, slice_pos, start, stop, step, nrowsinbuf, out, o_maindim, o_start, o_stop, o_step) = \ self._get_info(shape, maindim) if i_nrows == 0: # No elements to compute return self._single_row_out # Create a key that selects every element in inputs and output # (including the main dimension) i_slices = [slice(None)] * (maindim + 1) o_slices = [slice(None)] * (o_maindim + 1) # This is a hack to prevent doing unnecessary flavor conversions # while reading buffers for val in values: if hasattr(val, 'maindim'): val._v_convert = False # Start the computation itself for start2 in xrange(start, stop, step * nrowsinbuf): stop2 = start2 + step * nrowsinbuf if stop2 > stop: stop2 = stop # Set the proper slice for inputs i_slices[maindim] = slice(start2, stop2, step) # Get the input values vals = [] for i, val in enumerate(values): if i in slice_pos: vals.append(val.__getitem__(tuple(i_slices))) else: # A read of values is not apparently needed, as PyTables # leaves seems to work just fine inside Numexpr vals.append(val) # Do the actual computation for this slice rout = self._compiled_expr(*vals) # Set the values into the out buffer if self.append_mode: out.append(rout) else: # Compute the slice to be filled in output start3 = o_start + (start2 - start) // step stop3 = start3 + nrowsinbuf * o_step if stop3 > o_stop: stop3 = o_stop o_slices[o_maindim] = slice(start3, stop3, o_step) # Set the slice out[tuple(o_slices)] = rout # Activate the conversion again (default) for val in values: if hasattr(val, 'maindim'): val._v_convert = True return out def __iter__(self): """Iterate over the rows of the outcome of the expression. This iterator always returns rows as NumPy objects, so a possible out container specified in :meth:`Expr.set_output` method is ignored here. """ values, shape, maindim = self.values, self.shape, self.maindim # Get different info we need for the main computation loop (i_nrows, slice_pos, start, stop, step, nrowsinbuf) = \ self._get_info(shape, maindim, itermode=True) if i_nrows == 0: # No elements to compute return # Create a key that selects every element in inputs # (including the main dimension) i_slices = [slice(None)] * (maindim + 1) # This is a hack to prevent doing unnecessary flavor conversions # while reading buffers for val in values: if hasattr(val, 'maindim'): val._v_convert = False # Start the computation itself for start2 in xrange(start, stop, step * nrowsinbuf): stop2 = start2 + step * nrowsinbuf if stop2 > stop: stop2 = stop # Set the proper slice in the main dimension i_slices[maindim] = slice(start2, stop2, step) # Get the values for computing the buffer vals = [] for i, val in enumerate(values): if i in slice_pos: vals.append(val.__getitem__(tuple(i_slices))) else: # A read of values is not apparently needed, as PyTables # leaves seems to work just fine inside Numexpr vals.append(val) # Do the actual computation rout = self._compiled_expr(*vals) # Return one row per call for row in rout: yield row # Activate the conversion again (default) for val in values: if hasattr(val, 'maindim'): val._v_convert = True if __name__ == "__main__": # shape = (10000,10000) shape = (10, 10000) f = tb.open_file("/tmp/expression.h5", "w") # Create some arrays a = f.create_carray(f.root, 'a', atom=tb.Float32Atom(dflt=1.), shape=shape) b = f.create_carray(f.root, 'b', atom=tb.Float32Atom(dflt=2.), shape=shape) c = f.create_carray(f.root, 'c', atom=tb.Float32Atom(dflt=3.), shape=shape) out = f.create_carray(f.root, 'out', atom=tb.Float32Atom(dflt=3.), shape=shape) expr = Expr("a * b + c") expr.set_output(out) d = expr.eval() print("returned-->", repr(d)) # print(`d[:]`) f.close() ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/file.py000066400000000000000000003204331231437614300162570ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: September 4, 2002 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Create PyTables files and the object tree. This module support importing generic HDF5 files, on top of which PyTables files are created, read or extended. If a file exists, an object tree mirroring their hierarchical structure is created in memory. File class offer methods to traverse the tree, as well as to create new nodes. """ from __future__ import print_function import os import sys import time import weakref import warnings import collections import numexpr import numpy import tables.misc.proxydict from tables import hdf5extension from tables import utilsextension from tables import parameters from tables.exceptions import (ClosedFileError, FileModeError, NodeError, NoSuchNodeError, UndoRedoError, ClosedNodeError, PerformanceWarning) from tables.registry import get_class_by_name from tables.path import join_path, split_path from tables import undoredo from tables.description import (IsDescription, UInt8Col, StringCol, descr_from_dtype, dtype_from_descr) from tables.filters import Filters from tables.node import Node, NotLoggedMixin from tables.group import Group, RootGroup from tables.group import TransactionGroupG, TransactionG, MarkG from tables.leaf import Leaf from tables.array import Array from tables.carray import CArray from tables.earray import EArray from tables.vlarray import VLArray from tables.table import Table from tables import linkextension from tables.utils import detect_number_of_cores from tables import lrucacheextension from tables.flavor import flavor_of, array_as_internal from tables.atom import Atom from tables.link import SoftLink, ExternalLink from tables._past import previous_api, previous_api_property # format_version = "1.0" # Initial format # format_version = "1.1" # Changes in ucl compression # format_version = "1.2" # Support for enlargeable arrays and VLA's # # 1.2 was introduced in PyTables 0.8 # format_version = "1.3" # Support for indexes in Tables # # 1.3 was introduced in PyTables 0.9 # format_version = "1.4" # Support for multidimensional attributes # # 1.4 was introduced in PyTables 1.1 # format_version = "1.5" # Support for persistent defaults in tables # # 1.5 was introduced in PyTables 1.2 # format_version = "1.6" # Support for NumPy objects and new flavors for # # objects. # # 1.6 was introduced in pytables 1.3 #format_version = "2.0" # Pickles are not used anymore in system attrs # # 2.0 was introduced in PyTables 2.0 format_version = "2.1" # Numeric and numarray flavors are gone. compatible_formats = [] # Old format versions we can read # Empty means that we support all the old formats class _FileRegistry(object): def __init__(self): self._name_mapping = collections.defaultdict(set) self._handlers = set() @property def filenames(self): return self._name_mapping.keys() @property def handlers(self): #return set(self._handlers) # return a copy return self._handlers def __len__(self): return len(self._handlers) def __contains__(self, filename): return filename in self.filenames def add(self, handler): self._name_mapping[handler.filename].add(handler) self._handlers.add(handler) def remove(self, handler): filename = handler.filename self._name_mapping[filename].remove(handler) # remove enpty keys if not self._name_mapping[filename]: del self._name_mapping[filename] self._handlers.remove(handler) def get_handlers_by_name(self, filename): #return set(self._name_mapping[filename]) # return a copy return self._name_mapping[filename] def close_all(self): are_open_files = len(self._handlers) > 0 if are_open_files: sys.stderr.write("Closing remaining open files:") handlers = list(self._handlers) # make a copy for fileh in handlers: sys.stderr.write("%s..." % fileh.filename) fileh.close() sys.stderr.write("done") if are_open_files: sys.stderr.write("\n") # Dict of opened files (keys are filenames and values filehandlers) _open_files = _FileRegistry() # Opcodes for do-undo actions _op_to_code = { "MARK": 0, "CREATE": 1, "REMOVE": 2, "MOVE": 3, "ADDATTR": 4, "DELATTR": 5, } _code_to_op = ["MARK", "CREATE", "REMOVE", "MOVE", "ADDATTR", "DELATTR"] # Paths and names for hidden nodes related with transactions. _trans_version = '1.0' _trans_group_parent = '/' _trans_group_name = '_p_transactions' _trans_group_path = join_path(_trans_group_parent, _trans_group_name) _action_log_parent = _trans_group_path _action_log_name = 'actionlog' _action_log_path = join_path(_action_log_parent, _action_log_name) _trans_parent = _trans_group_path _trans_name = 't%d' # %d -> transaction number _trans_path = join_path(_trans_parent, _trans_name) _markParent = _trans_path _markName = 'm%d' # %d -> mark number _markPath = join_path(_markParent, _markName) _shadow_parent = _markPath _shadow_name = 'a%d' # %d -> action number _shadow_path = join_path(_shadow_parent, _shadow_name) def _checkfilters(filters): if not (filters is None or isinstance(filters, Filters)): raise TypeError("filter parameter has to be None or a Filter " "instance and the passed type is: '%s'" % type(filters)) def copy_file(srcfilename, dstfilename, overwrite=False, **kwargs): """An easy way of copying one PyTables file to another. This function allows you to copy an existing PyTables file named srcfilename to another file called dstfilename. The source file must exist and be readable. The destination file can be overwritten in place if existing by asserting the overwrite argument. This function is a shorthand for the :meth:`File.copy_file` method, which acts on an already opened file. kwargs takes keyword arguments used to customize the copying process. See the documentation of :meth:`File.copy_file` for a description of those arguments. """ # Open the source file. srcfileh = open_file(srcfilename, mode="r") try: # Copy it to the destination file. srcfileh.copy_file(dstfilename, overwrite=overwrite, **kwargs) finally: # Close the source file. srcfileh.close() copyFile = previous_api(copy_file) if tuple(map(int, utilsextension.get_hdf5_version().split('-')[0].split('.'))) \ < (1, 8, 7): _FILE_OPEN_POLICY = 'strict' else: _FILE_OPEN_POLICY = 'default' def open_file(filename, mode="r", title="", root_uep="/", filters=None, **kwargs): """Open a PyTables (or generic HDF5) file and return a File object. Parameters ---------- filename : str The name of the file (supports environment variable expansion). It is suggested that file names have any of the .h5, .hdf or .hdf5 extensions, although this is not mandatory. mode : str The mode to open the file. It can be one of the following: * *'r'*: Read-only; no data can be modified. * *'w'*: Write; a new file is created (an existing file with the same name would be deleted). * *'a'*: Append; an existing file is opened for reading and writing, and if the file does not exist it is created. * *'r+'*: It is similar to 'a', but the file must already exist. title : str If the file is to be created, a TITLE string attribute will be set on the root group with the given value. Otherwise, the title will be read from disk, and this will not have any effect. root_uep : str The root User Entry Point. This is a group in the HDF5 hierarchy which will be taken as the starting point to create the object tree. It can be whatever existing group in the file, named by its HDF5 path. If it does not exist, an HDF5ExtError is issued. Use this if you do not want to build the *entire* object tree, but rather only a *subtree* of it. .. versionchanged:: 3.0 The *rootUEP* parameter has been renamed into *root_uep*. filters : Filters An instance of the Filters (see :ref:`FiltersClassDescr`) class that provides information about the desired I/O filters applicable to the leaves that hang directly from the *root group*, unless other filter properties are specified for these leaves. Besides, if you do not specify filter properties for child groups, they will inherit these ones, which will in turn propagate to child nodes. Notes ----- In addition, it recognizes the (lowercase) names of parameters present in :file:`tables/parameters.py` as additional keyword arguments. See :ref:`parameter_files` for a detailed info on the supported parameters. .. note:: If you need to deal with a large number of nodes in an efficient way, please see :ref:`LRUOptim` for more info and advices about the integrated node cache engine. """ # XXX filename normalization ?? # Check already opened files if _FILE_OPEN_POLICY == 'strict': # This policy do not allows to open the same file multiple times # even in read-only mode if filename in _open_files: raise ValueError( "The file '%s' is already opened. " "Please close it before reopening. " "HDF5 v.%s, FILE_OPEN_POLICY = '%s'" % ( filename, utilsextension.get_hdf5_version(), _FILE_OPEN_POLICY)) else: for filehandle in _open_files.get_handlers_by_name(filename): omode = filehandle.mode # 'r' is incompatible with everything except 'r' itself if mode == 'r' and omode != 'r': raise ValueError( "The file '%s' is already opened, but " "not in read-only mode (as requested)." % filename) # 'a' and 'r+' are compatible with everything except 'r' elif mode in ('a', 'r+') and omode == 'r': raise ValueError( "The file '%s' is already opened, but " "in read-only mode. Please close it before " "reopening in append mode." % filename) # 'w' means that we want to destroy existing contents elif mode == 'w': raise ValueError( "The file '%s' is already opened. Please " "close it before reopening in write mode." % filename) # Finally, create the File instance, and return it return File(filename, mode, title, root_uep, filters, **kwargs) openFile = previous_api(open_file) # A dumb class that doesn't keep nothing at all class _NoCache(object): def __len__(self): return 0 def __contains__(self, key): return False def __iter__(self): return iter([]) def __setitem__(self, key, value): pass __marker = object() def pop(self, key, d=__marker): if d is not self.__marker: return d raise KeyError(key) class _DictCache(dict): def __init__(self, nslots): if nslots < 1: raise ValueError("Invalid number of slots: %d" % nslots) self.nslots = nslots super(_DictCache, self).__init__() def __setitem__(self, key, value): # Check if we are running out of space if len(self) > self.nslots: warnings.warn( "the dictionary of node cache is exceeding the recommended " "maximum number (%d); be ready to see PyTables asking for " "*lots* of memory and possibly slow I/O." % ( self.nslots), PerformanceWarning) super(_DictCache, self).__setitem__(key, value) class NodeManager(object): def __init__(self, nslots=64, node_factory=None): super(NodeManager, self).__init__() self.registry = weakref.WeakValueDictionary() if nslots > 0: cache = lrucacheextension.NodeCache(nslots) elif nslots == 0: cache = _NoCache() else: # nslots < 0 cache = _DictCache(-nslots) self.cache = cache # node_factory(node_path) self.node_factory = node_factory def register_node(self, node, key): if key is None: key = node._v_pathname if key in self.registry: if not self.registry[key]._v_isopen: del self.registry[key] elif self.registry[key] is not node: raise RuntimeError('trying to ragister a node with an ' 'existing key: ``%s``' % key) else: self.registry[key] = node def cache_node(self, node, key=None): if key is None: key = node._v_pathname self.register_node(node, key) if key in self.cache: oldnode = self.cache.pop(key) if oldnode is not node and oldnode._v_isopen: raise RuntimeError('trying to cache a node with an ' 'existing key: ``%s``' % key) self.cache[key] = node def get_node(self, key): node = self.cache.pop(key, None) if node is not None: if node._v_isopen: self.cache_node(node, key) return node else: # this should not happen warnings.warn("a closed node found in the cache: ``%s``" % key) if key in self.registry: node = self.registry[key] if node is None: # this should not happen since WeakValueDictionary drops all # dead weakrefs warnings.warn("None is stored in the registry for key: " "``%s``" % key) elif node._v_isopen: self.cache_node(node, key) return node else: # this should not happen warnings.warn("a closed node found in the registry: " "``%s``" % key) del self.registry[key] node = None if self.node_factory: node = self.node_factory(key) self.cache_node(node, key) return node def rename_node(self, oldkey, newkey): for cache in (self.cache, self.registry): if oldkey in cache: node = cache.pop(oldkey) cache[newkey] = node def drop_from_cache(self, nodepath): '''Remove the node from cache''' # Remove the node from the cache. self.cache.pop(nodepath, None) def drop_node(self, node, check_unregistered=True): """Drop the `node`. Remove the node from the cache and, if it has no more references, close it. """ # Remove all references to the node. nodepath = node._v_pathname self.drop_from_cache(nodepath) if nodepath in self.registry: if not node._v_isopen: del self.registry[nodepath] elif check_unregistered: # If the node is not in the registry (this should never happen) # we close it forcibly since it is not ensured that the __del__ # method is called for object that are still alive when the # interpreter is shut down if node._v_isopen: warnings.warn("dropping a node that is not in the registry: " "``%s``" % nodepath) node._g_pre_kill_hook() node._f_close() def flush_nodes(self): # Only iter on the nodes in the registry since nodes in the cahce # should always have an entry in the registry closed_keys = [] for path, node in self.registry.items(): if not node._v_isopen: closed_keys.append(path) elif '/_i_' not in path: # Indexes are not necessary to be flushed if isinstance(node, Leaf): node.flush() for path in closed_keys: # self.cache.pop(path, None) if path in self.cache: warnings.warn("closed node the cache: ``%s``" % path) self.cache.pop(path, None) self.registry.pop(path) @staticmethod def _close_nodes(nodepaths, get_node): for nodepath in nodepaths: try: node = get_node(nodepath) except KeyError: pass else: if not node._v_isopen or node._v__deleting: continue try: # Avoid descendent nodes to also iterate over # their descendents, which are already to be # closed by this loop. if hasattr(node, '_f_get_child'): node._g_close() else: node._f_close() del node except ClosedNodeError: #import traceback #type_, value, tb = sys.exc_info() #exception_dump = ''.join( # traceback.format_exception(type_, value, tb)) #warnings.warn( # "A '%s' exception occurred trying to close a node " # "that was supposed to be open.\n" # "%s" % (type_.__name__, exception_dump)) pass def close_subtree(self, prefix='/'): if not prefix.endswith('/'): prefix = prefix + '/' cache = self.cache registry = self.registry # Ensure tables are closed before their indices paths = [ path for path in cache if path.startswith(prefix) and '/_i_' not in path ] self._close_nodes(paths, cache.pop) # Close everything else (i.e. indices) paths = [path for path in cache if path.startswith(prefix)] self._close_nodes(paths, cache.pop) # Ensure tables are closed before their indices paths = [ path for path in registry if path.startswith(prefix) and '/_i_' not in path ] self._close_nodes(paths, registry.pop) # Close everything else (i.e. indices) paths = [path for path in registry if path.startswith(prefix)] self._close_nodes(paths, registry.pop) def shutdown(self): registry = self.registry cache = self.cache #self.close_subtree('/') keys = list(cache) # copy for key in keys: node = cache.pop(key) if node._v_isopen: registry.pop(node._v_pathname, None) node._f_close() while registry: key, node = registry.popitem() if node._v_isopen: node._f_close() class File(hdf5extension.File, object): """The in-memory representation of a PyTables file. An instance of this class is returned when a PyTables file is opened with the :func:`tables.open_file` function. It offers methods to manipulate (create, rename, delete...) nodes and handle their attributes, as well as methods to traverse the object tree. The *user entry point* to the object tree attached to the HDF5 file is represented in the root_uep attribute. Other attributes are available. File objects support an *Undo/Redo mechanism* which can be enabled with the :meth:`File.enable_undo` method. Once the Undo/Redo mechanism is enabled, explicit *marks* (with an optional unique name) can be set on the state of the database using the :meth:`File.mark` method. There are two implicit marks which are always available: the initial mark (0) and the final mark (-1). Both the identifier of a mark and its name can be used in *undo* and *redo* operations. Hierarchy manipulation operations (node creation, movement and removal) and attribute handling operations (setting and deleting) made after a mark can be undone by using the :meth:`File.undo` method, which returns the database to the state of a past mark. If undo() is not followed by operations that modify the hierarchy or attributes, the :meth:`File.redo` method can be used to return the database to the state of a future mark. Else, future states of the database are forgotten. Note that data handling operations can not be undone nor redone by now. Also, hierarchy manipulation operations on nodes that do not support the Undo/Redo mechanism issue an UndoRedoWarning *before* changing the database. The Undo/Redo mechanism is persistent between sessions and can only be disabled by calling the :meth:`File.disable_undo` method. File objects can also act as context managers when using the with statement introduced in Python 2.5. When exiting a context, the file is automatically closed. Parameters ---------- filename : str The name of the file (supports environment variable expansion). It is suggested that file names have any of the .h5, .hdf or .hdf5 extensions, although this is not mandatory. mode : str The mode to open the file. It can be one of the following: * *'r'*: Read-only; no data can be modified. * *'w'*: Write; a new file is created (an existing file with the same name would be deleted). * *'a'*: Append; an existing file is opened for reading and writing, and if the file does not exist it is created. * *'r+'*: It is similar to 'a', but the file must already exist. title : str If the file is to be created, a TITLE string attribute will be set on the root group with the given value. Otherwise, the title will be read from disk, and this will not have any effect. root_uep : str The root User Entry Point. This is a group in the HDF5 hierarchy which will be taken as the starting point to create the object tree. It can be whatever existing group in the file, named by its HDF5 path. If it does not exist, an HDF5ExtError is issued. Use this if you do not want to build the *entire* object tree, but rather only a *subtree* of it. .. versionchanged:: 3.0 The *rootUEP* parameter has been renamed into *root_uep*. filters : Filters An instance of the Filters (see :ref:`FiltersClassDescr`) class that provides information about the desired I/O filters applicable to the leaves that hang directly from the *root group*, unless other filter properties are specified for these leaves. Besides, if you do not specify filter properties for child groups, they will inherit these ones, which will in turn propagate to child nodes. Notes ----- In addition, it recognizes the (lowercase) names of parameters present in :file:`tables/parameters.py` as additional keyword arguments. See :ref:`parameter_files` for a detailed info on the supported parameters. .. rubric:: File attributes .. attribute:: filename The name of the opened file. .. attribute:: format_version The PyTables version number of this file. .. attribute:: isopen True if the underlying file is open, false otherwise. .. attribute:: mode The mode in which the file was opened. .. attribute:: root The *root* of the object tree hierarchy (a Group instance). .. attribute:: root_uep The UEP (user entry point) group name in the file (see the :func:`open_file` function). .. versionchanged:: 3.0 The *rootUEP* attribute has been renamed into *root_uep*. """ ## # The top level kinds. Group must go first! _node_kinds = ('Group', 'Leaf', 'Link', 'Unknown') rootUEP = previous_api_property('root_uep') _v_objectId = previous_api_property('_v_objectid') ## ## def _gettitle(self): return self.root._v_title def _settitle(self, title): self.root._v_title = title def _deltitle(self): del self.root._v_title title = property( _gettitle, _settitle, _deltitle, "The title of the root group in the file.") def _getfilters(self): return self.root._v_filters def _setfilters(self, filters): self.root._v_filters = filters def _delfilters(self): del self.root._v_filters filters = property( _getfilters, _setfilters, _delfilters, ("Default filter properties for the root group " "(see :ref:`FiltersClassDescr`).")) open_count = property( lambda self: self._open_count, None, None, """The number of times this file handle has been opened. .. versionchanged:: 3.1 The mechanism for caching and sharing file handles has been removed in PyTables 3.1. Now this property should always be 1 (or 0 for closed files). .. deprecated:: 3.1 """) ## def __init__(self, filename, mode="r", title="", root_uep="/", filters=None, **kwargs): self.filename = filename """The name of the opened file.""" self.mode = mode """The mode in which the file was opened.""" if mode not in ('r', 'r+', 'a', 'w'): raise ValueError("invalid mode string ``%s``. Allowed modes are: " "'r', 'r+', 'a' and 'w'" % mode) # Get all the parameters in parameter file(s) params = dict([(k, v) for k, v in parameters.__dict__.iteritems() if k.isupper() and not k.startswith('_')]) # Update them with possible keyword arguments if [k for k in kwargs if k.isupper()]: warnings.warn("The use of uppercase keyword parameters is " "deprecated", DeprecationWarning) kwargs = dict([(k.upper(), v) for k, v in kwargs.iteritems()]) params.update(kwargs) # If MAX_ * _THREADS is not set yet, set it to the number of cores # on this machine. if params['MAX_NUMEXPR_THREADS'] is None: params['MAX_NUMEXPR_THREADS'] = detect_number_of_cores() if params['MAX_BLOSC_THREADS'] is None: params['MAX_BLOSC_THREADS'] = detect_number_of_cores() self.params = params # Now, it is time to initialize the File extension self._g_new(filename, mode, **params) # Check filters and set PyTables format version for new files. new = self._v_new if new: _checkfilters(filters) self.format_version = format_version """The PyTables version number of this file.""" # The node manager must be initialized before the root group # initialization but the node_factory attribute is set onl later # because it is a bount method of the root grop itself. node_cache_slots = params['NODE_CACHE_SLOTS'] self._node_manager = NodeManager(nslots=node_cache_slots) # For the moment Undo/Redo is not enabled. self._undoEnabled = False # Set the flag to indicate that the file has been opened. # It must be set before opening the root group # to allow some basic access to its attributes. self.isopen = 1 """True if the underlying file os open, False otherwise.""" # Append the name of the file to the global dict of files opened. _open_files.add(self) # Set the number of times this file has been opened to 1 self._open_count = 1 # Get the root group from this file self.root = root = self.__get_root_group(root_uep, title, filters) """The *root* of the object tree hierarchy (a Group instance).""" # Complete the creation of the root node # (see the explanation in ``RootGroup.__init__()``. root._g_post_init_hook() self._node_manager.node_factory = self.root._g_load_child # Save the PyTables format version for this file. if new: if params['PYTABLES_SYS_ATTRS']: root._v_attrs._g__setattr( 'PYTABLES_FORMAT_VERSION', format_version) # If the file is old, and not opened in "read-only" mode, # check if it has a transaction log if not new and self.mode != "r" and _trans_group_path in self: # It does. Enable the undo. self.enable_undo() # Set the maximum number of threads for Numexpr numexpr.set_vml_num_threads(params['MAX_NUMEXPR_THREADS']) def __get_root_group(self, root_uep, title, filters): """Returns a Group instance which will act as the root group in the hierarchical tree. If file is opened in "r", "r+" or "a" mode, and the file already exists, this method dynamically builds a python object tree emulating the structure present on file. """ self._v_objectid = self._get_file_id() if root_uep in [None, ""]: root_uep = "/" # Save the User Entry Point in a variable class self.root_uep = root_uep new = self._v_new # Get format version *before* getting the object tree if not new: # Firstly, get the PyTables format version for this file self.format_version = utilsextension.read_f_attr( self._v_objectid, 'PYTABLES_FORMAT_VERSION') if not self.format_version: # PYTABLES_FORMAT_VERSION attribute is not present self.format_version = "unknown" self._isPTFile = False elif not isinstance(self.format_version, str): # system attributes should always be str if sys.version_info[0] < 3: self.format_version = self.format_version.encode() else: self.format_version = self.format_version.decode('utf-8') # Create new attributes for the root Group instance and # create the object tree return RootGroup(self, root_uep, title=title, new=new, filters=filters) __getRootGroup = previous_api(__get_root_group) def _get_or_create_path(self, path, create): """Get the given `path` or create it if `create` is true. If `create` is true, `path` *must* be a string path and not a node, otherwise a `TypeError`will be raised. """ if create: return self._create_path(path) else: return self.get_node(path) _getOrCreatePath = previous_api(_get_or_create_path) def _create_path(self, path): """Create the groups needed for the `path` to exist. The group associated with the given `path` is returned. """ if not hasattr(path, 'split'): raise TypeError("when creating parents, parent must be a path") if path == '/': return self.root parent, create_group = self.root, self.create_group for pcomp in path.split('/')[1:]: try: child = parent._f_get_child(pcomp) except NoSuchNodeError: child = create_group(parent, pcomp) parent = child return parent _createPath = previous_api(_create_path) def create_group(self, where, name, title="", filters=None, createparents=False): """Create a new group. Parameters ---------- where : str or Group The parent group from which the new group will hang. It can be a path string (for example '/level1/leaf5'), or a Group instance (see :ref:`GroupClassDescr`). name : str The name of the new group. title : str, optional A description for this node (it sets the TITLE HDF5 attribute on disk). filters : Filters An instance of the Filters class (see :ref:`FiltersClassDescr`) that provides information about the desired I/O filters applicable to the leaves that hang directly from this new group (unless other filter properties are specified for these leaves). Besides, if you do not specify filter properties for its child groups, they will inherit these ones. createparents : bool Whether to create the needed groups for the parent path to exist (not done by default). See Also -------- Group : for more information on groups """ parentnode = self._get_or_create_path(where, createparents) _checkfilters(filters) return Group(parentnode, name, title=title, new=True, filters=filters) createGroup = previous_api(create_group) def create_table(self, where, name, description=None, title="", filters=None, expectedrows=10000, chunkshape=None, byteorder=None, createparents=False, obj=None): """Create a new table with the given name in where location. Parameters ---------- where : str or Group The parent group from which the new table will hang. It can be a path string (for example '/level1/leaf5'), or a Group instance (see :ref:`GroupClassDescr`). name : str The name of the new table. description : Description This is an object that describes the table, i.e. how many columns it has, their names, types, shapes, etc. It can be any of the following: * *A user-defined class*: This should inherit from the IsDescription class (see :ref:`IsDescriptionClassDescr`) where table fields are specified. * *A dictionary*: For example, when you do not know beforehand which structure your table will have). * *A Description instance*: You can use the description attribute of another table to create a new one with the same structure. * *A NumPy dtype*: A completely general structured NumPy dtype. * *A NumPy (structured) array instance*: The dtype of this structured array will be used as the description. Also, in case the array has actual data, it will be injected into the newly created table. .. versionchanged:: 3.0 The *description* parameter can be None (default) if *obj* is provided. In that case the structure of the table is deduced by *obj*. title : str A description for this node (it sets the TITLE HDF5 attribute on disk). filters : Filters An instance of the Filters class (see :ref:`FiltersClassDescr`) that provides information about the desired I/O filters to be applied during the life of this object. expectedrows : int A user estimate of the number of records that will be in the table. If not provided, the default value is EXPECTED_ROWS_TABLE (see :file:`tables/parameters.py`). If you plan to create a bigger table try providing a guess; this will optimize the HDF5 B-Tree creation and management process time and memory used. chunkshape The shape of the data chunk to be read or written in a single HDF5 I/O operation. Filters are applied to those chunks of data. The rank of the chunkshape for tables must be 1. If None, a sensible value is calculated based on the expectedrows parameter (which is recommended). byteorder : str The byteorder of data *on disk*, specified as 'little' or 'big'. If this is not specified, the byteorder is that of the platform, unless you passed an array as the description, in which case its byteorder will be used. createparents : bool Whether to create the needed groups for the parent path to exist (not done by default). obj : python object The recarray to be saved. Accepted types are NumPy record arrays, as well as native Python sequences convertible to numpy record arrays. The *obj* parameter is optional and it can be provided in alternative to the *description* parameter. If both *obj* and *description* are provided they must be consistent with each other. .. versionadded:: 3.0 See Also -------- Table : for more information on tables """ if obj is not None: if not isinstance(obj, numpy.ndarray): raise TypeError('invalid obj parameter %r' % obj) descr, _ = descr_from_dtype(obj.dtype) if (description is not None and dtype_from_descr(description) != obj.dtype): raise TypeError('the desctiption parameter is not consistent ' 'with the data type of the obj parameter') elif description is None: description = descr parentnode = self._get_or_create_path(where, createparents) if description is None: raise ValueError("invalid table description: None") _checkfilters(filters) ptobj = Table(parentnode, name, description=description, title=title, filters=filters, expectedrows=expectedrows, chunkshape=chunkshape, byteorder=byteorder) if obj is not None: ptobj.append(obj) return ptobj createTable = previous_api(create_table) def create_array(self, where, name, obj=None, title="", byteorder=None, createparents=False, atom=None, shape=None): """Create a new array. Parameters ---------- where : str or Group The parent group from which the new array will hang. It can be a path string (for example '/level1/leaf5'), or a Group instance (see :ref:`GroupClassDescr`). name : str The name of the new array obj : python object The array or scalar to be saved. Accepted types are NumPy arrays and scalars, as well as native Python sequences and scalars, provided that values are regular (i.e. they are not like ``[[1,2],2]``) and homogeneous (i.e. all the elements are of the same type). Also, objects that have some of their dimensions equal to 0 are not supported (use an EArray node (see :ref:`EArrayClassDescr`) if you want to store an array with one of its dimensions equal to 0). .. versionchanged:: 3.0 The *Object parameter has been renamed into *obj*.* title : str A description for this node (it sets the TITLE HDF5 attribute on disk). byteorder : str The byteorder of the data *on disk*, specified as 'little' or 'big'. If this is not specified, the byteorder is that of the given object. createparents : bool, optional Whether to create the needed groups for the parent path to exist (not done by default). atom : Atom An Atom (see :ref:`AtomClassDescr`) instance representing the *type* and *shape* of the atomic objects to be saved. .. versionadded:: 3.0 shape : tuple of ints The shape of the stored array. .. versionadded:: 3.0 See Also -------- Array : for more information on arrays create_table : for more information on the rest of parameters """ if obj is None: if atom is None or shape is None: raise TypeError('if the obj parameter is not specified ' '(or None) then both the atom and shape ' 'parametes should be provided.') else: # Making strides=(0,...) below is a trick to create the # array fast and without memory consumption dflt = numpy.zeros((), dtype=atom.dtype) obj = numpy.ndarray(shape, dtype=atom.dtype, buffer=dflt, strides=(0,)*len(shape)) else: flavor = flavor_of(obj) # use a temporary object because converting obj at this stage # breaks some test. This is soultion performs a double, # potentially expensive, conversion of the obj parameter. _obj = array_as_internal(obj, flavor) if shape is not None and shape != _obj.shape: raise TypeError('the shape parameter do not match obj.shape') if atom is not None and atom.dtype != _obj.dtype: raise TypeError('the atom parameter is not consistent with ' 'the data type of the obj parameter') parentnode = self._get_or_create_path(where, createparents) return Array(parentnode, name, obj=obj, title=title, byteorder=byteorder) createArray = previous_api(create_array) def create_carray(self, where, name, atom=None, shape=None, title="", filters=None, chunkshape=None, byteorder=None, createparents=False, obj=None): """Create a new chunked array. Parameters ---------- where : str or Group The parent group from which the new array will hang. It can be a path string (for example '/level1/leaf5'), or a Group instance (see :ref:`GroupClassDescr`). name : str The name of the new array atom : Atom An Atom (see :ref:`AtomClassDescr`) instance representing the *type* and *shape* of the atomic objects to be saved. .. versionchanged:: 3.0 The *atom* parameter can be None (default) if *obj* is provided. shape : tuple The shape of the new array. .. versionchanged:: 3.0 The *shape* parameter can be None (default) if *obj* is provided. title : str, optional A description for this node (it sets the TITLE HDF5 attribute on disk). filters : Filters, optional An instance of the Filters class (see :ref:`FiltersClassDescr`) that provides information about the desired I/O filters to be applied during the life of this object. chunkshape : tuple or number or None, optional The shape of the data chunk to be read or written in a single HDF5 I/O operation. Filters are applied to those chunks of data. The dimensionality of chunkshape must be the same as that of shape. If None, a sensible value is calculated (which is recommended). byteorder : str, optional The byteorder of the data *on disk*, specified as 'little' or 'big'. If this is not specified, the byteorder is that of the given object. createparents : bool, optional Whether to create the needed groups for the parent path to exist (not done by default). obj : python object The array or scalar to be saved. Accepted types are NumPy arrays and scalars, as well as native Python sequences and scalars, provided that values are regular (i.e. they are not like ``[[1,2],2]``) and homogeneous (i.e. all the elements are of the same type). Also, objects that have some of their dimensions equal to 0 are not supported. Please use an EArray node (see :ref:`EArrayClassDescr`) if you want to store an array with one of its dimensions equal to 0. The *obj* parameter is optional and it can be provided in alternative to the *atom* and *shape* parameters. If both *obj* and *atom* and/or *shape* are provided they must be consistent with each other. .. versionadded:: 3.0 See Also -------- CArray : for more information on chunked arrays """ if obj is not None: flavor = flavor_of(obj) obj = array_as_internal(obj, flavor) if shape is not None and shape != obj.shape: raise TypeError('the shape parameter do not match obj.shape') else: shape = obj.shape if atom is not None and atom.dtype != obj.dtype: raise TypeError('the atom parameter is not consistent with ' 'the data type of the obj parameter') elif atom is None: atom = Atom.from_dtype(obj.dtype) parentnode = self._get_or_create_path(where, createparents) _checkfilters(filters) ptobj = CArray(parentnode, name, atom=atom, shape=shape, title=title, filters=filters, chunkshape=chunkshape, byteorder=byteorder) if obj is not None: ptobj[...] = obj return ptobj createCArray = previous_api(create_carray) def create_earray(self, where, name, atom=None, shape=None, title="", filters=None, expectedrows=1000, chunkshape=None, byteorder=None, createparents=False, obj=None): """Create a new enlargeable array. Parameters ---------- where : str or Group The parent group from which the new array will hang. It can be a path string (for example '/level1/leaf5'), or a Group instance (see :ref:`GroupClassDescr`). name : str The name of the new array atom : Atom An Atom (see :ref:`AtomClassDescr`) instance representing the *type* and *shape* of the atomic objects to be saved. .. versionchanged:: 3.0 The *atom* parameter can be None (default) if *obj* is provided. shape : tuple The shape of the new array. One (and only one) of the shape dimensions *must* be 0. The dimension being 0 means that the resulting EArray object can be extended along it. Multiple enlargeable dimensions are not supported right now. .. versionchanged:: 3.0 The *shape* parameter can be None (default) if *obj* is provided. title : str, optional A description for this node (it sets the TITLE HDF5 attribute on disk). expectedrows : int, optional A user estimate about the number of row elements that will be added to the growable dimension in the EArray node. If not provided, the default value is EXPECTED_ROWS_EARRAY (see tables/parameters.py). If you plan to create either a much smaller or a much bigger array try providing a guess; this will optimize the HDF5 B-Tree creation and management process time and the amount of memory used. chunkshape : tuple, numeric, or None, optional The shape of the data chunk to be read or written in a single HDF5 I/O operation. Filters are applied to those chunks of data. The dimensionality of chunkshape must be the same as that of shape (beware: no dimension should be 0 this time!). If None, a sensible value is calculated based on the expectedrows parameter (which is recommended). byteorder : str, optional The byteorder of the data *on disk*, specified as 'little' or 'big'. If this is not specified, the byteorder is that of the platform. createparents : bool, optional Whether to create the needed groups for the parent path to exist (not done by default). obj : python object The array or scalar to be saved. Accepted types are NumPy arrays and scalars, as well as native Python sequences and scalars, provided that values are regular (i.e. they are not like ``[[1,2],2]``) and homogeneous (i.e. all the elements are of the same type). The *obj* parameter is optional and it can be provided in alternative to the *atom* and *shape* parameters. If both *obj* and *atom* and/or *shape* are provided they must be consistent with each other. .. versionadded:: 3.0 See Also -------- EArray : for more information on enlargeable arrays """ if obj is not None: flavor = flavor_of(obj) obj = array_as_internal(obj, flavor) earray_shape = (0,) + obj.shape[1:] if shape is not None and shape != earray_shape: raise TypeError('the shape parameter is not compatible ' 'with obj.shape.') else: shape = earray_shape if atom is not None and atom.dtype != obj.dtype: raise TypeError('the atom parameter is not consistent with ' 'the data type of the obj parameter') elif atom is None: atom = Atom.from_dtype(obj.dtype) parentnode = self._get_or_create_path(where, createparents) _checkfilters(filters) ptobj = EArray(parentnode, name, atom=atom, shape=shape, title=title, filters=filters, expectedrows=expectedrows, chunkshape=chunkshape, byteorder=byteorder) if obj is not None: ptobj.append(obj) return ptobj createEArray = previous_api(create_earray) def create_vlarray(self, where, name, atom=None, title="", filters=None, expectedrows=None, chunkshape=None, byteorder=None, createparents=False, obj=None): """Create a new variable-length array. Parameters ---------- where : str or Group The parent group from which the new array will hang. It can be a path string (for example '/level1/leaf5'), or a Group instance (see :ref:`GroupClassDescr`). name : str The name of the new array atom : Atom An Atom (see :ref:`AtomClassDescr`) instance representing the *type* and *shape* of the atomic objects to be saved. .. versionchanged:: 3.0 The *atom* parameter can be None (default) if *obj* is provided. title : str, optional A description for this node (it sets the TITLE HDF5 attribute on disk). filters : Filters An instance of the Filters class (see :ref:`FiltersClassDescr`) that provides information about the desired I/O filters to be applied during the life of this object. expectedrows : int, optional A user estimate about the number of row elements that will be added to the growable dimension in the `VLArray` node. If not provided, the default value is ``EXPECTED_ROWS_VLARRAY`` (see ``tables/parameters.py``). If you plan to create either a much smaller or a much bigger `VLArray` try providing a guess; this will optimize the HDF5 B-Tree creation and management process time and the amount of memory used. .. versionadded:: 3.0 chunkshape : int or tuple of int, optional The shape of the data chunk to be read or written in a single HDF5 I/O operation. Filters are applied to those chunks of data. The dimensionality of chunkshape must be 1. If None, a sensible value is calculated (which is recommended). byteorder : str, optional The byteorder of the data *on disk*, specified as 'little' or 'big'. If this is not specified, the byteorder is that of the platform. createparents : bool, optional Whether to create the needed groups for the parent path to exist (not done by default). obj : python object The array or scalar to be saved. Accepted types are NumPy arrays and scalars, as well as native Python sequences and scalars, provided that values are regular (i.e. they are not like ``[[1,2],2]``) and homogeneous (i.e. all the elements are of the same type). The *obj* parameter is optional and it can be provided in alternative to the *atom* parameter. If both *obj* and *atom* and are provided they must be consistent with each other. .. versionadded:: 3.0 See Also -------- VLArray : for more informationon variable-length arrays .. versionchanged:: 3.0 The *expectedsizeinMB* parameter has been replaced by *expectedrows*. """ if obj is not None: flavor = flavor_of(obj) obj = array_as_internal(obj, flavor) if atom is not None and atom.dtype != obj.dtype: raise TypeError('the atom parameter is not consistent with ' 'the data type of the obj parameter') if atom is None: atom = Atom.from_dtype(obj.dtype) elif atom is None: raise ValueError('atom parameter cannot be None') parentnode = self._get_or_create_path(where, createparents) _checkfilters(filters) ptobj = VLArray(parentnode, name, atom=atom, title=title, filters=filters, expectedrows=expectedrows, chunkshape=chunkshape, byteorder=byteorder) if obj is not None: ptobj.append(obj) return ptobj createVLArray = previous_api(create_vlarray) def create_hard_link(self, where, name, target, createparents=False): """Create a hard link. Create a hard link to a `target` node with the given `name` in `where` location. `target` can be a node object or a path string. If `createparents` is true, the intermediate groups required for reaching `where` are created (the default is not doing so). The returned node is a regular `Group` or `Leaf` instance. """ targetnode = self.get_node(target) parentnode = self._get_or_create_path(where, createparents) linkextension._g_create_hard_link(parentnode, name, targetnode) # Refresh children names in link's parent node parentnode._g_add_children_names() # Return the target node return self.get_node(parentnode, name) createHardLink = previous_api(create_hard_link) def create_soft_link(self, where, name, target, createparents=False): """Create a soft link (aka symbolic link) to a `target` node. Create a soft link (aka symbolic link) to a `target` nodewith the given `name` in `where` location. `target` can be a node object or a path string. If `createparents` is true, the intermediate groups required for reaching `where` are created. (the default is not doing so). The returned node is a SoftLink instance. See the SoftLink class (in :ref:`SoftLinkClassDescr`) for more information on soft links. """ if not isinstance(target, str): if hasattr(target, '_v_pathname'): # quacks like a Node target = target._v_pathname else: raise ValueError( "`target` has to be a string or a node object") parentnode = self._get_or_create_path(where, createparents) slink = SoftLink(parentnode, name, target) # Refresh children names in link's parent node parentnode._g_add_children_names() return slink createSoftLink = previous_api(create_soft_link) def create_external_link(self, where, name, target, createparents=False): """Create an external link. Create an external link to a *target* node with the given *name* in *where* location. *target* can be a node object in another file or a path string in the form 'file:/path/to/node'. If *createparents* is true, the intermediate groups required for reaching *where* are created (the default is not doing so). The returned node is an :class:`ExternalLink` instance. """ if not isinstance(target, str): if hasattr(target, '_v_pathname'): # quacks like a Node target = target._v_file.filename + ':' + target._v_pathname else: raise ValueError( "`target` has to be a string or a node object") elif target.find(':/') == -1: raise ValueError( "`target` must expressed as 'file:/path/to/node'") parentnode = self._get_or_create_path(where, createparents) elink = ExternalLink(parentnode, name, target) # Refresh children names in link's parent node parentnode._g_add_children_names() return elink createExternalLink = previous_api(create_external_link) def _get_node(self, nodepath): # The root node is always at hand. if nodepath == '/': return self.root node = self._node_manager.get_node(nodepath) assert node is not None, "unable to instantiate node ``%s``" % nodepath return node _getNode = previous_api(_get_node) def get_node(self, where, name=None, classname=None): """Get the node under where with the given name. where can be a Node instance (see :ref:`NodeClassDescr`) or a path string leading to a node. If no name is specified, that node is returned. If a name is specified, this must be a string with the name of a node under where. In this case the where argument can only lead to a Group (see :ref:`GroupClassDescr`) instance (else a TypeError is raised). The node called name under the group where is returned. In both cases, if the node to be returned does not exist, a NoSuchNodeError is raised. Please note that hidden nodes are also considered. If the classname argument is specified, it must be the name of a class derived from Node. If the node is found but it is not an instance of that class, a NoSuchNodeError is also raised. """ self._check_open() # For compatibility with old default arguments. if name == '': name = None # Get the parent path (and maybe the node itself). if isinstance(where, Node): node = where node._g_check_open() # the node object must be open nodepath = where._v_pathname elif isinstance(where, (basestring, numpy.str_)): node = None if where.startswith('/'): nodepath = where else: raise NameError( "``where`` must start with a slash ('/')") else: raise TypeError( "``where`` is not a string nor a node: %r" % (where,)) # Get the name of the child node. if name is not None: node = None nodepath = join_path(nodepath, name) assert node is None or node._v_pathname == nodepath # Now we have the definitive node path, let us try to get the node. if node is None: node = self._get_node(nodepath) # Finally, check whether the desired node is an instance # of the expected class. if classname: class_ = get_class_by_name(classname) if not isinstance(node, class_): npathname = node._v_pathname nclassname = node.__class__.__name__ # This error message is right since it can never be shown # for ``classname in [None, 'Node']``. raise NoSuchNodeError( "could not find a ``%s`` node at ``%s``; " "instead, a ``%s`` node has been found there" % (classname, npathname, nclassname)) return node getNode = previous_api(get_node) def is_visible_node(self, path): """Is the node under `path` visible? If the node does not exist, a NoSuchNodeError is raised. """ # ``util.isvisiblepath()`` is still recommended for internal use. return self.get_node(path)._f_isvisible() isVisibleNode = previous_api(is_visible_node) def rename_node(self, where, newname, name=None, overwrite=False): """Change the name of the node specified by where and name to newname. Parameters ---------- where, name These arguments work as in :meth:`File.get_node`, referencing the node to be acted upon. newname : str The new name to be assigned to the node (a string). overwrite : bool Whether to recursively remove a node with the same newname if it already exists (not done by default). """ obj = self.get_node(where, name=name) obj._f_rename(newname, overwrite) renameNode = previous_api(rename_node) def move_node(self, where, newparent=None, newname=None, name=None, overwrite=False, createparents=False): """Move the node specified by where and name to newparent/newname. Parameters ---------- where, name : path These arguments work as in :meth:`File.get_node`, referencing the node to be acted upon. newparent The destination group the node will be moved into (a path name or a Group instance). If it is not specified or None, the current parent group is chosen as the new parent. newname The new name to be assigned to the node in its destination (a string). If it is not specified or None, the current name is chosen as the new name. Notes ----- The other arguments work as in :meth:`Node._f_move`. """ obj = self.get_node(where, name=name) obj._f_move(newparent, newname, overwrite, createparents) moveNode = previous_api(move_node) def copy_node(self, where, newparent=None, newname=None, name=None, overwrite=False, recursive=False, createparents=False, **kwargs): """Copy the node specified by where and name to newparent/newname. Parameters ---------- where : str These arguments work as in :meth:`File.get_node`, referencing the node to be acted upon. newparent : str or Group The destination group that the node will be copied into (a path name or a Group instance). If not specified or None, the current parent group is chosen as the new parent. newname : str The name to be assigned to the new copy in its destination (a string). If it is not specified or None, the current name is chosen as the new name. name : str These arguments work as in :meth:`File.get_node`, referencing the node to be acted upon. overwrite : bool, optional If True, the destination group will be overwritten if it already exists. Defaults to False. recursive : bool, optional If True, all descendant nodes of srcgroup are recursively copied. Defaults to False. createparents : bool, optional If True, any necessary parents of dstgroup will be created. Defaults to False. kwargs Additional keyword arguments can be used to customize the copying process. See the documentation of :meth:`Group._f_copy` for a description of those arguments. Returns ------- node : Node The newly created copy of the source node (i.e. the destination node). See :meth:`.Node._f_copy` for further details on the semantics of copying nodes. """ obj = self.get_node(where, name=name) if obj._v_depth == 0 and newparent and not newname: npobj = self.get_node(newparent) if obj._v_file is not npobj._v_file: # Special case for copying file1:/ --> file2:/path self.root._f_copy_children(npobj, overwrite=overwrite, recursive=recursive, **kwargs) return npobj else: raise IOError( "You cannot copy a root group over the same file") return obj._f_copy(newparent, newname, overwrite, recursive, createparents, **kwargs) copyNode = previous_api(copy_node) def remove_node(self, where, name=None, recursive=False): """Remove the object node *name* under *where* location. Parameters ---------- where, name These arguments work as in :meth:`File.get_node`, referencing the node to be acted upon. recursive : bool If not supplied or false, the node will be removed only if it has no children; if it does, a NodeError will be raised. If supplied with a true value, the node and all its descendants will be completely removed. """ obj = self.get_node(where, name=name) obj._f_remove(recursive) removeNode = previous_api(remove_node) def get_node_attr(self, where, attrname, name=None): """Get a PyTables attribute from the given node. Parameters ---------- where, name These arguments work as in :meth:`File.get_node`, referencing the node to be acted upon. attrname The name of the attribute to retrieve. If the named attribute does not exist, an AttributeError is raised. """ obj = self.get_node(where, name=name) return obj._f_getattr(attrname) getNodeAttr = previous_api(get_node_attr) def set_node_attr(self, where, attrname, attrvalue, name=None): """Set a PyTables attribute for the given node. Parameters ---------- where, name These arguments work as in :meth:`File.get_node`, referencing the node to be acted upon. attrname The name of the attribute to set. attrvalue The value of the attribute to set. Any kind of Python object (like strings, ints, floats, lists, tuples, dicts, small NumPy objects ...) can be stored as an attribute. However, if necessary, pickle is automatically used so as to serialize objects that you might want to save. See the :class:`AttributeSet` class for details. Notes ----- If the node already has a large number of attributes, a PerformanceWarning is issued. """ obj = self.get_node(where, name=name) obj._f_setattr(attrname, attrvalue) setNodeAttr = previous_api(set_node_attr) def del_node_attr(self, where, attrname, name=None): """Delete a PyTables attribute from the given node. Parameters ---------- where, name These arguments work as in :meth:`File.get_node`, referencing the node to be acted upon. attrname The name of the attribute to delete. If the named attribute does not exist, an AttributeError is raised. """ obj = self.get_node(where, name=name) obj._f_delattr(attrname) delNodeAttr = previous_api(del_node_attr) def copy_node_attrs(self, where, dstnode, name=None): """Copy PyTables attributes from one node to another. Parameters ---------- where, name These arguments work as in :meth:`File.get_node`, referencing the node to be acted upon. dstnode The destination node where the attributes will be copied to. It can be a path string or a Node instance (see :ref:`NodeClassDescr`). """ srcobject = self.get_node(where, name=name) dstobject = self.get_node(dstnode) srcobject._v_attrs._f_copy(dstobject) copyNodeAttrs = previous_api(copy_node_attrs) def copy_children(self, srcgroup, dstgroup, overwrite=False, recursive=False, createparents=False, **kwargs): """Copy the children of a group into another group. Parameters ---------- srcgroup : str The group to copy from. dstgroup : str The destination group. overwrite : bool, optional If True, the destination group will be overwritten if it already exists. Defaults to False. recursive : bool, optional If True, all descendant nodes of srcgroup are recursively copied. Defaults to False. createparents : bool, optional If True, any necessary parents of dstgroup will be created. Defaults to False. kwargs : dict Additional keyword arguments can be used to customize the copying process. See the documentation of :meth:`Group._f_copy_children` for a description of those arguments. """ srcgroup = self.get_node(srcgroup) # Does the source node exist? self._check_group(srcgroup) # Is it a group? srcgroup._f_copy_children( dstgroup, overwrite, recursive, createparents, **kwargs) copyChildren = previous_api(copy_children) def copy_file(self, dstfilename, overwrite=False, **kwargs): """Copy the contents of this file to dstfilename. Parameters ---------- dstfilename : str A path string indicating the name of the destination file. If it already exists, the copy will fail with an IOError, unless the overwrite argument is true. overwrite : bool, optional If true, the destination file will be overwritten if it already exists. In this case, the destination file must be closed, or errors will occur. Defaults to False. kwargs Additional keyword arguments discussed below. Notes ----- Additional keyword arguments may be passed to customize the copying process. For instance, title and filters may be changed, user attributes may be or may not be copied, data may be sub-sampled, stats may be collected, etc. Arguments unknown to nodes are simply ignored. Check the documentation for copying operations of nodes to see which options they support. In addition, it recognizes the names of parameters present in :file:`tables/parameters.py` as additional keyword arguments. See :ref:`parameter_files` for a detailed info on the supported parameters. Copying a file usually has the beneficial side effect of creating a more compact and cleaner version of the original file. """ self._check_open() # Check that we are not treading our own shoes if os.path.abspath(self.filename) == os.path.abspath(dstfilename): raise IOError("You cannot copy a file over itself") # Compute default arguments. # These are *not* passed on. filters = kwargs.pop('filters', None) if filters is None: # By checking the HDF5 attribute, we avoid setting filters # in the destination file if not explicitly set in the # source file. Just by assigning ``self.filters`` we would # not be able to tell. filters = getattr(self.root._v_attrs, 'FILTERS', None) copyuserattrs = kwargs.get('copyuserattrs', True) title = kwargs.pop('title', self.title) if os.path.isfile(dstfilename) and not overwrite: raise IOError(("file ``%s`` already exists; " "you may want to use the ``overwrite`` " "argument") % dstfilename) # Create destination file, overwriting it. dstfileh = open_file( dstfilename, mode="w", title=title, filters=filters, **kwargs) try: # Maybe copy the user attributes of the root group. if copyuserattrs: self.root._v_attrs._f_copy(dstfileh.root) # Copy the rest of the hierarchy. self.root._f_copy_children(dstfileh.root, recursive=True, **kwargs) finally: dstfileh.close() copyFile = previous_api(copy_file) def list_nodes(self, where, classname=None): """Return a *list* with children nodes hanging from where. This is a list-returning version of :meth:`File.iter_nodes`. """ group = self.get_node(where) # Does the parent exist? self._check_group(group) # Is it a group? return group._f_list_nodes(classname) listNodes = previous_api(list_nodes) def iter_nodes(self, where, classname=None): """Iterate over children nodes hanging from where. Parameters ---------- where This argument works as in :meth:`File.get_node`, referencing the node to be acted upon. classname If the name of a class derived from Node (see :ref:`NodeClassDescr`) is supplied, only instances of that class (or subclasses of it) will be returned. Notes ----- The returned nodes are alphanumerically sorted by their name. This is an iterator version of :meth:`File.list_nodes`. """ group = self.get_node(where) # Does the parent exist? self._check_group(group) # Is it a group? return group._f_iter_nodes(classname) iterNodes = previous_api(iter_nodes) def __contains__(self, path): """Is there a node with that path? Returns True if the file has a node with the given path (a string), False otherwise. """ try: self.get_node(path) except NoSuchNodeError: return False else: return True def __iter__(self): """Recursively iterate over the nodes in the tree. This is equivalent to calling :meth:`File.walk_nodes` with no arguments. Examples -------- :: # Recursively list all the nodes in the object tree. h5file = tables.open_file('vlarray1.h5') print("All nodes in the object tree:") for node in h5file: print(node) """ return self.walk_nodes('/') def walk_nodes(self, where="/", classname=None): """Recursively iterate over nodes hanging from where. Parameters ---------- where : str or Group, optional If supplied, the iteration starts from (and includes) this group. It can be a path string or a Group instance (see :ref:`GroupClassDescr`). classname If the name of a class derived from Node (see :ref:`GroupClassDescr`) is supplied, only instances of that class (or subclasses of it) will be returned. Notes ----- This version iterates over the leaves in the same group in order to avoid having a list referencing to them and thus, preventing the LRU cache to remove them after their use. Examples -------- :: # Recursively print all the nodes hanging from '/detector'. print("Nodes hanging from group '/detector':") for node in h5file.walk_nodes('/detector', classname='EArray'): print(node) """ class_ = get_class_by_name(classname) if class_ is Group: # only groups for group in self.walk_groups(where): yield group elif class_ is Node: # all nodes yield self.get_node(where) for group in self.walk_groups(where): for leaf in self.iter_nodes(group): yield leaf else: # only nodes of the named type for group in self.walk_groups(where): for leaf in self.iter_nodes(group, classname): yield leaf walkNodes = previous_api(walk_nodes) def walk_groups(self, where="/"): """Recursively iterate over groups (not leaves) hanging from where. The where group itself is listed first (preorder), then each of its child groups (following an alphanumerical order) is also traversed, following the same procedure. If where is not supplied, the root group is used. The where argument can be a path string or a Group instance (see :ref:`GroupClassDescr`). """ group = self.get_node(where) # Does the parent exist? self._check_group(group) # Is it a group? return group._f_walk_groups() walkGroups = previous_api(walk_groups) def _check_open(self): """Check the state of the file. If the file is closed, a `ClosedFileError` is raised. """ if not self.isopen: raise ClosedFileError("the file object is closed") _checkOpen = previous_api(_check_open) def _iswritable(self): """Is this file writable?""" return self.mode in ('w', 'a', 'r+') _isWritable = previous_api(_iswritable) def _check_writable(self): """Check whether the file is writable. If the file is not writable, a `FileModeError` is raised. """ if not self._iswritable(): raise FileModeError("the file is not writable") _checkWritable = previous_api(_check_writable) def _check_group(self, node): # `node` must already be a node. if not isinstance(node, Group): raise TypeError("node ``%s`` is not a group" % (node._v_pathname,)) _checkGroup = previous_api(_check_group) # def is_undo_enabled(self): """Is the Undo/Redo mechanism enabled? Returns True if the Undo/Redo mechanism has been enabled for this file, False otherwise. Please note that this mechanism is persistent, so a newly opened PyTables file may already have Undo/Redo support enabled. """ self._check_open() return self._undoEnabled isUndoEnabled = previous_api(is_undo_enabled) def _check_undo_enabled(self): if not self._undoEnabled: raise UndoRedoError("Undo/Redo feature is currently disabled!") _checkUndoEnabled = previous_api(_check_undo_enabled) def _create_transaction_group(self): tgroup = TransactionGroupG( self.root, _trans_group_name, "Transaction information container", new=True) # The format of the transaction container. tgroup._v_attrs._g__setattr('FORMATVERSION', _trans_version) return tgroup _createTransactionGroup = previous_api(_create_transaction_group) def _create_transaction(self, troot, tid): return TransactionG( troot, _trans_name % tid, "Transaction number %d" % tid, new=True) _createTransaction = previous_api(_create_transaction) def _create_mark(self, trans, mid): return MarkG( trans, _markName % mid, "Mark number %d" % mid, new=True) _createMark = previous_api(_create_mark) def enable_undo(self, filters=Filters(complevel=1)): """Enable the Undo/Redo mechanism. This operation prepares the database for undoing and redoing modifications in the node hierarchy. This allows :meth:`File.mark`, :meth:`File.undo`, :meth:`File.redo` and other methods to be called. The filters argument, when specified, must be an instance of class Filters (see :ref:`FiltersClassDescr`) and is meant for setting the compression values for the action log. The default is having compression enabled, as the gains in terms of space can be considerable. You may want to disable compression if you want maximum speed for Undo/Redo operations. Calling this method when the Undo/Redo mechanism is already enabled raises an UndoRedoError. """ maxundo = self.params['MAX_UNDO_PATH_LENGTH'] class ActionLog(NotLoggedMixin, Table): pass class ActionLogDesc(IsDescription): opcode = UInt8Col(pos=0) arg1 = StringCol(maxundo, pos=1, dflt=b"") arg2 = StringCol(maxundo, pos=2, dflt=b"") self._check_open() # Enabling several times is not allowed to avoid the user having # the illusion that a new implicit mark has been created # when calling enable_undo for the second time. if self.is_undo_enabled(): raise UndoRedoError("Undo/Redo feature is already enabled!") self._markers = {} self._seqmarkers = [] self._nmarks = 0 self._curtransaction = 0 self._curmark = -1 # No marks yet # Get the Group for keeping user actions try: tgroup = self.get_node(_trans_group_path) except NodeError: # The file is going to be changed. self._check_writable() # A transaction log group does not exist. Create it tgroup = self._create_transaction_group() # Create a transaction. self._trans = self._create_transaction( tgroup, self._curtransaction) # Create an action log self._actionlog = ActionLog( tgroup, _action_log_name, ActionLogDesc, "Action log", filters=filters) # Create an implicit mark self._actionlog.append([(_op_to_code["MARK"], str(0), '')]) self._nmarks += 1 self._seqmarkers.append(0) # current action is 0 # Create a group for mark 0 self._create_mark(self._trans, 0) # Initialize the marker pointer self._curmark = int(self._nmarks - 1) # Initialize the action pointer self._curaction = self._actionlog.nrows - 1 else: # The group seems to exist already # Get the default transaction self._trans = tgroup._f_get_child( _trans_name % self._curtransaction) # Open the action log and go to the end of it self._actionlog = tgroup.actionlog for row in self._actionlog: if row["opcode"] == _op_to_code["MARK"]: name = row["arg2"].decode('utf-8') self._markers[name] = self._nmarks self._seqmarkers.append(row.nrow) self._nmarks += 1 # Get the current mark and current action self._curmark = int(self._actionlog.attrs.CURMARK) self._curaction = self._actionlog.attrs.CURACTION # The Undo/Redo mechanism has been enabled. self._undoEnabled = True enableUndo = previous_api(enable_undo) def disable_undo(self): """Disable the Undo/Redo mechanism. Disabling the Undo/Redo mechanism leaves the database in the current state and forgets past and future database states. This makes :meth:`File.mark`, :meth:`File.undo`, :meth:`File.redo` and other methods fail with an UndoRedoError. Calling this method when the Undo/Redo mechanism is already disabled raises an UndoRedoError. """ self._check_open() if not self.is_undo_enabled(): raise UndoRedoError("Undo/Redo feature is already disabled!") # The file is going to be changed. self._check_writable() del self._markers del self._seqmarkers del self._curmark del self._curaction del self._curtransaction del self._nmarks del self._actionlog # Recursively delete the transaction group tnode = self.get_node(_trans_group_path) tnode._g_remove(recursive=1) # The Undo/Redo mechanism has been disabled. self._undoEnabled = False disableUndo = previous_api(disable_undo) def mark(self, name=None): """Mark the state of the database. Creates a mark for the current state of the database. A unique (and immutable) identifier for the mark is returned. An optional name (a string) can be assigned to the mark. Both the identifier of a mark and its name can be used in :meth:`File.undo` and :meth:`File.redo` operations. When the name has already been used for another mark, an UndoRedoError is raised. This method can only be called when the Undo/Redo mechanism has been enabled. Otherwise, an UndoRedoError is raised. """ self._check_open() self._check_undo_enabled() if name is None: name = '' else: if not isinstance(name, str): raise TypeError("Only strings are allowed as mark names. " "You passed object: '%s'" % name) if name in self._markers: raise UndoRedoError("Name '%s' is already used as a marker " "name. Try another one." % name) # The file is going to be changed. self._check_writable() self._markers[name] = self._curmark + 1 # Create an explicit mark # Insert the mark in the action log self._log("MARK", str(self._curmark + 1), name) self._curmark += 1 self._nmarks = self._curmark + 1 self._seqmarkers.append(self._curaction) # Create a group for the current mark self._create_mark(self._trans, self._curmark) return self._curmark def _log(self, action, *args): """Log an action. The `action` must be an all-uppercase string identifying it. Arguments must also be strings. This method should be called once the action has been completed. This method can only be called when the Undo/Redo mechanism has been enabled. Otherwise, an `UndoRedoError` is raised. """ assert self.is_undo_enabled() maxundo = self.params['MAX_UNDO_PATH_LENGTH'] # Check whether we are at the end of the action log or not if self._curaction != self._actionlog.nrows - 1: # We are not, so delete the trailing actions self._actionlog.remove_rows(self._curaction + 1, self._actionlog.nrows) # Reset the current marker group mnode = self.get_node(_markPath % (self._curtransaction, self._curmark)) mnode._g_reset() # Delete the marker groups with backup objects for mark in xrange(self._curmark + 1, self._nmarks): mnode = self.get_node(_markPath % (self._curtransaction, mark)) mnode._g_remove(recursive=1) # Update the new number of marks self._nmarks = self._curmark + 1 self._seqmarkers = self._seqmarkers[:self._nmarks] if action not in _op_to_code: # INTERNAL raise UndoRedoError("Action ``%s`` not in ``_op_to_code`` " "dictionary: %r" % (action, _op_to_code)) arg1 = "" arg2 = "" if len(args) <= 1: arg1 = args[0] elif len(args) <= 2: arg1 = args[0] arg2 = args[1] else: # INTERNAL raise UndoRedoError("Too many parameters for action log: " "%r").with_traceback(args) if (len(arg1) > maxundo or len(arg2) > maxundo): # INTERNAL raise UndoRedoError("Parameter arg1 or arg2 is too long: " "(%r, %r)" % (arg1, arg2)) # print("Logging-->", (action, arg1, arg2)) self._actionlog.append([(_op_to_code[action], arg1.encode('utf-8'), arg2.encode('utf-8'))]) self._curaction += 1 def _get_mark_id(self, mark): """Get an integer markid from a mark sequence number or name.""" if isinstance(mark, int): markid = mark elif isinstance(mark, str): if mark not in self._markers: lmarkers = sorted(self._markers.iterkeys()) raise UndoRedoError("The mark that you have specified has not " "been found in the internal marker list: " "%r" % lmarkers) markid = self._markers[mark] else: raise TypeError("Parameter mark can only be an integer or a " "string, and you passed a type <%s>" % type(mark)) # print("markid, self._nmarks:", markid, self._nmarks) return markid _getMarkID = previous_api(_get_mark_id) def _get_final_action(self, markid): """Get the action to go. It does not touch the self private attributes """ if markid > self._nmarks - 1: # The required mark is beyond the end of the action log # The final action is the last row return self._actionlog.nrows elif markid <= 0: # The required mark is the first one # return the first row return 0 return self._seqmarkers[markid] _getFinalAction = previous_api(_get_final_action) def _doundo(self, finalaction, direction): """Undo/Redo actions up to final action in the specificed direction.""" if direction < 0: actionlog = \ self._actionlog[finalaction + 1:self._curaction + 1][::-1] else: actionlog = self._actionlog[self._curaction:finalaction] # Uncomment this for debugging # print("curaction, finalaction, direction", \ # self._curaction, finalaction, direction) for i in xrange(len(actionlog)): if actionlog['opcode'][i] != _op_to_code["MARK"]: # undo/redo the action if direction > 0: # Uncomment this for debugging # print("redo-->", \ # _code_to_op[actionlog['opcode'][i]],\ # actionlog['arg1'][i],\ # actionlog['arg2'][i]) undoredo.redo(self, # _code_to_op[actionlog['opcode'][i]], # The next is a workaround for python < 2.5 _code_to_op[int(actionlog['opcode'][i])], actionlog['arg1'][i].decode('utf8'), actionlog['arg2'][i].decode('utf8')) else: # Uncomment this for debugging # print("undo-->", \ # _code_to_op[actionlog['opcode'][i]],\ # actionlog['arg1'][i].decode('utf8'),\ # actionlog['arg2'][i].decode('utf8')) undoredo.undo(self, # _code_to_op[actionlog['opcode'][i]], # The next is a workaround for python < 2.5 _code_to_op[int(actionlog['opcode'][i])], actionlog['arg1'][i].decode('utf8'), actionlog['arg2'][i].decode('utf8')) else: if direction > 0: self._curmark = int(actionlog['arg1'][i]) else: self._curmark = int(actionlog['arg1'][i]) - 1 # Protection against negative marks if self._curmark < 0: self._curmark = 0 self._curaction += direction def undo(self, mark=None): """Go to a past state of the database. Returns the database to the state associated with the specified mark. Both the identifier of a mark and its name can be used. If the mark is omitted, the last created mark is used. If there are no past marks, or the specified mark is not older than the current one, an UndoRedoError is raised. This method can only be called when the Undo/Redo mechanism has been enabled. Otherwise, an UndoRedoError is raised. """ self._check_open() self._check_undo_enabled() # print("(pre)UNDO: (curaction, curmark) = (%s,%s)" % \ # (self._curaction, self._curmark)) if mark is None: markid = self._curmark # Correction if we are settled on top of a mark opcode = self._actionlog.cols.opcode if opcode[self._curaction] == _op_to_code["MARK"]: markid -= 1 else: # Get the mark ID number markid = self._get_mark_id(mark) # Get the final action ID to go finalaction = self._get_final_action(markid) if finalaction > self._curaction: raise UndoRedoError("Mark ``%s`` is newer than the current mark. " "Use `redo()` or `goto()` instead." % (mark,)) # The file is going to be changed. self._check_writable() # Try to reach this mark by unwinding actions in the log self._doundo(finalaction - 1, -1) if self._curaction < self._actionlog.nrows - 1: self._curaction += 1 self._curmark = int(self._actionlog.cols.arg1[self._curaction]) # print("(post)UNDO: (curaction, curmark) = (%s,%s)" % \ # (self._curaction, self._curmark)) def redo(self, mark=None): """Go to a future state of the database. Returns the database to the state associated with the specified mark. Both the identifier of a mark and its name can be used. If the `mark` is omitted, the next created mark is used. If there are no future marks, or the specified mark is not newer than the current one, an UndoRedoError is raised. This method can only be called when the Undo/Redo mechanism has been enabled. Otherwise, an UndoRedoError is raised. """ self._check_open() self._check_undo_enabled() # print("(pre)REDO: (curaction, curmark) = (%s, %s)" % \ # (self._curaction, self._curmark)) if self._curaction >= self._actionlog.nrows - 1: # We are at the end of log, so no action return if mark is None: mark = self._curmark + 1 elif mark == -1: mark = int(self._nmarks) # Go beyond the mark bounds up to the end # Get the mark ID number markid = self._get_mark_id(mark) finalaction = self._get_final_action(markid) if finalaction < self._curaction + 1: raise UndoRedoError("Mark ``%s`` is older than the current mark. " "Use `redo()` or `goto()` instead." % (mark,)) # The file is going to be changed. self._check_writable() # Get the final action ID to go self._curaction += 1 # Try to reach this mark by redoing the actions in the log self._doundo(finalaction, 1) # Increment the current mark only if we are not at the end of marks if self._curmark < self._nmarks - 1: self._curmark += 1 if self._curaction > self._actionlog.nrows - 1: self._curaction = self._actionlog.nrows - 1 # print("(post)REDO: (curaction, curmark) = (%s,%s)" % \ # (self._curaction, self._curmark)) def goto(self, mark): """Go to a specific mark of the database. Returns the database to the state associated with the specified mark. Both the identifier of a mark and its name can be used. This method can only be called when the Undo/Redo mechanism has been enabled. Otherwise, an UndoRedoError is raised. """ self._check_open() self._check_undo_enabled() if mark == -1: # Special case mark = self._nmarks # Go beyond the mark bounds up to the end # Get the mark ID number markid = self._get_mark_id(mark) finalaction = self._get_final_action(markid) if finalaction < self._curaction: self.undo(mark) else: self.redo(mark) def get_current_mark(self): """Get the identifier of the current mark. Returns the identifier of the current mark. This can be used to know the state of a database after an application crash, or to get the identifier of the initial implicit mark after a call to :meth:`File.enable_undo`. This method can only be called when the Undo/Redo mechanism has been enabled. Otherwise, an UndoRedoError is raised. """ self._check_open() self._check_undo_enabled() return self._curmark getCurrentMark = previous_api(get_current_mark) def _shadow_name(self): """Compute and return a shadow name. Computes the current shadow name according to the current transaction, mark and action. It returns a tuple with the shadow parent node and the name of the shadow in it. """ parent = self.get_node( _shadow_parent % (self._curtransaction, self._curmark)) name = _shadow_name % (self._curaction,) return (parent, name) _shadowName = previous_api(_shadow_name) # def flush(self): """Flush all the alive leaves in the object tree.""" self._check_open() # Flush the cache to disk self._node_manager.flush_nodes() self._flush_file(0) # 0 means local scope, 1 global (virtual) scope def close(self): """Flush all the alive leaves in object tree and close the file.""" # If the file is already closed, return immediately if not self.isopen: return # If this file has been opened more than once, decrease the # counter and return if self._open_count > 1: self._open_count -= 1 return filename = self.filename if self._undoEnabled and self._iswritable(): # Save the current mark and current action self._actionlog.attrs._g__setattr("CURMARK", self._curmark) self._actionlog.attrs._g__setattr("CURACTION", self._curaction) # Close all loaded nodes. self.root._f_close() self._node_manager.shutdown() # Post-conditions assert len(self._node_manager.cache) == 0, \ ("cached nodes remain after closing: %s" % list(self._node_manager.cache)) # No other nodes should have been revived. assert len(self._node_manager.registry) == 0, \ ("alive nodes remain after closing: %s" % list(self._node_manager.registry)) # Close the file self._close_file() # After the objects are disconnected, destroy the # object dictionary using the brute force ;-) # This should help to the garbage collector self.__dict__.clear() # Set the flag to indicate that the file is closed self.isopen = 0 # Restore the filename attribute that is used by _FileRegistry self.filename = filename # Delete the entry from he registry of opened files _open_files.remove(self) def __enter__(self): """Enter a context and return the same file.""" return self def __exit__(self, *exc_info): """Exit a context and close the file.""" self.close() return False # do not hide exceptions def __str__(self): """Return a short string representation of the object tree. Examples -------- :: >>> f = tables.open_file('data/test.h5') >>> print(f) data/test.h5 (File) 'Table Benchmark' Last modif.: 'Mon Sep 20 12:40:47 2004' Object Tree: / (Group) 'Table Benchmark' /tuple0 (Table(100,)) 'This is the table title' /group0 (Group) '' /group0/tuple1 (Table(100,)) 'This is the table title' /group0/group1 (Group) '' /group0/group1/tuple2 (Table(100,)) 'This is the table title' /group0/group1/group2 (Group) '' """ if not self.isopen: return "" # Print all the nodes (Group and Leaf objects) on object tree try: date = time.asctime(time.localtime(os.stat(self.filename)[8])) except OSError: # in-memory file date = "" astring = self.filename + ' (File) ' + repr(self.title) + '\n' # astring += 'root_uep :=' + repr(self.root_uep) + '; ' # astring += 'format_version := ' + self.format_version + '\n' # astring += 'filters :=' + repr(self.filters) + '\n' astring += 'Last modif.: ' + repr(date) + '\n' astring += 'Object Tree: \n' for group in self.walk_groups("/"): astring += str(group) + '\n' for kind in self._node_kinds[1:]: for node in self.list_nodes(group, kind): astring += str(node) + '\n' return astring def __repr__(self): """Return a detailed string representation of the object tree.""" if not self.isopen: return "" # Print all the nodes (Group and Leaf objects) on object tree astring = 'File(filename=' + str(self.filename) + \ ', title=' + repr(self.title) + \ ', mode=' + repr(self.mode) + \ ', root_uep=' + repr(self.root_uep) + \ ', filters=' + repr(self.filters) + \ ')\n' for group in self.walk_groups("/"): astring += str(group) + '\n' for kind in self._node_kinds[1:]: for node in self.list_nodes(group, kind): astring += repr(node) + '\n' return astring def _update_node_locations(self, oldpath, newpath): """Update location information of nodes under `oldpath`. This only affects *already loaded* nodes. """ oldprefix = oldpath + '/' # root node can not be renamed, anyway oldprefix_len = len(oldprefix) # Update alive and dead descendents. for cache in [self._node_manager.cache, self._node_manager.registry]: for nodepath in cache: if nodepath.startswith(oldprefix) and nodepath != oldprefix: nodesuffix = nodepath[oldprefix_len:] newnodepath = join_path(newpath, nodesuffix) newnodeppath = split_path(newnodepath)[0] descendent_node = self._get_node(nodepath) descendent_node._g_update_location(newnodeppath) _updateNodeLocations = previous_api(_update_node_locations) # If a user hits ^C during a run, it is wise to gracefully close the # opened files. import atexit atexit.register(_open_files.close_all) ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/filters.py000066400000000000000000000336701231437614300170140ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: 2007-02-23 # Author: Ivan Vilata i Balaguer - ivan at selidor dot net # # $Id$ # ######################################################################## """Functionality related with filters in a PyTables file.""" # Imports # ======= import warnings import numpy from tables import ( utilsextension, blosc_compressor_list, blosc_compcode_to_compname) from tables.exceptions import FiltersWarning # Public variables # ================ __docformat__ = 'reStructuredText' """The format of documentation strings in this module.""" all_complibs = ['zlib', 'lzo', 'bzip2', 'blosc'] all_complibs += ['blosc:%s' % cname for cname in blosc_compressor_list()] """List of all compression libraries.""" foreign_complibs = ['szip'] """List of known but unsupported compression libraries.""" default_complib = 'zlib' """The default compression library.""" # Private variables # ================= _shuffle_flag = 0x1 _fletcher32_flag = 0x2 _rounding_flag = 0x4 # Classes # ======= class Filters(object): """Container for filter properties. This class is meant to serve as a container that keeps information about the filter properties associated with the chunked leaves, that is Table, CArray, EArray and VLArray. Instances of this class can be directly compared for equality. Parameters ---------- complevel : int Specifies a compression level for data. The allowed range is 0-9. A value of 0 (the default) disables compression. complib : str Specifies the compression library to be used. Right now, 'zlib' (the default), 'lzo', 'bzip2' and 'blosc' are supported. Additional compressors for Blosc like 'blosc:blosclz' ('blosclz' is the default in case the additional compressor is not specified), 'blosc:lz4', 'blosc:lz4hc', 'blosc:snappy' and 'blosc:zlib' are supported too. Specifying a compression library which is not available in the system issues a FiltersWarning and sets the library to the default one. shuffle : bool Whether or not to use the *Shuffle* filter in the HDF5 library. This is normally used to improve the compression ratio. A false value disables shuffling and a true one enables it. The default value depends on whether compression is enabled or not; if compression is enabled, shuffling defaults to be enabled, else shuffling is disabled. Shuffling can only be used when compression is enabled. fletcher32 : bool Whether or not to use the *Fletcher32* filter in the HDF5 library. This is used to add a checksum on each data chunk. A false value (the default) disables the checksum. least_significant_digit : int If specified, data will be truncated (quantized). In conjunction with enabling compression, this produces 'lossy', but significantly more efficient compression. For example, if *least_significant_digit=1*, data will be quantized using ``around(scale*data)/scale``, where ``scale = 2**bits``, and bits is determined so that a precision of 0.1 is retained (in this case bits=4). Default is *None*, or no quantization. .. note:: quantization is only applied if some form of compression is enabled Examples -------- This is a small example on using the Filters class:: import numpy from tables import * fileh = open_file('test5.h5', mode='w') atom = Float32Atom() filters = Filters(complevel=1, complib='blosc', fletcher32=True) arr = fileh.create_earray(fileh.root, 'earray', atom, (0,2), "A growable array", filters=filters) # Append several rows in only one call arr.append(numpy.array([[1., 2.], [2., 3.], [3., 4.]], dtype=numpy.float32)) # Print information on that enlargeable array print("Result Array:") print(repr(arr)) fileh.close() This enforces the use of the Blosc library, a compression level of 1 and a Fletcher32 checksum filter as well. See the output of this example:: Result Array: /earray (EArray(3, 2), fletcher32, shuffle, blosc(1)) 'A growable array' type = float32 shape = (3, 2) itemsize = 4 nrows = 3 extdim = 0 flavor = 'numpy' byteorder = 'little' .. rubric:: Filters attributes .. attribute:: fletcher32 Whether the *Fletcher32* filter is active or not. .. attribute:: complevel The compression level (0 disables compression). .. attribute:: complib The compression filter used (irrelevant when compression is not enabled). .. attribute:: shuffle Whether the *Shuffle* filter is active or not. """ @classmethod def _from_leaf(class_, leaf): # Get a dictionary with all the filters parent = leaf._v_parent filters_dict = utilsextension.get_filters(parent._v_objectid, leaf._v_name) if filters_dict is None: filters_dict = {} # not chunked kwargs = dict(complevel=0, shuffle=False, fletcher32=False, # all off least_significant_digit=None, _new=False) for (name, values) in filters_dict.iteritems(): if name == 'deflate': name = 'zlib' if name in all_complibs: kwargs['complib'] = name if name == "blosc": kwargs['complevel'] = values[4] # Shuffle filter is internal to blosc if values[5]: kwargs['shuffle'] = True # In Blosc 1.3 another parameter is used for the compressor if len(values) > 6: cname = blosc_compcode_to_compname(values[6]) kwargs['complib'] = "blosc:%s" % cname else: kwargs['complevel'] = values[0] elif name in foreign_complibs: kwargs['complib'] = name kwargs['complevel'] = 1 # any nonzero value will do elif name in ['shuffle', 'fletcher32']: kwargs[name] = True return class_(**kwargs) @classmethod def _unpack(class_, packed): """Create a new `Filters` object from a packed version. >>> Filters._unpack(0) Filters(complevel=0, shuffle=False, fletcher32=False, least_significant_digit=None) >>> Filters._unpack(0x101) Filters(complevel=1, complib='zlib', shuffle=False, fletcher32=False, least_significant_digit=None) >>> Filters._unpack(0x30109) Filters(complevel=9, complib='zlib', shuffle=True, fletcher32=True, least_significant_digit=None) >>> Filters._unpack(0x3010A) Traceback (most recent call last): ... ValueError: compression level must be between 0 and 9 >>> Filters._unpack(0x1) Traceback (most recent call last): ... ValueError: invalid compression library id: 0 """ kwargs = {'_new': False} # Byte 0: compression level. kwargs['complevel'] = complevel = packed & 0xff packed >>= 8 # Byte 1: compression library id (0 for none). if complevel > 0: complib_id = int(packed & 0xff) if not (0 < complib_id <= len(all_complibs)): raise ValueError("invalid compression library id: %d" % complib_id) kwargs['complib'] = all_complibs[complib_id - 1] packed >>= 8 # Byte 2: parameterless filters. kwargs['shuffle'] = packed & _shuffle_flag kwargs['fletcher32'] = packed & _fletcher32_flag has_rounding = packed & _rounding_flag packed >>= 8 # Byte 3: least significant digit. if has_rounding: kwargs['least_significant_digit'] = numpy.int8(packed & 0xff) else: kwargs['least_significant_digit'] = None return class_(**kwargs) def _pack(self): """Pack the `Filters` object into a 64-bit NumPy integer.""" packed = numpy.int64(0) # Byte 3: least significant digit. if self.least_significant_digit is not None: #assert isinstance(self.least_significant_digit, numpy.int8) packed |= self.least_significant_digit packed <<= 8 # Byte 2: parameterless filters. if self.shuffle: packed |= _shuffle_flag if self.fletcher32: packed |= _fletcher32_flag if self.least_significant_digit: packed |= _rounding_flag packed <<= 8 # Byte 1: compression library id (0 for none). if self.complevel > 0: packed |= all_complibs.index(self.complib) + 1 packed <<= 8 # Byte 0: compression level. packed |= self.complevel return packed def __init__(self, complevel=0, complib=default_complib, shuffle=True, fletcher32=False, least_significant_digit=None, _new=True): if not (0 <= complevel <= 9): raise ValueError("compression level must be between 0 and 9") if _new and complevel > 0: # These checks are not performed when loading filters from disk. if complib not in all_complibs: raise ValueError( "compression library ``%s`` is not supported; " "it must be one of: %s" % (complib, ", ".join(all_complibs))) if utilsextension.which_lib_version(complib) is None: warnings.warn("compression library ``%s`` is not available; " "using ``%s`` instead" % (complib, default_complib), FiltersWarning) complib = default_complib # always available complevel = int(complevel) complib = str(complib) shuffle = bool(shuffle) fletcher32 = bool(fletcher32) if least_significant_digit is not None: least_significant_digit = numpy.int8(least_significant_digit) if complevel == 0: # Override some inputs when compression is not enabled. complib = None # make it clear there is no compression shuffle = False # shuffling and not compressing makes no sense least_significant_digit = None elif complib not in all_complibs: # Do not try to use a meaningful level for unsupported libs. complevel = -1 self.complevel = complevel """The compression level (0 disables compression).""" self.complib = complib """The compression filter used (irrelevant when compression is not enabled). """ self.shuffle = shuffle """Whether the *Shuffle* filter is active or not.""" self.fletcher32 = fletcher32 """Whether the *Fletcher32* filter is active or not.""" self.least_significant_digit = least_significant_digit """The least significant digit to which data shall be truncated.""" def __repr__(self): args, complevel = [], self.complevel if complevel >= 0: # meaningful compression level args.append('complevel=%d' % complevel) if complevel != 0: # compression enabled (-1 or > 0) args.append('complib=%r' % self.complib) args.append('shuffle=%s' % self.shuffle) args.append('fletcher32=%s' % self.fletcher32) args.append( 'least_significant_digit=%s' % self.least_significant_digit) return '%s(%s)' % (self.__class__.__name__, ', '.join(args)) def __str__(self): return repr(self) def __eq__(self, other): if not isinstance(other, self.__class__): return False for attr in self.__dict__: if getattr(self, attr) != getattr(other, attr): return False return True # XXX: API incompatible change for PyTables 3 line # Overriding __eq__ blocks inheritance of __hash__ in 3.x # def __hash__(self): # return hash((self.__class__, self.complevel, self.complib, # self.shuffle, self.fletcher32)) def copy(self, **override): """Get a copy of the filters, possibly overriding some arguments. Constructor arguments to be overridden must be passed as keyword arguments. Using this method is recommended over replacing the attributes of an instance, since instances of this class may become immutable in the future:: >>> filters1 = Filters() >>> filters2 = filters1.copy() >>> filters1 == filters2 True >>> filters1 is filters2 False >>> filters3 = filters1.copy(complevel=1) #doctest: +ELLIPSIS Traceback (most recent call last): ... ValueError: compression library ``None`` is not supported... >>> filters3 = filters1.copy(complevel=1, complib='zlib') >>> print(filters1) Filters(complevel=0, shuffle=False, fletcher32=False, least_significant_digit=None) >>> print(filters3) Filters(complevel=1, complib='zlib', shuffle=False, fletcher32=False, least_significant_digit=None) >>> filters1.copy(foobar=42) Traceback (most recent call last): ... TypeError: __init__() got an unexpected keyword argument 'foobar' """ newargs = self.__dict__.copy() newargs.update(override) return self.__class__(**newargs) # Main part # ========= def _test(): """Run ``doctest`` on this module.""" import doctest doctest.testmod() if __name__ == '__main__': _test() PyTables-v.3.1.1/tables/flavor.py000066400000000000000000000275771231437614300166460ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: December 30, 2006 # Author: Ivan Vilata i Balaguer - ivan at selidor dot net # # $Id$ # ######################################################################## """Utilities for handling different array flavors in PyTables. Variables ========= `__docformat`__ The format of documentation strings in this module. `internal_flavor` The flavor used internally by PyTables. `all_flavors` List of all flavors available to PyTables. `alias_map` Maps old flavor names to the most similar current flavor. `description_map` Maps flavors to short descriptions of their supported objects. `identifier_map` Maps flavors to functions that can identify their objects. The function associated with a given flavor will return a true value if the object passed to it can be identified as being of that flavor. See the `flavor_of()` function for a friendlier interface to flavor identification. `converter_map` Maps (source, destination) flavor pairs to converter functions. Converter functions get an array of the source flavor and return an array of the destination flavor. See the `array_of_flavor()` and `flavor_to_flavor()` functions for friendlier interfaces to flavor conversion. """ # Imports # ======= import warnings from tables.exceptions import FlavorError, FlavorWarning # Public variables # ================ __docformat__ = 'reStructuredText' """The format of documentation strings in this module.""" internal_flavor = 'numpy' """The flavor used internally by PyTables.""" # This is very slightly slower than a set for a small number of values # in terms of (infrequent) lookup time, but allows `flavor_of()` # (which may be called much more frequently) to check for flavors in # order, beginning with the most common one. all_flavors = [] # filled as flavors are registered """List of all flavors available to PyTables.""" alias_map = {} # filled as flavors are registered """Maps old flavor names to the most similar current flavor.""" description_map = {} # filled as flavors are registered """Maps flavors to short descriptions of their supported objects.""" identifier_map = {} # filled as flavors are registered """Maps flavors to functions that can identify their objects. The function associated with a given flavor will return a true value if the object passed to it can be identified as being of that flavor. See the `flavor_of()` function for a friendlier interface to flavor identification. """ converter_map = {} # filled as flavors are registered """Maps (source, destination) flavor pairs to converter functions. Converter functions get an array of the source flavor and return an array of the destination flavor. See the `array_of_flavor()` and `flavor_to_flavor()` functions for friendlier interfaces to flavor conversion. """ # Public functions # ================ def check_flavor(flavor): """Raise a ``FlavorError`` if the `flavor` is not valid.""" if flavor not in all_flavors: available_flavs = ", ".join(flav for flav in all_flavors) raise FlavorError( "flavor ``%s`` is unsupported or unavailable; " "available flavors in this system are: %s" % (flavor, available_flavs)) def array_of_flavor2(array, src_flavor, dst_flavor): """Get a version of the given `array` in a different flavor. The input `array` must be of the given `src_flavor`, and the returned array will be of the indicated `dst_flavor`. Both flavors may be the same, but it is not guaranteed that the returned array will be the same object as the input one in this case. If the conversion is not supported, a ``FlavorError`` is raised. """ convkey = (src_flavor, dst_flavor) if convkey not in converter_map: raise FlavorError("conversion from flavor ``%s`` to flavor ``%s`` " "is unsupported or unavailable in this system" % (src_flavor, dst_flavor)) convfunc = converter_map[convkey] return convfunc(array) def flavor_to_flavor(array, src_flavor, dst_flavor): """Get a version of the given `array` in a different flavor. The input `array` must be of the given `src_flavor`, and the returned array will be of the indicated `dst_flavor` (see below for an exception to this). Both flavors may be the same, but it is not guaranteed that the returned array will be the same object as the input one in this case. If the conversion is not supported, a `FlavorWarning` is issued and the input `array` is returned as is. """ try: return array_of_flavor2(array, src_flavor, dst_flavor) except FlavorError as fe: warnings.warn("%s; returning an object of the ``%s`` flavor instead" % (fe.args[0], src_flavor), FlavorWarning) return array def internal_to_flavor(array, dst_flavor): """Get a version of the given `array` in a different `dst_flavor`. The input `array` must be of the internal flavor, and the returned array will be of the given `dst_flavor`. See `flavor_to_flavor()` for more information. """ return flavor_to_flavor(array, internal_flavor, dst_flavor) def array_as_internal(array, src_flavor): """Get a version of the given `array` in the internal flavor. The input `array` must be of the given `src_flavor`, and the returned array will be of the internal flavor. If the conversion is not supported, a ``FlavorError`` is raised. """ return array_of_flavor2(array, src_flavor, internal_flavor) def flavor_of(array): """Identify the flavor of a given `array`. If the `array` can not be matched with any flavor, a ``TypeError`` is raised. """ for flavor in all_flavors: if identifier_map[flavor](array): return flavor type_name = type(array).__name__ supported_descs = "; ".join(description_map[fl] for fl in all_flavors) raise TypeError( "objects of type ``%s`` are not supported in this context, sorry; " "supported objects are: %s" % (type_name, supported_descs)) def array_of_flavor(array, dst_flavor): """Get a version of the given `array` in a different `dst_flavor`. The flavor of the input `array` is guessed, and the returned array will be of the given `dst_flavor`. If the conversion is not supported, a ``FlavorError`` is raised. """ return array_of_flavor2(array, flavor_of(array), dst_flavor) def restrict_flavors(keep=['python']): """Disable all flavors except those in keep. Providing an empty keep sequence implies disabling all flavors (but the internal one). If the sequence is not specified, only optional flavors are disabled. .. important:: Once you disable a flavor, it can not be enabled again. """ keep = set(keep).union([internal_flavor]) remove = set(all_flavors).difference(keep) for flavor in remove: _disable_flavor(flavor) # Flavor registration # =================== # # The order in which flavors appear in `all_flavors` determines the # order in which they will be tested for by `flavor_of()`, so place # most frequent flavors first. import numpy all_flavors.append('numpy') # this is the internal flavor all_flavors.append('python') # this is always supported def _register_aliases(): """Register aliases of *available* flavors.""" for flavor in all_flavors: aliases = eval('_%s_aliases' % flavor) for alias in aliases: alias_map[alias] = flavor def _register_descriptions(): """Register descriptions of *available* flavors.""" for flavor in all_flavors: description_map[flavor] = eval('_%s_desc' % flavor) def _register_identifiers(): """Register identifier functions of *available* flavors.""" for flavor in all_flavors: identifier_map[flavor] = eval('_is_%s' % flavor) def _register_converters(): """Register converter functions between *available* flavors.""" def identity(array): return array for src_flavor in all_flavors: for dst_flavor in all_flavors: # Converters with the same source and destination flavor # are used when available, since they may perform some # optimizations on the resulting array (e.g. making it # contiguous). Otherwise, an identity function is used. convfunc = None try: convfunc = eval('_conv_%s_to_%s' % (src_flavor, dst_flavor)) except NameError: if src_flavor == dst_flavor: convfunc = identity if convfunc: converter_map[(src_flavor, dst_flavor)] = convfunc def _register_all(): """Register all *available* flavors.""" _register_aliases() _register_descriptions() _register_identifiers() _register_converters() def _deregister_aliases(flavor): """Deregister aliases of a given `flavor` (no checks).""" rm_aliases = [] for (an_alias, a_flavor) in alias_map.iteritems(): if a_flavor == flavor: rm_aliases.append(an_alias) for an_alias in rm_aliases: del alias_map[an_alias] def _deregister_description(flavor): """Deregister description of a given `flavor` (no checks).""" del description_map[flavor] def _deregister_identifier(flavor): """Deregister identifier function of a given `flavor` (no checks).""" del identifier_map[flavor] def _deregister_converters(flavor): """Deregister converter functions of a given `flavor` (no checks).""" rm_flavor_pairs = [] for flavor_pair in converter_map: if flavor in flavor_pair: rm_flavor_pairs.append(flavor_pair) for flavor_pair in rm_flavor_pairs: del converter_map[flavor_pair] def _disable_flavor(flavor): """Completely disable the given `flavor` (no checks).""" _deregister_aliases(flavor) _deregister_description(flavor) _deregister_identifier(flavor) _deregister_converters(flavor) all_flavors.remove(flavor) # Implementation of flavors # ========================= _python_aliases = [ 'List', 'Tuple', 'Int', 'Float', 'String', 'VLString', 'Object', ] _python_desc = ("homogeneous list or tuple, " "integer, float, complex or bytes") def _is_python(array): return isinstance(array, (tuple, list, int, float, complex, bytes)) _numpy_aliases = [] _numpy_desc = "NumPy array, record or scalar" def _is_numpy(array): return isinstance(array, (numpy.ndarray, numpy.generic)) def _numpy_contiguous(convfunc): """Decorate `convfunc` to return a *contiguous* NumPy array. Note: When arrays are 0-strided, the copy is avoided. This allows to use `array` to still carry info about the dtype and shape. """ def conv_to_numpy(array): nparr = convfunc(array) if (hasattr(nparr, 'flags') and not nparr.flags.contiguous and sum(nparr.strides) != 0): nparr = nparr.copy() # copying the array makes it contiguous return nparr conv_to_numpy.__name__ = convfunc.__name__ conv_to_numpy.__doc__ = convfunc.__doc__ return conv_to_numpy @_numpy_contiguous def _conv_numpy_to_numpy(array): # Passes contiguous arrays through and converts scalars into # scalar arrays. return numpy.asarray(array) @_numpy_contiguous def _conv_python_to_numpy(array): return numpy.array(array) def _conv_numpy_to_python(array): if array.shape != (): # Lists are the default for returning multidimensional objects array = array.tolist() else: # 0-dim or scalar case array = array.item() return array # Now register everything related with *available* flavors. _register_all() # Main part # ========= def _test(): """Run ``doctest`` on this module.""" import doctest doctest.testmod() if __name__ == '__main__': _test() PyTables-v.3.1.1/tables/group.py000066400000000000000000001331701231437614300164740ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: September 4, 2002 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Here is defined the Group class.""" import warnings import weakref import tables.misc.proxydict from tables import hdf5extension from tables import utilsextension from tables.registry import class_id_dict from tables.exceptions import ( NodeError, NoSuchNodeError, NaturalNameWarning, PerformanceWarning) from tables.filters import Filters from tables.registry import get_class_by_name from tables.path import check_name_validity, join_path, isvisiblename from tables.node import Node, NotLoggedMixin from tables.leaf import Leaf from tables.unimplemented import UnImplemented, Unknown from tables.link import Link, SoftLink, ExternalLink from tables._past import previous_api, previous_api_property obversion = "1.0" class _ChildrenDict(tables.misc.proxydict.ProxyDict): def _get_value_from_container(self, container, key): return container._f_get_child(key) _getValueFromContainer = previous_api(_get_value_from_container) class Group(hdf5extension.Group, Node): """Basic PyTables grouping structure. Instances of this class are grouping structures containing *child* instances of zero or more groups or leaves, together with supporting metadata. Each group has exactly one *parent* group. Working with groups and leaves is similar in many ways to working with directories and files, respectively, in a Unix filesystem. As with Unix directories and files, objects in the object tree are often described by giving their full (or absolute) path names. This full path can be specified either as a string (like in '/group1/group2') or as a complete object path written in *natural naming* schema (like in file.root.group1.group2). A collateral effect of the *natural naming* schema is that the names of members in the Group class and its instances must be carefully chosen to avoid colliding with existing children node names. For this reason and to avoid polluting the children namespace all members in a Group start with some reserved prefix, like _f_ (for public methods), _g_ (for private ones), _v_ (for instance variables) or _c_ (for class variables). Any attempt to create a new child node whose name starts with one of these prefixes will raise a ValueError exception. Another effect of natural naming is that children named after Python keywords or having names not valid as Python identifiers (e.g. class, $a or 44) can not be accessed using the node.child syntax. You will be forced to use node._f_get_child(child) to access them (which is recommended for programmatic accesses). You will also need to use _f_get_child() to access an existing child node if you set a Python attribute in the Group with the same name as that node (you will get a NaturalNameWarning when doing this). Parameters ---------- parentnode The parent :class:`Group` object. .. versionchanged:: 3.0 Renamed from *parentNode* to *parentnode* name : str The name of this node in its parent group. title The title for this group new If this group is new or has to be read from disk filters : Filters A Filters instance Notes ----- The following documentation includes methods that are automatically called when a Group instance is accessed in a special way. For instance, this class defines the __setattr__, __getattr__, and __delattr__ methods, and they set, get and delete *ordinary Python attributes* as normally intended. In addition to that, __getattr__ allows getting *child nodes* by their name for the sake of easy interaction on the command line, as long as there is no Python attribute with the same name. Groups also allow the interactive completion (when using readline) of the names of child nodes. For instance:: # get a Python attribute nchild = group._v_nchildren # Add a Table child called 'table' under 'group'. h5file.create_table(group, 'table', myDescription) table = group.table # get the table child instance group.table = 'foo' # set a Python attribute # (PyTables warns you here about using the name of a child node.) foo = group.table # get a Python attribute del group.table # delete a Python attribute table = group.table # get the table child instance again .. rubric:: Group attributes The following instance variables are provided in addition to those in Node (see :ref:`NodeClassDescr`): .. attribute:: _v_children Dictionary with all nodes hanging from this group. .. attribute:: _v_groups Dictionary with all groups hanging from this group. .. attribute:: _v_hidden Dictionary with all hidden nodes hanging from this group. .. attribute:: _v_leaves Dictionary with all leaves hanging from this group. .. attribute:: _v_links Dictionary with all links hanging from this group. .. attribute:: _v_unknown Dictionary with all unknown nodes hanging from this group. """ # Class identifier. _c_classid = 'GROUP' _c_classId = previous_api_property('_c_classid') # Children containers that should be loaded only in a lazy way. # These are documented in the ``Group._g_add_children_names`` method. _c_lazy_children_attrs = ( '__members__', '_v_children', '_v_groups', '_v_leaves', '_v_links', '_v_unknown', '_v_hidden') # # `_v_nchildren` is a direct read-only shorthand # for the number of *visible* children in a group. def _g_getnchildren(self): return len(self._v_children) _v_nchildren = property(_g_getnchildren, None, None, "The number of children hanging from this group.") # `_v_filters` is a direct read-write shorthand for the ``FILTERS`` # attribute with the default `Filters` instance as a default value. def _g_getfilters(self): filters = getattr(self._v_attrs, 'FILTERS', None) if filters is None: filters = Filters() return filters def _g_setfilters(self, value): if not isinstance(value, Filters): raise TypeError( "value is not an instance of `Filters`: %r" % (value,)) self._v_attrs.FILTERS = value def _g_delfilters(self): del self._v_attrs.FILTERS _v_filters = property( _g_getfilters, _g_setfilters, _g_delfilters, """Default filter properties for child nodes. You can (and are encouraged to) use this property to get, set and delete the FILTERS HDF5 attribute of the group, which stores a Filters instance (see :ref:`FiltersClassDescr`). When the group has no such attribute, a default Filters instance is used. """) # _v_maxGroupWidth = previous_api_property('_v_max_group_width') def __init__(self, parentnode, name, title="", new=False, filters=None, _log=True): # Remember to assign these values in the root group constructor # if it does not use this one! # First, set attributes belonging to group objects. self._v_version = obversion """The object version of this group.""" self._v_new = new """Is this the first time the node has been created?""" self._v_new_title = title """New title for this node.""" self._v_new_filters = filters """New default filter properties for child nodes.""" self._v_max_group_width = parentnode._v_file.params['MAX_GROUP_WIDTH'] """Maximum number of children on each group before warning the user. .. versionchanged:: 3.0 The *_v_maxGroupWidth* attribute has been renamed into *_v_max_group_width*. """ # Finally, set up this object as a node. super(Group, self).__init__(parentnode, name, _log) def _g_post_init_hook(self): if self._v_new: if self._v_file.params['PYTABLES_SYS_ATTRS']: # Save some attributes for the new group on disk. set_attr = self._v_attrs._g__setattr # Set the title, class and version attributes. set_attr('TITLE', self._v_new_title) set_attr('CLASS', self._c_classid) set_attr('VERSION', self._v_version) # Set the default filter properties. newfilters = self._v_new_filters if newfilters is None: # If no filters have been passed in the constructor, # inherit them from the parent group, but only if they # have been inherited or explicitly set. newfilters = getattr( self._v_parent._v_attrs, 'FILTERS', None) if newfilters is not None: set_attr('FILTERS', newfilters) else: # If the file has PyTables format, get the VERSION attr if 'VERSION' in self._v_attrs._v_attrnamessys: self._v_version = self._v_attrs.VERSION else: self._v_version = "0.0 (unknown)" # We don't need to get more attributes from disk, # since the most important ones are defined as properties. _g_postInitHook = previous_api(_g_post_init_hook) def __del__(self): if (self._v_isopen and self._v_pathname in self._v_file._node_manager.registry and '_v_children' in self.__dict__): # The group is going to be killed. Rebuild weak references # (that Python cancelled just before calling this method) so # that they are still usable if the object is revived later. selfref = weakref.ref(self) self._v_children.containerref = selfref self._v_groups.containerref = selfref self._v_leaves.containerref = selfref self._v_links.containerref = selfref self._v_unknown.containerref = selfref self._v_hidden.containerref = selfref super(Group, self).__del__() def _g_get_child_group_class(self, childname): """Get the class of a not-yet-loaded group child. `childname` must be the name of a *group* child. """ childCID = self._g_get_gchild_attr(childname, 'CLASS') if childCID is not None and not isinstance(childCID, str): childCID = childCID.decode('utf-8') if childCID in class_id_dict: return class_id_dict[childCID] # look up group class else: return Group # default group class _g_getChildGroupClass = previous_api(_g_get_child_group_class) def _g_get_child_leaf_class(self, childname, warn=True): """Get the class of a not-yet-loaded leaf child. `childname` must be the name of a *leaf* child. If the child belongs to an unknown kind of leaf, or if its kind can not be guessed, `UnImplemented` will be returned and a warning will be issued if `warn` is true. """ if self._v_file.params['PYTABLES_SYS_ATTRS']: childCID = self._g_get_lchild_attr(childname, 'CLASS') if childCID is not None and not isinstance(childCID, str): childCID = childCID.decode('utf-8') else: childCID = None if childCID in class_id_dict: return class_id_dict[childCID] # look up leaf class else: # Unknown or no ``CLASS`` attribute, try a guess. childCID2 = utilsextension.which_class(self._v_objectid, childname) if childCID2 == 'UNSUPPORTED': if warn: if childCID is None: warnings.warn( "leaf ``%s`` is of an unsupported type; " "it will become an ``UnImplemented`` node" % self._g_join(childname)) else: warnings.warn( ("leaf ``%s`` has an unknown class ID ``%s``; " "it will become an ``UnImplemented`` node") % (self._g_join(childname), childCID)) return UnImplemented assert childCID2 in class_id_dict return class_id_dict[childCID2] # look up leaf class _g_getChildLeafClass = previous_api(_g_get_child_leaf_class) def _g_add_children_names(self): """Add children names to this group taking into account their visibility and kind.""" mydict = self.__dict__ # The names of the lazy attributes mydict['__members__'] = members = [] """The names of visible children nodes for readline-style completion. """ mydict['_v_children'] = children = _ChildrenDict(self) """The number of children hanging from this group.""" mydict['_v_groups'] = groups = _ChildrenDict(self) """Dictionary with all groups hanging from this group.""" mydict['_v_leaves'] = leaves = _ChildrenDict(self) """Dictionary with all leaves hanging from this group.""" mydict['_v_links'] = links = _ChildrenDict(self) """Dictionary with all links hanging from this group.""" mydict['_v_unknown'] = unknown = _ChildrenDict(self) """Dictionary with all unknown nodes hanging from this group.""" mydict['_v_hidden'] = hidden = _ChildrenDict(self) """Dictionary with all hidden nodes hanging from this group.""" # Get the names of *all* child groups and leaves. (group_names, leaf_names, link_names, unknown_names) = \ self._g_list_group(self._v_parent) # Separate groups into visible groups and hidden nodes, # and leaves into visible leaves and hidden nodes. for (childnames, childdict) in ((group_names, groups), (leaf_names, leaves), (link_names, links), (unknown_names, unknown)): for childname in childnames: # See whether the name implies that the node is hidden. # (Assigned values are entirely irrelevant.) if isvisiblename(childname): # Visible node. members.insert(0, childname) children[childname] = None childdict[childname] = None else: # Hidden node. hidden[childname] = None _g_addChildrenNames = previous_api(_g_add_children_names) def _g_check_has_child(self, name): """Check whether 'name' is a children of 'self' and return its type.""" # Get the HDF5 name matching the PyTables name. node_type = self._g_get_objinfo(name) if node_type == "NoSuchNode": raise NoSuchNodeError( "group ``%s`` does not have a child named ``%s``" % (self._v_pathname, name)) return node_type _g_checkHasChild = previous_api(_g_check_has_child) def __iter__(self): """Iterate over the child nodes hanging directly from the group. This iterator is *not* recursive. Examples -------- :: # Non-recursively list all the nodes hanging from '/detector' print("Nodes in '/detector' group:") for node in h5file.root.detector: print(node) """ return self._f_iter_nodes() def __contains__(self, name): """Is there a child with that `name`? Returns a true value if the group has a child node (visible or hidden) with the given `name` (a string), false otherwise. """ self._g_check_open() try: self._g_check_has_child(name) except NoSuchNodeError: return False return True def _f_walknodes(self, classname=None): """Iterate over descendant nodes. This method recursively walks *self* top to bottom (preorder), iterating over child groups in alphanumerical order, and yielding nodes. If classname is supplied, only instances of the named class are yielded. If *classname* is Group, it behaves like :meth:`Group._f_walk_groups`, yielding only groups. If you don't want a recursive behavior, use :meth:`Group._f_iter_nodes` instead. Examples -------- :: # Recursively print all the arrays hanging from '/' print("Arrays in the object tree '/':") for array in h5file.root._f_walknodes('Array', recursive=True): print(array) """ self._g_check_open() # For compatibility with old default arguments. if classname == '': classname = None if classname == "Group": # Recursive algorithm for group in self._f_walk_groups(): yield group else: for group in self._f_walk_groups(): for leaf in group._f_iter_nodes(classname): yield leaf _f_walkNodes = previous_api(_f_walknodes) def _g_join(self, name): """Helper method to correctly concatenate a name child object with the pathname of this group.""" if name == "/": # This case can happen when doing copies return self._v_pathname return join_path(self._v_pathname, name) def _g_width_warning(self): """Issue a :exc:`PerformanceWarning` on too many children.""" warnings.warn("""\ group ``%s`` is exceeding the recommended maximum number of children (%d); \ be ready to see PyTables asking for *lots* of memory and possibly slow I/O.""" % (self._v_pathname, self._v_max_group_width), PerformanceWarning) _g_widthWarning = previous_api(_g_width_warning) def _g_refnode(self, childnode, childname, validate=True): """Insert references to a `childnode` via a `childname`. Checks that the `childname` is valid and does not exist, then creates references to the given `childnode` by that `childname`. The validation of the name can be omitted by setting `validate` to a false value (this may be useful for adding already existing nodes to the tree). """ # Check for name validity. if validate: check_name_validity(childname) childnode._g_check_name(childname) # Check if there is already a child with the same name. # This can be triggered because of the user # (via node construction or renaming/movement). # Links are not checked here because they are copied and referenced # using ``File.get_node`` so they already exist in `self`. if (not isinstance(childnode, Link)) and childname in self: raise NodeError( "group ``%s`` already has a child node named ``%s``" % (self._v_pathname, childname)) # Show a warning if there is an object attribute with that name. if childname in self.__dict__: warnings.warn( "group ``%s`` already has an attribute named ``%s``; " "you will not be able to use natural naming " "to access the child node" % (self._v_pathname, childname), NaturalNameWarning) # Check group width limits. if (len(self._v_children) + len(self._v_hidden) >= self._v_max_group_width): self._g_width_warning() # Update members information. # Insert references to the new child. # (Assigned values are entirely irrelevant.) if isvisiblename(childname): # Visible node. self.__members__.insert(0, childname) # enable completion self._v_children[childname] = None # insert node if isinstance(childnode, Unknown): self._v_unknown[childname] = None elif isinstance(childnode, Link): self._v_links[childname] = None elif isinstance(childnode, Leaf): self._v_leaves[childname] = None elif isinstance(childnode, Group): self._v_groups[childname] = None else: # Hidden node. self._v_hidden[childname] = None # insert node _g_refNode = previous_api(_g_refnode) def _g_unrefnode(self, childname): """Remove references to a node. Removes all references to the named node. """ # This can *not* be triggered because of the user. assert childname in self, \ ("group ``%s`` does not have a child node named ``%s``" % (self._v_pathname, childname)) # Update members information, if needed if '_v_children' in self.__dict__: if childname in self._v_children: # Visible node. members = self.__members__ member_index = members.index(childname) del members[member_index] # disables completion del self._v_children[childname] # remove node self._v_unknown.pop(childname, None) self._v_links.pop(childname, None) self._v_leaves.pop(childname, None) self._v_groups.pop(childname, None) else: # Hidden node. del self._v_hidden[childname] # remove node _g_unrefNode = previous_api(_g_unrefnode) def _g_move(self, newparent, newname): # Move the node to the new location. oldpath = self._v_pathname super(Group, self)._g_move(newparent, newname) newpath = self._v_pathname # Update location information in children. This node shouldn't # be affected since it has already been relocated. self._v_file._update_node_locations(oldpath, newpath) def _g_copy(self, newparent, newname, recursive, _log=True, **kwargs): # Compute default arguments. title = kwargs.get('title', self._v_title) filters = kwargs.get('filters', None) stats = kwargs.get('stats', None) # Fix arguments with explicit None values for backwards compatibility. if title is None: title = self._v_title # If no filters have been passed to the call, copy them from the # source group, but only if inherited or explicitly set. if filters is None: filters = getattr(self._v_attrs, 'FILTERS', None) # Create a copy of the object. new_node = Group(newparent, newname, title, new=True, filters=filters, _log=_log) # Copy user attributes if needed. if kwargs.get('copyuserattrs', True): self._v_attrs._g_copy(new_node._v_attrs, copyclass=True) # Update statistics if needed. if stats is not None: stats['groups'] += 1 if recursive: # Copy child nodes if a recursive copy was requested. # Some arguments should *not* be passed to children copy ops. kwargs = kwargs.copy() kwargs.pop('title', None) self._g_copy_children(new_node, **kwargs) return new_node def _g_copy_children(self, newparent, **kwargs): """Copy child nodes. Copies all nodes descending from this one into the specified `newparent`. If the new parent has a child node with the same name as one of the nodes in this group, the copy fails with a `NodeError`, maybe resulting in a partial copy. Nothing is logged. """ # Recursive version of children copy. # for srcchild in self._v_children.itervalues(): ## srcchild._g_copy_as_child(newparent, **kwargs) # Non-recursive version of children copy. parentstack = [(self, newparent)] # [(source, destination), ...] while parentstack: (srcparent, dstparent) = parentstack.pop() for srcchild in srcparent._v_children.itervalues(): dstchild = srcchild._g_copy_as_child(dstparent, **kwargs) if isinstance(srcchild, Group): parentstack.append((srcchild, dstchild)) _g_copyChildren = previous_api(_g_copy_children) def _f_get_child(self, childname): """Get the child called childname of this group. If the child exists (be it visible or not), it is returned. Else, a NoSuchNodeError is raised. Using this method is recommended over getattr() when doing programmatic accesses to children if childname is unknown beforehand or when its name is not a valid Python identifier. """ self._g_check_open() self._g_check_has_child(childname) childpath = join_path(self._v_pathname, childname) return self._v_file._get_node(childpath) _f_getChild = previous_api(_f_get_child) def _f_list_nodes(self, classname=None): """Return a *list* with children nodes. This is a list-returning version of :meth:`Group._f_iter_nodes()`. """ return list(self._f_iter_nodes(classname)) _f_listNodes = previous_api(_f_list_nodes) def _f_iter_nodes(self, classname=None): """Iterate over children nodes. Child nodes are yielded alphanumerically sorted by node name. If the name of a class derived from Node (see :ref:`NodeClassDescr`) is supplied in the classname parameter, only instances of that class (or subclasses of it) will be returned. This is an iterator version of :meth:`Group._f_list_nodes`. """ self._g_check_open() if not classname: # Returns all the children alphanumerically sorted names = sorted(self._v_children.iterkeys()) for name in names: yield self._v_children[name] elif classname == 'Group': # Returns all the groups alphanumerically sorted names = sorted(self._v_groups.iterkeys()) for name in names: yield self._v_groups[name] elif classname == 'Leaf': # Returns all the leaves alphanumerically sorted names = sorted(self._v_leaves.iterkeys()) for name in names: yield self._v_leaves[name] elif classname == 'Link': # Returns all the links alphanumerically sorted names = sorted(self._v_links.iterkeys()) for name in names: yield self._v_links[name] elif classname == 'IndexArray': raise TypeError( "listing ``IndexArray`` nodes is not allowed") else: class_ = get_class_by_name(classname) children = self._v_children childnames = sorted(children.iterkeys()) for childname in childnames: childnode = children[childname] if isinstance(childnode, class_): yield childnode _f_iterNodes = previous_api(_f_iter_nodes) def _f_walk_groups(self): """Recursively iterate over descendent groups (not leaves). This method starts by yielding *self*, and then it goes on to recursively iterate over all child groups in alphanumerical order, top to bottom (preorder), following the same procedure. """ self._g_check_open() stack = [self] yield self # Iterate over the descendants while stack: objgroup = stack.pop() groupnames = sorted(objgroup._v_groups.iterkeys()) # Sort the groups before delivering. This uses the groups names # for groups in tree (in order to sort() can classify them). for groupname in groupnames: stack.append(objgroup._v_groups[groupname]) yield objgroup._v_groups[groupname] _f_walkGroups = previous_api(_f_walk_groups) def __delattr__(self, name): """Delete a Python attribute called name. This method deletes an *ordinary Python attribute* from the object. It does *not* remove children nodes from this group; for that, use :meth:`File.remove_node` or :meth:`Node._f_remove`. It does *neither* delete a PyTables node attribute; for that, use :meth:`File.del_node_attr`, :meth:`Node._f_delattr` or :attr:`Node._v_attrs``. If there is an attribute and a child node with the same name, the child node will be made accessible again via natural naming. """ try: super(Group, self).__delattr__(name) # nothing particular except AttributeError as ae: hint = " (use ``node._f_remove()`` if you want to remove a node)" raise ae.__class__(str(ae) + hint) def __getattr__(self, name): """Get a Python attribute or child node called name. If the object has a Python attribute called name, its value is returned. Else, if the node has a child node called name, it is returned. Else, an AttributeError is raised. """ # That is true since a `NoSuchNodeError` is an `AttributeError`. mydict = self.__dict__ if name in mydict: return mydict[name] elif name in self._c_lazy_children_attrs: self._g_add_children_names() return mydict[name] return self._f_get_child(name) def __setattr__(self, name, value): """Set a Python attribute called name with the given value. This method stores an *ordinary Python attribute* in the object. It does *not* store new children nodes under this group; for that, use the File.create*() methods (see the File class in :ref:`FileClassDescr`). It does *neither* store a PyTables node attribute; for that, use :meth:`File.set_node_attr`, :meth`:Node._f_setattr` or :attr:`Node._v_attrs`. If there is already a child node with the same name, a NaturalNameWarning will be issued and the child node will not be accessible via natural naming nor getattr(). It will still be available via :meth:`File.get_node`, :meth:`Group._f_get_child` and children dictionaries in the group (if visible). """ # Show a warning if there is an child node with that name. # # ..note:: # # Using ``if name in self:`` is not right since that would # require ``_v_children`` and ``_v_hidden`` to be already set # when the very first attribute assignments are made. # Moreover, this warning is only concerned about clashes with # names used in natural naming, i.e. those in ``__members__``. # # ..note:: # # The check ``'__members__' in myDict`` allows attribute # assignment to happen before calling `Group.__init__()`, by # avoiding to look into the still not assigned ``__members__`` # attribute. This allows subclasses to set up some attributes # and then call the constructor of the superclass. If the # check above is disabled, that results in Python entering an # endless loop on exit! mydict = self.__dict__ if '__members__' in mydict and name in self.__members__: warnings.warn( "group ``%s`` already has a child node named ``%s``; " "you will not be able to use natural naming " "to access the child node" % (self._v_pathname, name), NaturalNameWarning) super(Group, self).__setattr__(name, value) def _f_flush(self): """Flush this Group.""" self._g_check_open() self._g_flush_group() def _g_close_descendents(self): """Close all the *loaded* descendent nodes of this group.""" node_manager = self._v_file._node_manager node_manager.close_subtree(self._v_pathname) _g_closeDescendents = previous_api(_g_close_descendents) def _g_close(self): """Close this (open) group.""" if self._v_isopen: # hdf5extension operations: # Close HDF5 group. self._g_close_group() # Close myself as a node. super(Group, self)._f_close() def _f_close(self): """Close this group and all its descendents. This method has the behavior described in :meth:`Node._f_close`. It should be noted that this operation closes all the nodes descending from this group. You should not need to close nodes manually because they are automatically opened/closed when they are loaded/evicted from the integrated LRU cache. """ # If the group is already closed, return immediately if not self._v_isopen: return # First, close all the descendents of this group, unless a) the # group is being deleted (evicted from LRU cache) or b) the node # is being closed during an aborted creation, in which cases # this is not an explicit close issued by the user. if not (self._v__deleting or self._v_objectid is None): self._g_close_descendents() # When all the descendents have been closed, close this group. # This is done at the end because some nodes may still need to # be loaded during the closing process; thus this node must be # open until the very end. self._g_close() def _g_remove(self, recursive=False, force=False): """Remove (recursively if needed) the Group. This version correctly handles both visible and hidden nodes. """ if self._v_nchildren > 0: if not (recursive or force): raise NodeError("group ``%s`` has child nodes; " "please set `recursive` or `force` to true " "to remove it" % (self._v_pathname,)) # First close all the descendents hanging from this group, # so that it is not possible to use a node that no longer exists. self._g_close_descendents() # Remove the node itself from the hierarchy. super(Group, self)._g_remove(recursive, force) def _f_copy(self, newparent=None, newname=None, overwrite=False, recursive=False, createparents=False, **kwargs): """Copy this node and return the new one. This method has the behavior described in :meth:`Node._f_copy`. In addition, it recognizes the following keyword arguments: Parameters ---------- title The new title for the destination. If omitted or None, the original title is used. This only applies to the topmost node in recursive copies. filters : Filters Specifying this parameter overrides the original filter properties in the source node. If specified, it must be an instance of the Filters class (see :ref:`FiltersClassDescr`). The default is to copy the filter properties from the source node. copyuserattrs You can prevent the user attributes from being copied by setting thisparameter to False. The default is to copy them. stats This argument may be used to collect statistics on the copy process. When used, it should be a dictionary with keys 'groups', 'leaves', 'links' and 'bytes' having a numeric value. Their values willbe incremented to reflect the number of groups, leaves and bytes, respectively, that have been copied during the operation. """ return super(Group, self)._f_copy( newparent, newname, overwrite, recursive, createparents, **kwargs) def _f_copy_children(self, dstgroup, overwrite=False, recursive=False, createparents=False, **kwargs): """Copy the children of this group into another group. Children hanging directly from this group are copied into dstgroup, which can be a Group (see :ref:`GroupClassDescr`) object or its pathname in string form. If createparents is true, the needed groups for the given destination group path to exist will be created. The operation will fail with a NodeError if there is a child node in the destination group with the same name as one of the copied children from this one, unless overwrite is true; in this case, the former child node is recursively removed before copying the later. By default, nodes descending from children groups of this node are not copied. If the recursive argument is true, all descendant nodes of this node are recursively copied. Additional keyword arguments may be passed to customize the copying process. For instance, title and filters may be changed, user attributes may be or may not be copied, data may be sub-sampled, stats may be collected, etc. Arguments unknown to nodes are simply ignored. Check the documentation for copying operations of nodes to see which options they support. """ self._g_check_open() # `dstgroup` is used instead of its path to avoid accepting # `Node` objects when `createparents` is true. Also, note that # there is no risk of creating parent nodes and failing later # because of destination nodes already existing. dstparent = self._v_file._get_or_create_path(dstgroup, createparents) self._g_check_group(dstparent) # Is it a group? if not overwrite: # Abort as early as possible when destination nodes exist # and overwriting is not enabled. for childname in self._v_children: if childname in dstparent: raise NodeError( "destination group ``%s`` already has " "a node named ``%s``; " "you may want to use the ``overwrite`` argument" % (dstparent._v_pathname, childname)) for child in self._v_children.itervalues(): child._f_copy(dstparent, None, overwrite, recursive, **kwargs) _f_copyChildren = previous_api(_f_copy_children) def __str__(self): """Return a short string representation of the group. Examples -------- :: >>> f=tables.open_file('data/test.h5') >>> print(f.root.group0) /group0 (Group) 'First Group' """ pathname = self._v_pathname classname = self.__class__.__name__ title = self._v_title return "%s (%s) %r" % (pathname, classname, title) def __repr__(self): """Return a detailed string representation of the group. Examples -------- :: >>> f = tables.open_file('data/test.h5') >>> f.root.group0 /group0 (Group) 'First Group' children := ['tuple1' (Table), 'group1' (Group)] """ rep = [ '%r (%s)' % (childname, child.__class__.__name__) for (childname, child) in self._v_children.iteritems() ] childlist = '[%s]' % (', '.join(rep)) return "%s\n children := %s" % (str(self), childlist) # Special definition for group root class RootGroup(Group): _v_objectId = previous_api_property('_v_objectid') def __init__(self, ptfile, name, title, new, filters): mydict = self.__dict__ # Set group attributes. self._v_version = obversion self._v_new = new if new: self._v_new_title = title self._v_new_filters = filters else: self._v_new_title = None self._v_new_filters = None # Set node attributes. self._v_file = ptfile self._v_isopen = True # root is always open self._v_pathname = '/' self._v_name = '/' self._v_depth = 0 self._v_max_group_width = ptfile.params['MAX_GROUP_WIDTH'] self._v__deleting = False self._v_objectid = None # later # Only the root node has the file as a parent. # Bypass __setattr__ to avoid the ``Node._v_parent`` property. mydict['_v_parent'] = ptfile ptfile._node_manager.register_node(self, '/') # hdf5extension operations (do before setting an AttributeSet): # Update node attributes. self._g_new(ptfile, name, init=True) # Open the node and get its object ID. self._v_objectid = self._g_open() # Set disk attributes and read children names. # # This *must* be postponed because this method needs the root node # to be created and bound to ``File.root``. # This is an exception to the rule, handled by ``File.__init()__``. # # self._g_post_init_hook() def _g_load_child(self, childname): """Load a child node from disk. The child node `childname` is loaded from disk and an adequate `Node` object is created and returned. If there is no such child, a `NoSuchNodeError` is raised. """ if self._v_file.root_uep != "/": childname = join_path(self._v_file.root_uep, childname) # Is the node a group or a leaf? node_type = self._g_check_has_child(childname) # Nodes that HDF5 report as H5G_UNKNOWN if node_type == 'Unknown': return Unknown(self, childname) # Guess the PyTables class suited to the node, # build a PyTables node and return it. if node_type == "Group": if self._v_file.params['PYTABLES_SYS_ATTRS']: ChildClass = self._g_get_child_group_class(childname) else: # Default is a Group class ChildClass = Group return ChildClass(self, childname, new=False) elif node_type == "Leaf": ChildClass = self._g_get_child_leaf_class(childname, warn=True) # Building a leaf may still fail because of unsupported types # and other causes. # return ChildClass(self, childname) # uncomment for debugging try: return ChildClass(self, childname) except Exception as exc: # XXX warnings.warn( "problems loading leaf ``%s``::\n\n" " %s\n\n" "The leaf will become an ``UnImplemented`` node." % (self._g_join(childname), exc)) # If not, associate an UnImplemented object to it return UnImplemented(self, childname) elif node_type == "SoftLink": return SoftLink(self, childname) elif node_type == "ExternalLink": return ExternalLink(self, childname) else: return UnImplemented(self, childname) _g_loadChild = previous_api(_g_load_child) def _f_rename(self, newname): raise NodeError("the root node can not be renamed") def _f_move(self, newparent=None, newname=None, createparents=False): raise NodeError("the root node can not be moved") def _f_remove(self, recursive=False): raise NodeError("the root node can not be removed") class TransactionGroupG(NotLoggedMixin, Group): _c_classid = 'TRANSGROUP' _c_classId = previous_api_property('_c_classid') def _g_width_warning(self): warnings.warn("""\ the number of transactions is exceeding the recommended maximum (%d);\ be ready to see PyTables asking for *lots* of memory and possibly slow I/O""" % (self._v_max_group_width,), PerformanceWarning) _g_widthWarning = previous_api(_g_width_warning) class TransactionG(NotLoggedMixin, Group): _c_classid = 'TRANSG' _c_classId = previous_api_property('_c_classid') def _g_width_warning(self): warnings.warn("""\ transaction ``%s`` is exceeding the recommended maximum number of marks (%d);\ be ready to see PyTables asking for *lots* of memory and possibly slow I/O""" % (self._v_pathname, self._v_max_group_width), PerformanceWarning) _g_widthWarning = previous_api(_g_width_warning) class MarkG(NotLoggedMixin, Group): # Class identifier. _c_classid = 'MARKG' _c_classId = previous_api_property('_c_classid') import re _c_shadow_name_re = re.compile(r'^a[0-9]+$') def _g_width_warning(self): warnings.warn("""\ mark ``%s`` is exceeding the recommended maximum action storage (%d nodes);\ be ready to see PyTables asking for *lots* of memory and possibly slow I/O""" % (self._v_pathname, self._v_max_group_width), PerformanceWarning) _g_widthWarning = previous_api(_g_width_warning) def _g_reset(self): """Empty action storage (nodes and attributes). This method empties all action storage kept in this node: nodes and attributes. """ # Remove action storage nodes. for child in self._v_children.values(): child._g_remove(True, True) # Remove action storage attributes. attrs = self._v_attrs shname = self._c_shadow_name_re for attrname in attrs._v_attrnamesuser[:]: if shname.match(attrname): attrs._g__delattr(attrname) ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/hdf5Extension.py000066400000000000000000000004111231437614300200520ustar00rootroot00000000000000from warnings import warn from tables.hdf5extension import * _warnmsg = ("hdf5Extension is pending deprecation, import hdf5extension instead. " "You may use the pt2to3 tool to update your source code.") warn(_warnmsg, DeprecationWarning, stacklevel=2) PyTables-v.3.1.1/tables/hdf5extension.pxd000066400000000000000000000016451231437614300202670ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## from numpy cimport ndarray from definitions cimport hid_t, hsize_t # Declaration of instance variables for shared classes cdef class Node: cdef object name cdef hid_t parent_id cdef class Leaf(Node): cdef hid_t dataset_id cdef hid_t type_id cdef hid_t base_type_id cdef hid_t disk_type_id cdef hsize_t *dims # Necessary to be here because of Leaf._g_truncate() cdef _get_type_ids(self) cdef _convert_time64(self, ndarray nparr, int sense) cdef class Array(Leaf): cdef int rank cdef hsize_t *maxdims cdef hsize_t *dims_chunk ## Local Variables: ## mode: python ## py-indent-offset: 2 ## tab-width: 2 ## fill-column: 78 ## End: PyTables-v.3.1.1/tables/hdf5extension.pyx000066400000000000000000002067051231437614300203200ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: September 21, 2002 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Cython interface between several PyTables classes and HDF5 library. Classes (type extensions): File AttributeSet Node Leaf Group Array VLArray UnImplemented Functions: Misc variables: """ import os import warnings from cpython cimport PY_MAJOR_VERSION if PY_MAJOR_VERSION < 3: import cPickle as pickle else: import pickle import numpy from tables.exceptions import HDF5ExtError, DataTypeWarning from tables.utils import (check_file_access, byteorders, correct_byteorder, SizeType) from tables.atom import Atom from tables.description import descr_from_dtype from tables.utilsextension import (encode_filename, set_blosc_max_threads, atom_to_hdf5_type, atom_from_hdf5_type, hdf5_to_np_ext_type, create_nested_type, pttype_to_hdf5, pt_special_kinds, npext_prefixes_to_ptkinds, hdf5_class_to_string, platform_byteorder) from tables._past import previous_api # Types, constants, functions, classes & other objects from everywhere from libc.stdlib cimport malloc, free from libc.string cimport strdup, strlen from numpy cimport import_array, ndarray, npy_intp from cpython cimport (PyBytes_AsString, PyBytes_FromStringAndSize, PyBytes_Check) from cpython.unicode cimport PyUnicode_DecodeUTF8 from definitions cimport (const_char, uintptr_t, hid_t, herr_t, hsize_t, hvl_t, H5S_seloper_t, H5D_FILL_VALUE_UNDEFINED, H5O_TYPE_UNKNOWN, H5O_TYPE_GROUP, H5O_TYPE_DATASET, H5O_TYPE_NAMED_DATATYPE, H5L_TYPE_ERROR, H5L_TYPE_HARD, H5L_TYPE_SOFT, H5L_TYPE_EXTERNAL, H5T_class_t, H5T_sign_t, H5T_NATIVE_INT, H5T_cset_t, H5T_CSET_ASCII, H5T_CSET_UTF8, H5F_SCOPE_GLOBAL, H5F_ACC_TRUNC, H5F_ACC_RDONLY, H5F_ACC_RDWR, H5P_DEFAULT, H5P_FILE_ACCESS, H5P_FILE_CREATE, H5S_SELECT_SET, H5S_SELECT_AND, H5S_SELECT_NOTB, H5Fcreate, H5Fopen, H5Fclose, H5Fflush, H5Fget_vfd_handle, H5Fget_filesize, H5Fget_create_plist, H5Gcreate, H5Gopen, H5Gclose, H5Ldelete, H5Lmove, H5Dopen, H5Dclose, H5Dread, H5Dwrite, H5Dget_type, H5Dget_space, H5Dvlen_reclaim, H5Dget_storage_size, H5Dvlen_get_buf_size, H5Tclose, H5Tis_variable_str, H5Tget_sign, H5Adelete, H5T_BITFIELD, H5T_INTEGER, H5T_FLOAT, H5T_STRING, H5Tget_order, H5Pcreate, H5Pset_cache, H5Pclose, H5Pget_userblock, H5Pset_userblock, H5Pset_fapl_sec2, H5Pset_fapl_log, H5Pset_fapl_stdio, H5Pset_fapl_core, H5Pset_fapl_split, H5Sselect_all, H5Sselect_elements, H5Sselect_hyperslab, H5Screate_simple, H5Sclose, H5ATTRset_attribute, H5ATTRset_attribute_string, H5ATTRget_attribute, H5ATTRget_attribute_string, H5ATTRget_attribute_vlen_string_array, H5ATTRfind_attribute, H5ATTRget_type_ndims, H5ATTRget_dims, H5ARRAYget_ndims, H5ARRAYget_info, set_cache_size, get_objinfo, get_linkinfo, Giterate, Aiterate, H5UIget_info, get_len_of_range, conv_float64_timeval32, truncate_dset, H5_HAVE_DIRECT_DRIVER, pt_H5Pset_fapl_direct, H5_HAVE_WINDOWS_DRIVER, pt_H5Pset_fapl_windows, H5_HAVE_IMAGE_FILE, pt_H5Pset_file_image, pt_H5Fget_file_image) cdef int H5T_CSET_DEFAULT = 16 from utilsextension cimport malloc_dims, get_native_type, cstr_to_pystr #------------------------------------------------------------------- # Functions from HDF5 ARRAY (this is not part of HDF5 HL; it's private) cdef extern from "H5ARRAY.h" nogil: herr_t H5ARRAYmake(hid_t loc_id, char *dset_name, char *obversion, int rank, hsize_t *dims, int extdim, hid_t type_id, hsize_t *dims_chunk, void *fill_data, int complevel, char *complib, int shuffle, int fletcher32, void *data) herr_t H5ARRAYappend_records(hid_t dataset_id, hid_t type_id, int rank, hsize_t *dims_orig, hsize_t *dims_new, int extdim, void *data ) herr_t H5ARRAYwrite_records(hid_t dataset_id, hid_t type_id, int rank, hsize_t *start, hsize_t *step, hsize_t *count, void *data) herr_t H5ARRAYread(hid_t dataset_id, hid_t type_id, hsize_t start, hsize_t nrows, hsize_t step, int extdim, void *data) herr_t H5ARRAYreadSlice(hid_t dataset_id, hid_t type_id, hsize_t *start, hsize_t *stop, hsize_t *step, void *data) herr_t H5ARRAYreadIndex(hid_t dataset_id, hid_t type_id, int notequal, hsize_t *start, hsize_t *stop, hsize_t *step, void *data) herr_t H5ARRAYget_chunkshape(hid_t dataset_id, int rank, hsize_t *dims_chunk) herr_t H5ARRAYget_fill_value( hid_t dataset_id, hid_t type_id, int *status, void *value) # Functions for dealing with VLArray objects cdef extern from "H5VLARRAY.h" nogil: herr_t H5VLARRAYmake( hid_t loc_id, char *dset_name, char *obversion, int rank, hsize_t *dims, hid_t type_id, hsize_t chunk_size, void *fill_data, int complevel, char *complib, int shuffle, int flecther32, void *data) herr_t H5VLARRAYappend_records( hid_t dataset_id, hid_t type_id, int nobjects, hsize_t nrecords, void *data ) herr_t H5VLARRAYmodify_records( hid_t dataset_id, hid_t type_id, hsize_t nrow, int nobjects, void *data ) herr_t H5VLARRAYget_info( hid_t dataset_id, hid_t type_id, hsize_t *nrecords, char *base_byteorder) #---------------------------------------------------------------------------- # Initialization code # The numpy API requires this function to be called before # using any numpy facilities in an extension module. import_array() #--------------------------------------------------------------------------- # Helper functions cdef hsize_t *npy_malloc_dims(int rank, npy_intp *pdims): """Returns a malloced hsize_t dims from a npy_intp *pdims.""" cdef int i cdef hsize_t *dims dims = NULL if rank > 0: dims = malloc(rank * sizeof(hsize_t)) for i from 0 <= i < rank: dims[i] = pdims[i] return dims cdef object getshape(int rank, hsize_t *dims): """Return a shape (tuple) from a dims C array of rank dimensions.""" cdef int i cdef object shape shape = [] for i from 0 <= i < rank: shape.append(SizeType(dims[i])) return tuple(shape) # Helper function for quickly fetch an attribute string cdef object get_attribute_string_or_none(hid_t node_id, char* attr_name): """Returns a string/unicode attribute if it exists in node_id. It returns ``None`` in case it don't exists (or there have been problems reading it). """ cdef char *attr_value cdef int cset = H5T_CSET_DEFAULT cdef object retvalue cdef hsize_t size attr_value = NULL retvalue = None # Default value if H5ATTRfind_attribute(node_id, attr_name): size = H5ATTRget_attribute_string(node_id, attr_name, &attr_value, &cset) if size == 0: return None if cset == H5T_CSET_UTF8: retvalue = PyUnicode_DecodeUTF8(attr_value, size, NULL) retvalue = numpy.unicode_(retvalue) else: retvalue = PyBytes_FromStringAndSize(attr_value, size) # AV: oct 2012 # since now we use the string size got form HDF5 we have to stip # trailing zeros used for padding. # The entire process is quite odd but due to a bug (??) in the way # numpy arrays are pickled in python 3 we can't assume that we can't # assume that strlen(attr_value) is the actual length of the attibute # and numpy.bytes_(attr_value) can give a truncated pickle sting retvalue = retvalue.rstrip(b'\x00') retvalue = numpy.bytes_(retvalue) # Important to release attr_value, because it has been malloc'ed! if attr_value: free(attr_value) return retvalue # Get the numpy dtype scalar attribute from an HDF5 type as fast as possible cdef object get_dtype_scalar(hid_t type_id, H5T_class_t class_id, size_t itemsize): cdef H5T_sign_t sign cdef object stype if class_id == H5T_BITFIELD: stype = "b1" elif class_id == H5T_INTEGER: # Get the sign sign = H5Tget_sign(type_id) if (sign > 0): stype = "i%s" % (itemsize) else: stype = "u%s" % (itemsize) elif class_id == H5T_FLOAT: stype = "f%s" % (itemsize) elif class_id == H5T_STRING: if H5Tis_variable_str(type_id): raise TypeError("variable length strings are not supported yet") stype = "S%s" % (itemsize) # Try to get a NumPy type. If this can't be done, return None. try: ntype = numpy.dtype(stype) except TypeError: ntype = None return ntype _supported_drivers = ( "H5FD_SEC2", "H5FD_DIRECT", #"H5FD_LOG", "H5FD_WINDOWS", "H5FD_STDIO", "H5FD_CORE", #"H5FD_FAMILY", #"H5FD_MULTI", "H5FD_SPLIT", #"H5FD_MPIO", #"H5FD_MPIPOSIX", #"H5FD_STREAM", ) HAVE_DIRECT_DRIVER = bool(H5_HAVE_DIRECT_DRIVER) HAVE_WINDOWS_DRIVER = bool(H5_HAVE_WINDOWS_DRIVER) # Type extensions declarations (these are subclassed by PyTables # Python classes) cdef class File: cdef hid_t file_id cdef hid_t access_plist cdef object name def _g_new(self, name, pymode, **params): cdef herr_t err = 0 cdef hid_t access_plist, create_plist = H5P_DEFAULT cdef hid_t meta_plist_id = H5P_DEFAULT, raw_plist_id = H5P_DEFAULT cdef size_t img_buf_len = 0, user_block_size = 0 cdef void *img_buf_p = NULL cdef bytes encname #cdef bytes logfile_name # Check if we can handle the driver driver = params["DRIVER"] if driver is not None and driver not in _supported_drivers: raise ValueError("Invalid or not supported driver: '%s'" % driver) if driver == "H5FD_SPLIT": meta_ext = params.get("DRIVER_SPLIT_META_EXT", "-m.h5") raw_ext = params.get("DRIVER_SPLIT_RAW_EXT", "-r.h5") meta_name = meta_ext % name if "%s" in meta_ext else name + meta_ext raw_name = raw_ext % name if "%s" in raw_ext else name + raw_ext enc_meta_ext = encode_filename(meta_ext) enc_raw_ext = encode_filename(raw_ext) # Create a new file using default properties self.name = name # Encode the filename in case it is unicode encname = encode_filename(name) # These fields can be seen from Python. self._v_new = None # this will be computed later # """Is this file going to be created from scratch?""" self._isPTFile = True # assume a PyTables file by default # """Does this HDF5 file have a PyTables format?""" assert pymode in ('r', 'r+', 'a', 'w'), ("an invalid mode string ``%s`` " "passed the ``check_file_access()`` test; " "please report this to the authors" % pymode) image = params.get('DRIVER_CORE_IMAGE') if image: if driver != "H5FD_CORE": warnings.warn("The DRIVER_CORE_IMAGE parameter will be ignored by " "the '%s' driver" % driver) elif not PyBytes_Check(image): raise TypeError("The DRIVER_CORE_IMAGE must be a string of bytes") elif not H5_HAVE_IMAGE_FILE: raise RuntimeError("Support for image files is only availabe in " "HDF5 >= 1.8.9") # After the following check we can be quite sure # that the file or directory exists and permissions are right. if driver == "H5FD_SPLIT": for n in meta_name, raw_name: check_file_access(n, pymode) else: backing_store = params.get("DRIVER_CORE_BACKING_STORE", 1) if driver != "H5FD_CORE" or backing_store: check_file_access(name, pymode) # Should a new file be created? if image: exists = True elif driver == "H5FD_SPLIT": exists = os.path.exists(meta_name) and os.path.exists(raw_name) else: exists = os.path.exists(name) self._v_new = not (pymode in ('r', 'r+') or (pymode == 'a' and exists)) user_block_size = params.get("USER_BLOCK_SIZE", 0) if user_block_size and not self._v_new: warnings.warn("The HDF5 file already esists: the USER_BLOCK_SIZE " "will be ignored") elif user_block_size: user_block_size = int(user_block_size) is_pow_of_2 = ((user_block_size & (user_block_size - 1)) == 0) if user_block_size < 512 or not is_pow_of_2: raise ValueError("The USER_BLOCK_SIZE must be a power od 2 greather " "than 512 or zero") # File creation property list create_plist = H5Pcreate(H5P_FILE_CREATE) err = H5Pset_userblock(create_plist, user_block_size) if err < 0: H5Pclose(create_plist) raise HDF5ExtError("Unable to set the user block size") # File access property list access_plist = H5Pcreate(H5P_FILE_ACCESS) # Set parameters for chunk cache H5Pset_cache(access_plist, 0, params["CHUNK_CACHE_NELMTS"], params["CHUNK_CACHE_SIZE"], params["CHUNK_CACHE_PREEMPT"]) # Set the I/O driver if driver == "H5FD_SEC2": err = H5Pset_fapl_sec2(access_plist) elif driver == "H5FD_DIRECT": if not H5_HAVE_DIRECT_DRIVER: H5Pclose(create_plist) H5Pclose(access_plist) raise RuntimeError("The H5FD_DIRECT driver is not available") err = pt_H5Pset_fapl_direct(access_plist, params["DRIVER_DIRECT_ALIGNMENT"], params["DRIVER_DIRECT_BLOCK_SIZE"], params["DRIVER_DIRECT_CBUF_SIZE"]) #elif driver == "H5FD_LOG": # if "DRIVER_LOG_FILE" not in params: # H5Pclose(access_plist) # raise ValueError("The DRIVER_LOG_FILE parameter is required for " # "the H5FD_LOG driver") # logfile_name = encode_filename(params["DRIVER_LOG_FILE"]) # err = H5Pset_fapl_log(access_plist, # logfile_name, # params["DRIVER_LOG_FLAGS"], # params["DRIVER_LOG_BUF_SIZE"]) elif driver == "H5FD_WINDOWS": if not H5_HAVE_WINDOWS_DRIVER: H5Pclose(access_plist) H5Pclose(create_plist) raise RuntimeError("The H5FD_WINDOWS driver is not available") err = pt_H5Pset_fapl_windows(access_plist) elif driver == "H5FD_STDIO": err = H5Pset_fapl_stdio(access_plist) elif driver == "H5FD_CORE": err = H5Pset_fapl_core(access_plist, params["DRIVER_CORE_INCREMENT"], backing_store) if image: img_buf_len = len(image) img_buf_p = PyBytes_AsString(image) err = pt_H5Pset_file_image(access_plist, img_buf_p, img_buf_len) if err < 0: H5Pclose(create_plist) H5Pclose(access_plist) raise HDF5ExtError("Unable to set the file image") #elif driver == "H5FD_FAMILY": # H5Pset_fapl_family(access_plist, # params["DRIVER_FAMILY_MEMB_SIZE"], # fapl_id) #elif driver == "H5FD_MULTI": # err = H5Pset_fapl_multi(access_plist, memb_map, memb_fapl, memb_name, # memb_addr, relax) elif driver == "H5FD_SPLIT": err = H5Pset_fapl_split(access_plist, enc_meta_ext, meta_plist_id, enc_raw_ext, raw_plist_id) if err < 0: e = HDF5ExtError("Unable to set the file access property list") H5Pclose(create_plist) H5Pclose(access_plist) raise e if pymode == 'r': self.file_id = H5Fopen(encname, H5F_ACC_RDONLY, access_plist) elif pymode == 'r+': self.file_id = H5Fopen(encname, H5F_ACC_RDWR, access_plist) elif pymode == 'a': if exists: # A test for logging. ## H5Pset_sieve_buf_size(access_plist, 0) ## H5Pset_fapl_log (access_plist, "test.log", H5FD_LOG_LOC_WRITE, 0) self.file_id = H5Fopen(encname, H5F_ACC_RDWR, access_plist) else: self.file_id = H5Fcreate(encname, H5F_ACC_TRUNC, create_plist, access_plist) elif pymode == 'w': self.file_id = H5Fcreate(encname, H5F_ACC_TRUNC, create_plist, access_plist) if self.file_id < 0: e = HDF5ExtError("Unable to open/create file '%s'" % name) H5Pclose(create_plist) H5Pclose(access_plist) raise e H5Pclose(create_plist) H5Pclose(access_plist) # Set the cache size set_cache_size(self.file_id, params["METADATA_CACHE_SIZE"]) # Set the maximum number of threads for Blosc set_blosc_max_threads(params["MAX_BLOSC_THREADS"]) # XXX: add the possibility to pass a pre-allocated buffer def get_file_image(self): """Retrieves an in-memory image of an existing, open HDF5 file. .. note:: this method requires HDF5 >= 1.8.9. .. versionadded:: 3.0 """ cdef ssize_t size = 0 cdef size_t buf_len = 0 cdef bytes image cdef char* cimage self.flush() # retrieve the size of the buffer for the file image size = pt_H5Fget_file_image(self.file_id, NULL, buf_len) if size < 0: raise HDF5ExtError("Unable to retrieve the size of the buffer for the " "file image. Plese note that not all drivers " "provide support for image files.") # allocate the memory buffer image = PyBytes_FromStringAndSize(NULL, size) if not image: raise RuntimeError("Unable to allecote meomory fir the file image") cimage = image buf_len = size size = pt_H5Fget_file_image(self.file_id, cimage, buf_len) if size < 0: raise HDF5ExtError("Unable to retrieve the file image. " "Plese note that not all drivers provide support " "for image files.") return image def get_filesize(self): """Returns the size of an HDF5 file. The returned size is that of the entire file, as opposed to only the HDF5 portion of the file. I.e., size includes the user block, if any, the HDF5 portion of the file, and any data that may have been appended beyond the data written through the HDF5 Library. .. versionadded:: 3.0 """ cdef herr_t err = 0 cdef hsize_t size = 0 err = H5Fget_filesize(self.file_id, &size) if err < 0: raise HDF5ExtError("Unable to retrieve the HDF5 file size") return size def get_userblock_size(self): """Retrieves the size of a user block. .. versionadded:: 3.0 """ cdef herr_t err = 0 cdef hsize_t size = 0 cdef hid_t create_plist create_plist = H5Fget_create_plist(self.file_id) if create_plist < 0: raise HDF5ExtError("Unable to get the creation property list") err = H5Pget_userblock(create_plist, &size) if err < 0: H5Pclose(create_plist) raise HDF5ExtError("unable to retrieve the user block size") H5Pclose(create_plist) return size # Accessor definitions def _get_file_id(self): return self.file_id def fileno(self): """Return the underlying OS integer file descriptor. This is needed for lower-level file interfaces, such as the ``fcntl`` module. """ cdef void *file_handle cdef uintptr_t *descriptor cdef herr_t err err = H5Fget_vfd_handle(self.file_id, H5P_DEFAULT, &file_handle) if err < 0: raise HDF5ExtError( "Problems getting file descriptor for file ``%s``" % self.name) # Convert the 'void *file_handle' into an 'int *descriptor' descriptor = file_handle return descriptor[0] _getFileId = previous_api(_get_file_id) def _flush_file(self, scope): # Close the file H5Fflush(self.file_id, scope) _flushFile = previous_api(_flush_file) def _close_file(self): # Close the file H5Fclose( self.file_id ) self.file_id = 0 # Means file closed _closeFile = previous_api(_close_file) # This method is moved out of scope, until we provide code to delete # the memory booked by this extension types def __dealloc__(self): cdef int ret if self.file_id > 0: # Close the HDF5 file because user didn't do that! ret = H5Fclose(self.file_id) if ret < 0: raise HDF5ExtError("Problems closing the file '%s'" % self.name) cdef class AttributeSet: cdef object name def _g_new(self, node): self.name = node._v_name def _g_list_attr(self, node): "Return a tuple with the attribute list" a = Aiterate(node._v_objectid) return a _g_listAttr = previous_api(_g_list_attr) def _g_setattr(self, node, name, object value): """Save Python or NumPy objects as HDF5 attributes. Scalar Python objects, scalar NumPy & 0-dim NumPy objects will all be saved as H5T_SCALAR type. N-dim NumPy objects will be saved as H5T_ARRAY type. """ cdef int ret cdef hid_t dset_id, type_id cdef hsize_t *dims cdef ndarray ndv cdef object byteorder, rabyteorder, baseatom cdef char* cname = NULL cdef bytes encoded_name cdef int cset = H5T_CSET_DEFAULT encoded_name = name.encode('utf-8') # get the C pointer cname = encoded_name # The dataset id of the node dset_id = node._v_objectid # Convert a NumPy scalar into a NumPy 0-dim ndarray if isinstance(value, numpy.generic): value = numpy.array(value) # Check if value is a NumPy ndarray and of a supported type if (isinstance(value, numpy.ndarray) and value.dtype.kind in ('V', 'S', 'b', 'i', 'u', 'f', 'c')): # get a contiguous array: fixes #270 and gh-176 #value = numpy.ascontiguousarray(value) value = value.copy() if value.dtype.kind == 'V': description, rabyteorder = descr_from_dtype(value.dtype) byteorder = byteorders[rabyteorder] type_id = create_nested_type(description, byteorder) else: # Get the associated native HDF5 type of the scalar type baseatom = Atom.from_dtype(value.dtype.base) byteorder = byteorders[value.dtype.byteorder] type_id = atom_to_hdf5_type(baseatom, byteorder) # Get dimensionality info ndv = value dims = npy_malloc_dims(ndv.ndim, ndv.shape) # Actually write the attribute ret = H5ATTRset_attribute(dset_id, cname, type_id, ndv.ndim, dims, ndv.data) if ret < 0: raise HDF5ExtError("Can't set attribute '%s' in node:\n %s." % (name, self._v_node)) # Release resources free(dims) H5Tclose(type_id) else: # Object cannot be natively represented in HDF5. if (isinstance(value, numpy.ndarray) and value.dtype.kind == 'U' and value.shape == ()): value = value[()].encode('utf-8') cset = H5T_CSET_UTF8 else: # Convert this object to a null-terminated string # (binary pickles are not supported at this moment) value = pickle.dumps(value, 0) ret = H5ATTRset_attribute_string(dset_id, cname, value, len(value), cset) if ret < 0: raise HDF5ExtError("Can't set attribute '%s' in node:\n %s." % (name, self._v_node)) _g_setAttr = previous_api(_g_setattr) # Get attributes def _g_getattr(self, node, attrname): """Get HDF5 attributes and retrieve them as NumPy objects. H5T_SCALAR types will be retrieved as scalar NumPy. H5T_ARRAY types will be retrieved as ndarray NumPy objects. """ cdef hsize_t *dims cdef H5T_class_t class_id cdef size_t type_size cdef hid_t mem_type, dset_id, type_id, native_type cdef int rank, ret, enumtype cdef void *rbuf cdef char *str_value cdef char **str_values = NULL cdef ndarray ndvalue cdef object shape, stype_atom, shape_atom, retvalue cdef int i, nelements cdef char* cattrname = NULL cdef bytes encoded_attrname cdef int cset = H5T_CSET_DEFAULT encoded_attrname = attrname.encode('utf-8') # Get the C pointer cattrname = encoded_attrname # The dataset id of the node dset_id = node._v_objectid dims = NULL ret = H5ATTRget_type_ndims(dset_id, cattrname, &type_id, &class_id, &type_size, &rank ) if ret < 0: raise HDF5ExtError("Can't get type info on attribute %s in node %s." % (attrname, self.name)) # Call a fast function for scalar values and typical class types if (rank == 0 and class_id == H5T_STRING): type_size = H5ATTRget_attribute_string(dset_id, cattrname, &str_value, &cset) if type_size == 0: raise HDF5ExtError("Can't read attribute %s in node %s." % (attrname, self.name)) if cset == H5T_CSET_UTF8: retvalue = PyUnicode_DecodeUTF8(str_value, strlen(str_value), NULL) retvalue = numpy.unicode_(retvalue) else: retvalue = PyBytes_FromStringAndSize(str_value, type_size) # AV: oct 2012 # since now we use the string size got form HDF5 we have to strip # trailing zeros used for padding. # The entire process is quite odd but due to a bug (??) in the way # numpy arrays are pickled in python 3 we can't assume that # strlen(attr_value) is the actual length of the attibute # and numpy.bytes_(attr_value) can give a truncated pickle sting retvalue = retvalue.rstrip(b'\x00') retvalue = numpy.bytes_(retvalue) # bytes # Important to release attr_value, because it has been malloc'ed! if str_value: free(str_value) H5Tclose(type_id) return retvalue elif (rank == 0 and class_id in (H5T_BITFIELD, H5T_INTEGER, H5T_FLOAT)): dtype_ = get_dtype_scalar(type_id, class_id, type_size) if dtype_ is None: warnings.warn("Unsupported type for attribute '%s' in node '%s'. " "Offending HDF5 class: %d" % (attrname, self.name, class_id), DataTypeWarning) self._v_unimplemented.append(attrname) return None shape = () else: # General case # Get the dimensional info dims = malloc(rank * sizeof(hsize_t)) ret = H5ATTRget_dims(dset_id, cattrname, dims) if ret < 0: raise HDF5ExtError("Can't get dims info on attribute %s in node %s." % (attrname, self.name)) shape = getshape(rank, dims) # dims is not needed anymore free( dims) # Get the NumPy dtype from the type_id try: stype_, shape_ = hdf5_to_np_ext_type(type_id, pure_numpy_types=True) dtype_ = numpy.dtype(stype_, shape_) except TypeError: if class_id == H5T_STRING and H5Tis_variable_str(type_id): nelements = H5ATTRget_attribute_vlen_string_array(dset_id, cattrname, &str_values, &cset) if nelements < 0: raise HDF5ExtError("Can't read attribute %s in node %s." % (attrname, self.name)) # The following generator expressions do not work with Cython 0.15.1 if cset == H5T_CSET_UTF8: #retvalue = numpy.fromiter( # PyUnicode_DecodeUTF8(str_values[i], # strlen(str_values[i]), # NULL) # for i in range(nelements), "O8") retvalue = numpy.array([ PyUnicode_DecodeUTF8(str_values[i], strlen(str_values[i]), NULL) for i in range(nelements)], "O8") else: #retvalue = numpy.fromiter( # str_values[i] for i in range(nelements), "O8") retvalue = numpy.array( [str_values[i] for i in range(nelements)], "O8") retvalue.shape = shape # Important to release attr_value, because it has been malloc'ed! for i in range(nelements): free(str_values[i]); free(str_values) return retvalue # This class is not supported. Instead of raising a TypeError, issue a # warning explaining the problem. This will allow to continue browsing # native HDF5 files, while informing the user about the problem. warnings.warn("Unsupported type for attribute '%s' in node '%s'. " "Offending HDF5 class: %d" % (attrname, self.name, class_id), DataTypeWarning) self._v_unimplemented.append(attrname) return None # Get the native type (so that it is HDF5 who is the responsible to deal # with non-native byteorders on-disk) native_type_id = get_native_type(type_id) # Get the container for data ndvalue = numpy.empty(dtype=dtype_, shape=shape) # Get the pointer to the buffer data area rbuf = ndvalue.data # Actually read the attribute from disk ret = H5ATTRget_attribute(dset_id, cattrname, native_type_id, rbuf) if ret < 0: raise HDF5ExtError("Attribute %s exists in node %s, but can't get it." % (attrname, self.name)) H5Tclose(native_type_id) H5Tclose(type_id) if rank > 0: # multidimensional case retvalue = ndvalue else: retvalue = ndvalue[()] # 0-dim ndarray becomes a NumPy scalar return retvalue _g_getAttr = previous_api(_g_getattr) def _g_remove(self, node, attrname): cdef int ret cdef hid_t dset_id cdef char *cattrname = NULL cdef bytes encoded_attrname encoded_attrname = attrname.encode('utf-8') # Get the C pointer cattrname = encoded_attrname # The dataset id of the node dset_id = node._v_objectid ret = H5Adelete(dset_id, cattrname) if ret < 0: raise HDF5ExtError("Attribute '%s' exists in node '%s', but cannot be " "deleted." % (attrname, self.name)) cdef class Node: # Instance variables declared in .pxd def _g_new(self, where, name, init): self.name = name # """The name of this node in its parent group.""" self.parent_id = where._v_objectid # """The identifier of the parent group.""" def _g_delete(self, parent): cdef int ret cdef bytes encoded_name encoded_name = self.name.encode('utf-8') # Delete this node ret = H5Ldelete(parent._v_objectid, encoded_name, H5P_DEFAULT) if ret < 0: raise HDF5ExtError("problems deleting the node ``%s``" % self.name) return ret def __dealloc__(self): self.parent_id = 0 cdef class Group(Node): cdef hid_t group_id def _g_create(self): cdef hid_t ret cdef bytes encoded_name encoded_name = self.name.encode('utf-8') # @TODO: set property list --> utf-8 # Create a new group ret = H5Gcreate(self.parent_id, encoded_name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT) if ret < 0: raise HDF5ExtError("Can't create the group %s." % self.name) self.group_id = ret return self.group_id def _g_open(self): cdef hid_t ret cdef bytes encoded_name encoded_name = self.name.encode('utf-8') ret = H5Gopen(self.parent_id, encoded_name, H5P_DEFAULT) if ret < 0: raise HDF5ExtError("Can't open the group: '%s'." % self.name) self.group_id = ret return self.group_id def _g_get_objinfo(self, object h5name): """Check whether 'name' is a children of 'self' and return its type.""" cdef int ret cdef object node_type cdef bytes encoded_name cdef char *cname encoded_name = h5name.encode('utf-8') # Get the C pointer cname = encoded_name ret = get_linkinfo(self.group_id, cname) if ret == -2 or ret == H5L_TYPE_ERROR: node_type = "NoSuchNode" elif ret == H5L_TYPE_SOFT: node_type = "SoftLink" elif ret == H5L_TYPE_EXTERNAL: node_type = "ExternalLink" elif ret == H5L_TYPE_HARD: ret = get_objinfo(self.group_id, cname) if ret == -2: node_type = "NoSuchNode" elif ret == H5O_TYPE_UNKNOWN: node_type = "Unknown" elif ret == H5O_TYPE_GROUP: node_type = "Group" elif ret == H5O_TYPE_DATASET: node_type = "Leaf" elif ret == H5O_TYPE_NAMED_DATATYPE: node_type = "NamedType" # Not supported yet else: node_type = "Unknown" return node_type def _g_list_group(self, parent): """Return a tuple with the groups and the leaves hanging from self.""" cdef bytes encoded_name encoded_name = self.name.encode('utf-8') return Giterate(parent._v_objectid, self._v_objectid, encoded_name) _g_listGroup = previous_api(_g_list_group) def _g_get_gchild_attr(self, group_name, attr_name): """Return an attribute of a child `Group`. If the attribute does not exist, ``None`` is returned. """ cdef hid_t gchild_id cdef object retvalue cdef bytes encoded_group_name cdef bytes encoded_attr_name encoded_group_name = group_name.encode('utf-8') encoded_attr_name = attr_name.encode('utf-8') # Open the group retvalue = None # Default value gchild_id = H5Gopen(self.group_id, encoded_group_name, H5P_DEFAULT) if gchild_id < 0: raise HDF5ExtError("Non-existing node ``%s`` under ``%s``" % (group_name, self._v_pathname)) retvalue = get_attribute_string_or_none(gchild_id, encoded_attr_name) # Close child group H5Gclose(gchild_id) return retvalue _g_getGChildAttr = previous_api(_g_get_gchild_attr) def _g_get_lchild_attr(self, leaf_name, attr_name): """Return an attribute of a child `Leaf`. If the attribute does not exist, ``None`` is returned. """ cdef hid_t leaf_id cdef object retvalue cdef bytes encoded_leaf_name cdef bytes encoded_attr_name encoded_leaf_name = leaf_name.encode('utf-8') encoded_attr_name = attr_name.encode('utf-8') # Open the dataset leaf_id = H5Dopen(self.group_id, encoded_leaf_name, H5P_DEFAULT) if leaf_id < 0: raise HDF5ExtError("Non-existing node ``%s`` under ``%s``" % (leaf_name, self._v_pathname)) retvalue = get_attribute_string_or_none(leaf_id, encoded_attr_name) # Close the dataset H5Dclose(leaf_id) return retvalue _g_getLChildAttr = previous_api(_g_get_lchild_attr) def _g_flush_group(self): # Close the group H5Fflush(self.group_id, H5F_SCOPE_GLOBAL) _g_flushGroup = previous_api(_g_flush_group) def _g_close_group(self): cdef int ret ret = H5Gclose(self.group_id) if ret < 0: raise HDF5ExtError("Problems closing the Group %s" % self.name) self.group_id = 0 # indicate that this group is closed _g_closeGroup = previous_api(_g_close_group) def _g_move_node(self, hid_t oldparent, oldname, hid_t newparent, newname, oldpathname, newpathname): cdef int ret cdef bytes encoded_oldname, encoded_newname encoded_oldname = oldname.encode('utf-8') encoded_newname = newname.encode('utf-8') ret = H5Lmove(oldparent, encoded_oldname, newparent, encoded_newname, H5P_DEFAULT, H5P_DEFAULT) if ret < 0: raise HDF5ExtError("Problems moving the node %s to %s" % (oldpathname, newpathname) ) return ret _g_moveNode = previous_api(_g_move_node) cdef class Leaf(Node): # Instance variables declared in .pxd def _get_storage_size(self): return H5Dget_storage_size(self.dataset_id) def _g_new(self, where, name, init): if init: # Put this info to 0 just when the class is initialized self.dataset_id = -1 self.type_id = -1 self.base_type_id = -1 self.disk_type_id = -1 super(Leaf, self)._g_new(where, name, init) cdef _get_type_ids(self): """Get the disk and native HDF5 types associated with this leaf. It is guaranteed that both disk and native types are not the same descriptor (so that it is safe to close them separately). """ cdef hid_t disk_type_id, native_type_id disk_type_id = H5Dget_type(self.dataset_id) native_type_id = get_native_type(disk_type_id) return (disk_type_id, native_type_id) cdef _convert_time64(self, ndarray nparr, int sense): """Converts a NumPy of Time64 elements between NumPy and HDF5 formats. NumPy to HDF5 conversion is performed when 'sense' is 0. Otherwise, HDF5 to NumPy conversion is performed. The conversion is done in place, i.e. 'nparr' is modified. """ cdef void *t64buf cdef long byteoffset, bytestride, nelements cdef hsize_t nrecords byteoffset = 0 # NumPy objects doesn't have an offset if (nparr).shape == (): # 0-dim array does contain *one* element nrecords = 1 bytestride = 8 else: nrecords = len(nparr) bytestride = nparr.strides[0] # supports multi-dimensional recarray nelements = nparr.size / nrecords t64buf = nparr.data conv_float64_timeval32( t64buf, byteoffset, bytestride, nrecords, nelements, sense) # can't do since cdef'd #_convertTime64 = previous_api(_convert_time64) def _g_truncate(self, hsize_t size): """Truncate a Leaf to `size` nrows.""" cdef hsize_t ret ret = truncate_dset(self.dataset_id, self.maindim, size) if ret < 0: raise HDF5ExtError("Problems truncating the leaf: %s" % self) classname = self.__class__.__name__ if classname in ('EArray', 'CArray'): # Update the new dimensionality self.dims[self.maindim] = size # Update the shape shape = list(self.shape) shape[self.maindim] = SizeType(size) self.shape = tuple(shape) elif classname in ('Table', 'VLArray'): self.nrows = size else: raise ValueError("Unexpected classname: %s" % classname) def _g_flush(self): # Flush the dataset (in fact, the entire buffers in file!) if self.dataset_id >= 0: H5Fflush(self.dataset_id, H5F_SCOPE_GLOBAL) def _g_close(self): # Close dataset in HDF5 space # Release resources if self.type_id >= 0: H5Tclose(self.type_id) if self.disk_type_id >= 0: H5Tclose(self.disk_type_id) if self.base_type_id >= 0: H5Tclose(self.base_type_id) if self.dataset_id >= 0: H5Dclose(self.dataset_id) cdef class Array(Leaf): # Instance variables declared in .pxd def _create_array(self, ndarray nparr, object title, object atom): cdef int i cdef herr_t ret cdef void *rbuf cdef bytes complib, version, class_ cdef object dtype_, atom_, shape cdef ndarray dims cdef bytes encoded_title, encoded_name cdef H5T_cset_t cset = H5T_CSET_ASCII encoded_title = title.encode('utf-8') encoded_name = self.name.encode('utf-8') # Get the HDF5 type associated with this numpy type shape = (nparr).shape if atom is None or atom.shape == (): dtype_ = nparr.dtype.base atom_ = Atom.from_dtype(dtype_) else: atom_ = atom shape = shape[:-len(atom_.shape)] self.disk_type_id = atom_to_hdf5_type(atom_, self.byteorder) # Allocate space for the dimension axis info and fill it dims = numpy.array(shape, dtype=numpy.intp) self.rank = len(shape) self.dims = npy_malloc_dims(self.rank, (dims.data)) # Get the pointer to the buffer data area strides = (nparr).strides # When the object is not a 0-d ndarray and its strides == 0, that # means that the array does not contain actual data if strides != () and sum(strides) == 0: rbuf = NULL else: rbuf = nparr.data # Save the array complib = (self.filters.complib or '').encode('utf-8') version = self._v_version.encode('utf-8') class_ = self._c_classid.encode('utf-8') self.dataset_id = H5ARRAYmake(self.parent_id, encoded_name, version, self.rank, self.dims, self.extdim, self.disk_type_id, NULL, NULL, self.filters.complevel, complib, self.filters.shuffle, self.filters.fletcher32, rbuf) if self.dataset_id < 0: raise HDF5ExtError("Problems creating the %s." % self.__class__.__name__) if self._v_file.params['PYTABLES_SYS_ATTRS']: if PY_MAJOR_VERSION > 2: cset = H5T_CSET_UTF8 # Set the conforming array attributes H5ATTRset_attribute_string(self.dataset_id, "CLASS", class_, len(class_), cset) H5ATTRset_attribute_string(self.dataset_id, "VERSION", version, len(version), cset) H5ATTRset_attribute_string(self.dataset_id, "TITLE", encoded_title, len(encoded_title), cset) # Get the native type (so that it is HDF5 who is the responsible to deal # with non-native byteorders on-disk) self.type_id = get_native_type(self.disk_type_id) return (self.dataset_id, shape, atom_) _createArray = previous_api(_create_array) def _create_carray(self, object title): cdef int i cdef herr_t ret cdef void *rbuf cdef bytes complib, version, class_ cdef ndarray dflts cdef void *fill_data cdef ndarray extdim cdef object atom cdef bytes encoded_title, encoded_name encoded_title = title.encode('utf-8') encoded_name = self.name.encode('utf-8') atom = self.atom self.disk_type_id = atom_to_hdf5_type(atom, self.byteorder) self.rank = len(self.shape) self.dims = malloc_dims(self.shape) if self.chunkshape: self.dims_chunk = malloc_dims(self.chunkshape) rbuf = NULL # The data pointer. We don't have data to save initially # Encode strings complib = (self.filters.complib or '').encode('utf-8') version = self._v_version.encode('utf-8') class_ = self._c_classid.encode('utf-8') # Get the fill values if isinstance(atom.dflt, numpy.ndarray) or atom.dflt: dflts = numpy.array(atom.dflt, dtype=atom.dtype) fill_data = dflts.data else: dflts = numpy.zeros((), dtype=atom.dtype) fill_data = NULL if atom.shape == (): # The default is preferred as a scalar value instead of 0-dim array atom.dflt = dflts[()] else: atom.dflt = dflts # Create the CArray/EArray self.dataset_id = H5ARRAYmake( self.parent_id, encoded_name, version, self.rank, self.dims, self.extdim, self.disk_type_id, self.dims_chunk, fill_data, self.filters.complevel, complib, self.filters.shuffle, self.filters.fletcher32, rbuf) if self.dataset_id < 0: raise HDF5ExtError("Problems creating the %s." % self.__class__.__name__) if self._v_file.params['PYTABLES_SYS_ATTRS']: # Set the conforming array attributes H5ATTRset_attribute_string(self.dataset_id, "CLASS", class_, len(class_), H5T_CSET_ASCII) H5ATTRset_attribute_string(self.dataset_id, "VERSION", version, len(version), H5T_CSET_ASCII) H5ATTRset_attribute_string(self.dataset_id, "TITLE", encoded_title, len(encoded_title), H5T_CSET_ASCII) if self.extdim >= 0: extdim = numpy.array([self.extdim], dtype="int32") # Attach the EXTDIM attribute in case of enlargeable arrays H5ATTRset_attribute(self.dataset_id, "EXTDIM", H5T_NATIVE_INT, 0, NULL, extdim.data) # Get the native type (so that it is HDF5 who is the responsible to deal # with non-native byteorders on-disk) self.type_id = get_native_type(self.disk_type_id) return self.dataset_id _createCArray = previous_api(_create_carray) def _open_array(self): cdef size_t type_size, type_precision cdef H5T_class_t class_id cdef char cbyteorder[11] # "irrelevant" fits easily here cdef int i cdef int extdim cdef herr_t ret cdef object shape, chunkshapes, atom cdef int fill_status cdef ndarray dflts cdef void *fill_data cdef bytes encoded_name cdef str byteorder encoded_name = self.name.encode('utf-8') # Open the dataset self.dataset_id = H5Dopen(self.parent_id, encoded_name, H5P_DEFAULT) if self.dataset_id < 0: raise HDF5ExtError("Non-existing node ``%s`` under ``%s``" % (self.name, self._v_parent._v_pathname)) # Get the datatype handles self.disk_type_id, self.type_id = self._get_type_ids() # Get the atom for this type atom = atom_from_hdf5_type(self.type_id) # Get the rank for this array object if H5ARRAYget_ndims(self.dataset_id, &self.rank) < 0: raise HDF5ExtError("Problems getting ndims!") # Allocate space for the dimension axis info self.dims = malloc(self.rank * sizeof(hsize_t)) self.maxdims = malloc(self.rank * sizeof(hsize_t)) # Get info on dimensions, class and type (of base class) ret = H5ARRAYget_info(self.dataset_id, self.disk_type_id, self.dims, self.maxdims, &class_id, cbyteorder) if ret < 0: raise HDF5ExtError("Unable to get array info.") byteorder = cstr_to_pystr(cbyteorder) # Get the extendable dimension (if any) self.extdim = -1 # default is non-extensible Array for i from 0 <= i < self.rank: if self.maxdims[i] == -1: self.extdim = i break # Get the shape as a python tuple shape = getshape(self.rank, self.dims) # Allocate space for the dimension chunking info self.dims_chunk = malloc(self.rank * sizeof(hsize_t)) if H5ARRAYget_chunkshape(self.dataset_id, self.rank, self.dims_chunk) < 0: # The Array class is not chunked! chunkshapes = None else: # Get the chunkshape as a python tuple chunkshapes = getshape(self.rank, self.dims_chunk) # Get the fill value dflts = numpy.zeros((), dtype=atom.dtype) fill_data = dflts.data H5ARRAYget_fill_value(self.dataset_id, self.type_id, &fill_status, fill_data); if fill_status == H5D_FILL_VALUE_UNDEFINED: # This can only happen with datasets created with other libraries # than PyTables. dflts = None if dflts is not None and atom.shape == (): # The default is preferred as a scalar value instead of 0-dim array atom.dflt = dflts[()] else: atom.dflt = dflts # Get the byteorder self.byteorder = correct_byteorder(atom.type, byteorder) return (self.dataset_id, atom, shape, chunkshapes) _openArray = previous_api(_open_array) def _append(self, ndarray nparr): cdef int ret, extdim cdef hsize_t *dims_arr cdef void *rbuf cdef object shape # Allocate space for the dimension axis info dims_arr = npy_malloc_dims(self.rank, nparr.shape) # Get the pointer to the buffer data area rbuf = nparr.data # Convert some NumPy types to HDF5 before storing. if self.atom.type == 'time64': self._convert_time64(nparr, 0) # Append the records extdim = self.extdim with nogil: ret = H5ARRAYappend_records(self.dataset_id, self.type_id, self.rank, self.dims, dims_arr, extdim, rbuf) if ret < 0: raise HDF5ExtError("Problems appending the elements") free(dims_arr) # Update the new dimensionality shape = list(self.shape) shape[self.extdim] = SizeType(self.dims[self.extdim]) self.shape = tuple(shape) def _read_array(self, hsize_t start, hsize_t stop, hsize_t step, ndarray nparr): cdef herr_t ret cdef void *rbuf cdef hsize_t nrows cdef int extdim # Get the pointer to the buffer data area rbuf = nparr.data # Number of rows to read nrows = get_len_of_range(start, stop, step) if hasattr(self, "extdim"): extdim = self.extdim else: extdim = -1 # Do the physical read with nogil: ret = H5ARRAYread(self.dataset_id, self.type_id, start, nrows, step, extdim, rbuf) if ret < 0: raise HDF5ExtError("Problems reading the array data.") if self.atom.kind == 'time': # Swap the byteorder by hand (this is not currently supported by HDF5) if H5Tget_order(self.type_id) != platform_byteorder: nparr.byteswap(True) # Convert some HDF5 types to NumPy after reading. if self.atom.type == 'time64': self._convert_time64(nparr, 1) return _readArray = previous_api(_read_array) def _g_read_slice(self, ndarray startl, ndarray stopl, ndarray stepl, ndarray nparr): cdef herr_t ret cdef hsize_t *start cdef hsize_t *stop cdef hsize_t *step cdef void *rbuf # Get the pointer to the buffer data area of startl, stopl and stepl arrays start = startl.data stop = stopl.data step = stepl.data # Get the pointer to the buffer data area rbuf = nparr.data # Do the physical read with nogil: ret = H5ARRAYreadSlice(self.dataset_id, self.type_id, start, stop, step, rbuf) if ret < 0: raise HDF5ExtError("Problems reading the array data.") if self.atom.kind == 'time': # Swap the byteorder by hand (this is not currently supported by HDF5) if H5Tget_order(self.type_id) != platform_byteorder: nparr.byteswap(True) # Convert some HDF5 types to NumPy after reading if self.atom.type == 'time64': self._convert_time64(nparr, 1) return _g_readSlice = previous_api(_g_read_slice) def _g_read_coords(self, ndarray coords, ndarray nparr): """Read coordinates in an already created NumPy array.""" cdef herr_t ret cdef hid_t space_id cdef hid_t mem_space_id cdef hsize_t size cdef void *rbuf cdef object mode # Get the dataspace handle space_id = H5Dget_space(self.dataset_id) # Create a memory dataspace handle size = nparr.size mem_space_id = H5Screate_simple(1, &size, NULL) # Select the dataspace to be read H5Sselect_elements(space_id, H5S_SELECT_SET, size, coords.data) # Get the pointer to the buffer data area rbuf = nparr.data # Do the actual read with nogil: ret = H5Dread(self.dataset_id, self.type_id, mem_space_id, space_id, H5P_DEFAULT, rbuf) if ret < 0: raise HDF5ExtError("Problems reading the array data.") # Terminate access to the memory dataspace H5Sclose(mem_space_id) # Terminate access to the dataspace H5Sclose(space_id) if self.atom.kind == 'time': # Swap the byteorder by hand (this is not currently supported by HDF5) if H5Tget_order(self.type_id) != platform_byteorder: nparr.byteswap(True) # Convert some HDF5 types to NumPy after reading if self.atom.type == 'time64': self._convert_time64(nparr, 1) return _g_readCoords = previous_api(_g_read_coords) def perform_selection(self, space_id, start, count, step, idx, mode): """Performs a selection using start/count/step in the given axis. All other axes have their full range selected. The selection is added to the current `space_id` selection using the given mode. Note: This is a backport from the h5py project. """ cdef int select_mode cdef ndarray start_, count_, step_ cdef hsize_t *startp cdef hsize_t *countp cdef hsize_t *stepp # Build arrays for the selection parameters startl, countl, stepl = [], [], [] for i, x in enumerate(self.shape): if i != idx: startl.append(0) countl.append(x) stepl.append(1) else: startl.append(start) countl.append(count) stepl.append(step) start_ = numpy.array(startl, dtype="i8") count_ = numpy.array(countl, dtype="i8") step_ = numpy.array(stepl, dtype="i8") # Get the pointers to array data startp = start_.data countp = count_.data stepp = step_.data # Do the actual selection select_modes = {"AND": H5S_SELECT_AND, "NOTB": H5S_SELECT_NOTB} assert mode in select_modes select_mode = select_modes[mode] H5Sselect_hyperslab(space_id, select_mode, startp, stepp, countp, NULL) def _g_read_selection(self, object selection, ndarray nparr): """Read a selection in an already created NumPy array.""" cdef herr_t ret cdef hid_t space_id cdef hid_t mem_space_id cdef hsize_t size cdef void *rbuf cdef object mode # Get the dataspace handle space_id = H5Dget_space(self.dataset_id) # Create a memory dataspace handle size = nparr.size mem_space_id = H5Screate_simple(1, &size, NULL) # Select the dataspace to be read # Start by selecting everything H5Sselect_all(space_id) # Now refine with outstanding selections for args in selection: self.perform_selection(space_id, *args) # Get the pointer to the buffer data area rbuf = nparr.data # Do the actual read with nogil: ret = H5Dread(self.dataset_id, self.type_id, mem_space_id, space_id, H5P_DEFAULT, rbuf) if ret < 0: raise HDF5ExtError("Problems reading the array data.") # Terminate access to the memory dataspace H5Sclose(mem_space_id) # Terminate access to the dataspace H5Sclose(space_id) if self.atom.kind == 'time': # Swap the byteorder by hand (this is not currently supported by HDF5) if H5Tget_order(self.type_id) != platform_byteorder: nparr.byteswap(True) # Convert some HDF5 types to NumPy after reading if self.atom.type == 'time64': self._convert_time64(nparr, 1) return _g_readSelection = previous_api(_g_read_selection) def _g_write_slice(self, ndarray startl, ndarray stepl, ndarray countl, ndarray nparr): """Write a slice in an already created NumPy array.""" cdef int ret cdef void *rbuf cdef void *temp cdef hsize_t *start cdef hsize_t *step cdef hsize_t *count # Get the pointer to the buffer data area rbuf = nparr.data # Get the start, step and count values start = startl.data step = stepl.data count = countl.data # Convert some NumPy types to HDF5 before storing. if self.atom.type == 'time64': self._convert_time64(nparr, 0) # Modify the elements: with nogil: ret = H5ARRAYwrite_records(self.dataset_id, self.type_id, self.rank, start, step, count, rbuf) if ret < 0: raise HDF5ExtError("Internal error modifying the elements " "(H5ARRAYwrite_records returned errorcode -%i)" % (-ret)) return _g_writeSlice = previous_api(_g_write_slice) def _g_write_coords(self, ndarray coords, ndarray nparr): """Write a selection in an already created NumPy array.""" cdef herr_t ret cdef hid_t space_id cdef hid_t mem_space_id cdef hsize_t size cdef void *rbuf cdef object mode # Get the dataspace handle space_id = H5Dget_space(self.dataset_id) # Create a memory dataspace handle size = nparr.size mem_space_id = H5Screate_simple(1, &size, NULL) # Select the dataspace to be written H5Sselect_elements(space_id, H5S_SELECT_SET, size, coords.data) # Get the pointer to the buffer data area rbuf = nparr.data # Convert some NumPy types to HDF5 before storing. if self.atom.type == 'time64': self._convert_time64(nparr, 0) # Do the actual write with nogil: ret = H5Dwrite(self.dataset_id, self.type_id, mem_space_id, space_id, H5P_DEFAULT, rbuf) if ret < 0: raise HDF5ExtError("Problems writing the array data.") # Terminate access to the memory dataspace H5Sclose(mem_space_id) # Terminate access to the dataspace H5Sclose(space_id) return _g_writeCoords = previous_api(_g_write_coords) def _g_write_selection(self, object selection, ndarray nparr): """Write a selection in an already created NumPy array.""" cdef herr_t ret cdef hid_t space_id cdef hid_t mem_space_id cdef hsize_t size cdef void *rbuf cdef object mode # Get the dataspace handle space_id = H5Dget_space(self.dataset_id) # Create a memory dataspace handle size = nparr.size mem_space_id = H5Screate_simple(1, &size, NULL) # Select the dataspace to be written # Start by selecting everything H5Sselect_all(space_id) # Now refine with outstanding selections for args in selection: self.perform_selection(space_id, *args) # Get the pointer to the buffer data area rbuf = nparr.data # Convert some NumPy types to HDF5 before storing. if self.atom.type == 'time64': self._convert_time64(nparr, 0) # Do the actual write with nogil: ret = H5Dwrite(self.dataset_id, self.type_id, mem_space_id, space_id, H5P_DEFAULT, rbuf) if ret < 0: raise HDF5ExtError("Problems writing the array data.") # Terminate access to the memory dataspace H5Sclose(mem_space_id) # Terminate access to the dataspace H5Sclose(space_id) return _g_writeSelection = previous_api(_g_write_selection) def __dealloc__(self): if self.dims: free(self.dims) if self.maxdims: free(self.maxdims) if self.dims_chunk: free(self.dims_chunk) cdef class VLArray(Leaf): # Instance variables cdef hsize_t nrecords def _create_array(self, object title): cdef int rank cdef hsize_t *dims cdef herr_t ret cdef void *rbuf cdef bytes complib, version, class_ cdef object type_, itemsize, atom, scatom cdef bytes encoded_title, encoded_name cdef H5T_cset_t cset = H5T_CSET_ASCII encoded_title = title.encode('utf-8') encoded_name = self.name.encode('utf-8') atom = self.atom if not hasattr(atom, 'size'): # it is a pseudo-atom atom = atom.base # Get the HDF5 type of the *scalar* atom scatom = atom.copy(shape=()) self.base_type_id = atom_to_hdf5_type(scatom, self.byteorder) # Allocate space for the dimension axis info rank = len(atom.shape) dims = malloc_dims(atom.shape) rbuf = NULL # We don't have data to save initially # Encode strings complib = (self.filters.complib or '').encode('utf-8') version = self._v_version.encode('utf-8') class_ = self._c_classid.encode('utf-8') # Create the vlarray self.dataset_id = H5VLARRAYmake(self.parent_id, encoded_name, version, rank, dims, self.base_type_id, self.chunkshape[0], rbuf, self.filters.complevel, complib, self.filters.shuffle, self.filters.fletcher32, rbuf) if dims: free(dims) if self.dataset_id < 0: raise HDF5ExtError("Problems creating the VLArray.") self.nrecords = 0 # Initialize the number of records saved if self._v_file.params['PYTABLES_SYS_ATTRS']: if PY_MAJOR_VERSION > 2: cset = H5T_CSET_UTF8 # Set the conforming array attributes H5ATTRset_attribute_string(self.dataset_id, "CLASS", class_, len(class_), cset) H5ATTRset_attribute_string(self.dataset_id, "VERSION", version, len(version), cset) H5ATTRset_attribute_string(self.dataset_id, "TITLE", encoded_title, len(encoded_title), cset) # Get the datatype handles self.disk_type_id, self.type_id = self._get_type_ids() return self.dataset_id _createArray = previous_api(_create_array) def _open_array(self): cdef char cbyteorder[11] # "irrelevant" fits easily here cdef int i, enumtype cdef int rank cdef herr_t ret cdef hsize_t nrecords, chunksize cdef object shape, type_ cdef bytes encoded_name cdef str byteorder encoded_name = self.name.encode('utf-8') # Open the dataset self.dataset_id = H5Dopen(self.parent_id, encoded_name, H5P_DEFAULT) if self.dataset_id < 0: raise HDF5ExtError("Non-existing node ``%s`` under ``%s``" % (self.name, self._v_parent._v_pathname)) # Get the datatype handles self.disk_type_id, self.type_id = self._get_type_ids() # Get the atom for this type atom = atom_from_hdf5_type(self.type_id) # Get info on dimensions & types (of base class) H5VLARRAYget_info(self.dataset_id, self.disk_type_id, &nrecords, cbyteorder) byteorder = cstr_to_pystr(cbyteorder) # Get some properties of the atomic type self._atomicdtype = atom.dtype self._atomictype = atom.type self._atomicshape = atom.shape self._atomicsize = atom.size # Get the byteorder self.byteorder = correct_byteorder(atom.type, byteorder) # Get the chunkshape (VLArrays are unidimensional entities) H5ARRAYget_chunkshape(self.dataset_id, 1, &chunksize) self.nrecords = nrecords # Initialize the number of records saved return self.dataset_id, SizeType(nrecords), (SizeType(chunksize),), atom _openArray = previous_api(_open_array) def _append(self, ndarray nparr, int nobjects): cdef int ret cdef void *rbuf # Get the pointer to the buffer data area if nobjects: rbuf = nparr.data # Convert some NumPy types to HDF5 before storing. if self.atom.type == 'time64': self._convert_time64(nparr, 0) else: rbuf = NULL # Append the records: with nogil: ret = H5VLARRAYappend_records(self.dataset_id, self.type_id, nobjects, self.nrecords, rbuf) if ret < 0: raise HDF5ExtError("Problems appending the records.") self.nrecords = self.nrecords + 1 def _modify(self, hsize_t nrow, ndarray nparr, int nobjects): cdef int ret cdef void *rbuf # Get the pointer to the buffer data area rbuf = nparr.data if nobjects: # Convert some NumPy types to HDF5 before storing. if self.atom.type == 'time64': self._convert_time64(nparr, 0) # Append the records: with nogil: ret = H5VLARRAYmodify_records(self.dataset_id, self.type_id, nrow, nobjects, rbuf) if ret < 0: raise HDF5ExtError("Problems modifying the record.") return nobjects # Because the size of each "row" is unknown, there is no easy way to # calculate this value def _get_memory_size(self): cdef hid_t space_id cdef hsize_t size cdef herr_t ret if self.nrows == 0: size = 0 else: # Get the dataspace handle space_id = H5Dget_space(self.dataset_id) # Return the size of the entire dataset ret = H5Dvlen_get_buf_size(self.dataset_id, self.type_id, space_id, &size) if ret < 0: size = -1 # Terminate access to the dataspace H5Sclose(space_id) return size def _read_array(self, hsize_t start, hsize_t stop, hsize_t step): cdef int i cdef size_t vllen cdef herr_t ret cdef hvl_t *rdata cdef hsize_t nrows cdef hid_t space_id cdef hid_t mem_space_id cdef object buf, nparr, shape, datalist # Compute the number of rows to read nrows = get_len_of_range(start, stop, step) if start + nrows > self.nrows: raise HDF5ExtError( "Asking for a range of rows exceeding the available ones!.", h5bt=False) # Now, read the chunk of rows with nogil: # Allocate the necessary memory for keeping the row handlers rdata = malloc(nrows*sizeof(hvl_t)) # Get the dataspace handle space_id = H5Dget_space(self.dataset_id) # Create a memory dataspace handle mem_space_id = H5Screate_simple(1, &nrows, NULL) # Select the data to be read H5Sselect_hyperslab(space_id, H5S_SELECT_SET, &start, &step, &nrows, NULL) # Do the actual read ret = H5Dread(self.dataset_id, self.type_id, mem_space_id, space_id, H5P_DEFAULT, rdata) if ret < 0: raise HDF5ExtError( "VLArray._read_array: Problems reading the array data.") datalist = [] for i from 0 <= i < nrows: # Number of atoms in row vllen = rdata[i].len # Get the pointer to the buffer data area if vllen > 0: # Create a buffer to keep this info. It is important to do a # copy, because we will dispose the buffer memory later on by # calling the H5Dvlen_reclaim. PyBytes_FromStringAndSize does this. buf = PyBytes_FromStringAndSize(rdata[i].p, vllen*self._atomicsize) else: # Case where there is info with zero lentgh buf = None # Compute the shape for the read array shape = list(self._atomicshape) shape.insert(0, vllen) # put the length at the beginning of the shape nparr = numpy.ndarray( buffer=buf, dtype=self._atomicdtype.base, shape=shape) # Set the writeable flag for this ndarray object nparr.flags.writeable = True if self.atom.kind == 'time': # Swap the byteorder by hand (this is not currently supported by HDF5) if H5Tget_order(self.type_id) != platform_byteorder: nparr.byteswap(True) # Convert some HDF5 types to NumPy after reading. if self.atom.type == 'time64': self._convert_time64(nparr, 1) # Append this array to the output list datalist.append(nparr) # Release resources # Reclaim all the (nested) VL data ret = H5Dvlen_reclaim(self.type_id, mem_space_id, H5P_DEFAULT, rdata) if ret < 0: raise HDF5ExtError("VLArray._read_array: error freeing the data buffer.") # Terminate access to the memory dataspace H5Sclose(mem_space_id) # Terminate access to the dataspace H5Sclose(space_id) # Free the amount of row pointers to VL row data free(rdata) return datalist _readArray = previous_api(_read_array) def get_row_size(self, row): """Return the total size in bytes of all the elements contained in a given row.""" cdef hid_t space_id cdef hsize_t size cdef herr_t ret cdef hsize_t offset[1] cdef hsize_t count[1] if row >= self.nrows: raise HDF5ExtError( "Asking for a range of rows exceeding the available ones!.", h5bt=False) # Get the dataspace handle space_id = H5Dget_space(self.dataset_id) offset[0] = row count[0] = 1 ret = H5Sselect_hyperslab(space_id, H5S_SELECT_SET, offset, NULL, count, NULL); if ret < 0: size = -1 ret = H5Dvlen_get_buf_size(self.dataset_id, self.type_id, space_id, &size) if ret < 0: size = -1 # Terminate access to the dataspace H5Sclose(space_id) return size cdef class UnImplemented(Leaf): def _open_unimplemented(self): cdef object shape cdef char cbyteorder[11] # "irrelevant" fits easily here cdef bytes encoded_name cdef str byteorder encoded_name = self.name.encode('utf-8') # Get info on dimensions shape = H5UIget_info(self.parent_id, encoded_name, cbyteorder) shape = tuple(map(SizeType, shape)) self.dataset_id = H5Dopen(self.parent_id, encoded_name, H5P_DEFAULT) byteorder = cstr_to_pystr(cbyteorder) return (shape, byteorder, self.dataset_id) def _g_close(self): H5Dclose(self.dataset_id) ## Local Variables: ## mode: python ## py-indent-offset: 2 ## tab-width: 2 ## fill-column: 78 ## End: PyTables-v.3.1.1/tables/idxutils.py000066400000000000000000000406641231437614300172120ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: April 02, 2007 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Utilities to be used mainly by the Index class.""" import sys import math import numpy from tables._past import previous_api # Hints for chunk/slice/block/superblock computations: # - The slicesize should not exceed 2**32 elements (because of # implementation reasons). Such an extreme case would make the # sorting algorithms to consume up to 64 GB of memory. # - In general, one should favor a small chunksize ( < 128 KB) if one # wants to reduce the latency for indexed queries. However, keep in # mind that a very low value of chunksize for big datasets may hurt # the performance by requering the HDF5 to use a lot of memory and CPU # for its internal B-Tree. def csformula(nrows): """Return the fitted chunksize (a float value) for nrows.""" # This formula has been computed using two points: # 2**12 = m * 2**(n + log10(10**6)) # 2**15 = m * 2**(n + log10(10**9)) # where 2**12 and 2**15 are reasonable values for chunksizes for indexes # with 10**6 and 10**9 elements respectively. # Yes, return a floating point number! return 64 * 2**math.log10(nrows) def limit_er(expectedrows): """Protection against creating too small or too large chunks or slices.""" if expectedrows < 10**5: expectedrows = 10**5 elif expectedrows > 10**12: expectedrows = 10**12 return expectedrows def computechunksize(expectedrows): """Get the optimum chunksize based on expectedrows.""" expectedrows = limit_er(expectedrows) zone = int(math.log10(expectedrows)) nrows = 10**zone return int(csformula(nrows)) def computeslicesize(expectedrows, memlevel): """Get the optimum slicesize based on expectedrows and memorylevel.""" expectedrows = limit_er(expectedrows) # First, the optimum chunksize cs = csformula(expectedrows) # Now, the actual chunksize chunksize = computechunksize(expectedrows) # The optimal slicesize ss = int(cs * memlevel**2) # We *need* slicesize to be an exact multiple of the actual chunksize ss = (ss // chunksize) * chunksize ss *= 4 # slicesize should be at least divisible by 4 # ss cannot be bigger than 2**31 - 1 elements because of fundamental # reasons (this limitation comes mainly from the way of compute # indices for indexes, but also because C keysort is not implemented # yet for the string type). Besides, it cannot be larger than # 2**30, because limitiations of the optimized binary search code # (in idx-opt.c, the line ``mid = lo + (hi-lo)/2;`` will overflow # for values of ``lo`` and ``hi`` >= 2**30). Finally, ss must be a # multiple of 4, so 2**30 must definitely be an upper limit. if ss > 2**30: ss = 2**30 return ss def computeblocksize(expectedrows, compoundsize, lowercompoundsize): """Calculate the optimum number of superblocks made from compounds blocks. This is useful for computing the sizes of both blocks and superblocks (using the PyTables terminology for blocks in indexes). """ nlowerblocks = (expectedrows // lowercompoundsize) + 1 if nlowerblocks > 2**20: # Protection against too large number of compound blocks nlowerblocks = 2**20 size = lowercompoundsize * nlowerblocks # We *need* superblocksize to be an exact multiple of the actual # compoundblock size (a ceil must be performed here!) size = ((size // compoundsize) + 1) * compoundsize return size def calc_chunksize(expectedrows, optlevel=6, indsize=4, memlevel=4): """Calculate the HDF5 chunk size for index and sorted arrays. The logic to do that is based purely in experiments playing with different chunksizes and compression flag. It is obvious that using big chunks optimizes the I/O speed, but if they are too large, the uncompressor takes too much time. This might (should) be further optimized by doing more experiments. """ chunksize = computechunksize(expectedrows) slicesize = computeslicesize(expectedrows, memlevel) # Correct the slicesize and the chunksize based on optlevel if indsize == 1: # ultralight chunksize, slicesize = ccs_ultralight(optlevel, chunksize, slicesize) elif indsize == 2: # light chunksize, slicesize = ccs_light(optlevel, chunksize, slicesize) elif indsize == 4: # medium chunksize, slicesize = ccs_medium(optlevel, chunksize, slicesize) elif indsize == 8: # full chunksize, slicesize = ccs_full(optlevel, chunksize, slicesize) # Finally, compute blocksize and superblocksize blocksize = computeblocksize(expectedrows, slicesize, chunksize) superblocksize = computeblocksize(expectedrows, blocksize, slicesize) # The size for different blocks information sizes = (superblocksize, blocksize, slicesize, chunksize) return sizes calcChunksize = previous_api(calc_chunksize) def ccs_ultralight(optlevel, chunksize, slicesize): """Correct the slicesize and the chunksize based on optlevel.""" if optlevel in (0, 1, 2): slicesize //= 2 slicesize += optlevel * slicesize elif optlevel in (3, 4, 5): slicesize *= optlevel - 1 elif optlevel in (6, 7, 8): slicesize *= optlevel - 1 elif optlevel == 9: slicesize *= optlevel - 1 return chunksize, slicesize def ccs_light(optlevel, chunksize, slicesize): """Correct the slicesize and the chunksize based on optlevel.""" if optlevel in (0, 1, 2): slicesize //= 2 elif optlevel in (3, 4, 5): pass elif optlevel in (6, 7, 8): chunksize /= 2 elif optlevel == 9: # Reducing the chunksize and enlarging the slicesize is the # best way to reduce the entropy with the current algorithm. chunksize /= 2 slicesize *= 2 return chunksize, slicesize def ccs_medium(optlevel, chunksize, slicesize): """Correct the slicesize and the chunksize based on optlevel.""" if optlevel in (0, 1, 2): slicesize //= 2 elif optlevel in (3, 4, 5): pass elif optlevel in (6, 7, 8): chunksize //= 2 elif optlevel == 9: # Reducing the chunksize and enlarging the slicesize is the # best way to reduce the entropy with the current algorithm. chunksize //= 2 slicesize *= 2 return chunksize, slicesize def ccs_full(optlevel, chunksize, slicesize): """Correct the slicesize and the chunksize based on optlevel.""" if optlevel in (0, 1, 2): slicesize //= 2 elif optlevel in (3, 4, 5): pass elif optlevel in (6, 7, 8): chunksize //= 2 elif optlevel == 9: # Reducing the chunksize and enlarging the slicesize is the # best way to reduce the entropy with the current algorithm. chunksize //= 2 slicesize *= 2 return chunksize, slicesize def calcoptlevels(nblocks, optlevel, indsize): """Compute the optimizations to be done. The calculation is based on the number of blocks, optlevel and indexing mode. """ if indsize == 2: # light return col_light(nblocks, optlevel) elif indsize == 4: # medium return col_medium(nblocks, optlevel) elif indsize == 8: # full return col_full(nblocks, optlevel) def col_light(nblocks, optlevel): """Compute the optimizations to be done for light indexes.""" optmedian, optstarts, optstops, optfull = (False,) * 4 if 0 < optlevel <= 3: optmedian = True elif 3 < optlevel <= 6: optmedian, optstarts = (True, True) elif 6 < optlevel <= 9: optmedian, optstarts, optstops = (True, True, True) return optmedian, optstarts, optstops, optfull def col_medium(nblocks, optlevel): """Compute the optimizations to be done for medium indexes.""" optmedian, optstarts, optstops, optfull = (False,) * 4 # Medium case if nblocks <= 1: if 0 < optlevel <= 3: optmedian = True elif 3 < optlevel <= 6: optmedian, optstarts = (True, True) elif 6 < optlevel <= 9: optfull = 1 else: # More than a block if 0 < optlevel <= 3: optfull = 1 elif 3 < optlevel <= 6: optfull = 2 elif 6 < optlevel <= 9: optfull = 3 return optmedian, optstarts, optstops, optfull def col_full(nblocks, optlevel): """Compute the optimizations to be done for full indexes.""" optmedian, optstarts, optstops, optfull = (False,) * 4 # Full case if nblocks <= 1: if 0 < optlevel <= 3: optmedian = True elif 3 < optlevel <= 6: optmedian, optstarts = (True, True) elif 6 < optlevel <= 9: optfull = 1 else: # More than a block if 0 < optlevel <= 3: optfull = 1 elif 3 < optlevel <= 6: optfull = 2 elif 6 < optlevel <= 9: optfull = 3 return optmedian, optstarts, optstops, optfull def get_reduction_level(indsize, optlevel, slicesize, chunksize): """Compute the reduction level based on indsize and optlevel.""" rlevels = [ [8, 8, 8, 8, 4, 4, 4, 2, 2, 1], # 8-bit indices (ultralight) [4, 4, 4, 4, 2, 2, 2, 1, 1, 1], # 16-bit indices (light) [2, 2, 2, 2, 1, 1, 1, 1, 1, 1], # 32-bit indices (medium) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], # 64-bit indices (full) ] isizes = {1: 0, 2: 1, 4: 2, 8: 3} rlevel = rlevels[isizes[indsize]][optlevel] # The next cases should only happen in tests if rlevel >= slicesize: rlevel = 1 if slicesize <= chunksize * rlevel: rlevel = 1 if indsize == 8: # Ensure that, for full indexes we will never perform a reduction. # This is required because of implementation assumptions. assert rlevel == 1 return rlevel # Python implementations of NextAfter and NextAfterF # # These implementations exist because the standard function # nextafterf is not available on Microsoft platforms. # # These implementations are based on the IEEE representation of # floats and doubles. # Author: Shack Toms - shack@livedata.com # # Thanks to Shack Toms shack@livedata.com for NextAfter and NextAfterF # implementations in Python. 2004-10-01 # epsilon = math.ldexp(1.0, -53) # smallest double such that # # 0.5 + epsilon != 0.5 # epsilonF = math.ldexp(1.0, -24) # smallest float such that 0.5 + epsilonF # != 0.5 # maxFloat = float(2**1024 - 2**971) # From the IEEE 754 standard # maxFloatF = float(2**128 - 2**104) # From the IEEE 754 standard # minFloat = math.ldexp(1.0, -1022) # min positive normalized double # minFloatF = math.ldexp(1.0, -126) # min positive normalized float # smallEpsilon = math.ldexp(1.0, -1074) # smallest increment for # # doubles < minFloat # smallEpsilonF = math.ldexp(1.0, -149) # smallest increment for # # floats < minFloatF infinity = math.ldexp(1.0, 1023) * 2 infinityf = math.ldexp(1.0, 128) # Finf = float("inf") # Infinite in the IEEE 754 standard (not avail in Win) # A portable representation of NaN # if sys.byteorder == "little": # testNaN = struct.unpack("d", '\x01\x00\x00\x00\x00\x00\xf0\x7f')[0] # elif sys.byteorder == "big": # testNaN = struct.unpack("d", '\x7f\xf0\x00\x00\x00\x00\x00\x01')[0] # else: # raise ValueError("Byteorder '%s' not supported!" % sys.byteorder) # This one seems better # testNaN = infinity - infinity # "infinity" for several types infinitymap = { 'bool': [0, 1], 'int8': [-2**7, 2**7 - 1], 'uint8': [0, 2**8 - 1], 'int16': [-2**15, 2**15 - 1], 'uint16': [0, 2**16 - 1], 'int32': [-2**31, 2**31 - 1], 'uint32': [0, 2**32 - 1], 'int64': [-2**63, 2**63 - 1], 'uint64': [0, 2**64 - 1], 'float32': [-infinityf, infinityf], 'float64': [-infinity, infinity], } if hasattr(numpy, 'float16'): infinitymap['float16'] = [-numpy.float16(numpy.inf), numpy.float16(numpy.inf)] if hasattr(numpy, 'float96'): infinitymap['float96'] = [-numpy.float96(numpy.inf), numpy.float96(numpy.inf)] if hasattr(numpy, 'float128'): infinitymap['float128'] = [-numpy.float128(numpy.inf), numpy.float128(numpy.inf)] # deprecated API infinityMap = infinitymap infinityF = infinityf # Utility functions def inftype(dtype, itemsize, sign=+1): """Return a superior limit for maximum representable data type.""" assert sign in [-1, +1] if dtype.kind == "S": if sign < 0: return b"\x00" * itemsize else: return b"\xff" * itemsize try: return infinitymap[dtype.name][sign >= 0] except KeyError: raise TypeError("Type %s is not supported" % dtype.name) infType = previous_api(inftype) def string_next_after(x, direction, itemsize): """Return the next representable neighbor of x in the appropriate direction.""" assert direction in [-1, +1] # Pad the string with \x00 chars until itemsize completion padsize = itemsize - len(x) if padsize > 0: x += b"\x00" * padsize if sys.version_info[0] < 3: xlist = list(x) else: # int.to_bytes is not available in Python < 3.2 # xlist = [i.to_bytes(1, sys.byteorder) for i in x] xlist = [bytes([i]) for i in x] xlist.reverse() i = 0 if direction > 0: if xlist == b"\xff" * itemsize: # Maximum value, return this return b"".join(xlist) for xchar in xlist: if ord(xchar) < 0xff: xlist[i] = chr(ord(xchar) + 1).encode('ascii') break else: xlist[i] = b"\x00" i += 1 else: if xlist == b"\x00" * itemsize: # Minimum value, return this return b"".join(xlist) for xchar in xlist: if ord(xchar) > 0x00: xlist[i] = chr(ord(xchar) - 1).encode('ascii') break else: xlist[i] = b"\xff" i += 1 xlist.reverse() return b"".join(xlist) StringNextAfter = previous_api(string_next_after) def int_type_next_after(x, direction, itemsize): """Return the next representable neighbor of x in the appropriate direction.""" assert direction in [-1, +1] # x is guaranteed to be either an int or a float if direction < 0: if isinstance(x, int): return x - 1 else: # return int(PyNextAfter(x, x - 1)) return int(numpy.nextafter(x, x - 1)) else: if isinstance(x, int): return x + 1 else: # return int(PyNextAfter(x,x + 1)) + 1 return int(numpy.nextafter(x, x + 1)) + 1 IntTypeNextAfter = previous_api(int_type_next_after) def bool_type_next_after(x, direction, itemsize): """Return the next representable neighbor of x in the appropriate direction.""" assert direction in [-1, +1] # x is guaranteed to be either a boolean if direction < 0: return False else: return True BoolTypeNextAfter = previous_api(bool_type_next_after) def nextafter(x, direction, dtype, itemsize): """Return the next representable neighbor of x in the appropriate direction.""" assert direction in [-1, 0, +1] assert dtype.kind == "S" or type(x) in (bool, int, long, float) if direction == 0: return x if dtype.kind == "S": return string_next_after(x, direction, itemsize) if dtype.kind in ['b']: return bool_type_next_after(x, direction, itemsize) elif dtype.kind in ['i', 'u']: return int_type_next_after(x, direction, itemsize) elif dtype.kind == "f": if direction < 0: return numpy.nextafter(x, x - 1) else: return numpy.nextafter(x, x + 1) # elif dtype.name == "float32": # if direction < 0: # return PyNextAfterF(x,x-1) # else: # return PyNextAfterF(x,x + 1) # elif dtype.name == "float64": # if direction < 0: # return PyNextAfter(x,x-1) # else: # return PyNextAfter(x,x + 1) raise TypeError("data type ``%s`` is not supported" % dtype) ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/index.py000066400000000000000000002651701231437614300164550ustar00rootroot00000000000000# -*- coding: utf-8 -*- ####################################################################### # # License: BSD # Created: June 08, 2004 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Here is defined the Index class.""" from __future__ import print_function import sys from bisect import bisect_left, bisect_right from time import time, clock import os import os.path import tempfile import math import warnings import numpy from tables.idxutils import (calc_chunksize, calcoptlevels, get_reduction_level, nextafter, inftype) from tables import indexesextension from tables.node import NotLoggedMixin from tables.atom import UIntAtom, Atom from tables.earray import EArray from tables.carray import CArray from tables.leaf import Filters from tables.indexes import CacheArray, LastRowArray, IndexArray from tables.group import Group from tables.path import join_path from tables.exceptions import PerformanceWarning from tables.utils import is_idx, idx2long, lazyattr from tables.lrucacheextension import ObjectCache from tables._past import previous_api, previous_api_property # default version for INDEX objects # obversion = "1.0" # Version of indexes in PyTables 1.x series # obversion = "2.0" # Version of indexes in PyTables Pro 2.0 series obversion = "2.1" # Version of indexes in PyTables Pro 2.1 and up series, # including the join 2.3 Std + Pro version debug = False # debug = True # Uncomment this for printing sizes purposes profile = False # profile = True # Uncomment for profiling if profile: from tables.utils import show_stats # The default method for sorting defsort = "quicksort" # defsort = "mergesort" # Default policy for automatically updating indexes after a table # append operation, or automatically reindexing after an # index-invalidating operation like removing or modifying table rows. default_auto_index = True # Keep in sync with ``Table.autoindex`` docstring. # Default filters used to compress indexes. This is quite fast and # compression is pretty good. # Remember to keep these defaults in sync with the docstrings and UG. default_index_filters = Filters(complevel=1, complib='zlib', shuffle=True, fletcher32=False) # Deprecated API defaultAutoIndex = default_auto_index defaultIndexFilters = default_index_filters # The list of types for which an optimised search in cython and C has # been implemented. Always add here the name of a new optimised type. opt_search_types = ("int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float32", "float64") # The upper limit for uint32 ints max32 = 2**32 def _table_column_pathname_of_index(indexpathname): names = indexpathname.split("/") for i, name in enumerate(names): if name.startswith('_i_'): break tablepathname = "/".join(names[:i]) + "/" + name[3:] colpathname = "/".join(names[i + 1:]) return (tablepathname, colpathname) _tableColumnPathnameOfIndex = previous_api(_table_column_pathname_of_index) class Index(NotLoggedMixin, indexesextension.Index, Group): """Represents the index of a column in a table. This class is used to keep the indexing information for columns in a Table dataset (see :ref:`TableClassDescr`). It is actually a descendant of the Group class (see :ref:`GroupClassDescr`), with some added functionality. An Index is always associated with one and only one column in the table. .. note:: This class is mainly intended for internal use, but some of its documented attributes and methods may be interesting for the programmer. Parameters ---------- parentnode The parent :class:`Group` object. .. versionchanged:: 3.0 Renamed from *parentNode* to *parentnode*. name : str The name of this node in its parent group. atom : Atom An Atom object representing the shape and type of the atomic objects to be saved. Only scalar atoms are supported. title Sets a TITLE attribute of the Index entity. kind The desired kind for this index. The 'full' kind specifies a complete track of the row position (64-bit), while the 'medium', 'light' or 'ultralight' kinds only specify in which chunk the row is (using 32-bit, 16-bit and 8-bit respectively). optlevel The desired optimization level for this index. filters : Filters An instance of the Filters class that provides information about the desired I/O filters to be applied during the life of this object. tmp_dir The directory for the temporary files. expectedrows Represents an user estimate about the number of row slices that will be added to the growable dimension in the IndexArray object. byteorder The byteorder of the index datasets *on-disk*. blocksizes The four main sizes of the compound blocks in index datasets (a low level parameter). """ _c_classid = 'INDEX' _c_classId = previous_api_property('_c_classid') # kind = property( lambda self: {1: 'ultralight', 2: 'light', 4: 'medium', 8: 'full'}[self.indsize], None, None, "The kind of this index.") filters = property( lambda self: self._v_filters, None, None, """Filter properties for this index - see Filters in :ref:`FiltersClassDescr`.""") def _getdirty(self): if 'DIRTY' not in self._v_attrs: # If there is no ``DIRTY`` attribute, index should be clean. return False return self._v_attrs.DIRTY def _setdirty(self, dirty): wasdirty, isdirty = self.dirty, bool(dirty) self._v_attrs.DIRTY = dirty # If an *actual* change in dirtiness happens, # notify the condition cache by setting or removing a nail. conditioncache = self.table._condition_cache if not wasdirty and isdirty: conditioncache.nail() if wasdirty and not isdirty: conditioncache.unnail() dirty = property( _getdirty, _setdirty, None, """Whether the index is dirty or not. Dirty indexes are out of sync with column data, so they exist but they are not usable. """) def _getcolumn(self): tablepath, columnpath = _table_column_pathname_of_index( self._v_pathname) table = self._v_file._get_node(tablepath) column = table.cols._g_col(columnpath) return column column = property(_getcolumn, None, None, """The Column (see :ref:`ColumnClassDescr`) instance for the indexed column.""") def _gettable(self): tablepath, columnpath = _table_column_pathname_of_index( self._v_pathname) table = self._v_file._get_node(tablepath) return table table = property(_gettable, None, None, "Accessor for the `Table` object of this index.") nblockssuperblock = property( lambda self: self.superblocksize // self.blocksize, None, None, "The number of blocks in a superblock.") nslicesblock = property( lambda self: self.blocksize // self.slicesize, None, None, "The number of slices in a block.") nchunkslice = property( lambda self: self.slicesize // self.chunksize, None, None, "The number of chunks in a slice.") def _g_nsuperblocks(self): # Last row should not be considered as a superblock nelements = self.nelements - self.nelementsILR nblocks = nelements // self.superblocksize if nelements % self.blocksize > 0: nblocks += 1 return nblocks nsuperblocks = property(_g_nsuperblocks, None, None, "The total number of superblocks in index.") def _g_nblocks(self): # Last row should not be considered as a block nelements = self.nelements - self.nelementsILR nblocks = nelements // self.blocksize if nelements % self.blocksize > 0: nblocks += 1 return nblocks nblocks = property(_g_nblocks, None, None, "The total number of blocks in index.") nslices = property( lambda self: self.nelements // self.slicesize, None, None, "The number of complete slices in index.") nchunks = property( lambda self: self.nelements // self.chunksize, None, None, "The number of complete chunks in index.") shape = property( lambda self: (self.nrows, self.slicesize), None, None, "The shape of this index (in slices and elements).") temp_required = property( lambda self: (self.indsize > 1 and self.optlevel > 0 and self.table.nrows > self.slicesize), None, None, "Whether a temporary file for indexes is required or not.") want_complete_sort = property( lambda self: (self.indsize == 8 and self.optlevel == 9), None, None, "Whether we should try to build a completely sorted index or not.") def _is_csi(self): if self.nelements == 0: # An index with 0 indexed elements is not a CSI one (by definition) return False if self.indsize < 8: # An index that is not full cannot be completely sorted return False # Try with the 'is_csi' attribute if 'is_csi' in self._v_attrs: return self._v_attrs.is_csi # If not, then compute the overlaps manually # (the attribute 'is_csi' will be set there) self.compute_overlaps(self, None, False) return self.noverlaps == 0 _is_CSI = previous_api(_is_csi) is_csi = property(_is_csi, None, None, """Whether the index is completely sorted or not. .. versionchanged:: 3.0 The *is_CSI* property has been renamed into *is_csi*. """) is_CSI = previous_api(is_csi) @lazyattr def nrowsinchunk(self): """The number of rows that fits in a *table* chunk.""" return self.table.chunkshape[0] @lazyattr def lbucket(self): """Return the length of a bucket based index type.""" # Avoid to set a too large lbucket size (mainly useful for tests) lbucket = min(self.nrowsinchunk, self.chunksize) if self.indsize == 1: # For ultra-light, we will never have to keep track of a # bucket outside of a slice. maxnb = 2**8 if self.slicesize > maxnb * lbucket: lbucket = int(math.ceil(float(self.slicesize) / maxnb)) elif self.indsize == 2: # For light, we will never have to keep track of a # bucket outside of a block. maxnb = 2**16 if self.blocksize > maxnb * lbucket: lbucket = int(math.ceil(float(self.blocksize) / maxnb)) else: # For medium and full indexes there should not be a need to # increase lbucket pass return lbucket # def __init__(self, parentnode, name, atom=None, title="", kind=None, optlevel=None, filters=None, tmp_dir=None, expectedrows=0, byteorder=None, blocksizes=None, new=True): self._v_version = None """The object version of this index.""" self.optlevel = optlevel """The optimization level for this index.""" self.tmp_dir = tmp_dir """The directory for the temporary files.""" self.expectedrows = expectedrows """The expected number of items of index arrays.""" if byteorder in ["little", "big"]: self.byteorder = byteorder else: self.byteorder = sys.byteorder """The byteorder of the index datasets.""" if atom is not None: self.dtype = atom.dtype.base self.type = atom.type """The datatypes to be stored by the sorted index array.""" ############### Important note ########################### # The datatypes saved as index values are NumPy native # types, so we get rid of type metainfo like Time* or Enum* # that belongs to HDF5 types (actually, this metainfo is # not needed for sorting and looking-up purposes). ########################################################## indsize = { 'ultralight': 1, 'light': 2, 'medium': 4, 'full': 8}[kind] assert indsize in (1, 2, 4, 8), "indsize should be 1, 2, 4 or 8!" self.indsize = indsize """The itemsize for the indices part of the index.""" self.nrows = None """The total number of slices in the index.""" self.nelements = None """The number of currently indexed rows for this column.""" self.blocksizes = blocksizes """The four main sizes of the compound blocks (if specified).""" self.dirtycache = True """Dirty cache (for ranges, bounds & sorted) flag.""" self.superblocksize = None """Size of the superblock for this index.""" self.blocksize = None """Size of the block for this index.""" self.slicesize = None """Size of the slice for this index.""" self.chunksize = None """Size of the chunk for this index.""" self.tmpfilename = None """Filename for temporary bounds.""" self.opt_search_types = opt_search_types """The types for which and optimized search has been implemented.""" self.noverlaps = -1 """The number of overlaps in an index. 0 means a completely sorted index. -1 means that this number is not computed yet.""" self.tprof = 0 """Time counter for benchmarking purposes.""" from tables.file import open_file self._openFile = open_file """The `open_file()` function, to avoid a circular import.""" super(Index, self).__init__(parentnode, name, title, new, filters) def _g_post_init_hook(self): if self._v_new: # The version for newly created indexes self._v_version = obversion super(Index, self)._g_post_init_hook() # Index arrays must only be created for new indexes if not self._v_new: idxversion = self._v_version # Set-up some variables from info on disk and return attrs = self._v_attrs # Coerce NumPy scalars to Python scalars in order # to avoid undesired upcasting operations. self.superblocksize = long(attrs.superblocksize) self.blocksize = long(attrs.blocksize) self.slicesize = int(attrs.slicesize) self.chunksize = int(attrs.chunksize) self.blocksizes = (self.superblocksize, self.blocksize, self.slicesize, self.chunksize) self.optlevel = int(attrs.optlevel) sorted = self.sorted indices = self.indices self.dtype = sorted.atom.dtype self.type = sorted.atom.type self.indsize = indices.atom.itemsize # Some sanity checks for slicesize, chunksize and indsize assert self.slicesize == indices.shape[1], "Wrong slicesize" assert self.chunksize == indices._v_chunkshape[ 1], "Wrong chunksize" assert self.indsize in (1, 2, 4, 8), "Wrong indices itemsize" if idxversion > "2.0": self.reduction = int(attrs.reduction) nelementsSLR = int(self.sortedLR.attrs.nelements) nelementsILR = int(self.indicesLR.attrs.nelements) else: self.reduction = 1 nelementsILR = self.indicesLR[-1] nelementsSLR = nelementsILR self.nrows = sorted.nrows self.nelements = self.nrows * self.slicesize + nelementsILR self.nelementsSLR = nelementsSLR self.nelementsILR = nelementsILR if nelementsILR > 0: self.nrows += 1 # Get the bounds as a cache (this has to remain here!) rchunksize = self.chunksize // self.reduction nboundsLR = (nelementsSLR - 1) // rchunksize if nboundsLR < 0: nboundsLR = 0 # correction for -1 bounds nboundsLR += 2 # bounds + begin + end # All bounds values (+begin + end) are at the end of sortedLR self.bebounds = self.sortedLR[ nelementsSLR:nelementsSLR + nboundsLR] return # The index is new. Initialize the values self.nrows = 0 self.nelements = 0 self.nelementsSLR = 0 self.nelementsILR = 0 # The atom atom = Atom.from_dtype(self.dtype) # The filters filters = self.filters # Compute the superblocksize, blocksize, slicesize and chunksize values # (in case these parameters haven't been passed to the constructor) if self.blocksizes is None: self.blocksizes = calc_chunksize( self.expectedrows, self.optlevel, self.indsize) (self.superblocksize, self.blocksize, self.slicesize, self.chunksize) = self.blocksizes if debug: print("blocksizes:", self.blocksizes) # Compute the reduction level self.reduction = get_reduction_level( self.indsize, self.optlevel, self.slicesize, self.chunksize) rchunksize = self.chunksize // self.reduction rslicesize = self.slicesize // self.reduction # Save them on disk as attributes self._v_attrs.superblocksize = numpy.uint64(self.superblocksize) self._v_attrs.blocksize = numpy.uint64(self.blocksize) self._v_attrs.slicesize = numpy.uint32(self.slicesize) self._v_attrs.chunksize = numpy.uint32(self.chunksize) # Save the optlevel as well self._v_attrs.optlevel = self.optlevel # Save the reduction level self._v_attrs.reduction = self.reduction # Create the IndexArray for sorted values sorted = IndexArray(self, 'sorted', atom, "Sorted Values", filters, self.byteorder) # Create the IndexArray for index values IndexArray(self, 'indices', UIntAtom(itemsize=self.indsize), "Number of chunk in table", filters, self.byteorder) # Create the cache for range values (1st order cache) CacheArray(self, 'ranges', atom, (0, 2), "Range Values", filters, self.expectedrows // self.slicesize, byteorder=self.byteorder) # median ranges EArray(self, 'mranges', atom, (0,), "Median ranges", filters, byteorder=self.byteorder, _log=False) # Create the cache for boundary values (2nd order cache) nbounds_inslice = (rslicesize - 1) // rchunksize CacheArray(self, 'bounds', atom, (0, nbounds_inslice), "Boundary Values", filters, self.nchunks, (1, nbounds_inslice), byteorder=self.byteorder) # begin, end & median bounds (only for numerical types) EArray(self, 'abounds', atom, (0,), "Start bounds", filters, byteorder=self.byteorder, _log=False) EArray(self, 'zbounds', atom, (0,), "End bounds", filters, byteorder=self.byteorder, _log=False) EArray(self, 'mbounds', atom, (0,), "Median bounds", filters, byteorder=self.byteorder, _log=False) # Create the Array for last (sorted) row values + bounds shape = (rslicesize + 2 + nbounds_inslice,) sortedLR = LastRowArray(self, 'sortedLR', atom, shape, "Last Row sorted values + bounds", filters, (rchunksize,), byteorder=self.byteorder) # Create the Array for the number of chunk in last row shape = (self.slicesize,) # enough for indexes and length indicesLR = LastRowArray(self, 'indicesLR', UIntAtom(itemsize=self.indsize), shape, "Last Row indices", filters, (self.chunksize,), byteorder=self.byteorder) # The number of elements in LR will be initialized here sortedLR.attrs.nelements = 0 indicesLR.attrs.nelements = 0 # All bounds values (+begin + end) are uninitialized in creation time self.bebounds = None # The starts and lengths initialization self.starts = numpy.empty(shape=self.nrows, dtype=numpy.int32) """Where the values fulfiling conditions starts for every slice.""" self.lengths = numpy.empty(shape=self.nrows, dtype=numpy.int32) """Lengths of the values fulfilling conditions for every slice.""" # Finally, create a temporary file for indexes if needed if self.temp_required: self.create_temp() _g_postInitHook = previous_api(_g_post_init_hook) def initial_append(self, xarr, nrow, reduction): """Compute an initial indices arrays for data to be indexed.""" if profile: tref = time() if profile: show_stats("Entering initial_append", tref) arr = xarr.pop() indsize = self.indsize slicesize = self.slicesize nelementsILR = self.nelementsILR if profile: show_stats("Before creating idx", tref) if indsize == 8: idx = numpy.arange(0, len(arr), dtype="uint64") + nrow * slicesize elif indsize == 4: # For medium (32-bit) all the rows in tables should be # directly reachable. But as len(arr) < 2**31, we can # choose uint32 for representing indices. In this way, we # consume far less memory during the keysort process. The # offset will be added in self.final_idx32() later on. # # This optimization also prevents the values in LR to # participate in the ``swap_chunks`` process, and this is # the main reason to not allow the medium indexes to create # completely sorted indexes. However, I don't find this to # be a big limitation, as probably fully indexes are much # more suitable for producing completely sorted indexes # because in this case the indices part is usable for # getting the reverse indices of the index, and I forsee # this to be a common requirement in many operations (for # example, in table sorts). # # F. Alted 2008-09-15 idx = numpy.arange(0, len(arr), dtype="uint32") else: idx = numpy.empty(len(arr), "uint%d" % (indsize * 8)) lbucket = self.lbucket # Fill the idx with the bucket indices offset = lbucket - ((nrow * (slicesize % lbucket)) % lbucket) idx[0:offset] = 0 for i in xrange(offset, slicesize, lbucket): idx[i:i + lbucket] = (i + lbucket - 1) // lbucket if indsize == 2: # Add a second offset in this case # First normalize the number of rows offset2 = (nrow % self.nslicesblock) * slicesize // lbucket idx += offset2 # Add the last row at the beginning of arr & idx (if needed) if (indsize == 8 and nelementsILR > 0): # It is possible that the values in LR are already sorted. # Fetch them and override existing values in arr and idx. assert len(arr) > nelementsILR self.read_slice_lr(self.sortedLR, arr[:nelementsILR]) self.read_slice_lr(self.indicesLR, idx[:nelementsILR]) # In-place sorting if profile: show_stats("Before keysort", tref) indexesextension.keysort(arr, idx) larr = arr[-1] if reduction > 1: # It's important to do a copy() here in order to ensure that # sorted._append() will receive a contiguous array. if profile: show_stats("Before reduction", tref) reduc = arr[::reduction].copy() if profile: show_stats("After reduction", tref) arr = reduc if profile: show_stats("After arr <-- reduc", tref) # A completely sorted index is not longer possible after an # append of an index with already one slice. if nrow > 0: self._v_attrs.is_csi = False if profile: show_stats("Exiting initial_append", tref) return larr, arr, idx def final_idx32(self, idx, offset): """Perform final operations in 32-bit indices.""" if profile: tref = time() if profile: show_stats("Entering final_idx32", tref) # Do an upcast first in order to add the offset. idx = idx.astype('uint64') idx += offset # The next partition is valid up to table sizes of # 2**30 * 2**18 = 2**48 bytes, that is, 256 Tera-elements, # which should be a safe figure, at least for a while. idx //= self.lbucket # After the division, we can downsize the indexes to 'uint32' idx = idx.astype('uint32') if profile: show_stats("Exiting final_idx32", tref) return idx def append(self, xarr, update=False): """Append the array to the index objects.""" if profile: tref = time() if profile: show_stats("Entering append", tref) if not update and self.temp_required: where = self.tmp # The reduction will take place *after* the optimization process reduction = 1 else: where = self reduction = self.reduction sorted = where.sorted indices = where.indices ranges = where.ranges mranges = where.mranges bounds = where.bounds mbounds = where.mbounds abounds = where.abounds zbounds = where.zbounds sortedLR = where.sortedLR indicesLR = where.indicesLR nrows = sorted.nrows # before sorted.append() larr, arr, idx = self.initial_append(xarr, nrows, reduction) # Save the sorted array sorted.append(arr.reshape(1, arr.size)) cs = self.chunksize // reduction ncs = self.nchunkslice # Save ranges & bounds ranges.append([[arr[0], larr]]) bounds.append([arr[cs::cs]]) abounds.append(arr[0::cs]) zbounds.append(arr[cs - 1::cs]) # Compute the medians smedian = arr[cs // 2::cs] mbounds.append(smedian) mranges.append([smedian[ncs // 2]]) if profile: show_stats("Before deleting arr & smedian", tref) del arr, smedian # delete references if profile: show_stats("After deleting arr & smedian", tref) # Now that arr is gone, we can upcast the indices and add the offset if self.indsize == 4: idx = self.final_idx32(idx, nrows * self.slicesize) indices.append(idx.reshape(1, idx.size)) if profile: show_stats("Before deleting idx", tref) del idx # Update counters after a successful append self.nrows = nrows + 1 self.nelements = self.nrows * self.slicesize self.nelementsSLR = 0 # reset the counter of the last row index to 0 self.nelementsILR = 0 # reset the counter of the last row index to 0 # The number of elements will be saved as an attribute. # This is necessary in case the LR arrays can remember its values # after a possible node preemtion/reload. sortedLR.attrs.nelements = self.nelementsSLR indicesLR.attrs.nelements = self.nelementsILR self.dirtycache = True # the cache is dirty now if profile: show_stats("Exiting append", tref) def append_last_row(self, xarr, update=False): """Append the array to the last row index objects.""" if profile: tref = time() if profile: show_stats("Entering appendLR", tref) # compute the elements in the last row sorted & bounds array nrows = self.nslices if not update and self.temp_required: where = self.tmp # The reduction will take place *after* the optimization process reduction = 1 else: where = self reduction = self.reduction indicesLR = where.indicesLR sortedLR = where.sortedLR larr, arr, idx = self.initial_append(xarr, nrows, reduction) nelementsSLR = len(arr) nelementsILR = len(idx) # Build the cache of bounds rchunksize = self.chunksize // reduction self.bebounds = numpy.concatenate((arr[::rchunksize], [larr])) # The number of elements will be saved as an attribute sortedLR.attrs.nelements = nelementsSLR indicesLR.attrs.nelements = nelementsILR # Save the number of elements, bounds and sorted values # at the end of the sorted array offset2 = len(self.bebounds) sortedLR[nelementsSLR:nelementsSLR + offset2] = self.bebounds sortedLR[:nelementsSLR] = arr del arr # Now that arr is gone, we can upcast the indices and add the offset if self.indsize == 4: idx = self.final_idx32(idx, nrows * self.slicesize) # Save the reverse index array indicesLR[:len(idx)] = idx del idx # Update counters after a successful append self.nrows = nrows + 1 self.nelements = nrows * self.slicesize + nelementsILR self.nelementsILR = nelementsILR self.nelementsSLR = nelementsSLR self.dirtycache = True # the cache is dirty now if profile: show_stats("Exiting appendLR", tref) appendLastRow = previous_api(append_last_row) def optimize(self, verbose=False): """Optimize an index so as to allow faster searches. verbose If True, messages about the progress of the optimization process are printed out. """ if not self.temp_required: return if verbose: self.verbose = True else: self.verbose = debug # Initialize last_tover and last_nover self.last_tover = 0 self.last_nover = 0 # Compute the correct optimizations for current optim level opts = calcoptlevels(self.nblocks, self.optlevel, self.indsize) optmedian, optstarts, optstops, optfull = opts if debug: print("optvalues:", opts) self.create_temp2() # Start the optimization process while True: if optfull: for niter in range(optfull): if self.swap('chunks', 'median'): break if self.nblocks > 1: # Swap slices only in the case that we have # several blocks if self.swap('slices', 'median'): break if self.swap('chunks', 'median'): break if self.swap('chunks', 'start'): break if self.swap('chunks', 'stop'): break else: if optmedian: if self.swap('chunks', 'median'): break if optstarts: if self.swap('chunks', 'start'): break if optstops: if self.swap('chunks', 'stop'): break break # If we reach this, exit the loop # Check if we require a complete sort. Important: this step # should be carried out *after* the optimization process has # been completed (this is to guarantee that the complete sort # does not take too much memory). if self.want_complete_sort: if self.noverlaps > 0: self.do_complete_sort() # Check that we have effectively achieved the complete sort if self.noverlaps > 0: warnings.warn( "OPSI was not able to achieve a completely sorted index." " Please report this to the authors.", UserWarning) # Close and delete the temporal optimization index file self.cleanup_temp() return def do_complete_sort(self): """Bring an already optimized index into a complete sorted state.""" if self.verbose: t1 = time() c1 = clock() ss = self.slicesize tmp = self.tmp ranges = tmp.ranges[:] nslices = self.nslices nelementsLR = self.nelementsILR if nelementsLR > 0: # Add the ranges corresponding to the last row rangeslr = numpy.array([self.bebounds[0], self.bebounds[-1]]) ranges = numpy.concatenate((ranges, [rangeslr])) nslices += 1 sorted = tmp.sorted indices = tmp.indices sortedLR = tmp.sortedLR indicesLR = tmp.indicesLR sremain = numpy.array([], dtype=self.dtype) iremain = numpy.array([], dtype='u%d' % self.indsize) starts = numpy.zeros(shape=nslices, dtype=numpy.int_) for i in xrange(nslices): # Find the overlapping elements for slice i sover = numpy.array([], dtype=self.dtype) iover = numpy.array([], dtype='u%d' % self.indsize) prev_end = ranges[i, 1] for j in xrange(i + 1, nslices): stj = starts[j] if ((j < self.nslices and stj == ss) or (j == self.nslices and stj == nelementsLR)): # This slice has been already dealt with continue if j < self.nslices: assert stj < ss, \ "Two slices cannot overlap completely at this stage!" next_beg = sorted[j, stj] else: assert stj < nelementsLR, \ "Two slices cannot overlap completely at this stage!" next_beg = sortedLR[stj] next_end = ranges[j, 1] if prev_end > next_end: # Complete overlapping case if j < self.nslices: sover = numpy.concatenate((sover, sorted[j, stj:])) iover = numpy.concatenate((iover, indices[j, stj:])) starts[j] = ss else: n = nelementsLR sover = numpy.concatenate((sover, sortedLR[stj:n])) iover = numpy.concatenate((iover, indicesLR[stj:n])) starts[j] = nelementsLR elif prev_end > next_beg: idx = self.search_item_lt(tmp, prev_end, j, ranges[j], stj) if j < self.nslices: sover = numpy.concatenate((sover, sorted[j, stj:idx])) iover = numpy.concatenate((iover, indices[j, stj:idx])) else: sover = numpy.concatenate((sover, sortedLR[stj:idx])) iover = numpy.concatenate((iover, indicesLR[stj:idx])) starts[j] = idx # Build the extended slices to sort out if i < self.nslices: ssorted = numpy.concatenate( (sremain, sorted[i, starts[i]:], sover)) sindices = numpy.concatenate( (iremain, indices[i, starts[i]:], iover)) else: ssorted = numpy.concatenate( (sremain, sortedLR[starts[i]:nelementsLR], sover)) sindices = numpy.concatenate( (iremain, indicesLR[starts[i]:nelementsLR], iover)) # Sort the extended slices indexesextension.keysort(ssorted, sindices) # Save the first elements of extended slices in the slice i if i < self.nslices: sorted[i] = ssorted[:ss] indices[i] = sindices[:ss] # Update caches for this slice self.update_caches(i, ssorted[:ss]) # Save the remaining values in a separate array send = len(sover) + len(sremain) sremain = ssorted[ss:ss + send] iremain = sindices[ss:ss + send] else: # Still some elements remain for the last row n = len(ssorted) assert n == nelementsLR send = 0 sortedLR[:n] = ssorted indicesLR[:n] = sindices # Update the caches for last row sortedlr = sortedLR[:nelementsLR] bebounds = numpy.concatenate( (sortedlr[::self.chunksize], [sortedlr[-1]])) sortedLR[nelementsLR:nelementsLR + len(bebounds)] = bebounds self.bebounds = bebounds # Verify that we have dealt with all the remaining values assert send == 0 # Compute the overlaps in order to verify that we have achieved # a complete sort. This has to be executed always (and not only # in verbose mode!). self.compute_overlaps(self.tmp, "do_complete_sort()", self.verbose) if self.verbose: t = round(time() - t1, 4) c = round(clock() - c1, 4) print("time: %s. clock: %s" % (t, c)) def swap(self, what, mode=None): """Swap chunks or slices using a certain bounds reference.""" # Thresholds for avoiding continuing the optimization # thnover = 4 * self.slicesize # minimum number of overlapping # # elements thnover = 40 thmult = 0.1 # minimum ratio of multiplicity (a 10%) thtover = 0.01 # minimum overlaping index for slices (a 1%) if self.verbose: t1 = time() c1 = clock() if what == "chunks": self.swap_chunks(mode) elif what == "slices": self.swap_slices(mode) if mode: message = "swap_%s(%s)" % (what, mode) else: message = "swap_%s" % (what,) (nover, mult, tover) = self.compute_overlaps( self.tmp, message, self.verbose) rmult = len(mult.nonzero()[0]) / float(len(mult)) if self.verbose: t = round(time() - t1, 4) c = round(clock() - c1, 4) print("time: %s. clock: %s" % (t, c)) # Check that entropy is actually decreasing if what == "chunks" and self.last_tover > 0. and self.last_nover > 0: tover_var = (self.last_tover - tover) / self.last_tover nover_var = (self.last_nover - nover) / self.last_nover if tover_var < 0.05 and nover_var < 0.05: # Less than a 5% of improvement is too few return True self.last_tover = tover self.last_nover = nover # Check if some threshold has met if nover < thnover: return True if rmult < thmult: return True # Additional check for the overlap ratio if tover >= 0. and tover < thtover: return True return False def create_temp(self): """Create some temporary objects for slice sorting purposes.""" # The index will be dirty during the index optimization process self.dirty = True # Build the name of the temporary file fd, self.tmpfilename = tempfile.mkstemp( ".tmp", "pytables-", self.tmp_dir) # Close the file descriptor so as to avoid leaks os.close(fd) # Create the proper PyTables file self.tmpfile = self._openFile(self.tmpfilename, "w") self.tmp = tmp = self.tmpfile.root cs = self.chunksize ss = self.slicesize filters = self.filters # temporary sorted & indices arrays shape = (0, ss) atom = Atom.from_dtype(self.dtype) EArray(tmp, 'sorted', atom, shape, "Temporary sorted", filters, chunkshape=(1, cs)) EArray(tmp, 'indices', UIntAtom(itemsize=self.indsize), shape, "Temporary indices", filters, chunkshape=(1, cs)) # temporary bounds nbounds_inslice = (ss - 1) // cs shape = (0, nbounds_inslice) EArray(tmp, 'bounds', atom, shape, "Temp chunk bounds", filters, chunkshape=(cs, nbounds_inslice)) shape = (0,) EArray(tmp, 'abounds', atom, shape, "Temp start bounds", filters, chunkshape=(cs,)) EArray(tmp, 'zbounds', atom, shape, "Temp end bounds", filters, chunkshape=(cs,)) EArray(tmp, 'mbounds', atom, shape, "Median bounds", filters, chunkshape=(cs,)) # temporary ranges EArray(tmp, 'ranges', atom, (0, 2), "Temporary range values", filters, chunkshape=(cs, 2)) EArray(tmp, 'mranges', atom, (0,), "Median ranges", filters, chunkshape=(cs,)) # temporary last row (sorted) shape = (ss + 2 + nbounds_inslice,) CArray(tmp, 'sortedLR', atom, shape, "Temp Last Row sorted values + bounds", filters, chunkshape=(cs,)) # temporary last row (indices) shape = (ss,) CArray(tmp, 'indicesLR', UIntAtom(itemsize=self.indsize), shape, "Temp Last Row indices", filters, chunkshape=(cs,)) def create_temp2(self): """Create some temporary objects for slice sorting purposes.""" # The algorithms for doing the swap can be optimized so that # one should be necessary to create temporaries for keeping just # the contents of a single superblock. # F. Alted 2007-01-03 cs = self.chunksize ss = self.slicesize filters = self.filters # temporary sorted & indices arrays shape = (self.nslices, ss) atom = Atom.from_dtype(self.dtype) tmp = self.tmp CArray(tmp, 'sorted2', atom, shape, "Temporary sorted 2", filters, chunkshape=(1, cs)) CArray(tmp, 'indices2', UIntAtom(itemsize=self.indsize), shape, "Temporary indices 2", filters, chunkshape=(1, cs)) # temporary bounds nbounds_inslice = (ss - 1) // cs shape = (self.nslices, nbounds_inslice) CArray(tmp, 'bounds2', atom, shape, "Temp chunk bounds 2", filters, chunkshape=(cs, nbounds_inslice)) shape = (self.nchunks,) CArray(tmp, 'abounds2', atom, shape, "Temp start bounds 2", filters, chunkshape=(cs,)) CArray(tmp, 'zbounds2', atom, shape, "Temp end bounds 2", filters, chunkshape=(cs,)) CArray(tmp, 'mbounds2', atom, shape, "Median bounds 2", filters, chunkshape=(cs,)) # temporary ranges CArray(tmp, 'ranges2', atom, (self.nslices, 2), "Temporary range values 2", filters, chunkshape=(cs, 2)) CArray(tmp, 'mranges2', atom, (self.nslices,), "Median ranges 2", filters, chunkshape=(cs,)) def cleanup_temp(self): """Copy the data and delete the temporaries for sorting purposes.""" if self.verbose: print("Copying temporary data...") # tmp -> index reduction = self.reduction cs = self.chunksize // reduction ncs = self.nchunkslice tmp = self.tmp for i in xrange(self.nslices): # Copy sorted & indices slices sorted = tmp.sorted[i][::reduction].copy() self.sorted.append(sorted.reshape(1, sorted.size)) # Compute ranges self.ranges.append([[sorted[0], sorted[-1]]]) # Compute chunk bounds self.bounds.append([sorted[cs::cs]]) # Compute start, stop & median bounds and ranges self.abounds.append(sorted[0::cs]) self.zbounds.append(sorted[cs - 1::cs]) smedian = sorted[cs // 2::cs] self.mbounds.append(smedian) self.mranges.append([smedian[ncs // 2]]) del sorted, smedian # delete references # Now that sorted is gone, we can copy the indices indices = tmp.indices[i] self.indices.append(indices.reshape(1, indices.size)) # Now it is the last row turn (if needed) if self.nelementsSLR > 0: # First, the sorted values sortedLR = self.sortedLR indicesLR = self.indicesLR nelementsLR = self.nelementsILR sortedlr = tmp.sortedLR[:nelementsLR][::reduction].copy() nelementsSLR = len(sortedlr) sortedLR[:nelementsSLR] = sortedlr # Now, the bounds self.bebounds = numpy.concatenate((sortedlr[::cs], [sortedlr[-1]])) offset2 = len(self.bebounds) sortedLR[nelementsSLR:nelementsSLR + offset2] = self.bebounds # Finally, the indices indicesLR[:] = tmp.indicesLR[:] # Update the number of (reduced) sorted elements self.nelementsSLR = nelementsSLR # The number of elements will be saved as an attribute self.sortedLR.attrs.nelements = self.nelementsSLR self.indicesLR.attrs.nelements = self.nelementsILR if self.verbose: print("Deleting temporaries...") self.tmp = None self.tmpfile.close() os.remove(self.tmpfilename) self.tmpfilename = None # The optimization process has finished, and the index is ok now self.dirty = False # ...but the memory data cache is dirty now self.dirtycache = True def get_neworder(self, neworder, src_disk, tmp_disk, lastrow, nslices, offset, dtype): """Get sorted & indices values in new order.""" cs = self.chunksize ncs = ncs2 = self.nchunkslice self_nslices = self.nslices tmp = numpy.empty(shape=self.slicesize, dtype=dtype) for i in xrange(nslices): ns = offset + i if ns == self_nslices: # The number of complete chunks in the last row ncs2 = self.nelementsILR // cs # Get slices in new order for j in xrange(ncs2): idx = neworder[i * ncs + j] ins = idx // ncs inc = (idx - ins * ncs) * cs ins += offset nc = j * cs if ins == self_nslices: tmp[nc:nc + cs] = lastrow[inc:inc + cs] else: tmp[nc:nc + cs] = src_disk[ins, inc:inc + cs] if ns == self_nslices: # The number of complete chunks in the last row lastrow[:ncs2 * cs] = tmp[:ncs2 * cs] # The elements in the last chunk of the last row will # participate in the global reordering later on, during # the phase of sorting of *two* slices at a time # (including the last row slice, see # self.reorder_slices()). The caches for last row will # be updated in self.reorder_slices() too. # F. Altet 2008-08-25 else: tmp_disk[ns] = tmp def swap_chunks(self, mode="median"): """Swap & reorder the different chunks in a block.""" boundsnames = { 'start': 'abounds', 'stop': 'zbounds', 'median': 'mbounds'} tmp = self.tmp sorted = tmp.sorted indices = tmp.indices tmp_sorted = tmp.sorted2 tmp_indices = tmp.indices2 sortedLR = tmp.sortedLR indicesLR = tmp.indicesLR cs = self.chunksize ncs = self.nchunkslice nsb = self.nslicesblock ncb = ncs * nsb ncb2 = ncb boundsobj = tmp._f_get_child(boundsnames[mode]) can_cross_bbounds = (self.indsize == 8 and self.nelementsILR > 0) for nblock in xrange(self.nblocks): # Protection for last block having less chunks than ncb remainingchunks = self.nchunks - nblock * ncb if remainingchunks < ncb: ncb2 = remainingchunks if ncb2 <= 1: # if only zero or one chunks remains we are done break nslices = ncb2 // ncs bounds = boundsobj[nblock * ncb:nblock * ncb + ncb2] # Do this only if lastrow elements can cross block boundaries if (nblock == self.nblocks - 1 and # last block can_cross_bbounds): nslices += 1 ul = self.nelementsILR // cs bounds = numpy.concatenate((bounds, self.bebounds[:ul])) sbounds_idx = bounds.argsort(kind=defsort) offset = nblock * nsb # Swap sorted and indices following the new order self.get_neworder(sbounds_idx, sorted, tmp_sorted, sortedLR, nslices, offset, self.dtype) self.get_neworder(sbounds_idx, indices, tmp_indices, indicesLR, nslices, offset, 'u%d' % self.indsize) # Reorder completely the index at slice level self.reorder_slices(tmp=True) def read_slice(self, where, nslice, buffer, start=0): """Read a slice from the `where` dataset and put it in `buffer`.""" # Create the buffers for specifying the coordinates self.startl = numpy.array([nslice, start], numpy.uint64) self.stopl = numpy.array([nslice + 1, start + buffer.size], numpy.uint64) self.stepl = numpy.ones(shape=2, dtype=numpy.uint64) where._g_read_slice(self.startl, self.stopl, self.stepl, buffer) def write_slice(self, where, nslice, buffer, start=0): """Write a `slice` to the `where` dataset with the `buffer` data.""" self.startl = numpy.array([nslice, start], numpy.uint64) self.stopl = numpy.array([nslice + 1, start + buffer.size], numpy.uint64) self.stepl = numpy.ones(shape=2, dtype=numpy.uint64) countl = self.stopl - self.startl # (1, self.slicesize) where._g_write_slice(self.startl, self.stepl, countl, buffer) # Read version for LastRow def read_slice_lr(self, where, buffer, start=0): """Read a slice from the `where` dataset and put it in `buffer`.""" startl = numpy.array([start], dtype=numpy.uint64) stopl = numpy.array([start + buffer.size], dtype=numpy.uint64) stepl = numpy.array([1], dtype=numpy.uint64) where._g_read_slice(startl, stopl, stepl, buffer) # Write version for LastRow def write_sliceLR(self, where, buffer, start=0): """Write a slice from the `where` dataset with the `buffer` data.""" startl = numpy.array([start], dtype=numpy.uint64) countl = numpy.array([start + buffer.size], dtype=numpy.uint64) stepl = numpy.array([1], dtype=numpy.uint64) where._g_write_slice(startl, stepl, countl, buffer) read_sliceLR = previous_api(read_slice_lr) def reorder_slice(self, nslice, sorted, indices, ssorted, sindices, tmp_sorted, tmp_indices): """Copy & reorder the slice in source to final destination.""" ss = self.slicesize # Load the second part in buffers self.read_slice(tmp_sorted, nslice, ssorted[ss:]) self.read_slice(tmp_indices, nslice, sindices[ss:]) indexesextension.keysort(ssorted, sindices) # Write the first part of the buffers to the regular leaves self.write_slice(sorted, nslice - 1, ssorted[:ss]) self.write_slice(indices, nslice - 1, sindices[:ss]) # Update caches self.update_caches(nslice - 1, ssorted[:ss]) # Shift the slice in the end to the beginning ssorted[:ss] = ssorted[ss:] sindices[:ss] = sindices[ss:] def update_caches(self, nslice, ssorted): """Update the caches for faster lookups.""" cs = self.chunksize ncs = self.nchunkslice tmp = self.tmp # update first & second cache bounds (ranges & bounds) tmp.ranges[nslice] = ssorted[[0, -1]] tmp.bounds[nslice] = ssorted[cs::cs] # update start & stop bounds tmp.abounds[nslice * ncs:(nslice + 1) * ncs] = ssorted[0::cs] tmp.zbounds[nslice * ncs:(nslice + 1) * ncs] = ssorted[cs - 1::cs] # update median bounds smedian = ssorted[cs // 2::cs] tmp.mbounds[nslice * ncs:(nslice + 1) * ncs] = smedian tmp.mranges[nslice] = smedian[ncs // 2] def reorder_slices(self, tmp): """Reorder completely the index at slice level. This method has to maintain the locality of elements in the ambit of ``blocks``, i.e. an element of a ``block`` cannot be sent to another ``block`` during this reordering. This is *critical* for ``light`` indexes to be able to use this. This version of reorder_slices is optimized in that *two* complete slices are taken at a time (including the last row slice) so as to sort them. Then, each new slice that is read is put at the end of this two-slice buffer, while the previous one is moved to the beginning of the buffer. This is in order to better reduce the entropy of the regular part (i.e. all except the last row) of the index. A secondary effect of this is that it takes at least *twice* of memory than a previous version of reorder_slices() that only reorders on a slice-by-slice basis. However, as this is more efficient than the old version, one can configure the slicesize to be smaller, so the memory consumption is barely similar. """ tmp = self.tmp sorted = tmp.sorted indices = tmp.indices if tmp: tmp_sorted = tmp.sorted2 tmp_indices = tmp.indices2 else: tmp_sorted = tmp.sorted tmp_indices = tmp.indices cs = self.chunksize ss = self.slicesize nsb = self.blocksize // self.slicesize nslices = self.nslices nblocks = self.nblocks nelementsLR = self.nelementsILR # Create the buffer for reordering 2 slices at a time ssorted = numpy.empty(shape=ss * 2, dtype=self.dtype) sindices = numpy.empty(shape=ss * 2, dtype=numpy.dtype('u%d' % self.indsize)) if self.indsize == 8: # Bootstrap the process for reordering # Read the first slice in buffers self.read_slice(tmp_sorted, 0, ssorted[:ss]) self.read_slice(tmp_indices, 0, sindices[:ss]) nslice = 0 # Just in case the loop behind executes nothing # Loop over the remainding slices in block for nslice in xrange(1, sorted.nrows): self.reorder_slice(nslice, sorted, indices, ssorted, sindices, tmp_sorted, tmp_indices) # End the process (enrolling the lastrow if necessary) if nelementsLR > 0: sortedLR = self.tmp.sortedLR indicesLR = self.tmp.indicesLR # Shrink the ssorted and sindices arrays to the minimum ssorted2 = ssorted[:ss + nelementsLR] sortedlr = ssorted2[ss:] sindices2 = sindices[:ss + nelementsLR] indiceslr = sindices2[ss:] # Read the last row info in the second part of the buffer self.read_slice_lr(sortedLR, sortedlr) self.read_slice_lr(indicesLR, indiceslr) indexesextension.keysort(ssorted2, sindices2) # Write the second part of the buffers to the lastrow indices self.write_sliceLR(sortedLR, sortedlr) self.write_sliceLR(indicesLR, indiceslr) # Update the caches for last row bebounds = numpy.concatenate((sortedlr[::cs], [sortedlr[-1]])) sortedLR[nelementsLR:nelementsLR + len(bebounds)] = bebounds self.bebounds = bebounds # Write the first part of the buffers to the regular leaves self.write_slice(sorted, nslice, ssorted[:ss]) self.write_slice(indices, nslice, sindices[:ss]) # Update caches for this slice self.update_caches(nslice, ssorted[:ss]) else: # Iterate over each block. No data should cross block # boundaries to avoid adressing problems with short indices. for nb in xrange(nblocks): # Bootstrap the process for reordering # Read the first slice in buffers nrow = nb * nsb self.read_slice(tmp_sorted, nrow, ssorted[:ss]) self.read_slice(tmp_indices, nrow, sindices[:ss]) # Loop over the remainding slices in block lrb = nrow + nsb if lrb > nslices: lrb = nslices nslice = nrow # Just in case the loop behind executes nothing for nslice in xrange(nrow + 1, lrb): self.reorder_slice(nslice, sorted, indices, ssorted, sindices, tmp_sorted, tmp_indices) # Write the first part of the buffers to the regular leaves self.write_slice(sorted, nslice, ssorted[:ss]) self.write_slice(indices, nslice, sindices[:ss]) # Update caches for this slice self.update_caches(nslice, ssorted[:ss]) def swap_slices(self, mode="median"): """Swap slices in a superblock.""" tmp = self.tmp sorted = tmp.sorted indices = tmp.indices tmp_sorted = tmp.sorted2 tmp_indices = tmp.indices2 ncs = self.nchunkslice nss = self.superblocksize // self.slicesize nss2 = nss for sblock in xrange(self.nsuperblocks): # Protection for last superblock having less slices than nss remainingslices = self.nslices - sblock * nss if remainingslices < nss: nss2 = remainingslices if nss2 <= 1: break if mode == "start": ranges = tmp.ranges[sblock * nss:sblock * nss + nss2, 0] elif mode == "stop": ranges = tmp.ranges[sblock * nss:sblock * nss + nss2, 1] elif mode == "median": ranges = tmp.mranges[sblock * nss:sblock * nss + nss2] sranges_idx = ranges.argsort(kind=defsort) # Don't swap the superblock at all if one doesn't need to ndiff = (sranges_idx != numpy.arange(nss2)).sum() / 2 if ndiff * 50 < nss2: # The number of slices to rearrange is less than 2.5%, # so skip the reordering of this superblock # (too expensive for such a little improvement) if self.verbose: print("skipping reordering of superblock ->", sblock) continue ns = sblock * nss2 # Swap sorted and indices slices following the new order for i in xrange(nss2): idx = sranges_idx[i] # Swap sorted & indices slices oi = ns + i oidx = ns + idx tmp_sorted[oi] = sorted[oidx] tmp_indices[oi] = indices[oidx] # Swap start, stop & median ranges tmp.ranges2[oi] = tmp.ranges[oidx] tmp.mranges2[oi] = tmp.mranges[oidx] # Swap chunk bounds tmp.bounds2[oi] = tmp.bounds[oidx] # Swap start, stop & median bounds j = oi * ncs jn = (oi + 1) * ncs xj = oidx * ncs xjn = (oidx + 1) * ncs tmp.abounds2[j:jn] = tmp.abounds[xj:xjn] tmp.zbounds2[j:jn] = tmp.zbounds[xj:xjn] tmp.mbounds2[j:jn] = tmp.mbounds[xj:xjn] # tmp -> originals for i in xrange(nss2): # Copy sorted & indices slices oi = ns + i sorted[oi] = tmp_sorted[oi] indices[oi] = tmp_indices[oi] # Copy start, stop & median ranges tmp.ranges[oi] = tmp.ranges2[oi] tmp.mranges[oi] = tmp.mranges2[oi] # Copy chunk bounds tmp.bounds[oi] = tmp.bounds2[oi] # Copy start, stop & median bounds j = oi * ncs jn = (oi + 1) * ncs tmp.abounds[j:jn] = tmp.abounds2[j:jn] tmp.zbounds[j:jn] = tmp.zbounds2[j:jn] tmp.mbounds[j:jn] = tmp.mbounds2[j:jn] def search_item_lt(self, where, item, nslice, limits, start=0): """Search a single item in a specific sorted slice.""" # This method will only works under the assumtion that item # *is to be found* in the nslice. assert limits[0] < item <= limits[1] cs = self.chunksize ss = self.slicesize nelementsLR = self.nelementsILR bstart = start // cs # Find the chunk if nslice < self.nslices: nchunk = bisect_left(where.bounds[nslice], item, bstart) else: # We need to subtract 1 chunk here because bebounds # has a leading value nchunk = bisect_left(self.bebounds, item, bstart) - 1 assert nchunk >= 0 # Find the element in chunk pos = nchunk * cs if nslice < self.nslices: pos += bisect_left(where.sorted[nslice, pos:pos + cs], item) assert pos <= ss else: end = pos + cs if end > nelementsLR: end = nelementsLR pos += bisect_left(self.sortedLR[pos:end], item) assert pos <= nelementsLR assert pos > 0 return pos def compute_overlaps_finegrain(self, where, message, verbose): """Compute some statistics about overlaping of slices in index. It returns the following info: noverlaps : int The total number of elements that overlaps in index. multiplicity : array of int The number of times that a concrete slice overlaps with any other. toverlap : float An ovelap index: the sum of the values in segment slices that overlaps divided by the entire range of values. This index is only computed for numerical types. """ ss = self.slicesize ranges = where.ranges[:] sorted = where.sorted sortedLR = where.sortedLR nslices = self.nslices nelementsLR = self.nelementsILR if nelementsLR > 0: # Add the ranges corresponding to the last row rangeslr = numpy.array([self.bebounds[0], self.bebounds[-1]]) ranges = numpy.concatenate((ranges, [rangeslr])) nslices += 1 soverlap = 0. toverlap = -1. multiplicity = numpy.zeros(shape=nslices, dtype="int_") overlaps = multiplicity.copy() starts = multiplicity.copy() for i in xrange(nslices): prev_end = ranges[i, 1] for j in xrange(i + 1, nslices): stj = starts[j] assert stj <= ss if stj == ss: # This slice has already been counted continue if j < self.nslices: next_beg = sorted[j, stj] else: next_beg = sortedLR[stj] next_end = ranges[j, 1] if prev_end > next_end: # Complete overlapping case multiplicity[j - i] += 1 if j < self.nslices: overlaps[i] += ss - stj starts[j] = ss # a sentinel else: overlaps[i] += nelementsLR - stj starts[j] = nelementsLR # a sentinel elif prev_end > next_beg: multiplicity[j - i] += 1 idx = self.search_item_lt( where, prev_end, j, ranges[j], stj) nelem = idx - stj overlaps[i] += nelem starts[j] = idx if self.type != "string": # Convert ranges into floats in order to allow # doing operations with them without overflows soverlap += float(ranges[i, 1]) - float(ranges[j, 0]) # Return the overlap as the ratio between overlaps and entire range if self.type != "string": erange = float(ranges[-1, 1]) - float(ranges[0, 0]) # Check that there is an effective range of values # Beware, erange can be negative in situations where # the values are suffering overflow. This can happen # specially on big signed integer values (on overflows, # the end value will become negative!). # Also, there is no way to compute overlap ratios for # non-numerical types. So, be careful and always check # that toverlap has a positive value (it must have been # initialized to -1. before) before using it. # F. Alted 2007-01-19 if erange > 0: toverlap = soverlap / erange if verbose and message != "init": print("toverlap (%s):" % message, toverlap) print("multiplicity:\n", multiplicity, multiplicity.sum()) print("overlaps:\n", overlaps, overlaps.sum()) noverlaps = overlaps.sum() # For full indexes, set the 'is_csi' flag if self.indsize == 8 and self._v_file._iswritable(): self._v_attrs.is_csi = (noverlaps == 0) # Save the number of overlaps for future references self.noverlaps = noverlaps return (noverlaps, multiplicity, toverlap) def compute_overlaps(self, where, message, verbose): """Compute some statistics about overlaping of slices in index. It returns the following info: noverlaps : int The total number of slices that overlaps in index. multiplicity : array of int The number of times that a concrete slice overlaps with any other. toverlap : float An ovelap index: the sum of the values in segment slices that overlaps divided by the entire range of values. This index is only computed for numerical types. """ ranges = where.ranges[:] nslices = self.nslices if self.nelementsILR > 0: # Add the ranges corresponding to the last row rangeslr = numpy.array([self.bebounds[0], self.bebounds[-1]]) ranges = numpy.concatenate((ranges, [rangeslr])) nslices += 1 noverlaps = 0 soverlap = 0. toverlap = -1. multiplicity = numpy.zeros(shape=nslices, dtype="int_") for i in xrange(nslices): for j in xrange(i + 1, nslices): if ranges[i, 1] > ranges[j, 0]: noverlaps += 1 multiplicity[j - i] += 1 if self.type != "string": # Convert ranges into floats in order to allow # doing operations with them without overflows soverlap += float(ranges[i, 1]) - float(ranges[j, 0]) # Return the overlap as the ratio between overlaps and entire range if self.type != "string": erange = float(ranges[-1, 1]) - float(ranges[0, 0]) # Check that there is an effective range of values # Beware, erange can be negative in situations where # the values are suffering overflow. This can happen # specially on big signed integer values (on overflows, # the end value will become negative!). # Also, there is no way to compute overlap ratios for # non-numerical types. So, be careful and always check # that toverlap has a positive value (it must have been # initialized to -1. before) before using it. # F. Altet 2007-01-19 if erange > 0: toverlap = soverlap / erange if verbose: print("overlaps (%s):" % message, noverlaps, toverlap) print(multiplicity) # For full indexes, set the 'is_csi' flag if self.indsize == 8 and self._v_file._iswritable(): self._v_attrs.is_csi = (noverlaps == 0) # Save the number of overlaps for future references self.noverlaps = noverlaps return (noverlaps, multiplicity, toverlap) def read_sorted_indices(self, what, start, stop, step): """Return the sorted or indices values in the specified range.""" (start, stop, step) = self._process_range(start, stop, step) if start >= stop: return numpy.empty(0, self.dtype) # Correction for negative values of step (reverse indices) if step < 0: tmp = start start = self.nelements - stop stop = self.nelements - tmp if what == "sorted": values = self.sorted valuesLR = self.sortedLR buffer_ = numpy.empty(stop - start, dtype=self.dtype) else: values = self.indices valuesLR = self.indicesLR buffer_ = numpy.empty(stop - start, dtype="u%d" % self.indsize) ss = self.slicesize nrow_start = start // ss istart = start % ss nrow_stop = stop // ss tlen = stop - start bstart = 0 ilen = 0 for nrow in xrange(nrow_start, nrow_stop + 1): blen = ss - istart if ilen + blen > tlen: blen = tlen - ilen if blen <= 0: break if nrow < self.nslices: self.read_slice( values, nrow, buffer_[bstart:bstart + blen], istart) else: self.read_slice_lr( valuesLR, buffer_[bstart:bstart + blen], istart) istart = 0 bstart += blen ilen += blen return buffer_[::step] def read_sorted(self, start=None, stop=None, step=None): """Return the sorted values of index in the specified range. The meaning of the start, stop and step arguments is the same as in :meth:`Table.read_sorted`. """ return self.read_sorted_indices('sorted', start, stop, step) readSorted = previous_api(read_sorted) def read_indices(self, start=None, stop=None, step=None): """Return the indices values of index in the specified range. The meaning of the start, stop and step arguments is the same as in :meth:`Table.read_sorted`. """ return self.read_sorted_indices('indices', start, stop, step) readIndices = previous_api(read_indices) def _process_range(self, start, stop, step): """Get a range specifc for the index usage.""" if start is not None and stop is None: # Special case for the behaviour of PyTables iterators stop = idx2long(start + 1) if start is None: start = 0L else: start = idx2long(start) if stop is None: stop = idx2long(self.nelements) else: stop = idx2long(stop) if step is None: step = 1L else: step = idx2long(step) return (start, stop, step) _processRange = previous_api(_process_range) def __getitem__(self, key): """Return the indices values of index in the specified range. If key argument is an integer, the corresponding index is returned. If key is a slice, the range of indices determined by it is returned. A negative value of step in slice is supported, meaning that the results will be returned in reverse order. This method is equivalent to :meth:`Index.read_indices`. """ if is_idx(key): if key < 0: # To support negative values key += self.nelements return self.read_indices(key, key + 1, 1)[0] elif isinstance(key, slice): return self.read_indices(key.start, key.stop, key.step) def __len__(self): return self.nelements def restorecache(self): "Clean the limits cache and resize starts and lengths arrays" params = self._v_file.params # The sorted IndexArray is absolutely required to be in memory # at the same time than the Index instance, so create a strong # reference to it. We are not introducing leaks because the # strong reference will disappear when this Index instance is # to be closed. self._sorted = self.sorted self._sorted.boundscache = ObjectCache(params['BOUNDS_MAX_SLOTS'], params['BOUNDS_MAX_SIZE'], 'non-opt types bounds') self.sorted.boundscache = ObjectCache(params['BOUNDS_MAX_SLOTS'], params['BOUNDS_MAX_SIZE'], 'non-opt types bounds') """A cache for the bounds (2nd hash) data. Only used for non-optimized types searches.""" self.limboundscache = ObjectCache(params['LIMBOUNDS_MAX_SLOTS'], params['LIMBOUNDS_MAX_SIZE'], 'bounding limits') """A cache for bounding limits.""" self.sortedLRcache = ObjectCache(params['SORTEDLR_MAX_SLOTS'], params['SORTEDLR_MAX_SIZE'], 'last row chunks') """A cache for the last row chunks. Only used for searches in the last row, and mainly useful for small indexes.""" self.starts = numpy.empty(shape=self.nrows, dtype=numpy.int32) self.lengths = numpy.empty(shape=self.nrows, dtype=numpy.int32) self.sorted._init_sorted_slice(self) self.dirtycache = False def search(self, item): """Do a binary search in this index for an item.""" if profile: tref = time() if profile: show_stats("Entering search", tref) if self.dirtycache: self.restorecache() # An empty item or if left limit is larger than the right one # means that the number of records is always going to be empty, # so we avoid further computation (including looking up the # limits cache). if not item or item[0] > item[1]: self.starts[:] = 0 self.lengths[:] = 0 return 0 tlen = 0 # Check whether the item tuple is in the limits cache or not nslot = self.limboundscache.getslot(item) if nslot >= 0: startlengths = self.limboundscache.getitem(nslot) # Reset the lengths array (not necessary for starts) self.lengths[:] = 0 # Now, set the interesting rows for nrow in xrange(len(startlengths)): nrow2, start, length = startlengths[nrow] self.starts[nrow2] = start self.lengths[nrow2] = length tlen = tlen + length return tlen # The item is not in cache. Do the real lookup. sorted = self.sorted if self.nslices > 0: if self.type in self.opt_search_types: # The next are optimizations. However, they hide the # CPU functions consumptions from python profiles. # You may want to de-activate them during profiling. if self.type == "int32": tlen = sorted._search_bin_na_i(*item) elif self.type == "int64": tlen = sorted._search_bin_na_ll(*item) elif self.type == "float16": tlen = sorted._search_bin_na_e(*item) elif self.type == "float32": tlen = sorted._search_bin_na_f(*item) elif self.type == "float64": tlen = sorted._search_bin_na_d(*item) elif self.type == "float96": tlen = sorted._search_bin_na_g(*item) elif self.type == "float128": tlen = sorted._search_bin_na_g(*item) elif self.type == "uint32": tlen = sorted._search_bin_na_ui(*item) elif self.type == "uint64": tlen = sorted._search_bin_na_ull(*item) elif self.type == "int8": tlen = sorted._search_bin_na_b(*item) elif self.type == "int16": tlen = sorted._search_bin_na_s(*item) elif self.type == "uint8": tlen = sorted._search_bin_na_ub(*item) elif self.type == "uint16": tlen = sorted._search_bin_na_us(*item) else: assert False, "This can't happen!" else: tlen = self.search_scalar(item, sorted) # Get possible remaining values in last row if self.nelementsSLR > 0: # Look for more indexes in the last row (start, stop) = self.search_last_row(item) self.starts[-1] = start self.lengths[-1] = stop - start tlen += stop - start if self.limboundscache.couldenablecache(): # Get a startlengths tuple and save it in cache. # This is quite slow, but it is a good way to compress # the bounds info. Moreover, the .couldenablecache() # is doing a good work so as to avoid computing this # when it is not necessary to do it. startlengths = [] for nrow, length in enumerate(self.lengths): if length > 0: startlengths.append((nrow, self.starts[nrow], length)) # Compute the size of the recarray (aproximately) # The +1 at the end is important to avoid 0 lengths # (remember, the object headers take some space) size = len(startlengths) * 8 * 2 + 1 # Put this startlengths list in cache self.limboundscache.setitem(item, startlengths, size) if profile: show_stats("Exiting search", tref) return tlen # This is an scalar version of search. It works with strings as well. def search_scalar(self, item, sorted): """Do a binary search in this index for an item.""" tlen = 0 # Do the lookup for values fullfilling the conditions for i in xrange(self.nslices): (start, stop) = sorted._search_bin(i, item) self.starts[i] = start self.lengths[i] = stop - start tlen += stop - start return tlen def search_last_row(self, item): # Variable initialization item1, item2 = item bebounds = self.bebounds b0, b1 = bebounds[0], bebounds[-1] bounds = bebounds[1:-1] itemsize = self.dtype.itemsize sortedLRcache = self.sortedLRcache hi = self.nelementsSLR # maximum number of elements rchunksize = self.chunksize // self.reduction nchunk = -1 # Lookup for item1 if item1 > b0: if item1 <= b1: # Search the appropriate chunk in bounds cache nchunk = bisect_left(bounds, item1) # Lookup for this chunk in cache nslot = sortedLRcache.getslot(nchunk) if nslot >= 0: chunk = sortedLRcache.getitem(nslot) else: begin = rchunksize * nchunk end = rchunksize * (nchunk + 1) if end > hi: end = hi # Read the chunk from disk chunk = self.sortedLR._read_sorted_slice( self.sorted, begin, end) # Put it in cache. It's important to *copy* # the buffer, as it is reused in future reads! sortedLRcache.setitem(nchunk, chunk.copy(), (end - begin) * itemsize) start = bisect_left(chunk, item1) start += rchunksize * nchunk else: start = hi else: start = 0 # Lookup for item2 if item2 >= b0: if item2 < b1: # Search the appropriate chunk in bounds cache nchunk2 = bisect_right(bounds, item2) if nchunk2 != nchunk: # Lookup for this chunk in cache nslot = sortedLRcache.getslot(nchunk2) if nslot >= 0: chunk = sortedLRcache.getitem(nslot) else: begin = rchunksize * nchunk2 end = rchunksize * (nchunk2 + 1) if end > hi: end = hi # Read the chunk from disk chunk = self.sortedLR._read_sorted_slice( self.sorted, begin, end) # Put it in cache. It's important to *copy* # the buffer, as it is reused in future reads! # See bug #60 in xot.carabos.com sortedLRcache.setitem(nchunk2, chunk.copy(), (end - begin) * itemsize) stop = bisect_right(chunk, item2) stop += rchunksize * nchunk2 else: stop = hi else: stop = 0 return (start, stop) searchLastRow = previous_api(search_last_row) def get_chunkmap(self): """Compute a map with the interesting chunks in index.""" if profile: tref = time() if profile: show_stats("Entering get_chunkmap", tref) ss = self.slicesize nsb = self.nslicesblock nslices = self.nslices lbucket = self.lbucket indsize = self.indsize bucketsinblock = float(self.blocksize) / lbucket nchunks = long(math.ceil(float(self.nelements) / lbucket)) chunkmap = numpy.zeros(shape=nchunks, dtype="bool") reduction = self.reduction starts = (self.starts - 1) * reduction + 1 stops = (self.starts + self.lengths) * reduction starts[starts < 0] = 0 # All negative values set to zero indices = self.indices for nslice in xrange(self.nrows): start = starts[nslice] stop = stops[nslice] if stop > start: idx = numpy.empty(shape=stop - start, dtype='u%d' % indsize) if nslice < nslices: indices._read_index_slice(nslice, start, stop, idx) else: self.indicesLR._read_index_slice(start, stop, idx) if indsize == 8: idx //= lbucket elif indsize == 2: # The chunkmap size cannot be never larger than 'int_' idx = idx.astype("int_") offset = long((nslice // nsb) * bucketsinblock) idx += offset elif indsize == 1: # The chunkmap size cannot be never larger than 'int_' idx = idx.astype("int_") offset = (nslice * ss) // lbucket idx += offset chunkmap[idx] = True # The case lbucket < nrowsinchunk should only happen in tests nrowsinchunk = self.nrowsinchunk if lbucket != nrowsinchunk: # Map the 'coarse grain' chunkmap into the 'true' chunkmap nelements = self.nelements tnchunks = long(math.ceil(float(nelements) / nrowsinchunk)) tchunkmap = numpy.zeros(shape=tnchunks, dtype="bool") ratio = float(lbucket) / nrowsinchunk idx = chunkmap.nonzero()[0] starts = (idx * ratio).astype('int_') stops = numpy.ceil((idx + 1) * ratio).astype('int_') for i in range(len(idx)): tchunkmap[starts[i]:stops[i]] = True chunkmap = tchunkmap if profile: show_stats("Exiting get_chunkmap", tref) return chunkmap def get_lookup_range(self, ops, limits): assert len(ops) in [1, 2] assert len(limits) in [1, 2] assert len(ops) == len(limits) column = self.column coldtype = column.dtype.base itemsize = coldtype.itemsize if len(limits) == 1: assert ops[0] in ['lt', 'le', 'eq', 'ge', 'gt'] limit = limits[0] op = ops[0] if op == 'lt': range_ = (inftype(coldtype, itemsize, sign=-1), nextafter(limit, -1, coldtype, itemsize)) elif op == 'le': range_ = (inftype(coldtype, itemsize, sign=-1), limit) elif op == 'gt': range_ = (nextafter(limit, +1, coldtype, itemsize), inftype(coldtype, itemsize, sign=+1)) elif op == 'ge': range_ = (limit, inftype(coldtype, itemsize, sign=+1)) elif op == 'eq': range_ = (limit, limit) elif len(limits) == 2: assert ops[0] in ('gt', 'ge') and ops[1] in ('lt', 'le') lower, upper = limits if lower > upper: # ``a <[=] x <[=] b`` is always false if ``a > b``. return () if ops == ('gt', 'lt'): # lower < col < upper range_ = (nextafter(lower, +1, coldtype, itemsize), nextafter(upper, -1, coldtype, itemsize)) elif ops == ('ge', 'lt'): # lower <= col < upper range_ = (lower, nextafter(upper, -1, coldtype, itemsize)) elif ops == ('gt', 'le'): # lower < col <= upper range_ = (nextafter(lower, +1, coldtype, itemsize), upper) elif ops == ('ge', 'le'): # lower <= col <= upper range_ = (lower, upper) return range_ getLookupRange = previous_api(get_lookup_range) def _f_remove(self, recursive=False): """Remove this Index object.""" # Index removal is always recursive, # no matter what `recursive` says. super(Index, self)._f_remove(True) def __str__(self): """This provides a more compact representation than __repr__""" # The filters filters = "" if self.filters.complevel: if self.filters.shuffle: filters += ", shuffle" filters += ", %s(%s)" % (self.filters.complib, self.filters.complevel) return "Index(%s, %s%s).is_csi=%s" % \ (self.optlevel, self.kind, filters, self.is_csi) def __repr__(self): """This provides more metainfo than standard __repr__""" cpathname = self.table._v_pathname + ".cols." + self.column.pathname retstr = """%s (Index for column %s) optlevel := %s kind := %s filters := %s is_csi := %s nelements := %s chunksize := %s slicesize := %s blocksize := %s superblocksize := %s filters := %s dirty := %s byteorder := %r""" % (self._v_pathname, cpathname, self.optlevel, self.kind, self.filters, self.is_csi, self.nelements, self.chunksize, self.slicesize, self.blocksize, self.superblocksize, self.filters, self.dirty, self.byteorder) retstr += "\n sorted := %s" % self.sorted retstr += "\n indices := %s" % self.indices retstr += "\n ranges := %s" % self.ranges retstr += "\n bounds := %s" % self.bounds retstr += "\n sortedLR := %s" % self.sortedLR retstr += "\n indicesLR := %s" % self.indicesLR return retstr class IndexesDescG(NotLoggedMixin, Group): _c_classid = 'DINDEX' _c_classId = previous_api_property('_c_classid') def _g_width_warning(self): warnings.warn( "the number of indexed columns on a single description group " "is exceeding the recommended maximum (%d); " "be ready to see PyTables asking for *lots* of memory " "and possibly slow I/O" % self._v_max_group_width, PerformanceWarning) _g_widthWarning = previous_api(_g_width_warning) class IndexesTableG(NotLoggedMixin, Group): _c_classid = 'TINDEX' _c_classId = previous_api_property('_c_classid') def _getauto(self): if 'AUTO_INDEX' not in self._v_attrs: return default_auto_index return self._v_attrs.AUTO_INDEX def _setauto(self, auto): self._v_attrs.AUTO_INDEX = bool(auto) def _delauto(self): del self._v_attrs.AUTO_INDEX auto = property(_getauto, _setauto, _delauto) def _g_width_warning(self): warnings.warn( "the number of indexed columns on a single table " "is exceeding the recommended maximum (%d); " "be ready to see PyTables asking for *lots* of memory " "and possibly slow I/O" % self._v_max_group_width, PerformanceWarning) _g_widthWarning = previous_api(_g_width_warning) def _g_check_name(self, name): if not name.startswith('_i_'): raise ValueError( "names of index groups must start with ``_i_``: %s" % name) _g_checkName = previous_api(_g_check_name) def _gettable(self): names = self._v_pathname.split("/") tablename = names.pop()[3:] # "_i_" is at the beginning parentpathname = "/".join(names) tablepathname = join_path(parentpathname, tablename) table = self._v_file._get_node(tablepathname) return table table = property( _gettable, None, None, "Accessor for the `Table` object of this `IndexesTableG` container.") class OldIndex(NotLoggedMixin, Group): """This is meant to hide indexes of PyTables 1.x files.""" _c_classid = 'CINDEX' _c_classId = previous_api_property('_c_classid') ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/indexes.py000066400000000000000000000151021231437614300167710ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: June 02, 2004 # Author: Francesc Alted - faltet@pytables.com # # $Source: /cvsroot/pytables/pytables/tables/indexes.py $ # $Id$ # ######################################################################## """Here is defined the IndexArray class.""" from bisect import bisect_left, bisect_right from tables.node import NotLoggedMixin from tables.carray import CArray from tables.earray import EArray from tables import indexesextension from tables._past import previous_api, previous_api_property # Declarations for inheriting class CacheArray(NotLoggedMixin, EArray, indexesextension.CacheArray): """Container for keeping index caches of 1st and 2nd level.""" # Class identifier. _c_classid = 'CACHEARRAY' _c_classId = previous_api_property('_c_classid') class LastRowArray(NotLoggedMixin, CArray, indexesextension.LastRowArray): """Container for keeping sorted and indices values of last row of an index.""" # Class identifier. _c_classid = 'LASTROWARRAY' _c_classId = previous_api_property('_c_classid') class IndexArray(NotLoggedMixin, EArray, indexesextension.IndexArray): """Represent the index (sorted or reverse index) dataset in HDF5 file. All NumPy typecodes are supported except for complex datatypes. Parameters ---------- parentnode The Index class from which this object will hang off. .. versionchanged:: 3.0 Renamed from *parentNode* to *parentnode*. name : str The name of this node in its parent group. atom An Atom object representing the shape and type of the atomic objects to be saved. Only scalar atoms are supported. title Sets a TITLE attribute on the array entity. filters : Filters An instance of the Filters class that provides information about the desired I/O filters to be applied during the life of this object. byteorder The byteroder of the data on-disk. """ # Class identifier. _c_classid = 'INDEXARRAY' _c_classId = previous_api_property('_c_classid') # Properties # ~~~~~~~~~~ chunksize = property( lambda self: self.chunkshape[1], None, None, """The chunksize for this object.""") slicesize = property( lambda self: self.shape[1], None, None, """The slicesize for this object.""") # Other methods # ~~~~~~~~~~~~~ def __init__(self, parentnode, name, atom=None, title="", filters=None, byteorder=None): """Create an IndexArray instance.""" self._v_pathname = parentnode._g_join(name) if atom is not None: # The shape and chunkshape needs to be fixed here if name == "sorted": reduction = parentnode.reduction shape = (0, parentnode.slicesize // reduction) chunkshape = (1, parentnode.chunksize // reduction) else: shape = (0, parentnode.slicesize) chunkshape = (1, parentnode.chunksize) else: # The shape and chunkshape will be read from disk later on shape = None chunkshape = None super(IndexArray, self).__init__( parentnode, name, atom, shape, title, filters, chunkshape=chunkshape, byteorder=byteorder) # This version of searchBin uses both ranges (1st level) and # bounds (2nd level) caches. It uses a cache for boundary rows, # but not for 'sorted' rows (this is only supported for the # 'optimized' types). def _search_bin(self, nrow, item): item1, item2 = item result1 = -1 result2 = -1 hi = self.shape[1] ranges = self._v_parent.rvcache boundscache = self.boundscache # First, look at the beginning of the slice begin = ranges[nrow, 0] # Look for items at the beginning of sorted slices if item1 <= begin: result1 = 0 if item2 < begin: result2 = 0 if result1 >= 0 and result2 >= 0: return (result1, result2) # Then, look for items at the end of the sorted slice end = ranges[nrow, 1] if result1 < 0: if item1 > end: result1 = hi if result2 < 0: if item2 >= end: result2 = hi if result1 >= 0 and result2 >= 0: return (result1, result2) # Finally, do a lookup for item1 and item2 if they were not found # Lookup in the middle of slice for item1 chunksize = self.chunksize # Number of elements/chunksize nchunk = -1 # Try to get the bounds row from the LRU cache nslot = boundscache.getslot(nrow) if nslot >= 0: # Cache hit. Use the row kept there. bounds = boundscache.getitem(nslot) else: # No luck with cached data. Read the row and put it in the cache. bounds = self._v_parent.bounds[nrow] size = bounds.size * bounds.itemsize boundscache.setitem(nrow, bounds, size) if result1 < 0: # Search the appropriate chunk in bounds cache nchunk = bisect_left(bounds, item1) chunk = self._read_sorted_slice(nrow, chunksize * nchunk, chunksize * (nchunk + 1)) result1 = self._bisect_left(chunk, item1, chunksize) result1 += chunksize * nchunk # Lookup in the middle of slice for item2 if result2 < 0: # Search the appropriate chunk in bounds cache nchunk2 = bisect_right(bounds, item2) if nchunk2 != nchunk: chunk = self._read_sorted_slice(nrow, chunksize * nchunk2, chunksize * (nchunk2 + 1)) result2 = self._bisect_right(chunk, item2, chunksize) result2 += chunksize * nchunk2 return (result1, result2) _searchBin = previous_api(_search_bin) def __str__(self): "A compact representation of this class" return "IndexArray(path=%s)" % self._v_pathname def __repr__(self): """A verbose representation of this class.""" return """%s atom = %r shape = %s nrows = %s chunksize = %s slicesize = %s byteorder = %r""" % (self, self.atom, self.shape, self.nrows, self.chunksize, self.slicesize, self.byteorder) ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/indexesExtension.py000066400000000000000000000004221231437614300206650ustar00rootroot00000000000000from warnings import warn from tables.indexesextension import * _warnmsg = ("indexesExtension is pending deprecation, import indexesextension instead. " "You may use the pt2to3 tool to update your source code.") warn(_warnmsg, DeprecationWarning, stacklevel=2) PyTables-v.3.1.1/tables/indexesextension.pyx000066400000000000000000001225461231437614300211310ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: May 18, 2006 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """cython interface for keeping indexes classes. Classes (type extensions): IndexArray CacheArray LastRowArray Functions: Misc variables: """ import numpy from tables.exceptions import HDF5ExtError from hdf5extension cimport Array # Types, constants, functions, classes & other objects from everywhere from numpy cimport (import_array, ndarray, npy_intp, npy_int8, npy_uint8, npy_int16, npy_uint16, npy_int32, npy_uint32, npy_int64, npy_uint64, npy_float32, npy_float64, npy_float96, npy_float128, npy_longdouble) ctypedef npy_uint16 npy_float16 from definitions cimport hid_t, herr_t, hsize_t, H5Screate_simple, H5Sclose from lrucacheextension cimport NumCache from tables._past import previous_api #------------------------------------------------------------------- # External C functions # Functions for optimized operations with ARRAY for indexing purposes cdef extern from "H5ARRAY-opt.h" nogil: herr_t H5ARRAYOinit_readSlice( hid_t dataset_id, hid_t *mem_space_id, hsize_t count) herr_t H5ARRAYOread_readSlice( hid_t dataset_id, hid_t type_id, hsize_t irow, hsize_t start, hsize_t stop, void *data) herr_t H5ARRAYOread_readSortedSlice( hid_t dataset_id, hid_t mem_space_id, hid_t type_id, hsize_t irow, hsize_t start, hsize_t stop, void *data) herr_t H5ARRAYOread_readBoundsSlice( hid_t dataset_id, hid_t mem_space_id, hid_t type_id, hsize_t irow, hsize_t start, hsize_t stop, void *data) herr_t H5ARRAYOreadSliceLR( hid_t dataset_id, hid_t type_id, hsize_t start, hsize_t stop, void *data) # Functions for optimized operations for dealing with indexes cdef extern from "idx-opt.h" nogil: int bisect_left_b(npy_int8 *a, long x, int hi, int offset) int bisect_left_ub(npy_uint8 *a, long x, int hi, int offset) int bisect_right_b(npy_int8 *a, long x, int hi, int offset) int bisect_right_ub(npy_uint8 *a, long x, int hi, int offset) int bisect_left_s(npy_int16 *a, long x, int hi, int offset) int bisect_left_us(npy_uint16 *a, long x, int hi, int offset) int bisect_right_s(npy_int16 *a, long x, int hi, int offset) int bisect_right_us(npy_uint16 *a, long x, int hi, int offset) int bisect_left_i(npy_int32 *a, long x, int hi, int offset) int bisect_left_ui(npy_uint32 *a, npy_uint32 x, int hi, int offset) int bisect_right_i(npy_int32 *a, long x, int hi, int offset) int bisect_right_ui(npy_uint32 *a, npy_uint32 x, int hi, int offset) int bisect_left_ll(npy_int64 *a, npy_int64 x, int hi, int offset) int bisect_left_ull(npy_uint64 *a, npy_uint64 x, int hi, int offset) int bisect_right_ll(npy_int64 *a, npy_int64 x, int hi, int offset) int bisect_right_ull(npy_uint64 *a, npy_uint64 x, int hi, int offset) int bisect_left_e(npy_float16 *a, npy_float64 x, int hi, int offset) int bisect_right_e(npy_float16 *a, npy_float64 x, int hi, int offset) int bisect_left_f(npy_float32 *a, npy_float64 x, int hi, int offset) int bisect_right_f(npy_float32 *a, npy_float64 x, int hi, int offset) int bisect_left_d(npy_float64 *a, npy_float64 x, int hi, int offset) int bisect_right_d(npy_float64 *a, npy_float64 x, int hi, int offset) int bisect_left_g(npy_longdouble *a, npy_longdouble x, int hi, int offset) int bisect_right_g(npy_longdouble *a, npy_longdouble x, int hi, int offset) int keysort_f96(npy_float96 *start1, char *start2, npy_intp num, int ts) int keysort_f128(npy_float128 *start1, char *start2, npy_intp num, int ts) int keysort_f64(npy_float64 *start1, char *start2, npy_intp num, int ts) int keysort_f32(npy_float32 *start1, char *start2, npy_intp num, int ts) int keysort_f16(npy_float16 *start1, char *start2, npy_intp num, int ts) int keysort_i64(npy_int64 *start1, char *start2, npy_intp num, int ts) int keysort_u64(npy_uint64 *start1, char *start2, npy_intp num, int ts) int keysort_i32(npy_int32 *start1, char *start2, npy_intp num, int ts) int keysort_u32(npy_uint32 *start1, char *start2, npy_intp num, int ts) int keysort_i16(npy_int16 *start1, char *start2, npy_intp num, int ts) int keysort_u16(npy_uint16 *start1, char *start2, npy_intp num, int ts) int keysort_i8(npy_int8 *start1, char *start2, npy_intp num, int ts) int keysort_u8(npy_uint8 *start1, char *start2, npy_intp num, int ts) int keysort_S(char *start1, int ss, char *start2, npy_intp num, int ts) #---------------------------------------------------------------------------- # Initialization code # The numpy API requires this function to be called before # using any numpy facilities in an extension module. import_array() #--------------------------------------------------------------------------- # Functions # Sorting functions def keysort(ndarray array1, ndarray array2): """Sort array1 in-place. array2 is also sorted following the array1 order. array1 can be of any type, except complex or string. array2 may be made of elements on any size. """ cdef npy_intp size cdef int elsize1, elsize2 size = array1.size elsize1 = array1.itemsize elsize2 = array2.itemsize if array1.dtype == "float64": return keysort_f64(array1.data, array2.data, size, elsize2) elif array1.dtype == "float32": return keysort_f32(array1.data, array2.data, size, elsize2) # elif array1.dtype == "float16": # raises an error if float16 is not defined elif array1.dtype.name == "float16": return keysort_f16(array1.data, array2.data, size, elsize2) elif array1.dtype.name == "float96": return keysort_f96(array1.data, array2.data, size, elsize2) elif array1.dtype.name == "float128": return keysort_f128(array1.data, array2.data, size, elsize2) elif array1.dtype == "int64": return keysort_i64(array1.data, array2.data, size, elsize2) elif array1.dtype == "uint64": return keysort_u64(array1.data, array2.data, size, elsize2) elif array1.dtype == "int32": return keysort_i32(array1.data, array2.data, size, elsize2) elif array1.dtype == "uint32": return keysort_u32(array1.data, array2.data, size, elsize2) elif array1.dtype == "int16": return keysort_i16(array1.data, array2.data, size, elsize2) elif array1.dtype == "uint16": return keysort_u16(array1.data, array2.data, size, elsize2) elif array1.dtype == "int8": return keysort_i8(array1.data, array2.data, size, elsize2) elif array1.dtype == "uint8": return keysort_u8(array1.data, array2.data, size, elsize2) elif array1.dtype == "bool": return keysort_u8(array1.data, array2.data, size, elsize2) elif array1.dtype.char == "S": return keysort_S(array1.data, elsize1, array2.data, size, elsize2) # As it turns out, an indirect sort is always faster, and much faster on # new processors. See # http://www.mail-archive.com/numpy-discussion@scipy.org/msg06639.html # # The next takes more memory (the idx array), but for large strings, this # should be negligible. # #sidx = array1.argsort() #array1[:] = array1[sidx] #array2[:] = array2[sidx] #return 0 else: raise ValueError("This shouldn't happen!") # Classes cdef class Index: pass cdef class CacheArray(Array): """Container for keeping index caches of 1st and 2nd level.""" cdef hid_t mem_space_id cdef initread(self, int nbounds): # "Actions to accelerate the reads afterwards." # Precompute the mem_space_id if (H5ARRAYOinit_readSlice(self.dataset_id, &self.mem_space_id, nbounds) < 0): raise HDF5ExtError("Problems initializing the bounds array data.") return cdef read_slice(self, hsize_t nrow, hsize_t start, hsize_t stop, void *rbuf): # "Read an slice of bounds." if (H5ARRAYOread_readBoundsSlice( self.dataset_id, self.mem_space_id, self.type_id, nrow, start, stop, rbuf) < 0): raise HDF5ExtError("Problems reading the bounds array data.") return def _g_close(self): super(Array, self)._g_close() # Release specific resources of this class if self.mem_space_id > 0: H5Sclose(self.mem_space_id) cdef class IndexArray(Array): """Container for keeping sorted and indices values.""" cdef void *rbufst cdef void *rbufln cdef void *rbufrv cdef void *rbufbc cdef void *rbuflb cdef hid_t mem_space_id cdef int l_chunksize, l_slicesize, nbounds, indsize cdef CacheArray bounds_ext cdef NumCache boundscache, sortedcache cdef ndarray bufferbc, bufferlb def _read_index_slice(self, hsize_t irow, hsize_t start, hsize_t stop, ndarray idx): cdef herr_t ret # Do the physical read with nogil: ret = H5ARRAYOread_readSlice(self.dataset_id, self.type_id, irow, start, stop, idx.data) if ret < 0: raise HDF5ExtError("Problems reading the index indices.") _readIndexSlice = previous_api(_read_index_slice) def _init_sorted_slice(self, index): """Initialize the structures for doing a binary search.""" cdef long ndims cdef int rank, buflen, cachesize cdef char *bname cdef hsize_t count[2] cdef ndarray starts, lengths, rvcache cdef object maxslots, rowsize dtype = self.atom.dtype # Create the buffer for reading sorted data chunks if not created yet if self.bufferlb is None: # Internal buffers self.bufferlb = numpy.empty(dtype=dtype, shape=self.chunksize) # Get the pointers to the different buffer data areas self.rbuflb = self.bufferlb.data # Init structures for accelerating sorted array reads rank = 2 count[0] = 1 count[1] = self.chunksize self.mem_space_id = H5Screate_simple(rank, count, NULL) # Cache some counters in local extension variables self.l_chunksize = self.chunksize self.l_slicesize = self.slicesize # Get the addresses of buffer data starts = index.starts lengths = index.lengths self.rbufst = starts.data self.rbufln = lengths.data # The 1st cache is loaded completely in memory and needs to be reloaded rvcache = index.ranges[:] self.rbufrv = rvcache.data index.rvcache = rvcache # Init the bounds array for reading self.nbounds = index.bounds.shape[1] self.bounds_ext = index.bounds self.bounds_ext.initread(self.nbounds) if str(dtype) in self._v_parent.opt_search_types: # The next caches should be defined only for optimized search types. # The 2nd level cache will replace the already existing ObjectCache and # already bound to the boundscache attribute. This way, the cache will # not be duplicated (I know, this smells badly, but anyway). params = self._v_file.params rowsize = (self.bounds_ext._v_chunkshape[1] * dtype.itemsize) maxslots = params['BOUNDS_MAX_SIZE'] / rowsize self.boundscache = NumCache( (maxslots, self.nbounds), dtype, 'non-opt types bounds') self.bufferbc = numpy.empty(dtype=dtype, shape=self.nbounds) # Get the pointer for the internal buffer for 2nd level cache self.rbufbc = self.bufferbc.data # Another NumCache for the sorted values rowsize = (self.chunksize*dtype.itemsize) maxslots = params['SORTED_MAX_SIZE'] / (self.chunksize*dtype.itemsize) self.sortedcache = NumCache( (maxslots, self.chunksize), dtype, 'sorted') _initSortedSlice = previous_api(_init_sorted_slice) cdef void *_g_read_sorted_slice(self, hsize_t irow, hsize_t start, hsize_t stop): """Read the sorted part of an index.""" with nogil: ret = H5ARRAYOread_readSortedSlice( self.dataset_id, self.mem_space_id, self.type_id, irow, start, stop, self.rbuflb) if ret < 0: raise HDF5ExtError("Problems reading the array data.") return self.rbuflb # can't time machine since this function is cdef'd #_g_read_sorted_slice = prveious_api(_g_read_sorted_slice) # This is callable from python def _read_sorted_slice(self, hsize_t irow, hsize_t start, hsize_t stop): """Read the sorted part of an index.""" self._g_read_sorted_slice(irow, start, stop) return self.bufferlb _readSortedSlice = previous_api(_read_sorted_slice) # This has been copied from the standard module bisect. # Checks for the values out of limits has been added at the beginning # because I forsee that this should be a very common case. # 2004-05-20 def _bisect_left(self, a, x, int hi): """Return the index where to insert item x in list a, assuming a is sorted. The return value i is such that all e in a[:i] have e < x, and all e in a[i:] have e >= x. So if x already appears in the list, i points just before the leftmost x already there. """ cdef int lo, mid lo = 0 if x <= a[0]: return 0 if a[-1] < x: return hi while lo < hi: mid = (lo+hi)/2 if a[mid] < x: lo = mid+1 else: hi = mid return lo def _bisect_right(self, a, x, int hi): """Return the index where to insert item x in list a, assuming a is sorted. The return value i is such that all e in a[:i] have e <= x, and all e in a[i:] have e > x. So if x already appears in the list, i points just beyond the rightmost x already there. """ cdef int lo, mid lo = 0 if x < a[0]: return 0 if a[-1] <= x: return hi while lo < hi: mid = (lo+hi)/2 if x < a[mid]: hi = mid else: lo = mid+1 return lo cdef void *get_lru_bounds(self, int nrow, int nbounds): """Get the bounds from the cache, or read them.""" cdef void *vpointer cdef long nslot nslot = self.boundscache.getslot_(nrow) if nslot >= 0: vpointer = self.boundscache.getitem1_(nslot) else: # Bounds row is not in cache. Read it and put it in the LRU cache. self.bounds_ext.read_slice(nrow, 0, nbounds, self.rbufbc) self.boundscache.setitem_(nrow, self.rbufbc, 0) vpointer = self.rbufbc return vpointer # can't time machine since get_lru_bounds() function is cdef'd cdef void *get_lru_sorted(self, int nrow, int ncs, int nchunk, int cs): """Get the sorted row from the cache or read it.""" cdef void *vpointer cdef npy_int64 nckey cdef long nslot cdef hsize_t start, stop # Compute the number of chunk read and use it as the key for the cache. nckey = nrow*ncs+nchunk nslot = self.sortedcache.getslot_(nckey) if nslot >= 0: vpointer = self.sortedcache.getitem1_(nslot) else: # The sorted chunk is not in cache. Read it and put it in the LRU cache. start = cs*nchunk stop = cs*(nchunk+1) vpointer = self._g_read_sorted_slice(nrow, start, stop) self.sortedcache.setitem_(nckey, vpointer, 0) return vpointer # can't time machine since get_lru_sorted() function is cdef'd # Optimized version for int8 def _search_bin_na_b(self, long item1, long item2): cdef int cs, ss, ncs, nrow, nrows, nbounds, rvrow cdef int start, stop, tlength, length, bread, nchunk, nchunk2 cdef int *rbufst cdef int *rbufln # Variables with specific type cdef npy_int8 *rbufrv cdef npy_int8 *rbufbc = NULL cdef npy_int8 *rbuflb = NULL cs = self.l_chunksize ss = self.l_slicesize ncs = ss / cs nbounds = self.nbounds nrows = self.nrows rbufst = self.rbufst rbufln = self.rbufln rbufrv = self.rbufrv tlength = 0 for nrow from 0 <= nrow < nrows: rvrow = nrow*2 bread = 0 nchunk = -1 # Look if item1 is in this row if item1 > rbufrv[rvrow]: if item1 <= rbufrv[rvrow+1]: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) bread = 1 nchunk = bisect_left_b(rbufbc, item1, nbounds, 0) # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk, cs) start = bisect_left_b(rbuflb, item1, cs, 0) + cs*nchunk else: start = ss else: start = 0 # Now, for item2 if item2 >= rbufrv[rvrow]: if item2 < rbufrv[rvrow+1]: if not bread: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) nchunk2 = bisect_right_b(rbufbc, item2, nbounds, 0) if nchunk2 <> nchunk: # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk2, cs) stop = bisect_right_b(rbuflb, item2, cs, 0) + cs*nchunk2 else: stop = ss else: stop = 0 length = stop - start tlength = tlength + length rbufst[nrow] = start rbufln[nrow] = length return tlength _searchBinNA_b = previous_api(_search_bin_na_b) # Optimized version for uint8 def _search_bin_na_ub(self, long item1, long item2): cdef int cs, ss, ncs, nrow, nrows, nbounds, rvrow cdef int start, stop, tlength, length, bread, nchunk, nchunk2 cdef int *rbufst cdef int *rbufln # Variables with specific type cdef npy_uint8 *rbufrv cdef npy_uint8 *rbufbc = NULL cdef npy_uint8 *rbuflb = NULL cs = self.l_chunksize ss = self.l_slicesize ncs = ss / cs nbounds = self.nbounds nrows = self.nrows rbufst = self.rbufst rbufln = self.rbufln rbufrv = self.rbufrv tlength = 0 for nrow from 0 <= nrow < nrows: rvrow = nrow*2 bread = 0 nchunk = -1 # Look if item1 is in this row if item1 > rbufrv[rvrow]: if item1 <= rbufrv[rvrow+1]: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) bread = 1 nchunk = bisect_left_ub(rbufbc, item1, nbounds, 0) # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk, cs) start = bisect_left_ub(rbuflb, item1, cs, 0) + cs*nchunk else: start = ss else: start = 0 # Now, for item2 if item2 >= rbufrv[rvrow]: if item2 < rbufrv[rvrow+1]: if not bread: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) nchunk2 = bisect_right_ub(rbufbc, item2, nbounds, 0) if nchunk2 <> nchunk: # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk2, cs) stop = bisect_right_ub(rbuflb, item2, cs, 0) + cs*nchunk2 else: stop = ss else: stop = 0 length = stop - start tlength = tlength + length rbufst[nrow] = start rbufln[nrow] = length return tlength _searchBinNA_ub = previous_api(_search_bin_na_ub) # Optimized version for int16 def _search_bin_na_s(self, long item1, long item2): cdef int cs, ss, ncs, nrow, nrows, nbounds, rvrow cdef int start, stop, tlength, length, bread, nchunk, nchunk2 cdef int *rbufst cdef int *rbufln # Variables with specific type cdef npy_int16 *rbufrv cdef npy_int16 *rbufbc = NULL cdef npy_int16 *rbuflb = NULL cs = self.l_chunksize ss = self.l_slicesize ncs = ss / cs nbounds = self.nbounds nrows = self.nrows rbufst = self.rbufst rbufln = self.rbufln rbufrv = self.rbufrv tlength = 0 for nrow from 0 <= nrow < nrows: rvrow = nrow*2 bread = 0 nchunk = -1 # Look if item1 is in this row if item1 > rbufrv[rvrow]: if item1 <= rbufrv[rvrow+1]: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) bread = 1 nchunk = bisect_left_s(rbufbc, item1, nbounds, 0) # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk, cs) start = bisect_left_s(rbuflb, item1, cs, 0) + cs*nchunk else: start = ss else: start = 0 # Now, for item2 if item2 >= rbufrv[rvrow]: if item2 < rbufrv[rvrow+1]: if not bread: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) nchunk2 = bisect_right_s(rbufbc, item2, nbounds, 0) if nchunk2 <> nchunk: # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk2, cs) stop = bisect_right_s(rbuflb, item2, cs, 0) + cs*nchunk2 else: stop = ss else: stop = 0 length = stop - start tlength = tlength + length rbufst[nrow] = start rbufln[nrow] = length return tlength _searchBinNA_s = previous_api(_search_bin_na_s) # Optimized version for uint16 def _search_bin_na_us(self, long item1, long item2): cdef int cs, ss, ncs, nrow, nrows, nbounds, rvrow cdef int start, stop, tlength, length, bread, nchunk, nchunk2 cdef int *rbufst cdef int *rbufln # Variables with specific type cdef npy_uint16 *rbufrv cdef npy_uint16 *rbufbc = NULL cdef npy_uint16 *rbuflb = NULL cs = self.l_chunksize ss = self.l_slicesize ncs = ss / cs nbounds = self.nbounds nrows = self.nrows rbufst = self.rbufst rbufln = self.rbufln rbufrv = self.rbufrv tlength = 0 for nrow from 0 <= nrow < nrows: rvrow = nrow*2 bread = 0 nchunk = -1 # Look if item1 is in this row if item1 > rbufrv[rvrow]: if item1 <= rbufrv[rvrow+1]: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) bread = 1 nchunk = bisect_left_us(rbufbc, item1, nbounds, 0) # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk, cs) start = bisect_left_us(rbuflb, item1, cs, 0) + cs*nchunk else: start = ss else: start = 0 # Now, for item2 if item2 >= rbufrv[rvrow]: if item2 < rbufrv[rvrow+1]: if not bread: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) nchunk2 = bisect_right_us(rbufbc, item2, nbounds, 0) if nchunk2 <> nchunk: # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk2, cs) stop = bisect_right_us(rbuflb, item2, cs, 0) + cs*nchunk2 else: stop = ss else: stop = 0 length = stop - start tlength = tlength + length rbufst[nrow] = start rbufln[nrow] = length return tlength _searchBinNA_us = previous_api(_search_bin_na_us) # Optimized version for int32 def _search_bin_na_i(self, long item1, long item2): cdef int cs, ss, ncs, nrow, nrows, nbounds, rvrow cdef int start, stop, tlength, length, bread, nchunk, nchunk2 cdef int *rbufst cdef int *rbufln # Variables with specific type cdef npy_int32 *rbufrv cdef npy_int32 *rbufbc = NULL cdef npy_int32 *rbuflb = NULL cs = self.l_chunksize ss = self.l_slicesize ncs = ss / cs nbounds = self.nbounds nrows = self.nrows rbufst = self.rbufst rbufln = self.rbufln rbufrv = self.rbufrv tlength = 0 for nrow from 0 <= nrow < nrows: rvrow = nrow*2 bread = 0 nchunk = -1 # Look if item1 is in this row if item1 > rbufrv[rvrow]: if item1 <= rbufrv[rvrow+1]: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) bread = 1 nchunk = bisect_left_i(rbufbc, item1, nbounds, 0) # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk, cs) start = bisect_left_i(rbuflb, item1, cs, 0) + cs*nchunk else: start = ss else: start = 0 # Now, for item2 if item2 >= rbufrv[rvrow]: if item2 < rbufrv[rvrow+1]: if not bread: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) nchunk2 = bisect_right_i(rbufbc, item2, nbounds, 0) if nchunk2 <> nchunk: # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk2, cs) stop = bisect_right_i(rbuflb, item2, cs, 0) + cs*nchunk2 else: stop = ss else: stop = 0 length = stop - start tlength = tlength + length rbufst[nrow] = start rbufln[nrow] = length return tlength _searchBinNA_i = previous_api(_search_bin_na_i) # Optimized version for uint32 def _search_bin_na_ui(self, npy_uint32 item1, npy_uint32 item2): cdef int cs, ss, ncs, nrow, nrows, nbounds, rvrow cdef int start, stop, tlength, length, bread, nchunk, nchunk2 cdef int *rbufst cdef int *rbufln # Variables with specific type cdef npy_uint32 *rbufrv cdef npy_uint32 *rbufbc = NULL cdef npy_uint32 *rbuflb = NULL cs = self.l_chunksize ss = self.l_slicesize ncs = ss / cs nbounds = self.nbounds nrows = self.nrows rbufst = self.rbufst rbufln = self.rbufln rbufrv = self.rbufrv tlength = 0 for nrow from 0 <= nrow < nrows: rvrow = nrow*2 bread = 0 nchunk = -1 # Look if item1 is in this row if item1 > rbufrv[rvrow]: if item1 <= rbufrv[rvrow+1]: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) bread = 1 nchunk = bisect_left_ui(rbufbc, item1, nbounds, 0) # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk, cs) start = bisect_left_ui(rbuflb, item1, cs, 0) + cs*nchunk else: start = ss else: start = 0 # Now, for item2 if item2 >= rbufrv[rvrow]: if item2 < rbufrv[rvrow+1]: if not bread: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) nchunk2 = bisect_right_ui(rbufbc, item2, nbounds, 0) if nchunk2 <> nchunk: # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk2, cs) stop = bisect_right_ui(rbuflb, item2, cs, 0) + cs*nchunk2 else: stop = ss else: stop = 0 length = stop - start tlength = tlength + length rbufst[nrow] = start rbufln[nrow] = length return tlength _searchBinNA_ui = previous_api(_search_bin_na_ui) # Optimized version for int64 def _search_bin_na_ll(self, npy_int64 item1, npy_int64 item2): cdef int cs, ss, ncs, nrow, nrows, nbounds, rvrow cdef int start, stop, tlength, length, bread, nchunk, nchunk2 cdef int *rbufst cdef int *rbufln # Variables with specific type cdef npy_int64 *rbufrv cdef npy_int64 *rbufbc = NULL cdef npy_int64 *rbuflb = NULL cs = self.l_chunksize ss = self.l_slicesize ncs = ss / cs nbounds = self.nbounds nrows = self.nrows rbufst = self.rbufst rbufln = self.rbufln rbufrv = self.rbufrv tlength = 0 for nrow from 0 <= nrow < nrows: rvrow = nrow*2 bread = 0 nchunk = -1 # Look if item1 is in this row if item1 > rbufrv[rvrow]: if item1 <= rbufrv[rvrow+1]: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) bread = 1 nchunk = bisect_left_ll(rbufbc, item1, nbounds, 0) # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk, cs) start = bisect_left_ll(rbuflb, item1, cs, 0) + cs*nchunk else: start = ss else: start = 0 # Now, for item2 if item2 >= rbufrv[rvrow]: if item2 < rbufrv[rvrow+1]: if not bread: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) nchunk2 = bisect_right_ll(rbufbc, item2, nbounds, 0) if nchunk2 <> nchunk: # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk2, cs) stop = bisect_right_ll(rbuflb, item2, cs, 0) + cs*nchunk2 else: stop = ss else: stop = 0 length = stop - start tlength = tlength + length rbufst[nrow] = start rbufln[nrow] = length return tlength _searchBinNA_ll = previous_api(_search_bin_na_ll) # Optimized version for uint64 def _search_bin_na_ull(self, npy_uint64 item1, npy_uint64 item2): cdef int cs, ss, ncs, nrow, nrows, nbounds, rvrow cdef int start, stop, tlength, length, bread, nchunk, nchunk2 cdef int *rbufst cdef int *rbufln # Variables with specific type cdef npy_uint64 *rbufrv cdef npy_uint64 *rbufbc = NULL cdef npy_uint64 *rbuflb = NULL cs = self.l_chunksize ss = self.l_slicesize ncs = ss / cs nbounds = self.nbounds nrows = self.nrows rbufst = self.rbufst rbufln = self.rbufln rbufrv = self.rbufrv tlength = 0 for nrow from 0 <= nrow < nrows: rvrow = nrow*2 bread = 0 nchunk = -1 # Look if item1 is in this row if item1 > rbufrv[rvrow]: if item1 <= rbufrv[rvrow+1]: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) bread = 1 nchunk = bisect_left_ull(rbufbc, item1, nbounds, 0) # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk, cs) start = bisect_left_ull(rbuflb, item1, cs, 0) + cs*nchunk else: start = ss else: start = 0 # Now, for item2 if item2 >= rbufrv[rvrow]: if item2 < rbufrv[rvrow+1]: if not bread: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) nchunk2 = bisect_right_ull(rbufbc, item2, nbounds, 0) if nchunk2 <> nchunk: # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk2, cs) stop = bisect_right_ull(rbuflb, item2, cs, 0) + cs*nchunk2 else: stop = ss else: stop = 0 length = stop - start tlength = tlength + length rbufst[nrow] = start rbufln[nrow] = length return tlength _searchBinNA_ull = previous_api(_search_bin_na_ull) # Optimized version for float16 def _search_bin_na_e(self, npy_float64 item1, npy_float64 item2): cdef int cs, ss, ncs, nrow, nrows, nrow2, nbounds, rvrow cdef int start, stop, tlength, length, bread, nchunk, nchunk2 cdef int *rbufst cdef int *rbufln # Variables with specific type cdef npy_float16 *rbufrv cdef npy_float16 *rbufbc = NULL cdef npy_float16 *rbuflb = NULL cs = self.l_chunksize ss = self.l_slicesize ncs = ss / cs nbounds = self.nbounds nrows = self.nrows tlength = 0 rbufst = self.rbufst rbufln = self.rbufln # Limits not in cache, do a lookup rbufrv = self.rbufrv for nrow from 0 <= nrow < nrows: rvrow = nrow*2 bread = 0 nchunk = -1 # Look if item1 is in this row if item1 > rbufrv[rvrow]: if item1 <= rbufrv[rvrow+1]: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) bread = 1 nchunk = bisect_left_e(rbufbc, item1, nbounds, 0) # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk, cs) start = bisect_left_e(rbuflb, item1, cs, 0) + cs*nchunk else: start = ss else: start = 0 # Now, for item2 if item2 >= rbufrv[rvrow]: if item2 < rbufrv[rvrow+1]: if not bread: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) nchunk2 = bisect_right_e(rbufbc, item2, nbounds, 0) if nchunk2 <> nchunk: # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk2, cs) stop = bisect_right_e(rbuflb, item2, cs, 0) + cs*nchunk2 else: stop = ss else: stop = 0 length = stop - start tlength = tlength + length rbufst[nrow] = start rbufln[nrow] = length return tlength _searchBinNA_e = previous_api(_search_bin_na_e) # Optimized version for float32 def _search_bin_na_f(self, npy_float64 item1, npy_float64 item2): cdef int cs, ss, ncs, nrow, nrows, nrow2, nbounds, rvrow cdef int start, stop, tlength, length, bread, nchunk, nchunk2 cdef int *rbufst cdef int *rbufln # Variables with specific type cdef npy_float32 *rbufrv cdef npy_float32 *rbufbc = NULL cdef npy_float32 *rbuflb = NULL cs = self.l_chunksize ss = self.l_slicesize ncs = ss / cs nbounds = self.nbounds nrows = self.nrows tlength = 0 rbufst = self.rbufst rbufln = self.rbufln # Limits not in cache, do a lookup rbufrv = self.rbufrv for nrow from 0 <= nrow < nrows: rvrow = nrow*2 bread = 0 nchunk = -1 # Look if item1 is in this row if item1 > rbufrv[rvrow]: if item1 <= rbufrv[rvrow+1]: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) bread = 1 nchunk = bisect_left_f(rbufbc, item1, nbounds, 0) # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk, cs) start = bisect_left_f(rbuflb, item1, cs, 0) + cs*nchunk else: start = ss else: start = 0 # Now, for item2 if item2 >= rbufrv[rvrow]: if item2 < rbufrv[rvrow+1]: if not bread: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) nchunk2 = bisect_right_f(rbufbc, item2, nbounds, 0) if nchunk2 <> nchunk: # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk2, cs) stop = bisect_right_f(rbuflb, item2, cs, 0) + cs*nchunk2 else: stop = ss else: stop = 0 length = stop - start tlength = tlength + length rbufst[nrow] = start rbufln[nrow] = length return tlength _searchBinNA_f = previous_api(_search_bin_na_f) # Optimized version for float64 def _search_bin_na_d(self, npy_float64 item1, npy_float64 item2): cdef int cs, ss, ncs, nrow, nrows, nrow2, nbounds, rvrow cdef int start, stop, tlength, length, bread, nchunk, nchunk2 cdef int *rbufst cdef int *rbufln # Variables with specific type cdef npy_float64 *rbufrv cdef npy_float64 *rbufbc = NULL cdef npy_float64 *rbuflb = NULL cs = self.l_chunksize ss = self.l_slicesize ncs = ss / cs nbounds = self.nbounds nrows = self.nrows tlength = 0 rbufst = self.rbufst rbufln = self.rbufln # Limits not in cache, do a lookup rbufrv = self.rbufrv for nrow from 0 <= nrow < nrows: rvrow = nrow*2 bread = 0 nchunk = -1 # Look if item1 is in this row if item1 > rbufrv[rvrow]: if item1 <= rbufrv[rvrow+1]: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) bread = 1 nchunk = bisect_left_d(rbufbc, item1, nbounds, 0) # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk, cs) start = bisect_left_d(rbuflb, item1, cs, 0) + cs*nchunk else: start = ss else: start = 0 # Now, for item2 if item2 >= rbufrv[rvrow]: if item2 < rbufrv[rvrow+1]: if not bread: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) nchunk2 = bisect_right_d(rbufbc, item2, nbounds, 0) if nchunk2 <> nchunk: # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk2, cs) stop = bisect_right_d(rbuflb, item2, cs, 0) + cs*nchunk2 else: stop = ss else: stop = 0 length = stop - start tlength = tlength + length rbufst[nrow] = start rbufln[nrow] = length return tlength _searchBinNA_d = previous_api(_search_bin_na_d) # Optimized version for npy_longdouble/float96/float128 def _search_bin_na_g(self, npy_longdouble item1, npy_longdouble item2): cdef int cs, ss, ncs, nrow, nrows, nrow2, nbounds, rvrow cdef int start, stop, tlength, length, bread, nchunk, nchunk2 cdef int *rbufst cdef int *rbufln # Variables with specific type cdef npy_longdouble *rbufrv cdef npy_longdouble *rbufbc = NULL cdef npy_longdouble *rbuflb = NULL cs = self.l_chunksize ss = self.l_slicesize ncs = ss / cs nbounds = self.nbounds nrows = self.nrows tlength = 0 rbufst = self.rbufst rbufln = self.rbufln # Limits not in cache, do a lookup rbufrv = self.rbufrv for nrow from 0 <= nrow < nrows: rvrow = nrow*2 bread = 0 nchunk = -1 # Look if item1 is in this row if item1 > rbufrv[rvrow]: if item1 <= rbufrv[rvrow+1]: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) bread = 1 nchunk = bisect_left_g(rbufbc, item1, nbounds, 0) # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk, cs) start = bisect_left_g(rbuflb, item1, cs, 0) + cs*nchunk else: start = ss else: start = 0 # Now, for item2 if item2 >= rbufrv[rvrow]: if item2 < rbufrv[rvrow+1]: if not bread: # Get the bounds row from the LRU cache or read them. rbufbc = self.get_lru_bounds(nrow, nbounds) nchunk2 = bisect_right_g(rbufbc, item2, nbounds, 0) if nchunk2 <> nchunk: # Get the sorted row from the LRU cache or read it. rbuflb = self.get_lru_sorted(nrow, ncs, nchunk2, cs) stop = bisect_right_g(rbuflb, item2, cs, 0) + cs*nchunk2 else: stop = ss else: stop = 0 length = stop - start tlength = tlength + length rbufst[nrow] = start rbufln[nrow] = length return tlength _searchBinNA_g = previous_api(_search_bin_na_g) def _g_close(self): super(Array, self)._g_close() # Release specific resources of this class if self.mem_space_id > 0: H5Sclose(self.mem_space_id) cdef class LastRowArray(Array): """ Container for keeping sorted and indices values of last rows of an index. """ def _read_index_slice(self, hsize_t start, hsize_t stop, ndarray idx): """Read the reverse index part of an LR index.""" with nogil: ret = H5ARRAYOreadSliceLR(self.dataset_id, self.type_id, start, stop, idx.data) if ret < 0: raise HDF5ExtError("Problems reading the index data in Last Row.") _readIndexSlice = previous_api(_read_index_slice) def _read_sorted_slice(self, IndexArray sorted, hsize_t start, hsize_t stop): """Read the sorted part of an LR index.""" cdef void *rbuflb rbuflb = sorted.rbuflb # direct access to rbuflb: very fast. with nogil: ret = H5ARRAYOreadSliceLR(self.dataset_id, self.type_id, start, stop, rbuflb) if ret < 0: raise HDF5ExtError("Problems reading the index data.") return sorted.bufferlb[:stop-start] _readSortedSlice = previous_api(_read_sorted_slice) ## Local Variables: ## mode: python ## py-indent-offset: 2 ## tab-width: 2 ## fill-column: 78 ## End: PyTables-v.3.1.1/tables/leaf.py000066400000000000000000000667151231437614300162610ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: October 14, 2002 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Here is defined the Leaf class.""" import warnings import math import numpy from tables.flavor import (check_flavor, internal_flavor, alias_map as flavor_alias_map) from tables.node import Node from tables.filters import Filters from tables.utils import byteorders, lazyattr, SizeType from tables.exceptions import PerformanceWarning from tables import utilsextension from tables._past import previous_api def csformula(expected_mb): """Return the fitted chunksize for expected_mb.""" # For a basesize of 8 KB, this will return: # 8 KB for datasets <= 1 MB # 1 MB for datasets >= 10 TB basesize = 8 * 1024 # 8 KB is a good minimum return basesize * int(2**math.log10(expected_mb)) def limit_es(expected_mb): """Protection against creating too small or too large chunks.""" if expected_mb < 1: # < 1 MB expected_mb = 1 elif expected_mb > 10**7: # > 10 TB expected_mb = 10**7 return expected_mb def calc_chunksize(expected_mb): """Compute the optimum HDF5 chunksize for I/O purposes. Rational: HDF5 takes the data in bunches of chunksize length to write the on disk. A BTree in memory is used to map structures on disk. The more chunks that are allocated for a dataset the larger the B-tree. Large B-trees take memory and causes file storage overhead as well as more disk I/O and higher contention for the meta data cache. You have to balance between memory and I/O overhead (small B-trees) and time to access to data (big B-trees). The tuning of the chunksize parameter affects the performance and the memory consumed. This is based on my own experiments and, as always, your mileage may vary. """ expected_mb = limit_es(expected_mb) zone = int(math.log10(expected_mb)) expected_mb = 10**zone chunksize = csformula(expected_mb) return chunksize * 8 # XXX: Multiply by 8 seems optimal for # sequential access class Leaf(Node): """Abstract base class for all PyTables leaves. A leaf is a node (see the Node class in :class:`Node`) which hangs from a group (see the Group class in :class:`Group`) but, unlike a group, it can not have any further children below it (i.e. it is an end node). This definition includes all nodes which contain actual data (datasets handled by the Table - see :ref:`TableClassDescr`, Array - see :ref:`ArrayClassDescr`, CArray - see :ref:`CArrayClassDescr`, EArray - see :ref:`EArrayClassDescr`, and VLArray - see :ref:`VLArrayClassDescr` classes) and unsupported nodes (the UnImplemented class - :ref:`UnImplementedClassDescr`) these classes do in fact inherit from Leaf. .. rubric:: Leaf attributes These instance variables are provided in addition to those in Node (see :ref:`NodeClassDescr`): .. attribute:: byteorder The byte ordering of the leaf data *on disk*. It will be either ``little`` or ``big``. .. attribute:: dtype The NumPy dtype that most closely matches this leaf type. .. attribute:: extdim The index of the enlargeable dimension (-1 if none). .. attribute:: nrows The length of the main dimension of the leaf data. .. attribute:: nrowsinbuf The number of rows that fit in internal input buffers. You can change this to fine-tune the speed or memory requirements of your application. .. attribute:: shape The shape of data in the leaf. """ # Properties # ~~~~~~~~~~ # Node property aliases # ````````````````````` # These are a little hard to override, but so are properties. attrs = Node._v_attrs """The associated AttributeSet instance - see :ref:`AttributeSetClassDescr` (This is an easier-to-write alias of :attr:`Node._v_attrs`.""" title = Node._v_title """A description for this node (This is an easier-to-write alias of :attr:`Node._v_title`).""" # Read-only node property aliases # ``````````````````````````````` name = property( lambda self: self._v_name, None, None, """The name of this node in its parent group (This is an easier-to-write alias of :attr:`Node._v_name`).""") chunkshape = property( lambda self: getattr(self, '_v_chunkshape', None), None, None, """The HDF5 chunk size for chunked leaves (a tuple). This is read-only because you cannot change the chunk size of a leaf once it has been created. """) object_id = property( lambda self: self._v_objectid, None, None, """A node identifier, which may change from run to run. (This is an easier-to-write alias of :attr:`Node._v_objectid`). .. versionchanged:: 3.0 The *objectID* property has been renamed into *object_id*. """) objectID = previous_api(object_id) ndim = property( lambda self: len(self.shape), None, None, """The number of dimensions of the leaf data. .. versionadded: 2.4""") # Lazy read-only attributes # ````````````````````````` @lazyattr def filters(self): """Filter properties for this leaf. See Also -------- Filters """ return Filters._from_leaf(self) # Other properties # ```````````````` def _getmaindim(self): if self.extdim < 0: return 0 # choose the first dimension return self.extdim maindim = property( _getmaindim, None, None, """The dimension along which iterators work. Its value is 0 (i.e. the first dimension) when the dataset is not extendable, and self.extdim (where available) for extendable ones. """) def _setflavor(self, flavor): self._v_file._check_writable() check_flavor(flavor) self._v_attrs.FLAVOR = self._flavor = flavor # logs the change def _delflavor(self): del self._v_attrs.FLAVOR self._flavor = internal_flavor flavor = property( lambda self: self._flavor, _setflavor, _delflavor, """The type of data object read from this leaf. It can be any of 'numpy' or 'python'. You can (and are encouraged to) use this property to get, set and delete the FLAVOR HDF5 attribute of the leaf. When the leaf has no such attribute, the default flavor is used.. """) size_on_disk = property(lambda self: self._get_storage_size(), None, None, """ The size of this leaf's data in bytes as it is stored on disk. If the data is compressed, this shows the compressed size. In the case of uncompressed, chunked data, this may be slightly larger than the amount of data, due to partially filled chunks. """) # Special methods # ~~~~~~~~~~~~~~~ def __init__(self, parentnode, name, new=False, filters=None, byteorder=None, _log=True): self._v_new = new """Is this the first time the node has been created?""" self.nrowsinbuf = None """ The number of rows that fits in internal input buffers. You can change this to fine-tune the speed or memory requirements of your application. """ self._flavor = None """Private storage for the `flavor` property.""" if new: # Get filter properties from parent group if not given. if filters is None: filters = parentnode._v_filters self.__dict__['filters'] = filters # bypass the property if byteorder not in (None, 'little', 'big'): raise ValueError( "the byteorder can only take 'little' or 'big' values " "and you passed: %s" % byteorder) self.byteorder = byteorder """The byte ordering of the leaf data *on disk*.""" # Existing filters need not be read since `filters` # is a lazy property that automatically handles their loading. super(Leaf, self).__init__(parentnode, name, _log) def __len__(self): """Return the length of the main dimension of the leaf data. Please note that this may raise an OverflowError on 32-bit platforms for datasets having more than 2**31-1 rows. This is a limitation of Python that you can work around by using the nrows or shape attributes. """ return self.nrows def __str__(self): """The string representation for this object is its pathname in the HDF5 object tree plus some additional metainfo.""" # Get this class name classname = self.__class__.__name__ # The title title = self._v_title # The filters filters = "" if self.filters.fletcher32: filters += ", fletcher32" if self.filters.complevel: if self.filters.shuffle: filters += ", shuffle" filters += ", %s(%s)" % (self.filters.complib, self.filters.complevel) return "%s (%s%s%s) %r" % \ (self._v_pathname, classname, self.shape, filters, title) # Private methods # ~~~~~~~~~~~~~~~ def _g_post_init_hook(self): """Code to be run after node creation and before creation logging. This method gets or sets the flavor of the leaf. """ super(Leaf, self)._g_post_init_hook() if self._v_new: # set flavor of new node if self._flavor is None: self._flavor = internal_flavor else: # flavor set at creation time, do not log if self._v_file.params['PYTABLES_SYS_ATTRS']: self._v_attrs._g__setattr('FLAVOR', self._flavor) else: # get flavor of existing node (if any) if self._v_file.params['PYTABLES_SYS_ATTRS']: flavor = getattr(self._v_attrs, 'FLAVOR', internal_flavor) self._flavor = flavor_alias_map.get(flavor, flavor) else: self._flavor = internal_flavor _g_postInitHook = previous_api(_g_post_init_hook) def _calc_chunkshape(self, expectedrows, rowsize, itemsize): """Calculate the shape for the HDF5 chunk.""" # In case of a scalar shape, return the unit chunksize if self.shape == (): return (SizeType(1),) # Compute the chunksize MB = 1024 * 1024 expected_mb = (expectedrows * rowsize) // MB chunksize = calc_chunksize(expected_mb) maindim = self.maindim # Compute the chunknitems chunknitems = chunksize // itemsize # Safeguard against itemsizes being extremely large if chunknitems == 0: chunknitems = 1 chunkshape = list(self.shape) # Check whether trimming the main dimension is enough chunkshape[maindim] = 1 newchunknitems = numpy.prod(chunkshape, dtype=SizeType) if newchunknitems <= chunknitems: chunkshape[maindim] = chunknitems // newchunknitems else: # No, so start trimming other dimensions as well for j in xrange(len(chunkshape)): # Check whether trimming this dimension is enough chunkshape[j] = 1 newchunknitems = numpy.prod(chunkshape, dtype=SizeType) if newchunknitems <= chunknitems: chunkshape[j] = chunknitems // newchunknitems break else: # Ops, we ran out of the loop without a break # Set the last dimension to chunknitems chunkshape[-1] = chunknitems return tuple(SizeType(s) for s in chunkshape) def _calc_nrowsinbuf(self): """Calculate the number of rows that fits on a PyTables buffer.""" params = self._v_file.params # Compute the nrowsinbuf rowsize = self.rowsize buffersize = params['IO_BUFFER_SIZE'] nrowsinbuf = buffersize // rowsize # tableextension.pyx performs an assertion # to make sure nrowsinbuf is greater than or # equal to the chunksize. # See gh-206 and gh-238 if self.chunkshape is not None: chunksize = self.chunkshape[self.maindim] if nrowsinbuf < chunksize: nrowsinbuf = chunksize # Safeguard against row sizes being extremely large if nrowsinbuf == 0: nrowsinbuf = 1 # If rowsize is too large, issue a Performance warning maxrowsize = params['BUFFER_TIMES'] * buffersize if rowsize > maxrowsize: warnings.warn("""\ The Leaf ``%s`` is exceeding the maximum recommended rowsize (%d bytes); be ready to see PyTables asking for *lots* of memory and possibly slow I/O. You may want to reduce the rowsize by trimming the value of dimensions that are orthogonal (and preferably close) to the *main* dimension of this leave. Alternatively, in case you have specified a very small/large chunksize, you may want to increase/decrease it.""" % (self._v_pathname, maxrowsize), PerformanceWarning) return nrowsinbuf # This method is appropriate for calls to __getitem__ methods def _process_range(self, start, stop, step, dim=None, warn_negstep=True): if dim is None: nrows = self.nrows # self.shape[self.maindim] else: nrows = self.shape[dim] if warn_negstep and step and step < 0: raise ValueError("slice step cannot be negative") # (start, stop, step) = slice(start, stop, step).indices(nrows) # The next function is a substitute for slice().indices in order to # support full 64-bit integer for slices even in 32-bit machines. # F. Alted 2005-05-08 start, stop, step = utilsextension.get_indices(start, stop, step, long(nrows)) return (start, stop, step) _processRange = previous_api(_process_range) # This method is appropiate for calls to read() methods def _process_range_read(self, start, stop, step, warn_negstep=True): nrows = self.nrows if start is None and stop is None: #if start is None and stop is None and step is None: start = 0 stop = nrows #step = 1 if start is not None and stop is None and step is None: # Protection against start greater than available records # nrows == 0 is a special case for empty objects if nrows > 0 and start >= nrows: raise IndexError("start of range (%s) is greater than " "number of rows (%s)" % (start, nrows)) step = 1 if start == -1: # corner case stop = nrows else: stop = start + 1 # Finally, get the correct values (over the main dimension) start, stop, step = self._process_range(start, stop, step, warn_negstep=warn_negstep) return (start, stop, step) _processRangeRead = previous_api(_process_range_read) def _g_copy(self, newparent, newname, recursive, _log=True, **kwargs): # Compute default arguments. start = kwargs.pop('start', None) stop = kwargs.pop('stop', None) step = kwargs.pop('step', None) title = kwargs.pop('title', self._v_title) filters = kwargs.pop('filters', self.filters) chunkshape = kwargs.pop('chunkshape', self.chunkshape) copyuserattrs = kwargs.pop('copyuserattrs', True) stats = kwargs.pop('stats', None) if chunkshape == 'keep': chunkshape = self.chunkshape # Keep the original chunkshape elif chunkshape == 'auto': chunkshape = None # Will recompute chunkshape # Fix arguments with explicit None values for backwards compatibility. if title is None: title = self._v_title if filters is None: filters = self.filters # Create a copy of the object. (new_node, bytes) = self._g_copy_with_stats( newparent, newname, start, stop, step, title, filters, chunkshape, _log, **kwargs) # Copy user attributes if requested (or the flavor at least). if copyuserattrs: self._v_attrs._g_copy(new_node._v_attrs, copyclass=True) elif 'FLAVOR' in self._v_attrs: if self._v_file.params['PYTABLES_SYS_ATTRS']: new_node._v_attrs._g__setattr('FLAVOR', self._flavor) new_node._flavor = self._flavor # update cached value # Update statistics if needed. if stats is not None: stats['leaves'] += 1 stats['bytes'] += bytes return new_node def _g_fix_byteorder_data(self, data, dbyteorder): "Fix the byteorder of data passed in constructors." dbyteorder = byteorders[dbyteorder] # If self.byteorder has not been passed as an argument of # the constructor, then set it to the same value of data. if self.byteorder is None: self.byteorder = dbyteorder # Do an additional in-place byteswap of data if the in-memory # byteorder doesn't match that of the on-disk. This is the only # place that we have to do the conversion manually. In all the # other cases, it will be HDF5 the responsible of doing the # byteswap properly. if dbyteorder in ['little', 'big']: if dbyteorder != self.byteorder: # if data is not writeable, do a copy first if not data.flags.writeable: data = data.copy() data.byteswap(True) else: # Fix the byteorder again, no matter which byteorder have # specified the user in the constructor. self.byteorder = "irrelevant" return data def _point_selection(self, key): """Perform a point-wise selection. `key` can be any of the following items: * A boolean array with the same shape than self. Those positions with True values will signal the coordinates to be returned. * A numpy array (or list or tuple) with the point coordinates. This has to be a two-dimensional array of size len(self.shape) by num_elements containing a list of of zero-based values specifying the coordinates in the dataset of the selected elements. The order of the element coordinates in the array specifies the order in which the array elements are iterated through when I/O is performed. Duplicate coordinate locations are not checked for. Return the coordinates array. If this is not possible, raise a `TypeError` so that the next selection method can be tried out. This is useful for whatever `Leaf` instance implementing a point-wise selection. """ if type(key) in (list, tuple): if isinstance(key, tuple) and len(key) > len(self.shape): raise IndexError("Invalid index or slice: %r" % (key,)) # Try to convert key to a numpy array. If not possible, # a TypeError will be issued (to be catched later on). try: key = numpy.array(key) except ValueError: raise TypeError("Invalid index or slice: %r" % (key,)) elif not isinstance(key, numpy.ndarray): raise TypeError("Invalid index or slice: %r" % (key,)) # Protection against empty keys if len(key) == 0: return numpy.array([], dtype="i8") if key.dtype.kind == 'b': if not key.shape == self.shape: raise IndexError( "Boolean indexing array has incompatible shape") # Get the True coordinates (64-bit indices!) coords = numpy.asarray(key.nonzero(), dtype='i8') coords = numpy.transpose(coords) elif key.dtype.kind == 'i' or key.dtype.kind == 'u': if len(key.shape) > 2: raise IndexError( "Coordinate indexing array has incompatible shape") elif len(key.shape) == 2: if key.shape[0] != len(self.shape): raise IndexError( "Coordinate indexing array has incompatible shape") coords = numpy.asarray(key, dtype="i8") coords = numpy.transpose(coords) else: # For 1-dimensional datasets coords = numpy.asarray(key, dtype="i8") else: raise TypeError("Only integer coordinates allowed.") # We absolutely need a contiguous array if not coords.flags.contiguous: coords = coords.copy() return coords _pointSelection = previous_api(_point_selection) # Public methods # ~~~~~~~~~~~~~~ # Tree manipulation # ````````````````` def remove(self): """Remove this node from the hierarchy. This method has the behavior described in :meth:`Node._f_remove`. Please note that there is no recursive flag since leaves do not have child nodes. """ self._f_remove(False) def rename(self, newname): """Rename this node in place. This method has the behavior described in :meth:`Node._f_rename()`. """ self._f_rename(newname) def move(self, newparent=None, newname=None, overwrite=False, createparents=False): """Move or rename this node. This method has the behavior described in :meth:`Node._f_move` """ self._f_move(newparent, newname, overwrite, createparents) def copy(self, newparent=None, newname=None, overwrite=False, createparents=False, **kwargs): """Copy this node and return the new one. This method has the behavior described in :meth:`Node._f_copy`. Please note that there is no recursive flag since leaves do not have child nodes. .. warning:: Note that unknown parameters passed to this method will be ignored, so may want to double check the spelling of these (i.e. if you write them incorrectly, they will most probably be ignored). Parameters ---------- title The new title for the destination. If omitted or None, the original title is used. filters : Filters Specifying this parameter overrides the original filter properties in the source node. If specified, it must be an instance of the Filters class (see :ref:`FiltersClassDescr`). The default is to copy the filter properties from the source node. copyuserattrs You can prevent the user attributes from being copied by setting this parameter to False. The default is to copy them. start, stop, step : int Specify the range of rows to be copied; the default is to copy all the rows. stats This argument may be used to collect statistics on the copy process. When used, it should be a dictionary with keys 'groups', 'leaves' and 'bytes' having a numeric value. Their values will be incremented to reflect the number of groups, leaves and bytes, respectively, that have been copied during the operation. chunkshape The chunkshape of the new leaf. It supports a couple of special values. A value of keep means that the chunkshape will be the same than original leaf (this is the default). A value of auto means that a new shape will be computed automatically in order to ensure best performance when accessing the dataset through the main dimension. Any other value should be an integer or a tuple matching the dimensions of the leaf. """ return self._f_copy( newparent, newname, overwrite, createparents, **kwargs) def truncate(self, size): """Truncate the main dimension to be size rows. If the main dimension previously was larger than this size, the extra data is lost. If the main dimension previously was shorter, it is extended, and the extended part is filled with the default values. The truncation operation can only be applied to *enlargeable* datasets, else a TypeError will be raised. """ # A non-enlargeable arrays (Array, CArray) cannot be truncated if self.extdim < 0: raise TypeError("non-enlargeable datasets cannot be truncated") self._g_truncate(size) def isvisible(self): """Is this node visible? This method has the behavior described in :meth:`Node._f_isvisible()`. """ return self._f_isvisible() isVisible = previous_api(isvisible) # Attribute handling # `````````````````` def get_attr(self, name): """Get a PyTables attribute from this node. This method has the behavior described in :meth:`Node._f_getattr`. """ return self._f_getattr(name) getAttr = previous_api(get_attr) def set_attr(self, name, value): """Set a PyTables attribute for this node. This method has the behavior described in :meth:`Node._f_setattr()`. """ self._f_setattr(name, value) setAttr = previous_api(set_attr) def del_attr(self, name): """Delete a PyTables attribute from this node. This method has the behavior described in :meth:`Node_f_delAttr`. """ self._f_delattr(name) delAttr = previous_api(del_attr) # Data handling # ````````````` def flush(self): """Flush pending data to disk. Saves whatever remaining buffered data to disk. It also releases I/O buffers, so if you are filling many datasets in the same PyTables session, please call flush() extensively so as to help PyTables to keep memory requirements low. """ self._g_flush() def _f_close(self, flush=True): """Close this node in the tree. This method has the behavior described in :meth:`Node._f_close`. Besides that, the optional argument flush tells whether to flush pending data to disk or not before closing. """ if not self._v_isopen: return # the node is already closed or not initialized # Only do a flush in case the leaf has an IO buffer. The # internal buffers of HDF5 will be flushed afterwards during the # self._g_close() call. Avoiding an unnecessary flush() # operation accelerates the closing for the unbuffered leaves. if flush and hasattr(self, "_v_iobuf"): self.flush() # Close the dataset and release resources self._g_close() # Close myself as a node. super(Leaf, self)._f_close() def close(self, flush=True): """Close this node in the tree. This method is completely equivalent to :meth:`Leaf._f_close`. """ self._f_close(flush) ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/link.py000066400000000000000000000224171231437614300162760ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: November 25, 2009 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Create links in the HDF5 file. This module implements containers for soft and external links. Hard links doesn't need a container as such as they are the same as regular nodes (groups or leaves). Classes: SoftLink ExternalLink Functions: Misc variables: """ import os import tables from tables import linkextension from tables.node import Node from tables.utils import lazyattr from tables.attributeset import AttributeSet import tables.file from tables._past import previous_api, previous_api_property def _g_get_link_class(parent_id, name): """Guess the link class.""" return linkextension._get_link_class(parent_id, name) _g_getLinkClass = previous_api(_g_get_link_class) class Link(Node): """Abstract base class for all PyTables links. A link is a node that refers to another node. The Link class inherits from Node class and the links that inherits from Link are SoftLink and ExternalLink. There is not a HardLink subclass because hard links behave like a regular Group or Leaf. Contrarily to other nodes, links cannot have HDF5 attributes. This is an HDF5 library limitation that might be solved in future releases. See :ref:`LinksTutorial` for a small tutorial on how to work with links. .. rubric:: Link attributes .. attribute:: target The path string to the pointed node. """ # Properties @lazyattr def _v_attrs(self): """ A *NoAttrs* instance replacing the typical *AttributeSet* instance of other node objects. The purpose of *NoAttrs* is to make clear that HDF5 attributes are not supported in link nodes. """ class NoAttrs(AttributeSet): def __getattr__(self, name): raise KeyError("you cannot get attributes from this " "`%s` instance" % self.__class__.__name__) def __setattr__(self, name, value): raise KeyError("you cannot set attributes to this " "`%s` instance" % self.__class__.__name__) def _g_close(self): pass return NoAttrs(self) def __init__(self, parentnode, name, target=None, _log=False): self._v_new = target is not None self.target = target """The path string to the pointed node.""" super(Link, self).__init__(parentnode, name, _log) # Public and tailored versions for copy, move, rename and remove methods def copy(self, newparent=None, newname=None, overwrite=False, createparents=False): """Copy this link and return the new one. See :meth:`Node._f_copy` for a complete explanation of the arguments. Please note that there is no recursive flag since links do not have child nodes. """ newnode = self._f_copy(newparent=newparent, newname=newname, overwrite=overwrite, createparents=createparents) # Insert references to a `newnode` via `newname` newnode._v_parent._g_refnode(newnode, newname, True) return newnode def move(self, newparent=None, newname=None, overwrite=False): """Move or rename this link. See :meth:`Node._f_move` for a complete explanation of the arguments. """ return self._f_move(newparent=newparent, newname=newname, overwrite=overwrite) def remove(self): """Remove this link from the hierarchy.""" return self._f_remove() def rename(self, newname=None, overwrite=False): """Rename this link in place. See :meth:`Node._f_rename` for a complete explanation of the arguments. """ return self._f_rename(newname=newname, overwrite=overwrite) def __repr__(self): return str(self) class SoftLink(linkextension.SoftLink, Link): """Represents a soft link (aka symbolic link). A soft link is a reference to another node in the *same* file hierarchy. Getting access to the pointed node (this action is called *dereferrencing*) is done via the __call__ special method (see below). """ # Class identifier. _c_classid = 'SOFTLINK' _c_classId = previous_api_property('_c_classid') def __call__(self): """Dereference `self.target` and return the object. Examples -------- :: >>> f=tables.open_file('data/test.h5') >>> print(f.root.link0) /link0 (SoftLink) -> /another/path >>> print(f.root.link0()) /another/path (Group) '' """ target = self.target # Check for relative pathnames if not self.target.startswith('/'): target = self._v_parent._g_join(self.target) return self._v_file._get_node(target) def __str__(self): """Return a short string representation of the link. Examples -------- :: >>> f=tables.open_file('data/test.h5') >>> print(f.root.link0) /link0 (SoftLink) -> /path/to/node """ classname = self.__class__.__name__ target = self.target # Check for relative pathnames if not self.target.startswith('/'): target = self._v_parent._g_join(self.target) if target in self._v_file: dangling = "" else: dangling = " (dangling)" return "%s (%s) -> %s%s" % (self._v_pathname, classname, self.target, dangling) class ExternalLink(linkextension.ExternalLink, Link): """Represents an external link. An external link is a reference to a node in *another* file. Getting access to the pointed node (this action is called *dereferencing*) is done via the :meth:`__call__` special method (see below). .. rubric:: ExternalLink attributes .. attribute:: extfile The external file handler, if the link has been dereferenced. In case the link has not been dereferenced yet, its value is None. """ # Class identifier. _c_classid = 'EXTERNALLINK' _c_classId = previous_api_property('_c_classid') def __init__(self, parentnode, name, target=None, _log=False): self.extfile = None """The external file handler, if the link has been dereferenced. In case the link has not been dereferenced yet, its value is None.""" super(ExternalLink, self).__init__(parentnode, name, target, _log) def _get_filename_node(self): """Return the external filename and nodepath from `self.target`.""" # This is needed for avoiding the 'C:\\file.h5' filepath notation filename, target = self.target.split(':/') return filename, '/' + target def __call__(self, **kwargs): """Dereference self.target and return the object. You can pass all the arguments supported by the :func:`open_file` function (except filename, of course) so as to open the referenced external file. Examples -------- :: >>> f=tables.open_file('data1/test1.h5') >>> print(f.root.link2) /link2 (ExternalLink) -> data2/test2.h5:/path/to/node >>> plink2 = f.root.link2('a') # open in 'a'ppend mode >>> print(plink2) /path/to/node (Group) '' >>> print(plink2._v_filename) 'data2/test2.h5' # belongs to referenced file """ filename, target = self._get_filename_node() if not os.path.isabs(filename): # Resolve the external link with respect to the this # file's directory. See #306. base_directory = os.path.dirname(self._v_file.filename) filename = os.path.join(base_directory, filename) if self.extfile is None or not self.extfile.isopen: self.extfile = tables.open_file(filename, **kwargs) else: # XXX: implement better consistency checks assert self.extfile.filename == filename assert self.extfile.mode == kwargs.get('mode', 'r') return self.extfile._get_node(target) def umount(self): """Safely unmount self.extfile, if opened.""" extfile = self.extfile # Close external file, if open if extfile is not None and extfile.isopen: extfile.close() self.extfile = None def _f_close(self): """Especific close for external links.""" self.umount() super(ExternalLink, self)._f_close() def __str__(self): """Return a short string representation of the link. Examples -------- :: >>> f=tables.open_file('data1/test1.h5') >>> print(f.root.link2) /link2 (ExternalLink) -> data2/test2.h5:/path/to/node """ classname = self.__class__.__name__ return "%s (%s) -> %s" % (self._v_pathname, classname, self.target) ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/linkExtension.py000066400000000000000000000004101231437614300201600ustar00rootroot00000000000000from warnings import warn from tables.linkextension import * _warnmsg = ("linkExtension is pending deprecation, import linextension instead. " "You may use the pt2to3 tool to update your source code.") warn(_warnmsg, DeprecationWarning, stacklevel=2) PyTables-v.3.1.1/tables/linkextension.pyx000066400000000000000000000201751231437614300204220ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: November 25, 2009 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Cython functions and classes for supporting links in HDF5.""" from tables.exceptions import HDF5ExtError from hdf5extension cimport Node from utilsextension cimport cstr_to_pystr from libc.stdlib cimport malloc, free from libc.string cimport strlen from cpython cimport PY_MAJOR_VERSION from cpython.unicode cimport PyUnicode_DecodeUTF8 from definitions cimport (H5P_DEFAULT, const_char, hid_t, herr_t, hbool_t, int64_t, H5T_cset_t, haddr_t) from tables._past import previous_api #---------------------------------------------------------------------- # External declarations cdef extern from "H5Lpublic.h" nogil: ctypedef enum H5L_type_t: H5L_TYPE_ERROR = (-1), # Invalid link type id H5L_TYPE_HARD = 0, # Hard link id H5L_TYPE_SOFT = 1, # Soft link id H5L_TYPE_EXTERNAL = 64, # External link id H5L_TYPE_MAX = 255 # Maximum link type id # Information struct for link (for H5Lget_info) cdef union _add_u: haddr_t address # Address hard link points to size_t val_size # Size of a soft link or UD link value ctypedef struct H5L_info_t: H5L_type_t type # Type of link hbool_t corder_valid # Indicate if creation order is valid int64_t corder # Creation order H5T_cset_t cset # Character set of link name _add_u u # Size of a soft link or UD link value # Operations with links herr_t H5Lcreate_hard( hid_t obj_loc_id, char *obj_name, hid_t link_loc_id, char *link_name, hid_t lcpl_id, hid_t lapl_id) herr_t H5Lcreate_soft( char *target_path, hid_t link_loc_id, char *link_name, hid_t lcpl_id, hid_t lapl_id) herr_t H5Lcreate_external( char *file_name, char *object_name, hid_t link_loc_id, char *link_name, hid_t lcpl_id, hid_t lapl_id) herr_t H5Lget_info( hid_t link_loc_id, char *link_name, H5L_info_t *link_buff, hid_t lapl_id) herr_t H5Lget_val( hid_t link_loc_id, char *link_name, void *linkval_buff, size_t size, hid_t lapl_id) herr_t H5Lunpack_elink_val( char *ext_linkval, size_t link_size, unsigned *flags, const_char **filename, const_char **obj_path) herr_t H5Lcopy( hid_t src_loc_id, char *src_name, hid_t dest_loc_id, char *dest_name, hid_t lcpl_id, hid_t lapl_id) #---------------------------------------------------------------------- # Helper functions def _get_link_class(parent_id, name): """Guess the link class.""" cdef herr_t ret cdef H5L_info_t link_buff cdef H5L_type_t link_type ret = H5Lget_info(parent_id, name, &link_buff, H5P_DEFAULT) if ret < 0: raise HDF5ExtError("failed to get info about link") link_type = link_buff.type if link_type == H5L_TYPE_SOFT: return "SoftLink" elif link_type == H5L_TYPE_EXTERNAL: return "ExternalLink" else: return "UnImplemented" _getLinkClass = previous_api(_get_link_class) def _g_create_hard_link(parentnode, str name, targetnode): """Create a hard link in the file.""" cdef herr_t ret cdef bytes encoded_name = name.encode('utf-8') cdef bytes encoded_v_name = targetnode._v_name.encode('utf-8') ret = H5Lcreate_hard(targetnode._v_parent._v_objectid, encoded_v_name, parentnode._v_objectid, encoded_name, H5P_DEFAULT, H5P_DEFAULT) if ret < 0: raise HDF5ExtError("failed to create HDF5 hard link") _g_createHardLink = previous_api(_g_create_hard_link) #---------------------------------------------------------------------- # Public classes cdef class Link(Node): """Extension class from which all link extensions inherits.""" def _g_copy(self, newparent, newname, recursive, _log=True, **kwargs): """Private part for the _f_copy() method.""" cdef herr_t ret cdef object stats cdef bytes encoded_name, encoded_newname encoded_name = self.name.encode('utf-8') encoded_newname = newname.encode('utf-8') # @TODO: set property list --> utf-8 ret = H5Lcopy(self.parent_id, encoded_name, newparent._v_objectid, encoded_newname, H5P_DEFAULT, H5P_DEFAULT) if ret < 0: raise HDF5ExtError("failed to copy HDF5 link") # Update statistics if needed. stats = kwargs.get('stats', None) if stats is not None: stats['links'] += 1 return newparent._v_file.get_node(newparent, newname) cdef class SoftLink(Link): """Extension class representing a soft link.""" def _g_create(self): """Create the link in file.""" cdef herr_t ret cdef bytes encoded_name = self.name.encode('utf-8') cdef bytes encoded_target = self.target.encode('utf-8') ret = H5Lcreate_soft(encoded_target, self.parent_id, encoded_name, H5P_DEFAULT, H5P_DEFAULT) if ret < 0: raise HDF5ExtError("failed to create HDF5 soft link") return 0 # Object ID is zero'ed, as HDF5 does not assign one for links def _g_open(self): """Open the link in file.""" cdef herr_t ret cdef H5L_info_t link_buff cdef size_t val_size cdef char *clinkval cdef bytes encoded_name encoded_name = self.name.encode('utf-8') ret = H5Lget_info(self.parent_id, encoded_name, &link_buff, H5P_DEFAULT) if ret < 0: raise HDF5ExtError("failed to get info about soft link") val_size = link_buff.u.val_size clinkval = malloc(val_size) ret = H5Lget_val(self.parent_id, encoded_name, clinkval, val_size, H5P_DEFAULT) if ret < 0: raise HDF5ExtError("failed to get target value") if PY_MAJOR_VERSION > 2: self.target = PyUnicode_DecodeUTF8(clinkval, strlen(clinkval), NULL) else: self.target = clinkval # Release resources free(clinkval) return 0 # Object ID is zero'ed, as HDF5 does not assign one for links cdef class ExternalLink(Link): """Extension class representing an external link.""" def _g_create(self): """Create the link in file.""" cdef herr_t ret cdef bytes encoded_name, encoded_filename, encoded_target encoded_name = self.name.encode('utf-8') filename, target = self._get_filename_node() encoded_filename = filename.encode('utf-8') encoded_target = target.encode('utf-8') ret = H5Lcreate_external(encoded_filename, encoded_target, self.parent_id, encoded_name, H5P_DEFAULT, H5P_DEFAULT) if ret < 0: raise HDF5ExtError("failed to create HDF5 external link") return 0 # Object ID is zero'ed, as HDF5 does not assign one for links def _g_open(self): """Open the link in file.""" cdef herr_t ret cdef H5L_info_t link_buff cdef size_t val_size cdef char *clinkval cdef char *cfilename cdef char *c_obj_path cdef unsigned flags cdef bytes encoded_name cdef str filename, obj_path encoded_name = self.name.encode('utf-8') ret = H5Lget_info(self.parent_id, encoded_name, &link_buff, H5P_DEFAULT) if ret < 0: raise HDF5ExtError("failed to get info about external link") val_size = link_buff.u.val_size clinkval = malloc(val_size) ret = H5Lget_val(self.parent_id, encoded_name, clinkval, val_size, H5P_DEFAULT) if ret < 0: raise HDF5ExtError("failed to get target value") ret = H5Lunpack_elink_val(clinkval, val_size, &flags, &cfilename, &c_obj_path) if ret < 0: raise HDF5ExtError("failed to unpack external link value") filename = cstr_to_pystr(cfilename) obj_path = cstr_to_pystr(c_obj_path) self.target = filename+':'+obj_path # Release resources free(clinkval) return 0 # Object ID is zero'ed, as HDF5 does not assign one for links ## Local Variables: ## mode: python ## py-indent-offset: 2 ## tab-width: 2 ## fill-column: 78 ## End: PyTables-v.3.1.1/tables/lrucacheExtension.py000066400000000000000000000004251231437614300210170ustar00rootroot00000000000000from warnings import warn from tables.lrucacheextension import * _warnmsg = ("lrucacheExtension is pending deprecation, import lrucacheextension instead. " "You may use the pt2to3 tool to update your source code.") warn(_warnmsg, DeprecationWarning, stacklevel=2) PyTables-v.3.1.1/tables/lrucacheextension.pxd000066400000000000000000000045201231437614300212220ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## from numpy cimport ndarray # Declaration of instance variables for shared classes # The NodeCache class is useful for caching general objects (like Nodes). cdef class NodeCache: cdef readonly long nslots cdef long nextslot cdef object nodes, paths cdef object setitem(self, object path, object node) cdef long getslot(self, object path) cdef object cpop(self, object path) # Base class for other caches cdef class BaseCache: cdef int iscachedisabled, incsetcount cdef long setcount, getcount, containscount cdef long disablecyclecount, disableeverycycles cdef long enablecyclecount, enableeverycycles cdef double nprobes, hitratio cdef long seqn_, nextslot, nslots cdef long *ratimes cdef double lowesthr cdef ndarray atimes cdef object name cdef int checkhitratio(self) cdef int couldenablecache_(self) cdef long incseqn(self) # Helper class for ObjectCache cdef class ObjectNode: cdef object key, obj cdef long nslot # The ObjectCache class is useful for general python objects cdef class ObjectCache(BaseCache): cdef long maxcachesize, cachesize, maxobjsize cdef long *rsizes cdef ndarray sizes cdef object __list, __dict cdef ObjectNode mrunode cdef removeslot_(self, long nslot) cdef clearcache_(self) cdef updateslot_(self, long nslot, long size, object key, object value) cdef long setitem_(self, object key, object value, long size) cdef long getslot_(self, object key) cdef object getitem_(self, long nslot) # The NumCache class is useful for caching numerical data in an efficient way cdef class NumCache(BaseCache): cdef long itemsize, slotsize cdef ndarray cacheobj, keys cdef void *rcache cdef long long *rkeys cdef object __dict cdef void *getaddrslot_(self, long nslot) cdef long setitem_(self, long long key, void *data, long start) cdef long setitem1_(self, long long key) cdef long getslot_(self, long long key) cdef getitem_(self, long nslot, void *data, long start) cdef void *getitem1_(self, long nslot) ## Local Variables: ## mode: python ## py-indent-offset: 2 ## tab-width: 2 ## fill-column: 78 ## End: PyTables-v.3.1.1/tables/lrucacheextension.pyx000066400000000000000000000515451231437614300212600ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: Aug 13, 2006 # Author: Francesc Alted - faltet@pytables.com # # $Id: $ # ######################################################################## """Cython interface for several LRU cache systems. Classes (type extensions): NodeCache ObjectCache NumCache Functions: Misc variables: """ cdef extern from "Python.h": int PyUnicode_Compare(object, object) import sys import numpy from libc.string cimport memcpy, strcmp from cpython.unicode cimport PyUnicode_Check from numpy cimport import_array, ndarray from tables.parameters import (DISABLE_EVERY_CYCLES, ENABLE_EVERY_CYCLES, LOWEST_HIT_RATIO) #---------------------------------------------------------------------------- # Initialization code. # The numpy API requires this function to be called before # using any numpy facilities in an extension module. import_array() #---------------------------------------------------------------------------- # ------- Minimalist NodeCache for nodes in PyTables --------- # The next NodeCache code relies on the fact that a node that is # fetched from the cache will be removed from it. Said in other words: # "A node cannot be alive and dead at the same time." # Thanks to the above behaviour, the next code has been stripped down # to a bare minimum (the info in cache is kept in just 2 lists). #*********************** Important note! ***************************** # The code behind has been carefully tuned to serve the needs of # PyTables cache for nodes. As a consequence, it is no longer # appropriate as a general LRU cache implementation. You have been # warned!. F. Alted 2006-08-08 #********************************************************************* cdef class NodeCache: """Least-Recently-Used (LRU) cache for PyTables nodes.""" def __init__(self, nslots): """Maximum nslots of the cache. If more than 'nslots' elements are added to the cache, the least-recently-used ones will be discarded. """ if nslots < 0: raise ValueError("Negative number (%s) of slots!" % nslots) self.nslots = nslots self.nextslot = 0 self.nodes = [] self.paths = [] def __len__(self): return len(self.nodes) def __setitem__(self, path, node): self.setitem(path, node) cdef setitem(self, object path, object node): """Puts a new node in the node list.""" if self.nslots == 0: # Oops, the cache is set to empty return # Check if we are growing out of space if self.nextslot == self.nslots: # It is critical to reduce nextslot *before* the preemption of # the LRU node. If not, this can lead with problems in situations # with very small caches (length 1 or so). # F. Alted 2008-10-22 self.nextslot = self.nextslot - 1 # Remove the LRU node and path (the start of the lists) del self.nodes[0] del self.paths[0] # The equality protection has been put for situations in which a # node is being preempted and added simultaneously (with very small # caches). if len(self.nodes) == len(self.paths): # Add the node and path to the end of its lists self.nodes.append(node) self.paths.append(path) self.nextslot = self.nextslot + 1 def __contains__(self, path): if self.getslot(path) == -1: return 0 else: return 1 cdef long getslot(self, object path): """Checks whether path is in this cache or not.""" cdef long i, nslot nslot = -1 # -1 means not found if PyUnicode_Check(path): # Start looking from the trailing values (most recently used) for i from self.nextslot > i >= 0: #if strcmp(encoded_path, self.paths[i]) == 0: if PyUnicode_Compare(path, self.paths[i]) == 0: nslot = i break else: # Start looking from the trailing values (most recently used) for i from self.nextslot > i >= 0: if strcmp(path, self.paths[i]) == 0: nslot = i break return nslot __marker = object() def pop(self, path, d=__marker): try: node = self.cpop(path) except KeyError: if d is not self.__marker: return d else: raise else: return node cdef object cpop(self, object path): cdef long nslot nslot = self.getslot(path) if nslot == -1: raise KeyError(path) else: node = self.nodes[nslot] del self.nodes[nslot] del self.paths[nslot] self.nextslot = self.nextslot - 1 return node def __iter__(self): # Do a copy of the paths list because it can be modified in the middle of # the iterator! copy = self.paths[:] return iter(copy) def __repr__(self): return "<%s (%d elements)>" % (str(self.__class__), len(self.paths)) ######################################################################## # Common code for other LRU cache classes ######################################################################## cdef class BaseCache: """Base class that implements automatic probing/disabling of the cache.""" def __init__(self, long nslots, object name): if nslots < 0: raise ValueError("Negative number (%s) of slots!" % nslots) self.setcount = 0; self.getcount = 0; self.containscount = 0 self.enablecyclecount = 0; self.disablecyclecount = 0 self.iscachedisabled = False # Cache is enabled by default self.disableeverycycles = DISABLE_EVERY_CYCLES self.enableeverycycles = ENABLE_EVERY_CYCLES self.lowesthr = LOWEST_HIT_RATIO self.nprobes = 0.0; self.hitratio = 0.0 self.nslots = nslots self.seqn_ = 0; self.nextslot = 0 self.name = name self.incsetcount = False # The array for keeping the access times (using long ints here) self.atimes = numpy.zeros(shape=nslots, dtype=numpy.int_) self.ratimes = self.atimes.data def __len__(self): return self.nslots # Machinery for determining whether the hit ratio is being effective # or not. If not, the cache will be disabled. The efficency will be # checked every cycle (the time that the cache would be refilled # completely). In situations where the cache is not being re-filled # (i.e. it is not enabled) for a long time, it is forced to be # re-enabled when a certain number of cycles has passed so as to # check whether a new scenario where the cache can be useful again # has come. # F. Alted 2006-08-09 cdef int checkhitratio(self): cdef double hitratio cdef long nslot if self.setcount > self.nslots: self.disablecyclecount = self.disablecyclecount + 1 self.enablecyclecount = self.enablecyclecount + 1 self.nprobes = self.nprobes + 1 hitratio = self.getcount / self.containscount self.hitratio = self.hitratio + hitratio # Reset the hit counters self.setcount = 0; self.getcount = 0; self.containscount = 0 if (not self.iscachedisabled and self.disablecyclecount >= self.disableeverycycles): # Check whether the cache is being effective or not if hitratio < self.lowesthr: # Hit ratio is low. Disable the cache. self.iscachedisabled = True else: # Hit ratio is acceptable. (Re-)Enable the cache. self.iscachedisabled = False self.disablecyclecount = 0 if self.enablecyclecount >= self.enableeverycycles: # We have reached the time for forcing the cache to act again self.iscachedisabled = False self.enablecyclecount = 0 return not self.iscachedisabled def couldenablecache(self): return self.couldenablecache_() # Check whether the cache is enabled or *could* be enabled in the next # setitem operation. This method can be used in order to probe whether # an (expensive) operation to be done before a .setitem() is worth the # effort or not. cdef int couldenablecache_(self): if self.nslots == 0: return False # Increment setitem because it can be that .setitem() doesn't # get called after calling this. self.setcount = self.setcount + 1; self.incsetcount = True if self.iscachedisabled: if self.setcount == self.nslots: # The cache *could* be enabled in the next setitem operation return True else: return False else: return True # Increase the access time (implemented as a C long sequence) cdef long incseqn(self): self.seqn_ = self.seqn_ + 1 if self.seqn_ < 0: # Ooops, the counter has run out of range! Reset all the access times. self.atimes[:] = sys.maxint # Set the counter to 1 (to indicate that it is newer than existing ones) self.seqn_ = 1 return self.seqn_ def __repr__(self): return "<%s(%s) (%d elements)>" % (self.name, str(self.__class__), self.nslots) ######################################################################## # Helper class for ObjectCache ######################################################################## cdef class ObjectNode: """Record of a cached value. Not for public consumption.""" def __init__(self, object key, object obj, long nslot): object.__init__(self) self.key = key self.obj = obj self.nslot = nslot def __repr__(self): return "<%s %s (slot #%s) => %s>" % (self.__class__, self.key, self.nslot, self.object) ######################################################################## # Minimalistic LRU cache implementation for general python objects # This is a *true* general lru cache for python objects ######################################################################## cdef class ObjectCache(BaseCache): """Least-Recently-Used (LRU) cache specific for python objects.""" def __init__(self, long nslots, long maxcachesize, object name): """Maximum size of the cache. If more than 'nslots' elements are added to the cache, the least-recently-used ones will be discarded. Parameters: nslots - The number of slots in cache name - A descriptive name for this cache """ super(ObjectCache, self).__init__(nslots, name) self.cachesize = 0 self.maxcachesize = maxcachesize # maxobjsize will be the same as the maximum cache size self.maxobjsize = maxcachesize self.__list = [None]*nslots self.__dict = {} self.mrunode = None # Most Recent Used node # The array for keeping the object size (using long ints here) self.sizes = numpy.zeros(shape=nslots, dtype=numpy.int_) self.rsizes = self.sizes.data # Clear cache cdef clearcache_(self): self.__list = [None]*self.nslots self.__dict = {} self.mrunode = None self.cachesize = 0 self.nextslot = 0 self.seqn_ = 0 # Remove a slot (if it exists in cache) cdef removeslot_(self, long nslot): cdef ObjectNode node assert nslot < self.nslots, "Attempting to remove beyond cache capacity." node = self.__list[nslot] if node is not None: self.__list[nslot] = None del self.__dict[node.key] self.cachesize = self.cachesize - self.rsizes[nslot] self.rsizes[nslot] = 0 if self.mrunode and self.mrunode.nslot == nslot: self.mrunode = None # The next slot to be updated will be this one self.nextslot = nslot # Update a slot cdef updateslot_(self, long nslot, long size, object key, object value): cdef ObjectNode node, oldnode cdef long nslot1, nslot2 cdef object lruidx assert nslot < self.nslots, "Number of nodes exceeding cache capacity." # Remove the previous nslot self.removeslot_(nslot) # Protection against too large data cache size while size + self.cachesize > self.maxcachesize: # Remove the LRU node among the 10 largest ones largidx = self.sizes.argsort()[-10:] nslot1 = self.atimes[largidx].argmin() nslot2 = largidx[nslot1] self.removeslot_(nslot2) # Insert the new one node = ObjectNode(key, value, nslot) self.ratimes[nslot] = self.incseqn() self.rsizes[nslot] = size self.__list[nslot] = node self.__dict[key] = node self.mrunode = node self.cachesize = self.cachesize + size # The next slot to update will be the LRU self.nextslot = self.atimes.argmin() # Put the object to the data in cache (for Python calls) def setitem(self, object key, object value, object size): return self.setitem_(key, value, size) # Put the object in cache (for cython calls) # size can be the exact size of the value object or an estimation. cdef long setitem_(self, object key, object value, long size): cdef long nslot if self.nslots == 0: # The cache has been set to empty return -1 nslot = -1 # Perhaps setcount has been already incremented in couldenablecache() if not self.incsetcount: self.setcount = self.setcount + 1 else: self.incsetcount = False if size > self.maxobjsize: # Check if the object is too large return -1 if self.checkhitratio(): nslot = self.nextslot self.updateslot_(nslot, size, key, value) else: # Empty the cache because it is not effective and it is taking space self.clearcache_() return nslot # Tells whether the key is in cache or not def __contains__(self, object key): return self.__dict.has_key(key) # Tells in which slot the key is. If not found, -1 is returned. def getslot(self, object key): return self.getslot_(key) # Tells in which slot the key is. If not found, -1 is returned. cdef long getslot_(self, object key): cdef ObjectNode node if self.nslots == 0: # The cache has been set to empty return -1 self.containscount = self.containscount + 1 # Give a chance to the MRU node node = self.mrunode if node and node.key == key: return node.nslot # No luck. Look in the dictionary. node = self.__dict.get(key) if node is None: return -1 return node.nslot # Return the object to the data in cache (for Python calls) def getitem(self, object nslot): return self.getitem_(nslot) # Return the object to the data in cache (for cython calls) cdef object getitem_(self, long nslot): cdef ObjectNode node self.getcount = self.getcount + 1 node = self.__list[nslot] self.ratimes[nslot] = self.incseqn() self.mrunode = node return node.obj def __repr__(self): if self.nprobes > 0: hitratio = self.hitratio / self.nprobes else: hitratio = self.getcount / self.containscount return """<%s(%s) (%d maxslots, %d slots used, %.3f KB cachesize, hit ratio: %.3f, disabled? %s)> """ % (self.name, str(self.__class__), self.nslots, self.nextslot, self.cachesize / 1024., hitratio, self.iscachedisabled) ################################################################### # Minimalistic LRU cache implementation for numerical data ################################################################### # The next code is more efficient in situations where efficiency is low. ################################################################### #*********************** Important note! **************************** # The code behind has been carefully tuned to serve the needs of # caching numerical data. As a consequence, it is no longer appropriate # as a general LRU cache implementation. You have been warned!. # F. Alted 2006-08-09 #******************************************************************** cdef class NumCache(BaseCache): """Least-Recently-Used (LRU) cache specific for Numerical data.""" def __init__(self, object shape, object dtype, object name): """Maximum size of the cache. If more than 'nslots' elements are added to the cache, the least-recently-used ones will be discarded. Parameters: shape - The rectangular shape of the cache (nslots, nelemsperslot) itemsize - The size of the element base in cache name - A descriptive name for this cache """ cdef long nslots nslots = shape[0]; self.slotsize = shape[1] if nslots >= 1<<16: # nslots can't be higher than 2**16. Will silently trunk the number. nslots = ((1<<16)-1) # Cast makes cython happy here super(NumCache, self).__init__(nslots, name) self.itemsize = dtype.itemsize self.__dict = {} # The cache object where all data will go # The last slot is to allow the setitem1_ method to still return # a valid scratch area for writing purposes self.cacheobj = numpy.empty(shape=(nslots+1, self.slotsize), dtype=dtype) self.rcache = self.cacheobj.data # The array for keeping the keys of slots self.keys = (-numpy.ones(shape=nslots, dtype=numpy.int64)) self.rkeys = self.keys.data # Returns the address of nslot cdef void *getaddrslot_(self, long nslot): if nslot >= 0: return self.rcache + nslot * self.slotsize * self.itemsize else: return self.rcache + self.nslots * self.slotsize * self.itemsize def setitem(self, long long key, ndarray nparr, long start): return self.setitem_(key, nparr.data, start) # Copy the new data into a cache slot cdef long setitem_(self, long long key, void *data, long start): cdef long nslot nslot = self.setitem1_(key) if nslot >= 0: # Copy the data to cache memcpy(self.rcache + nslot * self.slotsize * self.itemsize, data + start * self.itemsize, self.slotsize * self.itemsize) return nslot # Return a cache data pointer appropriate to save data. # Even if the cache is disabled, this will return a -1, which is # the last element in the cache. # This version avoids a memcpy of data, but the user should be # aware that data in nslot cannot be overwritten! cdef long setitem1_(self, long long key): cdef long nslot cdef object key2 if self.nslots == 0: # Oops, the cache is set to empty return -1 # Perhaps setcount has been already incremented in couldenablecache() if not self.incsetcount: self.setcount = self.setcount + 1 else: self.incsetcount = False nslot = -1 if self.checkhitratio(): # Check if we are growing out of space if self.nextslot == self.nslots: # Get the least recently used slot nslot = self.atimes.argmin() # Remove the slot from the dict key2 = self.keys[nslot] del self.__dict[key2] self.nextslot = self.nextslot - 1 else: # Get the next slot available nslot = self.nextslot # Insert the slot in the dictionary self.__dict[key] = nslot self.keys[nslot] = key self.ratimes[nslot] = self.incseqn() self.nextslot = self.nextslot + 1 # The next reduces the performance of the cache in scenarios where # the efficicency is near to zero. I don't understand exactly why. # F. Alted 24-03-2008 elif self.nextslot > 0: # Empty the cache if needed self.__dict.clear() self.nextslot = 0 return nslot def getslot(self, long long key): return self.getslot_(key) # Tells in which slot key is. If not found, -1 is returned. cdef long getslot_(self, long long key): cdef object nslot self.containscount = self.containscount + 1 if self.nextslot == 0: # No chances for finding a slot return -1 try: nslot = self.__dict[key] except KeyError: return -1 return nslot def getitem(self, long nslot, ndarray nparr, long start): self.getitem_(nslot, nparr.data, start) # This version copies data in cache to data+start. # The user should be responsible to provide a large enough data buffer # to keep all the data. cdef getitem_(self, long nslot, void *data, long start): cdef void *cachedata cachedata = self.getitem1_(nslot) # Copy the data in cache to destination memcpy(data + start * self.itemsize, cachedata, self.slotsize * self.itemsize) # Return the pointer to the data in cache # This version avoids a memcpy of data, but the user should be # aware that data in nslot cannot be overwritten! cdef void *getitem1_(self, long nslot): self.getcount = self.getcount + 1 self.ratimes[nslot] = self.incseqn() return self.rcache + nslot * self.slotsize * self.itemsize def __repr__(self): cachesize = (self.nslots * self.slotsize * self.itemsize) / 1024. if self.nprobes > 0: hitratio = self.hitratio / self.nprobes else: hitratio = self.getcount / self.containscount return """<%s(%s) (%d maxslots, %d slots used, %.3f KB cachesize, hit ratio: %.3f, disabled? %s)> """ % (self.name, str(self.__class__), self.nslots, self.nextslot, cachesize, hitratio, self.iscachedisabled) ## Local Variables: ## mode: python ## py-indent-offset: 2 ## tab-width: 2 ## fill-column: 78 ## End: PyTables-v.3.1.1/tables/misc/000077500000000000000000000000001231437614300157145ustar00rootroot00000000000000PyTables-v.3.1.1/tables/misc/__init__.py000066400000000000000000000007371231437614300200340ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Miscellaneous general-purpose modules The purpose, authorship and license of modules in this package is diverse, and they may be useful outside of PyTables. Please read their source code for further information. """ PyTables-v.3.1.1/tables/misc/enum.py000066400000000000000000000326101231437614300172340ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: May 4, 2005 # Author: Ivan Vilata i Balaguer - reverse:net.selidor@ivan # # $Id$ # ######################################################################## """Implementation of enumerated types. This module provides the `Enum` class, which can be used to construct enumerated types. Those types are defined by providing an *exhaustive set or list* of possible, named values for a variable of that type. Enumerated variables of the same type are usually compared between them for equality and sometimes for order, but are not usually operated upon. Enumerated values have an associated *name* and *concrete value*. Every name is unique and so are concrete values. An enumerated variable always takes the concrete value, not its name. Usually, the concrete value is not used directly, and frequently it is entirely irrelevant. For the same reason, an enumerated variable is not usually compared with concrete values out of its enumerated type. For that kind of use, standard variables and constants are more adequate. """ from tables._past import previous_api __docformat__ = 'reStructuredText' """The format of documentation strings in this module.""" class Enum(object): """Enumerated type. Each instance of this class represents an enumerated type. The values of the type must be declared *exhaustively* and named with *strings*, and they might be given explicit concrete values, though this is not compulsory. Once the type is defined, it can not be modified. There are three ways of defining an enumerated type. Each one of them corresponds to the type of the only argument in the constructor of Enum: - *Sequence of names*: each enumerated value is named using a string, and its order is determined by its position in the sequence; the concrete value is assigned automatically:: >>> boolEnum = Enum(['True', 'False']) - *Mapping of names*: each enumerated value is named by a string and given an explicit concrete value. All of the concrete values must be different, or a ValueError will be raised:: >>> priority = Enum({'red': 20, 'orange': 10, 'green': 0}) >>> colors = Enum({'red': 1, 'blue': 1}) Traceback (most recent call last): ... ValueError: enumerated values contain duplicate concrete values: 1 - *Enumerated type*: in that case, a copy of the original enumerated type is created. Both enumerated types are considered equal:: >>> prio2 = Enum(priority) >>> priority == prio2 True Please note that names starting with _ are not allowed, since they are reserved for internal usage:: >>> prio2 = Enum(['_xx']) Traceback (most recent call last): ... ValueError: name of enumerated value can not start with ``_``: '_xx' The concrete value of an enumerated value is obtained by getting its name as an attribute of the Enum instance (see __getattr__()) or as an item (see __getitem__()). This allows comparisons between enumerated values and assigning them to ordinary Python variables:: >>> redv = priority.red >>> redv == priority['red'] True >>> redv > priority.green True >>> priority.red == priority.orange False The name of the enumerated value corresponding to a concrete value can also be obtained by using the __call__() method of the enumerated type. In this way you get the symbolic name to use it later with __getitem__():: >>> priority(redv) 'red' >>> priority.red == priority[priority(priority.red)] True (If you ask, the __getitem__() method is not used for this purpose to avoid ambiguity in the case of using strings as concrete values.) """ def __init__(self, enum): mydict = self.__dict__ mydict['_names'] = {} mydict['_values'] = {} if isinstance(enum, list) or isinstance(enum, tuple): for (value, name) in enumerate(enum): # values become 0, 1, 2... self._check_and_set_pair(name, value) elif isinstance(enum, dict): for (name, value) in enum.iteritems(): self._check_and_set_pair(name, value) elif isinstance(enum, Enum): for (name, value) in enum._names.iteritems(): self._check_and_set_pair(name, value) else: raise TypeError("""\ enumerations can only be created from \ sequences, mappings and other enumerations""") def _check_and_set_pair(self, name, value): """Check validity of enumerated value and insert it into type.""" names = self._names values = self._values if not isinstance(name, basestring): raise TypeError( "name of enumerated value is not a string: %r" % (name,)) if name.startswith('_'): raise ValueError( "name of enumerated value can not start with ``_``: %r" % name) # This check is only necessary with a sequence base object. if name in names: raise ValueError( "enumerated values contain duplicate names: %r" % name) # This check is only necessary with a mapping base object. if value in values: raise ValueError( "enumerated values contain duplicate concrete values: %r" % value) names[name] = value values[value] = name self.__dict__[name] = value _checkAndSetPair = previous_api(_check_and_set_pair) def __getitem__(self, name): """Get the concrete value of the enumerated value with that name. The name of the enumerated value must be a string. If there is no value with that name in the enumeration, a KeyError is raised. Examples -------- Let ``enum`` be an enumerated type defined as: >>> enum = Enum({'T0': 0, 'T1': 2, 'T2': 5}) then: >>> enum['T1'] 2 >>> enum['foo'] Traceback (most recent call last): ... KeyError: "no enumerated value with that name: 'foo'" """ try: return self._names[name] except KeyError: raise KeyError("no enumerated value with that name: %r" % (name,)) def __setitem__(self, name, value): """This operation is forbidden.""" raise IndexError("operation not allowed") def __delitem__(self, name): """This operation is forbidden.""" raise IndexError("operation not allowed") def __getattr__(self, name): """Get the concrete value of the enumerated value with that name. The name of the enumerated value must be a string. If there is no value with that name in the enumeration, an AttributeError is raised. Examples -------- Let ``enum`` be an enumerated type defined as: >>> enum = Enum({'T0': 0, 'T1': 2, 'T2': 5}) then: >>> enum.T1 2 >>> enum.foo Traceback (most recent call last): ... AttributeError: no enumerated value with that name: 'foo' """ try: return self[name] except KeyError as ke: raise AttributeError(*ke.args) def __setattr__(self, name, value): """This operation is forbidden.""" raise AttributeError("operation not allowed") def __delattr__(self, name): """This operation is forbidden.""" raise AttributeError("operation not allowed") def __contains__(self, name): """Is there an enumerated value with that name in the type? If the enumerated type has an enumerated value with that name, True is returned. Otherwise, False is returned. The name must be a string. This method does *not* check for concrete values matching a value in an enumerated type. For that, please use the :meth:`Enum.__call__` method. Examples -------- Let ``enum`` be an enumerated type defined as: >>> enum = Enum({'T0': 0, 'T1': 2, 'T2': 5}) then: >>> 'T1' in enum True >>> 'foo' in enum False >>> 0 in enum Traceback (most recent call last): ... TypeError: name of enumerated value is not a string: 0 >>> enum.T1 in enum # Be careful with this! Traceback (most recent call last): ... TypeError: name of enumerated value is not a string: 2 """ if not isinstance(name, basestring): raise TypeError( "name of enumerated value is not a string: %r" % (name,)) return name in self._names def __call__(self, value, *default): """Get the name of the enumerated value with that concrete value. If there is no value with that concrete value in the enumeration and a second argument is given as a default, this is returned. Else, a ValueError is raised. This method can be used for checking that a concrete value belongs to the set of concrete values in an enumerated type. Examples -------- Let ``enum`` be an enumerated type defined as: >>> enum = Enum({'T0': 0, 'T1': 2, 'T2': 5}) then: >>> enum(5) 'T2' >>> enum(42, None) is None True >>> enum(42) Traceback (most recent call last): ... ValueError: no enumerated value with that concrete value: 42 """ try: return self._values[value] except KeyError: if len(default) > 0: return default[0] raise ValueError( "no enumerated value with that concrete value: %r" % (value,)) def __len__(self): """Return the number of enumerated values in the enumerated type. Examples -------- >>> len(Enum(['e%d' % i for i in range(10)])) 10 """ return len(self._names) def __iter__(self): """Iterate over the enumerated values. Enumerated values are returned as (name, value) pairs *in no particular order*. Examples -------- >>> enumvals = {'red': 4, 'green': 2, 'blue': 1} >>> enum = Enum(enumvals) >>> enumdict = dict([(name, value) for (name, value) in enum]) >>> enumvals == enumdict True """ for name_value in self._names.iteritems(): yield name_value def __eq__(self, other): """Is the other enumerated type equivalent to this one? Two enumerated types are equivalent if they have exactly the same enumerated values (i.e. with the same names and concrete values). Examples -------- Let ``enum*`` be enumerated types defined as: >>> enum1 = Enum({'T0': 0, 'T1': 2}) >>> enum2 = Enum(enum1) >>> enum3 = Enum({'T1': 2, 'T0': 0}) >>> enum4 = Enum({'T0': 0, 'T1': 2, 'T2': 5}) >>> enum5 = Enum({'T0': 0}) >>> enum6 = Enum({'T0': 10, 'T1': 20}) then: >>> enum1 == enum1 True >>> enum1 == enum2 == enum3 True >>> enum1 == enum4 False >>> enum5 == enum1 False >>> enum1 == enum6 False Comparing enumerated types with other kinds of objects produces a false result: >>> enum1 == {'T0': 0, 'T1': 2} False >>> enum1 == ['T0', 'T1'] False >>> enum1 == 2 False """ if not isinstance(other, Enum): return False return self._names == other._names def __ne__(self, other): """Is the `other` enumerated type different from this one? Two enumerated types are different if they don't have exactly the same enumerated values (i.e. with the same names and concrete values). Examples -------- Let ``enum*`` be enumerated types defined as: >>> enum1 = Enum({'T0': 0, 'T1': 2}) >>> enum2 = Enum(enum1) >>> enum3 = Enum({'T1': 2, 'T0': 0}) >>> enum4 = Enum({'T0': 0, 'T1': 2, 'T2': 5}) >>> enum5 = Enum({'T0': 0}) >>> enum6 = Enum({'T0': 10, 'T1': 20}) then: >>> enum1 != enum1 False >>> enum1 != enum2 != enum3 False >>> enum1 != enum4 True >>> enum5 != enum1 True >>> enum1 != enum6 True """ return not self.__eq__(other) # XXX: API incompatible change for PyTables 3 line # Overriding __eq__ blocks inheritance of __hash__ in 3.x # def __hash__(self): # return hash((self.__class__, tuple(self._names.items()))) def __repr__(self): """Return the canonical string representation of the enumeration. The output of this method can be evaluated to give a new enumeration object that will compare equal to this one. Examples -------- >>> repr(Enum({'name': 10})) "Enum({'name': 10})" """ return 'Enum(%s)' % self._names def _test(): import doctest return doctest.testmod() if __name__ == '__main__': _test() ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/misc/proxydict.py000066400000000000000000000050331231437614300203140ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: 2005-07-07 # Author: Ivan Vilata i Balaguer - ivan@selidor.net # # $Id$ # ######################################################################## """Proxy dictionary for objects stored in a container.""" import weakref from tables._past import previous_api, previous_api_property class ProxyDict(dict): """A dictionary which uses a container object to store its values.""" containerRef = previous_api_property('containerref') def __init__(self, container): self.containerref = weakref.ref(container) """A weak reference to the container object. .. versionchanged:: 3.0 The *containerRef* attribute has been renamed into *containerref*. """ def __getitem__(self, key): if key not in self: raise KeyError(key) # Values are not actually stored to avoid extra references. return self._get_value_from_container(self._get_container(), key) def __setitem__(self, key, value): # Values are not actually stored to avoid extra references. super(ProxyDict, self).__setitem__(key, None) def __repr__(self): return object.__repr__(self) def __str__(self): # C implementation does not use `self.__getitem__()`. :( itemFormat = '%r: %r' itemReprs = [itemFormat % item for item in self.iteritems()] return '{%s}' % ', '.join(itemReprs) def values(self): # C implementation does not use `self.__getitem__()`. :( valueList = [] for key in self.iterkeys(): valueList.append(self[key]) return valueList def itervalues(self): # C implementation does not use `self.__getitem__()`. :( for key in self.iterkeys(): yield self[key] raise StopIteration def items(self): # C implementation does not use `self.__getitem__()`. :( itemList = [] for key in self.iterkeys(): itemList.append((key, self[key])) return itemList def iteritems(self): # C implementation does not use `self.__getitem__()`. :( for key in self.iterkeys(): yield (key, self[key]) raise StopIteration def _get_container(self): container = self.containerref() if container is None: raise ValueError("the container object does no longer exist") return container _getContainer = previous_api(_get_container) PyTables-v.3.1.1/tables/node.py000066400000000000000000001023311231437614300162600ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: 2005-02-11 # Author: Ivan Vilata i Balaguer - ivan@selidor.net # # $Id$ # ######################################################################## """PyTables nodes.""" import warnings from tables.registry import class_name_dict, class_id_dict from tables.exceptions import (ClosedNodeError, NodeError, UndoRedoWarning, PerformanceWarning) from tables.path import join_path, split_path, isvisiblepath from tables.utils import lazyattr from tables.undoredo import move_to_shadow from tables.attributeset import AttributeSet, NotLoggedAttributeSet from tables._past import previous_api, previous_api_property __docformat__ = 'reStructuredText' """The format of documentation strings in this module.""" def _closedrepr(oldmethod): """Decorate string representation method to handle closed nodes. If the node is closed, a string like this is returned:: instead of calling `oldmethod` and returning its result. """ def newmethod(self): if not self._v_isopen: cmod = self.__class__.__module__ cname = self.__class__.__name__ addr = hex(id(self)) return '' % (cmod, cname, addr) return oldmethod(self) newmethod.__name__ = oldmethod.__name__ newmethod.__doc__ = oldmethod.__doc__ return newmethod class MetaNode(type): """Node metaclass. This metaclass ensures that their instance classes get registered into several dictionaries (namely the `tables.utils.class_name_dict` class name dictionary and the `tables.utils.class_id_dict` class identifier dictionary). It also adds sanity checks to some methods: * Check that the node is open when calling string representation and provide a default string if so. """ def __new__(class_, name, bases, dict_): # Add default behaviour for representing closed nodes. for mname in ['__str__', '__repr__']: if mname in dict_: dict_[mname] = _closedrepr(dict_[mname]) return type.__new__(class_, name, bases, dict_) def __init__(class_, name, bases, dict_): super(MetaNode, class_).__init__(name, bases, dict_) # Always register into class name dictionary. class_name_dict[class_.__name__] = class_ # Register into class identifier dictionary only if the class # has an identifier and it is different from its parents'. cid = getattr(class_, '_c_classid', None) if cid is not None: for base in bases: pcid = getattr(base, '_c_classid', None) if pcid == cid: break else: class_id_dict[cid] = class_ class Node(object): """Abstract base class for all PyTables nodes. This is the base class for *all* nodes in a PyTables hierarchy. It is an abstract class, i.e. it may not be directly instantiated; however, every node in the hierarchy is an instance of this class. A PyTables node is always hosted in a PyTables *file*, under a *parent group*, at a certain *depth* in the node hierarchy. A node knows its own *name* in the parent group and its own *path name* in the file. All the previous information is location-dependent, i.e. it may change when moving or renaming a node in the hierarchy. A node also has location-independent information, such as its *HDF5 object identifier* and its *attribute set*. This class gathers the operations and attributes (both location-dependent and independent) which are common to all PyTables nodes, whatever their type is. Nonetheless, due to natural naming restrictions, the names of all of these members start with a reserved prefix (see the Group class in :ref:`GroupClassDescr`). Sub-classes with no children (e.g. *leaf nodes*) may define new methods, attributes and properties to avoid natural naming restrictions. For instance, _v_attrs may be shortened to attrs and _f_rename to rename. However, the original methods and attributes should still be available. .. rubric:: Node attributes .. attribute:: _v_depth The depth of this node in the tree (an non-negative integer value). .. attribute:: _v_file The hosting File instance (see :ref:`FileClassDescr`). .. attribute:: _v_name The name of this node in its parent group (a string). .. attribute:: _v_pathname The path of this node in the tree (a string). .. attribute:: _v_objectid A node identifier (may change from run to run). .. versionchanged:: 3.0 The *_v_objectID* attribute has been renamed into *_v_object_id*. """ # This makes this class and all derived subclasses be handled by MetaNode. __metaclass__ = MetaNode # By default, attributes accept Undo/Redo. _AttributeSet = AttributeSet # # `_v_parent` is accessed via its file to avoid upwards references. def _g_getparent(self): (parentpath, nodename) = split_path(self._v_pathname) return self._v_file._get_node(parentpath) _v_parent = property( _g_getparent, None, None, ("The parent :class:`Group` instance")) # '_v_attrs' is defined as a lazy read-only attribute. # This saves 0.7s/3.8s. @lazyattr def _v_attrs(self): """The associated `AttributeSet` instance. See Also -------- tables.attributeset.AttributeSet : container for the HDF5 attributes """ return self._AttributeSet(self) # '_v_title' is a direct read-write shorthand for the 'TITLE' attribute # with the empty string as a default value. def _g_gettitle(self): if hasattr(self._v_attrs, 'TITLE'): return self._v_attrs.TITLE else: return '' def _g_settitle(self, title): self._v_attrs.TITLE = title _v_title = property(_g_gettitle, _g_settitle, None, ("A description of this node. A shorthand for " "TITLE attribute.")) # # This may be looked up by ``__del__`` when ``__init__`` doesn't get # to be called. See ticket #144 for more info. _v_isopen = False """Whehter this node is open or not.""" _v_objectId = previous_api_property('_v_objectid') _v_maxTreeDepth = previous_api_property('_v_maxtreedepth') # The ``_log`` argument is only meant to be used by ``_g_copy_as_child()`` # to avoid logging the creation of children nodes of a copied sub-tree. def __init__(self, parentnode, name, _log=True): # Remember to assign these values in the root group constructor # as it does not use this method implementation! self._v_file = None """The hosting File instance (see :ref:`FileClassDescr`).""" self._v_isopen = False """Whether this node is open or not.""" self._v_pathname = None """The path of this node in the tree (a string).""" self._v_name = None """The name of this node in its parent group (a string).""" self._v_depth = None """The depth of this node in the tree (an non-negative integer value). """ self._v_maxtreedepth = parentnode._v_file.params['MAX_TREE_DEPTH'] """Maximum tree depth before warning the user. .. versionchanged:: 3.0 Renamed into *_v_maxtreedepth* from *_v_maxTreeDepth*. """ self._v__deleting = False """Is the node being deleted?""" self._v_objectid = None """A node identifier (may change from run to run). .. versionchanged:: 3.0 The *_v_objectID* attribute has been renamed into *_v_objectid*. """ validate = new = self._v_new # set by subclass constructor # Is the parent node a group? Is it open? self._g_check_group(parentnode) parentnode._g_check_open() file_ = parentnode._v_file # Will the file be able to host a new node? if new: file_._check_writable() # Bind to the parent node and set location-dependent information. if new: # Only new nodes need to be referenced. # Opened nodes are already known by their parent group. parentnode._g_refnode(self, name, validate) self._g_set_location(parentnode, name) try: # hdf5extension operations: # Update node attributes. self._g_new(parentnode, name, init=True) # Create or open the node and get its object ID. if new: self._v_objectid = self._g_create() else: self._v_objectid = self._g_open() # The node *has* been created, log that. if new and _log and file_.is_undo_enabled(): self._g_log_create() # This allows extra operations after creating the node. self._g_post_init_hook() except: # If anything happens, the node must be closed # to undo every possible registration made so far. # We do *not* rely on ``__del__()`` doing it later, # since it might never be called anyway. self._f_close() raise def _g_log_create(self): self._v_file._log('CREATE', self._v_pathname) _g_logCreate = previous_api(_g_log_create) def __del__(self): # Closed `Node` instances can not be killed and revived. # Instead, accessing a closed and deleted (from memory, not # disk) one yields a *new*, open `Node` instance. This is # because of two reasons: # # 1. Predictability. After closing a `Node` and deleting it, # only one thing can happen when accessing it again: a new, # open `Node` instance is returned. If closed nodes could be # revived, one could get either a closed or an open `Node`. # # 2. Ease of use. If the user wants to access a closed node # again, the only condition would be that no references to # the `Node` instance were left. If closed nodes could be # revived, the user would also need to force the closed # `Node` out of memory, which is not a trivial task. # if not self._v_isopen: return # the node is already closed or not initialized self._v__deleting = True # If we get here, the `Node` is still open. try: node_manager = self._v_file._node_manager node_manager.drop_node(self, check_unregistered=False) finally: # At this point the node can still be open if there is still some # alive reference around (e.g. if the __del__ method is called # explicitly by the user). if self._v_isopen: self._v__deleting = True self._f_close() def _g_pre_kill_hook(self): """Code to be called before killing the node.""" pass _g_preKillHook = previous_api(_g_pre_kill_hook) def _g_create(self): """Create a new HDF5 node and return its object identifier.""" raise NotImplementedError def _g_open(self): """Open an existing HDF5 node and return its object identifier.""" raise NotImplementedError def _g_check_open(self): """Check that the node is open. If the node is closed, a `ClosedNodeError` is raised. """ if not self._v_isopen: raise ClosedNodeError("the node object is closed") assert self._v_file.isopen, "found an open node in a closed file" _g_checkOpen = previous_api(_g_check_open) def _g_set_location(self, parentnode, name): """Set location-dependent attributes. Sets the location-dependent attributes of this node to reflect that it is placed under the specified `parentnode`, with the specified `name`. This also triggers the insertion of file references to this node. If the maximum recommended tree depth is exceeded, a `PerformanceWarning` is issued. """ file_ = parentnode._v_file parentdepth = parentnode._v_depth self._v_file = file_ self._v_isopen = True root_uep = file_.root_uep if name.startswith(root_uep): # This has been called from File._get_node() assert parentdepth == 0 if root_uep == "/": self._v_pathname = name else: self._v_pathname = name[len(root_uep):] _, self._v_name = split_path(name) self._v_depth = name.count("/") - root_uep.count("/") + 1 else: # If we enter here is because this has been called elsewhere self._v_name = name self._v_pathname = join_path(parentnode._v_pathname, name) self._v_depth = parentdepth + 1 # Check if the node is too deep in the tree. if parentdepth >= self._v_maxtreedepth: warnings.warn("""\ node ``%s`` is exceeding the recommended maximum depth (%d);\ be ready to see PyTables asking for *lots* of memory and possibly slow I/O""" % (self._v_pathname, self._v_maxtreedepth), PerformanceWarning) if self._v_pathname != '/': file_._node_manager.cache_node(self, self._v_pathname) _g_setLocation = previous_api(_g_set_location) def _g_update_location(self, newparentpath): """Update location-dependent attributes. Updates location data when an ancestor node has changed its location in the hierarchy to `newparentpath`. In fact, this method is expected to be called by an ancestor of this node. This also triggers the update of file references to this node. If the maximum recommended node depth is exceeded, a `PerformanceWarning` is issued. This warning is assured to be unique. """ oldpath = self._v_pathname newpath = join_path(newparentpath, self._v_name) newdepth = newpath.count('/') self._v_pathname = newpath self._v_depth = newdepth # Check if the node is too deep in the tree. if newdepth > self._v_maxtreedepth: warnings.warn("""\ moved descendent node is exceeding the recommended maximum depth (%d);\ be ready to see PyTables asking for *lots* of memory and possibly slow I/O""" % (self._v_maxtreedepth,), PerformanceWarning) node_manager = self._v_file._node_manager node_manager.rename_node(oldpath, newpath) # Tell dependent objects about the new location of this node. self._g_update_dependent() _g_updateLocation = previous_api(_g_update_location) def _g_del_location(self): """Clear location-dependent attributes. This also triggers the removal of file references to this node. """ node_manager = self._v_file._node_manager pathname = self._v_pathname if not self._v__deleting: node_manager.drop_from_cache(pathname) # Note: node_manager.drop_node do not removes the node form the # registry if it is still open node_manager.registry.pop(pathname, None) self._v_file = None self._v_isopen = False self._v_pathname = None self._v_name = None self._v_depth = None _g_delLocation = previous_api(_g_del_location) def _g_post_init_hook(self): """Code to be run after node creation and before creation logging.""" pass _g_postInitHook = previous_api(_g_post_init_hook) def _g_update_dependent(self): """Update dependent objects after a location change. All dependent objects (but not nodes!) referencing this node must be updated here. """ if '_v_attrs' in self.__dict__: self._v_attrs._g_update_node_location(self) _g_updateDependent = previous_api(_g_update_dependent) def _f_close(self): """Close this node in the tree. This releases all resources held by the node, so it should not be used again. On nodes with data, it may be flushed to disk. You should not need to close nodes manually because they are automatically opened/closed when they are loaded/evicted from the integrated LRU cache. """ # After calling ``_f_close()``, two conditions are met: # # 1. The node object is detached from the tree. # 2. *Every* attribute of the node is removed. # # Thus, cleanup operations used in ``_f_close()`` in sub-classes # must be run *before* calling the method in the superclass. if not self._v_isopen: return # the node is already closed myDict = self.__dict__ # Close the associated `AttributeSet` # only if it has already been placed in the object's dictionary. if '_v_attrs' in myDict: self._v_attrs._g_close() # Detach the node from the tree if necessary. self._g_del_location() # Finally, clear all remaining attributes from the object. myDict.clear() # Just add a final flag to signal that the node is closed: self._v_isopen = False def _g_remove(self, recursive, force): """Remove this node from the hierarchy. If the node has children, recursive removal must be stated by giving `recursive` a true value; otherwise, a `NodeError` will be raised. If `force` is set to true, the node will be removed no matter it has children or not (useful for deleting hard links). It does not log the change. """ # Remove the node from the PyTables hierarchy. parent = self._v_parent parent._g_unrefnode(self._v_name) # Close the node itself. self._f_close() # hdf5extension operations: # Remove the node from the HDF5 hierarchy. self._g_delete(parent) def _f_remove(self, recursive=False, force=False): """Remove this node from the hierarchy. If the node has children, recursive removal must be stated by giving recursive a true value; otherwise, a NodeError will be raised. If the node is a link to a Group object, and you are sure that you want to delete it, you can do this by setting the force flag to true. """ self._g_check_open() file_ = self._v_file file_._check_writable() if file_.is_undo_enabled(): self._g_remove_and_log(recursive, force) else: self._g_remove(recursive, force) def _g_remove_and_log(self, recursive, force): file_ = self._v_file oldpathname = self._v_pathname # Log *before* moving to use the right shadow name. file_._log('REMOVE', oldpathname) move_to_shadow(file_, oldpathname) _g_removeAndLog = previous_api(_g_remove_and_log) def _g_move(self, newparent, newname): """Move this node in the hierarchy. Moves the node into the given `newparent`, with the given `newname`. It does not log the change. """ oldparent = self._v_parent oldname = self._v_name oldpathname = self._v_pathname # to move the HDF5 node # Try to insert the node into the new parent. newparent._g_refnode(self, newname) # Remove the node from the new parent. oldparent._g_unrefnode(oldname) # Remove location information for this node. self._g_del_location() # Set new location information for this node. self._g_set_location(newparent, newname) # hdf5extension operations: # Update node attributes. self._g_new(newparent, self._v_name, init=False) # Move the node. # self._v_parent._g_move_node(oldpathname, self._v_pathname) self._v_parent._g_move_node(oldparent._v_objectid, oldname, newparent._v_objectid, newname, oldpathname, self._v_pathname) # Tell dependent objects about the new location of this node. self._g_update_dependent() def _f_rename(self, newname, overwrite=False): """Rename this node in place. Changes the name of a node to *newname* (a string). If a node with the same newname already exists and overwrite is true, recursively remove it before renaming. """ self._f_move(newname=newname, overwrite=overwrite) def _f_move(self, newparent=None, newname=None, overwrite=False, createparents=False): """Move or rename this node. Moves a node into a new parent group, or changes the name of the node. newparent can be a Group object (see :ref:`GroupClassDescr`) or a pathname in string form. If it is not specified or None, the current parent group is chosen as the new parent. newname must be a string with a new name. If it is not specified or None, the current name is chosen as the new name. If createparents is true, the needed groups for the given new parent group path to exist will be created. Moving a node across databases is not allowed, nor it is moving a node *into* itself. These result in a NodeError. However, moving a node *over* itself is allowed and simply does nothing. Moving over another existing node is similarly not allowed, unless the optional overwrite argument is true, in which case that node is recursively removed before moving. Usually, only the first argument will be used, effectively moving the node to a new location without changing its name. Using only the second argument is equivalent to renaming the node in place. """ self._g_check_open() file_ = self._v_file oldparent = self._v_parent oldname = self._v_name # Set default arguments. if newparent is None and newname is None: raise NodeError("you should specify at least " "a ``newparent`` or a ``newname`` parameter") if newparent is None: newparent = oldparent if newname is None: newname = oldname # Get destination location. if hasattr(newparent, '_v_file'): # from node newfile = newparent._v_file newpath = newparent._v_pathname elif hasattr(newparent, 'startswith'): # from path newfile = file_ newpath = newparent else: raise TypeError("new parent is not a node nor a path: %r" % (newparent,)) # Validity checks on arguments. # Is it in the same file? if newfile is not file_: raise NodeError("nodes can not be moved across databases; " "please make a copy of the node") # The movement always fails if the hosting file can not be modified. file_._check_writable() # Moving over itself? oldpath = oldparent._v_pathname if newpath == oldpath and newname == oldname: # This is equivalent to renaming the node to its current name, # and it does not change the referenced object, # so it is an allowed no-op. return # Moving into itself? self._g_check_not_contains(newpath) # Note that the previous checks allow us to go ahead and create # the parent groups if `createparents` is true. `newparent` is # used instead of `newpath` to avoid accepting `Node` objects # when `createparents` is true. newparent = file_._get_or_create_path(newparent, createparents) self._g_check_group(newparent) # Is it a group? # Moving over an existing node? self._g_maybe_remove(newparent, newname, overwrite) # Move the node. oldpathname = self._v_pathname self._g_move(newparent, newname) # Log the change. if file_.is_undo_enabled(): self._g_log_move(oldpathname) def _g_log_move(self, oldpathname): self._v_file._log('MOVE', oldpathname, self._v_pathname) _g_logMove = previous_api(_g_log_move) def _g_copy(self, newparent, newname, recursive, _log=True, **kwargs): """Copy this node and return the new one. Creates and returns a copy of the node in the given `newparent`, with the given `newname`. If `recursive` copy is stated, all descendents are copied as well. Additional keyword argumens may affect the way that the copy is made. Unknown arguments must be ignored. On recursive copies, all keyword arguments must be passed on to the children invocation of this method. If `_log` is false, the change is not logged. This is *only* intended to be used by ``_g_copy_as_child()`` as a means of optimising sub-tree copies. """ raise NotImplementedError def _g_copy_as_child(self, newparent, **kwargs): """Copy this node as a child of another group. Copies just this node into `newparent`, not recursing children nor overwriting nodes nor logging the copy. This is intended to be used when copying whole sub-trees. """ return self._g_copy(newparent, self._v_name, recursive=False, _log=False, **kwargs) _g_copyAsChild = previous_api(_g_copy_as_child) def _f_copy(self, newparent=None, newname=None, overwrite=False, recursive=False, createparents=False, **kwargs): """Copy this node and return the new node. Creates and returns a copy of the node, maybe in a different place in the hierarchy. newparent can be a Group object (see :ref:`GroupClassDescr`) or a pathname in string form. If it is not specified or None, the current parent group is chosen as the new parent. newname must be a string with a new name. If it is not specified or None, the current name is chosen as the new name. If recursive copy is stated, all descendants are copied as well. If createparents is true, the needed groups for the given new parent group path to exist will be created. Copying a node across databases is supported but can not be undone. Copying a node over itself is not allowed, nor it is recursively copying a node into itself. These result in a NodeError. Copying over another existing node is similarly not allowed, unless the optional overwrite argument is true, in which case that node is recursively removed before copying. Additional keyword arguments may be passed to customize the copying process. For instance, title and filters may be changed, user attributes may be or may not be copied, data may be sub-sampled, stats may be collected, etc. See the documentation for the particular node type. Using only the first argument is equivalent to copying the node to a new location without changing its name. Using only the second argument is equivalent to making a copy of the node in the same group. """ self._g_check_open() srcfile = self._v_file srcparent = self._v_parent srcname = self._v_name dstparent = newparent dstname = newname # Set default arguments. if dstparent is None and dstname is None: raise NodeError("you should specify at least " "a ``newparent`` or a ``newname`` parameter") if dstparent is None: dstparent = srcparent if dstname is None: dstname = srcname # Get destination location. if hasattr(dstparent, '_v_file'): # from node dstfile = dstparent._v_file dstpath = dstparent._v_pathname elif hasattr(dstparent, 'startswith'): # from path dstfile = srcfile dstpath = dstparent else: raise TypeError("new parent is not a node nor a path: %r" % (dstparent,)) # Validity checks on arguments. if dstfile is srcfile: # Copying over itself? srcpath = srcparent._v_pathname if dstpath == srcpath and dstname == srcname: raise NodeError( "source and destination nodes are the same node: ``%s``" % self._v_pathname) # Recursively copying into itself? if recursive: self._g_check_not_contains(dstpath) # Note that the previous checks allow us to go ahead and create # the parent groups if `createparents` is true. `dstParent` is # used instead of `dstPath` because it may be in other file, and # to avoid accepting `Node` objects when `createparents` is # true. dstparent = srcfile._get_or_create_path(dstparent, createparents) self._g_check_group(dstparent) # Is it a group? # Copying to another file with undo enabled? if dstfile is not srcfile and srcfile.is_undo_enabled(): warnings.warn("copying across databases can not be undone " "nor redone from this database", UndoRedoWarning) # Copying over an existing node? self._g_maybe_remove(dstparent, dstname, overwrite) # Copy the node. # The constructor of the new node takes care of logging. return self._g_copy(dstparent, dstname, recursive, **kwargs) def _f_isvisible(self): """Is this node visible?""" self._g_check_open() return isvisiblepath(self._v_pathname) _f_isVisible = previous_api(_f_isvisible) def _g_check_group(self, node): # Node must be defined in order to define a Group. # However, we need to know Group here. # Using class_name_dict avoids a circular import. if not isinstance(node, class_name_dict['Node']): raise TypeError("new parent is not a registered node: %s" % node._v_pathname) if not isinstance(node, class_name_dict['Group']): raise TypeError("new parent node ``%s`` is not a group" % node._v_pathname) _g_checkGroup = previous_api(_g_check_group) def _g_check_not_contains(self, pathname): # The not-a-TARDIS test. ;) mypathname = self._v_pathname if (mypathname == '/' # all nodes fall below the root group or pathname == mypathname or pathname.startswith(mypathname + '/')): raise NodeError("can not move or recursively copy node ``%s`` " "into itself" % mypathname) _g_checkNotContains = previous_api(_g_check_not_contains) def _g_maybe_remove(self, parent, name, overwrite): if name in parent: if not overwrite: raise NodeError("""\ destination group ``%s`` already has a node named ``%s``; \ you may want to use the ``overwrite`` argument""" % (parent._v_pathname, name)) parent._f_get_child(name)._f_remove(True) _g_maybeRemove = previous_api(_g_maybe_remove) def _g_check_name(self, name): """Check validity of name for this particular kind of node. This is invoked once the standard HDF5 and natural naming checks have successfully passed. """ if name.startswith('_i_'): # This is reserved for table index groups. raise ValueError( "node name starts with reserved prefix ``_i_``: %s" % name) _g_checkName = previous_api(_g_check_name) # def _f_getattr(self, name): """Get a PyTables attribute from this node. If the named attribute does not exist, an AttributeError is raised. """ return getattr(self._v_attrs, name) _f_getAttr = previous_api(_f_getattr) def _f_setattr(self, name, value): """Set a PyTables attribute for this node. If the node already has a large number of attributes, a PerformanceWarning is issued. """ setattr(self._v_attrs, name, value) _f_setAttr = previous_api(_f_setattr) def _f_delattr(self, name): """Delete a PyTables attribute from this node. If the named attribute does not exist, an AttributeError is raised. """ delattr(self._v_attrs, name) _f_delAttr = previous_api(_f_delattr) # class NotLoggedMixin: # Include this class in your inheritance tree # to avoid changes to instances of your class from being logged. _AttributeSet = NotLoggedAttributeSet def _g_log_create(self): pass _g_logCreate = previous_api(_g_log_create) def _g_log_move(self, oldpathname): pass _g_logMove = previous_api(_g_log_move) def _g_remove_and_log(self, recursive, force): self._g_remove(recursive, force) _g_removeAndLog = previous_api(_g_remove_and_log) ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/nodes/000077500000000000000000000000001231437614300160715ustar00rootroot00000000000000PyTables-v.3.1.1/tables/nodes/__init__.py000066400000000000000000000013641231437614300202060ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: October 19, 2004 # Author: Ivan Vilata i Balaguer - reverse:net.selidor@ivan # # $Id$ # ######################################################################## """Special node behaviours for PyTables. This package contains several modules that give specific behaviours to PyTables nodes. For instance, the filenode module provides a file interface to a PyTables node. Package modules: filenode -- A file interface to nodes for PyTables databases. """ # The list of names to be exported to the importing module. __all__ = ['filenode'] ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## End: PyTables-v.3.1.1/tables/nodes/filenode.py000066400000000000000000000645011231437614300202360ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: October 2, 2004 # Author: Ivan Vilata i Balaguer - reverse:net.selidor@ivan # # $Id$ # ######################################################################## """A file interface to nodes for PyTables databases. The FileNode module provides a file interface for using inside of PyTables database files. Use the new_node() function to create a brand new file node which can be read and written as any ordinary Python file. Use the open_node() function to open an existing (i.e. created with new_node()) node for read-only or read-write access. Read acces is always available. Write access (enabled on new files and files opened with mode 'a+') only allows appending data to a file node. Currently only binary I/O is supported. See :ref:`filenode_usersguide` for instructions on use. .. versionchanged:: 3.0 In version 3.0 the module as been completely rewritten to be fully compliant with the interfaces defined in the :mod:`io` module. """ import io import os import warnings import numpy as np import tables from tables._past import previous_api NodeType = 'file' """Value for NODE_TYPE node system attribute.""" NodeTypeVersions = [1, 2] """Supported values for NODE_TYPE_VERSION node system attribute.""" # have a Python2/3 compatible way to check for string try: string_types = basestring except NameError: string_types = str class RawPyTablesIO(io.RawIOBase): """Base class for raw binary I/O on HDF5 files using PyTables.""" # A lambda to turn a size into a shape, for each version. _size_to_shape = [ None, lambda l: (l, 1), lambda l: (l, ), ] def __init__(self, node, mode=None): super(RawPyTablesIO, self).__init__() self._check_node(node) self._check_attributes(node) if mode is None: mode = node._v_file.mode else: self._check_mode(mode) self._cross_check_mode(mode, node._v_file.mode) self._node = node self._mode = mode self._pos = 0 self._version = int(node.attrs.NODE_TYPE_VERSION) self._vshape = self._size_to_shape[self._version] self._vtype = node.atom.dtype.base.type # read only attribute @property def mode(self): """File mode.""" return self._mode #def tell(self) -> int: def tell(self): """Return current stream position.""" self._checkClosed() return self._pos #def seek(self, pos: int, whence: int = 0) -> int: def seek(self, pos, whence=0): """Change stream position. Change the stream position to byte offset offset. offset is interpreted relative to the position indicated by whence. Values for whence are: * 0 -- start of stream (the default); offset should be zero or positive * 1 -- current stream position; offset may be negative * 2 -- end of stream; offset is usually negative Return the new absolute position. """ self._checkClosed() try: pos = pos.__index__() #except AttributeError as err: #raise TypeError("an integer is required") from err except AttributeError: raise TypeError("an integer is required") if whence == 0: if pos < 0: raise ValueError("negative seek position %r" % (pos,)) self._pos = pos elif whence == 1: self._pos = max(0, self._pos + pos) elif whence == 2: self._pos = max(0, self._node.nrows + pos) else: raise ValueError("invalid whence value") return self._pos #def seekable(self) -> bool: def seekable(self): """Return whether object supports random access. If False, seek(), tell() and truncate() will raise IOError. This method may need to do a test seek(). """ return True #def fileno(self) -> int: def fileno(self): """Returns underlying file descriptor if one exists. An IOError is raised if the IO object does not use a file descriptor. """ self._checkClosed() self._node._v_file.fileno() #def close(self) -> None: def close(self): """Flush and close the IO object. This method has no effect if the file is already closed. """ if not self.closed: if getattr(self._node, '_v_file', None) is None: warnings.warn("host PyTables file is already closed!") try: super(RawPyTablesIO, self).close() finally: # Release node object to allow closing the file. self._node = None def flush(self): """Flush write buffers, if applicable. This is not implemented for read-only and non-blocking streams. """ self._checkClosed() self._node.flush() #def truncate(self, pos: int = None) -> int: def truncate(self, pos=None): """Truncate file to size bytes. Size defaults to the current IO position as reported by tell(). Return the new size. Currently, this method only makes sense to grow the file node, since data can not be rewritten nor deleted. """ self._checkClosed() self._checkWritable() if pos is None: pos = self._pos elif pos < 0: raise ValueError("negative truncate position %r" % (pos,)) if pos < self._node.nrows: raise IOError("truncating is only allowed for growing a file") self._append_zeros(pos - self._node.nrows) return self.seek(pos) #def readable(self) -> bool: def readable(self): """Return whether object was opened for reading. If False, read() will raise IOError. """ mode = self._mode return 'r' in mode or '+' in mode #def writable(self) -> bool: def writable(self): """Return whether object was opened for writing. If False, write() and truncate() will raise IOError. """ mode = self._mode return 'w' in mode or 'a' in mode or '+' in mode #def readinto(self, b: bytearray) -> int: def readinto(self, b): """Read up to len(b) bytes into b. Returns number of bytes read (0 for EOF), or None if the object is set not to block as has no data to read. """ self._checkClosed() self._checkReadable() if self._pos >= self._node.nrows: return 0 n = len(b) start = self._pos stop = self._pos + n # XXX optimized path #if stop <= self._node.nrows and isinstance(b, np.ndarray): # self._node.read(start, stop, out=b) # self._pos += n # return n if stop > self._node.nrows: stop = self._node.nrows n = stop - start # XXX This ought to work with anything that supports the buffer API b[:n] = self._node.read(start, stop).tostring() self._pos += n return n #def readline(self, limit: int = -1) -> bytes: def readline(self, limit=-1): """Read and return a line from the stream. If limit is specified, at most limit bytes will be read. The line terminator is always ``\\n`` for binary files; for text files, the newlines argument to open can be used to select the line terminator(s) recognized. """ self._checkClosed() self._checkReadable() chunksize = self._node.chunkshape[0] # XXX: check lsep = b'\n' lseplen = len(lsep) # Set the remaining bytes to read to the specified size. remsize = limit partial = [] finished = False while not finished: # Read a string limited by the remaining number of bytes. if limit <= 0: ibuff = self.read(chunksize) else: ibuff = self.read(min(remsize, chunksize)) ibufflen = len(ibuff) remsize -= ibufflen if ibufflen >= lseplen: # Separator fits, look for EOL string. eolindex = ibuff.find(lsep) elif ibufflen == 0: # EOF was immediately reached. finished = True continue else: # ibufflen < lseplen # EOF was hit and separator does not fit. ;) partial.append(ibuff) finished = True continue if eolindex >= 0: # Found an EOL. If there are trailing characters, # cut the input buffer and seek back; # else add the whole input buffer. trailing = ibufflen - lseplen - eolindex # Bytes beyond EOL. if trailing > 0: obuff = ibuff[:-trailing] self.seek(-trailing, 1) remsize += trailing else: obuff = ibuff finished = True elif lseplen > 1 and (limit <= 0 or remsize > 0): # Seek back a little since the end of the read string # may have fallen in the middle of the line separator. obuff = ibuff[:-lseplen + 1] self.seek(-lseplen + 1, 1) remsize += lseplen - 1 else: # eolindex<0 and (lseplen<=1 or (limit>0 and remsize<=0)) # Did not find an EOL, add the whole input buffer. obuff = ibuff # Append (maybe cut) buffer. partial.append(obuff) # If a limit has been specified and the remaining count # reaches zero, the reading is finished. if limit > 0 and remsize <= 0: finished = True return b''.join(partial) #def write(self, b: bytes) -> int: def write(self, b): """Write the given buffer to the IO stream. Returns the number of bytes written, which may be less than len(b). """ self._checkClosed() self._checkWritable() if isinstance(b, unicode): raise TypeError("can't write str to binary stream") n = len(b) if n == 0: return 0 pos = self._pos # Is the pointer beyond the real end of data? end2off = pos - self._node.nrows if end2off > 0: # Zero-fill the gap between the end of data and the pointer. self._append_zeros(end2off) # Append data. self._node.append( np.ndarray(buffer=b, dtype=self._vtype, shape=self._vshape(n))) self._pos += n return n def _checkClosed(self): """Checks if file node is open. Checks whether the file node is open or has been closed. In the second case, a ValueError is raised. If the host PyTables has been closed, ValueError is also raised. """ super(RawPyTablesIO, self)._checkClosed() if getattr(self._node, '_v_file', None) is None: raise ValueError("host PyTables file is already closed!") def _check_node(self, node): if not isinstance(node, tables.EArray): raise TypeError('the "node" parameter should be a tables.EArray') if not isinstance(node.atom, tables.UInt8Atom): raise TypeError('only nodes with atom "UInt8Atom" are allowed') def _check_mode(self, mode): if not isinstance(mode, str): raise TypeError("invalid mode: %r" % mode) modes = set(mode) if modes - set("arwb+tU") or len(mode) > len(modes): raise ValueError("invalid mode: %r" % mode) reading = "r" in modes writing = "w" in modes appending = "a" in modes #updating = "+" in modes text = "t" in modes binary = "b" in modes if "U" in modes: if writing or appending: raise ValueError("can't use U and writing mode at once") reading = True if text and binary: raise ValueError("can't have text and binary mode at once") if reading + writing + appending > 1: raise ValueError("can't have read/write/append mode at once") if not (reading or writing or appending): raise ValueError("must have exactly one of read/write/append mode") def _cross_check_mode(self, mode, h5filemode): # XXX: check #readable = bool('r' in mode or '+' in mode) #h5readable = bool('r' in h5filemode or '+' in h5filemode) # #if readable and not h5readable: # raise ValueError("RawPyTablesIO can't be open in read mode if " # "the underlying hdf5 file is not readable") writable = bool('w' in mode or 'a' in mode or '+' in mode) h5writable = bool('w' in h5filemode or 'a' in h5filemode or '+' in h5filemode) if writable and not h5writable: raise ValueError("RawPyTablesIO can't be open in write mode if " "the underlying hdf5 file is not writable") def _check_attributes(self, node): """Checks file node-specific attributes. Checks for the presence and validity of the system attributes 'NODE_TYPE' and 'NODE_TYPE_VERSION' in the specified PyTables node (leaf). ValueError is raised if an attribute is missing or incorrect. """ attrs = node.attrs ltype = getattr(attrs, 'NODE_TYPE', None) ltypever = getattr(attrs, 'NODE_TYPE_VERSION', None) if ltype != NodeType: raise ValueError("invalid type of node object: %s" % (ltype,)) if ltypever not in NodeTypeVersions: raise ValueError( "unsupported type version of node object: %s" % (ltypever,)) _checkAttributes = previous_api(_check_attributes) def _append_zeros(self, size): """_append_zeros(size) -> None. Appends a string of zeros. Appends a string of 'size' zeros to the array, without moving the file pointer. """ # Appending an empty array would raise an error. if size == 0: return # XXX This may be redone to avoid a potentially large in-memory array. self._node.append( np.zeros(dtype=self._vtype, shape=self._vshape(size))) class FileNodeMixin(object): """Mixin class for FileNode objects. It provides access to the attribute set of the node that becomes available via the attrs property. You can add attributes there, but try to avoid attribute names in all caps or starting with '_', since they may clash with internal attributes. """ # The attribute set property methods. def _get_attrs(self): """Returns the attribute set of the file node.""" #sefl._checkClosed() return self._node.attrs getAttrs = previous_api(_get_attrs) def _set_attrs(self, value): """set_attrs(string) -> None. Raises ValueError.""" raise ValueError("changing the whole attribute set is not allowed") setAttrs = previous_api(_set_attrs) def _del_attrs(self): """del_attrs() -> None. Raises ValueError.""" raise ValueError("deleting the whole attribute set is not allowed") delAttrs = previous_api(_del_attrs) # The attribute set property. attrs = property( _get_attrs, _set_attrs, _del_attrs, "A property pointing to the attribute set of the file node.") class ROFileNode(FileNodeMixin, RawPyTablesIO): """Creates a new read-only file node. Creates a new read-only file node associated with the specified PyTables node, providing a standard Python file interface to it. The node has to have been created on a previous occasion using the new_node() function. The node used as storage is also made available via the read-only attribute node. Please do not tamper with this object if it's avoidable, since you may break the operation of the file node object. The constructor is not intended to be used directly. Use the open_node() function in read-only mode ('r') instead. :Version 1: implements the file storage as a UInt8 uni-dimensional EArray. :Version 2: uses an UInt8 N vector EArray. .. versionchanged:: 3.0 The offset attribute is no more available, please use seek/tell methods instead. .. versionchanged:: 3.0 The line_separator property is no more available. The only line separator used for binary I/O is ``\\n``. """ def __init__(self, node): RawPyTablesIO.__init__(self, node, 'r') self._checkReadable() @property def node(self): return self._node class RAFileNode(FileNodeMixin, RawPyTablesIO): """Creates a new read-write file node. The first syntax opens the specified PyTables node, while the second one creates a new node in the specified PyTables file. In the second case, additional named arguments 'where' and 'name' must be passed to specify where the file node is to be created. Other named arguments such as 'title' and 'filters' may also be passed. The special named argument 'expectedsize', indicating an estimate of the file size in bytes, may also be passed. Write access means reading as well as appending data is allowed. The node used as storage is also made available via the read-only attribute node. Please do not tamper with this object if it's avoidable, since you may break the operation of the file node object. The constructor is not intended to be used directly. Use the new_node() or open_node() functions instead. :Version 1: implements the file storage as a UInt8 uni-dimensional EArray. :Version 2: uses an UInt8 N vector EArray. .. versionchanged:: 3.0 The offset attribute is no more available, please use seek/tell methods instead. .. versionchanged:: 3.0 The line_separator property is no more available. The only line separator used for binary I/O is ``\\n``. """ # The atom representing a byte in the array, for each version. _byte_shape = [ None, (0, 1), (0,), ] __allowed_init_kwargs = [ 'where', 'name', 'title', 'filters', 'expectedsize'] def __init__(self, node, h5file, **kwargs): if node is not None: # Open an existing node and get its version. self._check_attributes(node) self._version = node.attrs.NODE_TYPE_VERSION elif h5file is not None: # Check for allowed keyword arguments, # to avoid unwanted arguments falling through to array constructor. for kwarg in kwargs: if kwarg not in self.__allowed_init_kwargs: raise TypeError( "%s keyword argument is not allowed" % repr(kwarg)) # Turn 'expectedsize' into 'expectedrows'. if 'expectedsize' in kwargs: # These match since one byte is stored per row. expectedrows = kwargs['expectedsize'] kwargs = kwargs.copy() del kwargs['expectedsize'] kwargs['expectedrows'] = expectedrows # Create a new array in the specified PyTables file. self._version = NodeTypeVersions[-1] shape = self._byte_shape[self._version] node = h5file.create_earray( atom=tables.UInt8Atom(), shape=shape, **kwargs) # Set the node attributes, else remove the array itself. try: self._set_attributes(node) except RuntimeError: h5file.remove_node(kwargs['where'], kwargs['name']) raise RawPyTablesIO.__init__(self, node, 'a+') self._checkReadable() self._checkWritable() @property def node(self): return self._node def _set_attributes(self, node): """_set_attributes(node) -> None. Adds file node-specific attributes. Sets the system attributes 'NODE_TYPE' and 'NODE_TYPE_VERSION' in the specified PyTables node (leaf). """ attrs = node.attrs attrs.NODE_TYPE = NodeType attrs.NODE_TYPE_VERSION = NodeTypeVersions[-1] _setAttributes = previous_api(_set_attributes) def new_node(h5file, **kwargs): """Creates a new file node object in the specified PyTables file object. Additional named arguments where and name must be passed to specify where the file node is to be created. Other named arguments such as title and filters may also be passed. The special named argument expectedsize, indicating an estimate of the file size in bytes, may also be passed. It returns the file node object. """ return RAFileNode(None, h5file, **kwargs) newNode = previous_api(new_node) def open_node(node, mode='r'): """Opens an existing file node. Returns a file node object from the existing specified PyTables node. If mode is not specified or it is 'r', the file can only be read, and the pointer is positioned at the beginning of the file. If mode is 'a+', the file can be read and appended, and the pointer is positioned at the end of the file. """ if mode == 'r': return ROFileNode(node) elif mode == 'a+': return RAFileNode(node, None) else: raise IOError("invalid mode: %s" % (mode,)) openNode = previous_api(open_node) def save_to_filenode(h5file, filename, where, name=None, overwrite=False, title="", filters=None): """Save a file's contents to a filenode inside a PyTables file. .. versionadded:: 3.2 Parameters ---------- h5file The PyTables file to be written to; can be either a string giving the file's location or a :class:`File` object. If a file with name *h5file* already exists, it will be opened in mode ``a``. filename Path of the file which shall be stored within the PyTables file. where, name Location of the filenode where the data shall be stored. If *name* is not given, and *where* is either a :class:`Group` object or a string ending on ``/``, the leaf name will be set to the file name of *filename*. overwrite Whether or not a possibly existing filenode of the specified name shall be overwritten. title A description for this node (it sets the ``TITLE`` HDF5 attribute on disk). filters An instance of the :class:`Filters` class that provides information about the desired I/O filters to be applied during the life of this object. """ # sanity checks if not os.access(filename, os.R_OK): raise IOError("The file '%s' could not be read" % filename) if isinstance(h5file, tables.file.File) and h5file.mode == "r": raise IOError("The file '%s' is opened read-only" % h5file.filename) # guess filenode's name if necessary if (name is None and (isinstance(where, tables.group.Group) or (isinstance(where, string_types) and where.endswith("/")))): name = os.path.split(filename)[1] new_h5file = not isinstance(h5file, tables.file.File) f = tables.File(h5file, "a") if new_h5file else h5file # check for already existing filenode try: n = f.get_node(where=where, name=name) if not overwrite: if new_h5file: f.close() raise IOError("Specified node already exists in file '%s'" % f.filename) except tables.NoSuchNodeError: pass # read data from disk with open(filename, "rb") as fd: data = fd.read() if isinstance(where, string_types) and name is None: nodepath = where.split("/") where = "/" + "/".join(nodepath[:-1]) name = nodepath[-1] # remove existing filenode if present try: f.remove_node(where=where, name=name) except tables.NoSuchNodeError: pass # write file's contents to filenode fnode = new_node(f, where=where, name=name, title=title, filters=filters) fnode.write(data) fnode.close() # cleanup if new_h5file: f.close() def read_from_filenode(h5file, filename, where, name=None, overwrite=False, create_target=False): """Read a filenode from a PyTables file and write its contents to a file. .. versionadded:: 3.2 Parameters ---------- h5file The PyTables file to be read from; can be either a string giving the file's location or a :class:`File` object. filename Path of the file where the contents of the filenode shall be written to. If *filename* points to a directory or ends with ``/`` (``\`` on Windows), the filename will be set to the *name* attribute of the read filenode. where, name Location of the filenode where the data shall be read from. overwrite Whether or not a possibly existing file of the specified *filename* shall be overwritten. create_target Whether or not the folder hierarchy needed to accomodate the given target ``filename`` will be created. """ new_h5file = not isinstance(h5file, tables.file.File) f = tables.File(h5file, "r") if new_h5file else h5file fnode = open_node(f.get_node(where=where, name=name)) # guess output filename if necessary if os.path.isdir(filename) or filename.endswith(os.path.sep): filename = os.path.join(filename, fnode.node.name) if os.access(filename, os.R_OK) and not overwrite: if new_h5file: f.close() raise IOError("The file '%s' already exists" % filename) # create folder hierarchy if necessary if create_target and not os.path.isdir(os.path.split(filename)[0]): os.makedirs(os.path.split(filename)[0]) if not os.access(os.path.split(filename)[0], os.W_OK): if new_h5file: f.close() raise IOError("The file '%s' cannot be written to" % filename) # read data from filenode data = fnode.read() fnode.close() # store data to file with open(filename, "wb") as fd: fd.write(data) # cleanup del data if new_h5file: f.close() ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## End: PyTables-v.3.1.1/tables/nodes/tests/000077500000000000000000000000001231437614300172335ustar00rootroot00000000000000PyTables-v.3.1.1/tables/nodes/tests/__init__.py000066400000000000000000000005011231437614300213400ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: 2007-01-18 # Author: Ivan Vilata i Balaguer - ivan@selidor.net # # $Id$ # ######################################################################## """Unit tests for special node behaviours.""" PyTables-v.3.1.1/tables/nodes/tests/test_filenode.dat000066400000000000000000000063221231437614300225540ustar00rootroot00000000000000#define test_width 64 #define test_height 64 static char test_bits[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xC4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x1E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xB8, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xF8, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xF1, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0x1F, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x38, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3D, 0xEE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0x01, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x70, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x84, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x18, 0x7C, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x19, 0x7C, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x31, 0x3C, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0x0E, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0x87, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xC7, 0x9E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x31, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x38, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xBC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x07, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x03, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xF1, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x06, 0x9E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x42, 0x84, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x79, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x71, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x1E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x03, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x61, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xC1, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x19, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x19, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x71, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; PyTables-v.3.1.1/tables/nodes/tests/test_filenode.py000066400000000000000000001023121231437614300224300ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: October 2, 2004 # Author: Ivan Vilata i Balaguer - reverse:net.selidor@ivan # # $Id$ # ######################################################################## """Unit test for the filenode module.""" import unittest import tempfile import os import shutil import warnings import tables from tables.nodes import filenode from tables.tests import common class NewFileTestCase(common.TempFileMixin, common.PyTablesTestCase): "Tests creating a new file node with the new_node() function." def test00_NewFile(self): "Creation of a brand new file node." try: fnode = filenode.new_node(self.h5file, where='/', name='test') node = self.h5file.get_node('/test') except LookupError: self.fail("filenode.new_node() failed to create a new node.") else: self.assertEqual( fnode.node, node, "filenode.new_node() created a node in the wrong place.") def test01_NewFileTooFewArgs(self): "Creation of a new file node without arguments for node creation." self.assertRaises(TypeError, filenode.new_node, self.h5file) def test02_NewFileWithExpectedSize(self): "Creation of a new file node with 'expectedsize' argument." try: filenode.new_node( self.h5file, where='/', name='test', expectedsize=100000) except TypeError: self.fail("\ filenode.new_node() failed to accept 'expectedsize' argument.") def test03_NewFileWithExpectedRows(self): "Creation of a new file node with illegal 'expectedrows' argument." self.assertRaises( TypeError, filenode.new_node, self.h5file, where='/', name='test', expectedrows=100000) class ClosedFileTestCase(common.TempFileMixin, common.PyTablesTestCase): "Tests calling several methods on a closed file." def setUp(self): """setUp() -> None This method sets the following instance attributes: * 'h5fname', the name of the temporary HDF5 file * 'h5file', the writable, temporary HDF5 file with a '/test' node * 'fnode', the closed file node in '/test' """ super(ClosedFileTestCase, self).setUp() self.fnode = filenode.new_node(self.h5file, where='/', name='test') self.fnode.close() def tearDown(self): """tearDown() -> None Closes 'h5file'; removes 'h5fname'. """ self.fnode = None super(ClosedFileTestCase, self).tearDown() # All these tests mey seem odd, but Python (2.3) files # do test whether the file is not closed regardless of their mode. def test00_Close(self): "Closing a closed file." try: self.fnode.close() except ValueError: self.fail("Could not close an already closed file.") def test01_Flush(self): "Flushing a closed file." self.assertRaises(ValueError, self.fnode.flush) def test02_Next(self): "Getting the next line of a closed file." self.assertRaises(ValueError, self.fnode.next) def test03_Read(self): "Reading a closed file." self.assertRaises(ValueError, self.fnode.read) def test04_Readline(self): "Reading a line from a closed file." self.assertRaises(ValueError, self.fnode.readline) def test05_Readlines(self): "Reading lines from a closed file." self.assertRaises(ValueError, self.fnode.readlines) def test06_Seek(self): "Seeking a closed file." self.assertRaises(ValueError, self.fnode.seek, 0) def test07_Tell(self): "Getting the pointer position in a closed file." self.assertRaises(ValueError, self.fnode.tell) def test08_Truncate(self): "Truncating a closed file." self.assertRaises(ValueError, self.fnode.truncate) def test09_Write(self): "Writing a closed file." self.assertRaises(ValueError, self.fnode.write, b'foo') def test10_Writelines(self): "Writing lines to a closed file." self.assertRaises(ValueError, self.fnode.writelines, [b'foo\n']) def copyFileToFile(srcfile, dstfile, blocksize=4096): """copyFileToFile(srcfile, dstfile[, blocksize]) -> None Copies a readable opened file 'srcfile' to a writable opened file 'destfile' in blocks of 'blocksize' bytes (4 KiB by default). """ data = srcfile.read(blocksize) while len(data) > 0: dstfile.write(data) data = srcfile.read(blocksize) class WriteFileTestCase(common.TempFileMixin, common.PyTablesTestCase): "Tests writing, seeking and truncating a new file node." datafname = 'test_filenode.dat' def setUp(self): """setUp() -> None This method sets the following instance attributes: * 'h5fname', the name of the temporary HDF5 file * 'h5file', the writable, temporary HDF5 file with a '/test' node * 'fnode', the writable file node in '/test' """ super(WriteFileTestCase, self).setUp() self.fnode = filenode.new_node(self.h5file, where='/', name='test') self.datafname = self._testFilename(self.datafname) def tearDown(self): """tearDown() -> None Closes 'fnode' and 'h5file'; removes 'h5fname'. """ self.fnode.close() self.fnode = None super(WriteFileTestCase, self).tearDown() def test00_WriteFile(self): "Writing a whole file node." datafile = open(self.datafname, 'rb') try: copyFileToFile(datafile, self.fnode) finally: datafile.close() def test01_SeekFile(self): "Seeking and writing file node." self.fnode.write(b'0123') self.fnode.seek(8) self.fnode.write(b'4567') self.fnode.seek(3) data = self.fnode.read(6) self.assertEqual( data, b'3\0\0\0\0'b'4', "Gap caused by forward seek was not properly filled.") self.fnode.seek(0) self.fnode.write(b'test') self.fnode.seek(0) data = self.fnode.read(4) self.assertNotEqual( data, b'test', "Data was overwritten instead of appended.") self.fnode.seek(-4, 2) data = self.fnode.read(4) self.assertEqual(data, b'test', "Written data was not appended.") self.fnode.seek(0, 2) oldendoff = self.fnode.tell() self.fnode.seek(-2, 2) self.fnode.write(b'test') newendoff = self.fnode.tell() self.assertEqual( newendoff, oldendoff - 2 + 4, "Pointer was not correctly moved on append.") def test02_TruncateFile(self): "Truncating a file node." self.fnode.write(b'test') self.fnode.seek(2) self.assertRaises(IOError, self.fnode.truncate) self.fnode.seek(6) self.fnode.truncate() self.fnode.seek(0) data = self.fnode.read() self.assertEqual(data, b'test\0\0', "File was not grown to the current offset.") self.fnode.truncate(8) self.fnode.seek(0) data = self.fnode.read() self.assertEqual(data, b'test\0\0\0\0', "File was not grown to an absolute size.") class OpenFileTestCase(common.TempFileMixin, common.PyTablesTestCase): "Tests opening an existing file node for reading and writing." def setUp(self): """setUp() -> None This method sets the following instance attributes: * 'h5fname', the name of the temporary HDF5 file * 'h5file', the writable, temporary HDF5 file with a '/test' node """ super(OpenFileTestCase, self).setUp() fnode = filenode.new_node(self.h5file, where='/', name='test') fnode.close() def test00_OpenFileRead(self): "Opening an existing file node for reading." node = self.h5file.get_node('/test') fnode = filenode.open_node(node) self.assertEqual( fnode.node, node, "filenode.open_node() opened the wrong node.") self.assertEqual( fnode.mode, 'r', "File was opened with an invalid mode %s." % repr(fnode.mode)) self.assertEqual( fnode.tell(), 0L, "Pointer is not positioned at the beginning of the file.") fnode.close() def test01_OpenFileReadAppend(self): "Opening an existing file node for reading and appending." node = self.h5file.get_node('/test') fnode = filenode.open_node(node, 'a+') self.assertEqual( fnode.node, node, "filenode.open_node() opened the wrong node.") self.assertEqual( fnode.mode, 'a+', "File was opened with an invalid mode %s." % repr(fnode.mode)) self.assertEqual( fnode.tell(), 0L, "Pointer is not positioned at the beginning of the file.") fnode.close() def test02_OpenFileInvalidMode(self): "Opening an existing file node with an invalid mode." self.assertRaises( IOError, filenode.open_node, self.h5file.get_node('/test'), 'w') # This no longer works since type and type version attributes # are now system attributes. ivb(2004-12-29) # def test03_OpenFileNoAttrs(self): ## "Opening a node with no type attributes." ## ## node = self.h5file.get_node('/test') ## self.h5file.del_node_attr('/test', '_type') ## # Another way to get the same result is changing the value. ## ##self.h5file.set_node_attr('/test', '_type', 'foobar') ## self.assertRaises(ValueError, filenode.open_node, node) class ReadFileTestCase(common.TempFileMixin, common.PyTablesTestCase): "Tests reading from an existing file node." datafname = 'test_filenode.xbm' def setUp(self): """setUp() -> None This method sets the following instance attributes: * 'datafile', the opened data file * 'h5fname', the name of the temporary HDF5 file * 'h5file', the writable, temporary HDF5 file with a '/test' node * 'fnode', the readable file node in '/test', with data in it """ self.datafname = self._testFilename(self.datafname) self.datafile = open(self.datafname, 'rb') super(ReadFileTestCase, self).setUp() fnode = filenode.new_node(self.h5file, where='/', name='test') copyFileToFile(self.datafile, fnode) fnode.close() self.datafile.seek(0) self.fnode = filenode.open_node(self.h5file.get_node('/test')) def tearDown(self): """tearDown() -> None Closes 'fnode', 'h5file' and 'datafile'; removes 'h5fname'. """ self.fnode.close() self.fnode = None super(ReadFileTestCase, self).tearDown() self.datafile.close() self.datafile = None def test00_CompareFile(self): "Reading and comparing a whole file node." # Try to use hashlib (included from Python 2.5 on) try: import hashlib dfiledigest = hashlib.md5(self.datafile.read()).digest() fnodedigest = hashlib.md5(self.fnode.read()).digest() except ImportError: import md5 dfiledigest = md5.new(self.datafile.read()).digest() fnodedigest = md5.new(self.fnode.read()).digest() self.assertEqual( dfiledigest, fnodedigest, "Data read from file node differs from that in the file on disk.") def test01_Write(self): "Writing on a read-only file." self.assertRaises(IOError, self.fnode.write, 'no way') def test02_UseAsImageFile(self): "Using a file node with Python Imaging Library." try: import Image Image.open(self.fnode) except ImportError: # PIL not available, nothing to do. pass except IOError: self.fail( "PIL was not able to create an image from the file node.") class ReadlineTestCase(common.TempFileMixin, common.PyTablesTestCase): """ Base class for text line-reading test cases. It provides a set of tests independent of the line separator string. Sub-classes must provide the 'line_separator' attribute. """ def setUp(self): """ This method sets the following instance attributes: * ``h5fname``: the name of the temporary HDF5 file. * ``h5file``: the writable, temporary HDF5 file with a ``/test`` node. * ``fnode``: the readable file node in ``/test``, with text in it. """ super(ReadlineTestCase, self).setUp() linesep = self.line_separator # Fill the node file with some text. fnode = filenode.new_node(self.h5file, where='/', name='test') #fnode.line_separator = linesep fnode.write(linesep) data = 'short line%sshort line%s%s' % ((linesep.decode('ascii'),) * 3) data = data.encode('ascii') fnode.write(data) fnode.write(b'long line ' * 20 + linesep) fnode.write(b'unterminated') fnode.close() # Re-open it for reading. self.fnode = filenode.open_node(self.h5file.get_node('/test')) #self.fnode.line_separator = linesep def tearDown(self): """tearDown() -> None Closes 'fnode' and 'h5file'; removes 'h5fname'. """ self.fnode.close() self.fnode = None super(ReadlineTestCase, self).tearDown() def test00_Readline(self): "Reading individual lines." linesep = self.line_separator line = self.fnode.readline() self.assertEqual(line, linesep) line = self.fnode.readline() # 'short line' + linesep line = self.fnode.readline() self.assertEqual(line, b'short line' + linesep) line = self.fnode.readline() self.assertEqual(line, linesep) line = self.fnode.readline() self.assertEqual(line, b'long line ' * 20 + linesep) line = self.fnode.readline() self.assertEqual(line, b'unterminated') line = self.fnode.readline() self.assertEqual(line, b'') line = self.fnode.readline() self.assertEqual(line, b'') def test01_ReadlineSeek(self): "Reading individual lines and seeking back and forth." linesep = self.line_separator lseplen = len(linesep) self.fnode.readline() # linesep self.fnode.readline() # 'short line' + linesep self.fnode.seek(-(lseplen + 4), 1) line = self.fnode.readline() self.assertEqual(line, b'line' + linesep, "Seeking back yielded different data.") self.fnode.seek(lseplen + 20, 1) # Into the long line. line = self.fnode.readline() self.assertEqual( line[-(lseplen + 10):], b'long line ' + linesep, "Seeking forth yielded unexpected data.") def test02_Iterate(self): "Iterating over the lines." linesep = self.line_separator # Iterate to the end. for line in self.fnode: pass self.assertRaises(StopIteration, self.fnode.next) self.fnode.seek(0) line = next(self.fnode) self.assertEqual(line, linesep) line = next(self.fnode) self.assertEqual(line, b'short line' + linesep) def test03_Readlines(self): "Reading a list of lines." linesep = self.line_separator lines = self.fnode.readlines() self.assertEqual(lines, [ linesep, b'short line' + linesep, b'short line' + linesep, linesep, b'long line ' * 20 + linesep, b'unterminated']) def test04_ReadlineSize(self): "Reading individual lines of limited size." linesep = self.line_separator lseplen = len(linesep) line = self.fnode.readline() # linesep line = self.fnode.readline(lseplen + 20) self.assertEqual(line, b'short line' + linesep) line = self.fnode.readline(5) self.assertEqual(line, b'short') line = self.fnode.readline(lseplen + 20) self.assertEqual(line, b' line' + linesep) line = self.fnode.readline(lseplen) self.assertEqual(line, linesep) self.fnode.seek(-4, 2) line = self.fnode.readline(4) self.assertEqual(line, b'ated') self.fnode.seek(-4, 2) line = self.fnode.readline(20) self.assertEqual(line, b'ated') def test05_ReadlinesSize(self): "Reading a list of lines with a limited size." linesep = self.line_separator data = '%sshort line%sshort' % ((linesep.decode('ascii'),) * 2) data = data.encode('ascii') lines = self.fnode.readlines(len(data)) #self.assertEqual(lines, [linesep, b'short line' + linesep, b'short']) # #line = self.fnode.readline() #self.assertEqual(line, b' line' + linesep) # NOTE: the test is relaxed because the *hint* parameter of # io.BaseIO.readlines controls the amout of read data in a coarse way self.assertEqual(len(lines), len(data.split(b'\n'))) self.assertEqual(lines[:-1], [linesep, b'short line' + linesep]) self.assertTrue(lines[-1].startswith(b'short')) class MonoReadlineTestCase(ReadlineTestCase): "Tests reading one-byte-separated text lines from an existing file node." line_separator = b'\n' #class MultiReadlineTestCase(ReadlineTestCase): # "Tests reading multibyte-separated text lines from an existing file node." # # line_separator = b'
    ' #class LineSeparatorTestCase(common.TempFileMixin, common.PyTablesTestCase): # "Tests text line separator manipulation in a file node." # # def setUp(self): # """setUp() -> None # # This method sets the following instance attributes: # * 'h5fname', the name of the temporary HDF5 file # * 'h5file', the writable, temporary HDF5 file with a '/test' node # * 'fnode', the writable file node in '/test' # """ # super(LineSeparatorTestCase, self).setUp() # self.fnode = filenode.new_node(self.h5file, where='/', name='test') # # def tearDown(self): # """tearDown() -> None # # Closes 'fnode' and 'h5file'; removes 'h5fname'. # """ # self.fnode.close() # self.fnode = None # super(LineSeparatorTestCase, self).tearDown() # # def test00_DefaultLineSeparator(self): # "Default line separator." # # self.assertEqual( # self.fnode.line_separator, os.linesep.encode('ascii'), # "Default line separator does not match that in os.linesep.") # # def test01_SetLineSeparator(self): # "Setting a valid line separator." # # try: # self.fnode.line_separator = b'SEPARATOR' # except ValueError: # self.fail("Valid line separator was not accepted.") # else: # self.assertEqual( # self.fnode.line_separator, b'SEPARATOR', # "Line separator was not correctly set.") # # def test02_SetInvalidLineSeparator(self): # "Setting an invalid line separator." # # self.assertRaises( # ValueError, setattr, self.fnode, 'line_separator', b'') # self.assertRaises( # ValueError, setattr, self.fnode, 'line_separator', b'x' * 1024) # self.assertRaises( # TypeError, setattr, self.fnode, 'line_separator', u'x') class AttrsTestCase(common.TempFileMixin, common.PyTablesTestCase): "Tests setting and getting file node attributes." def setUp(self): """setUp() -> None This method sets the following instance attributes: * 'h5fname', the name of the temporary HDF5 file * 'h5file', the writable, temporary HDF5 file with a '/test' node * 'fnode', the writable file node in '/test' """ super(AttrsTestCase, self).setUp() self.fnode = filenode.new_node(self.h5file, where='/', name='test') def tearDown(self): """tearDown() -> None Closes 'fnode' and 'h5file'; removes 'h5fname'. """ self.fnode.close() self.fnode = None super(AttrsTestCase, self).tearDown() # This no longer works since type and type version attributes # are now system attributes. ivb(2004-12-29) # def test00_GetTypeAttr(self): ## "Getting the type attribute of a file node." ## ## self.assertEqual( ## getattr(self.fnode.attrs, '_type', None), filenode.NodeType, ## "File node has no '_type' attribute.") def test00_MangleTypeAttrs(self): "Mangling the type attributes on a file node." nodeType = getattr(self.fnode.attrs, 'NODE_TYPE', None) self.assertEqual( nodeType, filenode.NodeType, "File node does not have a valid 'NODE_TYPE' attribute.") nodeTypeVersion = getattr(self.fnode.attrs, 'NODE_TYPE_VERSION', None) self.assertTrue( nodeTypeVersion in filenode.NodeTypeVersions, "File node does not have a valid 'NODE_TYPE_VERSION' attribute.") # System attributes are now writable. ivb(2004-12-30) # self.assertRaises( ## AttributeError, ## setattr, self.fnode.attrs, 'NODE_TYPE', 'foobar') # self.assertRaises( ## AttributeError, ## setattr, self.fnode.attrs, 'NODE_TYPE_VERSION', 'foobar') # System attributes are now removables. F. Alted (2007-03-06) # self.assertRaises( # AttributeError, # delattr, self.fnode.attrs, 'NODE_TYPE') # self.assertRaises( # AttributeError, # delattr, self.fnode.attrs, 'NODE_TYPE_VERSION') # System attributes are now writable. ivb(2004-12-30) # def test01_SetSystemAttr(self): ## "Setting a system attribute on a file node." ## ## self.assertRaises( # AttributeError, setattr, self.fnode.attrs, 'CLASS', 'foobar') def test02_SetGetDelUserAttr(self): "Setting a user attribute on a file node." self.assertEqual( getattr(self.fnode.attrs, 'userAttr', None), None, "Inexistent attribute has a value that is not 'None'.") self.fnode.attrs.userAttr = 'foobar' self.assertEqual( getattr(self.fnode.attrs, 'userAttr', None), 'foobar', "User attribute was not correctly set.") self.fnode.attrs.userAttr = 'bazquux' self.assertEqual( getattr(self.fnode.attrs, 'userAttr', None), 'bazquux', "User attribute was not correctly changed.") del self.fnode.attrs.userAttr self.assertEqual( getattr(self.fnode.attrs, 'userAttr', None), None, "User attribute was not deleted.") # Another way is looking up the attribute in the attribute list. # if 'userAttr' in self.fnode.attrs._f_list(): ## self.fail("User attribute was not deleted.") def test03_AttrsOnClosedFile(self): "Accessing attributes on a closed file node." self.fnode.close() self.assertRaises(AttributeError, getattr, self.fnode, 'attrs') class ClosedH5FileTestCase(common.TempFileMixin, common.PyTablesTestCase): "Tests accessing a file node in a closed PyTables file." def setUp(self): """setUp() -> None This method sets the following instance attributes: * 'h5fname', the name of the temporary HDF5 file * 'h5file', the closed HDF5 file with a '/test' node * 'fnode', the writable file node in '/test' """ super(ClosedH5FileTestCase, self).setUp() self.fnode = filenode.new_node(self.h5file, where='/', name='test') self.h5file.close() def tearDown(self): """tearDown() -> None Closes 'fnode'; removes 'h5fname'. """ # ivilata: We know that a UserWarning will be raised # because the PyTables file has already been closed. # However, we don't want it to pollute the test output. warnings.filterwarnings('ignore', category=UserWarning) try: self.fnode.close() except ValueError: pass finally: warnings.filterwarnings('default', category=UserWarning) self.fnode = None super(ClosedH5FileTestCase, self).tearDown() def test00_Write(self): "Writing to a file node in a closed PyTables file." self.assertRaises(ValueError, self.fnode.write, 'data') def test01_Attrs(self): "Accessing the attributes of a file node in a closed PyTables file." self.assertRaises(ValueError, getattr, self.fnode, 'attrs') class OldVersionTestCase(common.PyTablesTestCase): """Base class for old version compatibility test cases. It provides some basic tests for file operations and attribute handling. Sub-classes must provide the 'oldversion' attribute and the 'oldh5fname' attribute. """ def setUp(self): """ This method sets the following instance attributes: * ``h5fname``: the name of the temporary HDF5 file. * ``h5file``: the writable, temporary HDF5 file with a ``/test`` node. * ``fnode``: the readable file node in ``/test``. """ self.h5fname = tempfile.mktemp(suffix='.h5') self.oldh5fname = self._testFilename(self.oldh5fname) oldh5f = tables.open_file(self.oldh5fname) oldh5f.copy_file(self.h5fname) oldh5f.close() self.h5file = tables.open_file( self.h5fname, 'r+', title="Test for file node old version compatibility") self.fnode = filenode.open_node(self.h5file.root.test, 'a+') def tearDown(self): """Closes ``fnode`` and ``h5file``; removes ``h5fname``.""" self.fnode.close() self.fnode = None self.h5file.close() self.h5file = None os.remove(self.h5fname) def test00_Read(self): "Reading an old version file node." #self.fnode.line_separator = '\n' line = self.fnode.readline() self.assertEqual(line, 'This is only\n') line = self.fnode.readline() self.assertEqual(line, 'a test file\n') line = self.fnode.readline() self.assertEqual(line, 'for FileNode version %d\n' % self.oldversion) line = self.fnode.readline() self.assertEqual(line, '') self.fnode.seek(0) line = self.fnode.readline() self.assertEqual(line, 'This is only\n') def test01_Write(self): "Writing an old version file node." #self.fnode.line_separator = '\n' self.fnode.write('foobar\n') self.fnode.seek(-7, 2) line = self.fnode.readline() self.assertEqual(line, 'foobar\n') def test02_Attributes(self): "Accessing attributes in an old version file node." self.fnode.attrs.userAttr = 'foobar' self.assertEqual( getattr(self.fnode.attrs, 'userAttr', None), 'foobar', "User attribute was not correctly set.") self.fnode.attrs.userAttr = 'bazquux' self.assertEqual( getattr(self.fnode.attrs, 'userAttr', None), 'bazquux', "User attribute was not correctly changed.") del self.fnode.attrs.userAttr self.assertEqual( getattr(self.fnode.attrs, 'userAttr', None), None, "User attribute was not deleted.") class Version1TestCase(OldVersionTestCase): "Basic test for version 1 format compatibility." oldversion = 1 oldh5fname = 'test_filenode_v1.h5' class DirectReadWriteTestCase(common.TempFileMixin, common.PyTablesTestCase): datafname = 'test_filenode.dat' def setUp(self): """ This method sets the following instance attributes: * ``h5fname``: the name of the temporary HDF5 file. * ``h5file``, the writable, temporary HDF5 file with a '/test' node * ``datafname``: the name of the data file to be stored in the temporary HDF5 file. * ``data``: the contents of the file ``datafname`` * ``testfname``: the name of a temporary file to be written to. """ super(DirectReadWriteTestCase, self).setUp() self.datafname = self._testFilename(self.datafname) self.testfname = tempfile.mktemp() self.testh5fname = tempfile.mktemp(suffix=".h5") with open(self.datafname, "rb") as fd: self.data = fd.read() self.testdir = tempfile.mkdtemp() def tearDown(self): """tearDown() -> None Closes 'fnode' and 'h5file'; removes 'h5fname'. """ super(DirectReadWriteTestCase, self).tearDown() if os.access(self.testfname, os.R_OK): os.remove(self.testfname) if os.access(self.testh5fname, os.R_OK): os.remove(self.testh5fname) shutil.rmtree(self.testdir) def test01_WriteToFilename(self): # write contents of datafname to h5 testfile filenode.save_to_filenode(self.testh5fname, self.datafname, "/test1") # make sure writing to an existing node doesn't work ... self.assertRaises(IOError, filenode.save_to_filenode, self.testh5fname, self.datafname, "/test1") # ... except if overwrite is True filenode.save_to_filenode(self.testh5fname, self.datafname, "/test1", overwrite=True) # write again, this time specifying a name filenode.save_to_filenode(self.testh5fname, self.datafname, "/", name="test2") # read from test h5file filenode.read_from_filenode(self.testh5fname, self.testfname, "/test1") # and compare result to what it should be with open(self.testfname, "rb") as fd: self.assertEqual(fd.read(), self.data) # make sure extracting to an existing file doesn't work ... self.assertRaises(IOError, filenode.read_from_filenode, self.testh5fname, self.testfname, "/test1") # except overwrite is True. And try reading with a name filenode.read_from_filenode(self.testh5fname, self.testfname, "/", name="test2", overwrite=True) # and compare to what it should be with open(self.testfname, "rb") as fd: self.assertEqual(fd.read(), self.data) # cleanup os.remove(self.testfname) os.remove(self.testh5fname) def test02_WriteToHDF5File(self): # write contents of datafname to h5 testfile filenode.save_to_filenode(self.h5file, self.datafname, "/test1") # make sure writing to an existing node doesn't work ... self.assertRaises(IOError, filenode.save_to_filenode, self.h5file, self.datafname, "/test1") # ... except if overwrite is True filenode.save_to_filenode(self.h5file, self.datafname, "/test1", overwrite=True) # read from test h5file filenode.read_from_filenode(self.h5file, self.testfname, "/test1") # and compare result to what it should be with open(self.testfname, "rb") as fd: self.assertEqual(fd.read(), self.data) # make sure extracting to an existing file doesn't work ... self.assertRaises(IOError, filenode.read_from_filenode, self.h5file, self.testfname, "/test1") # make sure the original h5file is still alive and kicking self.assertEqual(isinstance(self.h5file, tables.file.File), True) self.assertEqual(self.h5file.mode, "w") def test03_AutomaticNameGuessing(self): # write using the filename as node name filenode.save_to_filenode(self.testh5fname, self.datafname, "/") # and read again datafname = os.path.split(self.datafname)[1] filenode.read_from_filenode(self.testh5fname, self.testdir, "/", name=datafname) # test if the output file really has the expected name self.assertEqual(os.access(os.path.join(self.testdir, datafname), os.R_OK), True) # and compare result to what it should be with open(os.path.join(self.testdir, datafname), "rb") as fd: self.assertEqual(fd.read(), self.data) #---------------------------------------------------------------------- def suite(): """suite() -> test suite Returns a test suite consisting of all the test cases in the module. """ theSuite = unittest.TestSuite() theSuite.addTest(unittest.makeSuite(NewFileTestCase)) theSuite.addTest(unittest.makeSuite(ClosedFileTestCase)) theSuite.addTest(unittest.makeSuite(WriteFileTestCase)) theSuite.addTest(unittest.makeSuite(OpenFileTestCase)) theSuite.addTest(unittest.makeSuite(ReadFileTestCase)) theSuite.addTest(unittest.makeSuite(MonoReadlineTestCase)) #theSuite.addTest(unittest.makeSuite(MultiReadlineTestCase)) #theSuite.addTest(unittest.makeSuite(LineSeparatorTestCase)) theSuite.addTest(unittest.makeSuite(AttrsTestCase)) theSuite.addTest(unittest.makeSuite(ClosedH5FileTestCase)) theSuite.addTest(unittest.makeSuite(DirectReadWriteTestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## End: PyTables-v.3.1.1/tables/nodes/tests/test_filenode.xbm000066400000000000000000000063221231437614300225720ustar00rootroot00000000000000#define test_width 64 #define test_height 64 static char test_bits[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xC4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x1E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xB8, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xF8, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xF1, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0x1F, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x38, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3D, 0xEE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0x01, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x70, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x84, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x18, 0x7C, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x19, 0x7C, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x31, 0x3C, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0x0E, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0x87, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xC7, 0x9E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x31, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x38, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xBC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x07, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x03, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xF1, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x06, 0x9E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x42, 0x84, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x79, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x71, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x1E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x03, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x61, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xC1, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x19, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x19, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x71, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; PyTables-v.3.1.1/tables/nodes/tests/test_filenode_v1.h5000066400000000000000000000215461231437614300227330ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ`#ÿÿÿÿÿÿÿÿ HEAP€testðTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà `Ø8((0ÿÿÿÿÿÿÿÿ (ôtYE (CLASSEARRAY 8 EXTDIM SNODÐTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô8ô€` @TITLEFileNode version 1 test file (CLASSGROUP (VERSION1.0 ÐFILTERS°ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I0 sS'complevel' p7 I0 sS'fletcher32' p8 I0 sS'complib' p9 S'zlib' p10 sb. 8PYTABLES_FORMAT_VERSION1.3This is only a test file for FileNode version 1  0FLAVOR numarray (VERSION1.1 (TITLE`P 0 NODE_TYPEfile  H NODE_TYPE_VERSION  PyTables-v.3.1.1/tables/parameters.py000066400000000000000000000343251231437614300175050ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: February 25, 2005 # Author: Ivan Vilata - reverse:net.selidor@ivan # # $Id$ # ######################################################################## """Parameters for PyTables.""" __docformat__ = 'reStructuredText' """The format of documentation strings in this module.""" _KB = 1024 """The size of a Kilobyte in bytes""" _MB = 1024 * _KB """The size of a Megabyte in bytes""" # Tunable parameters # ================== # Be careful when touching these! # Parameters for different internal caches # ---------------------------------------- BOUNDS_MAX_SIZE = 1 * _MB """The maximum size for bounds values cached during index lookups.""" BOUNDS_MAX_SLOTS = 4 * _KB """The maximum number of slots for the BOUNDS cache.""" ITERSEQ_MAX_ELEMENTS = 1 * _KB """The maximum number of iterator elements cached in data lookups.""" ITERSEQ_MAX_SIZE = 1 * _MB """The maximum space that will take ITERSEQ cache (in bytes).""" ITERSEQ_MAX_SLOTS = 128 """The maximum number of slots in ITERSEQ cache.""" LIMBOUNDS_MAX_SIZE = 256 * _KB """The maximum size for the query limits (for example, ``(lim1, lim2)`` in conditions like ``lim1 <= col < lim2``) cached during index lookups (in bytes).""" LIMBOUNDS_MAX_SLOTS = 128 """The maximum number of slots for LIMBOUNDS cache.""" TABLE_MAX_SIZE = 1 * _MB """The maximum size for table chunks cached during index queries.""" SORTED_MAX_SIZE = 1 * _MB """The maximum size for sorted values cached during index lookups.""" SORTEDLR_MAX_SIZE = 8 * _MB """The maximum size for chunks in last row cached in index lookups (in bytes).""" SORTEDLR_MAX_SLOTS = 1 * _KB """The maximum number of chunks for SORTEDLR cache.""" # Parameters for general cache behaviour # -------------------------------------- # # The next parameters will not be effective if passed to the # `open_file()` function (so, they can only be changed in a *global* # way). You can change them in the file, but this is strongly # discouraged unless you know well what you are doing. DISABLE_EVERY_CYCLES = 10 """The number of cycles in which a cache will be forced to be disabled if the hit ratio is lower than the LOWEST_HIT_RATIO (see below). This value should provide time enough to check whether the cache is being efficient or not.""" ENABLE_EVERY_CYCLES = 50 """The number of cycles in which a cache will be forced to be (re-)enabled, irregardingly of the hit ratio. This will provide a chance for checking if we are in a better scenario for doing caching again.""" LOWEST_HIT_RATIO = 0.6 """The minimum acceptable hit ratio for a cache to avoid disabling (and freeing) it.""" # Tunable parameters # ================== # Be careful when touching these! # Recommended maximum values # -------------------------- # Following are the recommended values for several limits. However, # these limits are somewhat arbitrary and can be increased if you have # enough resources. MAX_COLUMNS = 512 """Maximum number of columns in :class:`tables.Table` objects before a :exc:`tables.PerformanceWarning` is issued. This limit is somewhat arbitrary and can be increased. """ MAX_NODE_ATTRS = 4 * _KB """Maximum allowed number of attributes in a node.""" MAX_GROUP_WIDTH = 16 * _KB """Maximum allowed number of children hanging from a group.""" MAX_TREE_DEPTH = 2 * _KB """Maximum depth in object tree allowed.""" MAX_UNDO_PATH_LENGTH = 10 * _KB """Maximum length of paths allowed in undo/redo operations.""" # Cache limits # ------------ COND_CACHE_SLOTS = 128 """Maximum number of conditions for table queries to be kept in memory.""" CHUNK_CACHE_NELMTS = 521 """Number of elements for HDF5 chunk cache.""" CHUNK_CACHE_PREEMPT = 0.0 """Chunk preemption policy. This value should be between 0 and 1 inclusive and indicates how much chunks that have been fully read are favored for preemption. A value of zero means fully read chunks are treated no differently than other chunks (the preemption is strictly LRU) while a value of one means fully read chunks are always preempted before other chunks.""" CHUNK_CACHE_SIZE = 2 * _MB """Size (in bytes) for HDF5 chunk cache.""" # Size for new metadata cache system METADATA_CACHE_SIZE = 1 * _MB # 1 MB is the default for HDF5 """Size (in bytes) of the HDF5 metadata cache.""" # NODE_CACHE_SLOTS tells the number of nodes that fits in the cache. # # There are several forces driving the election of this number: # 1.- As more nodes, better chances to re-use nodes # --> better performance # 2.- As more nodes, the re-ordering of the LRU cache takes more time # --> less performance # 3.- As more nodes, the memory needs for PyTables grows, specially for table # writings (that could take double of memory than table reads!). # # The default value here is quite conservative. If you have a system # with tons of memory, and if you are touching regularly a very large # number of leaves, try increasing this value and see if it fits better # for you. Please report back your feedback. NODE_CACHE_SLOTS = 64 """Maximum number of nodes to be kept in the metadata cache. It is the number of nodes to be kept in the metadata cache. Least recently used nodes are unloaded from memory when this number of loaded nodes is reached. To load a node again, simply access it as usual. Nodes referenced by user variables and, in general, all nodes that are still open are registered in the node manager and can be quickly accessed even if they are not in the cache. Negative value means that all the touched nodes will be kept in an internal dictionary. This is the faster way to load/retrieve nodes. However, and in order to avoid a large memory comsumption, the user will be warned when the number of loaded nodes will reach the ``-NODE_CACHE_SLOTS`` value. Finally, a value of zero means that any cache mechanism is disabled. """ # Parameters for the I/O buffer in `Leaf` objects # ----------------------------------------------- IO_BUFFER_SIZE = 1 * _MB """The PyTables internal buffer size for I/O purposes. Should not exceed the amount of highest level cache size in your CPU.""" BUFFER_TIMES = 100 """The maximum buffersize/rowsize ratio before issuing a :exc:`tables.PerformanceWarning`.""" # Miscellaneous # ------------- EXPECTED_ROWS_EARRAY = 1000 """Default expected number of rows for :class:`EArray` objects.""" EXPECTED_ROWS_VLARRAY = 1000 """Default expected number of rows for :class:`VLArray` objects. .. versionadded:: 3.0 """ EXPECTED_ROWS_TABLE = 10000 """Default expected number of rows for :class:`Table` objects.""" PYTABLES_SYS_ATTRS = True """Set this to ``False`` if you don't want to create PyTables system attributes in datasets. Also, if set to ``False`` the possible existing system attributes are not considered for guessing the class of the node during its loading from disk (this work is delegated to the PyTables' class discoverer function for general HDF5 files).""" MAX_NUMEXPR_THREADS = None """The maximum number of threads that PyTables should use internally in Numexpr. If `None`, it is automatically set to the number of cores in your machine. In general, it is a good idea to set this to the number of cores in your machine or, when your machine has many of them (e.g. > 4), perhaps one less than this.""" MAX_BLOSC_THREADS = None """The maximum number of threads that PyTables should use internally in Blosc. If `None`, it is automatically set to the number of cores in your machine. In general, it is a good idea to set this to the number of cores in your machine or, when your machine has many of them (e.g. > 4), perhaps one less than this.""" USER_BLOCK_SIZE = 0 """Sets the user block size of a file. The default user block size is 0; it may be set to any power of 2 equal to 512 or greater (512, 1024, 2048, etc.). .. versionadded:: 3.0 """ # HDF5 driver management # ---------------------- DRIVER = None """The HDF5 driver that should be used for reading/writing to the file. Following drivers are supported: * H5FD_SEC2: this driver uses POSIX file-system functions like read and write to perform I/O to a single, permanent file on local disk with no system buffering. This driver is POSIX-compliant and is the default file driver for all systems. * H5FD_DIRECT: this is the H5FD_SEC2 driver except data is written to or read from the file synchronously without being cached by the system. * H5FD_WINDOWS: this driver was modified in HDF5-1.8.8 to be a wrapper of the POSIX driver, H5FD_SEC2. This change should not affect user applications. * H5FD_STDIO: this driver uses functions from the standard C stdio.h to perform I/O to a single, permanent file on local disk with additional system buffering. * H5FD_CORE: with this driver, an application can work with a file in memory for faster reads and writes. File contents are kept in memory until the file is closed. At closing, the memory version of the file can be written back to disk or abandoned. * H5FD_SPLIT: this file driver splits a file into two parts. One part stores metadata, and the other part stores raw data. This splitting a file into two parts is a limited case of the Multi driver. The following drivers are not currently supported: * H5FD_LOG: this is the H5FD_SEC2 driver with logging capabilities. * H5FD_FAMILY: with this driver, the HDF5 file’s address space is partitioned into pieces and sent to separate storage files using an underlying driver of the user’s choice. This driver is for systems that do not support files larger than 2 gigabytes. * H5FD_MULTI: with this driver, data can be stored in multiple files according to the type of the data. I/O might work better if data is stored in separate files based on the type of data. The Split driver is a special case of this driver. * H5FD_MPIO: this is the standard HDF5 file driver for parallel file systems. This driver uses the MPI standard for both communication and file I/O. * H5FD_MPIPOSIX: this parallel file system driver uses MPI for communication and POSIX file-system calls for file I/O. * H5FD_STREAM: this driver is no longer available. .. seealso:: the `Drivers section`_ of the `HDF5 User's Guide`_ for more information. .. note:: not all supported drivers are always available. For example the H5FD_WINDOWS driver is not available on non Windows platforms. If the user try to use a driver that is not available on the target platform a :exc:`RuntimeError` is raised. .. versionadded:: 3.0 .. _`Drivers section`: http://www.hdfgroup.org/HDF5/doc/UG/08_TheFile.html#Drivers .. _`HDF5 User's Guide`: http://www.hdfgroup.org/HDF5/doc/UG/index.html """ DRIVER_DIRECT_ALIGNMENT = 0 """Specifies the required alignment boundary in memory. A value of 0 (zero) means to use HDF5 Library’s default value. .. versionadded:: 3.0 """ DRIVER_DIRECT_BLOCK_SIZE = 0 """Specifies the file system block size. A value of 0 (zero) means to use HDF5 Library’s default value of 4KB. .. versionadded:: 3.0 """ DRIVER_DIRECT_CBUF_SIZE = 0 """Specifies the copy buffer size. A value of 0 (zero) means to use HDF5 Library’s default value. .. versionadded:: 3.0 """ # DRIVER_LOG_FLAGS = 0x0001ffff #"""Flags specifying the types of logging activity. # #.. versionadded:: 3.0 # #.. seeealso:: # http://www.hdfgroup.org/HDF5/doc/RM/RM_H5P.html#Property-SetFaplLog # #""" # # DRIVER_LOG_BUF_SIZE = 4 * _KB #"""The size of the logging buffers, in bytes. # # One buffer of size DRIVER_LOG_BUF_SIZE will be created for each of # H5FD_LOG_FILE_READ, H5FD_LOG_FILE_WRITE and H5FD_LOG_FLAVOR when those # flags are set; these buffers will not grow as the file increases in # size. # #.. versionadded:: 3.0 # #""" DRIVER_CORE_INCREMENT = 64 * _KB """Core driver memory increment. Specifies the increment by which allocated memory is to be increased each time more memory is required. .. versionadded:: 3.0 """ DRIVER_CORE_BACKING_STORE = 1 """Enables backing store for the core driver. With the H5FD_CORE driver, if the DRIVER_CORE_BACKING_STORE is set to 1 (True), the file contents are flushed to a file with the same name as this core file when the file is closed or access to the file is terminated in memory. The application is allowed to open an existing file with H5FD_CORE driver. In that case, if the DRIVER_CORE_BACKING_STORE is set to 1 and the flags for :func:`tables.open_file` is set to H5F_ACC_RDWR, any change to the file contents are saved to the file when the file is closed. If backing_store is set to 0 and the flags for :func:`tables.open_file` is set to H5F_ACC_RDWR, any change to the file contents will be lost when the file is closed. If the flags for :func:`tables.open_file` is set to H5F_ACC_RDONLY, no change to the file is allowed either in memory or on file. .. versionadded:: 3.0 """ DRIVER_CORE_IMAGE = None """String containing an HDF5 file image. If this oprion is passed to the :func:`tables.open_file` function then the returned file object is set up using the specified image. A file image can be retrieved from an existing (and opened) file object using the :meth:`tables.File.get_file_image` method. .. note:: requires HDF5 >= 1.8.9. .. versionadded:: 3.0 """ DRIVER_SPLIT_META_EXT = '-m.h5' """The extension for the metadata file used by the H5FD_SPLIT driver. If this option is passed to the :func:`tables.openFile` function along with driver='H5FD_SPLIT', the extension is appended to the name passed as the first parameter to form the name of the metadata file. If the string '%s' is used in the extension, the metadata file name is formed by replacing '%s' with the name passed as the first parameter instead. .. versionadded:: 3.1 """ DRIVER_SPLIT_RAW_EXT = '-r.h5' """The extension for the raw data file used by the H5FD_SPLIT driver. If this option is passed to the :func:`tables.openFile` function along with driver='H5FD_SPLIT', the extension is appended to the name passed as the first parameter to form the name of the raw data file. If the string '%s' is used in the extension, the raw data file name is formed by replacing '%s' with the name passed as the first parameter instead. .. versionadded:: 3.1 """ ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/path.py000066400000000000000000000125631231437614300162760ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: January 15, 2007 # Author: Ivan Vilata i Balaguer - ivan at selidor dot net # # $Id$ # ######################################################################## """Functionality related with node paths in a PyTables file. Variables ========= `__docformat`__ The format of documentation strings in this module. """ # Imports # ======= import re import warnings import keyword from tables.exceptions import NaturalNameWarning from tables._past import previous_api # Public variables # ================ __docformat__ = 'reStructuredText' """The format of documentation strings in this module.""" # Private variables # ================= _python_id_re = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') """Python identifier regular expression.""" _reserved_id_re = re.compile('^_[cfgv]_') """PyTables reserved identifier regular expression. - c: class variables - f: class public methods - g: class private methods - v: instance variables """ _hidden_name_re = re.compile('^_[pi]_') """Nodes with a name *matching* this expression are considered hidden. For instance, ``name`` whould be visible while ``_i_name`` would not. """ _hidden_path_re = re.compile('/_[pi]_') """Nodes with a path *containing* this expression are considered hidden. For instance, a node with a pathname like ``/a/b/c`` would be visible while nodes with pathnames like ``/a/c/_i_x`` or ``/a/_p_x/y`` would not. """ # Public functions # ================ def check_name_validity(name): """Check the validity of the `name` of an object. If the name is not valid, a ``ValueError`` is raised. If it is valid but it can not be used with natural naming, a `NaturalNameWarning` is issued. """ warnInfo = ( "you will not be able to use natural naming to access this object; " "using ``getattr()`` will still work, though") if not isinstance(name, basestring): # Python >= 2.3 raise TypeError("object name is not a string: %r" % (name,)) # Check whether `name` is a valid HDF5 name. # http://hdfgroup.org/HDF5/doc/UG/03_Model.html#Structure if name == '': raise ValueError("the empty string is not allowed as an object name") if name == '.': raise ValueError("``.`` is not allowed as an object name") if '/' in name: raise ValueError("the ``/`` character is not allowed " "in object names: %r" % name) # Check whether `name` is a valid Python identifier. if not _python_id_re.match(name): warnings.warn("object name is not a valid Python identifier: %r; " "it does not match the pattern ``%s``; %s" % (name, _python_id_re.pattern, warnInfo), NaturalNameWarning) return # However, Python identifiers and keywords have the same form. if keyword.iskeyword(name): warnings.warn("object name is a Python keyword: %r; %s" % (name, warnInfo), NaturalNameWarning) return # Still, names starting with reserved prefixes are not allowed. if _reserved_id_re.match(name): raise ValueError("object name starts with a reserved prefix: %r; " "it matches the pattern ``%s``" % (name, _reserved_id_re.pattern)) # ``__members__`` is the only exception to that rule. if name == '__members__': raise ValueError("``__members__`` is not allowed as an object name") checkNameValidity = previous_api(check_name_validity) def join_path(parentpath, name): """Join a *canonical* `parentpath` with a *non-empty* `name`. .. versionchanged:: 3.0 The *parentPath* parameter has been renamed into *parentpath*. >>> join_path('/', 'foo') '/foo' >>> join_path('/foo', 'bar') '/foo/bar' >>> join_path('/foo', '/foo2/bar') '/foo/foo2/bar' >>> join_path('/foo', '/') '/foo' """ if name.startswith('./'): # Support relative paths (mainly for links) name = name[2:] if parentpath == '/' and name.startswith('/'): pstr = '%s' % name elif parentpath == '/' or name.startswith('/'): pstr = '%s%s' % (parentpath, name) else: pstr = '%s/%s' % (parentpath, name) if pstr.endswith('/'): pstr = pstr[:-1] return pstr joinPath = previous_api(join_path) def split_path(path): """Split a *canonical* `path` into a parent path and a node name. The result is returned as a tuple. The parent path does not include a trailing slash. >>> split_path('/') ('/', '') >>> split_path('/foo/bar') ('/foo', 'bar') """ lastslash = path.rfind('/') ppath = path[:lastslash] name = path[lastslash + 1:] if ppath == '': ppath = '/' return (ppath, name) splitPath = previous_api(split_path) def isvisiblename(name): """Does this `name` make the named node a visible one?""" return _hidden_name_re.match(name) is None isVisibleName = previous_api(isvisiblename) def isvisiblepath(path): """Does this `path` make the named node a visible one?""" return _hidden_path_re.search(path) is None isVisiblePath = previous_api(isvisiblepath) # Main part # ========= def _test(): """Run ``doctest`` on this module.""" import doctest doctest.testmod() if __name__ == '__main__': _test() PyTables-v.3.1.1/tables/registry.py000066400000000000000000000052351231437614300172100ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: March 18, 2005 # Author: Ivan Vilata - reverse:net.selidor@ivan # # $Source$ # $Id$ # ######################################################################## """Miscellaneous mappings used to avoid circular imports. Variables: `class_name_dict` Node class name to class object mapping. `class_id_dict` Class identifier to class object mapping. Misc variables: `__docformat__` The format of documentation strings in this module. """ from tables._past import previous_api # Important: no modules from PyTables should be imported here # (but standard modules are OK), since the main reason for this module # is avoiding circular imports! __docformat__ = 'reStructuredText' """The format of documentation strings in this module.""" class_name_dict = {} """Node class name to class object mapping. This dictionary maps class names (e.g. ``'Group'``) to actual class objects (e.g. `Group`). Classes are registered here when they are defined, and they are not expected to be unregistered (by now), but they can be replaced when the module that defines them is reloaded. .. versionchanged:: 3.0 The *classNameDict* dictionary has been renamed into *class_name_dict*. """ class_id_dict = {} """Class identifier to class object mapping. This dictionary maps class identifiers (e.g. ``'GROUP'``) to actual class objects (e.g. `Group`). Classes defining a new ``_c_classid`` attribute are registered here when they are defined, and they are not expected to be unregistered (by now), but they can be replaced when the module that defines them is reloaded. .. versionchanged:: 3.0 The *classIdDict* dictionary has been renamed into *class_id_dict*. """ # Deprecated API classNameDict = class_name_dict classIdDict = class_id_dict def get_class_by_name(classname): """Get the node class matching the `classname`. If the name is not registered, a ``TypeError`` is raised. The empty string and ``None`` are also accepted, and mean the ``Node`` class. .. versionadded:: 3.0 """ # The empty string is accepted for compatibility # with old default arguments. if classname is None or classname == '': classname = 'Node' # Get the class object corresponding to `classname`. if classname not in class_name_dict: raise TypeError("there is no registered node class named ``%s``" % (classname,)) return class_name_dict[classname] getClassByName = previous_api(get_class_by_name) ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/req_versions.py000066400000000000000000000015161231437614300200550ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: November 5, 2010 # Author: Francesc Alted - faltet@pytables.com # ######################################################################## """Required versions for PyTables dependencies.""" #*************************************************************** # Keep these in sync with setup.py and user's guide and README #*************************************************************** # Minimum recommended versions for mandatory packages min_numpy_version = '1.4.1' min_numexpr_version = '2.0.0' min_cython_version = '0.13' # The THG team has decided to fix an API inconsistency in the definition # of the H5Z_class_t structure in version 1.8.3 min_hdf5_version = (1, 8, 4) # necessary for allowing 1.8.10 > 1.8.5 PyTables-v.3.1.1/tables/scripts/000077500000000000000000000000001231437614300164505ustar00rootroot00000000000000PyTables-v.3.1.1/tables/scripts/__init__.py000066400000000000000000000006731231437614300205670ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: 2005-12-01 # Author: Ivan Vilata i Balaguer - ivan@selidor.net # # $Id$ # ######################################################################## """Utility scripts for PyTables. This package contains some modules which provide a ``main()`` function (with no arguments), so that they can be used as scripts. """ PyTables-v.3.1.1/tables/scripts/pt2to3.py000066400000000000000000000051471231437614300201640ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: April 9, 2013 # Author: Anthony Scopatz - scopatz@gmail.com # # $Id$ # ######################################################################## """This utility helps you migrate from PyTables 2.x APIs to 3.x APIs, which are PEP 8 compliant. """ import os import re import sys import argparse from tables._past import old2newnames, new2oldnames # Note that it is tempting to use the ast module here, but then this # breaks transforming cython files. So instead we are going to do the # dumb thing with replace. def make_subs(ns): names = new2oldnames if ns.reverse else old2newnames s = '(?<=\W)({0})(?=\W)'.format('|'.join(names.keys())) if ns.ignore_previous: s += '(?!\s*?=\s*?previous_api(_property)?\()' s += '(?!\* to \*\w+\*)' s += '(?!\* parameter has been renamed into \*\w+\*\.)' s += '(?! is pending deprecation, import \w+ instead\.)' subs = re.compile(s, flags=re.MULTILINE) def repl(m): return names.get(m.group(1), m.group(0)) return subs, repl def main(): desc = ('PyTables 2.x -> 3.x API transition tool\n\n' 'This tool displays to standard out, so it is \n' 'common to pipe this to another file:\n\n' '$ pt2to3 oldfile.py > newfile.py') parser = argparse.ArgumentParser(description=desc) parser.add_argument('-r', '--reverse', action='store_true', default=False, dest='reverse', help="reverts changes, going from 3.x -> 2.x.") parser.add_argument('-p', '--no-ignore-previous', action='store_false', default=True, dest='ignore_previous', help="ignores previous_api() calls.") parser.add_argument('-o', default=None, dest='output', help="output file to write to.") parser.add_argument('-i', '--inplace', action='store_true', default=False, dest='inplace', help="overwrites the file in-place.") parser.add_argument('filename', help='path to input file.') ns = parser.parse_args() if not os.path.isfile(ns.filename): sys.exit('file {0!r} not found'.format(ns.filename)) with open(ns.filename, 'r') as f: src = f.read() subs, repl = make_subs(ns) targ = subs.sub(repl, src) ns.output = ns.filename if ns.inplace else ns.output if ns.output is None: sys.stdout.write(targ) else: with open(ns.output, 'w') as f: f.write(targ) if __name__ == '__main__': main() PyTables-v.3.1.1/tables/scripts/ptdump.py000066400000000000000000000126101231437614300203330ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: February 10, 2004 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """This utility lets you look into the data and metadata of your data files. Pass the flag -h to this for help on usage. """ from __future__ import print_function import argparse from tables.file import open_file from tables.group import Group from tables.leaf import Leaf from tables.table import Table, Column from tables.unimplemented import UnImplemented from tables._past import previous_api # default options options = argparse.Namespace( rng=slice(None), showattrs=0, verbose=0, dump=0, colinfo=0, idxinfo=0, ) def dump_leaf(leaf): if options.verbose: print(repr(leaf)) else: print(str(leaf)) if options.showattrs: print(" "+repr(leaf.attrs)) if options.dump and not isinstance(leaf, UnImplemented): print(" Data dump:") # print((leaf.read(options.rng.start, options.rng.stop, # options.rng.step)) # This is better for large objects if options.rng.start is None: start = 0 else: start = options.rng.start if options.rng.stop is None: if leaf.shape != (): stop = leaf.shape[0] else: stop = options.rng.stop if options.rng.step is None: step = 1 else: step = options.rng.step if leaf.shape == (): print("[SCALAR] %s" % (leaf[()])) else: for i in range(start, stop, step): print("[%s] %s" % (i, leaf[i])) if isinstance(leaf, Table) and options.colinfo: # Show info of columns for colname in leaf.colnames: print(repr(leaf.cols._f_col(colname))) if isinstance(leaf, Table) and options.idxinfo: # Show info of indexes for colname in leaf.colnames: col = leaf.cols._f_col(colname) if isinstance(col, Column) and col.index is not None: idx = col.index print(repr(idx)) dumpLeaf = previous_api(dump_leaf) def dump_group(pgroup): node_kinds = pgroup._v_file._node_kinds[1:] for group in pgroup._f_walk_groups(): print(str(group)) if options.showattrs: print(" "+repr(group._v_attrs)) for kind in node_kinds: for node in group._f_list_nodes(kind): if options.verbose or options.dump: dump_leaf(node) else: print(str(node)) dumpGroup = previous_api(dump_group) def _get_parser(): parser = argparse.ArgumentParser( description='''The ptdump utility allows you look into the contents of your PyTables files. It lets you see not only the data but also the metadata (that is, the *structure* and additional information in the form of *attributes*).''') parser.add_argument( '-v', '--verbose', action='store_true', help='dump more metainformation on nodes', ) parser.add_argument( '-d', '--dump', action='store_true', help='dump data information on leaves', ) parser.add_argument( '-a', '--showattrs', action='store_true', help='show attributes in nodes (only useful when -v or -d are active)', ) parser.add_argument( '-c', '--colinfo', action='store_true', help='''show info of columns in tables (only useful when -v or -d are active)''', ) parser.add_argument( '-i', '--idxinfo', action='store_true', help='''show info of indexed columns (only useful when -v or -d are active)''', ) parser.add_argument( '-R', '--range', dest='rng', metavar='RANGE', help='''select a RANGE of rows (in the form "start,stop,step") during the copy of *all* the leaves. Default values are "None,None,1", which means a copy of all the rows.''', ) parser.add_argument('src', metavar='filename[:nodepath]', help='name of the HDF5 file to dump') return parser def main(): parser = _get_parser() args = parser.parse_args(namespace=options) # Get the options if isinstance(args.rng, basestring): try: options.rng = eval("slice(" + args.rng + ")") except Exception: parser.error("Error when getting the range parameter.") else: args.dump = 1 # Catch the files passed as the last arguments src = args.src.split(':') if len(src) == 1: filename, nodename = src[0], "/" else: filename, nodename = src if nodename == "": # case where filename == "filename:" instead of "filename:/" nodename = "/" # Check whether the specified node is a group or a leaf h5file = open_file(filename, 'r') nodeobject = h5file.get_node(nodename) if isinstance(nodeobject, Group): # Close the file again and reopen using the root_uep dump_group(nodeobject) elif isinstance(nodeobject, Leaf): # If it is not a Group, it must be a Leaf dump_leaf(nodeobject) else: # This should never happen print("Unrecognized object:", nodeobject) # Close the file h5file.close() PyTables-v.3.1.1/tables/scripts/ptrepack.py000066400000000000000000000524321231437614300206410ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: February 10, 2004 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """This utility lets you repack your data files in a flexible way. Pass the flag -h to this for help on usage. """ from __future__ import print_function import sys import time import os.path import argparse import warnings from tables.file import open_file from tables.group import Group from tables.leaf import Filters from tables.flavor import internal_flavor from tables.exceptions import OldIndexWarning, NoSuchNodeError, FlavorWarning from tables._past import previous_api # Global variables verbose = False regoldindexes = True createsysattrs = True numpy_aliases = [ 'numeric', 'Numeric', 'numarray', 'NumArray', 'CharArray', ] def newdst_group(dstfileh, dstgroup, title, filters): group = dstfileh.root # Now, create the new group. This works even if dstgroup == '/' for nodeName in dstgroup.split('/'): if nodeName == '': continue # First try if possible intermediate groups does already exist. try: group2 = dstfileh.get_node(group, nodeName) except NoSuchNodeError: # The group does not exist. Create it. group2 = dstfileh.create_group(group, nodeName, title=title, filters=filters) group = group2 return group newdstGroup = previous_api(newdst_group) def recreate_indexes(table, dstfileh, dsttable): listoldindexes = table._listoldindexes if listoldindexes != []: if not regoldindexes: if verbose: print("[I]Not regenerating indexes for table: '%s:%s'" % (dstfileh.filename, dsttable._v_pathname)) return # Now, recreate the indexed columns if verbose: print("[I]Regenerating indexes for table: '%s:%s'" % (dstfileh.filename, dsttable._v_pathname)) for colname in listoldindexes: if verbose: print("[I]Indexing column: '%s'. Please wait..." % colname) colobj = dsttable.cols._f_col(colname) # We don't specify the filters for the indexes colobj.create_index(filters=None) recreateIndexes = previous_api(recreate_indexes) def copy_leaf(srcfile, dstfile, srcnode, dstnode, title, filters, copyuserattrs, overwritefile, overwrtnodes, stats, start, stop, step, chunkshape, sortby, checkCSI, propindexes, upgradeflavors): # Open the source file srcfileh = open_file(srcfile, 'r') # Get the source node (that should exist) srcNode = srcfileh.get_node(srcnode) # Get the destination node and its parent last_slash = dstnode.rindex('/') if last_slash == len(dstnode)-1: # print("Detected a trailing slash in destination node. " # "Interpreting it as a destination group.") dstgroup = dstnode[:-1] elif last_slash > 0: dstgroup = dstnode[:last_slash] else: dstgroup = "/" dstleaf = dstnode[last_slash + 1:] if dstleaf == "": dstleaf = srcNode.name # Check whether the destination group exists or not if os.path.isfile(dstfile) and not overwritefile: dstfileh = open_file(dstfile, 'a', pytables_sys_attrs=createsysattrs) try: dstGroup = dstfileh.get_node(dstgroup) except: # The dstgroup does not seem to exist. Try creating it. dstGroup = newdst_group(dstfileh, dstgroup, title, filters) else: # The node exists, but it is really a group? if not isinstance(dstGroup, Group): # No. Should we overwrite it? if overwrtnodes: parent = dstGroup._v_parent last_slash = dstGroup._v_pathname.rindex('/') dstgroupname = dstGroup._v_pathname[last_slash + 1:] dstGroup.remove() dstGroup = dstfileh.create_group(parent, dstgroupname, title=title, filters=filters) else: raise RuntimeError("Please check that the node names are " "not duplicated in destination, and " "if so, add the --overwrite-nodes " "flag if desired.") else: # The destination file does not exist or will be overwritten. dstfileh = open_file(dstfile, 'w', title=title, filters=filters, pytables_sys_attrs=createsysattrs) dstGroup = newdst_group(dstfileh, dstgroup, title="", filters=filters) # Finally, copy srcNode to dstNode try: dstNode = srcNode.copy( dstGroup, dstleaf, filters=filters, copyuserattrs=copyuserattrs, overwrite=overwrtnodes, stats=stats, start=start, stop=stop, step=step, chunkshape=chunkshape, sortby=sortby, checkCSI=checkCSI, propindexes=propindexes) except: (type, value, traceback) = sys.exc_info() print("Problems doing the copy from '%s:%s' to '%s:%s'" % (srcfile, srcnode, dstfile, dstnode)) print("The error was --> %s: %s" % (type, value)) print("The destination file looks like:\n", dstfileh) # Close all the open files: srcfileh.close() dstfileh.close() raise RuntimeError("Please check that the node names are not " "duplicated in destination, and if so, add " "the --overwrite-nodes flag if desired.") # Upgrade flavors in dstNode, if required if upgradeflavors: if srcfileh.format_version.startswith("1"): # Remove original flavor in case the source file has 1.x format dstNode.del_attr('FLAVOR') elif srcfileh.format_version < "2.1": if dstNode.get_attr('FLAVOR') in numpy_aliases: dstNode.set_attr('FLAVOR', internal_flavor) # Recreate possible old indexes in destination node if srcNode._c_classid == "TABLE": recreate_indexes(srcNode, dstfileh, dstNode) # Close all the open files: srcfileh.close() dstfileh.close() copyLeaf = previous_api(copy_leaf) def copy_children(srcfile, dstfile, srcgroup, dstgroup, title, recursive, filters, copyuserattrs, overwritefile, overwrtnodes, stats, start, stop, step, chunkshape, sortby, checkCSI, propindexes, upgradeflavors): "Copy the children from source group to destination group" # Open the source file with srcgroup as root_uep srcfileh = open_file(srcfile, 'r', root_uep=srcgroup) # Assign the root to srcGroup srcGroup = srcfileh.root created_dstGroup = False # Check whether the destination group exists or not if os.path.isfile(dstfile) and not overwritefile: dstfileh = open_file(dstfile, 'a', pytables_sys_attrs=createsysattrs) try: dstGroup = dstfileh.get_node(dstgroup) except: # The dstgroup does not seem to exist. Try creating it. dstGroup = newdst_group(dstfileh, dstgroup, title, filters) created_dstGroup = True else: # The node exists, but it is really a group? if not isinstance(dstGroup, Group): # No. Should we overwrite it? if overwrtnodes: parent = dstGroup._v_parent last_slash = dstGroup._v_pathname.rindex('/') dstgroupname = dstGroup._v_pathname[last_slash + 1:] dstGroup.remove() dstGroup = dstfileh.create_group(parent, dstgroupname, title=title, filters=filters) else: raise RuntimeError("Please check that the node names are " "not duplicated in destination, and " "if so, add the --overwrite-nodes " "flag if desired.") else: # The destination file does not exist or will be overwritten. dstfileh = open_file(dstfile, 'w', title=title, filters=filters, pytables_sys_attrs=createsysattrs) dstGroup = newdst_group(dstfileh, dstgroup, title="", filters=filters) created_dstGroup = True # Copy the attributes to dstGroup, if needed if created_dstGroup and copyuserattrs: srcGroup._v_attrs._f_copy(dstGroup) # Finally, copy srcGroup children to dstGroup try: srcGroup._f_copy_children( dstGroup, recursive=recursive, filters=filters, copyuserattrs=copyuserattrs, overwrite=overwrtnodes, stats=stats, start=start, stop=stop, step=step, chunkshape=chunkshape, sortby=sortby, checkCSI=checkCSI, propindexes=propindexes) except: (type, value, traceback) = sys.exc_info() print("Problems doing the copy from '%s:%s' to '%s:%s'" % (srcfile, srcgroup, dstfile, dstgroup)) print("The error was --> %s: %s" % (type, value)) print("The destination file looks like:\n", dstfileh) # Close all the open files: srcfileh.close() dstfileh.close() raise RuntimeError("Please check that the node names are not " "duplicated in destination, and if so, add the " "--overwrite-nodes flag if desired. In " "particular, pay attention that root_uep is not " "fooling you.") # Upgrade flavors in dstNode, if required if upgradeflavors: for dstNode in dstGroup._f_walknodes("Leaf"): if srcfileh.format_version.startswith("1"): # Remove original flavor in case the source file has 1.x format dstNode.del_attr('FLAVOR') elif srcfileh.format_version < "2.1": if dstNode.get_attr('FLAVOR') in numpy_aliases: dstNode.set_attr('FLAVOR', internal_flavor) # Convert the remaining tables with old indexes (if any) for table in srcGroup._f_walknodes("Table"): dsttable = dstfileh.get_node(dstGroup, table._v_pathname) recreate_indexes(table, dstfileh, dsttable) # Close all the open files: srcfileh.close() dstfileh.close() copyChildren = previous_api(copy_children) def _get_parser(): parser = argparse.ArgumentParser( description='''This utility is very powerful and lets you copy any leaf, group or complete subtree into another file. During the copy process you are allowed to change the filter properties if you want so. Also, in the case of duplicated pathnames, you can decide if you want to overwrite already existing nodes on the destination file. Generally speaking, ptrepack can be useful in may situations, like replicating a subtree in another file, change the filters in objects and see how affect this to the compression degree or I/O performance, consolidating specific data in repositories or even *importing* generic HDF5 files and create true PyTables counterparts.''') parser.add_argument( '-v', '--verbose', action='store_true', help='show verbose information', ) parser.add_argument( '-o', '--overwrite', action='store_true', dest='overwritefile', help='overwrite destination file', ) parser.add_argument( '-R', '--range', dest='rng', metavar='RANGE', help='''select a RANGE of rows (in the form "start,stop,step") during the copy of *all* the leaves. Default values are "None,None,1", which means a copy of all the rows.''', ) parser.add_argument( '--non-recursive', action='store_false', default=True, dest='recursive', help='do not do a recursive copy. Default is to do it', ) parser.add_argument( '--dest-title', dest='title', default='', help='title for the new file (if not specified, the source is copied)', ) parser.add_argument( '--dont-create-sysattrs', action='store_false', default=True, dest='createsysattrs', help='do not create sys attrs (default is to do it)', ) parser.add_argument( '--dont-copy-userattrs', action='store_false', default=True, dest='copyuserattrs', help='do not copy the user attrs (default is to do it)', ) parser.add_argument( '--overwrite-nodes', action='store_true', dest='overwrtnodes', help='''overwrite destination nodes if they exist. Default is to not overwrite them''', ) parser.add_argument( '--complevel', type=int, default=0, help='''set a compression level (0 for no compression, which is the default)''', ) parser.add_argument( '--complib', choices=( "zlib", "lzo", "bzip2", "blosc", "blosc:blosclz", "blosc:lz4", "blosc:lz4hc", "blosc:snappy", "blosc:zlib"), default='zlib', help='''set the compression library to be used during the copy. Defaults to %(default)s''', ) parser.add_argument( '--shuffle', type=int, choices=(0, 1), help='''activate or not the shuffling filter (default is active if complevel > 0)''', ) parser.add_argument( '--fletcher32', type=int, choices=(0, 1), help='''whether to activate or not the fletcher32 filter (not active by default)''', ) parser.add_argument( '--keep-source-filters', action='store_true', dest='keepfilters', help='''use the original filters in source files. The default is not doing that if any of --complevel, --complib, --shuffle or --fletcher32 option is specified''', ) parser.add_argument( '--chunkshape', default='keep', help='''set a chunkshape. Possible options are: "keep" | "auto" | int | tuple. A value of "auto" computes a sensible value for the chunkshape of the leaves copied. The default is to "keep" the original value''', ) parser.add_argument( '--upgrade-flavors', action='store_true', dest='upgradeflavors', help='''when repacking PyTables 1.x or PyTables 2.x files, the flavor of leaves will be unset. With this, such a leaves will be serialized as objects with the internal flavor ('numpy' for 3.x series)''', ) parser.add_argument( '--dont-regenerate-old-indexes', action='store_false', default=True, dest='regoldindexes', help='''disable regenerating old indexes. The default is to regenerate old indexes as they are found''', ) parser.add_argument( '--sortby', metavar='COLUMN', help='''do a table copy sorted by the index in "column". For reversing the order, use a negative value in the "step" part of "RANGE" (see "-r" flag). Only applies to table objects''', ) parser.add_argument( '--checkCSI', action='store_true', help='Force the check for a CSI index for the --sortby column', ) parser.add_argument( '--propindexes', action='store_true', help='''propagate the indexes existing in original tables. The default is to not propagate them. Only applies to table objects''', ) parser.add_argument( 'src', metavar='sourcefile:sourcegroup', help='source file/group', ) parser.add_argument( 'dst', metavar='destfile:destgroup', help='destination file/group', ) return parser def main(): global verbose global regoldindexes global createsysattrs parser = _get_parser() args = parser.parse_args() # check arguments if args.rng: try: args.rng = eval("slice(" + args.rng + ")") except Exception: parser.error("Error when getting the range parameter.") if args.chunkshape.isdigit() or args.chunkshape.startswith('('): args.chunkshape = eval(args.chunkshape) if args.complevel < 0 or args.complevel > 9: parser.error( 'invalid "complevel" value, it sould be in te range [0, 9]' ) # Catch the files passed as the last arguments src = args.src.split(':') dst = args.dst.split(':') if len(src) == 1: srcfile, srcnode = src[0], "/" else: srcfile, srcnode = src if len(dst) == 1: dstfile, dstnode = dst[0], "/" else: dstfile, dstnode = dst if srcnode == "": # case where filename == "filename:" instead of "filename:/" srcnode = "/" if dstnode == "": # case where filename == "filename:" instead of "filename:/" dstnode = "/" # Ignore the warnings for tables that contains oldindexes # (these will be handled by the copying routines) warnings.filterwarnings("ignore", category=OldIndexWarning) # Ignore the flavors warnings during upgrading flavor operations if args.upgradeflavors: warnings.filterwarnings("ignore", category=FlavorWarning) # Build the Filters instance filter_params = ( args.complevel, args.complib, args.shuffle, args.fletcher32, ) if (filter_params == (None,) * 4 or args.keepfilters): filters = None else: if args.complevel is None: args.complevel = 0 if args.shuffle is None: if args.complevel > 0: args.shuffle = True else: args.shuffle = False if args.complib is None: args.complib = "zlib" if args.fletcher32 is None: args.fletcher32 = False filters = Filters(complevel=args.complevel, complib=args.complib, shuffle=args.shuffle, fletcher32=args.fletcher32) # The start, stop and step params: start, stop, step = None, None, 1 # Defaults if args.rng: start, stop, step = args.rng.start, args.rng.stop, args.rng.step # Set globals verbose = args.verbose regoldindexes = args.regoldindexes createsysattrs = args.createsysattrs # Some timing t1 = time.time() cpu1 = time.clock() # Copy the file if verbose: print("+=+" * 20) print("Recursive copy:", args.recursive) print("Applying filters:", filters) if args.sortby is not None: print("Sorting table(s) by column:", args.sortby) print("Forcing a CSI creation:", args.checkCSI) if args.propindexes: print("Recreating indexes in copied table(s)") print("Start copying %s:%s to %s:%s" % (srcfile, srcnode, dstfile, dstnode)) print("+=+" * 20) # Check whether the specified source node is a group or a leaf h5srcfile = open_file(srcfile, 'r') srcnodeobject = h5srcfile.get_node(srcnode) # Close the file again h5srcfile.close() stats = {'groups': 0, 'leaves': 0, 'links': 0, 'bytes': 0} if isinstance(srcnodeobject, Group): copy_children( srcfile, dstfile, srcnode, dstnode, title=args.title, recursive=args.recursive, filters=filters, copyuserattrs=args.copyuserattrs, overwritefile=args.overwritefile, overwrtnodes=args.overwrtnodes, stats=stats, start=start, stop=stop, step=step, chunkshape=args.chunkshape, sortby=args.sortby, checkCSI=args.checkCSI, propindexes=args.propindexes, upgradeflavors=args.upgradeflavors) else: # If not a Group, it should be a Leaf copy_leaf( srcfile, dstfile, srcnode, dstnode, title=args.title, filters=filters, copyuserattrs=args.copyuserattrs, overwritefile=args.overwritefile, overwrtnodes=args.overwrtnodes, stats=stats, start=start, stop=stop, step=step, chunkshape=args.chunkshape, sortby=args.sortby, checkCSI=args.checkCSI, propindexes=args.propindexes, upgradeflavors=args.upgradeflavors) # Gather some statistics t2 = time.time() cpu2 = time.clock() tcopy = round(t2 - t1, 3) cpucopy = round(cpu2 - cpu1, 3) tpercent = int(round(cpucopy / tcopy, 2) * 100) if verbose: ngroups = stats['groups'] nleaves = stats['leaves'] nlinks = stats['links'] nbytescopied = stats['bytes'] nnodes = ngroups + nleaves + nlinks print(( "Groups copied:", ngroups, " Leaves copied:", nleaves, " Links copied:", nlinks, )) if args.copyuserattrs: print("User attrs copied") else: print("User attrs not copied") print("KBytes copied:", round(nbytescopied / 1024., 3)) print("Time copying: %s s (real) %s s (cpu) %s%%" % ( tcopy, cpucopy, tpercent)) print("Copied nodes/sec: ", round((nnodes) / float(tcopy), 1)) print("Copied KB/s :", int(nbytescopied / (tcopy * 1024))) PyTables-v.3.1.1/tables/table.py000066400000000000000000004403441231437614300164330ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: September 4, 2002 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Here is defined the Table class.""" import sys import math import warnings import os.path from time import time from functools import reduce as _reduce import numpy import numexpr from tables import tableextension from tables.lrucacheextension import ObjectCache, NumCache from tables.atom import Atom from tables.conditions import compile_condition from numexpr.necompiler import ( getType as numexpr_getType, double, is_cpu_amd_intel) from numexpr.expressions import functions as numexpr_functions from tables.flavor import flavor_of, array_as_internal, internal_to_flavor from tables.utils import is_idx, lazyattr, SizeType, NailedDict as CacheDict from tables.leaf import Leaf from tables.description import ( IsDescription, Description, Col, descr_from_dtype) from tables.exceptions import (NodeError, HDF5ExtError, PerformanceWarning, OldIndexWarning, NoSuchNodeError) from tables.utilsextension import get_nested_field from tables.path import join_path, split_path from tables.index import ( OldIndex, default_index_filters, default_auto_index, Index, IndexesDescG, IndexesTableG) profile = False # profile = True # Uncomment for profiling if profile: from tables.utils import show_stats from tables._past import previous_api, previous_api_property # 2.2: Added support for complex types. Introduced in version 0.9. # 2.2.1: Added suport for time types. # 2.3: Changed the indexes naming schema. # 2.4: Changed indexes naming schema (again). # 2.5: Added the FIELD_%d_FILL attributes. # 2.6: Added the FLAVOR attribute (optional). # 2.7: Numeric and numarray flavors are gone. obversion = "2.7" # The Table VERSION number try: # int_, long_ are only available in numexpr >= 2.1 from numexpr.necompiler import int_, long_ except ImportError: int_ = int long_ = long # Maps NumPy types to the types used by Numexpr. _nxtype_from_nptype = { numpy.bool_: bool, numpy.int8: int_, numpy.int16: int_, numpy.int32: int_, numpy.int64: long_, numpy.uint8: int_, numpy.uint16: int_, numpy.uint32: long_, numpy.uint64: long_, numpy.float32: float, numpy.float64: double, numpy.complex64: complex, numpy.complex128: complex, numpy.bytes_: bytes, } if sys.version_info[0] > 2: _nxtype_from_nptype[numpy.str_] = str if hasattr(numpy, 'float16'): _nxtype_from_nptype[numpy.float16] = float # XXX: check if hasattr(numpy, 'float96'): _nxtype_from_nptype[numpy.float96] = double # XXX: check if hasattr(numpy, 'float128'): _nxtype_from_nptype[numpy.float128] = double # XXX: check if hasattr(numpy, 'complec192'): _nxtype_from_nptype[numpy.complex192] = complex # XXX: check if hasattr(numpy, 'complex256'): _nxtype_from_nptype[numpy.complex256] = complex # XXX: check # The NumPy scalar type corresponding to `SizeType`. _npsizetype = numpy.array(SizeType(0)).dtype.type def _index_name_of(node): return '_i_%s' % node._v_name _indexNameOf = previous_api(_index_name_of) def _index_pathname_of(node): nodeParentPath = split_path(node._v_pathname)[0] return join_path(nodeParentPath, _index_name_of(node)) _indexPathnameOf = previous_api(_index_pathname_of) def _index_pathname_of_column(table, colpathname): return join_path(_index_pathname_of(table), colpathname) _indexPathnameOfColumn = previous_api(_index_pathname_of_column) # The next are versions that work with just paths (i.e. we don't need # a node instance for using them, which can be critical in certain # situations) def _index_name_of_(nodeName): return '_i_%s' % nodeName _indexNameOf_ = previous_api(_index_name_of_) def _index_pathname_of_(nodePath): nodeParentPath, nodeName = split_path(nodePath) return join_path(nodeParentPath, _index_name_of_(nodeName)) _indexPathnameOf_ = previous_api(_index_pathname_of_) def _index_pathname_of_column_(tablePath, colpathname): return join_path(_index_pathname_of_(tablePath), colpathname) _indexPathnameOfColumn_ = previous_api(_index_pathname_of_column_) def _table__setautoindex(self, auto): auto = bool(auto) try: indexgroup = self._v_file._get_node(_index_pathname_of(self)) except NoSuchNodeError: indexgroup = create_indexes_table(self) indexgroup.auto = auto # Update the cache in table instance as well self._autoindex = auto _table__setautoIndex = previous_api(_table__setautoindex) # **************** WARNING! *********************** # This function can be called during the destruction time of a table # so measures have been taken so that it doesn't have to revive # another node (which can fool the LRU cache). The solution devised # has been to add a cache for autoindex (Table._autoindex), populate # it in creation time of the cache (which is a safe period) and then # update the cache whenever it changes. # This solves the error when running test_indexes.py ManyNodesTestCase. # F. Alted 2007-04-20 # ************************************************** def _table__getautoindex(self): if self._autoindex is None: try: indexgroup = self._v_file._get_node(_index_pathname_of(self)) except NoSuchNodeError: self._autoindex = default_auto_index # update cache return self._autoindex else: self._autoindex = indexgroup.auto # update cache return self._autoindex else: # The value is in cache, return it return self._autoindex _table__getautoIndex = previous_api(_table__getautoindex) _table__autoindex = property( _table__getautoindex, _table__setautoindex, None, """Automatically keep column indexes up to date? Setting this value states whether existing indexes should be automatically updated after an append operation or recomputed after an index-invalidating operation (i.e. removal and modification of rows). The default is true. This value gets into effect whenever a column is altered. If you don't have automatic indexing activated and you want to do an an immediate update use `Table.flush_rows_to_index()`; for an immediate reindexing of invalidated indexes, use `Table.reindex_dirty()`. This value is persistent. """) _table__autoIndex = previous_api(_table__autoindex) def restorecache(self): # Define a cache for sparse table reads params = self._v_file.params chunksize = self._v_chunkshape[0] nslots = params['TABLE_MAX_SIZE'] / (chunksize * self._v_dtype.itemsize) self._chunkcache = NumCache((nslots, chunksize), self._v_dtype, 'table chunk cache') self._seqcache = ObjectCache(params['ITERSEQ_MAX_SLOTS'], params['ITERSEQ_MAX_SIZE'], 'Iter sequence cache') self._dirtycache = False def _table__where_indexed(self, compiled, condition, condvars, start, stop, step): if profile: tref = time() if profile: show_stats("Entering table_whereIndexed", tref) self._use_index = True # Clean the table caches for indexed queries if needed if self._dirtycache: restorecache(self) # Get the values in expression that are not columns values = [] for key, value in condvars.iteritems(): if isinstance(value, numpy.ndarray): values.append((key, value.item())) # Build a key for the sequence cache seqkey = (condition, tuple(values), (start, stop, step)) # Do a lookup in sequential cache for this query nslot = self._seqcache.getslot(seqkey) if nslot >= 0: # Get the row sequence from the cache seq = self._seqcache.getitem(nslot) if len(seq) == 0: return None seq = numpy.array(seq, dtype='int64') # Correct the ranges in cached sequence if (start, stop, step) != (0, self.nrows, 1): seq = seq[(seq >= start) & ( seq < stop) & ((seq - start) % step == 0)] return self.itersequence(seq) else: # No luck. Set row sequence to empty. It will be populated # in the iterator. If not possible, the slot entry will be # removed there. self._nslotseq = self._seqcache.setitem(seqkey, [], 1) # Compute the chunkmap for every index in indexed expression idxexprs = compiled.index_expressions strexpr = compiled.string_expression cmvars = {} tcoords = 0 for i, idxexpr in enumerate(idxexprs): var, ops, lims = idxexpr col = condvars[var] index = col.index assert index is not None, "the chosen column is not indexed" assert not index.dirty, "the chosen column has a dirty index" # Get the number of rows that the indexed condition yields. range_ = index.get_lookup_range(ops, lims) ncoords = index.search(range_) tcoords += ncoords if index.reduction == 1 and ncoords == 0: # No values from index condition, thus the chunkmap should be empty nrowsinchunk = self.chunkshape[0] nchunks = long(math.ceil(float(self.nrows) / nrowsinchunk)) chunkmap = numpy.zeros(shape=nchunks, dtype="bool") else: # Get the chunkmap from the index chunkmap = index.get_chunkmap() # Assign the chunkmap to the cmvars dictionary cmvars["e%d" % i] = chunkmap if index.reduction == 1 and tcoords == 0: # No candidates found in any indexed expression component, so leave now return None # Compute the final chunkmap chunkmap = numexpr.evaluate(strexpr, cmvars) # Method .any() is twice as faster than method .sum() if not chunkmap.any(): # The chunkmap is empty return None if profile: show_stats("Exiting table_whereIndexed", tref) return chunkmap _table__whereIndexed = previous_api(_table__where_indexed) def create_indexes_table(table): itgroup = IndexesTableG( table._v_parent, _index_name_of(table), "Indexes container for table " + table._v_pathname, new=True) return itgroup createIndexesTable = previous_api(create_indexes_table) def create_indexes_descr(igroup, dname, iname, filters): idgroup = IndexesDescG( igroup, iname, "Indexes container for sub-description " + dname, filters=filters, new=True) return idgroup createIndexesDescr = previous_api(create_indexes_descr) def _column__create_index(self, optlevel, kind, filters, tmp_dir, blocksizes, verbose): name = self.name table = self.table dtype = self.dtype descr = self.descr index = self.index get_node = table._v_file._get_node # Warn if the index already exists if index: raise ValueError("%s for column '%s' already exists. If you want to " "re-create it, please, try with reindex() method " "better" % (str(index), str(self.pathname))) # Check that the datatype is indexable. if dtype.str[1:] == 'u8': raise NotImplementedError( "indexing 64-bit unsigned integer columns " "is not supported yet, sorry") if dtype.kind == 'c': raise TypeError("complex columns can not be indexed") if dtype.shape != (): raise TypeError("multidimensional columns can not be indexed") # Get the indexes group for table, and if not exists, create it try: itgroup = get_node(_index_pathname_of(table)) except NoSuchNodeError: itgroup = create_indexes_table(table) # Create the necessary intermediate groups for descriptors idgroup = itgroup dname = "" pathname = descr._v_pathname if pathname != '': inames = pathname.split('/') for iname in inames: if dname == '': dname = iname else: dname += '/' + iname try: idgroup = get_node('%s/%s' % (itgroup._v_pathname, dname)) except NoSuchNodeError: idgroup = create_indexes_descr(idgroup, dname, iname, filters) # Create the atom assert dtype.shape == () atom = Atom.from_dtype(numpy.dtype((dtype, (0,)))) # Protection on tables larger than the expected rows (perhaps the # user forgot to pass this parameter to the Table constructor?) expectedrows = table._v_expectedrows if table.nrows > expectedrows: expectedrows = table.nrows # Create the index itself index = Index( idgroup, name, atom=atom, title="Index for %s column" % name, kind=kind, optlevel=optlevel, filters=filters, tmp_dir=tmp_dir, expectedrows=expectedrows, byteorder=table.byteorder, blocksizes=blocksizes) table._set_column_indexing(self.pathname, True) # Feed the index with values # Add rows to the index if necessary if table.nrows > 0: indexedrows = table._add_rows_to_index( self.pathname, 0, table.nrows, lastrow=True, update=False) else: indexedrows = 0 index.dirty = False table._indexedrows = indexedrows table._unsaved_indexedrows = table.nrows - indexedrows # Optimize the index that has been already filled-up index.optimize(verbose=verbose) # We cannot do a flush here because when reindexing during a # flush, the indexes are created anew, and that creates a nested # call to flush(). # table.flush() return indexedrows _column__createIndex = previous_api(_column__create_index) class _ColIndexes(dict): """Provides a nice representation of column indexes.""" def __repr__(self): """Gives a detailed Description column representation.""" rep = [' \"%s\": %s' % (k, self[k]) for k in self.iterkeys()] return '{\n %s}' % (',\n '.join(rep)) class Table(tableextension.Table, Leaf): """This class represents heterogeneous datasets in an HDF5 file. Tables are leaves (see the Leaf class in :ref:`LeafClassDescr`) whose data consists of a unidimensional sequence of *rows*, where each row contains one or more *fields*. Fields have an associated unique *name* and *position*, with the first field having position 0. All rows have the same fields, which are arranged in *columns*. Fields can have any type supported by the Col class (see :ref:`ColClassDescr`) and its descendants, which support multidimensional data. Moreover, a field can be *nested* (to an arbitrary depth), meaning that it includes further fields inside. A field named x inside a nested field a in a table can be accessed as the field a/x (its *path name*) from the table. The structure of a table is declared by its description, which is made available in the Table.description attribute (see :class:`Table`). This class provides new methods to read, write and search table data efficiently. It also provides special Python methods to allow accessing the table as a normal sequence or array (with extended slicing supported). PyTables supports *in-kernel* searches working simultaneously on several columns using complex conditions. These are faster than selections using Python expressions. See the :meth:`Table.where` method for more information on in-kernel searches. Non-nested columns can be *indexed*. Searching an indexed column can be several times faster than searching a non-nested one. Search methods automatically take advantage of indexing where available. When iterating a table, an object from the Row (see :ref:`RowClassDescr`) class is used. This object allows to read and write data one row at a time, as well as to perform queries which are not supported by in-kernel syntax (at a much lower speed, of course). Objects of this class support access to individual columns via *natural naming* through the :attr:`Table.cols` accessor. Nested columns are mapped to Cols instances, and non-nested ones to Column instances. See the Column class in :ref:`ColumnClassDescr` for examples of this feature. Parameters ---------- parentnode The parent :class:`Group` object. .. versionchanged:: 3.0 Renamed from *parentNode* to *parentnode*. name : str The name of this node in its parent group. description An IsDescription subclass or a dictionary where the keys are the field names, and the values the type definitions. In addition, a pure NumPy dtype is accepted. If None, the table metadata is read from disk, else, it's taken from previous parameters. title Sets a TITLE attribute on the HDF5 table entity. filters : Filters An instance of the Filters class that provides information about the desired I/O filters to be applied during the life of this object. expectedrows A user estimate about the number of rows that will be on table. If not provided, the default value is ``EXPECTED_ROWS_TABLE`` (see ``tables/parameters.py``). If you plan to save bigger tables, try providing a guess; this will optimize the HDF5 B-Tree creation and management process time and memory used. chunkshape The shape of the data chunk to be read or written as a single HDF5 I/O operation. The filters are applied to those chunks of data. Its rank for tables has to be 1. If ``None``, a sensible value is calculated based on the `expectedrows` parameter (which is recommended). byteorder The byteorder of the data *on-disk*, specified as 'little' or 'big'. If this is not specified, the byteorder is that of the platform, unless you passed a recarray as the `description`, in which case the recarray byteorder will be chosen. Notes ----- The instance variables below are provided in addition to those in Leaf (see :ref:`LeafClassDescr`). Please note that there are several col* dictionaries to ease retrieving information about a column directly by its path name, avoiding the need to walk through Table.description or Table.cols. .. rubric:: Table attributes .. attribute:: coldescrs Maps the name of a column to its Col description (see :ref:`ColClassDescr`). .. attribute:: coldflts Maps the name of a column to its default value. .. attribute:: coldtypes Maps the name of a column to its NumPy data type. .. attribute:: colindexed Is the column which name is used as a key indexed? .. attribute:: colinstances Maps the name of a column to its Column (see :ref:`ColumnClassDescr`) or Cols (see :ref:`ColsClassDescr`) instance. .. attribute:: colnames A list containing the names of *top-level* columns in the table. .. attribute:: colpathnames A list containing the pathnames of *bottom-level* columns in the table. These are the leaf columns obtained when walking the table description left-to-right, bottom-first. Columns inside a nested column have slashes (/) separating name components in their pathname. .. attribute:: cols A Cols instance that provides *natural naming* access to non-nested (Column, see :ref:`ColumnClassDescr`) and nested (Cols, see :ref:`ColsClassDescr`) columns. .. attribute:: coltypes Maps the name of a column to its PyTables data type. .. attribute:: description A Description instance (see :ref:`DescriptionClassDescr`) reflecting the structure of the table. .. attribute:: extdim The index of the enlargeable dimension (always 0 for tables). .. attribute:: indexed Does this table have any indexed columns? .. attribute:: nrows The current number of rows in the table. """ # Class identifier. _c_classid = 'TABLE' _c_classId = previous_api_property('_c_classid') _v_objectId = previous_api_property('_v_objectid') # Properties # ~~~~~~~~~~ @lazyattr def row(self): """The associated Row instance (see :ref:`RowClassDescr`).""" return tableextension.Row(self) @lazyattr def dtype(self): """The NumPy ``dtype`` that most closely matches this table.""" return self.description._v_dtype # Read-only shorthands # ```````````````````` shape = property( lambda self: (self.nrows,), None, None, "The shape of this table.") rowsize = property( lambda self: self.description._v_dtype.itemsize, None, None, "The size in bytes of each row in the table.") size_in_memory = property( lambda self: self.nrows * self.rowsize, None, None, """The size of this table's data in bytes when it is fully loaded into memory. This may be used in combination with size_on_disk to calculate the compression ratio of the data.""") # Lazy attributes # ``````````````` @lazyattr def _v_iobuf(self): """A buffer for doing I/O.""" return self._get_container(self.nrowsinbuf) @lazyattr def _v_wdflts(self): """The defaults for writing in recarray format.""" # First, do a check to see whether we need to set default values # different from 0 or not. for coldflt in self.coldflts.itervalues(): if isinstance(coldflt, numpy.ndarray) or coldflt: break else: # No default different from 0 found. Returning None. return None wdflts = self._get_container(1) for colname, coldflt in self.coldflts.iteritems(): ra = get_nested_field(wdflts, colname) ra[:] = coldflt return wdflts @lazyattr def _colunaligned(self): """The pathnames of unaligned, *unidimensional* columns.""" colunaligned, rarr = [], self._get_container(0) for colpathname in self.colpathnames: carr = get_nested_field(rarr, colpathname) if not carr.flags.aligned and carr.ndim == 1: colunaligned.append(colpathname) return frozenset(colunaligned) # Index-related properties # ```````````````````````` autoindex = _table__autoindex """Automatically keep column indexes up to date? Setting this value states whether existing indexes should be automatically updated after an append operation or recomputed after an index-invalidating operation (i.e. removal and modification of rows). The default is true. This value gets into effect whenever a column is altered. If you don't have automatic indexing activated and you want to do an immediate update use :meth:`Table.flush_rows_to_index`; for immediate reindexing of invalidated indexes, use :meth:`Table.reindex_dirty`. This value is persistent. .. versionchanged:: 3.0 The *autoIndex* property has been renamed into *autoindex*. """ autoIndex = previous_api_property('autoindex') indexedcolpathnames = property( lambda self: [_colpname for _colpname in self.colpathnames if self.colindexed[_colpname]], None, None, """List of pathnames of indexed columns in the table.""") colindexes = property( lambda self: _ColIndexes( ((_colpname, self.cols._f_col(_colpname).index) for _colpname in self.colpathnames if self.colindexed[_colpname])), None, None, """A dictionary with the indexes of the indexed columns.""") _dirtyindexes = property( lambda self: self._condition_cache._nailcount > 0, None, None, """Whether some index in table is dirty.""") # Other methods # ~~~~~~~~~~~~~ def __init__(self, parentnode, name, description=None, title="", filters=None, expectedrows=None, chunkshape=None, byteorder=None, _log=True): self._v_new = new = description is not None """Is this the first time the node has been created?""" self._v_new_title = title """New title for this node.""" self._v_new_filters = filters """New filter properties for this node.""" self.extdim = 0 # Tables only have one dimension currently """The index of the enlargeable dimension (always 0 for tables).""" self._v_recarray = None """A structured array to be stored in the table.""" self._rabyteorder = None """The computed byteorder of the self._v_recarray.""" if expectedrows is None: expectedrows = parentnode._v_file.params['EXPECTED_ROWS_TABLE'] self._v_expectedrows = expectedrows """The expected number of rows to be stored in the table.""" self.nrows = SizeType(0) """The current number of rows in the table.""" self.description = None """A Description instance (see :ref:`DescriptionClassDescr`) reflecting the structure of the table.""" self._time64colnames = [] """The names of ``Time64`` columns.""" self._strcolnames = [] """The names of ``String`` columns.""" self._colenums = {} """Maps the name of an enumerated column to its ``Enum`` instance.""" self._v_chunkshape = None """Private storage for the `chunkshape` property of the leaf.""" self.indexed = False """Does this table have any indexed columns?""" self._indexedrows = 0 """Number of rows indexed in disk.""" self._unsaved_indexedrows = 0 """Number of rows indexed in memory but still not in disk.""" self._listoldindexes = [] """The list of columns with old indexes.""" self._autoindex = None """Private variable that caches the value for autoindex.""" self.colnames = [] """A list containing the names of *top-level* columns in the table.""" self.colpathnames = [] """A list containing the pathnames of *bottom-level* columns in the table. These are the leaf columns obtained when walking the table description left-to-right, bottom-first. Columns inside a nested column have slashes (/) separating name components in their pathname. """ self.colinstances = {} """Maps the name of a column to its Column (see :ref:`ColumnClassDescr`) or Cols (see :ref:`ColsClassDescr`) instance.""" self.coldescrs = {} """Maps the name of a column to its Col description (see :ref:`ColClassDescr`).""" self.coltypes = {} """Maps the name of a column to its PyTables data type.""" self.coldtypes = {} """Maps the name of a column to its NumPy data type.""" self.coldflts = {} """Maps the name of a column to its default value.""" self.colindexed = {} """Is the column which name is used as a key indexed?""" self._use_index = False """Whether an index can be used or not in a search. Boolean.""" self._where_condition = None """Condition function and argument list for selection of values.""" max_slots = parentnode._v_file.params['COND_CACHE_SLOTS'] self._condition_cache = CacheDict(max_slots) """Cache of already compiled conditions.""" self._exprvars_cache = {} """Cache of variables participating in numexpr expressions.""" self._enabled_indexing_in_queries = True """Is indexing enabled in queries? *Use only for testing.*""" self._empty_array_cache = {} """Cache of empty arrays.""" self._v_dtype = None """The NumPy datatype fopr this table.""" self.cols = None """ A Cols instance that provides *natural naming* access to non-nested (Column, see :ref:`ColumnClassDescr`) and nested (Cols, see :ref:`ColsClassDescr`) columns. """ self._dirtycache = True """Whether the data caches are dirty or not. Initially set to yes.""" self._descflavor = None """Temporarily keeps the flavor of a description with data.""" # Initialize this object in case is a new Table # Try purely descriptive description objects. if new and isinstance(description, dict): # Dictionary case self.description = Description(description) elif new and (type(description) == type(IsDescription) and issubclass(description, IsDescription)): # IsDescription subclass case descr = description() self.description = Description(descr.columns) elif new and isinstance(description, Description): # It is a Description instance already self.description = description # No description yet? if new and self.description is None: # Try NumPy dtype instances if isinstance(description, numpy.dtype): self.description, self._rabyteorder = \ descr_from_dtype(description) # No description yet? if new and self.description is None: # Try structured array description objects. try: self._descflavor = flavor = flavor_of(description) except TypeError: # probably not an array pass else: if flavor == 'python': nparray = numpy.rec.array(description) else: nparray = array_as_internal(description, flavor) self.nrows = nrows = SizeType(nparray.size) # If `self._v_recarray` is set, it will be used as the # initial buffer. if nrows > 0: self._v_recarray = nparray self.description, self._rabyteorder = \ descr_from_dtype(nparray.dtype) # No description yet? if new and self.description is None: raise TypeError( "the ``description`` argument is not of a supported type: " "``IsDescription`` subclass, ``Description`` instance, " "dictionary, or structured array") # Check the chunkshape parameter if new and chunkshape is not None: if isinstance(chunkshape, (int, numpy.integer, long)): chunkshape = (chunkshape,) try: chunkshape = tuple(chunkshape) except TypeError: raise TypeError( "`chunkshape` parameter must be an integer or sequence " "and you passed a %s" % type(chunkshape)) if len(chunkshape) != 1: raise ValueError("`chunkshape` rank (length) must be 1: %r" % (chunkshape,)) self._v_chunkshape = tuple(SizeType(s) for s in chunkshape) super(Table, self).__init__(parentnode, name, new, filters, byteorder, _log) def _g_post_init_hook(self): # We are putting here the index-related issues # as well as filling general info for table # This is needed because we need first the index objects created # First, get back the flavor of input data (if any) for # `Leaf._g_post_init_hook()`. self._flavor, self._descflavor = self._descflavor, None super(Table, self)._g_post_init_hook() # Create a cols accessor. self.cols = Cols(self, self.description) # Place the `Cols` and `Column` objects into `self.colinstances`. colinstances, cols = self.colinstances, self.cols for colpathname in self.description._v_pathnames: colinstances[colpathname] = cols._g_col(colpathname) if self._v_new: # Columns are never indexed on creation. self.colindexed = dict((cpn, False) for cpn in self.colpathnames) return # The following code is only for opened tables. # Do the indexes group exist? indexesgrouppath = _index_pathname_of(self) igroup = indexesgrouppath in self._v_file oldindexes = False for colobj in self.description._f_walk(type="Col"): colname = colobj._v_pathname # Is this column indexed? if igroup: indexname = _index_pathname_of_column(self, colname) indexed = indexname in self._v_file self.colindexed[colname] = indexed if indexed: column = self.cols._g_col(colname) indexobj = column.index if isinstance(indexobj, OldIndex): indexed = False # Not a vaild index oldindexes = True self._listoldindexes.append(colname) else: # Tell the condition cache about columns with dirty # indexes. if indexobj.dirty: self._condition_cache.nail() else: indexed = False self.colindexed[colname] = False if indexed: self.indexed = True if oldindexes: # this should only appear under 2.x Pro warnings.warn( "table ``%s`` has column indexes with PyTables 1.x format. " "Unfortunately, this format is not supported in " "PyTables 2.x series. Note that you can use the " "``ptrepack`` utility in order to recreate the indexes. " "The 1.x indexed columns found are: %s" % (self._v_pathname, self._listoldindexes), OldIndexWarning) # It does not matter to which column 'indexobj' belongs, # since their respective index objects share # the same number of elements. if self.indexed: self._indexedrows = indexobj.nelements self._unsaved_indexedrows = self.nrows - self._indexedrows # Put the autoindex value in a cache variable self._autoindex = self.autoindex _g_postInitHook = previous_api(_g_post_init_hook) def _getemptyarray(self, dtype): # Acts as a cache for empty arrays key = dtype if key in self._empty_array_cache: return self._empty_array_cache[key] else: self._empty_array_cache[ key] = arr = numpy.empty(shape=0, dtype=key) return arr def _get_container(self, shape): "Get the appropriate buffer for data depending on table nestedness." # This is *much* faster than the numpy.rec.array counterpart return numpy.empty(shape=shape, dtype=self._v_dtype) def _get_type_col_names(self, type_): """Returns a list containing 'type_' column names.""" return [colobj._v_pathname for colobj in self.description._f_walk('Col') if colobj.type == type_] _getTypeColNames = previous_api(_get_type_col_names) def _get_enum_map(self): """Return mapping from enumerated column names to `Enum` instances.""" enumMap = {} for colobj in self.description._f_walk('Col'): if colobj.kind == 'enum': enumMap[colobj._v_pathname] = colobj.enum return enumMap _getEnumMap = previous_api(_get_enum_map) def _g_create(self): """Create a new table on disk.""" # Warning against assigning too much columns... # F. Alted 2005-06-05 maxColumns = self._v_file.params['MAX_COLUMNS'] if (len(self.description._v_names) > maxColumns): warnings.warn( "table ``%s`` is exceeding the recommended " "maximum number of columns (%d); " "be ready to see PyTables asking for *lots* of memory " "and possibly slow I/O" % (self._v_pathname, maxColumns), PerformanceWarning) # 1. Create the HDF5 table (some parameters need to be computed). # Fix the byteorder of the recarray and update the number of # expected rows if necessary if self._v_recarray is not None: self._v_recarray = self._g_fix_byteorder_data(self._v_recarray, self._rabyteorder) if len(self._v_recarray) > self._v_expectedrows: self._v_expectedrows = len(self._v_recarray) # Compute a sensible chunkshape if self._v_chunkshape is None: self._v_chunkshape = self._calc_chunkshape( self._v_expectedrows, self.rowsize, self.rowsize) # Correct the byteorder, if still needed if self.byteorder is None: self.byteorder = sys.byteorder # Cache some data which is already in the description. # This is necessary to happen before creation time in order # to be able to populate the self._v_wdflts self._cache_description_data() # After creating the table, ``self._v_objectid`` needs to be # set because it is needed for setting attributes afterwards. self._v_objectid = self._create_table( self._v_new_title, self.filters.complib or '', obversion) self._v_recarray = None # not useful anymore self._rabyteorder = None # not useful anymore # 2. Compute or get chunk shape and buffer size parameters. self.nrowsinbuf = self._calc_nrowsinbuf() # 3. Get field fill attributes from the table description and # set them on disk. if self._v_file.params['PYTABLES_SYS_ATTRS']: set_attr = self._v_attrs._g__setattr for i, colobj in enumerate(self.description._f_walk(type="Col")): fieldname = "FIELD_%d_FILL" % i set_attr(fieldname, colobj.dflt) return self._v_objectid def _g_open(self): """Opens a table from disk and read the metadata on it. Creates an user description on the flight to easy the access to the actual data. """ # 1. Open the HDF5 table and get some data from it. self._v_objectid, description, chunksize = self._get_info() self._v_expectedrows = self.nrows # the actual number of rows # 2. Create an instance description to host the record fields. validate = not self._v_file._isPTFile # only for non-PyTables files self.description = Description(description, validate=validate) # 3. Compute or get chunk shape and buffer size parameters. if chunksize == 0: self._v_chunkshape = self._calc_chunkshape( self._v_expectedrows, self.rowsize, self.rowsize) else: self._v_chunkshape = (chunksize,) self.nrowsinbuf = self._calc_nrowsinbuf() # 4. If there are field fill attributes, get them from disk and # set them in the table description. if self._v_file.params['PYTABLES_SYS_ATTRS']: if "FIELD_0_FILL" in self._v_attrs._f_list("sys"): i = 0 get_attr = self._v_attrs.__getattr__ for objcol in self.description._f_walk(type="Col"): colname = objcol._v_pathname # Get the default values for each column fieldname = "FIELD_%s_FILL" % i defval = get_attr(fieldname) if defval is not None: objcol.dflt = defval else: warnings.warn("could not load default value " "for the ``%s`` column of table ``%s``; " "using ``%r`` instead" % (colname, self._v_pathname, objcol.dflt)) defval = objcol.dflt i += 1 # Set also the correct value in the desc._v_dflts dictionary for descr in self.description._f_walk(type="Description"): names = descr._v_names for i in range(len(names)): objcol = descr._v_colobjects[names[i]] if isinstance(objcol, Col): descr._v_dflts[objcol._v_name] = objcol.dflt # 5. Cache some data which is already in the description. self._cache_description_data() return self._v_objectid def _cache_description_data(self): """Cache some data which is already in the description. Some information is extracted from `self.description` to build some useful (but redundant) structures: * `self.colnames` * `self.colpathnames` * `self.coldescrs` * `self.coltypes` * `self.coldtypes` * `self.coldflts` * `self._v_dtype` * `self._time64colnames` * `self._strcolnames` * `self._colenums` """ self.colnames = list(self.description._v_names) self.colpathnames = [ col._v_pathname for col in self.description._f_walk() if not hasattr(col, '_v_names')] # bottom-level # Find ``time64`` column names. self._time64colnames = self._get_type_col_names('time64') # Find ``string`` column names. self._strcolnames = self._get_type_col_names('string') # Get a mapping of enumerated columns to their `Enum` instances. self._colenums = self._get_enum_map() # Get info about columns for colobj in self.description._f_walk(type="Col"): colname = colobj._v_pathname # Get the column types, types and defaults self.coldescrs[colname] = colobj self.coltypes[colname] = colobj.type self.coldtypes[colname] = colobj.dtype self.coldflts[colname] = colobj.dflt # Assign _v_dtype for this table self._v_dtype = self.description._v_dtype _cacheDescriptionData = previous_api(_cache_description_data) def _get_column_instance(self, colpathname): """Get the instance of the column with the given `colpathname`. If the column does not exist in the table, a `KeyError` is raised. """ try: return _reduce(getattr, colpathname.split('/'), self.description) except AttributeError: raise KeyError("table ``%s`` does not have a column named ``%s``" % (self._v_pathname, colpathname)) _getColumnInstance = previous_api(_get_column_instance) _check_column = _get_column_instance def _disable_indexing_in_queries(self): """Force queries not to use indexing. *Use only for testing.* """ if not self._enabled_indexing_in_queries: return # already disabled # The nail avoids setting/getting compiled conditions in/from # the cache where indexing is used. self._condition_cache.nail() self._enabled_indexing_in_queries = False _disableIndexingInQueries = previous_api(_disable_indexing_in_queries) def _enable_indexing_in_queries(self): """Allow queries to use indexing. *Use only for testing.* """ if self._enabled_indexing_in_queries: return # already enabled self._condition_cache.unnail() self._enabled_indexing_in_queries = True _enableIndexingInQueries = previous_api(_enable_indexing_in_queries) def _required_expr_vars(self, expression, uservars, depth=1): """Get the variables required by the `expression`. A new dictionary defining the variables used in the `expression` is returned. Required variables are first looked up in the `uservars` mapping, then in the set of top-level columns of the table. Unknown variables cause a `NameError` to be raised. When `uservars` is `None`, the local and global namespace where the API callable which uses this method is called is sought instead. This mechanism will not work as expected if this method is not used *directly* from an API callable. To disable this mechanism, just specify a mapping as `uservars`. Nested columns and columns from other tables are not allowed (`TypeError` and `ValueError` are raised, respectively). Also, non-column variable values are converted to NumPy arrays. `depth` specifies the depth of the frame in order to reach local or global variables. """ # Get the names of variables used in the expression. exprvarscache = self._exprvars_cache if not expression in exprvarscache: # Protection against growing the cache too much if len(exprvarscache) > 256: # Remove 10 (arbitrary) elements from the cache for k in exprvarscache.keys()[:10]: del exprvarscache[k] cexpr = compile(expression, '', 'eval') exprvars = [var for var in cexpr.co_names if var not in ['None', 'False', 'True'] and var not in numexpr_functions] exprvarscache[expression] = exprvars else: exprvars = exprvarscache[expression] # Get the local and global variable mappings of the user frame # if no mapping has been explicitly given for user variables. user_locals, user_globals = {}, {} if uservars is None: # We use specified depth to get the frame where the API # callable using this method is called. For instance: # # * ``table._required_expr_vars()`` (depth 0) is called by # * ``table._where()`` (depth 1) is called by # * ``table.where()`` (depth 2) is called by # * user-space functions (depth 3) user_frame = sys._getframe(depth) user_locals = user_frame.f_locals user_globals = user_frame.f_globals colinstances = self.colinstances tblfile, tblpath = self._v_file, self._v_pathname # Look for the required variables first among the ones # explicitly provided by the user, then among implicit columns, # then among external variables (only if no explicit variables). reqvars = {} for var in exprvars: # Get the value. if uservars is not None and var in uservars: val = uservars[var] elif var in colinstances: val = colinstances[var] elif uservars is None and var in user_locals: val = user_locals[var] elif uservars is None and var in user_globals: val = user_globals[var] else: raise NameError("name ``%s`` is not defined" % var) # Check the value. if hasattr(val, 'pathname'): # non-nested column if val.shape[1:] != (): raise NotImplementedError( "variable ``%s`` refers to " "a multidimensional column, " "not yet supported in conditions, sorry" % var) if (val._table_file is not tblfile or val._table_path != tblpath): raise ValueError("variable ``%s`` refers to a column " "which is not part of table ``%s``" % (var, tblpath)) if val.dtype.str[1:] == 'u8': raise NotImplementedError( "variable ``%s`` refers to " "a 64-bit unsigned integer column, " "not yet supported in conditions, sorry; " "please use regular Python selections" % var) elif hasattr(val, '_v_colpathnames'): # nested column raise TypeError( "variable ``%s`` refers to a nested column, " "not allowed in conditions" % var) else: # only non-column values are converted to arrays # XXX: not 100% sure about this if isinstance(val, unicode): val = numpy.asarray(val.encode('ascii')) else: val = numpy.asarray(val) reqvars[var] = val return reqvars _requiredExprVars = previous_api(_required_expr_vars) def _get_condition_key(self, condition, condvars): """Get the condition cache key for `condition` with `condvars`. Currently, the key is a tuple of `condition`, column variables names, normal variables names, column paths and variable paths (all are tuples). """ # Variable names for column and normal variables. colnames, varnames = [], [] # Column paths and types for each of the previous variable. colpaths, vartypes = [], [] for (var, val) in condvars.iteritems(): if hasattr(val, 'pathname'): # column colnames.append(var) colpaths.append(val.pathname) else: # array try: varnames.append(var) vartypes.append(numexpr_getType(val)) # expensive except ValueError: # This is more clear than the error given by Numexpr. raise TypeError("variable ``%s`` has data type ``%s``, " "not allowed in conditions" % (var, val.dtype.name)) colnames, varnames = tuple(colnames), tuple(varnames) colpaths, vartypes = tuple(colpaths), tuple(vartypes) condkey = (condition, colnames, varnames, colpaths, vartypes) return condkey _getConditionKey = previous_api(_get_condition_key) def _compile_condition(self, condition, condvars): """Compile the `condition` and extract usable index conditions. This method returns an instance of ``CompiledCondition``. See the ``compile_condition()`` function in the ``conditions`` module for more information about the compilation process. This method makes use of the condition cache when possible. """ # Look up the condition in the condition cache. condcache = self._condition_cache condkey = self._get_condition_key(condition, condvars) compiled = condcache.get(condkey) if compiled: return compiled.with_replaced_vars(condvars) # bingo! # Bad luck, the condition must be parsed and compiled. # Fortunately, the key provides some valuable information. ;) (condition, colnames, varnames, colpaths, vartypes) = condkey # Extract more information from referenced columns. typemap = dict(zip(varnames, vartypes)) # start with normal variables indexedcols = [] for colname in colnames: col = condvars[colname] # Extract types from *all* the given variables. coltype = col.dtype.type typemap[colname] = _nxtype_from_nptype[coltype] # Get the set of columns with usable indexes. if (self._enabled_indexing_in_queries # not test in-kernel searches and self.colindexed[col.pathname] and not col.index.dirty): indexedcols.append(colname) indexedcols = frozenset(indexedcols) # Now let ``compile_condition()`` do the Numexpr-related job. compiled = compile_condition(condition, typemap, indexedcols) # Check that there actually are columns in the condition. if not set(compiled.parameters).intersection(set(colnames)): raise ValueError("there are no columns taking part " "in condition ``%s``" % (condition,)) # Store the compiled condition in the cache and return it. condcache[condkey] = compiled return compiled.with_replaced_vars(condvars) _compileCondition = previous_api(_compile_condition) def will_query_use_indexing(self, condition, condvars=None): """Will a query for the condition use indexing? The meaning of the condition and *condvars* arguments is the same as in the :meth:`Table.where` method. If condition can use indexing, this method returns a frozenset with the path names of the columns whose index is usable. Otherwise, it returns an empty list. This method is mainly intended for testing. Keep in mind that changing the set of indexed columns or their dirtiness may make this method return different values for the same arguments at different times. """ # Compile the condition and extract usable index conditions. condvars = self._required_expr_vars(condition, condvars, depth=2) compiled = self._compile_condition(condition, condvars) # Return the columns in indexed expressions idxcols = [condvars[var].pathname for var in compiled.index_variables] return frozenset(idxcols) willQueryUseIndexing = previous_api(will_query_use_indexing) def where(self, condition, condvars=None, start=None, stop=None, step=None): """Iterate over values fulfilling a condition. This method returns a Row iterator (see :ref:`RowClassDescr`) which only selects rows in the table that satisfy the given condition (an expression-like string). The condvars mapping may be used to define the variable names appearing in the condition. condvars should consist of identifier-like strings pointing to Column (see :ref:`ColumnClassDescr`) instances *of this table*, or to other values (which will be converted to arrays). A default set of condition variables is provided where each top-level, non-nested column with an identifier-like name appears. Variables in condvars override the default ones. When condvars is not provided or None, the current local and global namespace is sought instead of condvars. The previous mechanism is mostly intended for interactive usage. To disable it, just specify a (maybe empty) mapping as condvars. If a range is supplied (by setting some of the start, stop or step parameters), only the rows in that range and fulfilling the condition are used. The meaning of the start, stop and step parameters is the same as for Python slices. When possible, indexed columns participating in the condition will be used to speed up the search. It is recommended that you place the indexed columns as left and out in the condition as possible. Anyway, this method has always better performance than regular Python selections on the table. You can mix this method with regular Python selections in order to support even more complex queries. It is strongly recommended that you pass the most restrictive condition as the parameter to this method if you want to achieve maximum performance. .. warning:: When in the middle of a table row iterator, you should not use methods that can change the number of rows in the table (like :meth:`Table.append` or :meth:`Table.remove_rows`) or unexpected errors will happen. Examples -------- :: >>> passvalues = [ row['col3'] for row in ... table.where('(col1 > 0) & (col2 <= 20)', step=5) ... if your_function(row['col2']) ] >>> print("Values that pass the cuts:", passvalues) Note that, from PyTables 1.1 on, you can nest several iterators over the same table. For example:: for p in rout.where('pressure < 16'): for q in rout.where('pressure < 9'): for n in rout.where('energy < 10'): print("pressure, energy:", p['pressure'], n['energy']) In this example, iterators returned by :meth:`Table.where` have been used, but you may as well use any of the other reading iterators that Table objects offer. See the file :file:`examples/nested-iter.py` for the full code. .. note:: A special care should be taken when the query condition includes string literals. Indeed Python 2 string literals are string of bytes while Python 3 strings are unicode objects. Let's assume that the table ``table`` has the following structure:: class Record(IsDescription): col1 = StringCol(4) # 4-character String of bytes col2 = IntCol() col3 = FloatCol() The type of "col1" do not change depending on the Python version used (of course) and it always corresponds to strings of bytes. Any condition involving "col1" should be written using the appropriate type for string literals in order to avoid :exc:`TypeError`\ s. The code below will work fine in Python 2 but will fail with a :exc:`TypeError` in Python 3:: condition = 'col1 == "AAAA"' for record in table.where(condition): # TypeError in Python3 # do something with "record" The reason is that in Python 3 "condition" implies a comparison between a string of bytes ("col1" contents) and an unicode literal ("AAAA"). The correct way to write the condition is:: condition = 'col1 == b"AAAA"' .. versionchanged:: 3.0 The start, stop and step parameters now behave like in slice. """ return self._where(condition, condvars, start, stop, step) def _where(self, condition, condvars, start=None, stop=None, step=None): """Low-level counterpart of `self.where()`.""" if profile: tref = time() if profile: show_stats("Entering table._where", tref) # Adjust the slice to be used. (start, stop, step) = self._process_range_read(start, stop, step) if start >= stop: # empty range, reset conditions self._use_index = False self._where_condition = None return iter([]) # Compile the condition and extract usable index conditions. condvars = self._required_expr_vars(condition, condvars, depth=3) compiled = self._compile_condition(condition, condvars) # Can we use indexes? if compiled.index_expressions: chunkmap = _table__where_indexed( self, compiled, condition, condvars, start, stop, step) if not isinstance(chunkmap, numpy.ndarray): # If it is not a NumPy array it should be an iterator # Reset conditions self._use_index = False self._where_condition = None # ...and return the iterator if chunkmap is not None: return chunkmap else: chunkmap = None # default to an in-kernel query args = [condvars[param] for param in compiled.parameters] self._where_condition = (compiled.function, args) row = tableextension.Row(self) if profile: show_stats("Exiting table._where", tref) return row._iter(start, stop, step, chunkmap=chunkmap) def read_where(self, condition, condvars=None, field=None, start=None, stop=None, step=None): """Read table data fulfilling the given *condition*. This method is similar to :meth:`Table.read`, having their common arguments and return values the same meanings. However, only the rows fulfilling the *condition* are included in the result. The meaning of the other arguments is the same as in the :meth:`Table.where` method. """ self._g_check_open() coords = [p.nrow for p in self._where(condition, condvars, start, stop, step)] self._where_condition = None # reset the conditions if len(coords) > 1: cstart, cstop = coords[0], coords[-1] + 1 if cstop - cstart == len(coords): # Chances for monotonically increasing row values. Refine. inc_seq = numpy.alltrue( numpy.arange(cstart, cstop) == numpy.array(coords)) if inc_seq: return self.read(cstart, cstop, field=field) return self.read_coordinates(coords, field) readWhere = previous_api(read_where) def append_where(self, dstTable, condition, condvars=None, start=None, stop=None, step=None): """Append rows fulfilling the condition to the dstTable table. dstTable must be capable of taking the rows resulting from the query, i.e. it must have columns with the expected names and compatible types. The meaning of the other arguments is the same as in the :meth:`Table.where` method. The number of rows appended to dstTable is returned as a result. .. versionchanged:: 3.0 The *whereAppend* method has been renamed into *append_where*. """ self._g_check_open() # Check that the destination file is not in read-only mode. dstTable._v_file._check_writable() # Row objects do not support nested columns, so we must iterate # over the flat column paths. When rows support nesting, # ``self.colnames`` can be directly iterated upon. colNames = [colName for colName in self.colpathnames] dstRow = dstTable.row nrows = 0 for srcRow in self._where(condition, condvars, start, stop, step): for colName in colNames: dstRow[colName] = srcRow[colName] dstRow.append() nrows += 1 dstTable.flush() return nrows whereAppend = previous_api(append_where) def get_where_list(self, condition, condvars=None, sort=False, start=None, stop=None, step=None): """Get the row coordinates fulfilling the given condition. The coordinates are returned as a list of the current flavor. sort means that you want to retrieve the coordinates ordered. The default is to not sort them. The meaning of the other arguments is the same as in the :meth:`Table.where` method. """ self._g_check_open() coords = [p.nrow for p in self._where(condition, condvars, start, stop, step)] coords = numpy.array(coords, dtype=SizeType) # Reset the conditions self._where_condition = None if sort: coords = numpy.sort(coords) return internal_to_flavor(coords, self.flavor) getWhereList = previous_api(get_where_list) def itersequence(self, sequence): """Iterate over a sequence of row coordinates. Notes ----- This iterator can be nested (see :meth:`Table.where` for an example). """ if not hasattr(sequence, '__getitem__'): raise TypeError(("Wrong 'sequence' parameter type. Only sequences " "are suported.")) # start, stop and step are necessary for the new iterator for # coordinates, and perhaps it would be useful to add them as # parameters in the future (not now, because I've just removed # the `sort` argument for 2.1). # # *Important note*: Negative values for step are not supported # for the general case, but only for the itersorted() and # read_sorted() purposes! The self._process_range_read will raise # an appropiate error. # F. Alted 2008-09-18 # A.V. 20130513: _process_range_read --> _process_range (start, stop, step) = self._process_range(None, None, None) if (start > stop) or (len(sequence) == 0): return iter([]) row = tableextension.Row(self) return row._iter(start, stop, step, coords=sequence) def _check_sortby_csi(self, sortby, checkCSI): if isinstance(sortby, Column): icol = sortby elif isinstance(sortby, str): icol = self.cols._f_col(sortby) else: raise TypeError( "`sortby` can only be a `Column` or string object, " "but you passed an object of type: %s" % type(sortby)) if icol.is_indexed and icol.index.kind == "full": if checkCSI and not icol.index.is_csi: # The index exists, but it is not a CSI one. raise ValueError( "Field `%s` must have associated a CSI index " "in table `%s`, but the existing one is not. " % (sortby, self)) return icol.index else: raise ValueError( "Field `%s` must have associated a 'full' index " "in table `%s`." % (sortby, self)) _check_sortby_CSI = previous_api(_check_sortby_csi) def itersorted(self, sortby, checkCSI=False, start=None, stop=None, step=None): """Iterate table data following the order of the index of sortby column. The sortby column must have associated a full index. If you want to ensure a fully sorted order, the index must be a CSI one. You may want to use the checkCSI argument in order to explicitly check for the existence of a CSI index. The meaning of the start, stop and step arguments is the same as in :meth:`Table.read`. .. versionchanged:: 3.0 If the *start* parameter is provided and *stop* is None then the table is iterated from *start* to the last line. In PyTables < 3.0 only one element was returned. """ index = self._check_sortby_csi(sortby, checkCSI) # Adjust the slice to be used. (start, stop, step) = self._process_range(start, stop, step, warn_negstep=False) if (start > stop and 0 < step) or (start < stop and 0 > step): # Fall-back action is to return an empty iterator return iter([]) row = tableextension.Row(self) return row._iter(start, stop, step, coords=index) def read_sorted(self, sortby, checkCSI=False, field=None, start=None, stop=None, step=None): """Read table data following the order of the index of sortby column. The sortby column must have associated a full index. If you want to ensure a fully sorted order, the index must be a CSI one. You may want to use the checkCSI argument in order to explicitly check for the existence of a CSI index. If field is supplied only the named column will be selected. If the column is not nested, an *array* of the current flavor will be returned; if it is, a *structured array* will be used instead. If no field is specified, all the columns will be returned in a structured array of the current flavor. The meaning of the start, stop and step arguments is the same as in :meth:`Table.read`. .. versionchanged:: 3.0 The start, stop and step parameters now behave like in slice. """ self._g_check_open() index = self._check_sortby_csi(sortby, checkCSI) coords = index[start:stop:step] return self.read_coordinates(coords, field) readSorted = previous_api(read_sorted) def iterrows(self, start=None, stop=None, step=None): """Iterate over the table using a Row instance. If a range is not supplied, *all the rows* in the table are iterated upon - you can also use the :meth:`Table.__iter__` special method for that purpose. If you want to iterate over a given *range of rows* in the table, you may use the start, stop and step parameters. .. warning:: When in the middle of a table row iterator, you should not use methods that can change the number of rows in the table (like :meth:`Table.append` or :meth:`Table.remove_rows`) or unexpected errors will happen. See Also -------- tableextension.Row : the table row iterator and field accessor Examples -------- :: result = [ row['var2'] for row in table.iterrows(step=5) if row['var1'] <= 20 ] Notes ----- This iterator can be nested (see :meth:`Table.where` for an example). .. versionchanged:: 3.0 If the *start* parameter is provided and *stop* is None then the table is iterated from *start* to the last line. In PyTables < 3.0 only one element was returned. """ (start, stop, step) = self._process_range(start, stop, step, warn_negstep=False) if (start > stop and 0 < step) or (start < stop and 0 > step): # Fall-back action is to return an empty iterator return iter([]) row = tableextension.Row(self) return row._iter(start, stop, step) def __iter__(self): """Iterate over the table using a Row instance. This is equivalent to calling :meth:`Table.iterrows` with default arguments, i.e. it iterates over *all the rows* in the table. See Also -------- tableextension.Row : the table row iterator and field accessor Examples -------- :: result = [ row['var2'] for row in table if row['var1'] <= 20 ] Which is equivalent to:: result = [ row['var2'] for row in table.iterrows() if row['var1'] <= 20 ] Notes ----- This iterator can be nested (see :meth:`Table.where` for an example). """ return self.iterrows() def _read(self, start, stop, step, field=None, out=None): """Read a range of rows and return an in-memory object.""" select_field = None if field: if field not in self.coldtypes: if field in self.description._v_names: # Remember to select this field select_field = field field = None else: raise KeyError(("Field {0} not found in table " "{1}").format(field, self)) else: # The column hangs directly from the top dtype_field = self.coldtypes[field] # Return a rank-0 array if start > stop if (start >= stop and 0 < step) or (start <= stop and 0 > step): if field is None: nra = self._get_container(0) return nra return numpy.empty(shape=0, dtype=dtype_field) nrows = len(xrange(start, stop, step)) if out is None: # Compute the shape of the resulting column object if field: # Create a container for the results result = numpy.empty(shape=nrows, dtype=dtype_field) else: # Recarray case result = self._get_container(nrows) else: # there is no fast way to byteswap, since different columns may # have different byteorders if not out.dtype.isnative: raise ValueError(("output array must be in system's byteorder " "or results will be incorrect")) if field: bytes_required = dtype_field.itemsize * nrows else: bytes_required = self.rowsize * nrows if bytes_required != out.nbytes: raise ValueError(('output array size invalid, got {0} bytes, ' 'need {1} bytes').format(out.nbytes, bytes_required)) if not out.flags['C_CONTIGUOUS']: raise ValueError('output array not C contiguous') result = out # Call the routine to fill-up the resulting array if step == 1 and not field: # This optimization works three times faster than # the row._fill_col method (up to 170 MB/s on a pentium IV @ 2GHz) self._read_records(start, stop - start, result) # Warning!: _read_field_name should not be used until # H5TBread_fields_name in tableextension will be finished # F. Alted 2005/05/26 # XYX Ho implementem per a PyTables 2.0?? elif field and step > 15 and 0: # For step>15, this seems to work always faster than row._fill_col. self._read_field_name(result, start, stop, step, field) else: self.row._fill_col(result, start, stop, step, field) if select_field: return result[select_field] else: return result def read(self, start=None, stop=None, step=None, field=None, out=None): """Get data in the table as a (record) array. The start, stop and step parameters can be used to select only a *range of rows* in the table. Their meanings are the same as in the built-in Python slices. If field is supplied only the named column will be selected. If the column is not nested, an *array* of the current flavor will be returned; if it is, a *structured array* will be used instead. If no field is specified, all the columns will be returned in a structured array of the current flavor. Columns under a nested column can be specified in the field parameter by using a slash character (/) as a separator (e.g. 'position/x'). The out parameter may be used to specify a NumPy array to receive the output data. Note that the array must have the same size as the data selected with the other parameters. Note that the array's datatype is not checked and no type casting is performed, so if it does not match the datatype on disk, the output will not be correct. When specifying a single nested column with the field parameter, and supplying an output buffer with the out parameter, the output buffer must contain all columns in the table. The data in all columns will be read into the output buffer. However, only the specified nested column will be returned from the method call. When data is read from disk in NumPy format, the output will be in the current system's byteorder, regardless of how it is stored on disk. If the out parameter is specified, the output array also must be in the current system's byteorder. .. versionchanged:: 3.0 Added the *out* parameter. Also the start, stop and step parameters now behave like in slice. Examples -------- Reading the entire table:: t.read() Reading record n. 6:: t.read(6, 7) Reading from record n. 6 to the end of the table:: t.read(6) """ self._g_check_open() if field: self._check_column(field) if out is not None and self.flavor != 'numpy': msg = ("Optional 'out' argument may only be supplied if array " "flavor is 'numpy', currently is {0}").format(self.flavor) raise TypeError(msg) #(start, stop, step) = self._process_range_read(start, stop, step, (start, stop, step) = self._process_range(start, stop, step, warn_negstep=False) arr = self._read(start, stop, step, field, out) return internal_to_flavor(arr, self.flavor) def _read_coordinates(self, coords, field=None): """Private part of `read_coordinates()` with no flavor conversion.""" coords = self._point_selection(coords) ncoords = len(coords) # Create a read buffer only if needed if field is None or ncoords > 0: # Doing a copy is faster when ncoords is small (<1000) if ncoords < min(1000, self.nrowsinbuf): result = self._v_iobuf[:ncoords].copy() else: result = self._get_container(ncoords) # Do the real read if ncoords > 0: # Turn coords into an array of coordinate indexes, if necessary if not (isinstance(coords, numpy.ndarray) and coords.dtype.type is _npsizetype and coords.flags.contiguous and coords.flags.aligned): # Get a contiguous and aligned coordinate array coords = numpy.array(coords, dtype=SizeType) self._read_elements(coords, result) # Do the final conversions, if needed if field: if ncoords > 0: result = get_nested_field(result, field) else: # Get an empty array from the cache result = self._getemptyarray(self.coldtypes[field]) return result _readCoordinates = previous_api(_read_coordinates) def read_coordinates(self, coords, field=None): """Get a set of rows given their indexes as a (record) array. This method works much like the :meth:`Table.read` method, but it uses a sequence (coords) of row indexes to select the wanted columns, instead of a column range. The selected rows are returned in an array or structured array of the current flavor. """ self._g_check_open() result = self._read_coordinates(coords, field) return internal_to_flavor(result, self.flavor) readCoordinates = previous_api(read_coordinates) def get_enum(self, colname): """Get the enumerated type associated with the named column. If the column named colname (a string) exists and is of an enumerated type, the corresponding Enum instance (see :ref:`EnumClassDescr`) is returned. If it is not of an enumerated type, a TypeError is raised. If the column does not exist, a KeyError is raised. """ self._check_column(colname) try: return self._colenums[colname] except KeyError: raise TypeError( "column ``%s`` of table ``%s`` is not of an enumerated type" % (colname, self._v_pathname)) getEnum = previous_api(get_enum) def col(self, name): """Get a column from the table. If a column called name exists in the table, it is read and returned as a NumPy object. If it does not exist, a KeyError is raised. Examples -------- :: narray = table.col('var2') That statement is equivalent to:: narray = table.read(field='var2') Here you can see how this method can be used as a shorthand for the :meth:`Table.read` method. """ return self.read(field=name) def __getitem__(self, key): """Get a row or a range of rows from the table. If key argument is an integer, the corresponding table row is returned as a record of the current flavor. If key is a slice, the range of rows determined by it is returned as a structured array of the current flavor. In addition, NumPy-style point selections are supported. In particular, if key is a list of row coordinates, the set of rows determined by it is returned. Furthermore, if key is an array of boolean values, only the coordinates where key is True are returned. Note that for the latter to work it is necessary that key list would contain exactly as many rows as the table has. Examples -------- :: record = table[4] recarray = table[4:1000:2] recarray = table[[4,1000]] # only retrieves rows 4 and 1000 recarray = table[[True, False, ..., True]] Those statements are equivalent to:: record = table.read(start=4)[0] recarray = table.read(start=4, stop=1000, step=2) recarray = table.read_coordinates([4,1000]) recarray = table.read_coordinates([True, False, ..., True]) Here, you can see how indexing can be used as a shorthand for the :meth:`Table.read` and :meth:`Table.read_coordinates` methods. """ self._g_check_open() if is_idx(key): # Index out of range protection if key >= self.nrows: raise IndexError("Index out of range") if key < 0: # To support negative values key += self.nrows (start, stop, step) = self._process_range(key, key + 1, 1) return self.read(start, stop, step)[0] elif isinstance(key, slice): (start, stop, step) = self._process_range( key.start, key.stop, key.step) return self.read(start, stop, step) # Try with a boolean or point selection elif type(key) in (list, tuple) or isinstance(key, numpy.ndarray): return self._read_coordinates(key, None) else: raise IndexError("Invalid index or slice: %r" % (key,)) def __setitem__(self, key, value): """Set a row or a range of rows in the table. It takes different actions depending on the type of the *key* parameter: if it is an integer, the corresponding table row is set to *value* (a record or sequence capable of being converted to the table structure). If *key* is a slice, the row slice determined by it is set to *value* (a record array or sequence capable of being converted to the table structure). In addition, NumPy-style point selections are supported. In particular, if key is a list of row coordinates, the set of rows determined by it is set to value. Furthermore, if key is an array of boolean values, only the coordinates where key is True are set to values from value. Note that for the latter to work it is necessary that key list would contain exactly as many rows as the table has. Examples -------- :: # Modify just one existing row table[2] = [456,'db2',1.2] # Modify two existing rows rows = numpy.rec.array([[457,'db1',1.2],[6,'de2',1.3]], formats='i4,a3,f8') table[1:30:2] = rows # modify a table slice table[[1,3]] = rows # only modifies rows 1 and 3 table[[True,False,True]] = rows # only modifies rows 0 and 2 Which is equivalent to:: table.modify_rows(start=2, rows=[456,'db2',1.2]) rows = numpy.rec.array([[457,'db1',1.2],[6,'de2',1.3]], formats='i4,a3,f8') table.modify_rows(start=1, stop=3, step=2, rows=rows) table.modify_coordinates([1,3,2], rows) table.modify_coordinates([True, False, True], rows) Here, you can see how indexing can be used as a shorthand for the :meth:`Table.modify_rows` and :meth:`Table.modify_coordinates` methods. """ self._g_check_open() self._v_file._check_writable() if is_idx(key): # Index out of range protection if key >= self.nrows: raise IndexError("Index out of range") if key < 0: # To support negative values key += self.nrows return self.modify_rows(key, key + 1, 1, [value]) elif isinstance(key, slice): (start, stop, step) = self._process_range( key.start, key.stop, key.step) return self.modify_rows(start, stop, step, value) # Try with a boolean or point selection elif type(key) in (list, tuple) or isinstance(key, numpy.ndarray): return self.modify_coordinates(key, value) else: raise IndexError("Invalid index or slice: %r" % (key,)) def _save_buffered_rows(self, wbufRA, lenrows): """Update the indexes after a flushing of rows.""" self._open_append(wbufRA) self._append_records(lenrows) self._close_append() if self.indexed: self._unsaved_indexedrows += lenrows # The table caches for indexed queries are dirty now self._dirtycache = True if self.autoindex: # Flush the unindexed rows self.flush_rows_to_index(_lastrow=False) else: # All the columns are dirty now self._mark_columns_as_dirty(self.colpathnames) _saveBufferedRows = previous_api(_save_buffered_rows) def append(self, rows): """Append a sequence of rows to the end of the table. The rows argument may be any object which can be converted to a structured array compliant with the table structure (otherwise, a ValueError is raised). This includes NumPy structured arrays, lists of tuples or array records, and a string or Python buffer. Examples -------- :: from tables import * class Particle(IsDescription): name = StringCol(16, pos=1) # 16-character String lati = IntCol(pos=2) # integer longi = IntCol(pos=3) # integer pressure = Float32Col(pos=4) # float (single-precision) temperature = FloatCol(pos=5) # double (double-precision) fileh = open_file('test4.h5', mode='w') table = fileh.create_table(fileh.root, 'table', Particle, "A table") # Append several rows in only one call table.append([("Particle: 10", 10, 0, 10 * 10, 10**2), ("Particle: 11", 11, -1, 11 * 11, 11**2), ("Particle: 12", 12, -2, 12 * 12, 12**2)]) fileh.close() """ self._g_check_open() self._v_file._check_writable() if not self._chunked: raise HDF5ExtError( "You cannot append rows to a non-chunked table.", h5bt=False) # Try to convert the object into a recarray compliant with table try: iflavor = flavor_of(rows) if iflavor != 'python': rows = array_as_internal(rows, iflavor) # Works for Python structures and always copies the original, # so the resulting object is safe for in-place conversion. wbufRA = numpy.rec.array(rows, dtype=self._v_dtype) except Exception as exc: # XXX raise ValueError("rows parameter cannot be converted into a " "recarray object compliant with table '%s'. " "The error was: <%s>" % (str(self), exc)) lenrows = wbufRA.shape[0] # If the number of rows to append is zero, don't do anything else if lenrows > 0: # Save write buffer to disk self._save_buffered_rows(wbufRA, lenrows) def _conv_to_recarr(self, obj): """Try to convert the object into a recarray.""" try: iflavor = flavor_of(obj) if iflavor != 'python': obj = array_as_internal(obj, iflavor) if hasattr(obj, "shape") and obj.shape == (): # To allow conversion of scalars (void type) into arrays. # See http://projects.scipy.org/scipy/numpy/ticket/315 # for discussion on how to pass buffers to constructors # See also http://projects.scipy.org/scipy/numpy/ticket/348 recarr = numpy.array([obj], dtype=self._v_dtype) else: # Works for Python structures and always copies the original, # so the resulting object is safe for in-place conversion. recarr = numpy.rec.array(obj, dtype=self._v_dtype) except Exception as exc: # XXX raise ValueError("Object cannot be converted into a recarray " "object compliant with table format '%s'. " "The error was: <%s>" % (self.description._v_nested_descr, exc)) return recarr def modify_coordinates(self, coords, rows): """Modify a series of rows in positions specified in coords. The values in the selected rows will be modified with the data given in rows. This method returns the number of rows modified. The possible values for the rows argument are the same as in :meth:`Table.append`. """ if rows is None: # Nothing to be done return SizeType(0) # Convert the coordinates to something expected by HDF5 coords = self._point_selection(coords) lcoords = len(coords) if len(rows) < lcoords: raise ValueError("The value has not enough elements to fill-in " "the specified range") # Convert rows into a recarray recarr = self._conv_to_recarr(rows) if len(coords) > 0: # Do the actual update of rows self._update_elements(lcoords, coords, recarr) # Redo the index if needed self._reindex(self.colpathnames) return SizeType(lcoords) modifyCoordinates = previous_api(modify_coordinates) def modify_rows(self, start=None, stop=None, step=None, rows=None): """Modify a series of rows in the slice [start:stop:step]. The values in the selected rows will be modified with the data given in rows. This method returns the number of rows modified. Should the modification exceed the length of the table, an IndexError is raised before changing data. The possible values for the rows argument are the same as in :meth:`Table.append`. """ if step is None: step = 1 if rows is None: # Nothing to be done return SizeType(0) if start is None: start = 0 if start < 0: raise ValueError("'start' must have a positive value.") if step < 1: raise ValueError( "'step' must have a value greater or equal than 1.") if stop is None: # compute the stop value. start + len(rows)*step does not work stop = start + (len(rows) - 1) * step + 1 (start, stop, step) = self._process_range(start, stop, step) if stop > self.nrows: raise IndexError("This modification will exceed the length of " "the table. Giving up.") # Compute the number of rows to read. nrows = len(xrange(start, stop, step)) if len(rows) != nrows: raise ValueError("The value has different elements than the " "specified range") # Convert rows into a recarray recarr = self._conv_to_recarr(rows) lenrows = len(recarr) if start + lenrows > self.nrows: raise IndexError("This modification will exceed the length of the " "table. Giving up.") # Do the actual update self._update_records(start, stop, step, recarr) # Redo the index if needed self._reindex(self.colpathnames) return SizeType(lenrows) modifyRows = previous_api(modify_rows) def modify_column(self, start=None, stop=None, step=None, column=None, colname=None): """Modify one single column in the row slice [start:stop:step]. The colname argument specifies the name of the column in the table to be modified with the data given in column. This method returns the number of rows modified. Should the modification exceed the length of the table, an IndexError is raised before changing data. The *column* argument may be any object which can be converted to a (record) array compliant with the structure of the column to be modified (otherwise, a ValueError is raised). This includes NumPy (record) arrays, lists of scalars, tuples or array records, and a string or Python buffer. """ if step is None: step = 1 if not isinstance(colname, str): raise TypeError("The 'colname' parameter must be a string.") self._v_file._check_writable() if column is None: # Nothing to be done return SizeType(0) if start is None: start = 0 if start < 0: raise ValueError("'start' must have a positive value.") if step < 1: raise ValueError( "'step' must have a value greater or equal than 1.") # Get the column format to be modified: objcol = self._get_column_instance(colname) descr = [objcol._v_parent._v_nested_descr[objcol._v_pos]] # Try to convert the column object into a NumPy ndarray try: # If the column is a recarray (or kind of), convert into ndarray if hasattr(column, 'dtype') and column.dtype.kind == 'V': column = numpy.rec.array(column, dtype=descr).field(0) else: # Make sure the result is always a *copy* of the original, # so the resulting object is safe for in-place conversion. iflavor = flavor_of(column) column = array_as_internal(column, iflavor) except Exception as exc: # XXX raise ValueError("column parameter cannot be converted into a " "ndarray object compliant with specified column " "'%s'. The error was: <%s>" % (str(column), exc)) # Get rid of single-dimensional dimensions column = column.squeeze() if column.shape == (): # Oops, stripped off to much dimensions column.shape = (1,) if stop is None: # compute the stop value. start + len(rows)*step does not work stop = start + (len(column) - 1) * step + 1 (start, stop, step) = self._process_range(start, stop, step) if stop > self.nrows: raise IndexError("This modification will exceed the length of " "the table. Giving up.") # Compute the number of rows to read. nrows = len(xrange(start, stop, step)) if len(column) < nrows: raise ValueError("The value has not enough elements to fill-in " "the specified range") # Now, read the original values: mod_recarr = self._read(start, stop, step) # Modify the appropriate column in the original recarray mod_col = get_nested_field(mod_recarr, colname) mod_col[:] = column # save this modified rows in table self._update_records(start, stop, step, mod_recarr) # Redo the index if needed self._reindex([colname]) return SizeType(nrows) modifyColumn = previous_api(modify_column) def modify_columns(self, start=None, stop=None, step=None, columns=None, names=None): """Modify a series of columns in the row slice [start:stop:step]. The names argument specifies the names of the columns in the table to be modified with the data given in columns. This method returns the number of rows modified. Should the modification exceed the length of the table, an IndexError is raised before changing data. The columns argument may be any object which can be converted to a structured array compliant with the structure of the columns to be modified (otherwise, a ValueError is raised). This includes NumPy structured arrays, lists of tuples or array records, and a string or Python buffer. """ if step is None: step = 1 if type(names) not in (list, tuple): raise TypeError("The 'names' parameter must be a list of strings.") if columns is None: # Nothing to be done return SizeType(0) if start is None: start = 0 if start < 0: raise ValueError("'start' must have a positive value.") if step < 1: raise ValueError(("'step' must have a value greater or " "equal than 1.")) descr = [] for colname in names: objcol = self._get_column_instance(colname) descr.append(objcol._v_parent._v_nested_descr[objcol._v_pos]) # descr.append(objcol._v_parent._v_dtype[objcol._v_pos]) # Try to convert the columns object into a recarray try: # Make sure the result is always a *copy* of the original, # so the resulting object is safe for in-place conversion. iflavor = flavor_of(columns) if iflavor != 'python': columns = array_as_internal(columns, iflavor) recarray = numpy.rec.array(columns, dtype=descr) else: recarray = numpy.rec.fromarrays(columns, dtype=descr) except Exception as exc: # XXX raise ValueError("columns parameter cannot be converted into a " "recarray object compliant with table '%s'. " "The error was: <%s>" % (str(self), exc)) if stop is None: # compute the stop value. start + len(rows)*step does not work stop = start + (len(recarray) - 1) * step + 1 (start, stop, step) = self._process_range(start, stop, step) if stop > self.nrows: raise IndexError("This modification will exceed the length of " "the table. Giving up.") # Compute the number of rows to read. nrows = len(xrange(start, stop, step)) if len(recarray) < nrows: raise ValueError("The value has not enough elements to fill-in " "the specified range") # Now, read the original values: mod_recarr = self._read(start, stop, step) # Modify the appropriate columns in the original recarray for i, name in enumerate(recarray.dtype.names): mod_col = get_nested_field(mod_recarr, names[i]) mod_col[:] = recarray[name].squeeze() # save this modified rows in table self._update_records(start, stop, step, mod_recarr) # Redo the index if needed self._reindex(names) return SizeType(nrows) modifyColumns = previous_api(modify_columns) def flush_rows_to_index(self, _lastrow=True): """Add remaining rows in buffers to non-dirty indexes. This can be useful when you have chosen non-automatic indexing for the table (see the :attr:`Table.autoindex` property in :class:`Table`) and you want to update the indexes on it. """ rowsadded = 0 if self.indexed: # Update the number of unsaved indexed rows start = self._indexedrows nrows = self._unsaved_indexedrows for (colname, colindexed) in self.colindexed.iteritems(): if colindexed: col = self.cols._g_col(colname) if nrows > 0 and not col.index.dirty: rowsadded = self._add_rows_to_index( colname, start, nrows, _lastrow, update=True) self._unsaved_indexedrows -= rowsadded self._indexedrows += rowsadded return rowsadded flushRowsToIndex = previous_api(flush_rows_to_index) def _add_rows_to_index(self, colname, start, nrows, lastrow, update): """Add more elements to the existing index.""" # This method really belongs to Column, but since it makes extensive # use of the table, it gets dangerous when closing the file, since the # column may be accessing a table which is being destroyed. index = self.cols._g_col(colname).index slicesize = index.slicesize # The next loop does not rely on xrange so that it can # deal with long ints (i.e. more than 32-bit integers) # This allows to index columns with more than 2**31 rows # F. Alted 2005-05-09 startLR = index.sorted.nrows * slicesize indexedrows = startLR - start stop = start + nrows - slicesize + 1 while startLR < stop: index.append( [self._read(startLR, startLR + slicesize, 1, colname)], update=update) indexedrows += slicesize startLR += slicesize # index the remaining rows in last row if lastrow and startLR < self.nrows: index.append_last_row( [self._read(startLR, self.nrows, 1, colname)], update=update) indexedrows += self.nrows - startLR return indexedrows _addRowsToIndex = previous_api(_add_rows_to_index) def remove_rows(self, start=None, stop=None, step=None): """Remove a range of rows in the table. .. versionchanged:: 3.0 The start, stop and step parameters now behave like in slice. .. seealso:: remove_row() Parameters ---------- start : int Sets the starting row to be removed. It accepts negative values meaning that the count starts from the end. A value of 0 means the first row. stop : int Sets the last row to be removed to stop-1, i.e. the end point is omitted (in the Python range() tradition). Negative values are also accepted. step : int The step size between rows to remove. .. versionadded:: 3.0 Examples -------- Removing rows from 5 to 10 (excluded):: t.remove_rows(5, 10) Removing all rows starting drom the 10th:: t.remove_rows(10) Removing the 6th row:: t.remove_rows(6, 7) .. note:: removing a single row can be done using the specific :meth:`remove_row` method. """ (start, stop, step) = self._process_range(start, stop, step) nrows = numpy.abs(stop - start) if nrows >= self.nrows: raise NotImplementedError('You are trying to delete all the rows ' 'in table "%s". This is not supported ' 'right now due to limitations on the ' 'underlying HDF5 library. Sorry!' % self._v_pathname) nrows = self._remove_rows(start, stop, step) # remove_rows is a invalidating index operation self._reindex(self.colpathnames) return SizeType(nrows) removeRows = previous_api(remove_rows) def remove_row(self, n): """Removes a row from the table. If only start is supplied, only this row is to be deleted. If a range is supplied, i.e. both the start and stop parameters are passed, all the rows in the range are removed. A step parameter is not supported, and it is not foreseen to be implemented anytime soon. Parameters ---------- n : int The index of the row to remove. .. versionadded:: 3.0 """ self.remove_rows(start=n, stop=n + 1) def _g_update_dependent(self): super(Table, self)._g_update_dependent() # Update the new path in columns self.cols._g_update_table_location(self) # Update the new path in the Row instance, if cached. Fixes #224. if 'row' in self.__dict__: self.__dict__['row'] = tableextension.Row(self) _g_updateDependent = previous_api(_g_update_dependent) def _g_move(self, newparent, newname): """Move this node in the hierarchy. This overloads the Node._g_move() method. """ itgpathname = _index_pathname_of(self) # First, move the table to the new location. super(Table, self)._g_move(newparent, newname) # Then move the associated index group (if any). try: itgroup = self._v_file._get_node(itgpathname) except NoSuchNodeError: pass else: newigroup = self._v_parent newiname = _index_name_of(self) itgroup._g_move(newigroup, newiname) def _g_remove(self, recursive=False, force=False): # Remove the associated index group (if any). itgpathname = _index_pathname_of(self) try: itgroup = self._v_file._get_node(itgpathname) except NoSuchNodeError: pass else: itgroup._f_remove(recursive=True) self.indexed = False # there are indexes no more # Remove the leaf itself from the hierarchy. super(Table, self)._g_remove(recursive, force) def _set_column_indexing(self, colpathname, indexed): """Mark the referred column as indexed or non-indexed.""" colindexed = self.colindexed isindexed, wasindexed = bool(indexed), colindexed[colpathname] if isindexed == wasindexed: return # indexing state is unchanged # Changing the set of indexed columns invalidates the condition cache self._condition_cache.clear() colindexed[colpathname] = isindexed self.indexed = max(colindexed.values()) # this is an OR :) _setColumnIndexing = previous_api(_set_column_indexing) def _mark_columns_as_dirty(self, colnames): """Mark column indexes in `colnames` as dirty.""" assert len(colnames) > 0 if self.indexed: colindexed, cols = self.colindexed, self.cols # Mark the proper indexes as dirty for colname in colnames: if colindexed[colname]: col = cols._g_col(colname) col.index.dirty = True _markColumnsAsDirty = previous_api(_mark_columns_as_dirty) def _reindex(self, colnames): """Re-index columns in `colnames` if automatic indexing is true.""" if self.indexed: colindexed, cols = self.colindexed, self.cols colstoindex = [] # Mark the proper indexes as dirty for colname in colnames: if colindexed[colname]: col = cols._g_col(colname) col.index.dirty = True colstoindex.append(colname) # Now, re-index the dirty ones if self.autoindex and colstoindex: self._do_reindex(dirty=True) # The table caches for indexed queries are dirty now self._dirtycache = True _reIndex = previous_api(_reindex) def _do_reindex(self, dirty): """Common code for `reindex()` and `reindex_dirty()`.""" indexedrows = 0 for (colname, colindexed) in self.colindexed.iteritems(): if colindexed: indexcol = self.cols._g_col(colname) indexedrows = indexcol._do_reindex(dirty) # Update counters in case some column has been updated if indexedrows > 0: self._indexedrows = indexedrows self._unsaved_indexedrows = self.nrows - indexedrows return SizeType(indexedrows) _doReIndex = previous_api(_do_reindex) def reindex(self): """Recompute all the existing indexes in the table. This can be useful when you suspect that, for any reason, the index information for columns is no longer valid and want to rebuild the indexes on it. """ self._do_reindex(dirty=False) reIndex = previous_api(reindex) def reindex_dirty(self): """Recompute the existing indexes in table, *if* they are dirty. This can be useful when you have set :attr:`Table.autoindex` (see :class:`Table`) to false for the table and you want to update the indexes after a invalidating index operation (:meth:`Table.remove_rows`, for example). """ self._do_reindex(dirty=True) reIndexDirty = previous_api(reindex_dirty) def _g_copy_rows(self, object, start, stop, step, sortby, checkCSI): "Copy rows from self to object" if sortby is None: self._g_copy_rows_optim(object, start, stop, step) return lenbuf = self.nrowsinbuf absstep = abs(step) if sortby is not None: index = self._check_sortby_csi(sortby, checkCSI) for start2 in xrange(start, stop, absstep * lenbuf): stop2 = start2 + absstep * lenbuf if stop2 > stop: stop2 = stop # The next 'if' is not needed, but it doesn't bother either if sortby is None: rows = self[start2:stop2:step] else: coords = index[start2:stop2:step] rows = self.read_coordinates(coords) # Save the records on disk object.append(rows) object.flush() _g_copyRows = previous_api(_g_copy_rows) def _g_copy_rows_optim(self, object, start, stop, step): """Copy rows from self to object (optimized version)""" nrowsinbuf = self.nrowsinbuf object._open_append(self._v_iobuf) nrowsdest = object.nrows for start2 in xrange(start, stop, step * nrowsinbuf): # Save the records on disk stop2 = start2 + step * nrowsinbuf if stop2 > stop: stop2 = stop # Optimized version (it saves some conversions) nrows = ((stop2 - start2 - 1) // step) + 1 self.row._fill_col(self._v_iobuf, start2, stop2, step, None) # The output buffer is created anew, # so the operation is safe to in-place conversion. object._append_records(nrows) nrowsdest += nrows object._close_append() _g_copyRows_optim = previous_api(_g_copy_rows_optim) def _g_prop_indexes(self, other): """Generate index in `other` table for every indexed column here.""" oldcols, newcols = self.colinstances, other.colinstances for colname in newcols: if (isinstance(oldcols[colname], Column)): oldcolindexed = oldcols[colname].is_indexed if oldcolindexed: oldcolindex = oldcols[colname].index newcol = newcols[colname] newcol.create_index( kind=oldcolindex.kind, optlevel=oldcolindex.optlevel, filters=oldcolindex.filters, tmp_dir=None) _g_propIndexes = previous_api(_g_prop_indexes) def _g_copy_with_stats(self, group, name, start, stop, step, title, filters, chunkshape, _log, **kwargs): """Private part of Leaf.copy() for each kind of leaf.""" # Get the private args for the Table flavor of copy() sortby = kwargs.pop('sortby', None) propindexes = kwargs.pop('propindexes', False) checkCSI = kwargs.pop('checkCSI', False) # Compute the correct indices. (start, stop, step) = self._process_range_read( start, stop, step, warn_negstep=sortby is None) # And the number of final rows nrows = len(xrange(start, stop, step)) # Create the new table and copy the selected data. newtable = Table(group, name, self.description, title=title, filters=filters, expectedrows=nrows, chunkshape=chunkshape, _log=_log) self._g_copy_rows(newtable, start, stop, step, sortby, checkCSI) nbytes = newtable.nrows * newtable.rowsize # Generate equivalent indexes in the new table, if required. if propindexes and self.indexed: self._g_prop_indexes(newtable) return (newtable, nbytes) _g_copyWithStats = previous_api(_g_copy_with_stats) # This overloading of copy is needed here in order to document # the additional keywords for the Table case. def copy(self, newparent=None, newname=None, overwrite=False, createparents=False, **kwargs): """Copy this table and return the new one. This method has the behavior and keywords described in :meth:`Leaf.copy`. Moreover, it recognises the following additional keyword arguments. Parameters ---------- sortby If specified, and sortby corresponds to a column with an index, then the copy will be sorted by this index. If you want to ensure a fully sorted order, the index must be a CSI one. A reverse sorted copy can be achieved by specifying a negative value for the step keyword. If sortby is omitted or None, the original table order is used. checkCSI If true and a CSI index does not exist for the sortby column, an error will be raised. If false (the default), it does nothing. You can use this flag in order to explicitly check for the existence of a CSI index. propindexes If true, the existing indexes in the source table are propagated (created) to the new one. If false (the default), the indexes are not propagated. """ return super(Table, self).copy( newparent, newname, overwrite, createparents, **kwargs) def flush(self): """Flush the table buffers.""" # Flush rows that remains to be appended if 'row' in self.__dict__: self.row._flush_buffered_rows() if self.indexed and self.autoindex: # Flush any unindexed row rowsadded = self.flush_rows_to_index(_lastrow=True) assert rowsadded <= 0 or self._indexedrows == self.nrows, \ ("internal error: the number of indexed rows (%d) " "and rows in the table (%d) is not equal; " "please report this to the authors." % (self._indexedrows, self.nrows)) if self._dirtyindexes: # Finally, re-index any dirty column self.reindex_dirty() super(Table, self).flush() def _g_pre_kill_hook(self): """Code to be called before killing the node.""" # Flush the buffers before to clean-up them # self.flush() # It seems that flushing during the __del__ phase is a sure receipt for # bringing all kind of problems: # 1. Illegal Instruction # 2. Malloc(): trying to call free() twice # 3. Bus Error # 4. Segmentation fault # So, the best would be doing *nothing* at all in this __del__ phase. # As a consequence, the I/O will not be cleaned until a call to # Table.flush() would be done. This could lead to a potentially large # memory consumption. # NOTE: The user should make a call to Table.flush() whenever he has # finished working with his table. # I've added a Performance warning in order to compel the user to # call self.flush() before the table is being preempted. # F. Alted 2006-08-03 if (('row' in self.__dict__ and self.row._get_unsaved_nrows() > 0) or (self.indexed and self.autoindex and (self._unsaved_indexedrows > 0 or self._dirtyindexes))): warnings.warn(("table ``%s`` is being preempted from alive nodes " "without its buffers being flushed or with some " "index being dirty. This may lead to very " "ineficient use of resources and even to fatal " "errors in certain situations. Please do a call " "to the .flush() or .reindex_dirty() methods on " "this table before start using other nodes.") % (self._v_pathname), PerformanceWarning) # Get rid of the IO buffers (if they have been created at all) mydict = self.__dict__ if '_v_iobuf' in mydict: del mydict['_v_iobuf'] if '_v_wdflts' in mydict: del mydict['_v_wdflts'] _g_preKillHook = previous_api(_g_pre_kill_hook) def _f_close(self, flush=True): if not self._v_isopen: return # the node is already closed # .. note:: # # As long as ``Table`` objects access their indices on closing, # ``File.close()`` will need to make *two separate passes* # to first close ``Table`` objects and then ``Index`` hierarchies. # # Flush right now so the row object does not get in the middle. if flush: self.flush() # Some warnings can be issued after calling `self._g_set_location()` # in `self.__init__()`. If warnings are turned into exceptions, # `self._g_post_init_hook` may not be called and `self.cols` not set. # One example of this is # ``test_create.createTestCase.test05_maxFieldsExceeded()``. cols = self.cols if cols is not None: cols._g_close() # Close myself as a leaf. super(Table, self)._f_close(False) def __repr__(self): """This provides column metainfo in addition to standard __str__""" if self.indexed: format = """\ %s description := %r byteorder := %r chunkshape := %r autoindex := %r colindexes := %r""" return format % (str(self), self.description, self.byteorder, self.chunkshape, self.autoindex, _ColIndexes(self.colindexes)) else: return """\ %s description := %r byteorder := %r chunkshape := %r""" % \ (str(self), self.description, self.byteorder, self.chunkshape) class Cols(object): """Container for columns in a table or nested column. This class is used as an *accessor* to the columns in a table or nested column. It supports the *natural naming* convention, so that you can access the different columns as attributes which lead to Column instances (for non-nested columns) or other Cols instances (for nested columns). For instance, if table.cols is a Cols instance with a column named col1 under it, the later can be accessed as table.cols.col1. If col1 is nested and contains a col2 column, this can be accessed as table.cols.col1.col2 and so on. Because of natural naming, the names of members start with special prefixes, like in the Group class (see :ref:`GroupClassDescr`). Like the Column class (see :ref:`ColumnClassDescr`), Cols supports item access to read and write ranges of values in the table or nested column. .. rubric:: Cols attributes .. attribute:: _v_colnames A list of the names of the columns hanging directly from the associated table or nested column. The order of the names matches the order of their respective columns in the containing table. .. attribute:: _v_colpathnames A list of the pathnames of all the columns under the associated table or nested column (in preorder). If it does not contain nested columns, this is exactly the same as the :attr:`Cols._v_colnames` attribute. .. attribute:: _v_desc The associated Description instance (see :ref:`DescriptionClassDescr`). """ def _g_gettable(self): return self._v__tableFile._get_node(self._v__tablePath) _v_table = property( _g_gettable, None, None, "The parent Table instance (see :ref:`TableClassDescr`).") def __init__(self, table, desc): myDict = self.__dict__ myDict['_v__tableFile'] = table._v_file myDict['_v__tablePath'] = table._v_pathname myDict['_v_desc'] = desc myDict['_v_colnames'] = desc._v_names myDict['_v_colpathnames'] = table.description._v_pathnames # Put the column in the local dictionary for name in desc._v_names: if name in desc._v_types: myDict[name] = Column(table, name, desc) else: myDict[name] = Cols(table, desc._v_colobjects[name]) def _g_update_table_location(self, table): """Updates the location information about the associated `table`.""" myDict = self.__dict__ myDict['_v__tableFile'] = table._v_file myDict['_v__tablePath'] = table._v_pathname # Update the locations in individual columns. for colname in self._v_colnames: myDict[colname]._g_update_table_location(table) _g_updateTableLocation = previous_api(_g_update_table_location) def __len__(self): """Get the number of top level columns in table.""" return len(self._v_colnames) def _f_col(self, colname): """Get an accessor to the column colname. This method returns a Column instance (see :ref:`ColumnClassDescr`) if the requested column is not nested, and a Cols instance (see :ref:`ColsClassDescr`) if it is. You may use full column pathnames in colname. Calling cols._f_col('col1/col2') is equivalent to using cols.col1.col2. However, the first syntax is more intended for programmatic use. It is also better if you want to access columns with names that are not valid Python identifiers. """ if not isinstance(colname, str): raise TypeError("Parameter can only be an string. You passed " "object: %s" % colname) if ((colname.find('/') > -1 and not colname in self._v_colpathnames) and not colname in self._v_colnames): raise KeyError(("Cols accessor ``%s.cols%s`` does not have a " "column named ``%s``") % (self._v__tablePath, self._v_desc._v_pathname, colname)) return self._g_col(colname) def _g_col(self, colname): """Like `self._f_col()` but it does not check arguments.""" # Get the Column or Description object inames = colname.split('/') cols = self for iname in inames: cols = cols.__dict__[iname] return cols def __getitem__(self, key): """Get a row or a range of rows from a table or nested column. If key argument is an integer, the corresponding nested type row is returned as a record of the current flavor. If key is a slice, the range of rows determined by it is returned as a structured array of the current flavor. Examples -------- :: record = table.cols[4] # equivalent to table[4] recarray = table.cols.Info[4:1000:2] Those statements are equivalent to:: nrecord = table.read(start=4)[0] nrecarray = table.read(start=4, stop=1000, step=2).field('Info') Here you can see how a mix of natural naming, indexing and slicing can be used as shorthands for the :meth:`Table.read` method. """ table = self._v_table nrows = table.nrows if is_idx(key): # Index out of range protection if key >= nrows: raise IndexError("Index out of range") if key < 0: # To support negative values key += nrows (start, stop, step) = table._process_range(key, key + 1, 1) colgroup = self._v_desc._v_pathname if colgroup == "": # The root group return table.read(start, stop, step)[0] else: crecord = table.read(start, stop, step)[0] return crecord[colgroup] elif isinstance(key, slice): (start, stop, step) = table._process_range( key.start, key.stop, key.step) colgroup = self._v_desc._v_pathname if colgroup == "": # The root group return table.read(start, stop, step) else: crecarray = table.read(start, stop, step) if hasattr(crecarray, "field"): return crecarray.field(colgroup) # RecArray case else: return get_nested_field(crecarray, colgroup) # numpy case else: raise TypeError("invalid index or slice: %r" % (key,)) def __setitem__(self, key, value): """Set a row or a range of rows in a table or nested column. If key argument is an integer, the corresponding row is set to value. If key is a slice, the range of rows determined by it is set to value. Examples -------- :: table.cols[4] = record table.cols.Info[4:1000:2] = recarray Those statements are equivalent to:: table.modify_rows(4, rows=record) table.modify_column(4, 1000, 2, colname='Info', column=recarray) Here you can see how a mix of natural naming, indexing and slicing can be used as shorthands for the :meth:`Table.modify_rows` and :meth:`Table.modify_column` methods. """ table = self._v_table nrows = table.nrows if is_idx(key): # Index out of range protection if key >= nrows: raise IndexError("Index out of range") if key < 0: # To support negative values key += nrows (start, stop, step) = table._process_range(key, key + 1, 1) elif isinstance(key, slice): (start, stop, step) = table._process_range( key.start, key.stop, key.step) else: raise TypeError("invalid index or slice: %r" % (key,)) # Actually modify the correct columns colgroup = self._v_desc._v_pathname if colgroup == "": # The root group table.modify_rows(start, stop, step, rows=value) else: table.modify_column( start, stop, step, colname=colgroup, column=value) def _g_close(self): # First, close the columns (ie possible indices open) for col in self._v_colnames: colobj = self._g_col(col) if isinstance(colobj, Column): colobj.close() # Delete the reference to column del self.__dict__[col] else: colobj._g_close() self.__dict__.clear() def __str__(self): """The string representation for this object.""" # The pathname tablepathname = self._v__tablePath descpathname = self._v_desc._v_pathname if descpathname: descpathname = "." + descpathname # Get this class name classname = self.__class__.__name__ # The number of columns ncols = len(self._v_colnames) return "%s.cols%s (%s), %s columns" % \ (tablepathname, descpathname, classname, ncols) def __repr__(self): """A detailed string representation for this object.""" out = str(self) + "\n" for name in self._v_colnames: # Get this class name classname = getattr(self, name).__class__.__name__ # The type if name in self._v_desc._v_dtypes: tcol = self._v_desc._v_dtypes[name] # The shape for this column shape = (self._v_table.nrows,) + \ self._v_desc._v_dtypes[name].shape else: tcol = "Description" # Description doesn't have a shape currently shape = () out += " %s (%s%s, %s)" % (name, classname, shape, tcol) + "\n" return out class Column(object): """Accessor for a non-nested column in a table. Each instance of this class is associated with one *non-nested* column of a table. These instances are mainly used to read and write data from the table columns using item access (like the Cols class - see :ref:`ColsClassDescr`), but there are a few other associated methods to deal with indexes. .. rubric:: Column attributes .. attribute:: descr The Description (see :ref:`DescriptionClassDescr`) instance of the parent table or nested column. .. attribute:: name The name of the associated column. .. attribute:: pathname The complete pathname of the associated column (the same as Column.name if the column is not inside a nested column). Parameters ---------- table The parent table instance name The name of the column that is associated with this object descr The parent description object """ # Lazy read-only attributes # ````````````````````````` @lazyattr def dtype(self): """The NumPy dtype that most closely matches this column.""" return self.descr._v_dtypes[self.name].base # Get rid of shape info @lazyattr def type(self): """The PyTables type of the column (a string).""" return self.descr._v_types[self.name] # Properties # ~~~~~~~~~~ def _gettable(self): return self._table_file._get_node(self._table_path) table = property(_gettable, None, None, """The parent Table instance (see :ref:`TableClassDescr`).""") def _getindex(self): indexPath = _index_pathname_of_column_(self._table_path, self.pathname) try: index = self._table_file._get_node(indexPath) except NodeError: index = None # The column is not indexed return index index = property(_getindex, None, None, """The Index instance (see :ref:`IndexClassDescr`) associated with this column (None if the column is not indexed).""") def _getshape(self): return (self.table.nrows,) + self.descr._v_dtypes[self.name].shape shape = property(_getshape, None, None, "The shape of this column.") def _isindexed(self): if self.index is None: return False else: return True is_indexed = property(_isindexed, None, None, "True if the column is indexed, false otherwise.") maindim = property( lambda self: 0, None, None, """"The dimension along which iterators work. Its value is 0 (i.e. the first dimension).""") def __init__(self, table, name, descr): self._table_file = table._v_file self._table_path = table._v_pathname self.name = name """The name of the associated column.""" self.pathname = descr._v_colobjects[name]._v_pathname """The complete pathname of the associated column (the same as Column.name if the column is not inside a nested column).""" self.descr = descr """The Description (see :ref:`DescriptionClassDescr`) instance of the parent table or nested column.""" def _g_update_table_location(self, table): """Updates the location information about the associated `table`.""" self._table_file = table._v_file self._table_path = table._v_pathname _g_updateTableLocation = previous_api(_g_update_table_location) def __len__(self): """Get the number of elements in the column. This matches the length in rows of the parent table. """ return self.table.nrows def __getitem__(self, key): """Get a row or a range of rows from a column. If key argument is an integer, the corresponding element in the column is returned as an object of the current flavor. If key is a slice, the range of elements determined by it is returned as an array of the current flavor. Examples -------- :: print("Column handlers:") for name in table.colnames: print(table.cols._f_col(name)) print("Select table.cols.name[1]-->", table.cols.name[1]) print("Select table.cols.name[1:2]-->", table.cols.name[1:2]) print("Select table.cols.name[:]-->", table.cols.name[:]) print("Select table.cols._f_col('name')[:]-->", table.cols._f_col('name')[:]) The output of this for a certain arbitrary table is:: Column handlers: /table.cols.name (Column(), string, idx=None) /table.cols.lati (Column(), int32, idx=None) /table.cols.longi (Column(), int32, idx=None) /table.cols.vector (Column(2,), int32, idx=None) /table.cols.matrix2D (Column(2, 2), float64, idx=None) Select table.cols.name[1]--> Particle: 11 Select table.cols.name[1:2]--> ['Particle: 11'] Select table.cols.name[:]--> ['Particle: 10' 'Particle: 11' 'Particle: 12' 'Particle: 13' 'Particle: 14'] Select table.cols._f_col('name')[:]--> ['Particle: 10' 'Particle: 11' 'Particle: 12' 'Particle: 13' 'Particle: 14'] See the :file:`examples/table2.py` file for a more complete example. """ table = self.table # Generalized key support not there yet, but at least allow # for a tuple with one single element (the main dimension). # (key,) --> key if isinstance(key, tuple) and len(key) == 1: key = key[0] if is_idx(key): # Index out of range protection if key >= table.nrows: raise IndexError("Index out of range") if key < 0: # To support negative values key += table.nrows (start, stop, step) = table._process_range(key, key + 1, 1) return table.read(start, stop, step, self.pathname)[0] elif isinstance(key, slice): (start, stop, step) = table._process_range( key.start, key.stop, key.step) return table.read(start, stop, step, self.pathname) else: raise TypeError( "'%s' key type is not valid in this context" % key) def __iter__(self): """Iterate through all items in the column.""" table = self.table itemsize = self.dtype.itemsize nrowsinbuf = table._v_file.params['IO_BUFFER_SIZE'] // itemsize buf = numpy.empty((nrowsinbuf, ), self.dtype) max_row = len(self) for start_row in xrange(0, len(self), nrowsinbuf): end_row = min(start_row + nrowsinbuf, max_row) buf_slice = buf[0:end_row - start_row] table.read(start_row, end_row, 1, field=self.pathname, out=buf_slice) for row in buf_slice: yield row def __setitem__(self, key, value): """Set a row or a range of rows in a column. If key argument is an integer, the corresponding element is set to value. If key is a slice, the range of elements determined by it is set to value. Examples -------- :: # Modify row 1 table.cols.col1[1] = -1 # Modify rows 1 and 3 table.cols.col1[1::2] = [2,3] Which is equivalent to:: # Modify row 1 table.modify_columns(start=1, columns=[[-1]], names=['col1']) # Modify rows 1 and 3 columns = numpy.rec.fromarrays([[2,3]], formats='i4') table.modify_columns(start=1, step=2, columns=columns, names=['col1']) """ table = self.table table._v_file._check_writable() # Generalized key support not there yet, but at least allow # for a tuple with one single element (the main dimension). # (key,) --> key if isinstance(key, tuple) and len(key) == 1: key = key[0] if is_idx(key): # Index out of range protection if key >= table.nrows: raise IndexError("Index out of range") if key < 0: # To support negative values key += table.nrows return table.modify_column(key, key + 1, 1, [[value]], self.pathname) elif isinstance(key, slice): (start, stop, step) = table._process_range( key.start, key.stop, key.step) return table.modify_column(start, stop, step, value, self.pathname) else: raise ValueError("Non-valid index or slice: %s" % key) def create_index(self, optlevel=6, kind="medium", filters=None, tmp_dir=None, _blocksizes=None, _testmode=False, _verbose=False): """Create an index for this column. .. warning:: In some situations it is useful to get a completely sorted index (CSI). For those cases, it is best to use the :meth:`Column.create_csindex` method instead. Parameters ---------- optlevel : int The optimization level for building the index. The levels ranges from 0 (no optimization) up to 9 (maximum optimization). Higher levels of optimization mean better chances for reducing the entropy of the index at the price of using more CPU, memory and I/O resources for creating the index. kind : str The kind of the index to be built. It can take the 'ultralight', 'light', 'medium' or 'full' values. Lighter kinds ('ultralight' and 'light') mean that the index takes less space on disk, but will perform queries slower. Heavier kinds ('medium' and 'full') mean better chances for reducing the entropy of the index (increasing the query speed) at the price of using more disk space as well as more CPU, memory and I/O resources for creating the index. Note that selecting a full kind with an optlevel of 9 (the maximum) guarantees the creation of an index with zero entropy, that is, a completely sorted index (CSI) - provided that the number of rows in the table does not exceed the 2**48 figure (that is more than 100 trillions of rows). See :meth:`Column.create_csindex` method for a more direct way to create a CSI index. filters : Filters Specify the Filters instance used to compress the index. If None, default index filters will be used (currently, zlib level 1 with shuffling). tmp_dir When kind is other than 'ultralight', a temporary file is created during the index build process. You can use the tmp_dir argument to specify the directory for this temporary file. The default is to create it in the same directory as the file containing the original table. """ kinds = ['ultralight', 'light', 'medium', 'full'] if kind not in kinds: raise ValueError("Kind must have any of these values: %s" % kinds) if (not isinstance(optlevel, (int, long)) or (optlevel < 0 or optlevel > 9)): raise ValueError("Optimization level must be an integer in the " "range 0-9") if filters is None: filters = default_index_filters if tmp_dir is None: tmp_dir = os.path.dirname(self._table_file.filename) else: if not os.path.isdir(tmp_dir): raise ValueError("Temporary directory '%s' does not exist" % tmp_dir) if (_blocksizes is not None and (not isinstance(_blocksizes, tuple) or len(_blocksizes) != 4)): raise ValueError("_blocksizes must be a tuple with exactly 4 " "elements") idxrows = _column__create_index(self, optlevel, kind, filters, tmp_dir, _blocksizes, _verbose) return SizeType(idxrows) createIndex = previous_api(create_index) def create_csindex(self, filters=None, tmp_dir=None, _blocksizes=None, _testmode=False, _verbose=False): """Create a completely sorted index (CSI) for this column. This method guarantees the creation of an index with zero entropy, that is, a completely sorted index (CSI) -- provided that the number of rows in the table does not exceed the 2**48 figure (that is more than 100 trillions of rows). A CSI index is needed for some table methods (like :meth:`Table.itersorted` or :meth:`Table.read_sorted`) in order to ensure completely sorted results. For the meaning of filters and tmp_dir arguments see :meth:`Column.create_index`. Notes ----- This method is equivalent to Column.create_index(optlevel=9, kind='full', ...). """ return self.create_index( kind='full', optlevel=9, filters=filters, tmp_dir=tmp_dir, _blocksizes=_blocksizes, _testmode=_testmode, _verbose=_verbose) createCSIndex = previous_api(create_csindex) def _do_reindex(self, dirty): """Common code for reindex() and reindex_dirty() codes.""" index = self.index dodirty = True if dirty and not index.dirty: dodirty = False if index is not None and dodirty: self._table_file._check_writable() # Get the old index parameters kind = index.kind optlevel = index.optlevel filters = index.filters # We *need* to tell the index that it is going to be undirty. # This is needed here so as to unnail() the condition cache. index.dirty = False # Delete the existing Index index._f_remove() # Create a new Index with the previous parameters return SizeType(self.create_index( kind=kind, optlevel=optlevel, filters=filters)) else: return SizeType(0) # The column is not intended for indexing _doReIndex = previous_api(_do_reindex) def reindex(self): """Recompute the index associated with this column. This can be useful when you suspect that, for any reason, the index information is no longer valid and you want to rebuild it. This method does nothing if the column is not indexed. """ self._do_reindex(dirty=False) reIndex = previous_api(reindex) def reindex_dirty(self): """Recompute the associated index only if it is dirty. This can be useful when you have set :attr:`Table.autoindex` to false for the table and you want to update the column's index after an invalidating index operation (like :meth:`Table.remove_rows`). This method does nothing if the column is not indexed. """ self._do_reindex(dirty=True) reIndexDirty = previous_api(reindex_dirty) def remove_index(self): """Remove the index associated with this column. This method does nothing if the column is not indexed. The removed index can be created again by calling the :meth:`Column.create_index` method. """ self._table_file._check_writable() # Remove the index if existing. if self.is_indexed: index = self.index index._f_remove() self.table._set_column_indexing(self.pathname, False) removeIndex = previous_api(remove_index) def close(self): """Close this column.""" self.__dict__.clear() def __str__(self): """The string representation for this object.""" # The pathname tablepathname = self._table_path pathname = self.pathname.replace('/', '.') # Get this class name classname = self.__class__.__name__ # The shape for this column shape = self.shape # The type tcol = self.descr._v_types[self.name] return "%s.cols.%s (%s%s, %s, idx=%s)" % \ (tablepathname, pathname, classname, shape, tcol, self.index) def __repr__(self): """A detailed string representation for this object.""" return str(self) ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/tableExtension.py000066400000000000000000000004141231437614300203160ustar00rootroot00000000000000from warnings import warn from tables.tableextension import * _warnmsg = ("tableExtension is pending deprecation, import tableextension instead. " "You may use the pt2to3 tool to update your source code.") warn(_warnmsg, DeprecationWarning, stacklevel=2) PyTables-v.3.1.1/tables/tableextension.pyx000066400000000000000000001627601231437614300205630ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: June 17, 2005 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Here is where Table and Row extension types live. Classes (type extensions): Table Row Functions: Misc variables: """ import sys import numpy from time import time from tables.description import Col from tables.exceptions import HDF5ExtError from tables.conditions import call_on_recarr from tables.utilsextension import (get_nested_field, atom_from_hdf5_type, create_nested_type, hdf5_to_np_ext_type, create_nested_type, platform_byteorder, pttype_to_hdf5, pt_special_kinds, npext_prefixes_to_ptkinds, hdf5_class_to_string, H5T_STD_I64) from tables.utils import SizeType from utilsextension cimport get_native_type, cstr_to_pystr # numpy functions & objects from hdf5extension cimport Leaf from cpython cimport PY_MAJOR_VERSION from cpython.unicode cimport PyUnicode_DecodeUTF8 from libc.stdio cimport snprintf from libc.stdlib cimport malloc, free from libc.string cimport memcpy, strdup, strcmp, strlen from numpy cimport (import_array, ndarray, PyArray_GETITEM, PyArray_SETITEM, \ npy_intp) from definitions cimport (hid_t, herr_t, hsize_t, htri_t, H5F_ACC_RDONLY, H5P_DEFAULT, H5D_CHUNKED, H5T_DIR_DEFAULT, H5F_SCOPE_LOCAL, H5F_SCOPE_GLOBAL, H5T_COMPOUND, H5Tget_order, H5Fflush, H5Dget_create_plist, H5T_ORDER_LE, H5D_layout_t, H5Dopen, H5Dclose, H5Dread, H5Dget_type, H5Dget_space, H5Pget_layout, H5Pget_chunk, H5Pclose, H5Sget_simple_extent_ndims, H5Sget_simple_extent_dims, H5Sclose, H5T_class_t, H5Tget_size, H5Tset_size, H5Tcreate, H5Tcopy, H5Tclose, H5Tget_nmembers, H5Tget_member_name, H5Tget_member_type, H5Tget_native_type, H5Tget_member_value, H5Tinsert, H5Tget_class, H5Tget_super, H5Tget_offset, H5T_cset_t, H5T_CSET_ASCII, H5T_CSET_UTF8, H5ATTRset_attribute_string, H5ATTRset_attribute, get_len_of_range, get_order, set_order, is_complex, conv_float64_timeval32, truncate_dset) from lrucacheextension cimport ObjectCache, NumCache from tables._past import previous_api, previous_api_property #----------------------------------------------------------------- # Optimized HDF5 API for PyTables cdef extern from "H5TB-opt.h" nogil: herr_t H5TBOmake_table( char *table_title, hid_t loc_id, char *dset_name, char *version, char *class_, hid_t mem_type_id, hsize_t nrecords, hsize_t chunk_size, void *fill_data, int compress, char *complib, int shuffle, int fletcher32, void *data ) herr_t H5TBOread_records( hid_t dataset_id, hid_t mem_type_id, hsize_t start, hsize_t nrecords, void *data ) herr_t H5TBOread_elements( hid_t dataset_id, hid_t mem_type_id, hsize_t nrecords, void *coords, void *data ) herr_t H5TBOappend_records( hid_t dataset_id, hid_t mem_type_id, hsize_t nrecords, hsize_t nrecords_orig, void *data ) herr_t H5TBOwrite_records ( hid_t dataset_id, hid_t mem_type_id, hsize_t start, hsize_t nrecords, hsize_t step, void *data ) herr_t H5TBOwrite_elements( hid_t dataset_id, hid_t mem_type_id, hsize_t nrecords, void *coords, void *data ) herr_t H5TBOdelete_records( hid_t dataset_id, hid_t mem_type_id, hsize_t ntotal_records, size_t src_size, hsize_t start, hsize_t nrecords, hsize_t maxtuples ) #---------------------------------------------------------------------------- # Initialization code # The numpy API requires this function to be called before # using any numpy facilities in an extension module. import_array() #------------------------------------------------------------- # Private functions cdef get_nested_field_cache(recarray, fieldname, fieldcache): """Get the maybe nested field named `fieldname` from the `recarray`. The `fieldname` may be a simple field name or a nested field name with slah-separated components. It can also be an integer specifying the position of the field. """ try: field = fieldcache[fieldname] except KeyError: # Check whether fieldname is an integer and if so, get the field # straight from the recarray dictionary (it can't be anywhere else) if isinstance(fieldname, int): field = recarray[fieldname] else: field = get_nested_field(recarray, fieldname) fieldcache[fieldname] = field return field cdef join_path(object parent, object name): if parent == "": return name else: return parent + '/' + name # Public classes cdef class Table(Leaf): # instance variables cdef void *wbuf def _create_table(self, title, complib, obversion): cdef int offset cdef int ret cdef long buflen cdef hid_t oid cdef void *data cdef hsize_t nrows cdef bytes class_ cdef ndarray wdflts cdef void *fill_data cdef ndarray recarr cdef object name cdef bytes encoded_title, encoded_complib, encoded_obversion cdef char *ctitle = NULL cdef char *cobversion = NULL cdef bytes encoded_name cdef char fieldname[128] cdef int i cdef H5T_cset_t cset = H5T_CSET_ASCII encoded_title = title.encode('utf-8') encoded_complib = complib.encode('utf-8') encoded_obversion = obversion.encode('utf-8') encoded_name = self.name.encode('utf-8') # Get the C pointer ctitle = encoded_title cobversion = encoded_obversion # Compute the complete compound datatype based on the table description self.disk_type_id = create_nested_type(self.description, self.byteorder) #self.type_id = H5Tcopy(self.disk_type_id) # A H5Tcopy only is not enough, as we want the in-memory type to be # in the byteorder of the machine (sys.byteorder). self.type_id = create_nested_type(self.description, sys.byteorder) # The fill values area wdflts = self._v_wdflts if wdflts is None: fill_data = NULL else: fill_data = wdflts.data # test if there is data to be saved initially if self._v_recarray is not None: recarr = self._v_recarray data = recarr.data else: data = NULL class_ = self._c_classid.encode('utf-8') self.dataset_id = H5TBOmake_table(ctitle, self.parent_id, encoded_name, cobversion, class_, self.disk_type_id, self.nrows, self.chunkshape[0], fill_data, self.filters.complevel, encoded_complib, self.filters.shuffle, self.filters.fletcher32, data) if self.dataset_id < 0: raise HDF5ExtError("Problems creating the table") if self._v_file.params['PYTABLES_SYS_ATTRS']: if PY_MAJOR_VERSION > 2: cset = H5T_CSET_UTF8 # Set the conforming table attributes # Attach the CLASS attribute ret = H5ATTRset_attribute_string(self.dataset_id, "CLASS", class_, len(class_), cset) if ret < 0: raise HDF5ExtError("Can't set attribute '%s' in table:\n %s." % ("CLASS", self.name)) # Attach the VERSION attribute ret = H5ATTRset_attribute_string(self.dataset_id, "VERSION", cobversion, len(encoded_obversion), cset) if ret < 0: raise HDF5ExtError("Can't set attribute '%s' in table:\n %s." % ("VERSION", self.name)) # Attach the TITLE attribute ret = H5ATTRset_attribute_string(self.dataset_id, "TITLE", ctitle, len(encoded_title), cset) if ret < 0: raise HDF5ExtError("Can't set attribute '%s' in table:\n %s." % ("TITLE", self.name)) # Attach the NROWS attribute nrows = self.nrows ret = H5ATTRset_attribute(self.dataset_id, "NROWS", H5T_STD_I64, 0, NULL, &nrows) if ret < 0: raise HDF5ExtError("Can't set attribute '%s' in table:\n %s." % ("NROWS", self.name)) # Attach the FIELD_N_NAME attributes # We write only the first level names for i, name in enumerate(self.description._v_names): snprintf(fieldname, 128, "FIELD_%d_NAME", i) encoded_name = name.encode('utf-8') ret = H5ATTRset_attribute_string(self.dataset_id, fieldname, encoded_name, len(encoded_name), cset) if ret < 0: raise HDF5ExtError("Can't set attribute '%s' in table:\n %s." % (fieldname, self.name)) # If created in PyTables, the table is always chunked self._chunked = True # Accessible from python # Finally, return the object identifier. return self.dataset_id cdef get_nested_type(self, hid_t type_id, hid_t native_type_id, object colpath, object field_byteorders): """Open a nested type and return a nested dictionary as description.""" cdef hid_t member_type_id, native_member_type_id cdef hsize_t nfields cdef hsize_t dims[1] cdef size_t itemsize cdef int i cdef char *c_colname cdef H5T_class_t class_id cdef char c_byteorder2[11] # "irrelevant" fits easily here cdef char *sys_byteorder cdef object desc, colobj, colpath2, typeclassname, typeclass cdef object byteorder cdef str colname, byteorder2 offset = 0 desc = {} # Get the number of members nfields = H5Tget_nmembers(type_id) # Iterate thru the members for i from 0 <= i < nfields: # Get the member name c_colname = H5Tget_member_name(type_id, i) colname = cstr_to_pystr(c_colname) # Get the member type member_type_id = H5Tget_member_type(type_id, i) # Get the HDF5 class class_id = H5Tget_class(member_type_id) if class_id == H5T_COMPOUND and not is_complex(member_type_id): colpath2 = join_path(colpath, colname) # Create the native data in-memory (without gaps!) itemsize = H5Tget_size(member_type_id) native_member_type_id = H5Tcreate(H5T_COMPOUND, itemsize) desc[colname], itemsize = self.get_nested_type( member_type_id, native_member_type_id, colpath2, field_byteorders) desc[colname]["_v_pos"] = i # Remember the position else: # Get the member format and the corresponding Col object try: native_member_type_id = get_native_type(member_type_id) atom = atom_from_hdf5_type(native_member_type_id) colobj = Col.from_atom(atom, pos=i) itemsize = H5Tget_size(native_member_type_id) except TypeError, te: # Re-raise TypeError again with more info raise TypeError( ("table ``%s``, column ``%s``: %%s" % (self.name, colname)) % te.args[0]) desc[colname] = colobj # For time kinds, save the byteorder of the column # (useful for conversion of time datatypes later on) if colobj.kind == "time": colobj._byteorder = H5Tget_order(member_type_id) if colobj._byteorder == H5T_ORDER_LE: field_byteorders.append("little") else: field_byteorders.append("big") elif colobj.kind in ['int', 'uint', 'float', 'complex', 'enum']: # Keep track of the byteorder for this column get_order(member_type_id, c_byteorder2) byteorder2 = cstr_to_pystr(c_byteorder2) if byteorder2 in ["little", "big"]: field_byteorders.append(byteorder2) # Insert the native member H5Tinsert(native_type_id, c_colname, offset, native_member_type_id) # Update the offset offset = offset + itemsize # Release resources H5Tclose(native_member_type_id) H5Tclose(member_type_id) free(c_colname) # set the byteorder and other things (just in top level) if colpath == "": # Compute a byteorder for the entire table if len(field_byteorders) > 0: field_byteorders = numpy.array(field_byteorders) # Cython doesn't interpret well the extended comparison # operators so this: field_byteorders == "little" doesn't work # as expected if numpy.alltrue(field_byteorders.__eq__("little")): byteorder = "little" elif numpy.alltrue(field_byteorders.__eq__("big")): byteorder = "big" else: # Yes! someone has done it! byteorder = "mixed" else: byteorder = "irrelevant" self.byteorder = byteorder # Correct the type size in case the memory type size is less # than the type in-disk (probably due to reading native HDF5 # files written with tools allowing field padding) # Solves bug #23 if H5Tget_size(native_type_id) > offset: H5Tset_size(native_type_id, offset) return desc, offset def _get_info(self): """Get info from a table on disk.""" cdef hid_t space_id, plist cdef size_t type_size, size2 cdef hsize_t dims[1] # enough for unidimensional tables cdef hsize_t chunksize[1] cdef H5D_layout_t layout cdef bytes encoded_name encoded_name = self.name.encode('utf-8') # Open the dataset self.dataset_id = H5Dopen(self.parent_id, encoded_name, H5P_DEFAULT) if self.dataset_id < 0: raise HDF5ExtError("Non-existing node ``%s`` under ``%s``" % (self.name, self._v_parent._v_pathname)) # Get the datatype on disk self.disk_type_id = H5Dget_type(self.dataset_id) if H5Tget_class(self.disk_type_id) != H5T_COMPOUND: raise ValueError("Node ``%s`` is not a Table object" % (self._v_parent._v_leaves[self.name]._v_pathname)) # Get the number of rows space_id = H5Dget_space(self.dataset_id) H5Sget_simple_extent_dims(space_id, dims, NULL) self.nrows = SizeType(dims[0]) # Free resources H5Sclose(space_id) # Get the layout of the datatype plist = H5Dget_create_plist(self.dataset_id) layout = H5Pget_layout(plist) if layout == H5D_CHUNKED: self._chunked = 1 # Get the chunksize H5Pget_chunk(plist, 1, chunksize) else: self._chunked = 0 chunksize[0] = 0 H5Pclose(plist) # Get the type size type_size = H5Tget_size(self.disk_type_id) # Create the native data in-memory self.type_id = H5Tcreate(H5T_COMPOUND, type_size) # Fill-up the (nested) native type (removing the gaps!) and description desc, _ = self.get_nested_type(self.disk_type_id, self.type_id, "", []) if desc == {}: raise HDF5ExtError("Problems getting desciption for table %s", self.name) # Return the object ID and the description return (self.dataset_id, desc, SizeType(chunksize[0])) cdef _convert_time64_(self, ndarray nparr, hsize_t nrecords, int sense): """Converts a NumPy of Time64 elements between NumPy and HDF5 formats. NumPy to HDF5 conversion is performed when 'sense' is 0. Otherwise, HDF5 to NumPy conversion is performed. The conversion is done in place, i.e. 'nparr' is modified. """ cdef void *t64buf cdef long byteoffset, bytestride, nelements byteoffset = 0 # NumPy objects doesn't have an offset bytestride = nparr.strides[0] # supports multi-dimensional recarray # Compute the number of elements in the multidimensional cell nelements = nparr.size // len(nparr) t64buf = nparr.data conv_float64_timeval32( t64buf, byteoffset, bytestride, nrecords, nelements, sense) cpdef _convert_types(self, ndarray recarr, hsize_t nrecords, int sense): """Converts columns in 'recarr' between NumPy and HDF5 formats. NumPy to HDF5 conversion is performed when 'sense' is 0. Otherwise, HDF5 to NumPy conversion is performed. The conversion is done in place, i.e. 'recarr' is modified. """ # For reading, first swap the byteorder by hand # (this is not currently supported by HDF5) if sense == 1: for colpathname in self.colpathnames: if self.coltypes[colpathname] in ["time32", "time64"]: colobj = self.coldescrs[colpathname] if hasattr(colobj, "_byteorder"): if colobj._byteorder != platform_byteorder: column = get_nested_field(recarr, colpathname) # Do an *inplace* byteswapping column.byteswap(True) # This should be generalised to support other type conversions. for t64cname in self._time64colnames: column = get_nested_field(recarr, t64cname) self._convert_time64_(column, nrecords, sense) def _open_append(self, ndarray recarr): self._v_recarray = recarr # Get the pointer to the buffer data area self.wbuf = recarr.data def _append_records(self, int nrecords): cdef int ret cdef hsize_t nrows # Convert some NumPy types to HDF5 before storing. self._convert_types(self._v_recarray, nrecords, 0) nrows = self.nrows # release GIL (allow other threads to use the Python interpreter) with nogil: # Append the records: ret = H5TBOappend_records(self.dataset_id, self.type_id, nrecords, nrows, self.wbuf) if ret < 0: raise HDF5ExtError("Problems appending the records.") self.nrows = self.nrows + nrecords def _close_append(self): cdef hsize_t nrows if self._v_file.params['PYTABLES_SYS_ATTRS']: # Update the NROWS attribute nrows = self.nrows if (H5ATTRset_attribute(self.dataset_id, "NROWS", H5T_STD_I64, 0, NULL, &nrows) < 0): raise HDF5ExtError("Problems setting the NROWS attribute.") # Set the caches to dirty (in fact, and for the append case, # it should be only the caches based on limits, but anyway) self._dirtycache = True # Delete the reference to recarray as we doesn't need it anymore self._v_recarray = None def _update_records(self, hsize_t start, hsize_t stop, hsize_t step, ndarray recarr): cdef herr_t ret cdef void *rbuf cdef hsize_t nrecords, nrows # Get the pointer to the buffer data area rbuf = recarr.data # Compute the number of records to update nrecords = len(recarr) nrows = get_len_of_range(start, stop, step) if nrecords > nrows: nrecords = nrows # Convert some NumPy types to HDF5 before storing. self._convert_types(recarr, nrecords, 0) # Update the records: with nogil: ret = H5TBOwrite_records(self.dataset_id, self.type_id, start, nrecords, step, rbuf ) if ret < 0: raise HDF5ExtError("Problems updating the records.") # Set the caches to dirty self._dirtycache = True def _update_elements(self, hsize_t nrecords, ndarray coords, ndarray recarr): cdef herr_t ret cdef void *rbuf cdef void *rcoords # Get the chunk of the coords that correspond to a buffer rcoords = coords.data # Get the pointer to the buffer data area rbuf = recarr.data # Convert some NumPy types to HDF5 before storing. self._convert_types(recarr, nrecords, 0) # Update the records: with nogil: ret = H5TBOwrite_elements(self.dataset_id, self.type_id, nrecords, rcoords, rbuf) if ret < 0: raise HDF5ExtError("Problems updating the records.") # Set the caches to dirty self._dirtycache = True def _read_records(self, hsize_t start, hsize_t nrecords, ndarray recarr): cdef void *rbuf cdef int ret # Correct the number of records to read, if needed if (start + nrecords) > self.nrows: nrecords = self.nrows - start # Get the pointer to the buffer data area rbuf = recarr.data # Read the records from disk with nogil: ret = H5TBOread_records(self.dataset_id, self.type_id, start, nrecords, rbuf) if ret < 0: raise HDF5ExtError("Problems reading records.") # Convert some HDF5 types to NumPy after reading. self._convert_types(recarr, nrecords, 1) return nrecords cdef hsize_t _read_chunk(self, hsize_t nchunk, ndarray iobuf, long cstart): cdef long nslot cdef hsize_t start, nrecords, chunkshape cdef int ret cdef void *rbuf cdef NumCache chunkcache chunkcache = self._chunkcache chunkshape = chunkcache.slotsize # Correct the number of records to read, if needed start = nchunk*chunkshape nrecords = chunkshape if (start + nrecords) > self.nrows: nrecords = self.nrows - start rbuf = iobuf.data + cstart * chunkcache.itemsize # Try to see if the chunk is in cache nslot = chunkcache.getslot_(nchunk) if nslot >= 0: chunkcache.getitem_(nslot, rbuf, 0) else: # Chunk is not in cache. Read it and put it in the LRU cache. with nogil: ret = H5TBOread_records(self.dataset_id, self.type_id, start, nrecords, rbuf) if ret < 0: raise HDF5ExtError("Problems reading chunk records.") nslot = chunkcache.setitem_(nchunk, rbuf, 0) return nrecords def _read_elements(self, ndarray coords, ndarray recarr): cdef long nrecords cdef void *rbuf cdef void *rbuf2 cdef int ret # Get the chunk of the coords that correspond to a buffer nrecords = coords.size # Get the pointer to the buffer data area rbuf = recarr.data # Get the pointer to the buffer coords area rbuf2 = coords.data with nogil: ret = H5TBOread_elements(self.dataset_id, self.type_id, nrecords, rbuf2, rbuf) if ret < 0: raise HDF5ExtError("Problems reading records.") # Convert some HDF5 types to NumPy after reading. self._convert_types(recarr, nrecords, 1) return nrecords def _remove_rows(self, hsize_t start, hsize_t stop, long step): cdef size_t rowsize cdef hsize_t nrecords, nrecords2 cdef hsize_t i if step == 1: nrecords = stop - start rowsize = self.rowsize # Using self.disk_type_id should be faster (i.e. less conversions) if (H5TBOdelete_records(self.dataset_id, self.disk_type_id, self.nrows, rowsize, start, nrecords, self.nrowsinbuf) < 0): raise HDF5ExtError("Problems deleting records.") self.nrows = self.nrows - nrecords if self._v_file.params['PYTABLES_SYS_ATTRS']: # Attach the NROWS attribute nrecords2 = self.nrows H5ATTRset_attribute(self.dataset_id, "NROWS", H5T_STD_I64, 0, NULL, &nrecords2) # Set the caches to dirty self._dirtycache = True # Return the number of records removed return nrecords elif step == -1: self._remove_rows(self, stop+1, start+1, 1) elif step >= 1: # always want to go through the space backwards for i in range(stop - step, start - step, -step): self._remove_rows(self, i, i+1, 1) elif step <= -1: # always want to go through the space backwards for i in range(start, stop, step): self._remove_rows(self, i, i+1, 1) else: raise ValueError("step size may not be 0.") cdef class Row: """Table row iterator and field accessor. Instances of this class are used to fetch and set the values of individual table fields. It works very much like a dictionary, where keys are the pathnames or positions (extended slicing is supported) of the fields in the associated table in a specific row. This class provides an *iterator interface* so that you can use the same Row instance to access successive table rows one after the other. There are also some important methods that are useful for accessing, adding and modifying values in tables. .. rubric:: Row attributes .. attribute:: nrow The current row number. This property is useful for knowing which row is being dealt with in the middle of a loop or iterator. """ cdef long _row, _unsaved_nrows, _mod_nrows cdef hsize_t start, absstep cdef long long stop, step, nextelement, _nrow, stopb # has to be long long, not hsize_t, for negative step sizes cdef hsize_t nrowsinbuf, nrows, nrowsread cdef hsize_t chunksize, nchunksinbuf, totalchunks cdef hsize_t startb, lenbuf cdef long long indexchunk cdef int bufcounter, counter cdef int exist_enum_cols cdef int _riterator, _stride, _rowsize cdef int wherecond, indexed cdef int ro_filemode, chunked cdef int _bufferinfo_done, sss_on cdef int iterseq_max_elements cdef ndarray bufcoords, indexvalid, indexvalues, chunkmap cdef hsize_t *bufcoords_data cdef hsize_t *index_values_data cdef char *chunkmap_data cdef char *index_valid_data cdef object dtype cdef object iobuf, iobufcpy cdef object wrec, wreccpy cdef object wfields, rfields cdef object coords cdef object condfunc, condargs cdef object mod_elements, colenums cdef object rfieldscache, wfieldscache cdef object _table_file, _table_path cdef object modified_fields cdef object seq_available # Deprecated API indexChunk = previous_api_property('indexchunk') indexValid = previous_api_property('indexvalid') indexValues = previous_api_property('indexvalues') bufcoordsData = previous_api_property('bufcoords_data') indexValuesData = previous_api_property('index_values_data') chunkmapData = previous_api_property('chunkmap_data') indexValidData = previous_api_property('index_valid_data') whereCond = previous_api_property('wherecond') iterseqMaxElements = previous_api_property('iterseq_max_elements') IObuf = previous_api_property('iobuf') IObufcpy = previous_api_property('iobufcpy') # The nrow() method has been converted into a property, which is handier property nrow: """The current row number. This property is useful for knowing which row is being dealt with in the middle of a loop or iterator. """ def __get__(self): return SizeType(self._nrow) property table: def __get__(self): return self._table_file._get_node(self._table_path) def __cinit__(self, table): cdef int nfields, i # Location-dependent information. self._table_file = table._v_file self._table_path = table._v_pathname self._unsaved_nrows = 0 self._mod_nrows = 0 self._row = 0 self._nrow = 0 # Useful in mod_append read iterators self._riterator = 0 self._bufferinfo_done = 0 # Some variables from table will be cached here if table._v_file.mode == 'r': self.ro_filemode = 1 else: self.ro_filemode = 0 self.chunked = table._chunked self.colenums = table._colenums self.exist_enum_cols = len(self.colenums) self.nrowsinbuf = table.nrowsinbuf self.chunksize = table.chunkshape[0] self.nchunksinbuf = self.nrowsinbuf / self.chunksize self.dtype = table._v_dtype self._new_buffer(table) self.mod_elements = None self.rfieldscache = {} self.wfieldscache = {} self.modified_fields = set() def _iter(self, start=0, stop=0, step=1, coords=None, chunkmap=None): """Return an iterator for traversiong the data in table.""" self._init_loop(start, stop, step, coords, chunkmap) return iter(self) def __iter__(self): """Iterator that traverses all the data in the Table""" return self cdef _new_buffer(self, table): """Create the recarrays for I/O buffering""" wdflts = table._v_wdflts if wdflts is None: self.wrec = numpy.zeros(1, dtype=self.dtype) # Defaults are zero else: self.wrec = table._v_wdflts.copy() self.wreccpy = self.wrec.copy() # A copy of the defaults # Build the wfields dictionary for faster access to columns self.wfields = {} for name in self.dtype.names: self.wfields[name] = self.wrec[name] # Get the read buffer for this instance (it is private, remember!) buff = self.iobuf = table._get_container(self.nrowsinbuf) # Build the rfields dictionary for faster access to columns # This is quite fast, as it only takes around 5 us per column # in my laptop (Pentium 4 @ 2 GHz). # F. Alted 2006-08-18 self.rfields = {} for i, name in enumerate(self.dtype.names): self.rfields[i] = buff[name] self.rfields[name] = buff[name] # Get the stride of these buffers self._stride = buff.strides[0] # The rowsize self._rowsize = self.dtype.itemsize self.nrows = table.nrows # This value may change cdef _init_loop(self, hsize_t start, long long stop, long long step, object coords, object chunkmap): """Initialization for the __iter__ iterator""" table = self.table self._riterator = 1 # We are inside a read iterator self.start = start self.stop = stop self.step = step self.coords = coords self.startb = 0 if step > 0: self._row = -1 # a sentinel self.nrowsread = start elif step < 0: self._row = 0 self.nrowsread = 0 self.nextelement = start self._nrow = start - self.step self.wherecond = 0 self.indexed = 0 self.nrows = table.nrows # Update the row counter if coords is not None and 0 < step: self.nrowsread = start self.nextelement = start self.stop = min(stop, len(coords)) self.absstep = abs(step) return elif coords is not None and 0 > step: #self.nrowsread = 0 #self.nextelement = start #self.stop = min(stop, len(coords)) #self.stop = max(stop, start - len(coords)) self.absstep = abs(step) return if table._where_condition: self.wherecond = 1 self.condfunc, self.condargs = table._where_condition table._where_condition = None if table._use_index: self.indexed = 1 # Compute totalchunks here because self.nrows can change during the # life of a Row instance. self.totalchunks = self.nrows / self.chunksize if self.nrows % self.chunksize: self.totalchunks = self.totalchunks + 1 self.nrowsread = 0 self.nextelement = 0 self.chunkmap = chunkmap self.chunkmap_data = self.chunkmap.data table._use_index = False self.lenbuf = self.nrowsinbuf # Check if we have limitations on start, stop, step self.sss_on = (self.start > 0 or self.stop < self.nrows or self.step > 1) self.iterseq_max_elements = table._v_file.params['ITERSEQ_MAX_ELEMENTS'] self.seq_available = True def __next__(self): """next() method for __iter__() that is called on each iteration""" if not self._riterator: # The iterator is already exhausted! raise StopIteration if self.indexed: return self.__next__indexed() elif self.coords is not None: return self.__next__coords() elif self.wherecond: return self.__next__inkernel() else: return self.__next__general() cdef __next__indexed(self): """The version of next() for indexed columns and a chunkmap.""" cdef long recout, j, cs, vlen, rowsize cdef hsize_t nchunksread cdef object tmp_range cdef Table table cdef ndarray iobuf cdef void *IObufData cdef long nslot cdef object seq cdef ObjectCache seqcache assert self.nrowsinbuf >= self.chunksize while self.nextelement < self.stop: if self.nextelement >= self.nrowsread: # Skip until there is interesting information while self.start > self.nrowsread + self.nrowsinbuf: self.nrowsread = self.nrowsread + self.nrowsinbuf self.nextelement = self.nextelement + self.nrowsinbuf table = self.table iobuf = self.iobuf j = 0; recout = 0; cs = self.chunksize nchunksread = self.nrowsread / cs tmp_range = numpy.arange(0, cs, dtype='int64') self.bufcoords = numpy.empty(self.nrowsinbuf, dtype='int64') # Fetch valid chunks until the I/O buffer is full while nchunksread < self.totalchunks: if self.chunkmap_data[nchunksread]: self.bufcoords[j*cs:(j+1)*cs] = tmp_range + self.nrowsread # Not optimized read # recout = recout + table._read_records( # nchunksread*cs, cs, iobuf[j*cs:]) # # Optimized read through the use of a chunk cache. This cache has # more or less the same speed than the integrated HDF5 chunk # cache, but using the PyTables one has the advantage that the # user can easily change this parameter. recout = recout + table._read_chunk(nchunksread, iobuf, j*cs) j = j + 1 self.nrowsread = (nchunksread+1)*cs if self.nrowsread > self.stop: self.nrowsread = self.stop break elif j == self.nchunksinbuf: break nchunksread = nchunksread + 1 # Evaluate the condition on this table fragment. iobuf = iobuf[:recout] self.table._convert_types(iobuf, len(iobuf), 1) self.indexvalid = call_on_recarr( self.condfunc, self.condargs, iobuf) self.index_valid_data = self.indexvalid.data # Get the valid coordinates self.indexvalues = self.bufcoords[:recout][self.indexvalid] self.index_values_data = self.indexvalues.data self.lenbuf = self.indexvalues.size # Place the valid results at the beginning of the buffer iobuf[:self.lenbuf] = iobuf[self.indexvalid] # Initialize the internal buffer row counter self._row = -1 # Feed the indexvalues into the seqcache seqcache = table._seqcache nslot = table._nslotseq # See if we have a buffer available to place results if nslot >= 0 and self.seq_available: seq = seqcache.getitem_(nslot) if self.lenbuf + len(seq) < self.iterseq_max_elements: seq.extend(self.indexvalues) # Update the size of sequence in cache # Each element in indexvalues should take at least 8 bytes seqcache.rsizes[nslot] = len(seq) * 8 else: seqcache.removeslot_(nslot) self.seq_available = False self._row = self._row + 1 # Check whether we have read all the rows in buf if self._row == self.lenbuf: self.nextelement = self.nrowsread # Make _row to point to the last valid entry in buffer # (this is useful for accessing the last row after an iterator loop) self._row = self._row - 1 continue self._nrow = self.index_values_data[self._row] # Check additional conditions on start, stop, step params if self.sss_on: if (self._nrow < self.start or self._nrow >= self.stop): self.nextelement = self.nextelement + 1 continue if (self.step > 1 and ((self._nrow - self.start) % self.step > 0)): self.nextelement = self.nextelement + 1 continue # Return this row self.nextelement = self._nrow + 1 return self else: # All the elements have been read for this mode self._finish_riterator() cdef __next__coords(self): """The version of next() for user-required coordinates""" cdef int recout cdef long long lenbuf, nextelement cdef object tmp if 0 < self.step: while self.nextelement < self.stop: if self.nextelement >= self.nrowsread: # Correction for avoiding reading past self.stop if self.nrowsread+self.nrowsinbuf > self.stop: lenbuf = self.stop-self.nrowsread else: lenbuf = self.nrowsinbuf tmp = self.coords[self.nrowsread:self.nrowsread+lenbuf:self.step] # We have to get a contiguous buffer, so numpy.array is the way to go self.bufcoords = numpy.array(tmp, dtype="uint64") self._row = -1 if self.bufcoords.size > 0: recout = self.table._read_elements(self.bufcoords, self.iobuf) else: recout = 0 self.bufcoords_data = self.bufcoords.data self.nrowsread = self.nrowsread + lenbuf if recout == 0: # no items were read, skip out continue self._row = self._row + 1 self._nrow = self.bufcoords_data[self._row] self.nextelement = self.nextelement + self.absstep return self else: # All the elements have been read for this mode self._finish_riterator() elif 0 > self.step: #print("self.nextelement = ", self.nextelement, self.start, self.nrowsread, self.nextelement < self.start - self.nrowsread + 1) while self.nextelement - 1 > self.stop: if self.nextelement < self.start - ( self.nrowsread) + 1: if 0 > self.nextelement - ( self.nrowsinbuf) + 1: tmp = self.coords[0:self.nextelement + 1] else: tmp = self.coords[self.nextelement - ( self.nrowsinbuf) + 1:self.nextelement + 1] self.bufcoords = numpy.array(tmp, dtype="uint64") recout = self.table._read_elements(self.bufcoords, self.iobuf) self.bufcoords_data = self.bufcoords.data self.nrowsread = self.nrowsread + self.nrowsinbuf self._row = len(self.bufcoords) - 1 else: self._row = (self._row + self.step) % len(self.bufcoords) self._nrow = self.nextelement - self.step self.nextelement = self.nextelement + self.step # Return this value return self else: # All the elements have been read for this mode self._finish_riterator() else: self._finish_riterator() cdef __next__inkernel(self): """The version of next() in case of in-kernel conditions""" cdef hsize_t recout, correct cdef object numexpr_locals, colvar, col self.nextelement = self._nrow + self.step while self.nextelement < self.stop: if self.nextelement >= self.nrowsread: # Skip until there is interesting information while self.nextelement >= self.nrowsread + self.nrowsinbuf: self.nrowsread = self.nrowsread + self.nrowsinbuf # Compute the end for this iteration self.stopb = self.stop - self.nrowsread if self.stopb > self.nrowsinbuf: self.stopb = self.nrowsinbuf self._row = self.startb - self.step # Read a chunk recout = self.table._read_records(self.nextelement, self.nrowsinbuf, self.iobuf) self.nrowsread = self.nrowsread + recout self.indexchunk = -self.step # Evaluate the condition on this table fragment. self.indexvalid = call_on_recarr( self.condfunc, self.condargs, self.iobuf[:recout] ) self.index_valid_data = self.indexvalid.data # Is there any interesting information in this buffer? if not numpy.sometrue(self.indexvalid): # No, so take the next one if self.step >= self.nrowsinbuf: self.nextelement = self.nextelement + self.step else: self.nextelement = self.nextelement + self.nrowsinbuf # Correction for step size > 1 if self.step > 1: correct = (self.nextelement - self.start) % self.step self.nextelement = self.nextelement - correct continue self._row = self._row + self.step self._nrow = self.nextelement if self._row + self.step >= self.stopb: # Compute the start row for the next buffer self.startb = 0 self.nextelement = self._nrow + self.step # Return only if this value is interesting self.indexchunk = self.indexchunk + self.step if self.index_valid_data[self.indexchunk]: return self else: self._finish_riterator() cdef __next__general(self): """The version of next() for the general cases""" cdef int recout if 0 < self.step: self.nextelement = self._nrow + self.step while self.nextelement < self.stop: if self.nextelement >= self.nrowsread: # Skip until there is interesting information while self.nextelement >= self.nrowsread + self.nrowsinbuf: self.nrowsread = self.nrowsread + self.nrowsinbuf # Compute the end for this iteration self.stopb = self.stop - self.nrowsread if self.stopb > self.nrowsinbuf: self.stopb = self.nrowsinbuf self._row = self.startb - self.step # Read a chunk recout = self.table._read_records(self.nrowsread, self.nrowsinbuf, self.iobuf) self.nrowsread = self.nrowsread + recout self._row = self._row + self.step self._nrow = self.nextelement if self._row + self.step >= self.stopb: # Compute the start row for the next buffer self.startb = (self._row + self.step) % self.nrowsinbuf self.nextelement = self._nrow + self.step # Return this value return self else: self._finish_riterator() elif 0 > self.step: self.stopb = -1 while self.nextelement - 1 > self.stop: if self.nextelement < self.start - self.nrowsread + 1: # Read a chunk recout = self.table._read_records(self.nextelement - self.nrowsinbuf + 1, self.nrowsinbuf, self.iobuf) self.nrowsread = self.nrowsread + self.nrowsinbuf self._row = self.nrowsinbuf - 1 else: self._row = (self._row + self.step) % self.nrowsinbuf self._nrow = self.nextelement - self.step self.nextelement = self.nextelement + self.step # Return this value return self else: self._finish_riterator() cdef _finish_riterator(self): """Clean-up things after iterator has been done""" self.rfieldscache = {} # empty rfields cache self.wfieldscache = {} # empty wfields cache # Make a copy of the last read row in the private record # (this is useful for accessing the last row after an iterator loop) if self._row >= 0: self.wrec[:] = self.iobuf[self._row] self._riterator = 0 # out of iterator if self._mod_nrows > 0: # Check if there is some modified row self._flush_mod_rows() # Flush any possible modified row self.modified_fields = set() # Empty the set of modified fields raise StopIteration # end of iteration def _fill_col(self, result, start, stop, step, field): """Read a field from a table on disk and put the result in result""" cdef hsize_t startr, istartb cdef hsize_t istart, inrowsinbuf, inextelement cdef long long stopr, istopb, i, j, inrowsread cdef long long istop, istep cdef object fields # We can't reuse existing buffers in this context self._init_loop(start, stop, step, None, None) istart, istop, istep = (self.start, self.stop, self.step) inrowsinbuf, inextelement, inrowsread = (self.nrowsinbuf, istart, istart) istartb, startr = (self.startb, 0) i = istart if 0 < istep: while i < istop: if (inextelement >= inrowsread + inrowsinbuf): inrowsread = inrowsread + inrowsinbuf i = i + inrowsinbuf continue # Compute the end for this iteration istopb = istop - inrowsread if istopb > inrowsinbuf: istopb = inrowsinbuf stopr = startr + ((istopb - istartb - 1) / istep) + 1 # Read a chunk inrowsread = inrowsread + self.table._read_records(i, inrowsinbuf, self.iobuf) # Assign the correct part to result fields = self.iobuf if field: fields = get_nested_field(fields, field) result[startr:stopr] = fields[istartb:istopb:istep] # Compute some indexes for the next iteration startr = stopr j = istartb + ((istopb - istartb - 1) / istep) * istep istartb = (j+istep) % inrowsinbuf inextelement = inextelement + istep i = i + inrowsinbuf elif 0 > istep: inrowsinbuf = self.nrowsinbuf #istartb = self.startb istartb = self.nrowsinbuf - 1 #istopb = self.stopb - 1 istopb = -1 startr = 0 i = istart inextelement = istart inrowsread = 0 while i-1 > istop: #if (inextelement <= inrowsread + inrowsinbuf): if (inextelement < i - inrowsinbuf): inrowsread = inrowsread + inrowsinbuf i = i - inrowsinbuf continue # Compute the end for this iteration stopr = startr + ((istopb - istartb - 1) / istep) # Read a chunk inrowsread = inrowsread + self.table._read_records(i - inrowsinbuf + 1, inrowsinbuf, self.iobuf) # Assign the correct part to result fields = self.iobuf if field: fields = get_nested_field(fields, field) if istopb >= 0: result[startr:stopr] = fields[istartb:istopb:istep] else: result[startr:stopr] = fields[istartb::istep] # Compute some indexes for the next iteration startr = stopr istartb = (i - istartb)%inrowsinbuf inextelement = inextelement + istep i = i - inrowsinbuf self._riterator = 0 # out of iterator return _fillCol = previous_api(_fill_col) def append(self): """Add a new row of data to the end of the dataset. Once you have filled the proper fields for the current row, calling this method actually appends the new data to the *output buffer* (which will eventually be dumped to disk). If you have not set the value of a field, the default value of the column will be used. .. warning:: After completion of the loop in which :meth:`Row.append` has been called, it is always convenient to make a call to :meth:`Table.flush` in order to avoid losing the last rows that may still remain in internal buffers. Examples -------- :: row = table.row for i in xrange(nrows): row['col1'] = i-1 row['col2'] = 'a' row['col3'] = -1.0 row.append() table.flush() """ cdef ndarray iobuf, wrec, wreccpy if self.ro_filemode: raise IOError("Attempt to write over a file opened in read-only mode") if not self.chunked: raise HDF5ExtError("You cannot append rows to a non-chunked table.", h5tb=False) if self._riterator: raise NotImplementedError("You cannot append rows when in middle of a table iterator. If what you want is to update records, use Row.update() instead.") # Commit the private record into the write buffer # self.iobuf[self._unsaved_nrows] = self.wrec # The next is faster iobuf = self.iobuf; wrec = self.wrec memcpy(iobuf.data + self._unsaved_nrows * self._stride, wrec.data, self._rowsize) # Restore the defaults for the private record # self.wrec[:] = self.wreccpy # The next is faster wreccpy = self.wreccpy memcpy(wrec.data, wreccpy.data, self._rowsize) self._unsaved_nrows = self._unsaved_nrows + 1 # When the buffer is full, flush it if self._unsaved_nrows == self.nrowsinbuf: self._flush_buffered_rows() def _flush_buffered_rows(self): if self._unsaved_nrows > 0: self.table._save_buffered_rows(self.iobuf, self._unsaved_nrows) # Reset the buffer unsaved counter self._unsaved_nrows = 0 _flushBufferedRows = previous_api(_flush_buffered_rows) def _get_unsaved_nrows(self): return self._unsaved_nrows _getUnsavedNrows = previous_api(_get_unsaved_nrows) def update(self): """Change the data of the current row in the dataset. This method allows you to modify values in a table when you are in the middle of a table iterator like :meth:`Table.iterrows` or :meth:`Table.where`. Once you have filled the proper fields for the current row, calling this method actually changes data in the *output buffer* (which will eventually be dumped to disk). If you have not set the value of a field, its original value will be used. .. warning:: After completion of the loop in which :meth:`Row.update` has been called, it is always convenient to make a call to :meth:`Table.flush` in order to avoid losing changed rows that may still remain in internal buffers. Examples -------- :: for row in table.iterrows(step=10): row['col1'] = row.nrow row['col2'] = 'b' row['col3'] = 0.0 row.update() table.flush() which modifies every tenth row in table. Or:: for row in table.where('col1 > 3'): row['col1'] = row.nrow row['col2'] = 'b' row['col3'] = 0.0 row.update() table.flush() which just updates the rows with values bigger than 3 in the first column. """ cdef ndarray iobufcpy, iobuf if self.ro_filemode: raise IOError("Attempt to write over a file opened in read-only mode") if not self._riterator: raise NotImplementedError("You are only allowed to update rows through the Row.update() method if you are in the middle of a table iterator.") if self.mod_elements is None: # Initialize an array for keeping the modified elements # (just in case Row.update() would be used) self.mod_elements = numpy.empty(shape=self.nrowsinbuf, dtype=SizeType) # We need a different copy for self.iobuf here self.iobufcpy = self.iobuf.copy() # Add this row to the list of elements to be modified self.mod_elements[self._mod_nrows] = self._nrow # Copy the current buffer row in input to the output buffer # self.iobufcpy[self._mod_nrows] = self.iobuf[self._row] # The next is faster iobufcpy = self.iobufcpy; iobuf = self.iobuf memcpy(iobufcpy.data + self._mod_nrows * self._stride, iobuf.data + self._row * self._stride, self._rowsize) # Increase the modified buffer count by one self._mod_nrows = self._mod_nrows + 1 # When the buffer is full, flush it if self._mod_nrows == self.nrowsinbuf: self._flush_mod_rows() def _flush_mod_rows(self): """Flush any possible modified row using Row.update()""" table = self.table # Save the records on disk table._update_elements(self._mod_nrows, self.mod_elements, self.iobufcpy) # Reset the counter of modified rows to 0 self._mod_nrows = 0 # Mark the modified fields' indexes as dirty. table._mark_columns_as_dirty(self.modified_fields) _flushModRows = previous_api(_flush_mod_rows) def __contains__(self, item): """__contains__(item) A true value is returned if item is found in current row, false otherwise. """ return item in self.fetch_all_fields() # This method is twice as faster than __getattr__ because there is # not a lookup in the local dictionary def __getitem__(self, key): """__getitem__(key) Get the row field specified by the `key`. The key can be a string (the name of the field), an integer (the position of the field) or a slice (the range of field positions). When key is a slice, the returned value is a *tuple* containing the values of the specified fields. Examples -------- :: res = [row['var3'] for row in table.where('var2 < 20')] which selects the var3 field for all the rows that fulfil the condition. Or:: res = [row[4] for row in table if row[1] < 20] which selects the field in the *4th* position for all the rows that fulfil the condition. Or:: res = [row[:] for row in table if row['var2'] < 20] which selects the all the fields (in the form of a *tuple*) for all the rows that fulfil the condition. Or:: res = [row[1::2] for row in table.iterrows(2, 3000, 3)] which selects all the fields in even positions (in the form of a *tuple*) for all the rows in the slice [2:3000:3]. """ cdef long offset cdef ndarray field cdef object row, fields, fieldscache if self._riterator: # If in the middle of an iterator loop, the user probably wants to # access the read buffer fieldscache = self.rfieldscache; fields = self.rfields offset = self._row else: # We are not in an iterator loop, so the user probably wants to access # the write buffer fieldscache = self.wfieldscache; fields = self.wfields offset = 0 try: # Check whether this object is in the cache dictionary field = fieldscache[key] except (KeyError, TypeError): try: # Try to get it from fields (str or int keys) field = get_nested_field_cache(fields, key, fieldscache) except TypeError: # No luck yet. Still, the key can be a slice. # Fetch the complete row and convert it into a tuple if self._riterator: row = self.iobuf[self._row].copy().item() else: row = self.wrec[0].copy().item() # Try with __getitem__() return row[key] if field.ndim == 1: # For an scalar it is not needed a copy (immutable object) return PyArray_GETITEM(field, field.data + offset * self._stride) else: # Do a copy of the array, so that it can be overwritten by the user # without damaging the internal self.rfields buffer return field[offset].copy() # This is slightly faster (around 3%) than __setattr__ def __setitem__(self, object key, object value): """__setitem__(key, value) Set the key row field to the specified value. Differently from its __getitem__() counterpart, in this case key can only be a string (the name of the field). The changes done via __setitem__() will not take effect on the data on disk until any of the :meth:`Row.append` or :meth:`Row.update` methods are called. Examples -------- :: for row in table.iterrows(step=10): row['col1'] = row.nrow row['col2'] = 'b' row['col3'] = 0.0 row.update() table.flush() which modifies every tenth row in the table. """ cdef int ret cdef long offset cdef ndarray field cdef object fields, fieldscache if self.ro_filemode: raise IOError("attempt to write over a file opened in read-only mode") if self._riterator: # If in the middle of an iterator loop, or *after*, the user # probably wants to access the read buffer fieldscache = self.rfieldscache; fields = self.rfields offset = self._row else: # We are not in an iterator loop, so the user probably wants to access # the write buffer fieldscache = self.wfieldscache; fields = self.wfields offset = 0 # Check validity of enumerated value. if self.exist_enum_cols: if key in self.colenums: enum = self.colenums[key] for cenval in numpy.asarray(value).flat: enum(cenval) # raises ``ValueError`` on invalid values # Get the field to be modified field = get_nested_field_cache(fields, key, fieldscache) if key not in self.modified_fields: self.modified_fields.add(key) # Finally, try to set it to the value try: # Optimization for scalar values. This can optimize the writes # between a 10% and 100%, depending on the number of columns modified if field.ndim == 1: ret = PyArray_SETITEM(field, field.data + offset * self._stride, value) if ret < 0: raise TypeError ##### End of optimization for scalar values else: field[offset] = value except TypeError: raise TypeError("invalid type (%s) for column ``%s``" % (type(value), key)) def fetch_all_fields(self): """Retrieve all the fields in the current row. Contrarily to row[:] (see :ref:`RowSpecialMethods`), this returns row data as a NumPy void scalar. For instance:: [row.fetch_all_fields() for row in table.where('col1 < 3')] will select all the rows that fulfill the given condition as a list of NumPy records. """ # We need to do a cast for recognizing negative row numbers! if self._nrow < 0: return ("Warning: Row iterator has not been initialized for table:\n" " %s\n" " You will normally want to use this method in iterator " "contexts." % self.table) # Always return a copy of the row so that new data that is written # in self.iobuf doesn't overwrite the original returned data. return self.iobuf[self._row].copy() def __str__(self): """Represent the record as an string""" # We need to do a cast for recognizing negative row numbers! if self._nrow < 0: return ("Warning: Row iterator has not been initialized for table:\n" " %s\n" " You will normally want to use this object in iterator " "contexts." % self.table) tablepathname = self.table._v_pathname classname = self.__class__.__name__ return "%s.row (%s), pointing to row #%d" % (tablepathname, classname, self._nrow) def __repr__(self): """Represent the record as an string""" return str(self) ## Local Variables: ## mode: python ## py-indent-offset: 2 ## tab-width: 2 ## fill-column: 78 ## End: PyTables-v.3.1.1/tables/tests/000077500000000000000000000000001231437614300161235ustar00rootroot00000000000000PyTables-v.3.1.1/tables/tests/Table2_1_lzo_nrv2e_shuffle.h5000066400000000000000000000454061231437614300234370ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿKÿÿÿÿÿÿÿÿ €`HEAP€tuple0group0èTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà  ;Ȩvar1var2 var3 ?@4 4ÿdÿÿÿÿÿÿÿÿÈ<(SNOD€`(ÐHEAP0ð>TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ   ?€¨var1var2 var3 ?@4 4ÿdÿÿÿÿÿÿÿÿ @(SNOD` @  °HEAP0ÈBTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  øB€¨var1var2 var3 ?@4 4ÿdÿÿÿÿÿÿÿÿxD(SNOD@ è HEAPTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  FHTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿË+TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁË-TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅŒ0 àY@ 1úÀX;ý€½Ný@½ ý½þÀW=ý€½\ý@½(ý½.þÀVRý€½Oý@½ý½IþÀU<ý€½ ý@½Qý½þÀTœ'}€½ý@¼(ü½2þÀS'ý€½%ý@¼'½¼(ýRý€½Yý@¼'=½_þÀQ)ý€¼(ü½ý½SþÀPHý€¼'½@¼'}½/'½Oý½3þ€N6ý½Pþ€Mœ'ý½'}Lý¼'þ€Kœ'½½bþ€Jœ'= ½"'}I8ý½4þ€Hý¼'>€Gœ'ý¼(ýFD'ü½þ€E7ý¼(= D['|¼'~ €C '|¼'>€B'|¼'þ €Aœ'½ ½`þ€@@ý½'=?M'=>œ(½=œ'¾<œ'þ;œ(}:œ(½9+'}8 '=7œ(}6œ'þ5œ'¾ 4E'ý3'=2œ'> 1-=05'½.#'=,œ'>*œ(ý(œ'þ&'ý$U'="œ(} T'}œ'¾ œ(½^'½œ'~ œ(ýœ(¾ð?'è Ï  7àY@ ]úÀXý€½Oý@½\ý½>þÀW.ý€½`ý@½#ý½6þÀV^ý€½ý@½Qý½(þÀUý€½ ý@½ý½ þÀT/ý€½Vý@½ý¼'¾ÀS:ý€½ ý@-=->ÀRœ'½€½1'ü¼'½½þÀQ ý€½ý@½;ý½þÀPœ(ü½&ý@½ý½þ€ODý½+þ€N!ý½Wþ€Mý½Xþ€Lœ(ü½)'}Kœ'=¼'¾ €J"ý¼'¾€I_'|½'þ€HSý½Bþ€G0ý½Tþ€Fý½þ€Eý½,þ€Dœ'½¼(½Cœ(|½'ýBYý½Mþ€Aœ'=¼(ý@ý¼(=?'}>'==œ(ý<'};œ(}:œ'~9œ(ý85'ý7'=6œ'þ5-=4œ(= 3I'ý2@'=1œ'>0œ(}.œ'>,'ý*F'=(P'=&œ(ý$œ'þ"œ'> œ(ýœ(}œ(=œ(}œ(½<'=->ð?(h Î$ %ãY@ AúÀXaý€½Ký@½ý½,þÀW\ý€½Rý@-=¼(ýVYý€½Qý@½ý½IþÀUý€½Zý@½ ý½þÀT0ý€½bý@½ý½þÀSœ'½€½Wý@½?ý½DþÀRý€½_ý@½ý½]þÀQ1ý€½#ý@½2ý¼(ýPœ'}€½/ý@¼'}½=þ€Oý½'þ€Nœ(ü½V'}Mœ'½½`'}L ý½þ€K@ý¼(= Jœ'} ¼(}Iœ'ý¼'>€Hœ(ü½(þ €G-|¼(}FEý¼'>€Eœ'ý ½&þ€D3ý½-þ€C.ý½*þ€B7ý½cþ€A!ý½4þ€@œ(ü ¼û?'A>M'==œ'> <['};C'=:A'=9œ'>8L'}7œ'>6œ(=5œ'¾ 4'ý3œ(ý2œ(½1œ'>0œ(=.œ(},)'}*^'=('=&J'=$<'="œ': H'Aœ(}œ(= œ(½œ(½ œ'¾ œ(}ð(è'Ì Æ €` 0TITLETable Benchmark (CLASSGROUP (VERSION1.0 ˜FILTERSu(itables.Leaf Filters p1 (dp2 S'shuffle' p3 I0 sS'complevel' p4 I1 sS'fletcher32' p5 I0 sS'complib' p6 S'lzo' p7 sb. 8PYTABLES_FORMAT_VERSION1.2 0test2 just a test (1lzopª=#@ (CLASSTABLE (VERSION2.1 8TITLEThis is the table title 8 NROWS d 0 FIELD_0_NAMEvar1 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar3 8 test tuple1group1`( (TITLE (CLASSGROUP (VERSION1.0 ˜FILTERSu(itables.Leaf Filters p1 (dp2 S'shuffle' p3 I0 sS'complevel' p4 I1 sS'fletcher32' p5 I0 sS'complib' p6 S'lzo' p7 sb. 0test2 just a test (1lzo ª=#@ (CLASSTABLE (VERSION2.1 8TITLEThis is the table title 8 NROWS d 0 FIELD_0_NAMEvar1 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar3 8 test tuple2group2@  (TITLE (CLASSGROUP (VERSION1.0 ˜FILTERSu(itables.Leaf Filters p1 (dp2 S'shuffle' p3 I0 sS'complevel' p4 I1 sS'fletcher32' p5 I0 sS'complib' p6 S'lzo' p7 sb. 0test2 just a test (1lzoÐ"ª=#@ (CLASSTABLE (VERSION2.1 8TITLEThis is the table title 8 NROWS d 0 FIELD_0_NAMEvar1 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar3 8 test  è (TITLE (CLASSGROUP (VERSION1.0 ˜FILTERSu(itables.Leaf Filters p1 (dp2 S'shuffle' p3 I0 sS'complevel' p4 I1 sS'fletcher32' p5 I0 sS'complib' p6 S'lzo' p7 sb. PyTables-v.3.1.1/tables/tests/Tables_lzo1.h5000066400000000000000000000555031231437614300205500ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ=[ÿÿÿÿÿÿÿÿ €`HEAP€tuple0group0èTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàkTdZdZdZdZdZ e e e e d„Z eeee e e e e d „Z eeee e e e e d „Z e e e e d „Zd „Zd ddd„Ze d„Zd„Ze d„Zd„Zd„Zd„ZdS‰ˆxù·ˆxù·ÌPA§fä·DHͶ";E» oͶ¼sͶcE@¯à³Í¶d±ˆ· ÷cò{ͶkàڲͶ „ͶȊ°¸˜šâ·Œfâ·J´C’À`ä·O̶w³ ä·ŒpͶwqE„  ä·ˆª*MŸã ;ͶL±ˆ·o€ŠÍ¶äJͶjYsƒ`ŠÍ¶ŒsͶ°ž/áxlͶ|HͶ“÷D¿€=Ͷ\sͶÐU¥Þ`éâ·   ¨var3 ?@4 4ÿvar2 var1 dÿÿÿÿÿÿÿÿ SNOD€`(ÐHEAP0°TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ kTdZdZdZdZdZ e e e e d„Z eeee e e e e d „Z eeee e e e e d „Z e e e e d „Zd „Zd ddd„Ze d„Zd„Ze d„Zd„Zd„Zd„ZdS‰ˆxù·ˆxù·ÌPA§fä·DHͶ";E» oͶ¼sͶcE@¯à³Í¶d±ˆ· ÷cò{ͶkàڲͶ „ͶȊ°¸˜šâ·Œfâ·J´C’À`ä·O̶w³ ä·ŒpͶwqE„  ä·ˆª*MŸã ;ͶL±ˆ·o€ŠÍ¶äJͶjYsƒ`ŠÍ¶ŒsͶ°ž/áxlͶ|HͶ“÷D¿€=Ͷ\sͶÐU¥Þ`éâ· 0(¸¨var3 ?@4 4ÿvar2 var1 dÿÿÿÿÿÿÿÿè)SNOD` @  °HEAP0àTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ kTdZdZdZdZdZ e e e e d„Z eeee e e e e d „Z eeee e e e e d „Z e e e e d „Zd „Zd ddd„Ze d„Zd„Ze d„Zd„Zd„Zd„ZdS‰ˆxù·ˆxù·ÌPA§fä·DHͶ";E» oͶ¼sͶcE@¯à³Í¶d±ˆ· ÷cò{ͶkàڲͶ „ͶȊ°¸˜šâ·Œfâ·J´C’À`ä·O̶w³ ä·ŒpͶwqE„  ä·ˆª*MŸã ;ͶL±ˆ·o€ŠÍ¶äJͶjYsƒ`ŠÍ¶ŒsͶ°ž/áxlͶ|HͶ“÷D¿€=Ͷ\sͶÐU¥Þ`éâ· ø+¸¨var3 ?@4 4ÿvar2 var1 dÿÿÿÿÿÿÿÿ°-SNOD@ è HEAPTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ kTdZdZdZdZdZ e e e e d„Z eeee e e e e d „Z eeee e e e e d „Z e e e e d „Zd „Zd ddd„Ze d„Zd„Ze d„Zd„Zd„Zd„ZdS‰ˆxù·ˆxù·ÌPA§fä·DHͶ";E» oͶ¼sͶcE@¯à³Í¶d±ˆ· ÷cò{ͶkàڲͶ „ͶȊ°¸˜šâ·Œfâ·J´C’À`ä·O̶w³ ä·ŒpͶwqE„  ä·ˆª*MŸã ;ͶL±ˆ·o€ŠÍ¶äJͶjYsƒ`ŠÍ¶ŒsͶ°ž/áxlͶ|HͶ“÷D¿€=Ͷ\sͶÐU¥Þ`éâ· À/€TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿX0 €` 0TITLETable Benchmark (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I0 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb. 8PYTABLES_FORMAT_VERSION1.4 0test2 just a test (1lzopC¶B (CLASSTABLE (VERSION2.4 8TITLEThis is the table title 0 NROWS@d 0 FIELD_0_NAMEvar3 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar1 (testI2 .tuple1group1tuple2group2Y@A @XÀX@@Y X€X@'<@X@*<X@*<ÀW@*<€W@*<@W@*<W@*<ÀV@ *<€V@ *<@V@ *<V@ *<ÀU@ *<€U@*<@U@*<U@*<ÀT@*<€T@*<@T@*<T@*<ÀS@*<€S@*<@S@*<S@*<ÀR@*<€R@*<@R@*<R@*<ÀQ@*<€Q@*<@Q@*<Q@ *<ÀP@!*<€P@"*<@P@#*<P@$*<€O@%*<O@&*<€N@'*<N@(*<€M@)*<M@**<€L@+*<L@,*<€K@-*<K@.*<€J@/*<J@0*<€I@1*<I@2*<€H@3*<H@4*<€G@5*<G@6*<€F@7*<F@8*<€E@9*<E@:*<€D@;*<D@<*<€C@=*<C@>*<€B@?*?B@`€'|€A@A+A@B*<€@@C*<@@D+??@E+?>@F+?=@G+?<@H+?;@I+?:@J+?9@K+?8@L+?7@M+?6@N+?5@O+?4@P+?3@Q+?2@R+?1@S+?0@T+?.@U+?,@V+?*@W+?(@X+?&@Y+?$@Z+?"@[+? @\+?@]+?@^+?@_+?@`+?@a+?@b+?ð?c+<P ‘`( (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I0 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb. 0test2 just a test (1lzo@1C¶B (CLASSTABLE (VERSION2.4 8TITLEThis is the table title 0 NROWS@d 0 FIELD_0_NAMEvar3 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar1 (testI2 .@  (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I0 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb. 0test2 just a test (1lzo`HC¶B (CLASSTABLE (VERSION2.4 8TITLEThis is the table title 0 NROWS@d 0 FIELD_0_NAMEvar3 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar1 (testI2 . è (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I0 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb.TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿq`@ X@ ÀW@ €W@ @W@ W@ ÀV@ €V@ @V@ V@ ÀU@ €U@ @U@ U@ ÀT@ €T@ @T@ T@ ÀS@ €S@ @S@ S@ ÀR@ €R@ @R@ R@ ÀQ@ €Q@ @Q@ Q@ ÀP@! €P@" @P@# P@$ €O@% O@& €N@' N@( €M@) M@* €L@+ L@, €K@- K@. €J@/ J@0 €I@1 I@2 €H@3 H@4 €G@5 G@6 €F@7 F@8 €E@9 E@: €D@; D@< €C@= C@> €B@? B@@ €A@A A@B €@@C @@D ?@E >@F =@G <@H ;@I :@J 9@K 8@L 7@M 6@N 5@O 4@P 3@Q 2@R 1@S 0@T .@U ,@V *@W (@X &@Y $@Z "@[ @\ @] @^ @_ @` @a @b ð?c Y@A @XÀX@@Y X€X@'<@X@*<X@*<ÀW@*<€W@*<@W@*<W@*<ÀV@ *<€V@ *<@V@ *<V@ *<ÀU@ *<€U@*<@U@*<U@*<ÀT@*<€T@*<@T@*<T@*<ÀS@*<€S@*<@S@*<S@*<ÀR@*<€R@*<@R@*<R@*<ÀQ@*<€Q@*<@Q@*<Q@ *<ÀP@!*<€P@"*<@P@#*<P@$*<€O@%*<O@&*<€N@'*<N@(*<€M@)*<M@**<€L@+*<L@,*<€K@-*<K@.*<€J@/*<J@0*<€I@1*<I@2*<€H@3*<H@4*<€G@5*<G@6*<€F@7*<F@8*<€E@9*<E@:*<€D@;*<D@<*<€C@=*<C@>*<€B@?*?B@`€'|€A@A+A@B*<€@@C*<@@D+??@E+?>@F+?=@G+?<@H+?;@I+?:@J+?9@K+?8@L+?7@M+?6@N+?5@O+?4@P+?3@Q+?2@R+?1@S+?0@T+?.@U+?,@V+?*@W+?(@X+?&@Y+?$@Z+?"@[+? @\+?@]+?@^+?@_+?@`+?@a+?@b+?ð?c+<P ô ¡aÑ80Ë8@MPM`LpË8ÞÎ8M M°MÀMÐMàMðLÌ8M M0M@MPM`MpM€MM M°MÀMÐMàMðLÍ8L"Ù ]0M@MPM`MpM€MM M°MÀMÐMàMðMMM M0M@MPM`MpM€MM M°MÀMÐMàMðLÏ8LŒ]0M@MPM`MpM€MM M°MÀMÐMàMðLÐ8M M0M@MPM`MpM€MM M°MÀMÐMàMðM]/LŒ]0M@MPLl O l; OÐÝ ýl2 Z Ê_ b o b ¿ Z }ÝAÏ Z ýAŸ b ¯ b ÿ Z ýl¾]ƒ|Çà3 Vß Zéï  Þ|Ôð .? R-îÝ@}ÝO R .- >-<.¼ +=ýÝ@/ JÌ6ý Z }Ý Z ý"_ Z ýÍoÜì S¿ Z }ÍÏÜ SüíŸ b ¯ b ÿ b ÜÆm3,̆l K ß)Þ"8 KðÌ ïÌ8B VüÊ?),íî%Ü/ü ?O1  Í>   ü>½N / > >Œ} b  b _ b o b ¿ b Ï b Ÿ b ¯ b ÿ b  N||¢. ß bï  î :ìAm"? J->5­O5\| G - N)|) G /-  GÜ* b  b _ b o b ¿ b Ï b Ÿ b ¯ b ÿ b  >L*<.-î%­Aß bïÎ8C \:.  +<?Ï8$Ý1>Ü +| -O "ÝN ,&:- " :|m=­/ B |:  b  b _ b o b ¿ b Ï b Ÿ b ¯ b ÿ b Ìø: ?mß bï- > R]? FÍN9½&O F :M 1  N B/1 :Ì 3m b  b _ b o b ¿ b Ï b Ÿ b ¯ b ÿ b )m¹>), 3<6Mß bï b ?Ý/N-,#6Ü {]  BÜ 7}.9íAOÍ/Ü 7  ×\TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­ Pp(ñ*l5§·Œ³·øý'ð®'¨(X+àöÜ· öÜ· öÜ·€öÜ·€öÜ·˜+øý'‡A¦=釷$ï­¹`뇷Œζ`7臷,PͶDí40˜ê©·€îâ·›{ÙP燷,Ͷõ˜šx燷ζ!éU ˆ·äζ¯à ` ˆ·ôζ9ü“`ˆ·˜@â·C¯Å½èê©·dîD‚✀öÜ·,&ª·F‚âœàǫܷ̈ӷG‚✠öÜ·ŒXÓ·MQ X釷,ÕͶÓ?çë©·ζY4_Àê©·|ζ\7ܠ燷ÌÔͶlòI!&ª·€ ˆ·í?/MÀ@Ó·˜@â·ñOàØ8뇷ÌͶyGÝëØæ‡·lζzû"ª·,ŠÍ¶_n…€çá¶$ζ 8ÄPÜᶬͶ ªLÆ€%Ó·`„”»pßá¶œζ‰Û}(Üá¶,‰Í¶Và1ß·ìˆÍ¶”Ò› 0à¶`R}î Pâ·ÚbY@A @XÀX@@Y X€X@'<@X@*<X@*<ÀW@*<€W@*<@W@*<W@*<ÀV@ *<€V@ *<@V@ *<V@ *<ÀU@ *<€U@*<@U@*<U@*<ÀT@*<€T@*<@T@*<T@*<ÀS@*<€S@*<@S@*<S@*<ÀR@*<€R@*<@R@*<R@*<ÀQ@*<€Q@*<@Q@*<Q@ *<ÀP@!*<€P@"*<@P@#*<P@$*<€O@%*<O@&*<€N@'*<N@(*<€M@)*<M@**<€L@+*<L@,*<€K@-*<K@.*<€J@/*<J@0*<€I@1*<I@2*<€H@3*<H@4*<€G@5*<G@6*<€F@7*<F@8*<€E@9*<E@:*<€D@;*<D@<*<€C@=*<C@>*<€B@?*?B@`€'|€A@A+A@B*<€@@C*<@@D+??@E+?>@F+?=@G+?<@H+?;@I+?:@J+?9@K+?8@L+?7@M+?6@N+?5@O+?4@P+?3@Q+?2@R+?1@S+?0@T+?.@U+?,@V+?*@W+?(@X+?&@Y+?$@Z+?"@[+? @\+?@]+?@^+?@_+?@`+?@a+?@b+?ð?c+<P " Ë8xÀ7&/Ï<|7à *a b  b _ b o b ¿ b Ï b Ÿ b ¯ b ÿ bÐ aß bï- > R]? FÎNÎ8ÍO F :™1Ì8Ì:, m/1  , Ý b  b _ b o b ¿ b Ï b Ÿ b ¯ b ÿ bÑ(]>), Ì Ýß bï b ?Ý/N-,# L _ÎÍ(\ -Ü Ü ½.9íAOÍ/Ü L œ¨a`n°¥Y@A @X$r@Y Xx%r'<"ýq*\"Üq|"¼q|"œq|"|q|"\q|ÀV@ |"q|"üp|"Üp|"¼p|"œp|"|p|"\p|ÀT@|"p|@T@|"Üo|"¼o|"œo|"|o|"\o|ÀR@|"o|"ün|"Ün|"¼n|"œn|"|n|"\n|ÀP@!|"n|"üm|"Üm|"¼m|"œm|"|m|"\m|€M@)|"m|"ül|"Ül|"¼l|"œl|"|l|"\l|€I@1|"l|"ük|"Ük|"¼k|"œk|"|k|"\k|€E@9|"k|"üj|"Üj|"¼j|"œj|"|j*?B@`€'|€A@A+A@B—@@C|"Ôi+??@E+?>@F+?=@G+?<@H+?;@I+?:@J+?9@K+?8@L_7@M_6@N_5@O_4@P_3@Q_2@R_1@S_0@TG.@UO,@VW*@W_(@X_&@Y_$@Z_"@[_ @\G@]O@^O@_O@`W@aW@bTð?c+<P"Õu<3ô ¡aÑ80Ë8@MPM`LpË8ÞÎ8M M°MÀMÐMàMðLÌ8M M0MÐMpM€MM2ÄÍ8L"Ù ] ÐMM ‘Ï’ Œ ˆeÐ $P]/L)ˆLl O l; OÐÝ ýl2"€ƒ Z Ê_ b op¿ Z }ÝAÏgýAŸm¯qÿ`?ýl¾]ƒ|Çà3 Vß Zéï  Þ|Ôð .? R-îÝ@}ÝO R .- >-<.¼ +=ýÝ@/ JÌ6ýs }Ý Ÿý"_x ýÍoÜì S´ÍÏÜ Süí*<HLÜÆm3,̆l K ß)Þ"8 KðÌ ïÌ8B VüÊ?),íî%Ü/ü ?O1  Í>   ü>½N / > >Œ}yp*xUÏl.$ N||¢. T"bï  î :ìAm"? J->5­O5\| G d"N)|) G /-HGÜ* Ä>L*<.-î%­Aß bïÎ8C \:.  +<?Ï8$Ý1>@(+| -O "ÝN ,&:- " :|m=­/ B |: $Ìø: ?mÌx= R]? FÍN9½&O F :M 1  N B/1TÌ 3m ¤ )m¹>), 3<6Mß ˆ-b ?Ý/N-,#6Ü {]  BÜ 7}.9íAOÍ/Ü 7 Ÿ×\"t‹<Øõ;9\4ì Ä%ýAÜ Í. ìA ?­~ ’ 9¼i ?¼ t} ï= z¼ Ïý-- b\c 3 .=  N¼6~ ^ ^ü6\ O] ’Ü% 3 æ 3l àM +Íx. –íA~-ü% oMŽ =ü o¬ a=. ²íA~=ÜA oÍ Ž æü? o\ =N +|1 ü ¿Í+ Þm0Í ~m. ;ý~1½AM1L { Ž ~ìAí'^ –Í †|ü, @M ~¼ @ü.}Í æ­5M J~ zAÍ .Ž –½^ æ\ .¼ ×=M .\ × ªÝŽ ŽœA>Ý^ Jü'>,.}n òì.| =Ž zÜA ? ^ ŠBn ŠìA PyTables-v.3.1.1/tables/tests/Tables_lzo1_shuffle.h5000066400000000000000000000511511231437614300222570ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿcRÿÿÿÿÿÿÿÿ €`HEAP€tuple0group0èTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàXdSHðÀ¦ÏÿÿÿÿdZy dkZWn ej oZdZdkZdkZy(eideƒ\Z Z \Z Z Z Wn…ej oygiZeiƒD]-\Z Z Z e eijoee ƒq‹q‹[Zeo!deddieƒfZq nXde eiiƒdfZeeeƒe‚nXdklZlZlZlZlZd klZlZl Z l!Z!l"Z"l#Z#d kl$Z$l%Z%l&Z&l'Z'l(Z(l)Z)yd kl*Z*l+Z+Wnej onXd   ¨var3 ?@4 4ÿvar2 var1 dÿÿÿÿÿÿÿÿ (SNOD€`(ÐHEAP0ÈTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ XdSHðÀ¦ÏÿÿÿÿdZy dkZWn ej oZdZdkZdkZy(eideƒ\Z Z \Z Z Z Wn…ej oygiZeiƒD]-\Z Z Z e eijoee ƒq‹q‹[Zeo!deddieƒfZq nXde eiiƒdfZeeeƒe‚nXdklZlZlZlZlZd klZlZl Z l!Z!l"Z"l#Z#d kl$Z$l%Z%l&Z&l'Z'l(Z(l)Z)yd kl*Z*l+Z+Wnej onXd 0(¸¨var3 ?@4 4ÿvar2 var1 dÿÿÿÿÿÿÿÿè)(SNOD` @  °HEAP0øTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ XdSHðÀ¦ÏÿÿÿÿdZy dkZWn ej oZdZdkZdkZy(eideƒ\Z Z \Z Z Z Wn…ej oygiZeiƒD]-\Z Z Z e eijoee ƒq‹q‹[Zeo!deddieƒfZq nXde eiiƒdfZeeeƒe‚nXdklZlZlZlZlZd klZlZl Z l!Z!l"Z"l#Z#d kl$Z$l%Z%l&Z&l'Z'l(Z(l)Z)yd kl*Z*l+Z+Wnej onXd ,¸¨var3 ?@4 4ÿvar2 var1 dÿÿÿÿÿÿÿÿÈ-(SNOD@ è HEAPTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ XdSHðÀ¦ÏÿÿÿÿdZy dkZWn ej oZdZdkZdkZy(eideƒ\Z Z \Z Z Z Wn…ej oygiZeiƒD]-\Z Z Z e eijoee ƒq‹q‹[Zeo!deddieƒfZq nXde eiiƒdfZeeeƒe‚nXdklZlZlZlZlZd klZlZl Z l!Z!l"Z"l#Z#d kl$Z$l%Z%l&Z&l'Z'l(Z(l)Z)yd kl*Z*l+Z+Wnej onXd ð/€TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿl0 €` 0TITLETable Benchmark (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I1 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb. 8PYTABLES_FORMAT_VERSION1.4 0test2 just a test @shuffle1lzop:¶B (CLASSTABLE (VERSION2.4 8TITLEThis is the table title 0 NROWS@d 0 FIELD_0_NAMEvar3 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar1 (testI2 .tuple1group1tuple2group2 {À€@>€< ¹{RYXXXXWWWWVVVVUUUUTTTTSSSSRRRRQQQQPPPPOONNMMLLKKJJIIHHGGFFEEDDCCBBAA@@?>=<;:9876543210.,*(&$" ð šá@ A? šdR  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abc ›d >Ù B šÀJ Ba Úd ~ € : : @„¨WSNF<( *:JZ›AL ¾ @X @€ AdaÀVRME;&š +;K[d ¾YÔª ;H/ ¢dEÀ€„Ã:$šd ,N^š ôÀ€@>€< = dn>{UQKC7@R/?O_"äa =d ¿tRYXXXXWWWWVVVVUUUUTTTTSSSSRRRRQQQQPPPPOONNMMLLKKJJIIHHGGFFEEDDCCBBAA@@?>=<;:9876543210.,*(&$" ð ¤Ã ;ø @„  >zGdà 6  0@P`>J;Ô q@ A? ¸ª ;ø @„  >dRUQJB5A!1AQaÙ ?À;P £pQ  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abc Œ¸ª ;ø @„  ; €Ydà 4"2BRbB;Ô ¥p¸ª ; @„  ;dx#XTPIA3#3CSc at ¾ ¸ª ;€ @„ @€u#<„à 2?$4DT ÃDà AX @„ @€dÃXTPH@1ð %5EUB ?2t » ¬– = ð° :ø  ;¤ :  #l„Ã0 &6FVD ;HX À¬– = ° :ø  ;¤ :  !l { WSOG?.'7GW !D ଖ = „° :ø  dtdà >,(8HX Ú:Ø lÔ¢ < ¬ :ø  l«:” l¤  ìn=* )9IYd¼U|  `( (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I1 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb. 0test2 just a test @shuffle1lzop1:¶B (CLASSTABLE (VERSION2.4 8TITLEThis is the table title 0 NROWS@d 0 FIELD_0_NAMEvar3 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar1 (testI2 .@  (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I1 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb. 0test2 just a test @shuffle1lzo 9:¶B (CLASSTABLE (VERSION2.4 8TITLEThis is the table title 0 NROWS@d 0 FIELD_0_NAMEvar3 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar1 (testI2 . è (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I1 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb.TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ œ! X@ ÀW@ €W@ @W@ W@ ÀV@ €V@ @V@ V@ ÀU@ €U@ @U@ U@ ÀT@ €T@ @T@ T@ ÀS@ €S@ @S@ S@ ÀR@ €R@ @R@ R@ ÀQ@ €Q@ @Q@ Q@ ÀP@! €P@" @P@# P@$ €O@% O@& €N@' N@( €M@) M@* €L@+ L@, €K@- K@. €J@/ J@0 €I@1 I@2 €H@3 H@4 €G@5 G@6 €F@7 F@8 €E@9 E@: €D@; D@< €C@= C@> €B@? B@@ €A@A A@B €@@C @@D ?@E >@F =@G <@H ;@I :@J 9@K 8@L 7@M 6@N 5@O 4@P 3@Q 2@R 1@S 0@T .@U ,@V *@W (@X &@Y $@Z "@[ @\ @] @^ @_ @` @a @b ð?c TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓ Hp(ñ*,u³·¬s³·XÔÈi+¨(p +÷Ü·ÀöÜ·ÀöÜ· öÜ· öÜ·˜{ù·˜{ù·‡A¦=釷$ï­¹`뇷Œζ`7臷,@ͶDí40˜ê©·€îâ·›{ÙP燷LÖͶõ˜šx燷ζ!éUÀˆ·äζ¯à € ˆ·ôζ9ü“€ˆ·˜@â·C¯Å½èê©·dîD‚✠öÜ·L&ª·F‚âœ÷Ü·ì¨Ó·G‚âœÀöÜ·¬XÓ·MQ X釷lÕͶÓ?çë©·ζY4_Àê©·|ζ\7ܠ燷 ÕͶlòI! &ª·  ˆ·í?/MÀ@Ó·˜@â·ñOàØ8뇷LͶyGÝëØæ‡·lζzû "ª·ìͶ_n… çá¶$ζ 8ÄPÜá¶lͶ ªLÆ %Ó·`„”»pßá¶œζ‰Û}(Üá¶ÌͶV€0ß· ‰Í¶”Ò›@0à¶`R}î Pâ·Úb £¨(  ˆ*¨:ˆ :R -l°+Md+4 &0¼ ¶] &P7D7d b€€ð¸´ÍÜ VRLD8+Xx Á>l  @ 44X T(8HXl¸*B| ; P -p°+M+4 &0¼ ´ &H da(¬;*J(t 5&M ,\+𥠸 3µ @¸ 3€Í 8Ü @@ìü*/ hÐUQKC7Ð-U #$ ¡Ã?H °@( A(( $ )9IY(ü¼ µ .#(”åIà %¸´Ä $øÌ -¼ 4è-¤ X8¤<¶Z ?E -€¸´Ä 5¨Ô ½ ¹   ð¼ ¤ Œ¸ *nt­ ì:fÃ64…Q4X T¤Ãl°d' '$   *:J)Ð ¼ ¶b ` è $p(¤¸*$(t 5%X @`5 5Ü T¾I› ?Å; -€¸*, -„05V0H D°+5´+Œ?2ÀÀ9Í0O?ø €UQJB55U*K ü?ˆ.d+¨ $ð9 +;K[. ¼ '4N.x (=3 ($€ ¸*4€ :T¸ :„ Ih)œa*Ì G&*Ð !,¸*< !T< .A°<È tüÂì¼ ¶_ô YfÃ44C4X T4dÉ! ,4´ ,€<:Œ Gl)œa0ÅW0H }D* ®L ?Ä .€¸´ÉÜ (œa*¼ #xxXTPIA35u6*Ð ,;e1 T*Ü -=M5EU - D¤/*ð 5,¼ ¶T 5Œ(TUHYXXXXWWWWVVVVUUUUTTTTSSSSRRRRQQQQPPPPOONNMMLLKKJJIIHHGGFFEEDDCCBBAA@@?>=<;:9876543210.,*(&$" ð(À W%S -ÜØS*Qš*0 ',¼ µ/( '”ü .&¸´É Ü 2½ { #d…Ã25$ $AL@€d ¸* $^.>N^d 0 $¨d(X°(<+4 &0¼ µØ &h1@4 = $Xl1ô IN $@'¼¼R´Ñ Ü 2½ € -191L H¸*. 8'è(œa'H'"UÀ'(  XTPH@1"8'°+$1H+| 0¸(¨° µ $à/?O5hM #t•(ìдн¸ 3¼ @eU"ä6Q  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abc 3ˆ MF -´¸S´Å Ü 2+œ 2|é "à %¸*` $°G.œa*´ $T…Ã0x °d - ;d;XT  0@P`4´ -0I+dÁà )UXÀ )D.$"` .L ´< .Œ!¼R*+ ?Ø Aåá -*x®N #„4œa¤ $äWSOG?#x3¸´¡ 5`zÔ .d¸+œ¨ !1AQ5Œ .\¸* .l’0T -=°+˜1H+| ¨1( - ¸F*a .Ôª0¼ Aä -´ Ä*‘5à[È " $äeÃ>#Œ°*ð¬%„, ¨:fRÙ px"2BR5Ü $pt ¬ #* $´ 4"ä4d T°+é+4 0¤I* $´*: M:p l+ð¹ ¸ 3´ $eÀ:a7 #ˆWSNF=*d ¸*#”Il  4d¥ U¸ ° #3CS"[14 $(Ý.d¼ µ .À‡.® Aõ B0h *<€B@?*?B@`€'|€A@A+A@B*<€@@C*<@@D+??@E+?>@F+?=@G+?<@H+?;@I+?:@J+?9@K+?8@L+?7@M+?6@N+?5@O+?4@P+?3@Q+?2@R+?1@S+?0@T+?.@U+?,@V+?*@W+?(@X+?&@Y+?$@Z+?"@[+? @\+?@]+?@^+?@_+?@`+?@a+?@b+?ð?c+<P ‘`( (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I0 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb. 0test2 just a test (1lzo@1§¶B (CLASSTABLE (VERSION2.4 8TITLEThis is the table title 0 NROWS@d 0 FIELD_0_NAMEvar3 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar1 (testI2 .@  (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I0 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb. 0test2 just a test (1lzo`H§¶B (CLASSTABLE (VERSION2.4 8TITLEThis is the table title 0 NROWS@d 0 FIELD_0_NAMEvar3 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar1 (testI2 . è (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I0 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb.TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒ`@ X@ ÀW@ €W@ @W@ W@ ÀV@ €V@ @V@ V@ ÀU@ €U@ @U@ U@ ÀT@ €T@ @T@ T@ ÀS@ €S@ @S@ S@ ÀR@ €R@ @R@ R@ ÀQ@ €Q@ @Q@ Q@ ÀP@! €P@" @P@# P@$ €O@% O@& €N@' N@( €M@) M@* €L@+ L@, €K@- K@. €J@/ J@0 €I@1 I@2 €H@3 H@4 €G@5 G@6 €F@7 F@8 €E@9 E@: €D@; D@< €C@= C@> €B@? B@@ €A@A A@B €@@C @@D ?@E >@F =@G <@H ;@I :@J 9@K 8@L 7@M 6@N 5@O 4@P 3@Q 2@R 1@S 0@T .@U ,@V *@W (@X &@Y $@Z "@[ @\ @] @^ @_ @` @a @b ð?c Y@A @XÀX@@Y X€X@'<@X@*<X@*<ÀW@*<€W@*<@W@*<W@*<ÀV@ *<€V@ *<@V@ *<V@ *<ÀU@ *<€U@*<@U@*<U@*<ÀT@*<€T@*<@T@*<T@*<ÀS@*<€S@*<@S@*<S@*<ÀR@*<€R@*<@R@*<R@*<ÀQ@*<€Q@*<@Q@*<Q@ *<ÀP@!*<€P@"*<@P@#*<P@$*<€O@%*<O@&*<€N@'*<N@(*<€M@)*<M@**<€L@+*<L@,*<€K@-*<K@.*<€J@/*<J@0*<€I@1*<I@2*<€H@3*<H@4*<€G@5*<G@6*<€F@7*<F@8*<€E@9*<E@:*<€D@;*<D@<*<€C@=*<C@>*<€B@?*?B@`€'|€A@A+A@B*<€@@C*<@@D+??@E+?>@F+?=@G+?<@H+?;@I+?:@J+?9@K+?8@L+?7@M+?6@N+?5@O+?4@P+?3@Q+?2@R+?1@S+?0@T+?.@U+?,@V+?*@W+?(@X+?&@Y+?$@Z+?"@[+? @\+?@]+?@^+?@_+?@`+?@a+?@b+?ð?c+<P ô q‘Ó8`Í8pM€ML Í8Ñ8ÀMÐMàMðLÎ8M M0M@MPM`MpM€MM M°MÀMÐMàMðLÏ8M M0M@L"ÙP]`MpM€MM M°MÀMÐMàMðLÐ8M M0M@MPM`MpM€MM M°MÀMÐMàMðM]M M0M@LŒ]`MpM€MM M°MÀMÐMàMðLÒ8M M0M@MPM`MpM€MM M°MÀMÐMàMðMM.M M0M@LŒ]`MpM€Ll O ¯|< OÐÝ -l+¿ Z Ê b Ÿ b ï Z ­|u]Aÿ Z ýAÏ b ß b /|¸ð" N-ÝA? Z é b   lÎà .o   +ÙÝ@­"< ]ƒ R .=O- n-<.¼ +=-Í _Ü +l -¯ Z ­Ý£¿ Z ý% Z -ÝbŸ Z ý ï Z ­ÍÿÜì SÏ b ß b /ÌS S í?) \̆ì G)  V¬7Ï8r Vüý,o)í%8ü ?1  Ín  O ü>½~ _ > >Œ}¯ b ¿ b  b Ÿ b ï b ÿ b Ï b ß b /%ì7l… W ? b  b    :ìA}?o  | # n5­5\ #Ì  O- ~ R­/_-  ì +¯ b ¿ b  b Ÿ b ï b ÿ b Ï b ß b / Nl92m? Z %­A Z ì"Ñ8s \:ü 3oÑ8TÍ!nÜ 3œ:M "Ý~ m.O " :ŒmN=­_ B |: ¯ b ¿ b  b Ÿ b ï b ÿ b Ï b ß b / ><;:lí? b  b - n-<ü% ;o FÍ~9í, F :O1 N N B_1 :Ì 3m¯ b ¿ b  b Ÿ b ï b ÿ b Ï b ß b /%ü= 3ì M?) n), ü # VÜ*í b oÍ~ bl+*l -N  BOÜ Ü Í^9íAÍ_Ü < ŒTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐ PŒ³·x(à+àöÜ· öÜ· öÜ·€öÜ·€öÜ·Ð+¨­‡A¦=釷$ï­¹`뇷ŒÆÊ¶`7臷,ʶDí40˜ê©·€îâ·›{ÙP燷,Mʶõ˜šx燷ÆÊ¶!éU ˆ·äÃʶ¯à ` ˆ·ôÄʶ9ü“`ˆ·˜@â·C¯Å½èê©·dîD‚✀öÜ·,&ª·F‚âœàǫܷ̈ӷG‚✠öÜ·ŒXÓ·MQ X釷,•ʶÓ?çë©·ÇʶY4_Àê©·|Åʶ\7ܠ燷̔ʶlòI!&ª·€ ˆ·í?/MÀ@Ó·˜@â·ñOàØ8뇷ÌMʶyGÝëØæ‡·lÄʶzû"ª·,Jʶ_n…€çá¶$Èʶ 8ÄPÜᶬMʶ ªLÆ€%Ó·`„”»pßá¶œÇʶ‰Û}(Üá¶,IʶVà1ß·ìHʶ”Ò› 0à¶`R}î Pâ·Úb äá¶¼vʶ\ÓlÜá¶(®*…AdE˜@Ó·¼­*Y@A @XÀX@@Y X€X@'<@X@*<X@*<ÀW@*<€W@*<@W@*<W@*<ÀV@ *<€V@ *<@V@ *<V@ *<ÀU@ *<€U@*<@U@*<U@*<ÀT@*<€T@*<@T@*<T@*<ÀS@*<€S@*<@S@*<S@*<ÀR@*<€R@*<@R@*<R@*<ÀQ@*<€Q@*<@Q@*<Q@ *<ÀP@!*<€P@"*<@P@#*<P@$*<€O@%*<O@&*<€N@'*<N@(*<€M@)*<M@**<€L@+*<L@,*<€K@-*<K@.*<€J@/*<J@0*<€I@1*<I@2*<€H@3*<H@4*<€G@5*<G@6*<€F@7*<F@8*<€E@9*<E@:*<€D@;*<D@<*<€C@=*<C@>*<€B@?*?B@`€'|€A@A+A@B*<€@@C*<@@D+??@E+?>@F+?=@G+?<@H+?;@I+?:@J+?9@K+?8@L+?7@M+?6@N+?5@O+?4@P+?3@Q+?2@R+?1@S+?0@T+?.@U+?,@V+?*@W+?(@X+?&@Y+?$@Z+?"@[+? @\+?@]+?@^+?@_+?@`+?@a+?@b+?ð?c+<P " NÍ8xÀ7&_Ñ<|7à *a¯ b ¿ b  b Ÿ b ï b ÿ b Ï b ß b/Ò a? b  b - n-< *X7)o FÎ~Ð8Ý  F 7O1NÎ8Ì:, m_1  , ݯ b ¿ b  b Ÿ b ï b ÿ b Ï b ß b/Ó ML2?) n),2¬ 7M VÜ*= b oÍ~ bl+*l .NÏ m OÜ Ü Í^9íAÍ_Ü < Œ¨a`n ¦Y@A @X$r@Y Xx%r'<"ýq*\"Üq|"¼q|"œq|"|q|"\q|ÀV@ |"q|"üp|"Üp|"¼p|"œp|"|p|"\p|ÀT@|"p|@T@|"Üo|"¼o|"œo|"|o|"\o|ÀR@|"o|"ün|"Ün|"¼n|"œn|"|n|"\n|ÀP@!|"n|"üm|"Üm|"¼m|"œm|"|m|"\m|€M@)|"m|"ül|"Ül|"¼l|"œl|"|l|"\l|€I@1|"l|"ük|"Ük|"¼k|"œk|"|k|"\k|€E@9|"k|"üj|"Üj|"¼j|"œj|"|j*?B@`€'|€A@A+A@B—@@C|"Ôi+??@E+?>@F+?=@G+?<@H+?;@I+?:@J+?9@K+?8@L_7@M_6@N_5@O_4@P_3@Q_2@R_1@S_0@TG.@UO,@VW*@W_(@X_&@Y_$@Z_"@[_ @\G@]O@^O@_O@`W@aW@bTð?c+<P"Õu<3ô q‘Ó8`Í8pM€ML Í8Ñ8ÀMÐMàMðLÎ8M M0M@MPM`MÐM M°MÀM)ÅÏ*ÄL"ÙP]?ÑÐ*Ð?˜M](XLŒ ‰Ò ŠM.5ˆLl O ¯|< OÐÝ -l+"€ƒ ¿ Z Ê b Ÿpï Z ­|u]AÿoýAÏußp /|¸ð" N-ÝA?sé|4  lÎà .o   +ÙÝ@­"< ]ƒ R .=O- n-<.¼ +=-Í _Ü +l -¯žÝ£‡ý%x-ÝbŸ~ ý ´ÍÿÜì S)TMÌS S í?) \̆ì G)  V¬7Ï8r Vüý,o)í%8ü ?1  Ín  O ü>½~ _ > >Œ}¯e¿p*ŒU%ÿl)%ì7l… W ?`À#  :ìA}?o  | # n5­5\ #Ì  |"~ R­/_-\ì + Ü Nl92m?`&%­Ad& ì"Ñ8s \:ü 3oÑ8TÍ!nH.3œ:M "Ý~ m.O " :ŒmN=­_ B |: ><;:lí)ìÐ=ü% ;pBFÍ~9í, F :O1 N N B_1HÌ 3m ¸%ü= 3ì Mp>-n), ü # VÜ*í b oÍ~ bl+*l -N  BOÜ Ü Í^9íAÍsP<§‘ Œ"à‹)Ek9\4)˜ Á)M%ýANÜ Á^ ìA ?­® ’ Í9¼i ?¼ t}M ï=Í z¼ ÏýM--N bœk 3 ^= Í N¼6® ^M ^ü6\ O]Í ’Ü% 3M æ 3l àMN +Íx^ –íA®-ü% oM¾ =ü o¬ a=^ ²íA®=ÜA oÍ ¾ æü? o\ =~ +|1 ü ¿Í[ Þm0ý ~m^ ;ý®1½A}1L { ¾ ~ìAí'Ž –ý †|ü, @} ~¼ @ü.}ý æ­5} J® zAý .¾ –½Ž æ\ .¼ ×=} .\ × ªÝ¾ ŽœA>ÝŽ Jü'>,.}ž òì.| =¾ zÜA ? Ž ŠBž ŠìA PyTables-v.3.1.1/tables/tests/Tables_lzo2_shuffle.h5000066400000000000000000000511511231437614300222600ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿcRÿÿÿÿÿÿÿÿ €`HEAP€tuple0group0èTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàa   ¨var3 ?@4 4ÿvar2 var1 dÿÿÿÿÿÿÿÿ (SNOD€`(ÐHEAP0ÈTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ a 0(¸¨var3 ?@4 4ÿvar2 var1 dÿÿÿÿÿÿÿÿè)(SNOD` @  °HEAP0øTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ a ,¸¨var3 ?@4 4ÿvar2 var1 dÿÿÿÿÿÿÿÿÈ-(SNOD@ è HEAPTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ a ð/€TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿl0 €` 0TITLETable Benchmark (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I1 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb. 8PYTABLES_FORMAT_VERSION1.4 0test2 just a test @shuffle1lzop´¶B (CLASSTABLE (VERSION2.4 8TITLEThis is the table title 0 NROWS@d 0 FIELD_0_NAMEvar3 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar1 (testI2 .tuple1group1tuple2group2 {À€@>€< ¹{RYXXXXWWWWVVVVUUUUTTTTSSSSRRRRQQQQPPPPOONNMMLLKKJJIIHHGGFFEEDDCCBBAA@@?>=<;:9876543210.,*(&$" ð šá@ A? šdR  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abc ›d >Ù B šÀJ Ba Úd ~ € : : @„¨WSNF<( *:JZ›AL ¾ @X @€ AdaÀVRME;&š +;K[d ¾YÔª ;H/ ¢dEÀ€„Ã:$šd ,N^š ôÀ€@>€< = dn>{UQKC7@R/?O_"äa =d ¿tRYXXXXWWWWVVVVUUUUTTTTSSSSRRRRQQQQPPPPOONNMMLLKKJJIIHHGGFFEEDDCCBBAA@@?>=<;:9876543210.,*(&$" ð ¤Ã ;ø @„  >zGdà 6  0@P`>J;Ô q@ A? ¸ª ;ø @„  >dRUQJB5A!1AQaÙ ?À;P £pQ  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abc Œ¸ª ;ø @„  ; €Ydà 4"2BRbB;Ô ¥p¸ª ; @„  ;dx#XTPIA3#3CSc at ¾ ¸ª ;€ @„ @€u#<„à 2?$4DT ÃDà AX @„ @€dÃXTPH@1ð %5EUB ?2t » ¬– = ð° :ø  ;¤ :  #l„Ã0 &6FVD ;HX À¬– = ° :ø  ;¤ :  !l { WSOG?.'7GW !D ଖ = „° :ø  dtdà >,(8HX Ú:Ø lÔ¢ < ¬ :ø  l«:” l¤  ìn=* )9IYd¼U|  `( (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I1 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb. 0test2 just a test @shuffle1lzop1´¶B (CLASSTABLE (VERSION2.4 8TITLEThis is the table title 0 NROWS@d 0 FIELD_0_NAMEvar3 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar1 (testI2 .@  (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I1 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb. 0test2 just a test @shuffle1lzo 9´¶B (CLASSTABLE (VERSION2.4 8TITLEThis is the table title 0 NROWS@d 0 FIELD_0_NAMEvar3 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar1 (testI2 . è (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS¯ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I1 sS'complevel' p7 I1 sS'fletcher32' p8 I0 sS'complib' p9 S'lzo' p10 sb.TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ œ! X@ ÀW@ €W@ @W@ W@ ÀV@ €V@ @V@ V@ ÀU@ €U@ @U@ U@ ÀT@ €T@ @T@ T@ ÀS@ €S@ @S@ S@ ÀR@ €R@ @R@ R@ ÀQ@ €Q@ @Q@ Q@ ÀP@! €P@" @P@# P@$ €O@% O@& €N@' N@( €M@) M@* €L@+ L@, €K@- K@. €J@/ J@0 €I@1 I@2 €H@3 H@4 €G@5 G@6 €F@7 F@8 €E@9 E@: €D@; D@< €C@= C@> €B@? B@@ €A@A A@B €@@C @@D ?@E >@F =@G <@H ;@I :@J 9@K 8@L 7@M 6@N 5@O 4@P 3@Q 2@R 1@S 0@T .@U ,@V *@W (@X &@Y $@Z "@[ @\ @] @^ @_ @` @a @b ð?c TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓ H‡A¦=釷$ï­¹`뇷ŒÆÊ¶`7臷,ʶDí40˜ê©·€îâ·›{ÙP燷L–ʶõ˜šx燷ÆÊ¶!éUÀˆ·äÃʶ¯à € ˆ·ôÄʶ9ü“€ˆ·˜@â·C¯Å½èê©·dîD‚✠öÜ·L&ª·F‚âœ÷Ü·ì¨Ó·G‚âœÀöÜ·¬XÓ·MQ X釷l•ʶÓ?çë©·ÇʶY4_Àê©·|Åʶ\7ܠ燷 •ʶlòI! &ª·  ˆ·í?/MÀ@Ó·˜@â·ñOàØ8뇷LMʶyGÝëØæ‡·lÄʶzû "ª·ìMʶ_n… çá¶$Èʶ 8ÄPÜá¶lMʶ ªLÆ %Ó·`„”»pßá¶œÇʶ‰Û}(Üá¶ÌMʶV€0ß· Iʶ”Ò›@0à¶`R}î Pâ·ÚbÀäá¶¼vʶ\ÓlÜá¶H°*…AdE˜@ӷܯ* £¨(  ˆ*¨:ˆ :R -l°+Md+4 &0¼ ¶] &P7D7d b€€ð¸´ÍÜ VRLD8+Xx Á>l  @ 44X T(8HXl¸*B| ; P -p°+M+4 &0¼ ´ &H da(¬;*J(t 5&M ,\+𥠸 3µ @¸ 3€Í 8Ü @@ìü*/ hÐUQKC7Ð-U #$ ¡Ã?H °@( A(( $ )9IY(ü¼ µ .#(”åIà %¸´Ä $øÌ -¼ 4è-¤ X8¤<¶Z ?E -€¸´Ä 5¨Ô ½ ¹   ð¼ ¤ Œ¸ *nt­ ì:fÃ64…Q4X T¤Ãl°d' '$   *:J)Ð ¼ ¶b ` è $p(¤¸*$(t 5%X @`5 5Ü T¾I› ?Å; -€¸*, -„05V0H D°+5´+Œ?2ÀÀ9Í0O?ø €UQJB55U*K ü?ˆ.d+¨ $ð9 +;K[. ¼ '4N.x (=3 ($€ ¸*4€ :T¸ :„ Ih)œa*Ì G&*Ð !,¸*< !T< .A°<È tüÂì¼ ¶_ô YfÃ44C4X T4dÉ! ,4´ ,€<:Œ Gl)œa0ÅW0H }D* ®L ?Ä .€¸´ÉÜ (œa*¼ #xxXTPIA35u6*Ð ,;e1 T*Ü -=M5EU - D¤/*ð 5,¼ ¶T 5Œ(TUHYXXXXWWWWVVVVUUUUTTTTSSSSRRRRQQQQPPPPOONNMMLLKKJJIIHHGGFFEEDDCCBBAA@@?>=<;:9876543210.,*(&$" ð(À W%S -ÜØS*Qš*0 ',¼ µ/( '”ü .&¸´É Ü 2½ { #d…Ã25$ $AL@€d ¸* $^.>N^d 0 $¨d(X°(<+4 &0¼ µØ &h1@4 = $Xl1ô IN $@'¼¼R´Ñ Ü 2½ € -191L H¸*. 8'è(œa'H'"UÀ'(  XTPH@1"8'°+$1H+| 0¸(¨° µ $à/?O5hM #t•(ìдн¸ 3¼ @eU"ä6Q  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abc 3ˆ MF -´¸S´Å Ü 2+œ 2|é "à %¸*` $°G.œa*´ $T…Ã0x °d - ;d;XT  0@P`4´ -0I+dÁà )UXÀ )D.$"` .L ´< .Œ!¼R*+ ?Ø Aåá -*x®N #„4œa¤ $äWSOG?#x3¸´¡ 5`zÔ .d¸+œ¨ !1AQ5Œ .\¸* .l’0T -=°+˜1H+| ¨1( - ¸F*a .Ôª0¼ Aä -´ Ä*‘5à[È " $äeÃ>#Œ°*ð¬%„, ¨:fRÙ px"2BR5Ü $pt ¬ #* $´ 4"ä4d T°+é+4 0¤I* $´*: M:p l+ð¹ ¸ 3´ $eÀ:a7 #ˆWSNF=*d ¸*#”Il  4d¥ U¸ ° #3CS"[14 $(Ý.d¼ µ .À‡.® Aõ B0h [—ï V7Nÿy.¾þ´µ.›k¿Tâum>G©ƒìßQ~BŠvÿgÿ¬Ë§¼ù65¸øòíO÷]ÒY›Öе$ùy‰ÎÎï„íúLÙ2ïsñõ'hæSµûsaú³®dÓ:îZ–_pG-ÝãúÛðŒx ZŸÂ~“È>®ÏskÎú¹õâöuz¾Á7¤Ç”·mý¶Iç¤eÍß埃µ?퉳&¿ [¯?'g³(ë»6¿TK_kS¿¦¬ùäúÞÒi_¾ýy¬÷y.ßtnû{ñÌéßÖT¤¼õÝîqñ Xÿiuòëê€öߨX~í)lüÝÐÔNÿ™t-_™ül @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€þÏÿÜbHEAPX*TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¨Ø Lئÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ pcHHEAP0¸cTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐ1Ø Lئÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ø,À,HEAP`(ècTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 5 Ø Lئÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ Hd0SNOD 1€/H/HEAP0xeTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp9Ø Lئÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ P33SNODp5P33È ¨ p ˜x@HEAP`P¨eTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@= PQHLئÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ f°SNOD@9 7è6HEAPØ:TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@= PQHLئÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ¸f0SNOD=ð:¸:à@À>ˆ>hCHAA ðEÐC˜CHEAP¨>TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@= PQHLئÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ èg0HEAP0ATREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@= PQHLئÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ i0HEAP¸CTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@= PQHLئÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ Hj0HEAP@FTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@= PQHLئÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ xk0HEAPÈHTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@= PQHLئÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ¨l0HEAPPKTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@= PQHLئÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ Øm0HEAPØMTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@= PQHLئÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ o0ÿÿÿÿ ÿÿÿÿÿÿÿÿ˜Rèú?I€SNOD(xHXF F0KàH¨H8ˆMhK0K@PðM¸MH@PTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ †%HEAP08pTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿP]PQHLئÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ [ÈZSNOD Èaxesidtracesvectors8Ð 8 major_version  8 minor_version  8 release_version  (writerNI-HWS 0type NI-Waveformaxis0axis1¨ p 8 implicit? 8start ?@4 4ÿ @ increment ?@4 4ÿ:Œ0âŽyU> @ ref_time€ 8 numDigits9x@ 8 implicit? 8 explicit_vector  8 num_signals  8 data_type data È 8namewfm_group0:vector0p*8* (nametrace0 render_infox-axisy-axis8€/H/ (name 0 x_axis  0 y_axis  0 version  8 data_typedigital bit0bit1bit2bit3bit4bit5bit6bit7order 7è6 H user_specified_DWDT_order  @ max_channels_per_device ð:¸: (ID0 0name Signal 0 8 line_color ÿ 0 radix 0 showÀ>ˆ> (ID1 0name Signal 1 8 line_color ÿ 0 radix 0 showHAA (ID2 0name Signal 2 8 line_color ÿ 0 radix 0 showÐC˜C (ID3 0name Signal 3 8 line_color ÿ 0 radix 0 showXF F (ID4 0name Signal 4 8 line_color ÿ 0 radix 0 showàH¨H (ID5 0name Signal 5 8 line_color ÿ 0 radix 0 showhK0K (ID6 0name Signal 6 8 line_color ÿ 0 radix 0 showðM¸M (ID7 0name Signal 7 8 line_color ÿ 0 radix 0 showvector0 PyTables-v.3.1.1/tables/tests/blosc_bigendian.h5000066400000000000000000000273061231437614300214730ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿÀ.ÿÿÿÿÿÿÿÿ` èTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ HEAPX(Èi1i2i4i80ˆ¨ (TITLE (CLASSGROUP (VERSION1.0 8PYTABLES_FORMAT_VERSION2.0   € 0}blosc €À€%U'L (CLASSCARRAY``SNODðp ð! (VERSION1.0 (TITLETREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«ƒ,€ @  @ 0}blosc €@@%U'L (CLASSCARRAY (VERSION1.0 (TITLETREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿº.-@ @   0}blosc €À %U'L (CLASSCARRAY (VERSION1.0 (TITLETREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØè-  @ @  0}blosc €@#%U'L (CLASSCARRAY (VERSION1.0 (TITLETREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp+€€!àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"* àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø€€«“* àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿi€€ºK!àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ1S* àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ)€€Ø+!àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ+!àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ+!àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3* àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  PyTables-v.3.1.1/tables/tests/check_leaks.py000066400000000000000000000277311231437614300207430ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import os import time import tables tref = time.time() trel = tref def show_mem(explain): global tref, trel filename = "/proc/%s/status" % os.getpid() with open(filename) as fd: for line in fd: if line.startswith("VmSize:"): vmsize = int(line.split()[1]) elif line.startswith("VmRSS:"): vmrss = int(line.split()[1]) elif line.startswith("VmData:"): vmdata = int(line.split()[1]) elif line.startswith("VmStk:"): vmstk = int(line.split()[1]) elif line.startswith("VmExe:"): vmexe = int(line.split()[1]) elif line.startswith("VmLib:"): vmlib = int(line.split()[1]) print("\nMemory usage: ******* %s *******" % explain) print("VmSize: %7s kB\tVmRSS: %7s kB" % (vmsize, vmrss)) print("VmData: %7s kB\tVmStk: %7s kB" % (vmdata, vmstk)) print("VmExe: %7s kB\tVmLib: %7s kB" % (vmexe, vmlib)) print("WallClock time:", time.time() - tref, end=' ') print(" Delta time:", time.time() - trel) trel = time.time() def write_group(filename, nchildren, niter): for i in range(niter): fileh = tables.open_file(filename, mode="w") for child in range(nchildren): fileh.create_group(fileh.root, 'group' + str(child), "child: %d" % child) show_mem("After creating. Iter %s" % i) fileh.close() show_mem("After close") def read_group(filename, nchildren, niter): for i in range(niter): fileh = tables.open_file(filename, mode="r") for child in range(nchildren): node = fileh.get_node(fileh.root, 'group' + str(child)) assert node is not None # flavor = node._v_attrs.CLASS # for child in fileh.walk_nodes(): # pass show_mem("After reading metadata. Iter %s" % i) fileh.close() show_mem("After close") def write_array(filename, nchildren, niter): for i in range(niter): fileh = tables.open_file(filename, mode="w") for child in range(nchildren): fileh.create_array(fileh.root, 'array' + str(child), [1, 1], "child: %d" % child) show_mem("After creating. Iter %s" % i) fileh.close() show_mem("After close") def read_array(filename, nchildren, niter): for i in range(niter): fileh = tables.open_file(filename, mode="r") for child in range(nchildren): node = fileh.get_node(fileh.root, 'array' + str(child)) # flavor = node._v_attrs.FLAVOR data = node[:] # Read data assert data is not None show_mem("After reading data. Iter %s" % i) # for child in range(nchildren): # node = fileh.get_node(fileh.root, 'array' + str(child)) # flavor = node._v_attrs.FLAVOR # flavor = node._v_attrs # for child in fileh.walk_nodes(): # pass # show_mem("After reading metadata. Iter %s" % i) fileh.close() show_mem("After close") def write_carray(filename, nchildren, niter): for i in range(niter): fileh = tables.open_file(filename, mode="w") for child in range(nchildren): fileh.create_carray(fileh.root, 'array' + str(child), tables.IntAtom(), (2,), "child: %d" % child) show_mem("After creating. Iter %s" % i) fileh.close() show_mem("After close") def read_carray(filename, nchildren, niter): for i in range(niter): fileh = tables.open_file(filename, mode="r") for child in range(nchildren): node = fileh.get_node(fileh.root, 'array' + str(child)) # flavor = node._v_attrs.FLAVOR data = node[:] # Read data assert data is not None # print("data-->", data) show_mem("After reading data. Iter %s" % i) fileh.close() show_mem("After close") def write_earray(filename, nchildren, niter): for i in range(niter): fileh = tables.open_file(filename, mode="w") for child in range(nchildren): ea = fileh.create_earray(fileh.root, 'array' + str(child), tables.IntAtom(), shape=(0,), title="child: %d" % child) ea.append([1, 2, 3]) show_mem("After creating. Iter %s" % i) fileh.close() show_mem("After close") def read_earray(filename, nchildren, niter): for i in range(niter): fileh = tables.open_file(filename, mode="r") for child in range(nchildren): node = fileh.get_node(fileh.root, 'array' + str(child)) # flavor = node._v_attrs.FLAVOR data = node[:] # Read data assert data is not None # print("data-->", data) show_mem("After reading data. Iter %s" % i) fileh.close() show_mem("After close") def write_vlarray(filename, nchildren, niter): for i in range(niter): fileh = tables.open_file(filename, mode="w") for child in range(nchildren): vl = fileh.create_vlarray(fileh.root, 'array' + str(child), tables.IntAtom(), "child: %d" % child) vl.append([1, 2, 3]) show_mem("After creating. Iter %s" % i) fileh.close() show_mem("After close") def read_vlarray(filename, nchildren, niter): for i in range(niter): fileh = tables.open_file(filename, mode="r") for child in range(nchildren): node = fileh.get_node(fileh.root, 'array' + str(child)) # flavor = node._v_attrs.FLAVOR data = node[:] # Read data assert data is not None # print("data-->", data) show_mem("After reading data. Iter %s" % i) fileh.close() show_mem("After close") def write_table(filename, nchildren, niter): class Record(tables.IsDescription): var1 = tables.IntCol(pos=1) var2 = tables.StringCol(length=1, pos=2) var3 = tables.FloatCol(pos=3) for i in range(niter): fileh = tables.open_file(filename, mode="w") for child in range(nchildren): t = fileh.create_table(fileh.root, 'table' + str(child), Record, "child: %d" % child) t.append([[1, "2", 3.]]) show_mem("After creating. Iter %s" % i) fileh.close() show_mem("After close") def read_table(filename, nchildren, niter): for i in range(niter): fileh = tables.open_file(filename, mode="r") for child in range(nchildren): node = fileh.get_node(fileh.root, 'table' + str(child)) # klass = node._v_attrs.CLASS data = node[:] # Read data assert data is not None # print("data-->", data) show_mem("After reading data. Iter %s" % i) fileh.close() show_mem("After close") def write_xtable(filename, nchildren, niter): class Record(tables.IsDescription): var1 = tables.IntCol(pos=1) var2 = tables.StringCol(length=1, pos=2) var3 = tables.FloatCol(pos=3) for i in range(niter): fileh = tables.open_file(filename, mode="w") for child in range(nchildren): t = fileh.create_table(fileh.root, 'table' + str(child), Record, "child: %d" % child) t.append([[1, "2", 3.]]) t.cols.var1.create_index() show_mem("After creating. Iter %s" % i) fileh.close() show_mem("After close") def read_xtable(filename, nchildren, niter): for i in range(niter): fileh = tables.open_file(filename, mode="r") for child in range(nchildren): node = fileh.get_node(fileh.root, 'table' + str(child)) # klass = node._v_attrs.CLASS # data = node[:] # Read data # print("data-->", data) show_mem("After reading data. Iter %s" % i) fileh.close() show_mem("After close") del node if __name__ == '__main__': import pstats import argparse import profile as prof def _get_parser(): parser = argparse.ArgumentParser( description='Check for PyTables memory leaks.') parser.add_argument('-v', '--verbose', action='store_true', help='enable verbose mode') parser.add_argument('-p', '--profile', action='store_true', help='profile') parser.add_argument('-a', '--array', action='store_true', help='create/read arrays (default)') parser.add_argument('-c', '--carray', action='store_true', help='create/read carrays') parser.add_argument('-e', '--earray', action='store_true', help='create/read earrays') parser.add_argument('-l', '--vlarray', action='store_true', help='create/read vlarrays') parser.add_argument('-t', '--table', action='store_true', help='create/read tables') parser.add_argument('-x', '--indexed-table', action='store_true', dest='xtable', help='create/read indexed-tables') parser.add_argument('-g', '--group', action='store_true', help='create/read groups') parser.add_argument('-r', '--read', action='store_true', help='only read test') parser.add_argument('-w', '--write', action='store_true', help='only write test') parser.add_argument('-n', '--nchildren', type=int, default=1000, help='number of children (%(default)d is the ' 'default)') parser.add_argument('-i', '--niter', type=int, default=3, help='number of iterations (default: %(default)d)') parser.add_argument('filename', help='HDF5 file name') return parser parser = _get_parser() args = parser.parse_args() # set 'array' as default value if no ather option has been specified for name in ('carray', 'earray', 'vlarray', 'table', 'xtable', 'group'): if getattr(args, name): break else: args.array = True filename = args.filename nchildren = args.nchildren niter = args.niter if args.array: fwrite = 'write_array' fread = 'read_array' elif args.carray: fwrite = 'write_carray' fread = 'read_carray' elif args.earray: fwrite = 'write_earray' fread = 'read_earray' elif args.vlarray: fwrite = 'write_vlarray' fread = 'read_vlarray' elif args.table: fwrite = 'write_table' fread = 'read_table' elif args.xtable: fwrite = 'write_xtable' fread = 'read_xtable' elif args.group: fwrite = 'write_group' fread = 'read_group' show_mem("Before open") if args.write: if args.profile: prof.run(str(fwrite)+'(filename, nchildren, niter)', 'write_file.prof') stats = pstats.Stats('write_file.prof') stats.strip_dirs() stats.sort_stats('time', 'calls') if args.verbose: stats.print_stats() else: stats.print_stats(20) else: eval(fwrite+'(filename, nchildren, niter)') if args.read: if args.profile: prof.run(fread+'(filename, nchildren, niter)', 'read_file.prof') stats = pstats.Stats('read_file.prof') stats.strip_dirs() stats.sort_stats('time', 'calls') if args.verbose: print('profile -verbose') stats.print_stats() else: stats.print_stats(20) else: eval(fread+'(filename, nchildren, niter)') PyTables-v.3.1.1/tables/tests/common.py000066400000000000000000000307011231437614300177660ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: 2005-05-24 # Author: Ivan Vilata i Balaguer - ivan@selidor.net # # $Id$ # ######################################################################## """Utilities for PyTables' test suites.""" from __future__ import print_function import os import sys import time import unittest import tempfile import warnings import os.path import numpy import tables verbose = False """Show detailed output of the testing process.""" heavy = False """Run all tests even when they take long to complete.""" show_memory = False """Show the progress of memory consumption.""" if 'verbose' in sys.argv: verbose = True sys.argv.remove('verbose') if 'silent' in sys.argv: # take care of old flag, just in case verbose = False sys.argv.remove('silent') if '--heavy' in sys.argv: heavy = True sys.argv.remove('--heavy') def verbosePrint(string, nonl=False): """Print out the `string` if verbose output is enabled.""" if not verbose: return if nonl: print(string, end=' ') else: print(string) def cleanup(klass): # klass.__dict__.clear() # This is too hard. Don't do that # print("Class attributes deleted") for key in klass.__dict__: if not klass.__dict__[key].__class__.__name__ in ('instancemethod'): klass.__dict__[key] = None def allequal(a, b, flavor="numpy"): """Checks if two numerical objects are equal.""" # print("a-->", repr(a)) # print("b-->", repr(b)) if not hasattr(b, "shape"): # Scalar case return a == b if ((not hasattr(a, "shape") or a.shape == ()) and (not hasattr(b, "shape") or b.shape == ())): return a == b if a.shape != b.shape: if verbose: print("Shape is not equal:", a.shape, "!=", b.shape) return 0 # Way to check the type equality without byteorder considerations if hasattr(b, "dtype") and a.dtype.str[1:] != b.dtype.str[1:]: if verbose: print("dtype is not equal:", a.dtype, "!=", b.dtype) return 0 # Rank-0 case if len(a.shape) == 0: if a[()] == b[()]: return 1 else: if verbose: print("Shape is not equal:", a.shape, "!=", b.shape) return 0 # null arrays if a.size == 0: # len(a) is not correct for generic shapes if b.size == 0: return 1 else: if verbose: print("length is not equal") print("len(a.data) ==>", len(a.data)) print("len(b.data) ==>", len(b.data)) return 0 # Multidimensional case result = (a == b) result = numpy.all(result) if not result and verbose: print("Some of the elements in arrays are not equal") return result def areArraysEqual(arr1, arr2): """Are both `arr1` and `arr2` equal arrays? Arguments can be regular NumPy arrays, chararray arrays or structured arrays (including structured record arrays). They are checked for type and value equality. """ t1 = type(arr1) t2 = type(arr2) if not ((hasattr(arr1, 'dtype') and arr1.dtype == arr2.dtype) or issubclass(t1, t2) or issubclass(t2, t1)): return False return numpy.all(arr1 == arr2) def pyTablesTest(oldmethod): def newmethod(self, *args, **kwargs): self._verboseHeader() try: try: return oldmethod(self, *args, **kwargs) except SkipTest as se: if se.args: msg = se.args[0] else: msg = "" verbosePrint("\nSkipped test: %s" % msg) except self.failureException as fe: if fe.args: msg = fe.args[0] else: msg = "" verbosePrint("\nTest failed: %s" % msg) raise except Exception as exc: cname = exc.__class__.__name__ verbosePrint("\nError in test::\n\n %s: %s" % (cname, exc)) raise finally: verbosePrint('') # separator line between tests newmethod.__name__ = oldmethod.__name__ newmethod.__doc__ = oldmethod.__doc__ return newmethod class SkipTest(Exception): """When this exception is raised, the test is skipped successfully.""" pass class MetaPyTablesTestCase(type): """Metaclass for PyTables test case classes.""" # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/198078 def __new__(class_, name, bases, dict_): newdict = {} for (aname, avalue) in dict_.iteritems(): if callable(avalue) and aname.startswith('test'): avalue = pyTablesTest(avalue) newdict[aname] = avalue return type.__new__(class_, name, bases, newdict) class PyTablesTestCase(unittest.TestCase): """Abstract test case with useful methods.""" __metaclass__ = MetaPyTablesTestCase def _getName(self): """Get the name of this test case.""" return self.id().split('.')[-2] def _getMethodName(self): """Get the name of the method currently running in the test case.""" return self.id().split('.')[-1] def _verboseHeader(self): """Print a nice header for the current test method if verbose.""" if verbose: name = self._getName() methodName = self._getMethodName() title = "Running %s.%s" % (name, methodName) print('%s\n%s' % (title, '-' * len(title))) @classmethod def _testFilename(class_, filename): """Returns an absolute version of the `filename`, taking care of the location of the calling test case class.""" modname = class_.__module__ # When the definitive switch to ``setuptools`` is made, # this should definitely use the ``pkg_resouces`` API:: # # return pkg_resources.resource_filename(modname, filename) # modfile = sys.modules[modname].__file__ dirname = os.path.dirname(modfile) return os.path.join(dirname, filename) def failUnlessWarns(self, warnClass, callableObj, *args, **kwargs): """Fail unless a warning of class `warnClass` is issued. This method will fail if no warning belonging to the given `warnClass` is issued when invoking `callableObj` with arguments `args` and keyword arguments `kwargs`. Warnings of the `warnClass` are hidden, while others are shown. This method returns the value returned by the call to `callableObj`. """ issued = [False] # let's avoid scoping problems ;) # Save the original warning-showing function. showwarning = warnings.showwarning # This warning-showing function hides and takes note # of expected warnings and acts normally on others. def myShowWarning(message, category, filename, lineno, file=None, line=None): if issubclass(category, warnClass): issued[0] = True verbosePrint( "Great! The following ``%s`` was caught::\n" "\n" " %s\n" "\n" "In file ``%s``, line number %d.\n" % (category.__name__, message, filename, lineno)) else: showwarning(message, category, filename, lineno, file, line) # By forcing Python to always show warnings of the wanted class, # and replacing the warning-showing function with a tailored one, # we can check for *every* occurence of the warning. warnings.filterwarnings('always', category=warnClass) warnings.showwarning = myShowWarning try: # Run code and see what happens. ret = callableObj(*args, **kwargs) finally: # Restore the original warning-showing function # and warning filter. warnings.showwarning = showwarning warnings.filterwarnings('default', category=warnClass) if not issued[0]: raise self.failureException( "``%s`` was not issued" % warnClass.__name__) # We only get here if the call to `callableObj` was successful # and it issued the expected warning. return ret assertWarns = failUnlessWarns def failUnlessRaises(self, excClass, callableObj, *args, **kwargs): if not verbose: # Use the ordinary implementation from `unittest.TestCase`. return super(PyTablesTestCase, self).assertRaises( excClass, callableObj, *args, **kwargs) try: callableObj(*args, **kwargs) except excClass as exc: print(( "Great! The following ``%s`` was caught::\n" "\n" " %s\n" % (exc.__class__.__name__, exc))) else: raise self.failureException( "``%s`` was not raised" % excClass.__name__) assertRaises = failUnlessRaises def _checkEqualityGroup(self, node1, node2, hardlink=False): if verbose: print("Group 1:", node1) print("Group 2:", node2) if hardlink: self.assertTrue(node1._v_pathname != node2._v_pathname, "node1 and node2 have the same pathnames.") else: self.assertTrue(node1._v_pathname == node2._v_pathname, "node1 and node2 does not have the same pathnames.") self.assertTrue(node1._v_children == node2._v_children, "node1 and node2 does not have the same children.") def _checkEqualityLeaf(self, node1, node2, hardlink=False): if verbose: print("Leaf 1:", node1) print("Leaf 2:", node2) if hardlink: self.assertTrue(node1._v_pathname != node2._v_pathname, "node1 and node2 have the same pathnames.") else: self.assertTrue(node1._v_pathname == node2._v_pathname, "node1 and node2 does not have the same pathnames.") self.assertTrue(areArraysEqual(node1[:], node2[:]), "node1 and node2 does not have the same values.") class TempFileMixin: def setUp(self): """Set ``h5file`` and ``h5fname`` instance attributes. * ``h5fname``: the name of the temporary HDF5 file. * ``h5file``: the writable, empty, temporary HDF5 file. """ self.h5fname = tempfile.mktemp(suffix='.h5') self.h5file = tables.open_file( self.h5fname, 'w', title=self._getName()) def tearDown(self): """Close ``h5file`` and remove ``h5fname``.""" self.h5file.close() self.h5file = None os.remove(self.h5fname) # comment this for debugging purposes only def _reopen(self, mode='r'): """Reopen ``h5file`` in the specified ``mode``. Returns a true or false value depending on whether the file was reopenend or not. If not, nothing is changed. """ self.h5file.close() self.h5file = tables.open_file(self.h5fname, mode) return True class ShowMemTime(PyTablesTestCase): tref = time.time() """Test for showing memory and time consumption.""" def test00(self): """Showing memory and time consumption.""" # Obtain memory info (only for Linux 2.6.x) for line in open("/proc/self/status"): if line.startswith("VmSize:"): vmsize = int(line.split()[1]) elif line.startswith("VmRSS:"): vmrss = int(line.split()[1]) elif line.startswith("VmData:"): vmdata = int(line.split()[1]) elif line.startswith("VmStk:"): vmstk = int(line.split()[1]) elif line.startswith("VmExe:"): vmexe = int(line.split()[1]) elif line.startswith("VmLib:"): vmlib = int(line.split()[1]) print("\nWallClock time:", time.time() - self.tref) print("Memory usage: ******* %s *******" % self._getName()) print("VmSize: %7s kB\tVmRSS: %7s kB" % (vmsize, vmrss)) print("VmData: %7s kB\tVmStk: %7s kB" % (vmdata, vmstk)) print("VmExe: %7s kB\tVmLib: %7s kB" % (vmexe, vmlib)) ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/tests/create-nested-type.c000066400000000000000000000067021231437614300217760ustar00rootroot00000000000000// This program creates nested types with gaps for testing purposes. // F. Alted 2008-06-27 #include "hdf5.h" #include hid_t create_nested_type(void) { hid_t tid, tid2, tid3; size_t offset, offset2; offset = 1; offset2 = 2; // Create a coumpound type large enough (>= 20) tid = H5Tcreate(H5T_COMPOUND, 21); // Insert an atomic type tid2 = H5Tcopy(H5T_NATIVE_FLOAT); H5Tinsert(tid, "float", offset, tid2); H5Tclose(tid2); offset += 4 + 2; // add two to the offset so as to create gaps // Insert a nested compound tid2 = H5Tcreate(H5T_COMPOUND, 12); tid3 = H5Tcopy(H5T_NATIVE_CHAR); H5Tinsert(tid2, "char", offset2, tid3); H5Tclose(tid3); offset2 += 2; // add one space (for introducing gaps) tid3 = H5Tcopy(H5T_NATIVE_DOUBLE); H5Tinsert(tid2, "double", offset2, tid3); H5Tclose(tid3); offset2 += 5; // add one space (for introducing gaps) H5Tinsert(tid, "compound", offset, tid2); H5Tclose(tid2); offset += 12 + 1; return(tid); } size_t getNestedSizeType(hid_t type_id) { hid_t member_type_id; H5T_class_t class_id; hsize_t i, nfields; size_t itemsize, offset; nfields = H5Tget_nmembers(type_id); offset = 0; // Iterate thru the members for (i=0; i < nfields; i++) { // Get the member type member_type_id = H5Tget_member_type(type_id, i); // Get the HDF5 class class_id = H5Tget_class(member_type_id); if (class_id == H5T_COMPOUND) { // Get the member size for compound type itemsize = getNestedSizeType(member_type_id); } else { // Get the atomic member size itemsize = H5Tget_size(member_type_id); } // Update the offset offset = offset + itemsize; } return(offset); } int main(int argc, char **argv) { char file_name[256], dset_name[256]; hid_t file_id, dataset_id, space_id, plist_id, type_id; hsize_t dims[1], dims_chunk[1]; hsize_t maxdims[1] = { H5S_UNLIMITED }; size_t disk_type_size, computed_type_size, packed_type_size; if (argc < 3) { printf("Pass the name of the file and dataset to check as arguments\n"); return(0); } strcpy(file_name, argv[1]); strcpy(dset_name, argv[2]); dims[0] = 20; // Create 20 records dims_chunk[0] = 10; // Create a new file file_id = H5Fcreate(file_name, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); // Create a simple data space with unlimited size space_id = H5Screate_simple(1, dims, maxdims); // Modify dataset creation properties, i.e. enable chunking plist_id = H5Pcreate (H5P_DATASET_CREATE); H5Pset_chunk(plist_id, 1, dims_chunk); // Get the nested type type_id = create_nested_type(); // Create the dataset dataset_id = H5Dcreate(file_id, dset_name, type_id, space_id, H5P_DEFAULT, plist_id, H5P_DEFAULT); // Free resources H5Sclose(space_id); H5Pclose(plist_id); H5Dclose(dataset_id); H5Fclose(file_id); // Compute type sizes for native and packed disk_type_size = H5Tget_size(type_id); computed_type_size = getNestedSizeType(type_id); H5Tpack(type_id); // pack type packed_type_size = H5Tget_size(type_id); printf("Disk type size: %d\n", disk_type_size); printf("Packed type size: %d (should be %d)\n", packed_type_size, computed_type_size); H5Tclose(type_id); return(1); } PyTables-v.3.1.1/tables/tests/create_backcompat_indexes.py000066400000000000000000000021531231437614300236440ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Script for creating different kind of indexes in a small space as possible. # This is intended for testing purposes. from tables import * class Descr(IsDescription): var1 = StringCol(itemsize=4, shape=(), dflt='', pos=0) var2 = BoolCol(shape=(), dflt=False, pos=1) var3 = Int32Col(shape=(), dflt=0, pos=2) var4 = Float64Col(shape=(), dflt=0.0, pos=3) # Parameters for the table and index creation small_chunkshape = (2,) small_blocksizes = (64, 32, 16, 8) nrows = 43 # Create the new file f = open_file('indexes_2_1.h5', 'w') t1 = f.create_table(f.root, 'table1', Descr) row = t1.row for i in range(nrows): row['var1'] = i row['var2'] = i row['var3'] = i row['var4'] = i row.append() t1.flush() # Do a copy of table1 t1.copy(f.root, 'table2') # Create indexes of all kinds t1.cols.var1.create_index(0, 'ultralight', _blocksizes=small_blocksizes) t1.cols.var2.create_index(3, 'light', _blocksizes=small_blocksizes) t1.cols.var3.create_index(6, 'medium', _blocksizes=small_blocksizes) t1.cols.var4.create_index(9, 'full', _blocksizes=small_blocksizes) f.close() PyTables-v.3.1.1/tables/tests/elink.h5000066400000000000000000000067361231437614300174770ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿØ ÿÿÿÿÿÿÿÿ`ˆ¨ èTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈHEAPXÈpepHˆ¨ (TITLE (CLASSGROUP (VERSION1.0 8PYTABLES_FORMAT_VERSION2.0 ¨TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿx HEAPXppep3HSNODh p (TITLE (CLASSGROUP (VERSION1.0À ¨TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿx HEAPX PSNOD¸à (TITLE (CLASSGROUP (VERSION1.0ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ pep3¸ @pep2elink2.h5/pep PyTables-v.3.1.1/tables/tests/elink2.h5000066400000000000000000000042761231437614300175560ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ¸ÿÿÿÿÿÿÿÿ`ˆ¨ èTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈHEAPXÈpepHˆ¨ (TITLE (CLASSGROUP (VERSION1.0 8PYTABLES_FORMAT_VERSION2.0¨TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈHEAPXpPSNOD0P (TITLE (CLASSGROUP (VERSION1.0 PyTables-v.3.1.1/tables/tests/ex-noattr.h5000066400000000000000000000300661231437614300203070ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ00ÿÿÿÿÿÿÿÿ €`HEAP €detectorcolumnsàTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿX €`HEAP0@!TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ° ÐSNOD€`((Ðp!èÿÿÿÿÿÿÿÿ deflate20030130201707ø /`SNOD TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”0(/HEAP`(X#TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ `((P  ?@4 4ÿ200301302017070P 0TITLEPressure column@SNOD 0 °  20030130201707€  0TITLETDCcount columnXð?@"@0@9@B@€H@P@@T@ Particle: 0Particle: 1Particle: 2Particle: 3Particle: 4Particle: 5Particle: 6Particle: 7Particle: 8Particle: 9x^íÐ;N1@Ñ—‚(RPPPDˆð‡ÐÄD”l! $*IJVÃrXvÆÂ5ÝyÒÈ“ñ•cˆíìtËvžÞ?^Ÿß^nÛ9þµÑëõòïYý6ló“ˆÍªÛþ^E¿ßÏ/Óš—õÏé§9OÝö<Å`0È/“šï¶ùYÎn»íÃÃa¹À¸æó6?ϧ×ü8Åh4ÊIyÊìµùEÄWÍ—)Æãrrù‡2ûm~™/³îv×)&“rïrÿ2m~q×å›ûÓiÑ(:em~»zúCŠÙ¬˜û2Gm¾Œøìòô˜jf!@€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€üƒÀùN9 20030130201707¨  0TITLE Name column`table à/ADCcountTDCcountgrid_i grid_j idnumber @namepressure#  temperature' ?@4 4ÿpressureTDCname8x^íÒ¹nSA€áãx_å‚‚‚ÂB QÄì¡ÉRP¦ ¡A $*ÄøYxš<¢f›ñL|{ªïHѽñü¾qÄ~fõ±Ÿ‹w_¾~úðùòùf?Ç×–"z½^þÚ>tómÄî´.?£££ü2iyyìþ ç©.¯Sôûýü2nù¢›?ÌÙ‹º|;Å`P0jùº›?Ê»·ü8Åp8ÌIù+s£›?ޏjùIŠÑ¨ì\¾¡ÌÍnþ$欮ž¥˹ËùËÜêæO#Îk¾{•b2)E§Ì¦›?Ë]Ûý"ÅtZÌ‹}™;Ýü$â[ÍÓë³Ùõ½ÛÉ·ùV¯ÚîoRÌçóœüÉS6¸ßÍó­þh»¿M±X”«ùÝòòù÷V·ùV×/ëQß§X.—ùåWËóæùVïÕ|s™bµZå·Ÿ-Ï ‡yÞá¼æ»©‰x @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ÿOà/¼™hi PyTables-v.3.1.1/tables/tests/flavored_vlarrays-format1.6.h5000066400000000000000000000305151231437614300236250ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿG1ÿÿÿÿÿÿÿÿ €`HEAP(€vlarray1vlarray2ØTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàƒZdklZdklZlZl Z dk Tdk Tdk Tdk TdklZlZlZdklZdklZdklZlZd klZd klZlZlZd kl Z d k!l"Z"d k#l$Z$dk%l&Z&dk'l(Z(ddddddddddddddddd d!d"d#d$d%d&d'd(d)d*d+d,d-d.d/d0d1d2d3d4d5d6d7d8d9d:d;d<d=d>d?d@dAdBdCdDdEdFdGdHdIdJdKdLdMdNdOdPdQdRdSdTdUdVdWdXdYdZd[d\d]d^gOZ)d_S G)À ˜'Àÿÿÿÿÿÿÿÿ 8shuffledeflate(LÞE (CLASSVLARRAYSNODÐXTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿs`(GCOL  566567756988P ÿÿÿÿÿÿÿÿ 8shuffledeflateh¢ÞE (CLASSVLARRAY+ TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtÓ( (FLAVORnumeric (VERSION1.2 8TITLEragged array of intsxíÐA °Áx£oþ•pHà9.­632V @€ @€ @€ ÐY FÍÊÎl'@€^vÜ  @€ @€ ð“À/ÈBxíÐA ÀOôÍ¿‚žk ´×¢G† @€ @€ @€ @€/ TÖ¨ùòÛ  @€[§B€ @€ @€ü$°O¼K€` (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS°ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I0 sS'complevel' p7 I0 sS'fletcher32' p8 I0 sS'complib' p9 S'zlib' p10 sb. 8PYTABLES_FORMAT_VERSION1.5 (FLAVORpython (VERSION1.2 8TITLEragged array of strings PyTables-v.3.1.1/tables/tests/float.h5000066400000000000000000000112061231437614300174660ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿ`ˆ¨ˆ¨TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ00HEAPX@Èfloat16float32float64longdoublequadprecision(   `<’VDQhSNOD xˆ `0p(  œx’VDQh( ?@4 4ÿ ð’VDQh<@BDE<@BDEF@BDEFGBDEFGHDEFGH€H€?@@@€@ @€?@@@€@ @À@@@@€@ @À@à@@@€@ @À@à@A€@ @À@à@AAð?@@@@ð?@@@@@@@@@@@@@@@@ @@@@@ @"@€ÿ?€@À@€@ @€ÿ?€@@À@@€@@ @@À@ @€@@À@@€@"@ @À@à@À@€@ @À@à@€@€@ @À@à@€@@ÿ?@€@@@@ÿ?@€@@@@€@@€@@@@€@À@€@@@@€@À@@@@@€@À@@ @(OP@@ÿ? à’VDQh( €ppÿ?ä à’VDQh PyTables-v.3.1.1/tables/tests/idx-std-1.x.h5000066400000000000000000000640461231437614300203530ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ hÿÿÿÿÿÿÿÿ €`HEAP €table_i_tableàTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà 7])) vlarray.append([5, 6, 9, 8]) # Now, read it through an iterator: for x in vlarray: print vlarray.name+"["+str(vlarray.nrow)+"]-->", x # Close the file fileh.close() The output of the previous program looks like this:: vlarray1[0]--> [5 6] vlarray1[1]--> [5 6 7] vlarray1[2]--> [5 6 9 8] The `objects` argument is only retained for backwards compatib XÈ0Ð(SþظE (CLASSTABLE (VERSION2.6 (TITLESNODX8ÐTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈ0S€` (TITLE (CLASSGROUP (VERSION1.0 ØFILTERS²ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I00 sS'complevel' p7 I0 sS'fletcher32' p8 I00 sS'complib' p9 S'zlib' p10 sb. 8PYTABLES_FORMAT_VERSION1.6öÿÿÿöÿÿÿ ‡%À ‡%ÀþÿÿÿþÿÿÿpèfÀpèfÀÄ­T@Ä­T@ !]%@!]%@ dpÔ#@dpÔ#@Hk @Hk @ Hné&@Hné&@ ȶD(@ȶD(@ ¸öA%@¸öA%@@#ù @@#ù @ :$)@:$)@ ÀM$%@ÀM$%@|ý0@|ý0@æ9J.@æ9J.@ ˆíW'@ˆíW'@®0E3@®0E3@ †|)@†|)@ ôGš)@ôGš)@P­Z¼1@P­Z¼1@Vã83@Vã83@ äð$@äð$@Þ÷0@Þ÷0@"/43@"/43@=ž¶;@=ž¶;@x1/7@x1/7@Ô#é>@Ô#é>@5Â<8@5Â<8@ü5î3@ü5î3@´JÐ5@´JÐ5@""ü A@ü A@oÛ:@oÛ:@ò}@9@ò}@9@##Ð,¢A@Ð,¢A@àˆQ;@àˆQ;@&&Ò@C@Ò@C@'',~ÄC@,~ÄC@##`¥Û¦A@`¥Û¦A@## ›ŠûA@ ›ŠûA@%%ÀßÛB@ÀßÛB@((àøçD@àøçD@%%€äUØB@€äUØB@&&N[C@N[C@%%Ã'ÂB@Ã'ÂB@!!Ndÿ@@Ndÿ@@--€RÓF@€RÓF@))€ö}¡D@€ö}¡D@))€œN«D@€œN«D@33€yÒãI@€yÒãI@22S;I@S;I@++—v÷E@—v÷E@  ècol1 col2 col3 ?@4 4ÿcol4 ?@4 4ÿ2ÿÿÿÿÿÿÿÿ 0 NROWS@2 0 FIELD_0_NAMEcol1 0 FIELD_1_NAMEcol2 0 FIELD_2_NAMEcol3 0 FIELD_3_NAMEcol4 0FLAVOR numarray 8 AUTOMATIC_INDEX  0 REINDEX à 8 FIELD_0_FILL  8 FIELD_1_FILL  @ FIELD_2_FILL ?@4 4ÿ @ FIELD_3_FILL ?@4 4ÿHEAP0˜^TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ" 7])) vlarray.append([5, 6, 9, 8]) # Now, read it through an iterator: for x in vlarray: print vlarray.name+"["+str(vlarray.nrow)+"]-->", x # Close the file fileh.close() The output of the previous program looks like this:: vlarray1[0]--> [5 6] vlarray1[1]--> [5 6 7] vlarray1[2]--> [5 6 9 8] The `objects` argument is only retained for backwards compatib È^àHEAP0¨`TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿh$ 7])) vlarray.append([5, 6, 9, 8]) # Now, read it through an iterator: for x in vlarray: print vlarray.name+"["+str(vlarray.nrow)+"]-->", x # Close the file fileh.close() The output of the previous program looks like this:: vlarray1[0]--> [5 6] vlarray1[1]--> [5 6 7] vlarray1[2]--> [5 6 9 8] The `objects` argument is only retained for backwards compatib Ø`ÐSNODà!Àˆ01 Dø0  (2ÿÿÿÿÿÿÿÿ2 h: þظE 0CLASS INDEXARRAY¨bØSNOD°%X#  (2ÿÿÿÿÿÿÿÿ2 À& þظE 0CLASS INDEXARRAY€cØTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(ø( h2(2(¸2((˜4 HEAP0Xd ˆdÐ     !+ $%*(& ‡%ÀpèfÀÄ­T@Hk @@#ù @dpÔ#@äð$@ÀM$%@¸öA%@!]%@  !+$ %&(*öÿÿÿþÿÿÿ !"###%%%Hné&@ˆíW'@ȶD(@†|)@:$)@ôGš)@æ9J.@Þ÷0@|ý0@P­Z¼1@"/43@Vã83@®0E3@ü5î3@´JÐ5@x1/7@5Â<8@ò}@9@oÛ:@àˆQ;@=ž¶;@Ô#é>@Ndÿ@@ü A@Ð,¢A@`¥Û¦A@ ›ŠûA@Ã'ÂB@€äUØB@ÀßÛB@&&'())+-23")#'-.1,0/Ò@C@N[C@,~ÄC@àøçD@€ö}¡D@€œN«D@—v÷E@€RÓF@S;I@€yÒãI@")#'-.1,0/TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(à2( 3(03(X3((p4 TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐG 7])) vlarray.append([5, 6, 9, 8]) # Now, read it through an iterator: for x in vlarray: print vlarray.name+"["+str(vlarray.nrow)+"]-->", x # Close the file fileh.close() The output of the previous program looks like this:: vlarray1[0]--> [5 6] vlarray1[1]--> [5 6 7] vlarray1[2]--> [5 6 9 8] The `objects` argument is only retained for backwards compatib Xfð ?@4 4ÿ(2ÿÿÿÿÿÿÿÿ2 `T þظE 0CLASS INDEXARRAYSNODIÀF  (2ÿÿÿÿÿÿÿÿ2 (J þظE 0CLASS INDEXARRAYHgØTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(`1( ˆ1(°1(Ø1((5 TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿP2P €3PÐ3P 4P(À4 col2col48 HTITLE#Indexes container for table /table (CLASSTINDEX (VERSION1.0 ØFILTERS²ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I00 sS'complevel' p7 I0 sS'fletcher32' p8 I00 sS'complib' p9 S'zlib' p10 sb. 0 PATHNAME/tablesortedindicesÀˆ 8TITLEIndex for col2 column (CLASSCINDEX (VERSION1.0 ØFILTERS²ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I00 sS'complevel' p7 I0 sS'fletcher32' p8 I00 sS'complib' p9 S'zlib' p10 sb. 0 DIRTY  0 EXTDIM  0FLAVOR numarray (VERSION1.3 0TITLESorted Values 0 EXTDIM  0FLAVOR numarray (VERSION1.3 0TITLEReverse Indicessortedindices Dø0 8TITLEIndex for col4 column (CLASSCINDEX (VERSION1.0 ØFILTERS²ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I00 sS'complevel' p7 I0 sS'fletcher32' p8 I00 sS'complib' p9 S'zlib' p10 sb. 0 DIRTY  0 EXTDIM  0FLAVOR numarray (VERSION1.3 0TITLESorted Values 0 EXTDIM  0FLAVOR numarray (VERSION1.3 0TITLEReverse Indices PyTables-v.3.1.1/tables/tests/indexes_2_0.h5000066400000000000000000001666011231437614300204720ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ{íÿÿÿÿÿÿÿÿ €`HEAP(€table1table2_i_table1ØTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàˆ8 ôèÜh 8shuffledeflate8áªýÃG (CLASSTABLE (VERSION2.6SNODÜ)K4hÐ(Dh 8shuffledeflate¬!áªýÃG (CLASSTABLE (VERSION2.6TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄ 0á¼DHEAP`(ÀHEAPÀhsÀ  3Áhxí˜û_Ò÷ÇAE¼£‚ˆ* rPA‘/¢¦ÜUE„DA\­UËÖeG[iÍšµ¬­Ym«VëžîQéYZ[›ÖÙjêlUÇÕÊ.;ýùï?àùx<Ÿ¯ï÷—F£-¸®g^ ¸›-M,Ggr{¬ öÙš©1ZÇ×åî6×Ê[Är©så7™?òWhé>3Ö†*cÑØë¸JÈ]b®k¾IQá‰Ü¹¼Càb5>Ø0g‚îÆ¸'5Ÿ3Å®6νEJ Û8 ôÏÆÞà7ZËõïÓ„Ä_PÃÆÏdÂÀ,€çÞ~Bp;¦ÚªœOª>³“PñêLHäñ˜ñŒi=Õ’.oò]Wº?‘'Äx?øeà×ÐW˜_Tœ/Žãð×FÝu°Àº ©r¯Pß%02€ ÆÇÝ‹ìn%MKï²K:úÿrl3€;fJ?n!©58ÚTeBÊÄHÓT/)à<ØÏÙN‰a‘—zûv"à±iý±p?úwÚçQçy¿2ô&^¤/Ão eu;V9û­=©ºÏïïö5œ›+>%Ó!kržWÅëJ;©N–ìÄ,?¤ ]ÿ>)H¢ntMŽ×wE“¯•o‹“C¨•‰µçpŸÇòbØ.Ce†»Xv6Œu&ÆPÊ¥è!÷eÛ´e¢l}­gŽe(ÀçÚòI‰Œ®¦ÈˆWu]ÖâÚŽìDË‘²î¤É¬)Ü=ãù§å4P§,$JŒËƒïdŽ–>-–Æû¢…Éݦ›úô—\ŸææQGB ~ÛŸªrL9…@SAH¼½7ñaí“60Îý´FÐî\•A`;R×AÝø,í?ú)¸`’ QÓÖ² zäßa·âÛÖ Ö«¶ËzS_ê•®Ø#{å 8n°ü`°°©W²ôPÑ £%Ú¥«Òýï˜MQ&ƒÞI·¼TßÁWsO³s5Otþ„QÌq@véí¼‘¸c>sØØ¢*õkãMÀ1™º¡Þµ}„Ø]’' ÅöG• ç·ö”kE@ý†šÖ·:ÏËP Ž£]ƒ›$"øc°ñvL´C;³b+8£ü”¢5yWyL3²‘¡Ÿ·ü•~´Pà×iïzÉÔÁ”Û5’Âߢ'’û¨}–PǹôLÌ-z®Ä9Y,@k¶ ªå ¡Ò® •l¤¿+ÚahÍÚÇΖ¼WΊz¸Ÿw¢æ‘—{%ÌIj$†™eÿ!ÏÂüª.ƒžy§pƒdBá±ãÀ«ÊÉÜ,Â(ÃRüx¨á~˜4OfXt~ˆ¾<ìVYö§…$Šé)T5o ¡~jÞRÑY\ߊÑ^ÕÓ b顊½Íëq¹Á3|=¡Þ­»+ì¶(© ~êØ~3¿ó—h+t€ðRñ3}´Ãß³r)§±ùû 3ȤIz·@ +èò¬± (]£NÈ…_„úzÅg|„Ñ‹ ß¼"Ƶ£#Zm_ ÖZ&’θ¶7ËÛƒö@1Ú¡è¿Ú¼"d\µù5õóÔykÂÞ¸wv(pÖ}…CŒuM}ðÛš%÷d ªzcêZÛÎÚl4œ_1Ü܆7VD-Æž±Ù(“[´¼Ôú±H—츊º z¤:Ä v˜öròÁ5ouÁ ÁlbוSÌ4Áx|xÞÅ´[•Wþ¶ïy»Â®%’b‰bkwºm^;€•ý/i}ÀZà“8uàƒì½´Œw,½qeܱ`(sÊ[nz”ׂ9½Eû•¹øÄʃæÍáTro ‚½¦MÄþ*$=ì$ŠX ²3ŠXLâv’ge(Ÿ‰êõåuÕ*jðô*=ïJŽ–ã÷ú!sdz awØ÷UÍþ%Ô­‘äô¬6üH¯@h¯Án¢ Òoê€Qü.YOuqdÃî ×5Qs²]™NÚ5ÚçʈJQé·-+ \‘-»0Mø»õ¨„”1à¶»Cü|ªzîä£a¯<§É¾ z¤ÿÕdµZŠÒ‚ë?JÏ|…»!º˜Ë¤¥6¾ÈEîbÇë̔òË"²ç\˜Võj˜YOh6Ýñ=Ð$A…CÕ>1º$ùeãEÐû ýÁj ÝUJ1kt—RÀzKéÄÁ’™œh¨Ôö‚úƒþ%B`µû¦óèÉãA2Aÿi‹Ì¯dzÖX†>@`Šø¡?¹€q Îð4vù<ùEÁI·À“îäÖjo€žMføî ßÙÆõ–9Ì_K†| ‚nlßÊ­`ŸJ,*}z,|_Õ³–)aài ò³àwD_zÛš4´þÁeŽ!-ážôÌ¡¢Ë˜"½Ž5ˆÐ€!¡KæÞœIš˜± v$`SÀýfº(‚‡`I¾#pÊZçWÊ‚ÚÒàœ6&VQR½3qªk·@¨Á0WáU[™”3nL¸úOýBH5Á7]«ê ä´üSÌYÞ¼CÃOç`ÈI¯„Vh™î¢í©ÚåÞƒ,`À“wù¶DvAÑ6QA䡬çBª°ãU@&…ëž‚ÿäYc9 »!™àHŸ}œ3ÄÑ ù_E_sGÀjÌä„î+Åz>9i‘ÚÚüIÝ×Fxê òµ¸‰}ô +C¾ûýQÕµúm”o½—ŒKpk­Wê( ¿L”žH2ÑŽuÎVmŒ4Šiz°éjr“KX'ßõ”ŒóãǤÊ‹4ý,vâW v@ÝÃ#—CÏÃ\Xå•ÒüÙ›}¼ó !ˆ.+f$? †äL€óÓ^чýþ(žånì0¤…ü›Ú—_ê<¬;™rƒ¼)ª.ü¿¢#Q©ðÝGÌŽÀÍźìI8K#¥ÂŒæ>PŒ•$;»jw$ Ç®™ƒþυت‡V¥×Bd5‘+Ê\›‘á‚[ lm™e{p?}Ò3ÆÛ.à÷æ*²,Þø–ôL_*ÑKv©j0\ô\wÁD1ÝÆšÐC¬¡¢ãzv˜ý\=F¸\£UvÔm¬4Er¿Ó+PQÞ/œ{ªmØ5u[,>IKH e›­OG„Èõç‹ÂT•Å‚ò£æàK.’ø¼ð…“ù®XUÐx%5Ë„‘ª€sí$êá„wpG4o[ÏÃã½¹ ­ß¥š(%Ývqš3QSÁ¬RßÃÉ'<^ž¢¬Hæ-ù’éjs£²`Sé>™:¬ñ2ÿ6WHUmÇ%‹µX²¡>!> ä’z” qŠäCüÁ̱<{‹œ{­üµ¥ßl srŸµ?m‹i9…{ˆžÕ΄Ý=¨Ûx¤LEXªðüÁoý‹ü?Žyè€` (TITLE (CLASSGROUP (VERSION1.0 8PYTABLES_FORMAT_VERSION2.0àvar1var2var3 var4  ?@4 4ÿÿÿÿÿÿÿÿÿ @TITLEThis is the IndexArray title 0 NROWS@ 0 FIELD_0_NAMEvar1 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar3 0 FIELD_3_NAMEvar4 0 FIELD_0_FILL 8 FIELD_1_FILL 8 FIELD_2_FILL  @ FIELD_3_FILL ?@4 4ÿàvar1var2var3 var4  ?@4 4ÿÿÿÿÿÿÿÿÿ @TITLEThis is the IndexArray title 0 NROWS@ 0 FIELD_0_NAMEvar1 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar3 0 FIELD_3_NAMEvar4 0 FIELD_0_FILL 8 FIELD_1_FILL 8 FIELD_2_FILL  @ FIELD_3_FILL ?@4 4ÿTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ' $*áçEBüâ= ›Ãxc`£!0#5xí–÷?þG×H—DFŠ2.Ñu•p­dǵ®™‘[öÌÅ•=>ÙóÚ{7{gĵWfFW¸²"Y!ã^¾ß?âþ–×psžïÞbbbbÓr¯¿QßT»I;é¿×/å—›¤¼ÈÜbcBþU‹]M¡#þ^AWõ‘*{>v~òí¹ŠÞÜþ‘·¢¦üpÕÍ”oíÚÚÚŒ„j¹6!Ô‡+la}öâêY+Tå‚iÕŸa¦LJ8Éðöµ©ÍèC8:\¬õ ßìÞ^ÉÀWû&¿ Ãý I(»“÷O²Ë#moøÝB0–9¦ Íæ,w-g}”—§0»^±%Ñ»&_'·XQµ³¸!Nõ©¹“â ìÅ»Tbhß¶µ fb0iïÜu¤¢‡x[û¹)¿×´¿Jƒ•NµüJk·]†JL§XZÕÝ¡¿«›þ¨kÜ-¼_/ ‘|mb¥H°GüW¬ ‰jÄH°rÚïFE‘Vî’FFfBWêþri›2TQSP©Aùà ' íŽ]ž‹~ªsÆ®÷ìÔŠ§” û;=ª Fé4à F«—Œ¾4èö›sÜÃ-Ì1~C¹,Dt δ~ìZIÂ⯇ZÙðó9”(È#¤xn·!mqÚ ã=õ§t‘ÑH‚f¦¹å?Ó5³ü¯N*¢@—ù+ª+Yvš—“ºÀ¦wS|éxDïãnWՠί rÝ4ãÊZW1ô¡¦-Ø8 Oƒ·6ÓsÔÕ ôÑZ­uÆûéþØã* Hlmʈãê±6ùŽîFxÉ•4ÉA'o¾XžØ³Ráãc¹]ÁÉæë!++d¨&§v»?òß ¢[ö†tBÙN³¡‰S¤L¡Abœ”2©õ.yŽ2™â³ß›h-¤Wî?X¬)eæ†IMÊyåz"Þ'€ÂTTÇÈ~ÄÕhy$ä¯)%|ý„±M ,§}w+=§¹ß%PäYÀW:Oš­i¿èÒF¤=Œ”qÎeÀBøÝÀ-AùÆtP`r6jy>óO&Aõ¦c ãr¤ÿxhÔ»Æ åft9© d犘¬þútÿ@çý^@,¿g=†·H¹­ ¿†/H±u»‰ç. L»i![G:{°ú…Ó!fü‰ÖOf±‰áÊ_‹¤'XS‡$Ëc¡×²UÍEð2dÍ,¶ËºÁqÇ'êvˆw–&ÎÍ~˲—'R.šÀ>¿I\;W±ˆ %áyØÚ•ñ²/¹ÜCj½KžA¥Õ£¦VÈh.­{YìÉc¼Ï™]3ã»mõ+3Ë9žƒ{µ/ëójs“5¯áR¯Ôæ3Ãúסú[l¼lÄM»óÐ^ºùbv *µÓ>Ô§ÕH*wœ*0+çh€òbÇôÖœi¹:Ö¥,ìüÚ’«ŠºZ_¤<æ›I_8 T6]ßMXçPòß&¼Û½„Ôúv8n¥«+årôbö¼wžâf«ÛËÝÍ+÷ïùÁ‹¢çóÚЉӾ:!PÀQ¬uãÜ0 ÏWtðUÜ"x“¹1ìÐ IË^G‡…sxz Ê"Ô¿È{>n0šC…\äcµ„öy;àš+jIŠtÊ ©9” Æ-îj\SD›‘¹ƒ¢Â0m°=r·ú©õ.ySA}rxpïhV´ÜL …þhÜCÙœ ´uo9ãÀÐ{–G ´«ã ¿äBaD„z„!dÀ(2òíl—¼û£zÌ÷VfGÆfü`È–@ì¹­ŽV‰!²èÎޙƹù«p¿Z^â |S]¥æ¶îj´ª=ç]ºÿ²awûl|S¼\ÐNÆÀOzeQ‘ÅSØËAH]`0•ef¿ßºgYÛîøÙí D<ÏA)ê2{ëõͲLÿ=KºiVÚlíÙPóÇèDT~ Î÷7ã²þ‹y¡½C'®¨ï_¯XŸ;…˜®ñŧðWÚ@ÌŸ'Š)di7ø£>©¿åÂ/å¿ .a#„ÒTµxà„5º¦ò7K‹+ ®Ø´v5ý$kXV(ÍÊ∠©õ.y¢ *«psHÞ#‹˜ïuWüÆs¯¹G0J”'ÖSpçtÎJö‘¾0àÍì¬rH¡gÜDë05È_ðE>¸Vø´~Žbúeø)r¦áÅ–óŠ\ª¯Ø\}°dÍ1Fq8s;啣ьË>e2ù]^¦š^¯äJ¼„Ô¤c^oÛZ%uÆOáÙúŸÛ½ý¥Ê¥¤ƒ0ÿnÃÿ[w²á1ÿósùLBÝbE\ͧóþÔB»ûOTaÄoóQuzGº µ±\îܵ.òõëó:¶Û²Ò­‘žô4hM`«­gÛ`Þ¡ÆK)1** 3Mð³7Ví©à[Ô]{bDÈ›"µÞ%øÌ]rƒšmÒMœñœsG‹vÔ ’4Ó²±¹I3ÓCÎ8µP¡D"_²NA7ÛˆýÏI–?‚û=wÃL Uô|€uÉúF› ¶[â¸åúÍì±õ6ÀŽÅà· Áx1âpúSy¼ý¼’ƒ§±K8§ìÆZºð„м¬}°Ñfñ–²¬Óå'‹ä/’©ÐwJ6;2ª«Š d´ª~°#í¬>Øqe9%#©Pƒª*n„Þ¿S€¿/EÉçÚÅèr»<õCÙkù?JsOS)†[)©ÜéOWòâyÄ0EêÊ·ÂèÏP´ßœ-PÃYÿeÁÉ·‘¬ªÃ3ŸËGŒ@f¯V¥U !ƒàñÞg0ľÁRÆ¢È^þ°»¬Éýþyà‰HÙ ¸B¬í00ÉÞ±%ÍàÚuTú¦¤¥xýÔÌlž^ð‘}Ø.ãr3‘ êÊÐZ³ÛVW¿uÿM!²å.Ñ‰Žžq©Yå=˜`x§…âåHSš{? HêuœšËW³”\R5.P¸— xvÆix`Äçœ>¬snÖ«‘Ñ´Y èbÈ][ûÏïAÒ4ÿ¿[MW©§^+ï¬ëì§5³¼êN•s< ~âp'"¹šbÇ( ˉV·åª—>3gÆŽÙé!„-àð }¨ÎÇs¤·i[Aô ¦{zÄ»\£>&¿ Þ6œbdùc4’ \¬%/È`½gYøæ·âM„;-"WåXÝc£G3çcf¸×ƼØXO. 2õs×Iê÷ÏÃäÿ;G¦áŸƒÕ)˜Í-®:««2^ÍJÑŠˆáÝgþÀµ&5âÞ䡹!¯u4]”€Šš6ä¬ùù-oΨÆoç;h1çDb½ù€¤‚#§úÊgÔ[¶©ÞZB Ò†ïn¦d ¥©Rb[KÇe…nÂ(˜ný ËÛ é@‡%ˆ–2f•ã½]ù¹y}å¸|þùMHà@Ì€TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‹8ˆ8TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿã:@«D8SNOD»Y›WcW S¢3 ûŸ;‚€ãØk6  ›Äð ?@4 4ÿ( ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃGSNOD0k@([?+<XóE@‹B ‹Åð@( ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃG {Æð ?@4 4ÿ(ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃG  ?@4 4ÿÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿªýÃGkÇÐ ;Èð ?@4 4ÿ(ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃG  ?@4 4ÿÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿªýÃG+ÉÐ  ?@4 4ÿÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿªýÃGûÉÐ  ?@4 4ÿÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿªýÃGËÊÐ  ?@4 4ÿ 8shuffledeflate3OªýÃG›Ë°SNOD K>;=Ó9H›C8{A @ 8shuffledeflateGªýÃGK̰TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>²]?«\ TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿJh]HEAPÀhûÌTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûZ@+m8  »Íh ( ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃG#ÐØSNOD0ëh(Ûg«dXsn@ kxíÐ1 Â0Žù×<:h 0*p£ß¶  @€ @€ @€T <xíÐ  ÷Om( 0`À€ 0`À€÷xíÐ1 Â0Žù×<:h 0*p£ß¶  @€ @€ @€T <xíÐ1 Â0Žù×<:h 0*p£ß¶  @€ @€ @€T <xíÐ1 Â0Žù×<:h 0*p£ß¶  @€ @€ @€T <xíÐ1 AFdá_ ˜›û¹K¯J @€ @€ @€@š@_i¯ü!@à#0×giC€@šÀœ1±xíÐ  ëûw6‡D ³  0`À€ 0`À€ 0`À€ 0`ÀÀëßòxíÐ  G²ºÏ"ÔaÀ€ 0`À€ <>!xíÐ  ëûw6‡D ³  0`À€ 0`À€ 0`À€ 0`ÀÀëßòxíÐ  ëûw6‡D ³  0`À€ 0`À€ 0`À€ 0`ÀÀëßòxíÐ  ëûw6‡D ³  0`À€ 0`À€ 0`À€ 0`ÀÀëßò ûÐð@( ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃG (ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃGëÑØ ÃÒ¸ÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿªýÃG (CLASSEARRAY (ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃG{ÓØ SÔ¸ÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿªýÃG (CLASSEARRAY  Õ¸ÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿªýÃG (CLASSEARRAY ÃÕ¸ÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿªýÃG (CLASSEARRAY {Ö 8shuffledeflate³wªýÃG 0CLASS LASTROWARRAYSNOD Ëf»eëYHl8ûi @ 8shuffledeflateƒoªýÃG ×°TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>^?C\ TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ,ð]HEAPÀh»×TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ{ƒ@C8  {Øh  ( ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃGãÚØSNOD0‰(ó‡ÄX‹Ž@#‹ »Ûð@( ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃG  (ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃG«ÜØ ƒÝ¸ ÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿªýÃG (CLASSEARRAY  (ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃG;ÞØ ߸ ÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿªýÃG (CLASSEARRAY Ë߸ ÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿªýÃG (CLASSEARRAY ƒà¸ ÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿªýÃG (CLASSEARRAY   8shuffledeflateË—ªýÃG;á°SNOD ã†Ó…k‚H3Œ8Š @ 8shuffledeflate›ªýÃGëá°TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>Z^?ê\ TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ)‚\HEAPÀh›âTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“£@[­8  [ãh ( ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃGÃåØSNOD0©( ¨Û¤X£®@;« ›æð@( ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃG (ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃG‹çØ cè¸ÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿ ªýÃG (CLASSEARRAY (ÿÿÿÿÿÿÿÿ 8shuffledeflate ÿÿÿÿÿÿÿÿªýÃGéØ óé¸ÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿ ªýÃG (CLASSEARRAY «ê¸ÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿ ªýÃG (CLASSEARRAY cë¸ÿÿÿÿÿÿÿÿ 8shuffledeflateÿÿÿÿÿÿÿÿ ªýÃG (CLASSEARRAY  8shuffledeflateã·ªýÃGì°SNOD û¦ë¥ƒ¢HK¬8+ª @ 8shuffledeflate³¯ªýÃGËì°TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>˜^?)] TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ *var4var1var3var28sortedindicesrangesmrangesboundsaboundszboundsmboundssortedLRindicesLRXk6  8TITLEIndex for var4 column (CLASSINDEX (VERSION2.0 0 FILTERS@ 8 superblocksize@@ 8 blocksize@ 8 slicesize  8 chunksize  8 optlevel  0 DIRTY K4h HTITLE#Indexes container for table /table1 (CLASSTINDEX (VERSION1.0 0 FILTERS@ 0CLASS INDEXARRAY 0 EXTDIM  (VERSION1.0 0TITLESorted Values 0CLASS INDEXARRAY 0 EXTDIM  (VERSION1.0 0TITLEReverse Indices 0CLASS CACHEARRAY 0 EXTDIM  (VERSION1.0 0TITLE Range Values (CLASSEARRAY 0 EXTDIM  (VERSION1.0 0TITLEMedian ranges 0CLASS CACHEARRAY 0 EXTDIM  (VERSION1.0 0TITLEBoundary Values (CLASSEARRAY 0 EXTDIM  (VERSION1.0 0TITLE Start bounds (CLASSEARRAY 0 EXTDIM  (VERSION1.0 0TITLE End bounds (CLASSEARRAY 0 EXTDIM  (VERSION1.0 0TITLEMedian bounds 0CLASS LASTROWARRAY (VERSION1.0 @TITLE Last Row sorted values + bounds 0CLASS LASTROWARRAY (VERSION1.0 @TITLELast Row reverse indicessortedindicesrangesmrangesboundsaboundszboundsmboundssortedLRindicesLRX›WcW 8TITLEIndex for var1 column (CLASSINDEX (VERSION2.0 0 FILTERS@ 8 superblocksize@@ 8 blocksize@ 8 slicesize  8 chunksize  8 optlevel  0 DIRTY  0CLASS INDEXARRAY 0 EXTDIM  (VERSION1.0 0TITLESorted Values 0CLASS INDEXARRAY 0 EXTDIM  (VERSION1.0 0TITLEReverse Indices 0CLASS CACHEARRAY 0 EXTDIM  (VERSION1.0 0TITLE Range Values 0 EXTDIM  (VERSION1.0 0TITLEMedian ranges 0CLASS CACHEARRAY 0 EXTDIM  (VERSION1.0 0TITLEBoundary Values 0 EXTDIM  (VERSION1.0 0TITLE Start bounds 0 EXTDIM  (VERSION1.0 0TITLE End bounds 0 EXTDIM  (VERSION1.0 0TITLEMedian bounds (VERSION1.0 @TITLE Last Row sorted values + bounds 0CLASS LASTROWARRAY (VERSION1.0 @TITLELast Row reverse indicessortedindicesrangesmrangesboundsaboundszboundsmboundssortedLRindicesLRX€ã 8TITLEIndex for var3 column (CLASSINDEX (VERSION2.0 0 FILTERS@ 8 superblocksize@@ 8 blocksize@ 8 slicesize  8 chunksize  8 optlevel  0 DIRTY  0CLASS INDEXARRAY 0 EXTDIM  (VERSION1.0 0TITLESorted Values 0CLASS INDEXARRAY 0 EXTDIM  (VERSION1.0 0TITLEReverse Indices 0CLASS CACHEARRAY 0 EXTDIM  (VERSION1.0 0TITLE Range Values 0 EXTDIM  (VERSION1.0 0TITLEMedian ranges 0CLASS CACHEARRAY 0 EXTDIM  (VERSION1.0 0TITLEBoundary Values 0 EXTDIM  (VERSION1.0 0TITLE Start bounds 0 EXTDIM  (VERSION1.0 0TITLE End bounds 0 EXTDIM  (VERSION1.0 0TITLEMedian bounds 0CLASS LASTROWARRAY (VERSION1.0 @TITLE Last Row sorted values + bounds 0CLASS LASTROWARRAY (VERSION1.0 @TITLELast Row reverse indicessortedindicesrangesmrangesboundsaboundszboundsmboundssortedLRindicesLRX3 ûŸ 8TITLEIndex for var2 column (CLASSINDEX (VERSION2.0 0 FILTERS@ 8 superblocksize@@ 8 blocksize@ 8 slicesize  8 chunksize  8 optlevel  0 DIRTY  0CLASS INDEXARRAY 0 EXTDIM  (VERSION1.0 0TITLESorted Values 0CLASS INDEXARRAY 0 EXTDIM  (VERSION1.0 0TITLEReverse Indices 0CLASS CACHEARRAY 0 EXTDIM  (VERSION1.0 0TITLE Range Values 0 EXTDIM  (VERSION1.0 0TITLEMedian ranges 0CLASS CACHEARRAY 0 EXTDIM  (VERSION1.0 0TITLEBoundary Values 0 EXTDIM  (VERSION1.0 0TITLE Start bounds 0 EXTDIM  (VERSION1.0 0TITLE End bounds 0 EXTDIM  (VERSION1.0 0TITLEMedian bounds 0CLASS LASTROWARRAY (VERSION1.0 @TITLE Last Row sorted values + bounds 0CLASS LASTROWARRAY (VERSION1.0 @TITLELast Row reverse indices PyTables-v.3.1.1/tables/tests/indexes_2_1.h5000066400000000000000000004374701231437614300205000ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ2?ÿÿÿÿÿÿÿÿ` èTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜HEAPX(Ètable1table2_i_table10ˆ¨ (TITLE (CLASSGROUP (VERSION1.0 8PYTABLES_FORMAT_VERSION2.0€ àÈàvar1var2var3 var4  ?@4 4ÿÿÿÿÿÿÿÿÿ¨ á£U!ISNOD1>É1 (CLASSTABLE (VERSION2.6 (TITLE 0 FIELD_0_NAMEvar1 0 FIELD_1_NAMEvar2 0 FIELD_2_NAMEvar3 0 FIELD_3_NAMEvar4 0 FIELD_0_FILL 8 FIELD_1_FILL 8 FIELD_2_FILL  @ FIELD_3_FILL ?@4 4ÿ 0 NROWS@TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñØá01ð?2@3@4@5@6@7@8 @9 "@10 $@11 &@12 (@13 *@14,@15.@160@171@182@193@204@( àvar1var2var3 var4  ?@4 4ÿÿÿÿÿÿÿÿÿ6á£U!I (CLASSTABLE (VERSION2.6 (TITLE 0 NROWS@ 0 FIELD_0_FILL 0 FIELD_0_NAMEvar1 8 FIELD_1_FILL 0 FIELD_1_NAMEvar2 8 FIELD_2_FILL  0 FIELD_2_NAMEvar3 @ FIELD_3_FILL ?@4 4ÿ 0 FIELD_3_NAMEvar4TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñAáñ@ÈTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿyD HEAPX(™@var1var2var3var40Y>y@ HTITLE#Indexes container for table /table1 (CLASSTINDEX (VERSION1.0ÁEpTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿAI@iX8HEAP°hQ[ 0CLASS LASTROWARRAYx30„C#cS4B§·SNOD¹AÁ¿¹4 ±©Á®P 8TITLEIndex for var1 column (CLASSINDEX (VERSION2.1 0 FILTERS@ 8 superblocksize@@ 8 blocksize@ 8 slicesize  8 chunksize  8 optlevel  8 reduction  (ÿÿÿÿÿÿÿÿ 8shuffledeflate!]£U!I‰JØSNOD0aR(¡PaKXAZ@±U 0CLASS INDEXARRAY (VERSION1.0 0TITLESorted Values 0 EXTDIM ¬ È(ÿÿÿÿÿÿÿÿ 8shuffledeflate‰œ£U!I 0CLASS INDEXARRAY (VERSION1.0 @TITLENumber of chunk in table 0 EXTDIM ­ °(ÿÿÿÿÿÿÿÿ 8shuffledeflateYg£U!I 0CLASS CACHEARRAY (VERSION1.0 0TITLE Range Values 0 EXTDIM ­ ˜ÿÿÿÿÿÿÿÿ 8shuffledeflateY”£U!I (CLASSEARRAY (VERSION1.0 0TITLEMedian ranges 0 EXTDIM ® °(ÿÿÿÿÿÿÿÿ 8shuffledeflate‘q£U!I 0CLASS CACHEARRAY (VERSION1.0 0TITLEBoundary Values 0 EXTDIM ¶ ˜ÿÿÿÿÿÿÿÿ 8shuffledeflateÉ{£U!I (CLASSEARRAY (VERSION1.0 0TITLE Start bounds 0 EXTDIM ± ˜ÿÿÿÿÿÿÿÿ 8shuffledeflateùƒ£U!I (CLASSEARRAY (VERSION1.0 0TITLE End bounds 0 EXTDIM ² ˜ÿÿÿÿÿÿÿÿ 8shuffledeflate)Œ£U!I (CLASSEARRAY (VERSION1.0 0TITLEMedian bounds 0 EXTDIM ³ ±Y 8shuffledeflate¯£U!I 0CLASS LASTROWARRAYSNOD ùN9M1HHYW8 Tq\X (VERSION1.0 @TITLE Last Row sorted values + boundsÉ\X 8shuffledeflateA·£U!I!D8\psortedindicesrangesmrangesboundsaboundszboundsmboundssortedLRindicesLRH (VERSION1.0 8TITLELast Row indices 8 nelements  8 nelements TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿYDð¦TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?§TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ЧTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>–§TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿBÔ§TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿA¨TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<N§TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ Á¦ §xc``xc`dbfaecg ´xa [x326153·°dÀ/œ­xc`FF xíÐ1  Iê;ÿƪ£ H 0`À€ 0`À€ 0`À€ 0`À€ã@ŠjxíÐ  VõZ;`ƒ 0`À€ 0`À€ 0`À€ 0ð>0àZ7x3b``Ì3xíÐ  Gïn9ÁüaÀ€ 0`À€ 0`À€ 0`À€ Œ`‚cxíÐ1Ã0,qpÌ¿1„,uÐlF @€@™À•ýÚ%@€ @€ @€ XÕ xíÐ1Ã0,Á1ÿÒ²ÔA³ @€eWök— @€ @€4<°Éšx3444424b03·°403`@EÑÎxc`€xcd„, xcd„, xíÐ  úºN0  0`À€ 0`À€ 0`À€ 0`ÀÀùÀ?ÿxíÐ  úºN°Á€ 0`À€ 0`À€ 0`À€ x@xíÐ  úºN0  0`À€ 0`À€ 0`À€ 0`ÀÀùÀ?ÿxíÐ  úºN°  0`À€ 0`À€ 0`À€ 0`ÀÀùÀ_ÿxíÐ  úºN°  0`À€ 0`À€ 0`À€ 0`ÀÀùÀ_ÿxcd+xc`€xãàäâæáåãgÀ 4]xcd„, xíÐ  ôÿm;` 0`À€ 0`À€ 0`À€ 0`À€óxíÐ  ôo;`ƒ 0`À€ 0`À€ 0`À€ 0ð>0  xã```$ xíÐ1  Oû'6‡D  0`À€ 0`À€ 0`À€ 0`ÀÀùÀ  xíÐ1  }=çßmu4 ä 0`À€ 0`À€ 0`À€ 0``| àxíÐ1  =ç_ou4 ä 0`À€ 0`À€ 0`À€ 0``|  xaÀZxc`€xc` ((©¨ihéè9@(9xãàäâæáåãg Ô]xíÐ1 ±*ª†úWU Ì\˜!3F€ @€ @€Ô lÝ#‡H. u´ <°oxíÐ1 ±Ê©ŒúWU Ì\˜!3F€ @€ @€´ lÛ!ˆ..…´ <`ixc`ˆaxíÐ1 ±J©”úWU Ì\˜!3F€ @€ @€Ô lÝ#‡H. u´ <¾axíÐ11„  øW…æ^:ÿÒT @€ @€ &ГöȾ»ßRG€@šÀoÎËxíÐ11$ øW…æ^:ÿÒT @€ @€ &ГöȾ»ßRG€@šÀߏxc` ›˜08@ºxc`  @q¿P 0 is_csiTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿW¨TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ p¨áAD 0 DIRTY ÂpTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆ@YÕ8HEAP°haØ 0CLASS LASTROWARRAYxc`„$xc`€¹#P 8TITLEIndex for var2 column (CLASSINDEX (VERSION2.1 0 FILTERS@ 8 superblocksize@@ 8 blocksize@ 8 slicesize  8 chunksize  8 optlevel  8 reduction  (ÿÿÿÿÿÿÿÿ 8shuffledeflateÚ£U!IIÇØSNOD09Ï(qÍ!ÈXQ×@™Ò 0CLASS INDEXARRAY (VERSION1.0 0TITLESorted Values 0 EXTDIM ± È(ÿÿÿÿÿÿÿÿ 8shuffledeflate£U!I 0CLASS INDEXARRAY (VERSION1.0 @TITLENumber of chunk in table 0 EXTDIM ² ¸(ÿÿÿÿÿÿÿÿ 8shuffledeflateQä£U!I 0CLASS CACHEARRAY (VERSION1.0 0TITLE Range Values 0 EXTDIM ²  ÿÿÿÿÿÿÿÿ 8shuffledeflateQ £U!I (CLASSEARRAY (VERSION1.0 0TITLEMedian ranges 0 EXTDIM ³ ¸(ÿÿÿÿÿÿÿÿ 8shuffledeflate‰î£U!I 0CLASS CACHEARRAY (VERSION1.0 0TITLEBoundary Values 0 EXTDIM »  ÿÿÿÿÿÿÿÿ 8shuffledeflateÁø £U!I (CLASSEARRAY (VERSION1.0 0TITLE Start bounds 0 EXTDIM ¶  ÿÿÿÿÿÿÿÿ 8shuffledeflateñ £U!I (CLASSEARRAY (VERSION1.0 0TITLE End bounds 0 EXTDIM ·  ÿÿÿÿÿÿÿÿ 8shuffledeflate!  £U!I (CLASSEARRAY (VERSION1.0 0TITLEMedian bounds 0 EXTDIM ¸  8shuffledeflate $£U!I¡Ö°Ù@SNOD ÁËùÉñÄHIÔ8éÐ 0CLASS LASTROWARRAY (VERSION1.0 @TITLE Last Row sorted values + boundsÁÙX 8shuffledeflate9,£U!I)Â8ÙpsortedindicesrangesmrangesboundsaboundszboundsmboundssortedLRindicesLRH (VERSION1.0 8TITLELast Row indices 8 nelements  8 nelements TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ a {¨TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<‘¨TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ h¬TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<© TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<D© TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<€© TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ;ͨ TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ l †¨i4P 0 is_csiTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ¼©TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ È©é¿ Â 0 DIRTY y7pTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿù:@QJ8HEAP°hYM 0CLASS LASTROWARRAYxc`dbfaecgÀxc`€±˜P 8TITLEIndex for var3 column (CLASSINDEX (VERSION2.1 0 FILTERS@ 8 superblocksize@@ 8 blocksize@ 8 slicesize  8 chunksize  8 optlevel  8 reduction   (ÿÿÿÿÿÿÿÿ 8shuffledeflateO£U!IA<ØSNOD01D(iB=XIL@‘G 0CLASS INDEXARRAY (VERSION1.0 0TITLESorted Values 0 EXTDIM ¾ È(ÿÿÿÿÿÿÿÿ 8shuffledeflateyŽ£U!I 0CLASS INDEXARRAY (VERSION1.0 @TITLENumber of chunk in table 0 EXTDIM ¿ ¸ (ÿÿÿÿÿÿÿÿ 8shuffledeflateIY£U!I 0CLASS CACHEARRAY (VERSION1.0 0TITLE Range Values 0 EXTDIM ¿   ÿÿÿÿÿÿÿÿ 8shuffledeflateI†£U!I (CLASSEARRAY (VERSION1.0 0TITLEMedian ranges 0 EXTDIM À ¸ (ÿÿÿÿÿÿÿÿ 8shuffledeflatec£U!I 0CLASS CACHEARRAY (VERSION1.0 0TITLEBoundary Values 0 EXTDIM È   ÿÿÿÿÿÿÿÿ 8shuffledeflate¹m£U!I (CLASSEARRAY (VERSION1.0 0TITLE Start bounds 0 EXTDIM à   ÿÿÿÿÿÿÿÿ 8shuffledeflateéu£U!I (CLASSEARRAY (VERSION1.0 0TITLE End bounds 0 EXTDIM Ä   ÿÿÿÿÿÿÿÿ 8shuffledeflate~£U!I (CLASSEARRAY (VERSION1.0 0TITLEMedian bounds 0 EXTDIM Å   8shuffledeflate™£U!I™K°yN@SNOD ¹@ñ>é9HAI8áE 0CLASS LASTROWARRAY (VERSION1.0 @TITLE Last Row sorted values + bounds¹NX 8shuffledeflate1¡£U!I!78 NpsortedindicesrangesmrangesboundsaboundszboundsmboundssortedLRindicesLRH (VERSION1.0 8TITLELast Row indices 8 nelements  8 nelements TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿY7Ó©TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=ð©TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ iªTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=uªTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>²ªTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ðªTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<-ªTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ k7 å©a©P 0 is_csiTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.«TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ?«á47 0 DIRTY q¬pTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñ¯@É¿8HEAP°hÑ 0CLASS LASTROWARRAYxc` |`à‘a°w9xc¹Äˆ 8TITLEIndex for var4 column (CLASSINDEX (VERSION2.1 0 FILTERS@ 8 superblocksize@@ 8 blocksize@ 8 slicesize  8 chunksize  8 optlevel  8 reduction  9±ð ?@4 4ÿ(ÿÿÿÿÿÿÿÿ 8shuffledeflateAÅ£U!ISNOD0y¹(¡·)²XÁÁ@ù¼ 0CLASS INDEXARRAY (VERSION1.0 0TITLESorted Values 0 EXTDIM Ë Ð@(ÿÿÿÿÿÿÿÿ 8shuffledeflate©£U!I 0CLASS INDEXARRAY (VERSION1.0 @TITLENumber of chunk in table 0 EXTDIM Ì È ?@4 4ÿ(ÿÿÿÿÿÿÿÿ 8shuffledeflateyÏ£U!I 0CLASS CACHEARRAY (VERSION1.0 0TITLE Range Values 0 EXTDIM Ì ° ?@4 4ÿÿÿÿÿÿÿÿÿ 8shuffledeflateyü£U!I (CLASSEARRAY (VERSION1.0 0TITLEMedian ranges 0 EXTDIM Í È ?@4 4ÿ(ÿÿÿÿÿÿÿÿ 8shuffledeflate±Ù£U!I 0CLASS CACHEARRAY (VERSION1.0 0TITLEBoundary Values 0 EXTDIM Õ ° ?@4 4ÿÿÿÿÿÿÿÿÿ 8shuffledeflateéã£U!I (CLASSEARRAY (VERSION1.0 0TITLE Start bounds 0 EXTDIM Ð ° ?@4 4ÿÿÿÿÿÿÿÿÿ 8shuffledeflateì£U!I (CLASSEARRAY (VERSION1.0 0TITLE End bounds 0 EXTDIM Ñ ° ?@4 4ÿÿÿÿÿÿÿÿÿ 8shuffledeflateIô£U!I (CLASSEARRAY (VERSION1.0 0TITLEMedian bounds 0 EXTDIM Ò ÄX ?@4 4ÿ 8shuffledeflateá£U!IÁ°SNOD áµ ´á®H¹¾89» 0CLASS LASTROWARRAY (VERSION1.0 @TITLE Last Row sorted values + boundsÈ@ 8shuffledeflate£U!I¬8sortedindicesrangesmrangesboundsaboundszboundsmboundssortedLRindicesLRHaÄX (VERSION1.0 8TITLELast Row indices 8 nelements  8 nelements Ù©ù« 0 is_csi 0 DIRTY TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQ¬J«TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGs«TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ¬TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿG¬TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿJU¬TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿJŸ¬TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGº«TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿͦ`«TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿé¬TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿঠ­01ð?2@3@4@5@6@7@8 @9 "@10 $@11 &@12 (@13 *@14,@15.@160@171@182@193@204@ PyTables-v.3.1.1/tables/tests/matlab_file.mat000066400000000000000000000036261231437614300210740ustar00rootroot00000000000000MATLAB 7.3 MAT-file, Platform: PCWIN64, Created on: Wed Dec 19 10:25:30 2012 HDF5 schema 1.00 . IM‰HDF  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`ˆ¨ˆ¨TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHHEAPXÈaH ?@4 4ÿ ð?@@šÒP 0 MATLAB_classdoublePSNOD  PyTables-v.3.1.1/tables/tests/nested-type-with-gaps.h5000066400000000000000000000034461231437614300225320ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿ €`HEAP€nestedtypeèTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà €`(øÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÏiH€SNODÐðfloat  compound chardouble ?@4 4ÿ PyTables-v.3.1.1/tables/tests/non-chunked-table.h5000066400000000000000000000140501231437614300216570ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ"ÿÿÿÿÿÿÿÿ HEAP€test_varèTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿX €`HEAP0 "TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ° ÐSNOD(ÐR"(¢DˆSNOD @@@@dstructure variable&"a!?@4 4ÿb!?@4 4ÿc!?@4 4ÿd  PyTables-v.3.1.1/tables/tests/oldflavor_numeric.h5000066400000000000000000003332501231437614300221010ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ¨¶ÿÿÿÿÿÿÿÿ`ˆ¨ èTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ8HEAPXHÈarray1array2carray1carray2vlarray1vlarray2ˆ¨ (TITLE (CLASSGROUP (VERSION1.0 8PYTABLES_FORMAT_VERSION2.0  ?@4 4ÿ P&vN (CLASSARRAY (VERSION2.3``SNODÀ X(°80- (TITLE (FLAVORnumeric @ ?@4 4ÿ P&vN (CLASSARRAY (VERSION2.3 (TITLE (FLAVORpython H(ÿÿÿÿÿÿÿÿP&vN (CLASSCARRAY (VERSION1.0 (TITLE (FLAVORnumeric H(ÿÿÿÿÿÿÿÿP&vN (CLASSCARRAY (VERSION1.0 (TITLE (FLAVORpython @ ÿÿÿÿÿÿÿÿP&vN (CLASSVLARRAY (VERSION1.3 (TITLE (FLAVORnumericTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€¨6GCOL  566567756988P 8ÿÿÿÿÿÿÿÿx.P&vN (CLASSVLARRAY (VERSION1.3 (TITLE (FLAVORpythonTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¨¶000000PyTables-v.3.1.1/tables/tests/python2.h5000066400000000000000000002334521231437614300177750ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ$7ÿÿÿÿÿÿÿÿ`ˆ¨ ðTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ HEAPXHÈarraytableanarrayatableagroupagroup2anarray1X 0TITLE File title (CLASSGROUP (VERSION1.0 8PYTABLES_FORMAT_VERSION2.0 @ ^5sP (CLASSARRAY (VERSION2.3hhSNOD(Ø0H*X8à5  Ð 0TITLEArray example (FLAVORpython ð@var1 ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@^5sP (CLASSTABLE (VERSION2.6 0TITLETable example 0 NROWS@ 0 FIELD_0_NAMEvar1 8 FIELD_0_FILL 8ðˆ¨ 8 testattr@) 8@Ð^5sP (CLASSARRAY (VERSION2.3 0TITLE Array title (FLAVORpython ð@var1 ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@^5sP (CLASSTABLE (VERSION2.6 0TITLE Table title 0 NROWS@ 0 FIELD_0_NAMEvar1 8 FIELD_0_FILL TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ80HEAPX@àanarray1anarray2atable1atable2agroup3 À 0TITLE Group title (CLASSGROUP (VERSION1.0 8 testattr@* @ 8^5sP (CLASSARRAY (VERSION2.3€¨SNOD8¸-(((p0p 0TITLEArray title 1 (FLAVORpython 8 testattr@* 8@H ^5sP (CLASSARRAY (VERSION2.3 0TITLEArray title 2 (FLAVORpython ð@var1 ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@^5sP (CLASSTABLE (VERSION2.6 0TITLETable title 1 0 NROWS@ 0 FIELD_0_NAMEvar1 8 FIELD_0_FILL ¨f0f1  f2À'ˆª*^5sPTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿ(7ª*ÿÿÿÿÿÿÿÿ (CLASSTABLE (VERSION2.6 0TITLETable title 2 0 NROWS@ 0 FIELD_0_NAMEf0 0 FIELD_1_NAMEf1 0 FIELD_2_NAMEf2 8 FIELD_0_FILL @ FIELD_1_FILL   0 FIELD_2_FILL (FLAVORnumpy-°TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ80HEAPX°,Pp*, 0TITLE Group title 2 (CLASSGROUP (VERSION1.0x0°TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿè3HEAPX 0agroup4Hà-0 0TITLE Group title 3 (CLASSGROUP (VERSION1.005°TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿè3HEAPX3PSNOD(1P1p3 0TITLE Group title 4 (CLASSGROUP (VERSION1.0 8@P ^5sP (CLASSARRAY (VERSION2.3 0TITLEArray example (FLAVORpython0Aa PyTables-v.3.1.1/tables/tests/python3.h5000066400000000000000000002334521231437614300177760ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ$7ÿÿÿÿÿÿÿÿ`ˆ¨ ðTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ HEAPXHÈarraytableanarrayatableagroupagroup2anarray1X 0TITLE File title (CLASSGROUP (VERSION1.0 8PYTABLES_FORMAT_VERSION2.0 @ à4sP (CLASSARRAY (VERSION2.3hhSNOD(Ø0H*X8à5  Ð 0TITLEArray example (FLAVORpython ð@var1 ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@à4sP (CLASSTABLE (VERSION2.6 0TITLETable example 0 NROWS@ 0 FIELD_0_NAMEvar1 8 FIELD_0_FILL 8ðˆ¨ 8 testattr@) 8@Ðà4sP (CLASSARRAY (VERSION2.3 0TITLE Array title (FLAVORpython ð@var1 ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@à4sP (CLASSTABLE (VERSION2.6 0TITLE Table title 0 NROWS@ 0 FIELD_0_NAMEvar1 8 FIELD_0_FILL TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ80HEAPX@àanarray1anarray2atable1atable2agroup3 À 0TITLE Group title (CLASSGROUP (VERSION1.0 8 testattr@* @ 8à4sP (CLASSARRAY (VERSION2.3€¨SNOD8¸-(((p0p 0TITLEArray title 1 (FLAVORpython 8 testattr@* 8@H à4sP (CLASSARRAY (VERSION2.3 0TITLEArray title 2 (FLAVORpython ð@var1 ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@à4sP (CLASSTABLE (VERSION2.6 0TITLETable title 1 0 NROWS@ 0 FIELD_0_NAMEvar1 8 FIELD_0_FILL ¨f0f1  f2À'ˆª*à4sPTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿ(7ª*ÿÿÿÿÿÿÿÿ (CLASSTABLE (VERSION2.6 0TITLETable title 2 0 NROWS@ 0 FIELD_0_NAMEf0 0 FIELD_1_NAMEf1 0 FIELD_2_NAMEf2 8 FIELD_0_FILL @ FIELD_1_FILL   0 FIELD_2_FILL (FLAVORnumpy-°TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ80HEAPX°,Pp*, 0TITLEGroup title 2 (CLASSGROUP (VERSION1.0x0°TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿè3HEAPX 0agroup4Hà-0 0TITLEGroup title 3 (CLASSGROUP (VERSION1.005°TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿè3HEAPX3PSNOD(1P1p3 0TITLEGroup title 4 (CLASSGROUP (VERSION1.0 8@P à4sP (CLASSARRAY (VERSION2.3 0TITLEArray example (FLAVORpython0Aa PyTables-v.3.1.1/tables/tests/scalar.h5000066400000000000000000000201461231437614300176310ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ` ÿÿÿÿÿÿÿÿ`ˆ¨ˆ¨TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ0HEAPX Èvariable length string8`Œ…nNˆSNOD  `GCOL Some stringÐ PyTables-v.3.1.1/tables/tests/slink.h5000066400000000000000000000125761231437614300175140ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿxÿÿÿÿÿÿÿÿ`ˆ¨ èTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈHEAPX8Èpeppep2/peparrarr2/arr ˆ¨ (TITLE (CLASSGROUP (VERSION1.0 8PYTABLES_FORMAT_VERSION2.0¨TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿx HEAPXppep3HSNOD h (ÿÿÿÿÿÿÿÿ0ÿÿÿÿÿÿÿÿ0P (TITLE (CLASSGROUP (VERSION1.0À ¨TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿx HEAPX PSNOD¸à (TITLE (CLASSGROUP (VERSION1.0 0@h¶ K (CLASSARRAY (VERSION2.3 (TITLE (FLAVORpython PyTables-v.3.1.1/tables/tests/smpl_SDSextendible.h5000066400000000000000000000141461231437614300221170ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ`ÿÿÿÿÿÿÿÿ €`HEAP€ExtendibleArrayèTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà €`  ( ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ (™ACPSNODÐTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(ˆ(`(°(Ø( PyTables-v.3.1.1/tables/tests/smpl_compound_chunked.h5000066400000000000000000000132161231437614300227440ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿˆÿÿÿÿÿÿÿÿ`ˆ¨ˆ¨TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@HEAPXÈCompoundChunked@TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ P  ð à0žÜ0`0j0aœHello!     BDmÿôH0mô0cˆÿóÿô Hello!     ?uÂúÔ¬@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<ŸmëúÔ¨ÿõPúÓLÿõÐÿô°Hello!     ?õÂ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<ŸmHello!     @8Qì@¨È>BZî@¨È>BZî@¨È>BZî@¨È>BZî@¨È>BZî@¨È>BZî@¨È>BZî@¨È>BZî@¨È>BZî@¨È>BZîmcˆ00#ÿö Hello!     v$@uÂ׈¾@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿm 0tˆìÿ÷°Hello!     lX@™™š0µÜ@´щ7KÇ@´щ7KÇ@´щ7KÇ@´щ7KÇ@´щ7KÇ@´щ7KÇ@´щ7KÇ@´щ7KÇ@´щ7KÇ@´щ7KÇmëëG°à à =C€&àa_name  c_named_name*d  e_name€! f_nameˆ*P !?@4 4ÿg_nameØ v$@uÂ׈¾@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿm 0tˆìÿ÷°Hello!  SNODP PyTables-v.3.1.1/tables/tests/smpl_enum.h5000066400000000000000000000040561231437614300203650ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ(ÿÿÿÿÿÿÿÿ €`HEAP€EnumTestèTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà €`P  REDGREENBLUEWHITEBLACK  “±;CHSNODÐ PyTables-v.3.1.1/tables/tests/smpl_f64be.h5000066400000000000000000000043661231437614300203330ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿ €`HEAP€TestArrayèTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà €`!?@4 4ÿ š±;CpSNODÐ?ð@@@?ð@@@@@@@@@@@@@@@@@@@ @@@@ @" PyTables-v.3.1.1/tables/tests/smpl_f64le.h5000066400000000000000000000043661231437614300203450ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿ €`HEAP€TestArrayèTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà €` ?@4 4ÿ œ±;CpSNODÐð?@@@ð?@@@@@@@@@@@@@@@@@@ @@@@ @"@ PyTables-v.3.1.1/tables/tests/smpl_i32be.h5000066400000000000000000000041761231437614300203300ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿxÿÿÿÿÿÿÿÿ €`HEAP€TestArrayèTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà €`   ¥±;CxSNODÐ PyTables-v.3.1.1/tables/tests/smpl_i32le.h5000066400000000000000000000041761231437614300203420ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿxÿÿÿÿÿÿÿÿ €`HEAP€TestArrayèTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà €`  §±;CxSNODÐ PyTables-v.3.1.1/tables/tests/smpl_i64be.h5000066400000000000000000000043661231437614300203360ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿ €`HEAP€TestArrayèTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà €` @ ¬±;CxSNODÐ PyTables-v.3.1.1/tables/tests/smpl_i64le.h5000066400000000000000000000043661231437614300203500ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿ €`HEAP€TestArrayèTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà €`@ ­±;CxSNODÐ PyTables-v.3.1.1/tables/tests/smpl_unsupptype.h5000066400000000000000000000271361231437614300216610ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿX.ÿÿÿÿÿÿÿÿ €`HEAP€CompoundChunkedèTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà €`X&Ø(û=CˆSNODÐTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ0X0ˆ!GCOL -- Professor Cheng Man-ch'ing(In which case, you deserve what you get.;A combative stance means that you've accepted the contract.5A fight is a contract that takes two people to honor. -- Professor Cheng Man-ch'ing(In which case, you deserve what you get.;A combative stance means that you've accepted the contract.5A fight is a contract that takes two people to honor. -- Professor Cheng Man-ch'ing (In which case, you deserve what you get. ;A combative stance means that you've accepted the contract. 5A fight is a contract that takes two people to honor. -- Professor Cheng Man-ch'ing(In which case, you deserve what you get.;A combative stance means that you've accepted the contract.5A fight is a contract that takes two people to honor. -- Professor Cheng Man-ch'ing(In which case, you deserve what you get.;A combative stance means that you've accepted the contract.5A fight is a contract that takes two people to honor. -- Professor Cheng Man-ch'ing(In which case, you deserve what you get.;A combative stance means that you've accepted the contract.5A fight is a contract that takes two people to honor.ð 5X;X(X XHello!     m5X;X(X XHello!     ?uÂ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿ@ÚÔ,<Ÿm5X ;X (X X Hello!     ?õÂ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿ@ ÚÔ,<Ÿm5X;X(X X Hello!     @8Qì@¨È>BZî@¨È>BZî@¨È>BZî@¨È>BZî@¨È>BZî@¨È>BZî@¨È>BZî@¨È>BZî@¨È>BZî@¨È>BZîm5X;X(X XHello!     @uÂ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿ@°ÚÔ,<Ÿm5X;X(X XHello!     @™™š@´щ7KÇ@´щ7KÇ@´щ7KÇ@´щ7KÇ@´щ7KÇ@´щ7KÇ@´щ7KÇ@´щ7KÇ@´щ7KÇ@´щ7KÇmÐ&a_name  b_name@c_nameDd_nameJd  e_name°! f_name¸P !?@4 4ÿg_name PyTables-v.3.1.1/tables/tests/test_all.py000066400000000000000000000156111231437614300203100ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Run all test cases.""" from __future__ import print_function import re import sys import locale import unittest import platform import numpy import numexpr import tables from tables.req_versions import * from tables.tests import common from tables.utils import detect_number_of_cores def get_tuple_version(hexversion): """Get a tuple from a compact version in hex.""" h = hexversion return(h & 0xff0000) >> 16, (h & 0xff00) >> 8, h & 0xff def suite(): test_modules = [ 'tables.tests.test_attributes', 'tables.tests.test_basics', 'tables.tests.test_create', 'tables.tests.test_backcompat', 'tables.tests.test_types', 'tables.tests.test_lists', 'tables.tests.test_tables', 'tables.tests.test_tablesMD', 'tables.tests.test_array', 'tables.tests.test_earray', 'tables.tests.test_carray', 'tables.tests.test_vlarray', 'tables.tests.test_tree', 'tables.tests.test_timetype', 'tables.tests.test_do_undo', 'tables.tests.test_enum', 'tables.tests.test_nestedtypes', 'tables.tests.test_hdf5compat', 'tables.tests.test_numpy', 'tables.tests.test_queries', 'tables.tests.test_expression', 'tables.tests.test_links', 'tables.tests.test_indexes', 'tables.tests.test_indexvalues', 'tables.tests.test_index_backcompat', # Sub-packages 'tables.nodes.tests.test_filenode', ] # print('-=' * 38) # The test for garbage must be run *in the last place*. # Else, it is not as useful. test_modules.append('tables.tests.test_garbage') alltests = unittest.TestSuite() if common.show_memory: # Add a memory report at the beginning alltests.addTest(unittest.makeSuite(common.ShowMemTime)) for name in test_modules: # Unexpectedly, the following code doesn't seem to work anymore # in python 3 # exec('from %s import suite as test_suite' % name) __import__(name) test_suite = sys.modules[name].suite alltests.addTest(test_suite()) if common.show_memory: # Add a memory report after each test module alltests.addTest(unittest.makeSuite(common.ShowMemTime)) return alltests def print_versions(): """Print all the versions of software that PyTables relies on.""" print('-=' * 38) print("PyTables version: %s" % tables.__version__) print("HDF5 version: %s" % tables.which_lib_version("hdf5")[1]) print("NumPy version: %s" % numpy.__version__) tinfo = tables.which_lib_version("zlib") if numexpr.use_vml: # Get only the main version number and strip out all the rest vml_version = numexpr.get_vml_version() vml_version = re.findall("[0-9.]+", vml_version)[0] vml_avail = "using VML/MKL %s" % vml_version else: vml_avail = "not using Intel's VML/MKL" print("Numexpr version: %s (%s)" % (numexpr.__version__, vml_avail)) if tinfo is not None: print("Zlib version: %s (%s)" % (tinfo[1], "in Python interpreter")) tinfo = tables.which_lib_version("lzo") if tinfo is not None: print("LZO version: %s (%s)" % (tinfo[1], tinfo[2])) tinfo = tables.which_lib_version("bzip2") if tinfo is not None: print("BZIP2 version: %s (%s)" % (tinfo[1], tinfo[2])) tinfo = tables.which_lib_version("blosc") if tinfo is not None: blosc_date = tinfo[2].split()[1] print("Blosc version: %s (%s)" % (tinfo[1], blosc_date)) blosc_cnames = tables.blosc_compressor_list() print("Blosc compressors: %s" % (blosc_cnames,)) try: from Cython.Compiler.Main import Version as Cython_Version print('Cython version: %s' % Cython_Version.version) except: pass print('Python version: %s' % sys.version) print('Platform: %s' % platform.platform()) #if os.name == 'posix': # (sysname, nodename, release, version, machine) = os.uname() # print('Platform: %s-%s' % (sys.platform, machine)) print('Byte-ordering: %s' % sys.byteorder) print('Detected cores: %s' % detect_number_of_cores()) print('Default encoding: %s' % sys.getdefaultencoding()) print('Default locale: (%s, %s)' % locale.getdefaultlocale()) print('-=' * 38) # This should improve readability whan tests are run by CI tools sys.stdout.flush() def print_heavy(heavy): if heavy: print("""\ Performing the complete test suite!""") else: print("""\ Performing only a light (yet comprehensive) subset of the test suite. If you want a more complete test, try passing the --heavy flag to this script (or set the 'heavy' parameter in case you are using tables.test() call). The whole suite will take more than 4 hours to complete on a relatively modern CPU and around 512 MB of main memory.""") print('-=' * 38) def test(verbose=False, heavy=False): """Run all the tests in the test suite. If *verbose* is set, the test suite will emit messages with full verbosity (not recommended unless you are looking into a certain problem). If *heavy* is set, the test suite will be run in *heavy* mode (you should be careful with this because it can take a lot of time and resources from your computer). Return 0 (os.EX_OK) if all tests pass, 1 in case of failure """ print_versions() print_heavy(heavy) # What a context this is! oldverbose, common.verbose = common.verbose, verbose oldheavy, common.heavy = common.heavy, heavy try: result = unittest.TextTestRunner().run(suite()) if result.wasSuccessful(): return 0 else: return 1 finally: common.verbose = oldverbose common.heavy = oldheavy # there are pretty young heavies, too ;) if __name__ == '__main__': hdf5_version = get_tuple_version(tables.which_lib_version("hdf5")[0]) if hdf5_version < min_hdf5_version: print("*Warning*: HDF5 version is lower than recommended: %s < %s" % (hdf5_version, min_hdf5_version)) if numpy.__version__ < min_numpy_version: print("*Warning*: NumPy version is lower than recommended: %s < %s" % (numpy.__version__, min_numpy_version)) # Handle some global flags (i.e. only useful for test_all.py) only_versions = 0 args = sys.argv[:] for arg in args: # Remove 'show-versions' for PyTables 2.3 or higher if arg in ['--print-versions', '--show-versions']: only_versions = True sys.argv.remove(arg) elif arg == '--show-memory': common.show_memory = True sys.argv.remove(arg) print_versions() if not only_versions: print_heavy(common.heavy) unittest.main(defaultTest='tables.tests.suite') PyTables-v.3.1.1/tables/tests/test_array.py000066400000000000000000003100201231437614300206460ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import sys import unittest import os import tempfile import warnings import numpy from tables import * from tables.tests import common from tables.utils import byteorders from tables.tests.common import allequal # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup warnings.resetwarnings() class BasicTestCase(unittest.TestCase): """Basic test for all the supported typecodes present in numpy. All of them are included on pytables. """ endiancheck = False def write_read(self, testarray): a = testarray if common.verbose: print('\n', '-=' * 30) print("Running test for array with type '%s'" % a.dtype.type, end=' ') print("for class check:", self.title) # Create an instance of HDF5 file filename = tempfile.mktemp(".h5") try: with open_file(filename, mode="w") as fileh: root = fileh.root # Create the array under root and name 'somearray' if self.endiancheck and a.dtype.kind != "S": b = a.byteswap() b.dtype = a.dtype.newbyteorder() a = b fileh.create_array(root, 'somearray', a, "Some array") # Re-open the file in read-only mode with open_file(filename, mode="r") as fileh: root = fileh.root # Read the saved array b = root.somearray.read() # Compare them. They should be equal. if common.verbose and not allequal(a, b): print("Write and read arrays differ!") # print("Array written:", a) print("Array written shape:", a.shape) print("Array written itemsize:", a.itemsize) print("Array written type:", a.dtype.type) # print("Array read:", b) print("Array read shape:", b.shape) print("Array read itemsize:", b.itemsize) print("Array read type:", b.dtype.type) if a.dtype.kind != "S": print("Array written byteorder:", a.dtype.byteorder) print("Array read byteorder:", b.dtype.byteorder) # Check strictly the array equality self.assertEqual(a.shape, b.shape) self.assertEqual(a.shape, root.somearray.shape) if a.dtype.kind == "S": self.assertEqual(root.somearray.atom.type, "string") else: self.assertEqual(a.dtype.type, b.dtype.type) self.assertEqual(a.dtype.type, root.somearray.atom.dtype.type) abo = byteorders[a.dtype.byteorder] bbo = byteorders[b.dtype.byteorder] if abo != "irrelevant": self.assertEqual(abo, root.somearray.byteorder) self.assertEqual(bbo, sys.byteorder) if self.endiancheck: self.assertNotEqual(bbo, abo) obj = root.somearray self.assertEqual(obj.flavor, 'numpy') self.assertEqual(obj.shape, a.shape) self.assertEqual(obj.ndim, a.ndim) self.assertEqual(obj.chunkshape, None) if a.shape: nrows = a.shape[0] else: # scalar nrows = 1 self.assertEqual(obj.nrows, nrows) self.assertTrue(allequal(a, b)) finally: # Then, delete the file os.remove(filename) def write_read_out_arg(self, testarray): a = testarray if common.verbose: print('\n', '-=' * 30) print("Running test for array with type '%s'" % a.dtype.type, end=' ') print("for class check:", self.title) # Create an instance of HDF5 file filename = tempfile.mktemp(".h5") try: with open_file(filename, mode="w") as fileh: root = fileh.root # Create the array under root and name 'somearray' if self.endiancheck and a.dtype.kind != "S": b = a.byteswap() b.dtype = a.dtype.newbyteorder() a = b fileh.create_array(root, 'somearray', a, "Some array") # Re-open the file in read-only mode with open_file(filename, mode="r") as fileh: root = fileh.root # Read the saved array b = numpy.empty_like(a, dtype=a.dtype) root.somearray.read(out=b) # Check strictly the array equality self.assertEqual(a.shape, b.shape) self.assertEqual(a.shape, root.somearray.shape) if a.dtype.kind == "S": self.assertEqual(root.somearray.atom.type, "string") else: self.assertEqual(a.dtype.type, b.dtype.type) self.assertEqual(a.dtype.type, root.somearray.atom.dtype.type) abo = byteorders[a.dtype.byteorder] bbo = byteorders[b.dtype.byteorder] if abo != "irrelevant": self.assertEqual(abo, root.somearray.byteorder) self.assertEqual(abo, bbo) if self.endiancheck: self.assertNotEqual(bbo, sys.byteorder) self.assertTrue(allequal(a, b)) finally: # Then, delete the file os.remove(filename) def write_read_atom_shape_args(self, testarray): a = testarray atom = Atom.from_dtype(a.dtype) shape = a.shape byteorder = None if common.verbose: print('\n', '-=' * 30) print("Running test for array with type '%s'" % a.dtype.type, end=' ') print("for class check:", self.title) # Create an instance of HDF5 file filename = tempfile.mktemp(".h5") try: with open_file(filename, mode="w") as fileh: root = fileh.root # Create the array under root and name 'somearray' if self.endiancheck and a.dtype.kind != "S": b = a.byteswap() b.dtype = a.dtype.newbyteorder() if b.dtype.byteorder in ('>', '<'): byteorder = byteorders[b.dtype.byteorder] a = b ptarr = fileh.create_array(root, 'somearray', atom=atom, shape=shape, title="Some array", # specify the byteorder explicitly # since there is no way to deduce # it in this case byteorder=byteorder) self.assertEqual(shape, ptarr.shape) self.assertEqual(atom, ptarr.atom) ptarr[...] = a # Re-open the file in read-only mode with open_file(filename, mode="r") as fileh: root = fileh.root # Read the saved array b = root.somearray.read() # Compare them. They should be equal. if common.verbose and not allequal(a, b): print("Write and read arrays differ!") # print("Array written:", a) print("Array written shape:", a.shape) print("Array written itemsize:", a.itemsize) print("Array written type:", a.dtype.type) # print("Array read:", b) print("Array read shape:", b.shape) print("Array read itemsize:", b.itemsize) print("Array read type:", b.dtype.type) if a.dtype.kind != "S": print("Array written byteorder:", a.dtype.byteorder) print("Array read byteorder:", b.dtype.byteorder) # Check strictly the array equality self.assertEqual(a.shape, b.shape) self.assertEqual(a.shape, root.somearray.shape) if a.dtype.kind == "S": self.assertEqual(root.somearray.atom.type, "string") else: self.assertEqual(a.dtype.type, b.dtype.type) self.assertEqual(a.dtype.type, root.somearray.atom.dtype.type) abo = byteorders[a.dtype.byteorder] bbo = byteorders[b.dtype.byteorder] if abo != "irrelevant": self.assertEqual(abo, root.somearray.byteorder) self.assertEqual(bbo, sys.byteorder) if self.endiancheck: self.assertNotEqual(bbo, abo) obj = root.somearray self.assertEqual(obj.flavor, 'numpy') self.assertEqual(obj.shape, a.shape) self.assertEqual(obj.ndim, a.ndim) self.assertEqual(obj.chunkshape, None) if a.shape: nrows = a.shape[0] else: # scalar nrows = 1 self.assertEqual(obj.nrows, nrows) self.assertTrue(allequal(a, b)) finally: # Then, delete the file os.remove(filename) def setup00_char(self): """Data integrity during recovery (character objects)""" if not isinstance(self.tupleChar, numpy.ndarray): a = numpy.array(self.tupleChar, dtype="S") else: a = self.tupleChar return a def test00_char(self): a = self.setup00_char() self.write_read(a) def test00_char_out_arg(self): a = self.setup00_char() self.write_read_out_arg(a) def test00_char_atom_shape_args(self): a = self.setup00_char() self.write_read_atom_shape_args(a) def test00b_char(self): """Data integrity during recovery (string objects)""" a = self.tupleChar filename = tempfile.mktemp(".h5") try: # Create an instance of HDF5 file with open_file(filename, mode="w") as fileh: fileh.create_array(fileh.root, 'somearray', a, "Some array") # Re-open the file in read-only mode with open_file(filename, mode="r") as fileh: # Read the saved array b = fileh.root.somearray.read() if isinstance(a, bytes): self.assertEqual(type(b), bytes) self.assertEqual(a, b) else: # If a is not a python string, then it should be a list # or ndarray self.assertTrue(type(b) in [list, numpy.ndarray]) finally: # Then, delete the file os.remove(filename) def test00b_char_out_arg(self): """Data integrity during recovery (string objects)""" a = self.tupleChar filename = tempfile.mktemp(".h5") try: # Create an instance of HDF5 file with open_file(filename, mode="w") as fileh: fileh.create_array(fileh.root, 'somearray', a, "Some array") # Re-open the file in read-only mode with open_file(filename, mode="r") as fileh: # Read the saved array b = numpy.empty_like(a) if fileh.root.somearray.flavor != 'numpy': self.assertRaises(TypeError, lambda: fileh.root.somearray.read(out=b)) else: fileh.root.somearray.read(out=b) self.assertTrue(type(b), numpy.ndarray) finally: # Then, delete the file os.remove(filename) def test00b_char_atom_shape_args(self): """Data integrity during recovery (string objects)""" a = self.tupleChar filename = tempfile.mktemp(".h5") try: # Create an instance of HDF5 file with open_file(filename, mode="w") as fileh: nparr = numpy.asarray(a) atom = Atom.from_dtype(nparr.dtype) shape = nparr.shape if nparr.dtype.byteorder in ('>', '<'): byteorder = byteorders[nparr.dtype.byteorder] else: byteorder = None ptarr = fileh.create_array(fileh.root, 'somearray', atom=atom, shape=shape, byteorder=byteorder, title="Some array") self.assertEqual(shape, ptarr.shape) self.assertEqual(atom, ptarr.atom) ptarr[...] = a # Re-open the file in read-only mode with open_file(filename, mode="r") as fileh: # Read the saved array b = numpy.empty_like(a) if fileh.root.somearray.flavor != 'numpy': self.assertRaises(TypeError, lambda: fileh.root.somearray.read(out=b)) else: fileh.root.somearray.read(out=b) self.assertTrue(type(b), numpy.ndarray) finally: # Then, delete the file os.remove(filename) def setup01_char_nc(self): """Data integrity during recovery (non-contiguous character objects)""" if not isinstance(self.tupleChar, numpy.ndarray): a = numpy.array(self.tupleChar, dtype="S") else: a = self.tupleChar if a.ndim == 0: b = a.copy() else: b = a[::2] # Ensure that this numpy string is non-contiguous if len(b) > 1: self.assertEqual(b.flags.contiguous, False) return b def test01_char_nc(self): b = self.setup01_char_nc() self.write_read(b) def test01_char_nc_out_arg(self): b = self.setup01_char_nc() self.write_read_out_arg(b) def test01_char_nc_atom_shape_args(self): b = self.setup01_char_nc() self.write_read_atom_shape_args(b) def test02_types(self): """Data integrity during recovery (numerical types)""" typecodes = ['int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64', 'float32', 'float64', 'complex64', 'complex128'] for name in ('float16', 'float96', 'float128', 'complex192', 'complex256'): atomname = name.capitalize() + 'Atom' if atomname in globals(): typecodes.append(name) for typecode in typecodes: a = numpy.array(self.tupleInt, typecode) self.write_read(a) b = numpy.array(self.tupleInt, typecode) self.write_read_out_arg(b) c = numpy.array(self.tupleInt, typecode) self.write_read_atom_shape_args(c) def test03_types_nc(self): """Data integrity during recovery (non-contiguous numerical types)""" typecodes = ['int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64', 'float32', 'float64', 'complex64', 'complex128', ] for name in ('float16', 'float96', 'float128', 'complex192', 'complex256'): atomname = name.capitalize() + 'Atom' if atomname in globals(): typecodes.append(name) for typecode in typecodes: a = numpy.array(self.tupleInt, typecode) if a.ndim == 0: b1 = a.copy() b2 = a.copy() b3 = a.copy() else: b1 = a[::2] b2 = a[::2] b3 = a[::2] # Ensure that this array is non-contiguous if len(b1) > 1: self.assertEqual(b1.flags.contiguous, False) if len(b2) > 1: self.assertEqual(b2.flags.contiguous, False) if len(b3) > 1: self.assertEqual(b3.flags.contiguous, False) self.write_read(b1) self.write_read_out_arg(b2) self.write_read_atom_shape_args(b3) class Basic0DOneTestCase(BasicTestCase): # Scalar case title = "Rank-0 case 1" tupleInt = 3 tupleChar = b"3" endiancheck = True class Basic0DTwoTestCase(BasicTestCase): # Scalar case title = "Rank-0 case 2" tupleInt = 33 tupleChar = b"33" endiancheck = True class Basic1DZeroTestCase(BasicTestCase): # This test case is not supported by PyTables (HDF5 limitations) # 1D case title = "Rank-1 case 0" tupleInt = () tupleChar = () endiancheck = False class Basic1DOneTestCase(BasicTestCase): "Method doc" # 1D case title = "Rank-1 case 1" tupleInt = (3,) tupleChar = (b"a",) endiancheck = True class Basic1DTwoTestCase(BasicTestCase): # 1D case title = "Rank-1 case 2" tupleInt = (3, 4) tupleChar = (b"aaa",) endiancheck = True class Basic1DThreeTestCase(BasicTestCase): # 1D case title = "Rank-1 case 3" tupleInt = (3, 4, 5) tupleChar = (b"aaa", b"bbb",) endiancheck = True class Basic2DOneTestCase(BasicTestCase): # 2D case title = "Rank-2 case 1" tupleInt = numpy.array(numpy.arange((4)**2)) tupleInt.shape = (4,)*2 tupleChar = numpy.array(["abc"]*3**2, dtype="S3") tupleChar.shape = (3,)*2 endiancheck = True class Basic2DTwoTestCase(BasicTestCase): # 2D case, with a multidimensional dtype title = "Rank-2 case 2" tupleInt = numpy.array(numpy.arange((4)), dtype=(numpy.int_, (4,))) tupleChar = numpy.array(["abc"]*3, dtype=("S3", (3,))) endiancheck = True class Basic10DTestCase(BasicTestCase): # 10D case title = "Rank-10 test" tupleInt = numpy.array(numpy.arange((2)**10)) tupleInt.shape = (2,)*10 tupleChar = numpy.array( ["abc"]*2**10, dtype="S3") tupleChar.shape = (2,)*10 endiancheck = True class Basic32DTestCase(BasicTestCase): # 32D case (maximum) title = "Rank-32 test" tupleInt = numpy.array((32,)) tupleInt.shape = (1,)*32 tupleChar = numpy.array(["121"], dtype="S3") tupleChar.shape = (1,)*32 class ReadOutArgumentTests(unittest.TestCase): def setUp(self): self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode='w') self.size = 1000 def tearDown(self): self.fileh.close() os.remove(self.file) def create_array(self): array = numpy.arange(self.size, dtype='f8') disk_array = self.fileh.create_array('/', 'array', array) return array, disk_array def test_read_entire_array(self): array, disk_array = self.create_array() out_buffer = numpy.empty((self.size, ), 'f8') disk_array.read(out=out_buffer) numpy.testing.assert_equal(out_buffer, array) def test_read_contiguous_slice1(self): array, disk_array = self.create_array() out_buffer = numpy.arange(self.size, dtype='f8') out_buffer = numpy.random.permutation(out_buffer) out_buffer_orig = out_buffer.copy() start = self.size // 2 disk_array.read(start=start, stop=self.size, out=out_buffer[start:]) numpy.testing.assert_equal(out_buffer[start:], array[start:]) numpy.testing.assert_equal(out_buffer[:start], out_buffer_orig[:start]) def test_read_contiguous_slice2(self): array, disk_array = self.create_array() out_buffer = numpy.arange(self.size, dtype='f8') out_buffer = numpy.random.permutation(out_buffer) out_buffer_orig = out_buffer.copy() start = self.size // 4 stop = self.size - start disk_array.read(start=start, stop=stop, out=out_buffer[start:stop]) numpy.testing.assert_equal(out_buffer[start:stop], array[start:stop]) numpy.testing.assert_equal(out_buffer[:start], out_buffer_orig[:start]) numpy.testing.assert_equal(out_buffer[stop:], out_buffer_orig[stop:]) def test_read_non_contiguous_slice_contiguous_buffer(self): array, disk_array = self.create_array() out_buffer = numpy.empty((self.size // 2, ), dtype='f8') disk_array.read(start=0, stop=self.size, step=2, out=out_buffer) numpy.testing.assert_equal(out_buffer, array[0:self.size:2]) def test_read_non_contiguous_buffer(self): array, disk_array = self.create_array() out_buffer = numpy.empty((self.size, ), 'f8') out_buffer_slice = out_buffer[0:self.size:2] # once Python 2.6 support is dropped, this could change # to assertRaisesRegexp to check exception type and message at once self.assertRaises(ValueError, disk_array.read, 0, self.size, 2, out_buffer_slice) try: disk_array.read(0, self.size, 2, out_buffer_slice) except ValueError as exc: self.assertEqual('output array not C contiguous', str(exc)) def test_buffer_too_small(self): array, disk_array = self.create_array() out_buffer = numpy.empty((self.size // 2, ), 'f8') self.assertRaises(ValueError, disk_array.read, 0, self.size, 1, out_buffer) try: disk_array.read(0, self.size, 1, out_buffer) except ValueError as exc: self.assertTrue('output array size invalid, got' in str(exc)) def test_buffer_too_large(self): array, disk_array = self.create_array() out_buffer = numpy.empty((self.size + 1, ), 'f8') self.assertRaises(ValueError, disk_array.read, 0, self.size, 1, out_buffer) try: disk_array.read(0, self.size, 1, out_buffer) except ValueError as exc: self.assertTrue('output array size invalid, got' in str(exc)) class SizeOnDiskInMemoryPropertyTestCase(unittest.TestCase): def setUp(self): self.array_size = (10, 10) self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w") self.array = self.fileh.create_array( '/', 'somearray', numpy.zeros(self.array_size, 'i4')) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def test_all_zeros(self): self.assertEqual(self.array.size_on_disk, 10 * 10 * 4) self.assertEqual(self.array.size_in_memory, 10 * 10 * 4) class UnalignedAndComplexTestCase(unittest.TestCase): """Basic test for all the supported typecodes present in numpy. Most of them are included on PyTables. """ def setUp(self): # Create an instance of HDF5 file self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w") self.root = self.fileh.root def tearDown(self): self.fileh.close() # Then, delete the file os.remove(self.file) common.cleanup(self) def write_read(self, testArray): if common.verbose: print('\n', '-=' * 30) print("\nRunning test for array with type '%s'" % testArray.dtype.type) # Create the array under root and name 'somearray' a = testArray if self.endiancheck: byteorder = {"little": "big", "big": "little"}[sys.byteorder] else: byteorder = sys.byteorder self.fileh.create_array(self.root, 'somearray', a, "Some array", byteorder=byteorder) if self.reopen: self.fileh.close() # Re-open the file in read-only mode self.fileh = open_file(self.file, mode="r") self.root = self.fileh.root # Read the saved array b = self.root.somearray.read() # Get an array to be compared in the correct byteorder c = a.newbyteorder(byteorder) # Compare them. They should be equal. if not allequal(c, b) and common.verbose: print("Write and read arrays differ!") print("Array written:", a) print("Array written shape:", a.shape) print("Array written itemsize:", a.itemsize) print("Array written type:", a.dtype.type) print("Array read:", b) print("Array read shape:", b.shape) print("Array read itemsize:", b.itemsize) print("Array read type:", b.dtype.type) # Check strictly the array equality self.assertEqual(a.shape, b.shape) self.assertEqual(a.shape, self.root.somearray.shape) if a.dtype.byteorder != "|": self.assertEqual(a.dtype, b.dtype) self.assertEqual(a.dtype, self.root.somearray.atom.dtype) self.assertEqual(byteorders[b.dtype.byteorder], sys.byteorder) self.assertEqual(self.root.somearray.byteorder, byteorder) self.assertTrue(allequal(c, b)) def test01_signedShort_unaligned(self): "Checking an unaligned signed short integer array" r = numpy.rec.array(b'a'*200, formats='i1,f4,i2', shape=10) a = r["f2"] # Ensure that this array is non-aligned self.assertEqual(a.flags.aligned, False) self.assertEqual(a.dtype.type, numpy.int16) self.write_read(a) def test02_float_unaligned(self): "Checking an unaligned single precision array" r = numpy.rec.array(b'a'*200, formats='i1,f4,i2', shape=10) a = r["f1"] # Ensure that this array is non-aligned self.assertEqual(a.flags.aligned, 0) self.assertEqual(a.dtype.type, numpy.float32) self.write_read(a) def test03_byte_offset(self): "Checking an offsetted byte array" r = numpy.arange(100, dtype=numpy.int8) r.shape = (10, 10) a = r[2] self.write_read(a) def test04_short_offset(self): "Checking an offsetted unsigned short int precision array" r = numpy.arange(100, dtype=numpy.uint32) r.shape = (10, 10) a = r[2] self.write_read(a) def test05_int_offset(self): "Checking an offsetted integer array" r = numpy.arange(100, dtype=numpy.int32) r.shape = (10, 10) a = r[2] self.write_read(a) def test06_longlongint_offset(self): "Checking an offsetted long long integer array" r = numpy.arange(100, dtype=numpy.int64) r.shape = (10, 10) a = r[2] self.write_read(a) def test07_float_offset(self): "Checking an offsetted single precision array" r = numpy.arange(100, dtype=numpy.float32) r.shape = (10, 10) a = r[2] self.write_read(a) def test08_double_offset(self): "Checking an offsetted double precision array" r = numpy.arange(100, dtype=numpy.float64) r.shape = (10, 10) a = r[2] self.write_read(a) def test09_float_offset_unaligned(self): "Checking an unaligned and offsetted single precision array" r = numpy.rec.array(b'a'*200, formats='i1,3f4,i2', shape=10) a = r["f1"][3] # Ensure that this array is non-aligned self.assertEqual(a.flags.aligned, False) self.assertEqual(a.dtype.type, numpy.float32) self.write_read(a) def test10_double_offset_unaligned(self): "Checking an unaligned and offsetted double precision array" r = numpy.rec.array(b'a'*400, formats='i1,3f8,i2', shape=10) a = r["f1"][3] # Ensure that this array is non-aligned self.assertEqual(a.flags.aligned, False) self.assertEqual(a.dtype.type, numpy.float64) self.write_read(a) def test11_int_byteorder(self): "Checking setting data with different byteorder in a range (integer)" # Open a new empty HDF5 file file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Save an array with the reversed byteorder on it a = numpy.arange(25, dtype=numpy.int32).reshape(5, 5) a = a.byteswap() a = a.newbyteorder() array = fileh.create_array(fileh.root, 'array', a, "byteorder (int)") # Read a subarray (got an array with the machine byteorder) b = array[2:4, 3:5] b = b.byteswap() b = b.newbyteorder() # Set this subarray back to the array array[2:4, 3:5] = b b = b.byteswap() b = b.newbyteorder() # Set this subarray back to the array array[2:4, 3:5] = b # Check that the array is back in the correct byteorder c = array[...] if common.verbose: print("byteorder of array on disk-->", array.byteorder) print("byteorder of subarray-->", b.dtype.byteorder) print("subarray-->", b) print("retrieved array-->", c) self.assertTrue(allequal(a, c)) # Close the file fileh.close() # Then, delete the file os.remove(file) def test12_float_byteorder(self): "Checking setting data with different byteorder in a range (float)" # Open a new empty HDF5 file file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Save an array with the reversed byteorder on it a = numpy.arange(25, dtype=numpy.float64).reshape(5, 5) a = a.byteswap() a = a.newbyteorder() array = fileh.create_array(fileh.root, 'array', a, "byteorder (float)") # Read a subarray (got an array with the machine byteorder) b = array[2:4, 3:5] b = b.byteswap() b = b.newbyteorder() # Set this subarray back to the array array[2:4, 3:5] = b b = b.byteswap() b = b.newbyteorder() # Set this subarray back to the array array[2:4, 3:5] = b # Check that the array is back in the correct byteorder c = array[...] if common.verbose: print("byteorder of array on disk-->", array.byteorder) print("byteorder of subarray-->", b.dtype.byteorder) print("subarray-->", b) print("retrieved array-->", c) self.assertTrue(allequal(a, c)) # Close the file fileh.close() # Then, delete the file os.remove(file) class ComplexNotReopenNotEndianTestCase(UnalignedAndComplexTestCase): endiancheck = False reopen = False class ComplexReopenNotEndianTestCase(UnalignedAndComplexTestCase): endiancheck = False reopen = True class ComplexNotReopenEndianTestCase(UnalignedAndComplexTestCase): endiancheck = True reopen = False class ComplexReopenEndianTestCase(UnalignedAndComplexTestCase): endiancheck = True reopen = True class GroupsArrayTestCase(unittest.TestCase): """This test class checks combinations of arrays with groups.""" def test00_iterativeGroups(self): """Checking combinations of arrays with groups.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00_iterativeGroups..." % self.__class__.__name__) # Open a new empty HDF5 file file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Get the root group group = fileh.root # Set the type codes to test # The typecodes below does expose an ambiguity that is reported in: # http://projects.scipy.org/scipy/numpy/ticket/283 and # http://projects.scipy.org/scipy/numpy/ticket/290 typecodes = ['b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'f', 'd', 'F', 'D'] if 'Float16Atom' in globals(): typecodes.append('e') if 'Float96Atom' in globals() or 'Float128Atom' in globals(): typecodes.append('g') if 'Complex192Atom' in globals() or 'Complex256Atom' in globals(): typecodes.append('G') for i, typecode in enumerate(typecodes): a = numpy.ones((3,), typecode) dsetname = 'array_' + typecode if common.verbose: print("Creating dataset:", group._g_join(dsetname)) fileh.create_array(group, dsetname, a, "Large array") group = fileh.create_group(group, 'group' + str(i)) # Close the file fileh.close() # Open the previous HDF5 file in read-only mode fileh = open_file(file, mode="r") # Get the root group group = fileh.root # Get the metadata on the previosly saved arrays for i in range(len(typecodes)): # Create an array for later comparison a = numpy.ones((3,), typecodes[i]) # Get the dset object hanging from group dset = getattr(group, 'array_' + typecodes[i]) # Get the actual array b = dset.read() if common.verbose: print("Info from dataset:", dset._v_pathname) print(" shape ==>", dset.shape, end=' ') print(" type ==> %s" % dset.atom.dtype) print("Array b read from file. Shape: ==>", b.shape, end=' ') print(". Type ==> %s" % b.dtype) self.assertEqual(a.shape, b.shape) self.assertEqual(a.dtype, b.dtype) self.assertTrue(allequal(a, b)) # Iterate over the next group group = getattr(group, 'group' + str(i)) # Close the file fileh.close() # Then, delete the file os.remove(file) del a, b, fileh def test01_largeRankArrays(self): """Checking creation of large rank arrays (0 < rank <= 32) It also uses arrays ranks which ranges until maxrank. """ # maximum level of recursivity (deepest group level) achieved: # maxrank = 32 (for a effective maximum rank of 32) # This limit is due to HDF5 library limitations. minrank = 1 maxrank = 32 if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_largeRankArrays..." % self.__class__.__name__) print("Maximum rank for tested arrays:", maxrank) # Open a new empty HDF5 file # file = tempfile.mktemp(".h5") file = "test_array.h5" fileh = open_file(file, mode="w") group = fileh.root if common.verbose: print("Rank array writing progress: ", end=' ') for rank in range(minrank, maxrank + 1): # Create an array of integers, with incrementally bigger ranges a = numpy.ones((1,) * rank, numpy.int32) if common.verbose: print("%3d," % (rank), end=' ') fileh.create_array(group, "array", a, "Rank: %s" % rank) group = fileh.create_group(group, 'group' + str(rank)) # Flush the buffers fileh.flush() # Close the file fileh.close() # Open the previous HDF5 file in read-only mode fileh = open_file(file, mode="r") group = fileh.root if common.verbose: print() print("Rank array reading progress: ") # Get the metadata on the previosly saved arrays for rank in range(minrank, maxrank + 1): # Create an array for later comparison a = numpy.ones((1,) * rank, numpy.int32) # Get the actual array b = group.array.read() if common.verbose: print("%3d," % (rank), end=' ') if common.verbose and not allequal(a, b): print("Info from dataset:", dset._v_pathname) print(" Shape: ==>", dset.shape, end=' ') print(" typecode ==> %c" % dset.typecode) print("Array b read from file. Shape: ==>", b.shape, end=' ') print(". Type ==> %c" % b.dtype) self.assertEqual(a.shape, b.shape) self.assertEqual(a.dtype, b.dtype) self.assertTrue(allequal(a, b)) # print(fileh) # Iterate over the next group group = fileh.get_node(group, 'group' + str(rank)) if common.verbose: print() # This flush the stdout buffer # Close the file fileh.close() # Delete the file os.remove(file) class CopyTestCase(unittest.TestCase): def test01_copy(self): """Checking Array.copy() method.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_copy..." % self.__class__.__name__) # Create an instance of an HDF5 file file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an Array arr = numpy.array([[456, 2], [3, 457]], dtype='int16') array1 = fileh.create_array(fileh.root, 'array1', arr, "title array1") # Copy to another Array array2 = array1.copy('/', 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("array1-->", array1.read()) print("array2-->", array2.read()) # print("dirs-->", dir(array1), dir(array2)) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal self.assertTrue(allequal(array1.read(), array2.read())) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.flavor, array2.flavor) self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(array1.title, array2.title) # Close the file fileh.close() os.remove(file) def test02_copy(self): """Checking Array.copy() method (where specified)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_copy..." % self.__class__.__name__) # Create an instance of an HDF5 file file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an Array arr = numpy.array([[456, 2], [3, 457]], dtype='int16') array1 = fileh.create_array(fileh.root, 'array1', arr, "title array1") # Copy to another Array group1 = fileh.create_group("/", "group1") array2 = array1.copy(group1, 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.group1.array2 if common.verbose: print("array1-->", array1.read()) print("array2-->", array2.read()) # print("dirs-->", dir(array1), dir(array2)) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal self.assertTrue(allequal(array1.read(), array2.read())) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.flavor, array2.flavor) self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(array1.title, array2.title) # Close the file fileh.close() os.remove(file) def test03_copy(self): """Checking Array.copy() method (checking title copying)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_copy..." % self.__class__.__name__) # Create an instance of an HDF5 file file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an Array arr = numpy.array([[456, 2], [3, 457]], dtype='int16') array1 = fileh.create_array(fileh.root, 'array1', arr, "title array1") # Append some user attrs array1.attrs.attr1 = "attr1" array1.attrs.attr2 = 2 # Copy it to another Array array2 = array1.copy('/', 'array2', title="title array2") if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 # Assert user attributes if common.verbose: print("title of destination array-->", array2.title) self.assertEqual(array2.title, "title array2") # Close the file fileh.close() os.remove(file) def test04_copy(self): """Checking Array.copy() method (user attributes copied)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05_copy..." % self.__class__.__name__) # Create an instance of an HDF5 file file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an Array arr = numpy.array([[456, 2], [3, 457]], dtype='int16') array1 = fileh.create_array(fileh.root, 'array1', arr, "title array1") # Append some user attrs array1.attrs.attr1 = "attr1" array1.attrs.attr2 = 2 # Copy it to another Array array2 = array1.copy('/', 'array2', copyuserattrs=1) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Assert user attributes self.assertEqual(array2.attrs.attr1, "attr1") self.assertEqual(array2.attrs.attr2, 2) # Close the file fileh.close() os.remove(file) def test04b_copy(self): """Checking Array.copy() method (user attributes not copied)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05b_copy..." % self.__class__.__name__) # Create an instance of an HDF5 file file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an Array arr = numpy.array([[456, 2], [3, 457]], dtype='int16') array1 = fileh.create_array(fileh.root, 'array1', arr, "title array1") # Append some user attrs array1.attrs.attr1 = "attr1" array1.attrs.attr2 = 2 # Copy it to another Array array2 = array1.copy('/', 'array2', copyuserattrs=0) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Assert user attributes self.assertEqual(hasattr(array2.attrs, "attr1"), 0) self.assertEqual(hasattr(array2.attrs, "attr2"), 0) # Close the file fileh.close() os.remove(file) class CloseCopyTestCase(CopyTestCase): close = 1 class OpenCopyTestCase(CopyTestCase): close = 0 class CopyIndexTestCase(unittest.TestCase): def test01_index(self): """Checking Array.copy() method with indexes.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_index..." % self.__class__.__name__) # Create an instance of an HDF5 Array file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a numpy r = numpy.arange(200, dtype='int32') r.shape = (100, 2) # Save it in a array: array1 = fileh.create_array(fileh.root, 'array1', r, "title array1") # Copy to another array array2 = array1.copy("/", 'array2', start=self.start, stop=self.stop, step=self.step) if common.verbose: print("array1-->", array1.read()) print("array2-->", array2.read()) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal r2 = r[self.start:self.stop:self.step] self.assertTrue(allequal(r2, array2.read())) # Assert the number of rows in array if common.verbose: print("nrows in array2-->", array2.nrows) print("and it should be-->", r2.shape[0]) self.assertEqual(r2.shape[0], array2.nrows) # Close the file fileh.close() os.remove(file) def test02_indexclosef(self): """Checking Array.copy() method with indexes (close file version)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_indexclosef..." % self.__class__.__name__) # Create an instance of an HDF5 Array file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a numpy r = numpy.arange(200, dtype='int32') r.shape = (100, 2) # Save it in a array: array1 = fileh.create_array(fileh.root, 'array1', r, "title array1") # Copy to another array array2 = array1.copy("/", 'array2', start=self.start, stop=self.stop, step=self.step) # Close and reopen the file fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("array1-->", array1.read()) print("array2-->", array2.read()) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal r2 = r[self.start:self.stop:self.step] self.assertTrue(allequal(r2, array2.read())) # Assert the number of rows in array if common.verbose: print("nrows in array2-->", array2.nrows) print("and it should be-->", r2.shape[0]) self.assertEqual(r2.shape[0], array2.nrows) # Close the file fileh.close() os.remove(file) class CopyIndex1TestCase(CopyIndexTestCase): start = 0 stop = 7 step = 1 class CopyIndex2TestCase(CopyIndexTestCase): start = 0 stop = -1 step = 1 class CopyIndex3TestCase(CopyIndexTestCase): start = 1 stop = 7 step = 1 class CopyIndex4TestCase(CopyIndexTestCase): start = 0 stop = 6 step = 1 class CopyIndex5TestCase(CopyIndexTestCase): start = 3 stop = 7 step = 1 class CopyIndex6TestCase(CopyIndexTestCase): start = 3 stop = 6 step = 2 class CopyIndex7TestCase(CopyIndexTestCase): start = 0 stop = 7 step = 10 class CopyIndex8TestCase(CopyIndexTestCase): start = 6 stop = -1 # Negative values means starting from the end step = 1 class CopyIndex9TestCase(CopyIndexTestCase): start = 3 stop = 4 step = 1 class CopyIndex10TestCase(CopyIndexTestCase): start = 3 stop = 4 step = 2 class CopyIndex11TestCase(CopyIndexTestCase): start = -3 stop = -1 step = 2 class CopyIndex12TestCase(CopyIndexTestCase): start = -1 # Should point to the last element stop = None # None should mean the last element (including it) step = 1 class GetItemTestCase(unittest.TestCase): def test00_single(self): "Single element access (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charList arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file) arr = fileh.root.somearray # Get and compare an element if common.verbose: print("Original first element:", a[0], type(a[0])) print("Read first element:", arr[0], type(arr[0])) self.assertTrue(allequal(a[0], arr[0])) self.assertEqual(type(a[0]), type(arr[0])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test01_single(self): "Single element access (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalList arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file) arr = fileh.root.somearray # Get and compare an element if common.verbose: print("Original first element:", a[0], type(a[0])) print("Read first element:", arr[0], type(arr[0])) self.assertEqual(a[0], arr[0]) self.assertEqual(type(a[0]), type(arr[0])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test02_range(self): "Range element access (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file) arr = fileh.root.somearray # Get and compare an element if common.verbose: print("Original elements:", a[1:4]) print("Read elements:", arr[1:4]) self.assertTrue(allequal(a[1:4], arr[1:4])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test03_range(self): "Range element access (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file) arr = fileh.root.somearray # Get and compare an element if common.verbose: print("Original elements:", a[1:4]) print("Read elements:", arr[1:4]) self.assertTrue(allequal(a[1:4], arr[1:4])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test04_range(self): "Range element access, strided (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file) arr = fileh.root.somearray # Get and compare an element if common.verbose: print("Original elements:", a[1:4:2]) print("Read elements:", arr[1:4:2]) self.assertTrue(allequal(a[1:4:2], arr[1:4:2])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test05_range(self): "Range element access, strided (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file) arr = fileh.root.somearray # Get and compare an element if common.verbose: print("Original elements:", a[1:4:2]) print("Read elements:", arr[1:4:2]) self.assertTrue(allequal(a[1:4:2], arr[1:4:2])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test06_negativeIndex(self): "Negative Index element access (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file) arr = fileh.root.somearray # Get and compare an element if common.verbose: print("Original last element:", a[-1]) print("Read last element:", arr[-1]) self.assertTrue(allequal(a[-1], arr[-1])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test07_negativeIndex(self): "Negative Index element access (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file) arr = fileh.root.somearray # Get and compare an element if common.verbose: print("Original before last element:", a[-2]) print("Read before last element:", arr[-2]) if isinstance(a[-2], numpy.ndarray): self.assertTrue(allequal(a[-2], arr[-2])) else: self.assertEqual(a[-2], arr[-2]) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test08_negativeRange(self): "Negative range element access (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file) arr = fileh.root.somearray # Get and compare an element if common.verbose: print("Original last elements:", a[-4:-1]) print("Read last elements:", arr[-4:-1]) self.assertTrue(allequal(a[-4:-1], arr[-4:-1])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test09_negativeRange(self): "Negative range element access (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file) arr = fileh.root.somearray # Get and compare an element if common.verbose: print("Original last elements:", a[-4:-1]) print("Read last elements:", arr[-4:-1]) self.assertTrue(allequal(a[-4:-1], arr[-4:-1])) # Close the file fileh.close() # Then, delete the file os.remove(file) return class GI1NATestCase(GetItemTestCase): title = "Rank-1 case 1" numericalList = numpy.array([3]) numericalListME = numpy.array([3, 2, 1, 0, 4, 5, 6]) charList = numpy.array(["3"], 'S') charListME = numpy.array( ["321", "221", "121", "021", "421", "521", "621"], 'S') class GI1NAOpenTestCase(GI1NATestCase): close = 0 class GI1NACloseTestCase(GI1NATestCase): close = 1 class GI2NATestCase(GetItemTestCase): # A more complex example title = "Rank-1,2 case 2" numericalList = numpy.array([3, 4]) numericalListME = numpy.array([[3, 2, 1, 0, 4, 5, 6], [2, 1, 0, 4, 5, 6, 7], [4, 3, 2, 1, 0, 4, 5], [3, 2, 1, 0, 4, 5, 6], [3, 2, 1, 0, 4, 5, 6]]) charList = numpy.array(["a", "b"], 'S') charListME = numpy.array( [["321", "221", "121", "021", "421", "521", "621"], ["21", "21", "11", "02", "42", "21", "61"], ["31", "21", "12", "21", "41", "51", "621"], ["321", "221", "121", "021", "421", "521", "621"], ["3241", "2321", "13216", "0621", "4421", "5421", "a621"], ["a321", "s221", "d121", "g021", "b421", "5vvv21", "6zxzxs21"]], 'S') class GI2NAOpenTestCase(GI2NATestCase): close = 0 class GI2NACloseTestCase(GI2NATestCase): close = 1 class SetItemTestCase(unittest.TestCase): def test00_single(self): "Single element update (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charList arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file, 'a') arr = fileh.root.somearray # Modify a single element of a and arr: a[0] = b"b" arr[0] = b"b" # Get and compare an element if common.verbose: print("Original first element:", a[0]) print("Read first element:", arr[0]) self.assertTrue(allequal(a[0], arr[0])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test01_single(self): "Single element update (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalList arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file, 'a') arr = fileh.root.somearray # Modify elements of a and arr: a[0] = 333 arr[0] = 333 # Get and compare an element if common.verbose: print("Original first element:", a[0]) print("Read first element:", arr[0]) self.assertEqual(a[0], arr[0]) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test02_range(self): "Range element update (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file, 'a') arr = fileh.root.somearray # Modify elements of a and arr: a[1:3] = b"xXx" arr[1:3] = b"xXx" # Get and compare an element if common.verbose: print("Original elements:", a[1:4]) print("Read elements:", arr[1:4]) self.assertTrue(allequal(a[1:4], arr[1:4])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test03_range(self): "Range element update (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file, 'a') arr = fileh.root.somearray # Modify elements of a and arr: s = slice(1, 3, None) rng = numpy.arange(a[s].size)*2 + 3 rng.shape = a[s].shape a[s] = rng arr[s] = rng # Get and compare an element if common.verbose: print("Original elements:", a[1:4]) print("Read elements:", arr[1:4]) self.assertTrue(allequal(a[1:4], arr[1:4])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test04_range(self): "Range element update, strided (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file, 'a') arr = fileh.root.somearray # Modify elements of a and arr: s = slice(1, 4, 2) a[s] = b"xXx" arr[s] = b"xXx" # Get and compare an element if common.verbose: print("Original elements:", a[1:4:2]) print("Read elements:", arr[1:4:2]) self.assertTrue(allequal(a[1:4:2], arr[1:4:2])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test05_range(self): "Range element update, strided (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file, 'a') arr = fileh.root.somearray # Modify elements of a and arr: s = slice(1, 4, 2) rng = numpy.arange(a[s].size)*2 + 3 rng.shape = a[s].shape a[s] = rng arr[s] = rng # Get and compare an element if common.verbose: print("Original elements:", a[1:4:2]) print("Read elements:", arr[1:4:2]) self.assertTrue(allequal(a[1:4:2], arr[1:4:2])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test06_negativeIndex(self): "Negative Index element update (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file, 'a') arr = fileh.root.somearray # Modify elements of a and arr: s = -1 a[s] = b"xXx" arr[s] = b"xXx" # Get and compare an element if common.verbose: print("Original last element:", a[-1]) print("Read last element:", arr[-1]) self.assertTrue(allequal(a[-1], arr[-1])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test07_negativeIndex(self): "Negative Index element update (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file, 'a') arr = fileh.root.somearray # Modify elements of a and arr: s = -2 a[s] = a[s]*2 + 3 arr[s] = arr[s]*2 + 3 # Get and compare an element if common.verbose: print("Original before last element:", a[-2]) print("Read before last element:", arr[-2]) if isinstance(a[-2], numpy.ndarray): self.assertTrue(allequal(a[-2], arr[-2])) else: self.assertEqual(a[-2], arr[-2]) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test08_negativeRange(self): "Negative range element update (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file, 'a') arr = fileh.root.somearray # Modify elements of a and arr: s = slice(-4, -1, None) a[s] = b"xXx" arr[s] = b"xXx" # Get and compare an element if common.verbose: print("Original last elements:", a[-4:-1]) print("Read last elements:", arr[-4:-1]) self.assertTrue(allequal(a[-4:-1], arr[-4:-1])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test09_negativeRange(self): "Negative range element update (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file, 'a') arr = fileh.root.somearray # Modify elements of a and arr: s = slice(-3, -1, None) rng = numpy.arange(a[s].size)*2 + 3 rng.shape = a[s].shape a[s] = rng arr[s] = rng # Get and compare an element if common.verbose: print("Original last elements:", a[-4:-1]) print("Read last elements:", arr[-4:-1]) self.assertTrue(allequal(a[-4:-1], arr[-4:-1])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test10_outOfRange(self): "Out of range update (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file, 'a') arr = fileh.root.somearray # Modify elements of arr that are out of range: s = slice(1, a.shape[0]+1, None) s2 = slice(1, 1000, None) rng = numpy.arange(a[s].size)*2 + 3 rng.shape = a[s].shape a[s] = rng rng2 = numpy.arange(a[s2].size)*2 + 3 rng2.shape = a[s2].shape arr[s2] = rng2 # Get and compare an element if common.verbose: print("Original last elements:", a[-4:-1]) print("Read last elements:", arr[-4:-1]) self.assertTrue(allequal(a[-4:-1], arr[-4:-1])) # Close the file fileh.close() # Then, delete the file os.remove(file) return class SI1NATestCase(SetItemTestCase): title = "Rank-1 case 1" numericalList = numpy.array([3]) numericalListME = numpy.array([3, 2, 1, 0, 4, 5, 6]) charList = numpy.array(["3"], 'S') charListME = numpy.array( ["321", "221", "121", "021", "421", "521", "621"], 'S') class SI1NAOpenTestCase(SI1NATestCase): close = 0 class SI1NACloseTestCase(SI1NATestCase): close = 1 class SI2NATestCase(SetItemTestCase): # A more complex example title = "Rank-1,2 case 2" numericalList = numpy.array([3, 4]) numericalListME = numpy.array([[3, 2, 1, 0, 4, 5, 6], [2, 1, 0, 4, 5, 6, 7], [4, 3, 2, 1, 0, 4, 5], [3, 2, 1, 0, 4, 5, 6], [3, 2, 1, 0, 4, 5, 6]]) charList = numpy.array(["a", "b"], 'S') charListME = numpy.array( [["321", "221", "121", "021", "421", "521", "621"], ["21", "21", "11", "02", "42", "21", "61"], ["31", "21", "12", "21", "41", "51", "621"], ["321", "221", "121", "021", "421", "521", "621"], ["3241", "2321", "13216", "0621", "4421", "5421", "a621"], ["a321", "s221", "d121", "g021", "b421", "5vvv21", "6zxzxs21"]], 'S') class SI2NAOpenTestCase(SI2NATestCase): close = 0 class SI2NACloseTestCase(SI2NATestCase): close = 1 class GeneratorTestCase(unittest.TestCase): def test00a_single(self): "Testing generator access to Arrays, single elements (char)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charList arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file) arr = fileh.root.somearray # Get and compare an element ga = [i for i in a] garr = [i for i in arr] if common.verbose: print("Result of original iterator:", ga) print("Result of read generator:", garr) self.assertEqual(ga, garr) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test00b_me(self): "Testing generator access to Arrays, multiple elements (char)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file) arr = fileh.root.somearray # Get and compare an element ga = [i for i in a] garr = [i for i in arr] if common.verbose: print("Result of original iterator:", ga) print("Result of read generator:", garr) for i in range(len(ga)): self.assertTrue(allequal(ga[i], garr[i])) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test01a_single(self): "Testing generator access to Arrays, single elements (numeric)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalList arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file) arr = fileh.root.somearray # Get and compare an element ga = [i for i in a] garr = [i for i in arr] if common.verbose: print("Result of original iterator:", ga) print("Result of read generator:", garr) self.assertEqual(ga, garr) # Close the file fileh.close() # Then, delete the file os.remove(file) return def test01b_me(self): "Testing generator access to Arrays, multiple elements (numeric)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") if self.close: fileh.close() fileh = open_file(file) arr = fileh.root.somearray # Get and compare an element ga = [i for i in a] garr = [i for i in arr] if common.verbose: print("Result of original iterator:", ga) print("Result of read generator:", garr) for i in range(len(ga)): self.assertTrue(allequal(ga[i], garr[i])) # Close the file fileh.close() # Then, delete the file os.remove(file) return class GE1NATestCase(GeneratorTestCase): title = "Rank-1 case 1" numericalList = numpy.array([3]) numericalListME = numpy.array([3, 2, 1, 0, 4, 5, 6]) charList = numpy.array(["3"], 'S') charListME = numpy.array( ["321", "221", "121", "021", "421", "521", "621"], 'S') class GE1NAOpenTestCase(GE1NATestCase): close = 0 class GE1NACloseTestCase(GE1NATestCase): close = 1 class GE2NATestCase(GeneratorTestCase): # A more complex example title = "Rank-1,2 case 2" numericalList = numpy.array([3, 4]) numericalListME = numpy.array([[3, 2, 1, 0, 4, 5, 6], [2, 1, 0, 4, 5, 6, 7], [4, 3, 2, 1, 0, 4, 5], [3, 2, 1, 0, 4, 5, 6], [3, 2, 1, 0, 4, 5, 6]]) charList = numpy.array(["a", "b"], 'S') charListME = numpy.array( [["321", "221", "121", "021", "421", "521", "621"], ["21", "21", "11", "02", "42", "21", "61"], ["31", "21", "12", "21", "41", "51", "621"], ["321", "221", "121", "021", "421", "521", "621"], ["3241", "2321", "13216", "0621", "4421", "5421", "a621"], ["a321", "s221", "d121", "g021", "b421", "5vvv21", "6zxzxs21"]], 'S') class GE2NAOpenTestCase(GE2NATestCase): close = 0 class GE2NACloseTestCase(GE2NATestCase): close = 1 class NonHomogeneousTestCase(common.TempFileMixin, common.PyTablesTestCase): def test(self): """Test for creation of non-homogeneous arrays.""" # This checks ticket #12. h5file = self.h5file self.assertRaises(ValueError, h5file.create_array, '/', 'test', [1, [2, 3]]) self.assertRaises(NoSuchNodeError, h5file.remove_node, '/test') class TruncateTestCase(common.TempFileMixin, common.PyTablesTestCase): def test(self): """Test for unability to truncate Array objects.""" array1 = self.h5file.create_array('/', 'array1', [0, 2]) self.assertRaises(TypeError, array1.truncate, 0) class PointSelectionTestCase(common.PyTablesTestCase): def setUp(self): # Limits for selections self.limits = [ (0, 1), # just one element (20, -10), # no elements (-10, 4), # several elements (0, 10), # several elements (again) ] # Create an instance of an HDF5 Array self.file = tempfile.mktemp(".h5") self.fileh = fileh = open_file(self.file, "w") # Create a sample array size = numpy.prod(self.shape) nparr = numpy.arange(size, dtype=numpy.int32).reshape(self.shape) self.nparr = nparr self.tbarr = fileh.create_array(fileh.root, 'array', nparr) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def test01a_read(self): """Test for point-selections (read, boolean keys).""" nparr = self.nparr tbarr = self.tbarr for value1, value2 in self.limits: key = (nparr >= value1) & (nparr < value2) if common.verbose: print("Selection to test:", key) a = nparr[key] b = tbarr[key] # if common.verbose: # print("NumPy selection:", a) # print("PyTables selection:", b) self.assertTrue( numpy.alltrue(a == b), "NumPy array and PyTables selections does not match.") def test01b_read(self): """Test for point-selections (read, integer keys).""" nparr = self.nparr tbarr = self.tbarr for value1, value2 in self.limits: key = numpy.where((nparr >= value1) & (nparr < value2)) if common.verbose: print("Selection to test:", key) a = nparr[key] b = tbarr[key] # if common.verbose: # print("NumPy selection:", a) # print("PyTables selection:", b) self.assertTrue( numpy.alltrue(a == b), "NumPy array and PyTables selections does not match.") def test01c_read(self): """Test for point-selections (read, float keys).""" nparr = self.nparr tbarr = self.tbarr for value1, value2 in self.limits: key = numpy.where((nparr >= value1) & (nparr < value2)) if common.verbose: print("Selection to test:", key) # a = nparr[key] fkey = numpy.array(key, "f4") self.assertRaises(IndexError, tbarr.__getitem__, fkey) def test02a_write(self): """Test for point-selections (write, boolean keys).""" nparr = self.nparr tbarr = self.tbarr for value1, value2 in self.limits: key = (nparr >= value1) & (nparr < value2) if common.verbose: print("Selection to test:", key) s = nparr[key] nparr[key] = s * 2 tbarr[key] = s * 2 a = nparr[:] b = tbarr[:] # if common.verbose: # print("NumPy modified array:", a) # print("PyTables modifyied array:", b) self.assertTrue( numpy.alltrue(a == b), "NumPy array and PyTables modifications does not match.") def test02b_write(self): """Test for point-selections (write, integer keys).""" nparr = self.nparr tbarr = self.tbarr for value1, value2 in self.limits: key = numpy.where((nparr >= value1) & (nparr < value2)) if common.verbose: print("Selection to test:", key) s = nparr[key] nparr[key] = s * 2 tbarr[key] = s * 2 a = nparr[:] b = tbarr[:] # if common.verbose: # print("NumPy modified array:", a) # print("PyTables modifyied array:", b) self.assertTrue( numpy.alltrue(a == b), "NumPy array and PyTables modifications does not match.") def test02c_write(self): """Test for point-selections (write, integer values, broadcast).""" nparr = self.nparr tbarr = self.tbarr for value1, value2 in self.limits: key = numpy.where((nparr >= value1) & (nparr < value2)) if common.verbose: print("Selection to test:", key) # s = nparr[key] nparr[key] = 2 # force a broadcast tbarr[key] = 2 # force a broadcast a = nparr[:] b = tbarr[:] # if common.verbose: # print("NumPy modified array:", a) # print("PyTables modifyied array:", b) self.assertTrue( numpy.alltrue(a == b), "NumPy array and PyTables modifications does not match.") class PointSelection1(PointSelectionTestCase): shape = (5, 3, 3) class PointSelection2(PointSelectionTestCase): shape = (7, 3) class PointSelection3(PointSelectionTestCase): shape = (4, 3, 2, 1) class PointSelection4(PointSelectionTestCase): shape = (1, 3, 2, 5, 6) class FancySelectionTestCase(common.PyTablesTestCase): def setUp(self): M, N, O = self.shape # The next are valid selections for both NumPy and PyTables self.working_keyset = [ ([1, 3], slice(1, N-1), 2), ([M-1, 1, 3, 2], slice(None), 2), # unordered lists supported (slice(M), [N-1, 1, 0], slice(None)), (slice(1, M, 3), slice(1, N), [O-1, 1, 0]), (M-1, [2, 1], 1), (1, 2, 1), # regular selection ([1, 2], -2, -1), # negative indices ([1, -2], 2, -1), # more negative indices ([1, -2], 2, Ellipsis), # one ellipsis (Ellipsis, [1, 2]), # one ellipsis (numpy.array( [1, -2], 'i4'), 2, -1), # array 32-bit instead of list (numpy.array( [-1, 2], 'i8'), 2, -1), # array 64-bit instead of list ] # Using booleans instead of ints is deprecated since numpy 1.8 # Tests for keys that have to support the __index__ attribute #if (sys.version_info[0] >= 2 and sys.version_info[1] >= 5): # self.working_keyset.append( # (False, True), # equivalent to (0,1) ;-) # ) # Valid selections for NumPy, but not for PyTables (yet) # The next should raise an IndexError self.not_working_keyset = [ numpy.array([False, True], dtype="b1"), # boolean arrays ([1, 2, 1], 2, 1), # repeated values ([1, 2], 2, [1, 2]), # several lists ([], 2, 1), # empty selections (Ellipsis, [1, 2], Ellipsis), # several ellipsis # Using booleans instead of ints is deprecated since numpy 1.8 ([False, True]), # boolean values with incompatible shape ] # The next should raise an IndexError in both NumPy and PyTables self.not_working_oob = [ ([1, 2], 2, 1000), # out-of-bounds selections ([1, 2], 2000, 1), # out-of-bounds selections ] # The next should raise a IndexError in both NumPy and PyTables self.not_working_too_many = [ ([1, 2], 2, 1, 1), ] # Create an instance of an HDF5 file self.file = tempfile.mktemp(".h5") self.fileh = fileh = open_file(self.file, "w") # Create a sample array nparr = numpy.empty(self.shape, dtype=numpy.int32) data = numpy.arange(N * O, dtype=numpy.int32).reshape(N, O) for i in xrange(M): nparr[i] = data * i self.nparr = nparr self.tbarr = fileh.create_array(fileh.root, 'array', nparr) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def test01a_read(self): """Test for fancy-selections (working selections, read).""" nparr = self.nparr tbarr = self.tbarr for key in self.working_keyset: if common.verbose: print("Selection to test:", key) a = nparr[key] b = tbarr[key] # if common.verbose: # print("NumPy selection:", a) # print("PyTables selection:", b) self.assertTrue( numpy.alltrue(a == b), "NumPy array and PyTables selections does not match.") def test01b_read(self): """Test for fancy-selections (not working selections, read).""" # nparr = self.nparr tbarr = self.tbarr for key in self.not_working_keyset: if common.verbose: print("Selection to test:", key) # a = nparr[key] self.assertRaises(IndexError, tbarr.__getitem__, key) def test01c_read(self): """Test for fancy-selections (out-of-bound indexes, read).""" nparr = self.nparr tbarr = self.tbarr for key in self.not_working_oob: if common.verbose: print("Selection to test:", key) self.assertRaises(IndexError, nparr.__getitem__, key) self.assertRaises(IndexError, tbarr.__getitem__, key) def test01d_read(self): """Test for fancy-selections (too many indexes, read).""" nparr = self.nparr tbarr = self.tbarr for key in self.not_working_too_many: if common.verbose: print("Selection to test:", key) # ValueError for numpy 1.6.x and earlier # IndexError in numpy > 1.8.0 self.assertRaises((ValueError, IndexError), nparr.__getitem__, key) self.assertRaises(IndexError, tbarr.__getitem__, key) def test02a_write(self): """Test for fancy-selections (working selections, write).""" nparr = self.nparr tbarr = self.tbarr for key in self.working_keyset: if common.verbose: print("Selection to test:", key) s = nparr[key] nparr[key] = s * 2 tbarr[key] = s * 2 a = nparr[:] b = tbarr[:] # if common.verbose: # print("NumPy modified array:", a) # print("PyTables modifyied array:", b) self.assertTrue( numpy.alltrue(a == b), "NumPy array and PyTables modifications does not match.") def test02b_write(self): """Test for fancy-selections (working selections, write, broadcast).""" nparr = self.nparr tbarr = self.tbarr for key in self.working_keyset: if common.verbose: print("Selection to test:", key) # s = nparr[key] nparr[key] = 2 # broadcast value tbarr[key] = 2 # broadcast value a = nparr[:] b = tbarr[:] # if common.verbose: # print("NumPy modified array:", a) # print("PyTables modifyied array:", b) self.assertTrue( numpy.alltrue(a == b), "NumPy array and PyTables modifications does not match.") class FancySelection1(FancySelectionTestCase): shape = (5, 3, 3) # Minimum values class FancySelection2(FancySelectionTestCase): # shape = (5, 3, 3) # Minimum values shape = (7, 3, 3) class FancySelection3(FancySelectionTestCase): # shape = (5, 3, 3) # Minimum values shape = (7, 4, 5) class FancySelection4(FancySelectionTestCase): # shape = (5, 3, 3) # Minimum values shape = (5, 3, 10) class CopyNativeHDF5MDAtom(common.PyTablesTestCase): def setUp(self): filename = self._testFilename("array_mdatom.h5") self.fileh = open_file(filename, "r") self.arr = self.fileh.root.arr self.copy = tempfile.mktemp(".h5") self.copyh = open_file(self.copy, mode="w") self.arr2 = self.arr.copy(self.copyh.root, newname="arr2") def tearDown(self): self.fileh.close() self.copyh.close() os.remove(self.copy) def test01_copy(self): """Checking that native MD atoms are copied as-is""" self.assertEqual(self.arr.atom, self.arr2.atom) self.assertEqual(self.arr.shape, self.arr2.shape) def test02_reopen(self): """Checking that native MD atoms are copied as-is (re-open)""" self.copyh.close() self.copyh = open_file(self.copy, mode="r") self.arr2 = self.copyh.root.arr2 self.assertEqual(self.arr.atom, self.arr2.atom) self.assertEqual(self.arr.shape, self.arr2.shape) class AccessClosedTestCase(common.TempFileMixin, common.PyTablesTestCase): def setUp(self): super(AccessClosedTestCase, self).setUp() a = numpy.zeros((10, 10)) self.array = self.h5file.create_array(self.h5file.root, 'array', a) def test_read(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.array.read) def test_getitem(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.array.__getitem__, 0) def test_setitem(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.array.__setitem__, 0, 0) class BroadcastTest(common.TempFileMixin, common.PyTablesTestCase): def test(self): """Test correct broadcasting when the array atom is not scalar.""" array_shape = (2, 3) element_shape = (3,) dtype = numpy.dtype((numpy.int, element_shape)) atom = Atom.from_dtype(dtype) h5arr = self.h5file.create_carray(self.h5file.root, 'array', atom, array_shape) size = numpy.prod(element_shape) nparr = numpy.arange(size).reshape(element_shape) h5arr[0] = nparr self.assertTrue(numpy.all(h5arr[0] == nparr)) class TestCreateArrayArgs(common.TempFileMixin, common.PyTablesTestCase): where = '/' name = 'array' obj = numpy.array([[1, 2], [3, 4]]) title = 'title' byteorder = None createparents = False atom = Atom.from_dtype(obj.dtype) shape = obj.shape def test_positional_args(self): self.h5file.create_array(self.where, self.name, self.obj, self.title) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertTrue(allequal(self.obj, nparr)) def test_positional_args_atom_shape(self): self.h5file.create_array(self.where, self.name, None, self.title, self.byteorder, self.createparents, self.atom, self.shape) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertTrue(allequal(numpy.zeros_like(self.obj), nparr)) def test_kwargs_obj(self): self.h5file.create_array(self.where, self.name, title=self.title, obj=self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_atom_shape_01(self): ptarr = self.h5file.create_array(self.where, self.name, title=self.title, atom=self.atom, shape=self.shape) ptarr[...] = self.obj self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_atom_shape_02(self): ptarr = self.h5file.create_array(self.where, self.name, title=self.title, atom=self.atom, shape=self.shape) #ptarr[...] = self.obj self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertTrue(allequal(numpy.zeros_like(self.obj), nparr)) def test_kwargs_obj_atom(self): ptarr = self.h5file.create_array(self.where, self.name, title=self.title, obj=self.obj, atom=self.atom) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj_shape(self): ptarr = self.h5file.create_array(self.where, self.name, title=self.title, obj=self.obj, shape=self.shape) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj_atom_shape(self): ptarr = self.h5file.create_array(self.where, self.name, title=self.title, obj=self.obj, atom=self.atom, shape=self.shape) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj_atom_error(self): atom = Atom.from_dtype(numpy.dtype('complex')) #shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_array, self.where, self.name, title=self.title, obj=self.obj, atom=atom) def test_kwargs_obj_shape_error(self): #atom = Atom.from_dtype(numpy.dtype('complex')) shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_array, self.where, self.name, title=self.title, obj=self.obj, shape=shape) def test_kwargs_obj_atom_shape_error_01(self): atom = Atom.from_dtype(numpy.dtype('complex')) #shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_array, self.where, self.name, title=self.title, obj=self.obj, atom=atom, shape=self.shape) def test_kwargs_obj_atom_shape_error_02(self): #atom = Atom.from_dtype(numpy.dtype('complex')) shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_array, self.where, self.name, title=self.title, obj=self.obj, atom=self.atom, shape=shape) def test_kwargs_obj_atom_shape_error_03(self): atom = Atom.from_dtype(numpy.dtype('complex')) shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_array, self.where, self.name, title=self.title, obj=self.obj, atom=atom, shape=shape) #---------------------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() niter = 1 for i in range(niter): # The scalar case test should be refined in order to work theSuite.addTest(unittest.makeSuite(Basic0DOneTestCase)) theSuite.addTest(unittest.makeSuite(Basic0DTwoTestCase)) # theSuite.addTest(unittest.makeSuite(Basic1DZeroTestCase)) theSuite.addTest(unittest.makeSuite(Basic1DOneTestCase)) theSuite.addTest(unittest.makeSuite(Basic1DTwoTestCase)) theSuite.addTest(unittest.makeSuite(Basic1DThreeTestCase)) theSuite.addTest(unittest.makeSuite(Basic2DOneTestCase)) theSuite.addTest(unittest.makeSuite(Basic2DTwoTestCase)) theSuite.addTest(unittest.makeSuite(Basic10DTestCase)) # The 32 dimensions case is tested on GroupsArray # theSuite.addTest(unittest.makeSuite(Basic32DTestCase)) theSuite.addTest(unittest.makeSuite(ReadOutArgumentTests)) theSuite.addTest(unittest.makeSuite( SizeOnDiskInMemoryPropertyTestCase)) theSuite.addTest(unittest.makeSuite(GroupsArrayTestCase)) theSuite.addTest(unittest.makeSuite(ComplexNotReopenNotEndianTestCase)) theSuite.addTest(unittest.makeSuite(ComplexReopenNotEndianTestCase)) theSuite.addTest(unittest.makeSuite(ComplexNotReopenEndianTestCase)) theSuite.addTest(unittest.makeSuite(ComplexReopenEndianTestCase)) theSuite.addTest(unittest.makeSuite(CloseCopyTestCase)) theSuite.addTest(unittest.makeSuite(OpenCopyTestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex1TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex2TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex3TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex4TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex5TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex6TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex7TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex8TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex9TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex10TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex11TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex12TestCase)) theSuite.addTest(unittest.makeSuite(GI1NAOpenTestCase)) theSuite.addTest(unittest.makeSuite(GI1NACloseTestCase)) theSuite.addTest(unittest.makeSuite(GI2NAOpenTestCase)) theSuite.addTest(unittest.makeSuite(GI2NACloseTestCase)) theSuite.addTest(unittest.makeSuite(SI1NAOpenTestCase)) theSuite.addTest(unittest.makeSuite(SI1NACloseTestCase)) theSuite.addTest(unittest.makeSuite(SI2NAOpenTestCase)) theSuite.addTest(unittest.makeSuite(SI2NACloseTestCase)) theSuite.addTest(unittest.makeSuite(GE1NAOpenTestCase)) theSuite.addTest(unittest.makeSuite(GE1NACloseTestCase)) theSuite.addTest(unittest.makeSuite(GE2NAOpenTestCase)) theSuite.addTest(unittest.makeSuite(GE2NACloseTestCase)) theSuite.addTest(unittest.makeSuite(NonHomogeneousTestCase)) theSuite.addTest(unittest.makeSuite(TruncateTestCase)) theSuite.addTest(unittest.makeSuite(FancySelection1)) theSuite.addTest(unittest.makeSuite(FancySelection2)) theSuite.addTest(unittest.makeSuite(FancySelection3)) theSuite.addTest(unittest.makeSuite(FancySelection4)) theSuite.addTest(unittest.makeSuite(PointSelection1)) theSuite.addTest(unittest.makeSuite(PointSelection2)) theSuite.addTest(unittest.makeSuite(PointSelection3)) theSuite.addTest(unittest.makeSuite(PointSelection4)) theSuite.addTest(unittest.makeSuite(CopyNativeHDF5MDAtom)) theSuite.addTest(unittest.makeSuite(AccessClosedTestCase)) theSuite.addTest(unittest.makeSuite(TestCreateArrayArgs)) theSuite.addTest(unittest.makeSuite(BroadcastTest)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/test_attributes.py000066400000000000000000001732201231437614300217270ustar00rootroot00000000000000# -*- coding: utf-8 -*- """This test unit checks node attributes that are persistent (AttributeSet).""" from __future__ import print_function import os import sys import unittest import tempfile import numpy from numpy.testing import assert_array_equal, assert_almost_equal from tables.parameters import NODE_CACHE_SLOTS from tables import * from tables.tests import common from tables.tests.common import PyTablesTestCase from tables.exceptions import DataTypeWarning # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup class Record(IsDescription): var1 = StringCol(itemsize=4) # 4-character String var2 = IntCol() # integer var3 = Int16Col() # short integer var4 = FloatCol() # double (double-precision) var5 = Float32Col() # float (single-precision) class CreateTestCase(unittest.TestCase): def setUp(self): # Create an instance of HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file( self.file, mode="w", node_cache_slots=self.node_cache_slots) self.root = self.fileh.root # Create a table object self.table = self.fileh.create_table(self.root, 'atable', Record, "Table title") # Create an array object self.array = self.fileh.create_array(self.root, 'anarray', [1], "Array title") # Create a group object self.group = self.fileh.create_group(self.root, 'agroup', "Group title") def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #--------------------------------------- def test01_setAttributes(self): """Checking setting large string attributes (File methods)""" attrlength = 2048 # Try to put a long string attribute on a group object self.fileh.set_node_attr(self.root.agroup, "attr1", "p" * attrlength) # Now, try with a Table object self.fileh.set_node_attr(self.root.atable, "attr1", "a" * attrlength) # Finally, try with an Array object self.fileh.set_node_attr(self.root.anarray, "attr1", "n" * attrlength) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) self.root = self.fileh.root self.assertEqual(self.fileh.get_node_attr(self.root.agroup, 'attr1'), "p" * attrlength) self.assertEqual(self.fileh.get_node_attr(self.root.atable, 'attr1'), "a" * attrlength) self.assertEqual(self.fileh.get_node_attr(self.root.anarray, 'attr1'), "n" * attrlength) def test02_setAttributes(self): """Checking setting large string attributes (Node methods)""" attrlength = 2048 # Try to put a long string attribute on a group object self.root.agroup._f_setattr('attr1', "p" * attrlength) # Now, try with a Table object self.root.atable.set_attr('attr1', "a" * attrlength) # Finally, try with an Array object self.root.anarray.set_attr('attr1', "n" * attrlength) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) self.root = self.fileh.root self.assertEqual(self.root.agroup._f_getattr( 'attr1'), "p" * attrlength) self.assertEqual(self.root.atable.get_attr("attr1"), "a" * attrlength) self.assertEqual(self.root.anarray.get_attr("attr1"), "n" * attrlength) def test03_setAttributes(self): """Checking setting large string attributes (AttributeSet methods)""" attrlength = 2048 # Try to put a long string attribute on a group object self.group._v_attrs.attr1 = "p" * attrlength # Now, try with a Table object self.table.attrs.attr1 = "a" * attrlength # Finally, try with an Array object self.array.attrs.attr1 = "n" * attrlength if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) self.root = self.fileh.root # This should work even when the node cache is disabled self.assertEqual(self.root.agroup._v_attrs.attr1, "p" * attrlength) self.assertEqual(self.root.atable.attrs.attr1, "a" * attrlength) self.assertEqual(self.root.anarray.attrs.attr1, "n" * attrlength) def test04_listAttributes(self): """Checking listing attributes.""" # With a Group object self.group._v_attrs.pq = "1" self.group._v_attrs.qr = "2" self.group._v_attrs.rs = "3" if common.verbose: print("Attribute list:", self.group._v_attrs._f_list()) # Now, try with a Table object self.table.attrs.a = "1" self.table.attrs.c = "2" self.table.attrs.b = "3" if common.verbose: print("Attribute list:", self.table.attrs._f_list()) # Finally, try with an Array object self.array.attrs.k = "1" self.array.attrs.j = "2" self.array.attrs.i = "3" if common.verbose: print("Attribute list:", self.array.attrs._f_list()) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) self.root = self.fileh.root agroup = self.root.agroup self.assertEqual(agroup._v_attrs._f_list("user"), ["pq", "qr", "rs"]) self.assertEqual(agroup._v_attrs._f_list("sys"), ['CLASS', 'TITLE', 'VERSION']) self.assertEqual(agroup._v_attrs._f_list("all"), ['CLASS', 'TITLE', 'VERSION', "pq", "qr", "rs"]) atable = self.root.atable self.assertEqual(atable.attrs._f_list(), ["a", "b", "c"]) self.assertEqual(atable.attrs._f_list("sys"), ['CLASS', 'FIELD_0_FILL', 'FIELD_0_NAME', 'FIELD_1_FILL', 'FIELD_1_NAME', 'FIELD_2_FILL', 'FIELD_2_NAME', 'FIELD_3_FILL', 'FIELD_3_NAME', 'FIELD_4_FILL', 'FIELD_4_NAME', 'NROWS', 'TITLE', 'VERSION']) self.assertEqual(atable.attrs._f_list("all"), ['CLASS', 'FIELD_0_FILL', 'FIELD_0_NAME', 'FIELD_1_FILL', 'FIELD_1_NAME', 'FIELD_2_FILL', 'FIELD_2_NAME', 'FIELD_3_FILL', 'FIELD_3_NAME', 'FIELD_4_FILL', 'FIELD_4_NAME', 'NROWS', 'TITLE', 'VERSION', "a", "b", "c"]) anarray = self.root.anarray self.assertEqual(anarray.attrs._f_list(), ["i", "j", "k"]) self.assertEqual( anarray.attrs._f_list("sys"), ['CLASS', 'FLAVOR', 'TITLE', 'VERSION']) self.assertEqual( anarray.attrs._f_list("all"), ['CLASS', 'FLAVOR', 'TITLE', 'VERSION', "i", "j", "k"]) def test05_removeAttributes(self): """Checking removing attributes.""" # With a Group object self.group._v_attrs.pq = "1" self.group._v_attrs.qr = "2" self.group._v_attrs.rs = "3" # delete an attribute del self.group._v_attrs.pq if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) self.root = self.fileh.root agroup = self.root.agroup if common.verbose: print("Attribute list:", agroup._v_attrs._f_list()) # Check the local attributes names self.assertEqual(agroup._v_attrs._f_list(), ["qr", "rs"]) if common.verbose: print("Attribute list in disk:", agroup._v_attrs._f_list("all")) # Check the disk attribute names self.assertEqual(agroup._v_attrs._f_list("all"), ['CLASS', 'TITLE', 'VERSION', "qr", "rs"]) # delete an attribute (__delattr__ method) del agroup._v_attrs.qr if common.verbose: print("Attribute list:", agroup._v_attrs._f_list()) # Check the local attributes names self.assertEqual(agroup._v_attrs._f_list(), ["rs"]) if common.verbose: print("Attribute list in disk:", agroup._v_attrs._f_list()) # Check the disk attribute names self.assertEqual(agroup._v_attrs._f_list("all"), ['CLASS', 'TITLE', 'VERSION', "rs"]) def test05b_removeAttributes(self): """Checking removing attributes (using File.del_node_attr())""" # With a Group object self.group._v_attrs.pq = "1" self.group._v_attrs.qr = "2" self.group._v_attrs.rs = "3" # delete an attribute self.fileh.del_node_attr(self.group, "pq") if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) self.root = self.fileh.root agroup = self.root.agroup if common.verbose: print("Attribute list:", agroup._v_attrs._f_list()) # Check the local attributes names self.assertEqual(agroup._v_attrs._f_list(), ["qr", "rs"]) if common.verbose: print("Attribute list in disk:", agroup._v_attrs._f_list("all")) # Check the disk attribute names self.assertEqual(agroup._v_attrs._f_list("all"), ['CLASS', 'TITLE', 'VERSION', "qr", "rs"]) # delete an attribute (File.del_node_attr method) self.fileh.del_node_attr(self.root, "qr", "agroup") if common.verbose: print("Attribute list:", agroup._v_attrs._f_list()) # Check the local attributes names self.assertEqual(agroup._v_attrs._f_list(), ["rs"]) if common.verbose: print("Attribute list in disk:", agroup._v_attrs._f_list()) # Check the disk attribute names self.assertEqual(agroup._v_attrs._f_list("all"), ['CLASS', 'TITLE', 'VERSION', "rs"]) def test06_removeAttributes(self): """Checking removing system attributes.""" # remove a system attribute if common.verbose: print("Before removing CLASS attribute") print("System attrs:", self.group._v_attrs._v_attrnamessys) del self.group._v_attrs.CLASS self.assertEqual(self.group._v_attrs._f_list("sys"), ['TITLE', 'VERSION']) if common.verbose: print("After removing CLASS attribute") print("System attrs:", self.group._v_attrs._v_attrnamessys) def test07_renameAttributes(self): """Checking renaming attributes.""" # With a Group object self.group._v_attrs.pq = "1" self.group._v_attrs.qr = "2" self.group._v_attrs.rs = "3" # rename an attribute self.group._v_attrs._f_rename("pq", "op") if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) self.root = self.fileh.root agroup = self.root.agroup if common.verbose: print("Attribute list:", agroup._v_attrs._f_list()) # Check the local attributes names (alphabetically sorted) self.assertEqual(agroup._v_attrs._f_list(), ["op", "qr", "rs"]) if common.verbose: print("Attribute list in disk:", agroup._v_attrs._f_list("all")) # Check the disk attribute names (not sorted) self.assertEqual(agroup._v_attrs._f_list("all"), ['CLASS', 'TITLE', 'VERSION', "op", "qr", "rs"]) def test08_renameAttributes(self): """Checking renaming system attributes.""" if common.verbose: print("Before renaming CLASS attribute") print("All attrs:", self.group._v_attrs._v_attrnames) # rename a system attribute self.group._v_attrs._f_rename("CLASS", "op") if common.verbose: print("After renaming CLASS attribute") print("All attrs:", self.group._v_attrs._v_attrnames) # Check the disk attribute names (not sorted) agroup = self.root.agroup self.assertEqual(agroup._v_attrs._f_list("all"), ['TITLE', 'VERSION', "op"]) def test09_overwriteAttributes(self): """Checking overwriting attributes.""" # With a Group object self.group._v_attrs.pq = "1" self.group._v_attrs.qr = "2" self.group._v_attrs.rs = "3" # overwrite attributes self.group._v_attrs.pq = "4" self.group._v_attrs.qr = 2 self.group._v_attrs.rs = [1, 2, 3] if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) self.root = self.fileh.root agroup = self.root.agroup if common.verbose: print("Value of Attribute pq:", agroup._v_attrs.pq) # Check the local attributes names (alphabetically sorted) self.assertEqual(agroup._v_attrs.pq, "4") self.assertEqual(agroup._v_attrs.qr, 2) self.assertEqual(agroup._v_attrs.rs, [1, 2, 3]) if common.verbose: print("Attribute list in disk:", agroup._v_attrs._f_list("all")) # Check the disk attribute names (not sorted) self.assertEqual(agroup._v_attrs._f_list("all"), ['CLASS', 'TITLE', 'VERSION', "pq", "qr", "rs"]) def test10a_copyAttributes(self): """Checking copying attributes.""" # With a Group object self.group._v_attrs.pq = "1" self.group._v_attrs.qr = "2" self.group._v_attrs.rs = "3" # copy all attributes from "/agroup" to "/atable" self.group._v_attrs._f_copy(self.root.atable) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) self.root = self.fileh.root atable = self.root.atable if common.verbose: print("Attribute list:", atable._v_attrs._f_list()) # Check the local attributes names (alphabetically sorted) self.assertEqual(atable._v_attrs._f_list(), ["pq", "qr", "rs"]) if common.verbose: print("Complete attribute list:", atable._v_attrs._f_list("all")) # Check the disk attribute names (not sorted) self.assertEqual(atable._v_attrs._f_list("all"), ['CLASS', 'FIELD_0_FILL', 'FIELD_0_NAME', 'FIELD_1_FILL', 'FIELD_1_NAME', 'FIELD_2_FILL', 'FIELD_2_NAME', 'FIELD_3_FILL', 'FIELD_3_NAME', 'FIELD_4_FILL', 'FIELD_4_NAME', 'NROWS', 'TITLE', 'VERSION', "pq", "qr", "rs"]) def test10b_copyAttributes(self): """Checking copying attributes (copy_node_attrs)""" # With a Group object self.group._v_attrs.pq = "1" self.group._v_attrs.qr = "2" self.group._v_attrs.rs = "3" # copy all attributes from "/agroup" to "/atable" self.fileh.copy_node_attrs(self.group, self.root.atable) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) self.root = self.fileh.root atable = self.root.atable if common.verbose: print("Attribute list:", atable._v_attrs._f_list()) # Check the local attributes names (alphabetically sorted) self.assertEqual(atable._v_attrs._f_list(), ["pq", "qr", "rs"]) if common.verbose: print("Complete attribute list:", atable._v_attrs._f_list("all")) # Check the disk attribute names (not sorted) self.assertEqual(atable._v_attrs._f_list("all"), ['CLASS', 'FIELD_0_FILL', 'FIELD_0_NAME', 'FIELD_1_FILL', 'FIELD_1_NAME', 'FIELD_2_FILL', 'FIELD_2_NAME', 'FIELD_3_FILL', 'FIELD_3_NAME', 'FIELD_4_FILL', 'FIELD_4_NAME', 'NROWS', 'TITLE', 'VERSION', "pq", "qr", "rs"]) def test10c_copyAttributes(self): """Checking copying attributes during group copies.""" # With a Group object self.group._v_attrs['CLASS'] = "GROUP2" self.group._v_attrs['VERSION'] = "1.3" # copy "/agroup" to "/agroup2" self.fileh.copy_node(self.group, self.root, "agroup2") if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) self.root = self.fileh.root agroup2 = self.root.agroup2 if common.verbose: print("Complete attribute list:", agroup2._v_attrs._f_list("all")) self.assertEqual(agroup2._v_attrs['CLASS'], "GROUP2") self.assertEqual(agroup2._v_attrs['VERSION'], "1.3") def test10d_copyAttributes(self): """Checking copying attributes during leaf copies.""" # With a Group object atable = self.root.atable atable._v_attrs['CLASS'] = "TABLE2" atable._v_attrs['VERSION'] = "1.3" # copy "/agroup" to "/agroup2" self.fileh.copy_node(atable, self.root, "atable2") if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) self.root = self.fileh.root atable2 = self.root.atable2 if common.verbose: print("Complete attribute list:", atable2._v_attrs._f_list("all")) self.assertEqual(atable2._v_attrs['CLASS'], "TABLE2") self.assertEqual(atable2._v_attrs['VERSION'], "1.3") def test11a_getitem(self): """Checking the __getitem__ interface.""" attrs = self.group._v_attrs attrs.pq = "1" self.assertEqual(attrs['pq'], "1") def test11b_setitem(self): """Checking the __setitem__ interface.""" attrs = self.group._v_attrs attrs['pq'] = "2" self.assertEqual(attrs['pq'], "2") def test11c_delitem(self): """Checking the __delitem__ interface.""" attrs = self.group._v_attrs attrs.pq = "1" del attrs['pq'] self.assertTrue('pq' not in attrs._f_list()) def test11d_KeyError(self): """Checking that KeyError is raised in __getitem__/__delitem__.""" attrs = self.group._v_attrs self.assertRaises(KeyError, attrs.__getitem__, 'pq') self.assertRaises(KeyError, attrs.__delitem__, 'pq') def test_2d_non_contiguous(self): """Checking setting 2D and non-contiguous NumPy attributes""" # Regression for gh-176 numpy. # In the views old implementation PyTAbles performa a copy of the # array: # # value = numpy.array(value) # # in order to get a contiguous array. # Unfortunately array with swapped axis are copyed as they are so # thay are stored in to HDF5 attributes without being actually # contiguous and ths causes an error whn they are restored. data = numpy.array([[0, 1], [2, 3]]) self.array.attrs['a'] = data self.array.attrs['b'] = data.T.copy() self.array.attrs['c'] = data.T assert_array_equal(self.array.attrs['a'], data) assert_array_equal(self.array.attrs['b'], data.T) assert_array_equal(self.array.attrs['c'], data.T) # AssertionError! class NotCloseCreate(CreateTestCase): close = False node_cache_slots = NODE_CACHE_SLOTS class CloseCreate(CreateTestCase): close = True node_cache_slots = NODE_CACHE_SLOTS class NoCacheNotCloseCreate(CreateTestCase): close = False node_cache_slots = 0 class NoCacheCloseCreate(CreateTestCase): close = True node_cache_slots = 0 class DictCacheNotCloseCreate(CreateTestCase): close = False node_cache_slots = -NODE_CACHE_SLOTS class DictCacheCloseCreate(CreateTestCase): close = True node_cache_slots = -NODE_CACHE_SLOTS class TypesTestCase(unittest.TestCase): def setUp(self): # Create an instance of HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w") self.root = self.fileh.root # Create an array object self.array = self.fileh.create_array(self.root, 'anarray', [1], "Array title") # Create a group object self.group = self.fileh.create_group(self.root, 'agroup', "Group title") def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #--------------------------------------- def test00a_setBoolAttributes(self): """Checking setting Bool attributes (scalar, Python case)""" self.array.attrs.pq = True self.array.attrs.qr = False self.array.attrs.rs = True # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) print("qr -->", self.array.attrs.qr) print("rs -->", self.array.attrs.rs) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray self.assertEqual(self.root.anarray.attrs.pq, True) self.assertEqual(self.root.anarray.attrs.qr, False) self.assertEqual(self.root.anarray.attrs.rs, True) def test00b_setBoolAttributes(self): """Checking setting Bool attributes (scalar, NumPy case)""" self.array.attrs.pq = numpy.bool_(True) self.array.attrs.qr = numpy.bool_(False) self.array.attrs.rs = numpy.bool_(True) # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) print("qr -->", self.array.attrs.qr) print("rs -->", self.array.attrs.rs) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray self.assertTrue(isinstance(self.root.anarray.attrs.pq, numpy.bool_)) self.assertTrue(isinstance(self.root.anarray.attrs.qr, numpy.bool_)) self.assertTrue(isinstance(self.root.anarray.attrs.rs, numpy.bool_)) self.assertEqual(self.root.anarray.attrs.pq, True) self.assertEqual(self.root.anarray.attrs.qr, False) self.assertEqual(self.root.anarray.attrs.rs, True) def test00c_setBoolAttributes(self): """Checking setting Bool attributes (NumPy, 0-dim case)""" self.array.attrs.pq = numpy.array(True) self.array.attrs.qr = numpy.array(False) self.array.attrs.rs = numpy.array(True) # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) print("qr -->", self.array.attrs.qr) print("rs -->", self.array.attrs.rs) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray self.assertEqual(self.root.anarray.attrs.pq, True) self.assertEqual(self.root.anarray.attrs.qr, False) self.assertEqual(self.root.anarray.attrs.rs, True) def test00d_setBoolAttributes(self): """Checking setting Bool attributes (NumPy, multidim case)""" self.array.attrs.pq = numpy.array([True]) self.array.attrs.qr = numpy.array([[False]]) self.array.attrs.rs = numpy.array([[True, False], [True, False]]) # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) print("qr -->", self.array.attrs.qr) print("rs -->", self.array.attrs.rs) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray assert_array_equal(self.root.anarray.attrs.pq, numpy.array([True])) assert_array_equal(self.root.anarray.attrs.qr, numpy.array([[False]])) assert_array_equal(self.root.anarray.attrs.rs, numpy.array([[True, False], [True, False]])) def test01a_setIntAttributes(self): """Checking setting Int attributes (scalar, Python case)""" self.array.attrs.pq = 1 self.array.attrs.qr = 2 self.array.attrs.rs = 3 # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) print("qr -->", self.array.attrs.qr) print("rs -->", self.array.attrs.rs) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray self.assertTrue(isinstance(self.root.anarray.attrs.pq, numpy.int_)) self.assertTrue(isinstance(self.root.anarray.attrs.qr, numpy.int_)) self.assertTrue(isinstance(self.root.anarray.attrs.rs, numpy.int_)) self.assertEqual(self.root.anarray.attrs.pq, 1) self.assertEqual(self.root.anarray.attrs.qr, 2) self.assertEqual(self.root.anarray.attrs.rs, 3) def test01b_setIntAttributes(self): """Checking setting Int attributes (scalar, NumPy case)""" # 'UInt64' not supported on Win checktypes = ['Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32'] for dtype in checktypes: setattr(self.array.attrs, dtype, numpy.array(1, dtype=dtype)) # Check the results if common.verbose: for dtype in checktypes: print("type, value-->", dtype, getattr(self.array.attrs, dtype)) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray for dtype in checktypes: assert_array_equal(getattr(self.array.attrs, dtype), numpy.array(1, dtype=dtype)) def test01c_setIntAttributes(self): """Checking setting Int attributes (unidimensional NumPy case)""" # 'UInt64' not supported on Win checktypes = ['Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32'] for dtype in checktypes: setattr(self.array.attrs, dtype, numpy.array([1, 2], dtype=dtype)) # Check the results if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray for dtype in checktypes: if common.verbose: print("type, value-->", dtype, getattr(self.array.attrs, dtype)) assert_array_equal(getattr(self.array.attrs, dtype), numpy.array([1, 2], dtype=dtype)) def test01d_setIntAttributes(self): """Checking setting Int attributes (unidimensional, non-contiguous)""" # 'UInt64' not supported on Win checktypes = ['Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32'] for dtype in checktypes: arr = numpy.array([1, 2, 3, 4], dtype=dtype)[::2] setattr(self.array.attrs, dtype, arr) # Check the results if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray for dtype in checktypes: arr = numpy.array([1, 2, 3, 4], dtype=dtype)[::2] if common.verbose: print("type, value-->", dtype, getattr(self.array.attrs, dtype)) assert_array_equal(getattr(self.array.attrs, dtype), arr) def test01e_setIntAttributes(self): """Checking setting Int attributes (bidimensional NumPy case)""" # 'UInt64' not supported on Win checktypes = ['Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32'] for dtype in checktypes: setattr(self.array.attrs, dtype, numpy.array([[1, 2], [2, 3]], dtype=dtype)) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray # Check the results for dtype in checktypes: if common.verbose: print("type, value-->", dtype, getattr(self.array.attrs, dtype)) assert_array_equal(getattr(self.array.attrs, dtype), numpy.array([[1, 2], [2, 3]], dtype=dtype)) def test02a_setFloatAttributes(self): """Checking setting Float (double) attributes.""" # Set some attrs self.array.attrs.pq = 1.0 self.array.attrs.qr = 2.0 self.array.attrs.rs = 3.0 # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) print("qr -->", self.array.attrs.qr) print("rs -->", self.array.attrs.rs) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray self.assertTrue(isinstance(self.root.anarray.attrs.pq, numpy.float_)) self.assertTrue(isinstance(self.root.anarray.attrs.qr, numpy.float_)) self.assertTrue(isinstance(self.root.anarray.attrs.rs, numpy.float_)) self.assertTrue(self.root.anarray.attrs.pq, 1.0) self.assertTrue(self.root.anarray.attrs.qr, 2.0) self.assertTrue(self.root.anarray.attrs.rs, 3.0) def test02b_setFloatAttributes(self): """Checking setting Float attributes (scalar, NumPy case)""" checktypes = ['Float32', 'Float64'] for dtype in checktypes: setattr(self.array.attrs, dtype, numpy.array(1.1, dtype=dtype)) # Check the results if common.verbose: for dtype in checktypes: print("type, value-->", dtype, getattr(self.array.attrs, dtype)) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray for dtype in checktypes: # assert getattr(self.array.attrs, dtype) == 1.1 # In order to make Float32 tests pass. This is legal, not a trick. assert_almost_equal(getattr(self.array.attrs, dtype), 1.1) def test02c_setFloatAttributes(self): """Checking setting Float attributes (unidimensional NumPy case)""" checktypes = ['Float32', 'Float64'] for dtype in checktypes: setattr(self.array.attrs, dtype, numpy.array([1.1, 2.1], dtype=dtype)) # Check the results if common.verbose: for dtype in checktypes: print("type, value-->", dtype, getattr(self.array.attrs, dtype)) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray for dtype in checktypes: assert_array_equal(getattr(self.array.attrs, dtype), numpy.array([1.1, 2.1], dtype=dtype)) def test02d_setFloatAttributes(self): """Checking setting Float attributes (unidimensional, non-contiguous)""" checktypes = ['Float32', 'Float64'] for dtype in checktypes: arr = numpy.array([1.1, 2.1, 3.1, 4.1], dtype=dtype)[1::2] setattr(self.array.attrs, dtype, arr) # Check the results if common.verbose: for dtype in checktypes: print("type, value-->", dtype, getattr(self.array.attrs, dtype)) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray for dtype in checktypes: arr = numpy.array([1.1, 2.1, 3.1, 4.1], dtype=dtype)[1::2] assert_array_equal(getattr(self.array.attrs, dtype), arr) def test02e_setFloatAttributes(self): """Checking setting Int attributes (bidimensional NumPy case)""" checktypes = ['Float32', 'Float64'] for dtype in checktypes: setattr(self.array.attrs, dtype, numpy.array([[1.1, 2.1], [2.1, 3.1]], dtype=dtype)) # Check the results if common.verbose: for dtype in checktypes: print("type, value-->", dtype, getattr(self.array.attrs, dtype)) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray for dtype in checktypes: assert_array_equal( getattr(self.array.attrs, dtype), numpy.array([[1.1, 2.1], [2.1, 3.1]], dtype=dtype)) def test03_setObjectAttributes(self): """Checking setting Object attributes.""" # Set some attrs self.array.attrs.pq = [1.0, 2] self.array.attrs.qr = (1, 2) self.array.attrs.rs = {"ddf": 32.1, "dsd": 1} # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) print("qr -->", self.array.attrs.qr) print("rs -->", self.array.attrs.rs) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray self.assertEqual(self.root.anarray.attrs.pq, [1.0, 2]) self.assertEqual(self.root.anarray.attrs.qr, (1, 2)) self.assertEqual(self.root.anarray.attrs.rs, {"ddf": 32.1, "dsd": 1}) def test04a_setStringAttributes(self): """Checking setting string attributes (scalar case)""" self.array.attrs.pq = 'foo' self.array.attrs.qr = 'bar' self.array.attrs.rs = 'baz' # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) print("qr -->", self.array.attrs.qr) print("rs -->", self.array.attrs.rs) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray self.assertTrue(isinstance(self.root.anarray.attrs.pq, numpy.str_)) self.assertTrue(isinstance(self.root.anarray.attrs.qr, numpy.str_)) self.assertTrue(isinstance(self.root.anarray.attrs.rs, numpy.str_)) self.assertEqual(self.root.anarray.attrs.pq, 'foo') self.assertEqual(self.root.anarray.attrs.qr, 'bar') self.assertEqual(self.root.anarray.attrs.rs, 'baz') def test04b_setStringAttributes(self): """Checking setting string attributes (unidimensional 1-elem case)""" self.array.attrs.pq = numpy.array(['foo']) # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray assert_array_equal(self.root.anarray.attrs.pq, numpy.array(['foo'])) def test04c_setStringAttributes(self): """Checking setting string attributes (empty unidimensional 1-elem case)""" self.array.attrs.pq = numpy.array(['']) # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray if common.verbose: print("pq -->", self.array.attrs.pq) assert_array_equal(self.root.anarray.attrs.pq, numpy.array([''])) def test04d_setStringAttributes(self): """Checking setting string attributes (unidimensional 2-elem case)""" self.array.attrs.pq = numpy.array(['foo', 'bar3']) # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray assert_array_equal(self.root.anarray.attrs.pq, numpy.array(['foo', 'bar3'])) def test04e_setStringAttributes(self): """Checking setting string attributes (empty unidimensional 2-elem case)""" self.array.attrs.pq = numpy.array(['', '']) # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray assert_array_equal(self.root.anarray.attrs.pq, numpy.array(['', ''])) def test04f_setStringAttributes(self): """Checking setting string attributes (bidimensional 4-elem case)""" self.array.attrs.pq = numpy.array([['foo', 'foo2'], ['foo3', 'foo4']]) # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray assert_array_equal(self.root.anarray.attrs.pq, numpy.array([['foo', 'foo2'], ['foo3', 'foo4']])) def test05a_setComplexAttributes(self): """Checking setting Complex (python) attributes.""" # Set some attrs self.array.attrs.pq = 1.0 + 2j self.array.attrs.qr = 2.0 + 3j self.array.attrs.rs = 3.0 + 4j # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) print("qr -->", self.array.attrs.qr) print("rs -->", self.array.attrs.rs) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray self.assertTrue(isinstance(self.root.anarray.attrs.pq, numpy.complex_)) self.assertTrue(isinstance(self.root.anarray.attrs.qr, numpy.complex_)) self.assertTrue(isinstance(self.root.anarray.attrs.rs, numpy.complex_)) self.assertEqual(self.root.anarray.attrs.pq, 1.0 + 2j) self.assertEqual(self.root.anarray.attrs.qr, 2.0 + 3j) self.assertEqual(self.root.anarray.attrs.rs, 3.0 + 4j) def test05b_setComplexAttributes(self): """Checking setting Complex attributes (scalar, NumPy case)""" checktypes = ['complex64', 'complex128'] for dtype in checktypes: setattr(self.array.attrs, dtype, numpy.array(1.1 + 2j, dtype=dtype)) # Check the results if common.verbose: for dtype in checktypes: print("type, value-->", dtype, getattr(self.array.attrs, dtype)) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray for dtype in checktypes: # assert getattr(self.array.attrs, dtype) == 1.1 + 2j # In order to make Complex32 tests pass. assert_almost_equal(getattr(self.array.attrs, dtype), 1.1 + 2j) def test05c_setComplexAttributes(self): """Checking setting Complex attributes (unidimensional NumPy case)""" checktypes = ['Complex32', 'Complex64'] for dtype in checktypes: setattr(self.array.attrs, dtype, numpy.array([1.1, 2.1], dtype=dtype)) # Check the results if common.verbose: for dtype in checktypes: print("type, value-->", dtype, getattr(self.array.attrs, dtype)) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray for dtype in checktypes: assert_array_equal(getattr(self.array.attrs, dtype), numpy.array([1.1, 2.1], dtype=dtype)) def test05d_setComplexAttributes(self): """Checking setting Int attributes (bidimensional NumPy case)""" checktypes = ['Complex32', 'Complex64'] for dtype in checktypes: setattr(self.array.attrs, dtype, numpy.array([[1.1, 2.1], [2.1, 3.1]], dtype=dtype)) # Check the results if common.verbose: for dtype in checktypes: print("type, value-->", dtype, getattr(self.array.attrs, dtype)) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray for dtype in checktypes: assert_array_equal( getattr(self.array.attrs, dtype), numpy.array([[1.1, 2.1], [2.1, 3.1]], dtype=dtype)) def test06a_setUnicodeAttributes(self): """Checking setting unicode attributes (scalar case)""" self.array.attrs.pq = u'para\u0140lel' self.array.attrs.qr = u'' # check #213 or gh-64 self.array.attrs.rs = u'baz' # Check the results if common.verbose: if sys.platform != 'win32': # It seems that Windows cannot print this print("pq -->", repr(self.array.attrs.pq)) # XXX: try to use repr instead # print("pq -->", repr(self.array.attrs.pq)) print("qr -->", self.array.attrs.qr) print("rs -->", self.array.attrs.rs) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray self.assertTrue(isinstance(self.array.attrs.pq, numpy.unicode_)) self.assertTrue(isinstance(self.array.attrs.qr, numpy.unicode_)) self.assertTrue(isinstance(self.array.attrs.rs, numpy.unicode_)) self.assertEqual(self.array.attrs.pq, u'para\u0140lel') self.assertEqual(self.array.attrs.qr, u'') self.assertEqual(self.array.attrs.rs, u'baz') def test06b_setUnicodeAttributes(self): """Checking setting unicode attributes (unidimensional 1-elem case)""" self.array.attrs.pq = numpy.array([u'para\u0140lel']) # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray assert_array_equal(self.array.attrs.pq, numpy.array([u'para\u0140lel'])) def test06c_setUnicodeAttributes(self): """Checking setting unicode attributes (empty unidimensional 1-elem case)""" # The next raises a `TypeError` when unpickled. See: # http://projects.scipy.org/numpy/ticket/1037 # self.array.attrs.pq = numpy.array([u'']) self.array.attrs.pq = numpy.array([u''], dtype="U1") # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray if common.verbose: print("pq -->", repr(self.array.attrs.pq)) assert_array_equal(self.array.attrs.pq, numpy.array([u''], dtype="U1")) def test06d_setUnicodeAttributes(self): """Checking setting unicode attributes (unidimensional 2-elem case)""" self.array.attrs.pq = numpy.array([u'para\u0140lel', u'bar3']) # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray assert_array_equal(self.array.attrs.pq, numpy.array([u'para\u0140lel', u'bar3'])) def test06e_setUnicodeAttributes(self): """Checking setting unicode attributes (empty unidimensional 2-elem case)""" self.array.attrs.pq = numpy.array(['', ''], dtype="U1") # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray assert_array_equal(self.array.attrs.pq, numpy.array(['', ''], dtype="U1")) def test06f_setUnicodeAttributes(self): """Checking setting unicode attributes (bidimensional 4-elem case)""" self.array.attrs.pq = numpy.array([[u'para\u0140lel', 'foo2'], ['foo3', u'para\u0140lel4']]) # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray assert_array_equal(self.array.attrs.pq, numpy.array([[u'para\u0140lel', 'foo2'], ['foo3', u'para\u0140lel4']])) def test07a_setRecArrayAttributes(self): """Checking setting RecArray (NumPy) attributes.""" dt = numpy.dtype('i4,f8') # Set some attrs self.array.attrs.pq = numpy.zeros(2, dt) self.array.attrs.qr = numpy.ones((2, 2), dt) self.array.attrs.rs = numpy.array([(1, 2.)], dt) # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) print("qr -->", self.array.attrs.qr) print("rs -->", self.array.attrs.rs) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray self.assertTrue(isinstance(self.array.attrs.pq, numpy.ndarray)) self.assertTrue(isinstance(self.array.attrs.qr, numpy.ndarray)) self.assertTrue(isinstance(self.array.attrs.rs, numpy.ndarray)) assert_array_equal(self.array.attrs.pq, numpy.zeros(2, dt)) assert_array_equal(self.array.attrs.qr, numpy.ones((2, 2), dt)) assert_array_equal(self.array.attrs.rs, numpy.array([(1, 2.)], dt)) def test07b_setRecArrayAttributes(self): """Checking setting nested RecArray (NumPy) attributes.""" # Build a nested dtype dt = numpy.dtype([('f1', [('f1', 'i2'), ('f2', 'f8')])]) # Set some attrs self.array.attrs.pq = numpy.zeros(2, dt) self.array.attrs.qr = numpy.ones((2, 2), dt) self.array.attrs.rs = numpy.array([((1, 2.),)], dt) # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) print("qr -->", self.array.attrs.qr) print("rs -->", self.array.attrs.rs) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray self.assertTrue(isinstance(self.array.attrs.pq, numpy.ndarray)) self.assertTrue(isinstance(self.array.attrs.qr, numpy.ndarray)) self.assertTrue(isinstance(self.array.attrs.rs, numpy.ndarray)) assert_array_equal(self.array.attrs.pq, numpy.zeros(2, dt)) assert_array_equal(self.array.attrs.qr, numpy.ones((2, 2), dt)) assert_array_equal(self.array.attrs.rs, numpy.array([((1, 2),)], dt)) def test07c_setRecArrayAttributes(self): """Checking setting multidim nested RecArray (NumPy) attributes.""" # Build a nested dtype dt = numpy.dtype([('f1', [('f1', 'i2', (2,)), ('f2', 'f8')])]) # Set some attrs self.array.attrs.pq = numpy.zeros(2, dt) self.array.attrs.qr = numpy.ones((2, 2), dt) self.array.attrs.rs = numpy.array([(([1, 3], 2.),)], dt) # Check the results if common.verbose: print("pq -->", self.array.attrs.pq) print("qr -->", self.array.attrs.qr) print("rs -->", self.array.attrs.rs) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r+") self.root = self.fileh.root self.array = self.fileh.root.anarray self.assertTrue(isinstance(self.array.attrs.pq, numpy.ndarray)) self.assertTrue(isinstance(self.array.attrs.qr, numpy.ndarray)) self.assertTrue(isinstance(self.array.attrs.rs, numpy.ndarray)) assert_array_equal(self.array.attrs.pq, numpy.zeros(2, dt)) assert_array_equal(self.array.attrs.qr, numpy.ones((2, 2), dt)) assert_array_equal(self.array.attrs.rs, numpy.array( [(([1, 3], 2),)], dt)) class NotCloseTypesTestCase(TypesTestCase): close = 0 class CloseTypesTestCase(TypesTestCase): close = 1 class NoSysAttrsTestCase(unittest.TestCase): def setUp(self): # Create an instance of HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file( self.file, mode="w", pytables_sys_attrs=False) self.root = self.fileh.root # Create a table object self.table = self.fileh.create_table(self.root, 'atable', Record, "Table title") # Create an array object self.array = self.fileh.create_array(self.root, 'anarray', [1], "Array title") # Create a group object self.group = self.fileh.create_group(self.root, 'agroup', "Group title") def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def test00_listAttributes(self): """Checking listing attributes (no system attrs version).""" # With a Group object self.group._v_attrs.pq = "1" self.group._v_attrs.qr = "2" self.group._v_attrs.rs = "3" if common.verbose: print("Attribute list:", self.group._v_attrs._f_list()) # Now, try with a Table object self.table.attrs.a = "1" self.table.attrs.c = "2" self.table.attrs.b = "3" if common.verbose: print("Attribute list:", self.table.attrs._f_list()) # Finally, try with an Array object self.array.attrs.k = "1" self.array.attrs.j = "2" self.array.attrs.i = "3" if common.verbose: print("Attribute list:", self.array.attrs._f_list()) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file( self.file, mode="r+") self.root = self.fileh.root agroup = self.root.agroup self.assertEqual(agroup._v_attrs._f_list("user"), ["pq", "qr", "rs"]) self.assertEqual(agroup._v_attrs._f_list("sys"), []) self.assertEqual(agroup._v_attrs._f_list("all"), ["pq", "qr", "rs"]) atable = self.root.atable self.assertEqual(atable.attrs._f_list(), ["a", "b", "c"]) self.assertEqual(atable.attrs._f_list("sys"), []) self.assertEqual(atable.attrs._f_list("all"), ["a", "b", "c"]) anarray = self.root.anarray self.assertEqual(anarray.attrs._f_list(), ["i", "j", "k"]) self.assertEqual(anarray.attrs._f_list("sys"), []) self.assertEqual(anarray.attrs._f_list("all"), ["i", "j", "k"]) class NoSysAttrsNotClose(NoSysAttrsTestCase): close = False class NoSysAttrsClose(NoSysAttrsTestCase): close = True class SegFaultPythonTestCase(common.TempFileMixin, common.PyTablesTestCase): def test00_segfault(self): """Checking workaround for Python unpickle problem (see #253).""" self.h5file.root._v_attrs.trouble1 = "0" self.assertEqual(self.h5file.root._v_attrs.trouble1, "0") self.h5file.root._v_attrs.trouble2 = "0." self.assertEqual(self.h5file.root._v_attrs.trouble2, "0.") # Problem happens after reopening self._reopen() self.assertEqual(self.h5file.root._v_attrs.trouble1, "0") self.assertEqual(self.h5file.root._v_attrs.trouble2, "0.") if common.verbose: print("Great! '0' and '0.' values can be safely retrieved.") class VlenStrAttrTestCase(PyTablesTestCase): def test01_vlen_str_scalar(self): """Checking file with variable length string attributes.""" filename = self._testFilename('vlstr_attr.h5') fileh = open_file(filename) attr = "vlen_str_scalar" self.assertEqual(fileh.get_node_attr("/", attr), attr.encode('ascii')) fileh.close() def test02_vlen_str_array(self): """Checking file with variable length string attributes (1d).""" filename = self._testFilename('vlstr_attr.h5') fileh = open_file(filename) attr = "vlen_str_array" v = fileh.get_node_attr('/', attr) self.assertEqual(v.ndim, 1) for idx, item in enumerate(v): value = "%s_%d" % (attr, idx) self.assertEqual(item, value.encode('ascii')) fileh.close() def test03_vlen_str_matrix(self): """Checking file with variable length string attributes (2d).""" filename = self._testFilename('vlstr_attr.h5') fileh = open_file(filename) attr = "vlen_str_matrix" m = fileh.get_node_attr('/', attr) self.assertEqual(m.ndim, 2) for row, rowdata in enumerate(m): for col, item in enumerate(rowdata): value = "%s_%d%d" % (attr, row, col) self.assertEqual(item, value.encode('ascii')) fileh.close() class UnsupportedAttrTypeTestCase(PyTablesTestCase): def test00_unsupportedType(self): """Checking file with unsupported type.""" filename = self._testFilename('attr-u16.h5') fileh = open_file(filename) self.failUnlessWarns(DataTypeWarning, repr, fileh) fileh.close() # Test for specific system attributes in EArray class SpecificAttrsTestCase(common.TempFileMixin, common.PyTablesTestCase): def test00_earray(self): "Testing EArray specific attrs (create)." ea = self.h5file.create_earray('/', 'ea', Int32Atom(), (2, 0, 4)) if common.verbose: print("EXTDIM-->", ea.attrs.EXTDIM) self.assertEqual(ea.attrs.EXTDIM, 1) def test01_earray(self): "Testing EArray specific attrs (open)." ea = self.h5file.create_earray('/', 'ea', Int32Atom(), (0, 1, 4)) self._reopen('r') ea = self.h5file.root.ea if common.verbose: print("EXTDIM-->", ea.attrs.EXTDIM) self.assertEqual(ea.attrs.EXTDIM, 0) #---------------------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() niter = 1 for i in range(niter): theSuite.addTest(unittest.makeSuite(NotCloseCreate)) theSuite.addTest(unittest.makeSuite(CloseCreate)) theSuite.addTest(unittest.makeSuite(NoCacheNotCloseCreate)) theSuite.addTest(unittest.makeSuite(NoCacheCloseCreate)) theSuite.addTest(unittest.makeSuite(DictCacheNotCloseCreate)) theSuite.addTest(unittest.makeSuite(DictCacheCloseCreate)) theSuite.addTest(unittest.makeSuite(NotCloseTypesTestCase)) theSuite.addTest(unittest.makeSuite(CloseTypesTestCase)) theSuite.addTest(unittest.makeSuite(NoSysAttrsNotClose)) theSuite.addTest(unittest.makeSuite(NoSysAttrsClose)) theSuite.addTest(unittest.makeSuite(SegFaultPythonTestCase)) theSuite.addTest(unittest.makeSuite(VlenStrAttrTestCase)) theSuite.addTest(unittest.makeSuite(UnsupportedAttrTypeTestCase)) theSuite.addTest(unittest.makeSuite(SpecificAttrsTestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/test_backcompat.py000066400000000000000000000201401231437614300216350ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import os import shutil import tempfile import warnings import unittest import numpy from tables import * from tables.exceptions import FlavorWarning from tables.tests import common from tables.tests.common import allequal # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup # Check read Tables from pytables version 0.8 class BackCompatTablesTestCase(common.PyTablesTestCase): #---------------------------------------- def test01_readTable(self): """Checking backward compatibility of old formats of tables.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_readTable..." % self.__class__.__name__) # Create an instance of an HDF5 Table warnings.filterwarnings("ignore", category=UserWarning) self.fileh = open_file(self._testFilename(self.file), "r") warnings.filterwarnings("default", category=UserWarning) table = self.fileh.get_node("/tuple0") # Read the 100 records result = [rec['var2'] for rec in table] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Last record in table ==>", rec) print("Total selected records in table ==> ", len(result)) self.assertEqual(len(result), 100) self.fileh.close() class Table2_1LZO(BackCompatTablesTestCase): file = "Table2_1_lzo_nrv2e_shuffle.h5" # pytables 0.8.x versions and after class Tables_LZO1(BackCompatTablesTestCase): file = "Tables_lzo1.h5" # files compressed with LZO1 class Tables_LZO1_shuffle(BackCompatTablesTestCase): file = "Tables_lzo1_shuffle.h5" # files compressed with LZO1 and shuffle class Tables_LZO2(BackCompatTablesTestCase): file = "Tables_lzo2.h5" # files compressed with LZO2 class Tables_LZO2_shuffle(BackCompatTablesTestCase): file = "Tables_lzo2_shuffle.h5" # files compressed with LZO2 and shuffle # Check read attributes from PyTables >= 1.0 properly class BackCompatAttrsTestCase(common.PyTablesTestCase): file = "zerodim-attrs-%s.h5" def test01_readAttr(self): """Checking backward compatibility of old formats for attributes.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_readAttr..." % self.__class__.__name__) # Read old formats filename = self._testFilename(self.file) self.fileh = open_file(filename % self.format, "r") a = self.fileh.get_node("/a") scalar = numpy.array(1, dtype="int32") vector = numpy.array([1], dtype="int32") if self.format == "1.3": self.assertTrue(allequal(a.attrs.arrdim1, vector)) self.assertTrue(allequal(a.attrs.arrscalar, scalar)) self.assertEqual(a.attrs.pythonscalar, 1) elif self.format == "1.4": self.assertTrue(allequal(a.attrs.arrdim1, vector)) self.assertTrue(allequal(a.attrs.arrscalar, scalar)) self.assertTrue(allequal(a.attrs.pythonscalar, scalar)) self.fileh.close() class Attrs_1_3(BackCompatAttrsTestCase): format = "1.3" # pytables 1.0.x versions and earlier class Attrs_1_4(BackCompatAttrsTestCase): format = "1.4" # pytables 1.1.x versions and later class VLArrayTestCase(common.PyTablesTestCase): def test01_backCompat(self): """Checking backward compatibility with old flavors of VLArray.""" # Open a PYTABLES_FORMAT_VERSION=1.6 file filename = self._testFilename("flavored_vlarrays-format1.6.h5") fileh = open_file(filename, "r") # Check that we can read the contents without problems (nor warnings!) vlarray1 = fileh.root.vlarray1 self.assertEqual(vlarray1.flavor, "numeric") vlarray2 = fileh.root.vlarray2 self.assertEqual(vlarray2.flavor, "python") self.assertEqual(vlarray2[1], [b'5', b'6', b'77']) fileh.close() # Make sure that 1.x files with TimeXX types continue to be readable # and that its byteorder is correctly retrieved. class TimeTestCase(common.PyTablesTestCase): def setUp(self): # Open a PYTABLES_FORMAT_VERSION=1.x file filename = self._testFilename("time-table-vlarray-1_x.h5") self.fileh = open_file(filename, "r") def tearDown(self): self.fileh.close() def test00_table(self): """Checking backward compatibility with old TimeXX types (tables).""" # Check that we can read the contents without problems (nor warnings!) table = self.fileh.root.table self.assertEqual(table.byteorder, "little") def test01_vlarray(self): """Checking backward compatibility with old TimeXX types (vlarrays).""" # Check that we can read the contents without problems (nor warnings!) vlarray4 = self.fileh.root.vlarray4 self.assertEqual(vlarray4.byteorder, "little") vlarray8 = self.fileh.root.vlarray4 self.assertEqual(vlarray8.byteorder, "little") class OldFlavorsTestCase01(common.PyTablesTestCase): close = False # numeric def test01_open(self): """Checking opening of (X)Array (old 'numeric' flavor)""" # Open the HDF5 with old numeric flavor filename = self._testFilename("oldflavor_numeric.h5") fileh = open_file(filename) # Assert other properties in array self.assertEqual(fileh.root.array1.flavor, 'numeric') self.assertEqual(fileh.root.array2.flavor, 'python') self.assertEqual(fileh.root.carray1.flavor, 'numeric') self.assertEqual(fileh.root.carray2.flavor, 'python') self.assertEqual(fileh.root.vlarray1.flavor, 'numeric') self.assertEqual(fileh.root.vlarray2.flavor, 'python') # Close the file fileh.close() def test02_copy(self): """Checking (X)Array.copy() method ('numetic' flavor)""" srcfile = self._testFilename("oldflavor_numeric.h5") tmpfile = tempfile.mktemp(".h5") shutil.copy(srcfile, tmpfile) # Open the HDF5 with old numeric flavor fileh = open_file(tmpfile, "r+") # Copy to another location self.failUnlessWarns(FlavorWarning, fileh.root.array1.copy, '/', 'array1copy') fileh.root.array2.copy('/', 'array2copy') fileh.root.carray1.copy('/', 'carray1copy') fileh.root.carray2.copy('/', 'carray2copy') fileh.root.vlarray1.copy('/', 'vlarray1copy') fileh.root.vlarray2.copy('/', 'vlarray2copy') if self.close: fileh.close() fileh = open_file(tmpfile) else: fileh.flush() # Assert other properties in array self.assertEqual(fileh.root.array1copy.flavor, 'numeric') self.assertEqual(fileh.root.array2copy.flavor, 'python') self.assertEqual(fileh.root.carray1copy.flavor, 'numeric') self.assertEqual(fileh.root.carray2copy.flavor, 'python') self.assertEqual(fileh.root.vlarray1copy.flavor, 'numeric') self.assertEqual(fileh.root.vlarray2copy.flavor, 'python') # Close the file fileh.close() os.remove(tmpfile) class OldFlavorsTestCase02(common.PyTablesTestCase): close = True #---------------------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() niter = 1 lzo_avail = which_lib_version("lzo") is not None for n in range(niter): theSuite.addTest(unittest.makeSuite(VLArrayTestCase)) theSuite.addTest(unittest.makeSuite(TimeTestCase)) theSuite.addTest(unittest.makeSuite(OldFlavorsTestCase01)) theSuite.addTest(unittest.makeSuite(OldFlavorsTestCase02)) if lzo_avail: theSuite.addTest(unittest.makeSuite(Table2_1LZO)) theSuite.addTest(unittest.makeSuite(Tables_LZO1)) theSuite.addTest(unittest.makeSuite(Tables_LZO1_shuffle)) theSuite.addTest(unittest.makeSuite(Tables_LZO2)) theSuite.addTest(unittest.makeSuite(Tables_LZO2_shuffle)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/test_basics.py000066400000000000000000003111101231437614300207750ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import os import sys import Queue import shutil import tempfile import unittest import warnings import threading import subprocess try: import multiprocessing as mp multiprocessing_imported = True except ImportError: multiprocessing_imported = False import numpy import tables import tables.flavor from tables import * from tables.flavor import all_flavors, array_of_flavor from tables.tests import common from tables.parameters import NODE_CACHE_SLOTS from tables.description import descr_from_dtype, dtype_from_descr # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup class OpenFileFailureTestCase(common.PyTablesTestCase): def setUp(self): import tables.file self.N = len(tables.file._open_files) self.open_files = tables.file._open_files def test01_open_file(self): """Checking opening of a non existing file.""" filename = tempfile.mktemp(".h5") try: fileh = open_file(filename) fileh.close() except IOError: self.assertEqual(self.N, len(self.open_files)) else: self.fail("IOError exception not raised") def test02_open_file(self): """Checking opening of an existing non HDF5 file.""" # create a dummy file filename = tempfile.mktemp(".h5") open(filename, 'wb').close() # Try to open the dummy file try: try: fileh = tables.open_file(filename) fileh.close() except HDF5ExtError: self.assertEqual(self.N, len(self.open_files)) else: self.fail("HDF5ExtError exception not raised") finally: os.remove(filename) def test03_open_file(self): """Checking opening of an existing file with invalid mode.""" # See gh-318 # create a dummy file filename = tempfile.mktemp(".h5") fileh = tables.open_file(filename, "w") fileh.close() # Try to open the dummy file self.assertRaises(ValueError, tables.open_file, filename, "ab") os.remove(filename) class OpenFileTestCase(common.PyTablesTestCase): def setUp(self): # Create an HDF5 file self.file = tempfile.mktemp(".h5") fileh = open_file(self.file, mode="w", title="File title", node_cache_slots=self.node_cache_slots) root = fileh.root # Create an array fileh.create_array(root, 'array', [1, 2], title="Array example") fileh.create_table(root, 'table', {'var1': IntCol()}, "Table example") root._v_attrs.testattr = 41 # Create another array object fileh.create_array(root, 'anarray', [1], "Array title") fileh.create_table(root, 'atable', {'var1': IntCol()}, "Table title") # Create a group object group = fileh.create_group(root, 'agroup', "Group title") group._v_attrs.testattr = 42 # Create a some objects there array1 = fileh.create_array(group, 'anarray1', [1, 2, 3, 4, 5, 6, 7], "Array title 1") array1.attrs.testattr = 42 fileh.create_array(group, 'anarray2', [2], "Array title 2") fileh.create_table(group, 'atable1', { 'var1': IntCol()}, "Table title 1") ra = numpy.rec.array([(1, 11, 'a')], formats='u1,f4,a1') fileh.create_table(group, 'atable2', ra, "Table title 2") # Create a lonely group in first level fileh.create_group(root, 'agroup2', "Group title 2") # Create a new group in the second level group3 = fileh.create_group(group, 'agroup3', "Group title 3") # Create a new group in the third level fileh.create_group(group3, 'agroup4', "Group title 4") # Create an array in the root with the same name as one in 'agroup' fileh.create_array(root, 'anarray1', [1, 2], title="Array example") fileh.close() def tearDown(self): # Remove the temporary file os.remove(self.file) common.cleanup(self) def test00_newFile(self): """Checking creation of a new file.""" # Create an HDF5 file file = tempfile.mktemp(".h5") fileh = open_file( file, mode="w", node_cache_slots=self.node_cache_slots) fileh.create_array(fileh.root, 'array', [1, 2], title="Array example") # Get the CLASS attribute of the arr object class_ = fileh.root.array.attrs.CLASS # Close and delete the file fileh.close() os.remove(file) self.assertEqual(class_.capitalize(), "Array") def test00_newFile_unicode_filename(self): temp_dir = tempfile.mkdtemp() file_path = unicode(os.path.join(temp_dir, 'test.h5')) with open_file(file_path, 'w') as fileh: self.assertTrue(fileh, File) shutil.rmtree(temp_dir) def test00_newFile_numpy_str_filename(self): temp_dir = tempfile.mkdtemp() file_path = numpy.str_(os.path.join(temp_dir, 'test.h5')) with open_file(file_path, 'w') as fileh: self.assertTrue(fileh, File) shutil.rmtree(temp_dir) def test00_newFile_numpy_unicode_filename(self): temp_dir = tempfile.mkdtemp() file_path = numpy.unicode_(os.path.join(temp_dir, 'test.h5')) with open_file(file_path, 'w') as fileh: self.assertTrue(fileh, File) shutil.rmtree(temp_dir) def test01_openFile(self): """Checking opening of an existing file.""" # Open the old HDF5 file fileh = open_file( self.file, mode="r", node_cache_slots=self.node_cache_slots) # Get the CLASS attribute of the arr object title = fileh.root.array.get_attr("TITLE") self.assertEqual(title, "Array example") fileh.close() def test02_appendFile(self): """Checking appending objects to an existing file.""" # Append a new array to the existing file fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) fileh.create_array(fileh.root, 'array2', [3, 4], title="Title example 2") fileh.close() # Open this file in read-only mode fileh = open_file( self.file, mode="r", node_cache_slots=self.node_cache_slots) # Get the CLASS attribute of the arr object title = fileh.root.array2.get_attr("TITLE") self.assertEqual(title, "Title example 2") fileh.close() def test02b_appendFile2(self): """Checking appending objects to an existing file ("a" version)""" # Append a new array to the existing file fileh = open_file( self.file, mode="a", node_cache_slots=self.node_cache_slots) fileh.create_array(fileh.root, 'array2', [3, 4], title="Title example 2") fileh.close() # Open this file in read-only mode fileh = open_file( self.file, mode="r", node_cache_slots=self.node_cache_slots) # Get the CLASS attribute of the arr object title = fileh.root.array2.get_attr("TITLE") self.assertEqual(title, "Title example 2") fileh.close() # Begin to raise errors... def test03_appendErrorFile(self): """Checking appending objects to an existing file in "w" mode.""" # Append a new array to the existing file but in write mode # so, the existing file should be deleted! fileh = open_file( self.file, mode="w", node_cache_slots=self.node_cache_slots) fileh.create_array(fileh.root, 'array2', [3, 4], title="Title example 2") fileh.close() # Open this file in read-only mode fileh = open_file( self.file, mode="r", node_cache_slots=self.node_cache_slots) try: # Try to get the 'array' object in the old existing file fileh.root.array except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test04a_openErrorFile(self): """Checking opening a non-existing file for reading""" try: open_file("nonexistent.h5", mode="r", node_cache_slots=self.node_cache_slots) except IOError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next IOError was catched!") print(value) else: self.fail("expected an IOError") def test04b_alternateRootFile(self): """Checking alternate root access to the object tree.""" # Open the existent HDF5 file fileh = open_file(self.file, mode="r", root_uep="/agroup", node_cache_slots=self.node_cache_slots) # Get the CLASS attribute of the arr object if common.verbose: print("\nFile tree dump:", fileh) title = fileh.root.anarray1.get_attr("TITLE") # Get the node again, as this can trigger errors in some situations anarray1 = fileh.root.anarray1 self.assertTrue(anarray1 is not None) self.assertEqual(title, "Array title 1") fileh.close() # This test works well, but HDF5 emits a series of messages that # may loose the user. It is better to deactivate it. def notest04c_alternateRootFile(self): """Checking non-existent alternate root access to the object tree""" try: open_file(self.file, mode="r", root_uep="/nonexistent", node_cache_slots=self.node_cache_slots) except RuntimeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next RuntimeError was catched!") print(value) else: self.fail("expected an IOError") def test05a_removeGroupRecursively(self): """Checking removing a group recursively.""" # Delete a group with leafs fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) try: fileh.remove_node(fileh.root.agroup) except NodeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NodeError was catched!") print(value) else: self.fail("expected a NodeError") # This should work now fileh.remove_node(fileh.root, 'agroup', recursive=1) fileh.close() # Open this file in read-only mode fileh = open_file( self.file, mode="r", node_cache_slots=self.node_cache_slots) # Try to get the removed object try: fileh.root.agroup except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") # Try to get a child of the removed object try: fileh.get_node("/agroup/agroup3") except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test05b_removeGroupRecursively(self): """Checking removing a group recursively and access to it immediately.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05b_removeGroupRecursively..." % self.__class__.__name__) # Delete a group with leafs fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) try: fileh.remove_node(fileh.root, 'agroup') except NodeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NodeError was catched!") print(value) else: self.fail("expected a NodeError") # This should work now fileh.remove_node(fileh.root, 'agroup', recursive=1) # Try to get the removed object try: fileh.root.agroup except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") # Try to get a child of the removed object try: fileh.get_node("/agroup/agroup3") except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test06_removeNodeWithDel(self): """Checking removing a node using ``__delattr__()``""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) try: # This should fail because there is no *Python attribute* # called ``agroup``. del fileh.root.agroup except AttributeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next AttributeError was catched!") print(value) else: self.fail("expected an AttributeError") fileh.close() def test06a_removeGroup(self): """Checking removing a lonely group from an existing file.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) fileh.remove_node(fileh.root, 'agroup2') fileh.close() # Open this file in read-only mode fileh = open_file( self.file, mode="r", node_cache_slots=self.node_cache_slots) # Try to get the removed object try: fileh.root.agroup2 except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test06b_removeLeaf(self): """Checking removing Leaves from an existing file.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) fileh.remove_node(fileh.root, 'anarray') fileh.close() # Open this file in read-only mode fileh = open_file( self.file, mode="r", node_cache_slots=self.node_cache_slots) # Try to get the removed object try: fileh.root.anarray except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test06c_removeLeaf(self): """Checking removing Leaves and access it immediately.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) fileh.remove_node(fileh.root, 'anarray') # Try to get the removed object try: fileh.root.anarray except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test06d_removeLeaf(self): """Checking removing a non-existent node""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # Try to get the removed object try: fileh.remove_node(fileh.root, 'nonexistent') except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test06e_removeTable(self): """Checking removing Tables from an existing file.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) fileh.remove_node(fileh.root, 'atable') fileh.close() # Open this file in read-only mode fileh = open_file( self.file, mode="r", node_cache_slots=self.node_cache_slots) # Try to get the removed object try: fileh.root.atable except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test07_renameLeaf(self): """Checking renaming a leave and access it after a close/open.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) fileh.rename_node(fileh.root.anarray, 'anarray2') fileh.close() # Open this file in read-only mode fileh = open_file( self.file, mode="r", node_cache_slots=self.node_cache_slots) # Ensure that the new name exists array_ = fileh.root.anarray2 self.assertEqual(array_.name, "anarray2") self.assertEqual(array_._v_pathname, "/anarray2") self.assertEqual(array_._v_depth, 1) # Try to get the previous object with the old name try: fileh.root.anarray except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test07b_renameLeaf(self): """Checking renaming Leaves and accesing them immediately.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) fileh.rename_node(fileh.root.anarray, 'anarray2') # Ensure that the new name exists array_ = fileh.root.anarray2 self.assertEqual(array_.name, "anarray2") self.assertEqual(array_._v_pathname, "/anarray2") self.assertEqual(array_._v_depth, 1) # Try to get the previous object with the old name try: fileh.root.anarray except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test07c_renameLeaf(self): """Checking renaming Leaves and modify attributes after that.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) fileh.rename_node(fileh.root.anarray, 'anarray2') array_ = fileh.root.anarray2 array_.attrs.TITLE = "hello" # Ensure that the new attribute has been written correctly self.assertEqual(array_.title, "hello") self.assertEqual(array_.attrs.TITLE, "hello") fileh.close() def test07d_renameLeaf(self): """Checking renaming a Group under a nested group.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) fileh.rename_node(fileh.root.agroup.anarray2, 'anarray3') # Ensure that we can access n attributes in the new group node = fileh.root.agroup.anarray3 self.assertEqual(node._v_title, "Array title 2") fileh.close() def test08_renameToExistingLeaf(self): """Checking renaming a node to an existing name.""" # Open this file fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # Try to get the previous object with the old name try: fileh.rename_node(fileh.root.anarray, 'array') except NodeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NodeError was catched!") print(value) else: self.fail("expected an NodeError") # Now overwrite the destination node. anarray = fileh.root.anarray fileh.rename_node(anarray, 'array', overwrite=True) self.assertTrue('/anarray' not in fileh) self.assertTrue(fileh.root.array is anarray) fileh.close() def test08b_renameToNotValidNaturalName(self): """Checking renaming a node to a non-valid natural name""" # Open this file fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) warnings.filterwarnings("error", category=NaturalNameWarning) # Try to get the previous object with the old name try: fileh.rename_node(fileh.root.anarray, 'array 2') except NaturalNameWarning: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NaturalNameWarning was catched!") print(value) else: self.fail("expected an NaturalNameWarning") # Reset the warning warnings.filterwarnings("default", category=NaturalNameWarning) fileh.close() def test09_renameGroup(self): """Checking renaming a Group and access it after a close/open.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) fileh.rename_node(fileh.root.agroup, 'agroup3') fileh.close() # Open this file in read-only mode fileh = open_file( self.file, mode="r", node_cache_slots=self.node_cache_slots) # Ensure that the new name exists group = fileh.root.agroup3 self.assertEqual(group._v_name, "agroup3") self.assertEqual(group._v_pathname, "/agroup3") # The children of this group also must be accessible through the # new name path group2 = fileh.get_node("/agroup3/agroup3") self.assertEqual(group2._v_name, "agroup3") self.assertEqual(group2._v_pathname, "/agroup3/agroup3") # Try to get the previous object with the old name try: fileh.root.agroup except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") # Try to get a child with the old pathname try: fileh.get_node("/agroup/agroup3") except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test09b_renameGroup(self): """Checking renaming a Group and access it immediately.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) fileh.rename_node(fileh.root.agroup, 'agroup3') # Ensure that the new name exists group = fileh.root.agroup3 self.assertEqual(group._v_name, "agroup3") self.assertEqual(group._v_pathname, "/agroup3") # The children of this group also must be accessible through the # new name path group2 = fileh.get_node("/agroup3/agroup3") self.assertEqual(group2._v_name, "agroup3") self.assertEqual(group2._v_pathname, "/agroup3/agroup3") # Try to get the previous object with the old name try: fileh.root.agroup except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") # Try to get a child with the old pathname try: fileh.get_node("/agroup/agroup3") except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test09c_renameGroup(self): """Checking renaming a Group and modify attributes afterwards.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) fileh.rename_node(fileh.root.agroup, 'agroup3') # Ensure that we can modify attributes in the new group group = fileh.root.agroup3 group._v_attrs.TITLE = "Hello" self.assertEqual(group._v_title, "Hello") self.assertEqual(group._v_attrs.TITLE, "Hello") fileh.close() def test09d_renameGroup(self): """Checking renaming a Group under a nested group.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) fileh.rename_node(fileh.root.agroup.agroup3, 'agroup4') # Ensure that we can access n attributes in the new group group = fileh.root.agroup.agroup4 self.assertEqual(group._v_title, "Group title 3") fileh.close() def test09e_renameGroup(self): """Checking renaming a Group with nested groups in the LRU cache.""" # This checks for ticket #126. fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # Load intermediate groups and keep a nested one alive. g = fileh.root.agroup.agroup3.agroup4 self.assertTrue(g is not None) fileh.rename_node('/', name='agroup', newname='agroup_') self.assertTrue('/agroup_/agroup4' not in fileh) # see ticket #126 self.assertTrue('/agroup' not in fileh) for newpath in ['/agroup_', '/agroup_/agroup3', '/agroup_/agroup3/agroup4']: self.assertTrue(newpath in fileh) self.assertEqual(newpath, fileh.get_node(newpath)._v_pathname) fileh.close() def test10_moveLeaf(self): """Checking moving a leave and access it after a close/open.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) newgroup = fileh.create_group("/", "newgroup") fileh.move_node(fileh.root.anarray, newgroup, 'anarray2') fileh.close() # Open this file in read-only mode fileh = open_file( self.file, mode="r", node_cache_slots=self.node_cache_slots) # Ensure that the new name exists array_ = fileh.root.newgroup.anarray2 self.assertEqual(array_.name, "anarray2") self.assertEqual(array_._v_pathname, "/newgroup/anarray2") self.assertEqual(array_._v_depth, 2) # Try to get the previous object with the old name try: fileh.root.anarray except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test10b_moveLeaf(self): """Checking moving a leave and access it without a close/open.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) newgroup = fileh.create_group("/", "newgroup") fileh.move_node(fileh.root.anarray, newgroup, 'anarray2') # Ensure that the new name exists array_ = fileh.root.newgroup.anarray2 self.assertEqual(array_.name, "anarray2") self.assertEqual(array_._v_pathname, "/newgroup/anarray2") self.assertEqual(array_._v_depth, 2) # Try to get the previous object with the old name try: fileh.root.anarray except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test10c_moveLeaf(self): """Checking moving Leaves and modify attributes after that.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) newgroup = fileh.create_group("/", "newgroup") fileh.move_node(fileh.root.anarray, newgroup, 'anarray2') array_ = fileh.root.newgroup.anarray2 array_.attrs.TITLE = "hello" # Ensure that the new attribute has been written correctly self.assertEqual(array_.title, "hello") self.assertEqual(array_.attrs.TITLE, "hello") fileh.close() def test10d_moveToExistingLeaf(self): """Checking moving a leaf to an existing name.""" # Open this file fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # Try to get the previous object with the old name try: fileh.move_node(fileh.root.anarray, fileh.root, 'array') except NodeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NodeError was catched!") print(value) else: self.fail("expected an NodeError") fileh.close() def test10_2_moveTable(self): """Checking moving a table and access it after a close/open.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) newgroup = fileh.create_group("/", "newgroup") fileh.move_node(fileh.root.atable, newgroup, 'atable2') fileh.close() # Open this file in read-only mode fileh = open_file( self.file, mode="r", node_cache_slots=self.node_cache_slots) # Ensure that the new name exists table_ = fileh.root.newgroup.atable2 self.assertEqual(table_.name, "atable2") self.assertEqual(table_._v_pathname, "/newgroup/atable2") self.assertEqual(table_._v_depth, 2) # Try to get the previous object with the old name try: fileh.root.atable except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test10_2b_moveTable(self): """Checking moving a table and access it without a close/open.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) newgroup = fileh.create_group("/", "newgroup") fileh.move_node(fileh.root.atable, newgroup, 'atable2') # Ensure that the new name exists table_ = fileh.root.newgroup.atable2 self.assertEqual(table_.name, "atable2") self.assertEqual(table_._v_pathname, "/newgroup/atable2") self.assertEqual(table_._v_depth, 2) # Try to get the previous object with the old name try: fileh.root.atable except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test10_2b_bis_moveTable(self): """Checking moving a table and use cached row without a close/open.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) newgroup = fileh.create_group("/", "newgroup") # Cache the Row attribute prior to the move row = fileh.root.atable.row fileh.move_node(fileh.root.atable, newgroup, 'atable2') # Ensure that the new name exists table_ = fileh.root.newgroup.atable2 self.assertEqual(table_.name, "atable2") self.assertEqual(table_._v_pathname, "/newgroup/atable2") self.assertEqual(table_._v_depth, 2) # Ensure that cache Row attribute has been updated row = table_.row self.assertEqual(table_._v_pathname, row.table._v_pathname) nrows = table_.nrows # Add a new row just to make sure that this works row.append() table_.flush() self.assertEqual(table_.nrows, nrows + 1) fileh.close() def test10_2c_moveTable(self): """Checking moving tables and modify attributes after that.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) newgroup = fileh.create_group("/", "newgroup") fileh.move_node(fileh.root.atable, newgroup, 'atable2') table_ = fileh.root.newgroup.atable2 table_.attrs.TITLE = "hello" # Ensure that the new attribute has been written correctly self.assertEqual(table_.title, "hello") self.assertEqual(table_.attrs.TITLE, "hello") fileh.close() def test10_2d_moveToExistingTable(self): """Checking moving a table to an existing name.""" # Open this file fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # Try to get the previous object with the old name try: fileh.move_node(fileh.root.atable, fileh.root, 'table') except NodeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NodeError was catched!") print(value) else: self.fail("expected an NodeError") fileh.close() def test10_2e_moveToExistingTableOverwrite(self): """Checking moving a table to an existing name, overwriting it.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) srcNode = fileh.root.atable fileh.move_node(srcNode, fileh.root, 'table', overwrite=True) dstNode = fileh.root.table self.assertTrue(srcNode is dstNode) fileh.close() def test11_moveGroup(self): """Checking moving a Group and access it after a close/open.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) newgroup = fileh.create_group(fileh.root, 'newgroup') fileh.move_node(fileh.root.agroup, newgroup, 'agroup3') fileh.close() # Open this file in read-only mode fileh = open_file( self.file, mode="r", node_cache_slots=self.node_cache_slots) # Ensure that the new name exists group = fileh.root.newgroup.agroup3 self.assertEqual(group._v_name, "agroup3") self.assertEqual(group._v_pathname, "/newgroup/agroup3") self.assertEqual(group._v_depth, 2) # The children of this group must also be accessible through the # new name path group2 = fileh.get_node("/newgroup/agroup3/agroup3") self.assertEqual(group2._v_name, "agroup3") self.assertEqual(group2._v_pathname, "/newgroup/agroup3/agroup3") self.assertEqual(group2._v_depth, 3) # Try to get the previous object with the old name try: fileh.root.agroup except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") # Try to get a child with the old pathname try: fileh.get_node("/agroup/agroup3") except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test11b_moveGroup(self): """Checking moving a Group and access it immediately.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) newgroup = fileh.create_group(fileh.root, 'newgroup') fileh.move_node(fileh.root.agroup, newgroup, 'agroup3') # Ensure that the new name exists group = fileh.root.newgroup.agroup3 self.assertEqual(group._v_name, "agroup3") self.assertEqual(group._v_pathname, "/newgroup/agroup3") self.assertEqual(group._v_depth, 2) # The children of this group must also be accessible through the # new name path group2 = fileh.get_node("/newgroup/agroup3/agroup3") self.assertEqual(group2._v_name, "agroup3") self.assertEqual(group2._v_pathname, "/newgroup/agroup3/agroup3") self.assertEqual(group2._v_depth, 3) # Try to get the previous object with the old name try: fileh.root.agroup except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") # Try to get a child with the old pathname try: fileh.get_node("/agroup/agroup3") except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: self.fail("expected an LookupError") fileh.close() def test11c_moveGroup(self): """Checking moving a Group and modify attributes afterwards.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) newgroup = fileh.create_group(fileh.root, 'newgroup') fileh.move_node(fileh.root.agroup, newgroup, 'agroup3') # Ensure that we can modify attributes in the new group group = fileh.root.newgroup.agroup3 group._v_attrs.TITLE = "Hello" group._v_attrs.hola = "Hello" self.assertEqual(group._v_title, "Hello") self.assertEqual(group._v_attrs.TITLE, "Hello") self.assertEqual(group._v_attrs.hola, "Hello") fileh.close() def test11d_moveToExistingGroup(self): """Checking moving a group to an existing name.""" # Open this file fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # Try to get the previous object with the old name try: fileh.move_node(fileh.root.agroup, fileh.root, 'agroup2') except NodeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NodeError was catched!") print(value) else: self.fail("expected an NodeError") fileh.close() def test11e_moveToExistingGroupOverwrite(self): """Checking moving a group to an existing name, overwriting it.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # agroup2 -> agroup srcNode = fileh.root.agroup2 fileh.move_node(srcNode, fileh.root, 'agroup', overwrite=True) dstNode = fileh.root.agroup self.assertTrue(srcNode is dstNode) fileh.close() def test12a_moveNodeOverItself(self): """Checking moving a node over itself.""" fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # array -> array srcNode = fileh.root.array fileh.move_node(srcNode, fileh.root, 'array') dstNode = fileh.root.array self.assertTrue(srcNode is dstNode) fileh.close() def test12b_moveGroupIntoItself(self): """Checking moving a group into itself.""" # Open this file fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) try: # agroup2 -> agroup2/ fileh.move_node(fileh.root.agroup2, fileh.root.agroup2) except NodeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NodeError was catched!") print(value) else: self.fail("expected an NodeError") fileh.close() def test13a_copyLeaf(self): "Copying a leaf." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # array => agroup2/ new_node = fileh.copy_node(fileh.root.array, fileh.root.agroup2) dstNode = fileh.root.agroup2.array self.assertTrue(new_node is dstNode) fileh.close() def test13b_copyGroup(self): "Copying a group." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # agroup2 => agroup/ new_node = fileh.copy_node(fileh.root.agroup2, fileh.root.agroup) dstNode = fileh.root.agroup.agroup2 self.assertTrue(new_node is dstNode) fileh.close() def test13c_copyGroupSelf(self): "Copying a group into itself." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # agroup2 => agroup2/ new_node = fileh.copy_node(fileh.root.agroup2, fileh.root.agroup2) dstNode = fileh.root.agroup2.agroup2 self.assertTrue(new_node is dstNode) fileh.close() def test13d_copyGroupRecursive(self): "Recursively copying a group." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # agroup => agroup2/ new_node = fileh.copy_node( fileh.root.agroup, fileh.root.agroup2, recursive=True) dstNode = fileh.root.agroup2.agroup self.assertTrue(new_node is dstNode) dstChild1 = dstNode.anarray1 self.assertTrue(dstChild1 is not None) dstChild2 = dstNode.anarray2 self.assertTrue(dstChild2 is not None) dstChild3 = dstNode.agroup3 self.assertTrue(dstChild3 is not None) fileh.close() def test13e_copyRootRecursive(self): "Recursively copying the root group into the root of another file." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) file2 = tempfile.mktemp(".h5") fileh2 = open_file( file2, mode="w", node_cache_slots=self.node_cache_slots) # fileh.root => fileh2.root new_node = fileh.copy_node( fileh.root, fileh2.root, recursive=True) dstNode = fileh2.root self.assertTrue(new_node is dstNode) self.assertTrue("/agroup" in fileh2) self.assertTrue("/agroup/anarray1" in fileh2) self.assertTrue("/agroup/agroup3" in fileh2) fileh.close() fileh2.close() os.remove(file2) def test13f_copyRootRecursive(self): "Recursively copying the root group into a group in another file." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) file2 = tempfile.mktemp(".h5") fileh2 = open_file( file2, mode="w", node_cache_slots=self.node_cache_slots) fileh2.create_group('/', 'agroup2') # fileh.root => fileh2.root.agroup2 new_node = fileh.copy_node( fileh.root, fileh2.root.agroup2, recursive=True) dstNode = fileh2.root.agroup2 self.assertTrue(new_node is dstNode) self.assertTrue("/agroup2/agroup" in fileh2) self.assertTrue("/agroup2/agroup/anarray1" in fileh2) self.assertTrue("/agroup2/agroup/agroup3" in fileh2) fileh.close() fileh2.close() os.remove(file2) def test13g_copyRootItself(self): "Recursively copying the root group into itself." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) agroup2 = fileh.root self.assertTrue(agroup2 is not None) # fileh.root => fileh.root self.assertRaises(IOError, fileh.copy_node, fileh.root, fileh.root, recursive=True) fileh.close() def test14a_copyNodeExisting(self): "Copying over an existing node." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) try: # agroup2 => agroup fileh.copy_node(fileh.root.agroup2, newname='agroup') except NodeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NodeError was catched!") print(value) else: self.fail("expected an NodeError") fileh.close() def test14b_copyNodeExistingOverwrite(self): "Copying over an existing node, overwriting it." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # agroup2 => agroup new_node = fileh.copy_node(fileh.root.agroup2, newname='agroup', overwrite=True) dstNode = fileh.root.agroup self.assertTrue(new_node is dstNode) fileh.close() def test14b2_copyNodeExistingOverwrite(self): "Copying over an existing node in other file, overwriting it." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) file2 = tempfile.mktemp(".h5") fileh2 = open_file( file2, mode="w", node_cache_slots=self.node_cache_slots) # file1:/anarray1 => file2:/anarray1 new_node = fileh.copy_node(fileh.root.agroup.anarray1, newparent=fileh2.root) # file1:/ => file2:/ new_node = fileh.copy_node(fileh.root, fileh2.root, overwrite=True, recursive=True) dstNode = fileh2.root self.assertTrue(new_node is dstNode) fileh.close() fileh2.close() os.remove(file2) def test14c_copyNodeExistingSelf(self): "Copying over self." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) try: # agroup => agroup fileh.copy_node(fileh.root.agroup, newname='agroup') except NodeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NodeError was catched!") print(value) else: self.fail("expected an NodeError") fileh.close() def test14d_copyNodeExistingOverwriteSelf(self): "Copying over self, trying to overwrite." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) try: # agroup => agroup fileh.copy_node( fileh.root.agroup, newname='agroup', overwrite=True) except NodeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NodeError was catched!") print(value) else: self.fail("expected an NodeError") fileh.close() def test14e_copyGroupSelfRecursive(self): "Recursively copying a group into itself." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) try: # agroup => agroup/ fileh.copy_node( fileh.root.agroup, fileh.root.agroup, recursive=True) except NodeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NodeError was catched!") print(value) else: self.fail("expected an NodeError") fileh.close() def test15a_oneStepMove(self): "Moving and renaming a node in a single action." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # anarray1 -> agroup/array srcNode = fileh.root.anarray1 fileh.move_node(srcNode, fileh.root.agroup, 'array') dstNode = fileh.root.agroup.array self.assertTrue(srcNode is dstNode) fileh.close() def test15b_oneStepCopy(self): "Copying and renaming a node in a single action." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # anarray1 => agroup/array new_node = fileh.copy_node( fileh.root.anarray1, fileh.root.agroup, 'array') dstNode = fileh.root.agroup.array self.assertTrue(new_node is dstNode) fileh.close() def test16a_fullCopy(self): "Copying full data and user attributes." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # agroup => groupcopy srcNode = fileh.root.agroup new_node = fileh.copy_node( srcNode, newname='groupcopy', recursive=True) dstNode = fileh.root.groupcopy self.assertTrue(new_node is dstNode) self.assertEqual(srcNode._v_attrs.testattr, dstNode._v_attrs.testattr) self.assertEqual( srcNode.anarray1.attrs.testattr, dstNode.anarray1.attrs.testattr) self.assertEqual(srcNode.anarray1.read(), dstNode.anarray1.read()) fileh.close() def test16b_partialCopy(self): "Copying partial data and no user attributes." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) # agroup => groupcopy srcNode = fileh.root.agroup new_node = fileh.copy_node( srcNode, newname='groupcopy', recursive=True, copyuserattrs=False, start=0, stop=5, step=2) dstNode = fileh.root.groupcopy self.assertTrue(new_node is dstNode) self.assertFalse(hasattr(dstNode._v_attrs, 'testattr')) self.assertFalse(hasattr(dstNode.anarray1.attrs, 'testattr')) self.assertEqual(srcNode.anarray1.read()[ 0:5:2], dstNode.anarray1.read()) fileh.close() def test16c_fullCopy(self): "Copying full data and user attributes (from file to file)." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) file2 = tempfile.mktemp(".h5") fileh2 = open_file( file2, mode="w", node_cache_slots=self.node_cache_slots) # file1:/ => file2:groupcopy srcNode = fileh.root new_node = fileh.copy_node( srcNode, fileh2.root, newname='groupcopy', recursive=True) dstNode = fileh2.root.groupcopy self.assertTrue(new_node is dstNode) self.assertEqual(srcNode._v_attrs.testattr, dstNode._v_attrs.testattr) self.assertEqual( srcNode.agroup.anarray1.attrs.testattr, dstNode.agroup.anarray1.attrs.testattr) self.assertEqual(srcNode.agroup.anarray1.read(), dstNode.agroup.anarray1.read()) fileh.close() fileh2.close() os.remove(file2) def test17a_CopyChunkshape(self): "Copying dataset with a chunkshape." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) srcTable = fileh.root.table newTable = fileh.copy_node( srcTable, newname='tablecopy', chunkshape=11) self.assertEqual(newTable.chunkshape, (11,)) self.assertNotEqual(srcTable.chunkshape, newTable.chunkshape) fileh.close() def test17b_CopyChunkshape(self): "Copying dataset with a chunkshape with 'keep' value." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) srcTable = fileh.root.table newTable = fileh.copy_node( srcTable, newname='tablecopy', chunkshape='keep') self.assertEqual(srcTable.chunkshape, newTable.chunkshape) fileh.close() def test17c_CopyChunkshape(self): "Copying dataset with a chunkshape with 'auto' value." fileh = open_file( self.file, mode="r+", node_cache_slots=self.node_cache_slots) srcTable = fileh.root.table newTable = fileh.copy_node( srcTable, newname='tablecopy', chunkshape=11) newTable2 = fileh.copy_node( newTable, newname='tablecopy2', chunkshape='auto') self.assertEqual(srcTable.chunkshape, newTable2.chunkshape) fileh.close() def test18_closedRepr(self): "Representing a closed node as a string." fileh = open_file( self.file, node_cache_slots=self.node_cache_slots) for node in [fileh.root.agroup, fileh.root.anarray]: node._f_close() self.assertTrue('closed' in str(node)) self.assertTrue('closed' in repr(node)) fileh.close() def test19_fileno(self): """Checking that the 'fileno()' method works.""" # Open the old HDF5 file fileh = open_file( self.file, mode="r", node_cache_slots=self.node_cache_slots) # Get the file descriptor for this file fd = fileh.fileno() if common.verbose: print("Value of fileno():", fd) self.assertTrue(fd >= 0) fileh.close() class NodeCacheOpenFile(OpenFileTestCase): node_cache_slots = NODE_CACHE_SLOTS class NoNodeCacheOpenFile(OpenFileTestCase): node_cache_slots = 0 class DictNodeCacheOpenFile(OpenFileTestCase): node_cache_slots = -NODE_CACHE_SLOTS class CheckFileTestCase(common.PyTablesTestCase): def test00_isHDF5File(self): """Checking is_hdf5_file function (TRUE case)""" # Create a PyTables file (and by so, an HDF5 file) filename = tempfile.mktemp(".h5") fileh = open_file(filename, mode="w") fileh.create_array(fileh.root, 'array', [ 1, 2], title="Title example") # For this method to run, it needs a closed file fileh.close() # When file has an HDF5 format, always returns 1 if common.verbose: print("\nisHDF5File(%s) ==> %d" % (filename, is_hdf5_file(filename))) self.assertEqual(is_hdf5_file(filename), 1) # Then, delete the file os.remove(filename) def test01_isHDF5File(self): """Checking is_hdf5_file function (FALSE case)""" # Create a regular (text) file file = tempfile.mktemp(".h5") fileh = open(file, "w") fileh.write("Hello!") fileh.close() version = is_hdf5_file(file) # When file is not an HDF5 format, always returns 0 or # negative value self.assertTrue(version <= 0) # Then, delete the file os.remove(file) def test01x_isHDF5File_nonexistent(self): """Identifying a nonexistent HDF5 file.""" self.assertRaises(IOError, is_hdf5_file, 'nonexistent') def test01x_isHDF5File_unreadable(self): """Identifying an unreadable HDF5 file.""" if hasattr(os, 'getuid') and os.getuid() != 0: h5fname = tempfile.mktemp(suffix='.h5') open_file(h5fname, 'w').close() try: os.chmod(h5fname, 0) # no permissions at all self.assertRaises(IOError, is_hdf5_file, h5fname) finally: os.remove(h5fname) def test02_isPyTablesFile(self): """Checking is_pytables_file function (TRUE case)""" # Create a PyTables file file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") fileh.create_array(fileh.root, 'array', [ 1, 2], title="Title example") # For this method to run, it needs a closed file fileh.close() version = is_pytables_file(file) # When file has a PyTables format, always returns "1.0" string or # greater if common.verbose: print() print("\nPyTables format version number ==> %s" % version) self.assertTrue(version >= "1.0") # Then, delete the file os.remove(file) def test03_isPyTablesFile(self): """Checking is_pytables_file function (FALSE case)""" # Create a regular (text) file file = tempfile.mktemp(".h5") fileh = open(file, "w") fileh.write("Hello!") fileh.close() version = is_pytables_file(file) # When file is not a PyTables format, always returns 0 or # negative value if common.verbose: print() print("\nPyTables format version number ==> %s" % version) self.assertTrue(version is None) # Then, delete the file os.remove(file) def test04_openGenericHDF5File(self): """Checking opening of a generic HDF5 file.""" # Open an existing generic HDF5 file fileh = open_file(self._testFilename("ex-noattr.h5"), mode="r") # Check for some objects inside # A group columns = fileh.get_node("/columns", classname="Group") self.assertEqual(columns._v_name, "columns") # An Array array_ = fileh.get_node(columns, "TDC", classname="Array") self.assertEqual(array_._v_name, "TDC") # (The new LRU code defers the appearance of a warning to this point). # Here comes an Array of H5T_ARRAY type ui = fileh.get_node(columns, "pressure", classname="Array") self.assertEqual(ui._v_name, "pressure") if common.verbose: print("Array object with type H5T_ARRAY -->", repr(ui)) print("Array contents -->", ui[:]) # A Table table = fileh.get_node("/detector", "table", classname="Table") self.assertEqual(table._v_name, "table") fileh.close() def test04b_UnImplementedOnLoading(self): """Checking failure loading resulting in an ``UnImplemented`` node.""" ############### Note for developers ############################### # This test fails if you have the line: # # ##return ChildClass(self, childname) # uncomment for debugging # # uncommented in Group.py! # ################################################################### h5file = open_file(self._testFilename('smpl_unsupptype.h5')) try: node = self.assertWarns( UserWarning, h5file.get_node, '/CompoundChunked') self.assertTrue(isinstance(node, UnImplemented)) finally: h5file.close() def test04c_UnImplementedScalar(self): """Checking opening of HDF5 files containing scalar dataset of UnImlemented type.""" h5file = open_file(self._testFilename("scalar.h5")) try: node = self.assertWarns( UserWarning, h5file.get_node, '/variable length string') self.assertTrue(isinstance(node, UnImplemented)) finally: h5file.close() def test05_copyUnimplemented(self): """Checking that an UnImplemented object cannot be copied.""" # Open an existing generic HDF5 file fileh = open_file(self._testFilename("smpl_unsupptype.h5"), mode="r") ui = self.assertWarns( UserWarning, fileh.get_node, '/CompoundChunked') self.assertEqual(ui._v_name, 'CompoundChunked') if common.verbose: print("UnImplement object -->", repr(ui)) # Check that it cannot be copied to another file file2 = tempfile.mktemp(".h5") fileh2 = open_file(file2, mode="w") # Force the userwarning to issue an error warnings.filterwarnings("error", category=UserWarning) try: ui.copy(fileh2.root, "newui") except UserWarning: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next UserWarning was catched:") print(value) else: self.fail("expected an UserWarning") # Reset the warnings # Be careful with that, because this enables all the warnings # on the rest of the tests! # warnings.resetwarnings() # better use: warnings.filterwarnings("default", category=UserWarning) # Delete the new (empty) file fileh2.close() os.remove(file2) fileh.close() # The next can be used to check the copy of Array objects with H5T_ARRAY # in the future def _test05_copyUnimplemented(self): """Checking that an UnImplemented object cannot be copied.""" # Open an existing generic HDF5 file # We don't need to wrap this in a try clause because # it has already been tried and the warning will not happen again fileh = open_file(self._testFilename("ex-noattr.h5"), mode="r") # An unsupported object (the deprecated H5T_ARRAY type in # Array, from pytables 0.8 on) ui = fileh.get_node(fileh.root.columns, "pressure") self.assertEqual(ui._v_name, "pressure") if common.verbose: print("UnImplement object -->", repr(ui)) # Check that it cannot be copied to another file file2 = tempfile.mktemp(".h5") fileh2 = open_file(file2, mode="w") # Force the userwarning to issue an error warnings.filterwarnings("error", category=UserWarning) try: ui.copy(fileh2.root, "newui") except UserWarning: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next UserWarning was catched:") print(value) else: self.fail("expected an UserWarning") # Reset the warnings # Be careful with that, because this enables all the warnings # on the rest of the tests! # warnings.resetwarnings() # better use: warnings.filterwarnings("default", category=UserWarning) # Delete the new (empty) file fileh2.close() os.remove(file2) fileh.close() class ThreadingTestCase(common.TempFileMixin, common.PyTablesTestCase): def setUp(self): super(ThreadingTestCase, self).setUp() self.h5file.create_carray('/', 'test_array', tables.Int64Atom(), (200, 300)) self.h5file.close() def test(self): filename = self.h5fname def run(filename, q): try: f = tables.open_file(filename, mode='r') arr = f.root.test_array[8:12, 18:22] assert arr.max() == arr.min() == 0 f.close() except Exception as e: q.put(sys.exc_info()) else: q.put('OK') threads = [] q = Queue.Queue() for i in xrange(10): t = threading.Thread(target=run, args=(filename, q)) t.start() threads.append(t) for i in xrange(10): self.assertEqual(q.get(), 'OK') for t in threads: t.join() class PythonAttrsTestCase(common.TempFileMixin, common.PyTablesTestCase): """Test interactions of Python attributes and child nodes.""" def test00_attrOverChild(self): """Setting a Python attribute over a child node.""" root = self.h5file.root # Create ``/test`` and overshadow it with ``root.test``. child = self.h5file.create_array(root, 'test', [1]) attr = 'foobar' self.assertWarns(NaturalNameWarning, setattr, root, 'test', attr) self.assertTrue(root.test is attr) self.assertTrue(root._f_get_child('test') is child) # Now bring ``/test`` again to light. del root.test self.assertTrue(root.test is child) # Now there is no *attribute* named ``test``. self.assertRaises(AttributeError, delattr, root, 'test') def test01_childUnderAttr(self): """Creating a child node under a Python attribute.""" h5file = self.h5file root = h5file.root # Create ``root.test`` and an overshadowed ``/test``. attr = 'foobar' root.test = attr self.assertWarns(NaturalNameWarning, h5file.create_array, root, 'test', [1]) child = h5file.get_node('/test') self.assertTrue(root.test is attr) self.assertTrue(root._f_get_child('test') is child) # Now bring ``/test`` again to light. del root.test self.assertTrue(root.test is child) # Now there is no *attribute* named ``test``. self.assertRaises(AttributeError, delattr, root, 'test') def test02_nodeAttrInLeaf(self): """Assigning a ``Node`` value as an attribute to a ``Leaf``.""" h5file = self.h5file array1 = h5file.create_array('/', 'array1', [1]) array2 = h5file.create_array('/', 'array2', [1]) # This may make the garbage collector work a little. array1.array2 = array2 array2.array1 = array1 # Check the assignments. self.assertTrue(array1.array2 is array2) self.assertTrue(array2.array1 is array1) self.assertRaises(NoSuchNodeError, # ``/array1`` is not a group h5file.get_node, '/array1/array2') self.assertRaises(NoSuchNodeError, # ``/array2`` is not a group h5file.get_node, '/array2/array3') def test03_nodeAttrInGroup(self): """Assigning a ``Node`` value as an attribute to a ``Group``.""" h5file = self.h5file root = h5file.root array = h5file.create_array('/', 'array', [1]) # Assign the array to a pair of attributes, # one of them overshadowing the original. root.arrayAlias = array self.assertWarns(NaturalNameWarning, setattr, root, 'array', array) # Check the assignments. self.assertTrue(root.arrayAlias is array) self.assertTrue(root.array is array) self.assertRaises(NoSuchNodeError, h5file.get_node, '/arrayAlias') self.assertTrue(h5file.get_node('/array') is array) # Remove the attribute overshadowing the child. del root.array # Now there is no *attribute* named ``array``. self.assertRaises(AttributeError, delattr, root, 'array') class StateTestCase(common.TempFileMixin, common.PyTablesTestCase): """Test that ``File`` and ``Node`` operations check their state (open or closed, readable or writable) before proceeding.""" def test00_fileCopyFileClosed(self): """Test copying a closed file.""" h5cfname = tempfile.mktemp(suffix='.h5') self.h5file.close() try: self.assertRaises(ClosedFileError, self.h5file.copy_file, h5cfname) finally: if os.path.exists(h5cfname): os.remove(h5fcname) self.fail("a (maybe incomplete) copy " "of a closed file was created") def test01_fileCloseClosed(self): """Test closing an already closed file.""" self.h5file.close() try: self.h5file.close() except ClosedFileError: self.fail("could not close an already closed file") def test02_fileFlushClosed(self): """Test flushing a closed file.""" self.h5file.close() self.assertRaises(ClosedFileError, self.h5file.flush) def test03_fileFlushRO(self): """Flushing a read-only file.""" self._reopen('r') try: self.h5file.flush() except FileModeError: self.fail("could not flush a read-only file") def test04_fileCreateNodeClosed(self): """Test creating a node in a closed file.""" self.h5file.close() self.assertRaises(ClosedFileError, self.h5file.create_group, '/', 'test') def test05_fileCreateNodeRO(self): """Test creating a node in a read-only file.""" self._reopen('r') self.assertRaises(FileModeError, self.h5file.create_group, '/', 'test') def test06_fileRemoveNodeClosed(self): """Test removing a node from a closed file.""" self.h5file.create_group('/', 'test') self.h5file.close() self.assertRaises(ClosedFileError, self.h5file.remove_node, '/', 'test') def test07_fileRemoveNodeRO(self): """Test removing a node from a read-only file.""" self.h5file.create_group('/', 'test') self._reopen('r') self.assertRaises(FileModeError, self.h5file.remove_node, '/', 'test') def test08_fileMoveNodeClosed(self): """Test moving a node in a closed file.""" self.h5file.create_group('/', 'test1') self.h5file.create_group('/', 'test2') self.h5file.close() self.assertRaises(ClosedFileError, self.h5file.move_node, '/test1', '/', 'test2') def test09_fileMoveNodeRO(self): """Test moving a node in a read-only file.""" self.h5file.create_group('/', 'test1') self.h5file.create_group('/', 'test2') self._reopen('r') self.assertRaises(FileModeError, self.h5file.move_node, '/test1', '/', 'test2') def test10_fileCopyNodeClosed(self): """Test copying a node in a closed file.""" self.h5file.create_group('/', 'test1') self.h5file.create_group('/', 'test2') self.h5file.close() self.assertRaises(ClosedFileError, self.h5file.copy_node, '/test1', '/', 'test2') def test11_fileCopyNodeRO(self): """Test copying a node in a read-only file.""" self.h5file.create_group('/', 'test1') self._reopen('r') self.assertRaises(FileModeError, self.h5file.copy_node, '/test1', '/', 'test2') def test13_fileGetNodeClosed(self): """Test getting a node from a closed file.""" self.h5file.create_group('/', 'test') self.h5file.close() self.assertRaises(ClosedFileError, self.h5file.get_node, '/test') def test14_fileWalkNodesClosed(self): """Test walking a closed file.""" self.h5file.create_group('/', 'test1') self.h5file.create_group('/', 'test2') self.h5file.close() self.assertRaises(ClosedFileError, self.h5file.walk_nodes().next) def test15_fileAttrClosed(self): """Test setting and deleting a node attribute in a closed file.""" self.h5file.create_group('/', 'test') self.h5file.close() self.assertRaises(ClosedFileError, self.h5file.set_node_attr, '/test', 'foo', 'bar') self.assertRaises(ClosedFileError, self.h5file.del_node_attr, '/test', 'foo') def test16_fileAttrRO(self): """Test setting and deleting a node attribute in a read-only file.""" self.h5file.create_group('/', 'test') self.h5file.set_node_attr('/test', 'foo', 'foo') self._reopen('r') self.assertRaises(FileModeError, self.h5file.set_node_attr, '/test', 'foo', 'bar') self.assertRaises(FileModeError, self.h5file.del_node_attr, '/test', 'foo') def test17_fileUndoClosed(self): """Test undo operations in a closed file.""" self.h5file.enable_undo() self.h5file.create_group('/', 'test2') self.h5file.close() self.assertRaises(ClosedFileError, self.h5file.is_undo_enabled) self.assertRaises(ClosedFileError, self.h5file.get_current_mark) self.assertRaises(ClosedFileError, self.h5file.undo) self.assertRaises(ClosedFileError, self.h5file.disable_undo) def test18_fileUndoRO(self): """Test undo operations in a read-only file.""" self.h5file.enable_undo() self.h5file.create_group('/', 'test') self._reopen('r') self.assertEqual(self.h5file._undoEnabled, False) # self.assertRaises(FileModeError, self.h5file.undo) # self.assertRaises(FileModeError, self.h5file.disable_undo) def test19a_getNode(self): """Test getting a child of a closed node.""" g1 = self.h5file.create_group('/', 'g1') g2 = self.h5file.create_group('/g1', 'g2') # Close this *object* so that it should not be used. g1._f_close() self.assertRaises(ClosedNodeError, g1._f_get_child, 'g2') # Getting a node by its closed object is not allowed. self.assertRaises(ClosedNodeError, self.h5file.get_node, g1) # Going through that *node* should reopen it automatically. try: g2_ = self.h5file.get_node('/g1/g2') except ClosedNodeError: self.fail("closed parent group has not been reopened") # Already open nodes should be closed now, but not the new ones. self.assertTrue(g2._v_isopen is False, "open child of closed group has not been closed") self.assertTrue(g2_._v_isopen is True, "open child of closed group has not been closed") # And existing closed ones should remain closed, but not the new ones. g1_ = self.h5file.get_node('/g1') self.assertTrue(g1._v_isopen is False, "already closed group is not closed anymore") self.assertTrue(g1_._v_isopen is True, "newly opened group is still closed") def test19b_getNode(self): """Test getting a node that does not start with a slash ('/').""" # Create an array in the root self.h5file.create_array('/', 'array', [1, 2], title="Title example") # Get the array without specifying a leading slash self.assertRaises(NameError, self.h5file.get_node, "array") def test20_removeNode(self): """Test removing a closed node.""" # This test is a little redundant once we know that ``File.get_node()`` # will reload a closed node, but anyway... group = self.h5file.create_group('/', 'group') array = self.h5file.create_array('/group', 'array', [1]) # The closed *object* can not be used. group._f_close() self.assertRaises(ClosedNodeError, group._f_remove) self.assertRaises(ClosedNodeError, self.h5file.remove_node, group) # Still, the *node* is reloaded when necessary. try: self.h5file.remove_node('/group', recursive=True) except ClosedNodeError: self.fail("closed node has not been reloaded") # Objects of descendent removed nodes # should have been automatically closed when removed. self.assertRaises(ClosedNodeError, array._f_remove) self.assertTrue('/group/array' not in self.h5file) # just in case self.assertTrue('/group' not in self.h5file) # just in case def test21_attrsOfNode(self): """Test manipulating the attributes of a closed node.""" node = self.h5file.create_group('/', 'test') nodeAttrs = node._v_attrs nodeAttrs.test = attr = 'foo' node._f_close() self.assertRaises(ClosedNodeError, getattr, node, '_v_attrs') # The design of ``AttributeSet`` does not yet allow this test. ## self.assertRaises(ClosedNodeError, getattr, nodeAttrs, 'test') self.assertEqual(self.h5file.get_node_attr('/test', 'test'), attr) def test21b_attrsOfNode(self): """Test manipulating the attributes of a node in a read-only file.""" self.h5file.create_group('/', 'test') self.h5file.set_node_attr('/test', 'test', 'foo') self._reopen('r') self.assertRaises(FileModeError, self.h5file.set_node_attr, '/test', 'test', 'bar') def test22_fileClosesNode(self): """Test node closing because of file closing.""" node = self.h5file.create_group('/', 'test') self.h5file.close() self.assertRaises(ClosedNodeError, getattr, node, '_v_attrs') def test23_reopenFile(self): """Testing reopening a file and closing it several times.""" self.h5file.create_array('/', 'test', [1, 2, 3]) self.h5file.close() file1 = open_file(self.h5fname, "r") self.assertEqual(file1.open_count, 1) if tables.file._FILE_OPEN_POLICY == 'strict': self.assertRaises(ValueError, tables.open_file, self.h5fname, "r") file1.close() else: file2 = open_file(self.h5fname, "r") self.assertEqual(file1.open_count, 1) self.assertEqual(file2.open_count, 1) if common.verbose: print("(file1) open_count:", file1.open_count) print("(file1) test[1]:", file1.root.test[1]) self.assertEqual(file1.root.test[1], 2) file1.close() self.assertEqual(file2.open_count, 1) if common.verbose: print("(file2) open_count:", file2.open_count) print("(file2) test[1]:", file2.root.test[1]) self.assertEqual(file2.root.test[1], 2) file2.close() class FlavorTestCase(common.TempFileMixin, common.PyTablesTestCase): """Test that setting, getting and changing the ``flavor`` attribute of a leaf works as expected.""" array_data = numpy.arange(10) scalar_data = numpy.int32(10) def _reopen(self, mode='r'): super(FlavorTestCase, self)._reopen(mode) self.array = self.h5file.get_node('/array') self.scalar = self.h5file.get_node('/scalar') return True def setUp(self): super(FlavorTestCase, self).setUp() self.array = self.h5file.create_array('/', 'array', self.array_data) self.scalar = self.h5file.create_array('/', 'scalar', self.scalar_data) def tearDown(self): self.array = None super(FlavorTestCase, self).tearDown() def test00_invalid(self): """Setting an invalid flavor.""" self.assertRaises(FlavorError, setattr, self.array, 'flavor', 'foo') def test01_readonly(self): """Setting a flavor in a read-only file.""" self._reopen(mode='r') self.assertRaises(FileModeError, setattr, self.array, 'flavor', tables.flavor.internal_flavor) def test02_change(self): """Changing the flavor and reading data.""" for flavor in all_flavors: self.array.flavor = flavor self.assertEqual(self.array.flavor, flavor) idata = array_of_flavor(self.array_data, flavor) odata = self.array[:] self.assertTrue(common.allequal(odata, idata, flavor)) def test03_store(self): """Storing a changed flavor.""" for flavor in all_flavors: self.array.flavor = flavor self.assertEqual(self.array.flavor, flavor) self._reopen(mode='r+') self.assertEqual(self.array.flavor, flavor) def test04_missing(self): """Reading a dataset of a missing flavor.""" flavor = self.array.flavor # default is internal self.array._v_attrs.FLAVOR = 'foobar' # breaks flavor self._reopen(mode='r') idata = array_of_flavor(self.array_data, flavor) odata = self.assertWarns(FlavorWarning, self.array.read) self.assertTrue(common.allequal(odata, idata, flavor)) def test05_delete(self): """Deleting the flavor of a dataset.""" self.array.flavor = 'python' # non-default self.assertEqual(self.array.flavor, 'python') self.assertEqual(self.array.attrs.FLAVOR, 'python') del self.array.flavor self.assertEqual(self.array.flavor, tables.flavor.internal_flavor) self.assertRaises(AttributeError, getattr, self.array.attrs, 'FLAVOR') def test06_copyDeleted(self): """Copying a node with a deleted flavor (see #100).""" snames = [node._v_name for node in [self.array, self.scalar]] dnames = ['%s_copy' % name for name in snames] for name in snames: node = self.h5file.get_node('/', name) del node.flavor # Check the copied flavors right after copying and after reopening. for fmode in ['r+', 'r']: self._reopen(fmode) for sname, dname in zip(snames, dnames): if fmode == 'r+': snode = self.h5file.get_node('/', sname) node = snode.copy('/', dname) elif fmode == 'r': node = self.h5file.get_node('/', dname) self.assertEqual(node.flavor, tables.flavor.internal_flavor, "flavor of node ``%s`` is not internal: %r" % (node._v_pathname, node.flavor)) def test07_restrict_flavors(self): # regression test for gh-163 all_flavors = list(tables.flavor.all_flavors) alias_map = tables.flavor.alias_map.copy() converter_map = tables.flavor.converter_map.copy() identifier_map = tables.flavor.identifier_map.copy() description_map = tables.flavor.description_map.copy() try: tables.flavor.restrict_flavors(keep=[]) self.assertTrue(len(tables.flavor.alias_map) < len(alias_map)) self.assertTrue(len( tables.flavor.converter_map) < len(converter_map)) finally: tables.flavor.all_flavors[:] = all_flavors[:] tables.flavor.alias_map.update(alias_map) tables.flavor.converter_map.update(converter_map) tables.flavor.identifier_map.update(identifier_map) tables.flavor.description_map.update(description_map) class UnicodeFilename(common.PyTablesTestCase): unicode_prefix = u'para\u0140lel' def setUp(self): self.h5fname = tempfile.mktemp(prefix=self.unicode_prefix, suffix=".h5") self.h5file = tables.open_file(self.h5fname, "w") self.test = self.h5file.create_array('/', 'test', [1, 2]) # So as to check the reading self.h5file.close() self.h5file = tables.open_file(self.h5fname, "r") def tearDown(self): # Remove the temporary file os.remove(self.h5fname) def test01(self): """Checking creating a filename with Unicode chars.""" test = self.h5file.root.test if common.verbose: print("Filename:", self.h5fname) print("Array:", test[:]) print("Should look like:", [1, 2]) self.assertEqual(test[:], [1, 2], "Values does not match.") def test02(self): """Checking is_hdf5_file with a Unicode filename.""" self.h5file.close() if common.verbose: print("Filename:", self.h5fname) print("is_hdf5_file?:", tables.is_hdf5_file(self.h5fname)) self.assertTrue(tables.is_hdf5_file(self.h5fname)) def test03(self): """Checking is_pytables_file with a Unicode filename.""" self.h5file.close() if common.verbose: print("Filename:", self.h5fname) print("is_pytables_file?:", tables.is_pytables_file(self.h5fname)) self.assertNotEqual(tables.is_pytables_file(self.h5fname), False) class FilePropertyTestCase(common.PyTablesTestCase): def setUp(self): self.h5fname = tempfile.mktemp(".h5") self.h5file = None def tearDown(self): if self.h5file: self.h5file.close() if os.path.exists(self.h5fname): os.remove(self.h5fname) def test_get_filesize(self): data = numpy.zeros((2000, 2000)) datasize = numpy.prod(data.shape) * data.dtype.itemsize self.h5file = open_file(self.h5fname, mode="w") self.h5file.create_array(self.h5file.root, 'array', data) h5_filesize = self.h5file.get_filesize() self.h5file.close() fs_filesize = os.stat(self.h5fname)[6] self.assertTrue(h5_filesize >= datasize) self.assertEqual(h5_filesize, fs_filesize) def test01_null_userblock_size(self): self.h5file = open_file(self.h5fname, mode="w") self.h5file.create_array(self.h5file.root, 'array', [1, 2]) self.assertEqual(self.h5file.get_userblock_size(), 0) def test02_null_userblock_size(self): self.h5file = open_file(self.h5fname, mode="w") self.h5file.create_array(self.h5file.root, 'array', [1, 2]) self.h5file.close() self.h5file = open_file(self.h5fname, mode="r") self.assertEqual(self.h5file.get_userblock_size(), 0) def test03_null_userblock_size(self): USER_BLOCK_SIZE = 0 self.h5file = open_file(self.h5fname, mode="w", user_block_size=USER_BLOCK_SIZE) self.h5file.create_array(self.h5file.root, 'array', [1, 2]) self.assertEqual(self.h5file.get_userblock_size(), 0) def test01_userblock_size(self): USER_BLOCK_SIZE = 512 self.h5file = open_file(self.h5fname, mode="w", user_block_size=USER_BLOCK_SIZE) self.h5file.create_array(self.h5file.root, 'array', [1, 2]) self.assertEqual(self.h5file.get_userblock_size(), USER_BLOCK_SIZE) def test02_userblock_size(self): USER_BLOCK_SIZE = 512 self.h5file = open_file(self.h5fname, mode="w", user_block_size=USER_BLOCK_SIZE) self.h5file.create_array(self.h5file.root, 'array', [1, 2]) self.h5file.close() self.h5file = open_file(self.h5fname, mode="r") self.assertEqual(self.h5file.get_userblock_size(), USER_BLOCK_SIZE) def test_small_userblock_size(self): USER_BLOCK_SIZE = 12 self.assertRaises(ValueError, open_file, self.h5fname, mode="w", user_block_size=USER_BLOCK_SIZE) def test_invalid_userblock_size(self): USER_BLOCK_SIZE = 1025 self.assertRaises(ValueError, open_file, self.h5fname, mode="w", user_block_size=USER_BLOCK_SIZE) # Test for reading a file that uses Blosc and created on a big-endian platform class BloscBigEndian(common.PyTablesTestCase): def setUp(self): filename = self._testFilename("blosc_bigendian.h5") self.fileh = open_file(filename, "r") def tearDown(self): self.fileh.close() def test00_bigendian(self): """Checking compatibility with Blosc on big-endian machines.""" # Check that we can read the contents without problems (nor warnings!) for dset_name in ('i1', 'i2', 'i4', 'i8'): a = numpy.arange(10, dtype=dset_name) dset = self.fileh.get_node('/'+dset_name) self.assertTrue(common.allequal(a, dset[:]), "Error in big-endian data!") # Case test for Blosc and subprocesses (via multiprocessing module) # The worker function for the subprocess (needs to be here because Windows # has problems pickling nested functions with the multiprocess module :-/) def _worker(fn, qout=None): fp = tables.open_file(fn) if common.verbose: print("About to load: ", fn) rows = fp.root.table.where('(f0 < 10)') if common.verbose: print("Got the iterator, about to iterate") next(rows) if common.verbose: print("Succeeded in one iteration\n") fp.close() if qout is not None: qout.put("Done") class BloscSubprocess(common.PyTablesTestCase): def test_multiprocess(self): # From: Yaroslav Halchenko # Subject: Skip the unittest on kFreeBSD and Hurd -- locking seems to # be N/A # # on kfreebsd /dev/shm is N/A # on Hurd -- inter-process semaphore locking is N/A import platform if platform.system().lower() in ('gnu', 'gnu/kfreebsd'): raise common.SkipTest("multiprocessing module is not supported " "on Hurd/kFreeBSD") # Create a relatively large table with Blosc level 9 (large blocks) fn = tempfile.mktemp(prefix="multiproc-blosc9-", suffix=".h5") size = int(3e5) sa = numpy.fromiter(((i, i**2, i//3) for i in xrange(size)), 'i4,i8,f8') fp = open_file(fn, 'w') fp.create_table(fp.root, 'table', sa, filters=Filters(complevel=9, complib="blosc"), chunkshape=(size // 3,)) fp.close() if common.verbose: print("**** Running from main process:") _worker(fn) if common.verbose: print("**** Running from subprocess:") try: qout = mp.Queue() except OSError: print("Permission denied due to /dev/shm settings") else: ps = mp.Process(target=_worker, args=(fn, qout,)) ps.daemon = True ps.start() result = qout.get() if common.verbose: print(result) os.remove(fn) class HDF5ErrorHandling(common.PyTablesTestCase): def setUp(self): self._old_policy = tables.HDF5ExtError.DEFAULT_H5_BACKTRACE_POLICY def tearDown(self): tables.HDF5ExtError.DEFAULT_H5_BACKTRACE_POLICY = self._old_policy def test_silence_messages(self): code = """ import tables tables.silence_hdf5_messages(False) tables.silence_hdf5_messages() try: tables.open_file(r'%s') except tables.HDF5ExtError, e: pass """ fn = tempfile.mktemp(prefix="hdf5-error-handling-", suffix=".py") fp = open(fn, 'w') try: fp.write(code % fn) fp.close() p = subprocess.Popen([sys.executable, fn], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = p.communicate() self.assertFalse("HDF5-DIAG" in stderr.decode('ascii')) finally: os.remove(fn) def test_enable_messages(self): code = """ import tables tables.silence_hdf5_messages() tables.silence_hdf5_messages(False) try: tables.open_file(r'%s') except tables.HDF5ExtError as e: pass """ fn = tempfile.mktemp(prefix="hdf5-error-handling-", suffix=".py") fp = open(fn, 'w') try: fp.write(code % fn) fp.close() p = subprocess.Popen([sys.executable, fn], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = p.communicate() self.assertTrue("HDF5-DIAG" in stderr.decode('ascii')) finally: os.remove(fn) def _raise_exterror(self): filename = tempfile.mktemp(".h5") open(filename, 'wb').close() try: f = tables.open_file(filename) f.close() finally: os.remove(filename) def test_h5_backtrace_quiet(self): tables.HDF5ExtError.DEFAULT_H5_BACKTRACE_POLICY = True try: self._raise_exterror() except tables.HDF5ExtError as e: self.assertFalse(e.h5backtrace is None) else: self.fail("HDF5ExtError exception not raised") def test_h5_backtrace_verbose(self): tables.HDF5ExtError.DEFAULT_H5_BACKTRACE_POLICY = "VERBOSE" try: self._raise_exterror() except tables.HDF5ExtError as e: self.assertFalse(e.h5backtrace is None) msg = str(e) self.assertTrue(e.h5backtrace[-1][-1] in msg) else: self.fail("HDF5ExtError exception not raised") def test_h5_backtrace_ignore(self): tables.HDF5ExtError.DEFAULT_H5_BACKTRACE_POLICY = False try: self._raise_exterror() except tables.HDF5ExtError as e: self.assertTrue(e.h5backtrace is None) else: self.fail("HDF5ExtError exception not raised") class TestDescription(common.PyTablesTestCase): def test_isdescription_inheritance(self): # Regression test for gh-65 class TestDescParent(IsDescription): c = Int32Col() class TestDesc(TestDescParent): pass self.assertTrue('c' in TestDesc.columns) def test_descr_from_dtype(self): t = numpy.dtype([('col1', 'int16'), ('col2', float)]) descr, byteorder = descr_from_dtype(t) self.assertTrue('col1' in descr._v_colobjects) self.assertTrue('col2' in descr._v_colobjects) self.assertEqual(len(descr._v_colobjects), 2) self.assertTrue(isinstance(descr._v_colobjects['col1'], Col)) self.assertTrue(isinstance(descr._v_colobjects['col2'], Col)) self.assertEqual(descr._v_colobjects['col1'].dtype, numpy.int16) self.assertEqual(descr._v_colobjects['col2'].dtype, float) def test_descr_from_dtype_rich_dtype(self): header = [(('timestamp', 't'), 'u4'), (('unit (cluster) id', 'unit'), 'u2')] t = numpy.dtype(header) descr, byteorder = descr_from_dtype(t) self.assertEqual(len(descr._v_names), 2) self.assertEqual(sorted(descr._v_names), ['t', 'unit']) def test_dtype_from_descr_is_description(self): # See gh-152 class TestDescParent(IsDescription): col1 = Int16Col() col2 = FloatCol() dtype = numpy.dtype([('col1', 'int16'), ('col2', float)]) t = dtype_from_descr(TestDescParent) self.assertEqual(t, dtype) def test_dtype_from_descr_is_description_instance(self): # See gh-152 class TestDescParent(IsDescription): col1 = Int16Col() col2 = FloatCol() dtype = numpy.dtype([('col1', 'int16'), ('col2', float)]) t = dtype_from_descr(TestDescParent()) self.assertEqual(t, dtype) def test_dtype_from_descr_description_instance(self): # See gh-152 class TestDescParent(IsDescription): col1 = Int16Col() col2 = FloatCol() dtype = numpy.dtype([('col1', 'int16'), ('col2', float)]) desctiption = Description(TestDescParent().columns) t = dtype_from_descr(desctiption) self.assertEqual(t, dtype) def test_dtype_from_descr_dict(self): # See gh-152 dtype = numpy.dtype([('col1', 'int16'), ('col2', float)]) t = dtype_from_descr({'col1': Int16Col(), 'col2': FloatCol()}) self.assertEqual(t, dtype) def test_dtype_from_descr_invalid_type(self): # See gh-152 self.assertRaises(ValueError, dtype_from_descr, []) def test_dtype_from_descr_byteorder(self): # See gh-152 class TestDescParent(IsDescription): col1 = Int16Col() col2 = FloatCol() t = dtype_from_descr(TestDescParent, byteorder='>') self.assertEqual(t['col1'].byteorder, '>') self.assertEqual(t['col2'].byteorder, '>') def test_str_names(self): # see gh-42 d = {'name': tables.Int16Col()} descr = Description(d) self.assertEqual(sorted(descr._v_names), sorted(d.keys())) self.assertTrue(isinstance(descr._v_dtype, numpy.dtype)) self.assertTrue(sorted(descr._v_dtype.fields.keys()), sorted(d.keys())) if sys.version_info[0] < 3: def test_unicode_names(self): # see gh-42 # the name used is a valid ASCII identifier passed as unicode # string d = {unicode('name'): tables.Int16Col()} descr = Description(d) self.assertEqual(sorted(descr._v_names), sorted(d.keys())) self.assertTrue(isinstance(descr._v_dtype, numpy.dtype)) keys = [] for key in d.keys(): if isinstance(key, unicode): keys.append(key.encode()) else: keys.append(key) self.assertTrue(sorted(descr._v_dtype.fields.keys()), sorted(keys)) class TestAtom(common.PyTablesTestCase): def test_atom_attributes01(self): shape = (10, 10) a = Float64Atom(shape=shape) self.assertEqual(a.dflt, 0.) self.assertEqual(a.dtype, numpy.dtype((numpy.float64, shape))) self.assertEqual(a.itemsize, a.dtype.base.itemsize) self.assertEqual(a.kind, 'float') self.assertEqual(a.ndim, len(shape)) # self.assertEqual(a.recarrtype, ) self.assertEqual(a.shape, shape) self.assertEqual(a.size, a.itemsize * numpy.prod(shape)) self.assertEqual(a.type, 'float64') def test_atom_copy01(self): shape = (10, 10) a = Float64Atom(shape=shape) aa = a.copy() self.assertEqual(aa.shape, shape) def test_atom_copy02(self): dflt = 2.0 a = Float64Atom(dflt=dflt) aa = a.copy() self.assertEqual(aa.dflt, dflt) def test_atom_copy_override(self): shape = (10, 10) dflt = 2.0 a = Float64Atom(shape=shape, dflt=dflt) aa = a.copy(dflt=-dflt) self.assertEqual(aa.shape, shape) self.assertNotEqual(aa.dflt, dflt) self.assertEqual(aa.dflt, -dflt) class TestCol(common.PyTablesTestCase): def test_col_copy01(self): shape = (10, 10) c = Float64Col(shape=shape) cc = c.copy() self.assertEqual(cc.shape, shape) def test_col_copy02(self): dflt = 2.0 c = Float64Col(dflt=dflt) cc = c.copy() self.assertEqual(cc.dflt, dflt) def test_col_copy_override(self): shape = (10, 10) dflt = 2.0 pos = 3 c = Float64Col(shape=shape, dflt=dflt, pos=pos) cc = c.copy(pos=2) self.assertEqual(cc.shape, shape) self.assertEqual(cc.dflt, dflt) self.assertNotEqual(cc._v_pos, pos) self.assertEqual(cc._v_pos, 2) class TestSysattrCompatibility(common.PyTablesTestCase): def test_open_python2(self): filename = self._testFilename("python2.h5") fileh = open_file(filename, "r") self.assertTrue(fileh.isopen) fileh.close() def test_open_python3(self): filename = self._testFilename("python2.h5") fileh = open_file(filename, "r") self.assertTrue(fileh.isopen) fileh.close() #---------------------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() niter = 1 blosc_avail = which_lib_version("blosc") is not None for i in range(niter): theSuite.addTest(unittest.makeSuite(OpenFileFailureTestCase)) theSuite.addTest(unittest.makeSuite(NodeCacheOpenFile)) theSuite.addTest(unittest.makeSuite(NoNodeCacheOpenFile)) theSuite.addTest(unittest.makeSuite(DictNodeCacheOpenFile)) theSuite.addTest(unittest.makeSuite(CheckFileTestCase)) if tables.file._FILE_OPEN_POLICY != 'strict': theSuite.addTest(unittest.makeSuite(ThreadingTestCase)) theSuite.addTest(unittest.makeSuite(PythonAttrsTestCase)) theSuite.addTest(unittest.makeSuite(StateTestCase)) theSuite.addTest(unittest.makeSuite(FlavorTestCase)) theSuite.addTest(unittest.makeSuite(FilePropertyTestCase)) if blosc_avail: theSuite.addTest(unittest.makeSuite(BloscBigEndian)) if multiprocessing_imported: theSuite.addTest(unittest.makeSuite(BloscSubprocess)) theSuite.addTest(unittest.makeSuite(HDF5ErrorHandling)) theSuite.addTest(unittest.makeSuite(TestDescription)) theSuite.addTest(unittest.makeSuite(TestAtom)) theSuite.addTest(unittest.makeSuite(TestCol)) theSuite.addTest(unittest.makeSuite(TestSysattrCompatibility)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') ## Local Variables: ## mode: python ## End: PyTables-v.3.1.1/tables/tests/test_carray.py000066400000000000000000003011641231437614300210220ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import unittest import os import tempfile import numpy import tables from tables import * from tables.tests import common from tables.tests.common import allequal # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup class BasicTestCase(unittest.TestCase): # Default values obj = None flavor = "numpy" type = 'int32' shape = (2, 2) start = 0 stop = 10 step = 1 length = 1 chunkshape = (5, 5) compress = 0 complib = "zlib" # Default compression library shuffle = 0 fletcher32 = 0 reopen = 1 # Tells whether the file has to be reopened on each test or not def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") self.rootgroup = self.fileh.root self.populateFile() if self.reopen: # Close the file self.fileh.close() def populateFile(self): group = self.rootgroup obj = self.obj if obj is None: if self.type == "string": atom = StringAtom(itemsize=self.length) else: atom = Atom.from_type(self.type) else: atom = None title = self.__class__.__name__ filters = Filters(complevel=self.compress, complib=self.complib, shuffle=self.shuffle, fletcher32=self.fletcher32) carray = self.fileh.create_carray(group, 'carray1', atom=atom, shape=self.shape, title=title, filters=filters, chunkshape=self.chunkshape, obj=obj) carray.flavor = self.flavor # Fill it with data self.rowshape = list(carray.shape) self.objsize = self.length * numpy.prod(carray.shape) if self.flavor == "numpy": if self.type == "string": object = numpy.ndarray(buffer=b"a"*self.objsize, shape=self.shape, dtype="S%s" % carray.atom.itemsize) else: object = numpy.arange(self.objsize, dtype=carray.atom.dtype) object.shape = carray.shape if common.verbose: print("Object to append -->", repr(object)) carray[...] = object def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def _get_shape(self): if self.shape is not None: shape = self.shape else: shape = numpy.asarray(self.obj).shape return shape def test00_attributes(self): if self.reopen: self.fileh = open_file(self.file, "r") obj = self.fileh.get_node("/carray1") shape = self._get_shape() self.assertEqual(obj.flavor, self.flavor) self.assertEqual(obj.shape, shape) self.assertEqual(obj.ndim, len(shape)) self.assertEqual(obj.chunkshape, self.chunkshape) self.assertEqual(obj.nrows, shape[0]) self.assertEqual(obj.atom.type, self.type) def test01_readCArray(self): """Checking read() of chunked layout arrays.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_readCArray..." % self.__class__.__name__) # Create an instance of an HDF5 Table if self.reopen: self.fileh = open_file(self.file, "r") carray = self.fileh.get_node("/carray1") # Choose a small value for buffer size carray.nrowsinbuf = 3 if common.verbose: print("CArray descr:", repr(carray)) print("shape of read array ==>", carray.shape) print("reopening?:", self.reopen) shape = self._get_shape() # Build the array to do comparisons if self.flavor == "numpy": if self.type == "string": object_ = numpy.ndarray(buffer=b"a"*self.objsize, shape=self.shape, dtype="S%s" % carray.atom.itemsize) else: object_ = numpy.arange(self.objsize, dtype=carray.atom.dtype) object_.shape = shape stop = self.stop # stop == None means read only the element designed by start # (in read() contexts) if self.stop is None: if self.start == -1: # corner case stop = carray.nrows else: stop = self.start + 1 # Protection against number of elements less than existing # if rowshape[self.extdim] < self.stop or self.stop == 0: if carray.nrows < stop: # self.stop == 0 means last row only in read() # and not in [::] slicing notation stop = int(carray.nrows) # do a copy() in order to ensure that len(object._data) # actually do a measure of its length object = object_[self.start:stop:self.step].copy() # Read all the array try: data = carray.read(self.start, stop, self.step) except IndexError: if self.flavor == "numpy": data = numpy.empty(shape=self.shape, dtype=self.type) else: data = numpy.empty(shape=self.shape, dtype=self.type) if common.verbose: if hasattr(object, "shape"): print("shape should look as:", object.shape) print("Object read ==>", repr(data)) print("Should look like ==>", repr(object)) if hasattr(data, "shape"): self.assertEqual(len(data.shape), len(shape)) else: # Scalar case self.assertEqual(len(self.shape), 1) self.assertEqual(carray.chunkshape, self.chunkshape) self.assertTrue(allequal(data, object, self.flavor)) def test01_readCArray_out_argument(self): """Checking read() of chunked layout arrays.""" # Create an instance of an HDF5 Table if self.reopen: self.fileh = open_file(self.file, "r") carray = self.fileh.get_node("/carray1") shape = self._get_shape() # Choose a small value for buffer size carray.nrowsinbuf = 3 # Build the array to do comparisons if self.flavor == "numpy": if self.type == "string": object_ = numpy.ndarray(buffer=b"a"*self.objsize, shape=self.shape, dtype="S%s" % carray.atom.itemsize) else: object_ = numpy.arange(self.objsize, dtype=carray.atom.dtype) object_.shape = shape stop = self.stop # stop == None means read only the element designed by start # (in read() contexts) if self.stop is None: if self.start == -1: # corner case stop = carray.nrows else: stop = self.start + 1 # Protection against number of elements less than existing # if rowshape[self.extdim] < self.stop or self.stop == 0: if carray.nrows < stop: # self.stop == 0 means last row only in read() # and not in [::] slicing notation stop = int(carray.nrows) # do a copy() in order to ensure that len(object._data) # actually do a measure of its length object = object_[self.start:stop:self.step].copy() # Read all the array try: data = numpy.empty(shape, dtype=carray.atom.dtype) data = data[self.start:stop:self.step].copy() carray.read(self.start, stop, self.step, out=data) except IndexError: if self.flavor == "numpy": data = numpy.empty(shape=shape, dtype=self.type) else: data = numpy.empty(shape=shape, dtype=self.type) if hasattr(data, "shape"): self.assertEqual(len(data.shape), len(shape)) else: # Scalar case self.assertEqual(len(shape), 1) self.assertEqual(carray.chunkshape, self.chunkshape) self.assertTrue(allequal(data, object, self.flavor)) def test02_getitemCArray(self): """Checking chunked layout array __getitem__ special method.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_getitemCArray..." % self.__class__.__name__) if not hasattr(self, "slices"): # If there is not a slices attribute, create it self.slices = (slice(self.start, self.stop, self.step),) # Create an instance of an HDF5 Table if self.reopen: self.fileh = open_file(self.file, "r") carray = self.fileh.get_node("/carray1") if common.verbose: print("CArray descr:", repr(carray)) print("shape of read array ==>", carray.shape) print("reopening?:", self.reopen) shape = self._get_shape() # Build the array to do comparisons if self.type == "string": object_ = numpy.ndarray(buffer=b"a"*self.objsize, shape=self.shape, dtype="S%s" % carray.atom.itemsize) else: object_ = numpy.arange(self.objsize, dtype=carray.atom.dtype) object_.shape = shape # do a copy() in order to ensure that len(object._data) # actually do a measure of its length object = object_.__getitem__(self.slices).copy() # Read data from the array try: data = carray.__getitem__(self.slices) except IndexError: print("IndexError!") if self.flavor == "numpy": data = numpy.empty(shape=self.shape, dtype=self.type) else: data = numpy.empty(shape=self.shape, dtype=self.type) if common.verbose: print("Object read:\n", repr(data)) # , data.info() print("Should look like:\n", repr(object)) # , object.info() if hasattr(object, "shape"): print("Original object shape:", self.shape) print("Shape read:", data.shape) print("shape should look as:", object.shape) if not hasattr(data, "shape"): # Scalar case self.assertEqual(len(self.shape), 1) self.assertEqual(carray.chunkshape, self.chunkshape) self.assertTrue(allequal(data, object, self.flavor)) def test03_setitemCArray(self): """Checking chunked layout array __setitem__ special method.""" if self.__class__.__name__ == "Ellipsis6CArrayTestCase": # see test_earray.py BasicTestCase.test03_setitemEArray return if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_setitemCArray..." % self.__class__.__name__) if not hasattr(self, "slices"): # If there is not a slices attribute, create it self.slices = (slice(self.start, self.stop, self.step),) # Create an instance of an HDF5 Table if self.reopen: self.fileh = open_file(self.file, "a") carray = self.fileh.get_node("/carray1") if common.verbose: print("CArray descr:", repr(carray)) print("shape of read array ==>", carray.shape) print("reopening?:", self.reopen) shape = self._get_shape() # Build the array to do comparisons if self.type == "string": object_ = numpy.ndarray(buffer=b"a"*self.objsize, shape=self.shape, dtype="S%s" % carray.atom.itemsize) else: object_ = numpy.arange(self.objsize, dtype=carray.atom.dtype) object_.shape = shape # do a copy() in order to ensure that len(object._data) # actually do a measure of its length object = object_.__getitem__(self.slices).copy() if self.type == "string": if hasattr(self, "wslice"): object[self.wslize] = "xXx" carray[self.wslice] = "xXx" elif sum(object[self.slices].shape) != 0: object[:] = "xXx" if object.size > 0: carray[self.slices] = object else: if hasattr(self, "wslice"): object[self.wslice] = object[self.wslice] * 2 + 3 carray[self.wslice] = carray[self.wslice] * 2 + 3 elif sum(object[self.slices].shape) != 0: object = object * 2 + 3 if numpy.prod(object.shape) > 0: carray[self.slices] = carray[self.slices] * 2 + 3 # Cast again object to its original type object = numpy.array(object, dtype=carray.atom.dtype) # Read datafrom the array try: data = carray.__getitem__(self.slices) except IndexError: print("IndexError!") if self.flavor == "numpy": data = numpy.empty(shape=self.shape, dtype=self.type) else: data = numpy.empty(shape=self.shape, dtype=self.type) if common.verbose: print("Object read:\n", repr(data)) # , data.info() print("Should look like:\n", repr(object)) # , object.info() if hasattr(object, "shape"): print("Original object shape:", self.shape) print("Shape read:", data.shape) print("shape should look as:", object.shape) if not hasattr(data, "shape"): # Scalar case self.assertEqual(len(self.shape), 1) self.assertEqual(carray.chunkshape, self.chunkshape) self.assertTrue(allequal(data, object, self.flavor)) class BasicWriteTestCase(BasicTestCase): type = 'int32' shape = (2,) chunkshape = (5,) step = 1 wslice = 1 # single element case class BasicWrite2TestCase(BasicTestCase): type = 'int32' shape = (2,) chunkshape = (5,) step = 1 wslice = slice(shape[0]-2, shape[0], 2) # range of elements reopen = 0 # This case does not reopen files class BasicWrite3TestCase(BasicTestCase): obj = [1, 2] type = numpy.asarray(obj).dtype.name shape = None chunkshape = (5,) step = 1 reopen = 0 # This case does not reopen files class BasicWrite4TestCase(BasicTestCase): obj = numpy.array([1, 2]) type = obj.dtype.name shape = None chunkshape = (5,) step = 1 reopen = 0 # This case does not reopen files class BasicWrite5TestCase(BasicTestCase): obj = [[1, 2], [3, 4]] type = numpy.asarray(obj).dtype.name shape = None chunkshape = (5, 1) step = 1 reopen = 0 # This case does not reopen files class BasicWrite6TestCase(BasicTestCase): obj = [1, 2] type = numpy.asarray(obj).dtype.name shape = None chunkshape = (5,) step = 1 reopen = 1 # This case does reopen files class BasicWrite7TestCase(BasicTestCase): obj = numpy.array([1, 2]) type = obj.dtype.name shape = None chunkshape = (5,) step = 1 reopen = 1 # This case does reopen files class BasicWrite8TestCase(BasicTestCase): obj = [[1, 2], [3, 4]] type = numpy.asarray(obj).dtype.name shape = None chunkshape = (5, 1) step = 1 reopen = 1 # This case does reopen files class EmptyCArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 2) chunkshape = (5, 5) start = 0 stop = 10 step = 1 class EmptyCArray2TestCase(BasicTestCase): type = 'int32' shape = (2, 2) chunkshape = (5, 5) start = 0 stop = 10 step = 1 reopen = 0 # This case does not reopen files class SlicesCArrayTestCase(BasicTestCase): compress = 1 complib = "lzo" type = 'int32' shape = (2, 2) chunkshape = (5, 5) slices = (slice(1, 2, 1), slice(1, 3, 1)) class EllipsisCArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 2) chunkshape = (5, 5) # slices = (slice(1,2,1), Ellipsis) slices = (Ellipsis, slice(1, 2, 1)) class Slices2CArrayTestCase(BasicTestCase): compress = 1 complib = "lzo" type = 'int32' shape = (2, 2, 4) chunkshape = (5, 5, 5) slices = (slice(1, 2, 1), slice(None, None, None), slice(1, 4, 2)) class Ellipsis2CArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 2, 4) chunkshape = (5, 5, 5) slices = (slice(1, 2, 1), Ellipsis, slice(1, 4, 2)) class Slices3CArrayTestCase(BasicTestCase): compress = 1 # To show the chunks id DEBUG is on complib = "lzo" type = 'int32' shape = (2, 3, 4, 2) chunkshape = (5, 5, 5, 5) slices = (slice(1, 2, 1), slice( 0, None, None), slice(1, 4, 2)) # Don't work # slices = (slice(None, None, None), slice(0, None, None), # slice(1,4,1)) # W # slices = (slice(None, None, None), slice(None, None, None), # slice(1,4,2)) # N # slices = (slice(1,2,1), slice(None, None, None), slice(1,4,2)) # N # Disable the failing test temporarily with a working test case slices = (slice(1, 2, 1), slice(1, 4, None), slice(1, 4, 2)) # Y # slices = (slice(1,2,1), slice(0, 4, None), slice(1,4,1)) # Y slices = (slice(1, 2, 1), slice(0, 4, None), slice(1, 4, 2)) # N # slices = (slice(1,2,1), slice(0, 4, None), slice(1,4,2), # slice(0,100,1)) # N class Slices4CArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 4, 2, 5, 6) chunkshape = (5, 5, 5, 5, 5, 5) slices = (slice(1, 2, 1), slice(0, None, None), slice(1, 4, 2), slice(0, 4, 2), slice(3, 5, 2), slice(2, 7, 1)) class Ellipsis3CArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 4, 2) chunkshape = (5, 5, 5, 5) slices = (Ellipsis, slice(0, 4, None), slice(1, 4, 2)) slices = (slice(1, 2, 1), slice(0, 4, None), slice(1, 4, 2), Ellipsis) class Ellipsis4CArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 4, 5) chunkshape = (5, 5, 5, 5) slices = (Ellipsis, slice(0, 4, None), slice(1, 4, 2)) slices = (slice(1, 2, 1), Ellipsis, slice(1, 4, 2)) class Ellipsis5CArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 4, 5) chunkshape = (5, 5, 5, 5) slices = (slice(1, 2, 1), slice(0, 4, None), Ellipsis) class Ellipsis6CArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 4, 5) chunkshape = (5, 5, 5, 5) # The next slices gives problems with setting values (test03) # This is a problem on the test design, not the Array.__setitem__ # code, though. See # see test_earray.py Ellipsis6EArrayTestCase slices = (slice(1, 2, 1), slice(0, 4, None), 2, Ellipsis) class Ellipsis7CArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 4, 5) chunkshape = (5, 5, 5, 5) slices = (slice(1, 2, 1), slice(0, 4, None), slice(2, 3), Ellipsis) class MD3WriteTestCase(BasicTestCase): type = 'int32' shape = (2, 2, 3) chunkshape = (4, 4, 4) step = 2 class MD5WriteTestCase(BasicTestCase): type = 'int32' shape = (2, 2, 3, 4, 5) # ok # shape = (1, 1, 2, 1) # Minimum shape that shows problems with HDF5 1.6.1 # shape = (2, 3, 2, 4, 5) # Floating point exception (HDF5 1.6.1) # shape = (2, 3, 3, 2, 5, 6) # Segmentation fault (HDF5 1.6.1) chunkshape = (1, 1, 1, 1, 1) start = 1 stop = 10 step = 10 class MD6WriteTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 3, 2, 5, 6) chunkshape = (1, 1, 1, 1, 5, 6) start = 1 stop = 10 step = 3 class MD6WriteTestCase__(BasicTestCase): type = 'int32' shape = (2, 2) chunkshape = (1, 1) start = 1 stop = 3 step = 1 class MD7WriteTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 3, 4, 5, 2, 3) chunkshape = (10, 10, 10, 10, 10, 10, 10) start = 1 stop = 10 step = 2 class MD10WriteTestCase(BasicTestCase): type = 'int32' shape = (1, 2, 3, 4, 5, 5, 4, 3, 2, 2) chunkshape = (5, 5, 5, 5, 5, 5, 5, 5, 5, 5) start = -1 stop = -1 step = 10 class ZlibComprTestCase(BasicTestCase): compress = 1 complib = "zlib" start = 3 # stop = 0 # means last row stop = None # means last row from 0.8 on step = 10 class ZlibShuffleTestCase(BasicTestCase): shuffle = 1 compress = 1 complib = "zlib" # case start < stop , i.e. no rows read start = 3 stop = 1 step = 10 class BloscComprTestCase(BasicTestCase): compress = 1 # sss complib = "blosc" chunkshape = (10, 10) start = 3 stop = 10 step = 3 class BloscShuffleTestCase(BasicTestCase): shape = (20, 30) compress = 1 shuffle = 1 complib = "blosc" chunkshape = (100, 100) start = 3 stop = 10 step = 7 class BloscFletcherTestCase(BasicTestCase): # see gh-21 shape = (200, 300) compress = 1 shuffle = 1 fletcher32 = 1 complib = "blosc" chunkshape = (100, 100) start = 3 stop = 10 step = 7 class BloscBloscLZTestCase(BasicTestCase): shape = (20, 30) compress = 1 shuffle = 1 complib = "blosc:blosclz" chunkshape = (200, 100) start = 2 stop = 11 step = 7 class BloscLZ4TestCase(BasicTestCase): shape = (20, 30) compress = 1 shuffle = 1 complib = "blosc:lz4" chunkshape = (100, 100) start = 3 stop = 10 step = 7 class BloscLZ4HCTestCase(BasicTestCase): shape = (20, 30) compress = 1 shuffle = 1 complib = "blosc:lz4hc" chunkshape = (100, 100) start = 3 stop = 10 step = 7 class BloscSnappyTestCase(BasicTestCase): shape = (20, 30) compress = 1 shuffle = 1 complib = "blosc:snappy" chunkshape = (100, 100) start = 3 stop = 10 step = 7 class BloscZlibTestCase(BasicTestCase): shape = (20, 30) compress = 1 shuffle = 1 complib = "blosc:zlib" chunkshape = (100, 100) start = 3 stop = 10 step = 7 class LZOComprTestCase(BasicTestCase): compress = 1 # sss complib = "lzo" chunkshape = (10, 10) start = 3 stop = 10 step = 3 class LZOShuffleTestCase(BasicTestCase): shape = (20, 30) compress = 1 shuffle = 1 complib = "lzo" chunkshape = (100, 100) start = 3 stop = 10 step = 7 class Bzip2ComprTestCase(BasicTestCase): shape = (20, 30) compress = 1 complib = "bzip2" chunkshape = (100, 100) start = 3 stop = 10 step = 8 class Bzip2ShuffleTestCase(BasicTestCase): shape = (20, 30) compress = 1 shuffle = 1 complib = "bzip2" chunkshape = (100, 100) start = 3 stop = 10 step = 6 class Fletcher32TestCase(BasicTestCase): shape = (60, 50) compress = 0 fletcher32 = 1 chunkshape = (50, 50) start = 4 stop = 20 step = 7 class AllFiltersTestCase(BasicTestCase): compress = 1 shuffle = 1 fletcher32 = 1 complib = "zlib" chunkshape = (20, 20) # sss start = 2 stop = 99 step = 6 class FloatTypeTestCase(BasicTestCase): type = 'float64' shape = (2, 2) chunkshape = (5, 5) start = 3 stop = 10 step = 20 class ComplexTypeTestCase(BasicTestCase): type = 'complex128' shape = (2, 2) chunkshape = (5, 5) start = 3 stop = 10 step = 20 class StringTestCase(BasicTestCase): type = "string" length = 20 shape = (2, 2) # shape = (2,2,20) chunkshape = (5, 5) start = 3 stop = 10 step = 20 slices = (slice(0, 1), slice(1, 2)) class String2TestCase(BasicTestCase): type = "string" length = 20 shape = (2, 20) chunkshape = (5, 5) start = 1 stop = 10 step = 2 class StringComprTestCase(BasicTestCase): type = "string" length = 20 shape = (20, 2, 10) # shape = (20,0,10,20) compr = 1 # shuffle = 1 # this shouldn't do nothing on chars chunkshape = (50, 50, 2) start = -1 stop = 100 step = 20 class Int8TestCase(BasicTestCase): type = "int8" shape = (2, 2) compress = 1 shuffle = 1 chunkshape = (50, 50) start = -1 stop = 100 step = 20 class Int16TestCase(BasicTestCase): type = "int16" shape = (2, 2) compress = 1 shuffle = 1 chunkshape = (50, 50) start = 1 stop = 100 step = 1 class Int32TestCase(BasicTestCase): type = "int32" shape = (2, 2) compress = 1 shuffle = 1 chunkshape = (50, 50) start = -1 stop = 100 step = 20 class Float16TestCase(BasicTestCase): type = "float16" shape = (200,) compress = 1 shuffle = 1 chunkshape = (20,) start = -1 stop = 100 step = 20 class Float32TestCase(BasicTestCase): type = "float32" shape = (200,) compress = 1 shuffle = 1 chunkshape = (20,) start = -1 stop = 100 step = 20 class Float64TestCase(BasicTestCase): type = "float64" shape = (200,) compress = 1 shuffle = 1 chunkshape = (20,) start = -1 stop = 100 step = 20 class Float96TestCase(BasicTestCase): type = "float96" shape = (200,) compress = 1 shuffle = 1 chunkshape = (20,) start = -1 stop = 100 step = 20 class Float128TestCase(BasicTestCase): type = "float128" shape = (200,) compress = 1 shuffle = 1 chunkshape = (20,) start = -1 stop = 100 step = 20 class Complex64TestCase(BasicTestCase): type = "complex64" shape = (4,) compress = 1 shuffle = 1 chunkshape = (2,) start = -1 stop = 100 step = 20 class Complex128TestCase(BasicTestCase): type = "complex128" shape = (20,) compress = 1 shuffle = 1 chunkshape = (2,) start = -1 stop = 100 step = 20 class Complex192TestCase(BasicTestCase): type = "complex192" shape = (20,) compress = 1 shuffle = 1 chunkshape = (2,) start = -1 stop = 100 step = 20 class Complex256TestCase(BasicTestCase): type = "complex256" shape = (20,) compress = 1 shuffle = 1 chunkshape = (2,) start = -1 stop = 100 step = 20 class ComprTestCase(BasicTestCase): type = "float64" compress = 1 shuffle = 1 shape = (200,) compr = 1 chunkshape = (21,) start = 51 stop = 100 step = 7 # this is a subset of the tests in test_array.py, mostly to verify that errors # are handled in the same way class ReadOutArgumentTests(unittest.TestCase): def setUp(self): self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode='w') self.size = 1000 self.filters = Filters(complevel=1, complib='blosc') def tearDown(self): self.fileh.close() os.remove(self.file) def create_array(self): array = numpy.arange(self.size, dtype='i8') disk_array = self.fileh.create_carray('/', 'array', atom=Int64Atom(), shape=(self.size, ), filters=self.filters) disk_array[:] = array return array, disk_array def test_read_entire_array(self): array, disk_array = self.create_array() out_buffer = numpy.empty((self.size, ), 'i8') disk_array.read(out=out_buffer) numpy.testing.assert_equal(out_buffer, array) def test_read_non_contiguous_buffer(self): array, disk_array = self.create_array() out_buffer = numpy.empty((self.size, ), 'i8') out_buffer_slice = out_buffer[0:self.size:2] # once Python 2.6 support is dropped, this could change # to assertRaisesRegexp to check exception type and message at once self.assertRaises(ValueError, disk_array.read, 0, self.size, 2, out_buffer_slice) try: disk_array.read(0, self.size, 2, out_buffer_slice) except ValueError as exc: self.assertEqual('output array not C contiguous', str(exc)) def test_buffer_too_small(self): array, disk_array = self.create_array() out_buffer = numpy.empty((self.size // 2, ), 'i8') self.assertRaises(ValueError, disk_array.read, 0, self.size, 1, out_buffer) try: disk_array.read(0, self.size, 1, out_buffer) except ValueError as exc: self.assertTrue('output array size invalid, got' in str(exc)) class SizeOnDiskInMemoryPropertyTestCase(unittest.TestCase): def setUp(self): self.array_size = (10000, 10) # set chunkshape so it divides evenly into array_size, to avoid # partially filled chunks self.chunkshape = (1000, 10) # approximate size (in bytes) of non-data portion of hdf5 file self.hdf_overhead = 6000 self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w") def tearDown(self): self.fileh.close() # Then, delete the file os.remove(self.file) common.cleanup(self) def create_array(self, complevel): filters = Filters(complevel=complevel, complib='blosc') self.array = self.fileh.create_carray('/', 'somearray', atom=Int16Atom(), shape=self.array_size, filters=filters, chunkshape=self.chunkshape) def test_no_data(self): complevel = 0 self.create_array(complevel) self.assertEqual(self.array.size_on_disk, 0) self.assertEqual(self.array.size_in_memory, 10000 * 10 * 2) def test_data_no_compression(self): complevel = 0 self.create_array(complevel) self.array[:] = 1 self.assertEqual(self.array.size_on_disk, 10000 * 10 * 2) self.assertEqual(self.array.size_in_memory, 10000 * 10 * 2) def test_highly_compressible_data(self): complevel = 1 self.create_array(complevel) self.array[:] = 1 self.fileh.flush() file_size = os.stat(self.file).st_size self.assertTrue( abs(self.array.size_on_disk - file_size) <= self.hdf_overhead) self.assertTrue(self.array.size_on_disk < self.array.size_in_memory) self.assertEqual(self.array.size_in_memory, 10000 * 10 * 2) # XXX def test_random_data(self): complevel = 1 self.create_array(complevel) self.array[:] = numpy.random.randint(0, 1e6, self.array_size) self.fileh.flush() file_size = os.stat(self.file).st_size self.assertTrue( abs(self.array.size_on_disk - file_size) <= self.hdf_overhead) # XXX: check. The test fails if blosc is not available if which_lib_version('blosc') is not None: self.assertAlmostEqual(self.array.size_on_disk, 10000 * 10 * 2) else: self.assertTrue( abs(self.array.size_on_disk - 10000 * 10 * 2) < 200) class OffsetStrideTestCase(unittest.TestCase): mode = "w" compress = 0 complib = "zlib" # Default compression library def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, self.mode) self.rootgroup = self.fileh.root def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test01a_String(self): """Checking carray with offseted NumPy strings appends.""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test01a_String..." % self.__class__.__name__) shape = (3, 2, 2) # Create an string atom carray = self.fileh.create_carray(root, 'strings', atom=StringAtom(itemsize=3), shape=shape, title="Array of strings", chunkshape=(1, 2, 2)) a = numpy.array([[["a", "b"], [ "123", "45"], ["45", "123"]]], dtype="S3") carray[0] = a[0, 1:] a = numpy.array([[["s", "a"], [ "ab", "f"], ["s", "abc"], ["abc", "f"]]]) carray[1] = a[0, 2:] # Read all the data: data = carray.read() if common.verbose: print("Object read:", data) print("Nrows in", carray._v_pathname, ":", carray.nrows) print("Second row in carray ==>", data[1].tolist()) self.assertEqual(carray.nrows, 3) self.assertEqual(data[0].tolist(), [[b"123", b"45"], [b"45", b"123"]]) self.assertEqual(data[1].tolist(), [[b"s", b"abc"], [b"abc", b"f"]]) self.assertEqual(len(data[0]), 2) self.assertEqual(len(data[1]), 2) def test01b_String(self): """Checking carray with strided NumPy strings appends.""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test01b_String..." % self.__class__.__name__) shape = (3, 2, 2) # Create an string atom carray = self.fileh.create_carray(root, 'strings', atom=StringAtom(itemsize=3), shape=shape, title="Array of strings", chunkshape=(1, 2, 2)) a = numpy.array([[["a", "b"], [ "123", "45"], ["45", "123"]]], dtype="S3") carray[0] = a[0, ::2] a = numpy.array([[["s", "a"], [ "ab", "f"], ["s", "abc"], ["abc", "f"]]]) carray[1] = a[0, ::2] # Read all the rows: data = carray.read() if common.verbose: print("Object read:", data) print("Nrows in", carray._v_pathname, ":", carray.nrows) print("Second row in carray ==>", data[1].tolist()) self.assertEqual(carray.nrows, 3) self.assertEqual(data[0].tolist(), [[b"a", b"b"], [b"45", b"123"]]) self.assertEqual(data[1].tolist(), [[b"s", b"a"], [b"s", b"abc"]]) self.assertEqual(len(data[0]), 2) self.assertEqual(len(data[1]), 2) def test02a_int(self): """Checking carray with offseted NumPy ints appends.""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test02a_int..." % self.__class__.__name__) shape = (3, 3) # Create an string atom carray = self.fileh.create_carray(root, 'CAtom', atom=Int32Atom(), shape=shape, title="array of ints", chunkshape=(1, 3)) a = numpy.array([(0, 0, 0), (1, 0, 3), ( 1, 1, 1), (0, 0, 0)], dtype='int32') carray[0:2] = a[2:] # Introduce an offset a = numpy.array([(1, 1, 1), (-1, 0, 0)], dtype='int32') carray[2:3] = a[1:] # Introduce an offset # Read all the rows: data = carray.read() if common.verbose: print("Object read:", data) print("Nrows in", carray._v_pathname, ":", carray.nrows) print("Third row in carray ==>", data[2]) self.assertEqual(carray.nrows, 3) self.assertTrue(allequal(data[ 0], numpy.array([1, 1, 1], dtype='int32'))) self.assertTrue(allequal(data[ 1], numpy.array([0, 0, 0], dtype='int32'))) self.assertTrue(allequal(data[ 2], numpy.array([-1, 0, 0], dtype='int32'))) def test02b_int(self): """Checking carray with strided NumPy ints appends.""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test02b_int..." % self.__class__.__name__) shape = (3, 3) # Create an string atom carray = self.fileh.create_carray(root, 'CAtom', atom=Int32Atom(), shape=shape, title="array of ints", chunkshape=(1, 3)) a = numpy.array([(0, 0, 0), (1, 0, 3), ( 1, 1, 1), (3, 3, 3)], dtype='int32') carray[0:2] = a[::3] # Create an offset a = numpy.array([(1, 1, 1), (-1, 0, 0)], dtype='int32') carray[2:3] = a[::2] # Create an offset # Read all the rows: data = carray.read() if common.verbose: print("Object read:", data) print("Nrows in", carray._v_pathname, ":", carray.nrows) print("Third row in carray ==>", data[2]) self.assertEqual(carray.nrows, 3) self.assertTrue(allequal(data[ 0], numpy.array([0, 0, 0], dtype='int32'))) self.assertTrue(allequal(data[ 1], numpy.array([3, 3, 3], dtype='int32'))) self.assertTrue(allequal(data[ 2], numpy.array([1, 1, 1], dtype='int32'))) class CopyTestCase(unittest.TestCase): def test01a_copy(self): """Checking CArray.copy() method.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01a_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an CArray shape = (2, 2) atom = Int16Atom() array1 = fileh.create_carray(fileh.root, 'array1', atom=atom, shape=shape, title="title array1", chunkshape=(2, 2)) array1[...] = numpy.array([[456, 2], [3, 457]], dtype='int16') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another location array2 = array1.copy('/', 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("array1-->", array1.read()) print("array2-->", array2.read()) # print("dirs-->", dir(array1), dir(array2)) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal self.assertTrue(allequal(array1.read(), array2.read())) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.extdim, array2.extdim) self.assertEqual(array1.flavor, array2.flavor) self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(array1.atom.type, array2.atom.type) self.assertEqual(array1.title, array2.title) self.assertEqual(str(array1.atom), str(array2.atom)) # The next line is commented out because a copy should not # keep the same chunkshape anymore. # F. Alted 2006-11-27 # self.assertEqual(array1.chunkshape, array2.chunkshape) # Close the file fileh.close() os.remove(file) def test01b_copy(self): """Checking CArray.copy() method.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01b_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an CArray shape = (2, 2) atom = Int16Atom() array1 = fileh.create_carray(fileh.root, 'array1', atom=atom, shape=shape, title="title array1", chunkshape=(5, 5)) array1[...] = numpy.array([[456, 2], [3, 457]], dtype='int16') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another location array2 = array1.copy('/', 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("array1-->", array1.read()) print("array2-->", array2.read()) # print("dirs-->", dir(array1), dir(array2)) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal self.assertTrue(allequal(array1.read(), array2.read())) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.extdim, array2.extdim) self.assertEqual(array1.flavor, array2.flavor) self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(array1.atom.type, array2.atom.type) self.assertEqual(array1.title, array2.title) self.assertEqual(str(array1.atom), str(array2.atom)) # By default, the chunkshape should be the same self.assertEqual(array1.chunkshape, array2.chunkshape) # Close the file fileh.close() os.remove(file) def test01c_copy(self): """Checking CArray.copy() method.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01c_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an CArray shape = (5, 5) atom = Int16Atom() array1 = fileh.create_carray(fileh.root, 'array1', atom=atom, shape=shape, title="title array1", chunkshape=(2, 2)) array1[:2, :2] = numpy.array([[456, 2], [3, 457]], dtype='int16') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another location array2 = array1.copy('/', 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("array1-->", array1.read()) print("array2-->", array2.read()) # print("dirs-->", dir(array1), dir(array2)) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal self.assertTrue(allequal(array1.read(), array2.read())) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.extdim, array2.extdim) self.assertEqual(array1.flavor, array2.flavor) self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(array1.atom.type, array2.atom.type) self.assertEqual(array1.title, array2.title) self.assertEqual(str(array1.atom), str(array2.atom)) # The next line is commented out because a copy should not # keep the same chunkshape anymore. # F. Alted 2006-11-27 # self.assertEqual(array1.chunkshape, array2.chunkshape) # Close the file fileh.close() os.remove(file) def test02_copy(self): """Checking CArray.copy() method (where specified)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an CArray shape = (5, 5) atom = Int16Atom() array1 = fileh.create_carray(fileh.root, 'array1', atom=atom, shape=shape, title="title array1", chunkshape=(2, 2)) array1[:2, :2] = numpy.array([[456, 2], [3, 457]], dtype='int16') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy to another location group1 = fileh.create_group("/", "group1") array2 = array1.copy(group1, 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.group1.array2 if common.verbose: print("array1-->", array1.read()) print("array2-->", array2.read()) # print("dirs-->", dir(array1), dir(array2)) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal self.assertTrue(allequal(array1.read(), array2.read())) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.extdim, array2.extdim) self.assertEqual(array1.flavor, array2.flavor) self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(array1.atom.type, array2.atom.type) self.assertEqual(array1.title, array2.title) self.assertEqual(str(array1.atom), str(array2.atom)) # The next line is commented out because a copy should not # keep the same chunkshape anymore. # F. Alted 2006-11-27 # self.assertEqual(array1.chunkshape, array2.chunkshape) # Close the file fileh.close() os.remove(file) def test03a_copy(self): """Checking CArray.copy() method (python flavor)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03c_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") shape = (2, 2) atom = Int16Atom() array1 = fileh.create_carray(fileh.root, 'array1', atom=atom, shape=shape, title="title array1", chunkshape=(2, 2)) array1.flavor = "python" array1[...] = [[456, 2], [3, 457]] if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy to another location array2 = array1.copy('/', 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all elements are equal self.assertEqual(array1.read(), array2.read()) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.extdim, array2.extdim) self.assertEqual(array1.flavor, array2.flavor) # Very important here! self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(array1.atom.type, array2.atom.type) self.assertEqual(array1.title, array2.title) self.assertEqual(str(array1.atom), str(array2.atom)) # The next line is commented out because a copy should not # keep the same chunkshape anymore. # F. Alted 2006-11-27 # self.assertEqual(array1.chunkshape, array2.chunkshape) # Close the file fileh.close() os.remove(file) def test03b_copy(self): """Checking CArray.copy() method (string python flavor)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03d_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") shape = (2, 2) atom = StringAtom(itemsize=4) array1 = fileh.create_carray(fileh.root, 'array1', atom=atom, shape=shape, title="title array1", chunkshape=(2, 2)) array1.flavor = "python" array1[...] = [["456", "2"], ["3", "457"]] if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy to another location array2 = array1.copy('/', 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("type value-->", type(array2[:][0][0])) print("value-->", array2[:]) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all elements are equal self.assertEqual(array1.read(), array2.read()) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.extdim, array2.extdim) self.assertEqual(array1.flavor, array2.flavor) # Very important here! self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(array1.atom.type, array2.atom.type) self.assertEqual(array1.title, array2.title) self.assertEqual(str(array1.atom), str(array2.atom)) # The next line is commented out because a copy should not # keep the same chunkshape anymore. # F. Alted 2006-11-27 # self.assertEqual(array1.chunkshape, array2.chunkshape) # Close the file fileh.close() os.remove(file) def test03c_copy(self): """Checking CArray.copy() method (chararray flavor)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03e_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") shape = (2, 2) atom = StringAtom(itemsize=4) array1 = fileh.create_carray(fileh.root, 'array1', atom=atom, shape=shape, title="title array1", chunkshape=(2, 2)) array1[...] = numpy.array([["456", "2"], ["3", "457"]], dtype="S4") if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy to another location array2 = array1.copy('/', 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all elements are equal self.assertTrue(allequal(array1.read(), array2.read())) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.extdim, array2.extdim) self.assertEqual(array1.flavor, array2.flavor) # Very important here! self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(array1.atom.type, array2.atom.type) self.assertEqual(array1.title, array2.title) self.assertEqual(str(array1.atom), str(array2.atom)) # The next line is commented out because a copy should not # keep the same chunkshape anymore. # F. Alted 2006-11-27 # self.assertEqual(array1.chunkshape, array2.chunkshape) # Close the file fileh.close() os.remove(file) def test04_copy(self): """Checking CArray.copy() method (checking title copying)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an CArray shape = (2, 2) atom = Int16Atom() array1 = fileh.create_carray(fileh.root, 'array1', atom=atom, shape=shape, title="title array1", chunkshape=(2, 2)) array1[...] = numpy.array([[456, 2], [3, 457]], dtype='int16') # Append some user attrs array1.attrs.attr1 = "attr1" array1.attrs.attr2 = 2 if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another Array array2 = array1.copy('/', 'array2', title="title array2") if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 # Assert user attributes if common.verbose: print("title of destination array-->", array2.title) self.assertEqual(array2.title, "title array2") # Close the file fileh.close() os.remove(file) def test05_copy(self): """Checking CArray.copy() method (user attributes copied)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an CArray shape = (2, 2) atom = Int16Atom() array1 = fileh.create_carray(fileh.root, 'array1', atom=atom, shape=shape, title="title array1", chunkshape=(2, 2)) array1[...] = numpy.array([[456, 2], [3, 457]], dtype='int16') # Append some user attrs array1.attrs.attr1 = "attr1" array1.attrs.attr2 = 2 if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another Array array2 = array1.copy('/', 'array2', copyuserattrs=1) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Assert user attributes self.assertEqual(array2.attrs.attr1, "attr1") self.assertEqual(array2.attrs.attr2, 2) # Close the file fileh.close() os.remove(file) def test05b_copy(self): """Checking CArray.copy() method (user attributes not copied)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05b_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an Array shape = (2, 2) atom = Int16Atom() array1 = fileh.create_carray(fileh.root, 'array1', atom=atom, shape=shape, title="title array1", chunkshape=(2, 2)) array1[...] = numpy.array([[456, 2], [3, 457]], dtype='int16') # Append some user attrs array1.attrs.attr1 = "attr1" array1.attrs.attr2 = 2 if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another Array array2 = array1.copy('/', 'array2', copyuserattrs=0) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Assert user attributes self.assertEqual(hasattr(array2.attrs, "attr1"), 0) self.assertEqual(hasattr(array2.attrs, "attr2"), 0) # Close the file fileh.close() os.remove(file) class CloseCopyTestCase(CopyTestCase): close = 1 class OpenCopyTestCase(CopyTestCase): close = 0 class CopyIndexTestCase(unittest.TestCase): nrowsinbuf = 2 def test01_index(self): """Checking CArray.copy() method with indexes.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_index..." % self.__class__.__name__) # Create an instance of an HDF5 Array file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an CArray shape = (100, 2) atom = Int32Atom() array1 = fileh.create_carray(fileh.root, 'array1', atom=atom, shape=shape, title="title array1", chunkshape=(2, 2)) r = numpy.arange(200, dtype='int32') r.shape = shape array1[...] = r # Select a different buffer size: array1.nrowsinbuf = self.nrowsinbuf # Copy to another array array2 = array1.copy("/", 'array2', start=self.start, stop=self.stop, step=self.step) if common.verbose: print("array1-->", array1.read()) print("array2-->", array2.read()) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal r2 = r[self.start:self.stop:self.step] self.assertTrue(allequal(r2, array2.read())) # Assert the number of rows in array if common.verbose: print("nrows in array2-->", array2.nrows) print("and it should be-->", r2.shape[0]) # The next line is commented out because a copy should not # keep the same chunkshape anymore. # F. Alted 2006-11-27 # assert array1.chunkshape == array2.chunkshape self.assertEqual(r2.shape[0], array2.nrows) # Close the file fileh.close() os.remove(file) def _test02_indexclosef(self): """Checking CArray.copy() method with indexes (close file version)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_indexclosef..." % self.__class__.__name__) # Create an instance of an HDF5 Array file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an CArray shape = (100, 2) atom = Int32Atom() array1 = fileh.create_carray(fileh.root, 'array1', atom=atom, shape=shape, title="title array1", chunkshape=(2, 2)) r = numpy.arange(200, dtype='int32') r.shape = shape array1[...] = r # Select a different buffer size: array1.nrowsinbuf = self.nrowsinbuf # Copy to another array array2 = array1.copy("/", 'array2', start=self.start, stop=self.stop, step=self.step) # Close and reopen the file fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("array1-->", array1.read()) print("array2-->", array2.read()) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal r2 = r[self.start:self.stop:self.step] self.assertEqual(array1.chunkshape, array2.chunkshape) self.assertTrue(allequal(r2, array2.read())) # Assert the number of rows in array if common.verbose: print("nrows in array2-->", array2.nrows) print("and it should be-->", r2.shape[0]) self.assertEqual(r2.shape[0], array2.nrows) # Close the file fileh.close() os.remove(file) class CopyIndex1TestCase(CopyIndexTestCase): nrowsinbuf = 1 start = 0 stop = 7 step = 1 class CopyIndex2TestCase(CopyIndexTestCase): nrowsinbuf = 2 start = 0 stop = -1 step = 1 class CopyIndex3TestCase(CopyIndexTestCase): nrowsinbuf = 3 start = 1 stop = 7 step = 1 class CopyIndex4TestCase(CopyIndexTestCase): nrowsinbuf = 4 start = 0 stop = 6 step = 1 class CopyIndex5TestCase(CopyIndexTestCase): nrowsinbuf = 2 start = 3 stop = 7 step = 1 class CopyIndex6TestCase(CopyIndexTestCase): nrowsinbuf = 2 start = 3 stop = 6 step = 2 class CopyIndex7TestCase(CopyIndexTestCase): start = 0 stop = 7 step = 10 class CopyIndex8TestCase(CopyIndexTestCase): start = 6 stop = -1 # Negative values means starting from the end step = 1 class CopyIndex9TestCase(CopyIndexTestCase): start = 3 stop = 4 step = 1 class CopyIndex10TestCase(CopyIndexTestCase): nrowsinbuf = 1 start = 3 stop = 4 step = 2 class CopyIndex11TestCase(CopyIndexTestCase): start = -3 stop = -1 step = 2 class CopyIndex12TestCase(CopyIndexTestCase): start = -1 # Should point to the last element stop = None # None should mean the last element (including it) step = 1 # The next test should be run only in **heavy** mode class Rows64bitsTestCase(unittest.TestCase): narows = 1000 * 1000 # each array will have 1 million entries # narows = 1000 # for testing only nanumber = 1000 * 3 # That should account for more than 2**31-1 def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") fileh = self.fileh = open_file(self.file, "a") # Create an CArray shape = (self.narows * self.nanumber,) array = fileh.create_carray(fileh.root, 'array', atom=Int8Atom(), shape=shape, filters=Filters(complib='lzo', complevel=1)) # Fill the array na = numpy.arange(self.narows, dtype='int8') #~ for i in xrange(self.nanumber): #~ s = slice(i * self.narows, (i + 1)*self.narows) #~ array[s] = na s = slice(0, self.narows) array[s] = na s = slice((self.nanumber-1)*self.narows, self.nanumber * self.narows) array[s] = na def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test01_basiccheck(self): "Some basic checks for carrays exceeding 2**31 rows" fileh = self.fileh array = fileh.root.array if self.close: if common.verbose: # Check how many entries there are in the array print("Before closing") print("Entries:", array.nrows, type(array.nrows)) print("Entries:", array.nrows / (1000 * 1000), "Millions") print("Shape:", array.shape) # Close the file fileh.close() # Re-open the file fileh = self.fileh = open_file(self.file) array = fileh.root.array if common.verbose: print("After re-open") # Check how many entries there are in the array if common.verbose: print("Entries:", array.nrows, type(array.nrows)) print("Entries:", array.nrows / (1000 * 1000), "Millions") print("Shape:", array.shape) print("Last 10 elements-->", array[-10:]) stop = self.narows % 256 if stop > 127: stop -= 256 start = stop - 10 # print("start, stop-->", start, stop) print("Should look like:", numpy.arange(start, stop, dtype='int8')) nrows = self.narows * self.nanumber # check nrows self.assertEqual(array.nrows, nrows) # Check shape self.assertEqual(array.shape, (nrows,)) # check the 10 first elements self.assertTrue(allequal(array[:10], numpy.arange(10, dtype='int8'))) # check the 10 last elements stop = self.narows % 256 if stop > 127: stop -= 256 start = stop - 10 self.assertTrue(allequal(array[-10:], numpy.arange(start, stop, dtype='int8'))) class Rows64bitsTestCase1(Rows64bitsTestCase): close = 0 class Rows64bitsTestCase2(Rows64bitsTestCase): close = 1 class BigArrayTestCase(common.TempFileMixin, common.PyTablesTestCase): shape = (3000000000,) # more than 2**31-1 def setUp(self): super(BigArrayTestCase, self).setUp() # This should be fast since disk space isn't actually allocated, # so this case is OK for non-heavy test runs. self.h5file.create_carray('/', 'array', atom=Int8Atom(), shape=self.shape) def test00_shape(self): """Check that the shape doesn't overflow.""" # See ticket #147. self.assertEqual(self.h5file.root.array.shape, self.shape) try: self.assertEqual(len(self.h5file.root.array), self.shape[0]) except OverflowError: # In python 2.4 calling "len(self.h5file.root.array)" raises # an OverflowError also on 64bit platforms:: # OverflowError: __len__() should return 0 <= outcome < 2**31 import sys if sys.version_info[:2] > (2, 4): # This can't be avoided in 32-bit platforms. self.assertTrue(self.shape[0] > numpy.iinfo(int).max, "Array length overflowed but ``int`` " "is wide enough.") def test01_shape_reopen(self): """Check that the shape doesn't overflow after reopening.""" self._reopen('r') self.test00_shape() # Test for default values when creating arrays. class DfltAtomTestCase(common.TempFileMixin, common.PyTablesTestCase): def test00_dflt(self): "Check that Atom.dflt is honored (string version)." # Create a CArray with default values self.h5file.create_carray('/', 'bar', atom=StringAtom(itemsize=5, dflt=b"abdef"), shape=(10, 10)) if self.reopen: self._reopen() # Check the values values = self.h5file.root.bar[:] if common.verbose: print("Read values:", values) self.assertTrue( allequal(values, numpy.array(["abdef"]*100, "S5").reshape(10, 10))) def test01_dflt(self): "Check that Atom.dflt is honored (int version)." # Create a CArray with default values self.h5file.create_carray('/', 'bar', atom=IntAtom(dflt=1), shape=(10, 10)) if self.reopen: self._reopen() # Check the values values = self.h5file.root.bar[:] if common.verbose: print("Read values:", values) self.assertTrue(allequal(values, numpy.ones((10, 10), "i4"))) def test02_dflt(self): "Check that Atom.dflt is honored (float version)." # Create a CArray with default values self.h5file.create_carray('/', 'bar', atom=FloatAtom(dflt=1.134), shape=(10, 10)) if self.reopen: self._reopen() # Check the values values = self.h5file.root.bar[:] if common.verbose: print("Read values:", values) self.assertTrue(allequal(values, numpy.ones((10, 10), "f8")*1.134)) class DfltAtomNoReopen(DfltAtomTestCase): reopen = False class DfltAtomReopen(DfltAtomTestCase): reopen = True # Test for representation of defaults in atoms. Ticket #212. class AtomDefaultReprTestCase(common.TempFileMixin, common.PyTablesTestCase): def test00a_zeros(self): "Testing default values. Zeros (scalar)." N = () atom = StringAtom(itemsize=3, shape=N, dflt=b"") ca = self.h5file.create_carray('/', 'test', atom=atom, shape=(1,)) if self.reopen: self._reopen('a') ca = self.h5file.root.test # Check the value if common.verbose: print("First row-->", repr(ca[0])) print("Defaults-->", repr(ca.atom.dflt)) self.assertTrue(allequal(ca[0], numpy.zeros(N, 'S3'))) self.assertTrue(allequal(ca.atom.dflt, numpy.zeros(N, 'S3'))) def test00b_zeros(self): "Testing default values. Zeros (array)." N = 2 atom = StringAtom(itemsize=3, shape=N, dflt=b"") ca = self.h5file.create_carray('/', 'test', atom=atom, shape=(1,)) if self.reopen: self._reopen('a') ca = self.h5file.root.test # Check the value if common.verbose: print("First row-->", ca[0]) print("Defaults-->", ca.atom.dflt) self.assertTrue(allequal(ca[0], numpy.zeros(N, 'S3'))) self.assertTrue(allequal(ca.atom.dflt, numpy.zeros(N, 'S3'))) def test01a_values(self): "Testing default values. Ones." N = 2 atom = Int32Atom(shape=N, dflt=1) ca = self.h5file.create_carray('/', 'test', atom=atom, shape=(1,)) if self.reopen: self._reopen('a') ca = self.h5file.root.test # Check the value if common.verbose: print("First row-->", ca[0]) print("Defaults-->", ca.atom.dflt) self.assertTrue(allequal(ca[0], numpy.ones(N, 'i4'))) self.assertTrue(allequal(ca.atom.dflt, numpy.ones(N, 'i4'))) def test01b_values(self): "Testing default values. Generic value." N = 2 generic = 112.32 atom = Float32Atom(shape=N, dflt=generic) ca = self.h5file.create_carray('/', 'test', atom=atom, shape=(1,)) if self.reopen: self._reopen('a') ca = self.h5file.root.test # Check the value if common.verbose: print("First row-->", ca[0]) print("Defaults-->", ca.atom.dflt) self.assertTrue(allequal(ca[0], numpy.ones(N, 'f4')*generic)) self.assertTrue(allequal(ca.atom.dflt, numpy.ones(N, 'f4')*generic)) def test02a_None(self): "Testing default values. None (scalar)." N = () atom = Int32Atom(shape=N, dflt=None) ca = self.h5file.create_carray('/', 'test', atom=atom, shape=(1,)) if self.reopen: self._reopen('a') ca = self.h5file.root.test # Check the value if common.verbose: print("First row-->", repr(ca[0])) print("Defaults-->", repr(ca.atom.dflt)) self.assertTrue(allequal(ca.atom.dflt, numpy.zeros(N, 'i4'))) def test02b_None(self): "Testing default values. None (array)." N = 2 atom = Int32Atom(shape=N, dflt=None) ca = self.h5file.create_carray('/', 'test', atom=atom, shape=(1,)) if self.reopen: self._reopen('a') ca = self.h5file.root.test # Check the value if common.verbose: print("First row-->", ca[0]) print("Defaults-->", ca.atom.dflt) self.assertTrue(allequal(ca.atom.dflt, numpy.zeros(N, 'i4'))) class AtomDefaultReprNoReopen(AtomDefaultReprTestCase): reopen = False class AtomDefaultReprReopen(AtomDefaultReprTestCase): reopen = True class TruncateTestCase(common.TempFileMixin, common.PyTablesTestCase): def test(self): """Test for unability to truncate Array objects.""" array1 = self.h5file.create_carray('/', 'array1', IntAtom(), [2, 2]) self.assertRaises(TypeError, array1.truncate, 0) # Test for dealing with multidimensional atoms class MDAtomTestCase(common.TempFileMixin, common.PyTablesTestCase): def test01a_assign(self): "Assign a row to a (unidimensional) CArray with a MD atom." # Create an CArray ca = self.h5file.create_carray('/', 'test', atom=Int32Atom((2, 2)), shape=(1,)) if self.reopen: self._reopen('a') ca = self.h5file.root.test # Assign one row ca[0] = [[1, 3], [4, 5]] self.assertEqual(ca.nrows, 1) if common.verbose: print("First row-->", ca[0]) self.assertTrue(allequal(ca[0], numpy.array([[1, 3], [4, 5]], 'i4'))) def test01b_assign(self): "Assign several rows to a (unidimensional) CArray with a MD atom." # Create an CArray ca = self.h5file.create_carray('/', 'test', atom=Int32Atom((2, 2)), shape=(3,)) if self.reopen: self._reopen('a') ca = self.h5file.root.test # Assign three rows ca[:] = [[[1]], [[2]], [[3]]] # Simple broadcast self.assertEqual(ca.nrows, 3) if common.verbose: print("Third row-->", ca[2]) self.assertTrue(allequal(ca[2], numpy.array([[3, 3], [3, 3]], 'i4'))) def test02a_assign(self): "Assign a row to a (multidimensional) CArray with a MD atom." # Create an CArray ca = self.h5file.create_carray('/', 'test', atom=Int32Atom((2,)), shape=(1, 3)) if self.reopen: self._reopen('a') ca = self.h5file.root.test # Assign one row ca[:] = [[[1, 3], [4, 5], [7, 9]]] self.assertEqual(ca.nrows, 1) if common.verbose: print("First row-->", ca[0]) self.assertTrue(allequal(ca[0], numpy.array( [[1, 3], [4, 5], [7, 9]], 'i4'))) def test02b_assign(self): "Assign several rows to a (multidimensional) CArray with a MD atom." # Create an CArray ca = self.h5file.create_carray('/', 'test', atom=Int32Atom((2,)), shape=(3, 3)) if self.reopen: self._reopen('a') ca = self.h5file.root.test # Assign three rows ca[:] = [[[1, -3], [4, -5], [-7, 9]], [[-1, 3], [-4, 5], [7, -8]], [[-2, 3], [-5, 5], [7, -9]]] self.assertEqual(ca.nrows, 3) if common.verbose: print("Third row-->", ca[2]) self.assertTrue( allequal(ca[2], numpy.array([[-2, 3], [-5, 5], [7, -9]], 'i4'))) def test03a_MDMDMD(self): "Complex assign of a MD array in a MD CArray with a MD atom." # Create an CArray ca = self.h5file.create_carray('/', 'test', atom=Int32Atom((2, 4)), shape=(3, 2, 3)) if self.reopen: self._reopen('a') ca = self.h5file.root.test # Assign values # The shape of the atom should be added at the end of the arrays a = numpy.arange(2 * 3*2*4, dtype='i4').reshape((2, 3, 2, 4)) ca[:] = [a * 1, a*2, a*3] self.assertEqual(ca.nrows, 3) if common.verbose: print("Third row-->", ca[2]) self.assertTrue(allequal(ca[2], a * 3)) def test03b_MDMDMD(self): "Complex assign of a MD array in a MD CArray with a MD atom (II)." # Create an CArray ca = self.h5file.create_carray( '/', 'test', atom=Int32Atom((2, 4)), shape=(2, 3, 3)) if self.reopen: self._reopen('a') ca = self.h5file.root.test # Assign values # The shape of the atom should be added at the end of the arrays a = numpy.arange(2 * 3*3*2*4, dtype='i4').reshape((2, 3, 3, 2, 4)) ca[:] = a self.assertEqual(ca.nrows, 2) if common.verbose: print("Third row-->", ca[:, 2, ...]) self.assertTrue(allequal(ca[:, 2, ...], a[:, 2, ...])) def test03c_MDMDMD(self): "Complex assign of a MD array in a MD CArray with a MD atom (III)." # Create an CArray ca = self.h5file.create_carray('/', 'test', atom=Int32Atom((2, 4)), shape=(3, 1, 2)) if self.reopen: self._reopen('a') ca = self.h5file.root.test # Assign values # The shape of the atom should be added at the end of the arrays a = numpy.arange(3 * 1*2*2*4, dtype='i4').reshape((3, 1, 2, 2, 4)) ca[:] = a self.assertEqual(ca.nrows, 3) if common.verbose: print("Second row-->", ca[:, :, 1, ...]) self.assertTrue(allequal(ca[:, :, 1, ...], a[:, :, 1, ...])) class MDAtomNoReopen(MDAtomTestCase): reopen = False class MDAtomReopen(MDAtomTestCase): reopen = True # Test for building very large MD atoms without defaults. Ticket #211. class MDLargeAtomTestCase(common.TempFileMixin, common.PyTablesTestCase): def test01_create(self): "Create a CArray with a very large MD atom." N = 2**16 # 4x larger than maximum object header size (64 KB) ca = self.h5file.create_carray('/', 'test', atom=Int32Atom(shape=N), shape=(1,)) if self.reopen: self._reopen('a') ca = self.h5file.root.test # Check the value if common.verbose: print("First row-->", ca[0]) self.assertTrue(allequal(ca[0], numpy.zeros(N, 'i4'))) class MDLargeAtomNoReopen(MDLargeAtomTestCase): reopen = False class MDLargeAtomReopen(MDLargeAtomTestCase): reopen = True class AccessClosedTestCase(common.TempFileMixin, common.PyTablesTestCase): def setUp(self): super(AccessClosedTestCase, self).setUp() self.array = self.h5file.create_carray(self.h5file.root, 'array', atom=Int32Atom(), shape=(10, 10)) self.array[...] = numpy.zeros((10, 10)) def test_read(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.array.read) def test_getitem(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.array.__getitem__, 0) def test_setitem(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.array.__setitem__, 0, 0) class TestCreateCArrayArgs(common.TempFileMixin, common.PyTablesTestCase): obj = numpy.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) where = '/' name = 'carray' atom = Atom.from_dtype(obj.dtype) shape = obj.shape title = 'title' filters = None chunkshape = (1, 2) byteorder = None createparents = False def test_positional_args_01(self): self.h5file.create_carray(self.where, self.name, self.atom, self.shape, self.title, self.filters, self.chunkshape) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(numpy.zeros_like(self.obj), nparr)) def test_positional_args_02(self): ptarr = self.h5file.create_carray(self.where, self.name, self.atom, self.shape, self.title, self.filters, self.chunkshape) ptarr[...] = self.obj self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(self.obj, nparr)) def test_positional_args_obj(self): self.h5file.create_carray(self.where, self.name, None, None, self.title, self.filters, self.chunkshape, self.byteorder, self.createparents, self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj(self): self.h5file.create_carray(self.where, self.name, title=self.title, chunkshape=self.chunkshape, obj=self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_atom_shape_01(self): ptarr = self.h5file.create_carray(self.where, self.name, title=self.title, chunkshape=self.chunkshape, atom=self.atom, shape=self.shape) ptarr[...] = self.obj self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_atom_shape_02(self): ptarr = self.h5file.create_carray(self.where, self.name, title=self.title, chunkshape=self.chunkshape, atom=self.atom, shape=self.shape) #ptarr[...] = self.obj self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(numpy.zeros_like(self.obj), nparr)) def test_kwargs_obj_atom(self): ptarr = self.h5file.create_carray(self.where, self.name, title=self.title, chunkshape=self.chunkshape, obj=self.obj, atom=self.atom) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj_shape(self): ptarr = self.h5file.create_carray(self.where, self.name, title=self.title, chunkshape=self.chunkshape, obj=self.obj, shape=self.shape) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj_atom_shape(self): ptarr = self.h5file.create_carray(self.where, self.name, title=self.title, chunkshape=self.chunkshape, obj=self.obj, atom=self.atom, shape=self.shape) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj_atom_error(self): atom = Atom.from_dtype(numpy.dtype('complex')) #shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_carray, self.where, self.name, title=self.title, obj=self.obj, atom=atom) def test_kwargs_obj_shape_error(self): #atom = Atom.from_dtype(numpy.dtype('complex')) shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_carray, self.where, self.name, title=self.title, obj=self.obj, shape=shape) def test_kwargs_obj_atom_shape_error_01(self): atom = Atom.from_dtype(numpy.dtype('complex')) #shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_carray, self.where, self.name, title=self.title, obj=self.obj, atom=atom, shape=self.shape) def test_kwargs_obj_atom_shape_error_02(self): #atom = Atom.from_dtype(numpy.dtype('complex')) shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_carray, self.where, self.name, title=self.title, obj=self.obj, atom=self.atom, shape=shape) def test_kwargs_obj_atom_shape_error_03(self): atom = Atom.from_dtype(numpy.dtype('complex')) shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_carray, self.where, self.name, title=self.title, obj=self.obj, atom=atom, shape=shape) #---------------------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() niter = 1 # common.heavy = 1 # uncomment this only for testing purposes # theSuite.addTest(unittest.makeSuite(BasicTestCase)) for n in range(niter): theSuite.addTest(unittest.makeSuite(BasicWriteTestCase)) theSuite.addTest(unittest.makeSuite(BasicWrite2TestCase)) theSuite.addTest(unittest.makeSuite(BasicWrite3TestCase)) theSuite.addTest(unittest.makeSuite(BasicWrite4TestCase)) theSuite.addTest(unittest.makeSuite(BasicWrite5TestCase)) theSuite.addTest(unittest.makeSuite(BasicWrite6TestCase)) theSuite.addTest(unittest.makeSuite(BasicWrite7TestCase)) theSuite.addTest(unittest.makeSuite(BasicWrite8TestCase)) theSuite.addTest(unittest.makeSuite(EmptyCArrayTestCase)) theSuite.addTest(unittest.makeSuite(EmptyCArray2TestCase)) theSuite.addTest(unittest.makeSuite(SlicesCArrayTestCase)) theSuite.addTest(unittest.makeSuite(Slices2CArrayTestCase)) theSuite.addTest(unittest.makeSuite(EllipsisCArrayTestCase)) theSuite.addTest(unittest.makeSuite(Ellipsis2CArrayTestCase)) theSuite.addTest(unittest.makeSuite(Ellipsis3CArrayTestCase)) theSuite.addTest(unittest.makeSuite(ZlibComprTestCase)) theSuite.addTest(unittest.makeSuite(ZlibShuffleTestCase)) theSuite.addTest(unittest.makeSuite(BloscComprTestCase)) theSuite.addTest(unittest.makeSuite(BloscShuffleTestCase)) theSuite.addTest(unittest.makeSuite(BloscFletcherTestCase)) theSuite.addTest(unittest.makeSuite(BloscBloscLZTestCase)) if 'lz4' in tables.blosc_compressor_list(): theSuite.addTest(unittest.makeSuite(BloscLZ4TestCase)) theSuite.addTest(unittest.makeSuite(BloscLZ4HCTestCase)) if 'snappy' in tables.blosc_compressor_list(): theSuite.addTest(unittest.makeSuite(BloscSnappyTestCase)) if 'zlib' in tables.blosc_compressor_list(): theSuite.addTest(unittest.makeSuite(BloscZlibTestCase)) theSuite.addTest(unittest.makeSuite(LZOComprTestCase)) theSuite.addTest(unittest.makeSuite(LZOShuffleTestCase)) theSuite.addTest(unittest.makeSuite(Bzip2ComprTestCase)) theSuite.addTest(unittest.makeSuite(Bzip2ShuffleTestCase)) theSuite.addTest(unittest.makeSuite(FloatTypeTestCase)) theSuite.addTest(unittest.makeSuite(ComplexTypeTestCase)) theSuite.addTest(unittest.makeSuite(StringTestCase)) theSuite.addTest(unittest.makeSuite(String2TestCase)) theSuite.addTest(unittest.makeSuite(StringComprTestCase)) theSuite.addTest(unittest.makeSuite(Int8TestCase)) theSuite.addTest(unittest.makeSuite(Int16TestCase)) theSuite.addTest(unittest.makeSuite(Int32TestCase)) if 'Float16Atom' in globals(): theSuite.addTest(unittest.makeSuite(Float16TestCase)) theSuite.addTest(unittest.makeSuite(Float32TestCase)) theSuite.addTest(unittest.makeSuite(Float64TestCase)) if 'Float96Atom' in globals(): theSuite.addTest(unittest.makeSuite(Float96TestCase)) if 'Float128Atom' in globals(): theSuite.addTest(unittest.makeSuite(Float128TestCase)) theSuite.addTest(unittest.makeSuite(Complex64TestCase)) theSuite.addTest(unittest.makeSuite(Complex128TestCase)) if 'Complex192Atom' in globals(): theSuite.addTest(unittest.makeSuite(Complex192TestCase)) if 'Complex256Atom' in globals(): theSuite.addTest(unittest.makeSuite(Complex256TestCase)) theSuite.addTest(unittest.makeSuite(ComprTestCase)) theSuite.addTest(unittest.makeSuite(OffsetStrideTestCase)) theSuite.addTest(unittest.makeSuite(Fletcher32TestCase)) theSuite.addTest(unittest.makeSuite(AllFiltersTestCase)) theSuite.addTest(unittest.makeSuite(ReadOutArgumentTests)) theSuite.addTest(unittest.makeSuite( SizeOnDiskInMemoryPropertyTestCase)) theSuite.addTest(unittest.makeSuite(CloseCopyTestCase)) theSuite.addTest(unittest.makeSuite(OpenCopyTestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex1TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex2TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex3TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex4TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex5TestCase)) theSuite.addTest(unittest.makeSuite(BigArrayTestCase)) theSuite.addTest(unittest.makeSuite(DfltAtomNoReopen)) theSuite.addTest(unittest.makeSuite(DfltAtomReopen)) theSuite.addTest(unittest.makeSuite(AtomDefaultReprNoReopen)) theSuite.addTest(unittest.makeSuite(AtomDefaultReprReopen)) theSuite.addTest(unittest.makeSuite(TruncateTestCase)) theSuite.addTest(unittest.makeSuite(MDAtomNoReopen)) theSuite.addTest(unittest.makeSuite(MDAtomReopen)) theSuite.addTest(unittest.makeSuite(MDLargeAtomNoReopen)) theSuite.addTest(unittest.makeSuite(MDLargeAtomReopen)) theSuite.addTest(unittest.makeSuite(AccessClosedTestCase)) theSuite.addTest(unittest.makeSuite(TestCreateCArrayArgs)) if common.heavy: theSuite.addTest(unittest.makeSuite(Slices3CArrayTestCase)) theSuite.addTest(unittest.makeSuite(Slices4CArrayTestCase)) theSuite.addTest(unittest.makeSuite(Ellipsis4CArrayTestCase)) theSuite.addTest(unittest.makeSuite(Ellipsis5CArrayTestCase)) theSuite.addTest(unittest.makeSuite(Ellipsis6CArrayTestCase)) theSuite.addTest(unittest.makeSuite(Ellipsis7CArrayTestCase)) theSuite.addTest(unittest.makeSuite(MD3WriteTestCase)) theSuite.addTest(unittest.makeSuite(MD5WriteTestCase)) theSuite.addTest(unittest.makeSuite(MD6WriteTestCase)) theSuite.addTest(unittest.makeSuite(MD7WriteTestCase)) theSuite.addTest(unittest.makeSuite(MD10WriteTestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex6TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex7TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex8TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex9TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex10TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex11TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex12TestCase)) theSuite.addTest(unittest.makeSuite(Rows64bitsTestCase1)) theSuite.addTest(unittest.makeSuite(Rows64bitsTestCase2)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## End: PyTables-v.3.1.1/tables/tests/test_create.py000066400000000000000000003051351231437614300210060ustar00rootroot00000000000000# -*- coding: utf-8 -*- """This test unit checks object creation funtions, like open_file, create_table, create_array or create_group. It also checks: - name identifiers in tree objects - title character limit for objects (255) - limit in number in table fields (255) """ from __future__ import print_function import os import sys import hashlib import unittest import tempfile import warnings import numpy from tables import * # important objects to test from tables import Group, Leaf, Table, Array, hdf5_version from tables.tests import common from tables.parameters import MAX_COLUMNS from tables.hdf5extension import HAVE_DIRECT_DRIVER, HAVE_WINDOWS_DRIVER from tables.utils import quantize import tables # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup class Record(IsDescription): var1 = StringCol(itemsize=4) # 4-character String var2 = IntCol() # integer var3 = Int16Col() # short integer var4 = FloatCol() # double (double-precision) var5 = Float32Col() # float (single-precision) class createTestCase(unittest.TestCase): file = "test.h5" title = "This is the table title" expectedrows = 100 maxshort = 2 ** 15 maxint = 2147483648 # (2 ** 31) compress = 0 def setUp(self): # Create an instance of HDF5 Table self.fileh = open_file(self.file, mode="w") self.root = self.fileh.root # Create a table object self.table = self.fileh.create_table(self.root, 'atable', Record, "Table title") # Create an array object self.array = self.fileh.create_array(self.root, 'anarray', [1], "Array title") # Create a group object self.group = self.fileh.create_group(self.root, 'agroup', "Group title") def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test00_isClass(self): """Testing table creation.""" self.assertTrue(isinstance(self.table, Table)) self.assertTrue(isinstance(self.array, Array)) self.assertTrue(isinstance(self.array, Leaf)) self.assertTrue(isinstance(self.group, Group)) def test01_overwriteNode(self): """Checking protection against node overwriting.""" try: self.array = self.fileh.create_array(self.root, 'anarray', [1], "Array title") except NodeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NameError was catched!") print(value) else: self.fail("expected a NodeError") def test02_syntaxname(self): """Checking syntax in object tree names.""" # Now, try to attach an array to the object tree with # a not allowed Python variable name warnings.filterwarnings("error", category=NaturalNameWarning) try: self.array = self.fileh.create_array(self.root, ' array', [1], "Array title") except NaturalNameWarning: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NaturalNameWarning was catched!") print(value) else: self.fail("expected a NaturalNameWarning") # another name error try: self.array = self.fileh.create_array(self.root, '$array', [1], "Array title") except NaturalNameWarning: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NaturalNameWarning was catched!") print(value) else: self.fail("expected a NaturalNameWarning") # Finally, test a reserved word try: self.array = self.fileh.create_array(self.root, 'for', [1], "Array title") except NaturalNameWarning: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NaturalNameWarning was catched!") print(value) else: self.fail("expected a NaturalNameWarning") # Reset the warning warnings.filterwarnings("default", category=NaturalNameWarning) def test03a_titleAttr(self): """Checking the self.title attr in nodes.""" # Close the opened file to destroy the object tree self.fileh.close() # Open the file again to re-create the objects self.fileh = open_file(self.file, "r") # Now, test that self.title exists and is correct in all the nodes self.assertEqual(self.fileh.root.agroup._v_title, "Group title") self.assertEqual(self.fileh.root.atable.title, "Table title") self.assertEqual(self.fileh.root.anarray.title, "Array title") def test03b_titleLength(self): """Checking large title character length limit (1023)""" titlelength = 1023 # Try to put a very long title on a group object group = self.fileh.create_group(self.root, 'group', "t" * titlelength) self.assertEqual(group._v_title, "t" * titlelength) self.assertEqual(group._f_getattr('TITLE'), "t" * titlelength) # Now, try with a table object table = self.fileh.create_table(self.root, 'table', Record, "t" * titlelength) self.assertEqual(table.title, "t" * titlelength) self.assertEqual(table.get_attr("TITLE"), "t" * titlelength) # Finally, try with an Array object arr = self.fileh.create_array(self.root, 'arr', [1], "t" * titlelength) self.assertEqual(arr.title, "t" * titlelength) self.assertEqual(arr.get_attr("TITLE"), "t" * titlelength) def test04_maxFields(self): "Checking a large number of fields in tables" # The number of fields for a table varnumber = MAX_COLUMNS varnames = [] for i in range(varnumber): varnames.append('int%d' % i) # Build a dictionary with the types as values and varnames as keys recordDict = {} i = 0 for varname in varnames: recordDict[varname] = Col.from_type("int32", dflt=1, pos=i) i += 1 # Append this entry to indicate the alignment! recordDict['_v_align'] = "=" table = self.fileh.create_table(self.root, 'table', recordDict, "MetaRecord instance") row = table.row listrows = [] # Write 10 records for j in range(10): rowlist = [] for i in range(len(table.colnames)): row[varnames[i]] = i * j rowlist.append(i * j) row.append() listrows.append(tuple(rowlist)) # write data on disk table.flush() # Read all the data as a list listout = table.read().tolist() # Compare the input rowlist and output row list. They should # be equal. if common.verbose: print("Original row list:", listrows[-1]) print("Retrieved row list:", listout[-1]) self.assertEqual(listrows, listout) # The next limitation has been released. A warning is still there, though def test05_maxFieldsExceeded(self): "Checking an excess of the maximum number of fields in tables" # The number of fields for a table varnumber = MAX_COLUMNS + 1 varnames = [] for i in range(varnumber): varnames.append('int%d' % i) # Build a dictionary with the types as values and varnames as keys recordDict = {} i = 0 for varname in varnames: recordDict[varname] = Col.from_type("int32", dflt=1) i += 1 # Now, create a table with this record object # This way of creating node objects has been deprecated # table = Table(recordDict, "MetaRecord instance") # Attach the table to object tree warnings.filterwarnings("error", category=PerformanceWarning) # Here, a PerformanceWarning should be raised! try: self.fileh.create_table(self.root, 'table', recordDict, "MetaRecord instance") except PerformanceWarning: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next PerformanceWarning was catched!") print(value) else: self.fail("expected an PerformanceWarning") # Reset the warning warnings.filterwarnings("default", category=PerformanceWarning) # The next limitation has been released def _test06_maxColumnNameLengthExceeded(self): "Checking an excess (256) of the maximum length in column names" # Build a dictionary with the types as values and varnames as keys recordDict = {} recordDict["a" * 255] = IntCol(dflt=1) recordDict["b" * 256] = IntCol(dflt=1) # Should trigger a ValueError # Now, create a table with this record object # This way of creating node objects has been deprecated table = Table(recordDict, "MetaRecord instance") self.assertTrue(table is not None) # Attach the table to object tree # Here, ValueError should be raised! try: self.fileh.create_table(self.root, 'table', recordDict, "MetaRecord instance") except ValueError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next ValueError was catched!") print(value) else: self.fail("expected an ValueError") def test06_noMaxColumnNameLength(self): "Checking unlimited length in column names" # Build a dictionary with the types as values and varnames as keys recordDict = {} recordDict["a" * 255] = IntCol(dflt=1, pos=0) recordDict["b" * 1024] = IntCol(dflt=1, pos=1) # Should work well # Attach the table to object tree # Here, IndexError should be raised! table = self.fileh.create_table(self.root, 'table', recordDict, "MetaRecord instance") self.assertEqual(table.colnames[0], "a" * 255) self.assertEqual(table.colnames[1], "b" * 1024) class Record2(IsDescription): var1 = StringCol(itemsize=4) # 4-character String var2 = IntCol() # integer var3 = Int16Col() # short integer class FiltersTreeTestCase(unittest.TestCase): title = "A title" nrows = 10 def setUp(self): # Create a temporary file self.file = tempfile.mktemp(".h5") # Create an instance of HDF5 Table self.h5file = open_file(self.file, "w", filters=self.filters) self.populateFile() def populateFile(self): group = self.h5file.root # Create a tree with three levels of depth for j in range(5): # Create a table table = self.h5file.create_table(group, 'table1', Record2, title=self.title, filters=None) # Get the record object associated with the new table d = table.row # Fill the table for i in xrange(self.nrows): d['var1'] = '%04d' % (self.nrows - i) d['var2'] = i d['var3'] = i * 2 d.append() # This injects the Record values # Flush the buffer for this table table.flush() # Create a couple of arrays in each group var1List = [x['var1'] for x in table.iterrows()] var3List = [x['var3'] for x in table.iterrows()] self.h5file.create_array(group, 'array1', var1List, "col 1") self.h5file.create_array(group, 'array2', var3List, "col 3") # Create a couple of EArrays as well ea1 = self.h5file.create_earray(group, 'earray1', StringAtom(itemsize=4), (0,), "col 1") ea2 = self.h5file.create_earray(group, 'earray2', Int16Atom(), (0,), "col 3") # And fill them with some values ea1.append(var1List) ea2.append(var3List) # Finally a couple of VLArrays too vla1 = self.h5file.create_vlarray(group, 'vlarray1', StringAtom(itemsize=4), "col 1") vla2 = self.h5file.create_vlarray(group, 'vlarray2', Int16Atom(), "col 3") # And fill them with some values vla1.append(var1List) vla2.append(var3List) # Create a new group (descendant of group) if j == 1: # The second level group2 = self.h5file.create_group(group, 'group' + str(j), filters=self.gfilters) elif j == 2: # third level group2 = self.h5file.create_group(group, 'group' + str(j)) else: # The rest of levels group2 = self.h5file.create_group(group, 'group' + str(j), filters=self.filters) # Iterate over this new group (group2) group = group2 def tearDown(self): # Close the file if self.h5file.isopen: self.h5file.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test00_checkFilters(self): "Checking inheritance of filters on trees (open file version)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00_checkFilters..." % self.__class__.__name__) # First level check if common.verbose: print("Test filter:", repr(self.filters)) print("Filters in file:", repr(self.h5file.filters)) if self.filters is None: filters = Filters() else: filters = self.filters self.assertEqual(repr(filters), repr(self.h5file.filters)) # The next nodes have to have the same filter properties as # self.filters nodelist = [ '/table1', '/group0/earray1', '/group0/vlarray1', '/group0', ] for node in nodelist: object = self.h5file.get_node(node) if isinstance(object, Group): self.assertEqual(repr(filters), repr(object._v_filters)) else: self.assertEqual(repr(filters), repr(object.filters)) # Second and third level check group1 = self.h5file.root.group0.group1 if self.gfilters is None: if self.filters is None: gfilters = Filters() else: gfilters = self.filters else: gfilters = self.gfilters if common.verbose: print("Test gfilter:", repr(gfilters)) print("Filters in file:", repr(group1._v_filters)) self.assertEqual(repr(gfilters), repr(group1._v_filters)) # The next nodes have to have the same filter properties as # gfilters nodelist = ['/group0/group1', '/group0/group1/earray1', '/group0/group1/vlarray1', '/group0/group1/table1', '/group0/group1/group2/table1'] for node in nodelist: object = self.h5file.get_node(node) if isinstance(object, Group): self.assertEqual(repr(gfilters), repr(object._v_filters)) else: self.assertEqual(repr(gfilters), repr(object.filters)) # Fourth and fifth level check if self.filters is None: # If None, the filters are inherited! if self.gfilters is None: filters = Filters() else: filters = self.gfilters else: filters = self.filters group3 = self.h5file.root.group0.group1.group2.group3 if common.verbose: print("Test filter:", repr(filters)) print("Filters in file:", repr(group3._v_filters)) self.assertEqual(repr(filters), repr(group3._v_filters)) # The next nodes have to have the same filter properties as # self.filter nodelist = ['/group0/group1/group2/group3', '/group0/group1/group2/group3/earray1', '/group0/group1/group2/group3/vlarray1', '/group0/group1/group2/group3/table1', '/group0/group1/group2/group3/group4'] for node in nodelist: object = self.h5file.get_node(node) if isinstance(object, Group): self.assertEqual(repr(filters), repr(object._v_filters)) else: self.assertEqual(repr(filters), repr(object.filters)) # Checking the special case for Arrays in which the compression # should always be the empty Filter() # The next nodes have to have the same filter properties as # Filter() nodelist = ['/array1', '/group0/array1', '/group0/group1/array1', '/group0/group1/group2/array1', '/group0/group1/group2/group3/array1'] for node in nodelist: object = self.h5file.get_node(node) self.assertEqual(repr(Filters()), repr(object.filters)) def test01_checkFilters(self): "Checking inheritance of filters on trees (close file version)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_checkFilters..." % self.__class__.__name__) # Close the file self.h5file.close() # And open it again self.h5file = open_file(self.file, "r") # First level check if self.filters is None: filters = Filters() else: filters = self.filters if common.verbose: print("Test filter:", repr(filters)) print("Filters in file:", repr(self.h5file.filters)) self.assertEqual(repr(filters), repr(self.h5file.filters)) # The next nodes have to have the same filter properties as # self.filters nodelist = [ '/table1', '/group0/earray1', '/group0/vlarray1', '/group0', ] for node in nodelist: object_ = self.h5file.get_node(node) if isinstance(object_, Group): self.assertEqual(repr(filters), repr(object_._v_filters)) else: self.assertEqual(repr(filters), repr(object_.filters)) # Second and third level check group1 = self.h5file.root.group0.group1 if self.gfilters is None: if self.filters is None: gfilters = Filters() else: gfilters = self.filters else: gfilters = self.gfilters if common.verbose: print("Test filter:", repr(gfilters)) print("Filters in file:", repr(group1._v_filters)) repr(gfilters) == repr(group1._v_filters) # The next nodes have to have the same filter properties as # gfilters nodelist = ['/group0/group1', '/group0/group1/earray1', '/group0/group1/vlarray1', '/group0/group1/table1', '/group0/group1/group2/table1'] for node in nodelist: object_ = self.h5file.get_node(node) if isinstance(object_, Group): self.assertEqual(repr(gfilters), repr(object_._v_filters)) else: self.assertEqual(repr(gfilters), repr(object_.filters)) # Fourth and fifth level check if self.filters is None: if self.gfilters is None: filters = Filters() else: filters = self.gfilters else: filters = self.filters group3 = self.h5file.root.group0.group1.group2.group3 if common.verbose: print("Test filter:", repr(filters)) print("Filters in file:", repr(group3._v_filters)) repr(filters) == repr(group3._v_filters) # The next nodes have to have the same filter properties as # self.filters nodelist = ['/group0/group1/group2/group3', '/group0/group1/group2/group3/earray1', '/group0/group1/group2/group3/vlarray1', '/group0/group1/group2/group3/table1', '/group0/group1/group2/group3/group4'] for node in nodelist: object = self.h5file.get_node(node) if isinstance(object, Group): self.assertEqual(repr(filters), repr(object._v_filters)) else: self.assertEqual(repr(filters), repr(object.filters)) # Checking the special case for Arrays in which the compression # should always be the empty Filter() # The next nodes have to have the same filter properties as # Filter() nodelist = ['/array1', '/group0/array1', '/group0/group1/array1', '/group0/group1/group2/array1', '/group0/group1/group2/group3/array1'] for node in nodelist: object = self.h5file.get_node(node) self.assertEqual(repr(Filters()), repr(object.filters)) class FiltersCase1(FiltersTreeTestCase): filters = Filters() gfilters = Filters(complevel=1) class FiltersCase2(FiltersTreeTestCase): filters = Filters(complevel=1, complib="bzip2") gfilters = Filters(complevel=1) class FiltersCase3(FiltersTreeTestCase): filters = Filters(shuffle=True, complib="zlib") gfilters = Filters(complevel=1, shuffle=False, complib="lzo") class FiltersCase4(FiltersTreeTestCase): filters = Filters(shuffle=True) gfilters = Filters(complevel=1, shuffle=False) class FiltersCase5(FiltersTreeTestCase): filters = Filters(fletcher32=True) gfilters = Filters(complevel=1, shuffle=False) class FiltersCase6(FiltersTreeTestCase): filters = None gfilters = Filters(complevel=1, shuffle=False) class FiltersCase7(FiltersTreeTestCase): filters = Filters(complevel=1) gfilters = None class FiltersCase8(FiltersTreeTestCase): filters = None gfilters = None class FiltersCase9(FiltersTreeTestCase): filters = Filters(shuffle=True, complib="zlib") gfilters = Filters(complevel=5, shuffle=True, complib="bzip2") class FiltersCase10(FiltersTreeTestCase): filters = Filters(shuffle=False, complevel=1, complib="blosc") gfilters = Filters(complevel=5, shuffle=True, complib="blosc") class FiltersCaseBloscBloscLZ(FiltersTreeTestCase): filters = Filters(shuffle=False, complevel=1, complib="blosc:blosclz") gfilters = Filters(complevel=5, shuffle=True, complib="blosc:blosclz") class FiltersCaseBloscLZ4(FiltersTreeTestCase): filters = Filters(shuffle=False, complevel=1, complib="blosc:lz4") gfilters = Filters(complevel=5, shuffle=True, complib="blosc:lz4") class FiltersCaseBloscLZ4HC(FiltersTreeTestCase): filters = Filters(shuffle=False, complevel=1, complib="blosc:lz4hc") gfilters = Filters(complevel=5, shuffle=True, complib="blosc:lz4hc") class FiltersCaseBloscSnappy(FiltersTreeTestCase): filters = Filters(shuffle=False, complevel=1, complib="blosc:snappy") gfilters = Filters(complevel=5, shuffle=True, complib="blosc:snappy") class FiltersCaseBloscZlib(FiltersTreeTestCase): filters = Filters(shuffle=False, complevel=1, complib="blosc:zlib") gfilters = Filters(complevel=5, shuffle=True, complib="blosc:zlib") class CopyGroupTestCase(unittest.TestCase): title = "A title" nrows = 10 def setUp(self): # Create a temporary file self.file = tempfile.mktemp(".h5") self.file2 = tempfile.mktemp(".h5") # Create the source file self.h5file = open_file(self.file, "w") # Create the destination self.h5file2 = open_file(self.file2, "w") self.populateFile() def populateFile(self): group = self.h5file.root # Add some user attrs: group._v_attrs.attr1 = "an string for root group" group._v_attrs.attr2 = 124 # Create a tree for j in range(5): for i in range(2): # Create a new group (brother of group) group2 = self.h5file.create_group(group, 'bgroup' + str(i), filters=None) # Create a table table = self.h5file.create_table(group2, 'table1', Record2, title=self.title, filters=None) # Get the record object associated with the new table d = table.row # Fill the table for i in xrange(self.nrows): d['var1'] = '%04d' % (self.nrows - i) d['var2'] = i d['var3'] = i * 2 d.append() # This injects the Record values # Flush the buffer for this table table.flush() # Add some user attrs: table.attrs.attr1 = "an string" table.attrs.attr2 = 234 # Create a couple of arrays in each group var1List = [x['var1'] for x in table.iterrows()] var3List = [x['var3'] for x in table.iterrows()] self.h5file.create_array(group2, 'array1', var1List, "col 1") self.h5file.create_array(group2, 'array2', var3List, "col 3") # Create a couple of EArrays as well ea1 = self.h5file.create_earray(group2, 'earray1', StringAtom(itemsize=4), (0,), "col 1") ea2 = self.h5file.create_earray(group2, 'earray2', Int16Atom(), (0,), "col 3") # Add some user attrs: ea1.attrs.attr1 = "an string for earray" ea2.attrs.attr2 = 123 # And fill them with some values ea1.append(var1List) ea2.append(var3List) # Create a new group (descendant of group) group3 = self.h5file.create_group(group, 'group' + str(j), filters=None) # Iterate over this new group (group3) group = group3 # Add some user attrs: group._v_attrs.attr1 = "an string for group" group._v_attrs.attr2 = 124 def tearDown(self): # Close the file if self.h5file.isopen: self.h5file.close() if self.h5file2.isopen: self.h5file2.close() os.remove(self.file) os.remove(self.file2) common.cleanup(self) #---------------------------------------- def test00_nonRecursive(self): "Checking non-recursive copy of a Group" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00_nonRecursive..." % self.__class__.__name__) # Copy a group non-recursively srcgroup = self.h5file.root.group0.group1 # srcgroup._f_copy_children(self.h5file2.root, # recursive=False, # filters=self.filters) self.h5file.copy_children(srcgroup, self.h5file2.root, recursive=False, filters=self.filters) if self.close: # Close the destination file self.h5file2.close() # And open it again self.h5file2 = open_file(self.file2, "r") # Check that the copy has been done correctly dstgroup = self.h5file2.root nodelist1 = srcgroup._v_children.keys() nodelist2 = dstgroup._v_children.keys() # Sort the lists nodelist1.sort() nodelist2.sort() if common.verbose: print("The origin node list -->", nodelist1) print("The copied node list -->", nodelist2) self.assertEqual(srcgroup._v_nchildren, dstgroup._v_nchildren) self.assertEqual(nodelist1, nodelist2) def test01_nonRecursiveAttrs(self): "Checking non-recursive copy of a Group (attributes copied)" if common.verbose: print('\n', '-=' * 30) print(("Running %s.test01_nonRecursiveAttrs..." % self.__class__.__name__)) # Copy a group non-recursively with attrs srcgroup = self.h5file.root.group0.group1 srcgroup._f_copy_children(self.h5file2.root, recursive=False, filters=self.filters, copyuserattrs=1) if self.close: # Close the destination file self.h5file2.close() # And open it again self.h5file2 = open_file(self.file2, "r") # Check that the copy has been done correctly dstgroup = self.h5file2.root for srcnode in srcgroup: dstnode = getattr(dstgroup, srcnode._v_name) if isinstance(srcnode, Group): srcattrs = srcnode._v_attrs srcattrskeys = srcattrs._f_list("all") dstattrs = dstnode._v_attrs dstattrskeys = dstattrs._f_list("all") else: srcattrs = srcnode.attrs srcattrskeys = srcattrs._f_list("all") dstattrs = dstnode.attrs dstattrskeys = dstattrs._f_list("all") # Filters may differ, do not take into account if self.filters is not None: dstattrskeys.remove('FILTERS') # These lists should already be ordered if common.verbose: print("srcattrskeys for node %s: %s" % (srcnode._v_name, srcattrskeys)) print("dstattrskeys for node %s: %s" % (dstnode._v_name, dstattrskeys)) self.assertEqual(srcattrskeys, dstattrskeys) if common.verbose: print("The attrs names has been copied correctly") # Now, for the contents of attributes for srcattrname in srcattrskeys: srcattrvalue = str(getattr(srcattrs, srcattrname)) dstattrvalue = str(getattr(dstattrs, srcattrname)) self.assertEqual(srcattrvalue, dstattrvalue) if self.filters is not None: self.assertEqual(dstattrs.FILTERS, self.filters) if common.verbose: print("The attrs contents has been copied correctly") def test02_Recursive(self): "Checking recursive copy of a Group" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_Recursive..." % self.__class__.__name__) # Create the destination node group = self.h5file2.root for groupname in self.dstnode.split("/"): if groupname: group = self.h5file2.create_group(group, groupname) dstgroup = self.h5file2.get_node(self.dstnode) # Copy a group non-recursively srcgroup = self.h5file.get_node(self.srcnode) self.h5file.copy_children(srcgroup, dstgroup, recursive=True, filters=self.filters) lenSrcGroup = len(srcgroup._v_pathname) if lenSrcGroup == 1: lenSrcGroup = 0 # Case where srcgroup == "/" if self.close: # Close the destination file self.h5file2.close() # And open it again self.h5file2 = open_file(self.file2, "r") dstgroup = self.h5file2.get_node(self.dstnode) # Check that the copy has been done correctly lenDstGroup = len(dstgroup._v_pathname) if lenDstGroup == 1: lenDstGroup = 0 # Case where dstgroup == "/" first = 1 nodelist1 = [] for node in srcgroup._f_walknodes(): if first: # skip the first group first = 0 continue nodelist1.append(node._v_pathname[lenSrcGroup:]) first = 1 nodelist2 = [] for node in dstgroup._f_walknodes(): if first: # skip the first group first = 0 continue nodelist2.append(node._v_pathname[lenDstGroup:]) if common.verbose: print("The origin node list -->", nodelist1) print("The copied node list -->", nodelist2) self.assertEqual(nodelist1, nodelist2) def test03_RecursiveFilters(self): "Checking recursive copy of a Group (cheking Filters)" if common.verbose: print('\n', '-=' * 30) print(("Running %s.test03_RecursiveFilters..." % self.__class__.__name__)) # Create the destination node group = self.h5file2.root for groupname in self.dstnode.split("/"): if groupname: group = self.h5file2.create_group(group, groupname) dstgroup = self.h5file2.get_node(self.dstnode) # Copy a group non-recursively srcgroup = self.h5file.get_node(self.srcnode) srcgroup._f_copy_children(dstgroup, recursive=True, filters=self.filters) lenSrcGroup = len(srcgroup._v_pathname) if lenSrcGroup == 1: lenSrcGroup = 0 # Case where srcgroup == "/" if self.close: # Close the destination file self.h5file2.close() # And open it again self.h5file2 = open_file(self.file2, "r") dstgroup = self.h5file2.get_node(self.dstnode) # Check that the copy has been done correctly lenDstGroup = len(dstgroup._v_pathname) if lenDstGroup == 1: lenDstGroup = 0 # Case where dstgroup == "/" first = 1 nodelist1 = {} for node in srcgroup._f_walknodes(): if first: # skip the first group first = 0 continue nodelist1[node._v_name] = node._v_pathname[lenSrcGroup:] first = 1 for node in dstgroup._f_walknodes(): if first: # skip the first group first = 0 continue if isinstance(node, Group): repr(node._v_filters) == repr(nodelist1[node._v_name]) else: repr(node.filters) == repr(nodelist1[node._v_name]) class CopyGroupCase1(CopyGroupTestCase): close = 0 filters = None srcnode = '/group0/group1' dstnode = '/' class CopyGroupCase2(CopyGroupTestCase): close = 1 filters = None srcnode = '/group0/group1' dstnode = '/' class CopyGroupCase3(CopyGroupTestCase): close = 0 filters = None srcnode = '/group0' dstnode = '/group2/group3' class CopyGroupCase4(CopyGroupTestCase): close = 1 filters = Filters(complevel=1) srcnode = '/group0' dstnode = '/group2/group3' class CopyGroupCase5(CopyGroupTestCase): close = 0 filters = Filters() srcnode = '/' dstnode = '/group2/group3' class CopyGroupCase6(CopyGroupTestCase): close = 1 filters = Filters(fletcher32=True) srcnode = '/group0' dstnode = '/group2/group3' class CopyGroupCase7(CopyGroupTestCase): close = 0 filters = Filters(complevel=1, shuffle=False) srcnode = '/' dstnode = '/' class CopyGroupCase8(CopyGroupTestCase): close = 1 filters = Filters(complevel=1, complib="lzo") srcnode = '/' dstnode = '/' class CopyFileTestCase(unittest.TestCase): title = "A title" nrows = 10 def setUp(self): # Create a temporary file self.file = tempfile.mktemp(".h5") self.file2 = tempfile.mktemp(".h5") # Create the source file self.h5file = open_file(self.file, "w") self.populateFile() def populateFile(self): group = self.h5file.root # Add some user attrs: group._v_attrs.attr1 = "an string for root group" group._v_attrs.attr2 = 124 # Create a tree for j in range(5): for i in range(2): # Create a new group (brother of group) group2 = self.h5file.create_group(group, 'bgroup' + str(i), filters=None) # Create a table table = self.h5file.create_table(group2, 'table1', Record2, title=self.title, filters=None) # Get the record object associated with the new table d = table.row # Fill the table for i in xrange(self.nrows): d['var1'] = '%04d' % (self.nrows - i) d['var2'] = i d['var3'] = i * 2 d.append() # This injects the Record values # Flush the buffer for this table table.flush() # Add some user attrs: table.attrs.attr1 = "an string" table.attrs.attr2 = 234 # Create a couple of arrays in each group var1List = [x['var1'] for x in table.iterrows()] var3List = [x['var3'] for x in table.iterrows()] self.h5file.create_array(group2, 'array1', var1List, "col 1") self.h5file.create_array(group2, 'array2', var3List, "col 3") # Create a couple of EArrays as well ea1 = self.h5file.create_earray(group2, 'earray1', StringAtom(itemsize=4), (0,), "col 1") ea2 = self.h5file.create_earray(group2, 'earray2', Int16Atom(), (0,), "col 3") # Add some user attrs: ea1.attrs.attr1 = "an string for earray" ea2.attrs.attr2 = 123 # And fill them with some values ea1.append(var1List) ea2.append(var3List) # Create a new group (descendant of group) group3 = self.h5file.create_group(group, 'group' + str(j), filters=None) # Iterate over this new group (group3) group = group3 # Add some user attrs: group._v_attrs.attr1 = "an string for group" group._v_attrs.attr2 = 124 def tearDown(self): # Close the file if self.h5file.isopen: self.h5file.close() if hasattr(self, 'h5file2') and self.h5file2.isopen: self.h5file2.close() os.remove(self.file) if hasattr(self, 'file2') and os.path.exists(self.file2): os.remove(self.file2) common.cleanup(self) #---------------------------------------- def test00_overwrite(self): "Checking copy of a File (overwriting file)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00_overwrite..." % self.__class__.__name__) # Create a temporary file file2h = open(self.file2, "w") file2h.close() # Copy the file to the destination self.h5file.copy_file(self.file2, title=self.title, overwrite=1, copyuserattrs=0, filters=None) # Close the original file, if needed if self.close: self.h5file.close() # re-open it self.h5file = open_file(self.file, "r") # ...and open the destination file self.h5file2 = open_file(self.file2, "r") # Check that the copy has been done correctly srcgroup = self.h5file.root dstgroup = self.h5file2.root nodelist1 = srcgroup._v_children.keys() nodelist2 = dstgroup._v_children.keys() # Sort the lists nodelist1.sort() nodelist2.sort() if common.verbose: print("The origin node list -->", nodelist1) print("The copied node list -->", nodelist2) self.assertEqual(srcgroup._v_nchildren, dstgroup._v_nchildren) self.assertEqual(nodelist1, nodelist2) self.assertEqual(self.h5file2.title, self.title) def test00a_srcdstequal(self): "Checking copy of a File (srcfile == dstfile)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00a_srcdstequal..." % self.__class__.__name__) # Copy the file to the destination self.assertRaises(IOError, self.h5file.copy_file, self.h5file.filename) def test00b_firstclass(self): "Checking copy of a File (first-class function)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00b_firstclass..." % self.__class__.__name__) # Close the temporary file self.h5file.close() # Copy the file to the destination copy_file(self.file, self.file2, title=self.title, copyuserattrs=0, filters=None, overwrite=1) # ...and open the source and destination file self.h5file = open_file(self.file, "r") self.h5file2 = open_file(self.file2, "r") # Check that the copy has been done correctly srcgroup = self.h5file.root dstgroup = self.h5file2.root nodelist1 = srcgroup._v_children.keys() nodelist2 = dstgroup._v_children.keys() # Sort the lists nodelist1.sort() nodelist2.sort() if common.verbose: print("The origin node list -->", nodelist1) print("The copied node list -->", nodelist2) self.assertEqual(srcgroup._v_nchildren, dstgroup._v_nchildren) self.assertEqual(nodelist1, nodelist2) self.assertEqual(self.h5file2.title, self.title) def test01_copy(self): "Checking copy of a File (attributes not copied)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_copy..." % self.__class__.__name__) # Copy the file to the destination self.h5file.copy_file(self.file2, title=self.title, copyuserattrs=0, filters=self.filters) # Close the original file, if needed if self.close: self.h5file.close() # re-open it self.h5file = open_file(self.file, "r") # ...and open the destination file self.h5file2 = open_file(self.file2, "r") # Check that the copy has been done correctly srcgroup = self.h5file.root dstgroup = self.h5file2.root nodelist1 = srcgroup._v_children.keys() nodelist2 = dstgroup._v_children.keys() # Sort the lists nodelist1.sort() nodelist2.sort() if common.verbose: print("The origin node list -->", nodelist1) print("The copied node list -->", nodelist2) self.assertEqual(srcgroup._v_nchildren, dstgroup._v_nchildren) self.assertEqual(nodelist1, nodelist2) # print("_v_attrnames-->", self.h5file2.root._v_attrs._v_attrnames) # print("--> <%s,%s>" % (self.h5file2.title, self.title)) self.assertEqual(self.h5file2.title, self.title) # Check that user attributes has not been copied for srcnode in srcgroup: dstnode = getattr(dstgroup, srcnode._v_name) srcattrs = srcnode._v_attrs srcattrskeys = srcattrs._f_list("sys") dstattrs = dstnode._v_attrs dstattrskeys = dstattrs._f_list("all") # Filters may differ, do not take into account if self.filters is not None: dstattrskeys.remove('FILTERS') # These lists should already be ordered if common.verbose: print("srcattrskeys for node %s: %s" % (srcnode._v_name, srcattrskeys)) print("dstattrskeys for node %s: %s" % (dstnode._v_name, dstattrskeys)) self.assertEqual(srcattrskeys, dstattrskeys) if common.verbose: print("The attrs names has been copied correctly") # Now, for the contents of attributes for srcattrname in srcattrskeys: srcattrvalue = str(getattr(srcattrs, srcattrname)) dstattrvalue = str(getattr(dstattrs, srcattrname)) self.assertEqual(srcattrvalue, dstattrvalue) if self.filters is not None: self.assertEqual(dstattrs.FILTERS, self.filters) if common.verbose: print("The attrs contents has been copied correctly") def test02_Attrs(self): "Checking copy of a File (attributes copied)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_Attrs..." % self.__class__.__name__) # Copy the file to the destination self.h5file.copy_file(self.file2, title=self.title, copyuserattrs=1, filters=self.filters) # Close the original file, if needed if self.close: self.h5file.close() # re-open it self.h5file = open_file(self.file, "r") # ...and open the destination file self.h5file2 = open_file(self.file2, "r") # Check that the copy has been done correctly srcgroup = self.h5file.root dstgroup = self.h5file2.root for srcnode in srcgroup: dstnode = getattr(dstgroup, srcnode._v_name) srcattrs = srcnode._v_attrs srcattrskeys = srcattrs._f_list("all") dstattrs = dstnode._v_attrs dstattrskeys = dstattrs._f_list("all") # These lists should already be ordered if common.verbose: print("srcattrskeys for node %s: %s" % (srcnode._v_name, srcattrskeys)) print("dstattrskeys for node %s: %s" % (dstnode._v_name, dstattrskeys)) # Filters may differ, do not take into account if self.filters is not None: dstattrskeys.remove('FILTERS') self.assertEqual(srcattrskeys, dstattrskeys) if common.verbose: print("The attrs names has been copied correctly") # Now, for the contents of attributes for srcattrname in srcattrskeys: srcattrvalue = str(getattr(srcattrs, srcattrname)) dstattrvalue = str(getattr(dstattrs, srcattrname)) self.assertEqual(srcattrvalue, dstattrvalue) if self.filters is not None: self.assertEqual(dstattrs.FILTERS, self.filters) if common.verbose: print("The attrs contents has been copied correctly") class CopyFileCase1(CopyFileTestCase): close = 0 title = "A new title" filters = None class CopyFileCase2(CopyFileTestCase): close = 1 title = "A new title" filters = None class CopyFileCase3(CopyFileTestCase): close = 0 title = "A new title" filters = Filters(complevel=1) class CopyFileCase4(CopyFileTestCase): close = 1 title = "A new title" filters = Filters(complevel=1) class CopyFileCase5(CopyFileTestCase): close = 0 title = "A new title" filters = Filters(fletcher32=True) class CopyFileCase6(CopyFileTestCase): close = 1 title = "A new title" filters = Filters(fletcher32=True) class CopyFileCase7(CopyFileTestCase): close = 0 title = "A new title" filters = Filters(complevel=1, complib="lzo") class CopyFileCase8(CopyFileTestCase): close = 1 title = "A new title" filters = Filters(complevel=1, complib="lzo") class CopyFileCase10(unittest.TestCase): def test01_notoverwrite(self): "Checking copy of a File (checking not overwriting)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_notoverwrite..." % self.__class__.__name__) # Create two empty files: file = tempfile.mktemp(".h5") fileh = open_file(file, "w") file2 = tempfile.mktemp(".h5") fileh2 = open_file(file2, "w") fileh2.close() # close the second one # Copy the first into the second try: fileh.copy_file(file2, overwrite=False) except IOError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next IOError was catched!") print(value) else: self.fail("expected a IOError") # Delete files fileh.close() os.remove(file) os.remove(file2) class GroupFiltersTestCase(common.TempFileMixin, common.PyTablesTestCase): filters = tables.Filters(complevel=4) # something non-default def setUp(self): super(GroupFiltersTestCase, self).setUp() atom, shape = tables.IntAtom(), (1, 1) create_group = self.h5file.create_group create_carray = self.h5file.create_carray create_group('/', 'implicit_no') create_group('/implicit_no', 'implicit_no') create_carray('/implicit_no/implicit_no', 'implicit_no', atom=atom, shape=shape) create_carray('/implicit_no/implicit_no', 'explicit_no', atom=atom, shape=shape, filters=tables.Filters()) create_carray('/implicit_no/implicit_no', 'explicit_yes', atom=atom, shape=shape, filters=self.filters) create_group('/', 'explicit_yes', filters=self.filters) create_group('/explicit_yes', 'implicit_yes') create_carray('/explicit_yes/implicit_yes', 'implicit_yes', atom=atom, shape=shape) create_carray('/explicit_yes/implicit_yes', 'explicit_yes', atom=atom, shape=shape, filters=self.filters) create_carray('/explicit_yes/implicit_yes', 'explicit_no', atom=atom, shape=shape, filters=tables.Filters()) def _check_filters(self, h5file, filters=None): for node in h5file: # Get node filters. if hasattr(node, 'filters'): node_filters = node.filters else: node_filters = node._v_filters # Compare to given filters. if filters is not None: self.assertEqual(node_filters, filters) return # Guess filters to compare to by node name. if node._v_name.endswith('_no'): self.assertEqual( node_filters, tables.Filters(), "node ``%s`` should have no filters" % node._v_pathname) elif node._v_name.endswith('_yes'): self.assertEqual( node_filters, self.filters, "node ``%s`` should have filters" % node._v_pathname) def test00_propagate(self): """Filters propagating to children.""" self._check_filters(self.h5file) def _test_copyFile(self, filters=None): copyfname = tempfile.mktemp(suffix='.h5') try: self.h5file.copy_file(copyfname, filters=filters) try: copyf = tables.open_file(copyfname) self._check_filters(copyf, filters=filters) finally: copyf.close() finally: os.remove(copyfname) def test01_copyFile(self): """Keeping filters when copying a file.""" self._test_copyFile() def test02_copyFile_override(self): """Overriding filters when copying a file.""" self._test_copyFile(self.filters) def _test_change(self, pathname, change_filters, new_filters): group = self.h5file.get_node(pathname) # Check expected current filters. old_filters = tables.Filters() if pathname.endswith('_yes'): old_filters = self.filters self.assertEqual(group._v_filters, old_filters) # Change filters. change_filters(group) self.assertEqual(group._v_filters, new_filters) # Get and check changed filters. if self._reopen(): group = self.h5file.get_node(pathname) self.assertEqual(group._v_filters, new_filters) def test03_change(self): """Changing the filters of a group.""" def set_filters(group): group._v_filters = self.filters self._test_change('/', set_filters, self.filters) def test04_delete(self): """Deleting the filters of a group.""" def del_filters(group): del group._v_filters self._test_change('/explicit_yes', del_filters, tables.Filters()) class SetBloscMaxThreadsTestCase(common.TempFileMixin, common.PyTablesTestCase): filters = tables.Filters(complevel=4, complib="blosc") def test00(self): """Checking set_blosc_max_threads()""" nthreads_old = tables.set_blosc_max_threads(4) if common.verbose: print("Previous max threads:", nthreads_old) print("Should be:", self.h5file.params['MAX_BLOSC_THREADS']) self.assertEqual(nthreads_old, self.h5file.params['MAX_BLOSC_THREADS']) self.h5file.create_carray('/', 'some_array', atom=tables.Int32Atom(), shape=(3, 3), filters = self.filters) nthreads_old = tables.set_blosc_max_threads(1) if common.verbose: print("Previous max threads:", nthreads_old) print("Should be:", 4) self.assertEqual(nthreads_old, 4) def test01(self): """Checking set_blosc_max_threads() (re-open)""" nthreads_old = tables.set_blosc_max_threads(4) self.h5file.create_carray('/', 'some_array', atom=tables.Int32Atom(), shape=(3, 3), filters = self.filters) self._reopen() nthreads_old = tables.set_blosc_max_threads(4) if common.verbose: print("Previous max threads:", nthreads_old) print("Should be:", self.h5file.params['MAX_BLOSC_THREADS']) self.assertEqual(nthreads_old, self.h5file.params['MAX_BLOSC_THREADS']) class FilterTestCase(common.PyTablesTestCase): def test_filter_pack_type(self): self.assertEqual(type(Filters()._pack()), numpy.int64) @staticmethod def _hexl(n): if sys.version_info[0] > 2: return hex(int(n)) else: return hex(long(n)).rstrip('L') def test_filter_pack_01(self): filter_ = Filters() self.assertEqual(self._hexl(filter_._pack()), '0x0') def test_filter_pack_02(self): filter_ = Filters(1, shuffle=False) self.assertEqual(self._hexl(filter_._pack()), '0x101') def test_filter_pack_03(self): filter_ = Filters(9, 'zlib', shuffle=True, fletcher32=True) self.assertEqual(self._hexl(filter_._pack()), '0x30109') def test_filter_pack_04(self): filter_ = Filters(1, shuffle=False, least_significant_digit=5) self.assertEqual(self._hexl(filter_._pack()), '0x5040101') def test_filter_unpack_01(self): filter_ = Filters._unpack(numpy.int64(0x0)) self.assertFalse(filter_.shuffle) self.assertFalse(filter_.fletcher32) self.assertEqual(filter_.least_significant_digit, None) self.assertEqual(filter_.complevel, 0) self.assertEqual(filter_.complib, None) def test_filter_unpack_02(self): filter_ = Filters._unpack(numpy.int64(0x101)) self.assertFalse(filter_.shuffle) self.assertFalse(filter_.fletcher32) self.assertEqual(filter_.least_significant_digit, None) self.assertEqual(filter_.complevel, 1) self.assertEqual(filter_.complib, 'zlib') def test_filter_unpack_03(self): filter_ = Filters._unpack(numpy.int64(0x30109)) self.assertTrue(filter_.shuffle) self.assertTrue(filter_.fletcher32) self.assertEqual(filter_.least_significant_digit, None) self.assertEqual(filter_.complevel, 9) self.assertEqual(filter_.complib, 'zlib') def test_filter_unpack_04(self): filter_ = Filters._unpack(numpy.int64(0x5040101)) self.assertFalse(filter_.shuffle) self.assertFalse(filter_.fletcher32) self.assertEqual(filter_.least_significant_digit, 5) self.assertEqual(filter_.complevel, 1) self.assertEqual(filter_.complib, 'zlib') class DefaultDriverTestCase(common.PyTablesTestCase): DRIVER = None DRIVER_PARAMS = {} def setUp(self): self.h5fname = tempfile.mktemp(suffix=".h5") self.h5file = tables.open_file(self.h5fname, mode="w", driver=self.DRIVER, **self.DRIVER_PARAMS) # Create an HDF5 file and contents root = self.h5file.root self.h5file.set_node_attr(root, "testattr", 41) self.h5file.create_array(root, "array", [1, 2], title="array") self.h5file.create_table(root, "table", {"var1": tables.IntCol()}, title="table") def tearDown(self): if self.h5file: self.h5file.close() self.h5file = None if os.path.isfile(self.h5fname): os.remove(self.h5fname) def assertIsFile(self): self.assertTrue(os.path.isfile(self.h5fname)) def test_newFile(self): self.assertTrue(isinstance(self.h5file, tables.File)) self.assertIsFile() def test_readFile(self): self.h5file.close() self.h5file = None self.assertIsFile() # Open an existing HDF5 file self.h5file = tables.open_file(self.h5fname, mode="r", driver=self.DRIVER, **self.DRIVER_PARAMS) # check contents root = self.h5file.root self.assertEqual(self.h5file.get_node_attr(root, "testattr"), 41) self.assertTrue(isinstance(root.array, tables.Array)) self.assertEqual(root.array._v_title, "array") self.assertTrue(isinstance(root.table, tables.Table)) self.assertEqual(root.table._v_title, "table") self.assertTrue("var1" in root.table.colnames) self.assertEqual(root.table.cols.var1.dtype, tables.IntCol().dtype) def test_openFileA(self): self.h5file.close() self.h5file = None self.assertIsFile() # Open an existing HDF5 file in append mode self.h5file = tables.open_file(self.h5fname, mode="a", driver=self.DRIVER, **self.DRIVER_PARAMS) # check contents root = self.h5file.root self.assertEqual(self.h5file.get_node_attr(root, "testattr"), 41) self.assertTrue(isinstance(root.array, tables.Array)) self.assertEqual(root.array._v_title, "array") self.assertTrue(isinstance(root.table, tables.Table)) self.assertEqual(root.table._v_title, "table") self.assertTrue("var1" in root.table.colnames) self.assertEqual(root.table.cols.var1.dtype, tables.IntCol().dtype) # write new data root = self.h5file.root self.h5file.set_node_attr(root, "testattr2", 42) self.h5file.create_array(root, "array2", [1, 2], title="array2") self.h5file.create_table(root, "table2", {"var2": tables.FloatCol()}, title="table2") self.h5file.close() # check contents self.h5file = tables.open_file(self.h5fname, mode="a", driver=self.DRIVER, **self.DRIVER_PARAMS) root = self.h5file.root self.assertEqual(self.h5file.get_node_attr(root, "testattr"), 41) self.assertEqual(self.h5file.get_node_attr(root, "testattr2"), 42) self.assertTrue(isinstance(root.array, tables.Array)) self.assertEqual(root.array._v_title, "array") self.assertTrue(isinstance(root.array2, tables.Array)) self.assertEqual(root.array2._v_title, "array2") self.assertTrue(isinstance(root.table, tables.Table)) self.assertEqual(root.table._v_title, "table") self.assertTrue("var1" in root.table.colnames) self.assertEqual(root.table.cols.var1.dtype, tables.IntCol().dtype) self.assertTrue(isinstance(root.table2, tables.Table)) self.assertEqual(root.table2._v_title, "table2") self.assertTrue("var2" in root.table2.colnames) self.assertEqual(root.table2.cols.var2.dtype, tables.FloatCol().dtype) def test_openFileRW(self): self.h5file.close() self.h5file = None self.assertIsFile() # Open an existing HDF5 file in append mode self.h5file = tables.open_file(self.h5fname, mode="r+", driver=self.DRIVER, **self.DRIVER_PARAMS) # check contents root = self.h5file.root self.assertEqual(self.h5file.get_node_attr(root, "testattr"), 41) self.assertTrue(isinstance(root.array, tables.Array)) self.assertEqual(root.array._v_title, "array") self.assertTrue(isinstance(root.table, tables.Table)) self.assertEqual(root.table._v_title, "table") self.assertTrue("var1" in root.table.colnames) self.assertEqual(root.table.cols.var1.dtype, tables.IntCol().dtype) # write new data self.h5file.set_node_attr(root, "testattr2", 42) self.h5file.create_array(root, "array2", [1, 2], title="array2") self.h5file.create_table(root, "table2", {"var2": tables.FloatCol()}, title="table2") self.h5file.close() # check contents self.h5file = tables.open_file(self.h5fname, mode="r+", driver=self.DRIVER, **self.DRIVER_PARAMS) root = self.h5file.root self.assertEqual(self.h5file.get_node_attr(root, "testattr"), 41) self.assertEqual(self.h5file.get_node_attr(root, "testattr2"), 42) self.assertTrue(isinstance(root.array, tables.Array)) self.assertEqual(root.array._v_title, "array") self.assertTrue(isinstance(root.array2, tables.Array)) self.assertEqual(root.array2._v_title, "array2") self.assertTrue(isinstance(root.table, tables.Table)) self.assertEqual(root.table._v_title, "table") self.assertTrue("var1" in root.table.colnames) self.assertEqual(root.table.cols.var1.dtype, tables.IntCol().dtype) self.assertTrue(isinstance(root.table2, tables.Table)) self.assertEqual(root.table2._v_title, "table2") self.assertTrue("var2" in root.table2.colnames) self.assertEqual(root.table2.cols.var2.dtype, tables.FloatCol().dtype) class Sec2DriverTestCase(DefaultDriverTestCase): DRIVER = "H5FD_SEC2" if hdf5_version >= "1.8.9": def test_get_file_image(self): image = self.h5file.get_file_image() self.assertTrue(len(image) > 0) if sys.version_info[0] < 3: self.assertEqual([ord(i) for i in image[ :4]], [137, 72, 68, 70]) else: self.assertEqual([i for i in image[:4]], [137, 72, 68, 70]) class StdioDriverTestCase(DefaultDriverTestCase): DRIVER = "H5FD_STDIO" if hdf5_version >= "1.8.9": def test_get_file_image(self): image = self.h5file.get_file_image() self.assertTrue(len(image) > 0) if sys.version_info[0] < 3: self.assertEqual([ord(i) for i in image[ :4]], [137, 72, 68, 70]) else: self.assertEqual([i for i in image[:4]], [137, 72, 68, 70]) class CoreDriverTestCase(DefaultDriverTestCase): DRIVER = "H5FD_CORE" if hdf5_version >= "1.8.9": def test_get_file_image(self): image = self.h5file.get_file_image() self.assertTrue(len(image) > 0) if sys.version_info[0] < 3: self.assertEqual([ord(i) for i in image[ :4]], [137, 72, 68, 70]) else: self.assertEqual([i for i in image[:4]], [137, 72, 68, 70]) class CoreDriverNoBackingStoreTestCase(common.PyTablesTestCase): DRIVER = "H5FD_CORE" def setUp(self): self.h5fname = tempfile.mktemp(suffix=".h5") self.h5file = None def tearDown(self): if self.h5file: self.h5file.close() elif self.h5fname in tables.file._open_files: open_files = tables.file._open_files for h5file in open_files.get_handlers_by_name(self.h5fname): h5file.close() self.h5file = None if os.path.isfile(self.h5fname): os.remove(self.h5fname) def test_newFile(self): """Ensure that nothing is written to file.""" self.assertFalse(os.path.isfile(self.h5fname)) self.h5file = tables.open_file(self.h5fname, mode="w", driver=self.DRIVER, driver_core_backing_store=False) # Create an HDF5 file and contents root = self.h5file.root self.h5file.set_node_attr(root, "testattr", 41) self.h5file.create_array(root, "array", [1, 2], title="array") self.h5file.create_table(root, "table", {"var1": tables.IntCol()}, title="table") self.h5file.close() # flush self.assertFalse(os.path.isfile(self.h5fname)) def test_readNewFileW(self): self.assertFalse(os.path.isfile(self.h5fname)) # Create an HDF5 file and contents self.h5file = tables.open_file(self.h5fname, mode="w", driver=self.DRIVER, driver_core_backing_store=False) root = self.h5file.root self.h5file.set_node_attr(root, "testattr", 41) self.h5file.create_array(root, "array", [1, 2], title="array") self.h5file.create_table(root, "table", {"var1": tables.IntCol()}, title="table") self.assertEqual(self.h5file.get_node_attr(root, "testattr"), 41) self.assertTrue(isinstance(root.array, tables.Array)) self.assertEqual(root.array._v_title, "array") self.assertTrue(isinstance(root.table, tables.Table)) self.assertEqual(root.table._v_title, "table") self.assertTrue("var1" in root.table.colnames) self.assertEqual(root.table.cols.var1.dtype, tables.IntCol().dtype) self.h5file.close() # flush self.assertFalse(os.path.isfile(self.h5fname)) def test_readNewFileA(self): self.assertFalse(os.path.isfile(self.h5fname)) # Create an HDF5 file and contents self.h5file = tables.open_file(self.h5fname, mode="a", driver=self.DRIVER, driver_core_backing_store=False) root = self.h5file.root self.h5file.set_node_attr(root, "testattr", 41) self.h5file.create_array(root, "array", [1, 2], title="array") self.h5file.create_table(root, "table", {"var1": tables.IntCol()}, title="table") self.assertEqual(self.h5file.get_node_attr(root, "testattr"), 41) self.assertTrue(isinstance(root.array, tables.Array)) self.assertEqual(root.array._v_title, "array") self.assertTrue(isinstance(root.table, tables.Table)) self.assertEqual(root.table._v_title, "table") self.assertTrue("var1" in root.table.colnames) self.assertEqual(root.table.cols.var1.dtype, tables.IntCol().dtype) self.h5file.close() # flush self.assertFalse(os.path.isfile(self.h5fname)) def test_openNewFileRW(self): self.assertFalse(os.path.isfile(self.h5fname)) self.assertRaises(HDF5ExtError, tables.open_file, self.h5fname, mode="r+", driver=self.DRIVER, driver_core_backing_store=False) def test_openNewFileR(self): self.assertFalse(os.path.isfile(self.h5fname)) self.assertRaises(HDF5ExtError, tables.open_file, self.h5fname, mode="r", driver=self.DRIVER, driver_core_backing_store=False) def _create_file(self, filename): h5file = tables.open_file(filename, mode="w") root = h5file.root h5file.set_node_attr(root, "testattr", 41) h5file.create_array(root, "array", [1, 2], title="array") h5file.create_table(root, "table", {"var1": tables.IntCol()}, title="table") h5file.close() def test_readFile(self): self._create_file(self.h5fname) self.assertTrue(os.path.isfile(self.h5fname)) # Open an existing HDF5 file self.h5file = tables.open_file(self.h5fname, mode="r", driver=self.DRIVER, driver_core_backing_store=False) root = self.h5file.root self.assertEqual(self.h5file.get_node_attr(root, "testattr"), 41) self.assertTrue(isinstance(root.array, tables.Array)) self.assertEqual(root.array._v_title, "array") self.assertTrue(isinstance(root.table, tables.Table)) self.assertEqual(root.table._v_title, "table") self.assertTrue("var1" in root.table.colnames) self.assertEqual(root.table.cols.var1.dtype, tables.IntCol().dtype) def _get_digest(self, filename): md5 = hashlib.md5() fd = open(filename, 'rb') for data in fd: md5.update(data) fd.close() hexdigest = md5.hexdigest() return hexdigest def test_openFileA(self): self._create_file(self.h5fname) self.assertTrue(os.path.isfile(self.h5fname)) # compute the file hash hexdigest = self._get_digest(self.h5fname) # Open an existing HDF5 file in append mode self.h5file = tables.open_file(self.h5fname, mode="a", driver=self.DRIVER, driver_core_backing_store=False) # check contents root = self.h5file.root self.assertEqual(self.h5file.get_node_attr(root, "testattr"), 41) self.assertTrue(isinstance(root.array, tables.Array)) self.assertEqual(root.array._v_title, "array") self.assertTrue(isinstance(root.table, tables.Table)) self.assertEqual(root.table._v_title, "table") self.assertTrue("var1" in root.table.colnames) self.assertEqual(root.table.cols.var1.dtype, tables.IntCol().dtype) # write new data root = self.h5file.root self.h5file.set_node_attr(root, "testattr2", 42) self.h5file.create_array(root, "array2", [1, 2], title="array2") self.h5file.create_table(root, "table2", {"var2": tables.FloatCol()}, title="table2") self.h5file.close() # ensure that there is no change on the file on disk self.assertEqual(hexdigest, self._get_digest(self.h5fname)) def test_openFileRW(self): self._create_file(self.h5fname) self.assertTrue(os.path.isfile(self.h5fname)) # compute the file hash hexdigest = self._get_digest(self.h5fname) # Open an existing HDF5 file in append mode self.h5file = tables.open_file(self.h5fname, mode="r+", driver=self.DRIVER, driver_core_backing_store=False) # check contents root = self.h5file.root self.assertEqual(self.h5file.get_node_attr(root, "testattr"), 41) self.assertTrue(isinstance(root.array, tables.Array)) self.assertEqual(root.array._v_title, "array") self.assertTrue(isinstance(root.table, tables.Table)) self.assertEqual(root.table._v_title, "table") self.assertTrue("var1" in root.table.colnames) self.assertEqual(root.table.cols.var1.dtype, tables.IntCol().dtype) # write new data root = self.h5file.root self.h5file.set_node_attr(root, "testattr2", 42) self.h5file.create_array(root, "array2", [1, 2], title="array2") self.h5file.create_table(root, "table2", {"var2": tables.FloatCol()}, title="table2") self.h5file.close() # ensure that there is no change on the file on disk self.assertEqual(hexdigest, self._get_digest(self.h5fname)) if hdf5_version >= "1.8.9": def test_get_file_image(self): self.h5file = tables.open_file(self.h5fname, mode="w", driver=self.DRIVER, driver_core_backing_store=False) root = self.h5file.root self.h5file.set_node_attr(root, "testattr", 41) self.h5file.create_array(root, "array", [1, 2], title="array") self.h5file.create_table(root, "table", {"var1": tables.IntCol()}, title="table") image = self.h5file.get_file_image() self.assertTrue(len(image) > 0) if sys.version_info[0] < 3: self.assertEqual([ord(i) for i in image[ :4]], [137, 72, 68, 70]) else: self.assertEqual([i for i in image[:4]], [137, 72, 68, 70]) class SplitDriverTestCase(DefaultDriverTestCase): DRIVER = "H5FD_SPLIT" DRIVER_PARAMS = { "driver_split_meta_ext": "-xm.h5", "driver_split_raw_ext": "-xr.h5", } def setUp(self): self.h5fname = tempfile.mktemp() self.h5fnames = [self.h5fname + self.DRIVER_PARAMS[k] for k in ("driver_split_meta_ext", "driver_split_raw_ext")] self.h5file = tables.open_file(self.h5fname, mode="w", driver=self.DRIVER, **self.DRIVER_PARAMS) root = self.h5file.root self.h5file.set_node_attr(root, "testattr", 41) self.h5file.create_array(root, "array", [1, 2], title="array") self.h5file.create_table(root, "table", {"var1": tables.IntCol()}, title="table") def tearDown(self): if self.h5file: self.h5file.close() self.h5file = None for fname in self.h5fnames: if os.path.isfile(fname): os.remove(fname) def assertIsFile(self): for fname in self.h5fnames: self.assertTrue(os.path.isfile(fname)) class NotSpportedDriverTestCase(common.PyTablesTestCase): DRIVER = None DRIVER_PARAMS = {} EXCEPTION = ValueError def setUp(self): self.h5fname = tempfile.mktemp(suffix=".h5") def tearDown(self): open_files = tables.file._open_files if self.h5fname in open_files: for h5file in open_files.get_handlers_by_name(self.h5fname): h5file.close() if os.path.exists(self.h5fname): os.remove(self.h5fname) def test_newFile(self): self.assertRaises(self.EXCEPTION, tables.open_file, self.h5fname, mode="w", driver=self.DRIVER, **self.DRIVER_PARAMS) self.assertFalse(os.path.isfile(self.h5fname)) if "H5FD_LOG" in tables.hdf5extension._supported_drivers: BaseLogDriverTestCase = DefaultDriverTestCase else: BaseLogDriverTestCase = NotSpportedDriverTestCase class LogDriverTestCase(BaseLogDriverTestCase): DRIVER = "H5FD_LOG" def setUp(self): # local binding self.DRIVER_PARAMS = { "driver_log_file": tempfile.mktemp(suffix=".log") } super(LogDriverTestCase, self).setUp() def tearDown(self): super(LogDriverTestCase, self).tearDown() if os.path.exists(self.DRIVER_PARAMS["driver_log_file"]): os.remove(self.DRIVER_PARAMS["driver_log_file"]) if HAVE_DIRECT_DRIVER: class DirectDriverTestCase(DefaultDriverTestCase): DRIVER = "H5FD_DIRECT" else: class DirectDriverTestCase(NotSpportedDriverTestCase): DRIVER = "H5FD_DIRECT" EXCEPTION = RuntimeError if HAVE_WINDOWS_DRIVER: class WindowsDriverTestCase(DefaultDriverTestCase): DRIVER = "H5FD_WINDOWS" else: class WindowsDriverTestCase(NotSpportedDriverTestCase): DRIVER = "H5FD_WINDOWS" EXCEPTION = RuntimeError class FamilyDriverTestCase(NotSpportedDriverTestCase): DRIVER = "H5FD_FAMILY" class MultiDriverTestCase(NotSpportedDriverTestCase): DRIVER = "H5FD_MULTI" class MpioDriverTestCase(NotSpportedDriverTestCase): DRIVER = "H5FD_MPIO" class MpiPosixDriverTestCase(NotSpportedDriverTestCase): DRIVER = "H5FD_MPIPOSIX" class StreamDriverTestCase(NotSpportedDriverTestCase): DRIVER = "H5FD_STREAM" class InMemoryCoreDriverTestCase(common.PyTablesTestCase): DRIVER = "H5FD_CORE" def setUp(self): self.h5fname = tempfile.mktemp(".h5") self.h5file = None def tearDown(self): if self.h5file: self.h5file.close() self.h5file = None if os.path.isfile(self.h5fname): os.remove(self.h5fname) def _create_image(self, filename="in-memory", title="Title", mode='w'): fileh = open_file(filename, mode=mode, title=title, driver=self.DRIVER, driver_core_backing_store=0) try: fileh.create_array(fileh.root, 'array', [1, 2], title="Array") fileh.create_table(fileh.root, 'table', { 'var1': IntCol()}, "Table") fileh.root._v_attrs.testattr = 41 image = fileh.get_file_image() finally: fileh.close() return image def test_newFileW(self): image = self._create_image(self.h5fname, mode='w') self.assertTrue(len(image) > 0) if sys.version_info[0] < 3: self.assertEqual([ord(i) for i in image[:4]], [137, 72, 68, 70]) else: self.assertEqual([i for i in image[:4]], [137, 72, 68, 70]) self.assertFalse(os.path.exists(self.h5fname)) def test_newFileA(self): image = self._create_image(self.h5fname, mode='a') self.assertTrue(len(image) > 0) if sys.version_info[0] < 3: self.assertEqual([ord(i) for i in image[:4]], [137, 72, 68, 70]) else: self.assertEqual([i for i in image[:4]], [137, 72, 68, 70]) self.assertFalse(os.path.exists(self.h5fname)) def test_openFileR(self): image = self._create_image(self.h5fname) self.assertFalse(os.path.exists(self.h5fname)) # Open an existing file self.h5file = open_file(self.h5fname, mode="r", driver=self.DRIVER, driver_core_image=image, driver_core_backing_store=0) # Get the CLASS attribute of the arr object self.assertTrue(hasattr(self.h5file.root._v_attrs, "TITLE")) self.assertEqual(self.h5file.get_node_attr("/", "TITLE"), "Title") self.assertTrue(hasattr(self.h5file.root._v_attrs, "testattr")) self.assertEqual(self.h5file.get_node_attr("/", "testattr"), 41) self.assertTrue(hasattr(self.h5file.root, "array")) self.assertEqual(self.h5file.get_node_attr("/array", "TITLE"), "Array") self.assertTrue(hasattr(self.h5file.root, "table")) self.assertEqual(self.h5file.get_node_attr("/table", "TITLE"), "Table") self.assertEqual(self.h5file.root.array.read(), [1, 2]) def test_openFileRW(self): image = self._create_image(self.h5fname) self.assertFalse(os.path.exists(self.h5fname)) # Open an existing file self.h5file = open_file(self.h5fname, mode="r+", driver=self.DRIVER, driver_core_image=image, driver_core_backing_store=0) # Get the CLASS attribute of the arr object self.assertTrue(hasattr(self.h5file.root._v_attrs, "TITLE")) self.assertEqual(self.h5file.get_node_attr("/", "TITLE"), "Title") self.assertTrue(hasattr(self.h5file.root._v_attrs, "testattr")) self.assertEqual(self.h5file.get_node_attr("/", "testattr"), 41) self.assertTrue(hasattr(self.h5file.root, "array")) self.assertEqual(self.h5file.get_node_attr("/array", "TITLE"), "Array") self.assertTrue(hasattr(self.h5file.root, "table")) self.assertEqual(self.h5file.get_node_attr("/table", "TITLE"), "Table") self.assertEqual(self.h5file.root.array.read(), [1, 2]) self.h5file.create_array(self.h5file.root, 'array2', range(10000), title="Array2") self.h5file.root._v_attrs.testattr2 = 42 self.h5file.close() self.assertFalse(os.path.exists(self.h5fname)) def test_openFileRW_update(self): filename = tempfile.mktemp(".h5") image1 = self._create_image(filename) self.assertFalse(os.path.exists(self.h5fname)) # Open an existing file self.h5file = open_file(self.h5fname, mode="r+", driver=self.DRIVER, driver_core_image=image1, driver_core_backing_store=0) # Get the CLASS attribute of the arr object self.assertTrue(hasattr(self.h5file.root._v_attrs, "TITLE")) self.assertEqual(self.h5file.get_node_attr("/", "TITLE"), "Title") self.assertTrue(hasattr(self.h5file.root._v_attrs, "testattr")) self.assertEqual(self.h5file.get_node_attr("/", "testattr"), 41) self.assertTrue(hasattr(self.h5file.root, "array")) self.assertEqual(self.h5file.get_node_attr("/array", "TITLE"), "Array") self.assertTrue(hasattr(self.h5file.root, "table")) self.assertEqual(self.h5file.get_node_attr("/table", "TITLE"), "Table") self.assertEqual(self.h5file.root.array.read(), [1, 2]) data = range(2 * tables.parameters.DRIVER_CORE_INCREMENT) self.h5file.create_array(self.h5file.root, 'array2', data, title="Array2") self.h5file.root._v_attrs.testattr2 = 42 image2 = self.h5file.get_file_image() self.h5file.close() self.assertFalse(os.path.exists(self.h5fname)) self.assertNotEqual(len(image1), len(image2)) self.assertNotEqual(image1, image2) # Open an existing file self.h5file = open_file(self.h5fname, mode="r", driver=self.DRIVER, driver_core_image=image2, driver_core_backing_store=0) # Get the CLASS attribute of the arr object self.assertTrue(hasattr(self.h5file.root._v_attrs, "TITLE")) self.assertEqual(self.h5file.get_node_attr("/", "TITLE"), "Title") self.assertTrue(hasattr(self.h5file.root._v_attrs, "testattr")) self.assertEqual(self.h5file.get_node_attr("/", "testattr"), 41) self.assertTrue(hasattr(self.h5file.root, "array")) self.assertEqual(self.h5file.get_node_attr("/array", "TITLE"), "Array") self.assertTrue(hasattr(self.h5file.root, "table")) self.assertEqual(self.h5file.get_node_attr("/table", "TITLE"), "Table") self.assertEqual(self.h5file.root.array.read(), [1, 2]) self.assertTrue(hasattr(self.h5file.root._v_attrs, "testattr2")) self.assertEqual(self.h5file.get_node_attr("/", "testattr2"), 42) self.assertTrue(hasattr(self.h5file.root, "array2")) self.assertEqual(self.h5file.get_node_attr( "/array2", "TITLE"), "Array2") self.assertEqual(self.h5file.root.array2.read(), data) self.h5file.close() self.assertFalse(os.path.exists(self.h5fname)) def test_openFileA(self): image = self._create_image(self.h5fname) self.assertFalse(os.path.exists(self.h5fname)) # Open an existing file self.h5file = open_file(self.h5fname, mode="a", driver=self.DRIVER, driver_core_image=image, driver_core_backing_store=0) # Get the CLASS attribute of the arr object self.assertTrue(hasattr(self.h5file.root._v_attrs, "TITLE")) self.assertEqual(self.h5file.get_node_attr("/", "TITLE"), "Title") self.assertTrue(hasattr(self.h5file.root._v_attrs, "testattr")) self.assertEqual(self.h5file.get_node_attr("/", "testattr"), 41) self.assertTrue(hasattr(self.h5file.root, "array")) self.assertEqual(self.h5file.get_node_attr("/array", "TITLE"), "Array") self.assertTrue(hasattr(self.h5file.root, "table")) self.assertEqual(self.h5file.get_node_attr("/table", "TITLE"), "Table") self.assertEqual(self.h5file.root.array.read(), [1, 2]) self.h5file.close() self.assertFalse(os.path.exists(self.h5fname)) def test_openFileA_update(self): filename = tempfile.mktemp(".h5") image1 = self._create_image(filename) self.assertFalse(os.path.exists(self.h5fname)) # Open an existing file self.h5file = open_file(self.h5fname, mode="a", driver=self.DRIVER, driver_core_image=image1, driver_core_backing_store=0) # Get the CLASS attribute of the arr object self.assertTrue(hasattr(self.h5file.root._v_attrs, "TITLE")) self.assertEqual(self.h5file.get_node_attr("/", "TITLE"), "Title") self.assertTrue(hasattr(self.h5file.root._v_attrs, "testattr")) self.assertEqual(self.h5file.get_node_attr("/", "testattr"), 41) self.assertTrue(hasattr(self.h5file.root, "array")) self.assertEqual(self.h5file.get_node_attr("/array", "TITLE"), "Array") self.assertTrue(hasattr(self.h5file.root, "table")) self.assertEqual(self.h5file.get_node_attr("/table", "TITLE"), "Table") self.assertEqual(self.h5file.root.array.read(), [1, 2]) data = range(2 * tables.parameters.DRIVER_CORE_INCREMENT) self.h5file.create_array(self.h5file.root, 'array2', data, title="Array2") self.h5file.root._v_attrs.testattr2 = 42 image2 = self.h5file.get_file_image() self.h5file.close() self.assertFalse(os.path.exists(self.h5fname)) self.assertNotEqual(len(image1), len(image2)) self.assertNotEqual(image1, image2) # Open an existing file self.h5file = open_file(self.h5fname, mode="r", driver=self.DRIVER, driver_core_image=image2, driver_core_backing_store=0) # Get the CLASS attribute of the arr object self.assertTrue(hasattr(self.h5file.root._v_attrs, "TITLE")) self.assertEqual(self.h5file.get_node_attr("/", "TITLE"), "Title") self.assertTrue(hasattr(self.h5file.root._v_attrs, "testattr")) self.assertEqual(self.h5file.get_node_attr("/", "testattr"), 41) self.assertTrue(hasattr(self.h5file.root, "array")) self.assertEqual(self.h5file.get_node_attr("/array", "TITLE"), "Array") self.assertTrue(hasattr(self.h5file.root, "table")) self.assertEqual(self.h5file.get_node_attr("/table", "TITLE"), "Table") self.assertEqual(self.h5file.root.array.read(), [1, 2]) self.assertTrue(hasattr(self.h5file.root._v_attrs, "testattr2")) self.assertEqual(self.h5file.get_node_attr("/", "testattr2"), 42) self.assertTrue(hasattr(self.h5file.root, "array2")) self.assertEqual(self.h5file.get_node_attr( "/array2", "TITLE"), "Array2") self.assertEqual(self.h5file.root.array2.read(), data) self.h5file.close() self.assertFalse(os.path.exists(self.h5fname)) def test_str(self): self.h5file = open_file(self.h5fname, mode="w", title="Title", driver=self.DRIVER, driver_core_backing_store=0) self.h5file.create_array(self.h5file.root, 'array', [1, 2], title="Array") self.h5file.create_table(self.h5file.root, 'table', {'var1': IntCol()}, "Table") self.h5file.root._v_attrs.testattr = 41 # ensure that the __str__ method works even if there is no phisical # file on disk (in which case the os.stat operation for date retrieval # fails) self.assertTrue(str(self.h5file) is not None) self.h5file.close() self.assertFalse(os.path.exists(self.h5fname)) class QuantizeTestCase(unittest.TestCase): mode = "w" title = "This is the table title" expectedrows = 10 appendrows = 5 def setUp(self): self.data = numpy.linspace(-5., 5., 41) self.randomdata = numpy.random.random_sample(1000000) self.randomints = numpy.random.random_integers( -1000000, 1000000, 1000000).astype('int64') # Create a temporary file self.file = tempfile.mktemp(".h5") # Create an instance of HDF5 Table self.h5file = open_file(self.file, self.mode, self.title) self.populateFile() self.h5file.close() self.quantizeddata_0 = numpy.asarray( [-5.] * 2 + [-4.] * 5 + [-3.] * 3 + [-2.] * 5 + [-1.] * 3 + [0.] * 5 + [1.] * 3 + [2.] * 5 + [3.] * 3 + [4.] * 5 + [5.] * 2) self.quantizeddata_m1 = numpy.asarray( [-8.] * 4 + [0.] * 33 + [8.] * 4) def populateFile(self): root = self.h5file.root filters = Filters(complevel=1, complib="blosc", least_significant_digit=1) ints = self.h5file.create_carray(root, "integers", Int64Atom(), (1000000,), filters=filters) ints[:] = self.randomints floats = self.h5file.create_carray(root, "floats", Float32Atom(), (1000000,), filters=filters) floats[:] = self.randomdata data1 = self.h5file.create_carray(root, "data1", Float64Atom(), (41,), filters=filters) data1[:] = self.data filters = Filters(complevel=1, complib="blosc", least_significant_digit=0) data0 = self.h5file.create_carray(root, "data0", Float64Atom(), (41,), filters=filters) data0[:] = self.data filters = Filters(complevel=1, complib="blosc", least_significant_digit=2) data2 = self.h5file.create_carray(root, "data2", Float64Atom(), (41,), filters=filters) data2[:] = self.data filters = Filters(complevel=1, complib="blosc", least_significant_digit=-1) datam1 = self.h5file.create_carray(root, "datam1", Float64Atom(), (41,), filters=filters) datam1[:] = self.data def tearDown(self): # Close the file if self.h5file.isopen: self.h5file.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test00_quantizeData(self): """Checking the quantize() function.""" quantized_0 = quantize(self.data, 0) quantized_1 = quantize(self.data, 1) quantized_2 = quantize(self.data, 2) quantized_m1 = quantize(self.data, -1) numpy.testing.assert_array_equal(quantized_0, self.quantizeddata_0) numpy.testing.assert_array_equal(quantized_1, self.data) numpy.testing.assert_array_equal(quantized_2, self.data) numpy.testing.assert_array_equal(quantized_m1, self.quantizeddata_m1) def test01_quantizeDataMaxError(self): """Checking the maximum error introduced by the quantize() function.""" quantized_0 = quantize(self.randomdata, 0) quantized_1 = quantize(self.randomdata, 1) quantized_2 = quantize(self.randomdata, 2) quantized_m1 = quantize(self.randomdata, -1) # assertLess is new in Python 2.7 #self.assertLess(numpy.abs(quantized_0 - self.randomdata).max(), 0.5) #self.assertLess(numpy.abs(quantized_1 - self.randomdata).max(), 0.05) #self.assertLess(numpy.abs(quantized_2 - self.randomdata).max(), 0.005) #self.assertLess(numpy.abs(quantized_m1 - self.randomdata).max(), 1.) self.assertTrue(numpy.abs(quantized_0 - self.randomdata).max() < 0.5) self.assertTrue(numpy.abs(quantized_1 - self.randomdata).max() < 0.05) self.assertTrue(numpy.abs(quantized_2 - self.randomdata).max() < 0.005) self.assertTrue(numpy.abs(quantized_m1 - self.randomdata).max() < 1.) def test02_array(self): """Checking quantized data as written to disk.""" self.h5file = open_file(self.file, "r") numpy.testing.assert_array_equal(self.h5file.root.data1[:], self.data) numpy.testing.assert_array_equal(self.h5file.root.data2[:], self.data) numpy.testing.assert_array_equal(self.h5file.root.data0[:], self.quantizeddata_0) numpy.testing.assert_array_equal(self.h5file.root.datam1[:], self.quantizeddata_m1) numpy.testing.assert_array_equal(self.h5file.root.integers[:], self.randomints) self.assertEqual(self.h5file.root.integers[:].dtype, self.randomints.dtype) # assertLess is new in Python 2.7 #self.assertLess( # numpy.abs(self.h5file.root.floats[:] - self.randomdata).max(), # 0.05 #) self.assertTrue( numpy.abs(self.h5file.root.floats[:] - self.randomdata).max() < 0.05 ) #---------------------------------------------------------------------- def suite(): import doctest theSuite = unittest.TestSuite() niter = 1 # common.heavy = 1 # Uncomment this only for testing purposes! for i in range(niter): theSuite.addTest(unittest.makeSuite(FiltersCase1)) theSuite.addTest(unittest.makeSuite(FiltersCase2)) theSuite.addTest(unittest.makeSuite(FiltersCase10)) theSuite.addTest(unittest.makeSuite(FiltersCaseBloscBloscLZ)) if 'lz4' in tables.blosc_compressor_list(): theSuite.addTest(unittest.makeSuite(FiltersCaseBloscLZ4)) theSuite.addTest(unittest.makeSuite(FiltersCaseBloscLZ4HC)) if 'snappy' in tables.blosc_compressor_list(): theSuite.addTest(unittest.makeSuite(FiltersCaseBloscSnappy)) if 'zlib' in tables.blosc_compressor_list(): theSuite.addTest(unittest.makeSuite(FiltersCaseBloscZlib)) theSuite.addTest(unittest.makeSuite(CopyGroupCase1)) theSuite.addTest(unittest.makeSuite(CopyGroupCase2)) theSuite.addTest(unittest.makeSuite(CopyFileCase1)) theSuite.addTest(unittest.makeSuite(CopyFileCase2)) theSuite.addTest(unittest.makeSuite(GroupFiltersTestCase)) theSuite.addTest(unittest.makeSuite(SetBloscMaxThreadsTestCase)) theSuite.addTest(unittest.makeSuite(FilterTestCase)) theSuite.addTest(doctest.DocTestSuite(tables.filters)) theSuite.addTest(unittest.makeSuite(DefaultDriverTestCase)) theSuite.addTest(unittest.makeSuite(Sec2DriverTestCase)) theSuite.addTest(unittest.makeSuite(StdioDriverTestCase)) theSuite.addTest(unittest.makeSuite(CoreDriverTestCase)) theSuite.addTest(unittest.makeSuite(CoreDriverNoBackingStoreTestCase)) theSuite.addTest(unittest.makeSuite(SplitDriverTestCase)) theSuite.addTest(unittest.makeSuite(LogDriverTestCase)) theSuite.addTest(unittest.makeSuite(DirectDriverTestCase)) theSuite.addTest(unittest.makeSuite(WindowsDriverTestCase)) theSuite.addTest(unittest.makeSuite(FamilyDriverTestCase)) theSuite.addTest(unittest.makeSuite(MultiDriverTestCase)) theSuite.addTest(unittest.makeSuite(MpioDriverTestCase)) theSuite.addTest(unittest.makeSuite(MpiPosixDriverTestCase)) theSuite.addTest(unittest.makeSuite(StreamDriverTestCase)) if hdf5_version >= "1.8.9": theSuite.addTest(unittest.makeSuite(InMemoryCoreDriverTestCase)) theSuite.addTest(unittest.makeSuite(QuantizeTestCase)) if common.heavy: theSuite.addTest(unittest.makeSuite(createTestCase)) theSuite.addTest(unittest.makeSuite(FiltersCase3)) theSuite.addTest(unittest.makeSuite(FiltersCase4)) theSuite.addTest(unittest.makeSuite(FiltersCase5)) theSuite.addTest(unittest.makeSuite(FiltersCase6)) theSuite.addTest(unittest.makeSuite(FiltersCase7)) theSuite.addTest(unittest.makeSuite(FiltersCase8)) theSuite.addTest(unittest.makeSuite(FiltersCase9)) theSuite.addTest(unittest.makeSuite(CopyFileCase3)) theSuite.addTest(unittest.makeSuite(CopyFileCase4)) theSuite.addTest(unittest.makeSuite(CopyFileCase5)) theSuite.addTest(unittest.makeSuite(CopyFileCase6)) theSuite.addTest(unittest.makeSuite(CopyFileCase7)) theSuite.addTest(unittest.makeSuite(CopyFileCase8)) theSuite.addTest(unittest.makeSuite(CopyFileCase10)) theSuite.addTest(unittest.makeSuite(CopyGroupCase3)) theSuite.addTest(unittest.makeSuite(CopyGroupCase4)) theSuite.addTest(unittest.makeSuite(CopyGroupCase5)) theSuite.addTest(unittest.makeSuite(CopyGroupCase6)) theSuite.addTest(unittest.makeSuite(CopyGroupCase7)) theSuite.addTest(unittest.makeSuite(CopyGroupCase8)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/test_do_undo.py000066400000000000000000003061711231437614300211730ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import sys import unittest import os import tempfile import warnings from tables import * from tables.node import NotLoggedMixin from tables.path import join_path from tables.tests import common # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup class BasicTestCase(unittest.TestCase): """Test for basic Undo/Redo operations.""" _reopen = False """Whether to reopen the file at certain points.""" def _doReopen(self): if self._reopen: self.fileh.close() self.fileh = open_file(self.file, mode='r+') def setUp(self): # Create an HDF5 file # self.file = "/tmp/test.h5" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w", title="File title") fileh = self.fileh root = fileh.root # Create an array fileh.create_array(root, 'array', [1, 2], title="Title example") # Create another array object fileh.create_array(root, 'anarray', [1], "Array title") # Create a group object group = fileh.create_group(root, 'agroup', "Group title") # Create a couple of objects there fileh.create_array(group, 'anarray1', [2], "Array title 1") fileh.create_array(group, 'anarray2', [2], "Array title 2") # Create a lonely group in first level fileh.create_group(root, 'agroup2', "Group title 2") # Create a new group in the second level fileh.create_group(group, 'agroup3', "Group title 3") def tearDown(self): # Remove the temporary file self.fileh.close() os.remove(self.file) common.cleanup(self) def test00_simple(self): """Checking simple do/undo.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00_simple..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray', [3, 4], "Another array") # Now undo the past operation self.fileh.undo() # Check that otherarray does not exist in the object tree self.assertTrue("/otherarray" not in self.fileh) self.assertEqual(self.fileh._curaction, 0) self.assertEqual(self.fileh._curmark, 0) # Redo the operation self._doReopen() self.fileh.redo() if common.verbose: print("Object tree after redo:", self.fileh) # Check that otherarray has come back to life in a sane state self.assertTrue("/otherarray" in self.fileh) self.assertEqual(self.fileh.root.otherarray.read(), [3, 4]) self.assertEqual(self.fileh.root.otherarray.title, "Another array") self.assertEqual(self.fileh._curaction, 1) self.assertEqual(self.fileh._curmark, 0) def test01_twice(self): """Checking do/undo (twice operations intertwined)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_twice..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray', [3, 4], "Another array") self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") # Now undo the past operations self._doReopen() self.fileh.undo() self.assertTrue("/otherarray" not in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) self.assertEqual(self.fileh._curaction, 0) self.assertEqual(self.fileh._curmark, 0) # Redo the operation self.fileh.redo() # Check that otherarray has come back to life in a sane state self.assertTrue("/otherarray" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertEqual(self.fileh.root.otherarray.read(), [3, 4]) self.assertEqual(self.fileh.root.otherarray2.read(), [4, 5]) self.assertEqual(self.fileh.root.otherarray.title, "Another array") self.assertEqual(self.fileh.root.otherarray2.title, "Another array 2") self.assertEqual(self.fileh._curaction, 2) self.assertEqual(self.fileh._curmark, 0) def test02_twice2(self): """Checking twice ops and two marks.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_twice2..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray', [3, 4], "Another array") # Put a mark self._doReopen() self.fileh.mark() self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") self.assertEqual(self.fileh._curaction, 3) self.assertEqual(self.fileh._curmark, 1) # Unwind just one mark self.fileh.undo() self.assertTrue("/otherarray" in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) self.assertEqual(self.fileh._curaction, 2) self.assertEqual(self.fileh._curmark, 1) # Unwind another mark self.fileh.undo() self.assertEqual(self.fileh._curaction, 0) self.assertEqual(self.fileh._curmark, 0) self.assertTrue("/otherarray" not in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) # Redo until the next mark self.fileh.redo() self.assertTrue("/otherarray" in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) self._doReopen() self.assertEqual(self.fileh._curaction, 2) self.assertEqual(self.fileh._curmark, 1) # Redo until the end self.fileh.redo() self.assertTrue("/otherarray" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertEqual(self.fileh.root.otherarray.read(), [3, 4]) self.assertEqual(self.fileh.root.otherarray2.read(), [4, 5]) self.assertEqual(self.fileh.root.otherarray.title, "Another array") self.assertEqual(self.fileh.root.otherarray2.title, "Another array 2") self.assertEqual(self.fileh._curaction, 3) self.assertEqual(self.fileh._curmark, 1) def test03_6times3marks(self): """Checking with six ops and three marks.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_6times3marks..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [3, 4], "Another array 1") self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") # Put a mark self.fileh.mark() self.fileh.create_array('/', 'otherarray3', [5, 6], "Another array 3") self.fileh.create_array('/', 'otherarray4', [6, 7], "Another array 4") # Put a mark self._doReopen() self.fileh.mark() self.fileh.create_array('/', 'otherarray5', [7, 8], "Another array 5") self.fileh.create_array('/', 'otherarray6', [8, 9], "Another array 6") # Unwind just one mark self.fileh.undo() self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" in self.fileh) self.assertTrue("/otherarray4" in self.fileh) self.assertTrue("/otherarray5" not in self.fileh) self.assertTrue("/otherarray6" not in self.fileh) # Unwind another mark self.fileh.undo() self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) self.assertTrue("/otherarray5" not in self.fileh) self.assertTrue("/otherarray6" not in self.fileh) # Unwind all marks self.fileh.undo() self.assertTrue("/otherarray1" not in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) self.assertTrue("/otherarray5" not in self.fileh) self.assertTrue("/otherarray6" not in self.fileh) # Redo until the next mark self._doReopen() self.fileh.redo() self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) self.assertTrue("/otherarray5" not in self.fileh) self.assertTrue("/otherarray6" not in self.fileh) # Redo until the next mark self.fileh.redo() self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" in self.fileh) self.assertTrue("/otherarray4" in self.fileh) self.assertTrue("/otherarray5" not in self.fileh) self.assertTrue("/otherarray6" not in self.fileh) # Redo until the end self.fileh.redo() self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" in self.fileh) self.assertTrue("/otherarray4" in self.fileh) self.assertTrue("/otherarray5" in self.fileh) self.assertTrue("/otherarray6" in self.fileh) self.assertEqual(self.fileh.root.otherarray1.read(), [3, 4]) self.assertEqual(self.fileh.root.otherarray2.read(), [4, 5]) self.assertEqual(self.fileh.root.otherarray3.read(), [5, 6]) self.assertEqual(self.fileh.root.otherarray4.read(), [6, 7]) self.assertEqual(self.fileh.root.otherarray5.read(), [7, 8]) self.assertEqual(self.fileh.root.otherarray6.read(), [8, 9]) self.assertEqual(self.fileh.root.otherarray1.title, "Another array 1") self.assertEqual(self.fileh.root.otherarray2.title, "Another array 2") self.assertEqual(self.fileh.root.otherarray3.title, "Another array 3") self.assertEqual(self.fileh.root.otherarray4.title, "Another array 4") self.assertEqual(self.fileh.root.otherarray5.title, "Another array 5") self.assertEqual(self.fileh.root.otherarray6.title, "Another array 6") def test04_6times3marksro(self): """Checking with six operations, three marks and do/undo in random order.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_6times3marksro..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [3, 4], "Another array 1") self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") # Put a mark self.fileh.mark() self._doReopen() self.fileh.create_array('/', 'otherarray3', [5, 6], "Another array 3") self.fileh.create_array('/', 'otherarray4', [6, 7], "Another array 4") # Unwind the previous mark self.fileh.undo() self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) # Put a mark in the middle of stack if common.verbose: print("All nodes:", self.fileh.walk_nodes()) self.fileh.mark() self._doReopen() self.fileh.create_array('/', 'otherarray5', [7, 8], "Another array 5") self.fileh.create_array('/', 'otherarray6', [8, 9], "Another array 6") self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) self.assertTrue("/otherarray5" in self.fileh) self.assertTrue("/otherarray6" in self.fileh) # Unwind previous mark self.fileh.undo() self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) self.assertTrue("/otherarray5" not in self.fileh) self.assertTrue("/otherarray6" not in self.fileh) # Redo until the last mark self.fileh.redo() self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) self.assertTrue("/otherarray5" in self.fileh) self.assertTrue("/otherarray6" in self.fileh) # Redo until the next mark (non-existent, so no action) self._doReopen() self.fileh.redo() self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) self.assertTrue("/otherarray5" in self.fileh) self.assertTrue("/otherarray6" in self.fileh) self.assertEqual(self.fileh.root.otherarray1.read(), [3, 4]) self.assertEqual(self.fileh.root.otherarray2.read(), [4, 5]) self.assertEqual(self.fileh.root.otherarray5.read(), [7, 8]) self.assertEqual(self.fileh.root.otherarray6.read(), [8, 9]) self.assertEqual(self.fileh.root.otherarray1.title, "Another array 1") self.assertEqual(self.fileh.root.otherarray2.title, "Another array 2") self.assertEqual(self.fileh.root.otherarray5.title, "Another array 5") self.assertEqual(self.fileh.root.otherarray6.title, "Another array 6") def test05_destructive(self): """Checking with a destructive action during undo.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05_destructive..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [3, 4], "Another array 1") # Put a mark self.fileh.mark() self._doReopen() self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") # Now undo the past operation self.fileh.undo() # Do the destructive operation self._doReopen() self.fileh.create_array('/', 'otherarray3', [5, 6], "Another array 3") # Check objects self.assertTrue("/otherarray1" in self.fileh) self.assertEqual(self.fileh.root.otherarray1.read(), [3, 4]) self.assertEqual(self.fileh.root.otherarray1.title, "Another array 1") self.assertTrue("/otherarray2" not in self.fileh) self.assertTrue("/otherarray3" in self.fileh) self.assertEqual(self.fileh.root.otherarray3.read(), [5, 6]) self.assertEqual(self.fileh.root.otherarray3.title, "Another array 3") def test05b_destructive(self): """Checking with a destructive action during undo (II)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05b_destructive..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [3, 4], "Another array 1") # Put a mark self._doReopen() self.fileh.mark() self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") # Now undo the past operation self.fileh.undo() # Do the destructive operation self.fileh.create_array('/', 'otherarray3', [5, 6], "Another array 3") # Put a mark self._doReopen() self.fileh.mark() self.fileh.create_array('/', 'otherarray4', [6, 7], "Another array 4") self.assertTrue("/otherarray4" in self.fileh) # Now undo the past operation self.fileh.undo() # Check objects self.assertTrue("/otherarray1" in self.fileh) self.assertEqual(self.fileh.root.otherarray1.read(), [3, 4]) self.assertEqual(self.fileh.root.otherarray1.title, "Another array 1") self.assertTrue("/otherarray2" not in self.fileh) self.assertTrue("/otherarray3" in self.fileh) self.assertEqual(self.fileh.root.otherarray3.read(), [5, 6]) self.assertEqual(self.fileh.root.otherarray3.title, "Another array 3") self.assertTrue("/otherarray4" not in self.fileh) def test05c_destructive(self): """Checking with a destructive action during undo (III)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05c_destructive..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [3, 4], "Another array 1") # Put a mark self.fileh.mark() self._doReopen() self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") # Now undo the past operation self.fileh.undo() # Do the destructive operation self.fileh.create_array('/', 'otherarray3', [5, 6], "Another array 3") # Put a mark self.fileh.mark() self._doReopen() self.fileh.create_array('/', 'otherarray4', [6, 7], "Another array 4") self.assertTrue("/otherarray4" in self.fileh) # Now unwind twice self.fileh.undo() self._doReopen() self.fileh.undo() # Check objects self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) def test05d_destructive(self): """Checking with a destructive action during undo (IV)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05d_destructive..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [3, 4], "Another array 1") # Put a mark self._doReopen() self.fileh.mark() self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") # Now undo the past operation self.fileh.undo() # Do the destructive operation self.fileh.create_array('/', 'otherarray3', [5, 6], "Another array 3") # Put a mark self.fileh.mark() self.fileh.create_array('/', 'otherarray4', [6, 7], "Another array 4") self.assertTrue("/otherarray4" in self.fileh) # Now, go to the first mark self._doReopen() self.fileh.undo(0) # Check objects self.assertTrue("/otherarray1" not in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) def test05e_destructive(self): """Checking with a destructive action during undo (V)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05e_destructive..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [3, 4], "Another array 1") # Put a mark self.fileh.mark() self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") # Now undo the past operation self.fileh.undo() self._doReopen() # Do the destructive operation self.fileh.create_array('/', 'otherarray3', [5, 6], "Another array 3") # Now, unwind the actions self.fileh.undo(0) self._doReopen() # Check objects self.assertTrue("/otherarray1" not in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) def test05f_destructive(self): "Checking with a destructive creation of existing node during undo" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05f_destructive..." % self.__class__.__name__) self.fileh.enable_undo() self.fileh.create_array('/', 'newarray', [1]) self.fileh.undo() self._doReopen() self.assertTrue('/newarray' not in self.fileh) newarr = self.fileh.create_array('/', 'newarray', [1]) self.fileh.undo() self.assertTrue('/newarray' not in self.fileh) self._doReopen() self.fileh.redo() self.assertTrue('/newarray' in self.fileh) if not self._reopen: self.assertTrue(self.fileh.root.newarray is newarr) def test06_totalunwind(self): """Checking do/undo (total unwind)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test06_totalunwind..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray', [3, 4], "Another array") self.fileh.mark() self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") # Now undo the past operations self._doReopen() self.fileh.undo(0) self.assertTrue("/otherarray" not in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) def test07_totalrewind(self): """Checking do/undo (total rewind)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test07_totalunwind..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray', [3, 4], "Another array") self.fileh.mark() self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") # Now undo the past operations self.fileh.undo(0) # Redo all the operations self._doReopen() self.fileh.redo(-1) # Check that objects has come back to life in a sane state self.assertTrue("/otherarray" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertEqual(self.fileh.root.otherarray.read(), [3, 4]) self.assertEqual(self.fileh.root.otherarray2.read(), [4, 5]) self.assertEqual(self.fileh.root.otherarray.title, "Another array") self.assertEqual(self.fileh.root.otherarray2.title, "Another array 2") def test08_marknames(self): """Checking mark names.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test08_marknames..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [3, 4], "Another array 1") self.fileh.mark("first") self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") self.fileh.mark("second") self.fileh.create_array('/', 'otherarray3', [5, 6], "Another array 3") self.fileh.mark("third") self.fileh.create_array('/', 'otherarray4', [6, 7], "Another array 4") # Now go to mark "first" self.fileh.undo("first") self._doReopen() self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) # Go to mark "third" self.fileh.redo("third") self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) # Now go to mark "second" self.fileh.undo("second") self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) # Go to the end self._doReopen() self.fileh.redo(-1) self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" in self.fileh) self.assertTrue("/otherarray4" in self.fileh) # Check that objects has come back to life in a sane state self.assertEqual(self.fileh.root.otherarray1.read(), [3, 4]) self.assertEqual(self.fileh.root.otherarray2.read(), [4, 5]) self.assertEqual(self.fileh.root.otherarray3.read(), [5, 6]) self.assertEqual(self.fileh.root.otherarray4.read(), [6, 7]) def test08_initialmark(self): """Checking initial mark.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test08_initialmark..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() initmid = self.fileh.get_current_mark() # Create a new array self.fileh.create_array('/', 'otherarray', [3, 4], "Another array") self.fileh.mark() self._doReopen() self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") # Now undo the past operations self.fileh.undo(initmid) self.assertTrue("/otherarray" not in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) # Redo all the operations self.fileh.redo(-1) self._doReopen() # Check that objects has come back to life in a sane state self.assertTrue("/otherarray" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertEqual(self.fileh.root.otherarray.read(), [3, 4]) self.assertEqual(self.fileh.root.otherarray2.read(), [4, 5]) self.assertEqual(self.fileh.root.otherarray.title, "Another array") self.assertEqual(self.fileh.root.otherarray2.title, "Another array 2") def test09_marknames(self): """Checking mark names (wrong direction)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test09_marknames..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [3, 4], "Another array 1") self.fileh.mark("first") self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") self.fileh.mark("second") self._doReopen() self.fileh.create_array('/', 'otherarray3', [5, 6], "Another array 3") self.fileh.mark("third") self.fileh.create_array('/', 'otherarray4', [6, 7], "Another array 4") # Now go to mark "first" self.fileh.undo("first") # Try to undo up to mark "third" try: self.fileh.undo("third") except UndoRedoError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next UndoRedoError was catched!") print(value) else: self.fail("expected an UndoRedoError") # Now go to mark "third" self.fileh.redo("third") self._doReopen() # Try to redo up to mark "second" try: self.fileh.redo("second") except UndoRedoError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next UndoRedoError was catched!") print(value) else: self.fail("expected an UndoRedoError") # Final checks self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) def test10_goto(self): """Checking mark names (goto)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test10_goto..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [3, 4], "Another array 1") self._doReopen() self.fileh.mark("first") self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") self.fileh.mark("second") self.fileh.create_array('/', 'otherarray3', [5, 6], "Another array 3") self._doReopen() self.fileh.mark("third") self.fileh.create_array('/', 'otherarray4', [6, 7], "Another array 4") # Now go to mark "first" self.fileh.goto("first") self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) # Go to mark "third" self.fileh.goto("third") self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) # Now go to mark "second" self._doReopen() self.fileh.goto("second") self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) # Go to the end self.fileh.goto(-1) self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" in self.fileh) self.assertTrue("/otherarray4" in self.fileh) # Check that objects has come back to life in a sane state self.assertTrue("/otherarray2" in self.fileh) self.assertEqual(self.fileh.root.otherarray1.read(), [3, 4]) self.assertEqual(self.fileh.root.otherarray2.read(), [4, 5]) self.assertEqual(self.fileh.root.otherarray3.read(), [5, 6]) self.assertEqual(self.fileh.root.otherarray4.read(), [6, 7]) def test10_gotoint(self): """Checking mark sequential ids (goto)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test10_gotoint..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [3, 4], "Another array 1") self.fileh.mark("first") self.fileh.create_array('/', 'otherarray2', [4, 5], "Another array 2") self.fileh.mark("second") self._doReopen() self.fileh.create_array('/', 'otherarray3', [5, 6], "Another array 3") self.fileh.mark("third") self.fileh.create_array('/', 'otherarray4', [6, 7], "Another array 4") # Now go to mark "first" self.fileh.goto(1) self._doReopen() self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) # Go to beginning self.fileh.goto(0) self.assertTrue("/otherarray1" not in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) # Go to mark "third" self._doReopen() self.fileh.goto(3) self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) # Now go to mark "second" self.fileh.goto(2) self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) self.assertTrue("/otherarray4" not in self.fileh) # Go to the end self._doReopen() self.fileh.goto(-1) self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" in self.fileh) self.assertTrue("/otherarray4" in self.fileh) # Check that objects has come back to life in a sane state self.assertTrue("/otherarray2" in self.fileh) self.assertEqual(self.fileh.root.otherarray1.read(), [3, 4]) self.assertEqual(self.fileh.root.otherarray2.read(), [4, 5]) self.assertEqual(self.fileh.root.otherarray3.read(), [5, 6]) self.assertEqual(self.fileh.root.otherarray4.read(), [6, 7]) def test11_contiguous(self): "Creating contiguous marks" if common.verbose: print('\n', '-=' * 30) print("Running %s.test11_contiguous..." % self.__class__.__name__) self.fileh.enable_undo() m1 = self.fileh.mark() m2 = self.fileh.mark() self.assertNotEqual(m1, m2) self._doReopen() self.fileh.undo(m1) self.assertEqual(self.fileh.get_current_mark(), m1) self.fileh.redo(m2) self.assertEqual(self.fileh.get_current_mark(), m2) self.fileh.goto(m1) self.assertEqual(self.fileh.get_current_mark(), m1) self.fileh.goto(m2) self.assertEqual(self.fileh.get_current_mark(), m2) self.fileh.goto(-1) self._doReopen() self.assertEqual(self.fileh.get_current_mark(), m2) self.fileh.goto(0) self.assertEqual(self.fileh.get_current_mark(), 0) def test12_keepMark(self): "Ensuring the mark is kept after an UNDO operation" if common.verbose: print('\n', '-=' * 30) print("Running %s.test12_keepMark..." % self.__class__.__name__) self.fileh.enable_undo() self.fileh.create_array('/', 'newarray1', [1]) mid = self.fileh.mark() self.assertTrue(mid is not None) self._doReopen() self.fileh.undo() # We should have moved to the initial mark. self.assertEqual(self.fileh.get_current_mark(), 0) # So /newarray1 should not be there. self.assertTrue('/newarray1' not in self.fileh) def test13_severalEnableDisable(self): "Checking that successive enable/disable Undo works" if common.verbose: print('\n', '-=' * 30) print("Running %s.test13_severalEnableDisable..." % self.__class__.__name__) self.fileh.enable_undo() self.fileh.create_array('/', 'newarray1', [1]) self.fileh.undo() self._doReopen() # We should have moved to 'mid' mark, not the initial mark. self.assertEqual(self.fileh.get_current_mark(), 0) # So /newarray1 should still be there. self.assertTrue('/newarray1' not in self.fileh) # Close this do/undo session self.fileh.disable_undo() # Do something self.fileh.create_array('/', 'newarray2', [1]) # Enable again do/undo self.fileh.enable_undo() self.fileh.create_array('/', 'newarray3', [1]) mid = self.fileh.mark() self.fileh.create_array('/', 'newarray4', [1]) self.fileh.undo() # We should have moved to 'mid' mark, not the initial mark. self.assertEqual(self.fileh.get_current_mark(), mid) # So /newarray2 and /newarray3 should still be there. self.assertTrue('/newarray1' not in self.fileh) self.assertTrue('/newarray2' in self.fileh) self.assertTrue('/newarray3' in self.fileh) self.assertTrue('/newarray4' not in self.fileh) # Close this do/undo session self._doReopen() self.fileh.disable_undo() # Enable again do/undo self.fileh.enable_undo() self.fileh.create_array('/', 'newarray1', [1]) self.fileh.create_array('/', 'newarray4', [1]) # So /newarray2 and /newarray3 should still be there. self.assertTrue('/newarray1' in self.fileh) self.assertTrue('/newarray2' in self.fileh) self.assertTrue('/newarray3' in self.fileh) self.assertTrue('/newarray4' in self.fileh) self.fileh.undo() self._doReopen() self.assertTrue('/newarray1' not in self.fileh) self.assertTrue('/newarray2' in self.fileh) self.assertTrue('/newarray3' in self.fileh) self.assertTrue('/newarray4' not in self.fileh) # Close this do/undo session self.fileh.disable_undo() class PersistenceTestCase(BasicTestCase): """Test for basic Undo/Redo operations with persistence.""" _reopen = True class createArrayTestCase(unittest.TestCase): "Test for create_array operations" def setUp(self): # Create an HDF5 file self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w", title="File title") fileh = self.fileh root = fileh.root # Create an array fileh.create_array(root, 'array', [1, 2], title="Title example") # Create another array object fileh.create_array(root, 'anarray', [1], "Array title") # Create a group object group = fileh.create_group(root, 'agroup', "Group title") # Create a couple of objects there fileh.create_array(group, 'anarray1', [2], "Array title 1") fileh.create_array(group, 'anarray2', [2], "Array title 2") # Create a lonely group in first level fileh.create_group(root, 'agroup2', "Group title 2") # Create a new group in the second level fileh.create_group(group, 'agroup3', "Group title 3") def tearDown(self): # Remove the temporary file self.fileh.close() os.remove(self.file) common.cleanup(self) def test00(self): """Checking one action.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [1, 2], "Another array 1") # Now undo the past operation self.fileh.undo() # Check that otherarray does not exist in the object tree self.assertTrue("/otherarray1" not in self.fileh) # Redo the operation self.fileh.redo() # Check that otherarray has come back to life in a sane state self.assertTrue("/otherarray1" in self.fileh) self.assertEqual(self.fileh.root.otherarray1.title, "Another array 1") self.assertEqual(self.fileh.root.otherarray1.read(), [1, 2]) def test01(self): """Checking two actions.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [1, 2], "Another array 1") self.fileh.create_array('/', 'otherarray2', [2, 3], "Another array 2") # Now undo the past operation self.fileh.undo() # Check that otherarray does not exist in the object tree self.assertTrue("/otherarray1" not in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) # Redo the operation self.fileh.redo() # Check that otherarray has come back to life in a sane state self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertEqual(self.fileh.root.otherarray1.title, "Another array 1") self.assertEqual(self.fileh.root.otherarray2.title, "Another array 2") self.assertEqual(self.fileh.root.otherarray1.read(), [1, 2]) self.assertEqual(self.fileh.root.otherarray2.read(), [2, 3]) def test02(self): """Checking three actions.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [1, 2], "Another array 1") self.fileh.create_array('/', 'otherarray2', [2, 3], "Another array 2") self.fileh.create_array('/', 'otherarray3', [3, 4], "Another array 3") # Now undo the past operation self.fileh.undo() # Check that otherarray does not exist in the object tree self.assertTrue("/otherarray1" not in self.fileh) self.assertTrue("/otherarray2" not in self.fileh) self.assertTrue("/otherarray3" not in self.fileh) # Redo the operation self.fileh.redo() # Check that otherarray has come back to life in a sane state self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/otherarray2" in self.fileh) self.assertTrue("/otherarray3" in self.fileh) self.assertEqual(self.fileh.root.otherarray1.title, "Another array 1") self.assertEqual(self.fileh.root.otherarray2.title, "Another array 2") self.assertEqual(self.fileh.root.otherarray3.title, "Another array 3") self.assertEqual(self.fileh.root.otherarray1.read(), [1, 2]) self.assertEqual(self.fileh.root.otherarray2.read(), [2, 3]) self.assertEqual(self.fileh.root.otherarray3.read(), [3, 4]) def test03(self): """Checking three actions in different depth levels.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.create_array('/', 'otherarray1', [1, 2], "Another array 1") self.fileh.create_array('/agroup', 'otherarray2', [ 2, 3], "Another array 2") self.fileh.create_array('/agroup/agroup3', 'otherarray3', [ 3, 4], "Another array 3") # Now undo the past operation self.fileh.undo() # Check that otherarray does not exist in the object tree self.assertTrue("/otherarray1" not in self.fileh) self.assertTrue("/agroup/otherarray2" not in self.fileh) self.assertTrue("/agroup/agroup3/otherarray3" not in self.fileh) # Redo the operation self.fileh.redo() # Check that otherarray has come back to life in a sane state self.assertTrue("/otherarray1" in self.fileh) self.assertTrue("/agroup/otherarray2" in self.fileh) self.assertTrue("/agroup/agroup3/otherarray3" in self.fileh) self.assertEqual(self.fileh.root.otherarray1.title, "Another array 1") self.assertEqual(self.fileh.root.agroup.otherarray2.title, "Another array 2") self.assertEqual(self.fileh.root.agroup.agroup3.otherarray3.title, "Another array 3") self.assertEqual(self.fileh.root.otherarray1.read(), [1, 2]) self.assertEqual(self.fileh.root.agroup.otherarray2.read(), [2, 3]) self.assertEqual(self.fileh.root.agroup.agroup3.otherarray3.read(), [3, 4]) class createGroupTestCase(unittest.TestCase): "Test for create_group operations" def setUp(self): # Create an HDF5 file self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w", title="File title") fileh = self.fileh root = fileh.root # Create an array fileh.create_array(root, 'array', [1, 2], title="Title example") # Create another array object fileh.create_array(root, 'anarray', [1], "Array title") # Create a group object group = fileh.create_group(root, 'agroup', "Group title") # Create a couple of objects there fileh.create_array(group, 'anarray1', [2], "Array title 1") fileh.create_array(group, 'anarray2', [2], "Array title 2") # Create a lonely group in first level fileh.create_group(root, 'agroup2', "Group title 2") # Create a new group in the second level fileh.create_group(group, 'agroup3', "Group title 3") def tearDown(self): # Remove the temporary file self.fileh.close() os.remove(self.file) common.cleanup(self) def test00(self): """Checking one action.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new group self.fileh.create_group('/', 'othergroup1', "Another group 1") # Now undo the past operation self.fileh.undo() # Check that othergroup1 does not exist in the object tree self.assertTrue("/othergroup1" not in self.fileh) # Redo the operation self.fileh.redo() # Check that othergroup1 has come back to life in a sane state self.assertTrue("/othergroup1" in self.fileh) self.assertEqual(self.fileh.root.othergroup1._v_title, "Another group 1") def test01(self): """Checking two actions.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new group self.fileh.create_group('/', 'othergroup1', "Another group 1") self.fileh.create_group('/', 'othergroup2', "Another group 2") # Now undo the past operation self.fileh.undo() # Check that othergroup does not exist in the object tree self.assertTrue("/othergroup1" not in self.fileh) self.assertTrue("/othergroup2" not in self.fileh) # Redo the operation self.fileh.redo() # Check that othergroup* has come back to life in a sane state self.assertTrue("/othergroup1" in self.fileh) self.assertTrue("/othergroup2" in self.fileh) self.assertEqual(self.fileh.root.othergroup1._v_title, "Another group 1") self.assertEqual(self.fileh.root.othergroup2._v_title, "Another group 2") def test02(self): """Checking three actions.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new group self.fileh.create_group('/', 'othergroup1', "Another group 1") self.fileh.create_group('/', 'othergroup2', "Another group 2") self.fileh.create_group('/', 'othergroup3', "Another group 3") # Now undo the past operation self.fileh.undo() # Check that othergroup* does not exist in the object tree self.assertTrue("/othergroup1" not in self.fileh) self.assertTrue("/othergroup2" not in self.fileh) self.assertTrue("/othergroup3" not in self.fileh) # Redo the operation self.fileh.redo() # Check that othergroup* has come back to life in a sane state self.assertTrue("/othergroup1" in self.fileh) self.assertTrue("/othergroup2" in self.fileh) self.assertTrue("/othergroup3" in self.fileh) self.assertEqual(self.fileh.root.othergroup1._v_title, "Another group 1") self.assertEqual(self.fileh.root.othergroup2._v_title, "Another group 2") self.assertEqual(self.fileh.root.othergroup3._v_title, "Another group 3") def test03(self): """Checking three actions in different depth levels.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new group self.fileh.create_group('/', 'othergroup1', "Another group 1") self.fileh.create_group( '/othergroup1', 'othergroup2', "Another group 2") self.fileh.create_group( '/othergroup1/othergroup2', 'othergroup3', "Another group 3") # Now undo the past operation self.fileh.undo() # Check that othergroup* does not exist in the object tree self.assertTrue("/othergroup1" not in self.fileh) self.assertTrue("/othergroup1/othergroup2" not in self.fileh) self.assertTrue( "/othergroup1/othergroup2/othergroup3" not in self.fileh) # Redo the operation self.fileh.redo() # Check that othergroup* has come back to life in a sane state self.assertTrue("/othergroup1" in self.fileh) self.assertTrue("/othergroup1/othergroup2" in self.fileh) self.assertTrue("/othergroup1/othergroup2/othergroup3" in self.fileh) self.assertEqual(self.fileh.root.othergroup1._v_title, "Another group 1") self.assertEqual(self.fileh.root.othergroup1.othergroup2._v_title, "Another group 2") self.assertEqual( self.fileh.root.othergroup1.othergroup2.othergroup3._v_title, "Another group 3") minRowIndex = 10 def populateTable(where, name): "Create a table under where with name name" class Indexed(IsDescription): var1 = StringCol(itemsize=4, dflt=b"", pos=1) var2 = BoolCol(dflt=0, pos=2) var3 = IntCol(dflt=0, pos=3) var4 = FloatCol(dflt=0, pos=4) nrows = minRowIndex table = where._v_file.create_table(where, name, Indexed, "Indexed", None, nrows) for i in range(nrows): table.row['var1'] = str(i) # table.row['var2'] = i > 2 table.row['var2'] = i % 2 table.row['var3'] = i table.row['var4'] = float(nrows - i - 1) table.row.append() table.flush() # Index all entries: indexrows = table.cols.var1.create_index() indexrows = table.cols.var2.create_index() indexrows = table.cols.var3.create_index() # Do not index the var4 column # indexrows = table.cols.var4.create_index() if common.verbose: print("Number of written rows:", nrows) print("Number of indexed rows:", table.cols.var1.index.nelements) print("Number of indexed rows(2):", indexrows) class renameNodeTestCase(unittest.TestCase): "Test for rename_node operations" def setUp(self): # Create an HDF5 file self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w", title="File title") fileh = self.fileh root = fileh.root # Create an array fileh.create_array(root, 'array', [1, 2], title="Title example") # Create another array object fileh.create_array(root, 'anarray', [1], "Array title") # Create a group object group = fileh.create_group(root, 'agroup', "Group title") # Create a couple of objects there fileh.create_array(group, 'anarray1', [2], "Array title 1") fileh.create_array(group, 'anarray2', [2], "Array title 2") # Create a lonely group in first level fileh.create_group(root, 'agroup2', "Group title 2") # Create a new group in the second level fileh.create_group(group, 'agroup3', "Group title 3") # Create a table in root populateTable(self.fileh.root, 'table') def tearDown(self): # Remove the temporary file self.fileh.close() os.remove(self.file) common.cleanup(self) def test00(self): """Checking rename_node (over Groups without children)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.rename_node('/agroup2', 'agroup3') # Now undo the past operation self.fileh.undo() # Check that it does not exist in the object tree self.assertTrue("/agroup2" in self.fileh) self.assertTrue("/agroup3" not in self.fileh) self.assertEqual(self.fileh.root.agroup2._v_title, "Group title 2") # Redo the operation self.fileh.redo() # Check that otherarray has come back to life in a sane state self.assertTrue("/agroup2" not in self.fileh) self.assertTrue("/agroup3" in self.fileh) self.assertEqual(self.fileh.root.agroup3._v_title, "Group title 2") def test01(self): """Checking rename_node (over Groups with children)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.rename_node('/agroup', 'agroup3') # Now undo the past operation self.fileh.undo() # Check that it does not exist in the object tree self.assertTrue("/agroup" in self.fileh) self.assertTrue("/agroup3" not in self.fileh) # Check that children are reachable self.assertTrue("/agroup/anarray1" in self.fileh) self.assertTrue("/agroup/anarray2" in self.fileh) self.assertTrue("/agroup/agroup3" in self.fileh) self.assertEqual(self.fileh.root.agroup._v_title, "Group title") # Redo the operation self.fileh.redo() # Check that otherarray has come back to life in a sane state self.assertTrue("/agroup" not in self.fileh) self.assertTrue("/agroup3" in self.fileh) self.assertEqual(self.fileh.root.agroup3._v_title, "Group title") # Check that children are reachable self.assertTrue("/agroup3/anarray1" in self.fileh) self.assertTrue("/agroup3/anarray2" in self.fileh) self.assertTrue("/agroup3/agroup3" in self.fileh) def test01b(self): """Checking rename_node (over Groups with children 2)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01b..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.rename_node('/agroup', 'agroup3') self.fileh.rename_node('/agroup3', 'agroup4') # Now undo the past operation self.fileh.undo() # Check that it does not exist in the object tree self.assertTrue("/agroup" in self.fileh) self.assertTrue("/agroup4" not in self.fileh) # Check that children are reachable self.assertTrue("/agroup/anarray1" in self.fileh) self.assertTrue("/agroup/anarray2" in self.fileh) self.assertTrue("/agroup/agroup3" in self.fileh) self.assertEqual(self.fileh.root.agroup._v_title, "Group title") # Redo the operation self.fileh.redo() # Check that otherarray has come back to life in a sane state self.assertTrue("/agroup" not in self.fileh) self.assertTrue("/agroup4" in self.fileh) self.assertEqual(self.fileh.root.agroup4._v_title, "Group title") # Check that children are reachable self.assertTrue("/agroup4/anarray1" in self.fileh) self.assertTrue("/agroup4/anarray2" in self.fileh) self.assertTrue("/agroup4/agroup3" in self.fileh) def test02(self): """Checking rename_node (over Leaves)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.rename_node('/anarray', 'anarray2') # Now undo the past operation self.fileh.undo() # Check that otherarray does not exist in the object tree self.assertTrue("/anarray" in self.fileh) self.assertTrue("/anarray2" not in self.fileh) self.assertEqual(self.fileh.root.anarray.title, "Array title") # Redo the operation self.fileh.redo() # Check that otherarray has come back to life in a sane state self.assertTrue("/anarray" not in self.fileh) self.assertTrue("/anarray2" in self.fileh) self.assertEqual(self.fileh.root.anarray2.title, "Array title") def test03(self): """Checking rename_node (over Tables)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.rename_node('/table', 'table2') # Now undo the past operation self.fileh.undo() # Check that table2 does not exist in the object tree self.assertTrue("/table" in self.fileh) table = self.fileh.root.table self.assertTrue(table.cols.var1.index is not None) self.assertTrue(table.cols.var2.index is not None) self.assertTrue(table.cols.var3.index is not None) self.assertTrue(table.cols.var4.index is None) self.assertEqual(table.cols.var1.index.nelements, minRowIndex) self.assertEqual(table.cols.var2.index.nelements, minRowIndex) self.assertEqual(table.cols.var3.index.nelements, minRowIndex) self.assertTrue("/table2" not in self.fileh) self.assertEqual(self.fileh.root.table.title, "Indexed") # Redo the operation self.fileh.redo() # Check that table2 has come back to life in a sane state self.assertTrue("/table" not in self.fileh) self.assertTrue("/table2" in self.fileh) self.assertEqual(self.fileh.root.table2.title, "Indexed") table = self.fileh.root.table2 self.assertTrue(table.cols.var1.index is not None) self.assertTrue(table.cols.var2.index is not None) self.assertTrue(table.cols.var3.index is not None) self.assertEqual(table.cols.var1.index.nelements, minRowIndex) self.assertEqual(table.cols.var2.index.nelements, minRowIndex) self.assertEqual(table.cols.var3.index.nelements, minRowIndex) self.assertTrue(table.cols.var4.index is None) class moveNodeTestCase(unittest.TestCase): "Tests for move_node operations" def setUp(self): # Create an HDF5 file self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w", title="File title") fileh = self.fileh root = fileh.root # Create an array fileh.create_array(root, 'array', [1, 2], title="Title example") # Create another array object fileh.create_array(root, 'anarray', [1], "Array title") # Create a group object group = fileh.create_group(root, 'agroup', "Group title") # Create a couple of objects there fileh.create_array(group, 'anarray1', [2], "Array title 1") fileh.create_array(group, 'anarray2', [2], "Array title 2") # Create a lonely group in first level fileh.create_group(root, 'agroup2', "Group title 2") # Create a new group in the second level fileh.create_group(group, 'agroup3', "Group title 3") # Create a table in root populateTable(self.fileh.root, 'table') def tearDown(self): # Remove the temporary file self.fileh.close() os.remove(self.file) common.cleanup(self) def test00(self): """Checking move_node (over Leaf)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.move_node('/anarray', '/agroup/agroup3') # Now undo the past operation self.fileh.undo() # Check that it does not exist in the object tree self.assertTrue("/anarray" in self.fileh) self.assertTrue("/agroup/agroup3/anarray" not in self.fileh) self.assertEqual(self.fileh.root.anarray.title, "Array title") # Redo the operation self.fileh.redo() # Check that otherarray has come back to life in a sane state self.assertTrue("/anarray" not in self.fileh) self.assertTrue("/agroup/agroup3/anarray" in self.fileh) self.assertEqual(self.fileh.root.agroup.agroup3.anarray.title, "Array title") def test01(self): """Checking move_node (over Groups with children)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.move_node('/agroup', '/agroup2', 'agroup3') # Now undo the past operation self.fileh.undo() # Check that it does not exist in the object tree self.assertTrue("/agroup" in self.fileh) self.assertTrue("/agroup2/agroup3" not in self.fileh) # Check that children are reachable self.assertTrue("/agroup/anarray1" in self.fileh) self.assertTrue("/agroup/anarray2" in self.fileh) self.assertTrue("/agroup/agroup3" in self.fileh) self.assertEqual(self.fileh.root.agroup._v_title, "Group title") # Redo the operation self.fileh.redo() # Check that otherarray has come back to life in a sane state self.assertTrue("/agroup" not in self.fileh) self.assertTrue("/agroup2/agroup3" in self.fileh) self.assertEqual(self.fileh.root.agroup2.agroup3._v_title, "Group title") # Check that children are reachable self.assertTrue("/agroup2/agroup3/anarray1" in self.fileh) self.assertTrue("/agroup2/agroup3/anarray2" in self.fileh) self.assertTrue("/agroup2/agroup3/agroup3" in self.fileh) def test01b(self): """Checking move_node (over Groups with children 2)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01b..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.move_node('/agroup', '/', 'agroup3') self.fileh.move_node('/agroup3', '/agroup2', 'agroup4') # Now undo the past operation self.fileh.undo() # Check that it does not exist in the object tree self.assertTrue("/agroup" in self.fileh) self.assertTrue("/agroup2/agroup4" not in self.fileh) # Check that children are reachable self.assertTrue("/agroup/anarray1" in self.fileh) self.assertTrue("/agroup/anarray2" in self.fileh) self.assertTrue("/agroup/agroup3" in self.fileh) self.assertEqual(self.fileh.root.agroup._v_title, "Group title") # Redo the operation self.fileh.redo() # Check that otherarray has come back to life in a sane state self.assertTrue("/agroup" not in self.fileh) self.assertTrue("/agroup2/agroup4" in self.fileh) self.assertEqual(self.fileh.root.agroup2.agroup4._v_title, "Group title") # Check that children are reachable self.assertTrue("/agroup2/agroup4/anarray1" in self.fileh) self.assertTrue("/agroup2/agroup4/anarray2" in self.fileh) self.assertTrue("/agroup2/agroup4/agroup3" in self.fileh) def test02(self): """Checking move_node (over Leaves)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.move_node('/anarray', '/agroup2', 'anarray2') # Now undo the past operation self.fileh.undo() # Check that otherarray does not exist in the object tree self.assertTrue("/anarray" in self.fileh) self.assertTrue("/agroup2/anarray2" not in self.fileh) self.assertEqual(self.fileh.root.anarray.title, "Array title") # Redo the operation self.fileh.redo() # Check that otherarray has come back to life in a sane state self.assertTrue("/anarray" not in self.fileh) self.assertTrue("/agroup2/anarray2" in self.fileh) self.assertEqual(self.fileh.root.agroup2.anarray2.title, "Array title") def test03(self): """Checking move_node (over Tables)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.move_node('/table', '/agroup2', 'table2') # Now undo the past operation self.fileh.undo() # Check that table2 does not exist in the object tree self.assertTrue("/table" in self.fileh) self.assertTrue("/agroup2/table2" not in self.fileh) table = self.fileh.root.table self.assertTrue(table.cols.var1.index is not None) self.assertTrue(table.cols.var2.index is not None) self.assertTrue(table.cols.var3.index is not None) self.assertTrue(table.cols.var4.index is None) self.assertEqual(table.cols.var1.index.nelements, minRowIndex) self.assertEqual(table.cols.var2.index.nelements, minRowIndex) self.assertEqual(table.cols.var3.index.nelements, minRowIndex) self.assertEqual(self.fileh.root.table.title, "Indexed") # Redo the operation self.fileh.redo() # Check that table2 has come back to life in a sane state self.assertTrue("/table" not in self.fileh) self.assertTrue("/agroup2/table2" in self.fileh) self.assertEqual(self.fileh.root.agroup2.table2.title, "Indexed") table = self.fileh.root.agroup2.table2 self.assertTrue(table.cols.var1.index is not None) self.assertTrue(table.cols.var2.index is not None) self.assertTrue(table.cols.var3.index is not None) self.assertEqual(table.cols.var1.index.nelements, minRowIndex) self.assertEqual(table.cols.var2.index.nelements, minRowIndex) self.assertEqual(table.cols.var3.index.nelements, minRowIndex) self.assertTrue(table.cols.var4.index is None) class removeNodeTestCase(unittest.TestCase): "Test for remove_node operations" def setUp(self): # Create an HDF5 file # self.file = "/tmp/test.h5" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w", title="File title") fileh = self.fileh root = fileh.root # Create an array fileh.create_array(root, 'array', [1, 2], title="Title example") # Create another array object fileh.create_array(root, 'anarray', [1], "Array title") # Create a group object group = fileh.create_group(root, 'agroup', "Group title") # Create a couple of objects there fileh.create_array(group, 'anarray1', [2], "Array title 1") fileh.create_array(group, 'anarray2', [2], "Array title 2") # Create a lonely group in first level fileh.create_group(root, 'agroup2', "Group title 2") # Create a new group in the second level fileh.create_group(group, 'agroup3', "Group title 3") # Create a table in root populateTable(self.fileh.root, 'table') def tearDown(self): # Remove the temporary file self.fileh.close() os.remove(self.file) common.cleanup(self) def test00(self): """Checking remove_node (over Leaf)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Delete an existing array self.fileh.remove_node('/anarray') # Now undo the past operation self.fileh.undo() # Check that it does exist in the object tree self.assertTrue("/anarray" in self.fileh) self.assertEqual(self.fileh.root.anarray.title, "Array title") # Redo the operation self.fileh.redo() # Check that array has gone again self.assertTrue("/anarray" not in self.fileh) def test00b(self): """Checking remove_node (over several Leaves)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00b..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Delete a couple of arrays self.fileh.remove_node('/anarray') self.fileh.remove_node('/agroup/anarray2') # Now undo the past operation self.fileh.undo() # Check that arrays has come into life self.assertTrue("/anarray" in self.fileh) self.assertTrue("/agroup/anarray2" in self.fileh) self.assertEqual(self.fileh.root.anarray.title, "Array title") self.assertEqual( self.fileh.root.agroup.anarray2.title, "Array title 2") # Redo the operation self.fileh.redo() # Check that arrays has disappeared again self.assertTrue("/anarray" not in self.fileh) self.assertTrue("/agroup/anarray2" not in self.fileh) def test00c(self): """Checking remove_node (over Tables)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00c..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Create a new array self.fileh.remove_node('/table') # Now undo the past operation self.fileh.undo() # Check that table2 does not exist in the object tree self.assertTrue("/table" in self.fileh) table = self.fileh.root.table self.assertTrue(table.cols.var1.index is not None) self.assertTrue(table.cols.var2.index is not None) self.assertTrue(table.cols.var3.index is not None) self.assertTrue(table.cols.var4.index is None) self.assertEqual(table.cols.var1.index.nelements, minRowIndex) self.assertEqual(table.cols.var2.index.nelements, minRowIndex) self.assertEqual(table.cols.var3.index.nelements, minRowIndex) self.assertEqual(self.fileh.root.table.title, "Indexed") # Redo the operation self.fileh.redo() # Check that table2 has come back to life in a sane state self.assertTrue("/table" not in self.fileh) def test01(self): """Checking remove_node (over Groups with children)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Delete a group recursively self.fileh.remove_node('/agroup', recursive=1) # Now undo the past operation self.fileh.undo() # Check that parent and children has come into life in a sane state self.assertTrue("/agroup" in self.fileh) self.assertTrue("/agroup/anarray1" in self.fileh) self.assertTrue("/agroup/anarray2" in self.fileh) self.assertTrue("/agroup/agroup3" in self.fileh) self.assertEqual(self.fileh.root.agroup._v_title, "Group title") # Redo the operation self.fileh.redo() # Check that parent and children are not reachable self.assertTrue("/agroup" not in self.fileh) self.assertTrue("/agroup/anarray1" not in self.fileh) self.assertTrue("/agroup/anarray2" not in self.fileh) self.assertTrue("/agroup/agroup3" not in self.fileh) def test01b(self): """Checking remove_node (over Groups with children 2)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01b..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # Remove a couple of groups self.fileh.remove_node('/agroup', recursive=1) self.fileh.remove_node('/agroup2') # Now undo the past operation self.fileh.undo() # Check that they does exist in the object tree self.assertTrue("/agroup" in self.fileh) self.assertTrue("/agroup2" in self.fileh) # Check that children are reachable self.assertTrue("/agroup/anarray1" in self.fileh) self.assertTrue("/agroup/anarray2" in self.fileh) self.assertTrue("/agroup/agroup3" in self.fileh) self.assertEqual(self.fileh.root.agroup._v_title, "Group title") # Redo the operation self.fileh.redo() # Check that groups does not exist again self.assertTrue("/agroup" not in self.fileh) self.assertTrue("/agroup2" not in self.fileh) # Check that children are not reachable self.assertTrue("/agroup/anarray1" not in self.fileh) self.assertTrue("/agroup/anarray2" not in self.fileh) self.assertTrue("/agroup/agroup3" not in self.fileh) class copyNodeTestCase(unittest.TestCase): "Tests for copy_node and copy_children operations" def setUp(self): # Create an HDF5 file self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w", title="File title") fileh = self.fileh root = fileh.root # Create an array fileh.create_array(root, 'array', [1, 2], title="Title example") # Create another array object fileh.create_array(root, 'anarray', [1], "Array title") # Create a group object group = fileh.create_group(root, 'agroup', "Group title") # Create a couple of objects there fileh.create_array(group, 'anarray1', [2], "Array title 1") fileh.create_array(group, 'anarray2', [2], "Array title 2") # Create a lonely group in first level fileh.create_group(root, 'agroup2', "Group title 2") # Create a new group in the second level fileh.create_group(group, 'agroup3', "Group title 3") # Create a table in root populateTable(self.fileh.root, 'table') def tearDown(self): # Remove the temporary file self.fileh.close() os.remove(self.file) common.cleanup(self) def test00_copyLeaf(self): """Checking copy_node (over Leaves)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00_copyLeaf..." % self.__class__.__name__) # Enable undo/redo. self.fileh.enable_undo() # /anarray => /agroup/agroup3/ new_node = self.fileh.copy_node('/anarray', '/agroup/agroup3') # Undo the copy. self.fileh.undo() # Check that the copied node does not exist in the object tree. self.assertTrue('/agroup/agroup3/anarray' not in self.fileh) # Redo the copy. self.fileh.redo() # Check that the copied node exists again in the object tree. self.assertTrue('/agroup/agroup3/anarray' in self.fileh) self.assertTrue(self.fileh.root.agroup.agroup3.anarray is new_node) def test00b_copyTable(self): """Checking copy_node (over Tables)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00b_copyTable..." % self.__class__.__name__) # open the do/undo self.fileh.enable_undo() # /table => /agroup/agroup3/ warnings.filterwarnings("ignore", category=UserWarning) table = self.fileh.copy_node( '/table', '/agroup/agroup3', propindexes=True) warnings.filterwarnings("default", category=UserWarning) self.assertTrue("/agroup/agroup3/table" in self.fileh) table = self.fileh.root.agroup.agroup3.table self.assertEqual(table.title, "Indexed") self.assertTrue(table.cols.var1.index is not None) self.assertTrue(table.cols.var2.index is not None) self.assertTrue(table.cols.var3.index is not None) self.assertEqual(table.cols.var1.index.nelements, minRowIndex) self.assertEqual(table.cols.var2.index.nelements, minRowIndex) self.assertEqual(table.cols.var3.index.nelements, minRowIndex) self.assertTrue(table.cols.var4.index is None) # Now undo the past operation self.fileh.undo() table = self.fileh.root.table self.assertTrue(table.cols.var1.index is not None) self.assertTrue(table.cols.var2.index is not None) self.assertTrue(table.cols.var3.index is not None) self.assertTrue(table.cols.var4.index is None) self.assertEqual(table.cols.var1.index.nelements, minRowIndex) self.assertEqual(table.cols.var2.index.nelements, minRowIndex) self.assertEqual(table.cols.var3.index.nelements, minRowIndex) # Check that the copied node does not exist in the object tree. self.assertTrue("/agroup/agroup3/table" not in self.fileh) # Redo the operation self.fileh.redo() # Check that table has come back to life in a sane state self.assertTrue("/table" in self.fileh) self.assertTrue("/agroup/agroup3/table" in self.fileh) table = self.fileh.root.agroup.agroup3.table self.assertEqual(table.title, "Indexed") self.assertTrue(table.cols.var1.index is not None) self.assertTrue(table.cols.var2.index is not None) self.assertTrue(table.cols.var3.index is not None) self.assertEqual(table.cols.var1.index.nelements, minRowIndex) self.assertEqual(table.cols.var2.index.nelements, minRowIndex) self.assertEqual(table.cols.var3.index.nelements, minRowIndex) self.assertTrue(table.cols.var4.index is None) def test01_copyGroup(self): "Copying a group (recursively)." if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_copyGroup..." % self.__class__.__name__) # Enable undo/redo. self.fileh.enable_undo() # /agroup => /acopy new_node = self.fileh.copy_node( '/agroup', newname='acopy', recursive=True) # Undo the copy. self.fileh.undo() # Check that the copied node does not exist in the object tree. self.assertTrue('/acopy' not in self.fileh) self.assertTrue('/acopy/anarray1' not in self.fileh) self.assertTrue('/acopy/anarray2' not in self.fileh) self.assertTrue('/acopy/agroup3' not in self.fileh) # Redo the copy. self.fileh.redo() # Check that the copied node exists again in the object tree. self.assertTrue('/acopy' in self.fileh) self.assertTrue('/acopy/anarray1' in self.fileh) self.assertTrue('/acopy/anarray2' in self.fileh) self.assertTrue('/acopy/agroup3' in self.fileh) self.assertTrue(self.fileh.root.acopy is new_node) def test02_copyLeafOverwrite(self): "Copying a leaf, overwriting destination." if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_copyLeafOverwrite..." % self.__class__.__name__) # Enable undo/redo. self.fileh.enable_undo() # /anarray => /agroup/agroup oldNode = self.fileh.root.agroup new_node = self.fileh.copy_node( '/anarray', newname='agroup', overwrite=True) # Undo the copy. self.fileh.undo() # Check that the copied node does not exist in the object tree. # Check that the overwritten node exists again in the object tree. self.assertTrue(self.fileh.root.agroup is oldNode) # Redo the copy. self.fileh.redo() # Check that the copied node exists again in the object tree. # Check that the overwritten node does not exist in the object tree. self.assertTrue(self.fileh.root.agroup is new_node) def test03_copyChildren(self): "Copying the children of a group." if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_copyChildren..." % self.__class__.__name__) # Enable undo/redo. self.fileh.enable_undo() # /agroup/* => /agroup/ self.fileh.copy_children('/agroup', '/agroup2', recursive=True) # Undo the copy. self.fileh.undo() # Check that the copied nodes do not exist in the object tree. self.assertTrue('/agroup2/anarray1' not in self.fileh) self.assertTrue('/agroup2/anarray2' not in self.fileh) self.assertTrue('/agroup2/agroup3' not in self.fileh) # Redo the copy. self.fileh.redo() # Check that the copied nodes exist again in the object tree. self.assertTrue('/agroup2/anarray1' in self.fileh) self.assertTrue('/agroup2/anarray2' in self.fileh) self.assertTrue('/agroup2/agroup3' in self.fileh) class ComplexTestCase(unittest.TestCase): "Tests for a mix of all operations" def setUp(self): # Create an HDF5 file # self.file = "/tmp/test.h5" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w", title="File title") fileh = self.fileh root = fileh.root # Create an array fileh.create_array(root, 'array', [1, 2], title="Title example") # Create another array object fileh.create_array(root, 'anarray', [1], "Array title") # Create a group object group = fileh.create_group(root, 'agroup', "Group title") # Create a couple of objects there fileh.create_array(group, 'anarray1', [2], "Array title 1") fileh.create_array(group, 'anarray2', [2], "Array title 2") # Create a lonely group in first level fileh.create_group(root, 'agroup2', "Group title 2") # Create a new group in the second level fileh.create_group(group, 'agroup3', "Group title 3") def tearDown(self): # Remove the temporary file self.fileh.close() os.remove(self.file) common.cleanup(self) def test00(self): """Mix of create_array, create_group, renameNone, move_node, remove_node, copy_node and copy_children.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00..." % self.__class__.__name__) # Enable undo/redo. self.fileh.enable_undo() # Create an array self.fileh.create_array(self.fileh.root, 'anarray3', [1], "Array title 3") # Create a group self.fileh.create_group(self.fileh.root, 'agroup3', "Group title 3") # /anarray => /agroup/agroup3/ new_node = self.fileh.copy_node('/anarray3', '/agroup/agroup3') new_node = self.fileh.copy_children('/agroup', '/agroup3', recursive=1) # rename anarray self.fileh.rename_node('/anarray', 'anarray4') # Move anarray new_node = self.fileh.copy_node('/anarray3', '/agroup') # Remove anarray4 self.fileh.remove_node('/anarray4') # Undo the actions self.fileh.undo() self.assertTrue('/anarray4' not in self.fileh) self.assertTrue('/anarray3' not in self.fileh) self.assertTrue('/agroup/agroup3/anarray3' not in self.fileh) self.assertTrue('/agroup3' not in self.fileh) self.assertTrue('/anarray4' not in self.fileh) self.assertTrue('/anarray' in self.fileh) # Redo the actions self.fileh.redo() # Check that the copied node exists again in the object tree. self.assertTrue('/agroup/agroup3/anarray3' in self.fileh) self.assertTrue('/agroup/anarray3' in self.fileh) self.assertTrue('/agroup3/agroup3/anarray3' in self.fileh) self.assertTrue('/agroup3/anarray3' not in self.fileh) self.assertTrue(self.fileh.root.agroup.anarray3 is new_node) self.assertTrue('/anarray' not in self.fileh) self.assertTrue('/anarray4' not in self.fileh) def test01(self): "Test with multiple generations (Leaf case)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01..." % self.__class__.__name__) # Enable undo/redo. self.fileh.enable_undo() # remove /anarray self.fileh.remove_node('/anarray') # Create an array in the same place self.fileh.create_array(self.fileh.root, 'anarray', [2], "Array title 2") # remove the array again self.fileh.remove_node('/anarray') # Create an array self.fileh.create_array(self.fileh.root, 'anarray', [3], "Array title 3") # remove the array again self.fileh.remove_node('/anarray') # Create an array self.fileh.create_array(self.fileh.root, 'anarray', [4], "Array title 4") # Undo the actions self.fileh.undo() # Check that /anarray is in the correct state before redoing self.assertEqual(self.fileh.root.anarray.title, "Array title") self.assertEqual(self.fileh.root.anarray[:], [1]) # Redo the actions self.fileh.redo() self.assertEqual(self.fileh.root.anarray.title, "Array title 4") self.assertEqual(self.fileh.root.anarray[:], [4]) def test02(self): "Test with multiple generations (Group case)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02..." % self.__class__.__name__) # Enable undo/redo. self.fileh.enable_undo() # remove /agroup self.fileh.remove_node('/agroup2') # Create a group in the same place self.fileh.create_group(self.fileh.root, 'agroup2', "Group title 22") # remove the group self.fileh.remove_node('/agroup2') # Create a group self.fileh.create_group(self.fileh.root, 'agroup2', "Group title 3") # remove the group self.fileh.remove_node('/agroup2') # Create a group self.fileh.create_group(self.fileh.root, 'agroup2', "Group title 4") # Create a child group self.fileh.create_group(self.fileh.root.agroup2, 'agroup5', "Group title 5") # Undo the actions self.fileh.undo() # Check that /agroup is in the state before enabling do/undo self.assertEqual(self.fileh.root.agroup2._v_title, "Group title 2") self.assertTrue('/agroup2' in self.fileh) # Redo the actions self.fileh.redo() self.assertEqual(self.fileh.root.agroup2._v_title, "Group title 4") self.assertEqual(self.fileh.root.agroup2.agroup5._v_title, "Group title 5") def test03(self): "Test with multiple generations (Group case, recursive remove)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03..." % self.__class__.__name__) # Enable undo/redo. self.fileh.enable_undo() # remove /agroup self.fileh.remove_node('/agroup', recursive=1) # Create a group in the same place self.fileh.create_group(self.fileh.root, 'agroup', "Group title 2") # remove the group self.fileh.remove_node('/agroup') # Create a group self.fileh.create_group(self.fileh.root, 'agroup', "Group title 3") # remove the group self.fileh.remove_node('/agroup') # Create a group self.fileh.create_group(self.fileh.root, 'agroup', "Group title 4") # Create a child group self.fileh.create_group(self.fileh.root.agroup, 'agroup5', "Group title 5") # Undo the actions self.fileh.undo() # Check that /agroup is in the state before enabling do/undo self.assertTrue('/agroup' in self.fileh) self.assertEqual(self.fileh.root.agroup._v_title, "Group title") self.assertTrue('/agroup/anarray1' in self.fileh) self.assertTrue('/agroup/anarray2' in self.fileh) self.assertTrue('/agroup/agroup3' in self.fileh) self.assertTrue('/agroup/agroup5' not in self.fileh) # Redo the actions self.fileh.redo() self.assertTrue('/agroup' in self.fileh) self.assertEqual(self.fileh.root.agroup._v_title, "Group title 4") self.assertTrue('/agroup/agroup5' in self.fileh) self.assertEqual( self.fileh.root.agroup.agroup5._v_title, "Group title 5") def test03b(self): "Test with multiple generations (Group case, recursive remove, case 2)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03b..." % self.__class__.__name__) # Enable undo/redo. self.fileh.enable_undo() # Create a new group with a child self.fileh.create_group(self.fileh.root, 'agroup3', "Group title 3") self.fileh.create_group(self.fileh.root.agroup3, 'agroup4', "Group title 4") # remove /agroup3 self.fileh.remove_node('/agroup3', recursive=1) # Create a group in the same place self.fileh.create_group(self.fileh.root, 'agroup3', "Group title 4") # Undo the actions self.fileh.undo() # Check that /agroup is in the state before enabling do/undo self.assertTrue('/agroup3' not in self.fileh) # Redo the actions self.fileh.redo() self.assertEqual(self.fileh.root.agroup3._v_title, "Group title 4") self.assertTrue('/agroup3' in self.fileh) self.assertTrue('/agroup/agroup4' not in self.fileh) class AttributesTestCase(unittest.TestCase): "Tests for operation on attributes" def setUp(self): # Create an HDF5 file self.file = tempfile.mktemp(".h5") self.fileh = open_file( self.file, mode="w", title="Attribute operations") # Create an array. array = self.fileh.create_array('/', 'array', [1, 2]) # Set some attributes on it. attrs = array.attrs attrs.attr_1 = 10 attrs.attr_2 = 20 attrs.attr_3 = 30 def tearDown(self): # Remove the temporary file self.fileh.close() os.remove(self.file) common.cleanup(self) def test00_setAttr(self): "Setting a nonexistent attribute." if common.verbose: print('\n', '-=' * 30) print("Running %s.test00_setAttr..." % self.__class__.__name__) array = self.fileh.root.array attrs = array.attrs self.fileh.enable_undo() setattr(attrs, 'attr_0', 0) self.assertTrue('attr_0' in attrs) self.assertEqual(attrs.attr_0, 0) self.fileh.undo() self.assertTrue('attr_0' not in attrs) self.fileh.redo() self.assertTrue('attr_0' in attrs) self.assertEqual(attrs.attr_0, 0) def test01_setAttrExisting(self): "Setting an existing attribute." if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_setAttrExisting..." % self.__class__.__name__) array = self.fileh.root.array attrs = array.attrs self.fileh.enable_undo() setattr(attrs, 'attr_1', 11) self.assertTrue('attr_1' in attrs) self.assertEqual(attrs.attr_1, 11) self.fileh.undo() self.assertTrue('attr_1' in attrs) self.assertEqual(attrs.attr_1, 10) self.fileh.redo() self.assertTrue('attr_1' in attrs) self.assertEqual(attrs.attr_1, 11) def test02_delAttr(self): "Removing an attribute." if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_delAttr..." % self.__class__.__name__) array = self.fileh.root.array attrs = array.attrs self.fileh.enable_undo() delattr(attrs, 'attr_1') self.assertTrue('attr_1' not in attrs) self.fileh.undo() self.assertTrue('attr_1' in attrs) self.assertEqual(attrs.attr_1, 10) self.fileh.redo() self.assertTrue('attr_1' not in attrs) def test03_copyNodeAttrs(self): "Copying an attribute set." if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_copyNodeAttrs..." % self.__class__.__name__) rattrs = self.fileh.root._v_attrs rattrs.attr_0 = 0 rattrs.attr_1 = 100 array = self.fileh.root.array attrs = array.attrs self.fileh.enable_undo() attrs._f_copy(self.fileh.root) self.assertEqual(rattrs.attr_0, 0) self.assertEqual(rattrs.attr_1, 10) self.assertEqual(rattrs.attr_2, 20) self.assertEqual(rattrs.attr_3, 30) self.fileh.undo() self.assertEqual(rattrs.attr_0, 0) self.assertEqual(rattrs.attr_1, 100) self.assertTrue('attr_2' not in rattrs) self.assertTrue('attr_3' not in rattrs) self.fileh.redo() self.assertEqual(rattrs.attr_0, 0) self.assertEqual(rattrs.attr_1, 10) self.assertEqual(rattrs.attr_2, 20) self.assertEqual(rattrs.attr_3, 30) def test04_replaceNode(self): "Replacing a node with a rewritten attribute." if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_replaceNode..." % self.__class__.__name__) array = self.fileh.root.array attrs = array.attrs self.fileh.enable_undo() attrs.attr_1 = 11 self.fileh.remove_node('/array') arr = self.fileh.create_array('/', 'array', [1]) arr.attrs.attr_1 = 12 self.fileh.undo() self.assertTrue('attr_1' in self.fileh.root.array.attrs) self.assertEqual(self.fileh.root.array.attrs.attr_1, 10) self.fileh.redo() self.assertTrue('attr_1' in self.fileh.root.array.attrs) self.assertEqual(self.fileh.root.array.attrs.attr_1, 12) class NotLoggedTestCase(common.TempFileMixin, common.PyTablesTestCase): """Test not logged nodes.""" class NotLoggedArray(NotLoggedMixin, Array): pass def test00_hierarchy(self): """Performing hierarchy operations on a not logged node.""" self.h5file.create_group('/', 'tgroup') self.h5file.enable_undo() # Node creation is not undone. arr = self.NotLoggedArray(self.h5file.root, 'test', [1], self._getMethodName()) self.h5file.undo() self.assertTrue('/test' in self.h5file) # Node movement is not undone. arr.move('/tgroup') self.h5file.undo() self.assertTrue('/tgroup/test' in self.h5file) # Node removal is not undone. arr.remove() self.h5file.undo() self.assertTrue('/tgroup/test' not in self.h5file) def test01_attributes(self): """Performing attribute operations on a not logged node.""" arr = self.NotLoggedArray(self.h5file.root, 'test', [1], self._getMethodName()) self.h5file.enable_undo() # Attribute creation is not undone. arr._v_attrs.foo = 'bar' self.h5file.undo() self.assertEqual(arr._v_attrs.foo, 'bar') # Attribute change is not undone. arr._v_attrs.foo = 'baz' self.h5file.undo() self.assertEqual(arr._v_attrs.foo, 'baz') # Attribute removal is not undone. del arr._v_attrs.foo self.h5file.undo() self.assertRaises(AttributeError, getattr, arr._v_attrs, 'foo') class CreateParentsTestCase(common.TempFileMixin, common.PyTablesTestCase): """Test the ``createparents`` flag.""" def setUp(self): super(CreateParentsTestCase, self).setUp() g1 = self.h5file.create_group('/', 'g1') self.h5file.create_group(g1, 'g2') def existing(self, paths): """Return a set of the existing paths in `paths`.""" return frozenset(path for path in paths if path in self.h5file) def basetest(self, doit, pre, post): pre() self.h5file.enable_undo() paths = ['/g1', '/g1/g2', '/g1/g2/g3', '/g1/g2/g3/g4'] for newpath in paths: before = self.existing(paths) doit(newpath) after = self.existing(paths) self.assertTrue(after.issuperset(before)) self.h5file.undo() post(newpath) after = self.existing(paths) self.assertEqual(after, before) def test00_create(self): """Test creating a node.""" def pre(): pass def doit(newpath): self.h5file.create_array(newpath, 'array', [1], createparents=True) self.assertTrue(join_path(newpath, 'array') in self.h5file) def post(newpath): self.assertTrue(join_path(newpath, 'array') not in self.h5file) self.basetest(doit, pre, post) def test01_move(self): """Test moving a node.""" def pre(): self.h5file.create_array('/', 'array', [1]) def doit(newpath): self.h5file.move_node('/array', newpath, createparents=True) self.assertTrue('/array' not in self.h5file) self.assertTrue(join_path(newpath, 'array') in self.h5file) def post(newpath): self.assertTrue('/array' in self.h5file) self.assertTrue(join_path(newpath, 'array') not in self.h5file) self.basetest(doit, pre, post) def test02_copy(self): """Test copying a node.""" def pre(): self.h5file.create_array('/', 'array', [1]) def doit(newpath): self.h5file.copy_node('/array', newpath, createparents=True) self.assertTrue(join_path(newpath, 'array') in self.h5file) def post(newpath): self.assertTrue(join_path(newpath, 'array') not in self.h5file) self.basetest(doit, pre, post) def test03_copyChildren(self): """Test copying the children of a group.""" def pre(): g = self.h5file.create_group('/', 'group') self.h5file.create_array(g, 'array1', [1]) self.h5file.create_array(g, 'array2', [1]) def doit(newpath): self.h5file.copy_children('/group', newpath, createparents=True) self.assertTrue(join_path(newpath, 'array1') in self.h5file) self.assertTrue(join_path(newpath, 'array2') in self.h5file) def post(newpath): self.assertTrue(join_path(newpath, 'array1') not in self.h5file) self.assertTrue(join_path(newpath, 'array2') not in self.h5file) self.basetest(doit, pre, post) def suite(): theSuite = unittest.TestSuite() niter = 1 # common.heavy = 1 # uncomment this only for testing purposes for n in range(niter): theSuite.addTest(unittest.makeSuite(BasicTestCase)) theSuite.addTest(unittest.makeSuite(PersistenceTestCase)) theSuite.addTest(unittest.makeSuite(createArrayTestCase)) theSuite.addTest(unittest.makeSuite(createGroupTestCase)) theSuite.addTest(unittest.makeSuite(renameNodeTestCase)) theSuite.addTest(unittest.makeSuite(moveNodeTestCase)) theSuite.addTest(unittest.makeSuite(removeNodeTestCase)) theSuite.addTest(unittest.makeSuite(copyNodeTestCase)) theSuite.addTest(unittest.makeSuite(AttributesTestCase)) theSuite.addTest(unittest.makeSuite(ComplexTestCase)) theSuite.addTest(unittest.makeSuite(NotLoggedTestCase)) theSuite.addTest(unittest.makeSuite(CreateParentsTestCase)) if common.heavy: pass return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') ## Local Variables: ## mode: python ## End: PyTables-v.3.1.1/tables/tests/test_earray.py000066400000000000000000003162141231437614300210260ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import sys import unittest import os import tempfile import numpy from tables import * from tables.utils import byteorders from tables.tests import common from tables.tests.common import allequal # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup class BasicTestCase(unittest.TestCase): # Default values obj = None flavor = "numpy" type = 'int32' dtype = 'int32' shape = (2, 0) start = 0 stop = 10 step = 1 length = 1 chunksize = 5 nappends = 10 compress = 0 complib = "zlib" # Default compression library shuffle = 0 fletcher32 = 0 reopen = 1 # Tells whether the file has to be reopened on each test or not def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") self.rootgroup = self.fileh.root self.populateFile() if self.reopen: # Close the file self.fileh.close() def populateFile(self): group = self.rootgroup obj = self.obj if obj is None: if self.type == "string": atom = StringAtom(itemsize=self.length) else: atom = Atom.from_type(self.type) else: atom = None title = self.__class__.__name__ filters = Filters(complevel=self.compress, complib=self.complib, shuffle=self.shuffle, fletcher32=self.fletcher32) earray = self.fileh.create_earray(group, 'earray1', atom=atom, shape=self.shape, title=title, filters=filters, expectedrows=1, obj=obj) earray.flavor = self.flavor # Fill it with rows self.rowshape = list(earray.shape) if obj is not None: self.rowshape[0] = 0 self.objsize = self.length for i in self.rowshape: if i != 0: self.objsize *= i self.extdim = earray.extdim self.objsize *= self.chunksize self.rowshape[earray.extdim] = self.chunksize if self.type == "string": object = numpy.ndarray(buffer=b"a"*self.objsize, shape=self.rowshape, dtype="S%s" % earray.atom.itemsize) else: object = numpy.arange(self.objsize, dtype=earray.atom.dtype.base) object.shape = self.rowshape if common.verbose: if self.flavor == "numpy": print("Object to append -->", object) else: print("Object to append -->", repr(object)) for i in range(self.nappends): if self.type == "string": earray.append(object) else: earray.append(object * i) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def _get_shape(self): if self.shape is not None: shape = self.shape else: shape = numpy.asarray(self.obj).shape return shape def test00_attributes(self): if self.reopen: self.fileh = open_file(self.file, "r") obj = self.fileh.get_node("/earray1") shape = self._get_shape() shape = list(shape) shape[self.extdim] = self.chunksize * self.nappends if self.obj is not None: shape[self.extdim] += len(self.obj) shape = tuple(shape) self.assertEqual(obj.flavor, self.flavor) self.assertEqual(obj.shape, shape) self.assertEqual(obj.ndim, len(shape)) self.assertEqual(obj.nrows, shape[self.extdim]) self.assertEqual(obj.atom.type, self.type) def test01_iterEArray(self): """Checking enlargeable array iterator.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_iterEArray..." % self.__class__.__name__) # Create an instance of an HDF5 Table if self.reopen: self.fileh = open_file(self.file, "r") earray = self.fileh.get_node("/earray1") # Choose a small value for buffer size earray.nrowsinbuf = 3 if common.verbose: print("EArray descr:", repr(earray)) print("shape of read array ==>", earray.shape) print("reopening?:", self.reopen) # Build the array to do comparisons if self.type == "string": object_ = numpy.ndarray(buffer=b"a"*self.objsize, shape=self.rowshape, dtype="S%s" % earray.atom.itemsize) else: object_ = numpy.arange(self.objsize, dtype=earray.atom.dtype.base) object_.shape = self.rowshape object_ = object_.swapaxes(earray.extdim, 0) if self.obj is not None: initialrows = len(self.obj) else: initialrows = 0 shape = self._get_shape() # Read all the array for idx, row in enumerate(earray): if idx < initialrows: self.assertTrue( allequal(row, numpy.asarray(self.obj[idx]), self.flavor)) continue chunk = int((earray.nrow - initialrows) % self.chunksize) if chunk == 0: if self.type == "string": object__ = object_ else: i = int(earray.nrow - initialrows) object__ = object_ * (i // self.chunksize) object = object__[chunk] # The next adds much more verbosity if common.verbose and 0: print("number of row ==>", earray.nrow) if hasattr(object, "shape"): print("shape should look as:", object.shape) print("row in earray ==>", repr(row)) print("Should look like ==>", repr(object)) self.assertEqual(initialrows + self.nappends * self.chunksize, earray.nrows) self.assertTrue(allequal(row, object, self.flavor)) if hasattr(row, "shape"): self.assertEqual(len(row.shape), len(shape) - 1) else: # Scalar case self.assertEqual(len(shape), 1) # Check filters: if self.compress != earray.filters.complevel and common.verbose: print("Error in compress. Class:", self.__class__.__name__) print("self, earray:", self.compress, earray.filters.complevel) self.assertEqual(earray.filters.complevel, self.compress) if self.compress > 0 and which_lib_version(self.complib): self.assertEqual(earray.filters.complib, self.complib) if self.shuffle != earray.filters.shuffle and common.verbose: print("Error in shuffle. Class:", self.__class__.__name__) print("self, earray:", self.shuffle, earray.filters.shuffle) self.assertEqual(self.shuffle, earray.filters.shuffle) if self.fletcher32 != earray.filters.fletcher32 and common.verbose: print("Error in fletcher32. Class:", self.__class__.__name__) print("self, earray:", self.fletcher32, earray.filters.fletcher32) self.assertEqual(self.fletcher32, earray.filters.fletcher32) def test02_sssEArray(self): """Checking enlargeable array iterator with (start, stop, step)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_sssEArray..." % self.__class__.__name__) # Create an instance of an HDF5 Table if self.reopen: self.fileh = open_file(self.file, "r") earray = self.fileh.get_node("/earray1") # Choose a small value for buffer size earray.nrowsinbuf = 3 if common.verbose: print("EArray descr:", repr(earray)) print("shape of read array ==>", earray.shape) print("reopening?:", self.reopen) # Build the array to do comparisons if self.type == "string": object_ = numpy.ndarray(buffer=b"a"*self.objsize, shape=self.rowshape, dtype="S%s" % earray.atom.itemsize) else: object_ = numpy.arange(self.objsize, dtype=earray.atom.dtype.base) object_.shape = self.rowshape object_ = object_.swapaxes(earray.extdim, 0) if self.obj is not None: initialrows = len(self.obj) else: initialrows = 0 shape = self._get_shape() # Read all the array for idx, row in enumerate(earray.iterrows(start=self.start, stop=self.stop, step=self.step)): if idx < initialrows: self.assertTrue( allequal(row, numpy.asarray(self.obj[idx]), self.flavor)) continue if self.chunksize == 1: index = 0 else: index = int((earray.nrow - initialrows) % self.chunksize) if self.type == "string": object__ = object_ else: i = int(earray.nrow - initialrows) object__ = object_ * (i // self.chunksize) object = object__[index] # The next adds much more verbosity if common.verbose and 0: print("number of row ==>", earray.nrow) if hasattr(object, "shape"): print("shape should look as:", object.shape) print("row in earray ==>", repr(row)) print("Should look like ==>", repr(object)) self.assertEqual(initialrows + self.nappends * self.chunksize, earray.nrows) self.assertTrue(allequal(row, object, self.flavor)) if hasattr(row, "shape"): self.assertEqual(len(row.shape), len(shape) - 1) else: # Scalar case self.assertEqual(len(shape), 1) def test03_readEArray(self): """Checking read() of enlargeable arrays.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_readEArray..." % self.__class__.__name__) # This conversion made just in case indices are numpy scalars if self.start is not None: self.start = long(self.start) if self.stop is not None: self.stop = long(self.stop) if self.step is not None: self.step = long(self.step) # Create an instance of an HDF5 Table if self.reopen: self.fileh = open_file(self.file, "r") earray = self.fileh.get_node("/earray1") # Choose a small value for buffer size earray.nrowsinbuf = 3 if common.verbose: print("EArray descr:", repr(earray)) print("shape of read array ==>", earray.shape) print("reopening?:", self.reopen) # Build the array to do comparisons if self.type == "string": object_ = numpy.ndarray(buffer=b"a"*self.objsize, shape=self.rowshape, dtype="S%s" % earray.atom.itemsize) else: object_ = numpy.arange(self.objsize, dtype=earray.atom.dtype.base) object_.shape = self.rowshape object_ = object_.swapaxes(earray.extdim, 0) if self.obj is not None: initialrows = len(self.obj) else: initialrows = 0 rowshape = self.rowshape rowshape[self.extdim] *= (self.nappends + initialrows) if self.type == "string": object__ = numpy.empty( shape=rowshape, dtype="S%s" % earray.atom.itemsize) else: object__ = numpy.empty(shape=rowshape, dtype=self.dtype) object__ = object__.swapaxes(0, self.extdim) if initialrows: object__[0:initialrows] = self.obj for i in range(self.nappends): j = initialrows + i * self.chunksize if self.type == "string": object__[j:j + self.chunksize] = object_ else: object__[j:j + self.chunksize] = object_ * i stop = self.stop if self.nappends: # stop == None means read only the element designed by start # (in read() contexts) if self.stop is None: if self.start == -1: # corner case stop = earray.nrows else: stop = self.start + 1 # Protection against number of elements less than existing # if rowshape[self.extdim] < self.stop or self.stop == 0: if rowshape[self.extdim] < stop: # self.stop == 0 means last row only in read() # and not in [::] slicing notation stop = rowshape[self.extdim] # do a copy() in order to ensure that len(object._data) # actually do a measure of its length #object = object__[self.start:stop:self.step].copy() object = object__[self.start:self.stop:self.step].copy() # Swap the axes again to have normal ordering if self.flavor == "numpy": object = object.swapaxes(0, self.extdim) else: object = numpy.empty(shape=self.shape, dtype=self.dtype) # Read all the array try: row = earray.read(self.start, self.stop, self.step) except IndexError: row = numpy.empty(shape=self.shape, dtype=self.dtype) if common.verbose: if hasattr(object, "shape"): print("shape should look as:", object.shape) print("Object read ==>", repr(row)) print("Should look like ==>", repr(object)) self.assertEqual(initialrows + self.nappends * self.chunksize, earray.nrows) self.assertTrue(allequal(row, object, self.flavor)) shape = self._get_shape() if hasattr(row, "shape"): self.assertEqual(len(row.shape), len(shape)) if self.flavor == "numpy": self.assertEqual(row.itemsize, earray.atom.itemsize) else: # Scalar case self.assertEqual(len(shape), 1) def test03_readEArray_out_argument(self): """Checking read() of enlargeable arrays.""" # This conversion made just in case indices are numpy scalars if self.start is not None: self.start = long(self.start) if self.stop is not None: self.stop = long(self.stop) if self.step is not None: self.step = long(self.step) # Create an instance of an HDF5 Table if self.reopen: self.fileh = open_file(self.file, "r") earray = self.fileh.get_node("/earray1") # Choose a small value for buffer size earray.nrowsinbuf = 3 # Build the array to do comparisons if self.type == "string": object_ = numpy.ndarray(buffer=b"a"*self.objsize, shape=self.rowshape, dtype="S%s" % earray.atom.itemsize) else: object_ = numpy.arange(self.objsize, dtype=earray.atom.dtype.base) object_.shape = self.rowshape object_ = object_.swapaxes(earray.extdim, 0) if self.obj is not None: initialrows = len(self.obj) else: initialrows = 0 rowshape = self.rowshape rowshape[self.extdim] *= (self.nappends + initialrows) if self.type == "string": object__ = numpy.empty( shape=rowshape, dtype="S%s" % earray.atom.itemsize) else: object__ = numpy.empty(shape=rowshape, dtype=self.dtype) object__ = object__.swapaxes(0, self.extdim) if initialrows: object__[0:initialrows] = self.obj for i in range(self.nappends): j = initialrows + i * self.chunksize if self.type == "string": object__[j:j + self.chunksize] = object_ else: object__[j:j + self.chunksize] = object_ * i stop = self.stop if self.nappends: # stop == None means read only the element designed by start # (in read() contexts) if self.stop is None: if self.start == -1: # corner case stop = earray.nrows else: stop = self.start + 1 # Protection against number of elements less than existing # if rowshape[self.extdim] < self.stop or self.stop == 0: if rowshape[self.extdim] < stop: # self.stop == 0 means last row only in read() # and not in [::] slicing notation stop = rowshape[self.extdim] # do a copy() in order to ensure that len(object._data) # actually do a measure of its length #object = object__[self.start:stop:self.step].copy() object = object__[self.start:self.stop:self.step].copy() # Swap the axes again to have normal ordering if self.flavor == "numpy": object = object.swapaxes(0, self.extdim) else: object = numpy.empty(shape=self.shape, dtype=self.dtype) # Read all the array try: row = numpy.empty(earray.shape, dtype=earray.atom.dtype) slice_obj = [slice(None)] * len(earray.shape) #slice_obj[earray.maindim] = slice(self.start, stop, self.step) slice_obj[earray.maindim] = slice(self.start, self.stop, self.step) row = row[slice_obj].copy() earray.read(self.start, self.stop, self.step, out=row) except IndexError: row = numpy.empty(shape=self.shape, dtype=self.dtype) if common.verbose: if hasattr(object, "shape"): print("shape should look as:", object.shape) print("Object read ==>", repr(row)) print("Should look like ==>", repr(object)) self.assertEqual(initialrows + self.nappends * self.chunksize, earray.nrows) self.assertTrue(allequal(row, object, self.flavor)) shape = self._get_shape() if hasattr(row, "shape"): self.assertEqual(len(row.shape), len(shape)) if self.flavor == "numpy": self.assertEqual(row.itemsize, earray.atom.itemsize) else: # Scalar case self.assertEqual(len(shape), 1) def test04_getitemEArray(self): """Checking enlargeable array __getitem__ special method.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_getitemEArray..." % self.__class__.__name__) if not hasattr(self, "slices"): # If there is not a slices attribute, create it # This conversion made just in case indices are numpy scalars if self.start is not None: self.start = long(self.start) if self.stop is not None: self.stop = long(self.stop) if self.step is not None: self.step = long(self.step) self.slices = (slice(self.start, self.stop, self.step),) # Create an instance of an HDF5 Table if self.reopen: self.fileh = open_file(self.file, "r") earray = self.fileh.get_node("/earray1") # Choose a small value for buffer size # earray.nrowsinbuf = 3 # this does not really changes the chunksize if common.verbose: print("EArray descr:", repr(earray)) print("shape of read array ==>", earray.shape) print("reopening?:", self.reopen) # Build the array to do comparisons if self.type == "string": object_ = numpy.ndarray(buffer=b"a"*self.objsize, shape=self.rowshape, dtype="S%s" % earray.atom.itemsize) else: object_ = numpy.arange(self.objsize, dtype=earray.atom.dtype.base) object_.shape = self.rowshape object_ = object_.swapaxes(earray.extdim, 0) if self.obj is not None: initialrows = len(self.obj) else: initialrows = 0 rowshape = self.rowshape rowshape[self.extdim] *= (self.nappends + initialrows) if self.type == "string": object__ = numpy.empty( shape=rowshape, dtype="S%s" % earray.atom.itemsize) else: object__ = numpy.empty(shape=rowshape, dtype=self.dtype) # Additional conversion for the numpy case object__ = object__.swapaxes(0, earray.extdim) if initialrows: object__[0:initialrows] = self.obj for i in range(self.nappends): j = initialrows + i * self.chunksize if self.type == "string": object__[j:j + self.chunksize] = object_ else: object__[j:j + self.chunksize] = object_ * i if self.nappends: # Swap the axes again to have normal ordering if self.flavor == "numpy": object__ = object__.swapaxes(0, self.extdim) else: object__.swapaxes(0, self.extdim) # do a copy() in order to ensure that len(object._data) # actually do a measure of its length object = object__.__getitem__(self.slices).copy() else: object = numpy.empty(shape=self.shape, dtype=self.dtype) # Read all the array try: row = earray.__getitem__(self.slices) except IndexError: row = numpy.empty(shape=self.shape, dtype=self.dtype) if common.verbose: print("Object read:\n", repr(row)) print("Should look like:\n", repr(object)) if hasattr(object, "shape"): print("Original object shape:", self.shape) print("Shape read:", row.shape) print("shape should look as:", object.shape) self.assertEqual(initialrows + self.nappends * self.chunksize, earray.nrows) self.assertTrue(allequal(row, object, self.flavor)) if not hasattr(row, "shape"): # Scalar case self.assertEqual(len(self.shape), 1) def test05_setitemEArray(self): """Checking enlargeable array __setitem__ special method.""" if self.__class__.__name__ == "Ellipsis6EArrayTestCase": # We have a problem with test design here, but I think # it is not worth the effort to solve it # F.Alted 2004-10-27 return if common.verbose: print('\n', '-=' * 30) print("Running %s.test05_setitemEArray..." % self.__class__.__name__) if not hasattr(self, "slices"): # If there is not a slices attribute, create it # This conversion made just in case indices are numpy scalars if self.start is not None: self.start = long(self.start) if self.stop is not None: self.stop = long(self.stop) if self.step is not None: self.step = long(self.step) self.slices = (slice(self.start, self.stop, self.step),) # Create an instance of an HDF5 Table if self.reopen: self.fileh = open_file(self.file, "a") earray = self.fileh.get_node("/earray1") # Choose a small value for buffer size # earray.nrowsinbuf = 3 # this does not really changes the chunksize if common.verbose: print("EArray descr:", repr(earray)) print("shape of read array ==>", earray.shape) print("reopening?:", self.reopen) # Build the array to do comparisons if self.type == "string": object_ = numpy.ndarray(buffer=b"a"*self.objsize, shape=self.rowshape, dtype="S%s" % earray.atom.itemsize) else: object_ = numpy.arange(self.objsize, dtype=earray.atom.dtype.base) object_.shape = self.rowshape object_ = object_.swapaxes(earray.extdim, 0) if self.obj is not None: initialrows = len(self.obj) else: initialrows = 0 rowshape = self.rowshape rowshape[self.extdim] *= (self.nappends + initialrows) if self.type == "string": object__ = numpy.empty( shape=rowshape, dtype="S%s" % earray.atom.itemsize) else: object__ = numpy.empty(shape=rowshape, dtype=self.dtype) # Additional conversion for the numpy case object__ = object__.swapaxes(0, earray.extdim) for i in range(self.nappends): j = initialrows + i * self.chunksize if self.type == "string": object__[j:j + self.chunksize] = object_ else: object__[j:j + self.chunksize] = object_ * i # Modify the earray # earray[j:j + self.chunksize] = object_ * i # earray[self.slices] = 1 if initialrows: object__[0:initialrows] = self.obj if self.nappends: # Swap the axes again to have normal ordering if self.flavor == "numpy": object__ = object__.swapaxes(0, self.extdim) else: object__.swapaxes(0, self.extdim) # do a copy() in order to ensure that len(object._data) # actually do a measure of its length object = object__.__getitem__(self.slices).copy() else: object = numpy.empty(shape=self.shape, dtype=self.dtype) if self.flavor == "numpy": object = numpy.asarray(object) if self.type == "string": if hasattr(self, "wslice"): # These sentences should be equivalent # object[self.wslize] = object[self.wslice].pad("xXx") # earray[self.wslice] = earray[self.wslice].pad("xXx") object[self.wslize] = "xXx" earray[self.wslice] = "xXx" elif sum(object[self.slices].shape) != 0: # object[:] = object.pad("xXx") object[:] = "xXx" if object.size > 0: earray[self.slices] = object else: if hasattr(self, "wslice"): object[self.wslice] = object[self.wslice] * 2 + 3 earray[self.wslice] = earray[self.wslice] * 2 + 3 elif sum(object[self.slices].shape) != 0: object = object * 2 + 3 if numpy.prod(object.shape) > 0: earray[self.slices] = earray[self.slices] * 2 + 3 # Read all the array row = earray.__getitem__(self.slices) try: row = earray.__getitem__(self.slices) except IndexError: print("IndexError!") row = numpy.empty(shape=self.shape, dtype=self.dtype) if common.verbose: print("Object read:\n", repr(row)) print("Should look like:\n", repr(object)) if hasattr(object, "shape"): print("Original object shape:", self.shape) print("Shape read:", row.shape) print("shape should look as:", object.shape) self.assertEqual(initialrows + self.nappends * self.chunksize, earray.nrows) self.assertTrue(allequal(row, object, self.flavor)) if not hasattr(row, "shape"): # Scalar case self.assertEqual(len(self.shape), 1) class BasicWriteTestCase(BasicTestCase): type = 'int32' shape = (0,) chunksize = 5 nappends = 10 step = 1 # wslice = slice(1,nappends,2) wslice = 1 # single element case class Basic2WriteTestCase(BasicTestCase): type = 'int32' dtype = 'i4' shape = (0,) chunksize = 5 nappends = 10 step = 1 wslice = slice(chunksize-2, nappends, 2) # range of elements reopen = 0 # This case does not reopen files class Basic3WriteTestCase(BasicTestCase): obj = [1, 2] type = numpy.asarray(obj).dtype.name dtype = numpy.asarray(obj).dtype.str shape = (0,) chunkshape = (5,) step = 1 reopen = 0 # This case does not reopen files class Basic4WriteTestCase(BasicTestCase): obj = numpy.array([1, 2]) type = obj.dtype.name dtype = obj.dtype.str shape = None chunkshape = (5,) step = 1 reopen = 0 # This case does not reopen files class Basic5WriteTestCase(BasicTestCase): obj = [1, 2] type = numpy.asarray(obj).dtype.name dtype = numpy.asarray(obj).dtype.str shape = (0,) chunkshape = (5,) step = 1 reopen = 1 # This case does reopen files class Basic6WriteTestCase(BasicTestCase): obj = numpy.array([1, 2]) type = obj.dtype.name dtype = obj.dtype.str shape = None chunkshape = (5,) step = 1 reopen = 1 # This case does reopen files class Basic7WriteTestCase(BasicTestCase): obj = [[1, 2], [3, 4]] type = numpy.asarray(obj).dtype.name dtype = numpy.asarray(obj).dtype.str shape = (0, 2) chunkshape = (5,) step = 1 reopen = 0 # This case does not reopen files class Basic8WriteTestCase(BasicTestCase): obj = [[1, 2], [3, 4]] type = numpy.asarray(obj).dtype.name dtype = numpy.asarray(obj).dtype.str shape = (0, 2) chunkshape = (5,) step = 1 reopen = 1 # This case does reopen files class EmptyEArrayTestCase(BasicTestCase): type = 'int32' dtype = numpy.dtype('int32') shape = (2, 0) chunksize = 5 nappends = 0 start = 0 stop = 10 step = 1 class NP_EmptyEArrayTestCase(BasicTestCase): type = 'int32' dtype = numpy.dtype('()int32') shape = (2, 0) chunksize = 5 nappends = 0 class Empty2EArrayTestCase(BasicTestCase): type = 'int32' dtype = 'int32' shape = (2, 0) chunksize = 5 nappends = 0 start = 0 stop = 10 step = 1 reopen = 0 # This case does not reopen files class SlicesEArrayTestCase(BasicTestCase): compress = 1 complib = "lzo" type = 'int32' shape = (2, 0) chunksize = 5 nappends = 2 slices = (slice(1, 2, 1), slice(1, 3, 1)) class Slices2EArrayTestCase(BasicTestCase): compress = 1 complib = "blosc" type = 'int32' shape = (2, 0, 4) chunksize = 5 nappends = 20 slices = (slice(1, 2, 1), slice(None, None, None), slice(1, 4, 2)) class EllipsisEArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 0) chunksize = 5 nappends = 2 # slices = (slice(1,2,1), Ellipsis) slices = (Ellipsis, slice(1, 2, 1)) class Ellipsis2EArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 0, 4) chunksize = 5 nappends = 20 slices = (slice(1, 2, 1), Ellipsis, slice(1, 4, 2)) class Slices3EArrayTestCase(BasicTestCase): compress = 1 # To show the chunks id DEBUG is on complib = "blosc" type = 'int32' shape = (2, 3, 4, 0) chunksize = 5 nappends = 20 slices = (slice(1, 2, 1), slice(0, None, None), slice(1, 4, 2)) # Don't work # slices = (slice(None, None, None), slice(0, None, None), # slice(1,4,1)) # W # slices = (slice(None, None, None), slice(None, None, None), # slice(1,4,2)) # N # slices = (slice(1,2,1), slice(None, None, None), slice(1,4,2)) # N # Disable the failing test temporarily with a working test case slices = (slice(1, 2, 1), slice(1, 4, None), slice(1, 4, 2)) # Y # slices = (slice(1,2,1), slice(0, 4, None), slice(1,4,1)) # Y slices = (slice(1, 2, 1), slice(0, 4, None), slice(1, 4, 2)) # N # slices = (slice(1,2,1), slice(0, 4, None), slice(1,4,2), # slice(0,100,1)) # N class Slices4EArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 4, 0, 5, 6) chunksize = 5 nappends = 20 slices = (slice(1, 2, 1), slice(0, None, None), slice(1, 4, 2), slice(0, 4, 2), slice(3, 5, 2), slice(2, 7, 1)) class Ellipsis3EArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 4, 0) chunksize = 5 nappends = 20 slices = (Ellipsis, slice(0, 4, None), slice(1, 4, 2)) slices = (slice(1, 2, 1), slice(0, 4, None), slice(1, 4, 2), Ellipsis) class Ellipsis4EArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 4, 0) chunksize = 5 nappends = 20 slices = (Ellipsis, slice(0, 4, None), slice(1, 4, 2)) slices = (slice(1, 2, 1), Ellipsis, slice(1, 4, 2)) class Ellipsis5EArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 4, 0) chunksize = 5 nappends = 20 slices = (slice(1, 2, 1), slice(0, 4, None), Ellipsis) class Ellipsis6EArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 4, 0) chunksize = 5 nappends = 2 # The next slices gives problems with setting values (test05) # This is a problem on the test design, not the Array.__setitem__ # code, though. slices = (slice(1, 2, 1), slice(0, 4, None), 2, Ellipsis) class Ellipsis7EArrayTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 4, 0) chunksize = 5 nappends = 2 slices = (slice(1, 2, 1), slice(0, 4, None), slice(2, 3), Ellipsis) class MD3WriteTestCase(BasicTestCase): type = 'int32' shape = (2, 0, 3) chunksize = 4 step = 2 class MD5WriteTestCase(BasicTestCase): type = 'int32' shape = (2, 0, 3, 4, 5) # ok # shape = (1, 1, 0, 1) # Minimum shape that shows problems with HDF5 1.6.1 # shape = (2, 3, 0, 4, 5) # Floating point exception (HDF5 1.6.1) # shape = (2, 3, 3, 0, 5, 6) # Segmentation fault (HDF5 1.6.1) chunksize = 1 nappends = 1 start = 1 stop = 10 step = 10 class MD6WriteTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 3, 0, 5, 6) chunksize = 1 nappends = 10 start = 1 stop = 10 step = 3 class NP_MD6WriteTestCase(BasicTestCase): "Testing NumPy scalars as indexes" type = 'int32' shape = (2, 3, 3, 0, 5, 6) chunksize = 1 nappends = 10 class MD6WriteTestCase__(BasicTestCase): type = 'int32' shape = (2, 0) chunksize = 1 nappends = 3 start = 1 stop = 3 step = 1 class MD7WriteTestCase(BasicTestCase): type = 'int32' shape = (2, 3, 3, 4, 5, 0, 3) chunksize = 10 nappends = 1 start = 1 stop = 10 step = 2 class MD10WriteTestCase(BasicTestCase): type = 'int32' shape = (1, 2, 3, 4, 5, 5, 4, 3, 2, 0) chunksize = 5 nappends = 10 start = -1 stop = -1 step = 10 class NP_MD10WriteTestCase(BasicTestCase): type = 'int32' shape = (1, 2, 3, 4, 5, 5, 4, 3, 2, 0) chunksize = 5 nappends = 10 class ZlibComprTestCase(BasicTestCase): compress = 1 complib = "zlib" start = 3 # stop = 0 # means last row stop = None # means last row from 0.8 on step = 10 class ZlibShuffleTestCase(BasicTestCase): shuffle = 1 compress = 1 complib = "zlib" # case start < stop , i.e. no rows read start = 3 stop = 1 step = 10 class BloscComprTestCase(BasicTestCase): compress = 1 # sss complib = "blosc" chunksize = 10 nappends = 100 start = 3 stop = 10 step = 3 class BloscShuffleTestCase(BasicTestCase): compress = 1 shuffle = 1 complib = "blosc" chunksize = 100 nappends = 10 start = 3 stop = 10 step = 7 class LZOComprTestCase(BasicTestCase): compress = 1 # sss complib = "lzo" chunksize = 10 nappends = 100 start = 3 stop = 10 step = 3 class LZOShuffleTestCase(BasicTestCase): compress = 1 shuffle = 1 complib = "lzo" chunksize = 100 nappends = 10 start = 3 stop = 10 step = 7 class Bzip2ComprTestCase(BasicTestCase): compress = 1 complib = "bzip2" chunksize = 100 nappends = 10 start = 3 stop = 10 step = 8 class Bzip2ShuffleTestCase(BasicTestCase): compress = 1 shuffle = 1 complib = "bzip2" chunksize = 100 nappends = 10 start = 3 stop = 10 step = 6 class Fletcher32TestCase(BasicTestCase): compress = 0 fletcher32 = 1 chunksize = 50 nappends = 20 start = 4 stop = 20 step = 7 class AllFiltersTestCase(BasicTestCase): compress = 1 shuffle = 1 fletcher32 = 1 complib = "zlib" chunksize = 20 # sss nappends = 50 start = 2 stop = 99 step = 6 # chunksize = 3 # nappends = 2 # start = 1 # stop = 10 # step = 2 class FloatTypeTestCase(BasicTestCase): type = 'float64' dtype = 'float64' shape = (2, 0) chunksize = 5 nappends = 10 start = 3 stop = 10 step = 20 class ComplexTypeTestCase(BasicTestCase): type = 'complex128' dtype = 'complex128' shape = (2, 0) chunksize = 5 nappends = 10 start = 3 stop = 10 step = 20 class StringTestCase(BasicTestCase): type = "string" length = 20 shape = (2, 0) # shape = (2,0,20) chunksize = 5 nappends = 10 start = 3 stop = 10 step = 20 slices = (slice(0, 1), slice(1, 2)) class String2TestCase(BasicTestCase): type = "string" length = 20 shape = (0,) # shape = (0, 20) chunksize = 5 nappends = 10 start = 1 stop = 10 step = 2 class StringComprTestCase(BasicTestCase): type = "string" length = 20 shape = (20, 0, 10) # shape = (20,0,10,20) compr = 1 # shuffle = 1 # this shouldn't do nothing on chars chunksize = 50 nappends = 10 start = -1 stop = 100 step = 20 class SizeOnDiskInMemoryPropertyTestCase(unittest.TestCase): def setUp(self): self.array_size = (0, 10) # set chunkshape so it divides evenly into array_size, to avoid # partially filled chunks self.chunkshape = (1000, 10) # approximate size (in bytes) of non-data portion of hdf5 file self.hdf_overhead = 6000 self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w") def tearDown(self): self.fileh.close() # Then, delete the file os.remove(self.file) common.cleanup(self) def create_array(self, complevel): filters = Filters(complevel=complevel, complib='blosc') self.array = self.fileh.create_earray('/', 'earray', atom=Int32Atom(), shape=self.array_size, filters=filters, chunkshape=self.chunkshape) def test_zero_length(self): complevel = 0 self.create_array(complevel) self.assertEqual(self.array.size_on_disk, 0) self.assertEqual(self.array.size_in_memory, 0) # add 10 chunks of data in one append def test_no_compression_one_append(self): complevel = 0 self.create_array(complevel) self.array.append([tuple(range(10))] * self.chunkshape[0] * 10) self.assertEqual(self.array.size_on_disk, 10 * 1000 * 10 * 4) self.assertEqual(self.array.size_in_memory, 10 * 1000 * 10 * 4) # add 10 chunks of data in two appends def test_no_compression_multiple_appends(self): complevel = 0 self.create_array(complevel) self.array.append([tuple(range(10))] * self.chunkshape[0] * 5) self.array.append([tuple(range(10))] * self.chunkshape[0] * 5) self.assertEqual(self.array.size_on_disk, 10 * 1000 * 10 * 4) self.assertEqual(self.array.size_in_memory, 10 * 1000 * 10 * 4) def test_with_compression(self): complevel = 1 self.create_array(complevel) self.array.append([tuple(range(10))] * self.chunkshape[0] * 10) file_size = os.stat(self.file).st_size self.assertTrue( abs(self.array.size_on_disk - file_size) <= self.hdf_overhead) self.assertEqual(self.array.size_in_memory, 10 * 1000 * 10 * 4) self.assertTrue(self.array.size_on_disk < self.array.size_in_memory) class OffsetStrideTestCase(unittest.TestCase): mode = "w" compress = 0 complib = "zlib" # Default compression library def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, self.mode) self.rootgroup = self.fileh.root def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test01a_String(self): """Checking earray with offseted numpy strings appends.""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test01a_StringAtom..." % self.__class__.__name__) earray = self.fileh.create_earray(root, 'strings', atom=StringAtom(itemsize=3), shape=(0, 2, 2), title="Array of strings") a = numpy.array([[["a", "b"], [ "123", "45"], ["45", "123"]]], dtype="S3") earray.append(a[:, 1:]) a = numpy.array([[["s", "a"], [ "ab", "f"], ["s", "abc"], ["abc", "f"]]]) earray.append(a[:, 2:]) # Read all the rows: row = earray.read() if common.verbose: print("Object read:", row) print("Nrows in", earray._v_pathname, ":", earray.nrows) print("Second row in earray ==>", row[1].tolist()) self.assertEqual(earray.nrows, 2) self.assertEqual(row[0].tolist(), [[b"123", b"45"], [b"45", b"123"]]) self.assertEqual(row[1].tolist(), [[b"s", b"abc"], [b"abc", b"f"]]) self.assertEqual(len(row[0]), 2) self.assertEqual(len(row[1]), 2) def test01b_String(self): """Checking earray with strided numpy strings appends.""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test01b_StringAtom..." % self.__class__.__name__) earray = self.fileh.create_earray(root, 'strings', atom=StringAtom(itemsize=3), shape=(0, 2, 2), title="Array of strings") a = numpy.array([[["a", "b"], [ "123", "45"], ["45", "123"]]], dtype="S3") earray.append(a[:, ::2]) a = numpy.array([[["s", "a"], [ "ab", "f"], ["s", "abc"], ["abc", "f"]]]) earray.append(a[:, ::2]) # Read all the rows: row = earray.read() if common.verbose: print("Object read:", row) print("Nrows in", earray._v_pathname, ":", earray.nrows) print("Second row in earray ==>", row[1].tolist()) self.assertEqual(earray.nrows, 2) self.assertEqual(row[0].tolist(), [[b"a", b"b"], [b"45", b"123"]]) self.assertEqual(row[1].tolist(), [[b"s", b"a"], [b"s", b"abc"]]) self.assertEqual(len(row[0]), 2) self.assertEqual(len(row[1]), 2) def test02a_int(self): """Checking earray with offseted NumPy ints appends.""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test02a_int..." % self.__class__.__name__) # Create an string atom earray = self.fileh.create_earray(root, 'EAtom', atom=Int32Atom(), shape=(0, 3), title="array of ints") a = numpy.array([(0, 0, 0), (1, 0, 3), ( 1, 1, 1), (0, 0, 0)], dtype='int32') earray.append(a[2:]) # Create an offset a = numpy.array([(1, 1, 1), (-1, 0, 0)], dtype='int32') earray.append(a[1:]) # Create an offset # Read all the rows: row = earray.read() if common.verbose: print("Object read:", row) print("Nrows in", earray._v_pathname, ":", earray.nrows) print("Third row in vlarray ==>", row[2]) self.assertEqual(earray.nrows, 3) self.assertTrue(allequal(row[ 0], numpy.array([1, 1, 1], dtype='int32'))) self.assertTrue(allequal(row[ 1], numpy.array([0, 0, 0], dtype='int32'))) self.assertTrue(allequal(row[ 2], numpy.array([-1, 0, 0], dtype='int32'))) def test02b_int(self): """Checking earray with strided NumPy ints appends.""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test02b_int..." % self.__class__.__name__) earray = self.fileh.create_earray(root, 'EAtom', atom=Int32Atom(), shape=(0, 3), title="array of ints") a = numpy.array([(0, 0, 0), (1, 0, 3), ( 1, 1, 1), (3, 3, 3)], dtype='int32') earray.append(a[::3]) # Create an offset a = numpy.array([(1, 1, 1), (-1, 0, 0)], dtype='int32') earray.append(a[::2]) # Create an offset # Read all the rows: row = earray.read() if common.verbose: print("Object read:", row) print("Nrows in", earray._v_pathname, ":", earray.nrows) print("Third row in vlarray ==>", row[2]) self.assertEqual(earray.nrows, 3) self.assertTrue(allequal(row[ 0], numpy.array([0, 0, 0], dtype='int32'))) self.assertTrue(allequal(row[ 1], numpy.array([3, 3, 3], dtype='int32'))) self.assertTrue(allequal(row[ 2], numpy.array([1, 1, 1], dtype='int32'))) def test03a_int(self): """Checking earray with byteswapped appends (ints)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test03a_int..." % self.__class__.__name__) earray = self.fileh.create_earray(root, 'EAtom', atom=Int32Atom(), shape=(0, 3), title="array of ints") # Add a native ordered array a = numpy.array([(0, 0, 0), (1, 0, 3), ( 1, 1, 1), (3, 3, 3)], dtype='Int32') earray.append(a) # Change the byteorder of the array a = a.byteswap() a = a.newbyteorder() # Add a byteswapped array earray.append(a) # Read all the rows: native = earray[:4, :] swapped = earray[4:, :] if common.verbose: print("Native rows:", native) print("Byteorder native rows:", native.dtype.byteorder) print("Swapped rows:", swapped) print("Byteorder swapped rows:", swapped.dtype.byteorder) self.assertTrue(allequal(native, swapped)) def test03b_float(self): """Checking earray with byteswapped appends (floats)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test03b_float..." % self.__class__.__name__) earray = self.fileh.create_earray(root, 'EAtom', atom=Float64Atom(), shape=(0, 3), title="array of floats") # Add a native ordered array a = numpy.array([(0, 0, 0), (1, 0, 3), ( 1, 1, 1), (3, 3, 3)], dtype='Float64') earray.append(a) # Change the byteorder of the array a = a.byteswap() a = a.newbyteorder() # Add a byteswapped array earray.append(a) # Read all the rows: native = earray[:4, :] swapped = earray[4:, :] if common.verbose: print("Native rows:", native) print("Byteorder native rows:", native.dtype.byteorder) print("Swapped rows:", swapped) print("Byteorder swapped rows:", swapped.dtype.byteorder) self.assertTrue(allequal(native, swapped)) def test04a_int(self): """Checking earray with byteswapped appends (2, ints)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test04a_int..." % self.__class__.__name__) byteorder = {'little': 'big', 'big': 'little'}[sys.byteorder] earray = self.fileh.create_earray(root, 'EAtom', atom=Int32Atom(), shape=(0, 3), title="array of ints", byteorder=byteorder) # Add a native ordered array a = numpy.array([(0, 0, 0), (1, 0, 3), ( 1, 1, 1), (3, 3, 3)], dtype='Int32') earray.append(a) # Change the byteorder of the array a = a.byteswap() a = a.newbyteorder() # Add a byteswapped array earray.append(a) # Read all the rows: native = earray[:4, :] swapped = earray[4:, :] if common.verbose: print("Byteorder native rows:", byteorders[native.dtype.byteorder]) print("Byteorder earray on-disk:", earray.byteorder) self.assertEqual(byteorders[native.dtype.byteorder], sys.byteorder) self.assertEqual(earray.byteorder, byteorder) self.assertTrue(allequal(native, swapped)) def test04b_int(self): """Checking earray with byteswapped appends (2, ints, reopen)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test04b_int..." % self.__class__.__name__) byteorder = {'little': 'big', 'big': 'little'}[sys.byteorder] earray = self.fileh.create_earray(root, 'EAtom', atom=Int32Atom(), shape=(0, 3), title="array of ints", byteorder=byteorder) self.fileh.close() self.fileh = open_file(self.file, "a") earray = self.fileh.get_node("/EAtom") # Add a native ordered array a = numpy.array([(0, 0, 0), (1, 0, 3), ( 1, 1, 1), (3, 3, 3)], dtype='Int32') earray.append(a) # Change the byteorder of the array a = a.byteswap() a = a.newbyteorder() # Add a byteswapped array earray.append(a) # Read all the rows: native = earray[:4, :] swapped = earray[4:, :] if common.verbose: print("Byteorder native rows:", byteorders[native.dtype.byteorder]) print("Byteorder earray on-disk:", earray.byteorder) self.assertEqual(byteorders[native.dtype.byteorder], sys.byteorder) self.assertEqual(earray.byteorder, byteorder) self.assertTrue(allequal(native, swapped)) def test04c_float(self): """Checking earray with byteswapped appends (2, floats)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test04c_float..." % self.__class__.__name__) byteorder = {'little': 'big', 'big': 'little'}[sys.byteorder] earray = self.fileh.create_earray(root, 'EAtom', atom=Float64Atom(), shape=(0, 3), title="array of floats", byteorder=byteorder) # Add a native ordered array a = numpy.array([(0, 0, 0), (1, 0, 3), ( 1, 1, 1), (3, 3, 3)], dtype='Float64') earray.append(a) # Change the byteorder of the array a = a.byteswap() a = a.newbyteorder() # Add a byteswapped array earray.append(a) # Read all the rows: native = earray[:4, :] swapped = earray[4:, :] if common.verbose: print("Byteorder native rows:", byteorders[native.dtype.byteorder]) print("Byteorder earray on-disk:", earray.byteorder) self.assertEqual(byteorders[native.dtype.byteorder], sys.byteorder) self.assertEqual(earray.byteorder, byteorder) self.assertTrue(allequal(native, swapped)) def test04d_float(self): """Checking earray with byteswapped appends (2, floats, reopen)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test04d_float..." % self.__class__.__name__) byteorder = {'little': 'big', 'big': 'little'}[sys.byteorder] earray = self.fileh.create_earray(root, 'EAtom', atom=Float64Atom(), shape=(0, 3), title="array of floats", byteorder=byteorder) self.fileh.close() self.fileh = open_file(self.file, "a") earray = self.fileh.get_node("/EAtom") # Add a native ordered array a = numpy.array([(0, 0, 0), (1, 0, 3), ( 1, 1, 1), (3, 3, 3)], dtype='Float64') earray.append(a) # Change the byteorder of the array a = a.byteswap() a = a.newbyteorder() # Add a byteswapped array earray.append(a) # Read all the rows: native = earray[:4, :] swapped = earray[4:, :] if common.verbose: print("Byteorder native rows:", byteorders[native.dtype.byteorder]) print("Byteorder earray on-disk:", earray.byteorder) self.assertEqual(byteorders[native.dtype.byteorder], sys.byteorder) self.assertEqual(earray.byteorder, byteorder) self.assertTrue(allequal(native, swapped)) class CopyTestCase(unittest.TestCase): def test01_copy(self): """Checking EArray.copy() method.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an EArray atom = Int16Atom() array1 = fileh.create_earray(fileh.root, 'array1', atom=atom, shape=(0, 2), title="title array1") array1.append(numpy.array([[456, 2], [3, 457]], dtype='Int16')) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another location array2 = array1.copy('/', 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("array1-->", array1.read()) print("array2-->", array2.read()) # print("dirs-->", dir(array1), dir(array2)) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal self.assertTrue(allequal(array1.read(), array2.read())) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.extdim, array2.extdim) self.assertEqual(array1.flavor, array2.flavor) self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(array1.atom.type, array2.atom.type) self.assertEqual(array1.atom.itemsize, array2.atom.itemsize) self.assertEqual(array1.title, array2.title) self.assertEqual(str(array1.atom), str(array2.atom)) # Close the file fileh.close() os.remove(file) def test02_copy(self): """Checking EArray.copy() method (where specified)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an EArray atom = Int16Atom() array1 = fileh.create_earray(fileh.root, 'array1', atom=atom, shape=(0, 2), title="title array1") array1.append(numpy.array([[456, 2], [3, 457]], dtype='Int16')) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy to another location group1 = fileh.create_group("/", "group1") array2 = array1.copy(group1, 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.group1.array2 if common.verbose: print("array1-->", array1.read()) print("array2-->", array2.read()) # print("dirs-->", dir(array1), dir(array2)) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal self.assertTrue(allequal(array1.read(), array2.read())) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.extdim, array2.extdim) self.assertEqual(array1.flavor, array2.flavor) self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(array1.atom.type, array2.atom.type) self.assertEqual(array1.atom.itemsize, array2.atom.itemsize) self.assertEqual(array1.title, array2.title) self.assertEqual(str(array1.atom), str(array2.atom)) # Close the file fileh.close() os.remove(file) def test03a_copy(self): """Checking EArray.copy() method (python flavor)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03b_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") atom = Int16Atom() array1 = fileh.create_earray(fileh.root, 'array1', atom=atom, shape=(0, 2), title="title array1") array1.flavor = "python" array1.append(((456, 2), (3, 457))) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy to another location array2 = array1.copy('/', 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all elements are equal self.assertEqual(array1.read(), array2.read()) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.extdim, array2.extdim) self.assertEqual(array1.flavor, array2.flavor) # Very important here! self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(array1.atom.type, array2.atom.type) self.assertEqual(array1.atom.itemsize, array2.atom.itemsize) self.assertEqual(array1.title, array2.title) self.assertEqual(str(array1.atom), str(array2.atom)) # Close the file fileh.close() os.remove(file) def test03b_copy(self): """Checking EArray.copy() method (python string flavor)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03d_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") atom = StringAtom(itemsize=3) array1 = fileh.create_earray(fileh.root, 'array1', atom=atom, shape=(0, 2), title="title array1") array1.flavor = "python" array1.append([["456", "2"], ["3", "457"]]) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy to another location array2 = array1.copy('/', 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all elements are equal self.assertEqual(array1.read(), array2.read()) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.extdim, array2.extdim) self.assertEqual(array1.flavor, array2.flavor) # Very important here! self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(array1.atom.type, array2.atom.type) self.assertEqual(array1.atom.itemsize, array2.atom.itemsize) self.assertEqual(array1.title, array2.title) self.assertEqual(str(array1.atom), str(array2.atom)) # Close the file fileh.close() os.remove(file) def test03c_copy(self): """Checking EArray.copy() method (String flavor)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03e_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") atom = StringAtom(itemsize=4) array1 = fileh.create_earray(fileh.root, 'array1', atom=atom, shape=(0, 2), title="title array1") array1.flavor = "numpy" array1.append(numpy.array([["456", "2"], ["3", "457"]], dtype="S4")) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy to another location array2 = array1.copy('/', 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all elements are equal self.assertTrue(allequal(array1.read(), array2.read())) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.extdim, array2.extdim) self.assertEqual(array1.flavor, array2.flavor) # Very important here! self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(array1.atom.type, array2.atom.type) self.assertEqual(array1.atom.itemsize, array2.atom.itemsize) self.assertEqual(array1.title, array2.title) self.assertEqual(str(array1.atom), str(array2.atom)) # Close the file fileh.close() os.remove(file) def test04_copy(self): """Checking EArray.copy() method (checking title copying)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an EArray atom = Int16Atom() array1 = fileh.create_earray(fileh.root, 'array1', atom=atom, shape=(0, 2), title="title array1") array1.append(numpy.array([[456, 2], [3, 457]], dtype='Int16')) # Append some user attrs array1.attrs.attr1 = "attr1" array1.attrs.attr2 = 2 if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another Array array2 = array1.copy('/', 'array2', title="title array2") if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 # Assert user attributes if common.verbose: print("title of destination array-->", array2.title) self.assertEqual(array2.title, "title array2") # Close the file fileh.close() os.remove(file) def test05_copy(self): """Checking EArray.copy() method (user attributes copied)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an EArray atom = Int16Atom() array1 = fileh.create_earray(fileh.root, 'array1', atom=atom, shape=(0, 2), title="title array1") array1.append(numpy.array([[456, 2], [3, 457]], dtype='Int16')) # Append some user attrs array1.attrs.attr1 = "attr1" array1.attrs.attr2 = 2 if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another Array array2 = array1.copy('/', 'array2', copyuserattrs=1) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Assert user attributes self.assertEqual(array2.attrs.attr1, "attr1") self.assertEqual(array2.attrs.attr2, 2) # Close the file fileh.close() os.remove(file) def test05b_copy(self): """Checking EArray.copy() method (user attributes not copied)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05b_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an Array atom = Int16Atom() array1 = fileh.create_earray(fileh.root, 'array1', atom=atom, shape=(0, 2), title="title array1") array1.append(numpy.array([[456, 2], [3, 457]], dtype='Int16')) # Append some user attrs array1.attrs.attr1 = "attr1" array1.attrs.attr2 = 2 if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another Array array2 = array1.copy('/', 'array2', copyuserattrs=0) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Assert user attributes self.assertEqual(hasattr(array2.attrs, "attr1"), 0) self.assertEqual(hasattr(array2.attrs, "attr2"), 0) # Close the file fileh.close() os.remove(file) class CloseCopyTestCase(CopyTestCase): close = 1 class OpenCopyTestCase(CopyTestCase): close = 0 class CopyIndexTestCase(unittest.TestCase): nrowsinbuf = 2 def test01_index(self): """Checking EArray.copy() method with indexes.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_index..." % self.__class__.__name__) # Create an instance of an HDF5 Array file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an EArray atom = Int32Atom() array1 = fileh.create_earray(fileh.root, 'array1', atom=atom, shape=(0, 2), title="title array1") r = numpy.arange(200, dtype='int32') r.shape = (100, 2) array1.append(r) # Select a different buffer size: array1.nrowsinbuf = self.nrowsinbuf # Copy to another array array2 = array1.copy("/", 'array2', start=self.start, stop=self.stop, step=self.step) if common.verbose: print("array1-->", array1.read()) print("array2-->", array2.read()) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal r2 = r[self.start:self.stop:self.step] self.assertTrue(allequal(r2, array2.read())) # Assert the number of rows in array if common.verbose: print("nrows in array2-->", array2.nrows) print("and it should be-->", r2.shape[0]) self.assertEqual(r2.shape[0], array2.nrows) # Close the file fileh.close() os.remove(file) def test02_indexclosef(self): """Checking EArray.copy() method with indexes (close file version)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_indexclosef..." % self.__class__.__name__) # Create an instance of an HDF5 Array file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an EArray atom = Int32Atom() array1 = fileh.create_earray(fileh.root, 'array1', atom=atom, shape=(0, 2), title="title array1") r = numpy.arange(200, dtype='int32') r.shape = (100, 2) array1.append(r) # Select a different buffer size: array1.nrowsinbuf = self.nrowsinbuf # Copy to another array array2 = array1.copy("/", 'array2', start=self.start, stop=self.stop, step=self.step) # Close and reopen the file fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("array1-->", array1.read()) print("array2-->", array2.read()) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal r2 = r[self.start:self.stop:self.step] self.assertTrue(allequal(r2, array2.read())) # Assert the number of rows in array if common.verbose: print("nrows in array2-->", array2.nrows) print("and it should be-->", r2.shape[0]) self.assertEqual(r2.shape[0], array2.nrows) # Close the file fileh.close() os.remove(file) class CopyIndex1TestCase(CopyIndexTestCase): nrowsinbuf = 1 start = 0 stop = 7 step = 1 class CopyIndex2TestCase(CopyIndexTestCase): nrowsinbuf = 2 start = 0 stop = -1 step = 1 class CopyIndex3TestCase(CopyIndexTestCase): nrowsinbuf = 3 start = 1 stop = 7 step = 1 class CopyIndex4TestCase(CopyIndexTestCase): nrowsinbuf = 4 start = 0 stop = 6 step = 1 class CopyIndex5TestCase(CopyIndexTestCase): nrowsinbuf = 2 start = 3 stop = 7 step = 1 class CopyIndex6TestCase(CopyIndexTestCase): nrowsinbuf = 2 start = 3 stop = 6 step = 2 class CopyIndex7TestCase(CopyIndexTestCase): start = 0 stop = 7 step = 10 class CopyIndex8TestCase(CopyIndexTestCase): start = 6 stop = -1 # Negative values means starting from the end step = 1 class CopyIndex9TestCase(CopyIndexTestCase): start = 3 stop = 4 step = 1 class CopyIndex10TestCase(CopyIndexTestCase): nrowsinbuf = 1 start = 3 stop = 4 step = 2 class CopyIndex11TestCase(CopyIndexTestCase): start = -3 stop = -1 step = 2 class CopyIndex12TestCase(CopyIndexTestCase): start = -1 # Should point to the last element stop = None # None should mean the last element (including it) step = 1 class TruncateTestCase(unittest.TestCase): def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create an EArray atom = Int16Atom(dflt=3) array1 = self.fileh.create_earray(self.fileh.root, 'array1', atom=atom, shape=(0, 2), title="title array1") # Add a couple of rows array1.append(numpy.array([[456, 2], [3, 457]], dtype='Int16')) def tearDown(self): # Close the file self.fileh.close() os.remove(self.file) common.cleanup(self) def test00_truncate(self): """Checking EArray.truncate() method (truncating to 0 rows)""" array1 = self.fileh.root.array1 # Truncate to 0 elements array1.truncate(0) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") array1 = self.fileh.root.array1 if common.verbose: print("array1-->", array1.read()) self.assertTrue(allequal( array1[:], numpy.array([], dtype='Int16').reshape(0, 2))) def test01_truncate(self): """Checking EArray.truncate() method (truncating to 1 rows)""" array1 = self.fileh.root.array1 # Truncate to 1 element array1.truncate(1) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") array1 = self.fileh.root.array1 if common.verbose: print("array1-->", array1.read()) self.assertTrue(allequal( array1.read(), numpy.array([[456, 2]], dtype='Int16'))) def test02_truncate(self): """Checking EArray.truncate() method (truncating to == self.nrows)""" array1 = self.fileh.root.array1 # Truncate to 2 elements array1.truncate(2) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") array1 = self.fileh.root.array1 if common.verbose: print("array1-->", array1.read()) self.assertTrue( allequal(array1.read(), numpy.array([[456, 2], [3, 457]], dtype='Int16'))) def test03_truncate(self): """Checking EArray.truncate() method (truncating to > self.nrows)""" array1 = self.fileh.root.array1 # Truncate to 4 elements array1.truncate(4) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") array1 = self.fileh.root.array1 if common.verbose: print("array1-->", array1.read()) self.assertEqual(array1.nrows, 4) # Check the original values self.assertTrue(allequal(array1[:2], numpy.array([[456, 2], [3, 457]], dtype='Int16'))) # Check that the added rows have the default values self.assertTrue(allequal(array1[2:], numpy.array([[3, 3], [3, 3]], dtype='Int16'))) class TruncateOpenTestCase(TruncateTestCase): close = 0 class TruncateCloseTestCase(TruncateTestCase): close = 1 # The next test should be run only in **common.heavy** mode class Rows64bitsTestCase(unittest.TestCase): narows = 1000 * 1000 # each numpy object will have 1 million entries # narows = 1000 # for testing only nanumber = 1000 * 3 # That should account for more than 2**31-1 def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") fileh = self.fileh = open_file(self.file, "a") # Create an EArray array = fileh.create_earray(fileh.root, 'array', atom=Int8Atom(), shape=(0,), filters=Filters(complib='lzo', complevel=1), # Specifying expectedrows takes more # CPU, but less disk expectedrows=self.narows * self.nanumber) # Fill the array na = numpy.arange(self.narows, dtype='Int8') for i in range(self.nanumber): array.append(na) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test01_basiccheck(self): "Some basic checks for earrays exceeding 2**31 rows" fileh = self.fileh array = fileh.root.array if self.close: if common.verbose: # Check how many entries there are in the array print("Before closing") print("Entries:", array.nrows, type(array.nrows)) print("Entries:", array.nrows / (1000 * 1000), "Millions") print("Shape:", array.shape) # Close the file fileh.close() # Re-open the file fileh = self.fileh = open_file(self.file) array = fileh.root.array if common.verbose: print("After re-open") # Check how many entries there are in the array if common.verbose: print("Entries:", array.nrows, type(array.nrows)) print("Entries:", array.nrows / (1000 * 1000), "Millions") print("Shape:", array.shape) print("Last 10 elements-->", array[-10:]) stop = self.narows % 256 if stop > 127: stop -= 256 start = stop - 10 print("Should look like-->", numpy.arange(start, stop, dtype='Int8')) nrows = self.narows * self.nanumber # check nrows self.assertEqual(array.nrows, nrows) # Check shape self.assertEqual(array.shape, (nrows,)) # check the 10 first elements self.assertTrue(allequal(array[:10], numpy.arange(10, dtype='Int8'))) # check the 10 last elements stop = self.narows % 256 if stop > 127: stop -= 256 start = stop - 10 self.assertTrue(allequal(array[-10:], numpy.arange(start, stop, dtype='Int8'))) class Rows64bitsTestCase1(Rows64bitsTestCase): close = 0 class Rows64bitsTestCase2(Rows64bitsTestCase): close = 1 # Test for appending zero-sized arrays class ZeroSizedTestCase(unittest.TestCase): def setUp(self): self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "a") # Create an EArray ea = self.fileh.create_earray('/', 'test', atom=Int32Atom(), shape=(3, 0)) # Append a single row ea.append([[1], [2], [3]]) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def test01_canAppend(self): "Appending zero length array." fileh = self.fileh ea = fileh.root.test np = numpy.empty(shape=(3, 0), dtype='int32') ea.append(np) self.assertEqual(ea.nrows, 1, "The number of rows should be 1.") def test02_appendWithWrongShape(self): "Appending zero length array with wrong dimension." fileh = self.fileh ea = fileh.root.test np = numpy.empty(shape=(3, 0, 3), dtype='int32') self.assertRaises(ValueError, ea.append, np) # Test for dealing with multidimensional atoms class MDAtomTestCase(common.TempFileMixin, common.PyTablesTestCase): def test01a_append(self): "Append a row to a (unidimensional) EArray with a MD atom." # Create an EArray ea = self.h5file.create_earray('/', 'test', atom=Int32Atom((2, 2)), shape=(0,)) if self.reopen: self._reopen('a') ea = self.h5file.root.test # Append one row ea.append([[[1, 3], [4, 5]]]) self.assertEqual(ea.nrows, 1) if common.verbose: print("First row-->", ea[0]) self.assertTrue(allequal(ea[0], numpy.array([[1, 3], [4, 5]], 'i4'))) def test01b_append(self): "Append several rows to a (unidimensional) EArray with a MD atom." # Create an EArray ea = self.h5file.create_earray('/', 'test', atom=Int32Atom((2, 2)), shape=(0,)) if self.reopen: self._reopen('a') ea = self.h5file.root.test # Append three rows ea.append([[[1]], [[2]], [[3]]]) # Simple broadcast self.assertEqual(ea.nrows, 3) if common.verbose: print("Third row-->", ea[2]) self.assertTrue(allequal(ea[2], numpy.array([[3, 3], [3, 3]], 'i4'))) def test02a_append(self): "Append a row to a (multidimensional) EArray with a MD atom." # Create an EArray ea = self.h5file.create_earray('/', 'test', atom=Int32Atom((2,)), shape=(0, 3)) if self.reopen: self._reopen('a') ea = self.h5file.root.test # Append one row ea.append([[[1, 3], [4, 5], [7, 9]]]) self.assertEqual(ea.nrows, 1) if common.verbose: print("First row-->", ea[0]) self.assertTrue(allequal(ea[0], numpy.array( [[1, 3], [4, 5], [7, 9]], 'i4'))) def test02b_append(self): "Append several rows to a (multidimensional) EArray with a MD atom." # Create an EArray ea = self.h5file.create_earray('/', 'test', atom=Int32Atom((2,)), shape=(0, 3)) if self.reopen: self._reopen('a') ea = self.h5file.root.test # Append three rows ea.append([[[1, -3], [4, -5], [-7, 9]], [[-1, 3], [-4, 5], [7, -8]], [[-2, 3], [-5, 5], [7, -9]]]) self.assertEqual(ea.nrows, 3) if common.verbose: print("Third row-->", ea[2]) self.assertTrue(allequal( ea[2], numpy.array([[-2, 3], [-5, 5], [7, -9]], 'i4'))) def test03a_MDMDMD(self): "Complex append of a MD array in a MD EArray with a MD atom." # Create an EArray ea = self.h5file.create_earray('/', 'test', atom=Int32Atom((2, 4)), shape=(0, 2, 3)) if self.reopen: self._reopen('a') ea = self.h5file.root.test # Append three rows # The shape of the atom should be added at the end of the arrays a = numpy.arange(2 * 3*2*4, dtype='i4').reshape((2, 3, 2, 4)) ea.append([a * 1, a*2, a*3]) self.assertEqual(ea.nrows, 3) if common.verbose: print("Third row-->", ea[2]) self.assertTrue(allequal(ea[2], a * 3)) def test03b_MDMDMD(self): "Complex append of a MD array in a MD EArray with a MD atom (II)." # Create an EArray ea = self.h5file.create_earray('/', 'test', atom=Int32Atom((2, 4)), shape=(2, 0, 3)) if self.reopen: self._reopen('a') ea = self.h5file.root.test # Append three rows # The shape of the atom should be added at the end of the arrays a = numpy.arange(2 * 3*2*4, dtype='i4').reshape((2, 1, 3, 2, 4)) ea.append(a * 1) ea.append(a * 2) ea.append(a * 3) self.assertEqual(ea.nrows, 3) if common.verbose: print("Third row-->", ea[:, 2, ...]) self.assertTrue(allequal(ea[:, 2, ...], a.reshape((2, 3, 2, 4))*3)) def test03c_MDMDMD(self): "Complex append of a MD array in a MD EArray with a MD atom (III)." # Create an EArray ea = self.h5file.create_earray('/', 'test', atom=Int32Atom((2, 4)), shape=(2, 3, 0)) if self.reopen: self._reopen('a') ea = self.h5file.root.test # Append three rows # The shape of the atom should be added at the end of the arrays a = numpy.arange(2 * 3*2*4, dtype='i4').reshape((2, 3, 1, 2, 4)) ea.append(a * 1) ea.append(a * 2) ea.append(a * 3) self.assertEqual(ea.nrows, 3) if common.verbose: print("Third row-->", ea[:, :, 2, ...]) self.assertTrue(allequal(ea[:, :, 2, ...], a.reshape((2, 3, 2, 4))*3)) class MDAtomNoReopen(MDAtomTestCase): reopen = False class MDAtomReopen(MDAtomTestCase): reopen = True class AccessClosedTestCase(common.TempFileMixin, common.PyTablesTestCase): def setUp(self): super(AccessClosedTestCase, self).setUp() self.array = self.h5file.create_earray(self.h5file.root, 'array', atom=Int32Atom(), shape=(0, 10)) self.array.append(numpy.zeros((10, 10))) def test_read(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.array.read) def test_getitem(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.array.__getitem__, 0) def test_setitem(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.array.__setitem__, 0, 0) def test_append(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.array.append, numpy.zeros((10, 10))) class TestCreateEArrayArgs(common.TempFileMixin, common.PyTablesTestCase): obj = numpy.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) where = '/' name = 'earray' atom = Atom.from_dtype(obj.dtype) shape = (0,) + obj.shape[1:] title = 'title' filters = None expectedrows = 1000 chunkshape = (1, 2) byteorder = None createparents = False def test_positional_args_01(self): self.h5file.create_earray(self.where, self.name, self.atom, self.shape, self.title, self.filters, self.expectedrows, self.chunkshape) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.nrows, 0) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) def test_positional_args_02(self): ptarr = self.h5file.create_earray(self.where, self.name, self.atom, self.shape, self.title, self.filters, self.expectedrows, self.chunkshape) ptarr.append(self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.obj.shape) self.assertEqual(ptarr.nrows, self.obj.shape[0]) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(self.obj, nparr)) def test_positional_args_obj(self): self.h5file.create_earray(self.where, self.name, None, None, self.title, self.filters, self.expectedrows, self.chunkshape, self.byteorder, self.createparents, self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.obj.shape) self.assertEqual(ptarr.nrows, self.obj.shape[0]) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj(self): self.h5file.create_earray(self.where, self.name, title=self.title, chunkshape=self.chunkshape, obj=self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.obj.shape) self.assertEqual(ptarr.nrows, self.obj.shape[0]) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_atom_shape_01(self): ptarr = self.h5file.create_earray(self.where, self.name, title=self.title, chunkshape=self.chunkshape, atom=self.atom, shape=self.shape) ptarr.append(self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.obj.shape) self.assertEqual(ptarr.nrows, self.obj.shape[0]) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_atom_shape_02(self): ptarr = self.h5file.create_earray(self.where, self.name, title=self.title, chunkshape=self.chunkshape, atom=self.atom, shape=self.shape) #ptarr.append(self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.shape) self.assertEqual(ptarr.nrows, 0) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) def test_kwargs_obj_atom(self): ptarr = self.h5file.create_earray(self.where, self.name, title=self.title, chunkshape=self.chunkshape, obj=self.obj, atom=self.atom) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.obj.shape) self.assertEqual(ptarr.nrows, self.obj.shape[0]) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj_shape(self): ptarr = self.h5file.create_earray(self.where, self.name, title=self.title, chunkshape=self.chunkshape, obj=self.obj, shape=self.shape) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.obj.shape) self.assertEqual(ptarr.nrows, self.obj.shape[0]) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj_atom_shape(self): ptarr = self.h5file.create_earray(self.where, self.name, title=self.title, chunkshape=self.chunkshape, obj=self.obj, atom=self.atom, shape=self.shape) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, self.obj.shape) self.assertEqual(ptarr.nrows, self.obj.shape[0]) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertEqual(ptarr.chunkshape, self.chunkshape) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj_atom_error(self): atom = Atom.from_dtype(numpy.dtype('complex')) #shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_earray, self.where, self.name, title=self.title, obj=self.obj, atom=atom) def test_kwargs_obj_shape_error(self): #atom = Atom.from_dtype(numpy.dtype('complex')) shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_earray, self.where, self.name, title=self.title, obj=self.obj, shape=shape) def test_kwargs_obj_atom_shape_error_01(self): atom = Atom.from_dtype(numpy.dtype('complex')) #shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_earray, self.where, self.name, title=self.title, obj=self.obj, atom=atom, shape=self.shape) def test_kwargs_obj_atom_shape_error_02(self): #atom = Atom.from_dtype(numpy.dtype('complex')) shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_earray, self.where, self.name, title=self.title, obj=self.obj, atom=self.atom, shape=shape) def test_kwargs_obj_atom_shape_error_03(self): atom = Atom.from_dtype(numpy.dtype('complex')) shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_earray, self.where, self.name, title=self.title, obj=self.obj, atom=atom, shape=shape) #---------------------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() niter = 1 # common.heavy = 1 # uncomment this only for testing purposes # theSuite.addTest(unittest.makeSuite(BasicWriteTestCase)) # theSuite.addTest(unittest.makeSuite(Rows64bitsTestCase1)) # theSuite.addTest(unittest.makeSuite(Rows64bitsTestCase2)) for n in range(niter): theSuite.addTest(unittest.makeSuite(BasicWriteTestCase)) theSuite.addTest(unittest.makeSuite(Basic2WriteTestCase)) theSuite.addTest(unittest.makeSuite(Basic3WriteTestCase)) theSuite.addTest(unittest.makeSuite(Basic4WriteTestCase)) theSuite.addTest(unittest.makeSuite(Basic5WriteTestCase)) theSuite.addTest(unittest.makeSuite(Basic6WriteTestCase)) theSuite.addTest(unittest.makeSuite(Basic7WriteTestCase)) theSuite.addTest(unittest.makeSuite(Basic8WriteTestCase)) theSuite.addTest(unittest.makeSuite(EmptyEArrayTestCase)) theSuite.addTest(unittest.makeSuite(Empty2EArrayTestCase)) theSuite.addTest(unittest.makeSuite(SlicesEArrayTestCase)) theSuite.addTest(unittest.makeSuite(Slices2EArrayTestCase)) theSuite.addTest(unittest.makeSuite(EllipsisEArrayTestCase)) theSuite.addTest(unittest.makeSuite(Ellipsis2EArrayTestCase)) theSuite.addTest(unittest.makeSuite(Ellipsis3EArrayTestCase)) theSuite.addTest(unittest.makeSuite(ZlibComprTestCase)) theSuite.addTest(unittest.makeSuite(ZlibShuffleTestCase)) theSuite.addTest(unittest.makeSuite(BloscComprTestCase)) theSuite.addTest(unittest.makeSuite(BloscShuffleTestCase)) theSuite.addTest(unittest.makeSuite(LZOComprTestCase)) theSuite.addTest(unittest.makeSuite(LZOShuffleTestCase)) theSuite.addTest(unittest.makeSuite(Bzip2ComprTestCase)) theSuite.addTest(unittest.makeSuite(Bzip2ShuffleTestCase)) theSuite.addTest(unittest.makeSuite(FloatTypeTestCase)) theSuite.addTest(unittest.makeSuite(ComplexTypeTestCase)) theSuite.addTest(unittest.makeSuite(StringTestCase)) theSuite.addTest(unittest.makeSuite(String2TestCase)) theSuite.addTest(unittest.makeSuite(StringComprTestCase)) theSuite.addTest(unittest.makeSuite( SizeOnDiskInMemoryPropertyTestCase)) theSuite.addTest(unittest.makeSuite(OffsetStrideTestCase)) theSuite.addTest(unittest.makeSuite(Fletcher32TestCase)) theSuite.addTest(unittest.makeSuite(AllFiltersTestCase)) theSuite.addTest(unittest.makeSuite(CloseCopyTestCase)) theSuite.addTest(unittest.makeSuite(OpenCopyTestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex1TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex2TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex3TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex4TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex5TestCase)) theSuite.addTest(unittest.makeSuite(TruncateOpenTestCase)) theSuite.addTest(unittest.makeSuite(TruncateCloseTestCase)) theSuite.addTest(unittest.makeSuite(ZeroSizedTestCase)) theSuite.addTest(unittest.makeSuite(MDAtomNoReopen)) theSuite.addTest(unittest.makeSuite(MDAtomReopen)) theSuite.addTest(unittest.makeSuite(AccessClosedTestCase)) theSuite.addTest(unittest.makeSuite(TestCreateEArrayArgs)) if common.heavy: theSuite.addTest(unittest.makeSuite(Slices3EArrayTestCase)) theSuite.addTest(unittest.makeSuite(Slices4EArrayTestCase)) theSuite.addTest(unittest.makeSuite(Ellipsis4EArrayTestCase)) theSuite.addTest(unittest.makeSuite(Ellipsis5EArrayTestCase)) theSuite.addTest(unittest.makeSuite(Ellipsis6EArrayTestCase)) theSuite.addTest(unittest.makeSuite(Ellipsis7EArrayTestCase)) theSuite.addTest(unittest.makeSuite(MD3WriteTestCase)) theSuite.addTest(unittest.makeSuite(MD5WriteTestCase)) theSuite.addTest(unittest.makeSuite(MD6WriteTestCase)) theSuite.addTest(unittest.makeSuite(MD7WriteTestCase)) theSuite.addTest(unittest.makeSuite(MD10WriteTestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex6TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex7TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex8TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex9TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex10TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex11TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex12TestCase)) theSuite.addTest(unittest.makeSuite(Rows64bitsTestCase1)) theSuite.addTest(unittest.makeSuite(Rows64bitsTestCase2)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## End: PyTables-v.3.1.1/tables/tests/test_enum.py000066400000000000000000000542651231437614300205140ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: 2005-05-09 # Author: Ivan Vilata i Balaguer - ivan@selidor.net # # $Id$ # ######################################################################## """Test module for enumerated types under PyTables.""" import unittest import operator import itertools import tables from tables.tests import common class CreateColTestCase(common.PyTablesTestCase): """Test creating enumerated column descriptions.""" def _createCol(self, enum, dflt, base='uint32', shape=()): """Create and check an enumerated column description.""" enumcol = tables.EnumCol(enum, dflt, base=base, shape=shape) sameEnum = tables.Enum(enum) self.assertEqual(enumcol.type, 'enum') self.assertEqual(enumcol.dtype.base.name, enumcol.base.type) # To avoid 'LongInt' vs 'Int' issues # self.assertEqual(enumcol.dflt, sameEnum[dflt]) self.assertEqual(int(enumcol.dflt), int(sameEnum[dflt])) self.assertEqual(enumcol.dtype.shape, shape) self.assertEqual(enumcol.enum, sameEnum) def test00a_validFromEnum(self): """Describing an enumerated column from an enumeration.""" colors = tables.Enum(['red', 'green', 'blue']) self._createCol(colors, 'red') def test00b_validFromDict(self): """Describing an enumerated column from a dictionary.""" colors = {'red': 4, 'green': 2, 'blue': 1} self._createCol(colors, 'red') def test00c_validFromList(self): """Describing an enumerated column from a list.""" colors = ['red', 'green', 'blue'] self._createCol(colors, 'red') def test00d_invalidFromType(self): """Describing an enumerated column from an invalid object.""" colors = 123 self.assertRaises(TypeError, self._createCol, colors, 'red') def test01_invalidDflt(self): """Describing an enumerated column with an invalid default object.""" colors = {'red': 4, 'green': 2, 'blue': 1} self.assertRaises(KeyError, self._createCol, colors, 'black') def test02a_validDtypeBroader(self): """Describing an enumerated column with a broader type.""" colors = {'red': 4, 'green': 2, 'blue': 1} self._createCol(colors, 'red', 'int64') def test02b_invalidDtypeTooNarrow(self): """Describing an enumerated column with a too narrow type.""" colors = ['e%d' % i for i in range(300)] self.assertRaises(TypeError, self._createCol, colors, 'e0', 'uint8') def test03a_validShapeMD(self): """Describing an enumerated column with multidimensional shape.""" colors = ['red', 'green', 'blue'] self._createCol(colors, 'red', shape=(2,)) def test04a_validReprEnum(self): """Checking the string representation of an enumeration.""" colors = tables.Enum(['red', 'green', 'blue']) enumcol = tables.EnumCol(colors, 'red', base='uint32', shape=()) # needed due to "Hash randomization" (default on python 3.3) template = ( "EnumCol(enum=Enum({%s}), dflt='red', base=UInt32Atom(shape=(), " "dflt=0), shape=(), pos=None)" ) permitations = [ template % ', '.join(items) for items in itertools.permutations( ("'blue': 2", "'green': 1", "'red': 0")) ] self.assertTrue(repr(enumcol) in permitations) def test99a_nonIntEnum(self): """Describing an enumerated column of floats (not implemented).""" colors = {'red': 1.0} self.assertRaises(NotImplementedError, self._createCol, colors, 'red', base=tables.FloatAtom()) def test99b_nonIntDtype(self): """Describing an enumerated column encoded as floats. (not implemented). """ colors = ['red', 'green', 'blue'] self.assertRaises( NotImplementedError, self._createCol, colors, 'red', 'float64') def test99b_nonScalarEnum(self): """Describing an enumerated column of non-scalars (not implemented).""" colors = {'red': (1, 2, 3)} self.assertRaises(NotImplementedError, self._createCol, colors, 'red', base=tables.IntAtom(shape=3)) class CreateAtomTestCase(common.PyTablesTestCase): """Test creating enumerated atoms.""" def _createAtom(self, enum, dflt, base='uint32', shape=()): """Create and check an enumerated atom.""" enumatom = tables.EnumAtom(enum, dflt, base=base, shape=shape) sameEnum = tables.Enum(enum) self.assertEqual(enumatom.type, 'enum') self.assertEqual(enumatom.dtype.base.name, enumatom.base.type) self.assertEqual(enumatom.shape, shape) self.assertEqual(enumatom.enum, sameEnum) def test00a_validFromEnum(self): """Describing an enumerated atom from an enumeration.""" colors = tables.Enum(['red', 'green', 'blue']) self._createAtom(colors, 'red') def test00b_validFromDict(self): """Describing an enumerated atom from a dictionary.""" colors = {'red': 4, 'green': 2, 'blue': 1} self._createAtom(colors, 'red') def test00c_validFromList(self): """Describing an enumerated atom from a list.""" colors = ['red', 'green', 'blue'] self._createAtom(colors, 'red') def test00d_invalidFromType(self): """Describing an enumerated atom from an invalid object.""" colors = 123 self.assertRaises(TypeError, self._createAtom, colors, 'red') def test02a_validDtypeBroader(self): """Describing an enumerated atom with a broader type.""" colors = {'red': 4, 'green': 2, 'blue': 1} self._createAtom(colors, 'red', base='int64') def test02b_invalidDtypeTooNarrow(self): """Describing an enumerated atom with a too narrow type.""" colors = ['e%d' % i for i in range(300)] self.assertRaises(TypeError, self._createAtom, colors, 'red', 'uint8') def test03a_validShapeMD(self): """Describing an enumerated atom with multidimensional shape.""" colors = ['red', 'green', 'blue'] self._createAtom(colors, 'red', shape=(2,)) def test99a_nonIntEnum(self): """Describing an enumerated atom of floats (not implemented).""" colors = {'red': 1.0} self.assertRaises(NotImplementedError, self._createAtom, colors, 'red', base=tables.FloatAtom()) def test99b_nonIntDtype(self): """Describing an enumerated atom encoded as a float. (not implemented). """ colors = ['red', 'green', 'blue'] self.assertRaises( NotImplementedError, self._createAtom, colors, 'red', 'float64') def test99b_nonScalarEnum(self): """Describing an enumerated atom of non-scalars (not implemented).""" colors = {'red': (1, 2, 3)} self.assertRaises(NotImplementedError, self._createAtom, colors, 'red', base=tables.IntAtom(shape=3)) class EnumTableTestCase(common.TempFileMixin, common.PyTablesTestCase): """Test tables with enumerated columns.""" enum = tables.Enum({'red': 4, 'green': 2, 'blue': 1, 'black': 0}) defaultName = 'black' valueInEnum = enum.red valueOutOfEnum = 1234 enumType = 'uint16' def _description(self, shape=()): class TestDescription(tables.IsDescription): rid = tables.IntCol(pos=0) rcolor = tables.EnumCol( self.enum, self.defaultName, base=self.enumType, shape=shape, pos=1) return TestDescription def test00a_reopen(self): """Reopening a file with tables using enumerated data.""" self.h5file.create_table( '/', 'test', self._description(), title=self._getMethodName()) self._reopen() self.assertEqual( self.h5file.root.test.get_enum('rcolor'), self.enum, "Enumerated type was not restored correctly from disk.") def test00b_reopenMD(self): """ Reopening a file with tables using enumerated multi-dimensional data. """ self.h5file.create_table( '/', 'test', self._description((2,)), title=self._getMethodName()) self._reopen() self.assertEqual( self.h5file.root.test.get_enum('rcolor'), self.enum, "Enumerated type was not restored correctly from disk.") def test01_rowAppend(self): """Appending enumerated values using ``row.append()``.""" tbl = self.h5file.create_table( '/', 'test', self._description(), title=self._getMethodName()) appended = [ (10, self.valueInEnum), (20, self.valueOutOfEnum)] row = tbl.row row['rid'] = appended[0][0] row['rcolor'] = appended[0][1] row.append() row['rid'] = appended[1][0] self.assertRaises( ValueError, operator.setitem, row, 'rcolor', appended[1][1]) tbl.flush() tbl.flavor = 'python' read = tbl.read() common.verbosePrint( "* appended value: %s\n" "* read value: %s\n" % (appended[:-1], read)) self.assertEqual( appended[:-1], read, "Written and read values differ.") def test02_append(self): """Appending enumerated values using ``table.append()``.""" tbl = self.h5file.create_table( '/', 'test', self._description(), title=self._getMethodName()) appended = [ (10, self.valueInEnum), (20, self.valueOutOfEnum)] tbl.append(appended) tbl.flush() tbl.flavor = 'python' read = tbl.read() common.verbosePrint( "* appended value: %s\n" "* read value: %s\n" % (appended, read)) self.assertEqual(appended, read, "Written and read values differ.") def test03_setitem(self): """Changing enumerated values using ``table.__setitem__()``.""" tbl = self.h5file.create_table( '/', 'test', self._description(), title=self._getMethodName()) appended = [ (10, self.valueInEnum), (20, self.valueInEnum)] tbl.append(appended) written = [ (10, self.valueInEnum), (20, self.valueOutOfEnum)] tbl[:] = written tbl.flavor = 'python' read = tbl.read() common.verbosePrint( "* written value: %s\n" "* read value: %s\n" % (written, read)) self.assertEqual(written, read, "Written and read values differ.") def test04_multidim(self): """Appending multi-dimensional enumerated data.""" tbl = self.h5file.create_table( '/', 'test', self._description((2,)), title=self._getMethodName()) appended = [ (10, (self.valueInEnum, self.valueOutOfEnum)), (20, (self.valueInEnum, self.valueOutOfEnum))] row = tbl.row row['rid'] = appended[0][0] self.assertRaises( ValueError, operator.setitem, row, 'rcolor', appended[0][1]) tbl.append(appended) tbl.flush() tbl.flavor = 'python' read = tbl.read() for i in range(len(appended)): self.assertEqual(appended[i][0], read[i][0], "Written and read values differ.") self.assertEqual(appended[i][1][0], read[i][1][0], "Written and read values differ.") self.assertEqual(appended[i][1][1], read[i][1][1], "Written and read values differ.") def test05_where(self): """Searching enumerated data.""" tbl = self.h5file.create_table( '/', 'test', self._description(), title=self._getMethodName()) appended = [ (10, self.valueInEnum), (20, self.valueInEnum), (30, self.valueOutOfEnum)] tbl.append(appended) tbl.flush() searched = [ (row['rid'], row['rcolor']) for row in tbl.where('rcolor == v', {'v': self.valueInEnum})] common.verbosePrint( "* ``valueInEnum``: %s\n" "* ``rcolor`` column: ``%s``\n" "* ``searched``: %s\n" "* Should look like: %s\n" % (self.valueInEnum, tbl.cols.rcolor, searched, appended[:-1])) self.assertEqual( searched, appended[:-1], "Search returned incorrect results.") class EnumEArrayTestCase(common.TempFileMixin, common.PyTablesTestCase): """Test extendable arrays of enumerated values.""" enum = tables.Enum({'red': 4, 'green': 2, 'blue': 1, 'black': 0}) valueInEnum = enum.red valueOutOfEnum = 1234 enumType = 'uint16' def _atom(self, shape=()): return tables.EnumAtom( self.enum, 'red', base=self.enumType, shape=shape) def test00a_reopen(self): """Reopening a file with extendable arrays using enumerated data.""" self.h5file.create_earray( '/', 'test', self._atom(), shape=(0,), title=self._getMethodName()) self.h5file.root.test.flavor = 'python' self._reopen() self.assertEqual( self.h5file.root.test.get_enum(), self.enum, "Enumerated type was not restored correctly from disk.") def test00b_reopenMD(self): """ Reopening a file with extendable arrays using enumerated multi-dimensional data. """ self.h5file.create_earray( '/', 'test', self._atom(), shape=(0, 2), title=self._getMethodName()) self.h5file.root.test.flavor = 'python' self._reopen() self.assertEqual( self.h5file.root.test.get_enum(), self.enum, "Enumerated type was not restored correctly from disk.") def test_enum_default_persistence_red(self): dflt = 'red' atom = tables.EnumAtom( self.enum, dflt, base=self.enumType, shape=()) self.h5file.create_earray('/', 'test', atom, shape=(0,), title=self._getMethodName()) self._reopen() self.assertEqual( self.h5file.root.test.get_enum(), self.enum, "Enumerated type was not restored correctly from disk.") self.assertEqual( self.h5file.root.test.atom.dflt, self.enum[dflt], "The default value of enumerated type was not restored correctly " "from disk.") def test_enum_default_persistence_green(self): dflt = 'green' atom = tables.EnumAtom( self.enum, dflt, base=self.enumType, shape=()) self.h5file.create_earray('/', 'test', atom, shape=(0,), title=self._getMethodName()) self._reopen() self.assertEqual( self.h5file.root.test.get_enum(), self.enum, "Enumerated type was not restored correctly from disk.") self.assertEqual( self.h5file.root.test.atom.dflt, self.enum[dflt], "The default value of enumerated type was not restored correctly " "from disk.") def test_enum_default_persistence_blue(self): dflt = 'blue' atom = tables.EnumAtom( self.enum, dflt, base=self.enumType, shape=()) self.h5file.create_earray('/', 'test', atom, shape=(0,), title=self._getMethodName()) self._reopen() self.assertEqual( self.h5file.root.test.get_enum(), self.enum, "Enumerated type was not restored correctly from disk.") self.assertEqual( self.h5file.root.test.atom.dflt, self.enum[dflt], "The default value of enumerated type was not restored correctly " "from disk.") def test_enum_default_persistence_black(self): dflt = 'black' atom = tables.EnumAtom( self.enum, dflt, base=self.enumType, shape=()) self.h5file.create_earray('/', 'test', atom, shape=(0,), title=self._getMethodName()) self._reopen() self.assertEqual( self.h5file.root.test.get_enum(), self.enum, "Enumerated type was not restored correctly from disk.") self.assertEqual( self.h5file.root.test.atom.dflt, self.enum[dflt], "The default value of enumerated type was not restored correctly " "from disk.") def test01_append(self): """Appending scalar elements of enumerated values.""" earr = self.h5file.create_earray( '/', 'test', self._atom(), shape=(0,), title=self._getMethodName()) earr.flavor = 'python' appended = [self.valueInEnum, self.valueOutOfEnum] earr.append(appended) earr.flush() read = earr.read() self.assertEqual(appended, read, "Written and read values differ.") def test02_appendMD(self): """Appending multi-dimensional elements of enumerated values.""" earr = self.h5file.create_earray( '/', 'test', self._atom(), shape=(0, 2), title=self._getMethodName()) earr.flavor = 'python' appended = [ [self.valueInEnum, self.valueOutOfEnum], [self.valueInEnum, self.valueOutOfEnum]] earr.append(appended) earr.flush() read = earr.read() self.assertEqual(appended, read, "Written and read values differ.") def test03_setitem(self): """Changing enumerated values using ``earray.__setitem__()``.""" earr = self.h5file.create_earray( '/', 'test', self._atom(), shape=(0,), title=self._getMethodName()) earr.flavor = 'python' appended = (self.valueInEnum, self.valueInEnum) earr.append(appended) written = [self.valueInEnum, self.valueOutOfEnum] earr[:] = written read = earr.read() self.assertEqual(written, read, "Written and read values differ.") class EnumVLArrayTestCase(common.TempFileMixin, common.PyTablesTestCase): """Test variable-length arrays of enumerated values.""" enum = tables.Enum({'red': 4, 'green': 2, 'blue': 1, 'black': 0}) valueInEnum = enum.red valueOutOfEnum = 1234 enumType = 'uint16' def _atom(self, shape=()): return tables.EnumAtom( self.enum, 'red', base=self.enumType, shape=shape) def test00a_reopen(self): """Reopening a file with variable-length arrays using enumerated data.""" self.h5file.create_vlarray( '/', 'test', self._atom(), title=self._getMethodName()) self.h5file.root.test.flavor = 'python' self._reopen() self.assertEqual( self.h5file.root.test.get_enum(), self.enum, "Enumerated type was not restored correctly from disk.") def test00b_reopenMD(self): """ Reopening a file with variable-length arrays using enumerated multi-dimensional data. """ self.h5file.create_vlarray( '/', 'test', self._atom((2,)), title=self._getMethodName()) self.h5file.root.test.flavor = 'python' self._reopen() self.assertEqual( self.h5file.root.test.get_enum(), self.enum, "Enumerated type was not restored correctly from disk.") def test01_append(self): """Appending scalar elements of enumerated values.""" vlarr = self.h5file.create_vlarray( '/', 'test', self._atom(), title=self._getMethodName()) vlarr.flavor = 'python' appended = [ [self.valueInEnum, ], [self.valueInEnum, self.valueOutOfEnum]] vlarr.append(appended[0]) vlarr.append(appended[1]) vlarr.flush() read = vlarr.read() common.verbosePrint( "* appended value: %s\n" "* read value: %s\n" % (appended, read)) self.assertEqual(appended, read, "Written and read values differ.") def test02_appendMD(self): """Appending multi-dimensional elements of enumerated values.""" vlarr = self.h5file.create_vlarray( '/', 'test', self._atom((2,)), title=self._getMethodName()) vlarr.flavor = 'python' appended = [ [[self.valueInEnum, self.valueInEnum], ], [[self.valueInEnum, self.valueOutOfEnum], [self.valueInEnum, self.valueInEnum]]] vlarr.append(appended[0]) vlarr.append(appended[1]) vlarr.flush() read = vlarr.read() common.verbosePrint( "* appended value: %s\n" "* read value: %s\n" % (appended, read)) self.assertEqual(appended, read, "Written and read values differ.") def test03_setitem(self): """Changing enumerated values using ``vlarray.__setitem__()``.""" vlarr = self.h5file.create_vlarray( '/', 'test', self._atom(), title=self._getMethodName()) vlarr.flavor = 'python' appended = (self.valueInEnum, self.valueInEnum) vlarr.append(appended) written = [self.valueInEnum, self.valueOutOfEnum] vlarr[0] = written read = vlarr.read() common.verbosePrint( "* written value: %s\n" "* read value: %s\n" % (written, read)) self.assertEqual(written, read[0], "Written and read values differ.") def suite(): """Return a test suite consisting of all the test cases in the module.""" # These two are for including Enum's doctests here. import doctest from tables.misc import enum theSuite = unittest.TestSuite() niter = 1 # theSuite.addTest(unittest.makeSuite(EnumTableTestCase)) for i in range(niter): theSuite.addTest(doctest.DocTestSuite(enum)) theSuite.addTest(unittest.makeSuite(CreateColTestCase)) theSuite.addTest(unittest.makeSuite(CreateAtomTestCase)) theSuite.addTest(unittest.makeSuite(EnumTableTestCase)) theSuite.addTest(unittest.makeSuite(EnumEArrayTestCase)) theSuite.addTest(unittest.makeSuite(EnumVLArrayTestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/tests/test_expression.py000066400000000000000000001541541231437614300217450ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: 2009-06-14 # Author: Francesc Alted - faltet@pytables.org # # $Id$ # ######################################################################## """Test module for evaluating expressions under PyTables.""" from __future__ import print_function import unittest import numpy as np import tables as tb from tables.tests import common # An example of record class Record(tb.IsDescription): colInt32 = tb.Int32Col() colInt64 = tb.Int64Col() colFloat32 = tb.Float32Col() colFloat64 = tb.Float64Col() colComplex = tb.ComplexCol(itemsize=16) # Helper functions def get_sliced_vars(npvars, start, stop, step): npvars_ = {} for name, var in npvars.iteritems(): if hasattr(var, "__len__"): npvars_[name] = var[start:stop:step] else: npvars_[name] = var return npvars_ def get_sliced_vars2(npvars, start, stop, step, shape, maindim): npvars_ = {} slices = [slice(None) for dim in shape] slices[maindim] = slice(start, stop, step) for name, var in npvars.iteritems(): npvars_[name] = var.__getitem__(tuple(slices)) return npvars_ # Basic tests class ExprTestCase(common.TempFileMixin, common.PyTablesTestCase): # The shape for the variables in expressions shape = (10, 20) def setUp(self): super(ExprTestCase, self).setUp() # The expression self.expr = "2 * a*b + c" # Define the NumPy variables to be used in expression N = np.prod(self.shape) self.a = a = np.arange(0, N, dtype='int32').reshape(self.shape) self.b = b = np.arange(N, 2 * N, dtype='int64').reshape(self.shape) self.c = c = np.arange(2 * N, 3*N, dtype='int32').reshape(self.shape) self.r1 = r1 = np.empty(N, dtype='int64').reshape(self.shape) self.npvars = {"a": a, "b": b, "c": c, } # Define other variables, if needed root = self.h5file.root if self.kind == "Array": self.a = self.h5file.create_array(root, "a", a) self.b = self.h5file.create_array(root, "b", b) self.c = self.h5file.create_array(root, "c", c) self.r1 = self.h5file.create_array(root, "r1", r1) elif self.kind == "CArray": self.a = self.h5file.create_carray( root, "a", atom=tb.Atom.from_dtype(a.dtype), shape=self.shape) self.b = self.h5file.create_carray( root, "b", atom=tb.Atom.from_dtype(b.dtype), shape=self.shape) self.c = self.h5file.create_carray( root, "c", atom=tb.Atom.from_dtype(c.dtype), shape=self.shape) self.r1 = self.h5file.create_carray( root, "r1", atom=tb.Atom.from_dtype(r1.dtype), shape=self.shape) self.a[:] = a self.b[:] = b self.c[:] = c elif self.kind == "EArray": shape = list(self.shape) shape[0] = 0 self.a = self.h5file.create_earray( root, "a", atom=tb.Atom.from_dtype(a.dtype), shape=shape) self.b = self.h5file.create_earray( root, "b", atom=tb.Atom.from_dtype(b.dtype), shape=shape) self.c = self.h5file.create_earray( root, "c", atom=tb.Atom.from_dtype(c.dtype), shape=shape) self.r1 = self.h5file.create_earray( root, "r1", atom=tb.Atom.from_dtype(r1.dtype), shape=shape) self.a.append(a) self.b.append(b) self.c.append(c) self.r1.append(r1) # Fill with uninitialized values elif self.kind == "Column": ra = np.rec.fromarrays( [a, b, c, r1], dtype="%si4,%si8,%si4,%si8" % ((self.shape[1:],)*4)) t = self.h5file.create_table(root, "t", ra) self.a = t.cols.f0 self.b = t.cols.f1 self.c = t.cols.f2 self.d = t.cols.f3 self.vars = {"a": self.a, "b": self.b, "c": self.c, } def test00_simple(self): """Checking that expression is correctly evaluated.""" expr = tb.Expr(self.expr, self.vars) r1 = expr.eval() r2 = eval(self.expr, self.npvars) if common.verbose: print("Computed expression:", repr(r1)) print("Should look like:", repr(r2)) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") def test01_out(self): """Checking that expression is correctly evaluated (`out` param)""" expr = tb.Expr(self.expr, self.vars) expr.set_output(self.r1) r1 = expr.eval() if self.kind != "NumPy": r1 = r1[:] r2 = eval(self.expr, self.npvars) if common.verbose: print("Computed expression:", repr(r1)) print("Should look like:", repr(r2)) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") class ExprNumPy(ExprTestCase): kind = "NumPy" class ExprArray(ExprTestCase): kind = "Array" class ExprCArray(ExprTestCase): kind = "CArray" class ExprEArray(ExprTestCase): kind = "EArray" class ExprColumn(ExprTestCase): kind = "Column" # Test for mixed containers class MixedContainersTestCase(common.TempFileMixin, common.PyTablesTestCase): def setUp(self): super(MixedContainersTestCase, self).setUp() # The expression self.expr = "2 * a*b + c**2+d**2+e-f+g" # Create a directory in file for outputs root = self.h5file.root outs = self.h5file.create_group(root, "outs") # Define the NumPy variables to be used in expression N = np.prod(self.shape) # Initial values for variables a = np.arange(0, N, dtype='int32').reshape(self.shape) b = np.arange(N, 2 * N, dtype='int64').reshape(self.shape) c = np.arange(2 * N, 3*N, dtype='int32').reshape(self.shape) d = np.arange(3 * N, 4*N, dtype='int32').reshape(self.shape) e = np.arange(4 * N, 5*N, dtype='int32').reshape(self.shape) self.f = f = long(3) # a regular python type self.g = g = np.int16(2) # a NumPy scalar type # Original values self.npvars = {"a": a, "b": b, "c": c, "d": d, "e": e, "f": f, "g": g} rnda = b.copy() # ndarray input and output self.a = a self.rnda = rnda # Array input and output self.b = self.h5file.create_array(root, "b", b) self.rarr = self.b.copy(outs) # CArray input and output self.c = self.h5file.create_carray( root, "c", atom=tb.Atom.from_dtype(c.dtype), shape=self.shape) self.c[:] = c self.rcarr = self.c.copy(outs) # EArray input and output eshape = list(self.shape) eshape[0] = 0 self.d = self.h5file.create_earray( root, "d", atom=tb.Atom.from_dtype(d.dtype), shape=eshape) self.d.append(d) self.rearr = self.d.copy(outs) # Column input and output rtype = {} colshape = self.shape[1:] for i, col in enumerate((a, b, c, d, e, rnda)): rtype['f%d' % i] = tb.Col.from_sctype(col.dtype.type, colshape) t = self.h5file.create_table(root, "t", rtype) nrows = self.shape[0] row = t.row for nrow in range(nrows): for i, col in enumerate((a, b, c, d, e, rnda)): row['f%d' % i] = col[nrow] row.append() t.flush() self.e = t.cols.f4 self.rcol = t.cols.f5 # Input vars self.vars = {"a": self.a, "b": self.b, "c": self.c, "d": self.d, "e": self.e, "f": self.f, "g": self.g, } def test00a_simple(self): """Checking expressions with mixed objects.""" expr = tb.Expr(self.expr, self.vars) r1 = expr.eval() r2 = eval(self.expr, self.npvars) if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") def test00b_simple_scalars(self): """Checking that scalars in expression evaluate correctly.""" expr_str = "2 * f + g" expr = tb.Expr(expr_str, self.vars) r1 = expr.eval() r2 = eval(expr_str, self.npvars) if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue( r1.shape == r2.shape and r1.dtype == r2.dtype and r1 == r2, "Evaluate is returning a wrong value.") def test01a_out(self): """Checking expressions with mixed objects (`out` param)""" expr = tb.Expr(self.expr, self.vars) for r1 in self.rnda, self.rarr, self.rcarr, self.rearr, self.rcol: if common.verbose: print("Checking output container:", type(r1)) expr.set_output(r1) r1 = expr.eval() if not isinstance(r1, type(self.rnda)): r1 = r1[:] r2 = eval(self.expr, self.npvars) if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") def test01b_out_scalars(self): """Checking expressions with mixed objects (`out` param, scalars)""" if len(self.shape) > 1: # This test is only meant for undimensional outputs return expr_str = "2 * f + g" expr = tb.Expr(expr_str, self.vars) for r1 in self.rnda, self.rarr, self.rcarr, self.rearr, self.rcol: if common.verbose: print("Checking output container:", type(r1)) expr.set_output(r1) r1 = expr.eval() r1 = r1[()] # convert a 0-dim array into a scalar r2 = eval(expr_str, self.npvars) if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") def test02a_sss(self): """Checking mixed objects and start, stop, step (I)""" start, stop, step = (self.start, self.stop, 1) expr = tb.Expr(self.expr, self.vars) expr.set_inputs_range(start, stop, step) r1 = expr.eval() npvars = get_sliced_vars(self.npvars, start, stop, step) r2 = eval(self.expr, npvars) if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") def test02b_sss(self): """Checking mixed objects and start, stop, step (II)""" start, stop, step = (0, self.shape[0], self.step) expr = tb.Expr(self.expr, self.vars) expr.set_inputs_range(start, stop, step) r1 = expr.eval() npvars = get_sliced_vars(self.npvars, start, stop, step) r2 = eval(self.expr, npvars) if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") def test02c_sss(self): """Checking mixed objects and start, stop, step (III)""" start, stop, step = (self.start, self.stop, self.step) expr = tb.Expr(self.expr, self.vars) expr.set_inputs_range(start, stop, step) r1 = expr.eval() npvars = get_sliced_vars(self.npvars, start, stop, step) r2 = eval(self.expr, npvars) if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") def test03_sss(self): """Checking start, stop, step as numpy.int64.""" start, stop, step = [np.int64(i) for i in (self.start, self.stop, self.step)] expr = tb.Expr(self.expr, self.vars) expr.set_inputs_range(start, stop, step) r1 = expr.eval() npvars = get_sliced_vars(self.npvars, start, stop, step) r2 = eval(self.expr, npvars) if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") class MixedContainers0(MixedContainersTestCase): shape = (1,) start, stop, step = (0, 1, 1) class MixedContainers1(MixedContainersTestCase): shape = (10,) start, stop, step = (3, 6, 2) class MixedContainers2(MixedContainersTestCase): shape = (10, 5) start, stop, step = (2, 9, 3) class MixedContainers3(MixedContainersTestCase): shape = (10, 3, 2) start, stop, step = (2, -1, 1) # Test for unaligned objects class UnalignedObject(common.PyTablesTestCase): def test00_simple(self): """Checking expressions with unaligned objects.""" # Build unaligned arrays a0 = np.empty(10, dtype="int8") a1 = np.arange(10, dtype="int32") a2 = a1.copy() a3 = a2.copy() ra = np.rec.fromarrays([a0, a1, a2, a3]) # The inputs a = ra['f1'] b = ra['f2'] self.assertEqual(a.flags.aligned, False) self.assertEqual(b.flags.aligned, False) # The expression sexpr = "2 * a + b" expr = tb.Expr(sexpr) r1 = expr.eval() r2 = eval(sexpr) if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") def test01_md(self): """Checking expressions with unaligned objects (MD version)""" # Build unaligned arrays a0 = np.empty((10, 4), dtype="int8") a1 = np.arange(10 * 4, dtype="int32").reshape(10, 4) a2 = a1.copy() a3 = a2.copy() ra = np.rec.fromarrays([a0, a1, a2, a3]) # The inputs a = ra['f1'] b = ra['f2'] self.assertEqual(a.flags.aligned, False) self.assertEqual(b.flags.aligned, False) # The expression sexpr = "2 * a + b" expr = tb.Expr(sexpr) r1 = expr.eval() r2 = eval(sexpr) if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") # Test for non-contiguous objects class NonContiguousObject(common.PyTablesTestCase): def test00_simple(self): """Checking expressions with non-contiguous objects""" # Build non-contiguous arrays as inputs a = np.arange(10, dtype="int32") b = a[::2] a = b * 2 self.assertEqual(b.flags.contiguous, False) self.assertEqual(b.flags.aligned, True) # The expression sexpr = "2 * a + b" expr = tb.Expr(sexpr) r1 = expr.eval() r2 = eval(sexpr) if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") def test01a_md(self): """Checking expressions with non-contiguous objects (MD version, I)""" # Build non-contiguous arrays a = np.arange(10 * 4, dtype="int32").reshape(10, 4) b = a[::2] a = b * 2 self.assertEqual(b.flags.contiguous, False) self.assertEqual(b.flags.aligned, True) # The expression sexpr = "2 * a + b" expr = tb.Expr(sexpr) r1 = expr.eval() r2 = eval(sexpr) if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") def test01b_md(self): """Checking expressions with non-contiguous objects (MD version, II)""" # Build non-contiguous arrays a = np.arange(10 * 4, dtype="int32").reshape(10, 4) b = a[:, ::2] a = b * 2 self.assertEqual(b.flags.contiguous, False) self.assertEqual(b.flags.aligned, True) # The expression sexpr = "2 * a + b" expr = tb.Expr(sexpr) r1 = expr.eval() r2 = eval(sexpr) if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") # Test for errors class ExprError(common.TempFileMixin, common.PyTablesTestCase): # The shape for the variables in expressions shape = (10,) def setUp(self): super(ExprError, self).setUp() # Define the NumPy variables to be used in expression N = np.prod(self.shape) self.a = np.arange(N, dtype='int32').reshape(self.shape) self.b = np.arange(N, dtype='int64').reshape(self.shape) self.c = np.arange(N, dtype='int32').reshape(self.shape) self.r1 = np.empty(N, dtype='int64').reshape(self.shape) def _test00_shape(self): """Checking that inconsistent shapes are detected.""" self.b = self.b.reshape(self.shape+(1,)) expr = "a * b + c" vars_ = {"a": self.a, "b": self.b, "c": self.c, } expr = tb.Expr(expr, vars_) self.assertRaises(ValueError, expr.eval) def test02_uint64(self): """Checking that uint64 arrays in expression are detected.""" self.b = self.b.view('uint64') expr = "a * b + c" vars_ = {"a": self.a, "b": self.b, "c": self.c, } self.assertRaises(NotImplementedError, tb.Expr, expr, vars_) def test03_table(self): """Checking that tables in expression are detected.""" class Rec(tb.IsDescription): col1 = tb.Int32Col() col2 = tb.Int64Col() t = self.h5file.create_table("/", "a", Rec) expr = "a * b + c" vars_ = {"a": t, "b": self.b, "c": self.c, } self.assertRaises(TypeError, tb.Expr, expr, vars_) def test04_nestedcols(self): """Checking that nested cols in expression are detected.""" class Nested(tb.IsDescription): col1 = tb.Int32Col() class col2(tb.IsDescription): col3 = tb.Int64Col() t = self.h5file.create_table("/", "a", Nested) expr = "a * b + c" # The next non-nested column should work a = t.cols.col2.col3 vars_ = {"a": a, "b": self.b, "c": self.c, } expr = tb.Expr(expr, vars_) r1 = expr.eval() self.assertTrue(r1 is not None) # But a nested column should not a = t.cols.col2 vars_ = {"a": a, "b": self.b, "c": self.c, } self.assertRaises(TypeError, tb.Expr, expr, vars_) def test05_vlarray(self): """Checking that VLArrays in expression are detected.""" vla = self.h5file.create_vlarray("/", "a", tb.Int32Col()) expr = "a * b + c" vars_ = {"a": vla, "b": self.b, "c": self.c, } self.assertRaises(TypeError, tb.Expr, expr, vars_) # Test for broadcasting arrays class BroadcastTestCase(common.TempFileMixin, common.PyTablesTestCase): def test00_simple(self): """Checking broadcast in expression.""" shapes = (self.shape1, self.shape2, self.shape3) # Build arrays with different shapes as inputs a = np.arange(np.prod(shapes[0]), dtype="i4").reshape(shapes[0]) b = np.arange(np.prod(shapes[1]), dtype="i4").reshape(shapes[1]) c = np.arange(np.prod(shapes[2]), dtype="i4").reshape(shapes[2]) root = self.h5file.root if a.shape[0] > 0: a1 = self.h5file.create_array(root, 'a1', a) else: a1 = self.h5file.create_earray( root, 'a1', atom=tb.Int32Col(), shape=a.shape) self.assertTrue(a1 is not None) b1 = self.h5file.create_array(root, 'b1', b) self.assertTrue(b1 is not None) c1 = self.h5file.create_array(root, 'c1', c) self.assertTrue(c1 is not None) # The expression expr = tb.Expr("2 * a1 + b1-c1") r1 = expr.eval() r2 = eval("2 * a + b-c") if common.verbose: print("Tested shapes:", self.shape1, self.shape2, self.shape3) print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") class Broadcast0(BroadcastTestCase): shape1 = (0, 3, 4) shape2 = (3, 4) shape3 = (4,) class Broadcast1(BroadcastTestCase): shape1 = (2, 3, 4) shape2 = (3, 4) shape3 = (4,) class Broadcast2(BroadcastTestCase): shape1 = (3, 4,) shape2 = (3, 4) shape3 = (4,) class Broadcast3(BroadcastTestCase): shape1 = (4,) shape2 = (3, 4) shape3 = (4,) class Broadcast4(BroadcastTestCase): shape1 = (1,) shape2 = (3, 4) shape3 = (4,) class Broadcast5(BroadcastTestCase): shape1 = (1,) shape2 = (3, 1) shape3 = (4,) # Test for different length inputs class DiffLengthTestCase(common.TempFileMixin, common.PyTablesTestCase): def test00_simple(self): """Checking different length inputs in expression.""" shapes = (list(self.shape1), list(self.shape2), list(self.shape3)) # Build arrays with different shapes as inputs a = np.arange(np.prod(shapes[0]), dtype="i4").reshape(shapes[0]) b = np.arange(np.prod(shapes[1]), dtype="i4").reshape(shapes[1]) c = np.arange(np.prod(shapes[2]), dtype="i4").reshape(shapes[2]) # The expression expr = tb.Expr("2 * a + b-c") r1 = expr.eval() # Compute the minimum length for shapes maxdim = max([len(shape) for shape in shapes]) minlen = min([shape[0] for i, shape in enumerate(shapes) if len(shape) == maxdim]) for i, shape in enumerate(shapes): if len(shape) == maxdim: shape[0] = minlen # Build arrays with the new shapes as inputs a = np.arange(np.prod(shapes[0]), dtype="i4").reshape(shapes[0]) self.assertTrue(a is not None) b = np.arange(np.prod(shapes[1]), dtype="i4").reshape(shapes[1]) self.assertTrue(b is not None) c = np.arange(np.prod(shapes[2]), dtype="i4").reshape(shapes[2]) self.assertTrue(c is not None) r2 = eval("2 * a + b-c") if common.verbose: print("Tested shapes:", self.shape1, self.shape2, self.shape3) print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") class DiffLength0(DiffLengthTestCase): shape1 = (0,) shape2 = (10,) shape3 = (20,) class DiffLength1(DiffLengthTestCase): shape1 = (3,) shape2 = (10,) shape3 = (20,) class DiffLength2(DiffLengthTestCase): shape1 = (3, 4) shape2 = (2, 3, 4) shape3 = (4, 3, 4) class DiffLength3(DiffLengthTestCase): shape1 = (1, 3, 4) shape2 = (2, 3, 4) shape3 = (4, 3, 4) class DiffLength4(DiffLengthTestCase): shape1 = (0, 3, 4) shape2 = (2, 3, 4) shape3 = (4, 3, 4) # Test for different type inputs class TypesTestCase(common.TempFileMixin, common.PyTablesTestCase): def test00_bool(self): """Checking booleans in expression.""" # Build arrays with different shapes as inputs a = np.array([True, False, True]) b = np.array([False, True, False]) root = self.h5file.root a1 = self.h5file.create_array(root, 'a1', a) self.assertTrue(a1 is not None) b1 = self.h5file.create_array(root, 'b1', b) self.assertTrue(b1 is not None) expr = tb.Expr("a | b") r1 = expr.eval() r2 = eval("a | b") if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") def test01_shortint(self): """Checking int8,uint8,int16,uint16 and int32 in expression.""" for dtype in 'int8', 'uint8', 'int16', 'uint16', 'int32': if common.verbose: print("Checking type:", dtype) # Build arrays with different shapes as inputs a = np.array([1, 2, 3], dtype) b = np.array([3, 4, 5], dtype) root = self.h5file.root a1 = self.h5file.create_array(root, 'a1', a) b1 = self.h5file.create_array(root, 'b1', b) two = np.int32(2) expr = tb.Expr("two * a1-b1") r1 = expr.eval() a = np.array([1, 2, 3], 'int32') b = np.array([3, 4, 5], 'int32') r2 = eval("two * a-b") if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertEqual(r1.dtype, r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") # Remove created leaves a1.remove() b1.remove() def test02_longint(self): """Checking uint32 and int64 in expression.""" for dtype in 'uint32', 'int64': if common.verbose: print("Checking type:", dtype) # Build arrays with different shapes as inputs a = np.array([1, 2, 3], dtype) b = np.array([3, 4, 5], dtype) root = self.h5file.root a1 = self.h5file.create_array(root, 'a1', a) b1 = self.h5file.create_array(root, 'b1', b) expr = tb.Expr("2 * a1-b1") r1 = expr.eval() a = np.array([1, 2, 3], 'int64') b = np.array([3, 4, 5], 'int64') r2 = eval("2 * a-b") if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertEqual(r1.dtype, r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") # Remove created leaves a1.remove() b1.remove() def test03_float(self): """Checking float32 and float64 in expression.""" for dtype in 'float32', 'float64': if common.verbose: print("Checking type:", dtype) # Build arrays with different shapes as inputs a = np.array([1, 2, 3], dtype) b = np.array([3, 4, 5], dtype) root = self.h5file.root a1 = self.h5file.create_array(root, 'a1', a) b1 = self.h5file.create_array(root, 'b1', b) expr = tb.Expr("2 * a1-b1") r1 = expr.eval() a = np.array([1, 2, 3], dtype) b = np.array([3, 4, 5], dtype) r2 = eval("2 * a-b") if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertEqual(r1.dtype, r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") # Remove created leaves a1.remove() b1.remove() def test04_complex(self): """Checking complex64 and complex128 in expression.""" for dtype in 'complex64', 'complex128': if common.verbose: print("Checking type:", dtype) # Build arrays with different shapes as inputs a = np.array([1, 2j, 3 + 2j], dtype) b = np.array([3, 4j, 5 + 1j], dtype) root = self.h5file.root a1 = self.h5file.create_array(root, 'a1', a) b1 = self.h5file.create_array(root, 'b1', b) expr = tb.Expr("2 * a1-b1") r1 = expr.eval() a = np.array([1, 2j, 3 + 2j], 'complex128') b = np.array([3, 4j, 5 + 1j], 'complex128') r2 = eval("2 * a-b") if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertEqual(r1.dtype, r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") # Remove created leaves a1.remove() b1.remove() def test05_string(self): """Checking strings in expression.""" # Build arrays with different shapes as inputs a = np.array(['a', 'bd', 'cd'], 'S') b = np.array(['a', 'bdcd', 'ccdc'], 'S') root = self.h5file.root a1 = self.h5file.create_array(root, 'a1', a) self.assertTrue(a1 is not None) b1 = self.h5file.create_array(root, 'b1', b) self.assertTrue(b1 is not None) expr = tb.Expr("(a1 > b'a') | ( b1 > b'b')") r1 = expr.eval() r2 = eval("(a > b'a') | ( b > b'b')") if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") # Test for different functions class FunctionsTestCase(common.TempFileMixin, common.PyTablesTestCase): def test00_simple(self): """Checking some math functions in expression.""" # Build arrays with different shapes as inputs a = np.array([.1, .2, .3]) b = np.array([.3, .4, .5]) root = self.h5file.root a1 = self.h5file.create_array(root, 'a1', a) self.assertTrue(a1 is not None) b1 = self.h5file.create_array(root, 'b1', b) self.assertTrue(b1 is not None) # The expression expr = tb.Expr("sin(a1) * sqrt(b1)") r1 = expr.eval() r2 = np.sin(a) * np.sqrt(b) if common.verbose: print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") # Test for EArrays with maindim != 0 class MaindimTestCase(common.TempFileMixin, common.PyTablesTestCase): def test00_simple(self): """Checking other dimensions than 0 as main dimension.""" shape = list(self.shape) # Build input arrays a = np.arange(np.prod(shape), dtype="i4").reshape(shape) b = a.copy() c = a.copy() root = self.h5file.root shape[self.maindim] = 0 a1 = self.h5file.create_earray( root, 'a1', atom=tb.Int32Col(), shape=shape) b1 = self.h5file.create_earray( root, 'b1', atom=tb.Int32Col(), shape=shape) c1 = self.h5file.create_earray( root, 'c1', atom=tb.Int32Col(), shape=shape) a1.append(a) b1.append(b) c1.append(c) # The expression expr = tb.Expr("2 * a1 + b1-c1") r1 = expr.eval() r2 = eval("2 * a + b-c") if common.verbose: print("Tested shape:", shape) print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") def test01_out(self): """Checking other dimensions than 0 as main dimension (out)""" shape = list(self.shape) # Build input arrays a = np.arange(np.prod(shape), dtype="i4").reshape(shape) b = a.copy() c = a.copy() root = self.h5file.root shape[self.maindim] = 0 a1 = self.h5file.create_earray( root, 'a1', atom=tb.Int32Col(), shape=shape) b1 = self.h5file.create_earray( root, 'b1', atom=tb.Int32Col(), shape=shape) c1 = self.h5file.create_earray( root, 'c1', atom=tb.Int32Col(), shape=shape) r1 = self.h5file.create_earray( root, 'r1', atom=tb.Int32Col(), shape=shape) a1.append(a) b1.append(b) c1.append(c) r1.append(c) # The expression expr = tb.Expr("2 * a1 + b1-c1") expr.set_output(r1) expr.eval() r2 = eval("2 * a + b-c") if common.verbose: print("Tested shape:", shape) print("Computed expression:", repr(r1[:]), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1[:], r2), "Evaluate is returning a wrong value.") def test02_diff_in_maindims(self): """Checking different main dimensions in inputs.""" shape = list(self.shape) # Build input arrays a = np.arange(np.prod(shape), dtype="i4").reshape(shape) b = a.copy() c = a.copy() root = self.h5file.root shape2 = shape[:] shape[self.maindim] = 0 shape2[0] = 0 a1 = self.h5file.create_earray( root, 'a1', atom=tb.Int32Col(), shape=shape) self.assertTrue(a1.maindim, self.maindim) b1 = self.h5file.create_earray( root, 'b1', atom=tb.Int32Col(), shape=shape2) self.assertEqual(b1.maindim, 0) c1 = self.h5file.create_earray( root, 'c1', atom=tb.Int32Col(), shape=shape) r1 = self.h5file.create_earray( root, 'r1', atom=tb.Int32Col(), shape=shape) a1.append(a) b1.append(b) c1.append(c) r1.append(c) # The expression expr = tb.Expr("2 * a1 + b1-c1") r1 = expr.eval() r2 = eval("2 * a + b-c") if common.verbose: print("Tested shape:", shape) print("Computed expression:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") def test03_diff_in_out_maindims(self): """Checking different maindims in inputs and output.""" shape = list(self.shape) # Build input arrays a = np.arange(np.prod(shape), dtype="i4").reshape(shape) b = a.copy() c = a.copy() root = self.h5file.root shape2 = shape[:] shape[self.maindim] = 0 shape2[0] = 0 a1 = self.h5file.create_earray( root, 'a1', atom=tb.Int32Col(), shape=shape) self.assertTrue(a1.maindim, self.maindim) b1 = self.h5file.create_earray( root, 'b1', atom=tb.Int32Col(), shape=shape) c1 = self.h5file.create_earray( root, 'c1', atom=tb.Int32Col(), shape=shape) r1 = self.h5file.create_earray( root, 'r1', atom=tb.Int32Col(), shape=shape2) self.assertEqual(r1.maindim, 0) a1.append(a) b1.append(b) c1.append(c) r1.append(c) # The expression expr = tb.Expr("2 * a1 + b1-c1") expr.set_output(r1) expr.eval() r2 = eval("2 * a + b-c") if common.verbose: print("Tested shape:", shape) print("Computed expression:", repr(r1[:]), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1[:], r2), "Evaluate is returning a wrong value.") def test04_diff_in_out_maindims_lengths(self): """Checking different maindims and lengths in inputs and output.""" shape = list(self.shape) # Build input arrays a = np.arange(np.prod(shape), dtype="i4").reshape(shape) b = a.copy() c = a.copy() root = self.h5file.root shape2 = shape[:] shape[self.maindim] = 0 shape2[0] = 0 a1 = self.h5file.create_earray( root, 'a1', atom=tb.Int32Col(), shape=shape) self.assertTrue(a1.maindim, self.maindim) b1 = self.h5file.create_earray( root, 'b1', atom=tb.Int32Col(), shape=shape) c1 = self.h5file.create_earray( root, 'c1', atom=tb.Int32Col(), shape=shape) r1 = self.h5file.create_earray( root, 'r1', atom=tb.Int32Col(), shape=shape2) self.assertEqual(r1.maindim, 0) a1.append(a) a1.append(a) b1.append(b) b1.append(b) c1.append(c) c1.append(c) r1.append(c) # just once so that output is smaller # The expression expr = tb.Expr("2 * a1 + b1-c1") expr.set_output(r1) # This should raise an error self.assertRaises(ValueError, expr.eval) class Maindim0(MaindimTestCase): maindim = 1 shape = (1, 2) class Maindim1(MaindimTestCase): maindim = 1 shape = (2, 3) class Maindim2(MaindimTestCase): maindim = 1 shape = (2, 3, 4) class Maindim3(MaindimTestCase): maindim = 2 shape = (2, 3, 4) # Test `append` mode flag in `set_output()` class AppendModeTestCase(common.TempFileMixin, common.PyTablesTestCase): def test01_append(self): """Checking append mode in `set_output()`""" shape = [3, 2] # Build input arrays a = np.arange(np.prod(shape), dtype="i4").reshape(shape) b = a.copy() c = a.copy() shape[1] = 0 root = self.h5file.root a1 = self.h5file.create_earray( root, 'a1', atom=tb.Int32Col(), shape=shape) b1 = self.h5file.create_earray( root, 'b1', atom=tb.Int32Col(), shape=shape) c1 = self.h5file.create_earray( root, 'c1', atom=tb.Int32Col(), shape=shape) r1 = self.h5file.create_earray( root, 'r1', atom=tb.Int32Col(), shape=shape) a1.append(a) b1.append(b) c1.append(c) if not self.append: r1.append(c) # The expression expr = tb.Expr("2 * a1 + b1-c1") expr.set_output(r1, append_mode=self.append) expr.eval() r2 = eval("2 * a + b-c") if common.verbose: print("Tested shape:", shape) print("Computed expression:", repr(r1[:]), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1[:], r2), "Evaluate is returning a wrong value.") class AppendModeTrue(AppendModeTestCase): append = True class AppendModeFalse(AppendModeTestCase): append = False # Test for `__iter__()` iterator class iterTestCase(common.TempFileMixin, common.PyTablesTestCase): def setUp(self): super(iterTestCase, self).setUp() shape = list(self.shape) # Build input arrays a = np.arange(np.prod(shape), dtype="i4").reshape(shape) b = a.copy() c = a.copy() self.npvars = {'a': a, 'b': b, 'c': c} shape[self.maindim] = 0 root = self.h5file.root a1 = self.h5file.create_earray( root, 'a1', atom=tb.Int32Col(), shape=shape) b1 = self.h5file.create_earray( root, 'b1', atom=tb.Int32Col(), shape=shape) c1 = self.h5file.create_earray( root, 'c1', atom=tb.Int32Col(), shape=shape) a1.append(a) b1.append(b) c1.append(c) self.vars = {'a': a1, 'b': b1, 'c': c1} # The expression self.sexpr = "2 * a + b-c" def test00_iter(self): """Checking the __iter__ iterator.""" expr = tb.Expr(self.sexpr, self.vars) r1 = np.array([row for row in expr]) r2 = eval(self.sexpr, self.npvars) if common.verbose: print("Tested shape, maindim:", self.shape, self.maindim) print("Computed expression:", repr(r1[:]), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1[:], r2), "Evaluate is returning a wrong value.") def test01a_sss(self): """Checking the __iter__ iterator (with ranges, I)""" start, stop, step = self.range_[0], None, None expr = tb.Expr(self.sexpr, self.vars) expr.set_inputs_range(start, stop, step) r1 = np.array([row for row in expr]) npvars = get_sliced_vars2( self.npvars, start, stop, step, self.shape, self.maindim) r2 = eval(self.sexpr, npvars) if common.verbose: print("Tested shape, maindim:", self.shape, self.maindim) print("Computed expression:", repr(r1[:]), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1[:], r2), "Evaluate is returning a wrong value.") def test01b_sss(self): """Checking the __iter__ iterator (with ranges, II)""" start, stop, step = self.range_[0], self.range_[2], None expr = tb.Expr(self.sexpr, self.vars) expr.set_inputs_range(start, stop, step) r1 = np.array([row for row in expr]) npvars = get_sliced_vars2( self.npvars, start, stop, step, self.shape, self.maindim) r2 = eval(self.sexpr, npvars) if common.verbose: print("Tested shape, maindim:", self.shape, self.maindim) print("Computed expression:", repr(r1[:]), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1[:], r2), "Evaluate is returning a wrong value.") def test01c_sss(self): """Checking the __iter__ iterator (with ranges, III)""" start, stop, step = self.range_ expr = tb.Expr(self.sexpr, self.vars) expr.set_inputs_range(start, stop, step) r1 = np.array([row for row in expr]) npvars = get_sliced_vars2( self.npvars, start, stop, step, self.shape, self.maindim) r2 = eval(self.sexpr, npvars) if common.verbose: print("Tested shape, maindim:", self.shape, self.maindim) print("Computed expression:", repr(r1[:]), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1[:], r2), "Evaluate is returning a wrong value.") class iter0(iterTestCase): maindim = 0 shape = (0,) range_ = (1, 2, 1) class iter1(iterTestCase): maindim = 0 shape = (3,) range_ = (1, 2, 1) class iter2(iterTestCase): maindim = 0 shape = (3, 2) range_ = (0, 3, 2) class iter3(iterTestCase): maindim = 1 shape = (3, 2) range_ = (0, 3, 2) class iter4(iterTestCase): maindim = 2 shape = (3, 2, 1) range_ = (1, 3, 2) class iter5(iterTestCase): maindim = 2 shape = (1, 2, 5) range_ = (0, 4, 2) # Test for set_output_range class setOutputRangeTestCase(common.TempFileMixin, common.PyTablesTestCase): def test00_simple(self): """Checking the range selection for output.""" shape = list(self.shape) start, stop, step = self.range_ # Build input arrays a = np.arange(np.prod(shape), dtype="i4").reshape(shape) b = a.copy() r = a.copy() root = self.h5file.root a1 = self.h5file.create_array(root, 'a1', a) self.assertTrue(a1 is not None) b1 = self.h5file.create_array(root, 'b1', b) self.assertTrue(b1 is not None) r1 = self.h5file.create_array(root, 'r1', r) # The expression expr = tb.Expr("a1-b1-1") expr.set_output(r1) expr.set_output_range(start, stop, step) expr.eval() r2 = eval("a-b-1") r[start:stop:step] = r2[:len(xrange(start, stop, step))] if common.verbose: print("Tested shape:", shape) print("Computed expression:", repr(r1[:]), r1.dtype) print("Should look like:", repr(r), r.dtype) self.assertTrue(common.areArraysEqual(r1[:], r), "Evaluate is returning a wrong value.") def test01_maindim(self): """Checking the range selection for output (maindim > 0)""" shape = list(self.shape) start, stop, step = self.range_ # Build input arrays a = np.arange(np.prod(shape), dtype="i4").reshape(shape) b = a.copy() r = a.copy() shape[self.maindim] = 0 root = self.h5file.root a1 = self.h5file.create_earray( root, 'a1', atom=tb.Int32Col(), shape=shape) b1 = self.h5file.create_earray( root, 'b1', atom=tb.Int32Col(), shape=shape) r1 = self.h5file.create_earray( root, 'r1', atom=tb.Int32Col(), shape=shape) a1.append(a) b1.append(b) r1.append(r) # The expression expr = tb.Expr("a1-b1-1") expr.set_output(r1) expr.set_output_range(start, stop, step) expr.eval() r2 = eval("a-b-1") lsl = tuple([slice(None)] * self.maindim) # print "lsl-->", lsl + (slice(start,stop,step),) l = len(xrange(start, stop, step)) r.__setitem__(lsl + (slice(start, stop, step),), r2.__getitem__(lsl + (slice(0, l),))) if common.verbose: print("Tested shape:", shape) print("Computed expression:", repr(r1[:]), r1.dtype) print("Should look like:", repr(r), r.dtype) self.assertTrue(common.areArraysEqual(r1[:], r), "Evaluate is returning a wrong value.") class setOutputRange0(setOutputRangeTestCase): maindim = 0 shape = (10,) range_ = (0, 1, 2) class setOutputRange1(setOutputRangeTestCase): maindim = 0 shape = (10,) range_ = (0, 10, 2) class setOutputRange2(setOutputRangeTestCase): maindim = 0 shape = (10,) range_ = (1, 10, 2) class setOutputRange3(setOutputRangeTestCase): maindim = 0 shape = (10, 1) range_ = (1, 10, 3) class setOutputRange4(setOutputRangeTestCase): maindim = 0 shape = (10, 2) range_ = (1, 10, 3) class setOutputRange5(setOutputRangeTestCase): maindim = 0 shape = (5, 3, 1) range_ = (1, 5, 1) class setOutputRange6(setOutputRangeTestCase): maindim = 1 shape = (2, 5) range_ = (1, 3, 2) class setOutputRange7(setOutputRangeTestCase): maindim = 1 shape = (2, 5, 1) range_ = (1, 3, 2) class setOutputRange8(setOutputRangeTestCase): maindim = 2 shape = (1, 3, 5) range_ = (1, 5, 2) class setOutputRange9(setOutputRangeTestCase): maindim = 3 shape = (1, 3, 4, 5) range_ = (1, 5, 3) # Test for very large inputs class VeryLargeInputsTestCase(common.TempFileMixin, common.PyTablesTestCase): def test00_simple(self): """Checking very large inputs.""" shape = self.shape # Use filters so as to not use too much space if tb.which_lib_version("blosc") is not None: filters = tb.Filters(complevel=1, complib='blosc', shuffle=False) elif tb.which_lib_version("lzo") is not None: filters = tb.Filters(complevel=1, complib='lzo', shuffle=False) else: filters = tb.Filters(complevel=1, shuffle=False) # Build input arrays root = self.h5file.root a = self.h5file.create_carray(root, 'a', atom=tb.Float64Atom(dflt=3), shape=shape, filters=filters) self.assertTrue(a is not None) b = self.h5file.create_carray(root, 'b', atom=tb.Float64Atom(dflt=2), shape=shape, filters=filters) self.assertTrue(b is not None) r1 = self.h5file.create_carray(root, 'r1', atom=tb.Float64Atom(dflt=3), shape=shape, filters=filters) # The expression expr = tb.Expr("a * b-6") # Should give 0 expr.set_output(r1) expr.eval() r1 = r1[-10:] # Get the last ten rows r2 = np.zeros(10, dtype='float64') if common.verbose: print("Tested shape:", shape) print("Ten last rows:", repr(r1), r1.dtype) print("Should look like:", repr(r2), r2.dtype) self.assertTrue(common.areArraysEqual(r1, r2), "Evaluate is returning a wrong value.") def test01_iter(self): """Checking very large inputs (__iter__ version)""" shape = self.shape if shape[0] >= 2**24: # The iterator is much more slower, so don't run it for # extremeley large arrays. if common.verbose: print("Skipping this *very* long test") return # Use filters so as to not use too much space if tb.which_lib_version("lzo") is not None: filters = tb.Filters(complevel=1, complib='lzo', shuffle=False) else: filters = tb.Filters(complevel=1, shuffle=False) # Build input arrays root = self.h5file.root a = self.h5file.create_carray(root, 'a', atom=tb.Int32Atom(dflt=1), shape=shape, filters=filters) self.assertTrue(a is not None) b = self.h5file.create_carray(root, 'b', atom=tb.Int32Atom(dflt=2), shape=shape, filters=filters) self.assertTrue(b is not None) r1 = self.h5file.create_carray(root, 'r1', atom=tb.Int32Atom(dflt=3), shape=shape, filters=filters) # The expression expr = tb.Expr("a-b + 1") r1 = sum(expr) # Should give 0 if common.verbose: print("Tested shape:", shape) print("Cummulated sum:", r1) print("Should look like:", 0) self.assertEqual(r1, 0, "Evaluate is returning a wrong value.") # The next can go on regular tests, as it should be light enough class VeryLargeInputs1(VeryLargeInputsTestCase): shape = (2**20,) # larger than any internal I/O buffers # The next is only meant for 'heavy' mode as it can take more than 1 minute # on modern machines class VeryLargeInputs2(VeryLargeInputsTestCase): shape = (2**32 + 1,) # check that arrays > 32-bit are supported #---------------------------------------------------------------------- def suite(): """Return a test suite consisting of all the test cases in the module.""" theSuite = unittest.TestSuite() niter = 1 # common.heavy = 1 # uncomment this only for testing purposes for i in range(niter): theSuite.addTest(unittest.makeSuite(ExprNumPy)) theSuite.addTest(unittest.makeSuite(ExprArray)) theSuite.addTest(unittest.makeSuite(ExprCArray)) theSuite.addTest(unittest.makeSuite(ExprEArray)) theSuite.addTest(unittest.makeSuite(ExprColumn)) theSuite.addTest(unittest.makeSuite(MixedContainers0)) theSuite.addTest(unittest.makeSuite(MixedContainers1)) theSuite.addTest(unittest.makeSuite(MixedContainers2)) theSuite.addTest(unittest.makeSuite(MixedContainers3)) theSuite.addTest(unittest.makeSuite(UnalignedObject)) theSuite.addTest(unittest.makeSuite(NonContiguousObject)) theSuite.addTest(unittest.makeSuite(ExprError)) theSuite.addTest(unittest.makeSuite(Broadcast0)) theSuite.addTest(unittest.makeSuite(Broadcast1)) theSuite.addTest(unittest.makeSuite(Broadcast2)) theSuite.addTest(unittest.makeSuite(Broadcast3)) theSuite.addTest(unittest.makeSuite(Broadcast4)) theSuite.addTest(unittest.makeSuite(Broadcast5)) theSuite.addTest(unittest.makeSuite(DiffLength0)) theSuite.addTest(unittest.makeSuite(DiffLength1)) theSuite.addTest(unittest.makeSuite(DiffLength2)) theSuite.addTest(unittest.makeSuite(DiffLength3)) theSuite.addTest(unittest.makeSuite(DiffLength4)) theSuite.addTest(unittest.makeSuite(TypesTestCase)) theSuite.addTest(unittest.makeSuite(FunctionsTestCase)) theSuite.addTest(unittest.makeSuite(Maindim0)) theSuite.addTest(unittest.makeSuite(Maindim1)) theSuite.addTest(unittest.makeSuite(Maindim2)) theSuite.addTest(unittest.makeSuite(Maindim3)) theSuite.addTest(unittest.makeSuite(AppendModeTrue)) theSuite.addTest(unittest.makeSuite(AppendModeFalse)) theSuite.addTest(unittest.makeSuite(iter0)) theSuite.addTest(unittest.makeSuite(iter1)) theSuite.addTest(unittest.makeSuite(iter2)) theSuite.addTest(unittest.makeSuite(iter3)) theSuite.addTest(unittest.makeSuite(iter4)) theSuite.addTest(unittest.makeSuite(iter5)) theSuite.addTest(unittest.makeSuite(setOutputRange0)) theSuite.addTest(unittest.makeSuite(setOutputRange1)) theSuite.addTest(unittest.makeSuite(setOutputRange2)) theSuite.addTest(unittest.makeSuite(setOutputRange3)) theSuite.addTest(unittest.makeSuite(setOutputRange4)) theSuite.addTest(unittest.makeSuite(setOutputRange5)) theSuite.addTest(unittest.makeSuite(setOutputRange6)) theSuite.addTest(unittest.makeSuite(setOutputRange7)) theSuite.addTest(unittest.makeSuite(setOutputRange8)) theSuite.addTest(unittest.makeSuite(setOutputRange9)) theSuite.addTest(unittest.makeSuite(VeryLargeInputs1)) if common.heavy: theSuite.addTest(unittest.makeSuite(VeryLargeInputs2)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/tests/test_garbage.py000066400000000000000000000034701231437614300211300ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: 2005-09-20 # Author: Ivan Vilata i Balaguer - ivan@selidor.net # # $Id$ # ######################################################################## """Test module for detecting uncollectable garbage in PyTables. This test module *must* be loaded in the last place. It just checks for the existence of uncollectable garbage in ``gc.garbage`` after running all the tests. """ from __future__ import print_function import unittest import gc from tables.tests import common class GarbageTestCase(common.PyTablesTestCase): """Test for uncollectable garbage.""" def test00(self): """Checking for uncollectable garbage.""" garbageLen = len(gc.garbage) if garbageLen == 0: return # success if common.verbose: classCount = {} # Count uncollected objects for each class. for obj in gc.garbage: objClass = obj.__class__.__name__ if objClass in classCount: classCount[objClass] += 1 else: classCount[objClass] = 1 incidence = ['``%s``: %d' % (cls, cnt) for (cls, cnt) in classCount.iteritems()] print("Class incidence:", ', '.join(incidence)) self.fail("Possible leak: %d uncollected objects." % garbageLen) def suite(): """Return a test suite consisting of all the test cases in the module.""" theSuite = unittest.TestSuite() theSuite.addTest(unittest.makeSuite(GarbageTestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/tests/test_hdf5compat.py000066400000000000000000000263401231437614300215730ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: 2005-09-29 # Author: Ivan Vilata i Balaguer - ivan@selidor.net # # $Id$ # ######################################################################## """Test module for compatibility with plain HDF files.""" import unittest import tempfile import shutil import os import numpy import tables from tables.tests import common from tables.tests.common import allequal class HDF5CompatibilityTestCase(common.PyTablesTestCase): """Base class for HDF5 compatibility tests. Test cases deriving from this class must define an ``h5fname`` attribute with the name of the file to be opened, and a ``_test()`` method with checks on the opened file. """ def setUp(self): self.h5file = None def tearDown(self): if self.h5file is not None: self.h5file.close() self.h5file = None def test(self): self.h5fname = self._testFilename(self.h5fname) self.h5file = tables.open_file(self.h5fname) self._test() class EnumTestCase(HDF5CompatibilityTestCase): """Test for enumerated datatype. See ftp://ftp.hdfgroup.org/HDF5/current/src/unpacked/test/enum.c. """ h5fname = 'smpl_enum.h5' def _test(self): self.assertTrue('/EnumTest' in self.h5file) arr = self.h5file.get_node('/EnumTest') self.assertTrue(isinstance(arr, tables.Array)) enum = arr.get_enum() expectedEnum = tables.Enum(['RED', 'GREEN', 'BLUE', 'WHITE', 'BLACK']) self.assertEqual(enum, expectedEnum) data = list(arr.read()) expectedData = [ enum[name] for name in ['RED', 'GREEN', 'BLUE', 'WHITE', 'BLACK', 'RED', 'GREEN', 'BLUE', 'WHITE', 'BLACK']] self.assertEqual(data, expectedData) class NumericTestCase(HDF5CompatibilityTestCase): """Test for several numeric datatypes. See ftp://ftp.ncsa.uiuc.edu/HDF/files/hdf5/samples/[fiu]l?{8,16,32,64}{be,le}.c (they seem to be no longer available). """ def _test(self): self.assertTrue('/TestArray' in self.h5file) arr = self.h5file.get_node('/TestArray') self.assertTrue(isinstance(arr, tables.Array)) self.assertEqual(arr.atom.type, self.type) self.assertEqual(arr.byteorder, self.byteorder) self.assertEqual(arr.shape, (6, 5)) data = arr.read() expectedData = numpy.array([ [0, 1, 2, 3, 4], [1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8], [5, 6, 7, 8, 9]], dtype=self.type) self.assertTrue(common.areArraysEqual(data, expectedData)) class F64BETestCase(NumericTestCase): h5fname = 'smpl_f64be.h5' type = 'float64' byteorder = 'big' class F64LETestCase(NumericTestCase): h5fname = 'smpl_f64le.h5' type = 'float64' byteorder = 'little' class I64BETestCase(NumericTestCase): h5fname = 'smpl_i64be.h5' type = 'int64' byteorder = 'big' class I64LETestCase(NumericTestCase): h5fname = 'smpl_i64le.h5' type = 'int64' byteorder = 'little' class I32BETestCase(NumericTestCase): h5fname = 'smpl_i32be.h5' type = 'int32' byteorder = 'big' class I32LETestCase(NumericTestCase): h5fname = 'smpl_i32le.h5' type = 'int32' byteorder = 'little' class ChunkedCompoundTestCase(HDF5CompatibilityTestCase): """Test for a more complex and chunked compound structure. This is generated by a chunked version of the example in ftp://ftp.ncsa.uiuc.edu/HDF/files/hdf5/samples/compound2.c. """ h5fname = 'smpl_compound_chunked.h5' def _test(self): self.assertTrue('/CompoundChunked' in self.h5file) tbl = self.h5file.get_node('/CompoundChunked') self.assertTrue(isinstance(tbl, tables.Table)) self.assertEqual( tbl.colnames, ['a_name', 'c_name', 'd_name', 'e_name', 'f_name', 'g_name']) self.assertEqual(tbl.coltypes['a_name'], 'int32') self.assertEqual(tbl.coldtypes['a_name'].shape, ()) self.assertEqual(tbl.coltypes['c_name'], 'string') self.assertEqual(tbl.coldtypes['c_name'].shape, ()) self.assertEqual(tbl.coltypes['d_name'], 'int16') self.assertEqual(tbl.coldtypes['d_name'].shape, (5, 10)) self.assertEqual(tbl.coltypes['e_name'], 'float32') self.assertEqual(tbl.coldtypes['e_name'].shape, ()) self.assertEqual(tbl.coltypes['f_name'], 'float64') self.assertEqual(tbl.coldtypes['f_name'].shape, (10,)) self.assertEqual(tbl.coltypes['g_name'], 'uint8') self.assertEqual(tbl.coldtypes['g_name'].shape, ()) for m in range(len(tbl)): row = tbl[m] # This version of the loop seems to fail because of ``iterrows()``. # for (m, row) in enumerate(tbl): self.assertEqual(row['a_name'], m) self.assertEqual(row['c_name'], b"Hello!") dRow = row['d_name'] for n in range(5): for o in range(10): self.assertEqual(dRow[n][o], m + n + o) self.assertAlmostEqual(row['e_name'], m * 0.96, places=6) fRow = row['f_name'] for n in range(10): self.assertAlmostEqual(fRow[n], m * 1024.9637) self.assertEqual(row['g_name'], ord('m')) class ContiguousCompoundTestCase(HDF5CompatibilityTestCase): """Test for support of native contiguous compound datasets. This example has been provided by Dav Clark. """ h5fname = 'non-chunked-table.h5' def _test(self): self.assertTrue('/test_var/structure variable' in self.h5file) tbl = self.h5file.get_node('/test_var/structure variable') self.assertTrue(isinstance(tbl, tables.Table)) self.assertEqual( tbl.colnames, ['a', 'b', 'c', 'd']) self.assertEqual(tbl.coltypes['a'], 'float64') self.assertEqual(tbl.coldtypes['a'].shape, ()) self.assertEqual(tbl.coltypes['b'], 'float64') self.assertEqual(tbl.coldtypes['b'].shape, ()) self.assertEqual(tbl.coltypes['c'], 'float64') self.assertEqual(tbl.coldtypes['c'].shape, (2,)) self.assertEqual(tbl.coltypes['d'], 'string') self.assertEqual(tbl.coldtypes['d'].shape, ()) for row in tbl.iterrows(): self.assertEqual(row['a'], 3.0) self.assertEqual(row['b'], 4.0) self.assertTrue(allequal(row['c'], numpy.array([2.0, 3.0], dtype="float64"))) self.assertEqual(row['d'], b"d") self.h5file.close() class ContiguousCompoundAppendTestCase(HDF5CompatibilityTestCase): """Test for appending data to native contiguous compound datasets.""" h5fname = 'non-chunked-table.h5' def _test(self): self.assertTrue('/test_var/structure variable' in self.h5file) self.h5file.close() # Do a copy to a temporary to avoid modifying the original file h5fname_copy = tempfile.mktemp(".h5") shutil.copy(self.h5fname, h5fname_copy) # Reopen in 'a'ppend mode try: self.h5file = tables.open_file(h5fname_copy, 'a') except IOError: # Problems for opening (probably not permisions to write the file) return tbl = self.h5file.get_node('/test_var/structure variable') # Try to add rows to a non-chunked table (this should raise an error) self.assertRaises(tables.HDF5ExtError, tbl.append, [(4.0, 5.0, [2.0, 3.0], 'd')]) # Appending using the Row interface self.assertRaises(tables.HDF5ExtError, tbl.row.append) # Remove the file copy self.h5file.close() # Close the handler first os.remove(h5fname_copy) class ExtendibleTestCase(HDF5CompatibilityTestCase): """Test for extendible datasets. See the example programs in the Introduction to HDF5. """ h5fname = 'smpl_SDSextendible.h5' def _test(self): self.assertTrue('/ExtendibleArray' in self.h5file) arr = self.h5file.get_node('/ExtendibleArray') self.assertTrue(isinstance(arr, tables.EArray)) self.assertEqual(arr.byteorder, 'big') self.assertEqual(arr.atom.type, 'int32') self.assertEqual(arr.shape, (10, 5)) self.assertEqual(arr.extdim, 0) self.assertEqual(len(arr), 10) data = arr.read() expectedData = numpy.array([ [1, 1, 1, 3, 3], [1, 1, 1, 3, 3], [1, 1, 1, 0, 0], [2, 0, 0, 0, 0], [2, 0, 0, 0, 0], [2, 0, 0, 0, 0], [2, 0, 0, 0, 0], [2, 0, 0, 0, 0], [2, 0, 0, 0, 0], [2, 0, 0, 0, 0]], dtype=arr.atom.type) self.assertTrue(common.areArraysEqual(data, expectedData)) class SzipTestCase(HDF5CompatibilityTestCase): """Test for native HDF5 files with datasets compressed with szip.""" h5fname = 'test_szip.h5' def _test(self): self.assertTrue('/dset_szip' in self.h5file) arr = self.h5file.get_node('/dset_szip') filters = ("Filters(complib='szip', shuffle=False, fletcher32=False, " "least_significant_digit=None)") self.assertEqual(repr(arr.filters), filters) # this demonstrates github #203 class MatlabFileTestCase(common.PyTablesTestCase): def setUp(self): h5fname = 'matlab_file.mat' file_path = self._testFilename(h5fname) self.h5file = tables.open_file(file_path, 'r') def tearDown(self): self.h5file.close() def test_unicode(self): array = self.h5file.get_node(unicode('/'), unicode('a')) self.assertEqual(array.shape, (3, 1)) # in Python 3 this will be the same as the test above def test_string(self): array = self.h5file.get_node('/', 'a') self.assertEqual(array.shape, (3, 1)) def test_numpy_str(self): array = self.h5file.get_node(numpy.str_('/'), numpy.str_('a')) self.assertEqual(array.shape, (3, 1)) def suite(): """Return a test suite consisting of all the test cases in the module.""" theSuite = unittest.TestSuite() niter = 1 for i in range(niter): theSuite.addTest(unittest.makeSuite(EnumTestCase)) theSuite.addTest(unittest.makeSuite(F64BETestCase)) theSuite.addTest(unittest.makeSuite(F64LETestCase)) theSuite.addTest(unittest.makeSuite(I64BETestCase)) theSuite.addTest(unittest.makeSuite(I64LETestCase)) theSuite.addTest(unittest.makeSuite(I32BETestCase)) theSuite.addTest(unittest.makeSuite(I32LETestCase)) theSuite.addTest(unittest.makeSuite(ChunkedCompoundTestCase)) theSuite.addTest(unittest.makeSuite(ContiguousCompoundTestCase)) theSuite.addTest(unittest.makeSuite(ContiguousCompoundAppendTestCase)) theSuite.addTest(unittest.makeSuite(ExtendibleTestCase)) theSuite.addTest(unittest.makeSuite(SzipTestCase)) theSuite.addTest(unittest.makeSuite(MatlabFileTestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/tests/test_index_backcompat.py000066400000000000000000000134511231437614300230330ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import unittest from tables import * from tables.tests import common from tables.tests.common import verbose, cleanup # Check indexes from PyTables version 2.0 class IndexesTestCase(common.PyTablesTestCase): def setUp(self): self.fileh = open_file(self._testFilename(self.file_), "r") self.table1 = self.fileh.root.table1 self.table2 = self.fileh.root.table2 self.il = 0 self.sl = self.table1.cols.var1.index.slicesize def tearDown(self): self.fileh.close() cleanup(self) #---------------------------------------- def test00_version(self): """Checking index version.""" t1var1 = self.table1.cols.var1 if "2_0" in self.file_: self.assertEqual(t1var1.index._v_version, "2.0") elif "2_1" in self.file_: self.assertEqual(t1var1.index._v_version, "2.1") def test01_string(self): """Checking string indexes.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_string..." % self.__class__.__name__) table1 = self.table1 table2 = self.table2 # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do some selections and check the results # First selection t1var1 = table1.cols.var1 self.assertTrue(t1var1 is not None) results1 = [p["var1"] for p in table1.where('(il<=t1var1)&(t1var1<=sl)')] results2 = [p["var1"] for p in table2 if il <= p["var1"] <= sl] results1.sort() results2.sort() if verbose: # print "Superior & inferior limits:", il, sl # print "Selection results (index):", results1 print("Should look like:", results2) print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test02_bool(self): """Checking bool indexes.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_bool..." % self.__class__.__name__) table1 = self.table1 table2 = self.table2 # Do some selections and check the results t1var2 = table1.cols.var2 self.assertTrue(t1var2 is not None) results1 = [p["var2"] for p in table1.where('t1var2 == True')] results2 = [p["var2"] for p in table2 if p["var2"] is True] if verbose: print("Selection results (index):", results1) print("Should look like:", results2) print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test03_int(self): """Checking int indexes.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_int..." % self.__class__.__name__) table1 = self.table1 table2 = self.table2 # Convert the limits to the appropriate type il = int(self.il) sl = int(self.sl) # Do some selections and check the results t1col = table1.cols.var3 self.assertTrue(t1col is not None) # First selection results1 = [p["var3"] for p in table1.where('(il<=t1col)&(t1col<=sl)')] results2 = [p["var3"] for p in table2 if il <= p["var3"] <= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test04_float(self): """Checking float indexes.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_float..." % self.__class__.__name__) table1 = self.table1 table2 = self.table2 # Convert the limits to the appropriate type il = float(self.il) sl = float(self.sl) # Do some selections and check the results t1col = table1.cols.var4 self.assertTrue(t1col is not None) # First selection results1 = [p["var4"] for p in table1.where('(il<=t1col)&(t1col<=sl)')] results2 = [p["var4"] for p in table2 if il <= p["var4"] <= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1.sort(), results2.sort()) # Check indexes from PyTables version 2.0 class Indexes2_0TestCase(IndexesTestCase): file_ = "indexes_2_0.h5" # Check indexes from PyTables version 2.1 class Indexes2_1TestCase(IndexesTestCase): file_ = "indexes_2_1.h5" #---------------------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() niter = 1 for n in range(niter): theSuite.addTest(unittest.makeSuite(Indexes2_0TestCase)) theSuite.addTest(unittest.makeSuite(Indexes2_1TestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/test_indexes.py000066400000000000000000003001511231437614300211730ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import unittest import os import tempfile import copy from tables import * from tables.index import Index, default_auto_index, default_index_filters from tables.idxutils import calc_chunksize from tables.tests.common import verbose, allequal, heavy, cleanup, \ PyTablesTestCase, TempFileMixin from tables.exceptions import OldIndexWarning # To delete the internal attributes automagically unittest.TestCase.tearDown = cleanup import numpy # Sensible parameters for indexing with small blocksizes minRowIndex = 10 small_blocksizes = (96, 24, 6, 3) class TDescr(IsDescription): var1 = StringCol(itemsize=4, dflt=b"", pos=1) var2 = BoolCol(dflt=0, pos=2) var3 = IntCol(dflt=0, pos=3) var4 = FloatCol(dflt=0, pos=4) class BasicTestCase(PyTablesTestCase): compress = 0 complib = "zlib" shuffle = 0 fletcher32 = 0 nrows = minRowIndex ss = small_blocksizes[2] def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") self.rootgroup = self.fileh.root self.populateFile() # Close the file self.fileh.close() def populateFile(self): group = self.rootgroup # Create a table title = "This is the IndexArray title" self.filters = Filters(complevel=self.compress, complib=self.complib, shuffle=self.shuffle, fletcher32=self.fletcher32) table = self.fileh.create_table(group, 'table', TDescr, title, self.filters, self.nrows) for i in range(self.nrows): table.row['var1'] = str(i).encode('ascii') # table.row['var2'] = i > 2 table.row['var2'] = i % 2 table.row['var3'] = i table.row['var4'] = float(self.nrows - i - 1) table.row.append() table.flush() # Index all entries: for col in table.colinstances.itervalues(): indexrows = col.create_index(_blocksizes=small_blocksizes) if verbose: print("Number of written rows:", self.nrows) print("Number of indexed rows:", indexrows) return def tearDown(self): self.fileh.close() # print "File %s not removed!" % self.file os.remove(self.file) cleanup(self) #---------------------------------------- def test00_flushLastRow(self): """Checking flushing an Index incrementing only the last row.""" if verbose: print('\n', '-=' * 30) print("Running %s.test00_flushLastRow..." % self.__class__.__name__) # Open the HDF5 file in append mode self.fileh = open_file(self.file, mode="a") table = self.fileh.root.table # Add just 3 rows more for i in range(3): table.row['var1'] = str(i).encode('ascii') table.row.append() table.flush() # redo the indexes idxcol = table.cols.var1.index if verbose: print("Max rows in buf:", table.nrowsinbuf) print("Number of elements per slice:", idxcol.slicesize) print("Chunk size:", idxcol.sorted.chunksize) print("Elements in last row:", idxcol.indicesLR[-1]) # Do a selection results = [p["var1"] for p in table.where('var1 == b"1"')] self.assertEqual(len(results), 2) self.assertEqual(results, [b'1']*2) def test00_update(self): """Checking automatic re-indexing after an update operation.""" if verbose: print('\n', '-=' * 30) print("Running %s.test00_update..." % self.__class__.__name__) # Open the HDF5 file in append mode self.fileh = open_file(self.file, mode="a") table = self.fileh.root.table # Modify a couple of columns for i, row in enumerate(table.where("(var3>1) & (var3<5)")): row['var1'] = str(i) row['var3'] = i row.update() table.flush() # redo the indexes idxcol1 = table.cols.var1.index idxcol3 = table.cols.var3.index if verbose: print("Dirtyness of var1 col:", idxcol1.dirty) print("Dirtyness of var3 col:", idxcol3.dirty) self.assertEqual(idxcol1.dirty, False) self.assertEqual(idxcol3.dirty, False) # Do a couple of selections results = [p["var1"] for p in table.where('var1 == b"1"')] self.assertEqual(len(results), 2) self.assertEqual(results, [b'1']*2) results = [p["var3"] for p in table.where('var3 == 0')] self.assertEqual(len(results), 2) self.assertEqual(results, [0]*2) def test01_readIndex(self): """Checking reading an Index (string flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test01_readIndex..." % self.__class__.__name__) # Open the HDF5 file in read-only mode self.fileh = open_file(self.file, mode="r") table = self.fileh.root.table idxcol = table.cols.var1.index if verbose: print("Max rows in buf:", table.nrowsinbuf) print("Number of elements per slice:", idxcol.slicesize) print("Chunk size:", idxcol.sorted.chunksize) # Do a selection results = [p["var1"] for p in table.where('var1 == b"1"')] self.assertEqual(len(results), 1) self.assertEqual(results, [b'1']) def test02_readIndex(self): """Checking reading an Index (bool flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test02_readIndex..." % self.__class__.__name__) # Open the HDF5 file in read-only mode self.fileh = open_file(self.file, mode="r") table = self.fileh.root.table idxcol = table.cols.var2.index if verbose: print("Rows in table:", table.nrows) print("Max rows in buf:", table.nrowsinbuf) print("Number of elements per slice:", idxcol.slicesize) print("Chunk size:", idxcol.sorted.chunksize) # Do a selection results = [p["var2"] for p in table.where('var2 == True')] if verbose: print("Selected values:", results) self.assertEqual(len(results), self.nrows // 2) self.assertEqual(results, [True]*(self.nrows // 2)) def test03_readIndex(self): """Checking reading an Index (int flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test03_readIndex..." % self.__class__.__name__) # Open the HDF5 file in read-only mode self.fileh = open_file(self.file, mode="r") table = self.fileh.root.table idxcol = table.cols.var3.index if verbose: print("Max rows in buf:", table.nrowsinbuf) print("Number of elements per slice:", idxcol.slicesize) print("Chunk size:", idxcol.sorted.chunksize) # Do a selection results = [p["var3"] for p in table.where('(1 0)')] # Now, modify just one row: for row in table: if row.nrow == 3: row['var1'] = "asa" row['var2'] = True row['var3'] = 3 row['var4'] = 3.1 row.update() table.flush() if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table # Do a query that uses indexes resq = [row.nrow for row in table.where('(var2 == True) & (var3 > 0)')] res_ = res + [3] if verbose: print("AutoIndex?:", table.autoindex) print("Query results (original):", res) print("Query results (after modifying table):", resq) print("Should look like:", res_) self.assertEqual(res_, resq) def test07c_noauto(self): "Checking indexing queries (append, no-auto mode)" if verbose: print('\n', '-=' * 30) print("Running %s.test07c_noauto..." % self.__class__.__name__) table = self.table # Force a sync in indexes table.flush_rows_to_index() # Do a query that uses indexes res = [row.nrow for row in table.where('(var2 == True) & (var3 > 0)')] # Now, append three rows table.append([("asa", True, 1, 3.1)]) table.append([("asb", True, 2, 3.1)]) table.append([("asc", True, 3, 3.1)]) table.flush() if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table # Do a query that uses indexes resq = [row.nrow for row in table.where('(var2 == True) & (var3 > 0)')] res_ = res + [table.nrows-3, table.nrows-2, table.nrows-1] if verbose: print("AutoIndex?:", table.autoindex) print("Query results (original):", res) print("Query results (after modifying table):", resq) print("Should look like:", res_) self.assertEqual(res_, resq) def test08_dirty(self): "Checking dirty flags (modify_columns)" if verbose: print('\n', '-=' * 30) print("Running %s.test08_dirty..." % self.__class__.__name__) table = self.table # Force a sync in indexes table.flush_rows_to_index() # Non indexated rows should remain here if self.iprops is not DefaultProps: indexedrows = table._indexedrows self.assertTrue(indexedrows is not None) unsavedindexedrows = table._unsaved_indexedrows self.assertTrue(unsavedindexedrows is not None) # Now, modify a couple of rows: table.modify_columns(1, columns=[["asa", "asb"], [1., 2.]], names=["var1", "var4"]) if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table # Check the counters self.assertEqual(table.nrows, self.nrows) if self.iprops is NoAutoProps: self.assertTrue(table.cols.var1.index.dirty) # Check the dirty flag for indexes if verbose: for colname in table.colnames: if table.cols._f_col(colname).index: print("dirty flag col %s: %s" % (colname, table.cols._f_col(colname).index.dirty)) for colname in table.colnames: if table.cols._f_col(colname).index: if not table.autoindex: if colname in ["var1"]: self.assertEqual( table.cols._f_col(colname).index.dirty, True) else: self.assertEqual( table.cols._f_col(colname).index.dirty, False) else: self.assertEqual(table.cols._f_col(colname).index.dirty, False) def test09a_propIndex(self): "Checking propagate Index feature in Table.copy() (attrs)" if verbose: print('\n', '-=' * 30) print("Running %s.test09a_propIndex..." % self.__class__.__name__) table = self.table # Don't force a sync in indexes # table.flush_rows_to_index() # Non indexated rows should remain here if self.iprops is not DefaultProps: indexedrows = table._indexedrows self.assertTrue(indexedrows is not None) unsavedindexedrows = table._unsaved_indexedrows self.assertTrue(unsavedindexedrows is not None) # Now, remove some rows to make columns dirty # table.remove_rows(3,5) # Copy a Table to another location table2 = table.copy("/", 'table2', propindexes=True) if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table table2 = self.fileh.root.table2 index1 = table.cols.var1.index index2 = table2.cols.var1.index if verbose: print("Copied index:", index2) print("Original index:", index1) if index1: print("Elements in copied index:", index2.nelements) print("Elements in original index:", index1.nelements) # Check the counters self.assertEqual(table.nrows, table2.nrows) if table.indexed: self.assertTrue(table2.indexed) if self.iprops is DefaultProps: # No index: the index should not exist self.assertTrue(index1 is None) self.assertTrue(index2 is None) elif self.iprops is NoAutoProps: self.assertTrue(index2 is not None) # Check the dirty flag for indexes if verbose: for colname in table2.colnames: if table2.cols._f_col(colname).index: print("dirty flag col %s: %s" % (colname, table2.cols._f_col(colname).index.dirty)) for colname in table2.colnames: if table2.cols._f_col(colname).index: self.assertEqual(table2.cols._f_col(colname).index.dirty, False) def test09b_propIndex(self): "Checking that propindexes=False works" if verbose: print('\n', '-=' * 30) print("Running %s.test09b_propIndex..." % self.__class__.__name__) table = self.table # Don't force a sync in indexes # table.flush_rows_to_index() # Non indexated rows should remain here if self.iprops is not DefaultProps: indexedrows = table._indexedrows self.assertTrue(indexedrows is not None) unsavedindexedrows = table._unsaved_indexedrows self.assertTrue(unsavedindexedrows is not None) # Now, remove some rows to make columns dirty # table.remove_rows(3,5) # Copy a Table to another location table2 = table.copy("/", 'table2', propindexes=False) if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table table2 = self.fileh.root.table2 if verbose: print("autoindex?:", self.iprops.auto) print("Copied index indexed?:", table2.cols.var1.is_indexed) print("Original index indexed?:", table.cols.var1.is_indexed) if self.iprops is DefaultProps: # No index: the index should not exist self.assertFalse(table2.cols.var1.is_indexed) self.assertFalse(table.cols.var1.is_indexed) elif self.iprops is NoAutoProps: self.assertFalse(table2.cols.var1.is_indexed) self.assertTrue(table.cols.var1.is_indexed) def test10_propIndex(self): "Checking propagate Index feature in Table.copy() (values)" if verbose: print('\n', '-=' * 30) print("Running %s.test10_propIndex..." % self.__class__.__name__) table = self.table # Don't force a sync in indexes # table.flush_rows_to_index() # Non indexated rows should remain here if self.iprops is not DefaultProps: indexedrows = table._indexedrows self.assertTrue(indexedrows is not None) unsavedindexedrows = table._unsaved_indexedrows self.assertTrue(unsavedindexedrows is not None) # Now, remove some rows to make columns dirty # table.remove_rows(3,5) # Copy a Table to another location table2 = table.copy("/", 'table2', propindexes=True) if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table table2 = self.fileh.root.table2 index1 = table.cols.var3.index index2 = table2.cols.var3.index if verbose: print("Copied index:", index2) print("Original index:", index1) if index1: print("Elements in copied index:", index2.nelements) print("Elements in original index:", index1.nelements) def test11_propIndex(self): "Checking propagate Index feature in Table.copy() (dirty flags)" if verbose: print('\n', '-=' * 30) print("Running %s.test11_propIndex..." % self.__class__.__name__) table = self.table # Force a sync in indexes table.flush_rows_to_index() # Non indexated rows should remain here if self.iprops is not DefaultProps: indexedrows = table._indexedrows self.assertTrue(indexedrows is not None) unsavedindexedrows = table._unsaved_indexedrows self.assertTrue(unsavedindexedrows is not None) # Now, modify an indexed column and an unindexed one # to make the "var1" dirty table.modify_columns(1, columns=[["asa", "asb"], [1., 2.]], names=["var1", "var4"]) # Copy a Table to another location table2 = table.copy("/", 'table2', propindexes=True) if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table table2 = self.fileh.root.table2 index1 = table.cols.var1.index index2 = table2.cols.var1.index if verbose: print("Copied index:", index2) print("Original index:", index1) if index1: print("Elements in copied index:", index2.nelements) print("Elements in original index:", index1.nelements) # Check the dirty flag for indexes if verbose: for colname in table2.colnames: if table2.cols._f_col(colname).index: print("dirty flag col %s: %s" % (colname, table2.cols._f_col(colname).index.dirty)) for colname in table2.colnames: if table2.cols._f_col(colname).index: if table2.autoindex: # All the destination columns should be non-dirty because # the copy removes the dirty state and puts the # index in a sane state self.assertEqual(table2.cols._f_col(colname).index.dirty, False) # minRowIndex = 10000 # just if one wants more indexed rows to be checked class AI1TestCase(AutomaticIndexingTestCase): # nrows = 10002 nrows = 102 reopen = 0 iprops = NoAutoProps colsToIndex = ['var1', 'var2', 'var3'] class AI2TestCase(AutomaticIndexingTestCase): # nrows = 10002 nrows = 102 reopen = 1 iprops = NoAutoProps colsToIndex = ['var1', 'var2', 'var3'] class AI4bTestCase(AutomaticIndexingTestCase): # nrows = 10012 nrows = 112 reopen = 1 iprops = NoAutoProps colsToIndex = ['var1', 'var2', 'var3'] class AI5TestCase(AutomaticIndexingTestCase): sbs, bs, ss, cs = calc_chunksize(minRowIndex, memlevel=1) nrows = ss * 11-1 reopen = 0 iprops = NoAutoProps colsToIndex = ['var1', 'var2', 'var3'] class AI6TestCase(AutomaticIndexingTestCase): sbs, bs, ss, cs = calc_chunksize(minRowIndex, memlevel=1) nrows = ss * 21 + 1 reopen = 1 iprops = NoAutoProps colsToIndex = ['var1', 'var2', 'var3'] class AI7TestCase(AutomaticIndexingTestCase): sbs, bs, ss, cs = calc_chunksize(minRowIndex, memlevel=1) nrows = ss * 12-1 # nrows = ss * 1-1 # faster test reopen = 0 iprops = NoAutoProps colsToIndex = ['var1', 'var2', 'var3'] class AI8TestCase(AutomaticIndexingTestCase): sbs, bs, ss, cs = calc_chunksize(minRowIndex, memlevel=1) nrows = ss * 15 + 100 # nrows = ss * 1 + 100 # faster test reopen = 1 iprops = NoAutoProps colsToIndex = ['var1', 'var2', 'var3'] class AI9TestCase(AutomaticIndexingTestCase): sbs, bs, ss, cs = calc_chunksize(minRowIndex, memlevel=1) nrows = ss reopen = 0 iprops = DefaultProps colsToIndex = [] class AI10TestCase(AutomaticIndexingTestCase): # nrows = 10002 nrows = 102 reopen = 1 iprops = DefaultProps colsToIndex = [] class AI11TestCase(AutomaticIndexingTestCase): # nrows = 10002 nrows = 102 reopen = 0 iprops = ChangeFiltersProps colsToIndex = ['var1', 'var2', 'var3'] class AI12TestCase(AutomaticIndexingTestCase): # nrows = 10002 nrows = 102 reopen = 0 iprops = ChangeFiltersProps colsToIndex = ['var1', 'var2', 'var3'] class ManyNodesTestCase(PyTablesTestCase): def setUp(self): self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w", node_cache_slots=64) def test00(self): """Indexing many nodes in one single session (based on bug #26)""" IdxRecord = { 'f0': Int8Col(), 'f1': Int8Col(), 'f2': Int8Col(), } h5 = self.fileh for qn in range(5): for sn in range(5): qchr = 'chr' + str(qn) name = 'chr' + str(sn) path = "/at/%s/pt" % (qchr) table = h5.create_table(path, name, IdxRecord, createparents=1) table.cols.f0.create_index() table.cols.f1.create_index() table.cols.f2.create_index() table.row.append() table.flush() def tearDown(self): self.fileh.close() os.remove(self.file) cleanup(self) class IndexPropsChangeTestCase(TempFileMixin, PyTablesTestCase): """Test case for changing index properties in a table.""" class MyDescription(IsDescription): icol = IntCol() oldIndexProps = IndexProps() newIndexProps = IndexProps(auto=False, filters=Filters(complevel=9)) def setUp(self): super(IndexPropsChangeTestCase, self).setUp() table = self.h5file.create_table('/', 'test', self.MyDescription) table.autoindex = self.oldIndexProps.auto row = table.row for i in xrange(100): row['icol'] = i % 25 row.append() table.flush() self.table = table def tearDown(self): super(IndexPropsChangeTestCase, self).tearDown() def test_attributes(self): """Storing index properties as table attributes.""" for refprops in [self.oldIndexProps, self.newIndexProps]: self.assertEqual(self.table.autoindex, refprops.auto) self.table.autoindex = self.newIndexProps.auto def test_copyattrs(self): """Copying index properties attributes.""" oldtable = self.table newtable = oldtable.copy('/', 'test2') self.assertEqual(oldtable.autoindex, newtable.autoindex) class IndexFiltersTestCase(TempFileMixin, PyTablesTestCase): """Test case for setting index filters.""" def setUp(self): super(IndexFiltersTestCase, self).setUp() description = {'icol': IntCol()} self.table = self.h5file.create_table('/', 'test', description) def test_createIndex(self): """Checking input parameters in new indexes.""" # Different from default. argfilters = copy.copy(default_index_filters) argfilters.shuffle = not default_index_filters.shuffle # Different both from default and the previous one. idxfilters = copy.copy(default_index_filters) idxfilters.shuffle = not default_index_filters.shuffle idxfilters.fletcher32 = not default_index_filters.fletcher32 icol = self.table.cols.icol # First create icol.create_index(kind='ultralight', optlevel=4) self.assertEqual(icol.index.kind, 'ultralight') self.assertEqual(icol.index.optlevel, 4) self.assertEqual(icol.index.filters, default_index_filters) icol.remove_index() # Second create icol.create_index(kind='medium', optlevel=3, filters=argfilters) self.assertEqual(icol.index.kind, 'medium') self.assertEqual(icol.index.optlevel, 3) self.assertEqual(icol.index.filters, argfilters) icol.remove_index() def test_reindex(self): """Checking input parameters in recomputed indexes.""" icol = self.table.cols.icol icol.create_index( kind='full', optlevel=5, filters=Filters(complevel=3)) kind = icol.index.kind optlevel = icol.index.optlevel filters = icol.index.filters icol.reindex() ni = icol.index if verbose: print("Old parameters: %s, %s, %s" % (kind, optlevel, filters)) print("New parameters: %s, %s, %s" % ( ni.kind, ni.optlevel, ni.filters)) self.assertEqual(ni.kind, kind) self.assertEqual(ni.optlevel, optlevel) self.assertEqual(ni.filters, filters) class OldIndexTestCase(PyTablesTestCase): def test1_x(self): """Check that files with 1.x indexes are recognized and warned.""" fname = self._testFilename("idx-std-1.x.h5") f = open_file(fname) self.assertWarns(OldIndexWarning, f.get_node, "/table") f.close() # Sensible parameters for indexing with small blocksizes small_blocksizes = (512, 128, 32, 8) class CompletelySortedIndexTestCase(TempFileMixin, PyTablesTestCase): """Test case for testing a complete sort in a table.""" nrows = 100 nrowsinbuf = 11 class MyDescription(IsDescription): rcol = IntCol(pos=1) icol = IntCol(pos=2) def setUp(self): super(CompletelySortedIndexTestCase, self).setUp() table = self.h5file.create_table('/', 'table', self.MyDescription) row = table.row nrows = self.nrows for i in xrange(nrows): row['rcol'] = i row['icol'] = nrows - i row.append() table.flush() self.table = table self.icol = self.table.cols.icol # A full index with maximum optlevel should always be completely sorted self.icol.create_csindex(_blocksizes=small_blocksizes) def test00_isCompletelySortedIndex(self): """Testing the Column.is_csi property.""" icol = self.icol self.assertEqual(icol.index.is_csi, True) icol.remove_index() # Other kinds than full, should never return a CSI icol.create_index(kind="medium", optlevel=9) self.assertEqual(icol.index.is_csi, False) icol.remove_index() # As the table is small, lesser optlevels should be able to # create a completely sorted index too. icol.create_index(kind="full", optlevel=6) self.assertEqual(icol.index.is_csi, True) # Checking a CSI in a sorted copy self.table.copy("/", 'table2', sortby='icol', checkCSI=True) self.assertEqual(icol.index.is_csi, True) def test01_readSorted1(self): """Testing the Index.read_sorted() method with no arguments.""" icol = self.icol sortedcol = numpy.sort(icol[:]) sortedcol2 = icol.index.read_sorted() if verbose: print("Original sorted column:", sortedcol) print("The values from the index:", sortedcol2) self.assertTrue(allequal(sortedcol, sortedcol2)) def test01_readSorted2(self): """Testing the Index.read_sorted() method with arguments (I).""" icol = self.icol sortedcol = numpy.sort(icol[:])[30:55] sortedcol2 = icol.index.read_sorted(30, 55) if verbose: print("Original sorted column:", sortedcol) print("The values from the index:", sortedcol2) self.assertTrue(allequal(sortedcol, sortedcol2)) def test01_readSorted3(self): """Testing the Index.read_sorted() method with arguments (II).""" icol = self.icol sortedcol = numpy.sort(icol[:])[33:97] sortedcol2 = icol.index.read_sorted(33, 97) if verbose: print("Original sorted column:", sortedcol) print("The values from the index:", sortedcol2) self.assertTrue(allequal(sortedcol, sortedcol2)) def test02_readIndices1(self): """Testing the Index.read_indices() method with no arguments.""" icol = self.icol indicescol = numpy.argsort(icol[:]).astype('uint64') indicescol2 = icol.index.read_indices() if verbose: print("Original indices column:", indicescol) print("The values from the index:", indicescol2) self.assertTrue(allequal(indicescol, indicescol2)) def test02_readIndices2(self): """Testing the Index.read_indices() method with arguments (I).""" icol = self.icol indicescol = numpy.argsort(icol[:])[30:55].astype('uint64') indicescol2 = icol.index.read_indices(30, 55) if verbose: print("Original indices column:", indicescol) print("The values from the index:", indicescol2) self.assertTrue(allequal(indicescol, indicescol2)) def test02_readIndices3(self): """Testing the Index.read_indices() method with arguments (II).""" icol = self.icol indicescol = numpy.argsort(icol[:])[33:97].astype('uint64') indicescol2 = icol.index.read_indices(33, 97) if verbose: print("Original indices column:", indicescol) print("The values from the index:", indicescol2) self.assertTrue(allequal(indicescol, indicescol2)) def test02_readIndices4(self): """Testing the Index.read_indices() method with arguments (III).""" icol = self.icol indicescol = numpy.argsort(icol[:])[33:97:2].astype('uint64') indicescol2 = icol.index.read_indices(33, 97, 2) if verbose: print("Original indices column:", indicescol) print("The values from the index:", indicescol2) self.assertTrue(allequal(indicescol, indicescol2)) def test02_readIndices5(self): """Testing the Index.read_indices() method with arguments (IV).""" icol = self.icol indicescol = numpy.argsort(icol[:])[33:55:5].astype('uint64') indicescol2 = icol.index.read_indices(33, 55, 5) if verbose: print("Original indices column:", indicescol) print("The values from the index:", indicescol2) self.assertTrue(allequal(indicescol, indicescol2)) def test02_readIndices6(self): """Testing the Index.read_indices() method with step only.""" icol = self.icol indicescol = numpy.argsort(icol[:])[::3].astype('uint64') indicescol2 = icol.index.read_indices(step=3) if verbose: print("Original indices column:", indicescol) print("The values from the index:", indicescol2) self.assertTrue(allequal(indicescol, indicescol2)) def test03_getitem1(self): """Testing the Index.__getitem__() method with no arguments.""" icol = self.icol indicescol = numpy.argsort(icol[:]).astype('uint64') indicescol2 = icol.index[:] if verbose: print("Original indices column:", indicescol) print("The values from the index:", indicescol2) self.assertTrue(allequal(indicescol, indicescol2)) def test03_getitem2(self): """Testing the Index.__getitem__() method with start.""" icol = self.icol indicescol = numpy.argsort(icol[:])[31].astype('uint64') indicescol2 = icol.index[31] if verbose: print("Original indices column:", indicescol) print("The values from the index:", indicescol2) self.assertTrue(allequal(indicescol, indicescol2)) def test03_getitem3(self): """Testing the Index.__getitem__() method with start, stop.""" icol = self.icol indicescol = numpy.argsort(icol[:])[2:16].astype('uint64') indicescol2 = icol.index[2:16] if verbose: print("Original indices column:", indicescol) print("The values from the index:", indicescol2) self.assertTrue(allequal(indicescol, indicescol2)) def test04_itersorted1(self): """Testing the Table.itersorted() method with no arguments.""" table = self.table sortedtable = numpy.sort(table[:], order='icol') sortedtable2 = numpy.array( [row.fetch_all_fields() for row in table.itersorted( 'icol')], dtype=table._v_dtype) if verbose: print("Original sorted table:", sortedtable) print("The values from the iterator:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test04_itersorted2(self): """Testing the Table.itersorted() method with a start.""" table = self.table sortedtable = numpy.sort(table[:], order='icol')[15:] sortedtable2 = numpy.array( [row.fetch_all_fields() for row in table.itersorted( 'icol', start=15)], dtype=table._v_dtype) if verbose: print("Original sorted table:", sortedtable) print("The values from the iterator:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test04_itersorted3(self): """Testing the Table.itersorted() method with a stop.""" table = self.table sortedtable = numpy.sort(table[:], order='icol')[:20] sortedtable2 = numpy.array( [row.fetch_all_fields() for row in table.itersorted( 'icol', stop=20)], dtype=table._v_dtype) if verbose: print("Original sorted table:", sortedtable) print("The values from the iterator:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test04_itersorted4(self): """Testing the Table.itersorted() method with a start and stop.""" table = self.table sortedtable = numpy.sort(table[:], order='icol')[15:20] sortedtable2 = numpy.array( [row.fetch_all_fields() for row in table.itersorted( 'icol', start=15, stop=20)], dtype=table._v_dtype) if verbose: print("Original sorted table:", sortedtable) print("The values from the iterator:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test04_itersorted5(self): """Testing the Table.itersorted() method with a start, stop and step.""" table = self.table sortedtable = numpy.sort(table[:], order='icol')[15:45:4] sortedtable2 = numpy.array( [row.fetch_all_fields() for row in table.itersorted( 'icol', start=15, stop=45, step=4)], dtype=table._v_dtype) if verbose: print("Original sorted table:", sortedtable) print("The values from the iterator:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test04_itersorted6(self): """Testing the Table.itersorted() method with a start, stop and step.""" table = self.table sortedtable = numpy.sort(table[:], order='icol')[33:55:5] sortedtable2 = numpy.array( [row.fetch_all_fields() for row in table.itersorted( 'icol', start=33, stop=55, step=5)], dtype=table._v_dtype) if verbose: print("Original sorted table:", sortedtable) print("The values from the iterator:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test04_itersorted7(self): """Testing the Table.itersorted() method with checkCSI=True.""" table = self.table sortedtable = numpy.sort(table[:], order='icol') sortedtable2 = numpy.array( [row.fetch_all_fields() for row in table.itersorted( 'icol', checkCSI=True)], dtype=table._v_dtype) if verbose: print("Original sorted table:", sortedtable) print("The values from the iterator:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test04_itersorted8(self): """Testing the Table.itersorted() method with a start, stop and negative step.""" # see also gh-252 table = self.table sortedtable = numpy.sort(table[:], order='icol')[55:33:-5] sortedtable2 = numpy.array( [row.fetch_all_fields() for row in table.itersorted( 'icol', start=55, stop=33, step=-5)], dtype=table._v_dtype) if verbose: print("Original sorted table:", sortedtable) print("The values from the iterator:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test04_itersorted9(self): """Testing the Table.itersorted() method with a negative step.""" # see also gh-252 table = self.table sortedtable = numpy.sort(table[:], order='icol')[::-5] sortedtable2 = numpy.array( [row.fetch_all_fields() for row in table.itersorted( 'icol', step=-5)], dtype=table._v_dtype) if verbose: print("Original sorted table:", sortedtable) print("The values from the iterator:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test05_readSorted1(self): """Testing the Table.read_sorted() method with no arguments.""" table = self.table sortedtable = numpy.sort(table[:], order='icol') sortedtable2 = table.read_sorted('icol') if verbose: print("Original sorted table:", sortedtable) print("The values from read_sorted:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test05_readSorted2(self): """Testing the Table.read_sorted() method with a start.""" table = self.table sortedtable = numpy.sort(table[:], order='icol')[16:17] sortedtable2 = table.read_sorted('icol', start=16) if verbose: print("Original sorted table:", sortedtable) print("The values from read_sorted:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test05_readSorted3(self): """Testing the Table.read_sorted() method with a start and stop.""" table = self.table sortedtable = numpy.sort(table[:], order='icol')[16:33] sortedtable2 = table.read_sorted('icol', start=16, stop=33) if verbose: print("Original sorted table:", sortedtable) print("The values from read_sorted:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test05_readSorted4(self): """Testing the Table.read_sorted() method with a start, stop and step.""" table = self.table sortedtable = numpy.sort(table[:], order='icol')[33:55:5] sortedtable2 = table.read_sorted('icol', start=33, stop=55, step=5) if verbose: print("Original sorted table:", sortedtable) print("The values from read_sorted:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test05_readSorted5(self): """Testing the Table.read_sorted() method with only a step.""" table = self.table sortedtable = numpy.sort(table[:], order='icol')[::3] sortedtable2 = table.read_sorted('icol', step=3) if verbose: print("Original sorted table:", sortedtable) print("The values from read_sorted:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test05_readSorted6(self): """Testing the Table.read_sorted() method with negative step.""" table = self.table sortedtable = numpy.sort(table[:], order='icol')[::-1] sortedtable2 = table.read_sorted('icol', step=-1) if verbose: print("Original sorted table:", sortedtable) print("The values from read_sorted:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test05_readSorted7(self): """Testing the Table.read_sorted() method with negative step (II).""" table = self.table sortedtable = numpy.sort(table[:], order='icol')[::-2] sortedtable2 = table.read_sorted('icol', step=-2) if verbose: print("Original sorted table:", sortedtable) print("The values from read_sorted:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test05_readSorted8(self): """Testing the Table.read_sorted() method with negative step (III)).""" table = self.table sstart = 100-24-1 sstop = 100-54-1 sortedtable = numpy.sort(table[:], order='icol')[sstart:sstop:-1] sortedtable2 = table.read_sorted('icol', start=24, stop=54, step=-1) if verbose: print("Original sorted table:", sortedtable) print("The values from read_sorted:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test05_readSorted9(self): """Testing the Table.read_sorted() method with negative step (IV)).""" table = self.table sstart = 100-14-1 sstop = 100-54-1 sortedtable = numpy.sort(table[:], order='icol')[sstart:sstop:-3] sortedtable2 = table.read_sorted('icol', start=14, stop=54, step=-3) if verbose: print("Original sorted table:", sortedtable) print("The values from read_sorted:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test05_readSorted10(self): """Testing the Table.read_sorted() method with negative step (V)).""" table = self.table sstart = 100-24-1 sstop = 100-25-1 sortedtable = numpy.sort(table[:], order='icol')[sstart:sstop:-2] sortedtable2 = table.read_sorted('icol', start=24, stop=25, step=-2) if verbose: print("Original sorted table:", sortedtable) print("The values from read_sorted:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test05_readSorted11(self): """Testing the Table.read_sorted() method with start > stop.""" table = self.table sstart = 100-137-1 sstop = 100-25-1 sortedtable = numpy.sort(table[:], order='icol')[sstart:sstop:-2] sortedtable2 = table.read_sorted('icol', start=137, stop=25, step=-2) if verbose: print("Original sorted table:", sortedtable) print("The values from read_sorted:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test05a_readSorted12(self): """Testing the Table.read_sorted() method with checkCSI (I).""" table = self.table sortedtable = numpy.sort(table[:], order='icol') sortedtable2 = table.read_sorted('icol', checkCSI=True) if verbose: print("Original sorted table:", sortedtable) print("The values from read_sorted:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test05b_readSorted12(self): """Testing the Table.read_sorted() method with checkCSI (II).""" table = self.table self.assertRaises(ValueError, table.read_sorted, "rcol", checkCSI=False) def test06_copy_sorted1(self): """Testing the Table.copy(sortby) method with no arguments.""" table = self.table # Copy to another table table.nrowsinbuf = self.nrowsinbuf table2 = table.copy("/", 'table2', sortby="icol") sortedtable = numpy.sort(table[:], order='icol') sortedtable2 = table2[:] if verbose: print("Original sorted table:", sortedtable) print("The values from copy:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test06_copy_sorted2(self): """Testing the Table.copy(sortby) method with step=-1.""" table = self.table # Copy to another table table.nrowsinbuf = self.nrowsinbuf table2 = table.copy("/", 'table2', sortby="icol", step=-1) sortedtable = numpy.sort(table[:], order='icol')[::-1] sortedtable2 = table2[:] if verbose: print("Original sorted table:", sortedtable) print("The values from copy:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test06_copy_sorted3(self): """Testing the Table.copy(sortby) method with only a start.""" table = self.table # Copy to another table table.nrowsinbuf = self.nrowsinbuf table2 = table.copy("/", 'table2', sortby="icol", start=3) sortedtable = numpy.sort(table[:], order='icol')[3:4] sortedtable2 = table2[:] if verbose: print("Original sorted table:", sortedtable) print("The values from copy:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test06_copy_sorted4(self): """Testing the Table.copy(sortby) method with start, stop.""" table = self.table # Copy to another table table.nrowsinbuf = self.nrowsinbuf table2 = table.copy("/", 'table2', sortby="icol", start=3, stop=40) sortedtable = numpy.sort(table[:], order='icol')[3:40] sortedtable2 = table2[:] if verbose: print("Original sorted table:", sortedtable) print("The values from copy:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test06_copy_sorted5(self): """Testing the Table.copy(sortby) method with start, stop, step.""" table = self.table # Copy to another table table.nrowsinbuf = self.nrowsinbuf table2 = table.copy("/", 'table2', sortby="icol", start=3, stop=33, step=5) sortedtable = numpy.sort(table[:], order='icol')[3:33:5] sortedtable2 = table2[:] if verbose: print("Original sorted table:", sortedtable) print("The values from copy:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test06_copy_sorted6(self): """Testing the Table.copy(sortby) method after table re-opening.""" self._reopen(mode='a') table = self.h5file.root.table # Copy to another table table.nrowsinbuf = self.nrowsinbuf table2 = table.copy("/", 'table2', sortby="icol") sortedtable = numpy.sort(table[:], order='icol') sortedtable2 = table2[:] if verbose: print("Original sorted table:", sortedtable) print("The values from copy:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test06_copy_sorted7(self): """Testing the `checkCSI` parameter of Table.copy() (I).""" table = self.table # Copy to another table table.nrowsinbuf = self.nrowsinbuf table2 = table.copy("/", 'table2', sortby="icol") self.assertRaises(ValueError, table2.copy, "/", 'table3', sortby="rcol", checkCSI=False) def test06_copy_sorted8(self): """Testing the `checkCSI` parameter of Table.copy() (II).""" table = self.table # Copy to another table table.nrowsinbuf = self.nrowsinbuf table2 = table.copy("/", 'table2', sortby="icol") self.assertRaises(ValueError, table2.copy, "/", 'table3', sortby="rcol", checkCSI=True) def test07_isCSI_noelements(self): """Testing the representation of an index with no elements.""" t2 = self.h5file.create_table('/', 't2', self.MyDescription) irows = t2.cols.rcol.create_csindex() if verbose: print("repr(t2)-->\n", repr(t2)) self.assertEqual(irows, 0) self.assertEqual(t2.colindexes['rcol'].is_csi, False) class ReadSortedIndexTestCase(TempFileMixin, PyTablesTestCase): """Test case for testing sorted reading in a "full" sorted column.""" nrows = 100 nrowsinbuf = 11 class MyDescription(IsDescription): rcol = IntCol(pos=1) icol = IntCol(pos=2) def setUp(self): super(ReadSortedIndexTestCase, self).setUp() table = self.h5file.create_table('/', 'table', self.MyDescription) row = table.row nrows = self.nrows for i in xrange(nrows): row['rcol'] = i row['icol'] = nrows - i row.append() table.flush() self.table = table self.icol = self.table.cols.icol # A full index with maximum optlevel should always be completely sorted self.icol.create_index(optlevel=self.optlevel, kind="full", _blocksizes=small_blocksizes) def test01_readSorted1(self): """Testing the Table.read_sorted() method with no arguments.""" table = self.table sortedtable = numpy.sort(table[:], order='icol') sortedtable2 = table.read_sorted('icol') if verbose: print("Sorted table:", sortedtable) print("The values from read_sorted:", sortedtable2) # Compare with the sorted read table because we have no # guarantees that read_sorted returns a completely sorted table self.assertTrue(allequal(sortedtable, numpy.sort(sortedtable2, order="icol"))) def test01_readSorted2(self): """Testing the Table.read_sorted() method with no arguments (re-open). """ self._reopen() table = self.h5file.root.table sortedtable = numpy.sort(table[:], order='icol') sortedtable2 = table.read_sorted('icol') if verbose: print("Sorted table:", sortedtable) print("The values from read_sorted:", sortedtable2) # Compare with the sorted read table because we have no # guarantees that read_sorted returns a completely sorted table self.assertTrue(allequal(sortedtable, numpy.sort(sortedtable2, order="icol"))) def test02_copy_sorted1(self): """Testing the Table.copy(sortby) method.""" table = self.table # Copy to another table table.nrowsinbuf = self.nrowsinbuf table2 = table.copy("/", 'table2', sortby="icol") sortedtable = numpy.sort(table[:], order='icol') sortedtable2 = numpy.sort(table2[:], order='icol') if verbose: print("Original table:", table2[:]) print("The sorted values from copy:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) def test02_copy_sorted2(self): """Testing the Table.copy(sortby) method after table re-opening.""" self._reopen(mode='a') table = self.h5file.root.table # Copy to another table table.nrowsinbuf = self.nrowsinbuf table2 = table.copy("/", 'table2', sortby="icol") sortedtable = numpy.sort(table[:], order='icol') sortedtable2 = numpy.sort(table2[:], order='icol') if verbose: print("Original table:", table2[:]) print("The sorted values from copy:", sortedtable2) self.assertTrue(allequal(sortedtable, sortedtable2)) class ReadSortedIndex0(ReadSortedIndexTestCase): optlevel = 0 class ReadSortedIndex3(ReadSortedIndexTestCase): optlevel = 3 class ReadSortedIndex6(ReadSortedIndexTestCase): optlevel = 6 class ReadSortedIndex9(ReadSortedIndexTestCase): optlevel = 9 class Issue156TestBase(PyTablesTestCase): # field name in table according to which test_copysort() sorts the table sort_field = None def setUp(self): # create hdf5 file self.filename = tempfile.mktemp(".hdf5") self.file = open_file(self.filename, mode="w") # create nested table class Foo(IsDescription): frame = UInt16Col() class Bar(IsDescription): code = UInt16Col() table = self.file.create_table('/', 'foo', Foo, filters=Filters(3, 'zlib'), createparents=True) self.file.flush() # fill table with 10 random numbers for k in xrange(10): row = table.row row['frame'] = numpy.random.random_integers(0, 2**16-1) row['Bar/code'] = numpy.random.random_integers(0, 2**16-1) row.append() self.file.flush() def tearDown(self): self.file.close() os.remove(self.filename) def test_copysort(self): # copy table oldNode = self.file.get_node('/foo') # create completely sorted index on a main column oldNode.colinstances[self.sort_field].create_csindex() # this fails on ade2ba123efd267fd31 # see gh-156 new_node = oldNode.copy(newname='foo2', overwrite=True, sortby=self.sort_field, checkCSI=True, propindexes=True) # check column is sorted self.assertTrue(numpy.all( new_node.col(self.sort_field) == sorted(oldNode.col(self.sort_field)))) # check index is available self.assertTrue(self.sort_field in new_node.colindexes) # check CSI was propagated self.assertTrue(new_node.colindexes[self.sort_field].is_csi) class Issue156TestCase01(Issue156TestBase): # sort by field from non nested entry sort_field = 'frame' class Issue156TestCase02(Issue156TestBase): # sort by field from nested entry sort_field = 'Bar/code' class Issue119Time32ColTestCase(PyTablesTestCase): """TimeCol not properly indexing.""" col_typ = Time32Col values = [ 0.93240451618785880, 0.76322375510776170, 0.16695030056300875, 0.91259117097807850, 0.93977847053454630, 0.51450406513503090, 0.24452129962257563, 0.85475938924825230, 0.32512326762476930, 0.75127635627046820, ] def setUp(self): # create hdf5 file self.filename = tempfile.mktemp(".hdf5") self.file = open_file(self.filename, mode="w") class Descr(IsDescription): when = self.col_typ(pos=1) value = Float32Col(pos=2) self.table = self.file.create_table('/', 'test', Descr) self.t = 1321031471.0 # 11/11/11 11:11:11 data = [(self.t + i, item) for i, item in enumerate(self.values)] self.table.append(data) self.file.flush() def tearDown(self): self.file.close() os.remove(self.filename) def test_timecol_issue(self): tbl = self.table t = self.t wherestr = '(when >= %d) & (when < %d)' % (t, t + 5) no_index = tbl.read_where(wherestr) tbl.cols.when.create_index(_verbose=False) with_index = tbl.read_where(wherestr) self.assertTrue((no_index == with_index).all()) class Issue119Time64ColTestCase(Issue119Time32ColTestCase): col_typ = Time64Col class TestIndexingNans(TempFileMixin, PyTablesTestCase): def test_issue_282(self): trMap = {'index': Int64Col(), 'values': FloatCol()} table = self.h5file.create_table('/', 'table', trMap) r = table.row for i in range(5): r['index'] = i r['values'] = numpy.nan if i == 0 else i r.append() table.flush() table.cols.values.create_index() # retrieve result = table.read_where('(values >= 0)') self.assertTrue(len(result) == 4) def test_issue_327(self): table = self.h5file.create_table('/', 'table', dict( index=Int64Col(), values=FloatCol(shape=()), values2=FloatCol(shape=()), )) r = table.row for i in range(5): r['index'] = i r['values'] = numpy.nan if i == 2 or i == 3 else i r['values2'] = i r.append() table.flush() table.cols.values.create_index() table.cols.values2.create_index() results2 = table.read_where('(values2 > 0)') self.assertTrue(len(results2) == 4) results = table.read_where('(values > 0)') self.assertTrue(len(results) == 2) #---------------------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() niter = 1 # heavy = 1 # Uncomment this only for testing purposes! for n in range(niter): theSuite.addTest(unittest.makeSuite(BasicReadTestCase)) theSuite.addTest(unittest.makeSuite(ZlibReadTestCase)) theSuite.addTest(unittest.makeSuite(BloscReadTestCase)) theSuite.addTest(unittest.makeSuite(LZOReadTestCase)) theSuite.addTest(unittest.makeSuite(Bzip2ReadTestCase)) theSuite.addTest(unittest.makeSuite(ShuffleReadTestCase)) theSuite.addTest(unittest.makeSuite(Fletcher32ReadTestCase)) theSuite.addTest(unittest.makeSuite(ShuffleFletcher32ReadTestCase)) theSuite.addTest(unittest.makeSuite(OneHalfTestCase)) theSuite.addTest(unittest.makeSuite(UpperBoundTestCase)) theSuite.addTest(unittest.makeSuite(LowerBoundTestCase)) theSuite.addTest(unittest.makeSuite(AI1TestCase)) theSuite.addTest(unittest.makeSuite(AI2TestCase)) theSuite.addTest(unittest.makeSuite(AI9TestCase)) theSuite.addTest(unittest.makeSuite(DeepTableIndexTestCase)) theSuite.addTest(unittest.makeSuite(IndexPropsChangeTestCase)) theSuite.addTest(unittest.makeSuite(IndexFiltersTestCase)) theSuite.addTest(unittest.makeSuite(OldIndexTestCase)) theSuite.addTest(unittest.makeSuite(CompletelySortedIndexTestCase)) theSuite.addTest(unittest.makeSuite(ManyNodesTestCase)) theSuite.addTest(unittest.makeSuite(ReadSortedIndex0)) theSuite.addTest(unittest.makeSuite(ReadSortedIndex3)) theSuite.addTest(unittest.makeSuite(ReadSortedIndex6)) theSuite.addTest(unittest.makeSuite(ReadSortedIndex9)) theSuite.addTest(unittest.makeSuite(Issue156TestCase01)) theSuite.addTest(unittest.makeSuite(Issue156TestCase02)) theSuite.addTest(unittest.makeSuite(Issue119Time32ColTestCase)) theSuite.addTest(unittest.makeSuite(Issue119Time64ColTestCase)) theSuite.addTest(unittest.makeSuite(TestIndexingNans)) if heavy: # These are too heavy for normal testing theSuite.addTest(unittest.makeSuite(AI4bTestCase)) theSuite.addTest(unittest.makeSuite(AI5TestCase)) theSuite.addTest(unittest.makeSuite(AI6TestCase)) theSuite.addTest(unittest.makeSuite(AI7TestCase)) theSuite.addTest(unittest.makeSuite(AI8TestCase)) theSuite.addTest(unittest.makeSuite(AI10TestCase)) theSuite.addTest(unittest.makeSuite(AI11TestCase)) theSuite.addTest(unittest.makeSuite(AI12TestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/test_indexvalues.py000066400000000000000000003767671231437614300221150ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import os import random import unittest import tempfile import numpy from tables import * from tables.idxutils import calc_chunksize from tables.tests import common from tables.tests.common import verbose, heavy, cleanup # To delete the internal attributes automagically unittest.TestCase.tearDown = cleanup # An alias for frozenset fzset = frozenset # To make the tests values reproductibles random.seed(19) # Sensible parameters for indexing with small blocksizes small_blocksizes = (16, 8, 4, 2) # The smaller set of parameters... # The size for medium indexes minRowIndex = 1000 class Small(IsDescription): var1 = StringCol(itemsize=4, dflt=b"") var2 = BoolCol(dflt=0) var3 = IntCol(dflt=0) var4 = FloatCol(dflt=0) class SelectValuesTestCase(unittest.TestCase): compress = 1 complib = "zlib" shuffle = 1 fletcher32 = 0 chunkshape = 10 buffersize = 0 random = 0 values = None reopen = False def setUp(self): # Create an instance of an HDF5 Table if verbose: print("Checking index kind-->", self.kind) self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") self.rootgroup = self.fileh.root self.populateFile() def populateFile(self): # Set a seed for the random generator if needed. # This is useful when one need reproductible results. if self.random and hasattr(self, "seed"): random.seed(self.seed) group = self.rootgroup # Create an table title = "This is the IndexArray title" filters = Filters(complevel=self.compress, complib=self.complib, shuffle=self.shuffle, fletcher32=self.fletcher32) table1 = self.fileh.create_table(group, 'table1', Small, title, filters, self.nrows, chunkshape=(self.chunkshape,)) table2 = self.fileh.create_table(group, 'table2', Small, title, filters, self.nrows, chunkshape=(self.chunkshape,)) count = 0 for i in xrange(0, self.nrows, self.nrep): for j in range(self.nrep): if self.random: k = random.randrange(self.nrows) elif self.values is not None: lenvalues = len(self.values) if i >= lenvalues: i %= lenvalues k = self.values[i] else: k = i bk = str(k).encode('ascii') table1.row['var1'] = bk table2.row['var1'] = bk table1.row['var2'] = k % 2 table2.row['var2'] = k % 2 table1.row['var3'] = k table2.row['var3'] = k table1.row['var4'] = float(self.nrows - k - 1) table2.row['var4'] = float(self.nrows - k - 1) table1.row.append() table2.row.append() count += 1 table1.flush() table2.flush() if self.buffersize: # Change the buffersize by default table1.nrowsinbuf = self.buffersize # Index all entries: for col in table1.colinstances.itervalues(): indexrows = col.create_index( kind=self.kind, _blocksizes=self.blocksizes) if verbose: print("Number of written rows:", table1.nrows) print("Number of indexed rows:", indexrows) if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "a") # for flavor changes self.table1 = self.fileh.root.table1 self.table2 = self.fileh.root.table1 def tearDown(self): self.fileh.close() os.remove(self.file) cleanup(self) #---------------------------------------- def test01a(self): """Checking selecting values from an Index (string flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test01a..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do some selections and check the results # First selection t1var1 = table1.cols.var1 results1 = [p["var1"] for p in table1.where('(il<=t1var1)&(t1var1<=sl)')] results2 = [p["var1"] for p in table2 if il <= p["var1"] <= sl] results1.sort() results2.sort() if verbose: # print "Superior & inferior limits:", il, sl # print "Selection results (index):", results1 print("Should look like:", results2) print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Second selection t1var1 = table1.cols.var1 results1 = [p["var1"] for p in table1.where('(il<=t1var1)&(t1var1 sl')] results2 = [p["var1"] for p in table2 if p["var1"] > sl] results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Fourth selection t1var1 = table1.cols.var1 self.assertTrue(t1var1 is not None) results1 = [p["var1"] for p in table1.where('t1var1 >= sl')] results2 = [p["var1"] for p in table2 if p["var1"] >= sl] results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test02a(self): """Checking selecting values from an Index (bool flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test02a..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Do some selections and check the results t1var2 = table1.cols.var2 self.assertTrue(t1var2 is not None) results1 = [p["var2"] for p in table1.where('t1var2 == True')] results2 = [p["var2"] for p in table2 if p["var2"] is True] if verbose: # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test02b(self): """Checking selecting values from an Index (bool flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test02b..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Do some selections and check the results t1var2 = table1.cols.var2 self.assertTrue(t1var2 is not None) results1 = [p["var2"] for p in table1.where('t1var2 == False')] results2 = [p["var2"] for p in table2 if p["var2"] is False] if verbose: # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test03a(self): """Checking selecting values from an Index (int flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test03a..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = int(self.il) sl = int(self.sl) # Do some selections and check the results t1col = table1.cols.var3 self.assertTrue(t1col is not None) # First selection results1 = [p["var3"] for p in table1.where('(il<=t1col)&(t1col<=sl)')] results2 = [p["var3"] for p in table2 if il <= p["var3"] <= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Second selection results1 = [p["var3"] for p in table1.where('(il<=t1col)&(t1col sl')] results2 = [p["var3"] for p in table2 if p["var3"] > sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Fourth selection results1 = [p["var3"] for p in table1.where('t1col >= sl')] results2 = [p["var3"] for p in table2 if p["var3"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test03c(self): """Checking selecting values from an Index (long flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test03c..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type # il = long(self.il) sl = long(self.sl) # Do some selections and check the results t1col = table1.cols.var3 self.assertTrue(t1col is not None) # First selection results1 = [p["var3"] for p in table1.where('t1col < sl')] results2 = [p["var3"] for p in table2 if p["var3"] < sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Second selection results1 = [p["var3"] for p in table1.where('t1col <= sl')] results2 = [p["var3"] for p in table2 if p["var3"] <= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Third selection results1 = [p["var3"] for p in table1.where('t1col > sl')] results2 = [p["var3"] for p in table2 if p["var3"] > sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Fourth selection results1 = [p["var3"] for p in table1.where('t1col >= sl')] results2 = [p["var3"] for p in table2 if p["var3"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test03d(self): """Checking selecting values from an Index (long and int flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test03d..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type # il = int(self.il) sl = long(self.sl) # Do some selections and check the results t1col = table1.cols.var3 self.assertTrue(t1col is not None) # First selection results1 = [p["var3"] for p in table1.where('t1col < sl')] results2 = [p["var3"] for p in table2 if p["var3"] < sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Second selection results1 = [p["var3"] for p in table1.where('t1col <= sl')] results2 = [p["var3"] for p in table2 if p["var3"] <= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Third selection results1 = [p["var3"] for p in table1.where('t1col > sl')] results2 = [p["var3"] for p in table2 if p["var3"] > sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Fourth selection results1 = [p["var3"] for p in table1.where('t1col >= sl')] results2 = [p["var3"] for p in table2 if p["var3"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test04a(self): """Checking selecting values from an Index (float flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test04a..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = float(self.il) sl = float(self.sl) # Do some selections and check the results t1col = table1.cols.var4 self.assertTrue(t1col is not None) # First selection results1 = [p["var4"] for p in table1.where('(il<=t1col)&(t1col<=sl)')] results2 = [p["var4"] for p in table2 if il <= p["var4"] <= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1.sort(), results2.sort()) # Second selection results1 = [p["var4"] for p in table1.where('(il<=t1col)&(t1col sl')] results2 = [p["var4"] for p in table2 if p["var4"] > sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Fourth selection results1 = [p["var4"] for p in table1.where('t1col >= sl')] results2 = [p["var4"] for p in table2 if p["var4"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test05a(self): """Checking get_where_list & itersequence (string, python flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test05a..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do some selections and check the results t1col = table1.cols.var1 # First selection condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) table1.flavor = "python" rowList1 = table1.get_where_list(condition) results1 = [p['var1'] for p in table1.itersequence(rowList1)] results2 = [p["var1"] for p in table2 if il <= p["var1"] <= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1.sort(), results2.sort()) # Second selection condition = '(il<=t1col)&(t1col sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Fourth selection condition = 't1col>=sl' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) rowList1 = table1.get_where_list(condition) results1 = [p['var1'] for p in table1.itersequence(rowList1)] results2 = [p["var1"] for p in table2 if p["var1"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test06a(self): """Checking get_where_list & itersequence (bool flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test06a..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Do some selections and check the results t1var2 = table1.cols.var2 condition = 't1var2==True' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1var2.pathname])) table1.flavor = "python" rowList1 = table1.get_where_list(condition) results1 = [p['var2'] for p in table1.itersequence(rowList1)] results2 = [p["var2"] for p in table2 if p["var2"] is True] if verbose: # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test06b(self): """Checking get_where_list & itersequence (numpy bool limits & flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test06b..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Do some selections and check the results t1var2 = table1.cols.var2 false = numpy.bool_(False) self.assertFalse(false) # silence pyflakes condition = 't1var2==false' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1var2.pathname])) table1.flavor = "python" rowList1 = table1.get_where_list(condition) results1 = [p['var2'] for p in table1.itersequence(rowList1)] results2 = [p["var2"] for p in table2 if p["var2"] is False] if verbose: # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test07a(self): """Checking get_where_list & itersequence (int flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test07a..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = int(self.il) sl = int(self.sl) # Do some selections and check the results t1col = table1.cols.var3 # First selection condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) table1.flavor = "python" rowList1 = table1.get_where_list(condition) results1 = [p['var3'] for p in table1.itersequence(rowList1)] results2 = [p["var3"] for p in table2 if il <= p["var3"] <= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1.sort(), results2.sort()) # Second selection condition = '(il<=t1col)&(t1col sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Fourth selection condition = 't1col>=sl' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) rowList1 = table1.get_where_list(condition) results1 = [p['var3'] for p in table1.itersequence(rowList1)] results2 = [p["var3"] for p in table2 if p["var3"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test08a(self): """Checking get_where_list & itersequence (float flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test08a..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = float(self.il) sl = float(self.sl) # Do some selections and check the results t1col = table1.cols.var4 # First selection condition = '(il<=t1col)&(t1col<=sl)' # results1 = [p["var4"] for p in table1.where(condition)] self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) table1.flavor = "python" rowList1 = table1.get_where_list(condition) results1 = [p['var4'] for p in table1.itersequence(rowList1)] results2 = [p["var4"] for p in table2 if il <= p["var4"] <= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1.sort(), results2.sort()) # Second selection condition = '(il<=t1col)&(t1col sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Fourth selection condition = 't1col>=sl' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) rowList1 = table1.get_where_list(condition) results1 = [p['var4'] for p in table1.itersequence(rowList1)] results2 = [p["var4"] for p in table2 if p["var4"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limit:", sl) # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test09a(self): """Checking non-indexed where() (string flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test09a..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 table1._disable_indexing_in_queries() # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do some selections and check the results t1col = table1.cols.var1 self.assertTrue(t1col is not None) # First selection condition = 't1col<=sl' self.assertTrue(not table1.will_query_use_indexing(condition)) results1 = [p['var1'] for p in table1.where( condition, start=2, stop=10)] results2 = [p["var1"] for p in table2.iterrows(2, 10) if p["var1"] <= sl] if verbose: print("Limit:", sl) # print "Selection results (in-kernel):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Second selection condition = '(il p["var1"] > sl) ] if verbose: print("Limits:", il, sl) print("Limit:", sl) # print "Selection results (in-kernel):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # This selection to be commented out # condition = 't1col>=sl' # self.assertTrue(not table1.will_query_use_indexing(condition)) # results1 = [p['var1'] for p in table1.where(condition,start=2, # stop=-1,step=1)] # results2 = [p["var1"] for p in table2.iterrows(2, -1, 1) # if p["var1"] >= sl] # if verbose: # print "Limit:", sl # print "Selection results (in-kernel):", results1 # print "Should look like:", results2 # print "Length results:", len(results1) # print "Should be:", len(results2) # self.assertEqual(len(results1), len(results2)) # self.assertEqual(results1, results2) # Fourth selection # results1 = [p['var1'] for p in # table1.where(condition,start=2,stop=-1,step=3)] condition = 't1col>=sl' self.assertTrue(not table1.will_query_use_indexing(condition)) results1 = [p['var1'] for p in table1.where(condition, start=2, stop=-1, step=3)] results2 = [p["var1"] for p in table2.iterrows(2, -1, 3) if p["var1"] >= sl] if verbose: print("Limits:", il, sl) # print "Selection results (in-kernel):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Re-enable the indexing in queries basically to unnail the # condition cache and not raising the performance warning # about some indexes being dirty table1._enable_indexing_in_queries() def test09b(self): """Checking non-indexed where() (float flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test09b..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 table1._disable_indexing_in_queries() # Convert the limits to the appropriate type il = float(self.il) sl = float(self.sl) # Do some selections and check the results t1col = table1.cols.var4 self.assertTrue(t1col is not None) # First selection condition = 't1col= sl] if verbose: print("Limit:", sl) # print "Selection results (in-kernel):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Re-enable the indexing in queries basically to unnail the # condition cache and not raising the performance warning # about some indexes being dirty table1._enable_indexing_in_queries() def test09c(self): "Check non-indexed where() w/ ranges, changing step (string flavor)" if verbose: print('\n', '-=' * 30) print("Running %s.test09c..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 table1._disable_indexing_in_queries() # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do some selections and check the results t1col = table1.cols.var1 self.assertTrue(t1col is not None) # First selection condition = 't1col>=sl' self.assertTrue(not table1.will_query_use_indexing(condition)) results1 = [p['var1'] for p in table1.where(condition, start=2, stop=-1, step=3)] results2 = [p["var1"] for p in table2.iterrows(2, -1, 3) if p["var1"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Second selection condition = 't1col>=sl' self.assertTrue(not table1.will_query_use_indexing(condition)) results1 = [p['var1'] for p in table1.where(condition, start=5, stop=-1, step=10)] results2 = [p["var1"] for p in table2.iterrows(5, -1, 10) if p["var1"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Third selection condition = 't1col>=sl' self.assertTrue(not table1.will_query_use_indexing(condition)) results1 = [p['var1'] for p in table1.where(condition, start=5, stop=-3, step=11)] results2 = [p["var1"] for p in table2.iterrows(5, -3, 11) if p["var1"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Fourth selection condition = 't1col>=sl' self.assertTrue(not table1.will_query_use_indexing(condition)) results1 = [p['var1'] for p in table1.where(condition, start=2, stop=-1, step=300)] results2 = [p["var1"] for p in table2.iterrows(2, -1, 300) if p["var1"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Re-enable the indexing in queries basically to unnail the # condition cache and not raising the performance warning # about some indexes being dirty table1._enable_indexing_in_queries() def test09d(self): "Checking non-indexed where() w/ ranges, changing step (int flavor)" if verbose: print('\n', '-=' * 30) print("Running %s.test09d..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 table1._disable_indexing_in_queries() # Convert the limits to the appropriate type il = int(self.il) sl = int(self.sl) # Do some selections and check the results t3col = table1.cols.var3 self.assertTrue(t3col is not None) # First selection condition = 't3col>=sl' self.assertTrue(not table1.will_query_use_indexing(condition)) results1 = [p['var3'] for p in table1.where(condition, start=2, stop=-1, step=3)] results2 = [p["var3"] for p in table2.iterrows(2, -1, 3) if p["var3"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Second selection condition = 't3col>=sl' self.assertTrue(not table1.will_query_use_indexing(condition)) results1 = [p['var3'] for p in table1.where(condition, start=5, stop=-1, step=10)] results2 = [p["var3"] for p in table2.iterrows(5, -1, 10) if p["var3"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Third selection condition = 't3col>=sl' self.assertTrue(not table1.will_query_use_indexing(condition)) results1 = [p['var3'] for p in table1.where(condition, start=5, stop=-3, step=11)] results2 = [p["var3"] for p in table2.iterrows(5, -3, 11) if p["var3"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Fourth selection condition = 't3col>=sl' self.assertTrue(not table1.will_query_use_indexing(condition)) results1 = [p['var3'] for p in table1.where(condition, start=2, stop=-1, step=300)] results2 = [p["var3"] for p in table2.iterrows(2, -1, 300) if p["var3"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Re-enable the indexing in queries basically to unnail the # condition cache and not raising the performance warning # about some indexes being dirty table1._enable_indexing_in_queries() def test10a(self): """Checking indexed where() with ranges (string flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test10a..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do some selections and check the results t1col = table1.cols.var1 # First selection condition = 't1col<=sl' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [ p['var1'] for p in table1.where(condition, start=2, stop=10) ] results2 = [ p["var1"] for p in table2.iterrows(2, 10) if p["var1"] <= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Second selection condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [ p['var1'] for p in table1.where(condition, start=2, stop=30, step=1) ] results2 = [ p["var1"] for p in table2.iterrows(2, 30, 1) if il <= p["var1"] <= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Repeat second selection (testing caches) condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [ p['var1'] for p in table1.where(condition, start=2, stop=30, step=2) ] results2 = [ p["var1"] for p in table2.iterrows(2, 30, 2) if il <= p["var1"] <= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) print("Selection results (indexed):", results1) print("Should look like:", results2) print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Third selection condition = '(il= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test10b(self): """Checking indexed where() with ranges (int flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test10b..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = int(self.il) sl = int(self.sl) # Do some selections and check the results t3col = table1.cols.var3 # First selection condition = 't3col<=sl' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t3col.pathname])) results1 = [ p['var3'] for p in table1.where(condition, start=2, stop=10) ] results2 = [ p["var3"] for p in table2.iterrows(2, 10) if p["var3"] <= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Second selection condition = '(il<=t3col)&(t3col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t3col.pathname])) results1 = [ p['var3'] for p in table1.where(condition, start=2, stop=30, step=2) ] results2 = [ p["var3"] for p in table2.iterrows(2, 30, 2) if il <= p["var3"] <= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Third selection condition = '(il= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test10c(self): """Checking indexed where() with ranges, changing step (string flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test10c..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do some selections and check the results t1col = table1.cols.var1 # First selection condition = 't1col>=sl' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [p['var1'] for p in table1.where(condition, start=2, stop=-1, step=3)] results2 = [p["var1"] for p in table2.iterrows(2, -1, 3) if p["var1"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Second selection condition = 't1col>=sl' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [p['var1'] for p in table1.where(condition, start=5, stop=-1, step=10)] results2 = [p["var1"] for p in table2.iterrows(5, -1, 10) if p["var1"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Third selection condition = 't1col>=sl' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [p['var1'] for p in table1.where(condition, start=5, stop=-3, step=11)] results2 = [p["var1"] for p in table2.iterrows(5, -3, 11) if p["var1"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Fourth selection condition = 't1col>=sl' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [p['var1'] for p in table1.where(condition, start=2, stop=-1, step=300)] results2 = [p["var1"] for p in table2.iterrows(2, -1, 300) if p["var1"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test10d(self): """Checking indexed where() with ranges, changing step (int flavor)""" if verbose: print('\n', '-=' * 30) print("Running %s.test10d..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = int(self.il) sl = int(self.sl) # Do some selections and check the results t3col = table1.cols.var3 # First selection condition = 't3col>=sl' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t3col.pathname])) results1 = [p['var3'] for p in table1.where(condition, start=2, stop=-1, step=3)] results2 = [p["var3"] for p in table2.iterrows(2, -1, 3) if p["var3"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Second selection condition = 't3col>=sl' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t3col.pathname])) results1 = [p['var3'] for p in table1.where(condition, start=5, stop=-1, step=10)] results2 = [p["var3"] for p in table2.iterrows(5, -1, 10) if p["var3"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Third selection condition = 't3col>=sl' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t3col.pathname])) results1 = [p['var3'] for p in table1.where(condition, start=5, stop=-3, step=11)] results2 = [p["var3"] for p in table2.iterrows(5, -3, 11) if p["var3"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Fourth selection condition = 't3col>=sl' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t3col.pathname])) results1 = [p['var3'] for p in table1.where(condition, start=2, stop=-1, step=300)] results2 = [p["var3"] for p in table2.iterrows(2, -1, 300) if p["var3"] >= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test11a(self): """Checking selecting values from an Index via read_coordinates()""" if verbose: print('\n', '-=' * 30) print("Running %s.test11a..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do a selection and check the result t1var1 = table1.cols.var1 condition = '(il<=t1var1)&(t1var1<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1var1.pathname]) ) coords1 = table1.get_where_list(condition) table1.flavor = "python" results1 = table1.read_coordinates(coords1, field="var1") results2 = [p["var1"] for p in table2 if il <= p["var1"] <= sl] results1.sort() results2.sort() if verbose: # print "Superior & inferior limits:", il, sl # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test12a(self): """Checking selecting values after a Table.append() operation.""" if verbose: print('\n', '-=' * 30) print("Running %s.test12a..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Append more rows in already created indexes count = 0 for i in xrange(0, self.nrows//2, self.nrep): for j in range(self.nrep): if self.random: k = random.randrange(self.nrows) elif self.values is not None: lenvalues = len(self.values) if i >= lenvalues: i %= lenvalues k = self.values[i] else: k = i table1.row['var1'] = str(k) table2.row['var1'] = str(k) table1.row['var2'] = k % 2 table2.row['var2'] = k % 2 table1.row['var3'] = k table2.row['var3'] = k table1.row['var4'] = float(self.nrows - k - 1) table2.row['var4'] = float(self.nrows - k - 1) table1.row.append() table2.row.append() count += 1 table1.flush() table2.flush() t1var1 = table1.cols.var1 t1var2 = table1.cols.var2 t1var3 = table1.cols.var3 t1var4 = table1.cols.var4 self.assertFalse(t1var1.index.dirty) self.assertFalse(t1var2.index.dirty) self.assertFalse(t1var3.index.dirty) self.assertFalse(t1var4.index.dirty) # Do some selections and check the results # First selection: string # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') results1 = [p["var1"] for p in table1.where('(il<=t1var1)&(t1var1<=sl)')] results2 = [p["var1"] for p in table2 if il <= p["var1"] <= sl] results1.sort() results2.sort() if verbose: # print "Superior & inferior limits:", il, sl # print "Selection results (index):", results1 print("Should look like:", results2) print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Second selection: bool results1 = [p["var2"] for p in table1.where('t1var2 == True')] results2 = [p["var2"] for p in table2 if p["var2"] is True] if verbose: # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Third selection: int # Convert the limits to the appropriate type il = int(self.il) sl = int(self.sl) t1var3 = table1.cols.var3 results1 = [p["var3"] for p in table1.where( '(il<=t1var3)&(t1var3<=sl)')] results2 = [p["var3"] for p in table2 if il <= p["var3"] <= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Fourth selection: float # Convert the limits to the appropriate type il = float(self.il) sl = float(self.sl) # Do some selections and check the results results1 = [p["var4"] for p in table1.where( '(il<=t1var4)&(t1var4<=sl)')] results2 = [p["var4"] for p in table2 if il <= p["var4"] <= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: # print "Selection results (index):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1.sort(), results2.sort()) def test13a(self): """Checking repeated queries (checking caches)""" if verbose: print('\n', '-=' * 30) print("Running %s.test13a..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do some selections and check the results t1col = table1.cols.var1 condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [ p['var1'] for p in table1.where(condition, start=2, stop=30, step=1) ] results2 = [ p["var1"] for p in table2.iterrows(2, 30, 1) if il <= p["var1"] <= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Repeat the selection (testing caches) condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [ p['var1'] for p in table1.where(condition, start=2, stop=30, step=2) ] results2 = [ p["var1"] for p in table2.iterrows(2, 30, 2) if il <= p["var1"] <= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test13b(self): """Checking repeated queries, varying step (checking caches)""" if verbose: print('\n', '-=' * 30) print("Running %s.test13b..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do some selections and check the results t1col = table1.cols.var1 condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [ p['var1'] for p in table1.where(condition, start=2, stop=30, step=1) ] results2 = [ p["var1"] for p in table2.iterrows(2, 30, 1) if il <= p["var1"] <= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Repeat the selection (testing caches) condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [ p['var1'] for p in table1.where(condition, start=2, stop=30, step=2) ] results2 = [ p["var1"] for p in table2.iterrows(2, 30, 2) if il <= p["var1"] <= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test13c(self): """Checking repeated queries, varying start, stop, step.""" if verbose: print('\n', '-=' * 30) print("Running %s.test13c..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do some selections and check the results t1col = table1.cols.var1 condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [ p['var1'] for p in table1.where(condition, start=0, stop=1, step=2) ] results2 = [ p["var1"] for p in table2.iterrows(0, 1, 2) if il <= p["var1"] <= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Repeat the selection (testing caches) condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [ p['var1'] for p in table1.where(condition, start=0, stop=5, step=1) ] results2 = [ p["var1"] for p in table2.iterrows(0, 5, 1) if il <= p["var1"] <= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test13d(self): """Checking repeated queries, varying start, stop, step (another twist)""" if verbose: print('\n', '-=' * 30) print("Running %s.test13d..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do some selections and check the results t1col = table1.cols.var1 condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname]) ) results1 = [ p['var1'] for p in table1.where(condition, start=0, stop=1, step=1) ] results2 = [ p["var1"] for p in table2.iterrows(0, 1, 1) if il <= p["var1"] <= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Repeat the selection (testing caches) condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [ p['var1'] for p in table1.where(condition, start=0, stop=1, step=1) ] results2 = [ p["var1"] for p in table2.iterrows(0, 1, 1) if il <= p["var1"] <= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test13e(self): """Checking repeated queries, with varying condition.""" if verbose: print('\n', '-=' * 30) print("Running %s.test13e..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do some selections and check the results t1col = table1.cols.var1 condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [ p['var1'] for p in table1.where(condition, start=0, stop=10, step=1) ] results2 = [ p["var1"] for p in table2.iterrows(0, 10, 1) if il <= p["var1"] <= sl ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Repeat the selection with a more complex condition t2col = table1.cols.var2 condition = '(il<=t1col)&(t1col<=sl)&(t2col==True)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname, t2col.pathname])) results1 = [ p['var1'] for p in table1.where(condition, start=0, stop=10, step=1) ] results2 = [ p["var1"] for p in table2.iterrows(0, 10, 1) if il <= p["var1"] <= sl and p["var2"] is True ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test13f(self): """Checking repeated queries, with varying condition.""" if verbose: print('\n', '-=' * 30) print("Running %s.test13f..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Remove indexes in var2 column table1.cols.var2.remove_index() table2.cols.var2.remove_index() # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do some selections and check the results t1col = table1.cols.var1 t2col = table1.cols.var2 self.assertTrue(t2col is not None) condition = '(il<=t1col)&(t1col<=sl)&(t2col==True)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [p['var1'] for p in table1.where(condition, start=0, stop=10, step=1)] results2 = [ p["var1"] for p in table2.iterrows(0, 10, 1) if il <= p["var1"] <= sl and p["var2"] is True ] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Repeat the selection with a simpler condition condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [p['var1'] for p in table1.where(condition, start=0, stop=10, step=1)] results2 = [p["var1"] for p in table2.iterrows(0, 10, 1) if il <= p["var1"] <= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Repeat again with the original condition, but with a constant constant = True condition = '(il<=t1col)&(t1col<=sl)&(t2col==constant)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [p['var1'] for p in table1.where(condition, start=0, stop=10, step=1)] results2 = [p["var1"] for p in table2.iterrows(0, 10, 1) if il <= p["var1"] <= sl and p["var2"] == constant] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) def test13g(self): """Checking repeated queries, with different limits.""" if verbose: print('\n', '-=' * 30) print("Running %s.test13g..." % self.__class__.__name__) table1 = self.fileh.root.table1 table2 = self.fileh.root.table2 # Convert the limits to the appropriate type il = str(self.il).encode('ascii') sl = str(self.sl).encode('ascii') # Do some selections and check the results t1col = table1.cols.var1 condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [p['var1'] for p in table1.where(condition, start=0, stop=10, step=1)] results2 = [p["var1"] for p in table2.iterrows(0, 10, 1) if il <= p["var1"] <= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) # Repeat the selection with different limits il, sl = (str(self.il + 1).encode( 'ascii'), str(self.sl-2).encode('ascii')) t2col = table1.cols.var2 self.assertTrue(t2col is not None) condition = '(il<=t1col)&(t1col<=sl)' self.assertTrue( table1.will_query_use_indexing(condition) == fzset([t1col.pathname])) results1 = [p['var1'] for p in table1.where(condition, start=0, stop=10, step=1)] results2 = [p["var1"] for p in table2.iterrows(0, 10, 1) if il <= p["var1"] <= sl] # sort lists (indexing does not guarantee that rows are returned in # order) results1.sort() results2.sort() if verbose: print("Limits:", il, sl) # print "Selection results (indexed):", results1 # print "Should look like:", results2 print("Length results:", len(results1)) print("Should be:", len(results2)) self.assertEqual(len(results1), len(results2)) self.assertEqual(results1, results2) class SV1aTestCase(SelectValuesTestCase): blocksizes = small_blocksizes chunkshape = 1 buffersize = 2 ss = blocksizes[2] nrows = ss reopen = 0 nrep = ss il = 0 sl = ss class SV1bTestCase(SV1aTestCase): blocksizes = calc_chunksize(minRowIndex, memlevel=1) chunkshape = blocksizes[2]//2**9 buffersize = chunkshape * 5 class SV2aTestCase(SelectValuesTestCase): blocksizes = small_blocksizes chunkshape = 2 buffersize = 2 ss = blocksizes[2] nrows = ss * 2-1 reopen = 1 nrep = 1 il = 0 sl = 2 class SV2bTestCase(SV2aTestCase): blocksizes = calc_chunksize(minRowIndex, memlevel=1) chunkshape = blocksizes[2]//2**7 buffersize = chunkshape * 20 class SV3aTestCase(SelectValuesTestCase): blocksizes = small_blocksizes chunkshape = 2 buffersize = 3 ss = blocksizes[2] nrows = ss * 5-1 reopen = 1 nrep = 3 il = 0 sl = 3 class SV3bTestCase(SV3aTestCase): blocksizes = calc_chunksize(minRowIndex, memlevel=1) # chunkshape = 4 # buffersize = 16 chunkshape = 3 buffersize = 9 class SV4aTestCase(SelectValuesTestCase): blocksizes = small_blocksizes buffersize = 10 ss = blocksizes[2] nrows = ss * 3 reopen = 0 nrep = 1 # il = nrows-cs il = 0 sl = nrows class SV4bTestCase(SV4aTestCase): blocksizes = calc_chunksize(minRowIndex, memlevel=1) chunkshape = 500 buffersize = 1000 class SV5aTestCase(SelectValuesTestCase): blocksizes = small_blocksizes ss = blocksizes[2] nrows = ss * 5 reopen = 0 nrep = 1 il = 0 sl = nrows class SV5bTestCase(SV5aTestCase): blocksizes = calc_chunksize(minRowIndex, memlevel=1) class SV6aTestCase(SelectValuesTestCase): blocksizes = small_blocksizes ss = blocksizes[2] nrows = ss * 5 + 1 reopen = 0 cs = blocksizes[3] nrep = cs + 1 il = -1 sl = nrows class SV6bTestCase(SV6aTestCase): blocksizes = calc_chunksize(minRowIndex, memlevel=1) class SV7aTestCase(SelectValuesTestCase): random = 1 blocksizes = small_blocksizes ss = blocksizes[2] nrows = ss * 5 + 3 reopen = 0 cs = blocksizes[3] nrep = cs-1 il = -10 sl = nrows class SV7bTestCase(SV7aTestCase): blocksizes = calc_chunksize(minRowIndex, memlevel=1) class SV8aTestCase(SelectValuesTestCase): random = 0 chunkshape = 1 blocksizes = small_blocksizes ss = blocksizes[2] nrows = ss * 5-3 reopen = 0 cs = blocksizes[3] nrep = cs-1 il = 10 sl = nrows-10 class SV8bTestCase(SV8aTestCase): random = 0 blocksizes = calc_chunksize(minRowIndex, memlevel=1) class SV9aTestCase(SelectValuesTestCase): random = 1 blocksizes = small_blocksizes ss = blocksizes[2] nrows = ss * 5 + 11 reopen = 0 cs = blocksizes[3] nrep = cs-1 il = 10 sl = nrows-10 class SV9bTestCase(SV9aTestCase): blocksizes = calc_chunksize(minRowIndex, memlevel=1) class SV10aTestCase(SelectValuesTestCase): random = 1 blocksizes = small_blocksizes chunkshape = 1 buffersize = 1 ss = blocksizes[2] nrows = ss reopen = 0 nrep = ss il = 0 sl = ss class SV10bTestCase(SV10aTestCase): blocksizes = calc_chunksize(minRowIndex, memlevel=1) chunkshape = 5 buffersize = 6 class SV11aTestCase(SelectValuesTestCase): # This checks a special case that failed. It was discovered in a # random test above (SV10a). It is explicitely put here as a way # to always check that specific case. values = [1, 7, 6, 7, 0, 7, 4, 4, 9, 5] blocksizes = small_blocksizes chunkshape = 1 buffersize = 1 ss = blocksizes[2] nrows = ss reopen = 0 nrep = ss il = 0 sl = ss class SV11bTestCase(SelectValuesTestCase): # This checks a special case that failed. It was discovered in a # random test above (SV10a). It is explicitely put here as a way # to always check that specific case. values = [1, 7, 6, 7, 0, 7, 4, 4, 9, 5] chunkshape = 2 buffersize = 2 blocksizes = calc_chunksize(minRowIndex, memlevel=1) ss = blocksizes[2] nrows = ss reopen = 0 nrep = ss il = 0 sl = ss class SV12aTestCase(SelectValuesTestCase): # This checks a special case that failed. It was discovered in a # random test above (SV10b). It is explicitely put here as a way # to always check that specific case. # values = [0, 7, 0, 6, 5, 1, 6, 7, 0, 0] values = [4, 4, 1, 5, 2, 0, 1, 4, 3, 9] blocksizes = small_blocksizes chunkshape = 1 buffersize = 1 ss = blocksizes[2] nrows = ss reopen = 0 nrep = ss il = 0 sl = ss class SV12bTestCase(SelectValuesTestCase): # This checks a special case that failed. It was discovered in a # random test above (SV10b). It is explicitely put here as a way # to always check that specific case. # values = [0, 7, 0, 6, 5, 1, 6, 7, 0, 0] values = [4, 4, 1, 5, 2, 0, 1, 4, 3, 9] blocksizes = calc_chunksize(minRowIndex, memlevel=1) chunkshape = 2 buffersize = 2 ss = blocksizes[2] nrows = ss reopen = 1 nrep = ss il = 0 sl = ss class SV13aTestCase(SelectValuesTestCase): values = [0, 7, 0, 6, 5, 1, 6, 7, 0, 0] blocksizes = small_blocksizes chunkshape = 3 buffersize = 5 ss = blocksizes[2] nrows = ss reopen = 0 nrep = ss il = 0 sl = ss class SV13bTestCase(SelectValuesTestCase): values = [0, 7, 0, 6, 5, 1, 6, 7, 0, 0] blocksizes = calc_chunksize(minRowIndex, memlevel=1) chunkshape = 5 buffersize = 10 ss = blocksizes[2] nrows = ss reopen = 1 nrep = ss il = 0 sl = ss class SV14aTestCase(SelectValuesTestCase): values = [1, 7, 6, 7, 0, 7, 4, 4, 9, 5] blocksizes = small_blocksizes chunkshape = 2 buffersize = 5 ss = blocksizes[2] nrows = ss reopen = 0 cs = blocksizes[3] nrep = cs il = -5 sl = 500 class SV14bTestCase(SelectValuesTestCase): values = [1, 7, 6, 7, 0, 7, 4, 4, 9, 5] blocksizes = calc_chunksize(minRowIndex, memlevel=1) chunkshape = 9 buffersize = 10 ss = blocksizes[2] nrows = ss reopen = 1 nrep = 9 il = 0 cs = blocksizes[3] sl = ss-cs + 1 class SV15aTestCase(SelectValuesTestCase): # Test that checks for case where there are not valid values in # the indexed part, but they exist in the non-indexed region. # At least, test01b takes account of that random = 1 # Both values of seed below triggers a fail in indexing code # seed = 1885 seed = 183 blocksizes = small_blocksizes ss = blocksizes[2] nrows = ss * 5 + 1 reopen = 0 cs = blocksizes[3] nrep = cs-1 il = -10 sl = nrows class SV15bTestCase(SelectValuesTestCase): # Test that checks for case where there are not valid values in # the indexed part, but they exist in the non-indexed region. # At least, test01b takes account of that random = 1 # Both values of seed below triggers a fail in indexing code seed = 1885 # seed = 183 blocksizes = calc_chunksize(minRowIndex, memlevel=1) ss = blocksizes[2] nrows = ss * 5 + 1 reopen = 1 cs = blocksizes[3] nrep = cs-1 il = -10 sl = nrows class LastRowReuseBuffers(common.PyTablesTestCase): # Test that checks for possible reuse of buffers coming # from last row in the sorted part of indexes nelem = 1221 numpy.random.seed(1) random.seed(1) class Record(IsDescription): id1 = Int16Col() def test00_lrucache(self): filename = tempfile.mktemp(".h5") fp = open_file(filename, 'w', node_cache_slots=64) ta = fp.create_table('/', 'table', self.Record, filters=Filters(1)) id1 = numpy.random.randint(0, 2**15, self.nelem) ta.append([id1]) ta.cols.id1.create_index() for i in xrange(self.nelem): nrow = random.randint(0, self.nelem-1) value = id1[nrow] idx = ta.get_where_list('id1 == %s' % value) self.assertTrue(len(idx) > 0, "idx--> %s %s %s %s" % (idx, i, nrow, value)) self.assertTrue( nrow in idx, "nrow not found: %s != %s, %s" % (idx, nrow, value)) fp.close() os.remove(filename) def test01_nocache(self): filename = tempfile.mktemp(".h5") fp = open_file(filename, 'w', node_cache_slots=0) ta = fp.create_table('/', 'table', self.Record, filters=Filters(1)) id1 = numpy.random.randint(0, 2**15, self.nelem) ta.append([id1]) ta.cols.id1.create_index() for i in xrange(self.nelem): nrow = random.randint(0, self.nelem-1) value = id1[nrow] idx = ta.get_where_list('id1 == %s' % value) self.assertTrue(len(idx) > 0, "idx--> %s %s %s %s" % (idx, i, nrow, value)) self.assertTrue( nrow in idx, "nrow not found: %s != %s, %s" % (idx, nrow, value)) fp.close() os.remove(filename) def test02_dictcache(self): filename = tempfile.mktemp(".h5") fp = open_file(filename, 'w', node_cache_slots=-64) ta = fp.create_table('/', 'table', self.Record, filters=Filters(1)) id1 = numpy.random.randint(0, 2**15, self.nelem) ta.append([id1]) ta.cols.id1.create_index() for i in xrange(self.nelem): nrow = random.randint(0, self.nelem-1) value = id1[nrow] idx = ta.get_where_list('id1 == %s' % value) self.assertTrue(len(idx) > 0, "idx--> %s %s %s %s" % (idx, i, nrow, value)) self.assertTrue( nrow in idx, "nrow not found: %s != %s, %s" % (idx, nrow, value)) fp.close() os.remove(filename) normal_tests = ( "SV1aTestCase", "SV2aTestCase", "SV3aTestCase", ) heavy_tests = ( # The next are too hard to be in the 'normal' suite "SV1bTestCase", "SV2bTestCase", "SV3bTestCase", "SV4aTestCase", "SV5aTestCase", "SV6aTestCase", "SV7aTestCase", "SV8aTestCase", "SV9aTestCase", "SV10aTestCase", "SV11aTestCase", "SV12aTestCase", "SV13aTestCase", "SV14aTestCase", "SV15aTestCase", # This are properly heavy "SV4bTestCase", "SV5bTestCase", "SV6bTestCase", "SV7bTestCase", "SV8bTestCase", "SV9bTestCase", "SV10bTestCase", "SV11bTestCase", "SV12bTestCase", "SV13bTestCase", "SV14bTestCase", "SV15bTestCase", ) # Base classes for the different type indexes. class UltraLightITableMixin: kind = "ultralight" class LightITableMixin: kind = "light" class MediumITableMixin: kind = "medium" class FullITableMixin: kind = "full" # Parameters for indexed queries. ckinds = ['UltraLight', 'Light', 'Medium', 'Full'] testlevels = ['Normal', 'Heavy'] # Indexed queries: ``[ULMF]I[NH]SVXYTestCase``, where: # # 1. U is for 'UltraLight', L for 'Light', M for 'Medium', F for 'Full' indexes # 2. N is for 'Normal', H for 'Heavy' tests def iclassdata(): for ckind in ckinds: for ctest in normal_tests + heavy_tests: classname = '%sI%s%s' % (ckind[0], testlevels[heavy][0], ctest) # Uncomment the next one and comment the past one if one # don't want to include the methods (testing purposes only) # cbasenames = ( '%sITableMixin' % ckind, "object") cbasenames = ('%sITableMixin' % ckind, ctest) classdict = dict(heavy=bool(ctest in heavy_tests)) yield (classname, cbasenames, classdict) # Create test classes. for (cname, cbasenames, cdict) in iclassdata(): cbases = tuple(eval(cbase) for cbase in cbasenames) class_ = type(cname, cbases, cdict) exec('%s = class_' % cname) # ----------------------------- def suite(): theSuite = unittest.TestSuite() niter = 1 for n in range(niter): for cdata in iclassdata(): class_ = eval(cdata[0]) if not class_.heavy: suite_ = unittest.makeSuite(class_) theSuite.addTest(suite_) elif heavy: suite_ = unittest.makeSuite(class_) theSuite.addTest(suite_) theSuite.addTest(unittest.makeSuite(LastRowReuseBuffers)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/test_links.py000066400000000000000000000517441231437614300206670ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: 2009-11-24 # Author: Francesc Alted - faltet@pytables.org # # $Id$ # ######################################################################## """Test module for diferent kind of links under PyTables.""" from __future__ import print_function import os import unittest import tempfile import tables from tables.tests import common # Test for hard links class HardLinkTestCase(common.TempFileMixin, common.PyTablesTestCase): def _createFile(self): self.h5file.create_array('/', 'arr1', [1, 2]) group1 = self.h5file.create_group('/', 'group1') arr2 = self.h5file.create_array(group1, 'arr2', [1, 2, 3]) lgroup1 = self.h5file.create_hard_link('/', 'lgroup1', '/group1') self.assertTrue(lgroup1 is not None) larr1 = self.h5file.create_hard_link(group1, 'larr1', '/arr1') self.assertTrue(larr1 is not None) larr2 = self.h5file.create_hard_link('/', 'larr2', arr2) self.assertTrue(larr2 is not None) def test00_create(self): """Creating hard links.""" self._createFile() self._checkEqualityGroup(self.h5file.root.group1, self.h5file.root.lgroup1, hardlink=True) self._checkEqualityLeaf(self.h5file.root.arr1, self.h5file.root.group1.larr1, hardlink=True) self._checkEqualityLeaf(self.h5file.root.lgroup1.arr2, self.h5file.root.larr2, hardlink=True) def test01_open(self): """Opening a file with hard links.""" self._createFile() self._reopen() self._checkEqualityGroup(self.h5file.root.group1, self.h5file.root.lgroup1, hardlink=True) self._checkEqualityLeaf(self.h5file.root.arr1, self.h5file.root.group1.larr1, hardlink=True) self._checkEqualityLeaf(self.h5file.root.lgroup1.arr2, self.h5file.root.larr2, hardlink=True) def test02_removeLeaf(self): """Removing a hard link to a Leaf.""" self._createFile() # First delete the initial link self.h5file.root.arr1.remove() self.assertTrue('/arr1' not in self.h5file) # The second link should still be there if common.verbose: print("Remaining link:", self.h5file.root.group1.larr1) self.assertTrue('/group1/larr1' in self.h5file) # Remove the second link self.h5file.root.group1.larr1.remove() self.assertTrue('/group1/larr1' not in self.h5file) def test03_removeGroup(self): """Removing a hard link to a Group.""" self._createFile() if common.verbose: print("Original object tree:", self.h5file) # First delete the initial link self.h5file.root.group1._f_remove(force=True) self.assertTrue('/group1' not in self.h5file) # The second link should still be there if common.verbose: print("Remaining link:", self.h5file.root.lgroup1) print("Object tree:", self.h5file) self.assertTrue('/lgroup1' in self.h5file) # Remove the second link self.h5file.root.lgroup1._g_remove(recursive=True) self.assertTrue('/lgroup1' not in self.h5file) if common.verbose: print("Final object tree:", self.h5file) # Test for soft links class SoftLinkTestCase(common.TempFileMixin, common.PyTablesTestCase): def _createFile(self): self.h5file.create_array('/', 'arr1', [1, 2]) group1 = self.h5file.create_group('/', 'group1') arr2 = self.h5file.create_array(group1, 'arr2', [1, 2, 3]) lgroup1 = self.h5file.create_soft_link('/', 'lgroup1', '/group1') self.assertTrue(lgroup1 is not None) larr1 = self.h5file.create_soft_link(group1, 'larr1', '/arr1') self.assertTrue(larr1 is not None) larr2 = self.h5file.create_soft_link('/', 'larr2', arr2) self.assertTrue(larr2 is not None) def test00_create(self): """Creating soft links.""" self._createFile() self._checkEqualityGroup(self.h5file.root.group1, self.h5file.root.lgroup1()) self._checkEqualityLeaf(self.h5file.root.arr1, self.h5file.root.group1.larr1()) self._checkEqualityLeaf(self.h5file.root.lgroup1().arr2, self.h5file.root.larr2()) def test01_open(self): """Opening a file with soft links.""" self._createFile() self._reopen() self._checkEqualityGroup(self.h5file.root.group1, self.h5file.root.lgroup1()) self._checkEqualityLeaf(self.h5file.root.arr1, self.h5file.root.group1.larr1()) self._checkEqualityLeaf(self.h5file.root.lgroup1().arr2, self.h5file.root.larr2()) def test02_remove(self): """Removing a soft link.""" self._createFile() # First delete the referred link self.h5file.root.arr1.remove() self.assertTrue('/arr1' not in self.h5file) # The soft link should still be there (but dangling) if common.verbose: print("Dangling link:", self.h5file.root.group1.larr1) self.assertTrue('/group1/larr1' in self.h5file) # Remove the soft link itself self.h5file.root.group1.larr1.remove() self.assertTrue('/group1/larr1' not in self.h5file) def test03_copy(self): """Copying a soft link.""" self._createFile() # Copy the link into another location root = self.h5file.root lgroup1 = root.lgroup1 lgroup2 = lgroup1.copy('/', 'lgroup2') self.assertTrue('/lgroup1' in self.h5file) self.assertTrue('/lgroup2' in self.h5file) self.assertTrue('lgroup2' in root._v_children) self.assertTrue('lgroup2' in root._v_links) if common.verbose: print("Copied link:", lgroup2) # Remove the first link lgroup1.remove() self._checkEqualityGroup(self.h5file.root.group1, self.h5file.root.lgroup2()) def test03_overwrite(self): """Overwrite a soft link.""" self._createFile() # Copy the link into another location root = self.h5file.root lgroup1 = root.lgroup1 lgroup2 = lgroup1.copy('/', 'lgroup2') lgroup2 = lgroup1.copy('/', 'lgroup2', overwrite=True) self.assertTrue('/lgroup1' in self.h5file) self.assertTrue('/lgroup2' in self.h5file) self.assertTrue('lgroup2' in root._v_children) self.assertTrue('lgroup2' in root._v_links) if common.verbose: print("Copied link:", lgroup2) # Remove the first link lgroup1.remove() self._checkEqualityGroup(self.h5file.root.group1, self.h5file.root.lgroup2()) def test04_move(self): """Moving a soft link.""" self._createFile() # Move the link into another location lgroup1 = self.h5file.root.lgroup1 group2 = self.h5file.create_group('/', 'group2') lgroup1.move(group2, 'lgroup2') lgroup2 = self.h5file.root.group2.lgroup2 if common.verbose: print("Moved link:", lgroup2) self.assertTrue('/lgroup1' not in self.h5file) self.assertTrue('/group2/lgroup2' in self.h5file) self._checkEqualityGroup(self.h5file.root.group1, self.h5file.root.group2.lgroup2()) def test05_rename(self): """Renaming a soft link.""" self._createFile() # Rename the link lgroup1 = self.h5file.root.lgroup1 lgroup1.rename('lgroup2') lgroup2 = self.h5file.root.lgroup2 if common.verbose: print("Moved link:", lgroup2) self.assertTrue('/lgroup1' not in self.h5file) self.assertTrue('/lgroup2' in self.h5file) self._checkEqualityGroup(self.h5file.root.group1, self.h5file.root.lgroup2()) def test06a_relative_path(self): """Using soft links with relative paths.""" self._createFile() # Create new group self.h5file.create_group('/group1', 'group3') # ... and relative link lgroup3 = self.h5file.create_soft_link( '/group1', 'lgroup3', 'group3') if common.verbose: print("Relative path link:", lgroup3) self.assertTrue('/group1/lgroup3' in self.h5file) self._checkEqualityGroup(self.h5file.root.group1.group3, self.h5file.root.group1.lgroup3()) def test06b_relative_path(self): """Using soft links with relative paths (./ version)""" self._createFile() # Create new group self.h5file.create_group('/group1', 'group3') # ... and relative link lgroup3 = self.h5file.create_soft_link( '/group1', 'lgroup3', './group3') if common.verbose: print("Relative path link:", lgroup3) self.assertTrue('/group1/lgroup3' in self.h5file) self._checkEqualityGroup(self.h5file.root.group1.group3, self.h5file.root.group1.lgroup3()) def test07_walkNodes(self): """Checking `walk_nodes` with `classname` option.""" self._createFile() links = [node._v_pathname for node in self.h5file.walk_nodes('/', classname="Link")] if common.verbose: print("detected links (classname='Link'):", links) self.assertEqual(links, ['/larr2', '/lgroup1', '/group1/larr1']) links = [node._v_pathname for node in self.h5file.walk_nodes('/', classname="SoftLink")] if common.verbose: print("detected links (classname='SoftLink'):", links) self.assertEqual(links, ['/larr2', '/lgroup1', '/group1/larr1']) def test08__v_links(self): """Checking `Group._v_links`.""" self._createFile() links = [node for node in self.h5file.root._v_links] if common.verbose: print("detected links (under root):", links) self.assertEqual(len(links), 2) links = [node for node in self.h5file.root.group1._v_links] if common.verbose: print("detected links (under /group1):", links) self.assertEqual(links, ['larr1']) def test09_link_to_link(self): """Checking linked links.""" self._createFile() # Create a link to another existing link lgroup2 = self.h5file.create_soft_link( '/', 'lgroup2', '/lgroup1') # Dereference it once: self.assertTrue(lgroup2() is self.h5file.get_node('/lgroup1')) if common.verbose: print("First dereference is correct:", lgroup2()) # Dereference it twice: self.assertTrue(lgroup2()() is self.h5file.get_node('/group1')) if common.verbose: print("Second dereference is correct:", lgroup2()()) def test10_copy_link_to_file(self): """Checking copying a link to another file.""" self._createFile() fname = tempfile.mktemp(".h5") h5f = tables.open_file(fname, "a") h5f.create_array('/', 'arr1', [1, 2]) h5f.create_group('/', 'group1') lgroup1 = self.h5file.root.lgroup1 lgroup1_ = lgroup1.copy(h5f.root, 'lgroup1') self.assertTrue('/lgroup1' in self.h5file) self.assertTrue('/lgroup1' in h5f) self.assertTrue(lgroup1_ in h5f) if common.verbose: print("Copied link:", lgroup1_, 'in:', lgroup1_._v_file.filename) h5f.close() os.remove(fname) # Test for external links class ExternalLinkTestCase(common.TempFileMixin, common.PyTablesTestCase): def tearDown(self): """Remove ``extfname``.""" self.exth5file.close() super(ExternalLinkTestCase, self).tearDown() #open_files = tables.file._open_files #if self.extfname in open_files: # #assert False # for handler in open_files.get_handlers_by_name(self.extfname): # handler.close() os.remove(self.extfname) # comment this for debugging purposes only def _createFile(self): self.h5file.create_array('/', 'arr1', [1, 2]) group1 = self.h5file.create_group('/', 'group1') self.h5file.create_array(group1, 'arr2', [1, 2, 3]) # The external file self.extfname = tempfile.mktemp(".h5") self.exth5file = tables.open_file(self.extfname, "w") extarr1 = self.exth5file.create_array('/', 'arr1', [1, 2]) self.assertTrue(extarr1 is not None) extgroup1 = self.exth5file.create_group('/', 'group1') extarr2 = self.exth5file.create_array(extgroup1, 'arr2', [1, 2, 3]) # Create external links lgroup1 = self.h5file.create_external_link( '/', 'lgroup1', '%s:/group1' % self.extfname) self.assertTrue(lgroup1 is not None) larr1 = self.h5file.create_external_link( group1, 'larr1', '%s:/arr1' % self.extfname) self.assertTrue(larr1 is not None) larr2 = self.h5file.create_external_link('/', 'larr2', extarr2) self.assertTrue(larr2 is not None) # Re-open the external file in 'r'ead-only mode self.exth5file.close() self.exth5file = tables.open_file(self.extfname, "r") def test00_create(self): """Creating soft links.""" self._createFile() self._checkEqualityGroup(self.exth5file.root.group1, self.h5file.root.lgroup1()) self._checkEqualityLeaf(self.exth5file.root.arr1, self.h5file.root.group1.larr1()) self._checkEqualityLeaf(self.h5file.root.lgroup1().arr2, self.h5file.root.larr2()) def test01_open(self): """Opening a file with soft links.""" self._createFile() self._reopen() self._checkEqualityGroup(self.exth5file.root.group1, self.h5file.root.lgroup1()) self._checkEqualityLeaf(self.exth5file.root.arr1, self.h5file.root.group1.larr1()) self._checkEqualityLeaf(self.h5file.root.lgroup1().arr2, self.h5file.root.larr2()) def test02_remove(self): """Removing an external link.""" self._createFile() # Re-open the external file in 'a'ppend mode self.exth5file.close() self.exth5file = tables.open_file(self.extfname, "a") # First delete the referred link self.exth5file.root.arr1.remove() self.assertTrue('/arr1' not in self.exth5file) # The external link should still be there (but dangling) if common.verbose: print("Dangling link:", self.h5file.root.group1.larr1) self.assertTrue('/group1/larr1' in self.h5file) # Remove the external link itself self.h5file.root.group1.larr1.remove() self.assertTrue('/group1/larr1' not in self.h5file) def test03_copy(self): """Copying an external link.""" self._createFile() # Copy the link into another location root = self.h5file.root lgroup1 = root.lgroup1 lgroup2 = lgroup1.copy('/', 'lgroup2') self.assertTrue('/lgroup1' in self.h5file) self.assertTrue('/lgroup2' in self.h5file) self.assertTrue('lgroup2' in root._v_children) self.assertTrue('lgroup2' in root._v_links) if common.verbose: print("Copied link:", lgroup2) # Remove the first link lgroup1.remove() self._checkEqualityGroup(self.exth5file.root.group1, self.h5file.root.lgroup2()) def test03_overwrite(self): """Overwrite an external link.""" self._createFile() # Copy the link into another location root = self.h5file.root lgroup1 = root.lgroup1 lgroup2 = lgroup1.copy('/', 'lgroup2') lgroup2 = lgroup1.copy('/', 'lgroup2', overwrite=True) self.assertTrue('/lgroup1' in self.h5file) self.assertTrue('/lgroup2' in self.h5file) self.assertTrue('lgroup2' in root._v_children) self.assertTrue('lgroup2' in root._v_links) if common.verbose: print("Copied link:", lgroup2) # Remove the first link lgroup1.remove() self._checkEqualityGroup(self.exth5file.root.group1, self.h5file.root.lgroup2()) def test04_move(self): """Moving an external link.""" self._createFile() # Move the link into another location lgroup1 = self.h5file.root.lgroup1 group2 = self.h5file.create_group('/', 'group2') lgroup1.move(group2, 'lgroup2') lgroup2 = self.h5file.root.group2.lgroup2 if common.verbose: print("Moved link:", lgroup2) self.assertTrue('/lgroup1' not in self.h5file) self.assertTrue('/group2/lgroup2' in self.h5file) self._checkEqualityGroup(self.exth5file.root.group1, self.h5file.root.group2.lgroup2()) def test05_rename(self): """Renaming an external link.""" self._createFile() # Rename the link lgroup1 = self.h5file.root.lgroup1 lgroup1.rename('lgroup2') lgroup2 = self.h5file.root.lgroup2 if common.verbose: print("Moved link:", lgroup2) self.assertTrue('/lgroup1' not in self.h5file) self.assertTrue('/lgroup2' in self.h5file) self._checkEqualityGroup(self.exth5file.root.group1, self.h5file.root.lgroup2()) def test07_walkNodes(self): """Checking `walk_nodes` with `classname` option.""" self._createFile() # Create a new soft link self.h5file.create_soft_link('/group1', 'lgroup3', './group3') links = [node._v_pathname for node in self.h5file.walk_nodes('/', classname="Link")] if common.verbose: print("detected links (classname='Link'):", links) self.assertEqual(links, ['/larr2', '/lgroup1', '/group1/larr1', '/group1/lgroup3']) links = [node._v_pathname for node in self.h5file.walk_nodes('/', classname="ExternalLink")] if common.verbose: print("detected links (classname='ExternalLink'):", links) self.assertEqual(links, ['/larr2', '/lgroup1', '/group1/larr1']) def test08__v_links(self): """Checking `Group._v_links`.""" self._createFile() links = [node for node in self.h5file.root._v_links] if common.verbose: print("detected links (under root):", links) self.assertEqual(len(links), 2) links = [node for node in self.h5file.root.group1._v_links] if common.verbose: print("detected links (under /group1):", links) self.assertEqual(links, ['larr1']) def test09_umount(self): """Checking `umount()` method.""" self._createFile() link = self.h5file.root.lgroup1 self.assertTrue(link.extfile is None) # Dereference a external node (and hence, 'mount' a file) enode = link() self.assertTrue(enode is not None) self.assertTrue(link.extfile is not None) # Umount the link link.umount() self.assertTrue(link.extfile is None) def test10_copy_link_to_file(self): """Checking copying a link to another file.""" self._createFile() fname = tempfile.mktemp(".h5") h5f = tables.open_file(fname, "a") h5f.create_array('/', 'arr1', [1, 2]) h5f.create_group('/', 'group1') lgroup1 = self.h5file.root.lgroup1 lgroup1_ = lgroup1.copy(h5f.root, 'lgroup1') self.assertTrue('/lgroup1' in self.h5file) self.assertTrue('/lgroup1' in h5f) self.assertTrue(lgroup1_ in h5f) if common.verbose: print("Copied link:", lgroup1_, 'in:', lgroup1_._v_file.filename) h5f.close() os.remove(fname) #---------------------------------------------------------------------- def suite(): """Return a test suite consisting of all the test cases in the module.""" theSuite = unittest.TestSuite() niter = 1 # common.heavy = 1 # uncomment this only for testing purposes for i in range(niter): theSuite.addTest(unittest.makeSuite(HardLinkTestCase)) theSuite.addTest(unittest.makeSuite(SoftLinkTestCase)) if tables.file._FILE_OPEN_POLICY != 'strict': theSuite.addTest(unittest.makeSuite(ExternalLinkTestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/tests/test_lists.py000066400000000000000000000413051231437614300206750ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import sys import unittest import os import tempfile from tables import * from tables.tests import common # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup def WriteRead(filename, testTuple): if common.verbose: print('\n', '-=' * 30) print("Running test for object %s" % type(testTuple)) # Create an instance of HDF5 Table fileh = open_file(filename, mode="w") root = fileh.root try: # Create the array under root and name 'somearray' a = testTuple fileh.create_array(root, 'somearray', a, "Some array") finally: # Close the file fileh.close() # Re-open the file in read-only mode fileh = open_file(filename, mode="r") root = fileh.root # Read the saved array try: b = root.somearray.read() # Compare them. They should be equal. if not a == b and common.verbose: print("Write and read lists/tuples differ!") print("Object written:", a) print("Object read:", b) # Check strictly the array equality assert a == b finally: # Close the file fileh.close() class BasicTestCase(unittest.TestCase): def test00_char(self): "Data integrity during recovery (character types)" a = self.charList fname = tempfile.mktemp(".h5") try: WriteRead(fname, a) finally: os.remove(fname) def test01_types(self): "Data integrity during recovery (numerical types)" a = self.numericalList fname = tempfile.mktemp(".h5") try: WriteRead(fname, a) finally: os.remove(fname) class Basic0DOneTestCase(BasicTestCase): # Scalar case title = "Rank-0 case 1" numericalList = 3 charList = b"3" class Basic0DTwoTestCase(BasicTestCase): # Scalar case title = "Rank-0 case 2" numericalList = 33.34 charList = b"33"*500 # This does not work anymore because I've splitted the chunked arrays to happen # mainly in EArray objects # class Basic1DZeroTestCase(BasicTestCase): # title = "Rank-1 case 0" # numericalList = [] # charList = [] class Basic1DOneTestCase(BasicTestCase): # 1D case title = "Rank-1 case 1" numericalList = [3] charList = [b"a"] class Basic1DTwoTestCase(BasicTestCase): # 1D case title = "Rank-1 case 2" numericalList = [3.2, 4.2] charList = [b"aaa"] class Basic2DTestCase(BasicTestCase): # 2D case title = "Rank-2 case 1" numericalList = [[1, 2]]*5 charList = [[b"qq", b"zz"]]*5 class Basic10DTestCase(BasicTestCase): # 10D case title = "Rank-10 case 1" numericalList = [[[[[[[[[[1, 2], [3, 4]]]]]]]]]]*5 # Dimensions greather than 6 in strings gives some warnings charList = [[[[[[[[[[b"a", b"b"], [b"qq", b"zz"]]]]]]]]]]*5 class ExceptionTestCase(unittest.TestCase): def test00_char(self): "Non suppported lists objects (character objects)" if common.verbose: print('\n', '-=' * 30) print("Running test for %s" % (self.title)) a = self.charList try: fname = tempfile.mktemp(".h5") try: WriteRead(fname, a) finally: os.remove(fname) except ValueError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next error was catched!") print(type, ":", value) else: self.fail("expected a ValueError") def test01_types(self): "Non supported lists object (numerical types)" a = self.numericalList try: fname = tempfile.mktemp(".h5") try: WriteRead(fname, a) finally: os.remove(fname) except ValueError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next was catched!") print(value) else: self.fail("expected an ValueError") class Basic1DFourTestCase(ExceptionTestCase): title = "Rank-1 case 4 (non-regular list)" numericalList = [3, [4, 5.2]] charList = [b"aaa", [b"bbb", b"ccc"]] class GetItemTestCase(unittest.TestCase): def test00_single(self): "Single element access (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charList arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") # Get and compare an element if common.verbose: print("Original first element:", a[0]) print("Read first element:", arr[0]) self.assertEqual(a[0], arr[0]) # Close the file fileh.close() # Then, delete the file os.remove(file) def test01_single(self): "Single element access (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalList arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") # Get and compare an element if common.verbose: print("Original first element:", a[0]) print("Read first element:", arr[0]) self.assertEqual(a[0], arr[0]) # Close the file fileh.close() # Then, delete the file os.remove(file) def test02_range(self): "Range element access (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") # Get and compare an element if common.verbose: print("Original elements:", a[1:4]) print("Read elements:", arr[1:4]) self.assertEqual(a[1:4], arr[1:4]) # Close the file fileh.close() # Then, delete the file os.remove(file) def test03_range(self): "Range element access (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") # Get and compare an element if common.verbose: print("Original elements:", a[1:4]) print("Read elements:", arr[1:4]) self.assertEqual(a[1:4], arr[1:4]) # Close the file fileh.close() # Then, delete the file os.remove(file) def test04_range(self): "Range element access, strided (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") # Get and compare an element if common.verbose: print("Original elements:", a[1:4:2]) print("Read elements:", arr[1:4:2]) self.assertEqual(a[1:4:2], arr[1:4:2]) # Close the file fileh.close() # Then, delete the file os.remove(file) def test05_range(self): "Range element access (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") # Get and compare an element if common.verbose: print("Original elements:", a[1:4:2]) print("Read elements:", arr[1:4:2]) self.assertEqual(a[1:4:2], arr[1:4:2]) # Close the file fileh.close() # Then, delete the file os.remove(file) def test06_negativeIndex(self): "Negative Index element access (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") # Get and compare an element if common.verbose: print("Original last element:", a[-1]) print("Read last element:", arr[-1]) self.assertEqual(a[-1], arr[-1]) # Close the file fileh.close() # Then, delete the file os.remove(file) def test07_negativeIndex(self): "Negative Index element access (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") # Get and compare an element if common.verbose: print("Original before last element:", a[-2]) print("Read before last element:", arr[-2]) self.assertEqual(a[-2], arr[-2]) # Close the file fileh.close() # Then, delete the file os.remove(file) def test08_negativeRange(self): "Negative range element access (character types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") # Get and compare an element if common.verbose: print("Original last elements:", a[-4:-1]) print("Read last elements:", arr[-4:-1]) self.assertEqual(a[-4:-1], arr[-4:-1]) # Close the file fileh.close() # Then, delete the file os.remove(file) def test09_negativeRange(self): "Negative range element access (numerical types)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") # Get and compare an element if common.verbose: print("Original last elements:", a[-4:-1]) print("Read last elements:", arr[-4:-1]) self.assertEqual(a[-4:-1], arr[-4:-1]) # Close the file fileh.close() # Then, delete the file os.remove(file) class GI1ListTestCase(GetItemTestCase): title = "Rank-1 case 1 (lists)" numericalList = [3] numericalListME = [3, 2, 1, 0, 4, 5, 6] charList = [b"3"] charListME = [b"321", b"221", b"121", b"021", b"421", b"521", b"621"] class GI2ListTestCase(GetItemTestCase): # A more complex example title = "Rank-1,2 case 2 (lists)" numericalList = [3, 4] numericalListME = [[3, 2, 1, 0, 4, 5, 6], [2, 1, 0, 4, 5, 6, 7], [4, 3, 2, 1, 0, 4, 5], [3, 2, 1, 0, 4, 5, 6], [3, 2, 1, 0, 4, 5, 6]] charList = [b"a", b"b"] charListME = [ [b"321", b"221", b"121", b"021", b"421", b"521", b"621"], [b"21", b"21", b"11", b"02", b"42", b"21", b"61"], [b"31", b"21", b"12", b"21", b"41", b"51", b"621"], [b"321", b"221", b"121", b"021", b"421", b"521", b"621"], [b"3241", b"2321", b"13216", b"0621", b"4421", b"5421", b"a621"], [b"a321", b"s221", b"d121", b"g021", b"b421", b"5vvv21", b"6zxzxs21"], ] class GeneratorTestCase(unittest.TestCase): def test00a_single(self): "Testing generator access to Arrays, single elements (char)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charList arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") # Get and compare an element ga = [i for i in a] garr = [i for i in arr] if common.verbose: print("Result of original iterator:", ga) print("Result of read generator:", garr) self.assertEqual(ga, garr) # Close the file fileh.close() # Then, delete the file os.remove(file) def test00b_me(self): "Testing generator access to Arrays, multiple elements (char)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.charListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") # Get and compare an element if isinstance(a[0], tuple): ga = [list(i) for i in a] else: ga = [i for i in a] garr = [i for i in arr] if common.verbose: print("Result of original iterator:", ga) print("Result of read generator:", garr) self.assertEqual(ga, garr) # Close the file fileh.close() # Then, delete the file os.remove(file) def test01a_single(self): "Testing generator access to Arrays, single elements (numeric)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalList arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") # Get and compare an element ga = [i for i in a] garr = [i for i in arr] if common.verbose: print("Result of original iterator:", ga) print("Result of read generator:", garr) self.assertEqual(ga, garr) # Close the file fileh.close() # Then, delete the file os.remove(file) def test01b_me(self): "Testing generator access to Arrays, multiple elements (numeric)" file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Create the array under root and name 'somearray' a = self.numericalListME arr = fileh.create_array(fileh.root, 'somearray', a, "Some array") # Get and compare an element if isinstance(a[0], tuple): ga = [list(i) for i in a] else: ga = [i for i in a] garr = [i for i in arr] if common.verbose: print("Result of original iterator:", ga) print("Result of read generator:", garr) self.assertEqual(ga, garr) # Close the file fileh.close() # Then, delete the file os.remove(file) class GE1ListTestCase(GeneratorTestCase): # Scalar case title = "Rank-1 case 1 (lists)" numericalList = [3] numericalListME = [3, 2, 1, 0, 4, 5, 6] charList = [b"3"] charListME = [b"321", b"221", b"121", b"021", b"421", b"521", b"621"] class GE2ListTestCase(GeneratorTestCase): # Scalar case title = "Rank-1,2 case 2 (lists)" numericalList = [3, 4] numericalListME = [[3, 2, 1, 0, 4, 5, 6], [2, 1, 0, 4, 5, 6, 7], [4, 3, 2, 1, 0, 4, 5], [3, 2, 1, 0, 4, 5, 6], [3, 2, 1, 0, 4, 5, 6]] charList = [b"a", b"b"] charListME = [ [b"321", b"221", b"121", b"021", b"421", b"521", b"621"], [b"21", b"21", b"11", b"02", b"42", b"21", b"61"], [b"31", b"21", b"12", b"21", b"41", b"51", b"621"], [b"321", b"221", b"121", b"021", b"421", b"521", b"621"], [b"3241", b"2321", b"13216", b"0621", b"4421", b"5421", b"a621"], [b"a321", b"s221", b"d121", b"g021", b"b421", b"5vvv21", b"6zxzxs21"], ] def suite(): theSuite = unittest.TestSuite() niter = 1 for i in range(niter): theSuite.addTest(unittest.makeSuite(Basic0DOneTestCase)) theSuite.addTest(unittest.makeSuite(Basic0DTwoTestCase)) # theSuite.addTest(unittest.makeSuite(Basic1DZeroTestCase)) theSuite.addTest(unittest.makeSuite(Basic1DOneTestCase)) theSuite.addTest(unittest.makeSuite(Basic1DTwoTestCase)) theSuite.addTest(unittest.makeSuite(Basic1DFourTestCase)) theSuite.addTest(unittest.makeSuite(Basic2DTestCase)) theSuite.addTest(unittest.makeSuite(Basic10DTestCase)) theSuite.addTest(unittest.makeSuite(GI1ListTestCase)) theSuite.addTest(unittest.makeSuite(GI2ListTestCase)) theSuite.addTest(unittest.makeSuite(GE1ListTestCase)) theSuite.addTest(unittest.makeSuite(GE2ListTestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/test_nestedtypes.py000066400000000000000000001504471231437614300221160ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: 2005-05-18 # Author: Francesc Alted - faltet@pytables.org # Author: Ivan Vilata - ivan@selidor.net # # $Id$ # ######################################################################## """Test module for nested types under PyTables.""" from __future__ import print_function import sys import unittest import itertools import numpy import tables as t from tables.utils import SizeType from tables.tests import common from tables.description import Description minRowIndex = 10 # This is the structure of the table used for testing (DON'T PANIC!): # # +-+---------------------------------+-----+----------+-+-+ # |x|Info |color|info |y|z| # | +-----+--+----------------+----+--+ +----+-----+ | | # | |value|y2|Info2 |name|z2| |Name|Value| | | # | | | +----+-----+--+--+ | | | | | | | # | | | |name|value|y3|z3| | | | | | | | # +-+-----+--+----+-----+--+--+----+--+-----+----+-----+-+-+ # # Please note that some fields are explicitly ordered while others are # ordered alphabetically by name. # The declaration of the nested table: class Info(t.IsDescription): _v_pos = 3 Name = t.StringCol(itemsize=2) Value = t.ComplexCol(itemsize=16) class TestTDescr(t.IsDescription): """A description that has several nested columns.""" x = t.Int32Col(dflt=0, shape=2, pos=0) # 0 y = t.Float64Col(dflt=1, shape=(2, 2)) z = t.UInt8Col(dflt=1) color = t.StringCol(itemsize=2, dflt=b" ", pos=2) info = Info() class Info(t.IsDescription): # 1 _v_pos = 1 name = t.StringCol(itemsize=2) value = t.ComplexCol(itemsize=16, pos=0) # 0 y2 = t.Float64Col(dflt=1, pos=1) # 1 z2 = t.UInt8Col(dflt=1) class Info2(t.IsDescription): y3 = t.Time64Col(dflt=1, shape=2) z3 = t.EnumCol({'r': 4, 'g': 2, 'b': 1}, 'r', 'int32', shape=2) name = t.StringCol(itemsize=2) value = t.ComplexCol(itemsize=16, shape=2) # The corresponding nested array description: testADescr = [ ('x', '(2,)Int32'), ('Info', [ ('value', 'Complex64'), ('y2', 'Float64'), ('Info2', [ ('name', 'a2'), ('value', '(2,)Complex64'), ('y3', '(2,)Float64'), ('z3', '(2,)Int32')]), ('name', 'a2'), ('z2', 'UInt8')]), ('color', 'a2'), ('info', [ ('Name', 'a2'), ('Value', 'Complex64')]), ('y', '(2,2)Float64'), ('z', 'UInt8')] # The corresponding nested array description (brief version): testADescr2 = [ ('x', '(2,)i4'), ('Info', [ ('value', '()c16'), ('y2', '()f8'), ('Info2', [ ('name', '()S2'), ('value', '(2,)c16'), ('y3', '(2,)f8'), ('z3', '(2,)i4')]), ('name', '()S2'), ('z2', '()u1')]), ('color', '()S2'), ('info', [ ('Name', '()S2'), ('Value', '()c16')]), ('y', '(2, 2)f8'), ('z', '()u1')] # A nested array for testing: testABuffer = [ # x Info color info y z # value y2 Info2 name z2 Name Value # name value y3 z3 ((3, 2), (6j, 6., ('nn', (6j, 4j), (6., 4.), (1, 2)), 'NN', 8), 'cc', ('NN', 6j), ((6., 4.), (6., 4.)), 8), ((4, 3), (7j, 7., ('oo', (7j, 5j), (7., 5.), (2, 1)), 'OO', 9), 'dd', ('OO', 7j), ((7., 5.), (7., 5.)), 9), ] testAData = numpy.array(testABuffer, dtype=testADescr) # The name of the column to be searched: testCondCol = 'Info/z2' # The name of a nested column (it can not be searched): testNestedCol = 'Info' # The condition to be applied on the column (all but the last row match it): testCondition = '(2 < col) & (col < 9)' def areDescriptionsEqual(desc1, desc2): """Are both `desc1` and `desc2` equivalent descriptions? The arguments may be description objects (``IsDescription``, ``Description``) or dictionaries. """ if isinstance(desc1, t.Col): # This is a rough comparison but it suffices here. return (desc1.type == desc2.type and desc2.dtype == desc2.dtype and desc1._v_pos == desc2._v_pos # and desc1.dflt == desc2.dflt) and common.areArraysEqual(desc1.dflt, desc2.dflt)) if hasattr(desc1, '_v_colobjects'): # quacks like a Description cols1 = desc1._v_colobjects elif hasattr(desc1, 'columns'): # quacks like an IsDescription cols1 = desc1.columns else: # hope it quacks like a dictionary cols1 = desc1 if hasattr(desc2, '_v_colobjects'): # quacks like a Description cols2 = desc2._v_colobjects elif hasattr(desc2, 'columns'): # quacks like an IsDescription cols2 = desc2.columns else: # hope it quacks like a dictionary cols2 = desc2 if len(cols1) != len(cols2): return False for (colName, colobj1) in cols1.iteritems(): colobj2 = cols2[colName] if colName == '_v_pos': # The comparison may not be quite exhaustive! return colobj1 == colobj2 if not areDescriptionsEqual(colobj1, colobj2): return False return True # Test creating nested column descriptions class DescriptionTestCase(common.PyTablesTestCase): _TestTDescr = TestTDescr _testADescr = testADescr _testADescr2 = testADescr2 _testAData = testAData def test00_instance(self): """Creating an instance of a nested description.""" self.assertTrue( areDescriptionsEqual(self._TestTDescr, self._TestTDescr()), "Table description does not match the given one.") def test01_instance(self): """Checking attrs of an instance of a nested description.""" descr = Description(self._TestTDescr().columns) if common.verbose: print("Generated description:", descr._v_nested_descr) print("Should look like:", self._testADescr2) self.assertEqual(self._testADescr2, descr._v_nested_descr, "Description._v_nested_descr does not match.") # Test creating a nested table and opening it class CreateTestCase(common.TempFileMixin, common.PyTablesTestCase): _TestTDescr = TestTDescr _testABuffer = testABuffer _testAData = testAData def _checkColumns(self, cols, desc): """Check that `cols` has all the accessors for `self._TestTDescr`.""" # ``_desc`` is a leaf column and ``cols`` a ``Column``. if isinstance(desc, t.Col): return isinstance(cols, t.Column) # ``_desc`` is a description object and ``cols`` a ``Cols``. descColumns = desc._v_colobjects for colName in descColumns: if colName not in cols._v_colnames: return False if not self._checkColumns(cols._f_col(colName), descColumns[colName]): return False return True def _checkDescription(self, table): """Check that description of `table` matches `self._TestTDescr`.""" # Compare descriptions. self.assertTrue( areDescriptionsEqual(self._TestTDescr, table.description), "Table description does not match the given one.") # Check access to columns. self._checkColumns(table.cols, table.description) def _checkColinstances(self, table): """Check that ``colinstances`` and ``cols`` of `table` match.""" for colpathname in table.description._v_pathnames: self.assertTrue(table.colinstances[colpathname] is table.cols._f_col(colpathname)) def test00_create(self): """Creating a nested table.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) self._checkDescription(tbl) self._checkColinstances(tbl) def test01_open(self): """Opening a nested table.""" self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) self._reopen() tbl = self.h5file.root.test self._checkDescription(tbl) self._checkColinstances(tbl) def test02_NestedRecArrayCompat(self): """Creating a compatible nested record array``.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) nrarr = numpy.array(testABuffer, dtype=tbl.description._v_nested_descr) self.assertTrue(common.areArraysEqual(nrarr, self._testAData), "Can not create a compatible structured array.") def test03_NRA(self): """Creating a table from a nested record array object.""" tbl = self.h5file.create_table( '/', 'test', self._testAData, title=self._getMethodName()) tbl.flush() readAData = tbl.read() if common.verbose: print("Read data:", readAData) print("Should look like:", self._testAData) self.assertTrue(common.areArraysEqual(self._testAData, readAData), "Written and read values differ.") def test04_NRA2(self): """Creating a table from a generated nested record array object.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) readAData = tbl.read() tbl2 = self.h5file.create_table( '/', 'test2', readAData, title=self._getMethodName()) readAData2 = tbl2.read() self.assertTrue(common.areArraysEqual(self._testAData, readAData2), "Written and read values differ.") # Test writing data in a nested table class WriteTestCase(common.TempFileMixin, common.PyTablesTestCase): _TestTDescr = TestTDescr _testAData = testAData _testCondition = testCondition _testCondCol = testCondCol _testNestedCol = testNestedCol def _testCondVars(self, table): """Get condition variables for the given `table`.""" return {'col': table.cols._f_col(self._testCondCol)} def _testNestedCondVars(self, table): """Get condition variables for the given `table`.""" return {'col': table.cols._f_col(self._testNestedCol)} def _appendRow(self, row, index): """ Append the `index`-th row in `self._testAData` to `row`. Values are set field-by-field (be it nested or not). """ record = self._testAData[index] for fieldName in self._testAData.dtype.names: row[fieldName] = record[fieldName] row.append() def test00_append(self): """Appending a set of rows.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) tbl.flush() if self.reopen: self._reopen() tbl = self.h5file.root.test readAData = tbl.read() self.assertTrue(common.areArraysEqual(self._testAData, readAData), "Written and read values differ.") def test01_row(self): """Appending individual rows.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) row = tbl.row # Add the first row self._appendRow(row, 0) # Add the rest of the rows field by field. for i in range(1, len(self._testAData)): self._appendRow(row, i) tbl.flush() if self.reopen: self._reopen() tbl = self.h5file.root.test readAData = tbl.read() self.assertTrue(common.areArraysEqual(self._testAData, readAData), "Written and read values differ.") def test02_where(self): """Searching nested data.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) tbl.flush() if self.reopen: self._reopen() tbl = self.h5file.root.test searchedCoords = tbl.get_where_list( self._testCondition, self._testCondVars(tbl)) # All but the last row match the condition. searchedCoords.sort() self.assertEqual(searchedCoords.tolist(), range(len(self._testAData) - 1), "Search returned incorrect results.") def test02b_whereAppend(self): """Searching nested data and appending it to another table.""" tbl1 = self.h5file.create_table( '/', 'test1', self._TestTDescr, title=self._getMethodName()) tbl1.append(self._testAData) tbl1.flush() tbl2 = self.h5file.create_table( '/', 'test2', self._TestTDescr, title=self._getMethodName()) tbl1.append_where( tbl2, self._testCondition, self._testCondVars(tbl1)) if self.reopen: self._reopen() tbl1 = self.h5file.root.test1 tbl2 = self.h5file.root.test2 searchedCoords = tbl2.get_where_list( self._testCondition, self._testCondVars(tbl2)) # All but the last row match the condition. searchedCoords.sort() self.assertEqual(searchedCoords.tolist(), range(len(self._testAData) - 1), "Search returned incorrect results.") def test03_colscond(self): """Searching on a column with nested columns.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) tbl.flush() if self.reopen: self._reopen() tbl = self.h5file.root.test self.assertRaises( TypeError, tbl.get_where_list, self._testCondition, self._testNestedCondVars(tbl)) def test04_modifyColumn(self): """Modifying one single nested column (modify_column).""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) tbl.flush() nColumn = self._testNestedCol # Get the nested column data and swap the first and last rows. raTable = self._testAData.copy() raColumn = raTable[nColumn] # The next will not work until NestedRecords supports copies (raColumn[0], raColumn[-1]) = (raColumn[-1], raColumn[0]) # Write the resulting column and re-read the whole table. tbl.modify_column(colname=nColumn, column=raColumn) tbl.flush() if self.reopen: self._reopen() tbl = self.h5file.root.test raReadTable = tbl.read() if common.verbose: print("Table read:", raReadTable) print("Should look like:", raTable) # Compare it to the written one. self.assertTrue(common.areArraysEqual(raTable, raReadTable), "Written and read values differ.") def test05a_modifyColumns(self): """Modifying one nested column (modify_columns).""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) tbl.flush() nColumn = self._testNestedCol # Get the nested column data and swap the first and last rows. raTable = self._testAData.copy() raColumn = raTable[nColumn] (raColumn[0], raColumn[-1]) = (raColumn[-1].copy(), raColumn[0].copy()) newdtype = numpy.dtype([(nColumn, raTable.dtype.fields[nColumn][0])]) self.assertTrue(newdtype is not None) # Write the resulting column and re-read the whole table. tbl.modify_columns(names=[nColumn], columns=raColumn) tbl.flush() if self.reopen: self._reopen() tbl = self.h5file.root.test raReadTable = tbl.read() if common.verbose: print("Table read:", raReadTable) print("Should look like:", raTable) # Compare it to the written one. self.assertTrue(common.areArraysEqual(raTable, raReadTable), "Written and read values differ.") def test05b_modifyColumns(self): """Modifying two nested columns (modify_columns).""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) tbl.flush() # Get the nested column data and swap the first and last rows. colnames = ['x', 'color'] # Get the first two columns raCols = numpy.rec.fromarrays([ self._testAData['x'].copy(), self._testAData['color'].copy()], dtype=[('x', '(2,)i4'), ('color', '1a2')]) # descr=tbl.description._v_nested_descr[0:2]) # or... # names=tbl.description._v_nested_names[0:2], # formats=tbl.description._v_nested_formats[0:2]) (raCols[0], raCols[-1]) = (raCols[-1].copy(), raCols[0].copy()) # Write the resulting columns tbl.modify_columns(names=colnames, columns=raCols) tbl.flush() if self.reopen: self._reopen() tbl = self.h5file.root.test # Re-read the appropriate columns raCols2 = numpy.rec.fromarrays([tbl.cols._f_col('x'), tbl.cols._f_col('color')], dtype=raCols.dtype) if common.verbose: print("Table read:", raCols2) print("Should look like:", raCols) # Compare it to the written one. self.assertTrue(common.areArraysEqual(raCols, raCols2), "Written and read values differ.") def test06_modifyRows(self): "Checking modifying several rows at once (using nested rec array)" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) tbl.flush() # Get the nested record and swap the first and last rows. raTable = self._testAData.copy() (raTable[0], raTable[-1]) = (raTable[-1].copy(), raTable[0].copy()) # Write the resulting nested record and re-read the whole table. tbl.modify_rows(start=0, stop=2, rows=raTable) tbl.flush() if self.reopen: self._reopen() tbl = self.h5file.root.test raReadTable = tbl.read() if common.verbose: print("Table read:", raReadTable) print("Should look like:", raTable) # Compare it to the written one. self.assertTrue(common.areArraysEqual(raTable, raReadTable), "Written and read values differ.") def test07_index(self): """Checking indexes of nested columns.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName(), expectedrows=minRowIndex * 2) for i in range(minRowIndex): tbl.append(self._testAData) tbl.flush() coltoindex = tbl.cols._f_col(self._testCondCol) indexrows = coltoindex.create_index() self.assertTrue(indexrows is not None) if self.reopen: self._reopen() tbl = self.h5file.root.test coltoindex = tbl.cols._f_col(self._testCondCol) if common.verbose: print("Number of written rows:", tbl.nrows) print("Number of indexed rows:", coltoindex.index.nelements) # Check indexing flags: self.assertEqual(tbl.indexed, True, "Table not indexed") self.assertNotEqual(coltoindex.index, None, "Column not indexed") self.assertTrue(tbl.colindexed[ self._testCondCol], "Column not indexed") # Do a look-up for values searchedCoords = tbl.get_where_list( self._testCondition, self._testCondVars(tbl)) searchedCoords.sort() expectedCoords = numpy.arange(0, minRowIndex * 2, 2, SizeType) if common.verbose: print("Searched coords:", searchedCoords) print("Expected coords:", expectedCoords) # All even rows match the condition. self.assertEqual(searchedCoords.tolist(), expectedCoords.tolist(), "Search returned incorrect results.") def test08_setNestedField(self): "Checking modifying a nested field via natural naming." # See ticket #93 (http://www.pytables.org/trac/ticket/93). tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) tbl.flush() oldvalue = tbl.cols.Info.z2[0] tbl.cols.Info.z2[0] = oldvalue + 1 tbl.flush() if self.reopen: self._reopen() tbl = self.h5file.root.test newvalue = tbl.cols.Info.z2[0] self.assertEqual(newvalue, oldvalue + 1) class WriteNoReopen(WriteTestCase): reopen = 0 class WriteReopen(WriteTestCase): reopen = 1 class ReadTestCase(common.TempFileMixin, common.PyTablesTestCase): _TestTDescr = TestTDescr _testABuffer = testABuffer _testAData = testAData _testNestedCol = testNestedCol def test00a_repr(self): """Checking representation of a nested Table.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title="test00") tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test if common.verbose: print("str(tbl)-->", str(tbl)) print("repr(tbl)-->", repr(tbl)) self.assertEqual(str(tbl), "/test (Table(2,)) 'test00'") tblrepr = repr(tbl) # Remove the platform-dependent information (i.e. byteorder) tblrepr = "\n".join(tblrepr.split("\n")[:-2]) + "\n" if sys.version_info[0] < 3: template = """/test (Table(2,)) 'test00' description := { "x": Int32Col(shape=(2,), dflt=0, pos=0), "Info": { "value": ComplexCol(itemsize=16, shape=(), dflt=0j, pos=0), "y2": Float64Col(shape=(), dflt=1.0, pos=1), "Info2": { "name": StringCol(itemsize=2, shape=(), dflt='', pos=0), "value": ComplexCol(itemsize=16, shape=(2,), dflt=0j, pos=1), "y3": Time64Col(shape=(2,), dflt=1.0, pos=2), "z3": EnumCol(enum=Enum({%(value)s}), dflt='r', base=Int32Atom(shape=(), dflt=0), shape=(2,), pos=3)}, "name": StringCol(itemsize=2, shape=(), dflt='', pos=3), "z2": UInt8Col(shape=(), dflt=1, pos=4)}, "color": StringCol(itemsize=2, shape=(), dflt=' ', pos=2), "info": { "Name": StringCol(itemsize=2, shape=(), dflt='', pos=0), "Value": ComplexCol(itemsize=16, shape=(), dflt=0j, pos=1)}, "y": Float64Col(shape=(2, 2), dflt=1.0, pos=4), "z": UInt8Col(shape=(), dflt=1, pos=5)} """ else: template = """/test (Table(2,)) 'test00' description := { "x": Int32Col(shape=(2,), dflt=0, pos=0), "Info": { "value": ComplexCol(itemsize=16, shape=(), dflt=0j, pos=0), "y2": Float64Col(shape=(), dflt=1.0, pos=1), "Info2": { "name": StringCol(itemsize=2, shape=(), dflt=b'', pos=0), "value": ComplexCol(itemsize=16, shape=(2,), dflt=0j, pos=1), "y3": Time64Col(shape=(2,), dflt=1.0, pos=2), "z3": EnumCol(enum=Enum({%(value)s}), dflt='%(default)s', base=Int32Atom(shape=(), dflt=0), shape=(2,), pos=3)}, "name": StringCol(itemsize=2, shape=(), dflt=b'', pos=3), "z2": UInt8Col(shape=(), dflt=1, pos=4)}, "color": StringCol(itemsize=2, shape=(), dflt=b' ', pos=2), "info": { "Name": StringCol(itemsize=2, shape=(), dflt=b'', pos=0), "Value": ComplexCol(itemsize=16, shape=(), dflt=0j, pos=1)}, "y": Float64Col(shape=(2, 2), dflt=1.0, pos=4), "z": UInt8Col(shape=(), dflt=1, pos=5)} """ # The problem here is that the order in which items are stored in a # dict can't be assumed to be stable. # From python 3.3 on it is actually no more stable since the # "Hash randomization" feature is enable by default. # # For this reason we generate a representation string for each of the # prmutations of the Enum items. # # Also the default value of enum types is not preserved in HDF5. # It is assumed that the default value is the first one in the array # of Enum names and hence it is also affected by the issue related to # the "Hash randomization" feature. # # Also in this case it is genereted a representation string for each # of the possible default values. enums = [ ', '.join(items) for items in itertools.permutations( ("'r': 4", "'b': 1", "'g': 2")) ] defaults = ('r', 'b', 'g') values = [ template % {'value': v, 'default': d} for v, d in itertools.product(enums, defaults) ] self.assertTrue(tblrepr in values) def test00b_repr(self): """Checking representation of a root Column.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title="test00") tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test if common.verbose: print("str(tbl.cols.y)-->'%s'" % str(tbl.cols.y)) print("repr(tbl.cols.y)-->'%s'" % repr(tbl.cols.y)) self.assertEqual(str(tbl.cols.y), "/test.cols.y (Column(2, 2, 2), float64, idx=None)") self.assertEqual(repr(tbl.cols.y), "/test.cols.y (Column(2, 2, 2), float64, idx=None)") def test00c_repr(self): """Checking representation of a nested Column.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title="test00") tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test if common.verbose: print("str(tbl.cols.Info.z2)-->'%s'" % str(tbl.cols.Info.z2)) print("repr(tbl.cols.Info.z2)-->'%s'" % repr(tbl.cols.Info.z2)) self.assertEqual(str(tbl.cols.Info.z2), "/test.cols.Info.z2 (Column(2,), uint8, idx=None)") self.assertEqual(repr(tbl.cols.Info.z2), "/test.cols.Info.z2 (Column(2,), uint8, idx=None)") def test01_read(self): """Checking Table.read with subgroups with a range index with step.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test nrarr = numpy.rec.array(testABuffer, dtype=tbl.description._v_nested_descr) tblcols = tbl.read(start=0, step=2, field='Info') nrarrcols = nrarr['Info'][0::2] if common.verbose: print("Read cols:", tblcols) print("Should look like:", nrarrcols) self.assertTrue(common.areArraysEqual(nrarrcols, tblcols), "Original array are retrieved doesn't match.") def test01_read_out_arg(self): tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test nrarr = numpy.rec.array(testABuffer, dtype=tbl.description._v_nested_descr) # When reading an entire nested column, the output array must contain # all fields in the table. The output buffer will contain the contents # of all fields. The selected column alone will be returned from the # method call. all_cols = numpy.empty(1, tbl.dtype) tblcols = tbl.read(start=0, step=2, field='Info', out=all_cols) nrarrcols = nrarr['Info'][0::2] if common.verbose: print("Read cols:", tblcols) print("Should look like:", nrarrcols) self.assertTrue(common.areArraysEqual(nrarrcols, tblcols), "Original array are retrieved doesn't match.") self.assertTrue(common.areArraysEqual(nrarr[0::2], all_cols), "Output buffer does not match full table.") def test02_read(self): """Checking Table.read with a nested Column.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test tblcols = tbl.read(start=0, step=2, field='Info/value') nrarr = numpy.rec.array(testABuffer, dtype=tbl.description._v_nested_descr) nrarrcols = nrarr['Info']['value'][0::2] self.assertTrue(common.areArraysEqual(nrarrcols, tblcols), "Original array are retrieved doesn't match.") def test02_read_out_arg(self): """Checking Table.read with a nested Column.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test tblcols = numpy.empty(1, dtype='c16') tbl.read(start=0, step=2, field='Info/value', out=tblcols) nrarr = numpy.rec.array(testABuffer, dtype=tbl.description._v_nested_descr) nrarrcols = nrarr['Info']['value'][0::2] self.assertTrue(common.areArraysEqual(nrarrcols, tblcols), "Original array are retrieved doesn't match.") class ReadNoReopen(ReadTestCase): reopen = 0 class ReadReopen(ReadTestCase): reopen = 1 # Checking the Table.Cols accessor class ColsTestCase(common.TempFileMixin, common.PyTablesTestCase): _TestTDescr = TestTDescr _testABuffer = testABuffer _testAData = testAData _testNestedCol = testNestedCol def test00a_repr(self): """Checking string representation of Cols.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title="test00") if self.reopen: self._reopen() tbl = self.h5file.root.test if common.verbose: print("str(tbl.cols)-->", str(tbl.cols)) print("repr(tbl.cols)-->", repr(tbl.cols)) self.assertEqual(str(tbl.cols), "/test.cols (Cols), 6 columns") try: self.assertEqual(repr(tbl.cols), """/test.cols (Cols), 6 columns x (Column(0, 2), ('int32',(2,))) Info (Cols(), Description) color (Column(0,), |S2) info (Cols(), Description) y (Column(0, 2, 2), ('float64',(2, 2))) z (Column(0,), uint8) """ ) except AssertionError: self.assertEqual(repr(tbl.cols), """/test.cols (Cols), 6 columns x (Column(0, 2), ('%s', (2,))) Info (Cols(), Description) color (Column(0,), |S2) info (Cols(), Description) y (Column(0, 2, 2), ('%s', (2, 2))) z (Column(0,), uint8) """ % (numpy.int32(0).dtype.str, numpy.float64(0).dtype.str)) def test00b_repr(self): """Checking string representation of nested Cols.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) if self.reopen: self._reopen() tbl = self.h5file.root.test if common.verbose: print("str(tbl.cols.Info)-->", str(tbl.cols.Info)) print("repr(tbl.cols.Info)-->", repr(tbl.cols.Info)) self.assertEqual(str( tbl.cols.Info), "/test.cols.Info (Cols), 5 columns") self.assertEqual(repr(tbl.cols.Info), """/test.cols.Info (Cols), 5 columns value (Column(0,), complex128) y2 (Column(0,), float64) Info2 (Cols(), Description) name (Column(0,), |S2) z2 (Column(0,), uint8) """) def test01a_f_col(self): """Checking cols._f_col() with a subgroup.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) if self.reopen: self._reopen() tbl = self.h5file.root.test tblcol = tbl.cols._f_col(self._testNestedCol) if common.verbose: print("Column group name:", tblcol._v_desc._v_pathname) self.assertEqual(tblcol._v_desc._v_pathname, self._testNestedCol, "Column group name doesn't match.") def test01b_f_col(self): """Checking cols._f_col() with a column.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) if self.reopen: self._reopen() tbl = self.h5file.root.test tblcol = tbl.cols._f_col(self._testNestedCol + "/name") if common.verbose: print("Column name:", tblcol.name) self.assertEqual(tblcol.name, "name", "Column name doesn't match.") def test01c_f_col(self): """Checking cols._f_col() with a nested subgroup.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tblcol = tbl.cols._f_col(self._testNestedCol + "/Info2") if common.verbose: print("Column group name:", tblcol._v_desc._v_pathname) self.assertEqual(tblcol._v_desc._v_pathname, self._testNestedCol + "/Info2", "Column group name doesn't match.") def test02a__len__(self): """Checking cols.__len__() in root level.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) if self.reopen: self._reopen() tbl = self.h5file.root.test length = len(tbl.cols) if common.verbose: print("Column group length:", length) self.assertEqual(length, len(tbl.colnames), "Column group length doesn't match.") def test02b__len__(self): """Checking cols.__len__() in subgroup level.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) if self.reopen: self._reopen() tbl = self.h5file.root.test length = len(tbl.cols.Info) if common.verbose: print("Column group length:", length) self.assertEqual(length, len(tbl.cols.Info._v_colnames), "Column group length doesn't match.") def test03a__getitem__(self): """Checking cols.__getitem__() with a single index.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test nrarr = numpy.array(testABuffer, dtype=tbl.description._v_nested_descr) tblcols = tbl.cols[1] nrarrcols = nrarr[1] if common.verbose: print("Read cols:", tblcols) print("Should look like:", nrarrcols) self.assertTrue(common.areArraysEqual(nrarrcols, tblcols), "Original array are retrieved doesn't match.") def test03b__getitem__(self): """Checking cols.__getitem__() with a range index.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test nrarr = numpy.array(testABuffer, dtype=tbl.description._v_nested_descr) tblcols = tbl.cols[0:2] nrarrcols = nrarr[0:2] if common.verbose: print("Read cols:", tblcols) print("Should look like:", nrarrcols) self.assertTrue(common.areArraysEqual(nrarrcols, tblcols), "Original array are retrieved doesn't match.") def test03c__getitem__(self): """Checking cols.__getitem__() with a range index with step.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test nrarr = numpy.array(testABuffer, dtype=tbl.description._v_nested_descr) tblcols = tbl.cols[0::2] nrarrcols = nrarr[0::2] if common.verbose: print("Read cols:", tblcols) print("Should look like:", nrarrcols) self.assertTrue(common.areArraysEqual(nrarrcols, tblcols), "Original array are retrieved doesn't match.") def test04a__getitem__(self): """Checking cols.__getitem__() with subgroups with a single index.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test nrarr = numpy.array(testABuffer, dtype=tbl.description._v_nested_descr) tblcols = tbl.cols._f_col('Info')[1] nrarrcols = nrarr['Info'][1] if common.verbose: print("Read cols:", tblcols) print("Should look like:", nrarrcols) self.assertTrue(common.areArraysEqual(nrarrcols, tblcols), "Original array are retrieved doesn't match.") def test04b__getitem__(self): """Checking cols.__getitem__() with subgroups with a range index.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test nrarr = numpy.array(testABuffer, dtype=tbl.description._v_nested_descr) tblcols = tbl.cols._f_col('Info')[0:2] nrarrcols = nrarr['Info'][0:2] if common.verbose: print("Read cols:", tblcols) print("Should look like:", nrarrcols) self.assertTrue(common.areArraysEqual(nrarrcols, tblcols), "Original array are retrieved doesn't match.") def test04c__getitem__(self): """Checking cols.__getitem__() with subgroups with a range index with step.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test nrarr = numpy.array(testABuffer, dtype=tbl.description._v_nested_descr) tblcols = tbl.cols._f_col('Info')[0::2] nrarrcols = nrarr['Info'][0::2] if common.verbose: print("Read cols:", tblcols) print("Should look like:", nrarrcols) self.assertTrue(common.areArraysEqual(nrarrcols, tblcols), "Original array are retrieved doesn't match.") def test05a__getitem__(self): """Checking cols.__getitem__() with a column with a single index.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test nrarr = numpy.array(testABuffer, dtype=tbl.description._v_nested_descr) tblcols = tbl.cols._f_col('Info/value')[1] nrarrcols = nrarr['Info']['value'][1] if common.verbose: print("Read cols:", tblcols) print("Should look like:", nrarrcols) self.assertEqual(nrarrcols, tblcols, "Original array are retrieved doesn't match.") def test05b__getitem__(self): """Checking cols.__getitem__() with a column with a range index.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test nrarr = numpy.array(testABuffer, dtype=tbl.description._v_nested_descr) tblcols = tbl.cols._f_col('Info/value')[0:2] nrarrcols = nrarr['Info']['value'][0:2] if common.verbose: print("Read cols:", tblcols) print("Should look like:", nrarrcols) self.assertTrue(common.areArraysEqual(nrarrcols, tblcols), "Original array are retrieved doesn't match.") def test05c__getitem__(self): """Checking cols.__getitem__() with a column with a range index with step.""" tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test nrarr = numpy.array(testABuffer, dtype=tbl.description._v_nested_descr) tblcols = tbl.cols._f_col('Info/value')[0::2] nrarrcols = nrarr['Info']['value'][0::2] if common.verbose: print("Read cols:", tblcols) print("Should look like:", nrarrcols) self.assertTrue(common.areArraysEqual(nrarrcols, tblcols), "Original array are retrieved doesn't match.") def test_01a__iter__(self): tbl = self.h5file.create_table( '/', 'test', self._TestTDescr, title=self._getMethodName()) tbl.append(self._testAData) if self.reopen: self._reopen() tbl = self.h5file.root.test nrarr = numpy.array(testABuffer, dtype=tbl.description._v_nested_descr) row_num = 0 for item in tbl.cols.Info.value: self.assertEqual(item, nrarr['Info']['value'][row_num]) row_num += 1 self.assertEqual(row_num, len(nrarr)) class ColsNoReopen(ColsTestCase): reopen = 0 class ColsReopen(ColsTestCase): reopen = 1 class Nested(t.IsDescription): uid = t.IntCol(pos=1) value = t.FloatCol(pos=2) class A_Candidate(t.IsDescription): nested1 = Nested() nested2 = Nested() class B_Candidate(t.IsDescription): nested1 = Nested nested2 = Nested class C_Candidate(t.IsDescription): nested1 = Nested() nested2 = Nested Dnested = {'uid': t.IntCol(pos=1), 'value': t.FloatCol(pos=2), } D_Candidate = {"nested1": Dnested, "nested2": Dnested, } E_Candidate = {"nested1": Nested, "nested2": Dnested, } F_Candidate = {"nested1": Nested(), "nested2": Dnested, } # Checking several nested columns declared in the same way class SameNestedTestCase(common.TempFileMixin, common.PyTablesTestCase): correct_names = ['', # The root of columns 'nested1', 'nested1/uid', 'nested1/value', 'nested2', 'nested2/uid', 'nested2/value'] def test01a(self): """Checking same nested columns (instance flavor).""" tbl = self.h5file.create_table( '/', 'test', A_Candidate, title=self._getMethodName()) if self.reopen: self._reopen() tbl = self.h5file.root.test names = [col._v_pathname for col in tbl.description._f_walk( type="All")] if common.verbose: print("Pathnames of columns:", names) print("Should look like:", self.correct_names) self.assertEqual(names, self.correct_names, "Column nested names doesn't match.") def test01b(self): """Checking same nested columns (class flavor).""" tbl = self.h5file.create_table( '/', 'test', B_Candidate, title=self._getMethodName()) if self.reopen: self._reopen() tbl = self.h5file.root.test names = [col._v_pathname for col in tbl.description._f_walk( type="All")] if common.verbose: print("Pathnames of columns:", names) print("Should look like:", self.correct_names) self.assertEqual(names, self.correct_names, "Column nested names doesn't match.") def test01c(self): """Checking same nested columns (mixed instance/class flavor).""" tbl = self.h5file.create_table( '/', 'test', C_Candidate, title=self._getMethodName()) if self.reopen: self._reopen() tbl = self.h5file.root.test names = [col._v_pathname for col in tbl.description._f_walk( type="All")] if common.verbose: print("Pathnames of columns:", names) print("Should look like:", self.correct_names) self.assertEqual(names, self.correct_names, "Column nested names doesn't match.") def test01d(self): """Checking same nested columns (dictionary flavor).""" tbl = self.h5file.create_table( '/', 'test', D_Candidate, title=self._getMethodName()) if self.reopen: self._reopen() tbl = self.h5file.root.test names = [col._v_pathname for col in tbl.description._f_walk( type="All")] if common.verbose: print("Pathnames of columns:", names) print("Should look like:", self.correct_names) self.assertEqual(names, self.correct_names, "Column nested names doesn't match.") def test01e(self): """Checking same nested columns (mixed dictionary/class flavor).""" tbl = self.h5file.create_table( '/', 'test', E_Candidate, title=self._getMethodName()) if self.reopen: self._reopen() tbl = self.h5file.root.test names = [col._v_pathname for col in tbl.description._f_walk( type="All")] if common.verbose: print("Pathnames of columns:", names) print("Should look like:", self.correct_names) self.assertEqual(names, self.correct_names, "Column nested names doesn't match.") def test01f(self): """Checking same nested columns (mixed dictionary/instance flavor).""" tbl = self.h5file.create_table( '/', 'test', F_Candidate, title=self._getMethodName()) if self.reopen: self._reopen() tbl = self.h5file.root.test names = [col._v_pathname for col in tbl.description._f_walk( type="All")] if common.verbose: print("Pathnames of columns:", names) print("Should look like:", self.correct_names) self.assertEqual(names, self.correct_names, "Column nested names doesn't match.") def test02a(self): """Indexing two simple columns under the same nested column.""" desc = { 'nested': { 'i1': t.Int32Col(), 'i2': t.Int32Col() } } i1 = 'nested/i1' i2 = 'nested/i2' tbl = self.h5file.create_table( '/', 'test', desc, title=self._getMethodName()) row = tbl.row for i in xrange(1000): row[i1] = i row[i2] = i * 2 row.append() tbl.flush() cols = {'i1': tbl.cols.nested.i1, 'i2': tbl.cols.nested.i2, } cols['i1'].create_index() cols['i2'].create_index() if self.reopen: self._reopen() tbl = self.h5file.root.test # Redefine the cols dictionary cols = {'i1': tbl.cols.nested.i1, 'i2': tbl.cols.nested.i2, } i1res = [row[i1] for row in tbl.where('i1 < 10', cols)] i2res = [row[i2] for row in tbl.where('i2 < 10', cols)] if common.verbose: print("Retrieved values (i1):", i1res) print("Should look like:", range(10)) print("Retrieved values (i2):", i2res) print("Should look like:", range(0, 10, 2)) self.assertEqual(i1res, range(10), "Select for nested column (i1) doesn't match.") self.assertEqual(i2res, range(0, 10, 2), "Select for nested column (i2) doesn't match.") def test02b(self): """Indexing two simple columns under the same (very) nested column.""" desc = { 'nested1': { 'nested2': { 'nested3': { 'i1': t.Int32Col(), 'i2': t.Int32Col() } } } } i1 = 'nested1/nested2/nested3/i1' i2 = 'nested1/nested2/nested3/i2' tbl = self.h5file.create_table( '/', 'test', desc, title=self._getMethodName()) row = tbl.row for i in xrange(1000): row[i1] = i row[i2] = i * 2 row.append() tbl.flush() cols = {'i1': tbl.cols.nested1.nested2.nested3.i1, 'i2': tbl.cols.nested1.nested2.nested3.i2, } cols['i1'].create_index() cols['i2'].create_index() if self.reopen: self._reopen() tbl = self.h5file.root.test # Redefine the cols dictionary cols = {'i1': tbl.cols.nested1.nested2.nested3.i1, 'i2': tbl.cols.nested1.nested2.nested3.i2, } i1res = [row[i1] for row in tbl.where('i1 < 10', cols)] i2res = [row[i2] for row in tbl.where('i2 < 10', cols)] if common.verbose: print("Retrieved values (i1):", i1res) print("Should look like:", range(10)) print("Retrieved values (i2):", i2res) print("Should look like:", range(0, 10, 2)) self.assertEqual(i1res, range(10), "Select for nested column (i1) doesn't match.") self.assertEqual(i2res, range(0, 10, 2), "Select for nested column (i2) doesn't match.") class SameNestedNoReopen(SameNestedTestCase): reopen = 0 class SameNestedReopen(SameNestedTestCase): reopen = 1 class NestedTypesWithGaps(common.PyTablesTestCase): correct_descr = \ """{ "float": Float32Col(shape=(), dflt=0.0, pos=0), "compound": { "char": Int8Col(shape=(), dflt=0, pos=0), "double": Float64Col(shape=(), dflt=0.0, pos=1)}}""" def test01(self): """Opening a table with nested types with gaps.""" h5file = t.open_file(self._testFilename('nested-type-with-gaps.h5')) tbl = h5file.get_node('/nestedtype') type_descr = repr(tbl.description) if common.verbose: print("Type size with no gaps:", tbl.description._v_itemsize) print("And should be: 13") print("Representation of the nested type:\n", type_descr) print("And should be:\n", self.correct_descr) self.assertEqual(tbl.description._v_itemsize, 13) self.assertEqual(type_descr, self.correct_descr) if common.verbose: print("Great! Nested types with gaps recognized correctly.") h5file.close() #---------------------------------------------------------------------- def suite(): """Return a test suite consisting of all the test cases in the module.""" theSuite = unittest.TestSuite() niter = 1 # common.heavy = 1 # uncomment this only for testing purposes # theSuite.addTest(unittest.makeSuite(DescriptionTestCase)) # theSuite.addTest(unittest.makeSuite(WriteReopen)) for i in range(niter): theSuite.addTest(unittest.makeSuite(DescriptionTestCase)) theSuite.addTest(unittest.makeSuite(CreateTestCase)) theSuite.addTest(unittest.makeSuite(WriteNoReopen)) theSuite.addTest(unittest.makeSuite(WriteReopen)) theSuite.addTest(unittest.makeSuite(ColsNoReopen)) theSuite.addTest(unittest.makeSuite(ColsReopen)) theSuite.addTest(unittest.makeSuite(ReadNoReopen)) theSuite.addTest(unittest.makeSuite(ReadReopen)) theSuite.addTest(unittest.makeSuite(SameNestedNoReopen)) theSuite.addTest(unittest.makeSuite(SameNestedReopen)) theSuite.addTest(unittest.makeSuite(NestedTypesWithGaps)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/tests/test_numpy.py000066400000000000000000001465561231437614300207250ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import sys import unittest import os import tempfile import numpy from numpy import * from tables import * from tables.tests import common from tables.tests.common import allequal # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup typecodes = ['b', 'h', 'i', 'l', 'q', 'f', 'd'] # UInt64 checking disabled on win platforms # because this type is not supported if sys.platform != 'win32': typecodes += ['B', 'H', 'I', 'L', 'Q', 'F', 'D'] else: typecodes += ['B', 'H', 'I', 'L', 'F', 'D'] typecodes += ['b1'] # boolean if 'Float16Atom' in globals(): typecodes.append('e') if 'Float96Atom' in globals() or 'Float128Atom' in globals(): typecodes.append('g') if 'Complex192Atom' in globals() or 'Conplex256Atom' in globals(): typecodes.append('G') byteorder = {'little': '<', 'big': '>'}[sys.byteorder] class BasicTestCase(unittest.TestCase): """Basic test for all the supported typecodes present in NumPy. All of them are included on PyTables. """ endiancheck = 0 def WriteRead(self, testArray): if common.verbose: print('\n', '-=' * 30) print("Running test for array with typecode '%s'" % testArray.dtype.char, end=' ') print("for class check:", self.title) # Create an instance of HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w") self.root = self.fileh.root # Create the array under root and name 'somearray' a = testArray self.fileh.create_array(self.root, 'somearray', a, "Some array") # Close the file self.fileh.close() # Re-open the file in read-only mode self.fileh = open_file(self.file, mode="r") self.root = self.fileh.root # Read the saved array b = self.root.somearray.read() # For cases that read returns a python type instead of a numpy type if not hasattr(b, "shape"): b = array(b, dtype=a.dtype.str) # Compare them. They should be equal. # if not allequal(a,b, "numpy") and common.verbose: if common.verbose: print("Array written:", a) print("Array written shape:", a.shape) print("Array written itemsize:", a.itemsize) print("Array written type:", a.dtype.char) print("Array read:", b) print("Array read shape:", b.shape) print("Array read itemsize:", b.itemsize) print("Array read type:", b.dtype.char) type_ = self.root.somearray.atom.type # Check strictly the array equality self.assertEqual(type(a), type(b)) self.assertEqual(a.shape, b.shape) self.assertEqual(a.shape, self.root.somearray.shape) self.assertEqual(a.dtype, b.dtype) if a.dtype.char[0] == "S": self.assertEqual(type_, "string") else: self.assertEqual(a.dtype.base.name, type_) self.assertTrue(allequal(a, b, "numpy")) self.fileh.close() # Then, delete the file os.remove(self.file) return def test00_char(self): "Data integrity during recovery (character objects)" a = array(self.tupleChar, 'S'+str(len(self.tupleChar))) self.WriteRead(a) return def test01_char_nc(self): "Data integrity during recovery (non-contiguous character objects)" a = array(self.tupleChar, 'S'+str(len(self.tupleChar))) if a.shape == (): b = a # We cannot use the indexing notation else: b = a[::2] # Ensure that this numpy string is non-contiguous if a.shape[0] > 2: self.assertEqual(b.flags['CONTIGUOUS'], False) self.WriteRead(b) return def test02_types(self): "Data integrity during recovery (numerical types)" for typecode in typecodes: if self.tupleInt.shape: a = self.tupleInt.astype(typecode) else: # shape is the empty tuple () a = array(self.tupleInt, dtype=typecode) self.WriteRead(a) return def test03_types_nc(self): "Data integrity during recovery (non-contiguous numerical types)" for typecode in typecodes: if self.tupleInt.shape: a = self.tupleInt.astype(typecode) else: # shape is the empty tuple () a = array(self.tupleInt, dtype=typecode) # This should not be tested for the rank-0 case if len(a.shape) == 0: return b = a[::2] # Ensure that this array is non-contiguous (for non-trivial case) if a.shape[0] > 2: self.assertEqual(b.flags['CONTIGUOUS'], False) self.WriteRead(b) return class Basic0DOneTestCase(BasicTestCase): # Rank-0 case title = "Rank-0 case 1" tupleInt = array(3) tupleChar = "4" class Basic0DTwoTestCase(BasicTestCase): # Rank-0 case title = "Rank-0 case 2" tupleInt = array(33) tupleChar = "44" class Basic1DOneTestCase(BasicTestCase): # 1D case title = "Rank-1 case 1" tupleInt = array((3,)) tupleChar = ("a",) class Basic1DTwoTestCase(BasicTestCase): # 1D case title = "Rank-1 case 2" tupleInt = array((0, 4)) tupleChar = ("aaa",) class Basic1DThreeTestCase(BasicTestCase): # 1D case title = "Rank-1 case 3" tupleInt = array((3, 4, 5)) tupleChar = ("aaaa", "bbb",) class Basic2DTestCase(BasicTestCase): # 2D case title = "Rank-2 case 1" # tupleInt = reshape(array(arange((4)**2)), (4,)*2) tupleInt = ones((4,)*2) tupleChar = [["aaa", "ddddd"], ["d", "ss"], ["s", "tt"]] class Basic10DTestCase(BasicTestCase): # 10D case title = "Rank-10 case 1" # tupleInt = reshape(array(arange((2)**10)), (2,)*10) tupleInt = ones((2,)*10) # tupleChar = reshape(array([1],dtype="S1"),(1,)*10) # The next tuple consumes far more time, so this # test should be run in common.heavy mode. tupleChar = array(tupleInt, dtype="S1") # class Basic32DTestCase(BasicTestCase): # # 32D case (maximum) # tupleInt = reshape(array((22,)), (1,)*32) # # Strings seems to be very slow with somewhat large dimensions # # This should not be run unless the numarray people address this problem # # F. Alted 2006-01-04 # tupleChar = array(tupleInt, dtype="S1") class GroupsArrayTestCase(unittest.TestCase): """This test class checks combinations of arrays with groups. It also uses arrays ranks which ranges until 10. """ def test00_iterativeGroups(self): """Checking combinations of arrays with groups It also uses arrays ranks which ranges until 10. """ if common.verbose: print('\n', '-=' * 30) print("Running %s.test00_iterativeGroups..." % self.__class__.__name__) # Open a new empty HDF5 file file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") # Get the root group group = fileh.root i = 1 for typecode in typecodes: # Create an array of typecode, with incrementally bigger ranges a = ones((2,) * i, typecode) # Save it on the HDF5 file dsetname = 'array_' + typecode if common.verbose: print("Creating dataset:", group._g_join(dsetname)) fileh.create_array(group, dsetname, a, "Large array") # Create a new group group = fileh.create_group(group, 'group' + str(i)) # increment the range for next iteration i += 1 # Close the file fileh.close() # Open the previous HDF5 file in read-only mode fileh = open_file(file, mode="r") # Get the root group group = fileh.root # Get the metadata on the previosly saved arrays for i in range(1, len(typecodes)): # Create an array for later comparison a = ones((2,) * i, typecodes[i - 1]) # Get the dset object hanging from group dset = getattr(group, 'array_' + typecodes[i-1]) # Get the actual array b = dset.read() if not allequal(a, b, "numpy") and common.verbose: print("Array a original. Shape: ==>", a.shape) print("Array a original. Data: ==>", a) print("Info from dataset:", dset._v_pathname) print(" shape ==>", dset.shape, end=' ') print(" dtype ==> %s" % dset.dtype) print("Array b read from file. Shape: ==>", b.shape, end=' ') print(". Type ==> %s" % b.dtype.char) self.assertEqual(a.shape, b.shape) if dtype('l').itemsize == 4: if (a.dtype.char == "i" or a.dtype.char == "l"): # Special expection. We have no way to distinguish between # "l" and "i" typecode, and we can consider them the same # to all practical effects self.assertTrue(b.dtype.char == "l" or b.dtype.char == "i") elif (a.dtype.char == "I" or a.dtype.char == "L"): # Special expection. We have no way to distinguish between # "L" and "I" typecode, and we can consider them the same # to all practical effects self.assertTrue(b.dtype.char == "L" or b.dtype.char == "I") else: self.assertTrue(allequal(a, b, "numpy")) elif dtype('l').itemsize == 8: if (a.dtype.char == "q" or a.dtype.char == "l"): # Special expection. We have no way to distinguish between # "q" and "l" typecode in 64-bit platforms, and we can # consider them the same to all practical effects self.assertTrue(b.dtype.char == "l" or b.dtype.char == "q") elif (a.dtype.char == "Q" or a.dtype.char == "L"): # Special expection. We have no way to distinguish between # "Q" and "L" typecode in 64-bit platforms, and we can # consider them the same to all practical effects self.assertTrue(b.dtype.char == "L" or b.dtype.char == "Q") else: self.assertTrue(allequal(a, b, "numpy")) # Iterate over the next group group = getattr(group, 'group' + str(i)) # Close the file fileh.close() # Then, delete the file os.remove(file) def test01_largeRankArrays(self): """Checking creation of large rank arrays (0 < rank <= 32) It also uses arrays ranks which ranges until maxrank. """ # maximum level of recursivity (deepest group level) achieved: # maxrank = 32 (for a effective maximum rank of 32) # This limit is due to a limit in the HDF5 library. minrank = 1 maxrank = 32 if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_largeRankArrays..." % self.__class__.__name__) print("Maximum rank for tested arrays:", maxrank) # Open a new empty HDF5 file file = tempfile.mktemp(".h5") fileh = open_file(file, mode="w") group = fileh.root if common.verbose: print("Rank array writing progress: ", end=' ') for rank in range(minrank, maxrank + 1): # Create an array of integers, with incrementally bigger ranges a = ones((1,) * rank, 'i') if common.verbose: print("%3d," % (rank), end=' ') fileh.create_array(group, "array", a, "Rank: %s" % rank) group = fileh.create_group(group, 'group' + str(rank)) # Flush the buffers fileh.flush() # Close the file fileh.close() # Open the previous HDF5 file in read-only mode fileh = open_file(file, mode="r") group = fileh.root if common.verbose: print() print("Rank array reading progress: ") # Get the metadata on the previosly saved arrays for rank in range(minrank, maxrank + 1): # Create an array for later comparison a = ones((1,) * rank, 'i') # Get the actual array b = group.array.read() if common.verbose: print("%3d," % (rank), end=' ') if not a.tolist() == b.tolist() and common.verbose: print("Info from dataset:", dset._v_pathname) print(" Shape: ==>", dset.shape, end=' ') print(" typecode ==> %c" % dset.typecode) print("Array b read from file. Shape: ==>", b.shape, end=' ') print(". Type ==> %c" % b.dtype.char) self.assertEqual(a.shape, b.shape) if a.dtype.char == "i": # Special expection. We have no way to distinguish between # "l" and "i" typecode, and we can consider them the same # to all practical effects self.assertTrue(b.dtype.char == "l" or b.dtype.char == "i") else: self.assertEqual(a.dtype.char, b.dtype.char) self.assertEqual(a, b) # Iterate over the next group group = fileh.get_node(group, 'group' + str(rank)) if common.verbose: print() # This flush the stdout buffer # Close the file fileh.close() # Delete the file os.remove(file) # Test Record class class Record(IsDescription): var1 = StringCol(itemsize=4, dflt=b"abcd", pos=0) var2 = StringCol(itemsize=1, dflt=b"a", pos=1) var3 = BoolCol(dflt=1) var4 = Int8Col(dflt=1) var5 = UInt8Col(dflt=1) var6 = Int16Col(dflt=1) var7 = UInt16Col(dflt=1) var8 = Int32Col(dflt=1) var9 = UInt32Col(dflt=1) var10 = Int64Col(dflt=1) var11 = Float32Col(dflt=1.0) var12 = Float64Col(dflt=1.0) var13 = ComplexCol(itemsize=8, dflt=(1.+0.j)) var14 = ComplexCol(itemsize=16, dflt=(1.+0.j)) if 'Float16Col' in globals(): var15 = Float16Col(dflt=1.0) if 'Float96Col' in globals(): var16 = Float96Col(dflt=1.0) if 'Float128Col' in globals(): var17 = Float128Col(dflt=1.0) if 'Complex196Col' in globals(): var18 = ComplexCol(itemsize=24, dflt=(1.+0.j)) if 'Complex256Col' in globals(): var19 = ComplexCol(itemsize=32, dflt=(1.+0.j)) class TableReadTestCase(common.PyTablesTestCase): nrows = 100 def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") fileh = open_file(self.file, "w") table = fileh.create_table(fileh.root, 'table', Record) for i in range(self.nrows): table.row.append() # Fill 100 rows with default values fileh.close() self.fileh = open_file(self.file, "a") # allow flavor changes def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def test01_readTableChar(self): """Checking column conversion into NumPy in read(). Char flavor """ table = self.fileh.root.table table.flavor = "numpy" for colname in table.colnames: numcol = table.read(field=colname) typecol = table.coltypes[colname] itemsizecol = table.description._v_dtypes[colname].base.itemsize nctypecode = numcol.dtype.char if typecol == "string": if itemsizecol > 1: orignumcol = array(['abcd']*self.nrows, dtype='S4') else: orignumcol = array(['a']*self.nrows, dtype='S1') if common.verbose: print("Typecode of NumPy column read:", nctypecode) print("Should look like:", 'c') print("Itemsize of column:", itemsizecol) print("Shape of NumPy column read:", numcol.shape) print("Should look like:", orignumcol.shape) print("First 3 elements of read col:", numcol[:3]) # Check that both NumPy objects are equal self.assertTrue(allequal(numcol, orignumcol, "numpy")) def test01_readTableNum(self): """Checking column conversion into NumPy in read(). NumPy flavor """ table = self.fileh.root.table table.flavor = "numpy" for colname in table.colnames: numcol = table.read(field=colname) typecol = table.coltypes[colname] nctypecode = typeNA[numcol.dtype.char[0]] if typecol != "string": if common.verbose: print("Typecode of NumPy column read:", nctypecode) print("Should look like:", typecol) orignumcol = ones(shape=self.nrows, dtype=numcol.dtype.char) # Check that both NumPy objects are equal self.assertTrue(allequal(numcol, orignumcol, "numpy")) def test02_readCoordsChar(self): """Column conversion into NumPy in readCoords(). Chars """ table = self.fileh.root.table table.flavor = "numpy" coords = [1, 2, 3] self.nrows = len(coords) for colname in table.colnames: numcol = table.read_coordinates(coords, field=colname) typecol = table.coltypes[colname] itemsizecol = table.description._v_dtypes[colname].base.itemsize nctypecode = numcol.dtype.char if typecol == "string": if itemsizecol > 1: orignumcol = array(['abcd']*self.nrows, dtype='S4') else: orignumcol = array(['a']*self.nrows, dtype='S1') if common.verbose: print("Typecode of NumPy column read:", nctypecode) print("Should look like:", 'c') print("Itemsize of column:", itemsizecol) print("Shape of NumPy column read:", numcol.shape) print("Should look like:", orignumcol.shape) print("First 3 elements of read col:", numcol[:3]) # Check that both NumPy objects are equal self.assertTrue(allequal(numcol, orignumcol, "numpy")) def test02_readCoordsNum(self): """Column conversion into NumPy in read_coordinates(). NumPy. """ table = self.fileh.root.table table.flavor = "numpy" coords = [1, 2, 3] self.nrows = len(coords) for colname in table.colnames: numcol = table.read_coordinates(coords, field=colname) typecol = table.coltypes[colname] type_ = numcol.dtype.type if typecol != "string": if typecol == "int64": return if common.verbose: print("Type of read NumPy column:", type_) print("Should look like:", typecol) orignumcol = ones(shape=self.nrows, dtype=numcol.dtype.char) # Check that both NumPy objects are equal self.assertTrue(allequal(numcol, orignumcol, "numpy")) def test03_getIndexNumPy(self): """Getting table rows specifyied as NumPy scalar integers.""" table = self.fileh.root.table coords = numpy.array([1, 2, 3], dtype='int8') for colname in table.colnames: numcol = [table[coord][colname] for coord in coords] typecol = table.coltypes[colname] if typecol != "string": if typecol == "int64": return numcol = numpy.array(numcol, typecol) if common.verbose: type_ = numcol.dtype.type print("Type of read NumPy column:", type_) print("Should look like:", typecol) orignumcol = ones(shape=len(numcol), dtype=numcol.dtype.char) # Check that both NumPy objects are equal self.assertTrue(allequal(numcol, orignumcol, "numpy")) def test04_setIndexNumPy(self): """Setting table rows specifyied as NumPy integers.""" self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table table.flavor = "numpy" coords = numpy.array([1, 2, 3], dtype='int8') # Modify row 1 # From PyTables 2.0 on, assignments to records can be done # only as tuples (see http://projects.scipy.org/scipy/numpy/ticket/315) # table[coords[0]] = ["aasa","x"]+[232]*12 n = len(Record.columns) - 2 table[coords[0]] = tuple(["aasa", "x"]+[232]*n) # XXX # record = list(table[coords[0]]) record = table.read(coords[0], coords[0] + 1) if common.verbose: print("""Original row: ['aasa', 'x', True, -24, 232, 232, 232, 232, 232L, 232, 232.0, 232.0, (232 + 0j), (232+0j), 232.0, (232+0j)] """) print("Read row:\n", record) self.assertEqual(record['var1'], b'aasa') self.assertEqual(record['var2'], b'x') self.assertEqual(record['var3'], True) self.assertEqual(record['var4'], -24) self.assertEqual(record['var7'], 232) # The declaration of the nested table: class Info(IsDescription): _v_pos = 3 Name = StringCol(itemsize=2) Value = ComplexCol(itemsize=16) class TestTDescr(IsDescription): """A description that has several nested columns.""" x = Int32Col(dflt=0, shape=2, pos=0) # 0 y = FloatCol(dflt=1, shape=(2, 2)) z = UInt8Col(dflt=1) z3 = EnumCol({'r': 4, 'g': 2, 'b': 1}, 'r', 'int32', shape=2) color = StringCol(itemsize=4, dflt=b"ab", pos=2) info = Info() class Info(IsDescription): # 1 _v_pos = 1 name = StringCol(itemsize=2) value = ComplexCol(itemsize=16, pos=0) # 0 y2 = FloatCol(pos=1) # 1 z2 = UInt8Col() class Info2(IsDescription): y3 = Time64Col(shape=2) name = StringCol(itemsize=2) value = ComplexCol(itemsize=16, shape=2) class TableNativeFlavorTestCase(common.PyTablesTestCase): nrows = 100 def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") fileh = open_file(self.file, "w") table = fileh.create_table(fileh.root, 'table', TestTDescr, expectedrows=self.nrows) table.flavor = "numpy" for i in range(self.nrows): table.row.append() # Fill 100 rows with default values table.flush() self.fileh = fileh def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def test01a_basicTableRead(self): """Checking the return of a NumPy in read().""" if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table data = table[:] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check the value of some columns # A flat column col = table.cols.x[:3] self.assertTrue(isinstance(col, ndarray)) npcol = zeros((3, 2), dtype="int32") self.assertTrue(allequal(col, npcol, "numpy")) # A nested column col = table.cols.Info[:3] self.assertTrue(isinstance(col, ndarray)) dtype = [('value', 'c16'), ('y2', 'f8'), ('Info2', [('name', 'S2'), ('value', 'c16', (2,)), ('y3', 'f8', (2,))]), ('name', 'S2'), ('z2', 'u1')] npcol = zeros((3,), dtype=dtype) self.assertEqual(col.dtype.descr, npcol.dtype.descr) if common.verbose: print("col-->", col) print("npcol-->", npcol) # A copy() is needed in case the buffer can be in different segments self.assertEqual(bytes(col.copy().data), bytes(npcol.data)) def test01b_basicTableRead(self): """Checking the return of a NumPy in read() (strided version).""" if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table data = table[::3] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check the value of some columns # A flat column col = table.cols.x[:9:3] self.assertTrue(isinstance(col, ndarray)) npcol = zeros((3, 2), dtype="int32") self.assertTrue(allequal(col, npcol, "numpy")) # A nested column col = table.cols.Info[:9:3] self.assertTrue(isinstance(col, ndarray)) dtype = [('value', '%sc16' % byteorder), ('y2', '%sf8' % byteorder), ('Info2', [('name', '|S2'), ('value', '%sc16' % byteorder, (2,)), ('y3', '%sf8' % byteorder, (2,))]), ('name', '|S2'), ('z2', '|u1')] npcol = zeros((3,), dtype=dtype) self.assertEqual(col.dtype.descr, npcol.dtype.descr) if common.verbose: print("col-->", col) print("npcol-->", npcol) # A copy() is needed in case the buffer can be in different segments self.assertEqual(bytes(col.copy().data), bytes(npcol.data)) def test02_getWhereList(self): """Checking the return of NumPy in get_where_list method.""" if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table data = table.get_where_list('z == 1') if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check that all columns have been selected self.assertEqual(len(data), 100) # Finally, check that the contents are ok self.assertTrue(allequal(data, arange(100, dtype="i8"), "numpy")) def test03a_readWhere(self): """Checking the return of NumPy in read_where method (strings).""" table = self.fileh.root.table table.cols.color.create_index() if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table data = table.read_where('color == b"ab"') if common.verbose: print("Type of read:", type(data)) print("Length of the data read:", len(data)) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check that all columns have been selected self.assertEqual(len(data), self.nrows) def test03b_readWhere(self): """Checking the return of NumPy in read_where method (numeric).""" table = self.fileh.root.table table.cols.z.create_index() if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table data = table.read_where('z == 0') if common.verbose: print("Type of read:", type(data)) print("Length of the data read:", len(data)) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check that all columns have been selected self.assertEqual(len(data), 0) def test04a_createTable(self): """Checking the Table creation from a numpy recarray.""" dtype = [('value', '%sc16' % byteorder), ('y2', '%sf8' % byteorder), ('Info2', [('name', '|S2'), ('value', '%sc16' % byteorder, (2,)), ('y3', '%sf8' % byteorder, (2,))]), ('name', '|S2'), ('z2', '|u1')] npdata = zeros((3,), dtype=dtype) table = self.fileh.create_table(self.fileh.root, 'table2', npdata) if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table2 data = table[:] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) print("Length of the data read:", len(data)) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check the type self.assertEqual(data.dtype.descr, npdata.dtype.descr) if common.verbose: print("npdata-->", npdata) print("data-->", data) # A copy() is needed in case the buffer would be in different segments self.assertEqual(bytes(data.copy().data), bytes(npdata.data)) def test04b_appendTable(self): """Checking appending a numpy recarray.""" table = self.fileh.root.table npdata = table[3:6] table.append(npdata) if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table data = table[-3:] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("Last 3 elements of read:", data[-3:]) print("Length of the data read:", len(data)) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check the type self.assertEqual(data.dtype.descr, npdata.dtype.descr) if common.verbose: print("npdata-->", npdata) print("data-->", data) # A copy() is needed in case the buffer would be in different segments self.assertEqual(bytes(data.copy().data), bytes(npdata.data)) def test05a_assignColumn(self): """Checking assigning to a column.""" table = self.fileh.root.table table.cols.z[:] = zeros((100,), dtype='u1') if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table data = table.cols.z[:] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) print("Length of the data read:", len(data)) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check that all columns have been selected self.assertEqual(len(data), 100) # Finally, check that the contents are ok self.assertTrue(allequal(data, zeros((100,), dtype="u1"), "numpy")) def test05b_modifyingColumns(self): """Checking modifying several columns at once.""" table = self.fileh.root.table xcol = ones((3, 2), 'int32') ycol = zeros((3, 2, 2), 'float64') zcol = zeros((3,), 'uint8') table.modify_columns(3, 6, 1, [xcol, ycol, zcol], ['x', 'y', 'z']) if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table data = table.cols.y[3:6] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) print("Length of the data read:", len(data)) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check the type self.assertEqual(data.dtype.descr, ycol.dtype.descr) if common.verbose: print("ycol-->", ycol) print("data-->", data) # A copy() is needed in case the buffer would be in different segments self.assertEqual(data.copy().data, ycol.data) def test05c_modifyingColumns(self): """Checking modifying several columns using a single numpy buffer.""" table = self.fileh.root.table dtype = [('x', 'i4', (2,)), ('y', 'f8', (2, 2)), ('z', 'u1')] nparray = zeros((3,), dtype=dtype) table.modify_columns(3, 6, 1, nparray, ['x', 'y', 'z']) if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table ycol = zeros((3, 2, 2), 'float64') data = table.cols.y[3:6] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) print("Length of the data read:", len(data)) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check the type self.assertEqual(data.dtype.descr, ycol.dtype.descr) if common.verbose: print("ycol-->", ycol) print("data-->", data) # A copy() is needed in case the buffer would be in different segments self.assertEqual(data.copy().data, ycol.data) def test06a_assignNestedColumn(self): """Checking assigning a nested column (using modify_column).""" table = self.fileh.root.table dtype = [('value', '%sc16' % byteorder), ('y2', '%sf8' % byteorder), ('Info2', [('name', '|S2'), ('value', '%sc16' % byteorder, (2,)), ('y3', '%sf8' % byteorder, (2,))]), ('name', '|S2'), ('z2', '|u1')] npdata = zeros((3,), dtype=dtype) data = table.cols.Info[3:6] table.modify_column(3, 6, 1, column=npdata, colname='Info') if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table data = table.cols.Info[3:6] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) print("Length of the data read:", len(data)) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check the type self.assertEqual(data.dtype.descr, npdata.dtype.descr) if common.verbose: print("npdata-->", npdata) print("data-->", data) # A copy() is needed in case the buffer would be in different segments self.assertEqual(bytes(data.copy().data), bytes(npdata.data)) def test06b_assignNestedColumn(self): """Checking assigning a nested column (using the .cols accessor).""" table = self.fileh.root.table dtype = [('value', '%sc16' % byteorder), ('y2', '%sf8' % byteorder), ('Info2', [('name', '|S2'), ('value', '%sc16' % byteorder, (2,)), ('y3', '%sf8' % byteorder, (2,))]), ('name', '|S2'), ('z2', '|u1')] npdata = zeros((3,), dtype=dtype) # self.assertRaises(NotImplementedError, # table.cols.Info.__setitem__, slice(3,6,1), npdata) table.cols.Info[3:6] = npdata if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table data = table.cols.Info[3:6] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) print("Length of the data read:", len(data)) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check the type self.assertEqual(data.dtype.descr, npdata.dtype.descr) if common.verbose: print("npdata-->", npdata) print("data-->", data) # A copy() is needed in case the buffer would be in different segments self.assertEqual(bytes(data.copy().data), bytes(npdata.data)) def test07a_modifyingRows(self): """Checking modifying several rows at once (using modify_rows).""" table = self.fileh.root.table # Read a chunk of the table chunk = table[0:3] # Modify it somewhat chunk['y'][:] = -1 table.modify_rows(3, 6, 1, rows=chunk) if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table ycol = zeros((3, 2, 2), 'float64')-1 data = table.cols.y[3:6] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) print("Length of the data read:", len(data)) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check the type self.assertEqual(data.dtype.descr, ycol.dtype.descr) if common.verbose: print("ycol-->", ycol) print("data-->", data) self.assertTrue(allequal(ycol, data, "numpy")) def test07b_modifyingRows(self): """Checking modifying several rows at once (using cols accessor).""" table = self.fileh.root.table # Read a chunk of the table chunk = table[0:3] # Modify it somewhat chunk['y'][:] = -1 table.cols[3:6] = chunk if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table # Check that some column has been actually modified ycol = zeros((3, 2, 2), 'float64')-1 data = table.cols.y[3:6] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) print("Length of the data read:", len(data)) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check the type self.assertEqual(data.dtype.descr, ycol.dtype.descr) if common.verbose: print("ycol-->", ycol) print("data-->", data) self.assertTrue(allequal(ycol, data, "numpy")) def test08a_modifyingRows(self): """Checking modifying just one row at once (using modify_rows).""" table = self.fileh.root.table # Read a chunk of the table chunk = table[3:4] # Modify it somewhat chunk['y'][:] = -1 table.modify_rows(6, 7, 1, chunk) if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table # Check that some column has been actually modified ycol = zeros((2, 2), 'float64')-1 data = table.cols.y[6] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) print("Length of the data read:", len(data)) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check the type self.assertEqual(data.dtype.descr, ycol.dtype.descr) if common.verbose: print("ycol-->", ycol) print("data-->", data) self.assertTrue(allequal(ycol, data, "numpy")) def test08b_modifyingRows(self): """Checking modifying just one row at once (using cols accessor).""" table = self.fileh.root.table # Read a chunk of the table chunk = table[3:4] # Modify it somewhat chunk['y'][:] = -1 table.cols[6] = chunk if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table # Check that some column has been actually modified ycol = zeros((2, 2), 'float64')-1 data = table.cols.y[6] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) print("Length of the data read:", len(data)) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check the type self.assertEqual(data.dtype.descr, ycol.dtype.descr) if common.verbose: print("ycol-->", ycol) print("data-->", data) self.assertTrue(allequal(ycol, data, "numpy")) def test09a_getStrings(self): """Checking the return of string columns with spaces.""" if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table rdata = table.get_where_list('color == b"ab"') data = table.read_coordinates(rdata) if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check that all columns have been selected self.assertEqual(len(data), 100) # Finally, check that the contents are ok for idata in data['color']: self.assertEqual(idata, array("ab", dtype="|S4")) def test09b_getStrings(self): """Checking the return of string columns with spaces. (modify) """ if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table for i in range(50): table.cols.color[i] = "a " table.flush() data = table[:] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check that all columns have been selected self.assertEqual(len(data), 100) # Finally, check that the contents are ok for i in range(100): idata = data['color'][i] if i >= 50: self.assertEqual(idata, array("ab", dtype="|S4")) else: self.assertEqual(idata, array("a ", dtype="|S4")) def test09c_getStrings(self): """Checking the return of string columns with spaces. (append) """ if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") table = self.fileh.root.table row = table.row for i in range(50): row["color"] = "a " # note the trailing spaces row.append() table.flush() if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") data = self.fileh.root.table[:] if common.verbose: print("Type of read:", type(data)) print("Description of the record:", data.dtype.descr) print("First 3 elements of read:", data[:3]) # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check that all columns have been selected self.assertEqual(len(data), 150) # Finally, check that the contents are ok # Finally, check that the contents are ok for i in range(150): idata = data['color'][i] if i < 100: self.assertEqual(idata, array("ab", dtype="|S4")) else: self.assertEqual(idata, array("a ", dtype="|S4")) class TableNativeFlavorOpenTestCase(TableNativeFlavorTestCase): close = 0 class TableNativeFlavorCloseTestCase(TableNativeFlavorTestCase): close = 1 class AttributesTestCase(common.PyTablesTestCase): def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") self.fileh.create_group(self.fileh.root, 'group') def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def test01_writeAttribute(self): """Checking the creation of a numpy attribute.""" group = self.fileh.root.group g_attrs = group._v_attrs g_attrs.numpy1 = zeros((1, 1), dtype='int16') if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") group = self.fileh.root.group g_attrs = group._v_attrs # Check that we can retrieve a numpy object data = g_attrs.numpy1 npcomp = zeros((1, 1), dtype='int16') # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check the type self.assertEqual(data.dtype.descr, npcomp.dtype.descr) if common.verbose: print("npcomp-->", npcomp) print("data-->", data) self.assertTrue(allequal(npcomp, data, "numpy")) def test02_updateAttribute(self): """Checking the modification of a numpy attribute.""" group = self.fileh.root.group g_attrs = group._v_attrs g_attrs.numpy1 = zeros((1, 2), dtype='int16') if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") group = self.fileh.root.group g_attrs = group._v_attrs # Update this attribute g_attrs.numpy1 = ones((1, 2), dtype='int16') # Check that we can retrieve a numpy object data = g_attrs.numpy1 npcomp = ones((1, 2), dtype='int16') # Check that both NumPy objects are equal self.assertTrue(isinstance(data, ndarray)) # Check the type self.assertEqual(data.dtype.descr, npcomp.dtype.descr) if common.verbose: print("npcomp-->", npcomp) print("data-->", data) self.assertTrue(allequal(npcomp, data, "numpy")) class AttributesOpenTestCase(AttributesTestCase): close = 0 class AttributesCloseTestCase(AttributesTestCase): close = 1 class StrlenTestCase(common.PyTablesTestCase): def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") group = self.fileh.create_group(self.fileh.root, 'group') tablelayout = {'Text': StringCol(itemsize=1000), } self.table = self.fileh.create_table(group, 'table', tablelayout) self.table.flavor = 'numpy' row = self.table.row row['Text'] = 'Hello Francesc!' # XXX: check unicode --> bytes row.append() row['Text'] = 'Hola Francesc!' # XXX: check unicode --> bytes row.append() self.table.flush() def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def test01(self): """Checking the lengths of strings (read field).""" if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") self.table = self.fileh.root.group.table # Get both strings str1 = self.table.col('Text')[0] str2 = self.table.col('Text')[1] if common.verbose: print("string1-->", str1) print("string2-->", str2) # Check that both NumPy objects are equal self.assertEqual(len(str1), len(b'Hello Francesc!')) self.assertEqual(len(str2), len(b'Hola Francesc!')) self.assertEqual(str1, b'Hello Francesc!') self.assertEqual(str2, b'Hola Francesc!') def test02(self): """Checking the lengths of strings (read recarray).""" if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") self.table = self.fileh.root.group.table # Get both strings str1 = self.table[:]['Text'][0] str2 = self.table[:]['Text'][1] # Check that both NumPy objects are equal self.assertEqual(len(str1), len(b'Hello Francesc!')) self.assertEqual(len(str2), len(b'Hola Francesc!')) self.assertEqual(str1, b'Hello Francesc!') self.assertEqual(str2, b'Hola Francesc!') def test03(self): """Checking the lengths of strings (read recarray, row by row).""" if self.close: self.fileh.close() self.fileh = open_file(self.file, "a") self.table = self.fileh.root.group.table # Get both strings str1 = self.table[0]['Text'] str2 = self.table[1]['Text'] # Check that both NumPy objects are equal self.assertEqual(len(str1), len(b'Hello Francesc!')) self.assertEqual(len(str2), len(b'Hola Francesc!')) self.assertEqual(str1, b'Hello Francesc!') self.assertEqual(str2, b'Hola Francesc!') class StrlenOpenTestCase(StrlenTestCase): close = 0 class StrlenCloseTestCase(StrlenTestCase): close = 1 #-------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() niter = 1 # theSuite.addTest(unittest.makeSuite(StrlenOpenTestCase)) # theSuite.addTest(unittest.makeSuite(Basic0DOneTestCase)) # theSuite.addTest(unittest.makeSuite(GroupsArrayTestCase)) for i in range(niter): theSuite.addTest(unittest.makeSuite(Basic0DOneTestCase)) theSuite.addTest(unittest.makeSuite(Basic0DTwoTestCase)) theSuite.addTest(unittest.makeSuite(Basic1DOneTestCase)) theSuite.addTest(unittest.makeSuite(Basic1DTwoTestCase)) theSuite.addTest(unittest.makeSuite(Basic1DThreeTestCase)) theSuite.addTest(unittest.makeSuite(Basic2DTestCase)) theSuite.addTest(unittest.makeSuite(GroupsArrayTestCase)) theSuite.addTest(unittest.makeSuite(TableReadTestCase)) theSuite.addTest(unittest.makeSuite(TableNativeFlavorOpenTestCase)) theSuite.addTest(unittest.makeSuite(TableNativeFlavorCloseTestCase)) theSuite.addTest(unittest.makeSuite(AttributesOpenTestCase)) theSuite.addTest(unittest.makeSuite(AttributesCloseTestCase)) theSuite.addTest(unittest.makeSuite(StrlenOpenTestCase)) theSuite.addTest(unittest.makeSuite(StrlenCloseTestCase)) if common.heavy: theSuite.addTest(unittest.makeSuite(Basic10DTestCase)) # The 32 dimensions case takes forever to run!! # theSuite.addTest(unittest.makeSuite(Basic32DTestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/test_queries.py000066400000000000000000001235501231437614300212170ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: 2006-10-19 # Author: Ivan Vilata i Balaguer - ivan@selidor.net # # $Id$ # ######################################################################## """Test module for queries on datasets.""" import re import sys import types import unittest import numpy import tables from tables.utils import SizeType from tables.tests import common from tables.tests.common import verbosePrint as vprint # Data parameters # --------------- row_period = 50 """Maximum number of unique rows before they start cycling.""" md_shape = (2, 2) """Shape of multidimensional fields.""" _maxnvalue = row_period + numpy.prod(md_shape, dtype=SizeType) - 1 _strlen = int(numpy.log10(_maxnvalue-1)) + 1 str_format = '%%0%dd' % _strlen """Format of string values.""" small_blocksizes = (300, 60, 20, 5) # small_blocksizes = (512, 128, 32, 4) # for manual testing only """Sensible parameters for indexing with small blocksizes.""" # Type information # ---------------- type_info = { 'bool': (numpy.bool_, bool), 'int8': (numpy.int8, int), 'uint8': (numpy.uint8, int), 'int16': (numpy.int16, int), 'uint16': (numpy.uint16, int), 'int32': (numpy.int32, int), 'uint32': (numpy.uint32, long), 'int64': (numpy.int64, long), 'uint64': (numpy.uint64, long), 'float32': (numpy.float32, float), 'float64': (numpy.float64, float), 'complex64': (numpy.complex64, complex), 'complex128': (numpy.complex128, complex), 'time32': (numpy.int32, int), 'time64': (numpy.float64, float), 'enum': (numpy.uint8, int), # just for these tests 'string': ('S%s' % _strlen, numpy.string_), # just for these tests } """NumPy and Numexpr type for each PyTables type that will be tested.""" if hasattr(numpy, 'float16'): type_info['float16'] = (numpy.float16, float) # if hasattr(numpy, 'float96'): # type_info['float96'] = (numpy.float96, float) # if hasattr(numpy, 'float128'): # type_info['float128'] = (numpy.float128, float) # if hasattr(numpy, 'complex192'): # type_info['complex192'] = (numpy.complex192, complex) # if hasattr(numpy, 'complex256'): # type_info['complex256'] = (numpy.complex256, complex) sctype_from_type = dict((type_, info[0]) for (type_, info) in type_info.iteritems()) """Maps PyTables types to NumPy scalar types.""" nxtype_from_type = dict((type_, info[1]) for (type_, info) in type_info.iteritems()) """Maps PyTables types to Numexpr types.""" heavy_types = frozenset(['uint8', 'int16', 'uint16', 'float32', 'complex64']) """PyTables types to be tested only in heavy mode.""" enum = tables.Enum(dict(('n%d' % i, i) for i in range(_maxnvalue))) """Enumerated type to be used in tests.""" # Table description # ----------------- def append_columns(classdict, shape=()): """Append a ``Col`` of each PyTables data type to the `classdict`. A column of a certain TYPE gets called ``c_TYPE``. The number of added columns is returned. """ heavy = common.heavy for (itype, type_) in enumerate(sorted(type_info.iterkeys())): if not heavy and type_ in heavy_types: continue # skip heavy type in non-heavy mode colpos = itype + 1 colname = 'c_%s' % type_ if type_ == 'enum': base = tables.Atom.from_sctype(sctype_from_type[type_]) col = tables.EnumCol(enum, enum(0), base, shape=shape, pos=colpos) else: sctype = sctype_from_type[type_] dtype = numpy.dtype((sctype, shape)) col = tables.Col.from_dtype(dtype, pos=colpos) classdict[colname] = col ncols = colpos return ncols def nested_description(classname, pos, shape=()): """Return a nested column description with all PyTables data types. A column of a certain TYPE gets called ``c_TYPE``. The nested column will be placed in the position indicated by `pos`. """ classdict = {} append_columns(classdict, shape=shape) classdict['_v_pos'] = pos return type(classname, (tables.IsDescription,), classdict) def table_description(classname, nclassname, shape=()): """Return a table description for testing queries. The description consists of all PyTables data types, both in the top level and in the ``c_nested`` nested column. A column of a certain TYPE gets called ``c_TYPE``. An extra integer column ``c_extra`` is also provided. If a `shape` is given, it will be used for all columns. Finally, an extra indexed column ``c_idxextra`` is added as well in order to provide some basic tests for multi-index queries. """ classdict = {} colpos = append_columns(classdict, shape) ndescr = nested_description(nclassname, colpos, shape=shape) classdict['c_nested'] = ndescr colpos += 1 extracol = tables.IntCol(shape=shape, pos=colpos) classdict['c_extra'] = extracol colpos += 1 idxextracol = tables.IntCol(shape=shape, pos=colpos) classdict['c_idxextra'] = idxextracol colpos += 1 return type(classname, (tables.IsDescription,), classdict) TableDescription = table_description( 'TableDescription', 'NestedDescription') """Unidimensional table description for testing queries.""" MDTableDescription = table_description( 'MDTableDescription', 'MDNestedDescription', shape=md_shape) """Multidimensional table description for testing queries.""" # Table data # ---------- table_data = {} """Cached table data for a given shape and number of rows.""" # Data is cached because computing it row by row is quite slow. Hop! def fill_table(table, shape, nrows): """Fill the given `table` with `nrows` rows of data. Values in the i-th row (where 0 <= i < `row_period`) for a multidimensional field with M elements span from i to i + M-1. For subsequent rows, values repeat cyclically. The same goes for the ``c_extra`` column, but values range from -`row_period`/2 to +`row_period`/2. """ # Reuse already computed data if possible. tdata = table_data.get((shape, nrows)) if tdata is not None: table.append(tdata) table.flush() return heavy = common.heavy size = int(numpy.prod(shape, dtype=SizeType)) row, value = table.row, 0 for nrow in xrange(nrows): data = numpy.arange(value, value + size).reshape(shape) for (type_, sctype) in sctype_from_type.iteritems(): if not heavy and type_ in heavy_types: continue # skip heavy type in non-heavy mode colname = 'c_%s' % type_ ncolname = 'c_nested/%s' % colname if type_ == 'bool': coldata = data > (row_period // 2) elif type_ == 'string': sdata = [str_format % x for x in range(value, value + size)] coldata = numpy.array(sdata, dtype=sctype).reshape(shape) else: coldata = numpy.asarray(data, dtype=sctype) row[ncolname] = row[colname] = coldata row['c_extra'] = data - (row_period // 2) row['c_idxextra'] = data - (row_period // 2) row.append() value += 1 if value == row_period: value = 0 table.flush() # Make computed data reusable. tdata = table.read() table_data[(shape, nrows)] = tdata # Base test cases # --------------- class BaseTableQueryTestCase(common.TempFileMixin, common.PyTablesTestCase): """Base test case for querying tables. Sub-classes must define the following attributes: ``tableDescription`` The description of the table to be created. ``shape`` The shape of data fields in the table. ``nrows`` The number of data rows to be generated for the table. Sub-classes may redefine the following attributes: ``indexed`` Whether columns shall be indexed, if possible. Default is not to index them. ``optlevel`` The level of optimisation of column indexes. Default is 0. """ indexed = False optlevel = 0 colNotIndexable_re = re.compile(r"\bcan not be indexed\b") condNotBoolean_re = re.compile(r"\bdoes not have a boolean type\b") def create_indexes(self, colname, ncolname, extracolname): if not self.indexed: return try: kind = self.kind vprint("* Indexing ``%s`` columns. Type: %s." % (colname, kind)) for acolname in [colname, ncolname, extracolname]: acolumn = self.table.colinstances[acolname] acolumn.create_index( kind=self.kind, optlevel=self.optlevel, _blocksizes=small_blocksizes, _testmode=True) except TypeError as te: if self.colNotIndexable_re.search(str(te)): raise common.SkipTest( "Columns of this type can not be indexed.") raise except NotImplementedError: raise common.SkipTest( "Indexing columns of this type is not supported yet.") def setUp(self): super(BaseTableQueryTestCase, self).setUp() self.table = table = self.h5file.create_table( '/', 'test', self.tableDescription, expectedrows=self.nrows) fill_table(table, self.shape, self.nrows) class ScalarTableMixin: tableDescription = TableDescription shape = () class MDTableMixin: tableDescription = MDTableDescription shape = md_shape # Test cases on query data # ------------------------ operators = [ None, '~', '<', '<=', '==', '!=', '>=', '>', ('<', '<='), ('>', '>=')] """Comparison operators to check with different types.""" heavy_operators = frozenset(['~', '<=', '>=', '>', ('>', '>=')]) """Comparison operators to be tested only in heavy mode.""" left_bound = row_period // 4 """Operand of left side operator in comparisons with operator pairs.""" right_bound = row_period * 3 // 4 """Operand of right side operator in comparisons with operator pairs.""" extra_conditions = [ '', # uses one index '& ((c_extra + 1) < 0)', # uses one index '| (c_idxextra > 0)', # uses two indexes '| ((c_idxextra > 0) | ((c_extra + 1) > 0))', # can't use indexes ] """Extra conditions to append to comparison conditions.""" class TableDataTestCase(BaseTableQueryTestCase): """Base test case for querying table data. Automatically created test method names have the format ``test_XNNNN``, where ``NNNN`` is the zero-padded test number and ``X`` indicates whether the test belongs to the light (``l``) or heavy (``h``) set. """ _testfmt_light = 'test_l%04d' _testfmt_heavy = 'test_h%04d' def create_test_method(type_, op, extracond): sctype = sctype_from_type[type_] # Compute the value of bounds. condvars = {'bound': right_bound, 'lbound': left_bound, 'rbound': right_bound} for (bname, bvalue) in condvars.iteritems(): if type_ == 'string': bvalue = str_format % bvalue bvalue = nxtype_from_type[type_](bvalue) condvars[bname] = bvalue # Compute the name of columns. colname = 'c_%s' % type_ ncolname = 'c_nested/%s' % colname # Compute the query condition. if not op: # as is cond = colname elif op == '~': # unary cond = '~(%s)' % colname elif op == '<': # binary variable-constant cond = '%s %s %s' % (colname, op, repr(condvars['bound'])) elif isinstance(op, tuple): # double binary variable-constant cond = ('(lbound %s %s) & (%s %s rbound)' % (op[0], colname, colname, op[1])) else: # binary variable-variable cond = '%s %s bound' % (colname, op) if extracond: cond = '(%s) %s' % (cond, extracond) def test_method(self): vprint("* Condition is ``%s``." % cond) # Replace bitwise operators with their logical counterparts. pycond = cond for (ptop, pyop) in [('&', 'and'), ('|', 'or'), ('~', 'not')]: pycond = pycond.replace(ptop, pyop) pycond = compile(pycond, '', 'eval') table = self.table self.create_indexes(colname, ncolname, 'c_idxextra') table_slice = dict(start=1, stop=table.nrows - 5, step=3) rownos, fvalues = None, None # Test that both simple and nested columns work as expected. # Knowing how the table is filled, results must be the same. for acolname in [colname, ncolname]: # First the reference Python version. pyrownos, pyfvalues, pyvars = [], [], condvars.copy() for row in table.iterrows(**table_slice): pyvars[colname] = row[acolname] pyvars['c_extra'] = row['c_extra'] pyvars['c_idxextra'] = row['c_idxextra'] try: isvalidrow = eval(pycond, {}, pyvars) except TypeError: raise common.SkipTest( "The Python type does not support the operation.") if isvalidrow: pyrownos.append(row.nrow) pyfvalues.append(row[acolname]) pyrownos = numpy.array(pyrownos) # row numbers already sorted pyfvalues = numpy.array(pyfvalues, dtype=sctype) pyfvalues.sort() vprint("* %d rows selected by Python from ``%s``." % (len(pyrownos), acolname)) if rownos is None: rownos = pyrownos # initialise reference results fvalues = pyfvalues else: self.assertTrue(numpy.all(pyrownos == rownos)) # check self.assertTrue(numpy.all(pyfvalues == fvalues)) # Then the in-kernel or indexed version. ptvars = condvars.copy() ptvars[colname] = table.colinstances[acolname] ptvars['c_extra'] = table.colinstances['c_extra'] ptvars['c_idxextra'] = table.colinstances['c_idxextra'] try: isidxq = table.will_query_use_indexing(cond, ptvars) # Query twice to trigger possible query result caching. ptrownos = [table.get_where_list(cond, condvars, sort=True, **table_slice) for _ in range(2)] ptfvalues = [ table.read_where(cond, condvars, field=acolname, **table_slice) for _ in range(2) ] except TypeError as te: if self.condNotBoolean_re.search(str(te)): raise common.SkipTest("The condition is not boolean.") raise except NotImplementedError: raise common.SkipTest( "The PyTables type does not support the operation.") for ptfvals in ptfvalues: # row numbers already sorted ptfvals.sort() vprint("* %d rows selected by PyTables from ``%s``" % (len(ptrownos[0]), acolname), nonl=True) vprint("(indexing: %s)." % ["no", "yes"][bool(isidxq)]) self.assertTrue(numpy.all(ptrownos[0] == rownos)) self.assertTrue(numpy.all(ptfvalues[0] == fvalues)) # The following test possible caching of query results. self.assertTrue(numpy.all(ptrownos[0] == ptrownos[1])) self.assertTrue(numpy.all(ptfvalues[0] == ptfvalues[1])) test_method.__doc__ = "Testing ``%s``." % cond return test_method # Create individual tests. You may restrict which tests are generated # by replacing the sequences in the ``for`` statements. For instance: testn = 0 for type_ in type_info: # for type_ in ['string']: for op in operators: # for op in ['!=']: # Decide to which set the test belongs. heavy = type_ in heavy_types or op in heavy_operators if heavy: testfmt = TableDataTestCase._testfmt_heavy numfmt = ' [#H%d]' else: testfmt = TableDataTestCase._testfmt_light numfmt = ' [#L%d]' for extracond in extra_conditions: # for extracond in ['']: tmethod = create_test_method(type_, op, extracond) # The test number is appended to the docstring to help # identify failing methods in non-verbose mode. tmethod.__name__ = testfmt % testn # tmethod.__doc__ += numfmt % testn tmethod.__doc__ += testfmt % testn ptmethod = common.pyTablesTest(tmethod) if sys.version_info[0] < 3: imethod = types.MethodType(ptmethod, None, TableDataTestCase) else: imethod = ptmethod setattr(TableDataTestCase, tmethod.__name__, imethod) testn += 1 # Base classes for non-indexed queries. NX_BLOCK_SIZE1 = 128 # from ``interpreter.c`` in Numexpr NX_BLOCK_SIZE2 = 8 # from ``interpreter.c`` in Numexpr class SmallNITableMixin: nrows = row_period * 2 assert NX_BLOCK_SIZE2 < nrows < NX_BLOCK_SIZE1 assert nrows % NX_BLOCK_SIZE2 != 0 # to have some residual rows class BigNITableMixin: nrows = row_period * 3 assert nrows > NX_BLOCK_SIZE1 + NX_BLOCK_SIZE2 assert nrows % NX_BLOCK_SIZE1 != 0 assert nrows % NX_BLOCK_SIZE2 != 0 # to have some residual rows # Parameters for non-indexed queries. table_sizes = ['Small', 'Big'] heavy_table_sizes = frozenset(['Big']) table_ndims = ['Scalar'] # to enable multidimensional testing, include 'MD' # Non-indexed queries: ``[SB][SM]TDTestCase``, where: # # 1. S is for small and B is for big size table. # Sizes are listed in `table_sizes`. # 2. S is for scalar and M for multidimensional columns. # Dimensionalities are listed in `table_ndims`. def niclassdata(): for size in table_sizes: heavy = size in heavy_table_sizes for ndim in table_ndims: classname = '%s%sTDTestCase' % (size[0], ndim[0]) cbasenames = ('%sNITableMixin' % size, '%sTableMixin' % ndim, 'TableDataTestCase') classdict = dict(heavy=heavy) yield (classname, cbasenames, classdict) # Base classes for the different type index. class UltraLightITableMixin: kind = "ultralight" class LightITableMixin: kind = "light" class MediumITableMixin: kind = "medium" class FullITableMixin: kind = "full" # Base classes for indexed queries. class SmallSTableMixin: nrows = 50 class MediumSTableMixin: nrows = 100 class BigSTableMixin: nrows = 500 # Parameters for indexed queries. ckinds = ['UltraLight', 'Light', 'Medium', 'Full'] itable_sizes = ['Small', 'Medium', 'Big'] heavy_itable_sizes = frozenset(['Medium', 'Big']) itable_optvalues = [0, 1, 3, 7, 9] heavy_itable_optvalues = frozenset([0, 1, 7, 9]) # Indexed queries: ``[SMB]I[ulmf]O[01379]TDTestCase``, where: # # 1. S is for small, M for medium and B for big size table. # Sizes are listed in `itable_sizes`. # 2. U is for 'ultraLight', L for 'light', M for 'medium', F for 'Full' indexes # Index types are listed in `ckinds`. # 3. 0 to 9 is the desired index optimization level. # Optimizations are listed in `itable_optvalues`. def iclassdata(): for ckind in ckinds: for size in itable_sizes: for optlevel in itable_optvalues: heavy = (optlevel in heavy_itable_optvalues or size in heavy_itable_sizes) classname = '%sI%sO%dTDTestCase' % ( size[0], ckind[0], optlevel) cbasenames = ('%sSTableMixin' % size, '%sITableMixin' % ckind, 'ScalarTableMixin', 'TableDataTestCase') classdict = dict(heavy=heavy, optlevel=optlevel, indexed=True) yield (classname, cbasenames, classdict) # Create test classes. for cdatafunc in [niclassdata, iclassdata]: for (cname, cbasenames, cdict) in cdatafunc(): cbases = tuple(eval(cbase) for cbase in cbasenames) class_ = type(cname, cbases, cdict) exec('%s = class_' % cname) # Test cases on query usage # ------------------------- class BaseTableUsageTestCase(BaseTableQueryTestCase): nrows = row_period _gvar = None """Use this when a global variable is needed.""" class ScalarTableUsageTestCase(ScalarTableMixin, BaseTableUsageTestCase): """Test case for query usage on scalar tables. This also tests for most usage errors and situations. """ def test_empty_condition(self): """Using an empty condition.""" self.assertRaises(SyntaxError, self.table.where, '') def test_syntax_error(self): """Using a condition with a syntax error.""" self.assertRaises(SyntaxError, self.table.where, 'foo bar') def test_unsupported_object(self): """Using a condition with an unsupported object.""" self.assertRaises(TypeError, self.table.where, '[]') self.assertRaises(TypeError, self.table.where, 'obj', {'obj': {}}) self.assertRaises(TypeError, self.table.where, 'c_bool < []') def test_unsupported_syntax(self): """Using a condition with unsupported syntax.""" self.assertRaises(TypeError, self.table.where, 'c_bool[0]') self.assertRaises(TypeError, self.table.where, 'c_bool()') self.assertRaises(NameError, self.table.where, 'c_bool.__init__') def test_no_column(self): """Using a condition with no participating columns.""" self.assertRaises(ValueError, self.table.where, 'True') def test_foreign_column(self): """Using a condition with a column from other table.""" table2 = self.h5file.create_table('/', 'other', self.tableDescription) self.assertRaises(ValueError, self.table.where, 'c_int32_a + c_int32_b > 0', {'c_int32_a': self.table.cols.c_int32, 'c_int32_b': table2.cols.c_int32}) def test_unsupported_op(self): """Using a condition with unsupported operations on types.""" NIE = NotImplementedError self.assertRaises(NIE, self.table.where, 'c_complex128 > 0j') if sys.version_info[0] < 3: self.assertRaises(NIE, self.table.where, 'c_string + "a" > "abc"') else: self.assertRaises(NIE, self.table.where, 'c_string + b"a" > b"abc"') def test_not_boolean(self): """Using a non-boolean condition.""" self.assertRaises(TypeError, self.table.where, 'c_int32') def test_nested_col(self): """Using a condition with nested columns.""" self.assertRaises(TypeError, self.table.where, 'c_nested') def test_implicit_col(self): """Using implicit column names in conditions.""" # If implicit columns didn't work, a ``NameError`` would be raised. self.assertRaises(TypeError, self.table.where, 'c_int32') # If overriding didn't work, no exception would be raised. self.assertRaises(TypeError, self.table.where, 'c_bool', {'c_bool': self.table.cols.c_int32}) # External variables do not override implicit columns. def where_with_locals(): c_int32 = self.table.cols.c_bool # this wouldn't cause an error self.assertTrue(c_int32 is not None) self.table.where('c_int32') self.assertRaises(TypeError, where_with_locals) def test_condition_vars(self): """Using condition variables in conditions.""" # If condition variables didn't work, a ``NameError`` would be raised. self.assertRaises(NotImplementedError, self.table.where, 'c_string > bound', {'bound': 0}) def where_with_locals(): bound = 'foo' # this wouldn't cause an error self.table.where('c_string > bound', {'bound': 0}) self.assertRaises(NotImplementedError, where_with_locals) def where_with_globals(): global _gvar _gvar = 'foo' # this wouldn't cause an error try: self.table.where('c_string > _gvar', {'_gvar': 0}) finally: del _gvar # to keep global namespace clean self.assertRaises(NotImplementedError, where_with_globals) def test_scopes(self): """Looking up different scopes for variables.""" # Make sure the variable is not implicit. self.assertRaises(NameError, self.table.where, 'col') # First scope: dictionary of condition variables. self.assertRaises(TypeError, self.table.where, 'col', {'col': self.table.cols.c_int32}) # Second scope: local variables. def where_whith_locals(): col = self.table.cols.c_int32 self.assertTrue(col is not None) self.table.where('col') self.assertRaises(TypeError, where_whith_locals) # Third scope: global variables. def where_with_globals(): global _gvar _gvar = self.table.cols.c_int32 try: self.table.where('_gvar') finally: del _gvar # to keep global namespace clean self.assertRaises(TypeError, where_with_globals) class MDTableUsageTestCase(MDTableMixin, BaseTableUsageTestCase): """Test case for query usage on multidimensional tables.""" def test(self): """Using a condition on a multidimensional table.""" # Easy: queries on multidimensional tables are not implemented yet! self.assertRaises(NotImplementedError, self.table.where, 'c_bool') class IndexedTableUsage(ScalarTableMixin, BaseTableUsageTestCase): """Test case for query usage on indexed tables. Indexing could be used in more cases, but it is expected to kick in at least in the cases tested here. """ nrows = 50 indexed = True def setUp(self): super(IndexedTableUsage, self).setUp() self.table.cols.c_bool.create_index(_blocksizes=small_blocksizes) self.table.cols.c_int32.create_index(_blocksizes=small_blocksizes) self.will_query_use_indexing = self.table.will_query_use_indexing self.compileCondition = self.table._compile_condition self.requiredExprVars = self.table._required_expr_vars usable_idxs = set() for expr in self.idx_expr: idxvar = expr[0] if idxvar not in usable_idxs: usable_idxs.add(idxvar) self.usable_idxs = frozenset(usable_idxs) def test(self): for condition in self.conditions: c_usable_idxs = self.will_query_use_indexing(condition, {}) self.assertEqual(c_usable_idxs, self.usable_idxs, "\nQuery with condition: ``%s``\n" "Computed usable indexes are: ``%s``\n" "and should be: ``%s``" % (condition, c_usable_idxs, self.usable_idxs)) condvars = self.requiredExprVars(condition, None) compiled = self.compileCondition(condition, condvars) c_idx_expr = compiled.index_expressions self.assertEqual(c_idx_expr, self.idx_expr, "\nWrong index expression in condition:\n``%s``\n" "Compiled index expression is:\n``%s``\n" "and should be:\n``%s``" % (condition, c_idx_expr, self.idx_expr)) c_str_expr = compiled.string_expression self.assertEqual(c_str_expr, self.str_expr, "\nWrong index operations in condition:\n``%s``\n" "Computed index operations are:\n``%s``\n" "and should be:\n``%s``" % (condition, c_str_expr, self.str_expr)) vprint("* Query with condition ``%s`` will use " "variables ``%s`` for indexing." % (condition, compiled.index_variables)) class IndexedTableUsage1(IndexedTableUsage): conditions = [ '(c_int32 > 0)', '(c_int32 > 0) & (c_extra > 0)', '(c_int32 > 0) & ((~c_bool) | (c_extra > 0))', '(c_int32 > 0) & ((c_extra < 3) & (c_extra > 0))', ] idx_expr = [('c_int32', ('gt',), (0,))] str_expr = 'e0' class IndexedTableUsage2(IndexedTableUsage): conditions = [ '(c_int32 > 0) & (c_int32 < 5)', '(c_int32 > 0) & (c_int32 < 5) & (c_extra > 0)', '(c_int32 > 0) & (c_int32 < 5) & ((c_bool == True) | (c_extra > 0))', '(c_int32 > 0) & (c_int32 < 5) & ((c_extra > 0) | (c_bool == True))', ] idx_expr = [('c_int32', ('gt', 'lt'), (0, 5))] str_expr = 'e0' class IndexedTableUsage3(IndexedTableUsage): conditions = [ '(c_bool == True)', '(c_bool == True) & (c_extra > 0)', '(c_extra > 0) & (c_bool == True)', '((c_extra > 0) & (c_extra < 4)) & (c_bool == True)', '(c_bool == True) & ((c_extra > 0) & (c_extra < 4))', ] idx_expr = [('c_bool', ('eq',), (True,))] str_expr = 'e0' class IndexedTableUsage4(IndexedTableUsage): conditions = [ '((c_int32 > 0) & (c_bool == True)) & (c_extra > 0)', '((c_int32 > 0) & (c_bool == True)) & ((c_extra > 0)' + ' & (c_extra < 4))', ] idx_expr = [('c_int32', ('gt',), (0,)), ('c_bool', ('eq',), (True,)), ] str_expr = '(e0 & e1)' class IndexedTableUsage5(IndexedTableUsage): conditions = [ '(c_int32 >= 1) & (c_int32 < 2) & (c_bool == True)', '(c_int32 >= 1) & (c_int32 < 2) & (c_bool == True)' + ' & (c_extra > 0)', ] idx_expr = [('c_int32', ('ge', 'lt'), (1, 2)), ('c_bool', ('eq',), (True,)), ] str_expr = '(e0 & e1)' class IndexedTableUsage6(IndexedTableUsage): conditions = [ '(c_int32 >= 1) & (c_int32 < 2) & (c_int32 > 0) & (c_int32 < 5)', '(c_int32 >= 1) & (c_int32 < 2) & (c_int32 > 0) & (c_int32 < 5)' + ' & (c_extra > 0)', ] idx_expr = [('c_int32', ('ge', 'lt'), (1, 2)), ('c_int32', ('gt',), (0,)), ('c_int32', ('lt',), (5,)), ] str_expr = '((e0 & e1) & e2)' class IndexedTableUsage7(IndexedTableUsage): conditions = [ '(c_int32 >= 1) & (c_int32 < 2) & ((c_int32 > 0) & (c_int32 < 5))', '((c_int32 >= 1) & (c_int32 < 2)) & ((c_int32 > 0) & (c_int32 < 5))', '((c_int32 >= 1) & (c_int32 < 2)) & ((c_int32 > 0) & (c_int32 < 5))' + ' & (c_extra > 0)', ] idx_expr = [('c_int32', ('ge', 'lt'), (1, 2)), ('c_int32', ('gt', 'lt'), (0, 5)), ] str_expr = '(e0 & e1)' class IndexedTableUsage8(IndexedTableUsage): conditions = [ '(c_extra > 0) & ((c_int32 > 0) & (c_int32 < 5))', ] idx_expr = [('c_int32', ('gt', 'lt'), (0, 5)), ] str_expr = 'e0' class IndexedTableUsage9(IndexedTableUsage): conditions = [ '(c_extra > 0) & (c_int32 > 0) & (c_int32 < 5)', '((c_extra > 0) & (c_int32 > 0)) & (c_int32 < 5)', '(c_extra > 0) & (c_int32 > 0) & (c_int32 < 5) & (c_extra > 3)', ] idx_expr = [('c_int32', ('gt',), (0,)), ('c_int32', ('lt',), (5,))] str_expr = '(e0 & e1)' class IndexedTableUsage10(IndexedTableUsage): conditions = [ '(c_int32 < 5) & (c_extra > 0) & (c_bool == True)', '(c_int32 < 5) & (c_extra > 2) & c_bool', '(c_int32 < 5) & (c_bool == True) & (c_extra > 0) & (c_extra < 4)', '(c_int32 < 5) & (c_extra > 0) & (c_bool == True) & (c_extra < 4)', ] idx_expr = [('c_int32', ('lt',), (5,)), ('c_bool', ('eq',), (True,))] str_expr = '(e0 & e1)' class IndexedTableUsage11(IndexedTableUsage): """Complex operations are not eligible for indexing.""" conditions = [ 'sin(c_int32) > 0', '(c_int32 * 2.4) > 0', '(c_int32 + c_int32) > 0', 'c_int32**2 > 0', ] idx_expr = [] str_expr = '' class IndexedTableUsage12(IndexedTableUsage): conditions = [ '~c_bool', '~(c_bool)', '~c_bool & (c_extra > 0)', '~(c_bool) & (c_extra > 0)', ] idx_expr = [('c_bool', ('eq',), (False,))] str_expr = 'e0' class IndexedTableUsage13(IndexedTableUsage): conditions = [ '~(c_bool == True)', '~((c_bool == True))', '~(c_bool == True) & (c_extra > 0)', '~((c_bool == True)) & (c_extra > 0)', ] idx_expr = [('c_bool', ('eq',), (False,))] str_expr = 'e0' class IndexedTableUsage14(IndexedTableUsage): conditions = [ '~(c_int32 > 0)', '~((c_int32 > 0)) & (c_extra > 0)', '~(c_int32 > 0) & ((~c_bool) | (c_extra > 0))', '~(c_int32 > 0) & ((c_extra < 3) & (c_extra > 0))', ] idx_expr = [('c_int32', ('le',), (0,))] str_expr = 'e0' class IndexedTableUsage15(IndexedTableUsage): conditions = [ '(~(c_int32 > 0) | ~c_bool)', '(~(c_int32 > 0) | ~(c_bool)) & (c_extra > 0)', '(~(c_int32 > 0) | ~(c_bool == True)) & ((c_extra > 0)' + ' & (c_extra < 4))', ] idx_expr = [('c_int32', ('le',), (0,)), ('c_bool', ('eq',), (False,)), ] str_expr = '(e0 | e1)' class IndexedTableUsage16(IndexedTableUsage): conditions = [ '(~(c_int32 > 0) & ~(c_int32 < 2))', '(~(c_int32 > 0) & ~(c_int32 < 2)) & (c_extra > 0)', '(~(c_int32 > 0) & ~(c_int32 < 2)) & ((c_extra > 0)' + ' & (c_extra < 4))', ] idx_expr = [('c_int32', ('le',), (0,)), ('c_int32', ('ge',), (2,)), ] str_expr = '(e0 & e1)' class IndexedTableUsage17(IndexedTableUsage): conditions = [ '(~(c_int32 > 0) & ~(c_int32 < 2))', '(~(c_int32 > 0) & ~(c_int32 < 2)) & (c_extra > 0)', '(~(c_int32 > 0) & ~(c_int32 < 2)) & ((c_extra > 0)' + ' & (c_extra < 4))', ] idx_expr = [('c_int32', ('le',), (0,)), ('c_int32', ('ge',), (2,)), ] str_expr = '(e0 & e1)' # Negations of complex conditions are not supported yet class IndexedTableUsage18(IndexedTableUsage): conditions = [ '~((c_int32 > 0) & (c_bool))', '~((c_int32 > 0) & (c_bool)) & (c_extra > 0)', '~((c_int32 > 0) & (c_bool)) & ((c_extra > 0)' + ' & (c_extra < 4))', ] idx_expr = [] str_expr = '' class IndexedTableUsage19(IndexedTableUsage): conditions = [ '~((c_int32 > 0) & (c_bool)) & ((c_bool == False)' + ' & (c_extra < 4))', ] idx_expr = [('c_bool', ('eq',), (False,)), ] str_expr = 'e0' class IndexedTableUsage20(IndexedTableUsage): conditions = [ '((c_int32 > 0) & ~(c_bool))', '((c_int32 > 0) & ~(c_bool)) & (c_extra > 0)', '((c_int32 > 0) & ~(c_bool == True)) & ((c_extra > 0) & (c_extra < 4))', ] idx_expr = [('c_int32', ('gt',), (0,)), ('c_bool', ('eq',), (False,)), ] str_expr = '(e0 & e1)' class IndexedTableUsage21(IndexedTableUsage): conditions = [ '(~(c_int32 > 0) & (c_bool))', '(~(c_int32 > 0) & (c_bool)) & (c_extra > 0)', '(~(c_int32 > 0) & (c_bool == True)) & ((c_extra > 0)' + ' & (c_extra < 4))', ] idx_expr = [('c_int32', ('le',), (0,)), ('c_bool', ('eq',), (True,)), ] str_expr = '(e0 & e1)' class IndexedTableUsage22(IndexedTableUsage): conditions = [ '~((c_int32 >= 1) & (c_int32 < 2)) & ~(c_bool == True)', '~(c_bool == True) & (c_extra > 0)', '~((c_int32 >= 1) & (c_int32 < 2)) & (~(c_bool == True)' + ' & (c_extra > 0))', ] idx_expr = [('c_bool', ('eq',), (False,)), ] str_expr = 'e0' class IndexedTableUsage23(IndexedTableUsage): conditions = [ 'c_int32 != 1', 'c_bool != False', '~(c_int32 != 1)', '~(c_bool != False)', '(c_int32 != 1) & (c_extra != 2)', ] idx_expr = [] str_expr = '' class IndexedTableUsage24(IndexedTableUsage): conditions = [ 'c_bool', 'c_bool == True', 'True == c_bool', '~(~c_bool)', '~~c_bool', '~~~~c_bool', '~(~c_bool) & (c_extra != 2)', ] idx_expr = [('c_bool', ('eq',), (True,)), ] str_expr = 'e0' class IndexedTableUsage25(IndexedTableUsage): conditions = [ '~c_bool', 'c_bool == False', 'False == c_bool', '~(c_bool)', '~((c_bool))', '~~~c_bool', '~~(~c_bool) & (c_extra != 2)', ] idx_expr = [ ('c_bool', ('eq',), (False,)), ] str_expr = 'e0' class IndexedTableUsage26(IndexedTableUsage): conditions = [ 'c_bool != True', 'True != c_bool', 'c_bool != False', 'False != c_bool', ] idx_expr = [] str_expr = '' class IndexedTableUsage27(IndexedTableUsage): conditions = [ '(c_int32 == 3) | c_bool | (c_int32 == 5)', '(((c_int32 == 3) | (c_bool == True)) | (c_int32 == 5))' + ' & (c_extra > 0)', ] idx_expr = [ ('c_int32', ('eq',), (3,)), ('c_bool', ('eq',), (True,)), ('c_int32', ('eq',), (5,)), ] str_expr = '((e0 | e1) | e2)' class IndexedTableUsage28(IndexedTableUsage): conditions = [ '((c_int32 == 3) | c_bool) & (c_int32 == 5)', '(((c_int32 == 3) | (c_bool == True)) & (c_int32 == 5))' + ' & (c_extra > 0)', ] idx_expr = [ ('c_int32', ('eq',), (3,)), ('c_bool', ('eq',), (True,)), ('c_int32', ('eq',), (5,)), ] str_expr = '((e0 | e1) & e2)' class IndexedTableUsage29(IndexedTableUsage): conditions = [ '(c_int32 == 3) | ((c_int32 == 4) & (c_int32 == 5))', '((c_int32 == 3) | ((c_int32 == 4) & (c_int32 == 5)))' + ' & (c_extra > 0)', ] idx_expr = [ ('c_int32', ('eq',), (4,)), ('c_int32', ('eq',), (5,)), ('c_int32', ('eq',), (3,)), ] str_expr = '((e0 & e1) | e2)' class IndexedTableUsage30(IndexedTableUsage): conditions = [ '((c_int32 == 3) | (c_int32 == 4)) & (c_int32 == 5)', '((c_int32 == 3) | (c_int32 == 4)) & (c_int32 == 5)' + ' & (c_extra > 0)', ] idx_expr = [ ('c_int32', ('eq',), (3,)), ('c_int32', ('eq',), (4,)), ('c_int32', ('eq',), (5,)), ] str_expr = '((e0 | e1) & e2)' class IndexedTableUsage31(IndexedTableUsage): conditions = [ '(c_extra > 0) & ((c_extra < 4) & (c_bool == True))', '(c_extra > 0) & ((c_bool == True) & (c_extra < 5))', '((c_int32 > 0) | (c_extra > 0)) & (c_bool == True)', ] idx_expr = [ ('c_bool', ('eq',), (True,)), ] str_expr = 'e0' class IndexedTableUsage32(IndexedTableUsage): conditions = [ '(c_int32 < 5) & (c_extra > 0) & (c_bool == True) | (c_extra < 4)', ] idx_expr = [] str_expr = '' # Main part # --------- def suite(): """Return a test suite consisting of all the test cases in the module.""" testSuite = unittest.TestSuite() cdatafuncs = [niclassdata] # non-indexing data tests cdatafuncs.append(iclassdata) # indexing data tests heavy = common.heavy # Choose which tests to run in classes with autogenerated tests. if heavy: autoprefix = 'test' # all tests else: autoprefix = 'test_l' # only light tests niter = 1 for i in range(niter): # Tests on query data. for cdatafunc in cdatafuncs: for cdata in cdatafunc(): class_ = eval(cdata[0]) if heavy or not class_.heavy: suite_ = unittest.makeSuite(class_, prefix=autoprefix) testSuite.addTest(suite_) # Tests on query usage. testSuite.addTest(unittest.makeSuite(ScalarTableUsageTestCase)) testSuite.addTest(unittest.makeSuite(MDTableUsageTestCase)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage1)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage2)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage3)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage4)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage5)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage6)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage7)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage8)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage9)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage10)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage11)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage12)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage13)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage14)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage15)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage16)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage17)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage18)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage19)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage20)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage21)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage22)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage23)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage24)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage25)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage26)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage27)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage28)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage29)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage30)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage31)) testSuite.addTest(unittest.makeSuite(IndexedTableUsage32)) return testSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/test_szip.h5000066400000000000000000000127321231437614300204120ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿÔÿÿÿÿÿÿÿÿ €`HEAP€dset_szipèTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà €˜   ¸¨ èX0àHhhð0и#àð%øÈ(P+@Ø-P`0pè2ˆp5¨ø7È€:à= °ê@ €` ( (szip© ( u‘+APSNODÐTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿã8ç êè ì  @U€Iý *ª“ú(UT'ôxª¨OéAUPŸÓ"ª  ?§…U@QŠª€$þ¨UIýZ*ª“úÈUT'õ¸ª¨OëÁUPŸÐ"ª  ?¡…U@EŠª€$þUIý**ª“úhUT'ôøª¨Oà€ € € € ( € ( € € € € € € € € € € AJª€$þ‡•UIý*ª“úFUT'ô´ª¨Oé¹UPŸÔª  ?©eU@UJª€$þ¯•UIýi*ª“úæUT'ýõð„! á$þƒ•UIý*ª“ú6UT'ô”ª¨OéyUPŸÓ’ª  ?¨eU@@@@„P@P@@@@@@@@@@@ R ª€$þ©UIý\*ª“úÌUT'õÀª¨OëÑUPŸÐBª  ?¡ÅU@F ª€$þ‘UIý,*ª“úlUT'õª¨OêQUPŸÕBª  ?«ÅU@Z ª€$þ¹UIý|*ªÿø@AŠª€$þ( € ( €H €H €H à€ € € € € € € € € € € SJª€$þ«•UIýa*ª“úÖUT'õÔª¨Oûúñ|"„Iý *ª“ú&UT'ôtª¨Oé9UPŸÓª  ?§eU@QJª€$þ§•UIýY*ª“úÆUT'õ´ª¨Oë¹UPŸÐª  ?¡eU@P@@c„@ $@ $@ $Ð@@@@@@@@@@@ PyTables-v.3.1.1/tables/tests/test_tables.py000066400000000000000000007517761231437614300210350ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import sys import unittest import os import tempfile import warnings import numpy as np from numpy import rec as records from numpy import testing as npt import tables from tables import * from tables.utils import SizeType, byteorders from tables.tests import common from tables.tests.common import allequal, areArraysEqual from tables.description import descr_from_dtype # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup # Test Record class class Record(IsDescription): var1 = StringCol(itemsize=4, dflt=b"abcd", pos=0) # 4-character String var2 = IntCol(dflt=1, pos=1) # integer var3 = Int16Col(dflt=2, pos=2) # short integer var4 = Float64Col(dflt=3.1, pos=3) # double (double-precision) var5 = Float32Col(dflt=4.2, pos=4) # float (single-precision) var6 = UInt16Col(dflt=5, pos=5) # unsigned short integer var7 = StringCol(itemsize=1, dflt=b"e", pos=6) # 1-character String var8 = BoolCol(dflt=True, pos=7) # boolean var9 = ComplexCol(itemsize=8, dflt=( 0.+1.j), pos=8) # Complex single precision var10 = ComplexCol(itemsize=16, dflt=( 1.-0.j), pos=9) # Complex double precision if 'Float16Col' in globals(): var11 = Float16Col(dflt=6.4) # float (half-precision) if 'Float96Col' in globals(): var12 = Float96Col( dflt=6.4) # float (extended precision) if 'Float128Col' in globals(): var13 = Float128Col( dflt=6.4) # float (extended precision) if 'Complex192Col' in globals(): var14 = ComplexCol(itemsize=24, dflt=( 1.-0.j)) # Complex double (extended precision) if 'Complex256Col' in globals(): var15 = ComplexCol(itemsize=32, dflt=( 1.-0.j)) # Complex double (extended precision) # Dictionary definition RecordDescriptionDict = { 'var1': StringCol(itemsize=4, dflt=b"abcd", pos=0), # 4-character String 'var2': IntCol(dflt=1, pos=1), # integer 'var3': Int16Col(dflt=2, pos=2), # short integer 'var4': Float64Col(dflt=3.1, pos=3), # double (double-precision) 'var5': Float32Col(dflt=4.2, pos=4), # float (single-precision) 'var6': UInt16Col(dflt=5, pos=5), # unsigned short integer 'var7': StringCol(itemsize=1, dflt=b"e", pos=6), # 1-character String 'var8': BoolCol(dflt=True, pos=7), # boolean 'var9': ComplexCol(itemsize=8, dflt=(0.+1.j), pos=8), # Complex single precision 'var10': ComplexCol(itemsize=16, dflt=(1.-0.j), pos=9), # Complex double precision } if 'Float16Col' in globals(): RecordDescriptionDict['var11'] = Float16Col( dflt=6.4) # float (half-precision) if 'Float96Col' in globals(): RecordDescriptionDict['var12'] = Float96Col( dflt=6.4) # float (extended precision) if 'Float128Col' in globals(): RecordDescriptionDict['var13'] = Float128Col( dflt=6.4) # float (extended precision) if 'Complex192Col' in globals(): RecordDescriptionDict['var14'] = ComplexCol(itemsize=24, dflt=( 1.-0.j)) # Complex double (extended precision) if 'Complex256Col' in globals(): RecordDescriptionDict['var15'] = ComplexCol(itemsize=32, dflt=( 1.-0.j)) # Complex double (extended precision) # Old fashion of defining tables (for testing backward compatibility) class OldRecord(IsDescription): var1 = StringCol(itemsize=4, dflt=b"abcd", pos=0) var2 = Col.from_type("int32", (), 1, pos=1) var3 = Col.from_type("int16", (), 2, pos=2) var4 = Col.from_type("float64", (), 3.1, pos=3) var5 = Col.from_type("float32", (), 4.2, pos=4) var6 = Col.from_type("uint16", (), 5, pos=5) var7 = StringCol(itemsize=1, dflt=b"e", pos=6) var8 = Col.from_type("bool", shape=(), dflt=1, pos=7) var9 = ComplexCol(itemsize=8, shape=(), dflt=(0.+1.j), pos=8) var10 = ComplexCol(itemsize=16, shape=(), dflt=(1.-0.j), pos = 9) if 'Float16Col' in globals(): var11 = Col.from_type("float16", (), 6.4) if 'Float96Col' in globals(): var12 = Col.from_type("float96", (), 6.4) if 'Float128Col' in globals(): var13 = Col.from_type("float128", (), 6.4) if 'Complex192Col' in globals(): var14 = ComplexCol(itemsize=24, shape=(), dflt=(1.-0.j)) if 'Complex256Col' in globals(): var15 = ComplexCol(itemsize=32, shape=(), dflt=(1.-0.j)) class BasicTestCase(common.PyTablesTestCase): # file = "test.h5" mode = "w" title = "This is the table title" expectedrows = 100 appendrows = 20 compress = 0 shuffle = 0 fletcher32 = 0 complib = "zlib" # Default compression library record = Record recarrayinit = 0 maxshort = 1 << 15 def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, self.mode) self.rootgroup = self.fileh.root self.populateFile() self.fileh.close() def initRecArray(self): record = self.recordtemplate row = record[0] buflist = [] # Fill the recarray for i in xrange(self.expectedrows): tmplist = [] var1 = '%04d' % (self.expectedrows - i) tmplist.append(var1) var2 = i tmplist.append(var2) var3 = i % self.maxshort tmplist.append(var3) if isinstance(row['var4'], np.ndarray): tmplist.append([float(i), float(i * i)]) else: tmplist.append(float(i)) if isinstance(row['var5'], np.ndarray): tmplist.append(np.array((float(i),)*4)) else: tmplist.append(float(i)) # var6 will be like var3 but byteswaped tmplist.append(((var3 >> 8) & 0xff) + ((var3 << 8) & 0xff00)) var7 = var1[-1] tmplist.append(var7) if isinstance(row['var8'], np.ndarray): tmplist.append([0, 10]) # should be equivalent to [0,1] else: tmplist.append(10) # should be equivalent to 1 if isinstance(row['var9'], np.ndarray): tmplist.append([0.+float(i)*1j, float(i)+0.j]) else: tmplist.append(float(i)+0j) if isinstance(row['var10'], np.ndarray): tmplist.append([float(i)+0j, 1 + float(i)*1j]) else: tmplist.append(1 + float(i)*1j) if 'Float16Col' in globals(): if isinstance(row['var11'], np.ndarray): tmplist.append(np.array((float(i),)*4)) else: tmplist.append(float(i)) if 'Float96Col' in globals(): if isinstance(row['var12'], np.ndarray): tmplist.append(np.array((float(i),)*4)) else: tmplist.append(float(i)) if 'Float128Col' in globals(): if isinstance(row['var13'], np.ndarray): tmplist.append(np.array((float(i),)*4)) else: tmplist.append(float(i)) if 'Complex192Col' in globals(): if isinstance(row['var14'], np.ndarray): tmplist.append([float(i)+0j, 1 + float(i)*1j]) else: tmplist.append(1 + float(i)*1j) if 'Complex256Col' in globals(): if isinstance(row['var15'], np.ndarray): tmplist.append([float(i)+0j, 1 + float(i)*1j]) else: tmplist.append(1 + float(i)*1j) buflist.append(tmplist) self.record = records.array(buflist, dtype=record.dtype, shape=self.expectedrows) def populateFile(self): group = self.rootgroup if self.recarrayinit: # Initialize an starting buffer, if any self.initRecArray() for j in range(3): # Create a table filterprops = Filters(complevel=self.compress, shuffle=self.shuffle, fletcher32=self.fletcher32, complib=self.complib) if j < 2: byteorder = sys.byteorder else: # table2 will be byteswapped byteorder = {"little": "big", "big": "little"}[sys.byteorder] table = self.fileh.create_table(group, 'table'+str(j), self.record, title=self.title, filters=filterprops, expectedrows=self.expectedrows, byteorder=byteorder) if not self.recarrayinit: # Get the row object associated with the new table row = table.row # Fill the table for i in xrange(self.expectedrows): s = '%04d' % (self.expectedrows - i) row['var1'] = s.encode('ascii') row['var7'] = s[-1].encode('ascii') # row['var7'] = ('%04d' % (self.expectedrows - i))[-1] row['var2'] = i row['var3'] = i % self.maxshort if isinstance(row['var4'], np.ndarray): row['var4'] = [float(i), float(i * i)] else: row['var4'] = float(i) if isinstance(row['var8'], np.ndarray): row['var8'] = [0, 1] else: row['var8'] = 1 if isinstance(row['var9'], np.ndarray): row['var9'] = [0.+float(i)*1j, float(i)+0.j] else: row['var9'] = float(i)+0.j if isinstance(row['var10'], np.ndarray): row['var10'] = [float(i)+0.j, 1.+float(i)*1j] else: row['var10'] = 1.+float(i)*1j if isinstance(row['var5'], np.ndarray): row['var5'] = np.array((float(i),)*4) else: row['var5'] = float(i) if 'Float16Col' in globals(): if isinstance(row['var11'], np.ndarray): row['var11'] = np.array((float(i),)*4) else: row['var11'] = float(i) if 'Float96Col' in globals(): if isinstance(row['var12'], np.ndarray): row['var12'] = np.array((float(i),)*4) else: row['var12'] = float(i) if 'Float128Col' in globals(): if isinstance(row['var13'], np.ndarray): row['var13'] = np.array((float(i),)*4) else: row['var13'] = float(i) if 'Complex192Col' in globals(): if isinstance(row['var14'], np.ndarray): row['var14'] = [float(i)+0j, 1 + float(i)*1j] else: row['var14'] = 1 + float(i)*1j if 'Complex256Col' in globals(): if isinstance(row['var15'], np.ndarray): row['var15'] = [float(i)+0j, 1 + float(i)*1j] else: row['var15'] = 1 + float(i)*1j # var6 will be like var3 but byteswaped row['var6'] = (((row['var3'] >> 8) & 0xff) + ((row['var3'] << 8) & 0xff00)) # print("Saving -->", row) row.append() # Flush the buffer for this table table.flush() # Create a new group (descendant of group) group2 = self.fileh.create_group(group, 'group'+str(j)) # Iterate over this new group (group2) group = group2 def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test00_description(self): """Checking table description and descriptive fields.""" self.fileh = open_file(self.file) tbl = self.fileh.get_node('/table0') desc = tbl.description if isinstance(self.record, dict): columns = self.record elif isinstance(self.record, np.ndarray): descr, _ = descr_from_dtype(self.record.dtype) columns = descr._v_colobjects elif isinstance(self.record, np.dtype): descr, _ = descr_from_dtype(self.record) columns = descr._v_colobjects else: # This is an ordinary description. columns = self.record.columns # Check table and description attributes at the same time. # These checks are only valid for non-nested tables. # Column names. fix_n_column = 10 expectedNames = ['var%d' % n for n in range(1, fix_n_column + 1)] types = ("float16", "float96", "float128", "complex192", "complex256") for n, typename in enumerate(types, fix_n_column + 1): name = typename.capitalize() + 'Col' if name in globals(): expectedNames.append('var%d' % n) self.assertEqual(expectedNames, list(tbl.colnames)) self.assertEqual(expectedNames, list(desc._v_names)) # Column instances. for colname in expectedNames: self.assertTrue(tbl.colinstances[colname] is tbl.cols._f_col(colname)) # Column types. expectedTypes = [columns[colname].dtype for colname in expectedNames] self.assertEqual(expectedTypes, [tbl.coldtypes[v] for v in expectedNames]) self.assertEqual(expectedTypes, [desc._v_dtypes[v] for v in expectedNames]) # Column string types. expectedTypes = [columns[colname].type for colname in expectedNames] self.assertEqual(expectedTypes, [tbl.coltypes[v] for v in expectedNames]) self.assertEqual(expectedTypes, [desc._v_types[v] for v in expectedNames]) # Column defaults. for v in expectedNames: if common.verbose: print("dflt-->", columns[v].dflt, type(columns[v].dflt)) print("coldflts-->", tbl.coldflts[v], type(tbl.coldflts[v])) print("desc.dflts-->", desc._v_dflts[v], type(desc._v_dflts[v])) self.assertTrue(areArraysEqual(tbl.coldflts[v], columns[v].dflt)) self.assertTrue(areArraysEqual(desc._v_dflts[v], columns[v].dflt)) # Column path names. self.assertEqual(expectedNames, list(desc._v_pathnames)) # Column objects. for colName in expectedNames: expectedCol = columns[colName] col = desc._v_colobjects[colName] self.assertEqual(expectedCol.dtype, col.dtype) self.assertEqual(expectedCol.type, col.type) def test01_readTable(self): """Checking table read.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_readTable..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") # Choose a small value for buffer size table.nrowsinbuf = 3 # Read the records and select those with "var2" file less than 20 result = [rec['var2'] for rec in table.iterrows() if rec['var2'] < 20] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Last record in table ==>", rec) print("Total selected records in table ==> ", len(result)) nrows = self.expectedrows - 1 rec = list(table.iterrows())[-1] self.assertEqual((rec['var1'], rec['var2'], rec['var7']), (b"0001", nrows, b"1")) if isinstance(rec['var5'], np.ndarray): self.assertTrue(allequal(rec['var5'], np.array((float(nrows),)*4, np.float32))) else: self.assertEqual(rec['var5'], float(nrows)) if isinstance(rec['var9'], np.ndarray): self.assertTrue( allequal(rec['var9'], np.array([0.+float(nrows)*1.j, float(nrows)+0.j], np.complex64))) else: self.assertEqual((rec['var9']), float(nrows)+0.j) self.assertEqual(len(result), 20) def test01a_fetch_all_fields(self): """Checking table read (using Row.fetch_all_fields)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01a_fetch_all_fields..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") # Choose a small value for buffer size table.nrowsinbuf = 3 # Read the records and select those with "var2" file less than 20 result = [rec.fetch_all_fields() for rec in table.iterrows() if rec['var2'] < 20] rec = result[-1] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Last record in table ==>", rec) print("Total selected records in table ==> ", len(result)) nrows = 20 - 1 strnrows = "%04d" % (self.expectedrows - nrows) strnrows = strnrows.encode('ascii') self.assertEqual((rec['var1'], rec['var2'], rec['var7']), (strnrows, nrows, b"1")) if isinstance(rec['var5'], np.ndarray): self.assertTrue(allequal(rec['var5'], np.array((float(nrows),)*4, np.float32))) else: self.assertEqual(rec['var5'], float(nrows)) if isinstance(rec['var9'], np.ndarray): self.assertTrue( allequal(rec['var9'], np.array([0.+float(nrows)*1.j, float(nrows)+0.j], np.complex64))) else: self.assertEqual(rec['var9'], float(nrows)+0.j) self.assertEqual(len(result), 20) def test01a_integer(self): """Checking table read (using Row[integer])""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01a_integer..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") # Choose a small value for buffer size table.nrowsinbuf = 3 # Read the records and select those with "var2" file less than 20 result = [rec[1] for rec in table.iterrows() if rec['var2'] < 20] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Total selected records in table ==> ", len(result)) print("All results ==>", result) self.assertEqual(len(result), 20) self.assertEqual(result, range(20)) def test01a_extslice(self): """Checking table read (using Row[::2])""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01a_extslice..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") # Choose a small value for buffer size table.nrowsinbuf = 3 # Read the records and select those with "var2" file less than 20 result = [rec[::2] for rec in table.iterrows() if rec['var2'] < 20] rec = result[-1] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Last record in table ==>", rec) print("Total selected records in table ==> ", len(result)) nrows = 20 - 1 strnrows = "%04d" % (self.expectedrows - nrows) strnrows = strnrows.encode('ascii') self.assertEqual(rec[:2], (strnrows, 19)) self.assertEqual(rec[3], b'1') if isinstance(rec[2], np.ndarray): self.assertTrue(allequal(rec[2], np.array((float(nrows),)*4, np.float32))) else: self.assertEqual(rec[2], nrows) if isinstance(rec[4], np.ndarray): self.assertTrue( allequal(rec[4], np.array([0.+float(nrows)*1.j, float(nrows)+0.j], np.complex64))) else: self.assertEqual(rec[4], float(nrows)+0.j) self.assertEqual(len(result), 20) def test01a_nofield(self): """Checking table read (using Row['no-field'])""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01a_nofield..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") # Check that a KeyError is raised # self.assertRaises only work with functions # self.assertRaises(KeyError, [rec['no-field'] for rec in table]) try: result = [rec['no-field'] for rec in table] except KeyError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next KeyError was catched!") print(value) else: print(result) self.fail("expected a KeyError") def test01a_badtypefield(self): """Checking table read (using Row[{}])""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01a_badtypefield..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") # Check that a TypeError is raised # self.assertRaises only work with functions # self.assertRaises(TypeError, [rec[{}] for rec in table]) try: result = [rec[{}] for rec in table] except TypeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next TypeError was catched!") print(value) else: print(result) self.fail("expected a TypeError") def test01b_readTable(self): """Checking table read and cuts (multidimensional columns case)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01b_readTable..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") # Choose a small value for buffer size table.nrowsinbuf = 3 # Read the records and select those with "var2" file less than 20 result = [rec['var5'] for rec in table.iterrows() if rec['var2'] < 20] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Last record in table ==>", rec) print("rec['var5'] ==>", rec['var5'], end=' ') print("nrows ==>", table.nrows) print("Total selected records in table ==> ", len(result)) nrows = table.nrows rec = list(table.iterrows())[-1] if isinstance(rec['var5'], np.ndarray): npt.assert_array_equal(result[0], np.array((float(0),)*4, np.float32)) npt.assert_array_equal(result[1], np.array((float(1),)*4, np.float32)) npt.assert_array_equal(result[2], np.array((float(2),)*4, np.float32)) npt.assert_array_equal(result[3], np.array((float(3),)*4, np.float32)) npt.assert_array_equal(result[10], np.array((float(10),)*4, np.float32)) npt.assert_array_equal(rec['var5'], np.array((float(nrows-1),)*4, np.float32)) else: self.assertEqual(rec['var5'], float(nrows - 1)) # Read the records and select those with "var2" file less than 20 result = [rec['var10'] for rec in table.iterrows() if rec['var2'] < 20] if isinstance(rec['var10'], np.ndarray): npt.assert_array_equal( result[0], np.array([float(0)+0.j, 1.+float(0)*1j], np.complex128)) npt.assert_array_equal( result[1], np.array([float(1)+0.j, 1.+float(1)*1j], np.complex128)) npt.assert_array_equal( result[2], np.array([float(2)+0.j, 1.+float(2)*1j], np.complex128)) npt.assert_array_equal( result[3], np.array([float(3)+0.j, 1.+float(3)*1j], np.complex128)) npt.assert_array_equal( result[10], np.array([float(10)+0.j, 1.+float(10)*1j], np.complex128)) npt.assert_array_equal( rec['var10'], np.array([float(nrows-1)+0.j, 1.+float(nrows-1)*1j], np.complex128)) else: self.assertEqual(rec['var10'], 1.+float(nrows-1)*1j) self.assertEqual(len(result), 20) def test01c_readTable(self): """Checking nested iterators (reading)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01c_readTable..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") # Read the records and select those with "var2" file less than 20 result = [] for rec in table.iterrows(stop=2): for rec2 in table.iterrows(stop=2): if rec2['var2'] < 20: result.append([rec['var2'], rec2['var2']]) if common.verbose: print("result ==>", result) self.assertEqual(result, [[0, 0], [0, 1], [1, 0], [1, 1]]) def test01d_readTable(self): """Checking nested iterators (reading, mixed conditions)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01d_readTable..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") # Read the records and select those with "var2" file less than 20 result = [] for rec in table.iterrows(stop=2): for rec2 in table.where('var2 < 20', stop=2): result.append([rec['var2'], rec2['var2']]) if common.verbose: print("result ==>", result) self.assertEqual(result, [[0, 0], [0, 1], [1, 0], [1, 1]]) def test01e_readTable(self): """Checking nested iterators (reading, both conditions)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01e_readTable..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") # Read the records and select those with "var2" file less than 20 result = [] for rec in table.where('var3 < 2'): for rec2 in table.where('var2 < 3'): result.append([rec['var2'], rec2['var3']]) if common.verbose: print("result ==>", result) self.assertEqual(result, [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2]]) def test01f_readTable(self): """Checking nested iterators (reading, break in the loop)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01f_readTable..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") # Read the records and select those with "var2" file less than 20 result = [] for rec in table.where('var3 < 2'): for rec2 in table.where('var2 < 4'): if rec2['var2'] >= 3: break result.append([rec['var2'], rec2['var3']]) if common.verbose: print("result ==>", result) self.assertEqual(result, [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2]]) def test01g_readTable(self): """Checking iterator with an evanescent table.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01g_readTable..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") # Read from an evanescent table result = [rec['var2'] for rec in self.fileh.get_node("/table0") if rec['var2'] < 20] self.assertEqual(len(result), 20) def test02_AppendRows(self): """Checking whether appending record rows works or not.""" # Now, open it, but in "append" mode self.fileh = open_file(self.file, mode="a") self.rootgroup = self.fileh.root if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_AppendRows..." % self.__class__.__name__) # Get a table table = self.fileh.get_node("/group0/table1") # Get their row object row = table.row if common.verbose: print("Nrows in old", table._v_pathname, ":", table.nrows) print("Record Format ==>", table.description._v_nested_formats) print("Record Size ==>", table.rowsize) # Append some rows for i in xrange(self.appendrows): s = '%04d' % (self.appendrows - i) row['var1'] = s.encode('ascii') row['var7'] = s[-1].encode('ascii') row['var2'] = i row['var3'] = i % self.maxshort if isinstance(row['var4'], np.ndarray): row['var4'] = [float(i), float(i * i)] else: row['var4'] = float(i) if isinstance(row['var8'], np.ndarray): row['var8'] = [0, 1] else: row['var8'] = 1 if isinstance(row['var9'], np.ndarray): row['var9'] = [0.+float(i)*1j, float(i)+0.j] else: row['var9'] = float(i)+0.j if isinstance(row['var10'], np.ndarray): row['var10'] = [float(i)+0.j, 1.+float(i)*1j] else: row['var10'] = 1.+float(i)*1j if isinstance(row['var5'], np.ndarray): row['var5'] = np.array((float(i),)*4) else: row['var5'] = float(i) if 'Float16Col' in globals(): if isinstance(row['var11'], np.ndarray): row['var11'] = np.array((float(i),)*4) else: row['var11'] = float(i) if 'Float96Col' in globals(): if isinstance(row['var12'], np.ndarray): row['var12'] = np.array((float(i),)*4) else: row['var12'] = float(i) if 'Float128Col' in globals(): if isinstance(row['var13'], np.ndarray): row['var13'] = np.array((float(i),)*4) else: row['var13'] = float(i) if 'Complex192Col' in globals(): if isinstance(row['var14'], np.ndarray): row['var14'] = [float(i)+0j, 1 + float(i)*1j] else: row['var14'] = 1 + float(i)*1j if 'Complex256Col' in globals(): if isinstance(row['var15'], np.ndarray): row['var15'] = [float(i)+0j, 1 + float(i)*1j] else: row['var15'] = 1 + float(i)*1j row.append() # Flush the buffer for this table and read it table.flush() result = [row['var2'] for row in table.iterrows() if row['var2'] < 20] nrows = self.appendrows - 1 row = list(table.iterrows())[-1] self.assertEqual((row['var1'], row['var2'], row['var7']), (b"0001", nrows, b"1")) if isinstance(row['var5'], np.ndarray): self.assertTrue(allequal(row['var5'], np.array((float(nrows),)*4, np.float32))) else: self.assertEqual(row['var5'], float(nrows)) if self.appendrows <= 20: add = self.appendrows else: add = 20 self.assertEqual(len(result), 20 + add) # because we appended new rows # This test has been commented out because appending records without # flushing them explicitely is being warned from now on. # F. Alted 2006-08-03 def _test02a_AppendRows(self): """Checking appending records without flushing explicitely.""" # Now, open it, but in "append" mode self.fileh = open_file(self.file, mode="a") self.rootgroup = self.fileh.root if common.verbose: print('\n', '-=' * 30) print("Running %s.test02a_AppendRows..." % self.__class__.__name__) group = self.rootgroup for i in range(3): # Get a table table = self.fileh.get_node(group, 'table'+str(i)) # Get the next group group = self.fileh.get_node(group, 'group'+str(i)) # Get their row object row = table.row if common.verbose: print("Nrows in old", table._v_pathname, ":", table.nrows) print("Record Format ==>", table.description._v_nested_formats) print("Record Size ==>", table.rowsize) # Append some rows for i in xrange(self.appendrows): row['var1'] = '%04d' % (self.appendrows - i) row['var7'] = row['var1'][-1] row['var2'] = i row['var3'] = i % self.maxshort if isinstance(row['var4'], np.ndarray): row['var4'] = [float(i), float(i * i)] else: row['var4'] = float(i) if isinstance(row['var8'], np.ndarray): row['var8'] = [0, 1] else: row['var8'] = 1 if isinstance(row['var9'], np.ndarray): row['var9'] = [0.+float(i)*1j, float(i)+0.j] else: row['var9'] = float(i)+0.j if isinstance(row['var10'], np.ndarray): row['var10'] = [float(i)+0.j, 1.+float(i)*1j] else: row['var10'] = 1.+float(i)*1j if isinstance(row['var5'], np.ndarray): row['var5'] = np.array((float(i),)*4) else: row['var5'] = float(i) if 'Float16Col' in globals(): if isinstance(row['var11'], np.ndarray): row['var11'] = np.array((float(i),)*4) else: row['var11'] = float(i) if 'Float96Col' in globals(): if isinstance(row['var12'], np.ndarray): row['var12'] = np.array((float(i),)*4) else: row['var12'] = float(i) if 'Float128Col' in globals(): if isinstance(row['var13'], np.ndarray): row['var13'] = np.array((float(i),)*4) else: row['var13'] = float(i) if 'Complex192Col' in globals(): if isinstance(row['var14'], np.ndarray): row['var14'] = [float(i)+0j, 1 + float(i)*1j] else: row['var14'] = 1 + float(i)*1j if 'Complex256Col' in globals(): if isinstance(row['var15'], np.ndarray): row['var15'] = [float(i)+0j, 1 + float(i)*1j] else: row['var15'] = 1 + float(i)*1j row.append() table.flush() # Close the file and re-open it. self.fileh.close() self.fileh = open_file(self.file, mode="a") table = self.fileh.root.table0 # Flush the buffer for this table and read it result = [row['var2'] for row in table.iterrows() if row['var2'] < 20] nrows = self.appendrows - 1 self.assertEqual((row['var1'], row['var2'], row['var7']), ("0001", nrows, "1")) if isinstance(row['var5'], np.ndarray): self.assertTrue(allequal(row['var5'], np.array((float(nrows),)*4, np.float32))) else: self.assertEqual(row['var5'], float(nrows)) if self.appendrows <= 20: add = self.appendrows else: add = 20 self.assertEqual(len(result), 20 + add) # because we appended new rows def test02b_AppendRows(self): """Checking whether appending *and* reading rows works or not""" # Now, open it, but in "append" mode self.fileh = open_file(self.file, mode="a") self.rootgroup = self.fileh.root if common.verbose: print('\n', '-=' * 30) print("Running %s.test02b_AppendRows..." % self.__class__.__name__) # Get a table table = self.fileh.get_node("/group0/table1") if common.verbose: print("Nrows in old", table._v_pathname, ":", table.nrows) print("Record Format ==>", table.description._v_nested_formats) print("Record Size ==>", table.rowsize) # Set a small number of buffer to make this test faster table.nrowsinbuf = 3 # Get their row object row = table.row # Append some rows (3 * table.nrowsinbuf is enough for # checking purposes) for i in xrange(3 * table.nrowsinbuf): s = '%04d' % (self.appendrows - i) row['var1'] = s.encode('ascii') row['var7'] = s[-1].encode('ascii') # row['var7'] = table.cols['var1'][i][-1] row['var2'] = i row['var3'] = i % self.maxshort if isinstance(row['var4'], np.ndarray): row['var4'] = [float(i), float(i * i)] else: row['var4'] = float(i) if isinstance(row['var8'], np.ndarray): row['var8'] = [0, 1] else: row['var8'] = 1 if isinstance(row['var9'], np.ndarray): row['var9'] = [0.+float(i)*1j, float(i)+0.j] else: row['var9'] = float(i)+0.j if isinstance(row['var10'], np.ndarray): row['var10'] = [float(i)+0.j, 1.+float(i)*1j] else: row['var10'] = 1.+float(i)*1j if isinstance(row['var5'], np.ndarray): row['var5'] = np.array((float(i),)*4) else: row['var5'] = float(i) if 'Float16Col' in globals(): if isinstance(row['var11'], np.ndarray): row['var11'] = np.array((float(i),)*4) else: row['var11'] = float(i) if 'Float96Col' in globals(): if isinstance(row['var12'], np.ndarray): row['var12'] = np.array((float(i),)*4) else: row['var12'] = float(i) if 'Float128Col' in globals(): if isinstance(row['var13'], np.ndarray): row['var13'] = np.array((float(i),)*4) else: row['var13'] = float(i) if 'Complex192Col' in globals(): if isinstance(row['var14'], np.ndarray): row['var14'] = [float(i)+0j, 1 + float(i)*1j] else: row['var14'] = 1 + float(i)*1j if 'Complex256Col' in globals(): if isinstance(row['var15'], np.ndarray): row['var15'] = [float(i)+0j, 1 + float(i)*1j] else: row['var15'] = 1 + float(i)*1j row.append() # the next call can mislead the counters result = [row2['var2'] for row2 in table] # warning! the next will result into wrong results # result = [ row['var2'] for row in table ] # This is because the iterator for writing and for reading # cannot be shared! # Do not flush the buffer for this table and try to read it # We are forced now to flush tables after append operations # because of unsolved issues with the LRU cache that are too # difficult to track. # F. Alted 2006-08-03 table.flush() result = [row['var2'] for row in table.iterrows() if row['var2'] < 20] if common.verbose: print("Result length ==>", len(result)) print("Result contents ==>", result) self.assertEqual(len(result), 20 + 3 * table.nrowsinbuf) self.assertEqual(result, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8]) # Check consistency of I/O buffers when doing mixed I/O operations # That is, the next should work in these operations # row['var1'] = '%04d' % (self.appendrows - i) # row['var7'] = row['var1'][-1] result7 = [row['var7'] for row in table.iterrows() if row['var2'] < 20] if common.verbose: print("Result7 length ==>", len(result7)) print("Result7 contents ==>", result7) self.assertEqual( result7, [b'0', b'9', b'8', b'7', b'6', b'5', b'4', b'3', b'2', b'1', b'0', b'9', b'8', b'7', b'6', b'5', b'4', b'3', b'2', b'1', b'0', b'9', b'8', b'7', b'6', b'5', b'4', b'3', b'2']) # This test is commented out as it should not work anymore due to # the new policy of not doing a flush in the middle of a __del__ # operation. F. Alted 2006-08-24 def _test02c_AppendRows(self): """Checking appending with evanescent table objects.""" # This test is kind of magic, but it is a good sanity check anyway. # Now, open it, but in "append" mode self.fileh = open_file(self.file, mode="a") self.rootgroup = self.fileh.root if common.verbose: print('\n', '-=' * 30) print("Running %s.test02c_AppendRows..." % self.__class__.__name__) # Get a table table = self.fileh.get_node("/group0/table1") if common.verbose: print("Nrows in old", table._v_pathname, ":", table.nrows) print("Record Format ==>", table.description._v_nested_formats) print("Record Size ==>", table.rowsize) # Set a small number of buffer to make this test faster table.nrowsinbuf = 3 # Get their row object self.row = table.row # delete the table reference del table # Append some rows for i in xrange(22): self.row['var2'] = 100 + i self.row.append() # del self.row # force the table object to be destroyed (and the # user warned!) # convert a warning in an error warnings.filterwarnings('error', category=PerformanceWarning) self.assertRaises(PerformanceWarning, self.__dict__.pop, 'row') # try: # # force the table object to be destroyed # self.__dict__.pop('row') # except PerformanceWarning: # if common.verbose: # (type, value, traceback) = sys.exc_info() # print "\nGreat!, the next PerformanceWarning was catched:" # print value # # Ignore the warning and actually flush the table # warnings.filterwarnings("ignore", category=PerformanceWarning) # table = self.fileh.get_node("/group0/table1") # table.flush() # else: # self.fail("expected a PeformanceWarning") # reset the warning warnings.filterwarnings('default', category=PerformanceWarning) result = [row['var2'] for row in table.iterrows() if 100 <= row['var2'] < 122] if common.verbose: print("Result length ==>", len(result)) print("Result contents ==>", result) self.assertEqual(len(result), 22) self.assertEqual( result, [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121]) def test02d_AppendRows(self): """Checking appending using the same Row object after flushing.""" # This test is kind of magic, but it is a good sanity check anyway. # Now, open it, but in "append" mode self.fileh = open_file(self.file, mode="a") self.rootgroup = self.fileh.root if common.verbose: print('\n', '-=' * 30) print("Running %s.test02d_AppendRows..." % self.__class__.__name__) # Get a table table = self.fileh.get_node("/group0/table1") if common.verbose: print("Nrows in old", table._v_pathname, ":", table.nrows) print("Record Format ==>", table.description._v_nested_formats) print("Record Size ==>", table.rowsize) # Set a small number of buffer to make this test faster table.nrowsinbuf = 3 # Get their row object row = table.row # Append some rows for i in xrange(10): row['var2'] = 100 + i row.append() # Force a flush table.flush() # Add new rows for i in xrange(9): row['var2'] = 110 + i row.append() table.flush() # XXX al eliminar... result = [row['var2'] for row in table.iterrows() if 100 <= row['var2'] < 120] if common.verbose: print("Result length ==>", len(result)) print("Result contents ==>", result) if table.nrows > 119: # Case for big tables self.assertEqual(len(result), 39) self.assertEqual(result, [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118]) else: self.assertEqual(len(result), 19) self.assertEqual(result, [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118]) def test02e_AppendRows(self): """Checking appending using the Row of an unreferenced table.""" # See ticket #94 (http://www.pytables.org/trac/ticket/94). # Reopen the file in append mode. self.fileh = open_file(self.file, mode='a') # Get the row handler which will outlive the reference to the table. table = self.fileh.get_node('/group0/table1') oldnrows = table.nrows row = table.row # Few appends are made to avoid flushing the buffers in ``row``. # First case: append to an alive (referenced) table. row.append() table.flush() newnrows = table.nrows self.assertEqual(newnrows, oldnrows + 1, "Append to alive table failed.") if self.fileh._node_manager.cache.nslots == 0: # Skip this test from here on because the second case # won't work when thereis not a node cache. return # Second case: append to a dead (unreferenced) table. del table row.append() table = self.fileh.get_node('/group0/table1') table.flush() newnrows = table.nrows self.assertEqual(newnrows, oldnrows + 2, "Append to dead table failed.") # CAVEAT: The next test only works for tables with rows < 2**15 def test03_endianess(self): """Checking if table is endianess aware.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_endianess..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/group0/group1/table2") # Read the records and select the ones with "var3" column less than 20 result = [rec['var2'] for rec in table.iterrows() if rec['var3'] < 20] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("On-disk byteorder ==>", table.byteorder) print("Last record in table ==>", rec) print("Selected records ==>", result) print("Total selected records in table ==>", len(result)) nrows = self.expectedrows - 1 self.assertEqual(table.byteorder, {"little": "big", "big": "little"}[sys.byteorder]) rec = list(table.iterrows())[-1] self.assertEqual((rec['var1'], rec['var3']), (b"0001", nrows)) self.assertEqual(len(result), 20) def test04_delete(self): """Checking whether a single row can be deleted.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_delete..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "a") table = self.fileh.get_node("/table0") # Read the records and select the ones with "var2" column less than 20 result = [r['var2'] for r in table.iterrows() if r['var2'] < 20] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Last selected value ==>", result[-1]) print("Total selected records in table ==>", len(result)) nrows = table.nrows table.nrowsinbuf = 3 # small value of the buffer # Delete the twenty-th row table.remove_rows(19, 20) # Re-read the records result2 = [r['var2'] for r in table.iterrows() if r['var2'] < 20] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Last selected value ==>", result2[-1]) print("Total selected records in table ==>", len(result2)) self.assertEqual(table.nrows, nrows - 1) self.assertEqual(table.shape, (nrows - 1,)) # Check that the new list is smaller than the original one self.assertEqual(len(result), len(result2) + 1) self.assertEqual(result[:-1], result2) def test04a_delete(self): """Checking whether a single row can be deleted.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_delete..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "a") table = self.fileh.get_node("/table0") # Read the records and select the ones with "var2" column less than 20 result = [r['var2'] for r in table.iterrows() if r['var2'] < 20] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Last selected value ==>", result[-1]) print("Total selected records in table ==>", len(result)) nrows = table.nrows table.nrowsinbuf = 3 # small value of the buffer # Delete the twenty-th row table.remove_row(19) # Re-read the records result2 = [r['var2'] for r in table.iterrows() if r['var2'] < 20] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Last selected value ==>", result2[-1]) print("Total selected records in table ==>", len(result2)) self.assertEqual(table.nrows, nrows - 1) self.assertEqual(table.shape, (nrows - 1,)) # Check that the new list is smaller than the original one self.assertEqual(len(result), len(result2) + 1) self.assertEqual(result[:-1], result2) def test04b_delete(self): """Checking whether a range of rows can be deleted.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04b_delete..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "a") table = self.fileh.get_node("/table0") # Read the records and select the ones with "var2" column less than 20 result = [r['var2'] for r in table.iterrows() if r['var2'] < 20] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Last selected value ==>", result[-1]) print("Total selected records in table ==>", len(result)) nrows = table.nrows table.nrowsinbuf = 4 # small value of the buffer # Delete the last ten rows table.remove_rows(10, 20) # Re-read the records result2 = [r['var2'] for r in table.iterrows() if r['var2'] < 20] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Last selected value ==>", result2[-1]) print("Total selected records in table ==>", len(result2)) self.assertEqual(table.nrows, nrows - 10) self.assertEqual(table.shape, (nrows - 10,)) # Check that the new list is smaller than the original one self.assertEqual(len(result), len(result2) + 10) self.assertEqual(result[:10], result2) def test04c_delete(self): """Checking whether removing a bad range of rows is detected.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04c_delete..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "a") table = self.fileh.get_node("/table0") # Read the records and select the ones with "var2" column less than 20 result = [r['var2'] for r in table.iterrows() if r['var2'] < 20] nrows = table.nrows table.nrowsinbuf = 5 # small value of the buffer # Delete a too large range of rows table.remove_rows(10, nrows + 100) # Re-read the records result2 = [r['var2'] for r in table.iterrows() if r['var2'] < 20] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Last selected value ==>", result2[-1]) print("Total selected records in table ==>", len(result2)) self.assertEqual(table.nrows, 10) self.assertEqual(table.shape, (10,)) # Check that the new list is smaller than the original one self.assertEqual(len(result), len(result2) + 10) self.assertEqual(result[:10], result2) def test04d_delete(self): """Checking whether removing rows several times at once is working.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04d_delete..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "a") table = self.fileh.get_node("/table0") # Read the records and select the ones with "var2" column less than 20 result = [r['var2'] for r in table if r['var2'] < 20] nrows = table.nrows nrowsinbuf = table.nrowsinbuf table.nrowsinbuf = 6 # small value of the buffer # Delete some rows table.remove_rows(10, 15) # It's necessary to restore the value of buffer to use the row object # afterwards... table.nrowsinbuf = nrowsinbuf # Append some rows row = table.row for i in xrange(10, 15): row['var1'] = '%04d' % (self.appendrows - i) # This line gives problems on Windows. Why? # row['var7'] = row['var1'][-1] row['var2'] = i row['var3'] = i % self.maxshort if isinstance(row['var4'], np.ndarray): row['var4'] = [float(i), float(i * i)] else: row['var4'] = float(i) if isinstance(row['var8'], np.ndarray): row['var8'] = [0, 1] else: row['var8'] = 1 if isinstance(row['var9'], np.ndarray): row['var9'] = [0.+float(i)*1j, float(i)+0.j] else: row['var9'] = float(i)+0.j if isinstance(row['var10'], np.ndarray): row['var10'] = [float(i)+0.j, 1.+float(i)*1j] else: row['var10'] = 1.+float(i)*1j if isinstance(row['var5'], np.ndarray): row['var5'] = np.array((float(i),)*4) else: row['var5'] = float(i) if 'Float16Col' in globals(): if isinstance(row['var11'], np.ndarray): row['var11'] = np.array((float(i),)*4) else: row['var11'] = float(i) if 'Float96Col' in globals(): if isinstance(row['var12'], np.ndarray): row['var12'] = np.array((float(i),)*4) else: row['var12'] = float(i) if 'Float128Col' in globals(): if isinstance(row['var13'], np.ndarray): row['var13'] = np.array((float(i),)*4) else: row['var13'] = float(i) if 'Complex192Col' in globals(): if isinstance(row['var14'], np.ndarray): row['var14'] = [float(i)+0j, 1 + float(i)*1j] else: row['var14'] = 1 + float(i)*1j if 'Complex256Col' in globals(): if isinstance(row['var15'], np.ndarray): row['var15'] = [float(i)+0j, 1 + float(i)*1j] else: row['var15'] = 1 + float(i)*1j row.append() # Flush the buffer for this table table.flush() # Delete 5 rows more table.remove_rows(5, 10) # Re-read the records result2 = [r['var2'] for r in table if r['var2'] < 20] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Last selected value ==>", result2[-1]) print("Total selected records in table ==>", len(result2)) self.assertEqual(table.nrows, nrows - 5) self.assertEqual(table.shape, (nrows - 5,)) # Check that the new list is smaller than the original one self.assertEqual(len(result), len(result2) + 5) # The last values has to be equal self.assertEqual(result[10:15], result2[10:15]) def test05_filtersTable(self): """Checking tablefilters.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05_filtersTable..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") # Check filters: if self.compress != table.filters.complevel and common.verbose: print("Error in compress. Class:", self.__class__.__name__) print("self, table:", self.compress, table.filters.complevel) self.assertEqual(table.filters.complevel, self.compress) if self.compress > 0 and which_lib_version(self.complib): self.assertEqual(table.filters.complib, self.complib) if self.shuffle != table.filters.shuffle and common.verbose: print("Error in shuffle. Class:", self.__class__.__name__) print("self, table:", self.shuffle, table.filters.shuffle) self.assertEqual(self.shuffle, table.filters.shuffle) if self.fletcher32 != table.filters.fletcher32 and common.verbose: print("Error in fletcher32. Class:", self.__class__.__name__) print("self, table:", self.fletcher32, table.filters.fletcher32) self.assertEqual(self.fletcher32, table.filters.fletcher32) def test06_attributes(self): self.fileh = open_file(self.file) obj = self.fileh.get_node('/table0') self.assertEqual(obj.flavor, 'numpy') self.assertEqual(obj.shape, (self.expectedrows,)) self.assertEqual(obj.ndim, 1) self.assertEqual(obj.nrows, self.expectedrows) class BasicWriteTestCase(BasicTestCase): title = "BasicWrite" class OldRecordBasicWriteTestCase(BasicTestCase): title = "OldRecordBasicWrite" record = OldRecord class DictWriteTestCase(BasicTestCase): # This checks also unidimensional arrays as columns title = "DictWrite" record = RecordDescriptionDict nrows = 21 nrowsinbuf = 3 # Choose a small value for the buffer size start = 0 stop = 10 step = 3 if sys.version_info < (3,): class DictWriteTestCase2(DictWriteTestCase): record = RecordDescriptionDict.copy() record[unicode('var1')] = record.pop('var1') # Pure NumPy dtype class NumPyDTWriteTestCase(BasicTestCase): title = "NumPyDTWriteTestCase" formats = "a4,i4,i2,2f8,f4,i2,a1,b1,c8,c16".split(',') names = 'var1,var2,var3,var4,var5,var6,var7,var8,var9,var10'.split(',') if 'Float16Col' in globals(): formats.append('f2') names.append('var11') if 'Float96Col' in globals(): formats.append('f12') names.append('var12') if 'Float128Col' in globals(): formats.append('f16') names.append('var13') if 'Complex192Col' in globals(): formats.append('c24') names.append('var14') if 'Complex256Col' in globals(): formats.append('c32') names.append('var15') record = np.dtype(','.join(formats)) record.names = names class RecArrayOneWriteTestCase(BasicTestCase): title = "RecArrayOneWrite" formats = "a4,i4,i2,2f8,f4,i2,a1,b1,c8,c16".split(',') names = 'var1,var2,var3,var4,var5,var6,var7,var8,var9,var10'.split(',') if 'Float16Col' in globals(): formats.append('f2') names.append('var11') if 'Float96Col' in globals(): formats.append('f12') names.append('var12') if 'Float128Col' in globals(): formats.append('f16') names.append('var13') if 'Complex192Col' in globals(): formats.append('c24') names.append('var14') if 'Complex256Col' in globals(): formats.append('c32') names.append('var15') record = records.array(None, shape=0, formats=','.join(formats), names=names) class RecArrayTwoWriteTestCase(BasicTestCase): title = "RecArrayTwoWrite" expectedrows = 100 recarrayinit = 1 formats = "a4,i4,i2,2f8,f4,i2,a1,b1,c8,c16".split(',') names = 'var1,var2,var3,var4,var5,var6,var7,var8,var9,var10'.split(',') if 'Float16Col' in globals(): formats.append('f2') names.append('var11') if 'Float96Col' in globals(): formats.append('f12') names.append('var12') if 'Float128Col' in globals(): formats.append('f16') names.append('var13') if 'Complex192Col' in globals(): formats.append('c24') names.append('var14') if 'Complex256Col' in globals(): formats.append('c32') names.append('var15') recordtemplate = records.array(None, shape=1, formats=','.join(formats), names=names) class RecArrayThreeWriteTestCase(BasicTestCase): title = "RecArrayThreeWrite" expectedrows = 100 recarrayinit = 1 formats = "a4,i4,i2,2f8,f4,i2,a1,b1,c8,c16".split(',') names = 'var1,var2,var3,var4,var5,var6,var7,var8,var9,var10'.split(',') if 'Float16Col' in globals(): formats.append('f2') names.append('var11') if 'Float96Col' in globals(): formats.append('f12') names.append('var12') if 'Float128Col' in globals(): formats.append('f16') names.append('var13') if 'Complex192Col' in globals(): formats.append('c24') names.append('var14') if 'Complex256Col' in globals(): formats.append('c32') names.append('var15') recordtemplate = records.array(None, shape=1, formats=','.join(formats), names=names) class CompressBloscTablesTestCase(BasicTestCase): title = "CompressBloscTables" compress = 6 complib = "blosc" class CompressBloscShuffleTablesTestCase(BasicTestCase): title = "CompressBloscTables" compress = 1 shuffle = 1 complib = "blosc" class CompressBloscBloscLZTablesTestCase(BasicTestCase): title = "CompressBloscLZTables" compress = 1 shuffle = 1 complib = "blosc:blosclz" class CompressBloscLZ4TablesTestCase(BasicTestCase): title = "CompressLZ4Tables" compress = 1 shuffle = 1 complib = "blosc:lz4" class CompressBloscLZ4HCTablesTestCase(BasicTestCase): title = "CompressLZ4HCTables" compress = 1 shuffle = 1 complib = "blosc:lz4hc" class CompressBloscSnappyTablesTestCase(BasicTestCase): title = "CompressSnappyTables" compress = 1 shuffle = 1 complib = "blosc:snappy" class CompressBloscZlibTablesTestCase(BasicTestCase): title = "CompressZlibTables" compress = 1 shuffle = 1 complib = "blosc:zlib" class CompressLZOTablesTestCase(BasicTestCase): title = "CompressLZOTables" compress = 1 complib = "lzo" class CompressLZOShuffleTablesTestCase(BasicTestCase): title = "CompressLZOTables" compress = 1 shuffle = 1 complib = "lzo" class CompressBzip2TablesTestCase(BasicTestCase): title = "CompressBzip2Tables" compress = 1 complib = "bzip2" class CompressBzip2ShuffleTablesTestCase(BasicTestCase): title = "CompressBzip2Tables" compress = 1 shuffle = 1 complib = "bzip2" class CompressZLIBTablesTestCase(BasicTestCase): title = "CompressOneTables" compress = 1 complib = "zlib" class CompressZLIBShuffleTablesTestCase(BasicTestCase): title = "CompressOneTables" compress = 1 shuffle = 1 complib = "zlib" class Fletcher32TablesTestCase(BasicTestCase): title = "Fletcher32Tables" fletcher32 = 1 shuffle = 0 complib = "zlib" class AllFiltersTablesTestCase(BasicTestCase): title = "AllFiltersTables" compress = 1 fletcher32 = 1 shuffle = 1 complib = "zlib" class CompressTwoTablesTestCase(BasicTestCase): title = "CompressTwoTables" compress = 1 # This checks also unidimensional arrays as columns record = RecordDescriptionDict class BigTablesTestCase(BasicTestCase): title = "BigTables" # 10000 rows takes much more time than we can afford for tests # reducing to 1000 would be more than enough # F. Alted 2004-01-19 # Will be executed only in common.heavy mode expectedrows = 10000 appendrows = 100 class SizeOnDiskInMemoryPropertyTestCase(unittest.TestCase): def setUp(self): # set chunkshape so it divides evenly into array_size, to avoid # partially filled chunks self.chunkshape = (1000, ) self.dtype = np.format_parser(['i4'] * 10, [], []).dtype # approximate size (in bytes) of non-data portion of hdf5 file self.hdf_overhead = 6000 self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w") def tearDown(self): self.fileh.close() # Then, delete the file os.remove(self.file) common.cleanup(self) def create_table(self, complevel): filters = Filters(complevel=complevel, complib='blosc') self.table = self.fileh.create_table('/', 'sometable', self.dtype, filters=filters, chunkshape=self.chunkshape) def test_zero_length(self): complevel = 0 self.create_table(complevel) self.assertEqual(self.table.size_on_disk, 0) self.assertEqual(self.table.size_in_memory, 0) # add 10 chunks of data in one append def test_no_compression_one_append(self): complevel = 0 self.create_table(complevel) self.table.append([tuple(range(10))] * self.chunkshape[0] * 10) self.assertEqual(self.table.size_on_disk, 10 * 1000 * 10 * 4) self.assertEqual(self.table.size_in_memory, 10 * 1000 * 10 * 4) # add 10 chunks of data in two appends def test_no_compression_multiple_appends(self): complevel = 0 self.create_table(complevel) self.table.append([tuple(range(10))] * self.chunkshape[0] * 5) self.table.append([tuple(range(10))] * self.chunkshape[0] * 5) self.assertEqual(self.table.size_on_disk, 10 * 1000 * 10 * 4) self.assertEqual(self.table.size_in_memory, 10 * 1000 * 10 * 4) def test_with_compression(self): complevel = 1 self.create_table(complevel) self.table.append([tuple(range(10))] * self.chunkshape[0] * 10) file_size = os.stat(self.file).st_size self.assertTrue( abs(self.table.size_on_disk - file_size) <= self.hdf_overhead) self.assertEqual(self.table.size_in_memory, 10 * 1000 * 10 * 4) self.assertTrue(self.table.size_on_disk < self.table.size_in_memory) class NonNestedTableReadTestCase(unittest.TestCase): def setUp(self): self.dtype = np.format_parser(['i4'] * 10, [], []).dtype self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w") self.table = self.fileh.create_table('/', 'table', self.dtype) self.shape = (100, ) self.populate_file() def tearDown(self): self.fileh.close() os.remove(self.file) def populate_file(self): self.array = np.zeros(self.shape, self.dtype) for row_num, row in enumerate(self.array): start = row_num * len(self.array.dtype.names) for value, col in enumerate(self.array.dtype.names, start): row[col] = value self.table.append(self.array) self.assertEqual(len(self.table), len(self.array)) def test_read_all(self): output = self.table.read() npt.assert_array_equal(output, self.array) def test_read_slice1(self): output = self.table.read(0, 51) npt.assert_array_equal(output, self.array[0:51]) def test_read_all_rows_specified_field(self): output = self.table.read(field='f1') npt.assert_array_equal(output, self.array['f1']) def test_read_slice1_specified_field(self): output = self.table.read(1, 64, field='f1') npt.assert_array_equal(output, self.array['f1'][1:64]) def test_out_arg_with_non_numpy_flavor(self): output = np.empty(self.shape, self.dtype) self.table.flavor = 'python' self.assertRaises(TypeError, lambda: self.table.read(out=output)) try: self.table.read(out=output) except TypeError as exc: self.assertTrue("Optional 'out' argument may only be" in str(exc)) def test_read_all_out_arg(self): output = np.empty(self.shape, self.dtype) self.table.read(out=output) npt.assert_array_equal(output, self.array) def test_read_slice1_out_arg(self): output = np.empty((51, ), self.dtype) self.table.read(0, 51, out=output) npt.assert_array_equal(output, self.array[0:51]) def test_read_all_rows_specified_field_out_arg(self): output = np.empty(self.shape, 'i4') self.table.read(field='f1', out=output) npt.assert_array_equal(output, self.array['f1']) def test_read_slice1_specified_field_out_arg(self): output = np.empty((63, ), 'i4') self.table.read(1, 64, field='f1', out=output) npt.assert_array_equal(output, self.array['f1'][1:64]) def test_read_all_out_arg_sliced(self): output = np.empty((200, ), self.dtype) output['f0'] = np.random.randint(0, 10000, (200, )) output_orig = output.copy() self.table.read(out=output[0:100]) npt.assert_array_equal(output[0:100], self.array) npt.assert_array_equal(output[100:], output_orig[100:]) def test_all_fields_non_contiguous_slice_contiguous_buffer(self): output = np.empty((50, ), self.dtype) self.table.read(0, 100, 2, out=output) npt.assert_array_equal(output, self.array[0:100:2]) def test_specified_field_non_contiguous_slice_contiguous_buffer(self): output = np.empty((50, ), 'i4') self.table.read(0, 100, 2, field='f3', out=output) npt.assert_array_equal(output, self.array['f3'][0:100:2]) def test_all_fields_non_contiguous_buffer(self): output = np.empty((100, ), self.dtype) output_slice = output[0:100:2] self.assertRaises(ValueError, self.table.read, 0, 100, 2, None, output_slice) # once Python 2.6 support is dropped, this could change # to assertRaisesRegexp to check exception type and message at once try: self.table.read(0, 100, 2, field=None, out=output_slice) except ValueError as exc: self.assertEqual('output array not C contiguous', str(exc)) def test_specified_field_non_contiguous_buffer(self): output = np.empty((100, ), 'i4') output_slice = output[0:100:2] self.assertRaises(ValueError, self.table.read, 0, 100, 2, 'f3', output_slice) try: self.table.read(0, 100, 2, field='f3', out=output_slice) except ValueError as exc: self.assertEqual('output array not C contiguous', str(exc)) def test_all_fields_buffer_too_small(self): output = np.empty((99, ), self.dtype) self.assertRaises(ValueError, lambda: self.table.read(out=output)) try: self.table.read(out=output) except ValueError as exc: self.assertTrue('output array size invalid, got' in str(exc)) def test_specified_field_buffer_too_small(self): output = np.empty((99, ), 'i4') func = lambda: self.table.read(field='f5', out=output) self.assertRaises(ValueError, func) try: self.table.read(field='f5', out=output) except ValueError as exc: self.assertTrue('output array size invalid, got' in str(exc)) def test_all_fields_buffer_too_large(self): output = np.empty((101, ), self.dtype) self.assertRaises(ValueError, lambda: self.table.read(out=output)) try: self.table.read(out=output) except ValueError as exc: self.assertTrue('output array size invalid, got' in str(exc)) class TableReadByteorderTestCase(unittest.TestCase): def setUp(self): self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w") self.system_byteorder = sys.byteorder self.other_byteorder = { 'little': 'big', 'big': 'little'}[sys.byteorder] self.reverse_byteorders = {'little': '<', 'big': '>'} def tearDown(self): self.fileh.close() os.remove(self.file) def create_table(self, byteorder): table_dtype_code = self.reverse_byteorders[byteorder] + 'i4' table_dtype = np.format_parser([table_dtype_code, 'a1'], [], []).dtype self.table = self.fileh.create_table('/', 'table', table_dtype, byteorder=byteorder) input_dtype = np.format_parser(['i4', 'a1'], [], []).dtype self.input_array = np.zeros((10, ), input_dtype) self.input_array['f0'] = np.arange(10) self.input_array['f1'] = b'a' self.table.append(self.input_array) def test_table_system_byteorder_no_out_argument(self): self.create_table(self.system_byteorder) output = self.table.read() self.assertEqual(byteorders[output['f0'].dtype.byteorder], self.system_byteorder) npt.assert_array_equal(output['f0'], np.arange(10)) def test_table_other_byteorder_no_out_argument(self): self.create_table(self.other_byteorder) output = self.table.read() self.assertEqual(byteorders[output['f0'].dtype.byteorder], self.system_byteorder) npt.assert_array_equal(output['f0'], np.arange(10)) def test_table_system_byteorder_out_argument_system_byteorder(self): self.create_table(self.system_byteorder) out_dtype_code = self.reverse_byteorders[self.system_byteorder] + 'i4' out_dtype = np.format_parser([out_dtype_code, 'a1'], [], []).dtype output = np.empty((10, ), out_dtype) self.table.read(out=output) self.assertEqual(byteorders[output['f0'].dtype.byteorder], self.system_byteorder) npt.assert_array_equal(output['f0'], np.arange(10)) def test_table_other_byteorder_out_argument_system_byteorder(self): self.create_table(self.other_byteorder) out_dtype_code = self.reverse_byteorders[self.system_byteorder] + 'i4' out_dtype = np.format_parser([out_dtype_code, 'a1'], [], []).dtype output = np.empty((10, ), out_dtype) self.table.read(out=output) self.assertEqual(byteorders[output['f0'].dtype.byteorder], self.system_byteorder) npt.assert_array_equal(output['f0'], np.arange(10)) def test_table_system_byteorder_out_argument_other_byteorder(self): self.create_table(self.system_byteorder) out_dtype_code = self.reverse_byteorders[self.other_byteorder] + 'i4' out_dtype = np.format_parser([out_dtype_code, 'a1'], [], []).dtype output = np.empty((10, ), out_dtype) self.assertRaises(ValueError, lambda: self.table.read(out=output)) try: self.table.read(out=output) except ValueError as exc: self.assertTrue("array must be in system's byteorder" in str(exc)) def test_table_other_byteorder_out_argument_other_byteorder(self): self.create_table(self.other_byteorder) out_dtype_code = self.reverse_byteorders[self.other_byteorder] + 'i4' out_dtype = np.format_parser([out_dtype_code, 'a1'], [], []).dtype output = np.empty((10, ), out_dtype) self.assertRaises(ValueError, lambda: self.table.read(out=output)) try: self.table.read(out=output) except ValueError as exc: self.assertTrue("array must be in system's byteorder" in str(exc)) class BasicRangeTestCase(unittest.TestCase): # file = "test.h5" mode = "w" title = "This is the table title" record = Record maxshort = 1 << 15 expectedrows = 100 compress = 0 shuffle = 1 # Default values nrows = 20 nrowsinbuf = 3 # Choose a small value for the buffer size start = 1 stop = nrows checkrecarray = 0 checkgetCol = 0 def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, self.mode) self.rootgroup = self.fileh.root self.populateFile() self.fileh.close() def populateFile(self): group = self.rootgroup for j in range(3): # Create a table filterprops = Filters(complevel=self.compress, shuffle=self.shuffle) table = self.fileh.create_table(group, 'table'+str(j), self.record, title=self.title, filters=filterprops, expectedrows=self.expectedrows) # Get the row object associated with the new table row = table.row # Fill the table for i in xrange(self.expectedrows): row['var1'] = '%04d' % (self.expectedrows - i) row['var7'] = row['var1'][-1] row['var2'] = i row['var3'] = i % self.maxshort if isinstance(row['var4'], np.ndarray): row['var4'] = [float(i), float(i * i)] else: row['var4'] = float(i) if isinstance(row['var5'], np.ndarray): row['var5'] = np.array((float(i),)*4) else: row['var5'] = float(i) # var6 will be like var3 but byteswaped row['var6'] = ((row['var3'] >> 8) & 0xff) + \ ((row['var3'] << 8) & 0xff00) row.append() # Flush the buffer for this table table.flush() # Create a new group (descendant of group) group2 = self.fileh.create_group(group, 'group'+str(j)) # Iterate over this new group (group2) group = group2 def tearDown(self): if self.fileh.isopen: self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def check_range(self): # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") table.nrowsinbuf = self.nrowsinbuf r = slice(self.start, self.stop, self.step) resrange = r.indices(table.nrows) reslength = len(range(*resrange)) #print "self.checkrecarray = ", self.checkrecarray #print "self.checkgetCol = ", self.checkgetCol if self.checkrecarray: recarray = table.read(self.start, self.stop, self.step) result = [] for nrec in xrange(len(recarray)): if recarray['var2'][nrec] < self.nrows and 0 < self.step: result.append(recarray['var2'][nrec]) elif recarray['var2'][nrec] > self.nrows and 0 > self.step: result.append(recarray['var2'][nrec]) elif self.checkgetCol: column = table.read(self.start, self.stop, self.step, 'var2') result = [] for nrec in xrange(len(column)): if column[nrec] < self.nrows and 0 < self.step: result.append(column[nrec]) elif column[nrec] > self.nrows and 0 > self.step: result.append(column[nrec]) else: if 0 < self.step: result = [ rec['var2'] for rec in table.iterrows(self.start, self.stop, self.step) if rec['var2'] < self.nrows ] elif 0 > self.step: result = [ rec['var2'] for rec in table.iterrows(self.start, self.stop, self.step) if rec['var2'] > self.nrows ] if self.start < 0: startr = self.expectedrows + self.start else: startr = self.start if self.stop is None: if self.checkrecarray or self.checkgetCol: # data read using the read method stopr = startr + 1 else: # data read using the iterrows method stopr = self.nrows elif self.stop < 0: stopr = self.expectedrows + self.stop else: stopr = self.stop if self.nrows < stopr: stopr = self.nrows if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) if reslength: if self.checkrecarray: print("Last record *read* in recarray ==>", recarray[-1]) elif self.checkgetCol: print("Last value *read* in getCol ==>", column[-1]) else: print("Last record *read* in table range ==>", rec) print("Total number of selected records ==>", len(result)) print("Selected records:\n", result) print("Selected records should look like:\n", range(startr, stopr, self.step)) print("start, stop, step ==>", self.start, self.stop, self.step) print("startr, stopr, step ==>", startr, stopr, self.step) self.assertEqual(result, range(startr, stopr, self.step)) if not (self.checkrecarray or self.checkgetCol): if startr < stopr and 0 < self.step: rec = [r for r in table.iterrows(self.start, self.stop, self.step) if r['var2'] < self.nrows][-1] if self.nrows < self.expectedrows: self.assertEqual( rec['var2'], range(self.start, self.stop, self.step)[-1]) else: self.assertEqual( rec['var2'], range(startr, stopr, self.step)[-1]) elif startr > stopr and 0 > self.step: rec = [r['var2'] for r in table.iterrows(self.start, self.stop, self.step) if r['var2'] > self.nrows][0] if self.nrows < self.expectedrows: self.assertEqual( rec, range(self.start, self.stop or -1, self.step)[0]) else: self.assertEqual( rec, range(startr, stopr or -1, self.step)[0]) # Close the file self.fileh.close() def test01_range(self): """Checking ranges in table iterators (case1)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_range..." % self.__class__.__name__) # Case where step < nrowsinbuf < 2 * step self.nrows = 21 self.nrowsinbuf = 3 self.start = 0 self.stop = self.expectedrows self.step = 2 self.check_range() def test01a_range(self): """Checking ranges in table iterators (case1)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01a_range..." % self.__class__.__name__) # Case where step < nrowsinbuf < 2 * step self.nrows = 21 self.nrowsinbuf = 3 self.start = self.expectedrows - 1 self.stop = None self.step = -2 self.check_range() def test02_range(self): """Checking ranges in table iterators (case2)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_range..." % self.__class__.__name__) # Case where step < nrowsinbuf < 10 * step self.nrows = 21 self.nrowsinbuf = 31 self.start = 11 self.stop = self.expectedrows self.step = 3 self.check_range() def test03_range(self): """Checking ranges in table iterators (case3)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_range..." % self.__class__.__name__) # Case where step < nrowsinbuf < 1.1 * step self.nrows = self.expectedrows self.nrowsinbuf = 11 # Choose a small value for the buffer size self.start = 0 self.stop = self.expectedrows self.step = 10 self.check_range() def test04_range(self): """Checking ranges in table iterators (case4)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_range..." % self.__class__.__name__) # Case where step == nrowsinbuf self.nrows = self.expectedrows self.nrowsinbuf = 11 # Choose a small value for the buffer size self.start = 1 self.stop = self.expectedrows self.step = 11 self.check_range() def test05_range(self): """Checking ranges in table iterators (case5)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05_range..." % self.__class__.__name__) # Case where step > 1.1 * nrowsinbuf self.nrows = 21 self.nrowsinbuf = 10 # Choose a small value for the buffer size self.start = 1 self.stop = self.expectedrows self.step = 11 self.check_range() def test06_range(self): """Checking ranges in table iterators (case6)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test06_range..." % self.__class__.__name__) # Case where step > 3 * nrowsinbuf self.nrows = 3 self.nrowsinbuf = 3 # Choose a small value for the buffer size self.start = 2 self.stop = self.expectedrows self.step = 10 self.check_range() def test07_range(self): """Checking ranges in table iterators (case7)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test07_range..." % self.__class__.__name__) # Case where start == stop self.nrows = 2 self.nrowsinbuf = 3 # Choose a small value for the buffer size self.start = self.nrows self.stop = self.nrows self.step = 10 self.check_range() def test08_range(self): """Checking ranges in table iterators (case8)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test08_range..." % self.__class__.__name__) # Case where start > stop self.nrows = 2 self.nrowsinbuf = 3 # Choose a small value for the buffer size self.start = self.nrows + 1 self.stop = self.nrows self.step = 1 self.check_range() def test09_range(self): """Checking ranges in table iterators (case9)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test09_range..." % self.__class__.__name__) # Case where stop = None (last row) self.nrows = 100 self.nrowsinbuf = 3 # Choose a small value for the buffer size self.start = 1 self.stop = 2 self.step = 1 self.check_range() def test10_range(self): """Checking ranges in table iterators (case10)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test10_range..." % self.__class__.__name__) # Case where start < 0 and stop = None (last row) self.nrows = self.expectedrows self.nrowsinbuf = 5 # Choose a small value for the buffer size self.start = -6 self.startr = self.expectedrows + self.start self.stop = -5 self.stopr = self.expectedrows self.step = 2 self.check_range() def test10a_range(self): """Checking ranges in table iterators (case10a)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test10a_range..." % self.__class__.__name__) # Case where start < 0 and stop = 0 self.nrows = self.expectedrows self.nrowsinbuf = 5 # Choose a small value for the buffer size self.start = -6 self.startr = self.expectedrows + self.start self.stop = 0 self.stopr = self.expectedrows self.step = 2 self.check_range() def test11_range(self): """Checking ranges in table iterators (case11)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test11_range..." % self.__class__.__name__) # Case where start < 0 and stop < 0 self.nrows = self.expectedrows self.nrowsinbuf = 5 # Choose a small value for the buffer size self.start = -6 self.startr = self.expectedrows + self.start self.stop = -2 self.stopr = self.expectedrows + self.stop self.step = 1 self.check_range() def test12_range(self): """Checking ranges in table iterators (case12)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test12_range..." % self.__class__.__name__) # Case where start < 0 and stop < 0 and start > stop self.nrows = self.expectedrows self.nrowsinbuf = 5 # Choose a small value for the buffer size self.start = -1 self.startr = self.expectedrows + self.start self.stop = -2 self.stopr = self.expectedrows + self.stop self.step = 1 self.check_range() def test13_range(self): """Checking ranges in table iterators (case13)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test13_range..." % self.__class__.__name__) # Case where step < 0 self.step = -11 try: self.check_range() except ValueError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next ValueError was catched!") print(value) self.fileh.close() #else: # print rec # self.fail("expected a ValueError") # Case where step == 0 self.step = 0 try: self.check_range() except ValueError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next ValueError was catched!") print(value) self.fileh.close() #else: # print rec # self.fail("expected a ValueError") class IterRangeTestCase(BasicRangeTestCase): pass class RecArrayRangeTestCase(BasicRangeTestCase): checkrecarray = 1 class getColRangeTestCase(BasicRangeTestCase): checkgetCol = 1 def test01_nonexistentField(self): """Checking non-existing Field in getCol method """ if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_nonexistentField..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") self.root = self.fileh.root table = self.fileh.get_node("/table0") try: # column = table.read(field='non-existent-column') table.col('non-existent-column') except KeyError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next KeyError was catched!") print(value) self.fileh.close() else: print(rec) self.fail("expected a KeyError") class getItemTestCase(unittest.TestCase): mode = "w" title = "This is the table title" record = Record maxshort = 1 << 15 expectedrows = 100 compress = 0 shuffle = 1 # Default values nrows = 20 nrowsinbuf = 3 # Choose a small value for the buffer size start = 1 stop = nrows checkrecarray = 0 checkgetCol = 0 def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, self.mode) self.rootgroup = self.fileh.root self.populateFile() self.fileh.close() def populateFile(self): group = self.rootgroup for j in range(3): # Create a table filterprops = Filters(complevel=self.compress, shuffle=self.shuffle) table = self.fileh.create_table(group, 'table'+str(j), self.record, title=self.title, filters=filterprops, expectedrows=self.expectedrows) # Get the row object associated with the new table row = table.row # Fill the table for i in xrange(self.expectedrows): row['var1'] = '%04d' % (self.expectedrows - i) row['var7'] = row['var1'][-1] row['var2'] = i row['var3'] = i % self.maxshort if isinstance(row['var4'], np.ndarray): row['var4'] = [float(i), float(i * i)] else: row['var4'] = float(i) if isinstance(row['var5'], np.ndarray): row['var5'] = np.array((float(i),)*4) else: row['var5'] = float(i) # var6 will be like var3 but byteswaped row['var6'] = ((row['var3'] >> 8) & 0xff) + \ ((row['var3'] << 8) & 0xff00) row.append() # Flush the buffer for this table table.flush() # Create a new group (descendant of group) group2 = self.fileh.create_group(group, 'group'+str(j)) # Iterate over this new group (group2) group = group2 def tearDown(self): if self.fileh.isopen: self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test01a_singleItem(self): """Checking __getitem__ method with single parameter (int)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01a_singleItem..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 result = table[2] self.assertEqual(result["var2"], 2) result = table[25] self.assertEqual(result["var2"], 25) result = table[self.expectedrows-1] self.assertEqual(result["var2"], self.expectedrows - 1) def test01b_singleItem(self): """Checking __getitem__ method with single parameter (neg. int) """ if common.verbose: print('\n', '-=' * 30) print("Running %s.test01b_singleItem..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 result = table[-5] self.assertEqual(result["var2"], self.expectedrows - 5) result = table[-1] self.assertEqual(result["var2"], self.expectedrows - 1) result = table[-self.expectedrows] self.assertEqual(result["var2"], 0) def test01c_singleItem(self): """Checking __getitem__ method with single parameter (long)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01c_singleItem..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 result = table[2] self.assertEqual(result["var2"], 2) result = table[25] self.assertEqual(result["var2"], 25) result = table[self.expectedrows-1] self.assertEqual(result["var2"], self.expectedrows - 1) def test01d_singleItem(self): """Checking __getitem__ method with single parameter (neg. long) """ if common.verbose: print('\n', '-=' * 30) print("Running %s.test01d_singleItem..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 result = table[-5] self.assertEqual(result["var2"], self.expectedrows - 5) result = table[-1] self.assertEqual(result["var2"], self.expectedrows - 1) result = table[-self.expectedrows] self.assertEqual(result["var2"], 0) def test01e_singleItem(self): """Checking __getitem__ method with single parameter (rank-0 ints)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01e_singleItem..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 result = table[np.array(2)] self.assertEqual(result["var2"], 2) result = table[np.array(25)] self.assertEqual(result["var2"], 25) result = table[np.array(self.expectedrows-1)] self.assertEqual(result["var2"], self.expectedrows - 1) def test02_twoItems(self): """Checking __getitem__ method with start, stop parameters.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_twoItem..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 result = table[2:6] self.assertEqual(result["var2"].tolist(), range(2, 6)) result = table[2:-6] self.assertEqual(result["var2"].tolist(), range( 2, self.expectedrows-6)) result = table[2:] self.assertEqual(result["var2"].tolist(), range(2, self.expectedrows)) result = table[-2:] self.assertEqual(result["var2"].tolist(), range(self.expectedrows-2, self.expectedrows)) def test03_threeItems(self): """Checking __getitem__ method with start, stop, step parameters.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_threeItem..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 result = table[2:6:3] self.assertEqual(result["var2"].tolist(), range(2, 6, 3)) result = table[2::3] self.assertEqual(result["var2"].tolist(), range( 2, self.expectedrows, 3)) result = table[:6:2] self.assertEqual(result["var2"].tolist(), range(0, 6, 2)) result = table[::] self.assertEqual(result["var2"].tolist(), range( 0, self.expectedrows, 1)) def test04_negativeStep(self): """Checking __getitem__ method with negative step parameter.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_negativeStep..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 try: table[2:3:-3] except ValueError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next ValueError was catched!") print(value) else: self.fail("expected a ValueError") def test06a_singleItemCol(self): """Checking __getitem__ method in Col with single parameter.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test06a_singleItemCol..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 colvar2 = table.cols.var2 self.assertEqual(colvar2[2], 2) self.assertEqual(colvar2[25], 25) self.assertEqual(colvar2[self.expectedrows-1], self.expectedrows - 1) def test06b_singleItemCol(self): """Checking __getitem__ method in Col with single parameter. (negative) """ if common.verbose: print('\n', '-=' * 30) print("Running %s.test06b_singleItem..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 colvar2 = table.cols.var2 self.assertEqual(colvar2[-5], self.expectedrows - 5) self.assertEqual(colvar2[-1], self.expectedrows - 1) self.assertEqual(colvar2[-self.expectedrows], 0) def test07_twoItemsCol(self): """Checking __getitem__ method in Col with start, stop parameters.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test07_twoItemCol..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 colvar2 = table.cols.var2 self.assertEqual(colvar2[2:6].tolist(), range(2, 6)) self.assertEqual(colvar2[2:-6].tolist(), range(2, self.expectedrows-6)) self.assertEqual(colvar2[2:].tolist(), range(2, self.expectedrows)) self.assertEqual(colvar2[-2:].tolist(), range(self.expectedrows-2, self.expectedrows)) def test08_threeItemsCol(self): """Checking __getitem__ method in Col with start, stop, step parameters.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test08_threeItemCol..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 colvar2 = table.cols.var2 self.assertEqual(colvar2[2:6:3].tolist(), range(2, 6, 3)) self.assertEqual(colvar2[2::3].tolist(), range( 2, self.expectedrows, 3)) self.assertEqual(colvar2[:6:2].tolist(), range(0, 6, 2)) self.assertEqual(colvar2[::].tolist(), range(0, self.expectedrows, 1)) def test09_negativeStep(self): """Checking __getitem__ method in Col with negative step parameter.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test09_negativeStep..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 colvar2 = table.cols.var2 try: colvar2[2:3:-3] except ValueError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next ValueError was catched!") print(value) else: self.fail("expected a ValueError") def test10_list_integers(self): """Checking accessing Table with a list of integers.""" self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 idx = list(range(10, 70, 11)) result = table[idx] self.assertEqual(result["var2"].tolist(), idx) result = table.read_coordinates(idx) self.assertEqual(result["var2"].tolist(), idx) def test11_list_booleans(self): """Checking accessing Table with a list of boolean values.""" self.fileh = open_file(self.file, "r") table = self.fileh.root.table0 idx = list(range(10, 70, 11)) selection = [n in idx for n in range(self.expectedrows)] result = table[selection] self.assertEqual(result["var2"].tolist(), idx) result = table.read_coordinates(selection) self.assertEqual(result["var2"].tolist(), idx) class Rec(IsDescription): col1 = IntCol(pos=1) col2 = StringCol(itemsize=3, pos=2) col3 = FloatCol(pos=3) class setItem(common.PyTablesTestCase): def tearDown(self): self.fileh.close() # del self.fileh, self.rootgroup os.remove(self.file) common.cleanup(self) def test01(self): "Checking modifying one table row with __setitem__" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing row table[2] = (456, 'db2', 1.2) # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3], [456, b'db2', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test01b(self): "Checking modifying one table row with __setitem__ (long index)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing row table[2] = (456, 'db2', 1.2) # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3], [456, b'db2', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test02(self): "Modifying one row, with a step (__setitem__)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify two existing rows rows = records.array([[457, b'db1', 1.2]], formats="i4,a3,f8") table[1:3:2] = rows # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [457, b'db1', 1.2], [457, b'db1', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test03(self): "Checking modifying several rows at once (__setitem__)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify two existing rows rows = records.array([[457, b'db1', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8") # table.modify_rows(start=1, rows=rows) table[1:3] = rows # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [457, b'db1', 1.2], [5, b'de1', 1.3], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test04(self): "Modifying several rows at once, with a step (__setitem__)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify two existing rows rows = records.array([[457, b'db1', 1.2], [6, b'de2', 1.3]], formats="i4,a3,f8") # table[1:4:2] = rows table[1::2] = rows # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [457, b'db1', 1.2], [457, b'db1', 1.2], [6, b'de2', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test05(self): "Checking modifying one column (single element, __setitem__)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing column table.cols.col1[1] = -1 # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [-1, b'ded', 1.3], [457, b'db1', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test06a(self): "Checking modifying one column (several elements, __setitem__)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing column table.cols.col1[1:4] = [2, 3, 4] # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3], [3, b'db1', 1.2], [4, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test06b(self): "Checking modifying one column (iterator, __setitem__)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing column try: for row in table.iterrows(): row['col1'] = row.nrow + 1 row.append() table.flush() except NotImplementedError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NotImplementedError was catched!") print(value) else: self.fail("expected a NotImplementedError") # # Create the modified recarray # r1=records.array([[1,b'dbe',1.2],[2,b'ded',1.3], # [3,b'db1',1.2],[4,b'de1',1.3]], # formats="i4,a3,f8", # names = "col1,col2,col3") # # Read the modified table # if self.reopen: # self.fileh.close() # self.fileh = open_file(self.file, "r") # table = self.fileh.root.recarray # table.nrowsinbuf = self.buffersize # set buffer value # r2 = table.read() # if common.verbose: # print "Original table-->", repr(r2) # print "Should look like-->", repr(r1) # self.assertEqual(r1.tostring(), r2.tostring()) # self.assertEqual(table.nrows, 4) def test07(self): "Modifying one column (several elements, __setitem__, step)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 1, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing column table.cols.col1[1:4:2] = [2, 3] # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3], [457, b'db1', 1.2], [3, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test08(self): "Modifying one column (one element, __setitem__, step)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing column table.cols.col1[1:4:3] = [2] # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3], [457, b'db1', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test09(self): "Modifying beyond the table extend (__setitem__, step)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Try to modify beyond the extend # This will silently exclude the non-fitting rows rows = records.array([[457, b'db1', 1.2], [6, b'de2', 1.3]], formats="i4,a3,f8") table[1::2] = rows # How it should look like r1 = records.array([[456, b'dbe', 1.2], [457, b'db1', 1.2], [457, b'db1', 1.2], [6, b'de2', 1.3]], formats="i4,a3,f8") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) class setItem1(setItem): reopen = 0 buffersize = 1 class setItem2(setItem): reopen = 1 buffersize = 2 class setItem3(setItem): reopen = 0 buffersize = 1000 class setItem4(setItem): reopen = 1 buffersize = 1000 class updateRow(common.PyTablesTestCase): def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def test01(self): "Checking modifying one table row with Row.update" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing row for row in table.iterrows(2, 3): (row['col1'], row['col2'], row['col3']) = [456, 'db2', 1.2] row.update() # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3], [456, b'db2', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test02(self): "Modifying one row, with a step (Row.update)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify two existing rows for row in table.iterrows(1, 3, 2): if row.nrow == 1: (row['col1'], row['col2'], row['col3']) = [457, 'db1', 1.2] elif row.nrow == 3: (row['col1'], row['col2'], row['col3']) = [6, 'de2', 1.3] row.update() # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [457, b'db1', 1.2], [457, b'db1', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test03(self): "Checking modifying several rows at once (Row.update)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify two existing rows for row in table.iterrows(1, 3): if row.nrow == 1: (row['col1'], row['col2'], row['col3']) = [457, 'db1', 1.2] elif row.nrow == 2: (row['col1'], row['col2'], row['col3']) = [5, 'de1', 1.3] row.update() # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [457, b'db1', 1.2], [5, b'de1', 1.3], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test04(self): "Modifying several rows at once, with a step (Row.update)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify two existing rows for row in table.iterrows(1, stop=4, step=2): if row.nrow == 1: (row['col1'], row['col2'], row['col3']) = [457, 'db1', 1.2] elif row.nrow == 3: (row['col1'], row['col2'], row['col3']) = [6, 'de2', 1.3] row.update() # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [457, b'db1', 1.2], [457, b'db1', 1.2], [6, b'de2', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test05(self): "Checking modifying one column (single element, Row.update)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing column for row in table.iterrows(1, 2): row['col1'] = -1 row.update() # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [-1, b'ded', 1.3], [457, b'db1', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test06(self): "Checking modifying one column (several elements, Row.update)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing column for row in table.iterrows(1, 4): row['col1'] = row.nrow + 1 row.update() # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3], [3, b'db1', 1.2], [4, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test07(self): "Modifying values from a selection" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value # append new rows r = records.array([[456, b'dbe', 1.2], [ 1, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just rows with col1 < 456 for row in table.where('col1 < 456'): row['col1'] = 2 row['col2'] = 'ada' row.update() # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [2, b'ada', 1.3], [457, b'db1', 1.2], [2, b'ada', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test08(self): "Modifying a large table (Row.update)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value nrows = 100 # append new rows row = table.row for i in xrange(nrows): row['col1'] = i-1 row['col2'] = 'a'+str(i-1) row['col3'] = -1.0 row.append() table.flush() # Modify all the rows for row in table: row['col1'] = row.nrow row['col2'] = 'b'+str(row.nrow) row['col3'] = 0.0 row.update() # Create the modified recarray r1 = records.array(None, shape=nrows, formats="i4,a3,f8", names="col1,col2,col3") for i in xrange(nrows): r1['col1'][i] = i r1['col2'][i] = 'b'+str(i) r1['col3'][i] = 0.0 # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, nrows) def test08b(self): "Setting values on a large table without calling Row.update" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value nrows = 100 # append new rows row = table.row for i in xrange(nrows): row['col1'] = i-1 row['col2'] = 'a'+str(i-1) row['col3'] = -1.0 row.append() table.flush() # Modify all the rows (actually don't) for row in table: row['col1'] = row.nrow row['col2'] = 'b'+str(row.nrow) row['col3'] = 0.0 # row.update() # Create the modified recarray r1 = records.array(None, shape=nrows, formats="i4,a3,f8", names="col1,col2,col3") for i in xrange(nrows): r1['col1'][i] = i-1 r1['col2'][i] = 'a'+str(i-1) r1['col3'][i] = -1.0 # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, nrows) def test09(self): "Modifying selected values on a large table" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value nrows = 100 # append new rows row = table.row for i in xrange(nrows): row['col1'] = i-1 row['col2'] = 'a'+str(i-1) row['col3'] = -1.0 row.append() table.flush() # Modify selected rows for row in table.where('col1 > nrows-3'): row['col1'] = row.nrow row['col2'] = 'b'+str(row.nrow) row['col3'] = 0.0 row.update() # Create the modified recarray r1 = records.array(None, shape=nrows, formats="i4,a3,f8", names="col1,col2,col3") for i in xrange(nrows): r1['col1'][i] = i-1 r1['col2'][i] = 'a'+str(i-1) r1['col3'][i] = -1.0 # modify just the last line r1['col1'][i] = i r1['col2'][i] = 'b'+str(i) r1['col3'][i] = 0.0 # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, nrows) def test09b(self): "Modifying selected values on a large table (alternate values)" self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) table.nrowsinbuf = self.buffersize # set buffer value nrows = 100 # append new rows row = table.row for i in xrange(nrows): row['col1'] = i-1 row['col2'] = 'a'+str(i-1) row['col3'] = -1.0 row.append() table.flush() # Modify selected rows for row in table.iterrows(step=10): row['col1'] = row.nrow row['col2'] = 'b'+str(row.nrow) row['col3'] = 0.0 row.update() # Create the modified recarray r1 = records.array(None, shape=nrows, formats="i4,a3,f8", names="col1,col2,col3") for i in xrange(nrows): if i % 10 > 0: r1['col1'][i] = i-1 r1['col2'][i] = 'a'+str(i-1) r1['col3'][i] = -1.0 else: r1['col1'][i] = i r1['col2'][i] = 'b'+str(i) r1['col3'][i] = 0.0 # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, nrows) class updateRow1(updateRow): reopen = 0 buffersize = 1 class updateRow2(updateRow): reopen = 1 buffersize = 2 class updateRow3(updateRow): reopen = 0 buffersize = 1000 class updateRow4(updateRow): reopen = 1 buffersize = 1000 class RecArrayIO(unittest.TestCase): def test00(self): "Checking saving a regular recarray" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray r = records.array([[456, b'dbe', 1.2], [ 2, b'de', 1.3]], names='col1,col2,col3') # Save it in a table: fileh.create_table(fileh.root, 'recarray', r) # Read it again if self.reopen: fileh.close() fileh = open_file(file, "r") r2 = fileh.root.recarray.read() self.assertEqual(r.tostring(), r2.tostring()) fileh.close() os.remove(file) def test01(self): "Checking saving a recarray with an offset in its buffer" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray r = records.array([[456, b'dbe', 1.2], [ 2, b'de', 1.3]], names='col1,col2,col3') # Get an offsetted bytearray r1 = r[1:] # Save it in a table: fileh.create_table(fileh.root, 'recarray', r1) # Read it again if self.reopen: fileh.close() fileh = open_file(file, "r") r2 = fileh.root.recarray.read() self.assertEqual(r1.tostring(), r2.tostring()) fileh.close() os.remove(file) def test02(self): "Checking saving a large recarray with an offset in its buffer" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray r = records.array(b'a'*200000, 'f4,3i4,a5,i2', 3000) # Get an offsetted bytearray r1 = r[2000:] # Save it in a table: fileh.create_table(fileh.root, 'recarray', r1) # Read it again if self.reopen: fileh.close() fileh = open_file(file, "r") r2 = fileh.root.recarray.read() self.assertEqual(r1.tostring(), r2.tostring()) fileh.close() os.remove(file) def test03(self): "Checking saving a strided recarray with an offset in its buffer" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray r = records.array(b'a'*200000, 'f4,3i4,a5,i2', 3000) # Get an strided recarray r2 = r[::2] # Get an offsetted bytearray r1 = r2[1200:] # Save it in a table: fileh.create_table(fileh.root, 'recarray', r1) # Read it again if self.reopen: fileh.close() fileh = open_file(file, "r") r2 = fileh.root.recarray.read() self.assertEqual(r1.tostring(), r2.tostring()) fileh.close() os.remove(file) def test04(self): "Checking appending several rows at once" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") class Rec(IsDescription): col1 = IntCol(pos=1) col2 = StringCol(itemsize=3, pos=2) col3 = FloatCol(pos=3) # Save it in a table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Create the complete table r1 = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3], [457, b'db1', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the original table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = fileh.root.recarray.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test05(self): "Checking appending several rows at once (close file version)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Save it in a table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray # Create the complete table r1 = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3], [457, b'db1', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the original table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = fileh.root.recarray.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test06a(self): "Checking modifying one table row (list version)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test06a..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([(456, b'dbe', 1.2), ( 2, b'ded', 1.3)], formats="i4,a3,f8") table.append(r) table.append([(457, b'db1', 1.2), (5, b'de1', 1.3)]) # Modify just one existing rows table.modify_rows(start=1, rows=[(456, 'db1', 1.2)]) # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [456, b'db1', 1.2], [457, b'db1', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test06b(self): "Checking modifying one table row (recarray version)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test06b..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing rows table.modify_rows(start=2, rows=records.array([[456, 'db2', 1.2]], formats="i4,a3,f8")) # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3], [456, b'db2', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test07a(self): "Checking modifying several rows at once (list version)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test07a..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify two existing rows table.modify_rows(start=1, rows=[(457, 'db1', 1.2), (5, 'de1', 1.3)]) # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [457, b'db1', 1.2], [5, b'de1', 1.3], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test07b(self): "Checking modifying several rows at once (recarray version)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test07b..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify two existing rows rows = records.array([[457, b'db1', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8") table.modify_rows(start=1, rows=rows) # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [457, b'db1', 1.2], [5, b'de1', 1.3], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test07c(self): "Checking modifying several rows with a mismatching value" if common.verbose: print('\n', '-=' * 30) print("Running %s.test07c..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify two existing rows rows = records.array([[457, b'db1', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8") self.assertRaises(ValueError, table.modify_rows, start=1, stop=2, rows=rows) fileh.close() os.remove(file) def test08a(self): "Checking modifying one column (single column version)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test08a..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing column table.modify_columns(start=1, columns=[[2, 3, 4]], names=["col1"]) # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3], [3, b'db1', 1.2], [4, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test08a2(self): "Checking modifying one column (single column version, modify_column)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test08a2..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing column table.modify_column(start=1, column=[2, 3, 4], colname="col1") # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3], [3, b'db1', 1.2], [4, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test08b(self): "Checking modifying one column (single column version, recarray)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test08b..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing column columns = records.fromarrays(np.array([[2, 3, 4]]), formats="i4") table.modify_columns(start=1, columns=columns, names=["col1"]) # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3], [3, b'db1', 1.2], [4, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test08b2(self): """Checking modifying one column (single column version, recarray, modify_column)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test08b2..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing column columns = records.fromarrays(np.array([[2, 3, 4]]), formats="i4") table.modify_column(start=1, column=columns, colname="col1") # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [2, b'ded', 1.3], [3, b'db1', 1.2], [4, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test08c(self): "Checking modifying one column (single column version, single element)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test08c..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify just one existing column # columns = records.fromarrays(np.array([[4]]), formats="i4") # table.modify_columns(start=1, columns=columns, names=["col1"]) table.modify_columns(start=1, columns=[[4]], names=["col1"]) # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [4, b'ded', 1.3], [457, b'db1', 1.2], [5, b'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test09a(self): "Checking modifying table columns (multiple column version)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test09a..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify a couple of columns columns = [["aaa", "bbb", "ccc"], [1.2, .1, .3]] table.modify_columns(start=1, columns=columns, names=["col2", "col3"]) # Create the modified recarray r1 = records.array([[456, b'dbe', 1.2], [2, b'aaa', 1.2], [457, b'bbb', .1], [5, b'ccc', .3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test09b(self): "Checking modifying table columns (multiple columns, recarray)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test09b..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify a couple of columns columns = records.array([["aaa", 1.2], ["bbb", .1], ["ccc", .3]], formats="a3,f8") table.modify_columns(start=1, columns=columns, names=["col2", "col3"]) # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [2, 'aaa', 1.2], [457, 'bbb', .1], [5, 'ccc', .3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test09c(self): "Checking modifying table columns (single column, step)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test09c..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify a couple of columns columns = records.array([["aaa", 1.2], ["bbb", .1]], formats="a3,f8") table.modify_columns(start=1, step=2, columns=columns, names=["col2", "col3"]) # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [2, 'aaa', 1.2], [457, 'db1', 1.2], [5, 'bbb', .1]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test09d(self): "Checking modifying table columns (multiple columns, step)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test09d..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) # Modify a couple of columns columns = records.array([["aaa", 1.3], ["bbb", .1]], formats="a3,f8") table.modify_columns(start=0, step=2, columns=columns, names=["col2", "col3"]) # Create the modified recarray r1 = records.array([[456, 'aaa', 1.3], [2, 'ded', 1.3], [457, 'bbb', .1], [5, 'de1', 1.3]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test10a(self): "Checking modifying rows using coordinates (readCoords/modifyCoords)." if common.verbose: print('\n', '-=' * 30) print("Running %s.test10a..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) columns = table.read_coordinates([0, 3]) # Modify both rows columns['col1'][:] = [55, 56] columns['col3'][:] = [1.9, 1.8] # Modify the table in the same coordinates table.modify_coordinates([0, 3], columns) # Create the modified recarray r1 = records.array([[55, b'dbe', 1.9], [2, b'ded', 1.3], [457, b'db1', 1.2], [56, b'de1', 1.8]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test10b(self): "Checking modifying rows using coordinates (getitem/setitem)." if common.verbose: print('\n', '-=' * 30) print("Running %s.test10b..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # append new rows r = records.array([[456, b'dbe', 1.2], [ 2, b'ded', 1.3]], formats="i4,a3,f8") table.append(r) table.append([[457, b'db1', 1.2], [5, b'de1', 1.3]]) columns = table[[0, 3]] # Modify both rows columns['col1'][:] = [55, 56] columns['col3'][:] = [1.9, 1.8] # Modify the table in the same coordinates table[[0, 3]] = columns # Create the modified recarray r1 = records.array([[55, b'dbe', 1.9], [2, b'ded', 1.3], [457, b'db1', 1.2], [56, b'de1', 1.8]], formats="i4,a3,f8", names="col1,col2,col3") # Read the modified table if self.reopen: fileh.close() fileh = open_file(file, "r") table = fileh.root.recarray r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) class RecArrayIO1(RecArrayIO): reopen = 0 class RecArrayIO2(RecArrayIO): reopen = 1 class CopyTestCase(unittest.TestCase): def assertEqualColinstances(self, table1, table2): """Assert that column instance maps of both tables are equal.""" cinst1, cinst2 = table1.colinstances, table2.colinstances self.assertEqual(len(cinst1), len(cinst2)) for (cpathname, col1) in cinst1.iteritems(): self.assertTrue(cpathname in cinst2) col2 = cinst2[cpathname] self.assertTrue(isinstance(col1, type(col2))) if isinstance(col1, Column): self.assertEqual(col1.name, col2.name) self.assertEqual(col1.pathname, col2.pathname) self.assertEqual(col1.dtype, col2.dtype) self.assertEqual(col1.type, col2.type) elif isinstance(col1, Cols): self.assertEqual(col1._v_colnames, col2._v_colnames) self.assertEqual(col1._v_colpathnames, col2._v_colpathnames) def test01_copy(self): """Checking Table.copy() method.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray r = records.array([[456, b'dbe', 1.2], [ 2, b'de', 1.3]], names='col1,col2,col3') # Save it in a table: table1 = fileh.create_table(fileh.root, 'table1', r, "title table1") if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") table1 = fileh.root.table1 # Copy to another table table2 = table1.copy('/', 'table2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") table1 = fileh.root.table1 table2 = fileh.root.table2 if common.verbose: print("table1-->", table1.read()) print("table2-->", table2.read()) # print "dirs-->", dir(table1), dir(table2) print("attrs table1-->", repr(table1.attrs)) print("attrs table2-->", repr(table2.attrs)) # Check that all the elements are equal for row1 in table1: nrow = row1.nrow # current row # row1 is a Row instance, while table2[] is a # RecArray.Record instance # print "reprs-->", repr(row1), repr(table2.read(nrow)) for colname in table1.colnames: # Both ways to compare works well # self.assertEqual(row1[colname], table2[nrow][colname)) self.assertEqual(row1[colname], table2.read(nrow, field=colname)[0]) # Assert other properties in table self.assertEqual(table1.nrows, table2.nrows) self.assertEqual(table1.shape, table2.shape) self.assertEqual(table1.colnames, table2.colnames) self.assertEqual(table1.coldtypes, table2.coldtypes) self.assertEqualColinstances(table1, table2) self.assertEqual(repr(table1.description), repr(table2.description)) # This could be not the same when re-opening the file # self.assertEqual(table1.description._v_ColObjects, # table2.description._v_ColObjects) # Leaf attributes self.assertEqual(table1.title, table2.title) self.assertEqual(table1.filters.complevel, table2.filters.complevel) self.assertEqual(table1.filters.complib, table2.filters.complib) self.assertEqual(table1.filters.shuffle, table2.filters.shuffle) self.assertEqual(table1.filters.fletcher32, table2.filters.fletcher32) # Close the file fileh.close() os.remove(file) def test02_copy(self): """Checking Table.copy() method (where specified)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray r = records.array([[456, b'dbe', 1.2], [ 2, b'de', 1.3]], names='col1,col2,col3') # Save it in a table: table1 = fileh.create_table(fileh.root, 'table1', r, "title table1") if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") table1 = fileh.root.table1 # Copy to another table in another group group1 = fileh.create_group("/", "group1") table2 = table1.copy(group1, 'table2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") table1 = fileh.root.table1 table2 = fileh.root.group1.table2 if common.verbose: print("table1-->", table1.read()) print("table2-->", table2.read()) print("attrs table1-->", repr(table1.attrs)) print("attrs table2-->", repr(table2.attrs)) # Check that all the elements are equal for row1 in table1: nrow = row1.nrow # current row for colname in table1.colnames: # Both ways to compare works well # self.assertEqual(row1[colname], table2[nrow][colname)) self.assertEqual(row1[colname], table2.read(nrow, field=colname)[0]) # Assert other properties in table self.assertEqual(table1.nrows, table2.nrows) self.assertEqual(table1.shape, table2.shape) self.assertEqual(table1.colnames, table2.colnames) self.assertEqual(table1.coldtypes, table2.coldtypes) self.assertEqualColinstances(table1, table2) self.assertEqual(repr(table1.description), repr(table2.description)) # Leaf attributes self.assertEqual(table1.title, table2.title) self.assertEqual(table1.filters.complevel, table2.filters.complevel) self.assertEqual(table1.filters.complib, table2.filters.complib) self.assertEqual(table1.filters.shuffle, table2.filters.shuffle) self.assertEqual(table1.filters.fletcher32, table2.filters.fletcher32) # Close the file fileh.close() os.remove(file) def test03_copy(self): """Checking Table.copy() method (table larger than buffer)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray exceeding buffers capability # This works, but takes too much CPU for a test # It is better to reduce the buffer size (table1.nrowsinbuf) # r=records.array(b'aaaabbbbccccddddeeeeffffgggg'*20000, # formats='2i2,i4, (2,3)u2, (1,)f4, f8',shape=700) r = records.array(b'aaaabbbbccccddddeeeeffffgggg' * 200, formats='2i2,i4, (2,3)u2, (1,)f4, f8', shape=7) # Save it in a table: table1 = fileh.create_table(fileh.root, 'table1', r, "title table1") if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") table1 = fileh.root.table1 # Copy to another table in another group and other title group1 = fileh.create_group("/", "group1") table1.nrowsinbuf = 2 # small value of buffer table2 = table1.copy(group1, 'table2', title="title table2") if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") table1 = fileh.root.table1 table2 = fileh.root.group1.table2 if common.verbose: print("table1-->", table1.read()) print("table2-->", table2.read()) print("attrs table1-->", repr(table1.attrs)) print("attrs table2-->", repr(table2.attrs)) # Check that all the elements are equal for row1 in table1: nrow = row1.nrow # current row for colname in table1.colnames: # self.assertTrue(allequal(row1[colname], # table2[nrow][colname])) self.assertTrue(allequal(row1[colname], table2.read(nrow, field=colname)[0])) # Assert other properties in table self.assertEqual(table1.nrows, table2.nrows) self.assertEqual(table1.shape, table2.shape) self.assertEqual(table1.colnames, table2.colnames) self.assertEqual(table1.coldtypes, table2.coldtypes) self.assertEqualColinstances(table1, table2) self.assertEqual(repr(table1.description), repr(table2.description)) # Leaf attributes self.assertEqual("title table2", table2.title) self.assertEqual(table1.filters.complevel, table2.filters.complevel) self.assertEqual(table1.filters.complib, table2.filters.complib) self.assertEqual(table1.filters.shuffle, table2.filters.shuffle) self.assertEqual(table1.filters.fletcher32, table2.filters.fletcher32) # Close the file fileh.close() os.remove(file) def test04_copy(self): """Checking Table.copy() method (different compress level)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray r = records.array([[456, b'dbe', 1.2], [ 2, b'de', 1.3]], names='col1,col2,col3') # Save it in a table: table1 = fileh.create_table(fileh.root, 'table1', r, "title table1") if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") table1 = fileh.root.table1 # Copy to another table in another group group1 = fileh.create_group("/", "group1") table2 = table1.copy(group1, 'table2', filters=Filters(complevel=6)) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") table1 = fileh.root.table1 table2 = fileh.root.group1.table2 if common.verbose: print("table1-->", table1.read()) print("table2-->", table2.read()) print("attrs table1-->", repr(table1.attrs)) print("attrs table2-->", repr(table2.attrs)) # Check that all the elements are equal for row1 in table1: nrow = row1.nrow # current row for colname in table1.colnames: # Both ways to compare works well # self.assertEqual(row1[colname], table2[nrow][colname)) self.assertEqual(row1[colname], table2.read(nrow, field=colname)[0]) # Assert other properties in table self.assertEqual(table1.nrows, table2.nrows) self.assertEqual(table1.shape, table2.shape) self.assertEqual(table1.colnames, table2.colnames) self.assertEqual(table1.coldtypes, table2.coldtypes) self.assertEqualColinstances(table1, table2) self.assertEqual(repr(table1.description), repr(table2.description)) # Leaf attributes self.assertEqual(table1.title, table2.title) self.assertEqual(6, table2.filters.complevel) self.assertEqual(1, table2.filters.shuffle) self.assertEqual(table1.filters.fletcher32, table2.filters.fletcher32) # Close the file fileh.close() os.remove(file) def test05_copy(self): """Checking Table.copy() method (user attributes copied)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray r = records.array([[456, b'dbe', 1.2], [ 2, b'de', 1.3]], names='col1,col2,col3') # Save it in a table: table1 = fileh.create_table(fileh.root, 'table1', r, "title table1") # Add some user attributes table1.attrs.attr1 = "attr1" table1.attrs.attr2 = 2 if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") table1 = fileh.root.table1 # Copy to another table in another group group1 = fileh.create_group("/", "group1") table2 = table1.copy(group1, 'table2', copyuserattrs=1, filters=Filters(complevel=6)) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") table1 = fileh.root.table1 table2 = fileh.root.group1.table2 if common.verbose: print("table1-->", table1.read()) print("table2-->", table2.read()) print("attrs table1-->", repr(table1.attrs)) print("attrs table2-->", repr(table2.attrs)) # Check that all the elements are equal for row1 in table1: nrow = row1.nrow # current row for colname in table1.colnames: # self.assertEqual(row1[colname], table2[nrow][colname)) self.assertEqual(row1[colname], table2.read(nrow, field=colname)[0]) # Assert other properties in table self.assertEqual(table1.nrows, table2.nrows) self.assertEqual(table1.shape, table2.shape) self.assertEqual(table1.colnames, table2.colnames) self.assertEqual(table1.coldtypes, table2.coldtypes) self.assertEqualColinstances(table1, table2) self.assertEqual(repr(table1.description), repr(table2.description)) # Leaf attributes self.assertEqual(table1.title, table2.title) self.assertEqual(6, table2.filters.complevel) self.assertEqual(1, table2.filters.shuffle) self.assertEqual(table1.filters.fletcher32, table2.filters.fletcher32) # User attributes self.assertEqual(table2.attrs.attr1, "attr1") self.assertEqual(table2.attrs.attr2, 2) # Close the file fileh.close() os.remove(file) def test05b_copy(self): """Checking Table.copy() method (user attributes not copied)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05b_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray r = records.array([[456, b'dbe', 1.2], [ 2, b'de', 1.3]], names='col1,col2,col3') # Save it in a table: table1 = fileh.create_table(fileh.root, 'table1', r, "title table1") # Add some user attributes table1.attrs.attr1 = "attr1" table1.attrs.attr2 = 2 if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") table1 = fileh.root.table1 # Copy to another table in another group group1 = fileh.create_group("/", "group1") table2 = table1.copy(group1, 'table2', copyuserattrs=0, filters=Filters(complevel=6)) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") table1 = fileh.root.table1 table2 = fileh.root.group1.table2 if common.verbose: print("table1-->", table1.read()) print("table2-->", table2.read()) print("attrs table1-->", repr(table1.attrs)) print("attrs table2-->", repr(table2.attrs)) # Check that all the elements are equal for row1 in table1: nrow = row1.nrow # current row for colname in table1.colnames: # self.assertEqual(row1[colname], table2[nrow][colname)) self.assertEqual(row1[colname], table2.read(nrow, field=colname)[0]) # Assert other properties in table self.assertEqual(table1.nrows, table2.nrows) self.assertEqual(table1.shape, table2.shape) self.assertEqual(table1.colnames, table2.colnames) self.assertEqual(table1.coldtypes, table2.coldtypes) self.assertEqualColinstances(table1, table2) self.assertEqual(repr(table1.description), repr(table2.description)) # Leaf attributes self.assertEqual(table1.title, table2.title) self.assertEqual(6, table2.filters.complevel) self.assertEqual(1, table2.filters.shuffle) self.assertEqual(table1.filters.fletcher32, table2.filters.fletcher32) # User attributes # self.assertEqual(table2.attrs.attr1, None) # self.assertEqual(table2.attrs.attr2, None) self.assertEqual(hasattr(table2.attrs, "attr1"), 0) self.assertEqual(hasattr(table2.attrs, "attr2"), 0) # Close the file fileh.close() os.remove(file) class CloseCopyTestCase(CopyTestCase): close = 1 class OpenCopyTestCase(CopyTestCase): close = 0 class CopyIndexTestCase(unittest.TestCase): def test01_index(self): """Checking Table.copy() method with indexes.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_index..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray exceeding buffers capability r = records.array(b'aaaabbbbccccddddeeeeffffgggg' * 200, formats='2i2, (1,)i4, (2,3)u2, (1,)f4, (1,)f8', shape=10) # The line below exposes a bug in numpy # formats='2i2, i4, (2,3)u2, f4, f8',shape=10) # Save it in a table: table1 = fileh.create_table(fileh.root, 'table1', r, "title table1") if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") table1 = fileh.root.table1 # Copy to another table table1.nrowsinbuf = self.nrowsinbuf table2 = table1.copy("/", 'table2', start=self.start, stop=self.stop, step=self.step) if common.verbose: print("table1-->", table1.read()) print("table2-->", table2.read()) print("attrs table1-->", repr(table1.attrs)) print("attrs table2-->", repr(table2.attrs)) # Check that all the elements are equal r2 = r[self.start:self.stop:self.step] for nrow in range(r2.shape[0]): for colname in table1.colnames: self.assertTrue(allequal(r2[nrow][colname], table2[nrow][colname])) # Assert the number of rows in table if common.verbose: print("nrows in table2-->", table2.nrows) print("and it should be-->", r2.shape[0]) self.assertEqual(r2.shape[0], table2.nrows) # Close the file fileh.close() os.remove(file) def test02_indexclosef(self): """Checking Table.copy() method with indexes (close file version)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_indexclosef..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray exceeding buffers capability r = records.array(b'aaaabbbbccccddddeeeeffffgggg' * 200, formats='2i2, i4, (2,3)u2, f4, f8', shape=10) # Save it in a table: table1 = fileh.create_table(fileh.root, 'table1', r, "title table1") if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") table1 = fileh.root.table1 # Copy to another table table1.nrowsinbuf = self.nrowsinbuf table2 = table1.copy("/", 'table2', start=self.start, stop=self.stop, step=self.step) fileh.close() fileh = open_file(file, mode="r") table1 = fileh.root.table1 table2 = fileh.root.table2 if common.verbose: print("table1-->", table1.read()) print("table2-->", table2.read()) print("attrs table1-->", repr(table1.attrs)) print("attrs table2-->", repr(table2.attrs)) # Check that all the elements are equal r2 = r[self.start:self.stop:self.step] for nrow in range(r2.shape[0]): for colname in table1.colnames: self.assertTrue(allequal(r2[nrow][colname], table2[nrow][colname])) # Assert the number of rows in table if common.verbose: print("nrows in table2-->", table2.nrows) print("and it should be-->", r2.shape[0]) self.assertEqual(r2.shape[0], table2.nrows) # Close the file fileh.close() os.remove(file) class CopyIndex1TestCase(CopyIndexTestCase): nrowsinbuf = 2 close = 1 start = 0 stop = 7 step = 1 class CopyIndex2TestCase(CopyIndexTestCase): nrowsinbuf = 2 close = 0 start = 0 stop = -1 step = 1 class CopyIndex3TestCase(CopyIndexTestCase): nrowsinbuf = 3 close = 1 start = 1 stop = 7 step = 1 class CopyIndex4TestCase(CopyIndexTestCase): nrowsinbuf = 4 close = 0 start = 0 stop = 6 step = 1 class CopyIndex5TestCase(CopyIndexTestCase): nrowsinbuf = 2 close = 1 start = 3 stop = 7 step = 1 class CopyIndex6TestCase(CopyIndexTestCase): nrowsinbuf = 2 close = 0 start = 3 stop = 6 step = 2 class CopyIndex7TestCase(CopyIndexTestCase): nrowsinbuf = 2 close = 1 start = 0 stop = 7 step = 10 class CopyIndex8TestCase(CopyIndexTestCase): nrowsinbuf = 2 close = 0 start = 6 stop = 3 step = 1 class CopyIndex9TestCase(CopyIndexTestCase): nrowsinbuf = 2 close = 1 start = 3 stop = 4 step = 1 class CopyIndex10TestCase(CopyIndexTestCase): nrowsinbuf = 1 close = 0 start = 3 stop = 4 step = 2 class CopyIndex11TestCase(CopyIndexTestCase): nrowsinbuf = 2 close = 1 start = -3 stop = -1 step = 2 class CopyIndex12TestCase(CopyIndexTestCase): nrowsinbuf = 3 close = 0 start = -1 # Should point to the last element stop = None # None should mean the last element (including it) step = 1 class LargeRowSize(unittest.TestCase): def test00(self): "Checking saving a Table with a moderately large rowsize" file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray r = records.array([[np.arange(100)]*2]) # Save it in a table: fileh.create_table(fileh.root, 'largerow', r) # Read it again r2 = fileh.root.largerow.read() self.assertEqual(r.tostring(), r2.tostring()) fileh.close() os.remove(file) def test01(self): "Checking saving a Table with an extremely large rowsize" file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray r = records.array([[np.arange(40000)]*4]) # 640 KB # Save it in a table: # try: # fileh.create_table(fileh.root, 'largerow', r) # except ValueError: # if common.verbose: # (type, value, traceback) = sys.exc_info() # print "\nGreat!, the next ValueError was catched!" # print value # else: # self.fail("expected a ValueError") # From PyTables 1.3 on, we allow row sizes equal or larger than 640 KB fileh.create_table(fileh.root, 'largerow', r) # Read it again r2 = fileh.root.largerow.read() self.assertEqual(r.tostring(), r2.tostring()) fileh.close() os.remove(file) class DefaultValues(unittest.TestCase): record = Record def test00(self): "Checking saving a Table with default values (using the same Row)" file = tempfile.mktemp(".h5") # file = "/tmp/test.h5" fileh = open_file(file, "w") # Create a table table = fileh.create_table(fileh.root, 'table', self.record) table.nrowsinbuf = 46 # minimum amount that reproduces a problem # Take a number of records a bit greater nrows = int(table.nrowsinbuf * 1.1) row = table.row # Fill the table with nrows records for i in xrange(nrows): if i == 3: row['var2'] = 2 if i == 4: row['var3'] = 3 # This injects the row values. row.append() # We need to flush the buffers in table in order to get an # accurate number of records on it. table.flush() # Create a recarray with the same default values values = [b"abcd", 1, 2, 3.1, 4.2, 5, "e", 1, 1j, 1 + 0j] formats = 'a4,i4,i2,f8,f4,u2,a1,b1,c8,c16'.split(',') if 'Float16Col' in globals(): values.append(6.4) formats.append('f2') if 'Float96Col' in globals(): values.append(6.4) formats.append('f12') if 'Float128Col' in globals(): values.append(6.4) formats.append('f16') if 'Complex192Col' in globals(): values.append(1.-0.j) formats.append('c24') if 'Complex256Col' in globals(): values.append(1.-0.j) formats.append('c32') r = records.array([values]*nrows, formats=','.join(formats)) # Assign the value exceptions r["f1"][3] = 2 r["f2"][4] = 3 # Read the table in another recarray # r2 = table.read() r2 = table[::] # Equivalent to table.read() # This generates too much output. Activate only when # self.nrowsinbuf is very small (<10) if common.verbose: print("First 10 table values:") for row in table.iterrows(0, 10): print(row) print("The first 5 read recarray values:") print(r2[:5]) print("Records should look like:") print(r[:5]) for name1, name2 in zip(r.dtype.names, r2.dtype.names): self.assertTrue(allequal(r[name1], r2[name2])) # The following can give false errors when columns with extended # precision data type are present in the record. # It is probably due to some difference in the value of bits used # for patting (longdoubles use just 80 bits but are stored in 96 or # 128 bits in numpy arrays) # self.assertEqual(r.tostring(), r2.tostring()) fileh.close() os.remove(file) def test01(self): "Checking saving a Table with default values (using different Row)" file = tempfile.mktemp(".h5") # file = "/tmp/test.h5" fileh = open_file(file, "w") # Create a table table = fileh.create_table(fileh.root, 'table', self.record) table.nrowsinbuf = 46 # minimum amount that reproduces a problem # Take a number of records a bit greater nrows = int(table.nrowsinbuf * 1.1) # Fill the table with nrows records for i in xrange(nrows): if i == 3: table.row['var2'] = 2 if i == 4: table.row['var3'] = 3 # This injects the row values. table.row.append() # We need to flush the buffers in table in order to get an # accurate number of records on it. table.flush() # Create a recarray with the same default values values = [b"abcd", 1, 2, 3.1, 4.2, 5, "e", 1, 1j, 1 + 0j] formats = 'a4,i4,i2,f8,f4,u2,a1,b1,c8,c16'.split(',') if 'Float16Col' in globals(): values.append(6.4) formats.append('f2') if 'Float96Col' in globals(): values.append(6.4) formats.append('f12') if 'Float128Col' in globals(): values.append(6.4) formats.append('f16') if 'Complex192Col' in globals(): values.append(1.-0.j) formats.append('c24') if 'Complex256Col' in globals(): values.append(1.-0.j) formats.append('c32') r = records.array([values]*nrows, formats=','.join(formats)) # Assign the value exceptions r["f1"][3] = 2 r["f2"][4] = 3 # Read the table in another recarray # r2 = table.read() r2 = table[::] # Equivalent to table.read() # This generates too much output. Activate only when # self.nrowsinbuf is very small (<10) if common.verbose: print("First 10 table values:") for row in table.iterrows(0, 10): print(row) print("The first 5 read recarray values:") print(r2[:5]) print("Records should look like:") print(r[:5]) for name1, name2 in zip(r.dtype.names, r2.dtype.names): self.assertTrue(allequal(r[name1], r2[name2])) # The following can give false errors when columns with extended # precision data type are present in the record. # It is probably due to some difference in the value of bits used # for patting (longdoubles use just 80 bits but are stored in 96 or # 128 bits in numpy arrays) # self.assertEqual(r.tostring(), r2.tostring()) fileh.close() os.remove(file) class OldRecordDefaultValues(DefaultValues): title = "OldRecordDefaultValues" record = OldRecord class Record2(IsDescription): var1 = StringCol(itemsize=4, dflt=b"abcd") # 4-character String var2 = IntCol(dflt=1) # integer var3 = Int16Col(dflt=2) # short integer var4 = Float64Col(dflt=3.1) # double (double-precision) class LengthTestCase(unittest.TestCase): record = Record nrows = 20 def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") self.rootgroup = self.fileh.root self.populateFile() def populateFile(self): # Create a table table = self.fileh.create_table(self.fileh.root, 'table', self.record, title="__length__ test") # Get the row object associated with the new table row = table.row # Fill the table for i in xrange(self.nrows): row.append() # Flush the buffer for this table table.flush() self.table = table def tearDown(self): if self.fileh.isopen: self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test01_lengthrows(self): """Checking __length__ in Table.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_lengthrows..." % self.__class__.__name__) # Number of rows len(self.table) == self.nrows def test02_lengthcols(self): """Checking __length__ in Cols.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_lengthcols..." % self.__class__.__name__) # Number of columns if self.record is Record: len(self.table.cols) == 8 elif self.record is Record2: len(self.table.cols) == 4 def test03_lengthcol(self): """Checking __length__ in Column.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_lengthcol..." % self.__class__.__name__) # Number of rows for all columns column for colname in self.table.colnames: len(getattr(self.table.cols, colname)) == self.nrows class Length1TestCase(LengthTestCase): record = Record nrows = 20 class Length2TestCase(LengthTestCase): record = Record2 nrows = 100 class WhereAppendTestCase(common.TempFileMixin, common.PyTablesTestCase): """Tests `Table.append_where()` method.""" class SrcTblDesc(IsDescription): id = IntCol() v1 = FloatCol() v2 = StringCol(itemsize=8) def setUp(self): super(WhereAppendTestCase, self).setUp() tbl = self.h5file.create_table('/', 'test', self.SrcTblDesc) row = tbl.row row['id'] = 1 row['v1'] = 1.5 row['v2'] = 'a' * 8 row.append() row['id'] = 2 row['v1'] = 2.5 row['v2'] = 'b' * 6 row.append() tbl.flush() def test00_same(self): """Query with same storage.""" DstTblDesc = self.SrcTblDesc tbl1 = self.h5file.root.test tbl2 = self.h5file.create_table('/', 'test2', DstTblDesc) tbl1.append_where(tbl2, 'id > 1') # Rows resulting from the query are those in the new table. it2 = iter(tbl2) for r1 in tbl1.where('id > 1'): r2 = next(it2) self.assertTrue(r1['id'] == r2['id'] and r1['v1'] == r2['v1'] and r1['v2'] == r2['v2']) # There are no more rows. self.assertRaises(StopIteration, it2.next) def test01_compatible(self): """Query with compatible storage.""" class DstTblDesc(IsDescription): id = FloatCol() # float, not int v1 = FloatCol() v2 = StringCol(itemsize=16) # a longer column v3 = FloatCol() # extra column tbl1 = self.h5file.root.test tbl2 = self.h5file.create_table('/', 'test2', DstTblDesc) tbl1.append_where(tbl2, 'id > 1') # Rows resulting from the query are those in the new table. it2 = iter(tbl2) for r1 in tbl1.where('id > 1'): r2 = next(it2) self.assertTrue(r1['id'] == r2['id'] and r1['v1'] == r2['v1'] and r1['v2'] == r2['v2']) # There are no more rows. self.assertRaises(StopIteration, it2.next) def test02_lessPrecise(self): """Query with less precise storage.""" class DstTblDesc(IsDescription): id = IntCol() v1 = IntCol() # int, not float v2 = StringCol(itemsize=8) tbl1 = self.h5file.root.test tbl2 = self.h5file.create_table('/', 'test2', DstTblDesc) tbl1.append_where(tbl2, 'id > 1') # Rows resulting from the query are those in the new table. it2 = iter(tbl2) for r1 in tbl1.where('id > 1'): r2 = next(it2) self.assertTrue(r1['id'] == r2['id'] and int(r1['v1']) == r2['v1'] and r1['v2'] == r2['v2']) # There are no more rows. self.assertRaises(StopIteration, it2.next) def test03_incompatible(self): """Query with incompatible storage.""" class DstTblDesc(IsDescription): id = StringCol(itemsize=4) # string, not int v1 = FloatCol() v2 = StringCol(itemsize=8) tbl1 = self.h5file.root.test tbl2 = self.h5file.create_table('/', 'test2', DstTblDesc) self.assertRaises(NotImplementedError, tbl1.append_where, tbl2, 'v1 == b"1"') def test04_noColumn(self): """Query with storage lacking columns.""" class DstTblDesc(IsDescription): # no ``id`` field v1 = FloatCol() v2 = StringCol(itemsize=8) tbl1 = self.h5file.root.test tbl2 = self.h5file.create_table('/', 'test2', DstTblDesc) self.assertRaises(KeyError, tbl1.append_where, tbl2, 'id > 1') def test05_otherFile(self): """Appending to a table in another file.""" h5fname2 = tempfile.mktemp(suffix='.h5') h5file2 = open_file(h5fname2, 'w') try: tbl1 = self.h5file.root.test tbl2 = h5file2.create_table('/', 'test', self.SrcTblDesc) # RW to RW. tbl1.append_where(tbl2, 'id > 1') # RW to RO. h5file2.close() h5file2 = open_file(h5fname2, 'r') tbl2 = h5file2.root.test self.assertRaises(FileModeError, tbl1.append_where, tbl2, 'id > 1') # RO to RO. self._reopen('r') tbl1 = self.h5file.root.test self.assertRaises(FileModeError, tbl1.append_where, tbl2, 'id > 1') # RO to RW. h5file2.close() h5file2 = open_file(h5fname2, 'a') tbl2 = h5file2.root.test tbl1.append_where(tbl2, 'id > 1') finally: h5file2.close() os.remove(h5fname2) class DerivedTableTestCase(unittest.TestCase): def setUp(self): self.file = tempfile.mktemp('.h5') self.fileh = open_file(self.file, 'w', title='DeriveFromTable') self.fileh.create_table('/', 'original', Record) def tearDown(self): self.fileh.close() os.remove(self.file) def test00(self): """Deriving a table from the description of another.""" tbl1 = self.fileh.root.original tbl2 = self.fileh.create_table('/', 'derived', tbl1.description) self.assertEqual(tbl1.description, tbl2.description) class ChunkshapeTestCase(unittest.TestCase): def setUp(self): self.file = tempfile.mktemp('.h5') self.fileh = open_file(self.file, 'w', title='Chunkshape test') self.fileh.create_table('/', 'table', Record, chunkshape=13) def tearDown(self): self.fileh.close() os.remove(self.file) def test00(self): """Test setting the chunkshape in a table (no reopen).""" tbl = self.fileh.root.table if common.verbose: print("chunkshape-->", tbl.chunkshape) self.assertEqual(tbl.chunkshape, (13,)) def test01(self): """Test setting the chunkshape in a table (reopen).""" self.fileh.close() self.fileh = open_file(self.file, 'r') tbl = self.fileh.root.table if common.verbose: print("chunkshape-->", tbl.chunkshape) self.assertEqual(tbl.chunkshape, (13,)) # Test for appending zero-sized recarrays class ZeroSizedTestCase(unittest.TestCase): def setUp(self): self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "a") # Create a Table t = self.fileh.create_table('/', 'table', {'c1': Int32Col(), 'c2': Float64Col()}) # Append a single row t.append([(1, 2.2)]) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def test01_canAppend(self): "Appending zero length recarray." t = self.fileh.root.table a = np.empty(shape=(0,), dtype='i4,f8') t.append(a) self.assertEqual(t.nrows, 1, "The number of rows should be 1.") # Case for testing ticket #103, i.e. selections in columns which are # aligned but that its data length is not an exact multiple of the # length of the record. This exposes the problem only in 32-bit # machines, because in 64-bit machine, 'c2' is unaligned. However, # this should check most platforms where, while not unaligned, # len(datatype) > boundary_alignment is fullfilled. class IrregularStrideTestCase(unittest.TestCase): def setUp(self): class IRecord(IsDescription): c1 = Int32Col(pos=1) c2 = Float64Col(pos=2) self.file = tempfile.mktemp('.h5') self.fileh = open_file(self.file, 'w', title='Chunkshape test') table = self.fileh.create_table('/', 'table', IRecord) for i in range(10): table.row['c1'] = i table.row['c2'] = i table.row.append() table.flush() def tearDown(self): self.fileh.close() os.remove(self.file) def test00(self): """Selecting rows in a table with irregular stride (but aligned).""" table = self.fileh.root.table coords1 = table.get_where_list('c1<5') coords2 = table.get_where_list('c2<5') if common.verbose: print("\nSelected coords1-->", coords1) print("Selected coords2-->", coords2) self.assertTrue(allequal(coords1, np.arange(5, dtype=SizeType))) self.assertTrue(allequal(coords2, np.arange(5, dtype=SizeType))) class Issue262TestCase(unittest.TestCase): def setUp(self): class IRecord(IsDescription): c1 = Int32Col(pos=1) c2 = Float64Col(pos=2) self.file = tempfile.mktemp('.h5') self.fileh = open_file(self.file, 'w', title='Chunkshape test') table = self.fileh.create_table('/', 'table', IRecord) table.nrowsinbuf = 3 for i in range(20): table.row['c1'] = i table.row['c2'] = i table.row.append() table.row['c1'] = i % 29 table.row['c2'] = 300 - i table.row.append() table.row['c1'] = 300 - i table.row['c2'] = 100 + i % 30 table.row.append() table.flush() def tearDown(self): self.fileh.close() os.remove(self.file) def test_gh260(self): """Regression test for gh-260""" table = self.fileh.root.table coords1 = table.get_where_list('(c1>5)&(c2<30)', start=0, step=2) coords2 = table.get_where_list('(c1>5)&(c2<30)', start=1, step=2) data = table.read() data = data[np.where((data['c1'] > 5) & (data['c2'] < 30))] if common.verbose: print() print("Selected coords1-->", coords1) print("Selected coords2-->", coords2) print("Selected data-->", data) self.assertEqual(len(coords1) + len(coords2), len(data)) def test_gh262_01(self): """Regression test for gh-262 (start=0, step=1)""" table = self.fileh.root.table data = table.get_where_list('(c1>5)&(~(c1>5))', start=0, step=1) if common.verbose: print() print("data -->", data) self.assertEqual(len(data), 0) def test_gh262_02(self): """Regression test for gh-262 (start=1, step=1)""" table = self.fileh.root.table data = table.get_where_list('(c1>5)&(~(c1>5))', start=1, step=1) if common.verbose: print() print("data -->", data) self.assertEqual(len(data), 0) def test_gh262_03(self): """Regression test for gh-262 (start=0, step=2)""" table = self.fileh.root.table data = table.get_where_list('(c1>5)&(~(c1>5))', start=0, step=2) if common.verbose: print() print("data -->", data) self.assertEqual(len(data), 0) def test_gh262_04(self): """Regression test for gh-262 (start=1, step=2)""" table = self.fileh.root.table data = table.get_where_list('(c1>5)&(~(c1>5))', start=1, step=2) if common.verbose: print() print("data -->", data) self.assertEqual(len(data), 0) class TruncateTestCase(unittest.TestCase): def setUp(self): self.file = tempfile.mktemp('.h5') self.fileh = open_file(self.file, 'w', title='Chunkshape test') table = self.fileh.create_table('/', 'table', self.IRecord) # Fill just a couple of rows for i in range(2): table.row['c1'] = i table.row['c2'] = i table.row.append() table.flush() # The defaults self.dflts = table.coldflts def tearDown(self): # Close the file self.fileh.close() os.remove(self.file) common.cleanup(self) def test00_truncate(self): """Checking Table.truncate() method (truncating to 0 rows)""" table = self.fileh.root.table # Truncate to 0 elements table.truncate(0) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") table = self.fileh.root.table if common.verbose: print("table-->", table.read()) self.assertEqual(table.nrows, 0) for row in table: self.assertEqual(row['c1'], row.nrow) def test01_truncate(self): """Checking Table.truncate() method (truncating to 1 rows)""" table = self.fileh.root.table # Truncate to 1 element table.truncate(1) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") table = self.fileh.root.table if common.verbose: print("table-->", table.read()) self.assertEqual(table.nrows, 1) for row in table: self.assertEqual(row['c1'], row.nrow) def test02_truncate(self): """Checking Table.truncate() method (truncating to == self.nrows)""" table = self.fileh.root.table # Truncate to 2 elements table.truncate(2) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") table = self.fileh.root.table if common.verbose: print("table-->", table.read()) self.assertEqual(table.nrows, 2) for row in table: self.assertEqual(row['c1'], row.nrow) def test03_truncate(self): """Checking Table.truncate() method (truncating to > self.nrows)""" table = self.fileh.root.table # Truncate to 4 elements table.truncate(4) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") table = self.fileh.root.table if common.verbose: print("table-->", table.read()) self.assertEqual(table.nrows, 4) # Check the original values for row in table.iterrows(start=0, stop=2): self.assertEqual(row['c1'], row.nrow) # Check that the added rows have the default values for row in table.iterrows(start=2, stop=4): self.assertEqual(row['c1'], self.dflts['c1']) self.assertEqual(row['c2'], self.dflts['c2']) class TruncateOpen1(TruncateTestCase): class IRecord(IsDescription): c1 = Int32Col(pos=1) c2 = FloatCol(pos=2) close = 0 class TruncateOpen2(TruncateTestCase): class IRecord(IsDescription): c1 = Int32Col(pos=1, dflt=3) c2 = FloatCol(pos=2, dflt=-3.1) close = 0 class TruncateClose1(TruncateTestCase): class IRecord(IsDescription): c1 = Int32Col(pos=1) c2 = FloatCol(pos=2) close = 1 class TruncateClose2(TruncateTestCase): class IRecord(IsDescription): c1 = Int32Col(pos=1, dflt=4) c2 = FloatCol(pos=2, dflt=3.1) close = 1 class PointSelectionTestCase(common.PyTablesTestCase): def setUp(self): N = 100 # Limits for selections self.limits = [ (0, 1), # just one element (20, -10), # no elements (-10, 4), # several elements (0, 10), # several elements (again) ] # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = fileh = open_file(self.file, "w") # Create a sample tables self.data = data = np.arange(N) self.recarr = recarr = np.empty(N, dtype="i4,f4") recarr["f0"][:] = data recarr["f1"][:] = data self.table = fileh.create_table(fileh.root, 'table', recarr) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def test01a_read(self): """Test for point-selections (read, boolean keys).""" data = self.data recarr = self.recarr table = self.table for value1, value2 in self.limits: key = (data >= value1) & (data < value2) if common.verbose: print("Selection to test:", key) a = recarr[key] b = table[key] if common.verbose: print("NumPy selection:", a) print("PyTables selection:", b) npt.assert_array_equal( a, b, "NumPy array and PyTables selections does not match.") def test01b_read(self): """Test for point-selections (read, tuples of integers keys).""" data = self.data recarr = self.recarr table = self.table for value1, value2 in self.limits: key = np.where((data >= value1) & (data < value2)) if common.verbose: print("Selection to test:", key, type(key)) a = recarr[key] b = table[key] # if common.verbose: # print "NumPy selection:", a # print "PyTables selection:", b npt.assert_array_equal( a, b, "NumPy array and PyTables selections does not match.") def test01c_read(self): """Test for point-selections (read, tuples of floats keys).""" data = self.data recarr = self.recarr table = self.table for value1, value2 in self.limits: key = np.where((data >= value1) & (data < value2)) if common.verbose: print("Selection to test:", key) recarr[key] fkey = np.array(key, "f4") self.assertRaises(TypeError, table.__getitem__, fkey) def test01d_read(self): """Test for point-selections (read, numpy keys).""" data = self.data recarr = self.recarr table = self.table for value1, value2 in self.limits: key = np.where((data >= value1) & (data < value2))[0] if common.verbose: print("Selection to test:", key, type(key)) a = recarr[key] b = table[key] # if common.verbose: # print "NumPy selection:", a # print "PyTables selection:", b npt.assert_array_equal( a, b, "NumPy array and PyTables selections does not match.") def test01e_read(self): """Test for point-selections (read, list keys).""" data = self.data recarr = self.recarr table = self.table for value1, value2 in self.limits: key = np.where((data >= value1) & (data < value2))[0].tolist() if common.verbose: print("Selection to test:", key, type(key)) a = recarr[key] b = table[key] # if common.verbose: # print "NumPy selection:", a # print "PyTables selection:", b npt.assert_array_equal( a, b, "NumPy array and PyTables selections does not match.") def test02a_write(self): """Test for point-selections (write, boolean keys).""" data = self.data recarr = self.recarr table = self.table for value1, value2 in self.limits: key = np.where((data >= value1) & (data < value2)) if common.verbose: print("Selection to test:", key) s = recarr[key] # Modify the s recarray s["f0"][:] = data[:len(s)]*2 s["f1"][:] = data[:len(s)]*3 # Modify recarr and table recarr[key] = s table[key] = s a = recarr[:] b = table[:] # if common.verbose: # print "NumPy modified array:", a # print "PyTables modifyied array:", b npt.assert_array_equal( a, b, "NumPy array and PyTables modifications does not match.") def test02b_write(self): """Test for point-selections (write, integer keys).""" data = self.data recarr = self.recarr table = self.table for value1, value2 in self.limits: key = np.where((data >= value1) & (data < value2)) if common.verbose: print("Selection to test:", key) s = recarr[key] # Modify the s recarray s["f0"][:] = data[:len(s)]*2 s["f1"][:] = data[:len(s)]*3 # Modify recarr and table recarr[key] = s table[key] = s a = recarr[:] b = table[:] # if common.verbose: # print "NumPy modified array:", a # print "PyTables modifyied array:", b npt.assert_array_equal( a, b, "NumPy array and PyTables modifications does not match.") # Test for building very large MD columns without defaults class MDLargeColTestCase(common.TempFileMixin, common.PyTablesTestCase): def test01_create(self): "Create a Table with a very large MD column. Ticket #211." N = 2**18 # 4x larger than maximum object header size (64 KB) cols = {'col1': Int8Col(shape=N, dflt=0)} tbl = self.h5file.create_table('/', 'test', cols) tbl.row.append() # add a single row tbl.flush() if self.reopen: self._reopen('a') tbl = self.h5file.root.test # Check the value if common.verbose: print("First row-->", tbl[0]['col1']) npt.assert_array_equal(tbl[0]['col1'], np.zeros(N, 'i1')) class MDLargeColNoReopen(MDLargeColTestCase): reopen = False class MDLargeColReopen(MDLargeColTestCase): reopen = True # Test with itertools.groupby that iterates on exhausted Row iterator # See ticket #264. class ExhaustedIter(common.PyTablesTestCase): def setUp(self): """Create small database.""" class Observations(IsDescription): market_id = IntCol(pos=0) scenario_id = IntCol(pos=1) value = Float32Col(pos=3) self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, 'w') table = self.fileh.create_table('/', 'observations', Observations, chunkshape=32) # fill the database observations = np.arange(225) row = table.row for market_id in xrange(5): for scenario_id in xrange(3): for obs in observations: row['market_id'] = market_id row['scenario_id'] = scenario_id row['value'] = obs row.append() table.flush() def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def average(self, values): return sum(values, 0.0) / len(values) def f_scenario(self, row): return row['scenario_id'] def test00_groupby(self): """Checking iterating an exhausted iterator (ticket #264)""" from itertools import groupby rows = self.fileh.root.observations.where('(market_id == 3)') scenario_means = [] for scenario_id, rows_grouped in groupby(rows, self.f_scenario): vals = [row['value'] for row in rows_grouped] scenario_means.append(self.average(vals)) if common.verbose: print('Means -->', scenario_means) self.assertEqual(scenario_means, [112.0, 112.0, 112.0]) def test01_groupby(self): """Checking iterating an exhausted iterator (ticket #264). Reopen. """ from itertools import groupby self.fileh.close() self.fileh = open_file(self.file, 'r') rows = self.fileh.root.observations.where('(market_id == 3)') scenario_means = [] for scenario_id, rows_grouped in groupby(rows, self.f_scenario): vals = [row['value'] for row in rows_grouped] scenario_means.append(self.average(vals)) if common.verbose: print('Means -->', scenario_means) self.assertEqual(scenario_means, [112.0, 112.0, 112.0]) class SpecialColnamesTestCase(common.TempFileMixin, common.PyTablesTestCase): def test00_check_names(self): f = self.h5file a = np.array([(1, 2, 3)], dtype=[( "a", int), ("_b", int), ("__c", int)]) t = f.create_table(f.root, "test", a) self.assertEqual(len(t.colnames), 3, "Number of columns incorrect") if common.verbose: print("colnames -->", t.colnames) for name, name2 in zip(t.colnames, ("a", "_b", "__c")): self.assertEqual(name, name2) class RowContainsTestCase(common.TempFileMixin, common.PyTablesTestCase): def test00_row_contains(self): f = self.h5file a = np.array([(1, 2, 3)], dtype="i1,i2,i4") t = f.create_table(f.root, "test", a) row = [r for r in t.iterrows()][0] if common.verbose: print("row -->", row[:]) for item in (1, 2, 3): self.assertTrue(item in row) self.assertTrue(4 not in row) class AccessClosedTestCase(common.TempFileMixin, common.PyTablesTestCase): def setUp(self): super(AccessClosedTestCase, self).setUp() self.table = self.h5file.create_table( self.h5file.root, 'table', Record) row = self.table.row for i in range(10): row['var1'] = '%04d' % i row['var2'] = i row['var3'] = i % 3 row.append() self.table.flush() def test_read(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.table.read) def test_getitem(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.table.__getitem__, 0) def test_setitem(self): data = self.table[0] self.h5file.close() self.assertRaises(ClosedNodeError, self.table.__setitem__, 0, data) def test_append(self): data = self.table[0] self.h5file.close() self.assertRaises(ClosedNodeError, self.table.append, data) def test_readWhere(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.table.read_where, 'var2 > 3') def test_whereAppend(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.table.append_where, self.table, 'var2 > 3') def test_getWhereList(self): self.h5file.close() self.assertRaises( ClosedNodeError, self.table.get_where_list, 'var2 > 3') def test_readSorted(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.table.read_sorted, 'var2') def test_readCoordinates(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.table.read_coordinates, [2, 5]) class ColumnIterationTestCase(unittest.TestCase): def setUp(self): self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w") self.buffer_size = self.fileh.params['IO_BUFFER_SIZE'] def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def create_non_nested_table(self, nrows, dtype): array = np.empty((nrows, ), dtype) for name in dtype.names: array[name] = np.random.randint(0, 10000, nrows) table = self.fileh.create_table('/', 'table', dtype) table.append(array) return array, table def iterate(self, array, table): row_num = 0 for item in table.cols.f0: self.assertEqual(item, array['f0'][row_num]) row_num += 1 self.assertEqual(row_num, len(array)) def test_less_than_io_buffer(self): dtype = np.format_parser(['i8'] * 3, [], []).dtype rows_in_buffer = self.buffer_size // dtype[0].itemsize array, table = self.create_non_nested_table(rows_in_buffer // 2, dtype) self.iterate(array, table) def test_more_than_io_buffer(self): dtype = np.format_parser(['i8'] * 3, [], []).dtype rows_in_buffer = self.buffer_size // dtype[0].itemsize array, table = self.create_non_nested_table(rows_in_buffer * 3, dtype) self.iterate(array, table) def test_partially_filled_buffer(self): dtype = np.format_parser(['i8'] * 3, [], []).dtype rows_in_buffer = self.buffer_size // dtype[0].itemsize array, table = self.create_non_nested_table(rows_in_buffer * 2 + 2, dtype) self.iterate(array, table) def test_zero_length_table(self): dtype = np.format_parser(['i8'] * 3, [], []).dtype array, table = self.create_non_nested_table(0, dtype) self.assertEqual(len(table), 0) self.iterate(array, table) class TestCreateTableArgs(common.TempFileMixin, common.PyTablesTestCase): obj = np.array( [('aaaa', 1, 2.1), ('bbbb', 2, 3.2)], dtype=[('name', 'S4'), ('icol', np.int32), ('fcol', np.float32)]) where = '/' name = 'table' description, _ = descr_from_dtype(obj.dtype) title = 'title' filters = None expectedrows = 10000 chunkshape = None byteorder = None createparents = False def test_positional_args_01(self): self.h5file.create_table(self.where, self.name, self.description, self.title, self.filters, self.expectedrows) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, (0,)) self.assertEqual(ptarr.nrows, 0) self.assertEqual(tuple(ptarr.colnames), self.obj.dtype.names) def test_positional_args_02(self): ptarr = self.h5file.create_table(self.where, self.name, self.description, self.title, self.filters, self.expectedrows) ptarr.append(self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, (len(self.obj),)) self.assertEqual(ptarr.nrows, len(self.obj)) self.assertEqual(tuple(ptarr.colnames), self.obj.dtype.names) self.assertEqual(nparr.dtype, self.obj.dtype) self.assertTrue(allequal(self.obj, nparr)) def test_positional_args_obj(self): self.h5file.create_table(self.where, self.name, None, self.title, self.filters, self.expectedrows, self.chunkshape, self.byteorder, self.createparents, self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, (len(self.obj),)) self.assertEqual(ptarr.nrows, len(self.obj)) self.assertEqual(tuple(ptarr.colnames), self.obj.dtype.names) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj(self): self.h5file.create_table(self.where, self.name, title=self.title, obj=self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, (len(self.obj),)) self.assertEqual(ptarr.nrows, len(self.obj)) self.assertEqual(tuple(ptarr.colnames), self.obj.dtype.names) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_description_01(self): ptarr = self.h5file.create_table(self.where, self.name, title=self.title, description=self.description) ptarr.append(self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, (len(self.obj),)) self.assertEqual(ptarr.nrows, len(self.obj)) self.assertEqual(tuple(ptarr.colnames), self.obj.dtype.names) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_description_02(self): ptarr = self.h5file.create_table(self.where, self.name, title=self.title, description=self.description) #ptarr.append(self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, (0,)) self.assertEqual(ptarr.nrows, 0) self.assertEqual(tuple(ptarr.colnames), self.obj.dtype.names) def test_kwargs_obj_description(self): ptarr = self.h5file.create_table(self.where, self.name, title=self.title, obj=self.obj, description=self.description) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read() self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, (len(self.obj),)) self.assertEqual(ptarr.nrows, len(self.obj)) self.assertEqual(tuple(ptarr.colnames), self.obj.dtype.names) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj_description_error_01(self): self.assertRaises(TypeError, self.h5file.create_table, self.where, self.name, title=self.title, obj=self.obj, description=Record) def test_kwargs_obj_description_error_02(self): self.assertRaises(TypeError, self.h5file.create_table, self.where, self.name, title=self.title, obj=self.obj, description=Record()) def test_kwargs_obj_description_error_03(self): self.assertRaises(TypeError, self.h5file.create_table, self.where, self.name, title=self.title, obj=self.obj, description=RecordDescriptionDict) #---------------------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() niter = 1 # common.heavy = 1 # uncomment this only for testing purposes for n in range(niter): theSuite.addTest(unittest.makeSuite(BasicWriteTestCase)) theSuite.addTest(unittest.makeSuite(OldRecordBasicWriteTestCase)) theSuite.addTest(unittest.makeSuite(DictWriteTestCase)) if sys.version_info[0] < 3: theSuite.addTest(unittest.makeSuite(DictWriteTestCase2)) theSuite.addTest(unittest.makeSuite(NumPyDTWriteTestCase)) theSuite.addTest(unittest.makeSuite(RecArrayOneWriteTestCase)) theSuite.addTest(unittest.makeSuite(RecArrayTwoWriteTestCase)) theSuite.addTest(unittest.makeSuite(RecArrayThreeWriteTestCase)) theSuite.addTest(unittest.makeSuite(CompressBloscTablesTestCase)) theSuite.addTest(unittest.makeSuite( CompressBloscShuffleTablesTestCase)) theSuite.addTest(unittest.makeSuite( CompressBloscBloscLZTablesTestCase)) if 'lz4' in tables.blosc_compressor_list(): theSuite.addTest(unittest.makeSuite( CompressBloscLZ4TablesTestCase)) theSuite.addTest(unittest.makeSuite( CompressBloscLZ4HCTablesTestCase)) if 'snappy' in tables.blosc_compressor_list(): theSuite.addTest(unittest.makeSuite( CompressBloscSnappyTablesTestCase)) if 'zlib' in tables.blosc_compressor_list(): theSuite.addTest(unittest.makeSuite( CompressBloscZlibTablesTestCase)) theSuite.addTest(unittest.makeSuite(CompressLZOTablesTestCase)) theSuite.addTest(unittest.makeSuite(CompressLZOShuffleTablesTestCase)) theSuite.addTest(unittest.makeSuite(CompressZLIBTablesTestCase)) theSuite.addTest(unittest.makeSuite(CompressZLIBShuffleTablesTestCase)) theSuite.addTest(unittest.makeSuite(Fletcher32TablesTestCase)) theSuite.addTest(unittest.makeSuite(AllFiltersTablesTestCase)) theSuite.addTest(unittest.makeSuite(CompressTwoTablesTestCase)) theSuite.addTest(unittest.makeSuite( SizeOnDiskInMemoryPropertyTestCase)) theSuite.addTest(unittest.makeSuite(NonNestedTableReadTestCase)) theSuite.addTest(unittest.makeSuite(TableReadByteorderTestCase)) theSuite.addTest(unittest.makeSuite(IterRangeTestCase)) theSuite.addTest(unittest.makeSuite(RecArrayRangeTestCase)) theSuite.addTest(unittest.makeSuite(getColRangeTestCase)) theSuite.addTest(unittest.makeSuite(getItemTestCase)) theSuite.addTest(unittest.makeSuite(setItem1)) theSuite.addTest(unittest.makeSuite(setItem2)) theSuite.addTest(unittest.makeSuite(setItem3)) theSuite.addTest(unittest.makeSuite(setItem4)) theSuite.addTest(unittest.makeSuite(updateRow1)) theSuite.addTest(unittest.makeSuite(updateRow2)) theSuite.addTest(unittest.makeSuite(updateRow3)) theSuite.addTest(unittest.makeSuite(updateRow4)) theSuite.addTest(unittest.makeSuite(RecArrayIO1)) theSuite.addTest(unittest.makeSuite(RecArrayIO2)) theSuite.addTest(unittest.makeSuite(OpenCopyTestCase)) theSuite.addTest(unittest.makeSuite(CloseCopyTestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex1TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex2TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex3TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex4TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex5TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex6TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex7TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex8TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex9TestCase)) theSuite.addTest(unittest.makeSuite(DefaultValues)) theSuite.addTest(unittest.makeSuite(OldRecordDefaultValues)) theSuite.addTest(unittest.makeSuite(Length1TestCase)) theSuite.addTest(unittest.makeSuite(Length2TestCase)) theSuite.addTest(unittest.makeSuite(WhereAppendTestCase)) theSuite.addTest(unittest.makeSuite(DerivedTableTestCase)) theSuite.addTest(unittest.makeSuite(ChunkshapeTestCase)) theSuite.addTest(unittest.makeSuite(ZeroSizedTestCase)) theSuite.addTest(unittest.makeSuite(IrregularStrideTestCase)) theSuite.addTest(unittest.makeSuite(Issue262TestCase)) theSuite.addTest(unittest.makeSuite(TruncateOpen1)) theSuite.addTest(unittest.makeSuite(TruncateOpen2)) theSuite.addTest(unittest.makeSuite(TruncateClose1)) theSuite.addTest(unittest.makeSuite(TruncateClose2)) theSuite.addTest(unittest.makeSuite(PointSelectionTestCase)) theSuite.addTest(unittest.makeSuite(MDLargeColNoReopen)) theSuite.addTest(unittest.makeSuite(MDLargeColReopen)) theSuite.addTest(unittest.makeSuite(ExhaustedIter)) theSuite.addTest(unittest.makeSuite(SpecialColnamesTestCase)) theSuite.addTest(unittest.makeSuite(RowContainsTestCase)) theSuite.addTest(unittest.makeSuite(AccessClosedTestCase)) theSuite.addTest(unittest.makeSuite(ColumnIterationTestCase)) theSuite.addTest(unittest.makeSuite(TestCreateTableArgs)) if common.heavy: theSuite.addTest(unittest.makeSuite(CompressBzip2TablesTestCase)) theSuite.addTest(unittest.makeSuite( CompressBzip2ShuffleTablesTestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex10TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex11TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex12TestCase)) theSuite.addTest(unittest.makeSuite(LargeRowSize)) theSuite.addTest(unittest.makeSuite(BigTablesTestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/test_tablesMD.py000066400000000000000000002430511231437614300212340ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import sys import unittest import os import tempfile import numpy as np from numpy import rec as records from tables import * from tables.tests import common from tables.tests.common import allequal from tables.description import descr_from_dtype # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup # It is important that columns are ordered according to their names # to ease the comparison with structured arrays. # Test Record class class Record(IsDescription): var0 = StringCol(itemsize=4, dflt=b"", shape=2) # 4-character string array var1 = StringCol(itemsize=4, dflt=[b"abcd", b"efgh"], shape=(2, 2)) var1_ = IntCol(dflt=((1, 1),), shape=2) # integer array var2 = IntCol(dflt=((1, 1), (1, 1)), shape=(2, 2)) # integer array var3 = Int16Col(dflt=2) # short integer var4 = FloatCol(dflt=3.1) # double (double-precision) var5 = Float32Col(dflt=4.2) # float (single-precision) var6 = UInt16Col(dflt=5) # unsigned short integer var7 = StringCol(itemsize=1, dflt=b"e") # 1-character String # Dictionary definition RecordDescriptionDict = { 'var0': StringCol(itemsize=4, dflt=b"", shape=2), # 4-character string # array 'var1': StringCol(itemsize=4, dflt=[b"abcd", b"efgh"], shape=(2, 2)), #'var0': StringCol(itemsize=4, shape=2), # 4-character String #'var1': StringCol(itemsize=4, shape=(2,2)), # 4-character String 'var1_': IntCol(shape=2), # integer array 'var2': IntCol(shape=(2, 2)), # integer array 'var3': Int16Col(), # short integer 'var4': FloatCol(), # double (double-precision) 'var5': Float32Col(), # float (single-precision) 'var6': Int16Col(), # unsigned short integer 'var7': StringCol(itemsize=1), # 1-character String } # Record class with numpy dtypes (mixed shapes is checked here) class RecordDT(IsDescription): var0 = Col.from_dtype(np.dtype("2S4"), dflt=b"") # shape in dtype var1 = Col.from_dtype(np.dtype(("S4", ( 2, 2))), dflt=[b"abcd", b"efgh"]) # shape is a mix var1_ = Col.from_dtype(np.dtype("2i4"), dflt=((1, 1),)) # shape in dtype var2 = Col.from_sctype("i4", shape=( 2, 2), dflt=((1, 1), (1, 1))) # shape is a mix var3 = Col.from_dtype(np.dtype("i2"), dflt=2) var4 = Col.from_dtype(np.dtype("2f8"), dflt=3.1) var5 = Col.from_dtype(np.dtype("f4"), dflt=4.2) var6 = Col.from_dtype(np.dtype("()u2"), dflt=5) var7 = Col.from_dtype(np.dtype("1S1"), dflt=b"e") # no shape class BasicTestCase(common.PyTablesTestCase): # file = "test.h5" mode = "w" title = "This is the table title" expectedrows = 100 appendrows = 20 compress = 0 complib = "zlib" # Default compression library record = Record recarrayinit = 0 maxshort = 1 << 15 def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, self.mode) self.rootgroup = self.fileh.root self.populateFile() self.fileh.close() def initRecArray(self): record = self.recordtemplate row = record[0] buflist = [] # Fill the recarray for i in xrange(self.expectedrows + 1): tmplist = [] # Both forms (list or chararray) works var0 = ['%04d' % (self.expectedrows - i)] * 2 tmplist.append(var0) var1 = [['%04d' % (self.expectedrows - i)] * 2] * 2 tmplist.append(var1) var1_ = (i, 1) tmplist.append(var1_) var2 = ((i, 1), (1, 1)) # *-* tmplist.append(var2) var3 = i % self.maxshort tmplist.append(var3) if isinstance(row['var4'], np.ndarray): tmplist.append([float(i), float(i * i)]) else: tmplist.append(float(i)) if isinstance(row['var5'], np.ndarray): tmplist.append(np.array((float(i),)*4)) else: tmplist.append(float(i)) # var6 will be like var3 but byteswaped tmplist.append(((var3 >> 8) & 0xff) + ((var3 << 8) & 0xff00)) var7 = var1[0][0][-1] tmplist.append(var7) buflist.append(tmplist) self.record = np.rec.array(buflist, dtype=record.dtype, shape=self.expectedrows) return def populateFile(self): group = self.rootgroup if self.recarrayinit: # Initialize an starting buffer, if any self.initRecArray() for j in range(3): # Create a table filters = Filters(complevel=self.compress, complib=self.complib) if j < 2: byteorder = sys.byteorder else: # table2 will be byteswapped byteorder = {"little": "big", "big": "little"}[sys.byteorder] table = self.fileh.create_table(group, 'table'+str(j), self.record, title=self.title, filters=filters, expectedrows=self.expectedrows, byteorder=byteorder) if not self.recarrayinit: # Get the row object associated with the new table row = table.row # Fill the table for i in xrange(self.expectedrows): s = '%04d' % (self.expectedrows - i) row['var0'] = s.encode('ascii') row['var1'] = s.encode('ascii') row['var7'] = s[-1].encode('ascii') row['var1_'] = (i, 1) row['var2'] = ((i, 1), (1, 1)) # *-* row['var3'] = i % self.maxshort if isinstance(row['var4'], np.ndarray): row['var4'] = [float(i), float(i * i)] else: row['var4'] = float(i) if isinstance(row['var5'], np.ndarray): row['var5'] = np.array((float(i),)*4) else: row['var5'] = float(i) # var6 will be like var3 but byteswaped row['var6'] = (((row['var3'] >> 8) & 0xff) + ((row['var3'] << 8) & 0xff00)) row.append() # Flush the buffer for this table table.flush() # Create a new group (descendant of group) group2 = self.fileh.create_group(group, 'group'+str(j)) # Iterate over this new group (group2) group = group2 def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test00_description(self): """Checking table description and descriptive fields.""" self.fileh = open_file(self.file) tbl = self.fileh.get_node('/table0') desc = tbl.description if isinstance(self.record, dict): columns = self.record elif isinstance(self.record, np.ndarray): descr, _ = descr_from_dtype(self.record.dtype) columns = descr._v_colobjects elif isinstance(self.record, np.dtype): descr, _ = descr_from_dtype(self.record) columns = descr._v_colobjects else: # This is an ordinary description. columns = self.record.columns # Check table and description attributes at the same time. # These checks are only valid for non-nested tables. # Column names. expectedNames = ['var0', 'var1', 'var1_', 'var2', 'var3', 'var4', 'var5', 'var6', 'var7'] self.assertEqual(expectedNames, list(tbl.colnames)) self.assertEqual(expectedNames, list(desc._v_names)) # Column types. expectedTypes = [columns[colname].dtype for colname in expectedNames] self.assertEqual(expectedTypes, [tbl.coldtypes[v] for v in expectedNames]) self.assertEqual(expectedTypes, [desc._v_dtypes[v] for v in expectedNames]) # Column string types. expectedTypes = [columns[colname].type for colname in expectedNames] self.assertEqual(expectedTypes, [tbl.coltypes[v] for v in expectedNames]) self.assertEqual(expectedTypes, [desc._v_types[v] for v in expectedNames]) # Column defaults. for v in expectedNames: if common.verbose: print("dflt-->", columns[v].dflt) print("coldflts-->", tbl.coldflts[v]) print("desc.dflts-->", desc._v_dflts[v]) self.assertTrue(common.areArraysEqual(tbl.coldflts[v], columns[v].dflt)) self.assertTrue(common.areArraysEqual(desc._v_dflts[v], columns[v].dflt)) # Column path names. self.assertEqual(expectedNames, list(desc._v_pathnames)) # Column objects. for colName in expectedNames: expectedCol = columns[colName] col = desc._v_colobjects[colName] self.assertEqual(expectedCol.dtype, col.dtype) self.assertEqual(expectedCol.type, col.type) def test01_readTable(self): """Checking table read and cuts.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_readTable..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") # Choose a small value for buffer size table.nrowsinbuf = 3 # Read the records and select those with "var2" file less than 20 result = [r['var2'][0][0] for r in table.iterrows() if r['var2'][0][0] < 20] if common.verbose: print("Table:", repr(table)) print("Nrows in", table._v_pathname, ":", table.nrows) print("Last record in table ==>", r) print("Total selected records in table ==> ", len(result)) nrows = self.expectedrows - 1 r = [r for r in table.iterrows() if r['var2'][0][0] < 20][-1] self.assertEqual(( r['var0'][0], r['var1'][0][0], r['var1_'][0], r['var2'][0][0], r['var7'] ), (b"0001", b"0001", nrows, nrows, b"1")) if isinstance(r['var5'], np.ndarray): self.assertTrue(allequal(r['var5'], np.array((nrows,)*4, np.float32))) else: self.assertEqual(r['var5'], float(nrows)) self.assertEqual(len(result), 20) def test01b_readTable(self): """Checking table read and cuts (multidimensional columns case)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01b_readTable..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") # Choose a small value for buffer size table.nrowsinbuf = 3 # Read the records and select those with "var2" file less than 20 result = [r['var5'] for r in table.iterrows() if r['var2'][0][0] < 20] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("Last record in table ==>", r) print("Total selected records in table ==> ", len(result)) nrows = table.nrows r = [r for r in table.iterrows() if r['var2'][0][0] < 20][-1] if isinstance(r['var5'], np.ndarray): self.assertTrue(allequal(result[0], np.array((float(0),)*4, np.float32))) self.assertTrue(allequal(result[1], np.array((float(1),)*4, np.float32))) self.assertTrue(allequal(result[2], np.array((float(2),)*4, np.float32))) self.assertTrue(allequal(result[3], np.array((float(3),)*4, np.float32))) self.assertTrue(allequal(result[10], np.array((float(10),)*4, np.float32))) self.assertTrue(allequal(r['var5'], np.array((float(nrows-1),)*4, np.float32))) else: self.assertEqual(r['var5'], float(nrows-1)) self.assertEqual(len(result), 20) # Read the records and select those with "var2" file less than 20 result = [r['var1'] for r in table.iterrows() if r['var2'][0][0] < 20] r = [r for r in table.iterrows() if r['var2'][0][0] < 20][-1] if r['var1'].dtype.char == "S": a = np.array([['%04d' % (self.expectedrows - 0)]*2]*2, 'S') self.assertTrue(allequal(result[0], a)) a = np.array([['%04d' % (self.expectedrows - 1)]*2]*2, 'S') self.assertTrue(allequal(result[1], a)) a = np.array([['%04d' % (self.expectedrows - 2)]*2]*2, 'S') self.assertTrue(allequal(result[2], a)) a = np.array([['%04d' % (self.expectedrows - 3)]*2]*2, 'S') self.assertTrue(allequal(result[3], a)) a = np.array([['%04d' % (self.expectedrows - 10)]*2]*2, 'S') self.assertTrue(allequal(result[10], a)) a = np.array([['%04d' % (1)]*2]*2, 'S') self.assertTrue(allequal(r['var1'], a)) else: self.assertEqual(r['var1'], "0001") self.assertEqual(len(result), 20) def test01c_readTable(self): """Checking shape of multidimensional columns.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01c_readTable..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") if common.verbose: print("var2 col shape:", table.cols.var2.shape) print("Should be:", table.cols.var2[:].shape) self.assertEqual(table.cols.var2.shape, table.cols.var2[:].shape) def test02_AppendRows(self): """Checking whether appending record rows works or not.""" # Now, open it, but in "append" mode self.fileh = open_file(self.file, mode="a") self.rootgroup = self.fileh.root if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_AppendRows..." % self.__class__.__name__) # Get a table table = self.fileh.get_node("/group0/table1") # Get their row object row = table.row if common.verbose: print("Nrows in old", table._v_pathname, ":", table.nrows) print("Record Format ==>", table.description._v_nested_formats) print("Record Size ==>", table.rowsize) # Append some rows for i in xrange(self.appendrows): s = '%04d' % (self.appendrows - i) row['var0'] = s.encode('ascii') row['var1'] = s.encode('ascii') row['var7'] = s[-1].encode('ascii') row['var1_'] = (i, 1) row['var2'] = ((i, 1), (1, 1)) # *-* row['var3'] = i % self.maxshort if isinstance(row['var4'], np.ndarray): row['var4'] = [float(i), float(i * i)] else: row['var4'] = float(i) if isinstance(row['var5'], np.ndarray): row['var5'] = np.array((float(i),)*4) else: row['var5'] = float(i) row.append() # Flush the buffer for this table and read it table.flush() result = [row['var2'][0][0] for row in table.iterrows() if row['var2'][0][0] < 20] row = [r for r in table.iterrows() if r['var2'][0][0] < 20][-1] nrows = self.appendrows - 1 self.assertEqual(( row['var0'][0], row['var1'][0][0], row['var1_'][0], row['var2'][0][0], row['var7']), (b"0001", b"0001", nrows, nrows, b"1")) if isinstance(row['var5'], np.ndarray): self.assertTrue(allequal(row['var5'], np.array((float(nrows),)*4, np.float32))) else: self.assertEqual(row['var5'], float(nrows)) if self.appendrows <= 20: add = self.appendrows else: add = 20 self.assertEqual(len(result), 20 + add) # because we appended new rows # del table # CAVEAT: The next test only works for tables with rows < 2**15 def test03_endianess(self): """Checking if table is endianess aware.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_endianess..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/group0/group1/table2") # Read the records and select the ones with "var3" column less than 20 result = [r['var2'] for r in table.iterrows() if r['var3'] < 20] if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) print("On-disk byteorder ==>", table.byteorder) print("Last record in table ==>", r) print("Total selected records in table ==>", len(result)) nrows = self.expectedrows - 1 r = list(table.iterrows())[-1] self.assertEqual((r['var1'][0][0], r['var3']), (b"0001", nrows)) self.assertEqual(len(result), 20) class BasicWriteTestCase(BasicTestCase): title = "BasicWrite" pass class DictWriteTestCase(BasicTestCase): # This checks also unidimensional arrays as columns title = "DictWrite" record = RecordDescriptionDict nrows = 21 nrowsinbuf = 3 # Choose a small value for the buffer size start = 0 stop = 10 step = 3 class RecordDTWriteTestCase(BasicTestCase): title = "RecordDTWriteTestCase" record = RecordDT # Pure NumPy dtype class NumPyDTWriteTestCase(BasicTestCase): title = "NumPyDTWriteTestCase" record = np.dtype("(2,)S4,(2,2)S4,(2,)i4,(2,2)i4,i2,2f8,f4,i2,S1") record.names = 'var0,var1,var1_,var2,var3,var4,var5,var6,var7'.split(',') class RecArrayOneWriteTestCase(BasicTestCase): title = "RecArrayOneWrite" record = np.rec.array( None, formats="(2,)S4,(2,2)S4,(2,)i4,(2,2)i4,i2,2f8,f4,i2,S1", names='var0,var1,var1_,var2,var3,var4,var5,var6,var7', shape=0) class RecArrayTwoWriteTestCase(BasicTestCase): title = "RecArrayTwoWrite" expectedrows = 100 recarrayinit = 1 recordtemplate = np.rec.array( None, formats="(2,)a4,(2,2)a4,(2,)i4,(2,2)i4,i2,f8,f4,i2,a1", names='var0,var1,var1_,var2,var3,var4,var5,var6,var7', shape=1) class RecArrayThreeWriteTestCase(BasicTestCase): title = "RecArrayThreeWrite" expectedrows = 100 recarrayinit = 1 recordtemplate = np.rec.array( None, formats="(2,)a4,(2,2)a4,(2,)i4,(2,2)i4,i2,2f8,4f4,i2,a1", names='var0,var1,var1_,var2,var3,var4,var5,var6,var7', shape=1) class CompressBloscTablesTestCase(BasicTestCase): title = "CompressBloscTables" compress = 1 complib = "blosc" class CompressLZOTablesTestCase(BasicTestCase): title = "CompressLZOTables" compress = 1 complib = "lzo" class CompressBzip2TablesTestCase(BasicTestCase): title = "CompressBzip2Tables" compress = 1 complib = "bzip2" class CompressZLIBTablesTestCase(BasicTestCase): title = "CompressOneTables" compress = 1 complib = "zlib" class CompressTwoTablesTestCase(BasicTestCase): title = "CompressTwoTables" compress = 1 # This checks also unidimensional arrays as columns record = RecordDescriptionDict class BigTablesTestCase(BasicTestCase): title = "BigTables" # 10000 rows takes much more time than we can afford for tests # reducing to 1000 would be more than enough # F. Alted 2004-01-19 # expectedrows = 10000 # appendrows = 1000 expectedrows = 1000 appendrows = 100 class BasicRangeTestCase(unittest.TestCase): # file = "test.h5" mode = "w" title = "This is the table title" record = Record maxshort = 1 << 15 expectedrows = 100 compress = 0 # Default values nrows = 20 nrowsinbuf = 3 # Choose a small value for the buffer size start = 1 stop = nrows checkrecarray = 0 checkgetCol = 0 def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, self.mode) self.rootgroup = self.fileh.root self.populateFile() self.fileh.close() def populateFile(self): group = self.rootgroup for j in range(3): # Create a table table = self.fileh.create_table(group, 'table'+str(j), self.record, title=self.title, filters=Filters(self.compress), expectedrows=self.expectedrows) # Get the row object associated with the new table row = table.row # Fill the table for i in xrange(self.expectedrows): row['var1'] = '%04d' % (self.expectedrows - i) row['var7'] = row['var1'][0][0][-1] row['var2'] = i row['var3'] = i % self.maxshort if isinstance(row['var4'], np.ndarray): row['var4'] = [float(i), float(i * i)] else: row['var4'] = float(i) if isinstance(row['var5'], np.ndarray): row['var5'] = np.array((float(i),)*4) else: row['var5'] = float(i) # var6 will be like var3 but byteswaped row['var6'] = (((row['var3'] >> 8) & 0xff) + ((row['var3'] << 8) & 0xff00)) row.append() # Flush the buffer for this table table.flush() # Create a new group (descendant of group) group2 = self.fileh.create_group(group, 'group'+str(j)) # Iterate over this new group (group2) group = group2 def tearDown(self): if self.fileh.isopen: self.fileh.close() # del self.fileh, self.rootgroup os.remove(self.file) common.cleanup(self) #---------------------------------------- def check_range(self): # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") table = self.fileh.get_node("/table0") table.nrowsinbuf = self.nrowsinbuf r = slice(self.start, self.stop, self.step) resrange = r.indices(table.nrows) reslength = len(range(*resrange)) if self.checkrecarray: recarray = table.read(self.start, self.stop, self.step) result = [] for nrec in range(len(recarray)): if recarray['var2'][nrec][0][0] < self.nrows and 0 < self.step: result.append(recarray['var2'][nrec][0][0]) elif (recarray['var2'][nrec][0][0] > self.nrows and 0 > self.step): result.append(recarray['var2'][nrec][0][0]) elif self.checkgetCol: column = table.read(self.start, self.stop, self.step, 'var2') result = [] for nrec in range(len(column)): if column[nrec][0][0] < self.nrows and 0 < self.step: # *-* result.append(column[nrec][0][0]) # *-* elif column[nrec][0][0] > self.nrows and 0 > self.step: # *-* result.append(column[nrec][0][0]) # *-* else: if 0 < self.step: result = [ r['var2'][0][0] for r in table.iterrows(self.start, self.stop, self.step) if r['var2'][0][0] < self.nrows ] elif 0 > self.step: result = [ r['var2'][0][0] for r in table.iterrows(self.start, self.stop, self.step) if r['var2'][0][0] > self.nrows ] if self.start < 0: startr = self.expectedrows + self.start else: startr = self.start if self.stop is None: if self.checkrecarray or self.checkgetCol: # data read using the read method stopr = startr + 1 else: # data read using the iterrows method stopr = self.nrows elif self.stop < 0: stopr = self.expectedrows + self.stop else: stopr = self.stop if self.nrows < stopr: stopr = self.nrows if common.verbose: print("Nrows in", table._v_pathname, ":", table.nrows) if reslength: if self.checkrecarray: print("Last record *read* in recarray ==>", recarray[-1]) elif self.checkgetCol: print("Last value *read* in getCol ==>", column[-1]) else: print("Last record *read* in table range ==>", r) print("Total number of selected records ==>", len(result)) print("Selected records:\n", result) print("Selected records should look like:\n", range(startr, stopr, self.step)) print("start, stop, step ==>", startr, stopr, self.step) self.assertEqual(result, range(startr, stopr, self.step)) if not (self.checkrecarray or self.checkgetCol): if startr < stopr and 0 < self.step: r = [r['var2'] for r in table.iterrows(self.start, self.stop, self.step) if r['var2'][0][0] < self.nrows][-1] if self.nrows > self.expectedrows: self.assertEqual( r[0][0], range(self.start, self.stop, self.step)[-1]) else: self.assertEqual(r[0][0], range(startr, stopr, self.step)[-1]) elif startr > stopr and 0 > self.step: r = [r['var2'] for r in table.iterrows(self.start, self.stop, self.step) if r['var2'][0][0] > self.nrows][0] if self.nrows < self.expectedrows: self.assertEqual( r[0][0], range(self.start, self.stop or -1, self.step)[0]) else: self.assertEqual( r[0][0], range(startr, stopr or -1, self.step)[0]) # Close the file self.fileh.close() def test01_range(self): """Checking ranges in table iterators (case1)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_range..." % self.__class__.__name__) # Case where step < nrowsinbuf < 2 * step self.nrows = 21 self.nrowsinbuf = 3 self.start = 0 self.stop = self.expectedrows self.step = 2 self.check_range() def test01a_range(self): """Checking ranges in table iterators (case1)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_range..." % self.__class__.__name__) # Case where step < nrowsinbuf < 2 * step self.nrows = 21 self.nrowsinbuf = 3 self.start = self.expectedrows - 1 self.stop = None self.step = -2 self.check_range() def test02_range(self): """Checking ranges in table iterators (case2)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_range..." % self.__class__.__name__) # Case where step < nrowsinbuf < 10 * step self.nrows = 21 self.nrowsinbuf = 31 self.start = 11 self.stop = self.expectedrows self.step = 3 self.check_range() def test03_range(self): """Checking ranges in table iterators (case3)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_range..." % self.__class__.__name__) # Case where step < nrowsinbuf < 1.1 * step self.nrows = self.expectedrows self.nrowsinbuf = 11 # Choose a small value for the buffer size self.start = 0 self.stop = self.expectedrows self.step = 10 self.check_range() def test04_range(self): """Checking ranges in table iterators (case4)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_range..." % self.__class__.__name__) # Case where step == nrowsinbuf self.nrows = self.expectedrows self.nrowsinbuf = 11 # Choose a small value for the buffer size self.start = 1 self.stop = self.expectedrows self.step = 11 self.check_range() def test05_range(self): """Checking ranges in table iterators (case5)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05_range..." % self.__class__.__name__) # Case where step > 1.1 * nrowsinbuf self.nrows = 21 self.nrowsinbuf = 10 # Choose a small value for the buffer size self.start = 1 self.stop = self.expectedrows self.step = 11 self.check_range() def test06_range(self): """Checking ranges in table iterators (case6)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test06_range..." % self.__class__.__name__) # Case where step > 3 * nrowsinbuf self.nrows = 3 self.nrowsinbuf = 3 # Choose a small value for the buffer size self.start = 2 self.stop = self.expectedrows self.step = 10 self.check_range() def test07_range(self): """Checking ranges in table iterators (case7)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test07_range..." % self.__class__.__name__) # Case where start == stop self.nrows = 2 self.nrowsinbuf = 3 # Choose a small value for the buffer size self.start = self.nrows self.stop = self.nrows self.step = 10 self.check_range() def test08_range(self): """Checking ranges in table iterators (case8)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test08_range..." % self.__class__.__name__) # Case where start > stop self.nrows = 2 self.nrowsinbuf = 3 # Choose a small value for the buffer size self.start = self.nrows + 1 self.stop = self.nrows self.step = 1 self.check_range() def test09_range(self): """Checking ranges in table iterators (case9)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test09_range..." % self.__class__.__name__) # Case where stop = None self.nrows = 100 self.nrowsinbuf = 3 # Choose a small value for the buffer size self.start = 1 self.stop = 2 self.step = 1 self.check_range() def test10_range(self): """Checking ranges in table iterators (case10)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test10_range..." % self.__class__.__name__) # Case where start < 0 and stop = 0 self.nrows = self.expectedrows self.nrowsinbuf = 5 # Choose a small value for the buffer size self.start = -6 self.startr = self.expectedrows + self.start self.stop = 0 self.stopr = self.expectedrows + self.stop self.step = 2 self.check_range() def test11_range(self): """Checking ranges in table iterators (case11)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test11_range..." % self.__class__.__name__) # Case where start < 0 and stop < 0 self.nrows = self.expectedrows self.nrowsinbuf = 5 # Choose a small value for the buffer size self.start = -6 self.startr = self.expectedrows + self.start self.stop = -2 self.stopr = self.expectedrows + self.stop self.step = 1 self.check_range() def test12_range(self): """Checking ranges in table iterators (case12)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test12_range..." % self.__class__.__name__) # Case where start < 0 and stop < 0 and start > stop self.nrows = self.expectedrows self.nrowsinbuf = 5 # Choose a small value for the buffer size self.start = -1 self.startr = self.expectedrows + self.start self.stop = -2 self.stopr = self.expectedrows + self.stop self.step = 1 self.check_range() def test13_range(self): """Checking ranges in table iterators (case13)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test13_range..." % self.__class__.__name__) # Case where step < 0 self.step = -11 try: self.check_range() except ValueError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next ValueError was catched!") self.fileh.close() #else: # self.fail("expected a ValueError") # Case where step == 0 self.step = 0 try: self.check_range() except ValueError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next ValueError was catched!") self.fileh.close() #else: # self.fail("expected a ValueError") class IterRangeTestCase(BasicRangeTestCase): pass class RecArrayRangeTestCase(BasicRangeTestCase): checkrecarray = 1 class getColRangeTestCase(BasicRangeTestCase): checkgetCol = 1 def test01_nonexistentField(self): """Checking non-existing Field in getCol method """ if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_nonexistentField..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") self.root = self.fileh.root table = self.fileh.get_node("/table0") try: table.read(field='non-existent-column') except KeyError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next KeyError was catched!") else: self.fail("expected a KeyError") class Rec(IsDescription): col1 = IntCol(pos=1, shape=(2,)) col2 = StringCol(itemsize=3, pos=2, shape=(3,)) col3 = FloatCol(pos=3, shape=(3, 2)) class RecArrayIO(unittest.TestCase): def test00(self): "Checking saving a normal recarray" file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray intlist1 = [[456, 23]*3]*2 intlist2 = np.array([[2, 2]*3]*2, dtype=int) arrlist1 = [['dbe']*2]*3 arrlist2 = [['de']*2]*3 floatlist1 = [[1.2, 2.3]*3]*4 floatlist2 = np.array([[4.5, 2.4]*3]*4) b = [[intlist1, arrlist1, floatlist1], [ intlist2, arrlist2, floatlist2]] r = np.rec.array(b, formats='(2,6)i4,(3,2)a3,(4,6)f8', names='col1,col2,col3') # Save it in a table: fileh.create_table(fileh.root, 'recarray', r) # Read it again r2 = fileh.root.recarray.read() self.assertEqual(r.tostring(), r2.tostring()) fileh.close() os.remove(file) def test01(self): "Checking saving a recarray with an offset in its buffer" file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray intlist1 = [[456, 23]*3]*2 intlist2 = np.array([[2, 2]*3]*2, dtype=int) arrlist1 = [['dbe']*2]*3 arrlist2 = [['de']*2]*3 floatlist1 = [[1.2, 2.3]*3]*4 floatlist2 = np.array([[4.5, 2.4]*3]*4) b = [[intlist1, arrlist1, floatlist1], [ intlist2, arrlist2, floatlist2]] r = np.rec.array(b, formats='(2,6)i4,(3,2)a3,(4,6)f8', names='col1,col2,col3') # Get a view of the recarray r1 = r[1:] # Save it in a table: fileh.create_table(fileh.root, 'recarray', r1) # Read it again r2 = fileh.root.recarray.read() self.assertEqual(r1.tostring(), r2.tostring()) fileh.close() os.remove(file) def test02(self): "Checking saving a slice of a large recarray" file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray intlist1 = [[[23, 24, 35]*6]*6] intlist2 = np.array([[[2, 3, 4]*6]*6], dtype=int) arrlist1 = [['dbe']*2]*3 arrlist2 = [['de']*2]*3 floatlist1 = [[1.2, 2.3]*3]*4 floatlist2 = np.array([[4.5, 2.4]*3]*4) b = [[intlist1, arrlist1, floatlist1], [ intlist2, arrlist2, floatlist2]] r = np.rec.array(b * 300, formats='(1,6,18)i4,(3,2)a3,(4,6)f8', names='col1,col2,col3') # Get an slice of recarray r1 = r[290:292] # Save it in a table: fileh.create_table(fileh.root, 'recarray', r1) # Read it again r2 = fileh.root.recarray.read() self.assertEqual(r1.tostring(), r2.tostring()) fileh.close() os.remove(file) def test03(self): "Checking saving a slice of an strided recarray" file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a recarray intlist1 = [[[23, 24, 35]*6]*6] intlist2 = np.array([[[2, 3, 4]*6]*6], dtype=int) arrlist1 = [['dbe']*2]*3 arrlist2 = [['de']*2]*3 floatlist1 = [[1.2, 2.3]*3]*4 floatlist2 = np.array([[4.5, 2.4]*3]*4) b = [[intlist1, arrlist1, floatlist1], [ intlist2, arrlist2, floatlist2]] r = np.rec.array(b * 300, formats='(1,6,18)i4,(3,2)a3,(4,6)f8', names='col1,col2,col3', shape=300) # Get an strided recarray r2 = r[::2] # Get a slice r1 = r2[148:] # Save it in a table: fileh.create_table(fileh.root, 'recarray', r1) # Read it again r2 = fileh.root.recarray.read() self.assertEqual(r1.tostring(), r2.tostring()) fileh.close() os.remove(file) def test08a(self): "Checking modifying one column (single column version, list)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test08a..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # Append new rows s0, s1, s2, s3 = ['dbe']*3, ['ded']*3, ['db1']*3, ['de1']*3 f0, f1, f2, f3 = [[1.2]*2]*3, [[1.3]*2]*3, [[1.4]*2]*3, [[1.5]*2]*3 r = records.array([[[456, 457], s0, f0], [[2, 3], s1, f1]], formats="(2,)i4,(3,)a3,(3,2)f8") table.append(r) table.append([[[457, 458], s2, f2], [[5, 6], s3, f3]]) # Modify just one existing column table.cols.col1[1:] = [[[2, 3], [3, 4], [4, 5]]] # Create the modified recarray r1 = records.array([[[456, 457], s0, f0], [[2, 3], s1, f1], [[3, 4], s2, f2], [[4, 5], s3, f3]], formats="(2,)i4,(3,)a3,(3,2)f8", names="col1,col2,col3") # Read the modified table r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test08b(self): "Checking modifying one column (single column version, recarray)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test08b..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # Append new rows s0, s1, s2, s3 = ['dbe']*3, ['ded']*3, ['db1']*3, ['de1']*3 f0, f1, f2, f3 = [[1.2]*2]*3, [[1.3]*2]*3, [[1.4]*2]*3, [[1.5]*2]*3 r = records.array([[[456, 457], s0, f0], [[2, 3], s1, f1]], formats="(2,)i4,(3,)a3,(3,2)f8") table.append(r) table.append([[[457, 458], s2, f2], [[5, 6], s3, f3]]) # Modify just one existing column columns = records.fromarrays( np.array([[[2, 3], [3, 4], [4, 5]]]), formats="i4") table.modify_columns(start=1, columns=columns, names=["col1"]) # Create the modified recarray r1 = records.array([[[456, 457], s0, f0], [[2, 3], s1, f1], [[3, 4], s2, f2], [[4, 5], s3, f3]], formats="(2,)i4,(3,)a3,(3,2)f8", names="col1,col2,col3") # Read the modified table r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) def test08b2(self): """Checking modifying one column (single column version, recarray, modify_column)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test08b2..." % self.__class__.__name__) file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create a new table: table = fileh.create_table(fileh.root, 'recarray', Rec) # Append new rows s0, s1, s2, s3 = ['dbe']*3, ['ded']*3, ['db1']*3, ['de1']*3 f0, f1, f2, f3 = [[1.2]*2]*3, [[1.3]*2]*3, [[1.4]*2]*3, [[1.5]*2]*3 r = records.array([[[456, 457], s0, f0], [[2, 3], s1, f1]], formats="(2,)i4,(3,)a3,(3,2)f8") table.append(r) table.append([[[457, 458], s2, f2], [[5, 6], s3, f3]]) # Modify just one existing column columns = records.fromarrays( np.array([[[2, 3], [3, 4], [4, 5]]]), formats="i4") table.modify_column(start=1, column=columns, colname="col1") # Create the modified recarray r1 = records.array([[[456, 457], s0, f0], [[2, 3], s1, f1], [[3, 4], s2, f2], [[4, 5], s3, f3]], formats="(2,)i4,(3,)a3,(3,2)f8", names="col1,col2,col3") # Read the modified table r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) fileh.close() os.remove(file) class DefaultValues(unittest.TestCase): def test00(self): "Checking saving a Table MD with default values" file = tempfile.mktemp(".h5") # file = "/tmp/test.h5" fileh = open_file(file, "w") # Create a table table = fileh.create_table(fileh.root, 'table', Record) # Take a number of records a bit large # nrows = int(table.nrowsinbuf * 1.1) nrows = 5 # for test # Fill the table with nrows records for i in xrange(nrows): if i == 3 or i == 4: table.row['var2'] = ((2, 2), (2, 2)) # *-* # This injects the row values. table.row.append() # We need to flush the buffers in table in order to get an # accurate number of records on it. table.flush() # Create a recarray with the same default values buffer = [[ ["\x00"]*2, # just "" does not initialize the buffer properly [["abcd", "efgh"]]*2, (1, 1), ((1, 1), (1, 1)), 2, 3.1, 4.2, 5, "e"]] r = np.rec.array( buffer * nrows, formats='(2,)a4,(2,2)a4,(2,)i4,(2,2)i4,i2,f8,f4,u2,a1', names=['var0', 'var1', 'var1_', 'var2', 'var3', 'var4', 'var5', 'var6', 'var7']) # *-* # Assign the value exceptions r["var2"][3] = ((2, 2), (2, 2)) # *-* r["var2"][4] = ((2, 2), (2, 2)) # *-* # Read the table in another recarray r2 = table.read() # This generates too much output. Activate only when # self.nrowsinbuf is very small (<10) if common.verbose and 1: print("Table values:") print(r2) print("Record values:") print(r) # Both checks do work, however, tostring() seems more stringent. self.assertEqual(r.tostring(), r2.tostring()) # self.assertTrue(common.areArraysEqual(r,r2)) fileh.close() os.remove(file) class RecordT(IsDescription): var0 = IntCol(dflt=1, shape=()) # native int var1 = IntCol(dflt=[1], shape=(1,)) # 1-D int (one element) var2_s = IntCol(dflt=[1, 1], shape=2) # 1-D int (two elements) var2 = IntCol(dflt=[1, 1], shape=(2,)) # 1-D int (two elements) var3 = IntCol(dflt=[[0, 0], [1, 1]], shape=(2, 2)) # 2-D int class ShapeTestCase(unittest.TestCase): def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") self.populateFile() def populateFile(self): table = self.fileh.create_table(self.fileh.root, 'table', RecordT) row = table.row # Fill the table with some rows with default values for i in xrange(1): row.append() # Flush the buffer for this table table.flush() def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test00(self): "Checking scalar shapes" if self.reopen: self.fileh.close() self.fileh = open_file(self.file) table = self.fileh.root.table if common.verbose: print("The values look like:", table.cols.var0[:]) print("They should look like:", [1]) # The real check self.assertEqual(table.cols.var0[:].tolist(), [1]) def test01(self): "Checking undimensional (one element) shapes" if self.reopen: self.fileh.close() self.fileh = open_file(self.file) table = self.fileh.root.table if common.verbose: print("The values look like:", table.cols.var1[:]) print("They should look like:", [[1]]) # The real check self.assertEqual(table.cols.var1[:].tolist(), [[1]]) def test02(self): "Checking undimensional (two elements) shapes" if self.reopen: self.fileh.close() self.fileh = open_file(self.file) table = self.fileh.root.table if common.verbose: print("The values look like:", table.cols.var2[:]) print("They should look like:", [[1, 1]]) # The real check self.assertEqual(table.cols.var2[:].tolist(), [[1, 1]]) self.assertEqual(table.cols.var2_s[:].tolist(), [[1, 1]]) def test03(self): "Checking bidimensional shapes" if self.reopen: self.fileh.close() self.fileh = open_file(self.file) table = self.fileh.root.table if common.verbose: print("The values look like:", table.cols.var3[:]) print("They should look like:", [[[0, 0], [1, 1]]]) # The real check self.assertEqual(table.cols.var3[:].tolist(), [[[0, 0], [1, 1]]]) class ShapeTestCase1(ShapeTestCase): reopen = 0 class ShapeTestCase2(ShapeTestCase): reopen = 1 class setItem(common.PyTablesTestCase): def setUp(self): self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: self.table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) self.table.nrowsinbuf = self.buffersize # set buffer value def tearDown(self): self.fileh.close() # del self.fileh, self.rootgroup os.remove(self.file) common.cleanup(self) def test01(self): "Checking modifying one table row with __setitem__" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify just one existing row table[2] = (456, 'db2', 1.2) # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [2, 'ded', 1.3], [456, 'db2', 1.2], [5, 'de1', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test01b(self): "Checking modifying one table row with __setitem__ (long index)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify just one existing row table[2] = (456, 'db2', 1.2) # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [2, 'ded', 1.3], [456, 'db2', 1.2], [5, 'de1', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test02(self): "Modifying one row, with a step (__setitem__)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify two existing rows rows = records.array([[457, 'db1', 1.2]], formats=formats) table[1:3:2] = rows # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [457, 'db1', 1.2], [457, 'db1', 1.2], [5, 'de1', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test03(self): "Checking modifying several rows at once (__setitem__)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify two existing rows rows = records.array([[457, 'db1', 1.2], [5, 'de1', 1.3]], formats=formats) # table.modify_rows(start=1, rows=rows) table[1:3] = rows # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [457, 'db1', 1.2], [5, 'de1', 1.3], [5, 'de1', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test04(self): "Modifying several rows at once, with a step (__setitem__)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify two existing rows rows = records.array([[457, 'db1', 1.2], [6, 'de2', 1.3]], formats=formats) # table[1:4:2] = rows table[1::2] = rows # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [457, 'db1', 1.2], [457, 'db1', 1.2], [6, 'de2', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test05(self): "Checking modifying one column (single element, __setitem__)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify just one existing column table.cols.col1[1] = -1 # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [-1, 'ded', 1.3], [457, 'db1', 1.2], [5, 'de1', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test06a(self): "Checking modifying one column (several elements, __setitem__)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify just one existing column table.cols.col1[1:4] = [(2, 2), (3, 3), (4, 4)] # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [2, 'ded', 1.3], [3, 'db1', 1.2], [4, 'de1', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test06b(self): "Checking modifying one column (iterator, __setitem__)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify just one existing column try: for row in table.iterrows(): row['col1'] = row.nrow + 1 row.append() table.flush() except NotImplementedError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next NotImplementedError was catched!") print(value) else: self.fail("expected a NotImplementedError") def test07(self): "Modifying one column (several elements, __setitem__, step)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 1, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify just one existing column table.cols.col1[1:4:2] = [(2, 2), (3, 3)] # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [2, 'ded', 1.3], [457, 'db1', 1.2], [3, 'de1', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test08(self): "Modifying one column (one element, __setitem__, step)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify just one existing column table.cols.col1[1:4:3] = [(2, 2)] # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [2, 'ded', 1.3], [457, 'db1', 1.2], [5, 'de1', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test09(self): "Modifying beyond the table extend (__setitem__, step)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Try to modify beyond the extend # This will silently exclude the non-fitting rows rows = records.array([[457, 'db1', 1.2], [6, 'de2', 1.3]], formats=formats) table[1::2] = rows # How it should look like r1 = records.array([[456, 'dbe', 1.2], [457, 'db1', 1.2], [457, 'db1', 1.2], [6, 'de2', 1.3]], formats=formats) # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) class setItem1(setItem): reopen = 0 buffersize = 1 class setItem2(setItem): reopen = 1 buffersize = 2 class setItem3(setItem): reopen = 0 buffersize = 1000 class setItem4(setItem): reopen = 1 buffersize = 1000 class updateRow(common.PyTablesTestCase): def setUp(self): self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create a new table: self.table = self.fileh.create_table(self.fileh.root, 'recarray', Rec) self.table.nrowsinbuf = self.buffersize # set buffer value def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def test01(self): "Checking modifying one table row with Row.update" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify just one existing row for row in table.iterrows(2, 3): (row['col1'], row['col2'], row['col3']) = [456, 'db2', 1.2] row.update() # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [2, 'ded', 1.3], [456, 'db2', 1.2], [5, 'de1', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test02(self): "Modifying one row, with a step (Row.update)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify two existing rows for row in table.iterrows(1, 3, 2): if row.nrow == 1: (row['col1'], row['col2'], row['col3']) = [457, 'db1', 1.2] elif row.nrow == 3: (row['col1'], row['col2'], row['col3']) = [6, 'de2', 1.3] row.update() # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [457, 'db1', 1.2], [457, 'db1', 1.2], [5, 'de1', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test03(self): "Checking modifying several rows at once (Row.update)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify two existing rows for row in table.iterrows(1, 3): if row.nrow == 1: (row['col1'], row['col2'], row['col3']) = [457, 'db1', 1.2] elif row.nrow == 2: (row['col1'], row['col2'], row['col3']) = [5, 'de1', 1.3] row.update() # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [457, 'db1', 1.2], [5, 'de1', 1.3], [5, 'de1', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test04(self): "Modifying several rows at once, with a step (Row.update)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify two existing rows for row in table.iterrows(1, stop=4, step=2): if row.nrow == 1: (row['col1'], row['col2'], row['col3']) = [457, 'db1', 1.2] elif row.nrow == 3: (row['col1'], row['col2'], row['col3']) = [6, 'de2', 1.3] row.update() # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [457, 'db1', 1.2], [457, 'db1', 1.2], [6, 'de2', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertTrue(table.nrows, 4) def test05(self): "Checking modifying one column (single element, Row.update)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify just one existing column for row in table.iterrows(1, 2): row['col1'] = -1 row.update() # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [-1, 'ded', 1.3], [457, 'db1', 1.2], [5, 'de1', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test06(self): "Checking modifying one column (several elements, Row.update)" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 2, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify just one existing column for row in table.iterrows(1, 4): row['col1'] = row.nrow + 1 row.update() # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [2, 'ded', 1.3], [3, 'db1', 1.2], [4, 'de1', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test07(self): "Modifying values from a selection" table = self.table formats = table.description._v_nested_formats # append new rows r = records.array([[456, 'dbe', 1.2], [ 1, 'ded', 1.3]], formats=formats) table.append(r) table.append([[457, 'db1', 1.2], [5, 'de1', 1.3]]) # Modify just rows with col1 < 456 for row in table.iterrows(): if row['col1'][0] < 456: row['col1'] = 2 row['col2'] = 'ada' row.update() # Create the modified recarray r1 = records.array([[456, 'dbe', 1.2], [2, 'ada', 1.3], [457, 'db1', 1.2], [2, 'ada', 1.3]], formats=formats, names="col1,col2,col3") # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, 4) def test08(self): "Modifying a large table (Row.update)" table = self.table formats = table.description._v_nested_formats nrows = 100 # append new rows row = table.row for i in xrange(nrows): row['col1'] = i-1 row['col2'] = 'a'+str(i-1) row['col3'] = -1.0 row.append() table.flush() # Modify all the rows for row in table.iterrows(): row['col1'] = row.nrow row['col2'] = 'b'+str(row.nrow) row['col3'] = 0.0 row.update() # Create the modified recarray r1 = records.array(None, shape=nrows, formats=formats, names="col1,col2,col3") for i in xrange(nrows): r1['col1'][i] = i r1['col2'][i] = 'b'+str(i) r1['col3'][i] = 0.0 # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, nrows) def test08b(self): "Setting values on a large table without calling Row.update" table = self.table formats = table.description._v_nested_formats nrows = 100 # append new rows row = table.row for i in xrange(nrows): row['col1'] = i-1 row['col2'] = 'a'+str(i-1) row['col3'] = -1.0 row.append() table.flush() # Modify all the rows (actually don't) for row in table.iterrows(): row['col1'] = row.nrow row['col2'] = 'b'+str(row.nrow) row['col3'] = 0.0 # row.update() # Create the modified recarray r1 = records.array(None, shape=nrows, formats=formats, names="col1,col2,col3") for i in xrange(nrows): r1['col1'][i] = i-1 r1['col2'][i] = 'a'+str(i-1) r1['col3'][i] = -1.0 # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, nrows) def test09(self): "Modifying selected values on a large table" table = self.table formats = table.description._v_nested_formats nrows = 100 # append new rows row = table.row for i in xrange(nrows): row['col1'] = i-1 row['col2'] = 'a'+str(i-1) row['col3'] = -1.0 row.append() table.flush() # Modify selected rows for row in table.iterrows(): if row['col1'][0] > nrows-3: row['col1'] = row.nrow row['col2'] = 'b'+str(row.nrow) row['col3'] = 0.0 row.update() # Create the modified recarray r1 = records.array(None, shape=nrows, formats=formats, names="col1,col2,col3") for i in xrange(nrows): r1['col1'][i] = i-1 r1['col2'][i] = 'a'+str(i-1) r1['col3'][i] = -1.0 # modify just the last line r1['col1'][i] = i r1['col2'][i] = 'b'+str(i) r1['col3'][i] = 0.0 # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, nrows) def test09b(self): "Modifying selected values on a large table (alternate values)" table = self.table formats = table.description._v_nested_formats nrows = 100 # append new rows row = table.row for i in xrange(nrows): row['col1'] = i-1 row['col2'] = 'a'+str(i-1) row['col3'] = -1.0 row.append() table.flush() # Modify selected rows for row in table.iterrows(step=10): row['col1'] = row.nrow row['col2'] = 'b'+str(row.nrow) row['col3'] = 0.0 row.update() # Create the modified recarray r1 = records.array(None, shape=nrows, formats=formats, names="col1,col2,col3") for i in xrange(nrows): if i % 10 > 0: r1['col1'][i] = i-1 r1['col2'][i] = 'a'+str(i-1) r1['col3'][i] = -1.0 else: r1['col1'][i] = i r1['col2'][i] = 'b'+str(i) r1['col3'][i] = 0.0 # Read the modified table if self.reopen: self.fileh.close() self.fileh = open_file(self.file, "r") table = self.fileh.root.recarray table.nrowsinbuf = self.buffersize # set buffer value r2 = table.read() if common.verbose: print("Original table-->", repr(r2)) print("Should look like-->", repr(r1)) self.assertEqual(r1.tostring(), r2.tostring()) self.assertEqual(table.nrows, nrows) class updateRow1(updateRow): reopen = 0 buffersize = 1 class updateRow2(updateRow): reopen = 1 buffersize = 2 class updateRow3(updateRow): reopen = 0 buffersize = 1000 class updateRow4(updateRow): reopen = 1 buffersize = 1000 #---------------------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() niter = 1 # common.heavy = 1 # Uncomment this only for testing purposes for n in range(niter): theSuite.addTest(unittest.makeSuite(BasicWriteTestCase)) theSuite.addTest(unittest.makeSuite(DictWriteTestCase)) theSuite.addTest(unittest.makeSuite(RecordDTWriteTestCase)) theSuite.addTest(unittest.makeSuite(NumPyDTWriteTestCase)) theSuite.addTest(unittest.makeSuite(RecArrayOneWriteTestCase)) theSuite.addTest(unittest.makeSuite(RecArrayTwoWriteTestCase)) theSuite.addTest(unittest.makeSuite(RecArrayThreeWriteTestCase)) theSuite.addTest(unittest.makeSuite(CompressZLIBTablesTestCase)) theSuite.addTest(unittest.makeSuite(CompressTwoTablesTestCase)) theSuite.addTest(unittest.makeSuite(IterRangeTestCase)) theSuite.addTest(unittest.makeSuite(RecArrayRangeTestCase)) theSuite.addTest(unittest.makeSuite(getColRangeTestCase)) theSuite.addTest(unittest.makeSuite(DefaultValues)) theSuite.addTest(unittest.makeSuite(RecArrayIO)) theSuite.addTest(unittest.makeSuite(ShapeTestCase1)) theSuite.addTest(unittest.makeSuite(ShapeTestCase2)) theSuite.addTest(unittest.makeSuite(setItem1)) theSuite.addTest(unittest.makeSuite(setItem2)) theSuite.addTest(unittest.makeSuite(setItem3)) theSuite.addTest(unittest.makeSuite(setItem4)) theSuite.addTest(unittest.makeSuite(updateRow1)) theSuite.addTest(unittest.makeSuite(updateRow2)) theSuite.addTest(unittest.makeSuite(updateRow3)) theSuite.addTest(unittest.makeSuite(updateRow4)) theSuite.addTest(unittest.makeSuite(CompressBloscTablesTestCase)) theSuite.addTest(unittest.makeSuite(CompressLZOTablesTestCase)) if common.heavy: theSuite.addTest(unittest.makeSuite(CompressBzip2TablesTestCase)) theSuite.addTest(unittest.makeSuite(BigTablesTestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/test_timetype.py000066400000000000000000000513321231437614300214000ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: December 15, 2004 # Author: Ivan Vilata i Balaguer - reverse:net.selidor@ivan # # $Id$ # ######################################################################## """Unit test for the Time datatypes.""" from __future__ import print_function import unittest import tempfile import os import numpy import tables from tables.tests import common from tables.tests.common import allequal # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup class LeafCreationTestCase(common.PyTablesTestCase): "Tests creating Tables, VLArrays an EArrays with Time data." def setUp(self): """setUp() -> None This method sets the following instance attributes: * 'h5fname', the name of the temporary HDF5 file * 'h5file', the writable, empty, temporary HDF5 file """ self.h5fname = tempfile.mktemp(suffix='.h5') self.h5file = tables.open_file( self.h5fname, 'w', title="Test for creating a time leaves") def tearDown(self): """tearDown() -> None Closes 'h5file'; removes 'h5fname'. """ self.h5file.close() self.h5file = None os.remove(self.h5fname) def test00_UnidimLeaves(self): "Creating new nodes with unidimensional time elements." # Table creation. class MyTimeRow(tables.IsDescription): intcol = tables.IntCol() t32col = tables.Time32Col() t64col = tables.Time64Col() self.h5file.create_table('/', 'table', MyTimeRow) # VLArray creation. self.h5file.create_vlarray('/', 'vlarray4', tables.Time32Atom()) self.h5file.create_vlarray('/', 'vlarray8', tables.Time64Atom()) # EArray creation. self.h5file.create_earray('/', 'earray4', tables.Time32Atom(), shape=(0,)) self.h5file.create_earray('/', 'earray8', tables.Time64Atom(), shape=(0,)) def test01_MultidimLeaves(self): "Creating new nodes with multidimensional time elements." # Table creation. class MyTimeRow(tables.IsDescription): intcol = tables.IntCol(shape=(2, 1)) t32col = tables.Time32Col(shape=(2, 1)) t64col = tables.Time64Col(shape=(2, 1)) self.h5file.create_table('/', 'table', MyTimeRow) # VLArray creation. self.h5file.create_vlarray( '/', 'vlarray4', tables.Time32Atom(shape=(2, 1))) self.h5file.create_vlarray( '/', 'vlarray8', tables.Time64Atom(shape=(2, 1))) # EArray creation. self.h5file.create_earray( '/', 'earray4', tables.Time32Atom(), shape=(0, 2, 1)) self.h5file.create_earray( '/', 'earray8', tables.Time64Atom(), shape=(0, 2, 1)) class OpenTestCase(common.PyTablesTestCase): "Tests opening a file with Time nodes." # The description used in the test Table. class MyTimeRow(tables.IsDescription): t32col = tables.Time32Col(shape=(2, 1)) t64col = tables.Time64Col(shape=(2, 1)) # The atoms used in the test VLArrays. myTime32Atom = tables.Time32Atom(shape=(2, 1)) myTime64Atom = tables.Time64Atom(shape=(2, 1)) def setUp(self): """setUp() -> None This method sets the following instance attributes: * 'h5fname', the name of the temporary HDF5 file with '/table', '/vlarray4' and '/vlarray8' nodes. """ self.h5fname = tempfile.mktemp(suffix='.h5') h5file = tables.open_file( self.h5fname, 'w', title="Test for creating time leaves") # Create test Table. h5file.create_table('/', 'table', self.MyTimeRow) # Create test VLArrays. h5file.create_vlarray('/', 'vlarray4', self.myTime32Atom) h5file.create_vlarray('/', 'vlarray8', self.myTime64Atom) h5file.close() def tearDown(self): """tearDown() -> None Removes 'h5fname'. """ os.remove(self.h5fname) def test00_OpenFile(self): "Opening a file with Time nodes." h5file = tables.open_file(self.h5fname) # Test the Table node. tbl = h5file.root.table self.assertEqual( tbl.coldtypes['t32col'], self.MyTimeRow.columns['t32col'].dtype, "Column dtypes do not match.") self.assertEqual( tbl.coldtypes['t64col'], self.MyTimeRow.columns['t64col'].dtype, "Column dtypes do not match.") # Test the VLArray nodes. vla4 = h5file.root.vlarray4 self.assertEqual( vla4.atom.dtype, self.myTime32Atom.dtype, "Atom types do not match.") self.assertEqual( vla4.atom.shape, self.myTime32Atom.shape, "Atom shapes do not match.") vla8 = h5file.root.vlarray8 self.assertEqual( vla8.atom.dtype, self.myTime64Atom.dtype, "Atom types do not match.") self.assertEqual( vla8.atom.shape, self.myTime64Atom.shape, "Atom shapes do not match.") h5file.close() def test01_OpenFileStype(self): "Opening a file with Time nodes, comparing Atom.stype." h5file = tables.open_file(self.h5fname) # Test the Table node. tbl = h5file.root.table self.assertEqual( tbl.coltypes['t32col'], self.MyTimeRow.columns['t32col'].type, "Column types do not match.") self.assertEqual( tbl.coltypes['t64col'], self.MyTimeRow.columns['t64col'].type, "Column types do not match.") # Test the VLArray nodes. vla4 = h5file.root.vlarray4 self.assertEqual( vla4.atom.type, self.myTime32Atom.type, "Atom types do not match.") vla8 = h5file.root.vlarray8 self.assertEqual( vla8.atom.type, self.myTime64Atom.type, "Atom types do not match.") h5file.close() class CompareTestCase(common.PyTablesTestCase): "Tests whether stored and retrieved time data is kept the same." # The description used in the test Table. class MyTimeRow(tables.IsDescription): t32col = tables.Time32Col(pos=0) t64col = tables.Time64Col(shape=(2,), pos = 1) # The atoms used in the test VLArrays. myTime32Atom = tables.Time32Atom(shape=(2,)) myTime64Atom = tables.Time64Atom(shape=(2,)) def setUp(self): """setUp() -> None This method sets the following instance attributes: * 'h5fname', the name of the temporary HDF5 file """ self.h5fname = tempfile.mktemp(suffix='.h5') def tearDown(self): """tearDown() -> None Removes 'h5fname'. """ os.remove(self.h5fname) def test00_Compare32VLArray(self): "Comparing written 32-bit time data with read data in a VLArray." wtime = numpy.array((1234567890,) * 2, numpy.int32) # Create test VLArray with data. h5file = tables.open_file( self.h5fname, 'w', title="Test for comparing Time32 VL arrays") vla = h5file.create_vlarray('/', 'test', self.myTime32Atom) vla.append(wtime) h5file.close() # Check the written data. h5file = tables.open_file(self.h5fname) rtime = h5file.root.test.read()[0][0] h5file.close() self.assertTrue(allequal(rtime, wtime), "Stored and retrieved values do not match.") def test01_Compare64VLArray(self): "Comparing written 64-bit time data with read data in a VLArray." wtime = numpy.array((1234567890.123456,) * 2, numpy.float64) # Create test VLArray with data. h5file = tables.open_file( self.h5fname, 'w', title="Test for comparing Time64 VL arrays") vla = h5file.create_vlarray('/', 'test', self.myTime64Atom) vla.append(wtime) h5file.close() # Check the written data. h5file = tables.open_file(self.h5fname) rtime = h5file.root.test.read()[0][0] h5file.close() self.assertTrue(allequal(rtime, wtime), "Stored and retrieved values do not match.") def test01b_Compare64VLArray(self): "Comparing several written and read 64-bit time values in a VLArray." # Create test VLArray with data. h5file = tables.open_file( self.h5fname, 'w', title="Test for comparing Time64 VL arrays") vla = h5file.create_vlarray('/', 'test', self.myTime64Atom) # Size of the test. nrows = vla.nrowsinbuf + 34 # Add some more rows than buffer. # Only for home checks; the value above should check better # the I/O with multiple buffers. # nrows = 10 for i in xrange(nrows): j = i * 2 vla.append((j + 0.012, j + 1 + 0.012)) h5file.close() # Check the written data. h5file = tables.open_file(self.h5fname) arr = h5file.root.test.read() h5file.close() arr = numpy.array(arr) orig_val = numpy.arange(0, nrows * 2, dtype=numpy.int32) + 0.012 orig_val.shape = (nrows, 1, 2) if common.verbose: print("Original values:", orig_val) print("Retrieved values:", arr) self.assertTrue(allequal(arr, orig_val), "Stored and retrieved values do not match.") def test02_CompareTable(self): "Comparing written time data with read data in a Table." wtime = 1234567890.123456 # Create test Table with data. h5file = tables.open_file( self.h5fname, 'w', title="Test for comparing Time tables") tbl = h5file.create_table('/', 'test', self.MyTimeRow) row = tbl.row row['t32col'] = int(wtime) row['t64col'] = (wtime, wtime) row.append() h5file.close() # Check the written data. h5file = tables.open_file(self.h5fname) recarr = h5file.root.test.read(0) h5file.close() self.assertEqual(recarr['t32col'][0], int(wtime), "Stored and retrieved values do not match.") comp = (recarr['t64col'][0] == numpy.array((wtime, wtime))) self.assertTrue(numpy.alltrue(comp), "Stored and retrieved values do not match.") def test02b_CompareTable(self): "Comparing several written and read time values in a Table." # Create test Table with data. h5file = tables.open_file( self.h5fname, 'w', title="Test for comparing Time tables") tbl = h5file.create_table('/', 'test', self.MyTimeRow) # Size of the test. nrows = tbl.nrowsinbuf + 34 # Add some more rows than buffer. # Only for home checks; the value above should check better # the I/O with multiple buffers. # nrows = 10 row = tbl.row for i in xrange(nrows): row['t32col'] = i j = i * 2 row['t64col'] = (j + 0.012, j+1+0.012) row.append() h5file.close() # Check the written data. h5file = tables.open_file(self.h5fname) recarr = h5file.root.test.read() h5file.close() # Time32 column. orig_val = numpy.arange(nrows, dtype=numpy.int32) if common.verbose: print("Original values:", orig_val) print("Retrieved values:", recarr['t32col'][:]) self.assertTrue(numpy.alltrue(recarr['t32col'][:] == orig_val), "Stored and retrieved values do not match.") # Time64 column. orig_val = numpy.arange(0, nrows * 2, dtype=numpy.int32) + 0.012 orig_val.shape = (nrows, 2) if common.verbose: print("Original values:", orig_val) print("Retrieved values:", recarr['t64col'][:]) self.assertTrue(allequal(recarr['t64col'][:], orig_val, numpy.float64), "Stored and retrieved values do not match.") def test03_Compare64EArray(self): "Comparing written 64-bit time data with read data in an EArray." wtime = 1234567890.123456 # Create test EArray with data. h5file = tables.open_file( self.h5fname, 'w', title="Test for comparing Time64 EArrays") ea = h5file.create_earray( '/', 'test', tables.Time64Atom(), shape=(0,)) ea.append((wtime,)) h5file.close() # Check the written data. h5file = tables.open_file(self.h5fname) rtime = h5file.root.test[0] h5file.close() self.assertTrue(allequal(rtime, wtime), "Stored and retrieved values do not match.") def test03b_Compare64EArray(self): "Comparing several written and read 64-bit time values in an EArray." # Create test EArray with data. h5file = tables.open_file( self.h5fname, 'w', title="Test for comparing Time64 E arrays") ea = h5file.create_earray( '/', 'test', tables.Time64Atom(), shape=(0, 2)) # Size of the test. nrows = ea.nrowsinbuf + 34 # Add some more rows than buffer. # Only for home checks; the value above should check better # the I/O with multiple buffers. # nrows = 10 for i in xrange(nrows): j = i * 2 ea.append(((j + 0.012, j + 1 + 0.012),)) h5file.close() # Check the written data. h5file = tables.open_file(self.h5fname) arr = h5file.root.test.read() h5file.close() orig_val = numpy.arange(0, nrows * 2, dtype=numpy.int32) + 0.012 orig_val.shape = (nrows, 2) if common.verbose: print("Original values:", orig_val) print("Retrieved values:", arr) self.assertTrue(allequal(arr, orig_val), "Stored and retrieved values do not match.") class UnalignedTestCase(common.PyTablesTestCase): "Tests writing and reading unaligned time values in a table." # The description used in the test Table. # Time fields are unaligned because of 'i8col'. class MyTimeRow(tables.IsDescription): i8col = tables.Int8Col(pos=0) t32col = tables.Time32Col(pos=1) t64col = tables.Time64Col(shape=(2,), pos = 2) def setUp(self): """setUp() -> None This method sets the following instance attributes: * 'h5fname', the name of the temporary HDF5 file """ self.h5fname = tempfile.mktemp(suffix='.h5') def tearDown(self): """tearDown() -> None Removes 'h5fname'. """ os.remove(self.h5fname) def test00_CompareTable(self): "Comparing written unaligned time data with read data in a Table." # Create test Table with data. h5file = tables.open_file( self.h5fname, 'w', title="Test for comparing Time tables") tbl = h5file.create_table('/', 'test', self.MyTimeRow) # Size of the test. nrows = tbl.nrowsinbuf + 34 # Add some more rows than buffer. # Only for home checks; the value above should check better # the I/O with multiple buffers. # nrows = 10 row = tbl.row for i in xrange(nrows): row['i8col'] = i row['t32col'] = i j = i * 2 row['t64col'] = (j + 0.012, j+1+0.012) row.append() h5file.close() # Check the written data. h5file = tables.open_file(self.h5fname) recarr = h5file.root.test.read() h5file.close() # Int8 column. orig_val = numpy.arange(nrows, dtype=numpy.int8) if common.verbose: print("Original values:", orig_val) print("Retrieved values:", recarr['i8col'][:]) self.assertTrue(numpy.alltrue(recarr['i8col'][:] == orig_val), "Stored and retrieved values do not match.") # Time32 column. orig_val = numpy.arange(nrows, dtype=numpy.int32) if common.verbose: print("Original values:", orig_val) print("Retrieved values:", recarr['t32col'][:]) self.assertTrue(numpy.alltrue(recarr['t32col'][:] == orig_val), "Stored and retrieved values do not match.") # Time64 column. orig_val = numpy.arange(0, nrows * 2, dtype=numpy.int32) + 0.012 orig_val.shape = (nrows, 2) if common.verbose: print("Original values:", orig_val) print("Retrieved values:", recarr['t64col'][:]) self.assertTrue(allequal(recarr['t64col'][:], orig_val, numpy.float64), "Stored and retrieved values do not match.") class BigEndianTestCase(common.PyTablesTestCase): "Tests for reading big-endian time values in arrays and nested tables." def setUp(self): filename = self._testFilename('times-nested-be.h5') self.h5f = tables.open_file(filename, 'r') def tearDown(self): self.h5f.close() def test00a_Read32Array(self): "Checking Time32 type in arrays." # Check the written data. earr = self.h5f.root.earr32[:] # Generate the expected Time32 array. start = 1178896298 nrows = 10 orig_val = numpy.arange(start, start + nrows, dtype=numpy.int32) if common.verbose: print("Retrieved values:", earr) print("Should look like:", orig_val) self.assertTrue(numpy.alltrue(earr == orig_val), "Retrieved values do not match the expected values.") def test00b_Read64Array(self): "Checking Time64 type in arrays." # Check the written data. earr = self.h5f.root.earr64[:] # Generate the expected Time64 array. start = 1178896298.832258 nrows = 10 orig_val = numpy.arange(start, start + nrows, dtype=numpy.float64) if common.verbose: print("Retrieved values:", earr) print("Should look like:", orig_val) self.assertTrue(numpy.allclose(earr, orig_val, rtol=1.e-15), "Retrieved values do not match the expected values.") def test01a_ReadPlainColumn(self): "Checking Time32 type in plain columns." # Check the written data. tbl = self.h5f.root.tbl t32 = tbl.cols.t32[:] # Generate the expected Time32 array. start = 1178896298 nrows = 10 orig_val = numpy.arange(start, start + nrows, dtype=numpy.int32) if common.verbose: print("Retrieved values:", t32) print("Should look like:", orig_val) self.assertTrue(numpy.alltrue(t32 == orig_val), "Retrieved values do not match the expected values.") def test01b_ReadNestedColumn(self): "Checking Time64 type in nested columns." # Check the written data. tbl = self.h5f.root.tbl t64 = tbl.cols.nested.t64[:] # Generate the expected Time64 array. start = 1178896298.832258 nrows = 10 orig_val = numpy.arange(start, start + nrows, dtype=numpy.float64) if common.verbose: print("Retrieved values:", t64) print("Should look like:", orig_val) self.assertTrue(numpy.allclose(t64, orig_val, rtol=1.e-15), "Retrieved values do not match the expected values.") def test02_ReadNestedColumnTwice(self): "Checking Time64 type in nested columns (read twice)." # Check the written data. tbl = self.h5f.root.tbl dummy = tbl.cols.nested.t64[:] self.assertTrue(dummy is not None) t64 = tbl.cols.nested.t64[:] # Generate the expected Time64 array. start = 1178896298.832258 nrows = 10 orig_val = numpy.arange(start, start + nrows, dtype=numpy.float64) if common.verbose: print("Retrieved values:", t64) print("Should look like:", orig_val) self.assertTrue(numpy.allclose(t64, orig_val, rtol=1.e-15), "Retrieved values do not match the expected values.") #---------------------------------------------------------------------- def suite(): """suite() -> test suite Returns a test suite consisting of all the test cases in the module. """ theSuite = unittest.TestSuite() theSuite.addTest(unittest.makeSuite(LeafCreationTestCase)) theSuite.addTest(unittest.makeSuite(OpenTestCase)) theSuite.addTest(unittest.makeSuite(CompareTestCase)) theSuite.addTest(unittest.makeSuite(UnalignedTestCase)) theSuite.addTest(unittest.makeSuite(BigEndianTestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## End: PyTables-v.3.1.1/tables/tests/test_tree.py000066400000000000000000001113771231437614300205050ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import sys import warnings import unittest import os import tempfile from tables import * # Next imports are only necessary for this test suite from tables import Group, Leaf, Table, Array from tables.tests import common # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup # Test Record class class Record(IsDescription): var1 = StringCol(itemsize=4) # 4-character String var2 = IntCol() # integer var3 = Int16Col() # short integer var4 = FloatCol() # double (double-precision) var5 = Float32Col() # float (single-precision) class TreeTestCase(unittest.TestCase): mode = "w" title = "This is the table title" expectedrows = 10 appendrows = 5 def setUp(self): # Create a temporary file self.file = tempfile.mktemp(".h5") # Create an instance of HDF5 Table self.h5file = open_file(self.file, self.mode, self.title) self.populateFile() self.h5file.close() def populateFile(self): group = self.h5file.root maxshort = 1 << 15 # maxint = 2147483647 # (2 ** 31 - 1) for j in range(3): # Create a table table = self.h5file.create_table(group, 'table'+str(j), Record, title=self.title, filters=None, expectedrows=self.expectedrows) # Get the record object associated with the new table d = table.row # Fill the table for i in xrange(self.expectedrows): d['var1'] = '%04d' % (self.expectedrows - i) d['var2'] = i d['var3'] = i % maxshort d['var4'] = float(i) d['var5'] = float(i) d.append() # This injects the Record values # Flush the buffer for this table table.flush() # Create a couple of arrays in each group var1List = [x['var1'] for x in table.iterrows()] var4List = [x['var4'] for x in table.iterrows()] self.h5file.create_array(group, 'var1', var1List, "1") self.h5file.create_array(group, 'var4', var4List, "4") # Create a new group (descendant of group) group2 = self.h5file.create_group(group, 'group'+str(j)) # Iterate over this new group (group2) group = group2 def tearDown(self): # Close the file if self.h5file.isopen: self.h5file.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test00_getNode(self): "Checking the File.get_node() with string node names" if common.verbose: print('\n', '-=' * 30) print("Running %s.test00_getNode..." % self.__class__.__name__) self.h5file = open_file(self.file, "r") nodelist = ['/', '/table0', '/group0/var1', '/group0/group1/var4'] nodenames = [] for node in nodelist: object = self.h5file.get_node(node) nodenames.append(object._v_pathname) self.assertEqual(nodenames, nodelist) if common.verbose: print("get_node(pathname) test passed") nodegroups = [ '/', '/group0', '/group0/group1', '/group0/group1/group2'] nodenames = ['var1', 'var4'] nodepaths = [] for group in nodegroups: for name in nodenames: try: object = self.h5file.get_node(group, name) except LookupError: pass else: nodepaths.append(object._v_pathname) self.assertEqual(nodepaths, ['/var1', '/var4', '/group0/var1', '/group0/var4', '/group0/group1/var1', '/group0/group1/var4']) if common.verbose: print("get_node(groupname, name) test passed") nodelist = ['/', '/group0', '/group0/group1', '/group0/group1/group2', '/table0'] nodenames = [] groupobjects = [] # warnings.filterwarnings("error", category=UserWarning) for node in nodelist: try: object = self.h5file.get_node(node, classname='Group') except LookupError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next LookupError was catched!") print(value) else: nodenames.append(object._v_pathname) groupobjects.append(object) self.assertEqual(nodenames, ['/', '/group0', '/group0/group1', '/group0/group1/group2']) if common.verbose: print("get_node(groupname, classname='Group') test passed") # Reset the warning # warnings.filterwarnings("default", category=UserWarning) nodenames = ['var1', 'var4'] nodearrays = [] for group in groupobjects: for name in nodenames: try: object = self.h5file.get_node(group, name, 'Array') except: pass else: nodearrays.append(object._v_pathname) self.assertEqual(nodearrays, ['/var1', '/var4', '/group0/var1', '/group0/var4', '/group0/group1/var1', '/group0/group1/var4']) if common.verbose: print("get_node(groupobject, name, classname='Array') test passed") def test01_getNodeClass(self): "Checking the File.get_node() with instances" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_getNodeClass..." % self.__class__.__name__) self.h5file = open_file(self.file, "r") # This tree ways of get_node usage should return a table instance table = self.h5file.get_node("/group0/table1") self.assertTrue(isinstance(table, Table)) table = self.h5file.get_node("/group0", "table1") self.assertTrue(isinstance(table, Table)) table = self.h5file.get_node(self.h5file.root.group0, "table1") self.assertTrue(isinstance(table, Table)) # This should return an array instance arr = self.h5file.get_node("/group0/var1") self.assertTrue(isinstance(arr, Array)) self.assertTrue(isinstance(arr, Leaf)) # And this a Group group = self.h5file.get_node("/group0", "group1", "Group") self.assertTrue(isinstance(group, Group)) def test02_listNodes(self): "Checking the File.list_nodes() method" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_listNodes..." % self.__class__.__name__) # Made the warnings to raise an error # warnings.filterwarnings("error", category=UserWarning) self.h5file = open_file(self.file, "r") self.assertRaises(TypeError, self.h5file.list_nodes, '/', 'NoSuchClass') nodelist = ['/', '/group0', '/group0/table1', '/group0/group1/group2', '/var1'] nodenames = [] objects = [] for node in nodelist: try: objectlist = self.h5file.list_nodes(node) except: pass else: objects.extend(objectlist) for object in objectlist: nodenames.append(object._v_pathname) self.assertEqual(nodenames, ['/group0', '/table0', '/var1', '/var4', '/group0/group1', '/group0/table1', '/group0/var1', '/group0/var4']) if common.verbose: print("list_nodes(pathname) test passed") nodenames = [] for node in objects: try: objectlist = self.h5file.list_nodes(node) except: pass else: for object in objectlist: nodenames.append(object._v_pathname) self.assertEqual(nodenames, ['/group0/group1', '/group0/table1', '/group0/var1', '/group0/var4', '/group0/group1/group2', '/group0/group1/table2', '/group0/group1/var1', '/group0/group1/var4']) if common.verbose: print("list_nodes(groupobject) test passed") nodenames = [] for node in objects: try: objectlist = self.h5file.list_nodes(node, 'Leaf') except TypeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next TypeError was catched!") print(value) else: for object in objectlist: nodenames.append(object._v_pathname) self.assertEqual(nodenames, ['/group0/table1', '/group0/var1', '/group0/var4', '/group0/group1/table2', '/group0/group1/var1', '/group0/group1/var4']) if common.verbose: print("list_nodes(groupobject, classname = 'Leaf') test passed") nodenames = [] for node in objects: try: objectlist = self.h5file.list_nodes(node, 'Table') except TypeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next TypeError was catched!") print(value) else: for object in objectlist: nodenames.append(object._v_pathname) self.assertEqual(nodenames, ['/group0/table1', '/group0/group1/table2']) if common.verbose: print("list_nodes(groupobject, classname = 'Table') test passed") # Reset the warning # warnings.filterwarnings("default", category=UserWarning) def test02b_iterNodes(self): "Checking the File.iter_nodes() method" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02b_iterNodes..." % self.__class__.__name__) self.h5file = open_file(self.file, "r") self.assertRaises(TypeError, self.h5file.list_nodes, '/', 'NoSuchClass') nodelist = ['/', '/group0', '/group0/table1', '/group0/group1/group2', '/var1'] nodenames = [] objects = [] for node in nodelist: try: objectlist = [o for o in self.h5file.iter_nodes(node)] except: pass else: objects.extend(objectlist) for object in objectlist: nodenames.append(object._v_pathname) self.assertEqual(nodenames, ['/group0', '/table0', '/var1', '/var4', '/group0/group1', '/group0/table1', '/group0/var1', '/group0/var4']) if common.verbose: print("iter_nodes(pathname) test passed") nodenames = [] for node in objects: try: objectlist = [o for o in self.h5file.iter_nodes(node)] except: pass else: for object in objectlist: nodenames.append(object._v_pathname) self.assertEqual(nodenames, ['/group0/group1', '/group0/table1', '/group0/var1', '/group0/var4', '/group0/group1/group2', '/group0/group1/table2', '/group0/group1/var1', '/group0/group1/var4']) if common.verbose: print("iter_nodes(groupobject) test passed") nodenames = [] for node in objects: try: objectlist = [o for o in self.h5file.iter_nodes(node, 'Leaf')] except TypeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next TypeError was catched!") print(value) else: for object in objectlist: nodenames.append(object._v_pathname) self.assertEqual(nodenames, ['/group0/table1', '/group0/var1', '/group0/var4', '/group0/group1/table2', '/group0/group1/var1', '/group0/group1/var4']) if common.verbose: print("iter_nodes(groupobject, classname = 'Leaf') test passed") nodenames = [] for node in objects: try: objectlist = [o for o in self.h5file.iter_nodes(node, 'Table')] except TypeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next TypeError was catched!") print(value) else: for object in objectlist: nodenames.append(object._v_pathname) self.assertEqual(nodenames, ['/group0/table1', '/group0/group1/table2']) if common.verbose: print("iter_nodes(groupobject, classname = 'Table') test passed") # Reset the warning # warnings.filterwarnings("default", category=UserWarning) def test03_TraverseTree(self): "Checking the File.walk_groups() method" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_TraverseTree..." % self.__class__.__name__) self.h5file = open_file(self.file, "r") groups = [] tables = [] arrays = [] for group in self.h5file.walk_groups(): groups.append(group._v_pathname) for table in self.h5file.list_nodes(group, 'Table'): tables.append(table._v_pathname) for arr in self.h5file.list_nodes(group, 'Array'): arrays.append(arr._v_pathname) self.assertEqual(groups, ["/", "/group0", "/group0/group1", "/group0/group1/group2"]) self.assertEqual( tables, ["/table0", "/group0/table1", "/group0/group1/table2"]) self.assertEqual(arrays, ['/var1', '/var4', '/group0/var1', '/group0/var4', '/group0/group1/var1', '/group0/group1/var4']) if common.verbose: print("walk_groups() test passed") groups = [] tables = [] arrays = [] for group in self.h5file.walk_groups("/group0/group1"): groups.append(group._v_pathname) for table in self.h5file.list_nodes(group, 'Table'): tables.append(table._v_pathname) for arr in self.h5file.list_nodes(group, 'Array'): arrays.append(arr._v_pathname) self.assertEqual(groups, ["/group0/group1", "/group0/group1/group2"]) self.assertEqual(tables, ["/group0/group1/table2"]) self.assertEqual(arrays, [ '/group0/group1/var1', '/group0/group1/var4']) if common.verbose: print("walk_groups(pathname) test passed") def test04_walkNodes(self): "Checking File.walk_nodes" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_walkNodes..." % self.__class__.__name__) self.h5file = open_file(self.file, "r") self.assertRaises(TypeError, self.h5file.walk_nodes('/', 'NoSuchClass').next) groups = [] tables = [] tables2 = [] arrays = [] for group in self.h5file.walk_nodes(classname="Group"): groups.append(group._v_pathname) for table in group._f_iter_nodes(classname='Table'): tables.append(table._v_pathname) # Test the recursivity for table in self.h5file.root._f_walknodes('Table'): tables2.append(table._v_pathname) for arr in self.h5file.walk_nodes(classname='Array'): arrays.append(arr._v_pathname) self.assertEqual(groups, ["/", "/group0", "/group0/group1", "/group0/group1/group2"]) self.assertEqual(tables, ["/table0", "/group0/table1", "/group0/group1/table2"]) self.assertEqual(tables2, ["/table0", "/group0/table1", "/group0/group1/table2"]) self.assertEqual(arrays, ['/var1', '/var4', '/group0/var1', '/group0/var4', '/group0/group1/var1', '/group0/group1/var4']) if common.verbose: print("File.__iter__() and Group.__iter__ test passed") groups = [] tables = [] arrays = [] for group in self.h5file.walk_nodes("/group0/group1", classname="Group"): groups.append(group._v_pathname) for table in group._f_walknodes('Table'): tables.append(table._v_pathname) for arr in self.h5file.walk_nodes(group, 'Array'): arrays.append(arr._v_pathname) self.assertEqual(groups, ["/group0/group1", "/group0/group1/group2"]) self.assertEqual(tables, ["/group0/group1/table2"]) self.assertEqual(arrays, [ '/group0/group1/var1', '/group0/group1/var4']) if common.verbose: print("walk_nodes(pathname, classname) test passed") class DeepTreeTestCase(unittest.TestCase): """Checks for deep hierarchy levels in PyTables trees.""" def setUp(self): # Here we put a more conservative limit to deal with more platforms # With maxdepth = 64 this test would take less than 40 MB # of main memory to run, which is quite reasonable nowadays. # With maxdepth = 1024 this test will take around 300 MB. if common.heavy: self.maxdepth = 256 # Takes around 60 MB of memory! else: self.maxdepth = 64 # This should be safe for most machines if common.verbose: print("Maximum depth tested :", self.maxdepth) # Open a new empty HDF5 file self.file = tempfile.mktemp(".h5") fileh = open_file(self.file, mode="w") group = fileh.root if common.verbose: print("Depth writing progress: ", end=' ') # Iterate until maxdepth for depth in range(self.maxdepth): # Save it on the HDF5 file if common.verbose: print("%3d," % (depth), end=' ') # Create a couple of arrays here fileh.create_array(group, 'array', [1, 1], "depth: %d" % depth) fileh.create_array(group, 'array2', [1, 1], "depth: %d" % depth) # And also a group fileh.create_group(group, 'group2_' + str(depth)) # Finally, iterate over a new group group = fileh.create_group(group, 'group' + str(depth)) # Close the file fileh.close() def tearDown(self): os.remove(self.file) common.cleanup(self) def _check_tree(self, file): # Open the previous HDF5 file in read-only mode fileh = open_file(file, mode="r") group = fileh.root if common.verbose: print("\nDepth reading progress: ", end=' ') # Get the metadata on the previosly saved arrays for depth in range(self.maxdepth): if common.verbose: print("%3d," % (depth), end=' ') # Check the contents self.assertEqual(group.array[:], [1, 1]) self.assertTrue("array2" in group) self.assertTrue("group2_"+str(depth) in group) # Iterate over the next group group = fileh.get_node(group, 'group' + str(depth)) if common.verbose: print() # This flush the stdout buffer fileh.close() def test00_deepTree(self): "Creation of a large depth object tree." self._check_tree(self.file) def test01a_copyDeepTree(self): "Copy of a large depth object tree." fileh = open_file(self.file, mode="r") file2 = tempfile.mktemp(".h5") fileh2 = open_file(file2, mode="w") if common.verbose: print("\nCopying deep tree...") fileh.copy_node(fileh.root, fileh2.root, recursive=True) fileh.close() fileh2.close() self._check_tree(file2) os.remove(file2) def test01b_copyDeepTree(self): "Copy of a large depth object tree with small node cache." fileh = open_file(self.file, mode="r", node_cache_slots=10) file2 = tempfile.mktemp(".h5") fileh2 = open_file(file2, mode="w", node_cache_slots=10) if common.verbose: print("\nCopying deep tree...") fileh.copy_node(fileh.root, fileh2.root, recursive=True) fileh.close() fileh2.close() self._check_tree(file2) os.remove(file2) def test01c_copyDeepTree(self): "Copy of a large depth object tree with no node cache." fileh = open_file(self.file, mode="r", node_cache_slots=0) file2 = tempfile.mktemp(".h5") fileh2 = open_file(file2, mode="w", node_cache_slots=0) if common.verbose: print("\nCopying deep tree...") fileh.copy_node(fileh.root, fileh2.root, recursive=True) fileh.close() fileh2.close() self._check_tree(file2) os.remove(file2) def test01d_copyDeepTree(self): "Copy of a large depth object tree with static node cache." # Do not execute this in heavy mode if common.heavy: return fileh = open_file(self.file, mode="r", node_cache_slots=-256) file2 = tempfile.mktemp(".h5") fileh2 = open_file(file2, mode="w", node_cache_slots=-256) if common.verbose: print("\nCopying deep tree...") fileh.copy_node(fileh.root, fileh2.root, recursive=True) fileh.close() fileh2.close() self._check_tree(file2) os.remove(file2) class WideTreeTestCase(unittest.TestCase): """Checks for maximum number of children for a Group.""" def test00_Leafs(self): """Checking creation of large number of leafs (1024) per group. Variable 'maxchildren' controls this check. PyTables support up to 4096 children per group, but this would take too much memory (up to 64 MB) for testing purposes (may be we can add a test for big platforms). A 1024 children run takes up to 30 MB. A 512 children test takes around 25 MB. """ import time if common.heavy: maxchildren = 4096 else: maxchildren = 256 if common.verbose: print('\n', '-=' * 30) print("Running %s.test00_wideTree..." % self.__class__.__name__) print("Maximum number of children tested :", maxchildren) # Open a new empty HDF5 file file = tempfile.mktemp(".h5") # file = "test_widetree.h5" a = [1, 1] fileh = open_file(file, mode="w") if common.verbose: print("Children writing progress: ", end=' ') for child in range(maxchildren): if common.verbose: print("%3d," % (child), end=' ') fileh.create_array(fileh.root, 'array' + str(child), a, "child: %d" % child) if common.verbose: print() # Close the file fileh.close() t1 = time.time() a = [1, 1] # Open the previous HDF5 file in read-only mode fileh = open_file(file, mode="r") if common.verbose: print("\nTime spent opening a file with %d arrays: %s s" % (maxchildren, time.time()-t1)) print("\nChildren reading progress: ", end=' ') # Get the metadata on the previosly saved arrays for child in range(maxchildren): if common.verbose: print("%3d," % (child), end=' ') # Create an array for later comparison # Get the actual array array_ = getattr(fileh.root, 'array' + str(child)) b = array_.read() # Arrays a and b must be equal self.assertEqual(a, b) if common.verbose: print() # This flush the stdout buffer # Close the file fileh.close() # Then, delete the file os.remove(file) def test01_wideTree(self): """Checking creation of large number of groups (1024) per group. Variable 'maxchildren' controls this check. PyTables support up to 4096 children per group, but this would take too much memory (up to 64 MB) for testing purposes (may be we can add a test for big platforms). A 1024 children run takes up to 30 MB. A 512 children test takes around 25 MB. """ import time if common.heavy: # for big platforms! maxchildren = 4096 else: # for standard platforms maxchildren = 256 if common.verbose: print('\n', '-=' * 30) print("Running %s.test00_wideTree..." % self.__class__.__name__) print("Maximum number of children tested :", maxchildren) # Open a new empty HDF5 file file = tempfile.mktemp(".h5") # file = "test_widetree.h5" fileh = open_file(file, mode="w") if common.verbose: print("Children writing progress: ", end=' ') for child in range(maxchildren): if common.verbose: print("%3d," % (child), end=' ') fileh.create_group(fileh.root, 'group' + str(child), "child: %d" % child) if common.verbose: print() # Close the file fileh.close() t1 = time.time() # Open the previous HDF5 file in read-only mode fileh = open_file(file, mode="r") if common.verbose: print("\nTime spent opening a file with %d groups: %s s" % (maxchildren, time.time()-t1)) print("\nChildren reading progress: ", end=' ') # Get the metadata on the previosly saved arrays for child in range(maxchildren): if common.verbose: print("%3d," % (child), end=' ') # Get the actual group group = getattr(fileh.root, 'group' + str(child)) # Arrays a and b must be equal self.assertEqual(group._v_title, "child: %d" % child) if common.verbose: print() # This flush the stdout buffer # Close the file fileh.close() # Then, delete the file os.remove(file) class HiddenTreeTestCase(unittest.TestCase): """Check for hidden groups, leaves and hierarchies.""" def setUp(self): self.h5fname = tempfile.mktemp('.h5') self.h5file = open_file( self.h5fname, 'w', title="Test for hidden nodes") self.visible = [] # list of visible object paths self.hidden = [] # list of hidden object paths # Create some visible nodes: a, g, g/a1, g/a2, g/g, g/g/a. h5f = self.h5file h5f.create_array('/', 'a', [0]) g = h5f.create_group('/', 'g') h5f.create_array(g, 'a1', [0]) h5f.create_array(g, 'a2', [0]) g_g = h5f.create_group(g, 'g') h5f.create_array(g_g, 'a', [0]) self.visible.extend(['/a', '/g', '/g/a1', '/g/a2', '/g/g', '/g/g/a']) # Create some hidden nodes: _p_a, _p_g, _p_g/a, _p_g/_p_a, g/_p_a. h5f.create_array('/', '_p_a', [0]) hg = h5f.create_group('/', '_p_g') h5f.create_array(hg, 'a', [0]) h5f.create_array(hg, '_p_a', [0]) h5f.create_array(g, '_p_a', [0]) self.hidden.extend( ['/_p_a', '/_p_g', '/_p_g/a', '/_p_g/_p_a', '/g/_p_a']) def tearDown(self): self.h5file.close() self.h5file = None os.remove(self.h5fname) # The test behind commented out because the .objects dictionary # has been removed (as well as .leaves and .groups) def _test00_objects(self): """Absence of hidden nodes in `File.objects`.""" objects = self.h5file.objects warnings.filterwarnings('ignore', category=DeprecationWarning) for vpath in self.visible: self.assertTrue( vpath in objects, "Missing visible node ``%s`` from ``File.objects``." % vpath) for hpath in self.hidden: self.assertTrue( hpath not in objects, "Found hidden node ``%s`` in ``File.objects``." % hpath) warnings.filterwarnings('default', category=DeprecationWarning) # The test behind commented out because the .objects dictionary # has been removed (as well as .leaves and .groups) def _test00b_objects(self): """Object dictionaries conformance with ``walk_nodes()``.""" def dictCheck(dictName, classname): file_ = self.h5file objects = getattr(file_, dictName) walkPaths = [node._v_pathname for node in file_.walk_nodes('/', classname)] dictPaths = [path for path in objects] walkPaths.sort() dictPaths.sort() self.assertEqual( walkPaths, dictPaths, "nodes in ``%s`` do not match those from ``walk_nodes()``" % dictName) self.assertEqual( len(walkPaths), len(objects), "length of ``%s`` differs from that of ``walk_nodes()``" % dictName) warnings.filterwarnings('ignore', category=DeprecationWarning) dictCheck('objects', None) dictCheck('groups', 'Group') dictCheck('leaves', 'Leaf') warnings.filterwarnings('default', category=DeprecationWarning) def test01_getNode(self): """Node availability via `File.get_node()`.""" h5f = self.h5file for vpath in self.visible: h5f.get_node(vpath) for hpath in self.hidden: h5f.get_node(hpath) def test02_walkGroups(self): """Hidden group absence in `File.walk_groups()`.""" hidden = self.hidden for group in self.h5file.walk_groups('/'): pathname = group._v_pathname self.assertTrue(pathname not in hidden, "Walked across hidden group ``%s``." % pathname) def test03_walkNodes(self): """Hidden node absence in `File.walk_nodes()`.""" hidden = self.hidden for node in self.h5file.walk_nodes('/'): pathname = node._v_pathname self.assertTrue(pathname not in hidden, "Walked across hidden node ``%s``." % pathname) def test04_listNodesVisible(self): """Listing visible nodes under a visible group (list_nodes).""" hidden = self.hidden for node in self.h5file.list_nodes('/g'): pathname = node._v_pathname self.assertTrue(pathname not in hidden, "Listed hidden node ``%s``." % pathname) def test04b_listNodesVisible(self): """Listing visible nodes under a visible group (iter_nodes).""" hidden = self.hidden for node in self.h5file.iter_nodes('/g'): pathname = node._v_pathname self.assertTrue(pathname not in hidden, "Listed hidden node ``%s``." % pathname) def test05_listNodesHidden(self): """Listing visible nodes under a hidden group (list_nodes).""" hidden = self.hidden node_to_find = '/_p_g/a' found_node = False for node in self.h5file.list_nodes('/_p_g'): pathname = node._v_pathname if pathname == node_to_find: found_node = True self.assertTrue(pathname in hidden, "Listed hidden node ``%s``." % pathname) self.assertTrue(found_node, "Hidden node ``%s`` was not listed." % node_to_find) def test05b_iterNodesHidden(self): """Listing visible nodes under a hidden group (iter_nodes).""" hidden = self.hidden node_to_find = '/_p_g/a' found_node = False for node in self.h5file.iter_nodes('/_p_g'): pathname = node._v_pathname if pathname == node_to_find: found_node = True self.assertTrue(pathname in hidden, "Listed hidden node ``%s``." % pathname) self.assertTrue(found_node, "Hidden node ``%s`` was not listed." % node_to_find) # The test behind commented out because the .objects dictionary # has been removed (as well as .leaves and .groups) def _test06_reopen(self): """Reopening a file with hidden nodes.""" self.h5file.close() self.h5file = open_file(self.h5fname) self.test00_objects() def test07_move(self): """Moving a node between hidden and visible groups.""" is_visible_node = self.h5file.is_visible_node self.assertFalse(is_visible_node('/_p_g/a')) self.h5file.move_node('/_p_g/a', '/g', 'a') self.assertTrue(is_visible_node('/g/a')) self.h5file.move_node('/g/a', '/_p_g', 'a') self.assertFalse(is_visible_node('/_p_g/a')) def test08_remove(self): """Removing a visible group with hidden children.""" self.assertTrue('/g/_p_a' in self.h5file) self.h5file.root.g._f_remove(recursive=True) self.assertFalse('/g/_p_a' in self.h5file) class CreateParentsTestCase(common.TempFileMixin, common.PyTablesTestCase): """Test the ``createparents`` flag. These are mainly for the user interface. More thorough tests on the workings of the flag can be found in the ``test_do_undo.py`` module. """ filters = Filters(complevel=4) # simply non-default def setUp(self): super(CreateParentsTestCase, self).setUp() self.h5file.create_array('/', 'array', [1]) self.h5file.create_group('/', 'group', filters=self.filters) def test00_parentType(self): """Using the right type of parent node argument.""" h5file, root = self.h5file, self.h5file.root self.assertRaises(TypeError, h5file.create_array, root.group, 'arr', [1], createparents=True) self.assertRaises(TypeError, h5file.copy_node, '/array', root.group, createparents=True) self.assertRaises(TypeError, h5file.move_node, '/array', root.group, createparents=True) self.assertRaises(TypeError, h5file.copy_children, '/group', root, createparents=True) def test01_inside(self): """Placing a node inside a nonexistent child of itself.""" self.assertRaises(NodeError, self.h5file.move_node, '/group', '/group/foo/bar', createparents=True) self.assertFalse('/group/foo' in self.h5file) self.assertRaises(NodeError, self.h5file.copy_node, '/group', '/group/foo/bar', recursive=True, createparents=True) self.assertFalse('/group/foo' in self.h5file) def test02_filters(self): """Propagating the filters of created parent groups.""" self.h5file.create_group('/group/foo/bar', 'baz', createparents=True) self.assertTrue('/group/foo/bar/baz' in self.h5file) for group in self.h5file.walk_groups('/group'): self.assertEqual(self.filters, group._v_filters) #---------------------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() # This counter is useful when detecting memory leaks niter = 1 for i in range(niter): theSuite.addTest(unittest.makeSuite(TreeTestCase)) theSuite.addTest(unittest.makeSuite(DeepTreeTestCase)) theSuite.addTest(unittest.makeSuite(WideTreeTestCase)) theSuite.addTest(unittest.makeSuite(HiddenTreeTestCase)) theSuite.addTest(unittest.makeSuite(CreateParentsTestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/test_types.py000066400000000000000000000274531231437614300207130ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import sys import unittest import os import numpy from tables import * from tables.tests import common # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup # Test Record class class Record(IsDescription): var1 = StringCol(itemsize=4) # 4-character String var2 = Col.from_kind('int') # integer var3 = Col.from_kind('int', itemsize=2) # short integer var4 = Col.from_kind('float') # double (double-precision) var5 = Col.from_kind('float', itemsize=4) # float (single-precision) var6 = Col.from_kind('complex') # double-precision var7 = Col.from_kind('complex', itemsize=8) # single-precision if "Float16Atom" in globals(): var8 = Col.from_kind('float', itemsize=2) # half-precision if "Float96Atom" in globals(): var9 = Col.from_kind('float', itemsize=12) # extended-precision if "Float128Atom" in globals(): var10 = Col.from_kind('float', itemsize=16) # extended-precision if "Complex192Atom" in globals(): var11 = Col.from_kind('complex', itemsize=24) # extended-precision if "Complex256Atom" in globals(): var12 = Col.from_kind('complex', itemsize=32) # extended-precision class RangeTestCase(unittest.TestCase): file = "test.h5" title = "This is the table title" expectedrows = 100 maxshort = 2 ** 15 maxint = 2147483648 # (2 ** 31) compress = 0 def setUp(self): # Create an instance of HDF5 Table self.fileh = open_file(self.file, mode="w") self.rootgroup = self.fileh.root # Create a table self.table = self.fileh.create_table(self.rootgroup, 'table', Record, self.title) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test00_range(self): """Testing the range check.""" rec = self.table.row # Save a record i = self.maxshort rec['var1'] = '%04d' % (i) rec['var2'] = i rec['var3'] = i rec['var4'] = float(i) rec['var5'] = float(i) rec['var6'] = float(i) rec['var7'] = complex(i, i) if "Float16Atom" in globals(): rec['var8'] = float(i) if "Float96Atom" in globals(): rec['var9'] = float(i) if "Float128Atom" in globals(): rec['var10'] = float(i) try: rec.append() except ValueError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next ValueError was catched!") print(value) pass else: if common.verbose: print( "\nNow, the range overflow no longer issues a ValueError") def test01_type(self): """Testing the type check.""" rec = self.table.row # Save a record i = self.maxshort rec['var1'] = '%04d' % (i) rec['var2'] = i rec['var3'] = i % self.maxshort rec['var5'] = float(i) try: rec['var4'] = "124c" except TypeError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next TypeError was catched!") print(value) pass else: print(rec) self.fail("expected a TypeError") rec['var6'] = float(i) rec['var7'] = complex(i, i) if "Float16Atom" in globals(): rec['var8'] = float(i) if "Float96Atom" in globals(): rec['var9'] = float(i) if "Float128Atom" in globals(): rec['var10'] = float(i) # Check the dtype read-only attribute class DtypeTestCase(common.TempFileMixin, common.PyTablesTestCase): def test00a_table(self): """Check dtype accessor for Table objects.""" a = self.h5file.create_table('/', 'table', Record) self.assertEqual(a.dtype, a.description._v_dtype) def test00b_column(self): """Check dtype accessor for Column objects.""" a = self.h5file.create_table('/', 'table', Record) c = a.cols.var3 self.assertEqual(c.dtype, a.description._v_dtype['var3']) def test01_array(self): """Check dtype accessor for Array objects.""" a = self.h5file.create_array('/', 'array', [1, 2]) self.assertEqual(a.dtype, a.atom.dtype) def test02_carray(self): """Check dtype accessor for CArray objects.""" a = self.h5file.create_carray( '/', 'array', atom=FloatAtom(), shape=[1, 2]) self.assertEqual(a.dtype, a.atom.dtype) def test03_carray(self): """Check dtype accessor for EArray objects.""" a = self.h5file.create_earray( '/', 'array', atom=FloatAtom(), shape=[0, 2]) self.assertEqual(a.dtype, a.atom.dtype) def test04_vlarray(self): """Check dtype accessor for VLArray objects.""" a = self.h5file.create_vlarray('/', 'array', FloatAtom()) self.assertEqual(a.dtype, a.atom.dtype) class ReadFloatTestCase(common.PyTablesTestCase): filename = "float.h5" nrows = 5 ncols = 6 def setUp(self): self.fileh = open_file(self._testFilename(self.filename), mode="r") x = numpy.arange(self.ncols) y = numpy.arange(self.nrows) y.shape = (self.nrows, 1) self.values = x + y def tearDown(self): self.fileh.close() def test01_read_float16(self): dtype = "float16" if hasattr(numpy, dtype): ds = getattr(self.fileh.root, dtype) self.assertFalse(isinstance(ds, UnImplemented)) self.assertEqual(ds.shape, (self.nrows, self.ncols)) self.assertEqual(ds.dtype, dtype) self.assertTrue(common.allequal( ds.read(), self.values.astype(dtype))) else: ds = self.assertWarns(UserWarning, getattr, self.fileh.root, dtype) self.assertTrue(isinstance(ds, UnImplemented)) def test02_read_float32(self): dtype = "float32" ds = getattr(self.fileh.root, dtype) self.assertFalse(isinstance(ds, UnImplemented)) self.assertEqual(ds.shape, (self.nrows, self.ncols)) self.assertEqual(ds.dtype, dtype) self.assertTrue(common.allequal( ds.read(), self.values.astype(dtype))) def test03_read_float64(self): dtype = "float64" ds = getattr(self.fileh.root, dtype) self.assertFalse(isinstance(ds, UnImplemented)) self.assertEqual(ds.shape, (self.nrows, self.ncols)) self.assertEqual(ds.dtype, dtype) self.assertTrue(common.allequal( ds.read(), self.values.astype(dtype))) def test04_read_longdouble(self): dtype = "longdouble" if "Float96Atom" in globals() or "Float128Atom" in globals(): ds = getattr(self.fileh.root, dtype) self.assertFalse(isinstance(ds, UnImplemented)) self.assertEqual(ds.shape, (self.nrows, self.ncols)) self.assertEqual(ds.dtype, dtype) self.assertTrue(common.allequal( ds.read(), self.values.astype(dtype))) if "Float96Atom" in globals(): self.assertEqual(ds.dtype, "float96") elif "Float128Atom" in globals(): self.assertEqual(ds.dtype, "float128") else: # XXX: check # the behavior depends on the HDF5 lib configuration try: ds = self.assertWarns(UserWarning, getattr, self.fileh.root, dtype) self.assertTrue(isinstance(ds, UnImplemented)) except AssertionError: from tables.utilsextension import _broken_hdf5_long_double if not _broken_hdf5_long_double(): ds = getattr(self.fileh.root, dtype) self.assertEqual(ds.dtype, "float64") def test05_read_quadprecision_float(self): # XXX: check try: ds = self.assertWarns(UserWarning, getattr, self.fileh.root, "quadprecision") self.assertTrue(isinstance(ds, UnImplemented)) except AssertionError: # NOTE: it would be nice to have some sort of message that warns # against the potential precision loss: the quad-precision # dataset actually uses 128 bits for each element, not just # 80 bits (longdouble) ds = self.fileh.root.quadprecision self.assertEqual(ds.dtype, "longdouble") class AtomTestCase(common.PyTablesTestCase): def test_init_parameters_01(self): atom1 = StringAtom(itemsize=12) atom2 = atom1.copy() self.assertEqual(atom1, atom2) self.assertEqual(str(atom1), str(atom2)) self.assertFalse(atom1 is atom2) def test_init_parameters_02(self): atom1 = StringAtom(itemsize=12) atom2 = atom1.copy(itemsize=100, shape=(2, 2)) self.assertEqual(atom2, StringAtom(itemsize=100, shape=(2, 2), dflt=b'')) def test_init_parameters_03(self): atom1 = StringAtom(itemsize=12) self.assertRaises(TypeError, atom1.copy, foobar=42) def test_from_dtype_01(self): atom1 = Atom.from_dtype(numpy.dtype((numpy.int16, (2, 2)))) atom2 = Int16Atom(shape=(2, 2), dflt=0) self.assertEqual(atom1, atom2) self.assertEqual(str(atom1), str(atom2)) def test_from_dtype_02(self): atom1 = Atom.from_dtype(numpy.dtype('S5'), dflt=b'hello') atom2 = StringAtom(itemsize=5, shape=(), dflt=b'hello') self.assertEqual(atom1, atom2) self.assertEqual(str(atom1), str(atom2)) def test_from_dtype_03(self): atom1 = Atom.from_dtype(numpy.dtype('Float64')) atom2 = Float64Atom(shape=(), dflt=0.0) self.assertEqual(atom1, atom2) self.assertEqual(str(atom1), str(atom2)) def test_from_kind_01(self): atom1 = Atom.from_kind('int', itemsize=2, shape=(2, 2)) atom2 = Int16Atom(shape=(2, 2), dflt=0) self.assertEqual(atom1, atom2) self.assertEqual(str(atom1), str(atom2)) def test_from_kind_02(self): atom1 = Atom.from_kind('int', shape=(2, 2)) atom2 = Int32Atom(shape=(2, 2), dflt=0) self.assertEqual(atom1, atom2) self.assertEqual(str(atom1), str(atom2)) def test_from_kind_03(self): atom1 = Atom.from_kind('int', shape=1) atom2 = Int32Atom(shape=(1,), dflt=0) self.assertEqual(atom1, atom2) self.assertEqual(str(atom1), str(atom2)) def test_from_kind_04(self): atom1 = Atom.from_kind('string', itemsize=5, dflt=b'hello') atom2 = StringAtom(itemsize=5, shape=(), dflt=b'hello') self.assertEqual(atom1, atom2) self.assertEqual(str(atom1), str(atom2)) def test_from_kind_05(self): # ValueError: no default item size for kind ``string`` self.assertRaises(ValueError, Atom.from_kind, 'string', dflt=b'hello') def test_from_kind_06(self): # ValueError: unknown kind: 'Float' self.assertRaises(ValueError, Atom.from_kind, 'Float') #---------------------------------------------------------------------- def suite(): import doctest import tables.atom theSuite = unittest.TestSuite() for i in range(1): theSuite.addTest(doctest.DocTestSuite(tables.atom)) theSuite.addTest(unittest.makeSuite(AtomTestCase)) theSuite.addTest(unittest.makeSuite(RangeTestCase)) theSuite.addTest(unittest.makeSuite(DtypeTestCase)) theSuite.addTest(unittest.makeSuite(ReadFloatTestCase)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/test_vlarray.py000066400000000000000000005071401231437614300212230ustar00rootroot00000000000000# -*- coding: latin-1 -*- from __future__ import print_function import sys import unittest import os import tempfile #import cPickle import numpy import numpy.testing as npt import tables from tables import * from tables.tests import common from tables.tests.common import allequal from tables.utils import byteorders # To delete the internal attributes automagically unittest.TestCase.tearDown = common.cleanup class C: c = (3, 4.5) class BasicTestCase(unittest.TestCase): compress = 0 complib = "zlib" shuffle = 0 fletcher32 = 0 flavor = "numpy" def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") self.rootgroup = self.fileh.root self.populateFile() self.fileh.close() def populateFile(self): group = self.rootgroup filters = Filters(complevel=self.compress, complib=self.complib, shuffle=self.shuffle, fletcher32=self.fletcher32) vlarray = self.fileh.create_vlarray(group, 'vlarray1', atom=Int32Atom(), title="ragged array if ints", filters=filters, expectedrows=1000) vlarray.flavor = self.flavor # Fill it with 5 rows vlarray.append([1, 2]) if self.flavor == "numpy": vlarray.append(numpy.array([3, 4, 5], dtype='int32')) vlarray.append(numpy.array([], dtype='int32')) # Empty entry elif self.flavor == "python": vlarray.append((3, 4, 5)) vlarray.append(()) # Empty entry vlarray.append([6, 7, 8, 9]) vlarray.append([10, 11, 12, 13, 14]) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test00_attributes(self): self.fileh = open_file(self.file, "r") obj = self.fileh.get_node("/vlarray1") self.assertEqual(obj.flavor, self.flavor) self.assertEqual(obj.shape, (5,)) self.assertEqual(obj.ndim, 1) self.assertEqual(obj.nrows, 5) self.assertEqual(obj.atom.type, 'int32') def test01_read(self): """Checking vlarray read.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_read..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node("/vlarray1") # Choose a small value for buffer size vlarray.nrowsinbuf = 3 # Read some rows row = vlarray.read(0)[0] row2 = vlarray.read(2)[0] if common.verbose: print("Flavor:", vlarray.flavor) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row) nrows = 5 self.assertEqual(nrows, vlarray.nrows) if self.flavor == "numpy": self.assertEqual(type(row), numpy.ndarray) self.assertTrue( allequal(row, numpy.array([1, 2], dtype='int32'), self.flavor)) self.assertTrue( allequal(row2, numpy.array([], dtype='int32'), self.flavor)) elif self.flavor == "python": self.assertEqual(row, [1, 2]) self.assertEqual(row2, []) self.assertEqual(len(row), 2) # Check filters: if self.compress != vlarray.filters.complevel and common.verbose: print("Error in compress. Class:", self.__class__.__name__) print("self, vlarray:", self.compress, vlarray.filters.complevel) self.assertEqual(vlarray.filters.complevel, self.compress) if self.compress > 0 and which_lib_version(self.complib): self.assertEqual(vlarray.filters.complib, self.complib) if self.shuffle != vlarray.filters.shuffle and common.verbose: print("Error in shuffle. Class:", self.__class__.__name__) print("self, vlarray:", self.shuffle, vlarray.filters.shuffle) self.assertEqual(self.shuffle, vlarray.filters.shuffle) if self.fletcher32 != vlarray.filters.fletcher32 and common.verbose: print("Error in fletcher32. Class:", self.__class__.__name__) print("self, vlarray:", self.fletcher32, vlarray.filters.fletcher32) self.assertEqual(self.fletcher32, vlarray.filters.fletcher32) def test02a_getitem(self): """Checking vlarray __getitem__ (slices)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02a_getitem..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node("/vlarray1") rows = [[1, 2], [3, 4, 5], [], [6, 7, 8, 9], [10, 11, 12, 13, 14]] slices = [ slice(None, None, None), slice(1, 1, 1), slice(30, None, None), slice(0, None, None), slice(3, None, 1), slice(3, None, 2), slice(None, 1, None), slice(None, 2, 1), slice(None, 30, 2), slice(None, None, 1), slice(None, None, 2), slice(None, None, 3), ] for slc in slices: # Read the rows in slc rows2 = vlarray[slc] rows1 = rows[slc] rows1f = [] if common.verbose: print("Flavor:", vlarray.flavor) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Original rows ==>", rows1) print("Rows read in vlarray ==>", rows2) if self.flavor == "numpy": for val in rows1: rows1f.append(numpy.array(val, dtype='int32')) for i in range(len(rows1f)): self.assertTrue(allequal(rows2[i], rows1f[i], self.flavor)) elif self.flavor == "python": self.assertEqual(rows2, rows1) def test02b_getitem(self): """Checking vlarray __getitem__ (scalars)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02b_getitem..." % self.__class__.__name__) if self.flavor != "numpy": # This test is only valid for NumPy return # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node("/vlarray1") # Get a numpy array of objects rows = numpy.array(vlarray[:], dtype=numpy.object) for slc in [0, numpy.array(1), 2, numpy.array([3]), [4]]: # Read the rows in slc rows2 = vlarray[slc] rows1 = rows[slc] if common.verbose: print("Flavor:", vlarray.flavor) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Original rows ==>", rows1) print("Rows read in vlarray ==>", rows2) for i in range(len(rows1)): self.assertTrue(allequal(rows2[i], rows1[i], self.flavor)) def test03_append(self): """Checking vlarray append.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_append..." % self.__class__.__name__) # Create an instance of an HDF5 Table self.fileh = open_file(self.file, "a") vlarray = self.fileh.get_node("/vlarray1") # Append a new row vlarray.append([7, 8, 9, 10]) # Choose a small value for buffer size vlarray.nrowsinbuf = 3 # Read some rows: row1 = vlarray[0] row2 = vlarray[2] row3 = vlarray[-1] if common.verbose: print("Flavor:", vlarray.flavor) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row1) nrows = 6 self.assertEqual(nrows, vlarray.nrows) if self.flavor == "numpy": self.assertEqual(type(row1), type(numpy.array([1, 2]))) self.assertTrue( allequal(row1, numpy.array([1, 2], dtype='int32'), self.flavor)) self.assertTrue( allequal(row2, numpy.array([], dtype='int32'), self.flavor)) self.assertTrue( allequal(row3, numpy.array([7, 8, 9, 10], dtype='int32'), self.flavor)) elif self.flavor == "python": self.assertEqual(row1, [1, 2]) self.assertEqual(row2, []) self.assertEqual(row3, [7, 8, 9, 10]) self.assertEqual(len(row3), 4) def test04_get_row_size(self): """Checking get_row_size method.""" self.fileh = open_file(self.file, "a") vlarray = self.fileh.get_node("/vlarray1") self.assertEqual(vlarray.get_row_size(0), 2 * vlarray.atom.size) self.assertEqual(vlarray.get_row_size(1), 3 * vlarray.atom.size) self.assertEqual(vlarray.get_row_size(2), 0 * vlarray.atom.size) self.assertEqual(vlarray.get_row_size(3), 4 * vlarray.atom.size) self.assertEqual(vlarray.get_row_size(4), 5 * vlarray.atom.size) class BasicNumPyTestCase(BasicTestCase): flavor = "numpy" class BasicPythonTestCase(BasicTestCase): flavor = "python" class ZlibComprTestCase(BasicTestCase): compress = 1 complib = "zlib" class BloscComprTestCase(BasicTestCase): compress = 9 shuffle = 0 complib = "blosc" class BloscShuffleComprTestCase(BasicTestCase): compress = 6 shuffle = 1 complib = "blosc" class BloscBloscLZComprTestCase(BasicTestCase): compress = 9 shuffle = 1 complib = "blosc:blosclz" class BloscLZ4ComprTestCase(BasicTestCase): compress = 9 shuffle = 1 complib = "blosc:lz4" class BloscLZ4HCComprTestCase(BasicTestCase): compress = 9 shuffle = 1 complib = "blosc:lz4hc" class BloscSnappyComprTestCase(BasicTestCase): compress = 9 shuffle = 1 complib = "blosc:snappy" class BloscZlibComprTestCase(BasicTestCase): compress = 9 shuffle = 1 complib = "blosc:zlib" class LZOComprTestCase(BasicTestCase): compress = 1 complib = "lzo" class Bzip2ComprTestCase(BasicTestCase): compress = 1 complib = "bzip2" class ShuffleComprTestCase(BasicTestCase): compress = 1 shuffle = 1 class Fletcher32TestCase(BasicTestCase): fletcher32 = 1 class AllFiltersTestCase(BasicTestCase): compress = 1 shuffle = 1 fletcher32 = 1 class TypesTestCase(unittest.TestCase): mode = "w" compress = 0 complib = "zlib" # Default compression library def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, self.mode) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test01_StringAtom(self): """Checking vlarray with NumPy string atoms ('numpy' flavor)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_StringAtom..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray('/', 'stringAtom', atom=StringAtom(itemsize=3), title="Ragged array of strings") vlarray.flavor = "numpy" vlarray.append(numpy.array(["1", "12", "123", "1234", "12345"])) vlarray.append(numpy.array(["1", "12345"])) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) npt.assert_array_equal( row[0], numpy.array(["1", "12", "123", "123", "123"], 'S')) npt.assert_array_equal(row[1], numpy.array(["1", "123"], 'S')) self.assertEqual(len(row[0]), 5) self.assertEqual(len(row[1]), 2) def test01a_StringAtom(self): """Checking vlarray with NumPy string atoms ('numpy' flavor, strided)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01a_StringAtom..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray('/', 'stringAtom', atom=StringAtom(itemsize=3), title="Ragged array of strings") vlarray.flavor = "numpy" vlarray.append(numpy.array(["1", "12", "123", "1234", "12345"][::2])) vlarray.append(numpy.array(["1", "12345", "2", "321"])[::3]) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) npt.assert_array_equal(row[0], numpy.array(["1", "123", "123"], 'S')) npt.assert_array_equal(row[1], numpy.array(["1", "321"], 'S')) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test01a_2_StringAtom(self): """Checking vlarray with NumPy string atoms (NumPy flavor, no conv)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01a_2_StringAtom..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray('/', 'stringAtom', atom=StringAtom(itemsize=3), title="Ragged array of strings") vlarray.flavor = "numpy" vlarray.append(numpy.array(["1", "12", "123", "123"])) vlarray.append(numpy.array(["1", "2", "321"])) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) npt.assert_array_equal( row[0], numpy.array(["1", "12", "123", "123"], 'S')) npt.assert_array_equal(row[1], numpy.array(["1", "2", "321"], 'S')) self.assertEqual(len(row[0]), 4) self.assertEqual(len(row[1]), 3) def test01b_StringAtom(self): """Checking vlarray with NumPy string atoms (python flavor)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01b_StringAtom..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray('/', 'stringAtom2', atom=StringAtom(itemsize=3), title="Ragged array of strings") vlarray.flavor = "python" vlarray.append(["1", "12", "123", "1234", "12345"]) vlarray.append(["1", "12345"]) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing String flavor") print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertEqual(row[0], [b"1", b"12", b"123", b"123", b"123"]) self.assertEqual(row[1], [b"1", b"123"]) self.assertEqual(len(row[0]), 5) self.assertEqual(len(row[1]), 2) def test01c_StringAtom(self): """Checking updating vlarray with NumPy string atoms. ('numpy' flavor) """ if common.verbose: print('\n', '-=' * 30) print("Running %s.test01c_StringAtom..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray('/', 'stringAtom', atom=StringAtom(itemsize=3), title="Ragged array of strings") vlarray.flavor = "numpy" vlarray.append(numpy.array(["1", "12", "123", "1234", "12345"])) vlarray.append(numpy.array(["1", "12345"])) # Modify the rows vlarray[0] = numpy.array(["1", "123", "12", "", "12345"]) vlarray[1] = numpy.array(["44", "4"]) # This should work as well if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertTrue( allequal(row[0], numpy.array([b"1", b"123", b"12", b"", b"123"]))) self.assertTrue(allequal(row[1], numpy.array(["44", "4"], dtype="S3"))) self.assertEqual(len(row[0]), 5) self.assertEqual(len(row[1]), 2) def test01d_StringAtom(self): """Checking updating vlarray with string atoms (String flavor)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01d_StringAtom..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray('/', 'stringAtom2', atom=StringAtom(itemsize=3), title="Ragged array of strings") vlarray.flavor = "python" vlarray.append(["1", "12", "123", "1234", "12345"]) vlarray.append(["1", "12345"]) # Modify the rows vlarray[0] = ["1", "123", "12", "", "12345"] vlarray[1] = ["44", "4"] if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing String flavor") print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertEqual(row[0], [b"1", b"123", b"12", b"", b"123"]) self.assertEqual(row[1], [b"44", b"4"]) self.assertEqual(len(row[0]), 5) self.assertEqual(len(row[1]), 2) def test02_BoolAtom(self): """Checking vlarray with boolean atoms.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_BoolAtom..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray('/', 'BoolAtom', atom=BoolAtom(), title="Ragged array of Booleans") vlarray.append([1, 0, 3]) vlarray.append([-1, 0]) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertTrue(allequal(row[0], numpy.array([1, 0, 1], dtype='bool'))) self.assertTrue(allequal(row[1], numpy.array([1, 0], dtype='bool'))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test02b_BoolAtom(self): """Checking setting vlarray with boolean atoms.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02b_BoolAtom..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray('/', 'BoolAtom', atom=BoolAtom(), title="Ragged array of Booleans") vlarray.append([1, 0, 3]) vlarray.append([-1, 0]) # Modify the rows vlarray[0] = (0, 1, 3) vlarray[1] = (0, -1) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertTrue(allequal(row[0], numpy.array([0, 1, 1], dtype='bool'))) self.assertTrue(allequal(row[1], numpy.array([0, 1], dtype='bool'))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test03_IntAtom(self): """Checking vlarray with integer atoms.""" ttypes = [ "Int8", "UInt8", "Int16", "UInt16", "Int32", "UInt32", "Int64", #"UInt64", # Unavailable in some platforms ] if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_IntAtom..." % self.__class__.__name__) for atype in ttypes: vlarray = self.fileh.create_vlarray('/', atype, atom=Atom.from_sctype(atype)) vlarray.append([1, 2, 3]) vlarray.append([-1, 0]) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "a") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing type:", atype) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertTrue(allequal(row[ 0], numpy.array([1, 2, 3], dtype=atype))) self.assertTrue(allequal(row[ 1], numpy.array([-1, 0], dtype=atype))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test03a_IntAtom(self): """Checking vlarray with integer atoms (byteorder swapped)""" ttypes = { "Int8": numpy.int8, "UInt8": numpy.uint8, "Int16": numpy.int16, "UInt16": numpy.uint16, "Int32": numpy.int32, "UInt32": numpy.uint32, "Int64": numpy.int64, #"UInt64": numpy.int64, # Unavailable in some platforms } if common.verbose: print('\n', '-=' * 30) print("Running %s.test03a_IntAtom..." % self.__class__.__name__) for atype in ttypes: vlarray = self.fileh.create_vlarray( '/', atype, atom=Atom.from_sctype(ttypes[atype])) a0 = numpy.array([1, 2, 3], dtype=atype) a0 = a0.byteswap() a0 = a0.newbyteorder() vlarray.append(a0) a1 = numpy.array([-1, 0], dtype=atype) a1 = a1.byteswap() a1 = a1.newbyteorder() vlarray.append(a1) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "a") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing type:", atype) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertTrue( allequal(row[0], numpy.array([1, 2, 3], dtype=ttypes[atype]))) self.assertTrue( allequal(row[1], numpy.array([-1, 0], dtype=ttypes[atype]))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test03b_IntAtom(self): """Checking updating vlarray with integer atoms.""" ttypes = [ "Int8", "UInt8", "Int16", "UInt16", "Int32", "UInt32", "Int64", #"UInt64", # Unavailable in some platforms ] if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_IntAtom..." % self.__class__.__name__) for atype in ttypes: vlarray = self.fileh.create_vlarray( '/', atype, atom=Atom.from_sctype(atype)) vlarray.append([1, 2, 3]) vlarray.append([-1, 0]) # Modify rows vlarray[0] = (3, 2, 1) vlarray[1] = (0, -1) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "a") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing type:", atype) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertTrue(allequal(row[ 0], numpy.array([3, 2, 1], dtype=atype))) self.assertTrue(allequal(row[ 1], numpy.array([0, -1], dtype=atype))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test03c_IntAtom(self): """Checking updating vlarray with integer atoms (byteorder swapped)""" ttypes = { "Int8": numpy.int8, "UInt8": numpy.uint8, "Int16": numpy.int16, "UInt16": numpy.uint16, "Int32": numpy.int32, "UInt32": numpy.uint32, "Int64": numpy.int64, #"UInt64": numpy.int64, # Unavailable in some platforms } if common.verbose: print('\n', '-=' * 30) print("Running %s.test03c_IntAtom..." % self.__class__.__name__) for atype in ttypes: vlarray = self.fileh.create_vlarray( '/', atype, atom=Atom.from_sctype(ttypes[atype])) a0 = numpy.array([1, 2, 3], dtype=atype) vlarray.append(a0) a1 = numpy.array([-1, 0], dtype=atype) vlarray.append(a1) # Modify rows a0 = numpy.array([3, 2, 1], dtype=atype) a0 = a0.byteswap() a0 = a0.newbyteorder() vlarray[0] = a0 a1 = numpy.array([0, -1], dtype=atype) a1 = a1.byteswap() a1 = a1.newbyteorder() vlarray[1] = a1 if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "a") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing type:", atype) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertTrue( allequal(row[0], numpy.array([3, 2, 1], dtype=ttypes[atype]))) self.assertTrue( allequal(row[1], numpy.array([0, -1], dtype=ttypes[atype]))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test03d_IntAtom(self): """Checking updating vlarray with integer atoms (another byteorder)""" ttypes = { "Int8": numpy.int8, "UInt8": numpy.uint8, "Int16": numpy.int16, "UInt16": numpy.uint16, "Int32": numpy.int32, "UInt32": numpy.uint32, "Int64": numpy.int64, #"UInt64": numpy.int64, # Unavailable in some platforms } if common.verbose: print('\n', '-=' * 30) print("Running %s.test03d_IntAtom..." % self.__class__.__name__) byteorder = {'little': 'big', 'big': 'little'}[sys.byteorder] for atype in ttypes: vlarray = self.fileh.create_vlarray( '/', atype, atom=Atom.from_sctype(ttypes[atype]), byteorder=byteorder) a0 = numpy.array([1, 2, 3], dtype=atype) vlarray.append(a0) a1 = numpy.array([-1, 0], dtype=atype) vlarray.append(a1) # Modify rows a0 = numpy.array([3, 2, 1], dtype=atype) a0 = a0.byteswap() a0 = a0.newbyteorder() vlarray[0] = a0 a1 = numpy.array([0, -1], dtype=atype) a1 = a1.byteswap() a1 = a1.newbyteorder() vlarray[1] = a1 if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "a") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing type:", atype) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) byteorder2 = byteorders[row[0].dtype.byteorder] if byteorder2 != "irrelevant": self.assertEqual(byteorders[row[0].dtype.byteorder], sys.byteorder) self.assertEqual(vlarray.byteorder, byteorder) self.assertEqual(vlarray.nrows, 2) self.assertTrue( allequal(row[0], numpy.array([3, 2, 1], dtype=ttypes[atype]))) self.assertTrue( allequal(row[1], numpy.array([0, -1], dtype=ttypes[atype]))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test04_FloatAtom(self): """Checking vlarray with floating point atoms.""" ttypes = ["Float32", "Float64", ] for name in ("float16", "float96", "float128"): atomname = name.capitalize() + 'Atom' if atomname in globals(): ttypes.append(name) if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_FloatAtom..." % self.__class__.__name__) for atype in ttypes: vlarray = self.fileh.create_vlarray( '/', atype, atom=Atom.from_sctype(atype)) vlarray.append([1.3, 2.2, 3.3]) vlarray.append([-1.3e34, 1.e-32]) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "a") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing type:", atype) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertTrue(allequal(row[ 0], numpy.array([1.3, 2.2, 3.3], atype))) self.assertTrue(allequal(row[ 1], numpy.array([-1.3e34, 1.e-32], atype))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test04a_FloatAtom(self): """Checking vlarray with float atoms (byteorder swapped)""" ttypes = { "Float32": numpy.float32, "Float64": numpy.float64, } if "Float16Atom" in globals(): ttypes["float16"] = numpy.float16 if "Float96Atom" in globals(): ttypes["float96"] = numpy.float96 if "Float128Atom" in globals(): ttypes["float128"] = numpy.float128 if common.verbose: print('\n', '-=' * 30) print("Running %s.test04a_FloatAtom..." % self.__class__.__name__) for atype in ttypes: vlarray = self.fileh.create_vlarray( '/', atype, atom=Atom.from_sctype(ttypes[atype])) a0 = numpy.array([1.3, 2.2, 3.3], dtype=atype) a0 = a0.byteswap() a0 = a0.newbyteorder() vlarray.append(a0) a1 = numpy.array([-1.3e34, 1.e-32], dtype=atype) a1 = a1.byteswap() a1 = a1.newbyteorder() vlarray.append(a1) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "a") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing type:", atype) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertTrue(allequal(row[0], numpy.array([1.3, 2.2, 3.3], dtype=ttypes[atype]))) self.assertTrue(allequal(row[1], numpy.array([-1.3e34, 1.e-32], dtype=ttypes[atype]))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test04b_FloatAtom(self): """Checking updating vlarray with floating point atoms.""" ttypes = [ "Float32", "Float64", ] for name in ("float16", "float96", "float128"): atomname = name.capitalize() + 'Atom' if atomname in globals(): ttypes.append(name) if common.verbose: print('\n', '-=' * 30) print("Running %s.test04b_FloatAtom..." % self.__class__.__name__) for atype in ttypes: vlarray = self.fileh.create_vlarray( '/', atype, atom=Atom.from_sctype(atype)) vlarray.append([1.3, 2.2, 3.3]) vlarray.append([-1.3e34, 1.e-32]) # Modifiy some rows vlarray[0] = (4.3, 2.2, 4.3) vlarray[1] = (-1.1e34, 1.3e-32) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "a") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing type:", atype) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertTrue(allequal(row[ 0], numpy.array([4.3, 2.2, 4.3], atype))) self.assertTrue( allequal(row[1], numpy.array([-1.1e34, 1.3e-32], atype))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test04c_FloatAtom(self): """Checking updating vlarray with float atoms (byteorder swapped)""" ttypes = { "Float32": numpy.float32, "Float64": numpy.float64, } if "Float16Atom" in globals(): ttypes["float16"] = numpy.float16 if "Float96Atom" in globals(): ttypes["float96"] = numpy.float96 if "Float128Atom" in globals(): ttypes["float128"] = numpy.float128 if common.verbose: print('\n', '-=' * 30) print("Running %s.test04c_FloatAtom..." % self.__class__.__name__) for atype in ttypes: vlarray = self.fileh.create_vlarray( '/', atype, atom=Atom.from_sctype(ttypes[atype])) a0 = numpy.array([1.3, 2.2, 3.3], dtype=atype) vlarray.append(a0) a1 = numpy.array([-1, 0], dtype=atype) vlarray.append(a1) # Modify rows a0 = numpy.array([4.3, 2.2, 4.3], dtype=atype) a0 = a0.byteswap() a0 = a0.newbyteorder() vlarray[0] = a0 a1 = numpy.array([-1.1e34, 1.3e-32], dtype=atype) a1 = a1.byteswap() a1 = a1.newbyteorder() vlarray[1] = a1 if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "a") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing type:", atype) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertTrue(allequal(row[0], numpy.array([4.3, 2.2, 4.3], dtype=ttypes[atype]))) self.assertTrue(allequal(row[1], numpy.array([-1.1e34, 1.3e-32], dtype=ttypes[atype]))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test04d_FloatAtom(self): """Checking updating vlarray with float atoms (another byteorder)""" ttypes = { "Float32": numpy.float32, "Float64": numpy.float64, } if "Float16Atom" in globals(): ttypes["float16"] = numpy.float16 if "Float96Atom" in globals(): ttypes["float96"] = numpy.float96 if "Float128Atom" in globals(): ttypes["float128"] = numpy.float128 if common.verbose: print('\n', '-=' * 30) print("Running %s.test04d_FloatAtom..." % self.__class__.__name__) byteorder = {'little': 'big', 'big': 'little'}[sys.byteorder] for atype in ttypes: vlarray = self.fileh.create_vlarray( '/', atype, atom=Atom.from_sctype(ttypes[atype]), byteorder=byteorder) a0 = numpy.array([1.3, 2.2, 3.3], dtype=atype) vlarray.append(a0) a1 = numpy.array([-1, 0], dtype=atype) vlarray.append(a1) # Modify rows a0 = numpy.array([4.3, 2.2, 4.3], dtype=atype) a0 = a0.byteswap() a0 = a0.newbyteorder() vlarray[0] = a0 a1 = numpy.array([-1.1e34, 1.3e-32], dtype=atype) a1 = a1.byteswap() a1 = a1.newbyteorder() vlarray[1] = a1 if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "a") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing type:", atype) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.byteorder, byteorder) self.assertTrue(byteorders[row[0].dtype.byteorder], sys.byteorder) self.assertEqual(vlarray.nrows, 2) self.assertTrue(allequal(row[0], numpy.array([4.3, 2.2, 4.3], dtype=ttypes[atype]))) self.assertTrue(allequal(row[1], numpy.array([-1.1e34, 1.3e-32], dtype=ttypes[atype]))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test04_ComplexAtom(self): """Checking vlarray with numerical complex atoms.""" ttypes = [ "Complex32", "Complex64", ] if "Complex192Atom" in globals(): ttypes.append("Complex96") if "Complex256Atom" in globals(): ttypes.append("Complex128") if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_ComplexAtom..." % self.__class__.__name__) for atype in ttypes: vlarray = self.fileh.create_vlarray( '/', atype, atom=Atom.from_sctype(atype)) vlarray.append([(1.3 + 0j), (0+2.2j), (3.3+3.3j)]) vlarray.append([(0-1.3e34j), (1.e-32 + 0j)]) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "a") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing type:", atype) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertTrue( allequal(row[0], numpy.array([(1.3 + 0j), (0+2.2j), (3.3+3.3j)], atype))) self.assertTrue( allequal(row[1], numpy.array([(0-1.3e34j), (1.e-32 + 0j)], atype))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test04b_ComplexAtom(self): """Checking modifying vlarray with numerical complex atoms.""" ttypes = [ "Complex32", "Complex64", ] if "Complex192Atom" in globals(): ttypes.append("Complex96") if "Complex256Atom" in globals(): ttypes.append("Complex128") if common.verbose: print('\n', '-=' * 30) print("Running %s.test04b_ComplexAtom..." % self.__class__.__name__) for atype in ttypes: vlarray = self.fileh.create_vlarray( '/', atype, atom=Atom.from_sctype(atype)) vlarray.append([(1.3 + 0j), (0+2.2j), (3.3+3.3j)]) vlarray.append([(0-1.3e34j), (1.e-32 + 0j)]) # Modify the rows vlarray[0] = ((1.4 + 0j), (0+4.2j), (3.3+4.3j)) vlarray[1] = ((4-1.3e34j), (1.e-32 + 4j)) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "a") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing type:", atype) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertTrue( allequal(row[0], numpy.array([(1.4 + 0j), (0+4.2j), (3.3+4.3j)], atype))) self.assertTrue( allequal(row[1], numpy.array([(4-1.3e34j), (1.e-32 + 4j)], atype))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 2) def test05_VLStringAtom(self): """Checking vlarray with variable length strings.""" # Skip the test if the default encoding has been mangled. if sys.getdefaultencoding() != 'ascii': return if common.verbose: print('\n', '-=' * 30) print("Running %s.test05_VLStringAtom..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray( '/', "VLStringAtom", atom=VLStringAtom()) vlarray.append("asd") vlarray.append("asd\xe4") vlarray.append(u"aaana") vlarray.append("") # Check for ticket #62. self.assertRaises(TypeError, vlarray.append, ["foo", "bar"]) # `VLStringAtom` makes no encoding assumptions. See ticket #51. self.assertRaises(UnicodeEncodeError, vlarray.append, u"asd\xe4") if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 4) self.assertEqual(row[0], "asd") self.assertEqual(row[1], "asd\xe4") self.assertEqual(row[2], "aaana") self.assertEqual(row[3], "") self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 4) self.assertEqual(len(row[2]), 5) self.assertEqual(len(row[3]), 0) def test05b_VLStringAtom(self): """Checking updating vlarray with variable length strings.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05b_VLStringAtom..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray( '/', "VLStringAtom", atom=VLStringAtom()) vlarray.append("asd") vlarray.append(u"aaana") # Modify values vlarray[0] = "as4" vlarray[1] = "aaanc" self.assertRaises(ValueError, vlarray.__setitem__, 1, "shrt") self.assertRaises(ValueError, vlarray.__setitem__, 1, "toolong") if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", repr(row[0])) print("Second row in vlarray ==>", repr(row[1])) self.assertEqual(vlarray.nrows, 2) self.assertEqual(row[0], b"as4") self.assertEqual(row[1], b"aaanc") self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 5) def test06a_Object(self): """Checking vlarray with object atoms.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test06a_Object..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray( '/', "Object", atom=ObjectAtom()) vlarray.append([[1, 2, 3], "aaa", u"aaaççç"]) vlarray.append([3, 4, C()]) vlarray.append(42) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 3) self.assertEqual(row[0], [[1, 2, 3], "aaa", u"aaaççç"]) list1 = list(row[1]) obj = list1.pop() self.assertEqual(list1, [3, 4]) self.assertEqual(obj.c, C().c) self.assertEqual(row[2], 42) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 3) self.assertRaises(TypeError, len, row[2]) def test06b_Object(self): """Checking updating vlarray with object atoms.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test06b_Object..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray('/', "Object", atom=ObjectAtom()) # When updating an object, this seems to change the number # of bytes that pickle.dumps generates # vlarray.append(([1,2,3], "aaa", u"aaaççç")) vlarray.append(([1, 2, 3], "aaa", u"çç4")) # vlarray.append([3,4, C()]) vlarray.append([3, 4, [24]]) # Modify the rows # vlarray[0] = ([1,2,4], "aa4", u"aaaçç4") vlarray[0] = ([1, 2, 4], "aa4", u"çç5") # vlarray[1] = (3,4, C()) vlarray[1] = [4, 4, [24]] if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 2) self.assertEqual(row[0], ([1, 2, 4], "aa4", u"çç5")) list1 = list(row[1]) obj = list1.pop() self.assertEqual(list1, [4, 4]) # self.assertEqual(obj.c, C().c) self.assertEqual(obj, [24]) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 3) def test06c_Object(self): """Checking vlarray with object atoms (numpy arrays as values)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test06c_Object..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray('/', "Object", atom=ObjectAtom()) vlarray.append(numpy.array([[1, 2], [0, 4]], 'i4')) vlarray.append(numpy.array([0, 1, 2, 3], 'i8')) vlarray.append(numpy.array(42, 'i1')) if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 3) self.assertTrue(allequal(row[0], numpy.array([[1, 2], [0, 4]], 'i4'))) self.assertTrue(allequal(row[1], numpy.array([0, 1, 2, 3], 'i8'))) self.assertTrue(allequal(row[2], numpy.array(42, 'i1'))) def test06d_Object(self): """Checking updating vlarray with object atoms (numpy arrays)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test06d_Object..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray('/', "Object", atom=ObjectAtom()) vlarray.append(numpy.array([[1, 2], [0, 4]], 'i4')) vlarray.append(numpy.array([0, 1, 2, 3], 'i8')) vlarray.append(numpy.array(42, 'i1')) # Modify the rows. Since PyTables 2.2.1 we use a binary # pickle for arrays and ObjectAtoms, so the next should take # the same space than the above. vlarray[0] = numpy.array([[1, 0], [0, 4]], 'i4') vlarray[1] = numpy.array([0, 1, 0, 3], 'i8') vlarray[2] = numpy.array(22, 'i1') if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 3) self.assertTrue(allequal(row[0], numpy.array([[1, 0], [0, 4]], 'i4'))) self.assertTrue(allequal(row[1], numpy.array([0, 1, 0, 3], 'i8'))) self.assertTrue(allequal(row[2], numpy.array(22, 'i1'))) def test07_VLUnicodeAtom(self): """Checking vlarray with variable length Unicode strings.""" # Skip the test if the default encoding has been mangled. if sys.getdefaultencoding() != 'ascii': return if common.verbose: print('\n', '-=' * 30) print("Running %s.test07_VLUnicodeAtom..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray( '/', "VLUnicodeAtom", atom=VLUnicodeAtom()) vlarray.append("asd") vlarray.append(u"asd\u0140") vlarray.append(u"aaana") vlarray.append(u"") # Check for ticket #62. self.assertRaises(TypeError, vlarray.append, ["foo", "bar"]) # `VLUnicodeAtom` makes no encoding assumptions. self.assertRaises(UnicodeDecodeError, vlarray.append, "asd\xe4") if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 4) self.assertEqual(row[0], u"asd") self.assertEqual(row[1], u"asd\u0140") self.assertEqual(row[2], u"aaana") self.assertEqual(row[3], u"") self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 4) self.assertEqual(len(row[2]), 5) self.assertEqual(len(row[3]), 0) def test07b_VLUnicodeAtom(self): """Checking updating vlarray with variable length Unicode strings.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test07b_VLUnicodeAtom..." % self.__class__.__name__) vlarray = self.fileh.create_vlarray( '/', "VLUnicodeAtom", atom=VLUnicodeAtom()) vlarray.append("asd") vlarray.append(u"aaan\xe4") # Modify values vlarray[0] = u"as\xe4" vlarray[1] = u"aaan\u0140" self.assertRaises(ValueError, vlarray.__setitem__, 1, "shrt") self.assertRaises(ValueError, vlarray.__setitem__, 1, "toolong") if self.reopen: name = vlarray._v_pathname self.fileh.close() self.fileh = open_file(self.file, "r") vlarray = self.fileh.get_node(name) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", repr(row[0])) print("Second row in vlarray ==>", repr(row[1])) self.assertEqual(vlarray.nrows, 2) self.assertEqual(row[0], u"as\xe4") self.assertEqual(row[1], u"aaan\u0140") self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 5) class TypesReopenTestCase(TypesTestCase): title = "Reopen" reopen = True class TypesNoReopenTestCase(TypesTestCase): title = "No reopen" reopen = False class MDTypesTestCase(unittest.TestCase): mode = "w" compress = 0 complib = "zlib" # Default compression library def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, self.mode) self.rootgroup = self.fileh.root def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test01_StringAtom(self): """Checking vlarray with MD NumPy string atoms.""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_StringAtom..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, 'stringAtom', StringAtom(itemsize=3, shape=(2,)), "Ragged array of strings") vlarray.append([["123", "45"], ["45", "123"]]) vlarray.append([["s", "abc"], ["abc", "f"], ["s", "ab"], ["ab", "f"]]) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, 2) npt.assert_array_equal( row[0], numpy.array([["123", "45"], ["45", "123"]], 'S')) npt.assert_array_equal( row[1], numpy.array([["s", "abc"], ["abc", "f"], ["s", "ab"], ["ab", "f"]], 'S')) self.assertEqual(len(row[0]), 2) self.assertEqual(len(row[1]), 4) def test01b_StringAtom(self): """Checking vlarray with MD NumPy string atoms ('python' flavor)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test01b_StringAtom..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, 'stringAtom', StringAtom(itemsize=3, shape=(2,)), "Ragged array of strings") vlarray.flavor = "python" vlarray.append([["123", "45"], ["45", "123"]]) vlarray.append([["s", "abc"], ["abc", "f"], ["s", "ab"], ["ab", "f"]]) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, 2) self.assertEqual(row[0], [[b"123", b"45"], [b"45", b"123"]]) self.assertEqual(row[1], [[b"s", b"abc"], [b"abc", b"f"], [b"s", b"ab"], [b"ab", b"f"]]) self.assertEqual(len(row[0]), 2) self.assertEqual(len(row[1]), 4) def test01c_StringAtom(self): """Checking vlarray with MD NumPy string atoms (with offset)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test01c_StringAtom..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, 'stringAtom', StringAtom(itemsize=3, shape=(2,)), "Ragged array of strings") vlarray.flavor = "python" a = numpy.array([["a", "b"], ["123", "45"], ["45", "123"]], dtype="S3") vlarray.append(a[1:]) a = numpy.array([["s", "a"], ["ab", "f"], ["s", "abc"], ["abc", "f"], ["s", "ab"], ["ab", "f"]]) vlarray.append(a[2:]) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, 2) self.assertEqual(row[0], [[b"123", b"45"], [b"45", b"123"]]) self.assertEqual(row[1], [[b"s", b"abc"], [b"abc", b"f"], [b"s", b"ab"], [b"ab", b"f"]]) self.assertEqual(len(row[0]), 2) self.assertEqual(len(row[1]), 4) def test01d_StringAtom(self): """Checking vlarray with MD NumPy string atoms (with stride)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test01d_StringAtom..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, 'stringAtom', StringAtom(itemsize=3, shape=(2,)), "Ragged array of strings") vlarray.flavor = "python" a = numpy.array([["a", "b"], ["123", "45"], ["45", "123"]], dtype="S3") vlarray.append(a[1::2]) a = numpy.array([["s", "a"], ["ab", "f"], ["s", "abc"], ["abc", "f"], ["s", "ab"], ["ab", "f"]]) vlarray.append(a[::3]) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, 2) self.assertEqual(row[0], [[b"123", b"45"]]) self.assertEqual(row[1], [[b"s", b"a"], [b"abc", b"f"]]) self.assertEqual(len(row[0]), 1) self.assertEqual(len(row[1]), 2) def test02_BoolAtom(self): """Checking vlarray with MD boolean atoms.""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_BoolAtom..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, 'BoolAtom', BoolAtom(shape=(3,)), "Ragged array of Booleans") vlarray.append([(1, 0, 3), (1, 1, 1), (0, 0, 0)]) vlarray.append([(-1, 0, 0)]) # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, 2) self.assertTrue( allequal(row[0], numpy.array([[1, 0, 1], [1, 1, 1], [0, 0, 0]], dtype='bool'))) self.assertTrue( allequal(row[1], numpy.array([[1, 0, 0]], dtype='bool'))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 1) def test02b_BoolAtom(self): """Checking vlarray with MD boolean atoms (with offset)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test02b_BoolAtom..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, 'BoolAtom', BoolAtom(shape=(3,)), "Ragged array of Booleans") a = numpy.array([(0, 0, 0), (1, 0, 3), ( 1, 1, 1), (0, 0, 0)], dtype='bool') vlarray.append(a[1:]) # Create an offset a = numpy.array([(1, 1, 1), (-1, 0, 0)], dtype='bool') vlarray.append(a[1:]) # Create an offset # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, 2) self.assertTrue( allequal(row[0], numpy.array([[1, 0, 1], [1, 1, 1], [0, 0, 0]], dtype='bool'))) self.assertTrue(allequal(row[ 1], numpy.array([[1, 0, 0]], dtype='bool'))) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 1) def test02c_BoolAtom(self): """Checking vlarray with MD boolean atoms (with strides)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test02c_BoolAtom..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, 'BoolAtom', BoolAtom(shape=(3,)), "Ragged array of Booleans") a = numpy.array([(0, 0, 0), (1, 0, 3), ( 1, 1, 1), (0, 0, 0)], dtype='bool') vlarray.append(a[1::2]) # Create an strided array a = numpy.array([(1, 1, 1), (-1, 0, 0), (0, 0, 0)], dtype='bool') vlarray.append(a[::2]) # Create an strided array # Read all the rows: row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, 2) self.assertTrue( allequal(row[0], numpy.array([[1, 0, 1], [0, 0, 0]], dtype='bool'))) self.assertTrue( allequal(row[1], numpy.array([[1, 1, 1], [0, 0, 0]], dtype='bool'))) self.assertEqual(len(row[0]), 2) self.assertEqual(len(row[1]), 2) def test03_IntAtom(self): """Checking vlarray with MD integer atoms.""" ttypes = ["Int8", "UInt8", "Int16", "UInt16", "Int32", "UInt32", "Int64", #"UInt64", # Unavailable in some platforms ] root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_IntAtom..." % self.__class__.__name__) # Create an string atom for atype in ttypes: vlarray = self.fileh.create_vlarray( root, atype, atom=Atom.from_sctype(atype, (2, 3))) vlarray.append([numpy.ones((2, 3), atype), numpy.zeros((2, 3), atype)]) vlarray.append([numpy.ones((2, 3), atype)*100]) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing type:", atype) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", repr(row[1])) self.assertEqual(vlarray.nrows, 2) self.assertTrue( allequal(row[0], numpy.array([numpy.ones((2, 3)), numpy.zeros((2, 3))], atype))) self.assertTrue( allequal(row[1], numpy.array([numpy.ones((2, 3))*100], atype))) self.assertEqual(len(row[0]), 2) self.assertEqual(len(row[1]), 1) def test04_FloatAtom(self): """Checking vlarray with MD floating point atoms.""" ttypes = [ "Float32", "Float64", "Complex32", "Complex64", ] for name in ("float16", "float96", "float128"): atomname = name.capitalize() + "Atom" if atomname in globals(): ttypes.append(name.capitalize()) for itemsize in (192, 256): atomname = "Complex%dAtom" % itemsize if atomname in globals(): ttypes.append("Complex%d" % (itemsize // 2)) root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_FloatAtom..." % self.__class__.__name__) # Create an string atom for atype in ttypes: vlarray = self.fileh.create_vlarray( root, atype, atom=Atom.from_sctype(atype, (5, 2, 6))) vlarray.append([numpy.ones((5, 2, 6), atype)*1.3, numpy.zeros((5, 2, 6), atype)]) vlarray.append([numpy.ones((5, 2, 6), atype)*2.e4]) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing type:", atype) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, 2) self.assertTrue( allequal(row[0], numpy.array([numpy.ones((5, 2, 6))*1.3, numpy.zeros((5, 2, 6))], atype))) self.assertTrue( allequal(row[1], numpy.array([numpy.ones((5, 2, 6))*2.e4], atype))) self.assertEqual(len(row[0]), 2) self.assertEqual(len(row[1]), 1) class MDTypesNumPyTestCase(MDTypesTestCase): title = "MDTypes" class AppendShapeTestCase(unittest.TestCase): mode = "w" def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, self.mode) self.rootgroup = self.fileh.root def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test00_difinputs(self): """Checking vlarray.append() with different inputs.""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test00_difinputs..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, 'vlarray', Int32Atom(), "Ragged array of ints") vlarray.flavor = "python" # Check different ways to input # All of the next should lead to the same rows vlarray.append((1, 2, 3)) # a tuple vlarray.append([1, 2, 3]) # a unique list vlarray.append(numpy.array([1, 2, 3], dtype='int32')) # and array if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") vlarray = self.fileh.root.vlarray # Read all the vlarray row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 3) self.assertEqual(row[0], [1, 2, 3]) self.assertEqual(row[1], [1, 2, 3]) self.assertEqual(row[2], [1, 2, 3]) def test01_toomanydims(self): """Checking vlarray.append() with too many dimensions.""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_toomanydims..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, 'vlarray', StringAtom(itemsize=3), "Ragged array of strings") # Adding an array with one dimensionality more than allowed try: vlarray.append([["123", "456", "3"]]) except ValueError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next RuntimeError was catched!") print(value) else: self.fail("expected a ValueError") if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") vlarray = self.fileh.root.vlarray # Read all the rows (there should be none) row = vlarray.read() if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) self.assertEqual(vlarray.nrows, 0) def test02_zerodims(self): """Checking vlarray.append() with a zero-dimensional array""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_zerodims..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, 'vlarray', Int32Atom(), "Ragged array of ints") vlarray.append(numpy.zeros(dtype='int32', shape=(6, 0))) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") vlarray = self.fileh.root.vlarray # Read the only row in vlarray row = vlarray.read(0)[0] if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", repr(row)) self.assertEqual(vlarray.nrows, 1) self.assertTrue(allequal(row, numpy.zeros(dtype='int32', shape=(0,)))) self.assertEqual(len(row), 0) def test03a_cast(self): """Checking vlarray.append() with a casted array (upgrading case)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test03a_cast..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, 'vlarray', Int32Atom(), "Ragged array of ints") # This type has to be upgraded vlarray.append(numpy.array([1, 2], dtype='int16')) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") vlarray = self.fileh.root.vlarray # Read the only row in vlarray row = vlarray.read(0)[0] if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", repr(row)) self.assertEqual(vlarray.nrows, 1) self.assertTrue(allequal(row, numpy.array([1, 2], dtype='int32'))) self.assertEqual(len(row), 2) def test03b_cast(self): """Checking vlarray.append() with a casted array (downgrading case)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test03b_cast..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, 'vlarray', Int32Atom(), "Ragged array of ints") # This type has to be downcasted vlarray.append(numpy.array([1, 2], dtype='float64')) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") vlarray = self.fileh.root.vlarray # Read the only row in vlarray row = vlarray.read(0)[0] if common.verbose: print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", repr(row)) self.assertEqual(vlarray.nrows, 1) self.assertTrue(allequal(row, numpy.array([1, 2], dtype='int32'))) self.assertEqual(len(row), 2) class OpenAppendShapeTestCase(AppendShapeTestCase): close = 0 class CloseAppendShapeTestCase(AppendShapeTestCase): close = 1 class FlavorTestCase(unittest.TestCase): mode = "w" compress = 0 complib = "zlib" # Default compression library def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, self.mode) self.rootgroup = self.fileh.root def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #---------------------------------------- def test01a_EmptyVLArray(self): """Checking empty vlarrays with different flavors (closing the file)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_EmptyVLArray..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, "vlarray", Atom.from_kind('int', itemsize=4)) vlarray.flavor = self.flavor self.fileh.close() self.fileh = open_file(self.file, "r") # Read all the rows (it should be empty): vlarray = self.fileh.root.vlarray row = vlarray.read() if common.verbose: print("Testing flavor:", self.flavor) print("Object read:", row, repr(row)) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) # Check that the object read is effectively empty self.assertEqual(vlarray.nrows, 0) self.assertEqual(row, []) def test01b_EmptyVLArray(self): """Checking empty vlarrays with different flavors (no closing file)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_EmptyVLArray..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, "vlarray", Atom.from_kind('int', itemsize=4)) vlarray.flavor = self.flavor # Read all the rows (it should be empty): row = vlarray.read() if common.verbose: print("Testing flavor:", self.flavor) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) # Check that the object read is effectively empty self.assertEqual(vlarray.nrows, 0) self.assertEqual(row, []) def test02_BooleanAtom(self): """Checking vlarray with different flavors (boolean versions)""" root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_BoolAtom..." % self.__class__.__name__) # Create an string atom vlarray = self.fileh.create_vlarray(root, "Bool", BoolAtom()) vlarray.flavor = self.flavor vlarray.append([1, 2, 3]) vlarray.append(()) # Empty row vlarray.append([100, 0]) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing flavor:", self.flavor) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 3) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 0) self.assertEqual(len(row[2]), 2) if self.flavor == "python": arr1 = [1, 1, 1] arr2 = [] arr3 = [1, 0] elif self.flavor == "numpy": arr1 = numpy.array([1, 1, 1], dtype="bool") arr2 = numpy.array([], dtype="bool") arr3 = numpy.array([1, 0], dtype="bool") if self.flavor == "numpy": self.assertTrue(allequal(row[0], arr1, self.flavor)) self.assertTrue(allequal(row[1], arr2, self.flavor)) self.assertTrue(allequal(row[1], arr2, self.flavor)) else: # 'python' flavor self.assertEqual(row[0], arr1) self.assertEqual(row[1], arr2) self.assertEqual(row[2], arr3) def test03_IntAtom(self): """Checking vlarray with different flavors (integer versions)""" ttypes = ["Int8", "UInt8", "Int16", "UInt16", "Int32", "UInt32", "Int64", # Not checked because some platforms does not support it #"UInt64", ] root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_IntAtom..." % self.__class__.__name__) # Create an string atom for atype in ttypes: vlarray = self.fileh.create_vlarray(root, atype, Atom.from_sctype(atype)) vlarray.flavor = self.flavor vlarray.append([1, 2, 3]) vlarray.append(()) vlarray.append([100, 0]) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing flavor:", self.flavor) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 3) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 0) self.assertEqual(len(row[2]), 2) if self.flavor == "python": arr1 = [1, 2, 3] arr2 = [] arr3 = [100, 0] elif self.flavor == "numpy": arr1 = numpy.array([1, 2, 3], dtype=atype) arr2 = numpy.array([], dtype=atype) arr3 = numpy.array([100, 0], dtype=atype) if self.flavor == "numpy": self.assertTrue(allequal(row[0], arr1, self.flavor)) self.assertTrue(allequal(row[1], arr2, self.flavor)) self.assertTrue(allequal(row[2], arr3, self.flavor)) else: # "python" flavor self.assertEqual(row[0], arr1) self.assertEqual(row[1], arr2) self.assertEqual(row[2], arr3) def test03b_IntAtom(self): """Checking vlarray flavors (integer versions and closed file)""" ttypes = ["Int8", "UInt8", "Int16", "UInt16", "Int32", "UInt32", "Int64", # Not checked because some platforms does not support it #"UInt64", ] root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_IntAtom..." % self.__class__.__name__) # Create an string atom for atype in ttypes: vlarray = self.fileh.create_vlarray(root, atype, Atom.from_sctype(atype)) vlarray.flavor = self.flavor vlarray.append([1, 2, 3]) vlarray.append(()) vlarray.append([100, 0]) self.fileh.close() self.fileh = open_file(self.file, "a") # open in "a"ppend mode root = self.fileh.root # Very important! vlarray = self.fileh.get_node(root, str(atype)) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing flavor:", self.flavor) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 3) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 0) self.assertEqual(len(row[2]), 2) if self.flavor == "python": arr1 = [1, 2, 3] arr2 = [] arr3 = [100, 0] elif self.flavor == "numpy": arr1 = numpy.array([1, 2, 3], dtype=atype) arr2 = numpy.array([], dtype=atype) arr3 = numpy.array([100, 0], dtype=atype) if self.flavor == "numpy": self.assertTrue(allequal(row[0], arr1, self.flavor)) self.assertTrue(allequal(row[1], arr2, self.flavor)) self.assertTrue(allequal(row[2], arr3, self.flavor)) else: # Tuple or List flavors self.assertEqual(row[0], arr1) self.assertEqual(row[1], arr2) self.assertEqual(row[2], arr3) def test04_FloatAtom(self): """Checking vlarray with different flavors (floating point versions)""" ttypes = [ "Float32", "Float64", "Complex32", "Complex64", ] for name in ("float16", "float96", "float128"): atomname = name.capitalize() + "Atom" if atomname in globals(): ttypes.append(name.capitalize()) for itemsize in (192, 256): atomname = "Complex%dAtom" % itemsize if atomname in globals(): ttypes.append("Complex%d" % (itemsize // 2)) root = self.rootgroup if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_FloatAtom..." % self.__class__.__name__) # Create an string atom for atype in ttypes: vlarray = self.fileh.create_vlarray(root, atype, Atom.from_sctype(atype)) vlarray.flavor = self.flavor vlarray.append([1.3, 2.2, 3.3]) vlarray.append(()) vlarray.append([-1.3e34, 1.e-32]) # Read all the rows: row = vlarray.read() if common.verbose: print("Testing flavor:", self.flavor) print("Object read:", row) print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) self.assertEqual(vlarray.nrows, 3) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 0) self.assertEqual(len(row[2]), 2) if self.flavor == "python": arr1 = list(numpy.array([1.3, 2.2, 3.3], atype)) arr2 = list(numpy.array([], atype)) arr3 = list(numpy.array([-1.3e34, 1.e-32], atype)) elif self.flavor == "numpy": arr1 = numpy.array([1.3, 2.2, 3.3], dtype=atype) arr2 = numpy.array([], dtype=atype) arr3 = numpy.array([-1.3e34, 1.e-32], dtype=atype) if self.flavor == "numpy": self.assertTrue(allequal(row[0], arr1, self.flavor)) self.assertTrue(allequal(row[1], arr2, self.flavor)) self.assertTrue(allequal(row[2], arr3, self.flavor)) else: # Tuple or List flavors self.assertEqual(row[0], arr1) self.assertEqual(row[1], arr2) self.assertEqual(row[2], arr3) class NumPyFlavorTestCase(FlavorTestCase): flavor = "numpy" class PythonFlavorTestCase(FlavorTestCase): flavor = "python" class ReadRangeTestCase(unittest.TestCase): nrows = 100 mode = "w" compress = 0 complib = "zlib" # Default compression library def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, self.mode) self.rootgroup = self.fileh.root self.populateFile() self.fileh.close() def populateFile(self): group = self.rootgroup filters = Filters(complevel=self.compress, complib=self.complib) vlarray = self.fileh.create_vlarray(group, 'vlarray', Int32Atom(), "ragged array if ints", filters=filters, expectedrows=1000) # Fill it with 100 rows with variable length for i in range(self.nrows): vlarray.append(range(i)) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #------------------------------------------------------------------ def test01_start(self): "Checking reads with only a start value" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_start..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Read some rows: row = [] row.append(vlarray.read(0)[0]) row.append(vlarray.read(10)[0]) row.append(vlarray.read(99)[0]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 0) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 99) self.assertTrue(allequal(row[0], numpy.arange(0, dtype='int32'))) self.assertTrue(allequal(row[1], numpy.arange(10, dtype='int32'))) self.assertTrue(allequal(row[2], numpy.arange(99, dtype='int32'))) def test01b_start(self): "Checking reads with only a start value in a slice" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01b_start..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Read some rows: row = [] row.append(vlarray[0]) row.append(vlarray[10]) row.append(vlarray[99]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 0) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 99) self.assertTrue(allequal(row[0], numpy.arange(0, dtype='int32'))) self.assertTrue(allequal(row[1], numpy.arange(10, dtype='int32'))) self.assertTrue(allequal(row[2], numpy.arange(99, dtype='int32'))) def test01np_start(self): "Checking reads with only a start value in a slice (numpy indexes)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01np_start..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Read some rows: row = [] row.append(vlarray[numpy.int8(0)]) row.append(vlarray[numpy.int32(10)]) row.append(vlarray[numpy.int64(99)]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 0) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 99) self.assertTrue(allequal(row[0], numpy.arange(0, dtype='int32'))) self.assertTrue(allequal(row[1], numpy.arange(10, dtype='int32'))) self.assertTrue(allequal(row[2], numpy.arange(99, dtype='int32'))) def test02_stop(self): "Checking reads with only a stop value" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_stop..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Choose a small value for buffer size vlarray._nrowsinbuf = 3 # Read some rows: row = [] row.append(vlarray.read(stop=1)) row.append(vlarray.read(stop=10)) row.append(vlarray.read(stop=99)) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 1) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 99) self.assertTrue(allequal(row[0][0], numpy.arange(0, dtype='int32'))) for x in range(10): self.assertTrue(allequal(row[1][ x], numpy.arange(x, dtype='int32'))) for x in range(99): self.assertTrue(allequal(row[2][ x], numpy.arange(x, dtype='int32'))) def test02b_stop(self): "Checking reads with only a stop value in a slice" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02b_stop..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Choose a small value for buffer size vlarray._nrowsinbuf = 3 # Read some rows: row = [] row.append(vlarray[:1]) row.append(vlarray[:10]) row.append(vlarray[:99]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 1) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 99) for x in range(1): self.assertTrue(allequal(row[0][ x], numpy.arange(0, dtype='int32'))) for x in range(10): self.assertTrue(allequal(row[1][ x], numpy.arange(x, dtype='int32'))) for x in range(99): self.assertTrue(allequal(row[2][ x], numpy.arange(x, dtype='int32'))) def test03_startstop(self): "Checking reads with a start and stop values" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_startstop..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Choose a small value for buffer size vlarray._nrowsinbuf = 3 # Read some rows: row = [] row.append(vlarray.read(0, 10)) row.append(vlarray.read(5, 15)) row.append(vlarray.read(0, 100)) # read all the array if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 10) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 100) for x in range(0, 10): self.assertTrue(allequal(row[0][ x], numpy.arange(x, dtype='int32'))) for x in range(5, 15): self.assertTrue( allequal(row[1][x-5], numpy.arange(x, dtype='int32'))) for x in range(0, 100): self.assertTrue(allequal(row[2][ x], numpy.arange(x, dtype='int32'))) def test03b_startstop(self): "Checking reads with a start and stop values in slices" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03b_startstop..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Choose a small value for buffer size vlarray._nrowsinbuf = 3 # Read some rows: row = [] row.append(vlarray[0:10]) row.append(vlarray[5:15]) row.append(vlarray[:]) # read all the array if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 10) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 100) for x in range(0, 10): self.assertTrue(allequal(row[0][ x], numpy.arange(x, dtype='int32'))) for x in range(5, 15): self.assertTrue( allequal(row[1][x-5], numpy.arange(x, dtype='int32'))) for x in range(0, 100): self.assertTrue(allequal(row[2][ x], numpy.arange(x, dtype='int32'))) def test04_startstopstep(self): "Checking reads with a start, stop & step values" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_startstopstep..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Choose a small value for buffer size vlarray._nrowsinbuf = 3 # Read some rows: row = [] row.append(vlarray.read(0, 10, 2)) row.append(vlarray.read(5, 15, 3)) row.append(vlarray.read(0, 100, 20)) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 5) self.assertEqual(len(row[1]), 4) self.assertTrue(len(row[2]), 5) for x in range(0, 10, 2): self.assertTrue( allequal(row[0][x//2], numpy.arange(x, dtype='int32'))) for x in range(5, 15, 3): self.assertTrue( allequal(row[1][(x-5)//3], numpy.arange(x, dtype='int32'))) for x in range(0, 100, 20): self.assertTrue( allequal(row[2][x//20], numpy.arange(x, dtype='int32'))) def test04np_startstopstep(self): "Checking reads with a start, stop & step values (numpy indices)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04np_startstopstep..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Choose a small value for buffer size vlarray._nrowsinbuf = 3 # Read some rows: row = [] row.append(vlarray.read(numpy.int8(0), numpy.int8(10), numpy.int8(2))) row.append(vlarray.read(numpy.int8(5), numpy.int8(15), numpy.int8(3))) row.append(vlarray.read(numpy.int8( 0), numpy.int8(100), numpy.int8(20))) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 5) self.assertEqual(len(row[1]), 4) self.assertEqual(len(row[2]), 5) for x in range(0, 10, 2): self.assertTrue( allequal(row[0][x//2], numpy.arange(x, dtype='int32'))) for x in range(5, 15, 3): self.assertTrue( allequal(row[1][(x-5)//3], numpy.arange(x, dtype='int32'))) for x in range(0, 100, 20): self.assertTrue( allequal(row[2][x//20], numpy.arange(x, dtype='int32'))) def test04b_slices(self): "Checking reads with start, stop & step values in slices" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04b_slices..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Choose a small value for buffer size vlarray._nrowsinbuf = 3 # Read some rows: row = [] row.append(vlarray[0:10:2]) row.append(vlarray[5:15:3]) row.append(vlarray[0:100:20]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 5) self.assertEqual(len(row[1]), 4) self.assertEqual(len(row[2]), 5) for x in range(0, 10, 2): self.assertTrue( allequal(row[0][x//2], numpy.arange(x, dtype='int32'))) for x in range(5, 15, 3): self.assertTrue( allequal(row[1][(x-5)//3], numpy.arange(x, dtype='int32'))) for x in range(0, 100, 20): self.assertTrue( allequal(row[2][x//20], numpy.arange(x, dtype='int32'))) def test04bnp_slices(self): """Checking reads with start, stop & step values in slices. (numpy indices) """ if common.verbose: print('\n', '-=' * 30) print("Running %s.test04bnp_slices..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Choose a small value for buffer size vlarray._nrowsinbuf = 3 # Read some rows: row = [] row.append(vlarray[numpy.int16(0):numpy.int16(10):numpy.int32(2)]) row.append(vlarray[numpy.int16(5):numpy.int16(15):numpy.int64(3)]) row.append(vlarray[numpy.uint16(0):numpy.int32(100):numpy.int8(20)]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 5) self.assertEqual(len(row[1]), 4) self.assertEqual(len(row[2]), 5) for x in range(0, 10, 2): self.assertTrue( allequal(row[0][x//2], numpy.arange(x, dtype='int32'))) for x in range(5, 15, 3): self.assertTrue( allequal(row[1][(x-5)//3], numpy.arange(x, dtype='int32'))) for x in range(0, 100, 20): self.assertTrue( allequal(row[2][x//20], numpy.arange(x, dtype='int32'))) def test05_out_of_range(self): "Checking out of range reads" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05_out_of_range..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) try: row = vlarray.read(1000)[0] print("row-->", row) except IndexError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next IndexError was catched!") print(value) self.fileh.close() else: (type, value, traceback) = sys.exc_info() self.fail("expected a IndexError and got:\n%s" % value) class GetItemRangeTestCase(unittest.TestCase): nrows = 100 mode = "w" compress = 0 complib = "zlib" # Default compression library def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, self.mode) self.rootgroup = self.fileh.root self.populateFile() self.fileh.close() def populateFile(self): group = self.rootgroup filters = Filters(complevel=self.compress, complib=self.complib) vlarray = self.fileh.create_vlarray(group, 'vlarray', Int32Atom(), "ragged array if ints", filters=filters, expectedrows=1000) # Fill it with 100 rows with variable length for i in range(self.nrows): vlarray.append(range(i)) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #------------------------------------------------------------------ def test01_start(self): "Checking reads with only a start value" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_start..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Read some rows: row = [] row.append(vlarray[0]) # rank-0 array should work as a regular index (see #303) row.append(vlarray[numpy.array(10)]) row.append(vlarray[99]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 0) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 99) self.assertTrue( allequal(row[0], numpy.arange(0, dtype='int32'))) self.assertTrue( allequal(row[1], numpy.arange(10, dtype='int32'))) self.assertTrue( allequal(row[2], numpy.arange(99, dtype='int32'))) def test01b_start(self): "Checking reads with only a start value in a slice" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01b_start..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Read some rows: row = [] row.append(vlarray[0]) row.append(vlarray[10]) row.append(vlarray[99]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 0) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 99) self.assertTrue(allequal(row[0], numpy.arange(0, dtype='int32'))) self.assertTrue(allequal(row[1], numpy.arange(10, dtype='int32'))) self.assertTrue(allequal(row[2], numpy.arange(99, dtype='int32'))) def test02_stop(self): "Checking reads with only a stop value" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_stop..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Choose a small value for buffer size vlarray._nrowsinbuf = 3 # Read some rows: row = [] row.append(vlarray[:1]) row.append(vlarray[:10]) row.append(vlarray[:99]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("First row in vlarray ==>", row[0]) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 1) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 99) self.assertTrue(allequal(row[0][0], numpy.arange(0, dtype='int32'))) for x in range(10): self.assertTrue(allequal(row[1][ x], numpy.arange(x, dtype='int32'))) for x in range(99): self.assertTrue(allequal(row[2][ x], numpy.arange(x, dtype='int32'))) def test02b_stop(self): "Checking reads with only a stop value in a slice" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02b_stop..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Choose a small value for buffer size vlarray._nrowsinbuf = 3 # Read some rows: row = [] row.append(vlarray[:1]) row.append(vlarray[:10]) row.append(vlarray[:99]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 1) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 99) for x in range(1): self.assertTrue(allequal(row[0][ x], numpy.arange(0, dtype='int32'))) for x in range(10): self.assertTrue(allequal(row[1][ x], numpy.arange(x, dtype='int32'))) for x in range(99): self.assertTrue(allequal(row[2][ x], numpy.arange(x, dtype='int32'))) def test03_startstop(self): "Checking reads with a start and stop values" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_startstop..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Choose a small value for buffer size vlarray._nrowsinbuf = 3 # Read some rows: row = [] row.append(vlarray[0:10]) row.append(vlarray[5:15]) row.append(vlarray[0:100]) # read all the array if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 10) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 100) for x in range(0, 10): self.assertTrue(allequal(row[0][ x], numpy.arange(x, dtype='int32'))) for x in range(5, 15): self.assertTrue( allequal(row[1][x-5], numpy.arange(x, dtype='int32'))) for x in range(0, 100): self.assertTrue(allequal(row[2][ x], numpy.arange(x, dtype='int32'))) def test03b_startstop(self): "Checking reads with a start and stop values in slices" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03b_startstop..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Choose a small value for buffer size vlarray._nrowsinbuf = 3 # Read some rows: row = [] row.append(vlarray[0:10]) row.append(vlarray[5:15]) row.append(vlarray[:]) # read all the array if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 10) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 100) for x in range(0, 10): self.assertTrue(allequal(row[0][ x], numpy.arange(x, dtype='int32'))) for x in range(5, 15): self.assertTrue( allequal(row[1][x-5], numpy.arange(x, dtype='int32'))) for x in range(0, 100): self.assertTrue(allequal(row[2][ x], numpy.arange(x, dtype='int32'))) def test04_slices(self): "Checking reads with a start, stop & step values" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_slices..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Choose a small value for buffer size vlarray._nrowsinbuf = 3 # Read some rows: row = [] row.append(vlarray[0:10:2]) row.append(vlarray[5:15:3]) row.append(vlarray[0:100:20]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 5) self.assertEqual(len(row[1]), 4) self.assertTrue(len(row[2]), 5) for x in range(0, 10, 2): self.assertTrue( allequal(row[0][x//2], numpy.arange(x, dtype='int32'))) for x in range(5, 15, 3): self.assertTrue( allequal(row[1][(x-5)//3], numpy.arange(x, dtype='int32'))) for x in range(0, 100, 20): self.assertTrue( allequal(row[2][x//20], numpy.arange(x, dtype='int32'))) def test04bnp_slices(self): "Checking reads with start, stop & step values (numpy indices)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04np_slices..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray # Choose a small value for buffer size vlarray._nrowsinbuf = 3 # Read some rows: row = [] row.append(vlarray[numpy.int8(0):numpy.int8(10):numpy.int8(2)]) row.append(vlarray[numpy.int8(5):numpy.int8(15):numpy.int8(3)]) row.append(vlarray[numpy.int8(0):numpy.int8(100):numpy.int8(20)]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 5) self.assertEqual(len(row[1]), 4) self.assertEqual(len(row[2]), 5) for x in range(0, 10, 2): self.assertTrue( allequal(row[0][x//2], numpy.arange(x, dtype='int32'))) for x in range(5, 15, 3): self.assertTrue( allequal(row[1][(x-5)//3], numpy.arange(x, dtype='int32'))) for x in range(0, 100, 20): self.assertTrue( allequal(row[2][x//20], numpy.arange(x, dtype='int32'))) def test05_out_of_range(self): "Checking out of range reads" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05_out_of_range..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) try: row = vlarray[1000] print("row-->", row) except IndexError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next IndexError was catched!") print(value) self.fileh.close() else: (type, value, traceback) = sys.exc_info() self.fail("expected a IndexError and got:\n%s" % value) def test05np_out_of_range(self): "Checking out of range reads (numpy indexes)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05np_out_of_range..." % self.__class__.__name__) self.fileh = open_file(self.file, "r") vlarray = self.fileh.root.vlarray if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) try: row = vlarray[numpy.int32(1000)] print("row-->", row) except IndexError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next IndexError was catched!") print(value) self.fileh.close() else: (type, value, traceback) = sys.exc_info() self.fail("expected a IndexError and got:\n%s" % value) class SetRangeTestCase(unittest.TestCase): nrows = 100 mode = "w" compress = 0 complib = "zlib" # Default compression library def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, self.mode) self.rootgroup = self.fileh.root self.populateFile() self.fileh.close() def populateFile(self): group = self.rootgroup filters = Filters(complevel=self.compress, complib=self.complib) vlarray = self.fileh.create_vlarray(group, 'vlarray', Int32Atom(), "ragged array if ints", filters=filters, expectedrows=1000) # Fill it with 100 rows with variable length for i in range(self.nrows): vlarray.append(range(i)) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) #------------------------------------------------------------------ def test01_start(self): "Checking updates that modifies a complete row" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_start..." % self.__class__.__name__) self.fileh = open_file(self.file, "a") vlarray = self.fileh.root.vlarray # Modify some rows: vlarray[0] = vlarray[0]*2 + 3 vlarray[10] = vlarray[10]*2 + 3 vlarray[99] = vlarray[99]*2 + 3 # Read some rows: row = [] row.append(vlarray.read(0)[0]) row.append(vlarray.read(10)[0]) row.append(vlarray.read(99)[0]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 0) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 99) self.assertTrue( allequal(row[0], numpy.arange(0, dtype='int32')*2 + 3)) self.assertTrue( allequal(row[1], numpy.arange(10, dtype='int32')*2 + 3)) self.assertTrue( allequal(row[2], numpy.arange(99, dtype='int32')*2 + 3)) def test01np_start(self): "Checking updates that modifies a complete row" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01np_start..." % self.__class__.__name__) self.fileh = open_file(self.file, "a") vlarray = self.fileh.root.vlarray # Modify some rows: vlarray[numpy.int8(0)] = vlarray[numpy.int16(0)]*2 + 3 vlarray[numpy.int8(10)] = vlarray[numpy.int8(10)]*2 + 3 vlarray[numpy.int32(99)] = vlarray[numpy.int64(99)]*2 + 3 # Read some rows: row = [] row.append(vlarray.read(numpy.int8(0))[0]) row.append(vlarray.read(numpy.int8(10))[0]) row.append(vlarray.read(numpy.int8(99))[0]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 0) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 99) self.assertTrue( allequal(row[0], numpy.arange(0, dtype='int32')*2 + 3)) self.assertTrue( allequal(row[1], numpy.arange(10, dtype='int32')*2 + 3)) self.assertTrue( allequal(row[2], numpy.arange(99, dtype='int32')*2 + 3)) def test02_partial(self): "Checking updates with only a part of a row" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_partial..." % self.__class__.__name__) self.fileh = open_file(self.file, "a") vlarray = self.fileh.root.vlarray # Modify some rows: vlarray[0] = vlarray[0]*2 + 3 vlarray[10] = vlarray[10]*2 + 3 vlarray[96] = vlarray[99][3:]*2 + 3 # Read some rows: row = [] row.append(vlarray.read(0)[0]) row.append(vlarray.read(10)[0]) row.append(vlarray.read(96)[0]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 0) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 96) self.assertTrue( allequal(row[0], numpy.arange(0, dtype='int32')*2 + 3)) self.assertTrue( allequal(row[1], numpy.arange(10, dtype='int32')*2 + 3)) a = numpy.arange(3, 99, dtype='int32') a = a * 2 + 3 self.assertTrue(allequal(row[2], a)) def test03a_several_rows(self): "Checking updating several rows at once (slice style)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03a_several_rows..." % self.__class__.__name__) self.fileh = open_file(self.file, "a") vlarray = self.fileh.root.vlarray # Modify some rows: vlarray[3:6] = (vlarray[3]*2 + 3, vlarray[4]*2 + 3, vlarray[5]*2 + 3) # Read some rows: row = [] row.append(vlarray.read(3)[0]) row.append(vlarray.read(4)[0]) row.append(vlarray.read(5)[0]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 3) self.assertEqual(len(row[1]), 4) self.assertEqual(len(row[2]), 5) self.assertTrue(allequal(row[0], numpy.arange(3, dtype='int32')*2 + 3)) self.assertTrue(allequal(row[1], numpy.arange(4, dtype='int32')*2 + 3)) self.assertTrue(allequal(row[2], numpy.arange(5, dtype='int32')*2 + 3)) def test03b_several_rows(self): "Checking updating several rows at once (list style)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03b_several_rows..." % self.__class__.__name__) self.fileh = open_file(self.file, "a") vlarray = self.fileh.root.vlarray # Modify some rows: vlarray[[0, 10, 96]] = (vlarray[0]*2 + 3, vlarray[10]*2 + 3, vlarray[96]*2 + 3) # Read some rows: row = [] row.append(vlarray.read(0)[0]) row.append(vlarray.read(10)[0]) row.append(vlarray.read(96)[0]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 0) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 96) self.assertTrue( allequal(row[0], numpy.arange(0, dtype='int32')*2 + 3)) self.assertTrue( allequal(row[1], numpy.arange(10, dtype='int32')*2 + 3)) self.assertTrue( allequal(row[2], numpy.arange(96, dtype='int32')*2 + 3)) def test03c_several_rows(self): "Checking updating several rows at once (NumPy's where style)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03c_several_rows..." % self.__class__.__name__) self.fileh = open_file(self.file, "a") vlarray = self.fileh.root.vlarray # Modify some rows: vlarray[(numpy.array([0, 10, 96]),)] = (vlarray[0]*2 + 3, vlarray[10]*2 + 3, vlarray[96]*2 + 3) # Read some rows: row = [] row.append(vlarray.read(0)[0]) row.append(vlarray.read(10)[0]) row.append(vlarray.read(96)[0]) if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) print("Second row in vlarray ==>", row[1]) self.assertEqual(vlarray.nrows, self.nrows) self.assertEqual(len(row[0]), 0) self.assertEqual(len(row[1]), 10) self.assertEqual(len(row[2]), 96) self.assertTrue( allequal(row[0], numpy.arange(0, dtype='int32')*2 + 3)) self.assertTrue( allequal(row[1], numpy.arange(10, dtype='int32')*2 + 3)) self.assertTrue( allequal(row[2], numpy.arange(96, dtype='int32')*2 + 3)) def test04_out_of_range(self): "Checking out of range updates (first index)" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_out_of_range..." % self.__class__.__name__) self.fileh = open_file(self.file, "a") vlarray = self.fileh.root.vlarray if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) try: vlarray[1000] = [1] except IndexError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next IndexError was catched!") print(value) self.fileh.close() else: (type, value, traceback) = sys.exc_info() self.fail("expected a IndexError and got:\n%s" % value) def test05_value_error(self): "Checking out value errors" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05_value_error..." % self.__class__.__name__) self.fileh = open_file(self.file, "a") vlarray = self.fileh.root.vlarray if common.verbose: print("Nrows in", vlarray._v_pathname, ":", vlarray.nrows) try: vlarray[10] = [1]*100 print("row-->", row) except ValueError: if common.verbose: (type, value, traceback) = sys.exc_info() print("\nGreat!, the next ValueError was catched!") print(value) self.fileh.close() else: (type, value, traceback) = sys.exc_info() self.fail("expected a ValueError and got:\n%s" % value) class CopyTestCase(unittest.TestCase): close = True def test01a_copy(self): """Checking VLArray.copy() method.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01a_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an Vlarray arr = Int16Atom(shape=2) array1 = fileh.create_vlarray( fileh.root, 'array1', arr, "title array1") array1.flavor = "python" array1.append([[2, 3]]) array1.append(()) # an empty row array1.append([[3, 457], [2, 4]]) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another location array2 = array1.copy('/', 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("array1-->", repr(array1)) print("array2-->", repr(array2)) print("array1[:]-->", repr(array1.read())) print("array2[:]-->", repr(array2.read())) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal self.assertEqual(array1.read(), array2.read()) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.flavor, array2.flavor) self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(repr(array1.atom), repr(array2.atom)) self.assertEqual(array1.title, array2.title) # Close the file fileh.close() os.remove(file) def test01b_copy(self): """Checking VLArray.copy() method. Pseudo-atom case. """ if common.verbose: print('\n', '-=' * 30) print("Running %s.test01b_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an Vlarray arr = VLStringAtom() array1 = fileh.create_vlarray( fileh.root, 'array1', arr, "title array1") array1.flavor = "python" array1.append("a string") array1.append("") # an empty row array1.append("another string") if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another location array2 = array1.copy('/', 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("array1-->", repr(array1)) print("array2-->", repr(array2)) print("array1[:]-->", repr(array1.read())) print("array2[:]-->", repr(array2.read())) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal self.assertEqual(array1.read(), array2.read()) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.flavor, array2.flavor) self.assertEqual(array1.atom.type, array2.atom.type) self.assertEqual(repr(array1.atom), repr(array2.atom)) self.assertEqual(array1.title, array2.title) # Close the file fileh.close() os.remove(file) def test02_copy(self): """Checking VLArray.copy() method (where specified)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test02_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an VLArray arr = Int16Atom(shape=2) array1 = fileh.create_vlarray( fileh.root, 'array1', arr, "title array1") array1.flavor = "python" array1.append([[2, 3]]) array1.append(()) # an empty row array1.append([[3, 457], [2, 4]]) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy to another location group1 = fileh.create_group("/", "group1") array2 = array1.copy(group1, 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.group1.array2 if common.verbose: print("array1-->", repr(array1)) print("array2-->", repr(array2)) print("array1-->", array1.read()) print("array2-->", array2.read()) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Check that all the elements are equal self.assertEqual(array1.read(), array2.read()) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.flavor, array2.flavor) self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(repr(array1.atom), repr(array1.atom)) self.assertEqual(array1.title, array2.title) # Close the file fileh.close() os.remove(file) def test03_copy(self): """Checking VLArray.copy() method ('python' flavor)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test03_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an VLArray atom = Int16Atom(shape=2) array1 = fileh.create_vlarray(fileh.root, 'array1', atom, title="title array1") array1.flavor = "python" array1.append(((2, 3),)) array1.append(()) # an empty row array1.append(((3, 457), (2, 4))) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy to another location array2 = array1.copy('/', 'array2') if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Assert other properties in array self.assertEqual(array1.nrows, array2.nrows) self.assertEqual(array1.shape, array2.shape) self.assertEqual(array1.flavor, array2.flavor) # Very important here self.assertEqual(array1.atom.dtype, array2.atom.dtype) self.assertEqual(repr(array1.atom), repr(array1.atom)) self.assertEqual(array1.title, array2.title) # Close the file fileh.close() os.remove(file) def test04_copy(self): """Checking VLArray.copy() method (checking title copying)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test04_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an VLArray atom = Int16Atom(shape=2) array1 = fileh.create_vlarray(fileh.root, 'array1', atom=atom, title="title array1") array1.append(((2, 3),)) array1.append(()) # an empty row array1.append(((3, 457), (2, 4))) # Append some user attrs array1.attrs.attr1 = "attr1" array1.attrs.attr2 = 2 if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another Array array2 = array1.copy('/', 'array2', title="title array2") if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 # Assert user attributes if common.verbose: print("title of destination array-->", array2.title) self.assertEqual(array2.title, "title array2") # Close the file fileh.close() os.remove(file) def test05_copy(self): """Checking VLArray.copy() method (user attributes copied)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an Array atom = Int16Atom(shape=2) array1 = fileh.create_vlarray(fileh.root, 'array1', atom=atom, title="title array1") array1.append(((2, 3),)) array1.append(()) # an empty row array1.append(((3, 457), (2, 4))) # Append some user attrs array1.attrs.attr1 = "attr1" array1.attrs.attr2 = 2 if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another Array array2 = array1.copy('/', 'array2', copyuserattrs=1) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Assert user attributes self.assertEqual(array2.attrs.attr1, "attr1") self.assertEqual(array2.attrs.attr2, 2) # Close the file fileh.close() os.remove(file) def notest05b_copy(self): """Checking VLArray.copy() method (user attributes not copied)""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test05b_copy..." % self.__class__.__name__) # Create an instance of an HDF5 Table file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an VLArray atom = Int16Atom(shape=2) array1 = fileh.create_vlarray(fileh.root, 'array1', atom=atom, title="title array1") array1.append(((2, 3),)) array1.append(()) # an empty row array1.append(((3, 457), (2, 4))) # Append some user attrs array1.attrs.attr1 = "attr1" array1.attrs.attr2 = 2 if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy it to another Array array2 = array1.copy('/', 'array2', copyuserattrs=0) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="r") array1 = fileh.root.array1 array2 = fileh.root.array2 if common.verbose: print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) # Assert user attributes self.assertEqual(array2.attrs.attr1, None) self.assertEqual(array2.attrs.attr2, None) # Close the file fileh.close() os.remove(file) class CloseCopyTestCase(CopyTestCase): close = 1 class OpenCopyTestCase(CopyTestCase): close = 0 class CopyIndexTestCase(unittest.TestCase): def test01_index(self): """Checking VLArray.copy() method with indexes.""" if common.verbose: print('\n', '-=' * 30) print("Running %s.test01_index..." % self.__class__.__name__) # Create an instance of an HDF5 Array file = tempfile.mktemp(".h5") fileh = open_file(file, "w") # Create an VLArray atom = Int32Atom(shape=(2,)) array1 = fileh.create_vlarray(fileh.root, 'array1', atom, "t array1") array1.flavor = "python" # The next creates 20 rows of variable length r = [] for row in range(20): r.append([[row, row + 1]]) array1.append([row, row + 1]) if self.close: if common.verbose: print("(closing file version)") fileh.close() fileh = open_file(file, mode="a") array1 = fileh.root.array1 # Copy to another array array2 = array1.copy("/", 'array2', start=self.start, stop=self.stop, step=self.step) r2 = r[self.start:self.stop:self.step] if common.verbose: print("r2-->", r2) print("array2-->", array2[:]) print("attrs array1-->", repr(array1.attrs)) print("attrs array2-->", repr(array2.attrs)) print("nrows in array2-->", array2.nrows) print("and it should be-->", len(r2)) # Check that all the elements are equal self.assertEqual(r2, array2[:]) # Assert the number of rows in array self.assertEqual(len(r2), array2.nrows) # Close the file fileh.close() os.remove(file) class CopyIndex1TestCase(CopyIndexTestCase): close = 0 start = 0 stop = 7 step = 1 class CopyIndex2TestCase(CopyIndexTestCase): close = 1 start = 0 stop = -1 step = 1 class CopyIndex3TestCase(CopyIndexTestCase): close = 0 start = 1 stop = 7 step = 1 class CopyIndex4TestCase(CopyIndexTestCase): close = 1 start = 0 stop = 6 step = 1 class CopyIndex5TestCase(CopyIndexTestCase): close = 0 start = 3 stop = 7 step = 1 class CopyIndex6TestCase(CopyIndexTestCase): close = 1 start = 3 stop = 6 step = 2 class CopyIndex7TestCase(CopyIndexTestCase): close = 0 start = 0 stop = 7 step = 10 class CopyIndex8TestCase(CopyIndexTestCase): close = 1 start = 6 stop = -1 # Negative values means starting from the end step = 1 class CopyIndex9TestCase(CopyIndexTestCase): close = 0 start = 3 stop = 4 step = 1 class CopyIndex10TestCase(CopyIndexTestCase): close = 1 start = 3 stop = 4 step = 2 class CopyIndex11TestCase(CopyIndexTestCase): close = 0 start = -3 stop = -1 step = 2 class CopyIndex12TestCase(CopyIndexTestCase): close = 1 start = -1 # Should point to the last element stop = None # None should mean the last element (including it) step = 1 class ChunkshapeTestCase(unittest.TestCase): def setUp(self): self.file = tempfile.mktemp('.h5') self.fileh = open_file(self.file, 'w', title='Chunkshape test') atom = Int32Atom(shape=(2,)) self.fileh.create_vlarray('/', 'vlarray', atom=atom, title="t array1", chunkshape=13) def tearDown(self): self.fileh.close() os.remove(self.file) def test00(self): """Test setting the chunkshape in a table (no reopen).""" vla = self.fileh.root.vlarray if common.verbose: print("chunkshape-->", vla.chunkshape) self.assertEqual(vla.chunkshape, (13,)) def test01(self): """Test setting the chunkshape in a table (reopen).""" self.fileh.close() self.fileh = open_file(self.file, 'r') vla = self.fileh.root.vlarray if common.verbose: print("chunkshape-->", vla.chunkshape) self.assertEqual(vla.chunkshape, (13,)) class VLUEndianTestCase(common.PyTablesTestCase): def test(self): """Accessing ``vlunicode`` data of a different endianness.""" h5fname = self._testFilename('vlunicode_endian.h5') h5f = open_file(h5fname) try: bedata = h5f.root.vlunicode_big[0] ledata = h5f.root.vlunicode_little[0] self.assertEqual(bedata, u'para\u0140lel') self.assertEqual(ledata, u'para\u0140lel') finally: h5f.close() class TruncateTestCase(unittest.TestCase): def setUp(self): # Create an instance of an HDF5 Table self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, "w") # Create an VLArray arr = Int16Atom(dflt=3) array1 = self.fileh.create_vlarray( self.fileh.root, 'array1', arr, "title array1") # Add a couple of rows array1.append(numpy.array([456, 2], dtype='Int16')) array1.append(numpy.array([3], dtype='Int16')) def tearDown(self): # Close the file self.fileh.close() os.remove(self.file) common.cleanup(self) def test00_truncate(self): """Checking VLArray.truncate() method (truncating to 0 rows)""" array1 = self.fileh.root.array1 # Truncate to 0 elements array1.truncate(0) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") array1 = self.fileh.root.array1 if common.verbose: print("array1-->", array1.read()) self.assertEqual(array1.nrows, 0) self.assertEqual(array1[:], []) def test01_truncate(self): """Checking VLArray.truncate() method (truncating to 1 rows)""" array1 = self.fileh.root.array1 # Truncate to 1 element array1.truncate(1) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") array1 = self.fileh.root.array1 if common.verbose: print("array1-->", array1.read()) self.assertEqual(array1.nrows, 1) self.assertTrue( allequal(array1[0], numpy.array([456, 2], dtype='Int16'))) def test02_truncate(self): """Checking VLArray.truncate() method (truncating to == self.nrows)""" array1 = self.fileh.root.array1 # Truncate to 2 elements array1.truncate(2) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") array1 = self.fileh.root.array1 if common.verbose: print("array1-->", array1.read()) self.assertEqual(array1.nrows, 2) self.assertTrue( allequal(array1[0], numpy.array([456, 2], dtype='Int16'))) self.assertTrue(allequal(array1[1], numpy.array([3], dtype='Int16'))) def test03_truncate(self): """Checking VLArray.truncate() method (truncating to > self.nrows)""" array1 = self.fileh.root.array1 # Truncate to 4 elements array1.truncate(4) if self.close: if common.verbose: print("(closing file version)") self.fileh.close() self.fileh = open_file(self.file, mode="r") array1 = self.fileh.root.array1 if common.verbose: print("array1-->", array1.read()) self.assertEqual(array1.nrows, 4) # Check the original values self.assertTrue( allequal(array1[0], numpy.array([456, 2], dtype='Int16'))) self.assertTrue(allequal(array1[1], numpy.array([3], dtype='Int16'))) # Check that the added rows are empty self.assertTrue(allequal(array1[2], numpy.array([], dtype='Int16'))) self.assertTrue(allequal(array1[3], numpy.array([], dtype='Int16'))) class TruncateOpenTestCase(TruncateTestCase): close = 0 class TruncateCloseTestCase(TruncateTestCase): close = 1 class PointSelectionTestCase(common.PyTablesTestCase): def setUp(self): # The next are valid selections for both NumPy and PyTables self.working_keyset = [ [], # empty list [2], # single-entry list [0, 2], # list [0, -2], # negative values ([0, 2],), # tuple of list numpy.array([], dtype="i4"), # empty array numpy.array([1], dtype="i4"), # single-entry array numpy.array([True, False, True]), # array of bools ] # The next are invalid selections for VLArrays self.not_working_keyset = [ [1, 2, 100], # coordinate 100 > len(vlarray) ([True, False, True],), # tuple of bools ] # Create an instance of an HDF5 Array self.file = tempfile.mktemp(".h5") self.fileh = fileh = open_file(self.file, "w") # Create a sample array arr1 = numpy.array([5, 6], dtype="i4") arr2 = numpy.array([5, 6, 7], dtype="i4") arr3 = numpy.array([5, 6, 9, 8], dtype="i4") self.nparr = numpy.array([arr1, arr2, arr3], dtype="object") # Create the VLArray self.vlarr = vlarr = fileh.create_vlarray( fileh.root, 'vlarray', Int32Atom()) vlarr.append(arr1) vlarr.append(arr2) vlarr.append(arr3) def tearDown(self): self.fileh.close() os.remove(self.file) common.cleanup(self) def test01a_read(self): """Test for point-selections (read, boolean keys).""" nparr = self.nparr vlarr = self.vlarr for key in self.working_keyset: if common.verbose: print("Selection to test:", repr(key)) a = nparr[key].tolist() b = vlarr[key] # if common.verbose: # print "NumPy selection:", a, type(a) # print "PyTables selection:", b, type(b) self.assertEqual( repr(a), repr(b), "NumPy array and PyTables selections does not match.") def test01b_read(self): """Test for point-selections (not working selections, read).""" vlarr = self.vlarr for key in self.not_working_keyset: if common.verbose: print("Selection to test:", key) self.assertRaises(IndexError, vlarr.__getitem__, key) class SizeInMemoryPropertyTestCase(unittest.TestCase): def setUp(self): self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w") def tearDown(self): self.fileh.close() # Then, delete the file os.remove(self.file) common.cleanup(self) def create_array(self, atom, complevel): filters = Filters(complevel=complevel, complib='blosc') self.array = self.fileh.create_vlarray('/', 'vlarray', atom=atom, filters=filters) def test_zero_length(self): atom = Int32Atom() complevel = 0 self.create_array(atom, complevel) self.assertEqual(self.array.size_in_memory, 0) def int_tests(self, complevel, flavor): atom = Int32Atom() self.create_array(atom, complevel) self.array.flavor = flavor expected_size = 0 for i in xrange(10): row = numpy.arange((i + 1) * 10, dtype='i4') self.array.append(row) expected_size += row.nbytes return expected_size def test_numpy_int_numpy_flavor(self): complevel = 0 flavor = 'numpy' expected_size = self.int_tests(complevel, flavor) self.assertEqual(self.array.size_in_memory, expected_size) # compression will have no effect, since this is uncompressed size def test_numpy_int_numpy_flavor_compressed(self): complevel = 1 flavor = 'numpy' expected_size = self.int_tests(complevel, flavor) self.assertEqual(self.array.size_in_memory, expected_size) # flavor will have no effect on what's stored in HDF5 file def test_numpy_int_python_flavor(self): complevel = 0 flavor = 'python' expected_size = self.int_tests(complevel, flavor) self.assertEqual(self.array.size_in_memory, expected_size) # this relies on knowledge of the implementation, so it's not # a great test def test_object_atom(self): atom = ObjectAtom() complevel = 0 self.create_array(atom, complevel) obj = [1, 2, 3] for i in xrange(10): self.array.append(obj) pickle_array = atom.toarray(obj) expected_size = 10 * pickle_array.nbytes self.assertEqual(self.array.size_in_memory, expected_size) class SizeOnDiskPropertyTestCase(unittest.TestCase): def setUp(self): self.file = tempfile.mktemp(".h5") self.fileh = open_file(self.file, mode="w") def tearDown(self): self.fileh.close() # Then, delete the file os.remove(self.file) common.cleanup(self) def create_array(self, atom, complevel): filters = Filters(complevel=complevel, complib='blosc') self.fileh.create_vlarray('/', 'vlarray', atom, filters=filters) self.array = self.fileh.get_node('/', 'vlarray') def test_not_implemented(self): atom = IntAtom() complevel = 0 self.create_array(atom, complevel) self.assertRaises(NotImplementedError, getattr, self.array, 'size_on_disk') class AccessClosedTestCase(common.TempFileMixin, common.PyTablesTestCase): def setUp(self): super(AccessClosedTestCase, self).setUp() self.array = self.h5file.create_vlarray( self.h5file.root, 'array', atom=StringAtom(8)) self.array.append([str(i) for i in range(5, 5005, 100)]) def test_read(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.array.read) def test_getitem(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.array.__getitem__, 0) def test_setitem(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.array.__setitem__, 0, '0') def test_append(self): self.h5file.close() self.assertRaises(ClosedNodeError, self.array.append, 'xxxxxxxxx') class TestCreateVLArrayArgs(common.TempFileMixin, common.PyTablesTestCase): obj = numpy.array([1, 2, 3]) where = '/' name = 'vlarray' atom = Atom.from_dtype(obj.dtype) title = 'title' filters = None expectedrows = None chunkshape = None byteorder = None createparents = False def test_positional_args_01(self): self.h5file.create_vlarray(self.where, self.name, self.atom, self.title, self.filters, self.expectedrows) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, (0,)) self.assertEqual(ptarr.nrows, 0) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) def test_positional_args_02(self): ptarr = self.h5file.create_vlarray(self.where, self.name, self.atom, self.title, self.filters, self.expectedrows) ptarr.append(self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read()[0] self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, (1,)) self.assertEqual(ptarr[0].shape, self.obj.shape) self.assertEqual(ptarr.nrows, 1) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertTrue(allequal(self.obj, nparr)) def test_positional_args_obj(self): self.h5file.create_vlarray(self.where, self.name, None, self.title, self.filters, self.expectedrows, self.chunkshape, self.byteorder, self.createparents, self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read()[0] self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, (1,)) self.assertEqual(ptarr[0].shape, self.obj.shape) self.assertEqual(ptarr.nrows, 1) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj(self): self.h5file.create_vlarray(self.where, self.name, title=self.title, obj=self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read()[0] self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, (1,)) self.assertEqual(ptarr[0].shape, self.obj.shape) self.assertEqual(ptarr.nrows, 1) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_atom_01(self): ptarr = self.h5file.create_vlarray(self.where, self.name, title=self.title, atom=self.atom) ptarr.append(self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read()[0] self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, (1,)) self.assertEqual(ptarr[0].shape, self.obj.shape) self.assertEqual(ptarr.nrows, 1) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_atom_02(self): ptarr = self.h5file.create_vlarray(self.where, self.name, title=self.title, atom=self.atom) #ptarr.append(self.obj) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, (0,)) self.assertEqual(ptarr.nrows, 0) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) def test_kwargs_obj_atom(self): ptarr = self.h5file.create_vlarray(self.where, self.name, title=self.title, obj=self.obj, atom=self.atom) self.h5file.close() self.h5file = open_file(self.h5fname) ptarr = self.h5file.get_node(self.where, self.name) nparr = ptarr.read()[0] self.assertEqual(ptarr.title, self.title) self.assertEqual(ptarr.shape, (1,)) self.assertEqual(ptarr[0].shape, self.obj.shape) self.assertEqual(ptarr.nrows, 1) self.assertEqual(ptarr.atom, self.atom) self.assertEqual(ptarr.atom.dtype, self.atom.dtype) self.assertTrue(allequal(self.obj, nparr)) def test_kwargs_obj_atom_error(self): atom = Atom.from_dtype(numpy.dtype('complex')) #shape = self.shape + self.shape self.assertRaises(TypeError, self.h5file.create_vlarray, self.where, self.name, title=self.title, obj=self.obj, atom=atom) #---------------------------------------------------------------------- def suite(): theSuite = unittest.TestSuite() niter = 1 for n in range(niter): theSuite.addTest(unittest.makeSuite(BasicNumPyTestCase)) theSuite.addTest(unittest.makeSuite(BasicPythonTestCase)) theSuite.addTest(unittest.makeSuite(ZlibComprTestCase)) theSuite.addTest(unittest.makeSuite(BloscComprTestCase)) theSuite.addTest(unittest.makeSuite(BloscShuffleComprTestCase)) theSuite.addTest(unittest.makeSuite(BloscBloscLZComprTestCase)) if 'lz4' in tables.blosc_compressor_list(): theSuite.addTest(unittest.makeSuite(BloscLZ4ComprTestCase)) theSuite.addTest(unittest.makeSuite(BloscLZ4HCComprTestCase)) if 'snappy' in tables.blosc_compressor_list(): theSuite.addTest(unittest.makeSuite(BloscSnappyComprTestCase)) if 'zlib' in tables.blosc_compressor_list(): theSuite.addTest(unittest.makeSuite(BloscZlibComprTestCase)) theSuite.addTest(unittest.makeSuite(LZOComprTestCase)) theSuite.addTest(unittest.makeSuite(Bzip2ComprTestCase)) theSuite.addTest(unittest.makeSuite(TypesReopenTestCase)) theSuite.addTest(unittest.makeSuite(TypesNoReopenTestCase)) theSuite.addTest(unittest.makeSuite(MDTypesNumPyTestCase)) theSuite.addTest(unittest.makeSuite(OpenAppendShapeTestCase)) theSuite.addTest(unittest.makeSuite(CloseAppendShapeTestCase)) theSuite.addTest(unittest.makeSuite(PythonFlavorTestCase)) theSuite.addTest(unittest.makeSuite(NumPyFlavorTestCase)) theSuite.addTest(unittest.makeSuite(ReadRangeTestCase)) theSuite.addTest(unittest.makeSuite(GetItemRangeTestCase)) theSuite.addTest(unittest.makeSuite(SetRangeTestCase)) theSuite.addTest(unittest.makeSuite(ShuffleComprTestCase)) theSuite.addTest(unittest.makeSuite(Fletcher32TestCase)) theSuite.addTest(unittest.makeSuite(AllFiltersTestCase)) theSuite.addTest(unittest.makeSuite(CloseCopyTestCase)) theSuite.addTest(unittest.makeSuite(OpenCopyTestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex1TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex2TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex3TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex4TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex5TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex6TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex7TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex8TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex9TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex10TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex11TestCase)) theSuite.addTest(unittest.makeSuite(CopyIndex12TestCase)) theSuite.addTest(unittest.makeSuite(ChunkshapeTestCase)) theSuite.addTest(unittest.makeSuite(VLUEndianTestCase)) theSuite.addTest(unittest.makeSuite(TruncateOpenTestCase)) theSuite.addTest(unittest.makeSuite(TruncateCloseTestCase)) theSuite.addTest(unittest.makeSuite(PointSelectionTestCase)) theSuite.addTest(unittest.makeSuite(SizeInMemoryPropertyTestCase)) theSuite.addTest(unittest.makeSuite(SizeOnDiskPropertyTestCase)) theSuite.addTest(unittest.makeSuite(AccessClosedTestCase)) theSuite.addTest(unittest.makeSuite(TestCreateVLArrayArgs)) return theSuite if __name__ == '__main__': unittest.main(defaultTest='suite') PyTables-v.3.1.1/tables/tests/time-table-vlarray-1_x.h5000066400000000000000000000072661231437614300225620ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ°ÿÿÿÿÿÿÿÿ €`HEAP0€tablevlarray4vlarray8ÐTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà  Hà¨&t32col t64col@( ˆÿÿÿÿÿÿÿÿ¦SNODÐ( 8 0 ° €ÿÿÿÿÿÿÿÿW2ÇE (CLASSVLARRAY 0FLAVOR numarray 0@0€ÿÿÿÿÿÿÿÿW2ÇE (CLASSVLARRAY 0FLAVOR numarray€` @TITLETest for creating time leaves (CLASSGROUP (VERSION1.0 ØFILTERS²ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I00 sS'complevel' p7 I0 sS'fletcher32' p8 I00 sS'complib' p9 S'zlib' p10 sb. 8PYTABLES_FORMAT_VERSION1.6ÿÿÿÿÿÿÿÿW2ÇE (CLASSTABLE (VERSION2.6 (TITLE 0 NROWS@ 0 FIELD_0_NAMEt32col 0 FIELD_1_NAMEt64col 0FLAVOR numarray 8 AUTOMATIC_INDEX  0 REINDEX  àFILTERS_INDEX²ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I01 sS'complevel' p7 I1 sS'fletcher32' p8 I00 sS'complib' p9 S'zlib' p10 sb. 8 FIELD_0_FILL  @ FIELD_1_FILL ?@4 4ÿÿÿÿÿÿÿÿÿ (VERSION1.2 (TITLEÿÿÿÿÿÿÿÿ (VERSION1.2 (TITLE PyTables-v.3.1.1/tables/tests/times-nested-be.h5000066400000000000000000000542221231437614300213530ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿŒXÿÿÿÿÿÿÿÿ €`HEAP €tblearr32earr64àTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàntegersiisseeds must be in range(0, 256)Niÿÿÿii(R=RRxRZR/RSR*R$R ttRRR‚RR(RRRxRZRƒR$((Rt__whseedÃs<Z* 'cCsÀ|djo|iƒdSnt|ƒ}t|dƒ\}}t|dƒ\}}t|dƒ\}}||dpd}||dpd}||dpd}|i|||ƒdS(sbSeed from hashable obje Xè  nestedt64@t32 , ð(U SNOD",#ÐTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü0U €` (TITLE (CLASSGROUP (VERSION1.0 8PYTABLES_FORMAT_VERSION2.0 (VERSION1.0 (TITLE (VERSION1.0 (TITLEFD‡ª ³FD‡ªFD‡« ³FD‡«FD‡¬ ³FD‡¬FD‡­ ³FD‡­FD‡® ³FD‡®FD‡¯ ³FD‡¯FD‡° ³FD‡°FD‡± ³FD‡±FD‡² ³FD‡²FD‡³ ³FD‡³ ÿÿÿÿÿÿÿÿª‡DF (CLASSTABLE (VERSION2.6 (TITLE 0 NROWS @ 0 FIELD_0_NAMEnested 0 FIELD_1_NAMEt32 8 FIELD_0_FILL   @ FIELD_1_FILL!?@4 4ÿ @x  ÿÿÿÿÿÿÿÿ<$ª‡DF (CLASSEARRAY 0 EXTDIM   ¸x@ ÿÿÿÿÿÿÿÿl,ª‡DF (CLASSEARRAY 0 EXTDIM  TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒ82À3•€òiTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒHFD‡ªFD‡«FD‡¬FD‡­FD‡®FD‡¯FD‡°FD‡±FD‡²FD‡³FD‡ª ³FD‡« ³FD‡¬ ³FD‡­ ³FD‡® ³FD‡¯ ³FD‡° ³FD‡± ³FD‡² ³FD‡³ ³ PyTables-v.3.1.1/tables/tests/vlstr_attr.h5000066400000000000000000000122561231437614300205730ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ¨ÿÿÿÿÿÿÿÿ`ˆ¨ hTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHEAPXÈPˆ  Hvlen_str_scalarˆGCOLvlen_str_scalarvlen_str_array_2vlen_str_array_1vlen_str_array_0vlen_str_matrix_10vlen_str_matrix_11vlen_str_matrix_01vlen_str_matrix_00Ј¨ pvlen_str_arrayˆˆˆ ˆvlen_str_matrixˆˆˆˆ PyTables-v.3.1.1/tables/tests/vlunicode_endian.h5000066400000000000000000002401461231437614300216760ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿ`@ÿÿÿÿÿÿÿÿ €`HEAP0€vlunicode_bigvlunicode_littleÐTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà R›tNRR R R RRR(Rœ((Rt_test+st__main__(DR|twarningstwarnRDttypest MethodTypeR~tBuiltinMethodTypeRtmathtlogR@texpR^tpiReRRstsqrtRctacosRitcosRdtsinRwtosturandomR"tbinasciithexlifyR!t__all__RYRbRnRrR}R‡t_randomRRRR›Rt_instRRR Xè  ÿÿÿÿÿÿÿÿ( øRF (CLASSVLARRAY (VERSION1.3@pSNODÐ0 TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€0 GCOL para@lelÀ€` (TITLE (CLASSGROUP (VERSION1.0 8PYTABLES_FORMAT_VERSION2.0 (TITLE 8 PSEUDOATOM vlunicodeX  ÿÿÿÿÿÿÿÿ@¡¥SF (CLASSVLARRAY (VERSION1.3p¹pTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€`ÀGCOL para@lelÀ (TITLE 8 PSEUDOATOM vlunicodep© PyTables-v.3.1.1/tables/tests/zerodim-attrs-1.3.h5000066400000000000000000000117561231437614300214760ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿèÿÿÿÿÿÿÿÿ €`HEAP€aðTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà (À èß÷B (CLASSARRAY 0FLAVOR NumArray (VERSION2.1SNODЀ` (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS°ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I0 sS'complevel' p7 I0 sS'fletcher32' p8 I0 sS'complib' p9 S'zlib' p10 sb. 8PYTABLES_FORMAT_VERSION1.3 (TITLE   arrscalarxcnumarray.generic ClassicUnpickler p1 (cnumarray.numarraycore NumArray p2 (dp3 S'_type' p4 S'Int32' p5 sS'_bytestride' p6 I4 sS'_itemsize' p7 I4 sS'_shape' p8 (tsS'_version' p9 S'1.4.0' p10 sS'_byteoffset' p11 I0 sS'_byteorder' p12 S'little' p13 sS'_data' p14 cnumarray.memory memory_from_string p15 (S'\x01\x00\x00\x00' tRp16 sS'_strides' p17 (tsS'_flags' p18 I1793 stRp19 . ¨arrdim1†cnumarray.generic ClassicUnpickler p1 (cnumarray.numarraycore NumArray p2 (dp3 S'_type' p4 S'Int32' p5 sS'_bytestride' p6 I4 sS'_itemsize' p7 I4 sS'_shape' p8 (I1 tp9 sS'_version' p10 S'1.4.0' p11 sS'_byteoffset' p12 I0 sS'_byteorder' p13 S'little' p14 sS'_data' p15 cnumarray.memory memory_from_string p16 (S'\x01\x00\x00\x00' tRp17 sS'_strides' p18 (I4 tp19 sS'_flags' p20 I1793 stRp21 . @ pythonscalar  PyTables-v.3.1.1/tables/tests/zerodim-attrs-1.4.h5000066400000000000000000000104161231437614300214670ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ €`HEAP€aðTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà„Zd„Zd„Zdeed„Zded eeed „Z ded „Z ded eeed „Z dedeeed„Z eed„Z ed„Zeeeed„Zeeeeed„Zeed„Zed„Zed„Zed„Zed„Zed„Zed„Zed„Zed„Zeed„Zed„Zed„Zd „Zd!„Zded"„Z dd#„Z!d$„Z"d%„Z#d&„Z$d'„Z%d(„Z&d)„Z'd*„Z(e)d+dƒd,„Z*d-„Z (À sêB (CLASSARRAY 0FLAVOR NumArray (VERSION2.2SNODЀ` (TITLE (CLASSGROUP (VERSION1.0 ÐFILTERS°ccopy_reg _reconstructor p1 (ctables.Leaf Filters p2 c__builtin__ object p3 NtRp4 (dp5 S'shuffle' p6 I0 sS'complevel' p7 I0 sS'fletcher32' p8 I0 sS'complib' p9 S'zlib' p10 sb. 8PYTABLES_FORMAT_VERSION1.4 (TITLE 8 arrscalar  8 arrdim1  8 pythonscalar  PyTables-v.3.1.1/tables/undoredo.py000066400000000000000000000114441231437614300171560ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: February 15, 2005 # Author: Ivan Vilata - reverse:net.selidor@ivan # # $Source$ # $Id$ # ######################################################################## """Support for undoing and redoing actions. Functions: * undo(file, operation, *args) * redo(file, operation, *args) * move_to_shadow(file, path) * move_from_shadow(file, path) * attr_to_shadow(file, path, name) * attr_from_shadow(file, path, name) Misc variables: `__docformat__` The format of documentation strings in this module. """ from tables.path import split_path from tables._past import previous_api __docformat__ = 'reStructuredText' """The format of documentation strings in this module.""" def undo(file_, operation, *args): if operation == 'CREATE': undo_create(file_, args[0]) elif operation == 'REMOVE': undo_remove(file_, args[0]) elif operation == 'MOVE': undo_move(file_, args[0], args[1]) elif operation == 'ADDATTR': undo_add_attr(file_, args[0], args[1]) elif operation == 'DELATTR': undo_del_attr(file_, args[0], args[1]) else: raise NotImplementedError("the requested unknown operation %r can " "not be undone; please report this to the " "authors" % operation) def redo(file_, operation, *args): if operation == 'CREATE': redo_create(file_, args[0]) elif operation == 'REMOVE': redo_remove(file_, args[0]) elif operation == 'MOVE': redo_move(file_, args[0], args[1]) elif operation == 'ADDATTR': redo_add_attr(file_, args[0], args[1]) elif operation == 'DELATTR': redo_del_attr(file_, args[0], args[1]) else: raise NotImplementedError("the requested unknown operation %r can " "not be redone; please report this to the " "authors" % operation) def move_to_shadow(file_, path): node = file_._get_node(path) (shparent, shname) = file_._shadow_name() node._g_move(shparent, shname) moveToShadow = previous_api(move_to_shadow) def move_from_shadow(file_, path): (shparent, shname) = file_._shadow_name() node = shparent._f_get_child(shname) (pname, name) = split_path(path) parent = file_._get_node(pname) node._g_move(parent, name) moveFromShadow = previous_api(move_from_shadow) def undo_create(file_, path): move_to_shadow(file_, path) undoCreate = previous_api(undo_create) def redo_create(file_, path): move_from_shadow(file_, path) redoCreate = previous_api(redo_create) def undo_remove(file_, path): move_from_shadow(file_, path) undoRemove = previous_api(undo_remove) def redo_remove(file_, path): move_to_shadow(file_, path) redoRemove = previous_api(redo_remove) def undo_move(file_, origpath, destpath): (origpname, origname) = split_path(origpath) node = file_._get_node(destpath) origparent = file_._get_node(origpname) node._g_move(origparent, origname) undoMove = previous_api(undo_move) def redo_move(file_, origpath, destpath): (destpname, destname) = split_path(destpath) node = file_._get_node(origpath) destparent = file_._get_node(destpname) node._g_move(destparent, destname) redoMove = previous_api(redo_move) def attr_to_shadow(file_, path, name): node = file_._get_node(path) attrs = node._v_attrs value = getattr(attrs, name) (shparent, shname) = file_._shadow_name() shattrs = shparent._v_attrs # Set the attribute only if it has not been kept in the shadow. # This avoids re-pickling complex attributes on REDO. if not shname in shattrs: shattrs._g__setattr(shname, value) attrs._g__delattr(name) attrToShadow = previous_api(attr_to_shadow) def attr_from_shadow(file_, path, name): (shparent, shname) = file_._shadow_name() shattrs = shparent._v_attrs value = getattr(shattrs, shname) node = file_._get_node(path) node._v_attrs._g__setattr(name, value) # Keeping the attribute in the shadow allows reusing it on Undo/Redo. # shattrs._g__delattr(shname) attrFromShadow = previous_api(attr_from_shadow) def undo_add_attr(file_, path, name): attr_to_shadow(file_, path, name) undoAddAttr = previous_api(undo_add_attr) def redo_add_attr(file_, path, name): attr_from_shadow(file_, path, name) redoAddAttr = previous_api(redo_add_attr) def undo_del_attr(file_, path, name): attr_from_shadow(file_, path, name) undoDelAttr = previous_api(undo_del_attr) def redo_del_attr(file_, path, name): attr_to_shadow(file_, path, name) redoDelAttr = previous_api(redo_del_attr) ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## End: PyTables-v.3.1.1/tables/unimplemented.py000066400000000000000000000130411231437614300202000ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: January 14, 2004 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Here is defined the UnImplemented class.""" import warnings from tables import hdf5extension from tables.utils import SizeType from tables.node import Node from tables.leaf import Leaf from tables._past import previous_api_property class UnImplemented(hdf5extension.UnImplemented, Leaf): """This class represents datasets not supported by PyTables in an HDF5 file. When reading a generic HDF5 file (i.e. one that has not been created with PyTables, but with some other HDF5 library based tool), chances are that the specific combination of datatypes or dataspaces in some dataset might not be supported by PyTables yet. In such a case, this dataset will be mapped into an UnImplemented instance and the user will still be able to access the complete object tree of the generic HDF5 file. The user will also be able to *read and write the attributes* of the dataset, *access some of its metadata*, and perform *certain hierarchy manipulation operations* like deleting or moving (but not copying) the node. Of course, the user will not be able to read the actual data on it. This is an elegant way to allow users to work with generic HDF5 files despite the fact that some of its datasets are not supported by PyTables. However, if you are really interested in having full access to an unimplemented dataset, please get in contact with the developer team. This class does not have any public instance variables or methods, except those inherited from the Leaf class (see :ref:`LeafClassDescr`). """ # Class identifier. _c_classid = 'UNIMPLEMENTED' _c_classId = previous_api_property('_c_classid') def __init__(self, parentnode, name): """Create the `UnImplemented` instance.""" # UnImplemented objects always come from opening an existing node # (they can not be created). self._v_new = False """Is this the first time the node has been created?""" self.nrows = SizeType(0) """The length of the first dimension of the data.""" self.shape = (SizeType(0),) """The shape of the stored data.""" self.byteorder = None """The endianness of data in memory ('big', 'little' or 'irrelevant').""" super(UnImplemented, self).__init__(parentnode, name) def _g_open(self): (self.shape, self.byteorder, object_id) = self._open_unimplemented() try: self.nrows = SizeType(self.shape[0]) except IndexError: self.nrows = SizeType(0) return object_id def _g_copy(self, newparent, newname, recursive, _log=True, **kwargs): """Do nothing. This method does nothing, but a ``UserWarning`` is issued. Please note that this method *does not return a new node*, but ``None``. """ warnings.warn( "UnImplemented node %r does not know how to copy itself; skipping" % (self._v_pathname,)) return None # Can you see it? def _f_copy(self, newparent=None, newname=None, overwrite=False, recursive=False, createparents=False, **kwargs): """Do nothing. This method does nothing, since `UnImplemented` nodes can not be copied. However, a ``UserWarning`` is issued. Please note that this method *does not return a new node*, but ``None``. """ # This also does nothing but warn. self._g_copy(newparent, newname, recursive, **kwargs) return None # Can you see it? def __repr__(self): return """%s NOTE: """ % (str(self), self._v_file.filename) # Classes reported as H5G_UNKNOWN by HDF5 class Unknown(Node): """This class represents nodes reported as *unknown* by the underlying HDF5 library. This class does not have any public instance variables or methods, except those inherited from the Node class. """ # Class identifier _c_classid = 'UNKNOWN' _c_classId = previous_api_property('_c_classid') def __init__(self, parentnode, name): """Create the `Unknown` instance.""" self._v_new = False super(Unknown, self).__init__(parentnode, name) def _g_new(self, parentnode, name, init=False): pass def _g_open(self): return 0 def _g_copy(self, newparent, newname, recursive, _log=True, **kwargs): # Silently avoid doing copies of unknown nodes return None def _g_delete(self, parent): pass def __str__(self): pathname = self._v_pathname classname = self.__class__.__name__ return "%s (%s)" % (pathname, classname) def __repr__(self): return """%s NOTE: """ % (str(self)) # These are listed here for backward compatibility with PyTables 0.9.x indexes class OldIndexArray(UnImplemented): _c_classid = 'IndexArray' _c_classId = previous_api_property('_c_classid') PyTables-v.3.1.1/tables/utils.py000066400000000000000000000357011231437614300165010ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: March 4, 2003 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Utility functions.""" from __future__ import print_function import os import sys import warnings import subprocess from time import time import numpy from tables.flavor import array_of_flavor from tables._past import previous_api # The map between byteorders in NumPy and PyTables byteorders = { '>': 'big', '<': 'little', '=': sys.byteorder, '|': 'irrelevant', } # The type used for size values: indexes, coordinates, dimension # lengths, row numbers, shapes, chunk shapes, byte counts... SizeType = numpy.int64 def correct_byteorder(ptype, byteorder): """Fix the byteorder depending on the PyTables types.""" if ptype in ['string', 'bool', 'int8', 'uint8']: return "irrelevant" else: return byteorder def is_idx(index): """Checks if an object can work as an index or not.""" if type(index) in (int, long): return True elif hasattr(index, "__index__"): # Only works on Python 2.5 (PEP 357) # Exclude the array([idx]) as working as an index. Fixes #303. if (hasattr(index, "shape") and index.shape != ()): return False try: index.__index__() if isinstance(index, bool): warnings.warn( 'using a boolean instead of an integer will result in an ' 'error in the future', DeprecationWarning, stacklevel=2) return True except TypeError: return False elif isinstance(index, numpy.integer): return True # For Python 2.4 one should test 0-dim and 1-dim, 1-elem arrays as well elif (isinstance(index, numpy.ndarray) and (index.shape == ()) and index.dtype.str[1] == 'i'): return True return False def idx2long(index): """Convert a possible index into a long int.""" try: return long(index) except: raise TypeError("not an integer type.") # This is used in VLArray and EArray to produce NumPy object compliant # with atom from a generic python type. If copy is stated as True, it # is assured that it will return a copy of the object and never the same # object or a new one sharing the same memory. def convert_to_np_atom(arr, atom, copy=False): """Convert a generic object into a NumPy object compliant with atom.""" # First, convert the object into a NumPy array nparr = array_of_flavor(arr, 'numpy') # Copy of data if necessary for getting a contiguous buffer, or if # dtype is not the correct one. if atom.shape == (): # Scalar atom case nparr = numpy.array(nparr, dtype=atom.dtype, copy=copy) else: # Multidimensional atom case. Addresses #133. # We need to use this strange way to obtain a dtype compliant # array because NumPy doesn't honor the shape of the dtype when # it is multidimensional. See: # http://scipy.org/scipy/numpy/ticket/926 # for details. # All of this is done just to taking advantage of the NumPy # broadcasting rules. newshape = nparr.shape[:-len(atom.dtype.shape)] nparr2 = numpy.empty(newshape, dtype=[('', atom.dtype)]) nparr2['f0'][:] = nparr # Return a view (i.e. get rid of the record type) nparr = nparr2.view(atom.dtype) return nparr convertToNPAtom = previous_api(convert_to_np_atom) # The next is used in Array, EArray and VLArray, and it is a bit more # high level than convert_to_np_atom def convert_to_np_atom2(object, atom): """Convert a generic object into a NumPy object compliant with atom.""" # Check whether the object needs to be copied to make the operation # safe to in-place conversion. copy = atom.type in ['time64'] nparr = convert_to_np_atom(object, atom, copy) # Finally, check the byteorder and change it if needed byteorder = byteorders[nparr.dtype.byteorder] if (byteorder in ['little', 'big'] and byteorder != sys.byteorder): # The byteorder needs to be fixed (a copy is made # so that the original array is not modified) nparr = nparr.byteswap() return nparr convertToNPAtom2 = previous_api(convert_to_np_atom2) def check_file_access(filename, mode='r'): """Check for file access in the specified `mode`. `mode` is one of the modes supported by `File` objects. If the file indicated by `filename` can be accessed using that `mode`, the function ends successfully. Else, an ``IOError`` is raised explaining the reason of the failure. All this paraphernalia is used to avoid the lengthy and scaring HDF5 messages produced when there are problems opening a file. No changes are ever made to the file system. """ if mode == 'r': # The file should be readable. if not os.access(filename, os.F_OK): raise IOError("``%s`` does not exist" % (filename,)) if not os.path.isfile(filename): raise IOError("``%s`` is not a regular file" % (filename,)) if not os.access(filename, os.R_OK): raise IOError("file ``%s`` exists but it can not be read" % (filename,)) elif mode == 'w': if os.access(filename, os.F_OK): # Since the file is not removed but replaced, # it must already be accessible to read and write operations. check_file_access(filename, 'r+') else: # A new file is going to be created, # so the directory should be writable. parentname = os.path.dirname(filename) if not parentname: parentname = '.' if not os.access(parentname, os.F_OK): raise IOError("``%s`` does not exist" % (parentname,)) if not os.path.isdir(parentname): raise IOError("``%s`` is not a directory" % (parentname,)) if not os.access(parentname, os.W_OK): raise IOError("directory ``%s`` exists but it can not be " "written" % (parentname,)) elif mode == 'a': if os.access(filename, os.F_OK): check_file_access(filename, 'r+') else: check_file_access(filename, 'w') elif mode == 'r+': check_file_access(filename, 'r') if not os.access(filename, os.W_OK): raise IOError("file ``%s`` exists but it can not be written" % (filename,)) else: raise ValueError("invalid mode: %r" % (mode,)) checkFileAccess = previous_api(check_file_access) def lazyattr(fget): """Create a *lazy attribute* from the result of `fget`. This function is intended to be used as a *method decorator*. It returns a *property* which caches the result of calling the `fget` instance method. The docstring of `fget` is used for the property itself. For instance: >>> class MyClass(object): ... @lazyattr ... def attribute(self): ... 'Attribute description.' ... print('creating value') ... return 10 ... >>> type(MyClass.attribute) >>> MyClass.attribute.__doc__ 'Attribute description.' >>> obj = MyClass() >>> obj.__dict__ {} >>> obj.attribute creating value 10 >>> obj.__dict__ {'attribute': 10} >>> obj.attribute 10 >>> del obj.attribute Traceback (most recent call last): ... AttributeError: can't delete attribute .. warning:: Please note that this decorator *changes the type of the decorated object* from an instance method into a property. """ name = fget.__name__ def newfget(self): mydict = self.__dict__ if name in mydict: return mydict[name] mydict[name] = value = fget(self) return value return property(newfget, None, None, fget.__doc__) def show_stats(explain, tref, encoding=None): """Show the used memory (only works for Linux 2.6.x).""" if encoding is None: encoding = sys.getdefaultencoding() # Build the command to obtain memory info cmd = "cat /proc/%s/status" % os.getpid() sout = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout for line in sout: line = line.decode(encoding) if line.startswith("VmSize:"): vmsize = int(line.split()[1]) elif line.startswith("VmRSS:"): vmrss = int(line.split()[1]) elif line.startswith("VmData:"): vmdata = int(line.split()[1]) elif line.startswith("VmStk:"): vmstk = int(line.split()[1]) elif line.startswith("VmExe:"): vmexe = int(line.split()[1]) elif line.startswith("VmLib:"): vmlib = int(line.split()[1]) sout.close() print("Memory usage: ******* %s *******" % explain) print("VmSize: %7s kB\tVmRSS: %7s kB" % (vmsize, vmrss)) print("VmData: %7s kB\tVmStk: %7s kB" % (vmdata, vmstk)) print("VmExe: %7s kB\tVmLib: %7s kB" % (vmexe, vmlib)) tnow = time() print("WallClock time:", round(tnow - tref, 3)) return tnow # truncate data before calling __setitem__, to improve compression ratio # this function is taken verbatim from netcdf4-python def quantize(data, least_significant_digit): """quantize data to improve compression. Data is quantized using around(scale*data)/scale, where scale is 2**bits, and bits is determined from the least_significant_digit. For example, if least_significant_digit=1, bits will be 4. """ precision = pow(10., -least_significant_digit) exp = numpy.log10(precision) if exp < 0: exp = int(numpy.floor(exp)) else: exp = int(numpy.ceil(exp)) bits = numpy.ceil(numpy.log2(pow(10., -exp))) scale = pow(2., bits) datout = numpy.around(scale * data) / scale return datout # Utilities to detect leaked instances. See recipe 14.10 of the Python # Cookbook by Martelli & Ascher. tracked_classes = {} import weakref def log_instance_creation(instance, name=None): if name is None: name = instance.__class__.__name__ if name not in tracked_classes: tracked_classes[name] = [] tracked_classes[name].append(weakref.ref(instance)) logInstanceCreation = previous_api(log_instance_creation) def string_to_classes(s): if s == '*': c = sorted(tracked_classes.iterkeys()) return c else: return s.split() def fetch_logged_instances(classes="*"): classnames = string_to_classes(classes) return [(cn, len(tracked_classes[cn])) for cn in classnames] fetchLoggedInstances = previous_api(fetch_logged_instances) def count_logged_instances(classes, file=sys.stdout): for classname in string_to_classes(classes): file.write("%s: %d\n" % (classname, len(tracked_classes[classname]))) countLoggedInstances = previous_api(count_logged_instances) def list_logged_instances(classes, file=sys.stdout): for classname in string_to_classes(classes): file.write('\n%s:\n' % classname) for ref in tracked_classes[classname]: obj = ref() if obj is not None: file.write(' %s\n' % repr(obj)) listLoggedInstances = previous_api(list_logged_instances) def dump_logged_instances(classes, file=sys.stdout): for classname in string_to_classes(classes): file.write('\n%s:\n' % classname) for ref in tracked_classes[classname]: obj = ref() if obj is not None: file.write(' %s:\n' % obj) for key, value in obj.__dict__.iteritems(): file.write(' %20s : %s\n' % (key, value)) dumpLoggedInstances = previous_api(dump_logged_instances) # # A class useful for cache usage # class CacheDict(dict): """A dictionary that prevents itself from growing too much.""" def __init__(self, maxentries): self.maxentries = maxentries super(CacheDict, self).__init__(self) def __setitem__(self, key, value): # Protection against growing the cache too much if len(self) > self.maxentries: # Remove a 10% of (arbitrary) elements from the cache entries_to_remove = self.maxentries / 10 for k in self.keys()[:entries_to_remove]: super(CacheDict, self).__delitem__(k) super(CacheDict, self).__setitem__(key, value) class NailedDict(object): """A dictionary which ignores its items when it has nails on it.""" def __init__(self, maxentries): self.maxentries = maxentries self._cache = {} self._nailcount = 0 # Only a restricted set of dictionary methods are supported. That # is why we buy instead of inherit. # The following are intended to be used by ``Table`` code changing # the set of usable indexes. def clear(self): self._cache.clear() def nail(self): self._nailcount += 1 def unnail(self): self._nailcount -= 1 # The following are intended to be used by ``Table`` code handling # conditions. def __contains__(self, key): if self._nailcount > 0: return False return key in self._cache def __getitem__(self, key): if self._nailcount > 0: raise KeyError(key) return self._cache[key] def get(self, key, default=None): if self._nailcount > 0: return default return self._cache.get(key, default) def __setitem__(self, key, value): if self._nailcount > 0: return cache = self._cache # Protection against growing the cache too much if len(cache) > self.maxentries: # Remove a 10% of (arbitrary) elements from the cache entries_to_remove = self.maxentries // 10 for k in cache.keys()[:entries_to_remove]: del cache[k] cache[key] = value def detect_number_of_cores(): """Detects the number of cores on a system. Cribbed from pp. """ # Linux, Unix and MacOS: if hasattr(os, "sysconf"): if "SC_NPROCESSORS_ONLN" in os.sysconf_names: # Linux & Unix: ncpus = os.sysconf("SC_NPROCESSORS_ONLN") if isinstance(ncpus, int) and ncpus > 0: return ncpus else: # OSX: return int(os.popen2("sysctl -n hw.ncpu")[1].read()) # Windows: if "NUMBER_OF_PROCESSORS" in os.environ: ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]) if ncpus > 0: return ncpus return 1 # Default detectNumberOfCores = previous_api(detect_number_of_cores) # Main part # ========= def _test(): """Run ``doctest`` on this module.""" import doctest doctest.testmod() if __name__ == '__main__': _test() ## Local Variables: ## mode: python ## py-indent-offset: 4 ## tab-width: 4 ## fill-column: 72 ## End: PyTables-v.3.1.1/tables/utilsExtension.py000066400000000000000000000004141231437614300203670ustar00rootroot00000000000000from warnings import warn from tables.utilsextension import * _warnmsg = ("utilsExtension is pending deprecation, import utilsextension instead. " "You may use the pt2to3 tool to update your source code.") warn(_warnmsg, DeprecationWarning, stacklevel=2) PyTables-v.3.1.1/tables/utilsextension.pxd000066400000000000000000000011631231437614300205740ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: March 03, 2008 # Author: Francesc Alted - faltet@pytables.com # # $Id: definitions.pyd 1018 2005-06-20 09:43:34Z faltet $ # ######################################################################## """ These are declarations for functions in utilsextension.pyx that have to be shared with other extensions. """ from definitions cimport hsize_t, hid_t, const_char cdef hsize_t *malloc_dims(object) cdef hid_t get_native_type(hid_t) nogil cdef str cstr_to_pystr(const_char*) PyTables-v.3.1.1/tables/utilsextension.pyx000066400000000000000000001225441231437614300206300ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: May 20, 2005 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Cython utilities for PyTables and HDF5 library.""" import sys import warnings try: import zlib zlib_imported = True except ImportError: zlib_imported = False import numpy from tables.description import Description, Col from tables.misc.enum import Enum from tables.exceptions import HDF5ExtError from tables.atom import Atom, EnumAtom from tables.utils import check_file_access from tables._past import previous_api from cpython cimport PY_MAJOR_VERSION from libc.stdio cimport stderr from libc.stdlib cimport malloc, free from libc.string cimport strchr, strcmp, strncmp, strlen from cpython.bytes cimport PyBytes_Check from cpython.unicode cimport PyUnicode_DecodeUTF8, PyUnicode_Check from numpy cimport (import_array, ndarray, dtype, npy_int64, PyArray_DescrFromType, npy_intp, NPY_BOOL, NPY_STRING, NPY_INT8, NPY_INT16, NPY_INT32, NPY_INT64, NPY_UINT8, NPY_UINT16, NPY_UINT32, NPY_UINT64, NPY_FLOAT16, NPY_FLOAT32, NPY_FLOAT64, NPY_COMPLEX64, NPY_COMPLEX128) from definitions cimport (H5ARRAYget_info, H5ARRAYget_ndims, H5ATTRfind_attribute, H5ATTRget_attribute_string, H5D_CHUNKED, H5D_layout_t, H5Dclose, H5Dget_type, H5Dopen, H5E_DEFAULT, H5E_WALK_DOWNWARD, H5E_auto_t, H5E_error_t, H5E_walk_t, H5Eget_msg, H5Eprint, H5Eset_auto, H5Ewalk, H5F_ACC_RDONLY, H5Fclose, H5Fis_hdf5, H5Fopen, H5Gclose, H5Gopen, H5P_DEFAULT, H5T_ARRAY, H5T_BITFIELD, H5T_COMPOUND, H5T_CSET_ASCII, H5T_CSET_UTF8, H5T_C_S1, H5T_DIR_DEFAULT, H5T_ENUM, H5T_FLOAT, H5T_IEEE_F32BE, H5T_IEEE_F32LE, H5T_IEEE_F64BE, H5T_IEEE_F64LE, H5T_INTEGER, H5T_NATIVE_DOUBLE, H5T_NATIVE_LDOUBLE, H5T_NO_CLASS, H5T_OPAQUE, H5T_ORDER_BE, H5T_ORDER_LE, H5T_REFERENCE, H5T_STD_B8BE, H5T_STD_B8LE, H5T_STD_I16BE, H5T_STD_I16LE, H5T_STD_I32BE, H5T_STD_I32LE, H5T_STD_I64BE, H5T_STD_I64LE, H5T_STD_I8BE, H5T_STD_I8LE, H5T_STD_U16BE, H5T_STD_U16LE, H5T_STD_U32BE, H5T_STD_U32LE, H5T_STD_U64BE, H5T_STD_U64LE, H5T_STD_U8BE, H5T_STD_U8LE, H5T_STRING, H5T_TIME, H5T_UNIX_D32BE, H5T_UNIX_D32LE, H5T_UNIX_D64BE, H5T_UNIX_D64LE, H5T_VLEN, H5T_class_t, H5T_sign_t, H5Tarray_create, H5Tclose, H5Tcopy, H5Tcreate, H5Tenum_create, H5Tenum_insert, H5Tget_array_dims, H5Tget_array_ndims, H5Tget_class, H5Tget_member_name, H5Tget_member_type, H5Tget_member_value, H5Tget_native_type, H5Tget_nmembers, H5Tget_offset, H5Tget_order, H5Tget_precision, H5Tget_sign, H5Tget_size, H5Tget_super, H5Tinsert, H5Tis_variable_str, H5Tpack, H5Tset_precision, H5Tset_size, H5Tvlen_create, PyArray_Scalar, create_ieee_complex128, create_ieee_complex64, create_ieee_float16, create_ieee_complex192, create_ieee_complex256, get_len_of_range, get_order, herr_t, hid_t, hsize_t, hssize_t, htri_t, is_complex, register_blosc, set_order) # Platform-dependent types if sys.byteorder == "little": platform_byteorder = H5T_ORDER_LE # Standard types, independent of the byteorder H5T_STD_B8 = H5T_STD_B8LE H5T_STD_I8 = H5T_STD_I8LE H5T_STD_I16 = H5T_STD_I16LE H5T_STD_I32 = H5T_STD_I32LE H5T_STD_I64 = H5T_STD_I64LE H5T_STD_U8 = H5T_STD_U8LE H5T_STD_U16 = H5T_STD_U16LE H5T_STD_U32 = H5T_STD_U32LE H5T_STD_U64 = H5T_STD_U64LE H5T_IEEE_F32 = H5T_IEEE_F32LE H5T_IEEE_F64 = H5T_IEEE_F64LE H5T_UNIX_D32 = H5T_UNIX_D32LE H5T_UNIX_D64 = H5T_UNIX_D64LE else: # sys.byteorder == "big" platform_byteorder = H5T_ORDER_BE # Standard types, independent of the byteorder H5T_STD_B8 = H5T_STD_B8BE H5T_STD_I8 = H5T_STD_I8BE H5T_STD_I16 = H5T_STD_I16BE H5T_STD_I32 = H5T_STD_I32BE H5T_STD_I64 = H5T_STD_I64BE H5T_STD_U8 = H5T_STD_U8BE H5T_STD_U16 = H5T_STD_U16BE H5T_STD_U32 = H5T_STD_U32BE H5T_STD_U64 = H5T_STD_U64BE H5T_IEEE_F32 = H5T_IEEE_F32BE H5T_IEEE_F64 = H5T_IEEE_F64BE H5T_UNIX_D32 = H5T_UNIX_D32BE H5T_UNIX_D64 = H5T_UNIX_D64BE #---------------------------------------------------------------------------- # Conversion from PyTables string types to HDF5 native types # List only types that are susceptible of changing byteorder # (complex & enumerated types are special and should not be listed here) pttype_to_hdf5 = { 'int8' : H5T_STD_I8, 'uint8' : H5T_STD_U8, 'int16' : H5T_STD_I16, 'uint16' : H5T_STD_U16, 'int32' : H5T_STD_I32, 'uint32' : H5T_STD_U32, 'int64' : H5T_STD_I64, 'uint64' : H5T_STD_U64, 'float32': H5T_IEEE_F32, 'float64': H5T_IEEE_F64, 'float96': H5T_NATIVE_LDOUBLE, 'float128': H5T_NATIVE_LDOUBLE, 'time32' : H5T_UNIX_D32, 'time64' : H5T_UNIX_D64, } # Special cases whose byteorder cannot be directly changed pt_special_kinds = ['complex', 'string', 'enum', 'bool'] # Conversion table from NumPy extended codes prefixes to PyTables kinds npext_prefixes_to_ptkinds = { "S": "string", "b": "bool", "i": "int", "u": "uint", "f": "float", "c": "complex", "t": "time", "e": "enum", } # Names of HDF5 classes hdf5_class_to_string = { H5T_NO_CLASS : 'H5T_NO_CLASS', H5T_INTEGER : 'H5T_INTEGER', H5T_FLOAT : 'H5T_FLOAT', H5T_TIME : 'H5T_TIME', H5T_STRING : 'H5T_STRING', H5T_BITFIELD : 'H5T_BITFIELD', H5T_OPAQUE : 'H5T_OPAQUE', H5T_COMPOUND : 'H5T_COMPOUND', H5T_REFERENCE : 'H5T_REFERENCE', H5T_ENUM : 'H5T_ENUM', H5T_VLEN : 'H5T_VLEN', H5T_ARRAY : 'H5T_ARRAY', } # Depprecated API PTTypeToHDF5 = pttype_to_hdf5 PTSpecialKinds = pt_special_kinds NPExtPrefixesToPTKinds = npext_prefixes_to_ptkinds HDF5ClassToString = hdf5_class_to_string from numpy import typeDict cdef int have_float16 = ("float16" in typeDict) #---------------------------------------------------------------------- # External declarations # PyTables helper routines. cdef extern from "utils.h": int getLibrary(char *libname) nogil object _getTablesVersion() #object getZLIBVersionInfo() object getHDF5VersionInfo() object get_filter_names( hid_t loc_id, char *dset_name) H5T_class_t getHDF5ClassID(hid_t loc_id, char *name, H5D_layout_t *layout, hid_t *type_id, hid_t *dataset_id) nogil # To access to the slice.indices functionality for long long ints hssize_t getIndicesExt(object s, hsize_t length, hssize_t *start, hssize_t *stop, hssize_t *step, hsize_t *slicelength) # Functions from Blosc cdef extern from "blosc.h" nogil: void blosc_init() int blosc_set_nthreads(int nthreads) char* blosc_list_compressors() int blosc_compcode_to_compname(int compcode, char **compname) # @TODO: use the c_string_type and c_string_encoding global directives # (new in cython 0.19) cdef str cstr_to_pystr(const_char* cstring): if PY_MAJOR_VERSION > 2: pystring = PyUnicode_DecodeUTF8(cstring, strlen(cstring), NULL) else: pystring = bytes(cstring) return pystring #---------------------------------------------------------------------- # Initialization code # The NumPy API requires this function to be called before # using any NumPy facilities in an extension module. import_array() cdef register_blosc_(): cdef char *version cdef char *date register_blosc(&version, &date) compinfo = (version, date) free(version) free(date) if sys.version_info[0] > 2: return compinfo[0].decode('ascii'), compinfo[1].decode('ascii') else: return compinfo # The version of the blosc compression library that is currently included in # PyTables relies on unaligned memory access, so it is not functional on some # platforms (see https://github.com/FrancescAlted/blosc/issues/3 and # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=661286). # This function detects if blosc can work correctly on the current platform. # This function has been written by Julian Taylor . def _arch_without_blosc(): import platform arch = platform.machine().lower() for a in ["arm", "sparc", "mips"]: if a in arch: return True return False # Only register bloc compressor on platforms that actually support it. if _arch_without_blosc(): blosc_version = None else: blosc_version = register_blosc_() blosc_init() # from 1.2 on, Blosc library must be initialized # Important: Blosc calls that modifies global variables in Blosc must be # called from the same extension where Blosc is registered in HDF5. def set_blosc_max_threads(nthreads): """set_blosc_max_threads(nthreads) Set the maximum number of threads that Blosc can use. This actually overrides the :data:`tables.parameters.MAX_BLOSC_THREADS` setting in :mod:`tables.parameters`, so the new value will be effective until this function is called again or a new file with a different :data:`tables.parameters.MAX_BLOSC_THREADS` value is specified. Returns the previous setting for maximum threads. """ return blosc_set_nthreads(nthreads) setBloscMaxThreads = previous_api(set_blosc_max_threads) if sys.platform == "win32": # We need a different approach in Windows, because it complains when # trying to import the extension that is linked with a dynamic library # that is not installed in the system. # Initialize & register lzo if getLibrary("lzo2") == 0 or getLibrary("lzo1") == 0: import tables._comp_lzo lzo_version = tables._comp_lzo.register_() else: lzo_version = None # Initialize & register bzip2 if getLibrary("bzip2") == 0 or getLibrary("libbz2") == 0: import tables._comp_bzip2 bzip2_version = tables._comp_bzip2.register_() else: bzip2_version = None else: # Unix systems # Initialize & register lzo try: import tables._comp_lzo lzo_version = tables._comp_lzo.register_() except ImportError: lzo_version = None # Initialize & register bzip2 try: import tables._comp_bzip2 bzip2_version = tables._comp_bzip2.register_() except ImportError: bzip2_version = None # End of initialization code #--------------------------------------------------------------------- # Error handling helpers # XXX: silence warning about incompatible pointer types #ctypedef H5E_error_t* const_H5E_error_t_ptr "const H5E_error_t*" cdef herr_t e_walk_cb(unsigned n, H5E_error_t *err, void *data) with gil: cdef object bt = data # list #cdef char major_msg[256] #cdef char minor_msg[256] #cdef ssize_t msg_len if err == NULL: return -1 #msg_len = H5Eget_msg(err.maj_num, NULL, major_msg, 256) #if msg_len < 0: # major_msg[0] = '\0' #msg_len = H5Eget_msg(err.min_num, NULL, minor_msg, 256) #if msg_len < 0: # minor_msg[0] = '\0' #msg = "%s (MAJOR: %s, MINOR: %s)" % ( # bytes(err.desc).decode('utf-8'), # bytes(major_msg).decode('utf-8'), # bytes(minor_msg).decode('utf-8')) msg = bytes(err.desc).decode('utf-8') bt.append(( bytes(err.file_name).decode('utf-8'), err.line, bytes(err.func_name).decode('utf-8'), msg, )) return 0 def _dump_h5_backtrace(): cdef object bt = [] if H5Ewalk(H5E_DEFAULT, H5E_WALK_DOWNWARD, e_walk_cb, bt) < 0: return None return bt # Initialization of the _dump_h5_backtrace method of HDF5ExtError. # The unusual machinery is needed in order to avoid cirdular dependencies # between modules. HDF5ExtError._dump_h5_backtrace = _dump_h5_backtrace def silence_hdf5_messages(silence=True): """silence_hdf5_messages(silence=True) Silence (or re-enable) messages from the HDF5 C library. The *silence* parameter can be used control the behaviour and reset the standard HDF5 logging. .. versionadded:: 2.4 """ cdef herr_t err if silence: err = H5Eset_auto(H5E_DEFAULT, NULL, NULL) else: err = H5Eset_auto(H5E_DEFAULT, H5Eprint, stderr) if err < 0: raise HDF5ExtError("unable to configure HDF5 internal error handling") silenceHDF5Messages = previous_api(silence_hdf5_messages) # Disable automatic HDF5 error logging silence_hdf5_messages() def _broken_hdf5_long_double(): # HDF5 < 1.8.12 has a bug that prevents correct identification of the # long double data type when the code is built with gcc 4.8. # See also: http://hdf-forum.184993.n3.nabble.com/Issues-with-H5T-NATIVE-LDOUBLE-tt4026450.html return H5Tget_order(H5T_NATIVE_DOUBLE) != H5Tget_order(H5T_NATIVE_LDOUBLE) # Helper functions cdef hsize_t *malloc_dims(object pdims): """Return a malloced hsize_t dims from a python pdims.""" cdef int i, rank cdef hsize_t *dims dims = NULL rank = len(pdims) if rank > 0: dims = malloc(rank * sizeof(hsize_t)) for i from 0 <= i < rank: dims[i] = pdims[i] return dims cdef hid_t get_native_float_type(hid_t type_id) nogil: """Get a native type of an HDF5 float type. This functionn also handles half precision (float16) data type. """ cdef hid_t native_type_id cdef size_t precision precision = H5Tget_precision(type_id) if precision == 16 and have_float16: native_type_id = create_ieee_float16(NULL) else: native_type_id = H5Tget_native_type(type_id, H5T_DIR_DEFAULT) return native_type_id # This is a re-implementation of a working H5Tget_native_type for nested # compound types. I should report the flaw to THG as soon as possible. # F. Alted 2009-08-19 cdef hid_t get_nested_native_type(hid_t type_id) nogil: """Get a native nested type of an HDF5 type. In addition, it also recursively remove possible padding on type_id, i.e. it acts as a combination of H5Tget_native_type and H5Tpack. """ cdef hid_t tid, tid2 cdef hid_t member_type_id, native_type_id cdef hsize_t nfields cdef H5T_class_t class_id cdef size_t offset, itemsize, itemsize1 cdef char *colname cdef int i # Get the itemsize itemsize1 = H5Tget_size(type_id) # Build a new type container tid = H5Tcreate(H5T_COMPOUND, itemsize1) offset = 0 # Get the number of members nfields = H5Tget_nmembers(type_id) # Iterate thru the members for i from 0 <= i < nfields: # Get the member name colname = H5Tget_member_name(type_id, i) # Get the member type member_type_id = H5Tget_member_type(type_id, i) # Get the HDF5 class class_id = H5Tget_class(member_type_id) if class_id == H5T_COMPOUND: native_tid = get_nested_native_type(member_type_id) else: if class_id == H5T_FLOAT: native_tid = get_native_float_type(member_type_id) else: native_tid = H5Tget_native_type(member_type_id, H5T_DIR_DEFAULT) H5Tinsert(tid, colname, offset, native_tid) itemsize = H5Tget_size(native_tid) offset = offset + itemsize # Release resources H5Tclose(native_tid) H5Tclose(member_type_id) free(colname) # Correct the type size in case the memory type size is less # than the type in-disk (probably due to reading native HDF5 # files written with tools allowing field padding) if H5Tget_size(tid) > offset: H5Tset_size(tid, offset) return tid # This routine is more complex than required because HDF5 1.6.x does # not implement support for H5Tget_native_type with some types, like # H5T_BITFIELD and probably others. When 1.8.x would be a requisite, # this can be simplified. cdef hid_t get_native_type(hid_t type_id) nogil: """Get the native type of a HDF5 type.""" cdef H5T_class_t class_id, super_class_id cdef hid_t native_type_id = 0, super_type_id, native_super_type_id cdef int rank cdef hsize_t *dims class_id = H5Tget_class(type_id) if class_id == H5T_COMPOUND: # XXX It turns out that HDF5 does not correctly implement # H5Tget_native_type on nested compounds types. I should # report this to THG. # # *Note*: the next call *combines* the effect of H5Tget_native_type and # H5Tpack, and both effects are needed. Have this in mind if you # ever wants to replace get_nested_native_type by native HDF5 calls. # F. Alted 2009-08-19 return get_nested_native_type(type_id) elif class_id in (H5T_ARRAY, H5T_VLEN): # Get the array base component super_type_id = H5Tget_super(type_id) # Get the class super_class_id = H5Tget_class(super_type_id) if super_class_id == H5T_FLOAT: # replicate the logic of H5Tget_native_type for H5T_ARRAY and # H5T_VLEN taking into account extended floating point types # XXX: HDF5 error check native_super_type_id = get_native_float_type(super_type_id) H5Tclose(super_type_id) if class_id == H5T_ARRAY: rank = H5Tget_array_ndims(type_id) dims = malloc(rank * sizeof(hsize_t)) H5Tget_array_dims(type_id, dims) native_type_id = H5Tarray_create(native_super_type_id, rank, dims) free(dims) H5Tclose(native_super_type_id) return native_type_id elif class_id == H5T_VLEN: native_type_id = H5Tvlen_create(native_super_type_id) H5Tclose(native_super_type_id) return native_type_id class_id = super_class_id H5Tclose(super_type_id) if class_id == H5T_FLOAT: native_type_id = get_native_float_type(type_id) elif class_id in (H5T_INTEGER, H5T_ENUM): native_type_id = H5Tget_native_type(type_id, H5T_DIR_DEFAULT) else: # Fixing the byteorder for other types shouldn't be needed. # More in particular, H5T_TIME is not managed yet by HDF5 and so this # has to be managed explicitely inside the PyTables extensions. # Regarding H5T_BITFIELD, well, I'm not sure if changing the byteorder # of this is a good idea at all. native_type_id = H5Tcopy(type_id) return native_type_id def encode_filename(object filename): """Return the encoded filename in the filesystem encoding.""" cdef bytes encname if isinstance(filename, (unicode, numpy.str_)): # if type(filename) is unicode: encoding = sys.getfilesystemencoding() encname = filename.encode(encoding) else: encname = filename return encname # Main functions def is_hdf5_file(object filename): """is_hdf5_file(filename) Determine whether a file is in the HDF5 format. When successful, it returns a true value if the file is an HDF5 file, false otherwise. If there were problems identifying the file, an HDF5ExtError is raised. """ # Check that the file exists and is readable. check_file_access(filename) # Encode the filename in case it is unicode encname = encode_filename(filename) ret = H5Fis_hdf5(encname) if ret < 0: raise HDF5ExtError("problems identifying file ``%s``" % (filename,)) return ret > 0 isHDF5File = previous_api(is_hdf5_file) def is_pytables_file(object filename): """is_pytables_file(filename) Determine whether a file is in the PyTables format. When successful, it returns the format version string if the file is a PyTables file, None otherwise. If there were problems identifying the file, an HDF5ExtError is raised. """ cdef hid_t file_id cdef object isptf = None # A PYTABLES_FORMAT_VERSION attribute was not found if is_hdf5_file(filename): # Encode the filename in case it is unicode encname = encode_filename(filename) # The file exists and is HDF5, that's ok # Open it in read-only mode file_id = H5Fopen(encname, H5F_ACC_RDONLY, H5P_DEFAULT) isptf = read_f_attr(file_id, 'PYTABLES_FORMAT_VERSION') # Close the file H5Fclose(file_id) # system attributes should always be str if PY_MAJOR_VERSION < 3 and PyUnicode_Check(isptf): isptf = isptf.encode() elif PY_MAJOR_VERSION > 2 and PyBytes_Check(isptf): isptf = isptf.decode('utf-8') return isptf isPyTablesFile = previous_api(is_pytables_file) def get_hdf5_version(): """Get the underlying HDF5 library version""" return getHDF5VersionInfo()[1] getHDF5Version = previous_api(get_hdf5_version) def get_pytables_version(): """Return this extension version.""" return _getTablesVersion() getPyTablesVersion = previous_api(get_pytables_version) def which_lib_version(str name): """which_lib_version(name) Get version information about a C library. If the library indicated by name is available, this function returns a 3-tuple containing the major library version as an integer, its full version as a string, and the version date as a string. If the library is not available, None is returned. The currently supported library names are hdf5, zlib, lzo and bzip2. If another name is given, a ValueError is raised. """ cdef char *cname = NULL cdef bytes encoded_name encoded_name = name.encode('utf-8') # get the C pointer cname = encoded_name libnames = ('hdf5', 'zlib', 'lzo', 'bzip2', 'blosc') if strcmp(cname, "hdf5") == 0: binver, strver = getHDF5VersionInfo() return (binver, strver, None) # Should be always available elif strcmp(cname, "zlib") == 0: if zlib_imported: return (1, zlib.ZLIB_VERSION, None) elif strcmp(cname, "lzo") == 0: if lzo_version: (lzo_version_string, lzo_version_date) = lzo_version return (lzo_version, lzo_version_string, lzo_version_date) elif strcmp(cname, "bzip2") == 0: if bzip2_version: (bzip2_version_string, bzip2_version_date) = bzip2_version return (bzip2_version, bzip2_version_string, bzip2_version_date) elif strncmp(cname, "blosc", 5) == 0: if blosc_version: (blosc_version_string, blosc_version_date) = blosc_version return (blosc_version, blosc_version_string, blosc_version_date) else: raise ValueError("asked version of unsupported library ``%s``; " "supported library names are ``%s``" % (name, libnames)) # A supported library was specified, but no version is available. return None whichLibVersion = previous_api(which_lib_version) # A function returning all the compressors supported by local Blosc def blosc_compressor_list(): """ blosc_compressor_list() Returns a list of compressors available in the Blosc build. Parameters ---------- None Returns ------- out : list The list of names. """ list_compr = blosc_list_compressors().decode() clist = [str(cname) for cname in list_compr.split(',')] return clist # Convert compressor code to compressor name def blosc_compcode_to_compname_(compcode): """ blosc_compcode_to_compname() Returns the compressor name associated with compressor code. Parameters ---------- None Returns ------- out : string The name of the compressor. """ cdef char *cname cdef object compname compname = b"unknown (report this to developers)" if blosc_compcode_to_compname(compcode, &cname) >= 0: compname = cname return compname.decode() def which_class(hid_t loc_id, object name): """Detects a class ID using heuristics.""" cdef H5T_class_t class_id cdef H5D_layout_t layout cdef hsize_t nfields cdef char *field_name1 cdef char *field_name2 cdef int i cdef hid_t type_id, dataset_id cdef object classId cdef int rank cdef hsize_t *dims cdef hsize_t *maxdims cdef char byteorder[11] # "irrelevant" fits easily here cdef bytes encoded_name if isinstance(name, unicode): encoded_name = name.encode('utf-8') else: encoded_name = name classId = "UNSUPPORTED" # default value # Get The HDF5 class for the datatype in this dataset class_id = getHDF5ClassID(loc_id, encoded_name, &layout, &type_id, &dataset_id) # Check if this a dataset of supported classtype for ARRAY if ((class_id == H5T_INTEGER) or (class_id == H5T_FLOAT) or (class_id == H5T_BITFIELD) or (class_id == H5T_TIME) or (class_id == H5T_ENUM) or (class_id == H5T_STRING) or (class_id == H5T_ARRAY)): if layout == H5D_CHUNKED: if H5ARRAYget_ndims(dataset_id, &rank) < 0: raise HDF5ExtError("Problems getting ndims.") dims = malloc(rank * sizeof(hsize_t)) maxdims = malloc(rank * sizeof(hsize_t)) if H5ARRAYget_info(dataset_id, type_id, dims, maxdims, &class_id, byteorder) < 0: raise HDF5ExtError("Unable to get array info.") classId = "CARRAY" # Check whether some dimension is enlargeable for i in range(rank): if maxdims[i] == -1: classId = "EARRAY" break free(dims) free(maxdims) else: classId = "ARRAY" elif class_id == H5T_COMPOUND: # check whether the type is complex or not iscomplex = False nfields = H5Tget_nmembers(type_id) if nfields == 2: field_name1 = H5Tget_member_name(type_id, 0) field_name2 = H5Tget_member_name(type_id, 1) # The pair ("r", "i") is for PyTables. ("real", "imag") for Octave. if ( (strcmp(field_name1, "real") == 0 and strcmp(field_name2, "imag") == 0) or (strcmp(field_name1, "r") == 0 and strcmp(field_name2, "i") == 0) ): iscomplex = True free(field_name1) free(field_name2) if layout == H5D_CHUNKED: if iscomplex: classId = "CARRAY" else: classId = "TABLE" else: # Not chunked case # Octave saves complex arrays as non-chunked tables # with two fields: "real" and "imag" # Francesc Alted 2005-04-29 # Get number of records if iscomplex: classId = "ARRAY" # It is probably an Octave complex array else: # Added to support non-chunked tables classId = "TABLE" # A test for supporting non-growable tables elif class_id == H5T_VLEN: if layout == H5D_CHUNKED: classId = "VLARRAY" # Release the datatype. H5Tclose(type_id) # Close the dataset. H5Dclose(dataset_id) # Fallback return classId whichClass = previous_api(which_class) def get_nested_field(recarray, fieldname): """Get the maybe nested field named `fieldname` from the `recarray`. The `fieldname` may be a simple field name or a nested field name with slah-separated components. """ cdef bytes name = fieldname.encode('utf-8') try: if strchr(name, 47) != NULL: # ord('/') == 47 # It may be convenient to implement this way of descending nested # fields into the ``__getitem__()`` method of a subclass of # ``numpy.ndarray``. -- ivb field = recarray for nfieldname in fieldname.split('/'): field = field[nfieldname] else: # Faster method for non-nested columns field = recarray[fieldname] except KeyError: raise KeyError("no such column: %s" % (fieldname,)) return field getNestedField = previous_api(get_nested_field) def get_indices(object start, object stop, object step, hsize_t length): cdef hssize_t o_start, o_stop, o_step cdef hsize_t slicelength cdef object s # In order to convert possible numpy.integer values to long ones if start is not None: start = long(start) if stop is not None: stop = long(stop) if step is not None: step = long(step) s = slice(start, stop, step) if getIndicesExt(s, length, &o_start, &o_stop, &o_step, &slicelength) < 0: raise ValueError("Problems getting the indices on slice '%s'" % s) return (o_start, o_stop, o_step) getIndices = previous_api(get_indices) def read_f_attr(hid_t file_id, str attr_name): """Read PyTables file attributes (i.e. in root group). Returns the value of the `attr_name` attribute in root group, or `None` if it does not exist. This call cannot fail. """ cdef size_t size cdef char *attr_value cdef int cset = H5T_CSET_ASCII cdef object retvalue cdef bytes encoded_attr_name cdef char *c_attr_name = NULL encoded_attr_name = attr_name.encode('utf-8') # Get the C pointer c_attr_name = encoded_attr_name attr_value = NULL retvalue = None # Check if attribute exists if H5ATTRfind_attribute(file_id, c_attr_name): # Read the attr_name attribute size = H5ATTRget_attribute_string(file_id, c_attr_name, &attr_value, &cset) if size > 0: if cset == H5T_CSET_UTF8: retvalue = PyUnicode_DecodeUTF8(attr_value, strlen(attr_value), NULL) retvalue = numpy.str_(retvalue) else: retvalue = attr_value retvalue = numpy.bytes_(retvalue) # Important to release attr_value, because it has been malloc'ed! if attr_value: free(attr_value) return retvalue def get_filters(parent_id, name): """Get a dictionary with the filter names and cd_values""" cdef bytes encoded_name encoded_name = name.encode('utf-8') return get_filter_names(parent_id, encoded_name) getFilters = previous_api(get_filters) # This is used by several ._convert_types() methods. def get_type_enum(hid_t h5type): """_getTypeEnum(h5type) -> hid_t Get the native HDF5 enumerated type of `h5type`. If `h5type` is an enumerated type, it is returned. If it is a variable-length type with an enumerated base type, this is returned. If it is a multi-dimensional type with an enumerated base type, this is returned. Else, a ``TypeError`` is raised. """ cdef H5T_class_t typeClass cdef hid_t enumId, enumId2 typeClass = H5Tget_class(h5type) if typeClass < 0: raise HDF5ExtError("failed to get class of HDF5 type") if typeClass == H5T_ENUM: # Get the native type (in order to do byteorder conversions automatically) enumId = H5Tget_native_type(h5type, H5T_DIR_DEFAULT) elif typeClass in (H5T_ARRAY, H5T_VLEN): # The field is multi-dimensional or variable length. enumId2 = H5Tget_super(h5type) enumId = get_type_enum(enumId2) H5Tclose(enumId2) else: raise TypeError( "enumerated values can not be stored using the given type") return enumId getTypeEnum = previous_api(get_type_enum) def enum_from_hdf5(hid_t enumId, str byteorder): """enum_from_hdf5(enumId) -> (Enum, npType) Convert an HDF5 enumerated type to a PyTables one. This function takes an HDF5 enumerated type and returns an `Enum` instance built from that, and the NumPy type used to encode it. """ cdef hid_t baseId cdef int nelems, npenum, i cdef void *rbuf cdef char *ename cdef ndarray npvalue cdef object dtype cdef str pyename # Find the base type of the enumerated type, and get the atom baseId = H5Tget_super(enumId) atom = atom_from_hdf5_type(baseId) H5Tclose(baseId) if atom.kind not in ('int', 'uint'): raise NotImplementedError("sorry, only integer concrete values are " "supported at this moment") dtype = atom.dtype npvalue = numpy.array((0,), dtype=dtype) rbuf = npvalue.data # Get the name and value of each of the members # and put the pair in `enumDict`. enumDict = {} nelems = H5Tget_nmembers(enumId) if enumId < 0: raise HDF5ExtError( "failed to get element count of HDF5 enumerated type") for i from 0 <= i < nelems: ename = H5Tget_member_name(enumId, i) if ename == NULL: raise HDF5ExtError( "failed to get element name from HDF5 enumerated type") pyename = cstr_to_pystr(ename) free(ename) if H5Tget_member_value(enumId, i, rbuf) < 0: raise HDF5ExtError( "failed to get element value from HDF5 enumerated type") enumDict[pyename] = npvalue[0] # converted to NumPy scalar # Build an enumerated type from `enumDict` and return it. return Enum(enumDict), dtype enumFromHDF5 = previous_api(enum_from_hdf5) def enum_to_hdf5(object enum_atom, str byteorder): """Convert a PyTables enumerated type to an HDF5 one. This function creates an HDF5 enumerated type from the information contained in `enumAtom` (an ``Atom`` object), with the specified `byteorder` (a string). The resulting HDF5 enumerated type is returned. """ cdef bytes name cdef hid_t base_id, enum_id cdef long bytestride, i cdef void *rbuffer cdef void *rbuf cdef ndarray values cdef object base_atom # Get the base HDF5 type and create the enumerated type. base_atom = Atom.from_dtype(enum_atom.dtype.base) base_id = atom_to_hdf5_type(base_atom, byteorder) try: enum_id = H5Tenum_create(base_id) if enum_id < 0: raise HDF5ExtError("failed to create HDF5 enumerated type") finally: if H5Tclose(base_id) < 0: raise HDF5ExtError("failed to close HDF5 base type") # Set the name and value of each of the members. names = enum_atom._names values = enum_atom._values bytestride = values.strides[0] rbuffer = values.data i = names.index(enum_atom._defname) idx = list(range(len(names))) idx.pop(i) idx.insert(0, i) for i in idx: name = names[i].encode('utf-8') rbuf = (rbuffer + bytestride * i) if H5Tenum_insert(enum_id, name, rbuf) < 0: e = HDF5ExtError("failed to insert value into HDF5 enumerated type") if H5Tclose(enum_id) < 0: raise HDF5ExtError("failed to close HDF5 enumerated type") raise e # Return the new, open HDF5 enumerated type. return enum_id enumToHDF5 = previous_api(enum_to_hdf5) def atom_to_hdf5_type(atom, str byteorder): cdef hid_t tid = -1 cdef hsize_t *dims = NULL cdef bytes encoded_byteorder cdef char *cbyteorder = NULL encoded_byteorder = byteorder.encode('utf-8') # Get the C pointer cbyteorder = encoded_byteorder # Create the base HDF5 type if atom.type in pttype_to_hdf5: tid = H5Tcopy(pttype_to_hdf5[atom.type]) # Fix the byteorder if atom.kind != 'time': set_order(tid, cbyteorder) elif atom.type == 'float16': tid = create_ieee_float16(cbyteorder) elif atom.kind in pt_special_kinds: # Special cases (the byteorder doesn't need to be fixed afterwards) if atom.type == 'complex64': tid = create_ieee_complex64(cbyteorder) elif atom.type == 'complex128': tid = create_ieee_complex128(cbyteorder) elif atom.type == 'complex192': tid = create_ieee_complex192(cbyteorder) elif atom.type == 'complex256': tid = create_ieee_complex256(cbyteorder) elif atom.kind == 'string': tid = H5Tcopy(H5T_C_S1); H5Tset_size(tid, atom.itemsize) elif atom.kind == 'bool': tid = H5Tcopy(H5T_STD_B8); elif atom.kind == 'enum': tid = enum_to_hdf5(atom, byteorder) else: raise TypeError("Invalid type for atom %s" % (atom,)) # Create an H5T_ARRAY in case of non-scalar atoms if atom.shape != (): dims = malloc_dims(atom.shape) tid2 = H5Tarray_create(tid, len(atom.shape), dims) free(dims) H5Tclose(tid) tid = tid2 return tid AtomToHDF5Type = previous_api(atom_to_hdf5_type) def load_enum(hid_t type_id): """load_enum() -> (Enum, npType) Load the enumerated HDF5 type associated with this type_id. It returns an `Enum` instance built from that, and the NumPy type used to encode it. """ cdef hid_t enumId cdef char c_byteorder[11] # "irrelevant" fits well here cdef str byteorder # Get the enumerated type enumId = get_type_enum(type_id) # Get the byteorder get_order(type_id, c_byteorder) byteorder = cstr_to_pystr(c_byteorder) # Get the Enum and NumPy types and close the HDF5 type. try: return enum_from_hdf5(enumId, byteorder) finally: # (Yes, the ``finally`` clause *is* executed.) if H5Tclose(enumId) < 0: raise HDF5ExtError("failed to close HDF5 enumerated type") loadEnum = previous_api(load_enum) def hdf5_to_np_nested_type(hid_t type_id): """Given a HDF5 `type_id`, return a dtype string representation of it.""" cdef hid_t member_type_id cdef hsize_t nfields cdef int i cdef char *c_colname cdef H5T_class_t class_id cdef object desc cdef str colname desc = {} # Get the number of members nfields = H5Tget_nmembers(type_id) # Iterate thru the members for i from 0 <= i < nfields: # Get the member name c_colname = H5Tget_member_name(type_id, i) colname = cstr_to_pystr(c_colname) # Get the member type member_type_id = H5Tget_member_type(type_id, i) # Get the HDF5 class class_id = H5Tget_class(member_type_id) if class_id == H5T_COMPOUND and not is_complex(member_type_id): desc[colname] = hdf5_to_np_nested_type(member_type_id) desc[colname]["_v_pos"] = i # Remember the position else: atom = atom_from_hdf5_type(member_type_id, pure_numpy_types=True) desc[colname] = Col.from_atom(atom, pos=i) # Release resources H5Tclose(member_type_id) free(c_colname) return desc HDF5ToNPNestedType = previous_api(hdf5_to_np_nested_type) def hdf5_to_np_ext_type(hid_t type_id, pure_numpy_types=True, atom=False): """Map the atomic HDF5 type to a string repr of NumPy extended codes. If `pure_numpy_types` is true, detected HDF5 types that does not match pure NumPy types will raise a ``TypeError`` exception. If not, HDF5 types like TIME, VLEN or ENUM are passed through. If `atom` is true, the resulting repr is meant for atoms. If not, the result is meant for attributes. Returns the string repr of type and its shape. The exception is for compounds types, that returns a NumPy dtype and shape instead. """ cdef H5T_sign_t sign cdef hid_t super_type_id, native_type_id cdef H5T_class_t class_id cdef size_t itemsize cdef object stype, shape, shape2 cdef hsize_t *dims # default shape shape = () # Get the HDF5 class class_id = H5Tget_class(type_id) # Get the itemsize itemsize = H5Tget_size(type_id) if class_id == H5T_BITFIELD: stype = "b1" elif class_id == H5T_INTEGER: # Get the sign sign = H5Tget_sign(type_id) if (sign > 0): stype = "i%s" % itemsize else: stype = "u%s" % itemsize elif class_id == H5T_FLOAT: stype = "f%s" % itemsize elif class_id == H5T_COMPOUND: if is_complex(type_id): stype = "c%s" % itemsize else: if atom: raise TypeError("the HDF5 class ``%s`` is not supported yet" % hdf5_class_to_string[class_id]) # Recursively remove possible padding on type_id. native_type_id = get_nested_native_type(type_id) desc = Description(hdf5_to_np_nested_type(native_type_id)) # stype here is not exactly a string, but the NumPy dtype factory # will deal with this. stype = desc._v_dtype H5Tclose(native_type_id) elif class_id == H5T_STRING: if H5Tis_variable_str(type_id): raise TypeError("variable length strings are not supported yet") stype = "S%s" % itemsize elif class_id == H5T_TIME: if pure_numpy_types: raise TypeError("the HDF5 class ``%s`` is not supported yet" % hdf5_class_to_string[class_id]) stype = "t%s" % itemsize elif class_id == H5T_ENUM: if pure_numpy_types: raise TypeError("the HDF5 class ``%s`` is not supported yet" % hdf5_class_to_string[class_id]) stype = "e" elif class_id == H5T_VLEN: if pure_numpy_types: raise TypeError("the HDF5 class ``%s`` is not supported yet" % hdf5_class_to_string[class_id]) # Get the variable length base component super_type_id = H5Tget_super(type_id) # Find the super member format stype, shape = hdf5_to_np_ext_type(super_type_id, pure_numpy_types) # Release resources H5Tclose(super_type_id) elif class_id == H5T_ARRAY: # Get the array base component super_type_id = H5Tget_super(type_id) # Find the super member format stype, shape2 = hdf5_to_np_ext_type(super_type_id, pure_numpy_types) # Get shape shape = [] ndims = H5Tget_array_ndims(type_id) dims = malloc(ndims * sizeof(hsize_t)) H5Tget_array_dims(type_id, dims) for i from 0 <= i < ndims: shape.append(dims[i]) # cast to avoid long representation (i.e. 2L) shape = tuple(shape) # Release resources free(dims) H5Tclose(super_type_id) else: # Other types are not supported yet raise TypeError("the HDF5 class ``%s`` is not supported yet" % hdf5_class_to_string[class_id]) return stype, shape HDF5ToNPExtType = previous_api(hdf5_to_np_ext_type) def atom_from_hdf5_type(hid_t type_id, pure_numpy_types=False): """Get an atom from a type_id. See `hdf5_to_np_ext_type` for an explanation of the `pure_numpy_types` parameter. """ cdef object stype, shape, atom_, sctype, tsize, kind cdef object dflt, base, enum_, nptype stype, shape = hdf5_to_np_ext_type(type_id, pure_numpy_types, atom=True) # Create the Atom if stype == 'e': (enum_, nptype) = load_enum(type_id) # Take one of the names as the default in the enumeration. dflt = next(iter(enum_))[0] base = Atom.from_dtype(nptype) atom_ = EnumAtom(enum_, dflt, base, shape=shape) else: kind = npext_prefixes_to_ptkinds[stype[0]] tsize = int(stype[1:]) atom_ = Atom.from_kind(kind, tsize, shape=shape) return atom_ AtomFromHDF5Type = previous_api(atom_from_hdf5_type) def create_nested_type(object desc, str byteorder): """Create a nested type based on a description and return an HDF5 type.""" cdef hid_t tid, tid2 cdef size_t offset cdef bytes encoded_name tid = H5Tcreate(H5T_COMPOUND, desc._v_itemsize) if tid < 0: return -1 offset = 0 for k in desc._v_names: obj = desc._v_colobjects[k] if isinstance(obj, Description): tid2 = create_nested_type(obj, byteorder) else: tid2 = atom_to_hdf5_type(obj, byteorder) encoded_name = k.encode('utf-8') H5Tinsert(tid, encoded_name, offset, tid2) offset = offset + desc._v_dtype[k].itemsize # Release resources H5Tclose(tid2) return tid createNestedType = previous_api(create_nested_type) ## Local Variables: ## mode: python ## py-indent-offset: 2 ## tab-width: 2 ## fill-column: 78 ## End: PyTables-v.3.1.1/tables/vlarray.py000066400000000000000000000771171231437614300170300ustar00rootroot00000000000000# -*- coding: utf-8 -*- ######################################################################## # # License: BSD # Created: November 12, 2003 # Author: Francesc Alted - faltet@pytables.com # # $Id$ # ######################################################################## """Here is defined the VLArray class.""" import sys import numpy from tables import hdf5extension from tables.utils import (convert_to_np_atom, convert_to_np_atom2, idx2long, correct_byteorder, SizeType, is_idx, lazyattr) from tables.atom import ObjectAtom, VLStringAtom, VLUnicodeAtom from tables.flavor import internal_to_flavor from tables.leaf import Leaf, calc_chunksize from tables._past import previous_api, previous_api_property # default version for VLARRAY objects # obversion = "1.0" # initial version # obversion = "1.0" # add support for complex datatypes # obversion = "1.1" # This adds support for time datatypes. # obversion = "1.2" # This adds support for enumerated datatypes. # obversion = "1.3" # Introduced 'PSEUDOATOM' attribute. obversion = "1.4" # Numeric and numarray flavors are gone. class VLArray(hdf5extension.VLArray, Leaf): """This class represents variable length (ragged) arrays in an HDF5 file. Instances of this class represent array objects in the object tree with the property that their rows can have a *variable* number of homogeneous elements, called *atoms*. Like Table datasets (see :ref:`TableClassDescr`), variable length arrays can have only one dimension, and the elements (atoms) of their rows can be fully multidimensional. When reading a range of rows from a VLArray, you will *always* get a Python list of objects of the current flavor (each of them for a row), which may have different lengths. This class provides methods to write or read data to or from variable length array objects in the file. Note that it also inherits all the public attributes and methods that Leaf (see :ref:`LeafClassDescr`) already provides. .. note:: VLArray objects also support compression although compression is only performed on the data structures used internally by the HDF5 to take references of the location of the variable length data. Data itself (the raw data) are not compressed or filtered. Please refer to the `VLTypes Technical Note `_ for more details on the topic. Parameters ---------- parentnode The parent :class:`Group` object. .. versionchanged:: 3.0 Renamed from *parentNode* to *parentnode*. name : str The name of this node in its parent group. atom An `Atom` instance representing the *type* and *shape* of the atomic objects to be saved. title A description for this node (it sets the ``TITLE`` HDF5 attribute on disk). filters An instance of the `Filters` class that provides information about the desired I/O filters to be applied during the life of this object. expectedrows A user estimate about the number of row elements that will be added to the growable dimension in the `VLArray` node. If not provided, the default value is ``EXPECTED_ROWS_VLARRAY`` (see ``tables/parameters.py``). If you plan to create either a much smaller or a much bigger `VLArray` try providing a guess; this will optimize the HDF5 B-Tree creation and management process time and the amount of memory used. .. versionadded:: 3.0 chunkshape The shape of the data chunk to be read or written in a single HDF5 I/O operation. Filters are applied to those chunks of data. The dimensionality of `chunkshape` must be 1. If ``None``, a sensible value is calculated (which is recommended). byteorder The byteorder of the data *on disk*, specified as 'little' or 'big'. If this is not specified, the byteorder is that of the platform. .. versionchanged:: 3.0 The *expectedsizeinMB* parameter has been replaced by *expectedrows*. Examples -------- See below a small example of the use of the VLArray class. The code is available in :file:`examples/vlarray1.py`:: import tables from numpy import * # Create a VLArray: fileh = tables.open_file('vlarray1.h5', mode='w') vlarray = fileh.create_vlarray(fileh.root, 'vlarray1', tables.Int32Atom(shape=()), "ragged array of ints", filters=tables.Filters(1)) # Append some (variable length) rows: vlarray.append(array([5, 6])) vlarray.append(array([5, 6, 7])) vlarray.append([5, 6, 9, 8]) # Now, read it through an iterator: print('-->', vlarray.title) for x in vlarray: print('%s[%d]--> %s' % (vlarray.name, vlarray.nrow, x)) # Now, do the same with native Python strings. vlarray2 = fileh.create_vlarray(fileh.root, 'vlarray2', tables.StringAtom(itemsize=2), "ragged array of strings", filters=tables.Filters(1)) vlarray2.flavor = 'python' # Append some (variable length) rows: print('-->', vlarray2.title) vlarray2.append(['5', '66']) vlarray2.append(['5', '6', '77']) vlarray2.append(['5', '6', '9', '88']) # Now, read it through an iterator: for x in vlarray2: print('%s[%d]--> %s' % (vlarray2.name, vlarray2.nrow, x)) # Close the file. fileh.close() The output for the previous script is something like:: --> ragged array of ints vlarray1[0]--> [5 6] vlarray1[1]--> [5 6 7] vlarray1[2]--> [5 6 9 8] --> ragged array of strings vlarray2[0]--> ['5', '66'] vlarray2[1]--> ['5', '6', '77'] vlarray2[2]--> ['5', '6', '9', '88'] .. rubric:: VLArray attributes The instance variables below are provided in addition to those in Leaf (see :ref:`LeafClassDescr`). .. attribute:: atom An Atom (see :ref:`AtomClassDescr`) instance representing the *type* and *shape* of the atomic objects to be saved. You may use a *pseudo-atom* for storing a serialized object or variable length string per row. .. attribute:: flavor The type of data object read from this leaf. Please note that when reading several rows of VLArray data, the flavor only applies to the *components* of the returned Python list, not to the list itself. .. attribute:: nrow On iterators, this is the index of the current row. .. attribute:: nrows The current number of rows in the array. .. attribute:: extdim The index of the enlargeable dimension (always 0 for vlarrays). """ # Class identifier. _c_classid = 'VLARRAY' _c_classId = previous_api_property('_c_classid') # Lazy read-only attributes # ````````````````````````` @lazyattr def dtype(self): """The NumPy ``dtype`` that most closely matches this array.""" return self.atom.dtype # Properties # ~~~~~~~~~~ shape = property( lambda self: (self.nrows,), None, None, "The shape of the stored array.") def _get_size_on_disk(self): raise NotImplementedError('size_on_disk not implemented for VLArrays') size_on_disk = property(_get_size_on_disk, None, None, """ The HDF5 library does not include a function to determine size_on_disk for variable-length arrays. Accessing this attribute will raise a NotImplementedError. """) size_in_memory = property( lambda self: self._get_memory_size(), None, None, """ The size of this array's data in bytes when it is fully loaded into memory. .. note:: When data is stored in a VLArray using the ObjectAtom type, it is first serialized using pickle, and then converted to a NumPy array suitable for storage in an HDF5 file. This attribute will return the size of that NumPy representation. If you wish to know the size of the Python objects after they are loaded from disk, you can use this `ActiveState recipe `_. """) # Other methods # ~~~~~~~~~~~~~ def __init__(self, parentnode, name, atom=None, title="", filters=None, expectedrows=None, chunkshape=None, byteorder=None, _log=True): self._v_version = None """The object version of this array.""" self._v_new = new = atom is not None """Is this the first time the node has been created?""" self._v_new_title = title """New title for this node.""" self._v_new_filters = filters """New filter properties for this array.""" if expectedrows is None: expectedrows = parentnode._v_file.params['EXPECTED_ROWS_VLARRAY'] self._v_expectedrows = expectedrows """The expected number of rows to be stored in the array. .. versionadded:: 3.0 """ self._v_chunkshape = None """Private storage for the `chunkshape` property of Leaf.""" # Miscellaneous iteration rubbish. self._start = None """Starting row for the current iteration.""" self._stop = None """Stopping row for the current iteration.""" self._step = None """Step size for the current iteration.""" self._nrowsread = None """Number of rows read up to the current state of iteration.""" self._startb = None """Starting row for current buffer.""" self._stopb = None """Stopping row for current buffer. """ self._row = None """Current row in iterators (sentinel).""" self._init = False """Whether we are in the middle of an iteration or not (sentinel).""" self.listarr = None """Current buffer in iterators.""" # Documented (*public*) attributes. self.atom = atom """ An Atom (see :ref:`AtomClassDescr`) instance representing the *type* and *shape* of the atomic objects to be saved. You may use a *pseudo-atom* for storing a serialized object or variable length string per row. """ self.nrow = None """On iterators, this is the index of the current row.""" self.nrows = None """The current number of rows in the array.""" self.extdim = 0 # VLArray only have one dimension currently """The index of the enlargeable dimension (always 0 for vlarrays).""" # Check the chunkshape parameter if new and chunkshape is not None: if isinstance(chunkshape, (int, numpy.integer, long)): chunkshape = (chunkshape,) try: chunkshape = tuple(chunkshape) except TypeError: raise TypeError( "`chunkshape` parameter must be an integer or sequence " "and you passed a %s" % type(chunkshape)) if len(chunkshape) != 1: raise ValueError("`chunkshape` rank (length) must be 1: %r" % (chunkshape,)) self._v_chunkshape = tuple(SizeType(s) for s in chunkshape) super(VLArray, self).__init__(parentnode, name, new, filters, byteorder, _log) def _g_post_init_hook(self): super(VLArray, self)._g_post_init_hook() self.nrowsinbuf = 100 # maybe enough for most applications # This is too specific for moving it into Leaf def _calc_chunkshape(self, expectedrows): """Calculate the size for the HDF5 chunk.""" # For computing the chunkshape for HDF5 VL types, we have to # choose the itemsize of the *each* element of the atom and # not the size of the entire atom. I don't know why this # should be like this, perhaps I should report this to the # HDF5 list. # F. Alted 2006-11-23 # elemsize = self.atom.atomsize() elemsize = self._basesize # AV 2013-05-03 # This is just a quick workaround tha allows to change the API for # PyTables 3.0 release and remove the expected_mb parameter. # The algorithm for computing the chunkshape should be rewritten as # requested by gh-35. expected_mb = expectedrows * elemsize / 1024. ** 2 chunksize = calc_chunksize(expected_mb) # Set the chunkshape chunkshape = chunksize // elemsize # Safeguard against itemsizes being extremely large if chunkshape == 0: chunkshape = 1 return (SizeType(chunkshape),) def _g_create(self): """Create a variable length array (ragged array).""" atom = self.atom self._v_version = obversion # Check for zero dims in atom shape (not allowed in VLArrays) zerodims = numpy.sum(numpy.array(atom.shape) == 0) if zerodims > 0: raise ValueError("When creating VLArrays, none of the dimensions " "of the Atom instance can be zero.") if not hasattr(atom, 'size'): # it is a pseudo-atom self._atomicdtype = atom.base.dtype self._atomicsize = atom.base.size self._basesize = atom.base.itemsize else: self._atomicdtype = atom.dtype self._atomicsize = atom.size self._basesize = atom.itemsize self._atomictype = atom.type self._atomicshape = atom.shape # Compute the optimal chunkshape, if needed if self._v_chunkshape is None: self._v_chunkshape = self._calc_chunkshape(self._v_expectedrows) self.nrows = SizeType(0) # No rows at creation time # Correct the byteorder if needed if self.byteorder is None: self.byteorder = correct_byteorder(atom.type, sys.byteorder) # After creating the vlarray, ``self._v_objectid`` needs to be # set because it is needed for setting attributes afterwards. self._v_objectid = self._create_array(self._v_new_title) # Add an attribute in case we have a pseudo-atom so that we # can retrieve the proper class after a re-opening operation. if not hasattr(atom, 'size'): # it is a pseudo-atom self.attrs.PSEUDOATOM = atom.kind return self._v_objectid def _g_open(self): """Get the metadata info for an array in file.""" self._v_objectid, self.nrows, self._v_chunkshape, atom = \ self._open_array() # Check if the atom can be a PseudoAtom if "PSEUDOATOM" in self.attrs: kind = self.attrs.PSEUDOATOM if kind == 'vlstring': atom = VLStringAtom() elif kind == 'vlunicode': atom = VLUnicodeAtom() elif kind == 'object': atom = ObjectAtom() else: raise ValueError( "pseudo-atom name ``%s`` not known." % kind) elif self._v_file.format_version[:1] == "1": flavor1x = self.attrs.FLAVOR if flavor1x == "VLString": atom = VLStringAtom() elif flavor1x == "Object": atom = ObjectAtom() self.atom = atom return self._v_objectid def _getnobjects(self, nparr): """Return the number of objects in a NumPy array.""" # Check for zero dimensionality array zerodims = numpy.sum(numpy.array(nparr.shape) == 0) if zerodims > 0: # No objects to be added return 0 shape = nparr.shape atom_shape = self.atom.shape shapelen = len(nparr.shape) if isinstance(atom_shape, tuple): atomshapelen = len(self.atom.shape) else: atom_shape = (self.atom.shape,) atomshapelen = 1 diflen = shapelen - atomshapelen if shape == atom_shape: nobjects = 1 elif (diflen == 1 and shape[diflen:] == atom_shape): # Check if the leading dimensions are all ones # if shape[:diflen-1] == (1,)*(diflen-1): # nobjects = shape[diflen-1] # shape = shape[diflen:] # It's better to accept only inputs with the exact dimensionality # i.e. a dimensionality only 1 element larger than atom nobjects = shape[0] shape = shape[1:] elif atom_shape == (1,) and shapelen == 1: # Case where shape = (N,) and shape_atom = 1 or (1,) nobjects = shape[0] else: raise ValueError("The object '%s' is composed of elements with " "shape '%s', which is not compatible with the " "atom shape ('%s')." % (nparr, shape, atom_shape)) return nobjects def get_enum(self): """Get the enumerated type associated with this array. If this array is of an enumerated type, the corresponding Enum instance (see :ref:`EnumClassDescr`) is returned. If it is not of an enumerated type, a TypeError is raised. """ if self.atom.kind != 'enum': raise TypeError("array ``%s`` is not of an enumerated type" % self._v_pathname) return self.atom.enum getEnum = previous_api(get_enum) def append(self, sequence): """Add a sequence of data to the end of the dataset. This method appends the objects in the sequence to a *single row* in this array. The type and shape of individual objects must be compliant with the atoms in the array. In the case of serialized objects and variable length strings, the object or string to append is itself the sequence. """ self._g_check_open() self._v_file._check_writable() # Prepare the sequence to convert it into a NumPy object atom = self.atom if not hasattr(atom, 'size'): # it is a pseudo-atom sequence = atom.toarray(sequence) statom = atom.base else: try: # fastest check in most cases len(sequence) except TypeError: raise TypeError("argument is not a sequence") statom = atom if len(sequence) > 0: # The sequence needs to be copied to make the operation safe # to in-place conversion. nparr = convert_to_np_atom2(sequence, statom) nobjects = self._getnobjects(nparr) else: nobjects = 0 nparr = None self._append(nparr, nobjects) self.nrows += 1 def iterrows(self, start=None, stop=None, step=None): """Iterate over the rows of the array. This method returns an iterator yielding an object of the current flavor for each selected row in the array. If a range is not supplied, *all the rows* in the array are iterated upon. You can also use the :meth:`VLArray.__iter__` special method for that purpose. If you only want to iterate over a given *range of rows* in the array, you may use the start, stop and step parameters. Examples -------- :: for row in vlarray.iterrows(step=4): print('%s[%d]--> %s' % (vlarray.name, vlarray.nrow, row)) .. versionchanged:: 3.0 If the *start* parameter is provided and *stop* is None then the array is iterated from *start* to the last line. In PyTables < 3.0 only one element was returned. """ (self._start, self._stop, self._step) = self._process_range( start, stop, step) self._init_loop() return self def __iter__(self): """Iterate over the rows of the array. This is equivalent to calling :meth:`VLArray.iterrows` with default arguments, i.e. it iterates over *all the rows* in the array. Examples -------- :: result = [row for row in vlarray] Which is equivalent to:: result = [row for row in vlarray.iterrows()] """ if not self._init: # If the iterator is called directly, assign default variables self._start = 0 self._stop = self.nrows self._step = 1 # and initialize the loop self._init_loop() return self def _init_loop(self): """Initialization for the __iter__ iterator.""" self._nrowsread = self._start self._startb = self._start self._row = -1 # Sentinel self._init = True # Sentinel self.nrow = SizeType(self._start - self._step) # row number _initLoop = previous_api(_init_loop) def next(self): """Get the next element of the array during an iteration. The element is returned as a list of objects of the current flavor. """ if self._nrowsread >= self._stop: self._init = False raise StopIteration # end of iteration else: # Read a chunk of rows if self._row + 1 >= self.nrowsinbuf or self._row < 0: self._stopb = self._startb + self._step * self.nrowsinbuf self.listarr = self.read(self._startb, self._stopb, self._step) self._row = -1 self._startb = self._stopb self._row += 1 self.nrow += self._step self._nrowsread += self._step return self.listarr[self._row] def __getitem__(self, key): """Get a row or a range of rows from the array. If key argument is an integer, the corresponding array row is returned as an object of the current flavor. If key is a slice, the range of rows determined by it is returned as a list of objects of the current flavor. In addition, NumPy-style point selections are supported. In particular, if key is a list of row coordinates, the set of rows determined by it is returned. Furthermore, if key is an array of boolean values, only the coordinates where key is True are returned. Note that for the latter to work it is necessary that key list would contain exactly as many rows as the array has. Examples -------- :: a_row = vlarray[4] a_list = vlarray[4:1000:2] a_list2 = vlarray[[0,2]] # get list of coords a_list3 = vlarray[[0,-2]] # negative values accepted a_list4 = vlarray[numpy.array([True,...,False])] # array of bools """ self._g_check_open() if is_idx(key): # Index out of range protection if key >= self.nrows: raise IndexError("Index out of range") if key < 0: # To support negative values key += self.nrows (start, stop, step) = self._process_range(key, key + 1, 1) return self.read(start, stop, step)[0] elif isinstance(key, slice): start, stop, step = self._process_range( key.start, key.stop, key.step) return self.read(start, stop, step) # Try with a boolean or point selection elif type(key) in (list, tuple) or isinstance(key, numpy.ndarray): coords = self._point_selection(key) return self._read_coordinates(coords) else: raise IndexError("Invalid index or slice: %r" % (key,)) def _assign_values(self, coords, values): """Assign the `values` to the positions stated in `coords`.""" for nrow, value in zip(coords, values): if nrow >= self.nrows: raise IndexError("First index out of range") if nrow < 0: # To support negative values nrow += self.nrows object_ = value # Prepare the object to convert it into a NumPy object atom = self.atom if not hasattr(atom, 'size'): # it is a pseudo-atom object_ = atom.toarray(object_) statom = atom.base else: statom = atom value = convert_to_np_atom(object_, statom) nobjects = self._getnobjects(value) # Get the previous value nrow = idx2long( nrow) # To convert any possible numpy scalar value nparr = self._read_array(nrow, nrow + 1, 1)[0] nobjects = len(nparr) if len(value) > nobjects: raise ValueError("Length of value (%s) is larger than number " "of elements in row (%s)" % (len(value), nobjects)) try: nparr[:] = value except Exception as exc: # XXX raise ValueError("Value parameter:\n'%r'\n" "cannot be converted into an array object " "compliant vlarray[%s] row: \n'%r'\n" "The error was: <%s>" % (value, nrow, nparr[:], exc)) if nparr.size > 0: self._modify(nrow, nparr, nobjects) def __setitem__(self, key, value): """Set a row, or set of rows, in the array. It takes different actions depending on the type of the *key* parameter: if it is an integer, the corresponding table row is set to *value* (a record or sequence capable of being converted to the table structure). If *key* is a slice, the row slice determined by it is set to *value* (a record array or sequence of rows capable of being converted to the table structure). In addition, NumPy-style point selections are supported. In particular, if key is a list of row coordinates, the set of rows determined by it is set to value. Furthermore, if key is an array of boolean values, only the coordinates where key is True are set to values from value. Note that for the latter to work it is necessary that key list would contain exactly as many rows as the table has. .. note:: When updating the rows of a VLArray object which uses a pseudo-atom, there is a problem: you can only update values with *exactly* the same size in bytes than the original row. This is very difficult to meet with object pseudo-atoms, because :mod:`pickle` applied on a Python object does not guarantee to return the same number of bytes than over another object, even if they are of the same class. This effectively limits the kinds of objects than can be updated in variable-length arrays. Examples -------- :: vlarray[0] = vlarray[0] * 2 + 3 vlarray[99] = arange(96) * 2 + 3 # Negative values for the index are supported. vlarray[-99] = vlarray[5] * 2 + 3 vlarray[1:30:2] = list_of_rows vlarray[[1,3]] = new_1_and_3_rows """ self._g_check_open() self._v_file._check_writable() if is_idx(key): # If key is not a sequence, convert to it coords = [key] value = [value] elif isinstance(key, slice): (start, stop, step) = self._process_range( key.start, key.stop, key.step) coords = range(start, stop, step) # Try with a boolean or point selection elif type(key) in (list, tuple) or isinstance(key, numpy.ndarray): coords = self._point_selection(key) else: raise IndexError("Invalid index or slice: %r" % (key,)) # Do the assignment row by row self._assign_values(coords, value) # Accessor for the _read_array method in superclass def read(self, start=None, stop=None, step=1): """Get data in the array as a list of objects of the current flavor. Please note that, as the lengths of the different rows are variable, the returned value is a *Python list* (not an array of the current flavor), with as many entries as specified rows in the range parameters. The start, stop and step parameters can be used to select only a *range of rows* in the array. Their meanings are the same as in the built-in range() Python function, except that negative values of step are not allowed yet. Moreover, if only start is specified, then stop will be set to start + 1. If you do not specify neither start nor stop, then *all the rows* in the array are selected. """ self._g_check_open() start, stop, step = self._process_range_read(start, stop, step) if start == stop: listarr = [] else: listarr = self._read_array(start, stop, step) atom = self.atom if not hasattr(atom, 'size'): # it is a pseudo-atom outlistarr = [atom.fromarray(arr) for arr in listarr] else: # Convert the list to the right flavor flavor = self.flavor outlistarr = [internal_to_flavor(arr, flavor) for arr in listarr] return outlistarr def _read_coordinates(self, coords): """Read rows specified in `coords`.""" rows = [] for coord in coords: rows.append(self.read(coord)[0]) return rows def _g_copy_with_stats(self, group, name, start, stop, step, title, filters, chunkshape, _log, **kwargs): """Private part of Leaf.copy() for each kind of leaf.""" # Build the new VLArray object object = VLArray( group, name, self.atom, title=title, filters=filters, expectedrows=self._v_expectedrows, chunkshape=chunkshape, _log=_log) # Now, fill the new vlarray with values from the old one # This is not buffered because we cannot forsee the length # of each record. So, the safest would be a copy row by row. # In the future, some analysis can be done in order to buffer # the copy process. nrowsinbuf = 1 (start, stop, step) = self._process_range_read(start, stop, step) # Optimized version (no conversions, no type and shape checks, etc...) nrowscopied = SizeType(0) nbytes = 0 if not hasattr(self.atom, 'size'): # it is a pseudo-atom atomsize = self.atom.base.size else: atomsize = self.atom.size for start2 in xrange(start, stop, step * nrowsinbuf): # Save the records on disk stop2 = start2 + step * nrowsinbuf if stop2 > stop: stop2 = stop nparr = self._read_array(start=start2, stop=stop2, step=step)[0] nobjects = nparr.shape[0] object._append(nparr, nobjects) nbytes += nobjects * atomsize nrowscopied += 1 object.nrows = nrowscopied return (object, nbytes) _g_copyWithStats = previous_api(_g_copy_with_stats) def __repr__(self): """This provides more metainfo in addition to standard __str__""" return """%s atom = %r byteorder = %r nrows = %s flavor = %r""" % (self, self.atom, self.byteorder, self.nrows, self.flavor) PyTables-v.3.1.1/utils/000077500000000000000000000000001231437614300146475ustar00rootroot00000000000000PyTables-v.3.1.1/utils/pt2to3000077500000000000000000000001041231437614300157230ustar00rootroot00000000000000#!/usr/bin/env python from tables.scripts.pt2to3 import main main() PyTables-v.3.1.1/utils/ptdump000077500000000000000000000001041231437614300161010ustar00rootroot00000000000000#!/usr/bin/env python from tables.scripts.ptdump import main main() PyTables-v.3.1.1/utils/ptrepack000077500000000000000000000001061231437614300164030ustar00rootroot00000000000000#!/usr/bin/env python from tables.scripts.ptrepack import main main()