pax_global_header00006660000000000000000000000064147145165100014516gustar00rootroot0000000000000052 comment=a0ec05bbb4da7d845caf81b10022b9455ec66363 pyrgg-1.6/000077500000000000000000000000001471451651000125145ustar00rootroot00000000000000pyrgg-1.6/.coveragerc000066400000000000000000000002531471451651000146350ustar00rootroot00000000000000[run] branch = True omit = */pyrgg/__main__.py */pyrgg/__init__.py [report] # Regexes for lines to exclude from consideration exclude_lines = pragma: no cover pyrgg-1.6/.github/000077500000000000000000000000001471451651000140545ustar00rootroot00000000000000pyrgg-1.6/.github/CODE_OF_CONDUCT.md000066400000000000000000000064231471451651000166600ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at info@pyrgg.site. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq pyrgg-1.6/.github/CONTRIBUTING.md000066400000000000000000000013101471451651000163000ustar00rootroot00000000000000# Contribution Changes and improvements are more than welcome! ❤️ Feel free to fork and open a pull request. Please consider the following : 1. Fork it! 2. Create your feature branch (under `dev` branch) 3. Add your functions/methods to proper files 4. Add standard `docstring` to your functions/methods 5. Add tests for your functions/methods (`doctest` testcases in `test` folder) 6. Pass all CI tests 7. Update `References` section in `README.md` (`MLA` format) 8. Update `CHANGELOG.md` - Describe changes under `[Unreleased]` section 9. Update `AUTHORS.md` - Add your name under `# Other Contributors #` section 10. Submit a pull request into `dev` (please complete the pull request template)pyrgg-1.6/.github/FUNDING.yml000066400000000000000000000000521471451651000156660ustar00rootroot00000000000000custom: https://www.pyrgg.site/donate.htmlpyrgg-1.6/.github/ISSUE_TEMPLATE/000077500000000000000000000000001471451651000162375ustar00rootroot00000000000000pyrgg-1.6/.github/ISSUE_TEMPLATE/bug_report.yml000066400000000000000000000054171471451651000211410ustar00rootroot00000000000000name: Bug Report description: File a bug report title: "[Bug]: " body: - type: markdown attributes: value: | Thanks for your time to fill out this bug report! - type: input id: contact attributes: label: Contact details description: How can we get in touch with you if we need more info? placeholder: ex. email@example.com validations: required: false - type: textarea id: what-happened attributes: label: What happened? description: Provide a clear and concise description of what the bug is. placeholder: > Tell us a description of the bug. validations: required: true - type: textarea id: step-to-reproduce attributes: label: Steps to reproduce description: Provide details of how to reproduce the bug. placeholder: > ex. 1. Go to '...' validations: required: true - type: textarea id: expected-behavior attributes: label: Expected behavior description: What did you expect to happen? placeholder: > ex. I expected '...' to happen validations: required: true - type: textarea id: actual-behavior attributes: label: Actual behavior description: What did actually happen? placeholder: > ex. Instead '...' happened validations: required: true - type: dropdown id: operating-system attributes: label: Operating system description: Which operating system are you using? options: - Windows - macOS - Linux default: 0 validations: required: true - type: dropdown id: python-version attributes: label: Python version description: Which version of Python are you using? options: - Python 3.13 - Python 3.12 - Python 3.11 - Python 3.10 - Python 3.9 - Python 3.8 - Python 3.7 - Python 3.6 - Python 3.5 default: 1 validations: required: true - type: dropdown id: Pyrgg-version attributes: label: PyRGG version description: Which version of PyRGG are you using? options: - PyRGG 1.6 - PyRGG 1.5 - PyRGG 1.4 - PyRGG 1.3 - PyRGG 1.2 - PyRGG 1.1 - PyRGG 1.0 - PyRGG 0.9 - PyRGG 0.8 - PyRGG 0.7 - PyRGG 0.6 - PyRGG 0.5 - PyRGG 0.4 - PyRGG 0.3 - PyRGG 0.2 - PyRGG 0.1 default: 0 validations: required: true - type: textarea id: logs attributes: label: Relevant log output description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. render: shell pyrgg-1.6/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000002641471451651000202310ustar00rootroot00000000000000blank_issues_enabled: false contact_links: - name: Discord url: https://discord.com/invite/dfYAWVMaCW about: Ask questions and discuss with other PyRGG community members pyrgg-1.6/.github/ISSUE_TEMPLATE/feature_request.yml000066400000000000000000000017071471451651000221720ustar00rootroot00000000000000name: Feature Request description: Suggest a feature for this project title: "[Feature]: " body: - type: textarea id: description attributes: label: Describe the feature you want to add placeholder: > I'd like to be able to [...] validations: required: true - type: textarea id: possible-solution attributes: label: Describe your proposed solution placeholder: > I think this could be done by [...] validations: required: false - type: textarea id: alternatives attributes: label: Describe alternatives you've considered, if relevant placeholder: > Another way to do this would be [...] validations: required: false - type: textarea id: additional-context attributes: label: Additional context placeholder: > Add any other context or screenshots about the feature request here. validations: required: false pyrgg-1.6/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000001601471451651000176520ustar00rootroot00000000000000#### Reference Issues/PRs #### What does this implement/fix? Explain your changes. #### Any other comments? pyrgg-1.6/.github/dependabot.yml000066400000000000000000000002451471451651000167050ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: pip directory: "/" schedule: interval: weekly time: "01:30" open-pull-requests-limit: 10 target-branch: dev pyrgg-1.6/.github/workflows/000077500000000000000000000000001471451651000161115ustar00rootroot00000000000000pyrgg-1.6/.github/workflows/publish_conda.yaml000066400000000000000000000006721471451651000216140ustar00rootroot00000000000000name: publish_conda on: push: # Sequence of patterns matched against refs/tags tags: - '*' # Push events to matching v*, i.e. v1.0, v20.15.10 jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: publish-to-conda uses: sepandhaghighi/conda-package-publish-action@v1.2 with: subDir: 'otherfile' AnacondaToken: ${{ secrets.ANACONDA_TOKEN }} pyrgg-1.6/.github/workflows/publish_pypi.yml000066400000000000000000000017551471451651000213530ustar00rootroot00000000000000# This workflow will upload a Python Package using Twine when a release is created # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries name: Upload Python Package on: push: # Sequence of patterns matched against refs/tags tags: - '*' # Push events to matching v*, i.e. v1.0, v20.15.10 jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v1 with: python-version: '3.x' - name: Install dependencies run: | python -m pip install --upgrade pip pip install setuptools wheel twine - name: Build and publish env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | python setup.py sdist bdist_wheel twine upload dist/*.tar.gz twine upload dist/*.whl pyrgg-1.6/.github/workflows/test.yml000066400000000000000000000044601471451651000176170ustar00rootroot00000000000000# This workflow will install Python dependencies, run tests with a variety of Python versions # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions name: CI on: push: branches: - master - dev pull_request: branches: - master - dev env: TEST_PYTHON_VERSION: 3.9 TEST_OS: 'ubuntu-20.04' jobs: build: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-20.04, windows-2022, macOS-13] python-version: [3.6, 3.7, 3.8, 3.9, 3.10.0, 3.11.0, 3.12.0, 3.13.0] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Installation run: | python -m pip install --upgrade pip pip install . - name: First test run: | pyrgg test pyrgg --version - name: Test requirements Installation run: | python otherfile/requirements-splitter.py pip install --upgrade --upgrade-strategy=only-if-needed -r test-requirements.txt - name: Test with pytest run: | python -m pytest test --cov=pyrgg --cov-report=term - name: Version check run: | python otherfile/version_check.py if: matrix.python-version == env.TEST_PYTHON_VERSION - name: Other tests run: | python -m vulture pyrgg/ otherfile/ setup.py --min-confidence 65 --exclude=__init__.py --sort-by-size python -m bandit -r pyrgg -s B311,B403 python -m pydocstyle -v --match-dir=pyrgg if: matrix.python-version == env.TEST_PYTHON_VERSION - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: fail_ci_if_error: false if: matrix.python-version == env.TEST_PYTHON_VERSION && matrix.os == env.TEST_OS - name: cProfile - PyRGG Engine run: | python -m cProfile -s cumtime otherfile/profiles/pyrgg_profile.py - name: cProfile - Erdos Reyni Gilbert Engine run: | python -m cProfile -s cumtime otherfile/profiles/erg_profile.py - name: cProfile - Erdos Reyni Engine run: | python -m cProfile -s cumtime otherfile/profiles/er_profile.py pyrgg-1.6/.gitignore000066400000000000000000000024101471451651000145010ustar00rootroot00000000000000# Created by .ignore support plugin (hsz.mobi) ### Python template # Byte-compiled / optimized / DLL files node_modules/ datasets/ __pycache__/ *.py[cod] *$py.class *.gr *.json *.csv *.wel *.lp *.p *.dl *.tgf # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover .hypothesis/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # dotenv .env # virtualenv .venv/ venv/ ENV/ # Spyder project settings .spyderproject # Rope project settings .ropeproject ### Example user template template ### Example user template # IntelliJ project files .idea *.iml out gen pyrgg-1.6/AUTHORS.md000066400000000000000000000006131471451651000141630ustar00rootroot00000000000000# Core Developers ---------- - Sepand Haghighi - Open Science Laboratory ([Github](https://github.com/sepandhaghighi)) - Sadra Sabouri - Open Science Laboratory ([Github](https://github.com/sadrasabouri)) # Other Contributors ---------- - [@zhmbshr](https://github.com/zhmbshr) ** - [@ivanovmg](https://github.com/ivanovmg) - [@ahmadsalimi](https://github.com/ahmadsalimi) ** Graphic designerpyrgg-1.6/CHANGELOG.md000066400000000000000000000152471471451651000143360ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [1.6] - 2024-11-13 ### Added - `pyrgg.engines.erdos_reyni` module - `save_log` function ### Changed - PyPI badge in `README.md` updated - `logger` function format for `erdos_reyni_gilbert` changed - GitHub actions are limited to the `dev` and `master` branches - `README.md` modified - `build_exe.bat` modified - `Python 3.13` added to `test.yml` ## [1.5] - 2024-09-16 ### Added - `feature_request.yml` template - `config.yml` for issue template - `pyrgg.engines` package - `pyrgg.engines.pyrgg` module - `pyrgg.engines.erdos_reyni_gilbert` module - `Erdős-Rényi-Gilbert` generation model - Generation engine menu - `handle_string` function - `handle_pos_int` function - `handle_output_format` function - `handle_engine` function - `SECURITY.md` ### Changed - Metadata in files modified - `Python 3.5` support dropped - Bug report template modified - Cprofile tests separated in files for engines - `README.md` modified - `Python 3.12` added to `test.yml` - Menu options bug fixed - Test system modified - `engine` parameter added to `logger` function - `MENU_ITEMS1` parameter changed to `MENU_ITEMS` - `MENU_ITEMS2` parameter changed to `PYRGG_ENGINE_PARAMS` - `_update_using_first_menu` function changed to `_update_using_menu` - `_update_using_second_menu` function changed to `_update_with_engine_params` - `ITEM_CONVERTORS` renamed to `ITEM_HANDLERS` - Website domain changed to [https://www.pyrgg.site](https://www.pyrgg.site) ### Removed - `dimacs_init` function ## [1.4] - 2023-07-06 ### Added - `check_for_config` function - `load_config` function - `save_config` function ### Changed - `README.md` modified - Logo changed - `codecov` removed from `dev-requirements.txt` - Test system modified - Error messages updated ## [1.3] - 2022-11-30 ### Added - Graphviz(DOT) format ### Changed - [asciinema](https://asciinema.org) instruction video updated - Test system modified - `README.md` modified - `Python 3.11` added to `test.yml` - CLI mode updated - `dev-requirements.txt` updated - To-do list moved to `TODO.md` ## [1.2] - 2022-09-07 ### Added - Anaconda workflow - Discord badge ### Changed - Menu optimized - Docstrings modified - `branch_gen` function modified - `edge_gen` function modified - `precision` and `min_edge` parameters added to `branch_gen` function - `random_edge` parameter removed from `branch_gen` function - Test system modified - `AUTHORS.md` updated - License updated - `README.md` modified - `Python 3.10` added to `test.yml` ### Removed - `sign_gen` function - `random_edge_limits` function ## [1.1] - 2021-06-09 ### Added - `requirements-splitter.py` - `is_weighted` function - `_write_properties_to_json` function - `PYRGG_TEST_MODE` parameter ### Changed - Test system modified - JSON, YAML and Pickle formats value changed from `string` to `number` - `properties` section added to JSON, YAML and Pickle formats - `_write_to_json` function renamed to `_write_data_to_json` - `logger` function modified - `time_convert` function modified - `branch_gen` function modified - References updated ## [1.0] - 2021-01-11 ### Added - Number of files option ### Changed - All flags type changed to `bool` - Menu optimized - The `logger` function enhanced. - Time format in the `logger` changed to `%Y-%m-%d %H:%M:%S` - `dl_maker` function modified - `tgf_maker` function modified - `gdf_maker` function modified - `run` function modified ## [0.9] - 2020-10-07 ### Added - GEXF format - Float weight support - `tox.ini` ### Changed - Menu optimized - `pyrgg.py` renamed to `graph_gen.py` - Other functions moved to `functions.py` - Test system modified - `params.py` refactored - `graph_gen.py` refactored - `functions.py` refactored - `weight_str_to_number` function renamed to `convert_str_to_number` - `branch_gen` function bugs fixed - `input_filter` function bug fixed - `gl_maker` function bug fixed - `CONTRIBUTING.md` updated - `AUTHORS.md` updated ### Removed - `print_test` function - `left_justify` function - `justify` function - `zero_insert` function ## [0.8] - 2020-08-19 ### Added - GDF format - GML format ### Changed - CLI snapshots updated - `AUTHORS.md` updated ## [0.7] - 2020-08-07 ### Added - Graph Line format ### Changed - Menu optimized ## [0.6] - 2020-07-24 ### Added - Matrix Market format ### Changed - `json_maker` function optimized - `dl_maker` function optimized - `tgf_maker` function optimized - `lp_maker` function optimized ## [0.5] - 2020-07-01 ### Added - TSV format - Multigraph control ### Changed - `branch_gen` function modified - Website changed to [https://www.pyrgg.ir](https://www.pyrgg.ir) ## [0.4] - 2020-06-17 ### Added - Self loop control - Github action ### Changed - `appveyor.yml` updated ## [0.3] - 2019-11-29 ### Added - `__version__` variable - `CHANGELOG.md` - `dev-requirements.txt` - `requirements.txt` - `CODE_OF_CONDUCT.md` - `ISSUE_TEMPLATE.md` - `PULL_REQUEST_TEMPLATE.md` - `CONTRIBUTING.md` - `version_check.py` - `pyrgg_profile.py` - Unweighted graph - Undirected graph - Exe version ### Changed - Test system modified - `README.md` modified - Docstrings modified - `get_input` function modified - `edge_gen` function modified - Parameters moved to `params.py` ## [0.2] - 2017-09-20 ### Added - CSV format - YAML format - Weighted edge list format (WEL) - ASP format - Trivial graph format (TGF) - UCINET DL format - Pickle format ## [0.1] - 2017-08-19 ### Added - DIMACS format - JSON format - README [Unreleased]: https://github.com/sepandhaghighi/pyrgg/compare/v1.6...dev [1.6]: https://github.com/sepandhaghighi/pyrgg/compare/v1.5...v1.6 [1.5]: https://github.com/sepandhaghighi/pyrgg/compare/v1.4...v1.5 [1.4]: https://github.com/sepandhaghighi/pyrgg/compare/v1.3...v1.4 [1.3]: https://github.com/sepandhaghighi/pyrgg/compare/v1.2...v1.3 [1.2]: https://github.com/sepandhaghighi/pyrgg/compare/v1.1...v1.2 [1.1]: https://github.com/sepandhaghighi/pyrgg/compare/v1.0...v1.1 [1.0]: https://github.com/sepandhaghighi/pyrgg/compare/v0.9...v1.0 [0.9]: https://github.com/sepandhaghighi/pyrgg/compare/v0.8...v0.9 [0.8]: https://github.com/sepandhaghighi/pyrgg/compare/v0.7...v0.8 [0.7]: https://github.com/sepandhaghighi/pyrgg/compare/v0.6...v0.7 [0.6]: https://github.com/sepandhaghighi/pyrgg/compare/v0.5...v0.6 [0.5]: https://github.com/sepandhaghighi/pyrgg/compare/v0.4...v0.5 [0.4]: https://github.com/sepandhaghighi/pyrgg/compare/v0.3...v0.4 [0.3]: https://github.com/sepandhaghighi/pyrgg/compare/v0.2...v0.3 [0.2]: https://github.com/sepandhaghighi/pyrgg/compare/v0.1...v0.2 [0.1]: https://github.com/sepandhaghighi/pyrgg/compare/1e238cd...v0.1 pyrgg-1.6/LICENSE000066400000000000000000000021221471451651000135160ustar00rootroot00000000000000MIT License Copyright (c) 2017 Sepand Haghighi Copyright (c) 2021 Sadra Sabouri 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. pyrgg-1.6/MANIFEST.in000066400000000000000000000002561471451651000142550ustar00rootroot00000000000000include LICENSE include *.md include *.spec include *.txt include *.yml include *.ini recursive-include paper *.bib recursive-include paper *.md recursive-include paper *.pdfpyrgg-1.6/PYRGG.spec000066400000000000000000000014571471451651000142670ustar00rootroot00000000000000# -*- mode: python -*- block_cipher = None pyrgg_version = "1.6" a = Analysis(['pyrgg/__main__.py'], pathex=['pyrgg'], binaries=[], datas=[], hiddenimports=[], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, name='PYRGG-'+pyrgg_version, debug=False, strip=False, upx=True, runtime_tmpdir=None, icon='otherfile/icon.ico', version="otherfile/Version.rc", console=True ) pyrgg-1.6/README.md000066400000000000000000000455271471451651000140100ustar00rootroot00000000000000

PyRGG: Python Random Graph Generator

PyPI version Codecov built with Python3 Discord Channel
## Overview

PyRGG is a user-friendly synthetic random graph generator that is written in Python and supports multiple graph file formats, such as DIMACS-Graph files. It can generate graphs of various sizes and is specifically designed to create input files for a wide range of graph-based research applications, including testing, benchmarking, and performance analysis of graph processing frameworks. PyRGG is aimed at computer scientists who are studying graph algorithms and graph processing frameworks.

Open Hub
PyPI Counter PyPI Downloads
Github Stars
Branch master dev
CI
Code Quality codebeat badge CodeFactor
## Installation ### PyPI - Check [Python Packaging User Guide](https://packaging.python.org/installing/) - `pip install pyrgg==1.6` ### Source Code - Download [Version 1.6](https://github.com/sepandhaghighi/pyrgg/archive/v1.6.zip) or [Latest Source ](https://github.com/sepandhaghighi/pyrgg/archive/dev.zip) - `pip install .` ### Conda - Check [Conda Managing Package](https://conda.io) - `conda install -c sepandhaghighi pyrgg` ### Exe Version ⚠️ Only Windows ⚠️ For PyRGG targeting Windows < 10, the user needs to take special care to include the Visual C++ run-time `.dlls`(for more information visit [here](https://pyinstaller.org/en/v3.3.1/usage.html#windows)) - Download [Exe-Version 1.6](https://github.com/sepandhaghighi/pyrgg/releases/download/v1.6/PYRGG-1.6.exe) - Run `PYRGG-1.6.exe` ### System Requirements PyRGG will likely run on a modern dual core PC. Typical configuration is: - Dual Core CPU (2.0 Ghz+) - 4GB of RAM ⚠️ Note that it may run on lower end equipment though good performance is not guaranteed ## Usage - Open `CMD` (Windows) or `Terminal` (Linux) - Run `pyrgg` or `python -m pyrgg` (or run `PYRGG.exe`) - Enter data
## Engines ### PyRGG
Parameter Description
Vertices Number The total number of vertices in the graph
Min Edge Number The minimum number of edges connected to each vertex
Max Edge Number The maximum number of edges connected to each vertex
Weighted / Unweighted Specifies whether the graph is weighted or unweighted
Min Weight The minimum weight of the edges (if weighted)
Max Weight The maximum weight of the edges (if weighted)
Signed / Unsigned Specifies whether the edge weights are signed or unsigned
Directed / Undirected Specifies whether the graph is directed or undirected
Self Loop / No Self Loop Specifies whether self-loop is allowed or not
Simple / Multigraph Specifies whether the graph is a simple graph or a multigraph
### Erdős–Rényi-Gilbert
Parameter Description
Vertices Number The total number of vertices in the graph
Probability The probability for edge creation between any two vertices
Directed / Undirected Specifies whether the graph is directed or undirected
### Erdős–Rényi
Parameter Description
Vertices Number The total number of vertices in the graph
Edge Number The total number of edges in the graph
Directed / Undirected Specifies whether the graph is directed or undirected
## Supported Formats ### DIMACS ``` p sp a . . . a ``` * [Document](http://www.diag.uniroma1.it/challenge9/format.shtml) * [Sample 1](https://www.dropbox.com/s/i80tnwuuv4iyqet/100.gr.gz?dl=0) (100 Vertices , 3KB) * [Sample 2](https://www.dropbox.com/s/lqk42pwu7o4xauv/1000.gr.gz?dl=0) (1000 Vertices , 13KB) * [Sample 3](https://www.dropbox.com/s/93dp8cjs6lnu83u/1000000.gr.gz?dl=0) (1000000 Vertices , 7MB) * [Sample 4](https://www.dropbox.com/s/rrxdc4wt0ldonfk/5000000.gr.gz?dl=0) (5000000 Vertices , 37MB) ### CSV ``` ,, . . . ,, ``` * [Document](https://en.wikipedia.org/wiki/Comma-separated_values) * [Sample 1](https://www.dropbox.com/s/dmld0eadftnatr5/100.csv?dl=0) (100 Vertices , 3KB) * [Sample 2](https://www.dropbox.com/s/juxah4nwamzdegr/1000.csv?dl=0) (1000 Vertices , 51KB) ### TSV ``` . . . ``` * [Document](https://en.wikipedia.org/wiki/Tab-separated_values) * [Sample 1](https://www.dropbox.com/s/j3zgs4kx2paxe75/100.tsv?dl=0) (100 Vertices , 29KB) * [Sample 2](https://www.dropbox.com/s/ykagmjgwlpim6dq/1000.tsv?dl=0) (1000 Vertices , 420KB) ### JSON ``` { "properties": { "directed": true, "signed": true, "multigraph": true, "weighted": true, "self_loop": true }, "graph": { "nodes":[ { "id": 1 }, . . . { "id": n } ], "edges":[ { "source": head_1, "target": tail_1, "weight": weight_1 }, . . . { "source": head_n, "target": tail_n, "weight": weight_n } ] } } ``` * [Document](https://en.wikipedia.org/wiki/JSON) * [Sample 1](https://www.dropbox.com/s/yvevoyb8559nytb/100.json?dl=0) (100 Vertices , 26KB) * [Sample 2](https://www.dropbox.com/s/f6kljlch7p2rfhy/1000.json?dl=0) (1000 Vertices , 494KB) ### YAML ``` graph: edges: - source: head_1 target: tail_1 weight: weight_1 . . . - source: head_n target: tail_n weight: weight_n nodes: - id: 1 . . . - id: n properties: directed: true multigraph: true self_loop: true signed: true weighted: true ``` * [Document](https://en.wikipedia.org/wiki/YAML) * [Sample 1](https://www.dropbox.com/s/9seljohtoqjzjzy/30.yaml?dl=0) (30 Vertices , 6KB) * [Sample 2](https://www.dropbox.com/s/wtfh38rgmn29npi/100.yaml?dl=0) (100 Vertices , 35KB) ### Weighted Edge List ``` . . . ``` * [Document](http://www.cs.cmu.edu/~pbbs/benchmarks/graphIO.html) * [Sample 1](https://www.dropbox.com/s/moie1xb2wj90y33/100.wel?dl=0) (100 Vertices , 5KB) * [Sample 2](https://www.dropbox.com/s/h6pohl60okhdnt7/1000.wel?dl=0) (1000 Vertices , 192KB) ### ASP ``` node(1). . . . node(n). edge(head_1,tail_1,weight_1). . . . edge(head_n,tail_n,weight_n). ``` * [Document](https://www.mat.unical.it/aspcomp2013/MaximalClique) * [Sample 1](https://www.dropbox.com/s/4bufa1m4uamv48z/100.lp?dl=0) (100 Vertices , 7KB) * [Sample 2](https://www.dropbox.com/s/w79fh1qva64namw/1000.lp?dl=0) (1000 Vertices , 76KB) ### Trivial Graph Format ``` 1 . . . n # 1 2 weight_1 . . . n k weight_n ``` * [Document](https://en.wikipedia.org/wiki/Trivial_Graph_Format) * [Sample 1](https://www.dropbox.com/s/tehb6f3gz2o5v9c/100.tgf?dl=0) (100 Vertices , 4KB) * [Sample 2](https://www.dropbox.com/s/9mjeq4w973189cc/1000.tgf?dl=0) (1000 Vertices , 61KB) ### UCINET DL Format ``` dl format=edgelist1 n= data: 1 2 weight_1 . . . n k weight_n ``` * [Document](https://sites.google.com/site/ucinetsoftware/home) * [Sample 1](https://www.dropbox.com/s/82wrl86uowwjud2/100.dl?dl=0) (100 Vertices , 8KB) * [Sample 2](https://www.dropbox.com/s/kbzbsy47uvfqdsi/1000.dl?dl=0) (1000 Vertices , 729KB) ### Matrix Market ``` %%MatrixMarket matrix coordinate real general . . . ``` * [Document](https://math.nist.gov/MatrixMarket/formats.html) * [Sample 1](https://www.dropbox.com/s/ztw3vg0roups82q/100.mtx?dl=0) (100 Vertices , 59KB) * [Sample 2](https://www.dropbox.com/s/skjjvbbzrpvryl4/1000.mtx?dl=0) (1000 Vertices , 1.8MB) ### Graph Line ``` : : ... : : : ... : . . . : : ... : ``` * [Sample 1](https://www.dropbox.com/s/obmmb5nw1lca9z3/100.gl?dl=0) (100 Vertices , 17KB) * [Sample 2](https://www.dropbox.com/s/intufsbudnmfv8m/1000.gl?dl=0) (1000 Vertices , 2.4MB) ### GDF ``` nodedef>name VARCHAR,label VARCHAR node_1,node_1_label node_2,node_2_label . . . node_n,node_n_label edgedef>node1 VARCHAR,node2 VARCHAR, weight DOUBLE node_1,node_2,weight_1 node_1,node_3,weight_2 . . . node_n,node_2,weight_n ``` * [Sample 1](https://www.dropbox.com/s/7dqox0f8e1f859s/100.gdf?dl=0) (100 Vertices , 21KB) * [Sample 2](https://www.dropbox.com/s/xabjzpp0p5sr4b9/1000.gdf?dl=0) (1000 Vertices , 690KB) ### GML ``` graph [ multigraph 0 directed 0 node [ id 1 label "Node 1" ] node [ id 2 label "Node 2" ] . . . node [ id n label "Node n" ] edge [ source 1 target 2 value W1 ] edge [ source 2 target 4 value W2 ] . . . edge [ source n target r value Wn ] ] ``` * [Document](https://en.wikipedia.org/wiki/Graph_Modelling_Language) * [Sample 1](https://www.dropbox.com/s/g9uvywn1fwt9aq7/100.gml?dl=0) (100 Vertices , 120KB) * [Sample 2](https://www.dropbox.com/s/5gt5udezy56mlz9/1000.gml?dl=0) (1000 Vertices , 2.4MB) ### GEXF ``` PyRGG File Name ... ... ``` * [Document](https://github.com/gephi/gexf/wiki/Basic-Concepts#network-topology) * [Sample 1](https://www.dropbox.com/s/kgx8xl9j0dpk4us/100.gexf?dl=0) (100 Vertices , 63KB) * [Sample 2](https://www.dropbox.com/s/7a380kf35buvusr/1000.gexf?dl=0) (1000 Vertices , 6.4MB) ### Graphviz ``` graph example { node1 -- node2 [weight=W1]; node3 -- node4 [weight=W2]; node1 -- node3 [weight=W3]; . . . } ``` * [Document](https://graphviz.org/doc/info/lang.html) * [Sample 1](https://www.dropbox.com/s/ukev1hi4kguomri/100.gv?dl=0) (100 Vertices , 11KB) * [Sample 2](https://www.dropbox.com/s/vpvvliz96mdea1p/1000.gv?dl=0) (1000 Vertices , 106KB) * [Online Visualization](https://dreampuf.github.io/GraphvizOnline/) ### Pickle ⚠️ Binary format * [Document](https://docs.python.org/3.10/library/pickle.html) * [Sample 1](https://www.dropbox.com/s/4s8zt9i13z39gts/100.p?dl=0) (100 Vertices , 12KB) * [Sample 2](https://www.dropbox.com/s/fzurqu5au0p1b54/1000.p?dl=0) (1000 Vertices , 340KB) ## Issues & Bug Reports Just fill an issue and describe it. We'll check it ASAP! or send an email to [info@pyrgg.site](mailto:info@pyrgg.site "info@pyrgg.site"). You can also join our discord server Discord Channel ## Citing If you use PyRGG in your research, please cite the [JOSS paper](http://joss.theoj.org/papers/da33f691984d9a35f66ff93a391bbc26 "PyRGG JOSS Paper") ;-)
@article{Haghighi2017,
  doi = {10.21105/joss.00331},
  url = {https://doi.org/10.21105/joss.00331},
  year  = {2017},
  month = {sep},
  publisher = {The Open Journal},
  volume = {2},
  number = {17},
  author = {Sepand Haghighi},
  title = {Pyrgg: Python Random Graph Generator},
  journal = {The Journal of Open Source Software}
}
JOSS
Zenodo DOI
## References
1- 9th DIMACS Implementation Challenge - Shortest Paths
2- Problem Based Benchmark Suite
3- MaximalClique - ASP Competition 2013
4- Pitas, Ioannis, ed. Graph-based social media analysis. Vol. 39. CRC Press, 2016.
5- Roughan, Matthew, and Jonathan Tuke. "The hitchhikers guide to sharing graph data." 2015 3rd International Conference on Future Internet of Things and Cloud. IEEE, 2015.
6- Borgatti, Stephen P., Martin G. Everett, and Linton C. Freeman. "Ucinet for Windows: Software for social network analysis." Harvard, MA: analytic technologies 6 (2002).
7- Matrix Market: File Formats
8- Social Network Visualizer
9- Adar, Eytan. "GUESS: a language and interface for graph exploration." Proceedings of the SIGCHI conference on Human Factors in computing systems. 2006.
10- Skiena, Steven S. The algorithm design manual. Springer International Publishing, 2020.
11- Chakrabarti, Deepayan, Yiping Zhan, and Christos Faloutsos. "R-MAT: A recursive model for graph mining." Proceedings of the 2004 SIAM International Conference on Data Mining. Society for Industrial and Applied Mathematics, 2004.
12- Zhong, Jianlong, and Bingsheng He. "An overview of medusa: simplified graph processing on gpus." ACM SIGPLAN Notices 47.8 (2012): 283-284.
13- Ellson, John, et al. "Graphviz and dynagraph—static and dynamic graph drawing tools." Graph drawing software. Springer, Berlin, Heidelberg, 2004. 127-148.
14- Gilbert, Edgar N. "Random graphs." The Annals of Mathematical Statistics 30.4 (1959): 1141-1144.
15- Erdős, Paul, and Alfréd Rényi. "On the strength of connectedness of a random graph." Acta Mathematica Hungarica 12.1 (1961): 261-267.
## Show Your Support

Star This Repo

Give a ⭐️ if this project helped you!

Donate to Our Project

If you do like our project and we hope that you do, can you please support us? Our project is not and is never going to be working for profit. We need the money just so we can continue doing what we do ;-) . PyRGG Donation pyrgg-1.6/SECURITY.md000066400000000000000000000007501471451651000143070ustar00rootroot00000000000000# Security Policy ## Supported Versions | Version | Supported | | ------------- | ------------------ | | 1.6 | :white_check_mark: | | < 1.6 | :x: | ## Reporting a Vulnerability Please report security vulnerabilities by email to [info@pyrgg.site](mailto:info@pyrgg.site "info@pyrgg.site"). If the security vulnerability is accepted, a dedicated bugfix release will be issued as soon as possible (depending on the complexity of the fix).pyrgg-1.6/autopep8.bat000066400000000000000000000006161471451651000147540ustar00rootroot00000000000000python -m autopep8 pyrgg --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --max-line-length 120 --verbose python -m autopep8 setup.py --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --max-line-length 120 --verbose python -m autopep8 otherfile/version_check.py --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --max-line-length 120 --verbosepyrgg-1.6/build_exe.bat000066400000000000000000000004211471451651000151410ustar00rootroot00000000000000@echo off FOR /F "tokens=* USEBACKQ" %%F IN (`python --version`) DO ( SET py_version_str=%%F ) SET py_version=%py_version_str:~7% echo Your Python Version : %py_version% echo Recommended Python Version : ^>= 3.6 echo ----- echo ----- python -m PyInstaller PYRGG.spec pausepyrgg-1.6/codecov.yml000066400000000000000000000003071471451651000146610ustar00rootroot00000000000000codecov: require_ci_to_pass: yes coverage: precision: 2 round: up range: "70...100" status: patch: default: enabled: no project: default: threshold: 1% pyrgg-1.6/dev-requirements.txt000066400000000000000000000002551471451651000165560ustar00rootroot00000000000000art==6.3 pyyaml==6.0.2 scipy>=1.2.0 networkx>=2.1 pydot>=1.2.4 pytest>=4.3.1 pytest-cov>=2.6.1 setuptools>=40.8.0 vulture>=1.0 bandit>=1.5.1 pydocstyle>=3.0.0 flake8>=3.5.0 pyrgg-1.6/otherfile/000077500000000000000000000000001471451651000144755ustar00rootroot00000000000000pyrgg-1.6/otherfile/RELEASE.md000066400000000000000000000031341471451651000161000ustar00rootroot00000000000000# PyRGG Release Instructions **Last Update: 2024-10-06** 1. Create the `release` branch under `dev` 2. Update all version tags 1. `setup.py` 2. `README.md` 3. `SECURITY.md` 4. `PYRGG.spec` 5. `otherfile/version_check.py` 6. `otherfile/meta.yaml` 7. `otherfile/Version.rc` 8. `pyrgg/params.py` 3. Update `CHANGELOG.md` 1. Add a new header under `Unreleased` section (Example: `## [0.1] - 2022-08-17`) 2. Add a new compare link to the end of the file (Example: `[0.2]: https://github.com/sepandhaghighi/pyrgg/compare/v0.1...v0.2`) 3. Update `dev` compare link (Example: `[Unreleased]: https://github.com/sepandhaghighi/pyrgg/compare/v0.2...dev`) 4. Update `.github/ISSUE_TEMPLATE/bug_report.yml` 1. Add new version tag to `PyRGG version` dropbox options 5. Create a PR from `release` to `dev` 1. Title: `Version x.x` (Example: `Version 0.1`) 2. Tag all related issues 3. Labels: `release` 4. Set milestone 5. Wait for all CI pass 6. Need review (**1** reviewer) 7. Squash and merge 8. Delete `release` branch 6. Merge `dev` branch into `master` 1. Checkout to `master` 2. `git merge dev` 3. `git push origin master` 4. Wait for all CI pass 7. Build EXE file 1. Run `build_exe.bat` (Use `Python >= 3.6`) 8. Create a new release 1. Target branch: `master` 2. Tag: `vx.x` (Example: `v0.1`) 3. Title: `Version x.x` (Example: `Version 0.1`) 4. Copy changelogs 5. Tag all related issues 6. Upload EXE file 9. Bump!! 10. Close this version issues 11. Close milestone 12. Update website 1. `git checkout gh-pages` 2. Update all version tags 1. `index.html` 3. Update size of files 1. `index.html`pyrgg-1.6/otherfile/Version.rc000066400000000000000000000014601471451651000164510ustar00rootroot00000000000000VSVersionInfo( ffi=FixedFileInfo( filevers=(1, 6, 0, 0), prodvers=(1, 6, 0, 0), mask=0x3f, flags=0x0, OS=0x40004, fileType=0x1, subtype=0x0, date=(0, 0) ), kids=[ StringFileInfo( [ StringTable( u'040904B0', [StringStruct(u'CompanyName', u'PyRGG Development Team'), StringStruct(u'FileDescription', u'PYRGG.exe'), StringStruct(u'FileVersion', u'1.6.0.0'), StringStruct(u'InternalName', u'PYRGG.exe'), StringStruct(u'LegalCopyright', u'Copyright (c) 2024 PyRGG Development Team'), StringStruct(u'OriginalFilename', u'PYRGG.exe'), StringStruct(u'ProductName', u'PyRGG'), StringStruct(u'ProductVersion', u'1, 6, 0, 0')]) ]), VarFileInfo([VarStruct(u'Translation', [1033, 1200])]) ] )pyrgg-1.6/otherfile/donate-button.png000066400000000000000000000177721471451651000200040ustar00rootroot00000000000000PNG  IHDRn pHYs  ~PLTEGpL000...333333000000000333,,,***&&& """(((000"""(((...&&&,,,&&&((( """&&&,,,,,,"""***&&& 333000...&&&(((666,,,$$$坝***:::<<<ٳ888Տ"""LLL>>>̅ RRRfffHHHVVVZZZFFF݇XXXᱱ^^^é@@@JJJPPPDDDBBBpppbbbttt\\\~~~NNN```rrrTTTjjjvvvnnn|||xxxzzzlllA5tRNS3Uwf"Dfݻݪw̪"3̈D3DDfDDfU3ẅvIDATx["GƓ6y&3y'Wٟ$.}kdߑeUtdPE$0¸FG_Uu6'R]S꣏xo~ ym9=x-qgy5h=k ||Ɲq\?/}ˈGx1R<+~!o(s,{62~Q&®LDǘg<:|.܁K~ qh?:>>j|@hGWԿ(E>:8jyACrxg+g0D!Qy ~{_~Zt1Kş(j` s%N6HcH7MJ$1=E..>]Ԃb˛NjqCUZYM7j%F6555ǕX-ʤHlI*32A3l۩mSʺe̊SV$SattpHL(^aPk3)^TbWX1i:WAp6-%ެE ^{>ժ'cՀRQ50>\NtE^ MJNtD1`3KX,:zY~[pٰ9$3[duklR*hz9|tpsA*rO tCިV-w 71@PH"v J4zC܎<Ԅm*56R&{OgxH4uʓJ)?>cj\/_M5ϙOAPK>n`!jZF3tʦF* Ÿeeqqat1 la²G/ XTΘ,Sxrf3Cn",\6*etcŅ!ctHt\zOu%&,NiisW,jشD=S[|uUً)U +Ng9w[4Ct OY.LJS,:S$_IA<# @c[MSRx ^60j-fLw٦0uMAfyc$0EZ![}rb3g!㓗S?ȥڂn@ΦT-,7ix[n퐛ϑ;I-JT;,s](U:}d9E֛дgLqגѠt3֛qfi2SShfFMN1z<0{w/o0ߩ 0!yg8#c:7LsAI HޝWuæt6>fu5J'TnG] ƀYfYiBJU&Lcl*2˔6o>I/Ώ0$?hCVSAMipQiQc-pLJcx\ZJo[T.}UtT(< b-BOҀOY/j|`KBjr5}م:)謆gS]*+`RyMH"828I/` 2tHzT:| *s:^`?2 9̴Ph-6*o- ђnfCwQ%5ƻS3t&zNqј=0އfǿx0IIFtqʻnbGtٕ4A jӏth\ltb-]ō46 ;|+D2!bjjWzq=.B wT)XqLh[ <)*x@\T?|'8㤲tR)(KVm[Yw oG=bt1KDR=xOS'2RMg605|$+5Kx(aa}/ΰѹ`C(WUÇYP nP}vJr@`3w:ok== zWXwUf" g-0TnED ym򙆅~𐈱t $ԯloʐ'e/q`lcҗW0Ġ(cj^Z1dɺn $hqw̚TφqrU#F.‚B !,Y+D[TZe:T- k_I3߹M@e4A]Mڧqy0(ULP_/Q vR4t3Q$)]p{e/rMDŽ7y_[*7>СqnZrۇh,%iI_7#" v/oA[ o6B7M23ӥ - |RDtghPrnr(-7thWͳ|-~[iYe, :\ ѡb>];k6J^^4IYOK:?QŤwN^NFmt06-ExYa ӻ٠_.&'BT,KҐѠ X\tݓi:u׹ZؗaD۾h~-ӡ6k$ A֒YuHOrx32yC:0fr~:4+z;a?$gR5lafBs)tWj&kSk3Ipa@65zˁ@wϢa-轍8t8.#^z|zWK鰙="rK)' ɯ$KxU`&}NC!cxĩwYHccYj!>RTR(L@,~RH+:d$=/QI^J%9>¤Cl(I߱:MuQw@E]sBttȟ ()Hȏ4jh˙e;A i]'GtPVc:!J~5Jr,_CDrU1 ] Na}wd߱r7J/z[e&p.ՃƎ%W=Ae *,cWdʜQ-v22TFևGAk?!8t:4N"/QG9c6pݛ*av'5=A>nF6!{8Qɹ.F}g:Cp8Ր tE :I:4iuQ}E*_7g3c({͚=?䠩BK81+P"{5S<>\ƫ_hqQ̓^>Q2 q4W%k,kfcVp#Ia?UеD!č6-œ8QhIIDG7ECbpn]ąqCQota7%øJlD̻, RA|jO<138N!-VX+_ݛ,՞vPsRE[1P0lnK&;Aӡ#*Ra\EVQXR*92^،0pLkz{DnF2*sWSvpg t0M.ࠞ]|9|;R+*ܮ<1`9PrOua9{7mLof)!ti0}< Bdj7G>E$:m4CCk R:2&lD1%*|eDc9F J?v{k0Jƙ݈0ffyx$q&qH~nUKep wv~ě3{JcpnJdU*8㇞eXLԺ6 T*ƿh4k۳?[S^X,-=~2u~rXmk []ɔOv-:zXVΜZWi쿢u{liA%(c]ˤ˸>8qjd6'RՅZ$f>w ,[#i|~!:eh4k3~6)]HHnX BI q&Y;.pVt,#8yGֿnolF=|(y΂m]FmXV@X"3*QXUlzѨ<@6J/z.-ރ1ȩ8b06iE u7vtSno 6lfɖhP)g*o5CŕV ̮ۿ)x7 ~=MҠpI|=`n c,T@ocSz9:2ufdz ֱEֆkpߏgJ+|->eе士HWIԼ+:Ǿ[r+:xrgW+\䏻v1Ow3{mlR\1 HBs)$Y:HNXզ! tt_`ra^;y9&&.TTF% Z _ B,IAg?3]L W㢍R29LJ~|f=BCcx5DYu\Oz=? 2€RM1\xH0xb> LOB#EsJ^x{5Q]6jF9S#8Ly^W!Np*ehˊG(`$whMv& :J~$>uV)Iw~gkGetjj5zA_f!=ovJ!gs3MFypt遐C^t*{ތ`tNvZ[0.lLp頮tMG9u--yH4h)˱p5v$LؓMk<ѷHЯ4m:Җǘ!5u?ni-tp!18U5[bJJw_)[J>hF"!R)8o!xtE"ԉ=+u6U榧h>p>>SSS222~~~UUViiik777kVVV@@@454NNNHHHlllLLLkaaaTTTnnnUlllUAAA444___333/???/q)))q vvv___999xxxWXWhhh +bbbfff>>>AAA+^JJJeeennnddd^}}}sssZZZooo}}}EEElllSSSYYY|||PPPlmm;;;rrrSSSGGG;;;/XXXDDDmmmIJJjjjpppooo/TdddBBBhhh&&&;;;ttt+++eeeTzsss__`vvvCCClllzdee776qqp443jjjHHG777555ooo{{{@@@111ddduuu\\\hii;;;zzz565aaagggaaavvv;;;PPPgggCCCvvvccc111pppJJJeee888VVVuuu>?>^^^aaauuu===sss[[[___LLLxxx666~~~aaaqqqqqqdddvww\\\___JJJLLLmmm\\\mmm{{{lllEEE___GGGccc<<> RR@...nnn@ fQQQf !VVV777)))!.rrrqqqooojjjVVV.4uuufffcccUUUsssiih46)))>>>AAA***64FFFWXW4.|||HHH(((333wwwYYY.!KKK777mmmSSSddd!@@@333CCC+++___))) 887hhhGGF{{{888&&&kkkXYY''' fSSSLLLTTTOOO777vvv~mmmf@```UUUyyyeee^^^@zzz///^^^ccc...sssCCCwwwlll"""``` R///TTT***```CCCoooR mmlxxx WWWccc>ooo]]]DDDKKKYZZ> IJIKKK%%%JJJvwv222,,,wwwUUU S888555aab;;;___gggS^^^yzyKKK bbbMML~~~kkkxxyZIIJDDDvvv{{{ZKKK444wwwooo^^^|||555777kkk333QJJJRSR```888Q >>>555||| :ccc}}}OOO}}}{{{;;;9ssszzzwww lll;;; +++wwwuuubbbT}}}333mmm~~~T666 vvv`a`!!!000iiieeejjj Qiiixxw888+++[[[ VVV|||Q^^^tttijjYYYooo'''|||pppnnnnnn2"#"VVVrrrBBBjjj2]aaaABBvvv~~~PPPLLL888(((^^^]ffgiii|||vvv---}}}[[[,,,uuuOPPuuuRRRpppsstxxx222NNNTTUqrspppoooxxxhhh#***000hhhFFFnnn///mmmwww???#?||||}~++,CCC___uvvyyy~~~}}}kkk?`YYY}}~~~~QQQmmmxxx;;;`{{{gggnnnccc}}}###<<<}~}\]]QQQ!!!OOO222qqq[[[yyy222xxxZZZkkkddd***zzzQQQ^^^---777bbc```yyy000+,+RSRYYYpppvvv!!!555eee888)))''&^^^|||FFF|||+++vvvMMMabb###tttVVVuuuooorrrpppIII???OOO``_+++;;;999{{{888999///uuu565GHG///jjjggg<<>>CCC]]]DDDpqpttt~~~ZZZ+++9eeeJJJOOOqqq444DDDmmm: ***OOO]]]444IIIKKKsss666(((z{{ Q}~}EFElllQQQzzzQyyyooouvusss///"#"ZsssiiiGHH444hhhBBBrrrTTTZ___VVV///{{{vvv)))YYYPPPAAAbbbSCCCSSSOOOeeeRRREEDXXXS AAAHHH >www>{{{\\\qqqzzzKKK CCCDDDddd}}}999aaa}}}ttt RQQQLLL&&&___jjjR{{|nnnxxx555jjj^^^"""OOO---666vvvTTT,,,@vvvOOOUUUVVVsss333]]]FFF@fRRROOO###\\\\\\f {{{UUUEEEabaCCC %%%999~~~NNN777GGGCCC!UUU ~~~!.YYY666ffe---.4bbbkkkttt***///ttt46vvv@@@(((RRR64dddFGF***///4.qqq.!ooo! ff @@RR >> SS ZZQQ :9 TT QQ 2]]2#?``?#bbb????????????PNG  IHDR\rforNTϢwIDATxwxьʨ{oDG0ěw_6ޒlvNM@{MHOl.I3ss}s{$eF ‘aq#x|x;a~~ZE8.@%Bq9Gó{H,b2j E0gxt7s<_xVpXlC]'}Ph I 0ϜJ1|=&`;Bo[)# ,B2Qs/âAH~#ЗED ^"u}ggz{`xt#z^5 @ǁ.\@{{;|ᗄp À ,Jk 3s@4:b51iLaa!111XXXx}b.e͚5}j5|G? pgϞ=amm|r~_SW_GRRN\pbvM`` *ꞟx033 ܹRN8AMM 7nʾIsjg<%'zeB+壏>1lmm155E&!ܮjdjj5k.%Ɔ\rrr044vR3Oz I9CCCv[oNNNX[[cjjBחkbanL&crr,6oBLL FCii)zzzWD233CAANNN(JD*#%%W {{cշrғ#˴O8ľ-|ϛ+_gd2FGGD__,-),( 7/ZFFFt#У;w`BBBpvv&$$1._Lgg'Z"104S'Oq*I{GFcll NNN >O*bnn)gϞ͛7 FTz@R 6͛7quu%8$ǹx"88:>+N`(tO <#mg9ߗQ099IXX B}]ׯ_'-- ccc<kkk '##n ᬭٹs'!!!=zw466bbb²X\] _D̲KIް^8y2==,Ys'&& ::+V,4&i  <==IKKKKKv؁/gϞ׿57o&<<|A9D!<# COw+wD N|%011!!an...|r&&&ZΜ>F'''rss9~8$&&VERRAR111LMM1==Rd||FŅ˿$44T<~R__ /V,-=v \Nxx3gZfll ԃ1t::ggg\\]cʕ|'dggL&#""WWWϨ?>eDx;xN+uŻ<; R)˺u] /ilhӨ5ZE$ ЈvRCCCE㟘 5`ppDB?6mBR矋gyf>3ILLTTT,d<K9sիWt|}|HgY-Vs=Yp!<lPᓪx]}O 2 kkk Ãk׮188L&OOFۋ O}}=5LO+AA%allLII K`ii,[[Vϐ嘛s,--y077חb.]-r ` b##C+Xju\tG''sd2e2:;HKKcϞ=GTR|%@PܣiDPP?/Yn3j5>oHJJƆ>FF}v&&'pX[[o295͛7 ;;0000͍Z&''111a=|r sss^ؽKK$Aii)ME033CrQ33477ti NNĭXӧddd;w 4Dycbb)֭捛tvuLqI q:撉p򨫫cɒ%'$/"Hxq> h4?͍W^yz[[[z*:+WMv/mq/~A%K!bپ};!!!}}}Ԡ5hR)R{{{gdd>3듓ddd`ieŒ%KȠU}BADD>3nF8::IIq1333caiɅ4(//>#::i&nܸo~G}=?{xx_??/72;WFUJ#<=izj 'O@R eP.qӦ'$$G?(-//JEyYZ-Zv'<JAatt<WeF⿪Rw[<3nÿӅ`),,$YٳMLГJQybb"133#&&F(bh+{Nll,q><Ŏ={G駟r" Ғbcc 5͢YiLMM)++ 777J%GJEtT4v|( g7ZFP '::kk'D@Ǘ]-Ư@hLxw{`aailhVƍqttŋ |kk+MMMh4"##qvvcǘ'?e2#">CTr-3ʊ Ο;__->vvvo~!bNAAbA n{MLLprrXZZ2>>NiYsR5kٽ{78::VYl--->u={bnn.MOOW\add L&Q3`t_"B0{yI[{;xzzhhnnRFGG9r^^^b?s(..c5;TVTRfxxx888o>>>,& (..oV jZ$Gs Ο?籶F*$;v 99Y -[r.5;v^޼w/ܸqN\]] 2P<~Xɾ095FLuROyy9*0),,̌W_}Ԕ-[ѣa۶m011AWWׂ3/ё9$QQQVE@jtttpyinnFOpQ?`gljRDx9@211AAALMM111A\\NNN2RWCYot7<aD7vJD"!,,ӧO@__׳ajjjhnnҒ: iDFFi&LM?yf8r3l??;Gll,^^^n_?/`ݺu( jdjQ*K]]d2/ؘ۷nݺ;d2LM Cu… XZZ*V3MX BdX~ `C/}_FXm~5z077ʊA#fI J: $v2(-)a $&&UHCPyΜ9Cpp0XdžhllƍΑ#G JEdd]E.chhԴH,gxhSNaff+ qo_0d[/+ xG333 allyjN>MSSr=/y_ -[208Faa!VVlܸ(Q6\x0޽{O144V;/4j~(*,`==)666ؿp\ǟ|@PPN;Jd2P*YV__dyWٵk֭㷿-xyya`hFF&Hl)Hon!_>l޼OOO104$//G6aff͛7177DBmm-iBݝj*+ʊ1 ajj BCCU e0\4`q<`qU_=!cffu8~8CCSSS8R+W/3NNN 06B #8X(iii> sssilj".]eeeTVT055;;vFGGy8z(Gӓ~]v3DWWWy<㭐v=0333OښSN /JNNuuXXXo9V!?p^V ST%.ooor򰰰d//GF$d2bccyttvvMNNtZXZZbee/]]AMM 5>>8;9chdDoo/ ѣGH$!!a.,ˉwߥd1S,;wƆ Xx?D1vz\oA녱1EP5n|}}Q+pwsGV344t_ 0>>;[FRdttazzzDxjj&&&(*. KKKZ[hmmpvvܜW,`]]>>> nnnp BCCe@l‰DZ 44'OۋC~`c<`[( =Z^z%III!;+W}9Gƍ @OOBBZ-Q\\ Ble޽CCC hZΞ=X֬Y)vvvd^1 C&|rVUQZZJbNoccjZ=J$bbb&151Reb=xGrLOrejc0[ZSdgeۋ51[?{,?gh4갷Ϗ7o-0==M?MMMTWWʌJB!,]>غu*nܸ /@\\ކtΝ;HLL ͛7E[allN^!/LSG+@ݣFn1~=- IW ZM[[vvvr222xoJV%**R.^|{/_洷S__2 ֬Y8Y襗_&..N] LEܹsH`^$ Xl%%%zykddy744d͌RWW$Sz "~ h <#@™[qŌ ؐի)rnZ{=rssYz=ib͚5hZ ΦfT*mleӦM8;;cllLCCǏCwC`PR///$hiiYPP\zQv޽ Y'YnH$DBBB?lGsgҥ555qF\\\.\ڵk'#ceS\T5k9K$&''k}Y~=().Ȉ@n8v xGuI@ʓ^s6SPXH~~> [NR(;8PRR__{199I\\r}}xtq5!!44}/H@@ +W$..gggfffˮݻʕxq߾?hiiK| 8sÒ;ÜXz5gΜ!++K<;99͜y >r\}u?M q?(0)6x.Wytd2Ϗq\999,Y 6pQ[[[J%-P^^R$::aH888OGG!!ٲCQRR;w%Q\z1^xbs-[Fqq1_^3==}k=99)Fk[ & xʾ&ĒVNNט7^(..[X8s,j  ?я&44;v000@WWvvv$%%uuO//6oD{{;>֭Ғj5ZyrY͂s=V444JmM yyylxG:bg͚5={Yd DFFbkk\.wL&#..۷jiooGO2Ő`>?Mq*xzzOWWؠRM`MOd2/]C##:;;)))AV{exzzP5AĐZ˜?.&'Jw]hll͍ܜ 'f9h."066ӓfH=wOOx=Ŝٳg P\\Laa!6lZk2OQYYѣG$'cbb022wӸu0 9ea#'}O$''W^r  $** 6K/1<^^\tcc,,,011⅋ ?1LLLPVVƩSޞVb\@OOL&ɓξ)))Aq)m&&RϞ%)9H BPT.رctuv{n#3+n:)f,Y <==ٹsB Gp['}O;111TTT JEz.ruZF{xb hkk CCC'''xʪ* ٷoVb߾}/Me2nnnPYQ? իBQ.3:20*J$JL;:ҥKۋD"ȈV144DQ]]ͧ~Fa/bkkKNNKٲe s}vQK̀j Ht ˆǎS[SKrJghhu155Ezz:qǶ9s122 SSSQۛ^()-ET"ɰ07Ǣ MMӑNCш3gJxDyyyTVVhaռt:}]<==IHHxv3ncuVt:9y$===~^y|}}XkZ[[G٭"z{{jdddP[[˶Yd }!-l޼e˖U{vmVCH$BCCjڵhrsrؾc;U$$twInn.hZ qrrqhѼk :ut:cccŋaxdcǎȆ Irq/_B --~^}gw';A"X`哾'~RXXǬC(kjbbbR;;;֮]K\\MMMR\TDuu5?9aaa466Ik[>>>k͡Çqvqլ񇅅{061M|mffb`8:>x Z}b777|}}Efc+  "[Ark/gccCOoPJAHxzxQ[[Kyy}y SSSɑVn޼Illc%7'J1/*@s:~xyyEGGHR/_N]]/^E---8q>֯_OWWMMM? NNNT*100O <<̪Uxܹs\z vy_t'NGNNY,_J2 L--(z(--#GX;fMgm: xװ 55˗/ၗC]?OOO8s ---lܸ^q tZ-PSS͏~#3LOɉ 037 cl,_gqeff<ΦbccÞ{)))B2ݝLFGGf<<<ANN?񏩯ܜq|_P]]Mjj*ⰱAT%! Jr_VKaAG#,,[bllիyɓX[[@i#HHHH4`llZQQ$/"%%%nz}8qٶm0CnnR山Y,===f<<͛xxx ///ꘘӓ+Wr=;???<==yy'66 3;qn }}}F!))iAGG===wQT*˗/'44:::8z(^^^$$$p GE?)?0穄F!''cǏdn*fr9zzz455!hjjvJ IJNwd,-DoAt||TXb*BO? .]prrgqN=8;JGG+V`sack˩SbϞ=DEE;r997oH__/zR=BBBj#޻D"֖Z?w$::/Rv?w %%%QZZիWgk唗ڊ?066OLCphhÇ#Jٳg,A,dy #_j48}4+V`ƍ Fcc`zjE7nJ… jJd%2]v*HJJ=== ]8+~V]\uXsaE {9ho瓏?.xM̨JUCCJJK100p404ؘ'Or <==9p...>}r^x;C*/~ Ν; wgmm5q EO*ׯ ]FF}䢩)N- BUD}FFF tr<==qssͱC*ʕ+=znپ};!ϡÇqqq_ch)dVYN::: TUU!INNfzzSWWǞ={_`YԐ˩S(-δZ-k֬!88 DBss3LLMabbXXXP]]իWiG_.yyڵ8^^^\~yᶿ?o&ΝСCByy9yyyzjڵk\HKcٲe$* \vښLLMtillLJzN?xx?я؄_ K_?##Ì3==D"Dr}}33@j]\q}E%:Ogٲe81Iq7$fF ݼp"{4%w:幞llŠqL;ښа0JJJxHLLܹT6??> ^^^ZW}V]FCAa! /_sAʊ;wP(8|33ʦMXdɼɻKM%/?$﫤hffFdd0D.///Z-ׯ_DEEhcÆ XXXAAA333p1jGT"Aibj%~:)S^^.ʗ?(fffՕJKK_pdddDXXKZZ/_?6.7e [;;_`ͷ:,(399Iٳss_g8TJtt4EEE444zjz{{9qc 3IJ;m>^[[Kjj* fffnoo//`aaaAScӼK̸f|b[[[\\\pssEirooo<==Yz5r BCCYt)nnnt<ѣ~z) @||CU|닋98ੑZԩSkV1AD|}V-[pAN8033CVSRR}LLLظq#$++Nc Jύjim 1\~B 6$%D"!==Grj56'/z{333LOO3=;V[T2>>LNN244đ#GDq)=7Q\߂ W21:;; !>syq>&##}}}mλٱcQ*XZZOXXgΜ*BR6m̊xȹyI\\~~~Kѣcǎy$4 Y~0D퓷x0|Wv'ΆB~̷B.'|BC}=aa<|;w;v0<~~~8ߖdefQTTDlٲ\T*EOO]ww7W^%''^ٻw/t: HMM%KLii)111 HѠ'M"K/LqqX VˁLLLQIxpp˗/m۶{h4}TJXXyy444<@\^^^twwSPP@͛deeҥK/_&i6zee%ـ0Huz|D ظtHVSNQUU|Hʒ"<<<Ą":::ޞW^y122gRݝuq|}} ^֮]+&4 mmmdggSVV;{^jkk+Ljİ|re||\L&Nۉsqllm[?. Z-zl2|||8z(fffSr9nnn0999&&&lذ> ֭[wW]DZА8xθQZRBhhC7[IRprr">>Rrss)*,;{{B̙3,]˗388HCCeee%D"yRD}?4­+U@g t:/Ƴ,cffFDD$Ԕ*J***Dqss[[[,,,ן͍ ܹsX[[SXX#^h4Z[[̤ ;;; uuuuPubbg֭9rK/h' \xQT˗K\a/8pdgg2OP"m#^ B[o?02RpiHLL\899I_wvvfڵ|h4cjj1]!ᤥǷ=*,--Yr%>|_Kdd$XXX駟`===ynssxװbm6Q> w_㿾rсNctl1hkkc``14Z-t <*ccc 2 466OSv؁R]]MNN7oޤƦ&PTHg`###lllp7oޤk H8{,ϟCD>7oތ#d2JD;\6oތ666dgeD:6N:ŋ`-QRR pvvn-[Fyy9[(ŕLMO}O07W^]P(N"PR\<#/O}}=DGG/WQ\\L&w]N(\r/EYYJ^4 tvt`lbB||<7oYa$''c"##t] A O:/ v9::j*A7n$**Ajjj(gZD*`hd0+++lmm133CCEF[[[BBB#22ggg/i #}ϐ : M{{;mmmddd0=5%Hc[[JfNP(H$8::; Z4\711ŅF=NNNr gϤ VZٳgؘHz{{qVV8pOOO.^H~~>;v &&#۷o?ښ(rss U XP]]Çqpp`bDbb"~~~saHJJsssmlhkk:9psSQQQlٲTW^嗿% ߟ2X :Q[Z(++޾>ZT*144$fi wkϟޓo=#@ BhD`mm͟DeU%hZ4 266F__=== RRRj KKKA+KKML000@rYx뭷!&&ׯsY8 RBBٙhj5 ISSuuudegSTXJf~g?%===HMMepp''SSSC{G]p}~wժUlٲXR)N;w7Q( 7rV\>YYYڵKdIo4>,N 6 ;w\'WTSRR˗EEǁ 3gΠǸJROCCv֖JJK)--IFF[X`gg)8;;ビ֮#)x ,[ɉ [υ *NRdbbIe``@d\MMMhЗP ctt??h4RP)(U*fffZZFVJJBad#DGENu +3OO JsZRdzzQpmZtsi--,P*Hzz:x{y!G&!ɐF@Ԗ@--- 5kܵƆ4皚zzzطzHr9Y?QillDRaooFFF<ޚC"h_|AX ݛt:DGG311Y"@Ii7!Q+x뭷Xd8b5ۿ?LLLzj&'~QQQ" +**穮E=2zzpvvf"qnW*ıc֢06&))D:;;ɡl~l8 4 <x ŽsP(֖lmmQk{n{0ll6nH``Xܒt=+++c|b06l֭[iinK ""+Whh``>b399͛7t:9v(}}}ccc__ÇQX Bqq1*Jt_KKK쎦l۶>nJfsa\[%KؼyFFOs9qvhogjrR$K/agkKZy֯N|-[Z&HOOҥKDKk+o&80914k.<==qpp@. mҳsE͛LNM )))CQ+** {qn_l^tN/<x:(*.uZz-ɼv+W!θcv''{ӂ1( +W;Mlrty==FRvڙ3GhHyttt o=yff1ꢵf Q:[[[vꊣ#ȎyctuuNcddD`ő\.glrGgg'tww300؛ ɘQ13 3 syqssO>ۇM6UsA./ \B233vMMM=DGGHHH޸`nn.6X-blX^3P(s ;hO"'' ssV\IEEݻ333ܼ)D__͛7sAΜ9Þ={255ŹsJ$$$prVps @rssȠD,Y2~B_??&'&sHM=GYY̨gX$Lӧϰj*1i&ɰ ZCQښT`rbV+V fffh-TR$00{233ç|&T*ƆD,㜟B5hϞ=իWbZZ?~-7rJqqvf|b??b.yƯ0@y1 S$yArrrqR 6JFF###KbcNBBx)((`||mG}˗INN^4yt jkkٿ?RVh+)k׮%,,ׯ͛7Y~=!!!bjj+MͨT*jkk3558yRIJJ yy{ 2::*,.,B\%CttS]SR$***hnn8g``C `||bnaAee׮eÇ#c-%ćWRXXUv*r_??ILLdff)&淿 4 ~ `oo%#$%'/(Bee%OP)#÷}\xR\zVˊ+Xt)dffRs??Oӓ_NHH..dge144%$%%qyH7544pyV1007n7[lmmٺu+QQQ\|?7Yf ~˗͛x{yDFFiN\r27o͛9x4LzFV_ND"A ~::;iiiadTl"ؘcZ}h4Z,,--,twwJmm-kVϏ#G&#ˇ똚k.07gb ::ZL޽{ΦHpss#<< XvݢBIOOrSo@*C|" vc,YŠx^^^N$$&5,ɈB #KSڊ_OŅ+W"J|D+vt}DDDbggGkk+fdd7Qk4477>Rڒ<[++$ aϧZz{{iiim]]\P2>>%55 $gffy&gbnnΫ'OFTŋ^[gn+ tYz5K-SNi&VZ=~)k֬!<RTTDBBttt?44??q!Ĥ]LL ǩSnϕKB~&qqqLMMǮ]믿~777Yl=;0PTTUUq:; f]3N8B`-w$HRbcc)+-::ڙwrKKKo3yl߾CT*CCC8:8),,$11/g ENNN b##bkk ܜӧN$&'''҈bӦMr"#H/τۼT*J%&&'Q`oOʕDEEa@jj*x9; AAAyFCyy9gNFӱ~㩩)\"gESvBtttсkӤhx:˗ٙi._NG&1<>>H$Z-/.+ruv`naA[[vёՕe˖ ###jkjpwwF ZnFfLLLT/ߢYVV+i޽oo{~D"!""kjgAg4`ffJ__؃>11R3:&O?F{"lӦMj\p===¢YҌ Qre2x{{399Ikk+(",, 777=u:YYYaǎV kkk6l؀-'On޼xtkd000kאJ׳fVXޮY}}}bccy믩ʕ+ZkemMPp0Ÿ{xP.184ī;vܷT>T԰|')p㤧s!^ʚkc޽ nGee%/]bʕ4k|+3==MWW׼AZ&ptp^cΝj/,YoozzzEbjjJBB(=~{^ݝd\j*bbrcccX|vhZ8}41<iuۿ )~JiooرcjcϥKhiifffF2 44tv yUU 066Dc399)۶neXYYBii)NݝHܺ}{?}EǠattW+u:;:hiiA]XnߧcǎuD#H$ZZZt6 711ASc#vvv󢴖.\8[l!99dBb9 BBB{6m|פ]ii) clbmۉOH`||e o&&&TWWU xP{OXVc;=xxǏcbb֭[X:gbb"7_ ٳx{{b N8!D"Kgg'}}}|GOEy9!akk񴵵QRRBjj*iii.̠T*)..GGlllhnnƍ -vg؈3S9}cؿ?FFFdggs5HJJZp6ɓ9(Jhmm%bvŋ{oW^t8 mȣ)ΕJJZ,ط]dffIWF$ k=Toff555ZtbxB @ݝ??ёQq[llJ%iK/H 2|2]]]vz{{krsRgLL=2JKKptts }}:uo// r aرoᅬ@y VTTT,+|`.C`NsnȈs159uʸtk֬!""|=&&_vW#447S[[3bP~~>333b)w K__mmQPT$ |>3G흘q?****o` 'jfO?tJ%9rR:thu:Z[[122bll +++:;;yP(R~F\/:947!SSS=N_ш%I{9lmmOXz5d2^{ {{{._7Oc A੄>k֮HaęgbƍbFɓrc^SSCvV6XYYiӦ_B?ran=00@aAQQQwJ?;;;llmEC+&%%333T*io+Wkf|l խF F[ӿ(8I qww L>ʫ#ɄĠL&N9XBaNGyy9r}}t\MmM /۷?22Bƕ+d_/^z :%ׯO>LMN|_Jhsx뿙裏ʢaʕ}0u2+144رcLOOɓ'155g=~1>>Nmm-|1@5-,,矟H$N)h4,Yrn||FKEë3 dggg^{5b޽ڵkAbm?Iuvv͍7`Æ GMM !!!UpqqPǭX~=>>> 1*  ʊ*/jy~VKmm-OfxxX144$33?@KK 'O}EzzzP*LMMJ%*lvrrI8 Z-:2^RW$ՀBX?|ɘq_FRhV;{{HLL_|f{ 07GRPP@hXآgRIgg'%%%3>6 6l kkki,HHJJ"88i\]]ͪomkk7Øbnn.β+**"??t9utvObb]Ce\͌ Og?wccc\r,|}}ٻw+=efL4+M'>e2'}}} gB EI멬D.T*133E2&B @}iˣLFxx8Add$\]>ÃVB ,ell%K_J@diaAdD!8::>= Usy[֯_OSS?dxx ~󟓔cff!gmmͩSh4"1JQW[˙gDF\pJ?}%)J]KRR2kNE6ItJE[[!ezzغuӘ4xwCYYeee߿v>#s_Nى )))c``ӧ|2μ[,_`v}nn$\zzzttt066FDDX¬Fd2P[~^qpp`rrFCSSլ[+hlldʕQW[+$##/OO[>twws5N:eee퍇;XZZ100&b<۱a rΞ=oQ(OO ~ʩ' 鷇>^c9 C&T*شiw8}4׮]C0">>a~\.'((ȼv>@SZJGg~qqJg'xzz 2__,--Ɛ{B"?UUUH/r*ihhɓ Bll,*++HJJ\Gxx8j)N8 / VnnZ|kfddD==Y~#lHt144| ;ݻ6PcRƍ475 BK葕J%gξ.}7jJJJ;Μ>#Nٰa˗/'""YSXyhLOOGUUbRGqrrbӦMbɉk ˩^,򢱡NoAMM Ν#%%yNlhhG(2H$t^^u$&cbb?ǢlUw=zz^~('O[BYY  YU*ϟPSSƍYq /slZrrr8ujX0|lիWSPP@]] 555;z-[׳=#ccc9sR֬YNa<<ܼyɉd q!7'۶uӆr%֭[@4B188(PhՕABBCIIIczff+/sURIĻwH`jj.󯪊> PLNNIhh(%%z蘘7 կחv,--E$j5W^eff333.]Dbbې穻 lQ!55qq,ZzZ,4IHHi333QPP@yY@6mڄyyyGrryH|));8~2˗/GTrI*++.]J}]UU^e˖BaNKYAhA*bfffj222O_]ɸ\!.n9ׯgj$qp`@.ٳgŦ[MWW'Nۛu!{> Pq8~8lܸCCCq>{nn. 0lfV\!Ndjj[gVKvv6/^dڊTJFLMM bPhkk̙3(ٹs';wQ(--%(8+*T>q٭ N܌T*J[ZXcƺ… JOH 9Q8uuu؈)qqqbRVD{iT͛7ʻR Ȃ=3q&''~:EEÉ]t􈍍Ȉ_|hZj5W\ӓA~_PWWKxx₱1\xIIIׯSPPMsQ6~_qe@9i0D6_jvL}Kr1]F|Bsr_M6Oww7%%Nfq111rؼy3ׯ_E0 &U,,*"&&+WPUU1DEExN*T*ooBbggG@@R&:;y}lܸq^P^^NjYپ}8u=.]d˖_JyuJGɉk׊R0XOO'OĔ;w@vv6'OjF+W",, 9yܓk,0 cɒ%8;;?_"g?w7pkI0bxxO>att0R).lڴ E6r JRl룹E~zV\Izz:ޤ?22BFFQ`xxӧO`oʕ+ɼv cc|Cxq͍}111 ##:DDx8!t/ sVK?ՂQ[G2sΙ<穟hkȑ#$aaa|>a$AmttO//{yPo(@oo/^^h4***Xd-NBBdzzgg/OOG)1 d2Qѣ|Gٳ_ e@> ???~; )JjIDATg.]ZiI _5ׯ_GRҒ˗S[[ӧe˖-,aC}=ϝcҥDFF299IQQ..DmddwWbrr aee%M.>LL:d.5D*Jp1884SV˔VN7,6T:LɌgsb+.??>hLM嘚abb^СC|2CLl\l߾/oox^G6mB*0:2B`` =Q@Agg'Kxyy=4Dmmm"}8qbΝDGGڲ2I\.gؐC_n.$%B\\դ299mիx{y{y 8}4Nά]LFcc#l۶Q^^.8r^^>^")SSLdqFR):J% j ljj~RSSeWW7nܠZȔl6PRqrsrӓ^4lllpqqAjd$ұ騨˛9Ga`/((ŝ;Ψjv3g8z}Yڲ=2򄇂R)aaa3l-շy$z֮]KCCW\-[1#qFbcc5x|MT* } #C&dMMMёvW 99?\{___^{u|}}F ..-[FUU'OIT###RYYIyy9uu@_ZDattǏ,_= cPhhhhƍh4H$RX[`gk:uCc{ؘag}d2IIIaݺuDFFFnn.WQQARR Dϓ1S(8u兗 @ x mll\?JJJ(**_D*={V8i7ַO^O~~A 3àcǎ111ɏ~#^NGeΝڠ{w 7y7D :ù477sٱs8/ϝ75[{NEh4p gZcooiΞ9G}DLL@^LP` wqrr"44JECCc[lLLh좫JJK(/@"pirsrS[[#RSSs<<<8{ ! rr2"7@P>UAcmڴn^"##~P1tȀ<-LFHHNozztDGG@}}s ۟SCC.\`鲥s$"lbxxϏ#j|J@@gϞO?͛qy$ *!!!pV\9uQle:M&.]ȑ@m]]]]XYYOoo/uuZ7VpssHOߧ v??Nhh(TVV 3gPՄΊ+ZGXz5tttv*1*M(..4++᥽{… $'@Z۷og$!JM&##q-| h8O =ffTWWy_9wI֬YD"\2##֮]{S8::veE:* ccc>uWeMs" Zͮ]P*|LOOvZv%ZXX`jj쫣h8<8p+5_ F 1R),^](;vJagjcccM ƍ,˗fl|:ذafffhZ&'&a֭={ZZ[av9g)..fŊ}/%]@͛i$##u:V`ҥbVV4`! xjJ??{zЩ)RSS[jN<]$$ kkklll~:sB.&%adferAk`t;38dBBCfٲesڏMMM>}66lBksXIss(KJbb"aa,_jjj(** F Z-555MLptrbҥq5ڮӉ$-ZAq˗S\\Lxx-b||\+ڈH$mۆ;/kkk166F}}=]]]H$֮]+#B8=ːbrr-Gw77gE=r'''jZa!Q$lkkK222Bqq1iii K\\ܜ@% :IlT*`,ܸq"vǎۋ1\]]C*T* ;'qDX3+h())!j555'x?,]0j˗<.\֖o| jvލ5'F%z{zB&IɓƴN)J[SNNNT*]SXX'CCC|< s"y*+ށDJJJFqeÎ;pss(JoVy##t:,fddfaF8q$Ϩ`KMM ڵ{pp' m[~㔔,_gg5ĿJ"8bI*BAÄ́wx1Pj5GGG'233yW0L"BfV888y0o&TVVreZYlAAAΎ;4דHxx8M!,..ftdDԥQ?\NSSǏ#+V@Tʕ+ܹsqEeɒ%r 7o*Obʕ,^d޺[nd2!`[/|Ť$9-z)9}$%%o!MLLLPYQ4Z[[ eb]K ͋&b' 4{;; 7"%%NOpp0'Oή.N<֭[oE3;vСC7MH$h4q~~Ȼヒqqq=z/oovgΜ̔7z $D5ޞZZZhI*%i+???quu%f||}quu \D"a||1;)fbb† 8~8w{;;<=<8sVض[|9֠jٲe >|o~cemͺu fBBBYLbjjٌ^Dӳe1=d˖-|1HHH*ZB2pG.{~ ؽghZjkkINN@~R/)&l^tANj!P( `&7oǓ(MMM?^,ڵkaL&#**{wDSS'ODVo>dbgo{8Ο?OQa!KbbXz5ffftvޤFD"`ii *q1OLL055z HRWGk[2L|B BQ"| G`h211Akk+ׯ'<"rnB\rRRRc޽DEE @Š\~b ;v`ѢE駟Styw^ntt4\p;;;Hс6IHHTVT1SZZʥKXv-aaaՑFee%3fnw ؼ:6" H 3+KnaՕZرchZo~#2agg%:chh6lYl#sd۷i\rbkkKjJ -2`lbBa\$m4`b"T.sssDZ!&&WWWz{zarrKKKqtt XϞ=Kjj*(J6n܈ZP5ȑ#8887]]]<<<͛j<<<ؿ?K,O"44UVv[.jA0˗/<.nޔP&zj:::8uꔸsBffD^^^JN:?;v"\]\ؿ?AAAA#Z E0TOfGGG Y!ONNf`^Oww7 z{c||^"ttvv266Ą"!o=z( /8SZZ#NNN\ l * OO\]]ܜlYz5*Aؽ{6NKK TTTPZZBa#...ZcdSSS /UUUJFf&撐5Q*N# ĉL$~!ڵxzjf WR>飺F__X/--%??#2Lr,-rz=b/ rsr(v.T*bkkx!DEG~7('O^z ~ Z r1f ,jMQQ!/|JEcc#ތI__ݻRD" T ݴFyY999t:innƆ5kP^^ޞW^~4=hh!J$LJ@JիWNff&/]ڵk383`jj*L3>>.VΌ!kZ&''qptرc148Hjj*CqHz7Ž}OcuAOMeeDEEB~^ ,^̮ݻEP\\,q&:G''"É9 4IIIrQ˖-¢"._DUU5YYY/~9BS^^΍Q*\IMetlaLLLpqqa5 p!Iؽ{-BLMM+K+IIIaw,Xaffs"TN:;VV\pLj50*Otmݙ666l۶(O~BuM #,ZljDn 1 oߪUXd uuu$''LGg'Øs۶])IOOB ? ~)֊`dd_W׿fttWWW"#"ߟKJcS#wFP{tRV\yקАXWޙpi3GGG/ZL^~>dfd`kkKllBPD\.7nضmL^*U OtYhm[[;jkk^-4D~~466SSZ<<Z[o0<>sG"&&t:&&&IoO==E]m-h116s100@kk+ٳiDq:])hbb" "88QqHaa!T T 7j缞\.ёM6t9gaa=brb9 RIKK H$KDDFr9}]V\.\```^{ ooێUu% \v7nyf᜜0S(hjj0 M~^J%k֬!::bOSjkERHxxW800͛7immj4 r9666y(--{;; x{{uS*!.V+:>z{{9|0"u Dnl*y@ד_jٺu+LMMʖ͛qqu!%%/YŤ$z{{ GkaaApp0׋Ybֳ^װÃ~[ܑx1 ;;;effmmLNN%$www᧵\|6H||<''**;w[&+3SOEoo/{yr@p[l… HR$ ,ՅF4 dggSTT7l ::KKK0W(Xz5aaa|dggf1W*(J\]]YdHDC̶dKK o\zqȪ\^^.6EGGFggsPj2vu4i7V"ظ`<)]FEEܸqP8@@@x!ϟ?G]]='00^}Kff&,[ ]H$MuM f9>t:={ffflٺ'N$իžD$ "==]$FP[97Dihh(׮]###W^y͛`=zLF}}=+sSSSRɷ~L&˛3gS[[‚ 69twSRRŋPQYX|rRSRL& ER]]@ϟW77Q78$Lnܸȶ_fٲe}ڑ@g1Yxx;@&+!B"=={`R)GBBׯ_'++Yl>>䄻;EEEDFFߏɂD 6mqFd2.LNL}455qAx뭷7fu̙؛/0+DKřӧښN88~~~_~t:kjjJ\"GPZZʡC(--ؘ2n_RZ-۷m#2*jނ`II b$H$C%$$'LLL-lR,lccÊ+8v(,Z[[Lhim%hVECa93hgg^^^\zUd޲u+>>>HRE[ajjJ{{;/~+I[B7 ,^JEQQ@x28 #:Z-zjjn QWW׼.QIVVT*bVXHhHȜJbͼdfd~Hh4:;;ټy3;vStvvry &r9fP(ptp@mi3f |DFFڊTUU̷:233)))Ғ0RSRgtttާxL~ŷ^3Nիx,XV*[q/'O^Ņ@ E0^Oqq1G%00;vgϞe,^O~B}}= u:0Cq8 B!ԭ?02Xmmmtvv|rѨ9s4}}};HY1MnNW<% !!QWW c3 C +++y>x}Ν;Z͛puعs'JRm7 DgppJKKyd "`llM6O_lnʺu|jZrs9u4AAl9*$$###<ȴVK`P/"32cco|hnnݻw1${qƻ:o[qwwgǎ|zW숏م|;==ML5&&[`j܊N s4(18+R,99 ۇ[[[֮]ҥK)/+#;'{WWWk3.cddv\A-[/AUU]]999tuucccbPaaXh#HpssG*p u:!.DCJ"4CCC rd7ۑZ4MMdffRZRBDDZty%Z,_\~"//kkRpxyyzRRRW؀ښ4 vLoK#*:DZZZ)jܹs$$$aÆ bPNpdppJBCCPEtRQCTt)444#G"v2#"Ir"""8}2B Jqwwb@@P`aa-X[YTV +vbpvq 8|H oLPST!;Z"6nt׺ gN{8d2ZVpBTVTݻ ezzTNgn=͛o3vZmw|:r6oLoo//_ݝʪJOLLpe\–-[ mn?ˀGu$]Rʕ+) /<4B!JQ[ZLXx8r"a*C|*pYh%bjj|}@mll` etwwg1<<'ҙQĄ7cbb}}}G'0kCmm-\tccn–[qww'Jqrr"00VR#S^^HDsIXX؂5Jj*.3d&Yff&===lذ鎩)...^0#dd222ؾ};_H~O~9?'?18-FHcTVX%F"=/!@"`nnQQRS[Czz:y9r*jjj011!!!ý N:Eaa!wyꞵ¦M(**#+Inn.SSS1mmm477(7n`Ѣht:=OGN!2z*&ߟ(Ξ=GFGGyK"+J333cݺu8pn~q 룦F 7O 6lTq f n$%%7/Ǐ={tҧaohA;bfV7aۛbnܸ޽{HqTȑ#X[[}s =FFFxxxPPP| O 8tZ^}9: %KPYYI҅ 8;;wwwr9addĄX9~8uG +PbR@\8O>Hboo8vsscݺu\p),,|R)K.(?$00__e2{_3O {]u-C477ۋ lذᮊ?Ovvv?f޽T*+j҂QQb͍W 8ǎ# [KannΦMxb̨Zݝ166frj L+Nssd2|||xW_(lllK)?A-xOt?oebbB`` ###QVZƒ~;_FGݻR-f"cH)))∏(9; jmii9loǒ9}l^OII G֖aذ~=zC+W566ϏU8722BOO8Fldd$Fʁw/55]Sc1aiiIwnx?'b۽Ҁn$͛_M6=i /^Rf\3.T*qLNNbbb"~ @Wg'VVV3J-˗/쌹x󶴴`dd( ֖;wxlذ. 7n|9^Fazz@x}OOO8 +O@p]&^g }ϻo(((`ݺulٲ(#DM6ݗq(J6_3k׮GGGh vHH_~(>nf֭P5իEe˖ sssz-Df'zfJEww79zֶVKUիWYt)[vws#Y7o?fض};111%Ozz:~~~xxxKZZ F#&$$E_:.yΎ5k-LXXo6/|~#O9. =Ba`~bbb~mhpHJ*bll˗/舧'---07gpp{x&>>S$È%IIIj}LLL A(sEww7"55Ebb"ǏwreY~6664o0j 7@~~()*."++R1<(9w?ywO~Lf=P X0h4|ݻ^{3g|"ͬ4` X[I˗~ș)ǥKbooϩDnkqMMMQVV&S ~1VVV899(JLMMd `ٲe퍿?錍}??/ w7X mm\MO'22{{ڵk8q\Ξ={@ -Aǵ"G1X6V7)Dz4abbdYb ͙m[BU^&cZERbJbcꢽNG*RpaF%SHHH˗/h"  1==_ssC9FFFppp`pp'''23%.6%11xzzGGG'd\+ ((/{a6`,̊dĿ})++㥗^ܜbQ͛whZַ(..IdˑH$b+khh.n޼͛7~:W^E\a-`gogmm͖[puuÃ!j5###T6 NGII XT|ߥ]vQYYɥ9H^~^^DEE킢V**+Ȟ{?" o}[_vݣ$m•6N6!COO2]y&o =TVU!y׭[NJ+8u*vijjD_㼹m1D 7`bBLf;{{\\\"7'ƦFeh4㙜~8e!FGGr iWƆ9jIMMezZʕ+&))Rc۶mX$0Ͼ}B*bj*DDD088 'O H$9o}A6aud!d||V?m۶Ç|ɓ'y︙jB*222™3ghllds0MMMbؘ%KŋqAAA888P]]ᅬ>>(g"Ύ**+h4x{{coEjj*[ne||JbbbA`e|xa0==Myy9׮]C31AuUK.%;;JJJ]{FCC9&&&x9OrFC`` %%%XոrIzgoaa!N솇hkokׄY__MMMtvvbggT*֖v> ))FԩS9rDX;hmmE;5u#ߟ$cǎqQ$(0='M077^WfZ%$$IOrlq_fQ@uu5?ywIIMy&'QYX`,#Gb}ZIJePǚ5kP*\xRkT(LLLJ7obbbRKK5/]9]]]??DYYM7bnnNoo/0<2,.ziZz=:>P(ȵk022bYl,+VښQZr9*J~H$˘؈ qqqONccfH?^y= \Eh ~mP[[Kaa8S07g޽s)Q՜ӧOsM6mތ= Φ19PTdzxb#RSRĄ{x"HϏ 6^#fZ<<<^R\!=#bV3W/~ elذeKRYUō7ё2oΝOvxAÆIy`_OJFF]r:Dyy98os\ !!!撝'6mF硜VJj*ccl|e:;;9qݸcjj?_CII TWWv*}>>>DDDao6ϙ3gcŊakk+JwvvRRRBiYccc8;9ӓ8ǘ$//O?UAnj*d$_LFFtww\L%y|`<e:W 1{ cccbcc*}]]$)"FR)ooFc_ JbYl,444͛H Tsm۶Ĝ2##W׮]KLL h4\~M6}vLMMYd eeZΞ=ŋޞBrcvŹsILL MMM=@HH(fb\@plfv{N[|x, '}EH"&f6kLLVVxxz"ٴIgHLLe Oܹs̈s8;Q]]Mll9\..#3 (rOHsssٴq#;^x ԖH$|g6Z-gΜ!55???<==Yr%ڵk琥\͛7Yt);v:?2(`vx0b SSS|||L%022J% ?~:D'DH hZҘdƍOի F:44Drr2MMM֛h4/Z̺uH$bB"ryedd$|8R}d2d2+VҌ4v+5uz/_ qvvW_:S`sOx`A_a___~0:60ZFFF9 ٙ,墈D"!11DrWڲ2rssپ}m}^$2477^zhǍ7z4"##y';;l|O~{{{֬YéS !$$Dlo0OV%%%4VZō׉:L"Z <ã[ԕy022",,(ijj"I=133cbr.G9qqq9rZqE6^ω|N/ŋoIlmmhZ-׮p98pj jSSSw||iiZ +++ Xr%۷o'00pA)JDDׯ_ŋ"RΞ"""(//'el݂+yy%[{$xh0+ 1 ?011 V=55\x{}jXzfM@@dff#rM7D'nj066ƺD̄A|\\\hnn&+37w7ݹp0ѧ@ 53* ###:::͛LMM155Ů]ǩ 6G||@d@?| ]]rrr`o?'66G?ŗЗ1HML drsdʕwJ&2===( 9r(555s(r97n ^LJ!:;;),,d>)z=٧rO؀r4 ؾ};X[[Dj5VVV^<<477Vy饗f_TD?Amm- L99:# 8:8`TQL. Gs u8r9BKK ͛=R܏ WlK0:BE&J1035%(8J^}Uy y@'&&NioY233#6.O?j"""\.fRRRHIIazz\CCCLNN122 !?sᏎRTXH^^.z=+boo ѦZ\=IIId2^yfϵ$&&}{TTT (%??HXBNxto'5B*066fJ%<"ׯΖ-[cx7n % ) EVf&d2P*f@HEE==~Trsqvv^ڂ`lmlֶ6Jʘ`ddJeJsΡVٴi0==Mww(\ǒ%Kĝ)Qk=-> ͇GnIE/r98BQQ!!!ܹ>?W^y޸RDݑԔ˗駇"22RYWWGů+ηdffRUU___VZ(3P@__٘͊+fJ;@ΩS룻$֯_@H0h4XYZENN<ؗl鑆<Ax}!J153crrSSS"""8{,7oݝݻwpiv"f6 wKӋL033?:+Vp!TJ%Ӄ+V&88[[9IsssMh4w^֯_OkdggFpp0111xyy5Ǐ^ؘλPLѣG sVv6=zIV^ul辉>DzH=Њ0oD"a"##1ΎԔu:ŊȒs' L) QZZ|oדv uumXn(z]]]T`ee#27n%Kڊ:ү^i  booJcAZl>?v Zx嗱ח-[|4? O~(^O$ rSS&&'jg yxطol̙3XZZ'vsƁZ>###.]wA3>VbϞ= r9AAATTTe29x *NNN-gѢ4663gx-"22UV .^`ڵw0.|e^ˑJABBZiVvTec_ qߓ1b\UUŵkט0̙3XXX FFF2 &bt]Jff&H$ٙUVɉ'puu<ח&:::ؽ{O?_\N@@~~~tuuQ\\LNNQV^lܸvꝜ@?N^x___~ߢ&<"777QlժUttt˄fʊ """\"HG[kX> @\P/Bnb”v V55baorrId2֭ǏVQTd$HLL g[~266T*Az7^GVBdz~>c>E T#6m‚O?֖Z[Zptp08w֭lmmѣ kիWimi֖d2\x!6lXOnn* +*hkku/ ዝ~PA QYYɱcppp`ݺuDDD+| ۷ر  066ƻC~HR|MX~= Gu::(UUU8;;o>9r0}}}ٜ'qII | \+9sb~:Oo yyyz<=Eöގj͕Ֆg8`#6Ec/R_A\B~~>WGd2FGGqrrdeeŮ]xC]m-CCC˗9}X011L˗_zHHرcK⤟N///Z-|[brrG`ie)͛TJټq;vdƄ< QRZJ]] 33l&??BaS7m7Z/=8{,?8pBXX7CKs3 cnnN#..f4 "ˉ ''9Sqz9= EGGc燗㓏??gݨT*t"oR<6oތMMMBeU8q͛D$'7w܆P\$ ^ޜ;&ٵkB()!x8er 3BEaP322ZT~FGtttpu/^̛oIXX{7 #;;>L }`eeō7D`???OCC'O$??ǏŖ-[F&сٳ7|/C.OT?3%Յ)A߲˗Djj*:Ү\ UI{' &&&lڴ]vI$FTÆ ppp e:n?`߾}hIOOGC?` `R$4,JQ4{RTT-:ƍEFF/¢EccP`d$%<<|N=ahh/RSSŦ͛ 5WlIR(9'q 7- ~3DN>h" =;zTl\IMR͛{xPV^N?fffz g׮]888ARRsΡP(j(J._LEy9CCɦMX~=%%%,](hmm jkk~ʕ+- Ghggg}~1^u]\dlܴ WWWt:mmm8;;ENN6/###֯_ϯ~+PQQ1+++\XhVVV S[ǟ'&'&()+#++ɉ ]g}8%fƓr|b[6(zN%B^crrf,,,Hr"; \IMLJPJHHHk2鉽=YY͛kĠj9}&&,]+++"77 bcc ggg~_h$4,Tc``333ffXZYP(P*XXXFA~>o~kllmWWWѡ>?{lrp5W0~ӟ255iii9rJ6mڄ=RXjjINNfŊSSSۃEk(JBC3rd8Νgg۩!??UVCCCQ^^.:(TJpp0o&...±ep)<<\Ž;غuIX]M]]d2\]]&NMM_ =~p?'"g9I3Na;ٳeDFFT*Yn=MrulllI lCo_l޼~ȈZ.]uaaaAtt4R(K,|]\&## 6'NCRWWܹSp4 466R[[Knn.))(JĄ .ĦMغu%.AHsLI?<8 a :d2QQQsE>Lmm-ׯחxr uZZZ?&&##<""#flll^sLի?9( oqeŚEprvfjj!&99|BDd$ׯ@?۷0LMMqqYzj8w999ddd01#3FLL8 3{0~xJq"ofa׮]q9jkjؾ};1Kc7221 /oCC…]+++Wb̈́a^cbfĄVc<};W&** =r-Q*Kp~_~z,Yr;s*//RRLJkIkkw=#86~؞<5uZͨ]ap6Yx13b,^Xkjj =!KJJ8y$˖IIIѣ\x~T*dppQZ[ f,ؘ(r gΜ75X#""njprrbϞ=QSSCHHZFr= xܖip6ٻw/Ŝ={*T*tuufkjj8v@QQLOOѣG Be RdYl,.\ޞo~Miii wc,_\\jjjB 00 I"n:<<<(..&//O4| B(O D F7B &&&NRR999PV^(&&&8::Ω h&&hhh ';777m&鄅LGGQ_т-whh(;whwT*HrRbcciooի߳Np/ !?Tv'?[PbO؞(Jjz:eLNN @ htuu122"j|~_A}}=ݻW֓Jxxx@LL o111y R33J-&&&x{{@]]/Onn.v]vNA ɐJ9NhGTF2,th~D<~e`bbB\\۞{|DRFGGBa$"75Μ9CRR:)ÿxo-" Hpuue˖-| @gg#1Xlll(((Q {>Oyw 8O؞d2vZm۶!h4LNN2::JsM}cLly{666,]TW )JEqEx(<Ş3ϝXB؟ϗ}0(.055Ebb"?03b"O#ZZZ8@lj/8 [D TJXXYĢE+6![ˌh|_#a?J ܸqr|}}=G /W,fp`@/ _wteF:_19)ap_PH72~ :nqӓ>gxq{ͯqbh dd= 3<%,O,KO؞@9Da`ͯ<~_/#C'p_3_㇯0G`g;_zۇIG6" W  wp[A,_s/v?gWb!5X Dh%>ӇA HYO<"w<@,C#?c|@`aVq,8#  D,=1'@%ygW>p;`9<‘O—@ ¢N"-=<3+o³ GN9# sA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@AA(  BsA! 9 AAr  D9AA  Q@A zJACAtxƽ{jkkwYUUuܹF2 m9ի3g$&&N4IRRiӦM  9իg:tg}?}ٳo߾zzz9 6 :gϞ$$$8;;M<… 1 %9w?oÒs ׯ_& fsŝ;w"9NXh(>ի/^|.%BsׯKKKMLL*++i?"JOpBqqqNNƍ9j( >(rΝi)(˗O:p}u@8v###3z'YݻA|89Y^PPnaaQ]]O-ҥKǏ%1XAuuu666}ܥKII͛7?{?޻z Mkw a9r…rrrIIICׯ_Ԯ];v.t}ҥK&vv8uNuXNھ}{aa={"ѴA :Jnn#G=/od*#<<Ç<~ťO>vba)))hü w][[[_ݻۛ7ox‡?Sz޿ƍvӦ?fϟ?tx rs8s指˟?wϗ_~R!녓_ɱW_Eo VfXJJ0---C+++'''˗Yz;v쨮|2L`.]0s 0t @At/^X* A޶_|$0SN=qĿ9jPDXŋB]\\lllU9EEE%EEUUUmmc[[[7776o<vcK̤CArsd;vy --͵kLի?ؽ{'sI͛7hj 6/\Q>s~.駟?~ ' >|xʕ۱cGAAANNβeBBB|||,-- 544TL K;;OĿrhhhHIIQSSweP-,,DZp[n&Ox~qn޼yƍ{qFۯL`HRPP%߿W_}w׮]OPyݻw!.PL\III^^^ffڜBl칹(-)AJ9N>o|۽{׽{={O]C]WWwf诟9s㽥KOO744_Ǐyĉ6,'''.!cǎ;w4iҤcǎ7GJJ oܸq3}}KKK`B) w@At2#eee_zdyAdDԱ=+($[ lNht<@ C >|,DVq3nuֵk._Ondfff:iR޽!СCO>/wsyy9UUU]`>kmmmeeՉOvl۶ 2g 55NCC=--6MBB-^__O<9\x!7j7N^^itc|0` =(6o՝8gnfI_bŅ ڞ}z)bem*K z{{2aB 6 rrr~wfq󪪪3f(++СCJ//sƦFvBZRn:w3wP=X9Gyzx>|XƁjt1 \\\,Ivpp(**={2rq#33ՄYZZ.MI5 O rA';%%c.tǏ}޹`|t,Y}UV!LMN:Y烠/oxزel"ď?p͛7-p۷o?{ IFaѣGLӧwr[1b人@ gggESakk9mۦ ^nVQYb'O0 2w.JPUU <7 6 :GYXXуB20GlWC#_LsٳuttTTT<<<6mO[_ukXXG222P׼lj2g -sƍ0 KI'8poϟ7o,bΝ-9:9z^^^G*** @8Nee%JQ+W@,-,P}kk̋/b837 >19Nydv:׮]DX(()* C;Juud``D?PSWv/_p¦MAhii¹>377GtM02"9Ni+|GKII޼y3wA03ӿЅyy^;v,>>@VV17w @?=L #""JJJ1/ǁ>~29s/^HKKc֯ 8s i'޷`jij␌IHHDEEAFQw+++Inn B+/eٖpA D'(..7n\nځE"**dWXQZZzɆd;~׮]zzd{(&GFwνҚ7o^Ue%B B9a=B FIOKsuqQWSSUƢ֭[ޣFn~0`),ee3{w{;&ٿ9Z§+Vv0@IvtqM69h[lЀL@'N?,YUU`|diH! :Ǐ'''O:u}8pO?F z׬Y憮?҅a\FB'?0 22߸~?O%%% UBp>|r1}t//l_|@Ejjj9-5\5Ī*@S3_~% 0`ڵVV .p;i,:%%Ν;m J"p|PS擟Z`V\ut))!ּ%KLMU9XiNN(F|$Ў;v g̘T} yɓ'KKJ222ϟFXNNaffLJLLDt8p͛sLv# VZYÇR1mI'1bKzz:*«hk W>qp=d-w(eΟ?ǸHhhhG/($Z[֭[mr簱vtq Cz6~˗+X~3gBqEEEL--u=P ͣtLE[Kkܹ>CG 9[<{ YND 9|0$%91cnnؖp8˖-cN`VS )lhhx"R_MUظ}D_(>W^L믿!7oތϹ0u]]bsH_+K˃ sڵ{/ }kBc{gw}+0 (u&4 ۶msiIRsmmmΜ9Uojj*)) +l1C:A5Nk׮vqq֒Bjkk;9:/\SQQQ__SD8鞖W ('f!Yʆy ! sNjk1O:XE59v~~>sU?q hoEss3f Xοr:gΜ۷o3&ԢEPq AzzzAܹÌaǎ^^^jjj::/Y;Ur D9AA466"fddL6MSSwbbrz֭ XUWWwHbLy0s nb|˺{.KDz׮]@VVfzZZbBBDD9sedz,K~ƍM*~:ۇ2`'OEF%&&2@<\{AS3))<:PS檮}* lkk_TT# SnҲseహ7 l),K<}kdhhddu6΅IO:177И>}5k`oG>|fO@?r c A#7oNIIg.TTTJJJt%w`ŋH7o^xkݻʊnYtOINFW{AAAOOwrBf!utѽu ek->zdȑ[nm-Qgzyzr8Kc999rrrfGVeejkk"[L~q_111򗨨(Jdd$sD5tutk֬fh{$9s&u֢EФnnEW/..zp8NNNVh7IIIχ`9ܰaC\\Rvc[4 &,X`qb"R Y~~6GGG$ :ZZ.2b3Јycc3m4777d9s"""⡡6nܸ/{/Fg3fҥy(9r4ct _Qr ,g޽@͂`iijB; q_s1Wj;w|?_Z`Q2pʪӯFÆ.X \lxFFFǛy\СC(|LLjwi iiقb9%8C?r#jllܿ?\{KY?`2 9溩)!?(D;0';;//oKa;

|`)W._F*))١/xY/_KaYm\ׂeαjr…> ?xv֭L+x֯сB83ŀa !&&IAs+7I 0~ ʕ+ss#ף[ qA!zѣGM|3OڐPWSg h>N:fСX4UUUoݺ/ߟ464$%%-N Me"(ݻw Q~:a6l˗Y,B sXYvq,BCy<~eed?낵?U9__sεq``픖B/YC.\LAC >VЩ-))_ܹڻwﰰ02Fb/^8s k׮H8rcB5;VPPpttTWWgs\\oܸbz]]s#lG a^433Y7oެ[NEEۛyի1ј`LB>544֬Y#" Gp8ֺ|@8槣ciiYSS#x'GGHҲe-ڼyq+[o&00 ! >b IIIcǎ]vڵK}f̘hЋ2|%!!1zhqqӧCYnܸiشiTYKKKQAASC ̮;D " fgg([[Y߿Ҁ߹srpuu  WTTD"6Q ݻLTUU "ƍ(zc^ŋKR3Vh1e>}#.&`ws+Nu  jz%oo/Y4 zjTfk׮$ J̼ɑ-9rŋq.  c۶mfC 2dT11td]v׮]qեK)S@>VQV3OٔƆ p֭s***m`*99ٔAWc?~]{Ed4s$\@I dа!vُp쁭b``pՄUķgϞE9ٳNw`.:M_0+`O Uf̙ G-ԩS%$$|wF@ X--Mggg7mxɛ>i winn{hii5qP8~wȊs0gu55,ƍ%&$d%+ 4ռy󤥤de/""UnC`eKX{!s)'%%0C=rM"{nkkkMMtM@kC퇏e^={ի/^|+Vرڵk0dVZw"ħ2޲bpx?xaD8Ə${ܥ1fIR9sJJJ&wM6mՋ-bFFF۷oksƼ<555//zпYj;e=) qsu=uTzŘ֝dsZZZY+;;["Ӣ8 rf``vf^M|8555((h+WtĿr!**JLLLZJԴR SQQi9n޼ơx|rqqZf r@_Ʀ>x𠑱رc[]]]z̀ETWW[YY1QC@#8Ϙ!.. "˅/VmSlgggmmu)Gg:|ҥrr*TZREC{ҥ N4T±`|eee#CCF')!1ٳL=} Q xAzÆ ZZ۷Ϛ5K]M->>"!^P^f8ϸxb~3SYzu#"HCk׮喁|Jk&K,aCy9 ([@s-IJRPPHKMmm׮] TSSKKK>ZփHYZZ999H&QWW 8[ Oa[h4>6޽{ )> :}; A >PgX**ʋ/f.;VXyv,VUUMLLDZYYUUV؉>FF 믗/_ ^V-ZPޥ*N tqqa^~Q___ӡ8͏a0 /wejj'܊u':fWTT\lYk8P+h'JRP=JNNfTUUU, "ُU8"Z"UXXU⧟~l&GyF)}6Nrc###(655-..f!l¡ÆK>}ѣ6۰> X$(\{:ڧO8 Sm3g01 Pĭ%9E!Vn(RRRRSQZ ʪc6OرcN:::…$Ν)/'鉦ڜ0q"BVVD[PCC'bmZNql6f8p B` A-BA|Rܾ};44a p'Op\AA-qSLYv&!ASRRv999SwbyYWW'..%!t2|Ĉ/] jbsLL0s~zLt4Ͷ.**>vwwWSSC6CAZ{Ag*V!'2~`nn {=XBU=Ϝ{e˖![{rzƌp9gCSm! "w9yy~( ѣGb?<N7oRS>~ի X4n&G)(.&][[b:uՕ[NS`!ܷ/ZXreWSSݻX}}l|} 4y䄄^ĭptp={ӧGe*+!X::;vx>stVG .TVfegg=O4;f5$P~a /wCgXqᵆϘ`?===ܚ x )ݻw-[fld-g?d=}k޺u =sHh("\FZz;K{Fr{[ڹs'740HLL433322jA 9rRq/_~Ad7/(((>>AVF%al}}vw77UUU$u psuvz #@6@V0m0o޼Wr4@uP=,炂TLe19ѹא޹sefk֬ic͛YYY2:l*׉9zU(߰aC`,+66ᨵ%d>1~PÇ726vqqA45SRRn\.pPXھ}o0ArT28>>>22RVVvʔ)3gluN䤢qctķ'Np1gr ԑvc",,UTa3N0jjj S&Ovuug p$=~BΎDȈdgr{3C̝;kc&M>}5 'O<~vРA3?e1gjBC_o޼9{,NRBq沂?~8\#z9z--TlXkڼ*rx79O$+BHqvvDIfaa!8XHgDD`ݺu*H;LfZdյkV\ibb /`;|pk=iʻsՑ#Geo߾O60\SS3:*իhIJfb^09̕jj(IDP%%%A3|峯;' ++$4;KZZJ*habmzklg:stQ'6) ;w0m= I524ܱc9Aħ RM9)%%9@lڴfCJN>-"gr8Afz7ofzb4<<юنϷ^u0C#f!bq~~~]vݟ|7\-mB3Ni0Kvml ^~yB5l@>\@9 @x(JQBKK^c~ϟ?1 h~Mx5*,,^UU… 17RbbbtaӅeqssݶm[g5A >YR+VU(++#nTUveeeLr߫Su Ea׮]ffF>>>222W!>VQ151Y|9zO h^]]!mWTTuzhz=~;vƌY('Oڹi ===(Q|\?ߴHϘ9:8B_C]}gΨ#\dǎKZ~EϞXaŗ `8-ѣGLJX0X0;wm!K,bرk׮}) .jjh@U;9XiRRR>uuu DoXXz)))U3S!`P ٳg99YK^^^;/D]]l ;w7~1X,$c\\}׭p_vdeekij17z ŷnj```kk{߳g5us\xü%^"/''Wwrr=f4?ޅ6lXjj*s֩CUUUX)ӦMC4*+K"\d &劊¶>3u q{H]Mm}+x+ħLcCCxxDbBs]^N.&:IP\f5R1y%0W]899bY G\!щ1Nt,wm۾}/^|rhh E32V`YX"rg?7bɍܵ055EAի#ML4540>ƍ˖-3W=WhMKS}/_v$'/^ Abٳg 6m s@`!ԁ5N' nJNN҂vdee544+WTSUy@\Pw}+tns@VVV S\X: tiEE۷+++ USLz٣G/_&$̭Fƥͫ!͝+'' qܹs's͛7P+VM:ں=KAl?zz!ǻ-mv۲e Xb{۶mu 'K  (X``]]?V 5hHPSN+++,YŊLcKJJRPPHOKc%s2/^HIIA[UUuoq˗+XC8veddahh011^t)9j]RCX*XC ի#EEE>yI7s162={vnn.zO>m^Wl?a* oݲ] ID$=%**^]]]QAAs4Y#`[nedՑ"V@|Zj* xih6@; ϟ 7lp&456==]^N.11Qx8A퇜:WYYe hki!W #11&AY79??݃?N_-xg}#8ФKH]t Io0ihh &ADcafaa<ޔ{93QYY[B_G[kfffZ[lϟ$..>u48Y.yibF;A8l6LI փ̙*"##a*bRM~~~y2amxW .1cbb~5kVUP9b_ӧ?.)):VBIZ !ChK0ϔdXҔh+3V,_211AQR@ݠSQ3azu55޵kטAtr{&XnBd"&ѳgķv;xȐ&׿߯ ccbo P@dddPڛ7o"(6!(իWQEڰUT8cB|<| sxӧWgjjN90<ҥK;y΂>jϙ=[ZJՅ=/Q55LQsrws;u3A4Av֝8<!@`@;6mڄaU )֭?_BMb!Ǐ/|Idkji#Y,gcm @ :t'O`>(0xasLMud đ#G3vͷoTm۠ep uu%%Ec#޽{%BDf8πg+4x-&dj;*bgg=,=kނykhGmm-ESCϏy:-.##fϚuE WeeeFN AA| !JKK 9¤_QQ1-5Ccc=zRD2ҷC`)==g$O>=_~RRIII;wܶm,CBB\]---MpBMM@_ڴi3}}###Hk׮={~ ILHppp@X,ss3ҽGn޷o1?qvvAx$/YR^^~/^ `?Ɯ݇An[|qQ֮myTG^^.99C7`oݺ>{,@] X \tU500(*ڊZcHee%Zh >M^z9͞3g[u!MyO|B"'}?ߎ&P0$dBNNIl7o ޹sݻwo޼9+33...0 aidd /XJ1+W,((8x`yYDM~Vԓ_a_}eee̐З#G^z…ǏWWWرs[tҨ@777;[[SSSMMSݛ?/!j=k׮EE@{[webb/Zݻh"=X94 rѣG ޕgΜA/_QA!-5W5zg]/}! 077?}43@I  )o\pݹsƍW,_ccc$%%}PW751A ϙ3'!>>11q-^2籞 bEJr26,AB-͡8H~yy{75PAX/Pʛ *+---8ph?Ɩ@Ua{V<kk^[[dll\^^ޡ@A| .,,Dx]ꘙHFRw;sL11QF z_~9tPD5''k֬ @\`$֮ݶm[EEÇhL1q&N7a/_¢454lmm+P{4Cܽ{235ݴis+g/ -nzzz s ѿoSf̘b)YXXajj&MBzǎT-1(-[1eeeI3Q(0<_zu}={20X##~[KwߡbGDD$'c<fr1d{bP€!krse|;Dp%l.]FRfMW77C&|.] {7yk0_6?> MyYJ+ãAk֬QUU z*<{,22*11Y7o|QOOOuu 6;~i/OO))InA-IJZhǏ} xo߾}fڵaɓ'wѝ1pu'N\|Ç  Z+ :q8byyyy{{#PS...h[ݻw0k`&=]]<ܹs^h:,IN< QSUd`󳵱ʕ+jjNaAs O<> 偨@w}{`5C"mtnn.Bݻa##uF5Pgn߾}ʕ!!!j+++r8O?$|p.] pԽ;ILLD]]\߿|ŋ@\eed\]]ځf qqq4k׮2nN?t萇ٳ!1]tt)S̟̙ 09 >AE!c?pѢE^ /'@:޼yu똫1~__Ͽ6weh``nnt˖-SRR ]n]LLBEtuu]\\BCN4_F5|Ĉ10(ѣGoVPP6nȜ=Lq {yexxx/mPw0d<t KnU Ai:.\---EaHIIITTtp rx9O D]9 Ȫ&o\$222BZZZ nܸ1|tmW^dbqY|y;/z*33Ϝ¢7oެFǚ5PY'ODRoffbqobmmZb-//344pWWW;AD ,++q@c]xxr8>3v6ׯ_G0x{O)6&]1464VMMMT/^ U?Ak! >5׭3gd 7oLIIPWG?''qqjjjd "SII /0߹6ݛ;gҥK?Y[YڵKp@`]𪪪5k-Z>^p1III:::޿VU} &&Z^^.##C8._ﯠco LEjki221OYYyݺuEkkk"Vg͚enn\/X cŊrIfͮSf˖-kgzzxEj!n̿=޽{Gߵ9!!.)|&T Շ;5omVD󚚚._ GGE56UArS lLF U+Wmhk<#ddd͛tsxϥjjk׮x?O>ex DBBT]]ݶvz-ܹCիQP4軂eCBBdee>, 8|< 9I4EYXX0GBȞ,CMM%Kܾ}?*/To߮5k’&G%&&+ggcǎ1Y>`;88HHH8j԰ٝ;w9^ .qѽFo[UUj``IZzuUUեK={6)ۋ/> $Ck~oIA&M H@;ߘVsuu=rC`.)Up`-{iTn޼9/$o[nP}"s6GYYg ޽V't ;!/XG|nMw RPC藱QYYYkڇ&6mZg޿3 Xk? .6>}`)h"KDks?zYVhs8py9y;wdӧ#џ Bafeejժw{\$a <\B|ջ7GPpASjjTHHHk3&M:t}L leee Jcc6^%mi/_pL˾}Z;a|Xp :s Rgn2rq"11qԩSGyK矋KH9(Ύ;/cnܸn$, SĶnڢ7ɒ!!!-*C5dODgdTWW)˻ݻExAXXؔɓQ}͛5o 3qׯ'9Arѻ~ѣG7Ϻt100@Kta9$Ao-^xĈɄ8qb^^~,k۶mFFF˗Ϟ= awHӧu'N`sEI$$$l3㣣9N>}u֣GV7n(**^Zx ϟ?OHH6lB5jʕ=Ekjjib9MAMyg0D+ oNH_OE-^6wÆ Ar㦱q֬Y_5?a˖-Amٲe툴Cn=z4cƌ}@HKK(++)*X,):x544i@kkLM^R }$ 6 ,WSSeKLLܹs͛719#p޳t0 ?@Bb**(attt}}=߰~=ǧ}7(Q<8G<}܊ǘ7#66FVV<|0!>~ĉLv\]]aZЎ旔sfff jO={~yc@yzzML2a_}UƍgggWRRB~tTAX^>44tРAM 1~ڢE/^9ct[s33}}}M СCuƟ:lĉ񭺚}/T cŊduiX:҈ p8fsYn]]]rI!) ؉JL <%K85116Z  6urrhbbM.d~M4III9cǞU]Cڱh޽6gΜׯ Ziҥ XMtǏ_RUU3aD[[UV h"4acsrr?|şABA|0_tv.C/z3rsر̙3/_>stmmmuuuyyyqqMV\3i>}~ѣGݿs!&L2y2SN v1B#Ft_Eq_ʿr֭9glp.">|keewrr'ijjN3kkkcr3sV, ;v,s/l3a`蘘:a|USScccchhXZZ P|[LDD?mo>w77I WW{EE @lāPfbllfm'ب\@;(**,X/ZcLKK=ᅦ屲#ihhHNI1bD?ݻ[K[{9])))ccwAAsET0 9w^7 ; ppp055a6o዆]ul޼gϞA8 /] 'R?7h/{}7HHJXY[A8l6r B<#ٓXJӦMۺukeER{0(0pup/+w i_xGGEV/;{nU@x:-Z5 uk3ky7 ?vdd$k׮-X@b"{Q 64y}^ttt: Gfffc;iҤ~ k׮"<֬Yo``E?cYzzuu5r=HG=y_ GPpwG9??{ &`*.JHHXtWZf !GEE!/w؁D?~b<=QVQٳ'U#}8G55Ս7bT!B 4?!AA/^aZ $WQQiNT\SCk/uuwxb,sRp,--W\y7o`iYY@Zp0O8bll,ƌI9уH@B#$,Ybiiall `پ}{|\#,sٳ~͛7ׯ_ -%B&r-dX,%EE[hDc25bbbZZZ7n]IIIYb4=?w!~5 xp[[[,1&&…  >ܹLMMc-Y&0"'': Nx!J%)!wAS#Ȣw^ ݻ6m#y!-5k+ڵk׍_}}GF<Gi:& @Dz^Dx|C v72eɓWhGϞ_t޽gl|@[;ی5kdee-_ˉRO,a(:PEJJJs8,wb̘1/^h. |c##mm؃E1--| :xիWwƴ#9v#FVVV`rf>"%JMMMwwjV־}`P7kf 3~idd8cǏJMTׯ_SbhiiΚ5 (ř3g<ձ}eee~MOXwϱ%s7(i&gtT&!!LZZZZg?mXYZӏq}}=z ` f(TcҤ_a >> C湺"޽F6|?pt1'O>|xIR60OJJ*((|rssWZhJB||ddٳ54DZ 駟0r߿Yi`G(իW111ýlmmGñP~u>|x||X#wqq:e 9؈6 Kf233êik uuu E9}Z1|(شIKSѱzԩϙ3HBBʠAqarFZ+WTVfJp("$;;]S zܢ--%%--hC)X>>>f3R444" !:t(++k/JA^^CCCիy<`'N%XZ:FWGƈ37ss[ g3Q}C7n`u]`,[!uRm--;׿@;᫯@"s!9qwSE:\9s?R+ Ɨ/_޺u9s] q11IIa31dd7ŋChddd?>3OϮܸ~=(0PII6gϞ!,Ow㏈AA8~GQF)}wD̙+غu :y?((o=Pj=V?y?ƌy1gggv_f` +NNNP&s>z0OlXFv݀r8 T~#G2%.++ H 9G ",xjC !DlW?xV+++#!!>cdqqqhCBB0+^ʊZZs` o>џn.Y O䠛ffG駟0$22 !\Dxkך/{/BWOy;f|>4 \7WW%EE޵Oڰa=qgϞzkষoȀ}|||r_ 1uX,#000w͚Gbc%$$`bťC\ tV ZyyZZlppҚxi457onXXq /Aq8l$<DŲڶm˗=YYEEхDG ȹ׳X,OO&>Dј |`wю;w$%%r8hUu67IllZDD8d|+n޼N^Ԕ+SF())%f.\PRRr̙ϟ5 lL0 "  Q_* , C \d#.\PZRx3f@5N3{vVV&說[ 趢 QC!9pcc#F}jH\t>1rkɉ'P X:FFFX.7xHzHy1H[[j?f~9S3.]]Oq)))tut==<<\lTLcc#3ِ6z={ذa'SSS>c :wyk"!7e$7WW zzȤիW# 6mhkϙ={'`>v⾰c]mm-3iw tʵmuC $5KZZzȑgϞAwܠl\܀E 2o߾cǎg"zhЬ={ 1~LL *>֥{舵… !yyyyǎy/x_wKpwwAgffrrr (`a\ʂ0A<==؄ ۶m[h&WS6*,,bE7Mvq.A-BA|| &ї-++@OD:;v2eJ/b„ FFÇCP0fG$`MHSk++$<)&&:{*DBBnݺuyƠ nE1vɼ'xٲe.\hL';w.ԇA퀜x̙OcB#s玫kCvJBW {yyz"`3}煄Lf=ࡣ@2!/^ 5t!.[#ѥnhhxm?H[jkkݺuڵk.]'tuuW^EK^xy2UZZ>(n޼YOOuA1ZB|-Z'nN/h@=}t$ 5hbbbdh(%)?t90,ĉ"$P*t{iip#uF?XUUUFFA ::+/pS{dee-"DϞ=  `dTˈdd3UKOO0u?VXvڰaAYZZjh+((B`?::6>ޑ˗//((:uݻwZ<bV1aիW&I4HGCcc.ÇvDFF2p g^ʟWg9x̙3LJK---%#-=uThĻt2P>" I?a~9@qG'?*Yrr0$ԥ|骬,HQ @A0G&`6X*#/'%CMNN>|?&SN'OVTT[dcI'(LB`[ThSaP \~HD >޽hyѣGэx񢧧g~ځ1ݽ{w\67oޠ+_QQdɒi&Wͮ/KZQG튊B_#W"//odh(+#E#kaT={Vs R !..;*xP}tH0H˗CPR0LVKS3#cECQxE8wbhࠠӧX,ѷ߶Z/BCCÂCy` AIߏZc3[l5Xq_g chxE\\\ݛ:AFFFbgČ>{N8Bڵk Nᄾb„ ۶mkrȇ;kd@;В}tt***ULMWZGǏER+ƶ~԰0OOOKKK2{1_A曐6X-[ 666+-͛̓&͜Y__ mݻg-^xW`oh.ɓ&&&ݺu>uw1Ø't0oLFD'0/X`P1 w&o4Znn*2zj'K**+W0E iCϞ=k׮ L2׮]۷o:{z.}g={qF--aÆ`w7ݻcNN?T ܺu.VmooHYkݳg0~Ο?xb##CeKIQq„ ,e:ē&ONmpQY… rݘuuu)z̹ȁR١`ÇgH >>|[tO߿?>>͚5/^{ѣH#b[t*ynnn'a@أ:ϟWUUkhhp8wUYYime%B[`\t2#a lKaڡ#D~Xbbb|bt飃 >8计cCC?{OJJ"o~ JJ11 0aYY%֭u[lmmNjC^^ z9xaXX$*ή0m6UMM &"##o߾!sECrr 8DGqHcғb24Htk++7kii VMJrt'#GƥNgO(*(x{aF23nh1%&:V wރX,AA"[ӧ=bĈt:AACA#3|>>>kҮS<''GYYyΜ97nhު 8)&ӝn*|zo)IɈpO>ݱcDC]M!(/+CWx2[C,6oތ$+--573ͥ#F?~:l0kHocc9H;bo~q 0W^9r{=Ϟ=CѣGǻ{9WL\\[[;'N^MԩSnng$%%1omc˖B}}m-z׳-,MurrsqqaNGqiWli1g555oog1C#.-[&))9h^_}ۘ4i[EE! Z؈DQQƍwye$kGr4htb߇ٳ,--s*?. [la)+k?Eu0S3gDG~D4}||뛾TQTTD=zCB;;v,""BG[*br,NSSSUUD8 y9n3W\ K%=- NrJ,ή%0 a뒕_7l/z ZM61 ۷C!:_|@8'>|))_iq 555JJJ'tY|vZPPv~d2UĀ\FFFįj{nW^kR555TѦ($qVV*+f۷o#TTT܎?i =:PSJPԔA5`B`kPBɓEδիWh1т ͛qp\trRSS񿦦-[?v9@!//Ϗ#!z=}tU//Ϻt=gwcnUmcƌn;qɓ'!OG (;|ɒ%H_aϞ=pAssݻw7c9t萣BtT{ayaAA3te@P i)) ~ph"J W0;dƌl B rJ|%A|s-طoq$D^G8g۷o_Æ C[΁wdD7|ÔDq5F (? 2##Q0jԨ90Il˖-s$%%17\z500ݢfAk6!'ޭY0eҥ۷߾}榡9h@֮]6s̋/ ƹv횿\tttXXrٳ1>X(o`sn`]`38r~˗Ӎ*D >>ӯnbUr**lh۷}D;8+|&TQV^hG1$d֮sNdd$b3gt4YQ={s8m-'`0`[BBյmݻⱙbmmmT~ DyІnݺt͛7={ּHr಻vrtttʕ;vX` Q ]!vg0hrϟA6ܿ?<PP5c%>|8uӧh+Wn8idh!C 4Ǒe~~~gs5kT2CCC뚚)) swڵkPZ;;L3Bۀ'B<j/^sn~ +%%#ЙFA_w*hWl$%%utt!h1zzxxɇDžm)t5! ~֯BWkq C#n;dȐ '5#kkcchhm6^~^TTC ąNܕC"0~ZWW7++ RR5ƆF@RSNSQVGcǎ#Cyš)))B333}}4*  ȅ <=!"֎6=L*/+k"`3g$&$h#g͚UQQ؈@8\Wrsu}vT0ŋ+++-m=r8$D'GC}5QF ԵkW!-ۿ 4B}}}pp06ZǏckij_gBAܧParit4aaa3g4424yBHFFfӾ}>/_p8~#UU9ӧwB$BIIyՈ` s<9`9))IVV6"<z lױa9w\ mhhɐ:,&@55iX+6baJ[/v5UUccc e,l->r4]A!666;v@{Æ ZZ'N 34 ^ĄfG-Z!Ϟ=Chm)>Z]]=݀Y.]&w"N ͛'|"}xCl8z`7o|Mnnn“mQQQ((dAېiX,%Z);{Rmulmm{iް۷o566ڹs'N1bhhrIE[b6mb=Xek CMKKkN NVf&b޽sꪫ]]]0-nMYa>iee ׯϞ=[NVV__[…0lw ~>(@׮]!ĤIoܸ! c[HF ~6|U'111*ㅜzJr2~"#[|]IIo[[й'icco>P!)--'tR?襬ukk+55Ս7",TL+a8sȠ}JNؿo:Ӱwbm0g5!HG n߾bnFhj'GGT P}) o(eEEEg]!2P4x 憥CWPWWW䱋ˁ l0\Gљ666fUbXzkgLX,V1`mmm8taah-OJAA6QF/] 6?l𓧫^>9`XJKMŎOKS}tY=2v H++H}==m+Wqp鋉o}cc#JVQA`>[[[֝u.\h LXS:y0NS;Ql1f3ӧO%&{k>gl|mrX/OOy99crr2͔ ߻w;&t彞ȇbКE +- Mh, +|78q>͆CԠbhJ5}tȈvCD[;bF&>9 W`%$Ga/ÉEDcgm̻ɱwfgqA6z6gΜn޼٠@0Z &D!233cnaa;g.K42s(ãwtlH0̰ŀ2~L}!K+Y)V^ݻwo ={L\x۶m666,%% L ŠT;X5ǎЫ&/tJ]ͪNٽ9]mLt42}TGVp.TVVFl <(H\>2[RRbfj~9&iΠͅe||<ʆ 1[WGڼ~ΜRRRި|j-vlfXw^͐sK󑑑au޽1‰ǑC:t9pUQ4zoP81QII7\|98Cy鸝355杫B`Ғ4(!!i 4죇wޝ0\WG֬x ,,LA^>999z c?8,g߾} T PU ϝ;ò݄WrsV}}}/\ *Q BOwwwtqqp4Nӝz-|9E]}am;\vvvrrM+aÜ9nhgL]v-(H^c&@Eb6+++lÅ"8Erm>}z޼yJJJ7B8mۻw/<@c!.˗-mIn߮]owG{… 9l6['ݰOtuq9s h30ڭ[VVHiӦ۳E;cLMMeeeQM͏`p>!VjXh(䤥~z, ,.3 xLLLێ@hTɔɓq:~ A/ nnnhK=(!={v( ~,&+!>& :s4k!Pr͛7KKKpoB:x 4ki@gM~_XYYY***h%+COrHǏX,ep!XBCCb˖-aK;ް~}X4v̽ ̌J#}c[Geha>֭[454z(h+`7Х11XДɓNb[l}[/^LLH@c(ڿs;͈͛?9QPPcW'khի{RRR#K$;w+C(!PZF/eU'Ϊzyyo٫eepzIGmc^ %E0aW7W1+Wᑛ杷v \넢bKF:zf =r$++4,,XmwIC0$2r-elu·R2o),O7s;K8vdHe'''<o޼ŽެlԎ"w=3,ᖗ蠠 KU#ԇ$44DII}wYY>=Q'N̝3f#Soܸ$ yyyRR O f (%%y#=}+XEnReffbc;r8/oM:gΜğ v z1o<ΪU6mڄUY 5\x[666cmh` Fs>}?TRba>^\\ss X*-w#YQJ(ݻEEh6`Lyƍ8[[=rQ7W ܳg9~eee-C|s|߶ukkYt?T22RBN . tr!H.TQVFg+;˗/#$%%Rwt(Eo;>~322Dϻ1wn߾=9!8tիW'''#kU9v6=SkVˁ9-F(&&&!!1z̘i:wN`ϹsU(@:ZA=)KI)"<s+((@7ٙ9Ξ=lfhf\.Y`HKKYYZ"q'3@7_煆.|嵴.`P*рoe_AX\jJ|rs|b(@u^1sAI^@TUɍ7ЕĎ֔܇;) 'O3֭[ӧ0 dDDOW7!!ԩS/x`=DVY}Q'';vn ڟYdMϾy wB?-P؆1Nd:qqiiihS~WNuz9rC'i[l6K.v߿LWW?1k޽{F#*¹| `(3r?@`UT 4ƇŋWUU}Dch+/+F }|gee8COrO\An}4j9͛W8aOb#J32V(*(\x# }:v0;wv~KK CCCtyj;G >z:2QRR2o{6 E-*ڊ4;;>8'9'~7#;ń (e#3EO ၝA^^^=\A`/_QGa=S;_ᔔd^P ===t"sqA{vtm8轙.+++C ĺCCۋqHCC"/u'N0y R zѼ9(EeeeiiiAAAnn.Մ̝̈́uvv555Eh4))#FtѝB 2ՙɛpʔ)MtEBBF0ec*('yyy*'qbٳghBIMMY]=z䭁1oܸXE_󀨡?uʔxM*aC`4b@&СCӦM/eKaaZ1ܹs@hYYY, >ХW^u =m9EGGKKIEFFv$qAIUE ^/s?س)38/..bggW^^.|dC8+33}vJee֒%K&/qGGö\]\suT quTmΜ9c?Z㭉Ğz)ݺu'91cΝx;wVX=6mS)-Z;9` AY,R2{c 퍩BCa]^z-Y+-|5jOHCx64!jMGDP?;9xk(b&7oDqIIIͅ`Hކ JuAJ{`[lA±C Tf{v*h@& .DKKKH18~xccczg@ll6V *Bݿ?t )ܼYA񉀟.:yiii ί]b]( 'z(Ju䜻Ç=Hg#%)pa;UYYԒ:z2O:'%"xeeeٳgUhijaȒ]v6P~j採Æ:8cbg*VVFPUUUh2M9w@8,-1Wpp0R-99f 4e۶m'jjj/_}j!~cv{Q6ogbZUۀw H__im?y"V˺u\J a C=-&(Jtwu2!@.h%K&`XݭfD1:,$X3aػwo76`V'jkMQ3]0)))Z}o}kD455Kk#pwsC933uL'{ vL:t~4Jaaa@#cU|}}- {򧧧#_T0''GEEɁn4"RUU.,QӧObÆ z;vԩSwC{b]Z[[999yKkxJN-҅Q <6F3%Dm5kʯ0eꔉ%ÇqԨqY[[KX(muuäIϝ;מbttM-Ν;֯﫣3wΜ];w1?A!:hTԅ]K`]{C6ndn}=.Zqm{D@EEzK^HWH$$HH %tz UAu:\FBsy9)zu>0) pӧ7`PW/+-/ tÇ85LMM0lڴ YD3gDGG㥡8i! lD^5 p!m!U\stx0E_GbmmEApyVWW npnMN?x𠬬^ÇA$KJZyălRFn7n@ҊΨ;vFvh daa_~)2]\\rs1*$šCO:iӈB?Ԉ}(d0 k U\) vG>'6iUZZ>V`AR92w\uuuN]x^`W&ŋPZ:K=0u{[r֭Y(ԩS K^{mذa˗/_\stl5P<>r^HhƍgdhtҦI\XQQ!nn>I mȐ!DV>#}o޼Yv5,Qee~E#Qcǎ%n߾YE$Mu 3ML␓HMݤϟg[ vvvt &ظq#qA^% zzH ݠ%xA9ǟ~i۶m(,4Tݻ[?{i `J Ή+R %[epM&*=z0_X#*FСC+c##:j~t@IQxCa6:DII ]pttnzĔid&͚HWrс!\'·cyy9s^UAJ, |1l>O>o{G_lJz޸ynpӧ2ƚ[[[F4*.6.;99444g(<11Ɇ8x^ _XW`4SXXzz4=iɒ%dV++Oi𥥅ŪUZvRPKa6=" >(`׮]OINTM~:*-[P>Gu'O [nn$= !NN%cpѾ©-`= Iui?JuSm.,,$uuζnڴiߏcXldhb5T۷%[f>''uhhh`oogkcDڵԩzssu5kR%.yƫT!~  o=8lڵfPG< D踻j8lE`i# JnnHa9Q蹅 5`7||lzKWrё@?|ii)$L[GGUUbŊVp I$zޔ)S-ȨN2/ d9XC1_"~a˦PTw^jEhκ|bcc*Hb}9M9DR2BrSrSٳzRLY^^$@sHxZ`SN͚)(ݐm۶yܰKrrsZi?hVhhc 5hѢoIp0觧5V@PVTT5r$xݶRtfYY?ȂaqpDس)]\Yϴt7G |,22#u[@qgΜzXY)nCΝf͚۷ Q\,… K9c"VWUdLnwʽk^KKWWra "ۗ4!!@_0 C1h$9$Ȁ@Ϡ4mӮ]`~ >bUO,z2"<\LBY-,, t҆/.]4&&陟o߾KFktY؂sϟK?q/APueee{EP` j^ /vɱ^[[;##"庺`^1N?lʨGuXoH,طwرc=JJJVV8R1Em`G+ iSbyHC=R]qq1F~]VV ^@nܸ3gɪ M|ђe$"ݒAaZ*540psuEw)~ӦM8Q`` Mp*<ɫĉeFiPO7J8$4oCڵzѲaazzƹcVVVH(g4͛7ME#&yNOUU,&_̋ş[>=p+l իWsss&@#6oތhfO:f49RQAEí^ \޽)w!b~!7V^m5GBvY?!" hW@| 6-^S4][&+V̛7/+3wPMUA , X׿m,Mȱ`DRI١i9`% А|G9B}hڱCzٲeȑ#nj˿"K^ uAهzk\n^h$ct}TBժUSjkkCaʢs?쳠ɓ'#P~ZZ(VBM\Rm!M6;V2eЭ[WVV&$`*++7_OFܺy_eK3>qf4'MYb{߽{>}w$GAh!v=Rref͂$<=<8hC>>>EEEٳg:ޒ#16)(( ?.fԆwaaÇpm0/ N2~3Tؗ ג"4 MqQRU9%C nܸ!`|ǏO9#GrJ sW Tr pB$2bL:0 |(<, F>T'dn;Ҍ[xH36ÎX[z믿>~xK L|NGiˤ;w\XXX-xy?GH tONv6{?𯃣C%A;/YSЅSsr8ai(@9䚣y 4}_^vR=c`SXnH|}| E˗o9x TKܤ| !,D(V %FH޸i : MLΝ;D͛7/\0///.6L0MII`agddR5bŋdQK PUTU{P{뭷.+M` XMGGS\TD"KDIS*CnAi 00///o6vL7, &(سgOff-JGh@ dӧOLܧ@p(*(/_ׯ 237CeY𽦆FIqq qJJQQQǎkIл[n.^"(],#91hgUUyWS]YY"oL꧂h9 o|m,Ŷ%ODN:x"zzfffw=x2ܹg}AxI' }\stk֬_ɱ$۷/իwxi&2~yNNNBB/X %EE!A7$Yn۶ 6ݻGd $5 ٭:cǎ{5N,mH"ۡdtMI(aʈÇ~ <\]T]M :AEϟ_]]@aȾ^n>>H zEx`$ .ļdN; CπaPd팍M?ICqٳKݡ+{{{P!͛3-H͟GyoܸKsÆ vvv666Ħd,kjjd+iժUW g\ZP`=@c!=)q\]]hymmmKEp`[[ۮ]JFSKϳ v.]{=-k$ir˖-6ZݣGhvVSU511f2w8=Guzw|Aj&-A9: $Hа#FgW We1D($|o߾}LW\\9yrLL=EKKKEE#zX[{yyEFFd̜pB"Ν;dPQ[V56 $v 744Iم+/'"UUQ!CڱcGKkE_$‹,#+%3 sqqAyhk#Tz$Xʠ}1GII1ݞ:nlڮ1L FdܹJ E>SS71z衭  Ӎ7S= LM3g|.oC X.Ysĉy O6V4.[IIN>}t+˂~1Sn݊p3n:, [¥K؎͗.]r %׮mۺ~7B_ ls仵5c$]tɏ={BmY?LPTTm򝶃/ :[WWwx B'<, yb>ZZLUD7֛oFΝR=zTPTظi%9^y5GGB _7o9=tW9p #>| 蹢?+JC}ח=%+kvI IܺE4L"PI SW\)8R%m#8%Ƞs|Btz]vkHņ lzFnmY 2( `˗7_לl=]]lլmD8c.\xFYP=:tQ>MJ JJ)AOuݻ1c||^6/;S&'6$(}ߖw^ј+V^=\VRSԘ2eSMu`th Q!&&#c.EAAÿǟ|ܿ vev)l9ΐk41hHسgO+8E I!ѣYU*0aBXX!xD "kjh˺+/f<=p F- :thII D"4ˡQGnn.QlOI+vh"5G !;h ŋX|o-4I !^Jk̘|q߾}m ӛ3g(믿4]zq 99(H+KYfaɎ:ƎE9T3fPWW%k veNGVT1z|ɾJF?++ )Y 9~x>a֭C')NNNH@mmm;[[֣GY{axQ)-Z(2jii1ѱ 5H3VTWWCj/{{,cff*Wx:HXl?hwkѣ1cFkii2^O9iiL^Ir*䚣8w^G''G~<]8n  e,_| ۬H:dvvvdHظئ_OZmm-ѓtBe.(/'͚q;pرc!۷&"֮]Krŋ).**"\dff SRR⢣84ޗ41h ֧C< K/Ж,YҖ2Z[?gΜٯ_?IePoMJ0 E!®54`d}}{siSPjONNf8//OtC;Ph,?>FC_$ =^Wo7 ˞TTUvttPSSj@k.]vʏ}7444S~geL'> XUUޕ W5V\\jXb Q`2dUUcBGX)dcǎ DЅyfkgg}ON9nk9s.FD :ukjj %%b!~nذa={X|'ŋW,_ PďړȒZ xIQ-xBsxXXppyabbb Y1GH/ڐ4H,8zMWWAuQgV#n$ܹs9WoFp<`LFgӦ-pK TO/S۶1RPBQ`q)'OMehe"(t L$l@^pIjcc@v > 9hzzDk073qF%%ƵZߏ?qM7^րњek6"E_ٲeKKuɂSa=jbdg''CYI RVMLL9|?z(%"4^~\l,!8-,,%Ō-eerx`#5ʧFAta!ɱY[o 1Ȉx _C$TP˟edhHi.#ruu%Rxo.$rԔTKff&EBV\\L͛GNOzt˖!)HHttu|лw_}QqrrB<I 闤ON_z5%q9 &,!%HB|#2A~ b00' \Td+n޼=ȫ8p 5Pb䈘777q 􇉱1U4 6fv}w999 ^MǐC}}| ǎbcc>|lz9Ν;g^n.)XsUaQp'enY9 8Q_xed"d,1-ٳTUI<]" uGy[yF Bŋ;GjOq~ىe3'L@$ sww/,( md`$y]vuY;"s #Bs#9>6&F[K 3x` $>}:Hѓ),yӦ.(>#?̴h"]=Cp5j̙[CtuIO!k~#cE%s -I<#ϝ;q򒮅dbʕBi VRRB! ^zAjiG;}XR_OD n۶mɒ%. BLJоiJ +Zjĉ]Z[]}%CqDP翊'􋃯\B&Mر#-W@8͛O>}=66]x+KpAs#sd٥_[[k#ҺVهLWr޳gpoDsHk`E׮] 9… C=GMMmvI ݺuD|:*W@qUWW{&O䲰0WTTo_kʠs_M||ʕ+}fdde$u4BháC9 ,vLٳg %vDx8Zg޽*f\\2ZuVKxmDd 5Ǝ98'Ǘ8-4 Y`4H([[LjJ֦%8 ۷i:,lC -u'GG2)m2T 'QF'[so&Jv}dL%$Xdcfff_;O8S5eZI"k7-X KD]"#pAmAr$pƑlm (0А^YBZ"2S@'bSSS5\V¶e 񐄷7)I@x9cpd]]j_Cf0#8DHI),,DCí,-Q999• [( a)Z|BvNm9dw0jj*JJJJ C ֭DkѩS'H^~>*ys۷UTPmABhhj@04ڵkE<=<_suq8q"`(8_7|K.of?1r$1j1멢ႂe"XXB7qa-[02}}c(vʊ|@… :& x @sߓoeKGG݊/ƌK"Kf`s27M6@~PBԩSq!JJJ`_`ee yoذ~h9M-ua?ؽ`A9y$V__Rf{@8ww$EjӦN3ftQQqEڏaYggg4(1j$7n$HEHX///mm--XYKS [YYqFid͂`mmGGGGRDٳgCCCϺk4]]++卜)SzA{N8P.]$;==-88I!<~>EU:۳EG[@_[CcݻwEܹ3\޿Zоx /,,sFB/(>|CjX{ePH=zoF1 MZG 8:t5#\c3gϐnnn))8li%d@ \Zř5G1A4V޽}vRIAM ȍȴ'ؔ ?`bĹ;wzg/U@4BO_(**zxz)%xE(6K#㦍3Nȏ?D5d /РQQQSL+V D ƧBedd0)1ABx͝;5*44=#i1Gtuuur!|xϭPlSN%! CB~d,8[D0M%ܰaCMd4_t9 ^X6iҤFJ] i={6-Q;TMIa/]$99R]v YѣFEEE]fҴSɭrse( gtX]]#HELII!YGdggC$}s7O>## Z55UwT!)mS4IXwwwdƨ#AKEDf-mf)+ 7VU􁶶6jA ɴ:ҙdZ ܿ[n݂ $-/ŶЧDDDAA(Ę(//WoTD{mmtuqHD&JŪ魄#:vvv~/]t>bfdfaImlqTB/_ a>287ߔ>#їVV,o//cMLLH}$}ٲe4#$8Az9hr@H"&nvo8B3/Fo߾ K癚umٲE6R߹sGx$ *43m4irVJUGāYEEESn -ZC`jyy9LlJA\ĸ~~~;wX(LH0^hxL(z/_&ze)t$QQ4w_WzC֭kZf{!p A;… WA<--,`,$&8c%%&2]*A .$%%i<}_}ߪZс Zenp~Ȱ SsrP33gȚn8ͭ\hpPJNNZ 8X]]3-z9;v숎fР4wwG2@#CTȊ+JApy~o _|1k,҆K..aaF43"}jO ,F9\st<b³/ ٳ$[}]h/4$dɒ%$qׯ_ogkkhhcDOBG H̏m[7>tH6ʕ+))kTM֮XL$~~/ihhTTT[Ο;W^^NI"Ha?XrܟAYP^.mHX۷7>> AΟ?O~:Uh"!6K!jjjpTIq1"N:uRVt+bbZNNLt8G{;g_ߔ‚e˖ؾӌ2bϝ; ZZXƋ6ߺuNDFU'|B XRQIQ#G#.:zU+W"._J! zMM 1);8ƍBgٳi ?0P-K07nohhY (}~CJj:} Hc 0 '=md#!ˁ+W`J!ǟ!DIB' ċk׮-_<""Ęߟ䏘B(u~XZi 'B*𐿿$*=~HOK֦*`M6mf üȰ֒۷?(_~32( м:D-cZӧp2ߧgϞ:u<{.7ofz{{aҤ$O @ X[ E_N~kT"Px[[[d>jԨw}WDt%1[7o>|h]uuIqQb|8k+3c'ǰА9K/F p 3f^aސk'O644u9s5ujr^2޺ukꪘhBCCQ- xuk1 :SsrhI)yf?|lnn΄3g mA'N  B# 8E \-7 ~ǥ,߿OmjbB|eKr8w ߵkגcobnff&1XsV ͠Uƹ7:,[ف,@###ŕ Bը(rh=]]nē"Ui6GQAlZ*Z)ϢEI7GH!..q C0v,Dst _`72:r#(atQp/ b<1)jrǎNtmf:Ѭ44DJٴi A`׮])^wܩvuuA^L<HyyyLjշ 'Ω))m_" r@,@sY4oެL(n4q֭[>-1`-^jIvLiaaaeijG4\xZx8驏7),ituر@HnT{# !Θ1]Whf(~ucckG(hXYYٶm[Xh(GI MMLQh?@4gFɿ ,{ᣇ1@`G$M|fG< {αcup!YrbBLFQG\: b+2R 3a!pff&ڿÆURT464 ^XQc۶3O#Qkx#XQQFt0^^tD_Oo+[N#86~aAA#- Ƒ)))F=Xˋzyb^z9B9hk[GRdU9:4䚣ぉ-[y5iر42'q_r\PB[&-Nݺ%Z#v~^k HlgkKSٜ@3 dרJAYr'W/$2++vر+w!&VVP9dwwԔ"t``޽!! ٬`,HXΝKH6b9F\vۻωt zN=ҼMLLTTQfO555AZƍ&WXAْxE-O_W89b1) ۷ibl ~"tVf&捍ٳgIđJOq샃ȬyMLM0LG I& GS|XvyPSV+++1c}||TUT ׉?Kr!1GsrkRA|9Lα/OO uQLL )͛7F$[SL!vuqϞ-%9[XX\yN(UB!lMlkk~:)B3K,!y7Ǘwla9d!lO?.>!9s3^{O>-q5@~++u!GPPYo_wcM AEzǎE ϑ[oذCOΞݶmH( S2vX[QH#?RR.\xȑ~ PRTDv>|2kkk7=]?rn]mm}}I' $-h8aZt)>LtvMG [[ KxnWTZZڭ?q/_5k/#s3pFe ^ j ̞=A!p넳)YY::@{"0c{[RBrm!Ds+'I ǏADjlR Zd7oRVI頥 ~/Ӹq)lhhHWWW}5 F\3sL2r2L"m c:9v*t?9rÈd /]JHH@cBdb.؅%w:4^4[o8p3f4KԂ)r%{{{ ĸतlt~l"<,quK@455uuuݼy3Mbݻ u4qݵո%; 6m*#Elv"`Cjۅofw7]M/<8>-r7裏Ȇ#ZEʇa}Ħadе[ׯD0UWWxJۃUM:::v@?(( $}#G6z+6lƍ?~~:#󣣢գ4<<< Šy𓆺:L흓~ݺSp%K+ *lG?ڪ(+#֭[%B]SSյADbHH:Cϝkn 5n8f 0ij5ۨrwnVs>8q"4$D__t2Y9('w<ޅ$llWZAb|Ɣ WJ?y2X8|÷wFSSC@!z9 Dpttj;P ˗-'khh}luI>>***))e>nES "z𡀀bԩ ݻ!H^(s׮]Æ T/6faէagg 6m*JRN#P4fܖEMҪ VTT?~˗e]^ (@ 9:BUU2f(*GfDG=c99q6YxB'{{Cd7⹃ H_}]Y[oBm 5ק7UVtNaC'==|x͚5X Avmtl $G\st<EC As>1 6$%%YYYsw'>>m:v{o-X^vZ+KK[(\| cǒ]W S ~K>O(tIAA ǒ$tLi{9#7mD={ MիW׮]_ܹo~~ouu5$;j!== a*hF)@*9rDCCCt/033CdeeUTTΝCݺuΝggg'^oa痗~ݺK.1p Е+Wh4D~~dbB@81,axpՊ^\zaꊾD,]بFkjjP0,Z iv ٭[7&O@Grc`u։'~k\'Z ?QSS,A70Iqt!]RMV 0pR]M-' _o/PzbF-{UUSU/3_A=ZSKf,\;k#0(?Ɏ6/*++D?~iSEme/ ьJyY/Qx9>k˗/WVVWv@` i uԩ66cƌv,JNn`M2ry3o&իW!6#UV.j* #'Ӝ>=6.DF"n5k4%N~tQ d_]U%wH\Ȭm r%CC-K]]$dgkKa}G>B4ۻWH[T a'Fڭ2Pi/_&FbRuuu* SwP$ /fjbIiY04BHD(5 m:< ŀ{pudЂ rU8xXrf. O)8t={6&.7nL4q+ڲC~Q ÇAvP윚c#8$'SƆ(|j7k 4 giiI䚣󬬭x:t> };w=h)K Յ3p0gg'%EEc@O@ 㷩)XRSS3<, F@u˖-Ņh ܊1QKJD8Z}G+@L0a#F:lW9r9v6u*F| ,\pzSSЩ,V9kÜnݺIQ GϞcƎH׬!>(b qlmmUUUgϞo♾IDAT;OMNwPN/_h2"-e4uɒ%1GqiZVQѥ/77l---q׮]O-wE F0t땕=۷o۽֩hALvt?;; #?aHg /-;9k@VJLpctEQI :!y+D ^cǎHڎ@mx{yY[[$ZPKw"ZFٯ_{^tkŊϟ$5k֠?=0nڴiz~> 'F@ccb͛wAZG{4'^v-.61f@@@S/E !/UVDG99٘΢9TQ<:::h8XV|C*++#t\N@:DDDPoM5nv|S WwnO@R ɦZEzI]\ZYv2`tmUi dΝ%@E,666D aIV\ieiIP/tj9s062Zl HH #!c5j$ȠK.Dgw!<5ȴΝ I;88֭h0"UOOWXד/ NZxO?DMF@VMRxuDsZ7n%B*EDkhhXJf8Aոzj|\ŘNȬF{^~ע"}= ͛7Eow_!L!l!?AtJ׈W`I왗']Ri]F9k,9, *2xw%OwaV;hj0WVZcgg y#P3~Wccc汶4DP9mA V˗/d,XmQo^giiigeXWQ1)1iVDc&L`llD|YY?yfrw2-[[[dJƌ !C@N0%;vܹs 04ptHۀ.FB<,HFp ;8cbbh^FʹQ8Jظ6 ĉhtC/x !!Xr'O( uuzRYѣCLͣGNBYZZ"W^x dƌtJPǛ|3hb׮]qqqtĉxB[!c}¢EE/E#ioFǏu<%0 Mr-lmmx! Ⴌj@́ `L jժ#/F9! 3/L).]1Elk pytThɓBX9 c!Hy9}C7Xl-Y#F'|Br@BInضmaw޼yIIIl=jԘ1c*}}}+++_ǏGŵkBOB'O\rbF5c_~EP t_i5kٳg- %!!{8Oȕ233CK'U6mZJ]MsҤISL $u5jR <<[n9-Ē$*bc}رF/ , 8UEE-k<) #zխ[={ 4i˘2ig^PFB`/6E--_n1J~zq@e3SNR, ͜<^Kv8/(}׍gΝquaP#iį4 ف<]7x d+++jt`);;[Ȭ6[0 #+O Xy.r-!#m*qO{ǻ0Yb(`Yѓި[R&,B!$-&JMM Ͱ(vvr LII>}:3ԩ7oEۇ􉎎O}==XxD7ߌcF 8pvG@CfCހƳ]|8 {[3rm{ ]b4 JCDURb"=j]rDFѳŤocZ||ݻ[_xA?9qs__ 1?>?ꫯَWHQ..vvv{Spdަ fXS33s+AN^/~Ԉ d_qhֶvg]QWr!A #_~/"GzcOqQ1j)--֍~$;^" %%GJOO}(UDx8ĉlq'EECC 6pTm= - 믷o>s ` QEGiki V%u#F񠐐/ :hTUII)@'|\Hfzzu2rٿ?}D +Mp LMM~&LxbH副 m͊YUUU[yyy8:!y%0jĈrqзo_r…6dv؁݆ y]tɏ={:::2O 5g%..ٻe8/x:w-]J`v 1~gfA,8˾H' R; =iɰCBBDB̴{R#6&蟘b kjj|֩1׾4ܹsn=b h9UDnjkk?D!C >}|گCԤ$#jݻm$V@l+KKI.]r7nz]؝~a:%F@SZ~$+X^hFv=|Uc(̅`7CRc ;\]] Ed\t)<<jCnt_B.hkAnڸ&Zbn=.~-͛B;aaaЕտ[Ntyܹ49q¡r񊀙LD.5 ByL8<^e@rp*YzVff /褤QF^MM~L ⷞ$_(+OM 馧j)Ȍoii\[[J1,@ bжU"1ctLMl#CÏ>HB2x׿0)))===+++??֬YS87oބ  z-{,/]?`=ˠ mBwt 8WZ204TVQn7z 6IגGWW(˗dzGJΔ`Zb9F0 (S.nA>>_u7R!i'/ڵ(q V"}==)#y{<mm-\elmmjsss544'OlKLxd56mF5ǫ':d[r)@{큢ȡa#M=i -߶m߷oHaAx̙R®۷ocUBEM(6ROCnd'NggccS5RtzGAPӝ:,a/ v`?SLC/0ь3JJJ̙WTTИ+V֯_iƭ[{cǎ1Νrʍ7;z!{ERHf{Giii Ac!ȿ611 Hz7f̘޽{KN{ QߏDDPRTDk*++u㙔cffJ U͏/ hjj̘9y25Z= 'OHԟ 2Rw{x뭷> }7(ǀk|MgF J_R|vm5ቌ*,4tzF /**$!&`IvE F=z,{2dM۲4ޅH_x]]U%wH=ދ@=:q ;99!y ыogp,[n8@!G>|8#hѢSX@SSh q\\\%2f, ttt…j.6494ᅬA'mٲ̙3ΝxK^z5ؑ Çgdd 2AٿN/_@e/lݻw~`K#uup%{evm M͛cccu&5yy L b OH`͛7ѯ_o#)j^{o4z6AWrjD*~8zk s Yt711)++#IvuIMЖ,(sN:Ek.eEJg F/r wRiӦyyyBJJJ:~~~>۫W?dHC~CBuMJ0KXX؏?H ģ/k/;f-f޼yE* Bz6?~ CFHvHjTСC7p@O p##"Pl?ٻ *=QXP z#1ÂrTkX/מXij??Gp?߻*RE(0-ҹ>11G΃4ң5+ v  351$@K_MMxbp%dH!a4… &W\0 -֭8v>رc򂞒 W#Loᬧ P$ffH ##By'̀7nHpmnnݻ(8A㕕rk@uaܿAo444߿߿ѻ!Q-g``LA=z)ѐ!554JJJ|jP2<.Ln^^ޜ9sSB#^.ϵ殮v7oޜ$r 8|mhhhfjIWDI]GbwBXPU>}>Ӂ_=lx[:ESڵkS jkkn݊x]z+-"dkPP|-8O>}kr|5ǫ߄RmUUU*2 cǒž9s"9᥃xO;:V"W0`@ #!IO|$EEE5n۶Ʀ5s=tŠ$d၆RzDP'D؟z!!v틕 GI^4/_&o)3ȑ#'D 31#urF8؄JKK2OR%?h> 攬,٫8V5"Nr%䲳;ZCC#}moȑⷀs ]]z`+ ;w\~ŋQ]]ض6l`/_x 0RF q)ܭ ܧݤ36FWhkЉ"}Ikd I@9^qSRR` B|Þ={8@r j&*Æ kҢ#F:up:˲w޽.΄x3Ӆ[3rMᔶ~ʄ`(ǰH&1tK%-))^8z/Gp~cǎm޼y˖-wF +IvoڸC0|ӦMeP ;:`anZ~$8oϥm[ 7[x7+:]bblsNA0:iiiM%{D(;-RL0A$ "#C}}|hh ď`6^` {1/gqnHH0$&#yyy2gܕ_H">]DWr~ܹ }}}333??_b^b2M6M6LW]]\h8|N)nGAAH?yRH%%6w}"`*,@rz)$sNDuֵ>m&;p3sEl%AR VbkzM@,{EV13sI2/ pquOEdQ`>ݼt钰q@ޓ&MjexY){kEو{{{3ٳɜo_ DW 44DOb5 K%ď2䨖˗#kjj H;Vwiz}w޿%v!@7>$^0`e˰)9>k BXNv6) gggIvRz"WRb"M*lff:y66}>wֽ{޽542&N9s&NP:gm`Ċ "rhHHll,uAy9?zŠܖ%,DOrDRCCf6lpeAHΑCl7 ן}I!|y㓤8T$$Œ?0K◍;P CବL`QQQii)Y!zwΝ^rk &,A @qqPܹsǍ̙Cn@#V077w]\[bEVVʠ[nv v(IhѢh ssM U\\aF:,))!M9^a5D"BDDYրܹ?^z!QŪU fAf";F0bMMMŀ~Gi`0 d޼y~:ں::ht&^R`gkm6/77WQQ8HV*{X_rD}ŏchiiY[Yq),6fO% )>lfnh(QFVrhxڴiHa&a=#eǢMMMRlŵ0#:/++Qر~m7ސW:ֳgXa=sCGѤLIeeeGɓ&[h0UgϞ:ԏIR35ǫ͛73'O#؈>?I?y)@@!:tLH%ѡKH x`~ 8RRR`wK GGGm}N8A  c斕KGAU2B9iIsyAʈ(yƌHa \j<0͛G!aB+&&YV%;MrǏ ӛ3glmmW^ g#V\ildy粘H$^ڰqcʹSȽ454 m["#UD"r12"rR IhR+]˩SΝ(Z gdDtC.7`|A+g)**;FCCڊOZYlhh'4OMM555Eͣi۳x2~'DiiiXh()`XtSnc^;d'6rtP5+2{wx!ݎ=z4AaϞ=lv=H_HU{B^B|$<?(((%ѽ{ 2dJJJ$'"`5kֈyzi0mHJJZpÇݻ'T&3F!%[4hВ%KKҚ5k֯z%%.9Pخ..EEE.\hzy#۷oN*,0ajC1Φpiݺu+MLIW68$77Az`"Ӊk6l2e8wwъWooodݺusGVq]9UQ`^^^"ӦM#Hvaܹ&ڛnwݾ};q-ؘH `KM$᳷'c%ׯ_J+?^hndlܭ{w Kb9.]ڱcGqQJ|Ӏ]v S.5;>|tR^7~QQQm| XZ[[˄Znj7={6]'^y4v q~x ~RONNvqqҲ}WQQg"̤کYNw!߰B%L<ٳiFPhqf(PW0YP^.pA,ZăTCNrOA9^Y?N{ckff<{v %.!\޸qL7o^AA\aAd3gѣz-ۤH͐666:DS"gg:(u{Hŋ5jȐ!8ĉO8ȓC1c􎾄ҙYD+++ G7׸ar ܹsccclm4448j/,,Ps3X̉̑3wlڴuj'1p$iBBW;%t N|}}UīH'2%Cя@Yrp$¢Z9B9^M ^:~|{";ۉ$Up+(aE.8p662RSUMMIAB 9[vjN!iSii5kP-h1ɟfS;Ȱk׮!^{+))999OM;M9^a"o{b򆆆ɢe2_~e߾}iii­ؘM6b݊S ..Ύlmm`.l۶Y|A<*yʔ)s.!vvVōjO?q foyP8ą 88mR_LYtt$ @]h|rrjYPTTdblD"ȹg&;"`ׅbP#''lȥ9=?Ȉ$Cܜ5R)_MJJ"/%j?-[Htmo߾񵵵׆~@#_"So: … !cňA-ZhܸqzwoWo.~<)3g԰)Ϯׯs$yΝ<&&q,FNBS,ʠ2EwuqAGFFH %ݻG:AIN,[ԩS1zBCC%GS5++V***r8m4쒒7n!H!TVV"TG(`[.l `0w\jXgϞB|'#o#2OM/K{+a%r<;nݼVP0sʕe^o̜+jjjl޼ڵk/^GqqqtT3<\عs'R_ U k媕+",E/[76 G|}}Mk)YuYnn3}z[޺`m۶d r !>6Ɍp>}:dԩ#??_^9Y!)33sx}(H,FFsd=ԋN=Ԙsw}MEөt9NaQ2r$;;ԔzI'H$Ț}@T$?26&!埸CJLKe*!9>kW 'NMYGɾ6Ah+))"9::>gʮosɜr޽8q"`llR 5޹sŋ+'fd7j1Khy`` ͷֵoݿj mrpB܏iiihvmmm4kkk===CCCܕ`{~wd-]#DϜ9#A!bhhkch"sO4A͆ 4@UTTi믶 x։^4l`PXX(_7*G+kW ׮]ޯ@jb(ݫ $5R(hrr+ įےP9s,-,h[~^۷zD^aUƍhJ,mjAmƹH|'etsu540;vY&%啔|uƌ *r<#p!i̙L_ PSWSRTDr8:CaX\EqN\Gq}{:06LJf0eE ;,--JL(ɡ)Df:3cƌ5rrpPb mW C mh׮Iv!GskW$$$+$O!䆳Ht*BDܰaó\,%!#X[RS>>>ZZ$s-elH$8NHʈ }9t):x\34Tt:!>ctutrmW$C_!]vy7{}u\\YO޽s?hr穅r8͛w)))155;w.])d/)) 5333wtt@''Uܺu]I$$$'Nȯ#:r" |ر8K DFFΞ=CMÉVm1~ł|" mҤI$FTD̒͛w?H b[vƚ)YY66DL?L!~4eppp~֭$F4ᇯ=2;2dHKKm˖-;z(QjHùs˘^^f"&&]Ttk׮tsu52߶m…S"hbmzo L-[$;䐣5ǫ:Ã*/78zcE! B ݆B?O∯/^x"3>%\=PSSfQ")))@/z#R#::h/^v?bbbPHHi]]Ei鋽=UTWWONNFU!S?ϡCe]~pToD&jٳg+"L\d\Xv2M#""Ԙtqqq$5 &psڴitPN+x-dۊ+R5G!jld$B9DN"2:ۓ?Ι3|ݑ5kpxU YW/d`kwa#*}{ca~# EEE7nܢEOwjI#}7)!-\(5X  thLg)૭۷DhȠK.jrTﳌu PSѣ([&ˬYm)޼ys󽼼4UUU]]\ṛfKKKI>Ə0n&&&ǧ{F!䚣c E!F(+)5b Yf OC2aDP"y鑪=1!V8QT!};wLB 4~۶mX11;UTGܹs絆ԈٴqcLL :)=-oI&544x!<=<<9䐅\st`@kkk+KE5Z7J 9z bbb,,,444,ͣ.\x)J##ܹs}+++20ᶈd p֮%R$&;{p g6lptpPTP {"T͟?_pUCR8m۶-..+SC-֭[G݈0:uJ ͕+WdW,vIAAd]O-<sɓ8S]===غxbSSSggч[i@>uu(pS 2x %Lg@ك=z7ʎ~v4#rD5G󟺺:رqq/^$0~ХKSRR[aa|Rp7PDTPDDTVV14\jիBCC4Q?}`֦RPQUj/.A9~xɣe'q`jd $$v4>}H 0#4 ܮ逃*VUU5yd:)l=]r%6&Ŝq3glӲ9U_Ĕ-u$gẎ̠Fg1_ь~~~>HSSS]Mmڴ7n?=hcʕ<@x1mml Dw~rf5GG@ ;J&/0abV[@ -,,޽{/ޭۛoٳgϡÆ)*( wP}zҥ(QkkUUU@ppСC/be1=# La:$WɴN.9B5Gz۳woVV-ƭ_Hnɓ&;k,B'1?܍Ed47"P=]]R܄x-MM!Xbp-QN&vՒAJGp9 Kwܙ4i޴i:{ Mﭘ>|s̫ \ EۻyȀ>>H+WΥL/ Nohh,EE駟R;;[NeWUs0Ĝ/AG[}YA ?r+++࠵y蠠@SSSt35ZÞc:D`BUL4fVdDĩSr<r@ܹ|rIIݨQ:h "]jjjqq1qH>"&:tCeYD@d޽ e "cǎ{dd/]tZC2B*07Ν @)Zaooh`@n c#-DӸPppGȑ#;#+;zm:~8G_8XÇL+Wf '4atlѣG8VZebbo>^%j& 2YPH ???m--G```ld"I RRRTTU4HEEEWW71!]Bc&.]Zz5ό@j̜sN$ J$0ORb"5r$s>n9h'7o&%%>~H<==IԮ_.{C /wT=*СC[n׬YCY차hڴiXo!G_ jҥҵ#cddʊ_~tT:Ln4^# .|B2)+-8R hW_ݺw{ ȸC$Č2. |aÆ\___ aeih;p?Fv-|2c Y \QQ5E +blÃgΜ1qz޽Y}D0w0 PYYX3a_Q]u5cjjjdg.^rxe  Xlـ]cOr#oo/|'PHLO.Ebe9UYhAy9 ///33s„ QQQ$d𙃃jD---OWGx52ȳgib87N]WswwgpI'v_ɉYbޑ?;w*(>@dy삡f"T! }ᤐržIYR -9p${zxPBdDDB|<$+33?/ZJKKI{=ISSNiii$FHsN''GBm-T$QRbA||m}zQQQii+**P0+VXf uӦMC;uԹsg/_|u޽{!)~аPj1hOnݒ[I[/{շo#FDFF`Rmkk%%%zyzҋ 6*]6666m2>rb<-4jdo#KY-R:4jcMP PUQ mC@9:~g`фotҥK޽]~PdPP*|rҥN'd/RFFT@`"A$k`]ؘ͛7݈ڒ#Z`N2@f"""IpGWnw;8xPJƂ"BHlA:~#[[su׮]C\ֺxܹ3gmٲh,[?x_h,p$ ),=&+Vh68֢tݽ|}}訄񩩌)S+~h0h"D *z-m444-H%3l$YF47n/+уi6xbd()8~nn'ߝ(7610aBjjjrrrbB~LL4D8, `,|}}p\9Ha`-XTz4iJ=@y{{ 2Rx 'zI"(mmm \GIj)Cw&%%I% 5}UYĿ...of#Â/ @M>!da,젨m۶ZYY_NzÎ 1X…=X*t==Ֆ؃ZY[?୷bҲsmϞ=>>Lɓ_ׇ҄NNaJO9Z\stO;BZJJ idi\ "޽{]]\\]]n3f̀8~C --- (CطቐȨ&7e$HvvrffP<{,C#CtzӀDz!0c`}!blg>ļtХK=z'!"**Ǐ}tL4sLm-Ba)p{oۺݽ{!͐ꫯƎ4Ur_ZXGGGWQQn=>âEam\st$0䗎ĸgKˆ$34Ik.A!sU~»7h64xyCx-[&$"|TEC j`ƍgjb.M>AΝIa}|[xXX|||jjJZZp?BXKZZs"r?"?sPԲe˖/[rJέ^v-R`xwy7%g*aRc}~wС-ȑ#xxC?w)++ѥo;u]=Р?Ą4ܤIbcb|||`t䈺X# PLt\(`:\@_?99YX$˗###LMMWWUqTӘB4)87362⫕.]tpp9r G|`B:@Hv< t cĉ =HCp ̞=t*..뒭/$4!HV-?3 #0R +GgΜi p=QRRhk q:si=| A{= e/6|x@@; uTZ߾}%e=eBOTPmZgDDDBBˏ< dApPК5kȹa_m}GBj„ h(UY|%ůۿ?bRG1^]T2{nx'~BRr! U 5de-ZfӦM51Id HHTHppPJJʌ3***VWDaⷛ+[f$/// ļ696l؀*(*=J!cf #==}Qj#"@`aR VB2xɎ}wT,E Br! Z~}FaŁ .!7,66\VOODǏt !%~5Md pm[ _h*pVV?>?'suss\Ǎז޿7߼oWJW->|8((H__?))3}Ԑj*zjNTkSڕ+W֭[K k ִ-8E.P8j֭[ TZ{B+KzjpQ F=BN3 ϸ74\ݼy3#enn=w}ᜈ!<uŬSO>A-*_ qv LMMa(1:Bڌ֭[8"8e  zwQr^YBFR`jtbZ*e(0ZkWu\G′1%ZJ55Gԍo8xv$Ą{T$ Uܹs 7ߐ#̜I,?BI-pSsऌjQ-{̙3a\e%1cƄ͙3otFB5twCC,IP .DB)*(ɐݺuջCV.\@j@1.GhBM4h2TG4 , ]9"]jjS!ɖ tHHOëQ!,ԅ\C@f?2e˖-褝ٳgWX1aGubF\~0;-hadA.n\],G$ʥBD'7a*,(07\F{"&MHvA‹NN dbj J޽ s|֓D1**yyy%ي6w6\ hhhԂ΀!tA@ /jժ ==]tĉ,X秩 C(mt @_e ͣ}D6ϯz*=x~b## 8g7wرc9>Z[[hk蘚::: 4 A7oތΐxioo@|.762b]&nH()**++7Op!--M__kݍܲ`߇ *rݿ?9ƍ:)i#}A$y1@ڬ9j$!^r\;A/pƖqgѢEY֗ ŕ JBOWx%KZ04$|H2heK^mdX0sKCbFpw5Gak++Ȓƴ=c'Ndee8!E~sήY&+3ۛ;::ӣ8ܓ'O>QIPrki7n$&$/ZD#/TߊҢ#h$iJ3g嚘hGDDp$OXSCM9P!kӦM.i#v$w++ˤٳg兆XZZnvXY!} ;vܺu +SH_J՘"';B`te%% ssmmѧڤokhm;uݙ8q6,˖-C堥E9k,555, :n/]UUݴ?PÇfw`+Wŋ+>s33mשbm]JыG:Ӷ-|y]ߧ`X9"s5G%@ZeXZ=]l?{M;5;w7o}vɂii`[i]Z:زe74,㗲7Ma?v(f>>>d$Ξmzqmټ A/T}QRYY955ʕ+Oa-k.TGGGaXx55U=]]ooldd 2b(^E~yC=PcƌQRTvT(\fp|266OVRR~S ?/Y8 .lv\&5 w޸~~ O|c Ív% ڹ[8 Ȯ~,F:" F|즤,C ^1^T r!At c/3d$uuu$'O ׯ@DoN:uG:HmDWzU!jiR3fа Z/4 45`GH*hgbQzrZd l܇[tX/88)X*##FEY ^#~0ߋBJJJpi)[n999OwSW rQAE']nGQr KYi7&Y1⫁_ 6 &:USSƦ %8IJc_K,IIIEbb6 d&CgSh}vGקn8x QCYCy8zx͞zګWXXF]~oSn݂t0M?\xПv pgfd0o۶=Dg G DFE۷[n]to{9{{Jo)*9"ZTWG'ƀ-?ҷ/"B}p/dBcggLNT=J*LJ%Ͼ*ͥq qwj@3bvGzv o>{XppiI'fՀN+W'˜Ka!;8***섧/III'O#GDȞ=3wS F)1'y(qᔔZTzt 4_R–H4z9r$b|f- >iD{{;5sI$]&M\)ϛ I\2ZӘ >|rfBw_z;]pjSP }I`af6m4puE`CbrqqNNN=pROj~Qp ={6­$ '$] WZ= 340HKK@/+aTFj0͈#.^H#VhlltqvA׬YưGGw7;>oNLp71cƬ]nWT}Pڸq#lDCtM"1LOOoFdBNhhݻ̒K!^KKK,; ܲiӤ޶/luOɟ!iҥ jJnݞ3>Kڍ6ǧsZ{,X@&GW`'t ! Ӄp)FرcEEEQ{:AyKRĻb;wZXXC}߀@A~>yNN $dW 4~kڿ%;v"uְ0M ЙˮXI`\(~z^xK믹\СC۪+͛7ydfkkK ef|:TTUd0IOOo7ʶ' 2$..j'EPϥKLP UYQ˃yS?uVbepQ0a=ifj* k}+VW7WW>;Q`7F>:ܰpB<ZߪܹsgҥnnCŋJ@'):`^s]]]0ԦpkZ!d6/TQQѽ{w{ ̝; ]@~cjjJUkj[$̎sݷo/=Fn2Q>}H[(\[K}6a7`&#Ƙºєi Zw84԰ qM%4$[Y_ `ZŦM /[Я} IQ<<<$bwT3g~'-.P1r,0@ihhHoC0I&͚5iә 5Tb ]77wرc$ʊH#3޹3 |Z 4a4ް)a]Q&&&GanȿX߷o_II SameE!-bZBQ`C 9&m̞HbZ8"60OXN%dn0!-[222tuuH߯MnݺObCvG!n̸1pƍSN0'N>,v)x{ڳBa/:::_GDH fBmjiii``ז= fe [Lj޽=zP>5 @ⰅsIS/DSٰaCtt믿ޢѠݴ~s7o 2x0=CUUU>]MrVѳ|JzԜd/O*˗,YR@t yl3{o>˕|:~@feeE =i{w|j1p1[ZZ{t=y$@SS3 yӦN j3iˇsN&+1Q((00eh|Cm'|1'H ĄlAY80`Z/^]H9r)UEʋw|!E p-ŒL@/RjJ3"u^^nxxŋ1#/~L2Dvd =FDKS i;;;{;F'N(&elcc=;G!dxȑ){12nn1#Fp}N90( hkz[;(.uF4Ë735%߸q۷'NѤI Ni d}m>lwމ{et(g[J@Bi ?f̘z_֭ /$$$~6Em߾z:;9 ?$%q**qqT>55lgvҥG{K.~=vR* LTZ sdӊaÆ1=d"sj!90lmq#{F؎Ep'{Zd!"666"޽kQQQ&&']^m}EeMF,#/_L] "uٲe_~a$;<ˋ?a@ԲLSX"|=%1(zڵX8fQm60VF {Yd Q@O:W&fj%#E/`ɬmllSRRƏ͸H_ iHy2''|1Hz[0lY]䢇'N?Խ{CQ%33 ߶m=xDg|K_OWK>3P{ wZ'<em^+w9>fK+ڼс>5YYXXxRz5utN>݁R͛ŅqdM:$6&<ρ0H͙ UUU 1@jNsLMMiӦ}wAԟN`QD{ Eh ""t"G!+K=;vXI}1fVs1eEm'b--))i}}ac}Y?hqn?֗馟:v XòeKi|ջfΜ>XPΟ?W`Ǹq㌍ L$ s 6۶ѷjj#9 0LfJN]1:͸8ٹ bogǶ&za:_qb@^VVXphrݺu%Szxx.r[nvP}dQ~L~:EFt9{,S@NMkjjJ$o/21 :YD!`ƍM'gѢEg 憋y,]l#ti:m…¾5Qi'e|X]ܣR07%8WuBVz?+3Ev VVVjE$?~ ݷw/ޠOƪ;!ˋ~.)J+khVVO\\܆ :y[h+>ujssšI&uuvQWS{׻v /{}7 H$y.11#&Lpm۶AB0} 1Μ>=}t:Oȑ?tXa̰a/^L̛7oȑ;4|Y{ҥD"=Ց%<|=88H57e Y|pS"U|;v,l1!ljcD1АXmm-=tPjk׮'׳ĄΟˤg{ Ə G%3c|+_y$2(A"s< gϚellLx1N]PP/VЀXXX888%cv**؜ ?/߸}ȱtuu B*ajjRZR®Y).,,+UUG\s׮]EPE$QȈAWX!X`G\r~;HܔlZ\Tt^/tU_zҥK;we˦I,pPؼܔѣgC⠡GA]=>/޹s_ըɓCrJ_~?P$m"8I!F8q e騰z>[=7o.\F<*((p޽{( %uEUM h B0W&:( WH?=zȷ=?={[l$uu?2uk++Ǐ[4 ISjzS-D8P W<|eզ?σ[z~qQQ3g֬Ɔ{ؿӦMyIԴ4LZZZjzzobbҳW/yg\eoIurt$IOO']Ʀ ;$|F5 兂*o,v -[FeT@~mƆAV1Q\pBa!;!!yӦrJzEɀᕕFFɣFwc#Ȭu/z[2WUUBx,,ѥ[:#*jlKM}dZr>8p ,,bV+,,451qrrO: '%Cv >,;[q 31@hDxzDL5H,_ڎkDPXtRȈLŋ>}Ř|wڴi 9:AQ(XK/7V36_nmgNt02A?vqvڼys=s4+Bd(**bWٳcǎzGGGXb1Qiii 8sLRbp_aH5X&nMMMbC :eRUd9cСCa/u=PCRQw%2#bggG2{׮ _Nw8ߖ-[߁zx޽{IR1>LX?O"x9:8 UOONNN욜2++k22x(dl"^I| $֤V aX2U"(cNNǏ92".&fܹ kjJKJSS## Wg'7>ÂcF 2:+3hy9IIIt=.Ham-uuuCS ֩?#뭷/MLLFyQ '&߽{5k̞=iD 4447117Q![F>~ei|8f555A `&d:P&= ĿiI߸6|8 u|b@EEӳWڹK )S‹)yvQic9_qx-NRH222(="RP/NNNfYeܟ E9d&RQ28cKJqbMȀ@L3T?`gg`hh_|\nʸ),~LŠρ*(GVf] %(׫WVV۷$"U<=ɤGMM5^ Ոq H"BCFDGz:;8o_l̈IaQQ XfoogllLo VWXP@^~ +6SW[:d% \]]LLSl؜AW i4̀;³iȠAtuuA(hٳi&K=0`c˗/մ7W/]ľԄ |cQ.u$A+2v!襂|Z^nnZF#eʔ)L.O_}<|D's> "+/ty z>}]3c3BxgJDVɓϟaޤt1aRlRƐ.];%"|v8k?Qqe}=d[BȒ1?[B@Yz5 ÍcARRoL2d\2x&..ҹ\[[(9EYT YXLMaǒ>Be@ F}99&&>t}M1뚛C~~r C.ɢEZ'ٳ:,zãC R8`Ԟ~Mf"˿+b7!T\T42) &> d OK U6 > IIɴȏIjȭ(4pATTT/bnW^׿?4s!+ge-]`CSQ _K5U 2/(,|[Rn_~vihXSbBc&~SUUEH8qޣck Ô9;9KNrrDb񕼠N!:A Xܹs'N(w`:h3+TAXG@<= dP>maJ(-((9cƌZ[:a:1CK[ tug̘zSٳ 1DmSB5@AA& Pg=`QT>S gccmaa1~3jP hmm`14,q >|x!.ηkOz!F s̙4iҨ#doPc 0Φj̈;w)~~,,-_{uXO߾_5s2eʙӧ;UL _a.&^r(*;/:B} iki1+/4 03`s1'EtBE6.._ Cx˖.(f%-RښtMA]}>} XV`sEӤVds$2&l<0@ڭCĵL5& +OS5k֐`[B$%PD6yzz7@ƠA `NxҸqpē'O:ٖ4p{n:+~XΥKdeeII4")s9ō0]W3gĞ;S*[l><6MǼ1޻{fqb[n9uEv!y*!%-))`/R|%eB# Bݮ]F"pwH$"+aN Xܘ3Gz;Qgb$R!C <1 PX8glljj{ʦسW/V={vg_~aA-Z|2^@6zˮ0r1Ùldtrpp_|@LEGZc?۷@$Ǩ1Y߾}ThժU+@(..аaE&1'[dddQ>0o[f111jVD%2&' H*,^Ustp94'N8A9XY__Í;T]] 2DKS!ݼy3"XV^((SSS[l3+ˉd SW |srr0@A*IBi366vm2'~J*,~Bh(.*j|ȑ𖰐1%CCstYfv:P)΂@<>uuuƅJ$+}g ?dc۾:VjX_a 2|͙Trnnn4,WQU𣏰W_{_~-cw ϟOWVV¾ӦM:eĉ2328XF'''$$Čǔswww]ⴳch,@I8 MMJOj bnURϞ=]ʦM re 'nMc6W=),--1W *OMMMSF|¨`8 l&c&T0<\.}@cɁcmmc|/d8B"ĄCCg''>:*j٠޼saw}W^!%| f$$Cΰi& DXAf?Q9$2S(< "\z5ssr=6r'sٳ O:%Aw!+jnn&N|BиXK0 z>{g7Gوtq\A8_m\,c| +II{|}=aLML;v0h/uLA%i SXK```W[>.Ec॥<Ɂdy \xXcZt2pѫ I`Bpτ @R0&d0{)}Ŋ k׮9жoGfIhڵk'Nd4Gz36[UM@-QNv Lo//2,X,rX>|a) wÉrVd7!JOȉ $LN,--!("=O;x`Zb1Dp1x{dl%BfF`.((w#5:ұT"1 kK{=f8*DuK9FG 4 %2bׯ!">W_{o& y̙l8::`[lafrۉ#~dPJH'33ٳcphkÎ|j"Br|8+WC;[[& qW`ZIgUUUǧa/<Ƣ-~gup[mTz;+yՕK/[H.[~GfMcC Qk!j՞Z?9x󙖁 ADD,"<өbJaAMp09rY`ƌyypFhh}KA4035t}||"##9|L~mpΝ`J5D!^ 譓D=&˱׿;#a7EEE8y* 9;9!%&KJ#]j1E v"ߚ{DsD]]]ez@Oڳg:;;u?DC`LLDW%aCDM]o'۶mΦL33S___ӧOyrtBj.%L3:LWT&D R8a3^^^ 7`?hOԍTNȞ)\لhD7jiiߟVg} ΀6{^QMLLJ+EDK!'FyoJj/ũSMMM<ءphyY*ƚJ7N3K (Yp!s7֞;wkׄ)ĿZHá 1^(H͛7q9)YH3eo 40;::Y\\ & 7f0芻 {lOY__O&&&P5Կ8+HdSHUMg]v}V;^|>srrtrr« /#pw'dHw77"ȭssr ⬏6^ ӈ]:u*..gϞ 3C#Yp!uuuaeea~)@$%|377717/Aٲ111t"vC$-JhOpppp?֭Z1r!Umq'hjb@؁!P'~@&Mr3^t 3 RRR{L/{}0W49rANGaT+P%ZD%%%..JիLOH@xP{-{nnnf2/t]'gE "Aj .bd$E;m4Ғ:Y]GGkQla777vGآnDcǎQs=٣MH-~7\-AOSoojSoz)8qb뼟V^G} ?A+WT~2;. sLu9L$gggc##ooojK ܶm˩|üPr ؿ?,4񺯓"{_̲9`pCs^{55/vN*++.YR^^α C:zVVq999˗/۾}shCɎ:_7g؀l1) 2$77Zl"hpp]~|oll𯨨`.NN==<^( G}`` JNv6=D\ƍoNd+!'9 Q@o> " RaÆYYY(99uuurM $$wtV^cc#әkגؑ<qm ⡢tז9y`'Ik4s2tرvv.5k҇lK f+G/?CxU-褽?iMK f#xUƎle}}͂90 077~6O{ͥ/weI]q$sʈJd8n }{zrss93%~L}WWWj5lѢ{bƓ 0DUe%422&_ھ;wlll3q(=ݻ+#:ʚ}{'0(XpaDDV= Қ~W\Ax _b=aNhNoTyzz!TC03P#,,>y|G`$6pق)w%8,""{>iwljKR̙3y ***LK mۊccĬb@s8;;:99~3`Ӗ-[i8=ׯ2e 3A:Quuu.C 7l@_@y2EK,a7|^xרs%3Ɨjsė<R L&!˜na̞ S ___{#k5449%Z /]?L"sıOdQĝN#@}jkk,-222}ҥ  ½Dʔ Hokk 63)֮%:t趭[\E?HdSSSa 4ch|",@) ~,vAMЧb|zdXJ Ԅ ׭[}'LX똙{ @Mtt3g0vq0Q???oپ03f@B-L@iI0`*RsvYtcJi"han_#GJa5jˀmttԞ={@ ꦥ ^'˩<#_r ׬YC&xyILJ$N?3d777?~ gwyMMM_Zfbn=R>ĉmB^zIŋwfˆz*%2_KDRr`zVVGH_8ȉD$P ~O!~GbA^yR_?^O,H'A7667n=o$FKKKLرcڵkt,1ȞIxzx;ȷ@ر[fڵ#fYWWK{I :3 {A=n 1sa (/'KP+++rgDǏc8%؂r؜:޽ kwuqͥ[:ԩSIq/S:0 V$CuО 녒[4:O2 豘#ϟi&-0"@:kkd%)r؂2gϞݱ}+++L` c(E3**C 6k׮-_#!!@ (p@S?˅ m}Zԟ?$%9B-pDo/%}b"݂AAD(555//71^Kc>/o?> 2??3b3qM22NڵkieyΝݻwjjJ@@@Isƍ32TUUu2}ȃISRRWR]v=1:fQ֭'P2sZza̙= 5={}gMZm/Đhdh/,(: U{_xv-9KT"hs3/uF Qj{ahϷ}[$DG57ъJ&&&@/vT\ߣ@$9*M+ցW^ꫯ@a=nb9ѣD"8)4IRqk.^jȑ$S;;s )-fΜIɗ%M9HC;{壢Ӥʾ,ȱμU!ǘeOԈ˖.e8Y?55U;n/}:~[lVb.)=lj;ػ'ƈ466BNC3W:<{g\*** h;qlmydX1cLJ>]P] ˖11pq6W1btE-XB!_v`n}۶m>]=3#FhX6!066'7oOt688ȑ#"p%2_Eا~*.JRw2;i.^XPP@(&FҥKk֬&b ~ԩ0 f~ ղb2l~bŊVRuʕm>AA/8А5՝ dg= kJhǏ0g]]]70-8A]\TV1c֭[w`F?/xW{=n?.۞={puuussB@KS^_TWיW\qʬc'L@(#Gƍ555[`UYUպ j*'';:*b… y]]] &<'Hd#K.4h<([ǦHn/XXXHo$<IlApijhuJ"+`!u38Bȿxbt==zؽo٧o߯:--:Οg)E&=$= W sc9rD Q"Lxoo7:xW;I%Kd"XV6i`|D]]<|3Ow&@?B/^Lm۶6wM-Z:22ÝWN6m]{WPNUU\RB"13fs; _x1^EQfffXccc#tqD>}4 :4Iؙg5rl|Ɗ!^^Vu sί2}w ¯7!WUUUo//O3 FA˺uhNI(]3֦H|UUT8d`D}BL0ACCAx|šzjgg++ٳgG*\yŗ_7.}8hmm &Z>޻vbsOP7n`PCAQkN`~annv^م㙙mf"0Q‰'jjjnl#3>|Ճ?~<""BGGgm^)WFFFOٳgGAN/+k}?ɓf?/mK4ZdF qe<8i$wwwcccI`BXk'11VVVgݭX汯 cQ--CL2eĉs̑=+655n g],WHJ2a{x!sɓ7_O50~e{>b(AE5ӱԟo^^޹s: ф۷{yz2HCo֙y=z4<\56N0$d& -6,,LG[zaD]tLv2Ϩ ÊE+9B=|TttAyzzVTTԈ$4U}>pHh&[jUVf&1‚ "۷ok@zq}cm>&5'eѷحނh8IFotDc)ban>}wW?=t4<, ħ#G YdIgu0777B 0?g~  6l˖--AQ^^NBCᏎhp ]xlD%2_H"фL ۼy3i737koڴI>%̆H'LDpׯwX4¶/_N̕qWĵd?}ݹsHKK` )rqFW'G 6Y'.6 8077/,,bw }g椕%&DƖ {wV,^wpGYD]n]vvʐ!ftP ??i}֪?Bo3*;Qqʕ᫬_<" @1EC;w Јgo߾< #NmKJJ.\%J!q.Y\CHb;t .=9ħGS޽{Ç>"Is߭4*+3/!/˖-}AMCcSFKe"ee }2i$ښAx=G[v?̈́ wx ((0AvO~@g2Cս{wDH79y`040Lmmm}}3SSR>'vMMM;oիWgqnK. ̽m444dff33GZ|5i-u'*^]]m!{Ξ={K;^T4rL-LBW @4dZʗ>6Q1HTSdiӦ(Q/$^2̦Ma!w>qܦN&nHLHPq[[[>/X9yWq>Y7nxj#yyy8(ԁ?&Syy9cעDqJkiit"ŋ9M$[A #VRff~OB;FDD̘>3/?v,::U9S@ Kn޼I$~[̙Q8&͑͌ӱyyvcǎ-..O{oXXZS%z}/I1ϟ?s^ 8>K/ͫ)dH(#cX8|psg' GFF2LII $N5:l4BcR/ ŋ[]`RSSe/n(Dzﭽ=PKh{z]ӓÖ3)ݥˈy6r3H~ 222sËAaNskQWԄ̦{aw``hgںQqs D:Hc##c>L(,ljj"|XDiӦa? - ^J=!EO/iZMѴ77-w_eʀ97foogܹḶ66 o?>(^z5lgg6ii Q!Q‘II%>Ĕ7~!fP,-,477+qy05ᱵ'> =wXAiT utp@R?B1|pRbBܹsGwDu(9*N(˖&w&Đ>ҁ5u GEFZYZ%.[=hpIZ ĢE &`z] 7,ffem=s@8[|ws0{dI$ ҥKk  ᬸ1\{H'MdeiɡN621D 9 yƍN:D gkkk(Gq™a$ 裙3gٳge!_}UppҥKI"̼4'KB&j+bΝd4rĉrTd$xAnjjahhh˞O`ordXGGb=ZT4ё0xuuo~]v*6J!_-Y!EN,,̙'e#1e84//>`DA~!Crs\FUϟOKS̙3fx{M\:k,*҇틖VWW O)--'eʎ}Q%uknnXcc㠠իVY%2ǟ/CBB,--TWs`˿PSUU=;g}®_az%511!Mpm )"&v8{l:%7otuqaG I \X]TTooai줮^z~HjOOe˖J pqǎk׮/Աc0___'''`vrwH|}655mhheU>W!%w 6Wlmm332lٲq\ooo MM i}ʕ+cƌu55մT+xX#G&aTw2Ʌ !锔\"1N:y`PjjH 3:~;#Ÿ:t&j QÑ#Gꤤ$P>u9 122:Li(huٲB6¿ CqqtRp0MM@&9K߉i狔t ~ذaԭᣮ ;LwP>sVVVnY__…ɲ $&&)Bl}~/BÙ3gBCBӵkW ^SSQ| Ny`NN~~~#OKK/*Fc=[qꂎ}[|b/삺=Z[[;7'G8-btҀJfRL+/^\x1i=OS۬uQ=jtӧ+++JHY>X/_n~*++kqY`333h׮]7nzn_f=_i!$eCZZQL WMM8[nR a bCC]m۶S{Jd?Y_w77s%I!u޽aPoD?]4[ԯX1n8s uuqtTLldU(yԨI3RYYI>{nG<}׸Q7Xx Izea‚f”XcǓ|ٿVVmذcJ[֬YckkNm߶PÕ+W1SA,zcCiii\A}.`Ǿ}>̓K}Dv}*:;wp B-iBBjuOG}QGI'?r 3!8<<g& w"sj3>|8$$춰^"۳G.#^x9h(X%Ek18C3|K<w(5UA8;764wBuklltvvhh)c޼yt1cjKKˠ CFE' /a0a¹sZ;cG-JLLd_ZZZ8YAAMWR:eB]TwP uu///>R '44\pa8@0gG!fNUUnݺu{~ +{MIذO˗/p;0[څ +g /r`P2,555ɠ 7o9 xڵkyĉ#[7;g!}naaGR̙3ybN a={lmm- ًGNNη~ۙKl-fa||ÄfFw۹xS8wƍ˖.}'N>Iѿ/_FPe f?0Et* [o}g)'0TW_}ƛozk{Q?<*2._E듗  1l2W{{;--aÆ͘>KHH8~Xꤧ+Ȗ-[fcm'%* `:9;v {0^ٻ%µk*a&8IDݗDı-P`hӦM'xNPz*q~VTR2,g/ ?UE>l=6ES7® 0f̘|fsСm";$$PعsgZZ4޽_~;V2ujǏdR-wRT$[7&m`X{ryTI+VQ~E:ޓ&Mp߇T+-bz̄U8]AMأ;c}@ZAmVTUUpuu]re{9r`A2mÅ&v||ԩSl--,Dzx񧉃822B똚ٳ0h/-[LbnfF%Ybĉ_xhC~aC_~yُyjEp֭ǀ߄lٲE,x6, )(￯_3gNScÔ_ SAv tqqYv0!C6hAG`?X /̙3;::`{֢͛|ue}=pegg @Ӵz(dd… A///xqW0eX[[s-ʢlmmV\hkr@;i55U iean0ODst hر/^d f3w\[[[ӧF5.\S~39h( |?3ƍÓɣF]Pz(az*-!q?!6m$PիW,?Q3ԙUӁw۷ח5/[X a->',dG}N? P=Ο?_x*ѣG"#"bɓ'@ʢXlRR=|KH+,,Lz9NIUUU{ޗcƨ3TuΘ1{)G$L723/Ç#: ɓҳw3yڵf(Q)9qaME\PP?OG ,p_$žpÇ[XXԴyc1͛6bX[Y>5466b~Č1d:Қ4iȱchÁ.ddd b۷o/Ղ LKvnc>*HttJKKw%8P;΀V33m/_=z45LNN>|ҥKu람/obb\P~'1[zx1{7o 01&&&==>tYgoglg[$2ǟ F& {'9Y`@ b3gsc]v+W0;v0AA&&&pB^L$Ǥh~4cj38:_>.6 0am&...Üٳۻ07oQTTBo&cXsŊxkIiɓHh̘1&&T=ٳs`:uw}G[ܩKCnݼ LX,Bo>C}SS> , Ⱥ8;/_:ɪ^0O曷zՋff|zUԓHRSS$Mw2˧N>TZMM (Q#Y2ۼ̈$]*lKf@),q(lSRRpp0xuVooopܱ7 + ЮA};3`%Ϝ93<,ڢ@hI}G8a)i}gҺٳg?9х322& dW&dl޽3ث_@BK,yӊ%Hd'-B3f͚3Os8vH(x"=cҏ0*®n;[YYyOUgt>O"*_߾L+kJFĸ'CÇ_ "/(07%%EbhXZRBƍ4S"1(/Wf2wbNXXdnn&EiӦ޽ϺrOUT䙃m߾=00PEE\с4444 kjjk?'$$K.P'|P>[[DI?}t鄄x)S3=o/bᬂ?9rdxt4c` q$mɓ111k6pR!Ź7n^:""B[K ~jj Gl|r'GG h$X VHLL8+{ pҤICFn߾/*N (Ē?ӧݻXG!E~(7|^ϻtoywF?ϋ_/n (Q\"sf^aaaaBVVVO8sLbb"ըlRQQaO歌M0܎DF}^$oYFSS9ׯ 'ܹs JzϿd/ݻwΟ?/QOF"s9hii 5QV.],--=A؃+`uttV\)^$2k֬'a]|Nmq?Hzϋt)ze~gA~xwuIȑ# ,/utu==#pvaaaL^F*33sϞ=L Nw}&M\p/Q=>5\xuLLL3mwYt)3^304d17a8j(OĜz7ϻ<~e˞@A9}ڵk.\߿Cԓ] a8`@^ȴ:jjӦMb(A.\HOOǃI]0::c+#  iPݲe ?dȐ#Fhf͚57>&Q˗/gfd>СCY,vlR*gW+ &KNNH$fR.G^eUF'!!^͚DxJKK"3<D=1 _ߡdvd?~{{YZZOw¯%_Ϟ=i&nΝ{2D+Kd+p ^C|q1yºqɓ d^Ċ.Zc֭//_xg}K.ݺu{Oxʕ6֭[PrsumhhP?1n\LLymmmU5gΜ &GSxbFFFv/fWGG/D㱋4h br5I&9sFh[bW_}%TCY^<䆙~1;v젇k׮)h WDOv5??O$QtDL'Xp?EӴi,`SpYcƌd'O@ܹr?Qofmm;999 ?jeѣG}"VϘ1V񁮄\p!))I]]}СzzzʔOo˗/Z&OrRMEEE^ %K[mzLצh[6l;p@++\ j W\RI>]'-JS)9D#$dc+++7:u\Y"=\D  ㏻Š/j9MMMK,)//ɉupp022140G"#"ƍWZZᄏtuT?(ĉqqqZZ)))ϟg C{#mɓ'+gB$IJP'yn߽{7=z嗿__ߢT ƍ K'={[IrWb}VzD$2TDsիWϝ;8eaÆ@?z0 O0Az"drP{(f:#JbxEnooiki)Dǧ{V)髯Znv-y A555544h DU[[{a:ꞢdАYA }z'$&dff:99}b{.<<)!/KD3g۪U0DVV/,TwaTϟ<ٙ5-)_Cn)H:o455)@Qa!i6lMʲ2GG{{{驪蘛{xGDHN%KnܸA9F d̙3#FRS w9;{^|A.%(۶mS@ӧOU 226~w_{u$>!!::Z"0$MPW"KKK[qm߷oGԭk>}98;;;늋S|Q>#h(QA!)&ePeZx}}}mmm/^X25o488('vqqH}C_|>x`F]]-4$kb@ ‰'OR9sfO0@^ԀΎ|5q9g2:uͩmg(رc11x/'`x6Y:s3fT&zg*=a cFE,-SRRϊ ###omU+p@ÃfN{P풗b:ܹsNQs=oذa޽2ƽݞ70`ccr]t)##&->t˜תJݣ“: J8s̊+444$'O?~^^(c}K1]VXXOQ 9_~%KQ,';ƦhϞ=K. 1222vssc@@4ݴ45O*&4ikka}@?":t QS 2nܸ:P޼3b}xjI|W-~s 4h͚5+wٵkEe{D矛G XYY۷OPKKK54 G#ccc-۸q#Hmh55UAXhhYYٶ[!`A\4uU6&,,{Wuڵ[>DL9)7d z(Q !~ҤIzw}|joRWWÓ\]]`*22O$LYGSSoxp3cyyҷZZZy|&ş0eC** ˖-#/,,ĶpYCãut?裗~g{ ▗b_֯57ҥK/Zx?#BWC @[ (OٳoÇk֬)((fOwb;;[[!jkkie2V^zuEyyrr2ࠠ'Z ΠB|pNZZX0+}[D%2D8.scc111PEƸqm}X3gV[`AT quu)..^vŋ\>`p Ȓ˟,v,)-)1ܹs,!9Hֱ" Bxx~c >nID&9r,]BlA{U>!Z׮]h=nxZg^C6?sիWy֭KJJ266ӕbPȈ@z2 ; ={gn>}244AT͍i.I}jjj/<5=pL&69>% !i^{afjll,,,BmFfL?w;[[--i6ld8 22`]|r{>džK,Y_B!VVV[lI1$}=PDU~ڶ~UTT|!ed8u`` (˿P>䓏>XH1pSb `oxu$$$G0aznݺXYZ码 ܰn쑯(Ãp~]\\6ldmEHdQ{›{ ҿ>L[G[OOĘ|ժU'N/LLd-,᧟~\1TrnnZ4={ O8kbb~oܸd`!!!T xY)+))W^Իwoh)+3sҥ'Y&3œO?ȿhGO?>P ljjkQ`\̙II./턀! OOOMM s}}=%\V{4Ր*gkѣGOf`7<}ffejNPŋcƌ)--UNѱ؝ʼ~:>|# UU 322rrrLML$bvQHPH#tCC @ϝ;?+%J&9D -v|Xgu|R[na!-™BpС---q,ruVՅL]aNJHHҚ4i;e lٲϺȂ0тk׮ѳ'uÛ,YB}|| (gϞC0eɭ@]||xĮG~ (lЍa팍;w8pgΜ)~S1h24 z 0T ej߾}tp%ƍ􀐌$?|zbG%%zz99G]m5sl]#QQC[/,w0%; ϟ_={6TN>* rEL2ExmǢ̽{;9:^Z w͛7 YȎH-#""`?l޼aR~g?ʪiF8m:,*2ؘ֍9r֬Y͛7\(˟1c:99up?|l/7 D"ч d3577ÿ~ٝ^NI< RQ^>g37֭[^x>]T,Kmƍ-Ȁ)`%** Pgg礤$qs͂ff#Fؾ}{ƸqU9QC[xW"M6)Nf RUdT PС/v횲SGƿɏbc$11;~xIc縝VhhGdyz*255 +rVԟ?cՑaҦ&&$icCåKCnnY( c~|lG%$&TVVTVTL>(,,&Ǝ`EFF‚C<\\=MMM%3Cw޽Ig}200!`̌ ;;[ /*(J!EKO040,Kďкu9::zxyxx-_1Dc%2@\rӧO+hCC3Ԅ%\pa̙ibb74m^ށ2`33žH_f믿BBx$䱃GǏ_IDAT$zIC-//oA +(T?DtU{ѣkeoPuցhΎ;C ~L駅 +}FyX+-[|J_1w\Z{,-)9s eH),]Ãjȿ۱)44}Td@L\vXpݻO]jEtվpBCChff&14HII1byw%7777nܬٳbccx+KK|ǎc, r`/q>W_g4K(*]h$??rPRޢZZɡ2d$~ //<߭[=8|uuukÇ>}k֬8q"H ?nݺw#PUSe+D A;k֬27~վ}*++}||pkk뜜aÂ̲zOq||2e '_zW$,S ۰0sSJ-Cmm5016nMmqƞ={fϞ0ҩ& g˖饵kOrrZz'ϖ(N8]|1HfLBQέ[?ѣGY3ʿğ dgxyyZ{H.Ͼ{+ˆJJL aݽiii=qDDD@p3 rʡC~={|Ӈ;$_o}>~E.;s }kZ{ b,{'ZhhM߽tfhhe*|P ;wiB߾}[;o}vF:uÍ$ AǏ666E||<@5FOOOXD v/^\v ,,,$ ,XM'F;8:BT)[l)//zoXK1==<$kkH$|%FQ|(QZ޽ PC QE^H1˴6QaXTv wEXP{V ;uAAqxx|>ϵT`XpK;' ['Oׯ믧AHkҤI|yL (33? ToIzzzfffÆ +.*.\";sLq1~~&&&&jNeeeky;#Gܿv=K.Y0k֬iӦM81### 0O߾mk=<ъIdQO~w{OwRwݝmw_B[TH;B;YŢ:w{{E}}v B0v_J DPB &llnn&<2>gwD%2V7n܈ٳ +?ぅ-Y:::ѣԥKRRR T[CWW7<, '{A wS0ѣT4Bsߖ.[6dyK?/\bk֬oΟrքت]v<0a/_m[H`[L%Ɉ#>zgZ?]SK·T7ߤ†w#Gzګ(QX!ꫂ(KMMo}ṮzjGGaÆݻW"-f;;[UV_  "773 ijjzT؁7F L{n*,BIpCeeă+:uJs"ݯT~W-oDEEamZ̖/_.2d'Onk4k֬}{޺uV'V޲eP}==g//dϱL(G;ѣ 3G۳Ǐ?%JS&9D="_6mZ~?Ȅ :ȑ#[ Hv)ߚb`zzzcǎU\Mh_e MHHHxA]F`odɒ8FhNh*\544 7W?2z)SΝ;}vꙗgdl{M6j?seep _P[[+~뭷RuZ 7y{yedd(V(Q$2V+rB_VVل=Exh{n544`...U aƍ`0KknǺ-t8CCêTɱH$#GĹ̙9o={t|;%%%Z׮]/mK4~¡&NxI@BoDMS# lR]=?//=~x55ɓ'?y)Qn!)6и?Op!)_DBimmt歵0w77{+VwQg-!!TZ_X˲߮]K[^t6lآE`6{V?/_^ZZ:rHoB"1dsM mmDv}!@ʦM߿;W_mޗ &$_zWR~~zZZի6={6;;z7o<|pXhq]]]/J($2_D\\Dx{yGEEۻoRRy͂xʕqڔ/ʗ%><<$$$~j޽+44TG[ Z< -4bca/Yd;|rQQ}Ey ;;;m(QCӯ~رxv@@&w2C3kZ}]Ņ,ShVUE%""BPK넾=E-=?zjBK&MtiMh5³]v-^866@_&00^XnNofGjjjʧa}};v@8!<=SLGfΜ),g@ΝKĜ=sFV(Qڕ~ݺy3''meiI\VVFU.Ο>(F*33}Ο?qf dlܸn0ϝw -Zƅ@TϟonfqRR\paڴit$4$,ݹsرc3fHMM󳰰1H`ɓ'/]ducE744=sװY XZZl߾P=LKeAtssr7[[[ Լڰ?ք yJ(Q"szOO8 111 =urt(/W~`Vd~|i;dg㝭,Yg) ܼy3v8mٗ/_ @-x"muBe:~!==6m\ wp_n]˹_QcFI8VWWvm9yå4~ӧO+3Y`uqRQD/9D=f͚[XŒdgcxG`$È;vJG Ą6_Zׯ_766JJJƁ |=44TKK+<<π ^; /3۹sD2aVaøƍ .]|'昱/%%e̙W>rȝ;w3%"RSi/%S"G߯~%KghԘK_E{(11QUE} Vo, /..60{sDC$2_R& LG,3551.2"% h+,]z׼{yyX[/ZTיS€ #&$$dҥ .~iwPQ\ [%&5o4&Ktإ"Bl(bAT " h];1f Mf5dz;;s;;b0U0? !.Wnڴť|\v=z߿ĉs*T6Nuh*ѣɓ'wvLD?"""CAFqC_DܬwpHt?vZQPe˖A ?Ƶk /0+H!/LH@a QWS -?}-X֭VVǏKuQAAӣ޽Nvd/'+lRMMmYJpۦWXr=5 1OLL~РAZJFm1ǘ[KJJÛ6ccc}'N ֧W5ڻޅW͜93###`ݻC@' xifرC%#A>~7n477GT=s#  `P\\f{;;IȪKRO5oU P_(-- 2!u`8mii_K@g@fee-RSS^d F-"-MMHdhha-Ң(uګW/m|}}ǍkIIIu siBqo!n '%% ; pرjv[KNNRWWkպuz6k־}{$BЙK0-H7>dȐ!mԖ;t<̴ p899SBHe9rB8C #"1E&} ݖ~_(P(`eU:eOc}~SSSCQQ6lذGϞFFFvvhoHHHTTkdddד'Or]vUVM0뫪tҥ~Ҳ>vSNa-888-- f>}=B[jDիWvch`jgggRRӾ}f_6U6 ޱcy1DdڵN===ӄ Ǐ/,,D3eoii]0j(ٳgǏsСC͛N{|_aP:_E'%%͚5 }ٽG5jHCOЖaÆ)& ͛:t[Zj V=zס Jg|[OC v>=<V@~ 19iҤ2+*,\b-< !!\E<7`ܹС@X+JIi˖-=*..FUL pD;BM$%%s)LR_OsX[Y455C%Ə@Ѱ}1eFF}AH Z|ӧK'XP(335kJs^H$fw9jԨҫKw}Ν;jݺQ 6lܤI=lll>hڋ^Ϳ{722v񁽡3n#!D:$R"ɖ]7"9#,Ϙ?>BRK %y/2PDp 99,,___555z߾}:tHac-[6C, ~'O榥QcPШ/^? QYNp5~ Cͳ344D^P˗/oܰfggo_©QX O<8qb:u<3| |enߎB6@t1VK[ںgϞNNNpMWWW4g̙7⌌ [i8^G}mgBd9'AQQQTTVEQ#FˮJ K_-ZIeW=,ZJ,~%Pùsh9#ٟlU^JCQK $ >OO7168aDCCt쬬۷o ݻP 3퓫VŢinܸՄQ2dgϞ={;|ҤtH zLɁbqZdځ_~ͮ]sGV^ PRR ^fTWWG<9) VQBnnII ~9"… 䢯+cM CBB`*ҥpawwwHw-DǡJ9_o5_~EUU椩ah`0`l?|qq1V`_Ar 1^&;^>}Z I LX>5oy2scc1ҲWF޽1/**¾f5ɓ'{gٳ'Rјl,TF={~ dgeNmjqС$''X[Cbп^߲e˴iSD::z(aԩ;vs[Z0:vBo߾_Uw^[;wdll7t 1BWW5D7ApW^ş*1}:,ƍ~zTSUE?R!pBd(Ķt| 4SN^z+W ڈIĒ*F+z lݺ|ҿ 7>>֫_ԩS狺ui >C"ddhijAswA ygg|p-Zc+,,\8hΜ9o߮Ln pŸ,--эk׮E \'GF4,A<ldh{Y8ϟMU {+V홙h;MKK073hĥO>*h| H0*t&NoIJQͿ?gu!::Z>}сA;޽{JJJX獵BuKc4%%ހ*!#Gyyy'%77W8p)0pX,^t)?uTuu|0..N$UVFD"siA:$@*dddX[Yyyy;vL 7?-Zݟ|g?s?P79rD$Qt_7nHLLtppׯV3vlJ;v^ʃZZ[lAM*6ChpYzOB..TڵkSLAQIaSPPhܸr9rXCv :T?ہ>ênkkh"PcNJD:TL "m.8:8COOR'yN?BHe9'1''Mn~Ν8D?PLJ 8q %ؿ#oeee۹s{>1Ơ׼ݾ}i_UV;wFu޽}5[U_RRR"1cnݺ%]vR s$D4pȐ!7K.Mecczj^nxaBc;{l4GO=2l۶ 5D8ܫV@U۸q#.|$x 5%ɀ1uPSU>}X, @BH9'2c޽H;v n޼;g/kڴi&M[|ݼyseىݺu+<|6ґԩi!ˀ[r% )nӺ?*iQQU]x1Ӳe~Ǐ/@͛23YYaÇ{xxղW 3,dҤIPB:Y?~F EE1VnI:AbhLhѲeӦMH?~V?&aYu `0!ce'S^^^W&$$ܭ[Z*G6m6lωձw+/L!,<))IGGR'G^ ʄs &wmEXaffɖ-@Ϝ9ׯ ;;{@Bî=zEFծ]N:-[D-el Al<ãSN_ݢq 4nZmld.1*ݓ8ZSt*++yvZt;:_[KkĈP 4vժU"5ooo--#GÐ G :6`ن;w.bƍ.D,K׮-Z_~v0>t Ø/VBUUՓ'OG8꯽3[%&&h#MV97WAw(0? „s:+'O@½vYv…ff t7@!U@ 3ӧO߿Fh.qO!޽{u o޼944Դ2F"o?feEڵQa EA&O\P7e"nZC J](}CͰa}D9;ǩΘ1ڵk=hii lN8hnnӹK:ujKO5uǏWW˰^={3;h\+ϟ7رc#F WWSCM^|y޽iSjh㰚-8htKBHE9̋/0EICIzG d @EEEZ`޹KgWWW+++3c+W{-f͚0_^?`lle˖2|Ν b(::؅ SN&|{Pf鵕w9i5rŊ /(` C a>(yx)۷ouww߷o|}˗G_oU;wښ%''R&VӧOcbbzi`hWYCCcڵBN:5dhX,8a!!Bc)u֟YJr4o|ȑ՟LBI:t*GFv d{~W^3g"~WSS y^z'ebX6IX5:Aʄ!8vU&a6Hbθqn߾-]WP#;?m+c/uYibbf͚ 8gΜ161׷DsUjIWZegήW~ḴhG6߶4ig ڼy3hB@+"@ 3 MLL?ڮ];-U߈*AÆ&ߺuKۢ ߇;v;ʨOHPİҥK+1;|8ǬY*[s8JV&uΝ /2KCCch/o>_}ui$'O,3| |et=9`0dgg'3333g@rew nУ?>wD:P+M  !s Uܹ3u֭[˒6lhiiyС 2339Owu2yQ Pׯv]\\lhh/C z GU69ʉCϞSΉ7e9J[qvƇBfÓeFG:dZ/s2) zti9PK.z?]{OnP# hʕUwWQQYl˅V2#F(++jϞ=U;!A>rBN_u7|W۷NOO.+dYfAAA//Cx7؄ ҵkƎ`F=/K^/H۵k¼W!Ν+L7;_:{csyA~NnjeffoZh;vczAr( dialRϩ*U#*) }eogׯ_޽zEEET| 0n^z5>$$$..UOU}P8BwҤI?#FIIi 0PW8Q?B4''lhhd O[hhO6M~o666_moTDӧ饤T:… u{졥d:eZUō)#cX, X#~4O7WA^~=##`UV͚5KCCyׯ[.o۶G͛0È%K\|(DxPgϞ@h:9Q "d9LtNMmob'PUU׷/^X!@xO2kΜ9[:n޼ 3ŋjK׬^-/4댍RSS(;vo> N8fA\Z)5j`ewҥe%^zpo?s@W^GM0;I1Ȼ]P3fhjh̟?B9xPC RGGGss۷W.4$::ZOO488R,O0A(fF_IMJ-ۺup>x`7 b 6ٳAtرcm;uN< YA  255DFϞ=eW@',Clٲ^{6߶ߴiSO8iB ]5kVv+!;;/~ڹs'F/]TvV,^q9 ro#R>}z^^… hdѢEЈiSDݬhPx dڵBr  4Iv>>[t/_ܷo@=˗/+6Yz(///###ill=mT(Hɓ7oܲe dO>?%5XB6l)F (fbbbC ]E"~WRאSN֭q֢"e#M%8-sUىBCj*G% kٺnKɃ06447ob! iŊzzzSLvZ¡P+H3]`!N9]]*==a .W7774)a #Ǐc[4M07mڔX:cXƍaaayф|tʂBK&Mjݺ4yݻZ@e pÇӍkTUQ= 9P[[kqbuwW… C Arc:ɓU+WZXXQQQKEmYYZBfΜ/Abƌ04MT!LSWpm F>'6ƦuM5٫+lCUUqu؅>քYĉP JHx9wر7233a~eАw?6m:jԨ iB ]A- zQf$}dU+9񴝝ʕ+X6n܀H.[(=Q… 555\",|iFFF4CW\o5~KLL͝{]e - 4B7eld4 P r!t}nݺ}?bll""",8yrQׯZ }'DEM B}<==]]]---Q\OSRtOrԩSGWWw߾}]B9y`X;sL՗W{0@@! ͸ĉtt߹sHbxM/_Dv؁N7ryFâb̑?ہ]溅Eff>|xAs9կSF >qĨ(hǘ1cò173Cubmmm>CtP;[[M{H<ӱc0,[6~OFH7(dzg6l`meemmvZ|E22S B1}ZRRR&(#ޮ0>>݂]@p!YYY^^^&&&@߾֥:Ṙ7!8tBƍe%%;;#V+gd#GZZZ111T,ٴi%dB, '!19\]\9ހURRB6n܈:P888QWWWYY0rҋ/ _TTh.%䃇AAB۷o1zahy LXr$Е+WNBF!!!+WxTSzm AAAJ}@s0%&Nkkk pBp(arռѣG䰰{*'O\ti@@0}hh(Rǧm۶M6mԨ-ޣX,644eff請gϖ,Y"_d+`T⥦fld+EC/py-@Jp x葬[s@~ܼy399SOWa3gdUAݻw#Ξ= _YhL% `p'),$hHD) $lݻwK.~***HAUt3i$6vܵkWLpH]NNP_PPpxoɋwwiӦeeeaK.%%%80""bΝnnn111azn޸RUU1.h˫W LIIehk[YY 󋌌\lYvVLJСC7nܢE^zᏧ̬-|9yo`0a2SGeiH¿tAw##øs440X~=~EVYF90^`jja߾}o0&.\=zX,ѣGf͚6mڪu۷pm=^~.?JO3Q'Oi+ǏobRzc!7n333a ;w| u.$,X9`zU,lm}"j͚5;uyޱK{A#{HHH]>?0NY alCFb(6q"B4/// nݺN(}+`vvv!?~:9Ql>*(͛ҥo:ٳwCpdc9r$}'?AGխ[W~bVB>4/dgeGիWƍ+]شq#F궶w>wPIzz;t{H%Tk׮GGFF"ȟ?xbMMMe1?ƒ5ʗ/_vpp/illf՚5k.^`~ "wjj75tmZwuFZZ33dǟ~kF@FB 䃠ѕXurt\dիW',:nbx޼y'FD=rl 9\\*s?cǎnnnjj]"VVV{ptrD!)_6s,v:ڵ CKo޼bϫW޽f!CttcƌAcDlٲvrrڻwTjpЙzzzN:I`s򡀸*.. ֗ܩ0nݻwI,((HX@,!۶nݚ@^nܸ|ӦM9\]aPz{{ O>ݰaz 5j73((رc2@2@Tc!w9{,TcΝ0^r`(~z |K={vx;;>{,Y8//+ g脆߻ =z4a„6h 66@sa$x""na``9 11zKF-?TcB-[{PT\<~0tPu5aGn=:Nlذ4 %j a|۷o-<(EjJʼyM6vXCL>ֺMV5kr[/(6333pXި t{ӟgϞ:dTJJk%CjԨ!f͚:":;BȻAۃF544i]PP062B`O:YPԭ[[y9T/!agU[_;zRwNڷoՃJ A >!LLL,,,a䵮ҍɜ`...{_p]@eff?R\\޺ukAiبQn")s!Cƍ`婩֯ߺuL}]˖-;vXzǏ;g1±&N`jj*>==??2ya o0+ɛ v l715500ظq?'d+| n8b2;444---`ذ}899'r'x'/\[)E\|922R[[!==åB/WkպIIIJp Xݻwi3$m۶AJ&NX&po޼rJx&dnJTP21ĉަ&&k֬~WW]/^())w^Dxx= ss9s$sc $524ݻ?XA:_ԩW> {CI:ymc})))e&.?(R;rdXT VCO*~>|ecmm?p]5GFރA>  4##.33SC@ _ح[6LCC૯Y4%=+T!ۡ8Nv{ndQ s$ĭtU9իWnj:ȴtoC/Bb>w\bb L]]W^aaaq[nAE::SNJAB _קN6mƵ:t[2ǎN*jWa`<, O>jjs̉mTj0'OFFF‡:wܭ[7++y]r;F={v={033304lҤ :vڵjjаݻ'@׭[#tssۻw/B><@ܿ?%%{syzz:_^=ie/{% ,--Əix=99yڵ^zϞ=LX(\%%iQ~CsTtujjM:!>1^&W‚ښ·+ظ ʳfJHX0sLl^LPAQc~N< v_|񅥥{gq!CŢ% QtB} c.\۬Y3"|[v W\ݳgOFFƺu-]`qxyy:;;#)18:T}Bi4h=&!חvZɓC :!J={vPCΝ{Ѷu$/_ΣGڵkͫVLJJ0`@6mA*GF|||>q0py﫯BH{9r+JIIy_oK;Aȿʍ7vޝm۶ӧO!{?W D!RPTaAoVZ͝;g_xqq{{{vs7n<|pJ˜5U#F\xwu:!b+ %%%P7֭SVVi~⠫O8deew={8:8](sB -[fgg?***v{e >|xӧO_r@Qaattt}Ǎw?b}B:!@|9r$---99yW GyQC~*eggڔu8e!B CgϞ=}\!ݻaaa4ŋ7ZZZ:88lٲҟ y9!ŋ=z71c˿C C7nܸnݺ5oҊ+sBG֭[&M/g}V~}SS" y9!#˗300gHCm6&& B>?iӦ]J]C͛MB ۷+U-[7t B>^zu/ڵkZZF/tBx(**[ȿu֣Gv%:!|<Ͻ{֬Y奬ܽ{w333g={D! :!|T@;Ο?cǎGB!@ <իWB>BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!(:!BB!A!E@ B"sB!D9!BQtB!UYIENDB`pyrgg-1.6/otherfile/meta.yaml000066400000000000000000000023571471451651000163160ustar00rootroot00000000000000{% set name = "pyrgg" %} {% set version = "1.6" %} package: name: {{ name|lower }} version: {{ version }} source: git_url: https://github.com/sepandhaghighi/pyrgg git_rev: v{{ version }} build: noarch: python number: 0 script: {{ PYTHON }} -m pip install . -vv requirements: host: - pip - setuptools - python >=3.6 run: - art >=0.7 - pyyaml >=3.12 - python >=3.6 about: home: https://github.com/sepandhaghighi/pyrgg license: MIT license_family: MIT summary: Python Random Graph Generator description: | PyRGG is a user-friendly synthetic random graph generator that is written in Python and supports multiple graph file formats, such as DIMACS-Graph files. It can generate graphs of various sizes and is specifically designed to create input files for a wide range of graph-based research applications, including testing, benchmarking, and performance analysis of graph processing frameworks. PyRGG is aimed at computer scientists who are studying graph algorithms and graph processing frameworks. Website: https://www.pyrgg.site Repo: https://github.com/sepandhaghighi/pyrgg extra: recipe-maintainers: - sepandhaghighi pyrgg-1.6/otherfile/profiles/000077500000000000000000000000001471451651000163205ustar00rootroot00000000000000pyrgg-1.6/otherfile/profiles/er_profile.py000066400000000000000000000005041471451651000210170ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Profile file.""" from pyrgg import * import pyrgg.engines.erdos_reyni as er_engine import random os.environ["PYRGG_TEST_MODE"] = "1" random.seed(400) er_engine.gen_using( dimacs_maker, 'profile', { 'vertices': 10000, 'edge_number': 5000, 'direct': 1, } ) pyrgg-1.6/otherfile/profiles/erg_profile.py000066400000000000000000000005151471451651000211700ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Profile file.""" from pyrgg import * import pyrgg.engines.erdos_reyni_gilbert as erg_engine import random os.environ["PYRGG_TEST_MODE"] = "1" random.seed(400) erg_engine.gen_using( dimacs_maker, 'profile', { 'vertices': 10000, 'probability': 0.5, 'direct': 1, } ) pyrgg-1.6/otherfile/profiles/pyrgg_profile.py000066400000000000000000000007071471451651000215460ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Profile file.""" from pyrgg import * import pyrgg.engines.pyrgg as pyrgg_engine import random os.environ["PYRGG_TEST_MODE"] = "1" random.seed(400) pyrgg_engine.gen_using( dimacs_maker, 'profile', { 'min_weight':1, 'max_weight':5000, 'vertices':10000, 'min_edge':5, 'max_edge':600, 'sign':0, 'direct':1, 'self_loop':1, 'multigraph':1, } ) pyrgg-1.6/otherfile/requirements-splitter.py000066400000000000000000000004041471451651000214340ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Requirements splitter.""" test_req = "" with open('dev-requirements.txt', 'r') as f: for line in f: if '==' not in line: test_req += line with open('test-requirements.txt', 'w') as f: f.write(test_req) pyrgg-1.6/otherfile/version_check.py000066400000000000000000000065431471451651000177010ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Version-check script.""" import os import sys import codecs Failed = 0 VERSION = "1.6" VERSION_1 = VERSION.split(".")[0] VERSION_2 = str(int(float(VERSION) * 10 - int(VERSION_1) * 10)) VERSION_3 = str(int(float(VERSION) * 100 - int(VERSION_1) * 100 - int(VERSION_2) * 10)) VERSION_4 = "0" SETUP_ITEMS = [ "version='{0}'", 'https://github.com/sepandhaghighi/pyrgg/tarball/v{0}'] README_ITEMS = [ "[Version {0}](https://github.com/sepandhaghighi/pyrgg/archive/v{0}.zip)", "[Exe-Version {0}](https://github.com/sepandhaghighi/pyrgg/releases/download/v{0}/PYRGG-{0}.exe)", "Run `PYRGG-{0}.exe`", "pip install pyrgg=={0}"] CHANGELOG_ITEMS = [ "## [{0}]", "https://github.com/sepandhaghighi/pyrgg/compare/v{0}...dev", "[{0}]:"] RC_ITEMS = [ "filevers=({0}, {1}, {2}, {3})", "prodvers=({0}, {1}, {2}, {3})", "(u'FileVersion', u'{0}.{1}.{2}.{3}'),", "(u'ProductVersion', u'{0}, {1}, {2}, {3}')"] PARAMS_ITEMS = ['PYRGG_VERSION = "{0}"'] META_ITEMS = ['% set version = "{0}" %'] SPEC_ITEMS = ['pyrgg_version = "{0}"'] ISSUE_TEMPLATE_ITEMS = ["- PyRGG {0}"] SECURITY_ITEMS = ["| {0} | :white_check_mark: |", "| < {0} | :x: |"] FILES = { "setup.py": SETUP_ITEMS, "PYRGG.spec": SPEC_ITEMS, "README.md": README_ITEMS, "CHANGELOG.md": CHANGELOG_ITEMS, "SECURITY.md": SECURITY_ITEMS, os.path.join( "pyrgg", "params.py"): PARAMS_ITEMS, os.path.join( "otherfile", "meta.yaml"): META_ITEMS, os.path.join( ".github", "ISSUE_TEMPLATE", "bug_report.yml"): ISSUE_TEMPLATE_ITEMS, } TEST_NUMBER = len(FILES.keys()) + 1 def print_result(failed=False): """ Print final result. :param failed: failed flag :type failed: bool :return: None """ message = "Version tag tests " if not failed: print("\n" + message + "passed!") else: print("\n" + message + "failed!") print("Passed : " + str(TEST_NUMBER - Failed) + "/" + str(TEST_NUMBER)) if __name__ == "__main__": for file_name in FILES.keys(): try: file_content = codecs.open( file_name, "r", "utf-8", 'ignore').read() for test_item in FILES[file_name]: if file_content.find(test_item.format(VERSION)) == -1: print("Incorrect version tag in " + file_name) Failed += 1 break except Exception as e: Failed += 1 print("Error in " + file_name + "\n" + "Message : " + str(e)) try: file_content = codecs.open( os.path.join( "otherfile", "Version.rc"), "r", "utf-8", 'ignore').read() for test_item in RC_ITEMS: if file_content.find( test_item.format( VERSION_1, VERSION_2, VERSION_3, VERSION_4)) == -1: print("Incorrect version tag in " + "Version.rc") Failed += 1 break except Exception as e: Failed += 1 print("Error in Version.rc" + "\n" + "Message : " + str(e)) if Failed == 0: print_result(False) sys.exit(0) else: print_result(True) sys.exit(1) pyrgg-1.6/paper/000077500000000000000000000000001471451651000136235ustar00rootroot00000000000000pyrgg-1.6/paper/10.21105.joss.00331.pdf000066400000000000000000006032451471451651000166410ustar00rootroot00000000000000%PDF-1.5 % 19 0 obj << /Length 1413 /Filter /FlateDecode >> stream xڭWY6~ϯУ THL{4 r,bEhIYYHy7ίC9E  i,7b',Ȳv,J cYc}8]CK-Fg,_oXPJ4eV@Y2΃()/Hpe Q7-nƒyL"#̓p^EjOe I  K/ݠ}{?cy(iG;ry,kS ORRdK/],( ˬF49#E{w8 `o6W@2JrfƊpyT%JsCti?+cHsNO"tڌV{)pSzѸЬnG7ݭZmžQv픊Ox3NC Uet~:?g$tH#M& ӧ@nnxVYo]';cY-tN+#"|Q:"&hSւU. O)U@Ӕg3r^cZ{?՟/ko^; {Gmwv)hC7PȟS)Yupvۖ6޷'Qż{NXo\c=._^̫G-QŇ]'k'닫7dYNskpe?e|`ᒟTrm(&<-+Z<3ECCZi0N"'>^>t endstream endobj 33 0 obj << /Length 723 /Filter /FlateDecode >> stream x}TMo0 W(oK)M.Eum.뺃(lCH( P$e||~4 K*wrm3 b7v<_芛QgIDhK36J)2NӡR딓V*1]ȨTF`!7ZL*ԡ\&9Qged" 1wppjm;r—ηb;EuǗKT,J&~[ڦj(ԂT>3I)^0J‘eF{Uup hʴ8iʥ*}INpaQ<}jF* ,!Bl WNM lGmV.}[TF״*.bGUKEDo}RR0{\׏(%Z{Ȣm 1]Z{WiCօ~a*=2cL*Z>Z&_/{O_sPU endstream endobj 15 0 obj << /Type /XObject /Subtype /Image /Width 602 /Height 354 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 40 0 R /Length 30413 /Filter /FlateDecode >> stream x}u|m&X B  E ww/Nq@Rwww/B{[o|ϝdf2L0Lfs3{t@ @ @ @ @ @ @ @ @ @ @ @ @ AǿhW_2?@ HG3@?ƣL@  ^ vӧ۷qƭ[>|{nppއFe2@(w9jԨ-Z.\.eʔ)Ro[4igʔB ]v5kQ%*ƒJ B.]iӦ3g}ɓ'ڂSJG<+}]d _:$(ϝ;׻wo=ڿ/Pa 4cڴi~YD gϞ ' jxŋ-[AC񥈀S޼y+Vؽ{ӧ/YdӦM}{]f͂ ƍeɒ/xL:5^'Lſ2@ \O>}ڭ[4iP B܁Au֝3gӧO|&4bZyΜ9͛Gm( 6@ w(u|r//o0y)RÇz*2z@O8-ɐ)8:Fׯ_$j*uׯ[lAAn.]z˖-Z4Pϟ?=zp!9xY 8K.} TR$_|VҾ-ŋHY~m,Yo߮}%81<<|СaL:51o޼ܠ@  .]6mڴe޿_W -j?q͚54iB@ 4 qɒ%cBSi&$$Ģ0xٳ+B̓'Ǐu2%R曎;R o߾#GP!/K.]AsNlRLɭ:Hn݂Bd)[jm@ 0+mǏEU1z#zA^t9yߎ;V'@ 07W%p,X0((HdwP!n޼'t(Q޽{SHʐ!í[MyL,Y2f.\zV  `.s ~0\|.n3H *ٹvڊ $P fFٻxl޼ZT(߿ӄ׮]BA,...Eӧֳ]5^p!!i5k&l(ذ!t预DMK%K&K,UTɓ'?pNK@`0y󦝝 DV޼y?}dI)d={"VZԳN#8e#g¢SHsΟ?x@ 5ԫW) K(*<ԩN@ 0MΝzM:5a ~~~ɒ%\\\D XmXbE Uƌ?^O@>|&B.\(P ֭[PUv%8U)R'֮][@  WU3g}=eE!M%ɽ_~)R899ݾ_*[@ Mmڴ}|9s ҙT[a< Ucܹ†@ CCCdVZbvb@س~gϞ1)ү_?$ &x葽=G;H1HSPiРN @-kɓ0O! WӥK,XJ*Α#G_G}˗/ g,'@ H`҆<aPSov_x1cƌ#MTH,;eZ:t0f̘e˖;vÇo޼E=5& E^Zi'N M hnݺQfϞ=88X߿ !ů,YH'LM}ǏC{F6wZ'VȆ۶m PٳGg<#Y SDBK"OOC:2zlll~~~5߿~ӧO?y$p@  TN tfU7ą_ذaÆ|齁8o<\/cïA%o9^^^%Jh̙֬yLj|' qC… ɆKƅ_.B %KFf[KFwxP}ZԗY8fn@`=P}ًfС:13ȅ_.W\͛ECBT]̆P$srlllnj3BCCE uaxxx9h5kff6lGqC ۷ 'OK̤dC}GdP uxbn/_ k֬ɝ;OOOc38[no[1Rb5k@ , 3*6lh.igI4jOIGB{,ʕQBE 4=AUPܹcbqƆb&L0-LNo[ڐTw^D@ H@ׯ c†>>쪝%Kׯ_| qС\ ~ԩsQD@ H eRdʔ۷?6nؐb n}qasmEOk K9]v4&M>}Z U8Q YqҤIiӦkysس!#%J1A߹sGO">iӦ”)SJPez{{!իIx%U'=y$C }l8W!ҥK0̑9 \w 믿"Zo[zur"@vƍ‰@`2bxcdӧܹs! 1˗/̔ d߶(w oߎ|Y[turi۵Qz޽{‰@`g˖booGgϞ0?<3Q*˗:Mqx\vu%$8::4+(I. .I:5,ٲeíVsON2-Ɯ!CO>'/~zƌyZ^++ޣd5fcpnn1`qv N둇B?.Wvt`'L$CCC *RΞ=[g.?ԩSZ'T׾W'88x޼y06逧ĉ߼y#(V%] }9sVXqذa Ңp@ N-^ #ʖ5kZ~?-[r|/tԉ#s>|VZz q :t&QF6lиqYj n2eJ<#J5ٳٲe˧Oԅ' kYJ\p?'0k焷?bvTRdW>sQ cԩe"lii @UTIq"D0=>|Ԋ[8V#L6-t+մŋ]|F -( TgaaaŋïbR3֌baҤIϞ=JZfo`;8;O>ݺukJ | ӿ뾴eK: ]q"dTҥ f٩D}W 8~wE 5k=z#9rdp7oc .-UرcUTE`د_?eu)"rgϞYjBa5cƌ;w>tw,ʴ sѢEA AVvs,OewZ Z)Zb>I@X̞=;SLԩΝ;7s̚5k|g>|om۶=4obH ^bApppݻwӦM_ʒ*La.O,Y9ljW'OKNTUTs6.|:Zh_ >_͑#Xs;1lc*T7EΉmh>}04J6l3K"6{իWC QZȅ'k@ ı8q y{{Fg>}zСE@ c"KhiӦG@@@d/r`C2n8///P 9?@at5;%逘jԨC3O:xмbM{!.Q O%4hЀ,( ?CĶ3xrQC(){ F٬YbŊʕ+SL...Pnnn}$I5ET777nޙW%ZMN|rv혵 WXVÇ@E7xxx;wN;36{QWWWEZI@ 7yv-ƍu8ٳ͛7gswڨQ#u(U<ܡBm...<=Z@ 0/ e| wbm'*Un]jCp"_};w4i]8T%۷ٛ޲e,Q@`FО/*hѢ9q޽UVk8*+͛g%T%iӦHXa s޼y ύvZY 6kԨ:ٶ`A|( #GX-pTvb#Spur@ H@Pʕ+00PH.ÇWK-K.ÅD ؘǏ&; |Cggg&zzzx"rk6\Ϟ=t@`=޼y;wn% فM0yDC Qp b p&L!Ǐ/W fLZnƦ àL2Qa'8j-[6nwz{{,`@` ]pBյbI23gUT|ԨQ}褤"I:E.\Geaح['T1>Pׯ_N h=K*; i&ZI t~FJ8|p4ZzƘٓ,Y2KL`@ 0 n *L6իWŗN!^äoHHδ0i;v E$y{{ 9@5h;fplH'ŀG8B<+!VgCv}N:Pn_si4jԨ*L2%lPaR,]j۶mφ_("ctÆ `TRZVZ% [ Dc&N,\hQO2ebeC##dP\@+U5/a@ʕKu`c+)HQ@@;al7 -CmD*\۷o 4qF '$ˆΝk9s; 4GFΝ;:IMƞ3nnnb(dUl۶ [l6b&DڵkɆX{E.ʕ+  Θ1ÒK.Ŋ;'-F:4 9^.yd ]Ď LDXXXb)}!4hHnR bP l`4E'&a@@Qt3.4-Z4$$D \=z@Lyxx|1ٳgnnn 8PP m޼'Ox4A: ,YvUDˆQ%Heѐ_~5kVv\TJxxn ɂAu`cEUhTӦMI} Aҁ-93wKV&Mb1XrUVP`PaC Q͛s^q_4nœ5k`S TFŋ^^^l{ Ilg6&UH3gZjk֬Yv7lذi&:[lٺuwرk׮ݻwٳg޽?pC>|ȑcǎ?~ĉN:}3gΝ;w .\xҥK/_zk/ŻwߟERgϞ5Aφ&|\Kԩ8c $ IIvi;nZ@bC 00w-6}[؈ w?Y RDǃSL* 4]\}. Ǎǣ~q'Om($*Et`SՅҁ-!C`'7`gdIܪB̆XIҥEW 'ubUh0?~\'1Hwvʋ I<ӧO-X6m{ 9˗/CGC ʙ3'!٤I$\+_1`|mLn;cH), k'ڵK~ Ae7o~VZ5j0X$ yw1yxZbׯ3eT̢E, 9ԏ?XC G y?^Zw P÷xNr !B~ fvEg]8*ٳP@@ Çlٲ>-[688[>~xT1w޽C >~?T;ņ4j۷reӧO -lbC*\j.3uԠ9rzJdAGa_J=^QՁ-eʔǎ78ACYnݺ&MKlF_+ .Y֭[:kڌ㲇vvqqs;v_@p̘1jGoE@``.l<صkWv#(˒%K dCRy ˲]v*YX[ !r _Ο??.6mڴa!$\L"rf^zjՊ mjbl&{wΟ??lذy; tU]R_8[nX$DHTn,ڣ <-s>_@pAcj+; HP{+h\\\dD>hL2ƍ$|5K꾶NթSNg!?a߾}i|mccӱcG'cƐFO=zT'1R !l#oF6tFRX1}9^ o2رM6nnn7<~\Ϝ9&ժb(*'={f a[r%xT7oիWvqT{={VJoIҞW H ˚5¤zg~YiQvLwԩS}ə3ҥS2SLm۶ݽ{7݌|+Ip7oޤK$9sNsjy#$DdPdd .X뢜XNܹs3#Gyf ;nV' 2e p~&uc rDƍƍ+Rktrr]6ŋѓ+cC򣳳u_ofB\j{.̞=[߫$! 7m$T($W-T($[JWBäPq\|B|ZhѧOѣGO4͛:N P޽[~}fTFS4o޼#F8w|!z VV[xq -[4ux8hРgϞi bVqոdT^@8,*|;A"Ap*?~:K1 1ϙ3gVk- fʔ{Ƙ?wp}';wN~QXXدjccs1x }e3Mmn;2oV7n 2Օk...cǎ mmEE1##G QQ?3|pIP%$ 5k>|%؄W\G6#ycǎaq0LV˖-7le< JW :wڵM4a&p,YQi+$&гСC1: .CBBs6 jl̙eʔQpU^}޼y*`-hղm6.'''Ѝ.ڳf?:L6-&'E֭;};w޺u+<<<ρ̄ ܱc^V-LՆ: 5/\м#J,.;MPH!U8T ʊ+`!0۬dN+*VXX1k\J^2}v8n"wN v{{Ch,l QVDӐ-6TI{{O߿me mcɼy# o޼!;Xۍr"pŋ7i${'bn7od%Tb*Im ֶTR[ȏJ+ՒL@ X N81zaxO߾}/0|ʕ+ի=OiKŊ3f̍7̰@ ę6\r4lRk|}}w%0+n׮]LTDMXӻw'OAɼ@ fgg(k+$f(Š޴={yQJ0eʔ2ǎ;i A*fӦMQV]Bt%sr &/^\ա@ &''5k.[,JA²0jۅB!9&όlةS'uT^(Э[7o褽L+?~5TZ17TR;RdJxtQ bObu02yݳgHBUO>mذe˖S)y*%XB+ .l۶mժUpN:}E]vΝ7o6(VZ,L45axyޯm/ӽ{ () Ĺe ZJ oF5"u]~=L:z zվC̙SdI*ds׭[ }&vq2|je̘1+WnРA۶m۷o߸qի)RT4uz5T}b5sc޽;qRJ<ܛf4'''LҥKd^ ҀTT10LBh֬3Ee˖e{{{>֭[hhK*ixCmѰܹۚ2dx5< l믿tVP ?SGB7nE M6q˒}:S 4hKxrRHP  3gN!SV!׏׷t!cx왗}Ĉ:alٲēݑ j?}͚5J Ւ ;‹`xÇ%U۶muZ͏?,XPKmW7o\4I'Ndp<ݻ(p_".]m [I 6\r7oA޽{NNN9s/^X@ؑ#G0ڔ)S2O"8IK.}||2jFTjnnb##͛WBuFde1SlX\Yf=zH;* k`:9stVWÞ?~ E`}¯0vKׯ_uqq+Q#GT8$7F Xmu *T(888nDVlEUi {J8p*ի5؊+/d#2{z$I~N:e˖Mm jOϞ={Ϟ=?m-% #3Rcv8&i߾})A6l߾}L5kַoZ:F-6Ӿî]6c=rz \@(I0C -Zؼy r?%8A:Eqߟ2eJٲeY#@:Z"mڴիW_hb ƻΝԩSђ:mڴ) jұJ@b2ObPǎU"MSi'Do7o~@ rQxf<\k{nD4{U4 >جY3CgϞU{ȑgϞ[I&77ڵKԨjA@A={6g`׮].FHdQlnӤڞ:u*ȅa,Ž7߿_5 ٳ7Ǝ BmB *W$ׯAцkf2GgbLD_ASLcTd%KCBP3$}7c-#p1Q~~~>hôi۫y Hʕ9s&[ªmA@A ooo;3Ųɞ뱡Bc~}ܹ_||i```,YjԨa 5I|FӔ)gFq% $(5OfؽSjLQs 2#SLl9͉Zz5^`΄h} cu"콠Nwrʼ@ HlCm-`ɵ&MrD!|0ӽ{_*tE̘1U+$(1vԉC޽3RL+ ,ZRbˍeãG]챥500'Q@AWr*R`8&@SyAtxŰa,džO:88>0]t1,ĆL;l2folE A <<<_|dæM' 'uD;&!o<NZ(Gؼy3OWuH0QΠAsٻy󦽽 @ H g~f61*RJ+2-裘2e ի&)lh-0Rc9rO7n,l(^xΎ̞=[gx ahr|6To֬Xϛ7/{X {͍̙3Cu\aw4c9Xb: As)P!+,Yb8BBٰCL8ٲeAB0̆ 2 /,,Lgjh&2&uh= 3'U% >q^`64 #5 N-((Βnݺ:&a6'LM.ݍ7t:U  wţuVyV_!Cc*2/ 6 "E W^Y ?~Nmڴљì|J ;wNP $6ܾ}bÝ;wJ=1Kï)Cc)7Ӟ5&M4/^{.s<ݯ[nbCҸX*_LuH#۷OժoذP{3~†0R>}z`!6 tqqaF.4"\XdC<߿_P $6rUA&M*64@v^z,Zhxx0((͍>}̆A<~͚5Jƒ0@ $(О}6gΜɓ'O,YΝuohmCUG6lРΘN~g,;Z 9L], <9%K ! † )"O<Tb2YAvUgϞ=uY Λ7/{㏖eXņsaŋ:7n {6̕+v ?/ի66600'O(|ԡ+W6Ys枰Ð!Cp9)Rpww葰@ H"l믿 mmmo߾m3`0&bq7\x9WEmڴIg@guɗ/5 -0r͚5#CZhU  mc6mZÕ+WVry6̶.LN1'SH.ÇMSR1wp9*)Rߊ+0I4twuvvׯbʰh10 ěK.MVX1ϴg?U3vn1>8&day,YL`tB#`aAL)^aDh1~ݦkv9po+6mSN  $ņ<)u0cǎ5D,LJjUf:uJ 2|Rg ~xݺuv;wnZ)br!'6f˖( .?_704Íi/cbO}Қ7o>ѣ1TRAO8qx=el3FhyeԷQF\#W~jϑ` ̍YN>>/RHɒ%˗/_Zu6o޼}x>|ĉgΜ`+VlܸqG^r͛և^@>UiFsvZ >mڴx*e *sppAjիٳ1`'';wD9f˗7o[ fTXlM={ܾ}`dmТ@ ,왻;mQ8۶m})l F̙3UY}۶mۅ_~j1v H"V+8;vĀ6 C%ׅ5sCя0YNsr{W@ 0Ĵ CŪeʔwK}C"`cck.!DcܸqB\[@@YZņik֬ !CXi2 fHOKV.\ذaCpvlow߾}ЧzWhQ (y t;wfX?wȑL{ʳ-}#̲j*cuVky֭[...l)S&L#36lq*g@O0n߾}РA `1Q˫iӦcvR!$Y0 ,c)qu:vhؼys,h,Yܸq#~zݺu0+AL=UЂ w G73Bܰ*U$v8Brz3glР)~VJK.=x#GD.J ˥K֯_qx{{;vL9aJR`ҥKɛ7/lZ"m. v8gfHbŊ^g5jx]ta#,ܹs~( 7nسgOf@Z6`(4E۶mE=zHj@@/Zk={ [>}z-QXZǏ^^^{řER_4c Eֶ,Xgj׫W/$$$~ QQ#gL:88?>9 } juqrrRe%Q)Sf̘1y+ߎ&e#!!<|pPzAN%!0`~iQ&T@X'g͚5rv c`8R!.~]]]֪UP]]^|)1a2#gΜMTw5|Oq;YWڵ+%,#$Gϟ6lHPϏSw$gz!ng< 2 y>n[ر#ǣ%KΜ9Sxt=k1443Ɔ]?~믿BxPWW׆ %yfQ̗@-K, SøW_vF 0| 9@/Wg2[LPԪUqIj$细:ujȐ!{* J6lؠ퍲`Yݬ۷A;xqeQ \R G7 ǣ3b_f͚={¦) XmڴKlYRQ+WfъJu#Ch]/L&.]ڵk:g`[ Xsmڴ)|B:"-ZP깠"!4%lۗ.]5jo'OI9}ԩSk׮쬤%\f͚]xQf!AB-_#̙3weƍ_bȋ(C| ~r\9i\d`FΜ9aaw֭sP+W6bů`Ԏd"pԂYr%. S3vYMk˩TUdWmۆ6|2ȝ;#(uu+Ѐ~IڳfڵkW'O,CޟUVJ{%UY p H]JKz۷o_pp/Tc@#ԬYSMVk.m(l;h12ZbEF) $^ٳg9ri,KW&=o޼z:pUz,uH)?#t}QFj9)S39c ,6hK.ݹs޽{W\ww V&A'ϋ/~ȮKꅂ? op˜_J5uYdiݺeTY:ĴsN8ҴEeyEYΈו.]R8g̘qƍަ^۷[lԩ~5Jx֯_?%,]T5b Pdӥ ֺuF٢Eʕ+ *[HzoX0?O PÇ˚eLz ...`IGGG5wxE T^}ժUjm$AK^0ŋ5jpssoCLQOlٲ&L8~ʹqT1f̜9, c5]F~ɓbe&MB_|i&8$Aa?x2f1z͆3WK=iIj 08pTZJi'1 0Wo@__pAMO;r."1ym۶_)-f D>p OkTD =1mU1r8ɓ'k׮m޼yLN'+6zsiμu qkfѢEk .d[/_ ?xyyi5+=53g[$bY|hBQl(W\9vIԩScǎŜ%*P$ټyիWst&LI#+W̚5f͚ڌthFBD888(QbĈGvh?3$$LJ{C8.c;^T OCeaӧ# VP!`<"[AjjӦMQs~)0!) &)!JKIT"*a%$1e(Jh2 :*Vo=3gv8gwޯ?v9g||>]چߵkWrrryVQa$m@X"e Sˍڴydd$C⌌ Wlꡆ֬YCÉÆ ױ1xECCCEEŜ9sHmk'ܹʕ+; #ߟgee=nnnBitbe!,,,==ѣr(, 9d֬YڇFkk˗+%iJxbpp)n⚇JP[n5){MYYr|"D0%%e޽DֆQc$5# #kxO>9i$~+aff&;03=!ܻwoȐ!`H)(K Wϭз3`Ry Ov8DO]о}5t=F%K 6Lt/ALT?dTTyǏ #MxƢzooo-%cGM–'L`"&e!3LJJgQgرcʔ)^^^i Ry8p@^50jŋ ad4fyh ӿD;|Ƌ J/6G 2/r-_4uuu|@$yNƍ_5ZD!Aj ?w\2+j D{۷0aFYB\\\bbbVZ%T")Hg#Ch<Kz _kG Dڵk%+ٳg^%""E?I8WMZjժӧOC6eY n޽f8p 0 (&޽{ő8wx[oriIW%lv^z%>.\x@_E%_\v˿Vgg#F 0}Yxx8O- O!eccV^^y-DJךIIIoV.Q1F4F/I":EL0j˗m6}t___yYTL"kÇ%eQS>ѹFuܥ wPCr-モ|5D,]444pAǍ+V['zzь 2B?؂I$- *׻Dj{ ;& ĐQ I6>wܖ?v9?000P^=[>{ (=APe35.]y椤>}a uFMϖ倅&66ڷo_vWC~]ǡHʹG_ p8EP>iۣG_ȿJLQ;&$ѯ_22n^^^lHG-`phTr37"xzz^~-McE g,u^ y?o "maipwmڵ`kC˖-㿶urre! ,61i*5*OdootT<' ɓ'O~A4hx V"&.i#::_M k#+++ze^^jhlWoE62[n}GPnJ2mڴ7oBiX\jmxzz8ڱqFK "DXeʯ?HF}rh&د_Yf}L#(AcJc}]vկor-lQy]]]9!N^@fƀΝ{!/R;XAGQ-$$+566Ұ`zxxpuj0F}S!/ԫW/.X*& kJ t,p6RCySAA}̙, CJvBqehcСK,lJ #sγ>3^o}48B'DJJ y3f5$zD:(2mTCr͗333uv oLJ 95[8n#bv9 ՙ2b`???^F\ ai /v <8/^ÚB*ohii=pO:usٶm[iiiUU+*}:^̙3[nrGeooO<Ӭq4ڧeeeܹuuu.]~g،Pbސ.h!/k׮xҳgOҸRE}EEEG9uI1yxx\xQvn S25j&*֔Zs3f̀)ëW֒)S[.[(;) {`IgΜ16dmչ< KY2%Qqa5h8 OviYC5eTM⪞֕)bڐӎ\}>&L lllHA֯_/A[vSCndO֖o.aA)(˗9]}ԨQ1[4c{ÄΝ;[XX8;;P."cƌ.îd`i+ܣR5 <<χ1`}#G$YaAʒLK^NN/Ǡ N1`cUU}jjj$V󩭭uqq󴲲1`,õl2=!C6Ib׿m,/IKK0c||Tt$;%%ܜpw5th=?̝,,,%Ә@sXr%W:C{{~IBm ;v eii |T|+Kavv6#sܹ-[$H#!ѱ#A㏥//wk׮%WEhtL+VTDbpBӫ uR8eɔ&4<|;6{lN'vhccɓZYYH!#e.Wl 8~x[DajjjBBBHmL:Rqʕfff$FG...˗/v:rO(w.]\!@baaWB377'gm7{(.. q࣏>W_IMehg8"zԩPR(RC;;.--5ލVCFǎKNve舁) +Vptt5nđ|bNN˗onBە+W6l@jgs ]x1Rĉqqq/%"YdأG/|ܭ[Z!*;;{ўʊEallc0Dq/fff ]޽222V^y۷#mgff&''+YIh@ >XRL0A(ڡC&M*TKKN:SM}AwnݺeeeLSexǎM>‚:6ȍy"WHdϞ=cbbWm 8*F> stream x O3D.dd endstream endobj 51 0 obj << /Length1 1934 /Length2 22769 /Length3 0 /Length 23972 /Filter /FlateDecode >> stream xڴeT\5; @pwwww!%GyFݳfܵj=z4)"Pڑ %`co@+hci`c``#%;X ;쎦YC= h p @G}%7[ #B/ gHkZY)?(B6nf&r0-H7qq0[$26.F35hoi 1(ʊ" o rtlmmO7BS h߄ S#]ZDI@I]N=@{?eGه2}Pm*0ut墧wqq3qrp7KqZjG;M'$)3CIoG+?Hv h㟜*cWJNN `ofַ6twtrex9!/KǝiYzx[;97}ۆ6fg,;Y33l2"JRgM+mk:GWǿp09Y C*bm$dceO>hcF?SmambVc3k#?]7rW6s _ ?6#] MkR?Zakc 0ַtz?.p@㟎Fp#3CǏ!(pe6pmP/-?_cX[p26@ZN2V@nF[YwHD\Frfwo XX?V/}d1ٟ3 @?Y4:8?z?z?G-^ERIAA kC#3k+@^ cXYltkHt6BFz#vf'^߈@/W)q>~3п, z?Gf@?z@?kud;~(sQ/+&k'2g +:XU͌>_w7sdF׿iW` ڸzвph8Yl kq?gt-/r'7zOArҝU`IB,Mb  } lĸH-6Z~L$"0B.[ILy,^2JP;lzd|GJ"*k]˅t)glBDq]B]u|D+L51&eӅA[_pdJAկ=} 8K} "@ t*mպ|So(<'y~]Uu( rSNBie]pq|dB *Ƶd=ߏ:Be`ŀ&Bch0n}4#BcA\^P^BcrNO;+i*U!oVy~&i΁ʑrm1jh!_:CφJ0 X?@:$K8vE_Nk#iTTG.&#n_ .1Y,9tj~5Rۙ8sZ^Z6"\i/R|Ip]&= rh'xZLR gS ۃU{6{'S h0#lջ)Qo6f r8LL;=>aOKBUNRv{fqˎ?w2mWm<:XG4{*IN3:Sٛ*i/U=_\E2Xe'@chN-zf SHS [w#pFBR3ubvƎUd`e9IvgI$0`g%m#pb1fF$\<e%Ggd軆u5ՋG7ֵ^@`fyۋ "oDh5 O@2Xdz٩h ?u~kJM%[RPÃ=UG 4fELŦ퐬"i!SSwvO.ЉFsu7vke{vT\`^Јv3(Spn1x;<[Wzۯoq,VQ˰ m9{ E ,g0?ْ޾@[M 셸|[Q**flZ qx5Cj}cO;~w1WxP9O`q9 x%G̰ YǕAZ6/(Vd紱Jl/eȧQy-&.Cw04xI=؄ulZ'᫜ >D^A&U:lA̕*rj刻|jo텐5a)>`9:nqW}T.O(>כچ7íuJ6*y HdAjKNdVb+F\.69NO2nsŀ Wg͝y-c7UiGpvjU!O)fsm_&юe<mSsTW>{}NPYaftH'Fw!ԈhNbtUM6+G퍹 $Zym j2Q; v4n7&Q:6G$znr$Um-RQe1zS'"\ڿ+ c`+DP%jly.@0L;["4!2:U18UȖH/ZKi!-B~%وDIrZ2{e 2Laj'qU-`E%u!;͚9vēiT:P a*.]|yy>=E0#7 WI*N*j+N|5nSDwgqƭNgS Cj4ds'i.Ɂ2%x$e9\LQ`.dzC\ˠWpRZz3FK ۱K5d]4lj'_mQa|6+ƏS#ylz P°x [>?o;p^e^fS&hEfNoQ .2/lAm/PTҼ}'y06r%)sl[^EY0FZYyy_t b꓀!{ROsɰ=<26!{n)iEXwkX3^УؙcFfHc.y$!#5J}BA?P \NP$+Sߛ:J X0o*.ɓ9ꍭ&մa*{ y-g+Sy Gk vIK.ޣxwXZADž~{UCp,"dT[!R湻gz f/#k/Hg׸Op 9a+T::+tlQ>M&Lq TEf w="J^4%~D)pj K o}CrXtuM!ׇHD3Ck7B׼6=]OP:GGm6Bnb-͉|m Jau/i]_es=ʚp0F:ǧ 4E#Ed\#.P7L)_ Hqǫ}RUfygk{l; ǔYٳG;<' j[TYd}7Q8LMH簽ځ^`RV(ug~O*=5}Uw/ 11;Cޗ 2T/są_xMc\{8tAg?ŐzM prʜr$q'T AU-À5 -yNY.UllՎ۝%Т hMl%N3s(yy@xBWR9uQ9|F!w8C^:* di0|]4)vyyUUrp<+i-Y0<@工=+^F-Wo6~mDynwn5" EV.cm5 { #mKΥ[?T(@U}ͩYZ\"5MrU\G2#9w~H}vuV`Zƃ(@Lq>?ˢ fMGN "w<)N3lELڡ̔3ܚ⭮աϵP(*:5 su@Jq=/ QkpU+aީw(LɰdjQÅຶ:XQ Q ̨'34hDw)r|)TڗuMHU-tK@jdp*yo:#ftYwh"kB]V2Tz 1L;֯2W ;:ǥHBT'7x@l0R nA$lZ%oWTTlXfGoS}9UI܆I8tpd~>^r, . mj2Y]}[a*_k} J)>5}aeX2?#BBAa;8f#d4ݤ.8bF3rQ[Ϣӑvb03@|4*9zn7k/,'ޙs-ƧLwC"8XCpq^өYWuܕ1اlCAF½N22kf)Wc\<_O  'QGWS(C6~mf|2*񉡧`Coe7}zܚ=~?`S^ KWx8k5YPqp/#ݑke-Lr,~te1+Տq@JM6\~Ľ/M]f ?0~x,~ r3>0'B!5c-^ r z=0 M+Ԓ4>[_ \֞wbyX`z\̵8vq13O{퐃Jx ٖ$ba GgRv#)nc$F5o9b<יP~;mϛm-v%(YM4+^BL_^CJ+"Sez &7 "/ >D)6CǛ$n,`Ɇwt9f7t̳=\2b $0ݻ#%H 5O8?m]U EI`>gBᥪNabRN\2,,Jӏ:#NϢO[ Uڶ">]r[0%}]r'⯕6qmD< GA CPr'r2Z{#?-c}՛5+M.Gk kqZxnKdMlOZZyKC+x7]$5ֺd1QM'sеOUɨn"ѱ_kI(ZS/7>Kҽ߼j`TfBAȵsnjfR2HdOTј ︵^_L2"o)IZF1r{} i Z/.Ͷ$dJ(R 1R3T-q>T1{.]%b[x+BwZiTO-i[WkjυhT6h} ,Y&1\dusv>ybXƈXC TQ^‹d7u/qfhqf93W{ǰ4ᵏS)7zlŵ7o4 S͓\/sL:\ -CO!G$]bI?s-N+p!Y)Nl#"X^lu8dmd J1PaCe 3 5`=Iۖ< /UE։V(~JM"hO=CYn`s&Y@/[Q;_c(Uek7d)=7>; Dda9<{X#2z[X_sg+x:"^՝aFdo{A@ﺱ-ԐAzHka]kqȇ@I$+Eff-^]Nd\D'-{L ɐOng[VY/܄q-S{owV0R_j X":-#|@ Eԥ;EjBٽ{"nɋôb$כ4*@?8]*FQ^6'Y^k]b }iw!x ;Jisp$EwQ{9ejrXa鲥ꚤ38xe;%U4Vxs'! !|2aJl:>FuWbӮ9z;"EwƗȳkR[ G>Zq“q ΌfB׈11Ś[ BzMMM ЯlhC-ae׆~Hr?;{  *iNDm7 ͺZ.;LvJ^O3õg VJOX*.C0ws(S `%bjhQZ)4jgt| "b'CLRXM%#3)O4"XM j\S$lZm^jw+My5].kDK4&HB fJ"iE'xgbcW _<KHFmuJO_ KϛYZ Khk>D>l鯨nSos^ -?<9tSr4ےD9>5-S>Fnc8a NŃ"~\N,^N(y\ 3O;I0QS)+xGecٖ6dxIF ` / ѠO([P>ɯmG׏h@1zO`'of |q8s%سqucX4!LaZNg? #.m0P b O =nB]nE_֔FDgn._FrO@@`\8'7XTQL9MTxXL(E,mK0|4NΨ|f|Q4h{<{)Gq_Ca^وha,W09j;lehV9~--؊IZDe:r='sT&d*&̲W JOq(OKtTW(8: *rV)+/WQ! g\<1oKa=~Zv7 b91-/Ϟ2vr]3oxHXzz@4>f9*MSrXA!604X&J%h]84bBJ8Hs*SQ+X/O1C:3fX)P(a({Q'}Fݶn/ ZX;AS R6#pN.VWZ0 /ѻk.g,>DIsb]!$x*#}z| b>!]Yf{uvh@8,`: =[Z2Rz4VޠJ ǟ|0[$?Ok8j|qԅmgqJ|@g)!rUc]\l/.G @7yg ٖgeR IT|n1}J;X ,D?Lcr#y/7t-)|obBp3Fqݓbp S 7,vpX W2CF(iEGLS\u`Y̬F'ߌbE$>md3/}2@HLrNX:!W6=&od+0'AJ;)q8̧U;{Jj@ IZ85eU7s|7r(/9Fp8(Xea2[Qgp!7ZM 6L ~+TG9wu#M(ٗlǥ4 }WS\֡8pb.MnApiF(-' h7rWeL\%WV 3B?=ՊGo/*u(g;>RWAƃ G=iEDl&>gt&p*轤n޳K"<((XƟfDh#1 IHK; pR+E1&lq-bNiDo-6aqO pgB֗avx66ɒAbY2έB_<} #ne /rb݌ s w@G^!:Ӊқ.9O׈D߷Af'LjJe?|x/[ :_,F&y?pg;m@Ѱ =ontvq˭C4 ik~hnBm)?4T'psᓬ$M|ԟH6nW$-c\KD&h]obpzØv,p97@oBRDqph};eT ߧ\2s/2E.w6宕9=Sj н,-3ʟ NX7?k9@S0Dybu˿PM[Xe}|C)N;u@QntOB :LCC4q18x߂Mr!O+кiC+VQrW]*p| h/e⦖+=]1CqF%$NFW$d4OˠZ(WVb924UAUj$e NNrV`6O.k2 㚒# 5ڠ~iקoBOM [ׄ:kPOPlՔt0TL9cw6'Οo2#}Uhp-k[Hqxۅ$~ cS:WC_Naׇ9JF)t"1tH;ҚLHM0*L< A8LIo0nϋ٤0e,J-D!5p H_IW0w1Jbu@ :aqCFSG1 ~k2%tDhP#-2lec9`;nJA2\Oe>PPg'U". %]eK:>ܔm-۬/ ?*/﮵Hz9{nte-pl:(8ڥ@Tqyb!E6DdHroʐ6z co\ s}=*ʿyj^9ܱ}uaT7oCO),jn6܇Sr^Djnp9? F $Aj*^ 'V55Q$ B_H(TtìuM{^bk-ddrFotQS bw,&MjBvF\Ow2ˋbEO؇jKU>>]n|ɬauc63#Oz wM佾S!Cлj 턿!Sj ¡&SF t22{I# 3g+[ZOOi=T7uvkaKo)i]Zr-~O(C!5~&rzW0jڈEnF0um%lYoP{ڠX` #?fAWֵF1 #2+IJ{G5(?/p1q. XkYK` ȗޟDwZp)V:$0BS}R֋f*|*-"q篨!C;{d7FlRH^<*i$[.e4tpokbt."K=2 \"dp: ^\> υ~V$9s W EoT|>Sl֖WXq82֊Azݏ{;m\Cۯ{T?+5}%z?a:׆9OD9|i @ ftVx2]4PPvIOf;bl(IoSwātG{{WJ択-ɂq֡5cbOniJ6^^!j؈c)p{FV֘1ϐMAﱾ!8 j^<2oSA<"5㹏rjdM}Ji\Lݰ>h3[PXϝl]lşi7ÈX4&ٗ H ='ZXB<7ɑG:ҸFp:Tش>(2IqPX \Tʩ~tڨw=5KmH&:WϮ=eT+Q6h /_QGBEm(+ N e~K 1xœ)̛!v#vU6+)ceZذ4d]Hn$K/x -YR\I|K?D3 ru2% .]* -fKrQV3`N,&!̭v-WlڞnWhیZ<'N5KZfBtj6 :iSB?'g2¥7献~ˢPbԯef̏>xymp3ιΕe䬽*oNoA϶ ;yK,"{ a ѱ$_Vk#{eX9Ns%1^)/NsMENV,!iY@3 !/͞8o tbzi wԑ#rBe8ܔ On~Oؑ5{ZvkTρ|WL"q{Z"b٬ND`Pbjo?7R6iL]DgR+9 AF+Nn O~Ug,oǐ)؛طHKiؠ^vhVQy:Ecf,;g,PVe*gm\Cؾ+\Wԑh5Tvg+Z uIDOFJ¤){'߄3&A+8ӥ)P#b&Bs/N%C!1l*c~vgxC9귄-dham+Mga,V pNw:1pWV3Frr~JH *,ԧshvMvm܌&ofۮ55ٶk6m6;ϸ{lCxʢbqeThWWΌ~VOonw$Q_'3?{ I?\^&p0vQwtOTUJ^RͬlȖ W%8? _w/u7_s,t{|}VKsLG: l8а6n.ҟG.zֆWBkw}wuBIˡ=eLC.65ӝڳ} :z]6mSǔ1 quVukw_I8'h0` H4ej&!6RޭZaWMaN" &@x!8P5a?[$"RWgwz(,87$= 5߶_;&\ɛg>B\( EDZBJ"rm7✃@{DEG'*DuΘ$zE0L I+2%t`W,YgbBH'ezh.-WeM Uþ86`ag^|`j>S~P${녌yMLEy>+!Yk H84f{.b!9?gǝ?m@*㻠@EѩxMA2) M]uhR.Z770ӃU'zнUVGy' pFe>f<-8'~sxNZAXT-U6cFE /1qYJPO¢YRI>>=G ƌD w*/{k4Nb~zid x.^ ߜ$ER1AUșQhoV wL#ʵ.7*3[`eD,'N: ל@?+)O^5nNJܿJDNF3tl-2'K@fP`W4R7eBXm}ozZL;BI'Ǘzx-; xb-Z2y*(~{Ιx ";^%̦݅rgצR\N0. VWO;ͰNS>iʋ Brkl|0>;95H~DJ;݌0Fĭj/&f("܇ycQ{lg8Rj;W6m7( !m@ U-Γ[22=1,YaEǛ >Ty0:^BTlI_oW&j`n4JGw1dg! x$v Jjp$Mĥ$h}8!,SRlAq١D}+bf&} Y"VG{ X m,0㯧/X=ʐMݤXy/IFZypܥxs@2Ap>R8]x^[~`Z×Ko0Dxi-͙vP4o{=k'0A&{ibF=V ~::$ץz<\Lw id*饖#ktuS(*L8$ zhgM-sx,ĝꁞ9>IK-,cd}RpV81ok_{S^{4M߸3^YۇS&pTVbr÷팫F2F˫QM 5)&p'^;iYO= %Z{HY5o P#tMÂ~#bl__ۀ#NvWI)N0"ɿ\ Cys]R>1̦ ;(΅"Mr=*:t SԻ-3 r:ۃ&1 erujJNz+:MٿI!'q&%oM[&UPW"U"8OBwU n0n@I b칄-ݧT6FM~_TZ5-QqX튚UUɕNQ^jwnS_:\>r+`I.Mģ"iL*T# Mn3Ŀк' tt\*.;][mi*ΟT-=^y v>Mb̰2Hu yL } *o* :/rzzfdNA@k>=]$b_X>U5R- 3 98Ao`EWrI+jB>=GQ*kV^6eK6YG|vۯh %MqGqPOV6ʦP]l9!B Ff:.P&H]mb{91yd'-k3u4;~` T@[b<_RLQ-rG 7<93;E)%>KbS ek X|G>ӗN6pZo%jJ 3)IZC'c3,‡=,w ]dωR;ZS2Z !x$7ҫK#}6De9S;CH] `ޣeնF=ld0U_w9MQCvv|MMb 3vkAշBR\Ov!y#AL)]W HK%ggWc_9%-PP{k<9)59GWu!JJc'aħ/-_u©BK]@GfSgMm]*U/k '6PX뷘Qx@>9oN c5+D9Ya53+5փ%Ч2^ "9=8:ZKI7sm^S4fm=,zqD̃zOBޗz.ㆋ_aZy1ߺpÖ x?R8&~ȔܳK2 Zi5CqaŵӝVf*3s#fP =}!y!ESS`G:bѩν "`T^8ORd){ syױ_̌!{'Zl+Yf%Abff]μ{UWR:c$vh7vmQ5h> ~Wf'GfRD~P[dZ{DKH =VF?TE%X1 9s(,N e[Hv+N 9s-635R&ɦCĴLu՜!&&IK~Z1 XS֊'F:׵?4gY% ټN>GM9t">ΙEN68Mg(Qc"c/ pMh9:B?Xnd!\8Cn?B)ϻ\)cNEzpO1Z\KH%}q1dXo qB+` t~"D;樬ϊdvC)rꆗPy ADxXK: MVw%qmM.׶dž&ca~NlվEfً7 󏭗 UQ Zr"FQipa5ӔNR&\JLq_˞Ev*s~fe.? (8-FCɤ"$]xVU.F{"u^ӌё<9Vۥx ݀$22~aw%H`zX7U/Hرo)uDF'+EIjE~^.22.0J-_5lN*UDy K)<E4x"DGpUe[ q;{8&$ B|SUɘt6N$^[a 3i}0~\6?{us(giFv7`%{M%#yFN"_E$gM&ktD-C^#TfX1M1JJcf."8e|ʊ0&c4,.z ]okfzH_ ">Ard.nar%yeu@B^<1&P|.|ACFǫWXОQBв}7 .G Y_~￀TJ?]yR"dɔp =~Rɐ0F@f}]Ɖ^e*M +2ި9v韇xp2 Fς|XN|*aCS,_@׊Zӡ$qrB]?v؜^̣03\m>Ucvon w`sztcRIy"qvWa7^M`j{:ԧtMB誃|KQ:Ul'DSJݣ{~S?X-k hrѝլ*Y+To"b{v|8ӷ0xOZ볂Q$1U?ئ}(:c 9%wd@ 8Lhޯ$gF%1nV.ktQ)_C:zc){aV ]DIByU:`֮>sMʭ(=}c۶ܼޠڪh4KZoiq4Sǭz >[¶0MuL+# eu??/h@M$@ !)̭Nl cK8vgpbcȑ9,^JW&W ׅK1-:W^ђcRtT p#,]_HՐg1笠l<f-U n aXyY#m̤1˷+]|M-x LxcWis 8#uQ}fzUCܿ ~ %`Zw]@=֋=J [VsGDsG'X1p`NJ&VMSзC>= F!1KQL*- endstream endobj 53 0 obj << /Length1 1786 /Length2 20629 /Length3 0 /Length 21769 /Filter /FlateDecode >> stream xڴctd5mbضaŶm۶;vqұ1:ΗσF]kUG+ lxrv6,v&&&6XrrG3lP0vLt01qÒ$@O  t6T2 vNFNn-3Eo Vzf 3 ܜ,&i9ۧ@eg 0ZL@M @BYAMQᳰ"&AWj*?UU?|MSRRcf{f+os2jhgO=#3?[8WG5b\lM>t6%Zm3?}N@icnO,hkhkl0Pk@ @ov9O]t| s m]<{vNNNZXNw,l K~ ϖ^[gwp1qL"5 /}<99z0lljjakbu{F5[ ~`ft09V(寙/{;{yr2t]>^`ba)ύOu)[S;̟뿖MJCMl=&@SXFy;O1P Q67?B4G$[8[M-RΆ5~?&S3 @O-[Ǽ-#'%/Or'H 0tt4e ;;S&@D`dsLػ8La.$ Q_?hh?7YgBf&A6Avӿ϶. Y>y/;TtyVqvjX|V[ӧd?ퟯnlaa;w/z6N= 7sbVNk h dg529Whw5&tjl'h. P8?NVG7%T<}-fDIpWQLh"OA-0KnſD:Pm.=6q*2rJ[پ^V܂h꾂ۅ2 =.ڰ_hfޠ0sBҾY4h*mջ{/BԷä_ALJnz;E5ouSyV򜵰GYҼn:@Z}ޠH2@p5Uŵ3zsm*1{זyaPwkr-߀IW.\"w{ռ6Gq`91xSbZORe^$C8${ةǾNё;DO&EQLpLm-PGg@o~Z!{}cJJo#:,_cMVnIzJ(7{+q#>5sIˏA]HKev5&*p-4Ph/W ռđY_Tp3He;}z!>!ӯ E;~s Uh3:_zb%%p C8`_s 857ArIMH3G6MdcUThۈYA(F6ip*I" DK-F s,a8b17OBX]ow]0juGt3\n7eQ E,^RVSFv:n]d *}Q@nq8Jnآ/X2DW\uQ,u ^)}LE7 nx:ro&3jWk޹'_ kRl+=IEc lS EyQ)fgW`tq:/g+Td䩁[s&51ϬٸN%'a*{>}<5@v,<"15NC tG*7m=ٲcxiQaq&Uo9@Dr-Z+ZuTJ-Υ]zVY8nVwVb(Cǒqg> vY&}A`hGs;R*OLX}AQ 3px%TҲ=yy3lZ]EdM2ݷyynqJ(䈰ۻ2߱a"t~ Rlq(;d3ڊN< vJ&w7 ;P^0Q=X*tI[rǛ=9C+< C".^S,DDއ~y8qk;졗^UKQ#(e,w2VV ̿*؅NI_jː".BUj?0'*xh5;-{ h#oKl _!-0r޼ "LbFOS!FH^(zI`{)ץS2CE^(zϿ [HK S7k~=K SIT7g=G$6<ÚʪǪs\+S p)~T儨u5v|gug]ц۳#JN$]z%ݠ3˟;͡!.j[8rupq[MxJޮgg&,=i/FD%(HeaZp N*cp0Mk.8QND@F#?8IܯjZB$rG݊]U$:ۆfڻ7jhQu^̎$\Sw^8\VU'-R> \p uVwl"oS.\ 7rJ7^A~DHy#s-aZy"h=!')1'J;O}?6cl< qb,eeܮ ȑmk],IBWPF#KBfSNpqǑesZṟ񀧤'E(cXM0Pa*YDzj5H-EF'7Ók6 __D[iP=@+G "2(d<܅34r0‚6khAWj]`[// tz{ FA%WRjVm l mWaai Eg]~.E>x*(w**jz@5uB"au&^س DԎE\`:& DzMmy84nd8/ 8- .\Hǩog?ռv|FF‚Ec墳Hv5OQכ'JPS?īXy<$1ί(ř,+ۀwPYU&E1@(S@E~Y6KI ʂO'Y3,EhZq '~ci CMc1DttcPǬy9:z Guegdƾqg(zNiƯ+ J@."?mf/r?hERݘ'?6jh _xSBYF$W{qƚq#ޖUjTpo["+_^*s]P ŷO\Pе񔛾Gw*^~N:ޯǞ4՗ %4-[@Fh2WvkǣX"Z)܍s`zptjBX^$M>heDqc `4{ r^vh) Zor2љ.dϸ%$yճP-|uljH(M,[ZS6O.I5z^_$p=P`0nq;j R\,Za$ByFUv~?W* v/2tc2*D&1$ron$b%X?`~0^zR aOV|O>A'BاB5kI9Kq/G̨Uhn-y5{OPe/; @SHnt:W~.aFِl f0~ ˖_~~ޚ%R9[rrTh "Gq0p|QԮ+nQ=@ܮ>Z<%~`ܭA1L}e+ҩE{i^A$ w!TK%G`]r;GLKs&!~oGYYpՍP< lYFLTZsē+p6s.L:5CқR${Iv%ȶ ,YFx'Ub Dk ޑT 7s/M-W,us )b脫|ԉ}j-hzƝGnMx6EV!on~N^aFX2Y|2܎%:m=Ġ UI d@.mf{eucTqsosQ\?!dHHgWn/2z/%'^j#ҳ8\̮Ǵ0/l]eZ5ꭚ YPl* ((뉅Ɠf #1:k֞eGXҼ޿|Yt0Un0Fvʓ8R_1`6)k)hĮWx1xJVG&0 Cg$0Ux 6F})#) ! '7 z h̚Up_ qHq IL{X+ <U3~v SHƜ8,-cEуƼ{9p3i ibYP4pOfV7׸!l~].F<]mG{h ,3<ȫΠD }HYzH&WPo}/ +F8a>a >6Z"2\X9e,e }kv= U/ à3|:&] iN*A${"-߲VjfS򚽸xkKK<*Yz~{u7b j<M\mH$^?7sANǑH_8<=ӈ^p⋟0WmsRo mz.@#?㐨w} +ih7` e[#gh㫳̙#2y8fa >]aʖPмV\(6/=)(DL5E'EK5avKrAN<"s_ 8M$S!Q5^ʣYU8,Dv޾rQHc 1a9RH:ӰYofVkjNp :\cA󇼠fsQ$ ^׭V}`ةkV2|-$AhzaAu M+z}A6d8;WDi8p<&@G.v@{9avTJZ:K(+$a87ӛk<:I (%7Z цKtþIu.(ZU+xp2P>$ez>-7ƈ0@ߞeʄ@AiUbLZ{/bCjQ" U|?RuV~x ٽ'IDScfJ5ծkk0ivn};,[ș-x,Tk$r`,ohINxRyKV>qZ9o  g],"[fe%M)|uBw|Ŗ3BOi^gH>d 撂+%~'5( p3EɳK E떠P4&oWYgnΓG0{1Iz` Y%Spmsm>1ǩrs |Rx]=D7Ũ߶dɓ^0yfۇqrQ~5t9Jn3G1ӮVL`TB)QؿW0Ťp UJ3{ ra ԗLJ سKD.\&A8uP&0`FO~фUG5@N٠~&>GZ }ټ [-f E60VөgZuҁV}(k@SRa,&E1⡃bkFw$p !j6ۆu\rEU|vo;{!f ˾SYVf9w肴3ӵn }QZtM!I\Kͼ22zrN =۫TX}~׀V519V4Fٳ`/bC[a`0:\{o>%8fQ`.m:OyJ9YARq.nm~##s(i"&2RfJ{ioWN8@Xp9}+qjF7/w_%͓O t !i|Ы?3aTKiV#yZ$x>\J PJQ‰qHP-9ET]뢖SO?|8ST Gx]qPEﶮ-1Nm|~HUrQU~w^yX592̃ l~`G@K },$OgJwE{ Y% n 4}oy. *)Ҷ[E1l/ f I!oWE(]%!uHOfYU#Q %C̪{6"AcY7{kxWL^QS_esKQۦoХų; c#05Cu98 6)^]0 5=ƻeBzcz12|1Z6D_Eӣ9L8" cL(ջxM14(Yڦ΅/4)ϼ]|`[:b1LҶf"3ڻkcὪ֛g6mL5|qMhOhW~6A) :p@+7J^IN$5Q%n_(LE\$ SCh$j oNCE:;Y9pdEf!@j}Z@Ep4Rvhi`Υ`-&uw~OwgŢCTuC(M>"M[QX{ĔJͦV7S82=֟ͯw ?>G}Tڜi]/9RjXAᝇBiݵJ1,X8P_r;00 oo(5{T:P N'F֚}>R 12^S5 29.j#T)G6n\ҔD=S9D"ut4@4%!k 5TvO jgI:3>@\TbDx4:[x٦LNxLHB??bڽר/f2-կsekq~ԐH45.qT*kԶ_ H-^/)RJ7o($DPwQlT`'u |;6 хJ (6EY ;\ K_;ڽ֖OK>x݌bl4;K/MWDM>P̟Q߻9F]`?ְKMn_bTtldp ,Rt.gU՞q/]3q,alo'|a؞/K+Ccj)909噈@GNw`xTH)&j?7gϴ{wrz,ZP/0m:객`wML khҥ3bKK{/NϜ2:WRN?A(Hy BruvS]l?"2~>{N6ZjD&NF[w\>ͼDˋسgk`5!1 Vܰu{ܔS?#Wx]I7gTU©;!h"07u /6h^ʕy9_>Mթ™Õ|2_'֮3 8;?~븋ҿp#@@4 SoNf!+ ;b?.!-gT8-b^7t){y2x$\ mKIM w4פz D49\ZHb wy(>wM~3ϙDEWݝE!2 H3ӊ*C=Joܢ>ͯhLm> 9Oϋ Di[bFbM1MHBAgq~>)k9J+LzZj7"J)'}̆fYd#¢hg? meIRP9z[?v 㕪yĠwu<%ޏO,Mv;ǝ`GyfC NEFWR$SCCCfn=6܊KoITxU^=W: ёL:- a! oh=ܹyPF:!jًWЫ)ឧ9ƾSF$8cگ M߆gH`.wRʨ(:P 9Z}WvȦ\5N2ȝ=K=}Z瘙VHqCud="1&*tXŒrJP;;L3;=;wj,j@VﭽW x`}WEXq:^q0sHӉ8*L7q}ʀw[xl\7]^6ʷэC{sL$ewZ3­n3V˸2;xe"vW-unk)QVdT#H͞-!ɓI@]wpZGq:u}JtA6ĕ=iY 8^~hi'+ᷴ'Xe (}}?{Xtr8=qyhCҊ{+=L `^} !qtjGauP Wik4=V)Ȏ=kJ_ڝ`FB1`~/?I "!F0SA_"f"V6ԈȠ2J,q Řm-j-A,]8- 3h#O jjwLn` ]f#~Zٮ-% s^똃Stΰ{8j&@rf+% >XZ><\˄^|F$jS+EN0m=q}Oͣ1Xq^ Ju*8` \oGUBJΉB`oN53aelaQS,$)TBQHO,aRCb5Btq J>КZ˗`rI=g}C:0)u..y7#n:lmPF\V Xs˒M4^:u*7^6P..ae"3st[`Ae 8}y7(BRZ9Μ45)\0 3c,mY9 Ԅ\#%{^f[y>pМ&g vJ#/L4:1YOf%߲׋ Z]’Ln{h9^R El& z ] t7w=f.t4U LPl5C|)Ģ`nx8}$nW*#5tsϤ_s{ @2 vrwBfR(a9gtAtO0|s]Cyc"mdZ>T"\fV˝q q䌈|X n:X#TtG>ّy>#yhU_3S$*AO6c~Raڻg#8Y;4i4! D@?x蜇MIG ۅ0:ÛIeiN&K*2GxwIPPƴrqiȳ m)RZ ;O6Df'f$zqp omW;6"cB]gki‡t9]sl/@_{ gD.,ڄP"oyepQ\*@,o Lu_L)3N;n W-ښ]{V )WQ03B HL|PR0 &M}l*-ԟVGuK~r_07.rmBA@qv4:DjeX|9ӡ-Srp[8Zu`ILn2ɘD 2]Ɋ绻) >/|Yϝv{{y8K r>g'87N 8'a۹&mo5%13EtN!_R]VmF[%[t7c5NNf#k(8@}vin3cqY$WN&BxX8."GEcM˭)hqR{6d„ )фH=tFjXPGqh[nKco"_>ըSeSOMIsq%U2|/Pc:0X5])b2ey4?Y,]M`+͏tT9^fdnh" +7*J@6,->6ޞ=S}/?ꑞU Ω0W2[`!ο1!MHr@NpG]b|.mfxtVS*!"0 3Uv+(FyRi(ejC W_ݝ@"ԭ w;eb.8c!'b~̊eu.F ;DnU6AK3bӕgA̖ǬG)n X('"x\Q%Ԋpj`깣Ucٝ_0 <v~^rJW#b؟mq$w2z7ix^5Ϧ-^%@r@G ZH+2AUQXJ|HGM/_]2^TS 0XT[v%du8Bo9d2Dl.62*S߂E1Lɟ7C*x$,Ygq"jut/:4"p4xQO  >s"($*L#C)LQׯ ɐ)GǣcyиNGQ(!yP>㳸 s6l*+z /QlI{70ؠ\)Q:6BAH8K "7Lq<Ƒrvy{lut^X#Z2h.K{"|B=498zs6wٱ+HsaCzd;%Ē 'w-%>B7 eaat59RṸg^ }@)PK #֤+.rkZͷ I1Kݻ$&t'7TWسѦÔS@yɘI(Կb`3@JZVp& >Uf:A'dVAPW>ߤ,MAFŮzXN.E]7P4>هA !V5mAl)Q vE%A9i|F<ʤ3{qw_;% [d_!VQXlyC=G+?fH{ ^yz ^}dүƀ291!mՓe22F}[|uVИդ`=fǷӖ1:vcC]fM> KB^uof@>ii2K>If-9%~ r'Jqe,N'!>ca|d66v069~ # RXFYNJl^J\^{勣,bK#KLּ{zȪ7);1aL/#^(QĊ\6V&7"߶0=)ceرO!m2{j':yϺiQ}oAu+OA%˂O8|~&({ǽ{;}5:hE)ԇxX ?RJ?QWz6]isMO2gSg3$CdaJ<{%GJ' W?L|Πhr~VV{ w@xŨX ͼ(8ۖ1}Mr$hB88&>Lv~0جC`2}4dÚI yIK_'/q8\涱I\ GiɱHz""6I^-Ow@Mʴsj>x{VGgKU>'NX];޸:Ag`bX-'ܢPSD)Rx[x*bRTi} &Jt?iTCC2d7l9YReas4.WOV ^41_еic  ؔ-τP,d7X2@'=Mz JxÓ YTF9PKNokn QY{`3r;[Pno0:;D+DxG*+Lvd *3E>PW/>B:3v׻O@3wK1~ Gi,ZӶz/^3UhJ ?-`s0iD'Q鹙:UJo'gRa@:K?b̋s&U+3Kynyqgv^:vz"X{XVNd2ʻ6g ؐYD$ }:T?jwbLI/s@p#3.` ПO1*/#-&XmE<^f@)d;Xg+qg7:PV%C 05ךdS//{{ `K ^ 4]IZ84p%m-)JzH$ `ӥ$m FoGRWQ] p&0=S̅%v&9"ϖ< Pc X1KA7mi%T#0DbR`a5⭺K4Ҥ*YTy`lPFעdEm(ss$m+20u+p-H3ĭvE2V)(n2 @s1q] =prgy@PRz[t#$OQ^͉e˄CCsqtT#E6gJFgq9fh5מɟ=\I ۮu['^4wʈF#Î_\xlp a^K = !8Ehv-o嚜6!a- 1diH q2UyEӰ n)WNB+Wcgc'!-eͽdA;$9cfYnrCHj*}Gag,]9TP%.Kp EИqYdbB6(]gHd#{q/ $#UqhcnFb%YHFuO{й>$Qw CboҤ> ՉG=XeF Z\Xށ&D.^gVGjX9PY2.m$VÇ0VHYAa Nwem, W)gRXPd 1&e$ mg[kFg ϮtRՄ(hsݬbFf !gMX~Ƅ$QDH&*#$bm?qy) ZWPߙx֓VẆca薞'WÏg;A΢;_Mlnu}"rzY樜'r% ;H`h!~.؜'Ne^t.'fk6"QW=! l F8{LNuocS ZkA 6n,4W$ zW_[u_๽zim1_o{BbPI_tw8kfz%Er{ߩ5Ld֢\H4&oTB맯Ȼ&y̽u<:T6J4K;3: TmBR׵iY³Vy#%ZXZmX81+!JA&/{b.W_iw>bf_j܌wo5(aJ^ ?<{/+y?DC4T!9aKR%p~v37"M I޺ːi"%g)Z/\޵^3jTalp:rc4aML{][S{kWf6hӂe5b"TuXQZە5-p;タQrVcn  #hfBM"7=';%C*׵ǧ0til%1_xm62n֣ #0%oRo.uA:]#FL(^S0w,ѷ֕fSq'G'dēy!cWʼn=P8UJI1EۍEw=(#~W(^bJ*VE0gS94Bb Kv؟g+MY - H$-xX >.uUY\.,EQ^k&b4 'GJ~EwOT,OnM35{xH\Ht YK{B{FhJ=6sq񼾿,Ld7ۏbyYd^RЁ$J03 $İ1(PKy˺b\..prg rE#5 nc5C /I PE&n#֝9NJA=..%!* .dd'3K)O6"PU} HI5cE/FwrjJ뎏eH `>;KIEM~8 WA("Q>Y^.&\`b=֠F*wP9 9I;Oa2*zr^ ] ק&?mϻ<3L$$#_w'ӋSɯp;^n<4gN&El ,4mB@Gۈ,ʶ.lJgh)-{muUbémNYH-zw]| 0Fo.[$ ^qf:&#0f'o-^LȖ{w.*;R?};dY㦧)d= rtx5=Y hݜSqup-A{Ha5^YZ8[w;UJ}^!t"8+_<*uDٜBO/RiR -Lva(Lkmju,/q1An 4z!;p:B*{ivoT1䊓_$_y۳:7{X9MĠT @w5Y+e_t#Sks\AJa\s=R"N&a(o:txS:AcanM2$Za< tPd.;4./5pIb ;[|ː̣6ǺۋCή*YH u^rA#|ruK%m3{K[G-MZl $]`NR_gȸxk1l 27^WD,I[ce E,>?NA^Nz9x@J VtC<Uq]n44W Vo.U)JCy3Wi[‘"07'>ר)ɄbO' endstream endobj 55 0 obj << /Length1 2759 /Length2 32463 /Length3 0 /Length 34007 /Filter /FlateDecode >> stream xڴzeT\˺-ݝơ]4.]  uds7t֬V-P3Z̀R G03+?@AQ `ʤr7u3r"SSM6 G S0(!VV>dj4qZ̼@ @gPL]!n#"rrl1f`hcVd(< F`6,@:@ZMYSERX ?\54J#@ZS]W ##@I;]QRCTCWElwō $W5l fX1;Or@ hl W߻P1:'ItH I!@?Xp0qM!`S+/hA/@vͿ +333uts6\9@=7{{fMQTIVJR]I2xL :`O_ѿJ(xYl|VȐJ:Z ]'a rb߃mp?6psbtqvJO8ĄfX@gܚwÿ巙" `ij B!`7Dl< s0d!겎 ߿&vuT!hZ"(si߁6^ motJ SٸJx-Tl_vY)dE쁐mˤHCf0qr2v@WWҀ!1D|,*:o'hqsqL]\LY!a a0;` rA\ߦ!n`x,E?"X v`8,rA+AA*AiAkAZA"M O 6NHdH!?$d[89[dc4 SLߒ X6p;A?9!RZ{9Y Bx Bd`7A2$r(C ?nH1'SEc#'X]%8]l@ӟ 8Qu zMM68Va t$pMS.HW? L\lrAVvmc!{7Y 7)79H%o˿Zb7:dԶ-Db v vǿ2G?7߲@>L&vf,2<~5K_W~@sE@mꗰR28j> w:r +iD9;@Ⴀt "P͖ʩ U]SE"tIѱl-ftN #+򢍣 W lud 8(DŽ`k,}idkWO$1~pBDwJF'I?h 6 e2}љ h -~ [(9L~iF㧻&jsqAD4σyͺ g2,C7kr2a2E؈*v<O=IIX%^kvDH%:+dVk.!@B@$]|2:'|}Ӭ-Gc0EͿ3q5x[kA09PsSx謭3'rhc5|gbxZ9 o7W ra6oж%i~个~W>_퓍}諊d̘HK+)}ceh; 3J&RK٘zM2_ .%[d0}k2-AS("6u"Ϊu1`1y^Wj]#7;KO' ]P^d_pp9Splz8BL`oPR5e0O+<#4B@;0񑺥q6sF3mU4|}5MxE}}1]:E2BC|CT[X_\xM8w9w;{ k\=\5fDx2;wH\j{,hJy>91osˣN da2v`|ՌR7پ Sfz"K_nT m(FRI^CO S*!s mAI)8mpZg-GͷTyyoxGurW9(=ku%5js&֒@%M4g&#b9d)hcGbHEjU, y zD%Ȍ ^+$beY&#{CAv>f&nZ85JL5qpʤ`'`=񼟵g&^ܜ稞-E.6_B2_}XIjk(H>,#F2tnJſo+JVLf1q$Cr͡ݺXa}bf! &Tz,P6^%iuWɦOJPJjOt:L=f?0&H&d B7)hMͮ5lξr~)JxVW(_2RX}}3u Z5kl\/,9!1S/Qhn0lj5xgх_:gTΩF(o +FMJ=m?[l)RO´W{gBb``q\RX>$0"RʪF$^ˊgo"_n5i0"4f6^5&K3'P*6עIPˁ'= {%s|8KLyaj{#2k,I5"fM|lg`glʹ4iX@~PO8'V/4 >3? b Na܈J6"wH>< ~x%|c=@;z]06|mXJNe;"醷 pZSJMxCbIKIzH+l!Rؖ-2g B$In4_mK֙a4&"ГzgV:\B&2LFT곹[cSB'RE*Mv0KT i/!=u "2G)NH t~B,~o&e͹ HqHWOW(Sm۹+#~2 ?Fz/=̹/L-2ۥ"ғ[]^+ˈ߱ໜ">Lѫ_Nԯ]ϩJ׿Od&-~=VJGυ˭Dvk˹0R p{Pk=-+D weLPfG v  CBA|{ޘ1iFf楡mRC@Ԇ#'ܵRc (oo]AEX@I29= )pQhn!à|7j)&\^ث+.ȱʯ_skUv BT 4dĝX[~!67/. Σ;1jCSoE*,j.<^!Ha%( A¼ݫz{xkGyԐ~T~=AIxH]E3Nc5BrZhL^/RWzRϬ|cTaq!4f&>!sa4+%șN}JAX fKݫ:xՌqY8J}lc[O1li}BFG0|@~(F}+J3 RDPO{[ 2[#f8u'Ó}"h4? 0Q1[ ֲI۪z{#C֦& 6@POj>{(9 {b[;jŪQ7bX+o9`,Ӹ}e-۩icǟS-e>JRv?8.?bQEQςY_ vIXf0~e\u/_Ђɣ_'6ӒG+#>3mRVo{y^DB'mMsLi7~sn7pO4&4~K]SġX< LJG3r@%VdXOc}>eqXǯ>ȟoSPAe_J3&~X>B}e|()"Ky:  RSz!g(M(yx`tbFuSy_ˢI$c鹊xKuu1Dk{LBlH uTacAYs Z{ptN7vAk6g 8Nyf "l=yZkGc.к:fvuh2KdYKD8=29WӦ`4PRBlU3ʓHvZW8tIƒ!^I:8F4aA"fjfp'^B.Sǧ.\չwOpB_|.-JЏXam5%`3;Nأ02}) (9[4g)Uc1G-\ōkFs˾5r/P{<%{yq;"8BVTlL|$8Hg,iucK ]eX)|(&y`̟x8@BM<"źKڷ*YSӮD$bR3"bm ryɛ { µb'Qþ-̓ ?fGD!3,l_ԫQ,%@ Uh|$̡/y;N;Pԁ|]TdK|TYz+ޛG!r ڶӟ`Lh@lJag{L.񟿺#'` KyCoR-Q呈kPo.pθ娴J"Ѿ978(aq5Crfh^5oiM0eMUkU7:h Ù·BN(;iS\pήЃϭ0$"wc {u=)D(oG*s9J$ac*zKEVSeɰoǕ5f .5aY|40L@+(i6M0̤I E,0?(4 .*a6"츟2ϒLJ`HگagL{ (r/Kmr;{-+*()4]kv0t\mi&k6O2+tx`oMKO ~l>I0$!cbl|I3j8}e,>S}C3ac}2 S,'h"Όd{=Bs89Φm`-;;PKTW>0e-e^u ܻ\dQ"yY+$ Ci2Ǽ=n :tT!NXSJ_a >ѢK| 2ҥ3E|Z<+<rbz#uŭR`. =髯K!1O$Ǥ4<>Ďm]t>wNy.y͌'9m'C4;Dh[Mwq6ya}cn![՞qyyyuw<i}Q5%ʓ$"YxM+N |4f}@P !NIu%% ʸ-(tnoki4ѨRlGc|PG,2+[&32o(Sĥ;i j>=lYyQxiBh?O+!A6?sd$jZX,#t0B@ 8n.7waOܲ"$D=I]hX YM >|)\m 9F;B쎬S`*;*Lȯۅ3P_z*6qk:E*-p(#+pV7 abIr8.ް@%mǞ oάdޘ]z?0DH{3`Ki`kc _߾]%39됨Lw=:"^}LO%KBͯ|\MJ݇ArX Fm|qϔ }{?&1UYulOʐB?K[WUa}7=M\t(~(>~=];i+痶Y%REVޞ&i10NV܀]6*٩P p*x5Վ`~0_BN>}}̄g-h R:$bp4?sڤo@qr9;ҔΉzDZc=ȣ)#$ 9ePLߞP3҅U9p#'v^/ߨ :޸Mc.y~J߭Kw}uqؕBījSE6B7ڽP{aT8E6YfqنvzW$c,~IGqXYϋ5>{ DlHMήkhk:}YL\NدFCφdW Ǽ~-Eqʛʙ$ (L_@om)48LMf|KL[ۏ&ft4lR9H=hn?XQ>`-DZPqHTXIdޢRŮytl~u}LVIog7T|Q{I1QC)?(1^8sΆ"bk~~K(DžƜ3@Z4q L-haL-qI}Pp'N@t鶄:XQmdDInj49 <7(*MLmGѬ̩{qrvPl>֕JG{WKwfX$񑅛EC]@2vz>߶Q3s_-R<V TV d쒶ۆ4}Q_AǶ-ck>2NDmrbG׊#fz G'Ep5EcׯbZx\s!2Ć3NFsDA3}z18J5=U◃p^R0)6c3]5x^ lr oݼc  ݕ"B|a wwS$G 90Y:`v,@ړ8-ׄl ScN\$EGMW`~og ¡<*E^VD%3(e55 I?TC4.Y;3#F ^전XpӠ.ʩ^Tk'$<& LYYE$_3yZɿ/8'N|0}1/ԟ.b`K>dfU[S=SBSD5#SPE[|wu7yY]I?\3y*dL˼njB}v-lW2^dZe5ݛ)[w bGk/`ᬽz,^e\;kMu[JG_w '_D8هOPM8JLprƳ&'f;n7IzSe?A3̇HɃmJ$R ˞5EWR:^ Q <Ӎc7̛6/_E Wi&8] f'D"`}];"R1x?nU*r"e"F c!0oq{Ѓ.x3تh) [ u cx;".E)aˉz(Vr,Gx+K2W=$S+/LZQf)tCYS5H#+zlt՗AJztBkKDF7σIڴjf{Z[3af8¸\1j]O8Lߤ I@tq'!GKf]#ҝwW dNFfMJzڟo] h٥yf+7? dƼ ec'3U@Tc6l QbOcp;>Y%/jچO](fi w7ΌwJ]&)@;:BlYsdo6r72^OC1z1-7!}hs[0ϴFo #BםJȭh-8 /n~kD*W r_̅Ҩ=)gV|y+ǯ_ȁ(Bmru2iPU Y+ "d1'R)?Bw]Ukw(o`,E|%ӷ{oǭrx_Zh4.m\%F2]#[}sجxwNےĊE9pK&݃~L]#5%E>yEƑA&GSk(Lr@?eNCUqdѦFvm;/xKHovY[,hx ŕ74֬e.C!76Xd68)dκç_evyqkŚf/΋ڸaAP? Õ1Ӧp <!6Ke?ƴD &$@N/W4uH3tN3bubJ\XA.9 d^}[G RKӖa#7g(V[ڕF/8j"ù0u9Qϭ V|}҅^m.(CVz/0\W@-:HNNR62ᢓdz`xŒE(3{)6X# icurUMѳ򁤬=͒b4]S- ~pxjUh(hLu0~_"9OkXbZ(;ams͸FWީl0XW4-jˀhy~6Z]Ӓ~A= H)=,S.3^gSŋv9R'hɨӂ@)믇RGX-4OPgAs躖&bB W\QqU4O4<%&˂4N%K _?ug8rP ]ٻ2`2/.r򮀽|2]G9"M16N!-KP1qIq$s`-s *gatϏ"o52N4T뢂?$:.;",S<Q01F$М*CK' Sl7wcF[C_N|ZnA% "|%/HʤYʰi-@RXIP *B#Y'$%t\?PΫ;zpbA~4d#r"Xc Z[YץX݌aFWA뷊s;Cb#۴wfo;kqhC ,4ƏJægLSx oӂK;=/Ŗf@mrq1.`[=]EZoZqϞD71=v/q`r. C.( o`npFh%%FR&m:CnIGPBt9W3PJiQup3P3ުyȵN"Y 3Q+xg&CYZL%j XG/VKzXuRF7"b,v1V6c1zު#fA%!k.:]2:'M|GshC&2{8O#plJNfWj@ܦPGWɁFk (2b<¥[xYap?ySgKq}x`n-`TM#kǧBsD.E_%GR]m[UcPH&j<*n!3V)Vcב o9dDPŌ~,)djc=dRx,wFBʾKL~g3m<Q^.Ai] ]h:&GlJkDQ;,%qXܤ7d&=sb#g &Su19:PrYR;]zl]یߝt(l{9T,:saJU pTPʖ,_uO6[iPA;PnCg<\b{Cc0rʉ~'c;GJx~VFMYPq=xMD0n'rg(n땣-Xc!OHߑaێLw~DɰlZ$h0T֔% L`3IҐe( SLB 3|hV:=sƾI$)؛f=q6^5S.T࠼y嗅Q!1ȟqmW[;ZPe A@b|rQBe-q+O$#=ڿ4SQ$ ߼깵WVm:جf‰3;ܠ$f<}: ~Ə5d,IU 9= rOHMbǷUxlZ0I}uw;B}16cF޿J3p P[Q&ERkL T1)bu.nFUN{4k ZdĂEo]HƾbJXY/Rc_0ߘT3o1< E<R"6eKR+NE5 e+_KRa-y5UCޅNR?9"EkL[HvZdU $ڸw-UGhi^_a,xϟCV"ؾ50qB7@f:kZCГ>|[2h/V&\EUxQa"}%T@{>j#nUZTtZ-=xiK SJR$2ZPԋb\"mCj :dR#Xێleۅ(hq Lɋu5m^85^|(1?9RшpBHU&`O@*PaQt69V<4PRh쫲ةOWK[#apߖxMd;O; ;o4oۮ8ڗ\1ni32n[|f|'V*|ӕҰS=XJ3N9 cSҏ/ co[N{]TK6=I+N4˗0&?F!Q0vk: IOo>mRԿ#X4!S ҵ`,܍u;>cq!ƻT)yp6s0Rڈ-Q/qdעql-ކ `!Y!xSD2GTzܮaOlU^oόY[mq L],Rޜ rơl`98}$T:fN $Cu0̓[CH4zVm]<88"#w E*Y%xRh~˚oƦ ϛ0ډ,grKBM%zE;nU-? =F:kZ=S[>6l `="+ &:/.?h>tOFoYv _'z 6k|q;X2}p[xg=@]nk^XNpZf4Qv3* @:j94u@^*q}$ag2}7!+g,gth$YC<([ u{ ".,?ێQu5Bc lrȩG N!;an~ .&@@tɷ?ڨ3'pԫ6d3{=JW[S4M"JP!21yWGC@^imӝ՞XgR'B6ሗW_<i*ߩWO'tnWF Bܖ'N2KE*?!&@d^ehc&xMSN֬b#oZ{a=Y^:UzC E>Uz FV.&e)!LՅ2"ej9j k=%H)) }քF]]1a,}v&)`Cp::yr?ԳSR(+?u4ӳ"㚀ipeH7a`B!ݑ{D[FT+oD6do3nUS #CR3a0}&-i[wk'GJytB ߄yL(NH4s'$<55&$o/:ם+\Y85"%kTcKy :&[qT݂*P/h09ڍs$mv%t쩷K99"\̘% +uj, ^k9)mBddjAc,Z)qu[[~R-t$p``eֽމBjZ Mj| [f$S5:ͳ\pe4zbkUtԥ fEE>ʄ{Oԛu1Bs^MUJymb:^ n\s[%YB!gͣyPC591tN4)rI"LUm$K¯@8;|RsRl~\dt†N)&:]p-ֲ0SPZcmބ{6t8}WŨ,N; ) "6ݥ9'Xou:D/Z <)+XNH^1Ī,."RuH_CұjY$ jVq~3 ~l+7jOiqo~ɐϐ1fbdd[AF:z:,]~'!\5E9LBtM7ObGu٢G8/?wX{wB6n,/~{ܦ4hYaŘ2`\ ?k؞ܔZW8yLC" Uaa!ZǴh(ۓX 76BҁlG;e&nxVs}_7phn۱c+;O7&BڃGLHK'38ЍvН26hNdV8p}+KeUӁ=B,'aN]{- z]ԴƠ8Pfu4{d-$ : uqGFћkz;A'#]I݃!z9ӪXUF,!zd78`ɒF2+3%: 38y)1A)٪E#(m)%sbdP*Px<ΊGq߄q]69w}IFXOF֤H.ŵd8cM\DgXӱ3.T:ƧX`ʀQݽ,B&1 a;wYX(B%g4zvS2ok^P4R)J\O%\ȴD q1hsJg%$<HL7Sm$"HW4\uP6^(Jmfhjx [pE]%E#lKȑ|K0-K>vJ;+Zn.e#t]>_W:gΛfCoPR":jL6SvjʝnOM~fpswAʮ9\);A:= 3jF62J^ɫCq@&ûp2v\YT;%kt<~r'/hgUgrE8f4zsvk% .$5x[ ,k^L64]]ph=ni8j⭯>+ǥ)a/ShvBG\q`U(xkeA|zT v %wj ۭtt,(z4p6 XJH,5MPL <+7mk˗K\qA`~!ׅY񧨁w2HT Ob;vѮ+ /m <վxI45o @Jz5Їa$'! 5B#owu,Q7сUkS WQP(ev[ q_X<3'ִIG ´+e!thR`&&^ZFh&!,Br#űL['{4W.fʾX01Ƞ"o8ah+.3 B|.{o#xn L?AfC{רDm+GVqQ<]|/3EOckDDZ`&ߨ|1gvstJ/5RC^=5aܸ;è#=tvK눙Lͱo9z/vVFEh0U+m֨n)jFB33ԕYnhǠ=Cpw}Yed_o:6|N(z?O%Bs0bۨ  V0RTڧ=A,0׶ܠ-ޠ<L@|`B"M[D+X\CMO%afaɏd# 4~50=mȸY,ߥ&?RH ͔i6װ~5shpz_!E;`^o' O`KllCѴ@FopRC_ wl/ |f/M wjq lA@=(GF vcD ]O_] lDJ̧ڹn6ؾ6aHß\SeJQqWOGb$Aj5Oޡ;0ӏ<=b ‰ UE~?znkڣOeT\y xVns:)Ras]tm ZnOY̥[_>LK_8uKא)ԗkPPUs0Y+N5qh {$CR I 7rB48d1WvL&S fgQVd;>le)yTY3κW\3r2QyOгU J];m$m=!W*B]7F-eA ,k\$XqB7emMJMFN1֩ Z Oe X3 L{X94SLϜ4SnN_@g Zys`҅g(]$ IlG$H/~TL$@,O(=/kk 0.3[iUHKžq߷W:uο.~X byADKgb*&U 6 :>ݼAnUAxfx!^h#Eޓus\EzDtSܷ-σRxaߏqqHtljS3Ӂ\ۦՔVq//h>vgt囒/ig;k`$bwnbf5_#w6H5zX CYjey%i.¤3=yzB ;?[orh ̑9%ǍW~9V:$^T =OH+a&&b.FDeaݕeubQppC>dUΓNOTYxLn@t/r*\ƢUx0ੳ8Q/`67[,v縸EG)7Ě[eNw,`<e+ Hp`hTޖC碌Ǟ&eCyq ѡ ~k@fyĴT)wG_|$īfkRA'Ya5} }]Ƹ4tb5,oJiΑ,R @!n"/@f;!9"6cI&ŌE"-7ajin}6 o)7{XiM)x ڣ;cB̸ExG]x EߒԩQ`]IFʬ1dyioT 6O%`9//}@0Syof Jfxςp- t3ym~A4?<}Ui]Tgh*5O4>u:^4m}E#]58IIUȚU9OlMXWK'֗/[쌄B!̞oqm;U 5u2940 x gϛSJ{ C~GHdhNѤQ6NRVR\7ĽKA^dګɸx7_Pɻ;yu"Ek%2._ع dւrc0)]a~rQ?89y>;h4) ՞n3bж:wo]z4΁*cy r'det)j6 kORn1Y4zZC-cGf&wa[C˗&sxK_ /5 M芧׸h~ZR 2̮L."iڣz=nK0`WzI K*~&>)jj%9! //y[)rWJι\rP\:FCIf>tVwGȀ*m :ˡq{3K1MbޝƯJNj)UW i4~B0_6V$p:dS z΢H8+jyϾד&2\h;7lCF⺌xX|ik3 zxTmkAcOME pl"S3o!J'P+㬛RGz3ҽW , 4I|в.I;R+EElG+4Fz&8K`9&S`[oSmkےQD%i{)fBJʞxn5 [^^DBʊ,.6\;Usl`ڥ5qӔB(f]FD?^1hҘ޽YTȿM-V5uv. Jߪߏ;ɱYV~h/x(Q (A6iEXgOIraOd`:%Zm\섿y6szY1?sR9%V?kGIgoOEL({/`EN2 ^/|=qaduAp"p N@|4WʞXA١.8<ָu9\k|ZŌyLw#g:["قݱmPD3\kԡe7J.:phv#MO"OT,5A5e~Ѿ"mzI"'E߃7{{S%Uw~6=餽w}<M+F9T,m@7j#8-Y'uW%5+4In煬ai-'ʝ~p=2ݒ] Obҋ<鷊pTSjB]-:nh2q:< QlM!n1q{Z#q<>wZAyQc$ݡ8PDɲtpxfO45e,e i~^ɂ)!2<;\թ o[KS{6Yέ)-?ٮ$꽪I}|QR!}GW ~vܷV"n@\,!4["騇\*Ojs-kΛp x]S38P0iS&~"ok|;oEDOoyOK|*^mqϾ6N=a#`s=2ϡ!',%od2좷½&Y&T[~(SH2w0P joh0 ^FIcu<%KROb'_]MRJf.ͷIĂo+Eҁ"Fygk'0/N)&_#3!G7p\f+$+dl5%(lAoB\c ɄZdʌKáyյ-,d:A`cw64=dnQeǧ󃲱~Rݻ+1ܲx[D,a]:EϒLΔ *bjQ|(NߕoZVq<"y%_Zpj9lk1X_ʏ6m[\3˰8'm \^J`ڻ[a{l`SP\AcS,é 7-i%%o7 jiVShUGٷ9,[y$iT&RhAEt(F!M/Skcw}?, "@iQ/P>)F % s3rdsҩ |NM[/ ,wV>hLq] _Ȱ2$S/{(#:Q?%%i0<'eXO.y *n#k=[i D'B.vbޞYk|.^#?ׯ8Gpg#ң@Hڅ>Dhzmk5 8 F\ 4˶g8׷q/˘S,7Ps1._͜Q F}գJxuOS-^bɍY!qÿާ[ĽŊd/fq-MsdD ƞ>9 B?p#Hw?~U+_[[4f;2 gN>-F` ]"?[ֲe*|YPnW麔6QPb-o-^*V ө{"͏o]\_y+E"YGqgJm tΤrtGGc,c&"G)+* #+ЍgoaGI`dNU0`䁛f%DC, "Q6Py){EU <@-+0!Wqv߱88D #s6AnɝAs HeY L.\ҙ߸1fM3 AEgJ4Dz=K&zWh,h~djc {H@ `qOZ$)R6 aBGL#T}%焬b)?8JdY_a.r\'Ӓ>ήD7oOX ( 5 uE2 l>߽c"?X AK 'dW+H2pr Ql2ڋ2 sUʫkn-PGrBjv +WЈ' 0aJFd`xHI IgiVcp,oMxLp⦶`E<0q 1wݱp12 N5,KRBuBSwzM`KTa) "Ne'j940)3Ŝ^Pp.Q#AM_4( Ξ:PqKlGLj& h+OgSXz޹XNÌ1( qwM.i{$l-O|sw0Skڻ@+׼ravN {Y8YY~S)Ium"?S__@`T˻yxWgS7<h2l\H.`ڻ?^8R]YQOךc=^g 64W*&Q^#=} v^͂: )=*׼= 2ZC$ܩ4\(XHл^@P5Enjf-ިvR 燲9 V+F<czSGrWcXJ:kSv0SiFϒUt^Y]ãDk^zTzy 광XK-%71*zmGI?B֪>>.}QZ1TLUT-{_ލckؽ} O∲!WC|$ $L. jbgͅ[\q=J5V/p s9ʉosx+e/XӐ0wka)j4 Ά>(mlRʜC-q=IϿGW;p!_K׎ !߇אw=)ƽ^UE Zc*'ydhoҒ܉.z2 #8BP M] b;72^j͏ƫ_}؀kɡ^CBIL kE<;m9elTiB{:G:vs"FXPϚVIwd0faG-6ůP@ۏPW¸*;~5=;0pf:_9O^?ǰi;y? AHvO3cCaE*Oq;*X!|(M |" jCNޡ%/BTq?mbքE9mz(38AwAp 7"xxp꘱I5 TO)y7@[_!sYI1*W)h+ %Ӝ#\;G2xH +[X7$#lO3' gCyzJy75\o V_a }-J RQkP$ybK 0STv+#JC~!{7Y*zsu,֚a踹|J d{g| !CZTJ$.=HҸu;whݵh<(s+BD%Zv78Ց6Kc5n!QY׬j~%L%_H*d*j8"%V:#s`/"w6_KyYnO")x,"* uv_;BL)|ԗ(ز " <~kf#s, {TVQəg/;y6˲l6 ݼ(+0 حt|.,H8L>3pI//Y`bv5gf;B0҄u 8xy-.f1`& 8'c%1܊a0S*J+ Z'ZДVOaگhSNB `lׇg}I73edK^ ,bb)MlaA 58qfP{k B{l#+xb9J_뤅UXMm׺Sr7OR:3.XL-$/=V71? yn~`Mx& ĚIi!il_L8aD{"AbZ\J ,#n/Tńr(4s"%\?:rInuDل}襈ғ5h#"BQDR  7 qrMHKnvÀurN"<Y%ݿkBܟ{Yek:~; t;ئ4%/7q)TvHnqyP߫=3ַU 2Ѕ[Ub/fv쌻R@vdB~QHUG|Ah: |7??SQ`%[d.^4zd[24o}UZ |(ݣSį Pf q xTAiE?H9=,*#wOዳL"jpYl+ݸ>ʖ:Tܷ/;5}YHiq$ 4@ OKX)ySB?7wokOfd䙼ȮC=Gr8M͆$/]ȄF) "n"KU茔ըyEhgN`t|.Q1Ez1}r6(np0!Fãxi!:lI3 @E$6`-W@|0uc(R\+O6jeYlTr2Pioœ> $?tdߵ#>#q,Pbh/͋;I='p`yLϼ{u`U _@?1{'~'yU TœDӋ8 zPp)8xJ7>5|7nBK>4N,a"Ք{j$)a_hEFqɝ;񃊊[VddI~TnŽ?r*凉i`_FdcŨfh}bf>O鎫_ѷkzT幩 ׶62q~kWbʓFGX3SI _N_d=s6DkQLfjCn"yu`!aa=j/GDKj̃S @ֳYsK ¨{w4$\ " v]agHί%uk1> 䗝[Han"-AoCŮ@)ӼQm$ʮ\Z]$aD(8Pj!/zayfa*7Z7}p&i+Lo病SU\3:ﱺ3mJ\7y32ƥ Hׂ<_hI?k6Dg%z;F› #hpg2@$ߜ~ET?8$DwT`s+|ai ~aoSHw~TPܜJD <'|(%MuVO”y./nnbVz==8qy3|A/9 uK"Ơ1:afe. %gU2 Ĺ5Mpr,Yr(b> "f;K=64 jJ 07~%ݩo؉Y*%暵"jh,SU@,p~܋`b43uّrSe釡"}7}~I|b֗>ǯ+rLJf4 G z`[ Qe>8D d~CzgD"-Y28|#5qb?XZ̠!FG~g.:lP:w]ѱx]K3%‹V_ha ̏rz>q%~h'R hP21D3n ow>A!-5QZ(gb8 6^ÂQ1Щ\1mGR _*E~ۨKk:#6Vtp6qS(\.kOh(aX*6RM\bYhߞTSʵq5Z85;hNJԭWw >(d'#^1 ܯa%'yGߛ0}6 E@A+ٗ:+ޅb:l36BB?kgԴĿ#/qX`&# Q7SGIoDM'aDq O;nB)j Map臌yZ.qY@e'ÏI~ YAjO'0 tdp:aN[MfwǓ;WoBB~(aCP=2'PɏU @d, O(#Ք@j5g6Kz9AKê$y C˷.!v͢-bz#ڲ$7wdD;#{W_`yO^ WҲ|ճPwQC%_cV -B4ȅqڬU$(q<odα7jNK endstream endobj 57 0 obj << /Length1 2050 /Length2 23030 /Length3 0 /Length 24332 /Filter /FlateDecode >> stream xڴeX˶5݃Cp84\wNpG>g݌)5G5nJREFaS1Pd Wٱ1*]llL,,()EFΖ ;1#g /`ƒ@ ߝcWŅ%{kn`WN<><5|FӯOζ"; 8󦵳:Y)w3{Jl9>-'^d-Ǯ) .ht./6UxjiQ6`]. ¦]\R~JL6>V6j*7yD'js6!Mi @^% }c1ôkj|2H>80a[W)l$OP` O^+i+j5eb n:ְCמ| _ *;_Π]pr!l7K[kfnT+ J M/%%L7[S_iLxVRٖ$_jFofԜ L+Nl;sim9ӀSxZo5 N"'nE1`N/1/~[0:߹jBzxnC'#%ǡn2M;N^נ-tZx[C8wp˛6}dJ=G2a>HGF!7phU/Wq:KԿ0dX8JyTگ1u:Gp/` b(hnӏ0D |:x?m6ȶgd斞a`Z;Bo_ErȪ,!NQ~Qx5_=I1Aw$BO.bSMTpcυm**+g(AI̐՜dZ5gba,A;c\j<(,-Ruf}Z9+"eY piV[R k&HsTw6bbﶰ\RWǘ8GK?_padanwv$' -x`S\&ߪ>{M\cݐ~]#յ,$9oԝ[~Q,&?Z+]ôaXɞR HۣmWf(2VXɴ_EgF4ꈎ.GzV'nc\lDW3Lw=J|"M٘pM\&yh_Gp2/ -"AȤ<8%VkG_f(? R-'%a{"l\*DŽGFIu Ւ/]썕tpx$Vyy#k{)~uhs׌ H7%I/Oc_=}8]4KPFeY)Fs}yCd6ޑBX`ʪit/0]9{Fo[.sr0uޖ#R4Y#J#&yPS -h/OG@;E?1e\Z^Gbi!Kp݈PS'430OK6ʍ[?.VV84in-tࢮ&2 Nvt Ӡ=~̂V$!Z82 }%ѮmUӸp.,2㡇Ƅuޯ _.gM~@0݅ҵm{2~ޯQHarL?R7'ZMx;6COXo +1Ka8<&G@M>lA2oڐE[ĮB zIy Iq '.TO\KJKBZ߳/mΕot&Qh(( ,[qQx)J C4Gr0ZwXZYIv^zu݈SVgp X3С7*q5"j)gɸl(M\R6 RM@N9ߠMSX) Ax?X0 E1/MA(/M-)>^Kn o/L8/)#TU)KVݵH^axjz=? -`GLfy]W6s@O:eg&FJLsmnNjapeFQ K뱡Llmjkezx4zOfIkfꃪLεQJ!TRCYS>]'a,[n3"`j{>|F0{h&t a_x{a%c\V˝t +S?CZP=]fL?+~N @ f-GÅyeJ l`#wQ61Ƕ9!=wLk-{ҷlF#ȨƄϿkڦnvAwl?E~H'WLu߅c!}"2]V9o U,{`@hL7sbxCwȵIc5.dѮ46H%*۵2`aRWذYUf@* 1ALC縪v&,O fQ*ڊvLbNtf2 d%rGaU3]Y K[4&RơqA!4 gn|1;XEp5%@9p H&yUAK}nxo㳅y!N" ?b W _Qܜ#{+؍4a!v?Lƕ$$\(* MO)"]:l|X?BCӄhm_ F&Mn UU긐!ϸz.AjU&઺BX* xe'VOxl8 ^z48zWy˂Ӂ ƉI4|<u^ )F\4Hz:=/v[V/ ,v@ v!"Ə6`]44yۣtxO遣jP2jiHL1建kq8Lo3ƁXu O;DHa*#(C~dL%j>߷i?Y@خL$<[Xr Ƙm-&aTUd&S yܩ^;o~\&Xh+ۅ>Z~ƠMhdzmz,?`"Gb-zmrL?#Y;G(nX3_t|[[fٱ\ &ZFr'a:a_j.z6yqmD E)3"Y%S{bHHʭV`%}WQ^NsW.-p̾S/穨o?HYNE"uZ䘎(bcS瞈N{)1yU@D|(3jf1<̟E)Ԭй%9/r[ЮyxSyt cIE";luj"XA:+o;!yzfy PٻjuSpbpdΧV`ow%}2Ow1Jثl`hxW㐩r#S\N퀣F(M9~4*,kަ ">'mW胠Wvtj ȓ$FC܏_`XQ$d- XC?TE8K( \zYݦR@GT{x֛:ok[ zǫUZR_ķ盜Y9zˆJ KN2Ç(TmdTT4:r6ـ;4#44den^G>6Rh_d@p +CMEz QB9R5/0kqBK ;B($ eѴʿ\: ,L-ңK(0K2+C]^ѠئCK"EY~vBz|#2̍Ǎ+-fck? fB n*W]>4_I[1?gi?,$̬O^EDX HI^^V}L 2n*4fB7FnUF=y "хEF0⅔+m'A xöʁAUf xAl?ĹqزMX[8 P.ͻ;$fhIr/J}wQSQY#;#lKT;@l %$%tY ".uoTc/oC14^8|}N~VFzJnR1QƧob:ܻ詢-|}(kM,s\n{xt y#K*Bi*,fAN BsޏΔym ~1aJ6kY+8.c R]{xO~ @D^br6a!!*G#9VF51c5xB޽q&Bu"|ƯqtWPE]D</4ү{6m~cBg;,oU&+}0N ij=bt OT7F*Ȓ+{F?-^nZVp?70& u]u5a8V#/E.egWmw4DɅ.E}JVrd5ۘ!' ׏'Y=a{𿼔zgZ}. H.q|fi9'agiHeWV[i7zף"3r\+" D2/4a .%(.[ˢ? Y}[ur㧦Zӕ>)ZZ4G|%W84^W%(BVc5Q!zr=7|kGvvrVpLߗ^}/9ΧH7u^H5†a3vDqwywlO#NNeJ= C@6FO-z_NJuBIfvǓtZ `Rιc ʏ=$x =7w߂<d`Ym:CQpuB2ĿԦu/qq,~_;ǸXYPivy%RY^e yW GJt &w$29OFR zy)ו yMz;x(Kc-kGORR퉭)ъ֢VM}$ϣ`}<’ +7Ψ)ׯtxs2ɻSHoxTkE:DzXgkQC_Рh`ߚ~(E3QA}\e<,m ׯsNoV/G?,.veN gkyN1VtƵ* â 93ޏ1g PjsGmASuˬ!AqoA l]w3ɋx⽀' {Vs' _~>/Cs6KԦrfbRk|Yu>HJ`Vxs6 ~ ɇ/sXe]i"١6mv2Wν.d~دЗS:M5;m-g~ZZk_A/$CIe}lE&}'䫳ph}LZS [ Rcg;\ kP`b,PtopWqҤ2 C\%&ݟ;#~S>,qΩ@XtWu&#)$nDCl> 'Jߺ)|AO5 t0&kT^h+=cGr:5Q@t{I ?>JO=qD͙u]iџC;b; ~0 g}u62a5̱eeS(Ԣm%wOlO͇%+&'2C0%]lGV+-J[NOR츝c)s3(Zi{)L%sU8TO; Kucj%`c;*2nr?w*s>"{mDt#>by1IL6 ;QҋpͲ o&kno7xy b7~js )mNK>&ѳ`JI[r s6;ߵ W#;VD(;v޻< -KLK6w Ln&?}-8;Lzݒ^wrqnW+*N [xOܼǷX<a9:a*w!m݉ErdC XxsYȪf_wnUk^ 9,[ o[0_ug炌Q3?!wW6+ U wJ>&f?2[K\T xM) o4[e YjuXll=)=_wX';f4^ö6g+i-ej\4('ޖ=IVc^J_`V^q!TUUjIDA 5S-"lKo=RXm/PE!ew+MO >3G RCivq=ZD1mDS!M&Tݓ\p=tFzaGeq3D7Okk,C%٥O`hmw!1*ѺM#{qqiZ+v0ҢX y}Sk{.#rvD }SJ Oؔkv7\z<i3L (Ssy">=28oWn@āIGc/Bǰ:^C^&R>f7.#}P-_: NOb ܂Mt Mh[@̙>W P'H4ф_ɷS \IRǎ2q?< |RI:w.TDg!+.ȲSҗo!"POJ7AJ9]KaAy($ЗN+L:^;oHN)=uΙX[C6Aq<ĨO|0ǡk;yzEt8j"3m>[RWl^Bc0'275~m67`7S~mM^9;l=Rl=[9uk3b]CRL: Z;=dcY ۡB/!!AfdyJީ[B極uUEXim(FE[g%vAͲ3bcȈSrhdAd~?%n'LG{/ʊr+cqQ*.\' T;8"C_~"HFs?⢱PD͹4 4~CF `B{U$ŤP:Y`pR:\#,f Z~ݔI4e7RvKVFP1O?t؀'}:7Z8}l&i}Iw<8$Yٝ{t*<f sZ<$V@pG}PIR[#Vc+6rպ ^֪27 JF3z] ǯ҆(F'uuNKaeBdJ,*h#A;ckͲ"Է\r)¡eGlµhx5idӔoC{2az& k[AEaoDJӵp)$&HQV5 +ˑ ɧ م??/]KcRS,bͻ}Q m|l Ž_l^"Pj%va8=6t 4X&jZxr >Vh]tHK6kЮr@' ̛&\{ R5=%0ۣix9'$K ؇V:mz}p׮߄9sjK'OJ'G+Nx8s{P/=,M?{'3e1[4rzr7=*f[ܡ1VRU2a6kVD _O0gгAuAY]Vq|ӦA mkg.YsWp$ DL6?,z?!i?Ώƥ"=O-~'Ս4˾ܧKԯcmz旬2.##Nc`,G.fTPE$ܰ &bٿɷ7cIPf& vGwv7m%ōZ͉yfjn5ld`M)&XMʎ9/\t܌e)Q=k4 E4fwZ C%k1ZXYe~^/ X+Oj`Q uѢ4b[.l^_BZmI)-U82Tɼp(V`fZ.+hKm5̎ٵ&L4.2oߥb}PxjiF0踯z! 3 XMbklp˵2qgp/0&! Qȹ5It }v6(H[j VGȎ@ /SMo\7l^\f1D,ycƾx40>oCBdչ ~ĺNv*۪}EE= A)W(yv(gA (!&J/R#':ΠUcLJCϙ.p2$\z}D1Ә+_M ~2V4J ŗ猑 Km;:|^ЙM7RNI#QN7>梈˴%Z؟FͧiΛ| K7Y_C^AZ!Qzyv@4ەJw27DX7[ ͒1(j~L><5kAdUMJ0Q|*o ¶= =5tF.[3L0!V-Pxq3Zb9%XUYZːF]FXGwRz^`#8ܽ0{PkbH\)yn1woB9\tEnr+ xCjńMz+ 2 hkxpȍ>Rt91Q,gr a^qkD~]}lxiF;< dlUb7ǫ)4z}q1-MVǔ9+xew5ijg}N_mQu<i}hy+Jn*inf( b'9IFq(+< ɻ 2+@6?aI t15y=/gzr3a_!E8{$'vȮ[fڭ˖~ˍErd9A`jksXpTjP h^])t^δ0|hEȔˋϷ춘%ԧ]s˭i^X|HυBSar`ት\*|S dU3~~4\\}JtZR0Ϟs ݜv_:Rem+W9x6\ZE?8͝N&ͳdZzG=Pj:,߂HՐd$ WkZr?UL-Ѡ&"`2-#S8{yXWwaꓥ1@BZo,<9gX.^EBkٓӏI& FCe8{10IQg|}b8 7ff{}LUɷ?!=jK՟mlG/x8pN 8)PjnDZ[lc~>`mm ioGqT=N$ S۾ތ*P"rJ4Ƴsg7vYm7l&`EoY;Ye:kϿF-!,KvZc)lSM\^BYG-גж>N[۝,Aˏx=ىʪAEq5JyXDG9qdFC>qK$y6 lp cg}sz1t#Bb߆ cxs-^EGmw4~%E|K%YgE<*2A(gWV Đ7]]v%ЗmQ\;DI(/B O);$r*`=mIb̺s K鞝冸u~E/?@.8ӺT\˭Et<+~Mx}~)_lGY,J?h@qg9ce(̏Ssɍp}/-G$?5,GL̜V@+^DY.Q@_w@X%D칡p #8lsJyS 5pw QàT4=;6Jr*Iu_PGHmNy'Rɧ~bakԣ~7*U!+Ko,'Co ,oA3H#ϽƏOrII-U h,v6B~?OcCi5Pd@ F\]=F3(]箖u./Z fIڊQ,āSa+!C` l/!ZHEӍ6֚2j;5^/ i&p>B$h`fk۫їdtBZ1%;A4)'-dc1riš2̉h`^FXŠ5tДzM0i4$Թ[#a0AiMqb/hvV]SmVΉڂb%*ƙ\9|jv葠.dqBu~xN٥fE9f5:"DҾo!Fi{nu ^mZ Ո/"^Ղ2U8& b3#"b.0A^9V&ŻZB̏5I>{ewypl/+&_ƸpVC3u?L[wrbH[ۍ mS:V\B~#}m6[BCn70-t*¤.sa[9k;9W)\ 0 g:i\K8tҥc2:V !j "l50_8 ñMݽ P]`/nǕ!b#i`Xc*>[{$D\)>-5-}rEW8vݏSD/sPNs*7%pNTrye5 E UA W&ñq@"sgJ$ PDbm~[w)-L0蒌uoq8;M&Fz-Yu &H T4 ' S'TTƫ^K35 a}x>'݉oOܼ\aIy-^~۰ `lǨm!?:mr0d ,/ShFQշ)mKnޜBv}m/ i=|n8v?ѥh,͚z! ̲ukf n,b.e/,Fէ,Y),nRoYR.vE&,Py{csD01XCreS_0Y‰U]XmH=ȋ vbg}|r@h4#ND Si 4!́$>$ZE pK97O%Tbg.~Kҗ D*:q]T;o:_lLBv@j<@HQ(L#kNvL ѽE;(\?E%W]% -7iHPL~;E[ (eJ,%)pkNdz78{<fm \pp? փ@ɰeM`B 7Z7PF 7ug5RBMlɂFy*1IbY1csk :BKa]ge"nU>qN |C(:%{wkG.L_nS <7z>6Gib6gț}6T  CߊM,}nuE4ܿGߍ'6Lh781W }[el̺\85:gT|O'efR7Bd;wl4PcN |њP ~}ae_Dѧ#ǜ$9˳egJsS&fTѪFtզ+c`&A`/@Kk" @Ѕ*2A1uTJ4Sˢ0TƉi5{Jh zTq)APɯmn^%HQ %P'LaarZ,݌'\KK? N&累UHTIS.|~WR/ ͎>:qq*]%xnYy 4Q/jTLor) &8%\c'%`e,Uwd/pew`Vۨwʚ5a{<yI'\iz<ϽbINGaq%%yݾJ. ұ*Z'^+}ɣvR3vGrIbR9 735_F]~ tl'T :\f;Sxu?mdZbeu*Q[t(q1t3a7ڽ5CQgF=:p TC[a߽Y*Yݓ֤0>l4zVfTp팒Ќ%2µI;)U݋5B=D^x^ %㉡š\EId, ω[IW9zZD^8s7i?|`ݜOqFF}Ԭ+?6kWF UHtνyJd <"<% $'ErJ%bO04ZXK' &_W;/Iv~S*]jnm(Wq+ߑv=oz] !2"(4M`$p)]-3^R9w}#&gڷf#Odi+fkXgTIжr!ܱ? =|}s(ߋU u;y)c&m֡:.SEJ(j]`vmF=~Fe; ,UFDm) EZ;>R6[ٌ 8hdp,}m}lŅaL:1J j:pN}>l%+'\Ȝ> <*1r6ٲ?c[&L 7L̿ՠqTmBy -@u[FQ *(e(媯1[h4=E$ il ʋ:nQq'-WT(m\,-j$BN؅ZnI]'eh?ь[LÀb*Jg\pp/ "v>.LLf}d|R/|qģDŽ]okG!yN8.|1kLh8^WxI$FϩI2C7|i1VIJSnf~WJ1XK*$f:mWq sRi|lבļ J\^G)* fj>ϼ:84wUEJQY7z'^v$˹Jlׅ570ꈛG@P;mX):^ST,agOXr7R -J5J$?r l<;7QAc3T e-E@>סȏ.)Xp#x/rYK3>79^lrKr #2'ug02$j5إO4Q.QdS_{" 8F)IF$̙5Ӷ=rHX4 okCe{Xs*Wn/<"<*ӕ`&^,&<N rb0U.LU}+]?ƽݕ)SC&`!t4*^E >Qߐ '8_z2).&V"]Q%3wlJ1 R82K]GB^omx.έCk.7_F>քc9H k |f3X_ǃǩ\2틽{{BTDR^蜍 `<,>,vԭՠ9gF%|Uf?zZFqoĖGƄ$QDH4MF&/1$I-c%*-ܩrF̯ Zr"C,{ )M'NDxZQ'֒cT/Dz̡P[%/Y|K U "\yߺu8KV :z}#;06&U٬Rjel9AbPVTu.Ngv@B KG*^ b| HԸMgLJǟ*U:DfU*qG\dҢtsj WZ.5ETrKOJYGVM EQ[NF с',_i&CRLLzJj4 ݉bg\}P=ڶ'l2ק _0AS(F +ewv*̄%WO͘=[q}kzכ4#3U~7d}޾*@_f;*Cnr!m-+v CB颟Zσ.hqt5Se[-S)!$Y/j G*{y0r>5a(w^4s);i`H7B#H~ BbIFiD!yɘJՙY~_Уfjh#ֽ,Oy3҅ՑcEdl-*s.8V0fY2.o7bW)wRGeȰdP;A8lٗF)7V}( qt/3K~Hߕ;ɳ G/>5a?_ڵ-T E=0W-ҡ%PTM Nc }dZgS s5@xkhvx*D o+T`- U*:?T"7X~A8Qw+R.>74"hq9,Z1l[OtNh? j:݇62g=JLU*d/s5T~X,7%&)lQ#%D4Fg$| ȁ-H0 D¥+"A⟙ɰn(Y^Xc 6^i*5 օ}|*W0lSTccL͝Fc [||b]e< R4da>RtَO+#GnmaRp|:-(QL CvUlƏv`윊Ov3Ƀ+r`N"vxpڬ$Û29SD}cTjn-y'<$\|p%B-΃ذqҦ(9#h֌(` ?TW>h(2J]XҎtvnS:=h%b:4sIy !]&"uq.*볼l0$|pGKb.ձl#m^\]XH<|R3aB.$x+Axb.jZ#JHL|P&~2BEFI$kaܖ;Ȓ1 /Z5xaA;_j6Q Rc56vo9؝7L 8s@(@Sj>~e \r{is [9l!A{hd`u@$5gJ9vD "a?Q^8ns'F;Kk\mI9Ah;0 te}Ek9o5u- rYj]Zi"0\iK'Yi  |AI߅ҵ2*zuqYQBBiK.e5>g&+ynΚ_T(emc~u}0]> endstream endobj 59 0 obj << /Length1 1866 /Length2 22655 /Length3 0 /Length 23806 /Filter /FlateDecode >> stream xڴeX۶6 Npwww-łC`Bqw(R8wG:{~W=|s' :9( r31@&l)֑^Θj'ē&4.)&8Pc۽hI6 cK"%6ŬEiɿP>'_sKk|+ @sH!}7e AYZ{9&,g; %4¥H(eYPQy]hZi%s*B 5"c׆J*_+ /`j9E&/08o64ə ؊'ڬzHɁ_dj[ Zę&=#9۠ ީ[TRJP8@-b herTL<4HDR,=t)/ɀm y48~$/5(`jy5Ί($xk_{ X] ً}HסG!iW=ӎg/ïk,R?c=HRe0D8^F3qU׻FAIH.'?aWbsUNN>N"7}fMGZҚ&ІΌ>Ѓe>K77 'ӕû–gZvdP~W#v&AɼcYʡ֖*CXyʆߏ[>yntxb^l&;zO9lug~-K#L/CUrvK@ڲ!HQ]͔ŌR"$.p0tTi-OL̂D _f@u7guGB٨e tpG5g,vl .JF]EfuX}Ch(MN;Fbd[k~IZi DXhXflt ],t[B`gQ@"i\) s_֖ o3;QFUxyV  DZ2+N¾ʩ57+<,:kr綃x{fOѨ^uJ`kNivwv%Մ#3fhDwlmi6~c$כiiL|wb-0lCoD_Pymޒᛓ<7IHbjA|\&IQOLq-YLG^/p\bk-C_Inl⦢m'U X3?>m{<_mjDP0%pp8yO@`F$XJ 5$uhQ?9~PD-$tlŸnVu;ȰEw4|_7󚶹8苼ErUID~nݛT[hh^XE6ƹ։#'"X8:00f+Iw3n *Z+:OeZ2<|ǽ1؃4xsBI G Mݢ7?*Y΀NzǻY 7 AejrD=TJ_[˨4H(] LÉf ҵ ̑uT0gKZU 鳴~YSgi/w!Y J$a(65O>Pjx^iP87][&C&L%A8%Ԋx4ZU6pde" Bl YHCRz '&ZV Xj#<?iF4?e,3%#P +Óz= ់[~ݟ!:>)Vm<_fhQEHk|JɣrY=n) ~; 9^|[Mc_ Ǝ)qDI= %#Zym#{yMXv仉׷?{ wv壽3)hJJrTbJ3FQַ+N3fWя_1V>T * (;peL&J2?`fM6Ufڢm!>pCr*fZZWcyC1 HA? ٚ-;Ƿ tΦ!5 uv~,h7xn6Jkgzg=9TѹH'ij2, Lp/Tfư6QVpha̋uh0p~ϺPy8&ֆ[j;9bb$Cf% c,Mw[YS)??X#oLWФHN%V-ͳPuDU/h9qwʨ[c91MܺsUhpmaj$'dTkN[_IQQ+pnױ'O̽;~)<hHdr(BCeE|PmkQ_OӖ[GS*4iR7.%NZuM`l>[+$pd:z%HpoXs(KA̫0Fch`D2}BzR4.i'iz@kyVm~^|؏.Z1Zfl=zh|=?9db&쎳Iݾ(tWg]S%;two&je06Dob0826PR%:N}HgZm^k[JV$`8TtOlZ/@w_T>2 Gy҄M_ !K޲x 9zu}6^A l-&|R}S+EI4YELZfɅV+ ՚YY 쮨5odпiIZ("ng\r.>Tad9 'vNvDvЧ9RSCsDAiWaæ[z%f#R;K?d1|NJt3%/lI\q@&)rew!جJCbfq8blpAI4Jj|}A;,͹=9Ԡ5>.#8h L- CCm? t<ݬ*o:ols䀾u|yS0S2n!L9lCt"i.^Dz6gxRy Мh5sQG() 'Y{A6Ykr2WQN`W=-' 5P0ҰGK#{W?L, yMmu5^&jނ<_ҚD$2uFQ^|QJfX}t*i{\ %pghr9IsMxL*EJR/|P&>#Cr4XqQ7kU WY.D]ֆx#:{l7bcD1N 9|tio v&|eT.'#wץrPnpg\oVTf~6}"uR*Dw3jȟ%0h;D2gmoes\wrCz,TcwS&ȳݚT&ǮsV$Z^{5ݚXZr(]{x7lld~]]Vɜ( Gaܭ 9l,t;s|VlW4 9JV,ǣ٭@JӉP~m)E}#׏c߄4Ɲi>ʄDDxA.D4FI{-e\ʱml\հ%}"!/z~2>:`Q&Itm rL.VQ\ŵ9Gejwq`ZRE$oem}i jXj]RWT6*ɒF/;+a$" d Ihbo|8o9c!'.Z0 aVnmko6~!?h& a]o1X5HhL`BQ]±=!G&O7'c[́/2 IOqyҔR$hH9#蝻$#&"" 7"Ĺ,#ÃӨOd!aS=ӝ#9 I'=@A )aEܲ+s.gʓ:[9pd1U7t9d0*]#.1ה|ޖ=1rV_љ JU _]¼/B?h7@l?jjO)Z+.iѲaPV{OEw/f S"<#,"،CCֳC|CAIg ITu:^OTCM%c+,m)8Pf!g,{4>EoںgMa~/j_ R(l\ |lh/XPC~j*gk%Q.Wpȑ4霜 WN>$< %2UZv OnlYOAYO{d00helG /ͥ"Gvcylf՞q#xX)QJ(I0.׉KÄ`ƫ])0a#dh@)F/Z;5;dj YqFydeM;\i-GoK)H}] ȕ;#s? nt?!r/Ϣnb5۷ 9Mv/Bw֤WZ Uh~kQ c|5jǘR('Ƙb[3L}1Mg $AcǒRLrԞ;}l}GӁ=ɧrF#j(-?L<4O Nak–- ]am,^`$CZ&XxO<<2U Zhu=vTvޗd1.ʷًgB)U=fsGuΞXj h^D,iIƸI4THBD>k\tH_sB`]|Vk, "Zs||#4J:T^>ߩqmr9,:ĵUR纐*>p!n_NQTG ?Պ&Y\9\ 6!~3{=Idhfz;AztL&{D8Sgi!'*d-.B2܅oG ϵHkbD, d{aGBDn".%9 y" jb傈0 e<Tt[__ L;- eҿb.lg:-ۂRd((/?M$ zTk@[ːth]6ny_Yp ۇz2>+K˸HgM&1NRʃ^ p,7uE  D6;ah7=ďMe6a(5'~{#h4_}R(4}* k&x܉љClO[:SIo{fNt1ٕ l${auU>,JR-f&dũIea?BXPc"QWҍ*3MXG?WZ"vlr*Xr쾴^e;-: Oy0M,Je|W9Ib@*ٿJ u-Yi2}-A:evĊ́?Y5TFGktx*h,w`}۩ avYoHR%pVźkw*kkq/==~:Ueyd58?M:@<,{7./LLԕxAfBeBIe=6w+TZH 6 ةz*`l5R+JJ3rza֨*R @҃׆UޑH*f vPaL; ؑCbn?w[ XS20XzBڮY<&C('4hIrp;יr&@&P.:"}4A 8|9UM$l]b$2M~=ȸc‚7{(0Mu`tH0T@[q;?p%FO=Xiω=_"{MP2EMHEľD;8I -i;cT {Ea4g/tN;NxZl<,w!9 CuB=z~7eTN]@9KKԆrʻ{C}!LPLUUjc [_c.1c&^PoYr3ŗzTI $.lOVv886zvyg,[xwӫ ;&7?j,);+t$P#ݿ3kVѫ`%{m2PxF0vi'}5]Ru#M,-c` :kё{'fgd̾.q~=MUjG8(F5K8Vs~A;eQдxtWHN_^*S7ίy٦W*eh #ʹG~13Ux]"c?w`mF/BE28-r9}tA&D[O'"& 1mK#Y'ad.r 'D%BwSَ72[47/Ar%(C`(&YF޳Cx.fOKdiGw2%9/B[p6UN1bXhV6l)"v|3*4DFANO+%i#HV"SB'_0+%#Vu+)Jל+~Sm,69  L?*CnOlnE5ӗ̴} hT`?bÃd_5܃4BS6^Z>%TTF^K ڿiC7 SrB~>Έ Siȋ'j)Q-t|Tn?9-O;Ԗ`ZU_pyt $}]>=_CRŅJ{2yeQbu7Ђ!*5#Ed#8u #G͍1{R g21u<d'yv(axF /LB(tз_fgeA?vYE|r twR|0ŰC [(Ep8}d@D{3m`:VGЉntm~f328G%$weAL p4V GЗ[rHx?*:f6깾4^;[R >4S9v*NeY+ >qXJ{vbyž\u;0\v^bxu4Q˖ku"ė>6hDj}fh;NP|BCCSYS7" 7-}f<&7DR/*v %s9(k'@Q!ʧQehz Bg=l=(E5_Jbw{kw|r#ok"eLѧu]#/NQiQ_z u>aJb;ыYH0-E_ ʦO[a'_1ԫ4SlPK[.VFj2?s\'qbsxAyC Ү=Jt>bg&~,~,Ϝ*qPH8ׅcdp{`XeNfSh5\~Oz:n3[x/Ē')Dg NRҡjwFcÅ+(_ SbKJفpq<Ě[J>0K(Hd{iU EBg^J3OYLQ`}82ePkfvg/q+Ldb^+O A^36v(i\VF137E,c 8,t`UX ]V`akx|P_$$%xfp\r?HQ^A XuZj};7I~}g{a]W[#9-[r\dݣ\/vO79#b^x 5%&-!0FQĶvr!ݥpxQRH= Pk@¡3Bl1PXmʳ0ii8g)SS+~fvV5ɕ&(3L38^~(/rv4h6bݮ}o{Z1f#?^I'/gUJMcyYJǨo/ֆ,3r߹bzɷ>\i F{~)͕_LfkYO/%w7>FM0JYθ3|KgexOaDƓ\ve#RCyZ2٥Юl|H\څRU ނ>[~豚Zd[[ 6*fߩZ3Ǝk@sUvzʔZ\-,L.5_1H1[ } ]SVQ-6/='ǴiBgF+Ge]; ~_B9s 8"q!XXL)prp« ^&\G.tFaXBU[\ڀͤWOEHNL!)`"hkk(#ru~tIݏBN3 qOr߲F1_P*;jjOu #aشF8X+^.C V:,)`sc㢆bdY+ {[n7GJD`l_c5*yg/~-:s_d񗧦!" Vߝ@-eϵvR Kw :d b*4ㄎ*i~Fޛ^^y@<'ƫsPC4/mwX|w3?Lڟ~dX#E}nc~?c[곕'neß` 9j{RBz%@)x5 y:^T];R;5:գ; pwU4+A57PR':*JꛫY~Y7]r`&r? E:+-[P_S^#2SPѯw>I[{>ƈIߋb{R$:)q?QIenDbG֯ NL̏>iʉKx |MsCrn"rT-2[wvNeWAJ cZΚ;bړárV8z|\oe*V~m' 8%4KkyR(8]ɶfVeVb{w>fjzLbɼotk&T0Ն83C"ߚe_#Q0Dl^o!i>JSdwL;Ŋ"a!W|Ak>Y6\-bqGBwKBV88ԥM!Gf2Xedxux+|h(fB@?Ŕ=ZiMNbEs#hHR-$RRI:$O ްc@~ZG=tb}`ڶ6Y0C}hY#A zߙ\/UG4iTZW7Rvq\ ӣw:_>P%"l_L˔5Bg(9*W #7o3[LbcZ_eL%iKkm9L~xeaw81}O:4L3JN2%OYܗ gVd<"Z$vț UN4G7.p/ֶ"_0KPu- -v( ?NLا^THuAi<,{G 2J`ntzTIW/ +kU?{-Zl^J5AZ͏E(‰#?=Fs5|1Q*#=mYb|5tg q4.H-}匌k$buqB0K$ocT\؉vRJqvjXм@NI~/XqhqV?v&vKB ,%av+3ch؂  `O[4J%j%3:!.޹gVD\/ EԻٓ,JKc.ƻ% al:r:[ݣft}4UG ýH:B/:;'%J੩u3G=9,>/3]O~]Y8!# ^BKYfzS ~!U8'%[%se}HkBՏ 0<^3NUSs1\!]`7*y*Ecu z2"P[xcjΥZ3Z*\KuöʇgǨMt"3qZ3ɨ)"qfƴ䢭x€7ӄ6-~c|=OONj mSmGTCL4*_\sKOԶ{Q"q}Qw:O&{HMuclߛ)2Oʫ%&m D8Ҫɹt-`'69XϿm6X8;!Raz'Tb'sl+4C߽!$o'K|L`)fvl$7x!a1T>!՜󯜚R.+=/z [iմ%Nbeù7TvTkZQK4+ӝqdgMf]GM-anMNҽ} :;Oƽ5s[AN?rbIk2uAGN\&.i*+p!$(e5&:-jx͕|d#n c*>u4o _noq =^=+ajnMptJmn,%(`Ŗn,cJGFm[w@qZ۠s>[ Aל58ojڢ pWC4.ڒts 4wf;ئߕhR9)O2 l&Uh}):5! E"rCr7qЉDs|m sGk_)a2mA;ƟF$gK1i^`u*m4wcPEƩS}] ~־yq=1Xdd$̝5m8"R8~ZEwԵ 6BMxD4r[^ϓ6Bx3|\7ușbamϫ{B~vKz+EdwM{?~O QoĄcݸ@DZZV  }qz>t&f[$.} xwqHʶJ1A~({H3be׵KY>D.b_zad#REV4 ZȘ㐴P~HPKOw WL*xn9e?`D'!u2Lfj.9¡%N ?)9ܽ-7=˗0͘UA Q>j4ܼ /]k$^q4Qv5Z|su^kw&`rȺb3>%_75*O'mDޚw U(A0UiȹgTpb3+,;9#MۥY@ݼjZ&<ԟuu5(RJE~V5O3tswUb>ФO+U.>65 )BkTB$*ê-U5hWVxƬfI}L II$ƣ_ ʏ#U3jOV&$2%YA-Qε 5IPAS-PGײtwɁLg NYW7_PM71~a'Ax ZС{L%Hora/9Y.~mUK*DH&#D?+;ʭ\xW.l kZWW]pjUJs߃SRT!,t5qֶRm: Վce-;+VFҧU.z|)z)\'WQ %LkΗ6(:ԯʃFsY.KibhU/G2gm]LIdL=Fɚk ?41@45u ɢFU'3enj,f])߶q1; oirg>y31E݅8ז7 ;(=sYǓ!s;$'[ʗ?[2(k|R/-gV寘k`*Dۯ4FF9?3ONͅ/1[%;>fVsŀ4k%ҲXM/Mes@I&()/Lmù:EMV(l4N5T~)1PKQD44Jj&6'l'۶mNm۶1m}%(qS&rC8rxɋ]?a6<-ԈaDdrՓ;ݾ;}|:S"_T8sSPDg^t[4|O(S5Am'0A‘ؒ]TR&:gb߷l9wbƢ|/I"y{!/鈨詗'ol :Ycb>Fq vHӯfqOki,{~ NJᛎ<)r{,[kQT6ѭ? +1|ybz}) {6[S+ِ2ޛ3B4ʡ*P1W4:B?`}_H$N< rl|gj?OFMgdJs{~,2D9kgK?P|;"q6):/پ4<6APȑ}f)L4lKh#^ 黊xPhl#5YV{ԈmA0dg9<S*MdڡNqyaR]!LH|wYu `|OYwu[!|RYz}w԰/ĦY)2Jհ?%Uo)sOx ǥngnl]IۮrGV,Fg@NŦ?s e730bsaO$K/)^#..V] L+?5gh`Xz-">QBҴ(ܝc*؎1Z@X!TKc\*PZdH9$1?ۏ}Φ4%!t5K׳vCegal'2ھ- = Y(wj,0pC7 jt[LUJ JlNn+"LE-k|ё~w']]5,2nVDieQcJ:s&db6I-h HpmPqPnb6 A^K,_|"4${=r}]\ 41~kWzWۺ ׉9@f89tLkw^<% zߩl"K:V|1S8TM AL=ѺLI~]GqeTrֽVXGa8[ˆ3,(SUGN̡L-|Tuo\7zKqSj"ShOWaFvj i詻\& f37?ʅ7߆Sk-ROZCU0\yQaӝg%vsXvq/9VdE b}#¿]}Z\݋hZf9\4uz暧\ H[ ;5];X+,#0 wc =DU'q^އ1 9$^sOjL->j_ūYﶔB gsRz;kzS]F]511OSvgKr{ v>sZ:^ b:4<)Ru|r8##)=ޜI JI6~kUz4 Y;gקЯ^rţ7]Z CwBEKHα4g^zm>|D9ِ28=db֗*7lg5eņy,ߙ̕UVgC:mv+b$}Fn16Lk8 "Im,R[?-79[/s-&,O,d ? |ł4jsZpvNL"Ymj-Z,KzMFA,BB+QrpO-&;Q NK"Ac, 3JmF@32jbGfOH`~vtgDʉe-27TjhzyonD3ߝ!e*a(ƀP_Ĥ$KEop9oIz2vDH}3^wH]!m iԧ56\-L%,JX 4R1#dKJǡ ا.6IJ _8ŜdB{uD>?[`FAtmhef:og6h}f a'9f2rAL!5A+PuVkos8U~+F?f|̬EaRks,ݑ%+ZY(%Ζ#0oHl ۃW- B]{ՓZEfB|zYFRi-R̭t]&0l+y9Qx`kC~R֪_1h3c=t9j텴t(~ 4Y:õ!2A c,‚ F$?q}a4AhҲ3#ۡeinOGY_NTDpSFR>fzMpʜ{2i2b->ٚ[\ dڜŽ& WH9\MMQƠc5/XC?Ot,2eȒ&l#ۄ;if;ܳ",$U~WϮ I<+ +/4$pz+P*N#97 o3U#5a yOY`!|ց9ai 9XX 6q e^y衝ՙj\v&JJy_8|]"Op:ZǒOJ'^=!FAwijPW 7M+41 " t; iJOlxø-qC/.z=f;e2}kT\R4:LW<[VqgMb-Ior%N|:W1JwnClcTtnp*v%;QЈ"Z x8b<TMrDq۸uzeMQ5gF1Q,ǍH@ABy-I V_ '4*CN/skn9*uh 6/T("?yx)(?XC43=ijsZ$+)*ӅpɳKi]rjѣMc ]Bv7P?qpnUzV;!9(Ƕ߲j|jC g\­K_[' 𲏝gHX{&wP092pM l+p{[DS 9)4r[Y\:!ލq|ZԶ⭝FS'!\yV]+GYܣ[&5rv_e(yjGݭ]3Dޝ>}wqtWSGc^\EMMZSg:pRb Z=1q15^/RqBEWE*4En/kݣi.!ŲE,*{+l%6UspEtKRڜQuU^ȇЁ{9/dshbN#tu!${.݋IA̫v ƫpZF[e0b%wR)p]!}9rK0CE"emXӫFL9u蚈\[1_;1. 8ZszS/7iAI򎞕a(!3ݪwr"V35ѽ蚉/}E@RsPP .6&%R0|%~T_@=f&u==F"$i>^7]U*meD},/ hW(&VbRj?{K]4"Whu7_m˙aYLPpw%hEP%#b]Ҋ􏨳 ʬ>Yw@NTh4rDs58GϤT{/1͂c%p?KZmuX,q}Z&)$ !峤]ër*tQ j6j[(^. j_AiEA'n0POءKŬ,)z _BLz)(|D5DPߺYBv ߯l #ku5|o;([k)1!}aܸ3` e bW-IX3ٕ]000cYZmoln-i웕,}A NL']R·gnnyvv uA8ܝbR--6:SíYƑ3_rk qI2/i0}ZWn#1ka!a1 ZH2LF)0~,mқk8tχj+t~MK[ɤ)qq3ӨCsBqZCdCao9`2Qȡ7I=i<r~|zȊly\Yv~SR-@:H~#iT0˵8{N߯RDvE endstream endobj 61 0 obj << /Length1 1637 /Length2 14308 /Length3 0 /Length 15321 /Filter /FlateDecode >> stream xڵct$ܺ-cWұm۶Umum۶ɗ~9pF(R"eZA;#3-#@FVЖV hbm`c``%%v:[ي:ycNXR84ydΆ*@F? ;'gZ#CO7H"lghaf3-JRVvnNVC[,@h Mv@UYTI $LIYX+DTD@5W'~3ʧo鲢** `,ldg? \nnntf.NtvftS1p9Z>q5 ca uMϤOIߚ 8\33 ` 4!@ @ov96 ]sg:^ކnyb.N߶ӿ*?6YA9I1QeZO}cKOz"2\6#' S&v66`'bɓk+[;7[njakby{zU[ E` `@wcsQ_3_' ^vSCk') pvtz{Oǿ`&ΟBKښ8eD߮?J9&v), (%bm-ghOJ3"#B+,,܁& "_vIgO ښY?qc2|4:9q?i TR4O `h)&VV㧦M(@Ogkwq9=MN-+Z nay)YCgG wmO]0~?I_Il!!;w/ZϽ2q2Y8Lk;M~2 ݁ư vA)!>Setg|RqSX"9;@f ; .]$" t;E]CY\DQl5:Հ %Nbc6iM]yNf^`-?kmR~T+P0BCeIB|1丗|k_}Fs_ɤGqs q ;ѠڋE?$˄Z@wGu|k!˝PkM O ʣdG8nQ kET هe lh`60i?j6|_Z4Y'$[r,!C;8 6eR}-ǶYH[M "#%.fy"cBJD!G3&"jە/>˾»c2#ezKú c=~b 4C$& Tx(&j6B"x؎d5L:^"9"SCt끣qgݒ'TB9zZl[! xoZO"bv3Da#IrVxl+ozߋ%hDv)!;1CU|‰40j\ߔoP mbF} }~G5=b,=s4$R[eIR0= y #()q͛ EJ6Ȫ$evgby f`%%XIދ^cQݢ3#!7hITڻA`qD6{Um_N݀J dJTZAoSCxv|&)]Q%|xPon0#||>`3)\p{YmT~J8 rG)hpFOt QRV].lc 7NS6pIK%mt;lGBjAWo׶K6/b;l΍鐑sf>0JxHY |ڶ!`$-g";oHfKE}z-ng0qߌﻵjy(rע_;G2v\=J,\o`-@ LuU~Gw'Jz.RsQ5]%.>-ԧ7QҿϙIw$ Ln )%TX)䄣瑻VeLJn9tT,SΤX+b( CK(2 *6_ӯm]t(ƶځͪm9/k,J_` MBB`u ̮Y-ZMMa"O৴\23衮ɠ먽<4eyE+Q.Qs16:p`YJ%r2&vg$̧͊c._IP.A:nx@]Ho  P/yzCY򰬜nBaܔQeo~p# c.ޤIHIO&݇BQ;{UWDИmd ,[4!)jw%7WDy*CIh6FxXVqf*grx ;cW{p5YZvAXP80&m%y)w]h Py,t)Q$E^={JE\d R$es'Ǘ\mH@C_JH@-ˀc8/;LlBpjwMҿ`D Y&U} |,.e{8ɛ< zT'vUZ#L{WuOga#Γ8UiM\+:~xHb<S>vdb"2jyKUbeZ<$[U~,c7 DXzG)vd-&jY~:l! aIt{"tdJzC|yeǁus@#=aQCR@ 4+8?Bd_po/:[JB{ZL)G109R )|8ٙk;iʵ|nGI RӠDXαko}-[9UhwE1gN#nF_`[: 0ܕvtf%a+-SwSѯ_1U$0 e٩! )F3]yHxғ^z4 =v= R(|̵Te;aF^S# ry)itDBs֚dСgcIymOsTFcGۓvʫDy6}ЎS4Dɛ-lrP a+Rq6L\DMHlqHm2|-6tD[*lFO+%b|k+\<NʸWK8&>[z ~4ur^+GANY9#mXF 3mXzث,m FT[*U1 IUB߇/(W*nM&Zؕ}|[$YB$6UN+W @chx~u7R-X^u`KizeMxey/M~%eSb<| _l1l)Rh,]bFV2JNQmer7)"̋3 &kދ?3x\8 F@:P7E_AmSEbxeT㞖jWʄמ3)#vْht`8Xa*[mjDYy3[>hBfGگ;ЋLQm=kb+P܋,[#: (}.N8=L}r_8Ān𢡊73/UB_"=kx@+cMO& }@킣LBym,Lnws]s|ݥZp by5,_lCz1A&Ka19 u [w=X%ONVʐZ.Π$LgdqI_g/t/fg7]͊I\Xm%*VσSKhZ6M f@ygكCE>ZҠ&$Hs9x*(f5: x͓"C"Sh*,P6ԈparDBuƞ>GoVop} m&56M#lAg9F}qwYJ%%SEP Q)G-W9 t't `cBqP<!Df oM7m qy[D-xM-z7rL}s n$6ZQza 3m 3_p&"|MSaB3cǜ/Vj^uX =^L9yYjm4PgER<8JjN!=dR|L _nJբ?4xTs!o_+!0 >aZH?q;Iw]tLn+uT i{[/$X &׏Zg"YFYR׏#) +3ZDlY޼42 ҫ2y}YX@e`Tƣ^Q^D]Bq{̊A_2s900aj+B;(@fq&V/GRn1sKfPTLMa[1&#~? ޜ{8LoUEl`GEwshw$8M 5kJDu|JާxHVҐڀP Wڸ ][@KpzY{uȚ\g~f-bDZ{5.ۇ$$LB \OC/r!xUMj}bg.곢5?g_{DwjǸAf QLlcmshFdWJes]Kkx&5K^pcW_SnEM 597TTI[FlE~.vƺKbѸsa҂y]i}orSۊkk6(T_1ݕOBgggqC؋U7S:c fA~bpkBwxlZ{7a2[K(m(EtRhFR< q1m[>Uۀ6p V/⡧7Br'|7(jy`aS/dDڇJcVZՎțdDuN^u^(iE:kec.+I-iT}ط]u.lBl oo!4&5e؏O &9#Tc2 ayKX>%xič#-.%LF{ޔ:hcNZa"͵Gj*4M{̈́]׭o5oVadCm.4Ɵ-_u~PxqAJ1p@?ٴ b[ޅ8+#d tMog)hRM-RAw$.'-\2l緩iׄ߉rD2(!N_`4E{b[T:ԝ^ɴT>L]/Ʋ 5CNeGy&R=z(PB;z@$unq Rd /cWxQ$ZlgN?'92/^ڳb)P*b@щ %4QӁNs~^CƓVC9? B.C\m[{BDTxOT{۞fjխB2>|$%fM]g|RWS.|BAWQ+axSC[nP֗ tm-Ij|YvI}FXYuźCEG %yFlW(JvmN㋝굙1r+K e8tv8f)xqwu<8i7 bM,ڛc# "4<}W@~fɀIP vkkQ[DyA';KaPeT'-(Dt_=% T߭/FfV Sw髼A1؜+\]LxLK(Eg#.'8KɁ/yX(O){M$?׸[8c6%k@WsOރ0 i+N|\aΪ;D1X Zް`8UGP,("MR!}ܑ tB a4/{fٛ7t<8x9 %u=B.7sQԂamY,R@.r~ηsXu4zN+2Y)@E ^iZ}(who?+ݤb$q-Lqs6HyY_A*vv^AVS5)&,x'=.>٨,ls.׋3لp Ԇٍb vGڄ<(AKu}qȑ1dbRۇ%79zT@jFHw )$a*s(ufڢ o<]Mjs9KF b+1F(_h߷5;) Yχ]&`-f,o1j;Yi&t~tp 0vse~aN OJlg|HKLhce=wC`J'tR? ( & 1'ISwbt7"l P[ޏ7Ev!9˸Ą`QkK.,[=OнifoT}TL{+ׇXUz`#ԛ]Od\uH70VܧٗwJrݐ*:vpr ^͛WXS^}Tfs2n1b&eۗ9/}7*1߄uc|d`(E3Ox;p*zd~BV5ļ:PUZMXlaɡXpuBDx©vغ}Sk9!λP_mdI!D=a,O"_ďQ;#`|?ԟC6ԯ)ħcjv?UpTKH+]V{}^Y5ik1CniY5I͊ro':Ylj^c[Y襂RBhW>Yh)Qԝ03xLF Aw;ܡFмKeYkHt~ fhXU;mgti8"5XR~l*i폨Za*Ғ"Z}~MJ;)_x(r57盆0=er,E+LvŅZbV<=i/+OA'2~E(DžrGF/\<͠_u[p*eDjGDLYՖÞ7: Tv xI}Yѝs^ f=B-yˆTTԂnL8gόR%֘o;CVٝN3s{mM+ %7 xK@֒a{|F`4U$H;PԞ~1q()J U -eRÐb 1kSꥱ;b[ƾߩp#eTvkNhݬf5tU\mY @F*ZQuGSs3U٢Aj3[W/ 67mKq܉4UAIEMRqiFqHCэAP25'+bP\;]MY&^0Ӫ.ﳃ9-o5rŜbc5uGycG:{XAR0@vM3OJ: $O# Ꭷ$KL#}k2<-}ݱN/3,PFɑOPE$,PQca{4q`VOUa:ӕ}uRt'I֔rze3Wz?&@,& + ^A[% UKәw1P-IJ?I5,-A!Viß|Pu[e5E.^b?0WxF"1{1~8m/x L2&8Lh)ɏW^MUL*q1-!-ɡH7#Z!PeA ?\B2.=1X=j{6bcj>|X{p6:8xVCP,_Tb'5If%CIEluF{oi%"f>8[dTAx=\k0Ĺq2fLY/7ĠtvⲖC;mŶz<\Z7;Я?3iŅt4[J=E YdDkjH-9g]h$V4Z*ČކtxnfIj%A:i-0Aeyk ,l#+2J~4?-P#'(E{7M::V% H-MG {A! ,Py9G?%4.`*r;0HGEU8_(9I0#nsL_p ;Y`::ST9'ZkHshWokvwJ{|)b]cE~v@$*DC c.SXOc]WkPLF =Gϱ=+N'se_p9&,ėܫo*ri| Yզ7C:i3;O"BGbU4U3z[v]%66~&*xD ^f8#03WwU(`d~LrnF gWx]סϼFBls56!n!Deef!@w0.;22ٲ$S,!CH6y;KIb[fA3ꆳx YlB4ha$!K /_<@;|z,@GXP;zO|ULG뮆,>y{3-j}* 4w6BwU ŏx2}Y\YKJg&pn~irʓ c@|<GA5h=Ȑw2I:1$*$: uy&>?A w5cº5HW J\5c y2jb1]ؠإ7Pw ZZ{͟Uf:JT/Qu/XbMӸoK9Y!::d5~.Zp]S8W3U*#jv(O@̝L@m0>|2aoHVn3 Ln l-h-L)j3 0z?yg*&=gl[sO\O9[@0L4gxח&)a%dx If~S!f`1 QLUh!^M#5*1y; 9jRO:]TIL C~hEP+Gð^Y4lNYp/Ȕ`0s-𿀧PܑLJ}1O2ņ 뛢E Tʌ%Cܻދ_Y?.^Hj].v\OqUcNC +fDo~,'S?ʜ!dV=| dzR { )=RJAPEl Q,s eN8daʁ *B3k~. G [ѱa"fpZqk?. ' l) \}A|^ɤdcxM$Ny|Y0NCs[}{6D) a<L&fy&OUYq8Jg؏#/ps!5\J &G}p׶Ub}F 2M̾Jf<0 W-F)qƋgm!4% hduht1;:~gC"bW5XhѶ;`ɮKЛ b-ݿel%B((Cb+o=+~Tq. ]p~m2{|LB][&zr0j Lw^;| hC"퇘cHy!JPG<goE9w=k웨2*%ɯ˩o<~[9cuGI>/Ϟu+LW@Hq]5N^fA˓HቘLӔ{6ERKɧ'QݨaIbE{5 F+\7%Qbs&pfCK*q[+GҬP߾lQ!RNF_}T{nE䱯5؄DvfRjHۘ, p0ә{irICB?f,dyql>3ZmruBN],^Boyd ezNE sGn Ovpp&h<&{&Q#|64Vne -Wl{_Io\Xqk69A?Ӓޖu{U ^>2,kr,-Tkw3Uq2ěܳP)w|aH\LMPpxʌy)uv:g_(^6}p\zZ j&ЃGoMy\. Uܪ^ JÙG$N\ӹ VnΰVILí;/oPʲHkOoPqa59Ь>f 3Q>mCb~PDnY bbdF0# afi7GN$V{ȒWMǣ52뗗9OmRjwCOKP]K̞uχf^Wh\I [pf, l%UU:> stream xڵeT\۶5 %N \- )]{pA#{>~_V҇5fs QP6%@`zf&Jߚ^ oif`a`bbC@b` l b~K01q#R>voN# @Wq2 {0hmbf yKٸؙ`SO@Zdoaз6H31ANoF35`4շ4*@ 2Ue66 "@LX^ETSh@^[t9qaMqf?`8/noS{K5Y@m 02:9918؃@v& 6S158,ov@K_q6z' kfIi6ʷ7;S_{ ?ژ+ 7 `{_7Ј_Q;?=e6K]veږnNb^!l@%{?kffMNX^JB\Y^MxrX3E',&b0sD*nm$ zcmg|bfos\-ANnnlfmdgF6f@)~3!m3L-lh_jcfc~ `oi03}!;`;?9Ff7mĿKY21_HJK@֖.#1"<&p׷RH;N?#+D,օX/-WFB={M'v9&z!D_9|g} &az=%o!a.^9S7.zK9R^z!&""IgIUZ=_n=^Y104!Wx/5JmY։zxi?uFC|+e䨋؎bRKHS[wrV(h9iHؙ Z- Ls\m0m.5?SF^/fR|N[0[(G VYFyA Vd%oC|]s$B{rP:CktKRBu"uF]n?&cCB$ړpȂs$&-z.$)"?}YuE|ex6#OSvt .[1;[drmfwI 0=Bn~-\wXmѯ<,ډ`pGOaE5~ĞLs3 F5XAFǖƯ>&pMĠ0Ax~G y˼'.KH&w$vjye|7.U_+TilR֍PsďsYIcVS.̾)}T(+̢v:aK,Sw3{i-*z ߒ 4O(m*/d'{?[Tj";T]P?l|'ai~]E4 nZh΢g:b$EIsRv"KjLaE"JaJ'X2%”!<#3*8lS{ T.3-A;9@ea"j_ȶB6D?V44 5 W\IoΒl:o[o*/Я+~$AOU+h+]uc7>AЯ]\5B"?b6VU@-cQuYFV\gCj5?1o z;lgα q{Xq鈅J ~W>h^˒`) S߭+,VI]?@;PS>FL,^A;[v(HZ@K3ML J%+_@ˡUvˎHW2R a5фv5,Kԩ&C5W>D'̟Ktuڦ1mx s\r_7w1~M]R1,E(yȳhv.Dۥ;4?3 5_T(ONbVZsu!/ƅ!7QSq, rW/5a1fp/OkfFښ) |Håo ,!FqE()٤#u~Z |]6EY;̭΍DEWˏ   5sܥ鯑{P_2e "tz3A'Vh+ϓuQTTtv'FlmՍWr8k}ܨPkE&FܖG{-f[A1˸B[:s&hfVp3f°\OtlK^7L>;Xˋ~g;),PGQT_פ䜏oAVl?cު0׹R[jJ14MŋSœ.]VsEEV#:wYėBZ)IS% 1A~c7*~Kyqcd}Ǻuw72a :?Ԫe_OiNƁ/@LԩZZnbGK΄cI[D~ KޑzKZǷ-ɐ mtṃ ăLׇO\C! f V3Ƣ)\q-k Di& v5&SlW1o2T>4Oiǃk\EUաIJh']m-@A\i㈂u..Yi(u|j"y^@2<0k疈[ӠKm(Y7Bu= NzT)*̶{S'!HWÐgFsϰa4/Wij t;V]1vj'\xf"+ݝM=AϡLM"?l5!"JX:=,]Tp'5RPtTm bEhV-,GA'|"2=i&:qCP )BHW२-NF2!wJ|I=hXY%MTbVTMXAݞ$oU:*&S*&L]@":kpZ )&JKX(4)6n)8]ZF1QE <ٳH̚mΓDmƣҩpvv b;[Q- k5Qx{SPm>LDT$;%ИÜ!`m%ӏ0?>QQY,&miF(y,S=+Xb6]C"0):K _gC䵆!jxqeiZ-C :kTS@'<^3|M#n.vDU\5%Q#O/PNJL7&ȆWsVC ;UC$>*m* @ޥJl.S7%~B?% h}~˅ ' 7ÛnoU&xȹ;&l<>}XQ}?Z[{cN IYo@(Φ$_aߡ-R24Erk:4)L VA5p^G=뺯Kwˮ(i.f8Jj),R8 Y&V!TL0V$5E `$`bOۣC#Eަ!CAPp[KpܛY@`E3BF֏B]bzJZ\=EKFf[JfI!l}sya>4o&^:e.r9*/T8QiyĚbOE^TbEZNғ:+⪜BJxUa2[* ޓ=9uItz'S1\P F.I'U.o`xN*y`˅}vi=&},{6f2ZI Ԑ5@zc#ޣelvQӼ!q2W`:йkeFӡKl>52,BQ;q э.`9U^Ԛ"9x!1h~& +8nS|oݞ)y>цw,lyJND.8DZD [C?w,X7i$CCe`(a-C_Q Ԩc{F\CP]4#u 2Hhq BD44D'Q©iS&>.˙6DbGs,W6H'cq!;AAd zFh&G Q1:!TZ5?}y;eV ~Ni|tt+ z\L0a8!]A8}"3}2?+*Oc}Ռ<>jF4_8=Y֧hw ͕zv^dO5#OYzSwwQF;B@Ъ v޺t{b(t*W;)#ВP`z3ZJ].jlZmcN\kױ~7X~7 "+T#Qy^1S ';(ƲHR-+J' $võ'ӤŞ_leQV<"hR]=<,Ml@t\q!Jjc(ITu,K[k;>YYգraˆj\EA=8;؎7I%Xo "$pwN8=*.ĉ0R1#4(MR x*vSM~Kv(ܡ]-n1Ge-K x&Uȵ{Ves~)ϊ=C݌'4#]eڣJkQERUTƼk&j>B,B'o7y|B&z `>WiS%9u(T<+%S᮶wʹ菝i]@WY䯏8i&Բ >rN^X–'BdZ$46ZqvE 4lӱ#(FFˑkX:š9uo|,qm݀A/gc5-ZK#v1Ss73R2>W|z C\@W{*QTMw nC r6J`tꕂ% @&K&FHdM8Ƞ_Pd.|eA>R\aQB4wPO;+:gz]b ' Te[>E;ḩ!8Ts{y[f]:g*;ҐK<<'[%!k͆f B!\ݣ72&ACA;n1#1pZSE-tF.RYgL+7FMxI;xDf œs/ KegM/}M\`Z4EuQ7 +JQ_C_/,w&o5H&G.-5Õ>jC+ []m}C ࡷ@W!pVx-P QmtU_n߄mN҅WzjQ`'9b<2ѽDȶwt{@PYV6Y1- =Ol*^j8.dLb,6amZ^Wo#JFQ²ʴ1=@R- Ïcz6K=>]|d'w=OyatE'x԰],4Yh.Hxk܌DD}?ET1уMZ{)MY{L,o=jF^ 7 ['wދ0ہߑayv BJ]py{VdyY?jЩ'? ~ ~`p@/l؇Ը]gL S(՜CDꉒmH g;^Þ8G2ǵzx0MCYV:6 eEks\ FB3.vCF"I]U#4,gC#ʨQ|wxNQdFNcnsn9)f!(D:)cpXh1(2+'ޒ50lELFLw!"MaymEk V+7yL+<K}l*ϻP.ܒ>ݗ/Zx= ̯ nL:vU fR;2>lO}_u|FZRph3f?*5/u$sL_)L :ʓ"_#JEa1KCWM@"[^yo_<-~!=*X"θbu@ Ĺn%6 eYEF1W%锴C|7 zB{cP'{L!8Ӂ{*rv^*u;0ߚgnLO8.j"pծw*%[1oR-$v,-k_EqQtiz잛Q|Ռ4f !ܠ_bw@0g'O>K"p9UMW!:Z7ạQi捡tx7L~3L1әðdp L7ÁӺp(_x2&+ŮtlRYx']Z#D5pJ-.GH@02 rFԥSHa²yTqrJPxJGtkڷ߻bBʒ;Dj B^`4 6B/ aI# d" ]DOYr$\[2 H! C%5hhh7iП^E0[;J ;&tQ6;JYY1p8c' sXXԔ%7(f9Sogu]wXYq  XP0_rwwW(Qfx y{V-.?;lԁa.EYDヘYYٷ:8-5 N^mqtؕ.c E|rb%wQ~Vabr5 fT4M,"t̫VH.zBQ'qCWםl&C 'e- Cn.o ~k4t$u!}{6oy&&[/I>RIV]$5ĕnlAt!7%6#.nổ4 |]xD2V.lfS~k<1 N ۔ۇe>%lYǾa֧)jDD(v§JP= {Z!F#-g}H&=KݐՀGD%(Ů4;wS2h0KlP5%9c}-ݡMyȟ{aw?33~ HnOa>>b$mǍ=4dTW]Mz|f%mrxQxdD-,Gݬt@(C<@eӀa7ܒ) 8X g^[2 mKsKLC4b yβC*:< d*}v3$0 fU*YR7LTi~]]a6.3Ge[ΟOtDLvl\#!X6$]ބYf#͍!*2Ys@\Y:ՏTzgŌ~+)t#$N"Ø_2÷gU rp:C#ϔfqQ>ae þJW!U/L}cU( Rb3_sܘ\,E<"[FFL$%aBe%,-S>|Tn]Fs4/?LT:=.xVBF,v>gŊw7H-x-:|WQ]%"cpSM ' 1l/Jmm,ߊJQ[Vnm~C=[P_s҄)5 _an~fJzD][t{f2P{@2eYs1oȋ-,l&c[)Bǭt196LWD{K+c-/:BG- Aks~W{U0)7y3ag |kfp@jns$b/W}zXI 螖|pwMh_k:bl,u- _9zQ;q 1wڏxߢ/(<)PˑjP#0mhvhXI{AAgiqUKPQ-0]رX #uc eCSFyB~a׺׽|_B~ӱ T@q?3|8mbhBb`I0d =WW5!9ʑУP8. DvKi{œ׭.J}6#񍴵_PSTpd2bſ£2*TA0uj'ϭeڜ:%ݫ(ޫ0 iFunl|>Y7U1BpGGI C]Nmd@(V}*KEZh7{":?MO_<=7WٰN}%>Ғfл1cIsSy?\Ǫ:~,w ^Og|.mC̭V;-P) Jͮ@hՌPP }xĜYOGv$V)Qu5{h%ov<[:׵$l =AŕeD@y(&(bH,^"NQBOL8z6'"廕 g{In`ºB22d i^n\+u{!uiWF- wnwo۳S7m9uT#pb׷BEJM?#rH7vSdzv)0c#Ax €D(c) E1La(|.7~#EILR4  h ~ΣVm M,AzR)CЗ=HG.;S1 e]Оb8G­e/.GKw *Vh>2c~I\3ktaf99O$YWG3 >|}5zj->V@ٗH+v ٓuyGHR)Pf@6!> 0O"?nA{s ѼVH0E OyE#Eحk6Ke=sHmaE4j(*9u:dYp ) %# UcGp{̓/AJ#ON|2ؘS͐o|o,Ny_2춃8̄ +%%%zNMD#ZZ엕9[AFb)cQ,|;<6sݓ%q V0%RƎ~`=Hinmmw5ͻ|vz;+_1uqFs a d,|[iwJWgX-џ?bP&#;GHZ'uM`~W TBQ_sG]ۅv3}ȗgsȵ"kH\>ˡx 1[NKEV1`Ix}M Fgf/͒.t iD X1`*c8[~q9%Brd^#E$N 6 ieQ@7Tt3N4 h!f{N뵺_AK@=AotE9N؈TdjYvDmYnMxLC.k{mPpFyĬT8s죝[LSփ `^=B΃RƄW!hskW+K;0Ov+nזż{Z 2L,[-]7Nwhӊ*HB!ݏ@ԏϋ_,z5'FQŏzC RnT2wg'lfT%;in´zVQk,Wقw:m&2DvK<4an<'f6 CžA65ټ5`TS$ B$m߲=u\K@/6)2W|8& I>(rO [aℏ^SGENd414K)^rIGR44ja0yv'24XB DԿ.vcr 7iD{CR >܊qKhx%^|~T&t{@_QtcxIs!Iho2O7mP}nW!Q;w ZZS8̃Mꂢq OaJp>؏pa04{!f f\sex4R%ģPGrݗV qw3;vVn1q$3>|c`fpxƈS8@ԚXYUW8ƈ}+ ʬՑ'lU1'uA9+CR&Zf{~d{vhPs9,T;ټ fQ:wsu5Wv~`Ke6khCܠȸJN)5#j2 sP8_@FUau]~?9ZedwtʞEe Y[twXwO۱ }' 䚦ePD; lI@c *9T,U;sIZFEfJ|苼n6U{Όo( h<6Dw atF=~Bj$eXNy_v`b ٵb1=vw,%t!Xb` LoG 9l%:;t29V5?H endstream endobj 71 0 obj << /Author()/Title(\376\377\000P\000y\000r\000g\000g\000:\000\040\000P\000y\000t\000h\000o\000n\000\040\000R\000a\000n\000d\000o\000m\000\040\000G\000r\000a\000p\000h\000\040\000G\000e\000n\000e\000r\000a\000t\000o\000r)/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.15)/Keywords() /CreationDate (D:20170920132045-04'00') /ModDate (D:20170920132045-04'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.15 (TeX Live 2014/Fink) kpathsea version 6.2.0) >> endobj 2 0 obj << /Type /ObjStm /N 58 /First 439 /Length 2978 /Filter /FlateDecode >> stream xZYSI~ׯGt׆c"6؃|AH YeUIj n$2-c޳Ȥ1PL ɤbZ9&530is,hSf1SbIL'Đ3a h{'AM4 F f,=3)$%1h1:*hxLuY)10 ?k)f} J:!Ш`{](Ly1(I], a^b~d>s(#Cwfg--FÇb|y0=ՃvLU-/h!^QqMS1fk33u15gtQP1X4gxS v޸=!뛫Q㏚I[OM=~\Ϛ頞z86G}T'=0b&v&|>Ӓp0q[Y<n3W<__p}!*=IW"н>m?O;Noy^j،f:RTJJalV ܂)gY+W.sAG4|V_'|?~u3~@Rg*(W Q,_$AD SOaScPI;/BW)`r}qq"\|D_y$=x7؃JP3KiFyR[#":RO,T*řql\_Q_,p˖3`dvE7?zןƃxg^RJJ*M9Isq()粊>H'eQ]8 ?(\id\ ֮V]2-T zkw"(U*+gO"^i6ަX[c݋?@4%,f3!eWh%,R WRz#[3M19C Gq$欢^SoVf8ʸ,5/+HseY!S2FqRWnkv9$-le_ Ut2!")I$ح>&A3!d {p2\%ˊƊZR%SlX11%3u}ҦL]z#'j_(yXZŤfIB^;.A=|?\1FgaBG%O" K4䒦jxRZB$D4?oP2auȤRX>e"ns1E4c&Sʶ mHon2_xwfq_=q{-MZƶegmb<ؙ.j&pn˷`we%xt |wc_>Asy9_),ml\6Cu V Y!+N*_IRzMU}U_<ӱq=O7h{& N}TmEWWTlPurлt?ha qOqۅG >ğדav7'{8qo OHNƓԠ|Q6M^7m= QZޞO뚷70 g~fŚ>|s{xqܻ.5]X mF>ȁ9KF!.6^kwrO޾; %si2M;T̺;R%}^=L{{n-ru'w_HO&f8 쬦{^ޑ% ☑ N#_LntJ-$H$mNbITSK;%y,IK~L,nјR=>e9eFI'߆ve#9탋,uػYvm<2.}KnYK,2~iH%ҹy싑qTN1kRu$ RqX̭ &݊`j) ݱw 3`wl\9fOlƲ&-7#5x }aM,sr̺d_/[Ԍ>F]?g/iZ{q^ uϊB"b9qjQG,W2iZfыНF'q3~΃zZz?% <2DE4E7A03FFDFA81703972F809FA23C5>] /Length 192 /Filter /FlateDecode >> stream xM`E{@DA|"" ~$;pihN\1$+͙͗H_Va[0 d!KekPr p7pp]:Bl6TvX9=8*C аƿqִf=z:'_QmST3u}<:LL[{9 endstream endobj startxref 197866 %%EOF pyrgg-1.6/paper/outputformat.jpg000066400000000000000000001557511471451651000171140ustar00rootroot00000000000000JFIF"ExifMM*C      C  (" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?pG#ĿǥWtKj>GWIS=1IZnm2;2Kw;y0"#:.77rfKj:嗈<xĚmͤI)}IH_i%(?J+n}/L#R+x??_|?\] OxmllxP0,+*oL4lI1G`3]w5K Zwï%sE̍[H/|wSP j$0xOdűQF[}v4:¿~8otb[K/ӄOB R(P`dW *%-O6CJޟO9_ WA5{|JյO?Vo"k$1 Ee,S!Cx"+V|)|%OjT;ƻ>aq8R :f˧sLJ>ƾәmwөz2GxV&7W 4[7X]cdLhe~VߴWQt;O>$ӧ$mgx;[#آ@$g`v;u G5&Mwg:Lj* Fۓdf ad%-_qj7RhDInKMRN~.Äy~?E}mN M:67aEޛxGkݫ~7i?>volR{{/5Cn7=5Rk3Ҋ ((((((((((((((N7VďgŸ KjgAZ5w,-"g0T!FY,<{ÿt(nߏ/2i L #=y~[t| w+ҹ)J.̭mdo#<gT5]kw~InBŶ(P[bu lCeviwiw>JzWcsuIL%|CrX_ :w1]nݘLlWʿ?C7 ^\<'i'bs[@m ] +/K#!#25JS"6qR}4]!d͑Th_0dI]1AES((o)u{~$u xC4Դ 4،RHeH% a9")Ys(|\fh}S> > hHݥC1lC*0UG 6TՖxu ,#-܅c%xo'4m 62j+閗w6eC2WVH2 a$qx]ydO?^XkΞ2`ۿ>5@xo|u7-uoR=58Q(i ap=WGXoĭMn!7,bMBoL(imW»?_&͑5+Ja|%'gOV>9ౣw0M6l6y01O5u᷃6i~*.~&irG^D|A;1lMlMXۗ^?ǟW>+:Dž|Ck%n/N1ķy[䵍Kyo'=+9voS+se=i?]YguMi mh.W{_֧wgWa {]KA^IiQFrgW^~kyJ>Ymu}/G/0gqF}v'͘i?]Yk?y#83+?ݙ4iQFrgW_ ?hye=hҥ#9 3+?lcμug=hҥ(ϸ G*\\ɒ]2끷TiiQFrgW_ {]Kº4y#83+ug=h?lJqF}uן4͙4iRFqgWc {]K {]KK9gQq]+?u}/G+?u}/G/0gqF}v?,M,MTÑv}¸_4¸_4iRFqgWc {]K {]KK9gR?¸_4_&0g~SFur|n焵 4ɓX!/!2XKJFߋW~X'ZoK3Dm5kZZ@ֆx<@|\7͍Շû"?],Mݒ5qyU1__ZuCxO]e|Om5~--A8\Cyrlbϵz>/~ #xcvq^ɤ4Ȳ[\[MG, Wrr$r'?eh?,M%èkm? ҽ?fMk5O|B,ψai)YYG,Xc,$QƑ|zB? <{]AxNYZhayab;4S}Q?/?h?,v0+,w{z׶DcMbKnFr>,_&W_&2mAhϸ͟ş;>\Yk?\Yk?y#83+qg=hqg=hҥ(ş*^a>L[)kݾ^jףJ4-o2:ZZAI$2q$3$?^-[xjw|Kx[IEwjY'ڤ&6pU &Eo0<ߵφxGM'ZτuC7کlk;eF̍;k77Axg~&|R//!d\_^6Ao*Hy\V8[_?'늎qk$k$4T:21FG^AY[~=h/?l>"|Jw?ώ>xW"~}?R 緂o⾹ .p?'e/>3|R9Ҵm&7l]6Zl&ei[!Dg ߉~~k/iz?w^+ޤt,JСխ'%R#Vpd9;O]ǿ4>_xº ծs}`[mF${@%~[z#YFt?S xğ g_ٻEHXHm6qro(f7_/  _x7TXF𺲻G$R^)^9"Utte ~J1)~eK<+K:%=[Z^9tKy-m?1yY6Vٿ Q~xI> ox}fIMFGBQX4 C<,0_OݕϠw Ps_0M |#|rs5/^/xZ[c&sEε'u' {kmzRx9bug-;]OMn$2Ļ+c>:*ѫ]X5FBIB(t+'ωů 4߃?x"Qt}{m_>7V[+dRXe9_We u/zgx}_i2,ʈ!B6ȿ0R$@*o?/~ȟu_i|I*{藶V:}65.J#ǗXڇ}c | "|5Ŭ6ϪZt91RYnVܤL$n#UB默RHE9+ A~n-wsDŽcWC֛?.--|3 <]/f IL:?>W ~[8g^4L~X-MJLf{`g/½Yntm[T)%U+8yŘ/l9koWſi'_3|:ּ!uͩip%v4i,v҄aBHϳ̟FY>y |DΒ#!'umw^]ԪLكu]Ksi#ľ Kཟ/4Kq㙼Kͦ܉4{>mvӅvP±5/&&cSj:y $/$ǀ}8K|iu?HQIE4K3& JXn^[k|2.3ŃY'm1#D7{0 |V~J~|S]ڤ4;Qt-ޥPG wW "wy|3kq7_۹Ksh.[[ AHS7+?>T{m>h(5ili70ͫhee7XOq_3ӾA~'hG'KN5fJմ}Bs]N-geaXȏ~zDž;+ş xݶ)aXka/:/,5UbxVS/kokZ}e"Iiuߝ^ "bv$vI&obD??x_ږ>,Ӽk ` -BGLѰF CWg~g7w__ Qlj41 tVR~O_=| }[U%]o-m.k cMҖ!-%d)u+g glQ߄C|bTtW)Cw?j1MQK%HTA,d,H\1Oۆ;?kz׆|äijWvm'[B4ޝh%XbI0V&!~QGw7_~Xo~2]ZjŦ{]Q:wf%ծeHy+Ė啞ĿOjן ~xwEh<;giƯݛi#i {[d6b\ xۗŏ i_ ?TЬ5_Gџ_յ;'mmg =U*#i.!v]~(K`۫ǟmu]FmBIlЖRLZYEWdfe|hZ/>7]i:ƃW4_ YjRXjhe⻞@$\Iy4i"C]QEn&7QE"((((( _'Y}Cjڬ??մIzur*+͵ȩ$5-Y3zPWw!x;K㟆&kzl6:YcX9Tem|=DZ >-v ~.VC'4K;{y) ̒lHsy#ݺgqr9U)W=V? <+{xm:]K ENvn ި4u5g G_go x7GԞ ӮSM|Dϵy"mnwJEV/뷒㊻=O<^Iر߿#Wۻv7c8A\?2q3:u{_Vzew{Gpev̎^hB! 8*[utzEܚ (TaP$);@Q^{-~S;HޓZ^Ky v>շl%yhYF 7t(`RֿjxK[<>񶃪ZH^Ltѧ ͢ ɨ*-̛Eݣ)>֐1jEGf|'<-ڇltK_m''f[>`TP/Z\J +#3'ZfM)lPb)j}SXxV>oMRL4eԼHB*ʼnIۃ' i>B^*ﵫ'vƢHZI*zgKL>>__S:?|9i\,2O,\2i9UPJExoWxǿ5mFEXk\.IL&ȷwhf9&$ѻUkVÿY-*Oj^kO&; q0x%y ؑ3|b>9kO5q_^ ľ;yb=`-ͤђL$)b$E94=uQ@Q@Q@Q@Q@Q@#/ϟAKß5LR5 4JMi]Dr $gTt-V{q߅=SBf]AJ7Q_ʿ$3\9}_VCƶ7VI.; s pո&oFo/;]hO6+|HbuΌ2p3^oa_䏤!t5*"0?3<xs67>n5&4&'׆4AY`'HeUצ~̟tS'Ś^Z/ٜ Ϥz_~&Ė>)%{If?O~4|"g}%7lUCpV)+M ~ W/C<7OxWYK|a5͔lR1cXcI5x_uS:5|+UӴb`dK#;31fbMkof6z\iAb-' #B$[ges0:=͵Ѣ[0$V+>ab/;ߌE<%g#{ :I-<3ZڻƌaTV]=Fxׅ~ i?O_s/jOΝx [&Ǭi1vG,2S> a?g{t=kX +swꖰxK\(kdab^Cⶳ SW%H,7<]{vxdZAkxGK{xEy 0h?R>1xS|xi<oú^oaݶ>#"your ,v*jm}WZٞ4? d _h 'GIԮumd\TcJb0kZM᛭?\?wWF5Cun|u^X*X.r6dS)GOxtw=S^,<#Ui;w(hfVia$66?Jmğjxh/ŕksAjFRf;":J%$u5Nب&)#xo:lI}SEӚN-c);H<:F? |ZƁfo5ŚndWc7B( ^M7_t]x~Aq{YReI{O-P v!s[w  ~·E|rMqcL73 M&H`C,2G1c8#Ne'3I%[k8-G|F4?< x;6tL&o'z,ɖR1k!'/'U-{⵬ˮxS%׾-Y`h$VP=F5L6.kkku◁ Y_ àk 俶g.n<Ş( UD3OzcXc=Kfd.>EnryC~;uL hv?,tVFcJy%ԫ%8]`1>hxW+O?tKanh9ެJVv~"1|E|lվ"x/ď0,Q ]ؽXQkDD[gء g?fx4xho}#‘oggio.S2cUTS徶rg_G% 2xg2|Y{qx:EΗ ;OjxM,<fGg ^8>|u]uyTmWV5VVu8Ye1NHUTV|5O~Ξ?|_7H29v$ȱON¿h>(״&cmKZN[cԝm,Cg qi3[<)πjxG֟kcc$ ⴼk!mbLTG؃DkY^֯#ִ 4k}mnݤg}$.MOȹIs9/3υ߳G_<'ρ 3mSNе# wK"[4 #m$oiχ.5kφ |/_A泰5=QXY~PDЬ9Vp>K}_j!׌>|}f;]Q4hmtt{ Yd/Q|zĞ2sVi>,6X䲸/-q/䱮=u-|3%xB| ѼkԼK?z~\h"i,fy0aon!\F~Z?I-?1'όumxI<6xdͽ|$vZmxF>:_Ku߇ca&}7Q#b,}kg{+ڣ>*JoE{K%-riigJIIaBI-. бt&UÍSCo/;k xbHny$k8=>|%wh^(QCyMĿ?P_[XxNkzzi^M4זJmhVG;-f?J?b4x]ώf[՞YV"ίpx6O5#Hu:z<%;{}hx[֯urq<&[;ih~,Y|R3[29`ƅa&(@5GwO'.X1|N$ӓ:ͭf+#7vmee,^@%$o64̩Y_P?iy5sݿ)B|)'?_tnbf9nT +6^ }#e/j'Phl0Z}ɧȶNʍD!N_wF>:7>1|cs?hW]֟)RP׼{'/hk2MY?Xm X-<YozXj6i-bX/-BE" ו!ƾ8?d_ZW_`c,}N HQy֫>庞d1hT'UK7,>W2h3*G0Arzo/j_>1xԼ5sXE&e$;մt[۫lC2#V?f𖙧:Súz息hWkmh-ͻ:dkeL+Sl~0|!:xz? x+5Ki繰[*ܐ|Dzg MxPG#x(Lm }1Zۣ4! +l'٣\ԴZb_~~6o,.fR%e2I ѣ*(oվ?O#g<Ծ"þ ׭<5k,ZU㴞;&$ox}؜?goٯ?Õǃt,dQWyyu347/,x z)[]@((((((ǃ~ljk?ٓOV W񧌼i žZ$ׯ;(VX(!yG,~g(x;So&߇o4xG^ &7ڍ\sYȱ"\;YhJ`Vp>"qM 5,䷐ȱΆ61Ѹ`e!iESɥ,&_{LMcῌ5j^ռ[ bo BMk$JI6ڋk[^:sLx4h~i/mWMp4?Ni2-\{n#_?vl~6koĞ$?~g-^!Mb9ZOh3*0+86EԅIW~ן]Rďiv'P(i縸5,Q|Qp;s}~6˧sRq7aN} ΪcUL\w-4۽fm6-BMBwjM ;rGqWwW_l:\|N7GJG{Φcޖv%d@^8P/_?h_|'O4ih"oh!A LpDr-6V_ _?pz=||Fk?h ?,&k?W} ׼+4<7 +Ox^{}F{'Y A*UQʒQEDQ@Q@Q@Q@Q@ !_E5)Q@Q@dPHX_4xgL|C>~ÝC]Z7.)Ͳ@M$[Vwt%}/?|M^NMFE]28ӫ#mDA; TK[O-; kYkyVHRA}E&r K T!Zk(Ԟ,k俇 3H+|?5 K:VU\+mU)e$fEQKL~G|N/ ~;h6>CISeG,(lIcf>M5kwp$:N`yo?_WE;/Uu dCjFiW[you,q$X*FT_xQEQEQEQEQEQE|P|-񄚷_>7>^guZ_dw y$i",~b |;}xNk:~/}%)j^=DŽtmݟ䝚,"b]THУ//m[>RE"0=#-rq U])H(9 v7ߋ ~/xjM?x÷~Qu1<Ɔ+Pv_-xWke߇>|UY'ý/þ|scǩ3<6s{j~K_<%ikwo~|r躿K_'9&S_O;H >|E5đn(ӼPCoG^xp?Y~6?tXԭc.Ѱ-Z+W8V{jf"o:G> |73ǟճ54dϥ-vYHXJ/[.>$E5(FYJzʸcz'޺?)GjG}| >i,W?𵭾$orB0&J$y'~3x_H|;[C)PiOץ ʨMBkKނ&sH '?j6v2$\5dy,wHm=GO6?FI7\YLֿ |q?WīԓV6mMx3N-2M:K ~k/~":aEe钑 +5aOi|X⏌:ǂk;C49ah./>f4$+$8"#1qIR-y4q*6?X>Yk>n'aԮ[v7;K##d yXxh)p {k~nO$:٩+/m-oo|X||?x z8STҮu 6gni*Hcr!.4>'.]B7Nȼt֚!egE$ª2E3H0 ]Z寥(r&pEUQEQEQEQEQEQEQEQEIF3@2xoMuןu:^6a K$k#K|;^J?k(wWo/j^(llv@_MeT@1ƌ0T/-})v t ~`x_[;.׈_izח3ڧڜyoJM7; Q_Y~~ '>0+_2 IhIkn%w%&<`  x~ŚofV]BH-(̡WrF-_چ~,o|A_ZfFxwU$X\O5սVO)F.`Ƥm-/a?>$|yc';7TɦxwRxQѴm>mlD̉q3]KtގєE0 ( ( ( ( ( (09_U? |k_xgX[aj\ y&s(iPpYO|UM'*߉"-Oj%xa m喓;ɚHg L, 27E Y4 |Gx\ݍ^94DY`)CUdE[=B"E{#r>.A; KF⧂oo.bӵjoy+H@F;kн'(t>i?χz_~xMr #2J@Vy]<{Ҍ[^DsXWz/|a6v:xI}O(O,Z}(UL&`Z/H&jfʏwt#1 @<?*< ~&ơwzd}Ɗv˒r5_W5Y~֓_F+Hc :y`UHEtqʲ )˖}gC÷2v=CyB1o7YPc5_?k^:t>&&៊W>Km>#@ #FE #ޠKKuXᏉ\?|I%_=FJ34tl0# -=>&'{HV/=/TC׶N>*R_o5{g .|?yj0youۑ3;[ xA|I?]6]:P+U< +?־~/zt/:lZhO檆G!bK0I K(' "[ăq>.ZF`u{滹@hi f5 r<ޟ!~^_cc-đ %G $ zW ~$H,c_G"wPfg3Gqm&0"l'*gUs࿌:59o[j-<{!HZ8Yb /336 3'։__z,| r~3_ZC(\,FHUѥl.[-6[a #HFK3O_M|A d?diٿ;se=#7z$c WM]?KGn[nI[MMO~?OO߷G줺O| o_a< h˩h:iiqB̼0A3|we~ ੿|?' S647UKLw乁lT kԼ;ú?3O-6iwgo !6uP]v RO m~6;(W|!e|%|%kddyÿ?1؟ > x?7E?3kд4S+C*P[!G*l켿$uTQEQ!EPEPEPEPEPEPEPEPH́KMq@qxIrh+iCoӖ Ai;n n# gjrKOT׊5MFCu JaɐOvf*,ʪ *z&O?R|w.x#I῅/\4v_Q"](2}16Z׊|IZra3$쪊+GO#hڇ|K|==F;D񮹫y$Ǫ07%cx[_uxWv_ݟx/ӴSRFf,"'bKuw'g~ 7үm*!0ԭ4JC"1 ̜[pQ}uo~?(_ J΋"ؼzbF}P&IUT1$lrnݠnQE2B((((((1~փk/ߕKihmM Ѻ$0X)W?5ۏu/ -|wFSqom}q$+0`.2aχ(/m[YQTE>Io -[K:-CF9 8 *7 خXo+}nI.,K]K][$]#iI%PFX ߂zo.^&׭^?u;Im$Q}i05͟(+]oK֯>"җΆ&OR]i0UC!" F9F/Gֽ/RojKmhuѡ R0v? &|A|R<a1IMB<<]k_k^|?owjv=vQj"=M!wbrI&ȴ)n'8` I#H bxI4om࿰?7C>$fö>,MGQW+lCB -1$澀oş b#_LkQbm2d'r/į>1xZ<7ccf4 u4z^]i xN%6>s>+;7 V4\` 2G߂Koxoƞg [A!lgdb4,X  Y?i?6e|H~1.kw(]#{@4 GBWtVд VX{o=|3v¤h+aW?kZ}K#׏:W~!k>[ǝq-p eBK6]׬UYze妣0%եݬ4PI#u%YH! 迱ρ8Mvzǁ<)4l5iR -ygn݌8iW/n44^ uXm-!yqm U ~c]kY$@Z/>%+yM"۴|O0'~~?[E\^eڽI_5CC)2AIO&ů~ڟ/C4kk]qwˠ3CiؼDy>+ǶfAX_ڳo<~O.MV} #)/ >,Q~%I|l> 4.q2>!t&4^+-Vbܱy֩t$ja~߳|..qi:O#C6E;Aomy)rY$_}?+ҹ|F^Dthw1iŚ^Z0$hb]z3|-B~{O\'H;F Lmڸ3q,0d(_%oxK%6Q 0‚8EP0@Ɲ>;VAO5[d^6׎i-b$||H?`O`gh*$xoM=gģZ-4KټN̠a(gX|0񧆾jVQ8󤵿H]K;psq?ϣQ.֡P>ОxƗcXj$9#OuJG2 {p&4f>6PxO o RP,n&[a5C,ZDY̒%'Oď/ᖗvwcPVZģnnaPKBU0ߍ|U_|Y4,ִ`Zi>6U_5UT{>=eJzƿ|5Mo%>ѯ5 [xb܄'cƫφ?~$k$K]P㼐#_[-ht*Zkv4sI_>. jz&l^i-lʅ;b?s^?g/*m?zCx/?,,6?m/C*+y S=$J\F ٷ~? ? SB? >fK|^k[Ki ZS,8xfz5/X&fV6zv;_]OB&u>bqT,F2|uo.cyZ;{Fu$j̑")(.ݧLѾ)~j9Q|O}NOkZfY6W6VºuGgnsN#AC('wq{[E_/_,MOxrd{4^5V mo'u)61A3O+on> IxO7u7>.,>fc%s-O4A(#/~xG?>+G{,_k:]ޚu hF;r+JO_'W-Zs^ o<;gu,mUml*"CQ][]v}8 K@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@-qvZ֞3?>.d-#[? z}ym%K9%б-f(`Xo I<ڶKd"I}qdTFrYG⯅ !_ 6`-cP]YYPgO5??j(M' uG嶡%v4Eb TƅM )R?ݣEr\|^ixv>(TS"0K,i|_sG/Oo xgE *^j޷iVeOg3zl<Q)Y8?/ a ?KW/xjO/zվolE,RwF}o4h//[|-<9|@٢^k&m9eHՙ.p9 -Nە-Wlw ]?Wÿj4_ i:|([kCPcƧ!U$Vp"s+_Wm|5} I9Qm{^=2.-Y${`ye8rp 9{A_=ivs@jZoݣ}ɯtG=C 'o :o.b~Hs܅2:I9}_?֐`Wߴ_~-xO×+iH]Zɮؿ_+ύ_j/mψ4E ua_4O ڵH?~5kO:8rzl Gۘ ]u~!h noƗrmb5ǵQjᱹGRH6>)BBtS(((((((((((((((((((((cqox5xLK q-svXxZ'c9$y>ycg/'>xxg x;zi-Eϩ^Nу(#$,=~ =/Ś5],71 2;%Y?~x~/Կ,nmx/r [h L F)Z}|uo gtXbcG** / Ú'}cO/0[I.ƭajtB\HInVQs4RW3??/X~ ,[xmGD)5 _־.i:q;ʳ-e\o'bmeVY w>٧\Kk⿏>mxFZƛۏ_]wԦKf!ZvX $ƿZ/Sxx^=GI<)=~_y_BfgkuH}[̬jMUᏍ^ ߌ;úQwkZ|77AX2!Y  y+#m~.--6spK%c0Fe A },{\G"MN(((((((((((((((((((((y"mѿ6GcH~$|7Ooa:}^7k2c[] J WO>kgG9t_i]$>[83"3G5ØHerHv"V<' jz}o}RYmc0F|J̫'ҾdSK~k_qolK*E^VP AbHUe7}'~:q:񶬚ψfmK?bþ\CQ=ԏhb TfFGJ)R |kx{Vږpַ&7+R- }7e={ԝk7MΟO~!iO u(R6 2jWiwݼJS/g? jXu  6y_Vwo۽s)şŭ{ź5{ J馵w f`{4 eZק>㷸K#6Y"C#!  Q^%)?Γ;l ln w m-!gsp9CC "fs/Wx' yu`|⿆O?cegz-okk-"XI?gR8p|4~|}jWּ,-.KtdhgMOIxCcq3cR~FoojBiNo '|+,'1WW^o vz Ḥ.l0'_ցmn:烚u78&+}ku[g|)Xi&yզ avn8V1i/Ɖ&麵+4&m4w6w 1I!RpƽC<׏zi`]¿#ּ់Z?ޛ-wPLj/K^P^-6ݯm@?HbYU6tTɺjBM2ds_9JjkZ|3qs$߈ hr1uda"JacEfc7}|- _Sº%9UWt=*A$ .ƜM|zxGhmgLѴ/zMx^3}{X-ԳI k{vQ*fF)y o @ӿfM6\m>!I-e&D,ܱB'͒1g5[IOO!O-䳸I%IzP~/ҮWg> w. o ךԶ3\3FU_{i%-TB(@,[%[-_cğE_EFM([5Ɲʐ B9Aw=$n+ZI0ï|?HĞ24O xwP#i)k{(ogU Q 7fGUS+#OKH7῁׿ +.iͦ|/!|7i{2khS'I,on$DT0AGğSҵ'O[Ɇm&ċ(B8SU+Y;um>_UGgq/~""V4"LԮ]XɧZ-#-YY"L}l}3j_ OߴGoC׵=o'[OQMEm ְ#Ώ}%؟foWZ߈o4] vlқx&fjg1}oi>0m'TӵHaˑ.RuFv־mn_?a*_,tV.;V\?el|wm=#<|i~x/ _xxT6-uV5Łڡ~S2oMōcoA CJҮ5.@kzk-YZ[uIdI|q/O6Z\x@+\[sp J:7uM.4g'ASmB4c;K0,~AHI}ZI]?Hl.5 RkX/n%@2"D;D\Q/~Ϸ*~"0mi yscJYʢ. ^ )௏5ST:^h5ަ5͝O moe~$eՉ@bwdж$ol-dh$r(dH# MY~3e<#kH5,Q#;`w;TR{j+OZi4_C߁>é8? Jݸ 21">.#ҵ0`C*8 @`yo+.y?_>.#cӌ; W/6W/.Mz]\h/<5}h>5=׋+Jwu;Ӷ!W:,=?~ ߂|/vəMObª$I5̟ ?~k/.,4k&}&x,/^/9Y s^CcW^&?~2%$w;QT@ig`[ WW㿇f jj։ms6оc"U]p+nxO֮>XbmlgHFY< 7юgnt?ڛo e5H?Z=Bġ G ட xvͧL|u-[j:<9q\^A+4 (\#@k׿e(aRu=G. I5PV+̀p3z;+:}gizwv,pȡHIWFR`H +@( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (>q>1if-Uym=ZTtxu_Urt )!Cm+V`,no=@|J]+X ^i761C4WpZHlIGIWO #|1+|G_&4uQaEH`"& 퍍qEq$ .{XM*ăXF]܎>҉:PS@8S8t(ԿGIu_<~?χ|| |k[Y?MBJ,F7 v>S5{ij7û|ޓf[]"K#wH]xI#+ڿOFkf{{IӢ\qмj͎p qںZ1=Ztk 5;sbL,bTx؆*˕#J-ώރkOx?-<7xgR57t.d%%k"PY >uknm[ƲՆA_@?i/\|4cl H<%;7^/ey-w1Idz~;a jT`ZI;BVHFD?e_/a5L9ATU_Lյ^-s!?|<]ׇ񴺞wkMv 2%QH˭⏈_ |exSx~6|+hc_i.%1*C$Y,H$2U$Q2qEr_}ySsk;VM>f,*@CvWWv:֍¿:u 럳kI>?B4G Rw_^nIJ|\e`XlҬB@h-S?f O ~(4+-_I%%]B:ą"pl)H$֝ooQigAK_AM?O_4?˪Cŷ^.m"i㹹yg[ٲy Ma@Q@i։+ C_|h_xw[^od֞G4EDloUNv-Leq]=h=k{6ImqoV! 6P* s|b״_w|Bֺ-HZrٕ(*:[ t3~QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEZW|%.>"G᧌5 o|H }XrZ5㼠^N!%6_|#@!u1iǍ)x6|Gc7oxVl+M;V\]>%;Q[?WwF|l߄ݻb A; ~?ſM+ĺG\G ̚nq,0!=_EsQr8(/˕?_ß>=bo6zԤ{2Bӭ~"uXBڥ؀~Wo?#/ |IῈ>!w~h$os*ikgpy..c[7X(? Wh^Bo-w cu&:#7uM.Rvki77#^%TP`OZғm(yQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEm[Ekkׅy5=FkyȞlqʒF]Pjۅ#JI>0>{}Mό:׀(Ğ-]ƙE*j"?b8ph\Oy<|d\DQ_<|3~'Ȃx{Lazu:K%r`4t_ր|(G}%_Vk.o$'<+|Z_O AJ_]Knb`iIH8We>2D>k?OxV/Cځe6R#ʠ!>*5㿍=Iχ '[^.- (![Z)@$y$vhDT]}y_?:_}3 zčs?\l.V׮lU#XO ,m^;g s ?|U/C5K/, `E$33&4oh keZ-; Rӯ%6<#.Cכ~ܮi ?j.i~"K @Fծt-E8_wPtҊ((((((((((((((((((((((읪|{<Ŀzijֆ[+)YVxfX"?,HCG][ c?|TiM[Vin<j4=ыi$U]3Ks)*`#țeo jn6>L N)POc ujZA$qG9i$#eO_>xO]GZ1߮-R dPrYrzתQFWCJ~*7s|65OMK ;O |ӭKgqX-} IE7o:'&չ5O^2ԼcL%#巳N~e @$ (

%m|B<iy>NUfI_4?|q#/+g(?oߴ? c]{J_:݅խVYjoqy d]x ˵ʃ$Q</L~&7tڏ||W/|G?jiz`9.,e-TrodEm.i~Ծ5>&Ҽ3OVb}G(Q0? RYv_[~GV/? M-xvmZ[44/W΁IdLK#@85X|o|3>M]t ׶e$29HaWV 3U$Dm@Lags EC_j֭_I.+jĻIu,5;TQ@(57|oQ?Xx*Zӧ0_v2Csyz1\=*yE!M| wA(T~'<-iן(¿>|Hދ h~#t?ۼ`a{yѼh.c@ήEO$޷k|Xڧhx_Μ-㶞b^[XsWnW?> ~~<_:lj#&;-E풕e̦B\+wGc&Fv $_jg#Sm.eCKYdW .}E#z{6~Z֟Ś'&hZbj3G+VXd" ~~~^k>>+_Lfu[J];Q]?gތc8DmY+=w_OE((((((((((((((((((((((|cյsb=|x:˃Ѻյ~X&86iPi6_5~7?$vis]w#z5$\&o*Z]qvM )"Kiw?dچew.xKծ|;_j[_x{T Mm#U9cfIa)s9V?¶i/}:eΣs'147aی9DCwn?Z_uh|IOv,V7qE6̑I,Jxu}#6:׊|OY~휺]goG=&.Fߗ4_6|%~5|wWC~ ԼsOKd|xlF,\LJCP⍃~2*=1ks7?x_@ԖNG6Ec65vA!M$#tۋvЪ׭]޼C|ISOW>(nO2{5?R{4O<72ZZ1 p۲_, U!^~o?/wzFi.#Htđi dr"dWh^kz֑YQKx.-{h[dp>BLxʪ(+왤Ҟn|ExssPic.!h&DU)`::KGq Kru=ͤ%$k krȳw'sVUKcXD(D+LjS%|_|m:VO* hrjڜ afA$ZIPvdͿfWڎ'-Y:}pFHdu`!WGUeee)jwoo'Gy>3__|{궺'//|;X6[Ι5v)4:˚6a vm-ώ5 <}rK_JE-#\aԘ>\o%el&Wht^K"~Ihg aQcF(E#蟉_4_ K<9K)OOMʅ$V#}0iɷ[R򷈠Z'^6,5>g𦟦iY-iym[ED^t(rHUEsy+z>)|cagjLѮ o&阮2V) d o4vݢp7huVS.}\ Zqo.xBwU _A4J+GD7mތ * D5uag')~_W _wi 6~}#<Yjk#4񧌼 ^ Ǿ ;dg֢`:HV88䐏xi#A|7 NZe[~,}^[]Z\ )ʒ!p]YBѴץFȤoh }kVty-G Nmvl}KU[I-A EQ&O>SOx:uMPQXFNXJuu<`\]R]BK5DH24r Q'U=koGg)W= xڅג翺R;sq3wcVgk]]~3mg5q\~= |g6?YywQ/Aᆊ#~l]hr-%T؋Nm~ͿgO>*.d' }bPdڸ& k;KR!䷦&@FvjvuuIn఍INy/ׂbW_ϊn.+Ze;Up5auN5[x?Iz>$wj6./L s IrKQ@EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|Ikx_ 6s#e[>)L}nwawXkG3v+ˬZKKMoT8`Mrp]2ə\K/'σOm[&>hـcmp(Gִ?hKuEφ.EhumJ8YT[Y YDʊ?e_3P+WyOM|]a CTm݈ &8̲KI>In7mh&$ok:i4[)~[- PD2JڨLo@5~?>!O<_+w;&3}[ry*{y=q&jiڙ\5M>#Q^G-Y xo~ TuI#Ե+$\4(6%g gѳK5~ڗxo[hzzuckeXT3||5|/@EVFZOA&f!xaF0?Jl #T.ȂY_EcVଯxCM[A-c59Y>$fV#+@[|~ox+ᮗE-/G52$}< i8Ix>>^,WM׆l#Hp夑؄D@YݕT@ۣb *iօgaQ|let <4Ioe fI xKw:L$*ox3R.5Pi1 JXvXy6fMۧt.+|z?'ēKOw%׆A5%Ym;;K\l01b9_x`B~!Cĭ{Z*-4[M%Dښ%e$U˯ 6J.}~>|A&]s\ 5 SOtxNv-˺{Jj9S|k,5Q~&N~';;oۚO'xEtk .$kkq-3Udkd'gk[hߏ4m#ė60w\uI HoyU %|=T_iEZ]]\.<AHQ{~0ſ!/SuXM6Kahw#caZstߏRvQ?B3Ed?T<+_ٶ{yio# fbGZåW ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (u5ɹ"!A| _Fڶo@"Aiv\PIookg~8x^ψ<;[:=Ymv);UxQDQUe? ɾ4 MYN^!׵ o.nV6 ` mcm<|JxJfn1&dgnq]EԴ]CM!K[YVh.bp$GRU9ܽTjh\L6! YAkOwZTKy-/,YJ>ekNIo{OxV4 G$^K.iwq$ Ā_"61^{3[O%|5K^ ~ǿ;|"GٻJ*l_=~Mo~/ռY/";-'}/JizDrژ #Kx$9C*(;jP~5rKu PS^$Jؤ.I8&_Cþ;jr]kkhX仄A4X;CdW`J^b@|=-Aϝ2p4XtԴ:fkgiڍ]Z_A *K I>״+lohQм1Y3\۠L{XIdDNSi Gל迶Ofq|Rw|By"ŷ`xC2X}t@bjp@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@K[./5 f>jI$QHXƣpBcV,c[ ~mϿi:g>Khwj=i%4.2H,s~$kwV?l6W}u|6Mn[j-'tGEo'? ~3'wޓ%>tC״B%1EYX m=Ԍ2QwIEei~ɞ(ښ- [x-UQ&lV51 $1.̠rJ ~d~&gWGdx|iF>/DLȾ)6?lmG?f|AOÝrFo):.<%ާ+Q^ߏ?e?-c_Ծ!|/Os>}ꗗ6 o iGiDk!!cBy4Gm;~z\O`|y㦵7jZI>ڱ{7WuVy}6ݎx&F#FkO _|d_<}?o~(|{,/JhKԦdS }[KxmpO%}E^<%#+&/Z:ri~$0$ Yn-VWG- do|?7o/ 4YzD%:/5oͶ'i+sHZ/G<; t)oYѵ-&:5d[Ɋ$bg2,i._;끭zt ߰35 ]B{'̒@!Yel}{ixV:|34tvnl_9Vw r 5g['톚+ gCc&o3l'n*Lg77~_+?]`ּ@m&uDd.2LCW~U-4xA񍭯uP c5Y]cYJeUV'f> Լ+6$vtdOkĖ]LIG||=߿ /> .gٴ߉4cXM6]Y0xH~s,Fb3-Q/?Zr>~&Ɵ~F OBhl6;[[xB"(ÁU' (AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP9o `\.w¿>Cx@kMk:V;S, <ұgOۃuqCoq*=:h"1eV `Fr =R>ПvC@hVh?//|'.al}k/ Sf&(aH%TaĿ?Zn᷉3yW0 Z Hfˑvx>쓾޿/ F}J 6kY*F ,I2Iҕv_xSߴgۡ>2ozb“m+PK =Y&4l% =;V5oKN/gH&Xm-˒f8E/ը=ݜx9x'6TO{Fk m>-E1F uFŐh ik?7O5׊<;uA>/iљnmoZW,1hP@.y-K=ϪM֐.񯇾c׾&k>x_Lmax\5fF`'h"O_[q ^ ƞ$3z^CZscY [E܆\Bft @so7kO|q~Ж~<)=V zm%KV+XYas*(SF(((((((((((((((((((((((k o-OxUZM銷][SɷfĮe"v'ֵhYEo>.x׊^K/VmYKYZ5a1_0cW!xcWtohp].;7} !e+wRvZ$?f;Dk{p_T:֟%/ ޜK2y6fg¨_n1%mm#.⟋/ Ox[/D+P˻662ù&?.+J~;?B|y|7w*j_JRm p.$g1 68|oxXO]k^%g;]杕%UI$^5c^xoᯅl<'7][Z..6Ff,R8$AB0=D;_eoYc3 %¿x[|eukoaP h.->[8mve9Ϧk~6~~7$d+#SF @R0_BVti?bUfoLsN_JWߴ"x]' Ԓ_4BeUO"Ti'٧G;4_𾗪]'a47+jp?1g^ noNQ*%+eG,A$(5^g-G~?GMB%)=iwRˆy&C#4U4[#|P]& kG>7M 5{$Ao/?]S;nw k yz^K^Ճ*\E ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (0>Y}Cj^I\$>|$Eψg>nw\9yܜW̿VokgFx;qsk4=cvI 0xe9^9>vY6"m$}?$x#I&A񷊞j H/c쁊{ޥ* Fi qmtՕᕕA!mxaU!iZn0⸹&XcETUۻ?O|⋯7x~A/Xi%Pn i&Ib:_Mg=i_~]sXvcm[e2IxNFQJRһ|3|RjW2YiMEc$5$+cy6VLEo<|p} {x^ +!5ݥ (2fd+|a |1iw ?qu;_1yim6Fhxg'6<|O A,dd, FTqO/s'f6jlٶ-ZjA4O9{E|k/Smx.qֵ[{ƁAPӮt|=c'(Ӥ CH-f> )Ex?Ru/MZwVpLW6lL}Mot{&aui Yykr鬒.Aq5?j_ _ڿO$~36_V~~I;o}k} 6+H XȌ _Aɯ)iV-EqH 5_)smג}?4(QAAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP/~#5#>6Q/ xk-/Sbg,kqk"mK}#OhlmBdhdܪ}ҊҲ~߳׎>Vzo;{` ֫c|{|?߀ ~coàxXO[I(,NeolB6,ig NQE;qZHȧ/) l׋ҿַG[L_|=,.֭3đ(53)%C(':KO5lNk[R(π,R_cYٛ^|3 {R_g]wiٴm3>ТK i2AuVܪ{p2=>(((((((((((((((((((((((/ُॏu[xEk"Hnf"5i%FV4Vf<Me џ|[^u՛D'o SY6²aH1)i|. jvW?H PD;s]?ୟt}sy;׺ӴK(/$PMYoZB!GUUŸ==x_@wύ2atkOj3S4:KexyXW _~KacᯌWal,KA ~9D.-g~2><|MlMS_%kjKKÚާqwx%ԉ[KBYrs_H{K !cz_0Fڎu Ѣ%嵶v7| $+[~_)t`Y:<C/O.uygopTo41lE2IתmiZYO<xUII3g?4okׁw o[>޿hZV\K1a- 3&CS ğ d+}K hYٮmCY\_ihC'7 Npq\o~hxA,>x.8:ͰF[PwI@eoc?"ͯn>xR/B~y~yY F%1 [|քjx_Yu,k+hixKUWX3Mt9 G+ooTW~ؿΑ?Ƌ&u'{ $+v2"BIv_5} _(xRSiAoSO 14Ŗ߉w76kuG䵺$P4 >+,7Q4_5<-4iz&e'[B XɩyM:+a Oύ(?i>/-|E|1-t1'|o:LjͭEi={9z$~ n~F<+efo )]"iv 48uJ]oIC]v FiZFդKyռ}m޶/!S#/^_V ~5xLbwhv 9` @r]nmek÷؊ ?ijj_$ń&(wyXPWTi5%9;efQE"((((((((((((((((((((((O~5g^|YIGgOdbyʬ\d29\O۟w|'g&ki\fb9h=5Wm)~5~_ i;+_]-K8|I3 3F'GH%q+ Ҭa+{ktX$ HUG0QEXxGKu-\tcE-dK[$RE,N n+$Au;| ڼ{[3Z ŭTF #()1|7O_]JD 6Lwn ,p+P:|?K]/6V֞"mH-WDU Gz(N-mma5a@Š08U~xVӴm.99ynE.=H s PLwQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEpyrgg-1.6/paper/paper.bib000066400000000000000000000015051471451651000154110ustar00rootroot00000000000000@article{Zhong2012, doi = {10.1145/2370036.2145855}, url = {https://doi.org/10.1145/2370036.2145855}, year = {2012}, month = {sep}, publisher = {Association for Computing Machinery ({ACM})}, volume = {47}, number = {8}, pages = {283}, author = {Jianlong Zhong and Bingsheng He}, title = {An overview of Medusa}, journal = {{ACM} {SIGPLAN} Notices} } @incollection{Chakrabarti2004, doi = {10.1137/1.9781611972740.43}, url = {https://doi.org/10.1137/1.9781611972740.43}, year = {2004}, month = {apr}, publisher = {Society for Industrial and Applied Mathematics}, pages = {442--446}, author = {Deepayan Chakrabarti and Yiping Zhan and Christos Faloutsos}, title = {R-{MAT}: A Recursive Model for Graph Mining}, booktitle = {Proceedings of the 2004 {SIAM} International Conference on Data Mining} }pyrgg-1.6/paper/paper.md000066400000000000000000000024151471451651000152560ustar00rootroot00000000000000--- title: 'Pyrgg: Python Random Graph Generator' tags: - graph - script - generator - processing - random authors: - name: Sepand Haghighi orcid: 0000-0001-9450-2375 affiliation: 1 affiliations: - name: Sharif University of Technology index: 1 date: 18 July 2017 bibliography: paper.bib --- # Summary Pyrgg is an easy-to-use synthetic random graph generator written in Python which supports various graph file formats including DIMACS .gr files. Pyrgg has the ability to generate graphs of different sizes and is designed to provide input files for broad range of graph-based research applications, including but not limited to testing, benchmarking and performance-analysis of graph processing frameworks. [@Zhong2012; @Chakrabarti2004] Pyrgg target audiences are computer scientists who study graph algorithms and graph processing frameworks. Graph Specifications : - Weighted - Signed - Self Loop - Parallel Arc - Sparse - Dense Supported Formats: - DIMACS(.gr) - CSV(.csv) - JSON(.json) - YAML(.yaml) - Weighted Edge List(.wel) - ASP(.lp) - Trivial Graph Format(.tgf) - UCINET DL Format(.dl) - Pickle(.p) See README.md from the Pyrgg repository for further details website : pyrgg.shaghighi.ir ![Pyrgg Logo](pyrgg-logo.png) # References pyrgg-1.6/paper/pyrgg-logo.png000066400000000000000000001151501471451651000164220ustar00rootroot00000000000000PNG  IHDRZb.vsRGBgAMA a pHYs]IDATx^TE֮όP (Č  0`#f0 f1Y Pq`BI*f翷oM{t<>O=ݽjUUV B!"O!Bc$B!򄄖B!DB!ZB!yBBK!"OHh !B -!B<jD߫F_ߚiy^z軵tҰ`0eʔ駟s/.m7zj8:c]B! -Q%T Bf޼y᪫ w_sEvQ;;Z+kQԘ?~:ujx믿>ᅴȣO?dCb~I'dB,*BbCBKG w^3gNf=1u6dAJÙO?t3F0BN̻F'zΗB! X-^8y4OM.rv}pg[guqAUieTCu]y֢bo ]vY8Sl[5% !(6$DY&M w\裏َ4m / vX?Qk˛jzo~[k}y[7t0-!ń0\d1?-\КꈏE93¥^jڣxcѤ><}:t`}6h#-!E0~GpդIpmY5MTp1r/W^y-=Mx܈EB!ĊЪp".V=Q. {ænj"Q}r p >pGYf%V[m^|EfD!DB"7;wND^oݼF,T\"3gD4y22PB{BN_ qQ5{p'4G O{Bl B>Yxz- bQB! V-裏6"6Bb z)E:lv}-!Qa-Û 8z 7$FiƦaNBDkf>k=zh=tB! Z,&J_5mJ[/EHFE1c,*M%}-Ta"(߈ "*s纘:K.ֱ&ĉ'ڤ\+B$Z <8L:ռ=:*y%r[:4h`S15-b׍R-!5V-&}&8H!_o¤\\Hd/M4'҄H4y!&Ъ% Z~LnauZ11vEEB$O[V^SB!D.*C<Ig5o~gi'ӂXWkT9Y4Fq;w%KŒ3lB| Ufx,>?IJ6(&(r22J|Š03onS@B!D*Sh2Dhx=;)ZiuU5 묳M 4B.ħBV`kffͲ*Y0498uR-EhGk)%"Hh;wEAw}rO= bp5WHkVŠp.TV%>ÜBmaIJyB&$ ?Skիc:Wk\9 80!P33.؇H㹰"m}]/رc#QPGk‰'h"e.Ņ(E}[v /'O!D UfEHП {qQqXRM| F7nf4QO vwX?7kY6plbp& 9Hd !D#Uƽ59sDf__p!5c "/d+,vۼي“u%XBAO2 q6m lyQU"`FpE߷{e]v.%Bu֌H,. eèQR %VΝ;'Yot8{^x&v~g"bi"E|T<l5#W~Jo5"^9ذaì:cÍ7hyLGɓe^\BQ:Hh1GmF-6llEFu̜R=>T|'\slqշoп9Uv(}0~+IT"s$ .!( $ 2oDKlP ӦM刘::\bߗ]vY8SMlymXO`awGqDxcJp !D"U`1īz8;bJϽ袕7-\s"F"de}nyMQŶ74o<\tEᮻ cƌ1/sцzm$9i$[-g,VšO?CEέj]dJ'?J& a2n8x/*$*ް>B$V1W^y%t#ɹ?a]v rJ>}Jp !Dq!U渐S}M7T#þk}8'GqKɓ' $p-Ƿu 7wy4b:zmQF>!5V-MZz2G֌lAø,dٺ>Cj:\Y{w7oq7 ̈́|_z_>#M*E"߁C !DZt=km`1ƥ{臆g_~&6lmᆶMM VZgy&ԫW/q_ټE#c1Mo]ҥKK`;뮻H4{uj['x<.=m+k .:|aߎ`.\iK4'2gvup=XDB?pgZ #զL>c,^8?>tD  ?¹"^|Ek2t!2dCCPP6lc~>/6mjSwmekXXfL)_lk6}(cA9?RBQJZpwڴicMJkVx,a.*&\d#4ѹ ,6UEB:3"$}'m„ ϶xx-4$\} !Ck)(̙3Cj4'b> o1d\ RzOTCl²X<׿e}50w.򛼡|}?gOi6$oxhB;#"TMG1{p=:]MB-hRu}>|q"l#ژx{ b.YD~?C" \Ν':?+^ʍ$k&M,ѯ- .Ą"$j9n7tww{ #qq\ӆ (F=B(` 9 'aY"s$ܹs;_!xzOrʜOM&FpсUp%_,YFac >X2JxmpoUF_7BV-cq TxhND\!b0-[u5ah\HnDVs9f8?H,^ 5o6}7"e;qd^y5p~x4_}U oZ<.&^W.O(LDH]&gO/(ԋtЪ:u c`0L([rxn'Z%a L 7WQS-XM &D y' "Jdp>;Ι3:9!L>k TI\S8xLzpaN8f .!DuHhB(ra\0:$xu]0: #nS1:^'+,d$, "Ɠp.n5/K^nxx0g$O;5)2\âE,@-M|-F5kj+ zDBHQRLЌ'|b^Y.~ܹY1z~Ba e }s9q }α_~%7Tm&Lc=PC֭Xܘ+727x#?ɰ>e~d,X{c:t jF%ַow}g BW,vesR\$vl Vnh9w,.aJ[zbݺum>nX׮]cuIqpOą\쮻by$jnFab !&Md\- ƅ\nx\s[l;cߕĉc r!_΍rE!S?.QQոq_r)ؗucǎ&p8~T`j*v饗ƦMfF ~>'ñ/XxO꾿P7On۲|N!D頦ZDopc>4čE?;}R_)mhahsnjb-v '^{sf=Mă5mʈ$V7~,trƣگ_?;^ԛ7K.ŅmxU?oZh6߿5kSp'%W!j7Z7^{oiѢElf YQ{.D}cEG$!0ے0GqDlȐ!9sTel=="Al80&pER;59""\ ftbuax۷?޶me7W$śok׮qDz (%DEMCޚ>]DqChvc2f;6@a?&M[4 HmiƉK'*0cnyDa)x42* |gMi4Q^ĞɗDLr$ؾO͇LEs۲u#z-40 \s57oU<϶v!Ux/ A5y"y~Y¾Ʀ*cG"˅V ޟ' ؟ SqBG}0l0 @_C!nH._#6"z=pr-XX超ƅ! 8EHprOKl QK*s\D\xY￿Kyg`?w* \9\h̚5+ <(KgoF0,BF+&}U' εp^x pB;Xo=\ 2ߴ>Z,AF޸q㼾a{ժ)>顇2&Ixz!kwU(޽Ϳ"k6 >lhӦMך>c~EKit'J^QUHh).pM(PьALC\,_E~a:CB~\jkv_}Y'9|ꪫZ. UP5L=bp zЋB!gR^={ M6fE*g#FXRYQlH^p9k׮%Hh!h1gw~k0zhC_M"$ .&+GpKۛ1De'˼&?oCxCH|w-)y#(O$ Ytt?;EC~YOЛi\{x'FP/}#UuD~gG}E׽(D#UF1)vfC ;g=E!I\$1[xoAtRēk۶Śc!bQzI!Pի-,]y @ě+^:{뭷2֗q|%\*/#GL4jʚ`xKdbqA+l&ԧO nZN^oI'dj|E.xBd͟?߼Wѣrou>D7^ڷooK+ CIǎ;BCj*u]g}9x0cZhasahG3(-/йi̽Jjٟ{o*\d7.\|exăZMA@P7aG [[$x(q}uY"na-&@h.,s*"ix≉f ՀlP+^iꫯ ;S}] "~Wm-e //($ny(kƌ`مNseaa…7 6mjT 3aPETxZOc&fG(H!rV"Æ [n#< Q`Ex7S>c[”_3'p3|*Rɓm,2ČBLD駟'MfLdPUfa鸵2ǯXZ|'qВGK@V,oLC,oڵ E1o<6Th͜U~͔f-V3z&<Y7ا偄VOՅ^Əo ]t% qA38#f|Rf% ŠHBAֺpO?oS`XF̡ۢh{3DZ}w!rEߒF/PQQad(Jwt!GmբS8CϑaHT L^/RX QHh-gyf6mZ"CNYg~Yl`, sɒ%;SR頞Rb˅ʊ$/>W} ! {;$JFoݶAVD@'ϙ3~tPH]j #96G?7 1_s$ "  oo~UW~+׿B.]d( 4spV`iRU.ٳg[bŀl[ Z=U,em6\ve&FTSLTl6lg^(f='+'Mdެuo!Dy k^`x2ΠAS)vDP_hv.QrxH5mΗe5ݻS3d7uxu/X L2K`œ[؄hhxz-#v R2 ?2qD++xWr#?偄V P}]8+kB ).h_:t>-N&vwG#u-]#Fsb5# CFi^?򈗮Ν;r!Dy!x(Θ1W 0} 3g4BS oF E:Ð!CR[x"6?hCM6@ʴ^R<X\7B+͆ᄏm.! Yjk_@: U - uu' /0!k;j8#s_կ_?xv^4?>=:J3sJH}qOl!ϔ\ЪT~uX <016'O`>ШQ@Q;Q.(6F>r 8"kv|D/~LD`4^/9T0~'#X=_ +g*1EIh+ o.M# u!DTs1׋ /A\maC΍wyֳ}&̞Y=\Ta̘1@-[zx Q~议<0(\^J!hYe/*UKӧO:Bs1/9׉7zE !ugqnIz(g- ̀F5\csy(VZY'_&el+DNk&.E=P^3 $۽{wki<]mvwڵE“<"iӦ6SS' B/ZIf½ˌ2`N__tرa}d+0R7;fn)`ԩw4Q3r>ߣ{IecI!OƵ4o<}(rFP^IQz.Q @h?#+@>ɗ-,^8֢EX V[m5lSl_bcƌl27ډ-Vzfz[{myt]*}~jǏ Klj'hMݺum۶K:]hQlmS]ga\v w݌ ƨF3όPcw.Yp=lyfkD텺SU8qbK.mKD/A;ccÇ:غ^"K^7pثjAn%KvqG;w%vXsqE"W]ϕ~A!jZۃ1jpɍ;`5j(_۾JM\!8|駱Az0kFlWO]i^VI ׿NW\ᙙ>}$hnӭ[78A9 kfC@ꪱ^z%?_?/<ݻw[k:o/B yI'qIiō~%?xUT(?slԨQ6Duzw {L{n_!&?B'hb=}M;"ԣ>j:صuQ }k+$0o޼Xǎ-W._x[/%DCB+ U4m4alƧ1-2Oؾ.@*?>֯_?c_5i$v '^~D+zV]s4}7}FEr/^[o&Ylgȅ[_TѼFnț =B.$*7ͱc [JN'}x㍱V*(ʗrzL6͚vy1:|o*(oX b#GSwqQcGsCyqkBڇV% ꪫ졞J! c4񐍊_?֣G=:urM|W%Νkib.͓>Fqz"j{M!PQx4gAXQ%+T`}\d[mUfmB\x`hѢˊ) )nx+tRWO΅s:@ D?J?w(w~i*(7T|YU0ґ~x"j=q9&ivK4t'P4~^) ])gEV v,YgyXmԼPBp }g}f47IنD{:p$z ^8nݺ9uw:j.~ |rރ ӦMs^s5<4hvygoyM7f`Eʒ4c 32. wI?mO3gz葸U~BҤ -.43opteboƁIfA:qh"3ď=Xx뭷w}OsӶm[Vr}8DCփ>vqǒs/G.\Y{mنnի֭Xc E*&}7o^XxM cя2r-I'Nt|T tY`;찃-߯ʺLtl)MBdKZ?gϞÚN_7d!/֍7hGBNDeCv}Yz@# @?;/Sɾ_hҁ2BZ+K? &Ol=۳&\XA+W|m>׶&A10Je.q%beZPЛR Ƕ$梻FX^@yxP >4B2ƃ@cwlHK9]V޽Ð!CLP, }i.2d}T\L)p~i &*Cv]sΡYf|y"[$HPZ'r:Ph5;ې܋@!G -c&ޫ_|:߬߼;mĠ !Da,#P~jiHlˀ%;6lШQuxs,T]ƻX"~`+O?=\zOT J,L?OK.oh~BQ3`3;zheGYhd?-Aʈp!m۶` *Zf3X%)8CT>zi+\rfߖԺuphDpa&r\äIl;eI &*'^2;A+[?qtluny1yN,L1.袰zyb/ i3Jh!uÊ L/Ha=*mic2)G7'BxtCW"rEĉYA]V[nB &z[ !Dq3;3&;hEkt7Ohin!x~xBmň:Qt&3ivyi1tNeӧO"(W</☛у61I0Q^Dq[sSNV&cl.iׯravU3fsu b5: b'Z0[,vm)ϰX\yԩg؅^7n\3R<-oRUL2%uGh[{cqe췺rBQxwʞ||ꫯNlvڐN>d;?Pz&m! . ( :ujSN rWű|0tR֡m^z)v19u6l{Wm[Qכ|oV=oKqƱ޽{^yXr`5y# !HsŸ ~cgd=|psptlyָЊړO>[-#(V[2C`E3"׆}%_= Px8+re-WFIƏ>!o\Lq!3=c o2B?n'o{p֡[xO"*[n Cl/l#ԨȒ hlwe~gO>JX 6,֤I;7 Md]+'y\ؐ!Cb{キ僋/y̙Se~YB!rۄ[o!"s_~mG1oѢEj9E ĹטкAٳgׯo^v{ʭ/"*1.4sOkb\uro!TJVP 8  ɽ?|AlM6I\Ծ}؏?XqO?5qK(Wxrj}衇b|M志~̄BdN;-Ԇ]{OڻT6mZl 7kZc5x[䚼,g!pQG,Rk:9`gq"\W h<!:>Dy//L?&(Q}"+4oܖ[r*B!~q%/s=2k͡4ŵn:7.1Cy>WN:&M2WO7jȚ4h:R6?#6vq3;%'k-0$jT5v=1zO:B.Ac:ylС}'$M{%_V{R/!6PK)~-O?_#)|]w\^sN=ZD.baԨQMqoW)u Įo]w ͳL=zuR$$v,Ybj/! .國3UV᠃ |pjl;`5B^ܞ͚5+i"m]x뭷ªj m/hР_ZگN?Y5RH7&D :` t`??7f̘лw;C=4 >D7pDoN=ꫯ &.D{?O WH!Dꮻ2}Vf}}vqز\6Fןp fC&'c(ʙGEo?l'N uֵ U`o +0{._ ! +w=>\9J8+ڒ :;,^~^!۞ŋ>|fwaD ȅnj3̑mEpul/sCaУT(I-YX,+ s2,#o|{0}ᦛn2 2Ź/`N:Y.6,p8 !(}As૯ b\tVh"8/?LqڲeйsgY83hElɉ`0@Nc5TN dv;8A?DK.X"7<`4_νa!K 1B!DUD2-2@W 70y;v{T?Z\JYs~ي-O^tI]rllΈ kw>7♐#r__ڛ>}#WǶ<.3U;-(ۓbro5`}G !e[{s#nVb_3*sDvJ.o6v!g}<` o8qa=rA<3*\آE j^{5Fs% h>lTho,ׇ7 //iB$G\xY7;S!Hl`gnhBu^>u'y0rY.dϙ¦b~h>`>}z8p ۇk6̞=*5 O  >pwZPQ\ W!-D`]n̨+U:?r3px!6_Z.,>'W~}Y]T܃kTq=R{b 8^xycs jꩧ|#.b%B 6kӦM3[1x, ߝC%p ֦f͚Yxӝ""&:]".6tSS,v]벁Fys)%&ҥKM\Ank#iD1L⊼$e]مB!i6a2, F[3aU2>..4ic:=h>:Y / #͝;71!bٜX2ohl<\.(iFX!pp!< ʰQT_l䣋R+!5~h ;"'+' Ed('f`( 7,<8뮻5A ۅ\h1m+ cc=fHlYĔ+BQ~ zѨ++OV02n7+L3ȺUaZ= Z;L8-¢'ƅ=2oFD HneC֝|"JD*H:ϭOfT~ fm((o(Bd%ЯZke0kiyzL/V /fɶ+TƖUɢBf=Z[+ZķJZT~Iwz{7h"{s{Fo B!DK.౴gVx2bP8UVPz7LHh! wco8|]Q1kFk>q!-SˠBQhXk}og)úɰrʞLR;-#w=[r&&fNYdt\=\n 1&Ba)>mrbTúgV- '+EA!"\sMq*Z_dTмo2+dBQhV]uUO|~ڨlCc-R٠pʦ3]~HAggIݰТBQ y\hM:-O+sdŧ ?}":{*ZhQ8 &T4V ɍܦ -!-T;Q$Bl;eEL9Q!w pZx}*[MᄈO L4ZíB\1kF:NN ͳ#y" _CgBbb 6pL8HXݿycTV<ۍC>ѩ=ZT>VT6蔦,s9ϒmF BDv^z&~r!2Ĩ piO"L#xx>1pL g6aZ[" |c0(RAdC`9>jChB f#"\`|<}T{&Ol1B{޼2$#]n&ek2,223p4rXjq|koߞ={}"p!"veDh|> 414taduIS!N6mlbseOB18L7hi%&Dž^WBhw&"gB!r!UgCB (;ZSk̙aҤI,iLɓ Oeƛoi߹~*1u!(8V&9aԣ tή*E[UNrdelz[o8n4 J|>|Т vj2ʃ$ZT F@N CgMx/#)ygTZ^q_0wMDL:~7R&B҇g9~ w_kySO_|^}]SbTIJbP^`7Up*E:ꨣL`m뮻lyt C>3%h}{%ja?PV[GxvGo*"'6v4b0OCFJ}BJXϞ=wmo`9i^mBΝÖ[n*BQpx r=l$ F 9+Nb;-H-c},cѣm;lև~h#ݶeE<#2ߔ eOݻZD\AheC\ _Fl5׌ /6f[fdO`J).*X*.Y4WD\hQQFxŵ< ?N;)~ƚ5k;cb7nB|B!JOlBǎ;`Fےۂd1*ɏڱ6(ֺu6lkӦM6Y駟nא _i*SŠXuV?( /N̘1쯗cLhջR\A[:cmy':sв O%U/(ϪSeʰ<nqulᄆyO?dBEU !?nxfQ$bE_?-{b?9q:؂ bnNa> byQN˫?ޖZ:؉oիW/6š )WB+]圮W#oV@e7dؤIbO=TN7y&iV|*){csέ</s!ʼnŋǶzk1 80ֹsg^I2BoD*yݺuc_뮋TM ۿiӦٞ]S "&Clx d=ԉmr3~-hl./9cs}9 T}*J)mW.pM$FZogё0.{˖-ɏ(Ld/lyqdے@y>*h̡%~g&)BRg3t 9&p\8c-/sc}}Nدƍ[[,_/1< )63aNpx=bF!OVŀ_]weo \3Yz'rO!&py 䛋!C9^-\rO@Ht !D~g/r-b()޽{'UeS·l_q^7` pc& /غ|^ON #,Wï+JƯܤI+4Oۺ\^_T& !]#Gosf8/nD/GqDlСٳgi\RK!r*}{yNګsO~GK6稣2C{fJ(ljO>i6k7p˷&ɹ?i*25n8gٺ|_X>G,R`ޔwI'6.#!n׭;Cc 4sLc؅^{뭷 Ze%*q%N!bar6ɟ-Z;֎;R8_ퟘ`(VŹ>|xƒ`AyZ <#bj۶mlҥ.]htꩧڵx42ד&gy&ַo 9zC{ٽ^ܜ'pB{}嗕{M!j Uys{z-$z$ZyP$#Y!@yW|MQ8yRCEbI}FȆh_X+"{HQvyU=w+~]t v[/v\3SǬ,/B+*NԽ7^@SNNts@q9.5u1kf7*OUgۍr$[?.3еϵPlT_54sLA<j"n:\veCDz+Y^7^I.ꫯ1gs!mC*2 Dȓqƅ~;5*L2Ŗ9^^\[̟{ڜM6]n,E8c GE]m}&M3 Sڶu?/~m_l͸m.]>;Mmyg[(?8<_6PeExڵمۇ^rs&?;;0vXK;~=#Yge=M)$5. } qEoA:m}@\\. 0iҤpM7z s7;&nvX!ߪ{p̘1#=:3>o9kmK.pv>cשSr23-oQP({ԩSC-5}zQOQP(!V; P 3 n_mD7E]JqUݡC?C`6ؔ)Sl<(0b= AcqSgpu  u# %c3 ^0YBgDBW%H2[)GK<ϕ'FSox6 M\<?`A:8 sBr.\h֘QYJEl4QrINC`V{s o˼X(Ha2Ih“O>ix3#.xF6믿~hٲ5 М[ xAYs _X@ahvqhڎiߍ?T7l!?)+/(?@_?a{jX.ioW(Du}=h.;wqciwf:_ɹPusꪫo"F#чoݺu<̨9ynK?MX4 RvgoGq@s v%3nBe'\fk^ŎI?O{ŋm9MO.F8'ё.^PʭZ2/Gv5bʇ %}aĉIGdFE)Fұ2JfLg/oqg^#E9omڴ v[;FKSO=:Ӊ9s駇n~STr<3z)+9sضlG=9ooo;udߣeĶgqN-:uDžWDۆk^gCDp?U\7x\A -ԢSBB0B jɒ% ^1p=恍k ̏'\sߞE#MMY; T.Y6]BH`+qCTOP,{Ѥн{0tP:qZу9W9quE$Ϟ38#w}ˍӧb}|\FqEH^ 53o޼5ye.!<QؿSM3ZFB7-*Vh9<8E2|וkP[E_>83 qsl86pǯA O(^hEr}}RFQ!#=\y?YΫo<]{챇Cx5o܌0'zR<+zbfAܢ" rZ@M\?RI :s92)@%%jsߌ{:Ca}b … r('? }sq^{52!rCI d!Gu!,jA-?)$-V/]:^<dKTWz*.÷/zCs*f1) ]daDYVSe?-=yUdD  #G4!B/q+~#k/oK"ˆ*(i%j'4.Boh"o<͍4)"<ѷ+b}XGߛIٰQ%F"3Js!Me"?_袋_xeLa !|0 ؆2qyL}1Ҟ-m)D#%J0++c= %9[Z\ J6^xX0lذ7Thy{yO-7/#_!}迄b1͂.s8])mUHZ:&Iƒ"PHL ǣoʌi<,"9>n6rOU@~.ch;']](FhI+D$"Tb<UM42l~̍,G}Z />#[IT\6̼LM38Ɲ YH -Qk1Hv"Ľ]tGCvhҔA䩁60g:Gy$a;0_SM=hFoޞk6}ى&B:VT~ѯ/.F*'MttLJ} NJ>4 qE7Y }EXq_Rq ŀIQ>BDyw0."ZGò>)x.F,]ܰ3Coof'esTsÛܢA|K,1Šp 'uWW/WsEtv{Oosŵѯb͂~TSRDBKP_xp"tOf(F*]F>1n ֳ%Ӏl7_Ay4 ]\ݜKZ}`+bPLu9Gϋxo<(0q ~2a%unq2B*ZB &j<ORH}&1dӬD\ FZ1m(Ɇ*\,BuI Mx?ϋWh8ꨣlnoeoq4h\ ö́\3K_\N4)Z{'sߩ1nD'x-H,X,;3ž&辨^OS+! -!r #;;gQ[7@1H /jl 9=N:$3rˊ0b?zM}cY5d¹away҄ח鞷ohçzʛZ&LQ[=s⁥C;(#a[㓺J]H$!H\ g$V #7}^!*D͡ ETNԙgit1"ܘ7FGy/[x| i:̕iȫt<#Lբ<|J#eJr̚5E\?^mS%PsG\(WB -!jn3hb/̴iOwDIū@sO?=vmv H =3J4A6"=54d!kG&Ks3&"&9OSpKg+J<湢cG3Z/#SOOBqc^Atav`d.wQlDwuY 718ĕ8_%LWD!Cgi FZ%tZ#*cf?=29wSw\x!M4IO΅Ռ3L&k:#7_x!X(Lۯ9Pڄƍ$$DfϞ=;a0nLdp ֑RE :<7xw&bH% 1kph׼oW3W]3Z|HCҌ39/G; eJ8~+vj5:% -! 7p1Ge`ĢBE؞ DD\s5Y\@ ޣs'-F dJ]oƝ2#EPF#%.m en fv^74hb\1 ~ 6\藸"wHh Qp{b$1zɆo&̙3@vܾifª43̮;QHCp]2c;+^L:3S>YmVtM-}O"7,D AuQƸQ믿6nsr@k dDy!gGSa ,7ef&|PLo3gΜP"_/eγk߾:CQ&b?}c[D-r9$|B I#,!򏄖%d &ƓnD~*rZ$s9m ؅V4 Ec|yDR`# 0}њxtrm(38^oݚ W$q%D"%D xLJ&%DVoNq1-G8>.(Z4΋4gQWVad9BE?OfYLŊ>S& 7~7ӧ#&? ^ۯ_?kJsE9]uֵxP-!Jn]79Gh*Fͱ8>" ._| .z|4qkܸq)92tQY">u+2$\zMK/a1Ua  A$I"KCBK!3r#Ol/F1Վ#lA6;4 $? xXFs,•E $(=$(10$Fa'|IyHXdۢsv<ǃfLެad, /\tOLLPXG͛[X" }Q;>N,緋ԙkЯ!#  <7p}_XZ").`MhīڎMhsE'uқfǣWMSd!F3fr:0y7b=B@BK"[DqX>6 O'JxңYrX~q} *>'&8$p)0BZB#v[ E믿<;45rXM|CCvC>Z^z%_/(?tg !b/ "¾;vlMXi%­"9OKW_}վӜ7䃄l!D&ƒ:~Og^u~sYЧ)2Z*GjB&oA>pͳH֭<B 4xڷoo-h#勄"AL/Z>īC3ڬYLtW+N>al=[3gN6l Mw {キ勼YB7ÅyŅW^&.K. W^yܫ) wm]s5aѢE&0ɋN:ɚPz!D"%;:͛CBuϤӬXMM (i`-2qvf Q.Bj!.2j]p N枭J9rBRfbP>!~-7:/PNy%'F@\@׮]Ë/hqn 4(ԥ 9, Xc wF\'KBKGBKQc ~ac=̣4u։mJ?>,.6!G-".Ą叚5["B ?`>=_ ?ix9Hd Q GKQ!!8o5\3O CZӚ38#qvy0f]\ =ȣ%Q\daȐ!a G}4\~&V-8n)r-Yt|:YB.$5*[j^ +LФx)v;Я_?dq}$"o /v^QF rYgHDċ75#bDP4CFY ݻwOx?T~B;WTTXwy'!^y*&!\x[,DzڄNMBN^QP+ad 7ܐ#<:s=C-/ AŨIq~, Ⱥl[U"Kڋ":s_oMtV[o=lv{j!LN;0zhE?O=p뭷Jd ! B$k!fy@S~rB5xMCH&Zk-;ODe]&Ob9B s 7'E\_CggrBQZBs4vX޼yl?\ EyCJ |_?t9#,\:$%""=ȏ5*L81|嗶o{H " + SOURCE_DIR) with open("logfile.log", "a") as file: ENGINE_MAPPER[engine].logger(file, file_name + SUFFIX_MENU[output_format], elapsed_time_format, input_dict) def run(input_dict=None): """ Run proper converter. :param input_dict: input data :type input_dict: dict :return: None """ if input_dict is None: input_dict = get_input() if input_dict["config"] is True: print("Config --> " + save_config(input_dict)) file_name = input_dict["file_name"] number_of_files = input_dict["number_of_files"] line(40) for i in range(number_of_files): print("Generating {0} from {1}".format(i + 1, number_of_files)) file_name_temp = file_name if number_of_files > 1: file_name_temp = file_name + "_" + str(i + 1) gen_graph(input_dict, file_name_temp) line(40) def main(): """ CLI main function. :return: None """ parser = argparse.ArgumentParser() parser.add_argument('--version', help='version', nargs="?", const=1) parser.add_argument('--config', help='config') parser.add_argument('test', help='test', nargs="?", const=1) args = parser.parse_args() if args.version: print(PYRGG_VERSION) elif args.test: print("Testing ...") error_flag = doctest.testfile("test.py", verbose=False)[0] if error_flag == 0: print("Done!") sys.exit(error_flag) else: tprint("PyRGG", "larry3d") tprint("v" + PYRGG_VERSION) description_print() EXIT_FLAG = False input_dict = None while not EXIT_FLAG: if args.config: input_dict = load_config(args.config) else: input_dict = check_for_config() run(input_dict) INPUTINDEX = str( input("Press [R] to restart PyRGG or any other key to exit.")) if INPUTINDEX.upper() != "R": EXIT_FLAG = True if __name__ == "__main__": main() pyrgg-1.6/pyrgg/engines/000077500000000000000000000000001471451651000152745ustar00rootroot00000000000000pyrgg-1.6/pyrgg/engines/__init__.py000066400000000000000000000000561471451651000174060ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Engines module.""" pyrgg-1.6/pyrgg/engines/erdos_reyni.py000066400000000000000000000056651471451651000202040ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Erdős-Rényi Engine module.""" from random import shuffle from pyrgg.params import ENGINE_MENU, PYRGG_LOGGER_ERROR_MESSAGE from pyrgg.functions import save_log def edge_gen(n, m, direct): """ Generate each vertex connection number. :param n: number of vertices :type n: int :param m: number of edges :type m: int :param direct: directed graph flag :type direct: bool :return: list of dicts """ edge_dic = {} weight_list = [] edge_mold = [] max_edge = (n * (n - 1)) // 2 if direct: max_edge *= 2 m = min(m, max_edge) edge_mold = m * [1] + (max_edge - m) * [0] shuffle(edge_mold) for i in range(1, n + 1): edge_dic[i] = [] temp_list = [] dest_list = range(i + 1, n + 1) if direct: dest_list = [*range(1, i), *dest_list] for j in dest_list: if edge_mold.pop() == 1: temp_list.append(1) edge_dic[i].append(j) weight_list.append(temp_list) return [edge_dic, dict(zip(range(1, n + 1), weight_list)), m] def gen_using( gen_function, file_name, input_dict): """ Generate graph using given function based on Erdos Renyi - G(n, m) model. Refer to (https://en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93R%C3%A9nyi_model). :param gen_function: generation function :type gen_function: function object :param file_name: file name :type file_name: str :param input_dict: input data :type input_dict: dict :return: number of edges as int """ edge_dic, weight_dic, edge_number = edge_gen( input_dict['vertices'], input_dict['edge_number'], input_dict['direct']) gen_function( edge_dic, weight_dic, { "file_name": file_name, "vertices_number": input_dict['vertices'], "edge_number": edge_number, "weighted": False, "max_weight": 1, "min_weight": 1, "direct": input_dict['direct'], "multigraph": False, }) return edge_number def logger(file, file_name, elapsed_time, input_dict): """ Save generated graph logs for Erdős-Rényi engine. :param file: file to write log into :type file: file object :param file_name: file name :type file_name: str :param elapsed_time: elapsed time :type elapsed_time: str :param input_dict: input data :type input_dict: dict :return: None """ try: text = "Vertices : {0}\n".format(input_dict['vertices']) text += "Total Edges : {0}\n".format(input_dict['edge_number']) text += "Directed : {0}\n".format(bool(input_dict['direct'])) text += "Engine : {0} ({1})\n".format(input_dict['engine'], ENGINE_MENU[input_dict['engine']]) save_log(file, file_name, elapsed_time, text) except Exception: print(PYRGG_LOGGER_ERROR_MESSAGE) pyrgg-1.6/pyrgg/engines/erdos_reyni_gilbert.py000066400000000000000000000056201471451651000217030ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Erdős-Rényi-Gilbert Engine module.""" from random import random from pyrgg.params import ENGINE_MENU, PYRGG_LOGGER_ERROR_MESSAGE from pyrgg.functions import save_log def edge_gen(n, p, direct): """ Generate each vertex connection number. :param n: number of vertices :type n: int :param p: probability :type p: float :param direct: directed graph flag :type direct: bool :return: list of dicts """ edge_dic = {} edge_number = 0 weight_list = [] for i in range(1, n + 1): edge_dic[i] = [] temp_list = [] dest_list = range(i + 1, n + 1) if direct: dest_list = [*range(1, i), *dest_list] for j in dest_list: if random() < p: temp_list.append(1) edge_dic[i].append(j) edge_number += 1 weight_list.append(temp_list) return [edge_dic, dict(zip(range(1, n + 1), weight_list)), edge_number] def gen_using( gen_function, file_name, input_dict): """ Generate graph using given function based on Erdos Renyi Gilbert - G(n, p) model. Refer to (https://en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93R%C3%A9nyi_model). :param gen_function: generation function :type gen_function: function object :param file_name: file name :type file_name: str :param input_dict: input data :type input_dict: dict :return: number of edges as int """ edge_dic, weight_dic, edge_number = edge_gen( input_dict['vertices'], input_dict['probability'], input_dict['direct']) gen_function( edge_dic, weight_dic, { "file_name": file_name, "vertices_number": input_dict['vertices'], "edge_number": edge_number, "weighted": False, "max_weight": 1, "min_weight": 1, "direct": input_dict['direct'], "multigraph": False, }) return edge_number def logger(file, file_name, elapsed_time, input_dict): """ Save generated graph logs for Erdős-Rényi-Gilbert engine. :param file: file to write log into :type file: file object :param file_name: file name :type file_name: str :param elapsed_time: elapsed time :type elapsed_time: str :param input_dict: input data :type input_dict: dict :return: None """ try: text = "Vertices : {0}\n".format(input_dict['vertices']) text += "Probability : {0}\n".format(input_dict['probability']) text += "Total Edges : {0}\n".format(input_dict['edge_number']) text += "Directed : {0}\n".format(bool(input_dict['direct'])) text += "Engine : {0} ({1})\n".format(input_dict['engine'], ENGINE_MENU[input_dict['engine']]) save_log(file, file_name, elapsed_time, text) except Exception: print(PYRGG_LOGGER_ERROR_MESSAGE) pyrgg-1.6/pyrgg/engines/pyrgg.py000066400000000000000000000217721471451651000170070ustar00rootroot00000000000000# -*- coding: utf-8 -*- """PyRGG Engine module.""" import os from random import randint, uniform, choice from pyrgg.params import ENGINE_MENU, PYRGG_LOGGER_ERROR_MESSAGE from pyrgg.functions import is_weighted, get_precision, threshold_calc from pyrgg.functions import get_min_max_weight, is_multigraph from pyrgg.functions import save_log def branch_gen( vertex_index, max_edge, min_edge, min_weight, max_weight, precision, sign, direct, self_loop, multigraph, used_vertices, degree_dict, degree_sort_dict): """ Generate branch and weight vector of each vertex. :param vertex_index: origin vertex index :type vertex_index: int :param max_edge: maximum number of edges (connected to each vertex) :type max_edge: int :param min_edge: minimum number of edges (connected to each vertex) :type min_edge: int :param min_weight: weight min range :type min_weight: int :param max_weight: weight max range :type max_weight: int :param precision: numbers precision :type precision: int :param sign: weight sign flag :type sign: bool :param direct: directed and undirected graph flag :type direct: bool :param self_loop: self loop flag :type self_loop: bool :param multigraph: multigraph flag :type multigraph: bool :param used_vertices: used vertices dictionary :type used_vertices: dict :param degree_dict: all vertices degree :type degree_dict: dict :param degree_sort_dict: degree to vertices list :type degree_sort_dict: dict :return: branch and weight list """ index = 0 branch_list = [] weight_list = [] reference_vertices = [] random_unit = randint vertex_degree = degree_dict[vertex_index] if vertex_degree >= max_edge: return [branch_list, weight_list] threshold = threshold_calc( min_edge=min_edge, max_edge=max_edge, vertex_degree=vertex_degree) for i in range(max_edge + 1): reference_vertices.extend(list(degree_sort_dict[i].values())) if len(reference_vertices) >= threshold: break if precision > 0: random_unit = uniform if not direct and ( vertex_index in used_vertices) and not multigraph: reference_vertices = list( set(reference_vertices) - set(used_vertices[vertex_index])) if not self_loop and vertex_index in reference_vertices: reference_vertices.remove(vertex_index) if int(os.environ.get("PYRGG_TEST_MODE", 0)): reference_vertices.sort() while (index < threshold): vertex_degree = degree_dict[vertex_index] if vertex_degree >= max_edge: break if len(reference_vertices) == 0: break random_tail_index = choice( range(len(reference_vertices))) random_tail = reference_vertices[random_tail_index] random_tail_degree = degree_dict[random_tail] if random_tail_degree >= max_edge or ( random_tail == vertex_index and random_tail_degree >= ( max_edge - 1)): reference_vertices.pop(random_tail_index) continue if not direct: try: used_vertices[random_tail].append(vertex_index) except KeyError: used_vertices[random_tail] = [vertex_index] weight_sign = 1 if sign: weight_sign = choice([1, -1]) random_weight = weight_sign * random_unit(min_weight, max_weight) random_weight = round(random_weight, precision) branch_list.append(random_tail) weight_list.append(random_weight) index += 1 del degree_sort_dict[vertex_degree][vertex_index] degree_dict[random_tail] += 1 degree_dict[vertex_index] += 1 degree_sort_dict[degree_dict[vertex_index] ][vertex_index] = vertex_index if random_tail != vertex_index: del degree_sort_dict[random_tail_degree][random_tail] degree_sort_dict[degree_dict[random_tail] ][random_tail] = random_tail if not multigraph: reference_vertices.pop(random_tail_index) return [branch_list, weight_list] def edge_gen( vertices_number, min_weight, max_weight, min_edge, max_edge, sign, direct, self_loop, multigraph): """ Generate each vertex connection number. :param vertices_number: number of vertices :type vertices_number: int :param min_weight: weight min range :type min_weight: int :param max_weight: weight max range :type max_weight: int :param min_edge: minimum number of edges (connected to each vertex) :type min_edge: int :param max_edge: maximum number of edges (connected to each vertex) :type max_edge: int :param sign: weight sign flag :type sign: bool :param direct: directed and undirected graph flag :type direct: bool :param self_loop: self loop flag :type self_loop: bool :param multigraph: multigraph flag :type multigraph: bool :return: list of dicts """ precision = max( get_precision(max_weight), get_precision(min_weight)) temp = 0 vertices_id = list(range(1, vertices_number + 1)) vertices_edge = [] weight_list = [] used_vertices = {} degree_sort_dict = {i: {} for i in range(max_edge + 1)} degree_dict = {} for i in vertices_id: degree_dict[i] = 0 degree_sort_dict[0][i] = i branch_gen_params = { "max_edge": max_edge, "min_edge": min_edge, "min_weight": min_weight, "max_weight": max_weight, "sign": sign, "direct": direct, "self_loop": self_loop, "multigraph": multigraph, "used_vertices": used_vertices, "degree_dict": degree_dict, "degree_sort_dict": degree_sort_dict, "precision": precision} for i in vertices_id: temp_list = branch_gen(vertex_index=i, **branch_gen_params) vertices_edge.append(temp_list[0]) weight_list.append(temp_list[1]) temp = temp + len(temp_list[0]) return [dict(zip(vertices_id, vertices_edge)), dict(zip(vertices_id, weight_list)), temp] def gen_using( gen_function, file_name, input_dict): """ Generate graph using given function based on PyRGG model. :param gen_function: generation function :type gen_function: function object :param file_name: file name :type file_name: str :param input_dict: input data :type input_dict: dict :return: number of edges as int """ edge_dic, weight_dic, edge_number = edge_gen( input_dict['vertices'], input_dict['min_weight'], input_dict['max_weight'], input_dict['min_edge'], input_dict['max_edge'], input_dict['sign'], input_dict['direct'], input_dict['self_loop'], input_dict['multigraph']) min_weight, max_weight = get_min_max_weight(weight_dic) weighted = is_weighted(max_weight, min_weight, bool(input_dict['sign'])) gen_function( edge_dic, weight_dic, { "file_name": file_name, "vertices_number": input_dict['vertices'], "weighted": weighted, "edge_number": edge_number, "min_weight": min_weight, "max_weight": max_weight, "direct": input_dict['direct'], "multigraph": is_multigraph(edge_dic), }) return edge_number def logger(file, file_name, elapsed_time, input_dict): """ Save generated graph logs for PyRGG engine. :param file: file to write log into :type file: file object :param file_name: file name :type file_name: str :param elapsed_time: elapsed time :type elapsed_time: str :param input_dict: input data :type input_dict: dict :return: None """ try: text = "Vertices : {0}\n".format(input_dict['vertices']) text += "Total Edges : {0}\n".format(input_dict['edge_number']) text += "Max Edge : {0}\n".format(input_dict['max_edge']) text += "Min Edge : {0}\n".format(input_dict['min_edge']) text += "Directed : {0}\n".format(bool(input_dict['direct'])) text += "Signed : {0}\n".format(bool(input_dict['sign'])) text += "Multigraph : {0}\n".format(bool(input_dict['multigraph'])) text += "Self Loop : {0}\n".format(bool(input_dict['self_loop'])) text += "Weighted : {0}\n".format( is_weighted(input_dict['max_weight'], input_dict['min_weight'], bool(input_dict['sign']))) text += "Max Weight : {0}\n".format(input_dict['max_weight']) text += "Min Weight : {0}\n".format(input_dict['min_weight']) text += "Engine : {0} ({1})\n".format( input_dict['engine'], ENGINE_MENU[input_dict['engine']]) save_log(file, file_name, elapsed_time, text) except Exception: print(PYRGG_LOGGER_ERROR_MESSAGE) pyrgg-1.6/pyrgg/functions.py000066400000000000000000000370641471451651000162400ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Pyrgg functions module.""" import os from random import randint from json import loads as json_loads from json import dump as json_dump from pickle import dump as pickle_dump from yaml import safe_dump as yaml_dump import datetime import pyrgg.params def is_weighted(max_weight, min_weight, signed): """ Check the graph is weighted or not. :param max_weight: maximum weight :type max_weight: int :param min_weight: minimum weight :type min_weight: int :param signed: weight sign flag :type signed: bool :return: result as bool """ if max_weight == min_weight and min_weight == 0: return False if max_weight == min_weight and min_weight == 1 and not signed: return False return True def get_min_max_weight(weight_dic): """ Get minimum and maximum weight values. :param weight_dic: weight dictionary :type weight_dic: dict :return: minimum and maximum weight values """ all_weights = [abs(w) for weights in weight_dic.values() for w in weights] return min(all_weights), max(all_weights) def is_signed(weight_dic): # pragma: no cover """ Check if the graph is signed. :param weight_dic: weight dictionary :type weight_dic: dict :return: signed flag """ return any([any([w < 0 for w in weights]) for weights in weight_dic.values()]) def has_self_loop(edge_dic): # pragma: no cover """ Check if the graph has self loops. :param edge_dic: edge dictionary :type edge_dic: dict :return: self looped flag """ return any([v in edges for v, edges in edge_dic.items()]) def is_multigraph(edge_dic): """ Check if the graph is a multigraph. :param edge_dic: edge dictionary :type edge_dic: dict :return: multigraph flag """ return any([len(set(edges)) != len(edges) for edges in edge_dic.values()]) def get_precision(input_number): """ Return precision of input number. :param input_number: input number :type input_number: float :return: precision as int """ try: number_str = str(input_number) _, decimal_part = number_str.split(".") return len(decimal_part) except Exception: return 0 def threshold_calc(min_edge, max_edge, vertex_degree): """ Calculate threshold for branch_gen_pyrgg function. :param min_edge: minimum number of edges (connected to each vertex) :type min_edge: int :param max_edge: maximum number of edges (connected to each vertex) :type max_edge: int :param vertex_degree: vertex degree :type vertex_degree: int :return: threshold as int """ threshold = min_edge lower_limit = 0 upper_limit = max_edge - vertex_degree if vertex_degree < min_edge: lower_limit = min_edge - vertex_degree if upper_limit > lower_limit: threshold = randint(lower_limit, upper_limit) return threshold def is_float(input_number): """ Check input for float conversion. :param input_number: input number :type input_number: float or int or str :return: result as bool """ try: _, decimal_part = divmod(float(input_number), 1) except TypeError: return False else: return True if decimal_part else False def handle_string(string): """ Handle string and raise ValueError if it is empty. :param string: input string :type string: str :return: result as str """ if string == "": raise ValueError return string def handle_pos_int(input_number): """ Handle input number and raise ValueError if it is negative. :param input_number: input number :type input_number: float or int or str :return: result as int """ val = int(input_number) if val < 0: raise ValueError return val def handle_str_to_number(string): """ Convert string to float or int. :param string: input string :type string: str :return: result as float or int """ return float(string) if is_float(string) else int(string) def handle_str_prob(string): """ Convert string to float and raise ValueError if string is invalid. :param string: input string :type string: str :return: result as float """ val = handle_str_to_number(string) if val < 0: raise ValueError if val > 1: raise ValueError return val def handle_str_to_bool(string): """ Convert 0/1 string to bool and raise ValueError if string is invalid. :param string: input string :type string: str :return: result as bool """ val = int(string) if val not in [0, 1]: raise ValueError return bool(val) def handle_output_format(string): """ Convert string to output format index. :param string: input string :type string: str :return: output format index as int """ output_format = handle_pos_int(string) if output_format not in pyrgg.params.SUFFIX_MENU: raise ValueError return output_format def handle_engine(string): """ Convert string to engine index. :param string: input string :type string: str :return: engine index as int """ engine = handle_pos_int(string) if engine not in pyrgg.params.ENGINE_MENU: raise ValueError return engine ITEM_HANDLERS = { "file_name": handle_string, "output_format": handle_output_format, "weight": handle_str_to_bool, "engine": handle_engine, "vertices": handle_pos_int, "number_of_files": handle_pos_int, "max_weight": handle_str_to_number, "min_weight": handle_str_to_number, "min_edge": handle_pos_int, "max_edge": handle_pos_int, "edge_number": handle_pos_int, "sign": handle_str_to_bool, "direct": handle_str_to_bool, "self_loop": handle_str_to_bool, "multigraph": handle_str_to_bool, "config": handle_str_to_bool, "probability": handle_str_prob, } def description_print(): """ Print justified description for overview in console. :return: None """ print(pyrgg.params.PYRGG_LINKS) line(40) print("\n") print(pyrgg.params.PYRGG_DESCRIPTION) print("\n") line(40) def line(num=11, char="#"): """ Print line of char. :param num: number of character in this line :type num : int :param char: character :type char: str :return: None """ print(char * num) def convert_bytes(num): """ Convert num to idiomatic byte unit. :param num: the input number. :type num: int :return: result as str """ for x in ['bytes', 'KB', 'MB', 'GB', 'TB']: if num < 1024.0: return "%3.1f %s" % (num, x) num /= 1024.0 def filesize(fileaddr): # pragma: no cover """ Calculate output file size. :param fileaddr: file addresses :type fileaddr: str :return: file size for print as string """ file_info = os.stat(fileaddr) file_size = file_info.st_size print("Graph File Size : " + convert_bytes(file_size)) def time_convert(input_time): """ Convert input_time from sec to DD,HH,MM,SS format. :param input_time: input time in sec :type input_time: float :return: converted time as str """ postfix_dict = {"s": "second", "d": "day", "h": "hour", "m": "minute"} value_dict = {"s": 0, "d": 0, "h": 0, "m": 0} value_dict["s"] = float(input_time) value_dict["d"], value_dict["s"] = divmod(value_dict["s"], 24 * 3600) value_dict["h"], value_dict["s"] = divmod(value_dict["s"], 3600) value_dict["m"], value_dict["s"] = divmod(value_dict["s"], 60) for i in postfix_dict: if value_dict[i] != 1: postfix_dict[i] += "s" return ", ".join([ "{0:02.0f} {1}".format(value_dict["d"], postfix_dict["d"]), "{0:02.0f} {1}".format(value_dict["h"], postfix_dict["h"]), "{0:02.0f} {1}".format(value_dict["m"], postfix_dict["m"]), "{0:02.0f} {1}".format(value_dict["s"], postfix_dict["s"]), ]) def input_filter(input_dict): """ Filter input data. :param input_dict: input dictionary :type input_dict: dict :return: filtered data as dict """ filtered_dict = input_dict.copy() edge_upper_threshold = filtered_dict["vertices"] for key in ["min_edge", "max_edge", "vertices"]: if filtered_dict[key] < 0: filtered_dict[key] *= -1 if filtered_dict["min_weight"] > filtered_dict["max_weight"]: filtered_dict["min_weight"], filtered_dict["max_weight"] = ( filtered_dict["max_weight"], filtered_dict["min_weight"] ) if filtered_dict["min_edge"] > filtered_dict["max_edge"]: filtered_dict["min_edge"], filtered_dict["max_edge"] = ( filtered_dict["max_edge"], filtered_dict["min_edge"] ) if not filtered_dict["self_loop"]: edge_upper_threshold -= 1 if not filtered_dict["multigraph"]: for key in ["min_edge", "max_edge"]: filtered_dict[key] = min(filtered_dict[key], edge_upper_threshold) return filtered_dict def get_input(input_func=input): """ Get input from user and return as dictionary. :param input_func: input function :type input_func: function object :return: inputs as dict """ result_dict = { "file_name": "", "number_of_files": 1, "vertices": 0, "max_weight": 1, "min_weight": 1, "min_edge": 0, "max_edge": 0, "edge_number": 0, "sign": True, "output_format": 1, "weight": True, "engine": 1, "direct": True, "self_loop": True, "multigraph": False, "config": False, "probability": 0.5, } result_dict = _update_using_menu(result_dict, input_func) result_dict = _update_with_engine_params( result_dict, input_func, pyrgg.params.ENGINE_PARAM_MAP[result_dict['engine']]) return input_filter(result_dict) def _update_using_menu(result_dict, input_func): """ Update result_dict using user input from the menu. :param result_dict: result data :type result_dict: dict :param input_func: input function :type input_func: function object :return: result_dict as dict """ for index in sorted(pyrgg.params.MENU_ITEMS): item1, item2 = pyrgg.params.MENU_ITEMS[index] while True: try: result_dict[item1] = ITEM_HANDLERS[item1]( input_func(item2) ) except Exception: print(pyrgg.params.PYRGG_INPUT_ERROR_MESSAGE) else: break return result_dict def _update_with_engine_params(result_dict, input_func, engine_params): """ Update result_dict using user input based on given engine requirements. :param result_dict: result data :type result_dict: dict :param input_func: input function :type input_func: function object :param engine_params: engine parameters :type engine_params: dict :return: result_dict as dict """ for index in sorted(engine_params): item1, item2 = engine_params[index] if not result_dict["weight"] and item1 in ["max_weight", "min_weight"]: continue while True: try: result_dict[item1] = ITEM_HANDLERS[item1]( input_func(item2) ) except Exception: print(pyrgg.params.PYRGG_INPUT_ERROR_MESSAGE) else: break return result_dict def json_to_yaml(filename): """ Convert json file to yaml file. :param filename: filename :type filename: str :return: None """ try: with open(filename + ".json", "r") as json_file: json_data = json_loads(json_file.read()) with open(filename + ".yaml", "w") as yaml_file: yaml_dump(json_data, yaml_file, default_flow_style=False) except FileNotFoundError: print(pyrgg.params.PYRGG_YAML_ERROR_MESSAGE) def json_to_pickle(filename): """ Convert json file to pickle file. :param filename: filename :type filename: str :return: None """ try: with open(filename + ".json", "r") as json_file: json_data = json_loads(json_file.read()) with open(filename + ".p", "wb") as pickle_file: pickle_dump(json_data, pickle_file) except FileNotFoundError: print(pyrgg.params.PYRGG_PICKLE_ERROR_MESSAGE) def save_config(input_dict): """ Save input_dict as the generation config. :param input_dict: input data :type input_dict: dict :return: path to file as str """ try: input_dict_temp = input_dict.copy() input_dict_temp['engine'] = pyrgg.params.ENGINE_MENU[input_dict_temp['engine']] input_dict_temp['pyrgg_version'] = pyrgg.params.PYRGG_VERSION input_dict_temp['output_format'] = pyrgg.params.OUTPUT_FORMAT[input_dict_temp['output_format']] fname = pyrgg.params.CONFIG_FILE_FORMAT.format( input_dict_temp['file_name']) with open(fname, "w") as json_file: json_dump(input_dict_temp, json_file, indent=2) return os.path.abspath(fname) except BaseException: print(pyrgg.params.PYRGG_CONFIG_SAVE_ERROR_MESSAGE) def load_config(path): """ Load config based on given path. :param path: path to config file :type path: str :return: input data as dict """ try: with open(path, "r") as json_file: config = json_loads(json_file.read()) config['output_format'] = pyrgg.params.OUTPUT_FORMAT_INV[config['output_format']] config['engine'] = pyrgg.params.ENGINE_MENU_INV[config['engine']] return input_filter(config) except BaseException: print(pyrgg.params.PYRGG_CONFIG_LOAD_ERROR_MESSAGE) def _print_select_config(configs, input_func=input): """ Print configs in current directory and get input from user. :param configs: configs path :type configs: list :param input_func: input function :type input_func: function object :return: input data as dict """ if len(configs) == 0: return None print(pyrgg.params.PYRGG_CONFIG_LIST_MESSAGE) for i, config in enumerate(configs): print("[{}] - {}".format(i + 1, config)) key = input_func(pyrgg.params.PYRGG_CONFIG_LOAD_MESSAGE) try: return load_config(configs[int(key) - 1]) except BaseException: return None def check_for_config(input_func=input): """ Check for config files in source directory. :param input_func: input function :type input_func: function object :return: input data as dict """ configs = [] for filename in os.listdir(pyrgg.params.SOURCE_DIR): file = os.path.join(pyrgg.params.SOURCE_DIR, filename) if os.path.isfile(file) and filename.endswith( pyrgg.params.CONFIG_FILE_FORMAT.format("")): configs.append(file) return _print_select_config(configs, input_func) def save_log(file, file_name, elapsed_time, text): """ Save generated graph logs. :param file: file to write log into :type file: file object :param file_name: file name :type file_name: str :param elapsed_time: elapsed time :type elapsed_time: str :param text: rest part of the text to write :type text: str :return: None """ text2file = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "\n" text2file += "Filename : {0}\n".format(file_name) text2file += text text2file += "Elapsed Time : {0}\n".format(elapsed_time) text2file += "-------------------------------\n" file.write(text2file) pyrgg-1.6/pyrgg/graph_gen.py000066400000000000000000000370571471451651000161640ustar00rootroot00000000000000# -*- coding: utf-8 -*- """PyRGG graph generators module.""" import random import datetime from pyrgg.params import * from pyrgg.functions import * # random_system=random.SystemRandom() random_system = random def dimacs_maker( edge_dic, weight_dic, mdata): """ Create output file and fill in. :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param mdata: meta data :type mdata: dict :return: None """ with open(mdata['file_name'] + ".gr", "w") as buf: buf.write( DIMACS_FIX.format( mdata['file_name'], str(mdata['vertices_number']), str(mdata['edge_number']), str(mdata['max_weight']), str(mdata['min_weight']))) _write_separated_file( buf, edge_dic, weight_dic, separator=' ', prefix='a', ) def json_maker( edge_dic, weight_dic, mdata): """ Create output file in json format. :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param mdata: meta data :type mdata: dict :return: None """ with open(mdata['file_name'] + ".json", "w") as buf: _write_properties_to_json( buf, mdata['weighted'], mdata['direct'], mdata['multigraph'],) _write_data_to_json( buf, edge_dic, weight_dic, ) def _write_data_to_json(buf, edge_dic, weight_dic): """Write data to json buffer. :param buf: output file object :type buf: file_object :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :return: None """ buf.write('\n\t"graph": {\n') _write_nodes_to_json(buf, edge_dic) buf.write("\n\t\t],\n") _write_edges_to_json(buf, edge_dic, weight_dic) buf.write("\n\t\t]\n\t}\n}") def _write_properties_to_json( buf, weighted, direct, multigraph): """ Write properties to json buffer. :param buf: output file object :type buf: file_object :param weighted: weighted graph flag :type weighted: bool :param direct: directed and undirected graph flag :type direct: bool :param multigraph: multigraph flag :type multigraph: bool :return: None """ buf.write('{\n\t"properties": {\n') buf.write('\t\t"weighted": ' + str(weighted).lower() + ",\n") buf.write('\t\t"multigraph": ' + str(multigraph).lower() + ",\n") buf.write('\t\t"directed": ' + str(direct).lower() + "\n") buf.write("},\n") def _write_nodes_to_json(buf, edge_dic): """Write nodes to json. :param buf: output file object :type buf: file_object :param edge_dic: dictionary containing edges data :type edge_dic: dict :return: None """ first_line = True nodes = '\t\t"nodes":[\n' buf.write(nodes) for key in edge_dic: nodes = "" if first_line: first_line = False else: nodes += ",\n" nodes = "".join([ nodes, '\t\t{\n\t\t\t', '"id": ', str(key), '\n\t\t}' ]) buf.write(nodes) def _write_edges_to_json(buf, edge_dic, weight_dic): """Write edges to json. :param buf: output file object :type buf: file_object :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :return: None """ edges = '\t\t"edges":[\n' first_line = True buf.write(edges) for key, edge_val in edge_dic.items(): for j, value in enumerate(edge_val): edges = "" if first_line: first_line = False else: edges += ",\n" edges = "".join([ edges, '\t\t{\n\t\t\t"source": ', str(key), ',\n\t\t\t', '"target": ', str(value), ',\n\t\t\t', '"weight": ', str(weight_dic[key][j]), '\n\t\t}' ]) buf.write(edges) def csv_maker( edge_dic, weight_dic, mdata): """ Create output file in csv format. :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param mdata: meta data :type mdata: dict :return: None """ with open(mdata['file_name'] + ".csv", "w") as buf: _write_separated_file(buf, edge_dic, weight_dic, separator=',') def tsv_maker( edge_dic, weight_dic, mdata): """ Create output file in tsv format. :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param mdata: meta data :type mdata: dict :return: None """ with open(mdata['file_name'] + ".tsv", "w") as buf: _write_separated_file(buf, edge_dic, weight_dic, separator='\t') def _write_separated_file(buf, edge_dic, weight_dic, separator, prefix=''): r"""Write data to buffer separated with ``separator``. :param buf: output file object :type buf: file_object :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param separator: separator in a separated file, like ',', '\t', ' ', etc. :type separator: str :param prefix: prefix to be added in front of each line :type prefix: str :return: None """ dummy_prefix = object() prefix = prefix or dummy_prefix for key, edge_val in edge_dic.items(): for j, value in enumerate(edge_val): elements = [ prefix, str(key), str(value), str(weight_dic[key][j]) + "\n" ] string = separator.join(x for x in elements if x != dummy_prefix) buf.write(string) def wel_maker( edge_dic, weight_dic, mdata): """ Create output file in wel format. :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param mdata: meta data :type mdata: dict :return: None """ with open(mdata['file_name'] + ".wel", "w") as buf: _write_separated_file(buf, edge_dic, weight_dic, separator=' ') def mtx_maker( edge_dic, weight_dic, mdata): """ Create output file in Matrix Market format. :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param mdata: meta data :type mdata: dict :return: None """ max_edge_length = len(str(mdata['vertices_number'])) with open(mdata['file_name'] + ".mtx", "w") as buf: buf.write("%%MatrixMarket matrix coordinate real general\n") buf.write( "{0} {0} {1}\n".format(str(mdata['vertices_number']), str(mdata['edge_number'])) ) for key, edge_val in edge_dic.items(): for j, value in enumerate(edge_val): shift1 = (max_edge_length - len(str(key))) + 4 shift2 = (max_edge_length - len(str(value))) + 4 buf.write(str(key) + shift1 * " " + str(value) + shift2 * " " + str(weight_dic[key][j]) + "\n") def lp_maker( edge_dic, weight_dic, mdata): """ Create output file in ASP format. :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param mdata: meta data :type mdata: dict :return: None """ with open(mdata['file_name'] + ".lp", "w") as buf: for key in edge_dic: buf.write('node(' + str(key) + ").\n") for key, edge_val in edge_dic.items(): for j, value in enumerate(edge_val): buf.write('edge(' + str(key) + "," + str(value) + "," + str(weight_dic[key][j]) + ").\n") def tgf_maker( edge_dic, weight_dic, mdata): """ Create output file in Trivial Graph Format (TGF). :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param mdata: meta data :type mdata: dict :return: None """ with open(mdata['file_name'] + ".tgf", "w") as buf: for key in edge_dic: buf.write(str(key) + "\n") buf.write("#\n") _write_separated_file(buf, edge_dic, weight_dic, separator=' ') def gl_maker( edge_dic, weight_dic, mdata): """ Create output file in Graph Line(GL). :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param mdata: meta data :type mdata: dict :return: None """ with open(mdata['file_name'] + ".gl", "w") as buf: for key, edge_val in edge_dic.items(): line_data = str(key) write_flag = False for j, value in enumerate(edge_val): write_flag = True line_data += " " + str(value) + ":" + str(weight_dic[key][j]) if write_flag: buf.write(line_data + "\n") def dl_maker( edge_dic, weight_dic, mdata): """ Create output file in UCINET DL Format. :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param mdata: meta data :type mdata: dict :return: None """ with open(mdata['file_name'] + ".dl", "w") as buf: buf.write("dl\nformat=edgelist1\nn=" + str(mdata['vertices_number']) + "\ndata:\n") _write_separated_file(buf, edge_dic, weight_dic, separator=' ') def gdf_maker( edge_dic, weight_dic, mdata): """ Create output file in GDF Format. :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param mdata: meta data :type mdata: dict :return: None """ with open(mdata['file_name'] + ".gdf", "w") as buf: buf.write("nodedef>name VARCHAR,label VARCHAR\n") for key in edge_dic: buf.write(str(key) + "," + "Node{0}".format(str(key)) + "\n") buf.write("edgedef>node1 VARCHAR,node2 VARCHAR,weight DOUBLE\n") _write_separated_file(buf, edge_dic, weight_dic, separator=',') def gml_maker( edge_dic, weight_dic, mdata): """ Create output file in GML Format. :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param mdata: meta data :type mdata: dict :return: None """ header = 'graph\n[\n multigraph {0}\n directed {1}\n'.format( int(mdata['multigraph']), int(mdata['direct'])) with open(mdata['file_name'] + ".gml", "w") as buf: buf.write(header) for key in edge_dic: buf.write( " node\n [\n id " + str(key) + "\n" + ' label "Node {0}"\n'.format( str(key)) + " ]\n") for key, edge_val in edge_dic.items(): for j, value in enumerate(edge_val): buf.write(" edge\n [\n source " + str(key) + "\n" + " target " + str(value) + "\n" + " value " + str(weight_dic[key][j]) + "\n" + " ]\n") buf.write("]") def gexf_maker( edge_dic, weight_dic, mdata): """ Create output file in GEXF Format. :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param mdata: meta data :type mdata: dict :return: None """ header = '\n' header += '\n' date = datetime.datetime.now().date() meta = " " * 4 + '\n'.format(date) meta += " " * 8 + 'PyRGG\n' meta += " " * 8 + '{0}\n'.format(mdata['file_name']) meta += " " * 4 + '\n' if mdata['direct']: defaultedgetype = "directed" else: defaultedgetype = "undirected" with open(mdata['file_name'] + ".gexf", "w") as buf: buf.write(header) buf.write(meta) buf.write( " " * 4 + '\n' ) buf.write(" " * 8 + "\n") for key in edge_dic: buf.write( " " * 12 + ''.format( str(key)) + "\n") buf.write(" " * 8 + "\n") buf.write(" " * 8 + "\n") edge_id = 1 for key, edge_val in edge_dic.items(): for j, value in enumerate(edge_val): buf.write( " " * 12 + ''.format( str(weight_dic[key][j])) + "\n") edge_id += 1 buf.write(" " * 8 + "\n") buf.write(" " * 4 + "\n") buf.write("") def dot_maker( edge_dic, weight_dic, mdata): """ Create output file in Dot Format. :param edge_dic: dictionary containing edges data :type edge_dic: dict :param weight_dic: dictionary containing weights data :type weight_dic: dict :param mdata: meta data :type mdata: dict :return: None """ header = "{0} {1}" linker = "--" if mdata['direct']: header = header.format("digraph", mdata['file_name']) linker = "->" else: header = header.format("graph", mdata['file_name']) with open(mdata['file_name'] + ".gv", "w") as buf: buf.write(header + " {") for key, edge_val in edge_dic.items(): for j, value in enumerate(edge_val): buf.write( "\n" + str(key) + " " + linker + " " + str(value) + " [weight={}]".format( weight_dic[key][j]) + ";") buf.write("\n}") pyrgg-1.6/pyrgg/params.py000066400000000000000000000104261471451651000155040ustar00rootroot00000000000000# -*- coding: utf-8 -*- """PyRGG params.""" from textwrap import dedent, fill import os os.environ["PYRGG_TEST_MODE"] = "0" MENU_ITEMS = { 1: ["engine", dedent( """\ - Select generation engine : 1- PyRGG 2- Erdos-Renyi-Gilbert - G(n, p) 3- Erdos-Renyi - G(n, m) """ )], 2: ["file_name", "- File Name (Not Empty) : "], 3: ["number_of_files", "- Number of Files (>=0) : "], 4: ["output_format", dedent( """\ - Graph Formats : 1- DIMACS(.gr) 2- JSON(.json) 3- CSV(.csv) 4- YAML(.yaml) 5- WEL(.wel) 6- ASP(.lp) 7- Pickle(.p) 8- UCINET DL(.dl) 9- TGF(.tgf) 10- TSV(.tsv) 11- Matrix Market(.mtx) 12- Graph Line(.gl) 13- GDF(.gdf) 14- GML(.gml) 15- GEXF(.gexf) 16- DOT(.gv) """ )], 5: ["config", "- Save Config[1] or Not[0]"], } SUFFIX_MENU = { 1: ".gr", 2: ".json", 3: ".csv", 4: ".yaml", 5: ".wel", 6: ".lp", 7: ".p", 8: ".dl", 9: ".tgf", 10: ".tsv", 11: ".mtx", 12: ".gl", 13: ".gdf", 14: ".gml", 15: ".gexf", 16: ".gv" } ENGINE_MENU = { 1: "pyrgg", 2: "erg", 3: "er", } ENGINE_MENU_INV = {v: k for k, v in ENGINE_MENU.items()} PYRGG_ENGINE_PARAMS = { 1: ["vertices", "- Vertices Number (>=0) : "], 2: ["min_edge", "- Min Edge Number - Connected to Each Vertex (>=0) : "], 3: ["max_edge", "- Max Edge Number - Connected to Each Vertex (>=0) : "], 4: ["weight", "- Unweighted[0] or Weighted[1]"], 5: ["min_weight", "- Min Weight : "], 6: ["max_weight", "- Max Weight : "], 7: ["sign", "- Unsigned[0] or Signed[1]"], 8: ["direct", "- Undirected[0] or Directed[1]"], 9: ["self_loop", "- No Self Loop[0] or Self Loop[1]"], 10: ["multigraph", "- Simple[0] or Multigraph[1]"], } ERG_ENGINE_PARAMS = { 1: ["vertices", "- Vertices Number (>=0) : "], 2: ["probability", "- Probability (0 <= p <= 1) : "], 3: ["direct", "- Undirected[0] or Directed[1]"], } ER_ENGINE_PARAMS = { 1: ["vertices", "- Vertices Number (>=0) : "], 2: ["edge_number", "- Edge Number (>=0) : "], 3: ["direct", "- Undirected[0] or Directed[1]"], } ENGINE_PARAM_MAP = { 1: PYRGG_ENGINE_PARAMS, 2: ERG_ENGINE_PARAMS, 3: ER_ENGINE_PARAMS, } OUTPUT_FORMAT = {i: output_format[1:].upper() for i, output_format in SUFFIX_MENU.items()} OUTPUT_FORMAT_INV = {v: k for k, v in OUTPUT_FORMAT.items()} PYRGG_VERSION = "1.6" SOURCE_DIR = os.getcwd() CONFIG_FILE_FORMAT = "{}.pyrgg.config.json" PYRGG_LINKS = """ Webpage : https://www.pyrgg.site Repository : https://github.com/sepandhaghighi/pyrgg Paper : https://doi.org/10.21105/joss.00331 * If you use PyRGG in your research, please cite our paper """ _description = """\ PyRGG is a user-friendly synthetic random graph generator that is written in Python and supports multiple graph file formats, such as DIMACS-Graph files. It can generate graphs of various sizes and is specifically designed to create input files for a wide range of graph-based research applications, including testing, benchmarking, and performance analysis of graph processing frameworks. PyRGG is aimed at computer scientists who are studying graph algorithms and graph processing frameworks. """ PYRGG_DESCRIPTION = fill(_description, width=113) PYRGG_INPUT_ERROR_MESSAGE = "[Error] Bad input!" PYRGG_FILE_ERROR_MESSAGE = "[Error] Bad input file!" PYRGG_INVALID_ENGINE_ERROR_MESSAGE = "[Error] Invalid engine!" PYRGG_YAML_ERROR_MESSAGE = "[Error] Failed to generate YAML file!" PYRGG_PICKLE_ERROR_MESSAGE = "[Error] Failed to generate Pickle file!" PYRGG_LOGGER_ERROR_MESSAGE = "[Error] Logger failed!" PYRGG_CONFIG_LOAD_ERROR_MESSAGE = "[Error] Failed to load config file!" PYRGG_CONFIG_SAVE_ERROR_MESSAGE = "[Error] Failed to save config file!" PYRGG_CONFIG_LIST_MESSAGE = "Config files detected in the current directory are listed below:" PYRGG_CONFIG_LOAD_MESSAGE = "Press the config index to load or any other keys to start with the new one:" DIMACS_FIX = dedent( """\ c FILE :{0}.gr c No. of vertices :{1} c No. of edges :{2} c Max. weight :{3} c Min. weight :{4} p sp {1} {2} """ ) pyrgg-1.6/pyrgg/test.py000066400000000000000000000635651471451651000152140ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Test file.""" """ >>> from pyrgg import * >>> import pyrgg.params >>> import random >>> import os >>> import json >>> import yaml >>> import pickle >>> os.environ["PYRGG_TEST_MODE"] = "1" >>> import pyrgg.engines.pyrgg as engine >>> get_precision(2) 0 >>> get_precision(2.2) 1 >>> get_precision(2.22) 2 >>> get_precision(2.223) 3 >>> handle_str_to_number("20") 20 >>> handle_str_to_number("20.2") 20.2 >>> handle_str_to_bool("1") True >>> handle_str_to_bool("3") Traceback (most recent call last): ... ValueError >>> handle_str_to_bool("0") False >>> is_float(10) False >>> is_float(10.2) True >>> is_float(None) False >>> result = input_filter({"file_name": "test","vertices": 5,"max_weight": 1000,"min_weight":455,"min_edge": -45,"max_edge": -11,"sign": False,"output_format": 1, "direct": False,"self_loop": True,"multigraph":False,"number_of_files":2,"engine":1}) >>> result == {'output_format': 1, 'min_weight': 455, 'min_edge': 5, 'max_edge': 5, 'file_name': 'test', 'vertices': 5, 'max_weight': 1000, 'sign': False, "direct": False,"self_loop": True,"multigraph":False,"number_of_files":2,"engine":1} True >>> result = input_filter({"file_name": "test","vertices": 5,"max_weight": 1000,"min_weight":455,"min_edge": -45,"max_edge": -11,"sign": False,"output_format": 1, "direct": False,"self_loop": False,"multigraph":False,"number_of_files":2,"engine":1}) >>> result == {'output_format': 1, 'min_weight': 455, 'min_edge': 4, 'max_edge': 4, 'file_name': 'test', 'vertices': 5, 'max_weight': 1000, 'sign': False, "direct": False,"self_loop": False,"multigraph":False,"number_of_files":2,"engine":1} True >>> result = input_filter({"file_name": "test","vertices": -5,"max_weight": 1000,"min_weight":455,"min_edge": -45,"max_edge": -11,"sign": False,"output_format": 1, "direct": False,"self_loop": False,"multigraph":True,"number_of_files":1,"engine":1}) >>> result == {'output_format': 1, 'min_weight': 455, 'min_edge': 11, 'max_edge': 45, 'file_name': 'test', 'vertices': 5, 'max_weight': 1000, 'sign': False, "direct": False,"self_loop": False,"multigraph":True,"number_of_files":1,"engine":1} True >>> result = input_filter({"file_name": "test2","vertices": 23,"max_weight": 2,"min_weight": 80,"min_edge": 23,"max_edge": 1,"sign": True,"output_format": 1, "direct": False,"self_loop": True,"multigraph":False,"number_of_files":100,"engine":1}) >>> result == {'min_weight': 2, 'vertices': 23, 'file_name': 'test2', 'max_edge': 23, 'min_edge': 1, 'max_weight': 80, 'output_format': 1, 'sign': True, "direct": False,"self_loop": True,"multigraph":False,"number_of_files":100,"engine":1} True >>> with open('logfile.log','a') as file: ... engine.logger(file,'test','2min',{'vertices':100,'edge_number':50,'max_edge':1000,'min_edge':10,'direct':1,'sign':0,'multigraph':0,'self_loop':1,'max_weight':20,'min_weight':1,'engine':1,'output_format':1}) >>> file = open('logfile.log','r') >>> print("\n".join(file.read().splitlines()[1:-1])) Filename : test Vertices : 100 Total Edges : 50 Max Edge : 1000 Min Edge : 10 Directed : True Signed : False Multigraph : False Self Loop : True Weighted : True Max Weight : 20 Min Weight : 1 Engine : 1 (pyrgg) Elapsed Time : 2min >>> convert_bytes(200) '200.0 bytes' >>> convert_bytes(6000) '5.9 KB' >>> convert_bytes(80000) '78.1 KB' >>> time_convert(33) '00 days, 00 hours, 00 minutes, 33 seconds' >>> time_convert(15000) '00 days, 04 hours, 10 minutes, 00 seconds' >>> time_convert('sadasdasd') Traceback (most recent call last): ... ValueError: could not convert string to float: 'sadasdasd' >>> line(12,"*") ************ >>> used_vertices = {k:[] for k in range(1,41)} >>> degree_dict = {k:0 for k in range(1,41)} >>> degree_dict_sort = {k:{} for k in range(41)} >>> degree_dict_sort[0] = {i:i for i in range(1,41)} >>> all_vertices = list(range(1, 41)) >>> random.seed(2) >>> engine.branch_gen(1,10,10,1,20,0,True,True,True,False,used_vertices,degree_dict,degree_dict_sort) [[4, 25, 18, 3, 30, 34, 2, 26, 14, 11], [3, 10, 20, 14, -18, -2, -15, -14, 8, 6]] >>> random.seed(20) >>> engine.branch_gen(1,10,4,1,20,0,False,True,True,False,used_vertices,degree_dict,degree_dict_sort) [[], []] >>> used_vertices = {k:[] for k in range(1,41)} >>> degree_dict = {k:0 for k in range(1,41)} >>> degree_dict_sort = {k:{} for k in range(41)} >>> degree_dict_sort[0] = {i:i for i in range(1,41)} >>> engine.branch_gen(1,10,4,1,20,0,False,True,True,False,used_vertices,degree_dict,degree_dict_sort) [[10, 7, 39, 2, 30, 9, 25, 35, 18], [9, 11, 6, 14, 3, 5, 16, 14, 7]] >>> engine.branch_gen(40,1,20,1) Traceback (most recent call last): ... TypeError: branch_gen() missing 9 required positional arguments: 'max_weight', 'precision', 'sign', 'direct', 'self_loop', 'multigraph', 'used_vertices', 'degree_dict', and 'degree_sort_dict' >>> random.seed(2) >>> engine.edge_gen(20,0,400,2,10,True,True,True,False) [{1: [3, 7], 2: [4, 17, 20, 9, 11], 3: [14, 8, 5, 12, 16, 19, 15], 4: [15, 17, 12, 8, 14, 13], 5: [16, 9, 7, 20, 19, 18, 13, 5], 6: [6, 10], 7: [18, 10, 11], 8: [], 9: [], 10: [12, 18, 8, 1, 14], 11: [9, 11], 12: [], 13: [], 14: [19, 16, 17, 20, 15], 15: [6, 1, 19], 16: [12, 13, 8, 9, 17], 17: [], 18: [9, 12, 17, 6, 20, 19, 1], 19: [13], 20: []}, {1: [184, -128], 2: [220, -278, -257, 14, -163], 3: [286, 118, 166, 261, -263, 228, -303], 4: [-82, -335, 250, -256, -338, -179], 5: [-337, -358, -395, -155, -159, 250, -350, -371], 6: [30, -302], 7: [386, -125, 216], 8: [], 9: [], 10: [127, 42, 12, 191, 80], 11: [-301, 77], 12: [], 13: [], 14: [146, -15, -282, 135, 242], 15: [-52, -65, -249], 16: [-132, -334, 343, -17, 87], 17: [], 18: [126, -37, 302, -131, -142, 77, -209], 19: [123], 20: []}, 61] >>> random.seed(11) >>> engine.edge_gen(20,0,100,2,10,False,True,True,False) [{1: [18, 15, 19, 7, 20, 11, 2, 6, 3], 2: [17], 3: [8, 4, 5, 9, 12, 10, 14, 16], 4: [20, 13, 4, 6], 5: [12, 7, 11, 10, 14], 6: [9], 7: [19], 8: [8, 18, 11, 2, 16, 17, 10], 9: [15, 12, 18], 10: [20, 14, 13, 15, 17, 16], 11: [19, 7, 20], 12: [13], 13: [2, 16, 13], 14: [18, 19, 6, 14, 17, 15], 15: [6, 7, 16], 16: [17, 20, 12, 18], 17: [19], 18: [7, 6, 9, 12, 20], 19: [19, 11, 4], 20: []}, {1: [99, 57, 75, 23, 80, 23, 57, 18, 68], 2: [50], 3: [79, 67, 7, 24, 76, 99, 41, 75], 4: [29, 63, 84, 58], 5: [70, 90, 40, 65, 3], 6: [51], 7: [37], 8: [2, 0, 26, 60, 90, 53, 72], 9: [43, 39, 1], 10: [15, 31, 1, 59, 22, 57], 11: [98, 53, 49], 12: [53], 13: [34, 2, 23], 14: [82, 12, 18, 56, 1, 37], 15: [9, 26, 1], 16: [47, 58, 75, 73], 17: [23], 18: [39, 78, 92, 20, 49], 19: [10, 6, 13], 20: []}, 74] >>> engine.edge_gen(0,400,2,10,1) Traceback (most recent call last): ... TypeError: edge_gen() missing 4 required positional arguments: 'sign', 'direct', 'self_loop', and 'multigraph' >>> random.seed(2) >>> engine.gen_using(dimacs_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.gr','r') >>> print(file.read()) c FILE :testfile.gr c No. of vertices :10 c No. of edges :7 c Max. weight :148 c Min. weight :7 p sp 10 7 a 4 3 -64 a 5 6 148 a 5 9 110 a 6 10 -139 a 7 7 7 a 8 2 -97 a 9 1 60 >>> random.seed(4) >>> engine.gen_using(dimacs_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> file=open('testfile2.gr','r') >>> print(file.read()) c FILE :testfile2.gr c No. of vertices :30 c No. of edges :35 c Max. weight :50 c Min. weight :2 p sp 30 35 a 1 10 46 a 2 18 5 a 2 4 25 a 2 22 -48 a 4 23 -17 a 5 7 -13 a 7 15 10 a 7 17 -40 a 8 8 -42 a 8 25 11 a 9 29 -5 a 10 3 -36 a 10 27 -48 a 11 13 -27 a 11 26 -27 a 11 21 14 a 11 16 -2 a 14 20 -44 a 14 14 43 a 14 12 26 a 15 28 -11 a 16 30 -40 a 16 24 20 a 19 19 7 a 20 12 -29 a 20 1 22 a 22 24 20 a 22 23 -9 a 23 18 18 a 23 27 28 a 24 6 -24 a 25 17 23 a 27 6 -50 a 28 21 28 a 28 13 -13 >>> random.seed(20) >>> engine.gen_using(dimacs_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':1, 'self_loop':1, 'multigraph':0}) 137 >>> file=open('testfile3.gr','r') >>> print(file.read()) c FILE :testfile3.gr c No. of vertices :100 c No. of edges :137 c Max. weight :30 c Min. weight :10 p sp 100 137 a 1 34 30 a 3 76 15 a 3 5 23 a 4 13 13 a 4 21 20 a 4 67 28 a 5 60 16 a 5 32 20 a 5 92 20 a 6 64 12 a 6 94 26 a 7 62 12 a 7 36 28 a 7 42 11 a 8 20 12 a 9 47 19 a 10 49 15 a 10 27 10 a 11 48 17 a 11 51 11 a 13 58 14 a 13 70 29 a 14 37 30 a 14 61 27 a 14 87 15 a 15 84 13 a 16 83 28 a 17 45 17 a 17 24 29 a 17 18 26 a 18 59 15 a 19 98 12 a 21 2 30 a 21 99 20 a 22 69 26 a 22 96 11 a 22 88 15 a 24 79 20 a 24 12 12 a 24 82 13 a 26 50 30 a 26 30 19 a 29 52 26 a 31 25 26 a 32 68 14 a 33 65 13 a 33 78 13 a 33 55 17 a 34 63 13 a 35 44 27 a 35 57 14 a 37 74 10 a 37 41 16 a 37 100 30 a 38 72 13 a 38 56 16 a 39 91 19 a 39 43 13 a 41 28 22 a 41 81 19 a 42 90 13 a 42 46 28 a 42 97 16 a 45 86 10 a 45 53 18 a 46 85 13 a 46 23 11 a 47 71 29 a 48 95 12 a 48 77 19 a 48 93 11 a 49 75 22 a 50 73 18 a 50 40 24 a 50 54 28 a 51 80 17 a 51 66 19 a 51 89 20 a 52 58 29 a 52 16 21 a 52 43 12 a 53 8 13 a 53 98 17 a 54 55 10 a 56 62 26 a 56 27 10 a 57 70 26 a 58 44 22 a 59 90 27 a 59 91 19 a 59 78 29 a 60 87 12 a 60 92 25 a 61 69 14 a 61 79 17 a 62 25 21 a 63 97 27 a 63 29 30 a 65 9 26 a 65 64 21 a 66 67 27 a 66 95 19 a 66 93 30 a 68 30 18 a 70 83 12 a 70 99 15 a 71 31 17 a 71 89 20 a 73 36 18 a 75 72 12 a 76 2 26 a 76 12 25 a 76 86 22 a 78 23 19 a 78 100 27 a 79 40 24 a 80 84 26 a 80 80 14 a 81 20 16 a 82 15 16 a 82 88 22 a 83 19 19 a 84 85 13 a 84 28 16 a 85 77 16 a 85 94 23 a 86 1 21 a 87 74 15 a 87 96 19 a 90 93 22 a 92 49 14 a 95 98 26 a 95 55 11 a 97 38 28 a 99 19 29 a 99 89 24 a 100 40 11 >>> dimacs_maker({}) Traceback (most recent call last): ... TypeError: dimacs_maker() missing 2 required positional arguments: 'weight_dic' and 'mdata' >>> random.seed(2) >>> engine.gen_using(json_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.json','r') >>> testfile_1=json.load(file) >>> testfile_1['graph']['nodes'][1] {'id': 2} >>> testfile_1['graph']['edges'][1]['source'] 5 >>> testfile_1['graph']['edges'][1]['target'] 6 >>> testfile_1['graph']['edges'][1]['weight'] 148 >>> json_to_yaml('testfile') >>> file=open('testfile.yaml','r') >>> testfile_1_yaml=yaml.safe_load(file) >>> testfile_1_yaml['graph']['edges'][1]['source'] 5 >>> testfile_1_yaml['graph']['edges'][1]['target'] 6 >>> testfile_1_yaml['graph']['edges'][1]['weight'] 148 >>> json_to_pickle('testfile') >>> testfile_1_p=pickle.load( open( 'testfile.p', 'rb' ) ) >>> testfile_1_p['graph']['edges'][1]['source'] 5 >>> testfile_1_p['graph']['edges'][1]['target'] 6 >>> testfile_1_p['graph']['edges'][1]['weight'] 148 >>> random.seed(4) >>> engine.gen_using(json_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> file=open('testfile2.json','r') >>> testfile_2=json.load(file) >>> testfile_2['graph']['nodes'][1] {'id': 2} >>> testfile_2['graph']['edges'][1]['source'] 2 >>> testfile_2['graph']['edges'][1]['target'] 18 >>> testfile_2['graph']['edges'][1]['weight'] 5 >>> json_to_yaml('testfile2') >>> file=open('testfile2.yaml','r') >>> testfile_2_yaml=yaml.safe_load(file) >>> testfile_2_yaml['graph']['nodes'][1] {'id': 2} >>> testfile_2_yaml['graph']['edges'][1]['source'] 2 >>> testfile_2_yaml['graph']['edges'][1]['target'] 18 >>> testfile_2_yaml['graph']['edges'][1]['weight'] 5 >>> json_to_pickle('testfile2') >>> testfile_2_p=pickle.load( open( 'testfile2.p', 'rb' ) ) >>> testfile_2_p['graph']['edges'][1]['source'] 2 >>> testfile_2_p['graph']['edges'][1]['target'] 18 >>> testfile_2_p['graph']['edges'][1]['weight'] 5 >>> random.seed(20) >>> engine.gen_using(json_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':1, 'self_loop':1, 'multigraph':0}) 137 >>> file=open('testfile3.json','r') >>> testfile_3=json.load(file) >>> testfile_3['graph']['nodes'][1] {'id': 2} >>> testfile_3['graph']['edges'][1]['source'] 3 >>> testfile_3['graph']['edges'][1]['target'] 76 >>> testfile_3['graph']['edges'][1]['weight'] 15 >>> json_to_yaml('testfile3') >>> file=open('testfile3.yaml','r') >>> testfile_3_yaml=yaml.safe_load(file) >>> testfile_3_yaml['graph']['nodes'][1] {'id': 2} >>> testfile_3_yaml['graph']['edges'][1]['source'] 3 >>> testfile_3_yaml['graph']['edges'][1]['target'] 76 >>> testfile_3_yaml['graph']['edges'][1]['weight'] 15 >>> json_to_yaml('testfile24') [Error] Failed to generate YAML file! >>> json_to_pickle('testfile24') [Error] Failed to generate Pickle file! >>> json_maker({}, {}) Traceback (most recent call last): ... TypeError: json_maker() missing 1 required positional argument: 'mdata' >>> json_to_pickle('testfile3') >>> testfile_3_p=pickle.load( open( 'testfile3.p', 'rb' ) ) >>> testfile_3_p['graph']['edges'][1]['source'] 3 >>> testfile_3_p['graph']['edges'][1]['target'] 76 >>> testfile_3_p['graph']['edges'][1]['weight'] 15 >>> random.seed(2) >>> engine.gen_using(csv_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> random.seed(2) >>> engine.gen_using(gml_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.gml','r') >>> print(file.read()) graph [ multigraph 0 directed 1 node [ id 1 label "Node 1" ] node [ id 2 label "Node 2" ] node [ id 3 label "Node 3" ] node [ id 4 label "Node 4" ] node [ id 5 label "Node 5" ] node [ id 6 label "Node 6" ] node [ id 7 label "Node 7" ] node [ id 8 label "Node 8" ] node [ id 9 label "Node 9" ] node [ id 10 label "Node 10" ] edge [ source 4 target 3 value -64 ] edge [ source 5 target 6 value 148 ] edge [ source 5 target 9 value 110 ] edge [ source 6 target 10 value -139 ] edge [ source 7 target 7 value 7 ] edge [ source 8 target 2 value -97 ] edge [ source 9 target 1 value 60 ] ] >>> random.seed(2) >>> engine.gen_using(gexf_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.gexf', 'r') >>> random.seed(2) >>> engine.gen_using(mtx_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> random.seed(2) >>> engine.gen_using(tsv_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.mtx','r') >>> print(file.read()) %%MatrixMarket matrix coordinate real general 10 10 7 4 3 -64 5 6 148 5 9 110 6 10 -139 7 7 7 8 2 -97 9 1 60 >>> random.seed(2) >>> engine.gen_using(gdf_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.gdf','r') >>> print(file.read()) nodedef>name VARCHAR,label VARCHAR 1,Node1 2,Node2 3,Node3 4,Node4 5,Node5 6,Node6 7,Node7 8,Node8 9,Node9 10,Node10 edgedef>node1 VARCHAR,node2 VARCHAR,weight DOUBLE 4,3,-64 5,6,148 5,9,110 6,10,-139 7,7,7 8,2,-97 9,1,60 >>> random.seed(2) >>> engine.gen_using(gl_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.gl','r') >>> print(file.read()) 4 3:-64 5 6:148 9:110 6 10:-139 7 7:7 8 2:-97 9 1:60 >>> file=open('testfile.csv','r') >>> print(file.read()) 4,3,-64 5,6,148 5,9,110 6,10,-139 7,7,7 8,2,-97 9,1,60 >>> random.seed(4) >>> engine.gen_using(csv_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> file=open('testfile2.csv','r') >>> print(file.read()) 1,10,46 2,18,5 2,4,25 2,22,-48 4,23,-17 5,7,-13 7,15,10 7,17,-40 8,8,-42 8,25,11 9,29,-5 10,3,-36 10,27,-48 11,13,-27 11,26,-27 11,21,14 11,16,-2 14,20,-44 14,14,43 14,12,26 15,28,-11 16,30,-40 16,24,20 19,19,7 20,12,-29 20,1,22 22,24,20 22,23,-9 23,18,18 23,27,28 24,6,-24 25,17,23 27,6,-50 28,21,28 28,13,-13 >>> random.seed(4) >>> engine.gen_using(csv_maker, 'testfile4', {'min_weight':0, 'max_weight':50.2, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 41 >>> file=open('testfile4.csv','r') >>> print(file.read()) 1,10,36.2 2,6,3.3 2,16,-40.2 2,29,11.1 3,17,-39.1 3,7,-10.8 3,3,-40.2 4,12,-14.5 5,9,-33.7 5,28,8.9 6,21,47.4 6,27,-0.4 6,15,-42.6 7,20,-30.1 8,23,11.7 8,18,4.1 8,25,-26.0 9,24,50.1 9,13,20.7 9,14,-13.9 10,26,-31.8 10,19,-5.1 12,22,6.1 13,30,-1.3 14,11,-36.9 14,22,16.2 15,16,-43.2 15,11,-31.0 16,19,12.6 17,21,18.2 18,18,-39.3 18,25,-28.7 19,23,-46.0 24,20,27.4 25,4,-50.1 25,1,-38.8 26,27,-10.1 26,30,-24.7 26,29,-12.5 27,28,-9.4 29,20,26.4 >>> random.seed(20) >>> engine.gen_using(csv_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':1, 'self_loop':1, 'multigraph':0}) 137 >>> file=open('testfile3.csv','r') >>> print(file.read()) 1,34,30 3,76,15 3,5,23 4,13,13 4,21,20 4,67,28 5,60,16 5,32,20 5,92,20 6,64,12 6,94,26 7,62,12 7,36,28 7,42,11 8,20,12 9,47,19 10,49,15 10,27,10 11,48,17 11,51,11 13,58,14 13,70,29 14,37,30 14,61,27 14,87,15 15,84,13 16,83,28 17,45,17 17,24,29 17,18,26 18,59,15 19,98,12 21,2,30 21,99,20 22,69,26 22,96,11 22,88,15 24,79,20 24,12,12 24,82,13 26,50,30 26,30,19 29,52,26 31,25,26 32,68,14 33,65,13 33,78,13 33,55,17 34,63,13 35,44,27 35,57,14 37,74,10 37,41,16 37,100,30 38,72,13 38,56,16 39,91,19 39,43,13 41,28,22 41,81,19 42,90,13 42,46,28 42,97,16 45,86,10 45,53,18 46,85,13 46,23,11 47,71,29 48,95,12 48,77,19 48,93,11 49,75,22 50,73,18 50,40,24 50,54,28 51,80,17 51,66,19 51,89,20 52,58,29 52,16,21 52,43,12 53,8,13 53,98,17 54,55,10 56,62,26 56,27,10 57,70,26 58,44,22 59,90,27 59,91,19 59,78,29 60,87,12 60,92,25 61,69,14 61,79,17 62,25,21 63,97,27 63,29,30 65,9,26 65,64,21 66,67,27 66,95,19 66,93,30 68,30,18 70,83,12 70,99,15 71,31,17 71,89,20 73,36,18 75,72,12 76,2,26 76,12,25 76,86,22 78,23,19 78,100,27 79,40,24 80,84,26 80,80,14 81,20,16 82,15,16 82,88,22 83,19,19 84,85,13 84,28,16 85,77,16 85,94,23 86,1,21 87,74,15 87,96,19 90,93,22 92,49,14 95,98,26 95,55,11 97,38,28 99,19,29 99,89,24 100,40,11 >>> csv_maker({}) Traceback (most recent call last): ... TypeError: csv_maker() missing 2 required positional arguments: 'weight_dic' and 'mdata' >>> random.seed(2) >>> engine.gen_using(wel_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.wel','r') >>> print(file.read()) 4 3 -64 5 6 148 5 9 110 6 10 -139 7 7 7 8 2 -97 9 1 60 >>> random.seed(4) >>> engine.gen_using(wel_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> file=open('testfile2.wel','r') >>> print(file.read()) 1 10 46 2 18 5 2 4 25 2 22 -48 4 23 -17 5 7 -13 7 15 10 7 17 -40 8 8 -42 8 25 11 9 29 -5 10 3 -36 10 27 -48 11 13 -27 11 26 -27 11 21 14 11 16 -2 14 20 -44 14 14 43 14 12 26 15 28 -11 16 30 -40 16 24 20 19 19 7 20 12 -29 20 1 22 22 24 20 22 23 -9 23 18 18 23 27 28 24 6 -24 25 17 23 27 6 -50 28 21 28 28 13 -13 >>> random.seed(20) >>> engine.gen_using(wel_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':1, 'self_loop':1, 'multigraph':0}) 137 >>> file=open('testfile3.wel','r') >>> print(file.read()) 1 34 30 3 76 15 3 5 23 4 13 13 4 21 20 4 67 28 5 60 16 5 32 20 5 92 20 6 64 12 6 94 26 7 62 12 7 36 28 7 42 11 8 20 12 9 47 19 10 49 15 10 27 10 11 48 17 11 51 11 13 58 14 13 70 29 14 37 30 14 61 27 14 87 15 15 84 13 16 83 28 17 45 17 17 24 29 17 18 26 18 59 15 19 98 12 21 2 30 21 99 20 22 69 26 22 96 11 22 88 15 24 79 20 24 12 12 24 82 13 26 50 30 26 30 19 29 52 26 31 25 26 32 68 14 33 65 13 33 78 13 33 55 17 34 63 13 35 44 27 35 57 14 37 74 10 37 41 16 37 100 30 38 72 13 38 56 16 39 91 19 39 43 13 41 28 22 41 81 19 42 90 13 42 46 28 42 97 16 45 86 10 45 53 18 46 85 13 46 23 11 47 71 29 48 95 12 48 77 19 48 93 11 49 75 22 50 73 18 50 40 24 50 54 28 51 80 17 51 66 19 51 89 20 52 58 29 52 16 21 52 43 12 53 8 13 53 98 17 54 55 10 56 62 26 56 27 10 57 70 26 58 44 22 59 90 27 59 91 19 59 78 29 60 87 12 60 92 25 61 69 14 61 79 17 62 25 21 63 97 27 63 29 30 65 9 26 65 64 21 66 67 27 66 95 19 66 93 30 68 30 18 70 83 12 70 99 15 71 31 17 71 89 20 73 36 18 75 72 12 76 2 26 76 12 25 76 86 22 78 23 19 78 100 27 79 40 24 80 84 26 80 80 14 81 20 16 82 15 16 82 88 22 83 19 19 84 85 13 84 28 16 85 77 16 85 94 23 86 1 21 87 74 15 87 96 19 90 93 22 92 49 14 95 98 26 95 55 11 97 38 28 99 19 29 99 89 24 100 40 11 >>> wel_maker({}, {}) Traceback (most recent call last): ... TypeError: wel_maker() missing 1 required positional argument: 'mdata' >>> random.seed(2) >>> engine.gen_using(lp_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.lp','r') >>> print(file.read()) node(1). node(2). node(3). node(4). node(5). node(6). node(7). node(8). node(9). node(10). edge(4,3,-64). edge(5,6,148). edge(5,9,110). edge(6,10,-139). edge(7,7,7). edge(8,2,-97). edge(9,1,60). >>> random.seed(4) >>> engine.gen_using(lp_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> file=open('testfile2.lp','r') >>> print(file.read()) node(1). node(2). node(3). node(4). node(5). node(6). node(7). node(8). node(9). node(10). node(11). node(12). node(13). node(14). node(15). node(16). node(17). node(18). node(19). node(20). node(21). node(22). node(23). node(24). node(25). node(26). node(27). node(28). node(29). node(30). edge(1,10,46). edge(2,18,5). edge(2,4,25). edge(2,22,-48). edge(4,23,-17). edge(5,7,-13). edge(7,15,10). edge(7,17,-40). edge(8,8,-42). edge(8,25,11). edge(9,29,-5). edge(10,3,-36). edge(10,27,-48). edge(11,13,-27). edge(11,26,-27). edge(11,21,14). edge(11,16,-2). edge(14,20,-44). edge(14,14,43). edge(14,12,26). edge(15,28,-11). edge(16,30,-40). edge(16,24,20). edge(19,19,7). edge(20,12,-29). edge(20,1,22). edge(22,24,20). edge(22,23,-9). edge(23,18,18). edge(23,27,28). edge(24,6,-24). edge(25,17,23). edge(27,6,-50). edge(28,21,28). edge(28,13,-13). >>> random.seed(2) >>> engine.gen_using(tgf_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.tgf','r') >>> print(file.read()) 1 2 3 4 5 6 7 8 9 10 # 4 3 -64 5 6 148 5 9 110 6 10 -139 7 7 7 8 2 -97 9 1 60 >>> random.seed(4) >>> engine.gen_using(tgf_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> file=open('testfile2.tgf','r') >>> print(file.read()) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 # 1 10 46 2 18 5 2 4 25 2 22 -48 4 23 -17 5 7 -13 7 15 10 7 17 -40 8 8 -42 8 25 11 9 29 -5 10 3 -36 10 27 -48 11 13 -27 11 26 -27 11 21 14 11 16 -2 14 20 -44 14 14 43 14 12 26 15 28 -11 16 30 -40 16 24 20 19 19 7 20 12 -29 20 1 22 22 24 20 22 23 -9 23 18 18 23 27 28 24 6 -24 25 17 23 27 6 -50 28 21 28 28 13 -13 >>> random.seed(2) >>> engine.gen_using(dl_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.dl','r') >>> print(file.read()) dl format=edgelist1 n=10 data: 4 3 -64 5 6 148 5 9 110 6 10 -139 7 7 7 8 2 -97 9 1 60 >>> random.seed(4) >>> engine.gen_using(dl_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> file=open('testfile2.dl','r') >>> print(file.read()) dl format=edgelist1 n=30 data: 1 10 46 2 18 5 2 4 25 2 22 -48 4 23 -17 5 7 -13 7 15 10 7 17 -40 8 8 -42 8 25 11 9 29 -5 10 3 -36 10 27 -48 11 13 -27 11 26 -27 11 21 14 11 16 -2 14 20 -44 14 14 43 14 12 26 15 28 -11 16 30 -40 16 24 20 19 19 7 20 12 -29 20 1 22 22 24 20 22 23 -9 23 18 18 23 27 28 24 6 -24 25 17 23 27 6 -50 28 21 28 28 13 -13 >>> file.close() >>> os.remove('testfile.csv') >>> os.remove('testfile.gml') >>> os.remove('testfile.gexf') >>> os.remove('testfile.tsv') >>> os.remove('testfile.dl') >>> os.remove('testfile.gr') >>> os.remove('testfile.json') >>> os.remove('testfile.lp') >>> os.remove('testfile.p') >>> os.remove('testfile.tgf') >>> os.remove('testfile.wel') >>> os.remove('testfile.yaml') >>> os.remove('testfile.mtx') >>> os.remove('testfile.gdf') >>> os.remove('testfile.gl') >>> os.remove('testfile2.csv') >>> os.remove('testfile2.dl') >>> os.remove('testfile2.gr') >>> os.remove('testfile2.json') >>> os.remove('testfile2.lp') >>> os.remove('testfile2.p') >>> os.remove('testfile2.tgf') >>> os.remove('testfile2.wel') >>> os.remove('testfile2.yaml') >>> os.remove('testfile3.csv') >>> os.remove('testfile4.csv') >>> os.remove('testfile3.gr') >>> os.remove('testfile3.json') >>> os.remove('testfile3.p') >>> os.remove('testfile3.wel') >>> os.remove('testfile3.yaml') >>> os.remove('logfile.log') """ pyrgg-1.6/pytest.ini000066400000000000000000000001571471451651000145500ustar00rootroot00000000000000# content of pytest.ini [pytest] addopts = --doctest-modules doctest_optionflags= NORMALIZE_WHITESPACE ELLIPSISpyrgg-1.6/requirements.txt000066400000000000000000000000251471451651000157750ustar00rootroot00000000000000pyyaml>=3.12 art>=0.7pyrgg-1.6/setup.py000066400000000000000000000065761471451651000142440ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Setup module.""" try: from setuptools import setup except ImportError: from distutils.core import setup from textwrap import fill _description = """\ PyRGG is a user-friendly synthetic random graph generator that is written in Python and supports multiple graph file formats, such as DIMACS-Graph files. It can generate graphs of various sizes and is specifically designed to create input files for a wide range of graph-based research applications, including testing, benchmarking, and performance analysis of graph processing frameworks. PyRGG is aimed at computer scientists who are studying graph algorithms and graph processing frameworks. """ PYRGG_DESCRIPTION = fill(_description, width=113) def get_requires(): """Read requirements.txt.""" requirements = open("requirements.txt", "r").read() return list(filter(lambda x: x != "", requirements.split())) def read_description(): """Read README.md and CHANGELOG.md.""" try: with open("README.md") as r: description = "\n" description += r.read() with open("CHANGELOG.md") as c: description += "\n" description += c.read() return description except Exception: return PYRGG_DESCRIPTION.replace("\n", " ") setup( name='pyrgg', packages=['pyrgg', 'pyrgg.engines'], version='1.6', description='Python Random Graph Generator', long_description=read_description(), long_description_content_type='text/markdown', author='PyRGG Development Team', author_email='info@pyrgg.site', url='https://github.com/sepandhaghighi/pyrgg', download_url='https://github.com/sepandhaghighi/pyrgg/tarball/v1.6', keywords='random graph python3 python generator graph-process generator DIMACS JSON YAML Pickle CSV TSV WEL ASP TGF UCINET', project_urls={ 'Webpage': 'https://www.pyrgg.site', 'Source': 'https://github.com/sepandhaghighi/pyrgg', }, install_requires=get_requires(), python_requires='>=3.6', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Natural Language :: English', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', 'Intended Audience :: Developers', 'Intended Audience :: Education', 'Intended Audience :: End Users/Desktop', 'Intended Audience :: Manufacturing', 'Intended Audience :: Science/Research', 'Topic :: Scientific/Engineering :: Information Analysis', 'Topic :: Education', 'Topic :: Scientific/Engineering', 'Topic :: Scientific/Engineering :: Artificial Intelligence', 'Topic :: Scientific/Engineering :: Human Machine Interfaces', 'Topic :: Scientific/Engineering :: Mathematics', 'Topic :: Scientific/Engineering :: Physics', ], license='MIT', entry_points={ 'console_scripts': [ 'pyrgg = pyrgg.__main__:main', ]}) pyrgg-1.6/test/000077500000000000000000000000001471451651000134735ustar00rootroot00000000000000pyrgg-1.6/test/engines/000077500000000000000000000000001471451651000151235ustar00rootroot00000000000000pyrgg-1.6/test/engines/erdos_reyni_gilbert_test.py000066400000000000000000000270701471451651000225740ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ >>> from pyrgg.functions import * >>> from pyrgg.graph_gen import * >>> import pyrgg.params >>> import random >>> import os >>> import json >>> import pyrgg.engines.erdos_reyni_gilbert as engine >>> os.environ["PYRGG_TEST_MODE"] = "1" >>> ###################################### >>> ## ========= logger function ========= >>> ###################################### >>> with open('logfile.log','a') as file: ... engine.logger(file,'test','2min',{'vertices':100,'edge_number':50,'direct':0,'engine':2,'probability':0.5,'output_format':1}) >>> file = open('logfile.log','r') >>> print("\\n".join(file.read().splitlines()[1:-1])) Filename : test Vertices : 100 Probability : 0.5 Total Edges : 50 Directed : False Engine : 2 (erg) Elapsed Time : 2min >>> class StrError: ... def __init__(self): ... pass ... def __str__(self): ... raise ValueError >>> str_error_object = StrError() >>> with open('logfile.log','a') as file: ... engine.logger(file,'test','2min',{'vertices':str_error_object,'edge_number':50,'direct':0,'engine':2,'probability':0.5,'output_format':1}) [Error] Logger failed! >>> ########################################## >>> ## ========= edge_gen function ========= >>> ########################################## >>> random.seed(2) >>> edge_dic, weight_dic, edge_number = engine.edge_gen(20, 0.5, False) >>> edge_dic == {1: [4, 5, 9, 13, 14, 15, 20], 2: [3, 4, 5, 6, 7, 8, 12, 13, 14, 15, 19], 3: [9, 12, 15, 17], 4: [5, 8, 12, 13, 14, 16, 18, 20], 5: [12, 13, 17], 6: [7, 9, 11, 12, 14, 17, 18, 20],7: [9, 10, 11, 13, 15, 17, 18, 20], 8: [9, 11, 12, 14, 15, 16, 19, 20], 9: [10, 11, 18, 19], 10: [11, 12, 13, 15, 17, 19], 11: [12, 14, 15, 17, 18, 19, 20], 12: [13, 19, 20], 13: [14, 19, 20], 14: [16, 17, 18, 19], 15: [16, 17, 18, 19, 20], 16: [18, 19], 17: [18], 18: [20], 19: [], 20: []} True >>> edge_number == 93 True >>> random.seed(11) >>> edge_dic, weight_dic, edge_number = engine.edge_gen(20, 0.1, True) >>> edge_dic == {1: [12, 14, 17], 2: [4, 6, 9], 3: [7, 14, 20], 4: [5, 12], 5: [], 6: [1, 2], 7: [8, 18], 8: [6, 10], 9: [2, 3, 7, 14], 10: [15], 11: [5, 7, 17], 12: [], 13: [16, 17, 19], 14: [4, 7, 11], 15: [3, 17], 16: [], 17: [7, 12, 14], 18: [9], 19: [12], 20: [1, 19]} True >>> edge_number == 40 True >>> engine.edge_gen(0) Traceback (most recent call last): ... TypeError: edge_gen() missing 2 required positional arguments: 'p' and 'direct' >>> ######################################### >>> ## ========= gen_using function ========= >>> ######################################### >>> #################### dimacs_maker #################### >>> random.seed(2) >>> engine.gen_using(dimacs_maker, 'testfile', {'vertices':10, 'probability':0.1, 'direct':0}) 5 >>> file=open('testfile.gr','r') >>> print(file.read()) c FILE :testfile.gr c No. of vertices :10 c No. of edges :5 c Max. weight :1 c Min. weight :1 p sp 10 5 a 1 4 1 a 1 5 1 a 3 7 1 a 3 8 1 a 4 10 1 >>> random.seed(4) >>> engine.gen_using(dimacs_maker, 'testfile2', {'vertices':10, 'probability':0.1, 'direct':1}) 8 >>> file=open('testfile2.gr','r') >>> print(file.read()) c FILE :testfile2.gr c No. of vertices :10 c No. of edges :8 c Max. weight :1 c Min. weight :1 p sp 10 8 a 1 6 1 a 3 9 1 a 4 6 1 a 6 3 1 a 6 4 1 a 8 4 1 a 9 3 1 a 9 10 1 >>> #################### json_maker #################### >>> random.seed(2) >>> engine.gen_using(json_maker, 'testfile', {'vertices':10, 'probability':0.1, 'direct':0}) 5 >>> file=open('testfile.json','r') >>> testfile_1=json.load(file) >>> testfile_1['graph']['nodes'][1] {'id': 2} >>> testfile_1['graph']['edges'][1]['source'] 1 >>> testfile_1['graph']['edges'][1]['target'] 5 >>> random.seed(4) >>> engine.gen_using(json_maker, 'testfile2', {'vertices':10, 'probability':0.1, 'direct':1}) 8 >>> file=open('testfile2.json','r') >>> testfile_2=json.load(file) >>> testfile_2['graph']['nodes'][1] {'id': 2} >>> testfile_2['graph']['edges'][1]['source'] 3 >>> testfile_2['graph']['edges'][1]['target'] 9 >>> #################### csv_maker #################### >>> random.seed(2) >>> engine.gen_using(csv_maker, 'testfile', {'vertices':10, 'probability':0.1, 'direct':0}) 5 >>> file=open('testfile.csv','r') >>> print(file.read()) 1,4,1 1,5,1 3,7,1 3,8,1 4,10,1 >>> random.seed(4) >>> engine.gen_using(csv_maker, 'testfile2', {'vertices':10, 'probability':0.1, 'direct':1}) 8 >>> file=open('testfile2.csv','r') >>> print(file.read()) 1,6,1 3,9,1 4,6,1 6,3,1 6,4,1 8,4,1 9,3,1 9,10,1 >>> #################### gdf_maker #################### >>> random.seed(2) >>> engine.gen_using(gdf_maker, 'testfile', {'vertices':10, 'probability':0.1, 'direct':0}) 5 >>> file=open('testfile.gdf','r') >>> print(file.read()) nodedef>name VARCHAR,label VARCHAR 1,Node1 2,Node2 3,Node3 4,Node4 5,Node5 6,Node6 7,Node7 8,Node8 9,Node9 10,Node10 edgedef>node1 VARCHAR,node2 VARCHAR,weight DOUBLE 1,4,1 1,5,1 3,7,1 3,8,1 4,10,1 >>> random.seed(4) >>> engine.gen_using(gdf_maker, 'testfile2', {'vertices':10, 'probability':0.1, 'direct':1}) 8 >>> file=open('testfile2.gdf','r') >>> print(file.read()) nodedef>name VARCHAR,label VARCHAR 1,Node1 2,Node2 3,Node3 4,Node4 5,Node5 6,Node6 7,Node7 8,Node8 9,Node9 10,Node10 edgedef>node1 VARCHAR,node2 VARCHAR,weight DOUBLE 1,6,1 3,9,1 4,6,1 6,3,1 6,4,1 8,4,1 9,3,1 9,10,1 >>> #################### gl_maker #################### >>> random.seed(2) >>> engine.gen_using(gl_maker, 'testfile', {'vertices':10, 'probability':0.1, 'direct':0}) 5 >>> file=open('testfile.gl','r') >>> print(file.read()) 1 4:1 5:1 3 7:1 8:1 4 10:1 >>> random.seed(4) >>> engine.gen_using(gl_maker, 'testfile2', {'vertices':10, 'probability':0.1, 'direct':1}) 8 >>> file=open('testfile2.gl','r') >>> print(file.read()) 1 6:1 3 9:1 4 6:1 6 3:1 4:1 8 4:1 9 3:1 10:1 >>> #################### mtx_maker #################### >>> from scipy.io import mmread >>> random.seed(2) >>> engine.gen_using(mtx_maker, 'testfile', {'vertices':10, 'probability':0.1, 'direct':0}) 5 >>> g = mmread("testfile.mtx") >>> print(g.data.tolist()) [1.0, 1.0, 1.0, 1.0, 1.0] >>> random.seed(4) >>> engine.gen_using(mtx_maker, 'testfile2', {'vertices':10, 'probability':0.1, 'direct':1}) 8 >>> g = mmread("testfile2.mtx") >>> print(g.data.tolist()) [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] >>> #################### tsv_maker #################### >>> random.seed(2) >>> engine.gen_using(tsv_maker, 'testfile', {'vertices':10, 'probability':0.1, 'direct':0}) 5 >>> file=open('testfile.tsv','r') >>> print(file.read()) 1 4 1 1 5 1 3 7 1 3 8 1 4 10 1 >>> random.seed(4) >>> engine.gen_using(tsv_maker, 'testfile2', {'vertices':10, 'probability':0.1, 'direct':1}) 8 >>> file=open('testfile2.tsv','r') >>> print(file.read()) 1 6 1 3 9 1 4 6 1 6 3 1 6 4 1 8 4 1 9 3 1 9 10 1 >>> #################### wel_maker #################### >>> random.seed(2) >>> engine.gen_using(wel_maker, 'testfile', {'vertices':10, 'probability':0.1, 'direct':0}) 5 >>> file=open('testfile.wel','r') >>> print(file.read()) 1 4 1 1 5 1 3 7 1 3 8 1 4 10 1 >>> random.seed(4) >>> engine.gen_using(wel_maker, 'testfile2', {'vertices':10, 'probability':0.1, 'direct':1}) 8 >>> file=open('testfile2.wel','r') >>> print(file.read()) 1 6 1 3 9 1 4 6 1 6 3 1 6 4 1 8 4 1 9 3 1 9 10 1 >>> #################### lp_maker #################### >>> random.seed(2) >>> engine.gen_using(lp_maker, 'testfile', {'vertices':10, 'probability':0.1, 'direct':0}) 5 >>> file=open('testfile.lp','r') >>> print(file.read()) node(1). node(2). node(3). node(4). node(5). node(6). node(7). node(8). node(9). node(10). edge(1,4,1). edge(1,5,1). edge(3,7,1). edge(3,8,1). edge(4,10,1). >>> random.seed(4) >>> engine.gen_using(lp_maker, 'testfile2', {'vertices':10, 'probability':0.1, 'direct':1}) 8 >>> file=open('testfile2.lp','r') >>> print(file.read()) node(1). node(2). node(3). node(4). node(5). node(6). node(7). node(8). node(9). node(10). edge(1,6,1). edge(3,9,1). edge(4,6,1). edge(6,3,1). edge(6,4,1). edge(8,4,1). edge(9,3,1). edge(9,10,1). >>> #################### tgf_maker #################### >>> random.seed(2) >>> engine.gen_using(tgf_maker, 'testfile', {'vertices':10, 'probability':0.1, 'direct':0}) 5 >>> file=open('testfile.tgf','r') >>> print(file.read()) 1 2 3 4 5 6 7 8 9 10 # 1 4 1 1 5 1 3 7 1 3 8 1 4 10 1 >>> random.seed(4) >>> engine.gen_using(tgf_maker, 'testfile2', {'vertices':10, 'probability':0.1, 'direct':1}) 8 >>> file=open('testfile2.tgf','r') >>> print(file.read()) 1 2 3 4 5 6 7 8 9 10 # 1 6 1 3 9 1 4 6 1 6 3 1 6 4 1 8 4 1 9 3 1 9 10 1 >>> #################### dl_maker #################### >>> random.seed(2) >>> engine.gen_using(dl_maker, 'testfile', {'vertices':10, 'probability':0.1, 'direct':0}) 5 >>> file=open('testfile.dl','r') >>> print(file.read()) dl format=edgelist1 n=10 data: 1 4 1 1 5 1 3 7 1 3 8 1 4 10 1 >>> random.seed(4) >>> engine.gen_using(dl_maker, 'testfile2', {'vertices':10, 'probability':0.1, 'direct':1}) 8 >>> file=open('testfile2.dl','r') >>> print(file.read()) dl format=edgelist1 n=10 data: 1 6 1 3 9 1 4 6 1 6 3 1 6 4 1 8 4 1 9 3 1 9 10 1 >>> #################### gml_maker #################### >>> from networkx.readwrite.gml import read_gml >>> random.seed(2) >>> engine.gen_using(gml_maker, 'testfile', {'vertices':10, 'probability':0.1, 'direct':0}) 5 >>> gml1 = read_gml("testfile.gml") >>> type(gml1) >>> random.seed(4) >>> engine.gen_using(gml_maker, 'testfile2', {'vertices':10, 'probability':0.1, 'direct':1}) 8 >>> gml2 = read_gml("testfile2.gml") >>> type(gml2) >>> #################### gexf_maker #################### >>> from networkx.readwrite.gexf import read_gexf >>> random.seed(2) >>> engine.gen_using(gexf_maker, 'testfile', {'vertices':10, 'probability':0.1, 'direct':0}) 5 >>> gexf1 = read_gexf("testfile.gexf") >>> type(gexf1) >>> random.seed(4) >>> engine.gen_using(gexf_maker, 'testfile2', {'vertices':10, 'probability':0.1, 'direct':1}) 8 >>> gexf2 = read_gexf("testfile2.gexf") >>> type(gexf2) >>> #################### dot_maker #################### >>> import pydot >>> random.seed(2) >>> engine.gen_using(dot_maker, 'testfile', {'vertices':10, 'probability':0.1, 'direct':0}) 5 >>> file=open('testfile.gv','r') >>> g1 = pydot.graph_from_dot_data(file.read()) >>> g1[0].get_type() 'graph' >>> len(g1[0].get_edge_list()) 5 >>> random.seed(4) >>> engine.gen_using(dot_maker, 'testfile2', {'vertices':10, 'probability':0.1, 'direct':1}) 8 >>> file=open('testfile2.gv','r') >>> g2 = pydot.graph_from_dot_data(file.read()) >>> g2[0].get_type() 'digraph' >>> len(g2[0].get_edge_list()) 8 >>> file.close() >>> os.remove('testfile.gr') >>> os.remove('testfile2.gr') >>> os.remove('testfile.json') >>> os.remove('testfile2.json') >>> os.remove('testfile.csv') >>> os.remove('testfile2.csv') >>> os.remove('testfile.gdf') >>> os.remove('testfile2.gdf') >>> os.remove('testfile.gl') >>> os.remove('testfile2.gl') >>> os.remove('testfile.mtx') >>> os.remove('testfile2.mtx') >>> os.remove('testfile.tsv') >>> os.remove('testfile2.tsv') >>> os.remove('testfile.wel') >>> os.remove('testfile2.wel') >>> os.remove('testfile.lp') >>> os.remove('testfile2.lp') >>> os.remove('testfile.tgf') >>> os.remove('testfile2.tgf') >>> os.remove('testfile.dl') >>> os.remove('testfile2.dl') >>> os.remove('testfile.gml') >>> os.remove('testfile2.gml') >>> os.remove('testfile.gexf') >>> os.remove('testfile2.gexf') >>> os.remove('testfile.gv') >>> os.remove('testfile2.gv') >>> os.remove('logfile.log') """ pyrgg-1.6/test/engines/erdos_reyni_test.py000066400000000000000000000266311471451651000210660ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ >>> from pyrgg.functions import * >>> from pyrgg.graph_gen import * >>> import pyrgg.params >>> import random >>> import os >>> import json >>> import pyrgg.engines.erdos_reyni as engine >>> os.environ["PYRGG_TEST_MODE"] = "1" >>> ###################################### >>> ## ========= logger function ========= >>> ###################################### >>> with open('logfile.log','a') as file: ... engine.logger(file,'test','2min',{'vertices':100,'edge_number':50,'direct':0,'engine':3,'output_format':1}) >>> file = open('logfile.log','r') >>> print("\\n".join(file.read().splitlines()[1:-1])) Filename : test Vertices : 100 Total Edges : 50 Directed : False Engine : 3 (er) Elapsed Time : 2min >>> class StrError: ... def __init__(self): ... pass ... def __str__(self): ... raise ValueError >>> str_error_object = StrError() >>> with open('logfile.log','a') as file: ... engine.logger(file,'test','2min',{'vertices':str_error_object,'edge_number':50,'direct':0,'engine':3,'output_format':1}) [Error] Logger failed! >>> ########################################## >>> ## ========= edge_gen function ========= >>> ########################################## >>> random.seed(2) >>> edge_dic, weight_dic, edge_number = engine.edge_gen(20, 10, False) >>> edge_dic == {1: [13], 2: [9], 3: [4], 4: [], 5: [], 6: [], 7: [16], 8: [], 9: [10, 18], 10: [12, 17], 11: [13], 12: [], 13: [], 14: [], 15: [], 16: [], 17: [], 18: [20], 19: [], 20: []} True >>> edge_number == 10 True >>> random.seed(11) >>> edge_dic, weight_dic, edge_number = engine.edge_gen(20, 100, True) >>> edge_dic == {1: [8, 9, 14, 15, 18, 19], 2: [4, 11, 13, 15, 16, 17], 3: [1, 13, 14, 19], 4: [7, 9, 14, 15, 16, 19], 5: [4, 10, 15, 16, 18, 19], 6: [3, 4, 9, 12, 19], 7: [5, 10, 13, 15, 16, 17, 20], 8: [1, 18, 19, 20], 9: [3, 6, 11, 13, 16, 18, 20], 10: [1, 3, 4, 13, 17], 11: [6, 7, 8, 17, 18], 12: [3, 5, 8, 17], 13: [5, 15], 14: [7, 12, 17, 19], 15: [1, 4, 7, 9, 14, 19, 20], 16: [3, 7, 8, 9, 12, 20], 17: [3, 5, 8, 14, 16], 18: [2, 9, 10, 11, 14], 19: [4, 9, 12], 20: [1, 12, 13]} True >>> edge_number == 100 True >>> engine.edge_gen(0) Traceback (most recent call last): ... TypeError: edge_gen() missing 2 required positional arguments: 'm' and 'direct' >>> ######################################### >>> ## ========= gen_using function ========= >>> ######################################### >>> #################### dimacs_maker #################### >>> random.seed(2) >>> engine.gen_using(dimacs_maker, 'testfile', {'vertices':10, 'edge_number':5, 'direct':0}) 5 >>> file=open('testfile.gr','r') >>> print(file.read()) c FILE :testfile.gr c No. of vertices :10 c No. of edges :5 c Max. weight :1 c Min. weight :1 p sp 10 5 a 1 2 1 a 1 10 1 a 3 6 1 a 3 7 1 a 6 10 1 >>> random.seed(4) >>> engine.gen_using(dimacs_maker, 'testfile2', {'vertices':10, 'edge_number':8, 'direct':1}) 8 >>> file=open('testfile2.gr','r') >>> print(file.read()) c FILE :testfile2.gr c No. of vertices :10 c No. of edges :8 c Max. weight :1 c Min. weight :1 p sp 10 8 a 1 10 1 a 2 5 1 a 3 6 1 a 4 10 1 a 6 5 1 a 8 3 1 a 9 8 1 a 10 2 1 >>> #################### json_maker #################### >>> random.seed(2) >>> engine.gen_using(json_maker, 'testfile', {'vertices':10, 'edge_number':5, 'direct':0}) 5 >>> file=open('testfile.json','r') >>> testfile_1=json.load(file) >>> testfile_1['graph']['nodes'][1] {'id': 2} >>> testfile_1['graph']['edges'][1]['source'] 1 >>> testfile_1['graph']['edges'][1]['target'] 10 >>> random.seed(4) >>> engine.gen_using(json_maker, 'testfile2', {'vertices':10, 'edge_number':8, 'direct':1}) 8 >>> file=open('testfile2.json','r') >>> testfile_2=json.load(file) >>> testfile_2['graph']['nodes'][1] {'id': 2} >>> testfile_2['graph']['edges'][1]['source'] 2 >>> testfile_2['graph']['edges'][1]['target'] 5 >>> #################### csv_maker #################### >>> random.seed(2) >>> engine.gen_using(csv_maker, 'testfile', {'vertices':10, 'edge_number':5, 'direct':0}) 5 >>> file=open('testfile.csv','r') >>> print(file.read()) 1,2,1 1,10,1 3,6,1 3,7,1 6,10,1 >>> random.seed(4) >>> engine.gen_using(csv_maker, 'testfile2', {'vertices':10, 'edge_number':8, 'direct':1}) 8 >>> file=open('testfile2.csv','r') >>> print(file.read()) 1,10,1 2,5,1 3,6,1 4,10,1 6,5,1 8,3,1 9,8,1 10,2,1 >>> #################### gdf_maker #################### >>> random.seed(2) >>> engine.gen_using(gdf_maker, 'testfile', {'vertices':10, 'edge_number':5, 'direct':0}) 5 >>> file=open('testfile.gdf','r') >>> print(file.read()) nodedef>name VARCHAR,label VARCHAR 1,Node1 2,Node2 3,Node3 4,Node4 5,Node5 6,Node6 7,Node7 8,Node8 9,Node9 10,Node10 edgedef>node1 VARCHAR,node2 VARCHAR,weight DOUBLE 1,2,1 1,10,1 3,6,1 3,7,1 6,10,1 >>> random.seed(4) >>> engine.gen_using(gdf_maker, 'testfile2', {'vertices':10, 'edge_number':8, 'direct':1}) 8 >>> file=open('testfile2.gdf','r') >>> print(file.read()) nodedef>name VARCHAR,label VARCHAR 1,Node1 2,Node2 3,Node3 4,Node4 5,Node5 6,Node6 7,Node7 8,Node8 9,Node9 10,Node10 edgedef>node1 VARCHAR,node2 VARCHAR,weight DOUBLE 1,10,1 2,5,1 3,6,1 4,10,1 6,5,1 8,3,1 9,8,1 10,2,1 >>> #################### gl_maker #################### >>> random.seed(2) >>> engine.gen_using(gl_maker, 'testfile', {'vertices':10, 'edge_number':5, 'direct':0}) 5 >>> file=open('testfile.gl','r') >>> print(file.read()) 1 2:1 10:1 3 6:1 7:1 6 10:1 >>> random.seed(4) >>> engine.gen_using(gl_maker, 'testfile2', {'vertices':10, 'edge_number':8, 'direct':1}) 8 >>> file=open('testfile2.gl','r') >>> print(file.read()) 1 10:1 2 5:1 3 6:1 4 10:1 6 5:1 8 3:1 9 8:1 10 2:1 >>> #################### mtx_maker #################### >>> from scipy.io import mmread >>> random.seed(2) >>> engine.gen_using(mtx_maker, 'testfile', {'vertices':10, 'edge_number':5, 'direct':0}) 5 >>> g = mmread("testfile.mtx") >>> print(g.data.tolist()) [1.0, 1.0, 1.0, 1.0, 1.0] >>> random.seed(4) >>> engine.gen_using(mtx_maker, 'testfile2', {'vertices':10, 'edge_number':8, 'direct':1}) 8 >>> g = mmread("testfile2.mtx") >>> print(g.data.tolist()) [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] >>> #################### tsv_maker #################### >>> random.seed(2) >>> engine.gen_using(tsv_maker, 'testfile', {'vertices':10, 'edge_number':5, 'direct':0}) 5 >>> file=open('testfile.tsv','r') >>> print(file.read()) 1 2 1 1 10 1 3 6 1 3 7 1 6 10 1 >>> random.seed(4) >>> engine.gen_using(tsv_maker, 'testfile2', {'vertices':10, 'edge_number':8, 'direct':1}) 8 >>> file=open('testfile2.tsv','r') >>> print(file.read()) 1 10 1 2 5 1 3 6 1 4 10 1 6 5 1 8 3 1 9 8 1 10 2 1 >>> #################### wel_maker #################### >>> random.seed(2) >>> engine.gen_using(wel_maker, 'testfile', {'vertices':10, 'edge_number':5, 'direct':0}) 5 >>> file=open('testfile.wel','r') >>> print(file.read()) 1 2 1 1 10 1 3 6 1 3 7 1 6 10 1 >>> random.seed(4) >>> engine.gen_using(wel_maker, 'testfile2', {'vertices':10, 'edge_number':8, 'direct':1}) 8 >>> file=open('testfile2.wel','r') >>> print(file.read()) 1 10 1 2 5 1 3 6 1 4 10 1 6 5 1 8 3 1 9 8 1 10 2 1 >>> #################### lp_maker #################### >>> random.seed(2) >>> engine.gen_using(lp_maker, 'testfile', {'vertices':10, 'edge_number':5, 'direct':0}) 5 >>> file=open('testfile.lp','r') >>> print(file.read()) node(1). node(2). node(3). node(4). node(5). node(6). node(7). node(8). node(9). node(10). edge(1,2,1). edge(1,10,1). edge(3,6,1). edge(3,7,1). edge(6,10,1). >>> random.seed(4) >>> engine.gen_using(lp_maker, 'testfile2', {'vertices':10, 'edge_number':8, 'direct':1}) 8 >>> file=open('testfile2.lp','r') >>> print(file.read()) node(1). node(2). node(3). node(4). node(5). node(6). node(7). node(8). node(9). node(10). edge(1,10,1). edge(2,5,1). edge(3,6,1). edge(4,10,1). edge(6,5,1). edge(8,3,1). edge(9,8,1). edge(10,2,1). >>> #################### tgf_maker #################### >>> random.seed(2) >>> engine.gen_using(tgf_maker, 'testfile', {'vertices':10, 'edge_number':5, 'direct':0}) 5 >>> file=open('testfile.tgf','r') >>> print(file.read()) 1 2 3 4 5 6 7 8 9 10 # 1 2 1 1 10 1 3 6 1 3 7 1 6 10 1 >>> random.seed(4) >>> engine.gen_using(tgf_maker, 'testfile2', {'vertices':10, 'edge_number':8, 'direct':1}) 8 >>> file=open('testfile2.tgf','r') >>> print(file.read()) 1 2 3 4 5 6 7 8 9 10 # 1 10 1 2 5 1 3 6 1 4 10 1 6 5 1 8 3 1 9 8 1 10 2 1 >>> #################### dl_maker #################### >>> random.seed(2) >>> engine.gen_using(dl_maker, 'testfile', {'vertices':10, 'edge_number':5, 'direct':0}) 5 >>> file=open('testfile.dl','r') >>> print(file.read()) dl format=edgelist1 n=10 data: 1 2 1 1 10 1 3 6 1 3 7 1 6 10 1 >>> random.seed(4) >>> engine.gen_using(dl_maker, 'testfile2', {'vertices':10, 'edge_number':8, 'direct':1}) 8 >>> file=open('testfile2.dl','r') >>> print(file.read()) dl format=edgelist1 n=10 data: 1 10 1 2 5 1 3 6 1 4 10 1 6 5 1 8 3 1 9 8 1 10 2 1 >>> #################### gml_maker #################### >>> from networkx.readwrite.gml import read_gml >>> random.seed(2) >>> engine.gen_using(gml_maker, 'testfile', {'vertices':10, 'edge_number':5, 'direct':0}) 5 >>> gml1 = read_gml("testfile.gml") >>> type(gml1) >>> random.seed(4) >>> engine.gen_using(gml_maker, 'testfile2', {'vertices':10, 'edge_number':8, 'direct':1}) 8 >>> gml2 = read_gml("testfile2.gml") >>> type(gml2) >>> #################### gexf_maker #################### >>> from networkx.readwrite.gexf import read_gexf >>> random.seed(2) >>> engine.gen_using(gexf_maker, 'testfile', {'vertices':10, 'edge_number':5, 'direct':0}) 5 >>> gexf1 = read_gexf("testfile.gexf") >>> type(gexf1) >>> random.seed(4) >>> engine.gen_using(gexf_maker, 'testfile2', {'vertices':10, 'edge_number':8, 'direct':1}) 8 >>> gexf2 = read_gexf("testfile2.gexf") >>> type(gexf2) >>> #################### dot_maker #################### >>> import pydot >>> random.seed(2) >>> engine.gen_using(dot_maker, 'testfile', {'vertices':10, 'edge_number':5, 'direct':0}) 5 >>> file=open('testfile.gv','r') >>> g1 = pydot.graph_from_dot_data(file.read()) >>> g1[0].get_type() 'graph' >>> len(g1[0].get_edge_list()) 5 >>> random.seed(4) >>> engine.gen_using(dot_maker, 'testfile2', {'vertices':10, 'edge_number':8, 'direct':1}) 8 >>> file=open('testfile2.gv','r') >>> g2 = pydot.graph_from_dot_data(file.read()) >>> g2[0].get_type() 'digraph' >>> len(g2[0].get_edge_list()) 8 >>> file.close() >>> os.remove('testfile.gr') >>> os.remove('testfile2.gr') >>> os.remove('testfile.json') >>> os.remove('testfile2.json') >>> os.remove('testfile.csv') >>> os.remove('testfile2.csv') >>> os.remove('testfile.gdf') >>> os.remove('testfile2.gdf') >>> os.remove('testfile.gl') >>> os.remove('testfile2.gl') >>> os.remove('testfile.mtx') >>> os.remove('testfile2.mtx') >>> os.remove('testfile.tsv') >>> os.remove('testfile2.tsv') >>> os.remove('testfile.wel') >>> os.remove('testfile2.wel') >>> os.remove('testfile.lp') >>> os.remove('testfile2.lp') >>> os.remove('testfile.tgf') >>> os.remove('testfile2.tgf') >>> os.remove('testfile.dl') >>> os.remove('testfile2.dl') >>> os.remove('testfile.gml') >>> os.remove('testfile2.gml') >>> os.remove('testfile.gexf') >>> os.remove('testfile2.gexf') >>> os.remove('testfile.gv') >>> os.remove('testfile2.gv') >>> os.remove('logfile.log') """ pyrgg-1.6/test/engines/pyrgg_test.py000066400000000000000000001044341471451651000176720ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ >>> from pyrgg.functions import * >>> from pyrgg.graph_gen import * >>> import pyrgg.params >>> import random >>> import os >>> import json >>> import pyrgg.engines.pyrgg as engine >>> os.environ["PYRGG_TEST_MODE"] = "1" >>> ###################################### >>> ## ========= logger function ========= >>> ###################################### >>> with open('logfile.log','a') as file: ... engine.logger(file,'test','2min',{'vertices':100,'edge_number':50,'max_edge':1000,'min_edge':10,'direct':1,'sign':0,'multigraph':0,'self_loop':1,'max_weight':20,'min_weight':1,'engine':1,'output_format':1}) >>> file = open('logfile.log','r') >>> print("\\n".join(file.read().splitlines()[1:-1])) Filename : test Vertices : 100 Total Edges : 50 Max Edge : 1000 Min Edge : 10 Directed : True Signed : False Multigraph : False Self Loop : True Weighted : True Max Weight : 20 Min Weight : 1 Engine : 1 (pyrgg) Elapsed Time : 2min >>> class StrError: ... def __init__(self): ... pass ... def __str__(self): ... raise ValueError >>> str_error_object = StrError() >>> with open('logfile.log','a') as file: ... engine.logger(file,'test','2min',{'vertices':str_error_object,'edge_number':50,'max_edge':1000,'min_edge':10,'direct':1,'sign':0,'multigraph':0,'self_loop':1,'max_weight':20,'min_weight':1,'engine':1,'output_format':1}) [Error] Logger failed! >>> ########################################## >>> ## ========= branch_gen function ========= >>> ########################################## >>> used_vertices = {k:[] for k in range(1,6)} >>> degree_dict = {1:2,2:3,3:3,4:3,5:3} >>> degree_dict_sort = {0:{},1:{},2:{1:1},3:{2:2,3:3,4:4,5:5},4:{},5:{}} >>> all_vertices = list(range(1, 6)) >>> engine.branch_gen(1,3,3,300,3000,0,True,False,False,False,used_vertices,degree_dict,degree_dict_sort) [[], []] >>> used_vertices = {k:[] for k in range(1,41)} >>> degree_dict = {k:0 for k in range(1,41)} >>> degree_dict_sort = {k:{} for k in range(41)} >>> degree_dict_sort[0] = {i:i for i in range(1,41)} >>> all_vertices = list(range(1, 41)) >>> random.seed(2) >>> engine.branch_gen(1,10,10,1,20,0,True,True,True,False,used_vertices,degree_dict,degree_dict_sort) [[4, 25, 18, 3, 30, 34, 2, 26, 14, 11], [3, 10, 20, 14, -18, -2, -15, -14, 8, 6]] >>> random.seed(20) >>> engine.branch_gen(1,10,4,1,20,0,False,True,True,False,used_vertices,degree_dict,degree_dict_sort) [[], []] >>> used_vertices = {k:[] for k in range(1,41)} >>> degree_dict = {k:0 for k in range(1,41)} >>> degree_dict_sort = {k:{} for k in range(41)} >>> degree_dict_sort[0] = {i:i for i in range(1,41)} >>> engine.branch_gen(1,10,4,1,20,0,False,True,True,False,used_vertices,degree_dict,degree_dict_sort) [[10, 7, 39, 2, 30, 9, 25, 35, 18], [9, 11, 6, 14, 3, 5, 16, 14, 7]] >>> engine.branch_gen(40,1,20,1) Traceback (most recent call last): ... TypeError: branch_gen() missing 9 required positional arguments: 'max_weight', 'precision', 'sign', 'direct', 'self_loop', 'multigraph', 'used_vertices', 'degree_dict', and 'degree_sort_dict' >>> ########################################## >>> ## ========= edge_gen function ========= >>> ########################################## >>> random.seed(2) >>> engine.edge_gen(20,0,400,2,10,True,True,True,False) [{1: [3, 7], 2: [4, 17, 20, 9, 11], 3: [14, 8, 5, 12, 16, 19, 15], 4: [15, 17, 12, 8, 14, 13], 5: [16, 9, 7, 20, 19, 18, 13, 5], 6: [6, 10], 7: [18, 10, 11], 8: [], 9: [], 10: [12, 18, 8, 1, 14], 11: [9, 11], 12: [], 13: [], 14: [19, 16, 17, 20, 15], 15: [6, 1, 19], 16: [12, 13, 8, 9, 17], 17: [], 18: [9, 12, 17, 6, 20, 19, 1], 19: [13], 20: []}, {1: [184, -128], 2: [220, -278, -257, 14, -163], 3: [286, 118, 166, 261, -263, 228, -303], 4: [-82, -335, 250, -256, -338, -179], 5: [-337, -358, -395, -155, -159, 250, -350, -371], 6: [30, -302], 7: [386, -125, 216], 8: [], 9: [], 10: [127, 42, 12, 191, 80], 11: [-301, 77], 12: [], 13: [], 14: [146, -15, -282, 135, 242], 15: [-52, -65, -249], 16: [-132, -334, 343, -17, 87], 17: [], 18: [126, -37, 302, -131, -142, 77, -209], 19: [123], 20: []}, 61] >>> random.seed(11) >>> engine.edge_gen(20,0,100,2,10,False,True,True,False) [{1: [18, 15, 19, 7, 20, 11, 2, 6, 3], 2: [17], 3: [8, 4, 5, 9, 12, 10, 14, 16], 4: [20, 13, 4, 6], 5: [12, 7, 11, 10, 14], 6: [9], 7: [19], 8: [8, 18, 11, 2, 16, 17, 10], 9: [15, 12, 18], 10: [20, 14, 13, 15, 17, 16], 11: [19, 7, 20], 12: [13], 13: [2, 16, 13], 14: [18, 19, 6, 14, 17, 15], 15: [6, 7, 16], 16: [17, 20, 12, 18], 17: [19], 18: [7, 6, 9, 12, 20], 19: [19, 11, 4], 20: []}, {1: [99, 57, 75, 23, 80, 23, 57, 18, 68], 2: [50], 3: [79, 67, 7, 24, 76, 99, 41, 75], 4: [29, 63, 84, 58], 5: [70, 90, 40, 65, 3], 6: [51], 7: [37], 8: [2, 0, 26, 60, 90, 53, 72], 9: [43, 39, 1], 10: [15, 31, 1, 59, 22, 57], 11: [98, 53, 49], 12: [53], 13: [34, 2, 23], 14: [82, 12, 18, 56, 1, 37], 15: [9, 26, 1], 16: [47, 58, 75, 73], 17: [23], 18: [39, 78, 92, 20, 49], 19: [10, 6, 13], 20: []}, 74] >>> engine.edge_gen(0,400,2,10,1) Traceback (most recent call last): ... TypeError: edge_gen() missing 4 required positional arguments: 'sign', 'direct', 'self_loop', and 'multigraph' >>> ######################################### >>> ## ========= gen_using function ========= >>> ######################################### >>> #################### dimacs_maker #################### >>> random.seed(2) >>> engine.gen_using(dimacs_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.gr','r') >>> print(file.read()) c FILE :testfile.gr c No. of vertices :10 c No. of edges :7 c Max. weight :148 c Min. weight :7 p sp 10 7 a 4 3 -64 a 5 6 148 a 5 9 110 a 6 10 -139 a 7 7 7 a 8 2 -97 a 9 1 60 >>> random.seed(4) >>> engine.gen_using(dimacs_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> file=open('testfile2.gr','r') >>> print(file.read()) c FILE :testfile2.gr c No. of vertices :30 c No. of edges :35 c Max. weight :50 c Min. weight :2 p sp 30 35 a 1 10 46 a 2 18 5 a 2 4 25 a 2 22 -48 a 4 23 -17 a 5 7 -13 a 7 15 10 a 7 17 -40 a 8 8 -42 a 8 25 11 a 9 29 -5 a 10 3 -36 a 10 27 -48 a 11 13 -27 a 11 26 -27 a 11 21 14 a 11 16 -2 a 14 20 -44 a 14 14 43 a 14 12 26 a 15 28 -11 a 16 30 -40 a 16 24 20 a 19 19 7 a 20 12 -29 a 20 1 22 a 22 24 20 a 22 23 -9 a 23 18 18 a 23 27 28 a 24 6 -24 a 25 17 23 a 27 6 -50 a 28 21 28 a 28 13 -13 >>> random.seed(20) >>> engine.gen_using(dimacs_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':1, 'self_loop':1, 'multigraph':0}) 137 >>> file=open('testfile3.gr','r') >>> print(file.read()) c FILE :testfile3.gr c No. of vertices :100 c No. of edges :137 c Max. weight :30 c Min. weight :10 p sp 100 137 a 1 34 30 a 3 76 15 a 3 5 23 a 4 13 13 a 4 21 20 a 4 67 28 a 5 60 16 a 5 32 20 a 5 92 20 a 6 64 12 a 6 94 26 a 7 62 12 a 7 36 28 a 7 42 11 a 8 20 12 a 9 47 19 a 10 49 15 a 10 27 10 a 11 48 17 a 11 51 11 a 13 58 14 a 13 70 29 a 14 37 30 a 14 61 27 a 14 87 15 a 15 84 13 a 16 83 28 a 17 45 17 a 17 24 29 a 17 18 26 a 18 59 15 a 19 98 12 a 21 2 30 a 21 99 20 a 22 69 26 a 22 96 11 a 22 88 15 a 24 79 20 a 24 12 12 a 24 82 13 a 26 50 30 a 26 30 19 a 29 52 26 a 31 25 26 a 32 68 14 a 33 65 13 a 33 78 13 a 33 55 17 a 34 63 13 a 35 44 27 a 35 57 14 a 37 74 10 a 37 41 16 a 37 100 30 a 38 72 13 a 38 56 16 a 39 91 19 a 39 43 13 a 41 28 22 a 41 81 19 a 42 90 13 a 42 46 28 a 42 97 16 a 45 86 10 a 45 53 18 a 46 85 13 a 46 23 11 a 47 71 29 a 48 95 12 a 48 77 19 a 48 93 11 a 49 75 22 a 50 73 18 a 50 40 24 a 50 54 28 a 51 80 17 a 51 66 19 a 51 89 20 a 52 58 29 a 52 16 21 a 52 43 12 a 53 8 13 a 53 98 17 a 54 55 10 a 56 62 26 a 56 27 10 a 57 70 26 a 58 44 22 a 59 90 27 a 59 91 19 a 59 78 29 a 60 87 12 a 60 92 25 a 61 69 14 a 61 79 17 a 62 25 21 a 63 97 27 a 63 29 30 a 65 9 26 a 65 64 21 a 66 67 27 a 66 95 19 a 66 93 30 a 68 30 18 a 70 83 12 a 70 99 15 a 71 31 17 a 71 89 20 a 73 36 18 a 75 72 12 a 76 2 26 a 76 12 25 a 76 86 22 a 78 23 19 a 78 100 27 a 79 40 24 a 80 84 26 a 80 80 14 a 81 20 16 a 82 15 16 a 82 88 22 a 83 19 19 a 84 85 13 a 84 28 16 a 85 77 16 a 85 94 23 a 86 1 21 a 87 74 15 a 87 96 19 a 90 93 22 a 92 49 14 a 95 98 26 a 95 55 11 a 97 38 28 a 99 19 29 a 99 89 24 a 100 40 11 >>> random.seed(20) >>> engine.gen_using(dimacs_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':0, 'self_loop':1, 'multigraph':1}) 131 >>> file=open('testfile3.gr','r') >>> print(file.read()) c FILE :testfile3.gr c No. of vertices :100 c No. of edges :131 c Max. weight :30 c Min. weight :10 p sp 100 131 a 1 34 30 a 3 76 15 a 3 5 23 a 4 13 13 a 4 20 20 a 4 65 28 a 5 60 16 a 5 32 20 a 5 90 20 a 6 64 12 a 6 93 26 a 7 62 12 a 7 36 28 a 7 41 11 a 8 21 12 a 9 47 19 a 10 49 15 a 10 27 10 a 11 48 17 a 11 100 17 a 14 61 18 a 14 33 30 a 14 68 29 a 14 84 14 a 15 87 15 a 15 42 22 a 17 82 28 a 18 46 17 a 18 24 29 a 18 18 26 a 19 57 15 a 20 97 12 a 22 2 30 a 22 95 20 a 23 69 26 a 23 94 11 a 23 85 15 a 25 78 20 a 25 12 12 a 25 79 13 a 27 52 30 a 27 31 19 a 30 51 26 a 32 26 26 a 33 66 14 a 34 59 13 a 34 74 13 a 34 54 17 a 35 56 13 a 36 44 27 a 36 83 14 a 38 70 10 a 38 39 16 a 38 91 30 a 39 67 13 a 39 99 19 a 40 63 29 a 41 43 13 a 41 16 18 a 43 75 19 a 43 77 24 a 43 45 12 a 44 58 10 a 44 96 12 a 44 98 29 a 45 86 10 a 45 37 18 a 46 81 13 a 46 28 11 a 47 55 29 a 48 92 12 a 48 92 21 a 48 72 24 a 50 88 27 a 51 73 18 a 51 29 24 a 51 29 28 a 52 80 17 a 52 71 19 a 52 89 20 a 53 58 29 a 53 16 21 a 53 40 12 a 54 8 13 a 54 98 17 a 55 55 10 a 56 82 21 a 57 73 14 a 57 2 17 a 58 76 24 a 59 67 22 a 60 78 29 a 60 88 19 a 62 61 21 a 62 99 14 a 62 75 17 a 63 26 21 a 63 66 30 a 63 87 14 a 65 12 26 a 65 37 21 a 66 50 27 a 67 74 25 a 67 42 14 a 68 72 10 a 70 84 18 a 71 91 20 a 74 30 13 a 76 13 25 a 78 9 25 a 78 81 22 a 80 96 19 a 81 90 23 a 81 28 24 a 82 93 25 a 83 31 16 a 85 21 16 a 85 94 22 a 86 17 19 a 86 86 27 a 89 97 19 a 91 49 23 a 91 77 13 a 92 100 17 a 93 24 27 a 93 69 19 a 96 64 14 a 97 95 14 a 98 1 15 a 98 1 13 a 99 35 28 >>> random.seed(4) >>> engine.gen_using(dimacs_maker, 'testfile4', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':0, 'multigraph':0}) 37 >>> file=open('testfile4.gr','r') >>> print(file.read()) c FILE :testfile4.gr c No. of vertices :30 c No. of edges :37 c Max. weight :50 c Min. weight :0 p sp 30 37 a 1 11 46 a 2 19 5 a 2 5 25 a 2 23 -48 a 4 24 -17 a 5 8 -13 a 7 16 10 a 7 18 -40 a 8 9 -42 a 8 26 11 a 9 30 -5 a 10 29 0 a 10 14 -48 a 10 22 26 a 10 20 -27 a 11 12 19 a 11 17 5 a 11 3 -40 a 12 25 -44 a 12 15 43 a 13 6 -12 a 14 27 22 a 14 28 -40 a 14 21 -6 a 16 19 7 a 17 15 -29 a 17 1 22 a 19 25 20 a 20 21 49 a 20 28 -39 a 21 4 -39 a 21 18 -18 a 22 24 -38 a 23 13 23 a 25 6 -50 a 26 29 28 a 26 3 -13 >>> #################### json_maker #################### >>> random.seed(2) >>> engine.gen_using(json_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':True, 'direct':True, 'self_loop':True, 'multigraph':False}) 7 >>> file=open('testfile.json','r') >>> testfile_1=json.load(file) >>> testfile_1['graph']['nodes'][1] {'id': 2} >>> testfile_1['graph']['edges'][1]['source'] 5 >>> testfile_1['graph']['edges'][1]['target'] 6 >>> testfile_1['graph']['edges'][1]['weight'] 148 >>> testfile_1['properties']['directed'] True >>> testfile_1['properties']['multigraph'] False >>> testfile_1['properties']['weighted'] True >>> random.seed(4) >>> engine.gen_using(json_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':True, 'direct':True, 'self_loop':True, 'multigraph':False}) 35 >>> file=open('testfile2.json','r') >>> testfile_2=json.load(file) >>> testfile_2['graph']['nodes'][1] {'id': 2} >>> testfile_2['graph']['edges'][1]['source'] 2 >>> testfile_2['graph']['edges'][1]['target'] 18 >>> testfile_2['graph']['edges'][1]['weight'] 5 >>> testfile_2['properties']['multigraph'] False >>> testfile_2['properties']['directed'] True >>> testfile_2['properties']['weighted'] True >>> random.seed(20) >>> engine.gen_using(json_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':False, 'direct':True, 'self_loop':True, 'multigraph':False}) 137 >>> file=open('testfile3.json','r') >>> testfile_3=json.load(file) >>> testfile_3['graph']['nodes'][1] {'id': 2} >>> testfile_3['graph']['edges'][1]['source'] 3 >>> testfile_3['graph']['edges'][1]['target'] 76 >>> testfile_3['graph']['edges'][1]['weight'] 15 >>> testfile_3['properties']['directed'] True >>> testfile_3['properties']['multigraph'] False >>> testfile_3['properties']['weighted'] True >>> random.seed(20) >>> engine.gen_using(json_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':False, 'direct':False, 'self_loop':True, 'multigraph':True}) 131 >>> file=open('testfile3.json','r') >>> testfile_3=json.load(file) >>> testfile_3['properties']['directed'] False >>> testfile_3['properties']['multigraph'] True >>> testfile_3['properties']['weighted'] True >>> random.seed(21) >>> engine.gen_using(json_maker, 'testfile3', {'min_weight':1, 'max_weight':1, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':False, 'direct':False, 'self_loop':True, 'multigraph':True}) 136 >>> file=open('testfile3.json','r') >>> testfile_3=json.load(file) >>> testfile_3['properties']['weighted'] False >>> random.seed(21) >>> engine.gen_using(json_maker, 'testfile3', {'min_weight':1, 'max_weight':1, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':True, 'direct':False, 'self_loop':True, 'multigraph':True}) 158 >>> file=open('testfile3.json','r') >>> testfile_3=json.load(file) >>> testfile_3['properties']['weighted'] True >>> #################### csv_maker #################### >>> random.seed(2) >>> engine.gen_using(csv_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':True, 'direct':True, 'self_loop':True, 'multigraph':False}) 7 >>> file=open('testfile.csv','r') >>> print(file.read()) 4,3,-64 5,6,148 5,9,110 6,10,-139 7,7,7 8,2,-97 9,1,60 >>> random.seed(4) >>> engine.gen_using(csv_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':True, 'direct':True, 'self_loop':True, 'multigraph':False}) 35 >>> file=open('testfile2.csv','r') >>> print(file.read()) 1,10,46 2,18,5 2,4,25 2,22,-48 4,23,-17 5,7,-13 7,15,10 7,17,-40 8,8,-42 8,25,11 9,29,-5 10,3,-36 10,27,-48 11,13,-27 11,26,-27 11,21,14 11,16,-2 14,20,-44 14,14,43 14,12,26 15,28,-11 16,30,-40 16,24,20 19,19,7 20,12,-29 20,1,22 22,24,20 22,23,-9 23,18,18 23,27,28 24,6,-24 25,17,23 27,6,-50 28,21,28 28,13,-13 >>> random.seed(20) >>> engine.gen_using(csv_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':False, 'direct':True, 'self_loop':True, 'multigraph':False}) 137 >>> file=open('testfile3.csv','r') >>> print(file.read()) 1,34,30 3,76,15 3,5,23 4,13,13 4,21,20 4,67,28 5,60,16 5,32,20 5,92,20 6,64,12 6,94,26 7,62,12 7,36,28 7,42,11 8,20,12 9,47,19 10,49,15 10,27,10 11,48,17 11,51,11 13,58,14 13,70,29 14,37,30 14,61,27 14,87,15 15,84,13 16,83,28 17,45,17 17,24,29 17,18,26 18,59,15 19,98,12 21,2,30 21,99,20 22,69,26 22,96,11 22,88,15 24,79,20 24,12,12 24,82,13 26,50,30 26,30,19 29,52,26 31,25,26 32,68,14 33,65,13 33,78,13 33,55,17 34,63,13 35,44,27 35,57,14 37,74,10 37,41,16 37,100,30 38,72,13 38,56,16 39,91,19 39,43,13 41,28,22 41,81,19 42,90,13 42,46,28 42,97,16 45,86,10 45,53,18 46,85,13 46,23,11 47,71,29 48,95,12 48,77,19 48,93,11 49,75,22 50,73,18 50,40,24 50,54,28 51,80,17 51,66,19 51,89,20 52,58,29 52,16,21 52,43,12 53,8,13 53,98,17 54,55,10 56,62,26 56,27,10 57,70,26 58,44,22 59,90,27 59,91,19 59,78,29 60,87,12 60,92,25 61,69,14 61,79,17 62,25,21 63,97,27 63,29,30 65,9,26 65,64,21 66,67,27 66,95,19 66,93,30 68,30,18 70,83,12 70,99,15 71,31,17 71,89,20 73,36,18 75,72,12 76,2,26 76,12,25 76,86,22 78,23,19 78,100,27 79,40,24 80,84,26 80,80,14 81,20,16 82,15,16 82,88,22 83,19,19 84,85,13 84,28,16 85,77,16 85,94,23 86,1,21 87,74,15 87,96,19 90,93,22 92,49,14 95,98,26 95,55,11 97,38,28 99,19,29 99,89,24 100,40,11 >>> random.seed(20) >>> engine.gen_using(csv_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':0, 'self_loop':1, 'multigraph':1}) 131 >>> random.seed(2) >>> engine.gen_using(csv_maker, 'testfile4', {'min_weight':0.0, 'max_weight':200.22, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 5 >>> file = open("testfile4.csv") >>> print(file.read()) 4,3,-50.37 6,1,200.16 6,9,-160.91 7,7,-100.52 10,10,-181.75 >>> random.seed(2) >>> engine.gen_using(csv_maker, 'testfile4', {'min_weight':0.0, 'max_weight':200.222, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 5 >>> file = open("testfile4.csv") >>> print(file.read()) 4,3,-50.373 6,1,200.166 6,9,-160.912 7,7,-100.525 10,10,-181.752 >>> #################### gdf_maker #################### >>> random.seed(2) >>> engine.gen_using(gdf_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.gdf','r') >>> print(file.read()) nodedef>name VARCHAR,label VARCHAR 1,Node1 2,Node2 3,Node3 4,Node4 5,Node5 6,Node6 7,Node7 8,Node8 9,Node9 10,Node10 edgedef>node1 VARCHAR,node2 VARCHAR,weight DOUBLE 4,3,-64 5,6,148 5,9,110 6,10,-139 7,7,7 8,2,-97 9,1,60 >>> random.seed(4) >>> engine.gen_using(gdf_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> random.seed(20) >>> engine.gen_using(gdf_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':1, 'self_loop':1, 'multigraph':0}) 137 >>> random.seed(20) >>> engine.gen_using(gdf_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':0, 'self_loop':1, 'multigraph':1}) 131 >>> #################### gl_maker #################### >>> random.seed(2) >>> engine.gen_using(gl_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':1, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.gl','r') >>> print(file.read()) 1 2:92 3 7:155 9:110 4 10:-139 8:-9 5 6:-81 6 9:143 >>> random.seed(4) >>> engine.gen_using(gl_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':2, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 44 >>> file=open('testfile2.gl','r') >>> print(file.read()) 1 10:46 14:-9 2 4:25 22:-48 3 12:-17 9:16 4 5:-17 5 15:-18 6 23:38 21:-32 18:15 30:-5 7 29:0 16:-48 25:26 20:-27 8 13:19 24:5 8:-40 9 27:-44 19:43 10 11:-12 11 28:-17 17:-27 26:20 12 13:17 13 21:21 19:-29 14 14:-44 15 20:-1 16 23:-9 30:-39 17 18:-39 25:-18 22:-24 18 26:-36 24:-2 19 27:-50 20 29:28 22 28:3 25 10:38 2:-37 27 1:33 28 23:30 >>> random.seed(20) >>> engine.gen_using(gl_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':3, 'max_edge':4, 'sign':0, 'direct':1, 'self_loop':1, 'multigraph':0}) 178 >>> file=open('testfile3.gl','r') >>> print(file.read()) 1 34:30 13:20 76:15 2 56:23 11:13 20:20 3 83:24 60:16 31:20 92:20 4 65:12 95:26 74:22 16:16 5 9:16 22:12 37:18 6 54:18 33:29 27:10 51:17 7 48:11 8:30 62:18 8 69:29 88:14 9 86:15 41:22 32:14 10 75:17 21:29 15:26 30:23 11 35:12 25:19 12 94:30 96:20 82:22 13 70:11 85:15 28:24 14 14:12 79:13 26:20 15 91:12 97:19 98:11 16 44:17 77:26 17 42:10 81:14 57:14 18 55:13 68:13 49:17 47:17 19 66:14 80:14 71:12 20 61:10 39:16 100:30 21 58:13 50:16 59:29 22 38:13 23:18 29:22 23 64:22 87:13 40:28 24 53:10 43:27 78:20 90:10 25 63:19 93:13 26 36:16 67:29 27 46:28 99:21 72:24 28 52:29 89:27 29 73:18 45:24 84:28 30 70:17 55:19 89:20 31 58:29 37:21 49:12 32 33:13 67:17 73:14 33 91:10 43:21 34 75:14 35:17 68:26 35 54:22 66:27 36 80:29 92:19 88:12 37 60:21 47:24 38 83:14 71:20 39 99:27 48:18 40 94:16 78:22 41 98:26 74:25 42 56:14 76:10 43 97:15 44 59:17 93:20 46:14 45 63:13 53:19 51:25 46 87:11 47 86:22 95:13 48 77:26 49 64:24 96:26 50 90:14 57:16 62:19 51 69:24 52 84:12 79:30 61:16 53 100:19 54 85:19 55 82:13 72:26 56 65:27 57 75:14 80:22 58 81:26 59 93:12 60 95:26 61 68:18 62 67:27 77:29 63 82:23 100:11 64 98:15 65 86:29 66 73:17 79:17 69 89:20 81:27 70 97:11 71 74:26 84:10 72 99:21 85:19 73 78:21 76 87:17 78 91:11 81 94:27 82 83:19 83 92:20 85 88:14 88 90:16 91 96:13 94 68:30 95 89:18 96 87:29 98 7:10 100 28:14 >>> #################### mtx_maker #################### >>> from scipy.io import mmread >>> random.seed(2) >>> engine.gen_using(mtx_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> g = mmread("testfile.mtx") >>> print(g.data.tolist()) [-64.0, 148.0, 110.0, -139.0, 7.0, -97.0, 60.0] >>> random.seed(4) >>> engine.gen_using(mtx_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> g = mmread("testfile2.mtx") >>> print(int(sum(g.data.tolist()))) -179 >>> random.seed(20) >>> engine.gen_using(mtx_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':1, 'self_loop':1, 'multigraph':0}) 137 >>> g = mmread("testfile3.mtx") >>> print(int(sum(g.data.tolist()))) 2644 >>> random.seed(20) >>> engine.gen_using(mtx_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':0, 'self_loop':1, 'multigraph':1}) 131 >>> #################### tsv_maker #################### >>> random.seed(2) >>> engine.gen_using(tsv_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.tsv','r') >>> print(file.read()) 4 3 -64 5 6 148 5 9 110 6 10 -139 7 7 7 8 2 -97 9 1 60 >>> random.seed(4) >>> engine.gen_using(tsv_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> random.seed(20) >>> engine.gen_using(tsv_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':1, 'self_loop':1, 'multigraph':0}) 137 >>> random.seed(20) >>> engine.gen_using(tsv_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':0, 'self_loop':1, 'multigraph':1}) 131 >>> #################### wel_maker #################### >>> random.seed(2) >>> engine.gen_using(wel_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.wel','r') >>> print(file.read()) 4 3 -64 5 6 148 5 9 110 6 10 -139 7 7 7 8 2 -97 9 1 60 >>> random.seed(4) >>> engine.gen_using(wel_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> file=open('testfile2.wel','r') >>> print(file.read()) 1 10 46 2 18 5 2 4 25 2 22 -48 4 23 -17 5 7 -13 7 15 10 7 17 -40 8 8 -42 8 25 11 9 29 -5 10 3 -36 10 27 -48 11 13 -27 11 26 -27 11 21 14 11 16 -2 14 20 -44 14 14 43 14 12 26 15 28 -11 16 30 -40 16 24 20 19 19 7 20 12 -29 20 1 22 22 24 20 22 23 -9 23 18 18 23 27 28 24 6 -24 25 17 23 27 6 -50 28 21 28 28 13 -13 >>> random.seed(20) >>> engine.gen_using(wel_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':1, 'self_loop':1, 'multigraph':0}) 137 >>> file=open('testfile3.wel','r') >>> print(file.read()) 1 34 30 3 76 15 3 5 23 4 13 13 4 21 20 4 67 28 5 60 16 5 32 20 5 92 20 6 64 12 6 94 26 7 62 12 7 36 28 7 42 11 8 20 12 9 47 19 10 49 15 10 27 10 11 48 17 11 51 11 13 58 14 13 70 29 14 37 30 14 61 27 14 87 15 15 84 13 16 83 28 17 45 17 17 24 29 17 18 26 18 59 15 19 98 12 21 2 30 21 99 20 22 69 26 22 96 11 22 88 15 24 79 20 24 12 12 24 82 13 26 50 30 26 30 19 29 52 26 31 25 26 32 68 14 33 65 13 33 78 13 33 55 17 34 63 13 35 44 27 35 57 14 37 74 10 37 41 16 37 100 30 38 72 13 38 56 16 39 91 19 39 43 13 41 28 22 41 81 19 42 90 13 42 46 28 42 97 16 45 86 10 45 53 18 46 85 13 46 23 11 47 71 29 48 95 12 48 77 19 48 93 11 49 75 22 50 73 18 50 40 24 50 54 28 51 80 17 51 66 19 51 89 20 52 58 29 52 16 21 52 43 12 53 8 13 53 98 17 54 55 10 56 62 26 56 27 10 57 70 26 58 44 22 59 90 27 59 91 19 59 78 29 60 87 12 60 92 25 61 69 14 61 79 17 62 25 21 63 97 27 63 29 30 65 9 26 65 64 21 66 67 27 66 95 19 66 93 30 68 30 18 70 83 12 70 99 15 71 31 17 71 89 20 73 36 18 75 72 12 76 2 26 76 12 25 76 86 22 78 23 19 78 100 27 79 40 24 80 84 26 80 80 14 81 20 16 82 15 16 82 88 22 83 19 19 84 85 13 84 28 16 85 77 16 85 94 23 86 1 21 87 74 15 87 96 19 90 93 22 92 49 14 95 98 26 95 55 11 97 38 28 99 19 29 99 89 24 100 40 11 >>> random.seed(20) >>> engine.gen_using(wel_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':0, 'self_loop':1, 'multigraph':1}) 131 >>> #################### lp_maker #################### >>> random.seed(2) >>> engine.gen_using(lp_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.lp','r') >>> print(file.read()) node(1). node(2). node(3). node(4). node(5). node(6). node(7). node(8). node(9). node(10). edge(4,3,-64). edge(5,6,148). edge(5,9,110). edge(6,10,-139). edge(7,7,7). edge(8,2,-97). edge(9,1,60). >>> random.seed(4) >>> engine.gen_using(lp_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> file=open('testfile2.lp','r') >>> print(file.read()) node(1). node(2). node(3). node(4). node(5). node(6). node(7). node(8). node(9). node(10). node(11). node(12). node(13). node(14). node(15). node(16). node(17). node(18). node(19). node(20). node(21). node(22). node(23). node(24). node(25). node(26). node(27). node(28). node(29). node(30). edge(1,10,46). edge(2,18,5). edge(2,4,25). edge(2,22,-48). edge(4,23,-17). edge(5,7,-13). edge(7,15,10). edge(7,17,-40). edge(8,8,-42). edge(8,25,11). edge(9,29,-5). edge(10,3,-36). edge(10,27,-48). edge(11,13,-27). edge(11,26,-27). edge(11,21,14). edge(11,16,-2). edge(14,20,-44). edge(14,14,43). edge(14,12,26). edge(15,28,-11). edge(16,30,-40). edge(16,24,20). edge(19,19,7). edge(20,12,-29). edge(20,1,22). edge(22,24,20). edge(22,23,-9). edge(23,18,18). edge(23,27,28). edge(24,6,-24). edge(25,17,23). edge(27,6,-50). edge(28,21,28). edge(28,13,-13). >>> #################### tgf_maker #################### >>> random.seed(2) >>> engine.gen_using(tgf_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.tgf','r') >>> print(file.read()) 1 2 3 4 5 6 7 8 9 10 # 4 3 -64 5 6 148 5 9 110 6 10 -139 7 7 7 8 2 -97 9 1 60 >>> random.seed(4) >>> engine.gen_using(tgf_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> file=open('testfile2.tgf','r') >>> print(file.read()) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 # 1 10 46 2 18 5 2 4 25 2 22 -48 4 23 -17 5 7 -13 7 15 10 7 17 -40 8 8 -42 8 25 11 9 29 -5 10 3 -36 10 27 -48 11 13 -27 11 26 -27 11 21 14 11 16 -2 14 20 -44 14 14 43 14 12 26 15 28 -11 16 30 -40 16 24 20 19 19 7 20 12 -29 20 1 22 22 24 20 22 23 -9 23 18 18 23 27 28 24 6 -24 25 17 23 27 6 -50 28 21 28 28 13 -13 >>> #################### dl_maker #################### >>> random.seed(2) >>> engine.gen_using(dl_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.dl','r') >>> print(file.read()) dl format=edgelist1 n=10 data: 4 3 -64 5 6 148 5 9 110 6 10 -139 7 7 7 8 2 -97 9 1 60 >>> random.seed(4) >>> engine.gen_using(dl_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> file=open('testfile2.dl','r') >>> print(file.read()) dl format=edgelist1 n=30 data: 1 10 46 2 18 5 2 4 25 2 22 -48 4 23 -17 5 7 -13 7 15 10 7 17 -40 8 8 -42 8 25 11 9 29 -5 10 3 -36 10 27 -48 11 13 -27 11 26 -27 11 21 14 11 16 -2 14 20 -44 14 14 43 14 12 26 15 28 -11 16 30 -40 16 24 20 19 19 7 20 12 -29 20 1 22 22 24 20 22 23 -9 23 18 18 23 27 28 24 6 -24 25 17 23 27 6 -50 28 21 28 28 13 -13 >>> #################### gml_maker #################### >>> from networkx.readwrite.gml import read_gml >>> random.seed(2) >>> engine.gen_using(gml_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> gml1 = read_gml("testfile.gml") >>> type(gml1) >>> random.seed(4) >>> engine.gen_using(gml_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':1}) 38 >>> gml2 = read_gml("testfile2.gml") >>> type(gml2) >>> random.seed(20) >>> engine.gen_using(gml_maker, 'testfile3', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':0, 'self_loop':1, 'multigraph':1}) 35 >>> gml3 = read_gml("testfile3.gml") >>> type(gml3) >>> random.seed(120) >>> engine.gen_using(gml_maker, 'testfile4', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':0, 'self_loop':1, 'multigraph':0}) 35 >>> gml4 = read_gml("testfile4.gml") >>> type(gml4) >>> #################### gexf_maker #################### >>> from networkx.readwrite.gexf import read_gexf >>> random.seed(2) >>> engine.gen_using(gexf_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> gexf1 = read_gexf("testfile.gexf") >>> type(gexf1) >>> random.seed(8) >>> engine.gen_using(gexf_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':1}) 35 >>> gexf2 = read_gexf("testfile2.gexf") >>> type(gexf2) >>> random.seed(20) >>> engine.gen_using(gexf_maker, 'testfile3', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':0, 'self_loop':1, 'multigraph':1}) 35 >>> gexf3 = read_gexf("testfile3.gexf") >>> type(gexf3) >>> random.seed(120) >>> engine.gen_using(gexf_maker, 'testfile4', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':0, 'self_loop':1, 'multigraph':0}) 35 >>> gexf4 = read_gexf("testfile4.gexf") >>> type(gexf4) >>> random.seed(120) >>> engine.gen_using(gexf_maker, 'testfile5', {'min_weight':0, 'max_weight':50.2, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':0, 'self_loop':1, 'multigraph':0}) 40 >>> gexf5 = read_gexf("testfile5.gexf") >>> type(gexf5) >>> #################### dot_maker #################### >>> import pydot >>> random.seed(2) >>> engine.gen_using(dot_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 7 >>> file=open('testfile.gv','r') >>> g1 = pydot.graph_from_dot_data(file.read()) >>> g1[0].get_type() 'digraph' >>> len(g1[0].get_edge_list()) 7 >>> random.seed(4) >>> engine.gen_using(dot_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':1, 'direct':1, 'self_loop':1, 'multigraph':0}) 35 >>> file=open('testfile2.gv','r') >>> g2 = pydot.graph_from_dot_data(file.read()) >>> g2[0].get_type() 'digraph' >>> len(g2[0].get_edge_list()) 35 >>> random.seed(20) >>> engine.gen_using(dot_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':0, 'direct':0, 'self_loop':1, 'multigraph':1}) 131 >>> file=open('testfile3.gv','r') >>> g3 = pydot.graph_from_dot_data(file.read()) >>> g3[0].get_type() 'graph' >>> len(g3[0].get_edge_list()) 131 >>> file.close() >>> os.remove('testfile.gr') >>> os.remove('testfile2.gr') >>> os.remove('testfile3.gr') >>> os.remove('testfile4.gr') >>> os.remove('testfile.json') >>> os.remove('testfile2.json') >>> os.remove('testfile3.json') >>> os.remove('testfile.csv') >>> os.remove('testfile2.csv') >>> os.remove('testfile3.csv') >>> os.remove('testfile4.csv') >>> os.remove('testfile.gdf') >>> os.remove('testfile2.gdf') >>> os.remove('testfile3.gdf') >>> os.remove('testfile.gl') >>> os.remove('testfile2.gl') >>> os.remove('testfile3.gl') >>> os.remove('testfile.mtx') >>> os.remove('testfile2.mtx') >>> os.remove('testfile3.mtx') >>> os.remove('testfile.tsv') >>> os.remove('testfile2.tsv') >>> os.remove('testfile3.tsv') >>> os.remove('testfile.wel') >>> os.remove('testfile2.wel') >>> os.remove('testfile3.wel') >>> os.remove('testfile.lp') >>> os.remove('testfile2.lp') >>> os.remove('testfile.tgf') >>> os.remove('testfile2.tgf') >>> os.remove('testfile.dl') >>> os.remove('testfile2.dl') >>> os.remove('testfile.gml') >>> os.remove('testfile2.gml') >>> os.remove('testfile3.gml') >>> os.remove('testfile4.gml') >>> os.remove('testfile.gexf') >>> os.remove('testfile2.gexf') >>> os.remove('testfile3.gexf') >>> os.remove('testfile4.gexf') >>> os.remove('testfile5.gexf') >>> os.remove('testfile.gv') >>> os.remove('testfile2.gv') >>> os.remove('testfile3.gv') >>> os.remove('logfile.log') """ pyrgg-1.6/test/functions_test.py000066400000000000000000000375231471451651000171260ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ >>> from pyrgg.functions import * >>> import pyrgg.params >>> import random >>> import os >>> os.environ["PYRGG_TEST_MODE"] = "1" >>> description_print() Webpage : https://www.pyrgg.site Repository : https://github.com/sepandhaghighi/pyrgg Paper : https://doi.org/10.21105/joss.00331 * If you use PyRGG in your research, please cite our paper ######################################## PyRGG is a user-friendly synthetic random graph generator that is written in Python and supports multiple graph file formats, such as DIMACS-Graph files. It can generate graphs of various sizes and is specifically designed to create input files for a wide range of graph-based research applications, including testing, benchmarking, and performance analysis of graph processing frameworks. PyRGG is aimed at computer scientists who are studying graph algorithms and graph processing frameworks. ######################################## >>> result = input_filter({"file_name": "test","vertices": 5,"max_weight": 1000,"min_weight":455,"min_edge": -45,"max_edge": -11,"sign": False,"output_format": 1, "direct": False,"self_loop": True,"multigraph":False,"number_of_files":2,"engine":1}) >>> result == {'output_format': 1, 'min_weight': 455, 'min_edge': 5, 'max_edge': 5, 'file_name': 'test', 'vertices': 5, 'max_weight': 1000, 'sign': False, "direct": False,"self_loop": True,"multigraph":False,"number_of_files":2,"engine":1} True >>> result = input_filter({"file_name": "test","vertices": 5,"max_weight": 1000,"min_weight":455,"min_edge": -45,"max_edge": -11,"sign": False,"output_format": 1, "direct": False,"self_loop": False,"multigraph":False,"number_of_files":10,"engine":1}) >>> result == {'output_format': 1, 'min_weight': 455, 'min_edge': 4, 'max_edge': 4, 'file_name': 'test', 'vertices': 5, 'max_weight': 1000, 'sign': False, "direct": False,"self_loop": False,"multigraph":False,"number_of_files":10,"engine":1} True >>> result = input_filter({"file_name": "test","vertices": -5,"max_weight": 1000,"min_weight":455,"min_edge": -45,"max_edge": -11,"sign": False,"output_format": 1, "direct": False,"self_loop": False,"multigraph":True,"number_of_files":1,"engine":1}) >>> result == {'output_format': 1, 'min_weight': 455, 'min_edge': 11, 'max_edge': 45, 'file_name': 'test', 'vertices': 5, 'max_weight': 1000, 'sign': False, "direct": False,"self_loop": False,"multigraph":True,"number_of_files":1,"engine":1} True >>> result = input_filter({"file_name": "test","vertices": -5,"max_weight": 1000,"min_weight":455,"min_edge": -45,"max_edge": -11,"sign": False,"output_format": 1, "direct": False,"self_loop": False,"multigraph":True,"number_of_files":1,"engine":1}) >>> result == {'output_format': 1, 'min_weight': 455, 'min_edge': 11, 'max_edge': 45, 'file_name': 'test', 'vertices': 5, 'max_weight': 1000, 'sign': False, "direct": False,"self_loop": False,"multigraph":True,"number_of_files":1,"engine":1} True >>> result = input_filter({"file_name": "test2","vertices": 23,"max_weight": 2,"min_weight": 80,"min_edge": 23,"max_edge": 1,"sign": True,"output_format": 1, "direct": False,"self_loop": True,"multigraph":False,"number_of_files":2,"engine":1}) >>> result == {'min_weight': 2, 'vertices': 23, 'file_name': 'test2', 'max_edge': 23, 'min_edge': 1, 'max_weight': 80, 'output_format': 1, 'sign': True, "direct": False,"self_loop": True,"multigraph":False,"number_of_files":2,"engine":1} True >>> convert_bytes(200) '200.0 bytes' >>> convert_bytes(6000) '5.9 KB' >>> convert_bytes(80000) '78.1 KB' >>> time_convert(33) '00 days, 00 hours, 00 minutes, 33 seconds' >>> time_convert(15000) '00 days, 04 hours, 10 minutes, 00 seconds' >>> time_convert(1) '00 days, 00 hours, 00 minutes, 01 second' >>> time_convert(60) '00 days, 00 hours, 01 minute, 00 seconds' >>> time_convert(60*60) '00 days, 01 hour, 00 minutes, 00 seconds' >>> time_convert(60*60*24) '01 day, 00 hours, 00 minutes, 00 seconds' >>> time_convert(60*60*24 + 60*60 + 60 + 1) '01 day, 01 hour, 01 minute, 01 second' >>> time_convert(0) '00 days, 00 hours, 00 minutes, 00 seconds' >>> time_convert('sadasdasd') Traceback (most recent call last): ... ValueError: could not convert string to float: 'sadasdasd' >>> line(12,"*") ************ >>> is_weighted(0,0,False) False >>> is_weighted(0,0,True) False >>> is_weighted(20,20,False) True >>> is_weighted(1,1,False) False >>> is_weighted(1,1,True) True >>> get_precision(2) 0 >>> get_precision(2.2) 1 >>> get_precision(2.22) 2 >>> get_precision(2.223) 3 >>> handle_str_to_number("20") 20 >>> handle_str_to_number("20.2") 20.2 >>> handle_str_to_bool("1") True >>> handle_str_to_bool("3") Traceback (most recent call last): ... ValueError >>> handle_str_to_bool("0") False >>> handle_str_prob("0.2") 0.2 >>> handle_str_prob("-0.2") Traceback (most recent call last): ... ValueError >>> handle_str_prob("1.2") Traceback (most recent call last): ... ValueError >>> is_float(10) False >>> is_float(10.2) True >>> is_float(None) False >>> prev_item = "" >>> input_func_dict = {"vertices":"120","max_weight":"110","min_weight":"0","min_edge":"1","max_edge":"1000","sign":"1","direct":"1","self_loop":"1","multigraph":"0","file_name":"File 1","output_format":"2","weight":"1","error":"120","number_of_files":"3","config":"0","engine":"1"} >>> def input_func_test(input_data): ... global prev_item ... for index in pyrgg.params.MENU_ITEMS: ... item1, item2 = pyrgg.params.MENU_ITEMS[index] ... if input_data == item2: ... if item1 != prev_item: ... prev_item = item1 ... return input_func_dict[item1] ... else: ... return input_func_dict["error"] ... for index in pyrgg.params.PYRGG_ENGINE_PARAMS: ... item1, item2 = pyrgg.params.PYRGG_ENGINE_PARAMS[index] ... if input_data == item2: ... if item1 != prev_item : ... prev_item = item1 ... return input_func_dict[item1] ... else: ... return input_func_dict["error"] ... for index in pyrgg.params.ERG_ENGINE_PARAMS: ... item1, item2 = pyrgg.params.ERG_ENGINE_PARAMS[index] ... if input_data == item2: ... if item1 != prev_item : ... prev_item = item1 ... return input_func_dict[item1] ... else: ... return input_func_dict["error"] ... for index in pyrgg.params.ER_ENGINE_PARAMS: ... item1, item2 = pyrgg.params.ER_ENGINE_PARAMS[index] ... if input_data == item2: ... if item1 != prev_item : ... prev_item = item1 ... return input_func_dict[item1] ... else: ... return input_func_dict["error"] >>> def input_func_conf_test1(input_data): ... return "1" >>> def input_func_conf_test2(input_data): ... return "2" >>> input_data = get_input(input_func_test) >>> input_data["vertices"] 120 >>> input_data["max_weight"] 110 >>> input_data["min_weight"] 0 >>> input_data["min_edge"] 1 >>> input_data["max_edge"] 120 >>> input_data["number_of_files"] 3 >>> input_data["sign"] True >>> input_data["weight"] True >>> input_data["engine"] 1 >>> prev_item = "" >>> input_func_dict = {"vertices":"120","max_weight":"110","min_weight":"1.2","min_edge":"10000","max_edge":"2","sign":"1","direct":"1","self_loop":"1","multigraph":"0","file_name":"File 1","output_format":"2","weight":"1","error":"120","number_of_files":"1","config":"1","engine":"1"} >>> input_data = get_input(input_func_test) >>> input_data["vertices"] 120 >>> input_data["max_weight"] 110 >>> input_data["number_of_files"] 1 >>> input_data["min_weight"] 1.2 >>> input_data["min_edge"] 2 >>> input_data["max_edge"] 120 >>> input_data["sign"] True >>> input_data["weight"] True >>> input_data["engine"] 1 >>> loaded_config = check_for_config(input_func_conf_test1) >>> input_data["config"] True >>> input_data_ = input_data.copy() >>> config_path = save_config(input_data) >>> loaded_config = load_config(config_path) >>> loaded_config["vertices"] == input_data_["vertices"] True >>> loaded_config["number_of_files"] == input_data_["number_of_files"] True >>> loaded_config["output_format"] == input_data_["output_format"] True >>> save_config("test") [Error] Failed to save config file! >>> load_config("test123456789") [Error] Failed to load config file! >>> loaded_config = check_for_config(input_func_conf_test1) Config files detected in the current directory are listed below: [1] - ... >>> loaded_config["vertices"] == input_data_["vertices"] True >>> loaded_config["number_of_files"] == input_data_["number_of_files"] True >>> loaded_config["output_format"] == input_data_["output_format"] True >>> loaded_config = check_for_config(input_func_conf_test2) Config files detected in the current directory are listed below: [1] - ... >>> loaded_config == None True >>> input_func_dict = {"vertices":"120","max_weight":"110.45","min_weight":"test","min_edge":"10000","max_edge":"2","sign":"1","direct":"1","self_loop":"0","multigraph":"0","file_name":"File 2","output_format":"1","weight":"0","number_of_files":"1","error":"120","config":"0","engine":"1"} >>> input_data = get_input(input_func_test) >>> input_data["vertices"] 120 >>> input_data["min_weight"] 1 >>> input_data["max_edge"] 119 >>> input_data["max_weight"] 1 >>> input_data["weight"] False >>> input_data["sign"] True >>> input_data["direct"] True >>> input_data["multigraph"] False >>> prev_item = "" >>> input_func_dict = {"vertices":"wrong vertices","max_weight":"110.45","min_weight":"2","min_edge":"10000","max_edge":"2","sign":"1","direct":"1","self_loop":"1","multigraph":"1","file_name":"File 2","output_format":"1","weight":"1","error":"1200","number_of_files":"2","config":"0","engine":"1"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["vertices"] 1200 >>> prev_item = "" >>> input_func_dict = {"vertices":"1200","max_weight":"wrong max_weight","min_weight":"2","min_edge":"10000","max_edge":"2","sign":"1","direct":"1","self_loop":"1","multigraph":"1","file_name":"File 2","output_format":"1","weight":"1","error":"400","number_of_files":"2","config":"0","engine":"1"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["max_weight"] 400 >>> prev_item = "" >>> input_func_dict = {"vertices":"1200","max_weight":"400","min_weight":"wrong min_weight","min_edge":"10000","max_edge":"2","sign":"1","direct":"1","self_loop":"1","multigraph":"1","file_name":"File 2","output_format":"1","weight":"1","error":"2","number_of_files":"2","config":"0","engine":"1"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["min_weight"] 2 >>> prev_item = "" >>> input_func_dict = {"vertices":"1200","max_weight":"400","min_weight":"2","min_edge":"wrong min_edge","max_edge":"2","sign":"1","direct":"1","self_loop":"1","multigraph":"1","file_name":"File 2","output_format":"1","weight":"1","error":"0","number_of_files":"2","config":"0","engine":"1"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["min_edge"] 0 >>> prev_item = "" >>> input_func_dict = {"vertices":"1200","max_weight":"400","min_weight":"2","min_edge":"0","max_edge":"wrong max_edge","sign":"1","direct":"1","self_loop":"1","multigraph":"1","file_name":"File 2","output_format":"1","weight":"1","error":"2000","number_of_files":"2","config":"0","engine":"1"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["max_edge"] 2000 >>> prev_item = "" >>> input_func_dict = {"vertices":"1200","max_weight":"400","min_weight":"2","min_edge":"0","max_edge":"2000","sign":"400","direct":"1","self_loop":"1","multigraph":"1","file_name":"File 2","output_format":"1","weight":"1","error":"1","number_of_files":"2","config":"0","engine":"1"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["sign"] True >>> prev_item = "" >>> input_func_dict = {"vertices":"1200","max_weight":"400","min_weight":"2","min_edge":"0","max_edge":"2000","sign":"1","direct":"4000","self_loop":"1","multigraph":"1","file_name":"File 2","output_format":"1","weight":"1","error":"1","number_of_files":"2","config":"0","engine":"1"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["direct"] True >>> prev_item = "" >>> input_func_dict = {"vertices":"1200","max_weight":"400","min_weight":"2","min_edge":"0","max_edge":"2000","sign":"1","direct":"1","self_loop":"4000","multigraph":"1","file_name":"File 2","output_format":"1","weight":"1","error":"0","number_of_files":"2","config":"0","engine":"1"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["self_loop"] False >>> prev_item = "" >>> input_func_dict = {"vertices":"1200","max_weight":"400","min_weight":"2","min_edge":"0","max_edge":"2000","sign":"1","direct":"1","self_loop":"0","multigraph":"40000","file_name":"File 2","output_format":"1","weight":"1","error":"0","number_of_files":"2","config":"0","engine":"1"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["multigraph"] False >>> prev_item = "" >>> input_func_dict = {"vertices":"1200","max_weight":"400","min_weight":"2","min_edge":"0","max_edge":"2000","sign":"1","direct":"1","self_loop":"0","multigraph":"0","file_name":"","output_format":"1","weight":"1","error":"file1","number_of_files":"2","config":"0","engine":"1"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["file_name"] 'file1' >>> prev_item = "" >>> input_func_dict = {"vertices":"1200","max_weight":"400","min_weight":"2","min_edge":"0","max_edge":"2000","sign":"1","direct":"1","self_loop":"0","multigraph":"0","file_name":"file1","output_format":"4000","weight":"1","error":"1","number_of_files":"2","config":"0","engine":"1"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["output_format"] 1 >>> prev_item = "" >>> input_func_dict = {"vertices":"1200","max_weight":"400","min_weight":"2","min_edge":"0","max_edge":"2000","sign":"1","direct":"1","self_loop":"0","multigraph":"0","file_name":"file1","output_format":"1","weight":"4000","error":"0","number_of_files":"2","config":"0","engine":"1"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["weight"] False >>> prev_item = "" >>> input_func_dict = {"vertices":"1200","max_weight":"400","min_weight":"2","min_edge":"0","max_edge":"2000","sign":"1","direct":"1","self_loop":"0","multigraph":"0","file_name":"file1","output_format":"1","weight":"0","error":"1","number_of_files":"-5","config":"0","engine":"1"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["number_of_files"] 1 >>> prev_item = "" >>> input_func_dict = {"vertices":"1200","max_weight":"400","min_weight":"2","min_edge":"0","max_edge":"2000","sign":"1","direct":"1","self_loop":"0","multigraph":"0","file_name":"file1","output_format":"1","weight":"0","error":"0","number_of_files":"1","config":"4000","engine":"1"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["config"] False >>> prev_item = "" >>> input_func_dict = {"vertices":"1200","max_weight":"400","min_weight":"2","min_edge":"0","max_edge":"2000","sign":"1","direct":"1","self_loop":"0","multigraph":"0","file_name":"file1","output_format":"1","weight":"0","error":"1","number_of_files":"1","config":"0","engine":"100000"} >>> input_data = get_input(input_func_test) [Error] Bad input! >>> input_data["engine"] 1 >>> handle_string("2") '2' >>> handle_string("") Traceback (most recent call last): ... ValueError >>> handle_pos_int(1) 1 >>> handle_pos_int(-1) Traceback (most recent call last): ... ValueError >>> handle_output_format("1") 1 >>> handle_output_format("-14") Traceback (most recent call last): ... ValueError >>> handle_output_format("10000000000") Traceback (most recent call last): ... ValueError >>> handle_engine("1") 1 >>> handle_engine(-4) Traceback (most recent call last): ... ValueError >>> handle_engine("10000000000") Traceback (most recent call last): ... ValueError >>> os.remove('File 1.pyrgg.config.json') """ pyrgg-1.6/test/graph_gen_test.py000066400000000000000000000114111471451651000170340ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ >>> from pyrgg import * >>> import pyrgg.params >>> import pyrgg.engines.pyrgg as engine >>> import random >>> import os >>> import json >>> import yaml >>> import pickle >>> os.environ["PYRGG_TEST_MODE"] = "1" >>> dimacs_maker({}, {}) Traceback (most recent call last): ... TypeError: dimacs_maker() missing 1 required positional argument: 'mdata' >>> random.seed(2) >>> engine.gen_using(json_maker, 'testfile', {'min_weight':0, 'max_weight':200, 'vertices':10, 'min_edge':0, 'max_edge':2, 'sign':True, 'direct':True, 'self_loop':True, 'multigraph':False}) 7 >>> json_to_yaml('testfile') >>> file=open('testfile.yaml','r') >>> testfile_1_yaml=yaml.safe_load(file) >>> testfile_1_yaml['graph']['edges'][1]['source'] 5 >>> testfile_1_yaml['graph']['edges'][1]['target'] 6 >>> testfile_1_yaml['graph']['edges'][1]['weight'] 148 >>> testfile_1_yaml['properties']['directed'] True >>> testfile_1_yaml['properties']['multigraph'] False >>> testfile_1_yaml['properties']['weighted'] True >>> json_to_pickle('testfile') >>> testfile_1_p=pickle.load( open( 'testfile.p', 'rb' ) ) >>> testfile_1_p['graph']['edges'][1]['source'] 5 >>> testfile_1_p['graph']['edges'][1]['target'] 6 >>> testfile_1_p['graph']['edges'][1]['weight'] 148 >>> testfile_1_p['properties']['directed'] True >>> testfile_1_p['properties']['multigraph'] False >>> testfile_1_p['properties']['weighted'] True >>> random.seed(4) >>> engine.gen_using(json_maker, 'testfile2', {'min_weight':0, 'max_weight':50, 'vertices':30, 'min_edge':0, 'max_edge':4, 'sign':True, 'direct':True, 'self_loop':True, 'multigraph':False}) 35 >>> json_to_yaml('testfile2') >>> file=open('testfile2.yaml','r') >>> testfile_2_yaml=yaml.safe_load(file) >>> testfile_2_yaml['graph']['nodes'][1] {'id': 2} >>> testfile_2_yaml['graph']['edges'][1]['source'] 2 >>> testfile_2_yaml['graph']['edges'][1]['target'] 18 >>> testfile_2_yaml['graph']['edges'][1]['weight'] 5 >>> testfile_2_yaml['properties']['directed'] True >>> testfile_2_yaml['properties']['multigraph'] False >>> testfile_2_yaml['properties']['weighted'] True >>> json_to_pickle('testfile2') >>> testfile_2_p=pickle.load( open( 'testfile2.p', 'rb' ) ) >>> testfile_2_p['graph']['edges'][1]['source'] 2 >>> testfile_2_p['graph']['edges'][1]['target'] 18 >>> testfile_2_p['graph']['edges'][1]['weight'] 5 >>> testfile_2_p['properties']['directed'] True >>> testfile_2_p['properties']['multigraph'] False >>> testfile_2_p['properties']['weighted'] True >>> random.seed(20) >>> engine.gen_using(json_maker, 'testfile3', {'min_weight':10, 'max_weight':30, 'vertices':100, 'min_edge':0, 'max_edge':4, 'sign':False, 'direct':True, 'self_loop':True, 'multigraph':False}) 137 >>> json_to_yaml('testfile3') >>> file=open('testfile3.yaml','r') >>> testfile_3_yaml=yaml.safe_load(file) >>> testfile_3_yaml['graph']['nodes'][1] {'id': 2} >>> testfile_3_yaml['graph']['edges'][1]['source'] 3 >>> testfile_3_yaml['graph']['edges'][1]['target'] 76 >>> testfile_3_yaml['graph']['edges'][1]['weight'] 15 >>> testfile_3_yaml['properties']['directed'] True >>> testfile_3_yaml['properties']['multigraph'] False >>> testfile_3_yaml['properties']['weighted'] True >>> json_to_yaml('testfile24') [Error] Failed to generate YAML file! >>> json_to_pickle('testfile24') [Error] Failed to generate Pickle file! >>> json_maker({}, {}) Traceback (most recent call last): ... TypeError: json_maker() missing 1 required positional argument: 'mdata' >>> json_to_pickle('testfile3') >>> testfile_3_p=pickle.load( open( 'testfile3.p', 'rb' ) ) >>> testfile_3_p['graph']['edges'][1]['source'] 3 >>> testfile_3_p['graph']['edges'][1]['target'] 76 >>> testfile_3_p['graph']['edges'][1]['weight'] 15 >>> testfile_3_p['properties']['directed'] True >>> testfile_3_p['properties']['multigraph'] False >>> testfile_3_p['properties']['weighted'] True >>> gl_maker({}) Traceback (most recent call last): ... TypeError: gl_maker() missing 2 required positional arguments: 'weight_dic' and 'mdata' >>> csv_maker({}, {}) Traceback (most recent call last): ... TypeError: csv_maker() missing 1 required positional argument: 'mdata' >>> gdf_maker({}) Traceback (most recent call last): ... TypeError: gdf_maker() missing 2 required positional arguments: 'weight_dic' and 'mdata' >>> tsv_maker({}, {}) Traceback (most recent call last): ... TypeError: tsv_maker() missing 1 required positional argument: 'mdata' >>> wel_maker({}, {}) Traceback (most recent call last): ... TypeError: wel_maker() missing 1 required positional argument: 'mdata' >>> file.close() >>> os.remove('testfile.json') >>> os.remove('testfile.p') >>> os.remove('testfile.yaml') >>> os.remove('testfile2.json') >>> os.remove('testfile2.p') >>> os.remove('testfile2.yaml') >>> os.remove('testfile3.json') >>> os.remove('testfile3.p') >>> os.remove('testfile3.yaml') """ pyrgg-1.6/tox.ini000066400000000000000000000003121471451651000140230ustar00rootroot00000000000000[tox] envlist = py35,py36,py37,py38,flake8 [testenv] deps = -r dev-requirements.txt commands = pytest --cov=pyrgg/ test/ [testenv:flake8] deps = -r dev-requirements.txt commands = flake8 pyrgg/ test/